Как загрузить 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 между потоками без внешней блокировки.