Práce se scénovým grafem

Veškerý 3D obsah v Aspose.3D FOSS pro TypeScript se nachází uvnitř Scene objekt organizovaný jako strom Node objektů. Porozumění této hierarchii je základem pro vytváření, načítání a zpracování jakéhokoli 3D souboru.

Installation

Nainstalujte balíček z npm před spuštěním jakéhokoli kódu v tomto průvodci:

npm install @aspose/3d

Ujistěte se, že vaše tsconfig.json obsahuje "module": "commonjs" a "moduleResolution": "node" pro správné řešení importu podcesty.

Koncepty scénového grafu

Scénový graf má tři úrovně:

ÚroveňTřídaRole
ScénaSceneKontejner nejvyšší úrovně. Obsahuje rootNode, animationClips, a assetInfo.
UzelNodePojmenovaný uzel stromu. Může mít podřízené uzly, entitu, transformaci a materiály.
EntitaMesh, Camera, Light, …Obsah připojený k uzlu. Uzlu může nést nejvýše jednu entitu.

Řetězec dědičnosti pro hlavní stavební bloky je:

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

scene.rootNode je vytvořen automaticky. Nevytváříte jej ručně; vytváříte pod ním podřízené uzly.

Krok 1: Vytvořte scénu

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

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

Nový Scene začíná s prázdným kořenovým uzlem a bez animačních klipů. Obsah vytváříte připojováním podřízených uzlů.

Krok 2: Přidat podřízené uzly

Použijte createChildNode() k růstu stromu. Metoda vrací nový Node, takže můžete řetězit další volání z libovolné úrovně:

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)

Názvy uzlů jsou libovolné řetězce. Názvy nemusí být jedinečné, ale používání smysluplných názvů usnadňuje ladění kódu pro procházení.

Krok 3: Vytvořit Mesh a nastavit vrcholy

Mesh je primární geometrická třída. Přidejte pozice vrcholů pomocí vkládání Vector4 hodnot do mesh.controlPoints, pak zavolejte createPolygon() k definování ploch podle indexu vrcholů:

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 používá homogenní souřadnice: w komponenta je 1 pro pozice a 0 pro směrové vektory.

Krok 4: Nastavte transformace uzlů

Každý uzel má transform vlastnost s translation, rotation, a scaling. Nastavte translation k posunutí uzlu relativně k jeho rodiči:

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 poskytuje matici transformace ve světovém prostoru (pouze pro čtení), vypočtenou spojením všech transformací předků.

Krok 5: Procházet strom

Napište rekurzivní funkci, která navštíví každý uzel. Zkontrolujte node.entity a node.childNodes na každé úrovni:

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

Pro výše vytvořenou hierarchii bude výstup:

 [none]
  parent [none]
    child [Mesh]

Název kořenového uzlu je prázdný řetězec, protože Scene vytváří jej bez argumentu name.

Vždy chraňte přístup k entitě kontrolou na null před přetypováním na konkrétní typ. Ne každý uzel nese entitu.

Krok 6: Uložit do glTF nebo GLB

Použijte GltfSaveOptions k řízení výstupního formátu. Nastavte binaryMode = true k vytvoření jediného samostatného .glb soubor; nechte ho false pro JSON .gltf + .bin sidecar pár:

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

Předat GltfFormat.getInstance() jako argument formátu, aby knihovna použila správný enkodér bez ohledu na příponu souboru.

Tipy a osvědčené postupy

  • Použijte createChildNode() namísto vytváření Node přímo: createChildNode() automaticky propojí vztah rodič‑dítě a zaregistruje uzel ve stromu.
  • Zkontrolujte node.entity před přístupem k vlastnostem entity: mnoho uzlů (skupinové uzly, kosti, lokátory) nemá žádnou entitu. Vždy to ošetřete kontrolou na null nebo instanceof test.
  • Nastavit translation na podřízených uzlech, ne na vrcholech mesh:modifikace transform.translation je nedestruktivní a lze jej kombinovat s nadřazenými transformacemi.
  • Preferujte binaryMode = true pro GLB:jediný .glb soubor je snazší distribuovat, načíst v prohlížečích a importovat do herních enginů než rozdělený .gltf + .bin formát.
  • Procházet pomocí for...of přes childNodes:vyhněte se číselnému indexování; použijte iterovatelný objekt přímo pro budoucí kompatibilitu.

Běžné problémy

SymptomPravděpodobná příčinaOprava
child.entity = mesh nemá žádný vliv na exportEntita přiřazena k nesprávné úrovni uzluPřiřadit entity k listovému uzlu, ne ke skupinovému uzlu
node.entity je vždy nullPouze kontrola rootNode sámRekurzivně do node.childNodes; rootNode obvykle nemá žádnou entitu
Transformace se neodráží v prohlížeči GLBglobalTransform neaktualizovánoglobalTransform se vypočítá při uložení; nastavit transform.translation před voláním scene.save()
GLB vytváří samostatný .bin sidecarbinaryMode výchozí hodnota je falseNastavit saveOpts.binaryMode = true

Viz také

 Čeština