Como fazer um gráfico de cena 3D no TypeScript

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

Guia passo a passo

Passo 1: Instalação e importação

Instalando o pacote:

npm install @aspose/3d

Importar 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.entity Antes de acessar propriedades específicas da entidade. muitos nodos são nodas puros de grupo que não transportam uma entidade .
  • Usando instanceof Sobre o constructor.name para os tipos de verificação em caminhos lógicos. instanceof é refactor-seguro; corrente comparativa em constructor.name A partir da minificação.
  • Passando por via for...of Sobre o childNodes: 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

SintomasCausaFixar
childNodes Tem uma duração zero em rootNodeModelo não carregadoSeguro scene.open() Completado sem erro antes de atravessar
node.entity instanceof Mesh Nunca verdadeiraErrado Mesh Caminho de importaçãoImportação Mesh de @aspose/3d/entities,Não de @aspose/3d Raiz
A viagem perde as mesasNão recorrer a todos os filhosAssegure-se que a chamada recursiva cobre todos os elementos em seu node.childNodes
mesh.controlPoints.length É 0Mesh carregado mas não contém geometriaVerifique 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.

Veja também

 Português