├── .babelrc ├── .jshintrc ├── .npmignore ├── LICENSE ├── README.md ├── demo ├── demo.gif ├── demo.js └── mesh │ ├── level.babylon │ ├── level.babylon.manifest │ ├── level.blend │ ├── level.log │ ├── level.mtl │ └── level.obj ├── dist ├── babylon-navigation-mesh.js └── babylon-navigation-mesh.min.js ├── index.html ├── lib-es5 ├── Astar.js ├── BinaryHeap.js ├── Channel.js └── Navigation.js ├── lib ├── Astar.js ├── BinaryHeap.js ├── Channel.js └── Navigation.js └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "futurehostile": true, 3 | "freeze": true, 4 | "latedef": false, 5 | "noarg": true, 6 | "nocomma": true, 7 | "nonbsp": true, 8 | "nonew": true, 9 | "undef": true, 10 | "browser": true, 11 | "browserify": true, 12 | "mocha": true, 13 | "shadow": false, 14 | "esnext": true, 15 | "loopfunc": true, 16 | "newcap": false, 17 | "globals": { 18 | "console": false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | demo 2 | article.md 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 WANADEV 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Babylon-navigation-mesh 2 | 3 | [![NPM version](https://badge.fury.io/js/babylon-navigation-mesh.png)](http://badge.fury.io/js/babylon-navigation-mesh) 4 | 5 | [Demo](http://wanadev.github.io/babylon-navigation-mesh/) 6 | 7 | A toolkit to navigate on a mesh with BABYLON.js. Largely inspired by [PatrolJS](https://github.com/nickjanssen/PatrolJS) for ThreeJS. 8 | 9 | Babylon-navigation-mesh is a path finder for AI agents. It use the A star and Funnel algorithms to calculate a path on a navigation mesh. 10 | 11 | ## Usage 12 | 13 | Add the npm package [babylon-navigation-mesh](https://www.npmjs.com/package/babylon-navigation-mesh) to your project: 14 | 15 | npm install babylon-navigation-mesh --save 16 | 17 | or clone: 18 | 19 | git clone git@github.com:wanadev/babylon-navigation-mesh.git 20 | npm install 21 | npm run build 22 | 23 | then 24 | 25 | var Navigation = require("babylon-navigation-mesh"); 26 | 27 | And create your object and the associated graph: 28 | 29 | ```javascript 30 | var navigation = new Navigation(); 31 | var scene = engine.scene; 32 | 33 | var navmesh = scene.getMeshByName("Navmesh"); 34 | var zoneNodes = navigation.buildNodes(navmesh); 35 | navigation.setZoneData('scene', zoneNodes); 36 | ``` 37 | 38 | To calculate the path : 39 | 40 | ```javascript 41 | var zone = navigation.getGroup('scene', agentPosition); 42 | var path = navigation.findPath(agentPosition, dest, 'scene', zone); 43 | ``` 44 | And to project a position on the navmesh: 45 | 46 | ```javascript 47 | var newPosition = navigation.projectOnNavmesh(this.position, 'scene', navigation.getGroup('level', this.position)); 48 | ``` 49 | 50 | An article is available to create and use a navigation mesh [here](https://www.wanadev.fr/43-tuto-creer-et-utiliser-un-maillage-de-navigation-avec-babylon-js/) (french) 51 | 52 | ## Demo 53 | 54 | ![](https://github.com/wanadev/babylon-navigation-mesh/blob/master/demo/demo.gif?raw=true) 55 | -------------------------------------------------------------------------------- /demo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanadev/babylon-navigation-mesh/f050149c8c44fb25051f1c39279d2c8722d2f141/demo/demo.gif -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // get the canvas DOM element 4 | var canvas = document.getElementById('renderCanvas'); 5 | 6 | // load the 3D engine 7 | var engine = new BABYLON.Engine(canvas, true); 8 | 9 | var navigation = new Navigation(); 10 | var line = null; 11 | 12 | var loadScene = function() { 13 | //load babylon scene of museum 14 | var onLoaded = function(loadedScene) { 15 | var navmesh = scene.getMeshByName("Navmesh"); 16 | navmesh.material = new BABYLON.StandardMaterial("navMaterial", scene); 17 | navmesh.material.diffuseColor = new BABYLON.Color3(0, 1, 0); 18 | navmesh.material.alpha = 0.5; 19 | navmesh.material.wireframe = true; 20 | for (var i = 0; i < scene.meshes.length; i++) { 21 | scene.meshes[i].convertToFlatShadedMesh(); 22 | } 23 | 24 | var zoneNodes = navigation.buildNodes(navmesh); 25 | navigation.setZoneData('level', zoneNodes); 26 | }; 27 | 28 | BABYLON.SceneLoader.Append("./demo/mesh/", "level.babylon", scene, onLoaded.bind(this)); 29 | }; 30 | 31 | // Set the basics 32 | var scene = new BABYLON.Scene(engine); 33 | var camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1, 0.8, 10, new BABYLON.Vector3(0, 0, 0), scene); 34 | camera.setTarget(BABYLON.Vector3.Zero()); 35 | camera.attachControl(canvas, false); 36 | var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene); 37 | light.intensity = 0.5; 38 | 39 | // Create a minimoi 40 | var minimoi = BABYLON.Mesh.CreateBox("Me", 0.2, scene); 41 | minimoi.material = new BABYLON.StandardMaterial("navMaterial", scene); 42 | minimoi.material.diffuseColor = new BABYLON.Color3(1., 0., 0); 43 | minimoi.position = new BABYLON.Vector3( -3.7426157086231813, 0.32968033243017736, -5.410392414960055); 44 | loadScene(); 45 | 46 | // run the render loop 47 | engine.runRenderLoop(function(){ 48 | scene.render(); 49 | }); 50 | 51 | // the canvas/window resize event handler 52 | window.addEventListener('resize', function(){ 53 | engine.resize(); 54 | }); 55 | 56 | canvas.addEventListener('click', function(event) { 57 | var pickingInfo = scene.pick(scene.pointerX, scene.pointerY); 58 | if (!pickingInfo.hit) return; 59 | 60 | var path = navigation.findPath(minimoi.position, pickingInfo.pickedPoint, 'level', navigation.getGroup('level', minimoi.position)) || []; 61 | if (path && path.length > 0) { 62 | var length = 0; 63 | var direction = [{ 64 | frame: 0, 65 | value: minimoi.position 66 | }]; 67 | for (var i = 0; i < path.length; i++) { 68 | var vector = new BABYLON.Vector3(path[i].x, path[i].y, path[i].z); 69 | length += BABYLON.Vector3.Distance(direction[i].value, vector); 70 | direction.push({ 71 | frame: length*100, 72 | value: vector 73 | }); 74 | } 75 | 76 | for (var i = 0; i < direction.length; i++) { 77 | direction[i].frame /= length; 78 | } 79 | 80 | var moveCamera = new BABYLON.Animation("CameraMove", "position", 180/length+10, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT); 81 | moveCamera.setKeys(direction); 82 | minimoi.animations.push(moveCamera); 83 | 84 | if (line) line.dispose(); 85 | line = BABYLON.Mesh.CreateLines("lines", [minimoi.position].concat(path), scene); 86 | line.color = new BABYLON.Color3(1, 0, 0); 87 | line.position.y = 0.001; 88 | 89 | scene.beginAnimation(minimoi, 0, 100); 90 | } 91 | 92 | }); 93 | -------------------------------------------------------------------------------- /demo/mesh/level.babylon: -------------------------------------------------------------------------------- 1 | {"autoClear":true,"clearColor":[0.0509,0.0509,0.0509],"ambientColor":[0,0,0],"gravity":[0,-9.81,0], 2 | "materials":[{"name":"level.Default","id":"level.Default","ambient":[0.8,0.8,0.8],"diffuse":[0.64,0.64,0.64],"specular":[0.5,0.5,0.5],"emissive":[0,0,0],"specularPower":50,"alpha":1,"backFaceCulling":true},{"name":"level.Material","id":"level.Material","ambient":[0.8,0.8,0.8],"diffuse":[0.64,0.64,0.64],"specular":[0.5,0.5,0.5],"emissive":[0,0,0],"specularPower":50,"alpha":1,"backFaceCulling":true}], 3 | "multiMaterials":[], 4 | "skeletons":[], 5 | "meshes":[{"name":"Navmesh","id":"Navmesh","billboardMode":0,"position":[0,0,0],"rotation":[0,0,0],"scaling":[1,1,1],"isVisible":true,"isEnabled":true,"useFlatShading":false,"checkCollisions":false,"receiveShadows":false 6 | ,"positions":[-4.4779,0.3,-4.0106,-4.6779,0.3,-4.0106,-4.3779,0.6,-4.2106,1.0221,0.3,-4.6106,0.3221,0.3,-4.2106,0.8221,0.3,-4.7106,1.5221,0.3,-4.9106,0.9221,0.3,-5.0106,1.6221,0.3,-5.3106,-4.2779,1.1,-3.5106,-4.1779,0.6,-4.1106,-3.3779,1.1,-3.3106,-3.1779,0.6,-4.1106,-0.1779,0.3,-4.6106,-2.9779,0.3,-3.8106,-2.7779,0.4,-3.9773,-1.9779,0.3,-3.7106,-2.7445,0.4,-4.244,-1.8779,0.3,-4.5106,-2.7779,0.4,-3.9773,-2.6779,0.4,-4.3106,-1.6779,0.3,-4.7106,-2.7445,0.4,-4.244,-4.3779,0.4,-4.8106,-4.3779,0.3,-6.0106,-3.4779,0.4,-4.5856,-4.3779,0.4,-4.8106,-2.6779,0.4,-4.3106,-3.4779,0.4,-4.5856,4.0221,3.2,-4.8106,4.0221,3.2,-4.4106,3.4221,3.2,-4.9106,3.3221,3.2,-4.4106,3.5721,3.3,-4.3106,3.1221,3.2,-4.2106,1.3221,2.4,-5.5106,1.3221,2.3,-5.9106,2.1221,2.5,-5.4106,4.0221,2.7,-5.1106,3.1221,2.6,-5.1106,4.0221,2.7,-5.5106,1.1221,2.5,-4.5106,1.9221,2.6,-4.4106,0.9221,2.7,-3.5106,3.5721,3.3,-4.3106,3.8888,3,-3.844,3.6221,3.1,-2.7106,1.0221,2.8,-3.0106,2.9221,2.8,-4.3106,2.1221,2.6,-4.7106,1.6221,0.3,-1.3106,1.5221,0.3,-1.1106,1.1221,0.3,-1.3106,1.1221,0.3,-1.0106,0.9221,0.3,-1.5106,-0.8779,0.3,-1.6106,-0.5779,0.3,-1.5106,-0.9779,0.3,-1.1106,2.0221,0.3,-4.8106,3.7221,0.3,-4.7106,3.0221,0.3,-4.6106,3.8221,0.3,-5.0106,-0.0779,0.3,-2.4106,0.2221,0.3,-2.9106,0.0221,0.3,-2.0106,3.2221,0.3,-4.2106,3.0221,0.3,-3.3106,3.0221,0.3,-4.3106,1.1221,0.3,-3.3106,1.3221,0.3,-3.5106,1.9221,0.3,-4.4106,4.3221,0.3,-4.5106,2.2221,0.3,0.4894,2.0221,0.3,0.3894,2.8221,0.3,0.4894,2.3221,0.3,-1.2106,2.9221,0.3,-1.1106,2.3221,0.3,-0.5106,2.0221,0.3,-0.4106,3.0221,0.3,0.0894,5.3221,0.3,-4.9106,3.3221,0.3,-3.1106,3.7221,0.3,0.5894,3.6221,0.3,0.0894,4.6221,0.3,0.7894,4.1221,0.3,-4.1106,3.1221,0.3,-1.3106,8.0221,2,-1.9106,7.5221,2,-2.1106,8.1221,2.3,-2.9106,8.8221,1.7,-1.3106,9.0721,2.4,-2.5356,9.5221,1.6,-1.1106,8.8888,2.4,-2.444,8.9221,2.3,-3.0106,9.6079,2.4,-2.7392,9.7221,2.3,-4.9106,9.0721,2.4,-2.5356,7.5621,2.4,-2.6906,7.7221,2.3,-5.0106,6.9221,0.7,-0.6106,6.0221,0.4,-0.6106,7.1221,0.8,-0.9106,9.1221,0.4,-2.9678,8.4364,0.4,-3.0106,9.1221,0.3,-4.5106,9.1221,0.9,-0.9106,7.5221,0.9,-1.0106,7.0421,0.9,-4.7506,6.665,0.9,-4.2678,6.5221,0.7,-4.8106,8.4364,0.4,-3.0106,7.0936,0.5,-2.6392,6.665,0.9,-4.2678,6.6079,0.9,-4.2535,7.0936,0.5,-2.6392,6.865,0.4,-2.5821,6.6079,0.9,-4.2535,6.4596,0.9,-4.2856,6.2721,0.4,-2.7106,6.865,0.4,-2.5821,10.8221,0.4,-0.4106,10.2421,0.6,-0.7106,11.9221,0.3,-0.4106,9.7221,0.7,-0.4106,12.265,1.1,-3.5821,11.9221,1.1,-3.7106,12.3221,1,-4.1106,11.1221,0.6,-2.9106,12.0364,0.4,-1.4678,10.3221,0.4,-2.1106,10.2421,0.6,-0.7106,10.1888,0.5,-4.3773,9.1221,0.4,-2.9678,10.3221,0.4,-2.1106,11.1221,0.6,-2.9106,11.7888,1.1,-4.1773,11.9221,1.1,-3.7106,-4.3779,0.3,-3.7106,-1.1779,0.3,-1.0106,-1.3779,0.3,-2.5106,-1.2779,0.3,-2.0106,-1.8779,0.3,-2.5106,-1.7779,0.3,0.0894,-2.0779,0.3,-0.4106,-1.1779,0.3,0.0894,-1.5779,0.3,-1.1106,-1.2779,0.3,-1.5106,-1.4779,0.3,-2.0106,-2.0779,0.3,-2.0106,-2.0779,0.3,-2.7106,-3.0779,0.3,-3.6106,-2.1779,0.3,-1.4106,-4.9779,0.3,-1.4106,-0.1779,3.2,-0.0106,-0.1779,3.1,0.1894,-0.3779,2.8,-0.1106,-1.5779,2.8,-1.7106,-1.6779,2.8,-1.4106,-1.8779,2.8,-1.7106,-2.0779,2.8,-1.3106,-0.3779,2.8,-0.4106,-0.8779,2.6,-0.4106,-1.3779,2.6,-0.0106,-1.0779,2.6,-0.6106,-1.5779,2.4,-0.2106,-1.3779,2.5,-1.2606,-1.3779,2.5,-1.0106,-1.0779,2.4,-1.1106,-1.3779,2.5,-1.0106,-1.6445,2.5,-1.0106,-1.6445,2.5,-1.0106,-3.4445,1.5,-2.744,-3.5779,2.4,-1.6106,-4.5445,2.5,-1.5106,-4.6779,2.4,-0.5106,-4.3445,1.5,-3.0106,-4.4112,2.1,-2.5106,-3.3779,2.4,-1.4106,-0.1779,2.4,1.3894,0.1221,2.4,1.3894,-0.2779,2.4,1.6894,1.6221,2.8,2.9894,1.4221,2.8,2.9894,1.8221,2.8,2.5894,3.0221,2.8,2.3894,3.0221,2.8,2.5894,2.6221,2.8,2.1894,1.8221,2.8,0.6894,1.6221,2.9,0.9894,1.4221,2.8,0.6894,1.4221,2.5,1.2894,1.4721,2.9,2.5394,1.1221,2.8,2.4894,-0.1279,3.3,-0.1606,-0.0779,3.4,-0.5106,-0.2779,2.4,1.9894,2.6221,2.9,2.5894,2.2221,2.5,2.3894,2.2221,2.5,2.5894,-0.8779,3.8,-2.8106,-0.5779,3.8,-3.1106,-0.9779,3.8,-2.3106,-0.7779,3.8,-1.7106,-0.3779,3.8,-1.5106,1.1221,3,0.2894,2.6221,2.4,1.6894,2.1721,2.5,1.8894,1.7221,2.4,1.5894,2.1721,2.5,1.8894,2.2221,2.5,2.3894,1.4221,2.5,2.0394,1.4721,2.9,2.5394,-0.3779,2.4,2.3894,1.4221,2.5,2.0394,1.2221,2.5,2.0894,0.2221,3.8,-3.1106,0.6221,3.8,-2.8106,0.0721,3.1,0.5394,0.3221,2.8,0.8894,-0.1279,3.3,-0.1606,0.6221,2.5,2.456,0.7888,2.5,2.1227,1.2221,2.5,2.0894,0.5555,2.7,1.356,0.7888,2.5,2.1227,0.5555,2.7,1.356,0.5221,3.8,-1.8106,8.7221,1.4,-0.6106,9.4221,1.1,-0.1106,7.0221,0.8,-0.3106,8.0221,1.4,-0.7106,7.3221,1.2,-0.5106,7.5221,1.3,-0.5106,9.6221,1.1,-0.0106,9.4221,0.6,0.8894,9.4221,0.4,1.3894,5.8221,0.3,0.9894,7.5221,1.3,-0.5106,7.5021,1.2,-0.2706,7.5021,1.2,-0.2706,7.9821,1,0.3694,9.4221,0.6,0.8894,7.9821,1,0.3694,7.8793,0.7,1.2179,-4.1779,0.3,2.3894,-4.5779,0.3,2.3894,-4.3779,0.3,2.0894,-0.4779,2.4,1.7894,-0.8779,2.4,1.2894,-2.9779,0.3,0.8894,-3.2779,0.3,1.3894,-3.1779,0.3,0.6894,-3.5779,0.3,1.2894,-4.2779,0.3,1.6894,-2.0779,0.3,-1.2106,-4.6779,0.3,2.5894,-5.3779,0.3,2.2894,-3.3279,0.4,0.8894,-3.0779,0.6,0.4894,-1.4779,2.2,2.1894,-2.2445,0.4,-0.6106,-2.2779,0.6,-0.2106,-3.3279,0.4,0.8894,-3.4779,0.4,0.8894,-1.4279,1.8,1.0894,-2.2779,1.5,1.3394,-2.5279,1.2,0.6894,-1.8779,2.1,1.7644,-1.9279,1.2,0.1644,-2.5279,1.2,0.6894,-1.4279,1.8,1.0894,-1.2279,1.8,0.9144,-2.2445,0.4,-0.6106,-2.6279,0.4,-0.4606,-2.8529,0.7,0.0144,-2.6279,0.4,-0.4606,-3.4579,0.4,0.1094,-2.8529,0.7,0.0144,-3.4779,0.4,0.8894,-3.5379,0.4,0.8494,-3.5379,0.4,0.8494,-3.4579,0.4,0.1094,-0.9779,0.3,4.1894,-0.7779,0.3,4.0894,-1.0779,0.3,4.4894,1.4221,0.3,-0.6106,1.0221,0.3,2.9894,0.4221,0.3,3.0894,1.0221,0.3,2.4894,0.8221,0.3,4.0894,0.8221,0.3,4.6894,0.3221,0.3,3.8894,1.3221,0.3,0.4894,1.4221,0.3,1.1894,1.4221,0.3,2.3894,1.2221,0.3,1.0894,1.1221,0.3,1.0894,-0.8779,0.3,3.7894,-1.1779,0.3,3.9894,-1.2779,0.3,2.2894,-0.6779,0.3,2.3894,-0.4779,0.3,2.1894,-0.3779,0.3,1.1894,-0.8779,0.3,0.1894,-0.8779,0.3,-0.8106,-0.5779,0.3,-0.6106,-0.2779,0.3,0.4894,0.4221,0.3,0.4894,-0.2779,0.3,0.8894,0.8221,0.3,0.8894,0.5221,0.3,-0.4106,0.8221,0.3,-0.8106,-0.5779,1,-0.6106,-0.7779,1,-0.1106,-0.8779,1,-0.8106,-0.8779,1,0.8894,-0.6779,1,0.4894,0.3221,1,0.5894,0.8221,1,0.8894,0.5221,1,0.1894,0.8221,1,-0.8106,0.4221,1,-0.5106,10.8221,0.4,-0.4106,11.2421,0.1,1.2694,11.6221,0,1.6894,11.2421,0.1,1.2694,-2.4779,0.3,0.4894,-2.6779,0.3,1.8894,-2.9779,0.3,2.1894,-2.4779,0.3,2.1894,-3.4779,0.3,2.4894,-5.5779,0.3,3.1894,-4.6779,0.3,3.3894,-5.4779,0.3,3.9894,-3.5779,0.3,3.4894,3.9221,0.3,2.9894,3.9221,0.3,2.4894,4.3221,0.3,2.9894,1.9221,0.3,2.7894,1.7221,0.3,2.3894,2.4221,0.3,2.6894,2.9221,0.3,0.8894,3.7221,0.3,0.8894,2.6221,0.3,2.0894,2.0221,0.3,1.1894,3.4221,0.3,0.9894,3.2221,0.3,2.0894,5.9221,0.8,2.5894,6.0971,0.9,2.7394,5.5221,0.7,2.6894,6.6721,0.8,2.7894,7.8221,0.3,2.8894,7.6221,0.8,5.9894,6.6621,0.9,5.9094,7.8221,0.8,5.7894,5.2221,0.4,5.7894,6.0971,0.9,2.7394,6.1793,0.9,3.5751,7.8221,0.4,4.0494,6.8364,0.5,4.4608,7.8221,0.6,5.2094,6.6721,0.8,2.7894,6.1793,0.9,3.5751,5.4221,0.8,3.7227,5.3221,0.4,4.756,6.8364,0.5,4.4608,1.9221,0.3,3.0894,3.7221,0.3,3.3894,1.5221,0.3,4.0894,2.8221,0.3,3.3894,1.6221,0.3,3.2894,4.1221,0.3,5.0894,7.8221,0.6,5.2094,7.9221,0.8,5.8894,7.8721,0.4,4.3894,7.8221,0.4,4.0494,11.3421,1.1,3.8294,10.9846,1.1,3.6144,11.4221,1,3.2894,9.6721,0.5,4.5894,9.9888,0.4,5.956,8.7971,0.5,5.2394,11.0221,0.3,5.9894,8.8507,0.4,3.0037,7.8721,0.4,4.3894,8.7971,0.5,5.2394,9.6721,0.5,4.5894,10.9079,1.1,3.2322,10.9846,1.1,3.6144,6.6621,0.9,5.9094,6.7821,0.9,5.9694,7.8221,0.8,6.0894,6.5793,1,7.0751,4.9221,0.3,8.3894,6.7821,0.9,5.9694,6.5793,1,7.0751,6.5421,0.7,8.5694,7.7021,1,7.6494,7.6221,0.6,8.6894,7.7021,1,7.6494,7.8021,1.1,7.0094,9.9888,0.4,5.956,10.7221,0,9.0894,7.8021,1.1,7.0094] 7 | ,"normals":[0,0.8851,0.4653,-0.2401,0.9537,0.1809,-0.3498,0.9222,-0.1645,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0.1469,0.7838,-0.6033,0.0277,0.9717,-0.2346,0.0929,0.8184,-0.567,0.1411,0.9461,-0.2914,0,1,0,0.0422,0.9476,0.3165,0.0333,0.8389,0.5433,0.048,0.9979,0.0439,0.239,0.9705,0.0298,0.1495,0.9817,0.1178,0.2606,0.9649,0.0326,0.219,0.9508,0.219,0.0267,0.9996,-0.0066,0.4399,0.7829,0.4399,-0.537,0.838,-0.0966,-0.0273,0.997,-0.0719,0.0407,0.95,-0.3094,0.0457,0.9821,-0.1829,0.0747,0.9732,-0.2174,0.0751,0.9784,-0.1924,0,1,0,0.1187,0.9892,0.0859,0,1,0,-0.14,0.9609,-0.2387,-0.0551,0.7716,-0.6337,-0.5026,0.8224,0.2664,-0.0905,0.9661,-0.2415,-0.0972,0.9679,-0.2316,-0.0948,0.9798,-0.1762,-0.1104,0.9939,0,-0.0986,0.9926,-0.0709,-0.1096,0.9937,-0.0213,-0.0949,0.9723,-0.2134,-0.15,0.9677,-0.2026,-0.0732,0.979,-0.19,0.0757,0.8613,0.5024,0.2184,0.957,0.1907,-0.1016,0.9936,-0.0485,-0.1003,0.9784,-0.1809,-0.3185,0.9423,-0.1029,-0.1254,0.9788,-0.1618,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-0.15,0.9147,0.3751,-0.0479,0.9141,0.4027,0.1077,0.9883,0.1075,0.0094,0.891,0.4538,0.0337,0.9885,0.1474,0.0343,0.893,0.4488,0.1503,0.9418,0.3007,-0.0587,0.9963,-0.0631,-0.0017,0.9942,0.1075,-0.0275,0.9995,-0.0116,0.0563,0.9874,0.1481,0.2946,0.8912,0.3448,0.0268,0.9996,-0.0051,-0.3159,0.9478,-0.0422,-0.3561,0.9342,-0.0206,-0.2904,0.9529,-0.0874,0.0096,0.9879,-0.1543,0.0107,0.9852,-0.1708,0.0286,0.9995,-0.0146,0.144,0.976,-0.1635,-0.1261,0.969,-0.2126,0.0334,0.9994,-0.0121,0.0155,0.9992,0.0353,-0.2399,0.9305,-0.2767,0.1527,0.9879,0.0248,0.0687,0.9974,-0.02,-0.0127,0.9986,-0.0507,-0.0268,0.9991,-0.0316,-0.3731,0.922,0.1033,-0.3687,0.9256,0.0855,-0.0103,0.9988,0.0473,0.0102,0.9988,-0.047,-0.0867,0.9931,0.0791,0.0073,0.9994,-0.0338,0.1089,0.9113,0.397,0.2401,0.958,0.1568,0.1209,0.9811,0.1509,0.4158,0.8972,0.1486,-0.089,0.9873,0.1317,-0.0754,0.9767,0.2011,0.1383,0.9845,-0.1078,-0.1617,0.9548,0.2492,-0.0004,0.9946,0.1037,0.0614,0.9979,-0.0187,0.1782,0.9802,-0.0858,-0.2027,0.9789,0.026,-0.0354,0.9976,-0.0598,0.0187,0.9995,-0.0262,-0.226,0.9682,0.1068,-0.1825,0.9802,0.077,-0.2295,0.9711,0.0656,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0.309,0.9466,-0.0917,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0.0281,0.9985,-0.0469,-0.0203,0.9996,-0.0197,-0.6176,0.7081,0.3423,-0.6238,0.7595,0.184,-0.0857,0.8267,-0.556,0,1,0,0.1218,0.9545,0.272,0,1,0,-0.0355,0.931,0.3633,-0.3714,0.9285,0,-0.1778,0.959,-0.2206,-0.3788,0.8854,-0.2692,-0.1862,0.9786,-0.087,-0.2623,0.965,0.0037,0.5764,0.8172,0,0.3732,0.9157,-0.1489,0.2392,0.9442,-0.2266,-0.1048,0.9605,0.2576,0,0.9224,0.3863,0.4607,0.8648,0.1994,0.1623,0.7914,-0.5893,0.1032,0.9717,-0.2122,0.0931,0.9915,-0.0909,0.0489,0.9976,0.0489,0.239,0.752,-0.6143,0.3799,0.7129,-0.5894,-0.0448,0.9967,0.0672,0,1,0,-0.2681,0.9488,0.1671,0,1,0,0,1,0,-0.0162,0.9906,0.1356,0.2628,0.9424,-0.2069,0,1,0,0.1218,0.985,-0.1218,-0.338,0.9106,-0.2377,0,0.9487,-0.3162,-0.5094,0.8589,0.0533,-0.173,0.9459,0.2744,0.0684,0.9794,0.1897,-0.0354,0.9764,0.213,-0.2275,0.8883,-0.3989,-0.8802,0.474,0.0225,0.0839,0.9411,0.3274,0,1,0,-0.2221,0.9511,-0.2145,-0.2574,0.9647,-0.0553,-0.0709,0.9975,0,0,1,0,0,1,0,0,1,0,0,1,0,0.0465,0.9891,0.1395,0.0692,0.9174,0.392,-0.0558,0.8371,-0.5442,-0.012,0.8832,-0.4687,0.1254,0.9704,-0.2062,-0.0361,0.9769,-0.2107,0.1352,0.9907,-0.0135,-0.1151,0.8834,-0.4542,0.1115,0.7705,-0.6276,-0.1123,0.9937,-0.0062,0.0192,0.9849,-0.1719,-0.1051,0.9012,-0.4205,0,1,0,0,1,0,0.5669,0.8079,0.161,0.0394,0.9372,0.3466,-0.0754,0.9529,0.2938,-0.2533,0.959,-0.1267,-0.2566,0.9579,-0.1283,0.0623,0.983,-0.1728,-0.2257,0.9357,0.2712,-0.1004,0.9949,0.0078,-0.0728,0.9671,0.2436,0.0458,0.9879,0.1483,-0.0274,0.8506,0.5251,-0.0213,0.8977,0.4401,-0.3805,0.908,0.1752,-0.073,0.9058,0.4174,-0.2806,0.7497,0.5994,-0.4046,0.8091,-0.4262,0.5678,0.6904,0.4482,-0.5263,0.7875,0.3205,0.2094,0.9371,0.2792,-0.2783,0.9542,0.1101,-0.2345,0.9041,0.3572,-0.3616,0.8668,0.3434,-0.3921,0.7524,0.5292,-0.0412,0.9217,0.3857,0.1096,0.9111,0.3972,-0.206,0.9302,0.3039,-0.0706,0.9552,0.2875,0,1,0,0,1,0,0,1,0,-0.0879,0.9954,0.0387,-0.3556,0.9271,-0.1182,0,1,0,0,1,0,0.6649,0.5925,0.4547,0.1365,0.9681,0.21,0.0961,0.9793,0.178,0,1,0,0,1,0,-0.0082,0.9999,0.01,0.8232,0.1291,0.5528,-0.3375,0.9399,-0.0515,-0.2365,0.9589,-0.157,0.6185,0.7753,-0.1278,-0.1148,0.8642,-0.4899,0,0.9243,0.3816,0.0274,0.9523,0.304,-0.4313,0.8095,-0.3984,-0.4553,0.8214,-0.3434,-0.6595,0.7462,-0.0907,-0.0917,0.8858,-0.4548,-0.4212,0.7687,-0.4813,-0.4441,0.7383,-0.5076,-0.4014,0.7927,-0.4588,-0.4238,0.7653,-0.4843,-0.0979,0.9632,-0.2503,-0.1058,0.901,-0.4206,0.2118,0.9711,0.1097,-0.1052,0.9727,-0.2069,-0.2287,0.9609,-0.1557,-0.4541,0.7788,-0.4327,-0.1285,0.9728,0.1927,-0.0959,0.981,0.1683,-0.1599,0.987,-0.0173,-0.171,0.9851,-0.0185,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0.1909,0.9735,0.1261,0.1163,0.9824,0.1463,0.1241,0.9847,0.1221,0.1688,0.9792,0.1128,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-0.1673,0.8867,-0.431,0.0889,0.7777,-0.6222,-0.2578,0.9632,-0.0756,0.236,0.4011,-0.8851,0.1907,0.9741,-0.121,0.1106,0.9897,-0.0901,-0.0529,0.4534,-0.8897,0.1576,0.9524,-0.2609,-0.2359,0.966,-0.1056,-0.1113,0.9937,0.0109,0.135,0.9876,0.0793,0.2085,0.9779,-0.0151,0.148,0.9864,0.071,0.1149,0.9535,-0.2787,0.2716,0.9624,-0.0021,-0.0636,0.9755,0.2104,-0.0962,0.9806,0.1708,-0.0449,0.9868,0.1554,-0.0786,0.9922,0.0971,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-0.1783,0.9521,-0.2483,0.1796,0.965,-0.1908,0.7282,0.6755,-0.1158,0.5239,0.8483,-0.077,-0.0833,0.9868,0.1385,-0.0829,0.987,0.1379,0.1469,0.9836,-0.1048,-0.0568,0.9817,0.1818,0.0989,0.995,0.0149,0.139,0.9859,-0.0935,0.0119,0.9879,0.1548,-0.1543,0.988,0.0015,-0.0113,0.9944,-0.1052,0.0337,0.9877,-0.1529,-0.1824,0.9829,0.0226,-0.1352,0.99,0.0408,-0.1698,0.9849,0.0341,-0.2644,0.8065,0.5288,0.0637,0.9898,-0.1274,-0.3233,0.9204,-0.22,-0.2492,0.9661,-0.0677,-0.3309,0.94,0.0834,-0.087,0.9906,-0.1055,-0.0839,0.9935,0.0768,-0.1374,0.9622,0.235,-0.0011,0.9926,0.1208,0.1206,0.9365,0.3293,0.9035,0.3789,0.2004,-0.9416,0.3184,-0.109,0.1481,0.9848,0.091,0.2021,0.9728,0.1131,0.3656,0.9305,-0.0212] 8 | ,"indices":[0,1,2,3,4,5,6,7,8,5,4,7,9,10,11,11,10,12,4,13,7,10,2,12,14,15,16,15,14,12,16,17,18,17,19,12,19,17,16,18,20,21,20,22,12,22,20,18,1,23,2,23,1,24,7,13,8,2,25,12,25,26,24,26,25,2,27,28,21,21,28,24,28,27,12,13,21,8,8,21,24,29,30,31,31,30,32,32,33,34,33,32,30,35,36,37,38,39,40,41,42,43,44,45,34,34,45,46,45,44,30,43,42,47,34,46,48,36,40,37,40,39,37,37,39,49,39,48,49,49,48,42,46,47,48,48,47,42,50,51,52,51,53,52,52,53,54,55,56,57,6,8,58,59,60,61,62,63,64,65,66,67,56,64,57,57,64,53,53,64,54,64,63,54,54,63,68,63,4,68,68,4,69,4,3,69,3,70,69,69,70,66,66,70,67,70,58,67,67,58,60,61,60,8,60,58,8,59,61,71,72,73,74,75,76,77,78,77,73,74,73,79,73,77,79,61,80,71,66,65,81,77,76,79,82,83,84,65,85,81,79,76,83,71,80,85,76,86,83,83,86,84,85,80,81,86,81,84,84,81,80,87,88,89,90,91,92,91,93,94,93,91,90,95,94,96,94,95,97,97,95,92,88,98,89,89,98,99,96,94,99,94,89,99,100,101,102,103,104,105,104,106,107,106,104,103,108,109,110,109,111,112,112,111,107,111,108,105,108,111,109,113,114,110,114,115,116,116,107,102,107,116,115,115,114,113,117,118,110,118,117,119,119,102,101,102,119,120,120,119,117,121,122,123,122,124,106,124,122,121,125,126,127,126,125,128,128,129,130,130,131,106,131,129,123,129,128,125,129,131,130,132,133,105,133,134,106,134,133,135,135,136,137,137,136,127,136,135,132,132,135,133,0,138,1,57,139,55,140,141,142,55,139,141,143,144,145,145,144,139,144,146,139,146,147,139,139,147,141,147,148,141,141,148,142,148,149,142,142,149,150,14,16,151,149,152,150,16,150,151,150,152,151,1,138,153,152,153,151,151,153,138,154,155,156,157,158,159,159,158,160,161,156,162,156,155,162,155,163,162,162,163,164,163,165,164,166,167,158,167,168,164,168,167,166,169,170,158,170,169,165,165,169,164,158,171,160,160,171,165,11,172,9,9,172,173,174,173,175,173,176,9,176,173,177,177,173,174,173,178,175,175,178,165,178,160,165,179,180,181,182,183,184,185,186,187,188,189,190,190,189,191,183,192,184,192,183,193,154,194,155,194,154,195,181,180,196,186,197,187,187,197,198,198,199,184,199,198,197,200,201,202,203,202,204,190,191,205,206,207,208,207,206,187,209,184,208,184,209,210,210,209,187,184,211,208,211,212,193,212,211,184,196,180,213,208,214,191,191,214,215,215,214,193,216,217,201,218,195,219,195,218,220,220,218,155,221,222,193,222,221,180,180,221,213,223,224,191,224,225,180,225,223,193,223,225,224,226,219,191,219,226,180,191,219,205,219,195,205,205,195,227,195,204,227,204,202,227,201,217,202,217,227,202,90,92,228,92,229,228,100,230,101,87,231,88,88,231,232,232,233,230,233,232,231,234,235,229,235,234,236,228,229,231,101,230,237,238,239,230,239,231,229,231,239,238,240,241,230,241,242,236,242,241,229,229,241,240,243,244,230,230,244,237,244,243,236,245,246,247,181,248,179,196,213,248,179,248,249,250,251,252,251,253,252,247,246,254,146,144,255,256,257,246,252,258,259,258,252,253,255,144,152,213,260,248,248,260,249,144,261,152,261,144,262,246,257,254,263,264,259,264,253,254,253,264,263,260,265,249,265,266,267,267,266,259,266,265,268,268,265,260,269,270,262,262,270,259,270,269,271,271,272,249,272,271,269,273,274,152,274,262,275,275,262,259,262,274,273,152,276,153,153,276,277,277,278,259,278,277,276,279,280,259,280,254,257,254,280,279,281,282,259,282,281,153,153,281,257,283,284,285,51,286,53,287,288,289,290,291,292,73,293,78,78,293,286,294,295,296,295,289,296,286,293,53,53,293,297,293,296,297,296,289,297,291,285,292,285,284,292,284,298,292,292,298,288,299,300,298,300,301,298,298,301,288,301,302,288,288,302,289,302,303,289,303,297,289,304,305,306,307,308,309,309,308,310,308,311,310,310,311,312,311,306,312,306,305,312,313,314,315,315,314,316,314,317,316,317,318,316,316,318,319,318,320,319,319,320,321,320,322,321,313,315,322,315,321,322,234,124,236,323,324,124,324,123,325,123,324,323,326,236,124,236,326,325,283,285,299,327,328,250,250,328,251,251,328,329,328,330,329,329,330,331,257,256,332,256,333,332,332,333,334,331,330,335,333,335,334,300,299,330,330,299,335,334,335,285,285,335,299,336,337,338,339,340,341,74,342,72,82,84,343,341,340,344,72,342,345,343,84,346,295,294,340,294,345,340,340,345,344,345,342,344,344,342,347,342,346,347,347,346,337,338,337,84,84,337,346,348,349,350,349,348,351,351,348,352,353,354,355,355,354,356,357,358,350,358,359,360,360,361,355,361,360,359,359,362,352,362,358,357,358,362,359,363,364,350,364,363,365,365,366,356,356,366,355,366,365,363,339,341,367,336,338,368,290,369,291,341,370,367,371,367,369,367,370,369,338,372,368,368,372,370,372,291,370,291,369,370,355,373,374,374,373,375,375,376,352,376,375,373,377,378,379,378,377,380,380,381,382,382,381,374,381,380,383,383,380,377,384,385,352,385,386,374,386,385,387,387,388,389,389,388,379,388,387,384,384,387,385,390,391,356,391,353,392,353,391,390,356,393,394,393,395,392,395,393,356,396,397,394,397,398,399,398,396,392,396,398,397,400,401,399,401,392,374,392,401,400,402,403,374,403,402,383,403,404,374,404,403,399] 9 | ,"subMeshes":[{"materialIndex":0,"verticesStart":0,"verticesCount":405,"indexStart":0,"indexCount":1293}] 10 | ,"instances":[]} 11 | ,{"name":"Cube.001","id":"Cube.001","materialId":"level.Default","billboardMode":0,"position":[0,0,0],"rotation":[-1.5708,0,0],"scaling":[1,1,1],"isVisible":true,"isEnabled":true,"useFlatShading":false,"checkCollisions":false,"receiveShadows":false 12 | ,"positions":[-3.1695,3.7678,0.2418,-4.3694,3.914,0.2418,-4.3694,3.914,0.5613,-3.1172,4.1974,0.2418,-3.1172,4.1974,0.5613,-4.3171,4.3435,0.5613,-4.3171,4.3435,0.2418,-3.1695,3.7678,0.5613,-3.2159,3.3874,0.5064,-4.4158,3.5336,0.5064,-4.4158,3.5336,0.8259,-3.1635,3.817,0.8259,-4.3634,3.9632,0.8259,-4.3634,3.9632,0.5064,-3.1635,3.817,0.5064,-3.2159,3.3874,0.8259,-3.2622,3.007,0.7689,-4.4621,3.1531,0.7689,-4.4621,3.1531,1.0884,-3.2099,3.4366,1.0884,-4.4098,3.5827,1.0884,-4.4098,3.5827,0.7689,-3.2099,3.4366,0.7689,-3.2622,3.007,1.0884,-3.3084,2.6277,1.058,-4.5083,2.7738,1.058,-4.5083,2.7738,1.3776,-3.2561,3.0573,1.3776,-4.456,3.2034,1.3776,-4.456,3.2034,1.058,-3.2561,3.0573,1.058,-3.3084,2.6277,1.3776,-3.3552,2.2435,1.6384,-3.3552,2.2435,1.3188,-4.5551,2.3896,1.3188,-3.3029,2.6731,1.6384,-4.5028,2.8192,1.6384,-4.5028,2.8192,1.3188,-3.3029,2.6731,1.3188,-4.5551,2.3896,1.6384,-3.4019,1.8604,1.5964,-4.6018,2.0066,1.5964,-4.6018,2.0066,1.9159,-3.3496,2.29,1.9159,-4.5494,2.4362,1.9159,-4.5494,2.4362,1.5964,-3.3496,2.29,1.5964,-3.4019,1.8604,1.9159,-3.447,1.4906,1.8474,-4.6468,1.6368,1.8474,-4.6468,1.6368,2.1669,-3.3946,1.9202,2.1669,-4.5945,2.0664,2.1669,-4.5945,2.0664,1.8474,-3.3946,1.9202,1.8474,-3.447,1.4906,2.1669,4.2131,-5.2645,0.2662,4.2131,-5.2645,0.0288,-5.7779,-4.0473,0.0288,5.4628,4.9934,0.2662,-4.5282,6.2106,0.2662,-4.5282,6.2106,0.0288,5.4628,4.9934,0.0288,-5.7779,-4.0473,0.2662,-0.5997,-2.2534,0.2669,-2.5008,-2.0218,0.2669,-2.5008,-2.0218,0.0295,-0.368,-0.3523,0.2669,-0.368,-0.3523,0.0295,-2.2692,-0.1207,0.0295,-0.5997,-2.2534,0.0295,2.9761,1.2788,0.0298,2.9761,1.2788,0.2672,1.075,1.5104,0.2675,3.2077,3.1799,0.0298,1.3066,3.4115,0.0298,1.3066,3.4115,0.2672,3.2077,3.1799,0.2672,1.075,1.5104,0.03,-0.0251,2.4628,0.268,-1.9262,2.6944,0.2675,-1.9262,2.6944,0.0302,0.2065,4.3639,0.2676,0.2065,4.3639,0.0302,-1.6946,4.5955,0.0301,-1.6946,4.5955,0.2675,-2.2692,-0.1207,0.2669,-0.0251,2.4628,0.0305,-2.0559,0.1171,0.2526,-2.938,-0.7093,0.2526,-2.938,-0.7093,0.5722,-2.3517,0.4329,0.5722,-3.2339,-0.3934,0.5722,-3.2339,-0.3934,0.2526,-2.3517,0.4329,0.2526,-2.0559,0.1171,0.5722,-1.7939,-0.1626,0.5173,-2.6761,-0.9889,0.5173,-2.6761,-0.9889,0.8368,-2.0898,0.1533,0.8368,-2.9719,-0.6731,0.8368,-2.9719,-0.6731,0.5173,-2.0898,0.1533,0.5173,-1.7939,-0.1626,0.8368,-1.5319,-0.4423,0.7797,-2.4141,-1.2686,0.7797,-2.4141,-1.2686,1.0993,-1.8278,-0.1264,0.7797,-1.8278,-0.1264,1.0993,-2.7099,-0.9528,1.0993,-2.7099,-0.9528,0.7797,-1.5319,-0.4423,1.0993,-1.2707,-0.7211,1.0689,-2.1528,-1.5475,1.0689,-2.1528,-1.5475,1.3884,-1.5665,-0.4053,1.3884,-2.4487,-1.2316,1.3884,-2.4487,-1.2316,1.0689,-1.5665,-0.4053,1.0689,-1.2707,-0.7211,1.3884,-1.0061,-1.0036,1.3297,-1.8882,-1.8299,1.3297,-1.8882,-1.8299,1.6492,-1.3019,-0.6877,1.6492,-2.1841,-1.5141,1.6492,-2.1841,-1.5141,1.3297,-1.3019,-0.6877,1.3297,-1.0061,-1.0036,1.6492,-0.7423,-1.2852,1.6072,-1.6244,-2.1115,1.6072,-1.6244,-2.1115,1.9268,-1.0381,-0.9693,1.9268,-1.9203,-1.7957,1.9268,-1.9203,-1.7957,1.6072,-1.0381,-0.9693,1.6072,-0.7423,-1.2852,1.9268,-0.4876,-1.5571,1.8583,-1.3697,-2.3834,1.8583,-1.3697,-2.3834,2.1778,-0.7835,-1.2412,1.8583,-0.7835,-1.2412,2.1778,-1.6656,-2.0676,2.1778,-1.6656,-2.0676,1.8583,-0.4876,-1.5571,2.1778,0.2929,-0.411,0.1804,-0.6268,-0.299,0.1804,-0.6268,-0.299,2.7952,0.3849,0.3446,2.7952,-0.5347,0.4566,2.7952,-0.5347,0.4566,0.1804,0.3849,0.3446,0.1804,0.2929,-0.411,2.7952,2.161,0.5529,0.1804,1.5994,0.6213,0.1804,1.5994,0.6213,2.7952,2.2232,1.0634,2.7952,1.6616,1.1319,2.7952,1.6616,1.1319,0.1804,2.2232,1.0634,0.1804,2.161,0.5529,2.7952,-1.508,1.2854,0.1804,-2.0696,1.3538,0.1804,-2.0696,1.3538,2.7952,-1.4458,1.796,2.7952,-2.0074,1.8644,2.7952,-2.0074,1.8644,0.1804,-1.4458,1.796,0.1804,-1.508,1.2854,2.7952,1.9624,-1.077,0.1804,1.4008,-1.0086,0.1804,1.4008,-1.0086,2.7952,2.0246,-0.5664,2.7952,1.463,-0.498,2.7952,1.463,-0.498,0.1804,2.0246,-0.5664,0.1804,1.9624,-1.077,2.7952,1.7141,-3.1153,0.1804,1.1525,-3.0469,0.1804,1.1525,-3.0469,2.7952,1.7763,-2.6047,2.7952,1.2147,-2.5363,2.7952,1.2147,-2.5363,0.1804,1.7763,-2.6047,0.1804,1.7141,-3.1153,2.7952,3.5369,-0.7975,0.1804,2.9753,-0.7291,0.1804,2.9753,-0.7291,2.7952,3.5991,-0.2869,2.7952,3.0375,-0.2185,2.7952,3.0375,-0.2185,0.1804,3.5991,-0.2869,0.1804,3.5369,-0.7975,2.7952,3.1447,-2.7778,2.7952,3.1447,-2.7778,0.1804,2.5831,-2.7094,0.1804,3.2069,-2.2672,2.7952,2.6453,-2.1988,2.7952,2.6453,-2.1988,0.1804,3.2069,-2.2672,0.1804,2.5831,-2.7094,2.7952,-3.6649,-3.3781,0.2291,-4.5846,-3.2661,0.2291,-4.5846,-3.2661,0.9372,-3.5729,-2.6225,0.9372,-4.4925,-2.5105,0.9372,-4.4925,-2.5105,0.2291,-3.5729,-2.6225,0.2291,-3.6649,-3.3781,0.9372,-3.0861,-2.0991,0.2291,-3.8775,-2.5809,0.2291,-3.8775,-2.5809,0.9372,-3.4819,-1.4489,0.9372,-4.2733,-1.9307,0.9372,-4.2733,-1.9307,0.2291,-3.4819,-1.4489,0.2291,-3.0861,-2.0991,0.9372,-3.3669,-3.1774,1.6379,-3.3669,-3.1774,0.9298,-4.224,-2.8257,0.9298,-3.0779,-2.4732,1.6379,-3.935,-2.1215,1.6379,-3.935,-2.1215,0.9298,-3.0779,-2.4732,0.9298,-4.224,-2.8257,1.6379,1.398,-3.9949,0.9372,1.398,-3.9949,0.2291,0.4784,-3.8829,0.2291,1.4901,-3.2393,0.9372,0.5704,-3.1273,0.9372,0.5704,-3.1273,0.2291,1.4901,-3.2393,0.2291,0.4784,-3.8829,0.9372,3.7009,-3.2893,0.2291,2.7813,-3.1773,0.2291,2.7813,-3.1773,0.9372,3.793,-2.5338,0.9372,2.8733,-2.4217,0.9372,2.8733,-2.4217,0.2291,3.793,-2.5338,0.2291,3.7009,-3.2893,0.9372,2.6466,-2.8602,2.1751,-1.1153,-2.4019,2.1751,-1.1153,-2.4019,2.3876,2.805,-1.5596,2.3876,-0.9569,-1.1013,2.3876,-0.9569,-1.1013,2.1751,2.805,-1.5596,2.1751,2.6466,-2.8602,2.3876,-1.044,-0.0436,2.114,-4.8059,0.4147,2.114,-4.8059,0.4147,2.3264,-0.8856,1.257,2.3264,-4.6475,1.7153,2.3264,-4.6475,1.7153,2.114,-0.8856,1.257,2.114,-1.044,-0.0436,2.3264,0.3918,-1.7881,2.3709,0.3702,-1.7136,2.173,-0.6134,1.6748,3.5562,1.6501,-1.4228,2.3709,0.6665,1.9656,3.754,0.6449,2.0401,3.5562,1.6285,-1.3484,2.173,-0.5918,1.6004,3.754,-0.4092,-0.2177,2.5464,-0.4092,-0.2177,1.8383,-1.3288,-0.1056,1.8383,-0.3171,0.5379,2.5464,-1.2368,0.65,2.5464,-1.2368,0.65,1.8383,-0.3171,0.5379,1.8383,-1.3288,-0.1056,2.5464,-0.7094,1.6285,0.9372,-0.7094,1.6285,0.2291,-1.2771,2.3607,0.2291,-0.1078,2.0949,0.9372,-0.6755,2.8271,0.9372,-0.6755,2.8271,0.2291,-0.1078,2.0949,0.2291,-1.2771,2.3607,0.9372,2.7899,5.1695,1.633,2.6946,4.6706,1.633,2.6946,4.6706,1.1605,3.2066,4.574,1.6338,2.7899,5.1695,1.1605,3.2059,4.5741,1.1612,3.2883,5.0755,1.6338,4.212,4.9938,0.5165,3.2976,5.1427,0.5176,3.1753,4.3918,0.4945,4.0897,4.2429,0.4933,4.0801,4.1639,3.1069,3.288,5.0637,3.1311,4.2024,4.9148,3.13,3.2906,5.0814,2.5813,3.1657,4.3128,3.108,2.0091,5.3526,0.5192,1.0947,5.5015,0.5204,0.9724,4.7506,0.4972,1.085,5.4225,3.1339,1.9995,5.2736,3.1328,1.8771,4.5226,3.1096,0.9627,4.6716,3.1107,2.0027,5.2966,2.4065,1.8798,4.5413,2.528,1.8868,4.6017,0.4961,1.8806,4.5479,2.3099,3.709,2.5644,3.0737,3.7311,2.5273,2.8656,0.7604,2.948,2.4761,4.2058,5.6185,2.5826,1.2351,6.0391,2.193,1.2572,6.002,1.985,1.1379,5.4411,2.2892,0.7383,2.985,2.6841,1.0434,4.6878,2.1963,4.2279,5.5814,2.3745,4.1249,4.9487,2.4763,4.0966,4.9467,2.6906,4.0021,4.1934,2.5977,3.1677,4.3261,2.7027,3.9737,4.1914,2.8121,1.015,4.6858,2.4107,3.1685,4.3327,2.4846,3.2914,5.088,2.3632,2.0035,5.3032,2.1885,3.2876,5.0757,1.1612,0.8123,2.2413,3.1962,0.8123,2.2413,3.7884,0.7429,2.7321,3.7884,0.7429,2.7321,3.1962,0.4374,3.1224,3.7884,0.4374,3.1224,3.1962,-0.0224,3.3076,3.7884,-0.0224,3.3076,3.1962,-0.5132,3.2382,3.7884,-0.5132,3.2382,3.1962,-0.9035,2.9326,3.7884,-0.9035,2.9326,3.1962,-1.0888,2.4729,3.7884,-1.0888,2.4729,3.1962,-1.0193,1.9821,3.7884,-1.0193,1.9821,3.1962,-0.7138,1.5918,3.7884,-0.254,1.4065,3.1962,-0.7138,1.5918,3.1962,-0.254,1.4065,3.7884,0.2368,1.4759,3.7884,0.2368,1.4759,3.1962,0.6271,1.7815,3.7884,0.6271,1.7815,3.1962,7.0799,-0.4496,0.4034,7.4145,2.9225,2.1001,7.4051,2.8279,2.2901,9.4693,-0.6867,0.4034,9.4599,-0.7813,0.5934,9.7946,2.5908,2.2901,9.8039,2.6854,2.1001,7.0705,-0.5442,0.5934,8.1166,0.8544,1.3052,8.3181,2.8847,2.2874,8.3177,2.8813,3.4423,8.6285,0.8036,1.3052,8.6285,0.8036,3.4423,8.8296,2.8305,3.4423,8.8299,2.834,2.2874,8.1166,0.8544,3.4423,7.4139,2.8145,2.0874,7.653,5.2242,2.0874,7.653,5.2242,2.2999,9.6444,2.5931,2.0874,9.6444,2.5931,2.2999,9.8836,5.0028,2.2999,9.8836,5.0028,2.0874,7.4139,2.8145,2.2999,1.1663,5.4431,2.0749,6.2889,-1.1626,0.2762,6.2889,-1.1626,0.0808,5.6811,-1.0886,0.0585,6.8966,-1.2367,0.3433,7.5044,-1.3107,0.4251,7.5044,-1.3107,0.2273,6.8966,-1.2367,0.1466,8.1121,-1.3848,0.4589,8.1121,-1.3848,0.2607,8.7199,-1.4588,0.2273,9.3276,-1.5328,0.1282,8.7199,-1.4588,0.4251,9.9353,-1.6069,-0.0071,9.3276,-1.5328,0.3249,10.5431,-1.6809,-0.1421,9.9353,-1.6069,0.1865,11.1508,-1.755,-0.214,10.5431,-1.6809,0.0505,11.7586,-1.829,-0.2335,11.1508,-1.755,-0.0219,6.3629,-0.5549,0.1466,5.7552,-0.4809,0.0691,6.9707,-0.6289,0.3012,7.5784,-0.703,0.4608,8.1861,-0.777,0.5195,8.7939,-0.8511,0.4608,9.4016,-0.9251,0.2873,10.0094,-0.9991,0.0731,10.6171,-1.0732,-0.1059,11.2249,-1.1472,-0.1914,11.8326,-1.2213,-0.0219,11.8326,-1.2213,-0.214,11.2249,-1.1472,0.0006,11.7586,-1.829,-0.0409,10.6171,-1.0732,0.0865,10.0094,-0.9991,0.2699,9.4016,-0.9251,0.4906,8.7939,-0.8511,0.6594,8.1861,-0.777,0.7178,7.5784,-0.703,0.6594,6.3629,-0.5549,0.3433,6.9707,-0.6289,0.504,5.6811,-1.0886,0.2528,5.7552,-0.4809,0.2634,6.437,0.0528,0.2273,5.8292,0.1269,0.0892,7.0447,-0.0212,0.4608,7.6524,-0.0952,0.6328,8.2602,-0.1693,0.6847,8.8679,-0.2433,0.6328,9.4757,-0.3174,0.4561,10.0834,-0.3914,0.1879,10.6912,-0.4654,-0.0182,11.2989,-0.5395,-0.1165,11.9066,-0.6135,0.0505,11.9066,-0.6135,-0.1421,11.2989,-0.5395,0.0758,10.6912,-0.4654,0.1755,10.0834,-0.3914,0.3868,9.4757,-0.3174,0.6557,8.8679,-0.2433,0.8323,8.2602,-0.1693,0.884,7.6524,-0.0952,0.8323,7.0447,-0.0212,0.6594,6.437,0.0528,0.4251,5.8292,0.1269,0.2848,6.511,0.6606,0.2607,5.9033,0.7346,0.1007,7.1187,0.5865,0.5195,7.7265,0.5125,0.6847,8.3342,0.4385,0.7297,8.942,0.3644,0.6847,9.5497,0.2904,0.5195,10.1575,0.2163,0.2505,10.7652,0.1423,0.0614,11.3729,0.0683,-0.015,11.9807,-0.0058,0.1631,11.9807,-0.0058,-0.0295,11.3729,0.0683,0.1778,10.7652,0.1423,0.2572,10.1575,0.2163,0.4492,9.5497,0.2904,0.7178,8.942,0.3644,0.884,8.3342,0.4385,0.9278,7.7265,0.5125,0.884,7.1187,0.5865,0.7178,6.511,0.6606,0.4589,5.9033,0.7346,0.2966,5.9773,1.3424,0.0892,6.585,1.2683,0.2273,7.1928,1.1943,0.4608,7.8005,1.1202,0.6328,9.016,0.9722,0.6328,8.4083,1.0462,0.6847,9.6238,0.8981,0.4608,10.2315,0.8241,0.2273,10.8392,0.75,0.0846,11.447,0.676,0.0451,12.0547,0.602,0.2337,12.0547,0.602,0.0401,11.447,0.676,0.2388,10.8392,0.75,0.2802,10.2315,0.8241,0.4251,9.6238,0.8981,0.6594,9.016,0.9722,0.8323,7.8005,1.1202,0.8323,8.4083,1.0462,0.884,7.1928,1.1943,0.6594,6.585,1.2683,0.4251,5.9773,1.3424,0.2848,6.0513,1.9501,0.0691,6.6591,1.8761,0.1466,7.2668,1.802,0.3012,7.8746,1.728,0.4608,9.0901,1.5799,0.4608,8.4823,1.6539,0.5195,9.6978,1.5059,0.3012,10.3055,1.4318,0.1466,10.9133,1.3578,0.0691,11.521,1.2837,0.0585,12.1288,1.2097,0.2528,12.1288,1.2097,0.0585,11.521,1.2837,0.2528,10.9133,1.3578,0.2634,10.3055,1.4318,0.3433,9.6978,1.5059,0.504,9.0901,1.5799,0.6594,7.8746,1.728,0.6594,8.4823,1.6539,0.7178,7.2668,1.802,0.504,6.6591,1.8761,0.3433,6.0513,1.9501,0.2634,6.1254,2.5578,0.0585,6.7331,2.4838,0.0817,7.3409,2.4098,0.1466,7.9486,2.3357,0.2273,9.1641,2.1876,0.2273,8.5563,2.2617,0.2607,9.7718,2.1136,0.1466,10.3796,2.0396,0.0808,10.9873,1.9655,0.0702,11.5951,1.8915,0.0924,12.2028,1.8174,0.1051,12.2028,1.8174,0.3012,11.5951,1.8915,0.2882,10.9873,1.9655,0.2645,10.3796,2.0396,0.2762,9.7718,2.1136,0.3433,9.1641,2.1876,0.4251,7.9486,2.3357,0.4251,8.5563,2.2617,0.4589,7.3409,2.4098,0.3433,6.7331,2.4838,0.2774,6.1254,2.5578,0.2527,6.8072,3.0916,0.1832,6.1994,3.1656,0.1314,7.4149,3.0175,0.142,8.0226,2.9435,0.0965,9.2381,2.7954,0.0892,8.6304,2.8694,0.1007,9.8459,2.7213,0.0691,10.4536,2.6473,0.0832,11.0614,2.5733,0.1557,11.6691,2.4992,0.2448,12.2768,2.4252,0.2817,12.2768,2.4252,0.4803,11.6691,2.4992,0.443,11.0614,2.5733,0.3527,10.4536,2.6473,0.2787,9.8459,2.7213,0.2634,9.2381,2.7954,0.2848,8.0226,2.9435,0.2918,8.6304,2.8694,0.2966,7.4149,3.0175,0.3403,6.8072,3.0916,0.383,6.1994,3.1656,0.3297,6.8812,3.6993,0.4878,6.2735,3.7733,0.3681,7.4889,3.6253,0.3681,8.7044,3.4772,0.0585,8.0967,3.5512,0.1314,9.3122,3.4031,0.0585,9.9199,3.3291,0.0702,10.5277,3.2551,0.1557,11.1354,3.181,0.3264,11.7431,3.107,0.5025,12.3509,3.0329,0.5673,12.3509,3.0329,0.7659,11.7431,3.107,0.7016,11.1354,3.181,0.53,10.5277,3.2551,0.3527,9.9199,3.3291,0.2645,9.3122,3.4031,0.2527,8.7044,3.4772,0.2527,7.4889,3.6253,0.5731,8.0967,3.5512,0.3297,6.8812,3.6993,0.6946,6.2735,3.7733,0.5731,6.9552,4.307,0.5934,6.3475,4.3811,0.4878,7.563,4.233,0.4878,8.1707,4.159,0.1832,9.3862,4.0109,0.0585,8.7785,4.0849,0.0593,9.994,3.9368,0.0924,10.6017,3.8628,0.2448,11.2094,3.7888,0.5025,11.8172,3.7147,0.6923,12.4249,3.6407,0.7496,12.4249,3.6407,0.9494,11.8172,3.7147,0.8923,11.2094,3.7888,0.7016,10.6017,3.8628,0.443,9.994,3.9368,0.2882,8.7785,4.0849,0.254,9.3862,4.0109,0.2527,8.1707,4.159,0.383,7.563,4.233,0.6946,6.9552,4.307,0.7962,6.3475,4.3811,0.6946,7.0293,4.9148,0.6946,6.4215,4.9888,0.5731,6.4215,4.9888,0.3681,7.637,4.8407,0.3681,7.637,4.8407,0.5731,8.2448,4.7667,0.1314,8.2448,4.7667,0.3297,8.8525,4.6927,0.0585,8.8525,4.6927,0.2527,9.4603,4.6186,0.2527,10.068,4.5446,0.3012,9.4603,4.6186,0.0585,10.6757,4.4705,0.4803,10.068,4.5446,0.1051,11.2835,4.3965,0.7659,10.6757,4.4705,0.2817,11.8912,4.3225,0.9494,11.2835,4.3965,0.5673,12.499,4.2484,0.9978,11.8912,4.3225,0.7496,7.0293,4.9148,0.4878,12.499,4.2484,0.7993,-0.9599,-4.0118,0.1956,-0.9999,-4.0118,0.1956,-0.9999,-4.0118,2.1956,-0.9599,-3.9718,2.1956,-0.9999,-3.9718,2.1956,-0.9999,-3.9718,0.1956,-0.9599,-3.9718,0.1956,-0.9599,-4.0118,2.1956,-0.3164,-1.8756,2.1425,-0.3564,-1.8756,2.1425,-0.3564,-1.8756,4.1425,-0.3164,-1.8356,4.1425,-0.3564,-1.8356,4.1425,-0.3564,-1.8356,2.1425,-0.3164,-1.8356,2.1425,-0.3164,-1.8756,4.1425,5.328,-8.5937,0.2762,5.328,-8.5937,0.0808,4.7203,-8.5196,0.0585,5.9357,-8.6677,0.3433,5.9357,-8.6677,0.1466,6.5435,-8.7417,0.4251,6.5435,-8.7417,0.2273,7.1512,-8.8158,0.4589,7.1512,-8.8158,0.2607,7.759,-8.8898,0.2273,8.3667,-8.9639,0.1282,7.759,-8.8898,0.4251,8.9744,-9.0379,-0.0071,8.3667,-8.9639,0.3249,9.5822,-9.1119,-0.1421,8.9744,-9.0379,0.1865,10.1899,-9.186,-0.214,9.5822,-9.1119,0.0505,10.7977,-9.26,-0.2335,10.1899,-9.186,-0.0219,5.402,-7.9859,0.1466,4.7943,-7.9119,0.0691,6.0098,-8.06,0.3012,6.6175,-8.134,0.4608,7.2253,-8.208,0.5195,7.833,-8.2821,0.4608,8.4407,-8.3561,0.2873,9.0485,-8.4302,0.0731,9.6562,-8.5042,-0.1059,10.264,-8.5782,-0.1914,10.8717,-8.6523,-0.0219,10.8717,-8.6523,-0.214,10.264,-8.5782,0.0006,10.7977,-9.26,-0.0409,9.6562,-8.5042,0.0865,9.0485,-8.4302,0.2699,8.4407,-8.3561,0.4906,7.833,-8.2821,0.6594,7.2253,-8.208,0.7178,6.6175,-8.134,0.6594,6.0098,-8.06,0.504,5.402,-7.9859,0.3433,4.7203,-8.5196,0.2528,4.7943,-7.9119,0.2634,5.4761,-7.3782,0.2273,4.8683,-7.3041,0.0892,6.0838,-7.4522,0.4608,6.6916,-7.5263,0.6328,7.2993,-7.6003,0.6847,7.907,-7.6743,0.6328,8.5148,-7.7484,0.4561,9.1225,-7.8224,0.1879,9.7303,-7.8965,-0.0182,10.338,-7.9705,-0.1165,10.9458,-8.0445,0.0505,10.9458,-8.0445,-0.1421,10.338,-7.9705,0.0758,9.7303,-7.8965,0.1756,9.1225,-7.8224,0.3868,8.5148,-7.7484,0.6557,7.907,-7.6743,0.8323,7.2993,-7.6003,0.884,6.6916,-7.5263,0.8323,6.0838,-7.4522,0.6594,5.4761,-7.3782,0.4251,4.8683,-7.3041,0.2848,5.5501,-6.7704,0.2607,4.9424,-6.6964,0.1007,6.1579,-6.8445,0.5195,6.7656,-6.9185,0.6847,7.3733,-6.9926,0.7297,7.9811,-7.0666,0.6847,8.5888,-7.1406,0.5195,9.1966,-7.2147,0.2505,9.8043,-7.2887,0.0614,10.4121,-7.3628,-0.015,11.0198,-7.4368,0.1631,11.0198,-7.4368,-0.0295,10.4121,-7.3628,0.1778,9.8043,-7.2887,0.2572,9.1966,-7.2147,0.4492,8.5888,-7.1406,0.7178,7.9811,-7.0666,0.884,7.3733,-6.9926,0.9278,6.7656,-6.9185,0.884,6.1579,-6.8445,0.7178,5.5501,-6.7704,0.4589,4.9424,-6.6964,0.2966,5.0164,-6.0887,0.0892,5.6242,-6.1627,0.2273,6.2319,-6.2367,0.4608,6.8396,-6.3108,0.6328,8.0551,-6.4589,0.6328,7.4474,-6.3848,0.6847,8.6629,-6.5329,0.4608,9.2706,-6.6069,0.2273,9.8783,-6.681,0.0846,10.4861,-6.755,0.0451,11.0938,-6.8291,0.2337,11.0938,-6.8291,0.0401,10.4861,-6.755,0.2388,9.8783,-6.681,0.2802,9.2706,-6.6069,0.4251,8.6629,-6.5329,0.6594,8.0551,-6.4589,0.8323,6.8396,-6.3108,0.8323,7.4474,-6.3848,0.884,6.2319,-6.2367,0.6594,5.6242,-6.1627,0.4251,5.0164,-6.0887,0.2848,5.0905,-5.4809,0.0691,5.6982,-5.555,0.1466,6.3059,-5.629,0.3012,6.9137,-5.703,0.4608,8.1292,-5.8511,0.4608,7.5214,-5.7771,0.5195,8.7369,-5.9252,0.3012,9.3446,-5.9992,0.1466,9.9524,-6.0732,0.0691,10.5601,-6.1473,0.0585,11.1679,-6.2213,0.2528,11.1679,-6.2213,0.0585,10.5601,-6.1473,0.2528,9.9524,-6.0732,0.2634,9.3446,-5.9992,0.3433,8.7369,-5.9252,0.504,8.1292,-5.8511,0.6594,6.9137,-5.703,0.6594,7.5214,-5.7771,0.7178,6.3059,-5.629,0.504,5.6982,-5.555,0.3433,5.0905,-5.4809,0.2634,5.1645,-4.8732,0.0585,5.7722,-4.9472,0.0817,6.38,-5.0213,0.1466,6.9877,-5.0953,0.2273,8.2032,-5.2434,0.2273,7.5955,-5.1693,0.2607,8.8109,-5.3174,0.1466,9.4187,-5.3915,0.0808,10.0264,-5.4655,0.0702,10.6342,-5.5395,0.0924,11.2419,-5.6136,0.1051,11.2419,-5.6136,0.3012,10.6342,-5.5395,0.2882,10.0264,-5.4655,0.2645,9.4187,-5.3915,0.2762,8.8109,-5.3174,0.3433,8.2032,-5.2434,0.4251,6.9877,-5.0953,0.4251,7.5955,-5.1693,0.4589,6.38,-5.0213,0.3433,5.7722,-4.9472,0.2774,5.1645,-4.8732,0.2528,5.8463,-4.3395,0.1832,5.2385,-4.2654,0.1314,6.454,-4.4135,0.142,7.0618,-4.4876,0.0965,8.2772,-4.6356,0.0892,7.6695,-4.5616,0.1007,8.885,-4.7097,0.0691,9.4927,-4.7837,0.0832,10.1005,-4.8578,0.1557,10.7082,-4.9318,0.2448,11.316,-5.0058,0.2817,11.316,-5.0058,0.4803,10.7082,-4.9318,0.443,10.1005,-4.8578,0.3527,9.4927,-4.7837,0.2787,8.885,-4.7097,0.2634,8.2772,-4.6356,0.2848,7.0618,-4.4876,0.2918,7.6695,-4.5616,0.2966,6.454,-4.4135,0.3403,5.8463,-4.3395,0.383,5.2385,-4.2654,0.3297,5.9203,-3.7317,0.4878,5.3126,-3.6577,0.3682,6.5281,-3.8058,0.3682,7.7435,-3.9539,0.0585,7.1358,-3.8798,0.1314,8.3513,-4.0279,0.0585,8.959,-4.1019,0.0702,9.5668,-4.176,0.1557,10.1745,-4.25,0.3264,10.7823,-4.3241,0.5025,11.39,-4.3981,0.5673,11.39,-4.3981,0.7659,10.7823,-4.3241,0.7016,10.1745,-4.25,0.53,9.5668,-4.176,0.3527,8.959,-4.1019,0.2645,8.3513,-4.0279,0.2528,7.7435,-3.9539,0.2528,6.5281,-3.8058,0.5731,7.1358,-3.8798,0.3297,5.9203,-3.7317,0.6946,5.3126,-3.6577,0.5731,5.9944,-3.124,0.5934,5.3866,-3.05,0.4878,6.6021,-3.198,0.4878,7.2098,-3.2721,0.1832,7.8176,-3.3461,0.0593,9.0331,-3.4942,0.0924,8.4253,-3.4202,0.0585,9.6408,-3.5682,0.2448,10.2485,-3.6423,0.5025,10.8563,-3.7163,0.6923,11.464,-3.7904,0.7496,11.464,-3.7904,0.9494,10.8563,-3.7163,0.8923,10.2485,-3.6423,0.7016,9.6408,-3.5682,0.443,9.0331,-3.4942,0.2882,7.8176,-3.3461,0.254,8.4253,-3.4202,0.2528,7.2098,-3.2721,0.383,6.6021,-3.198,0.6946,5.9944,-3.124,0.7962,5.3866,-3.05,0.6946,6.0684,-2.5162,0.6946,5.4607,-2.4422,0.5731,5.4607,-2.4422,0.3682,6.6761,-2.5903,0.3682,6.6761,-2.5903,0.5731,7.2839,-2.6643,0.1314,7.2839,-2.6643,0.3297,7.8916,-2.7384,0.0585,7.8916,-2.7384,0.2528,8.4994,-2.8124,0.2528,9.1071,-2.8865,0.3012,8.4994,-2.8124,0.0585,9.7148,-2.9605,0.4803,9.1071,-2.8865,0.1051,10.3226,-3.0345,0.7659,9.7148,-2.9605,0.2817,10.9303,-3.1086,0.9494,10.3226,-3.0345,0.5673,11.5381,-3.1826,0.9978,10.9303,-3.1086,0.7496,6.0684,-2.5162,0.4878,11.5381,-3.1826,0.7993,7.8078,-5.9389,0.1956,7.7678,-5.9389,0.1956,7.7678,-5.9389,2.1956,7.8078,-5.8989,2.1956,7.7678,-5.8989,2.1956,7.7678,-5.8989,0.1956,7.8078,-5.8989,0.1956,7.8078,-5.9389,2.1956] 13 | ,"normals":[0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,-0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.643,0.5773,0.5033,-0.6429,-0.5774,-0.6429,-0.5033,-0.5774,0.643,0.5033,0.5772,-0.5033,0.643,0.5772,-0.5032,0.6429,-0.5774,0.6428,0.5033,-0.5774,-0.643,-0.5033,0.5773,-0.2629,0.3356,0.9046,0.3356,0.2627,0.9046,0.3359,0.2629,-0.9044,-0.3359,-0.2629,0.9044,-0.3356,-0.2628,-0.9046,0.2629,-0.3356,-0.9046,-0.2628,0.3359,-0.9044,-0.2632,0.3358,-0.9044,-0.2625,0.3357,0.9046,0.3359,0.2626,0.9045,-0.3359,-0.263,-0.9044,0.2625,-0.3358,-0.9046,0.2631,-0.3357,0.9044,-0.3355,-0.2627,0.9046,0.3356,0.263,-0.9045,-0.2626,0.3356,0.9046,0.3355,0.2629,0.9046,0.3361,0.2628,-0.9044,-0.3357,-0.2622,0.9048,-0.3358,-0.2635,-0.9043,0.2629,-0.3363,-0.9043,0.2628,-0.3352,0.9047,0.2627,-0.3359,0.9045,-0.263,0.3359,-0.9044,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.0266,0.816,-0.5773,0.816,-0.0266,0.5773,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.0266,0.816,-0.5773,0.816,-0.0266,0.5773,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,-0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.816,-0.0266,0.5773,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.0266,0.816,-0.5773,0.816,-0.0266,0.5773,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.0266,0.816,-0.5773,0.816,-0.0266,0.5773,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.0266,0.816,-0.5773,0.816,-0.0266,0.5773,0.816,-0.0266,-0.5773,-0.0266,-0.816,-0.5773,-0.0266,-0.816,0.5773,0.0266,0.816,-0.5773,0.0266,0.816,0.5773,-0.816,0.0266,0.5773,-0.816,0.0266,-0.5773,0.816,-0.0266,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.7934,-0.1929,-0.5773,-0.1929,-0.7934,-0.5773,-0.1929,-0.7934,0.5773,0.1929,0.7934,0.5773,-0.7934,0.1929,0.5773,-0.7934,0.1929,-0.5773,0.1929,0.7934,-0.5773,0.7934,-0.1929,0.5773,0.315,-0.7533,0.5773,0.315,-0.7533,-0.5773,-0.7533,-0.315,-0.5773,0.7533,0.315,0.5773,-0.315,0.7533,0.5773,-0.315,0.7533,-0.5773,0.7533,0.315,-0.5773,-0.7533,-0.315,0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,-0.6429,-0.5033,0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,0.5033,-0.6429,0.5773,-0.3459,-0.8795,0.3268,-0.4634,-0.4748,-0.7482,-0.763,0.5576,-0.3268,0.763,-0.5576,0.3268,0.4634,0.4748,0.7482,0.3459,0.8795,-0.3268,0.6456,-0.1529,-0.7482,-0.6456,0.1529,0.7482,0.5033,-0.6429,0.5773,0.5033,-0.6429,-0.5773,-0.6429,-0.5033,-0.5773,0.6429,0.5033,0.5773,-0.5033,0.6429,0.5773,-0.5033,0.6429,-0.5773,0.6429,0.5033,-0.5773,-0.6429,-0.5033,0.5773,-0.1025,-0.81,0.5773,-0.1025,-0.81,-0.5773,-0.81,0.1025,-0.5773,0.81,-0.1025,0.5773,0.1025,0.81,0.5773,0.1025,0.81,-0.5773,0.81,-0.1025,-0.5773,-0.81,0.1025,0.5773,-0.4599,0.6749,0.577,-0.6753,-0.4596,0.5768,-0.6746,-0.4596,-0.5777,-0.9434,-0.1493,0.2962,-0.4592,0.6746,-0.5779,-0.9438,-0.1508,-0.294,-0.8414,0.4432,0.3091,0.6648,0.494,-0.5604,-0.4751,0.6796,-0.5589,-0.6605,-0.4595,-0.5937,0.4791,-0.6451,-0.5952,0.4744,-0.6808,0.558,-0.4797,0.6442,0.5957,0.6606,0.4585,0.5945,-0.3459,0.7308,-0.5885,-0.6649,-0.4951,0.5592,0.6647,0.494,-0.5605,-0.475,0.6796,-0.559,-0.6605,-0.4595,-0.5938,-0.4793,0.6445,0.5957,0.6603,0.4588,0.5945,0.4745,-0.6804,0.5584,-0.6647,-0.4947,0.5598,0.2173,0.4297,0.8764,0.5578,-0.6373,-0.5317,0.479,-0.6451,-0.5952,0.2936,-0.4452,-0.8459,0.4154,-0.5424,0.7302,0.5352,-0.7437,-0.4005,-0.5986,-0.5832,-0.5492,0.5986,0.5832,0.5491,-0.5352,0.7437,0.4005,-0.4154,0.5424,-0.7302,-0.1549,0.9467,0.2825,-0.7183,-0.3819,0.5815,-0.3581,-0.8706,-0.3372,0.7183,0.3818,-0.5815,0.5315,0.8162,-0.2266,0.3629,0.8682,0.3384,0.1348,-0.9493,-0.2838,-0.4746,-0.0615,0.878,0.1575,-0.9308,0.3298,-0.5138,-0.8271,0.2279,-0.2178,-0.43,-0.8761,-0.201,0.2362,-0.9507,0.4735,0.0612,-0.8786,-0.8414,0.4449,-0.3068,0.7516,-0.0916,-0.6532,0.7516,-0.0916,0.6532,0.6967,0.2965,0.6532,0.6967,0.2965,-0.6532,0.4551,0.6051,0.6532,0.4551,0.6051,-0.6532,0.0916,0.7516,0.6532,0.0916,0.7516,-0.6532,-0.2965,0.6967,0.6532,-0.2965,0.6967,-0.6532,-0.6051,0.4551,0.6532,-0.6051,0.4551,-0.6532,-0.7516,0.0916,0.6532,-0.7516,0.0916,-0.6532,-0.6967,-0.2965,0.6532,-0.6967,-0.2965,-0.6532,-0.4551,-0.6051,0.6532,-0.0916,-0.7516,-0.6532,-0.4551,-0.6051,-0.6532,-0.0916,-0.7516,0.6532,0.2965,-0.6967,0.6532,0.2965,-0.6967,-0.6532,0.6051,-0.4551,0.6532,0.6051,-0.4551,-0.6532,-0.6,-0.1995,-0.7747,-0.498,0.8279,-0.2578,-0.5491,0.3135,0.7747,0.5491,-0.3135,-0.7747,0.498,-0.828,0.2578,0.6,0.1995,0.7747,0.651,0.7139,-0.2578,-0.651,-0.7139,0.2578,-0.5983,-0.3846,-0.703,-0.5348,0.7315,-0.4229,-0.5177,0.6306,0.5781,0.511,-0.4946,-0.703,0.5175,-0.6315,0.5773,0.6316,0.5166,0.5781,0.6682,0.6121,-0.4229,-0.6315,-0.5175,0.5773,-0.6315,-0.5175,-0.5773,-0.5175,0.6315,-0.5773,-0.5175,0.6315,0.5773,0.5175,-0.6315,-0.5773,0.5175,-0.6315,0.5773,0.6315,0.5175,0.5773,0.6315,0.5175,-0.5773,-0.6315,-0.5175,0.5773,-0.1635,0.9292,-0.3314,-0.1629,-0.75,0.6411,0.0022,-0.6526,-0.7576,-0.6214,-0.4903,-0.6111,-0.1849,-0.7849,0.5914,-0.1583,-0.8061,0.5702,0.0145,-0.5798,-0.8146,0.0448,-0.6111,-0.7902,-0.0997,-0.8185,0.5657,-0.0685,-0.5622,-0.8242,-0.1657,-0.5618,-0.8104,-0.2438,-0.5852,-0.7733,-0.0305,-0.819,0.5729,-0.2631,-0.6228,-0.7368,0.0349,-0.8044,0.593,-0.2157,-0.6475,-0.7309,0.0677,-0.7762,0.6268,-0.1404,-0.6704,-0.7286,0.0326,-0.7541,0.6559,0.4939,-0.6309,-0.5983,-0.0345,-0.7327,0.6797,0.2006,0.0963,-0.9749,-0.6412,0.1159,-0.7586,0.2541,0.1989,-0.9465,0.1867,0.2748,-0.9432,0.0382,0.3135,-0.9488,-0.126,0.3132,-0.9413,-0.25,0.2677,-0.9305,-0.2824,0.1861,-0.941,-0.1971,0.1249,-0.9724,-0.0788,0.0908,-0.9927,0.7184,-0.1394,0.6815,0.6856,-0.028,-0.7274,0.079,-0.0912,0.9927,0.5121,-0.6542,0.5565,0.2004,-0.1263,0.9715,0.288,-0.1888,0.9388,0.251,-0.2701,0.9295,0.123,-0.3151,0.941,-0.0383,-0.3146,0.9484,-0.1845,-0.2772,0.9429,-0.2057,-0.0963,0.9738,-0.2557,-0.2002,0.9458,-0.6637,-0.5158,0.5417,-0.7596,0.0596,0.6477,0.2893,0.0474,-0.9561,-0.6151,0.1022,-0.7818,0.3116,0.1152,-0.9432,0.1907,0.1493,-0.9702,0.0205,0.1681,-0.9855,-0.154,0.1927,-0.9691,-0.3019,0.2021,-0.9317,-0.3352,0.1712,-0.9265,-0.2154,0.1533,-0.9644,-0.0772,0.1474,-0.9861,0.7098,-0.1866,0.6793,0.6942,0.0211,-0.7194,0.0784,-0.1484,0.9858,0.2192,-0.1557,0.9632,0.3386,-0.1726,0.925,0.3038,-0.1997,0.9315,0.1529,-0.1921,0.9694,-0.0204,-0.1679,0.9856,-0.1901,-0.1488,0.9704,-0.3134,-0.1125,0.9429,-0.2915,-0.0481,0.9553,-0.7801,0.0725,0.6213,0.3123,-0.038,-0.9492,-0.6113,0.0745,-0.7879,0.3135,-0.0382,-0.9488,0.1681,-0.0205,-0.9855,0,0,-1,-0.1689,0.0214,-0.9854,-0.3212,0.0486,-0.9457,-0.3368,0.0832,-0.9379,-0.2023,0.1113,-0.973,-0.0525,0.1327,-0.9897,0.6892,-0.1866,0.7001,0.715,0.0148,-0.6989,0.0547,-0.1337,0.9895,0.2063,-0.1128,0.972,0.3388,-0.0831,0.9372,0.3222,-0.048,0.9454,0.1685,-0.0212,0.9854,0,0,1,-0.1679,0.0204,0.9856,-0.3146,0.0383,0.9484,-0.314,0.0382,0.9486,-0.7831,0.0954,0.6145,-0.6216,0.0484,-0.7818,0.2694,-0.1155,-0.9561,0.2748,-0.1867,-0.9432,0.1493,-0.1907,-0.9702,-0.1907,-0.1493,-0.9702,-0.0205,-0.1681,-0.9855,-0.3116,-0.1152,-0.9432,-0.293,-0.0398,-0.9553,-0.1546,0.0294,-0.9875,-0.0305,0.0659,-0.9973,0.6888,-0.1349,0.7123,0.7151,-0.0381,-0.698,0.0319,-0.0673,0.9972,0.1579,-0.029,0.987,0.2952,0.0409,0.9545,0.3134,0.1125,0.9429,0.1901,0.1488,0.9704,-0.1488,0.1901,0.9704,0.0204,0.1679,0.9856,-0.2772,0.1845,0.9429,-0.2714,0.1167,0.9553,-0.7747,0.1169,0.6213,-0.6502,0.0414,-0.7586,0.1718,-0.1412,-0.9749,0.1988,-0.2539,-0.9466,0.1152,-0.3116,-0.9432,-0.1867,-0.2748,-0.9432,-0.0382,-0.3135,-0.9488,-0.2541,-0.1989,-0.9465,-0.198,-0.0943,-0.9756,-0.0794,-0.0084,-0.9968,-0.0085,0.0376,-0.9992,0.7026,-0.1245,0.7006,0.7014,-0.0474,-0.7112,0.0087,-0.039,0.9992,0.0811,0.0089,0.9966,0.2032,0.0942,0.9746,0.2557,0.2002,0.9458,0.1845,0.2772,0.9429,-0.1125,0.3134,0.9429,0.0383,0.3146,0.9484,-0.2,0.2555,0.9459,-0.1769,0.1422,0.9739,-0.7517,0.1245,0.6477,-0.6579,0.1101,-0.745,0.0662,-0.0173,-0.9977,0.1133,-0.1549,-0.9814,0.037,-0.2701,-0.9621,-0.1155,-0.2694,-0.9561,-0.0392,-0.3108,-0.9496,-0.1363,-0.1674,-0.9764,-0.0681,-0.0438,-0.9967,0.0173,0.0682,-0.9975,0.0438,0.1429,-0.9888,0.7004,0.0405,-0.7125,0.7057,-0.2078,0.6773,-0.0452,-0.1455,0.9883,-0.0175,-0.07,0.9974,0.0705,0.0448,0.9965,0.1372,0.1722,0.9754,0.1167,0.2714,0.9553,-0.037,0.2717,0.9616,0.0394,0.3125,0.9491,-0.1136,0.1572,0.981,-0.0687,0.0166,0.9975,-0.7468,0.0624,0.662,0.0395,0.302,-0.9525,-0.6123,0.2779,-0.7402,-0.0131,0.1849,-0.9827,-0.0883,-0.0509,-0.9948,-0.0393,-0.1296,-0.9908,-0.0227,-0.1578,-0.9872,-0.0096,-0.0587,-0.9982,0.0799,0.0539,-0.9953,0.1503,0.1842,-0.9713,0.1262,0.2943,-0.9473,0.7312,0.1554,-0.6642,0.6733,-0.3275,0.6628,-0.1275,-0.2965,0.9465,-0.1513,-0.1895,0.9701,-0.0826,-0.0552,0.995,0.0095,0.0604,0.9981,0.0406,0.132,0.9904,0.0919,0.0504,0.9945,0.0228,0.1608,0.9867,0.0145,-0.1906,0.9815,-0.0404,-0.3095,0.95,-0.7871,-0.0774,0.6119,0.0358,0.2802,-0.9593,-0.6328,0.2846,-0.7201,-0.2065,0.2549,-0.9446,-0.0545,-0.0015,-0.9985,-0.2403,0.1099,-0.9644,0.0089,-0.0242,-0.9997,0.088,0.0151,-0.996,0.2174,0.1037,-0.9705,0.2773,0.2171,-0.9359,0.2036,0.2996,-0.9321,0.7758,0.1334,-0.6167,0.622,-0.3377,0.7064,-0.2012,-0.3022,0.9317,-0.2791,-0.2185,0.9351,-0.223,-0.1036,0.9693,-0.0899,-0.0157,0.9958,-0.0089,0.0248,0.9996,0.0569,0.0009,0.9984,0.2101,-0.2596,0.9426,0.2474,-0.1142,0.9621,-0.036,-0.2813,0.959,-0.767,-0.0923,0.6349,0,0,-1,-0.6891,0.0839,-0.7198,-0.2821,0.0344,-0.9587,-0.3054,0.0372,-0.9515,0.0299,-0.0009,-0.9995,-0.0994,0.0123,-0.995,0.1516,0.0101,-0.9884,0.316,0.0518,-0.9473,0.3397,0.1257,-0.9321,0.2086,0.1633,-0.9643,0.7715,0.0271,-0.6357,0.6274,-0.2186,0.7474,-0.2079,-0.1628,0.9645,-0.3417,-0.1228,0.9317,-0.3184,-0.0526,0.9465,-0.1545,-0.0108,0.9879,0.1034,-0.0126,0.9945,-0.0306,0.001,0.9995,0.3133,-0.0381,0.9489,0.2832,-0.0345,0.9584,0,0,1,-0.7194,0.0876,0.689,0.0876,0.7194,0.689,-0.5479,0.7,0.458,-0.4468,0.5708,-0.6889,-0.1243,0.6826,-0.7201,0.2738,0.7224,0.6349,-0.1228,0.6611,-0.7402,0.2641,0.7455,0.6119,0.0255,0.6883,-0.7249,0.1448,0.7173,0.6816,0.0596,0.7143,0.6973,-0.0323,0.735,0.6773,0.1112,0.6901,-0.715,-0.1563,0.7323,0.6628,0.2074,0.6702,-0.7125,-0.1785,0.6849,0.7064,0.3264,0.6725,-0.6642,-0.0616,0.6615,0.7474,0.3157,0.7211,-0.6167,0.6123,0.4793,0.6288,0.2115,0.7424,-0.6357,0.0839,0.6891,-0.7198,0.6709,0.5252,-0.5234,0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,0.5773,0.5773,0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,0.5773,0.5773,0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773,-0.1629,-0.75,0.6411,0.0022,-0.6526,-0.7576,-0.6214,-0.4903,-0.6111,-0.1849,-0.7849,0.5914,0.0448,-0.6111,-0.7902,-0.1583,-0.8061,0.5702,0.0145,-0.5798,-0.8146,-0.0997,-0.8185,0.5657,-0.0685,-0.5622,-0.8242,-0.1657,-0.5618,-0.8104,-0.2438,-0.5852,-0.7733,-0.0305,-0.819,0.5729,-0.2631,-0.6228,-0.7368,0.0349,-0.8044,0.593,-0.2157,-0.6475,-0.7309,0.0677,-0.7762,0.6268,-0.1404,-0.6704,-0.7286,0.0326,-0.7541,0.6559,0.4939,-0.6309,-0.5983,-0.0345,-0.7327,0.6797,0.2006,0.0963,-0.9749,-0.6412,0.1159,-0.7586,0.2541,0.1989,-0.9465,0.1867,0.2748,-0.9432,0.0382,0.3135,-0.9488,-0.126,0.3132,-0.9413,-0.25,0.2677,-0.9305,-0.2824,0.1861,-0.941,-0.1971,0.1249,-0.9724,-0.0788,0.0908,-0.9927,0.7184,-0.1394,0.6815,0.6856,-0.028,-0.7274,0.079,-0.0912,0.9927,0.5121,-0.6542,0.5565,0.2004,-0.1263,0.9715,0.288,-0.1888,0.9388,0.251,-0.2701,0.9295,0.123,-0.3151,0.941,-0.0383,-0.3146,0.9484,-0.1845,-0.2772,0.9429,-0.2557,-0.2002,0.9458,-0.2057,-0.0963,0.9738,-0.6637,-0.5158,0.5417,-0.7596,0.0596,0.6477,0.2893,0.0474,-0.9561,-0.6151,0.1022,-0.7818,0.3116,0.1152,-0.9432,0.1907,0.1493,-0.9702,0.0205,0.1681,-0.9855,-0.154,0.1927,-0.9691,-0.3019,0.2021,-0.9317,-0.3352,0.1712,-0.9265,-0.2154,0.1533,-0.9644,-0.0772,0.1474,-0.9861,0.7098,-0.1866,0.6793,0.6942,0.0211,-0.7194,0.0784,-0.1484,0.9858,0.2192,-0.1557,0.9632,0.3386,-0.1726,0.925,0.3038,-0.1997,0.9315,0.1529,-0.1921,0.9694,-0.0204,-0.1679,0.9856,-0.1901,-0.1488,0.9704,-0.3134,-0.1125,0.9429,-0.2915,-0.0481,0.9553,-0.7801,0.0725,0.6213,0.3123,-0.038,-0.9492,-0.6113,0.0745,-0.7879,0.3135,-0.0382,-0.9488,0.1681,-0.0205,-0.9855,0,0,-1,-0.1689,0.0214,-0.9854,-0.3212,0.0486,-0.9457,-0.3463,0.0755,-0.9351,-0.1949,0.1015,-0.9755,-0.0525,0.1327,-0.9897,0.6892,-0.1866,0.7001,0.715,0.0148,-0.6989,0.0547,-0.1337,0.9895,0.2063,-0.1128,0.972,0.3388,-0.0831,0.9372,0.3222,-0.048,0.9454,0.1685,-0.0212,0.9854,0,0,1,-0.1679,0.0204,0.9856,-0.3146,0.0383,0.9484,-0.314,0.0382,0.9486,-0.7831,0.0954,0.6145,-0.6216,0.0484,-0.7818,0.2694,-0.1155,-0.9561,0.2748,-0.1867,-0.9432,0.1493,-0.1907,-0.9702,-0.1907,-0.1493,-0.9702,-0.0205,-0.1681,-0.9855,-0.3116,-0.1152,-0.9432,-0.3008,-0.0297,-0.9532,-0.1445,0.0373,-0.9888,-0.0304,0.0659,-0.9973,0.6888,-0.1349,0.7123,0.7151,-0.0381,-0.698,0.0319,-0.0673,0.9972,0.1579,-0.029,0.987,0.2952,0.0409,0.9545,0.3134,0.1125,0.9429,0.1901,0.1488,0.9704,-0.1488,0.1901,0.9704,0.0204,0.1679,0.9856,-0.2772,0.1845,0.9429,-0.2714,0.1167,0.9553,-0.7747,0.1169,0.6213,-0.6502,0.0414,-0.7586,0.1718,-0.1412,-0.9749,0.1988,-0.2539,-0.9466,0.1152,-0.3116,-0.9432,-0.1867,-0.2748,-0.9432,-0.0382,-0.3135,-0.9488,-0.2541,-0.1989,-0.9465,-0.198,-0.0943,-0.9756,-0.0794,-0.0084,-0.9968,-0.0085,0.0376,-0.9992,0.7026,-0.1245,0.7006,0.7014,-0.0474,-0.7112,0.0087,-0.039,0.9992,0.0811,0.0089,0.9966,0.2032,0.0942,0.9746,0.2557,0.2002,0.9458,0.1845,0.2772,0.9429,-0.1125,0.3134,0.9429,0.0383,0.3146,0.9484,-0.2,0.2555,0.9459,-0.1769,0.1422,0.9739,-0.7517,0.1245,0.6477,-0.6579,0.1101,-0.745,0.0662,-0.0173,-0.9977,0.1133,-0.1549,-0.9814,0.037,-0.2701,-0.9621,-0.1155,-0.2694,-0.9561,-0.0392,-0.3108,-0.9496,-0.1363,-0.1674,-0.9764,-0.0681,-0.0438,-0.9967,0.0173,0.0682,-0.9975,0.0438,0.1429,-0.9888,0.7004,0.0405,-0.7125,0.7057,-0.2078,0.6773,-0.0452,-0.1455,0.9883,-0.0175,-0.07,0.9974,0.0705,0.0448,0.9965,0.1372,0.1722,0.9754,0.1167,0.2714,0.9553,-0.037,0.2717,0.9616,0.0394,0.3125,0.9491,-0.1136,0.1572,0.981,-0.0687,0.0166,0.9975,-0.7468,0.0624,0.662,0.0395,0.302,-0.9525,-0.6123,0.2779,-0.7402,-0.0131,0.1849,-0.9827,-0.0883,-0.0509,-0.9948,-0.0393,-0.1296,-0.9908,-0.0227,-0.1578,-0.9872,-0.0096,-0.0587,-0.9982,0.0799,0.0539,-0.9953,0.1503,0.1842,-0.9713,0.1262,0.2943,-0.9473,0.7312,0.1554,-0.6642,0.6733,-0.3275,0.6628,-0.1275,-0.2965,0.9465,-0.1513,-0.1895,0.9701,-0.0826,-0.0552,0.995,0.0095,0.0604,0.9981,0.0406,0.132,0.9904,0.0919,0.0504,0.9945,0.0228,0.1608,0.9867,0.0145,-0.1906,0.9815,-0.0404,-0.3095,0.95,-0.7871,-0.0774,0.6119,0.0358,0.2802,-0.9593,-0.6328,0.2846,-0.7201,-0.2065,0.2549,-0.9446,-0.0544,-0.0013,-0.9985,-0.2403,0.1099,-0.9644,0.0088,-0.024,-0.9997,0.088,0.0151,-0.996,0.2174,0.1037,-0.9705,0.2773,0.2171,-0.9359,0.2036,0.2996,-0.9321,0.7758,0.1334,-0.6167,0.622,-0.3377,0.7064,-0.2012,-0.3022,0.9317,-0.2791,-0.2185,0.9351,-0.223,-0.1036,0.9693,-0.0899,-0.0157,0.9958,-0.0089,0.0248,0.9996,0.0569,0.0009,0.9984,0.2101,-0.2596,0.9426,0.2474,-0.1142,0.9621,-0.036,-0.2813,0.959,-0.767,-0.0923,0.6349,0,0,-1,-0.6891,0.0839,-0.7198,-0.2821,0.0344,-0.9587,-0.3054,0.0372,-0.9515,-0.0993,0.0121,-0.995,0.1516,0.0101,-0.9884,0.0297,-0.001,-0.9995,0.316,0.0518,-0.9473,0.3397,0.1257,-0.9321,0.2086,0.1633,-0.9643,0.7715,0.0271,-0.6357,0.6274,-0.2186,0.7474,-0.2079,-0.1628,0.9645,-0.3417,-0.1228,0.9317,-0.3184,-0.0526,0.9465,-0.1545,-0.0108,0.9879,0.1034,-0.0126,0.9945,-0.0306,0.0011,0.9995,0.3133,-0.0381,0.9489,0.2832,-0.0345,0.9584,0,0,1,-0.7194,0.0876,0.689,0.0876,0.7194,0.689,-0.5479,0.7,0.458,-0.4468,0.5708,-0.6889,-0.1243,0.6826,-0.7201,0.2738,0.7224,0.6349,-0.1228,0.6611,-0.7402,0.2641,0.7455,0.6119,0.0255,0.6883,-0.7249,0.1448,0.7173,0.6816,0.0596,0.7143,0.6973,-0.0323,0.735,0.6773,0.1112,0.6901,-0.715,-0.1563,0.7323,0.6628,0.2074,0.6702,-0.7125,-0.1785,0.6849,0.7064,0.3264,0.6725,-0.6642,-0.0616,0.6615,0.7474,0.3157,0.7211,-0.6167,0.6123,0.4793,0.6288,0.2115,0.7424,-0.6357,0.0839,0.6891,-0.7198,0.6709,0.5252,-0.5234,0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,0.5773,0.5773,0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773] 14 | ,"uvs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.995,0.995,0.0049,0.995,0.0049,0.995,0.995,0.0049,0.0049,0.0049,0.0049,0.0049,0.995,0.0049,0.995,0.995,0.995,0.995,0.0049,0.995,0.0049,0.995,0.995,0.0049,0.0049,0.0049,0.0049,0.0049,0.995,0.0049,0.995,0.995,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.995,0.995,0.0049,0.995,0.0049,0.995,0.995,0.0049,0.0049,0.0049,0.0049,0.0049,0.995,0.0049,0.995,0.995] 15 | ,"indices":[0,1,2,3,4,5,3,6,1,7,4,3,2,5,4,6,5,2,8,9,10,11,12,13,14,13,9,14,8,15,11,15,10,13,12,10,16,17,18,19,20,21,21,17,16,22,16,23,19,23,18,20,18,17,24,25,26,27,28,29,30,29,25,30,24,31,27,31,26,29,28,26,32,33,34,35,36,37,38,37,34,35,38,33,35,32,39,36,39,34,40,41,42,43,44,45,46,45,41,43,46,40,43,47,42,45,44,42,48,49,50,51,52,53,54,53,49,54,48,55,51,55,50,52,50,49,56,57,58,59,60,61,62,57,56,60,63,58,64,65,66,67,68,69,67,64,70,69,66,65,71,72,73,74,75,76,74,77,72,75,78,73,79,80,81,82,83,84,83,82,79,84,81,80,56,72,77,59,77,76,59,82,85,82,76,73,85,80,63,80,86,63,65,63,86,65,64,56,86,80,79,79,73,67,72,56,73,64,67,73,57,70,66,78,68,70,71,78,57,57,62,74,75,74,62,84,83,62,83,87,78,81,69,68,87,68,78,69,58,66,84,61,58,81,58,69,88,89,90,91,92,93,94,93,89,94,88,95,91,95,90,92,90,89,96,97,98,99,100,101,101,97,96,99,102,96,100,99,103,101,100,98,104,105,106,107,108,109,107,110,105,107,104,111,108,111,106,109,106,105,112,113,114,115,116,117,117,113,112,115,118,112,115,119,114,116,114,113,120,121,122,123,124,125,125,121,120,126,120,127,123,127,122,125,124,122,128,129,130,131,132,133,133,129,128,131,134,128,132,131,135,133,132,130,136,137,138,139,140,141,142,137,136,139,136,143,141,140,143,141,138,137,144,145,146,147,148,149,149,145,144,147,150,144,147,151,146,148,146,145,152,153,154,155,156,157,157,153,152,158,152,159,155,159,154,156,154,153,160,161,162,163,164,165,165,161,160,166,160,167,163,167,162,164,162,161,168,169,170,171,172,173,173,169,168,174,168,175,171,175,170,173,172,170,176,177,178,179,180,181,181,177,176,182,176,183,179,183,178,181,180,178,184,185,186,187,188,189,190,189,185,190,184,191,187,191,186,188,186,185,192,193,194,195,196,197,198,197,194,198,193,192,195,192,199,196,199,194,200,201,202,203,204,205,206,205,201,206,200,207,203,207,202,205,204,202,208,209,210,211,212,213,213,209,208,214,208,215,212,211,215,213,212,210,216,217,218,219,220,221,221,218,217,219,222,217,220,219,216,220,223,218,224,225,226,227,228,229,229,226,225,230,225,224,228,227,224,229,228,231,232,233,234,235,236,237,238,237,233,238,232,239,235,239,234,237,236,234,240,241,242,243,244,245,245,241,240,246,240,247,243,247,242,244,242,241,248,249,250,251,252,253,253,249,248,254,248,255,251,255,250,252,250,249,256,257,258,259,260,261,261,258,257,259,262,257,259,256,263,260,263,258,264,265,266,267,268,269,269,266,265,270,265,264,267,264,271,269,268,271,272,273,274,275,276,277,277,274,273,278,273,272,275,272,279,276,279,274,280,281,282,282,281,283,284,282,285,280,286,283,287,288,289,287,290,291,292,293,291,294,292,295,296,297,298,299,300,301,299,302,298,303,304,301,296,305,306,307,308,309,310,311,312,312,311,313,314,309,315,316,317,318,319,308,307,313,311,310,303,310,318,320,304,303,314,320,321,320,314,322,323,324,325,326,288,324,326,285,289,285,283,323,323,283,286,284,326,286,327,328,329,330,329,331,332,331,333,334,333,335,336,335,337,338,337,339,340,339,341,342,341,343,344,345,343,344,346,347,348,347,349,327,350,349,351,352,353,354,355,356,354,357,352,354,351,358,356,355,358,356,353,352,359,360,361,362,363,364,365,360,359,363,362,359,363,366,361,365,364,361,367,368,369,370,371,372,370,373,368,370,367,374,371,374,369,372,369,368,319,321,291,349,347,346,297,375,313,324,317,316,308,319,323,322,315,298,293,318,317,344,348,350,376,377,378,377,376,379,380,381,382,383,384,381,385,384,383,386,385,387,388,386,389,390,388,391,392,390,393,394,392,395,396,397,378,398,396,377,399,398,382,400,399,381,400,384,385,401,385,386,402,386,388,403,388,390,404,390,392,405,392,394,406,407,394,408,406,409,410,408,395,411,410,393,412,411,391,413,412,389,414,413,387,414,383,380,415,380,379,376,416,417,418,419,416,419,418,378,420,421,397,422,420,396,423,422,398,424,423,399,424,400,401,425,401,402,426,402,403,427,403,404,428,404,405,429,405,407,430,431,407,432,430,406,433,432,408,434,433,410,435,434,411,436,435,412,437,436,413,437,414,415,438,415,417,439,417,416,440,416,419,441,419,397,442,443,421,444,442,420,445,444,422,446,445,423,446,424,425,447,425,426,448,426,427,449,427,428,450,428,429,451,429,431,452,453,431,454,452,430,455,454,432,456,455,433,457,456,434,458,457,435,459,458,436,459,437,438,460,438,439,461,439,440,462,440,441,463,441,421,464,443,442,465,442,444,466,444,445,467,445,446,468,469,446,470,468,447,471,470,448,472,471,449,472,450,451,473,451,453,474,475,453,476,474,452,477,476,454,477,455,456,478,456,457,479,457,458,480,458,459,481,482,459,483,481,460,484,483,461,485,484,462,464,485,463,486,464,465,487,465,466,488,466,467,489,467,469,490,491,469,492,490,468,493,492,470,494,493,471,495,494,472,495,473,475,496,497,475,498,496,474,498,476,477,499,477,478,500,478,479,501,479,480,502,480,482,503,504,482,505,503,481,506,505,483,507,506,484,486,507,485,508,486,487,509,487,488,510,488,489,511,489,491,512,513,491,514,512,490,515,514,492,516,515,493,517,516,494,518,517,495,519,518,497,519,496,498,520,498,499,521,499,500,522,500,501,523,501,502,524,502,504,525,526,504,527,525,503,528,527,505,529,528,506,508,529,507,530,531,508,532,530,509,532,510,511,533,511,513,534,535,513,536,534,512,537,536,514,538,537,515,539,538,516,540,539,517,541,540,518,541,519,520,542,520,521,543,521,522,544,522,523,545,523,524,546,524,526,547,548,526,549,547,525,549,527,528,550,528,529,551,529,508,552,553,531,552,530,532,554,532,533,555,556,533,557,555,535,558,557,534,559,558,536,560,559,537,561,560,538,562,561,539,563,562,540,563,541,542,564,542,543,565,543,544,566,544,545,567,545,546,568,546,548,569,548,547,570,571,547,572,570,549,572,550,551,573,551,531,574,575,553,574,552,554,576,554,556,577,556,555,578,579,555,580,578,557,581,580,558,582,581,559,583,582,560,584,583,561,585,584,562,585,563,564,586,564,565,587,565,566,588,566,567,589,567,568,590,591,568,592,590,569,593,592,571,594,593,570,594,572,573,595,573,553,596,597,598,599,600,596,601,602,600,603,604,602,605,604,603,606,605,607,608,606,609,610,608,611,612,610,613,614,612,615,598,575,574,599,616,574,601,599,576,603,601,577,607,603,579,609,607,578,611,609,580,613,611,581,615,613,582,617,615,583,614,617,584,614,585,586,612,586,587,610,587,588,608,588,589,606,589,591,605,591,590,604,590,592,602,592,593,600,593,594,597,596,594,598,597,595,618,619,620,621,622,623,623,619,618,621,624,618,621,625,620,622,620,619,626,627,628,629,630,631,631,627,626,632,626,633,629,633,628,630,628,627,634,635,636,637,638,635,639,640,638,641,642,640,643,642,641,644,643,645,646,644,647,648,646,649,650,648,651,652,650,653,654,655,636,656,654,635,657,656,638,658,657,640,658,642,643,659,643,644,660,644,646,661,646,648,662,648,650,663,650,652,664,665,652,666,664,667,668,666,653,669,668,651,670,669,649,671,670,647,672,671,645,672,641,639,673,639,637,674,637,634,675,634,676,677,676,636,678,679,655,680,678,654,681,680,656,682,681,657,682,658,659,683,659,660,684,660,661,685,661,662,686,662,663,687,663,665,688,689,665,690,688,664,691,690,666,692,691,668,693,692,669,694,693,670,695,694,671,695,672,673,696,673,674,697,674,675,698,675,677,699,677,655,700,701,679,702,700,678,703,702,680,704,703,681,704,682,683,705,683,684,706,684,685,707,685,686,708,686,687,709,687,689,710,711,689,712,710,688,713,712,690,714,713,691,715,714,692,716,715,693,717,716,694,717,695,696,718,696,697,719,697,698,720,698,699,721,699,679,722,701,700,723,700,702,724,702,703,725,703,704,726,727,704,728,726,705,729,728,706,729,707,708,730,708,709,731,709,711,732,733,711,734,732,710,735,734,712,735,713,714,736,714,715,737,715,716,738,716,717,739,740,717,741,739,718,742,741,719,743,742,720,722,743,721,744,722,723,745,723,724,746,724,725,747,725,727,748,749,727,750,748,726,751,750,728,752,751,729,753,752,730,753,731,733,754,755,733,756,754,732,756,734,735,757,735,736,758,736,737,759,737,738,760,738,740,761,762,740,763,761,739,764,763,741,765,764,742,744,765,743,766,744,745,767,745,746,768,746,747,769,747,749,770,771,749,772,770,748,773,772,750,774,773,751,775,774,752,776,775,753,777,776,755,777,754,756,778,756,757,779,757,758,780,758,759,781,759,760,782,760,762,783,784,762,785,783,761,786,785,763,787,786,764,766,787,765,788,789,766,790,788,767,790,768,769,791,769,771,792,793,771,794,792,770,795,794,772,796,795,773,797,796,774,798,797,775,799,798,776,799,777,778,800,778,779,801,779,780,802,780,781,803,781,782,804,782,784,805,806,784,807,805,783,807,785,786,808,786,787,809,787,766,810,811,789,810,788,790,812,790,791,813,814,791,815,813,793,816,815,792,817,816,794,818,817,795,819,818,796,820,819,797,821,820,798,821,799,800,822,800,801,823,801,802,824,802,803,825,803,804,826,804,806,827,806,805,828,829,805,830,828,807,830,808,809,831,809,789,832,833,811,832,810,812,834,812,814,835,814,813,836,813,815,837,838,815,839,837,816,840,839,817,841,840,818,842,841,819,843,842,820,843,821,822,844,822,823,845,823,824,846,824,825,847,825,826,848,849,826,850,848,827,851,850,829,852,851,828,852,830,831,853,831,811,854,855,856,857,858,854,859,860,858,861,862,860,863,862,861,864,863,865,866,864,867,868,866,869,870,868,871,872,870,873,856,833,832,857,874,832,859,857,834,861,859,835,865,861,836,867,865,838,869,867,837,871,869,839,873,871,840,875,873,841,872,875,842,872,843,844,870,844,845,868,845,846,866,846,847,864,847,849,863,849,848,862,848,850,860,850,851,858,851,852,855,854,852,856,855,853,876,877,878,879,880,881,881,877,876,882,876,883,879,883,878,880,878,877,7,0,2,6,3,5,0,3,1,0,7,3,7,2,4,1,6,2,15,8,10,14,11,13,8,14,9,11,14,15,12,11,10,9,13,10,23,16,18,22,19,21,22,21,16,19,22,23,20,19,18,21,20,17,31,24,26,30,27,29,24,30,25,27,30,31,28,27,26,25,29,26,39,32,34,38,35,37,33,38,34,32,35,33,36,35,39,37,36,34,47,40,42,46,43,45,40,46,41,47,43,40,44,43,42,41,45,42,55,48,50,54,51,53,48,54,49,51,54,55,52,51,50,53,52,49,63,56,58,62,59,61,59,62,56,61,60,58,70,64,66,86,67,69,68,67,70,86,69,65,78,71,73,77,74,76,71,74,72,76,75,73,87,79,81,85,82,84,87,83,79,85,84,80,59,56,77,82,59,76,60,59,85,79,82,73,60,85,63,63,65,56,67,86,79,56,64,73,58,57,66,57,78,70,71,57,74,83,75,62,61,84,62,75,83,78,87,81,68,81,84,58,95,88,90,94,91,93,88,94,89,91,94,95,92,91,90,93,92,89,103,96,98,102,99,101,102,101,96,103,99,96,98,100,103,97,101,98,111,104,106,110,107,109,104,107,105,108,107,111,109,108,106,110,109,105,119,112,114,118,115,117,118,117,112,119,115,112,116,115,114,117,116,113,127,120,122,126,123,125,126,125,120,123,126,127,124,123,122,121,125,122,135,128,130,134,131,133,134,133,128,135,131,128,130,132,135,129,133,130,143,136,138,142,139,141,139,142,136,140,139,143,138,141,143,142,141,137,151,144,146,150,147,149,150,149,144,151,147,144,148,147,146,149,148,145,159,152,154,158,155,157,158,157,152,155,158,159,156,155,154,157,156,153,167,160,162,166,163,165,166,165,160,163,166,167,164,163,162,165,164,161,175,168,170,174,171,173,174,173,168,171,174,175,172,171,170,169,173,170,183,176,178,182,179,181,182,181,176,179,182,183,180,179,178,177,181,178,191,184,186,190,187,189,184,190,185,187,190,191,188,187,186,189,188,185,199,192,194,198,195,197,193,198,194,195,198,192,196,195,199,197,196,194,207,200,202,206,203,205,200,206,201,203,206,207,204,203,202,201,205,202,215,208,210,214,211,213,214,213,208,211,214,215,210,212,215,209,213,210,223,216,218,222,219,221,222,221,217,216,219,217,223,220,216,221,220,218,231,224,226,230,227,229,230,229,225,227,230,224,231,228,224,226,229,231,239,232,234,238,235,237,232,238,233,235,238,239,236,235,234,233,237,234,247,240,242,246,243,245,246,245,240,243,246,247,244,243,242,245,244,241,255,248,250,254,251,253,254,253,248,251,254,255,252,251,250,253,252,249,263,256,258,262,259,261,262,261,257,256,259,257,260,259,263,261,260,258,271,264,266,270,267,269,270,269,265,267,270,264,268,267,271,266,269,271,279,272,274,278,275,277,278,277,273,275,278,272,276,275,279,277,276,274,284,280,282,285,282,283,326,284,285,281,280,283,290,287,289,293,287,291,295,292,291,320,294,295,305,296,298,302,299,301,297,299,298,300,303,301,325,296,306,314,307,309,316,310,312,375,312,313,322,314,315,310,316,318,321,319,307,303,313,310,294,303,318,294,320,303,307,314,321,304,320,322,306,323,325,286,326,324,288,326,289,289,285,323,324,323,286,280,284,286,330,327,329,332,330,331,334,332,333,336,334,335,338,336,337,340,338,339,342,340,341,345,342,343,346,344,343,348,344,347,350,348,349,328,327,349,358,351,353,357,354,356,351,354,352,355,354,358,353,356,358,357,356,352,366,359,361,365,362,364,362,365,359,366,363,359,364,363,361,360,365,361,374,367,369,373,370,372,367,370,368,371,370,374,372,371,369,373,372,368,290,289,323,291,321,295,290,323,319,339,337,335,291,290,319,331,329,333,321,320,295,339,346,341,325,375,296,349,346,333,328,349,329,329,349,333,339,335,333,303,300,299,346,343,341,346,339,333,313,303,299,306,315,309,313,299,297,312,375,325,375,297,296,324,316,325,316,312,325,323,306,309,298,315,305,322,302,304,308,323,309,292,294,318,302,301,304,298,302,322,324,288,287,315,306,305,317,324,287,293,292,318,317,287,293,382,377,379,344,340,342,418,376,378,345,344,342,336,338,340,336,340,334,350,327,330,340,344,334,350,330,334,334,344,350,330,332,334,379,380,382,380,383,381,387,385,383,389,386,387,391,388,389,393,390,391,395,392,393,409,394,395,377,396,378,382,398,377,381,399,382,384,400,381,401,400,385,402,401,386,403,402,388,404,403,390,405,404,392,407,405,394,409,406,394,395,408,409,393,410,395,391,411,393,389,412,391,387,413,389,383,414,387,415,414,380,417,415,379,379,376,417,376,418,416,397,419,378,396,420,397,398,422,396,399,423,398,400,424,399,425,424,401,426,425,402,427,426,403,428,427,404,429,428,405,431,429,407,406,430,407,408,432,406,410,433,408,411,434,410,412,435,411,413,436,412,414,437,413,438,437,415,439,438,417,440,439,416,441,440,419,421,441,397,420,442,421,422,444,420,423,445,422,424,446,423,447,446,425,448,447,426,449,448,427,450,449,428,451,450,429,453,451,431,430,452,431,432,454,430,433,455,432,434,456,433,435,457,434,436,458,435,437,459,436,460,459,438,461,460,439,462,461,440,463,462,441,443,463,421,465,464,442,466,465,444,467,466,445,469,467,446,447,468,446,448,470,447,449,471,448,450,472,449,473,472,451,475,473,453,452,474,453,454,476,452,455,477,454,478,477,456,479,478,457,480,479,458,482,480,459,460,481,459,461,483,460,462,484,461,463,485,462,443,464,463,487,486,465,488,487,466,489,488,467,491,489,469,468,490,469,470,492,468,471,493,470,472,494,471,473,495,472,497,495,475,474,496,475,476,498,474,499,498,477,500,499,478,501,500,479,502,501,480,504,502,482,481,503,482,483,505,481,484,506,483,485,507,484,464,486,485,509,508,487,510,509,488,511,510,489,513,511,491,490,512,491,492,514,490,493,515,492,494,516,493,495,517,494,497,518,495,496,519,497,520,519,498,521,520,499,522,521,500,523,522,501,524,523,502,526,524,504,503,525,504,505,527,503,506,528,505,507,529,506,486,508,507,509,530,508,510,532,509,533,532,511,535,533,513,512,534,513,514,536,512,515,537,514,516,538,515,517,539,516,518,540,517,519,541,518,542,541,520,543,542,521,544,543,522,545,544,523,546,545,524,548,546,526,525,547,526,527,549,525,550,549,528,551,550,529,531,551,508,530,552,531,554,552,532,556,554,533,535,555,533,534,557,535,536,558,534,537,559,536,538,560,537,539,561,538,540,562,539,541,563,540,564,563,542,565,564,543,566,565,544,567,566,545,568,567,546,569,568,548,571,569,547,549,570,547,550,572,549,573,572,551,553,573,531,552,574,553,576,574,554,577,576,556,579,577,555,557,578,555,558,580,557,559,581,558,560,582,559,561,583,560,562,584,561,563,585,562,586,585,564,587,586,565,588,587,566,589,588,567,591,589,568,569,590,568,571,592,569,570,593,571,572,594,570,595,594,573,575,595,553,616,596,598,616,599,596,599,601,600,601,603,602,607,605,603,609,606,607,611,608,609,613,610,611,615,612,613,617,614,615,616,598,574,576,599,574,577,601,576,579,603,577,578,607,579,580,609,578,581,611,580,582,613,581,583,615,582,584,617,583,585,614,584,612,614,586,610,612,587,608,610,588,606,608,589,605,606,591,604,605,590,602,604,592,600,602,593,596,600,594,595,597,594,575,598,595,625,618,620,624,621,623,624,623,618,625,621,618,622,621,620,623,622,619,633,626,628,632,629,631,632,631,626,629,632,633,630,629,628,631,630,627,676,634,636,634,637,635,637,639,638,639,641,640,645,643,641,647,644,645,649,646,647,651,648,649,653,650,651,667,652,653,635,654,636,638,656,635,640,657,638,642,658,640,659,658,643,660,659,644,661,660,646,662,661,648,663,662,650,665,663,652,667,664,652,653,666,667,651,668,653,649,669,651,647,670,649,645,671,647,641,672,645,673,672,639,674,673,637,675,674,634,677,675,676,655,677,636,654,678,655,656,680,654,657,681,656,658,682,657,683,682,659,684,683,660,685,684,661,686,685,662,687,686,663,689,687,665,664,688,665,666,690,664,668,691,666,669,692,668,670,693,669,671,694,670,672,695,671,696,695,673,697,696,674,698,697,675,699,698,677,679,699,655,678,700,679,680,702,678,681,703,680,682,704,681,705,704,683,706,705,684,707,706,685,708,707,686,709,708,687,711,709,689,688,710,689,690,712,688,691,713,690,692,714,691,693,715,692,694,716,693,695,717,694,718,717,696,719,718,697,720,719,698,721,720,699,701,721,679,723,722,700,724,723,702,725,724,703,727,725,704,705,726,704,706,728,705,707,729,706,730,729,708,731,730,709,733,731,711,710,732,711,712,734,710,713,735,712,736,735,714,737,736,715,738,737,716,740,738,717,718,739,717,719,741,718,720,742,719,721,743,720,701,722,721,745,744,723,746,745,724,747,746,725,749,747,727,726,748,727,728,750,726,729,751,728,730,752,729,731,753,730,755,753,733,732,754,733,734,756,732,757,756,735,758,757,736,759,758,737,760,759,738,762,760,740,739,761,740,741,763,739,742,764,741,743,765,742,722,744,743,767,766,745,768,767,746,769,768,747,771,769,749,748,770,749,750,772,748,751,773,750,752,774,751,753,775,752,755,776,753,754,777,755,778,777,756,779,778,757,780,779,758,781,780,759,782,781,760,784,782,762,761,783,762,763,785,761,764,786,763,765,787,764,744,766,765,767,788,766,768,790,767,791,790,769,793,791,771,770,792,771,772,794,770,773,795,772,774,796,773,775,797,774,776,798,775,777,799,776,800,799,778,801,800,779,802,801,780,803,802,781,804,803,782,806,804,784,783,805,784,785,807,783,808,807,786,809,808,787,789,809,766,788,810,789,812,810,790,814,812,791,793,813,791,792,815,793,794,816,792,795,817,794,796,818,795,797,819,796,798,820,797,799,821,798,822,821,800,823,822,801,824,823,802,825,824,803,826,825,804,827,826,806,829,827,805,807,828,805,808,830,807,831,830,809,811,831,789,810,832,811,834,832,812,835,834,814,836,835,813,838,836,815,816,837,815,817,839,816,818,840,817,819,841,818,820,842,819,821,843,820,844,843,822,845,844,823,846,845,824,847,846,825,849,847,826,827,848,826,829,850,827,828,851,829,830,852,828,853,852,831,833,853,811,874,854,856,874,857,854,857,859,858,859,861,860,865,863,861,867,864,865,869,866,867,871,868,869,873,870,871,875,872,873,874,856,832,834,857,832,835,859,834,836,861,835,838,865,836,837,867,838,839,869,837,840,871,839,841,873,840,842,875,841,843,872,842,870,872,844,868,870,845,866,868,846,864,866,847,863,864,849,862,863,848,860,862,850,858,860,851,854,858,852,853,855,852,833,856,853,883,876,878,882,879,881,882,881,876,879,882,883,880,879,878,881,880,877] 16 | ,"subMeshes":[{"materialIndex":0,"verticesStart":0,"verticesCount":884,"indexStart":0,"indexCount":4836}] 17 | ,"instances":[]} 18 | ,{"name":"Cube","id":"Cube","materialId":"level.Material","billboardMode":0,"position":[0,0,0],"rotation":[0,0,0],"scaling":[1,1,1],"isVisible":true,"isEnabled":true,"useFlatShading":false,"checkCollisions":false,"receiveShadows":false 19 | ,"positions":[1,-1,-1,-1,-1,-1,-1,-1,1,-1,1,1,-1,1,-1,1,1,-1,1,1,1,1,-1,1] 20 | ,"normals":[0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,-0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,-0.5773,0.5773,0.5773,-0.5773,0.5773,0.5773,0.5773,0.5773,-0.5773,0.5773] 21 | ,"indices":[0,1,2,3,4,5,6,5,0,5,4,1,1,4,3,7,2,3,7,0,2,6,3,5,7,6,0,0,5,1,2,1,3,6,7,3] 22 | ,"subMeshes":[{"materialIndex":0,"verticesStart":0,"verticesCount":8,"indexStart":0,"indexCount":36}] 23 | ,"instances":[]} 24 | ], 25 | "cameras":[{"name":"Camera","id":"Camera","position":[7.4811,5.3437,-6.5076],"rotation":[0.4615,-0.8149,0.0108],"fov":0.8576,"minZ":0.1,"maxZ":100,"speed":1,"inertia":0.9,"checkCollisions":false,"applyGravity":false,"ellipsoid":[0.2,0.9,0.2],"type":"FreeCamera"}],"activeCamera":"Camera", 26 | "lights":[{"name":"Lamp","id":"Lamp","type":0,"position":[4.0762,5.9039,1.0055],"intensity":1,"diffuse":[1,1,1],"specular":[1,1,1]}], 27 | "shadowGenerators":[]} -------------------------------------------------------------------------------- /demo/mesh/level.babylon.manifest: -------------------------------------------------------------------------------- 1 | {"version": 3, "enableSceneOffline": true, "enableTexturesOffline": true } 2 | -------------------------------------------------------------------------------- /demo/mesh/level.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wanadev/babylon-navigation-mesh/f050149c8c44fb25051f1c39279d2c8722d2f141/demo/mesh/level.blend -------------------------------------------------------------------------------- /demo/mesh/level.log: -------------------------------------------------------------------------------- 1 | Babylon.js Exporter version: 1.8.2, Blender version: 2.75 (sub 0) 2 | ========= Conversion from Blender to Babylon.js ========= 3 | Python World class constructor completed 4 | processing begun of material: level.Default 5 | processing begun of material: level.Material 6 | WARNING texture type not currently supported: NONE, ignored. 7 | processing begun of mesh: Navmesh 8 | WARNING: No materials have been assigned: 9 | num positions : 405 10 | num normals : 405 11 | num uvs : 0 12 | num uvs2 : 0 13 | num colors : 0 14 | num indices : 1293 15 | processing begun of mesh: Cube.001 16 | num positions : 884 17 | num normals : 884 18 | num uvs : 1768 19 | num uvs2 : 0 20 | num colors : 0 21 | num indices : 4836 22 | processing begun of mesh: Cube 23 | num positions : 8 24 | num normals : 8 25 | num uvs : 0 26 | num uvs2 : 0 27 | num colors : 0 28 | num indices : 36 29 | processing begun of camera (FreeCamera): Camera 30 | processing begun of light (POINT): Lamp 31 | ========= Writing of scene file started ========= 32 | ========= Writing of scene file completed ========= 33 | ========= end of processing ========= 34 | -------------------------------------------------------------------------------- /demo/mesh/level.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'level.blend' 2 | # Material Count: 3 3 | 4 | newmtl Default 5 | Ns 96.078431 6 | Ka 0.000000 0.000000 0.000000 7 | Kd 0.640000 0.640000 0.640000 8 | Ks 0.500000 0.500000 0.500000 9 | Ni 1.000000 10 | d 1.000000 11 | illum 2 12 | 13 | newmtl Material 14 | Ns 96.078431 15 | Ka 0.000000 0.000000 0.000000 16 | Kd 0.640000 0.640000 0.640000 17 | Ks 0.500000 0.500000 0.500000 18 | Ni 1.000000 19 | d 1.000000 20 | illum 2 21 | 22 | newmtl None 23 | Ns 0 24 | Ka 0.000000 0.000000 0.000000 25 | Kd 0.8 0.8 0.8 26 | Ks 0.8 0.8 0.8 27 | d 1 28 | illum 2 29 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Babynav 6 | 7 | 8 | 9 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /lib-es5/Astar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Class = require("abitbol"); 4 | var BABYLON = require("babylonjs"); 5 | var BinaryHeap = require("./BinaryHeap.js"); 6 | 7 | var Astar = Class.$extend({ 8 | 9 | init: function init(graph) { 10 | for (var x = 0; x < graph.length; x++) { 11 | //for(var x in graph) { 12 | var node = graph[x]; 13 | node.f = 0; 14 | node.g = 0; 15 | node.h = 0; 16 | node.cost = 1.0; 17 | node.visited = false; 18 | node.closed = false; 19 | node.parent = null; 20 | } 21 | }, 22 | 23 | cleanUp: function cleanUp(graph) { 24 | for (var x = 0; x < graph.length; x++) { 25 | var node = graph[x]; 26 | delete node.f; 27 | delete node.g; 28 | delete node.h; 29 | delete node.cost; 30 | delete node.visited; 31 | delete node.closed; 32 | delete node.parent; 33 | } 34 | }, 35 | 36 | heap: function heap() { 37 | return new BinaryHeap(function (node) { 38 | return node.f; 39 | }); 40 | }, 41 | 42 | search: function search(graph, start, end) { 43 | this.init(graph); 44 | //heuristic = heuristic || astar.manhattan; 45 | 46 | 47 | var openHeap = this.heap(); 48 | 49 | openHeap.push(start); 50 | 51 | while (openHeap.size() > 0) { 52 | 53 | // Grab the lowest f(x) to process next. Heap keeps this sorted for us. 54 | var currentNode = openHeap.pop(); 55 | 56 | // End case -- result has been found, return the traced path. 57 | if (currentNode === end) { 58 | var curr = currentNode; 59 | var ret = []; 60 | while (curr.parent) { 61 | ret.push(curr); 62 | curr = curr.parent; 63 | } 64 | this.cleanUp(ret); 65 | return ret.reverse(); 66 | } 67 | 68 | // Normal case -- move currentNode from open to closed, process each of its neighbours. 69 | currentNode.closed = true; 70 | 71 | // Find all neighbours for the current node. Optionally find diagonal neighbours as well (false by default). 72 | var neighbours = this.neighbours(graph, currentNode); 73 | 74 | for (var i = 0, il = neighbours.length; i < il; i++) { 75 | var neighbour = neighbours[i]; 76 | 77 | if (neighbour.closed) { 78 | // Not a valid node to process, skip to next neighbour. 79 | continue; 80 | } 81 | 82 | // The g score is the shortest distance from start to current node. 83 | // We need to check if the path we have arrived at this neighbour is the shortest one we have seen yet. 84 | var gScore = currentNode.g + neighbour.cost; 85 | var beenVisited = neighbour.visited; 86 | 87 | if (!beenVisited || gScore < neighbour.g) { 88 | 89 | // Found an optimal (so far) path to this node. Take score for node to see how good it is. 90 | neighbour.visited = true; 91 | neighbour.parent = currentNode; 92 | if (!neighbour.centroid || !end.centroid) debugger; 93 | neighbour.h = neighbour.h || this.heuristic(neighbour.centroid, end.centroid); 94 | neighbour.g = gScore; 95 | neighbour.f = neighbour.g + neighbour.h; 96 | 97 | if (!beenVisited) { 98 | // Pushing to heap will put it in proper place based on the 'f' value. 99 | openHeap.push(neighbour); 100 | } else { 101 | // Already seen the node, but since it has been rescored we need to reorder it in the heap 102 | openHeap.rescoreElement(neighbour); 103 | } 104 | } 105 | } 106 | } 107 | 108 | // No result was found - empty array signifies failure to find path. 109 | return []; 110 | }, 111 | heuristic: function heuristic(pos1, pos2) { 112 | return BABYLON.Vector3.DistanceSquared(pos1, pos2); 113 | }, 114 | neighbours: function neighbours(graph, node) { 115 | var ret = []; 116 | 117 | for (var e = 0; e < node.neighbours.length; e++) { 118 | ret.push(graph[node.neighbours[e]]); 119 | } 120 | 121 | return ret; 122 | } 123 | 124 | }); 125 | 126 | module.exports = Astar; -------------------------------------------------------------------------------- /lib-es5/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Class = require("abitbol"); 4 | 5 | var BinaryHeap = Class.$extend({ 6 | __init__: function __init__(scoreFunction) { 7 | this.content = []; 8 | this.scoreFunction = scoreFunction || new function () {}(); 9 | }, 10 | 11 | push: function push(element) { 12 | // Add the new element to the end of the array. 13 | this.content.push(element); 14 | 15 | // Allow it to sink down. 16 | this.sinkDown(this.content.length - 1); 17 | }, 18 | pop: function pop() { 19 | // Store the first element so we can return it later. 20 | var result = this.content[0]; 21 | // Get the element at the end of the array. 22 | var end = this.content.pop(); 23 | // If there are any elements left, put the end element at the 24 | // start, and let it bubble up. 25 | if (this.content.length > 0) { 26 | this.content[0] = end; 27 | this.bubbleUp(0); 28 | } 29 | return result; 30 | }, 31 | remove: function remove(node) { 32 | var i = this.content.indexOf(node); 33 | 34 | // When it is found, the process seen in 'pop' is repeated 35 | // to fill up the hole. 36 | var end = this.content.pop(); 37 | 38 | if (i !== this.content.length - 1) { 39 | this.content[i] = end; 40 | 41 | if (this.scoreFunction(end) < this.scoreFunction(node)) { 42 | this.sinkDown(i); 43 | } else { 44 | this.bubbleUp(i); 45 | } 46 | } 47 | }, 48 | size: function size() { 49 | return this.content.length; 50 | }, 51 | rescoreElement: function rescoreElement(node) { 52 | this.sinkDown(this.content.indexOf(node)); 53 | }, 54 | sinkDown: function sinkDown(n) { 55 | // Fetch the element that has to be sunk. 56 | var element = this.content[n]; 57 | 58 | // When at 0, an element can not sink any further. 59 | while (n > 0) { 60 | 61 | // Compute the parent element's index, and fetch it. 62 | var parentN = (n + 1 >> 1) - 1, 63 | parent = this.content[parentN]; 64 | // Swap the elements if the parent is greater. 65 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 66 | this.content[parentN] = element; 67 | this.content[n] = parent; 68 | // Update 'n' to continue at the new position. 69 | n = parentN; 70 | } 71 | 72 | // Found a parent that is less, no need to sink any further. 73 | else { 74 | break; 75 | } 76 | } 77 | }, 78 | bubbleUp: function bubbleUp(n) { 79 | // Look up the target element and its score. 80 | var length = this.content.length, 81 | element = this.content[n], 82 | elemScore = this.scoreFunction(element); 83 | 84 | while (true) { 85 | // Compute the indices of the child elements. 86 | var child2N = n + 1 << 1, 87 | child1N = child2N - 1; 88 | // This is used to store the new position of the element, 89 | // if any. 90 | var swap = null; 91 | // If the first child exists (is inside the array)... 92 | if (child1N < length) { 93 | // Look it up and compute its score. 94 | var child1 = this.content[child1N], 95 | child1Score = this.scoreFunction(child1); 96 | 97 | // If the score is less than our element's, we need to swap. 98 | if (child1Score < elemScore) swap = child1N; 99 | } 100 | 101 | // Do the same checks for the other child. 102 | if (child2N < length) { 103 | var child2 = this.content[child2N], 104 | child2Score = this.scoreFunction(child2); 105 | if (child2Score < (swap === null ? elemScore : child1Score)) { 106 | swap = child2N; 107 | } 108 | } 109 | 110 | // If the element needs to be moved, swap it, and continue. 111 | if (swap !== null) { 112 | this.content[n] = this.content[swap]; 113 | this.content[swap] = element; 114 | n = swap; 115 | } 116 | 117 | // Otherwise, we are done. 118 | else { 119 | break; 120 | } 121 | } 122 | } 123 | }); 124 | 125 | module.exports = BinaryHeap; -------------------------------------------------------------------------------- /lib-es5/Channel.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Class = require("abitbol"); 4 | var BABYLON = require("babylonjs"); 5 | 6 | var Channel = Class.$extend({ 7 | __init__: function __init__() { 8 | this.portals = []; 9 | }, 10 | 11 | push: function push(p1, p2) { 12 | if (p2 === undefined) p2 = p1; 13 | this.portals.push({ 14 | left: p1, 15 | right: p2 16 | }); 17 | }, 18 | 19 | _vequal: function _vequal(a, b) { 20 | return BABYLON.Vector3.DistanceSquared(a, b) < 0.00001; 21 | }, 22 | 23 | _triarea2: function _triarea2(a, b, c) { 24 | var ax = b.x - a.x; 25 | var az = b.z - a.z; 26 | var bx = c.x - a.x; 27 | var bz = c.z - a.z; 28 | return bx * az - ax * bz; 29 | }, 30 | 31 | stringPull: function stringPull() { 32 | var portals = this.portals; 33 | var pts = []; 34 | // Init scan state 35 | var portalApex, portalLeft, portalRight; 36 | var apexIndex = 0, 37 | leftIndex = 0, 38 | rightIndex = 0; 39 | 40 | portalApex = portals[0].left; 41 | portalLeft = portals[0].left; 42 | portalRight = portals[0].right; 43 | 44 | // Add start point. 45 | pts.push(portalApex); 46 | 47 | for (var i = 1; i < portals.length; i++) { 48 | var left = portals[i].left; 49 | var right = portals[i].right; 50 | 51 | // Update right vertex. 52 | if (this._triarea2(portalApex, portalRight, right) >= 0.0) { 53 | if (this._vequal(portalApex, portalRight) || this._triarea2(portalApex, portalLeft, right) < 0.0) { 54 | // Tighten the funnel. 55 | portalRight = right; 56 | rightIndex = i; 57 | } else { 58 | // Right over left, insert left to path and restart scan from portal left point. 59 | pts.push(portalLeft); 60 | // Make current left the new apex. 61 | portalApex = portalLeft; 62 | apexIndex = leftIndex; 63 | // Reset portal 64 | portalLeft = portalApex; 65 | portalRight = portalApex; 66 | leftIndex = apexIndex; 67 | rightIndex = apexIndex; 68 | // Restart scan 69 | i = apexIndex; 70 | continue; 71 | } 72 | } 73 | 74 | // Update left vertex. 75 | if (this._triarea2(portalApex, portalLeft, left) <= 0.0) { 76 | if (this._vequal(portalApex, portalLeft) || this._triarea2(portalApex, portalRight, left) > 0.0) { 77 | // Tighten the funnel. 78 | portalLeft = left; 79 | leftIndex = i; 80 | } else { 81 | // Left over right, insert right to path and restart scan from portal right point. 82 | pts.push(portalRight); 83 | // Make current right the new apex. 84 | portalApex = portalRight; 85 | apexIndex = rightIndex; 86 | // Reset portal 87 | portalLeft = portalApex; 88 | portalRight = portalApex; 89 | leftIndex = apexIndex; 90 | rightIndex = apexIndex; 91 | // Restart scan 92 | i = apexIndex; 93 | continue; 94 | } 95 | } 96 | } 97 | 98 | if (pts.length === 0 || !this._vequal(pts[pts.length - 1], portals[portals.length - 1].left)) { 99 | // Append last point to path. 100 | pts.push(portals[portals.length - 1].left); 101 | } 102 | 103 | this.path = pts; 104 | return pts; 105 | } 106 | }); 107 | 108 | module.exports = Channel; -------------------------------------------------------------------------------- /lib-es5/Navigation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Class = require("abitbol"); 4 | var _ = require("lodash"); 5 | var Astar = require("./Astar.js"); 6 | var Channel = require("./Channel.js"); 7 | 8 | var BABYLON = require("babylonjs"); 9 | 10 | /** 11 | * This component generates screenshots of 3D models, in order to preview them when they are rendered. 12 | * 13 | * @class Focus3D 14 | * @constructor 15 | */ 16 | 17 | var Navigation = Class.$extend({ 18 | __init__: function __init__() { 19 | this.zoneNodes = {}; 20 | this.astar = new Astar(); 21 | this.yTolerance = 1; 22 | }, 23 | 24 | buildNodes: function buildNodes(mesh) { 25 | var navigationMesh = this._buildNavigationMesh(mesh.geometry); 26 | 27 | var zoneNodes = this._groupNavMesh(navigationMesh); 28 | 29 | return zoneNodes; 30 | }, 31 | 32 | setZoneData: function setZoneData(zone, data) { 33 | this.zoneNodes[zone] = data; 34 | }, 35 | 36 | setHeightTolerance: function setHeightTolerance(tolerance) { 37 | this.yTolerance = tolerance; 38 | }, 39 | 40 | getGroup: function getGroup(zone, position) { 41 | 42 | if (!this.zoneNodes[zone]) { 43 | return null; 44 | } 45 | 46 | var closestNodeGroup = null; 47 | 48 | var distance = Infinity; 49 | 50 | _.each(this.zoneNodes[zone].groups, function (group, index) { 51 | _.each(group, function (node) { 52 | var measuredDistance = BABYLON.Vector3.DistanceSquared(node.centroid, position); 53 | if (measuredDistance < distance) { 54 | closestNodeGroup = index; 55 | distance = measuredDistance; 56 | } 57 | }); 58 | }); 59 | 60 | return closestNodeGroup; 61 | }, 62 | 63 | getRandomNode: function getRandomNode(zone, group, nearPosition, nearRange) { 64 | 65 | if (!this.zoneNodes[zone]) return new BABYLON.Vector3(); 66 | 67 | nearPosition = nearPosition || null; 68 | nearRange = nearRange || 0; 69 | 70 | var candidates = []; 71 | 72 | var polygons = this.zoneNodes[zone].groups[group]; 73 | 74 | _.each(polygons, function (p) { 75 | if (nearPosition && nearRange) { 76 | if (BABYLON.Vector3.DistanceSquared(nearPosition, p.centroid) < nearRange * nearRange) { 77 | candidates.push(p.centroid); 78 | } 79 | } else { 80 | candidates.push(p.centroid); 81 | } 82 | }); 83 | 84 | return _.sample(candidates) || new BABYLON.Vector3(); 85 | }, 86 | 87 | projectOnNavmesh: function projectOnNavmesh(position, zone, group) { 88 | var allNodes = this.zoneNodes[zone].groups[group]; 89 | var vertices = this.zoneNodes[zone].vertices; 90 | 91 | var closestNode = null; 92 | var distance = Infinity; 93 | var finalProj = null, 94 | proj = null, 95 | node = null, 96 | measuredDistance = 0; 97 | 98 | for (var i = 0; i < allNodes.length; i++) { 99 | node = allNodes[i]; 100 | 101 | proj = this._getProjectionOnNode(position, node, vertices); 102 | measuredDistance = BABYLON.Vector3.DistanceSquared(proj, position); 103 | 104 | if (measuredDistance < distance) { 105 | distance = measuredDistance; 106 | //this.meshes[3].position.copyFrom(proj); 107 | finalProj = proj; 108 | closestNode = node; 109 | } 110 | } 111 | 112 | return finalProj; 113 | }, 114 | 115 | _projectPointOnPlane: function _projectPointOnPlane(point, plane) { 116 | var coef = BABYLON.Vector3.Dot(point, plane.normal) + plane.d; 117 | var proj = point.subtract(plane.normal.scale(coef)); 118 | 119 | return proj; 120 | }, 121 | 122 | _getProjectionOnNode: function _getProjectionOnNode(position, node, vertices) { 123 | 124 | var A = this.getVectorFrom(vertices, node.vertexIds[0]); 125 | var B = this.getVectorFrom(vertices, node.vertexIds[1]); 126 | var C = this.getVectorFrom(vertices, node.vertexIds[2]); 127 | var u = B.subtract(A); 128 | var v = C.subtract(A); 129 | var n = BABYLON.Vector3.Cross(u, v).normalize(); 130 | 131 | var plane = { 132 | normal: n, 133 | d: -BABYLON.Vector3.Dot(A, n) 134 | }; 135 | var p = this._projectPointOnPlane(position, plane); 136 | // Compute barycentric coordinates (u, v, w) for 137 | // point p with respect to triangle (a, b, c) 138 | var barycentric = function barycentric(p, a, b, c) { 139 | var ret = {}; 140 | 141 | var v0 = c.subtract(a), 142 | v1 = b.subtract(a), 143 | v2 = p.subtract(a); 144 | 145 | var d00 = BABYLON.Vector3.Dot(v0, v0); 146 | var d01 = BABYLON.Vector3.Dot(v0, v1); 147 | var d02 = BABYLON.Vector3.Dot(v0, v2); 148 | var d11 = BABYLON.Vector3.Dot(v1, v1); 149 | var d12 = BABYLON.Vector3.Dot(v1, v2); 150 | var denom = d00 * d11 - d01 * d01; 151 | ret.u = (d11 * d02 - d01 * d12) / denom; 152 | ret.v = (d00 * d12 - d01 * d02) / denom; 153 | ret.w = 1 - ret.u - ret.v; 154 | 155 | return ret; 156 | }; 157 | 158 | var bary = barycentric(p, A, B, C); 159 | 160 | bary.u = Math.min(Math.max(bary.u, 0), 1); 161 | bary.v = Math.min(Math.max(bary.v, 0), 1); 162 | 163 | if (bary.u + bary.v >= 1) { 164 | var sum = bary.u + bary.v; 165 | bary.u /= sum; 166 | bary.v /= sum; 167 | } 168 | 169 | var proj = A.add(B.subtract(A).scale(bary.v).add(C.subtract(A).scale(bary.u))); 170 | 171 | return proj; 172 | }, 173 | 174 | findPath: function findPath(startPosition, targetPosition, zone, group) { 175 | 176 | var allNodes = this.zoneNodes[zone].groups[group]; 177 | var vertices = this.zoneNodes[zone].vertices; 178 | 179 | var startingNode = null; 180 | 181 | for (var i = 0; i < allNodes.length; i++) { 182 | if (this._isVectorInPolygon(startPosition, allNodes[i], vertices)) { 183 | startingNode = allNodes[i]; 184 | break; 185 | } 186 | } 187 | 188 | var endNode = null; 189 | 190 | for (var i = 0; i < allNodes.length; i++) { 191 | if (this._isVectorInPolygon(targetPosition, allNodes[i], vertices)) { 192 | endNode = allNodes[i]; 193 | break; 194 | } 195 | } 196 | // If we can't find any node, theres no path to target 197 | if (!startingNode || !endNode) { 198 | return null; 199 | } 200 | 201 | if (startingNode.id != endNode.id) { 202 | // if the starting node and target node are at the same polygon skip searching and funneling as there is no obstacle. 203 | var paths = this.astar.search(allNodes, startingNode, endNode); 204 | } else { 205 | vectors = []; 206 | vectors.push(new BABYLON.Vector3(targetPosition.x, targetPosition.y, targetPosition.z)); 207 | return vectors; 208 | } 209 | 210 | var getPortalFromTo = function getPortalFromTo(a, b) { 211 | for (var i = 0; i < a.neighbours.length; i++) { 212 | if (a.neighbours[i] === b.id) { 213 | return a.portals[i]; 214 | } 215 | } 216 | }; 217 | 218 | // We got the corridor 219 | // Now pull the rope 220 | 221 | var channel = new Channel(); 222 | 223 | channel.push(startPosition); 224 | 225 | for (var i = 0; i < paths.length; i++) { 226 | var polygon = paths[i]; 227 | 228 | var nextPolygon = paths[i + 1]; 229 | 230 | if (nextPolygon) { 231 | var portals = getPortalFromTo(polygon, nextPolygon); 232 | channel.push(this.getVectorFrom(vertices, portals[0]), this.getVectorFrom(vertices, portals[1])); 233 | } 234 | } 235 | 236 | channel.push(targetPosition); 237 | 238 | channel.stringPull(); 239 | 240 | var vectors = []; 241 | 242 | channel.path.forEach(function (c) { 243 | var vec = new BABYLON.Vector3(c.x, c.y, c.z); 244 | 245 | // console.log(vec.clone().sub(startPosition).length()); 246 | 247 | // Ensure the intermediate steps aren't too close to the start position 248 | // var dist = vec.clone().sub(startPosition).lengthSq(); 249 | // if (dist > 0.01 * 0.01) { 250 | vectors.push(vec); 251 | // } 252 | 253 | }); 254 | 255 | // We don't need the first one, as we already know our start position 256 | vectors.shift(); 257 | 258 | return vectors; 259 | }, 260 | 261 | _isPointInPoly: function _isPointInPoly(poly, pt) { 262 | for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) { 263 | (poly[i].z <= pt.z && pt.z < poly[j].z || poly[j].z <= pt.z && pt.z < poly[i].z) && pt.x < (poly[j].x - poly[i].x) * (pt.z - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x && (c = !c); 264 | }return c; 265 | }, 266 | 267 | _isVectorInPolygon: function _isVectorInPolygon(vector, polygon, vertices) { 268 | 269 | // reference point will be the centroid of the polygon 270 | // We need to rotate the vector as well as all the points which the polygon uses 271 | var lowestPoint = 100000; 272 | var highestPoint = -100000; 273 | 274 | var polygonVertices = []; 275 | 276 | _.each(polygon.vertexIds, function (vId) { 277 | var point = this.getVectorFrom(vertices, vId); 278 | lowestPoint = Math.min(point.y, lowestPoint); 279 | highestPoint = Math.max(point.y, highestPoint); 280 | polygonVertices.push(point); 281 | }.bind(this)); 282 | 283 | if (vector.y < highestPoint + this.yTolerance && vector.y > lowestPoint - this.yTolerance && this._isPointInPoly(polygonVertices, vector)) { 284 | return true; 285 | } 286 | return false; 287 | }, 288 | 289 | _computeCentroids: function _computeCentroids(geometry) { 290 | var centroids = []; 291 | var indices = geometry.getIndices(); 292 | var vertices = geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind); 293 | var c = new BABYLON.Vector3(0, 0, 0); 294 | 295 | for (var f = 0; f < indices.length; f += 3) { 296 | var p1 = this.getVectorFrom(vertices, indices[f]); 297 | var p2 = this.getVectorFrom(vertices, indices[f + 1]); 298 | var p3 = this.getVectorFrom(vertices, indices[f + 2]); 299 | 300 | c.copyFromFloats(0, 0, 0); 301 | 302 | c.addInPlace(p1); 303 | c.addInPlace(p2); 304 | c.addInPlace(p3); 305 | 306 | c.scaleInPlace(1 / 3); 307 | 308 | centroids.push(c.clone()); 309 | } 310 | geometry.centroids = centroids; 311 | }, 312 | 313 | _roundNumber: function _roundNumber(number, decimals) { 314 | var newnumber = new Number(number + '').toFixed(parseInt(decimals)); 315 | return parseFloat(newnumber); 316 | }, 317 | 318 | _mergeVertexIds: function _mergeVertexIds(aList, bList) { 319 | 320 | var sharedVertices = []; 321 | 322 | aList.forEach(function (vId) { 323 | if (_.includes(bList, vId)) { 324 | sharedVertices.push(vId); 325 | } 326 | }); 327 | 328 | if (sharedVertices.length < 2) return []; 329 | 330 | // console.log("TRYING aList:", aList, ", bList:", bList, ", sharedVertices:", sharedVertices); 331 | 332 | if (_.includes(sharedVertices, aList[0]) && _.includes(sharedVertices, aList[aList.length - 1])) { 333 | // Vertices on both edges are bad, so shift them once to the left 334 | aList.push(aList.shift()); 335 | } 336 | 337 | if (_.includes(sharedVertices, bList[0]) && _.includes(sharedVertices, bList[bList.length - 1])) { 338 | // Vertices on both edges are bad, so shift them once to the left 339 | bList.push(bList.shift()); 340 | } 341 | 342 | // Again! 343 | sharedVertices = []; 344 | 345 | aList.forEach(function (vId) { 346 | if (_.includes(bList, vId)) { 347 | sharedVertices.push(vId); 348 | } 349 | }); 350 | 351 | var clockwiseMostSharedVertex = sharedVertices[1]; 352 | var counterClockwiseMostSharedVertex = sharedVertices[0]; 353 | 354 | var cList = _.clone(aList); 355 | while (cList[0] !== clockwiseMostSharedVertex) { 356 | cList.push(cList.shift()); 357 | } 358 | 359 | var c = 0; 360 | 361 | var temp = _.clone(bList); 362 | while (temp[0] !== counterClockwiseMostSharedVertex) { 363 | temp.push(temp.shift()); 364 | 365 | if (c++ > 10) break; 366 | } 367 | 368 | // Shave 369 | temp.shift(); 370 | temp.pop(); 371 | 372 | cList = cList.concat(temp); 373 | 374 | // console.log("aList:", aList, ", bList:", bList, ", cList:", cList, ", sharedVertices:", sharedVertices); 375 | 376 | return cList; 377 | }, 378 | 379 | _setPolygonCentroid: function _setPolygonCentroid(polygon, navigationMesh) { 380 | var sum = new BABYLON.Vector3(0, 0, 0); 381 | 382 | var vertices = navigationMesh.vertices; 383 | 384 | _.each(polygon.vertexIds, function (vId) { 385 | sum.x += vertices[vId * 3]; 386 | sum.y += vertices[vId * 3 + 1]; 387 | sum.z += vertices[vId * 3 + 2]; 388 | }); 389 | 390 | sum.scaleInPlace(1 / polygon.vertexIds.length); 391 | 392 | polygon.centroid.copyFrom(sum); 393 | }, 394 | 395 | getVectorFrom: function getVectorFrom(vertices, id, _vector) { 396 | if (_vector) { 397 | _vector.copyFromFloats(vertices[id * 3], vertices[id * 3 + 1], vertices[id * 3 + 2]); 398 | return _vector; 399 | } 400 | return new BABYLON.Vector3(vertices[id * 3], vertices[id * 3 + 1], vertices[id * 3 + 2]); 401 | }, 402 | 403 | _cleanPolygon: function _cleanPolygon(polygon, navigationMesh) { 404 | 405 | var newVertexIds = []; 406 | 407 | var vertices = navigationMesh.vertices; 408 | 409 | for (var i = 0; i < polygon.vertexIds.length; i++) { 410 | 411 | var vertex = this.getVectorFrom(vertices, polygon.vertexIds[i]); 412 | 413 | var nextVertexId, previousVertexId; 414 | var nextVertex, previousVertex; 415 | 416 | // console.log("nextVertex: ", nextVertex); 417 | 418 | if (i === 0) { 419 | nextVertexId = polygon.vertexIds[1]; 420 | previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 1]; 421 | } else if (i === polygon.vertexIds.length - 1) { 422 | nextVertexId = polygon.vertexIds[0]; 423 | previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 2]; 424 | } else { 425 | nextVertexId = polygon.vertexIds[i + 1]; 426 | previousVertexId = polygon.vertexIds[i - 1]; 427 | } 428 | 429 | nextVertex = this.getVectorFrom(vertices, nextVertexId); 430 | previousVertex = this.getVectorFrom(vertices, previousVertexId); 431 | 432 | var a = nextVertex.clone().sub(vertex); 433 | var b = previousVertex.clone().sub(vertex); 434 | 435 | var angle = a.angleTo(b); 436 | 437 | // console.log(angle); 438 | 439 | if (angle > Math.PI - 0.01 && angle < Math.PI + 0.01) { 440 | // Unneccesary vertex 441 | // console.log("Unneccesary vertex: ", polygon.vertexIds[i]); 442 | // console.log("Angle between "+previousVertexId+", "+polygon.vertexIds[i]+" "+nextVertexId+" was: ", angle); 443 | 444 | 445 | // Remove the neighbours who had this vertex 446 | var goodNeighbours = []; 447 | polygon.neighbours.forEach(function (neighbour) { 448 | if (!_.includes(neighbour.vertexIds, polygon.vertexIds[i])) { 449 | goodNeighbours.push(neighbour); 450 | } 451 | }); 452 | polygon.neighbours = goodNeighbours; 453 | 454 | // TODO cleanup the list of vertices and rebuild vertexIds for all polygons 455 | } else { 456 | newVertexIds.push(polygon.vertexIds[i]); 457 | } 458 | } 459 | 460 | // console.log("New vertexIds: ", newVertexIds); 461 | 462 | polygon.vertexIds = newVertexIds; 463 | 464 | this._setPolygonCentroid(polygon, navigationMesh); 465 | }, 466 | 467 | _isConvex: function _isConvex(polygon, navigationMesh) { 468 | 469 | var vertices = navigationMesh.vertices; 470 | 471 | if (polygon.vertexIds.length < 3) return false; 472 | 473 | var convex = true; 474 | 475 | var total = 0; 476 | 477 | var results = []; 478 | 479 | for (var i = 0; i < polygon.vertexIds.length; i++) { 480 | 481 | var vertex = this.getVectorFrom(vertices, polygon.vertexIds[i]); 482 | 483 | var nextVertex, previousVertex; 484 | 485 | // console.log("nextVertex: ", nextVertex); 486 | 487 | if (i === 0) { 488 | nextVertex = this.getVectorFrom(vertices, polygon.vertexIds[1]); 489 | previousVertex = this.getVectorFrom(vertices, polygon.vertexIds[polygon.vertexIds.length - 1]); 490 | } else if (i === polygon.vertexIds.length - 1) { 491 | nextVertex = this.getVectorFrom(vertices, polygon.vertexIds[0]); 492 | previousVertex = this.getVectorFrom(vertices, polygon.vertexIds[polygon.vertexIds.length - 2]); 493 | } else { 494 | nextVertex = this.getVectorFrom(vertices, polygon.vertexIds[i + 1]); 495 | previousVertex = this.getVectorFrom(vertices, polygon.vertexIds[i - 1]); 496 | } 497 | 498 | var a = nextVertex.clone().sub(vertex); 499 | var b = previousVertex.clone().sub(vertex); 500 | 501 | var angle = a.angleTo(b); 502 | total += angle; 503 | 504 | // console.log(angle); 505 | if (angle === Math.PI || angle === 0) return false; 506 | 507 | var r = BABYLON.Vector3.Cross(a, b).y; 508 | results.push(r); 509 | // console.log("pushed: ", r); 510 | } 511 | 512 | // if ( total > (polygon.vertexIds.length-2)*Math.PI ) return false; 513 | 514 | results.forEach(function (r) { 515 | if (r === 0) convex = false; 516 | }); 517 | 518 | if (results[0] > 0) { 519 | results.forEach(function (r) { 520 | if (r < 0) convex = false; 521 | }); 522 | } else { 523 | results.forEach(function (r) { 524 | if (r > 0) convex = false; 525 | }); 526 | } 527 | 528 | // console.log("allowed: "+total+", max: "+(polygon.vertexIds.length-2)*Math.PI); 529 | // if ( total > (polygon.vertexIds.length-2)*Math.PI ) convex = false; 530 | 531 | // console.log("Convex: "+(convex ? "true": "false")); 532 | 533 | 534 | return convex; 535 | }, 536 | 537 | _buildPolygonGroups: function _buildPolygonGroups(navigationMesh) { 538 | 539 | var polygons = navigationMesh.polygons; 540 | 541 | var polygonGroups = []; 542 | var groupCount = 0; 543 | 544 | var spreadGroupId = function spreadGroupId(polygon) { 545 | _.each(polygon.neighbours, function (neighbour) { 546 | if (_.isUndefined(neighbour.group)) { 547 | neighbour.group = polygon.group; 548 | spreadGroupId(neighbour); 549 | } 550 | }); 551 | }; 552 | 553 | _.each(polygons, function (polygon) { 554 | 555 | if (_.isUndefined(polygon.group)) { 556 | polygon.group = groupCount++; 557 | // Spread it 558 | spreadGroupId(polygon); 559 | } 560 | 561 | if (!polygonGroups[polygon.group]) polygonGroups[polygon.group] = []; 562 | 563 | polygonGroups[polygon.group].push(polygon); 564 | }); 565 | 566 | console.log("Groups built: ", polygonGroups.length); 567 | 568 | return polygonGroups; 569 | }, 570 | 571 | _array_intersect: function _array_intersect() { 572 | var i, 573 | shortest, 574 | nShortest, 575 | n, 576 | len, 577 | ret = [], 578 | obj = {}, 579 | nOthers; 580 | nOthers = arguments.length - 1; 581 | nShortest = arguments[0].length; 582 | shortest = 0; 583 | for (i = 0; i <= nOthers; i++) { 584 | n = arguments[i].length; 585 | if (n < nShortest) { 586 | shortest = i; 587 | nShortest = n; 588 | } 589 | } 590 | 591 | for (i = 0; i <= nOthers; i++) { 592 | n = i === shortest ? 0 : i || shortest; //Read the shortest array first. Read the first array instead of the shortest 593 | len = arguments[n].length; 594 | for (var j = 0; j < len; j++) { 595 | var elem = arguments[n][j]; 596 | if (obj[elem] === i - 1) { 597 | if (i === nOthers) { 598 | ret.push(elem); 599 | obj[elem] = 0; 600 | } else { 601 | obj[elem] = i; 602 | } 603 | } else if (i === 0) { 604 | obj[elem] = 0; 605 | } 606 | } 607 | } 608 | return ret; 609 | }, 610 | 611 | _buildPolygonNeighbours: function _buildPolygonNeighbours(polygon, navigationMesh) { 612 | polygon.neighbours = []; 613 | 614 | // All other nodes that contain at least two of our vertices are our neighbours 615 | for (var i = 0, len = navigationMesh.polygons.length; i < len; i++) { 616 | if (polygon === navigationMesh.polygons[i]) continue; 617 | 618 | // Don't check polygons that are too far, since the intersection tests take a long time 619 | if (BABYLON.Vector3.DistanceSquared(polygon.centroid, navigationMesh.polygons[i].centroid) > 100 * 100) continue; 620 | 621 | var matches = this._array_intersect(polygon.vertexIds, navigationMesh.polygons[i].vertexIds); 622 | // var matches = _.intersection(polygon.vertexIds, navigationMesh.polygons[i].vertexIds); 623 | 624 | if (matches.length >= 2) { 625 | polygon.neighbours.push(navigationMesh.polygons[i]); 626 | } 627 | } 628 | }, 629 | 630 | _buildPolygonsFromGeometry: function _buildPolygonsFromGeometry(geometry) { 631 | 632 | var polygons = []; 633 | var vertices = geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind); 634 | var indices = geometry.getIndices(); 635 | var polygonId = 1; 636 | 637 | console.log("Vertices:", vertices.length / 3, "polygons:", indices.length / 3); 638 | 639 | // Convert the faces into a custom format that supports more than 3 vertices 640 | for (var i = 0; i < indices.length; i += 3) { 641 | 642 | var a = this.getVectorFrom(vertices, indices[i]); 643 | var b = this.getVectorFrom(vertices, indices[i + 1]); 644 | var c = this.getVectorFrom(vertices, indices[i + 2]); 645 | var normal = BABYLON.Vector3.Cross(b.subtract(a), b.subtract(c)).normalize(); 646 | 647 | polygons.push({ 648 | id: polygonId++, 649 | vertexIds: [indices[i], indices[i + 1], indices[i + 2]], 650 | centroid: geometry.centroids[i / 3], 651 | normal: normal, 652 | neighbours: [] 653 | }); 654 | } 655 | 656 | var navigationMesh = { 657 | polygons: polygons, 658 | vertices: vertices 659 | }; 660 | 661 | // Build a list of adjacent polygons 662 | _.each(polygons, function (polygon) { 663 | this._buildPolygonNeighbours(polygon, navigationMesh); 664 | }.bind(this)); 665 | 666 | return navigationMesh; 667 | }, 668 | 669 | _cleanNavigationMesh: function _cleanNavigationMesh(navigationMesh) { 670 | 671 | var polygons = navigationMesh.polygons; 672 | var vertices = navigationMesh.vertices; 673 | 674 | // Remove steep triangles 675 | var up = new BABYLON.Vector3(0, 1, 0); 676 | polygons = _.filter(polygons, function (polygon) { 677 | var angle = Math.acos(BABYLON.Vector3.Dot(up, polygon.normal)); 678 | return angle < Math.PI / 4; 679 | }); 680 | 681 | // Remove unnecessary edges using the Hertel-Mehlhorn algorithm 682 | 683 | // 1. Find a pair of adjacent nodes (i.e., two nodes that share an edge between them) 684 | // whose normals are nearly identical (i.e., their surfaces face the same direction). 685 | 686 | 687 | var newPolygons = []; 688 | 689 | _.each(polygons, function (polygon) { 690 | 691 | if (polygon.toBeDeleted) return; 692 | 693 | var keepLooking = true; 694 | 695 | while (keepLooking) { 696 | keepLooking = false; 697 | 698 | _.each(polygon.neighbours, function (otherPolygon) { 699 | 700 | if (polygon === otherPolygon) return; 701 | 702 | if (Math.acos(BABYLON.Vector3.Dot(polygon.normal, otherPolygon.normal)) < 0.01) { 703 | // That's pretty equal alright! 704 | 705 | // Merge otherPolygon with polygon 706 | 707 | var testPolygon = { 708 | vertexIds: this._mergeVertexIds(polygon.vertexIds, otherPolygon.vertexIds), 709 | neighbours: polygon.neighbours, 710 | normal: polygon.normal.clone(), 711 | centroid: polygon.centroid.clone() 712 | }; 713 | 714 | this._cleanPolygon(testPolygon, navigationMesh); 715 | 716 | if (this._isConvex(testPolygon, navigationMesh)) { 717 | otherPolygon.toBeDeleted = true; 718 | 719 | // Inherit the neighbours from the to be merged polygon, except ourself 720 | _.each(otherPolygon.neighbours, function (otherPolygonNeighbour) { 721 | 722 | // Set this poly to be merged to be no longer our neighbour 723 | otherPolygonNeighbour.neighbours = _.without(otherPolygonNeighbour.neighbours, otherPolygon); 724 | 725 | if (otherPolygonNeighbour !== polygon) { 726 | // Tell the old Polygon's neighbours about the new neighbour who has merged 727 | otherPolygonNeighbour.neighbours.push(polygon); 728 | } else { 729 | // For ourself, we don't need to know about ourselves 730 | // But we inherit the old neighbours 731 | polygon.neighbours = polygon.neighbours.concat(otherPolygon.neighbours); 732 | polygon.neighbours = _.uniq(polygon.neighbours); 733 | 734 | // Without ourselves in it! 735 | polygon.neighbours = _.without(polygon.neighbours, polygon); 736 | } 737 | }); 738 | 739 | polygon.vertexIds = this._mergeVertexIds(polygon.vertexIds, otherPolygon.vertexIds); 740 | 741 | this._cleanPolygon(polygon, navigationMesh); 742 | 743 | keepLooking = true; 744 | } 745 | } 746 | }.bind(this)); 747 | } 748 | 749 | if (!polygon.toBeDeleted) { 750 | newPolygons.push(polygon); 751 | } 752 | }); 753 | 754 | var isUsed = function isUsed(vId) { 755 | var contains = false; 756 | _.each(newPolygons, function (p) { 757 | if (!contains && _.includes(p.vertexIds, vId)) { 758 | contains = true; 759 | } 760 | }); 761 | return contains; 762 | }; 763 | 764 | // Clean vertices 765 | for (var i = 0; i < vertices.length; i++) { 766 | if (!isUsed(i)) { 767 | 768 | // Decrement all vertices that are higher than i 769 | _.each(newPolygons, function (p) { 770 | for (var j = 0; j < p.vertexIds.length; j++) { 771 | if (p.vertexIds[j] > i) { 772 | p.vertexIds[j]--; 773 | } 774 | } 775 | }); 776 | 777 | vertices.splice(i, 1); 778 | i--; 779 | } 780 | } 781 | 782 | navigationMesh.polygons = newPolygons; 783 | navigationMesh.vertices = vertices; 784 | }, 785 | 786 | _buildNavigationMesh: function _buildNavigationMesh(geometry) { 787 | // Prepare geometry 788 | this._computeCentroids(geometry); 789 | 790 | this._mergeVertices(geometry); 791 | // BABYLON.GeometryUtils.triangulateQuads(geometry); 792 | 793 | // console.log("vertices:", geometry.vertices.length, "polygons:", geometry.faces.length); 794 | 795 | var navigationMesh = this._buildPolygonsFromGeometry(geometry); 796 | 797 | // cleanNavigationMesh(navigationMesh); 798 | // console.log("Pre-clean:", navigationMesh.polygons.length, "polygons,", navigationMesh.vertices.length, "vertices."); 799 | 800 | // console.log("") 801 | // console.log("Vertices:", navigationMesh.vertices.length, "polygons,", navigationMesh.polygons.length, "vertices."); 802 | 803 | return navigationMesh; 804 | }, 805 | 806 | _mergeVertices: function _mergeVertices(geometry) { 807 | var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) 808 | var unique = [], 809 | changes = []; 810 | 811 | var v, key; 812 | var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 813 | var precision = Math.pow(10, precisionPoints); 814 | var indices; 815 | var ind = geometry.getIndices(), 816 | vert = geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind); 817 | 818 | for (var i = 0; i < vert.length; i += 3) { 819 | 820 | v = new BABYLON.Vector3(vert[i], vert[i + 1], vert[i + 2]); 821 | key = Math.round(v.x * precision) + '_' + Math.round(v.y * precision) + '_' + Math.round(v.z * precision); 822 | 823 | if (verticesMap[key] === undefined) { 824 | 825 | verticesMap[key] = i / 3; 826 | unique.push(v.clone()); 827 | changes[i / 3] = unique.length - 1; 828 | } else { 829 | 830 | //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); 831 | changes[i / 3] = changes[verticesMap[key]]; 832 | } 833 | } 834 | 835 | // if faces are completely degenerate after merging vertices, we 836 | // have to remove them from the geometry. 837 | var faceIndicesToRemove = []; 838 | 839 | for (i = 0; i < ind.length; i += 3) { 840 | 841 | ind[i] = changes[ind[i]]; 842 | ind[i + 1] = changes[ind[i + 1]]; 843 | ind[i + 2] = changes[ind[i + 2]]; 844 | 845 | indices = [ind[i], ind[i + 1], ind[i + 2]]; 846 | 847 | var dupIndex = -1; 848 | 849 | // if any duplicate vertices are found in a Face3 850 | // we have to remove the face as nothing can be saved 851 | for (var n = 0; n < 3; n++) { 852 | 853 | if (indices[n] === indices[(n + 1) % 3]) { 854 | 855 | dupIndex = n; 856 | faceIndicesToRemove.push(i); 857 | break; 858 | } 859 | } 860 | } 861 | 862 | for (i = faceIndicesToRemove.length - 1; i >= 0; i--) { 863 | 864 | var idx = faceIndicesToRemove[i]; 865 | 866 | ind.splice(idx, 3); 867 | } 868 | 869 | // Use unique set of vertices 870 | 871 | var diff = vert.length / 3 - unique.length; 872 | vert = []; 873 | for (i = 0; i < unique.length; i++) { 874 | vert.push(unique[i].x, unique[i].y, unique[i].z); 875 | } 876 | 877 | geometry.setIndices(ind); 878 | geometry.setVerticesData(BABYLON.VertexBuffer.PositionKind, vert); 879 | 880 | return diff; 881 | }, 882 | 883 | _getSharedVerticesInOrder: function _getSharedVerticesInOrder(a, b) { 884 | 885 | var aList = a.vertexIds; 886 | var bList = b.vertexIds; 887 | 888 | var sharedVertices = []; 889 | 890 | _.each(aList, function (vId) { 891 | if (_.includes(bList, vId)) { 892 | sharedVertices.push(vId); 893 | } 894 | }); 895 | 896 | if (sharedVertices.length < 2) return []; 897 | 898 | // console.log("TRYING aList:", aList, ", bList:", bList, ", sharedVertices:", sharedVertices); 899 | 900 | if (_.includes(sharedVertices, aList[0]) && _.includes(sharedVertices, aList[aList.length - 1])) { 901 | // Vertices on both edges are bad, so shift them once to the left 902 | aList.push(aList.shift()); 903 | } 904 | 905 | if (_.includes(sharedVertices, bList[0]) && _.includes(sharedVertices, bList[bList.length - 1])) { 906 | // Vertices on both edges are bad, so shift them once to the left 907 | bList.push(bList.shift()); 908 | } 909 | 910 | // Again! 911 | sharedVertices = []; 912 | 913 | _.each(aList, function (vId) { 914 | if (_.includes(bList, vId)) { 915 | sharedVertices.push(vId); 916 | } 917 | }); 918 | 919 | return sharedVertices; 920 | }, 921 | 922 | _groupNavMesh: function _groupNavMesh(navigationMesh) { 923 | 924 | var saveObj = {}; 925 | 926 | _.each(navigationMesh.vertices, function (v) { 927 | v = this._roundNumber(v, 2); 928 | }.bind(this)); 929 | 930 | saveObj.vertices = navigationMesh.vertices; 931 | 932 | var groups = this._buildPolygonGroups(navigationMesh); 933 | 934 | saveObj.groups = []; 935 | 936 | var findPolygonIndex = function findPolygonIndex(group, p) { 937 | for (var i = 0; i < group.length; i++) { 938 | if (p === group[i]) return i; 939 | } 940 | }; 941 | 942 | _.each(groups, function (group) { 943 | 944 | var newGroup = []; 945 | 946 | _.each(group, function (p) { 947 | 948 | var neighbours = []; 949 | 950 | _.each(p.neighbours, function (n) { 951 | neighbours.push(findPolygonIndex(group, n)); 952 | }); 953 | 954 | // Build a portal list to each neighbour 955 | var portals = []; 956 | _.each(p.neighbours, function (n) { 957 | portals.push(this._getSharedVerticesInOrder(p, n)); 958 | }.bind(this)); 959 | 960 | p.centroid.x = this._roundNumber(p.centroid.x, 2); 961 | p.centroid.y = this._roundNumber(p.centroid.y, 2); 962 | p.centroid.z = this._roundNumber(p.centroid.z, 2); 963 | 964 | newGroup.push({ 965 | id: findPolygonIndex(group, p), 966 | neighbours: neighbours, 967 | vertexIds: p.vertexIds, 968 | centroid: p.centroid, 969 | portals: portals 970 | }); 971 | }.bind(this)); 972 | 973 | saveObj.groups.push(newGroup); 974 | }.bind(this)); 975 | 976 | return saveObj; 977 | } 978 | }); 979 | 980 | module.exports = Navigation; -------------------------------------------------------------------------------- /lib/Astar.js: -------------------------------------------------------------------------------- 1 | var Class = require("abitbol"); 2 | var BABYLON = require("babylonjs"); 3 | var BinaryHeap = require("./BinaryHeap.js"); 4 | 5 | var Astar = Class.$extend({ 6 | 7 | init: function (graph) { 8 | for (var x = 0; x < graph.length; x++) { 9 | //for(var x in graph) { 10 | var node = graph[x]; 11 | node.f = 0; 12 | node.g = 0; 13 | node.h = 0; 14 | node.cost = 1.0; 15 | node.visited = false; 16 | node.closed = false; 17 | node.parent = null; 18 | } 19 | }, 20 | 21 | cleanUp: function (graph) { 22 | for (var x = 0; x < graph.length; x++) { 23 | var node = graph[x]; 24 | delete node.f; 25 | delete node.g; 26 | delete node.h; 27 | delete node.cost; 28 | delete node.visited; 29 | delete node.closed; 30 | delete node.parent; 31 | } 32 | }, 33 | 34 | heap: function () { 35 | return new BinaryHeap(function (node) { 36 | return node.f; 37 | }); 38 | }, 39 | 40 | search: function (graph, start, end) { 41 | this.init(graph); 42 | //heuristic = heuristic || astar.manhattan; 43 | 44 | 45 | var openHeap = this.heap(); 46 | 47 | openHeap.push(start); 48 | 49 | while (openHeap.size() > 0) { 50 | 51 | // Grab the lowest f(x) to process next. Heap keeps this sorted for us. 52 | var currentNode = openHeap.pop(); 53 | 54 | // End case -- result has been found, return the traced path. 55 | if (currentNode === end) { 56 | var curr = currentNode; 57 | var ret = []; 58 | while (curr.parent) { 59 | ret.push(curr); 60 | curr = curr.parent; 61 | } 62 | this.cleanUp(ret); 63 | return ret.reverse(); 64 | } 65 | 66 | // Normal case -- move currentNode from open to closed, process each of its neighbours. 67 | currentNode.closed = true; 68 | 69 | // Find all neighbours for the current node. Optionally find diagonal neighbours as well (false by default). 70 | var neighbours = this.neighbours(graph, currentNode); 71 | 72 | for (var i = 0, il = neighbours.length; i < il; i++) { 73 | var neighbour = neighbours[i]; 74 | 75 | if (neighbour.closed) { 76 | // Not a valid node to process, skip to next neighbour. 77 | continue; 78 | } 79 | 80 | // The g score is the shortest distance from start to current node. 81 | // We need to check if the path we have arrived at this neighbour is the shortest one we have seen yet. 82 | var gScore = currentNode.g + neighbour.cost; 83 | var beenVisited = neighbour.visited; 84 | 85 | if (!beenVisited || gScore < neighbour.g) { 86 | 87 | // Found an optimal (so far) path to this node. Take score for node to see how good it is. 88 | neighbour.visited = true; 89 | neighbour.parent = currentNode; 90 | if (!neighbour.centroid || !end.centroid) debugger; 91 | neighbour.h = neighbour.h || this.heuristic(neighbour.centroid, end.centroid); 92 | neighbour.g = gScore; 93 | neighbour.f = neighbour.g + neighbour.h; 94 | 95 | if (!beenVisited) { 96 | // Pushing to heap will put it in proper place based on the 'f' value. 97 | openHeap.push(neighbour); 98 | } else { 99 | // Already seen the node, but since it has been rescored we need to reorder it in the heap 100 | openHeap.rescoreElement(neighbour); 101 | } 102 | } 103 | } 104 | } 105 | 106 | // No result was found - empty array signifies failure to find path. 107 | return []; 108 | }, 109 | heuristic: function (pos1, pos2) { 110 | return BABYLON.Vector3.DistanceSquared(pos1, pos2); 111 | }, 112 | neighbours: function (graph, node) { 113 | var ret = []; 114 | 115 | for (var e = 0; e < node.neighbours.length; e++) { 116 | ret.push(graph[node.neighbours[e]]); 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | }); 123 | 124 | module.exports = Astar; -------------------------------------------------------------------------------- /lib/BinaryHeap.js: -------------------------------------------------------------------------------- 1 | var Class = require("abitbol"); 2 | 3 | var BinaryHeap = Class.$extend({ 4 | __init__: function(scoreFunction) { 5 | this.content = []; 6 | this.scoreFunction = scoreFunction || new function () {}; 7 | }, 8 | 9 | push: function (element) { 10 | // Add the new element to the end of the array. 11 | this.content.push(element); 12 | 13 | // Allow it to sink down. 14 | this.sinkDown(this.content.length - 1); 15 | }, 16 | pop: function () { 17 | // Store the first element so we can return it later. 18 | var result = this.content[0]; 19 | // Get the element at the end of the array. 20 | var end = this.content.pop(); 21 | // If there are any elements left, put the end element at the 22 | // start, and let it bubble up. 23 | if (this.content.length > 0) { 24 | this.content[0] = end; 25 | this.bubbleUp(0); 26 | } 27 | return result; 28 | }, 29 | remove: function (node) { 30 | var i = this.content.indexOf(node); 31 | 32 | // When it is found, the process seen in 'pop' is repeated 33 | // to fill up the hole. 34 | var end = this.content.pop(); 35 | 36 | if (i !== this.content.length - 1) { 37 | this.content[i] = end; 38 | 39 | if (this.scoreFunction(end) < this.scoreFunction(node)) { 40 | this.sinkDown(i); 41 | } else { 42 | this.bubbleUp(i); 43 | } 44 | } 45 | }, 46 | size: function () { 47 | return this.content.length; 48 | }, 49 | rescoreElement: function (node) { 50 | this.sinkDown(this.content.indexOf(node)); 51 | }, 52 | sinkDown: function (n) { 53 | // Fetch the element that has to be sunk. 54 | var element = this.content[n]; 55 | 56 | // When at 0, an element can not sink any further. 57 | while (n > 0) { 58 | 59 | // Compute the parent element's index, and fetch it. 60 | var parentN = ((n + 1) >> 1) - 1, 61 | parent = this.content[parentN]; 62 | // Swap the elements if the parent is greater. 63 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 64 | this.content[parentN] = element; 65 | this.content[n] = parent; 66 | // Update 'n' to continue at the new position. 67 | n = parentN; 68 | } 69 | 70 | // Found a parent that is less, no need to sink any further. 71 | else { 72 | break; 73 | } 74 | } 75 | }, 76 | bubbleUp: function (n) { 77 | // Look up the target element and its score. 78 | var length = this.content.length, 79 | element = this.content[n], 80 | elemScore = this.scoreFunction(element); 81 | 82 | while (true) { 83 | // Compute the indices of the child elements. 84 | var child2N = (n + 1) << 1, 85 | child1N = child2N - 1; 86 | // This is used to store the new position of the element, 87 | // if any. 88 | var swap = null; 89 | // If the first child exists (is inside the array)... 90 | if (child1N < length) { 91 | // Look it up and compute its score. 92 | var child1 = this.content[child1N], 93 | child1Score = this.scoreFunction(child1); 94 | 95 | // If the score is less than our element's, we need to swap. 96 | if (child1Score < elemScore) 97 | swap = child1N; 98 | } 99 | 100 | // Do the same checks for the other child. 101 | if (child2N < length) { 102 | var child2 = this.content[child2N], 103 | child2Score = this.scoreFunction(child2); 104 | if (child2Score < (swap === null ? elemScore : child1Score)) { 105 | swap = child2N; 106 | } 107 | } 108 | 109 | // If the element needs to be moved, swap it, and continue. 110 | if (swap !== null) { 111 | this.content[n] = this.content[swap]; 112 | this.content[swap] = element; 113 | n = swap; 114 | } 115 | 116 | // Otherwise, we are done. 117 | else { 118 | break; 119 | } 120 | } 121 | } 122 | }); 123 | 124 | module.exports = BinaryHeap; -------------------------------------------------------------------------------- /lib/Channel.js: -------------------------------------------------------------------------------- 1 | var Class = require("abitbol"); 2 | var BABYLON = require("babylonjs"); 3 | 4 | var Channel = Class.$extend({ 5 | __init__: function() { 6 | this.portals = []; 7 | }, 8 | 9 | push: function (p1, p2) { 10 | if (p2 === undefined) p2 = p1; 11 | this.portals.push({ 12 | left: p1, 13 | right: p2 14 | }); 15 | }, 16 | 17 | _vequal: function (a, b) { 18 | return BABYLON.Vector3.DistanceSquared(a, b) < 0.00001; 19 | }, 20 | 21 | _triarea2: function (a, b, c) { 22 | var ax = b.x - a.x; 23 | var az = b.z - a.z; 24 | var bx = c.x - a.x; 25 | var bz = c.z - a.z; 26 | return bx * az - ax * bz; 27 | }, 28 | 29 | stringPull: function () { 30 | var portals = this.portals; 31 | var pts = []; 32 | // Init scan state 33 | var portalApex, portalLeft, portalRight; 34 | var apexIndex = 0, 35 | leftIndex = 0, 36 | rightIndex = 0; 37 | 38 | portalApex = portals[0].left; 39 | portalLeft = portals[0].left; 40 | portalRight = portals[0].right; 41 | 42 | // Add start point. 43 | pts.push(portalApex); 44 | 45 | for (var i = 1; i < portals.length; i++) { 46 | var left = portals[i].left; 47 | var right = portals[i].right; 48 | 49 | // Update right vertex. 50 | if (this._triarea2(portalApex, portalRight, right) >= 0.0) { 51 | if (this._vequal(portalApex, portalRight) || this._triarea2(portalApex, portalLeft, right) < 0.0) { 52 | // Tighten the funnel. 53 | portalRight = right; 54 | rightIndex = i; 55 | } else { 56 | // Right over left, insert left to path and restart scan from portal left point. 57 | pts.push(portalLeft); 58 | // Make current left the new apex. 59 | portalApex = portalLeft; 60 | apexIndex = leftIndex; 61 | // Reset portal 62 | portalLeft = portalApex; 63 | portalRight = portalApex; 64 | leftIndex = apexIndex; 65 | rightIndex = apexIndex; 66 | // Restart scan 67 | i = apexIndex; 68 | continue; 69 | } 70 | } 71 | 72 | // Update left vertex. 73 | if (this._triarea2(portalApex, portalLeft, left) <= 0.0) { 74 | if (this._vequal(portalApex, portalLeft) || this._triarea2(portalApex, portalRight, left) > 0.0) { 75 | // Tighten the funnel. 76 | portalLeft = left; 77 | leftIndex = i; 78 | } else { 79 | // Left over right, insert right to path and restart scan from portal right point. 80 | pts.push(portalRight); 81 | // Make current right the new apex. 82 | portalApex = portalRight; 83 | apexIndex = rightIndex; 84 | // Reset portal 85 | portalLeft = portalApex; 86 | portalRight = portalApex; 87 | leftIndex = apexIndex; 88 | rightIndex = apexIndex; 89 | // Restart scan 90 | i = apexIndex; 91 | continue; 92 | } 93 | } 94 | } 95 | 96 | if ((pts.length === 0) || (!this._vequal(pts[pts.length - 1], portals[portals.length - 1].left))) { 97 | // Append last point to path. 98 | pts.push(portals[portals.length - 1].left); 99 | } 100 | 101 | this.path = pts; 102 | return pts; 103 | } 104 | }); 105 | 106 | module.exports = Channel; -------------------------------------------------------------------------------- /lib/Navigation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Class = require("abitbol"); 4 | var _ = require("lodash"); 5 | var Astar = require("./Astar.js"); 6 | var Channel = require("./Channel.js"); 7 | 8 | var BABYLON = require("babylonjs"); 9 | 10 | /** 11 | * This component generates screenshots of 3D models, in order to preview them when they are rendered. 12 | * 13 | * @class Focus3D 14 | * @constructor 15 | */ 16 | 17 | var Navigation = Class.$extend({ 18 | __init__: function () { 19 | this.zoneNodes = {}; 20 | this.astar = new Astar(); 21 | this.yTolerance = 1; 22 | }, 23 | 24 | buildNodes: function (mesh) { 25 | var navigationMesh = this._buildNavigationMesh(mesh.geometry); 26 | 27 | var zoneNodes = this._groupNavMesh(navigationMesh); 28 | 29 | return zoneNodes; 30 | }, 31 | 32 | setZoneData: function (zone, data) { 33 | this.zoneNodes[zone] = data; 34 | }, 35 | 36 | setHeightTolerance: function(tolerance) { 37 | this.yTolerance = tolerance; 38 | }, 39 | 40 | getGroup: function (zone, position) { 41 | 42 | if (!this.zoneNodes[zone]) { 43 | return null; 44 | } 45 | 46 | var closestNodeGroup = null; 47 | 48 | var distance = Infinity; 49 | 50 | _.each(this.zoneNodes[zone].groups, function (group, index) { 51 | _.each(group, function (node) { 52 | var measuredDistance = BABYLON.Vector3.DistanceSquared(node.centroid, position); 53 | if (measuredDistance < distance) { 54 | closestNodeGroup = index; 55 | distance = measuredDistance; 56 | } 57 | }); 58 | }); 59 | 60 | return closestNodeGroup; 61 | }, 62 | 63 | getRandomNode: function (zone, group, nearPosition, nearRange) { 64 | 65 | if (!this.zoneNodes[zone]) return new BABYLON.Vector3(); 66 | 67 | nearPosition = nearPosition || null; 68 | nearRange = nearRange || 0; 69 | 70 | var candidates = []; 71 | 72 | var polygons = this.zoneNodes[zone].groups[group]; 73 | 74 | _.each(polygons, function (p) { 75 | if (nearPosition && nearRange) { 76 | if (BABYLON.Vector3.DistanceSquared(nearPosition, p.centroid) < nearRange * nearRange) { 77 | candidates.push(p.centroid); 78 | } 79 | } else { 80 | candidates.push(p.centroid); 81 | } 82 | }); 83 | 84 | return _.sample(candidates) || new BABYLON.Vector3(); 85 | }, 86 | 87 | projectOnNavmesh: function (position, zone, group) { 88 | var allNodes = this.zoneNodes[zone].groups[group]; 89 | var vertices = this.zoneNodes[zone].vertices; 90 | 91 | var closestNode = null; 92 | var distance = Infinity; 93 | var finalProj = null, 94 | proj = null, 95 | node = null, 96 | measuredDistance = 0; 97 | 98 | for (var i = 0; i < allNodes.length; i++) { 99 | node = allNodes[i]; 100 | 101 | proj = this._getProjectionOnNode(position, node, vertices); 102 | measuredDistance = BABYLON.Vector3.DistanceSquared(proj, position); 103 | 104 | if (measuredDistance < distance) { 105 | distance = measuredDistance; 106 | //this.meshes[3].position.copyFrom(proj); 107 | finalProj = proj; 108 | closestNode = node; 109 | } 110 | 111 | } 112 | 113 | return finalProj; 114 | }, 115 | 116 | _projectPointOnPlane: function (point, plane) { 117 | var coef = BABYLON.Vector3.Dot(point, plane.normal) + plane.d; 118 | var proj = point.subtract(plane.normal.scale(coef)); 119 | 120 | return proj; 121 | }, 122 | 123 | _getProjectionOnNode: function (position, node, vertices) { 124 | 125 | var A = this.getVectorFrom(vertices, node.vertexIds[0]); 126 | var B = this.getVectorFrom(vertices, node.vertexIds[1]); 127 | var C = this.getVectorFrom(vertices, node.vertexIds[2]); 128 | var u = B.subtract(A); 129 | var v = C.subtract(A); 130 | var n = BABYLON.Vector3.Cross(u, v).normalize(); 131 | 132 | var plane = { 133 | normal: n, 134 | d: -BABYLON.Vector3.Dot(A, n) 135 | }; 136 | var p = this._projectPointOnPlane(position, plane); 137 | // Compute barycentric coordinates (u, v, w) for 138 | // point p with respect to triangle (a, b, c) 139 | var barycentric = function (p, a, b, c) { 140 | var ret = {}; 141 | 142 | var v0 = c.subtract(a), 143 | v1 = b.subtract(a), 144 | v2 = p.subtract(a); 145 | 146 | var d00 = BABYLON.Vector3.Dot(v0, v0); 147 | var d01 = BABYLON.Vector3.Dot(v0, v1); 148 | var d02 = BABYLON.Vector3.Dot(v0, v2); 149 | var d11 = BABYLON.Vector3.Dot(v1, v1); 150 | var d12 = BABYLON.Vector3.Dot(v1, v2); 151 | var denom = d00 * d11 - d01 * d01; 152 | ret.u = (d11 * d02 - d01 * d12) / denom; 153 | ret.v = (d00 * d12 - d01 * d02) / denom; 154 | ret.w = 1 - ret.u - ret.v; 155 | 156 | return ret; 157 | }; 158 | 159 | var bary = barycentric(p, A, B, C); 160 | 161 | bary.u = Math.min(Math.max(bary.u, 0), 1); 162 | bary.v = Math.min(Math.max(bary.v, 0), 1); 163 | 164 | if (bary.u + bary.v >= 1) { 165 | var sum = bary.u + bary.v; 166 | bary.u /= sum; 167 | bary.v /= sum; 168 | } 169 | 170 | var proj = A.add(B.subtract(A).scale(bary.v).add(C.subtract(A).scale(bary.u))); 171 | 172 | return proj; 173 | }, 174 | 175 | findPath: function (startPosition, targetPosition, zone, group) { 176 | 177 | var allNodes = this.zoneNodes[zone].groups[group]; 178 | var vertices = this.zoneNodes[zone].vertices; 179 | 180 | var startingNode = null; 181 | 182 | for(var i = 0; i < allNodes.length; i++) { 183 | if (this._isVectorInPolygon(startPosition, allNodes[i], vertices)) { 184 | startingNode = allNodes[i]; 185 | break; 186 | } 187 | } 188 | 189 | var endNode = null; 190 | 191 | for(var i = 0; i < allNodes.length; i++) { 192 | if (this._isVectorInPolygon(targetPosition, allNodes[i], vertices)) { 193 | endNode = allNodes[i]; 194 | break; 195 | } 196 | } 197 | // If we can't find any node, theres no path to target 198 | if (!startingNode || !endNode) { 199 | return null; 200 | } 201 | 202 | if (startingNode.id != endNode.id) { // if the starting node and target node are at the same polygon skip searching and funneling as there is no obstacle. 203 | var paths = this.astar.search(allNodes, startingNode, endNode); 204 | } else { 205 | vectors = []; 206 | vectors.push(new BABYLON.Vector3(targetPosition.x, targetPosition.y, targetPosition.z)); 207 | return vectors; 208 | } 209 | 210 | var getPortalFromTo = function (a, b) { 211 | for (var i = 0; i < a.neighbours.length; i++) { 212 | if (a.neighbours[i] === b.id) { 213 | return a.portals[i]; 214 | } 215 | } 216 | }; 217 | 218 | // We got the corridor 219 | // Now pull the rope 220 | 221 | var channel = new Channel(); 222 | 223 | channel.push(startPosition); 224 | 225 | for (var i = 0; i < paths.length; i++) { 226 | var polygon = paths[i]; 227 | 228 | var nextPolygon = paths[i + 1]; 229 | 230 | if (nextPolygon) { 231 | var portals = getPortalFromTo(polygon, nextPolygon); 232 | channel.push( 233 | this.getVectorFrom(vertices, portals[0]), 234 | this.getVectorFrom(vertices, portals[1]) 235 | ); 236 | } 237 | 238 | } 239 | 240 | channel.push(targetPosition); 241 | 242 | channel.stringPull(); 243 | 244 | 245 | var vectors = []; 246 | 247 | channel.path.forEach(function (c) { 248 | var vec = new BABYLON.Vector3(c.x, c.y, c.z); 249 | 250 | // console.log(vec.clone().sub(startPosition).length()); 251 | 252 | // Ensure the intermediate steps aren't too close to the start position 253 | // var dist = vec.clone().sub(startPosition).lengthSq(); 254 | // if (dist > 0.01 * 0.01) { 255 | vectors.push(vec); 256 | // } 257 | 258 | 259 | }); 260 | 261 | // We don't need the first one, as we already know our start position 262 | vectors.shift(); 263 | 264 | return vectors; 265 | }, 266 | 267 | _isPointInPoly: function (poly, pt) { 268 | for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) 269 | ((poly[i].z <= pt.z && pt.z < poly[j].z) || (poly[j].z <= pt.z && pt.z < poly[i].z)) && (pt.x < (poly[j].x - poly[i].x) * (pt.z - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x) && (c = !c); 270 | return c; 271 | }, 272 | 273 | _isVectorInPolygon: function (vector, polygon, vertices) { 274 | 275 | // reference point will be the centroid of the polygon 276 | // We need to rotate the vector as well as all the points which the polygon uses 277 | var lowestPoint = 100000; 278 | var highestPoint = -100000; 279 | 280 | var polygonVertices = []; 281 | 282 | _.each(polygon.vertexIds, function (vId) { 283 | var point = this.getVectorFrom(vertices, vId); 284 | lowestPoint = Math.min(point.y, lowestPoint); 285 | highestPoint = Math.max(point.y, highestPoint); 286 | polygonVertices.push(point); 287 | }.bind(this)); 288 | 289 | if (vector.y < highestPoint + this.yTolerance && vector.y > lowestPoint - this.yTolerance && 290 | this._isPointInPoly(polygonVertices, vector)) { 291 | return true; 292 | } 293 | return false; 294 | }, 295 | 296 | _computeCentroids: function (geometry) { 297 | var centroids = []; 298 | var indices = geometry.getIndices(); 299 | var vertices = geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind); 300 | var c = new BABYLON.Vector3(0, 0, 0); 301 | 302 | for (var f = 0; f < indices.length; f += 3) { 303 | var p1 = this.getVectorFrom(vertices, indices[f]); 304 | var p2 = this.getVectorFrom(vertices, indices[f + 1]); 305 | var p3 = this.getVectorFrom(vertices, indices[f + 2]); 306 | 307 | c.copyFromFloats(0, 0, 0); 308 | 309 | c.addInPlace(p1); 310 | c.addInPlace(p2); 311 | c.addInPlace(p3); 312 | 313 | c.scaleInPlace(1 / 3); 314 | 315 | centroids.push(c.clone()); 316 | } 317 | geometry.centroids = centroids; 318 | }, 319 | 320 | 321 | _roundNumber: function (number, decimals) { 322 | var newnumber = new Number(number + '').toFixed(parseInt(decimals)); 323 | return parseFloat(newnumber); 324 | }, 325 | 326 | _mergeVertexIds: function (aList, bList) { 327 | 328 | var sharedVertices = []; 329 | 330 | aList.forEach(function (vId) { 331 | if (_.includes(bList, vId)) { 332 | sharedVertices.push(vId); 333 | } 334 | }); 335 | 336 | if (sharedVertices.length < 2) return []; 337 | 338 | // console.log("TRYING aList:", aList, ", bList:", bList, ", sharedVertices:", sharedVertices); 339 | 340 | if (_.includes(sharedVertices, aList[0]) && _.includes(sharedVertices, aList[aList.length - 1])) { 341 | // Vertices on both edges are bad, so shift them once to the left 342 | aList.push(aList.shift()); 343 | } 344 | 345 | if (_.includes(sharedVertices, bList[0]) && _.includes(sharedVertices, bList[bList.length - 1])) { 346 | // Vertices on both edges are bad, so shift them once to the left 347 | bList.push(bList.shift()); 348 | } 349 | 350 | // Again! 351 | sharedVertices = []; 352 | 353 | aList.forEach(function (vId) { 354 | if (_.includes(bList, vId)) { 355 | sharedVertices.push(vId); 356 | } 357 | }); 358 | 359 | var clockwiseMostSharedVertex = sharedVertices[1]; 360 | var counterClockwiseMostSharedVertex = sharedVertices[0]; 361 | 362 | 363 | var cList = _.clone(aList); 364 | while (cList[0] !== clockwiseMostSharedVertex) { 365 | cList.push(cList.shift()); 366 | } 367 | 368 | var c = 0; 369 | 370 | var temp = _.clone(bList); 371 | while (temp[0] !== counterClockwiseMostSharedVertex) { 372 | temp.push(temp.shift()); 373 | 374 | if (c++ > 10) break; 375 | } 376 | 377 | // Shave 378 | temp.shift(); 379 | temp.pop(); 380 | 381 | cList = cList.concat(temp); 382 | 383 | // console.log("aList:", aList, ", bList:", bList, ", cList:", cList, ", sharedVertices:", sharedVertices); 384 | 385 | return cList; 386 | }, 387 | 388 | _setPolygonCentroid: function (polygon, navigationMesh) { 389 | var sum = new BABYLON.Vector3(0, 0, 0); 390 | 391 | var vertices = navigationMesh.vertices; 392 | 393 | _.each(polygon.vertexIds, function (vId) { 394 | sum.x += vertices[vId * 3]; 395 | sum.y += vertices[vId * 3 + 1]; 396 | sum.z += vertices[vId * 3 + 2]; 397 | }); 398 | 399 | sum.scaleInPlace(1 / polygon.vertexIds.length); 400 | 401 | polygon.centroid.copyFrom(sum); 402 | }, 403 | 404 | getVectorFrom: function (vertices, id, _vector) { 405 | if (_vector) { 406 | _vector.copyFromFloats(vertices[id * 3], vertices[id * 3 + 1], vertices[id * 3 + 2]); 407 | return _vector; 408 | } 409 | return new BABYLON.Vector3(vertices[id * 3], vertices[id * 3 + 1], vertices[id * 3 + 2]); 410 | }, 411 | 412 | _cleanPolygon: function (polygon, navigationMesh) { 413 | 414 | var newVertexIds = []; 415 | 416 | var vertices = navigationMesh.vertices; 417 | 418 | for (var i = 0; i < polygon.vertexIds.length; i++) { 419 | 420 | var vertex = this.getVectorFrom(vertices, polygon.vertexIds[i]); 421 | 422 | var nextVertexId, previousVertexId; 423 | var nextVertex, previousVertex; 424 | 425 | // console.log("nextVertex: ", nextVertex); 426 | 427 | if (i === 0) { 428 | nextVertexId = polygon.vertexIds[1]; 429 | previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 1]; 430 | } else if (i === polygon.vertexIds.length - 1) { 431 | nextVertexId = polygon.vertexIds[0]; 432 | previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 2]; 433 | } else { 434 | nextVertexId = polygon.vertexIds[i + 1]; 435 | previousVertexId = polygon.vertexIds[i - 1]; 436 | } 437 | 438 | nextVertex = this.getVectorFrom(vertices, nextVertexId); 439 | previousVertex = this.getVectorFrom(vertices, previousVertexId); 440 | 441 | var a = nextVertex.clone().sub(vertex); 442 | var b = previousVertex.clone().sub(vertex); 443 | 444 | var angle = a.angleTo(b); 445 | 446 | // console.log(angle); 447 | 448 | if (angle > Math.PI - 0.01 && angle < Math.PI + 0.01) { 449 | // Unneccesary vertex 450 | // console.log("Unneccesary vertex: ", polygon.vertexIds[i]); 451 | // console.log("Angle between "+previousVertexId+", "+polygon.vertexIds[i]+" "+nextVertexId+" was: ", angle); 452 | 453 | 454 | // Remove the neighbours who had this vertex 455 | var goodNeighbours = []; 456 | polygon.neighbours.forEach(function (neighbour) { 457 | if (!_.includes(neighbour.vertexIds, polygon.vertexIds[i])) { 458 | goodNeighbours.push(neighbour); 459 | } 460 | }); 461 | polygon.neighbours = goodNeighbours; 462 | 463 | 464 | // TODO cleanup the list of vertices and rebuild vertexIds for all polygons 465 | } else { 466 | newVertexIds.push(polygon.vertexIds[i]); 467 | } 468 | 469 | } 470 | 471 | // console.log("New vertexIds: ", newVertexIds); 472 | 473 | polygon.vertexIds = newVertexIds; 474 | 475 | this._setPolygonCentroid(polygon, navigationMesh); 476 | 477 | }, 478 | 479 | _isConvex: function (polygon, navigationMesh) { 480 | 481 | var vertices = navigationMesh.vertices; 482 | 483 | if (polygon.vertexIds.length < 3) return false; 484 | 485 | var convex = true; 486 | 487 | var total = 0; 488 | 489 | var results = []; 490 | 491 | for (var i = 0; i < polygon.vertexIds.length; i++) { 492 | 493 | var vertex = this.getVectorFrom(vertices, polygon.vertexIds[i]); 494 | 495 | var nextVertex, previousVertex; 496 | 497 | // console.log("nextVertex: ", nextVertex); 498 | 499 | if (i === 0) { 500 | nextVertex = this.getVectorFrom(vertices, polygon.vertexIds[1]); 501 | previousVertex = this.getVectorFrom(vertices, polygon.vertexIds[polygon.vertexIds.length - 1]); 502 | } else if (i === polygon.vertexIds.length - 1) { 503 | nextVertex = this.getVectorFrom(vertices, polygon.vertexIds[0]); 504 | previousVertex = this.getVectorFrom(vertices, polygon.vertexIds[polygon.vertexIds.length - 2]); 505 | } else { 506 | nextVertex = this.getVectorFrom(vertices, polygon.vertexIds[i + 1]); 507 | previousVertex = this.getVectorFrom(vertices, polygon.vertexIds[i - 1]); 508 | } 509 | 510 | var a = nextVertex.clone().sub(vertex); 511 | var b = previousVertex.clone().sub(vertex); 512 | 513 | var angle = a.angleTo(b); 514 | total += angle; 515 | 516 | // console.log(angle); 517 | if (angle === Math.PI || angle === 0) return false; 518 | 519 | var r = BABYLON.Vector3.Cross(a, b).y; 520 | results.push(r); 521 | // console.log("pushed: ", r); 522 | } 523 | 524 | // if ( total > (polygon.vertexIds.length-2)*Math.PI ) return false; 525 | 526 | results.forEach(function (r) { 527 | if (r === 0) convex = false; 528 | }); 529 | 530 | if (results[0] > 0) { 531 | results.forEach(function (r) { 532 | if (r < 0) convex = false; 533 | }); 534 | } else { 535 | results.forEach(function (r) { 536 | if (r > 0) convex = false; 537 | }); 538 | } 539 | 540 | // console.log("allowed: "+total+", max: "+(polygon.vertexIds.length-2)*Math.PI); 541 | // if ( total > (polygon.vertexIds.length-2)*Math.PI ) convex = false; 542 | 543 | // console.log("Convex: "+(convex ? "true": "false")); 544 | 545 | 546 | 547 | return convex; 548 | }, 549 | 550 | _buildPolygonGroups: function (navigationMesh) { 551 | 552 | var polygons = navigationMesh.polygons; 553 | 554 | var polygonGroups = []; 555 | var groupCount = 0; 556 | 557 | var spreadGroupId = function (polygon) { 558 | _.each(polygon.neighbours, function (neighbour) { 559 | if (_.isUndefined(neighbour.group)) { 560 | neighbour.group = polygon.group; 561 | spreadGroupId(neighbour); 562 | } 563 | }); 564 | }; 565 | 566 | _.each(polygons, function (polygon) { 567 | 568 | if (_.isUndefined(polygon.group)) { 569 | polygon.group = groupCount++; 570 | // Spread it 571 | spreadGroupId(polygon); 572 | } 573 | 574 | if (!polygonGroups[polygon.group]) polygonGroups[polygon.group] = []; 575 | 576 | polygonGroups[polygon.group].push(polygon); 577 | }); 578 | 579 | console.log("Groups built: ", polygonGroups.length); 580 | 581 | return polygonGroups; 582 | }, 583 | 584 | _array_intersect: function () { 585 | var i, shortest, nShortest, n, len, ret = [], 586 | obj = {}, 587 | nOthers; 588 | nOthers = arguments.length - 1; 589 | nShortest = arguments[0].length; 590 | shortest = 0; 591 | for (i = 0; i <= nOthers; i++) { 592 | n = arguments[i].length; 593 | if (n < nShortest) { 594 | shortest = i; 595 | nShortest = n; 596 | } 597 | } 598 | 599 | for (i = 0; i <= nOthers; i++) { 600 | n = (i === shortest) ? 0 : (i || shortest); //Read the shortest array first. Read the first array instead of the shortest 601 | len = arguments[n].length; 602 | for (var j = 0; j < len; j++) { 603 | var elem = arguments[n][j]; 604 | if (obj[elem] === i - 1) { 605 | if (i === nOthers) { 606 | ret.push(elem); 607 | obj[elem] = 0; 608 | } else { 609 | obj[elem] = i; 610 | } 611 | } else if (i === 0) { 612 | obj[elem] = 0; 613 | } 614 | } 615 | } 616 | return ret; 617 | }, 618 | 619 | _buildPolygonNeighbours: function (polygon, navigationMesh) { 620 | polygon.neighbours = []; 621 | 622 | // All other nodes that contain at least two of our vertices are our neighbours 623 | for (var i = 0, len = navigationMesh.polygons.length; i < len; i++) { 624 | if (polygon === navigationMesh.polygons[i]) continue; 625 | 626 | // Don't check polygons that are too far, since the intersection tests take a long time 627 | if (BABYLON.Vector3.DistanceSquared(polygon.centroid, navigationMesh.polygons[i].centroid) > 100 * 100) continue; 628 | 629 | var matches = this._array_intersect(polygon.vertexIds, navigationMesh.polygons[i].vertexIds); 630 | // var matches = _.intersection(polygon.vertexIds, navigationMesh.polygons[i].vertexIds); 631 | 632 | if (matches.length >= 2) { 633 | polygon.neighbours.push(navigationMesh.polygons[i]); 634 | } 635 | } 636 | }, 637 | 638 | _buildPolygonsFromGeometry: function (geometry) { 639 | 640 | var polygons = []; 641 | var vertices = geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind); 642 | var indices = geometry.getIndices(); 643 | var polygonId = 1; 644 | 645 | console.log("Vertices:", vertices.length / 3, "polygons:", indices.length / 3); 646 | 647 | // Convert the faces into a custom format that supports more than 3 vertices 648 | for (var i = 0; i < indices.length; i += 3) { 649 | 650 | var a = this.getVectorFrom(vertices, indices[i]); 651 | var b = this.getVectorFrom(vertices, indices[i + 1]); 652 | var c = this.getVectorFrom(vertices, indices[i + 2]); 653 | var normal = BABYLON.Vector3.Cross(b.subtract(a), b.subtract(c)).normalize(); 654 | 655 | polygons.push({ 656 | id: polygonId++, 657 | vertexIds: [indices[i], indices[i + 1], indices[i + 2]], 658 | centroid: geometry.centroids[i / 3], 659 | normal: normal, 660 | neighbours: [] 661 | }); 662 | } 663 | 664 | var navigationMesh = { 665 | polygons: polygons, 666 | vertices: vertices 667 | }; 668 | 669 | // Build a list of adjacent polygons 670 | _.each(polygons, function (polygon) { 671 | this._buildPolygonNeighbours(polygon, navigationMesh); 672 | }.bind(this)); 673 | 674 | return navigationMesh; 675 | }, 676 | 677 | _cleanNavigationMesh: function (navigationMesh) { 678 | 679 | var polygons = navigationMesh.polygons; 680 | var vertices = navigationMesh.vertices; 681 | 682 | 683 | // Remove steep triangles 684 | var up = new BABYLON.Vector3(0, 1, 0); 685 | polygons = _.filter(polygons, function (polygon) { 686 | var angle = Math.acos(BABYLON.Vector3.Dot(up, polygon.normal)); 687 | return angle < (Math.PI / 4); 688 | }); 689 | 690 | 691 | // Remove unnecessary edges using the Hertel-Mehlhorn algorithm 692 | 693 | // 1. Find a pair of adjacent nodes (i.e., two nodes that share an edge between them) 694 | // whose normals are nearly identical (i.e., their surfaces face the same direction). 695 | 696 | 697 | var newPolygons = []; 698 | 699 | _.each(polygons, function (polygon) { 700 | 701 | if (polygon.toBeDeleted) return; 702 | 703 | var keepLooking = true; 704 | 705 | while (keepLooking) { 706 | keepLooking = false; 707 | 708 | _.each(polygon.neighbours, function (otherPolygon) { 709 | 710 | if (polygon === otherPolygon) return; 711 | 712 | if (Math.acos(BABYLON.Vector3.Dot(polygon.normal, otherPolygon.normal)) < 0.01) { 713 | // That's pretty equal alright! 714 | 715 | // Merge otherPolygon with polygon 716 | 717 | var testPolygon = { 718 | vertexIds: this._mergeVertexIds(polygon.vertexIds, otherPolygon.vertexIds), 719 | neighbours: polygon.neighbours, 720 | normal: polygon.normal.clone(), 721 | centroid: polygon.centroid.clone() 722 | }; 723 | 724 | this._cleanPolygon(testPolygon, navigationMesh); 725 | 726 | if (this._isConvex(testPolygon, navigationMesh)) { 727 | otherPolygon.toBeDeleted = true; 728 | 729 | 730 | // Inherit the neighbours from the to be merged polygon, except ourself 731 | _.each(otherPolygon.neighbours, function (otherPolygonNeighbour) { 732 | 733 | // Set this poly to be merged to be no longer our neighbour 734 | otherPolygonNeighbour.neighbours = _.without(otherPolygonNeighbour.neighbours, otherPolygon); 735 | 736 | if (otherPolygonNeighbour !== polygon) { 737 | // Tell the old Polygon's neighbours about the new neighbour who has merged 738 | otherPolygonNeighbour.neighbours.push(polygon); 739 | } else { 740 | // For ourself, we don't need to know about ourselves 741 | // But we inherit the old neighbours 742 | polygon.neighbours = polygon.neighbours.concat(otherPolygon.neighbours); 743 | polygon.neighbours = _.uniq(polygon.neighbours); 744 | 745 | // Without ourselves in it! 746 | polygon.neighbours = _.without(polygon.neighbours, polygon); 747 | } 748 | }); 749 | 750 | polygon.vertexIds = this._mergeVertexIds(polygon.vertexIds, otherPolygon.vertexIds); 751 | 752 | this._cleanPolygon(polygon, navigationMesh); 753 | 754 | keepLooking = true; 755 | } 756 | 757 | } 758 | }.bind(this)); 759 | } 760 | 761 | 762 | if (!polygon.toBeDeleted) { 763 | newPolygons.push(polygon); 764 | } 765 | 766 | }); 767 | 768 | var isUsed = function (vId) { 769 | var contains = false; 770 | _.each(newPolygons, function (p) { 771 | if (!contains && _.includes(p.vertexIds, vId)) { 772 | contains = true; 773 | } 774 | }); 775 | return contains; 776 | }; 777 | 778 | // Clean vertices 779 | for (var i = 0; i < vertices.length; i++) { 780 | if (!isUsed(i)) { 781 | 782 | // Decrement all vertices that are higher than i 783 | _.each(newPolygons, function (p) { 784 | for (var j = 0; j < p.vertexIds.length; j++) { 785 | if (p.vertexIds[j] > i) { 786 | p.vertexIds[j]--; 787 | } 788 | } 789 | }); 790 | 791 | vertices.splice(i, 1); 792 | i--; 793 | } 794 | 795 | } 796 | 797 | navigationMesh.polygons = newPolygons; 798 | navigationMesh.vertices = vertices; 799 | 800 | }, 801 | 802 | _buildNavigationMesh: function (geometry) { 803 | // Prepare geometry 804 | this._computeCentroids(geometry); 805 | 806 | this._mergeVertices(geometry); 807 | // BABYLON.GeometryUtils.triangulateQuads(geometry); 808 | 809 | // console.log("vertices:", geometry.vertices.length, "polygons:", geometry.faces.length); 810 | 811 | var navigationMesh = this._buildPolygonsFromGeometry(geometry); 812 | 813 | // cleanNavigationMesh(navigationMesh); 814 | // console.log("Pre-clean:", navigationMesh.polygons.length, "polygons,", navigationMesh.vertices.length, "vertices."); 815 | 816 | // console.log("") 817 | // console.log("Vertices:", navigationMesh.vertices.length, "polygons,", navigationMesh.polygons.length, "vertices."); 818 | 819 | return navigationMesh; 820 | }, 821 | 822 | _mergeVertices: function (geometry) { 823 | var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) 824 | var unique = [], 825 | changes = []; 826 | 827 | var v, key; 828 | var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 829 | var precision = Math.pow(10, precisionPoints); 830 | var indices; 831 | var ind = geometry.getIndices(), 832 | vert = geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind); 833 | 834 | for (var i = 0; i < vert.length; i += 3) { 835 | 836 | v = new BABYLON.Vector3(vert[i], vert[i + 1], vert[i + 2]); 837 | key = Math.round(v.x * precision) + '_' + Math.round(v.y * precision) + '_' + Math.round(v.z * precision); 838 | 839 | if (verticesMap[key] === undefined) { 840 | 841 | verticesMap[key] = i / 3; 842 | unique.push(v.clone()); 843 | changes[i / 3] = unique.length - 1; 844 | 845 | } else { 846 | 847 | //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); 848 | changes[i / 3] = changes[verticesMap[key]]; 849 | 850 | } 851 | 852 | } 853 | 854 | 855 | // if faces are completely degenerate after merging vertices, we 856 | // have to remove them from the geometry. 857 | var faceIndicesToRemove = []; 858 | 859 | for (i = 0; i < ind.length; i += 3) { 860 | 861 | ind[i] = changes[ind[i]]; 862 | ind[i + 1] = changes[ind[i + 1]]; 863 | ind[i + 2] = changes[ind[i + 2]]; 864 | 865 | indices = [ind[i], ind[i + 1], ind[i + 2]]; 866 | 867 | var dupIndex = -1; 868 | 869 | // if any duplicate vertices are found in a Face3 870 | // we have to remove the face as nothing can be saved 871 | for (var n = 0; n < 3; n++) { 872 | 873 | if (indices[n] === indices[(n + 1) % 3]) { 874 | 875 | dupIndex = n; 876 | faceIndicesToRemove.push(i); 877 | break; 878 | 879 | } 880 | 881 | } 882 | 883 | } 884 | 885 | for (i = faceIndicesToRemove.length - 1; i >= 0; i--) { 886 | 887 | var idx = faceIndicesToRemove[i]; 888 | 889 | ind.splice(idx, 3); 890 | 891 | } 892 | 893 | // Use unique set of vertices 894 | 895 | var diff = vert.length / 3 - unique.length; 896 | vert = []; 897 | for (i = 0; i < unique.length; i++) { 898 | vert.push(unique[i].x, unique[i].y, unique[i].z); 899 | } 900 | 901 | geometry.setIndices(ind); 902 | geometry.setVerticesData(BABYLON.VertexBuffer.PositionKind, vert); 903 | 904 | return diff; 905 | }, 906 | 907 | 908 | _getSharedVerticesInOrder: function (a, b) { 909 | 910 | var aList = a.vertexIds; 911 | var bList = b.vertexIds; 912 | 913 | var sharedVertices = []; 914 | 915 | _.each(aList, function (vId) { 916 | if (_.includes(bList, vId)) { 917 | sharedVertices.push(vId); 918 | } 919 | }); 920 | 921 | if (sharedVertices.length < 2) return []; 922 | 923 | // console.log("TRYING aList:", aList, ", bList:", bList, ", sharedVertices:", sharedVertices); 924 | 925 | if (_.includes(sharedVertices, aList[0]) && _.includes(sharedVertices, aList[aList.length - 1])) { 926 | // Vertices on both edges are bad, so shift them once to the left 927 | aList.push(aList.shift()); 928 | } 929 | 930 | if (_.includes(sharedVertices, bList[0]) && _.includes(sharedVertices, bList[bList.length - 1])) { 931 | // Vertices on both edges are bad, so shift them once to the left 932 | bList.push(bList.shift()); 933 | } 934 | 935 | // Again! 936 | sharedVertices = []; 937 | 938 | _.each(aList, function (vId) { 939 | if (_.includes(bList, vId)) { 940 | sharedVertices.push(vId); 941 | } 942 | }); 943 | 944 | return sharedVertices; 945 | }, 946 | 947 | _groupNavMesh: function (navigationMesh) { 948 | 949 | var saveObj = {}; 950 | 951 | _.each(navigationMesh.vertices, function (v) { 952 | v = this._roundNumber(v, 2); 953 | }.bind(this)); 954 | 955 | saveObj.vertices = navigationMesh.vertices; 956 | 957 | var groups = this._buildPolygonGroups(navigationMesh); 958 | 959 | saveObj.groups = []; 960 | 961 | var findPolygonIndex = function (group, p) { 962 | for (var i = 0; i < group.length; i++) { 963 | if (p === group[i]) return i; 964 | } 965 | }; 966 | 967 | _.each(groups, function (group) { 968 | 969 | var newGroup = []; 970 | 971 | _.each(group, function (p) { 972 | 973 | var neighbours = []; 974 | 975 | _.each(p.neighbours, function (n) { 976 | neighbours.push(findPolygonIndex(group, n)); 977 | }); 978 | 979 | 980 | // Build a portal list to each neighbour 981 | var portals = []; 982 | _.each(p.neighbours, function (n) { 983 | portals.push(this._getSharedVerticesInOrder(p, n)); 984 | }.bind(this)); 985 | 986 | 987 | p.centroid.x = this._roundNumber(p.centroid.x, 2); 988 | p.centroid.y = this._roundNumber(p.centroid.y, 2); 989 | p.centroid.z = this._roundNumber(p.centroid.z, 2); 990 | 991 | newGroup.push({ 992 | id: findPolygonIndex(group, p), 993 | neighbours: neighbours, 994 | vertexIds: p.vertexIds, 995 | centroid: p.centroid, 996 | portals: portals 997 | }); 998 | 999 | }.bind(this)); 1000 | 1001 | saveObj.groups.push(newGroup); 1002 | }.bind(this)); 1003 | 1004 | return saveObj; 1005 | }, 1006 | }); 1007 | 1008 | module.exports = Navigation; 1009 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babylon-navigation-mesh", 3 | "version": "1.2.6", 4 | "description": "A toolkit to move on navigation mesh with BABYLONJS", 5 | "scripts": { 6 | "test": "jshint lib", 7 | "build": "npm run build-es5 && npm run build-dist && npm run build-uglify", 8 | "build-es5": "cd lib/ && babel *.js -d ../lib-es5", 9 | "build-dist": "rm -rf dist && mkdir dist && browserify -t [ babelify --presets [ es2015 ] ] -s Navigation lib/Navigation.js -o dist/babylon-navigation-mesh.js", 10 | "build-uglify": "uglifyjs dist/babylon-navigation-mesh.js -o dist/babylon-navigation-mesh.min.js", 11 | "watchify": "rm -rf dist && mkdir dist && watchify -v -t [ babelify --presets [ es2015 ] ] -s Navigation lib/Navigation.js -o dist/babylon-navigation-mesh.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/wanadev/babylon-navigation-mesh.git" 16 | }, 17 | "author": { 18 | "name": "Wanadev", 19 | "url": "http://wanadev.fr/" 20 | }, 21 | "maintainers": [ 22 | { 23 | "name": "clementlvsr", 24 | "email": "clevasseur@wanadev.fr" 25 | }, 26 | { 27 | "name": "wanadev", 28 | "email": "it@wanadev.fr" 29 | } 30 | ], 31 | "license": "MIT", 32 | "main": "./lib-es5/Navigation.js", 33 | "devDependencies": { 34 | "babel": "^6.5.2", 35 | "babel-cli": "^6.9.0", 36 | "babel-preset-es2015": "^6.18.0", 37 | "babelify": "^7.3.0", 38 | "browserify": "^13.0.1", 39 | "jshint": "^2.9.2", 40 | "uglify-js": "^2.6.2", 41 | "watchify": "^3.7.0" 42 | }, 43 | "dependencies": { 44 | "abitbol": "^1.0.3", 45 | "babylonjs": "^2.3.0", 46 | "lodash": "^4.5.1" 47 | }, 48 | "bugs": { 49 | "url": "https://github.com/wanadev/babylon-navigation-mesh/issues" 50 | }, 51 | "keywords": [ 52 | "babylon.js", 53 | "babylon", 54 | "navigation", 55 | "mesh", 56 | "agent", 57 | "AI", 58 | "path finding" 59 | ], 60 | "gitHead": "7c63b5e12cb29e178cdf7a0d1ee46523541e3882", 61 | "_id": "babylon-navigation-mesh@1.0.0", 62 | "_shasum": "80708436afe7a073b405e2bb41528e301664ff3a", 63 | "_from": "babylon-navigation-mesh@*" 64 | } 65 | --------------------------------------------------------------------------------