Wie man einen 3D-Szenengraph in TypeScript durchläuft

Wie man einen 3D-Szenengraph in TypeScript durchläuft

Die Szene Grafik in Aspose.3D FOSS für TypeScript ist ein Baum von Node Objekte, die auf der scene.rootNode.Die Überfahrt ist rekursiv: Jeder Knoten zeigt eine childNodes Erweiterbar und optional entity Diese Anleitung zeigt, wie man den gesamten Baum durchläuft, Entitätstypen identifiziert und Netzstatistiken erhebt.

Voraussetzungen für die Durchführung der Maßnahmen

  • Node.js 18 or later
  • TypScript 5.0 oder neuer Version
  • @aspose/3d installiert

Schritt für Schritte

Schritt 1: Installieren und Importieren

Installieren Sie das Paket:

asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.

Importieren der in diesem Leitfaden verwendeten Klassen:

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

Scene und . Mesh sind die Kernklassen. ObjLoadOptions wird im Lastbeispiel verwendet; die Klasse “Matching Options” für andere Formate ersetzen.


Schritt 2: Laden Sie eine Szene aus einer Datei ein

Erstellen einer Scene und rufen Sie an. scene.open() Die Formaterkennung erfolgt automatisch aus binären Zauberzahlen, so dass Sie das Format für GLB-, STL- oder 3MF-Dateien nicht angeben müssen:

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

Sie können auch von einem Buffer in der Speicherverarbeitung mit Hilfe von scene.openFromBuffer(buffer, options); nützlich in serverlosen Pipelines, bei denen keine Festplatten-E/A verfügbar ist.


Schritt 3: Eine rekursive Durchlauffunktion schreiben

Rückfall über childNodes Die Funktion besucht jeden Knoten zuerst in der Tiefe:

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 eine Szene mit einem Netz namens Cube, wird die Ausgabe wie folgt aussehen:

[-] 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=“Code kopieren”

<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 ist . null Die Daten werden für die Gruppe Knoten, Knochen und Lokatoren. constructor.name Prüfarbeiten für jeden Entitätstyp: Mesh, Camera, Light, usw.


Schritt 4: Zugriff auf den Entity-Typ in jedem Knoten

Um Maßnahmen auf der Grundlage des Entitätstyps zu ergreifen, verwenden Sie eine instanceof Nach dem Nullschutz überprüfen:

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 ist der sicherste Weg, um zu bestätigen, dass die Entität ein Polygonmesch vor dem Zugriff ist. controlPoints, polygonCount, oder Winkel-Elemente.


Schritt 5: Filterknoten nach Entitätstyp

Um nur Netzträgerknoten zu sammeln, ohne den gesamten Baum auszudrucken, wird ein rekursiver Akkumulator verwendet:

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

Die Funktion akzeptiert eine optionale results Array, damit Anrufer es vor-befüllen können, um Ergebnisse über mehrere Unterbäume hinweg zu verschmelzen.


Schritt 6: Sammeln Sie alle Meshes und Drucken Sie die Vertex-Zählungen

Erweitern Sie den Sammler, um die Statistiken pro Masche zu drucken:

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

Beispiel für eine zweimeschnittene Szene:

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=“Code kopieren”

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

Tipps und beste Praktiken

  • Immer null-check node.entity Viele Knoten sind reine Gruppenknoten, die keine Entität tragen.
  • Verwendung instanceof Überholt constructor.name für Typprüfungen in Logikpfaden. instanceof ist refaktorsicher; Stringvergleich auf constructor.name mit der Minifizierung bricht.
  • Durchschnittsweg for...of Überholt childNodes:Die Iteration erfolgt mit allen Arraygrößen. Vermeiden Sie die numerische Indexierung für eine nach vorne kompatible Lösung.
  • Vermeiden Sie die Mutation des Baumes während der Durchdringung.: keine Knoten in der rekursiven Aufforderung hinzufügen oder entfernen. Sammeln Sie zuerst die Ergebnisse, dann ändern Sie sie.
  • Übertragen eines Ergebnis-Arrays als Parameter: so wird vermieden, bei jedem rekursiven Aufruf ein neues Array zuzuweisen und es ist einfach, die Ergebnisse des Unterbaums zusammenzuführen.

Häufige Probleme

Symptom derUrsacheBehebung
childNodes hat eine Länge von 0 auf rootNodeModell nicht geladenSicherstellen scene.open() vor der Überquerung fehlerfrei abgeschlossen.
node.entity instanceof Mesh - Das stimmt nie .Falsch . Mesh ImportwegEinfuhr Mesh von @aspose/3d/entities, nicht ab @aspose/3d Wurzel
Durchschnittsmaschen mit verschachtelten MaschenNicht bei allen Kindern wiederkehrend.Sicherstellen, dass der rekursive Aufruf alle Elemente in den node.childNodes
mesh.controlPoints.length ist 0Maschen mit Geometrie, jedoch ohne BearbeitungÜberprüfen Sie die OBJ-Quelle auf leere Gruppen; verwenden mesh.polygonCount als Sekundärprüfung
Stacküberschuss bei tiefen HierarchienSehr tiefe Szenenbaum (Hunderte von Ebenen)Ersetzen Sie die Rekursion durch einen expliziten Stack, indem Sie Array.push / Array.pop

Häufig gestellte Fragen

Ist es so ? scene.rootNode - Sie tragen eine Entität? Nein. Der Root-Knoten ist ein Container, der automatisch von der Bibliothek erstellt wird. Er hat keine Entität. Ihre Geometrie und andere Szenobjekte leben auf Kindernoden eine oder mehrere Ebenen unterhalb. rootNode.

Was ist der Unterschied zwischen node.entity und . node.entities? node.entity enthält die einzige primäre Entität (Common Case). Einige ältere FBX- und COLLADA-Dateien können Knoten mit mehreren angeschlossenen Entitäten erzeugen; in diesem Fall node.entities (Mehrzahl) enthält die vollständige Liste.

Kann ich in der Breite-ersten Ordnung statt tiefste erste durchqueren? Ja, benutze eine Warteschlange anstelle eines rekursiven Anrufs: Drücken. scene.rootNode in eine Array, dann verschieben und Prozessknoten während jeder Knoten drücken childNodes in die Schlange.

Ist . scene.open() Synchron? - Ja, das ist gut. scene.open() und . scene.openFromBuffer() Beide blockieren den Anrufer, bis die Datei vollständig analysiert ist. Wickeln Sie sie in einen Worker-Thread ein, wenn Sie die Ereignisschleife reaktionsfähig halten müssen.

Wie bekomme ich Weltraumpositionen von einem Knoten? Lesen Sie . node.globalTransform; es gibt einen Lesebericht zurück. GlobalTransform Für explizite Matrix-Mathematik, nennen Sie die Weltraummatrix. node.evaluateGlobalTransform(false).

Welche Entitätstypen sind möglich, außer: Mesh? Camera, Light, und benutzerdefinierte Skelett-/Knochenentitäten. node.entity.constructor.name oder Verwendung instanceof mit der spezifischen Klasse, die aus: @aspose/3d.

Siehe auch:

 Deutsch