Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear blend skinning support. #18

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/Text/GLTF/Loader/Gltf.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Text.GLTF.Loader.Gltf
Sampler (..),
SamplerWrap (..),
Scene (..),
Skin (..),
Texture (..),
TextureInfo (..),

Expand Down Expand Up @@ -110,6 +111,7 @@ data Gltf = Gltf
gltfNodes :: Vector Node,
gltfSamplers :: Vector Sampler,
gltfScenes :: Vector Scene,
gltfSkins :: Vector Skin,
gltfTextures :: Vector Texture
}
deriving (Eq, Show)
Expand Down Expand Up @@ -179,6 +181,8 @@ data Node = Node
nodeRotation :: Maybe (Quaternion Float),
-- | The node's non-uniform scale
nodeScale :: Maybe (V3 Float),
-- | The index of the skin if it contains a skinned mesh
nodeSkin :: Maybe Int,
-- | The node's translation along the x, y, and z axes.
nodeTranslation :: Maybe (V3 Float),
-- | The weights of the instantiated morph target.
Expand Down Expand Up @@ -210,6 +214,16 @@ data Scene = Scene
}
deriving (Eq, Show)

data Skin = Skin
{ -- | The inverse bind matrices of the joints in this skin
skinInverseBindMatrices :: Vector (M44 Float),
-- | The user-defined name of this object
skinName :: Maybe Text,
-- | The node indices of the joints used by this skin
skinJoints :: Vector Int
}
deriving (Eq, Show)

-- | A texture and its sampler.
data Texture = Texture
{ -- | The user-defined name of this object.
Expand All @@ -236,7 +250,11 @@ data MeshPrimitive = MeshPrimitive
-- | A Vector of vertex texture coordinates
meshPrimitiveTexCoords :: Vector (V2 Float),
-- | A Vector of vertex colors.
meshPrimitiveColors :: Vector (V4 Float)
meshPrimitiveColors :: Vector (V4 Float),
-- | A Vector of the joints which affect each vertex
meshPrimitiveJoints :: Vector (V4 Word16),
-- | A Vector of joint weights for each vertex
meshPrimitiveWeights :: Vector (V4 Float)
}
deriving (Eq, Show)

Expand Down
34 changes: 33 additions & 1 deletion src/Text/GLTF/Loader/Internal/Adapter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import qualified Codec.GlTF.Node as Node
import qualified Codec.GlTF.PbrMetallicRoughness as PbrMetallicRoughness
import qualified Codec.GlTF.Sampler as Sampler
import qualified Codec.GlTF.Scene as Scene
import qualified Codec.GlTF.Skin as Skin
import qualified Codec.GlTF.Texture as Texture
import qualified Codec.GlTF.TextureInfo as TextureInfo
import qualified Data.HashMap.Strict as HashMap
Expand All @@ -60,6 +61,12 @@ attributeTexCoord = "TEXCOORD_0"
attributeColors :: Text
attributeColors = "COLOR_0"

attributeJoints :: Text
attributeJoints = "JOINTS_0"

attributeWeights :: Text
attributeWeights = "WEIGHTS_0"

runAdapter
:: GlTF.GlTF
-> Vector GltfBuffer
Expand All @@ -75,6 +82,7 @@ adaptGltf = do

gltfImages <- adaptImages images
gltfMeshes <- adaptMeshes meshes
gltfSkins <- adaptSkins skins

return
$ Gltf
Expand All @@ -85,6 +93,7 @@ adaptGltf = do
gltfNodes = adaptNodes nodes,
gltfSamplers = adaptSamplers samplers,
gltfScenes = adaptScenes scenes,
gltfSkins = gltfSkins,
gltfTextures = adaptTextures textures
}

Expand All @@ -107,6 +116,24 @@ adaptImages codecImages = do
iforM images' $ \imgId img ->
adaptImage (imageData ! imgId) img

adaptSkins :: Maybe (Vector Skin.Skin) -> Adapter (Vector Skin)
adaptSkins = maybe (return mempty) (mapM adaptSkin)

adaptSkin :: Skin.Skin -> Adapter Skin
adaptSkin skin = do
gltf <- getGltf
buffers <- getBuffers
let inverseBindMatrices' =
maybe mempty (inverseBindMatrices gltf buffers)
. Skin.inverseBindMatrices
$ skin
return
$ Skin
{ skinInverseBindMatrices = inverseBindMatrices',
skinName = Skin.name skin,
skinJoints = fmap Node.unNodeIx . Skin.joints $ skin
}

adaptMaterials :: Maybe (Vector Material.Material) -> Vector Material
adaptMaterials = maybe mempty (fmap adaptMaterial)

Expand Down Expand Up @@ -172,6 +199,7 @@ adaptNode Node.Node{..} =
nodeName = name,
nodeRotation = toQuaternion <$> rotation,
nodeScale = toV3 <$> scale,
nodeSkin = Skin.unSkinIx <$> skin,
nodeTranslation = toV3 <$> translation,
nodeWeights = maybe [] toList weights
}
Expand Down Expand Up @@ -270,13 +298,17 @@ adaptMeshPrimitive Mesh.MeshPrimitive{..} = do
meshPrimitivePositions = maybe mempty (vertexPositions gltf buffers') positions,
meshPrimitiveTexCoords = maybe mempty (vertexTexCoords gltf buffers') texCoords,
meshPrimitiveColors =
maybe mempty (fmap (mapV4 toRatio) . vertexColors gltf buffers') colors
maybe mempty (fmap (mapV4 toRatio) . vertexColors gltf buffers') colors,
meshPrimitiveJoints = maybe mempty (vertexJoints gltf buffers') joints,
meshPrimitiveWeights = maybe mempty (vertexWeights gltf buffers') weights
}
where
positions = attributes HashMap.!? attributePosition
normals = attributes HashMap.!? attributeNormal
texCoords = attributes HashMap.!? attributeTexCoord
colors = attributes HashMap.!? attributeColors
joints = attributes HashMap.!? attributeJoints
weights = attributes HashMap.!? attributeWeights
toRatio w = fromIntegral w / fromIntegral (maxBound :: Word16)
mapV4 f (V4 w x y z) = V4 (f w) (f x) (f y) (f z)

Expand Down
55 changes: 54 additions & 1 deletion src/Text/GLTF/Loader/Internal/BufferAccessor.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ module Text.GLTF.Loader.Internal.BufferAccessor
vertexNormals,
vertexTexCoords,
vertexColors,
vertexJoints,
vertexWeights,
inverseBindMatrices,
imageDataRaw,
) where

import Text.GLTF.Loader.Internal.Decoders
import Prelude (max)

import Codec.GLB (Chunk (..))
import Codec.GlTF
import Codec.GlTF.Accessor
import Codec.GlTF.Accessor hiding (max)
import Codec.GlTF.Buffer
import Codec.GlTF.BufferView
import Codec.GlTF.Image
Expand Down Expand Up @@ -52,6 +56,23 @@ data BufferAccessor = BufferAccessor
buffer :: GltfBuffer
}

-- | Calculates fractional values from normalized integer types
class Real a => Normalized a where
denormalize :: (Fractional b, Ord b) => a -> b

instance Normalized Word8 where
denormalize = (flip (/) `on` realToFrac) maxBound

instance Normalized Word16 where
denormalize = (flip (/) `on` realToFrac) maxBound

instance Normalized Int8 where
denormalize = max (-1) . (flip (/) `on` realToFrac) maxBound

instance Normalized Int16 where
denormalize = max (-1) . (flip (/) `on` realToFrac) maxBound


-- | Read all the buffers into memory
loadBuffers
:: MonadUnliftIO io
Expand Down Expand Up @@ -117,6 +138,38 @@ vertexTexCoords = readBufferWithGet getTexCoords
vertexColors :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (V4 Word16)
vertexColors = readBufferWithGet getColors

-- | Decode vertex joints
vertexJoints :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (V4 Word16)
vertexJoints gltf buffers' accessorId =
fromMaybe mempty $ do
buffer@BufferAccessor{componentType = componentType} <-
bufferAccessor gltf buffers' accessorId

case componentType of
UNSIGNED_SHORT ->
Just (readFromBuffer (Proxy @(V4 Word16)) getJoints16 buffer)
UNSIGNED_BYTE ->
Just (fmap fromIntegral <$> readFromBuffer (Proxy @(V4 Word8)) getJoints buffer)
_ -> Nothing

-- | Decode vertex weights
vertexWeights :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (V4 Float)
vertexWeights gltf buffers' accessorId =
fromMaybe mempty $ do
buffer@BufferAccessor{componentType = componentType} <-
bufferAccessor gltf buffers' accessorId

case componentType of
UNSIGNED_SHORT ->
Just (fmap denormalize <$> readFromBuffer (Proxy @(V4 Word16)) getWeights16 buffer)
UNSIGNED_BYTE ->
Just (fmap denormalize <$> readFromBuffer (Proxy @(V4 Word8)) getWeights8 buffer)
FLOAT -> Just (readFromBuffer (Proxy @(V4 Float)) getWeights buffer)
_ -> Nothing

inverseBindMatrices :: GlTF -> Vector GltfBuffer -> AccessorIx -> Vector (M44 Float)
inverseBindMatrices = readBufferWithGet getInverseBindMatrices

-- | Read an image from a buffer view
imageDataRaw :: GlTF -> Vector GltfBuffer -> BufferViewIx -> Maybe ByteString
imageDataRaw = readBufferView
Expand Down
30 changes: 30 additions & 0 deletions src/Text/GLTF/Loader/Internal/Decoders.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ module Text.GLTF.Loader.Internal.Decoders
getNormals,
getTexCoords,
getColors,
getJoints,
getJoints16,
getWeights,
getWeights8,
getWeights16,
getInverseBindMatrices,

-- * GLTF Accessor Type decoders
getScalar,
Expand Down Expand Up @@ -54,6 +60,30 @@ getTexCoords = getVec2 getFloat
getColors :: Get (Vector (V4 Word16))
getColors = getVec4 getUnsignedShort

-- | Vertex joints binary decoder, for unsigned bytes
getJoints :: Get (Vector (V4 Word8))
getJoints = getVec4 getUnsignedByte

-- | Vertex joints binary decoder, for unsigned shorts
getJoints16 :: Get (Vector (V4 Word16))
getJoints16 = getVec4 getUnsignedShort

-- | Vertex joint weights binary decoder, for floats
getWeights :: Get (Vector (V4 Float))
getWeights = getVec4 getFloat

-- | Vertex weights binary decoder, for unsigned bytes
getWeights8 :: Get (Vector (V4 Word8))
getWeights8 = getVec4 getUnsignedByte

-- | Vertex weights binary decoder, for unsigned shorts
getWeights16 :: Get (Vector (V4 Word16))
getWeights16 = getVec4 getUnsignedShort

-- | Inverse bind matrix decoder
getInverseBindMatrices :: Get (Vector (M44 Float))
getInverseBindMatrices = getMat4 getFloat

-- | Scalar (simple) type binary decoder
getScalar :: Get a -> Get (Vector a)
getScalar = getVector
Expand Down
Loading