Cara Menelusuri Graf Adegan 3D dalam TypeScript

Cara Menelusuri Graf Adegan 3D dalam TypeScript

Graf adegan dalam Aspose.3D FOSS untuk TypeScript adalah pokok objek Node yang berakar pada scene.rootNode. Penelusuran bersifat rekursif: setiap nod memperlihatkan iterable childNodes dan sifat pilihan entity. Panduan ini menunjukkan cara melintasi seluruh pokok, mengenal pasti jenis entiti, dan mengumpul statistik mesh.

Prasyarat

  • Node.js 18 atau versi lebih baru
  • TypeScript 5.0 atau versi lebih baru
  • @aspose/3d dipasang

Panduan Langkah demi Langkah

Langkah 1: Pasang dan Import

Pasang pakej:

npm install @aspose/3d

Import kelas yang digunakan dalam panduan ini:

import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';

Scene dan Mesh adalah kelas teras. ObjLoadOptions digunakan dalam contoh pemuatan; gantikan kelas pilihan yang sepadan untuk format lain.


Langkah 2: Muatkan Adegan dari Fail

Buat Scene dan panggil scene.open() dengan laluan fail. Pengesanan format adalah automatik daripada nombor sihir binari, jadi anda tidak perlu menentukan format untuk fail GLB, STL, atau 3MF:

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

const scene = new Scene();
scene.open('model.obj', new ObjLoadOptions());

console.log(`Root node: "${scene.rootNode.name}"`);
console.log(`Top-level children: ${scene.rootNode.childNodes.length}`);

Anda juga boleh memuatkan dari Buffer dalam memori menggunakan scene.openFromBuffer(buffer, options); berguna dalam pipeline tanpa pelayan di mana I/O cakera tidak tersedia.


Langkah 3: Tulis Fungsi Traversal Rekursif

Pengulangan atas childNodes adalah corak standard. Fungsi melawat setiap nod secara kedalaman pertama:

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

traverse(scene.rootNode);

Untuk adegan dengan satu mesh bernama Cube, output akan kelihatan seperti:

[-] RootNode
  [Mesh] Cube

<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Copy code”

<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>

node.entity ialah null untuk nod kumpulan, tulang, dan penentu lokasi. Pemeriksaan constructor.name berfungsi untuk mana-mana jenis entiti: Mesh, Camera, Light, dll.


Langkah 4: Akses Jenis Entiti pada Setiap Nod

Untuk mengambil tindakan berdasarkan jenis entiti, gunakan pemeriksaan instanceof selepas penjaga null:

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

function visitWithTypeCheck(node: any, depth = 0): void {
    const indent = '  '.repeat(depth);
    if (node.entity instanceof Mesh) {
        const mesh = node.entity as Mesh;
        console.log(`${indent}MESH "${node.name}": ${mesh.controlPoints.length} vertices`);
    } else if (node.entity) {
        console.log(`${indent}${node.entity.constructor.name} "${node.name}"`);
    } else {
        console.log(`${indent}GROUP "${node.name}"`);
    }
    for (const child of node.childNodes) {
        visitWithTypeCheck(child, depth + 1);
    }
}

visitWithTypeCheck(scene.rootNode);

instanceof Mesh adalah cara yang paling selamat untuk mengesahkan entiti adalah mesh poligon sebelum mengakses controlPoints, polygonCount, atau elemen vertex.


Langkah 5: Tapis Nod mengikut Jenis Entiti

Untuk mengumpulkan hanya nod yang mempunyai mesh tanpa mencetak keseluruhan pokok, gunakan akumulator rekursif:

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

function collectMeshes(
    node: any,
    results: Array<{ name: string; mesh: Mesh }> = []
): Array<{ name: string; mesh: Mesh }> {
    if (node.entity instanceof Mesh) {
        results.push({ name: node.name, mesh: node.entity as Mesh });
    }
    for (const child of node.childNodes) {
        collectMeshes(child, results);
    }
    return results;
}

const meshNodes = collectMeshes(scene.rootNode);
console.log(`Found ${meshNodes.length} mesh node(s)`);

Fungsi ini menerima tatasusunan results opsional supaya pemanggil dapat pra‑mengisi ia untuk menggabungkan hasil merentasi pelbagai subpokok.


Langkah 6: Kumpulkan Semua Mesh dan Cetak Bilangan Vertex

Kembangkan pengumpul untuk mencetak statistik per‑mesh:

import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';

function collectMeshes(node: any, results: Array<{name: string, mesh: Mesh}> = []) {
    if (node.entity instanceof Mesh) {
        results.push({ name: node.name, mesh: node.entity as Mesh });
    }
    for (const child of node.childNodes) {
        collectMeshes(child, results);
    }
    return results;
}

const scene = new Scene();
scene.open('model.obj', new ObjLoadOptions());

const meshes = collectMeshes(scene.rootNode);
for (const { name, mesh } of meshes) {
    console.log(`${name}: ${mesh.controlPoints.length} vertices, ${mesh.polygonCount} polygons`);
}

Contoh output untuk adegan dua-mesh:

Cube: 8 vertices, 6 polygons
Sphere: 482 vertices, 480 polygons

<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Copy code”

<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>

Petua dan Amalan Terbaik

  • Sentiasa periksa null node.entity sebelum mengakses sifat khusus entiti. Banyak nod adalah nod kumpulan tulen yang tidak membawa entiti.
  • Gunakan instanceof berbanding constructor.name untuk pemeriksaan jenis dalam laluan logik. instanceof selamat untuk refaktor; perbandingan rentetan pada constructor.name akan rosak dengan minifikasi.
  • Lalui melalui for...of berbanding childNodes: iterable mengendalikan semua saiz tatasusunan dengan selamat. Elakkan penomboran indeks secara numerik untuk keserasian ke hadapan.
  • Elakkan mengubah pokok semasa penelusuran: jangan menambah atau membuang nod dalam panggilan rekursif. Kumpulkan keputusan terlebih dahulu, kemudian ubah.
  • Hantar satu tatasusunan keputusan sebagai parameter: ini mengelakkan peruntukan tatasusunan baru pada setiap panggilan rekursif dan memudahkan menggabungkan keputusan subpokok.

Isu Umum

GejalaPuncaPenyelesaian
childNodes mempunyai panjang sifar pada rootNodeModel tidak dimuatkanPastikan scene.open() selesai tanpa ralat sebelum menelusuri
node.entity instanceof Mesh tidak pernah benarLaluan import Mesh yang salahImport Mesh dari @aspose/3d/entities, bukan dari akar @aspose/3d
Penelusuran terlepas mesh bersarangTidak melakukan rekursi ke semua anakPastikan panggilan rekursif meliputi setiap elemen dalam node.childNodes
mesh.controlPoints.length ialah 0Mesh dimuatkan tetapi tidak mengandungi geometriPeriksa sumber OBJ untuk kumpulan kosong; gunakan mesh.polygonCount sebagai pemeriksaan sekunder
Tumpukan melimpah pada hierarki yang dalamPokok adegan yang sangat dalam (ratusan peringkat)Gantikan rekursi dengan tumpukan eksplisit menggunakan Array.push / Array.pop

Soalan Lazim

Adakah scene.rootNode itu sendiri membawa entiti?
Tidak. Nod akar ialah kontena yang dicipta secara automatik oleh perpustakaan. Ia tidak mempunyai entiti. Geometri anda dan objek‑objek adegan lain berada pada nod anak satu atau lebih tahap di bawah rootNode.

Apakah perbezaan antara node.entity dan node.entities?
node.entity menyimpan entiti utama tunggal (kes biasa). Beberapa fail FBX dan COLLADA lama mungkin menghasilkan nod dengan pelbagai entiti yang dilampirkan; dalam kes itu node.entities (jamak) menyediakan senarai penuh.

Bolehkah saya menelusuri dalam susunan lebar-pertama dan bukannya dalam susunan dalam-pertama?
Ya. Gunakan queue dan bukannya panggilan rekursif: push scene.rootNode ke dalam array, kemudian shift dan proses nod sambil push setiap childNodes nod ke ekor queue.

Adakah scene.open() segerak?
Ya. scene.open() dan scene.openFromBuffer() kedua-duanya menyekat thread pemanggil sehingga fail selesai diparse. Balutkan mereka dalam thread pekerja jika anda perlu mengekalkan gelung peristiwa responsif.

Bagaimana saya dapat kedudukan ruang-dunia dari nod?
Baca node.globalTransform; ia mengembalikan GlobalTransform yang hanya‑baca dengan matriks ruang-dunia, yang terdiri daripada semua transformasi nenek moyang. Untuk matematik matriks eksplisit, panggil node.evaluateGlobalTransform(false).

Apakah jenis entiti yang mungkin selain Mesh?
Camera, Light, dan entiti rangka/tulang khusus. Semak node.entity.constructor.name atau gunakan instanceof dengan kelas khusus yang diimport dari @aspose/3d.

Lihat Juga

 Bahasa Melayu