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-fossNie 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ę.