Como Carregar Modelos 3D em Python
Aspose.3D FOSS para Python fornece uma API simples para abrir arquivos 3D sem dependências nativas. Após carregar um arquivo em um Scene objeto, você pode percorrer a hierarquia de nós e ler os dados de geometria bruta de cada malha na cena.
Guia passo a passo
Etapa 1: Instalar o pacote
Instale Aspose.3D FOSS a partir do PyPI. Nenhuma biblioteca de sistema adicional é necessária.
pip install aspose-3d-fossVersões suportadas do Python: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
Etapa 2: Importar a classe Scene
O Scene classe é o contêiner de nível superior para todos os dados 3D. Importe-a junto com quaisquer classes de opções de carregamento que você precisar.
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptionsTodas as classes públicas estão sob aspose.threed ou seus subpacotes (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).
Etapa 3: Carregar um arquivo
Use o estático Scene.from_file() método para abrir qualquer formato suportado. A biblioteca detecta o formato automaticamente a partir da extensão do arquivo.
##Automatic format detection
scene = Scene.from_file("model.obj")Alternativamente, crie um Scene instância e chame open(); útil quando você deseja passar opções de carregamento ou tratar erros explicitamente:
scene = Scene()
scene.open("model.obj")Ambos os métodos suportam arquivos OBJ, STL (binário e ASCII), glTF 2.0 / GLB, COLLADA (DAE) e 3MF.
Etapa 4: Percorrer os nós da cena
Uma cena carregada é uma árvore de Node objetos enraizados em scene.root_node. Itere recursivamente para encontrar todos os nós:
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 pode conter zero ou mais Entity objetos (malhas, câmeras, luzes). Verifique node.entities para ver o que está anexado.
Etapa 5: Acessar Dados de Vértices e Polígonos
Converta a entidade de um nó para Mesh e leia seus pontos de controle (posições dos vértices) e polígonos (listas de índices de faces):
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 é uma lista de Vector4 objetos; x, y, z carregam a posição e w é a coordenada homogênea (normalmente 1.0).
mesh.polygons é uma lista de listas de inteiros, onde cada lista interna é o conjunto ordenado de índices de pontos de controle para uma face.
Etapa 6: Aplicar Opções de Carregamento Específicas ao Formato
Para controle detalhado sobre como um arquivo OBJ é interpretado, passe um ObjLoadOptions instância para 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)Para arquivos STL, a classe equivalente é StlLoadOptions. Para glTF, use GltfLoadOptions. Consulte o referência da API para uma lista completa.
Problemas comuns e correções
FileNotFoundError ao chamar Scene.from_file()
O caminho deve ser absoluto ou relativo correto ao diretório de trabalho em tempo de execução. Use pathlib.Path para construir caminhos confiáveis:
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á vazio após carregar um arquivo STL
Arquivos STL armazenam triângulos como facetas brutas, não como uma malha indexada. Após o carregamento, polígonos são sintetizados a partir dessas facetas. Se polygons parece vazio, verifique len(mesh.control_points); se a contagem for múltipla de 3, a geometria está armazenada em forma não indexada e cada tripla consecutiva de vértices forma um triângulo.
Incompatibilidade de sistema de coordenadas (o modelo parece rotacionado ou espelhado)
Ferramentas diferentes usam convenções diferentes (Y-up vs Z-up, mão esquerda vs mão direita). Defina ObjLoadOptions.flip_coordinate_system = True ou aplique uma rotação ao root node’s Transform após o carregamento.
AttributeError: 'NoneType' object has no attribute 'polygons'
A lista de entidades de um node pode conter entidades que não são malha (câmeras, luzes). Sempre verifique com isinstance(entity, Mesh) antes de casting.
Perguntas Frequentes (FAQ)
Quais formatos 3D eu posso carregar?
OBJ (Wavefront), STL (binário e ASCII), glTF 2.0 / GLB, COLLADA (DAE) e 3MF. A tokenização de arquivos FBX tem suporte parcial, mas o parsing completo ainda não está concluído.
Carregar um arquivo OBJ também carrega o .mtl material?
Sim, quando ObjLoadOptions.enable_materials = True (o padrão). A biblioteca procura o .mtl arquivo no mesmo diretório que o .obj arquivo. Se o .mtl está ausente, a geometria ainda é carregada e um aviso é emitido.
Posso carregar um arquivo a partir de um fluxo de bytes em vez de um caminho?
Sim. scene.open() aceita qualquer objeto semelhante a arquivo com um .read() método além de uma string de caminho de arquivo. Passe um fluxo binário aberto (por exemplo,., io.BytesIO) diretamente. Scene.from_file() aceita apenas uma string de caminho de arquivo.
Como obtenho as normais de superfície?
Após o carregamento, verifique mesh.get_element(VertexElementType.NORMAL). Isso retorna um VertexElementNormal cujo data lista contém um vetor normal por referência, mapeado de acordo com 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 vectorA biblioteca é thread-safe para carregar vários arquivos simultaneamente?
Cada Scene objeto é independente. Carregar arquivos separados em Scene instâncias de threads separadas é seguro, desde que você não compartilhe um único Scene entre threads sem bloqueio externo.