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/3dSä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å | Klass | Roll |
|---|---|---|
| Scen | Scene | Behållare på toppnivå. Innehåller rootNode, animationClips, och assetInfo. |
| Nod | Node | Namngiven trädnod. Kan ha barnnoder, en entitet, en transform och material. |
| Entitet | Mesh, 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 konstrueraNodedirekt:createChildNode()kopplar automatiskt föräldra‑barn‑relationen och registrerar noden i trädet. - Kontrollera
node.entityinnan du får åtkomst till entitetsegenskaper: många noder (gruppnoder, ben, lokatorer) har ingen entitet. Säkerställ alltid med en null check ellerinstanceoftest. - Sätt
translationpå barnnoder, inte på mesh vertices:modifieratransform.translationär icke-destruktiv och kan kombineras med föräldraomvandlingar. - Föredra
binaryMode = trueför GLB: en enda.glbfil är enklare att distribuera, ladda i webbläsare och importera till spelmotorer än den delade.gltf+.binformatet. - Traversera via
for...oföverchildNodes: undvik numerisk indexering; använd den itererbara direkt för framtida kompatibilitet.
Vanliga problem
| Symtom | Trolig orsak | Åtgärd |
|---|---|---|
child.entity = mesh har ingen effekt på export | Entitet tilldelad fel nodnivå | Tilldela entity till bladnod, inte till en gruppnod |
node.entity är alltid null | Endast kontrollerar rootNode sig själv | Rekursion i node.childNodes; rootNode har vanligtvis ingen entitet |
| Transformering visas inte i GLB-visaren | globalTransform inte uppdaterad | globalTransform beräknas vid sparning; sätt transform.translation innan anrop scene.save() |
GLB skapar en separat .bin sidecar | binaryMode standard är false | Ange saveOpts.binaryMode = true |
Se även
- Funktioner och funktionalitet: fullständig API-referens för alla funktionsområden.
- Formatstöd: stödda 3D-format, läs/skriv-förmåga och formatalternativ.
- Hur man bygger ett 3D-mesh programatiskt