گراف صحنه
یک گراف صحنه مدل دادهٔ بنیادی Aspose.3D FOSS برای Python است. هر فایل 3D، چه از دیسک بارگذاری شود و چه در حافظه ساخته شود، بهصورت درختی از Node اشیائی که ریشه در Scene.root_node. هر گره میتواند گرههای فرزند و یک یا چند Entity اشیاء (مشها، دوربینها، نورها). درک گراف صحنه به شما دسترسی مستقیم به هندسه، مواد و تبدیلات فضایی هر شیء در یک صحنه میدهد.
نصب و راهاندازی
کتابخانه را از PyPI نصب کنید:
pip install aspose-3d-fossهیچ افزونهٔ بومی، کامپایلر یا بستهٔ سیستمی اضافی لازم نیست. برای دستورالعملهای کامل نصب، به راهنمای نصب.
بررسی کلی: مفاهیم گراف صحنه
گراف صحنه در Aspose.3D FOSS از یک سلسلهمراتب سادهٔ دربرگیری پیروی میکند:
Scene
└── root_node (Node)
├── child_node_A (Node)
│ ├── entity: Mesh
│ └── transform: translation, rotation, scale
├── child_node_B (Node)
│ └── child_node_C (Node)
│ └── entity: Mesh
└── ...| شیء | نقش |
|---|---|
Scene | محفظه سطح بالا. شامل root_node, asset_info, animation_clips, و sub_scenes. |
Node | گره درختی نامگذاریشده. دارای والد، صفر یا بیشتر فرزند، صفر یا بیشتر موجودیت، و یک Transform. |
Entity | هندسه یا شیء صحنهای که به گره متصل است. انواع رایج موجودیت: Mesh, Camera, Light. |
Transform | موقعیت، چرخش و مقیاس در فضای محلی برای یک گره. نتیجه در فضای جهانی از این خوانده میشود global_transform. |
گام به گام: ساخت گراف صحنه بهصورت برنامهنویسی
گام ۱: ایجاد یک صحنه
یک جدید Scene همیشه با یک خالی شروع میشود root_node:
from aspose.threed import Scene
scene = Scene()
print(scene.root_node.name) # "" (empty string — root node has no name by default)
print(len(scene.root_node.child_nodes)) # 0Scene نقطه ورودی برای همه چیز است: بارگذاری فایلها، ذخیرهسازی فایلها، ایجاد کلیپهای انیمیشن، و دسترسی به درخت گرهها.
گام ۲: ایجاد گرههای فرزند
از create_child_node(name) برای افزودن گرههای نامگذاریشده به درخت استفاده کنید:
from aspose.threed import Scene
scene = Scene()
parent = scene.root_node.create_child_node("parent")
child = parent.create_child_node("child")
print(parent.name) # "parent"
print(child.parent_node.name) # "parent"
print(len(scene.root_node.child_nodes)) # 1بهجای آن، یک شیء مستقل ایجاد کنید Node و آن را بهصورت صریح پیوست کنید:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)هر دو روش همان نتیجه را تولید میکنند. create_child_node برای ساخت درونخطی، مختصرتر است.
گام ۳: ایجاد یک موجودیت مش و اتصال آن
یک Mesh دادههای راس را ذخیره میکند (control_points) و توپولوژی سطح (polygons). یک مورد ایجاد کنید، هندسه را اضافه کنید، سپس آن را به یک گره پیوست کنید:
from aspose.threed import Scene, Node
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector3, Vector4
scene = Scene()
parent = scene.root_node.create_child_node("parent")
child = parent.create_child_node("child")
##Create a quad mesh (four vertices, one polygon)
# Note: control_points returns a copy of the internal list; append to
# _control_points directly to actually add vertices. This is a known
# library limitation — a public add_control_point() API is not yet available.
mesh = Mesh("cube")
mesh._control_points.append(Vector4(0, 0, 0, 1))
mesh._control_points.append(Vector4(1, 0, 0, 1))
mesh._control_points.append(Vector4(1, 1, 0, 1))
mesh._control_points.append(Vector4(0, 1, 0, 1))
mesh.create_polygon(0, 1, 2, 3)
child.add_entity(mesh)
print(f"Mesh name: {mesh.name}")
print(f"Vertex count: {len(mesh.control_points)}")
print(f"Polygon count: {mesh.polygon_count}")Vector4(x, y, z, w) یک مختصات همگن را نشان میدهد. از w=1 برای موقعیتهای نقطهای معمولی.
create_polygon(*indices) شاخصهای راس را میپذیرد و یک وجه را در فهرست چندضلعیها ثبت میکند. برای یک مثلث سه شاخص، برای یک چهارضلعی چهار شاخص بدهید.
مرحله ۴: تنظیم تبدیلات گره
هر گره دارای یک Transform که موقعیت، جهتگیری و اندازهٔ آن را در فضای محلی کنترل میکند:
from aspose.threed.utilities import Vector3, Quaternion
##Translate the node 2 units along the X axis
child.transform.translation = Vector3(2.0, 0.0, 0.0)
##Scale the node to half its natural size
child.transform.scaling = Vector3(0.5, 0.5, 0.5)
##Rotate 45 degrees around the Y axis using Euler angles
child.transform.euler_angles = Vector3(0.0, 45.0, 0.0)تبدیلها تجمعی هستند: موقعیت فضای جهانی یک گره فرزند ترکیبی از تبدیل خود آن با تمام تبدیلهای اجدادی است. نتیجهٔ ارزیابیشدهٔ فضای جهانی را از node.global_transform (غیرقابل تغییر، فقط‑خواندنی).
مرحله ۵: پیمایش بازگشتی گراف صحنه
کل درخت را با بازگشت از طریق node.child_nodes:
def traverse(node, depth=0):
indent = " " * depth
entity_type = type(node.entity).__name__ if node.entity else "None"
print(f"{indent}{node.name} [{entity_type}]")
for child in node.child_nodes:
traverse(child, depth + 1)
traverse(scene.root_node)خروجی نمونه برای صحنه ساختهشده در بالا (نام گره ریشه یک رشته خالی است):
[None]
parent [None]
child [Mesh]برای صحنههایی که در هر گره چندین موجودیت دارند، تکرار کنید node.entities بهجای node.entity:
def traverse_full(node, depth=0):
indent = " " * depth
entity_names = [type(e).__name__ for e in node.entities] or ["None"]
print(f"{indent}{node.name} [{', '.join(entity_names)}]")
for child in node.child_nodes:
traverse_full(child, depth + 1)
traverse_full(scene.root_node)مرحله ۶: ذخیره صحنه
یک مسیر فایل به scene.save(). قالب از پسوند فایل استخراج میشود:
scene.save("scene.gltf") # JSON glTF 2.0
scene.save("scene.glb") # Binary GLB container
scene.save("scene.obj") # Wavefront OBJ
scene.save("scene.stl") # STLبرای گزینههای مخصوص قالب، یک شیء گزینههای ذخیره را به عنوان آرگومان دوم بدهید:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)نکات و بهترین شیوهها
- به هر گره نام بدهید. به گرهها نامهای معنادار دادن، اشکالزدایی عبورها را بسیار آسانتر میکند و اطمینان میدهد که نامها در فایل صادر شده حفظ میشوند.
- یک مش برای هر گره. نگه داشتن موجودیتها به صورت 1:1 با گرهها، تبدیلها و پرسوجوهای برخورد را ساده میکند.
- استفاده کنید
create_child_nodeبهجای اتصال دستی. بهطور خودکار مرجع والد را تنظیم میکند و خطاپذیری کمتری دارد. - خواندن
global_transformپس از ساخت سلسلهمراتب. نتیجه در فضای جهان تنها زمانی پایدار میشود که تمام تبدیلهای اجداد تنظیم شوند. - در طول پیمایش، درخت را تغییر ندهید. افزودن یا حذف گرههای فرزند هنگام تکرار
child_nodesمنجر به رفتار غیرقابل پیشبینی میشود. ابتدا گرهها را جمعآوری کنید، سپس تغییر دهید. - نقاط کنترل استفاده میکنند
Vector4,، نهVector3. همیشه پاس کنیدw=1برای موقعیتهای رأس عادی؛;w=0یک بردار جهت را نشان میدهد (نه یک نقطه). mesh.control_pointsیک نسخه باز میگرداند. اینcontrol_pointsویژگی مقدار را برمیگرداندlist(self._control_points)— افزودن به لیست بازگرداندهشده مش را تغییر نمیدهد. همیشه بهmesh._control_pointsمستقیم هنگام ساخت هندسه بهصورت برنامهنویسی. این یک محدودیت شناختهشده کتابخانه است؛ هنوز API عمومی برای تغییر وجود ندارد.
مشکلات رایج
| مسئله | راهحل |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | با Guard if node.entity is not None قبل از دسترسی به ویژگیهای موجودیت. گرهای بدون موجودیتها entity = None. |
مش در مبدأ ظاهر میشود علیرغم تنظیم translation | transform.translation یک جابجایی محلی اعمال میکند. اگر گره والد خودش تبدیل غیرهویت داشته باشد، موقعیت جهانی ممکن است متفاوت باشد. بررسی کنید global_transform. |
گرههای فرزند پس از scene.save() / بارگذاریمجدد | برخی فرمتها (OBJ) ساختار سلسلهمراتبی را صاف میکنند. برای حفظ درخت گره کامل از glTF یا COLLADA استفاده کنید. |
polygon_count بعد از 0 است mesh.create_polygon(...) | اطمینان حاصل کنید که شاخصهای راس به create_polygon در محدوده (0 به len(control_points) - 1). |
Node.get_child(name) باز میگرداند None | نام به حروف بزرگ/کوچک حساس است. رشته نام دقیق استفاده شده در زمان ایجاد را تأیید کنید. |
| پیمایش گرهها را به ترتیب غیرمنتظره بازدید میکند | child_nodes فرزندان را به ترتیب درج (the order add_child_node / create_child_node نامیده شد). |