Travailler avec le graphe de scène
Tout le contenu 3D dans Aspose.3D FOSS pour TypeScript se trouve à l’intérieur d’un Scene objet organisé comme un arbre de Node objets. Comprendre cette hiérarchie est la base pour construire, charger et traiter tout fichier 3D.
Installation
Installez le paquet depuis npm avant d’exécuter le code de ce guide :
npm install @aspose/3dAssurez-vous que votre tsconfig.json inclut "module": "commonjs" et "moduleResolution": "node" pour une résolution correcte des importations de sous‑chemins.
Concepts du graphe de scène
Le graphe de scène comporte trois niveaux :
| Niveau | Classe | Rôle |
|---|---|---|
| Scène | Scene | Conteneur de niveau supérieur. Contient rootNode, animationClips, et assetInfo. |
| Nœud | Node | Nœud d’arbre nommé. Peut avoir des nœuds enfants, une entité, une transformation et des matériaux. |
| Entité | Mesh, Camera, Light, … | Contenu attaché à un nœud. Un nœud porte au maximum une entité. |
La chaîne d’héritage des principaux blocs de construction est :
A3DObject
└─ SceneObject
├─ Node (tree structure)
└─ Entity
└─ Geometry
└─ Mesh (polygon geometry)scene.rootNode est créé automatiquement. Vous ne le créez pas manuellement ; vous créez des nœuds enfants sous celui-ci.
Étape 1 : Créer une scène
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 nouveau Scene commence avec un nœud racine vide et aucune séquence d’animation. Vous construisez le contenu en attachant des nœuds enfants.
Étape 2 : Ajouter des nœuds enfants
Utilisez createChildNode() pour développer l’arbre. La méthode renvoie le nouveau Node, ainsi vous pouvez chaîner d’autres appels depuis n’importe quel niveau :
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)
Les noms de nœud sont des chaînes arbitraires. Les noms n’ont pas besoin d’être uniques, mais utiliser des noms significatifs facilite le débogage du code de traversée.
Étape 3 : Créer un maillage et définir les sommets
Mesh est la classe géométrique principale. Ajoutez les positions des sommets en poussant Vector4 des valeurs dans mesh.controlPoints, puis appelez createPolygon() pour définir les faces par indice de sommet :
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 utilise des coordonnées homogènes : le w composant est 1 pour les positions et 0 pour les vecteurs de direction.
Étape 4 : définir les transformations des nœuds
Chaque nœud possède un transform propriété avec translation, rotation, et scaling. Définir translation pour déplacer le nœud par rapport à son parent :
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 fournit la matrice de transformation en espace mondial (lecture seule), calculée en concaténant toutes les transformations des ancêtres.
Étape 5 : Parcourir l’arbre
Écrivez une fonction récursive pour visiter chaque nœud. Vérifiez node.entity et node.childNodes à chaque niveau :
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);Pour la hiérarchie créée ci‑dessus, la sortie sera :
[none]
parent [none]
child [Mesh]Le nom du nœud racine est une chaîne vide parce que Scene le crée sans argument de nom.
Toujours protéger l’accès à l’entité avec une vérification de null avant de la caster vers un type spécifique. Tous les nœuds ne portent pas d’entité.
Étape 6 : Enregistrer en glTF ou GLB
Utilisez GltfSaveOptions pour contrôler le format de sortie. Définir binaryMode = true pour produire un seul autonome .glb fichier ; laissez‑le false pour le JSON .gltf + .bin paire 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');Passer GltfFormat.getInstance() en tant qu’argument de format afin que la bibliothèque utilise l’encodeur correct quel que soit l’extension du fichier.
Conseils et bonnes pratiques
- Utilisez
createChildNode()au lieu de construireNodedirectement:createChildNode()établit automatiquement la relation parent‑enfant et enregistre le nœud dans l’arbre. - Vérifiez
node.entityavant d’accéder aux propriétés de l’entité: de nombreux nœuds (nœuds de groupe, os, localisateurs) ne possèdent aucune entité. Toujours protéger avec une vérification de null ouinstanceoftest. - Définir
translationsur les nœuds enfants, pas sur les sommets du maillage: modificationtransform.translationest non destructif et composable avec les transformations parentes. - Préférez
binaryMode = truepour GLB: un seul.glbfichier est plus facile à distribuer, à charger dans les navigateurs et à importer dans les moteurs de jeu que le fichier fractionné.gltf+.binformat. - Parcourir via
for...ofsurchildNodes: éviter l’indexation numérique ; utilisez l’itérable directement pour une compatibilité future.
Problèmes courants
| Symptôme | Cause probable | Correction |
|---|---|---|
child.entity = mesh n’a aucun effet sur l’exportation | Entité assignée au mauvais niveau de nœud | Attribuer entity au nœud feuille, pas à un nœud groupe |
node.entity est toujours null | Vérification uniquement rootNode lui‑même | Récursivité dans node.childNodes; rootNode n’a généralement aucune entité |
| Transformation non reflétée dans le visualiseur GLB | globalTransform non mis à jour | globalTransform est calculé lors de l’enregistrement ; définir transform.translation avant d’appeler scene.save() |
GLB produit un fichier séparé .bin sidecar | binaryMode par défaut false | Définir saveOpts.binaryMode = true |
Voir aussi
- Fonctionnalités et capacités: référence complète de l’API pour tous les domaines de fonctionnalités.
- Prise en charge des formats: formats 3D pris en charge, capacité de lecture/écriture et options de format.
- Comment créer un maillage 3D de manière programmatique