Kuinka käydä läpi 3D-skenegraafi TypeScriptissä

Kuinka käydä läpi 3D-skenegraafi TypeScriptissä

Kohtausgrafi Aspose.3D FOSS for TypeScript -kirjastossa on puu Node-objekteista, jonka juuri on scene.rootNode. Traversointi on rekursiivista: jokainen solmu tarjoaa childNodes-iteroinnin ja valinnaisen entity-ominaisuuden. Tämä opas näyttää, miten käydään läpi koko puu, tunnistetaan entiteettityypit ja kerätään verkkojen tilastot.

Edellytykset

  • Node.js 18 tai uudempi
  • TypeScript 5.0 tai uudempi
  • @aspose/3d asennettu

Vaiheittainen opas

Vaihe 1: Asenna ja tuo

Asenna paketti:

npm install @aspose/3d

Tuo tässä oppaassa käytetyt luokat:

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

Scene ja Mesh ovat ydinklassit. ObjLoadOptions on käytetty latausesimerkissä; korvaa se vastaavalla asetuskoodilla muille formaateille.


Vaihe 2: Lataa kohtaus tiedostosta

Luo Scene ja kutsu scene.open() tiedostopolulla. Muodon tunnistus tapahtuu automaattisesti binaarisista taikamerkeistä, joten sinun ei tarvitse määrittää formaattia GLB-, STL- tai 3MF‑tiedostoille:

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}`);

Voit myös ladata Buffer muistista käyttämällä scene.openFromBuffer(buffer, options); hyödyllistä serverittömissä putkissa, joissa levy‑I/O ei ole käytettävissä.


Vaihe 3: Kirjoita rekursiivinen läpikäyntifunktio

Rekursio childNodes:n yli on vakio­malli. Funktio käy läpi jokaisen solmun syvyys­ensimmäisenä:

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);

Kun kohtauksessa on yksi verkko nimeltä Cube, tuloste näyttää tältä:

[-] 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 on null ryhmäsolmuille, luuille ja paikannuksille. constructor.name tarkistus toimii kaikille entiteettityypeille: Mesh, Camera, Light, jne.


Vaihe 4: Hae entiteettityyppi jokaiselta solmulta

Suorittaaksesi toiminnon entiteettityypin perusteella, käytä instanceof tarkistusta nollasuojaimen jälkeen:

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 on turvallisin tapa varmistaa, että entiteetti on polygoniverkko ennen kuin käytetään controlPoints, polygonCount tai vertex-elementtejä.


Vaihe 5: Suodata solmut entiteettityypin mukaan

Kerääksesi vain verkkoa kantavat solmut ilman koko puun tulostamista, käytä rekursiivista kertyjää:

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)`);

Funktio hyväksyy valinnaisen results-taulukon, jotta kutsujat voivat esitäyttää sen yhdistääkseen tuloksia useiden alipuiden välillä.


Vaihe 6: Kerää kaikki verkot ja tulosta solmujen määrät

Laajenna kerääjää tulostamaan per‑verkko‑tilastot:

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`);
}

Esimerkkituloste kahden verkon kohtaukselle:

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>

Vinkkejä ja parhaita käytäntöjä

  • Tarkista aina null node.entity ennen kuin käytät entiteettiin liittyviä ominaisuuksia. Monet solmut ovat puhtaita ryhmäsolmuja, jotka eivät sisällä entiteettiä.
  • Käytä instanceof sen sijaan, että käyttäisit constructor.name tyyppitarkistuksiin loogisissa poluissa. instanceof on refaktorointiturvallinen; merkkijonojen vertailu constructor.name:ssa rikkoutuu minifioinnissa.
  • Kulje for...of:n kautta childNodes:n sijaan: iteroitava käsittelee kaikki taulukon koot turvallisesti. Vältä numeerista indeksointia tulevaisuuden yhteensopivuuden vuoksi.
  • Vältä puun muokkaamista kulun aikana: älä lisää tai poista solmuja rekursiivisen kutsun sisällä. Kerää tulokset ensin, sitten muokkaa.
  • Välitä tuloslista parametrina: tämä estää uuden taulukon varaamisen jokaisessa rekursiivisessa kutsussa ja tekee alipuiden tulosten yhdistämisestä helppoa.

Yleiset ongelmat

OireSyyKorjaus
childNodes on nollapituinen kohteessa rootNodeMalli ei ladattuVarmista, että scene.open() on suoritettu ilman virheitä ennen läpikäyntiä
node.entity instanceof Mesh ei koskaan tosiVäärä Mesh tuontipolkuTuo Mesh kohteesta @aspose/3d/entities, äläkä kohteesta @aspose/3d juuri
Läpikäynti ohittaa sisäkkäiset verkotEi rekursiota kaikkiin lapsiinVarmista, että rekursiivinen kutsu kattaa jokaisen elementin kohteessa node.childNodes
mesh.controlPoints.length on 0Verkko ladattu, mutta ei sisällä geometriaaTarkista OBJ-lähde tyhjien ryhmien varalta; käytä mesh.polygonCount toissijaisena tarkistuksena
Pinon ylivuoto syvissä hierarkioissaErittäin syvä kohtauspuu (satoja tasoja)Korvaa rekursio eksplisiittisellä pinolla käyttäen Array.push / Array.pop

Usein kysytyt kysymykset

Sisältääkö scene.rootNode itse entiteetin?
Ei. Juurisolmu on kirjastoautomaatisesti luotu säiliö. Sillä ei ole entiteettiä. Geometriasi ja muut kohtausobjektit sijaitsevat lapsisolmuissa yhden tai useamman tason alapuolella rootNode.

Mikä on ero node.entity ja node.entities välillä? node.entity sisältää yhden ensisijaisen entiteetin (yleinen tapa). Jotkut vanhemmat FBX- ja COLLADA-tiedostot saattavat tuottaa solmuja, joissa on useita liitettyjä entiteettejä; siinä tapauksessa node.entities (monikko) tarjoaa koko luettelon.

Voinko kulkea leveyssuuntaisesti syvyysjärjestyksen sijaan? Kyllä. Käytä queuea rekursiivisen kutsun sijaan: push scene.rootNode arrayen, sitten shift ja käsittele solmut samalla kun push jokaisen solmun childNodes queue:n hännän.

Onko scene.open() synkroninen? Kyllä. scene.open() ja scene.openFromBuffer() estävät kutsuvan säikeen toiminnan, kunnes tiedosto on täysin jäsennetty. Kääri ne työntekijäsäikeeseen, jos sinun täytyy pitää tapahtumasilmukka reagoivana.

Miten saan maailmanavaruuden sijainnit solmusta?
Lue node.globalTransform; se palauttaa vain‑luku GlobalTransform, jossa on maailmanavaruuden matriisi, koostettuna kaikista esivanhempien muunnoksista. Jos haluat tehdä eksplisiittistä matriisilaskentaa, kutsu node.evaluateGlobalTransform(false).

Mitä entiteettityyppejä on mahdollista käyttää Meshn lisäksi?
Camera, Light ja mukautetut luuranko/luu‑entiteetit. Tarkista node.entity.constructor.name tai käytä instanceof yhdessä erityisen luokan kanssa, joka on tuotu @aspose/3dsta.

Katso myös

 Suomi