Bekerja dengan Scene Graph

Semua konten 3D dalam Aspose.3D FOSS untuk TypeScript berada di dalam sebuah Scene objek yang diatur sebagai pohon dari Node objek. Memahami hierarki ini adalah dasar untuk membangun, memuat, dan memproses setiap file 3D.

Instalasi

Instal paket dari npm sebelum menjalankan kode apa pun dalam panduan ini:

npm install @aspose/3d

Pastikan tsconfig.json menyertakan "module": "commonjs" dan "moduleResolution": "node" untuk resolusi impor sub‑path yang benar.

Konsep Scene Graph

Scene graph memiliki tiga tingkatan:

TingkatKelasPeran
AdeganSceneKontainer tingkat atas. Menyimpan rootNode, animationClips, dan assetInfo.
NodeNodeNode pohon bernama. Dapat memiliki node anak, sebuah entitas, transformasi, dan material.
EntitasMesh, Camera, Light, …Konten yang terlampir pada sebuah node. Sebuah node membawa paling banyak satu entitas.

Rantai pewarisan untuk blok bangunan utama adalah:

A3DObject
  └─ SceneObject
       ├─ Node          (tree structure)
       └─ Entity
            └─ Geometry
                 └─ Mesh   (polygon geometry)

scene.rootNode dibuat secara otomatis. Anda tidak membuatnya secara manual; Anda membuat node anak di bawahnya.

Langkah 1: Buat sebuah Scene

import { Scene } from '@aspose/3d';

const scene = new Scene();
console.log(scene.rootNode.name); // '' (empty string — the root node is created with no name)

Baru Scene dimulai dengan node root kosong dan tidak ada klip animasi. Anda membangun konten dengan melampirkan node anak.

Langkah 2: Tambahkan Node Anak

Gunakan createChildNode() untuk menumbuhkan pohon. Metode mengembalikan yang baru Node, sehingga Anda dapat menautkan panggilan lebih lanjut dari level mana pun:

import { Scene } from '@aspose/3d';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

console.log(scene.rootNode.childNodes.length); // 1 (parent)
console.log(parent.childNodes.length);         // 1 (child)

Nama node adalah string sewenang-wenang. Nama tidak harus unik, tetapi menggunakan nama yang bermakna membuat kode traversal lebih mudah di‑debug.

Langkah 3: Buat Mesh dan Atur Vertex

Mesh adalah kelas geometri utama. Tambahkan posisi vertex dengan mendorong Vector4 nilai ke dalam mesh.controlPoints, lalu panggil createPolygon() untuk mendefinisikan wajah dengan indeks vertex:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4 } from '@aspose/3d/utilities';

const scene = new Scene();
const child = scene.rootNode.createChildNode('parent').createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3); // quad face using all four vertices

child.entity = mesh;

console.log(mesh.controlPoints.length); // 4
console.log(mesh.polygonCount);         // 1

Vector4 menggunakan koordinat homogen: w komponen adalah 1 untuk posisi dan 0 untuk vektor arah.

Langkah 4: Atur Transformasi Node

Setiap node memiliki sebuah transform properti dengan translation, rotation, dan scaling. translation untuk memindahkan node relatif terhadap induknya:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4, Vector3 } from '@aspose/3d/utilities';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3);
child.entity = mesh;

child.transform.translation = new Vector3(2.0, 0.0, 0.0);

globalTransform menyediakan world-space transformation matrix (read-only), dihitung dengan menggabungkan semua transformasi nenek moyang.

Langkah 5: Jelajahi Pohon

Tulislah fungsi rekursif untuk mengunjungi setiap node. Periksa node.entity dan node.childNodes pada setiap level:

function traverse(node: any, depth = 0): void {
    const indent = '  '.repeat(depth);
    const entityType = node.entity ? node.entity.constructor.name : 'none';
    console.log(`${indent}${node.name} [${entityType}]`);
    for (const child of node.childNodes) {
        traverse(child, depth + 1);
    }
}

traverse(scene.rootNode);

Untuk hierarki yang dibuat di atas, outputnya akan menjadi:

 [none]
  parent [none]
    child [Mesh]

Nama node akar adalah string kosong karena Scene membuatnya tanpa argumen nama.

Selalu lindungi akses entitas dengan pemeriksaan null sebelum melakukan casting ke tipe tertentu. Tidak setiap node membawa entitas.

Langkah 6: Simpan ke glTF atau GLB

Gunakan GltfSaveOptions untuk mengontrol format output. Atur binaryMode = true untuk menghasilkan satu berkas mandiri .glb berkas; biarkan false untuk JSON .gltf + .bin pasangan sidecar:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4, Vector3 } from '@aspose/3d/utilities';
import { GltfSaveOptions, GltfFormat } from '@aspose/3d/formats/gltf';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3);
child.entity = mesh;

child.transform.translation = new Vector3(2.0, 0.0, 0.0);

const saveOpts = new GltfSaveOptions();
saveOpts.binaryMode = true;                             // write a single .glb file
scene.save('scene.glb', GltfFormat.getInstance(), saveOpts);

console.log('Scene saved to scene.glb');

Berikan GltfFormat.getInstance() sebagai argumen format sehingga perpustakaan menggunakan encoder yang tepat terlepas dari ekstensi berkas.

Tips dan Praktik Terbaik

  • Gunakan createChildNode() alih-alih membangun Node secara langsung: createChildNode() secara otomatis menghubungkan hubungan induk-anak dan mendaftarkan node dalam pohon.
  • Periksa node.entity sebelum mengakses properti entitas: banyak node (node grup, tulang, lokator) tidak memiliki entitas. Selalu lindungi dengan pemeriksaan null atau instanceof pengujian.
  • Setel translation pada node anak, bukan pada vertex mesh: memodifikasi transform.translation tidak merusak dan dapat digabungkan dengan transformasi induk.
  • Lebih disukai binaryMode = true untuk GLB: satu .glb file lebih mudah didistribusikan, dimuat di peramban, dan diimpor ke mesin game dibandingkan yang terpisah .gltf + .bin format.
  • Jelajahi melalui for...of di atas childNodes: hindari pengindeksan numerik; gunakan iterable secara langsung untuk kompatibilitas ke depan.

Masalah Umum

GejalaPenyebab KemungkinanPerbaikan
child.entity = mesh tidak berpengaruh pada eksporEntitas ditetapkan pada level node yang salahTetapkan entity ke node daun, bukan ke node grup
node.entity selalu nullHanya memeriksa rootNode sendiriMerekur ke dalam node.childNodes; rootNode biasanya tidak memiliki entitas
Transformasi tidak tercermin di penampil GLBglobalTransform tidak diperbaruiglobalTransform dihitung saat disimpan; set transform.translation sebelum memanggil scene.save()
GLB menghasilkan terpisah .bin sidecarbinaryMode default ke falseAtur saveOpts.binaryMode = true

Lihat Juga

 Bahasa Indonesia