Graf Adegan
A grafik adegan adalah model data dasar dari Aspose.3D FOSS untuk Python. Setiap file 3D, baik yang dimuat dari disk maupun yang dibangun di memori, direpresentasikan sebagai pohon dari Node objek yang berakar pada Scene.root_node. Setiap node dapat menampung node anak dan satu atau lebih Entity objek (meshes, cameras, lights). Memahami scene graph memberi Anda akses langsung ke geometri, material, dan transformasi spasial setiap objek dalam sebuah adegan.
Instalasi dan Penyiapan
Instal perpustakaan dari PyPI:
pip install aspose-3d-fossTidak diperlukan ekstensi native, kompiler, atau paket sistem tambahan. Untuk petunjuk instalasi lengkap, lihat Panduan Instalasi.
Gambaran Umum: Konsep Graf Adegan
Graf adegan dalam Aspose.3D FOSS mengikuti hierarki kontainmen yang sederhana:
Scene
└── root_node (Node)
├── child_node_A (Node)
│ ├── entity: Mesh
│ └── transform: translation, rotation, scale
├── child_node_B (Node)
│ └── child_node_C (Node)
│ └── entity: Mesh
└── ...| Objek | Peran |
|---|---|
Scene | Kontainer tingkat atas. Menampung root_node, asset_info, animation_clips, dan sub_scenes. |
Node | Node pohon bernama. Memiliki induk, nol atau lebih anak, nol atau lebih entitas, dan sebuah Transform. |
Entity | Geometri atau objek adegan yang terlampir pada sebuah node. Jenis entitas umum: Mesh, Camera, Light. |
Transform | Posisi, rotasi, dan skala ruang lokal untuk sebuah node. Hasil ruang dunia dibaca dari global_transform. |
Langkah demi Langkah: Membangun Graf Adegan secara Programatik
Langkah 1: Buat sebuah Scene
Sebuah baru Scene selalu dimulai dengan kosong root_node:
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 adalah titik masuk untuk segala hal: memuat file, menyimpan file, membuat klip animasi, dan mengakses pohon node.
Langkah 2: Buat Node Anak
Gunakan create_child_node(name) untuk menambahkan node bernama ke pohon:
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)) # 1Sebagai alternatif, buat sebuah standalone Node dan lampirkan secara eksplisit:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)Kedua pendekatan menghasilkan hasil yang sama. create_child_node lebih ringkas untuk konstruksi inline.
Langkah 3: Buat Entitas Mesh dan Lampirkan
Sebuah Mesh menyimpan data vertex (control_points) dan topologi wajah (polygons). Buat satu, tambahkan geometri, lalu lampirkan ke node:
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 posisi titik biasa.
create_polygon(*indices) menerima indeks vertex dan mendaftarkan satu wajah dalam daftar poligon. Berikan tiga indeks untuk segitiga, empat untuk kuad.
Langkah 4: Atur Transformasi Node
Setiap node memiliki sebuah Transform yang mengontrol posisi, orientasi, dan ukuran dalam ruang lokal:
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: posisi ruang-dunia node anak adalah komposisi transformasinya sendiri dengan semua transformasi leluhur. Baca hasil ruang-dunia yang telah dievaluasi dari node.global_transform (tidak dapat diubah, hanya-baca).
Langkah 5: Jelajahi Graf Adegan Secara Rekursif
Jelajahi seluruh pohon dengan merekursi 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 dibangun di atas (nama node akar adalah string kosong):
[None]
parent [None]
child [Mesh]Untuk adegan dengan beberapa entitas per node, lakukan iterasi node.entities daripada 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 jalur file ke scene.save(). Formatnya ditentukan dari ekstensi file:
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 opsi khusus format, berikan objek save-options sebagai argumen kedua:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)Tips dan Praktik Terbaik
- Beri nama setiap node. Memberi node nama yang bermakna membuat debugging traversal jauh lebih mudah dan memastikan nama tetap dipertahankan dalam file yang diekspor.
- Satu mesh per node. Menjaga entitas 1:1 dengan node menyederhanakan transformasi dan kueri tabrakan.
- Gunakan
create_child_nodedaripada penempelan manual. Ini mengatur referensi induk secara otomatis dan lebih sedikit rawan kesalahan. - Baca
global_transformsetelah membangun hierarki. Hasil ruang-dunia hanya stabil setelah semua transformasi nenek moyang diatur. - Jangan mengubah pohon selama traversal. Menambahkan atau menghapus node anak saat iterasi
child_nodesakan menghasilkan perilaku yang tidak dapat diprediksi. Kumpulkan node terlebih dahulu, kemudian modifikasi. - Titik kontrol menggunakan
Vector4, bukanVector3. Selalu berikanw=1untuk posisi vertex biasa;w=0mewakili vektor arah (bukan titik). mesh.control_pointsmengembalikan salinan. Thecontrol_pointsproperti mengembalikanlist(self._control_points)— menambahkan ke daftar yang dikembalikan tidak mengubah mesh. Selalu tambahkan kemesh._control_pointslangsung saat membangun geometri secara programatik. Ini adalah keterbatasan perpustakaan yang diketahui; API mutasi publik belum tersedia.
Masalah Umum
| Masalah | Solusi |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Lindungi dengan if node.entity is not None sebelum mengakses properti entitas. Sebuah node tanpa entitas memiliki entity = None. |
Mesh muncul di asal meskipun telah mengatur translation | transform.translation menerapkan offset lokal. Jika node induk sendiri memiliki transformasi bukan identitas, posisi dunia mungkin berbeda. Periksa global_transform. |
Node anak hilang setelah scene.save() / muat ulang | Beberapa format (OBJ) meratakan hierarki. Gunakan glTF atau COLLADA untuk mempertahankan seluruh pohon node. |
polygon_count adalah 0 setelah mesh.create_polygon(...) | Verifikasi bahwa indeks vertex yang diberikan ke create_polygon berada dalam rentang (0 ke len(control_points) - 1). |
Node.get_child(name) mengembalikan None | Nama bersifat case-sensitive. Konfirmasikan string nama yang tepat yang digunakan pada saat pembuatan. |
| Traversal mengunjungi node dalam urutan yang tidak terduga | child_nodes mengembalikan anak dalam urutan penyisipan (urutan add_child_node / create_child_node dipanggil). |