Cara Menelusuri Graf Adegan 3D dalam Python
Graf adegan dalam Aspose.3D FOSS adalah pokok bagi Node objek yang berakar pada scene.root_node. Setiap fail 3D, sama ada dimuatkan daripada OBJ, glTF, STL, COLLADA, atau 3MF, menghasilkan struktur pokok yang sama. Mengetahui cara menelusurnya membolehkan anda memeriksa geometri, mengira poligon, menapis objek mengikut jenis, dan memproses bahagian tertentu dalam adegan yang kompleks.
Panduan Langkah demi Langkah
Langkah 1: Pasang dan Import
Pasang Aspose.3D FOSS dari PyPI:
pip install aspose-3d-fossImport kelas yang anda perlukan:
from aspose.threed import Scene
from aspose.threed.entities import MeshSemua kelas awam berada di bawah aspose.threed atau sub-pakejnya (aspose.threed.entities, aspose.threed.utilities).
Langkah 2: Muatkan Adegan dari Fail
Gunakan statik Scene.from_file() kaedah untuk membuka mana-mana format yang disokong. Format dikesan secara automatik daripada sambungan fail:
scene = Scene.from_file("model.gltf")Anda juga boleh membuka dengan pilihan muat yang eksplisit:
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.enable_materials = True
scene = Scene()
scene.open("model.obj", options)Selepas memuatkan, scene.root_node adalah akar pokok. Semua nod yang diimport adalah anak atau keturunan nod ini.
Langkah 3: Tulis Fungsi Traversal Rekursif
Traversal paling mudah melawat setiap nod dalam susunan depth-first:
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)Contoh output:
[-] RootNode
[-] 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>
node.child_nodes mengembalikan anak dalam susunan sisipan (susunan di mana pengimport atau pengguna menambahnya).
Langkah 4: Akses Sifat Entiti pada Setiap Nod
Gunakan node.entity untuk mendapatkan entiti pertama yang dilampirkan pada nod, atau node.entities untuk mengulangi semua. Periksa jenisnya sebelum mengakses sifat khusus 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, dan node.transform.scaling memberi transformasi ruang setempat. node.global_transform memberi hasil ruang dunia yang dinilai (dengan global_transform.scale untuk skala ruang-dunia).
Langkah 5: Tapis Nod mengikut Jenis Entiti
Untuk beroperasi hanya pada jenis entiti tertentu, tambahkan satu isinstance penjaga di dalam penelusuran:
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}")Corak ini berguna untuk operasi pukal: menerapkan transformasi pada setiap nod mesh, menggantikan bahan, atau mengeksport sub-pokok.
Langkah 6: Kumpulkan Semua Mesh dan Cetak Bilangan Vertex
Kumpulkan statistik mesh dalam satu laluan:
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")Isu Umum
| Isu | Resolusi |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Lindungi dengan if node.entity is not None atau isinstance(node.entity, Mesh) sebelum mengakses sifat mesh. Nod tanpa entiti mengembalikan None. |
| Penelusuran berhenti lebih awal | Pastikan rekursi mencapai ke dalam node.child_nodes. Jika anda hanya mengulangi scene.root_node.child_nodes (tidak secara rekursif), anda terlepas semua keturunan. |
| Mesh tiada dalam hasil yang dikumpulkan | Pastikan format fail mengekalkan hierarki. OBJ meratakan semua geometri ke dalam satu tahap nod. Gunakan glTF atau COLLADA untuk hierarki yang mendalam. |
node.entity mengembalikan hanya entiti pertama | Gunakan node.entities (senarai penuh) apabila satu nod membawa pelbagai entiti. node.entity adalah singkatan untuk node.entities[0] bila entities tidak kosong. |
collect_meshes mengembalikan 0 keputusan untuk STL yang dimuatkan | Fail STL biasanya menghasilkan satu nod rata dengan satu entiti mesh secara langsung di bawah root_node. Periksa root_node.child_nodes[0].entity secara langsung. |
| Nama nod adalah rentetan kosong | Beberapa format (STL binari, beberapa fail OBJ) tidak menyimpan nama objek. Nod akan mempunyai rentetan kosong name rentetan; gunakan indeks untuk mengenal pasti sebaliknya. |
Soalan Lazim
Sejauh mana kedalaman graf adegan?
Tiada had keras. Had rekursi lalai Python (1000 bingkai) dikenakan pada fungsi penelusuran rekursif. Untuk hierarki yang sangat dalam, tukar rekursi kepada timbunan eksplisit:
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))Bolehkah saya mengubah pokok semasa menelusurnya?
Jangan menambah atau membuang nod daripada child_nodes semasa mengiterasinya. Kumpulkan nod yang hendak diubah dalam satu laluan pertama, kemudian terapkan perubahan dalam laluan kedua.
Bagaimana saya mencari nod tertentu mengikut nama?
Gunakan node.get_child(name) untuk mencari anak langsung mengikut nama. Untuk carian mendalam, lalui pokok dengan penapis nama:
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")Adakah node.entity sentiasa mengembalikan Mesh?
Tidak. Sebuah nod boleh memegang sebarang jenis entiti: Mesh, Camera, Light, atau entiti tersuai. Sentiasa semak dengan isinstance(node.entity, Mesh) sebelum menggunakan sifat khusus mesh.
Bagaimana saya mendapatkan kedudukan ruang dunia bagi sebuah nod?
Baca node.global_transform.translation. Ini adalah kedudukan yang dinilai dalam ruang dunia, mengambil kira semua transformasi nenek moyang. Ia hanya-baca; ubah node.transform.translation untuk menempat semula nod itu.
Bolehkah saya mengira jumlah poligon dalam satu adegan tanpa menulis penelusuran?
Tidak secara langsung melalui API; tiada scene.total_polygon_count harta. Gunakan collect_meshes dan jumlahkan mesh.polygon_count di seluruh keputusan, seperti yang ditunjukkan dalam Langkah 6.