使用场景图
所有位于 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 sidecar | binaryMode 默认是 false | 设置 saveOpts.binaryMode = true |
另见
- 功能和特性::所有功能区域的完整 API 参考。.
- 格式支持::支持的 3D 格式、读写能力以及格式选项。.
- 如何以编程方式构建 3D Mesh