Arbejde med Scene Graph

Alt 3D‑indhold i Aspose.3D FOSS for TypeScript findes inde i et Scene‑objekt organiseret som et træ af Node‑objekter. Forståelse af dette hierarki er grundlaget for at bygge, indlæse og behandle enhver 3D‑fil.

Installation

Installer pakken fra npm, før du kører nogen af koden i denne vejledning:

npm install @aspose/3d

Sørg for, at din tsconfig.json inkluderer "module": "commonjs" og "moduleResolution": "node" for korrekt sub‑sti importløsning.

Scene Graph-koncept

Scenegrafen har tre niveauer:

NiveauKlasseRolle
SceneSceneTopniveau-beholder. Indeholder rootNode, animationClips og assetInfo.
NodeNodeNavngivet trænode. Kan have undernoder, en entitet, en transformation og materialer.
EntityMesh, Camera, Light, …Indhold knyttet til en node. En node bærer højst én entitet.

Arvekæden for de vigtigste byggeklodser er:

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

scene.rootNode oprettes automatisk. Du opretter den ikke manuelt; du opretter underordnede noder under den.

Trin 1: Opret en 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)

En ny Scene starter med en tom rotnode og ingen animationsklip. Du bygger indhold ved at vedhæfte undernoder.

Trin 2: Tilføj underknuder

Brug createChildNode() til at vokse træet. Metoden returnerer den nye Node, så du kan kæde yderligere kald fra ethvert niveau:

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)

Node-navne er vilkårlige strenge. Navne behøver ikke at være unikke, men ved at bruge meningsfulde navne bliver traversalkode lettere at fejlfinde.

Trin 3: Opret et mesh og indstil vertexer

Mesh er den primære geometriklasse. Tilføj vertexpositioner ved at skubbe Vector4‑værdier ind i mesh.controlPoints, og kald derefter createPolygon() for at definere flader efter vertexindeks:

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 bruger homogene koordinater: w‑komponenten er 1 for positioner og 0 for retningsvektorer.

Trin 4: Indstil node-transformationer

Hver node har en transform-egenskab med translation, rotation og scaling. Indstil translation for at flytte noden i forhold til dens overordnede:

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 leverer world-space transformationsmatrix (kun læse), beregnet ved at sammenkæde alle forfædres transformationer.

Trin 5: Gå gennem træet

Skriv en rekursiv funktion for at besøge hver node. Tjek node.entity og node.childNodes på hvert niveau:

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

For hierarkiet oprettet ovenfor, vil output være:

 [none]
  parent [none]
    child [Mesh]

Rodnodens navn er en tom streng, fordi Scene opretter den uden navneargument.

Sørg altid for at beskytte entitetsadgang med et null‑tjek, før du caster til en specifik type. Ikke hver node bærer en entitet.

Trin 6: Gem som glTF eller GLB

Brug GltfSaveOptions til at styre outputformatet. Indstil binaryMode = true til at producere en enkelt selvstændig .glb fil; lad den false for JSON .gltf + .bin sidecar-par:

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

Angiv GltfFormat.getInstance() som formatargument, så biblioteket bruger den korrekte encoder uanset filtypen.

Tips og bedste praksis

  • Brug createChildNode() i stedet for at konstruere Node direkte: createChildNode() opretter automatisk forældre‑barn‑forholdet og registrerer noden i træet.
  • Tjek node.entity før du får adgang til entitetsegenskaber: mange noder (gruppenoder, knogler, lokatorer) har ingen entitet. Beskyt altid med en null‑tjek eller instanceof‑test.
  • Sæt translation på underordnede noder, ikke på mesh‑vertexer: ændring af transform.translation er ikke‑destruktiv og kan sammensættes med forældre‑transformeringer.
  • Foretræk binaryMode = true for GLB: en enkelt .glb‑fil er lettere at distribuere, indlæse i browsere og importere i spilmotorer end det opdelt .gltf + .bin‑format.
  • Traversér via for...of i stedet for childNodes: undgå numerisk indeksering; brug den iterable direkte for fremtidig kompatibilitet.

Almindelige problemer

SymptomSandsynlig årsagLøsning
child.entity = mesh har ingen effekt på eksportEntitet tildelt forkert nodeniveauTildel entity til bladnoden, ikke til en gruppenode
node.entity er altid nullKun tjekker rootNode selvGå rekursivt ind i node.childNodes; rootNode har typisk ingen entitet
Transformation afspejles ikke i GLB-fremviserglobalTransform er ikke opdateretglobalTransform beregnes ved gem; sæt transform.translation før du kalder scene.save()
GLB genererer en separat .bin sidecarbinaryMode har standardværdien falseSæt saveOpts.binaryMode = true

Se også

 Dansk