Travailler avec le graphe de scène

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

Assurez-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 :

NiveauClasseRôle
ScèneSceneConteneur de niveau supérieur. Contient rootNode, animationClips, et assetInfo.
NœudNodeNœ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 construire Node directement: createChildNode() établit automatiquement la relation parent‑enfant et enregistre le nœud dans l’arbre.
  • Vérifiez node.entity avant 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 ou instanceof test.
  • Définir translation sur les nœuds enfants, pas sur les sommets du maillage: modification transform.translation est non destructif et composable avec les transformations parentes.
  • Préférez binaryMode = true pour GLB: un seul .glb fichier est plus facile à distribuer, à charger dans les navigateurs et à importer dans les moteurs de jeu que le fichier fractionné .gltf + .bin format.
  • Parcourir via for...of sur childNodes: éviter l’indexation numérique ; utilisez l’itérable directement pour une compatibilité future.

Problèmes courants

SymptômeCause probableCorrection
child.entity = mesh n’a aucun effet sur l’exportationEntité assignée au mauvais niveau de nœudAttribuer entity au nœud feuille, pas à un nœud groupe
node.entity est toujours nullVérification uniquement rootNode lui‑mêmeRécursivité dans node.childNodes; rootNode n’a généralement aucune entité
Transformation non reflétée dans le visualiseur GLBglobalTransform non mis à jourglobalTransform est calculé lors de l’enregistrement ; définir transform.translation avant d’appeler scene.save()
GLB produit un fichier séparé .bin sidecarbinaryMode par défaut falseDéfinir saveOpts.binaryMode = true

Voir aussi

 Français