Jak projít 3D scénovým grafem v Pythonu

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 Mesh

Vš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.

 Čeština