zhouenguang 3 lat temu
commit
d56c7ffd54
100 zmienionych plików z 27338 dodań i 0 usunięć
  1. 117 0
      index.html
  2. 2 0
      libs/Oimo.js
  3. 925 0
      libs/ammo.js
  4. 3 0
      libs/babylon.gui.min.js
  5. 1 0
      libs/babylon.gui.min.js.map
  6. 3 0
      libs/babylon.inspector.bundle.js
  7. 1 0
      libs/babylon.inspector.bundle.js.map
  8. 2 0
      libs/babylon.js
  9. 1 0
      libs/babylon.js.map
  10. 11466 0
      libs/babylonjs.loaders.js
  11. 1 0
      libs/babylonjs.loaders.js.map
  12. 3 0
      libs/babylonjs.materials.min.js
  13. 1 0
      libs/babylonjs.materials.min.js.map
  14. 3 0
      libs/babylonjs.postProcess.min.js
  15. 1 0
      libs/babylonjs.postProcess.min.js.map
  16. 3 0
      libs/babylonjs.proceduralTextures.min.js
  17. 1 0
      libs/babylonjs.proceduralTextures.min.js.map
  18. 2 0
      libs/babylonjs.serializers.min.js
  19. 1 0
      libs/babylonjs.serializers.min.js.map
  20. 13687 0
      libs/cannon.js
  21. 2 0
      libs/dat.gui.min.js
  22. 1 0
      libs/earcut.min.js
  23. 21 0
      libs/recast.js
  24. 169 0
      modules/Charactor.js
  25. 146 0
      modules/CharactorManager.js
  26. 43 0
      modules/common.js
  27. 31 0
      modules/data.js
  28. 178 0
      modules/houseShader copy.js
  29. 88 0
      modules/houseShader.js
  30. 286 0
      modules/index.js
  31. 14 0
      modules/settings.js
  32. BIN
      scenes/charactors/man_YXL.glb
  33. BIN
      scenes/house/000.glb
  34. BIN
      scenes/house/YQT_Low_xverse.glb
  35. BIN
      textures/123.mp4
  36. BIN
      textures/environment.env
  37. BIN
      textures/op/0/0.mp4
  38. BIN
      textures/op/0/0_1_0.mp4
  39. BIN
      textures/op/0/0_1_1.mp4
  40. BIN
      textures/op/0/0_1_2.mp4
  41. BIN
      textures/op/0/0_1_3.mp4
  42. BIN
      textures/op/0/0_1_4.mp4
  43. BIN
      textures/op/0/0_1_5.mp4
  44. BIN
      textures/op/0/0_1_6.mp4
  45. BIN
      textures/op/0/0_1_7.mp4
  46. BIN
      textures/op/1/1.mp4
  47. BIN
      textures/op/1/1_0_0.mp4
  48. BIN
      textures/op/1/1_0_1.mp4
  49. BIN
      textures/op/1/1_0_2.mp4
  50. BIN
      textures/op/1/1_0_3.mp4
  51. BIN
      textures/op/1/1_0_4.mp4
  52. BIN
      textures/op/1/1_0_5.mp4
  53. BIN
      textures/op/1/1_0_6.mp4
  54. BIN
      textures/op/1/1_0_7.mp4
  55. 135 0
      textures/op/points.json
  56. BIN
      textures/outputmp4/0/0.mp4
  57. BIN
      textures/outputmp4/0/0_1_0.mp4
  58. BIN
      textures/outputmp4/0/0_1_1.mp4
  59. BIN
      textures/outputmp4/0/0_1_2.mp4
  60. BIN
      textures/outputmp4/0/0_1_3.mp4
  61. BIN
      textures/outputmp4/0/0_1_4.mp4
  62. BIN
      textures/outputmp4/0/0_1_5.mp4
  63. BIN
      textures/outputmp4/0/0_1_6.mp4
  64. BIN
      textures/outputmp4/0/0_1_7.mp4
  65. BIN
      textures/outputmp4/0/0_3_0.mp4
  66. BIN
      textures/outputmp4/0/0_3_1.mp4
  67. BIN
      textures/outputmp4/0/0_3_2.mp4
  68. BIN
      textures/outputmp4/0/0_3_3.mp4
  69. BIN
      textures/outputmp4/0/0_3_4.mp4
  70. BIN
      textures/outputmp4/0/0_3_5.mp4
  71. BIN
      textures/outputmp4/0/0_3_6.mp4
  72. BIN
      textures/outputmp4/0/0_3_7.mp4
  73. BIN
      textures/outputmp4/0/0_4_0.mp4
  74. BIN
      textures/outputmp4/0/0_4_1.mp4
  75. BIN
      textures/outputmp4/0/0_4_2.mp4
  76. BIN
      textures/outputmp4/0/0_4_3.mp4
  77. BIN
      textures/outputmp4/0/0_4_4.mp4
  78. BIN
      textures/outputmp4/0/0_4_5.mp4
  79. BIN
      textures/outputmp4/0/0_4_6.mp4
  80. BIN
      textures/outputmp4/0/0_4_7.mp4
  81. BIN
      textures/outputmp4/1/1.mp4
  82. BIN
      textures/outputmp4/1/1_0_0.mp4
  83. BIN
      textures/outputmp4/1/1_0_1.mp4
  84. BIN
      textures/outputmp4/1/1_0_2.mp4
  85. BIN
      textures/outputmp4/1/1_0_3.mp4
  86. BIN
      textures/outputmp4/1/1_0_4.mp4
  87. BIN
      textures/outputmp4/1/1_0_5.mp4
  88. BIN
      textures/outputmp4/1/1_0_6.mp4
  89. BIN
      textures/outputmp4/1/1_0_7.mp4
  90. BIN
      textures/outputmp4/1/1_2_0.mp4
  91. BIN
      textures/outputmp4/1/1_2_1.mp4
  92. BIN
      textures/outputmp4/1/1_2_2.mp4
  93. BIN
      textures/outputmp4/1/1_2_3.mp4
  94. BIN
      textures/outputmp4/1/1_2_4.mp4
  95. BIN
      textures/outputmp4/1/1_2_5.mp4
  96. BIN
      textures/outputmp4/1/1_2_6.mp4
  97. BIN
      textures/outputmp4/1/1_2_7.mp4
  98. BIN
      textures/outputmp4/1/1_3_0.mp4
  99. BIN
      textures/outputmp4/1/1_3_1.mp4
  100. 0 0
      textures/outputmp4/1/1_3_2.mp4

+ 117 - 0
index.html

@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html lang="en">
+<html>
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <title>demo</title>
+        <style>
+            html, body {
+                overflow: hidden;
+                width: 100%;
+                height: 100%;
+                margin: 0;
+                padding: 0;
+            }
+
+            #renderCanvas {
+                width: 100%;
+                height: 100%;
+                touch-action: none;
+            }
+
+            #videoTextureBox video {
+                display: none;
+            }
+        </style>
+    </head>
+<body>
+
+    <canvas id="renderCanvas"></canvas>
+
+    <div id = "videoTextureBox">
+        <video id="houseTexture0" src="./textures/outputmp4/0/0.mp4" crossOrigin="anonymous" playsinline autoplay muted></video>
+    </div>
+
+    <!-- Babylon.js -->
+    <script src="./libs/dat.gui.min.js"></script>
+    <script src="./libs/ammo.js"></script>
+    <script src="./libs/cannon.js"></script>
+    <script src="./libs/Oimo.js"></script>
+    <script src="./libs/earcut.min.js"></script>
+    <script src="./libs/recast.js"></script>
+    
+    <script src="./libs/babylon.js"></script>
+    <script src="./libs/babylonjs.materials.min.js"></script>
+    <script src="./libs/babylonjs.proceduralTextures.min.js"></script>
+    <script src="./libs/babylonjs.postProcess.min.js"></script>
+    <script src="./libs/babylonjs.loaders.js"></script>
+    <script src="./libs/babylonjs.serializers.min.js"></script>
+    <script src="./libs/babylon.gui.min.js"></script>
+    <script src="./libs/babylon.inspector.bundle.js"></script>
+
+    <script type="module">
+
+        import App from "./modules/index.js"
+
+        var canvas = document.getElementById("renderCanvas");
+
+        var engine = null;
+        var scene = null;
+        var sceneToRender = null;
+
+        var startRenderLoop = function (engine, canvas) {
+            engine.runRenderLoop(function () {
+                if (sceneToRender && sceneToRender.activeCamera) {
+                    sceneToRender.render();
+                }
+            });
+        }
+
+        var createDefaultEngine = function() { 
+            return new BABYLON.Engine(canvas, true, { 
+                preserveDrawingBuffer: true,
+                stencil: true,  
+                disableWebGL2Support: false
+            }); 
+        };
+
+        var asyncEngineCreation = async function() {
+            try {
+                return createDefaultEngine();
+            } catch(e) {
+                console.log("the available createEngine function failed. Creating the default engine instead");
+                return createDefaultEngine();
+            }
+        }
+        
+
+        window.initFunction = async function() {
+
+            engine = await asyncEngineCreation();
+
+            if (!engine) throw 'engine should not be null.';
+            startRenderLoop(engine, canvas);
+
+            engine.enableOfflineSupport = false;
+        
+            // Scene and Camera
+            window.app = new App(engine)
+            scene = window.app.scene
+
+        };
+
+        initFunction().then(() => {sceneToRender = scene});
+
+
+        window.scene = scene;
+        window.engine = engine
+        // Resize
+        window.addEventListener("resize", function () {
+            engine.resize();
+        });
+
+
+    </script>
+</body>
+</html>

Plik diff jest za duży
+ 2 - 0
libs/Oimo.js


Plik diff jest za duży
+ 925 - 0
libs/ammo.js


Plik diff jest za duży
+ 3 - 0
libs/babylon.gui.min.js


Plik diff jest za duży
+ 1 - 0
libs/babylon.gui.min.js.map


Plik diff jest za duży
+ 3 - 0
libs/babylon.inspector.bundle.js


Plik diff jest za duży
+ 1 - 0
libs/babylon.inspector.bundle.js.map


Plik diff jest za duży
+ 2 - 0
libs/babylon.js


Plik diff jest za duży
+ 1 - 0
libs/babylon.js.map


Plik diff jest za duży
+ 11466 - 0
libs/babylonjs.loaders.js


Plik diff jest za duży
+ 1 - 0
libs/babylonjs.loaders.js.map


Plik diff jest za duży
+ 3 - 0
libs/babylonjs.materials.min.js


Plik diff jest za duży
+ 1 - 0
libs/babylonjs.materials.min.js.map


Plik diff jest za duży
+ 3 - 0
libs/babylonjs.postProcess.min.js


Plik diff jest za duży
+ 1 - 0
libs/babylonjs.postProcess.min.js.map


Plik diff jest za duży
+ 3 - 0
libs/babylonjs.proceduralTextures.min.js


Plik diff jest za duży
+ 1 - 0
libs/babylonjs.proceduralTextures.min.js.map


Plik diff jest za duży
+ 2 - 0
libs/babylonjs.serializers.min.js


Plik diff jest za duży
+ 1 - 0
libs/babylonjs.serializers.min.js.map


Plik diff jest za duży
+ 13687 - 0
libs/cannon.js


Plik diff jest za duży
+ 2 - 0
libs/dat.gui.min.js


Plik diff jest za duży
+ 1 - 0
libs/earcut.min.js


Plik diff jest za duży
+ 21 - 0
libs/recast.js


+ 169 - 0
modules/Charactor.js

@@ -0,0 +1,169 @@
+export default class Charactor {
+
+    constructor(newMeshes, particleSystems, skeletons, animationGroups) {
+
+        this.mesh = newMeshes[0]
+        this.particleSystem = particleSystems[0]
+        this.skeleton = skeletons[0]
+
+        // 设置人物动画权重
+        this.animation = {}
+        animationGroups.forEach((ani, index) => {
+
+            this.animation[ani.name] = ani
+            ani.play(true)
+
+            if(index == 0) {
+                ani.setWeightForAllAnimatables(1);
+                this.actionType = ani.name + "-" + ani.name
+            }
+            else ani.setWeightForAllAnimatables(0);
+
+        })
+
+        // 动画沙漏, 用于人物模型动画权重转换
+        this.aniHourglass = {
+            upper: 0,
+            lower: 1,
+        }
+
+        // 人物行走数据,通过startWalk更新
+        this.walkData = {
+            pathArr: [],
+            currentPoint: 0
+        }
+    }
+
+    updateAniTrans() {
+
+        // 实时更新角色模型动画
+        if(this.aniHourglass.upper > 0) {
+
+            // 10帧动画过渡
+            this.aniHourglass.upper = (this.aniHourglass.upper * 10 - 1) / 10
+            this.aniHourglass.lower = (this.aniHourglass.lower * 10 + 1) / 10
+
+            let fromAni = this.actionType.split("-")[0]
+            let toAni = this.actionType.split("-")[1]
+
+            this.animation[fromAni].setWeightForAllAnimatables(this.aniHourglass.upper);
+            this.animation[toAni].setWeightForAllAnimatables(this.aniHourglass.lower);
+
+        }
+    }
+
+    AniTransfromTo(aniName) {
+
+        let lastAniName = this.actionType.split("-")[1]
+        if(lastAniName == aniName) return
+
+        // 颠倒沙漏
+        this.aniHourglass = {
+            upper: 1,
+            lower: 0,
+        }
+
+        this.actionType = lastAniName + "-" + aniName
+    }
+
+    startWalk(pathArr, charactorManager) {
+
+        this.walkData = {
+            pathArr: pathArr,
+            currentPoint: 0
+        }
+        
+        if(pathArr.length >= 2 && this.actionType.split("-")[1] != "Walking") 
+        {    
+            let video = pathArr[1].video
+            if(video.isLoaded) {
+                this.AniTransfromTo("Walking")
+                this.walkByPath(charactorManager)
+            } else {
+                video.onloadeddata = () => {
+                    this.AniTransfromTo("Walking")
+                    this.walkByPath(charactorManager)
+                }
+            }
+        }
+    }
+    
+    walkByPath(charactorManager) {
+
+        let charactor = this
+
+        charactor.walkData.currentPoint++
+
+        // 更新房间的视频贴图
+        let video = charactor.walkData.pathArr[charactor.walkData.currentPoint].video
+        charactorManager.app.updateHouseVideo(video)
+
+
+        let newPos = charactor.walkData.pathArr[charactor.walkData.currentPoint].point
+
+        // 要跳转的位置与当前位置相同的话,就直接跳过,否则动画会出bug
+        if(newPos.x == this.mesh.position.x && newPos.z == this.mesh.position.z) 
+        {
+            console.warn("跳转点位与当前点位相同, 已跳过!")
+
+            if(charactor.walkData.pathArr[charactor.walkData.currentPoint+1]) {
+                charactor.walkByPath(charactorManager)
+            } else {
+                charactor.AniTransfromTo("Idle")
+                charactorManager.app.lockCamera(false)
+            }
+            
+            return
+        }
+
+
+        let startingPoint = charactor.mesh.position.clone();
+        startingPoint.y = newPos.y;
+        let walkDirc = newPos.subtract(startingPoint).normalize();
+
+
+        // 行走动画
+        const walkAni = new BABYLON.Animation("walk", "position", charactorManager.frameRate, 
+            BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE);
+
+        let walkFrameNum = charactorManager.frameRate * video.duration
+        const walkKeyFrames = [{
+            frame: 0,
+            value: charactor.mesh.position
+        },{
+            frame: walkFrameNum,
+            value: newPos
+        }]; 
+
+        walkAni.setKeys(walkKeyFrames);
+
+
+        // 转身动画
+        const newQuar = BABYLON.Quaternion.FromUnitVectorsToRef(new BABYLON.Vector3(0, 0, 1), walkDirc, new BABYLON.Quaternion())
+        const turnAroundAni = new BABYLON.Animation("turnAround", "rotationQuaternion", charactorManager.frameRate, 
+            BABYLON.Animation.ANIMATIONTYPE_QUATERNION, BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE);
+
+        let turnAroundFrameNum = charactorManager.frameRate * 0.2  // 0.2秒的帧数
+        const turnAroundFrames = [{
+            frame: 0,
+            value: charactor.mesh.rotationQuaternion
+        },{
+            frame: turnAroundFrameNum,
+            value: newQuar
+        }]; 
+
+        turnAroundAni.setKeys(turnAroundFrames);
+
+
+        charactorManager.app.scene.beginDirectAnimation(charactor.mesh, [walkAni, turnAroundAni], 0, Math.max(walkFrameNum, turnAroundFrameNum), false, 1, 
+            () => {
+                // 如果还有下一个点位就继续走,否则变为站立
+                if(charactor.walkData.pathArr[charactor.walkData.currentPoint+1]) {
+                    charactor.walkByPath(charactorManager)
+                } else {
+                    charactor.AniTransfromTo("Idle")
+                    charactorManager.app.lockCamera(false)
+                }
+            });
+    }
+}

+ 146 - 0
modules/CharactorManager.js

@@ -0,0 +1,146 @@
+import Charactor from "./Charactor.js";
+import common from "./common.js";
+import data from "./data.js";
+import settings from "./settings.js";
+
+export default class CharactorManager {
+
+    constructor(app) {
+
+        this.app = app
+
+        this.frameRate = settings.video.frameRate;
+
+        fetch("../textures/outputmp4/points.json", {
+            headers: {
+                'content-type': 'application/json'
+            },
+            method: 'GET',
+        })
+        .then(response => response.json())
+        .then(response => {
+            this.pointsData = response
+        })
+    }
+
+    importCharactorModel(modelPath, modelName) {
+
+        let self = this
+
+        BABYLON.SceneLoader.ImportMesh("", modelPath, modelName, this.app.scene, function (newMeshes, particleSystems, skeletons, animationGroups) {
+            
+            self.charactor = new Charactor(newMeshes, particleSystems, skeletons, animationGroups)
+
+            self.charactor.mesh.scaling.scaleInPlace(1.4);
+            
+            // 初始人物位置
+            self.charactor.mesh.position = new BABYLON.Vector3(
+                self.pointsData[0].position.x, 
+                0,
+                self.pointsData[0].position.y
+            )
+        });
+
+    }
+
+    clickHouse() {
+
+        var scene = this.app.scene
+        var pickinfo = scene.pick(scene.pointerX, scene.pointerY);
+
+        if(pickinfo.pickedPoint) {
+
+            this.charactorWalkTo(pickinfo.pickedPoint)
+
+            // // 在行走之前,首先要把人物矫正到45度的倍数(有视频的8个方向)
+
+            // if(this.charactor.actionType.split("-")[1] == "Walking") 
+            // {
+            //     // 如果是行走时改方向的话,相机保持之前已校正的度数,所以不用再改
+            //     this.charactorWalkTo(pickinfo.pickedPoint)
+            // } 
+            // else {
+
+            //     let cameraAlphaAmend = parseInt(this.app.camera.alpha / (Math.PI / 2)) * (Math.PI / 2)
+            //     let sendData = {
+            //         type: "CameraRotate",
+            //         point: this.charactor.mesh.position,
+            //         dirc: {
+            //             from: this.app.camera.alpha % (Math.PI * 2),
+            //             to: cameraAlphaAmend % (Math.PI * 2)
+            //         }
+            //     }
+                
+            //     // todo 发送数据
+            //     // common.postData("", sendData).then(response => {
+
+            //         let video = response[0].video
+
+            //         this.app.updateHouseVideo(video)
+
+            //         let rotateAni = new BABYLON.Animation("rotate", "alpha", this.frameRate, 
+            //             BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE);
+
+            //         const rotateFrameNum = this.frameRate * video.duration
+            //         const rotateKeyFrames = [{
+            //             frame: 0,
+            //             value: this.app.camera.alpha
+            //         },{
+            //             frame: rotateFrameNum,
+            //             value: cameraAlphaAmend
+            //         }]; 
+
+            //         rotateAni.setKeys(rotateKeyFrames);
+
+            //         scene.beginDirectAnimation(this.app.camera, [rotateAni], 0, rotateFrameNum, false, 1, () => {
+            //             // 旋转完后开始行走
+            //             this.charactorWalkTo(pickinfo.pickedPoint)
+            //         })
+
+            //     // })
+            // }
+        }
+    }
+
+    onBeforeAnimation() {
+
+        this.charactor.updateAniTrans()
+
+    }
+
+    charactorWalkTo(endPoint) {
+
+        let currentPath = this.charactor.walkData.pathArr[this.charactor.walkData.currentPoint]
+        let startPoint = (currentPath && currentPath.point) || this.charactor.mesh.position
+        let sendData = {
+            type: "Walk",
+            point: {
+                from: { x: startPoint.x, y: startPoint.z },
+                to: { x: endPoint.x, y: endPoint.z }
+            },
+            dirc: this.app.camera.alpha % (Math.PI * 2)
+        }
+
+        // todo 发送数据
+        // common.postData("", sendData).then(response => {
+
+            let path = data.walkPath    // response
+            path.forEach(data => data.point = this.pointsData[data.id].position)
+
+            path = path.map(data => { 
+                return { 
+                    point: new BABYLON.Vector3(data.point.x, 0, data.point.y), 
+                    video: data.video && common.createVideoElement(data.video) 
+                } 
+            })
+
+            // 行走时锁定camera
+            this.app.lockCamera(true)
+
+            this.charactor.startWalk(path, this)
+
+        // })
+
+    }
+
+}

+ 43 - 0
modules/common.js

@@ -0,0 +1,43 @@
+export default {
+    
+    postData: (url, data) => {
+        return fetch(url, {
+            body: JSON.stringify(data),
+            // cache: 'no-cache',
+            // credentials: 'same-origin',
+            headers: {
+                // 'user-agent': 'Mozilla/4.0 MDN Example',
+                'content-type': 'application/json'
+            },
+            method: 'POST',
+            // mode: 'cors',
+            // redirect: 'follow',
+            // referrer: 'no-referrer',
+        })
+        .then(response => response.json())
+    },
+
+    createVideoElement: (path) => {
+
+        let videoName = path.split("/")[path.split("/").length-1].split(".")[0]
+        let oldVideo = document.getElementById(videoName)
+        if(oldVideo) {
+            oldVideo.currentTime = 0
+            oldVideo.isLoaded = true
+            return oldVideo
+        }
+
+        let video = document.createElement("video")
+        video.src = path
+        video.id = videoName
+        video.crossOrigin = "anonymous" 
+        video.playsinline = "playsinline" 
+        video.autoplay = "autoplay" 
+        video.muted = "muted"
+
+        document.getElementById("videoTextureBox").appendChild(video)
+        
+        return video
+    }
+
+}

+ 31 - 0
modules/data.js

@@ -0,0 +1,31 @@
+export default {
+
+    walkPath: [
+        {
+            // point: { x: -0.5, y: 0 },
+            id: 0,
+            video: null
+        },
+        {
+            // point: { x: -0.5, y: 0.1 },
+            id: 1,
+            video: "../textures/outputmp4/0/0_1_0.mp4"
+        },
+        {
+            // point: { x: -0.5, y: 2 },
+            id: 2,
+            video: "../textures/outputmp4/1/1_2_0.mp4"
+        },
+        {
+            // point: { x: 1.5, y: 2 },
+            id: 5,
+            video: "../textures/outputmp4/2/2_5_0.mp4"
+        },
+        {
+            // point: { x: 3.5, y: 2 },
+            id: 8,
+            video: "../textures/outputmp4/5/5_8_0.mp4"
+        }
+    ]
+
+}

+ 178 - 0
modules/houseShader copy.js

@@ -0,0 +1,178 @@
+export default {
+    
+    attributes: ['uv', 'position', 'world0', 'world1', 'world2', 'world3'],
+
+    uniforms: ['view', 'projection', 'worldViewProjection', 'world', 
+        'fireworkLight', 'fireworkLightPosition', 
+        'haveShadowLight', 'lightSpaceMatrix', 'shadowSampler', 
+        'isYUV', 'focal_width_height', 'texture_video'],
+
+    defines: ["#define SHADOWFULLFLOAT", "#define NUM_BONE_INFLUENCERS 0", "#define NUM_MORPH_INFLUENCERS 0"],
+
+    samplers: ['shadowSampler', 'texture_video'],
+
+    vertex: `
+
+        precision highp float;
+
+        varying vec3 ModelPos;
+        
+        varying vec4 vPositionFromLight;
+        varying float fireworkDistance;
+        varying float fireworkCosTheta;
+        
+        attribute vec2 uv;
+        attribute vec3 position;
+        
+        attribute vec4 world0;
+        attribute vec4 world1;
+        attribute vec4 world2;
+        attribute vec4 world3;
+        
+        #ifdef NORMAL
+        attribute vec3 normal;
+        #endif
+        
+        
+        
+        uniform vec3 fireworkLightPosition;
+        uniform mat4 view;
+        uniform mat4 projection;
+        uniform mat4 lightSpaceMatrix;
+        
+        uniform mat4 world;
+        uniform mat4 worldViewProjection;
+
+
+        float DistanceCalculation(vec3 Q, vec3 P)
+        {
+            return (Q.x - P.x) * (Q.x - P.x) + (Q.y - P.y) * (Q.y - P.y) + (Q.z - P.z) * (Q.z - P.z);
+        }
+        float CosThetaCalculation(vec3 Q, vec3 P)
+        {
+            return max(0.,dot(Q, P));
+        }
+        void main()
+        {
+            #include<instancesVertex>
+        
+            vPositionFromLight =  lightSpaceMatrix * finalWorld * vec4(position, 1.0);
+            
+            fireworkDistance = distance(vec3(finalWorld * vec4(position, 1.0)), fireworkLightPosition);
+            
+            fireworkCosTheta = 1.0;
+            
+            #ifdef NORMAL
+            vec3 directionFirework = fireworkLightPosition.xyz - vec3(finalWorld * vec4(position, 1.0));
+            directionFirework = normalize(directionFirework);
+            fireworkCosTheta = CosThetaCalculation(directionFirework, normal);
+            #endif
+            
+            ModelPos = vec3( view * finalWorld * vec4(position , 1.0));
+            gl_Position = projection * view * finalWorld * vec4(position , 1.0); 
+        }
+
+    `,
+
+    fragment: `
+
+        precision highp  float;
+
+        varying vec3 ModelPos;
+        
+        uniform float isYUV;  // false: 0, true: 1.0
+        uniform sampler2D texture_video;
+        
+        uniform float haveShadowLight;
+        varying vec4 vPositionFromLight;
+        uniform float fireworkLight;
+        varying float fireworkDistance;
+        varying float fireworkCosTheta;
+        
+        uniform sampler2D shadowSampler;
+        
+        uniform vec3 focal_width_height;
+        
+        const float inv_2_PI = 0.1591549; // 1 / (2 * pi)
+        const float inv_PI =   0.3183099; // 1 / (  pi)
+        const vec2 invAtan = vec2(0.1591549, 0.3183099);
+        
+        float unpack(vec4 color)
+        {
+            const vec4 bit_shift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
+            return dot(color, bit_shift);
+        }
+        
+        float ShadowCalculation(vec4 vPositionFromLight, sampler2D ShadowMap)
+        {
+            vec3 projCoords = vPositionFromLight.xyz / vPositionFromLight.w;
+            vec3 depth = 0.5 * projCoords + vec3(0.5);
+            vec2 uv = depth.xy;
+            if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)
+            {
+                return 1.0;
+            }
+            #ifndef SHADOWFULLFLOAT
+                float shadow = unpack(texture2D(ShadowMap, uv));
+            #else
+                float shadow = texture2D(ShadowMap, uv).x;
+            #endif
+        
+            if (depth.z > shadow - 1e-4)
+            {
+                return 0.7;
+            }
+            else
+            {
+                return 1.0;
+            }
+        }
+        
+        vec2 SampleTex(vec3 pt3d)
+        {
+            return focal_width_height.x / focal_width_height.yz *pt3d.xy/pt3d.z + 0.5;
+        }
+        
+        void main()
+        {    
+            vec3 yuv;
+            vec3 rgb;
+            vec2 uv;
+            vec3 color = vec3(0,0,0);
+            vec3 flash_color = fireworkLight * 1000.0 / fireworkDistance * fireworkCosTheta * vec3(1,0,0); 
+            float shadow = 1.0;
+            if (haveShadowLight > 0.5)
+            {
+                shadow = ShadowCalculation(vPositionFromLight, shadowSampler);
+            }
+        
+            uv =  SampleTex( normalize(ModelPos) );
+            if( isYUV < 0.5 )
+            {
+                color = texture2D(texture_video, uv).rgb;
+            }else{
+                const mat4 YUV2RGB = mat4
+                (
+                    1.1643828125, 0, 1.59602734375, -.87078515625,
+                    1.1643828125, -.39176171875, -.81296875, .52959375,
+                    1.1643828125, 2.017234375, 0, -1.081390625,
+                    0, 0, 0, 1
+                );    
+                
+                vec4 result = vec4( 
+                    texture2D( texture_video, vec2( uv.x,   uv.y * 0.666666 + 0.333333 ) ).x,
+                    texture2D( texture_video, vec2( uv.x * 0.5,        uv.y * 0.333333 ) ).x, 
+                    texture2D( texture_video, vec2( 0.5 + uv.x * 0.5,  uv.y * 0.333333 ) ).x,
+                    1
+                ) * YUV2RGB;  
+
+                color = clamp(result.rgb, 0.0, 1.0); 
+            }
+            if( uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 )
+            {
+                color = vec3(0,0,0);
+            }
+            gl_FragColor = vec4(shadow * (color + flash_color) * 1.0, 1.0); 
+        }
+    `
+}

+ 88 - 0
modules/houseShader.js

@@ -0,0 +1,88 @@
+export default {
+    
+    attributes: ['uv', 'position'],
+
+    uniforms: ['view', 'projection', 'worldViewProjection', 'world', 
+        'isYUV', 'focal_width_height', 'texture_video'],
+
+    defines: ["#define SHADOWFULLFLOAT", "#define NUM_BONE_INFLUENCERS 0", "#define NUM_MORPH_INFLUENCERS 0"],
+
+    samplers: ['shadowSampler', 'texture_video'],
+
+    vertex: `
+
+        precision highp float;
+
+        varying vec3 ModelPos;
+        
+        attribute vec2 uv;
+        attribute vec3 position;
+        
+        uniform mat4 view;
+        uniform mat4 projection;
+        
+        uniform mat4 world;
+        uniform mat4 worldViewProjection;
+
+        void main()
+        {
+            #include<instancesVertex>
+        
+            ModelPos = vec3( view * finalWorld * vec4(position , 1.0));
+            gl_Position = projection * view * finalWorld * vec4(position , 1.0); 
+        }
+
+    `,
+
+    fragment: `
+
+        precision highp  float;
+
+        varying vec3 ModelPos;
+        
+        uniform float isYUV;  // false: 0, true: 1.0
+        uniform sampler2D texture_video;
+        
+        uniform vec3 focal_width_height;
+        
+        vec2 SampleTex(vec3 pt3d)
+        {
+            return focal_width_height.x / focal_width_height.yz * pt3d.xy / pt3d.z + 0.5;
+        }
+        
+        void main()
+        {
+            vec3 color = vec3(0,0,0);
+            float shadow = 1.0;
+            
+            vec2 uv =  SampleTex( normalize(ModelPos) );
+
+            if( isYUV < 0.5 )
+            {
+                color = texture2D(texture_video, uv).rgb;
+            }else{
+                const mat4 YUV2RGB = mat4
+                (
+                    1.1643828125, 0, 1.59602734375, -.87078515625,
+                    1.1643828125, -.39176171875, -.81296875, .52959375,
+                    1.1643828125, 2.017234375, 0, -1.081390625,
+                    0, 0, 0, 1
+                );    
+                
+                vec4 result = vec4( 
+                    texture2D( texture_video, vec2( uv.x,   uv.y * 0.666666 + 0.333333 ) ).x,
+                    texture2D( texture_video, vec2( uv.x * 0.5,        uv.y * 0.333333 ) ).x, 
+                    texture2D( texture_video, vec2( 0.5 + uv.x * 0.5,  uv.y * 0.333333 ) ).x,
+                    1
+                ) * YUV2RGB;  
+
+                color = clamp(result.rgb, 0.0, 1.0); 
+            }
+            if( uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0 )
+            {
+                color = vec3(0,0,0);
+            }
+            gl_FragColor = vec4(shadow * color * 1.0, 1.0); 
+        }
+    `
+}

+ 286 - 0
modules/index.js

@@ -0,0 +1,286 @@
+import CharactorManager from "./CharactorManager.js";
+import common from "./common.js";
+import houseShader from "./houseShader.js";
+import settings from "./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 = Math.PI / 2
+        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.wheelDeltaPercentage = 0.01;
+        camera1.angularSensibilityX /= 2;
+        // camera1.targetScreenOffset = new BABYLON.Vector2(0, -1.65)
+        this.camera = camera1
+        this.lastCameraAlpha = 0
+        // camera1.checkCollisions = true;
+        // camera1.collisionRadius = new BABYLON.Vector3(0.1, 0.1, 0.1)
+        
+        // let self = this
+        // var ray = new BABYLON.Ray(new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(0,0,1), 50);
+        // BABYLON.RayHelper.CreateAndShow(ray, scene, new BABYLON.Color3(1, 0.1, 0.1));
+        // camera1.onCollide = function(mesh) {
+        //     let targetPos = camera1.position.clone()
+        //     targetPos.y = 1
+        //     ray.origin = self.charactorManager.charactor.mesh.position
+        //     ray.direction = BABYLON.Vector3.Normalize( targetPos.subtract(ray.origin) )
+        //     var info = ray.intersectsMesh(mesh);
+        //     if(!info.distance) return
+        //     camera1.lowerRadiusLimit = Math.max(info.distance - 0.5, 0.2)
+        //     camera1.upperRadiusLimit = Math.max(info.distance - 0.5, 0.2)
+        //     console.error(info.distance)
+        // }
+        // console.error(camera1)
+
+        
+        var ray = new BABYLON.Ray(new BABYLON.Vector3(0,0,0), new BABYLON.Vector3(0,0,1), 50);
+        BABYLON.RayHelper.CreateAndShow(ray, scene, new BABYLON.Color3(1, 0.1, 0.1));
+        this.ray = ray
+    
+        // 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)
+
+            newMeshes.forEach(m => {
+                // m.scaling.scaleInPlace(1.4);
+                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("", document.getElementById("houseTexture0"), scene)
+                    // document.getElementById("houseTexture0").play()
+                    // document.getElementById("houseTexture0").loop = "loop"
+
+                    shaderMaterial.setTexture("texture_video", videoTexture)
+                    shaderMaterial.setVector3("focal_width_height", new BABYLON.Vector3(735, settings.video.width, settings.video.height))   // 1100 470 860
+                    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.pointDown = true
+                    break;
+
+                case BABYLON.PointerEventTypes.POINTERUP:
+                    this.pointDown = false
+                    break;
+
+                case BABYLON.PointerEventTypes.POINTERMOVE:
+                    if(this.pointDown) this.rotateCamera()
+                    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 = 1.5
+        this.camera.target = 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.5
+        if(info.distance > settings.camera.distanceFromCharactor + offset) return
+        this.camera.lowerRadiusLimit = Math.max( info.distance - offset, 0.1 )
+        this.camera.upperRadiusLimit = Math.max( info.distance - offset, 0.1 )
+        // console.error(info.pickedPoint, this.camera.position)
+
+        // let r = this.cameraCollision()
+        // this.camera.lowerRadiusLimit = Math.max( r, 0.1 )
+        // this.camera.upperRadiusLimit = Math.max( r, 0.1 )
+
+    }
+
+    rotateCamera() {
+
+        if(!this.charactorManager || !this.charactorManager.charactor) return
+
+        let charactor = this.charactorManager.charactor
+
+        if(charactor.actionType.split("-")[1] == "Walking") {
+            // 行走时旋转相机,行走停止
+            charactor.startWalk([], this.charactorManager)
+        } 
+        // 根据单个点位视频的总帧数,计算每一帧的度数,只在度数至少到达一帧时,才去请求视频
+        else if(Math.abs(this.camera.alpha - this.lastCameraAlpha) >= Math.PI * 2 / settings.rotateVideoFrame)
+        {
+            this.lockCamera(true)
+            let currentPath = charactor.walkData.pathArr[charactor.walkData.currentPoint]
+            let startPoint = (currentPath && currentPath.point) || charactor.mesh.position
+            let sendData = {
+                type: "RotateCamera",
+                point: { x: startPoint.x, y: startPoint.z },
+                dirc: {
+                    from: this.lastCameraAlpha % (Math.PI * 2),
+                    to: this.camera.alpha % (Math.PI * 2)
+                }
+            }
+
+            setTimeout(() => {
+                // this.lastCameraAlpha = this.camera.alpha
+                this.lockCamera(false)
+            }, 10)
+
+            // todo 发送数据
+            // common.postData("", sendData).then(response => {
+            
+            //     this.updateHouseVideo(response[0].video)
+
+            //     // this.lastCameraAlpha = this.camera.alpha
+            //     this.lockCamera(false)
+
+            // })
+
+            this.lastCameraAlpha = this.camera.alpha
+        }
+
+    }
+
+    lockCamera(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()
+    }
+
+    // cameraCollision() {
+    //     // if(this.cameraAngle) {
+    //     //     let {info, point, normal} = this.getRayInfoOnCircle(this.cameraAngle)
+    //     //     if( 
+    //     //         Math.abs(info.distance - point.clone().subtract(this.ray.origin).length()) < 1 &&
+    //     //         this.ray.origin.clone().subtract(info.pickedPoint).addInPlace(normal).length() <= 3.5 
+    //     //     ) {
+    //     //         return this.ray.origin.clone().subtract(info.pickedPoint).addInPlace(normal).length()
+    //     //     } 
+    //     //     else {
+    //     //         this.cameraAngle = null
+    //     //         return 3.5
+    //     //     }
+    //     // }
+
+    //     for(let i = 0; i < Math.PI * 2; i += Math.PI / 8) {
+    //         let {info, point, normal} = this.getRayInfoOnCircle(i)
+    //         if( 
+    //             Math.abs(info.distance - point.clone().subtract(this.ray.origin).length()) < 0.5 &&
+    //             (this.ray.origin.clone().subtract(info.pickedPoint).addInPlace(normal).length() - 3.5) < 0.5  
+    //         ) {
+    //             // this.cameraAngle = i
+    //             return this.ray.origin.clone().subtract(info.pickedPoint).addInPlace(normal).length()
+    //         }
+    //     }
+    //     // this.cameraAngle = null
+    //     return 3.5
+    // }
+
+    // getRayInfoOnCircle(angle) {
+    //     let normal = new BABYLON.Vector3(0, 0, 0.5)
+    //     normal.rotateByQuaternionToRef(BABYLON.Quaternion.RotationAxis(new BABYLON.Vector3(0, 1, 0), angle), normal)
+    //     let point = this.camera.position.clone().addInPlace(normal)    // 圆上的点坐标
+    //     let direction = BABYLON.Vector3.Normalize( point.clone().subtract(this.ray.origin) )
+    //     var ray = new BABYLON.Ray(this.ray.origin, direction, 50);
+    //     let info = ray.intersectsMeshes(this.house)[0];
+    //     return { info: info, point: point, normal: normal }
+    // }
+}

+ 14 - 0
modules/settings.js

@@ -0,0 +1,14 @@
+export default {
+    video: {
+        frameRate: 30,  // 帧/s
+        width: 1728, //1920,
+        height: 720, //1080
+    },
+    rotateVideoFrame: 60,   // 帧
+    walkVidoeSpeed: 1,   // m/s
+    camera: {
+        fov: Math.PI / 2,
+        height: 1.5,
+        distanceFromCharactor: 3.5
+    }
+}

BIN
scenes/charactors/man_YXL.glb


BIN
scenes/house/000.glb


BIN
scenes/house/YQT_Low_xverse.glb


BIN
textures/123.mp4


BIN
textures/environment.env


BIN
textures/op/0/0.mp4


BIN
textures/op/0/0_1_0.mp4


BIN
textures/op/0/0_1_1.mp4


BIN
textures/op/0/0_1_2.mp4


BIN
textures/op/0/0_1_3.mp4


BIN
textures/op/0/0_1_4.mp4


BIN
textures/op/0/0_1_5.mp4


BIN
textures/op/0/0_1_6.mp4


BIN
textures/op/0/0_1_7.mp4


BIN
textures/op/1/1.mp4


BIN
textures/op/1/1_0_0.mp4


BIN
textures/op/1/1_0_1.mp4


BIN
textures/op/1/1_0_2.mp4


BIN
textures/op/1/1_0_3.mp4


BIN
textures/op/1/1_0_4.mp4


BIN
textures/op/1/1_0_5.mp4


BIN
textures/op/1/1_0_6.mp4


BIN
textures/op/1/1_0_7.mp4


+ 135 - 0
textures/op/points.json

@@ -0,0 +1,135 @@
+[
+    {
+        "id": 0,
+        "position": {
+            "x": -0.5,
+            "y": 0,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [1,3,4]
+    },
+    {
+        "id": 1,
+        "position": {
+            "x": -0.5,
+            "y": 0.1,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [0,2,3,4,5]
+    },
+    {
+        "id": 2,
+        "position": {
+            "x": -0.5,
+            "y": 2,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [1,4,5]
+    },
+    {
+        "id": 3,
+        "position": {
+            "x": 1.5,
+            "y": 0,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [0,1,4,6,7]
+    },
+    {
+        "id": 4,
+        "position": {
+            "x": 1.5,
+            "y": 0.1,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 1.5,
+            "y": 0,
+            "z": 1.5
+        },
+        "contact": [0,1,2,3,4,5,6,7,8]
+    },
+    {
+        "id": 5,
+        "position": {
+            "x": 1.5,
+            "y": 2,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 1.5,
+            "y": 0,
+            "z": 1.5
+        },
+        "contact": [1,2,4,7,8]
+    },
+    {
+        "id": 6,
+        "position": {
+            "x": 3.5,
+            "y": 0,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [3,4,7]
+    },
+    {
+        "id": 7,
+        "position": {
+            "x": 3.5,
+            "y": 0.1,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [3,4,5,6,8]
+    },
+    {
+        "id": 8,
+        "position": {
+            "x": 3.5,
+            "y": 2.0,
+            "z": 1.5
+        },
+        "rotation": {
+            "x": 0,
+            "y": 0,
+            "z": 0,
+            "w": 1
+        },
+        "contact": [4,5,7]
+    }
+]

BIN
textures/outputmp4/0/0.mp4


BIN
textures/outputmp4/0/0_1_0.mp4


BIN
textures/outputmp4/0/0_1_1.mp4


BIN
textures/outputmp4/0/0_1_2.mp4


BIN
textures/outputmp4/0/0_1_3.mp4


BIN
textures/outputmp4/0/0_1_4.mp4


BIN
textures/outputmp4/0/0_1_5.mp4


BIN
textures/outputmp4/0/0_1_6.mp4


BIN
textures/outputmp4/0/0_1_7.mp4


BIN
textures/outputmp4/0/0_3_0.mp4


BIN
textures/outputmp4/0/0_3_1.mp4


BIN
textures/outputmp4/0/0_3_2.mp4


BIN
textures/outputmp4/0/0_3_3.mp4


BIN
textures/outputmp4/0/0_3_4.mp4


BIN
textures/outputmp4/0/0_3_5.mp4


BIN
textures/outputmp4/0/0_3_6.mp4


BIN
textures/outputmp4/0/0_3_7.mp4


BIN
textures/outputmp4/0/0_4_0.mp4


BIN
textures/outputmp4/0/0_4_1.mp4


BIN
textures/outputmp4/0/0_4_2.mp4


BIN
textures/outputmp4/0/0_4_3.mp4


BIN
textures/outputmp4/0/0_4_4.mp4


BIN
textures/outputmp4/0/0_4_5.mp4


BIN
textures/outputmp4/0/0_4_6.mp4


BIN
textures/outputmp4/0/0_4_7.mp4


BIN
textures/outputmp4/1/1.mp4


BIN
textures/outputmp4/1/1_0_0.mp4


BIN
textures/outputmp4/1/1_0_1.mp4


BIN
textures/outputmp4/1/1_0_2.mp4


BIN
textures/outputmp4/1/1_0_3.mp4


BIN
textures/outputmp4/1/1_0_4.mp4


BIN
textures/outputmp4/1/1_0_5.mp4


BIN
textures/outputmp4/1/1_0_6.mp4


BIN
textures/outputmp4/1/1_0_7.mp4


BIN
textures/outputmp4/1/1_2_0.mp4


BIN
textures/outputmp4/1/1_2_1.mp4


BIN
textures/outputmp4/1/1_2_2.mp4


BIN
textures/outputmp4/1/1_2_3.mp4


BIN
textures/outputmp4/1/1_2_4.mp4


BIN
textures/outputmp4/1/1_2_5.mp4


BIN
textures/outputmp4/1/1_2_6.mp4


BIN
textures/outputmp4/1/1_2_7.mp4


BIN
textures/outputmp4/1/1_3_0.mp4


BIN
textures/outputmp4/1/1_3_1.mp4


+ 0 - 0
textures/outputmp4/1/1_3_2.mp4


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików