Jak konwertować modele 3D w Pythonie

Jak konwertować modele 3D w Pythonie

Konwersja formatu z Aspose.3D FOSS for Python to dwustopniowy proces: wczytanie do obiektu Scene, a następnie zapis do żądanego formatu wyjściowego. Ponieważ cała geometria jest przechowywana w wspólnej reprezentacji w pamięci, nie są potrzebne specyficzne dla formatu kroki pośrednie. Poniższe sekcje pokazują najczęstsze konwersje wraz z działającym kodem.

Przewodnik krok po kroku

Krok 1: Zainstaluj pakiet

pip install aspose-3d-foss

Nie są wymagane żadne biblioteki systemowe, kompilatory ani dodatkowe zależności środowiska uruchomieniowego.


Krok 2: Załaduj model źródłowy

Użyj Scene.from_file() w najprostszym przypadku: format jest wykrywany automatycznie na podstawie rozszerzenia pliku:

from aspose.threed import Scene

scene = Scene.from_file("model.obj")

Dla plików OBJ, w których potrzebna jest kontrola nad systemem współrzędnych lub ładowaniem materiałów, użyj scene.open() z ObjLoadOptions:

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

options = ObjLoadOptions()
options.flip_coordinate_system = True  # Convert to Z-up if needed
options.enable_materials = True        # Load the accompanying .mtl file
options.normalize_normal = True

scene = Scene()
scene.open("model.obj", options)

Oba podejścia generują identyczny obiekt Scene dla kolejnego kroku zapisu.


Krok 3: Sprawdź załadowaną scenę

Zanim przystąpisz do konwersji, warto sprawdzić, czy geometria została poprawnie załadowana. Brakujący plik, nieobsługiwana funkcja FBX lub problem ze ścieżką w pliku .mtl mogą spowodować pustą scenę.

from aspose.threed import Scene
from aspose.threed.entities import Mesh

scene = Scene.from_file("model.obj")

mesh_count = 0
total_vertices = 0

def count_meshes(node) -> None:
    global mesh_count, total_vertices
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh_count += 1
            total_vertices += len(entity.control_points)
    for child in node.child_nodes:
        count_meshes(child)

count_meshes(scene.root_node)
print(f"Loaded {mesh_count} mesh(es), {total_vertices} total vertices")

if mesh_count == 0:
    raise ValueError("Scene contains no geometry: check the source file path and format")

Krok 4: Zapisz w formacie docelowym

Wywołaj scene.save() z ścieżką wyjściową. Przekaż obiekt opcji zapisu specyficzny dla formatu, aby kontrolować wyjście binarne vs ASCII, osie współrzędnych i kompresję.

OBJ do STL (binarny)

from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

scene = Scene.from_file("model.obj")

save_opts = StlSaveOptions()
##StlSaveOptions defaults to binary output, which is more compact.

scene.save("model.stl", save_opts)
print("Saved model.stl")

OBJ do glTF 2.0

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

scene = Scene.from_file("model.obj")

save_opts = GltfSaveOptions()

scene.save("model.gltf", save_opts)
print("Saved model.gltf")

Aby zapisać jako samodzielny plik binarny GLB zamiast .gltf + zewnętrznych buforów, zmień rozszerzenie wyjściowe na .glb:

scene.save("model.glb", save_opts)

OBJ do 3MF

from aspose.threed import Scene
from aspose.threed.formats import ThreeMfSaveOptions

scene = Scene.from_file("model.obj")

save_opts = ThreeMfSaveOptions()

scene.save("model.3mf", save_opts)
print("Saved model.3mf")

STL do glTF 2.0

Ten sam wzorzec ma zastosowanie niezależnie od formatu źródłowego:

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

scene = Scene.from_file("input.stl")
scene.save("output.gltf", GltfSaveOptions())
print("Saved output.gltf")

Krok 5: Zweryfikuj wynik

Po zapisaniu potwierdź, że plik wyjściowy istnieje i ma niezerowy rozmiar. Aby przeprowadzić dokładniejszą kontrolę, wczytaj go ponownie i porównaj liczbę siatek:

import os
from aspose.threed import Scene
from aspose.threed.entities import Mesh

output_path = "model.stl"

##Basic file-system check
size = os.path.getsize(output_path)
print(f"Output file size: {size} bytes")
if size == 0:
    raise RuntimeError("Output file is empty: save may have failed silently")

##Round-trip verification: reload and count geometry
def _iter_nodes(node):
    yield node
    for child in node.child_nodes:
        yield from _iter_nodes(child)

reloaded = Scene.from_file(output_path)
mesh_count = sum(
    1
    for node in _iter_nodes(reloaded.root_node)
    for entity in node.entities
    if isinstance(entity, Mesh)
)
print(f"Round-trip check: {mesh_count} mesh(es) in output")

Typowe problemy i rozwiązania

Plik wyjściowy jest tworzony, ale nie zawiera geometrii

Plik źródłowy może zostać załadowany z zerową liczbą siatek. Dodaj krok inspekcji z Kroku 3 przed zapisem. Upewnij się również, że rozszerzenie pliku odpowiada rzeczywistemu formatowi; Aspose.3D używa rozszerzenia do wyboru parsera.

Wyjście glTF nie zawiera tekstur

Aspose.3D FOSS przenosi właściwości geometrii i materiałów podczas konwersji. Jeśli źródłowy plik OBJ odwołuje się do zewnętrznych plików obrazów w .mtl, te pliki obrazów nie są automatycznie kopiowane razem z .gltf. Skopiuj obrazy tekstur do katalogu wyjściowego ręcznie po zapisaniu.

Wyjście STL wygląda od wewnątrz na zewnątrz (normalne twarzy odwrócone)

STL nie zawiera metadanych kolejności wiązania. Jeśli wyjściowe normalne są odwrócone, ustaw opcje StlSaveOptions, jeśli są dostępne, lub odwróć układ współrzędnych podczas ładowania: ObjLoadOptions.flip_coordinate_system = True.

ValueError: unsupported format podczas zapisywania

Sprawdź, czy rozszerzenie pliku wyjściowego jest jednym z .obj, .stl, .gltf, .glb, .dae, .3mf. Rozszerzenia są rozróżniane pod względem wielkości liter w systemie Linux.

Bardzo duże pliki powodują wolną konwersję

Aspose.3D FOSS przetwarza geometrię w pamięci. Dla plików z milionami wielokątów zapewnij wystarczającą ilość RAM. Obecnie nie ma API do zapisu strumieniowego.


Najczęściej zadawane pytania (FAQ)

Czy mogę przekonwertować plik bez zapisywania go najpierw na dysku?

Tak. Zarówno scene.open(), jak i scene.save() akceptują binarne obiekty podobne do pliku oprócz ścieżek do plików. Przekaż dowolny obiekt implementujący read() do ładowania lub write() do zapisywania:

import io
from aspose.threed import Scene

# Load from an in-memory buffer
data = open('model.obj', 'rb').read()
scene = Scene()
scene.open(io.BytesIO(data))

# Save to an in-memory buffer
buf = io.BytesIO()
scene.save(buf)

Czy FBX jest obsługiwany jako format źródłowy do konwersji?

Tokenizacja FBX jest częściowo zaimplementowana, ale parser nie jest kompletny. Wejście FBX może generować niekompletne sceny. Użyj formatów OBJ, STL, glTF, COLLADA lub 3MF jako niezawodnych formatów źródłowych.

Czy materiały przetrwają konwersję OBJ do glTF?

Podstawowe właściwości materiału Phong/Lambert (kolor rozpraszania) są przenoszone przez model Scene i zapisywane w bloku materiału glTF. Proceduralne lub niestandardowe parametry shaderów, które nie mogą być wyrażone w modelu materiału glTF, są pomijane.

Czy mogę konwertować wiele plików w pętli?

Tak. Każde wywołanie Scene.from_file() tworzy niezależny obiekt, więc pętla po liście ścieżek jest prosta:

from pathlib import Path
from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

source_dir = Path("input")
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

opts = StlSaveOptions()
for obj_file in source_dir.glob("*.obj"):
    scene = Scene.from_file(str(obj_file))
    out_path = output_dir / obj_file.with_suffix(".stl").name
    scene.save(str(out_path), opts)
    print(f"Converted {obj_file.name} -> {out_path.name}")

Czy konwersja zachowuje hierarchię sceny (węzły nadrzędne/podrzędne)?

Tak. Drzewo węzłów jest zachowywane w miarę możliwości formatu docelowego. Formaty takie jak STL przechowują tylko płaską geometrię bez struktury węzłów; hierarchia jest spłaszczana przy zapisie. Formaty takie jak glTF i COLLADA zachowują pełną hierarchię.

 Polski