Trabajando con el Grafo de Escena
Todo el contenido 3D en Aspose.3D FOSS para TypeScript se encuentra dentro de un Scene objeto organizado como un árbol de Node objetos. Comprender esta jerarquía es la base para crear, cargar y procesar cualquier archivo 3D.
Installation
Instala el paquete desde npm antes de ejecutar cualquier código de esta guía:
npm install @aspose/3dAsegúrate de que tu tsconfig.json incluya "module": "commonjs" y "moduleResolution": "node" para una correcta resolución de importación de subrutas.
Conceptos del Grafo de Escena
El grafo de escena tiene tres niveles:
| Nivel | Clase | Rol |
|---|---|---|
| Escena | Scene | Contenedor de nivel superior. Contiene rootNode, animationClips, y assetInfo. |
| Nodo | Node | Nodo de árbol con nombre. Puede tener nodos hijos, una entidad, una transformación y materiales. |
| Entidad | Mesh, Camera, Light, … | Contenido adjunto a un nodo. Un nodo lleva como máximo una entidad. |
La cadena de herencia para los bloques de construcción principales es:
A3DObject
└─ SceneObject
├─ Node (tree structure)
└─ Entity
└─ Geometry
└─ Mesh (polygon geometry)scene.rootNode se crea automáticamente. No lo creas manualmente; creas nodos hijos bajo él.
Paso 1: Crear una Escena
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 nuevo Scene comienza con un nodo raíz vacío y sin clips de animación. Construyes contenido adjuntando nodos hijos.
Paso 2: Añadir Nodos Hijos
Usa createChildNode() para expandir el árbol. El método devuelve el nuevo Node, así que puedes encadenar llamadas adicionales desde cualquier 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)
Los nombres de los nodos son cadenas arbitrarias. No es necesario que los nombres sean únicos, pero usar nombres significativos facilita la depuración del código de recorrido.
Paso 3: Crear un Mesh y Definir Vértices
Mesh es la clase principal de geometría. Añade posiciones de vértices empujando Vector4 valores en mesh.controlPoints, luego llama a createPolygon() para definir caras por índice de vértice:
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 utiliza coordenadas homogéneas: el w componente es 1 para posiciones y 0 para vectores de dirección.
Paso 4: Configurar transformaciones de nodo
Cada nodo tiene un transform propiedad con translation, rotation, y scaling. Establecer translation para mover el nodo relativo a su padre:
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 proporciona la matriz de transformación en espacio mundial (solo lectura), calculada concatenando todas las transformaciones de los ancestros.
Paso 5: Recorrer el árbol
Escribe una función recursiva para visitar cada nodo. Comprueba node.entity y node.childNodes en cada 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);Para la jerarquía creada arriba, la salida será:
[none]
parent [none]
child [Mesh]El nombre del nodo raíz es una cadena vacía porque Scene lo crea sin argumento de nombre.
Siempre protege el acceso a la entidad con una verificación de null antes de convertir a un tipo específico. No todos los nodos llevan una entidad.
Paso 6: Guardar a glTF o GLB
Usa GltfSaveOptions para controlar el formato de salida. Establece binaryMode = true para producir un único auto‑contenedor .glb archivo; déjalo false para el JSON .gltf + .bin par 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');Pasa GltfFormat.getInstance() como argumento de formato para que la biblioteca use el codificador correcto sin importar la extensión del archivo.
Consejos y mejores prácticas
- Usa
createChildNode()en lugar de construirNodedirectamente:createChildNode()conecta automáticamente la relación padre‑hijo y registra el nodo en el árbol. - Verifica
node.entityantes de acceder a las propiedades de la entidad: muchos nodos (nodos de grupo, huesos, localizadores) no llevan entidad. Siempre protege con una null check oinstanceofprueba. - Establecer
translationen nodos hijos, no en vértices de malla: modificandotransform.translationes no destructivo y composable con las transformaciones del padre. - Preferir
binaryMode = truepara GLB: un único.glbarchivo es más fácil de distribuir, cargar en navegadores e importar a motores de juego que el dividido.gltf+.binformato. - Recorrer vía
for...ofsobrechildNodes: evite la indexación numérica; use el iterable directamente para compatibilidad futura.
Problemas comunes
| Síntoma | Causa probable | Solución |
|---|---|---|
child.entity = mesh no tiene efecto en la exportación | Entidad asignada al nivel de nodo incorrecto | Asignar entity al nodo hoja, no a un nodo de grupo |
node.entity siempre es null | Solo comprobando rootNode sí mismo | Recursar en node.childNodes; rootNode normalmente no tiene entidad |
| Transformación no reflejada en el visor GLB | globalTransform no actualizado | globalTransform se calcula al guardar; establecer transform.translation antes de llamar scene.save() |
GLB produce un archivo separado .bin sidecar | binaryMode por defecto es false | Establecer saveOpts.binaryMode = true |
Ver también
- Características y Funcionalidades: referencia completa de la API para todas las áreas de características.
- Compatibilidad de Formatos: formatos 3D compatibles, capacidad de lectura/escritura y opciones de formato.
- Cómo crear un Mesh 3D programáticamente