Importing 3-D Models¶
Model3D loads a 3-D model file – geometry, UVs, textures, PBR
materials, node hierarchy and rigid animation – and gives you an ordinary Algan
Mob.
from algan import *
model = Model3D('dragon.glb', fit_to_size=2.0).scale(3).spawn()
with Seq(runtime=4, easing=easings.identity):
model.rotate(360, UP)
Scene.save_video()
File Formats¶
Formats |
Requirements |
|---|---|
|
Loaded through |
|
Loaded through |
glTF / glB is the format to prefer: it is the best supported, needs no extra install, and carries PBR materials and embedded textures.
To use FBX, install the extra and the native library:
pip install "algan[fbx]"
pyassimp is only a ctypes wrapper, so the native assimp library must be
installed separately – conda install -c conda-forge assimp,
apt install libassimp5, brew install assimp, or an assimp*.dll on
PATH on Windows. Algan raises an error naming the missing piece if it cannot
find it.
Model paths are resolved against the working directory and then your script’s directory, like every other Algan asset.
Scale and Position¶
Model files use wildly inconsistent unit scales – one file’s “1” is a metre,
another’s is a centimetre. fit_to_size recentres the model and uniformly
scales it so its bounding-box diagonal is that many world units, which makes an unfamiliar
asset usable immediately:
# Predictable size regardless of the file's units.
model = Model3D('asset.glb', fit_to_size=3).spawn()
# Or take the file's own scale and adjust by hand.
model = Model3D('asset.glb').scale(0.01).spawn()
After that it is a normal Mob: scale(),
move_to(),
rotate() and
scale_to_height() all behave as usual.
Materials and Textures¶
By default Algan applies each imported material’s PBR parameters – metalness,
roughness, emissive – as a
MeshStandardMaterial, so imported meshes shade
with Cook-Torrance GGX and respond correctly to your lighting. Diffuse texture maps
and tangent-space normal maps are loaded and applied too.
Argument |
Effect |
|---|---|
|
Apply each material’s PBR parameters. Default True; False keeps Algan’s default lit shader. |
|
Load diffuse texture maps. Default True; False (or a failed load) falls back to the material’s flat base color. |
|
Apply tangent-space normal maps. Default True. Requires per-vertex UVs. |
|
Use the mesh’s authored per-vertex normals. Default True; False derives flat per-face normals at render time, for a low-poly look. |
Note
Batches carrying a normal map are routed automatically to the general wavefront tracer, which supports per-fragment normal perturbation. That is a performance consideration, not something you have to configure – but see Performance and Quality if an imported model renders more slowly than you expect.
Because the materials land on the meshes as ordinary Algan materials, their properties are animatable attributes like any other:
model = Model3D('robot.glb', fit_to_size=2.0).spawn()
with Seq(runtime=3):
model.roughness = 0.1 # polish the whole model
Working With Parts¶
An imported model keeps its node hierarchy, so you can reach in and animate a single part:
model = Model3D('robot.glb', fit_to_size=2.0).spawn()
print(model.node_names) # what's in the file
arm = model.get_part('LeftArm') # one TriangleMesh, or a list of them
with Seq(runtime=2):
arm.rotate(45, OUT)
arm.color = RED
node_names lists the nodes that carry geometry, and
get_part() returns the mesh Mob (or list of them) for a named
node. A part is a normal Mob, so everything in
Basic Animations applies – and because it is a child of
the model, moving the model still carries it along (see
Grouping Mobs).
Playing Baked Animations¶
If the file carries animation clips, play_animation() records
one onto Algan’s timeline:
model = Model3D('walking.glb', fit_to_size=2.0).spawn()
print(model.animation_names) # available clips
model.play_animation('Walk', runtime=4, loop=2)
Argument |
Meaning |
|---|---|
|
Which clip. Defaults to the first one. |
|
Seconds per loop. Defaults to the clip’s authored runtime. |
|
How many times to repeat it. |
|
Sampling rate used when baking. Higher is smoother for fast rotation, because poses are interpolated linearly in between. Default 30. |
|
Easing. Defaults to |
Because the clip is recorded on the timeline like any other animation, it composes with animation contexts – you can play a walk cycle while simultaneously moving the model across the frame:
with Sync(runtime=4):
model.play_animation('Walk', runtime=4)
model.move(RIGHT * 6)
Important
Rigid node animation only. Algan bakes each clip by evaluating the animated node transforms and composing them down the hierarchy, so a part that translates, rotates or scales plays back correctly. Skeletal skinning – where vertices are weighted to several bones and deform between them – is not applied. A model animated by moving rigid parts works; one animated by deforming a continuous skinned mesh will move at the node level only.
precompute_animation() exposes the same computation without
touching the scene, returning the sample times and per-frame geometry, if you want
to inspect or post-process the poses yourself.
Troubleshooting¶
Symptom |
Likely cause |
|---|---|
Nothing appears |
The model’s units. Try |
Faceted where it should be smooth |
The file has no authored normals; they are being derived per face. |
Untextured / flat color |
A texture failed to load, or the mesh has no UVs. |
Black or very dark |
A metallic material with nothing to reflect. Add an environment map – see Lighting and Shadows. |
Very slow |
Triangle count, or normal maps forcing the wavefront tracer. See Performance and Quality. |
An FBX raises on load |
The native |
See Also¶
Images and Textures – texturing surfaces you build yourself.
Shaders and Materials – overriding an imported model’s materials.
Lighting and Shadows – lighting an imported asset.
Importing From Manim – importing geometry from Manim instead of from a model file.
Grouping Mobs – the parent/child rules that make
get_partbehave, and moving a model with its parts.Performance and Quality – triangle count and normal maps, the two things that make an imported model slow.