import common from "./utils/common.js"; import settings from "./utils/settings.js" export default class CameraController { constructor(app) { this.app = app var camera1 = new BABYLON.ArcRotateCamera("camera1", 0, Math.PI / 2, 10, new BABYLON.Vector3(0, 0, -1), app.scene); app.scene.activeCamera = camera1; // app.scene.activeCamera.attachControl(app.scene.canvas, true); camera1.inertia = 0 camera1.minZ = 100 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.enable = true this.lastCameraAlpha = 0 this.lastDirc = 0 this.initAlpha = 0 // 初始角度积累 // 人物相机射线 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)); } updateCameraPos() { if(!this.app.charactorManager || !this.app.charactorManager.charactor) return let charactor = this.app.charactorManager.charactor // 实时更新相机target let cameraTarget = 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.app.house)[0]; const offset = 0.05 if(!info || info.distance > settings.camera.distanceFromCharactor + offset) return let charactorVisi = 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) charactor.visible = false // if(info.distance - offset >= 0.25 && !charactorVisi) charactor.visible = true } startMouseRotate(pointerInfo) { this.lastFramePoint = new BABYLON.Vector2(pointerInfo.event.clientX, pointerInfo.event.clientY) } endMouseRotate() { this.lastFramePoint = null // this.lastDirc = 0 } /** * 鼠标移动时,计算xoffset,得到旋转方向 * xoffset越大,瞬时速度越快,转的角度就越大 * 指定xoffset大于一定值的帧,根据旋转方向和角度请求视频,相机animation改alpha与视频一致 * 视频旋转中,计算每帧的xoffset,如果方向没变,不再发出请求 * 如果方向相反,根据瞬时速度请求新视频,并停止当前动画,播放新动画 */ mouseRotating(pointerInfo) { if(!this.app.charactorManager || !this.app.charactorManager.charactor || !this.enable) return let charactor = this.app.charactorManager.charactor let currentFramePoint = new BABYLON.Vector2(pointerInfo.event.clientX, pointerInfo.event.clientY) let pointerOffset = currentFramePoint.clone().subtract(this.lastFramePoint).length() // 旋转方向:向右为负方向,顺时针;向左为正方向,逆时针(reverse) let dirc = Math.sign(currentFramePoint.x - this.lastFramePoint.x) // 一般来说瞬时距离不会超过100,定100时转180度 // let alphaOffset = Math.max(Math.floor((pointerOffset / 100 * Math.PI / (Math.PI / 30)) * (Math.PI / 30) + Math.PI / 60), Math.PI / 30) let alphaOffset = Math.min( pointerOffset / 1000 * Math.PI, Math.PI ) alphaOffset *= dirc this.initAlpha += alphaOffset if(charactor.actionType.split("-")[1] == "Walking") { // 行走时旋转相机,行走停止 charactor.startWalk([], this.app.charactorManager) } // && dirc * this.lastDirc <= 0 else if(Math.abs(this.initAlpha) > Math.PI / 30 && dirc != 0) { // let currentPath = charactor.walkData.pathArr[charactor.walkData.currentPoint] // let startPoint = (currentPath && currentPath.point) || charactor.mesh.position // let point1 = Math.floor((-this.camera.alpha + Math.PI * 2) % (Math.PI * 2) / Math.PI * 180 / 6) * 6 // let point2 = Math.floor((-this.camera.alpha - this.initAlpha + Math.PI * 2) % (Math.PI * 2) / Math.PI * 180 / 6) * 6 let point1 = Math.floor(this.camera.alpha / Math.PI * 180 / 6) * 6 let point2 = Math.floor((this.camera.alpha + this.initAlpha) / Math.PI * 180 / 6) * 6 // let sangle, eangle this.alphaOffset = (point1 - point2) / 180 * Math.PI // if(dirc < 0) { // sangle = point1 // eangle = point2 == 0 ? 359 : point2 // } else { // sangle = point2 // eangle = point1 == 0 ? 359 : point1 // } // let pointData = this.app.charactorManager.getClosestPointData(startPoint) // let sendData = { // videoPath: pointData.id + "/" + pointData.id, // sangle: sangle, // eangle: eangle, // reverses: dirc < 0, // sceneCode: settings.sceneCode, // roomId: settings.roomId, // userId: settings.userId, // } // window.connection.socket.emit("getRotateVideo", sendData); // console.log("[3D] send: ", sendData) this.rotateCamera(this.alphaOffset, dirc) this.enable = false this.initAlpha = 0 } this.lastFramePoint = currentFramePoint if(dirc != 0 && this.lastDirc * dirc > 0) this.lastDirc = dirc } async rotateCamera(alphaOffset, dirc, func) { if(dirc == 0) { func && func() return } let video0 if(this.lastDirc * dirc <= 0) { if(dirc < 0) { // 顺时针 video0 = document.getElementById("houseTexture") } if(dirc > 0) { // 逆时针 video0 = document.getElementById("houseTextureReverse") } this.lastDirc = dirc } else { // 当行走结束后,texture是walk视频,所以必须重赋值 if(this.lastDirc < 0) { // 顺时针 video0 = document.getElementById("houseTexture") } if(this.lastDirc > 0) { // 逆时针 video0 = document.getElementById("houseTextureReverse") } } if(video0.id != "houseTexture" && video0.id != "houseTextureReverse") return let startTime = (this.camera.alpha % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2) / (Math.PI * 2) * video0.duration if(dirc < 0) { startTime = video0.duration - startTime } let durtime = Math.abs(alphaOffset / (Math.PI * 2) * video0.duration) 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); video0.currentTime = startTime if(this.app.hasVideoTexture) this.app.house[1].material._textures.texture_video.video = video0 await video0.play() // 实时矫正currentTime let endTime = (startTime + durtime) % video0.duration let handle = setInterval(() => { if(endTime > startTime) { // 不跨0 if(video0.currentTime > endTime) video0.currentTime = endTime } else { if(video0.currentTime > endTime && video0.currentTime < startTime) video0.currentTime = endTime } }, 10) this.app.scene.beginDirectAnimation(this.camera, [rotateAni], 0, rotateCameraFrameNum, false, 1, () => { clearInterval(handle) this.enable = true video0.currentTime = (startTime + durtime) % video0.duration video0.pause() func && func() }); } lockCamera(isTrue) { this.cameraControlEnable = isTrue this.camera.lowerAlphaLimit = isTrue ? this.camera.alpha : null this.camera.upperAlphaLimit = isTrue ? this.camera.alpha : null } }