איך לטעון מודלים תלת‑ממדיים ב‑Python

איך לטעון מודלים תלת‑ממדיים ב‑Python

Aspose.3D FOSS עבור Python מספק API פשוט לפתיחת קבצי 3D ללא תלותיות מקומיות. לאחר טעינת קובץ לתוך a Scene אובייקט, אתה יכול לעבור על היררכיית הצמתים ולקרוא נתוני גאומטריה גולמיים עבור כל mesh בסצנה.

מדריך שלב אחר שלב

שלב 1: התקנת החבילה

התקן את Aspose.3D FOSS מ‑PyPI. אין צורך בספריות מערכת נוספות.

pip install aspose-3d-foss

גרסאות Python נתמכות: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


שלב 2: ייבוא מחלקת Scene

ה Scene class הוא המכולה ברמת העל עבור כל נתוני ה-3D. ייבא אותו יחד עם כל מחלקות אפשרויות הטעינה שאתה צריך.

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

כל המחלקות הציבוריות ממוקמות תחת aspose.threed או תחת חבילות המשנה שלו (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).


שלב 3: טעינת קובץ

השתמש ב-static Scene.from_file() method כדי לפתוח כל פורמט נתמך. הספרייה מזהה את הפורמט אוטומטית מהרחבת הקובץ.

##Automatic format detection
scene = Scene.from_file("model.obj")

לחלופין, צור a Scene instance וקרא open(); שימושי כאשר אתה רוצה להעביר אפשרויות טעינה או לטפל בשגיאות במפורש:

scene = Scene()
scene.open("model.obj")

שתי השיטות תומכות בקבצי OBJ, STL (בינארי ו‑ASCII), glTF 2.0 / GLB, COLLADA (DAE) ו‑3MF.


שלב 4: מעבר על צמתי Scene

סצנה טעונה היא עץ של Node אובייקטים שמקורש ב scene.root_node. חזור באופן רקורסיבי כדי למצוא את כל הצמתים:

from aspose.threed import Scene, Node

scene = Scene.from_file("model.obj")

def walk(node: Node, depth: int = 0) -> None:
    indent = "  " * depth
    print(f"{indent}Node: {node.name!r}")
    for child in node.child_nodes:
        walk(child, depth + 1)

walk(scene.root_node)

כל Node יכול לשאת אפס או יותר Entity אובייקטים (רשתות, מצלמות, אורות). בדוק node.entities כדי לראות מה מחובר.


שלב 5: גישה לנתוני קודקודים ופוליגונים

המרת את הישות של הצומת ל Mesh וקרא את נקודות הבקרה שלו (מיקומי קודקוד) ופוליגונים (רשימות אינדקסי פנים):

from aspose.threed import Scene
from aspose.threed.entities import Mesh

scene = Scene.from_file("model.obj")

for node in scene.root_node.child_nodes:
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh: Mesh = entity
            print(f"Mesh '{node.name}': "
                  f"{len(mesh.control_points)} vertices, "
                  f"{len(mesh.polygons)} polygons")

            # First vertex position
            if mesh.control_points:
                v = mesh.control_points[0]
                print(f"  First vertex: ({v.x:.4f}, {v.y:.4f}, {v.z:.4f})")

            # First polygon face (list of control-point indices)
            if mesh.polygons:
                print(f"  First polygon: {mesh.polygons[0]}")

mesh.control_points הוא רשימה של Vector4 אובייקטים; x, y, z נושאים את המיקום ו w הוא הקואורדינטה ההומוגנית (בדרך כלל 1.0).

mesh.polygons הוא רשימה של רשימות של מספרים שלמים, כאשר כל רשימה פנימית היא הקבוצה הממוינת של אינדקסי נקודות הבקרה עבור פנים אחת.


שלב 6: החלת אפשרויות טעינה ספציפיות לפורמט

לשליטה מדוקדקת על האופן שבו קובץ OBJ מפוענח, העבר ObjLoadOptions מופע ל scene.open():

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

options = ObjLoadOptions()
options.flip_coordinate_system = True   # Convert right-hand Y-up to Z-up
options.scale = 0.01                    # Convert centimetres to metres
options.enable_materials = True         # Load .mtl material file
options.normalize_normal = True         # Normalize all normals to unit length

scene = Scene()
scene.open("model.obj", options)

לקבצי STL, המחלקה המקבילה היא StlLoadOptions. עבור glTF, השתמש GltfLoadOptions. ראה את תיעוד API לרשימה מלאה.


בעיות נפוצות ותיקונים

FileNotFoundError בעת קריאה ל Scene.from_file()

הנתיב חייב להיות מוחלט או יחסי נכון לתיקיית העבודה בזמן ריצה. השתמש pathlib.Path כדי לבנות נתיבים אמינים:

from pathlib import Path
from aspose.threed import Scene

path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))

mesh.polygons הוא ריק לאחר טעינת קובץ STL

קבצי STL מאחסנים משולשים כפרצופים גולמיים, ולא כרשת ממוספרת. לאחר הטעינה, פוליגונים מסונטים מהפרצופים האלה. אם polygons נראה ריק, בדוק len(mesh.control_points); אם הספירה היא כפולה של 3, הגיאומטריה מאוחסנת בצורה בלתי ממוספרת וכל שלושה קודקודים רצופים יוצרים משולש אחד.

אי‑התאמה של מערכת הקואורדינטות (המודל נראה מסובב או משוקף)

כלים שונים משתמשים בקונבנציות שונות (Y-למעלה מול Z-למעלה, יד שמאלית מול יד ימנית). הגדר ObjLoadOptions.flip_coordinate_system = True או להחיל סיבוב על צומת השורש Transform לאחר הטעינה.

AttributeError: 'NoneType' object has no attribute 'polygons'

רשימת הישויות של צומת עשויה להכיל ישויות שאינן רשת (מצלמות, אורות). תמיד יש להגן עם isinstance(entity, Mesh) לפני ההמרה.


שאלות נפוצות (FAQ)

אילו פורמטים תלת‑ממדיים אני יכול לטעון?

OBJ (Wavefront), STL (בינארי ו‑ASCII), glTF 2.0 / GLB, COLLADA (DAE) ו‑3MF. טוקניזציית קבצי FBX נתמכת חלקית, אך פענוח מלא עדיין אינו מושלם.

האם טעינת קובץ OBJ טוענת גם את ה‑ .mtl חומר?

כן, כאשר ObjLoadOptions.enable_materials = True (ברירת המחדל). הספרייה מחפשת את ה‑ .mtl קובץ באותו תיקייה כמו ה‑ .obj קובץ. אם ה‑ .mtl חסר, הגאומטריה עדיין נטענת והאזהרה נשלחת.

האם אפשר לטעון קובץ מזרם בתים במקום מנתיב?

כן. scene.open() מקבל כל אובייקט דמוי‑קובץ עם .read() מתודה בנוסף למחרוזת נתיב קובץ. העבר זרם בינארי פתוח (למשל,., io.BytesIO) ישירות. Scene.from_file() מקבל רק מחרוזת נתיב קובץ.

איך לקבל נורמליות של פני השטח?

לאחר הטעינה, בדוק mesh.get_element(VertexElementType.NORMAL). זה מחזיר VertexElementNormal של data הרשימה מכילה וקטור נורמלי אחד לכל הפנייה, ממופה לפי mapping_mode ו reference_mode.

from aspose.threed.entities import Mesh, VertexElementType

normals = mesh.get_element(VertexElementType.NORMAL)
if normals:
    print(normals.data[0])  # First normal vector

האם הספרייה thread-safe לטעינת קבצים מרובים במקביל?

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

 עברית