import * as THREE from "../../../../libs/three.js/build/three.module.js"; const initDir = new THREE.Vector3(0,1,0)//指南针模型的北方向 向屏幕里 class Compass extends THREE.EventDispatcher{ constructor(dom, viewport){ super() this.angle = 0; this.show = false; if(dom){ this.dom = $(dom); } this.viewport = viewport this.init() } init(){ var width = 100, height = 100 if(!this.dom){ this.dom = $('
') $(viewer.renderArea).append(this.dom) } this.dom.css({ display:"none", position:"absolute",right:"1%",top: "60px",width:width+"px",height:height+"px", "z-index":100,"pointer-events":"none" }) let child = $("
"+/* (config.lang=='zh'? */'北'/* :'N') */+"
") this.dom.append(child) this.dom.find(".dirText").css({textAlign:"center","font-size":"10px","position":"absolute", width: "100%", height: "25px", "line-height": "25px"}) this.dom.find(".north").css({"color":"#02a0e9","top":"0"}) this.dom.find(".south").css({"color":"#ff1414","bottom":"0"}) this.dom.find(".center").css({ //"background":`url(${config.getStaticResource('img')}/dire.png)`, width: width/2+"px", height: height/2+"px", "background-size": "contain", "background-position": "center", left: "50%", top: "50%", transform: "translate(-50%,-50%)", position: "absolute" }) this.dom.find(".dirText").css({ "text-align": "center", "font-size": "10px", "color": "rgb(255, 255, 255)", "position": "absolute", "top": "50%", "left": "50%", "width": "45%", "height": "0px", "transform-origin": "left center", }) this.dom.find(".dirText span").css({ display: "block", position: "absolute", right: "5px", top: "0", width: "20px", height: "20px", "line-height": "20px", // "font-size": ".75rem ", "margin-top": "-10px", }) try { this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha:true })//许钟文 添加个抗锯齿,否则添加的线条锯齿严重, this.renderer.autoClear = !0 this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1) this.renderer.domElement.setAttribute('name','compass') this.renderer.setClearAlpha(0.0) //xst修改 //this.renderer.setSize(width/2, height/2, false, window.devicePixelRatio ? window.devicePixelRatio : 1); //xst修改 //this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1); //this.renderer.setSize(width/2, height/2); //xst修改 this.renderer.setDrawingBufferSize( width/2, height/2, window.devicePixelRatio ? window.devicePixelRatio : 1 ) //this.emit(SceneRendererEvents.ContextCreated) } catch (e) { viewer.dispatchEvent('webglError', {msg:e}) } this.dom.find(".center")[0].appendChild(this.renderer.domElement); this.renderer.domElement.style.width = this.renderer.domElement.style.height = '100%' this.camera = new THREE.PerspectiveCamera; this.camera.fov = 50; this.camera.updateProjectionMatrix() this.scene = new THREE.Scene, this.scene.add(this.camera) this.createCompass() viewer.addEventListener('camera_changed', e => { if (e.viewport == this.viewport && (e.changeInfo.quaternionChanged /* || e.changeInfo.quaternionChanged */)) { this.update() } }) this.setDomPos() if(this.viewport)this.setDisplay(true) } createCompass(){ //ConeBufferGeometry(radius : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float) const height = 2; const geometry1 = new THREE.ConeBufferGeometry( 0.7, height, 4, true ); const geometry2 = new THREE.ConeBufferGeometry( 0.7, height, 4, true ); const material = new THREE.MeshBasicMaterial({ vertexColors :true }) //指南针由两个四棱锥拼成,为了渐变颜色,采用指定vertexColor的方式。 var setColor = function(geometry, color1,color2){ const colors = []; for ( let i = 0, n = geometry.attributes.position.count; i < n; ++ i ) { colors.push( 1, 1, 1 ); } var set = function(index, color){//设置第index个点的颜色 colors[index*3+0] = color[0] colors[index*3+1] = color[1] colors[index*3+2] = color[2] } var mid = [(color1[0]+color2[0])/2, (color1[1]+color2[1])/2, (color1[2]+color2[2])/2 ] set(1,color1); set(5,color1);set(6,color1); set(2,mid); set(3,mid);set(7,mid); set(4,color2); set(8,color2);set(9,color2); geometry.setAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3)) } var blue1 = [1/255,238/255,245/255] //逐渐变深 var blue2 = [20/255,146/255,170/255] var blue3 = [40/255,60/255,103/255] setColor(geometry1, blue1,blue2) setColor(geometry2, blue2,blue3) /* 朝箭头方向看点构成如下 虽然geometry.attributes.position.count = 19 只有1-9设置的颜色是有效的 另外为什么7决定了上下两边的颜色呢…… 5、9可将其分成上下两个颜色 6 /|\ / | \ 7 /_2|1_\ 5 \ 3|4 / 9 \ | / \|/ 8 */ const cone = new THREE.Mesh( geometry1, material ); cone.position.setY(height/2) geometry1.computeVertexNormals()//computeFaceNormals geometry2.computeVertexNormals() const cones = new THREE.Object3D(); cones.add(cone) let cone2 = new THREE.Mesh( geometry2, material ); cone2.rotation.x = Math.PI; cone2.position.setY(-height/2) cones.add(cone2) //cones.rotation.x = Math.PI / 2;//转向initDir的方向 //cones.rotation.z = Math.PI / 2; cones.rotation.z = Math.PI ;//转向initDir的方向 cones.scale.set(0.7,0.7,0.7) this.scene.add(cones) this.cones = cones } setNorth(){ //设置北方向,这决定了指南针自身的朝向。 const floors = store.getters['scene/houstFloor'].floors if(!floors || !floors.length){ return } const floor = floors[0] const metadata = app.store.getters['scene/metadata'] || {} this.angle = (floor && floor.dire || 0) + THREE.Math.radToDeg(parseFloat(metadata.floorPlanAngle || 0)) //基础朝向 this.cones.rotation.y = Math.PI / 2 - THREE.Math.degToRad(this.angle) //console.log("dir:"+floor.dire+", floorPlanAngle:"+metadata.floorPlanAngle) this.update() } update(quaternion){ if(!this.show)return; if(!quaternion) quaternion = this.viewport.camera.quaternion.clone(); this.updateCamera(quaternion) this.updateLabel(quaternion) this.render() } /*updateLabel(quaternion){//更新北标签 var dir = viewer.mainViewport.view.direction; var oriDir = initDir.clone() //指南针最初始时的北方向 var extraQua if(objects.player.mode == "transitioning"){//当transitioning时,相机的quaternion不是用control的lookAt算出来,而是直接由一个quaternion过渡到另一个,这样相机将会是歪的,投影面也就不会是原先的水平面。 var tempCamera = new THREE.Camera(); //借用camera的lookAt算出如果正视同样的target, quaternion会是什么值。 将它乘以当前相机quaternion,得到的就是相机歪的旋转值。 tempCamera.position.copy(this.camera.position); tempCamera.lookAt(tempCamera.position.clone().add(dir)) var q = tempCamera.quaternion.inverse() extraQua = q.premultiply(quaternion) //歪掉的额外旋转值 } //北标签的方向为指南针轮盘方向,也就是要将camera的方向投影到水平面上。 但是如果相机歪了,看到的世界都会歪一定角度,投影面也要歪一定角度。 var up = new THREE.Vector3(0,0,1) //投影水平面的法线,也是相机的摆正的up方向 extraQua && up.applyQuaternion(extraQua) dir.projectOnPlane(up) //将方向投影到水平面上; 如果相机不是正视(extraQua不为0001),就要将水平面也转动 oriDir.projectOnPlane(up)//为什么initDir投影了和没有投影angle结果一样 var angle = dir.angleTo(oriDir) if(dir.cross(oriDir).y > 0)angle = -angle var deg = this.angle - 90 + THREE.Math.radToDeg(angle) //因为css写的样式初始是指向右方,和initDir差了90°,所以减去。 this.dom.find(".dirText").css( "transform","rotate("+deg+"deg)" ) this.dom.find(".dirText span").css("transform","rotate("+(-deg)+"deg)") } */ updateLabel(quaternion){//更新北标签 let deg = THREE.Math.radToDeg(this.viewport.view.yaw) - 90 this.dom.find(".dirText").css( "transform","rotate("+deg+"deg)" ) this.dom.find(".dirText span").css("transform","rotate("+(-deg)+"deg)") } updateCamera(quaternion){ //更新canvas中的指南针表现,也就是更新相机,和场景中的相机朝向一致。 const radius = 5; //相机距离 this.camera.quaternion.copy(quaternion); var dir = this.viewport.view.direction; //相机朝向 this.camera.position.copy(dir.multiplyScalar(radius).negate()) //相机绕着指南针中心(000)转动 } changeViewport(viewport){ this.viewport = viewport; this.update(); //因相机更新了 } render(){ this.renderer.render(this.scene, this.camera) } setDisplay(state){ this.show = !!state; if(this.show){ this.update() this.dom.fadeIn(100) }else{ this.dom.fadeOut(100) } } autoJudgeDisplay(){ } setDomPos(){ if(!this.viewport)return let right = this.viewport.left + this.viewport.width this.dom.css({'right':((1-right)*100 + 1) + '%'}) } } export default Compass;