使用场景图

所有位于 Aspose.3D 的 3D 内容,TypeScript 的 FOSS 位于一个 Scene 对象,组织成一个树形结构的 Node 对象。理解此层次结构是构建、加载和处理任何 3D 文件的基础。.

安装

在运行本指南中的任何代码之前,请先从 npm 安装该包::

npm install @aspose/3d

确保你的 tsconfig.json 包含 "module": "commonjs" 以及 "moduleResolution": "node" 以实现正确的子路径导入解析。.

场景图概念

场景图分为三个层级::

层级角色
场景Scene顶层容器。包含 rootNode, animationClips,,以及 assetInfo.
节点Node具名树节点。可以拥有子节点、实体、变换和材质。.
实体Mesh, Camera, Light, …内容附加到节点。一个节点最多携带一个实体。.

主要构建块的继承链如下::

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

scene.rootNode 会自动创建。您不需要手动创建它;您在其下创建子节点。.

Step 1: Create a Scene

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 并设置顶点

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 用于方向向量。.

Step 4: Set Node Transforms

每个节点都有一个 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 提供 world-space transformation matrix(只读),通过连接所有祖先变换计算得到。.

步骤 5:遍历树

编写递归函数以访问每个节点。检查 node.entity 并且 node.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 在没有名称参数的情况下创建它。.

在将实体强制转换为特定类型之前,始终使用空值检查来保护实体访问。并非所有节点都携带实体。.

步骤 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() 作为格式参数,以便库使用正确的编码器,而不受文件扩展名的影响。.

技巧与最佳实践

  • 使用 createChildNode() 而不是直接构造 Node 直接: createChildNode() 会自动连接父子关系并将节点注册到树中。.
  • 检查 node.entity 在访问实体属性之前: 许多节点(组节点、骨骼、定位器)没有实体。始终使用空值检查进行防护,或 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 sidecarbinaryMode 默认是 false设置 saveOpts.binaryMode = true

另见

 中文