Lucrul cu graful de scenă
Tot conținutul 3D din Aspose.3D FOSS pentru TypeScript se află în interiorul unui Scene obiect organizat ca un arbore de Node obiecte. Înțelegerea acestei ierarhii este fundamentul pentru construirea, încărcarea și procesarea oricărui fișier 3D.
Instalație
Instalați pachetul de pe npm înainte de a rula orice cod din acest ghid:
npm install @aspose/3dAsigurați-vă că tsconfig.json include "module": "commonjs" și "moduleResolution": "node" pentru rezolvarea corectă a importurilor sub-cale.
Concepte ale grafului de scenă
Graful de scenă are trei niveluri:
| Nivel | Clasă | Rol |
|---|---|---|
| Scenă | Scene | Container de nivel superior. Conține rootNode, animationClips, și assetInfo. |
| Nod | Node | Nod de arbore denumit. Poate avea noduri copil, o entitate, o transformare și materiale. |
| Entitate | Mesh, Camera, Light, … | Conținut atașat unui nod. Un nod poartă cel mult o entitate. |
Lanțul de moștenire pentru blocurile principale este:
A3DObject
└─ SceneObject
├─ Node (tree structure)
└─ Entity
└─ Geometry
└─ Mesh (polygon geometry)scene.rootNode este creat automat. Nu îl creezi manual; creezi noduri copil sub el.
Step 1: Create a Scene
import { Scene } from '@aspose/3d';
const scene = new Scene();
console.log(scene.rootNode.name); // '' (empty string — the root node is created with no name)
Un nou Scene începe cu un nod rădăcină gol și fără clipuri de animație. Construiești conținutul prin atașarea nodurilor copil.
Pasul 2: Adăugați noduri copil
Folosește createChildNode() pentru a crește arborele. Metoda returnează noul Node, astfel încât să poți înlănțui apeluri suplimentare de la orice nivel:
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)
Numele nodurilor sunt șiruri arbitrare. Numele nu trebuie să fie unice, dar utilizarea unor nume semnificative face codul de traversare mai ușor de depanat.
Pasul 3: Creați un Mesh și setați vârfurile
Mesh este clasa principală de geometrie. Adaugă pozițiile vârfurilor prin împingere Vector4 valori în mesh.controlPoints, apoi apelează createPolygon() pentru a defini fețele prin indicele vârfului:
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 folosește coordonate omogene: componenta w componentă este 1 pentru poziții și 0 pentru vectorii de direcție.
Step 4: Set Node Transforms
Fiecare nod are un transform proprietate cu translation, rotation, și scaling. Setaţi translation pentru a muta nodul relativ la părintele său:
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 furnizează matricea de transformare în spațiul mondial (doar citire), calculată prin concatenarea tuturor transformărilor strămoșilor.
Pasul 5: Parcurge arborele
Scrie o funcție recursivă pentru a vizita fiecare nod. Verifică node.entity și node.childNodes la fiecare nivel:
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);Pentru ierarhia creată mai sus, rezultatul va fi:
[none]
parent [none]
child [Mesh]Numele nodului rădăcină este un șir gol deoarece Scene îl creează fără argument de nume.
Întotdeauna protejează accesul la entitate cu o verificare null înainte de a face cast la un tip specific. Nu fiecare nod conține o entitate.
Pasul 6: Salvează în glTF sau GLB
Folosește GltfSaveOptions pentru a controla formatul de ieșire. Setează binaryMode = true pentru a produce un singur autocontinut .glb fișier; lăsați-l false pentru JSON .gltf + .bin pereche sidecar:
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');Transmite GltfFormat.getInstance() ca argument de format, astfel încât biblioteca să folosească codificatorul corect, indiferent de extensia fișierului.
Sfaturi și bune practici
- Folosește
createChildNode()în loc de a construiNodedirect:createChildNode()conectează automat relația părinte-copil și înregistrează nodul în arbore. - Verifică
node.entityînainte de a accesa proprietățile entității: multe noduri (noduri de grup, oase, localizatoare) nu au nicio entitate. Întotdeauna protejați cu o verificare null sauinstanceoftest. - Setați
translationpe nodurile copil, nu pe vârfurile mesh-ului: modificareatransform.translationeste nedistructivă și compozabilă cu transformările părintelui. - Preferă
binaryMode = truepentru GLB: un singur.glbfișier este mai ușor de distribuit, încărcat în browsere și importat în motoarele de jocuri decât cel împărțit.gltf+.binformat. - Parcurge prin
for...ofpestechildNodes: evita indexarea numerică; folosește iterabilul direct pentru compatibilitate viitoare.
Probleme comune
| Simptom | Cauză Probabilă | Remediere |
|---|---|---|
child.entity = mesh nu are efect asupra exportului | Entitatea atribuită la nivelul greșit al nodului | Atribuie entity la nodul frunză, nu la un nod grup |
node.entity este întotdeauna null | Doar verificare rootNode în sine | Recursiv în node.childNodes; rootNode de obicei nu are entitate |
| Transformarea nu este reflectată în vizualizatorul GLB | globalTransform nu este actualizat | globalTransform este calculat la salvare; set transform.translation înainte de a apela scene.save() |
GLB produce un separat .bin sidecar | binaryMode implicit false | Setare saveOpts.binaryMode = true |
Vezi și
- Funcționalități și caracteristici: referință completă API pentru toate domeniile de funcționalitate.
- Suport pentru formate: formate 3D suportate, capacitate de citire/scriere și opțiuni de format.
- Cum să construiești un Mesh 3D programatic