Cómo crear una 3D Mesh programáticamente en TypeScript

Cómo crear una 3D Mesh programáticamente en TypeScript

Aspose.3D FOSS for TypeScript le permite crear geometría 3D completamente en código sin cargar ningún archivo. Define las posiciones de los vértices como puntos de control, especifica las caras de los polígonos por índice y adjunta elementos de vértice opcionales como normales, UVs o colores de vértice. El resultado puede guardarse en cualquier formato escribible: glTF, GLB, STL, FBX o COLLADA.

Requisitos

  • Node.js 18 o posterior
  • TypeScript 5.0 o posterior
  • @aspose/3d instalado (ver Paso 1)

Guía paso a paso

Paso 1: Instalar @aspose/3d

npm install @aspose/3d

No se requieren complementos nativos ni bibliotecas del sistema. El paquete incluye definiciones de tipos de TypeScript.

Mínimo tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true
  }
}

Paso 2: Crear una escena y un nodo

Un Scene es el contenedor de nivel superior. Toda la geometría debe estar adjunta a un Node dentro del árbol de escena:

import { Scene } from '@aspose/3d';

const scene = new Scene();
const node = scene.rootNode.createChildNode('triangle');

createChildNode(name) crea un nodo con nombre y lo enlaza como hijo del nodo actual. El objeto Node devuelto es donde adjuntarás la malla en el Paso 7.


Paso 3: Crear un objeto Mesh

Mesh contiene posiciones de vértices y definiciones de polígonos. Construye uno con un nombre opcional:

import { Mesh } from '@aspose/3d/entities';

const mesh = new Mesh('triangle');

La malla comienza vacía: sin vértices y sin caras. Los añades en los siguientes pasos.


Paso 4: Añadir puntos de control (vértices)

Los puntos de control son las posiciones de los vértices en el espacio local. Empuje los valores Vector4 a mesh.controlPoints. El cuarto componente (w) es 1 para las posiciones:

import { Vector4 } from '@aspose/3d/utilities';

mesh.controlPoints.push(new Vector4(0.0, 0.0, 0.0, 1.0)); // index 0
mesh.controlPoints.push(new Vector4(1.0, 0.0, 0.0, 1.0)); // index 1
mesh.controlPoints.push(new Vector4(0.5, 1.0, 0.0, 1.0)); // index 2

Usted hace referencia a estas posiciones mediante su índice basado en cero al definir caras de polígonos.


Paso 5: Crear caras de polígono

createPolygon() define una cara enumerando los índices de los vértices en orden. Tres índices forman un triángulo:

mesh.createPolygon(0, 1, 2);

También puedes definir quads (cuatro índices) o polígonos arbitrarios para los formatos que los admiten. Para glTF, la biblioteca triangulará automáticamente los quads y n-gons al exportar.


Paso 6: Añadir normales de vértice

Los normales mejoran la calidad del renderizado. Use mesh.createElement() para crear un VertexElementNormal, recopile los vectores normales en una matriz, luego llame a setData() para almacenarlos. El getter data devuelve una copia defensiva — agregar elementos a ella no tiene efecto. Use FVector3 (float de precisión simple) para los datos de normales, no Vector4.

import { VertexElementNormal } from '@aspose/3d/entities';
import { VertexElementType, MappingMode, ReferenceMode } from '@aspose/3d/entities';
import { FVector3 } from '@aspose/3d/utilities';

const normals = mesh.createElement(
    VertexElementType.NORMAL,
    MappingMode.CONTROL_POINT,
    ReferenceMode.DIRECT
) as VertexElementNormal;

// Build the normal array, then call setData() — do NOT push to normals.data
normals.setData([
    new FVector3(0, 0, 1), // normal for vertex 0 (pointing +Z)
    new FVector3(0, 0, 1), // normal for vertex 1
    new FVector3(0, 0, 1), // normal for vertex 2
]);

MappingMode.CONTROL_POINT significa una normal por vértice. ReferenceMode.DIRECT significa que la matriz de datos está indexada directamente por el índice del vértice del polígono.


Paso 7: Adjuntar la malla y guardar en glTF

Asigne la malla al nodo mediante node.entity, luego guarde la escena:

import { GltfSaveOptions, GltfFormat } from '@aspose/3d/formats/gltf';

node.entity = mesh;

const saveOpts = new GltfSaveOptions();
scene.save('triangle.gltf', GltfFormat.getInstance(), saveOpts);
console.log('Triangle mesh saved to triangle.gltf');

Para producir un único archivo .glb autocontenido en su lugar, establezca saveOpts.binaryMode = true y cambie la extensión del archivo de salida a .glb.

Ejemplo completo

El siguiente es el script completo que combina todos los pasos anteriores:

import { Scene } from '@aspose/3d';
import { Mesh, VertexElementNormal } from '@aspose/3d/entities';
import { VertexElementType, MappingMode, ReferenceMode } from '@aspose/3d/entities';
import { Vector4, FVector3 } from '@aspose/3d/utilities';
import { GltfSaveOptions, GltfFormat } from '@aspose/3d/formats/gltf';

const scene = new Scene();
const node = scene.rootNode.createChildNode('triangle');

const mesh = new Mesh('triangle');

mesh.controlPoints.push(new Vector4(0.0, 0.0, 0.0, 1.0));
mesh.controlPoints.push(new Vector4(1.0, 0.0, 0.0, 1.0));
mesh.controlPoints.push(new Vector4(0.5, 1.0, 0.0, 1.0));

mesh.createPolygon(0, 1, 2);

const normals = mesh.createElement(
    VertexElementType.NORMAL,
    MappingMode.CONTROL_POINT,
    ReferenceMode.DIRECT
) as VertexElementNormal;

// setData() is the correct API — normals.data returns a defensive copy; pushing to it has no effect
normals.setData([
    new FVector3(0, 0, 1),
    new FVector3(0, 0, 1),
    new FVector3(0, 0, 1),
]);

node.entity = mesh;

const saveOpts = new GltfSaveOptions();
scene.save('triangle.gltf', GltfFormat.getInstance(), saveOpts);
console.log('Triangle mesh saved to triangle.gltf');

Ejecutar con ts-node:

npx ts-node triangle.ts

Problemas comunes

IssueCauseFix
mesh.controlPoints.length es 0 después de pushMalla no referenciada por ningún nodoHacer push antes de asignar node.entity; el orden no importa, pero verifica la referencia
La exportación produce geometría vacíanode.entity no asignadoAsegúrate de node.entity = mesh antes de llamar a scene.save()
Desajuste en el recuento de normalesArreglo pasado a setData() más corto que controlPointsAñade una entrada FVector3 por punto de control al usar MappingMode.CONTROL_POINT
El visor glTF muestra malla negraNormales apuntando hacia adentroInvierte el orden de winding en createPolygon (p.ej., 0, 2, 1) o niega los vectores normales
TypeScript: propiedad ’normals.data’ no encontradaRuta de importación incorrectaImporta VertexElementNormal desde @aspose/3d/entities, no desde la raíz @aspose/3d

Preguntas frecuentes

¿Puedo crear quads en lugar de triángulos?
Sí. Pase cuatro índices a createPolygon(0, 1, 2, 3). La biblioteca triangula los quads durante la exportación a formatos que requieren triángulos (glTF, STL).

¿Cuál es la diferencia entre MappingMode.CONTROL_POINT y MappingMode.POLYGON_VERTEX?
CONTROL_POINT almacena un valor por vértice único. POLYGON_VERTEX almacena un valor por par polígono‑vértice, lo que permite normales diferentes en el mismo vértice cuando pertenece a varios polígonos (bordes duros).

¿Necesito triangular la malla antes de guardarla en STL?
No. La biblioteca maneja la triangulación automáticamente al exportar a formatos que requieren triángulos. Puedes definir cuádruples y n-gonos en la malla y guardarla directamente en STL.

¿Cómo añado coordenadas UV?
Utilice mesh.createElementUV(TextureMapping.Diffuse, MappingMode.CONTROL_POINT, ReferenceMode.DIRECT) para crear un VertexElementUV, luego llame a setData([...]) con una matriz de valores FVector2 o FVector3 — uno por punto de control. El getter data devuelve una copia; no lo empuje directamente.

¿Puedo crear varios meshes en una sola escena?
Sí. Crea varios nodos bajo scene.rootNode y asigna un Mesh separado a la propiedad entity de cada nodo.

Ver también

 Español