index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import CharactorManager from "./CharactorManager.js";
  2. import common from "./utils/common.js";
  3. import houseShader from "./shaders/houseShader.js";
  4. import settings from "./utils/settings.js";
  5. export default class App {
  6. constructor(engine) {
  7. var scene = new BABYLON.Scene(engine);
  8. this.scene = scene
  9. scene.collisionsEnabled = true;
  10. var camera1 = new BABYLON.ArcRotateCamera("camera1", 0, Math.PI / 2, 10, new BABYLON.Vector3(0, 0, 0), scene);
  11. scene.activeCamera = camera1;
  12. // scene.activeCamera.attachControl(scene.canvas, true);
  13. camera1.inertia = 0
  14. camera1.minZ = 0
  15. camera1.fov = settings.camera.fov
  16. camera1.fovMode = BABYLON.ArcRotateCamera.FOVMODE_HORIZONTAL_FIXED
  17. camera1.lowerBetaLimit = Math.PI / 2
  18. camera1.upperBetaLimit = Math.PI / 2
  19. camera1.lowerRadiusLimit = settings.camera.distanceFromCharactor;
  20. camera1.upperRadiusLimit = settings.camera.distanceFromCharactor;
  21. camera1.angularSensibilityX /= 2;
  22. this.camera = camera1
  23. this.lastCameraAlpha = 0
  24. this.cameraControlEnable = true
  25. // 人物相机射线
  26. this.ray = new BABYLON.Ray(new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(0,0,1), 50);
  27. // BABYLON.RayHelper.CreateAndShow(this.ray, scene, new BABYLON.Color3(1, 0.1, 0.1));
  28. // Lights
  29. var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, -1, 0), scene);
  30. light.intensity = 1.0;
  31. light.specular = BABYLON.Color3.Black();
  32. var light2 = new BABYLON.HemisphericLight("dir01", new BABYLON.Vector3(0, 1, 0), scene);
  33. light2.intensity = 0.1;
  34. light2.position = new BABYLON.Vector3(0, 5, 5);
  35. // Skybox
  36. var skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 1000.0 }, scene);
  37. var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
  38. skyboxMaterial.backFaceCulling = false;
  39. skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("textures/environment.env", scene);
  40. skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
  41. skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
  42. skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
  43. skybox.material = skyboxMaterial;
  44. this.init()
  45. this.bindEvents()
  46. }
  47. init() {
  48. let self = this
  49. BABYLON.SceneLoader.ImportMesh("", "../scenes/house/", "000.glb", this.scene, function (newMeshes, particleSystems, skeletons, animationGroups) {
  50. self.house = newMeshes
  51. self.house[0].position = new BABYLON.Vector3(0.6, 2.1, 1.5)
  52. // self.house[0].position = new BABYLON.Vector3(-22, 0, 12)
  53. let houseVideo = document.getElementById("houseTexture0")
  54. newMeshes.forEach(m => {
  55. // m.scaling.scaleInPlace(100);
  56. m._geometry && (m.checkCollisions = true)
  57. if(m.material)
  58. {
  59. BABYLON.Effect.ShadersStore['aFragmentShader'] = houseShader.fragment;
  60. BABYLON.Effect.ShadersStore['aVertexShader'] = houseShader.vertex;
  61. let shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, { vertex: "a", fragment: "a", }, {
  62. attributes: houseShader.attributes,
  63. uniforms: houseShader.uniforms,
  64. defines: houseShader.defines
  65. });
  66. let videoTexture = new BABYLON.VideoTexture("", houseVideo, scene)
  67. // document.getElementById("houseTexture0").play()
  68. // document.getElementById("houseTexture0").loop = "loop"
  69. shaderMaterial.setTexture("texture_video", videoTexture)
  70. shaderMaterial.setVector3("focal_width_height", new BABYLON.Vector3(
  71. 864 * window.innerWidth / settings.video.width,
  72. settings.video.width * window.innerHeight / settings.video.height,
  73. window.innerHeight
  74. ))
  75. shaderMaterial.setFloat("isYUV", 0)
  76. m.material = shaderMaterial
  77. }
  78. });
  79. self.charactorManager = new CharactorManager(self)
  80. self.charactorManager.importCharactorModel("../scenes/charactors/", "man_YXL.glb")
  81. });
  82. }
  83. bindEvents() {
  84. this.scene.onPointerObservable.add((pointerInfo) => {
  85. switch (pointerInfo.type) {
  86. case BABYLON.PointerEventTypes.POINTERDOWN:
  87. this.lastFramePoint = new BABYLON.Vector2(pointerInfo.event.clientX, pointerInfo.event.clientY)
  88. break;
  89. case BABYLON.PointerEventTypes.POINTERUP:
  90. this.lastFramePoint = null
  91. this.lastDirc = 0
  92. break;
  93. case BABYLON.PointerEventTypes.POINTERMOVE:
  94. if(this.lastFramePoint) this.cameraControl(pointerInfo)
  95. break;
  96. case BABYLON.PointerEventTypes.POINTERWHEEL:
  97. break;
  98. case BABYLON.PointerEventTypes.POINTERPICK:
  99. break;
  100. case BABYLON.PointerEventTypes.POINTERTAP:
  101. if(pointerInfo.pickInfo.hit && this.house.indexOf(pointerInfo.pickInfo.pickedMesh)) {
  102. this.charactorManager.clickHouse()
  103. }
  104. break;
  105. case BABYLON.PointerEventTypes.POINTERDOUBLETAP:
  106. break;
  107. }
  108. });
  109. this.scene.onBeforeAnimationsObservable.add(() => {
  110. if(this.charactorManager && this.charactorManager.charactor)
  111. this.charactorManager.onBeforeAnimation()
  112. })
  113. this.scene.onBeforeRenderObservable.add(() => {
  114. this.updateCameraPos()
  115. })
  116. }
  117. updateCameraPos() {
  118. if(!this.charactorManager || !this.charactorManager.charactor) return
  119. // 实时更新相机target
  120. let cameraTarget = this.charactorManager.charactor.mesh.position.clone()
  121. cameraTarget.y = settings.camera.height
  122. this.camera.setTarget(cameraTarget)
  123. // 相机碰撞检测
  124. this.ray.origin = cameraTarget
  125. this.ray.direction = BABYLON.Vector3.Normalize( this.camera.position.clone().subtract(cameraTarget) )
  126. let info = this.ray.intersectsMeshes(this.house)[0];
  127. const offset = 0 // 0.6
  128. if(!info || info.distance > settings.camera.distanceFromCharactor + offset) return
  129. let charactorVisi = this.charactorManager.charactor.visible
  130. this.camera.lowerRadiusLimit = Math.max( info.distance - offset, 0.1 )
  131. this.camera.upperRadiusLimit = Math.max( info.distance - offset, 0.1 )
  132. // 根据相机位置更新人物显隐
  133. if(info.distance - offset < 0.25 && charactorVisi) this.charactorManager.charactor.visible = false
  134. if(info.distance - offset >= 0.25 && !charactorVisi) this.charactorManager.charactor.visible = true
  135. }
  136. /**
  137. * 鼠标移动时,计算xoffset,得到旋转方向
  138. * xoffset越大,瞬时速度越快,转的角度就越大
  139. * 指定xoffset大于一定值的帧,根据旋转方向和角度请求视频,相机animation改alpha与视频一致
  140. * 视频旋转中,计算每帧的xoffset,如果方向没变,不再发出请求
  141. * 如果方向相反,根据瞬时速度请求新视频,并停止当前动画,播放新动画
  142. */
  143. cameraControl(pointerInfo) {
  144. if(!this.charactorManager || !this.charactorManager.charactor || !this.cameraControlEnable) return
  145. let charactor = this.charactorManager.charactor
  146. let currentFramePoint = new BABYLON.Vector2(pointerInfo.event.clientX, pointerInfo.event.clientY)
  147. let pointerOffset = currentFramePoint.clone().subtract(this.lastFramePoint).length()
  148. let dirc = Math.sign(this.lastFramePoint.x - currentFramePoint.x)
  149. if(!this.lastDirc) this.lastDirc = 0
  150. // 一般来说瞬时距离不会超过100,定100时转180度
  151. let alphaOffset = pointerOffset / 100 * Math.PI
  152. alphaOffset *= dirc
  153. if(charactor.actionType.split("-")[1] == "Walking") {
  154. // 行走时旋转相机,行走停止
  155. charactor.startWalk([], this.charactorManager)
  156. }
  157. else if(dirc != 0 && dirc * this.lastDirc <= 0) {
  158. let currentPath = charactor.walkData.pathArr[charactor.walkData.currentPoint]
  159. let startPoint = (currentPath && currentPath.point) || charactor.mesh.position
  160. let pointData = this.charactorManager.getClosestPointData(startPoint)
  161. let sendData = {
  162. video: [pointData.id],
  163. reverse: dirc < 0
  164. }
  165. // window.connection.socket.emit("getPush", sendData)
  166. this.rotateCamera(alphaOffset)
  167. }
  168. this.lastFramePoint = currentFramePoint
  169. this.lastDirc = dirc
  170. }
  171. rotateCamera(alphaOffset, func) {
  172. // todo
  173. let video0 = document.getElementById("houseTexture0")
  174. let startTime = Math.abs(this.camera.alpha) % (Math.PI * 2) / (Math.PI * 2) * video0.duration
  175. let durtime = Math.abs(alphaOffset / (Math.PI * 2) * video0.duration)
  176. if(video0.paused) {
  177. video0.currentTime = startTime
  178. video0.play()
  179. // if(dirc * this.lastDirc < 0) {
  180. // 清除已有动画再播新动画
  181. this.scene.stopAnimation(this.camera, "rotateCamera")
  182. // }
  183. const rotateAni = new BABYLON.Animation("rotateCamera", "alpha", settings.video.frameRate,
  184. BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE);
  185. let rotateCameraFrameNum = settings.video.frameRate * durtime
  186. const rotateFrames = [{
  187. frame: 0,
  188. value: this.camera.alpha
  189. },{
  190. frame: rotateCameraFrameNum,
  191. value: this.camera.alpha + alphaOffset
  192. }];
  193. rotateAni.setKeys(rotateFrames);
  194. this.scene.beginDirectAnimation(this.camera, [rotateAni], 0, rotateCameraFrameNum, false, 1,
  195. () => {
  196. this.lastDirc = 0
  197. video0.pause()
  198. func && func()
  199. });
  200. } else {
  201. // console.error("-------------")
  202. video0.pause()
  203. }
  204. }
  205. lockCamera(isTrue) {
  206. this.cameraControlEnable = isTrue
  207. // this.camera.lowerAlphaLimit = isTrue ? this.camera.alpha : null
  208. // this.camera.upperAlphaLimit = isTrue ? this.camera.alpha : null
  209. }
  210. updateHouseVideo(video) {
  211. let videoTexture = new BABYLON.VideoTexture("", video, this.scene)
  212. this.house.forEach(mesh => {
  213. mesh.material && mesh.material.setTexture("texture_video", videoTexture)
  214. })
  215. video.play()
  216. }
  217. }