Lavorare con il grafo della scena
Tutto il contenuto 3D in Aspose.3D FOSS per TypeScript si trova all’interno di un Scene oggetto organizzato come un albero di Node oggetti. Comprendere questa gerarchia è la base per costruire, caricare e processare qualsiasi file 3D.
Installazione
Installa il pacchetto da npm prima di eseguire qualsiasi codice in questa guida:
npm install @aspose/3dAssicurati che il tuo tsconfig.json include "module": "commonjs" e "moduleResolution": "node" per una corretta risoluzione degli import di sotto-percorso.
Concetti del grafo della scena
Il grafo della scena ha tre livelli:
| Livello | Classe | Ruolo |
|---|---|---|
| Scena | Scene | Contenitore di livello superiore. Contiene rootNode, animationClips, e assetInfo. |
| Nodo | Node | Nodo dell’albero con nome. Può avere nodi figli, un’entità, una trasformazione e materiali. |
| Entità | Mesh, Camera, Light, … | Contenuto collegato a un nodo. Un nodo trasporta al massimo un’entità. |
La catena di ereditarietà per i blocchi principali è:
A3DObject
└─ SceneObject
├─ Node (tree structure)
└─ Entity
└─ Geometry
└─ Mesh (polygon geometry)scene.rootNode viene creato automaticamente. Non lo crei manualmente; crei nodi figlio sotto di esso.
Passo 1: Crea una scena
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 nuovo Scene inizia con un nodo radice vuoto e nessuna clip di animazione. Costruisci il contenuto collegando nodi figlio.
Passo 2: Aggiungere nodi figli
Usa createChildNode() per far crescere l’albero. Il metodo restituisce il nuovo Node, così puoi concatenare ulteriori chiamate da qualsiasi livello:
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)
I nomi dei nodi sono stringhe arbitrarie. I nomi non devono essere unici, ma usare nomi significativi rende più facile il debug del codice di traversata.
Passo 3: Creare una Mesh e impostare i vertici
Mesh è la classe geometrica primaria. Aggiungi le posizioni dei vertici spingendo Vector4 valori in mesh.controlPoints, quindi chiama createPolygon() per definire le facce tramite indice dei vertici:
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 utilizza coordinate omogenee: il w componente è 1 per le posizioni e 0 per i vettori direzionali.
Passo 4: Imposta le trasformazioni dei nodi
Ogni nodo ha un transform proprietà con translation, rotation, e scaling. Imposta translation per spostare il nodo rispetto al suo genitore:
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 fornisce la matrice di trasformazione nello spazio mondo (sola lettura), calcolata concatenando tutte le trasformazioni dei genitori.
Passo 5: Attraversa l’albero
Scrivi una funzione ricorsiva per visitare ogni nodo. Verifica node.entity e node.childNodes a ogni livello:
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);Per la gerarchia creata sopra, l’output sarà:
[none]
parent [none]
child [Mesh]Il nome del nodo radice è una stringa vuota perché Scene lo crea senza argomento name.
Proteggi sempre l’accesso all’entità con un controllo null prima di effettuare il cast a un tipo specifico. Non tutti i nodi contengono un’entità.
Passo 6: Salva in glTF o GLB
Usa GltfSaveOptions per controllare il formato di output. Imposta binaryMode = true per produrre un unico file autonomo .glb file; lascialo false per il JSON .gltf + .bin coppia 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');Passa GltfFormat.getInstance() come argomento format affinché la libreria utilizzi il codificatore corretto indipendentemente dall’estensione del file.
Suggerimenti e migliori pratiche
- Usa
createChildNode()invece di costruireNodedirettamente:createChildNode()collega automaticamente la relazione genitore-figlio e registra il nodo nell’albero. - Verifica
node.entityprima di accedere alle proprietà dell’entità: molti nodi (nodi di gruppo, ossa, localizzatori) non hanno alcuna entità. Controlla sempre con un controllo null oinstanceoftest. - Imposta
translationsui nodi figlio, non sui vertici della mesh: modificatransform.translationè non distruttivo e composabile con le trasformazioni genitore. - Preferisci
binaryMode = trueper GLB: un singolo.glbil file è più facile da distribuire, caricare nei browser e importare nei motori di gioco rispetto al diviso.gltf+.binformato. - Attraversa tramite
for...ofsuchildNodes: evita l’indicizzazione numerica; usa direttamente l’iterabile per la compatibilità futura.
Problemi comuni
| Sintomo | Probabile causa | Correzione |
|---|---|---|
child.entity = mesh non ha alcun effetto sull’esportazione | Entità assegnata al livello di nodo errato | Assegna entity al nodo foglia, non a un nodo gruppo |
node.entity è sempre null | Solo verifica rootNode se stesso | Ricorsione in node.childNodes; rootNode tipicamente non ha entità |
| Trasformazione non riflessa nel visualizzatore GLB | globalTransform non aggiornato | globalTransform viene calcolato al salvataggio; impostare transform.translation prima di chiamare scene.save() |
GLB produce un separato .bin sidecar | binaryMode predefinito a false | Imposta saveOpts.binaryMode = true |
Vedi anche
- Caratteristiche e funzionalità: riferimento API completo per tutte le aree funzionali.
- Supporto dei formati: formati 3D supportati, capacità di lettura/scrittura e opzioni di formato.
- Come costruire una mesh 3D programmaticamente