{"remainingRequest":"D:\\jenkins\\workspace\\sfz-lh-fvue\\node_modules\\vue-loader\\lib\\index.js??vue-loader-options!D:\\jenkins\\workspace\\sfz-lh-fvue\\src\\views\\BigScreen\\3DModel\\modelIndex.vue?vue&type=script&lang=js&","dependencies":[{"path":"D:\\jenkins\\workspace\\sfz-lh-fvue\\src\\views\\BigScreen\\3DModel\\modelIndex.vue","mtime":1706594417306},{"path":"D:\\jenkins\\workspace\\sfz-lh-fvue\\node_modules\\babel-loader\\lib\\index.js","mtime":315532800000},{"path":"D:\\jenkins\\workspace\\sfz-lh-fvue\\node_modules\\cache-loader\\dist\\cjs.js","mtime":499162500000},{"path":"D:\\jenkins\\workspace\\sfz-lh-fvue\\node_modules\\vue-loader\\lib\\index.js","mtime":499162500000}],"contextDependencies":[],"result":["//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\r\nimport player from '@/views/Normal/components/videoPlayerDH.vue'\r\n\r\nimport * as THREE from 'three'\r\nimport {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'\r\n// import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'\r\nimport {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'\r\nimport {\r\n CSS2DObject,\r\n CSS2DRenderer,\r\n} from 'three/examples/jsm/renderers/CSS2DRenderer.js'\r\nimport {GUI} from 'three/examples/jsm/libs/lil-gui.module.min.js'\r\n\r\n// 用于模型边缘高亮\r\nimport {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer.js'\r\nimport {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass.js'\r\nimport {OutlinePass} from 'three/examples/jsm/postprocessing/OutlinePass.js'\r\nimport {OutputPass} from 'three/examples/jsm/postprocessing/OutputPass.js'\r\nimport {ShaderPass} from 'three/examples/jsm/postprocessing/ShaderPass.js'\r\nimport {FXAAShader} from 'three/examples/jsm/shaders/FXAAShader.js'\r\nimport deviceTypeList from './deviceTypeList.js'\r\nimport laneData from './laneData.js'\r\nimport iconName from './iconName.js'\r\nimport utils from '@/utils.js'\r\nimport deviceAPI from '@/api/DeviceManagement/device.js'\r\nimport commonAPI from '@/api/bigScreen/common.js'\r\nimport DBUtils from './loader.js'\r\nexport default {\r\n name: 'ModelIndex',\r\n components: {player},\r\n data() {\r\n return {\r\n width: 0,\r\n height: 0,\r\n scene: null,\r\n camera: null,\r\n controls: null,\r\n renderer: null,\r\n css2Renderer: null,\r\n clock: null,\r\n mixer: null,\r\n stats: null,\r\n animationId: null,\r\n chooseObj: null,\r\n tag: null,\r\n percent: 0,\r\n gui: null,\r\n composer: null, //效果组合器\r\n outlinePass: null, //发光通道\r\n effectFXAA: null, //自定义的着色器通道\r\n pole1: null,\r\n deviceList: {}, // 设备对象\r\n activeList: [],\r\n tagData: null, //设备数据\r\n iconGroup: {}, // 图标对象的组合\r\n }\r\n },\r\n mounted() {\r\n this.width = window.innerWidth\r\n this.height = window.innerHeight\r\n this.init()\r\n this.animate()\r\n },\r\n methods: {\r\n getDictName(code, value) {\r\n return utils.getDictName(code, value)\r\n },\r\n changeDevice(list) {\r\n this.drop()\r\n this.activeList = []\r\n list.forEach((item) => {\r\n for (let key in this.deviceList[item]) {\r\n let icon = this.createIcon(item)\r\n this.iconGroup[this.deviceList[item][key].name] = icon\r\n this.deviceList[item][key].add(\r\n this.iconGroup[this.deviceList[item][key].name]\r\n )\r\n this.activeList.push(this.deviceList[item][key])\r\n }\r\n })\r\n this.pick(this.activeList)\r\n this.animate()\r\n },\r\n //栏杆机旋转\r\n ploeRotate() {\r\n if (this.poloState.pole1 == 1) {\r\n this.pole1.rotation.set(Math.PI / 2, 0, 0, 'XZY')\r\n this.poloState.pole1 = 0\r\n } else {\r\n this.pole1.rotation.set(0, 0, 0, 'XZY')\r\n this.poloState.pole1 = 1\r\n }\r\n },\r\n close() {\r\n if (this.chooseObj) {\r\n //把原来选中模型对应的标签和发光描边隐藏\r\n this.chooseObj.remove(this.tag) //从场景移除\r\n }\r\n },\r\n addComposer() {\r\n // 创建一个EffectComposer(效果组合器)对象,然后在该对象上添加后期处理通道。\r\n this.composer = new EffectComposer(this.renderer)\r\n // 新建一个场景通道 为了覆盖到原来的场景上\r\n let renderPass = new RenderPass(this.scene, this.camera)\r\n this.composer.addPass(renderPass)\r\n // 物体边缘发光通道\r\n this.outlinePass = new OutlinePass(\r\n new THREE.Vector2(this.width, this.height),\r\n this.scene,\r\n this.camera\r\n )\r\n this.outlinePass.visibleEdgeColor.set(parseInt(0x07e1e7)) // 呼吸显示的颜色\r\n this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0) // 呼吸消失的颜色\r\n this.composer.addPass(this.outlinePass)\r\n\r\n // 解决高亮后环境变暗的问题\r\n const outputPass = new OutputPass()\r\n this.composer.addPass(outputPass)\r\n\r\n // 自定义的着色器通道 作为参数\r\n this.effectFXAA = new ShaderPass(FXAAShader)\r\n this.effectFXAA.uniforms['resolution'].value.set(\r\n 1 / this.width,\r\n 1 / this.height\r\n )\r\n this.composer.addPass(this.effectFXAA)\r\n },\r\n createIcon(type) {\r\n let name = iconName[type]\r\n // 创建div元素(作为标签)\r\n var div = document.createElement('div')\r\n div.style.width = '30px'\r\n div.style.height = '52px'\r\n let img = require(`@/assets/img/bigScreen/${name}.png`)\r\n div.style.background = `url(${img}) no-repeat center / 100% 100%`\r\n div.classList.add('icon-box')\r\n\r\n var label = new CSS2DObject(div)\r\n // div.style.pointerEvents = 'none'\r\n return label //返回CSS2模型标签\r\n },\r\n getALLDevice(gltf) {\r\n for (let key in deviceTypeList) {\r\n this.deviceList[key] = {}\r\n deviceTypeList[key].forEach((item) => {\r\n this.deviceList[key][item] =\r\n gltf.scene.getObjectByName(item)\r\n })\r\n }\r\n },\r\n loadModel() {\r\n const indexDB = new DBUtils('indexdb001', 'sfz-lh', 1)\r\n\r\n indexDB.get('./static/3DModel/sfz-lh.gltf').then((blob) => {\r\n const url = URL.createObjectURL(new Blob([blob]))\r\n const loader = new GLTFLoader()\r\n loader.load(\r\n url,\r\n (gltf) => {\r\n const root = gltf.scene\r\n //...(下面部分就和threejs加载glb模型代码一样,按业务需求做相应的操作)\r\n this.percent = 100\r\n this.getALLDevice(gltf)\r\n this.createLane()\r\n this.scene.add(root)\r\n THREE.Cache.add('model', gltf)\r\n document.getElementById('loading').style.display =\r\n 'none'\r\n this.animate()\r\n },\r\n (xhr) => {\r\n const {loaded, total} = xhr\r\n let percent = Math.floor((loaded / total) * 100)\r\n if (percent < 96) {\r\n this.percent = percent\r\n }\r\n }\r\n )\r\n })\r\n },\r\n // loadModel() {\r\n // const loader = new GLTFLoader()\r\n // loader.load(\r\n // './static/3DModel/sfz-lh.gltf',\r\n // (gltf) => {\r\n // this.percent = 100\r\n\r\n // this.getALLDevice(gltf)\r\n // this.createLane()\r\n\r\n // // this.pole1 = gltf.scene.getObjectByName('G-__588756')\r\n\r\n // this.scene.add(gltf.scene)\r\n // document.getElementById('loading').style.display = 'none'\r\n // },\r\n // (xhr) => {\r\n // let percent = Math.floor((xhr.loaded / xhr.total) * 100)\r\n // if (percent < 96) {\r\n // this.percent = percent\r\n // }\r\n // },\r\n // (error) => {\r\n // console.log(error)\r\n // }\r\n // )\r\n // },\r\n GUIControler(mesh) {\r\n this.gui = new GUI()\r\n\r\n this.gui.domElement.style.right = '0px'\r\n this.gui.domElement.style.top = '100px'\r\n this.gui.domElement.style.width = '300px'\r\n\r\n this.gui.add(mesh.position, 'x', -100, 100).name('X坐标').step(0.1)\r\n this.gui.add(mesh.position, 'y', -100, 100).name('Y坐标').step(0.1)\r\n this.gui.add(mesh.position, 'z', -100, 100).name('Z坐标').step(0.1)\r\n },\r\n createLane() {\r\n this.deviceList['入口'] = {}\r\n this.deviceList['出口'] = {}\r\n laneData.Lanes.forEach((item) => {\r\n // PlaneGeometry\r\n const geometry = new THREE.PlaneGeometry(30, 4.5, 1, 1)\r\n geometry.rotateX(Math.PI / 2)\r\n // 材质对象Material\r\n const material = new THREE.MeshBasicMaterial({\r\n color: 0x434446, //设置材质颜色\r\n opacity: 0.01,\r\n transparent: true, //开启透明\r\n side: THREE.DoubleSide,\r\n })\r\n\r\n const mesh = new THREE.Mesh(geometry, material)\r\n mesh.name = item.name\r\n mesh.position.set(item.x, item.y, item.z)\r\n this.scene.add(mesh) //网格模型添加到场景中\r\n if (item.type == '入口') {\r\n this.deviceList['入口'][item.name] = mesh\r\n } else {\r\n this.deviceList['出口'][item.name] = mesh\r\n }\r\n })\r\n },\r\n createCss2Render() {\r\n let container = this.$refs.threeDBox\r\n const div = document.getElementById('tag')\r\n // HTML元素转化为threejs的CSS2模型对象\r\n this.tag = new CSS2DObject(div)\r\n // mesh.add(tag)\r\n document.getElementById('close').style.pointerEvents = 'auto'\r\n\r\n this.css2Renderer = new CSS2DRenderer()\r\n\r\n this.css2Renderer.setSize(window.innerWidth, window.innerHeight)\r\n\r\n container.appendChild(this.css2Renderer.domElement)\r\n\r\n this.css2Renderer.domElement.style.pointerEvents = 'none'\r\n this.css2Renderer.domElement.style.position = 'absolute'\r\n this.css2Renderer.domElement.style.top = '0px'\r\n },\r\n createAmbient() {\r\n const ambient = new THREE.AmbientLight(0xffffff, 1)\r\n this.scene.add(ambient)\r\n\r\n const directionalLight = new THREE.DirectionalLight(0xffffff, 2)\r\n // 设置光源的方向:通过光源position属性和目标指向对象的position属性计算\r\n directionalLight.position.set(80, 100, 50)\r\n // 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0\r\n // directionalLight.target = mesh\r\n this.scene.add(directionalLight)\r\n },\r\n init() {\r\n THREE.Cache.enabled = true //开启缓存\r\n let container = this.$refs.threeDBox\r\n this.scene = new THREE.Scene()\r\n // 创建纹理加载器\r\n const textureLoader = new THREE.TextureLoader()\r\n // 创建天空球\r\n const skyGeometry = new THREE.SphereGeometry(1000, 60, 60)\r\n const skyTexture = textureLoader.load(\r\n // './static/3DModel/img/sky.jpg'\r\n './static/3DModel/img/sky.png'\r\n ) // 天空纹理\r\n const skyMaterial = new THREE.MeshBasicMaterial({\r\n map: skyTexture, // 颜色贴图\r\n })\r\n // 反转球体,主要是反转z轴\r\n skyGeometry.scale(1, 1, -1)\r\n const sky = new THREE.Mesh(skyGeometry, skyMaterial)\r\n this.scene.add(sky)\r\n\r\n this.camera = new THREE.PerspectiveCamera(\r\n 30,\r\n window.innerWidth / window.innerHeight,\r\n 1,\r\n 3000\r\n )\r\n // -87.0511, 4.7929, 0.799\r\n\r\n this.camera.position.set(-82.3681, 4.6218, 0.3505)\r\n this.camera.lookAt(0, 0, 0)\r\n\r\n this.createCss2Render() // 二维弹窗\r\n this.loadModel() //加载模型\r\n\r\n // const axesHelper = new THREE.AxesHelper(100)\r\n // this.scene.add(axesHelper)\r\n\r\n this.createAmbient()\r\n\r\n this.renderer = new THREE.WebGLRenderer({antialias: true})\r\n this.renderer.setPixelRatio(window.devicePixelRatio)\r\n this.renderer.setSize(window.innerWidth, window.innerHeight)\r\n\r\n this.renderer.outputColorSpace = THREE.SRGBColorSpace //设置为SRGB颜色空间\r\n container.appendChild(this.renderer.domElement)\r\n\r\n this.controls = new OrbitControls(\r\n this.camera,\r\n this.renderer.domElement\r\n )\r\n //-65.1083, 4.2508, 0.851\r\n this.controls.target = new THREE.Vector3()\r\n this.controls.minDistance = 0\r\n this.controls.maxDistance = 200\r\n this.controls.minPolarAngle = 0\r\n this.controls.maxPolarAngle = 1.55 //Math.PI\r\n // this.controls.enablePan = false // 移动\r\n this.controls.update()\r\n\r\n this.controls.addEventListener('change', this.animate)\r\n\r\n window.addEventListener('resize', this.onWindowResize)\r\n\r\n this.renderer.domElement.addEventListener('click', (event) => {\r\n // .offsetY、.offsetX以canvas画布左上角为坐标原点,单位px\r\n const px = event.offsetX\r\n const py = event.offsetY\r\n // 屏幕坐标px、py转WebGL标准设备坐标x、y\r\n // width、height表示canvas画布宽高度\r\n const x = (px / window.innerWidth) * 2 - 1\r\n const y = -(py / window.innerHeight) * 2 + 1\r\n\r\n //创建一个射线投射器`Raycaster`\r\n const raycaster = new THREE.Raycaster()\r\n\r\n //.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray\r\n // 形象点说就是在点击位置创建一条射线,射线穿过的模型代表选中\r\n raycaster.setFromCamera(new THREE.Vector2(x, y), this.camera)\r\n //.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算\r\n // 未选中对象返回空数组[],选中一个对象,数组1个元素,选中两个对象,数组两个元素\r\n // const intersects = raycaster.intersectObjects([mesh])\r\n // const intersects = raycaster.intersectObject(this.scene, true)\r\n\r\n const intersects = raycaster.intersectObjects(this.activeList)\r\n // console.log('射线器返回的对象', intersects)\r\n // intersects.length大于0说明,说明选中了模型\r\n if (intersects.length > 0) {\r\n // 选中模型的第一个模型,设置为红色\r\n // this.drop()\r\n // intersects[0].object.material.color.set(0xff0000)\r\n\r\n this.chooseObj = intersects[0].object\r\n intersects[0].object.add(this.tag)\r\n this.getData()\r\n\r\n // this.pick([this.chooseObj])\r\n } else {\r\n // mesh.material.color.set(0xfff000)\r\n if (this.chooseObj) {\r\n //把原来选中模型对应的标签和发光描边隐藏\r\n this.chooseObj.remove(this.tag) //从场景移除\r\n // this.drop()\r\n this.chooseObj = null\r\n this.tagData = null\r\n }\r\n }\r\n this.animate()\r\n })\r\n this.addComposer()\r\n setTimeout(() => {\r\n this.state = '异常'\r\n }, 10000)\r\n },\r\n getData() {\r\n let play = document.getElementById('play')\r\n // console.log(this.chooseObj, play)\r\n if (this.chooseObj.name.indexOf('lane') == -1) {\r\n let sysCode =\r\n this.chooseObj.name.length > 10\r\n ? this.chooseObj.name.slice(0, 10)\r\n : this.chooseObj.name\r\n let queryData = {\r\n pageBean: {\r\n page: 1,\r\n pageSize: -1,\r\n showTotal: true,\r\n },\r\n querys: [\r\n {\r\n group: 'main',\r\n operation: 'EQUAL',\r\n parentGroup: '',\r\n property: 'sysCode',\r\n relation: 'AND',\r\n value: sysCode,\r\n },\r\n ],\r\n }\r\n deviceAPI.queryDeviceList(queryData).then((res) => {\r\n if (res.value && res.value.rows) {\r\n this.tagData = res.value.rows[0]\r\n if (this.tagData && this.tagData.type == 1) {\r\n this.$nextTick(() => {\r\n document.getElementById(\r\n 'tag'\r\n ).style.zIndex = 999\r\n this.$refs.player.play(this.tagData)\r\n })\r\n }\r\n }\r\n })\r\n } else {\r\n let id = this.chooseObj.name.replace('lane', '')\r\n let queryData = this.qs.stringify({\r\n laneId: id,\r\n })\r\n commonAPI.queryLaneData(queryData).then((res) => {\r\n if (res.value && res.value.length) {\r\n this.tagData = res.value[0]\r\n }\r\n })\r\n }\r\n },\r\n drop() {\r\n this.activeList.forEach((item) => {\r\n item.remove(this.iconGroup[item.name])\r\n })\r\n this.outlinePass.selectedObjects = []\r\n },\r\n pick(selectedObjects) {\r\n // 筛选出车道mesh\r\n let outlineObj = selectedObjects.filter(\r\n (item) => item.name.indexOf('lane') == -1\r\n )\r\n this.outlinePass.selectedObjects = [...outlineObj]\r\n },\r\n onWindowResize() {\r\n const width = window.innerWidth\r\n const height = window.innerHeight\r\n this.camera.aspect = width / height\r\n this.renderer.setSize(width, height)\r\n this.css2Renderer.setSize(width, height)\r\n this.composer.setSize(width, height)\r\n this.camera.updateProjectionMatrix()\r\n this.effectFXAA.uniforms['resolution'].value.set(\r\n 1 / this.width,\r\n 1 / this.height\r\n )\r\n },\r\n animate() {\r\n // this.animationId = requestAnimationFrame(this.animate)\r\n this.css2Renderer.render(this.scene, this.camera)\r\n this.composer.render(this.scene, this.camera)\r\n // this.renderer.render(this.scene, this.camera)\r\n\r\n // 根据相机控件可视化调试相机位置\r\n // console.log('controls.target', this.controls.target)\r\n // console.log('camera.position', this.camera.position)\r\n },\r\n\r\n showData() {\r\n console.log('controls.target', this.controls.target)\r\n console.log('camera.position', this.camera.position)\r\n },\r\n removeEventListeners() {},\r\n\r\n destroyThreejs() {\r\n try {\r\n this.renderer.dispose()\r\n this.renderer.forceContextLoss()\r\n this.renderer.content = null\r\n let gl = this.renderer.domElement.getContext('webgl')\r\n if (gl && gl.getExtension('WEBGL_lose_context')) {\r\n gl.getExtension('WEBGL_lose_context').loseContext()\r\n }\r\n this.renderer = null\r\n this.camera = null\r\n this.scene.traverse((child) => {\r\n if (child.material) {\r\n child.material.dispose()\r\n }\r\n if (child.geometry) {\r\n child.geometry.dispose()\r\n }\r\n child = null\r\n })\r\n this.scene = null\r\n } catch (e) {\r\n console.error('Failed to destroy threejs', e)\r\n }\r\n },\r\n },\r\n beforeDestroy() {\r\n cancelAnimationFrame(this.animationId)\r\n this.removeEventListeners()\r\n this.destroyThreejs()\r\n // this.gui.domElement.style.display = 'none'\r\n },\r\n}\r\n",null]}