Introduction

Aspose.3D FOSS for Python is a zero-dependency, MIT-licensed library published on PyPI as aspose-3d-foss. Its API is distributed under the aspose_3d_foss package and exposed through the aspose.threed namespace used in the import statements throughout this post.

Every file the library loads, and every scene you build in code, is represented with the same object model: a Scene holding a tree of Node objects, each node carrying a Transform and zero or more attached Entity objects such as Mesh. This post covers that core model directly — constructing nodes, attaching geometry, and reading back mesh data — rather than file conversion.

This is useful whenever you need to generate or inspect 3D content programmatically: building simple geometry at runtime, walking an existing scene graph to collect statistics, or composing primitives into a larger scene before saving it out.


What’s Included

Building the Scene Graph

A Scene starts with an empty root_node. New nodes are attached under it with create_child_node(), which accepts an entity (such as a mesh produced by a primitive’s to_mesh()) and an optional material:

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")

Mesh and Polygon Data

A Mesh stores geometry as a control_points list of Vector4 positions and a set of polygons built by index with create_polygon(). This is the lowest-level way to build or inspect geometry, independent of any file format:

from aspose.threed import Scene
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector4

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(1.0, 1.0, 0.0, 1.0))
mesh.create_polygon(0, 1, 2)

node = scene.root_node.create_child_node("triangle_node")
node.entity = mesh

print(f"control points: {len(mesh.control_points)}, polygons: {mesh.polygon_count}")
scene.save("triangle.stl")

mesh.control_points and mesh.polygon_count can also be read back from any mesh, whether it was built in code or loaded from a file.

Node Transforms

Each Node carries a transform (a Transform object) with translation, scaling, and rotation properties, applied independently of the node’s geometry:

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")

Traversing the Node Hierarchy

Because every scene — built in code or loaded from a file — shares the same Node tree, the same recursive walk works regardless of the source:

from aspose.threed import Scene

scene = Scene()
scene.open("model.gltf")

def walk(node, depth=0):
    print("  " * depth + (node.name or "(unnamed)"))
    for child in node.child_nodes:
        walk(child, depth + 1)

walk(scene.root_node)

node.entity (or the node.entities list, for nodes with more than one attachment) gives access to whatever Mesh or other entity a node carries while walking the tree.


Quick Start

pip install aspose-3d-foss

Build a one-triangle mesh with a PBR material, attach it to a new node, and write it out as glTF:

import io
import json
from aspose.threed import Scene, FileFormat
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector3, Vector4
from aspose.threed.formats.gltf import GltfSaveOptions
from aspose.threed.shading import PbrMaterial

scene = Scene()
mesh = Mesh("TestMesh")
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)

albedo = Vector3(0.8, 0.2, 0.3)
material = PbrMaterial("RedMaterial", albedo)
material.metallic_factor = 0.5
material.roughness_factor = 0.7

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

stream = io.BytesIO()
options = GltfSaveOptions(FileFormat.get_format_by_extension(".gltf"))
options.binary_mode = False
scene.save(stream, options)

stream.seek(0)
gltf_data = json.loads(stream.read().decode("utf-8"))
print(gltf_data["materials"][0]["pbrMetallicRoughness"])

Supported Formats

FormatExtension(s)LoadSaveNotes
OBJ.objYesYes.mtl material loading supported
STL.stlYesYesBinary and ASCII; coordinate-system flip option
glTF.gltf, .glbYesYes.glb (self-contained binary) supported
COLLADA.daeYesYesColladaSaveOptions available with flip_coordinate_system, enable_materials, indented
3MF.3mfYesYesSuitable for 3D printing workflows
FBX.fbxPartialN/ATokenizer only; full parser in progress

The Scene/Node/Mesh object model described above is the same across every format in this table — only the loader or exporter for a given format changes.


Open Source & Licensing

Aspose.3D FOSS for Python is MIT licensed. You can use it in commercial applications, modify it, and redistribute it without restriction. The source is available on GitHub under the Aspose organisation, and the package is published on PyPI.


Getting Started