چگونه مدل‌های سه‌بعدی را در پایتون بارگذاری کنیم

چگونه مدل‌های سه‌بعدی را در پایتون بارگذاری کنیم

Aspose.3D FOSS for Python یک API ساده برای باز کردن فایل‌های 3D بدون هیچ وابستگی بومی فراهم می‌کند. پس از بارگذاری یک فایل به داخل شیء Scene، می‌توانید سلسله‌مراتب گره‌ها را پیمایش کنید و داده‌های هندسی خام هر مش در صحنه را بخوانید.

راهنمای گام به گام

مرحله 1: نصب بسته

Aspose.3D FOSS را از PyPI نصب کنید. هیچ کتابخانهٔ سیستمی اضافی مورد نیاز نیست.

pip install aspose-3d-foss

نسخه‌های پایتون پشتیبانی‌شده: 3.7، 3.8، 3.9، 3.10، 3.11، 3.12.


مرحله ۲: وارد کردن Scene Class

کلاس Scene بالاترین سطحِ ظرف برای تمام داده‌های 3D است. آن را همراه با هر کلاس گزینه‌بارگذاری که نیاز دارید، وارد کنید.

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

تمام کلاس‌های عمومی زیر aspose.threed یا بسته‌های فرعی آن (aspose.threed.entities، aspose.threed.formats، aspose.threed.utilities) قرار دارند.


مرحله ۳: بارگذاری یک فایل

از متد ایستا Scene.from_file() برای باز کردن هر فرمت پشتیبانی‌شده استفاده کنید. کتابخانه فرمت را به‌صورت خودکار از پسوند فایل تشخیص می‌دهد.

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

به‌جای آن، یک نمونه Scene ایجاد کنید و open() را فراخوانی کنید؛ این کار زمانی مفید است که بخواهید گزینه‌های بارگذاری را پاس کنید یا خطاها را به‌صورت صریح مدیریت کنید:

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

هر دو روش از فایل‌های OBJ، STL (باینری و ASCII)، glTF 2.0 / GLB، COLLADA (DAE) و 3MF پشتیبانی می‌کنند.


مرحله ۴: عبور از گره‌های صحنه

یک صحنه بارگذاری‌شده درختی از اشیای 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 reference مراجعه کنید.


مشکلات رایج و راه‌حل‌ها

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) را بررسی کنید؛ اگر تعداد آن مضربی از ۳ باشد، هندسه به‌صورت بدون ایندکس ذخیره شده است و هر سه‌تایی متوالی از رئوس یک مثلث را تشکیل می‌دهد.

عدم تطابق سیستم مختصات (مدل به نظر می‌رسد چرخیده یا آینه‌ای باشد)

ابزارهای مختلف از قراردادهای متفاوتی استفاده می‌کنند (Y‑up در مقابل Z‑up، دست چپ در مقابل دست راست). ObjLoadOptions.flip_coordinate_system = True را تنظیم کنید یا پس از بارگذاری یک چرخش به Transform گره ریشه اعمال کنید.

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

لیست موجودیت‌های یک گره ممکن است شامل موجودیت‌های غیر مش (دوربین‌ها، نورها) باشد. همیشه قبل از تبدیل، با isinstance(entity, Mesh) محافظت کنید.


سوالات متداول (FAQ)

کدام فرمت‌های 3D را می‌توانم بارگذاری کنم؟

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 واحد را بدون قفل‌گذاری خارجی بین رشته‌ها به اشتراک نگذارید.

 فارسی