Cómo hacer un gráfico de escenario 3D en TypeScript

Cómo hacer un gráfico de escenario 3D en TypeScript

El gráfico de escena en Aspose.3D FOSS para TypeScript es un árbol de Node Objetos enracinados en scene.rootNode.El paso es recursivo: cada nodo exponerá a una childNodes iterable y una opción entity Esta guía muestra cómo caminar por todo el árbol, identificar tipos de entidades y recoger estadísticas de mesh.

Prediciones

  • Node.js 18 o más tarde
  • Tipo de gráficos 5.0 o más tarde
  • @aspose/3d Instalado

Guía paso a paso

Paso 1: Instalar y importar

Instalar el paquete:

npm install @aspose/3d

Importa las clases utilizadas en esta guía:

import { Scene } from '@aspose/3d';
import { ObjLoadOptions } from '@aspose/3d/formats/obj';
import { Mesh } from '@aspose/3d/entities';

Scene y Mesh Son las clases principales. ObjLoadOptions se utiliza en el ejemplo de carga; sustituye la clase de opciones de ajuste por otros formatos.


Paso 2: Cargar una escena de un archivo

Creación a Scene y llamar scene.open() La detección de formato es automática desde los números mágicos binarios, por lo que no necesita especificar el formato para los archivos GLB, STL o 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}`);

También puedes descargar de una Buffer En memoria, utilizando scene.openFromBuffer(buffer, options);· útil en los tubos sin servidor donde el disco I/O no está disponible.


Paso 3: Escribe una función recursiva de viaje

Recursión sobre childNodes La función visita cada nodo de profundidad primero:

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 una escena con un mesh llamado Cube,La producción se verá como:

[-] 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 Es el null para los nodos de grupo, huesos y locadores. el constructor.name Verificar las obras para cualquier tipo de entidad: Mesh, Camera, Light,etc.


Paso 4: Acceso al tipo de entidad en cada nodo

Para tomar acciones basadas en el tipo de entidad, utilice un instanceof Consulte el control de la guardia nula:

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 es la forma más segura de confirmar que la entidad es una mezcla poligonal antes de acceder a controlPoints, polygonCount,El elemento vertex.


Paso 5: Filtrar los nodos por tipo de entidad

Para recoger sólo los nodos de mezcla sin imprimir el árbol completo, use un 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)`);

La función acepta una opción opcional. results array para que los llamadores puedan pre-popularlo para la fusión de resultados a través de múltiples substratos.


Paso 6: Recoger todas las mesas y imprimir cuentas vertex

Extendir el colector para imprimir estadí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`);
}

Exemplos de salida para una escena de dos mesas:

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>

Consejos y mejores prácticas

  • Siempre no-check node.entity Muchos nodos son nodas de grupo puros que no llevan ninguna entidad.
  • Uso instanceof Sobre constructor.name para los controles de tipo en caminos lógicos. instanceof es refactor-seguro; la comparación de las líneas en el constructor.name Se rompe con la minificación.
  • Viaje a través for...of Sobre childNodes: el iterable maneja todos los tamaños de la línea de forma segura. Evite la indicación numérica para compatibilidad a frente.
  • Evitar mutiendo el árbol durante la cruzada:No añade o elimine los núcleos dentro de la llamada recursiva.Recoger resultados primero, luego modificar.
  • Pasar un resultado como parámetro:Esto evita la asignación de una nueva línea en cada llamada recursiva y hace que sea fácil combinar los resultados subterráneos.

Problemas comunes

síntomasCausasFixado
childNodes tiene una longitud cero rootNodeModelo no cargadoSeguridad scene.open() Completado sin error antes de cruzar
node.entity instanceof Mesh Nunca verdaderoequivocado Mesh El camino de importaciónImportación Mesh de @aspose/3d/entities,No de @aspose/3d Rojo
El viaje perde las mesasNo recurrir a todos los niñosAsegúrese de que la llamada recursiva cubre cada elemento en el node.childNodes
mesh.controlPoints.length Es 0El césped cargado pero no contiene geometríaVerificar la fuente OBJ para grupos vacíos; usar mesh.polygonCount Como un control secundario
El paso de las hierarquías profundasÁrbol de escena muy profunda (cientos de niveles)Substituir la recursión con un estaco explícito utilizando Array.push / Array.pop

Preguntas frecuentes

¿Qué hace scene.rootNode ¿Tiene una entidad? No. El nodo de raíz es un contenedor creado automáticamente por la biblioteca. no tiene ninguna entidad. su geometría y otros objetos de escena viven en los nodos infantiles uno o más niveles debajo rootNode.

¿Cuál es la diferencia entre node.entity y node.entities? node.entity Algunas ficheras de FBX y COLLADA mayores pueden producir nódulos con múltiples entidades adheridas; en ese caso, node.entities (plural) proporciona la lista completa.

¿Puedo cruzar en orden de ancho y no en primer lugar? Use una cueva en lugar de una llamada recursiva: pulse scene.rootNode en una array, luego cambiar y procesar los nodos mientras empujando cada nodo de la childNodes En el cuello de la caña.

Es scene.open() ¿Síntomas en sí? Sí sí. scene.open() y scene.openFromBuffer() ambos bloquean el hilo de llamada hasta que el archivo esté completamente parsado. envuelvelos en un hilo del trabajador si necesita mantener la barra de eventos respondiendo.

¿Cómo puedo obtener posiciones del espacio mundial desde un nodo? Leer node.globalTransform;que devolve una lectura única GlobalTransform con la matriz del espacio, compuesta de todas las transformaciones ancestrales. para matemáticas explícitas, llame node.evaluateGlobalTransform(false).

¿Qué tipos de entidades son posibles además de Mesh? Camera, Light, y las entidades esqueletas/bones personalizadas. ver node.entity.constructor.name o usar instanceof de la clase importada por los @aspose/3d.

Ver también

 Español