├── .npmignore
├── .babelrc
├── demo
├── demo.gif
├── mesh
│ ├── level.babylon.manifest
│ ├── level.blend
│ ├── level.mtl
│ ├── level.log
│ └── level.babylon
└── demo.js
├── .jshintrc
├── index.html
├── LICENSE
├── README.md
├── package.json
├── lib
├── Channel.js
├── Astar.js
├── BinaryHeap.js
└── Navigation.js
└── lib-es5
├── Channel.js
├── Astar.js
├── BinaryHeap.js
└── Navigation.js
/.npmignore:
--------------------------------------------------------------------------------
1 | demo
2 | article.md
3 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
--------------------------------------------------------------------------------
/demo/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wanadev/babylon-navigation-mesh/HEAD/demo/demo.gif
--------------------------------------------------------------------------------
/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/HEAD/demo/mesh/level.blend
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Babylon-navigation-mesh
2 |
3 | [](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 | 
55 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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-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;
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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-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/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/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 |
--------------------------------------------------------------------------------
/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":[]}
--------------------------------------------------------------------------------