Jak przejść przez 3D Scene Graph w Pythonie
Graficzna scena w Aspose.3D FOSS jest drzewem Node Obiekty z korzeniami w scene.root_node.Każdy plik 3D, niezależnie od tego, czy jest ładowany z OBJ, glTF, STL, COLLADA lub 3MF produkuje tę samą strukturę drzew. wiedza jak ją przechodzić pozwala sprawdzić geometrię, liczyć poligony, filtrować obiekty według typu i przetwarzać konkretne części złożonej sceny.
Przewodnik krok po kroku
Krok 1: Instalacja i import
Instalacja Aspose.3D FOSS z PyPI:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.
Wprowadź klasę, której potrzebujesz:
from aspose.threed import Scene
from aspose.threed.entities import MeshWszystkie klasy publiczne żyją pod aspose.threed lub jego podpakowania (aspose.threed.entities, aspose.threed.utilities).
Krok 2: Pobierz scenę z pliku
Użyj statycznego Scene.from_file() metoda otwierania dowolnego obsługiwanego formatu. format jest automatycznie wykryty z rozszerzenia pliku:
scene = Scene.from_file("model.gltf")Możesz również otworzyć z wyraźnymi opcjami ładowania:
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.enable_materials = True
scene = Scene()
scene.open("model.obj", options)po ładowaniu, scene.root_node Wszystkie importowane węzły to dzieci lub potomstwo tego wązu.
Krok 3: Napisz funkcję recursywnej
Najprostsza przepływowa odwiedza każdy węzły w pierwszej kolejności:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def traverse(node, depth=0):
prefix = " " * depth
entity_name = type(node.entity).__name__ if node.entity else "-"
print(f"{prefix}[{entity_name}] {node.name}")
for child in node.child_nodes:
traverse(child, depth + 1)
scene = Scene.from_file("model.gltf")
traverse(scene.root_node)Przykładowe wyniki:
[-]
[-] Armature
[Mesh] Body
[Mesh] Eyes
[-] Ground
[Mesh] Plane<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Copy code”
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
Uwaga : Właściciele są w stanie wyznaczyć pustą nazwę (
""W tym przypadku pierwsza linia pokazuje[-]Bez nazwiska następnego.
node.child_nodes zwraca dzieci w zamówieniu wprowadzania (porządek, w którym importer lub użytkownik dodał je).
Krok 4: Właściwości podmiotu dostępu na każdym przyciskie
Używanie node.entity aby uzyskać pierwszą jednostkę przymocowaną do nodu, lub node.entities Sprawdź typ przed uzyskaniem dostępu do właściwości specyficznych dla formatu:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def print_entity_details(node, depth=0):
indent = " " * depth
for entity in node.entities:
if isinstance(entity, Mesh):
mesh = entity
print(f"{indent}Mesh '{node.name}':")
print(f"{indent} vertices : {len(mesh.control_points)}")
print(f"{indent} polygons : {mesh.polygon_count}")
print(f"{indent} cast_shadows : {mesh.cast_shadows}")
print(f"{indent} receive_shadows: {mesh.receive_shadows}")
for child in node.child_nodes:
print_entity_details(child, depth + 1)
scene = Scene.from_file("model.gltf")
print_entity_details(scene.root_node)node.transform.translation, node.transform.rotation,i node.transform.scaling Zapewniamy przemianę lokalną. node.global_transform Wskaźnik oceny wyników (z global_transform.scale w skali kosmicznej).
Krok 5: Filtruj węzły według typu podmiotu
Aby działać tylko na określonych typach podmiotów, dodaj jeden isinstance Strażnik wewnątrz przewodu:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def find_mesh_nodes(node, results=None):
if results is None:
results = []
for entity in node.entities:
if isinstance(entity, Mesh):
results.append(node)
break # One match per node is enough
for child in node.child_nodes:
find_mesh_nodes(child, results)
return results
scene = Scene.from_file("model.gltf")
mesh_nodes = find_mesh_nodes(scene.root_node)
print(f"Found {len(mesh_nodes)} mesh node(s)")
for n in mesh_nodes:
print(f" {n.name}")Ten wzór jest przydatny dla operacji masowych: zastosowanie transformy do każdego węzła, zastąpienie materiałów lub wyeksportowanie poddrzew.
Krok 6: Zbieraj wszystkie koszyki i drukuj konty vertekowe
Łączysz statystyki w jednym pasie:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def collect_meshes(node, results=None):
if results is None:
results = []
if isinstance(node.entity, Mesh):
results.append((node.name, node.entity))
for child in node.child_nodes:
collect_meshes(child, results)
return results
scene = Scene.from_file("model.gltf")
meshes = collect_meshes(scene.root_node)
print(f"Total meshes: {len(meshes)}")
total_verts = 0
total_polys = 0
for name, mesh in meshes:
verts = len(mesh.control_points)
polys = mesh.polygon_count
total_verts += verts
total_polys += polys
print(f" {name}: {verts} vertices, {polys} polygons")
print(f"Scene totals: {total_verts} vertices, {total_polys} polygons")wspólne kwestie
| kwestia | Rezolucja |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Straż z if node.entity is not None lub isinstance(node.entity, Mesh) przed uzyskaniem dostępu do nieruchomości. noty bez podmiotów powrót None. |
| Podróże wcześnie zatrzymują się | Upewnij się, że odwrócenie dotrze do node.child_nodes.Jeśli tylko iterujesz scene.root_node.child_nodes (nie odwracając się), brakuje Ci wszystkich potomków. |
| Mesh brakuje z zebranych wyników | Sprawdź, czy format pliku zachowuje hierarchię. OBJ rozcieńcz całą geometrię na poziomie jednego węzła. Użyj glTF lub COLLADA do głębokich hierarchii. |
node.entity Wróci tylko pierwsza jednostka. | Używanie node.entities (pełna lista) kiedy węzły przynoszą wiele podmiotów. node.entity Jest to krótka ręką dla node.entities[0] Kiedy entities Jest niepustny. |
collect_meshes zwraca 0 wyników dla ładowanego STL | Pliki STL zazwyczaj wytwarzają jeden płaski węzły z jednym podmiotem masowym bezpośrednio poniżej root_node.Sprawdź root_node.child_nodes[0].entity bezpośrednio . |
| Nazwy node są puste paski | Niektóre formaty (binary STL, niektóre pliki OBJ) nie przechowują nazw obiektów. name Zamiast tego użyj indeksów do identyfikacji. |
Często zadawane pytania
Jak głęboko może być graf sceny?
Nie ma żadnych trudnych granic. domyślne ograniczenie recursji Pythona (1000 ram) stosuje się do funkcji recurzyjnej przewrotnej. Dla bardzo głębokich hierarchii, przekształć recury w wyraźny szlak:
from collections import deque
from aspose.threed import Scene
scene = Scene.from_file("deep.gltf")
queue = deque([(scene.root_node, 0)])
while queue:
node, depth = queue.popleft()
print(" " * depth + node.name)
for child in node.child_nodes:
queue.append((child, depth + 1))Czy mogę zmienić drzewo podczas przechodzenia przez niego?
Nie dodaj ani nie usuwaj węzłów z child_nodes Zbieraj przyciski do modyfikacji w pierwszym pasie, a następnie wprowadź zmiany w drugim pasze.
Jak znaleźć konkretny węzły według nazwy?
Używanie node.get_child(name) Aby znaleźć bezpośrednie dziecko według nazwy.W celu wyszukiwania, przejdź przez drzewo z filtrem imienia:
def find_by_name(root, name):
if root.name == name:
return root
for child in root.child_nodes:
result = find_by_name(child, name)
if result:
return result
return None
target = find_by_name(scene.root_node, "Wheel_FL")Czyli node.entity Zawsze powróci do Mesh?
No. Podstawowe podmioty mogą posiadać dowolny typ jednostki: Mesh, Camera, Light,lub podmioty indywidualne. zawsze sprawdź z isinstance(node.entity, Mesh) przed użyciem właściwości specjalnych dla mysz.
Jak uzyskać światową pozycję kosmiczną węzła?
Czytaj node.global_transform.translation.To oceniana pozycja w przestrzeni światowej, z uwzględnieniem wszystkich przodków transformacji. jest czytane; modyfikowane node.transform.translation W celu odbudowy węzła.
Czy mogę liczyć całkowite poligony w scenie bez pisania przewodu?
Nie bezpośrednio przez API; nie ma scene.total_polygon_count Właściwości - użycie collect_meshes i sumy mesh.polygon_count W wyniku tego, jak pokazano w kroku 6.