گراف صحنه
یک گراف صحنه مدل دادهٔ بنیادی Aspose.3D FOSS برای Python است. هر فایل 3D، چه از دیسک بارگذاری شود و چه در حافظه ساخته شود، بهصورت درختی از Node اشیائی که ریشه در Scene.root_node. هر گره میتواند گرههای فرزند و یک یا چند Entity اشیاء (مشها، دوربینها، نورها). درک گراف صحنه به شما دسترسی مستقیم به هندسه، مواد و تبدیلات فضایی هر شیء در یک صحنه میدهد.
نصب و راهاندازی
کتابخانه را از PyPI نصب کنید:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.هیچ افزونهٔ بومی، کامپایلر یا بستهٔ سیستمی اضافی لازم نیست. برای دستورالعملهای کامل نصب، به راهنمای نصب.
بررسی کلی: مفاهیم گراف صحنه
گراف صحنه در 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 نامیده شد). |