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/3dStellen 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:
| Stufe | Klasse | Rolle |
|---|---|---|
| Szene | Scene | Top-Level-Container. Enthält rootNode, animationClips, und assetInfo. |
| Knoten | Node | Benannter Baumknoten. Kann Kindknoten, eine Entität, eine Transformation und Materialien haben. |
| Entität | Mesh, 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 konstruierenNodedirekt:createChildNode()verknüpft automatisch die Eltern‑Kind‑Beziehung und registriert den Knoten im Baum. - Prüfen
node.entityvor dem Zugriff auf Entity-Eigenschaften: viele Knoten (Gruppenknoten, Knochen, Locatoren) besitzen keine Entity. Immer mit einer Nullprüfung absichern oderinstanceofTest. - Setzen
translationauf Kindknoten, nicht auf Mesh-Vertexen: Modifizierentransform.translationist nicht-destruktiv und lässt sich mit übergeordneten Transformationen kombinieren. - Bevorzugen
binaryMode = truefür GLB: eine einzelne.glbDatei ist einfacher zu verteilen, in Browsern zu laden und in Spiel-Engines zu importieren als die geteilte.gltf+.binFormat. - Durchlaufen über
for...ofüberchildNodes: Vermeiden Sie numerische Indizierung; verwenden Sie das Iterable direkt für zukünftige Kompatibilität.
Häufige Probleme
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
child.entity = mesh hat keinen Einfluss auf den Export | Entität falscher Knotenebene zugewiesen | Zuweisen entity zum Blattknoten, nicht zu einem Gruppenknoten |
node.entity ist immer null | Nur prüfen rootNode selbst | Rekursiv in node.childNodes; rootNode hat typischerweise keine Entität |
| Transform wird im GLB-Viewer nicht wiedergegeben | globalTransform nicht aktualisiert | globalTransform wird beim Speichern berechnet; set transform.translation vor dem Aufruf scene.save() |
GLB erzeugt ein separates .bin Sidecar | binaryMode Standardwert ist false | Setzen saveOpts.binaryMode = true |
Siehe auch
- Funktionen und Merkmale: vollständige API-Referenz für alle Funktionsbereiche.
- Formatunterstützung: unterstützte 3D-Formate, Lese-/Schreibfähigkeit und Formatoptionen.
- Wie man ein 3D-Mesh programmgesteuert erstellt.