Como fazer um gráfico de cena 3D no TypeScript
O gráfico de cena em Aspose.3D FOSS para TypeScript é uma árvore de Node Objetos que se enraizam em scene.rootNode.A travessal é recursiva: cada nodo expõe um childNodes Impressão e opcional entity Este guia mostra como andar a toda a árvore, identificar tipos de entidades e recolher estatísticas de mesh.
Precondições
- Node.js 18 ou mais tarde
- Tipo de texto 5.0 ou posterior
@aspose/3dInstalado
Guia passo a passo
Passo 1: Instalação e importação
Instalando o pacote:
npm install @aspose/3dImportar as classes utilizadas neste guia:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';Scene e Mesh São as classes principais. ObjLoadOptions é usado no exemplo de carga; substitui a classe de opções de correspondência para outros formatos.
Passo 2: Carregar uma cena de um arquivo
Criar a Scene e chamando scene.open() Detecção de formato é automaticamente a partir dos números mágicos binários, então você não precisa especificar o formato para arquivos GLB, STL ou 3MF:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
const scene = new Scene();
scene.open('model.obj', new ObjLoadOptions());
console.log(`Root node: "${scene.rootNode.name}"`);
console.log(`Top-level children: ${scene.rootNode.childNodes.length}`);Você também pode carregar a partir de um Buffer Em memória, usando scene.openFromBuffer(buffer, options);Utilidade em tubos sem servidor onde o disco I/O não está disponível.
Passo 3: Escreva uma função de viagem recursiva
Recurso sobre childNodes A função visita cada nodo em profundidade primeiro:
function traverse(node: any, depth = 0): void {
const indent = ' '.repeat(depth);
const entityType = node.entity ? node.entity.constructor.name : '-';
console.log(`${indent}[${entityType}] ${node.name}`);
for (const child of node.childNodes) {
traverse(child, depth + 1);
}
}
traverse(scene.rootNode);Para uma cena com um mesh chamado Cube,A produção vai parecer:
[-] RootNode
[Mesh] Cube<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Copy code”
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
node.entity É null para os nodos de grupo, ossos e locadores. o constructor.name Verificar as atividades para qualquer tipo de entidade: Mesh, Camera, Light,e assim por diante.
Passo 4: Acesso ao tipo de entidade em cada nodo
Para agir com base no tipo de entidade, use um instanceof Verifique após o zero guard:
import { Mesh } from '@aspose/3d/entities';
function visitWithTypeCheck(node: any, depth = 0): void {
const indent = ' '.repeat(depth);
if (node.entity instanceof Mesh) {
const mesh = node.entity as Mesh;
console.log(`${indent}MESH "${node.name}": ${mesh.controlPoints.length} vertices`);
} else if (node.entity) {
console.log(`${indent}${node.entity.constructor.name} "${node.name}"`);
} else {
console.log(`${indent}GROUP "${node.name}"`);
}
for (const child of node.childNodes) {
visitWithTypeCheck(child, depth + 1);
}
}
visitWithTypeCheck(scene.rootNode);instanceof Mesh É a maneira mais segura de confirmar que a entidade é uma mesha poligônica antes de acessar controlPoints, polygonCount,ou elementos verticais.
Passo 5: Filtrar Nodes por Tipo de Entidade
Para recolher apenas nodos de mesh-baixando sem imprimir a árvore inteira, use um acumulador recursivo:
import { Mesh } from '@aspose/3d/entities';
function collectMeshes(
node: any,
results: Array<{ name: string; mesh: Mesh }> = []
): Array<{ name: string; mesh: Mesh }> {
if (node.entity instanceof Mesh) {
results.push({ name: node.name, mesh: node.entity as Mesh });
}
for (const child of node.childNodes) {
collectMeshes(child, results);
}
return results;
}
const meshNodes = collectMeshes(scene.rootNode);
console.log(`Found ${meshNodes.length} mesh node(s)`);A função aceita uma opção results array para que os chamadores possam pre-popular para obter resultados de fusão em várias substradas.
Passo 6: Coleta todas as mesas e imprime contas vertex
Extendendo o coletor para imprimir estatísticas per-mesh:
import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';
function collectMeshes(node: any, results: Array<{name: string, mesh: Mesh}> = []) {
if (node.entity instanceof Mesh) {
results.push({ name: node.name, mesh: node.entity as Mesh });
}
for (const child of node.childNodes) {
collectMeshes(child, results);
}
return results;
}
const scene = new Scene();
scene.open('model.obj', new ObjLoadOptions());
const meshes = collectMeshes(scene.rootNode);
for (const { name, mesh } of meshes) {
console.log(`${name}: ${mesh.controlPoints.length} vertices, ${mesh.polygonCount} polygons`);
}Exemplo de uma cena de dois meses:
Cube: 8 vertices, 6 polygons
Sphere: 482 vertices, 480 polygons<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Copy code”
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
Dicas e Melhores Práticas
- Sempre com zero check
node.entityAntes de acessar propriedades específicas da entidade. muitos nodos são nodas puros de grupo que não transportam uma entidade . - Usando
instanceofSobre oconstructor.namepara os tipos de verificação em caminhos lógicos.instanceofé refactor-seguro; corrente comparativa emconstructor.nameA partir da minificação. - Passando por via
for...ofSobre ochildNodes: o iterável gerencia todos os tamanhos de array com segurança. Evite indicação numérica para compatibilidade à frente. - Evite mover a árvore durante o cruzeiro:Não adicione ou remova nódulos dentro da chamada recursiva. recolha os resultados primeiro, depois modifique.
- Passar um resultado como parâmetro:Isso evita a atribuição de uma nova linha em cada chamada recursiva e torna fácil combinar resultados subterrâneos.
Problemas comuns
| Sintomas | Causa | Fixar |
|---|---|---|
childNodes Tem uma duração zero em rootNode | Modelo não carregado | Seguro scene.open() Completado sem erro antes de atravessar |
node.entity instanceof Mesh Nunca verdadeira | Errado Mesh Caminho de importação | Importação Mesh de @aspose/3d/entities,Não de @aspose/3d Raiz |
| A viagem perde as mesas | Não recorrer a todos os filhos | Assegure-se que a chamada recursiva cobre todos os elementos em seu node.childNodes |
mesh.controlPoints.length É 0 | Mesh carregado mas não contém geometria | Verifique a fonte OBJ para grupos vazios; use mesh.polygonCount Como um cheque secundário |
| Avaliação de profundas hierarquias | Árvore de cena muito profunda (centos de níveis) | Substituir a recursão com um estágio explícito usando Array.push / Array.pop |
Perguntas frequentes
O que faz scene.rootNode Você tem uma entidade? No. O nodo de raiz é um recipiente criado automaticamente pela biblioteca. não tem entidade. sua geometria e outros objetos da cena vivem em uma ou mais níveis abaixo dos nodos infantis. rootNode.
Qual a diferença entre node.entity e node.entities? node.entity Alguns arquivos FBX e COLLADA mais antigos podem produzir nódulos com várias entidades anexadas; neste caso, node.entities (plural) fornece a lista completa.
Posso atravessar em ordem de largura-primeiro, em vez de profundidade-primente? Use uma quebra em vez de um chamado recursivo: pressione scene.rootNode em um array, então mude e processar nódulos enquanto pressionando cada nodo de childNodes para o queue tail.
É scene.open() E sim sincronizado? Sim sim. scene.open() e scene.openFromBuffer() ambos bloquear o fio de chamada até que o arquivo seja completamente parsed. Enrolá-los em um fijo de trabalhadores se você precisar manter o loop do evento respondiço.
Como posso obter posições espaciais mundiais de um nodo? Leia node.globalTransform;Ele retorna um leitor-somente GlobalTransform com a matriz do espaço-mundo, composto de todas as transformações ancestrais. para matemática explicita, chama node.evaluateGlobalTransform(false).
Quais tipos de entidades são possíveis além Mesh? Camera, Light, e as entidades esqueleto/bone personalizadas. verificar node.entity.constructor.name ou usar instanceof com a classe específica importada de @aspose/3d.