class new in Git master
GltfImporterglTF importer plugin
Imports glTF (*.gltf
) and binary glTF (*.glb
) files. You can use GltfSceneConverter to encode scenes into this format.
Usage
This plugin depends on the Trade library and the AnyImageImporter plugin and is built if MAGNUM_WITH_GLTFIMPORTER
is enabled when building Magnum Plugins. To use as a dynamic plugin, load "GltfImporter"
via Corrade::
Additionally, if you're using Magnum as a CMake subproject, bundle the magnum-plugins repository and do the following:
set(MAGNUM_WITH_ANYIMAGEIMPORTER ON CACHE BOOL "" FORCE) add_subdirectory(magnum EXCLUDE_FROM_ALL) set(MAGNUM_WITH_GLTFIMPORTER ON CACHE BOOL "" FORCE) add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL) # So the dynamically loaded plugin gets built implicitly add_dependencies(your-app MagnumPlugins::GltfImporter)
To use as a static plugin or as a dependency of another plugin with CMake, put FindMagnumPlugins.cmake into your modules/
directory, request the GltfImporter
component of the MagnumPlugins
package and link to the MagnumPlugins::GltfImporter
target:
find_package(MagnumPlugins REQUIRED GltfImporter) # ... target_link_libraries(your-app PRIVATE MagnumPlugins::GltfImporter)
See Downloading and building plugins, Plugin usage with CMake, Loading and using plugins and File format support for more information.
Behavior and limitations
The plugin supports ImporterFeature::
The content of the global extensionsRequired array is checked against all extensions supported by the plugin. If a glTF file requires an unknown extension, the import will fail. This behaviour can be disabled with the ignoreRequiredExtensions
configuration option.
Import of morph data is not supported at the moment.
The plugin recognizes ImporterFlag::
Scene import
- Imported scenes always have SceneMappingType::
UnsignedInt and are always 3D. The objectCount() returns count of all nodes in the file, while SceneData:: mappingBound() returns an upper bound on node IDs contained in a particular scene. - Nodes that are not referenced by any scene are ignored.
- All objects contained in a scene have a SceneField::
Parent (of type SceneFieldType:: Int). Size of this field is the count of nodes contained in the scene. The mapping is unordered and may be sparse if the file contains multiple scenes or nodes not referenced by any scene. - All nodes that contain transformation matrices or TRS components have a SceneField::
Transformation (of type SceneFieldType:: Matrix4x4). This field is not present if all such nodes have TRS components, in which a matrix is considered redundant. Nodes that don't have any transformation matrix nor a TRS component don't have this field assigned. - If any node contains a translation, a SceneField::
Translation (of type SceneFieldType:: Vector3) is present; if any node contains a rotation, a SceneField:: Rotation (of type SceneFieldType:: Quaternion) is present; if any node contains a scaling, a SceneField:: Scaling (of type SceneFieldType:: Vector3) is present. - If the scene references meshes, a SceneField::
Mesh (of type SceneFieldType:: UnsignedInt) is present. If any of the referenced meshes have assigned materials, SceneField:: MeshMaterial (of type SceneFieldType:: Int) is present as well. While a single node can only reference a single mesh at most, in case it references a multi-primitive mesh, it's represented as several SceneField:: Mesh (and SceneField:: MeshMaterial) assignments. See Mesh import and Material import for further details. - If the scene references skins, a SceneField::
Skin (of type SceneFieldType:: UnsignedInt) is present. A single node can only reference one skin at most. See Animation and skin import for further details. - If the scene references cameras, a SceneField::
Camera (of type SceneFieldType:: UnsignedInt) is present. A single node can only reference one camera at most. See Camera import for further details. - If the scene references lights, a SceneField::
Light (of type SceneFieldType:: UnsignedInt) is present. A single node can only reference one light at most. See Light import for further details. - If node rotation quaternion is not normalized, the importer prints a warning and normalizes it. Can be disabled per-object with the
normalizeQuaternions
configuration option. - Node extras are imported as custom scene fields, with names exposed through sceneFieldName() / sceneFieldForName() right upon opening the file. The
extras
property has to be an object, otherwise it's ignored with a warning. Boolean values are imported as SceneFieldType::Bit, numeric values are implicitly imported as SceneFieldType:: Float and the type can be overriden using the [customSceneFieldTypes]
configuration group, string values are imported as SceneFieldType::StringOffset32. Homogeneous arrays with boolean, numeric and string values are imported as multiple values of the same field, same approach is taken if the extras
property contains the same key multiple times. Objects are processed recursively, with field names for nested keys being separated with dots. Other value types, heterogeneous arrays and values that don't have a consistent type for given key across all nodes are ignored with a warning. - SceneField::
Mesh and SceneField:: MeshMaterial fields are always marked with SceneFieldFlag:: MultiEntry. No other builtin fields can have multiple entries for a single object. Node extras
that were parsed from an array are also marked with SceneFieldFlag::MultiEntry in order to unambiguously distinguish them from non-array values.
Animation and skin import
- Linear quaternion rotation tracks are postprocessed in order to make it possible to use the faster Math::
lerp() / Math:: slerp() functions instead of Math:: lerpShortestPath() / Math:: slerpShortestPath(). Can be disabled per-animation with the optimizeQuaternionShortestPath
configuration option. This doesn't affect spline-interpolated rotation tracks. - If linear quaternion rotation tracks are not normalized, the importer prints a warning and normalizes them. Can be disabled per-animation with the
normalizeQuaternions
configuration option. This doesn't affect spline-interpolated rotation tracks. - Skin
skeleton
property is not imported - Morph target animations are not supported
- Animation tracks are always imported with Animation::
Extrapolation:: Constant, because glTF doesn't support anything else - It's possible to request all animation clips to be merged into one using the
mergeAnimationClips
option in order to for example preserve cinematic animations when using the Blender glTF exporter (as it otherwise outputs a separate clip for each object). When this option is enabled, animationCount() always report either0
or1
and the merged animation has no name. With this option enabled, however, it can happen that multiple conflicting tracks affecting the same node are merged in the same clip, causing the animation to misbehave.
Camera import
- Cameras in glTF are specified with vertical FoV and vertical:horizontal aspect ratio, these values are recalculated for horizontal FoV and horizontal:vertical aspect ratio as is common in Magnum
Light import
- The importer supports the KHR_
lights_ punctual extension
Mesh import
- Indices are imported as either MeshIndexType::
UnsignedByte, MeshIndexType:: UnsignedShort or MeshIndexType:: UnsignedInt - Positions are imported as VertexFormat::
Vector3, VertexFormat:: Vector3ub, VertexFormat:: Vector3b, VertexFormat:: Vector3us, VertexFormat:: Vector3s, VertexFormat:: Vector3ubNormalized, VertexFormat:: Vector3bNormalized, VertexFormat:: Vector3usNormalized or VertexFormat:: Vector3sNormalized (which includes the additional types specified by KHR_ mesh_ quantization) - Normals are imported as VertexFormat::
Vector3, VertexFormat:: Vector3bNormalized or VertexFormat:: Vector3sNormalized - Tangents are imported as VertexFormat::
Vector4, VertexFormat:: Vector4bNormalized or VertexFormat:: Vector4sNormalized - Texture coordinates are imported as VertexFormat::
Vector2, VertexFormat:: Vector2ub, VertexFormat:: Vector2b, VertexFormat:: Vector2us, VertexFormat:: Vector2s, VertexFormat:: Vector2ubNormalized, VertexFormat:: Vector2bNormalized, VertexFormat:: Vector2usNormalized or VertexFormat:: Vector2sNormalized (which includes the additional types specified by KHR_ mesh_ quantization). The data are by default Y-flipped on import unless textureCoordinateYFlipInMaterial
is either explicitly enabled, or if the file contains non-normalized integer or normalized signed integer texture coordinates (which can't easily be flipped). In that case texture coordinate data are kept as-is and materials provide a texture transformation that does the Y-flip instead. - Colors are imported as VertexFormat::
Vector3, VertexFormat:: Vector4, VertexFormat:: Vector3ubNormalized, VertexFormat:: Vector4ubNormalized, VertexFormat:: Vector3usNormalized or VertexFormat:: Vector4usNormalized - Skin joint IDs are imported as arrays of VertexFormat::
UnsignedInt, VertexFormat:: UnsignedByte or VertexFormat:: UnsignedShort, skin weights then as arrays of VertexFormat:: Float, VertexFormat:: UnsignedByteNormalized or VertexFormat:: UnsignedShortNormalized. The MeshData:: attributeArraySize() is currently always 4
, but is reserved to change (for example representing two consecutive sets as a single array of 8 items). For backwards compatibility, unless thecompatibilitySkinningAttributes
configuration option or MAGNUM_BUILD_ DEPRECATED is disabled, these are also exposed as custom "JOINTS"
and"WEIGHTS"
attributes with VertexFormat::Vector4ub / VertexFormat:: Vector4us and VertexFormat:: Vector4 / VertexFormat:: Vector4ubNormalized / VertexFormat:: Vector4usNormalized (non-array) formats, respectively, with as many instances of these as needed to cover all array items. The compatibility attributes alias the builtin ones, i.e. point to the same memory, so their presence causes no extra overhead. - Per-vertex object ID attribute is imported as either VertexFormat::
UnsignedInt, VertexFormat:: UnsignedShort or VertexFormat:: UnsignedByte. By default _OBJECT_ID
is the recognized name, use theobjectIdAttribute
configuration option to change the identifier that's being looked for. - If a builtin attribute doesn't match the above-specified types, a message is printed to Warning and it's imported as a custom attribute instead, with its name available through meshAttributeName(). Such attributes are an invalid glTF but this allows the application to import them and fix in a postprocessing step. Enable the
strict
configuration option to fail the import in such cases instead. - Multi-primitive meshes are split into individual meshes, nodes that reference a multi-primitive mesh have multiple SceneField::
Mesh (and SceneField:: MeshMaterial) entries in the imported SceneData. - Attribute-less meshes either with or without an index buffer are supported, however since glTF has no way of specifying vertex count for those, returned Trade::
MeshData:: vertexCount() is set to 0
- Morph targets, if present, have their attributes imported with Trade::
MeshData:: attributeMorphTargetId() set to index of the morph target. Non-sparse buffers aren't supported for those at the moment.
By default, the mesh import silently allows certain features that aren't strictly valid according to the glTF specification, such as 32-bit integer VertexFormat, because they're useful in general. Enable the strict
configuration option to fail the import in such cases instead.
Custom and unrecognized vertex attributes of allowed types are present in the imported meshes as well. Their mapping to/from a string can be queried using meshAttributeName() and meshAttributeForName(). Attributes with unsupported types (such as non-normalized integer matrices) cause the import to fail.
Material import
- If present, builtin metallic/
roughness material is imported, setting MaterialType:: PbrMetallicRoughness on the MaterialData. - If the KHR_
materials_ pbrSpecularGlossiness extension is present, its properties are imported with MaterialType:: PbrSpecularGlossiness present in material types. - Additional normal, occlusion and emissive maps are imported, together with related properties
- If the KHR_
materials_ unlit extension is present, MaterialType:: Flat is set in material types, replacing MaterialType:: PbrMetallicRoughness or MaterialType:: PbrSpecularGlossiness. - If the KHR_
materials_ clearcoat extension is present, MaterialType:: PbrClearCoat is set in material types, and a new layer with clearcoat properties is added - Custom texture coordinate sets as well as KHR_
texture_ transform properties are imported on all textures. - Unrecognized material extensions are imported as custom layers with a
#
prefix. Extension properties are imported with their raw names and types, the following of which are supported:- MaterialAttributeType::
String - MaterialAttributeType::
Bool - All numbers as MaterialAttributeType::
Float to avoid inconsistency with different glTF exporters. The only exception are texture indices and coordinate sets inside textureInfo objects, which get imported as MaterialAttributeType:: UnsignedInt, consistently with types of builtin *Texture
and*TextureCoordinates
MaterialAttribute entries. - Number arrays as MaterialAttributeType::
Vector2 / MaterialAttributeType:: Vector3 / MaterialAttributeType:: Vector4. Empty arrays, arrays of size 5 or higher as well as arrays containing anything that isn't a number are ignored. - textureInfo objects, including all attributes handled for regular textures. Texture attributes are prefixed by the name of the object: e.g. if an extension has a
someTexture
property, the texture index, matrix, coordinate set and scale would be imported assomeTexture
,someTextureMatrix
,someTextureCoordinates
andsomeTextureScale
, consistently with builtin texture-related MaterialAttribute names. Non-texture object types are ignored. If you handle any of these custom material extensions, it may make sense to enable theignoreRequiredExtensions
configuration option.
- MaterialAttributeType::
- Extras metadata is imported into the base material layer. The
extras
property has to be an object, otherwise it's ignored with a warning. Type support is the same as for unrecognized material extensions, except for textureInfo objects — contrary to glTF material extensions, where sub-objects can be assumed to contain texture info, theextras
can contain just anything. - If the on-by-default
phongMaterialFallback
configuration option is enabled, the importer provides a Phong fallback for backwards compatibility:- MaterialType::
Phong is added to material types - Base color and base color texture along with custom texture coordinate set and transformation, if present, is exposed as a diffuse color and texture, unless already present together with specular color / texture from the specular/glossiness material
- All other PhongMaterialData values are is kept at their defaults
- MaterialType::
Texture and image import
- Texture type is Trade::
TextureType:: Texture2D. If the experimentalKhrTextureKtx
configuration option is enabled, it can be also Trade::TextureType:: Texture2DArray, as described below. - Z coordinate of Trade::
TextureData:: wrapping() is always SamplerWrapping:: Repeat, as glTF doesn't support 3D textures glTF leaves the defaults of sampler properties to the application, the following defaults have been chosen for this importer:
- Minification/magnification/mipmap filter: SamplerFilter::
Linear, SamplerMipmap:: Linear - Wrapping (all axes): SamplerWrapping::
Repeat
- Minification/magnification/mipmap filter: SamplerFilter::
The importer supports the following extensions for image types not defined in the core glTF 2.0 specification: MSFT_
texture_ dds for DirectDraw Surface images ( *.dds
), EXT_texture_ webp for WebP images ( *.webp
), KHR_texture_ basisu for Khronos Texture 2.0 images ( *.ktx2
) with Basis Universal supercompression, as well as the original provisionalGOOGLE_texture_basis
extension for referencing plain Basis Universal files (*.basis
). There was no formal specification of the extension but the use is like below, equivalently to Basis own glTF example:{ ... "textures": [ { "extensions": { "GOOGLE_texture_basis": { "source": 0 } } } ], "images": [ { "mimeType": "image/x-basis", "uri": "texture.basis" } ], "extensionsUsed": [ "GOOGLE_texture_basis" ], "extensionsRequired": [ "GOOGLE_texture_basis" ] }
The MIME type (if one exists) is ignored by the importer. Delegation to the correct importer alias happens via AnyImageImporter which uses the file extension or buffer content to determine the image type.
- If a texture contains and extension together with a fallback source, or multiple extensions, the image referenced by the first recognized extension appearing in the file will be picked, others ignored.
2D array texture support
If the experimentalKhrTextureKtx
configuration option is enabled, textures can contain also the proposed KHR_*.ktx2
files the same way as other texture extensions listed above, it can also reference 2D array textures. The importer behavior is as follows and may get adapted to eventual changes in the extension proposal:
- If a
texture
object contains theKHR_texture_ktx
extension with alayer
property, the image it references is assumed to be a 2D array. Otherwise, it's assumed to be 2D. The*.ktx2
file itself is not checked upfront as that goes against the goal of accessing the data only once actually needed. - If the same image is referenced as both 2D and 2D array, it's an import error. In other words, a texture referencing a 2D array image has to have the
layer
property even if it would be zero. - Images that are assumed to be 2D arrays are available through image3D() and related APIs, other images through image2D(). The sum of image2DCount() and image3DCount() is the count of glTF image objects in the file.
- Textures referencing the same 2D array image are collapsed together and are exposed as a TextureData with TextureType::
Texture2DArray, with TextureData:: image() giving an index of a 3D image instead of a 2D one. Differing samplers are not taken into account, the resulting TextureData will always use the sampler properties referenced from the first texture. This means that, in this case, textureCount() may be less the actual count of glTF texture objects in the file. - Materials referencing
KHR_texture_ktx
textures with thelayer
property then get a*TextureLayer
attribute. I.e., if MaterialAttribute::BaseColorTexture is a 2D array texture, the material will get a MaterialAttribute:: BaseColorTextureLayer as well, containing the value of the layer
property.
Plugin-specific configuration
It's possible to tune various output options through configuration(). See below for all options and their default values.
[configuration] # Don't fail the import and only print a warning if an unknown or unsupported # extension is listed in extensionsRequired. Some things might be missing or # not get imported correctly if this is enabled. ignoreRequiredExtensions=false # Allow only strictly valid glTF files. If enabled, the following cause a # particular data import to fail: # - Meshes with zero vertices, zero indices or zero attributes # - Meshes with 32-bit integer attributes # - Builtin mesh attributes with invalid types (such as unnormalized colors). # By default, those produce a warning and are imported as custom attributes # in order to make it possible to fix them post-import. # This can be controlled separately for each data import. strict=false # Optimize imported linearly-interpolated quaternion animation tracks to # ensure shortest path is always chosen. This can be controlled separately # for each animation import. optimizeQuaternionShortestPath=true # Normalize transformation quaternions and linearly-interpolated quaternion # animation tracks, if they are not already. Note that spline-interpolated # quaternion animation tracks are not patched. This can be controlled # separately for each object/animation import. normalizeQuaternions=true # Merge all animations into a single clip. Useful for preserving cinematic # animations when using the Blender glTF exporter, as it exports animation of # every object as a separate clip. For more information see # https://blender.stackexchange.com/q/5689 and # https://github.com/KhronosGroup/glTF-Blender-Exporter/pull/166. mergeAnimationClips=false # Perform Y-flip for texture coordinates in a material texture transform. By # default texture coordinates are Y-flipped directly in the mesh data to # avoid the need to supply texture transformation matrix to a shader, # enabling this will cause all texture coordinate data to be unchanged and # instead all materials will have a Y-flipping texture transformation # present. Note that this flag has to be enabled before opening a file, # changing it during import will have undefined behavior. textureCoordinateYFlipInMaterial=false # The non-standard MeshAttribute::ObjectId is by default recognized under # this name. Change if your file uses a different identifier. objectIdAttribute=_OBJECT_ID # Expose MeshAttribute::JointIds and Weights under custom "JOINTS" and # "WEIGHTS" aliases for backwards compatibility with code that relies on # those being present. If Magnum is built with MAGNUM_BUILD_DEPRECATED # disabled, no aliases are provided and this option is ignored. compatibilitySkinningAttributes=true # Provide basic Phong material attributes even for PBR materials in order to # be compatible with PhongMaterialData workflows from version 2020.06 and # before. This option will eventually become disabled by default. phongMaterialFallback=true # Experimental KHR_texture_ktx support, which enables use of 2D array # textures. The extension is not stabilized yet, thus the implementation may # not reflect latest changes to the proposal. experimentalKhrTextureKtx=false # By default, numeric extra properties of scene nodes are imported as custom # SceneFieldType::Float fields. To override this for fields of particular # names, add <name>=<type> entries to this group, where <type> is Float, # UnsignedInt or Int. Properties that don't fit into given type will be # ignored. The overrides only have an effect if set before a file is opened. [configuration/customSceneFieldTypes]
See Editing plugin-specific configuration for more information and an example showing how to edit the configuration values.
Access to internal importer state
The glTF JSON is internally parsed using Utility::
- Calling importerState() returns a pointer to the Utility::
Json instance. If you use this class statically, you get the concrete type instead of a const void*
pointer as returned by AbstractImporter::importerState(). If not, it's allowed to cast away the const
on a mutable importer instance to access the parsing APIs. - Importer state on data class instances returned from this importer return pointers to Utility::
JsonToken of particular glTF objects: - AnimationData::
importerState() returns a glTF animation object, or nullptr
if themergeAnimationClips
option is enabled - CameraData::
importerState() returns a glTF camera object - ImageData::
importerState() returns a glTF image object - LightData::
importerState() returns a glTF light object - MaterialData::
importerState() returns a glTF material object - MeshData::
importerState() returns a glTF mesh primitive object. You can access the enclosing mesh object in a third-level Utility:: JsonToken:: parent(). - SceneData::
importerState() returns a glTF scene object and all objects have a SceneField:: ImporterState with their own glTF node object - SkinData::
importerState() returns a glTF skin object - TextureData::
importerState() returns a glTF texture object. You can access the glTF sampler object by going through the top-level glTF object accessible via Utility:: Json:: root().
- AnimationData::
Be aware that not all of the JSON may be parsed when accessed — where possible, the importerimplementation defers parsing only to when a particular data is accessed, and tokens unrecognized by the importers may be left unparsed. In order to parse what you need, do it through the Utility::
Containers::Pointer<Trade::AbstractImporter> importer = …; Containers::Optional<Trade::MeshData> mesh = importer->mesh(…); /* Get a mutable reference to the Json instance so we can parse using it */ Utility::Json& gltf = *static_cast<Utility::Json*>( const_cast<void*>(importer->importerState()) ); /* Get the outline indices accessor, if present. Can't assume anything is parsed, so call parseObject() and parseUnsignedInt() before accessing every value. */ Containers::Optional<UnsignedInt> indices; if(const Utility::JsonToken* gltfExtensions = gltf.parseObject( *static_cast<const Utility::JsonToken*>(mesh->importerState()))->find("extensions")) { if(const Utility::JsonToken* gltfCesiumPrimitiveOutline = gltf.parseObject(*gltfExtensions)->find("CESIUM_primitive_outline")) { indices = gltf.parseUnsignedInt(( *gltf.parseObject(*gltfCesiumPrimitiveOutline))["indices"]); } }
Base classes
- class AbstractImporter
- Base for importer plugins.
Derived classes
- class CgltfImporter deprecated in Git master
- glTF importer plugin
Constructors, destructors, conversion operators
- GltfImporter() deprecated in Git master explicit
- Default constructor.
-
GltfImporter(PluginManager::
Manager<AbstractImporter>& manager) deprecated in Git master explicit - Constructor.
-
GltfImporter(PluginManager::
AbstractManager& manager, const Containers:: StringView& plugin) explicit - Plugin manager constructor.
Public functions
-
auto importerState() -> Utility::
Json* - Importer state.
-
auto importerState() const -> const Utility::
Json*
Function documentation
Magnum:: Trade:: GltfImporter:: GltfImporter() explicit
Default constructor.
Magnum:: Trade:: GltfImporter:: GltfImporter(PluginManager:: Manager<AbstractImporter>& manager) explicit
Constructor.
Utility:: Json* Magnum:: Trade:: GltfImporter:: importerState()
Importer state.
See class documentation for more information.
const Utility:: Json* Magnum:: Trade:: GltfImporter:: importerState() const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.