//爆炸拆解传入模型
explodeModel(obj, val) {
if (!obj) return;
const center = this.getCenterPoint(obj);
let y = obj.position.y;
obj.traverse((child) => {
if (child.isMesh) {
// 保存原始位置
if (!child.userData.originalPosition) {
child.userData.originalPosition = child.position.clone();
}
// 计算相对中心的位置
const originalPosition = child.userData.originalPosition;
const direction = originalPosition.clone().sub(center).normalize();
// 计算目标位置,基于原始位置加上方向向量乘以拆解系数
const targetPosition = originalPosition
.clone()
.add(direction.multiplyScalar((val - 1) * 5));
// 创建动画
new TWEEN.Tween(child.position)
.to(
{
x: targetPosition.x,
y: originalPosition.y,
z: targetPosition.z,
},
1000
)
.easing(TWEEN.Easing.Quadratic.InOut)
.onUpdate(() => { })
.start();
}
});
},
//计算传入模型包围盒的中心点
getCenterPoint(obj) {
//包围盒
const box = new THREE.Box3().setFromObject(obj);
var center = new THREE.Vector3();
//获取包围盒的中心点
box.getCenter(center);
return center;
},
剖切需在渲染器设置localClippingEnabled为true
/**
* 轴剖切传入模型 同时存在多个剖切面 需在渲染器设置localClippingEnabled
* @param {Object3D} modelObj 模型
* @param {Array} clippingValue 滑块值 数组
* @param {Array} axes 轴 数组
*
*/
applyClipping(modelObj, clippingValues, axes) {
if (!modelObj || !Array.isArray(clippingValues) || !Array.isArray(axes)) return;
// 确保剖切值数组和轴数组的长度相同
if (clippingValues.length !== axes.length) {
console.error("clippingValues和 axes 长度必须相同");
return;
}
// 创建剖切平面数组
const clippingPlanes = [];
for (let i = 0; i < axes.length; i++) {
// 确保剖切值在允许的范围内
const value = Math.max(-300, Math.min(clippingValues[i], 300));
let plane;
if (axes[i] === "x") {
plane = new THREE.Plane(new THREE.Vector3(1, 0, 0), -value);
} else if (axes[i] === "z") {
plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), -value);
}
if (plane) {
clippingPlanes.push(plane);
}
}
// 更新模型的剖切平面
modelObj.traverse((child) => {
if (child.isMesh) {
child.material.clippingPlanes = clippingPlanes;
child.material.needsUpdate = true;
}
});
// 创建和更新剖切平面的辅助器
if (planeHelpers) {
planeHelpers.forEach((helper) => scene.remove(helper));
}
planeHelpers = clippingPlanes.map((plane) => {
const helper = new THREE.PlaneHelper(plane, 100, 0xffffff); // 使用更大的尺寸以覆盖整个模型
// scene.add(helper);
return helper;
});
},
标尺
//传入模型显示标尺
addBoundingBoxScale(model) {
// 清除之前的标尺
if (linesAndArrows) this.clearScale();
const boundingBox = new THREE.Box3().setFromObject(model);
const size = boundingBox.getSize(new THREE.Vector3());
const center = boundingBox.getCenter(new THREE.Vector3());
// 标尺线段的偏移量和箭头大小
const offset = 30; // 可以根据需要调整偏移量
const arrowLength = 3; // 箭头的长度
const arrowRadius = 0.8; // 箭头的半径
const lineWidth = 4; // 线段的粗细
// 创建线段
const createScaleLine = (start, end, material) => {
const geometry = new THREE.BufferGeometry().setFromPoints([start, end]);
return new THREE.Line(geometry, material);
};
// 创建箭头
const createArrow = (dir, origin) => {
arrowGeometry = new THREE.ConeGeometry(arrowRadius, arrowLength, 32); // 增大了箭头的细节
arrowMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
arrowMesh = new THREE.Mesh(arrowGeometry, arrowMaterial);
arrowMesh.position.copy(origin);
arrowMesh.lookAt(dir);
arrowMesh.rotateX(Math.PI / 2); // 根据需要调整箭头的方向
return arrowMesh;
};
// 创建材质,增加了线段的粗细
materialLine = new THREE.LineBasicMaterial({
color: 0xffffff,
linewidth: lineWidth,
});
// 创建线段和箭头
linesAndArrows = {
width: {
line: createScaleLine(
new THREE.Vector3(center.x + size.x / 2, (size.y / 2) + offset, center.z * 3),
new THREE.Vector3(center.x - size.x / 2, (size.y / 2) + offset, center.z * 3),
materialLine
),
arrow1: createArrow(
new THREE.Vector3(
center.x - size.x / 2 - arrowLength,
(size.y / 2) + offset,
center.z * 3
),
new THREE.Vector3(center.x - size.x / 2, (size.y / 2) + offset, center.z * 3)
),
arrow2: createArrow(
new THREE.Vector3(
center.x + size.x / 2 + arrowLength,
(size.y / 2) + offset,
center.z * 3
),
new THREE.Vector3(center.x + size.x / 2, (size.y / 2) + offset, center.z * 3)
),
},
height: {
line: createScaleLine(
new THREE.Vector3(
center.x - size.x / 2 - offset,
center.y - size.y / 2,
center.z
),
new THREE.Vector3(
center.x - size.x / 2 - offset,
center.y + size.y / 2,
center.z
),
materialLine
),
arrow1: createArrow(
new THREE.Vector3(
center.x - size.x / 2 - offset,
center.y - size.y / 2 - arrowLength,
center.z
),
new THREE.Vector3(
center.x - size.x / 2 - offset,
center.y - size.y / 2,
center.z
)
),
arrow2: createArrow(
new THREE.Vector3(
center.x - size.x / 2 - offset,
center.y + size.y / 2 + arrowLength,
center.z
),
new THREE.Vector3(
center.x - size.x / 2 - offset,
center.y + size.y / 2,
center.z
)
),
},
depth: {
line: createScaleLine(
new THREE.Vector3(
center.x + size.x / 2 + offset,
size.y / 2,
center.z - size.z / 2
),
new THREE.Vector3(
center.x + size.x / 2 + offset,
size.y / 2,
center.z + size.z / 2
),
materialLine
),
arrow1: createArrow(
new THREE.Vector3(
center.x + size.x / 2 + offset,
size.y / 2,
center.z - size.z / 2 - arrowLength
),
new THREE.Vector3(
center.x + size.x / 2 + offset,
size.y / 2,
center.z - size.z / 2
)
),
arrow2: createArrow(
new THREE.Vector3(
center.x + size.x / 2 + offset,
size.y / 2,
center.z + size.z / 2 + arrowLength
),
new THREE.Vector3(
center.x + size.x / 2 + offset,
size.y / 2,
center.z + size.z / 2
)
),
},
};
// 创建标尺文字
const createText = (text, position) => {
const loader = new FontLoader();
loader.load("/font/helvetiker_regular.typeface.json", function (font) {
const textGeometry = new TextGeometry(text, {
font: font,
size: 6,
height: 0.05,
});
const textMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });
textMesh = new THREE.Mesh(textGeometry, textMaterial);
// 调整位置
textMesh.position.set(position.x, position.y, position.z);
textMesh.visible = false;
scene.add(textMesh);
TextMeshes.push(textMesh);
});
};
//添加标签到线段中点
const addLabelToLine = (line) => {
const start = new THREE.Vector3().fromBufferAttribute(
line.geometry.attributes.position,
0
);
const end = new THREE.Vector3().fromBufferAttribute(
line.geometry.attributes.position,
1
);
const midPoint = new THREE.Vector3().lerpVectors(start, end, 0.5);
const distance = start.distanceTo(end).toFixed(2); // 计算距离并保留两位小数
const distanceText = `${distance}m`; // 创建距离文本
createText(distanceText, midPoint); // 在中点位置创建文本标签
};
// 添加线段和箭头到场景
Object.values(linesAndArrows).forEach(({ line, arrow1, arrow2 }) => {
addLabelToLine(line);
scene.add(line);
scene.add(arrow1);
scene.add(arrow2);
});
// 开启标尺
const showScale = () => {
scale = true
Object.values(linesAndArrows).forEach(({ line, arrow1, arrow2 }) => {
// 设置初始状态
line.scale.set(0, 0, 0); // 将线段长度缩放到0
arrow1.visible = false; // 隐藏箭头
arrow2.visible = false; // 隐藏箭头
// 创建缩放动画
new TWEEN.Tween(line.scale)
.to({ x: 1, y: 1, z: 1 }, 1300) // 1秒内将线段长度缩放到1(原始长度)
.easing(TWEEN.Easing.Quadratic.Out)
.onStart(() => {
line.visible = true; // 开始动画时显示线段
})
.onUpdate(() => {
// 动画更新时调整箭头位置
arrow1.position.x =
line.geometry.attributes.position.array[3] * line.scale.x;
arrow2.position.x =
line.geometry.attributes.position.array[0] * line.scale.x;
})
.onComplete(() => {
arrow1.visible = true; // 动画完成后显示箭头
arrow2.visible = true; // 动画完成后显示箭头
TextMeshes.forEach((textMesh) => {
textMesh.visible = true;
});
})
.start();
});
};
// 关闭标尺
const hideScale = () => {
if (!scale) return
scale = false
Object.values(linesAndArrows).forEach(({ line, arrow1, arrow2 }) => {
// 创建缩放动画
new TWEEN.Tween(line.scale)
.to({ x: 0, y: 0, z: 0 }, 800) // 1秒内将线段长度缩放到0
.easing(TWEEN.Easing.Quadratic.Out)
.onUpdate(() => {
// 动画更新时调整箭头位置
TextMeshes.forEach((textMesh) => {
textMesh.visible = false;
});
arrow1.position.x =
line.geometry.attributes.position.array[3] * line.scale.x;
arrow2.position.x =
line.geometry.attributes.position.array[0] * line.scale.x;
arrow1.visible = false;
arrow2.visible = false;
})
.onComplete(() => {
arrow1.visible = false; // 动画完成后隐藏箭头
arrow2.visible = false; // 动画完成后隐藏箭头
line.visible = false; // 动画完成后隐藏线段
})
.start();
});
};
return { showScale, hideScale };
},
//清除标尺
clearScale() {
// 移除所有标尺线、箭头和文本
linesAndArrows.width.line && scene.remove(linesAndArrows.width.line);
linesAndArrows.width.arrow1 && scene.remove(linesAndArrows.width.arrow1);
linesAndArrows.width.arrow2 && scene.remove(linesAndArrows.width.arrow2);
linesAndArrows.height.line && scene.remove(linesAndArrows.height.line);
linesAndArrows.height.arrow1 && scene.remove(linesAndArrows.height.arrow1);
linesAndArrows.height.arrow2 && scene.remove(linesAndArrows.height.arrow2);
linesAndArrows.depth.line && scene.remove(linesAndArrows.depth.line);
linesAndArrows.depth.arrow1 && scene.remove(linesAndArrows.depth.arrow1);
linesAndArrows.depth.arrow2 && scene.remove(linesAndArrows.depth.arrow2);
TextMeshes.forEach(textMesh => {
scene.remove(textMesh);
textMesh.geometry.dispose();
textMesh.material.dispose();
});
TextMeshes = [];
},
用到的变量和库自行创建引入