gltf-loader-0.3.0.0: src/Text/GLTF/Loader/Internal/Adapter.hs
-- |Transform a `Codec.GlTF.GlTF` to `Text.GLTF.Loader.Gltf.Gltf`
module Text.GLTF.Loader.Internal.Adapter
( attributePosition,
attributeNormal,
attributeTexCoord,
runAdapter,
adaptGltf,
adaptAsset,
adaptImages,
adaptMaterials,
adaptMeshes,
adaptNodes,
adaptTextures,
adaptImage,
adaptMaterial,
adaptMesh,
adaptNode,
adaptTexture,
adaptAlphaMode,
adaptPbrMetallicRoughness,
adaptMeshPrimitives,
adaptMeshPrimitive,
adaptMeshPrimitiveMode
) where
import Text.GLTF.Loader.Gltf
import Text.GLTF.Loader.Internal.BufferAccessor
import Text.GLTF.Loader.Internal.MonadAdapter
import Linear (V3(..), V4(..))
import RIO
import RIO.Partial (toEnum)
import RIO.Vector.Partial ((!))
import qualified Codec.GlTF as GlTF
import qualified Codec.GlTF.Asset as Asset
import qualified Codec.GlTF.Image as Image
import qualified Codec.GlTF.Material as Material
import qualified Codec.GlTF.PbrMetallicRoughness as PbrMetallicRoughness
import qualified Codec.GlTF.Mesh as Mesh
import qualified Codec.GlTF.Node as Node
import qualified Codec.GlTF.Sampler as Sampler
import qualified Codec.GlTF.Texture as Texture
import qualified Codec.GlTF.TextureInfo as TextureInfo
import qualified Data.HashMap.Strict as HashMap
import qualified RIO.Vector as V
attributePosition :: Text
attributePosition = "POSITION"
attributeNormal :: Text
attributeNormal = "NORMAL"
attributeTexCoord :: Text
attributeTexCoord = "TEXCOORD_0"
runAdapter
:: GlTF.GlTF
-> Vector GltfBuffer
-> Vector GltfImageData
-> Gltf
runAdapter gltf buffers images = runReader adaptGltf env
where env = AdaptEnv gltf buffers images
adaptGltf :: Adapter Gltf
adaptGltf = do
GlTF.GlTF{..} <- getGltf
gltfImages <- adaptImages images
gltfMeshes <- adaptMeshes meshes
return $ Gltf
{ gltfAsset = adaptAsset asset,
gltfImages = gltfImages,
gltfMaterials = adaptMaterials materials,
gltfMeshes = gltfMeshes,
gltfNodes = adaptNodes nodes,
gltfSamplers = adaptSamplers samplers,
gltfTextures = adaptTextures textures
}
adaptAsset :: Asset.Asset -> Asset
adaptAsset Asset.Asset{..} = Asset
{ assetVersion = version,
assetCopyright = copyright,
assetGenerator = generator,
assetMinVersion = minVersion
}
adaptImages :: Maybe (Vector Image.Image) -> Adapter (Vector Image)
adaptImages codecImages = do
imageData <- getImages
let images' = fromMaybe mempty codecImages
iforM = flip V.imapM
iforM images' $ \imgId img ->
adaptImage (imageData ! imgId) img
adaptMaterials :: Maybe (Vector Material.Material) -> Vector Material
adaptMaterials = maybe mempty (fmap adaptMaterial)
adaptMeshes :: Maybe (Vector Mesh.Mesh) -> Adapter (Vector Mesh)
adaptMeshes = maybe (return mempty) (V.mapM adaptMesh)
adaptNodes :: Maybe (Vector Node.Node) -> Vector Node
adaptNodes = maybe mempty (fmap adaptNode)
adaptSamplers :: Maybe (Vector Sampler.Sampler) -> Vector Sampler
adaptSamplers = maybe mempty (fmap adaptSampler)
adaptTextures :: Maybe (Vector Texture.Texture) -> Vector Texture
adaptTextures = maybe mempty (fmap adaptTexture)
adaptImage :: GltfImageData -> Image.Image -> Adapter Image
adaptImage imgData Image.Image{..} = do
payload <- getImageData imgData
-- Note that we treat mimeType as required, even though it may not be in the
-- specification. Tests in Blender suggest it's ALWAYS provided; When we come
-- across an example where it isn't, we'll address it then.
case mimeType of
Nothing -> error "Invalid Image: no mime-type specified"
Just mimeType' -> return Image
{ imageData = payload,
imageMimeType = mimeType',
imageName = name
}
adaptMaterial :: Material.Material -> Material
adaptMaterial Material.Material{..} = Material
{ materialAlphaCutoff = alphaCutoff,
materialAlphaMode = adaptAlphaMode alphaMode,
materialDoubleSided = doubleSided,
materialEmissiveFactor = toV3 emissiveFactor,
materialName = name,
materialPbrMetallicRoughness = adaptPbrMetallicRoughness <$> pbrMetallicRoughness
}
adaptMesh :: Mesh.Mesh -> Adapter Mesh
adaptMesh Mesh.Mesh{..} = do
primitives' <- adaptMeshPrimitives primitives
return $ Mesh
{ meshPrimitives = primitives',
meshWeights = fromMaybe mempty weights,
meshName = name
}
adaptNode :: Node.Node -> Node
adaptNode Node.Node{..} = Node
{ nodeChildren = maybe mempty (fmap Node.unNodeIx) children,
nodeMeshId = Mesh.unMeshIx <$> mesh,
nodeName = name,
nodeRotation = toV4 <$> rotation,
nodeScale = toV3 <$> scale,
nodeTranslation = toV3 <$> translation,
nodeWeights = maybe [] toList weights
}
adaptSampler :: Sampler.Sampler -> Sampler
adaptSampler Sampler.Sampler{..} = Sampler
{ samplerMagFilter = adaptMagFilter <$> magFilter,
samplerMinFilter = adaptMinFilter <$> minFilter,
samplerName = name,
samplerWrapS = adaptSamplerWrap wrapS,
samplerWrapT = adaptSamplerWrap wrapT
}
adaptTexture :: Texture.Texture -> Texture
adaptTexture Texture.Texture{..} = Texture
{ textureName = name,
textureSamplerId = Sampler.unSamplerIx <$> sampler,
textureSourceId = Image.unImageIx <$> source
}
getImageData :: GltfImageData -> Adapter (Maybe ByteString)
getImageData (ImageData payload) = return $ Just payload
getImageData NoImageData = return Nothing
getImageData (ImageBufferView bufferViewId) = imageDataRaw' <$> getGltf <*> getBuffers
where imageDataRaw' gltf buffers' = imageDataRaw gltf buffers' bufferViewId
adaptAlphaMode :: Material.MaterialAlphaMode -> MaterialAlphaMode
adaptAlphaMode Material.BLEND = Blend
adaptAlphaMode Material.MASK = Mask
adaptAlphaMode Material.OPAQUE = Opaque
adaptAlphaMode (Material.MaterialAlphaMode alphaMode)
= error $ "Invalid MaterialAlphaMode: " <> show alphaMode
adaptPbrMetallicRoughness
:: PbrMetallicRoughness.PbrMetallicRoughness
-> PbrMetallicRoughness
adaptPbrMetallicRoughness PbrMetallicRoughness.PbrMetallicRoughness{..}
= PbrMetallicRoughness
{ pbrBaseColorFactor = toV4 baseColorFactor,
pbrBaseColorTexture = adaptTextureInfo <$> baseColorTexture,
pbrMetallicFactor = metallicFactor,
pbrRoughnessFactor = roughnessFactor
}
adaptMeshPrimitives :: Vector Mesh.MeshPrimitive -> Adapter (Vector MeshPrimitive)
adaptMeshPrimitives = V.mapM adaptMeshPrimitive
adaptMagFilter :: Sampler.SamplerMagFilter -> MagFilter
adaptMagFilter Sampler.MAG_LINEAR = MagLinear
adaptMagFilter Sampler.MAG_NEAREST = MagNearest
adaptMagFilter mode = error $ "Invalid MagFilter: " <> show mode
adaptMinFilter :: Sampler.SamplerMinFilter -> MinFilter
adaptMinFilter Sampler.MIN_NEAREST = MinNearest
adaptMinFilter Sampler.MIN_LINEAR = MinLinear
adaptMinFilter Sampler.MIN_NEAREST_MIPMAP_NEAREST = MinNearestMipmapNearest
adaptMinFilter Sampler.MIN_NEAREST_MIPMAP_LINEAR = MinNearestMipmapLinear
adaptMinFilter Sampler.MIN_LINEAR_MIPMAP_NEAREST = MinLinearMipmapNearest
adaptMinFilter Sampler.MIN_LINEAR_MIPMAP_LINEAR = MinLinearMipmapLinear
adaptMinFilter mode = error $ "Invalid MinFilter: " <> show mode
adaptSamplerWrap :: Sampler.SamplerWrap -> SamplerWrap
adaptSamplerWrap Sampler.CLAMP_TO_EDGE = ClampToEdge
adaptSamplerWrap Sampler.MIRRORED_REPEAT = MirroredRepeat
adaptSamplerWrap Sampler.REPEAT = Repeat
adaptSamplerWrap mode = error $ "Invalid SamplerWrap: " <> show mode
adaptTextureInfo :: TextureInfo.TextureInfo a -> TextureInfo
adaptTextureInfo TextureInfo.TextureInfo{..} = TextureInfo
{ textureId = index,
textureTexCoord = texCoord
}
adaptMeshPrimitive :: Mesh.MeshPrimitive -> Adapter MeshPrimitive
adaptMeshPrimitive Mesh.MeshPrimitive{..} = do
gltf <- getGltf
buffers' <- getBuffers
return $ MeshPrimitive
{ meshPrimitiveIndices = maybe mempty (vertexIndices gltf buffers') indices,
meshPrimitiveMaterial = Material.unMaterialIx <$> material,
meshPrimitiveMode = adaptMeshPrimitiveMode mode,
meshPrimitiveNormals = maybe mempty (vertexNormals gltf buffers') normals,
meshPrimitivePositions = maybe mempty (vertexPositions gltf buffers') positions,
meshPrimitiveTexCoords = maybe mempty (vertexTexCoords gltf buffers') texCoords
}
where positions = attributes HashMap.!? attributePosition
normals = attributes HashMap.!? attributeNormal
texCoords = attributes HashMap.!? attributeTexCoord
adaptMeshPrimitiveMode :: Mesh.MeshPrimitiveMode -> MeshPrimitiveMode
adaptMeshPrimitiveMode = toEnum . Mesh.unMeshPrimitiveMode
toV3 :: (a, a, a) -> V3 a
toV3 (x, y, z) = V3 x y z
toV4 :: (a, a, a, a) -> V4 a
toV4 (w, x, y, z) = V4 w x y z