import { handleLClick } from "./handleRLclick/handleLclick"; import { handleRClick } from "./handleRLclick/handleRclick"; import { handleLHover } from "./handleRLclick/handleLhover.js"; import { handleDBLClick } from "./handleDBLclick"; import { checkAnimation } from "./checkTag"; import { handleEndChange, handleStartChange, } from "./handleOrbitControlsChange"; import { saveState, resetState } from "./viewBack"; import addFunction from "./addEvent"; import { editTunnelInit } from "./editTunnelInit"; import { addEquipment, removeEquipment } from "./editEquipment"; import previewtunnelModeInit from "./previewTunnelInit"; import { StreetSignTag } from "./utils/StreetSignTag/StreetSignTag.js"; // import { fa } from "element-plus/es/locale/index.js"; // import { ref } from "vue"; export default class Demo { // 摄像机看向位置s origin = null; //附着点的模型122 points = []; //设备模型数组 deviceModels = []; constructor(three, mountedElement) { this._StreetSignTag = StreetSignTag; this._handleLClick = handleLClick; this._handleLHover = handleLHover; this._handleRClick = handleRClick; this._handleDBLClick = handleDBLClick; this._checkAnimation = checkAnimation; this._saveState = saveState; this._resetState = resetState; this.addEquipment = addEquipment; this.removeEquipment = removeEquipment; this.previewtunnelModeInit = previewtunnelModeInit; this.editTunnelInit = editTunnelInit; // 外部可添加函数 this.addFunction = addFunction; this.THREE = three; this.mountedElement = mountedElement; //初始化场景 this.scene = new this.THREE.Scene(); //初始化光影 this.ambientLight = new this.THREE.AmbientLight(0xffffff, 2); //设置环境光 // 初始化摄像机 this.camera = new this.THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); this.camera.position.z = 1.24; this.camera.position.y = 9.14; this.camera.position.x = -63.79; this.camera.lookAt(0, 0, 1000); // 初始化渲染器 this.renderer = new this.THREE.WebGLRenderer({ // 抗锯齿 antialias: true, }); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.domElement.style.position = "absolute"; this.mountedElement.appendChild(this.renderer.domElement); // 创建渲染帧 this.render = () => { this.__renderScope(); requestAnimationFrame(this.render); }; this.render(); this.canvasResize(); this.addXYZ(); this.addLight(); } //渲染函数作用域(这里主要写渲染帧内操作) __renderScope() { this.renderer.render(this.scene, this.camera); // 轨道控制器更新 if (this.orbitControls) { this.orbitControls.update(); // console.log(this.camera.position); } //html标签渲染 if (this.CSS2Renderer) { this.CSS2Renderer.render(this.scene, this.camera); } // 选中标签动画渲染 if (this.checkAnimationTween) { this.checkAnimationTween.update(); } // 视角重置动画 if (this.resetViewAngleAnimation) { this.resetViewAngleAnimation.update(); } //视角回退动画 if (this.viewSateResetAnimation) { this.viewSateResetAnimation.update(); } // 视角跟随动画 if (this.intoPointAnimation) { this.intoPointAnimation.update(); } // 隧道颜色渐变动画 if (this.opacityTween) { this.opacityTween.update(); } // 双击进入动画 if (this.dblIntoTween) { this.dblIntoTween.update(); } // 风机叶片旋转动画 if (this.fanSpinArray && this.fanSpinArray.length) { this.fanSpinArray.forEach((element) => element.update()); } } // 添加世界坐标系 addXYZ() { // const axesHelper = new this.THREE.AxesHelper(100); // this.scene.add(axesHelper); } addLight() { this.scene.add(this.ambientLight); //将环境光添加到场景中 } // 屏幕自适应 canvasResize() { window.addEventListener("resize", () => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight); }); } // 模型加载 /** * @param {Object} GLTFLoader 模型加载器 * @param {String} path 模型资源路径 */ loadModel(GLTFLoader, path, isedit) { return new Promise((resolve, reject) => { this.gltfloader = new GLTFLoader(); this.gltfloader.load( //下面是模型加载的路径 path, (gltf) => { // console.log(isedit); gltf.scene.traverse((child) => { this._forModels(child); }); // 初始化模型缩放倍率 gltf.scene.scale.set(0.75, 0.75, 0.75); // 初始化场景位置 gltf.scene.position.set(-5, 0, 10); this.scene.add(gltf.scene); // 加载完后可执行函数 // console.log(isedit); this._afterLoaded(gltf.scene, isedit); resolve(gltf); }, (xhr) => { // console.log((xhr.loaded / xhr.total) * 100 + "% loaded"); }, (error) => { console.log(error); reject(error); } ); }); } // 模型加载完函数 _afterLoaded(scene, isedit) { // console.log(isedit); // 可以进行选中 this._hoverModel(this.points, isedit); // 可以进行点击 if (isedit == true) { this._ClickModel(this.points); } //将墙壁进行隐藏 this.WallInit(); //对路牌进行相关操作 // this.SignsInf() } // 在此方法中对模型批量操作,这里遍历附着点 _forModels(child) { // 匹配附着点(这里不适合对单个模型进行保存,批量对模型进行操作) if (child.isMesh && /^point+/.test(child.name)) { this.points.push(child); // 改变为基础材质 child.material = new this.THREE.MeshBasicMaterial(); child.scale.set(0.01, 0.01, 0.01); if (/b[rl]$/.test(child.name)) child.translateZ(2); // 遍历一个属性是否存在设备 child.hasDevice = false; //初始化 } // child.material.emissive = child.material.color // child.material.emissiveMap = child.material.map } //是否显示附着点 pointsVisible(isEnable = true) { this.points.forEach((element) => { element.material.visible = isEnable; }); } // 添加轨道控制器 /** * @param {Object} OrbitControls 轨道控制器 */ addOrbitControls(OrbitControls, isedit) { this.orbitControls = new OrbitControls( this.camera, this.renderer.domElement ); //初始化轨道控制器 this.orbitControls.object.position.set(-29, 18, -50); // this.orbitControls.maxPolarAngle = (4 * Math.PI) / 7; //下面的设置是使隧道的上下不进行旋转,只左右旋转即可 if (isedit == true) { this.orbitControls.maxPolarAngle = (3.69 * Math.PI) / 7; } else { this.orbitControls.maxPolarAngle = (4 * Math.PI) / 8.5; this.orbitControls.minPolarAngle = (4 * Math.PI) / 8.5; } this.orbitControls.enableDamping = true; // 初始化轨道控制器距离 this.orbitControls.minDistance = 5; this.orbitControls.maxDistance = 87; // 监听控制器变化 this.orbitControls.addEventListener("end", handleEndChange.bind(this)); this.orbitControls.addEventListener("start", handleStartChange.bind(this)); } /** * @param {Class} Tween 添加补间动画 */ addTween(Tween) { this.TWEEN = Tween; } /*** * 鼠标移入可以高亮的模型 *@param {Array} hoverModels 需要高亮的模型 */ _hoverModel(hoverModels, isedit) { this.renderer.domElement.addEventListener("mousemove", (e) => { const mouse = new this.THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; const raycaster = new this.THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects(hoverModels); if (intersects.length > 0) { if (!this.preHover) { this.preHover = intersects[0].object; } else { if (isedit != true) { // console.log(isedit); //附着点设置方框(已经被选中了的情况) this.setBoxHelper(intersects[0].object); this._handleLHover(intersects[0].object); } this.preHover.material.color.set("white"); this.preHover = intersects[0].object; } intersects[0].object.material.color.set("red"); const tagP = intersects[0].object.getWorldPosition( new this.THREE.Vector3() ); // if (isedit != false) { this._checkAnimation(tagP); // } } else { if (!this.preHover) return; //当鼠标移出的时候,将轨道控制器打开,并且将面板关闭 if (isedit != true) { this.isControlOrbit(true); this.clearTagsObj(isedit); } this.preHover.material.color.set("white"); } // this._handleLHover(intersects[0].object); // this._handleLClick(intersects[0].object); }); } /** * @param {Array} isClickModels 可以进行点击的模型吧 */ _ClickModel(isClickModels) { // 点击两次处理事件 this.renderer.domElement.addEventListener( "dblclick", this.__handleListenerDBLClickEvent.bind(this, isClickModels) //注意这里重新指向this,第一个是修改this指向,第二个是传入函数的参数 ); // 点击一次时处理时间 this.renderer.domElement.addEventListener( "mousedown", this.__handleListenerClickRLEvent.bind(this, isClickModels) //注意这里重新指向this,第一个是修改this指向,第二个是传入函数的参数 ); } // 处理点击2次触发事件事件 __handleListenerDBLClickEvent(isClickModels, e) { e.preventDefault(); // 如果监听到双击则清空单次点击事件 clearTimeout(this.EventTimer); //最后一个是监听器默认参数 const mouse = new this.THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; const raycaster = new this.THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects(isClickModels); // 取消定位的视角返回初始化 if (intersects.length === 0) { this.clearTagsObj(); this._resetState(); return; } // 处理连续双击击事件 this._handleDBLClick(intersects[0].object); } // 处理点击左右键触发的事件 __handleListenerClickRLEvent(isClickModels, e) { e.preventDefault(); if (!this.enableLeftBtn) return; clearTimeout(this.EventTimer); this.EventTimer = setTimeout(() => { const mouse = new this.THREE.Vector2(); mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; const raycaster = new this.THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects(isClickModels); if (intersects.length === 0) return; //附着点设置方框 this.setBoxHelper(intersects[0].object); // 处理点击左右键事件 if (e.button === 0) { this._handleLClick(intersects[0].object); } else if (e.button === 2) { this._handleRClick(intersects[0].object); } }, 400); } // 添加css3Renderer addCSS3Renderer(CSS2Renderer, CSS2DObject, [infoDom, editDom]) { this.CSS2Renderer = new CSS2Renderer(); this.CSS2Renderer.setSize(window.innerWidth, window.innerHeight - 100); this.CSS2Renderer.render(this.scene, this.camera); this.mountedElement.appendChild(this.CSS2Renderer.domElement); // 初始化html标签 this.tagHtml = infoDom; this.tag2Html = infoDom; this.tag3Html = editDom; this.CSS2DObject = CSS2DObject; // 创建html标签模型 this.tagCSS2DObj = new CSS2DObject(this.tagHtml); this.tag2CSS2DObj = new CSS2DObject(this.tag2Html); this.tag3CSS2DObj = new CSS2DObject(this.tag3Html); this.tagCSS2DObj.element.style.display = "none"; this.tag2CSS2DObj.element.style.display = "none"; this.tag3CSS2DObj.element.style.display = "none"; // // 设置该标签初始化透明 // this.tagCSS2DObj.element.style.opacity = "1"; // this.tag2CSS2DObj.element.style.opacity = "1"; // 下面是3D的,下面把它注释,之后方便来修正 this.tagCSS2DObj.scale.set(0.1, 0.1, 0.1); this.tag3CSS2DObj.scale.set(0.02, 0.02, 0.02); //编辑框 this.tag3CSS2DObj.position.set(10, 0, 10); // 下面是2D的显示方案 // this.tagCSS2DObj.scale.set(0.001, 0.001, 0.001); // this.tag3CSS2DObj.scale.set(0.001, 0.001, 0.001); //编辑框 // this.tag3CSS2DObj.position.set(1, 0, 1); } // 让面板消失的函数 clearTagsObj(isedit) { if (this.preDBLModel) { this.preDBLModel.remove(this.tagCSS2DObj); } // 所有标签看不见 this.scene.remove(this.tag2CSS2DObj); this.scene.remove(this.tag3CSS2DObj); this.tag3CSS2DObj.element.style.opacity = "0"; // 删除标记动画 // this.scene.remove(this.group); } // 附着点选中线宽包围 setBoxHelper(obj) { // 判断场景是否纯在该3d对象 if (this.scene.getObjectByName("boxHelper")) { const preModel = this.scene.getObjectByName("boxHelper"); this.scene.remove(preModel); } const boxHelper = new this.THREE.BoxHelper(obj, 0xffff00); boxHelper.name = "boxHelper"; this.scene.add(boxHelper); } /** *@param {Class} DRACOLoader 解压器 * @param {String} dracoPath 解压器路径 */ addDARCOLoder(DRACOLoader, dracoPath) { this.DRAC = new DRACOLoader(); this.DRAC.setDecoderPath(dracoPath); //给模型加载器添解压器 this.gltfloader.setDRACOLoader(this.DRAC); } /** * @param {Map} meshes gltf加载过后的模型Map */ initDevicesModel(equMap) { // 把风机叶片做为一个组,方便后期旋转 const group = new this.THREE.Group(); group.name = "fanLeaf"; // 初始化风机 this.equMap = equMap; // 初始化风机颜色 this.equMap.get("equ_fan").traverse((v) => { v.material = new this.THREE.MeshBasicMaterial(); // v.material.color = new this.THREE.Color(0xFF0000); v.material.color = new this.THREE.Color(0xc0c0c0); if (/^leaf/.test(v.name) || /^roller/.test(v.name)) { group.add(v.clone()); // console.log(group); v.visible = false; // console.log(v); } }); // const axesHelper = new this.THREE.AxesHelper(100); // 改变叶子旋转中心 let x = 0, y = -0.2099, z = 0; const wrapper = new this.THREE.Object3D(); wrapper.position.set(x, y, z); wrapper.add(group); group.position.set(-x, -y, -z); // wrapper.add(axesHelper); wrapper.rotation.z = Math.PI / 4; wrapper.name = "fan_leafs"; this.equMap.get("equ_fan").add(wrapper); // 初其他传感器机颜色 this.equMap.get("equ_sensors").scale.set(0.2, 0.2, 0.2); this.equMap.get("equ_sensors").traverse((v) => { v.material = new this.THREE.MeshBasicMaterial(); v.material.color = new this.THREE.Color(0xababab); }); // 初始标签面板 const tag = new this.THREE.Mesh( new this.THREE.PlaneGeometry(7, 5), new this.THREE.MeshBasicMaterial({ color: "white" }) ); this.equMap.get("equ_sensors").rotation.x += Math.PI / 2; this.equMap.get("equ_sensors").add(tag); tag.name = "tag"; tag.rotation.x = Math.PI / 2; tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序 tag.rotation.y = Math.PI; tag.rotation.z -= Math.PI / 2; tag.translateZ(2.0); } /** * @param {String} devId 删除的设备ID */ removeDevice(devId) { removeDev.bind(this, devId); } /** * * @param {Number} distance 设置锚点之间的间隔距离 */ setDistance(distance = 10) { this.distance = distance; } loadBackground(hdrLoader, backColorSet) { this.scene.background = new this.THREE.TextureLoader().load( "/images/background/background.png", function (texture) { texture.encoding = backColorSet; } ); } /** * * @param {Boolean} option 是否禁用轨道控制器 */ isControlOrbit(option = true) { this.orbitControls.enabled = option; // const mesh = this.scene.getObjectByName("chanel"); // const mesh2 = this.scene.getObjectByName("chanel_1"); // const mesh3 = this.scene.getObjectByName("chanel_2"); // const mesh4 = this.scene.getObjectByName("chanel_3"); const opacityTween = (startVal, endVal, isNeedLookAt = false) => { this.opacityTween = new this.TWEEN.Tween({ opacity: startVal, lookX: this.orbitControls.target.x, lookY: this.orbitControls.target.y, lookZ: this.orbitControls.target.z, }); this.opacityTween.to( { opacity: endVal, lookX: 0, lookY: 0, lookZ: 0, }, 500 ); this.opacityTween.start(); this.opacityTween.onUpdate((obj) => { // mesh.material.opacity = obj.opacity; // mesh2.material.opacity = obj.opacity; // mesh3.material.opacity = obj.opacity; // mesh4.material.opacity = obj.opacity; if (!isNeedLookAt) return; this.camera.lookAt(obj.lookX, obj.lookY, obj.lookZ); this.orbitControls.target.set(obj.lookX, obj.lookY, obj.lookZ); }); }; if (!option) { // 显示编辑框隧道颜色 opacityTween(0.5, 0.1); } else { // 退出编辑框什么颜色 opacityTween(0.1, 0.5, true); } } //初始将墙壁进行隐藏 WallInit() { for (let line = 1; line <= 20; line++) { if (line < 10) { let wall = "wall_" + "0" + line; this.scene.getObjectByName(wall).visible = false; } else if (line >= 10) { let wall = "wall_" + line; this.scene.getObjectByName(wall).visible = false; } } } SignsInf(tunnelName, tunnelLength) { let Signs = this.scene.getObjectByName("streetSigns"); // console.log(Signs); const tag = new this.THREE.Mesh( new this.THREE.PlaneGeometry(100, 76), new this.THREE.MeshBasicMaterial({ color: "white" }) ); Signs.add(tag); tag.name = "tag"; tag.rotation.x = Math.PI / 2; tag.rotation.z = Math.PI / 2; //旋转这里改变文字顺序 tag.rotation.y = Math.PI; tag.rotation.z -= Math.PI / 2; tag.translateZ(3); tag.translateY(40); let EquipmentTag = this._StreetSignTag(tunnelName, tunnelLength); Signs.getObjectByName("tag").material = EquipmentTag; } }