如何在 Python 中加载 3D 模型
Aspose.3D FOSS for Python 提供了一个直接的 API,用于在没有任何本地依赖的情况下打开 3D 文件。将文件加载到一个 Scene 对象后,您可以遍历节点层次结构,并读取场景中每个网格的原始几何数据。.
分步指南
步骤 1:安装包
从 PyPI 安装 Aspose.3D FOSS。无需额外的系统库。.
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:遍历 Scene 节点
已加载的场景是一个由 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 向上 vs Z 向上,左手坐标系 vs 右手坐标系)。设置 ObjLoadOptions.flip_coordinate_system = True 或对根节点进行旋转 Transform 加载后。.
AttributeError: 'NoneType' object has no attribute 'polygons'
节点的实体列表可能包含非网格实体(相机、灯光)。始终使用 isinstance(entity, Mesh) 在强制转换之前。.
常见问题 (FAQ)
我可以加载哪些 3D 格式??
OBJ(Wavefront)、STL(二进制和 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 跨线程而不进行外部锁定。.