Hur man passerar en 3D Scene Graph i Python

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 Mesh

Alla 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

Problemetresolution
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 tidigtSe 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 resultatKontrollera 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 STLSTL-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ängarVissa 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.

 Svenska