Skip to content

Commit 46ef599

Browse files
committed
Animation support.
1 parent cb22717 commit 46ef599

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

src/Text/GLTF/Loader/Gltf.hs

+41
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ module Text.GLTF.Loader.Gltf
22
( -- * Data constructors
33
Gltf (..),
44
Asset (..),
5+
Animation (..),
6+
Channel (..),
7+
ChannelSamplerInterpolation (..),
8+
ChannelSamplerOutput (..),
59
Image (..),
610
MagFilter (..),
711
MinFilter (..),
@@ -104,6 +108,7 @@ import RIO
104108
-- | The root data type for a glTF asset
105109
data Gltf = Gltf
106110
{ gltfAsset :: Asset,
111+
gltfAnimations :: Vector Animation,
107112
gltfImages :: Vector Image,
108113
gltfMaterials :: Vector Material,
109114
gltfMeshes :: Vector Mesh,
@@ -127,6 +132,16 @@ data Asset = Asset
127132
}
128133
deriving (Eq, Show)
129134

135+
-- | Keyframe animations for tranforming and morphing scene nodes
136+
data Animation = Animation
137+
{ -- | Defines the animation keyframes for up to one of each from translation
138+
-- , rotation, scale and morph weights.
139+
animationChannels :: Vector Channel,
140+
-- | The user-defined name of this object.
141+
animationName :: Maybe Text
142+
}
143+
deriving (Eq, Show)
144+
130145
-- | Image data used to create a texture.
131146
data Image = Image
132147
{ -- | The binary data of the image
@@ -308,6 +323,32 @@ data TextureInfo = TextureInfo
308323
}
309324
deriving (Eq, Show)
310325

326+
data Channel = Channel
327+
{ -- | The target node to apply this channel of the animation to.
328+
channelTargetNode :: Maybe Int,
329+
-- | The interpolation to use for inputs between each animation keyframe
330+
-- sample.
331+
channelSamplerInterpolation :: ChannelSamplerInterpolation,
332+
-- | The timestamps of each of the animation's keyframes.
333+
channelSamplerInputs :: Vector Float,
334+
-- | The values representing the animated property of each keyframe.
335+
channelSamplerOutputs :: ChannelSamplerOutput
336+
}
337+
deriving (Eq, Show)
338+
339+
data ChannelSamplerOutput
340+
= MorphTargetWeights (Vector Float)
341+
| Rotation (Vector (Quaternion Float))
342+
| Scale (Vector (V3 Float))
343+
| Translation (Vector (V3 Float))
344+
deriving (Eq, Show)
345+
346+
data ChannelSamplerInterpolation
347+
= CubicSpline
348+
| Linear
349+
| Step
350+
deriving (Eq, Show)
351+
311352
-- | Metadata about the glTF asset
312353
_asset :: Lens' Gltf Asset
313354
_asset = lens gltfAsset (\gltf asset -> gltf{gltfAsset = asset})

src/Text/GLTF/Loader/Internal/Adapter.hs

+49
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Text.GLTF.Loader.Internal.Adapter
77
runAdapter,
88
adaptGltf,
99
adaptAsset,
10+
adaptAnimations,
1011
adaptImages,
1112
adaptMaterials,
1213
adaptMeshes,
@@ -32,6 +33,7 @@ import Text.GLTF.Loader.Internal.MonadAdapter
3233

3334
import qualified Codec.GlTF as GlTF
3435
import qualified Codec.GlTF.Asset as Asset
36+
import qualified Codec.GlTF.Animation as Animation
3537
import qualified Codec.GlTF.Image as Image
3638
import qualified Codec.GlTF.Material as Material
3739
import qualified Codec.GlTF.Mesh as Mesh
@@ -73,12 +75,14 @@ adaptGltf :: Adapter Gltf
7375
adaptGltf = do
7476
GlTF.GlTF{..} <- getGltf
7577

78+
gltfAnimations <- adaptAnimations animations
7679
gltfImages <- adaptImages images
7780
gltfMeshes <- adaptMeshes meshes
7881

7982
return
8083
$ Gltf
8184
{ gltfAsset = adaptAsset asset,
85+
gltfAnimations = gltfAnimations,
8286
gltfImages = gltfImages,
8387
gltfMaterials = adaptMaterials materials,
8488
gltfMeshes = gltfMeshes,
@@ -97,6 +101,51 @@ adaptAsset Asset.Asset{..} =
97101
assetMinVersion = minVersion
98102
}
99103

104+
adaptAnimations
105+
:: Maybe (Vector Animation.Animation)
106+
-> Adapter (Vector Animation)
107+
adaptAnimations = maybe (return mempty) (mapM adaptAnimation)
108+
109+
adaptAnimation :: Animation.Animation -> Adapter Animation
110+
adaptAnimation Animation.Animation{..} = do
111+
gltfChannels <- mapM (adaptAnimationChannel samplers) channels
112+
return
113+
$ Animation
114+
{ animationChannels = gltfChannels,
115+
animationName = name
116+
}
117+
118+
adaptAnimationChannel
119+
:: Vector Animation.AnimationSampler
120+
-> Animation.AnimationChannel
121+
-> Adapter Channel
122+
adaptAnimationChannel samplers Animation.AnimationChannel{..} = do
123+
gltf <- getGltf
124+
buffers <- getBuffers
125+
let Animation.AnimationSampler{ input, interpolation, output } =
126+
samplers ! Animation.unAnimationSamplerIx sampler
127+
Animation.AnimationChannelTarget{ node, path } = target
128+
outputs = case path of
129+
Animation.ROTATION -> Rotation $ animationSamplerRotationOutputs gltf buffers output
130+
Animation.SCALE -> Scale $ animationSamplerScaleOutputs gltf buffers output
131+
Animation.TRANSLATION -> Translation $ animationSamplerTranslationOutputs gltf buffers output
132+
Animation.WEIGHTS -> MorphTargetWeights $ animationSamplerWeightsOutputs gltf buffers output
133+
_ -> error $ "Invalid Channel path: " <> show path
134+
return
135+
$ Channel
136+
{ channelTargetNode = fmap Node.unNodeIx node,
137+
channelSamplerInterpolation = adaptInterpolation interpolation,
138+
channelSamplerInputs = animationSamplerInputs gltf buffers input,
139+
channelSamplerOutputs = outputs
140+
}
141+
142+
adaptInterpolation :: Animation.AnimationSamplerInterpolation -> ChannelSamplerInterpolation
143+
adaptInterpolation Animation.CUBICSPLINE = CubicSpline
144+
adaptInterpolation Animation.LINEAR = Linear
145+
adaptInterpolation Animation.STEP = Step
146+
adaptInterpolation (Animation.AnimationSamplerInterpolation interpolation) =
147+
error $ "Invalid ChannelSamplerInterpolation: " <> show interpolation
148+
100149
adaptImages :: Maybe (Vector Image.Image) -> Adapter (Vector Image)
101150
adaptImages codecImages = do
102151
imageData <- getImages

src/Text/GLTF/Loader/Internal/BufferAccessor.hs

+34
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ module Text.GLTF.Loader.Internal.BufferAccessor
77
loadImages,
88

99
-- * Deserializing Accessors
10+
animationSamplerInputs,
11+
animationSamplerRotationOutputs,
12+
animationSamplerScaleOutputs,
13+
animationSamplerTranslationOutputs,
14+
animationSamplerWeightsOutputs,
1015
vertexIndices,
1116
vertexPositions,
1217
vertexNormals,
@@ -87,6 +92,35 @@ loadImages GlTF{images = images} basePath = do
8792
let fallbackImageData = return $ maybe NoImageData ImageBufferView bufferView
8893
maybe fallbackImageData (fmap ImageData . loadUri' basePath) uri
8994

95+
animationSamplerInputs :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector Float
96+
animationSamplerInputs = readBufferWithGet (getScalar getFloat)
97+
98+
animationSamplerRotationOutputs :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (Quaternion Float)
99+
animationSamplerRotationOutputs gltf buffers' accessorId =
100+
fromMaybe (error "Invalid animation sampler output component type.") $ do
101+
buffer@BufferAccessor{componentType = componentType} <-
102+
bufferAccessor gltf buffers' accessorId
103+
104+
case componentType of
105+
FLOAT -> Just . readFromBuffer (Proxy @(Quaternion Float)) (getQuaternion getFloat) $ buffer
106+
_ -> Nothing
107+
108+
animationSamplerScaleOutputs :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (V3 Float)
109+
animationSamplerScaleOutputs = readBufferWithGet (getVec3 getFloat)
110+
111+
animationSamplerTranslationOutputs :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (V3 Float)
112+
animationSamplerTranslationOutputs = readBufferWithGet (getVec3 getFloat)
113+
114+
animationSamplerWeightsOutputs :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector Float
115+
animationSamplerWeightsOutputs gltf buffers' accessorId =
116+
fromMaybe (error "Invalid animation sampler output component type.") $ do
117+
buffer@BufferAccessor{componentType = componentType} <-
118+
bufferAccessor gltf buffers' accessorId
119+
120+
case componentType of
121+
FLOAT -> Just . readFromBuffer (Proxy @Float) (getScalar getFloat) $ buffer
122+
_ -> Nothing
123+
90124
-- | Decode vertex indices
91125
vertexIndices :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector Word32
92126
vertexIndices gltf buffers' accessorId =

src/Text/GLTF/Loader/Internal/Decoders.hs

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module Text.GLTF.Loader.Internal.Decoders
1515
getMat2,
1616
getMat3,
1717
getMat4,
18+
getQuaternion,
1819

1920
-- * GLTF Component Type decoders
2021
getByte,
@@ -97,6 +98,14 @@ getMat4 getter =
9798
<*> (V4 <$> getter <*> getter <*> getter <*> getter)
9899
<*> (V4 <$> getter <*> getter <*> getter <*> getter)
99100

101+
-- | Quaternion binary decoder
102+
getQuaternion :: Get a -> Get (Vector (Quaternion a))
103+
getQuaternion getter = getVector $ do
104+
v3 <- V3 <$> getter <*> getter <*> getter
105+
Quaternion
106+
<$> getter
107+
<*> pure v3
108+
100109
-- | Byte binary decoder
101110
getByte :: Get Int8
102111
getByte = getInt8

0 commit comments

Comments
 (0)