Jak ładować modele 3D w Python

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-foss

Obsł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 ObjLoadOptions

Wszystkie 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 vector

Czy 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.

 Polski