如何在 Python 中使用 Aspose.3D 构建 3D 网格
Aspose.3D FOSS for Python 让您完全在代码中构建 3D 几何体:无需外部建模工具。您创建一个 Mesh,用顶点位置(control_points)和面定义(polygons)填充它,附加可选的顶点属性,如法线,然后将场景保存为任何受支持的格式。
分步指南
步骤 1:安装软件包
从 PyPI 安装 Aspose.3D FOSS。无需本机扩展或编译工具链。
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,并可选择提供描述性名称:
from aspose.threed.entities import Mesh
mesh = Mesh("triangle")网格最初是空的:没有顶点,没有多边形。您将在以下步骤中填充它。
步骤 4:添加控制点(顶点)
控制点是顶点位置。每个顶点存储为 Vector4(x, y, z, w),其中 w=1 表示三维空间中的一点:
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 返回内部顶点列表的 副本(getter 执行 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 中的有效位置(基于 0,且在范围内)。对于外向法线,绕向顺序为逆时针。
步骤 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 是单精度浮点向量;VertexElementFVector 子类中的顶点属性数据使用此类型。
第7步:将 Mesh 附加到 Node 并保存
将网格添加到节点,然后保存场景:
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 exports with zero vertices | mesh.control_points.append(...) 静默丢弃顶点,因为该属性返回的是副本。请改用 mesh._control_points.append(...)。 |
| Normals count does not match vertex count | 使用 MappingMode.CONTROL_POINT + ReferenceMode.DIRECT 时,normals.data 必须恰好有 len(control_points) 条目。 |
| Mesh missing from saved file | 确认已在 scene.save() 之前调用了 node.add_entity(mesh)。未附加到任何节点的网格不会被导出。 |
| Wrong winding order (face appears invisible) | 逆时针的顶点顺序会产生指向外部的法线。请在 create_polygon 中反转索引顺序以翻转它。 |
polygon_count returns 0 | polygon_count 读取的列表与 polygons 相同。如果未调用 create_polygon,该列表为空。 |
| Normals appear incorrect in viewer | 确保所有法向量都是单位长度。使用 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,然后用 Vector4 条目填充其 data 列表。UV 坐标使用 x 和 y;z 和 w 通常为 0。第一个参数必须是一个 TextureMapping 常量(例如 TextureMapping.DIFFUSE),用于标识 UV 层所属的纹理槽。
网格需要法线才能正确导出吗?
不。法线是可选的。如果省略,大多数查看器会根据多边形的环绕顺序计算每个面的法线。添加显式的每顶点法线会产生更平滑的着色。
我能在一个节点上添加多个网格吗?
是的。多次调用 node.add_entity(mesh)。每次调用都会向 node.entities 添加一个新实体。某些格式在导出时可能会将多个实体合并为一个。
如何对混合多边形类型的网格进行三角化?
调用 mesh.triangulate() 将所有四边形和 N‑边形就地转换为三角形。这在保存到仅支持三角形的格式之前非常有用。