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-foss

Tiada 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
    └── ...
ObjectRole
SceneBekas peringkat atas. Menyimpan root_node, asset_info, animation_clips, dan sub_scenes.
NodeNod pokok bernama. Mempunyai ibu bapa, sifar atau lebih anak, sifar atau lebih entiti, dan Transform setempat.
EntityGeometri atau objek adegan yang dilampirkan pada nod. Jenis entiti biasa: Mesh, Camera, Light.
TransformKedudukan, 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))  # 0

Scene 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))     # 1

Sebaliknya, 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")    # STL

Untuk 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_node berbanding lampiran manual. Ia menetapkan rujukan ibu bapa secara automatik dan kurang terdedah kepada ralat.
  • Baca global_transform selepas 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_nodes akan menghasilkan kelakuan yang tidak dapat diramalkan. Kumpulkan nod terlebih dahulu, kemudian ubah.
  • Titik kawalan gunakan Vector4, bukan Vector3. Sentiasa hantar w=1 untuk kedudukan vertex biasa; w=0 mewakili vektor arah (bukan titik).
  • mesh.control_points mengembalikan salinan. Sifat control_points mengembalikan list(self._control_points) — menambah ke senarai yang dikembalikan tidak mengubah mesh. Sentiasa tambahkan ke mesh._control_points secara langsung semasa membina geometri secara programatik. Ini adalah batasan perpustakaan yang diketahui; API mutasi awam belum wujud.

Isu Umum

IssueResolution
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 translationtransform.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() / reloadBeberapa 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 NoneNama adalah sensitif huruf. Sahkan rentetan nama tepat yang digunakan semasa penciptaan.
Traversal visits nodes in unexpected orderchild_nodes mengembalikan anak dalam susunan sisipan (susunan add_child_node / create_child_node dipanggil).
 Bahasa Melayu