zhangming %!s(int64=4) %!d(string=hai) anos
pai
achega
1ea28b0b11

+ 5 - 0
package.json

@@ -8,13 +8,17 @@
   },
   "dependencies": {
     "axios": "^0.21.0",
+    "cesium": "^1.78.0",
     "core-js": "^3.6.5",
     "dayjs": "^1.8.36",
     "echarts": "^4.8.0",
     "echarts-gl": "^1.1.1",
     "element-ui": "^2.4.5",
+    "three-collada-loader": "^0.0.1",
     "three-css2drender": "^1.0.0",
+    "three-fbx-loader": "^1.0.2",
     "three-obj-mtl-loader": "^1.0.3",
+    "three.js": "^0.77.1",
     "vue": "^2.6.11",
     "vue-router": "^3.2.0"
   },
@@ -25,6 +29,7 @@
     "node-sass": "^4.9.2",
     "sass": "^1.26.5",
     "sass-loader": "^8.0.2",
+    "three": "^0.125.2",
     "vue-cli-plugin-element": "~1.0.1",
     "vue-template-compiler": "^2.6.11"
   },

BIN=BIN
public/static/fan.fbx


BIN=BIN
public/static/fan1.FBX


+ 167 - 0
public/static/three_sprite_utils.js

@@ -0,0 +1,167 @@
+
+import * as Three from "three";
+var three_sprite_utils_vars = {
+    spriteInfoStorage:[],
+    spriteParams:null,
+    scene_src:null,
+}
+//This array will store
+function SetScene(_scene_src) {
+    three_sprite_utils_vars.scene_src = _scene_src;
+}
+export function SwitchSprite(_str, _position, attachedObject) {
+    var sprite_tmp;
+    sprite_tmp = findSprite(attachedObject)
+    if(sprite_tmp == null)
+        return CreateSprite(_str, _position, attachedObject);
+    else{
+        return RemoveSprite(attachedObject);
+    }
+}
+function RemoveSprite(attachedObject) {
+    var spriteIndex = findSpriteIndex(attachedObject);
+    if(spriteIndex == -1)
+        return null;
+    three_sprite_utils_vars.scene_src.remove(three_sprite_utils_vars.spriteInfoStorage[spriteIndex]['spriteItem']);
+    three_sprite_utils_vars.spriteInfoStorage.splice(spriteIndex, 1)
+    // delete  three_sprite_utils_vars.spriteInfoStorage[spriteIndex];
+    return null;
+}
+function findSprite(attachedObject) {
+    // var attachedObj_tmp;
+    var spriteIndex = findSpriteIndex(attachedObject);
+    if(spriteIndex == -1)
+        return null;
+    return three_sprite_utils_vars.spriteInfoStorage[spriteIndex]['spriteItem']
+
+    // return attachedObj_tmp;
+}
+function findSpriteIndex(attachedObject) {
+    var attachedObj_tmp;
+    var flag = false;
+    var i = -1;
+    for (i = 0; i < three_sprite_utils_vars.spriteInfoStorage.length; i++) {        
+        var spriteInfo_tmp = three_sprite_utils_vars.spriteInfoStorage[i];
+        console.log(spriteInfo_tmp);
+        attachedObj_tmp = spriteInfo_tmp['objItem'];
+        if(attachedObj_tmp == attachedObject){
+            flag = true;
+            break;
+            return spriteInfo_tmp['spriteItem'];
+        }
+    }
+    if(i == three_sprite_utils_vars.spriteInfoStorage.length){
+        return -1;
+    }
+    return i;
+    // return attachedObj_tmp;
+}
+function CreateSprite(str, _position, _obj) {
+    var newSprite = makeTextSprite(str, {position:_position});
+    var spriteInfoItem = {'objItem':_obj, 'spriteItem':newSprite};
+    three_sprite_utils_vars.spriteInfoStorage.push(spriteInfoItem);
+    return newSprite;
+}
+function makeTextSprite(message, parameters) {
+    console.log(message)
+    console.log(parameters)
+    if (parameters === undefined) parameters = {};
+
+    var fontface = parameters.hasOwnProperty("fontface") ?
+        parameters["fontface"] : "Arial";
+
+    /* 字体大小 */
+    var fontsize = parameters.hasOwnProperty("fontsize") ?
+        parameters["fontsize"] : 72;
+
+    /* 边框厚度 */
+    var borderThickness = parameters.hasOwnProperty("borderThickness") ?
+        parameters["borderThickness"] : 5;
+
+    /* 边框颜色 */
+    var borderColor = parameters.hasOwnProperty("borderColor") ?
+        parameters["borderColor"] : { r: 90, g: 90, b: 90, a: 1.0 };
+
+    /* 背景颜色 */
+    var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
+        parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 };
+
+    var position = parameters.hasOwnProperty("position") ?
+        parameters["position"] : { x: 0, y: 0, z: 0 };
+
+    /* 创建画布 */
+    var canvas = document.createElement('canvas');
+    var context = canvas.getContext('2d');
+
+    /* 字体加粗 */
+    context.font = "Bold " + fontsize + "px " + fontface;
+
+    /* 获取文字的大小数据,高度取决于文字的大小 */
+    var metrics = context.measureText(message);
+    var textWidth = metrics.width;
+
+    /* 背景颜色 */
+    context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
+        + backgroundColor.b + "," + backgroundColor.a + ")";
+
+    /* 边框的颜色 */
+    context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
+        + borderColor.b + "," + borderColor.a + ")";
+    context.lineWidth = borderThickness;
+
+    /* 绘制圆角矩形 */
+    roundRect(context, borderThickness / 2, borderThickness / 2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
+    // normRect(context, 0, 0, textWidth, fontsize)
+    /* 字体颜色 */
+    context.fillStyle = "rgba(222, 111, 0, 1)";
+    context.fillText(message, borderThickness, fontsize + borderThickness);
+
+    /* 画布内容用于纹理贴图 */
+    var texture = new Three.Texture(canvas);
+    texture.needsUpdate = true;
+
+    var spriteMaterial = new Three.SpriteMaterial({ map: texture });
+    var sprite = new Three.Sprite(spriteMaterial);
+
+    console.log(sprite.spriteMaterial);
+
+    /* 缩放比例 */
+    sprite.position.set(parameters.position.x, parameters.position.y, parameters.position.z);
+    sprite.scale.set(10,10,10);
+
+    return sprite;
+
+}
+function normRect(ctx, x, y, w, h) {
+    ctx.beginPath();
+    // ctx.moveTo(x, y);
+    // ctx.lineTo(x, y - 2*h);
+    // ctx.lineTo(x + 2*w, y - 2*h);
+    // ctx.lineTo(x + 2*w, y);
+    // ctx.lineTo(x, y);
+    ctx.moveTo(x, y);
+    ctx.lineTo(x, y + 2 * h);
+    ctx.lineTo(x - 2 * w, y + 2 * h);
+    ctx.lineTo(x - 2 * w, y);
+    ctx.lineTo(x, y);
+    ctx.closePath();
+    ctx.fill();
+    // ctx.stroke();
+}
+function roundRect(ctx, x, y, w, h, r) {
+
+    ctx.beginPath();
+    ctx.moveTo(x + r, y);
+    ctx.lineTo(x + w - r, y);
+    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
+    ctx.lineTo(x + w, y + h - r);
+    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
+    ctx.lineTo(x + r, y + h);
+    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
+    ctx.lineTo(x, y + r);
+    ctx.quadraticCurveTo(x, y, x + r, y);
+    ctx.closePath();
+    ctx.fill();
+    ctx.stroke();
+
+}

+ 117 - 0
src/router/index copy.ts

@@ -0,0 +1,117 @@
+
+  
+import Stats from 'stats.js';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
+import { AnimationAction } from 'three/src/animation/AnimationAction';
+import { Scene, PerspectiveCamera, WebGLRenderer, PlaneBufferGeometry, Mesh, MeshBasicMaterial, DoubleSide, GridHelper, AmbientLight, AnimationMixer, Clock, Raycaster, Vector2, Object3D } from 'three';
+
+export class Application {
+  private scene: Scene;
+  private clock: Clock;
+  private camera: PerspectiveCamera;
+  private renderer: WebGLRenderer;
+  private stats: Stats;
+  private gltfLoader: GLTFLoader;
+  private walkAction: AnimationAction;
+  private orbitControls: OrbitControls;
+  private animationMixer: AnimationMixer;
+  // 动画是否暂停
+  private paused: boolean = false;
+
+  constructor() {
+    this.scene = new Scene();
+    this.clock = new Clock();
+    this.camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
+    this.renderer = new WebGLRenderer({ antialias: true });
+    this.gltfLoader = new GLTFLoader();
+    this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
+    this.animationMixer = new AnimationMixer(this.scene);
+
+    this.renderer.setSize(window.innerWidth, window.innerHeight);
+    document.body.appendChild(this.renderer.domElement);
+    window.addEventListener('resize', () => this.onWindowResize());
+
+    this.camera.position.set(-0.5, 2, 1);
+
+    this.stats = new Stats();
+    this.stats.showPanel(0);
+    window.document.body.appendChild(this.stats.dom);
+
+
+    const planeBufferGeometry = new PlaneBufferGeometry(100, 100);
+    const plane = new Mesh(planeBufferGeometry, new MeshBasicMaterial({ color: 0xFFFFFF, side: DoubleSide }));
+    plane.name = 'plane';
+    plane.rotation.x = -Math.PI / 2;
+    this.scene.add(plane);
+    this.scene.add(new GridHelper(100, 100));
+
+    this.gltfLoader.load('../assets/Soldier.glb', gltf => {
+      console.log(gltf);
+      gltf.scene.name = 'Soldier';
+      gltf.scene.rotation.y = Math.PI;
+      this.scene.add(gltf.scene);
+      this.scene.add(new AmbientLight(0xFFFFFF, 2));
+
+      this.orbitControls.target.set(0, 1, 0);
+
+      const animationClip = gltf.animations.find(animationClip => animationClip.name === 'Walk');
+      this.walkAction = this.animationMixer.clipAction(animationClip);
+      this.walkAction.play();
+    });
+
+    this.renderer.domElement.addEventListener('click', event => {
+      const { offsetX, offsetY }  = event;
+      const x = ( offsetX / window.innerWidth ) * 2 - 1;
+    	const y = - ( offsetY / window.innerHeight ) * 2 + 1;
+      const mousePoint = new Vector2(x, y);
+      const raycaster = new Raycaster();
+      // 设置鼠标位置和参考相机
+      raycaster.setFromCamera(mousePoint, this.camera);
+      // 鼠标点击对应的物体(所有鼠标映射到的物体,包括被遮挡的)
+      const intersects =  raycaster.intersectObjects(this.scene.children, true);
+      // 过滤网格和地面
+      const intersect = intersects.filter(intersect => !(intersect.object instanceof GridHelper) && intersect.object.name !== 'plane')[0];
+      if(intersect && this.isClickSoldier(intersect.object)) {
+        // 停止动画
+        // this.walkAction.stop();
+        // 暂停动画
+        this.walkAction.paused = !this.walkAction.paused;
+      }
+    });
+
+    this.render();
+  }
+
+  /**
+   * 递归判断是否点击到人物
+   * @param object
+   */
+  private isClickSoldier(object: Object3D) {
+    if(object.name === 'Soldier') {
+      return object;
+    } else if(object.parent) {
+      return this.isClickSoldier(object.parent);
+    } else {
+      return null;
+    }
+  }
+
+  private render() {
+    // 更新动画
+    this.animationMixer.update(this.clock.getDelta());
+
+    this.renderer.render(this.scene, this.camera);
+
+    this.orbitControls.update();
+    window.requestAnimationFrame(() => this.render());
+    this.stats.update();
+  }
+
+  private onWindowResize() {
+    this.renderer.setSize(window.innerWidth, window.innerHeight);
+    this.camera.aspect = window.innerWidth / window.innerHeight;
+    this.camera.updateProjectionMatrix();
+  }
+
+}

+ 8 - 2
src/router/index.js

@@ -10,7 +10,7 @@ const routes = [
     component: () => import('../views/CesiumScene.vue')
   },
   {
-    path: '/WT',
+    path: '/wt',
     name: 'WT',
     component: () => import('../views/WT.vue')
   },
@@ -25,10 +25,16 @@ const routes = [
     component: () => import('../views/cesiumtest.vue')
   },
   {
-    path: '/',
+    path: '/1',
     name: 'monitor',
     component: () => import('../views/monitor1.vue')
   }
+  ,
+  {
+    path: '/',
+    name: 'monitor',
+    component: () => import('../views/three.vue')
+  }
 
 ]
 

+ 2 - 4
src/views/WT.vue

@@ -63,7 +63,7 @@
 </template>
 <script>
 import * as THREE from "three";
-import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
+import  {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
 import { MTLLoader, OBJLoader } from "three-obj-mtl-loader";
 var ColladaLoader = require("three-collada-loader");
 export default {
@@ -178,9 +178,7 @@ export default {
       raycaster: null,
       /**鼠标 */
       mouse: null,
-      /**屋顶 */
-      roof: null,
-      /**组 */
+      mesh:null,
       group: null,
       requestId: null,
       /**渲染文字 */

+ 258 - 0
src/views/three.vue

@@ -0,0 +1,258 @@
+<template>
+  <div>
+    <div id="container"></div>
+    <canvas id="canvas"></canvas>
+    <div class="top">
+      <el-select v-model="value" size="mini" placeholder="请选择">
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        >
+        </el-option>
+      </el-select>
+      <el-select v-model="value" size="mini"  placeholder="请选择">
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        >
+        </el-option>
+      </el-select>
+    </div>
+    <div class="rt">
+      <el-select v-model="value" size="mini"  placeholder="请选择">
+        <el-option
+          v-for="item in options"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        >
+        </el-option>
+      </el-select>
+    </div>
+    <div class="menu">
+      <el-button
+        v-if="playStatu"
+        icon="el-icon-video-play"
+        @click="paly"
+        circle
+      ></el-button>
+      <el-button
+        v-else
+        icon="el-icon-video-pause"
+        @click="paly"
+        circle
+      ></el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import * as Three from "three";
+import { SwitchSprite } from "../../public/static/three_sprite_utils";
+import { GridHelper, Raycaster, Vector2 } from "three";
+var camera = null;
+var scene = null;
+var renderer = null;
+var clock = null;
+var orbitControls = null;
+var animationMixer = null;
+var walkAction = null;
+var sprite = null;
+var group = null;
+export default {
+  data() {
+    return {
+      publicPath: process.env.BASE_URL,
+      playStatu: true,
+      options: [{
+          value: '选项1',
+          label: '黄金糕'
+        }, {
+          value: '选项2',
+          label: '双皮奶'
+        }, {
+          value: '选项3',
+          label: '蚵仔煎'
+        }, {
+          value: '选项4',
+          label: '龙须面'
+        }, {
+          value: '选项5',
+          label: '北京烤鸭'
+        }],
+        value: ''
+    };
+  },
+  mounted() {
+    this.element = document.getElementById("container");
+    // this.animate();
+    this.init();
+    // this.spriteText()
+  },
+  methods: {
+    paly() {
+      this.playStatu == true ? walkAction.play() : walkAction.stop();
+      this.playStatu = !this.playStatu;
+    },
+    //初始化
+    init() {
+      scene = new Three.Scene();
+      clock = new Three.Clock();
+      camera = new Three.PerspectiveCamera(
+        70,
+        window.innerWidth / window.innerHeight,
+        1,
+        1000
+      );
+      camera.position.set(5, 21, -1);
+      renderer = new Three.WebGLRenderer({ antialias: true });
+      orbitControls = new OrbitControls(camera, renderer.domElement);
+      orbitControls.target = new Three.Vector3(0, 19, 0);
+      animationMixer = new Three.AnimationMixer(scene);
+      const container = document.getElementById("container");
+      renderer.setSize(window.innerWidth, window.innerHeight);
+      container.appendChild(renderer.domElement);
+      window.addEventListener("resize", () => this.onWindowResize());
+      // PolarGridHelper( radius:标网格的半径, radials:径向线的数量, circles:圆圈数,
+      // divisions:每个圆圈使用的线段数, color1:用于网格元素的第一种颜色, color2:用于网格元素的第一种颜色 )
+      var radius = 20;
+      var radials = 15;
+      var circles = 15;
+      var divisions = 100;
+      var helper02 = new Three.PolarGridHelper(
+        radius,
+        radials,
+        circles,
+        divisions,
+        "#6adcff",
+        "#6adcff"
+      );
+      // helper02.position.set(0, -20, 0)
+      scene.add(helper02);
+
+      let that = this;
+      var loader = new FBXLoader();
+      loader.load(`${that.publicPath}static/fan1.fbx`, async (fbx3d) => {
+        await console.log(fbx3d);
+
+        fbx3d.position.set(0, 12.4, -0.6);
+        fbx3d.scale.set(0.2, 0.2, 0.2);
+        fbx3d.children[9].material = fbx3d.children[6].material.clone();
+        fbx3d.children[6].material = fbx3d.children[6].material.clone();
+        fbx3d.children[9].material.transparent = true;
+        fbx3d.children[9].material.opacity = 0.3;
+        // fbx3d.children[9].material.wireframe = true;
+        // fbx3d.children[9].material.wireframeLinewidth = 20
+        // fbx3d.children[6].material.wireframe = true;
+        // fbx3d.children[9].material.color.set("#00bfff");
+        // fbx3d.children[6].material.color.set("#00bfff");
+        fbx3d.children[2].materialnew = new Three.MeshPhysicalMaterial({
+          color: 0xff0000,
+          // 材质像金属的程度. 非金属材料,如木材或石材,使用0.0,金属使用1.0,中间没有(通常).
+          // 默认 0.5. 0.0到1.0之间的值可用于生锈的金属外观
+          metalness: 1.0,
+          // 材料的粗糙程度. 0.0表示平滑的镜面反射,1.0表示完全漫反射. 默认 0.5
+          roughness: 0.6,
+          // 设置环境贴图
+          // 反射程度, 从 0.0 到1.0.默认0.5.
+          // 这模拟了非金属材料的反射率。 当metalness为1.0时无效
+          // reflectivity: 0.5,
+        });
+        scene.add(fbx3d);
+        scene.add(new Three.AmbientLight(0xffffff, 1.5));
+        const animationClip = fbx3d.animations.find(
+          (animationClip) => animationClip.name === "Take 001"
+        );
+        walkAction = animationMixer.clipAction(animationClip);
+      });
+
+      //射线
+      renderer.domElement.addEventListener("click", (event) => {
+        const { offsetX, offsetY } = event;
+        const x = (offsetX / window.innerWidth) * 2 - 1;
+        const y = -(offsetY / window.innerHeight) * 2 + 1;
+        const mousePoint = new Vector2(x, y);
+        const raycaster = new Raycaster();
+        raycaster.params = { sprite: {} };
+        raycaster.setFromCamera(mousePoint, camera);
+        let intersects = raycaster.intersectObjects(scene.children, true);
+        intersects = intersects.filter(
+          (intersect) => !(intersect.object instanceof GridHelper)
+        );
+        console.log(intersects)
+        var selectedMesh = intersects[0].object;
+        var newSprite = SwitchSprite(
+          selectedMesh.name || "aa",
+          selectedMesh.position,
+          selectedMesh
+        );
+        if (newSprite != null) scene.add(newSprite);
+      });
+
+      this.render();
+    },
+    render() {
+      // 更新动画
+      animationMixer.update(clock.getDelta());
+      renderer.render(scene, camera);
+      orbitControls.update();
+      window.requestAnimationFrame(() => this.render());
+    },
+    onWindowResize() {
+      renderer.setSize(window.innerWidth, window.innerHeight);
+      camera.aspect = window.innerWidth / window.innerHeight;
+      camera.updateProjectionMatrix();
+    },
+  },
+  beforeDestroy() {
+    camera = null;
+    scene = null;
+    renderer = null;
+    clock = null;
+    orbitControls = null;
+    animationMixer = null;
+    walkAction = null;
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+#container {
+  width: 1920px;
+  height: 100vh;
+}
+.menu {
+  position: fixed;
+  left: 20px;
+  top: 20px;
+  background-color: #fff;
+  border: 1px solid #fff;
+  border-radius: 20px;
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+}
+.top{
+  width: 60%;
+  display: flex;
+   position: fixed;
+   justify-content: space-around;
+   top: 20px;
+   left: 10%;
+}
+.rt {
+  width: 200px;
+  min-height: 200px;
+  position: fixed;
+  right: 20px;
+  top: 20px;
+  // border: 1px solid #fff;
+  // border-radius: 20px;
+}
+</style>