123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- 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.lastDirc = 0
- this.initAlpha = 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/", "close_to_bottom_L.glb", this.scene, async 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, // 1500
- settings.video.width * window.innerHeight / settings.video.height,
- window.innerHeight
- ))
- shaderMaterial.setFloat("isYUV", 0)
- m.material = shaderMaterial
- }
- });
- self.charactorManager = new CharactorManager(self)
- await self.charactorManager.readPointData()
- 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)
- // 一般来说瞬时距离不会超过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 = pointerOffset / 100 * Math.PI
- alphaOffset *= dirc
- this.initAlpha += alphaOffset
- if(charactor.actionType.split("-")[1] == "Walking") {
- // 行走时旋转相机,行走停止
- charactor.startWalk([], this.charactorManager)
- }
- // && dirc * this.lastDirc <= 0
- else if(Math.abs(this.initAlpha) > Math.PI / 30 && dirc != 0) {
- this.initAlpha = 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 = {
- videoList: [pointData.id + "/" + pointData.id],
- // reverse: dirc < 0
- videoId: common.uuid()
- }
- // window.connection.socket.emit("pushVideo", sendData)
- // console.log("[3D] send: ", sendData)
- this.rotateCamera(alphaOffset)
- }
- this.lastFramePoint = currentFramePoint
- this.lastDirc = dirc
- }
- async rotateCamera(alphaOffset, func) {
- // todo
- let video0 = this.house[1].material._textures.texture_video.video
- 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) {
- // 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);
- video0.currentTime = startTime
- await video0.play()
- 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, notPlay) {
- let videoTexture = new BABYLON.VideoTexture("", video, this.scene)
- this.house.forEach(mesh => {
- mesh.material && mesh.material.setTexture("texture_video", videoTexture)
- })
- !notPlay && video.play()
- }
- async updateHouseVideoBlob(blobUrl, notPlay) {
- let video = await common.createVideoElement(blobUrl)
- video.loop = "loop"
- let videoTexture = new BABYLON.VideoTexture("", video, this.scene)
- this.house.forEach(mesh => {
- mesh.material && mesh.material.setTexture("texture_video", videoTexture)
- })
- !notPlay && video.play()
- }
- }
|