Core API
Overview
Scene, Node, and Mesh form the backbone of every program written against this library. Scene owns the graph and the file I/O; Node provides the tree structure and per-node transform; Mesh is the concrete geometry most nodes carry.
Scene: the root container
A Scene owns a single root_node and the file-level open()/save() operations. Scene also tracks animation_clips and a library of shared objects (materials, for example) that multiple nodes can reference:
from aspose.threed import Scene
from aspose.threed.formats.obj import ObjLoadOptions
scene = Scene()
options = ObjLoadOptions()
options.enable_materials = True
scene.open("model.obj", options)
for node in scene.root_node.child_nodes:
if node.entity is not None:
mesh = node.entity
print(f"{node.name}: {len(mesh.control_points)} vertices, {mesh.polygon_count} polygons")Scene.from_file() is a shortcut that constructs a Scene and loads it from a path in one call, without a separate LoadOptions object.
Node: the scene-graph tree
Every Node has a parent_node, a list of child_nodes, an optional entity, and an optional material. create_child_node() is the usual way to grow the tree — it creates a new Node, attaches the entity and material you pass, and appends it as a child in one call:
from aspose.threed import Scene
from aspose.threed.entities import Box
from aspose.threed.shading import LambertMaterial
from aspose.threed.utilities import Vector3
scene = Scene()
box = Box(length=2.0, width=1.0, height=1.0)
material = LambertMaterial()
material.diffuse_color = Vector3(0.2, 0.6, 0.9)
scene.root_node.create_child_node("Crate", entity=box.to_mesh(), material=material)
scene.save("crate.gltf")Each Node also carries a transform (local Transform: translation, scaling, rotation) and a global_transform (the accumulated GlobalTransform in world space), plus get_bounding_box() and evaluate_global_transform() for computing world-space extents.
Mesh: geometry data
Mesh stores geometry as control_points (a flat list of vertex positions) and polygons built from indices into that list via create_polygon(). Reading an imported mesh’s basic shape only needs two properties:
from aspose.threed.entities import Box
box = Box(10, 20, 30)
mesh = box.to_mesh()
print(f"Control points: {len(mesh.control_points)}")mesh.polygon_count and mesh.polygons give the polygon count and the raw index lists; mesh.vertex_elements holds per-vertex data such as normals and UVs loaded from a file; mesh.deformers holds skin and morph-target data (see Deformers).
Tips and Best Practices
- Use Scene.from_file() for a quick load when you don’t need format-specific LoadOptions; use Scene() + Scene.open() when you do.
- A Node’s material and entity are independent — you can change one without touching the other.
- evaluate_global_transform() computes the accumulated world-space matrix from the node up to the scene root; use it instead of manually combining parent transforms by hand.
- Mesh.control_points and Mesh.polygons are read/write lists — treat them as live data, not an immutable snapshot.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
| child_nodes is empty right after Scene() | A brand-new Scene has no nodes until you add one or load a file | Call create_child_node() or Scene.open()/Scene.from_file() before inspecting child_nodes |
| node.material is None after create_child_node | material was not passed to create_child_node | Pass material=… explicitly, or set node.material afterward |
| get_bounding_box() looks wrong after moving a node | The transform was changed but global state was read before re-evaluating | Call evaluate_global_transform() after changing transform, before relying on world-space bounds |
FAQ
Does every Scene start with a populated root_node?
No — root_node always exists, but it has no child_nodes until you add one explicitly or load a file into the scene.
What is the difference between Node.transform and Node.global_transform?
transform is the node’s own local Transform relative to its parent. global_transform (a GlobalTransform) is the accumulated world-space result after combining every ancestor’s transform.
Can a Mesh exist without being attached to a Node?
Yes — box.to_mesh() returns a Mesh you can inspect immediately; it only becomes part of the scene graph once you attach it to a Node’s entity, for example through create_child_node().
API Reference Summary
| Class/Method | Description |
|---|---|
| Scene() | Create an empty scene |
| Scene.open(file_or_stream, options) | Load a file into an existing Scene |
| Scene.from_file(file_name) | Create and load a Scene from a path in one call |
| Scene.save(file_or_stream, format_or_options) | Save the scene |
| Scene.root_node | The root Node of the scene graph |
| Node.create_child_node(node_name, entity, material) | Create and attach a child Node |
| Node.child_nodes / Node.parent_node | Tree navigation |
| Node.entity / Node.material | What the node carries |
| Node.transform / Node.global_transform | Local and world-space transforms |
| Node.evaluate_global_transform(with_geometric_transform) | Compute the accumulated world-space matrix |
| Mesh.control_points | Vertex positions |
| Mesh.create_polygon() | Build a polygon from control-point indices |
| Mesh.polygon_count / Mesh.polygons | Polygon count and raw index data |