Kaip naršyti 3D scenos grafą TypeScript kalba

Kaip naršyti 3D scenos grafą TypeScript kalba

Scenos grafas Aspose.3D FOSS for TypeScript yra Node objektų medis, kurio šaknis yra scene.rootNode. Perėjimas yra rekursinis: kiekvienas mazgas atskleidžia childNodes iteruojamą objektą ir pasirenkamą entity savybę. Šiame vadove parodyta, kaip pereiti visą medį, identifikuoti objektų tipus ir surinkti mesh statistiką.

Būtinosios sąlygos

  • Node.js 18 arba vėlesnė
  • TypeScript 5.0 arba vėlesnė
  • @aspose/3d įdiegta

Žingsnis po žingsnio vadovas

Žingsnis 1: Įdiegti ir importuoti

Įdiekite paketą:

npm install @aspose/3d

Importuokite šio vadovo naudojamas klases:

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

Scene ir Mesh yra pagrindinės klasės. ObjLoadOptions naudojama įkėlimo pavyzdyje; pakeiskite atitinkamą parametrų klasę kitoms formatams.


Žingsnis 2: Įkelti sceną iš failo

Sukurkite Scene ir iškvieskite scene.open() su failo keliu. Formato aptikimas yra automatinis pagal binarinius magiškus skaičius, todėl nereikia nurodyti formato GLB, STL ar 3MF failams:

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

Taip pat galite įkelti iš Buffer atmintyje naudodami scene.openFromBuffer(buffer, options); tai naudinga serverless konvejeriuose, kur nėra prieinamos disko I/O.


Žingsnis 3: Parašykite rekursinę perėjimo funkciją

Rekursija per childNodes yra standartinis šablonas. Funkcija lankosi kiekviename mazge gilumo pirmumo tvarka:

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

Scenoje, kurioje yra vienas tinklas pavadintas Cube, išvestis atrodys taip:

[-] 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 yra null grupės mazgams, kaulams ir lokatorių. constructor.name patikrinimas veikia bet kuriam objekto tipui: Mesh, Camera, Light ir t.t.


Žingsnis 4: Gauti objekto tipą kiekviename mazge

Norėdami imtis veiksmų pagal objekto tipą, naudokite instanceof patikrinimą po nulio apsaugos:

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 yra saugiausias būdas patvirtinti, kad objektas yra daugiakampio tinklelis, prieš pasiekiant controlPoints, polygonCount arba viršūnių elementus.


Žingsnis 5: Filtruoti mazgus pagal objekto tipą

Norėdami surinkti tik tinklo turinčius mazgus neatspausdindami visą medį, naudokite rekursinį akumuliatorių:

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

Funkcija priima pasirenkamą results masyvą, kad kviečiantys galėtų iš anksto užpildyti jį, sujungiant rezultatus iš kelių potėklių.


Žingsnis 6: Surinkti visus tinklus ir spausdinti viršūnių skaičius

Išplėsti rinkiklį, kad spausdintų statistinius duomenis kiekvienam tinklui:

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

Pavyzdinė išvestis dviejų tinklų scenai:

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>

Patarimai ir geriausia praktika

  • Visada atlikite null patikrinimą node.entity prieš prieigą prie objektui būdingų savybių. Daugelis mazgų yra gryni grupės mazgai, kurie neturi objekto.
  • Naudokite instanceof vietoj constructor.name tipų patikrinimams loginėse šakose. instanceof yra saugus perrefaktorizavimui; eilutės palyginimas su constructor.name sugriūva su minifikacija.
  • Naršykite per for...of vietoj childNodes: iteruojamas objektas saugiai tvarko bet kokio dydžio masyvus. Venkite skaitmeninio indeksavimo, kad išlaikytumėte ateities suderinamumą.
  • Venkite medžio modifikavimo naršymo metu: neįtraukite ir nepašalinkite mazgų rekursinio kvietimo viduje. Pirma surinkite rezultatus, tada modifikuokite.
  • Perduokite rezultatų masyvą kaip parametrą: tai išvengia naujo masyvo alokavimo kiekvieno rekursinio kvietimo metu ir palengvina poantrankių rezultatų sujungimą.

Dažnos problemos

SymptomCauseFix
childNodes turi nulio ilgį ant rootNodeModelis neįkeliamasĮsitikinkite, kad scene.open() baigta be klaidų prieš peržvalgą
node.entity instanceof Mesh niekada nėra teisingaNeteisingas Mesh importo keliasImportuokite Mesh@aspose/3d/entities, o ne iš @aspose/3d šaknies
Peržvalga praleidžia įdėtus tinklusNerecursuojama į visus vaikusĮsitikinkite, kad rekursinis kvietimas apima kiekvieną elementą node.childNodes
mesh.controlPoints.length yra 0Tinklas įkeliamas, bet neturi geometrijosPatikrinkite OBJ šaltinį dėl tuščių grupių; naudokite mesh.polygonCount kaip antrinį patikrinimą
Steko perpildymas giliose hierarchijoseLabai gili scenos medžio struktūra (šimtai lygių)Pakeiskite rekursiją į aiškų steką naudojant Array.push / Array.pop

Dažnai užduodami klausimai

Ar scene.rootNode pats neša objektą?
Ne. Šakninis mazgas yra konteineris, sukurtas automatiškai bibliotekoje. Jis neturi objekto. Jūsų geometrija ir kiti scenos objektai yra vaikų mazguose, viename ar keliuose lygiuose žemiau rootNode.

Kuo skiriasi node.entity ir node.entities? node.entity saugo vieną pirminį objektą (dažniausia situacija). Kai kurie senesni FBX ir COLLADA failai gali sukurti mazgus su keliais prijungtais objektais; tokiu atveju node.entities (daugiskaita) pateikia visą sąrašą.

Ar galiu atlikti perėjimą plačiausio pirmumo tvarka vietoj gylio pirmumo?
Taip. Naudokite queue vietoj rekursinio kvietimo: push scene.rootNode į array, tada shift ir process nodes, tuo pačiu push kiekvieno node childNodes į queue tail.

Ar scene.open() sinchroninis? Taip. scene.open() ir scene.openFromBuffer() abu blokuoja iškviečiančią giją, kol failas pilnai išanalizuojamas. Įdėkite juos į darbo giją, jei reikia, kad įvykių ciklas išliktų reaguojantis.

Kaip gauti pasaulio erdvės pozicijas iš mazgo?
Skaitykite node.globalTransform; jis grąžina tik skaitymui skirtą GlobalTransform su pasaulio erdvės matrica, sudaryta iš visų tėvų transformacijų. Išsamiai atliekant matricos skaičiavimus, kvieskite node.evaluateGlobalTransform(false).

Kokie objektų tipai yra galimi, išskyrus Mesh?
Camera, Light, ir pasirinktini skeletų/kostų objektai. Patikrinkite node.entity.constructor.name arba naudokite instanceof su konkrečia klase, importuota iš @aspose/3d.

Žr. taip pat

 Lietuvių