Работа с графа на сцената

Всичко 3D съдържание в Aspose.3D FOSS за TypeScript се намира вътре в Scene обект, организиран като дърво от Node обекти. Разбирането на тази йерархия е основата за създаване, зареждане и обработка на всеки 3D файл.

Installation

Инсталирайте пакета от npm преди да изпълните какъвто и да е код от това ръководство:

npm install @aspose/3d

Уверете се, че вашият tsconfig.json включва "module": "commonjs" и "moduleResolution": "node" за правилно sub-path import resolution.

Концепции за графа на сцената

Графът на сцената има три нива:

НивоКласРоля
СценаSceneКонтейнер от най-високо ниво. Съдържа rootNode, animationClips, и assetInfo.
ВъзелNodeИменуван възел в дървото. Може да има дъщерни възли, обект, трансформация и материали.
ОбектMesh, Camera, Light, …Съдържание, прикрепено към възел. Възелът съдържа най-много един обект.

Верига на наследяване за основните градивни блокове е:

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 и задаване на върхове

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.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 създава го без аргумент за име.

Винаги проверявайте достъпа до entity с проверка за null преди да го кастнете към конкретен тип. Не всеки възел съдържа entity.

Стъпка 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 преди да достъпите свойствата на обекта: много възли (групови възли, кости, локатори) не носят обект. Винаги проверявайте за 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

Вижте също

 Български