Як завантажити 3D‑моделі у Python

Як завантажити 3D‑моделі у Python

Aspose.3D FOSS для Python надає простий API для відкриття 3D‑файлів без будь‑яких нативних залежностей. Після завантаження файлу у Scene об’єкт, ви можете пройти ієрархію вузлів і прочитати необроблені дані геометрії для кожної сітки в сцені.

Покроковий посібник

Крок 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: Завантажте файл

Використовуйте статичний 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.


Крок 4: Обійдіть вузли сцени

Завантажена сцена — це дерево 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‑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 (за замовчуванням). Бібліотека шукає the .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

Чи є бібліотека потокобезпечною для одночасного завантаження кількох файлів?

Кожен Scene об’єкт є незалежним. Завантаження окремих файлів у окремі Scene екземпляри з окремих потоків безпечні, доки ви не ділите один Scene через потоки без зовнішнього блокування.

 Українська