Ver Fonte

初始化模型

luogang há 7 meses atrás
pai
commit
22a2a10ef1

+ 1 - 0
ems-ui-cloud/package.json

@@ -65,6 +65,7 @@
     "quill": "1.3.7",
     "screenfull": "5.0.2",
     "sortablejs": "1.10.2",
+    "three": "^0.156.1",
     "uuidjs": "^5.1.0",
     "vue": "2.6.12",
     "vue-count-to": "1.0.13",

BIN
ems-ui-cloud/public/models/model.FBX


BIN
ems-ui-cloud/src/assets/images/models/bg.png


+ 3 - 0
ems-ui-cloud/src/bus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+const Bus = new Vue()
+export default Bus

+ 2 - 1
ems-ui-cloud/src/main.js

@@ -37,7 +37,8 @@ import DictTag from '@/components/DictTag'
 import VueMeta from 'vue-meta'
 // 字典数据组件
 import DictData from '@/components/DictData'
-
+import bus from './bus'
+Vue.prototype.$EventBus = bus
 // 全局方法挂载
 Vue.prototype.getDicts = getDicts
 Vue.prototype.getConfigKey = getConfigKey

+ 28 - 16
ems-ui-cloud/src/views/largeScreen/center.vue

@@ -1,43 +1,55 @@
 <template>
-  <div class="center">
-    <CusTabs  :tabs="areaTabs" :active.sync="areaType" @tab-click="areaTabClick" />
+  <div class="center"  id="model">
+    <CusTabs :tabs="areaTabs" :active.sync="areaType" @tab-click="areaTabClick" />
   </div>
 </template>
 <script>
 import CusTabs from './components/CusTabs.vue';
-import { mapMutations } from 'vuex';
+import {mapMutations} from 'vuex';
+import renderModel from './three/renderModel'
 export default {
-name: 'Center',
+  name: 'Center',
   data () {
     return {
-      areaType:'',
+      areaType: '',
       areaTabs: [{name: '全部', value: ''}, {name: '北区', value: '1'}, {name: '南区', value: '2'}],
     };
   },
   components: {
     CusTabs
   },
-  mounted(){},
+  mounted () {
+    this.initThree();
+  },
   methods: {
-    ...mapMutations('userState',['setAreaType']),
+    ...mapMutations('userState', ['setAreaType']),
     areaTabClick () {
       this.setAreaType(this.areaType)
+    },
+    initThree () {
+      setTimeout(() => {
+        const modelApi = new renderModel('#model')
+        modelApi.init()
+      },100)
+
     }
   }
 }
 </script>
 <style lang='scss' scoped>
-.center{
-  background: url("~@/assets/images/center.jpg") no-repeat;
-  background-size:cover ;
-  width: calc(100% - 806px);
-  margin:auto auto 0;
-  height: calc(100% - 90px);
+.center {
+  // background: url("~@/assets/images/center.jpg") no-repeat;
+  // background-size:cover ;
+  // width: calc(100% - 806px);
+  width: 100%;
+  // margin: auto auto 0;
+  height:100%;
   position: relative;
-  .tabs{
+
+  .tabs {
     position: absolute;
-    top: 20px;
-    left: 20px;
+    top: 90px;
+    left: 403px;
     z-index: 45;
   }
 }

+ 26 - 0
ems-ui-cloud/src/views/largeScreen/three/material.js

@@ -0,0 +1,26 @@
+
+import * as THREE from 'three' // 导入整个 three.js核心库
+
+// 底图材质
+export function getBottomMaterial(color,url,blending,repeat) {
+  const texture = new THREE.TextureLoader().load(url)
+  texture.needsUpdate = true // 使用贴图时进行更新
+  texture.wrapS = texture.wrapT = THREE.RepeatWrapping
+  if (repeat) {
+    texture.repeat.set(repeat, repeat)
+  } else {
+    texture.repeat.set(1000, 1000)
+  }
+  return new THREE.MeshStandardMaterial({
+    map: texture,
+    transparent: true,
+    color: new THREE.Color(color) || '0xffffff',
+    blending: blending == 1 ? 1 : 2,
+    // depthWrite: false,
+    alphaTest: 0.8,
+    opacity: 0.8,
+    roughness: 0.2,
+    metalness: 0.5
+  })
+}
+

+ 283 - 0
ems-ui-cloud/src/views/largeScreen/three/renderModel.js

@@ -0,0 +1,283 @@
+import * as THREE from 'three'; //导入整个 three.js核心库
+import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader';
+import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
+import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; //导入控制器模块,轨道控制器
+import { CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
+import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
+import { getBottomMaterial } from './material.js';
+// 定义一个 class类
+class renderModel {
+  constructor(selector) {
+    this.container = document.querySelector(selector);
+    // 相机
+    this.camera;
+    // 场景
+    this.scene;
+    //渲染器
+    this.renderer;
+    // 控制器
+    this.controls;
+    // 3d文字渲染器
+    this.css3DRenderer = null;
+    // 3d文字控制器
+    this.css3dControls = null;
+    // 模型
+    this.model;
+    // 环境光
+    this.ambientLight;
+    //模型平面
+    this.planeGeometry;
+  }
+  // 初始化加载模型方法
+  init() {
+    //初始化场景
+    this.initScene();
+    //初始化相机
+    this.initCamera();
+    //初始化渲染器
+    this.initRender();
+    // 创建灯光
+    this.createLight();
+    //初始化控制器,控制摄像头,控制器一定要在渲染器后
+    this.initControls();
+    this.setFBXModel();
+    //监听场景大小改变,跳转渲染尺寸
+    window.addEventListener('resize', this.onWindowResizes);
+    //场景渲染
+    this.sceneAnimation();
+  }
+  //创建场景
+  initScene() {
+    this.scene = new THREE.Scene();
+    this.scene.background = new THREE.Color(0x000000);
+    const axesHelper = new THREE.AxesHelper(150);
+    this.scene.add(axesHelper);
+    //底部平面
+    const geometry = new THREE.PlaneGeometry(1, 1);
+    // 创建材质,可以设置贴图
+    const material = getBottomMaterial(
+      '#ffffff', // 设置模型的颜色
+      require('@/assets/images/models/bg.png'),
+      1,
+      5
+    );
+    // 创建平面网格
+    const plane = new THREE.Mesh(geometry, material);
+    plane.scale.set(1000, 1000, 1000);
+    plane.position.y = 0;
+    const plane2 = plane.clone();
+    plane2.material = getBottomMaterial(
+      '#000000', // 设置模型的颜色
+      require('@/assets/images/models/bg.png'),
+      2,
+      5
+    );
+    this.scene.add(plane2);
+    this.scene.add(plane);
+  }
+  // 创建相机
+  initCamera() {
+    const { clientHeight, clientWidth } = this.container;
+    this.camera = new THREE.PerspectiveCamera(14, clientWidth / clientHeight, 1, 10000);
+  }
+  // 创建渲染器
+  initRender() {
+    // this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }) //设置抗锯齿
+    this.renderer = new THREE.WebGLRenderer({
+      logarithmicDepthBuffer: true,
+      antialias: true, // true/false表示是否开启反锯齿
+      alpha: true, // true/false 表示是否可以设置背景色透明
+      precision: 'mediump', // highp/mediump/lowp 表示着色精度选择
+      premultipliedAlpha: true // true/false 表示是否可以设置像素深度(用来度量图像的分辨率)
+      // preserveDrawingBuffer: false, // true/false 表示是否保存绘图缓冲
+      // physicallyCorrectLights: true, // true/false 表示是否开启物理光照
+    });
+    //设置屏幕像素比
+    this.renderer.setPixelRatio(window.devicePixelRatio);
+    //渲染的尺寸大小
+    const { clientHeight, clientWidth } = this.container;
+    this.renderer.setSize(clientWidth, clientHeight);
+    this.container.appendChild(this.renderer.domElement);
+
+    // 创建一个CSS3DRenderer
+    // this.css3DRenderer = new CSS3DRenderer()
+    // this.css3DRenderer.setSize(clientWidth, clientHeight)
+    // this.css3DRenderer.domElement.style.position = 'absolute'
+    // this.css3DRenderer.domElement.style.pointerEvents = 'none'
+    // this.css3DRenderer.domElement.style.top = 0
+  }
+  // 创建光源
+  createLight() {
+    this.scene.add(new THREE.AmbientLight(0xffffff, 10));
+  }
+  initControls() {
+    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
+    this.controls.enableDamping = true;
+    this.controls.maxDistance = 1000;
+    this.controls.update();
+    //标签控制器
+    // this.css3dControls = new OrbitControls(this.camera, this.css3DRenderer.domElement)
+    // this.css3dControls.enablePan = false
+    // this.css3dControls.enableDamping = true
+    // this.css3dControls.target.set(0, 0, 0)
+    // this.css3dControls.update()
+  }
+  animate() {
+    this.renderer.render(this.scene, this.camera);
+    this.controls.update();
+  }
+  // 使用动画器不断更新场景
+  sceneAnimation() {
+    this.renderer.setAnimationLoop(this.animate.bind(this));
+  }
+  tag3D(name) {
+    // 创建div元素(作为标签)
+    let div = document.createElement('div');
+    div.innerHTML = name;
+    div.style.color = '#000';
+    // div.classList.add("tag");
+    //div元素包装为CSS3模型对象CSS3DObject
+    let label = new CSS3DSprite(div);
+    div.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
+    // 设置HTML元素标签在three.js世界坐标中位置
+    // label.position.set(x, y, z);
+    //缩放CSS3DObject模型对象
+    // label.scale.set(0.02, 0.02, 0.02); //根据相机渲染范围控制HTML 3D标签尺寸
+    // label.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
+    // label.rotateX(-Math.PI/2);
+    return label; //返回CSS3模型标签
+  }
+
+  dialog(html) {
+    //div元素包装为CSS3模型对象CSS3DSprite
+    const element = document.createElement('div');
+    element.className = 'tag';
+    element.innerHTML = html;
+    const dialog = new CSS3DSprite(element);
+    element.style.pointerEvents = 'none'; //避免HTML标签遮挡三维场景的鼠标事件
+    // 设置HTML元素标签在three.js世界坐标中位置
+    // label.position.set(x, y, z);
+    //缩放CSS3DSprite模型对象
+    dialog.scale.set(0.01, 0.01, 0.01); //根据相机渲染范围控制HTML 3D标签尺寸
+    dialog.rotateY(Math.PI / 2); //控制HTML标签CSS3对象姿态角度
+    // dialog.rotateX(-Math.PI/2);
+    return dialog; //返回CSS3模型标签
+  }
+  /**
+   * 设置加载模型居中
+   * {Object} object 模型对象
+   */
+  setModelPosition(object) {
+    object.updateMatrixWorld();
+    // 获得包围盒得min和max
+    const box = new THREE.Box3().setFromObject(object);
+    // 返回包围盒的中心点
+    const center = box.getCenter(new THREE.Vector3());
+    object.position.x += object.position.x - center.x;
+    object.position.y += object.position.y - center.y;
+    object.position.z = 0;
+  }
+  //加载模型
+  setBaseModel() {
+    var mtlLoader = new MTLLoader();
+    mtlLoader.load('model/model.mtl', materials => {
+      var objLoader = new OBJLoader();
+      objLoader.setMaterials(materials);
+      //加载.obj文件,执行obj函数
+      objLoader.load('model/model3.obj', object => {
+        this.setModelPosition(object);
+        this.model = object;
+        // object.traverse((obj) => {
+        //   if (obj instanceof THREE.Mesh) {
+        //     obj.material = new THREE.MeshPhysicalMaterial({ color: '#43617B' })
+        //   }
+        // })
+        object.traverse(function (child) {
+          if (child.isMesh) {
+            child.material.emissive = child.material.color;
+            child.material.emissiveMap = child.material.map;
+          }
+        });
+        // 设置相机位置
+        this.camera.position.set(0, -600, 200);
+        // 设置相机坐标系
+        this.camera.lookAt(0, 0, 0);
+        // 将模型添加到场景中去
+        this.scene.add(this.model);
+      });
+    });
+  }
+  setFBXModel() {
+    var fbxLoader = new FBXLoader();
+    //加载.obj文件,执行obj函数
+    fbxLoader.load('/models/model.FBX', object => {
+      this.calcMeshCenter(object);
+      this.model = object;
+      object.traverse(function (child) {
+        if (child.isMesh) {
+          child.material.emissive = child.material.color;
+          child.material.emissiveMap = child.material.map;
+        }
+      });
+      // 设置相机位置
+      this.camera.position.set(0, -800, 500);
+      // 设置相机坐标系
+      this.camera.lookAt(0, 0, 0);
+      // 将模型添加到场景中去
+      this.scene.add(this.model);
+      // 初始化 GUI 控件
+      const camFolder = new GUI().addFolder('Camera');
+      camFolder.add(this.camera.position, 'x', -6500, 6500, 10).name('X Axis');
+      camFolder.add(this.camera.position, 'y', -6500, 6500, 10).name('Y Axis');
+      camFolder.add(this.camera.position, 'z', -6500, 6500, 10).name('Z Axis');
+      camFolder.open();
+      // const div = document.createElement('div');
+      // div.className = 'workshop-text';
+      // div.innerHTML = '<p>仓库2</p>';
+      // // 创建CSS3DSprite
+      // const tag = new CSS3DSprite(div);
+      // tag.position.set(8, 3, 38); // 调整标签位置
+      // this.model.add(tag);
+      //在GUI中添加一个用于调整tag位置的文件夹
+      // const tagFolder = new GUI().addFolder('食堂标签');
+      // // 将tag.position对象中的x, y, z属性添加到GUI中
+      // tagFolder.add(tag.position, 'x', -200, 200).name('X Position');
+      // tagFolder.add(tag.position, 'y', -200, 200).name('Y Position');
+      // tagFolder.add(tag.position, 'z', -200, 200).name('Z Position');
+      // tagFolder.open(); // 打开此文件夹以默认显示控制器
+      // const label3D = this.tag3D('光伏11111111'); //设置标签名称
+      // // label3D.position.copy(this.model.position);
+      // label3D.position.y += 0;
+      // label3D.position.x += 0;
+      // label3D.position.z = 20;
+      // this.model.add(label3D);
+    });
+  }
+  onWindowResizes() {
+    if (!this.container) return false;
+    const { clientHeight, clientWidth } = this.container;
+    //调整屏幕大小
+    this.camera.aspect = clientWidth / clientHeight; // 摄像机宽高比例
+    this.camera.updateProjectionMatrix(); //相机更新矩阵,将3d内容投射到2d面上转换
+    this.renderer.setSize(clientWidth, clientHeight);
+  }
+  calcMeshCenter(group) {
+    /**
+     * 包围盒全自动计算:模型整体居中
+     */
+    var box3 = new THREE.Box3();
+    // 计算层级模型group的包围盒
+    // 模型group是加载一个三维模型返回的对象,包含多个网格模型
+    box3.expandByObject(group);
+    // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
+    var center = new THREE.Vector3();
+    box3.getCenter(center);
+    // 重新设置模型的位置,使之居中。
+    group.position.x = 0;
+    group.position.y = 0;
+    group.position.z = 17;
+  }
+}
+
+export default renderModel;