Come caricare modelli 3D in Python
Aspose.3D FOSS per Python fornisce un’API semplice per aprire file 3D senza dipendenze native. Dopo aver caricato un file in un Scene oggetto, è possibile attraversare la gerarchia dei nodi e leggere i dati di geometria grezzi per ogni mesh nella scena.
Guida passo-passo
Passo 1: Installa il pacchetto
Installa Aspose.3D FOSS da PyPI. Non sono richieste librerie di sistema aggiuntive.
pip install aspose-3d-fossVersioni Python supportate: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
Passo 2: Importa la classe Scene
Il Scene classe è il contenitore di livello superiore per tutti i dati 3D. Importala insieme a qualsiasi classe di opzioni di caricamento di cui hai bisogno.
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptionsTutte le classi pubbliche si trovano sotto aspose.threed o nei suoi sotto-pacchetti (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).
Passo 3: Carica un file
Usa il metodo statico Scene.from_file() per aprire qualsiasi formato supportato. La libreria rileva automaticamente il formato dall’estensione del file.
##Automatic format detection
scene = Scene.from_file("model.obj")In alternativa, crea un Scene istanza e chiama open(); utile quando vuoi passare opzioni di caricamento o gestire gli errori esplicitamente:
scene = Scene()
scene.open("model.obj")Entrambi i metodi supportano i file OBJ, STL (binario e ASCII), glTF 2.0 / GLB, COLLADA (DAE) e 3MF.
Passo 4: Attraversa i nodi della scena
Una scena caricata è un albero di Node oggetti radicati in scene.root_node. Itera ricorsivamente per trovare tutti i nodi:
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)Ogni Node può contenere zero o più Entity oggetti (mesh, telecamere, luci). Controlla node.entities per vedere cosa è collegato.
Passo 5: Accedi ai dati dei vertici e dei poligoni
Esegui il cast dell’entità di un nodo a Mesh e leggi i suoi punti di controllo (posizioni dei vertici) e i poligoni (elenchi di indici delle facce):
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 è un elenco di Vector4 oggetti; x, y, z contengono la posizione e w è la coordinata omogenea (normalmente 1.0).
mesh.polygons è un elenco di elenchi di interi, dove ogni elenco interno è l’insieme ordinato di indici dei punti di controllo per una faccia.
Passo 6: Applica le opzioni di caricamento specifiche per il formato
Per un controllo dettagliato su come viene interpretato un file OBJ, passa un ObjLoadOptions istanza 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 i file STL, la classe equivalente è StlLoadOptions. Per glTF, usa GltfLoadOptions. Vedi la riferimento API per un elenco completo.
Problemi comuni e soluzioni
FileNotFoundError durante la chiamata a Scene.from_file()
Il percorso deve essere assoluto o corretto relativo alla directory di lavoro a runtime. Usa pathlib.Path per costruire percorsi affidabili:
from pathlib import Path
from aspose.threed import Scene
path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))mesh.polygons è vuoto dopo il caricamento di un file STL
I file STL memorizzano i triangoli come faccette grezze, non come una mesh indicizzata. Dopo il caricamento, i poligoni vengono sintetizzati da quelle faccette. Se polygons appare vuoto, controlla len(mesh.control_points); se il conteggio è un multiplo di 3 la geometria è memorizzata in forma non indicizzata e ogni tripla consecutiva di vertici forma un triangolo.
Discrepanza del sistema di coordinate (il modello appare ruotato o specchiato)
Strumenti diversi usano convenzioni diverse (Y-up vs Z-up, mano sinistra vs mano destra). Imposta ObjLoadOptions.flip_coordinate_system = True oppure applicare una rotazione al nodo radice’s Transform dopo il caricamento.
AttributeError: 'NoneType' object has no attribute 'polygons'
L’elenco delle entità di un nodo può contenere entità non mesh (telecamere, luci). Proteggi sempre con isinstance(entity, Mesh) prima del cast.
Domande frequenti (FAQ)
Quali formati 3D posso caricare?
OBJ (Wavefront), STL (binario e ASCII), glTF 2.0 / GLB, COLLADA (DAE) e 3MF. La tokenizzazione dei file FBX è parzialmente supportata, ma il parsing completo non è ancora terminato.
Il caricamento di un file OBJ carica anche il .mtl materiale?
Sì, quando ObjLoadOptions.enable_materials = True (predefinito). La libreria cerca il .mtl file nella stessa directory del .obj file. Se il .mtl è mancante, la geometria viene comunque caricata e viene emesso un avviso.
Posso caricare un file da uno stream di byte invece che da un percorso?
Sì. scene.open() accetta qualsiasi oggetto simile a un file con un .read() metodo oltre a una stringa di percorso file. Passa un flusso binario aperto (ad es., io.BytesIO) direttamente. Scene.from_file() accetta solo una stringa di percorso file.
Come ottengo le normali di superficie?
Dopo il caricamento, verifica mesh.get_element(VertexElementType.NORMAL). Questo restituisce un VertexElementNormal il cui data l’elenco contiene un vettore normale per riferimento, mappato secondo mapping_mode e reference_mode.
from aspose.threed.entities import Mesh, VertexElementType
normals = mesh.get_element(VertexElementType.NORMAL)
if normals:
print(normals.data[0]) # First normal vectorLa libreria è thread-safe per il caricamento di più file contemporaneamente?
Ogni Scene l’oggetto è indipendente. Caricare file separati in separati Scene le istanze da thread separati sono sicure finché non condividi un singolo Scene tra i thread senza blocco esterno.