シーングラフの操作

TypeScript 用の Aspose.3D FOSS のすべての 3D コンテンツは、内部にあります Scene ツリー構造のオブジェクトとして編成されています Node オブジェクトです。この階層構造を理解することは、任意の 3D ファイルを構築、ロード、処理するための基礎です。.

インストール

このガイドのコードを実行する前に、npm からパッケージをインストールしてください::

npm install @aspose/3d

確実に tsconfig.json が含まれています "module": "commonjs""moduleResolution": "node" 正しいサブパスインポート解決のために.

シーングラフの概念

シーングラフは 3 つの層から構成されています::

ティアクラスロール
シーンSceneトップレベルコンテナ。保持します rootNode, animationClips, および assetInfo.
ノードNode名前付きツリーノード。子ノード、エンティティ、トランスフォーム、マテリアルを持つことができます。.
エンティティMesh, Camera, Light, …ノードに添付されたコンテンツ。ノードは最大で1つのエンティティを保持します。.

主要な構成要素の継承チェーンは次のとおりです::

A3DObject
  └─ SceneObject
       ├─ Node          (tree structure)
       └─ Entity
            └─ Geometry
                 └─ Mesh   (polygon geometry)

scene.rootNode は自動的に作成されます。手動で作成する必要はありません;その下に子ノードを作成します。.

ステップ 1: シーンを作成する

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

const scene = new Scene();
console.log(scene.rootNode.name); // '' (empty string — the root node is created with no name)

新しい Scene は空のルートノードとアニメーションクリップがない状態で開始します。子ノードを添付してコンテンツを構築します。.

ステップ 2: 子ノードを追加する

使用 createChildNode() ツリーを成長させます。メソッドは新しいものを返します Node,、したがって任意のレベルからさらに呼び出しをチェーンできます::

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

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

console.log(scene.rootNode.childNodes.length); // 1 (parent)
console.log(parent.childNodes.length);         // 1 (child)

ノード名は任意の文字列です。名前が一意である必要はありませんが、意味のある名前を付けることでトラバーサルコードのデバッグが容易になります。.

ステップ 3: メッシュを作成し頂点を設定する

Mesh は主要なジオメトリクラスです。頂点位置はプッシュして追加します Vector4 値を mesh.controlPoints,、その後呼び出します createPolygon() 頂点インデックスで面を定義します::

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

const scene = new Scene();
const child = scene.rootNode.createChildNode('parent').createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3); // quad face using all four vertices

child.entity = mesh;

console.log(mesh.controlPoints.length); // 4
console.log(mesh.polygonCount);         // 1

Vector4 は同次座標を使用します:その w 成分は 1 位置用で、 0 方向ベクトル用です。.

ステップ 4: ノードの変換を設定する

各ノードは transform プロパティを持ち、 translation, rotation,、および scaling.。設定 translation ノードを親に対して相対的に移動させるには:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4, Vector3 } from '@aspose/3d/utilities';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3);
child.entity = mesh;

child.transform.translation = new Vector3(2.0, 0.0, 0.0);

globalTransform は、すべての祖先変換を連結して計算された、ワールド空間変換行列(読み取り専用)を提供します。.

ステップ 5: ツリーを走査する

すべてのノードを訪問する再帰関数を書きます。チェック node.entitynode.childNodes 各レベルで:

function traverse(node: any, depth = 0): void {
    const indent = '  '.repeat(depth);
    const entityType = node.entity ? node.entity.constructor.name : 'none';
    console.log(`${indent}${node.name} [${entityType}]`);
    for (const child of node.childNodes) {
        traverse(child, depth + 1);
    }
}

traverse(scene.rootNode);

上記で作成した階層に対する出力は次のようになります:

 [none]
  parent [none]
    child [Mesh]

ルートノードの名前は空文字列です。なぜなら Scene は名前引数なしで作成します。.

エンティティにアクセスする際は、特定の型にキャストする前に必ず null チェックで保護してください。すべてのノードがエンティティを持つわけではありません。.

ステップ 6: glTF または GLB に保存する

使用 GltfSaveOptions で出力形式を制御します。設定 binaryMode = true 単一の自己完結型を生成するために .glb ファイル; そのままにしておく false JSON 用に .gltf + .bin サイドカー ペア:

import { Scene } from '@aspose/3d';
import { Mesh } from '@aspose/3d/entities';
import { Vector4, Vector3 } from '@aspose/3d/utilities';
import { GltfSaveOptions, GltfFormat } from '@aspose/3d/formats/gltf';

const scene = new Scene();
const parent = scene.rootNode.createChildNode('parent');
const child = parent.createChildNode('child');

const mesh = new Mesh('cube');
mesh.controlPoints.push(new Vector4(0, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 0, 0, 1));
mesh.controlPoints.push(new Vector4(1, 1, 0, 1));
mesh.controlPoints.push(new Vector4(0, 1, 0, 1));
mesh.createPolygon(0, 1, 2, 3);
child.entity = mesh;

child.transform.translation = new Vector3(2.0, 0.0, 0.0);

const saveOpts = new GltfSaveOptions();
saveOpts.binaryMode = true;                             // write a single .glb file
scene.save('scene.glb', GltfFormat.getInstance(), saveOpts);

console.log('Scene saved to scene.glb');

渡す GltfFormat.getInstance() format 引数として渡すことで、ファイル拡張子に関係なくライブラリが正しいエンコーダを使用します。.

ヒントとベストプラクティス

  • 使用する createChildNode() 構築する代わりに Node 直接: createChildNode() 親子関係を自動的に接続し、ツリーにノードを登録します。.
  • 確認する node.entity エンティティのプロパティにアクセスする前に::多くのノード(グループノード、ボーン、ロケーター)はエンティティを持ちません。必ず null チェックや instanceof テスト。.
  • 設定 translation 子ノードに対して、メッシュの頂点には設定しません::変更 transform.translation は破壊的ではなく、親の変換と合成可能です。.
  • 推奨 binaryMode = true GLB 用に::単一の .glb ファイルは、分割されたものより配布やブラウザでの読み込み、ゲームエンジンへのインポートが容易です .gltf + .bin フォーマット。.
  • 経由で走査 for...of 上で childNodes: 数値インデックスは避け、将来の互換性のためにイテラブルを直接使用してください。.

一般的な問題

症状考えられる原因修正
child.entity = mesh エクスポートに影響しませんエンティティが誤ったノードレベルに割り当てられています割り当て entity リーフノードに割り当て、グループノードには割り当てないでください
node.entity は常に nullチェックのみ rootNode それ自体再帰的に入る node.childNodes; rootNode 通常はエンティティがありません
変換がGLBビューアに反映されていませんglobalTransform 更新されていませんglobalTransform 保存時に計算されます; 設定 transform.translation 呼び出す前に scene.save()
GLBは別個の .bin サイドカーbinaryMode デフォルトは false設定 saveOpts.binaryMode = true

関連項目

 日本語