Як створити 3D‑меш за допомогою Aspose.3D у Python

Як створити 3D‑меш за допомогою Aspose.3D у Python

Aspose.3D FOSS for Python дозволяє створювати 3D‑геометрію повністю в коді: не потрібен зовнішній інструмент моделювання. Ви створюєте Mesh, заповнюєте його позиціями вершин (control_points) та визначеннями граней (polygons), додаєте необов’язкові атрибути вершин, такі як нормалі, а потім зберігаєте сцену в будь‑якому підтримуваному форматі.

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

Крок 1: Встановити пакет

Встановіть Aspose.3D FOSS з PyPI. Нативні розширення або інструментарій компілятора не потрібні.

pip install aspose-3d-foss

Перевірте встановлення:

from aspose.threed import Scene
print("Aspose.3D FOSS ready")

Підтримувані версії Python: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


Крок 2: Створити сцену та вузол

Кожна сітка повинна знаходитися всередині графа сцени. Створіть Scene і додайте іменований Node для розміщення сітки:

from aspose.threed import Scene

scene = Scene()
node = scene.root_node.create_child_node("triangle")

Назва вузла зберігається у експортованому файлі і корисна для налагодження та подальшого отримання через node.get_child("triangle").


Крок 3: Створити Mesh Object

Створіть Mesh з необов’язковою описовою назвою:

from aspose.threed.entities import Mesh

mesh = Mesh("triangle")

Сітка спочатку порожня: немає вершин, немає полігонів. Ви заповнюєте її наступними кроками.


Крок 4: Додати контрольні точки (вершини)

Контрольні точки — це позиції вершин. Кожна вершина зберігається як Vector4(x, y, z, w), де w=1 вказує на точку у 3D‑просторі:

from aspose.threed.utilities import Vector4

##Vertex 0: origin
# Note: control_points returns a copy of the internal vertex list.
# Appending to the returned copy discards the vertex silently.
# Use _control_points to mutate the backing list directly.
# This is a known library limitation — a public add_control_point() API is not yet available.
mesh._control_points.append(Vector4(0.0, 0.0, 0.0, 1.0))

##Vertex 1: 1 unit along X
mesh._control_points.append(Vector4(1.0, 0.0, 0.0, 1.0))

##Vertex 2: apex
mesh._control_points.append(Vector4(0.5, 1.0, 0.0, 1.0))

print(f"Vertices added: {len(mesh.control_points)}")

Важливо: mesh.control_points повертає копію внутрішнього списку вершин (геттер виконує list(self._control_points)). Виклик mesh.control_points.append(v) додає до копії, а не до сітки, тому вершина без повідомлення відкидається. Завжди використовуйте mesh._control_points.append(v) для додавання вершин. Доступ до приватного стану через _control_points є відомим обходом; інтерфейс може змінитися у майбутній версії бібліотеки.


Крок 5: Створити грані полігонів

Визначте топологію грані за допомогою індексів вершин. Передайте індекси вершин у create_polygon(). Три індекси утворюють трикутник; чотири — чотирикутник:

##Triangle: connect vertices 0 → 1 → 2
mesh.create_polygon(0, 1, 2)

print(f"Polygon count: {mesh.polygon_count}")

Для сітки з чотирикутників ви передасте чотири індекси: mesh.create_polygon(0, 1, 2, 3).

Індекси мають бути дійсними позиціями у control_points (нульовий індекс, в межах діапазону). Порядок обходу – проти годинникової стрілки для зовнішньо орієнтованих нормалей.


Крок 6: Додати нормалі вершин

Нормалі вершин зберігаються як VertexElement, прикріплений до меша. Використовуйте mesh.create_element() разом з VertexElementType.NORMAL, MappingMode.CONTROL_POINT та ReferenceMode.DIRECT:

from aspose.threed.entities import VertexElementType, MappingMode, ReferenceMode, VertexElementNormal
from aspose.threed.utilities import Vector4, FVector4

##Create the normal element (returns VertexElementNormal, a VertexElementFVector subclass)
normals: VertexElementNormal = mesh.create_element(
    VertexElementType.NORMAL,
    MappingMode.CONTROL_POINT,
    ReferenceMode.DIRECT
)

##One normal per vertex: all pointing out of the XY plane (0, 0, 1)
normals.set_data([
    FVector4(0, 0, 1, 0),   # vertex 0
    FVector4(0, 0, 1, 0),   # vertex 1
    FVector4(0, 0, 1, 0),   # vertex 2
])

print("Normal layer attached.")

MappingMode.CONTROL_POINT означає один нормаль на вершину. ReferenceMode.DIRECT означає, що дані нормалей читаються в тому ж порядку, що й контрольні точки (без додаткового індексного буфера).

Нормальні вектори використовують FVector4(x, y, z, w) разом з w=0, щоб вказувати напрямок, а не позицію. FVector4 — це вектор з одинарною точністю (float); дані атрибутів вершин у підкласах VertexElementFVector використовують цей тип.


Крок 7: Прикріпити сітку до вузла та зберегти

Додайте сітку до вузла, потім збережіть сцену:

node.add_entity(mesh)

scene.save("triangle.gltf")
print("Saved triangle.gltf")

Повний робочий скрипт (усі кроки разом):

from aspose.threed import Scene
from aspose.threed.entities import Mesh, VertexElementType, MappingMode, ReferenceMode, VertexElementNormal
from aspose.threed.utilities import Vector3, Vector4, FVector4

scene = Scene()
node = scene.root_node.create_child_node("triangle")

mesh = Mesh("triangle")

##Add 3 vertices (x, y, z, w)
# Use _control_points to mutate the backing list directly (control_points returns a copy)
mesh._control_points.append(Vector4(0.0, 0.0, 0.0, 1.0))
mesh._control_points.append(Vector4(1.0, 0.0, 0.0, 1.0))
mesh._control_points.append(Vector4(0.5, 1.0, 0.0, 1.0))

##Create a triangle polygon
mesh.create_polygon(0, 1, 2)

##Add normals (create_element returns VertexElementNormal, a VertexElementFVector subclass)
normals: VertexElementNormal = mesh.create_element(VertexElementType.NORMAL, MappingMode.CONTROL_POINT, ReferenceMode.DIRECT)
normals.set_data([
    FVector4(0, 0, 1, 0),
    FVector4(0, 0, 1, 0),
    FVector4(0, 0, 1, 0),
])

node.add_entity(mesh)
scene.save("triangle.gltf")

Типові проблеми

ПроблемаРішення
IndexError у create_polygonПеревірте, що всі індекси знаходяться в межах range(len(mesh.control_points)). Індекси нульові (починаються з 0).
Експорт сітки з нульовою кількістю вершинmesh.control_points.append(...) без повідомлення відкидає вершини, оскільки властивість повертає копію. Використовуйте mesh._control_points.append(...) замість цього.
Кількість нормалей не відповідає кількості вершинПри використанні MappingMode.CONTROL_POINT + ReferenceMode.DIRECT, normals.data має містити точно len(control_points) записів.
Сітка відсутня у збереженому файліПереконайтеся, що node.add_entity(mesh) був викликаний до scene.save(). Сітка, не приєднана до жодного вузла, не експортується.
Неправильний порядок обходу (поверхня здається невидимою)Протилежний (проти годинникової стрілки) порядок вершин створює нормаль, спрямовану назовні. Змініть порядок індексів у create_polygon, щоб інвертувати його.
polygon_count повертає 0polygon_count читає той самий список, що й polygons. Якщо create_polygon не був викликаний, список порожній.
Нормалі виглядають некоректно у переглядачіПереконайтеся, що всі вектори нормалей мають одиничну довжину. Обчисліть їх за допомогою n / abs(n) або передайте попередньо нормалізовані значення.

Поширені запитання

У чому різниця між Vector3 та Vector4 для контрольних точок?

control_points зберігає Vector4 об’єкти. Компонент w є однорідною координатою: використовуйте w=1 для позицій вершин і w=0 для векторів напрямку, таких як нормалі. Vector3 використовується для перетворень (переміщення, масштаб), але не для зберігання геометрії.

Чи можу я створити сітку з чотирикутників замість трикутників?

Так. Викличте mesh.create_polygon(0, 1, 2, 3) з чотирма індексами, щоб визначити чотирикутник. Деякі цілі збереження (STL, 3MF) вимагають трикутників і автоматично триангулят чотирикутники. glTF і COLLADA зберігають чотирикутники.

Як додати UV-координати?

Використайте mesh.create_element_uv(TextureMapping.DIFFUSE, MappingMode.POLYGON_VERTEX), щоб створити VertexElementUV для дифузного каналу, а потім заповніть його список data з Vector4 записами. Координати UV використовують x і y; z і w зазвичай дорівнюють 0. Перший аргумент має бути константою TextureMapping (наприклад, TextureMapping.DIFFUSE), що визначає, до якого слоту текстури належить шар UV.

Чи потрібні нормалі у сітці для правильного експорту?

Ні. Нормалі є необов’язковими. Якщо їх опустити, більшість переглядачів обчислює нормалі для кожної грані за порядком обходу полігонів. Додавання явних нормалей для кожної вершини забезпечує більш плавне затінення.

Чи можу я додати кілька мешів до одного вузла?

Так. Викликайте node.add_entity(mesh) кілька разів. Кожен виклик додає нову сутність до node.entities. Деякі формати можуть згладжувати кілька сутностей в одну під час експорту.

Як я можу триангуляти сітку зі змішаними типами полігонів?

Викличте mesh.triangulate(), щоб на місці перетворити всі квадрати та N‑гони у трикутники. Це корисно перед збереженням у формати, які підтримують лише трикутники.

 Українська