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-fossUvezite klase koje su vam potrebne:
from aspose.threed import Scene
from aspose.threed.entities import MeshSve 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
| Problem | Rješ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 prerano | Osigurajte 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 rezultatima | Provjerite 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 entitet | Koristite 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 STL | STL 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 stringovi | Neki 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.