How to Build a 3D Mesh with Aspose.3D in Python

How to Build a 3D Mesh with Aspose.3D in Python

Aspose.3D FOSS for Python lets you build 3D geometry entirely in code: no external modelling tool required. You create a Mesh, populate it with vertex positions (control_points) and face definitions (polygons), attach optional vertex attributes such as normals, then save the scene to any supported format.

Step-by-Step Guide

Step 1: Install the Package

Install Aspose.3D FOSS from PyPI. No native extensions or compiler toolchain is required.

pip install aspose-3d-foss

Verify the installation:

from aspose.threed import Scene
print("Aspose.3D FOSS ready")

Supported Python versions: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


Step 2: Create a Scene and a Node

Every mesh must live inside a scene graph. Create a Scene and add a named Node to hold the mesh:

from aspose.threed import Scene

scene = Scene()
node = scene.root_node.create_child_node("triangle")

The node name is preserved in the exported file and is useful for debugging and later retrieval via node.get_child("triangle").


Step 3: Create a Mesh Object

Instantiate a Mesh with an optional descriptive name:

from aspose.threed.entities import Mesh

mesh = Mesh("triangle")

The mesh is initially empty: no vertices, no polygons. You populate it in the following steps.


Step 4: Add Control Points (Vertices)

Control points are the vertex positions. Each vertex is stored as a Vector4(x, y, z, w) where w=1 indicates a point in 3D space:

from aspose.threed.utilities import Vector4

##Vertex 0: origin
# Note: control_points returns a copy of the internal vertex list.
# Appending to the returned copy discards the vertex silently.
# Use _control_points to mutate the backing list directly.
# This is a known library limitation — a public add_control_point() API is not yet available.
mesh._control_points.append(Vector4(0.0, 0.0, 0.0, 1.0))

##Vertex 1: 1 unit along X
mesh._control_points.append(Vector4(1.0, 0.0, 0.0, 1.0))

##Vertex 2: apex
mesh._control_points.append(Vector4(0.5, 1.0, 0.0, 1.0))

print(f"Vertices added: {len(mesh.control_points)}")

Important: mesh.control_points returns a copy of the internal vertex list (the getter executes list(self._control_points)). Calling mesh.control_points.append(v) appends to the copy, not to the mesh, so the vertex is silently discarded. Always use mesh._control_points.append(v) to add vertices. Accessing private state via _control_points is a known workaround; the interface may change in a future version of the library.


Step 5: Create Polygon Faces

Define the face topology using vertex indices. Pass vertex indices to create_polygon(). Three indices produce a triangle; four produce a quad:

##Triangle: connect vertices 0 → 1 → 2
mesh.create_polygon(0, 1, 2)

print(f"Polygon count: {mesh.polygon_count}")

For a quad mesh you would pass four indices: mesh.create_polygon(0, 1, 2, 3).

Indices must be valid positions in control_points (0-based, within range). Winding order is counter-clockwise for outward-facing normals.


Step 6: Add Vertex Normals

Vertex normals are stored as a VertexElement attached to the mesh. Use mesh.create_element() with VertexElementType.NORMAL, MappingMode.CONTROL_POINT, and ReferenceMode.DIRECT:

from aspose.threed.entities import VertexElementType, MappingMode, ReferenceMode, VertexElementNormal
from aspose.threed.utilities import Vector4, FVector4

##Create the normal element (returns VertexElementNormal, a VertexElementFVector subclass)
normals: VertexElementNormal = mesh.create_element(
    VertexElementType.NORMAL,
    MappingMode.CONTROL_POINT,
    ReferenceMode.DIRECT
)

##One normal per vertex: all pointing out of the XY plane (0, 0, 1)
normals.set_data([
    FVector4(0, 0, 1, 0),   # vertex 0
    FVector4(0, 0, 1, 0),   # vertex 1
    FVector4(0, 0, 1, 0),   # vertex 2
])

print("Normal layer attached.")

MappingMode.CONTROL_POINT means one normal per vertex. ReferenceMode.DIRECT means the normal data is read in the same order as the control points (no extra index buffer).

Normal vectors use FVector4(x, y, z, w) with w=0 to indicate a direction rather than a position. FVector4 is a single-precision float vector; vertex attribute data in VertexElementFVector subclasses uses this type.


Step 7: Attach the Mesh to the Node and Save

Add the mesh to the node, then save the scene:

node.add_entity(mesh)

scene.save("triangle.gltf")
print("Saved triangle.gltf")

The complete working script (all steps combined):

from aspose.threed import Scene
from aspose.threed.entities import Mesh, VertexElementType, MappingMode, ReferenceMode, VertexElementNormal
from aspose.threed.utilities import Vector3, Vector4, FVector4

scene = Scene()
node = scene.root_node.create_child_node("triangle")

mesh = Mesh("triangle")

##Add 3 vertices (x, y, z, w)
# Use _control_points to mutate the backing list directly (control_points returns a copy)
mesh._control_points.append(Vector4(0.0, 0.0, 0.0, 1.0))
mesh._control_points.append(Vector4(1.0, 0.0, 0.0, 1.0))
mesh._control_points.append(Vector4(0.5, 1.0, 0.0, 1.0))

##Create a triangle polygon
mesh.create_polygon(0, 1, 2)

##Add normals (create_element returns VertexElementNormal, a VertexElementFVector subclass)
normals: VertexElementNormal = mesh.create_element(VertexElementType.NORMAL, MappingMode.CONTROL_POINT, ReferenceMode.DIRECT)
normals.set_data([
    FVector4(0, 0, 1, 0),
    FVector4(0, 0, 1, 0),
    FVector4(0, 0, 1, 0),
])

node.add_entity(mesh)
scene.save("triangle.gltf")

Common Issues

IssueResolution
IndexError in create_polygonVerify that all indices are within range(len(mesh.control_points)). Indices are 0-based.
Mesh exports with zero verticesmesh.control_points.append(...) silently discards vertices because the property returns a copy. Use mesh._control_points.append(...) instead.
Normals count does not match vertex countWhen using MappingMode.CONTROL_POINT + ReferenceMode.DIRECT, normals.data must have exactly len(control_points) entries.
Mesh missing from saved fileConfirm that node.add_entity(mesh) was called before scene.save(). A mesh not attached to any node is not exported.
Wrong winding order (face appears invisible)Counter-clockwise vertex order produces an outward-facing normal. Reverse the index order in create_polygon to flip it.
polygon_count returns 0polygon_count reads the same list as polygons. If create_polygon was not called, the list is empty.
Normals appear incorrect in viewerEnsure all normal vectors are unit-length. Compute with n / abs(n) or pass pre-normalised values.

Frequently Asked Questions

What is the difference between Vector3 and Vector4 for control points?

control_points stores Vector4 objects. The w component is the homogeneous coordinate: use w=1 for vertex positions and w=0 for direction vectors such as normals. Vector3 is used for transforms (translation, scale) but not for geometry storage.

Can I build a mesh with quads instead of triangles?

Yes. Call mesh.create_polygon(0, 1, 2, 3) with four indices to define a quad. Some save targets (STL, 3MF) require triangles and will triangulate quads automatically. glTF and COLLADA preserve quads.

How do I add UV coordinates?

Use mesh.create_element_uv(TextureMapping.DIFFUSE, MappingMode.POLYGON_VERTEX) to create a VertexElementUV for the diffuse channel, then populate its data list with Vector4 entries. UV coordinates use x and y; z and w are typically 0. The first argument must be a TextureMapping constant (e.g., TextureMapping.DIFFUSE) identifying which texture slot the UV layer belongs to.

Does the mesh need normals to export correctly?

No. Normals are optional. If omitted, most viewers compute per-face normals from the polygon winding order. Adding explicit per-vertex normals produces smoother shading.

Can I add multiple meshes to one node?

Yes. Call node.add_entity(mesh) multiple times. Each call appends a new entity to node.entities. Some formats may flatten multiple entities into one on export.

How do I triangulate a mesh with mixed polygon types?

Call mesh.triangulate() to convert all quads and N-gons to triangles in place. This is useful before saving to formats that only support triangles.

 English