Cómo convertir modelos 3D en Python
La conversión de formato con Aspose.3D FOSS para Python es un proceso de dos pasos: cargar en un objeto Scene, luego guardar al formato de salida deseado. Debido a que toda la geometría se mantiene en una representación común en memoria, no se requieren pasos intermedios específicos de formato. Las secciones a continuación muestran las conversiones más comunes con código funcional.
Guía paso a paso
Paso 1: Instalar el paquete
pip install aspose-3d-fossNo se requieren bibliotecas del sistema, compiladores ni dependencias de tiempo de ejecución adicionales.
Paso 2: Cargar el modelo fuente
Utilice Scene.from_file() para el caso más simple: el formato se detecta automáticamente a partir de la extensión del archivo:
from aspose.threed import Scene
scene = Scene.from_file("model.obj")Para archivos OBJ donde necesite controlar el sistema de coordenadas o la carga de materiales, use scene.open() con 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)Ambos enfoques producen un objeto Scene idéntico para el paso de guardado posterior.
Paso 3: Inspeccionar la escena cargada
Antes de comprometerse con una conversión, vale la pena comprobar que la geometría se haya cargado correctamente. Un archivo faltante, una característica de FBX no compatible o un problema de ruta con un archivo .mtl pueden producir una escena vacía.
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")Paso 4: Guardar en el formato de destino
Llame a scene.save() con la ruta de salida. Pase un objeto de opciones de guardado específico del formato para controlar la salida binaria vs ASCII, los ejes de coordenadas y la compresión.
OBJ a STL (binario)
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")Para guardar como un binario GLB autocontenido en lugar de un .gltf + buffers externos, cambie la extensión de salida 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 mismo patrón se aplica independientemente del formato de 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")Paso 5: Verificar la salida
Después de guardar, confirme que el archivo de salida exista y tenga un tamaño distinto de cero. Para una verificación más exhaustiva, recárguelo y compare los recuentos de mallas:
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")Problemas comunes y soluciones
El archivo de salida se crea pero no contiene geometría
El archivo fuente puede haberse cargado sin mallas. Añada el paso de inspección del Paso 3 antes de guardar. También confirme que la extensión del archivo coincida con el formato real; Aspose.3D usa la extensión para seleccionar el analizador.
Salida de glTF sin texturas
Aspose.3D FOSS conserva la geometría y las propiedades de material durante la conversión. Si el OBJ de origen hace referencia a archivos de imagen externos en el .mtl, esos archivos de imagen no se copian automáticamente junto al .gltf. Copie las imágenes de textura al directorio de salida manualmente después de guardar.
La salida STL parece al revés (normales de cara volteadas)
STL no lleva metadatos de orden de devanado. Si las normales de salida están invertidas, establezca las opciones StlSaveOptions si están disponibles, o invierta el sistema de coordenadas durante la carga: ObjLoadOptions.flip_coordinate_system = True.
ValueError: unsupported format al guardar
Verifique que la extensión del archivo de salida sea una de .obj, .stl, .gltf, .glb, .dae, .3mf. Las extensiones distinguen entre mayúsculas y minúsculas en Linux.
Los archivos muy grandes causan una conversión lenta
Aspose.3D FOSS procesa geometría en memoria. Para archivos con millones de polígonos, asegúrese de contar con suficiente RAM. Actualmente no hay una API streaming-write.
Preguntas Frecuentes (FAQ)
¿Puedo convertir un archivo sin escribirlo primero en el disco?
Sí. Tanto scene.open() como scene.save() aceptan objetos binarios similares a archivos además de rutas de archivo. Pase cualquier objeto que implemente read() para cargar o write() para guardar:
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)¿Se admite FBX como formato de origen para la conversión?
La tokenización de FBX está implementada parcialmente, pero el analizador no está completo. La entrada FBX puede producir escenas incompletas. Utilice OBJ, STL, glTF, COLLADA o 3MF como formatos de origen fiables.
¿Sobrevivirán los materiales a una conversión de OBJ a glTF?
Las propiedades básicas del material Phong/Lambert (color difuso) se trasladan a través del modelo Scene y se escriben en el bloque de material glTF. Los parámetros de sombreado procedimentales o personalizados que no son expresables en el modelo de material glTF se descartan.
¿Puedo convertir varios archivos en un bucle?
Sí. Cada llamada Scene.from_file() crea un objeto independiente, por lo que un bucle sobre una lista de rutas es sencillo:
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ón preserva la jerarquía de la escena (nodos padre/hijo)?
Sí. El árbol de nodos se conserva en la medida en que lo permita el formato de destino. Los formatos como STL almacenan solo geometría plana sin estructura de nodos; la jerarquía se aplana al guardar. Los formatos como glTF y COLLADA conservan la jerarquía completa.