Com carregar models 3D a Python

Com carregar models 3D a Python

Aspose.3D FOSS per a Python ofereix una API senzilla per obrir fitxers 3D sense cap dependència nativa. Després de carregar un fitxer en un Scene objecte, podeu recórrer la jerarquia de nodes i llegir les dades de geometria en brut per a cada malla a l’escena.

Guia pas a pas

Pas 1: Instal·la el paquet

Instal·leu Aspose.3D FOSS des de PyPI. No es requereixen biblioteques de sistema addicionals.

pip install aspose-3d-foss

Versions compatibles de Python: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


Pas 2: Importa la classe Scene

El Scene class és el contenidor de nivell superior per a totes les dades 3D. Importeu-la juntament amb qualsevol classe d’opcions de càrrega que necessiteu.

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

Totes les classes públiques es troben a aspose.threed o als seus subpaquets (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).


Pas 3: Carrega un fitxer

Utilitzeu el estàtic Scene.from_file() mètode per obrir qualsevol format compatible. La biblioteca detecta el format automàticament a partir de l’extensió del fitxer.

##Automatic format detection
scene = Scene.from_file("model.obj")

Alternativament, creeu una Scene instància i invoqueu open(); útil quan voleu passar opcions de càrrega o gestionar errors explícitament:

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

Ambdós mètodes admeten fitxers OBJ, STL (binari i ASCII), glTF 2.0 / GLB, COLLADA (DAE) i 3MF.


Pas 4: Recorre els nodes de l’escena

Una escena carregada és un arbre de Node objectes arrelats a scene.root_node. Itera recursivament per trobar tots els nodes:

from aspose.threed import Scene, Node

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

def walk(node: Node, depth: int = 0) -> None:
    indent = "  " * depth
    print(f"{indent}Node: {node.name!r}")
    for child in node.child_nodes:
        walk(child, depth + 1)

walk(scene.root_node)

Cada Node pot portar zero o més Entity objectes (meshes, càmeres, llums). Comprova node.entities per veure què hi ha adjuntat.


Pas 5: Accedir a les dades de Vèrtex i Polígon

Converteix l’entitat d’un node a Mesh i llegeix els seus punts de control (posicions dels vèrtexs) i polígons (llistes d’índex de cares):

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

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

for node in scene.root_node.child_nodes:
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh: Mesh = entity
            print(f"Mesh '{node.name}': "
                  f"{len(mesh.control_points)} vertices, "
                  f"{len(mesh.polygons)} polygons")

            # First vertex position
            if mesh.control_points:
                v = mesh.control_points[0]
                print(f"  First vertex: ({v.x:.4f}, {v.y:.4f}, {v.z:.4f})")

            # First polygon face (list of control-point indices)
            if mesh.polygons:
                print(f"  First polygon: {mesh.polygons[0]}")

mesh.control_points és una llista de Vector4 objectes; x, y, z porta la posició i w és la coordenada homogènia (normalment 1.0).

mesh.polygons és una llista de llistes d’enters, on cada llista interna és el conjunt ordenat d’índexs de punts de control per a una cara.


Pas 6: Aplicar opcions de càrrega específiques del format

Per a un control detallat de com s’interpreta un fitxer OBJ, passa un ObjLoadOptions instància a scene.open():

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

options = ObjLoadOptions()
options.flip_coordinate_system = True   # Convert right-hand Y-up to Z-up
options.scale = 0.01                    # Convert centimetres to metres
options.enable_materials = True         # Load .mtl material file
options.normalize_normal = True         # Normalize all normals to unit length

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

Per a fitxers STL, la classe equivalent és StlLoadOptions. Per a glTF, utilitza GltfLoadOptions. Vegeu el referència de l’API per a una llista completa.


Problemes comuns i solucions

FileNotFoundError en cridar Scene.from_file()

El camí ha de ser absolut o correcte relatiu al directori de treball en temps d’execució. Utilitzeu pathlib.Path per construir camins fiables:

from pathlib import Path
from aspose.threed import Scene

path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))

mesh.polygons està buit després de carregar un fitxer STL

Els fitxers STL emmagatzemen triangles com a facetes en brut, no com a malla indexada. Després de carregar, els polígons es sintetitzen a partir d’aquestes facetes. Si polygons apareix buit, comproveu len(mesh.control_points); si el comptatge és múltiple de 3, la geometria s’emmagatzema en forma no indexada i cada triple consecutiu de vèrtexs forma un triangle.

Desajust del sistema de coordenades (el model apareix girat o mirallat)

Diferents eines utilitzen convencions diferents (Y-up vs Z-up, mà esquerra vs mà dreta). Establiu ObjLoadOptions.flip_coordinate_system = True o apliqueu una rotació al node arrel’s Transform després de carregar.

AttributeError: 'NoneType' object has no attribute 'polygons'

La llista d’entitats d’un node pot contenir entitats que no són malles (càmeres, llums). Sempre protegeix amb isinstance(entity, Mesh) abans del casting.


Preguntes freqüents (FAQ)

Quins formats 3D puc carregar?

OBJ (Wavefront), STL (binari i ASCII), glTF 2.0 / GLB, COLLADA (DAE) i 3MF. La tokenització de fitxers FBX és parcialment compatible, però l’anàlisi completa encara no està acabada.

Carregar un fitxer OBJ també carrega el .mtl material?

Sí, quan ObjLoadOptions.enable_materials = True (per defecte). La biblioteca busca el .mtl fitxer al mateix directori que el .obj fitxer. Si el .mtl manca, la geometria encara es carrega i s’emet una advertència.

Puc carregar un fitxer des d’un flux de bytes en lloc d’un camí?

Sí. scene.open() accepta qualsevol objecte semblant a un fitxer amb un .read() mètode a més d’una cadena de ruta de fitxer. Passa un flux binari obert (p. ex., io.BytesIO) directament. Scene.from_file() només accepta una cadena de ruta de fitxer.

Com puc obtenir les normals de superfície?

Després de carregar, comprova mesh.get_element(VertexElementType.NORMAL). Això retorna un VertexElementNormal el qual data la llista conté un vector normal per referència, assignat segons mapping_mode i reference_mode.

from aspose.threed.entities import Mesh, VertexElementType

normals = mesh.get_element(VertexElementType.NORMAL)
if normals:
    print(normals.data[0])  # First normal vector

És la biblioteca thread-safe per a carregar múltiples fitxers simultàniament?

Cada Scene l’objecte és independent. Carregar fitxers separats en separats Scene instàncies des de fils separats és segur sempre que no comparteixis un únic Scene entre fils sense bloqueig extern.

 Català