Arbeta med scengrafen

Allt 3D-innehåll i Aspose.3D FOSS för TypeScript finns inuti en Scene objekt organiserat som ett träd av Node objekt. Att förstå denna hierarki är grunden för att bygga, ladda och bearbeta vilken 3D-fil som helst.

Installation

Installera paketet från npm innan du kör någon av koderna i den här guiden:

npm install @aspose/3d

Säkerställ att din tsconfig.json inkluderar "module": "commonjs" och "moduleResolution": "node" för korrekt sub‑sökvägsimportslösning.

Scengrafkoncept

Scengrafen har tre nivåer:

NivåKlassRoll
ScenSceneBehållare på toppnivå. Innehåller rootNode, animationClips, och assetInfo.
NodNodeNamngiven trädnod. Kan ha barnnoder, en entitet, en transform och material.
EntitetMesh, Camera, Light, …Innehåll som är bifogat till en nod. En nod bär högst en entitet.

Arvskedjan för de viktigaste byggstenarna är:

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

scene.rootNode skapas automatiskt. Du skapar den inte manuellt; du skapar undernoder under den.

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)

En ny Scene börjar med en tom rotnod och inga animationsklipp. Du bygger innehåll genom att bifoga undernoder.

Steg 2: Lägg till barnnoder

Använd createChildNode() för att växa trädet. Metoden returnerar den nya Node, så att du kan kedja ytterligare anrop från vilken nivå som helst:

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-namn är godtyckliga strängar. Namnen behöver inte vara unika, men att använda meningsfulla namn gör traversalkoden lättare att felsöka.

Steg 3: Skapa ett Mesh och sätt vertexar

Mesh är den primära geometriklassen. Lägg till vertexpositioner genom att pusha Vector4 värden i mesh.controlPoints, sedan anropa createPolygon() för att definiera ytor med vertexindex:

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 använder homogena koordinater: den w komponenten är 1 för positioner och 0 för riktningsvektorer.

Step 4: Set Node Transforms

Varje nod har en transform egenskap med translation, rotation, och scaling. Ställ in translation för att flytta noden relativt dess förälder:

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 tillhandahåller världsrumstransformationsmatrisen (skrivskyddad), beräknad genom att konkatenera alla förfäders transformationer.

Steg 5: Traversera trädet

Skriv en rekursiv funktion för att besöka varje nod. Kontrollera node.entity och node.childNodes på varje nivå:

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

För hierarkin som skapades ovan kommer utskriften att vara:

 [none]
  parent [none]
    child [Mesh]

Rotnodens namn är en tom sträng eftersom Scene skapar den utan namnargument.

Säkerställ alltid åtkomst till en entitet med en null‑kontroll innan du castar till en specifik typ. Inte varje nod har en entitet.

Steg 6: Spara till glTF eller GLB

Använd GltfSaveOptions för att styra utdataformatet. Ställ in binaryMode = true för att producera en enda självständig .glb fil; låt den false för 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');

Skicka GltfFormat.getInstance() som formatargument så att biblioteket använder rätt kodare oavsett filändelse.

Tips och bästa praxis

  • Använd createChildNode() istället för att konstruera Node direkt: createChildNode() kopplar automatiskt föräldra‑barn‑relationen och registrerar noden i trädet.
  • Kontrollera node.entity innan du får åtkomst till entitetsegenskaper: många noder (gruppnoder, ben, lokatorer) har ingen entitet. Säkerställ alltid med en null check eller instanceof test.
  • Sätt translation på barnnoder, inte på mesh vertices:modifiera transform.translation är icke-destruktiv och kan kombineras med föräldraomvandlingar.
  • Föredra binaryMode = true för GLB: en enda .glb fil är enklare att distribuera, ladda i webbläsare och importera till spelmotorer än den delade .gltf + .bin formatet.
  • Traversera via for...of över childNodes: undvik numerisk indexering; använd den itererbara direkt för framtida kompatibilitet.

Vanliga problem

SymtomTrolig orsakÅtgärd
child.entity = mesh har ingen effekt på exportEntitet tilldelad fel nodnivåTilldela entity till bladnod, inte till en gruppnod
node.entity är alltid nullEndast kontrollerar rootNode sig självRekursion i node.childNodes; rootNode har vanligtvis ingen entitet
Transformering visas inte i GLB-visarenglobalTransform inte uppdateradglobalTransform beräknas vid sparning; sätt transform.translation innan anrop scene.save()
GLB skapar en separat .bin sidecarbinaryMode standard är falseAnge saveOpts.binaryMode = true

Se även

 Svenska