גרף סצנה
א גרף סצנה הוא מודל הנתונים הבסיסי של Aspose.3D FOSS עבור Python. כל קובץ תלת‑ממדי, בין אם נטען מהדיסק או נבנה בזיכרון, מיוצג כעץ של 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. |
שלב‑אחר‑שלב: בניית גרף סצנה תכנותית
שלב 1: צור סצנה
חדשה 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 הוא נקודת הכניסה לכל: טעינת קבצים, שמירת קבצים, יצירת קטעי אנימציה, וגישה לעץ הצמתים.
שלב 2: צור צמתים ילדים
השתמש 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 קצר יותר לבנייה משולבת.
שלב 3: צור ישות Mesh וצרף אותה
א 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) מקבל אינדקסי קודקוד ורושם פנים אחת ברשימת הפוליגונים. העבר שלושה אינדקסים למשולש, ארבעה לריבוע.
שלב 4: הגדרת טרנספורמציות הצומת
לכל צומת יש 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 (בלתי ניתן לשינוי, קריאה בלבד).
שלב 5: מעבר על גרף הסצנה באופן רקורסיבי
הולך בכל העץ על‑ידי רקורסיה דרך 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)שלב 6: שמירת הסצנה
העבר נתיב קובץ ל 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לאפשרויות ספציפיות לפורמט, העבר אובייקט save-options כארגומנט השני:
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' | הגן עם 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 מחזיר ילדים בסדר ההוספה (הסדר add_child_node / create_child_node נקרא). |