import {ViewerBase} from "../custom/viewer/viewerBase.js" import * as THREE from "../../libs/three.js/build/three.module.js"; import Viewport from "../custom/viewer/Viewport.js" import {ExtendView} from "./ExtendView.js" import {LineDraw/* , MeshDraw */} from "../custom/utils/DrawUtil.js"; import {InputHandler} from "../navigation/InputHandlerNew.js"; import {CameraMode } from "../defines.js" //import {FirstPersonControls} from '../navigation/FirstPersonControlsNew.js' import {OrbitControls} from "../navigation/OrbitControlsNew.js"; const Colors = { black : '#161A1A', blue: '#3290ff', gray: '#f6f6f6' , grayDeep:'#f3f3f3', white:'#ffffff' } let navCubeViewer class base{ constructor(){ this.faceDefaultColor = Colors.gray//14936556, /* this.wireframeDefaultColor = 13421772, this.faceHighlightColor = 12255212, this.wireframeHighlightColor = 3330982 */ } createMesh(e) { for (var t = new THREE.BufferGeometry, i = e.length - 2, n = new Uint32Array(3 * i), o = 0, s = 1; s <= i; s++) n[o++] = 0, n[o++] = s, n[o++] = s + 1; var r = new THREE.MeshBasicMaterial({ color: this.faceDefaultColor, side: THREE.DoubleSide, }) , a = new Float32Array(3 * e.length); t.setAttribute("position", new THREE.BufferAttribute(a,3).copyVector3sArray(e)), t.setIndex(new THREE.BufferAttribute(n,1)); var l = new THREE.Mesh(t,r); return l.componentId = this.componentId, l } createWireframe(e ) { let line = LineDraw.createFatLine(e,{ color: Colors.grayDeep , lineWidth : 2, viewer: navCubeViewer, depthTest:true, depthWrite:true, transparent:true }); line.renderOrder = 3; return line } getMesh() { return this.mesh } getWireframe() { return this.wireframeMesh } transparent(e) { e.material && (e.material.transparent = !0, e.material.opacity = 0) } opaque(e) { e.material && (e.material.transparent = !1, e.material.opacity = 1) } getId() { return this.componentId } } /* class Edge extends base{ constructor(t, i, n) { super() this.highlightWidth = 3, this.width = 15, this.vertices = t, this.indices = i, this.componentId = n, this.highlightWireframeMesh = null, this.testWireframe = null, this.build() } build() { var e = this.indices[0] , t = this.indices[1] , i = this.vertices[e] , n = this.vertices[t] , o = i.clone().add(n).multiplyScalar(.5).clone().multiplyScalar(-1) , s = o.clone().normalize() , r = [] , a = n.clone().sub(i).normalize() , l = i.clone().add(a.clone().multiplyScalar(20)) , h = i.clone().add(a.clone().multiplyScalar(80)) , c = []; if (0 !== o.x) { var d = o.x > 0 ? this.width : -this.width; c.push((new THREE.Vector3).setX(d).add(s)) } if (0 !== o.y) { var u = o.y > 0 ? this.width : -this.width; c.push((new THREE.Vector3).setY(u).add(s)) } if (0 !== o.z) { var g = o.z > 0 ? this.width : -this.width; c.push((new THREE.Vector3).setZ(g).add(s)) } 2 === c.length && (r.push(l.clone().add(s)), r.push(l.clone().add(c[0])), r.push(h.clone().add(c[0])), r.push(h.clone().add(s)), r.push(h.clone().add(c[1])), r.push(l.clone().add(c[1]))), this.mesh = this.createMesh(r) this.transparent(this.mesh) //this.wireframeMesh = this.createWireframe([l, h]), //this.highlightWireframeMesh = this.createHighlightWireframe([l.sub(s), h.sub(s)]) } createHighlightWireframe(e) { for (var t = [], i = 0; i < e.length; i++) { var n = e[i]; t.push(n.x, n.y, n.z) } var o = new THREE.LineGeometry; o.setPositions(t); var s = new THREE.LineMaterial({ color: this.wireframeHighlightColor, linewidth: this.highlightWidth, dashed: !1 }); s.resolution.set(160, 160); var r = new THREE.Line2(o,s); return r.computeLineDistances(), r.scale.set(1, 1, 1), r.visible = !1, r.renderOrder = 100, r } getTestWireframe() { return this.testWireframe } getHighlightWireframeMesh() { return this.highlightWireframeMesh } highlight() { this.highlightWireframeMesh.visible = !0, this.highlightWireframeMesh.renderOrder = 100 } cancelHighlight() { this.highlightWireframeMesh.visible = !1 } } */ class Corner extends base{ constructor(t, i) { super() this.length = 20, this.vertex = t, this.cornerFace = null, this.cornerWireframe = null, this.componentId = i, this.cornerVertices = null, this.build() } build() { var e = [] , t = this.vertex.clone() , i = t.clone().multiplyScalar(-1); e.push(t); var n = this.vertex.clone() , o = i.x > 0 ? this.length : -this.length; n.x += o, e.push(n); var s = this.vertex.clone() , r = i.y > 0 ? this.length : -this.length; s.y += r, e.push(s); var a = this.vertex.clone() , l = i.z > 0 ? this.length : -this.length; a.z += l, e.push(a), this.cornerVertices = e this.mesh = this.createMesh([n, s, a]) /* this.wireframeMesh = this.createWireframe([n, s, a, n], 2), this.buildCornerFace() this.buildCornerWireframe() */ } /* highlight() { this.wireframeMesh.material.color.setHex(this.wireframeHighlightColor), this.wireframeMesh.renderOrder = 100, this.mesh.material.color.setHex(this.faceHighlightColor), this.cornerFace.material.color.setHex(this.faceHighlightColor), this.cornerFace.material.transparent = !0, this.cornerFace.material.opacity = .5, this.cornerWireframe.material.color.setHex(this.wireframeHighlightColor), this.cornerWireframe.visible = !0 } cancelHighlight() { this.wireframeMesh.material.color.setHex(this.wireframeDefaultColor), this.wireframeMesh.renderOrder = 0, this.mesh.material.color.setHex(this.faceDefaultColor), this.transparent(this.cornerFace), this.cornerWireframe.visible = !1 } buildCornerFace() { if (!this.cornerFace) { var e = this.cornerVertices; e.push(e[1]) this.cornerFace = this.createMesh(e) this.transparent(this.cornerFace) } } getCornerFace() { return this.cornerFace } getCornerWireframe() { return this.cornerWireframe } buildCornerWireframe() { if (!this.cornerWireframe) { for (var e = [], t = 1; t < this.cornerVertices.length; t++) { var i = this.cornerVertices[0] , n = this.cornerVertices[t]; e.push(i, n) } this.cornerWireframe = this.createWireframe(e), this.cornerWireframe.visible = !1 } } */ } class Face extends base{ constructor(t, i, n, o){ super() this.length = 60 this.vertices = t, this.indices = i, this.componentId = n, this.vertexUvs = null, this.texture = o, this.highlightMesh = null, this.wireframeMesh = null, this.vertexUvs = [], this.vertexUvs.push(new THREE.Vector2(0,.2)), this.vertexUvs.push(new THREE.Vector2(0,.8)), this.vertexUvs.push(new THREE.Vector2(.2,1)), this.vertexUvs.push(new THREE.Vector2(.8,1)), this.vertexUvs.push(new THREE.Vector2(1,.8)), this.vertexUvs.push(new THREE.Vector2(1,.2)), this.vertexUvs.push(new THREE.Vector2(.8,0)), this.vertexUvs.push(new THREE.Vector2(.2,0)), this.build() } build() { for (var e = [], t = null, i = null, n = 0, o = this.indices.length; n < o; n++) { var s = this.indices[n] , r = this.indices[n + 1]; t = this.vertices[s], i = this.vertices[r], n === o - 1 && (i = this.vertices[this.indices[0]]); var a = i.clone().sub(t).normalize() , l = t.clone().add(i).multiplyScalar(.5); e.push(l.clone().sub(a.clone().multiplyScalar(this.length / 2))), e.push(l.clone().add(a.clone().multiplyScalar(this.length / 2))) } this.createTexturedMesh(e); for (var h = new THREE.Box3, c = 0; c < this.indices.length; c++) { var d = this.indices[c]; h.expandByPoint(this.vertices[d]) } for (var u = h.getCenter(new THREE.Vector3).normalize(), g = [], p = 0; p < e.length; p++) { var m = e[p]; g.push(m.clone().add(u)) } /* this.highlightMesh = this.createMesh(g) this.highlightMesh.visible = !1 this.highlightMesh.isHighlightMesh = !0 */ g.push(g[0]) this.wireframeMesh = this.createWireframe(g, 1) } /* highlight() { this.highlightMesh.visible = !0 this.highlightMesh.material.color.setHex(this.faceHighlightColor) this.highlightMesh.material.transparent = !0 this.highlightMesh.material.opacity = .5 //this.wireframeMesh.material.color.setHex(this.wireframeHighlightColor) } cancelHighlight() { this.highlightMesh.visible = !1 //this.wireframeMesh.material.color.setHex(this.wireframeDefaultColor) } */ createTexturedMesh(e) { for (var t = e.length - 2, i = new Uint32Array(3 * t), n = 0, o = 1; o <= t; o++) i[n++] = 0, i[n++] = o, i[n++] = o + 1; var s = new THREE.BufferGeometry , r = new Float32Array(3 * e.length) , a = new Float32Array(2 * this.vertexUvs.length); s.setAttribute("position", new THREE.BufferAttribute(r,3).copyVector3sArray(e)), s.setAttribute("uv", new THREE.BufferAttribute(a,2).copyVector2sArray(this.vertexUvs)), s.setIndex(new THREE.BufferAttribute(i,1)); var l = new THREE.ShaderMaterial({ side: THREE.DoubleSide, transparent: !1, uniforms:{ faceColor: {type:'v3', value: new THREE.Color(Colors.white) } , textColor: {type:'v3', value: new THREE.Color(Colors.black) } , map: {type: 't', value: this.texture }, }, vertexShader:` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, fragmentShader:` varying vec2 vUv; uniform vec3 faceColor; uniform vec3 textColor; uniform sampler2D map; void main() { //从图上实测 const float mapFaceColorR = 0.85; //0.8862745098039215 (取色) const float mapTextColotR = 0.66; //0.6431372549019608 (取色)//越高锯齿越强,越低越模糊颜色越灰 vec4 texColor = texture2D(map, vUv); if(texColor.r > mapFaceColorR){ gl_FragColor = vec4(faceColor, texColor.a); }else if(texColor.r{ let name = this.enumViewMode[this.faceIds[n]] var url = Potree.resourcePath+'/textures/navigation/' + name + ".png" , r = new THREE.TextureLoader; r.setCrossOrigin("anonymous"); r.load(url, (tex)=> { var face = new Face(e,t[n], this.faceIds[n], tex); this.componentList.push(face) let faceMesh = face.getMesh() this.scene.add(faceMesh) this.scene.add(face.getWireframe()) //每个面是八边形 /* this.scene.add(face.getHighlightMesh()) */ if(6 == ++texturesLoaded){ navCubeViewer.needRender = true } //不过平板无hover事件 faceMesh.addEventListener('mouseover', (e)=>{ if(viewer.mainViewport.view.isFlying())return faceMesh.material.uniforms.faceColor.value.set(Colors.blue) //console.log('变', name) navCubeViewer.dispatchEvent('content_changed') }); faceMesh.addEventListener('mouseleave', (e)=>{ if(viewer.mainViewport.view.isFlying())return faceMesh.material.uniforms.faceColor.value.set(Colors.white) //console.log('回', name) navCubeViewer.dispatchEvent('content_changed') }); faceMesh.addEventListener('click', (e)=>{ if(viewer.mainViewport.view.isFlying())return let dir = directions[name] if(name == 'Top'){ let baseLine = viewer.scene.measurements.find(e=>e.isBaseLine && e.points.length == 2) //使基准线在俯视图中水平 let yaw = baseLine ? new THREE.Vector2().subVectors(baseLine.points[0], baseLine.points[1]).angle() : 0 dir = baseLine ? {dir: new THREE.Vector3(0,0,-1),yaw,pitch: -1.5707963267948966} : directions[name] } navCubeViewer.switchView('ortho', dir, ()=>{ faceMesh.material.uniforms.faceColor.value.set(Colors.white) viewer.dispatchEvent({type:'viewChanged', name }) }) faceMesh.material.uniforms.faceColor.value.set(Colors.blue) }); }) } for (var e = this.vertices, t = this.faceIndices, s = 0; s < 6; s++){ create(s) } } buildCorners() { for (var e = this.vertices, t = this.vertexIds, i = 0; i < 8; i++) { var n = new Corner(e[i],t[i]); this.componentList.push(n) this.scene.add(n.getMesh()) //8个三角形 //this.scene.add(n.getWireframe()) //this.scene.add(n.getCornerFace()) //this.scene.add(n.getCornerWireframe()) } } /* buildEdges() { for (var e = this.vertices, t = this.edgeIndices, i = this.edgeIds, n = 0; n < 12; n++) { var o = new Edge(e,t[n],i[n]); this.componentList.push(o) //this.scene.add(o.getMesh()) //this.scene.add(o.getWireframe()) //this.scene.add(o.getHighlightWireframeMesh()) } } */ getComponent(e) { for (var t = 0; t < this.componentList.length; t++) { var i = this.componentList[t]; if (i.getId() == e) return i } return null } } class NavCubeViewer extends ViewerBase{ constructor(domElement, listenViewport){ super(domElement, {name:'navCube', antialias:true/* , preserveDrawingBuffer:true */} ) domElement.style.opacity = 0.9 navCubeViewer = this this.scene = new THREE.Scene this.scene.rotation.x = Math.PI/2 //Yup->Zup this.listenViewport = listenViewport let w = 200 this.camera = new THREE.OrthographicCamera(-w/2,w/2,w/2,-w/2 , 1, w*4); this.camera.zoom = (domElement.clientWidth || 300) / w * 1.267//zoom越大视野越小 this.camera.updateProjectionMatrix() this.view = new ExtendView(); /* this.view.position.set(0,0,0); this.view.lookAt(0,0,0) */ this.viewports = [new Viewport( this.view, this.camera, { left:0, bottom:0, width:1, height: 1, name:'navCube' }) ] this.cube = new NavigationCube(this.scene, w/2 ) viewer.addEventListener('camera_changed', e => { if (e.viewport == listenViewport && (/* e.changeInfo.positionChanged || */e.changeInfo.quaternionChanged)) { this.updateCamera() } }) this.controls = new OrbitControls(this, this.viewports[0]); this.controls.setEnable(true) this.controls.setScene(true) this.view.radius = this.cube.length * 3//相机距离 //事件 this.inputHandler = new InputHandler(this, this.scene); this.inputHandler.name = 'navCube' //this.inputHandler.addInputListener(this.controls); this.inputHandler.registerInteractiveScene(this.scene);//interactiveScenes this.viewports[0].interactiveScenes = this.inputHandler.interactiveScenes;//供viewer的inputHandler使用 viewer.addEventListener('allLoaded',()=>{ let viewportProps = [{ left:0, bottom:0, width:1, height:1, name : "MainView", //axis:["y","z"], //direction : new THREE.Vector3(1,0,0), active: true, //相机位置在x轴负向 右下角屏 viewContainsPoints:[new THREE.Vector3(0,0,0)], margin:{x:300, y:250} , } ] viewer.splitScreen.splitStart(viewportProps) viewer.images360.addEventListener('beginChangeMode',(e)=>{ if(e.mode == 'showPanos'){ this.switchView('perspective',{}, ()=>{ Potree.settings.displayMode = viewer.images360.latestRequestMode }) } }) //外部传消息,使进入俯视 this.addEventListener('enterTopView',()=>{ let baseLine = viewer.scene.measurements.find(e=>e.isBaseLine && e.points.length == 2) //使基准线在俯视图中水平 let yaw = baseLine ? new THREE.Vector2().subVectors(baseLine.points[0], baseLine.points[1]).angle() : 0 this.switchView('ortho', {dir: new THREE.Vector3(0,0,-1),yaw,pitch: -1.5707963267948966}, ()=>{ viewer.dispatchEvent({type:'viewChanged', name:'Top' }) }) }) this.addEventListener('leaveTopView',()=>{ if(this.lastView){ this.switchView2(this.lastView) } }) }) } render(){ this.renderer.render(this.scene, this.camera) } update(delta){ if(this.renderArea.clientWidth <= 0) return //unvisible this.updateScreenSize() this.controls.update(delta) this.view.applyToCamera(this.camera) let changed = this.cameraChanged() if(changed || this.needRender){ this.needRender = false this.render() this.applyToMainView() } } updateCamera(){ let view = this.listenViewport.view this.view.yaw = view.yaw this.view.pitch = view.pitch var dir = view.direction; //相机朝向 this.view.position.copy(dir.multiplyScalar(this.view.radius).negate()) //相机绕着指南针中心(000)转动 } applyToMainView(){ let view = this.listenViewport.view view.rotation = this.view.rotation } pushHomeBtn(){ this.switchView('perspective') } switchView(type, {yaw, pitch, dir}={}, done){ if(viewer.mainViewport.view.isFlying())return let view = viewer.mainViewport.view let oldlastPerspect = this.lastPerspectView this.lastView = view.clone() if(viewer.mainViewport.camera.type == 'OrthographicCamera'){ this.lastView.isOrtho = true; this.lastView.zoom = viewer.mainViewport.camera.zoom }else{ this.lastPerspectView = this.lastView } if(type == 'ortho'){ let startCamera, endCamera if(viewer.mainViewport.camera != viewer.scene.cameraO){ startCamera = viewer.scene.cameraP endCamera = viewer.scene.cameraO viewer.scene.cameraO.position.copy(viewer.mainViewport.camera.position) viewer.scene.cameraO.quaternion.copy(viewer.mainViewport.camera.quaternion) viewer.mainViewport.camera = viewer.scene.cameraO //先设置为cameraO, 才能计算top viewer.setCameraMode(CameraMode.ORTHOGRAPHIC) //updateScreenSize set cameraO.top this.controls.setEnable(false) //假设保持到目前中心的视角范围不变 viewer.splitScreen.setShiftTarget(viewer.mainViewport, viewer.bound.center) let dis = new THREE.Vector3().subVectors(viewer.mainViewport.shiftTarget, viewer.scene.cameraO.position).length() ; //-nearestPano[0].score //根据2d->3d的式子逆求zoom let halfHeight = Math.abs(dis) * Math.tan( THREE.Math.degToRad(viewer.scene.cameraP.fov/2)); viewer.scene.cameraO.zoom = viewer.scene.cameraO.top / halfHeight; viewer.scene.cameraO.updateProjectionMatrix(); } //console.log('变成正交') viewer.focusOnObject(viewer.bound, 'boundingBox', 1000, { endPitch: pitch, endYaw: yaw , dir, startCamera, endCamera }).promise.done(()=>{ /* let baseLine = viewer.scene.measurements.find(e=>e.isBaseLine && e.points.length == 2) baseLine && Potree.Utils.updateVisible(baseLine,'enterOrthoView',true)//基准线仅在正交视图可见 */ done && done() navCubeViewer.dispatchEvent('content_changed') }) }else{ if(viewer.mainViewport.camera == viewer.scene.cameraO){ return this.switchView2(oldlastPerspect, done) //viewer.scene.cameraP.position.copy(viewer.mainViewport.camera.position) //viewer.scene.cameraP.quaternion.copy(viewer.mainViewport.camera.quaternion) viewer.mainViewport.camera = viewer.scene.cameraP viewer.setCameraMode(CameraMode.PERSPECTIVE) //假设保持到目前中心的视角范围不变 viewer.splitScreen.setShiftTarget(viewer.mainViewport, viewer.bound.center) let halfHeight = viewer.scene.cameraO.top/viewer.scene.cameraO.zoom let dis = halfHeight / Math.tan( THREE.Math.degToRad(viewer.scene.cameraP.fov/2)) let position = new THREE.Vector3().copy(viewer.mainViewport.shiftTarget).sub(view.direction.clone().multiplyScalar(dis)); //view.position.copy(viewer.mainViewport.shiftTarget).sub(view.direction.clone().multiplyScalar(dis)); this.controls.setEnable(true) //viewer.dispatchEvent('leaveTopView') console.log('变回透视') view.tranCamera(viewer.mainViewport, { position , callback:()=>{ done && done() viewer.dispatchEvent({type:'viewChanged', name:'perspective' }) }, startCamera:viewer.scene.cameraO, endCamera:viewer.scene.cameraP, midCamera:viewer.scene.cameraBasic }, 500) } } } switchView2(viewInfo, done){ //直接输入view改变 let view = viewer.mainViewport.view let startCamera, endCamera if(viewInfo.isOrtho){ if(viewer.mainViewport.camera != viewer.scene.cameraO){ startCamera = viewer.scene.cameraP endCamera = viewer.scene.cameraO }else{ view.moveOrthoCamera(viewer.mainViewport, {endPosition:viewInfo.position, endPitch: viewInfo.pitch, endYaw: viewInfo.yaw , zoom: viewInfo.zoom, callback:()=>{ done && done() }, }, 800) } }else{ if(viewer.mainViewport.camera == viewer.scene.cameraO){ startCamera = viewer.scene.cameraO endCamera = viewer.scene.cameraP /* let baseLine = viewer.scene.measurements.find(e=>e.isBaseLine && e.points.length == 2) baseLine && Potree.Utils.updateVisible(baseLine,'enterOrthoView',false) //基准线仅在正交视图可见 */ }else{ console.log('switchView2', 'not ortho') view.setView({ position:viewInfo.position, endPitch: viewInfo.pitch, endYaw: viewInfo.yaw , startCamera, endCamera, callback:()=>{ }, }, 800) } } if(startCamera){ if(endCamera == viewer.scene.cameraO){ this.controls.setEnable(false) } this.controls.setEnable(false) view.tranCamera(viewer.mainViewport, { position:viewInfo.position, endPitch: viewInfo.pitch, endYaw: viewInfo.yaw , startCamera, endCamera, midCamera:viewer.scene.cameraBasic , callback:()=>{ if(endCamera != viewer.scene.cameraO){ this.controls.setEnable(true) viewer.dispatchEvent({type:'viewChanged', name:'perspective' }) }else{ viewer.dispatchEvent({type:'viewChanged', name:'perspective' }) } done && done() }, }, 800) } } rotateSideCamera(angle){ viewer.splitScreen.rotateSideCamera(viewer.mainViewport,angle) } } export {NavCubeViewer}