كيفية تحويل النماذج ثلاثية الأبعاد في بايثون

كيفية تحويل النماذج ثلاثية الأبعاد في بايثون

تحويل الصيغ باستخدام Aspose.3D FOSS للبايثون هو عملية من خطوتين: تحميل إلى كائن Scene، ثم حفظ إلى صيغة الإخراج المطلوبة. لأن جميع الهندسة محفوظة في تمثيل موحد في الذاكرة، لا تحتاج إلى خطوات وسيطة خاصة بالصيغ. الأقسام أدناه تُظهر أكثر التحويلات شيوعًا مع كود يعمل.

دليل خطوة بخطوة

الخطوة 1: تثبيت الحزمة

pip install aspose-3d-foss

لا توجد مكتبات نظام أو مترجمات أو تبعيات تشغيل إضافية مطلوبة.


الخطوة 2: تحميل النموذج المصدر

استخدم Scene.from_file() للحالة الأبسط: يتم اكتشاف التنسيق تلقائيًا من امتداد الملف:

from aspose.threed import Scene

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

بالنسبة لملفات OBJ التي تحتاج فيها إلى التحكم في نظام الإحداثيات أو تحميل المواد، استخدم scene.open() مع ObjLoadOptions:

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

options = ObjLoadOptions()
options.flip_coordinate_system = True  # Convert to Z-up if needed
options.enable_materials = True        # Load the accompanying .mtl file
options.normalize_normal = True

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

كلا النهجين ينتجان كائنًا متطابقًا Scene للخطوة اللاحقة للحفظ.


الخطوة 3: فحص المشهد المحمَّل

قبل الالتزام بالتحويل، من المفيد التحقق من أن الهندسة تم تحميلها بشكل صحيح. ملف مفقود، أو ميزة FBX غير مدعومة، أو مشكلة في المسار مع ملف .mtl يمكن أن تُنتج جميعها مشهدًا فارغًا.

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

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

mesh_count = 0
total_vertices = 0

def count_meshes(node) -> None:
    global mesh_count, total_vertices
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh_count += 1
            total_vertices += len(entity.control_points)
    for child in node.child_nodes:
        count_meshes(child)

count_meshes(scene.root_node)
print(f"Loaded {mesh_count} mesh(es), {total_vertices} total vertices")

if mesh_count == 0:
    raise ValueError("Scene contains no geometry: check the source file path and format")

الخطوة 4: حفظ إلى التنسيق المستهدف

استدعِ scene.save() مع مسار الإخراج. مرّر كائن خيارات الحفظ الخاص بالتنسيق للتحكم في الإخراج الثنائي مقابل ASCII، ومحاور الإحداثيات، والضغط.

OBJ إلى STL (ثنائي)

from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

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

save_opts = StlSaveOptions()
##StlSaveOptions defaults to binary output, which is more compact.

scene.save("model.stl", save_opts)
print("Saved model.stl")

OBJ إلى glTF 2.0

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

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

save_opts = GltfSaveOptions()

scene.save("model.gltf", save_opts)
print("Saved model.gltf")

لحفظه كملف ثنائي GLB متكامل بدلاً من .gltf + المخازن الخارجية، غيّر امتداد الإخراج إلى .glb:

scene.save("model.glb", save_opts)

OBJ إلى 3MF

from aspose.threed import Scene
from aspose.threed.formats import ThreeMfSaveOptions

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

save_opts = ThreeMfSaveOptions()

scene.save("model.3mf", save_opts)
print("Saved model.3mf")

STL إلى glTF 2.0

ينطبق النمط نفسه بغض النظر عن تنسيق المصدر:

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

scene = Scene.from_file("input.stl")
scene.save("output.gltf", GltfSaveOptions())
print("Saved output.gltf")

الخطوة 5: تحقق من الإخراج

بعد الحفظ، تأكد من وجود ملف الإخراج وأن حجمه غير صفر. لإجراء فحص أكثر شمولاً، أعد تحميله وقارن عدد الشبكات:

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

output_path = "model.stl"

##Basic file-system check
size = os.path.getsize(output_path)
print(f"Output file size: {size} bytes")
if size == 0:
    raise RuntimeError("Output file is empty: save may have failed silently")

##Round-trip verification: reload and count geometry
def _iter_nodes(node):
    yield node
    for child in node.child_nodes:
        yield from _iter_nodes(child)

reloaded = Scene.from_file(output_path)
mesh_count = sum(
    1
    for node in _iter_nodes(reloaded.root_node)
    for entity in node.entities
    if isinstance(entity, Mesh)
)
print(f"Round-trip check: {mesh_count} mesh(es) in output")

المشكلات الشائعة والحلول

تم إنشاء ملف الإخراج لكنه لا يحتوي على أي هندسة

قد يكون الملف المصدر قد تم تحميله بدون أي شبكات. أضف خطوة الفحص من الخطوة 3 قبل الحفظ. كما تأكد من أن امتداد الملف يتطابق مع الصيغة الفعلية؛ Aspose.3D يستخدم الامتداد لاختيار المحلل.

إخراج glTF يفتقد القوام

Aspose.3D FOSS يحمل خصائص الهندسة والمواد عبر التحويل. إذا كان ملف OBJ المصدر يشير إلى ملفات صور خارجية في .mtl، فإن تلك ملفات الصور لا تُنسخ تلقائيًا إلى جانب .gltf. قم بنسخ صور القوام إلى دليل الإخراج يدويًا بعد الحفظ.

مخرجات STL تبدو داخلية (اتجاهات الوجوه مقلوبة)

لا يحمل STL بيانات تعريف ترتيب اللف. إذا كانت المتجهات العمودية الناتجة مقلوبة، اضبط خيارات StlSaveOptions إذا كانت متاحة، أو اقلب نظام الإحداثيات أثناء التحميل: ObjLoadOptions.flip_coordinate_system = True.

ValueError: unsupported format عند الحفظ

تحقق من أن امتداد ملف الإخراج هو أحد .obj، .stl، .gltf، .glb، .dae، .3mf. الامتدادات حساسة لحالة الأحرف على نظام Linux.

الملفات الكبيرة جدًا تسبب بطء التحويل

تقوم Aspose.3D FOSS بمعالجة الهندسة في الذاكرة. بالنسبة للملفات التي تحتوي على ملايين المضلعات، تأكد من وجود ذاكرة RAM كافية. لا توجد حاليًا واجهة برمجة تطبيقات للكتابة المتدفقة.


الأسئلة المتكررة (FAQ)

هل يمكنني تحويل ملف دون كتابته على القرص أولاً؟

نعم. كل من scene.open() وscene.save() يقبلان كائنات شبيهة بالملف الثنائي بالإضافة إلى مسارات الملفات. مرّر أي كائن يطبق read() للتحميل أو write() للحفظ:

import io
from aspose.threed import Scene

# Load from an in-memory buffer
data = open('model.obj', 'rb').read()
scene = Scene()
scene.open(io.BytesIO(data))

# Save to an in-memory buffer
buf = io.BytesIO()
scene.save(buf)

هل يتم دعم FBX كتنسيق مصدر للتحويل؟

تم تنفيذ تجزئة FBX جزئيًا، لكن المحلل غير مكتمل. قد ينتج عن إدخال FBX مشاهد غير مكتملة. استخدم OBJ أو STL أو glTF أو COLLADA أو 3MF كتنسيقات مصدر موثوقة.

هل ستبقى المواد صالحة بعد تحويل OBJ إلى glTF؟

خصائص المادة الأساسية Phong/Lambert (لون الانتشار) يتم نقلها عبر نموذج Scene وتُكتب في كتلة مادة glTF. تُهمل معلمات الظل الإجرائية أو المخصصة التي لا يمكن التعبير عنها في نموذج مادة glTF.

هل يمكنني تحويل ملفات متعددة في حلقة؟

نعم. كل استدعاء Scene.from_file() ينشئ كائنًا مستقلاً، لذا فإن حلقة على قائمة من المسارات تكون مباشرةً:

from pathlib import Path
from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

source_dir = Path("input")
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

opts = StlSaveOptions()
for obj_file in source_dir.glob("*.obj"):
    scene = Scene.from_file(str(obj_file))
    out_path = output_dir / obj_file.with_suffix(".stl").name
    scene.save(str(out_path), opts)
    print(f"Converted {obj_file.name} -> {out_path.name}")

هل يحافظ التحويل على التسلسل الهرمي للمشهد (العقد الأصلية/الفرعية)؟

نعم. يتم الحفاظ على شجرة العقد بقدر ما يسمح به تنسيق الهدف. تنسيقات مثل STL تخزن فقط الهندسة المسطحة دون بنية عقد؛ يتم تسطيح التسلسل الهرمي عند الحفظ. تنسيقات مثل glTF و COLLADA تحتفظ بالتسلسل الهرمي الكامل.

 العربية