QML中加载obj文件的Mesh组件顶点坐标存储位置及获取方法
解决方案
顶点数据存储位置
你通过source属性加载的Mesh,内部会自动解析模型文件并生成QGeometry结构,所有顶点属性(位置、法线、纹理坐标等)都存储在Mesh.geometry.attributes列表中,其中顶点位置对应名称为Qt3DRender.Attribute.VertexPosition的Attribute对象,原始二进制数据存放在该Attribute关联的Buffer组件中。
实现步骤
1. 等待Mesh加载完成
Mesh加载模型是异步操作,必须等status变为Mesh.Ready后才能读取顶点数据,避免读取到空数据。
2. 预解析顶点位置缓存
Mesh加载完成后遍历attributes找到顶点位置属性,从关联的Buffer中读取二进制数据,解析为Vector3D数组存储备用。
3. 拾取时通过索引读取坐标
TrianglePicking模式下,PickEvent会返回vertexIndex0/vertexIndex1/vertexIndex2三个三角形顶点索引,直接用索引从预解析的顶点数组中取值即可;需要世界坐标的话,再乘上Transform的变换矩阵做空间转换。
修改后完整代码示例
// 注意先导入对应版本的Qt3D.Render模块 import Qt3D.Render 2.15 Entity{ id: part0Entity; objectName: "part0Entity"; enabled: true; // 定义属性存储解析好的顶点局部坐标 property var vertexPositions: [] Connections{ target: visEnt0; onClicked:{ part0Entity.enabled = !part0Entity.enabled; } } Mesh{ id: part0Mesh; source: "file:/home/zhaoyiji/Desktop/SF6-C1400/part0.obj" // 监听加载状态,完成后解析顶点数据 onStatusChanged: { if (status !== Mesh.Ready) return let positionAttr = null // 遍历属性列表查找顶点位置属性 for (let i = 0; i < geometry.attributes.length; i++) { const attr = geometry.attributes[i] if (attr.name === Attribute.VertexPosition) { positionAttr = attr break } } if (!positionAttr) return const buffer = positionAttr.buffer const rawData = buffer.data() const stride = positionAttr.byteStride || 12 // 默认3个float共12字节 const offset = positionAttr.byteOffset vertexPositions = [] // 逐顶点解析坐标 for (let i = 0; i < positionAttr.count; i++) { const byteIdx = offset + i * stride const x = rawData.getFloat32(byteIdx, true) const y = rawData.getFloat32(byteIdx + 4, true) const z = rawData.getFloat32(byteIdx + 8, true) vertexPositions.push(Qt.vector3d(x, y, z)) } } } Transform{ id: part0Transform; matrix: Qt.matrix4x4(); } DiffuseSpecularMaterial{ id: part0Diffuse; ambient: "#80DC143C"; } ObjectPicker{ id: part0Picker; dragEnabled: true; onClicked: { if (pick.button == PickEvent.MiddleButton){ // 读取选中三角形的三个顶点局部坐标 const v0Local = vertexPositions[pick.vertexIndex0] const v1Local = vertexPositions[pick.vertexIndex1] const v2Local = vertexPositions[pick.vertexIndex2] console.log("局部坐标:", v0Local, v1Local, v2Local) // 转换为世界坐标 const v0World = part0Transform.matrix.map(v0Local) const v1World = part0Transform.matrix.map(v1Local) const v2World = part0Transform.matrix.map(v2Local) console.log("世界坐标:", v0World, v1World, v2World) } } } components: [part0Mesh, part0Transform, part0Diffuse, part0Picker] }
注意事项
- 如果读取到的顶点坐标数值异常,可尝试调整
getFloat32的第二个参数(小端序开关),部分特殊导出的obj可能使用大端序存储 - 若模型包含多个子网格,可遍历
Mesh.geometry.children获取每个子网格的几何数据,对应索引范围参考子网格的boundingVolume属性 - 顶点坐标默认是模型局部空间坐标,存在缩放、平移、旋转时必须乘Transform矩阵才能得到世界空间下的实际位置
内容的提问来源于stack exchange,提问作者ZYJ
相关产品推荐
相关产品推荐

