three 组合模型

three 同步导入glb模型,组合模型,点击模型弹窗


vue代码

实现效果为同步加载模型计算当前模型的Y轴高度,并且把模型按Y轴重叠在一起,点击可弹出对应消息弹框,弹框可随轨道控制器移动,并且窗口为自适应,

monitorThree
在这里插入图片描述

<template>
  <div id="threeBox">
    <div :style="cLheight" ref="three" id="ThreeId233"></div>
    <!-- <div id="myStats"></div> -->
    <a-card
      id="modal"
      size="small"
      showDia
      v-show="false"
      :title="showModelDetail.bhaName"
      style="width: 200px; height: 90px"
    >
      <p>型号:{{ showModelDetail.bhaModel }}</p>
      <p>尺寸:{{ showModelDetail.bhaSpec }}</p>
      <p>长度:{{ showModelDetail.bhaLength }}</p>
    </a-card>
  </div>
</template>

<script>
import * as THREE from "three";
import { BoxGeometry, MeshLambertMaterial, Scene } from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; // 轨道控制器  可以用鼠标进行放大缩小 调整方位
import * as Stats from "stats.js"; // 检测动画运行帧数
import * as dat from "dat.gui"; // datGui 可以为我们简化实验
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";

import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";

import { selBhaDetail } from "@/services/monitor/index.js";

import {
  CSS2DRenderer,
  CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
export default {
  props: ["cLheight", "ThreeId", "sidebarItem", "bhaId2"],
  data() {
    return {
      glftModel: "",
      renderer: null,
      close: true,
      scene: null,
      camera: null,
      labelRenderer: null,
      OrbitControlsAr: null,
      showDia: false,
      material: "",
      y: 0,
      group: null,
      DirectionalLight: null,
      label: null,
      showModelDetail: {},
    };
  },
  created() {
    console.log("测试11", this.ThreeId, this.cLheight);
  },
  mounted() {
    this.$nextTick(() => {
      this.setThree();
      this.add3dGlb();
      this.monitorThree();
    });
  },
  watch: {
    bhaId2: {
      handler(newValue, oldValue) {
        console.log("测试????");
        if(this.label != null) {
          this.label.visible = false;
        }

        this.add3dGlb();
      },
      deep: true,
    },
  },
  methods: {
    setThree() {
      let THIS = this;
      this.scene = new THREE.Scene(); // 创建场景
      // 场景雾化效果  第二个是雾化的近处值,第三个是雾化的远处值
      // scene.fog = new THREE.Fog(0xff0000, 0.01, 100);
      // 场景雾化效果  第一个是颜色,第二个是雾化浓度
      // scene.fog = new THREE.FogExp2(0x0000ff,0.02)
      // 强制让场景中的对象使用相同的材质
      // scene.overrideMaterial = new THREE.MeshLambertMaterial({color:0x0000ff})
      let container = document.getElementById("ThreeId233");

      this.camera = new THREE.PerspectiveCamera(
        75,
        container.clientWidth / container.clientHeight,
        0.1,
        1000
      ); // 创建透视摄像机
      // let camera = new THREE.OrthographicCamera(window.innerWidth/-16,window.innerWidth/16,window.innerHeight/16,window.innerHeight/-16,-200,500)

      this.renderer = new THREE.WebGLRenderer({
        logarithmicDepthBuffer: true, // 对数深度缓存
        antialias: true, // 抗锯齿
      });
      this.renderer.outputEncoding = THREE.sRGBEncoding;
      // 设置渲染器的宽高
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.shadowMap.enabled = true; // 如果要渲染阴影必须为真

      // 把渲染器渲染好的dom,加入到我们创建的元素中
      container.appendChild(this.renderer.domElement);
      // let geometry = new THREE.BoxGeometry(8, 8, 8); // 创建一个立方体
      // let material = new THREE.MeshLambertMaterial({ color: 0x00ff00 }); // 创建基础网格材质网格基础材质

      // let cube = new THREE.Mesh(geometry, material); // 创建一个网格对象
      // cube.position.y = 20
      let axes = new THREE.AxesHelper(50); // 创建坐标轴
      // scene.add(axes);
      console.log("axes", axes);
      this.camera.position.x = -1.5;
      this.camera.position.y = 3; // 这里把相机拉高可以更直观的看到  光源显示 辅助线效果
      this.camera.position.z = 0.1; // 设置相机的距离   Z 轴

      // 创建地面网格对象  围成一个正方体
      let planeGeometry = new THREE.PlaneGeometry(200, 200);
      let planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });

      let plan = new THREE.Mesh(planeGeometry, planeMaterial);
      plan.name = "方盒";
      let plan2 = plan.clone();
      let plan3 = plan.clone();
      let plan4 = plan.clone();
      let plan5 = plan.clone();
      let plan6 = plan.clone();
      plan2.position.z = -100;
      plan2.position.y = 100;
      plan2.position.x = 15;
      plan3.rotation.y = 0.5 * Math.PI;
      plan3.position.x = -85;
      plan3.position.y = 100;
      plan4.rotation.y = -0.5 * Math.PI;
      plan4.position.x = 115;
      plan4.position.y = 100;
      plan5.rotation.x = 0.5 * Math.PI;
      plan5.position.y = 200;
      plan5.position.x = 15;
      plan6.rotation.y = Math.PI;
      plan6.position.y = 100;
      plan6.position.z = 100;
      plan6.position.x = 15;
      plan.rotation.x = -0.5 * Math.PI;
      plan.position.set(15, 0, 0);
      plan.receiveShadow = true;
      this.scene.add(plan);
      this.scene.add(plan2);
      this.scene.add(plan3);
      this.scene.add(plan4);
      this.scene.add(plan5);
      this.scene.add(plan6);
      //聚光灯光源
      // 1.光照颜色
      // 2.光照强度
      // 3.光源发出光的最大距离
      // 4.光线散射角度
      // 5.聚光锥的半影衰减百分比
      // 6.沿着光照距离的衰减量
      this.DirectionalLight = new THREE.SpotLight(0xffffff);
      this.DirectionalLight.size = 10;
      this.DirectionalLight.castShadow = true;
      this.DirectionalLight.angle = Math.PI / 2;
      this.DirectionalLight.position.set(-20, 65, 45);

      // spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
      // spotLight.shadow.camera.far = 130;
      // spotLight.shadow.camera.near = 40;
      // 辅助对象
      let DirectionalLightHelper = new THREE.SpotLightHelper(
        this.DirectionalLight
      );
      // this.scene.add(DirectionalLightHelper);
      let ambienLight = new THREE.AmbientLight(0xcccccc); // 基本光源 会照亮场景中所有物体不会产生阴影
      this.scene.add(this.DirectionalLight);
      this.scene.add(ambienLight);
      const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
      hemiLight.position.set(0, 220, 0);
      this.scene.add(hemiLight);
      ///

      THIS.labelRenderer = new CSS2DRenderer();
      THIS.labelRenderer.setSize(container.clientWidth, container.clientHeight);
      THIS.labelRenderer.domElement.style.position = "absolute";
      THIS.labelRenderer.domElement.style.top = "0px";
      THIS.labelRenderer.domElement.style.right = "0px";
      let threeBox = document.getElementById("threeBox");
      threeBox.appendChild(THIS.labelRenderer.domElement);

      let ctrlObj = {};
      console.log("labelRenderer", THIS.labelRenderer);
      THIS.OrbitControlsAr = new OrbitControls(
        THIS.camera,
        THIS.labelRenderer.domElement
      );

      this.scene.add(THIS.OrbitControlsAr);
      THIS.OrbitControlsAr.update();
      renderScene();
      // 监听参数变化, 渲染器重新渲染
      function renderScene() {
        if (THIS.close != false) {
          requestAnimationFrame(renderScene); // stats  方法  启动
        }
        if (THIS.renderer != null) {
          // stats.update();
          if (THIS.glftModel) {
            THIS.group.rotation.y += 0.01;
          }
          THIS.OrbitControlsAr.update();
          THIS.renderer.render(THIS.scene, THIS.camera); // 渲染器 渲染
          THIS.labelRenderer.render(THIS.scene, THIS.camera);
        }
      }

      // 将检测动画运行帧数的界面添加到页面上
      function addStats() {
        let stats = new Stats();
        stats.domElement.style.position = "absolute";
        stats.domElement.style.left = "0px";
        stats.domElement.style.top = "0px";
        stats.setMode(0);
        document.getElementById("myStats").appendChild(stats.domElement);
        return stats;
      }

      // 监听页面变化 自适应场景
      window.addEventListener("resize", onWindowResize, false);
      function onWindowResize() {
        let container22 = document.getElementById("ThreeId233");

        THIS.camera.aspect = container22.clientWidth / container22.clientHeight;
        // 更新相机的投影矩阵
        THIS.camera.updateProjectionMatrix();
        // 渲染器调用  重置  场景的渲染尺寸

        THIS.renderer.setSize(
          container22.clientWidth,
          container22.clientHeight
        );
        THIS.labelRenderer.setSize(
          container22.clientWidth,
          container22.clientHeight
        );

        THIS.monitorThree();
      }
    },
    async add3dGlb() {
      let THIS = this;
      if (THIS.label != null) {
        console.log('label11',THIS.label)
        // THIS.scene.remove(THIS.label);
      }

      let rows = await selBhaDetail(this.bhaId2);
      let modelSort = [];
      let arr = [];
      if (rows.data.rows.length > 0) {
        rows.data.rows.map((el) => {
          arr.push(el.bhaName);

          switch (true) {
            case el.bhaName.indexOf("接头") != -1:
              el.url = "static" + "/zjzh" + "/" + "接头" + ".glb";
              modelSort.push(el);
              break;
            case el.bhaName.indexOf("钻杆") != -1:
              el.url = "static" + "/zjzh" + "/" + "钻杆" + ".glb";
              modelSort.push(el);
              break;
            case el.bhaName.indexOf("PDC") != -1:
              el.url = "static" + "/zjzh" + "/" + "PDC钻头" + ".glb";
              modelSort.push(el);
              break;
            case el.bhaName == "牙轮钻头":
              el.url = "static" + "/zjzh" + "/" + "牙轮钻头" + ".glb";
              modelSort.push(el);
              break;
            case el.bhaName.indexOf("钻铤") != -1:
              if (el.bhaName == "无磁钻铤") {
                el.url = "static" + "/zjzh" + "/" + "无磁钻铤" + ".glb";
                modelSort.push(el);
              } else if (el.bhaName.indexOf("钻铤") != -1) {
                el.url = "static" + "/zjzh" + "/" + "无磁钻铤" + ".glb";
                modelSort.push(el);
              }
              break;
            case el.bhaName == "稳定器" || el.bhaName == "扶正器":
              el.url = "static" + "/zjzh" + "/" + "扶正器" + ".glb";
              modelSort.push(el);
              break;
          }
        });
      }
      console.log("arr", arr);
      console.log("modelSort", modelSort);
      console.log("row", rows);
      let loader = new GLTFLoader();
      let dracoLoader = new DRACOLoader();
      dracoLoader.setDecoderPath("static/"); // 这个是加载draco算法,这样才能解析压缩后的gltf模型格式.
      loader.setDRACOLoader(dracoLoader);
      let url = "";
      console.log("this.sidebarItem", this.sidebarItem);
      let data3dFile = [
        {
          url: "static/zjzh/扶正器.glb",
        },
        {
          url: "static/zjzh/接头.glb",
        },
        {
          url: "static/zjzh/螺杆.glb",
        },
        {
          url: "static/zjzh/套洗头.glb",
        },
        {
          url: "static/zjzh/无磁钻铤.glb",
        },
        {
          url: "static/zjzh/牙轮钻头.glb",
        },
        {
          url: "static/zjzh/钻铤.glb",
        },
        {
          url: "static/zjzh/钻杆.glb",
        },
        {
          url: "static/zjzh/PDC钻头.glb",
        },

        // {
        //   url: "static/三闸板.glb",
        // },
      ];

      THIS.group = new THREE.Group();
      for (let x = 0; x < modelSort.length; x++) {
        let gltf = await loader.loadAsync(modelSort[x].url);
        //初始展示的模型的缩放比例
     
        gltf.scene.scale.set(1, 1, 1);

        gltf.scene.name = `ceshi${x}`;
        gltf.scene.userData = modelSort[x];
        THIS.glftModel = gltf;
        gltf.scene.traverse(function (child) {
          if (child.isMesh) {
            // console.log(child,999)
            child.frustumCulled = false;
            //模型阴影
            child.castShadow = true;
            //模型自发光
            // child.material.emissive = new THREE.Color(0x000000);
            // child.material.roughness = 0;
            // child.material.metalness = 1;
            // child.material.emissiveMap = child.material.map;
          }
        });
        
        let box = new THREE.Box3().setFromObject(gltf.scene);
        console.log("box", box);
        if (x == 0) {
          gltf.scene.position.set(0, 1, 0);
          THIS.y = box.max.y + 1;
          // const geometry = new THREE.CylinderGeometry(1, 1, box.max.y, 32);
          // const material = new THREE.MeshBasicMaterial({ color: 0xffff00 });
          // const cylinder = new THREE.Mesh(geometry, material);
          // cylinder.position.set(0, THIS.y, 0);
          // cylinder.name = x + '圆柱体'
          // THIS.scene.add(cylinder);
        } else {
        
          gltf.scene.position.set(0, THIS.y, 0);
          THIS.y += box.max.y;
          // const geometry = new THREE.CylinderGeometry(1, 1, box.max.y, 32);
          // const material = new THREE.MeshBasicMaterial({ color: x % 2 != 0 ? 0xffff00 : 0xff00ff   });
          // material.transparent = true
          // material.opacity = 0.5
          // const cylinder = new THREE.Mesh(geometry, material);
          //  cylinder.name = x + '圆柱体'
          // cylinder.position.set(0, THIS.y, 0);
          // THIS.scene.add(cylinder);
        }

        THIS.group.add(gltf.scene);
        if (x == modelSort.length - 1) {
          console.log("THIS.group", THIS.group);

          if (THIS.group.children.length > 0) {
            THIS.group.children;
          }

          THIS.OrbitControlsAr.minPolarAngle = Math.PI / 2;
          THIS.OrbitControlsAr.maxPolarAngle = Math.PI / 2;
          THIS.OrbitControlsAr.minDistance = 0.5;
          THIS.OrbitControlsAr.maxDistance = 20;
          const a = new THREE.Vector3(0, 2, 0);
          THIS.OrbitControlsAr.target = a; // 把相机对准某个点
          THIS.group.name = "钻具组合";
          let findObj = THIS.scene.getObjectByName("钻具组合", false);
          THIS.scene.remove(findObj);
          THIS.scene.add(THIS.group);
          THIS.DirectionalLight.position.y = THIS.y / 2;
          THIS.DirectionalLight.target = THIS.group;
        }
      }

      console.log("group", THIS.group);
    },
    monitorThree() {
      let THIS = this;
      let dom = THIS.renderer.domElement;
      // let dom = document.getElementById(THIS.ThreeId);
      THIS.labelRenderer.domElement.removeEventListener(
        "click",
        zzclickZj,
        false
      );
      let rect = THIS.labelRenderer.domElement.getBoundingClientRect();
      THIS.labelRenderer.domElement.addEventListener("click", zzclickZj, false);
      function zzclickZj() {
        console.log("dom", THIS.labelRenderer);
        const { clientX, clientY } = event;
        console.log("clientX", [window], event, clientX, clientY);
        let x = ((clientX - rect.left) / rect.width) * 2 - 1;
        let y = -((clientY - rect.top) / rect.height) * 2 + 1;

        const mousePoint = new THREE.Vector2(x, y);

        const raycaster = new THREE.Raycaster();
        raycaster.setFromCamera(mousePoint, THIS.camera);
        console.log("THIS.scene", THIS.scene, THIS.camera);

        const intersects = raycaster.intersectObjects(
          THIS.scene.children,
          true
        );
        console.log("intersects111", intersects);
        const intersect = intersects.filter(
          (item) =>
            item.object.name != "方盒" &&
            !(item.object instanceof THREE.AxesHelper)
        );
        console.log("inser", intersect, intersect.length);
        if (intersect.length > 0) {
          THIS.showModelDetail = intersect[0].object.parent.userData;
          THIS.showDetailDia(intersect[0].point);
        } else {
          this.showDia = false;
          THIS.showDetailDia("空");
        }
      }
    },
    // 显示弹窗
    showDetailDia(mousePoint) {
      let THIS = this;
      console.log("mousePoint", mousePoint);
      this.showDia = true;
      this.$nextTick(() => {
        if (mousePoint == "空") {
          if (THIS.label != null) {
            THIS.label.visible = false;
          }
        } else {
          let testDiv = document.getElementById("modal");

          THIS.label = new CSS2DObject(testDiv);

          THIS.label.position.set(mousePoint.x, mousePoint.y, 0);
          // mesh.add(label);
          this.scene.add(THIS.label);
        }
      });
    },
    clearRenderer() {
      let THIS = this;
      THIS.scene.remove(THIS.label);
      this.close = false;
      // let dom = document.querySelector("#three");
      // // 把渲染器渲染好的dom,加入到我们创建的元素中
      // dom.removeChild(this.renderer.domElement);
      // this.glftModel.geometry.dispose();
      // this.glftModel.material.dispose();
      if (this.renderer) {
        this.renderer.dispose();
        this.renderer.forceContextLoss();
        this.renderer.context = null;
        this.renderer.domElement = null;
        this.renderer = null;
      }
    },
  },

  beforeDestroy() {
    console.log("销毁");
    this.clearRenderer();
  },
};
</script>

<style lang="less" scoped>
/deep/ #modal {
  height: 190px !important;
}
/deep/ .ant-card-body {
  height: 190px !important;
}
</style>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值