Jak projít 3D scénovým grafem v Pythonu
Graf scény v Aspose.3D FOSS je stromem Node objekty zakořeněné v scene.root_node.Každý 3D soubor, ať už je načten z OBJ, glTF, STL, COLLADA nebo 3MF, vytváří stejnou stromu. Když víte jak ji projít, můžete kontrolovat geometrii, počítat polygony, filtrovat objekty podle typu a zpracovávat specifické části složitého scény.
Krok za krokem
Krok 1: Instalace a import
Instalace Aspose.3D FOSS z PyPI:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.
Import potřebných tříd:
from aspose.threed import Scene
from aspose.threed.entities import MeshVšechny veřejné třídy žijí pod aspose.threed nebo jeho podbalíčky (aspose.threed.entities, aspose.threed.utilities).
Krok 2: Nahrát scénu z souboru
Použijte statické . Scene.from_file() Metodou pro otevření jakéhokoli podporovaného formátu. Formát je automaticky detekován z rozšíření souboru:
scene = Scene.from_file("model.gltf")Můžete také otevřít s explicitními možnostmi načítání:
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 naložení, scene.root_node Všechny dovážené uzly jsou dětmi nebo potomky tohoto uzlu.
Krok 3: Napište rekurzivní průchodovou funkci
Nejjednodušší průjezd navštěvuje každý uzel v prvním řádu hlubiny:
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)Příkladový výstup:
[-]
[-] 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=“Zkopírovat kód”
<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>
Poznámka: Root node má prázdný název (
""), takže první řádek ukazuje, že se jedná o[-]bez jména.
node.child_nodes vrací děti v pořadí vložení (pořadím, ve kterém je dovozce nebo uživatel přidal).
Krok 4: Přístup k vlastnostem entit na každém uzlu
Použití: node.entity získat první entitu připojenou k uzlu, nebo node.entities Před přístupem k vlastnostem specifickým pro formát ověřte typ:
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,a) node.transform.scaling Převod místního prostoru. node.global_transform dává vyhodnocený výsledek světového prostoru (s global_transform.scale pro světový vesmírný rozsah).
Krok 5: Filtrovat uzly podle typu entity
Pro provoz pouze na konkrétních typech subjektů se přidá položka “Přidání” a doplňková částka ‘Zpětná hodnota’. isinstance Stráž uvnitř průchodu:
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}")Tento vzor je užitečný pro velké operace: aplikace transformace na každý uzel sítě, nahrazení materiálů nebo exportování podstromů.
Krok 6: Sbírá všechny mísy a tisknout počty vrcholů
Souhrnné statistické údaje o síti v jednom průletu:
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")Obvyklé problémy
| Výše: | Rozhodnutí |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Stráž s: if node.entity is not None nebo: isinstance(node.entity, Mesh) před přístupem k vlastnostem sítě. Vstupy uzlů bez entit None. |
| Přestup se zastaví brzy. | Zajistit rekurzi dojde do node.child_nodes.Pokud se to bude opakovat jen… scene.root_node.child_nodes (ne rekurzivně) chybí vám všechny potomky. |
| Získané výsledky chybí. | Ověřte, zda formát souboru zachovává hierarchii. OBJ ploší celou geometrii na jednu úroveň uzlu. Použijte glTF nebo COLLADA pro hluboké hierarchie. |
node.entity Způsob, jakým se vypořádá s vývojem. | Použití: node.entities (plný seznam) když uzel nese více entit. node.entity je zkratka pro: node.entities[0] kdy? entities není prázdná. |
collect_meshes Vrátí 0 výsledků pro nabitý STL. | STL soubory obvykle vytvářejí jediný plochý uzel s jedním síťovým subjektem přímo pod root_node.- Ček. root_node.child_nodes[0].entity přímo. |
| Jména uzlů jsou prázdné řetězce. | Některé formáty (binární STL, některé soubory OBJ) neschovávají jména objektů. Vázky budou mít prázdné name řetězce; místo toho použijte index pro identifikaci. |
Časté otázky
Jak hluboký může být graf scény?
Neexistuje žádný pevný limit. Pythonova výchozí rekurzní hranice (1000 rámců) se vztahuje na recurzivní traverse funkce. Pro velmi hluboké hierarchie konvertujte rekursí do explicitního stacku:
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))Můžu změnit strom, když ho procházím?
Nepridávejte ani neodstraňujte uzly z: child_nodes Sbíráme uzly, které budeme upravovat v prvním průchodu a pak použijeme změny ve druhém.
Jak najdu konkrétní uzel podle jména?
Použití: node.get_child(name) Pro hluboké vyhledávání projděte stromem s filtrem jmen:
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")- Ano. node.entity Vždycky vracíš Mesh?
Ne. Vstupní můžou mít jakýkoliv typ entit: Mesh, Camera, Light, nebo celních subjektů. isinstance(node.entity, Mesh) před použitím vlastností specifických pro okovy.
Jak se dostaneme k pozici uzlu ve vesmíru?
Čtěte. node.global_transform.translation. Toto je hodnocená pozice ve světovém prostoru, s přihlédnutím ke všem předků. Je pouze pro čtení; modify node.transform.translation Přemístění uzlu.
Můžu počítat celkové mnohoúhlé v scéně bez písemného průchodu?
Ne přímo prostřednictvím API; neexistuje žádná scene.total_polygon_count majetek, použití collect_meshes a součet mesh.polygon_count v rámci výsledků, jak je znázorněno ve kroku 6.