Como Construir uma Malha 3D Programaticamente em TypeScript
Aspose.3D FOSS for TypeScript permite que você construa geometria 3D inteiramente em código sem carregar nenhum arquivo. Você define posições de vértices como pontos de controle, especifica faces de polígonos por índice e anexa elementos de vértice opcionais, como normais, UVs ou cores de vértice. O resultado pode ser salvo em qualquer formato gravável: glTF, GLB, STL, FBX ou COLLADA.
Pré-requisitos
- Node.js 18 ou posterior
- TypeScript 5.0 ou posterior
@aspose/3dinstalado (veja a Etapa 1)
Guia passo a passo
Etapa 1: Instalar @aspose/3d
npm install @aspose/3dNenhum addon nativo ou biblioteca do sistema é necessário. O pacote inclui definições de tipo TypeScript.
Mínimo tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true
}
}Passo 2: Criar uma Cena e um Nó
Um Scene é o contêiner de nível superior. Toda a geometria deve ser anexada a um Node dentro da árvore de cena:
import { Scene } from '@aspose/3d';
const scene = new Scene();
const node = scene.rootNode.createChildNode('triangle');createChildNode(name) cria um nó nomeado e o conecta como filho do nó atual. O objeto Node retornado é onde você anexará a malha na Etapa 7.
Etapa 3: Criar um Objeto Mesh
Mesh contém posições de vértices e definições de polígonos. Construa um com um nome opcional:
import { Mesh } from '@aspose/3d/entities';
const mesh = new Mesh('triangle');A mesh começa vazia: sem vértices e sem faces. Você os adiciona nas próximas etapas.
Etapa 4: Adicionar Pontos de Controle (Vértices)
Pontos de controle são as posições dos vértices no espaço local. Envie os valores Vector4 para mesh.controlPoints. O quarto componente (w) é 1 para posições:
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
Você referencia essas posições pelo índice baseado em zero ao definir faces de polígonos.
Etapa 5: Criar Faces de Polígono
createPolygon() define uma face listando os índices dos vértices em ordem. Três índices formam um triângulo:
mesh.createPolygon(0, 1, 2);Você também pode definir quads (quatro índices) ou polígonos arbitrários para formatos que os suportam. Para glTF, a biblioteca triangulará automaticamente quads e n-gons na exportação.
Etapa 6: Adicionar Normais de Vértice
Normais melhoram a qualidade da renderização. Use mesh.createElement() para criar um VertexElementNormal, coletar vetores normais em um array, então chame setData() para armazená‑los. O getter data retorna uma cópia defensiva — adicionar a ele não tem efeito. Use FVector3 (float de precisão simples) para dados de normais, não 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 uma normal por vértice. ReferenceMode.DIRECT significa que o array de dados é indexado diretamente pelo índice do vértice do polígono.
Passo 7: Anexar a Malha e Salvar como glTF
Atribua a malha ao nó via node.entity, então salve a cena:
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 produzir um único arquivo .glb autocontido, defina saveOpts.binaryMode = true e altere a extensão do arquivo de saída para .glb.
Exemplo completo
O seguinte é o script completo combinando todas as etapas acima:
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');Execute com ts-node:
npx ts-node triangle.tsProblemas Comuns
| Issue | Cause | Fix |
|---|---|---|
mesh.controlPoints.length é 0 após o push | Malha não referenciada por nenhum nó | Faça o push antes de atribuir node.entity; a ordem não importa, mas verifique a referência |
| Exportação produz geometria vazia | node.entity não atribuído | Garanta node.entity = mesh antes de chamar scene.save() |
| Incompatibilidade na contagem de normais | Array passado para setData() é mais curto que controlPoints | Adicione uma entrada FVector3 por ponto de controle ao usar MappingMode.CONTROL_POINT |
| Visualizador glTF mostra malha preta | Normais apontando para dentro | Inverta a ordem de winding em createPolygon (por exemplo, 0, 2, 1) ou negue os vetores normais |
| TypeScript: propriedade ’normals.data’ não encontrada | Caminho de importação errado | Importe VertexElementNormal de @aspose/3d/entities, não da raiz @aspose/3d |
Perguntas Frequentes
Posso criar quads em vez de triângulos?
Sim. Passe quatro índices para createPolygon(0, 1, 2, 3). A biblioteca triangula quads durante a exportação para formatos que exigem triângulos (glTF, STL).
Qual é a diferença entre MappingMode.CONTROL_POINT e MappingMode.POLYGON_VERTEX?CONTROL_POINT armazena um valor por vértice único. POLYGON_VERTEX armazena um valor por par polígono‑vértice, o que permite normais diferentes no mesmo vértice quando ele pertence a múltiplos polígonos (bordas duras).
Preciso triangular a malha antes de salvar em STL?
Não. A biblioteca lida com a triangulação automaticamente ao exportar para formatos que exigem triângulos. Você pode definir quads e n‑gons na malha e salvar diretamente em STL.
Como adiciono coordenadas UV?
Use mesh.createElementUV(TextureMapping.Diffuse, MappingMode.CONTROL_POINT, ReferenceMode.DIRECT) para criar um VertexElementUV, então chame setData([...]) com um array de valores FVector2 ou FVector3 — um por ponto de controle. O getter data retorna uma cópia; não faça push diretamente nele.
Posso criar várias malhas em uma única cena?
Sim. Crie vários nós sob scene.rootNode e atribua um Mesh separado à propriedade entity de cada nó.