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/3dImportuokite š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.entityprieš prieigą prie objektui būdingų savybių. Daugelis mazgų yra gryni grupės mazgai, kurie neturi objekto. - Naudokite
instanceofvietojconstructor.nametipų patikrinimams loginėse šakose.instanceofyra saugus perrefaktorizavimui; eilutės palyginimas suconstructor.namesugriūva su minifikacija. - Naršykite per
for...ofvietojchildNodes: 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
| Symptom | Cause | Fix |
|---|---|---|
childNodes turi nulio ilgį ant rootNode | Modelis neįkeliamas | Įsitikinkite, kad scene.open() baigta be klaidų prieš peržvalgą |
node.entity instanceof Mesh niekada nėra teisinga | Neteisingas Mesh importo kelias | Importuokite Mesh iš @aspose/3d/entities, o ne iš @aspose/3d šaknies |
| Peržvalga praleidžia įdėtus tinklus | Nerecursuojama į visus vaikus | Įsitikinkite, kad rekursinis kvietimas apima kiekvieną elementą node.childNodes |
mesh.controlPoints.length yra 0 | Tinklas įkeliamas, bet neturi geometrijos | Patikrinkite OBJ šaltinį dėl tuščių grupių; naudokite mesh.polygonCount kaip antrinį patikrinimą |
| Steko perpildymas giliose hierarchijose | Labai 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.