index.js 12 KB

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