Kako proći kroz 3D graf scene u Python

Kako proći kroz 3D graf scene u Python

Graf scena u Aspose.3D FOSS je stablo od Node objekata ukorijenjenih u scene.root_node. Svaki 3D datoteka, bilo da je učitana iz OBJ, glTF, STL, COLLADA ili 3MF, stvara istu strukturu stabla. Poznavanje kako je proći omogućuje vam pregled geometrije, brojanje poligona, filtriranje objekata po tipu i obradu određenih dijelova složene scene.

Vodič korak po korak

Korak 1: Instaliraj i uvezi

Instalirajte Aspose.3D FOSS s PyPI-a:

pip install aspose-3d-foss

Uvezite klase koje su vam potrebne:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

Sve javne klase nalaze se pod aspose.threed ili njegovim podpaketima (aspose.threed.entities, aspose.threed.utilities).


Korak 2: Učitaj scenu iz datoteke

Koristite statičku Scene.from_file() metodu za otvaranje bilo kojeg podržanog formata. Format se automatski otkriva iz ekstenzije datoteke:

scene = Scene.from_file("model.gltf")

Također možete otvoriti s eksplicitnim opcijama učitavanja:

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

options = ObjLoadOptions()
options.enable_materials = True

scene = Scene()
scene.open("model.obj", options)

Nakon učitavanja, scene.root_node je korijen stabla. Svi uvezeni čvorovi su djeca ili potomci ovog čvora.


Korak 3: Napišite rekurzivnu funkciju za traversiranje

Najjednostavnije traversiranje posjećuje svaki čvor u dubinskom pretraživanju:

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)

Primjer izlaza:

[-]
  [-] 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>

Napomena: Korijenski čvor ima prazno ime (""), pa prva linija prikazuje [-] bez imena nakon toga.

node.child_nodes vraća djecu u redoslijedu umetanja (redoslijed u kojem ih je uvoznik ili korisnik dodao).


Korak 4: Pristupite svojstvima entiteta na svakom čvoru

Koristite node.entity za dobivanje prve entitete pričvršćene uz čvor, ili node.entities za iteriranje svih njih. Provjerite tip prije pristupa svojstvima specifičnim za format:

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 daju transformaciju u lokalnom prostoru. node.global_transform daje izračunati rezultat u svjetskom prostoru (s global_transform.scale za skaliranje u svjetskom prostoru).


Korak 5: Filtriraj čvorove po tipu entiteta

Za rad samo na određenim vrstama entiteta, dodajte isinstance zaštitu unutar prolaza:

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}")

Ovaj uzorak je koristan za masovne operacije: primjenu transformacije na svaki čvor mreže, zamjenu materijala ili izvoz podstabala.


Korak 6: Prikupi sve mreže i ispiši broj vrhova

Aggregirajte statistiku mreža u jednom prolazu:

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")

Uobičajeni problemi

ProblemRješenje
AttributeError: 'NoneType' object has no attribute 'polygons'Zaštita s if node.entity is not None ili isinstance(node.entity, Mesh) prije pristupa svojstvima mreže. Čvorovi bez entiteta vraćaju None.
Prolaz se zaustavlja preranoOsigurajte da rekurzija dopre do node.child_nodes. Ako iterirate samo scene.root_node.child_nodes (ne rekurzivno), propuštate sve potomke.
Mreža nedostaje u prikupljenim rezultatimaProvjerite da li format datoteke očuva hijerarhiju. OBJ izravnava svu geometriju u jednu razinu čvora. Koristite glTF ili COLLADA za duboke hijerarhije.
node.entity vraća samo prvi entitetKoristite node.entities (cijeli popis) kada čvor nosi više entiteta. node.entity je skraćenica za node.entities[0] kada entities nije prazan.
collect_meshes vraća 0 rezultata za učitani STLSTL datoteke obično stvaraju jedan ravni čvor s jednim mesh entitetom izravno ispod root_node. Provjeri root_node.child_nodes[0].entity izravno.
Imena čvorova su prazni stringoviNeki formati (binary STL, neki OBJ fileovi) ne pohranjuju nazive objekata. Čvorovi će imati prazne name stringove; umjesto toga koristite indeks za identifikaciju.

Često postavljana pitanja

Koliko dubok može biti graf scene?

Ne postoji čvrsto ograničenje. Zadani limit rekurzije Python (1000 okvira) primjenjuje se na rekurzivne funkcije prolaza. Za vrlo duboke hijerarhije, pretvorite rekurziju u eksplicitni 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))

Mogu li modificirati stablo dok ga prolazim?

Nemojte dodavati ili uklanjati čvorove iz child_nodes dok ga iterirate. Prikupite čvorove za izmjenu u prvom prolazu, zatim primijenite promjene u drugom prolazu.

Kako pronaći određeni čvor po imenu?

Koristite node.get_child(name) za pronalaženje izravnog djeteta po imenu. Za dubinsku pretragu, prođite kroz stablo s filterom imena:

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")

Vraća li node.entity uvijek vraća Mesh?

Ne. Čvor može sadržavati bilo koju vrstu entiteta: Mesh, Camera, Light, ili prilagođene entitete. Uvijek provjerite s isinstance(node.entity, Mesh) prije korištenja mesh-specific svojstava.

Kako dobiti položaj čvora u svjetskom prostoru?

Čitanje node.global_transform.translation. Ovo je izračunata pozicija u svjetskom prostoru, uzimajući u obzir sve transformacije pretka. To je samo za čitanje; izmijeni node.transform.translation za premještanje čvora.

Mogu li izbrojati ukupni broj poligona u sceni bez pisanja traversala?

Ne izravno putem API-ja; ne postoji scene.total_polygon_count svojstvo. Upotrijebite collect_meshes i zbroj mesh.polygon_count preko rezultata, kako je prikazano u koraku 6.

 Hrvatski