Как да заредим 3D модели в Python

Как да заредим 3D модели в Python

Aspose.3D FOSS for Python предоставя прост API за отваряне на 3D файлове без никакви native зависимости. След като заредите файл в обект 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 Class

Класът 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 обекти (мрежи, камери, светлини). Проверете 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 (binary and ASCII), glTF 2.0 / GLB, COLLADA (DAE) и 3MF. Токенизирането на FBX файлове се поддържа частично, но пълният парсинг все още не е завършен.

Зарежда ли зареждането на OBJ файл също и материала .mtl?

Да, когато ObjLoadOptions.enable_materials = True (по подразбиране). Библиотеката търси файла .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 между нишките без външно заключване.

 Български