three.js 引用外部模型,并使用其绑定的动画

<template>   <div id="container">     <!-- <img src="/models/yunlog.png" alt /> -->     <button @click="dispose('robot')">       模型切换     </button>   </div> </template> <script> import * as Three from "three"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { DoubleSide, GridHelper, Mesh, MeshBasicMaterial, PlaneBufferGeometry, AnimationMixer, Clock, Raycaster, Vector2 } from 'three';
export default {   data() {     return {       // 场景的宽高获取容器宽高       sceneHeight: null,       sceneWidth: null,
      // 场景, 灯光, 摄像机, 控制器, 渲染器       scene: "",       light: "",       camera: "",       controls: "",       renderer: "",             // 动画       animationMixer: null,       clock: null,       animationClipList: [],       animationActionList: {},
      // 可改变的变量       // 点击触发的动作       clickAction: 'Wave'     };   },
  mounted() {     this.init();     // this.drawPlane();     this.drweSkybox()     this.loadGltf();
    // 监测点击事件     this.clickRender()   },
  methods: {     //初始化three.js相关内容     init() {       const container = document.getElementById("container");       // console.log("三维盒子", container, container.offsetHeight, container.offsetWidth)
      // 创建场景对象       this.scene = new Three.Scene();       this.sceneHeight = container.offsetHeight       this.sceneWidth = container.offsetWidth
      // 创建灯光       this.scene.add(new Three.AmbientLight(0x999889)); //环境光       this.light = new Three.DirectionalLight(0xdfebff, 0.5); //从正上方(不是位置)照射过来的平行光,0.45的强度       this.light.position.set(1, 1, 0);       this.light.position.multiplyScalar(0.5);       this.scene.add(this.light);
      //初始化相机  (视野大小, 长宽比, 近景, 远景)       this.camera = new Three.PerspectiveCamera(70, this.sceneWidth / this.sceneHeight, 0.01, 100)       this.camera.position.set(0, 8, 8);  
      this.renderer = new Three.WebGLRenderer({         alpha: true,         antialias: true       });             //初始化控制器       this.controls = new OrbitControls(this.camera, this.renderer.domElement);       // this.controls.target.set(0, 0, 0);//------------------       // this.controls.minDistance = 3;       // this.controls.maxDistance = 100;       // this.controls.maxPolarAngle = Math.PI / 3;       this.controls.update();
      //渲染       this.renderer.setPixelRatio(window.devicePixelRatio); //为了兼容高清屏幕       this.renderer.setSize(this.sceneWidth, this.sceneHeight);       this.renderer.outputEncoding = Three.sRGBEncoding;
      container.appendChild(this.renderer.domElement);       window.addEventListener("resize", this.onWindowResize, false); //添加窗口监听事件(resize-onresize即窗口或框架被重新调整大小)     },     //窗口监听函数     onWindowResize() {       // 模型比例调整       this.camera.aspect = this.sceneWidth / this.sceneHeight;       this.camera.updateProjectionMatrix();
      this.renderer.setSize(this.sceneWidth, this.sceneHeight);     },
    // 绘制平面     drawPlane() {       const planeBufferGeometry = new PlaneBufferGeometry( 30, 30 );       const plane = new Mesh( planeBufferGeometry, new MeshBasicMaterial({ color: '#000', side: DoubleSide }) )       plane.name = 'plane'       // 平面绕x轴旋转90度(弧度制)       plane.rotation.x = -Math.PI / 2       this.scene.add( plane )
      // 添加网格       this.scene.add(new GridHelper(30, 30))     },
    // 绘制天空盒     drweSkybox() {       let skyBox = new Three.BoxGeometry(100, 100, 100)                                                                       // 创建立方体
      let textureLoader = new Three.TextureLoader       let right = require('../assets/skybox/right.jpg')       let left = require('../assets/skybox/left.jpg')       let top = require('../assets/skybox/top.jpg')       let bottom = require('../assets/skybox/bottom.jpg')       let back = require('../assets/skybox/back.jpg')       let front = require('../assets/skybox/front.jpg')
      let skyBoxMaterialList = [         new Three.MeshBasicMaterial({ map: textureLoader.load(right), side: DoubleSide }),         new Three.MeshBasicMaterial({ map: textureLoader.load(left), side: DoubleSide }),         new Three.MeshBasicMaterial({ map: textureLoader.load(top), side: DoubleSide }),         new Three.MeshBasicMaterial({ map: textureLoader.load(bottom), side: DoubleSide }),         new Three.MeshBasicMaterial({ map: textureLoader.load(front), side: DoubleSide }),         new Three.MeshBasicMaterial({ map: textureLoader.load(back), side: DoubleSide })       ]             let sky = new Three.Mesh(skyBox, skyBoxMaterialList)                                                                          // 创建网格       this.scene.add(sky)     },
    //外部模型加载函数     loadGltf() {       // 加载模型       var loader = new GLTFLoader()       loader.load("static/RobotExpressive.glb", (data) => {         // console.log(data)         let mesh = data.scene;         // mesh.position.set(0, 0, 0);         mesh.name = 'robot'         this.scene.add(mesh); // 将模型引入three
        this.animationClipList = data.animations         this.setAnimation()
        this.render();       });     },
    // 加载动画     setAnimation() {       this.animationMixer = new AnimationMixer(this.scene)       this.clock = new Clock()
      console.log("动画列表", this.animationClipList)     },
    render()  {       requestAnimationFrame(() => { this.render() });             // 调用动画       this.animationMixer.update(this.clock.getDelta())       this.renderer.render(this.scene, this.camera);     },
    // 监测点击事件     clickRender() {       this.renderer.domElement.addEventListener('click', event => {         // 获取屏幕坐标         let { offsetX, offsetY } = event         // 计算画布上的二维坐标         let x = ( offsetX / this.sceneWidth ) * 2 - 1         let y = - ( offsetY / this.sceneHeight ) * 2 + 1         let mousePoint = new Vector2( x, y )
        // 获取点击元素         let raycaster = new Raycaster()         // 按照this.camera摄像头下,mousePoint位置设置raycaster         raycaster.setFromCamera( mousePoint, this.camera )         // 获取点击的元素         let intersect = raycaster.intersectObjects(this.scene.children, true)         // 筛选去除点击的网格元素及平面元素         intersect = intersect.filter( intersect => !(intersect.object instanceof GridHelper) && intersect.object.name != "plane" )
        console.log("点击元素", intersect)         let hasClick = []         // 所有被点击的指定元素及其子元素组成数组,通过数组中有无数据判断是否点击         intersect.forEach((v) => {           let click = this.isClick(v.object, 'robot')           // console.log("比较结果", click)           if( this.isClick(v.object, 'robot') ){             hasClick.push(v)           }         })         // console.log("比较结果", hasClick)
        let action = this.getAnimationAction(this.clickAction)         // 判断元素被点击,并且该点击动作还未触发,触发动作,若已触发动作停止动作         // if( hasClick.length > 0 && action.isRunning () ){         //   action.stop()         // } else if( hasClick.length > 0 ) {         //   action.play()         // }                 action.play()         action.weight = 0         console.log("权重", action.getEffectiveWeight () )         if( hasClick.length > 0 && action.getEffectiveWeight () ){           action.fadeOut(1)         } else if( hasClick.length > 0 ) {           action.fadeIn(1)         }       })     },
    // 判断点击的元素是否是指定元素或其子元素     isClick(object, name) {       if( object.name == name ){         return true       }else if( object.parent ){         return this.isClick(object.parent, name)       }else {         return false       }     },
    // 动画调用方法,当动作已使用混合器生成AnimationAction直接返回储存的AnimationAction,若还未生成,生成一个AnimationAction返回,并储存下次使用     getAnimationAction(name) {       let animationActionList = this.animationActionList       // 检索animationActionList中是否已储存AnimationAction       for( let actionName in animationActionList ) {         if( actionName == name ){           return animationActionList[name]         }       }
      // 若还未储存AnimationAction       let animationClip = this.animationClipList.find(animationClip => animationClip.name == name )       animationActionList[name] = this.animationMixer.clipAction(animationClip)       return animationActionList[name]     },
    // 删除对象     dispose(name) {       // console.log('删除测试', this.scene)       let childrenList = this.scene.children       let group = childrenList.find(v => v.name == name)       group.visible = !group.visible     }   }   }; </script>
<style scoped> #container {   width: 600px;   margin: 0 auto;   height: 400px;   overflow: hidden; } </style>
上一篇:航测-CC建模软件名词解释


下一篇:Cesium随笔:鹰眼功能