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, 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 = () => { 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; (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; (group?.getObjectByName('text5')).visible = true; (group?.getObjectByName('text4')).visible = false; } setTimeout(async () => { 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 textArr = [ { text: `煤矿巷道远程局部风机系统`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 50, y: 110, }, { text: `进风量(m³/min):`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 165, }, { text: `${selectData.windQuantity1}`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 290, y: 165, }, { text: `供风量(m³/min): `, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 220, }, { text: ` ${selectData.windQuantity2}`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 280, y: 220, }, { text: `故障诊断:`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 0, y: 275, }, { text: `${selectData.fault}`, font: 'normal 30px Arial', color: '#009900', strokeStyle: '#002200', x: 280, y: 275, }, { text: `煤炭科学技术研究院有限公司研制`, font: 'normal 28px Arial', color: '#009900', strokeStyle: '#002200', x: 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.01); group.add(planeMesh); } }); }; // // css3D文字 export const addCssText = () => { if (!group) return; if (!group.getObjectByName('text1')) { const element = document.getElementById('inputBox') as HTMLElement; 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, -17.74); group.add(fanLocalCSS3D); } if (!group.getObjectByName('text2')) { const element = document.getElementById('outBox') as HTMLElement; 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; const fanLocalCSS3D = new CSS3DObject(element); fanLocalCSS3D.name = 'text3'; fanLocalCSS3D.scale.set(0.1, 0.1, 0.1); fanLocalCSS3D.rotation.y = -Math.PI / 2; fanLocalCSS3D.position.set(35.28, 10.05, -37.23); 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); } } }; // export const playSmoke = (controlType, deviceType, frequency, state) => { // if (frequency) { // setSmokeFrequency(deviceType, frequency); // } // if (controlType === 'startSmoke') { // runFly(deviceType, state); // } // }; export const playSmoke = (selectData) => { 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 (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是由大变小 }, ]; if (!topSmoke) { topSmoke = new Smoke('/model/img/texture-smoke.png', '#ffffff', 0, 0.8, 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.8, 0.5, 400); downSmoke.setPath(downCurve); await downSmoke.setPoints(); group?.add(downSmoke.points); } }; const runFly = (deviceType, state) => { if (state === 'open') { if (deviceType === 'top') { if (downSmoke.frameId) { downSmoke.stopSmoke(); } if (!topSmoke.frameId) { topSmoke.startSmoke(); } } else { if (topSmoke.frameId) { topSmoke.stopSmoke(); } if (!downSmoke.frameId) { downSmoke.startSmoke(); } } } else { if (deviceType === 'top') { if (topSmoke.frameId) { topSmoke.stopSmoke(); } } else { if (downSmoke.frameId) { downSmoke.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(); }; // 初始化事件 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; if (mesh.name === 'player1') { if (new Date().getTime() - playerStartClickTime1 < 400) { // 双击,视频放大 if (player1) { player1.requestFullscreen(); } } playerStartClickTime1 = new Date().getTime(); return true; } }); }); 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 = new UseThree('#fanLocal3D', '#fanLocal3DCSS'); model = new UseThree('#fanLocal3D'); model.setEnvMap('test1'); 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; } 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(); const videoPlayer1 = document.getElementById('jb-player1')?.getElementsByClassName('vjs-tech')[0]; if (videoPlayer1) { const mesh = renderVideo(group, videoPlayer1, 'player1'); if (mesh) { mesh.scale.set(0.222, 0.19, 0.2); mesh.position.set(-84.87, 0.298, 24.76); mesh.rotation.y = -Math.PI / 2; group.add(mesh); } } else { const textArr = [ { text: `无信号输入`, font: 'normal 40px Arial', color: '#009900', strokeStyle: '#002200', x: 170, y: 40, }, ]; getTextCanvas(560, 346, textArr, '').then((canvas: HTMLCanvasElement) => { const textMap = new THREE.CanvasTexture(canvas); // 关键一步 const textMaterial = new THREE.MeshBasicMaterial({ map: textMap, // 设置纹理贴图 transparent: true, side: THREE.DoubleSide, // 这里是双面渲染的意思 }); textMaterial.blending = THREE.CustomBlending; const monitorPlane = group?.getObjectByName('noPlayer'); if (monitorPlane) { monitorPlane.material = textMaterial; } else { const planeGeometry = new THREE.PlaneGeometry(100, 100); // 平面3维几何体PlaneGeometry const planeMesh = new THREE.Mesh(planeGeometry, textMaterial); if (!videoPlayer1) { planeMesh.name = 'noPlayer'; planeMesh.scale.set(0.07, 0.05, 0.07); planeMesh.position.set(-84.82, -1.53, 24.94); planeMesh.rotation.y = -Math.PI / 2; group?.add(planeMesh.clone()); } } }); } startAnimation(); resolve(model); }); }); }; export const destroy = () => { if (model) { model.isRender = false; console.log('场景销毁后信息----------->', model.renderer?.info); clearFly(); topSmoke = undefined; downSmoke = undefined; if (fcFanObj) fcFanObj.destroy(); if (fmFanObj) fmFanObj.destroy(); group = undefined; model.destroy(); model = undefined; } };