import * as THREE from 'three'; import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js'; import { animateCamera, getTextCanvas, renderVideo, updateAxisCenter } from '/@/utils/threejs/util'; import UseThree from '../../../../utils/threejs/useThree'; import fcFan from './fcfanLocal.three'; import fmFan from './fmfanLocal.three'; import Smoke from '/@/views/vent/comment/threejs/Smoke'; import gsap from 'gsap'; import useEvent from '../../../../utils/threejs/useEvent'; // import * as dat from 'dat.gui'; // const gui = new dat.GUI(); // gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999'; const modelName = 'jbfj-hd'; // 模型对象、 文字对象 let model: UseThree | undefined, group: THREE.Object3D | undefined, fcFanObj: fcFan | undefined, fmFanObj: fmFan | undefined, player1, fanType, topSmoke: Smoke | undefined, downSmoke: Smoke | undefined, // returnSmoke: Smoke | undefined, topLife: number | undefined, downLife: number | undefined, playerStartClickTime1 = new Date().getTime(); const { mouseDownFn, mousemoveFn, mouseUpFn } = useEvent(); // 打灯光 const addLight = () => { const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2); directionalLight.position.set(-564, 602, -261); group?.add(directionalLight); // directionalLight.target = group; // const spotLight = new THREE.SpotLight(); // spotLight.angle = Math.PI / 16; // spotLight.penumbra = 0; // spotLight.castShadow = true; // spotLight.position.set(0, 463, 687); // scene.add(spotLight); // spotLight.shadow.camera.near = 0.5; // default // spotLight.shadow.camera.far = 1000; // default // spotLight.shadow.focus = 1; // spotLight.shadow.bias = -0.000002; // spotLight.target = group; // const pointLight6 = new THREE.PointLight(0xffffff, 1, 500); // pointLight6.position.set(-131, 64, -1); // pointLight6.shadow.bias = 0.05; // group?.add(pointLight6); // gui.add(directionalLight.position, 'x', -1000, 1000); // gui.add(directionalLight.position, 'y', -1000, 1000); // gui.add(directionalLight.position, 'z', -1000, 1000); // gui.add(pointLight6.position, 'x', -500, 500); // gui.add(pointLight6.position, 'y', -500, 500); // gui.add(pointLight6.position, 'z', -500, 500); // gui.add(pointLight6, 'distance', 0, 500); }; // 重置摄像头 const resetCamera = () => { model.camera.position.set(0, -1000, 100); model.camera.far = 1000; model.orbitControls?.update(); model.camera.updateProjectionMatrix(); }; // 设置模型位置 const setModalPosition = () => { if (!group) return; group.position.set(0, 10, -50); group.rotation.y = Math.PI / 2; }; const setControls = () => { if (model && model.orbitControls) { model.orbitControls.panSpeed = 0.5; model.orbitControls.rotateSpeed = 0.5; model.orbitControls.maxPolarAngle = Math.PI / 2.4; model.orbitControls.minPolarAngle = Math.PI / 3; } }; // 切换局部通风机类型 export const setModelType = (type) => { fanType = type; return new Promise((resolve) => { // 显示双道风窗 let childGroup; if (fanType === 'fm' && fcFanObj && fcFanObj.group) { if (group?.getObjectByName('jbfj-fc')) { group?.remove(fcFanObj.group); } childGroup = fmFanObj?.group; if (group && group?.getObjectByName('text5') && group?.getObjectByName('text4')) { group.getObjectByName('text5')['visible'] = false; group.getObjectByName('text4')['visible'] = true; } } else if (fanType === 'fc' && fmFanObj && fmFanObj.group) { // 显示单道风窗 if (group?.getObjectByName('jbfj-fm')) { group?.remove(fmFanObj.group); } childGroup = fcFanObj?.group; if (group && group?.getObjectByName('text5') && group?.getObjectByName('text4')) { group.getObjectByName('text5')['visible'] = true; group.getObjectByName('text4')['visible'] = false; } } else { if (group?.getObjectByName('jbfj-fc')) { group?.remove(fcFanObj.group); } if (group?.getObjectByName('jbfj-fm')) { group?.remove(fmFanObj.group); } if (group && group?.getObjectByName('text5') && group?.getObjectByName('text4')) { group.getObjectByName('text5')['visible'] = false; group.getObjectByName('text4')['visible'] = false; } childGroup = null; } setTimeout(async () => { if (childGroup) 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 }, model, 0.8); resolve(null); }, 300); }); }; /* 添加监控数据 */ export const addText = (selectData) => { if (!group) { return; } const screenDownText = VENT_PARAM['modalText'] ? VENT_PARAM['modalText'] : 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.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, //20 煤炭科学技术研究院有限公司研制 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 = 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.0135, 0.0135, 0.0135); planeMesh.rotation.y = -Math.PI / 2; planeMesh.position.set(-84.79, 0.82, 17.0); group.add(planeMesh); } }); }; // // css3D文字 export const addCssText = () => { if (!group) return; if (!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); group.add(fanLocalCSS3D); } } if (!group.getObjectByName('text2')) { const element = document.getElementById('outBox') as HTMLElement; if (element) { const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text2'; fanLocalCSS3D.scale.set(0.1, 0.1, 0.1); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(74.63, 13.54, 3.84); group.add(fanLocalCSS3D); } } if (!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); group.add(fanLocalCSS3D); } } if (!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); group.add(fanLocalCSS3D); } } if (!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); group.add(fanLocalCSS3D); } } if (!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, 4.97, -18.92); group.add(fanLocalCSS3D); } } }; // export const playSmoke = (controlType, deviceType, frequency, state) => { // if (frequency) { // setSmokeFrequency(deviceType, frequency); // } // if (controlType === 'startSmoke') { // runFly(deviceType, state); // } // }; export const playSmoke = (selectData) => { // debugger; console.log('selectData[Fan1fHz]------------》', selectData['Fan1fHz'], Number(selectData['Fan1fHz'])); if (selectData['Fan1StartStatus'] == '1') { // 主风机打开 // setSmokeFrequency('top', Number(selectData['Fan1fHz']) || 40); setSmokeFrequency('top', 40); runFly('top', 'open'); } else { // 备风机关闭 runFly('top', 'close'); } if (selectData['Fan2StartStatus'] == '1') { // 备风机打开 // setSmokeFrequency('down', Number(selectData['Fan2fHz']) || 40); setSmokeFrequency('down', 40); runFly('down', 'open'); } else { // 备风机关闭 runFly('down', 'close'); } if (selectData['Fan1StartStatus'] != '1' && selectData['Fan2StartStatus'] != '1') { runFly('all', 'close'); } // if (frequency) { // setSmokeFrequency(deviceType, frequency); // } // if (controlType === 'startSmoke') { // runFly(deviceType, state); // } }; const initFly = async () => { 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 (!topSmoke) { topSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400); topSmoke.setPath(topCurve); await topSmoke.setPoints(); group?.add(topSmoke.points); } if (!downSmoke) { downSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.75, 0.5, 400); downSmoke.setPath(downCurve); await downSmoke.setPoints(); group?.add(downSmoke.points); } if (!returnSmoke) { returnSmoke = new Smoke('/model/img/texture-smoke.png', '#777777', 0, 0.35, 1.5, 200); returnSmoke.setPath(returnCurve); await returnSmoke.setPoints(); group?.add(returnSmoke.points); } }; const runFly = (deviceType, state) => { if (state === 'open') { if (deviceType === 'top') { if (downSmoke && downSmoke.frameId) { downSmoke.stopSmoke(); } if (topSmoke && !topSmoke.frameId) { topSmoke.startSmoke(); } } else { if (topSmoke && topSmoke.frameId) { topSmoke.stopSmoke(); } if (downSmoke && !downSmoke.frameId) { downSmoke.startSmoke(); } } if (returnSmoke && !returnSmoke.frameId) { returnSmoke?.startSmoke(); } } else { if (deviceType === 'top') { if (topSmoke && topSmoke.frameId) { topSmoke.stopSmoke(); } } else { if (downSmoke && downSmoke.frameId) { downSmoke.stopSmoke(); } } } if (deviceType === 'all' && state === 'close') { returnSmoke?.stopSmoke(); } }; // 调频 30-50 (life 300 - 800) , 25 = (800 - 300)/ 20 const setSmokeFrequency = (deviceType, frequency) => { const life = (frequency - 30) * 25; let duration = 0; let smoke; if (deviceType === 'top') { if (topLife == life) { return; } topLife = life; smoke = topSmoke; duration = (Math.abs(life - smoke.life) / 500) * 25; } else { if (downLife == life) { return; } downLife = life; smoke = downSmoke; duration = (Math.abs(life - smoke.life) / 500) * 25; } if (smoke) { gsap.to(smoke, { life: life, duration: duration, ease: 'easeInCubic', overwrite: true, }); } }; const clearFly = () => { if (topSmoke) topSmoke.clearSmoke(); if (downSmoke) downSmoke.clearSmoke(); if (returnSmoke) returnSmoke.clearSmoke(); }; // 初始化事件 const startAnimation = () => { // 定义鼠标点击事件 model.canvasContainer?.addEventListener('mousedown', mouseEvent.bind(null)); model.canvasContainer?.addEventListener('pointerup', mouseUp); }; // 鼠标点击、松开事件 const mouseEvent = (event) => { if (event.button == 0) { model?.canvasContainer?.addEventListener('mousemove', mousemove); mouseDownFn(model, group, event, (intersects) => { intersects.find((intersect) => { const mesh = intersect.object; }); }); console.log('摄像头控制信息', model?.orbitControls, model?.camera); } }; const mouseUp = () => { if (!model) return; mouseUpFn(model); model.canvasContainer?.removeEventListener('mousemove', mousemove); }; const mousemove = () => { mousemoveFn(); }; export const mountedThree = (playerVal1) => { player1 = playerVal1; return new Promise((resolve) => { // model = VENT_PARAM['simulatedPassword'] ? new UseThree('#fanLocal3D', '#fanLocal3DCSS') : new UseThree('#fanLocal3D'); model = new UseThree('#fanLocal3D', '#fanLocal3DCSS'); model.setEnvMap('test1.hdr'); model.renderer.toneMappingExposure = 1.0; if (model.renderer) { model.renderer.sortObjects = true; } resetCamera(); model.setGLTFModel([modelName]).then(async (gltf) => { group = gltf[0]; const Fengtongbu01 = group?.getObjectByName('Cylinder1054') as THREE.Mesh; if (Fengtongbu01) { const textMaterial = new THREE.MeshBasicMaterial({ color: '#000', transparent: true, opacity: 0.3, side: THREE.DoubleSide, // 这里是双面渲染的意思 }); Fengtongbu01.material = textMaterial; Fengtongbu01.renderOrder = 300; } // 隐藏风机文字 const planeTextFan1 = group?.getObjectByName('Plane12125') as THREE.Mesh; const planeTextFan2 = group?.getObjectByName('Plane12128') as THREE.Mesh; planeTextFan1.visible = false; planeTextFan2.visible = false; model?.scene?.add(group); setModalPosition(); setControls(); await initFly(); model?.animate(); addLight(model?.scene); fcFanObj = new fcFan(model); await fcFanObj.mountedThree(); fmFanObj = new fmFan(model); await fmFanObj.mountedThree(); startAnimation(); resolve(model); }); }); }; export const destroy = () => { if (model) { model.isRender = false; console.log('场景销毁后信息----------->', model.renderer?.info); clearFly(); topSmoke = undefined; downSmoke = undefined; returnSmoke = undefined; if (fcFanObj) fcFanObj.destroy(); if (fmFanObj) fmFanObj.destroy(); group = undefined; model.destroy(); model = undefined; } };