Hur man passerar en 3D Scene Graph i TypeScript

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/3d installerade

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.entity många noder är rena grupp nodar som inte bär någon enhet.
  • Användning instanceof över constructor.name för typcheck i logiska vägar. instanceof är återställare-säker; sträng jämförelse på constructor.name Avbrott med miniatyr.
  • genom att for...of över childNodes: 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

SymtomorsakerFixar
childNodes har noll längd på rootNodeModellen inte laddasSäkerhet scene.open() slutfört utan fel innan du passerar
node.entity instanceof Mesh Aldrig sannfelaktigt Mesh ImportvägenImport av Mesh från @aspose/3d/entities,Inte från @aspose/3d Röda
Traversal missar nötta meshInte vänder sig till alla barnSe till att den återkommande samtalet täcker varje element i node.childNodes
mesh.controlPoints.length är 0Mesh laddad men innehåller ingen geometriKontrollera OBJ-källan för tomma grupper; användning mesh.polygonCount som sekundärcheck
Stark överflöd på djupa hierarkerMycket 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.

Se även

 Svenska