Compass.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. import * as THREE from "../../../../libs/three.js/build/three.module.js";
  2. const initDir = new THREE.Vector3(0,1,0)//指南针模型的北方向 向屏幕里
  3. class Compass extends THREE.EventDispatcher{
  4. constructor(dom, viewport){
  5. super()
  6. this.angle = 0;
  7. this.show = false;
  8. if(dom){
  9. this.dom = $(dom);
  10. }
  11. this.viewport = viewport
  12. this.init()
  13. }
  14. init(){
  15. var width = 100, height = 100
  16. if(!this.dom){
  17. this.dom = $('<div name="compass"></div>')
  18. $(viewer.renderArea).append(this.dom)
  19. }
  20. this.dom.css({ display:"none", position:"absolute",right:"1%",top: "60px",width:width+"px",height:height+"px", "z-index":100,"pointer-events":"none" })
  21. let child = $("<div class='dirText north'><span>"+/* (config.lang=='zh'? */'北'/* :'N') */+"</span></div><div class='center'></div>")
  22. this.dom.append(child)
  23. this.dom.find(".dirText").css({textAlign:"center","font-size":"10px","position":"absolute",
  24. width: "100%",
  25. height: "25px",
  26. "line-height": "25px"})
  27. this.dom.find(".north").css({"color":"#02a0e9","top":"0"})
  28. this.dom.find(".south").css({"color":"#ff1414","bottom":"0"})
  29. this.dom.find(".center").css({
  30. //"background":`url(${config.getStaticResource('img')}/dire.png)`,
  31. width: width/2+"px",
  32. height: height/2+"px",
  33. "background-size": "contain",
  34. "background-position": "center",
  35. left: "50%",
  36. top: "50%",
  37. transform: "translate(-50%,-50%)",
  38. position: "absolute"
  39. })
  40. this.dom.find(".dirText").css({
  41. "text-align": "center",
  42. "font-size": "10px",
  43. "color": "rgb(255, 255, 255)",
  44. "position": "absolute",
  45. "top": "50%",
  46. "left": "50%",
  47. "width": "45%",
  48. "height": "0px",
  49. "transform-origin": "left center",
  50. })
  51. this.dom.find(".dirText span").css({
  52. display: "block",
  53. position: "absolute",
  54. right: "5px",
  55. top: "0",
  56. width: "20px",
  57. height: "20px",
  58. "line-height": "20px",
  59. // "font-size": ".75rem ",
  60. "margin-top": "-10px",
  61. })
  62. try {
  63. this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha:true })//许钟文 添加个抗锯齿,否则添加的线条锯齿严重,
  64. this.renderer.autoClear = !0
  65. this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1)
  66. this.renderer.domElement.setAttribute('name','compass')
  67. this.renderer.setClearAlpha(0.0)
  68. //xst修改
  69. //this.renderer.setSize(width/2, height/2, false, window.devicePixelRatio ? window.devicePixelRatio : 1);
  70. //xst修改
  71. //this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1);
  72. //this.renderer.setSize(width/2, height/2);
  73. //xst修改
  74. this.renderer.setDrawingBufferSize( width/2, height/2, window.devicePixelRatio ? window.devicePixelRatio : 1 )
  75. //this.emit(SceneRendererEvents.ContextCreated)
  76. } catch (e) {
  77. viewer.dispatchEvent('webglError', {msg:e})
  78. }
  79. this.dom.find(".center")[0].appendChild(this.renderer.domElement);
  80. this.renderer.domElement.style.width = this.renderer.domElement.style.height = '100%'
  81. this.camera = new THREE.PerspectiveCamera;
  82. this.camera.fov = 50;
  83. this.camera.updateProjectionMatrix()
  84. this.scene = new THREE.Scene,
  85. this.scene.add(this.camera)
  86. this.createCompass()
  87. viewer.addEventListener('camera_changed', e => {
  88. if (e.viewport == this.viewport && (e.changeInfo.positionChanged || e.changeInfo.quaternionChanged)) {
  89. this.update()
  90. }
  91. })
  92. this.setDomPos()
  93. if(this.viewport)this.setDisplay(true)
  94. }
  95. createCompass(){
  96. //ConeBufferGeometry(radius : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
  97. const height = 2;
  98. const geometry1 = new THREE.ConeBufferGeometry( 0.7, height, 4, true );
  99. const geometry2 = new THREE.ConeBufferGeometry( 0.7, height, 4, true );
  100. const material = new THREE.MeshBasicMaterial({
  101. vertexColors :true
  102. })
  103. //指南针由两个四棱锥拼成,为了渐变颜色,采用指定vertexColor的方式。
  104. var setColor = function(geometry, color1,color2){
  105. const colors = [];
  106. for ( let i = 0, n = geometry.attributes.position.count; i < n; ++ i ) {
  107. colors.push( 1, 1, 1 );
  108. }
  109. var set = function(index, color){//设置第index个点的颜色
  110. colors[index*3+0] = color[0]
  111. colors[index*3+1] = color[1]
  112. colors[index*3+2] = color[2]
  113. }
  114. var mid = [(color1[0]+color2[0])/2, (color1[1]+color2[1])/2, (color1[2]+color2[2])/2 ]
  115. set(1,color1); set(5,color1);set(6,color1);
  116. set(2,mid); set(3,mid);set(7,mid);
  117. set(4,color2); set(8,color2);set(9,color2);
  118. geometry.setAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3))
  119. }
  120. var blue1 = [1/255,238/255,245/255] //逐渐变深
  121. var blue2 = [20/255,146/255,170/255]
  122. var blue3 = [40/255,60/255,103/255]
  123. setColor(geometry1, blue1,blue2)
  124. setColor(geometry2, blue2,blue3)
  125. /* 朝箭头方向看点构成如下 虽然geometry.attributes.position.count = 19 只有1-9设置的颜色是有效的 另外为什么7决定了上下两边的颜色呢…… 5、9可将其分成上下两个颜色
  126. 6
  127. /|\
  128. / | \
  129. 7 /_2|1_\ 5
  130. \ 3|4 / 9
  131. \ | /
  132. \|/
  133. 8
  134. */
  135. const cone = new THREE.Mesh( geometry1, material );
  136. cone.position.setY(height/2)
  137. geometry1.computeVertexNormals()//computeFaceNormals
  138. geometry2.computeVertexNormals()
  139. const cones = new THREE.Object3D();
  140. cones.add(cone)
  141. let cone2 = new THREE.Mesh( geometry2, material );
  142. cone2.rotation.x = Math.PI;
  143. cone2.position.setY(-height/2)
  144. cones.add(cone2)
  145. //cones.rotation.x = Math.PI / 2;//转向initDir的方向
  146. //cones.rotation.z = Math.PI / 2;
  147. cones.rotation.z = Math.PI ;//转向initDir的方向
  148. cones.scale.set(0.7,0.7,0.7)
  149. this.scene.add(cones)
  150. this.cones = cones
  151. }
  152. setNorth(){ //设置北方向,这决定了指南针自身的朝向。
  153. const floors = store.getters['scene/houstFloor'].floors
  154. if(!floors || !floors.length){
  155. return
  156. }
  157. const floor = floors[0]
  158. const metadata = app.store.getters['scene/metadata'] || {}
  159. this.angle = (floor && floor.dire || 0) + THREE.Math.radToDeg(parseFloat(metadata.floorPlanAngle || 0)) //基础朝向
  160. this.cones.rotation.y = Math.PI / 2 - THREE.Math.degToRad(this.angle)
  161. //console.log("dir:"+floor.dire+", floorPlanAngle:"+metadata.floorPlanAngle)
  162. this.update()
  163. }
  164. update(quaternion){
  165. if(!this.show)return;
  166. if(!quaternion) quaternion = this.viewport.camera.quaternion.clone();
  167. this.updateCamera(quaternion)
  168. this.updateLabel(quaternion)
  169. this.render()
  170. }
  171. /*updateLabel(quaternion){//更新北标签
  172. var dir = viewer.mainViewport.view.direction;
  173. var oriDir = initDir.clone() //指南针最初始时的北方向
  174. var extraQua
  175. if(objects.player.mode == "transitioning"){//当transitioning时,相机的quaternion不是用control的lookAt算出来,而是直接由一个quaternion过渡到另一个,这样相机将会是歪的,投影面也就不会是原先的水平面。
  176. var tempCamera = new THREE.Camera(); //借用camera的lookAt算出如果正视同样的target, quaternion会是什么值。 将它乘以当前相机quaternion,得到的就是相机歪的旋转值。
  177. tempCamera.position.copy(this.camera.position);
  178. tempCamera.lookAt(tempCamera.position.clone().add(dir))
  179. var q = tempCamera.quaternion.inverse()
  180. extraQua = q.premultiply(quaternion) //歪掉的额外旋转值
  181. }
  182. //北标签的方向为指南针轮盘方向,也就是要将camera的方向投影到水平面上。 但是如果相机歪了,看到的世界都会歪一定角度,投影面也要歪一定角度。
  183. var up = new THREE.Vector3(0,0,1) //投影水平面的法线,也是相机的摆正的up方向
  184. extraQua && up.applyQuaternion(extraQua)
  185. dir.projectOnPlane(up) //将方向投影到水平面上; 如果相机不是正视(extraQua不为0001),就要将水平面也转动
  186. oriDir.projectOnPlane(up)//为什么initDir投影了和没有投影angle结果一样
  187. var angle = dir.angleTo(oriDir)
  188. if(dir.cross(oriDir).y > 0)angle = -angle
  189. var deg = this.angle - 90 + THREE.Math.radToDeg(angle) //因为css写的样式初始是指向右方,和initDir差了90°,所以减去。
  190. this.dom.find(".dirText").css( "transform","rotate("+deg+"deg)" )
  191. this.dom.find(".dirText span").css("transform","rotate("+(-deg)+"deg)")
  192. } */
  193. updateLabel(quaternion){//更新北标签
  194. let deg = THREE.Math.radToDeg(this.viewport.view.yaw) - 90
  195. this.dom.find(".dirText").css( "transform","rotate("+deg+"deg)" )
  196. this.dom.find(".dirText span").css("transform","rotate("+(-deg)+"deg)")
  197. }
  198. updateCamera(quaternion){ //更新canvas中的指南针表现,也就是更新相机,和场景中的相机朝向一致。
  199. const radius = 5; //相机距离
  200. this.camera.quaternion.copy(quaternion);
  201. var dir = this.viewport.view.direction; //相机朝向
  202. this.camera.position.copy(dir.multiplyScalar(radius).negate()) //相机绕着指南针中心(000)转动
  203. }
  204. changeViewport(viewport){
  205. this.viewport = viewport;
  206. this.update(); //因相机更新了
  207. }
  208. render(){
  209. this.renderer.render(this.scene, this.camera)
  210. }
  211. setDisplay(state){
  212. this.show = !!state;
  213. if(this.show){
  214. this.update()
  215. this.dom.fadeIn(100)
  216. }else{
  217. this.dom.fadeOut(100)
  218. }
  219. }
  220. autoJudgeDisplay(){
  221. }
  222. setDomPos(){
  223. if(!this.viewport)return
  224. let right = this.viewport.left + this.viewport.width
  225. this.dom.css({'right':((1-right)*100 + 1) + '%'})
  226. }
  227. }
  228. export default Compass;