Як створити 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 повертає 0 | polygon_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‑гони у трикутники. Це корисно перед збереженням у формати, які підтримують лише трикутники.