import CharactorManager from "./CharactorManager.js"; import common from "./utils/common.js"; import houseShader from "./shaders/houseShader.js"; import settings from "./utils/settings.js"; export default class App { constructor(engine) { var scene = new BABYLON.Scene(engine); this.scene = scene scene.collisionsEnabled = true; var camera1 = new BABYLON.ArcRotateCamera("camera1", 0, Math.PI / 2, 10, new BABYLON.Vector3(0, 0, 0), scene); scene.activeCamera = camera1; // scene.activeCamera.attachControl(scene.canvas, true); camera1.inertia = 0 camera1.minZ = 0 camera1.fov = settings.camera.fov camera1.fovMode = BABYLON.ArcRotateCamera.FOVMODE_HORIZONTAL_FIXED camera1.lowerBetaLimit = Math.PI / 2 camera1.upperBetaLimit = Math.PI / 2 camera1.lowerRadiusLimit = settings.camera.distanceFromCharactor; camera1.upperRadiusLimit = settings.camera.distanceFromCharactor; camera1.angularSensibilityX /= 2; this.camera = camera1 this.lastCameraAlpha = 0 this.cameraControlEnable = true // 人物相机射线 this.ray = new BABYLON.Ray(new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(0,0,1), 50); // BABYLON.RayHelper.CreateAndShow(this.ray, scene, new BABYLON.Color3(1, 0.1, 0.1)); // Lights var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, -1, 0), scene); light.intensity = 1.0; light.specular = BABYLON.Color3.Black(); var light2 = new BABYLON.HemisphericLight("dir01", new BABYLON.Vector3(0, 1, 0), scene); light2.intensity = 0.1; light2.position = new BABYLON.Vector3(0, 5, 5); // Skybox var skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 1000.0 }, scene); var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene); skyboxMaterial.backFaceCulling = false; skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("textures/environment.env", scene); skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE; skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0); skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0); skybox.material = skyboxMaterial; this.init() this.bindEvents() } init() { let self = this BABYLON.SceneLoader.ImportMesh("", "../scenes/house/", "000.glb", this.scene, function (newMeshes, particleSystems, skeletons, animationGroups) { self.house = newMeshes self.house[0].position = new BABYLON.Vector3(0.6, 2.1, 1.5) // self.house[0].position = new BABYLON.Vector3(-22, 0, 12) let houseVideo = document.getElementById("houseTexture0") newMeshes.forEach(m => { // m.scaling.scaleInPlace(100); m._geometry && (m.checkCollisions = true) if(m.material) { BABYLON.Effect.ShadersStore['aFragmentShader'] = houseShader.fragment; BABYLON.Effect.ShadersStore['aVertexShader'] = houseShader.vertex; let shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, { vertex: "a", fragment: "a", }, { attributes: houseShader.attributes, uniforms: houseShader.uniforms, defines: houseShader.defines }); let videoTexture = new BABYLON.VideoTexture("", houseVideo, scene) // document.getElementById("houseTexture0").play() // document.getElementById("houseTexture0").loop = "loop" shaderMaterial.setTexture("texture_video", videoTexture) shaderMaterial.setVector3("focal_width_height", new BABYLON.Vector3( 864 * window.innerWidth / settings.video.width, settings.video.width * window.innerHeight / settings.video.height, window.innerHeight )) shaderMaterial.setFloat("isYUV", 0) m.material = shaderMaterial } }); self.charactorManager = new CharactorManager(self) self.charactorManager.importCharactorModel("../scenes/charactors/", "man_YXL.glb") }); } bindEvents() { this.scene.onPointerObservable.add((pointerInfo) => { switch (pointerInfo.type) { case BABYLON.PointerEventTypes.POINTERDOWN: this.lastFramePoint = new BABYLON.Vector2(pointerInfo.event.clientX, pointerInfo.event.clientY) break; case BABYLON.PointerEventTypes.POINTERUP: this.lastFramePoint = null this.lastDirc = 0 break; case BABYLON.PointerEventTypes.POINTERMOVE: if(this.lastFramePoint) this.cameraControl(pointerInfo) break; case BABYLON.PointerEventTypes.POINTERWHEEL: break; case BABYLON.PointerEventTypes.POINTERPICK: break; case BABYLON.PointerEventTypes.POINTERTAP: if(pointerInfo.pickInfo.hit && this.house.indexOf(pointerInfo.pickInfo.pickedMesh)) { this.charactorManager.clickHouse() } break; case BABYLON.PointerEventTypes.POINTERDOUBLETAP: break; } }); this.scene.onBeforeAnimationsObservable.add(() => { if(this.charactorManager && this.charactorManager.charactor) this.charactorManager.onBeforeAnimation() }) this.scene.onBeforeRenderObservable.add(() => { this.updateCameraPos() }) } updateCameraPos() { if(!this.charactorManager || !this.charactorManager.charactor) return // 实时更新相机target let cameraTarget = this.charactorManager.charactor.mesh.position.clone() cameraTarget.y = settings.camera.height this.camera.setTarget(cameraTarget) // 相机碰撞检测 this.ray.origin = cameraTarget this.ray.direction = BABYLON.Vector3.Normalize( this.camera.position.clone().subtract(cameraTarget) ) let info = this.ray.intersectsMeshes(this.house)[0]; const offset = 0 // 0.6 if(!info || info.distance > settings.camera.distanceFromCharactor + offset) return let charactorVisi = this.charactorManager.charactor.visible this.camera.lowerRadiusLimit = Math.max( info.distance - offset, 0.1 ) this.camera.upperRadiusLimit = Math.max( info.distance - offset, 0.1 ) // 根据相机位置更新人物显隐 if(info.distance - offset < 0.25 && charactorVisi) this.charactorManager.charactor.visible = false if(info.distance - offset >= 0.25 && !charactorVisi) this.charactorManager.charactor.visible = true } /** * 鼠标移动时,计算xoffset,得到旋转方向 * xoffset越大,瞬时速度越快,转的角度就越大 * 指定xoffset大于一定值的帧,根据旋转方向和角度请求视频,相机animation改alpha与视频一致 * 视频旋转中,计算每帧的xoffset,如果方向没变,不再发出请求 * 如果方向相反,根据瞬时速度请求新视频,并停止当前动画,播放新动画 */ cameraControl(pointerInfo) { if(!this.charactorManager || !this.charactorManager.charactor || !this.cameraControlEnable) return let charactor = this.charactorManager.charactor let currentFramePoint = new BABYLON.Vector2(pointerInfo.event.clientX, pointerInfo.event.clientY) let pointerOffset = currentFramePoint.clone().subtract(this.lastFramePoint).length() let dirc = Math.sign(this.lastFramePoint.x - currentFramePoint.x) if(!this.lastDirc) this.lastDirc = 0 // 一般来说瞬时距离不会超过100,定100时转180度 let alphaOffset = pointerOffset / 100 * Math.PI alphaOffset *= dirc if(charactor.actionType.split("-")[1] == "Walking") { // 行走时旋转相机,行走停止 charactor.startWalk([], this.charactorManager) } else if(dirc != 0 && dirc * this.lastDirc <= 0) { let currentPath = charactor.walkData.pathArr[charactor.walkData.currentPoint] let startPoint = (currentPath && currentPath.point) || charactor.mesh.position let pointData = this.charactorManager.getClosestPointData(startPoint) let sendData = { video: [pointData.id], reverse: dirc < 0 } // window.connection.socket.emit("getPush", sendData) this.rotateCamera(alphaOffset) } this.lastFramePoint = currentFramePoint this.lastDirc = dirc } rotateCamera(alphaOffset, func) { // todo let video0 = document.getElementById("houseTexture0") let startTime = Math.abs(this.camera.alpha) % (Math.PI * 2) / (Math.PI * 2) * video0.duration let durtime = Math.abs(alphaOffset / (Math.PI * 2) * video0.duration) if(video0.paused) { video0.currentTime = startTime video0.play() // if(dirc * this.lastDirc < 0) { // 清除已有动画再播新动画 this.scene.stopAnimation(this.camera, "rotateCamera") // } const rotateAni = new BABYLON.Animation("rotateCamera", "alpha", settings.video.frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE); let rotateCameraFrameNum = settings.video.frameRate * durtime const rotateFrames = [{ frame: 0, value: this.camera.alpha },{ frame: rotateCameraFrameNum, value: this.camera.alpha + alphaOffset }]; rotateAni.setKeys(rotateFrames); this.scene.beginDirectAnimation(this.camera, [rotateAni], 0, rotateCameraFrameNum, false, 1, () => { this.lastDirc = 0 video0.pause() func && func() }); } else { // console.error("-------------") video0.pause() } } lockCamera(isTrue) { this.cameraControlEnable = isTrue // this.camera.lowerAlphaLimit = isTrue ? this.camera.alpha : null // this.camera.upperAlphaLimit = isTrue ? this.camera.alpha : null } updateHouseVideo(video) { let videoTexture = new BABYLON.VideoTexture("", video, this.scene) this.house.forEach(mesh => { mesh.material && mesh.material.setTexture("texture_video", videoTexture) }) video.play() } }