threejs模型拆解、剖切、标尺

//爆炸拆解传入模型
        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 = [];
        },

用到的变量和库自行创建引入

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值