Com carregar models 3D a Python
Aspose.3D FOSS for Python ofereix una API senzilla per obrir fitxers 3D sense cap dependència nativa. Després de carregar un fitxer en un objecte Scene, podeu recórrer la jerarquia de nodes i llegir les dades de geometria en brut per a cada malla de l’escena.
Guia pas a pas
Pas 1: Instal·la el paquet
Instal·la Aspose.3D FOSS des de PyPI. No es requereixen biblioteques del sistema addicionals.
pip install aspose-3d-fossVersions de Python compatibles: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
Pas 2: Importa la classe Scene
La classe Scene és el contenidor de nivell superior per a totes les dades 3D. Importa-la juntament amb qualsevol classe d’opció de càrrega que necessitis.
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptionsTotes 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 mètode estàtic Scene.from_file() 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 instància Scene 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: Recórrer els nodes de l’escena
Una escena carregada és un arbre d’objectes Node arrelat 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 objectes Entity (malles, càmeres, llums). Comproveu node.entities per veure què hi ha adjuntat.
Pas 5: Accés 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’índexs 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 d’objectes Vector4; x, y, z porten 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: Aplica opcions de càrrega específiques del format
Per a un control detallat sobre com s’interpreta un fitxer OBJ, passeu una instància ObjLoadOptions 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 als fitxers STL, la classe equivalent és StlLoadOptions. Per a glTF, utilitzeu GltfLoadOptions. Consulteu la 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 respecte al directori de treball en temps d’execució. Utilitzeu pathlib.Path per crear 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 recompte é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 sembla girat o reflectit)
Diferents eines utilitzen diferents convencions (Y-up vs Z-up, mà esquerra vs mà dreta). Establiu ObjLoadOptions.flip_coordinate_system = True o apliqueu una rotació al Transform del node arrel 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 de fer el 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 suportada però l’anàlisi completa encara no està completa.
Carregar un fitxer OBJ també carrega el material .mtl?
Sí, quan ObjLoadOptions.enable_materials = True (per defecte). La biblioteca busca el fitxer .mtl al mateix directori que el fitxer .obj. Si el .mtl falta, 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 similar a un fitxer amb un mètode .read() a més d’una cadena de ruta de fitxer. Passeu directament un flux binari obert (p. ex., io.BytesIO). Scene.from_file() només accepta una cadena de ruta de fitxer.
Com puc obtenir les normals de superfície?
Després de carregar, comproveu mesh.get_element(VertexElementType.NORMAL). Això retorna un VertexElementNormal, la llista de data 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 diversos fitxers simultàniament?
Cada objecte Scene és independent. Carregar fitxers separats en instàncies separades de Scene des de fils separats és segur sempre que no comparteixis un únic Scene entre fils sense bloqueig extern.