Arbeiten mit dem Szenengraphen

Arbeiten mit dem Szenengraphen

Alle 3D-Inhalte in Aspose.3D FOSS für TypeScript befinden sich innerhalb eines Scene Objekt, das als Baum von Node Objekten. Das Verständnis dieser Hierarchie ist die Grundlage für das Erstellen, Laden und Verarbeiten jeder 3D-Datei.

Installation

Installieren Sie das Paket von npm, bevor Sie irgendeinen Code in diesem Leitfaden ausführen:

npm install @aspose/3d

Stellen Sie sicher, dass Ihr tsconfig.json enthält "module": "commonjs" und "moduleResolution": "node" für die korrekte Auflösung von Unterpfad-Importen.

Konzepte des Szenengraphen

Der Szenengraph besteht aus drei Ebenen:

StufeKlasseRolle
SzeneSceneTop-Level-Container. Enthält rootNode, animationClips, und assetInfo.
KnotenNodeBenannter Baumknoten. Kann Kindknoten, eine Entität, eine Transformation und Materialien haben.
EntitätMesh, Camera, Light, …Inhalt, der an einen Knoten angehängt ist. Ein Knoten trägt höchstens eine Entität.

Die Vererbungskette für die wichtigsten Bausteine ist:

A3DObject
  └─ SceneObject
       ├─ Node          (tree structure)
       └─ Entity
            └─ Geometry
                 └─ Mesh   (polygon geometry)

scene.rootNode wird automatisch erstellt. Sie erstellen es nicht manuell; Sie erstellen Kindknoten darunter.

Schritt 1: Eine Szene erstellen

import { Scene } from '@aspose/3d';

const scene = new Scene();
console.log(scene.rootNode.name); // '' (empty string — the root node is created with no name)

Ein neuer Scene beginnt mit einem leeren Wurzelknoten und keinen Animationsclips. Sie bauen Inhalt, indem Sie Kindknoten anhängen.

Schritt 2: Kindknoten hinzufügen

Verwenden Sie createChildNode() um den Baum zu erweitern. Die Methode gibt das neue zurück. Node, sodass Sie weitere Aufrufe von jeder Ebene aus verketten können:

import { Scene } from '@aspose/3d';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

console.log(scene.rootNode.childNodes.length); // 1 (parent)
console.log(parent.childNodes.length);         // 1 (child)

Knotennamen sind beliebige Zeichenketten. Die Namen müssen nicht eindeutig sein, aber die Verwendung aussagekräftiger Namen erleichtert das Debuggen von Traversierungscode.

Schritt 3: Ein Mesh erstellen und Scheitelpunkte setzen

Mesh ist die primäre Geometrieklasse. Fügen Sie Scheitelpunktpositionen hinzu, indem Sie sie pushen Vector4 Werte in mesh.controlPoints, dann rufen Sie createPolygon() auf, um Flächen anhand von Scheitelindizes zu definieren:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4 } from '@aspose/3d/utilities';

const scene = new Scene();
const child = scene.rootNode.createChildNode('parent').createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3); // quad face using all four vertices

child.entity = mesh;

console.log(mesh.controlPoints.length); // 4
console.log(mesh.polygonCount);         // 1

Vector4 verwendet homogene Koordinaten: die w Komponente ist 1 für Positionen und 0 für Richtungsvektoren.

Schritt 4: Knoten-Transformationen festlegen

Jeder Knoten hat ein transform Eigenschaft mit translation, rotation, und scaling. Setzen translation um den Knoten relativ zu seinem Elternknoten zu verschieben:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4, Vector3 } from '@aspose/3d/utilities';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3);
child.entity = mesh;

child.transform.translation = new Vector3(2.0, 0.0, 0.0);

globalTransform liefert die Welt-Raum-Transformationsmatrix (schreibgeschützt), berechnet durch das Konkatenieren aller Vorfahren-Transformationen.

Schritt 5: Baum durchlaufen

Schreiben Sie eine rekursive Funktion, um jeden Knoten zu besuchen. Prüfen Sie node.entity und node.childNodes auf jeder Ebene:

function traverse(node: any, depth = 0): void {
    const indent = '  '.repeat(depth);
    const entityType = node.entity ? node.entity.constructor.name : 'none';
    console.log(`${indent}${node.name} [${entityType}]`);
    for (const child of node.childNodes) {
        traverse(child, depth + 1);
    }
}

traverse(scene.rootNode);

Für die oben erstellte Hierarchie wird die Ausgabe sein:

 [none]
  parent [none]
    child [Mesh]

Der Name des Wurzelknotens ist eine leere Zeichenkette, weil Scene erstellt ihn ohne Namensargument.

Schütze den Zugriff auf Entitäten immer mit einem Null‑Check, bevor du zu einem spezifischen Typ castest. Nicht jeder Knoten enthält eine Entität.

Schritt 6: In glTF oder GLB speichern

Verwenden Sie GltfSaveOptions um das Ausgabeformat zu steuern. Setzen binaryMode = true um ein einzelnes eigenständiges .glb Datei; belassen Sie sie false für das JSON .gltf + .bin Sidecar-Paar:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4, Vector3 } from '@aspose/3d/utilities';
import { GltfSaveOptions, GltfFormat } from '@aspose/3d/formats/gltf';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3);
child.entity = mesh;

child.transform.translation = new Vector3(2.0, 0.0, 0.0);

const saveOpts = new GltfSaveOptions();
saveOpts.binaryMode = true;                             // write a single .glb file
scene.save('scene.glb', GltfFormat.getInstance(), saveOpts);

console.log('Scene saved to scene.glb');

Übergabe GltfFormat.getInstance() als Format-Argument, damit die Bibliothek den korrekten Encoder verwendet, unabhängig von der Dateierweiterung.

Tipps und bewährte Verfahren

  • Verwenden createChildNode() statt zu konstruieren Node direkt: createChildNode() verknüpft automatisch die Eltern‑Kind‑Beziehung und registriert den Knoten im Baum.
  • Prüfen node.entity vor dem Zugriff auf Entity-Eigenschaften: viele Knoten (Gruppenknoten, Knochen, Locatoren) besitzen keine Entity. Immer mit einer Nullprüfung absichern oder instanceof Test.
  • Setzen translation auf Kindknoten, nicht auf Mesh-Vertexen: Modifizieren transform.translation ist nicht-destruktiv und lässt sich mit übergeordneten Transformationen kombinieren.
  • Bevorzugen binaryMode = true für GLB: eine einzelne .glb Datei ist einfacher zu verteilen, in Browsern zu laden und in Spiel-Engines zu importieren als die geteilte .gltf + .bin Format.
  • Durchlaufen über for...of über childNodes: Vermeiden Sie numerische Indizierung; verwenden Sie das Iterable direkt für zukünftige Kompatibilität.

Häufige Probleme

SymptomWahrscheinliche UrsacheLösung
child.entity = mesh hat keinen Einfluss auf den ExportEntität falscher Knotenebene zugewiesenZuweisen entity zum Blattknoten, nicht zu einem Gruppenknoten
node.entity ist immer nullNur prüfen rootNode selbstRekursiv in node.childNodes; rootNode hat typischerweise keine Entität
Transform wird im GLB-Viewer nicht wiedergegebenglobalTransform nicht aktualisiertglobalTransform wird beim Speichern berechnet; set transform.translation vor dem Aufruf scene.save()
GLB erzeugt ein separates .bin SidecarbinaryMode Standardwert ist falseSetzen saveOpts.binaryMode = true

Siehe auch

 Deutsch