العمل مع مخطط المشهد

العمل مع مخطط المشهد

جميع المحتوى ثلاثي الأبعاد في 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 يبدأ بعقدة جذر فارغة ولا يحتوي على animation clips. تقوم بإنشاء المحتوى عن طريق إرفاق child nodes.

الخطوة 2: إضافة عقد فرعية

استخدم createChildNode() لتوسيع الشجرة. تُعيد method العنصر new 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 هي primary geometry class. أضف vertex positions عن طريق دفع Vector4 values إلى mesh.controlPoints,، ثم استدعِ createPolygon() لتحديد faces بواسطة vertex index:

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 يوفر مصفوفة التحويل في الفضاء العالمي (للقراءة فقط)، يتم حسابها بدمج جميع التحويلات السلفية.

الخطوة 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 يقوم بإنشائها دون تمرير معامل الاسم.

دائمًا احمِ الوصول إلى الكيان بفحص null قبل التحويل إلى نوع محدد. ليست كل عقدة تحمل كيانًا.

الخطوة 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() كمعامل format بحيث تستخدم المكتبة المشفر الصحيح بغض النظر عن امتداد الملف.

نصائح وأفضل الممارسات

  • استخدم 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

انظر أيضًا

 العربية