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

Asiguraț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:

NivelClasăRol
ScenăSceneContainer de nivel superior. Conține rootNode, animationClips, și assetInfo.
NodNodeNod de arbore denumit. Poate avea noduri copil, o entitate, o transformare și materiale.
EntitateMesh, 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 construi Node direct: 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 sau instanceof test.
  • Setați translation pe nodurile copil, nu pe vârfurile mesh-ului: modificarea transform.translation este nedistructivă și compozabilă cu transformările părintelui.
  • Preferă binaryMode = true pentru GLB: un singur .glb fișier este mai ușor de distribuit, încărcat în browsere și importat în motoarele de jocuri decât cel împărțit .gltf + .bin format.
  • Parcurge prin for...of peste childNodes: evita indexarea numerică; folosește iterabilul direct pentru compatibilitate viitoare.

Probleme comune

SimptomCauză ProbabilăRemediere
child.entity = mesh nu are efect asupra exportuluiEntitatea atribuită la nivelul greșit al noduluiAtribuie entity la nodul frunză, nu la un nod grup
node.entity este întotdeauna nullDoar verificare rootNode în sineRecursiv în node.childNodes; rootNode de obicei nu are entitate
Transformarea nu este reflectată în vizualizatorul GLBglobalTransform nu este actualizatglobalTransform este calculat la salvare; set transform.translation înainte de a apela scene.save()
GLB produce un separat .bin sidecarbinaryMode implicit falseSetare saveOpts.binaryMode = true

Vezi și

 Română