Com convertir models 3D a Python

Com convertir models 3D a Python

Conversió de format amb Aspose.3D FOSS per a Python és un procés de dos passos: carregar en un objecte Scene, i després desar al format de sortida desitjat. Com que tota la geometria es manté en una representació comuna en memòria, no calen passos intermedis específics del format. Les seccions següents mostren les conversions més comunes amb codi funcional.

Guia pas a pas

Pas 1: Instal·la el paquet

pip install aspose-3d-foss

No es requereixen biblioteques del sistema, compiladors ni dependències d’execució addicionals.


Pas 2: Carrega el model d’origen

Utilitzeu Scene.from_file() per al cas més senzill: el format es detecta automàticament a partir de l’extensió del fitxer:

from aspose.threed import Scene

scene = Scene.from_file("model.obj")

Per als fitxers OBJ en què necessiteu controlar el sistema de coordenades o la càrrega de materials, utilitzeu scene.open() amb ObjLoadOptions:

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

options = ObjLoadOptions()
options.flip_coordinate_system = True  # Convert to Z-up if needed
options.enable_materials = True        # Load the accompanying .mtl file
options.normalize_normal = True

scene = Scene()
scene.open("model.obj", options)

Ambdós enfocaments produeixen un objecte Scene idèntic per al pas de guardat posterior.


Pas 3: Inspecciona l’escena carregada

Abans de comprometre’s amb una conversió, val la pena comprovar que la geometria s’ha carregat correctament. Un fitxer que falta, una característica d’FBX no compatible o un problema de ruta amb un fitxer .mtl pot produir una escena buida.

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

scene = Scene.from_file("model.obj")

mesh_count = 0
total_vertices = 0

def count_meshes(node) -> None:
    global mesh_count, total_vertices
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh_count += 1
            total_vertices += len(entity.control_points)
    for child in node.child_nodes:
        count_meshes(child)

count_meshes(scene.root_node)
print(f"Loaded {mesh_count} mesh(es), {total_vertices} total vertices")

if mesh_count == 0:
    raise ValueError("Scene contains no geometry: check the source file path and format")

Pas 4: Desa al format de destinació

Crida scene.save() amb el camí de sortida. Passa un objecte d’opcions de desament específic del format per controlar la sortida binària vs ASCII, els eixos de coordenades i la compressió.

OBJ a STL (binari)

from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

scene = Scene.from_file("model.obj")

save_opts = StlSaveOptions()
##StlSaveOptions defaults to binary output, which is more compact.

scene.save("model.stl", save_opts)
print("Saved model.stl")

OBJ a glTF 2.0

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

scene = Scene.from_file("model.obj")

save_opts = GltfSaveOptions()

scene.save("model.gltf", save_opts)
print("Saved model.gltf")

Per desar com a binari GLB autònom en lloc d’un .gltf + buffers externs, canvieu l’extensió de sortida a .glb:

scene.save("model.glb", save_opts)

OBJ a 3MF

from aspose.threed import Scene
from aspose.threed.formats import ThreeMfSaveOptions

scene = Scene.from_file("model.obj")

save_opts = ThreeMfSaveOptions()

scene.save("model.3mf", save_opts)
print("Saved model.3mf")

STL a glTF 2.0

El mateix patró s’aplica independentment del format d’origen:

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

scene = Scene.from_file("input.stl")
scene.save("output.gltf", GltfSaveOptions())
print("Saved output.gltf")

Pas 5: Verifica la sortida

Després de desar, confirmeu que el fitxer de sortida existeix i té una mida diferent de zero. Per a una comprovació més exhaustiva, torneu-lo a carregar i compareu els recomptes de malles:

import os
from aspose.threed import Scene
from aspose.threed.entities import Mesh

output_path = "model.stl"

##Basic file-system check
size = os.path.getsize(output_path)
print(f"Output file size: {size} bytes")
if size == 0:
    raise RuntimeError("Output file is empty: save may have failed silently")

##Round-trip verification: reload and count geometry
def _iter_nodes(node):
    yield node
    for child in node.child_nodes:
        yield from _iter_nodes(child)

reloaded = Scene.from_file(output_path)
mesh_count = sum(
    1
    for node in _iter_nodes(reloaded.root_node)
    for entity in node.entities
    if isinstance(entity, Mesh)
)
print(f"Round-trip check: {mesh_count} mesh(es) in output")

Problemes comuns i solucions

El fitxer de sortida es crea però no conté cap geometria

El fitxer d’origen pot haver‑se carregat amb zero malles. Afegeix el pas d’inspecció del Pas 3 abans de desar. Confirma també que l’extensió del fitxer coincideixi amb el format real; Aspose.3D utilitza l’extensió per seleccionar l’analitzador.

La sortida glTF no conté textures

Aspose.3D FOSS conserva la geometria i les propietats del material durant la conversió. Si l’OBJ d’origen fa referència a fitxers d’imatge externs a .mtl, aquests fitxers d’imatge no es copien automàticament al costat de .gltf. Copieu les imatges de textura al directori de sortida manualment després de desar.

La sortida STL sembla cap a dins (normals de cara invertides)

STL no porta metadades d’ordre d’enrotllament. Si les normals de sortida estan invertides, establiu les opcions StlSaveOptions si estan disponibles, o invertiu el sistema de coordenades durant la càrrega: ObjLoadOptions.flip_coordinate_system = True.

ValueError: unsupported format en desar

Comproveu que l’extensió del fitxer de sortida sigui una de .obj, .stl, .gltf, .glb, .dae, .3mf. Les extensions són sensibles a majúscules i minúscules a Linux.

Fitxers molt grans provoquen una conversió lenta

Aspose.3D FOSS processa geometria en memòria. Per a fitxers amb milions de polígons, assegureu-vos de disposar de suficient RAM. Actualment no hi ha cap API d’escriptura en flux.


Preguntes freqüents (FAQ)

Puc convertir un fitxer sense escriure’l al disc primer?

Sí. Tant scene.open() com scene.save() accepten objectes binaris semblants a fitxers a més de rutes de fitxer. Passeu qualsevol objecte que implementi read() per a carregar o write() per a desar:

import io
from aspose.threed import Scene

# Load from an in-memory buffer
data = open('model.obj', 'rb').read()
scene = Scene()
scene.open(io.BytesIO(data))

# Save to an in-memory buffer
buf = io.BytesIO()
scene.save(buf)

És FBX compatible com a format d’origen per a la conversió?

La tokenització de FBX està parcialment implementada, però l’analitzador no està complet. L’entrada FBX pot generar escenes incompletes. Utilitzeu OBJ, STL, glTF, COLLADA o 3MF com a formats d’origen fiables.

Sobreviuran els materials en una conversió d’OBJ a glTF?

Les propietats bàsiques del material Phong/Lambert (color difús) es transmeten a través del model Scene i s’escriuen al bloc de material glTF. Els paràmetres de shader procedimentals o personalitzats que no es poden expressar en el model de material glTF es descarten.

Puc convertir diversos fitxers en un bucle?

Sí. Cada crida Scene.from_file() crea un objecte independent, de manera que un bucle sobre una llista de camins és senzill:

from pathlib import Path
from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

source_dir = Path("input")
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

opts = StlSaveOptions()
for obj_file in source_dir.glob("*.obj"):
    scene = Scene.from_file(str(obj_file))
    out_path = output_dir / obj_file.with_suffix(".stl").name
    scene.save(str(out_path), opts)
    print(f"Converted {obj_file.name} -> {out_path.name}")

La conversió conserva la jerarquia de l’escena (nodes pare/fill)?

Sí. L’arbre de nodes es conserva tant com permet el format de destinació. Formats com STL emmagatzemen només geometria plana sense estructura de nodes; la jerarquia es aplaça en desar. Formats com glTF i COLLADA conserven la jerarquia completa.

 Català