Graf Adegan
A scene graph adalah model data asas Aspose.3D FOSS untuk Python. Setiap fail 3D, sama ada dimuatkan dari cakera atau dibina dalam memori, diwakili sebagai pokok objek Node yang berakar pada Scene.root_node. Setiap nod boleh memegang nod anak dan satu atau lebih objek Entity (meshes, cameras, lights). Memahami scene graph memberi anda akses langsung kepada geometri, bahan, dan transformasi spatial setiap objek dalam adegan.
Pemasangan dan Persediaan
Pasang perpustakaan dari PyPI:
pip install aspose-3d-fossTiada sambungan asli, pengkompil, atau pakej sistem tambahan diperlukan. Untuk arahan pemasangan penuh, lihat Installation Guide.
Gambaran Keseluruhan: Konsep Graf Adegan
Graf adegan dalam Aspose.3D FOSS mengikuti hierarki penahanan yang jelas:
Scene
└── root_node (Node)
├── child_node_A (Node)
│ ├── entity: Mesh
│ └── transform: translation, rotation, scale
├── child_node_B (Node)
│ └── child_node_C (Node)
│ └── entity: Mesh
└── ...| Object | Role |
|---|---|
Scene | Bekas peringkat atas. Menyimpan root_node, asset_info, animation_clips, dan sub_scenes. |
Node | Nod pokok bernama. Mempunyai ibu bapa, sifar atau lebih anak, sifar atau lebih entiti, dan Transform setempat. |
Entity | Geometri atau objek adegan yang dilampirkan pada nod. Jenis entiti biasa: Mesh, Camera, Light. |
Transform | Kedudukan, putaran, dan skala dalam ruang setempat untuk nod. Keputusan ruang dunia dibaca daripada global_transform. |
Langkah demi Langkah: Membina Graf Adegan secara Programatik
Langkah 1: Buat Adegan
Sebuah Scene baru selalu bermula dengan root_node kosong:
from aspose.threed import Scene
scene = Scene()
print(scene.root_node.name) # "" (empty string — root node has no name by default)
print(len(scene.root_node.child_nodes)) # 0Scene ialah titik masuk untuk segala-galanya: memuatkan fail, menyimpan fail, membuat klip animasi, dan mengakses pokok nod.
Langkah 2: Buat Nod Anak
Gunakan create_child_node(name) untuk menambah nod bernama ke dalam pokok:
from aspose.threed import Scene
scene = Scene()
parent = scene.root_node.create_child_node("parent")
child = parent.create_child_node("child")
print(parent.name) # "parent"
print(child.parent_node.name) # "parent"
print(len(scene.root_node.child_nodes)) # 1Sebaliknya, buat satu Node berdiri sendiri dan lampirkan secara eksplisit:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)Kedua-dua pendekatan menghasilkan keputusan yang sama. create_child_node lebih ringkas untuk pembinaan dalam baris.
Langkah 3: Cipta Entiti Mesh dan Lampirkan Ia
Sebuah Mesh menyimpan data vertex (control_points) dan topologi muka (polygons). Buat satu, tambahkan geometri, kemudian lampirkan kepada nod:
from aspose.threed import Scene, Node
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector3, Vector4
scene = Scene()
parent = scene.root_node.create_child_node("parent")
child = parent.create_child_node("child")
##Create a quad mesh (four vertices, one polygon)
# Note: control_points returns a copy of the internal list; append to
# _control_points directly to actually add vertices. This is a known
# library limitation — a public add_control_point() API is not yet available.
mesh = Mesh("cube")
mesh._control_points.append(Vector4(0, 0, 0, 1))
mesh._control_points.append(Vector4(1, 0, 0, 1))
mesh._control_points.append(Vector4(1, 1, 0, 1))
mesh._control_points.append(Vector4(0, 1, 0, 1))
mesh.create_polygon(0, 1, 2, 3)
child.add_entity(mesh)
print(f"Mesh name: {mesh.name}")
print(f"Vertex count: {len(mesh.control_points)}")
print(f"Polygon count: {mesh.polygon_count}")Vector4(x, y, z, w) mewakili koordinat homogen. Gunakan w=1 untuk kedudukan titik biasa.
create_polygon(*indices) menerima indeks vertex dan mendaftar satu muka dalam senarai poligon. Berikan tiga indeks untuk segitiga, empat untuk kuad.
Langkah 4: Tetapkan Transformasi Nod
Setiap nod mempunyai Transform yang mengawal kedudukan, orientasi, dan saiznya dalam ruang setempat:
from aspose.threed.utilities import Vector3, Quaternion
##Translate the node 2 units along the X axis
child.transform.translation = Vector3(2.0, 0.0, 0.0)
##Scale the node to half its natural size
child.transform.scaling = Vector3(0.5, 0.5, 0.5)
##Rotate 45 degrees around the Y axis using Euler angles
child.transform.euler_angles = Vector3(0.0, 45.0, 0.0)Transformasi bersifat kumulatif: kedudukan ruang-dunia nod anak adalah komposisi transformasinya sendiri dengan semua transformasi nenek moyang. Baca hasil ruang-dunia yang telah dievaluasi dari node.global_transform (tidak boleh diubah, hanya baca).
Langkah 5: Menelusuri Graf Adegan Secara Rekursif
Lalui seluruh pokok dengan mengulang melalui node.child_nodes:
def traverse(node, depth=0):
indent = " " * depth
entity_type = type(node.entity).__name__ if node.entity else "None"
print(f"{indent}{node.name} [{entity_type}]")
for child in node.child_nodes:
traverse(child, depth + 1)
traverse(scene.root_node)Contoh output untuk adegan yang dibina di atas (nama nod akar adalah rentetan kosong):
[None]
parent [None]
child [Mesh]Untuk adegan dengan pelbagai entiti setiap nod, ulangi node.entities dan bukannya node.entity:
def traverse_full(node, depth=0):
indent = " " * depth
entity_names = [type(e).__name__ for e in node.entities] or ["None"]
print(f"{indent}{node.name} [{', '.join(entity_names)}]")
for child in node.child_nodes:
traverse_full(child, depth + 1)
traverse_full(scene.root_node)Langkah 6: Simpan Adegan
Berikan laluan fail kepada scene.save(). Format ditentukan daripada sambungan fail:
scene.save("scene.gltf") # JSON glTF 2.0
scene.save("scene.glb") # Binary GLB container
scene.save("scene.obj") # Wavefront OBJ
scene.save("scene.stl") # STLUntuk pilihan khusus format, hantarkan objek save-options sebagai argumen kedua:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)Petua dan Amalan Terbaik
- Namakan setiap nod. Memberi nod nama yang bermakna menjadikan penyahpepijatan traversals jauh lebih mudah dan memastikan nama disimpan dalam fail yang dieksport.
- Satu mesh per nod. Menjaga entiti 1:1 dengan nod mempermudah transformasi dan pertanyaan perlanggaran.
- Gunakan
create_child_nodeberbanding lampiran manual. Ia menetapkan rujukan ibu bapa secara automatik dan kurang terdedah kepada ralat. - Baca
global_transformselepas membina hierarki. Keputusan ruang-dunia hanya stabil setelah semua transformasi nenek moyang ditetapkan. - Jangan ubah pokok semasa traversing. Menambah atau membuang nod anak semasa iterasi
child_nodesakan menghasilkan kelakuan yang tidak dapat diramalkan. Kumpulkan nod terlebih dahulu, kemudian ubah. - Titik kawalan gunakan
Vector4, bukanVector3. Sentiasa hantarw=1untuk kedudukan vertex biasa;w=0mewakili vektor arah (bukan titik). mesh.control_pointsmengembalikan salinan. Sifatcontrol_pointsmengembalikanlist(self._control_points)— menambah ke senarai yang dikembalikan tidak mengubah mesh. Sentiasa tambahkan kemesh._control_pointssecara langsung semasa membina geometri secara programatik. Ini adalah batasan perpustakaan yang diketahui; API mutasi awam belum wujud.
Isu Umum
| Issue | Resolution |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Lindungi dengan if node.entity is not None sebelum mengakses sifat entiti. Nod tanpa entiti mempunyai entity = None. |
Mesh appears at the origin despite setting translation | transform.translation menggunakan offset tempatan. Jika nod induk itu sendiri mempunyai transformasi bukan identiti, kedudukan dunia mungkin berbeza. Periksa global_transform. |
Child nodes missing after scene.save() / reload | Beberapa format (OBJ) meratakan hierarki. Gunakan glTF atau COLLADA untuk mengekalkan pokok nod penuh. |
polygon_count is 0 after mesh.create_polygon(...) | Sahkan bahawa indeks vertex yang dihantar ke create_polygon berada dalam julat (0 hingga len(control_points) - 1). |
Node.get_child(name) returns None | Nama adalah sensitif huruf. Sahkan rentetan nama tepat yang digunakan semasa penciptaan. |
| Traversal visits nodes in unexpected order | child_nodes mengembalikan anak dalam susunan sisipan (susunan add_child_node / create_child_node dipanggil). |