Evaluating animations in a GLB or GLTF file lets you inspect how 3D objects move, rotate, and scale over time without rendering the scene. You can use this approach to analyze animation data, verify movements, or export transformations for further processing. In this tutorial, you will load an animated GLB model, inspect its clips, evaluate five timestamps, and export node transformations to CSV.

Load and evaluate GLB animations in Python

An animation clip contains animation data associated with a scene. Its start and stop times define the period you can evaluate. The workflow is to load a model, select an animation clip, apply it at specific times, inspect the scene nodes, and export their transformations.

Install Aspose.3D for Python via .NET

Install Aspose.3D for Python via .NET, which provides the animation evaluation API used here:

pip install aspose-3d==26.10.0

See the installation documentation for more details.

Evaluate animation and export node transformations

The following code example evaluates the first animation clip at five evenly spaced timestamps and saves each node’s local translation, rotation, and scaling in animation_transforms.csv.

import csv
import aspose.threed as a3d

INPUT_FILE = "animated_model.glb"
OUTPUT_FILE = "animation_transforms.csv"
FRAME_COUNT = 5


def get_nodes(root):
    """Recursively collect all nodes in the scene."""
    nodes = [root]

    for child in root.child_nodes:
        nodes.extend(get_nodes(child))

    return nodes


# Load the animated model
scene = a3d.Scene.from_file(INPUT_FILE)

# Retrieve animation clips
clips = list(scene.animation_clips)

if not clips:
    raise RuntimeError("No animation clips found.")

print(f"Found {len(clips)} animation clip(s).")

for i, item in enumerate(clips):
    print(
        f"{i}: {item.name} "
        f"({item.start:.3f} to {item.stop:.3f} seconds)"
    )

# Select the first clip and collect scene nodes
clip = clips[0]
nodes = get_nodes(scene.root_node)

print(f"\nEvaluating: {clip.name}")
print(f"Scene nodes: {len(nodes)}")

records = []

for i in range(FRAME_COUNT):
    time = float(
        clip.start +
        (clip.stop - clip.start) * i / (FRAME_COUNT - 1)
    )

    # Apply animation at the requested time
    clip.apply_to(time)

    print(f"\nTimestamp: {time:.3f}s")

    for index, node in enumerate(nodes):
        transform = node.transform

        translation = str(transform.translation)
        rotation = str(transform.rotation)
        scaling = str(transform.scaling)

        records.append([
            time,
            index,
            node.name or "(unnamed)",
            translation,
            rotation,
            scaling
        ])

        print(
            f"  {node.name or '(unnamed)'}: "
            f"position={translation}, "
            f"rotation={rotation}"
        )

# Write evaluated transformations to CSV
with open(
    OUTPUT_FILE, "w", newline="", encoding="utf-8"
) as file:
    writer = csv.writer(file)

    writer.writerow([
        "Time",
        "Node Index",
        "Node Name",
        "Translation",
        "Rotation",
        "Scaling"
    ])

    writer.writerows(records)

print(f"\nSaved animation data to {OUTPUT_FILE}")

How the animation evaluation works

Load the GLB scene and retrieve its clips

Scene.from_file() loads the GLB or GLTF model. The scene.animation_clips collection provides the available clips:

scene = a3d.Scene.from_file("animated_model.glb")
clips = list(scene.animation_clips)

Each clip exposes a name and a time range in seconds through name, start, and stop. See the AnimationClip API reference.

Apply the animation at specific timestamps

The central operation is clip.apply_to(time). For the sample clip, whose duration is approximately 3.708 seconds, the five timestamps are:

SampleTime (seconds)
10.000
20.927
31.854
42.781
53.708

You can change FRAME_COUNT to collect more samples. These samples are evaluated animation states, not rendered image frames.

Read translation, rotation, and scaling

After applying the clip, the script traverses all scene nodes and reads:

  • transform.translation: the node’s local position.
  • transform.rotation: the node’s local rotation.
  • transform.scaling: the node’s local scale.

These are local transformations, not necessarily world-space coordinates. Parent node transformations also affect a node’s final position in the scene. Nodes without animation can retain the same values at every timestamp.

CSV output from the animation

The script creates animation_transforms.csv. For the supplied sample, the console begins with:

Found 1 animation clip(s).
0: Animation_1 (0.000 to 3.708 seconds)

Evaluating: Animation_1

The CSV contains one row per scene node at each sampled timestamp and these columns:

ColumnDescription
TimeEvaluation timestamp in seconds
Node IndexNode’s index in the recursive traversal
Node NameName of the scene node
TranslationLocal position at the timestamp
RotationLocal rotation at the timestamp
ScalingLocal scale at the timestamp

You can open the CSV in a spreadsheet application or analyze it with Python to compare transformations across the clip.

What to check when evaluating animations

Multiple clips. This example evaluates clips[0]. To evaluate another animation, choose a different index or retrieve a clip by name using scene.get_animation_clip(name).

Sampling resolution. Five timestamps provide a basic overview, but might miss fast motion. Increase FRAME_COUNT or choose specific times to inspect more detail.

Skeletons and morph targets. Not all animation effects appear as changes in a node’s local translation, rotation, or scaling. This CSV workflow does not capture every kind of skeletal or morph-target deformation.

GLTF dependencies. Text-based .gltf files may reference external buffers and textures. Keep those files at their expected relative paths.

Does evaluating GLB animations require a GPU?

This example does not invoke Scene.render() or the Vulkan rendering backend. It evaluates animation properties and writes CSV data instead of generating images. Aspose.3D rendering has separate Vulkan hardware requirements, which do not apply to this example’s CSV-only workflow.

Get a Free Temporary License

You can request a free 30-day temporary license to evaluate Aspose.3D for Python via .NET without evaluation limitations. To apply the license in your Python application, follow the Aspose.3D licensing guide.

Free Learning Resources

Explore the following resources to learn more about working with 3D models, animations, and supported file formats using Aspose.3D for Python via .NET:

Conclusion

Aspose.3D for Python via .NET 26.10 lets you evaluate GLB and GLTF animation clips at selected times with AnimationClip.apply_to(). By reading node transformations after each evaluation, you can inspect movement and export structured animation data without rendering the model. Adapt the example to evaluate additional clips, increase sampling frequency, or analyze selected nodes.

Read More