Jak ładować modele 3D w Python
Aspose.3D FOSS dla Python zapewnia prosty interfejs API do otwierania plików 3D bez żadnych natywnych zależności. Po załadowaniu pliku do Scene obiekt, możesz przejść hierarchię węzłów i odczytać surowe dane geometryczne dla każdej siatki w scenie.
Przewodnik krok po kroku
Krok 1: Zainstaluj pakiet
Zainstaluj Aspose.3D FOSS z PyPI. Nie są wymagane dodatkowe biblioteki systemowe.
pip install aspose-3d-fossObsługiwane wersje Python: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
Krok 2: Zaimportuj klasę Scene
Ten Scene class jest kontenerem najwyższego poziomu dla wszystkich danych 3D. Zaimportuj go wraz z dowolnymi klasami opcji ładowania, które są potrzebne.
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptionsWszystkie klasy publiczne znajdują się pod aspose.threed lub jego podpakietów (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).
Krok 3: Załaduj plik
Użyj statycznego Scene.from_file() metoda otwierania dowolnego obsługiwanego formatu. Biblioteka automatycznie wykrywa format na podstawie rozszerzenia pliku.
##Automatic format detection
scene = Scene.from_file("model.obj")Alternatywnie, utwórz Scene instancja i wywołanie open(); przydatne, gdy chcesz przekazać opcje ładowania lub obsłużyć błędy jawnie:
scene = Scene()
scene.open("model.obj")Obie metody obsługują pliki OBJ, STL (binarny i ASCII), glTF 2.0 / GLB, COLLADA (DAE) oraz 3MF.
Krok 4: Przejdź po węzłach sceny
Załadowana scena jest drzewem Node obiektów zakorzenionych w scene.root_node. Iteruj rekurencyjnie, aby znaleźć wszystkie węzły:
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)Każdy Node może zawierać zero lub więcej Entity obiektów (meshes, cameras, lights). Sprawdź node.entities aby zobaczyć, co jest podłączone.
Krok 5: Dostęp do danych wierzchołków i wielokątów
Rzutuj entity węzła na Mesh i odczytaj jego punkty kontrolne (vertex positions) oraz wielokąty (face index lists):
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 jest listą Vector4 obiektów; x, y, z przenosi pozycję i w jest współrzędną jednorodną (zwykle 1.0).
mesh.polygons jest listą list liczb całkowitych, gdzie każda wewnętrzna lista jest uporządkowanym zestawem indeksów punktów kontrolnych dla jednej ściany.
Krok 6: Zastosowanie opcji ładowania specyficznych dla formatu
Aby uzyskać precyzyjną kontrolę nad tym, jak interpretowany jest plik OBJ, przekaż ObjLoadOptions instancję do 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)Dla plików STL odpowiednią klasą jest StlLoadOptions. Dla glTF użyj GltfLoadOptions. Zobacz referencję API dla pełnej listy.
Typowe problemy i rozwiązania
FileNotFoundError podczas wywoływania Scene.from_file()
Ścieżka musi być absolutna lub poprawna względem katalogu roboczego w czasie wykonywania. Użyj pathlib.Path do budowania niezawodnych ścieżek:
from pathlib import Path
from aspose.threed import Scene
path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))mesh.polygons jest pusty po załadowaniu pliku STL
Pliki STL przechowują trójkąty jako surowe fasety, a nie jako siatkę indeksowaną. Po załadowaniu wielokąty są syntetyzowane z tych faset. Jeśli polygons wygląda na pusty, sprawdź len(mesh.control_points); jeśli liczba jest wielokrotnością 3, geometria jest przechowywana w formie nieindeksowanej i każda kolejna trójka wierzchołków tworzy jeden trójkąt.
Niezgodność systemu współrzędnych (model wydaje się obrócony lub odbity)
Różne narzędzia używają różnych konwencji (Y-up vs Z-up, leworęczna vs praworęczna). Ustaw ObjLoadOptions.flip_coordinate_system = True lub zastosuj rotację do root node’s Transform po załadowaniu.
AttributeError: 'NoneType' object has no attribute 'polygons'
Lista encji węzła może zawierać encje niebędące siatkami (kamery, światła). Zawsze zabezpieczaj przy użyciu isinstance(entity, Mesh) przed rzutowaniem.
Najczęściej zadawane pytania (FAQ)
Jakie formaty 3D mogę załadować?
OBJ (Wavefront), STL (binarny i ASCII), glTF 2.0 / GLB, COLLADA (DAE) oraz 3MF. Tokenizacja plików FBX jest częściowo wspierana, ale pełne parsowanie nie jest jeszcze zakończone.
Czy wczytanie pliku OBJ również wczytuje .mtl materiał?
Tak, gdy ObjLoadOptions.enable_materials = True (domyślnie). Biblioteka szuka .mtl pliku w tym samym katalogu co .obj plik. Jeśli .mtl brakuje, geometria jest nadal wczytywana i zostaje wyemitowane ostrzeżenie.
Czy mogę załadować plik ze strumienia bajtów zamiast ze ścieżki?
Tak. scene.open() akceptuje każdy obiekt podobny do pliku z .read() metodą oprócz łańcucha ścieżki pliku. Przekaż otwarty strumień binarny (np., io.BytesIO) bezpośrednio. Scene.from_file() akceptuje tylko ciąg znaków ścieżki do pliku.
Jak uzyskać wektory normalne powierzchni?
Po załadowaniu sprawdź mesh.get_element(VertexElementType.NORMAL). Zwraca to VertexElementNormal którego data lista zawiera jeden wektor normalny na odniesienie, mapowany zgodnie z mapping_mode oraz reference_mode.
from aspose.threed.entities import Mesh, VertexElementType
normals = mesh.get_element(VertexElementType.NORMAL)
if normals:
print(normals.data[0]) # First normal vectorCzy biblioteka jest bezpieczna wątkowo przy jednoczesnym ładowaniu wielu plików?
Każdy Scene obiekt jest niezależny. Ładowanie oddzielnych plików do oddzielnych Scene instancji z oddzielnych wątków jest bezpieczne, o ile nie udostępniasz pojedynczego Scene pomiędzy wątkami bez zewnętrznego blokowania.