// 加载材质
const mtlLoader = new MTLLoader();
mtlLoader.load(model_mtl, (materials) => {
materials.preload(); // 预加载材质
// 加载 OBJ 模型
const objLoader = new OBJLoader();
objLoader.setMaterials(materials); // 设置加载的材质
objLoader.load(model_obj, (object) => {
scene.add(object); // 将模型添加到场景中
const volume = this.calculateVolume(object.children[0].geometry);
console.log(`模型的体积为: ${volume}`);
})
})
calculateVolume(geometry) {
let volume = 0;
const position = geometry.attributes.position.array; // 顶点位置数据
// 如果几何体有索引数据,则需要通过索引访问顶点
const indices = geometry.index ? geometry.index.array : null;
const vec1 = new THREE.Vector3();
const vec2 = new THREE.Vector3();
const vec3 = new THREE.Vector3();
const crossProduct = new THREE.Vector3();
if (indices) {
// 按索引处理三角形
for (let i = 0; i < indices.length; i += 3) {
// 顶点索引
const a = indices[i] * 3;
const b = indices[i + 1] * 3;
const c = indices[i + 2] * 3;
// 获取三角形顶点
vec1.set(position[a], position[a + 1], position[a + 2]);
vec2.set(position[b], position[b + 1], position[b + 2]);
vec3.set(position[c], position[c + 1], position[c + 2]);
// 计算三角形的体积
crossProduct.crossVectors(vec2.clone().sub(vec1), vec3.clone().sub(vec1));
volume += vec1.dot(crossProduct) / 6;
}
} else {
// 如果没有索引,直接按顺序处理顶点数据
for (let i = 0; i < position.length; i += 9) {
// 每三个顶点形成一个三角形
vec1.set(position[i], position[i + 1], position[i + 2]);
vec2.set(position[i + 3], position[i + 4], position[i + 5]);
vec3.set(position[i + 6], position[i + 7], position[i + 8]);
// 计算三角形的体积
crossProduct.crossVectors(vec2.clone().sub(vec1), vec3.clone().sub(vec1));
volume += vec1.dot(crossProduct) / 6;
}
}
return Math.abs(volume); // 返回体积的绝对值
}
注意事项:
- 单位与比例:
如果你的模型使用了缩放(scale),需要在计算体积后乘以 scale.x * scale.y * scale.z 的立方值,确保体积与实际大小一致:
const scale = object.scale;
const scaledVolume = volume * scale.x * scale.y * scale.z;
console.log(`缩放后的模型体积为: ${scaledVolume}`);
- 几何体类型:
此方法适用于 BufferGeometry 类型的几何体。如果你的几何体是 Geometry 类型,请先将其转换为 BufferGeometry:
geometry = new THREE.BufferGeometry().fromGeometry(geometry);
- 法线方向:
如果某些三角形的法线方向错误(顶点顺序导致计算结果为负),最终结果仍然是正确的,因为 Math.abs 会处理负值。