// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause /** * @class vtkGLTFDocumentLoader * @brief Deserialize a GLTF model file. * * * vtkGLTFDocument loader is an internal utility class which defines data structures and functions * with the purpose of deserializing a glTF model from a glTF file, loading its data from binary * buffers and creating vtk objects with the extracted geometry. * It contains an internal Model structure into which all loading is performed. * * The GL Transmission Format (glTF) is an API-neutral runtime asset delivery format. * A glTF asset is represented by: * - A JSON-formatted file (.gltf) containing a full scene description: node hierarchy, materials, * cameras, as well as descriptor information for meshes, animations, and other constructs * - Binary files (.bin) containing geometry and animation data, and other buffer-based data * - Image files (.jpg, .png) for textures * * For the full specification, see: * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0 */ #ifndef vtkGLTFDocumentLoader_h #define vtkGLTFDocumentLoader_h #include "GLTFSampler.h" // For "Sampler" #include "vtkIOGeometryModule.h" // For export macro #include "vtkObject.h" #include "vtkResourceStream.h" // For "vtkResourceStream" #include "vtkSmartPointer.h" // For "vtkSmartPointer" #include "vtkURILoader.h" // For "vtkURILoader" #include // For std::map #include // For std::shared_ptr #include // For std::string #include // For std::vector VTK_ABI_NAMESPACE_BEGIN class vtkCellArray; class vtkDataArray; class vtkFloatArray; class vtkImageData; class vtkMatrix4x4; class vtkPoints; class vtkPolyData; class vtkUnsignedShortArray; class VTKIOGEOMETRY_EXPORT vtkGLTFDocumentLoader : public vtkObject { public: static vtkGLTFDocumentLoader* New(); vtkTypeMacro(vtkGLTFDocumentLoader, vtkObject); void PrintSelf(ostream& os, vtkIndent indent) override; /** * Define an openGL draw target. */ enum class Target : unsigned short { ARRAY_BUFFER = 34962, ELEMENT_ARRAY_BUFFER = 34963 }; /** * Defines an accessor's type. These are defined as strings in the glTF specification. * Each type implies a specific number of components. */ enum class AccessorType : unsigned char { SCALAR, VEC2, VEC3, VEC4, MAT2, MAT3, MAT4, INVALID }; /** * Define a type for different data components. Values match with the corresponding GLenum, as * they are used in the glTF specification. */ enum class ComponentType : unsigned short { BYTE = 5120, UNSIGNED_BYTE = 5121, SHORT = 5122, UNSIGNED_SHORT = 5123, UNSIGNED_INT = 5125, FLOAT = 5126 }; /* The following structs help deserialize a glTF document, representing each object. As such, * their members mostly match with the specification. Default values and boundaries are set * according to the specification. * Most of these structs contain a name property, which is optional, and, while being loaded, is * not currently exploited by the loader. * They are mostly root-level properties, and once created, are stored into vectors in the Model * structure. */ /** * This struct describes a glTF bufferView object. * A bufferView represents a subset of a glTF binary buffer */ struct BufferView { int Buffer; int ByteOffset; int ByteLength; int ByteStride; int Target; std::string Name; }; /** * This struct describes an accessor glTF object. * An accessor defines a method for retrieving data as typed arrays from a bufferView. * They contain type information, as well as the location and size of of the data within the * bufferView. */ struct Accessor { /** * This struct describes an accessor.sparse glTF object. * This object describes the elements that deviate from their initialization value. */ struct Sparse { int Count; int IndicesBufferView; int IndicesByteOffset; ComponentType IndicesComponentType; int ValuesBufferView; int ValuesByteOffset; }; int BufferView; int ByteOffset; ComponentType ComponentTypeValue; bool Normalized; int Count; unsigned int NumberOfComponents; AccessorType Type; std::vector Max; std::vector Min; bool IsSparse; Sparse SparseObject; std::string Name; }; /** * This struct describes a glTF Morph Target object. * A Morph Target is a morphable Mesh where primitives' attributes are obtained by adding the * original attributes to a weighted sum of targets attributes. * Only three attributes (position, normals and tangents) are supported. */ struct MorphTarget { // accessor indices from the .gltf file, the map's keys correspond to attribute names std::map AttributeIndices; // attribute values std::map> AttributeValues; }; /** * This struct describes a glTF primitive object. * primitives specify vertex attributes, as well as connectivity information for a draw call. * A primitive also specifies a material and GPU primitive type (e.g: triangle set) * Data is first stored as integer indices, pointing to different accessors, then extracted into * vtk data structures and finally used to build a vtkPolyData object. */ struct Primitive { // accessor indices from the .glTF file, the map's keys correspond to attribute names std::map AttributeIndices; int IndicesId; vtkSmartPointer Indices; // attribute values from buffer data std::map> AttributeValues; vtkSmartPointer Geometry; std::vector Targets; int Material; int Mode; int CellSize; // 1, 2 or 3, depending on draw mode // Primitive-specific extension metadata struct Extensions { // KHR_draco_mesh_compression extension // Only metadata are read (decoding and modifying the internal model is not done yet) struct KHRDracoMeshCompression { int BufferView = -1; std::map AttributeIndices; }; Primitive::Extensions::KHRDracoMeshCompression KHRDracoMetaData; }; Primitive::Extensions ExtensionMetaData; }; /** * This struct describes a glTF node object. * A node represents an object within a scene. * Nodes can contain transform properties (stored as vtkMatrix4x4 objects) as well as indices to * children nodes, forming a hierarchy. No node may be a direct descendant of more than one node. */ struct Node { std::vector Children; int Camera; int Mesh; int Skin; vtkSmartPointer Transform; vtkSmartPointer GlobalTransform; bool TRSLoaded; vtkSmartPointer Matrix; std::vector InitialRotation; std::vector InitialTranslation; std::vector InitialScale; std::vector InitialWeights; std::vector Rotation; std::vector Translation; std::vector Scale; std::vector Weights; // Object-specific extension metadata struct Extensions { // KHR_lights_punctual extension struct KHRLightsPunctual { int Light = -1; }; Node::Extensions::KHRLightsPunctual KHRLightsPunctualMetaData; }; Node::Extensions ExtensionMetaData; std::string Name; void UpdateTransform(); }; /** * This struct describes a glTF mesh object. * A mesh contains an array of primitives. */ struct Mesh { std::vector Primitives; std::vector Weights; std::string Name; }; /** * This struct describes a glTF textureInfo object, mostly used in material descriptions * They contain two indexes, one to a texture object, and the second being used to construct a * string with the format TEXCOORD_, which references a key in mesh.primitives.attributes. */ struct TextureInfo { int Index = -1; int TexCoord = -1; }; /** * This struct describes a glTF image object. * Images can be referenced either by URI or with a bufferView. mimeType is required in this case. */ struct Image { int BufferView; std::string MimeType; std::string Uri; vtkSmartPointer ImageData; std::string Name; }; /** * This struct describes a glTF material object. * glTF materials are defined using the metallic-roughness model. The values for most properties * can be defined using either factors or textures (via textureInfo). * Materials also define normal, occlusion and emissive maps. */ struct Material { enum class AlphaModeType : unsigned char { OPAQUE, MASK, BLEND }; struct PbrMetallicRoughness { TextureInfo BaseColorTexture; std::vector BaseColorFactor; TextureInfo MetallicRoughnessTexture; float MetallicFactor; float RoughnessFactor; }; PbrMetallicRoughness PbrMetallicRoughness; TextureInfo NormalTexture; double NormalTextureScale; TextureInfo OcclusionTexture; double OcclusionTextureStrength; TextureInfo EmissiveTexture; std::vector EmissiveFactor; AlphaModeType AlphaMode; double AlphaCutoff; bool DoubleSided; std::string Name; // extension KHR_materials_unlit bool Unlit; }; /** * This struct describes a glTF texture object. * A texture is defined two indexes, one to an image resource, and the second to a sampler index. */ struct Texture { int Sampler; int Source; std::string Name; }; /** * This struct describes a glTF sampler object. * Samplers specify filter and wrapping options corresponding to GL types. */ struct Sampler : public GLTFSampler { std::string Name; }; /** * This struct describes a glTF scene object. * A scene contains a set of indices of nodes to render. * Scene.Nodes can be empty, in which case nothing is required to be rendered */ struct Scene { std::vector Nodes; std::string Name; }; /** * This struct describes a glTF asset. * It is meant to mimic a .glTF file, containing all its root-level properties, stored as arrays * when relevant. */ struct Skin { std::vector> InverseBindMatrices; std::vector Joints; int InverseBindMatricesAccessorId; int Skeleton; std::string Name; }; /** * This struct describes a glTF animation object. * Animations contain multiple channel and sampler objects. * Channels define the target node and value to be animated. * Samplers define keyframes and how to interpolate in between them. */ struct Animation { struct Sampler { enum class InterpolationMode : unsigned char { LINEAR, STEP, CUBICSPLINE }; InterpolationMode Interpolation; unsigned int Input; unsigned int Output; int NumberOfComponents; vtkSmartPointer InputData; vtkSmartPointer OutputData; /** * Get the interpolated animation output at time t */ void GetInterpolatedData(float t, size_t numberOfComponents, std::vector* output, bool forceStep = false, bool isRotation = false) const; }; struct Channel { enum class PathType : unsigned char { ROTATION, TRANSLATION, SCALE, WEIGHTS }; int Sampler; int TargetNode; PathType TargetPath; }; float Duration; // In seconds std::vector Channels; std::vector Samplers; std::string Name; }; /** * This struct describes a glTF camera object. * glTF can define both perpective or orthographic cameras. * Some of the struct's members will be unused depending on the camera type. */ struct Camera { // common properties double Znear; double Zfar; bool IsPerspective; // if not, camera mode is orthographic // perspective double Xmag; double Ymag; // orthographic double Yfov; double AspectRatio; std::string Name; }; /** * This struct contains extension metadata. * This is for extension properties in the root-level 'extensions' object. * Object-specific extension metadata is added directly to the extended object (see Node for an * example) */ struct Extensions { // KHR_lights_punctual extension struct KHRLightsPunctual { struct Light { enum class LightType : unsigned char { DIRECTIONAL, POINT, SPOT }; LightType Type; std::vector Color; double Intensity; double Range; // Type-specific parameters double SpotInnerConeAngle; double SpotOuterConeAngle; std::string Name; }; std::vector Lights; }; KHRLightsPunctual KHRLightsPunctualMetaData; }; /** * This struct contains all data from a gltf asset */ struct Model { std::vector Accessors; std::vector Animations; std::vector> Buffers; std::vector BufferViews; std::vector Cameras; std::vector Images; std::vector Materials; std::vector Meshes; std::vector Nodes; std::vector Samplers; std::vector Scenes; std::vector Skins; std::vector Textures; Extensions ExtensionMetaData; std::string BufferMetaData; int DefaultScene; std::string FileName; vtkSmartPointer Stream; vtkSmartPointer URILoader; }; /** * Apply the specified animation, at the specified time value t, to the internal Model. Changes * node transforms and morphing weights. */ bool ApplyAnimation(float t, int animationId, bool forceStep = false); /** * Restore the transforms that were modified by an animation to their initial state */ void ResetAnimation(int animationId); ///@{ /** * @brief Load the binary part of a binary glTF (.glb) file. * Input can either be a file (LoadFileBuffer) or a stream (LoadStreamBuffer). * @return false if no valid binary part was found. */ bool LoadFileBuffer(VTK_FILEPATH const std::string& fileName, std::vector& glbBuffer); bool LoadStreamBuffer(vtkResourceStream* stream, std::vector& glbBuffer); ///@} ///@{ /** * @brief Reset internal Model struct, and serialize glTF metadata (all json information) into it. * * To load buffers, use LoadModelData. * Input can either be a file (LoadModelMetaDataFromFile) or a stream + optional URI loader. * * @return `true` if internal model is correctly filled, `false` otherwise. */ bool LoadModelMetaDataFromFile(VTK_FILEPATH const std::string& FileName); bool LoadModelMetaDataFromStream(vtkResourceStream* stream, vtkURILoader* loader = nullptr); ///@} /** * Load buffer data into the internal Model. */ bool LoadModelData(const std::vector& glbBuffer); /** * Converts the internal Model's loaded data into more convenient vtk objects. */ bool BuildModelVTKGeometry(); /** * Get the internal Model. */ std::shared_ptr GetInternalModel(); /** * Returns the number of components for a given accessor type. */ static unsigned int GetNumberOfComponentsForType(vtkGLTFDocumentLoader::AccessorType type); /** * Get the list of extensions that are supported by this loader */ virtual std::vector GetSupportedExtensions(); /** * Get the list of extensions that are used by the current model */ const std::vector& GetUsedExtensions(); /** * Concatenate the current node's local transform to its parent's global transform, storing * the resulting transform in the node's globalTransform field. Then does the same for the current * node's children. * Recursive. */ void BuildGlobalTransforms(unsigned int nodeIndex, vtkSmartPointer parentTransform); /** * Build all global transforms */ void BuildGlobalTransforms(); /** * Compute all joint matrices of the skin of a specific node */ static void ComputeJointMatrices(const Model& model, const Skin& skin, Node& node, std::vector>& jointMats); /** * Some extensions require a preparation on the model before building VTK objects. * For example, a subclass supporting KHR_draco_mesh_compression could override this function * to consume the extension metadata and modify the internal model. * This is not done in VTK yet which does not modify the internal model once read. */ virtual void PrepareData() {} ///@{ /** * Set/Get the Stream start, where the GLB starts. By default it is 0, * but can be different than 0 for file formats have a GLB embeded in it, * for instance 3D Tiles B3DM. */ vtkSetMacro(GLBStart, vtkTypeInt64); vtkGetMacro(GLBStart, vtkTypeInt64); ///@} protected: vtkGLTFDocumentLoader() = default; ~vtkGLTFDocumentLoader() override = default; private: struct AccessorLoadingWorker; struct SparseAccessorLoadingWorker; template struct BufferDataExtractionWorker; vtkGLTFDocumentLoader(const vtkGLTFDocumentLoader&) = delete; void operator=(const vtkGLTFDocumentLoader&) = delete; /** * Load inverse bind matrices from buffers into the model's Skin structs. */ bool LoadSkinMatrixData(); /** * Uses the Primitive's attributeIndices member to extract all vertex attributes from accessors * into the Primitive's corresponding vtk arrays. */ bool ExtractPrimitiveAttributes(Primitive& primitive); /** * Uses all the primitive's different accessor indices to extract the corresponding data from * binary buffers. These arrays are then stored in the primitive's different vtk arrays. * Extracts connectivity information then calls ExtractPrimitiveAttributes for the vertex * attributes */ bool ExtractPrimitiveAccessorData(Primitive& primitive); /** * Creates and populates the Primitive's geometry vtkPolyData member with all the vertex attribute * and connectivity information the Primitive contains. */ bool BuildPolyDataFromPrimitive(Primitive& primitive); /** * Load keyframes from buffers. */ bool LoadAnimationData(); /** * Load Model's Images into vtkImageData objects, from filesystem and bufferView when specified. */ bool LoadImageData(); std::shared_ptr InternalModel; static const std::vector SupportedExtensions; std::vector UsedExtensions; vtkTypeInt64 GLBStart = 0; }; VTK_ABI_NAMESPACE_END #endif