עבודה עם גרף הסצנה

עבודה עם גרף הסצנה

כל תוכן תלת‑ממדי ב-Aspose.3D FOSS עבור TypeScript נמצא בתוך Scene אובייקט המאורגן כעץ של Node אובייקטים. הבנת ההיררכיה הזו היא הבסיס לבנייה, טעינה ועיבוד של כל קובץ תלת‑ממדי.

Installation

התקן את החבילה מ‑npm לפני הרצת כל קוד במדריך זה:

npm install @aspose/3d

ודא ש- tsconfig.json כולל "module": "commonjs" ו "moduleResolution": "node" לפתור נכון של ייבוא תתי‑נתיב.

מושגי גרף הסצנה

לגרף הסצנה יש שלושה רמות:

רמהמחלקהתפקיד
סצנהSceneמכולה ברמת עליונה. מכילה rootNode, animationClips, ו assetInfo.
צומתNodeצומת עץ בעל שם. יכול להכיל צמתים צאצאים, ישות, שינוי, וחומרים.
ישותMesh, Camera, Light, …תוכן שמצורף לצומת. צומת נושא לכל היותר ישות אחת.

שרשרת הירושה של הבלוקים המרכזיים היא:

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

scene.rootNode נוצר באופן אוטומטי. אתה לא יוצר אותו ידנית; אתה יוצר צמתים צאצאים מתחתיו.

שלב 1: צור סצנה

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

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

חדש Scene מתחיל עם צומת שורש ריק וללא קטעי אנימציה. אתה בונה תוכן על ידי צירוף צמתים צאצאים.

שלב 2: הוספת צמתים ילדים

השתמש createChildNode() כדי להגדיל את העץ. המתודה מחזירה את החדש Node, כך שאתה יכול לשרשר קריאות נוספות מכל רמה:

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)

שמות הצמתים הם מחרוזות שרירותיות. אין צורך שהשמות יהיו ייחודיים, אך שימוש בשמות משמעותיים מקל על ניפוי קוד הניווט.

שלב 3: יצירת Mesh והגדרת קודקודים

Mesh היא מחלקת הגיאומטריה הראשית. הוסף vertex positions על‑ידי דחיפה Vector4 ערכים אל mesh.controlPoints, ואז קרא createPolygon() כדי להגדיר משטחים לפי אינדקס קודקוד:

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 משתמש בקואורדינטות הומוגניות: ה- w המרכיב הוא 1 למיקומים ו 0 לוקטורי כיוון.

שלב 4: הגדרת טרנספורמציות הצומת

לכל צומת יש transform מאפיין עם translation, rotation, ו scaling. translation הגדר כדי להזיז את הצומת יחסית להורה שלו:

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 מספק את world-space transformation matrix (read-only), המחושב על‑ידי צירוף כל ה‑ancestor transforms.

שלב 5: עבור על העץ

כתוב פונקציה רקורסיבית לביקור בכל צומת. בדוק node.entity ו node.childNodes בכל רמה:

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

למבנה ההיררכי שנוצר למעלה, הפלט יהיה:

 [none]
  parent [none]
    child [Mesh]

שם צומת השורש הוא מחרוזת ריקה כי Scene יוצר אותו ללא ארגומנט שם.

תמיד הגן על גישה ל‑entity עם בדיקת null לפני המרה לסוג ספציפי. לא כל צומת נושא entity.

שלב 6: שמור ל‑glTF או GLB

השתמש ב GltfSaveOptions כדי לשלוט בפורמט הפלט. הגדר binaryMode = true להפיק קובץ יחיד עצמאי .glb קובץ; השאר את false ל-JSON .gltf + .bin צמד sidecar:

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

העבר GltfFormat.getInstance() כארגומנט פורמט כדי שהספרייה תשתמש במקודד הנכון ללא קשר לסיומת הקובץ.

טיפים ושיטות עבודה מומלצות

  • השתמש ב createChildNode() במקום לבנות Node ישירות: createChildNode() מחבר אוטומטית את יחסי הורה-ילד ורושם את הצומת בעץ.
  • בדוק node.entity לפני גישה למאפייני הישות: רבים מהצמתים (צמתי קבוצה, עצמות, ממקמים) אינם נושאים ישות. תמיד יש להגן עם בדיקת null או instanceof בדיקה.
  • הגדר translation על צמתים צאצאים, ולא על קודקודי רשת: שינוי transform.translation אינו הרסני וניתן להרכבה עם טרנספורמציות הורה.
  • העדף binaryMode = true עבור GLB: אחד .glb קובץ הוא קל יותר להפצה, טעינה בדפדפנים, וייבוא למנועי משחק מאשר המפוצל .gltf + .bin פורמט.
  • עבור דרך for...of על childNodes: הימנע מאינדוקס מספרי; השתמש באיטרטור ישירות לתאימות עתידית.

בעיות נפוצות

תסמיןסיבה אפשריתתיקון
child.entity = mesh אין לו השפעה על הייצואישות משוייכת לרמת צומת שגויההקצה entity לצומת העלה, לא לצומת קבוצה
node.entity תמיד nullרק בודק rootNode עצמוחזור לתוך node.childNodes; rootNode בדרך כלל אין ישות
הטרנספורמציה אינה משתקפת במציג GLBglobalTransform לא עודכןglobalTransform מחושב בעת שמירה; הגדר transform.translation לפני קריאה scene.save()
GLB מייצר נפרד .bin קובץ צדbinaryMode מוגדר כברירת מחדל ל falseהגדר saveOpts.binaryMode = true

ראה גם

 עברית