|  | @@ -0,0 +1,323 @@
 | 
	
		
			
				|  |  | +import * as THREE from 'three';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
 | 
	
		
			
				|  |  | +import gsap from 'gsap';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class ddFc_7 {
 | 
	
		
			
				|  |  | +  model;
 | 
	
		
			
				|  |  | +  modelName = 'ddFcGroup';
 | 
	
		
			
				|  |  | +  group: THREE.Object3D = new THREE.Object3D();
 | 
	
		
			
				|  |  | +  animationTimer;
 | 
	
		
			
				|  |  | +  isLRAnimation = true;
 | 
	
		
			
				|  |  | +  direction = 1;
 | 
	
		
			
				|  |  | +  windowsActionArr = {
 | 
	
		
			
				|  |  | +    frontWindow: [],
 | 
	
		
			
				|  |  | +    backWindow: [],
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  player1;
 | 
	
		
			
				|  |  | +  player2;
 | 
	
		
			
				|  |  | +  playerStartClickTime1 = new Date().getTime();
 | 
	
		
			
				|  |  | +  mixers: THREE.AnimationMixer | undefined;
 | 
	
		
			
				|  |  | +  frontClipAction;
 | 
	
		
			
				|  |  | +  constructor(model) {
 | 
	
		
			
				|  |  | +    this.model = model;
 | 
	
		
			
				|  |  | +    this.group.name = 'ddFcGroup';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  addLight = () => {
 | 
	
		
			
				|  |  | +    if (!this.group || !this.group) return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
 | 
	
		
			
				|  |  | +    directionalLight.position.set(-437, 61, 559);
 | 
	
		
			
				|  |  | +    this.group.add(directionalLight);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const pointLight2 = new THREE.PointLight(0xffffff, 1, 150);
 | 
	
		
			
				|  |  | +    pointLight2.position.set(-101, 34, 16);
 | 
	
		
			
				|  |  | +    pointLight2.shadow.bias = 0.05;
 | 
	
		
			
				|  |  | +    this.group.add(pointLight2);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const pointLight3 = new THREE.PointLight(0xffffff, 1, 150);
 | 
	
		
			
				|  |  | +    pointLight3.position.set(19, 25, -7);
 | 
	
		
			
				|  |  | +    pointLight3.shadow.bias = 0.05;
 | 
	
		
			
				|  |  | +    this.group.add(pointLight3);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const pointLight6 = new THREE.PointLight(0xffffff, 1, 300);
 | 
	
		
			
				|  |  | +    pointLight6.position.set(51, 51, 9);
 | 
	
		
			
				|  |  | +    pointLight6.shadow.bias = 0.05;
 | 
	
		
			
				|  |  | +    this.group.add(pointLight6);
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // 设置模型位置
 | 
	
		
			
				|  |  | +  setModalPosition() {
 | 
	
		
			
				|  |  | +    this.group?.scale.set(22, 22, 22);
 | 
	
		
			
				|  |  | +    this.group?.position.set(-35, 25, 15);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  addMonitorText(selectData) {
 | 
	
		
			
				|  |  | +    if (!this.group) {
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    const screenDownText = VENT_PARAM['modalText']
 | 
	
		
			
				|  |  | +      ? VENT_PARAM['modalText']
 | 
	
		
			
				|  |  | +      : History_Type['type'] == 'remote'
 | 
	
		
			
				|  |  | +      ? `国能神东煤炭集团监制`
 | 
	
		
			
				|  |  | +      : '煤炭科学技术研究院有限公司研制';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const screenDownTextX = 90 - (screenDownText.length - 10) * 6;
 | 
	
		
			
				|  |  | +    const textArr = [
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: `远程定量调节自动风窗`,
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 100,
 | 
	
		
			
				|  |  | +        y: 95,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: `${selectData.OpenDegree ? '开度值(°)' : selectData.forntArea ? '过风面积(㎡)' : '过风面积(㎡)'}:`,
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 5,
 | 
	
		
			
				|  |  | +        y: 145,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: selectData.OpenDegree
 | 
	
		
			
				|  |  | +          ? Number(`${selectData.OpenDegree}`).toFixed(2)
 | 
	
		
			
				|  |  | +          : selectData.forntArea
 | 
	
		
			
				|  |  | +          ? Number(`${selectData.forntArea}`).toFixed(2)
 | 
	
		
			
				|  |  | +          : '-',
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 330,
 | 
	
		
			
				|  |  | +        y: 145,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速(m/s)' : '通信状态:'}:`,
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 5,
 | 
	
		
			
				|  |  | +        y: 200,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: `${
 | 
	
		
			
				|  |  | +          selectData.frontRearDP
 | 
	
		
			
				|  |  | +            ? selectData.frontRearDP
 | 
	
		
			
				|  |  | +            : selectData.windSpeed
 | 
	
		
			
				|  |  | +            ? selectData.windSpeed
 | 
	
		
			
				|  |  | +            : selectData.netStatus == '0'
 | 
	
		
			
				|  |  | +            ? '断开'
 | 
	
		
			
				|  |  | +            : '连接'
 | 
	
		
			
				|  |  | +        }`,
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 330,
 | 
	
		
			
				|  |  | +        y: 200,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: `${selectData.fWindowM3 ? '过风量(m³/min)' : '风窗道数'}: `,
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 5,
 | 
	
		
			
				|  |  | +        y: 250,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: `${selectData.fWindowM3 ? selectData.fWindowM3 : selectData.nwindownum}`,
 | 
	
		
			
				|  |  | +        font: 'normal 30px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: 330,
 | 
	
		
			
				|  |  | +        y: 250,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      {
 | 
	
		
			
				|  |  | +        text: screenDownText,
 | 
	
		
			
				|  |  | +        font: 'normal 28px Arial',
 | 
	
		
			
				|  |  | +        color: '#009900',
 | 
	
		
			
				|  |  | +        strokeStyle: '#002200',
 | 
	
		
			
				|  |  | +        x: screenDownTextX,
 | 
	
		
			
				|  |  | +        y: 300,
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +    ];
 | 
	
		
			
				|  |  | +    getTextCanvas(750, 546, textArr, '').then((canvas: HTMLCanvasElement) => {
 | 
	
		
			
				|  |  | +      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
 | 
	
		
			
				|  |  | +      const textMaterial = new THREE.MeshBasicMaterial({
 | 
	
		
			
				|  |  | +        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
 | 
	
		
			
				|  |  | +        map: textMap, // 设置纹理贴图
 | 
	
		
			
				|  |  | +        transparent: true,
 | 
	
		
			
				|  |  | +        side: THREE.DoubleSide, // 这里是双面渲染的意思
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      textMap.dispose();
 | 
	
		
			
				|  |  | +      textMaterial.blending = THREE.CustomBlending;
 | 
	
		
			
				|  |  | +      const monitorPlane = this.group?.getObjectByName('monitorText');
 | 
	
		
			
				|  |  | +      if (monitorPlane) {
 | 
	
		
			
				|  |  | +        monitorPlane.material = textMaterial;
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
 | 
	
		
			
				|  |  | +        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
 | 
	
		
			
				|  |  | +        planeMesh.name = 'monitorText';
 | 
	
		
			
				|  |  | +        planeMesh.scale.set(0.003, 0.0034, 0.004);
 | 
	
		
			
				|  |  | +        planeMesh.position.set(4.25, 0.44, -0.27);
 | 
	
		
			
				|  |  | +        this.group?.add(planeMesh);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* 提取风门序列帧,初始化前后门动画 */
 | 
	
		
			
				|  |  | +  initAnimation() {
 | 
	
		
			
				|  |  | +    debugger;
 | 
	
		
			
				|  |  | +    const modalGroup = this.group?.getObjectByName('ddFc-bd3');
 | 
	
		
			
				|  |  | +    // 初始化窗得动画
 | 
	
		
			
				|  |  | +    const meshArr01: THREE.Object3D[] = [];
 | 
	
		
			
				|  |  | +    const meshArr02: THREE.Object3D[] = [];
 | 
	
		
			
				|  |  | +    const fmGroup = modalGroup?.getObjectByName('FengMen_ShouDong_1')?.getObjectByName('FengMen_1');
 | 
	
		
			
				|  |  | +    const fcGroup1 = fmGroup?.getObjectByName('Men_1');
 | 
	
		
			
				|  |  | +    const fcGroup2 = fmGroup?.getObjectByName('Men_2');
 | 
	
		
			
				|  |  | +    if (fcGroup1 && fcGroup2) {
 | 
	
		
			
				|  |  | +      fcGroup1.getObjectByName('shanye_1')?.children.filter((obj) => {
 | 
	
		
			
				|  |  | +        if (obj.name.includes('shanye_1')) {
 | 
	
		
			
				|  |  | +          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
 | 
	
		
			
				|  |  | +          meshArr01.push(obj);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      fcGroup1.getObjectByName('shanye_2')?.children.filter((obj) => {
 | 
	
		
			
				|  |  | +        if (obj.name.includes('shanye_2')) {
 | 
	
		
			
				|  |  | +          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
 | 
	
		
			
				|  |  | +          meshArr01.push(obj);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      fcGroup2.children.filter((obj) => {
 | 
	
		
			
				|  |  | +        if (obj.name.includes('shanye_3')) {
 | 
	
		
			
				|  |  | +          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
 | 
	
		
			
				|  |  | +          meshArr02.push(obj);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      fcGroup2.getObjectByName('shanye_4')?.children.filter((obj) => {
 | 
	
		
			
				|  |  | +        if (obj.name.includes('shanye_4')) {
 | 
	
		
			
				|  |  | +          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
 | 
	
		
			
				|  |  | +          meshArr02.push(obj);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      this.windowsActionArr.frontWindow = [...meshArr01];
 | 
	
		
			
				|  |  | +      this.windowsActionArr.backWindow = [...meshArr02];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // 初始化门的动画
 | 
	
		
			
				|  |  | +    if (modalGroup && fmGroup) {
 | 
	
		
			
				|  |  | +      const tracks = modalGroup.animations[0].tracks;
 | 
	
		
			
				|  |  | +      const fontTracks: any[] = [];
 | 
	
		
			
				|  |  | +      for (let i = 0; i < tracks.length; i++) {
 | 
	
		
			
				|  |  | +        const track = tracks[i];
 | 
	
		
			
				|  |  | +        if (track.name.startsWith('Men')) {
 | 
	
		
			
				|  |  | +          fontTracks.push(track);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      this.mixers = new THREE.AnimationMixer(fmGroup);
 | 
	
		
			
				|  |  | +      const frontDoor = new THREE.AnimationClip('frontDoor', 15, fontTracks);
 | 
	
		
			
				|  |  | +      this.frontClipAction = this.mixers.clipAction(frontDoor, fmGroup);
 | 
	
		
			
				|  |  | +      this.frontClipAction.clampWhenFinished = true;
 | 
	
		
			
				|  |  | +      this.frontClipAction.reset();
 | 
	
		
			
				|  |  | +      this.frontClipAction.time = 0;
 | 
	
		
			
				|  |  | +      this.frontClipAction.timeScale = 1;
 | 
	
		
			
				|  |  | +      this.frontClipAction.clampWhenFinished = true;
 | 
	
		
			
				|  |  | +      this.frontClipAction.loop = THREE.LoopOnce;
 | 
	
		
			
				|  |  | +      this.frontClipAction.play();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  play(rotationParam, flag) {
 | 
	
		
			
				|  |  | +    if (!this.windowsActionArr.frontWindow) {
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (flag === 1) {
 | 
	
		
			
				|  |  | +      // 前风窗动画
 | 
	
		
			
				|  |  | +      this.windowsActionArr.frontWindow.forEach((mesh: THREE.Mesh) => {
 | 
	
		
			
				|  |  | +        gsap.to(mesh.rotation, {
 | 
	
		
			
				|  |  | +          z: THREE.MathUtils.degToRad(rotationParam.frontDeg1),
 | 
	
		
			
				|  |  | +          duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 - mesh.rotation.z),
 | 
	
		
			
				|  |  | +          overwrite: true,
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    } else if (flag === 2) {
 | 
	
		
			
				|  |  | +      // 后风窗动画
 | 
	
		
			
				|  |  | +      this.windowsActionArr.backWindow.forEach((mesh: THREE.Mesh) => {
 | 
	
		
			
				|  |  | +        gsap.to(mesh.rotation, {
 | 
	
		
			
				|  |  | +          z: THREE.MathUtils.degToRad(rotationParam.backDeg1),
 | 
	
		
			
				|  |  | +          duration: (1 / 9) * Math.abs(rotationParam.backDeg1 - mesh.rotation.z),
 | 
	
		
			
				|  |  | +          overwrite: true,
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    } else if (flag === 0) {
 | 
	
		
			
				|  |  | +      ([...this.windowsActionArr.frontWindow] as THREE.Mesh[]).forEach((mesh) => {
 | 
	
		
			
				|  |  | +        gsap.to(mesh.rotation, {
 | 
	
		
			
				|  |  | +          z: THREE.MathUtils.degToRad(90),
 | 
	
		
			
				|  |  | +          overwrite: true,
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* 点击风窗,风窗全屏 */
 | 
	
		
			
				|  |  | +  mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
 | 
	
		
			
				|  |  | +    if (this.animationTimer) {
 | 
	
		
			
				|  |  | +      clearTimeout(this.animationTimer);
 | 
	
		
			
				|  |  | +      this.animationTimer = null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // 判断是否点击到视频
 | 
	
		
			
				|  |  | +    intersects.find((intersect) => {
 | 
	
		
			
				|  |  | +      const mesh = intersect.object;
 | 
	
		
			
				|  |  | +      if (mesh.name === 'player1') {
 | 
	
		
			
				|  |  | +        if (new Date().getTime() - this.playerStartClickTime1 < 400) {
 | 
	
		
			
				|  |  | +          // 双击,视频放大
 | 
	
		
			
				|  |  | +          if (this.player1) {
 | 
	
		
			
				|  |  | +            this.player1.requestFullscreen();
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        this.playerStartClickTime1 = new Date().getTime();
 | 
	
		
			
				|  |  | +        return true;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return false;
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  mouseUpModel() {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* 风门动画 */
 | 
	
		
			
				|  |  | +  render() {
 | 
	
		
			
				|  |  | +    if (!this.model) {
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (this.mixers) {
 | 
	
		
			
				|  |  | +      this.mixers.update(2);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  mountedThree() {
 | 
	
		
			
				|  |  | +    return new Promise((resolve) => {
 | 
	
		
			
				|  |  | +      this.model.setGLTFModel(['ddFc-bd3'], this.group).then(() => {
 | 
	
		
			
				|  |  | +        console.log(this.group);
 | 
	
		
			
				|  |  | +        this.setModalPosition();
 | 
	
		
			
				|  |  | +        this.initAnimation();
 | 
	
		
			
				|  |  | +        resolve(null);
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  destroy() {
 | 
	
		
			
				|  |  | +    if (this.mixers) {
 | 
	
		
			
				|  |  | +      const modalGroup = this.group?.getObjectByName('ddFc-bd3');
 | 
	
		
			
				|  |  | +      const fmGroup = modalGroup?.getObjectByName('FengMen_ShouDong_1')?.getObjectByName('FengMen_1');
 | 
	
		
			
				|  |  | +      this.mixers.uncacheClip(this.frontClipAction.getClip());
 | 
	
		
			
				|  |  | +      this.mixers.uncacheAction(this.frontClipAction.getClip(), fmGroup);
 | 
	
		
			
				|  |  | +      if (fmGroup) this.mixers.uncacheRoot(fmGroup);
 | 
	
		
			
				|  |  | +      if (this.model.animations[0]) this.model.animations[0].tracks = [];
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this.model.clearGroup(this.group);
 | 
	
		
			
				|  |  | +    this.windowsActionArr.frontWindow = undefined;
 | 
	
		
			
				|  |  | +    this.windowsActionArr.backWindow = undefined;
 | 
	
		
			
				|  |  | +    this.model = null;
 | 
	
		
			
				|  |  | +    this.group = null;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +export default ddFc_7;
 |