How to Build Entities in Python

How to Build Entities in Python

Aspose.3D for Python builds 3D geometry two ways: by instantiating a ready-made primitive shape (Box, Sphere, Cylinder, Torus, Plane, Dish) and converting it to a Mesh with to_mesh(), or by constructing a Mesh directly from control points and polygons. Either way, the resulting entity is attached to a Node in the scene graph before the scene is saved.

Step-by-Step Guide

Step 1: Install the Package

pip install aspose-3d-foss

Verify the install:

python -c "import aspose.threed; print(aspose.threed.__name__)"

Step 2: Import Required Classes

from aspose.threed import Scene
from aspose.threed.entities import Box, Sphere, Mesh
from aspose.threed.shading import LambertMaterial, PbrMaterial
from aspose.threed.utilities import Vector3, Vector4

Step 3: Build a Primitive Entity

Primitive helper classes take their dimensions as constructor arguments and expose a to_mesh() method that converts them into a renderable/saveable Mesh.

from aspose.threed.entities import Box

box = Box(10, 20, 30)          # length, width, height
mesh = box.to_mesh()
print(f"Control points: {len(mesh.control_points)}")

A Sphere works the same way, and can be shaded with a PbrMaterial in the same step it is attached to a node:

from aspose.threed import Scene
from aspose.threed.entities import Sphere
from aspose.threed.shading import PbrMaterial
from aspose.threed.utilities import Vector3

scene = Scene()
sphere = Sphere()
material = PbrMaterial(albedo=Vector3(0.8, 0.1, 0.1))
material.metallic_factor = 0.9
material.roughness_factor = 0.2

scene.root_node.create_child_node("Ball", entity=sphere.to_mesh(), material=material)
scene.save("ball.stl")

Step 4: Position and Scale with a Transform

Every Node has a transform with translation, scaling, and rotation (as a Quaternion) properties for placing the entity in the scene:

from aspose.threed import Scene
from aspose.threed.entities import Cylinder
from aspose.threed.utilities import Vector3

scene = Scene()
cylinder = Cylinder()
node = scene.root_node.create_child_node("Post", entity=cylinder.to_mesh())
node.transform.translation = Vector3(0.0, 1.0, 0.0)
node.transform.scaling = Vector3(0.5, 2.0, 0.5)

scene.save("post.obj")

Step 5: Build a Custom Mesh from Control Points

When no primitive matches the shape you need, build a Mesh directly: append vertex positions as Vector4 entries to its control-points list, then connect them into triangles with create_polygon().

from aspose.threed import Scene
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector3, Vector4
from aspose.threed.shading import LambertMaterial

scene = Scene()
mesh = Mesh("Triangle")
mesh.control_points.add(Vector4(0.0, 0.0, 0.0, 1.0))
mesh.control_points.add(Vector4(1.0, 0.0, 0.0, 1.0))
mesh.control_points.add(Vector4(0.0, 1.0, 0.0, 1.0))
mesh.create_polygon(0, 1, 2)

material = LambertMaterial()
material.diffuse_color = Vector3(0.2, 0.6, 0.9)

scene.root_node.create_child_node("Triangle", entity=mesh, material=material)
scene.save("triangle.gltf")

For a shape with more polygons, call create_polygon() once per face, passing the control-point indices in winding order — twelve calls build a complete cube from eight control points, two triangles per face.


Step 6: Attach the Entity and Material to a Node

create_child_node() combines node creation, entity attachment, and material assignment in a single call, as used in the steps above. The equivalent longer form, useful when the node already exists, is:

node = scene.root_node.create_child_node("Crate")
node.entity = mesh
node.material = material

Common Issues and Fixes

to_mesh() returns an empty-looking object on a Primitive subclass with zero segments configured. Primitive shapes such as Cylinder and Sphere expose segment-count properties (width/height segment counts, …); leaving them at their defaults still produces a valid mesh, but setting one to zero before calling to_mesh() can produce a degenerate geometry. Keep segment counts at 1 or above.

Polygon winding looks inverted after create_polygon(). The order of the indices passed to create_polygon() determines the face normal direction by the right-hand rule. If a face appears to be facing the wrong way after export, reverse the index order for that face.

Adding a control point raises a type error. The control-points list on Mesh holds Vector4 entries (homogeneous coordinates), not Vector3. Always construct the fourth component explicitly, typically the value 1.0, e.g. Vector4(x, y, z, 1.0).

Material assigned but the exported file looks untextured/flat. LambertMaterial, PhongMaterial, and PbrMaterial all drive shading through solid colour and factor properties in this release — texture image binding is not implemented, so a material built only from colour and factor properties is the expected, fully supported result.

Node created but nothing appears in the saved file. Confirm the entity was actually assigned, either via create_child_node(..., entity=mesh) or by setting node.entity = mesh afterwards — a Node with no entity is a valid empty transform node and will not render or show polygons in the output format.

Frequently Asked Questions

Which primitive shapes are available besides Box, Sphere, and Cylinder?

Torus, Plane, and Dish are also available as ready-made primitives, each with its own constructor parameters and a to_mesh() method that follows the same pattern shown above.

Can I convert a primitive to a mesh and then keep editing it?

Yes — to_mesh() returns a regular Mesh object, so after conversion you can call create_polygon(), add_element(), or any other Mesh method on the result just as you would on a hand-built mesh.

Do I need to call triangulate() on a mesh I built myself with triangles?

No. create_polygon() accepts any number of indices per call; if you only ever pass three indices per call, the mesh is already fully triangulated. triangulate() is for converting meshes that contain higher-order (quad or n-gon) polygons.

Is there a simpler way to create many identical entities?

Not a built-in batch constructor — call the primitive constructor and create_child_node() in a loop, varying the name and the transform for each instance.

See Also