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/3dSø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:
| Niveau | Klasse | Rolle |
|---|---|---|
| Scene | Scene | Topniveau-beholder. Indeholder rootNode, animationClips og assetInfo. |
| Node | Node | Navngivet trænode. Kan have undernoder, en entitet, en transformation og materialer. |
| Entity | Mesh, 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 konstruereNodedirekte:createChildNode()opretter automatisk forældre‑barn‑forholdet og registrerer noden i træet. - Tjek
node.entityfør du får adgang til entitetsegenskaber: mange noder (gruppenoder, knogler, lokatorer) har ingen entitet. Beskyt altid med en null‑tjek ellerinstanceof‑test. - Sæt
translationpå underordnede noder, ikke på mesh‑vertexer: ændring aftransform.translationer ikke‑destruktiv og kan sammensættes med forældre‑transformeringer. - Foretræk
binaryMode = truefor 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...ofi stedet forchildNodes: undgå numerisk indeksering; brug den iterable direkte for fremtidig kompatibilitet.
Almindelige problemer
| Symptom | Sandsynlig årsag | Løsning |
|---|---|---|
child.entity = mesh har ingen effekt på eksport | Entitet tildelt forkert nodeniveau | Tildel entity til bladnoden, ikke til en gruppenode |
node.entity er altid null | Kun tjekker rootNode selv | Gå rekursivt ind i node.childNodes; rootNode har typisk ingen entitet |
| Transformation afspejles ikke i GLB-fremviser | globalTransform er ikke opdateret | globalTransform beregnes ved gem; sæt transform.translation før du kalder scene.save() |
GLB genererer en separat .bin sidecar | binaryMode har standardværdien false | Sæt saveOpts.binaryMode = true |
Se også
- Funktioner og funktionaliteter: fuld API-reference for alle funktionsområder.
- Formatunderstøttelse: understøttede 3D-formater, læse-/skrivefunktionalitet og formatindstillinger.
- Sådan bygges et 3D-mesh programmatisk