How to Convert Formats in Python
Aspose.3D for Python converts between 3D file formats by loading a file into a
Scene with Scene.open() and writing it back out with scene.save(). The format
on each side is normally inferred from the file extension; for finer control, you
can construct a format-specific save-options object explicitly.
Step-by-Step Guide
Step 1: Install the Package
pip install aspose-3d-fosspython -c "import aspose.threed; print(aspose.threed.__name__)"Step 2: Import Required Classes
from aspose.threed import Scene, FileFormat
from aspose.threed.formats.obj import ObjLoadOptionsStep 3: Convert with Extension Auto-Detection
The simplest conversion loads a source file and immediately saves it under a different extension — both ends are detected automatically:
from aspose.threed import Scene
scene = Scene.from_file("input.obj")
scene.save("output.glb")This same two-line pattern works for any of the reliably round-trippable formats:
OBJ, STL, glTF/GLB, COLLADA (.dae), and 3MF.
Step 4: Use Format-Specific Load Options
To control import behaviour — for example, whether OBJ materials are loaded — build
the matching load-options object and pass it to Scene.open():
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")Step 5: Save with Explicit Save Options
When you need to tune the output (binary vs. text glTF, compression, …), resolve the
target FileFormat by extension and build its save-options object rather than
relying purely on auto-detection — this also lets you save to an in-memory stream
instead of a path. A directly-constructed options object has no output format set,
and scene.save() cannot infer the format from a bare stream (only filename-based
saves fall back to extension detection), so set it explicitly before saving:
import io
from aspose.threed import Scene, FileFormat
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector4
from aspose.threed.formats.stl import StlSaveOptions
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)
scene.root_node.create_child_node("triangle_node").entity = mesh
stream = io.StringIO()
options = StlSaveOptions()
options.file_format = FileFormat.get_format_by_extension(".stl")
options.binary_mode = False
scene.save(stream, options)
print(stream.getvalue())For glTF specifically, pass the resolved FileFormat into the GltfSaveOptions
constructor explicitly — a default-constructed options object saved against a bare
stream (no filename) has nothing to infer the output format from:
import io
from aspose.threed import Scene, FileFormat
from aspose.threed.formats.gltf import GltfSaveOptions
scene = Scene.from_file("model.obj")
stream = io.BytesIO()
options = GltfSaveOptions(FileFormat.get_format_by_extension(".gltf"))
options.binary_mode = False
scene.save(stream, options)Step 6: Triangulate Before Converting to a Triangle-Only Format
Some target formats or downstream tools expect triangle-only meshes. If the source
scene may contain quads or n-gons, triangulate each entity first with
PolygonModifier.triangulate():
from aspose.threed import Scene
from aspose.threed.entities import PolygonModifier
scene = Scene()
scene.open("model.obj")
for node in scene.root_node.child_nodes:
if node.entity is not None:
triangulated = PolygonModifier.triangulate(node.entity)
if triangulated is not None:
node.entity = triangulated
scene.save("model.stl")Common Issues and Fixes
Converting to or from FBX fails or raises an error. FBX export is not
implemented in this release — scene.save("out.fbx") will not succeed. FBX import
works but the parser is experimental and can struggle with deeply nested or unusual
scope structures in complex files. Prefer OBJ, glTF/GLB, COLLADA, or 3MF as the
interchange format on both ends of a conversion when possible.
scene.save() to a bare stream writes nothing useful or raises an error. Saving
to a stream (rather than a file path) gives scene.save() no extension to detect the
format from. Always pass an explicit save-options object — built from
FileFormat.get_format_by_extension(...).create_save_options() or the
format-specific constructor — when the destination is a stream.
Materials are lost after converting OBJ to another format. Confirm
ObjLoadOptions.enable_materials was explicitly enabled before calling
Scene.open() — material loading for OBJ is opt-in, not automatic.
Output mesh has fewer polygons than expected after triangulation.
PolygonModifier.triangulate() returns nothing when it has no work to do (the
entity is already triangle-only or is not a mesh-convertible entity) — the code
above only replaces the node’s entity when a real triangulated mesh comes back,
leaving already-triangulated entities untouched.
Frequently Asked Questions
Which formats give the most reliable round trip?
OBJ, STL, glTF/GLB, COLLADA, and 3MF. Each of these can be loaded with
Scene.open()/Scene.from_file() and written back out with scene.save() without
format-specific caveats.
Can I convert directly between two formats without inspecting the scene?
Yes — Scene.from_file("input.ext") followed immediately by scene.save("output.ext")
is sufficient for a straight conversion; the intermediate inspection shown in Step 4
is optional.
How do I pick the right *SaveOptions class for a format?
Call FileFormat.get_format_by_extension(".ext").create_save_options() rather than
importing and constructing the options class by name — this guarantees the options
object’s format matches the extension you intend to save with.
Does converting change the number of control points or polygons?
Not for the formats in Step 3’s round-trip list — vertex and polygon counts are preserved through a load/save cycle for OBJ, STL, glTF/GLB, COLLADA, and 3MF.