TypeScriptでプログラム的に3Dメッシュを構築する方法

TypeScriptでプログラム的に3Dメッシュを構築する方法

Aspose.3D FOSS for TypeScript は、ファイルを読み込むことなく、コードだけで 3D ジオメトリを完全に構築できるようにします。頂点位置を制御点として定義し、インデックスでポリゴン面を指定し、法線、UV、または頂点カラーなどのオプションの頂点要素を添付できます。結果は、glTF、GLB、STL、FBX、または COLLADA のいずれかの書き込み可能な形式で保存できます。

前提条件

  • Node.js 18 以降
  • TypeScript 5.0 以降
  • @aspose/3d がインストール済み(ステップ1参照)

ステップバイステップガイド

ステップ 1: @aspose/3d をインストール

npm install @aspose/3d

ネイティブアドオンやシステムライブラリは必要ありません。このパッケージには TypeScript の型定義が含まれています。

最小 tsconfig.json:

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

ステップ 2: シーンとノードを作成する

A Scene はトップレベルのコンテナです。すべてのジオメトリはシーンツリー内の Node にアタッチする必要があります。

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

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

createChildNode(name) は名前付きノードを作成し、現在のノードの子として接続します。返される Node オブジェクトはステップ7でメッシュを添付する場所です。


Step 3: メッシュオブジェクトを作成

Mesh は頂点位置とポリゴン定義を保持します。任意の名前で構築します:

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

const mesh = new Mesh('triangle');

メッシュは空の状態で開始します:頂点も面もありません。次の手順でそれらを追加します。


ステップ 4: 制御点(頂点)を追加

制御点はローカル空間における頂点位置です。Vector4 の値を mesh.controlPoints にプッシュします。4番目のコンポーネント(w)は位置に対して 1 です。

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

ポリゴンの面を定義する際、これらの位置はゼロベースのインデックスで参照します。


Step 5: ポリゴン面の作成

createPolygon() は、頂点インデックスを順番に列挙することで面を定義します。3つのインデックスで三角形が構成されます:

mesh.createPolygon(0, 1, 2);

サポートされているフォーマットでは、クアッド(four indices)や任意のポリゴンを定義することもできます。glTF では、ライブラリがエクスポート時にクアッドと n-gons を自動的に三角形化します。


ステップ 6: 頂点法線を追加

法線はレンダリング品質を向上させます。mesh.createElement() を使用して VertexElementNormal を作成し、法線ベクトルを配列に収集してから、setData() を呼び出して保存します。data の getter は防御的コピーを返すため、そこにプッシュしても効果はありません。法線データには FVector3(単精度浮動小数点)を使用し、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 は頂点ごとに1つの法線を意味します。 ReferenceMode.DIRECT はデータ配列がポリゴン頂点インデックスで直接インデックス付けされることを意味します。


ステップ 7: メッシュをアタッチし、glTF に保存

node.entity を介してメッシュをノードに割り当て、シーンを保存します:

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');

代わりに単一の自己完結型.glbファイルを生成するには、saveOpts.binaryMode = trueを設定し、出力ファイルの拡張子を.glbに変更します。

完全な例

以下は、上記のすべての手順を組み合わせた完全なスクリプトです。

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');

ts-nodeで実行:

npx ts-node triangle.ts

一般的な問題

IssueCauseFix
mesh.controlPoints.length がプッシュ後に 0 になるメッシュが任意のノードから参照されていないnode.entity を割り当てる前にプッシュする; 順序は関係ないが、参照を確認する
エクスポートで空のジオメトリが生成されるnode.entity が割り当てられていないscene.save() を呼び出す前に node.entity = mesh を確実に行う
法線数の不一致setData() に渡された配列が controlPoints より短いMappingMode.CONTROL_POINT を使用する場合、制御点ごとに FVector3 エントリを 1 つ追加する
glTF ビューアでメッシュが黒く表示される法線が内側を向いているcreatePolygon の winding 順序を反転する(例: 0, 2, 1)または法線ベクトルの符号を反転する
TypeScript: ’normals.data’ プロパティが見つからないインポートパスが間違っているVertexElementNormal@aspose/3d/entities からインポートし、@aspose/3d ルートからではない

よくある質問

三角形の代わりにクアッドを作成できますか?
はい。createPolygon(0, 1, 2, 3) に4つのインデックスを渡してください。ライブラリは、三角形が必要な形式(glTF、STL)へのエクスポート時にクアッドを三角形に分割します。

MappingMode.CONTROL_POINTMappingMode.POLYGON_VERTEX の違いは何ですか?
CONTROL_POINT はユニークな頂点ごとに 1 つの値を格納します。POLYGON_VERTEX はポリゴン‑頂点ペアごとに 1 つの値を格納し、同じ頂点が複数のポリゴンに属する場合に異なる法線を持たせることができます(ハードエッジ)。

STLに保存する前にメッシュを三角形化する必要がありますか?
いいえ。ライブラリは、三角形が必要なフォーマットへエクスポートする際に自動的に三角形化を処理します。メッシュ内でクアッドや n‑gon を定義したまま、直接 STL に保存できます。

UV座標はどうやって追加しますか?
Use mesh.createElementUV(TextureMapping.Diffuse, MappingMode.CONTROL_POINT, ReferenceMode.DIRECT) を使用して VertexElementUV を作成し、次に setData([...]) を呼び出して FVector2 または FVector3 の配列を渡します — 制御点ごとに 1 つです。data の getter はコピーを返します。直接プッシュしないでください。

1つのシーンで複数のメッシュを作成できますか?
はい。scene.rootNode の下に複数のノードを作成し、各ノードの entity プロパティに別々の Mesh を割り当てます。

参照

 日本語