Model¶
Inherits: Mesh
C++ type: Model
Description¶
Loads and displays 3D model files in a scene. Model extends Mesh and adds support for loading OBJ and GLTF files (including binary GLB), including skeletal animation (via Animation / Bone), blend-shape morph targets, and PBR materials.
A single GLTF file can contain multiple meshes, materials, textures, skins, and animations; Doriax imports them into the scene hierarchy and makes each accessible through the API. Files with animation clips, or more than one skin, import the full glTF node tree so joints, mesh nodes, and transform-only helpers keep the parentage authored in the file. Use getAnimation() and getBone() to drive skeletal playback, and setMorphWeight() to control blend shapes. See 3D Graphics — GLTF node hierarchy.
For static multi-node GLTFs that need GPU instancing on the root entity, set ModelComponent::mergeStaticMeshes (or use Merge static model in the editor) so child transforms bake into the root mesh instead of creating child mesh entities. See 3D Graphics — Merging static model meshes.
Methods¶
| Type | Name | Langs |
|---|---|---|
| bool | loadModel | C++ | Lua |
| bool | loadOBJ | C++ | Lua |
| bool | loadGLTF | C++ | Lua |
| Animation | getAnimation | C++ | Lua |
| Animation | findAnimation | C++ | Lua |
| void | playAnimation | C++ | Lua |
| void | stopAnimations | C++ | Lua |
| Bone | getBone | C++ | Lua |
| float | getMorphWeight | C++ | Lua |
| void | setMorphWeight | C++ | Lua |
| void | resetToBindPose | C++ | Lua |
GLTF import support and limits¶
| Feature | Import behaviour |
|---|---|
| Skinning | Bone matrices are a storage buffer (Vulkan, Metal, Direct3D 11) or an unfilterable bone texture (OpenGL / OpenGL ES), so shader variants are not capped by a uniform-block size. The CPU array is still limited by MAX_BONES (default 128). The editor grows past that for large skins; export raises MAX_BONES to the largest skin in the project's scenes. A runtime load above the compiled capacity is rejected and logged. |
| Multiple skins | Supported. Each skinned mesh keeps its own joint order and inverse-bind matrices. |
| Node hierarchy | Animation clips, or more than one skin, import every glTF node as a child entity. Animation channels target those nodes, including transform-only helpers. |
| Position-only morph targets | Up to 8 targets per mesh primitive. |
| Position + normal or position + tangent morph targets | Up to 4 targets per mesh primitive. |
| Position + normal + tangent morph targets | Up to 2 targets per mesh primitive. |
| Sparse morph accessors | Supported. The loader expands sparse target data into a dense GPU buffer, including accessors that omit the optional base bufferView and therefore start from zeroes. |
The morph limit is derived from the leading target prefix that the loader actually binds. Extra trailing targets are ignored; semantics that appear only after that loaded prefix do not reduce its shader capacity. Keep the same morph semantics on every target when exporting from a DCC tool for predictable results.
Imported PBR materials also preserve GLTF OPAQUE, MASK, and BLEND alpha modes and
the alphaCutoff value used by masked materials. Blended submeshes disable depth writes
in the colour pass. See Material.
Loading rewrites every submesh, so submesh edits made outside the loader are kept in
ModelComponent::submeshOverrides and re-applied at the end of each load. The editor
fills that list when you change a submesh in Properties; in code you can add entries
yourself, keyed by the GLTF node and primitive (nodeIndex / primitiveIndex, with
nodeIndex = -1 and the material index for OBJ) and a fields mask of the values to
pin. See 3D Graphics — Editing an imported model's submeshes.
Method details¶
loadModel¶
- bool loadModel(const std::string& filename)
Loads a 3D model file from disk. Doriax automatically detects the format based on file extension:
.obj— Wavefront OBJ.gltf/.glb— GL Transmission Format (GLTF 2.0 / binary GLB)
Returns true on success. On failure, check the log for details.
loadOBJ / loadGLTF¶
- bool loadOBJ(const std::string& filename)
- bool loadGLTF(const std::string& filename)
Format-specific loaders. Use these when you want to be explicit about the format, or when the file extension is non-standard. loadGLTF() accepts both text .gltf and binary .glb files.
getAnimation / findAnimation¶
Retrieve a skeletal or morph-target animation embedded in the loaded model.
getAnimation() accesses by zero-based index; findAnimation() searches the animation
entity name. GLTF import initializes that entity name from the clip name exported by the
DCC tool, and an unnamed imported clip receives a generated name such as "Animation 0".
Both methods return an Animation object that can be started, stopped, and
configured for looping.
Renaming an animation entity changes the string accepted by findAnimation() and the
named playAnimation() overloads. The
AnimationComponent itself has no separate name property.
playAnimation / stopAnimations¶
- void playAnimation(int index)
- void playAnimation(int index, float fadeTime)
- void playAnimation(const std::string& name)
- void playAnimation(const std::string& name, float fadeTime)
- void stopAnimations(float fadeTime)
Switch the model's active clip with a smooth crossfade instead of an instant pose snap. playAnimation() fades out every other running clip on this model and fades the requested one in over fadeTime seconds; stopAnimations() fades all running clips out.
This is the recommended way to change animation state at runtime (idle → run → jump → die). During the fade both clips play and their poses are blended by weight, so the character eases between motions rather than popping. When fadeTime is omitted, the target clip's authored defaultFadeTime is used (set per-clip as AnimationComponent → Fade time). A fadeTime of 0 switches instantly.
Unlike getAnimation(), which returns a handle you start yourself, playAnimation() manages the transition for you. Set looping on the clips you intend to hold (idle, run) via Animation::loop.
Model hero(&scene);
hero.loadModel("characters/hero.glb");
// Make locomotion clips loop, then start on idle
hero.findAnimation("Idle").setLoop(true);
hero.findAnimation("Run").setLoop(true);
hero.playAnimation("Idle"); // uses each clip's Fade time
// Later, on input:
hero.playAnimation("Run", 0.2f); // crossfade to run over 0.2s
hero.playAnimation("Die", 0.1f); // snappy transition into a one-shot
local hero = Model(scene)
hero:loadModel("characters/hero.glb")
hero:findAnimation("Idle").loop = true
hero:findAnimation("Run").loop = true
hero:playAnimation("Idle") -- uses each clip's Fade time
-- Later, on input:
hero:playAnimation("Run", 0.2) -- crossfade to run over 0.2s
hero:playAnimation("Die", 0.1) -- snappy transition into a one-shot
Crossfades assume full-skeleton clips
The blend is a weight-normalized average per bone, so it looks best when both clips animate the whole skeleton (the usual case for GLTF character clips). A bone animated by only the outgoing clip holds its pose until that clip finishes fading rather than easing.
getBone¶
Returns a Bone handle by joint name or by glTF node index (id). That integer is the node's index in the glTF file, which can differ from its ordinal in a skin's joint list. A Bone inherits Object so you can read and write its local transform to override the skeleton. For example, you can aim a character's head bone toward a target while the rest of the body plays a walk cycle.
getMorphWeight / setMorphWeight¶
- float getMorphWeight(const std::string& name)
- float getMorphWeight(int id)
- void setMorphWeight(const std::string& name, float value)
- void setMorphWeight(int id, float value)
Read or write the weight of a blend-shape morph target. Weights range from 0.0 (no influence) to 1.0 (full influence). Multiple morphs can be active simultaneously. Access by exported name or by zero-based index.
resetToBindPose¶
- void resetToBindPose()
Resets every imported node (or, on the legacy skeleton path, every bone) back to the bind pose defined in the model file, clearing any programmatic or animation-driven overrides. Useful when switching between animations or when stopping all playback.