import * as THREE from 'three'; import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'; import { animateCamera, getTextCanvas, setModalCenter } from '/@/utils/threejs/util'; import Smoke from '/@/views/vent/comment/threejs/Smoke'; import fcFan from './fcfanLocal.three'; import fmFan from './fmfanLocal.three'; import gsap from 'gsap'; // import { setModalCenter } from '/@/utils/threejs/util'; // import * as dat from 'dat.gui'; // const gui = new dat.GUI(); // gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999'; // 本模型的上下文对象,用于实现本模型的特定功能,代码参考了旧有的 fanLocal.three.ts class ModelContext { model; modelName = 'jbfj-hd'; group: THREE.Object3D | null = null; fanType?: string; fcFanObj?: fcFan; fmFanObj?: fmFan; topSmoke?: Smoke; downSmoke?: Smoke; returnSmoke?: Smoke; topLife?: number; downLife?: number; constructor(model) { this.model = model; } addLight() { // optional implementation } mountedThree() { return new Promise((resolve) => { this.model.setGLTFModel([this.modelName]).then(async (gltf) => { this.group = gltf[0]; if (this.group) { const Fengtongbu01 = this.group.getObjectByName('Cylinder1054') as THREE.Mesh; const textMaterial = new THREE.MeshBasicMaterial({ color: '#000', transparent: true, opacity: 0.3, side: THREE.DoubleSide, // 这里是双面渲染的意思 }); Fengtongbu01.material = textMaterial; Fengtongbu01.renderOrder = 300; setModalCenter(this.group); this.addLight(); this.initFly(); this.setModalPosition(); this.fcFanObj = new fcFan(this.model); await this.fcFanObj.mountedThree(); this.fmFanObj = new fmFan(this.model); await this.fmFanObj.mountedThree(); resolve(null); } }); }); } destroy() { if (this.model) { this.model.isRender = false; this.clearFly(); this.topSmoke = undefined; this.downSmoke = undefined; this.returnSmoke = undefined; if (this.fcFanObj) this.fcFanObj.destroy(); if (this.fmFanObj) this.fmFanObj.destroy(); // @ts-ignore this.group = undefined; this.model.destroy(); } } async initFly() { const topCurve = [ { path0: new THREE.Vector3(-89.84, 2.359, 4.91), path1: new THREE.Vector3(-85.678, 2.359, 3.61), isSpread: true, spreadDirection: -1, // }, { path0: new THREE.Vector3(-85.678, 2.352, 3.66), path1: new THREE.Vector3(-85.636, 2.353, -3.829), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(-85.636, 2.353, -3.829), path1: new THREE.Vector3(-85.636, 1.026, -5.881), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(-85.636, 1.026, -5.881), path1: new THREE.Vector3(-85.618, 0.887, -12.862), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(-85.618, 0.827, -12.962), path1: new THREE.Vector3(80.404, 0.827, -12.962), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(80.404, 0.827, -12.962), path1: new THREE.Vector3(93.164, 0.85, -12.962), isSpread: true, spreadDirection: 1, // 1是由小变大,-1是由大变小 }, ]; const downCurve = [ { path0: new THREE.Vector3(-94.84, -0.388, 3.61), path1: new THREE.Vector3(-85.678, -0.393, 3.61), isSpread: true, spreadDirection: -1, // }, { path0: new THREE.Vector3(-85.678, -0.393, 3.275), path1: new THREE.Vector3(-85.636, -0.392, -3.829), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(-85.636, -0.392, -3.829), path1: new THREE.Vector3(-85.636, 0.926, -5.881), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(-85.636, 1.026, -5.881), path1: new THREE.Vector3(-85.618, 0.887, -12.862), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(-85.618, 0.887, -12.962), path1: new THREE.Vector3(80.404, 0.887, -12.962), isSpread: false, spreadDirection: 0, }, { path0: new THREE.Vector3(80.404, 0.887, -12.962), path1: new THREE.Vector3(93.164, 0.91, -12.962), isSpread: true, spreadDirection: 1, // 1是由小变大,-1是由大变小 }, ]; const returnCurve = [ { path0: new THREE.Vector3(93.164, 0.85, -12.962), path1: new THREE.Vector3(86.39, 0.827, -12.962), isSpread: false, spreadDirection: 2, }, { path0: new THREE.Vector3(86.39, 0.827, -12.962), path1: new THREE.Vector3(83.341, 0.847, -17.658), isSpread: false, spreadDirection: 2, }, { path0: new THREE.Vector3(83.341, 0.847, -17.658), path1: new THREE.Vector3(-29.077, 0.847, -17.658), isSpread: false, spreadDirection: 2, }, { path0: new THREE.Vector3(-29.077, 0.847, -17.658), path1: new THREE.Vector3(-29.64, 0.827, -39.047), isSpread: false, spreadDirection: 2, }, ]; if (!this.topSmoke) { this.topSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400); this.topSmoke.setPath(topCurve); await this.topSmoke.setPoints(); this.group?.add(this.topSmoke.points); } if (!this.downSmoke) { this.downSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400); this.downSmoke.setPath(downCurve); await this.downSmoke.setPoints(); this.group?.add(this.downSmoke.points); } if (!this.returnSmoke) { this.returnSmoke = new Smoke('/model/img/texture-smoke.png', '#777777', 0, 0.35, 1.5, 200); this.returnSmoke.setPath(returnCurve); await this.returnSmoke.setPoints(); this.group?.add(this.returnSmoke.points); } } playSmoke(selectData) { // debugger; // console.log('selectData[Fan1fHz]------------》', selectData['Fan1fHz'], Number(selectData['Fan1fHz'])); if (selectData['Fan1StartStatus'] == '1') { // 主风机打开 // setSmokeFrequency('top', Number(selectData['Fan1fHz']) || 40); this.setSmokeFrequency('top', 40); this.runFly('top', 'open'); } else { // 备风机关闭 this.runFly('top', 'close'); } if (selectData['Fan2StartStatus'] == '1') { // 备风机打开 // setSmokeFrequency('down', Number(selectData['Fan2fHz']) || 40); this.setSmokeFrequency('down', 40); this.runFly('down', 'open'); } else { // 备风机关闭 this.runFly('down', 'close'); } if (selectData['Fan1StartStatus'] != '1' && selectData['Fan2StartStatus'] != '1') { this.runFly('all', 'close'); } // if (frequency) { // setSmokeFrequency(deviceType, frequency); // } // if (controlType === 'startSmoke') { // runFly(deviceType, state); // } } runFly(deviceType, state) { if (state === 'open') { if (deviceType === 'top') { if (this.downSmoke && this.downSmoke.frameId) { this.downSmoke.stopSmoke(); } if (this.topSmoke && !this.topSmoke.frameId) { this.topSmoke.startSmoke(); } } else { if (this.topSmoke && this.topSmoke.frameId) { this.topSmoke.stopSmoke(); } if (this.downSmoke && !this.downSmoke.frameId) { this.downSmoke.startSmoke(); } } if (this.returnSmoke && !this.returnSmoke.frameId) { this.returnSmoke?.startSmoke(); } } else { if (deviceType === 'top') { if (this.topSmoke && this.topSmoke.frameId) { this.topSmoke.stopSmoke(); } } else { if (this.downSmoke && this.downSmoke.frameId) { this.downSmoke.stopSmoke(); } } } if (deviceType === 'all' && state === 'close') { this.returnSmoke?.stopSmoke(); } } setSmokeFrequency(deviceType, frequency) { const life = (frequency - 30) * 25; let duration = 0; let smoke; if (deviceType === 'top') { if (this.topLife == life) { return; } this.topLife = life; smoke = this.topSmoke; duration = (Math.abs(life - smoke.life) / 500) * 25; } else { if (this.downLife == life) { return; } this.downLife = life; smoke = this.downSmoke; duration = (Math.abs(life - smoke.life) / 500) * 25; } if (smoke) { gsap.to(smoke, { life: life, duration: duration, ease: 'easeInCubic', overwrite: true, }); } } addText(selectData) { if (!this.group) { return; } // @ts-ignore const screenDownText = VENT_PARAM['modalText'] ? // @ts-ignore VENT_PARAM['modalText'] : // @ts-ignore History_Type['type'] == 'remote' ? `国能神东煤炭集团监制` : '煤炭科学技术研究院有限公司研制'; const screenDownTextX = 80 - (screenDownText.length - 10) * 6; const textArr = [ { text: `智能局部通风机监测与控制系统`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 20, y: 108, }, { text: `供风距离(m):`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 152, }, { text: `${ selectData.airSupplyDistence_merge ? selectData.airSupplyDistence_merge : selectData.fchimenylength ? selectData.fchimenylength : selectData.airSupplyDistence_merge ? selectData.airSupplyDistence_merge : '-' }`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 228, y: 152, }, { text: `风筒直径(mm): `, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 200, }, { text: ` ${selectData.fchimenydiamlimit ? selectData.fchimenydiamlimit : selectData.ductDiameter_merge ? selectData.ductDiameter_merge : '-'}`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 220, y: 200, }, { text: `故障诊断:`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 245, }, { text: `${selectData.warnLevel_str ? selectData.warnLevel_str : '-'}`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 220, y: 245, }, { text: `型号功率:`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 285, }, { text: `${selectData.model_Power_merge ? selectData.model_Power_merge : '-'}`, font: 'normal 26px Arial', color: '#009900', strokeStyle: '#002200', x: 220, y: 285, }, { text: screenDownText, font: 'normal 28px Arial', color: '#009900', strokeStyle: '#002200', x: screenDownTextX, y: 325, }, ]; getTextCanvas(526, 346, textArr, '').then((canvas: HTMLCanvasElement) => { const textMap = new THREE.CanvasTexture(canvas); // 关键一步 const textMaterial = new THREE.MeshBasicMaterial({ // 关于材质并未讲解 实操即可熟悉 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质. map: textMap, // 设置纹理贴图 transparent: true, side: THREE.FrontSide, // 这里是双面渲染的意思 }); textMaterial.blending = THREE.CustomBlending; const monitorPlane = this.group?.getObjectByName('monitorText'); if (monitorPlane) { // @ts-ignore-next-line 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.0135, 0.0135, 0.0135); planeMesh.rotation.y = -Math.PI / 2; planeMesh.position.set(-84.79, 0.82, 17.0); this.group?.add(planeMesh); } }); } addCssText() { if (!this.group) return; if (!this.group.getObjectByName('text1')) { const element = document.getElementById('inputBox') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text1'; fanLocalCSS3D.scale.set(0.04, 0.04, 0.04); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-85.68, 5.97, -3.39); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text2')) { const element = document.getElementById('outBox') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text2'; fanLocalCSS3D.scale.set(0.15, 0.15, 0.15); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(85.62, 17.65, 7.71); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text3')) { const element = document.getElementById('returnBox') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text3'; fanLocalCSS3D.scale.set(0.07, 0.07, 0.07); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-25.97, 9.3, -15.09); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text4')) { const element = document.getElementById('gateBox') as HTMLElement; if (element) { // element.innerHTML = ''; const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text4'; fanLocalCSS3D.scale.set(0.04, 0.04, 0.04); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-73.13, 8.44, -23.52); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text5')) { const element = document.getElementById('windownBox') as HTMLElement; if (element) { // element.innerHTML = ''; const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text5'; fanLocalCSS3D.scale.set(0.07, 0.07, 0.07); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-28.44, 9.78, -40.42); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text7')) { const element = document.getElementById('inputBox0') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text7'; fanLocalCSS3D.scale.set(0.04, 0.04, 0.04); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-84.23, 4.97, -18.92); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text6')) { const element = document.getElementById('inputBox1') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text6'; fanLocalCSS3D.scale.set(0.04, 0.04, 0.04); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-84.23, 6.01, -0.03); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text8')) { const element = document.getElementById('gasBox3') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text8'; fanLocalCSS3D.scale.set(0.03, 0.03, 0.03); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-90.04, 6, 5); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text9')) { const element = document.getElementById('gasBox2') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text9'; fanLocalCSS3D.scale.set(0.07, 0.07, 0.07); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(-8, 7.46, -35.28); this.group.add(fanLocalCSS3D); } } if (!this.group.getObjectByName('text10')) { const element = document.getElementById('gasBox1') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text10'; fanLocalCSS3D.scale.set(0.1, 0.1, 0.1); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(80, 9, -43); this.group.add(fanLocalCSS3D); } } } /** 设置模型类型并切换,不同的类型通常对应不同的具体模型,在模型总控制器下的具体模型会根据传入的参数彼此交互、切换 */ setModelType(type: 'fm' | 'fc' | string) { this.fanType = type; return new Promise((resolve) => { if (!this.group) return; // 显示双道风窗 let childGroup; if (this.fanType === 'fm' && this.fcFanObj && this.fcFanObj.group) { if (this.group?.getObjectByName('jbfj-fc')) { this.group?.remove(this.fcFanObj.group); } childGroup = this.fmFanObj?.group; if (this.group && this.group?.getObjectByName('text5') && this.group?.getObjectByName('text4')) { // @ts-ignore-next-line this.group.getObjectByName('text5')['visible'] = false; // @ts-ignore-next-line this.group.getObjectByName('text4')['visible'] = true; } } else if (this.fanType === 'fc' && this.fmFanObj && this.fmFanObj.group) { // 显示单道风窗 if (this.group?.getObjectByName('jbfj-fm')) { this.group?.remove(this.fmFanObj.group); } childGroup = this.fcFanObj?.group; if (this.group && this.group?.getObjectByName('text5') && this.group?.getObjectByName('text4')) { // @ts-ignore-next-line this.group.getObjectByName('text5')['visible'] = true; // @ts-ignore-next-line this.group.getObjectByName('text4')['visible'] = false; } } else { if (this.group?.getObjectByName('jbfj-fc')) { // @ts-ignore-next-line this.group?.remove(this.fcFanObj.group); } if (this.group?.getObjectByName('jbfj-fm')) { // @ts-ignore-next-line this.group?.remove(this.fmFanObj.group); } if (this.group && this.group?.getObjectByName('text5') && this.group?.getObjectByName('text4')) { // @ts-ignore-next-line this.group.getObjectByName('text5')['visible'] = false; // @ts-ignore-next-line this.group.getObjectByName('text4')['visible'] = false; } childGroup = null; } setTimeout(async () => { if (childGroup) this.group?.add(childGroup); // const oldCameraPosition = { x: 615, y: 275, z: 744 }; // await animateCamera( // oldCameraPosition, // { x: 0, y: 0, z: 0 }, // { x: 0.08, y: 21.73, z: 78.45 }, // { x: 0.13, y: -0.82, z: 0.236 }, // this.model, // 0.8 // ); resolve(null); }, 300); }); } clearFly() { if (this.topSmoke) this.topSmoke.clearSmoke(); if (this.downSmoke) this.downSmoke.clearSmoke(); if (this.returnSmoke) this.returnSmoke.clearSmoke(); } // 设置模型位置 setModalPosition() { if (!this.group) return; this.group.scale.set(0.7, 0.7, 0.7); this.group.position.set(0, 6, -50); this.group.rotation.y = Math.PI / 2; } } export default ModelContext;