Hur man passerar en 3D Scene Graph i TypeScript
Scenogrammet i Aspose.3D FOSS för TypeScript är ett träd av Node föremål som är rötta i scene.rootNode.Traversal är recursiv: varje nod exponerar en childNodes Iterbar och optional entity Denna guide visar hur man går hela trädet, identifiera entitetstyper och samla mässtatistik.
Förutsättningar
- Node.js 18 eller senare
- TypScript 5.0 eller senare
@aspose/3dinstallerade
Steg för steg guide
Steg 1: Installera och importera
Installera paketet:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.
Import av de klasser som används i denna guide:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';Scene och Mesh Det är de kärnklasser. ObjLoadOptions används i laddningsexempelet; ersätta matchande alternativ klass för andra format.
Steg 2: Ladda upp en scen från en fil
Skapa en a Scene och ringer scene.open() Formatdetektion är automatiskt från binära magiska siffror, så du behöver inte ange formatet för GLB, STL eller 3MF filer:
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}`);Du kan också ladda från en Buffer i minnet med användning scene.openFromBuffer(buffer, options);Användbar i serverfria rör där I/O-skivan inte är tillgänglig.
Steg 3: Skriv en recursiv resefunktion
Recursion över childNodes är standardmallen. funktionen besöker varje nod djup-först:
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);För en scen med ett mesh namnet Cube,Produktionen kommer att se ut som:
[-] 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 är null för grupp noder, ben och lokatorer. constructor.name Kontrollera arbetet för någon typ av enhet: Mesh, Camera, Light,och etc.
Steg 4: Tillgång till entitetstypen på varje nod
För att agera baserat på enhetstyp, använd en instanceof Kontrollera efter nollguard:
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 är det säkraste sättet att bekräfta entiteten är en polygonmässa innan du får tillgång till controlPoints, polygonCount,Eller vertex element.
Steg 5: Filtrera noder efter entitetstyp
För att endast samla knappar utan att skriva ut hela trädet, använd en återkommande akkumulator:
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)`);Funktionen accepterar ett valfritt results array så att samtalare kan förpopulera det för att kombinera resultat över flera underträd.
Steg 6: Samla alla mässa och trycka vertex-konton
Utöka samlaren för att skriva ut per-mesh statistik:
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`);
}Exempel på utgången för en tvåmässa scen:
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>
Tips och bästa praxis
- Alltid nollcheck
node.entitymånga noder är rena grupp nodar som inte bär någon enhet. - Användning
instanceoföverconstructor.nameför typcheck i logiska vägar.instanceofär återställare-säker; sträng jämförelse påconstructor.nameAvbrott med miniatyr. - genom att
for...oföverchildNodes: det iterabla hanterar alla arraystorlekar säkert. Undvik numerisk indexering för framåtkompatibilitet. - Undvik att mutera trädet under traversalen:• Lägg inte till eller ta bort knappar inom den återkommande samtalet.Samla in resultat först och ändra sedan.
- Pass en resultat array som parameter:Detta undviker att tilldela en ny rad på varje återkommande samtal och gör det enkelt att kombinera resultat under trädet.
Vanliga frågor
| Symtom | orsaker | Fixar |
|---|---|---|
childNodes har noll längd på rootNode | Modellen inte laddas | Säkerhet scene.open() slutfört utan fel innan du passerar |
node.entity instanceof Mesh Aldrig sann | felaktigt Mesh Importvägen | Import av Mesh från @aspose/3d/entities,Inte från @aspose/3d Röda |
| Traversal missar nötta mesh | Inte vänder sig till alla barn | Se till att den återkommande samtalet täcker varje element i node.childNodes |
mesh.controlPoints.length är 0 | Mesh laddad men innehåller ingen geometri | Kontrollera OBJ-källan för tomma grupper; användning mesh.polygonCount som sekundärcheck |
| Stark överflöd på djupa hierarker | Mycket djupa scenen träd (hundratals nivåer) | Ersätt recursion med en uttrycklig stack med hjälp av Array.push / Array.pop |
Ofta ställda frågor
Gör det scene.rootNode Själv bär man en entitet? No. Root nod är en behållare som skapats automatiskt av biblioteket. Den har ingen entitet. Din geometri och andra scenenobjekt lever på barn noder ett eller flera nivåer nedan rootNode.
Vad är skillnaden mellan node.entity och node.entities? node.entity vissa äldre FBX och COLLADA-filer kan producera noder med flera anslutna enheter; i det fallet node.entities (Plural) ger den fullständiga listan.
Kan jag gå i bredd-första ordningen istället för djup-fjärde? Använd en kruka istället för ett återkommande samtal: push scene.rootNode i en rad, sedan byta och bearbeta noder medan du trycker varje nod. childNodes In i den här kronan.
är scene.open() Synkroniserad ? Ja ja . scene.open() och scene.openFromBuffer() Båda blockerar samtalsträngen tills filen är helt parsedd. Häll dem i en arbetarråd om du behöver hålla händelsespåret responsivt.
Hur får jag världsutrymme positioner från en nod? Läsning node.globalTransform;återvänder en läs-enbart GlobalTransform med världs-rymdmatris, bestående av alla förfäder transformationer. För explicit matrix matematik, kallas node.evaluateGlobalTransform(false).
Vilka typer av enheter är möjliga utöver Mesh? Camera, Light, och anpassade skelett/kört entiteter. kontrollera node.entity.constructor.name eller använda instanceof med den särskilda klass som importeras från @aspose/3d.