Как загрузить 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 класс является контейнером верхнего уровня для всех 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 объекты (meshes, cameras, lights). Проверьте 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 vs Z‑up, левая‑рука vs правая‑рука). Установите ObjLoadOptions.flip_coordinate_system = True или применить вращение к корневому узлу Transform после загрузки.

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

Список сущностей узла может содержать не мешевые сущности (камеры, источники света). Всегда проверяйте с помощью isinstance(entity, Mesh) перед приведением типа.


Часто задаваемые вопросы (FAQ)

Какие 3D‑форматы я могу загружать?

OBJ (Wavefront), STL (binary и 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 между потоками без внешней блокировки.

 Русский