Cómo convertir modelos 3D en Python

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

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

 Español