Trabalhando com o Scene Graph
Todo o conteúdo 3D em Aspose.3D FOSS para TypeScript reside dentro de um Scene objeto organizado como uma árvore de Node objetos. Compreender essa hierarquia é a base para construir, carregar e processar qualquer arquivo 3D.
Instalação
Instale o pacote do npm antes de executar qualquer código deste guia:
npm install @aspose/3dGaranta que seu tsconfig.json inclui "module": "commonjs" e "moduleResolution": "node" para a resolução correta de importação de subcaminhos.
Conceitos de Scene Graph
O scene graph tem três níveis:
| Nível | Classe | Função |
|---|---|---|
| Cena | Scene | Contêiner de nível superior. Contém rootNode, animationClips, e assetInfo. |
| Nó | Node | Nó de árvore nomeado. Pode ter nós filhos, uma entidade, uma transformação e materiais. |
| Entidade | Mesh, Camera, Light, … | Conteúdo anexado a um nó. Um nó carrega no máximo uma entidade. |
A cadeia de herança dos principais blocos de construção é:
A3DObject
└─ SceneObject
├─ Node (tree structure)
└─ Entity
└─ Geometry
└─ Mesh (polygon geometry)scene.rootNode é criado automaticamente. Você não o cria manualmente; você cria nós filhos sob ele.
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)
Um novo Scene começa com um nó raiz vazio e sem clipes de animação. Você constrói o conteúdo anexando nós filhos.
Etapa 2: Adicionar nós filhos
Use createChildNode() para expandir a árvore. O método retorna o novo Node, assim você pode encadear chamadas adicionais a partir de qualquer nível:
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)
Os nomes dos nós são strings arbitrárias. Os nomes não precisam ser únicos, mas usar nomes significativos facilita a depuração do código de travessia.
Etapa 3: Criar um Mesh e definir vértices
Mesh é a classe de geometria principal. Adicione posições de vértices usando push Vector4 valores em mesh.controlPoints, então chame createPolygon() para definir faces 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 usa coordenadas homogêneas: o w componente é 1 para posições e 0 para vetores de direção.
Step 4: Set Node Transforms
Cada nó tem um transform propriedade com translation, rotation, e scaling. Defina translation para mover o nó em relação ao seu pai:
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 fornece a matriz de transformação no espaço mundial (somente leitura), calculada concatenando todas as transformações dos ancestrais.
Etapa 5: Percorrer a Árvore
Escreva uma função recursiva para visitar cada nó. Verifique node.entity e node.childNodes em cada nível:
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 a hierarquia criada acima, a saída será:
[none]
parent [none]
child [Mesh]O nome do nó raiz é uma string vazia porque Scene cria-o sem argumento de nome.
Sempre proteja o acesso à entidade com uma verificação de null antes de fazer cast para um tipo específico. Nem todo nó possui uma entidade.
Etapa 6: Salvar em glTF ou GLB
Use GltfSaveOptions para controlar o formato de saída. Defina binaryMode = true para produzir um único auto-contido .glb arquivo; deixe-o false para o 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');Passe GltfFormat.getInstance() como argumento de formato para que a biblioteca use o codificador correto independentemente da extensão do arquivo.
Dicas e Melhores Práticas
- Use
createChildNode()em vez de construirNodediretamente:createChildNode()conecta automaticamente a relação pai‑filho e registra o nó na árvore. - Verifique
node.entityantes de acessar as propriedades da entidade: muitos nós (nós de grupo, ossos, localizadores) não possuem entidade. Sempre proteja com uma verificação de null ouinstanceofteste. - Definir
translationem nós filhos, não nos vértices da malha: modificandotransform.translationnão é destrutivo e pode ser composto com as transformações dos pais. - Prefira
binaryMode = truepara GLB: um único.glbarquivo é mais fácil de distribuir, carregar em navegadores e importar em engines de jogo do que o dividido.gltf+.binformato. - Percorrer via
for...ofsobrechildNodes: evite indexação numérica; use o iterável diretamente para compatibilidade futura.
Problemas Comuns
| Sintoma | Causa Provável | Correção |
|---|---|---|
child.entity = mesh não tem efeito na exportação | Entidade atribuída ao nível de nó errado | Atribuir entity ao nó folha, não a um nó de grupo |
node.entity é sempre null | Apenas verificando rootNode ele mesmo | Recursar em node.childNodes; rootNode geralmente não tem entidade |
| Transform não refletido no visualizador GLB | globalTransform não atualizado | globalTransform é calculado ao salvar; definir transform.translation antes de chamar scene.save() |
GLB produz um separado .bin sidecar | binaryMode padrão é false | Definir saveOpts.binaryMode = true |
Veja Também
- Recursos e Funcionalidades: referência completa da API para todas as áreas de recursos.
- Suporte a Formatos: formatos 3D suportados, capacidade de leitura/gravação e opções de formato.
- Como Construir um Mesh 3D Programaticamente