Bekerja dengan Scene Graph

Semua kandungan 3D dalam Aspose.3D FOSS untuk TypeScript berada di dalam objek Scene yang disusun sebagai pokok objek Node. Memahami hierarki ini adalah asas untuk membina, memuatkan, dan memproses mana-mana fail 3D.

Pemasangan

Pasang pakej dari npm sebelum menjalankan sebarang kod dalam panduan ini:

npm install @aspose/3d

Pastikan tsconfig.json anda termasuk "module": "commonjs" dan "moduleResolution": "node" untuk penyelesaian import sub‑laluan yang betul.

Konsep Graf Adegan

Graf adegan mempunyai tiga peringkat:

TierClassRole
SceneSceneKontena peringkat atas. Menyimpan rootNode, animationClips, dan assetInfo.
NodeNodeNod pokok bernama. Boleh mempunyai nod anak, entiti, transform, dan bahan.
EntityMesh, Camera, Light, …Kandungan yang dilampirkan pada nod. Sebuah nod membawa paling banyak satu entiti.

Rantaian pewarisan untuk blok binaan utama ialah:

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

scene.rootNode dibuat secara automatik. Anda tidak membuatnya secara manual; anda membuat nod anak di bawahnya.

Langkah 1: Cipta Adegan

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

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

Sebuah Scene baru bermula dengan node akar kosong dan tiada klip animasi. Anda membina kandungan dengan melampirkan node anak.

Langkah 2: Tambah Nod Anak

Gunakan createChildNode() untuk menumbuhkan pokok. Kaedah ini mengembalikan Node yang baru, jadi anda boleh menyambung panggilan selanjutnya dari mana-mana peringkat:

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 nod adalah rentetan sewenang-wenangnya. Nama tidak perlu unik, tetapi menggunakan nama yang bermakna menjadikan kod penelusuran lebih mudah untuk dibaiki.

Langkah 3: Cipta Mesh dan Tetapkan Vertices

Mesh adalah kelas geometri utama. Tambahkan posisi vertex dengan menolak nilai Vector4 ke dalam mesh.controlPoints, kemudian panggil createPolygon() untuk mendefinisikan muka mengikut 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: komponen w ialah 1 untuk kedudukan dan 0 untuk vektor arah.

Langkah 4: Tetapkan Transformasi Nod

Setiap nod mempunyai sifat transform dengan translation, rotation, dan scaling. Tetapkan translation untuk menggerakkan nod relatif kepada ibu bapa:

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 matriks transformasi ruang-dunia (baca-sahaja), yang dikira dengan menggabungkan semua transformasi nenek moyang.

Langkah 5: Menelusuri Pokok

Tulis fungsi rekursif untuk melawat setiap nod. Semak node.entity dan node.childNodes pada setiap peringkat:

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 nod akar adalah rentetan kosong kerana Scene menciptanya tanpa argumen nama.

Sentiasa lindungi akses entiti dengan pemeriksaan null sebelum menukarkan kepada jenis tertentu. Tidak setiap nod membawa entiti.

Langkah 6: Simpan ke glTF atau GLB

Gunakan GltfSaveOptions untuk mengawal format output. Tetapkan binaryMode = true untuk menghasilkan satu fail .glb yang berdiri sendiri; biarkan false untuk pasangan sidecar JSON .gltf + .bin:

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 parameter format supaya perpustakaan menggunakan pengekod yang betul tanpa mengira sambungan fail.

Petua dan Amalan Terbaik

  • Gunakan createChildNode() dan bukannya membina Node secara langsung: createChildNode() secara automatik menyambungkan hubungan ibu‑bapa dan mendaftar nod dalam pokok.
  • Periksa node.entity sebelum mengakses sifat entiti: banyak nod (nod kumpulan, tulang, penunjuk) tidak membawa entiti. Sentiasa lindungi dengan pemeriksaan null atau ujian instanceof.
  • Tetapkan translation pada nod anak, bukan pada verteks mesh: mengubah transform.translation tidak merosakkan dan boleh digabungkan dengan transformasi ibu bapa.
  • Utamakan binaryMode = true untuk GLB: satu fail .glb lebih mudah diedarkan, dimuatkan dalam pelayar, dan diimport ke dalam enjin permainan berbanding format terbahagi .gltf + .bin.
  • Lalui melalui for...of berbanding childNodes: elakkan penomboran indeks; gunakan iterable secara langsung untuk keserasian ke hadapan.

Isu Umum

SymptomLikely CauseFix
child.entity = mesh tidak memberi kesan pada eksportEntiti ditetapkan pada tahap nod yang salahTetapkan entity kepada nod daun, bukan kepada nod kumpulan
node.entity sentiasa nullHanya memeriksa rootNode itu sendiriLakukan rekursi ke dalam node.childNodes; rootNode biasanya tidak mempunyai entiti
Transformasi tidak dipaparkan dalam penonton GLBglobalTransform tidak dikemas kiniglobalTransform dikira semasa menyimpan; tetapkan transform.translation sebelum memanggil scene.save()
GLB menghasilkan fail .bin sidecar berasinganbinaryMode secara lalai kepada falseTetapkan saveOpts.binaryMode = true

Lihat Juga

 Bahasa Melayu