index.js 11 KB

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