Cara Memuat Model 3D di Python

Cara Memuat Model 3D di Python

Aspose.3D FOSS for Python menyediakan API yang sederhana untuk membuka file 3D tanpa ketergantungan native apa pun. Setelah memuat file ke dalam objek Scene, Anda dapat menelusuri hierarki node dan membaca data geometri mentah untuk setiap mesh dalam adegan.

Panduan Langkah-demi-Langkah

Langkah 1: Instal Paket

Instal Aspose.3D FOSS dari PyPI. Tidak diperlukan pustaka sistem tambahan.

pip install aspose-3d-foss

Versi Python yang didukung: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


Langkah 2: Impor Kelas Scene

Kelas Scene adalah kontainer tingkat atas untuk semua data 3D. Impor kelas tersebut bersama dengan kelas opsi‑muat apa pun yang Anda perlukan.

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

Semua kelas publik berada di bawah aspose.threed atau sub‑paketnya (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).


Langkah 3: Muat File

Gunakan metode statis Scene.from_file() untuk membuka format apa pun yang didukung. Perpustakaan mendeteksi format secara otomatis dari ekstensi file.

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

Sebagai alternatif, buat instance Scene dan panggil open(); berguna ketika Anda ingin melewatkan opsi pemuatan atau menangani kesalahan secara eksplisit:

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

Kedua metode mendukung file OBJ, STL (biner dan ASCII), glTF 2.0 / GLB, COLLADA (DAE), dan 3MF.


Langkah 4: Jelajahi Node Adegan

Sebuah scene yang dimuat adalah pohon objek Node yang berakar pada scene.root_node. Iterasikan secara rekursif untuk menemukan semua 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)

Setiap Node dapat membawa nol atau lebih objek Entity (meshes, kamera, lampu). Periksa node.entities untuk melihat apa yang terpasang.


Langkah 5: Akses Data Vertex dan Poligon

Ubah entitas node menjadi Mesh dan baca titik kontrolnya (posisi vertex) serta poligon (daftar indeks wajah):

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 adalah daftar objek Vector4; x, y, z membawa posisi dan w adalah koordinat homogen (biasanya 1.0).

mesh.polygons adalah daftar daftar bilangan bulat, di mana setiap daftar dalamnya adalah himpunan terurut indeks control-point untuk satu wajah.


Langkah 6: Terapkan Opsi Muat Spesifik Format

Untuk kontrol yang sangat detail atas cara file OBJ diinterpretasikan, berikan instance ObjLoadOptions ke 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)

Untuk file STL, kelas yang setara adalah StlLoadOptions. Untuk glTF, gunakan GltfLoadOptions. Lihat referensi API untuk daftar lengkap.


Masalah Umum dan Solusi

FileNotFoundError saat memanggil Scene.from_file()

Jalur harus bersifat absolut atau relatif yang benar terhadap direktori kerja pada saat runtime. Gunakan pathlib.Path untuk membuat jalur yang dapat diandalkan:

from pathlib import Path
from aspose.threed import Scene

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

mesh.polygons kosong setelah memuat file STL

File STL menyimpan segitiga sebagai faset mentah, bukan mesh terindeks. Setelah dimuat, poligon disintesis dari faset-faset tersebut. Jika polygons muncul kosong, periksa len(mesh.control_points); jika hitungannya merupakan kelipatan 3, geometri disimpan dalam bentuk tidak terindeks dan setiap tiga titik berturut‑turut membentuk satu segitiga.

Ketidaksesuaian sistem koordinat (model tampak diputar atau dicerminkan)

Berbagai alat menggunakan konvensi yang berbeda (Y-up vs Z-up, tangan kiri vs tangan kanan). Atur ObjLoadOptions.flip_coordinate_system = True atau terapkan rotasi pada Transform node akar setelah memuat.

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

Daftar entitas node dapat berisi entitas non‑mesh (kamera, lampu). Selalu lindungi dengan isinstance(entity, Mesh) sebelum melakukan casting.


Pertanyaan yang Sering Diajukan (FAQ)

Format 3D apa yang dapat saya muat?

OBJ (Wavefront), STL (binary dan ASCII), glTF 2.0 / GLB, COLLADA (DAE), dan 3MF. Tokenisasi file FBX didukung sebagian tetapi parsing lengkap belum selesai.

Apakah memuat file OBJ juga memuat material .mtl?

Ya, ketika ObjLoadOptions.enable_materials = True (default). Perpustakaan mencari file .mtl di direktori yang sama dengan file .obj. Jika .mtl tidak ada, geometri tetap dimuat dan peringatan dikeluarkan.

Bisakah saya memuat file dari aliran byte alih-alih jalur?

Ya. scene.open() menerima objek mirip file apa pun dengan metode .read() selain string jalur file. Lewatkan aliran biner terbuka (mis., io.BytesIO) secara langsung. Scene.from_file() hanya menerima string jalur file.

Bagaimana cara saya mendapatkan normal permukaan?

Setelah memuat, periksa mesh.get_element(VertexElementType.NORMAL). Ini mengembalikan sebuah VertexElementNormal yang daftar data‑nya berisi satu vektor normal per referensi, dipetakan sesuai mapping_mode dan reference_mode.

from aspose.threed.entities import Mesh, VertexElementType

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

Apakah perpustakaan ini thread-safe untuk memuat beberapa file secara bersamaan?

Setiap Scene objek bersifat independen. Memuat file terpisah ke dalam Scene instance yang terpisah dari thread yang berbeda aman selama Anda tidak membagikan satu Scene antar thread tanpa penguncian eksternal.

 Bahasa Indonesia