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/3dInstalado
Guía paso a paso
Paso 1: Instalar y importar
Instalar el paquete:
npm install @aspose/3dImporta 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.entityMuchos nodos son nodas de grupo puros que no llevan ninguna entidad. - Uso
instanceofSobreconstructor.namepara los controles de tipo en caminos lógicos.instanceofes refactor-seguro; la comparación de las líneas en elconstructor.nameSe rompe con la minificación. - Viaje a través
for...ofSobrechildNodes: 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íntomas | Causas | Fixado |
|---|---|---|
childNodes tiene una longitud cero rootNode | Modelo no cargado | Seguridad scene.open() Completado sin error antes de cruzar |
node.entity instanceof Mesh Nunca verdadero | equivocado Mesh El camino de importación | Importación Mesh de @aspose/3d/entities,No de @aspose/3d Rojo |
| El viaje perde las mesas | No recurrir a todos los niños | Asegúrese de que la llamada recursiva cubre cada elemento en el node.childNodes |
mesh.controlPoints.length Es 0 | El césped cargado pero no contiene geometría | Verificar 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.