Hur man passerar en 3D Scene Graph i Python
Scenografin i Aspose.3D FOSS är ett träd av Node föremål som är rötta i scene.root_node.Varje 3D-fil, oavsett om den laddas från OBJ, glTF, STL, COLLADA eller 3MF producerar samma trädstruktur. Att veta hur man passerar det gör att du kan inspektera geometri, räkna polygoner, filtrera objekt efter typ och behandla specifika delar av en komplex scen.
Steg för steg guide
Steg 1: Installera och importera
Installera Aspose.3D FOSS från PyPI:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.
Import de klasser du behöver:
from aspose.threed import Scene
from aspose.threed.entities import MeshAlla offentliga klasser lever under aspose.threed eller dess underförpackningar (aspose.threed.entities, aspose.threed.utilities).
Steg 2: Ladda upp en scen från en fil
Använd statisk Scene.from_file() Metoden för att öppna något stöds format. Formatet upptäcks automatiskt från filutvidgningen:
scene = Scene.from_file("model.gltf")Du kan också öppna med uttryckliga laddningsalternativ:
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.enable_materials = True
scene = Scene()
scene.open("model.obj", options)Efter att laddningen, scene.root_node Alla importerade noder är barn eller efterkommande av denna nod.
Steg 3: Skriv en recursiv resefunktion
Den enklaste traversalen besöker varje nod i djup-första ordningen:
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)Exempel på utgång:
[-]
[-] 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>
Notera på: Röda noden har ett tomt namn (
""1) Så den första linjen visar[-]utan namn efter.
node.child_nodes returnerar barn i insättningsorder (den order som importören eller användaren har lagt till dem).
Steg 4: Tillgången till entitetsegenskaper på varje nod
Användning node.entity för att få den första enheten ansluten till ett nod, eller node.entities Kontrollera typen innan du får tillgång till formatspecifika egenskaper:
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,och node.transform.scaling ge den lokala rymden omvandling. node.global_transform ger den utvärderade världsutrymmeresultatet (med global_transform.scale för världsskala).
Steg 5: Filtrera noder efter entitetstyp
För att endast fungera på specifika enhetstyper, lägga till en isinstance Vakt i gränsen:
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}")Denna mönster är användbar för massoperationer: tillämpa en transformation på varje mesh nod, ersätta material eller exportera underträd.
Steg 6: Samla alla mässa och trycka vertex-konton
Aggregera mesh statistik i en enda pass:
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")Vanliga frågor
| Problemet | resolution |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Vakt med if node.entity is not None eller isinstance(node.entity, Mesh) innan du får tillgång till mesh-ägenheter. knappar utan entiteter återvänder None. |
| Resan stannar tidigt | Se till att återgången når in i node.child_nodes.Om du bara itererar scene.root_node.child_nodes (och inte återkommande) du saknar alla efterkomlingar. |
| Mesh saknas från samlade resultat | Kontrollera att filformat bevarar hierarki. OBJ flätar all geometri i en enda node nivå. Använd glTF eller COLLADA för djupa hierarkier. |
node.entity återkommer endast den första enheten. | Användning node.entities (den fullständiga listan) när en nod bär flera entiteter. node.entity är en kort hand för node.entities[0] När entities är icke-vakt. |
collect_meshes returnerar 0 resultat för en laddad STL | STL-filer producerar vanligtvis en enda platt nod med ett mäs entitet direkt under root_node.- Kontrollera root_node.child_nodes[0].entity direkt från . |
| Nodenamn är tomma strängar | Vissa format (binär STL, vissa OBJ-filer) lagrar inte objektnamn. name strängar; använd indexet för identifiering istället. |
Ofta ställda frågor
Hur djupt kan en scen graf vara?
Det finns ingen hård gräns. Pythons standardgränsen för återvändande (1000 ram) gäller för recursiva traversalfunktioner. För mycket djupa hierarki, konvertera återgången till en uttrycklig stack:
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))Kan jag ändra trädet när jag passerar det?
Lägg inte till eller ta bort knappar från child_nodes Samla knapparna för att ändra i en första pass, sedan tillämpa förändringar i ett andra pass.
Hur hittar jag en specifik node per namn?
Användning node.get_child(name) för att hitta ett direkt barn efter namn. För en djup sökning, gå igenom trädet med ett namnfilter:
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")Gör det node.entity Alltid tillbaka en mässa?
En nod kan inneha någon typ av enhet: Mesh, Camera, Light,eller anpassade enheter. alltid kolla med isinstance(node.entity, Mesh) innan du använder mässpesifika egenskaper.
Hur får jag en världsutrymmeposition av ett nod?
Läsning node.global_transform.translation.Detta är den utvärderade positionen i världsutrymmet, som tar hänsyn till alla förfädernas omvandlingar. det läses bara; modifieras node.transform.translation för att återställa knuten.
Kan jag räkna totalpolyganer i en scen utan att skriva ett traversal?
Inte direkt genom API; det finns inte scene.total_polygon_count äganderätt - Användning collect_meshes och sum mesh.polygon_count i alla resultat, som visas i steg 6.