How to Convert Formats in Python

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-foss
python -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 ObjLoadOptions

Step 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.

See Also