├── texture ├── ring.png ├── sky.jpg ├── wall.png ├── sonic.jpg ├── sonic.png ├── spring.jpg ├── wall2.png ├── monitor.png ├── wall-ghost.png ├── monitor-display.jpg ├── monitor-display.png ├── monitor-specular.png └── water-shield-icon.png ├── js ├── Engine.js ├── Sky.js ├── Spring.js ├── json.addons.js ├── InputController.js ├── Ring.js ├── console.js ├── main.js ├── ResourceManager.js ├── WorldEngine.js ├── Player.js └── GraphicsEngine.js ├── index.html ├── css └── style.css ├── shader ├── depth.jsonshader ├── sphere.jsonshader ├── metal.jsonshader ├── sonic.jsonshader ├── sky.jsonshader ├── monitor.jsonshader ├── glow.jsonshader └── default.jsonshader ├── README.md ├── LICENSE ├── tools └── md3tojson3d.c ├── lib ├── sylvester.addons.js └── sylvester.js ├── level └── lvl0.jsonlevel └── mesh ├── sky.jsonmesh └── spinball.jsonmesh /texture/ring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/ring.png -------------------------------------------------------------------------------- /texture/sky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/sky.jpg -------------------------------------------------------------------------------- /texture/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/wall.png -------------------------------------------------------------------------------- /texture/sonic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/sonic.jpg -------------------------------------------------------------------------------- /texture/sonic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/sonic.png -------------------------------------------------------------------------------- /texture/spring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/spring.jpg -------------------------------------------------------------------------------- /texture/wall2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/wall2.png -------------------------------------------------------------------------------- /texture/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/monitor.png -------------------------------------------------------------------------------- /texture/wall-ghost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/wall-ghost.png -------------------------------------------------------------------------------- /texture/monitor-display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/monitor-display.jpg -------------------------------------------------------------------------------- /texture/monitor-display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/monitor-display.png -------------------------------------------------------------------------------- /texture/monitor-specular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/monitor-specular.png -------------------------------------------------------------------------------- /texture/water-shield-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreh-deprecated/WebSonic/HEAD/texture/water-shield-icon.png -------------------------------------------------------------------------------- /js/Engine.js: -------------------------------------------------------------------------------- 1 | var Engine = function(canvas) { 2 | var engine = {} 3 | engine.gfx = GraphicsEngine(engine, canvas); 4 | engine.resources = ResourceManager(engine); 5 | engine.world = WorldEngine(engine); 6 | engine.input = InputController(engine); 7 | return engine; 8 | } -------------------------------------------------------------------------------- /js/Sky.js: -------------------------------------------------------------------------------- 1 | var Sky = function(engine, camera) { 2 | var sky = new engine.world.MeshEntity(engine.resources.get("mesh/sky.jsonmesh")); 3 | sky.layer = -99; 4 | 5 | sky.scale($V([20,2,20])); 6 | sky.castsShadows = false; 7 | 8 | sky.update = function(timeDelta) { 9 | sky.position(camera.position()); 10 | } 11 | 12 | return sky; 13 | } -------------------------------------------------------------------------------- /js/Spring.js: -------------------------------------------------------------------------------- 1 | var Spring = function(engine, player) { 2 | var spring = engine.world.MeshEntity(engine.resources.get("mesh/spring.jsonmesh")); 3 | var deformation = 0.0; 4 | 5 | spring.update = function(timeDelta) { 6 | if (player.position().subtract(spring.position()).modulus() < 32 && player.position().elements[1] > spring.position().elements[1]) { 7 | var oldSpeed = player.speed(); 8 | player.speed($V([oldSpeed.elements[0], 12 * 60, oldSpeed.elements[2]])); 9 | player.state(player.STATE_SPRING); 10 | deformation = 0.5; 11 | } 12 | if (deformation > 0) { 13 | spring.frame = 1; 14 | } else { 15 | spring.frame = 0; 16 | } 17 | deformation -= timeDelta; 18 | } 19 | 20 | return spring; 21 | } -------------------------------------------------------------------------------- /js/json.addons.js: -------------------------------------------------------------------------------- 1 | JSON.forgivingParse = function(data) { 2 | var dataTokens = data.split("\""); 3 | var insideString = false; 4 | for (var i in dataTokens) { 5 | if (insideString) { 6 | // escape control characters 7 | dataTokens[i] = dataTokens[i].replace(/[\t]/g, "\\t"); 8 | dataTokens[i] = dataTokens[i].replace(/[\r]+/g, "\\r"); 9 | dataTokens[i] = dataTokens[i].replace(/[\n]+/g, "\\n"); 10 | } else { 11 | // strip comments 12 | dataTokens[i] = dataTokens[i].replace(/\*.*?\*/g, ""); 13 | dataTokens[i] = dataTokens[i].replace(/\/\/.*?(\n|\r|\r\n)/g, ""); 14 | 15 | // strip additional commas 16 | dataTokens[i] = dataTokens[i].replace(/,\s*\]/g, "]"); 17 | dataTokens[i] = dataTokens[i].replace(/,\s*\}/g, "}"); 18 | } 19 | insideString = !insideString 20 | if (!insideString && dataTokens[i][dataTokens[i].length - 1] == "\\") { 21 | insideString = true; 22 | } 23 | } 24 | return JSON.parse(dataTokens.join("\"")); 25 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSonic 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | Score0
28 | Time0:00
29 | Rings0
30 |
31 |
32 |
33 |
34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | overflow: hidden; 4 | background: #000; 5 | color: white; 6 | font-family: monospace; 7 | } 8 | 9 | #console { 10 | position: absolute; 11 | left: 0; 12 | top: 0; 13 | right: 0; 14 | bottom: 0; 15 | overflow: hidden; 16 | } 17 | 18 | #console > div { 19 | background: rgba(127,127,127, 0.1); 20 | } 21 | 22 | #console > div > div { 23 | padding: 4px; 24 | color: rgba(255,255,255,0.3); 25 | -moz-border-radius: 2px; 26 | -webkit-border-radius: 2px; 27 | border-radius: 2px; 28 | font-size: 12px; 29 | } 30 | 31 | #console .warning { 32 | color: rgba(255,200,0,1.0); 33 | background: rgba(255,255,0,0.1); 34 | text-shadow: 0px 1px 2px black; 35 | } 36 | 37 | #console .error { 38 | background: rgba(255,0,0,0.5); 39 | color: white; 40 | text-shadow: 0px 1px 2px black; 41 | } 42 | 43 | #console .info { 44 | color: white; 45 | text-shadow: 0px 1px 2px black; 46 | } 47 | 48 | canvas { 49 | -webkit-transform: scale(1.0,1.0); 50 | } 51 | 52 | #hud { 53 | position: absolute; 54 | top: 0; 55 | left: 0; 56 | font-family: Helvetica, Arial, sans-serif; 57 | font-weight: bold; 58 | padding: 0.5em; 59 | text-transform: uppercase; 60 | text-shadow: 0 1px 1px black; 61 | font-size: 30px; 62 | } 63 | 64 | #hud strong { 65 | color: #ee0; 66 | width: 4em; 67 | position: relative; 68 | display: inline-block; 69 | } 70 | 71 | #hud span { 72 | width: 4em; 73 | display: inline-block; 74 | text-align: right; 75 | } -------------------------------------------------------------------------------- /js/InputController.js: -------------------------------------------------------------------------------- 1 | var InputController = function(engine){ 2 | var input = {}; 3 | var keysHeld = {}; 4 | var keysPressed = {}; 5 | $(window).keydown(function(e){ 6 | if (!e.ctrlKey && !e.metaKey) { 7 | // For keysPressed, we need to check if the key was already held, 8 | // because chrome sends keyDown events of auto-repeated keys 9 | if (keysHeld[e.which] != true) { 10 | keysPressed[e.which] = true; 11 | } 12 | keysHeld[e.which] = true; 13 | e.preventDefault(); 14 | e.stopPropagation(); 15 | } 16 | }); 17 | $(window).keyup(function(e){ 18 | keysHeld[e.which] = false; 19 | if (!e.ctrlKey && !e.metaKey) { 20 | e.preventDefault(); 21 | e.stopPropagation(); 22 | } 23 | }); 24 | $(window).keypress(function(e){ 25 | if (!e.ctrlKey && !e.metaKey) { 26 | e.preventDefault(); 27 | e.stopPropagation(); 28 | } 29 | }); 30 | 31 | input.pressedUp = function() { return input.pressed(38); } 32 | input.pressedDown = function() { return input.pressed(40); } 33 | input.pressedLeft = function() { return input.pressed(37); } 34 | input.pressedRight = function() { return input.pressed(39); } 35 | 36 | input.pressed = function(keycode) { 37 | return keysPressed[keycode] == true; 38 | } 39 | 40 | input.held = function(keycode) { 41 | return keysHeld[keycode] == true; 42 | } 43 | 44 | input.heldUp = function() { return input.held(38); } 45 | input.heldDown = function() { return input.held(40); } 46 | input.heldLeft = function() { return input.held(37); } 47 | input.heldRight = function() { return input.held(39); } 48 | 49 | input.resetPressed = function() { 50 | keysPressed = {}; 51 | } 52 | return input; 53 | } -------------------------------------------------------------------------------- /shader/depth.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_modelView", 3 | "u_projection" ], 4 | "attributes": ["a_position"], 5 | "vertexShader": " 6 | #ifdef GL_ES 7 | precision highp float; 8 | #endif 9 | 10 | attribute vec3 a_position; 11 | attribute vec3 a_normal; 12 | attribute vec2 a_texCoords; 13 | 14 | uniform mat4 u_cameraView; 15 | uniform mat4 u_modelView; 16 | uniform mat4 u_projection; 17 | uniform mat4 u_normalCameraView; 18 | uniform mat4 u_normalModelView; 19 | 20 | uniform sampler2D u_diffuseMap; 21 | 22 | varying vec2 v_texCoords; 23 | varying vec3 v_normal; 24 | varying vec4 v_globalPosition; 25 | varying vec4 v_localPosition; 26 | 27 | void main(void) { 28 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 29 | v_localPosition = u_cameraView * v_globalPosition; 30 | gl_Position = u_projection * v_localPosition; 31 | } 32 | ", 33 | "fragmentShader": " 34 | #ifdef GL_ES 35 | precision highp float; 36 | #endif 37 | 38 | varying vec3 v_normal; 39 | varying vec4 v_globalPosition; 40 | varying vec4 v_localPosition; 41 | varying vec2 v_texCoords; 42 | 43 | uniform sampler2D u_levelShadowMap; 44 | uniform sampler2D u_diffuseMap; 45 | uniform sampler2D u_ghostMap; 46 | uniform vec3 u_levelSize; 47 | uniform vec3 u_lightDirection; 48 | uniform vec4 u_lightColor; 49 | uniform vec4 u_ambientLightColor; 50 | uniform vec3 u_playerPosition; 51 | uniform mat4 u_cameraView; 52 | uniform mat4 u_modelView; 53 | uniform mat4 u_normalCameraView; 54 | uniform float u_time; 55 | 56 | void main(void) { 57 | gl_FragColor = vec4(-v_localPosition.z/1000.0, -v_localPosition.z/1000.0, -v_localPosition.z/1000.0, 1.0); 58 | gl_FragColor = vec4(1.0 -gl_FragColor.rgb, 1.0); 59 | } 60 | " 61 | } -------------------------------------------------------------------------------- /js/Ring.js: -------------------------------------------------------------------------------- 1 | var Ring = function(engine, player, level) { 2 | var ring = new engine.world.MeshEntity(engine.resources.get("mesh/ring.jsonmesh")); 3 | var speed = $V([0,0,0]); 4 | // var speed = $V([(Math.random()-0.5)*10,Math.random()-0.5,(Math.random()-0.5)*10]); 5 | var time = 0; 6 | var grv = 0.09375 * 60.0 * 60.0 7 | var i = Math.floor(Math.random() * 5); 8 | var creationTime = engine.world.time(); 9 | 10 | ring.speed = function(newSpeed) { 11 | if (newSpeed) { 12 | speed = newSpeed; 13 | } 14 | return speed; 15 | } 16 | 17 | ring.update = function(timeDelta) { 18 | time+= timeDelta; 19 | // ring.rotation(ring.rotation().x(Matrix.RotationY(1.875 * Math.PI * timeDelta).ensure4x4())); 20 | speed = speed.add($V([0,-grv*timeDelta,0])); 21 | ring.position(ring.position().add(speed.x(timeDelta))); 22 | i++; 23 | if (i == 5) { 24 | i = 0; 25 | var intersect = level.rayIntersect(ring.position(), speed.toUnitVector(), 16, 0); 26 | if (isFinite(intersect.distance) && intersect.normal.dot(speed) <= 0) { 27 | ring.position(ring.position().subtract(speed.toUnitVector().x(16-intersect.distance))); 28 | var length = intersect.normal.dot(speed); 29 | speed = speed.subtract(intersect.normal.x(length*2)); 30 | speed.elements[1] *= 0.75; 31 | } 32 | } 33 | if (engine.world.time() > creationTime + 4.26) { 34 | engine.world.remove(ring); 35 | } 36 | 37 | } 38 | ring.isStatic = true; 39 | var superRender = ring.render; 40 | ring.render = function(renderParams) { 41 | // ring.rotation(Matrix.RotationY(1.875 * Math.PI * renderParams.uniforms[0].u_time).ensure4x4()); 42 | // if (engine.world.time() < creationTime + 3) { 43 | superRender(renderParams); 44 | // } else { 45 | // if (Math.floor(engine.world.time() * 20) % 2 == 0) 46 | // superRender(renderParams); 47 | // } 48 | } 49 | 50 | return ring; 51 | } -------------------------------------------------------------------------------- /js/console.js: -------------------------------------------------------------------------------- 1 | //if (!window.console) { 2 | window.console = {}; 3 | window.console.initialTime = (new Date()).getTime(); 4 | window.console.log = function(message){ 5 | //return; 6 | var div = document.createElement("div"); 7 | div.innerHTML = " [ " + (((new Date()).getTime() - window.console.initialTime) / 1000).toFixed(4) + "s ] " + message; 8 | $("#log")[0].appendChild(div); 9 | $("#console").attr({ scrollTop: $("#console").attr("scrollHeight") }, 3000); 10 | setTimeout(function(){ 11 | $(div).fadeOut(1000,function(){ 12 | div.parentNode.removeChild(div); 13 | }); 14 | }, 2000); 15 | } 16 | window.console.warn = function(message){ 17 | var div = document.createElement("div"); 18 | div.innerHTML = " [ " + (((new Date()).getTime() - window.console.initialTime) / 1000).toFixed(4) + "s ] Warning: " + message; 19 | div.className = "warning"; 20 | $("#log")[0].appendChild(div); 21 | $("#console").attr({ scrollTop: $("#console").attr("scrollHeight") }, 3000); 22 | setTimeout(function(){ 23 | $(div).fadeOut(1000,function(){ 24 | div.parentNode.removeChild(div); 25 | }); 26 | }, 2000); 27 | } 28 | window.console.error = function(message){ 29 | var div = document.createElement("div"); 30 | div.innerHTML = "Error: " + message; 31 | div.className = "error"; 32 | $("#log")[0].appendChild(div); 33 | $("#console").attr({ scrollTop: $("#console").attr("scrollHeight") }, 3000); 34 | } 35 | window.console.info = function(message){ 36 | var div = document.createElement("div"); 37 | div.innerHTML = message; 38 | div.className = "info"; 39 | $("#info")[0].appendChild(div); 40 | $("#console").attr({ scrollTop: $("#console").attr("scrollHeight") }, 3000); 41 | setTimeout(function(){ 42 | $(div).fadeOut(2000,function(){ 43 | div.parentNode.removeChild(div); 44 | }); 45 | }, 10000); 46 | } 47 | //} -------------------------------------------------------------------------------- /shader/sphere.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_normalCameraView", 3 | "u_modelView", 4 | "u_normalModelView", 5 | "u_projection", 6 | "u_diffuseMap", 7 | "u_lightDirection", 8 | "u_lightColor", 9 | "u_ambientLightColor", 10 | ], 11 | "attributes": ["a_position", "a_normal", "a_texCoords"], 12 | "vertexShader": " 13 | #ifdef GL_ES 14 | precision highp float; 15 | #endif 16 | 17 | attribute vec3 a_position; 18 | attribute vec3 a_normal; 19 | attribute vec2 a_texCoords; 20 | 21 | uniform mat4 u_cameraView; 22 | uniform mat4 u_modelView; 23 | uniform mat4 u_projection; 24 | uniform mat4 u_normalCameraView; 25 | uniform mat4 u_normalModelView; 26 | 27 | uniform sampler2D u_diffuseMap; 28 | 29 | varying vec2 v_texCoords; 30 | varying vec3 v_normal; 31 | varying vec4 v_globalPosition; 32 | varying vec4 v_localPosition; 33 | 34 | void main(void) { 35 | v_texCoords = a_texCoords; 36 | v_normal = (u_normalCameraView * (u_normalModelView * vec4(a_normal, 0.0))).xyz; 37 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 38 | v_localPosition = u_cameraView * v_globalPosition; 39 | gl_Position = u_projection * v_localPosition; 40 | } 41 | ", 42 | "fragmentShader": " 43 | #ifdef GL_ES 44 | precision highp float; 45 | #endif 46 | 47 | varying vec3 v_normal; 48 | varying vec4 v_globalPosition; 49 | varying vec4 v_localPosition; 50 | varying vec2 v_texCoords; 51 | 52 | uniform sampler2D u_diffuseMap; 53 | uniform vec3 u_lightDirection; 54 | uniform vec4 u_lightColor; 55 | uniform vec4 u_ambientLightColor; 56 | uniform vec3 u_playerPosition; 57 | uniform mat4 u_cameraView; 58 | uniform mat4 u_modelView; 59 | uniform mat4 u_normalCameraView; 60 | uniform float u_time; 61 | 62 | void main(void) { 63 | vec3 normal = normalize(v_normal); 64 | float product = dot(normal, (u_normalCameraView * vec4(u_lightDirection, 0.0)).xyz); 65 | gl_FragColor = u_ambientLightColor + u_lightColor * (max(0.0, product) * 3.0) / 3.0; 66 | gl_FragColor *= texture2D(u_diffuseMap, vec2( v_texCoords.x, v_texCoords.y)); 67 | float dotp = dot(reflect((u_normalCameraView * vec4(u_lightDirection,0.0)).xyz, (vec4(-normal,0.0)).xyz), (vec4(0.0, 0.0, -1.0, 0.0)).xyz); 68 | gl_FragColor += pow(dotp, 30.0) * u_lightColor; 69 | // gl_FragColor += texture2D(u_diffuseMap, (normal.xy + 1.0) / 2.0) * pow(length(normal.xy), 10.0); 70 | //gl_FragColor = vec4(v_worldPosition.y / 256.0, 1.0, 1.0, 1.0); 71 | // gl_FragColor += pow(length(normal.xy), 22.0) * vec4(1.0,1.0,1.0,1.0) * (1.0) * gl_FragColor; 72 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 73 | } 74 | " 75 | } -------------------------------------------------------------------------------- /shader/metal.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_modelView", 3 | "u_normalCameraView", 4 | "u_normalModelView", 5 | "u_projection", 6 | "u_diffuseMap", 7 | ], 8 | "attributes": ["a_position", "a_normal"], 9 | "vertexShader": " 10 | #ifdef GL_ES 11 | precision highp float; 12 | #endif 13 | 14 | attribute vec3 a_position; 15 | attribute vec3 a_normal; 16 | attribute vec2 a_texCoords; 17 | 18 | uniform mat4 u_cameraView; 19 | uniform mat4 u_modelView; 20 | uniform mat4 u_projection; 21 | uniform mat4 u_normalCameraView; 22 | uniform mat4 u_normalModelView; 23 | 24 | uniform sampler2D u_diffuseMap; 25 | 26 | varying vec2 v_texCoords; 27 | varying vec3 v_normal; 28 | varying vec4 v_globalPosition; 29 | varying vec4 v_localPosition; 30 | 31 | void main(void) { 32 | v_texCoords = a_texCoords; 33 | v_normal = (u_normalCameraView * (u_normalModelView * vec4(a_normal, 0.0))).xyz; 34 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 35 | v_localPosition = u_cameraView * v_globalPosition; 36 | gl_Position = u_projection * v_localPosition; 37 | } 38 | ", 39 | "fragmentShader": " 40 | #ifdef GL_ES 41 | precision highp float; 42 | #endif 43 | 44 | varying vec3 v_normal; 45 | varying vec4 v_globalPosition; 46 | varying vec4 v_localPosition; 47 | varying vec2 v_texCoords; 48 | 49 | uniform sampler2D u_diffuseMap; 50 | uniform vec3 u_lightDirection; 51 | uniform vec4 u_lightColor; 52 | uniform vec4 u_ambientLightColor; 53 | uniform vec3 u_playerPosition; 54 | uniform mat4 u_cameraView; 55 | uniform mat4 u_modelView; 56 | uniform mat4 u_normalCameraView; 57 | uniform float u_time; 58 | 59 | void main(void) { 60 | vec3 normal = normalize(v_normal); 61 | // float product = dot(normal, (u_normalCameraView * vec4(u_lightDirection, 0.0)).xyz); 62 | // gl_FragColor = u_ambientLightColor + u_lightColor * (max(0.0, product) * 3.0) / 3.0; 63 | if (normal.z > 0.0) { 64 | gl_FragColor = texture2D(u_diffuseMap, vec2(normal.x,-normal.y)/2.0 + (1.0/2.0)); 65 | } else { 66 | vec2 nn = normalize(normal.xy); 67 | gl_FragColor = texture2D(u_diffuseMap, vec2(nn.x,-nn.y)/2.0 + (1.0/2.0)); 68 | } 69 | 70 | // gl_FragColor = vec4(1.0,1.0,1.0,1.0); 71 | // float dotp = dot(reflect((u_normalCameraView * vec4(u_lightDirection,0.0)).xyz, (vec4(-normal,0.0)).xyz), (vec4(0.0, 0.0, -1.0, 0.0)).xyz); 72 | // gl_FragColor += pow(dotp, 30.0) * u_lightColor; 73 | // gl_FragColor += texture2D(u_diffuseMap, (normal.xy + 1.0) / 2.0) * pow(length(normal.xy), 10.0); 74 | // gl_FragColor = vec4(v_worldPosition.y / 256.0, 1.0, 1.0, 1.0); 75 | // gl_FragColor += pow(length(normal.xy), 22.0) * vec4(1.0,1.0,1.0,1.0) * (1.0) * gl_FragColor; 76 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 77 | } 78 | " 79 | } -------------------------------------------------------------------------------- /shader/sonic.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_normalCameraView", 3 | "u_modelView", 4 | "u_normalModelView", 5 | "u_projection", 6 | "u_diffuseMap", 7 | "u_lightDirection", 8 | "u_lightColor", 9 | "u_ambientLightColor", 10 | "u_levelSize", 11 | "u_levelShadowMap" 12 | ], 13 | "attributes": ["a_position", "a_normal", "a_texCoords"], 14 | "vertexShader": " 15 | #ifdef GL_ES 16 | precision highp float; 17 | #endif 18 | 19 | attribute vec3 a_position; 20 | attribute vec3 a_normal; 21 | attribute vec2 a_texCoords; 22 | 23 | uniform mat4 u_cameraView; 24 | uniform mat4 u_modelView; 25 | uniform mat4 u_projection; 26 | uniform mat4 u_normalCameraView; 27 | uniform mat4 u_normalModelView; 28 | 29 | uniform sampler2D u_diffuseMap; 30 | 31 | varying vec2 v_texCoords; 32 | varying vec3 v_normal; 33 | varying vec4 v_globalPosition; 34 | varying vec4 v_localPosition; 35 | 36 | void main(void) { 37 | v_texCoords = a_texCoords; 38 | v_normal = (u_normalCameraView * (u_normalModelView * vec4(a_normal, 0.0))).xyz; 39 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 40 | v_localPosition = u_cameraView * v_globalPosition; 41 | gl_Position = u_projection * v_localPosition; 42 | } 43 | ", 44 | "fragmentShader": " 45 | #ifdef GL_ES 46 | precision highp float; 47 | #endif 48 | 49 | varying vec3 v_normal; 50 | varying vec4 v_globalPosition; 51 | varying vec4 v_localPosition; 52 | varying vec2 v_texCoords; 53 | 54 | uniform sampler2D u_diffuseMap; 55 | uniform sampler2D u_levelShadowMap; 56 | uniform vec3 u_levelSize; 57 | uniform vec3 u_lightDirection; 58 | uniform vec4 u_lightColor; 59 | uniform vec4 u_ambientLightColor; 60 | uniform vec3 u_playerPosition; 61 | uniform mat4 u_cameraView; 62 | uniform mat4 u_modelView; 63 | uniform mat4 u_normalCameraView; 64 | uniform float u_time; 65 | 66 | void main(void) { 67 | vec3 normal = normalize(v_normal); 68 | float product = dot(normal, (u_normalCameraView * vec4(u_lightDirection, 0.0)).xyz); 69 | float shadowSourceHeight = texture2D(u_levelShadowMap, vec2((v_globalPosition.x)/ u_levelSize.x, (v_globalPosition.z) / u_levelSize.z)).r; 70 | float shadowCoefficient = max(1.0/10.0,min(1.0,(v_globalPosition.y - shadowSourceHeight * u_levelSize.y) / 20.0 + 1.0)); 71 | product *= shadowCoefficient; 72 | gl_FragColor = u_ambientLightColor + u_lightColor * (max(0.0, product) * 3.0) / 3.0; 73 | gl_FragColor *= texture2D(u_diffuseMap, vec2( v_texCoords.x, v_texCoords.y)); 74 | float dotp = dot(reflect((u_normalCameraView * vec4(u_lightDirection,0.0)).xyz, (vec4(-normal,0.0)).xyz), (vec4(0.0, 0.0, -1.0, 0.0)).xyz); 75 | gl_FragColor += pow(max(dotp, 0.0), 30.0) * u_lightColor / 5.0 * shadowCoefficient; 76 | // gl_FragColor += texture2D(u_diffuseMap, (normal.xy + 1.0) / 2.0) * pow(length(normal.xy), 10.0); 77 | //gl_FragColor = vec4(v_worldPosition.y / 256.0, 1.0, 1.0, 1.0); 78 | gl_FragColor += pow(length(normal.xy), 10.0) * u_lightColor * (1.0 - product) * gl_FragColor; 79 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 80 | } 81 | " 82 | } -------------------------------------------------------------------------------- /shader/sky.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_modelView", 3 | "u_normalModelView", 4 | "u_projection", 5 | "u_cloudMap", 6 | "u_time" 7 | ], 8 | "attributes": ["a_position", "a_normal", "a_texCoords"], 9 | "vertexShader": " 10 | #ifdef GL_ES 11 | precision highp float; 12 | #endif 13 | 14 | attribute vec3 a_position; 15 | attribute vec3 a_normal; 16 | attribute vec2 a_texCoords; 17 | 18 | uniform mat4 u_cameraView; 19 | uniform mat4 u_modelView; 20 | uniform mat4 u_projection; 21 | uniform mat4 u_normalCameraView; 22 | uniform mat4 u_normalModelView; 23 | 24 | uniform sampler2D u_diffuseMap; 25 | 26 | varying vec2 v_texCoords; 27 | varying vec3 v_normal; 28 | varying vec4 v_globalPosition; 29 | varying vec4 v_localPosition; 30 | varying vec4 v_originalPosition; 31 | 32 | void main(void) { 33 | v_texCoords = a_texCoords; 34 | v_normal = (u_normalCameraView * (u_normalModelView * vec4(a_normal, 0.0))).xyz; 35 | v_originalPosition = vec4(a_position, 1.0); 36 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 37 | v_localPosition = u_cameraView * v_globalPosition; 38 | gl_Position = u_projection * v_localPosition; 39 | } 40 | ", 41 | "fragmentShader": " 42 | #ifdef GL_ES 43 | precision highp float; 44 | #endif 45 | 46 | varying vec3 v_normal; 47 | varying vec4 v_globalPosition; 48 | varying vec4 v_localPosition; 49 | varying vec2 v_texCoords; 50 | varying vec4 v_originalPosition; 51 | 52 | uniform sampler2D u_cloudMap; 53 | uniform mat4 u_cameraView; 54 | uniform mat4 u_modelView; 55 | uniform mat4 u_normalCameraView; 56 | uniform float u_time; 57 | 58 | void main(void) { 59 | vec3 normal = normalize(v_normal); 60 | float t_time = u_time * 3.0; 61 | gl_FragColor = texture2D(u_cloudMap, v_originalPosition.xz / 10.0 + vec2(t_time/100.0, t_time/10.0) + vec2(cos(v_originalPosition.x/5.0+v_originalPosition.z/5.0+t_time/50.0),sin(v_originalPosition.x/5.0+v_originalPosition.z/5.0+t_time/100.0))/8.0); 62 | gl_FragColor *= texture2D(u_cloudMap, v_originalPosition.xz / 15.0 + vec2(t_time/20.0, t_time/40.0) + vec2(cos(v_originalPosition.x/2.0+v_originalPosition.z/3.0+t_time/20.0+1.0),sin(v_originalPosition.x/3.0+v_originalPosition.z/2.0+t_time/50.0+1.0))/12.0); 63 | gl_FragColor *= texture2D(u_cloudMap, v_originalPosition.xz / 20.0 + vec2(t_time/80.0, t_time/70.0) + vec2(cos(v_originalPosition.x/8.0+v_originalPosition.z/4.0+t_time/100.0+2.0),sin(v_originalPosition.x/4.0+v_originalPosition.z/8.0+t_time/20.0+2.0))/20.0); 64 | gl_FragColor *= texture2D(u_cloudMap, v_originalPosition.xz / 50.0 + vec2(t_time/40.0, t_time/20.0)); 65 | gl_FragColor *= abs(v_originalPosition.y/10.0); 66 | gl_FragColor = min(max(gl_FragColor, 1.0/5.0), 3.0/4.0) - 1.0/5.0; 67 | gl_FragColor += 1.0/abs(v_originalPosition.y/5.0); 68 | gl_FragColor *= 10.0/13.0; 69 | gl_FragColor += vec4(1.0/5.0,1.0/3.0,4.0/5.0,0.0); 70 | // gl_FragColor += vec4(3.0/5.0,1.0/3.0,1.0/10.0,0.0); 71 | gl_FragColor += vec4(3.0/50.0,1.0/30.0,1.0/10.0,0.0) * 2.0; 72 | gl_FragColor = min(gl_FragColor, 1.0); 73 | // if (v_originalPosition.y < 0.0) { 74 | // gl_FragColor *= vec4(10.0,10.0,10.0,1.0) / abs(v_originalPosition.y); 75 | // } 76 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 77 | } 78 | " 79 | } -------------------------------------------------------------------------------- /shader/monitor.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_normalCameraView", 3 | "u_modelView", 4 | "u_normalModelView", 5 | "u_projection", 6 | "u_diffuseMap", 7 | "u_displayMap", 8 | "u_displayImageMap", 9 | "u_specularMap", 10 | "u_lightDirection", 11 | "u_lightColor", 12 | "u_ambientLightColor", 13 | "u_time", 14 | "u_levelShadowMap", 15 | "u_levelSize" 16 | ], 17 | "attributes": ["a_position", "a_normal", "a_texCoords"], 18 | "vertexShader": " 19 | #ifdef GL_ES 20 | precision highp float; 21 | #endif 22 | 23 | attribute vec3 a_position; 24 | attribute vec3 a_normal; 25 | attribute vec2 a_texCoords; 26 | 27 | uniform mat4 u_cameraView; 28 | uniform mat4 u_modelView; 29 | uniform mat4 u_projection; 30 | uniform mat4 u_normalCameraView; 31 | uniform mat4 u_normalModelView; 32 | 33 | uniform sampler2D u_diffuseMap; 34 | 35 | varying vec2 v_texCoords; 36 | varying vec3 v_normal; 37 | varying vec4 v_globalPosition; 38 | varying vec4 v_localPosition; 39 | 40 | void main(void) { 41 | v_texCoords = a_texCoords; 42 | v_normal = (u_normalCameraView * (u_normalModelView * vec4(a_normal, 0.0))).xyz; 43 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 44 | v_localPosition = u_cameraView * v_globalPosition; 45 | gl_Position = u_projection * v_localPosition; 46 | } 47 | ", 48 | "fragmentShader": " 49 | #ifdef GL_ES 50 | precision highp float; 51 | #endif 52 | 53 | varying vec3 v_normal; 54 | varying vec4 v_globalPosition; 55 | varying vec4 v_localPosition; 56 | varying vec2 v_texCoords; 57 | 58 | uniform sampler2D u_diffuseMap; 59 | uniform sampler2D u_specularMap; 60 | uniform sampler2D u_displayMap; 61 | uniform sampler2D u_displayImageMap; 62 | uniform sampler2D u_levelShadowMap; 63 | uniform vec3 u_levelSize; 64 | uniform vec3 u_lightDirection; 65 | uniform vec4 u_lightColor; 66 | uniform vec4 u_ambientLightColor; 67 | uniform vec3 u_playerPosition; 68 | uniform mat4 u_cameraView; 69 | uniform mat4 u_modelView; 70 | uniform mat4 u_normalCameraView; 71 | uniform float u_time; 72 | 73 | void main(void) { 74 | vec3 normal = normalize(v_normal); 75 | float product = dot(normal, (u_normalCameraView * vec4(u_lightDirection, 0.0)).xyz); 76 | float shadowSourceHeight = texture2D(u_levelShadowMap, vec2((v_globalPosition.x)/ u_levelSize.x, (v_globalPosition.z) / u_levelSize.z)).r; 77 | float shadowCoefficient = max(0.0,min(1.0,(v_globalPosition.y - shadowSourceHeight * u_levelSize.y) / 20.0 + 1.0)); 78 | product *= shadowCoefficient; 79 | gl_FragColor = u_ambientLightColor + u_lightColor * max(0.0, product); 80 | gl_FragColor *= texture2D(u_diffuseMap, v_texCoords); 81 | // gl_FragColor = vec4(0.0,0.0,0.0,1.0); 82 | float dotp = dot(reflect((u_normalCameraView * vec4(u_lightDirection,0.0)).xyz, (vec4(-normal,0.0)).xyz), (vec4(0.0, 0.0, -1.0, 0.0)).xyz); 83 | gl_FragColor += pow(max(0.0, dotp), 100.0 * texture2D(u_specularMap, v_texCoords).r) * texture2D(u_specularMap, v_texCoords) * 10.0 * shadowCoefficient; 84 | vec4 imgPosition = texture2D(u_displayMap, v_texCoords); 85 | float interf = (cos(imgPosition.g + u_time * 2.0) - 29.0/30.0) * 30.0; 86 | vec4 imgColor = imgPosition.b * texture2D(u_displayImageMap, imgPosition.rg + vec2((1.0 - max(0.0,interf)) + sin(u_time / 2.0) * cos(u_time * 12.0 + imgPosition.g * 20.0) / 40.0 + sin(u_time / 2.0) / 32.0, sin(u_time / 2.0) / 16.0)); 87 | if (interf > 0.0) { 88 | float gray = (imgColor.r + imgColor.g + imgColor.b) / 3.0; 89 | imgColor = vec4(gray,gray,gray, 1.0) * (2.0 - interf); 90 | } 91 | gl_FragColor += imgColor; 92 | // gl_FragColor += texture2D(u_diffuseMap, (normal.xy + 1.0) / 2.0) * pow(length(normal.xy), 10.0); 93 | //gl_FragColor = vec4(v_worldPosition.y / 256.0, 1.0, 1.0, 1.0); 94 | // gl_FragColor += pow(length(normal.xy), 22.0) * vec4(1.0,1.0,1.0,1.0) * (1.0) * gl_FragColor; 95 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 96 | } 97 | " 98 | } -------------------------------------------------------------------------------- /shader/glow.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_image" ], 2 | "attributes": ["a_position", "a_texCoords" ], 3 | "vertexShader": " 4 | #ifdef GL_ES 5 | precision highp float; 6 | #endif 7 | 8 | uniform sampler2D u_image; 9 | 10 | attribute vec3 a_position; 11 | attribute vec2 a_texCoords; 12 | 13 | varying vec2 v_texCoords; 14 | 15 | void main(void) { 16 | gl_Position = vec4(a_position, 1.0); 17 | v_texCoords = a_texCoords; 18 | } 19 | ", 20 | "fragmentShader": " 21 | #ifdef GL_ES 22 | precision highp float; 23 | #endif 24 | 25 | uniform sampler2D u_image; 26 | 27 | varying vec2 v_texCoords; 28 | 29 | void main(void) { 30 | const float frac = 600.0; 31 | const float frac2 = 450.0; 32 | // float x = 0.5; 33 | float i = 0.0; 34 | vec4 sumGlow = vec4(0.0,0.0,0.0,0.0); 35 | vec4 maxGlow = vec4(0.0,0.0,0.0,0.0); 36 | for(float x = 0.5; x < 10.0; x+=3.0/10.0) { 37 | /* vec4 thisGlow = (texture2D(u_image, v_texCoords + vec2(x/frac,x/frac)) + 38 | texture2D(u_image, v_texCoords + vec2(-x/frac,x/frac)) + 39 | texture2D(u_image, v_texCoords + vec2(x/frac,-x/frac)) + 40 | texture2D(u_image, v_texCoords + vec2(-x/frac,-x/frac)) + 41 | texture2D(u_image, v_texCoords + vec2(0.0,-x/frac2)) + 42 | texture2D(u_image, v_texCoords + vec2(-x/frac2,0.0)) + 43 | texture2D(u_image, v_texCoords + vec2(0.0, x/frac2)) + 44 | texture2D(u_image, v_texCoords + vec2(x/frac2, 0.0))) * (10.0-x);// * (1.0 + vec4(cos(x), sin(x), cos(x+2.0), 1.0)/5.0); */ 45 | vec4 thisGlow = vec4(0.0,0.0,0.0,0.0); 46 | vec4 c; 47 | float len; 48 | c = texture2D(u_image, v_texCoords + vec2(x/frac,x/frac)); 49 | len = length(c.xyz); 50 | if (length(c.xyz) > 1.0) { 51 | thisGlow += c - c/len; 52 | } 53 | c = texture2D(u_image, v_texCoords + vec2(-x/frac,x/frac)); 54 | len = length(c.xyz); 55 | if (length(c.xyz) > 1.0) { 56 | thisGlow += c - c/len; 57 | } 58 | c = texture2D(u_image, v_texCoords + vec2(x/frac,-x/frac)); 59 | len = length(c.xyz); 60 | if (length(c.xyz) > 1.0) { 61 | thisGlow += c - c/len; 62 | } 63 | c = texture2D(u_image, v_texCoords + vec2(-x/frac,-x/frac)); 64 | len = length(c.xyz); 65 | if (length(c.xyz) > 1.0) { 66 | thisGlow += c - c/len; 67 | } 68 | /* c = texture2D(u_image, v_texCoords + vec2(x/frac2,0.0)); 69 | if (length(c.xyz) > 1.0) { 70 | thisGlow += c - normalize(c); 71 | } 72 | c = texture2D(u_image, v_texCoords + vec2(-x/frac2,0.0)); 73 | if (length(c.xyz) > 1.0) { 74 | thisGlow += c - normalize(c); 75 | } 76 | c = texture2D(u_image, v_texCoords + vec2(0.0,x/frac2)); 77 | if (length(c.xyz) > 1.0) { 78 | thisGlow += c - normalize(c); 79 | } 80 | c = texture2D(u_image, v_texCoords + vec2(0.0,-x/frac2)); 81 | if (length(c.xyz) > 1.0) { 82 | thisGlow += c - normalize(c); 83 | }*/ 84 | thisGlow *= (10.0-x); 85 | maxGlow = max(maxGlow, thisGlow); 86 | sumGlow += thisGlow; 87 | // x += 3.0/1.0; 88 | i += (10.0-x) * 3.0/1.0;//* (1.0 + vec4(cos(x), sin(x), cos(x+2.0), 1.0)/5.0); 89 | } 90 | sumGlow /= 2.0 * i; 91 | maxGlow /= 2.0 * (i / 4.0); 92 | 93 | float len = length(maxGlow.rgb); 94 | // float lengthPow = min(len, 10.0/10.0) / 5.0; 95 | // glow = glow * pow(lengthPow, 2.0); 96 | vec4 glow; 97 | if (length(sumGlow.rgb) > 0.0) { 98 | glow = vec4(normalize(sumGlow.rgb), 1.0); 99 | } else { 100 | glow = sumGlow; 101 | } 102 | // len = pow(max(len, 0.0), 1.0); 103 | glow = glow * len; 104 | 105 | /* vec4 color = vec4(texture2D(u_image, v_texCoords * vec2(101.0/100.0, 100.0/100.0) + vec2(0.0,0.0)).r, 106 | texture2D(u_image, v_texCoords * vec2(100.0/100.0, 101.0/100.0) + vec2(0.0,0.0)).g, 107 | texture2D(u_image, v_texCoords * vec2(99.0/100.0, 100.0/100.0) + vec2(0.0,0.0)).b, 108 | 1.0);*/ 109 | vec4 color = texture2D(u_image, v_texCoords + vec2(0.0,0.0)); 110 | 111 | gl_FragColor = color + glow; 112 | 113 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 114 | } 115 | " 116 | } -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | window.onload = function(){ 2 | var canvas = $("#c")[0]; 3 | var engine = Engine(canvas); 4 | 5 | /* var pitch = 0.0; 6 | var yaw = 0.0; 7 | var holding = false; 8 | var oldX, oldY, oldPitch, oldYaw; 9 | var alpha = 0.0; 10 | window.addEventListener("mouseup", function(e){ 11 | holding = false; 12 | }, false); 13 | window.addEventListener("mousedown", function(e){ 14 | oldX = e.clientX; 15 | oldY = e.clientY; 16 | oldYaw = yaw; 17 | oldPitch = pitch; 18 | holding = true; 19 | e.preventDefault(); 20 | e.stopPropagation(); 21 | }, false); 22 | window.addEventListener("mousemove", function(e){ 23 | if (holding) { 24 | pitch = oldPitch + (e.clientY - oldY) * 0.01; 25 | yaw = oldYaw + (e.clientX - oldX) * 0.01; 26 | } 27 | }, false);*/ 28 | engine.resources.load("mesh/sky.jsonmesh"); 29 | engine.resources.load("mesh/monitor.jsonmesh"); 30 | engine.resources.load("mesh/spinball.jsonmesh") 31 | engine.resources.load("mesh/sonic.jsonmesh"); 32 | engine.resources.load("mesh/spring.jsonmesh"); 33 | engine.resources.load("mesh/ring.jsonmesh"); 34 | engine.resources.load("level/lvl0.jsonlevel"); 35 | engine.resources.load("shader/depth.jsonshader"); 36 | engine.resources.load("shader/glow.jsonshader"); 37 | var interval = setInterval( 38 | function () { 39 | // try { 40 | if (engine.resources.totalPending() == 0) { 41 | var camera = engine.world.Camera(); 42 | camera.rotation(Matrix.RotationX(-1/4*Math.PI).ensure4x4()); 43 | engine.world.add(camera); 44 | camera.position($V([128,512,512])); 45 | var level = engine.world.LevelEntity(engine.resources.get("level/lvl0.jsonlevel")); 46 | engine.world.add(level); 47 | var sky = new Sky(engine, camera); 48 | engine.world.add(sky); 49 | /* for (var c = 0; c<32; c++) { 50 | var ring = Ring(engine, player, level); 51 | ring.position($V([1536/2, 64, 64])); 52 | // mesh.scale($V([0.3,0.3,0.3])); 53 | engine.world.add(ring); 54 | }*/ 55 | 56 | 57 | 58 | 59 | var player = Player(engine, camera, level); 60 | player.position($V([1536/2,128/2,128/2])); 61 | engine.world.add(player); 62 | engine.world.add(new Spring(engine, player)); 63 | 64 | clearInterval(interval); 65 | var alpha = 0.0; 66 | var updateFunc; 67 | interval = requestAnimationFrame( updateFunc = function () { 68 | alpha += 0.1; 69 | if ($(window).width() != canvas.width || $(window).height() != canvas.height) { 70 | canvas.width = $(window).width(); 71 | canvas.height = $(window).height(); 72 | engine.gfx.gl.viewport(0,0,canvas.width, canvas.height); 73 | } 74 | 75 | /* //alpha+= 0.1; 76 | // for (var c = 0; c < 10; c++) { 77 | //engine.resources.get("mesh/lvl0.jsonmesh").render(Matrix.Perspective(45, canvas.width/canvas.height, 1, 1000), Matrix.Translation($V([0,0,-50])).x(Matrix.RotationY(yaw+alpha *0.1).ensure4x4()).x(Matrix.RotationX(pitch).ensure4x4()).x(Matrix.Translation($V([0,-10,0]))).x($V([256.0,256.0,256.0,1.0]).toDiagonalMatrix()), 0, [{ u_lightColor: $V([1.0,1.0,1.0,1.0]), u_lightDirection: $V([Math.sin(alpha* 0.01), Math.cos(alpha * 0.01), 0]) }, {u_ambientLightColor: $V([0.0,0.2,0.1,1.0])}]); 78 | engine.resources.get("level/lvl0.jsonlevel").render(Matrix.Perspective(45, canvas.width/canvas.height, 1, 10000).x(Matrix.RotationX(pitch).ensure4x4()).x(Matrix.RotationY(yaw).ensure4x4()).x(Matrix.Translation($V([-128,-128,-128]))), [{ u_lightColor: $V([1.0,1.0,1.0,1.0]), u_lightDirection: $V([Math.sin(alpha* 0.1), Math.cos(alpha * 0.1), 0]) }, {u_ambientLightColor: $V([0.0,0.2,0.1,1.0])}]); 79 | // }*/ 80 | //camera.position($V([0, 0, -10])); 81 | 82 | try { 83 | alpha += 0.001; 84 | //camera.rotation(Matrix.RotationX(0.0).ensure4x4().x(Matrix.RotationY(0.1).ensure4x4()).x(camera.rotation())) 85 | engine.world.update(); 86 | engine.world.render(); 87 | var seconds = engine.world.time(); 88 | $("#time")[0].innerHTML = Math.floor(seconds / 60) + ":" + ((Math.floor(seconds) % 60 < 10)?"0":"") + Math.floor(seconds) % 60; 89 | requestAnimationFrame(updateFunc); 90 | } catch(e) { 91 | console.error(e.message + e.stack); 92 | //clearInterval(interval); 93 | } 94 | }); 95 | } 96 | /* } catch (e) { 97 | console.error(e.message); 98 | clearInterval(interval); 99 | }*/ 100 | }, 1000/60.0); 101 | } -------------------------------------------------------------------------------- /js/ResourceManager.js: -------------------------------------------------------------------------------- 1 | var ResourceManager = function(engine){ 2 | var resourceManager = {}; 3 | var loadedResources = {}; 4 | var totalPending = 0; 5 | 6 | resourceManager.load = function(path) { 7 | if (loadedResources[path] !== undefined) { 8 | // resource already loaded or loading 9 | return; 10 | } 11 | 12 | console.log("Requesting remote resource file: " + path); 13 | 14 | var extension = path.split(".")[1]; 15 | switch(extension) { 16 | case "bmp": // I don't think we'll be using BMP, but who knows? 17 | case "gif": 18 | case "png": 19 | case "jpeg": 20 | case "jpg": 21 | var image = document.createElement("img"); 22 | image.onload = function() { 23 | var texture = engine.gfx.gl.createTexture(); 24 | engine.gfx.gl.bindTexture( engine.gfx.gl.TEXTURE_2D, texture); 25 | engine.gfx.gl.texImage2D( engine.gfx.gl.TEXTURE_2D, 0, engine.gfx.gl.RGBA, engine.gfx.gl.RGBA, engine.gfx.gl.UNSIGNED_BYTE, image); 26 | engine.gfx.gl.texParameteri( engine.gfx.gl.TEXTURE_2D, engine.gfx.gl.TEXTURE_MAG_FILTER, engine.gfx.gl.LINEAR); 27 | engine.gfx.gl.texParameteri( engine.gfx.gl.TEXTURE_2D, engine.gfx.gl.TEXTURE_MIN_FILTER, engine.gfx.gl.LINEAR); 28 | engine.gfx.gl.generateMipmap(engine.gfx.gl.TEXTURE_2D); 29 | // if (Math.floor((Math.log(image.width) / Math.log( 2 ))) != (Math.log(image.width) / Math.log( 2 ))) { 30 | // console.warn("Texture " + path + " is not square and/or doesn't have power of two dimensions."); 31 | // } 32 | loadedResources[path] = texture; 33 | totalPending--; 34 | console.log("Resource loaded: " + path); 35 | }; 36 | totalPending++; 37 | image.src = path; 38 | break; 39 | case "jsonmesh": 40 | totalPending++; 41 | $.ajax({ url: path, dataType: "text", success: function(data) { 42 | console.log("Resource loaded: " + path); 43 | try { 44 | data = JSON.parse(data); 45 | } catch (e) { 46 | try { 47 | data = JSON.forgivingParse(data); 48 | console.warn("Falling back to non-standard JSON parsing. (Have you escaped all control characters and removed all comments?)"); 49 | } catch (e) { 50 | console.error("Malformed JSON."); 51 | throw e; 52 | } 53 | } 54 | try { 55 | var mesh = new engine.gfx.Mesh(data); 56 | loadedResources[path] = mesh; 57 | totalPending--; 58 | } catch (e) { 59 | console.error(e.message + e.stack) 60 | throw e; 61 | } 62 | } }); 63 | break; 64 | case "jsonshader": 65 | totalPending++; 66 | $.ajax({ url: path, dataType: "text", success: function(data) { 67 | console.log("Resource loaded: " + path); 68 | try { 69 | data = JSON.parse(data); 70 | } catch (e) { 71 | try { 72 | data = JSON.forgivingParse(data); 73 | console.warn("Falling back to non-standard JSON parsing. (Have you escaped all control characters and removed all comments?)"); 74 | } catch (e) { 75 | console.error("Malformed JSON."); 76 | throw e; 77 | } 78 | } 79 | try { 80 | var shader = new engine.gfx.Shader(data, path); 81 | loadedResources[path] = shader; 82 | totalPending--; 83 | } catch (e) { 84 | console.error(e.message + e.stack) 85 | throw e; 86 | } 87 | } }); 88 | break; 89 | case "jsonlevel": 90 | totalPending++; 91 | $.ajax({ url: path, dataType: "text", success: function(data) { 92 | console.log("Resource loaded: " + path); 93 | try { 94 | data = JSON.parse(data); 95 | } catch (e) { 96 | try { 97 | data = JSON.forgivingParse(data); 98 | console.warn("Falling back to non-standard JSON parsing. (Have you escaped all control characters and comments?)"); 99 | } catch (e) { 100 | console.error("Malformed JSON."); 101 | throw e; 102 | } 103 | } 104 | try { 105 | var level = new engine.gfx.Level(data, path); 106 | loadedResources[path] = level; 107 | totalPending--; 108 | } catch (e) { 109 | console.error(e.message + e.stack) 110 | throw e; 111 | } 112 | } }); 113 | break; 114 | } 115 | loadedResources[path] = null; 116 | } 117 | 118 | resourceManager.hasLoaded = function(path) { 119 | if (loadedResources[path]) { 120 | return true; 121 | } 122 | return false; 123 | } 124 | 125 | resourceManager.get = function(path) { 126 | var resource = loadedResources[path]; 127 | if (resource === undefined) { 128 | throw new Error("Tried to use a resource that hasn't been requested: " + path); 129 | } else if (resource === null) { 130 | throw new Error("Tried to use a resource that hasn't finished loading: " + path); 131 | } 132 | return resource; 133 | } 134 | 135 | resourceManager.totalPending = function() { 136 | return totalPending; 137 | } 138 | 139 | return resourceManager; 140 | } -------------------------------------------------------------------------------- /shader/default.jsonshader: -------------------------------------------------------------------------------- 1 | {"uniforms": [ "u_cameraView", 2 | "u_normalCameraView", 3 | "u_modelView", 4 | "u_normalModelView", 5 | "u_projection", 6 | "u_diffuseMap", 7 | "u_ghostMap", 8 | "u_lightDirection", 9 | "u_lightColor", 10 | "u_ambientLightColor", 11 | "u_levelShadowMap", 12 | "u_time", 13 | "u_levelSize", 14 | "u_dynamicShadowMap", 15 | "u_dynamicShadowMapPosition"], 16 | "attributes": ["a_position", "a_normal", "a_texCoords"], 17 | "vertexShader": " 18 | #ifdef GL_ES 19 | precision highp float; 20 | #endif 21 | 22 | attribute vec3 a_position; 23 | attribute vec3 a_normal; 24 | attribute vec2 a_texCoords; 25 | 26 | uniform mat4 u_cameraView; 27 | uniform mat4 u_modelView; 28 | uniform mat4 u_projection; 29 | uniform mat4 u_normalCameraView; 30 | uniform mat4 u_normalModelView; 31 | 32 | uniform sampler2D u_diffuseMap; 33 | 34 | varying vec2 v_texCoords; 35 | varying vec3 v_normal; 36 | varying vec4 v_globalPosition; 37 | varying vec4 v_localPosition; 38 | 39 | void main(void) { 40 | v_texCoords = a_texCoords; 41 | v_normal = (u_normalCameraView * (u_normalModelView * vec4(a_normal, 0.0))).xyz; 42 | v_globalPosition = u_modelView * vec4(a_position, 1.0); 43 | v_localPosition = u_cameraView * v_globalPosition; 44 | gl_Position = u_projection * v_localPosition; 45 | // float distortion = (pow(v_localPosition.x, 2.0) + pow(v_localPosition.y, 2.0))/ 2000.0; 46 | // gl_Position += vec4(-(distortion) * normalize(gl_Position.xy),sqrt(distortion),0.0); 47 | } 48 | ", 49 | "fragmentShader": " 50 | #ifdef GL_ES 51 | precision highp float; 52 | #endif 53 | 54 | varying vec3 v_normal; 55 | varying vec4 v_globalPosition; 56 | varying vec4 v_localPosition; 57 | varying vec2 v_texCoords; 58 | 59 | uniform sampler2D u_levelShadowMap; 60 | uniform sampler2D u_diffuseMap; 61 | uniform sampler2D u_ghostMap; 62 | uniform sampler2D u_dynamicShadowMap; 63 | uniform vec3 u_dynamicShadowMapPosition; 64 | uniform vec3 u_levelSize; 65 | uniform vec3 u_lightDirection; 66 | uniform vec4 u_lightColor; 67 | uniform vec4 u_ambientLightColor; 68 | uniform vec3 u_playerPosition; 69 | uniform mat4 u_cameraView; 70 | uniform mat4 u_modelView; 71 | uniform mat4 u_normalCameraView; 72 | uniform float u_time; 73 | 74 | void main(void) { 75 | float z = - pow(v_localPosition.x * v_localPosition.x * 5.0 / 10.0 + v_localPosition.y * v_localPosition.y * 4.0, 1.0/2.0) * 6.0 / 10.0 + v_localPosition.z * 1.0 + v_localPosition.y * 12.0/10.0 + cos(v_localPosition.y/6.0 + u_time * 6.0) * 2.0 + sin(v_localPosition.x/6.0 + u_time * 6.0) * 2.0; 76 | if (z > -280.0) { 77 | if (z > -270.0) { 78 | vec4 textureColor = texture2D(u_ghostMap, v_texCoords);//*/vec2(v_texCoords.x + sin(v_localPosition.x*12.0 + u_time * 3.0) / 100.0, v_texCoords.y + cos(v_localPosition.y*12.0 + u_time * 3.0) / 100.0)); 79 | if (textureColor.a > 20.0/100.0) { 80 | gl_FragColor = vec4(textureColor.a,textureColor.a, textureColor.a,1.0); 81 | } else { 82 | discard; 83 | } 84 | } 85 | else { 86 | if (z < -278.0) { //mod(z, 7.0) < 6.0) { 87 | gl_FragColor = vec4(1.0,1.0,1.0,1.0); 88 | } else { 89 | discard; 90 | } 91 | } 92 | } else { 93 | vec3 normal = normalize(v_normal); 94 | float product = dot(normal, (u_normalCameraView * vec4(u_lightDirection, 0.0)).xyz); 95 | float shadowSourceHeight = texture2D(u_levelShadowMap, vec2((v_globalPosition.x)/ u_levelSize.x, (v_globalPosition.z) / u_levelSize.z)).r; 96 | float dynamicShadowHeight = texture2D(u_dynamicShadowMap, vec2(0.0, 1.0) + vec2(1.0,-1.0) * (v_globalPosition.xz - u_dynamicShadowMapPosition.xz + 170.0)/350.0).r / 1000.0 / u_levelSize.y; 97 | if (dynamicShadowHeight > 0.0) 98 | shadowSourceHeight = max(dynamicShadowHeight + u_dynamicShadowMapPosition.y / u_levelSize.y, shadowSourceHeight); 99 | float shadowCoefficient = max(0.0,min(1.0,(v_globalPosition.y - shadowSourceHeight * u_levelSize.y) / 10.0 + 1.0)); 100 | product *= shadowCoefficient; 101 | gl_FragColor = u_ambientLightColor + u_lightColor * max(0.0, product); 102 | // gl_FragColor += pow(length(normal.xy), 9.0) * vec4(0.1,0.0,0.0,1.0) * 1.0; 103 | gl_FragColor *= texture2D(u_diffuseMap, v_texCoords); 104 | //// gl_FragColor += texture2D(u_dynamicShadowMap, (v_globalPosition.xz - u_dynamicShadowMapPosition.xz)/100.0); 105 | // gl_FragColor.r = shadowSourceHeight; 106 | product = dot(reflect((u_normalCameraView * vec4(u_lightDirection,0.0)).xyz, (vec4(-normal,0.0)).xyz), (vec4(0.0, 0.0, -1.0, 0.0)).xyz); 107 | gl_FragColor += pow(max(0.0, product), 30.0) * u_lightColor * shadowCoefficient; 108 | // gl_FragColor += texture2D(u_diffuseMap, (normal.xy + 1.0) / 2.0) * pow(length(normal.xy), 10.0); 109 | //gl_FragColor = vec4(v_worldPosition.y / 256.0, 1.0, 1.0, 1.0); 110 | float fog = pow(v_localPosition.z / 1700.0, 2.0); 111 | gl_FragColor *= (1.0 - fog); 112 | gl_FragColor += fog * u_ambientLightColor + pow(1.0 - length(normal.xy), 25.0) * 3.0/10.0; 113 | gl_FragColor = vec4(gl_FragColor.rgb, 1.0); 114 | } 115 | } 116 | " 117 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WebSonic Engine - WebGL Sonic the Hedgehog Game Engine 2 | ====================================================== 3 | 4 | This is a Sonic engine I showcased on Sonic Retro last year. (2010) 5 | 6 | It's written on JavaScript, and makes use of the WebGL canvas API of HTML5 to 7 | draw its graphics. 8 | 9 | To play it, you'll need a modern browser with WebGL support (Firefox 4.0 and 10 | Chrome 10.0 are good enough) and a semi-decent graphics card. Just fire up 11 | `index.html` and you're ready to go. 12 | 13 | Keep in mind that to run it locally in Chrome, without a Web server, you'll 14 | need to launch the browser with the `--allow-file-access-from-files` command 15 | line flag. 16 | 17 | Introduction 18 | ------------ 19 | 20 | I was planning on a bigger release for this project, with finished levels and 21 | gameplay, but couldn't get it done due to time constraints. This perfectly 22 | usable piece of code had been sitting on my hard drive for months untouched, 23 | which is totally unfair, so I figured it was a good idea to release it for 24 | other people to tinker with it. 25 | 26 | This Engine is a more or less accurate implementation of the algorithms on the 27 | [Sonic Physics Guide](http://info.sonicretro.org/Sonic_Physics_Guide), 28 | generalized to ℝ³. Some subtle adaptions had to be made, since the original 29 | Sonic engine used height maps for collisions, (I couldn't afford to do that 30 | here) used a strange fixed point encoding for numbers, (I couldn't bother to 31 | use something other than JS's `Number`) and had like, only two dimensions to 32 | worry about. 33 | 34 | My goal here was to make it "feel" almost exactly as the original Sonic games, 35 | if you only walked left and right and hit jump. I think I've accomplished that 36 | goal. 37 | 38 | **Beware:** The engine code is really messy, hacky and poorly documented. 39 | Sorry, I didn't have enough free time to refactor/clean it up. You might also 40 | find code doing something stupid as far as WebGL is concerned, since I wrote 41 | this engine as a learning exercise for WebGL. 42 | 43 | Controls 44 | -------- 45 | 46 | [Q][W] - Rotate Camera 47 | [S] - Look Up 48 | [X] - Jump [^] 49 | [ Spacebar - Crouch Down ] [<][V][>] - Move 50 | 51 | Spindash = Crouch Dowm + Jump 52 | 53 | Technical Info 54 | -------------- 55 | 56 | - The engine uses simple JSON wrappers for GLSL fragment and vector shaders. 57 | That seemed like a pretty good idea at the time, but is now one aspect of the 58 | engine I'm not completely happy about. 59 | 60 | - Textures are regular PNG or JPEG (or even SVG!) images loaded by the browser. 61 | 62 | - Meshes are stored on a custom JSON based format. (Yeah I know, using ASCII 63 | for binary data is kind of stupid, but it's easier to load.) Animations are 64 | vertex-based. (Yeah I know.) The source code of a simple converter 65 | from Quake's `md3` files to this custom format is included under `tools/`. 66 | You'll need to compile it with a C compiler to use it. 67 | 68 | - Sylvester is used for Matrix and Vector math. It's probably going to be 69 | slow by today's standards, specially if compared to something using 70 | `Float32Array`s, but it's only used on non-critical sections of the code. 71 | 72 | - Maps are tile-based! We have 128x128x128 tiles, as you would expect from a 73 | Sonic engine. :-) The JSON format used should be self-descriptive. 74 | 75 | - Objects can be indexed spatially using a grid, to allow for greater 76 | performance when having lots of objects. 77 | 78 | - The only form of collision checking available is ray-triangle intersection. 79 | 80 | - Check the Web Inspector / Web Console of your browser for (not-so) 81 | interesting debug messages. 82 | 83 | - The camera is always aligned to one of eight axes in relation to the player: 84 | 85 | \ | / 86 | \|/ 87 | -----+----- 88 | /|\ 89 | / | \ 90 | 91 | This is very important, as it allows the player to move with great precision 92 | on a 3D environment using only the arrow keys. 93 | 94 | FAQ 95 | --- 96 | 97 | ### The control scheme sucks! 98 | 99 | *This is not really a question*. Anyway, I like it this way. Feel free to 100 | change it on the source. 101 | 102 | ### Can I play this with a Joystick? 103 | 104 | As of 2011, natively, no. You can use something like JoyToKey if you want it 105 | *that bad*. Expect that to change as progress is done by WHATWG and browser 106 | implementors with the HTML living standard's `` tag. 107 | 108 | Known Bugs/Issues 109 | ----------------- 110 | 111 | - The matrix inversion algorithm used is numerically unstable. This is actually 112 | a Sylvester issue, and might result in wrong lighting on the models when 113 | they're rotated to specific angles. 114 | 115 | - Post-processing effects were *super slow*, and are commented out. 116 | 117 | - There's something very wrong with my shadow math. It currently depends on the 118 | camera position, and will break if you change the camera distance, FoV or 119 | orientation on any axis other than Y. 120 | 121 | Cool ideas if you have free time 122 | -------------------------------- 123 | 124 | ### Easy 125 | 126 | - Edit the current test level! 127 | 128 | - Make a new level! 129 | 130 | - Change/add more textures! 131 | 132 | ### Medium 133 | 134 | - Implement new objects! 135 | 136 | - Add sound/music support! 137 | 138 | ### Hard 139 | 140 | - Add customizable control schemes! 141 | 142 | - Change the shader format to something more reasonable! 143 | 144 | - Add support for binary 3d model formats! 145 | 146 | - Refactor/clean up the code! 147 | 148 | - Fix the shadow code! 149 | 150 | - Implement bone-based animation! 151 | 152 | - Optimize/enhance post-processing code. 153 | 154 | - Make a level editor! 155 | 156 | - Implement online multiplayer using something like Socket.IO! (This one is 157 | pretty cool) 158 | 159 | ### Near-impossible 160 | 161 | - Make a finished game out of this ;D -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | WebSonic Engine 2 | Copyright (C) 2010, 2011 by Marco Aurélio "Mark the Echidna" 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | ------------------------------------------------------------------------------- 23 | 24 | The license above covers all the source code of the WebSonic engine: 25 | 26 | index.html 27 | js/* 28 | css/* 29 | tools/* 30 | level/* 31 | shader/* 32 | 33 | ------------------------------------------------------------------------------- 34 | 35 | The 3d models and textures are covered by a Creative Commons BY-NC License: 36 | 37 | mesh/* 38 | texture/* 39 | 40 | ------------------------------------------------------------------------------- 41 | 42 | This distribution also includes external libraries on which WebSonic depends. 43 | They are: 44 | 45 | lib/jquery.js 46 | 47 | /*! 48 | * jQuery JavaScript Library v1.4.2 49 | * http://jquery.com/ 50 | * 51 | * Copyright 2010, John Resig 52 | * Dual licensed under the MIT or GPL Version 2 licenses. 53 | * http://jquery.org/license 54 | * 55 | * Includes Sizzle.js 56 | * http://sizzlejs.com/ 57 | * Copyright 2010, The Dojo Foundation 58 | * Released under the MIT, BSD, and GPL Licenses. 59 | * 60 | * Date: Sat Feb 13 22:33:48 2010 -0500 61 | */ 62 | 63 | lib/sylvester.js 64 | 65 | // === Sylvester === 66 | // Vector and Matrix mathematics modules for JavaScript 67 | // Copyright (c) 2007 James Coglan 68 | // 69 | // Permission is hereby granted, free of charge, to any person obtaining 70 | // a copy of this software and associated documentation files (the "Software"), 71 | // to deal in the Software without restriction, including without limitation 72 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 73 | // and/or sell copies of the Software, and to permit persons to whom the 74 | // Software is furnished to do so, subject to the following conditions: 75 | // 76 | // The above copyright notice and this permission notice shall be included 77 | // in all copies or substantial portions of the Software. 78 | // 79 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 80 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 81 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 82 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 83 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 84 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 85 | // DEALINGS IN THE SOFTWARE. 86 | 87 | lib/sylvester.addons.js 88 | 89 | This is a piece of sample code extracted from a WebGL tutorial. I could no 90 | longer find the original source/author of the code, but a Google search for its 91 | first line: "// augment Sylvester some" revealed that it's in widespread use. 92 | 93 | I'm going to assume a MIT/BSD license here, since that's usual. If you're/know 94 | the original author, drop me a message and I'll attribute you/him. (Or, if you 95 | have problems with me using this code, drop me a message and I'll replace it 96 | with something else.) 97 | 98 | ------------------------------------------------------------------------------- 99 | 100 | A Note About Sonic the Hedgehog Intelectual Property 101 | ---------------------------------------------------- 102 | 103 | "Sonic the Hedgehog" is a registered trademark of SEGA Corporation. Some 104 | specific Sonic gameplay elements might be also patented by SEGA Corporation. 105 | 106 | All the code and assets included on this distribution are completely 107 | new/original material. This includes the textures, objects and the Sonic 108 | 3D model. NO CONTENT OR DATA "RIPPED" OR SOMEHOW "PIRATED" FROM ANY SONIC GAME 109 | IS INCLUDED ALONG WITH THIS CODE. 110 | 111 | The Sonic-related content on this distribution therefore violates NO COPYRIGHTS, 112 | but might violate trademark and patent rights, since it's used without express, 113 | written permission from SEGA. 114 | 115 | This is considered NOT TO be the case, due to: 116 | 117 | 1. The purely artistic/hobby nature of the content, comparable to 118 | fanfiction, fan-art or sculpures depicting Sonic the Hedgehog; 119 | 120 | 2. The strictly non-commercial license on which the Sonic related assets 121 | are distributed; 122 | 123 | 3. The existing good/strong bounds between the personal from SEGA and the 124 | Sonic fanbase; 125 | 126 | 4. Large amounts of similar, precedent material regarding which SEGA 127 | Corporation expressed no opposition or no concern (hundreds of existing Sonic 128 | fan-games) 129 | 130 | If you make use of the source on this distribution to create, specifically, 131 | a Sonic fan-game, you're STRONGLY ADVISED not to directly or indirectly profit 132 | from it. (e.g. selling licenses or advertising in game) Unless, of course, 133 | you're authorized to do so by SEGA Corporation (which is totally cool, then.) 134 | 135 | This is obviously NOT an obligation, since the code is MIT licensed, but not 136 | following this advice WILL likely get you into legal trouble, and may backfire 137 | at the Sonic fan-gaming community as a whole. 138 | 139 | If you make use of the source code on this distribution for anything else, do 140 | as you please. Just honor the MIT license. -------------------------------------------------------------------------------- /tools/md3tojson3d.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef unsigned char U8; 6 | typedef signed short int S16; 7 | typedef signed long int S32; 8 | typedef unsigned long int U32; 9 | typedef float F32; 10 | typedef struct { F32 x; F32 y; F32 z; } VEC3; 11 | 12 | #define MAX_QPATH 64 13 | #define MD3_XYZ_SCALE (1.0/64) 14 | 15 | typedef struct MD3 { 16 | S32 ident; 17 | S32 version; 18 | U8 name[MAX_QPATH]; 19 | S32 flags; 20 | S32 numFrames; 21 | S32 numTags; 22 | S32 numSurfaces; 23 | S32 numSkins; 24 | S32 offsetFrames; 25 | S32 offsetT0ags; 26 | S32 offsetSurfaces; 27 | S32 offsetEOF; 28 | } MD3; 29 | 30 | typedef struct Frame { 31 | VEC3 minBounds; 32 | VEC3 maxBounds; 33 | VEC3 localOrigin; 34 | F32 radius; 35 | U8 name[16]; 36 | } Frame; 37 | 38 | typedef struct Surface { 39 | U32 ident; 40 | U8 name[MAX_QPATH]; 41 | S32 flags; 42 | S32 numFrames; 43 | S32 numShaders; 44 | S32 numVerts; 45 | S32 numTriangles; 46 | S32 offsetTriangles; 47 | S32 offsetShaders; 48 | S32 offsetSt; 49 | S32 offsetXYZNormal; 50 | S32 offsetEnd; 51 | } Surface; 52 | 53 | typedef struct Triangle { 54 | S32 v1; 55 | S32 v2; 56 | S32 v3; 57 | } Triangle; 58 | 59 | typedef struct TexCoord { 60 | F32 s; 61 | F32 t; 62 | } TexCoord; 63 | 64 | typedef struct Vertex { 65 | S16 x; 66 | S16 y; 67 | S16 z; 68 | S16 normal; 69 | } Vertex; 70 | 71 | int main (int argc, char **argv){ 72 | if (argc != 2) { 73 | printf("usage: %s [FILENAME]\n", argv[0]); 74 | } else { 75 | FILE * file = fopen(argv[1], "rb"); 76 | if (!file) { 77 | fprintf(stderr, "Error: Cannot find source file\n"); 78 | exit(-1); 79 | } 80 | MD3 md3; 81 | 82 | if (!fread(&md3, sizeof(MD3), 1, file)) { 83 | fprintf(stderr, "Error: Cannot read source file (unexpected end of file)\n"); 84 | exit(-1); 85 | } 86 | 87 | if (md3.ident != 0x33504449) { 88 | fprintf(stderr, "Error: File is not a valid MD3 mesh.\n"); 89 | exit(-1); 90 | } 91 | 92 | printf("{\n"); 93 | printf("\"format\": \"JSON3D 1.0\",\n"); 94 | printf("\"surfaces\": [\n"); 95 | 96 | int lastOffset = md3.offsetSurfaces; 97 | fseek(file, lastOffset, SEEK_SET); 98 | for (int i = 0; i < md3.numSurfaces; i++) { 99 | Surface surface; 100 | if (!fread(&surface, sizeof(Surface), 1, file)) { 101 | fprintf(stderr, "Error: Cannot read source file (unexpected end of file)\n"); 102 | exit(-1); 103 | } 104 | printf("{\n"); 105 | printf("\"name\": \"%s\",\n", surface.name); 106 | 107 | Vertex *vertices = malloc(sizeof(Vertex) * surface.numVerts * surface.numFrames); 108 | Triangle *tris = malloc(sizeof(Triangle) * surface.numTriangles); 109 | TexCoord *texCoords = malloc(sizeof(TexCoord) * surface.numVerts); 110 | fseek(file, lastOffset + surface.offsetXYZNormal, SEEK_SET); 111 | if (fread(vertices, sizeof(Vertex), surface.numVerts * surface.numFrames, file) < surface.numVerts * surface.numFrames) { 112 | fprintf(stderr, "Error: Cannot read source file (unexpected end of file)\n"); 113 | exit(-1); 114 | } 115 | fseek(file, lastOffset + surface.offsetTriangles, SEEK_SET); 116 | if (fread(tris, sizeof(Triangle), surface.numTriangles, file) < surface.numTriangles) { 117 | fprintf(stderr, "Error: Cannot read source file (unexpected end of file)\n"); 118 | exit(-1); 119 | } 120 | fseek(file, lastOffset + surface.offsetSt, SEEK_SET); 121 | if (fread(texCoords, sizeof(TexCoord), surface.numVerts, file) < surface.numVerts) { 122 | fprintf(stderr, "Error: Cannot read source file (unexpected end of file)\n"); 123 | exit(-1); 124 | } 125 | printf("\"frames\": [\n"); 126 | for (int f = 0; f < surface.numFrames; f++) { 127 | printf("{\n"); 128 | printf("\"vertices\": [\n"); 129 | for (int j = 0; j < surface.numTriangles; j++) { 130 | printf("%.1f, %.1f, %.1f,\n", vertices[surface.numVerts * f + tris[j].v1].x * MD3_XYZ_SCALE, vertices[surface.numVerts * f + tris[j].v1].z * MD3_XYZ_SCALE, vertices[surface.numVerts * f + tris[j].v1].y * MD3_XYZ_SCALE); 131 | printf("%.1f, %.1f, %.1f,\n", vertices[surface.numVerts * f + tris[j].v3].x * MD3_XYZ_SCALE, vertices[surface.numVerts * f + tris[j].v3].z * MD3_XYZ_SCALE, vertices[surface.numVerts * f + tris[j].v3].y * MD3_XYZ_SCALE); 132 | printf("%.1f, %.1f, %.1f,\n", vertices[surface.numVerts * f + tris[j].v2].x * MD3_XYZ_SCALE, vertices[surface.numVerts * f + tris[j].v2].z * MD3_XYZ_SCALE, vertices[surface.numVerts * f + tris[j].v2].y * MD3_XYZ_SCALE); 133 | } 134 | printf("], \n"); 135 | 136 | printf("\"normals\": [\n"); 137 | for (int j = 0; j < surface.numTriangles; j++) { 138 | float lat, lng, x, y, z; 139 | lat = ((vertices[surface.numVerts * f + tris[j].v1].normal >> 8) & 255) * (2 * 3.14159265) / 255.0; 140 | lng = (vertices[surface.numVerts * f + tris[j].v1].normal & 255) * (2 * 3.14159265) / 255.0; 141 | x = cos( lat ) * sin( lng ); 142 | y = sin( lat ) * sin( lng ); 143 | z = cos( lng ); 144 | 145 | printf("%.2f, %.2f, %.2f,\n", x, z, y); 146 | 147 | lat = ((vertices[surface.numVerts * f + tris[j].v3].normal >> 8) & 255) * (2 * 3.14159265) / 255.0; 148 | lng = (vertices[surface.numVerts * f + tris[j].v3].normal & 255) * (2 * 3.14159265) / 255.0; 149 | x = cos( lat ) * sin( lng ); 150 | y = sin( lat ) * sin( lng ); 151 | z = cos( lng ); 152 | printf("%.2f, %.2f, %.2f,\n", x, z, y); 153 | 154 | lat = ((vertices[surface.numVerts * f + tris[j].v2].normal >> 8) & 255) * (2 * 3.14159265) / 255.0; 155 | lng = (vertices[surface.numVerts * f + tris[j].v2].normal & 255) * (2 * 3.14159265) / 255.0; 156 | x = cos( lat ) * sin( lng ); 157 | y = sin( lat ) * sin( lng ); 158 | z = cos( lng ); 159 | 160 | printf("%.2f, %.2f, %.2f,\n", x, z, y); 161 | 162 | 163 | } 164 | printf("] \n"); 165 | printf("},\n"); 166 | } 167 | printf("],\n"); 168 | printf("\"textureCoordinates\": [\n"); 169 | for (int j = 0; j < surface.numTriangles; j++) { 170 | printf("%f, %f,\n", texCoords[tris[j].v1].s, texCoords[tris[j].v1].t); 171 | printf("%f, %f,\n", texCoords[tris[j].v3].s, texCoords[tris[j].v3].t); 172 | printf("%f, %f,\n", texCoords[tris[j].v2].s, texCoords[tris[j].v2].t); 173 | } 174 | printf("]\n"); 175 | free(tris); 176 | free(vertices); 177 | 178 | if (i == md3.numSurfaces - 1) { 179 | printf("}\n"); 180 | } else { 181 | printf("},\n"); 182 | } 183 | 184 | lastOffset += surface.offsetEnd; 185 | fseek(file, lastOffset, SEEK_SET); 186 | } 187 | 188 | printf("]\n"); 189 | printf("}\n"); 190 | 191 | fclose(file); 192 | } 193 | } -------------------------------------------------------------------------------- /lib/sylvester.addons.js: -------------------------------------------------------------------------------- 1 | // augment Sylvester some 2 | Matrix.Translation = function (v) 3 | { 4 | if (v.elements.length == 2) { 5 | var r = Matrix.I(3); 6 | r.elements[2][0] = v.elements[0]; 7 | r.elements[2][1] = v.elements[1]; 8 | return r; 9 | } 10 | 11 | if (v.elements.length == 3) { 12 | var r = Matrix.I(4); 13 | r.elements[0][3] = v.elements[0]; 14 | r.elements[1][3] = v.elements[1]; 15 | r.elements[2][3] = v.elements[2]; 16 | return r; 17 | } 18 | 19 | throw "Invalid length for Translation"; 20 | } 21 | 22 | Matrix.prototype.flatten = function () 23 | { 24 | // var result = []; 25 | if (this.elements.length == 0) 26 | return []; 27 | 28 | var stride = this.elements[0].length; 29 | var length = stride * this.elements.length; 30 | var result = new Float32Array(length); 31 | 32 | for (var j = 0; j < this.elements[0].length; j++) 33 | for (var i = 0; i < this.elements.length; i++) 34 | result[j * stride + i] = (this.elements[i][j]); 35 | 36 | return result; 37 | } 38 | 39 | Matrix.prototype.ensure4x4 = function() 40 | { 41 | if (this.elements.length == 4 && 42 | this.elements[0].length == 4) 43 | return this; 44 | 45 | if (this.elements.length > 4 || 46 | this.elements[0].length > 4) 47 | return null; 48 | 49 | for (var i = 0; i < this.elements.length; i++) { 50 | for (var j = this.elements[i].length; j < 4; j++) { 51 | if (i == j) 52 | this.elements[i].push(1); 53 | else 54 | this.elements[i].push(0); 55 | } 56 | } 57 | 58 | for (var i = this.elements.length; i < 4; i++) { 59 | if (i == 0) 60 | this.elements.push([1, 0, 0, 0]); 61 | else if (i == 1) 62 | this.elements.push([0, 1, 0, 0]); 63 | else if (i == 2) 64 | this.elements.push([0, 0, 1, 0]); 65 | else if (i == 3) 66 | this.elements.push([0, 0, 0, 1]); 67 | } 68 | 69 | return this; 70 | }; 71 | 72 | Matrix.prototype.make3x3 = function() 73 | { 74 | if (this.elements.length != 4 || 75 | this.elements[0].length != 4) 76 | return null; 77 | 78 | return Matrix.create([[this.elements[0][0], this.elements[0][1], this.elements[0][2]], 79 | [this.elements[1][0], this.elements[1][1], this.elements[1][2]], 80 | [this.elements[2][0], this.elements[2][1], this.elements[2][2]]]); 81 | }; 82 | 83 | Matrix.prototype.makeModeMatrix = function() { 84 | var used = [false, false, false, false]; 85 | var newM = []; 86 | for (var i in this.elements) { 87 | var biggest = 0; 88 | var biggestJ = 0; 89 | for (var j in this.elements[i]) { 90 | if (Math.abs(this.elements[i][j]) > biggest) { 91 | if (!used[j]) { 92 | used[j] = true; 93 | biggest = Math.abs(this.elements[i][j]); 94 | biggestJ = j; 95 | } 96 | } 97 | } 98 | var newRow = []; 99 | for (var j in this.elements[i]) { 100 | if (j == biggestJ) { 101 | newRow.push((this.elements[i][j] > 0)? 1 : -1) 102 | } else { 103 | newRow.push(0); 104 | } 105 | } 106 | newM.push(newRow); 107 | } 108 | 109 | var result = $M(newM); 110 | console.log(result.inspect()); 111 | return result; 112 | } 113 | 114 | Vector.prototype.to4D = function() { 115 | if (this.dimensions() == 4) { 116 | return this; 117 | } else if (this.dimensions() == 3) { 118 | return $V([this.elements[0], this.elements[1], this.elements[2], 1.0]); 119 | } else if (this.dimensions() == 2) { 120 | return $V([this.elements[0], this.elements[1], 0.0, 1.0]); 121 | } 122 | } 123 | 124 | Vector.prototype.xyz = function() 125 | { 126 | return $V([this.elements[0],this.elements[1],this.elements[2]]) 127 | } 128 | 129 | Vector.prototype.xy = function() 130 | { 131 | return $V([this.elements[0],this.elements[1]]) 132 | } 133 | 134 | Vector.prototype.xz = function() 135 | { 136 | return $V([this.elements[0],this.elements[2]]) 137 | } 138 | 139 | Vector.prototype.yz = function() 140 | { 141 | return $V([this.elements[1],this.elements[2]]) 142 | } 143 | 144 | 145 | Vector.prototype.flatten = function () 146 | { 147 | return this.elements; 148 | }; 149 | 150 | function mht(m) { 151 | var s = ""; 152 | if (m.length == 16) { 153 | for (var i = 0; i < 4; i++) { 154 | s += "[" + m[i*4+0].toFixed(4) + "," + m[i*4+1].toFixed(4) + "," + m[i*4+2].toFixed(4) + "," + m[i*4+3].toFixed(4) + "]
"; 155 | } 156 | } else if (m.length == 9) { 157 | for (var i = 0; i < 3; i++) { 158 | s += "[" + m[i*3+0].toFixed(4) + "," + m[i*3+1].toFixed(4) + "," + m[i*3+2].toFixed(4) + "]
"; 159 | } 160 | } else { 161 | return m.toString(); 162 | } 163 | return s; 164 | } 165 | 166 | Matrix.LookAt = function(eye, center, up) 167 | { 168 | if (!up) 169 | up = $V([0, 1, 0]); 170 | 171 | var z = eye.subtract(center).toUnitVector(); 172 | var x = up.cross(z).toUnitVector(); 173 | var y = z.cross(x).toUnitVector(); 174 | 175 | var m = $M([[x.e(1), x.e(2), x.e(3), 0], 176 | [y.e(1), y.e(2), y.e(3), 0], 177 | [z.e(1), z.e(2), z.e(3), 0], 178 | [ 0, 0, 0, 1]]); 179 | 180 | /* var t = $M([[1, 0, 0, -eye.elements[0]], 181 | [0, 1, 0, -eye.elements[1]], 182 | [0, 0, 1, -eye.elements[2]], 183 | [0, 0, 0, 1]]);*/ 184 | return m; 185 | } 186 | 187 | Matrix.prototype.AlignYAxis = function(y, alpha) { 188 | // var x = $V([this.elements[0][0], this.elements[1][0], this.elements[2][0]]); 189 | var y = $V([y.elements[0] * alpha + this.elements[0][1] * (1 - alpha), y.elements[1] * alpha + this.elements[1][1] * (1 - alpha), y.elements[2] * alpha + this.elements[2][1] * (1 - alpha)]).toUnitVector(); 190 | // console.log(y.inspect()); 191 | var z = $V([this.elements[0][2], this.elements[1][2], this.elements[2][2]]); 192 | var x = z.cross(y).toUnitVector().x(-1); 193 | z = x.cross(y).toUnitVector(); 194 | return $M([x.elements,y.elements,z.elements]).ensure4x4().transpose(); 195 | } 196 | 197 | Matrix.LookAtY = function(targetY, back) 198 | { 199 | if (!back) 200 | back = $V([0, 0, 1]); 201 | 202 | var y = targetY.x(-1).toUnitVector(); 203 | var x = back.cross(y).toUnitVector(); 204 | var z = y.cross(x).toUnitVector(); 205 | 206 | if (y.elements[1] < 0) { 207 | var m = $M([[x.e(1), x.e(2), x.e(3), 0], 208 | [-y.e(1), -y.e(2), -y.e(3), 0], 209 | [z.e(1), z.e(2), z.e(3), 0], 210 | [ 0, 0, 0, 1]]); 211 | } else { 212 | var m = $M([[-x.e(1), -x.e(2), -x.e(3), 0], 213 | [-y.e(1), -y.e(2), -y.e(3), 0], 214 | [-z.e(1), -z.e(2), -z.e(3), 0], 215 | [ 0, 0, 0, 1]]); 216 | } 217 | 218 | return m; 219 | } 220 | 221 | 222 | // 223 | // gluPerspective 224 | // 225 | Matrix.Perspective = function(fovy, aspect, znear, zfar) 226 | { 227 | var ymax = znear * Math.tan(fovy * Math.PI / 360.0); 228 | var ymin = -ymax; 229 | var xmin = ymin * aspect; 230 | var xmax = ymax * aspect; 231 | 232 | return Matrix.Frustum(xmin, xmax, ymin, ymax, znear, zfar); 233 | } 234 | 235 | // 236 | // glFrustum 237 | // 238 | Matrix.Frustum = function(left, right, 239 | bottom, top, 240 | znear, zfar) 241 | { 242 | var X = 2*znear/(right-left); 243 | var Y = 2*znear/(top-bottom); 244 | var A = (right+left)/(right-left); 245 | var B = (top+bottom)/(top-bottom); 246 | var C = -(zfar+znear)/(zfar-znear); 247 | var D = -2*zfar*znear/(zfar-znear); 248 | 249 | return $M([[X, 0, A, 0], 250 | [0, Y, B, 0], 251 | [0, 0, C, D], 252 | [0, 0, -1, 0]]); 253 | } 254 | 255 | // 256 | // glOrtho 257 | // 258 | Matrix.Ortho = function(left, right, bottom, top, znear, zfar) 259 | { 260 | var tx = - (right + left) / (right - left); 261 | var ty = - (top + bottom) / (top - bottom); 262 | var tz = - (zfar + znear) / (zfar - znear); 263 | 264 | return $M([[2 / (right - left), 0, 0, tx], 265 | [0, 2 / (top - bottom), 0, ty], 266 | [0, 0, -2 / (zfar - znear), tz], 267 | [0, 0, 0, 1]]); 268 | } 269 | -------------------------------------------------------------------------------- /level/lvl0.jsonlevel: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Test Zone", 3 | "mesh": "mesh/lvl0.jsonmesh", 4 | "data": [ 5 | [ 6 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], 7 | [ 2, 0, 4, 4, 4, 1, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, null, null, 0, null ], 8 | [ 2, 0, 4, 4, 4, 1, 0, 0, 0, 0, 0, null, null, null, null, null, null, null, null, 4, 3, 0, 0, null, null, null, 0 ], 9 | [ null, 0, 0, 0, 4, null, null, 0, 1, 0, null, null, null, null, null, null, null, null, null, 4, 0, 0, 0, null, null, 0, null ], 10 | [ null, null, null, null, 0, null, null, null, 1, 0, null, null, null, null, null, null, null, null, null, 4, 0, 0, 0, null, null, null, 0 ], 11 | [ null, null, null, null, 0, null, null, 4, 4, 4, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null ], 12 | [ null, null, null, null, 0, 0, 0, 4, 4, 4, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 13 | [ null, null, null, null, 0, null, 0, 4, 4, 4, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null ], 14 | [ null, null, null, null, null, null, 0, null, null, 4, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 15 | [ null, null, null, null, null, null, null, 0, 0, 0, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null ], 16 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 17 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 18 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 19 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 20 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 21 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 22 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 23 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 24 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 25 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 26 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 27 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 28 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null ], 29 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 30 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 31 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ], 32 | [ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0 ] 33 | ], 34 | [ 35 | [ null, null, null, null, 0, null, null, null, null ], 36 | [ 2, null, 1, 0, 0, null, null, null, null ], 37 | [ 2, null, 1, 0, 0, null, null, null, null ], 38 | [ null, null, null, null, null, null, null, 1, null ], 39 | [ null, null, null, null, null, null, null, 1, null ], 40 | [ null, 0, null, null, null, null, null, 4, 4 ], 41 | [ null, 0, null, null, null, null, null, null, null ], 42 | [ null, 0, null, null, null, null, null, 4, 4 ], 43 | [ null, 0, null, 0, null, null, null, null, null ], 44 | [ null, 0, null, 0, null, null, null, null, null ], 45 | [ null, 0, null, 0, null, null, null, null, null ], 46 | [ null, 0, 0, 0, null, null, null, null, null ] 47 | ], 48 | [ 49 | [ null, null, null, null, null, 0, null, null, null ], 50 | [ 2, 1, null, null, null, 0, null, null, null ], 51 | [ 2, 1, null, null, null, 0, null, null, null ], 52 | [ null, null, null, null, null, 0, 0, null, null ], 53 | [ null, null, null, null, null, null, 0, null, null ], 54 | [ null, null, null, null, null, null, null, 4, 4 ], 55 | [ null, null, null, null, null, null, null, 4, 4 ], 56 | [ null, null, null, null, null, null, null, 4, 4 ], 57 | [ null, null, null, null, null, null, null, null, null ], 58 | [ null, null, null, null, null, null, null, null, null ], 59 | [ null, null, null, null, null, null, null, null, null ], 60 | [ null, null, null, null, null, null, null, null, null ] 61 | ], 62 | [ 63 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4 ], 64 | [ 4, null, 4, null, null, 4, 4, 4, 4 ], 65 | [ 4, null, 4, 4, null, 0, 5, 0, 4 ], 66 | [ 4, 4, 4, 4, null, 8, 4, 4, 4 ], 67 | [ 4, 4, 4, 4, null, 8, 4, 4, 4 ], 68 | [ 4, 4, null, null, null, 0, 4, 4, 4 ], 69 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4 ], 70 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4 ] 71 | ], 72 | [ 73 | [ 4, 4, 4, 4, 4, 4, 4, null, null ], 74 | [ 4, null, null, 0, 0, 0, 4, null, null ], 75 | [ 4, null, null, 0, 0, 0, 4, null, null ], 76 | [ 4, 4, 4, 4, 0, 0, 4, null, null ], 77 | [ 4, 4, 4, 4, 0, 0, 4, 0, 4 ], 78 | [ 4, null, 1, 0, 0, 0, 5, null, 4 ], 79 | [ 4, 4, 4, 4, 4, 4, 4, null, 4 ], 80 | [ 4, 4, 4, 4, 4, 4, 4, null, null ] 81 | ], 82 | [ 83 | [ null, null, null, null, 5, 5, null, null, 4 ], 84 | [ 4, 8, 4, 4, 4, 4, null, null, 4 ], 85 | [ 4, 8, 4, 4, 4, 4, null, null, 4 ], 86 | [ 4, 8, 4, 4, 4, 4, null, null, null ], 87 | [ 4, 8, 4, 4, 4, 4, null, 4, 4 ], 88 | [ 4, 0, null, 4, 4, 4, null, 4, 4 ], 89 | [ 4, 4, 4, 4, 4, 4, null, 4, 4 ], 90 | [ 4, 4, 4, 4, 4, 4, null, null, null ] 91 | ], 92 | [ 93 | [ null, null, null, null, null, null, null, null, null ], 94 | [ null, null, null, null, 1, null, null, null, null ], 95 | [ null, null, null, null, null, null, 0, 0, null ], 96 | [ null, null, null, null, null, null, null, null, 0 ], 97 | [ null, null, null, null, null, null, null, null, null ], 98 | [ null, null, null, null, null, null, null, null, null ], 99 | [ null, null, null, null, null, null, null, null, null ], 100 | [ null, null, null, null, null, null, null, null, null ] 101 | ], 102 | [ 103 | [ null, null, null, null, null, null, null, null, null ], 104 | [ 2, 1, 1, 1, null, null, null, null, null ], 105 | [ null, null, null, null, null, null, null, null, null ], 106 | [ null, null, null, null, null, null, null, null, null ], 107 | [ null, null, null, null, null, null, null, null, null ], 108 | [ null, null, null, null, null, null, null, null, null ], 109 | [ null, null, null, null, null, null, null, null, null ], 110 | [ null, null, null, null, null, null, null, null, null ] 111 | ], 112 | [ 113 | [ null, null, null, null, null, null, null, null, null ], 114 | [ 2, null, null, null, null, null, null, null, null ], 115 | [ null, null, null, null, null, null, null, null, null ], 116 | [ null, null, null, null, null, null, null, null, null ], 117 | [ null, null, null, null, null, null, null, null, null ], 118 | [ null, null, null, null, null, null, null, null, null ], 119 | [ null, null, null, null, null, null, null, null, null ], 120 | [ null, null, null, null, null, null, null, null, null ] 121 | ], 122 | [ 123 | [ null, null, null, null, null, null, null, null, null ], 124 | [ 2, null, 0, 0, 0, null, null, null, null ], 125 | [ null, null, 0, 0, 0, null, null, null, null ], 126 | [ null, 0, 0, 0, 0, 5, 5, 5, 5 ], 127 | [ null, null, null, null, null, null, null, null, null ], 128 | [ null, null, null, null, null, null, null, null, null ], 129 | [ null, null, null, null, null, null, null, null, null ], 130 | [ null, null, null, null, null, null, null, null, null ] 131 | ], 132 | [ 133 | [ null, null, null, null, null, null, null, null, null ], 134 | [ 2, null, null, null, null, null, null, null, null ], 135 | [ null, null, null, null, null, null, null, null, null ], 136 | [ null, null, null, null, null, null, null, null, null ], 137 | [ null, null, null, null, null, null, null, null, null ], 138 | [ null, null, null, null, null, null, null, null, null ], 139 | [ null, null, null, null, null, null, null, null, null ], 140 | [ null, null, null, null, null, null, null, null, null ] 141 | ], 142 | ] 143 | } -------------------------------------------------------------------------------- /lib/sylvester.js: -------------------------------------------------------------------------------- 1 | eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9 17={3i:\'0.1.3\',16:1e-6};l v(){}v.23={e:l(i){8(i<1||i>7.4.q)?w:7.4[i-1]},2R:l(){8 7.4.q},1u:l(){8 F.1x(7.2u(7))},24:l(a){9 n=7.4.q;9 V=a.4||a;o(n!=V.q){8 1L}J{o(F.13(7.4[n-1]-V[n-1])>17.16){8 1L}}H(--n);8 2x},1q:l(){8 v.u(7.4)},1b:l(a){9 b=[];7.28(l(x,i){b.19(a(x,i))});8 v.u(b)},28:l(a){9 n=7.4.q,k=n,i;J{i=k-n;a(7.4[i],i+1)}H(--n)},2q:l(){9 r=7.1u();o(r===0){8 7.1q()}8 7.1b(l(x){8 x/r})},1C:l(a){9 V=a.4||a;9 n=7.4.q,k=n,i;o(n!=V.q){8 w}9 b=0,1D=0,1F=0;7.28(l(x,i){b+=x*V[i-1];1D+=x*x;1F+=V[i-1]*V[i-1]});1D=F.1x(1D);1F=F.1x(1F);o(1D*1F===0){8 w}9 c=b/(1D*1F);o(c<-1){c=-1}o(c>1){c=1}8 F.37(c)},1m:l(a){9 b=7.1C(a);8(b===w)?w:(b<=17.16)},34:l(a){9 b=7.1C(a);8(b===w)?w:(F.13(b-F.1A)<=17.16)},2k:l(a){9 b=7.2u(a);8(b===w)?w:(F.13(b)<=17.16)},2j:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x+V[i-1]})},2C:l(a){9 V=a.4||a;o(7.4.q!=V.q){8 w}8 7.1b(l(x,i){8 x-V[i-1]})},22:l(k){8 7.1b(l(x){8 x*k})},x:l(k){8 7.22(k)},2u:l(a){9 V=a.4||a;9 i,2g=0,n=7.4.q;o(n!=V.q){8 w}J{2g+=7.4[n-1]*V[n-1]}H(--n);8 2g},2f:l(a){9 B=a.4||a;o(7.4.q!=3||B.q!=3){8 w}9 A=7.4;8 v.u([(A[1]*B[2])-(A[2]*B[1]),(A[2]*B[0])-(A[0]*B[2]),(A[0]*B[1])-(A[1]*B[0])])},2A:l(){9 m=0,n=7.4.q,k=n,i;J{i=k-n;o(F.13(7.4[i])>F.13(m)){m=7.4[i]}}H(--n);8 m},2Z:l(x){9 a=w,n=7.4.q,k=n,i;J{i=k-n;o(a===w&&7.4[i]==x){a=i+1}}H(--n);8 a},3g:l(){8 S.2X(7.4)},2d:l(){8 7.1b(l(x){8 F.2d(x)})},2V:l(x){8 7.1b(l(y){8(F.13(y-x)<=17.16)?x:y})},1o:l(a){o(a.K){8 a.1o(7)}9 V=a.4||a;o(V.q!=7.4.q){8 w}9 b=0,2b;7.28(l(x,i){2b=x-V[i-1];b+=2b*2b});8 F.1x(b)},3a:l(a){8 a.1h(7)},2T:l(a){8 a.1h(7)},1V:l(t,a){9 V,R,x,y,z;2S(7.4.q){27 2:V=a.4||a;o(V.q!=2){8 w}R=S.1R(t).4;x=7.4[0]-V[0];y=7.4[1]-V[1];8 v.u([V[0]+R[0][0]*x+R[0][1]*y,V[1]+R[1][0]*x+R[1][1]*y]);1I;27 3:o(!a.U){8 w}9 C=a.1r(7).4;R=S.1R(t,a.U).4;x=7.4[0]-C[0];y=7.4[1]-C[1];z=7.4[2]-C[2];8 v.u([C[0]+R[0][0]*x+R[0][1]*y+R[0][2]*z,C[1]+R[1][0]*x+R[1][1]*y+R[1][2]*z,C[2]+R[2][0]*x+R[2][1]*y+R[2][2]*z]);1I;2P:8 w}},1t:l(a){o(a.K){9 P=7.4.2O();9 C=a.1r(P).4;8 v.u([C[0]+(C[0]-P[0]),C[1]+(C[1]-P[1]),C[2]+(C[2]-(P[2]||0))])}1d{9 Q=a.4||a;o(7.4.q!=Q.q){8 w}8 7.1b(l(x,i){8 Q[i-1]+(Q[i-1]-x)})}},1N:l(){9 V=7.1q();2S(V.4.q){27 3:1I;27 2:V.4.19(0);1I;2P:8 w}8 V},2n:l(){8\'[\'+7.4.2K(\', \')+\']\'},26:l(a){7.4=(a.4||a).2O();8 7}};v.u=l(a){9 V=25 v();8 V.26(a)};v.i=v.u([1,0,0]);v.j=v.u([0,1,0]);v.k=v.u([0,0,1]);v.2J=l(n){9 a=[];J{a.19(F.2F())}H(--n);8 v.u(a)};v.1j=l(n){9 a=[];J{a.19(0)}H(--n);8 v.u(a)};l S(){}S.23={e:l(i,j){o(i<1||i>7.4.q||j<1||j>7.4[0].q){8 w}8 7.4[i-1][j-1]},33:l(i){o(i>7.4.q){8 w}8 v.u(7.4[i-1])},2E:l(j){o(j>7.4[0].q){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][j-1])}H(--n);8 v.u(a)},2R:l(){8{2D:7.4.q,1p:7.4[0].q}},2D:l(){8 7.4.q},1p:l(){8 7.4[0].q},24:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(7.4.q!=M.q||7.4[0].q!=M[0].q){8 1L}9 b=7.4.q,15=b,i,G,10=7.4[0].q,j;J{i=15-b;G=10;J{j=10-G;o(F.13(7.4[i][j]-M[i][j])>17.16){8 1L}}H(--G)}H(--b);8 2x},1q:l(){8 S.u(7.4)},1b:l(a){9 b=[],12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;b[i]=[];J{j=10-G;b[i][j]=a(7.4[i][j],i+1,j+1)}H(--G)}H(--12);8 S.u(b)},2i:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4.q==M.q&&7.4[0].q==M[0].q)},2j:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x+M[i-1][j-1]})},2C:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2i(M)){8 w}8 7.1b(l(x,i,j){8 x-M[i-1][j-1]})},2B:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}8(7.4[0].q==M.q)},22:l(a){o(!a.4){8 7.1b(l(x){8 x*a})}9 b=a.1u?2x:1L;9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}o(!7.2B(M)){8 w}9 d=7.4.q,15=d,i,G,10=M[0].q,j;9 e=7.4[0].q,4=[],21,20,c;J{i=15-d;4[i]=[];G=10;J{j=10-G;21=0;20=e;J{c=e-20;21+=7.4[i][c]*M[c][j]}H(--20);4[i][j]=21}H(--G)}H(--d);9 M=S.u(4);8 b?M.2E(1):M},x:l(a){8 7.22(a)},32:l(a,b,c,d){9 e=[],12=c,i,G,j;9 f=7.4.q,1p=7.4[0].q;J{i=c-12;e[i]=[];G=d;J{j=d-G;e[i][j]=7.4[(a+i-1)%f][(b+j-1)%1p]}H(--G)}H(--12);8 S.u(e)},31:l(){9 a=7.4.q,1p=7.4[0].q;9 b=[],12=1p,i,G,j;J{i=1p-12;b[i]=[];G=a;J{j=a-G;b[i][j]=7.4[j][i]}H(--G)}H(--12);8 S.u(b)},1y:l(){8(7.4.q==7.4[0].q)},2A:l(){9 m=0,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(F.13(7.4[i][j])>F.13(m)){m=7.4[i][j]}}H(--G)}H(--12);8 m},2Z:l(x){9 a=w,12=7.4.q,15=12,i,G,10=7.4[0].q,j;J{i=15-12;G=10;J{j=10-G;o(7.4[i][j]==x){8{i:i+1,j:j+1}}}H(--G)}H(--12);8 w},30:l(){o(!7.1y){8 w}9 a=[],n=7.4.q,k=n,i;J{i=k-n;a.19(7.4[i][i])}H(--n);8 v.u(a)},1K:l(){9 M=7.1q(),1c;9 n=7.4.q,k=n,i,1s,1n=7.4[0].q,p;J{i=k-n;o(M.4[i][i]==0){2e(j=i+1;j17.16){1Y++;1I}}H(--G)}H(--a);8 1Y},3d:l(){8 7.1Y()},2W:l(a){9 M=a.4||a;o(1g(M[0][0])==\'1f\'){M=S.u(M).4}9 T=7.1q(),1p=T.4[0].q;9 b=T.4.q,15=b,i,G,10=M[0].q,j;o(b!=M.q){8 w}J{i=15-b;G=10;J{j=10-G;T.4[i][1p+j]=M[i][j]}H(--G)}H(--b);8 T},2w:l(){o(!7.1y()||7.2y()){8 w}9 a=7.4.q,15=a,i,j;9 M=7.2W(S.I(a)).1K();9 b,1n=M.4[0].q,p,1c,2v;9 c=[],2c;J{i=a-1;1c=[];b=1n;c[i]=[];2v=M.4[i][i];J{p=1n-b;2c=M.4[i][p]/2v;1c.19(2c);o(p>=15){c[i].19(2c)}}H(--b);M.4[i]=1c;2e(j=0;j3||b.4.q>3){8 w}9 c=b.1u();o(c===0){8 w}7.K=a;7.U=v.u([b.4[0]/c,b.4[1]/c,b.4[2]/c]);8 7}};14.u=l(a,b){9 L=25 14();8 L.1Z(a,b)};14.X=14.u(v.1j(3),v.i);14.Y=14.u(v.1j(3),v.j);14.Z=14.u(v.1j(3),v.k);l 11(){}11.23={24:l(a){8(7.1h(a.K)&&7.1m(a))},1q:l(){8 11.u(7.K,7.W)},2U:l(a){9 V=a.4||a;8 11.u([7.K.4[0]+V[0],7.K.4[1]+V[1],7.K.4[2]+(V[2]||0)],7.W)},1m:l(a){9 b;o(a.W){b=7.W.1C(a.W);8(F.13(b)<=17.16||F.13(F.1A-b)<=17.16)}1d o(a.U){8 7.W.2k(a.U)}8 w},2k:l(a){9 b=7.W.1C(a.W);8(F.13(F.1A/2-b)<=17.16)},1o:l(a){o(7.1v(a)||7.1h(a)){8 0}o(a.K){9 A=7.K.4,B=a.K.4,N=7.W.4;8 F.13((A[0]-B[0])*N[0]+(A[1]-B[1])*N[1]+(A[2]-B[2])*N[2])}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;8 F.13((A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2])}},1h:l(a){o(a.W){8 w}o(a.U){8(7.1h(a.K)&&7.1h(a.K.2j(a.U)))}1d{9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=F.13(N[0]*(A[0]-P[0])+N[1]*(A[1]-P[1])+N[2]*(A[2]-(P[2]||0)));8(b<=17.16)}},1v:l(a){o(1g(a.U)==\'1f\'&&1g(a.W)==\'1f\'){8 w}8!7.1m(a)},1U:l(a){o(!7.1v(a)){8 w}o(a.U){9 A=a.K.4,D=a.U.4,P=7.K.4,N=7.W.4;9 b=(N[0]*(P[0]-A[0])+N[1]*(P[1]-A[1])+N[2]*(P[2]-A[2]))/(N[0]*D[0]+N[1]*D[1]+N[2]*D[2]);8 v.u([A[0]+D[0]*b,A[1]+D[1]*b,A[2]+D[2]*b])}1d o(a.W){9 c=7.W.2f(a.W).2q();9 N=7.W.4,A=7.K.4,O=a.W.4,B=a.K.4;9 d=S.1j(2,2),i=0;H(d.2y()){i++;d=S.u([[N[i%3],N[(i+1)%3]],[O[i%3],O[(i+1)%3]]])}9 e=d.2w().4;9 x=N[0]*A[0]+N[1]*A[1]+N[2]*A[2];9 y=O[0]*B[0]+O[1]*B[1]+O[2]*B[2];9 f=[e[0][0]*x+e[0][1]*y,e[1][0]*x+e[1][1]*y];9 g=[];2e(9 j=1;j<=3;j++){g.19((i==j)?0:f[(j+(5-i)%3)%3])}8 14.u(g,c)}},1r:l(a){9 P=a.4||a;9 A=7.K.4,N=7.W.4;9 b=(A[0]-P[0])*N[0]+(A[1]-P[1])*N[1]+(A[2]-(P[2]||0))*N[2];8 v.u([P[0]+N[0]*b,P[1]+N[1]*b,(P[2]||0)+N[2]*b])},1V:l(t,a){9 R=S.1R(t,a.U).4;9 C=a.1r(7.K).4;9 A=7.K.4,N=7.W.4;9 b=C[0],1E=C[1],1J=C[2],1w=A[0],18=A[1],1a=A[2];9 x=1w-b,y=18-1E,z=1a-1J;8 11.u([b+R[0][0]*x+R[0][1]*y+R[0][2]*z,1E+R[1][0]*x+R[1][1]*y+R[1][2]*z,1J+R[2][0]*x+R[2][1]*y+R[2][2]*z],[R[0][0]*N[0]+R[0][1]*N[1]+R[0][2]*N[2],R[1][0]*N[0]+R[1][1]*N[1]+R[1][2]*N[2],R[2][0]*N[0]+R[2][1]*N[1]+R[2][2]*N[2]])},1t:l(a){o(a.W){9 A=7.K.4,N=7.W.4;9 b=A[0],18=A[1],1a=A[2],2M=N[0],2L=N[1],2Q=N[2];9 c=7.K.1t(a).4;9 d=b+2M,2p=18+2L,2m=1a+2Q;9 Q=a.1r([d,2p,2m]).4;9 e=[Q[0]+(Q[0]-d)-c[0],Q[1]+(Q[1]-2p)-c[1],Q[2]+(Q[2]-2m)-c[2]];8 11.u(c,e)}1d o(a.U){8 7.1V(F.1A,a)}1d{9 P=a.4||a;8 11.u(7.K.1t([P[0],P[1],(P[2]||0)]),7.W)}},1Z:l(a,b,c){a=v.u(a);a=a.1N();o(a===w){8 w}b=v.u(b);b=b.1N();o(b===w){8 w}o(1g(c)==\'1f\'){c=w}1d{c=v.u(c);c=c.1N();o(c===w){8 w}}9 d=a.4[0],18=a.4[1],1a=a.4[2];9 e=b.4[0],1W=b.4[1],1X=b.4[2];9 f,1i;o(c!==w){9 g=c.4[0],2l=c.4[1],2t=c.4[2];f=v.u([(1W-18)*(2t-1a)-(1X-1a)*(2l-18),(1X-1a)*(g-d)-(e-d)*(2t-1a),(e-d)*(2l-18)-(1W-18)*(g-d)]);1i=f.1u();o(1i===0){8 w}f=v.u([f.4[0]/1i,f.4[1]/1i,f.4[2]/1i])}1d{1i=F.1x(e*e+1W*1W+1X*1X);o(1i===0){8 w}f=v.u([b.4[0]/1i,b.4[1]/1i,b.4[2]/1i])}7.K=a;7.W=f;8 7}};11.u=l(a,b,c){9 P=25 11();8 P.1Z(a,b,c)};11.2I=11.u(v.1j(3),v.k);11.2H=11.u(v.1j(3),v.i);11.2G=11.u(v.1j(3),v.j);11.36=11.2I;11.35=11.2H;11.3j=11.2G;9 $V=v.u;9 $M=S.u;9 $L=14.u;9 $P=11.u;',62,206,'||||elements|||this|return|var||||||||||||function|||if||length||||create|Vector|null|||||||||Math|nj|while||do|anchor||||||||Matrix||direction||normal||||kj|Plane|ni|abs|Line|ki|precision|Sylvester|A2|push|A3|map|els|else||undefined|typeof|contains|mod|Zero|D3|D2|isParallelTo|kp|distanceFrom|cols|dup|pointClosestTo|np|reflectionIn|modulus|intersects|A1|sqrt|isSquare|X2|PI|X3|angleFrom|mod1|C2|mod2|sin|cos|break|C3|toRightTriangular|false|Y3|to3D|E2|E1|E3|Rotation|Y2|Y1|intersectionWith|rotate|v12|v13|rank|setVectors|nc|sum|multiply|prototype|eql|new|setElements|case|each|PA3|PA2|part|new_element|round|for|cross|product|AD2|isSameSizeAs|add|isPerpendicularTo|v22|AN3|inspect|AD3|AN2|toUnitVector|PsubQ3|PsubQ2|v23|dot|divisor|inverse|true|isSingular|determinant|max|canMultiplyFromLeft|subtract|rows|col|random|ZX|YZ|XY|Random|join|N2|N1|D1|slice|default|N3|dimensions|switch|liesIn|translate|snapTo|augment|Diagonal|trace|indexOf|diagonal|transpose|minor|row|isAntiparallelTo|ZY|YX|acos|RotationZ|RotationY|liesOn|RotationX|inv|rk|tr|det|toDiagonalMatrix|toUpperTriangular|version|XZ'.split('|'),0,{})) -------------------------------------------------------------------------------- /js/WorldEngine.js: -------------------------------------------------------------------------------- 1 | var WorldEngine = function(engine) { 2 | var worldEngine = {} 3 | var gameTime = 0.0; 4 | var realTime = 0.0; 5 | var timeDelta; 6 | var TARGET_FPS = 60; 7 | var MIN_FPS = 2; 8 | var SUBSTEPS_PER_FRAME = 1; 9 | var BUCKET_WIDTH = 128.0; 10 | 11 | var stepCount = 0; 12 | var entities = []; 13 | var cameras = []; 14 | var buckets = {}; 15 | var fps = 0.0; 16 | var removalFlag = false; 17 | 18 | var uniforms = 19 | { u_lightColor: $V([1.0,1.0,1.0,1.0]), 20 | u_lightDirection: $V([-0.05, 1, -0.1]).toUnitVector(), 21 | u_ambientLightColor: $V([0.2,0.2,0.25,1.0])}; 22 | 23 | var calculateBucketId = function(position) { 24 | return [Math.floor(position.elements[0]/BUCKET_WIDTH), Math.floor(position.elements[1]/BUCKET_WIDTH), Math.floor(position.elements[2]/BUCKET_WIDTH)].toString(); 25 | } 26 | 27 | worldEngine.uniforms = function(value) { 28 | if (value !== undefined) { 29 | uniforms = value; 30 | } 31 | return uniforms; 32 | } 33 | 34 | worldEngine.update = function() { 35 | var newRealTime = new Date().getTime() * 0.001; 36 | timeDelta = newRealTime - realTime; 37 | if (timeDelta > 1.0/MIN_FPS) { 38 | timeDelta = 1.0/MIN_FPS; 39 | } 40 | 41 | timeDelta *= 1.0; 42 | 43 | var substeps; 44 | if (timeDelta > 1.0/TARGET_FPS) { 45 | substeps = Math.ceil(timeDelta / (1.0/TARGET_FPS)) * SUBSTEPS_PER_FRAME; 46 | } else { 47 | substeps = SUBSTEPS_PER_FRAME; 48 | } 49 | 50 | fps = fps * 0.9 + (1/timeDelta) * 0.1; 51 | 52 | for (var i = 0; i < substeps; i++) { 53 | gameTime += timeDelta / substeps; 54 | 55 | uniforms.u_time = gameTime; 56 | 57 | removalFlag = false; 58 | for (var j = 0; j < entities.length; j++) { 59 | entities[j].update(timeDelta / substeps); 60 | if (removalFlag) { 61 | j--; 62 | removalFlag = false; 63 | } 64 | }; 65 | 66 | cameras.forEach(function(camera) { 67 | var cameraCenter = camera.modelView().x($V([0,0,-4*BUCKET_WIDTH,1.0])); 68 | var xmin = Math.floor(cameraCenter.elements[0] / BUCKET_WIDTH) - 3; 69 | var ymin = Math.floor(cameraCenter.elements[1] / BUCKET_WIDTH) - 6; 70 | var zmin = Math.floor(cameraCenter.elements[2] / BUCKET_WIDTH) - 3; 71 | var xmax = xmin + 6; 72 | var ymax = ymin + 15; 73 | var zmax = zmin + 6; 74 | for (var x = xmin; x <= xmax; x++) { 75 | for (var y = ymin; y <= ymax; y++) { 76 | for (var z = zmin; z <= zmax; z++) { 77 | var bucket = buckets[[x,y,z]]; 78 | if (bucket) { 79 | for (var j = 0; j < bucket.length; j++) { 80 | if (bucket[j].lastStep != stepCount) { 81 | bucket[j].lastStep = stepCount; 82 | bucket[j].update(timeDelta / substeps); 83 | } 84 | if (!removalFlag) { 85 | var entity = bucket[j]; 86 | var newBucketId = calculateBucketId(entity.position()); 87 | if (entity.bucketId != newBucketId) { 88 | // Entity is changing bucket 89 | worldEngine.remove(entity); 90 | worldEngine.add(entity); 91 | } 92 | } 93 | if (removalFlag) { 94 | j--; 95 | removalFlag = false; 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | }); 103 | 104 | engine.input.resetPressed(); 105 | stepCount++; 106 | } 107 | realTime = newRealTime; 108 | } 109 | 110 | var shadowMapTarget = null; 111 | var shadowMapPosition = null; 112 | var renderShadows = function(camera) { 113 | var SHADOW_MAP_HEIGHT = 512; 114 | var SHADOW_MAP_WIDTH = 512; 115 | engine.gfx.clear(); 116 | if (!shadowMapTarget) { 117 | shadowMapTarget = new engine.gfx.RenderTarget(SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT); 118 | } 119 | shadowMapTarget.activate(); 120 | shadowMapPosition = camera.modelView().x($V([0,0,-300,1.0])).xyz(); 121 | var oldCameraPosition = camera.position(); 122 | var oldCameraRotation = camera.rotation(); 123 | camera.position(shadowMapPosition.add($V([0,550,0]))); 124 | camera.rotation(Matrix.RotationX(-Math.PI/1.99).ensure4x4()); 125 | var renderParams = { 126 | camera: camera, 127 | projection: camera.projection(shadowMapTarget.aspectRatio()), 128 | projectionFlat: camera.projection(shadowMapTarget.aspectRatio()).flatten(), 129 | cameraView: camera.modelView().inverse(), 130 | cameraViewFlat: camera.modelView().inverse().flatten(), 131 | uniforms: [uniforms], 132 | shader: engine.resources.get("shader/depth.jsonshader") 133 | } 134 | removalFlag = false; 135 | for (var i = 0; i < entities.length; i++) { 136 | if (entities[i].castsShadows) { 137 | entities[i].render(renderParams);//inversedCameraModelView); 138 | if (removalFlag) { 139 | i--; 140 | removalFlag = false; 141 | } 142 | } 143 | } 144 | var cameraCenter = camera.modelView().x($V([0,0,-4*BUCKET_WIDTH,1.0])); 145 | var xmin = Math.floor(cameraCenter.elements[0] / BUCKET_WIDTH) - 3; 146 | var ymin = Math.floor(cameraCenter.elements[1] / BUCKET_WIDTH) - 3; 147 | var zmin = Math.floor(cameraCenter.elements[2] / BUCKET_WIDTH) - 3; 148 | var xmax = xmin + 6; 149 | var ymax = ymin + 5; 150 | var zmax = zmin + 6; 151 | for (var x = xmin; x <= xmax; x++) { 152 | for (var y = ymin; y <= ymax; y++) { 153 | for (var z = zmin; z <= zmax; z++) { 154 | var bucket = buckets[[x,y,z]]; 155 | if (bucket) { 156 | for (var i = 0; i < bucket.length; i++) { 157 | if (bucket[i].castsShadows) { 158 | bucket[i].render(renderParams); 159 | if (removalFlag) { 160 | i--; 161 | removalFlag = false; 162 | } 163 | } 164 | } 165 | } 166 | } 167 | } 168 | } 169 | 170 | camera.position(oldCameraPosition); 171 | camera.rotation(oldCameraRotation); 172 | 173 | shadowMapTarget.deactivate(); 174 | } 175 | 176 | var colorTarget = null; 177 | worldEngine.render = function() { 178 | if (!colorTarget) { 179 | colorTarget = new engine.gfx.RenderTarget(); 180 | } 181 | 182 | cameras.forEach(function(camera) { 183 | renderShadows(camera); 184 | // colorTarget.activate(); 185 | // engine.gfx.gl.clearColor(40/256.0,124/256.0,235/256.0,1.0); 186 | engine.gfx.clear(); 187 | engine.gfx.gl.clearColor(0.0, 0.0, 0.0, 0.0); 188 | uniforms.u_dynamicShadowMap = shadowMapTarget.texture(); 189 | uniforms.u_dynamicShadowMapPosition = shadowMapPosition; 190 | var renderParams = { 191 | camera: camera, 192 | projection: camera.projection(colorTarget.aspectRatio()), 193 | projectionFlat: camera.projection(colorTarget.aspectRatio()).flatten(), 194 | cameraView: camera.modelView().inverse(), 195 | cameraViewFlat: camera.modelView().inverse().flatten(), 196 | uniforms: [uniforms], 197 | // shader: engine.resources.get("shader/depth.jsonshader") 198 | }; 199 | removalFlag = false; 200 | var renderList = []; 201 | 202 | for (var i = 0; i < entities.length; i++) { 203 | renderList.push(entities[i]); 204 | //entities[i].render(renderParams);//inversedCameraModelView); 205 | if (removalFlag) { 206 | i--; 207 | removalFlag = false; 208 | } 209 | } 210 | var cameraCenter = camera.modelView().x($V([0,0,-4*BUCKET_WIDTH,1.0])); 211 | var xmin = Math.floor(cameraCenter.elements[0] / BUCKET_WIDTH) - 3; 212 | var ymin = Math.floor(cameraCenter.elements[1] / BUCKET_WIDTH) - 3; 213 | var zmin = Math.floor(cameraCenter.elements[2] / BUCKET_WIDTH) - 3; 214 | var xmax = xmin + 6; 215 | var ymax = ymin + 5; 216 | var zmax = zmin + 6; 217 | for (var x = xmin; x <= xmax; x++) { 218 | for (var y = ymin; y <= ymax; y++) { 219 | for (var z = zmin; z <= zmax; z++) { 220 | var bucket = buckets[[x,y,z]]; 221 | if (bucket) { 222 | for (var i = 0; i < bucket.length; i++) { 223 | renderList.push(bucket[i]); 224 | //bucket[i].render(renderParams); 225 | if (removalFlag) { 226 | i--; 227 | removalFlag = false; 228 | } 229 | } 230 | } 231 | } 232 | } 233 | } 234 | 235 | renderList.sort(function(a,b){ 236 | if (!a.layer) 237 | a.layer = 0; 238 | if (!b.layer) 239 | b.layer = 0; 240 | return a.layer - b.layer; 241 | }) 242 | 243 | if (renderList.length > 0) { 244 | var lastLayer = renderList[0].layer; 245 | for (var i in renderList) { 246 | if (lastLayer != renderList[i].layer) { 247 | engine.gfx.clearDepth(); 248 | lastLayer = renderList[i].layer; 249 | } 250 | renderList[i].render(renderParams); 251 | } 252 | } 253 | 254 | // colorTarget.deactivate(); 255 | // engine.gfx.clear(); 256 | // colorTarget.renderToScreen(); 257 | 258 | engine.gfx.gl.finish(); 259 | }); 260 | } 261 | 262 | worldEngine.time = function() { 263 | return gameTime; 264 | } 265 | 266 | worldEngine.add = function(entity) { 267 | if (!entity.isStatic) { 268 | entities.push(entity); 269 | if (entity.isCamera) { 270 | cameras.push(entity); 271 | } 272 | } else { 273 | var position = entity.position(); 274 | var bucketId = calculateBucketId(position); 275 | if (!buckets[bucketId]) { 276 | buckets[bucketId] = []; 277 | } 278 | buckets[bucketId].push(entity); 279 | entity.bucketId = bucketId; 280 | } 281 | } 282 | 283 | worldEngine.remove = function(entity) { 284 | var i; 285 | if (!entity.isStatic) { 286 | for (i in entities) { 287 | if (entities[i] === entity) { 288 | break; 289 | } 290 | } 291 | entities.splice(i, 1); 292 | } else { 293 | bucket = buckets[entity.bucketId]; 294 | for (i in bucket) { 295 | if (bucket[i] === entity) { 296 | break; 297 | } 298 | } 299 | bucket.splice(i, 1); 300 | removalFlag = true; 301 | } 302 | } 303 | 304 | worldEngine.Entity = function() { 305 | 306 | var entity = {}; 307 | var position = $V([0, 0, 0]); 308 | var rotation = Matrix.I(4); 309 | var scale = $V([1, 1, 1]); 310 | var modelView = null; 311 | var normalModelView = null; 312 | 313 | var computeModelView = function() { 314 | modelView = Matrix.Translation(position) 315 | .x(rotation) 316 | .x(scale.toDiagonalMatrix().ensure4x4()); 317 | normalModelView = null; 318 | } 319 | 320 | var computeNormalModelView = function() { 321 | normalModelView = modelView.inverse().transpose(); 322 | } 323 | 324 | entity.isEntity = true; 325 | entity.position = function(value) { 326 | if (value !== undefined) { 327 | position = value; 328 | modelView = null; 329 | } 330 | return position; 331 | } 332 | 333 | entity.rotation = function(value) { 334 | if (value !== undefined && value != null) { 335 | rotation = value; 336 | modelView = null; 337 | } 338 | return rotation; 339 | } 340 | 341 | entity.scale = function(value) { 342 | if (value !== undefined) { 343 | scale = value; 344 | modelView = null; 345 | } 346 | return scale; 347 | } 348 | 349 | entity.modelView = function() { 350 | if (modelView === null) { 351 | computeModelView(); 352 | } 353 | return modelView; 354 | } 355 | 356 | entity.normalModelView = function() { 357 | if (modelView === null) { 358 | computeModelView(); 359 | computeNormalModelView(); 360 | return normalModelView; 361 | } 362 | if (normalModelView === null) { 363 | computeNormalModelView(); 364 | } 365 | return entity.modelView().inverse().transpose(); 366 | } 367 | 368 | entity.render = function(projection, inversedCameraModelView) { 369 | } 370 | 371 | entity.update = function(timeDelta) { 372 | } 373 | 374 | return entity; 375 | } 376 | 377 | worldEngine.Camera = function() { 378 | 379 | var camera = worldEngine.Entity(); 380 | 381 | var fov = 45; 382 | var minZ = 50; 383 | var maxZ = 5000; 384 | 385 | camera.isCamera = true; 386 | camera.fov = function(value) { 387 | if (value !== undefined) { 388 | fov = value; 389 | projection = null; 390 | } 391 | return fov; 392 | } 393 | 394 | camera.minZ = function(value) { 395 | if (value !== undefined) { 396 | minZ = value; 397 | projection = null; 398 | } 399 | return minZ; 400 | } 401 | 402 | camera.maxZ = function(value) { 403 | if (value !== undefined) { 404 | maxZ = value; 405 | projection = null; 406 | } 407 | return maxZ; 408 | } 409 | 410 | camera.projection = function(aspectRatio) { 411 | if (!aspectRatio) { 412 | aspectRatio = engine.gfx.aspectRatio(); 413 | } 414 | return Matrix.Perspective(fov, aspectRatio, minZ, maxZ); 415 | } 416 | 417 | return camera; 418 | } 419 | 420 | worldEngine.MeshEntity = function(mesh) { 421 | var meshEntity = worldEngine.Entity(); 422 | 423 | meshEntity.isMeshEntity = true; 424 | meshEntity.castsShadows = true; 425 | meshEntity.frame = 0; 426 | meshEntity.render = function(renderParams) { 427 | renderParams.modelView = meshEntity.modelView(); 428 | renderParams.normalModelView = meshEntity.normalModelView(); 429 | mesh.render(renderParams, Math.floor(meshEntity.frame)); 430 | } 431 | 432 | return meshEntity; 433 | } 434 | 435 | worldEngine.LevelEntity = function(level) { 436 | var levelEntity = worldEngine.Entity(); 437 | 438 | levelEntity.render = function(renderParams) { 439 | var shadowMap = level.shadowMap(); 440 | if (shadowMap) { 441 | uniforms.u_levelShadowMap = shadowMap; 442 | } 443 | uniforms.u_levelSize = level.levelSize(); 444 | renderParams.modelView = levelEntity.modelView(); 445 | renderParams.normalModelView = levelEntity.normalModelView(); 446 | var cameraCenter = renderParams.camera.modelView().x($V([0,0,-730,1.0])); 447 | level.render(renderParams, cameraCenter); 448 | } 449 | 450 | levelEntity.rayIntersect = function(point, direction, maxDist, minDist) { 451 | return level.rayIntersect(point, direction, maxDist, minDist); 452 | } 453 | 454 | return levelEntity; 455 | } 456 | 457 | return worldEngine; 458 | } -------------------------------------------------------------------------------- /mesh/sky.jsonmesh: -------------------------------------------------------------------------------- 1 | { 2 | "format": "JSON3D 1.0", 3 | "surfaces": [ 4 | { 5 | "name": "Sphere01", 6 | "shader": "shader/sky.jsonshader", 7 | "samplers": { "u_cloudMap": "texture/sky.jpg" }, 8 | "frames": [ 9 | { 10 | "vertices": [0.0,37.6,21.7,0.0,43.4,0.0,-10.8,37.6,18.8,-10.8,37.6,18.8,0.0,43.4,0.0,-18.8,37.6,10.8,-18.8,37.6,10.8,0.0,43.4,0.0,-21.7,37.6,0.0,-21.7,37.6,0.0,0.0,43.4,0.0,-18.8,37.6,-10.8,-18.8,37.6,-10.8,0.0,43.4,0.0,-10.8,37.6,-18.8,-10.8,37.6,-18.8,0.0,43.4,0.0,0.0,37.6,-21.7,0.0,37.6,-21.7,0.0,43.4,0.0,10.8,37.6,-18.8,10.8,37.6,-18.8,0.0,43.4,0.0,18.8,37.6,-10.8,18.8,37.6,-10.8,0.0,43.4,0.0,21.7,37.6,0.0,21.7,37.6,0.0,0.0,43.4,0.0,18.8,37.6,10.8,18.8,37.6,10.8,0.0,43.4,0.0,10.8,37.6,18.8,10.8,37.6,18.8,0.0,43.4,0.0,0.0,37.6,21.7,0.0,21.7,37.6,0.0,37.6,21.7,-18.8,21.7,32.5,-18.8,21.7,32.5,0.0,37.6,21.7,-10.8,37.6,18.8,-18.8,21.7,32.5,-10.8,37.6,18.8,-32.5,21.7,18.8,-32.5,21.7,18.8,-10.8,37.6,18.8,-18.8,37.6,10.8,-32.5,21.7,18.8,-18.8,37.6,10.8,-37.6,21.7,0.0,-37.6,21.7,0.0,-18.8,37.6,10.8,-21.7,37.6,0.0,-37.6,21.7,0.0,-21.7,37.6,0.0,-32.5,21.7,-18.8,-32.5,21.7,-18.8,-21.7,37.6,0.0,-18.8,37.6,-10.8,-32.5,21.7,-18.8,-18.8,37.6,-10.8,-18.8,21.7,-32.5,-18.8,21.7,-32.5,-18.8,37.6,-10.8,-10.8,37.6,-18.8,-18.8,21.7,-32.5,-10.8,37.6,-18.8,0.0,21.7,-37.6,0.0,21.7,-37.6,-10.8,37.6,-18.8,0.0,37.6,-21.7,0.0,21.7,-37.6,0.0,37.6,-21.7,18.8,21.7,-32.5,18.8,21.7,-32.5,0.0,37.6,-21.7,10.8,37.6,-18.8,18.8,21.7,-32.5,10.8,37.6,-18.8,32.5,21.7,-18.8,32.5,21.7,-18.8,10.8,37.6,-18.8,18.8,37.6,-10.8,32.5,21.7,-18.8,18.8,37.6,-10.8,37.6,21.7,0.0,37.6,21.7,0.0,18.8,37.6,-10.8,21.7,37.6,0.0,37.6,21.7,0.0,21.7,37.6,0.0,32.5,21.7,18.8,32.5,21.7,18.8,21.7,37.6,0.0,18.8,37.6,10.8,32.5,21.7,18.8,18.8,37.6,10.8,18.8,21.7,32.5,18.8,21.7,32.5,18.8,37.6,10.8,10.8,37.6,18.8,18.8,21.7,32.5,10.8,37.6,18.8,0.0,21.7,37.6,0.0,21.7,37.6,10.8,37.6,18.8,0.0,37.6,21.7,0.0,0.0,43.4,0.0,21.7,37.6,-21.7,0.0,37.6,-21.7,0.0,37.6,0.0,21.7,37.6,-18.8,21.7,32.5,-21.7,0.0,37.6,-18.8,21.7,32.5,-37.6,0.0,21.7,-37.6,0.0,21.7,-18.8,21.7,32.5,-32.5,21.7,18.8,-37.6,0.0,21.7,-32.5,21.7,18.8,-43.4,0.0,0.0,-43.4,0.0,0.0,-32.5,21.7,18.8,-37.6,21.7,0.0,-43.4,0.0,0.0,-37.6,21.7,0.0,-37.6,0.0,-21.7,-37.6,0.0,-21.7,-37.6,21.7,0.0,-32.5,21.7,-18.8,-37.6,0.0,-21.7,-32.5,21.7,-18.8,-21.7,0.0,-37.6,-21.7,0.0,-37.6,-32.5,21.7,-18.8,-18.8,21.7,-32.5,-21.7,0.0,-37.6,-18.8,21.7,-32.5,0.0,0.0,-43.4,0.0,0.0,-43.4,-18.8,21.7,-32.5,0.0,21.7,-37.6,0.0,0.0,-43.4,0.0,21.7,-37.6,21.7,0.0,-37.6,21.7,0.0,-37.6,0.0,21.7,-37.6,18.8,21.7,-32.5,21.7,0.0,-37.6,18.8,21.7,-32.5,37.6,0.0,-21.7,37.6,0.0,-21.7,18.8,21.7,-32.5,32.5,21.7,-18.8,37.6,0.0,-21.7,32.5,21.7,-18.8,43.4,0.0,0.0,43.4,0.0,0.0,32.5,21.7,-18.8,37.6,21.7,0.0,43.4,0.0,0.0,37.6,21.7,0.0,37.6,0.0,21.7,37.6,0.0,21.7,37.6,21.7,0.0,32.5,21.7,18.8,37.6,0.0,21.7,32.5,21.7,18.8,21.7,0.0,37.6,21.7,0.0,37.6,32.5,21.7,18.8,18.8,21.7,32.5,21.7,0.0,37.6,18.8,21.7,32.5,0.0,0.0,43.4,0.0,0.0,43.4,18.8,21.7,32.5,0.0,21.7,37.6,0.0,-21.7,37.6,0.0,0.0,43.4,-18.8,-21.7,32.5,-18.8,-21.7,32.5,0.0,0.0,43.4,-21.7,0.0,37.6,-18.8,-21.7,32.5,-21.7,0.0,37.6,-32.5,-21.7,18.8,-32.5,-21.7,18.8,-21.7,0.0,37.6,-37.6,0.0,21.7,-32.5,-21.7,18.8,-37.6,0.0,21.7,-37.6,-21.7,0.0,-37.6,-21.7,0.0,-37.6,0.0,21.7,-43.4,0.0,0.0,-37.6,-21.7,0.0,-43.4,0.0,0.0,-32.5,-21.7,-18.8,-32.5,-21.7,-18.8,-43.4,0.0,0.0,-37.6,0.0,-21.7,-32.5,-21.7,-18.8,-37.6,0.0,-21.7,-18.8,-21.7,-32.5,-18.8,-21.7,-32.5,-37.6,0.0,-21.7,-21.7,0.0,-37.6,-18.8,-21.7,-32.5,-21.7,0.0,-37.6,0.0,-21.7,-37.6,0.0,-21.7,-37.6,-21.7,0.0,-37.6,0.0,0.0,-43.4,0.0,-21.7,-37.6,0.0,0.0,-43.4,18.8,-21.7,-32.5,18.8,-21.7,-32.5,0.0,0.0,-43.4,21.7,0.0,-37.6,18.8,-21.7,-32.5,21.7,0.0,-37.6,32.5,-21.7,-18.8,32.5,-21.7,-18.8,21.7,0.0,-37.6,37.6,0.0,-21.7,32.5,-21.7,-18.8,37.6,0.0,-21.7,37.6,-21.7,0.0,37.6,-21.7,0.0,37.6,0.0,-21.7,43.4,0.0,0.0,37.6,-21.7,0.0,43.4,0.0,0.0,32.5,-21.7,18.8,32.5,-21.7,18.8,43.4,0.0,0.0,37.6,0.0,21.7,32.5,-21.7,18.8,37.6,0.0,21.7,18.8,-21.7,32.5,18.8,-21.7,32.5,37.6,0.0,21.7,21.7,0.0,37.6,18.8,-21.7,32.5,21.7,0.0,37.6,0.0,-21.7,37.6,0.0,-21.7,37.6,21.7,0.0,37.6,0.0,0.0,43.4,0.0,-37.6,21.7,0.0,-21.7,37.6,-10.8,-37.6,18.8,-10.8,-37.6,18.8,0.0,-21.7,37.6,-18.8,-21.7,32.5,-10.8,-37.6,18.8,-18.8,-21.7,32.5,-18.8,-37.6,10.8,-18.8,-37.6,10.8,-18.8,-21.7,32.5,-32.5,-21.7,18.8,-18.8,-37.6,10.8,-32.5,-21.7,18.8,-21.7,-37.6,0.0,-21.7,-37.6,0.0,-32.5,-21.7,18.8,-37.6,-21.7,0.0,-21.7,-37.6,0.0,-37.6,-21.7,0.0,-18.8,-37.6,-10.8,-18.8,-37.6,-10.8,-37.6,-21.7,0.0,-32.5,-21.7,-18.8,-18.8,-37.6,-10.8,-32.5,-21.7,-18.8,-10.8,-37.6,-18.8,-10.8,-37.6,-18.8,-32.5,-21.7,-18.8,-18.8,-21.7,-32.5,-10.8,-37.6,-18.8,-18.8,-21.7,-32.5,0.0,-37.6,-21.7,0.0,-37.6,-21.7,-18.8,-21.7,-32.5,0.0,-21.7,-37.6,0.0,-37.6,-21.7,0.0,-21.7,-37.6,10.8,-37.6,-18.8,10.8,-37.6,-18.8,0.0,-21.7,-37.6,18.8,-21.7,-32.5,10.8,-37.6,-18.8,18.8,-21.7,-32.5,18.8,-37.6,-10.8,18.8,-37.6,-10.8,18.8,-21.7,-32.5,32.5,-21.7,-18.8,18.8,-37.6,-10.8,32.5,-21.7,-18.8,21.7,-37.6,0.0,21.7,-37.6,0.0,32.5,-21.7,-18.8,37.6,-21.7,0.0,21.7,-37.6,0.0,37.6,-21.7,0.0,18.8,-37.6,10.8,18.8,-37.6,10.8,37.6,-21.7,0.0,32.5,-21.7,18.8,18.8,-37.6,10.8,32.5,-21.7,18.8,10.8,-37.6,18.8,10.8,-37.6,18.8,32.5,-21.7,18.8,18.8,-21.7,32.5,10.8,-37.6,18.8,18.8,-21.7,32.5,0.0,-37.6,21.7,0.0,-37.6,21.7,18.8,-21.7,32.5,0.0,-21.7,37.6,-10.8,-37.6,18.8,0.0,-43.4,0.0,0.0,-37.6,21.7,-18.8,-37.6,10.8,0.0,-43.4,0.0,-10.8,-37.6,18.8,-21.7,-37.6,0.0,0.0,-43.4,0.0,-18.8,-37.6,10.8,-18.8,-37.6,-10.8,0.0,-43.4,0.0,-21.7,-37.6,0.0,-10.8,-37.6,-18.8,0.0,-43.4,0.0,-18.8,-37.6,-10.8,0.0,-37.6,-21.7,0.0,-43.4,0.0,-10.8,-37.6,-18.8,10.8,-37.6,-18.8,0.0,-43.4,0.0,0.0,-37.6,-21.7,18.8,-37.6,-10.8,0.0,-43.4,0.0,10.8,-37.6,-18.8,21.7,-37.6,0.0,0.0,-43.4,0.0,18.8,-37.6,-10.8,18.8,-37.6,10.8,0.0,-43.4,0.0,21.7,-37.6,0.0,10.8,-37.6,18.8,0.0,-43.4,0.0,18.8,-37.6,10.8,0.0,-37.6,21.7,0.0,-43.4,0.0,10.8,-37.6,18.8,], 11 | "normals": [0.08,-0.81,-0.58,0.01,-1.00,0.00,0.36,-0.81,-0.46,0.36,-0.81,-0.46,0.01,-1.00,0.00,0.54,-0.81,-0.23,0.54,-0.81,-0.23,0.01,-1.00,0.00,0.58,-0.81,0.06,0.58,-0.81,0.06,0.01,-1.00,0.00,0.48,-0.81,0.34,0.48,-0.81,0.34,0.01,-1.00,0.00,0.25,-0.81,0.53,0.25,-0.81,0.53,0.01,-1.00,0.00,-0.05,-0.81,0.59,-0.05,-0.81,0.59,0.01,-1.00,0.00,-0.34,-0.81,0.48,-0.34,-0.81,0.48,0.01,-1.00,0.00,-0.53,-0.81,0.25,-0.53,-0.81,0.25,0.01,-1.00,0.00,-0.58,-0.81,-0.08,-0.58,-0.81,-0.08,0.01,-1.00,0.00,-0.47,-0.81,-0.36,-0.47,-0.81,-0.36,0.01,-1.00,0.00,-0.22,-0.81,-0.55,-0.22,-0.81,-0.55,0.01,-1.00,0.00,0.08,-0.81,-0.58,0.06,-0.46,-0.89,0.08,-0.81,-0.58,0.51,-0.46,-0.73,0.51,-0.46,-0.73,0.08,-0.81,-0.58,0.36,-0.81,-0.46,0.51,-0.46,-0.73,0.36,-0.81,-0.46,0.80,-0.46,-0.38,0.80,-0.46,-0.38,0.36,-0.81,-0.46,0.54,-0.81,-0.23,0.80,-0.46,-0.38,0.54,-0.81,-0.23,0.89,-0.46,0.02,0.89,-0.46,0.02,0.54,-0.81,-0.23,0.58,-0.81,0.06,0.89,-0.46,0.02,0.58,-0.81,0.06,0.76,-0.46,0.46,0.76,-0.46,0.46,0.58,-0.81,0.06,0.48,-0.81,0.34,0.76,-0.46,0.46,0.48,-0.81,0.34,0.42,-0.46,0.79,0.42,-0.46,0.79,0.48,-0.81,0.34,0.25,-0.81,0.53,0.42,-0.46,0.79,0.25,-0.81,0.53,-0.03,-0.46,0.89,-0.03,-0.46,0.89,0.25,-0.81,0.53,-0.05,-0.81,0.59,-0.03,-0.46,0.89,-0.05,-0.81,0.59,-0.46,-0.46,0.76,-0.46,-0.46,0.76,-0.05,-0.81,0.59,-0.34,-0.81,0.48,-0.46,-0.46,0.76,-0.34,-0.81,0.48,-0.78,-0.46,0.43,-0.78,-0.46,0.43,-0.34,-0.81,0.48,-0.53,-0.81,0.25,-0.78,-0.46,0.43,-0.53,-0.81,0.25,-0.89,-0.46,-0.08,-0.89,-0.46,-0.08,-0.53,-0.81,0.25,-0.58,-0.81,-0.08,-0.89,-0.46,-0.08,-0.58,-0.81,-0.08,-0.73,-0.46,-0.51,-0.73,-0.46,-0.51,-0.58,-0.81,-0.08,-0.47,-0.81,-0.36,-0.73,-0.46,-0.51,-0.47,-0.81,-0.36,-0.39,-0.46,-0.80,-0.39,-0.46,-0.80,-0.47,-0.81,-0.36,-0.22,-0.81,-0.55,-0.39,-0.46,-0.80,-0.22,-0.81,-0.55,0.06,-0.46,-0.89,0.06,-0.46,-0.89,-0.22,-0.81,-0.55,0.08,-0.81,-0.58,0.04,0.02,-1.00,0.06,-0.46,-0.89,0.53,0.02,-0.85,0.53,0.02,-0.85,0.06,-0.46,-0.89,0.51,-0.46,-0.73,0.53,0.02,-0.85,0.51,-0.46,-0.73,0.88,0.02,-0.47,0.88,0.02,-0.47,0.51,-0.46,-0.73,0.80,-0.46,-0.38,0.88,0.02,-0.47,0.80,-0.46,-0.38,1.00,0.02,0.00,1.00,0.02,0.00,0.80,-0.46,-0.38,0.89,-0.46,0.02,1.00,0.02,0.00,0.89,-0.46,0.02,0.87,0.02,0.49,0.87,0.02,0.49,0.89,-0.46,0.02,0.76,-0.46,0.46,0.87,0.02,0.49,0.76,-0.46,0.46,0.51,0.02,0.86,0.51,0.02,0.86,0.76,-0.46,0.46,0.42,-0.46,0.79,0.51,0.02,0.86,0.42,-0.46,0.79,0.02,0.02,1.00,0.02,0.02,1.00,0.42,-0.46,0.79,-0.03,-0.46,0.89,0.02,0.02,1.00,-0.03,-0.46,0.89,-0.50,0.02,0.87,-0.50,0.02,0.87,-0.03,-0.46,0.89,-0.46,-0.46,0.76,-0.50,0.02,0.87,-0.46,-0.46,0.76,-0.86,0.02,0.51,-0.86,0.02,0.51,-0.46,-0.46,0.76,-0.78,-0.46,0.43,-0.86,0.02,0.51,-0.78,-0.46,0.43,-1.00,0.02,-0.04,-1.00,0.02,-0.04,-0.78,-0.46,0.43,-0.89,-0.46,-0.08,-1.00,0.02,-0.04,-0.89,-0.46,-0.08,-0.85,0.02,-0.53,-0.85,0.02,-0.53,-0.89,-0.46,-0.08,-0.73,-0.46,-0.51,-0.85,0.02,-0.53,-0.73,-0.46,-0.51,-0.46,0.02,-0.89,-0.46,0.02,-0.89,-0.73,-0.46,-0.51,-0.39,-0.46,-0.80,-0.46,0.02,-0.89,-0.39,-0.46,-0.80,0.04,0.02,-1.00,0.04,0.02,-1.00,-0.39,-0.46,-0.80,0.06,-0.46,-0.89,-0.01,0.49,-0.87,0.04,0.02,-1.00,0.43,0.49,-0.76,0.43,0.49,-0.76,0.04,0.02,-1.00,0.53,0.02,-0.85,0.43,0.49,-0.76,0.53,0.02,-0.85,0.76,0.49,-0.43,0.76,0.49,-0.43,0.53,0.02,-0.85,0.88,0.02,-0.47,0.76,0.49,-0.43,0.88,0.02,-0.47,0.87,0.49,0.00,0.87,0.49,0.00,0.88,0.02,-0.47,1.00,0.02,0.00,0.87,0.49,0.00,1.00,0.02,0.00,0.78,0.49,0.39,0.78,0.49,0.39,1.00,0.02,0.00,0.87,0.02,0.49,0.78,0.49,0.39,0.87,0.02,0.49,0.48,0.49,0.73,0.48,0.49,0.73,0.87,0.02,0.49,0.51,0.02,0.86,0.48,0.49,0.73,0.51,0.02,0.86,0.04,0.49,0.87,0.04,0.49,0.87,0.51,0.02,0.86,0.02,0.02,1.00,0.04,0.49,0.87,0.02,0.02,1.00,-0.40,0.49,0.78,-0.40,0.49,0.78,0.02,0.02,1.00,-0.50,0.02,0.87,-0.40,0.49,0.78,-0.50,0.02,0.87,-0.73,0.49,0.48,-0.73,0.49,0.48,-0.50,0.02,0.87,-0.86,0.02,0.51,-0.73,0.49,0.48,-0.86,0.02,0.51,-0.87,0.49,0.05,-0.87,0.49,0.05,-0.86,0.02,0.51,-1.00,0.02,-0.04,-0.87,0.49,0.05,-1.00,0.02,-0.04,-0.75,0.49,-0.44,-0.75,0.49,-0.44,-1.00,0.02,-0.04,-0.85,0.02,-0.53,-0.75,0.49,-0.44,-0.85,0.02,-0.53,-0.44,0.49,-0.76,-0.44,0.49,-0.76,-0.85,0.02,-0.53,-0.46,0.02,-0.89,-0.44,0.49,-0.76,-0.46,0.02,-0.89,-0.01,0.49,-0.87,-0.01,0.49,-0.87,-0.46,0.02,-0.89,0.04,0.02,-1.00,-0.03,0.83,-0.56,-0.01,0.49,-0.87,0.25,0.83,-0.50,0.25,0.83,-0.50,-0.01,0.49,-0.87,0.43,0.49,-0.76,0.25,0.83,-0.50,0.43,0.49,-0.76,0.46,0.83,-0.31,0.46,0.83,-0.31,0.43,0.49,-0.76,0.76,0.49,-0.43,0.46,0.83,-0.31,0.76,0.49,-0.43,0.56,0.83,-0.04,0.56,0.83,-0.04,0.76,0.49,-0.43,0.87,0.49,0.00,0.56,0.83,-0.04,0.87,0.49,0.00,0.51,0.83,0.23,0.51,0.83,0.23,0.87,0.49,0.00,0.78,0.49,0.39,0.51,0.83,0.23,0.78,0.49,0.39,0.33,0.83,0.45,0.33,0.83,0.45,0.78,0.49,0.39,0.48,0.49,0.73,0.33,0.83,0.45,0.48,0.49,0.73,0.07,0.83,0.55,0.07,0.83,0.55,0.48,0.49,0.73,0.04,0.49,0.87,0.07,0.83,0.55,0.04,0.49,0.87,-0.22,0.83,0.51,-0.22,0.83,0.51,0.04,0.49,0.87,-0.40,0.49,0.78,-0.22,0.83,0.51,-0.40,0.49,0.78,-0.45,0.83,0.33,-0.45,0.83,0.33,-0.40,0.49,0.78,-0.73,0.49,0.48,-0.45,0.83,0.33,-0.73,0.49,0.48,-0.55,0.83,0.06,-0.55,0.83,0.06,-0.73,0.49,0.48,-0.87,0.49,0.05,-0.55,0.83,0.06,-0.87,0.49,0.05,-0.50,0.83,-0.25,-0.50,0.83,-0.25,-0.87,0.49,0.05,-0.75,0.49,-0.44,-0.50,0.83,-0.25,-0.75,0.49,-0.44,-0.31,0.83,-0.46,-0.31,0.83,-0.46,-0.75,0.49,-0.44,-0.44,0.49,-0.76,-0.31,0.83,-0.46,-0.44,0.49,-0.76,-0.03,0.83,-0.56,-0.03,0.83,-0.56,-0.44,0.49,-0.76,-0.01,0.49,-0.87,0.25,0.83,-0.50,0.00,1.00,0.00,-0.03,0.83,-0.56,0.46,0.83,-0.31,0.00,1.00,0.00,0.25,0.83,-0.50,0.56,0.83,-0.04,0.00,1.00,0.00,0.46,0.83,-0.31,0.51,0.83,0.23,0.00,1.00,0.00,0.56,0.83,-0.04,0.33,0.83,0.45,0.00,1.00,0.00,0.51,0.83,0.23,0.07,0.83,0.55,0.00,1.00,0.00,0.33,0.83,0.45,-0.22,0.83,0.51,0.00,1.00,0.00,0.07,0.83,0.55,-0.45,0.83,0.33,0.00,1.00,0.00,-0.22,0.83,0.51,-0.55,0.83,0.06,0.00,1.00,0.00,-0.45,0.83,0.33,-0.50,0.83,-0.25,0.00,1.00,0.00,-0.55,0.83,0.06,-0.31,0.83,-0.46,0.00,1.00,0.00,-0.50,0.83,-0.25,-0.03,0.83,-0.56,0.00,1.00,0.00,-0.31,0.83,-0.46,] 12 | }, 13 | ], 14 | "textureCoordinates": [0.000000,0.166667,0.000000,0.000000,0.083333,0.166667,0.083333,0.166667,0.083333,0.000000,0.166667,0.166667,0.166667,0.166667,0.166667,0.000000,0.250000,0.166667,0.250000,0.166667,0.250000,0.000000,0.333333,0.166667,0.333333,0.166667,0.333333,0.000000,0.416667,0.166667,0.416667,0.166667,0.416667,0.000000,0.500000,0.166667,0.500000,0.166667,0.500000,0.000000,0.583333,0.166667,0.583333,0.166667,0.583333,0.000000,0.666667,0.166667,0.666667,0.166667,0.666667,0.000000,0.750000,0.166667,0.750000,0.166667,0.750000,0.000000,0.833333,0.166667,0.833333,0.166667,0.833333,0.000000,0.916667,0.166667,0.916667,0.166667,0.916667,0.000000,1.000000,0.166667,0.000000,0.333333,0.000000,0.166667,0.083333,0.333333,0.083333,0.333333,0.000000,0.166667,0.083333,0.166667,0.083333,0.333333,0.083333,0.166667,0.166667,0.333333,0.166667,0.333333,0.083333,0.166667,0.166667,0.166667,0.166667,0.333333,0.166667,0.166667,0.250000,0.333333,0.250000,0.333333,0.166667,0.166667,0.250000,0.166667,0.250000,0.333333,0.250000,0.166667,0.333333,0.333333,0.333333,0.333333,0.250000,0.166667,0.333333,0.166667,0.333333,0.333333,0.333333,0.166667,0.416667,0.333333,0.416667,0.333333,0.333333,0.166667,0.416667,0.166667,0.416667,0.333333,0.416667,0.166667,0.500000,0.333333,0.500000,0.333333,0.416667,0.166667,0.500000,0.166667,0.500000,0.333333,0.500000,0.166667,0.583333,0.333333,0.583333,0.333333,0.500000,0.166667,0.583333,0.166667,0.583333,0.333333,0.583333,0.166667,0.666667,0.333333,0.666667,0.333333,0.583333,0.166667,0.666667,0.166667,0.666667,0.333333,0.666667,0.166667,0.750000,0.333333,0.750000,0.333333,0.666667,0.166667,0.750000,0.166667,0.750000,0.333333,0.750000,0.166667,0.833333,0.333333,0.833333,0.333333,0.750000,0.166667,0.833333,0.166667,0.833333,0.333333,0.833333,0.166667,0.916667,0.333333,0.916667,0.333333,0.833333,0.166667,0.916667,0.166667,0.916667,0.333333,0.916667,0.166667,1.000000,0.333333,1.000000,0.333333,0.916667,0.166667,1.000000,0.166667,0.000000,0.500000,0.000000,0.333333,0.083333,0.500000,0.083333,0.500000,0.000000,0.333333,0.083333,0.333333,0.083333,0.500000,0.083333,0.333333,0.166667,0.500000,0.166667,0.500000,0.083333,0.333333,0.166667,0.333333,0.166667,0.500000,0.166667,0.333333,0.250000,0.500000,0.250000,0.500000,0.166667,0.333333,0.250000,0.333333,0.250000,0.500000,0.250000,0.333333,0.333333,0.500000,0.333333,0.500000,0.250000,0.333333,0.333333,0.333333,0.333333,0.500000,0.333333,0.333333,0.416667,0.500000,0.416667,0.500000,0.333333,0.333333,0.416667,0.333333,0.416667,0.500000,0.416667,0.333333,0.500000,0.500000,0.500000,0.500000,0.416667,0.333333,0.500000,0.333333,0.500000,0.500000,0.500000,0.333333,0.583333,0.500000,0.583333,0.500000,0.500000,0.333333,0.583333,0.333333,0.583333,0.500000,0.583333,0.333333,0.666667,0.500000,0.666667,0.500000,0.583333,0.333333,0.666667,0.333333,0.666667,0.500000,0.666667,0.333333,0.750000,0.500000,0.750000,0.500000,0.666667,0.333333,0.750000,0.333333,0.750000,0.500000,0.750000,0.333333,0.833333,0.500000,0.833333,0.500000,0.750000,0.333333,0.833333,0.333333,0.833333,0.500000,0.833333,0.333333,0.916667,0.500000,0.916667,0.500000,0.833333,0.333333,0.916667,0.333333,0.916667,0.500000,0.916667,0.333333,1.000000,0.500000,1.000000,0.500000,0.916667,0.333333,1.000000,0.333333,0.000000,0.666667,0.000000,0.500000,0.083333,0.666667,0.083333,0.666667,0.000000,0.500000,0.083333,0.500000,0.083333,0.666667,0.083333,0.500000,0.166667,0.666667,0.166667,0.666667,0.083333,0.500000,0.166667,0.500000,0.166667,0.666667,0.166667,0.500000,0.250000,0.666667,0.250000,0.666667,0.166667,0.500000,0.250000,0.500000,0.250000,0.666667,0.250000,0.500000,0.333333,0.666667,0.333333,0.666667,0.250000,0.500000,0.333333,0.500000,0.333333,0.666667,0.333333,0.500000,0.416667,0.666667,0.416667,0.666667,0.333333,0.500000,0.416667,0.500000,0.416667,0.666667,0.416667,0.500000,0.500000,0.666667,0.500000,0.666667,0.416667,0.500000,0.500000,0.500000,0.500000,0.666667,0.500000,0.500000,0.583333,0.666667,0.583333,0.666667,0.500000,0.500000,0.583333,0.500000,0.583333,0.666667,0.583333,0.500000,0.666667,0.666667,0.666667,0.666667,0.583333,0.500000,0.666667,0.500000,0.666667,0.666667,0.666667,0.500000,0.750000,0.666667,0.750000,0.666667,0.666667,0.500000,0.750000,0.500000,0.750000,0.666667,0.750000,0.500000,0.833333,0.666667,0.833333,0.666667,0.750000,0.500000,0.833333,0.500000,0.833333,0.666667,0.833333,0.500000,0.916667,0.666667,0.916667,0.666667,0.833333,0.500000,0.916667,0.500000,0.916667,0.666667,0.916667,0.500000,1.000000,0.666667,1.000000,0.666667,0.916667,0.500000,1.000000,0.500000,0.000000,0.833333,0.000000,0.666667,0.083333,0.833333,0.083333,0.833333,0.000000,0.666667,0.083333,0.666667,0.083333,0.833333,0.083333,0.666667,0.166667,0.833333,0.166667,0.833333,0.083333,0.666667,0.166667,0.666667,0.166667,0.833333,0.166667,0.666667,0.250000,0.833333,0.250000,0.833333,0.166667,0.666667,0.250000,0.666667,0.250000,0.833333,0.250000,0.666667,0.333333,0.833333,0.333333,0.833333,0.250000,0.666667,0.333333,0.666667,0.333333,0.833333,0.333333,0.666667,0.416667,0.833333,0.416667,0.833333,0.333333,0.666667,0.416667,0.666667,0.416667,0.833333,0.416667,0.666667,0.500000,0.833333,0.500000,0.833333,0.416667,0.666667,0.500000,0.666667,0.500000,0.833333,0.500000,0.666667,0.583333,0.833333,0.583333,0.833333,0.500000,0.666667,0.583333,0.666667,0.583333,0.833333,0.583333,0.666667,0.666667,0.833333,0.666667,0.833333,0.583333,0.666667,0.666667,0.666667,0.666667,0.833333,0.666667,0.666667,0.750000,0.833333,0.750000,0.833333,0.666667,0.666667,0.750000,0.666667,0.750000,0.833333,0.750000,0.666667,0.833333,0.833333,0.833333,0.833333,0.750000,0.666667,0.833333,0.666667,0.833333,0.833333,0.833333,0.666667,0.916667,0.833333,0.916667,0.833333,0.833333,0.666667,0.916667,0.666667,0.916667,0.833333,0.916667,0.666667,1.000000,0.833333,1.000000,0.833333,0.916667,0.666667,1.000000,0.666667,0.083333,0.833333,0.000000,1.000000,0.000000,0.833333,0.166667,0.833333,0.083333,1.000000,0.083333,0.833333,0.250000,0.833333,0.166667,1.000000,0.166667,0.833333,0.333333,0.833333,0.250000,1.000000,0.250000,0.833333,0.416667,0.833333,0.333333,1.000000,0.333333,0.833333,0.500000,0.833333,0.416667,1.000000,0.416667,0.833333,0.583333,0.833333,0.500000,1.000000,0.500000,0.833333,0.666667,0.833333,0.583333,1.000000,0.583333,0.833333,0.750000,0.833333,0.666667,1.000000,0.666667,0.833333,0.833333,0.833333,0.750000,1.000000,0.750000,0.833333,0.916667,0.833333,0.833333,1.000000,0.833333,0.833333,1.000000,0.833333,0.916667,1.000000,0.916667,0.833333,] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /js/Player.js: -------------------------------------------------------------------------------- 1 | var Player = function(engine, camera, level){ 2 | var acc = 0.046875 * 3600.0; // Acceleration 3 | var air = 0.09375 * 3600.0; // Air Acceleration 4 | var dec = 0.5 * 3600.0; // Deceleration 5 | var max = 6 * 60.0; // Max speed 6 | var frc = 0.046875 * 3600.0; // Friction 7 | var grv = 0.21875 * 3600.0; // Gravity 8 | var jmp = 6.5 * 60.0; // Jump Stength 9 | var ljp = 4.0 * 60.0; // Low Jump Strength (for variable jump) 10 | var slp = 0.125 * 3600.0; // Slope Acceleration 11 | var mgs = 2.5 * 60.0; // Minimum Ground Speed to Stick on Walls 12 | var mrs = 1.03125 * 60.0; // Mininum Rolling Speed 13 | var mrr = 0.5 * 60.0; // Mininum Rolling Speed After Already Rolling 14 | var sru = 0.078125 * 60.0 * 60.0; 15 | var srd = 0.3125 * 60.0 * 60.0; 16 | 17 | var mesh = engine.resources.get("mesh/sonic.jsonmesh"); 18 | var spinball = engine.resources.get("mesh/spinball.jsonmesh"); 19 | var player = new engine.world.MeshEntity(mesh); 20 | var speed = $V([0,0,0]); 21 | var targetCameraAngle = Math.PI/2; 22 | var cameraAngle = Math.PI/2; 23 | var cameraDistance = 256+64; 24 | var cameraHeight = 64; 25 | var targetCameraHeight = 100; 26 | 27 | var onGround = false; 28 | var onLedge = false; 29 | var isHoldingJump = false; 30 | var cameraBoxPosition = player.position(); 31 | var cameraBoxWidth = 16; 32 | var cameraBoxHeight = 64; 33 | var cameraBoxYOffset = 32; // 128 - 96 34 | var cameraOffsetY = 16; // 128 - 112 35 | 36 | var animations = { 37 | stopped: { 38 | start: 0, 39 | end: 0, 40 | speed: 1 41 | }, 42 | walking: { 43 | start: 1, 44 | end: 8, 45 | speed: 10 46 | }, 47 | running: { 48 | start: 9, 49 | end: 12, 50 | speed: 20 51 | }, 52 | spinning: { 53 | start: 13, 54 | end: 13, 55 | speed: 0 56 | }, 57 | lookUp: { 58 | start: 14, 59 | end: 14, 60 | speed: 0 61 | }, 62 | spring: { 63 | start: 15, 64 | end: 15, 65 | speed: 0 66 | }, 67 | hit: { 68 | start: 16, 69 | end: 16, 70 | speed: 0 71 | }, 72 | crouchDown: { 73 | start: 17, 74 | end: 17, 75 | speed: 0 76 | }, 77 | hold: { 78 | start: 18, 79 | end: 18, 80 | speed: 0 81 | }, 82 | waiting: { 83 | start: 19, 84 | end: 20, 85 | speed: 3 86 | }, 87 | dying: { 88 | start: 21, 89 | end: 21, 90 | speed: 2 91 | }, 92 | balancing: { 93 | start: 22, 94 | end: 23, 95 | speed: 4 96 | }, 97 | pushing: { 98 | start: 24, 99 | end: 26, 100 | speed: 4 101 | }, 102 | breathing: { 103 | start: 28, 104 | end: 28, 105 | speed: 0 106 | } 107 | }; 108 | var currentAnimation = "stopped"; 109 | var lastAnimation = "stopped"; 110 | var animationTransform = Matrix.I(4); 111 | 112 | var SPEED_BASED_ANIMATION_GAP = 0.2; 113 | 114 | var timeWaiting = 0.0; 115 | var isPushing = false; 116 | 117 | var STATE_NORMAL = 1; 118 | var STATE_JUMPING = 2; 119 | var STATE_ROLLING = 3; 120 | var STATE_CROUCH_DOWN = 4; 121 | var STATE_SPINDASH = 5; 122 | var STATE_LOOK_UP = 6; 123 | var STATE_SPRING = 7; 124 | player.STATE_NORMAL = STATE_NORMAL; 125 | player.STATE_JUMPING = STATE_JUMPING; 126 | player.STATE_ROLLING = STATE_ROLLING; 127 | player.STATE_CROUCH_DOWN = STATE_CROUCH_DOWN; 128 | player.STATE_SPINDASH = STATE_SPINDASH; 129 | player.STATE_LOOK_UP = STATE_LOOK_UP; 130 | player.STATE_SPRING = STATE_SPRING; 131 | 132 | var state = STATE_NORMAL; 133 | 134 | player.state = function(newState) { 135 | if (newState !== undefined) { 136 | state = newState; 137 | } 138 | return state; 139 | } 140 | 141 | var spindashCharge = 0; 142 | 143 | var direction = $V([0,0,0]); 144 | 145 | var angle = 0.0; 146 | var angleOverride = false; 147 | 148 | camera.fov(35); 149 | // player.scale($V([0.20,0.41,0.20])); 150 | //player.scale($V([3,3,3])); 151 | 152 | var convertMotionToAir = function() { 153 | speed = player.rotation().make3x3().x(speed); 154 | onGround = false; 155 | } 156 | 157 | var convertMotionToGround = function() { 158 | speed = player.rotation().inverse().make3x3().x(speed); 159 | onGround = true; 160 | } 161 | 162 | var alignPlayerToNormal = function(normal, timeDelta) { 163 | if (speed.modulus() < mgs) { 164 | player.rotation(player.rotation().AlignYAxis(normal, 1.0 - Math.pow(0.0001, timeDelta))); 165 | } else { 166 | player.rotation(player.rotation().AlignYAxis(normal, 1.0)); 167 | } 168 | } 169 | 170 | var updateCamera = function(timeDelta) { 171 | 172 | if (engine.input.pressed("Q".charCodeAt(0))) 173 | targetCameraAngle += Math.PI/4; 174 | if (engine.input.pressed("W".charCodeAt(0))) 175 | targetCameraAngle -= Math.PI/4; 176 | 177 | cameraAngle -= (cameraAngle - targetCameraAngle) * 4.0 * timeDelta; 178 | cameraHeight -= (cameraHeight - (targetCameraHeight - 0.0 * Math.min(0, speed.elements[1]))) * 1.0 * timeDelta; 179 | 180 | var transformedSpeed = player.rotation().make3x3().x(speed).x(timeDelta); 181 | 182 | // Horizontal movement 183 | if (player.position().elements[0] > (cameraBoxPosition.elements[0] + cameraBoxWidth / 2)) { 184 | cameraBoxPosition.elements[0] += Math.min(player.position().elements[0] - (cameraBoxPosition.elements[0] + cameraBoxWidth / 2), 16 * 60 * timeDelta); 185 | } 186 | 187 | if (player.position().elements[0] < (cameraBoxPosition.elements[0] - cameraBoxWidth / 2)) { 188 | cameraBoxPosition.elements[0] += Math.max(player.position().elements[0] - (cameraBoxPosition.elements[0] - cameraBoxWidth / 2), -16 * 60 * timeDelta); 189 | } 190 | 191 | if (player.position().elements[2] > (cameraBoxPosition.elements[2] + cameraBoxWidth / 2)) { 192 | cameraBoxPosition.elements[2] += Math.min(player.position().elements[2] - (cameraBoxPosition.elements[2] + cameraBoxWidth / 2), 16 * 60 * timeDelta); 193 | } 194 | 195 | if (player.position().elements[2] < (cameraBoxPosition.elements[2] - cameraBoxWidth / 2)) { 196 | cameraBoxPosition.elements[2] += Math.max(player.position().elements[2] - (cameraBoxPosition.elements[2] - cameraBoxWidth / 2), -16 * 60 * timeDelta); 197 | } 198 | 199 | // Vertical movement 200 | if (!onGround) { 201 | if (player.position().elements[1] > cameraBoxPosition.elements[1] + cameraBoxHeight ) { 202 | cameraBoxPosition.elements[1] += Math.min(player.position().elements[1] - (cameraBoxPosition.elements[1] + cameraBoxHeight), 16 * 60 * timeDelta); 203 | } 204 | if (player.position().elements[1] < cameraBoxPosition.elements[1]) { 205 | cameraBoxPosition.elements[1] += Math.max(player.position().elements[1] - (cameraBoxPosition.elements[1]), - 16 * 60 * timeDelta); 206 | } 207 | } else { 208 | if (player.position().elements[1] > cameraBoxPosition.elements[1] + cameraBoxYOffset ) { 209 | if (Math.abs(transformedSpeed.elements[1]) > 6 * timeDelta) { 210 | // Fast catch up 211 | cameraBoxPosition.elements[1] += Math.min(player.position().elements[1] - (cameraBoxPosition.elements[1] + cameraBoxYOffset), 16 * 60 * timeDelta); 212 | } else { 213 | // Slow catch up 214 | cameraBoxPosition.elements[1] += Math.min(player.position().elements[1] - (cameraBoxPosition.elements[1] + cameraBoxYOffset), 6 * 60 * timeDelta); 215 | } 216 | } 217 | if (player.position().elements[1] < cameraBoxPosition.elements[1] + cameraBoxYOffset ) { 218 | if (Math.abs(transformedSpeed.elements[1]) > 6 * timeDelta) { 219 | // Fast catch up 220 | cameraBoxPosition.elements[1] += Math.max(player.position().elements[1] - (cameraBoxPosition.elements[1] + cameraBoxYOffset), -16 * 60 * timeDelta); 221 | } else { 222 | // Slow catch up 223 | cameraBoxPosition.elements[1] += Math.max(player.position().elements[1] - (cameraBoxPosition.elements[1] + cameraBoxYOffset), -6 * 60 * timeDelta); 224 | } 225 | } 226 | } 227 | 228 | camera.position(cameraBoxPosition.add($V([Math.cos(cameraAngle) * cameraDistance, cameraHeight + cameraOffsetY, Math.sin(cameraAngle) * cameraDistance]))); 229 | camera.rotation(Matrix.LookAt(camera.position(), cameraBoxPosition.add($V([0,cameraOffsetY,0]))).inverse()); 230 | } 231 | 232 | var handleControls = function(timeDelta) { 233 | 234 | timeWaiting += timeDelta; 235 | 236 | // Controls aligned to camera 237 | var canMove = true; 238 | var moveX = camera.rotation().x($V([1.0,0.0,0.0,0.0])).xyz(); 239 | var moveZ = camera.rotation().x($V([0.0,0.0,1.0,0.0])).xyz(); 240 | 241 | moveX.elements[1] = 0.0; 242 | moveZ.elements[1] = 0.0; 243 | 244 | moveX = moveX.toUnitVector(); 245 | moveZ = moveZ.toUnitVector(); 246 | 247 | direction = $V([0,0,0]); 248 | 249 | if (engine.input.heldLeft()) 250 | direction = direction.add(moveX.x(-1)); 251 | 252 | if (engine.input.heldRight()) 253 | direction = direction.add(moveX.x(1)); 254 | 255 | if (engine.input.heldUp()) 256 | direction = direction.add(moveZ.x(-1)); 257 | 258 | if (engine.input.heldDown()) 259 | direction = direction.add(moveZ.x(1)); 260 | 261 | if (state == STATE_CROUCH_DOWN || state == STATE_SPINDASH || state == STATE_LOOK_UP) { 262 | canMove = false; 263 | } 264 | 265 | if (state != STATE_ROLLING) { 266 | if (direction.modulus() > 0 && canMove) { 267 | 268 | timeWaiting = 0; 269 | 270 | // The player is holding the arrow keys on some direction 271 | direction = direction.toUnitVector(); 272 | 273 | if ($V([speed.elements[0], 0, speed.elements[2]]).dot(direction) >= 0) { 274 | // The direction pressed is similar to the direction Sonic's facing 275 | 276 | if (speed.xz().modulus() > 2 && !angleOverride) { 277 | angle = Math.atan2(speed.elements[0], speed.elements[2]); 278 | // console.log(angle); 279 | } 280 | 281 | // Store the old speed (for max speed check) 282 | var oldSpeedModulus = speed.xz().modulus(); 283 | 284 | if (onGround) { 285 | // Ground acc 286 | speed = speed.add((direction) .x (acc * timeDelta)); 287 | } else { 288 | // Air acc 289 | speed = speed.add((direction) .x (air * timeDelta)); 290 | } 291 | // Store the new speed 292 | var newSpeedModulus = speed.xz().modulus(); 293 | 294 | if (newSpeedModulus > max) { 295 | // We're faster than the max speed 296 | if (oldSpeedModulus < max) { 297 | // We got here by "natural means". Get back to the max speed, but allow handling 298 | speed.elements[0] = (speed.elements[0] / newSpeedModulus) * max; 299 | speed.elements[2] = (speed.elements[2] / newSpeedModulus) * max; 300 | } else { 301 | // We got here by "supernatural means" (e.g. a Spring). 302 | if (newSpeedModulus > oldSpeedModulus) { 303 | // We're faster than we were before: Get back to as fast as we already were, but allow handling 304 | speed.elements[0] = (speed.elements[0] / newSpeedModulus) * oldSpeedModulus; 305 | speed.elements[2] = (speed.elements[2] / newSpeedModulus) * oldSpeedModulus; 306 | } 307 | } 308 | } 309 | 310 | } else { 311 | // The direction pressed is opposite to the direction Sonic's facing 312 | if (onGround) { 313 | // Ground deceleration 314 | speed = speed.add((direction) .x (dec * timeDelta)); 315 | } else { 316 | // Air deceleration ( = the same as acceleration) 317 | speed = speed.add((direction) .x (air * timeDelta)); 318 | } 319 | } 320 | } else { 321 | // The player is not holding on any direction 322 | if (onGround) { 323 | // We're on the floor. Enters friction. 324 | direction = $V([-speed.elements[0], 0, -speed.elements[2]]).toUnitVector(); 325 | speed = speed.add(direction.x(frc * timeDelta)); 326 | 327 | // Stop when speed is lower than friction 328 | if (speed.modulus() < (frc * timeDelta)) { 329 | speed = $V([0,0,0]); 330 | } 331 | } else { 332 | // no friction on air (we calculate air drag later) 333 | } 334 | } 335 | } else { 336 | // Rolling physics 337 | 338 | // Reduced friction. 339 | var frictionDirection = $V([-speed.elements[0], 0, -speed.elements[2]]).toUnitVector(); 340 | speed = speed.add(frictionDirection.x(frc / 2 * timeDelta)); 341 | 342 | // Stop when speed is lower than friction 343 | if (speed.modulus() < (frc * timeDelta)) { 344 | speed = $V([0,0,0]); 345 | } else { 346 | // Deceleration 347 | if ($V([speed.elements[0], 0, speed.elements[2]]).dot(direction) < 0) { 348 | speed = speed.add((direction) .x (dec / 4 * timeDelta)); 349 | } 350 | } 351 | } 352 | 353 | // Jump/Spindash/Spindash Rev 354 | if (onGround && engine.input.pressed("X".charCodeAt(0))) { 355 | if (state == STATE_NORMAL || state == STATE_ROLLING) { 356 | isHoldingJump = true; 357 | speed.elements[1] = jmp; 358 | onGround = false; 359 | timeWaiting = 0; 360 | state = STATE_JUMPING; 361 | } else if (state == STATE_CROUCH_DOWN) { 362 | state = STATE_SPINDASH; 363 | spindashCharge = 0; 364 | } else if (state == STATE_SPINDASH) { 365 | spindashCharge += 2; 366 | if (spindashCharge > 8) { 367 | spindashCharge = 8; 368 | } 369 | } 370 | } 371 | 372 | // Variable jump 373 | if (!engine.input.held("X".charCodeAt(0)) && isHoldingJump) { 374 | isHoldingJump = false; 375 | if (speed.elements[1] > ljp) { 376 | speed.elements[1] = ljp; 377 | } 378 | } 379 | 380 | // gratuitous speed 381 | if (engine.input.pressed("P".charCodeAt(0))) { 382 | if (speed.elements[0] != 0 || speed.elements[2] != 0) { 383 | var length = speed.xz().modulus(); 384 | speed.elements[0] *= 12 * 60 / length; 385 | speed.elements[2] *= 12 * 60 / length; 386 | } 387 | } 388 | 389 | // Look Up 390 | if (engine.input.held("S".charCodeAt(0))) { 391 | timeWaiting = 0; 392 | if (onGround && state == STATE_NORMAL) { 393 | if (speed.xz().modulus() < mrs) { 394 | state = STATE_LOOK_UP; 395 | } 396 | } 397 | } else { 398 | if (state == STATE_LOOK_UP) { 399 | state = STATE_NORMAL; 400 | } 401 | } 402 | 403 | // Roll/Crouch 404 | if (engine.input.held(" ".charCodeAt(0))) { 405 | timeWaiting = 0; 406 | // Only roll or crouch if we're on ground, and on normal state 407 | if (onGround && state == STATE_NORMAL || state == STATE_CROUCH_DOWN) { 408 | // We're moving. Roll 409 | if (speed.xz().modulus() > mrs) { 410 | if (state != STATE_ROLLING) { 411 | state = STATE_ROLLING; 412 | } 413 | } else { 414 | // We're not moving. Crouch. 415 | if (state != STATE_SPINDASH) { 416 | state = STATE_CROUCH_DOWN; 417 | } 418 | } 419 | } 420 | } else { 421 | // We've released the crouch button 422 | if (state == STATE_CROUCH_DOWN) { 423 | // We're crouching. Get back to normal. 424 | state = STATE_NORMAL; 425 | } 426 | if (state == STATE_SPINDASH) { 427 | // We're spindashing. Roll at the right speed. 428 | state = STATE_ROLLING; 429 | speed.elements[0] = (8 + Math.floor(Math.floor(spindashCharge) / 2)) * 60 * Math.sin(angle); 430 | speed.elements[2] = (8 + Math.floor(Math.floor(spindashCharge) / 2)) * 60 * Math.cos(angle); 431 | } 432 | } 433 | 434 | spindashCharge *= Math.pow(0.148834266, timeDelta); 435 | 436 | if (engine.input.pressed("R".charCodeAt(0))) { 437 | ringLoss(); 438 | } 439 | } 440 | 441 | var handleGround = function(timeDelta) { 442 | var normal = player.rotation().make3x3().x($V([0,1,0])); 443 | 444 | // Slope Acceleration 445 | if (state != STATE_ROLLING) { 446 | speed = speed.add($V([normal.elements[0] * slp * timeDelta, 0, normal.elements[2] * slp * timeDelta])) 447 | } else { 448 | if ($V([normal.elements[0], 0, normal.elements[2]]).dot($V([speed.elements[0], 0, speed.elements[2]])) >= 0) { 449 | // Rolling Downhill 450 | speed = speed.add($V([normal.elements[0] * srd * timeDelta, 0, normal.elements[2] * srd * timeDelta])) 451 | } else { 452 | // Rolling Uphill 453 | speed = speed.add($V([normal.elements[0] * sru * timeDelta, 0, normal.elements[2] * sru * timeDelta])) 454 | } 455 | } 456 | 457 | // Fall off from the walls if we're too slow 458 | if (normal.dot($V([0,1,0])) <= 0.0 && speed.xz().modulus() < mgs) { 459 | convertMotionToAir(); 460 | player.rotation(Matrix.I(4)); 461 | return; 462 | } 463 | 464 | // Stop rolling if we're too slow. 465 | if (speed.modulus() <= mrr && state == STATE_ROLLING) { 466 | state = STATE_NORMAL; 467 | } 468 | } 469 | 470 | var handleAir = function(timeDelta) { 471 | // Add gravity to the player's y speed 472 | speed.elements[1] -= grv * timeDelta; 473 | 474 | if (speed.elements[1] > 0 && speed.elements[1] < 4 * 60) { 475 | // We're going upwards, but no that much 476 | if (speed.xz().modulus() > 0.125 * 60.0) { 477 | // We're going fast. Air drag kicks in 478 | speed.elements[0] *= Math.pow(0.148834266, timeDelta); 479 | speed.elements[2] *= Math.pow(0.148834266, timeDelta); 480 | } 481 | } 482 | 483 | 484 | if (state == STATE_ROLLING) { 485 | state = STATE_JUMPING; 486 | } else if (state == STATE_SPRING) { 487 | if (speed.elements[1] < 0) { 488 | state = STATE_NORMAL; 489 | } 490 | } 491 | } 492 | 493 | var handleCollisions = function(timeDelta) { 494 | 495 | var didPush = false; 496 | for (var i = 0; i < 2*Math.PI; i+= 0.25*Math.PI) { 497 | var sensorDirection = $V([Math.sin(i), 0, Math.cos(i)]); 498 | var intersect = level.rayIntersect(player.position().add($V([0,0,-4])), player.rotation().make3x3().x(sensorDirection), 15, 0); 499 | var speedComponent = sensorDirection.xz().dot(speed.xz()); 500 | 501 | if (isFinite(intersect.distance)) { 502 | if (speedComponent >= 0) { 503 | speed.elements[0] -= speedComponent * sensorDirection.elements[0]; 504 | speed.elements[2] -= speedComponent * sensorDirection.elements[2]; 505 | } 506 | if (direction.xz().dot(sensorDirection.xz()) > 0.8) { 507 | angle = i; 508 | didPush = true; 509 | } 510 | player.position(player.position().subtract(player.rotation().make3x3().x(sensorDirection.x(15-intersect.distance)))); 511 | } 512 | } 513 | if (isPushing && !didPush) { 514 | angleOverride = false; 515 | } 516 | isPushing = didPush; 517 | if (isPushing) { 518 | angleOverride = true; 519 | } 520 | 521 | 522 | var modeMatrix = player.rotation().make3x3();//.makeModeMatrix(); 523 | var modeVector = modeMatrix.x($V([0, -1, 0])); 524 | var intersectA1 = level.rayIntersect(player.position().add($V([-9,0,-9])), modeVector, 36, 0); 525 | var intersectA2 = level.rayIntersect(player.position().add($V([-9,0,9])), modeVector, 36, 0); 526 | var intersectB1 = level.rayIntersect(player.position().add($V([9,0,-9])), modeVector, 36, 0); 527 | var intersectB2 = level.rayIntersect(player.position().add($V([9,0,9])), modeVector, 36, 0); 528 | 529 | var minIntersectA = ((intersectA1.distance < intersectA2.distance)?intersectA1:intersectA2); 530 | var minIntersectB = ((intersectB1.distance < intersectB2.distance)?intersectB1:intersectB2); 531 | var minIntersect = ((minIntersectA.distance < minIntersectB.distance)?minIntersectA:minIntersectB) 532 | /* var minIntersect = { 533 | distance: 0.0, 534 | normal: $V([0,0,0]) 535 | }*/ 536 | minIntersect.normal = $V([0,0,0]); 537 | var count = 0; 538 | if (isFinite(intersectA1.distance)) { 539 | // minIntersect.distance += intersectA1.distance; 540 | minIntersect.normal = minIntersect.normal.add(intersectA1.normal); 541 | count++; 542 | } 543 | if (isFinite(intersectA2.distance)) { 544 | // minIntersect.distance += intersectA2.distance; 545 | minIntersect.normal = minIntersect.normal.add(intersectA2.normal); 546 | count++; 547 | } 548 | if (isFinite(intersectB1.distance)) { 549 | // minIntersect.distance += intersectB1.distance; 550 | minIntersect.normal = minIntersect.normal.add(intersectB1.normal); 551 | count++; 552 | } 553 | if (isFinite(intersectB2.distance)) { 554 | // minIntersect.distance += intersectB2.distance; 555 | minIntersect.normal = minIntersect.normal.add(intersectB2.normal); 556 | count++; 557 | } 558 | // minIntersect.distance /= count; 559 | minIntersect.normal = minIntersect.normal.x(1/count).toUnitVector(); 560 | 561 | if (count != 4) { 562 | onLedge = true; 563 | } else { 564 | onLedge = false; 565 | } 566 | 567 | if (isFinite(minIntersect.distance)) { 568 | if ((minIntersect.distance <= 20 || onGround) && speed.elements[1] <= 0) { 569 | 570 | player.position(player.position().add(player.rotation().make3x3().x($V([0,20-minIntersect.distance,0])))); 571 | if (minIntersect.normal.dot(player.rotation().make3x3().x($V([0,1,0]))) > 0.0) { 572 | alignPlayerToNormal(minIntersect.normal, timeDelta); 573 | } 574 | 575 | if (!onGround) { 576 | convertMotionToGround(); 577 | } 578 | 579 | if (state == STATE_JUMPING || state == STATE_SPRING) { 580 | state = STATE_NORMAL; 581 | } 582 | 583 | speed.elements[1] = 0.0; 584 | } 585 | } else { 586 | convertMotionToAir(); 587 | player.rotation(Matrix.I(4)); 588 | } 589 | 590 | var modeVector = modeMatrix.x($V([0, 1, 0])); 591 | var intersectA1 = level.rayIntersect(player.position().add($V([-9,0,-9])), modeVector, 36, 0); 592 | var intersectA2 = level.rayIntersect(player.position().add($V([-9,0,9])), modeVector, 36, 0); 593 | var intersectB1 = level.rayIntersect(player.position().add($V([9,0,-9])), modeVector, 36, 0); 594 | var intersectB2 = level.rayIntersect(player.position().add($V([9,0,9])), modeVector, 36, 0); 595 | 596 | var minIntersectA = ((intersectA1.distance < intersectA2.distance)?intersectA1:intersectA2); 597 | var minIntersectB = ((intersectB1.distance < intersectB2.distance)?intersectB1:intersectB2); 598 | var minIntersect = ((minIntersectA.distance < minIntersectB.distance)?minIntersectA:minIntersectB) 599 | 600 | if (isFinite(minIntersect.distance)) { 601 | if (minIntersect.distance <= 20 && speed.elements[1] >= 0) { 602 | player.position(player.position().subtract(player.rotation().make3x3().x($V([0,20-minIntersect.distance,0])))); 603 | speed.elements[1] = 0.0; 604 | } 605 | } 606 | } 607 | 608 | var repositionPlayer = function(timeDelta) { 609 | player.position(player.position().add(player.rotation().make3x3().x(speed).x(timeDelta))); 610 | } 611 | 612 | var updateAnimations = function(timeDelta) { 613 | if (currentAnimation != lastAnimation) { 614 | player.frame = animations[currentAnimation].start; 615 | lastAnimation = currentAnimation; 616 | } 617 | 618 | player.frame += animations[currentAnimation].speed * timeDelta; 619 | 620 | if (player.frame >= animations[currentAnimation].end + 1) { 621 | player.frame -= animations[currentAnimation].end - animations[currentAnimation].start + 1; 622 | } 623 | } 624 | 625 | var handleAnimations = function(timeDelta) { 626 | // if (speed.xz().modulus() > frc * timeDelta) { 627 | // angle = Math.atan2(speed.elements[0], speed.elements[2]); 628 | // } 629 | var speedModulus = speed.xz().modulus(); 630 | if (onGround) { 631 | if (state == STATE_ROLLING) { 632 | currentAnimation = "spinning"; 633 | } else if (state == STATE_CROUCH_DOWN) { 634 | currentAnimation = "crouchDown"; 635 | } else if (state == STATE_LOOK_UP) { 636 | currentAnimation = "lookUp"; 637 | } else if (state == STATE_SPINDASH) { 638 | currentAnimation = "spinning"; 639 | } else { 640 | if (speedModulus <= SPEED_BASED_ANIMATION_GAP) { 641 | if (onLedge) { 642 | currentAnimation = "balancing"; 643 | } else { 644 | if (isPushing) { 645 | currentAnimation = "pushing"; 646 | } else { 647 | if (timeWaiting > 5) { 648 | currentAnimation = "waiting"; 649 | } else { 650 | currentAnimation = "stopped"; 651 | } 652 | } 653 | } 654 | } else if (speedModulus < max - SPEED_BASED_ANIMATION_GAP) { 655 | if (isPushing) { 656 | currentAnimation = "pushing"; 657 | } else { 658 | currentAnimation = "walking"; 659 | } 660 | } else { 661 | currentAnimation = "running"; 662 | } 663 | } 664 | } else { 665 | if (state == STATE_JUMPING) { 666 | currentAnimation = "spinning"; 667 | } else if (state == STATE_SPRING) { 668 | currentAnimation = "spring"; 669 | } else { 670 | if (speedModulus <= SPEED_BASED_ANIMATION_GAP) { 671 | currentAnimation = "stopped"; 672 | } else if (speedModulus < max - SPEED_BASED_ANIMATION_GAP) { 673 | currentAnimation = "walking"; 674 | } else { 675 | currentAnimation = "running"; 676 | } 677 | } 678 | } 679 | updateAnimations(timeDelta); 680 | 681 | if (currentAnimation == "spinning") { 682 | animationTransform = Matrix.RotationX(-engine.world.time()*40.0).ensure4x4(); 683 | if (Math.floor(engine.world.time() * 40) % 2 == 0) { 684 | spinball.visible = true; 685 | } else { 686 | spinball.visible = false; 687 | } 688 | } else if (currentAnimation == "spring") { 689 | animationTransform = Matrix.RotationZ(-engine.world.time()*10.0).ensure4x4(); 690 | spinball.visible = false; 691 | } else { 692 | spinball.visible = false; 693 | animationTransform = Matrix.I(4); 694 | } 695 | } 696 | 697 | var ringLoss = function() { 698 | var rotation = Matrix.RotationY(-cameraAngle+Math.PI/2); 699 | var speed = 4 * 60.0; 700 | var angle = 101.25 / 360.0 * 2 * Math.PI; 701 | var n = false; 702 | for (var t = 0; t<32; t++) { 703 | var ring = Ring(engine, player, level); 704 | ring.position(player.position()); 705 | if (n) { 706 | ring.speed(rotation.x($V([Math.sin(angle)*speed, -Math.cos(angle)*speed, -4]))); 707 | angle += 22.5 / 360 * 2 * Math.PI; 708 | } else { 709 | ring.speed(rotation.x($V([-Math.sin(angle)*speed, Math.cos(angle)*speed, +4]))); 710 | } 711 | engine.world.add(ring); 712 | n = !n; 713 | if (t == 15) { 714 | speed = 2 * 60; 715 | angle = 101.25 / 360 * 2 * Math.PI; 716 | } 717 | } 718 | } 719 | 720 | var superUpdate = player.update; 721 | 722 | player.update = function(timeDelta) { 723 | 724 | superUpdate(timeDelta); 725 | 726 | handleControls(timeDelta); 727 | 728 | if (onGround) { 729 | handleGround(timeDelta); 730 | } else { 731 | handleAir(timeDelta); 732 | } 733 | 734 | repositionPlayer(timeDelta); 735 | 736 | handleCollisions(timeDelta); 737 | 738 | handleAnimations(timeDelta); 739 | 740 | if (player.position().elements[1] < -100) { 741 | // player.position(player.position().add($V([0,1000,0]))); 742 | speed = $V([speed.elements[0],500,speed.elements[2]]); 743 | } 744 | 745 | updateCamera(timeDelta); 746 | 747 | } 748 | 749 | var superRender = player.render; 750 | player.render = function(renderParams) { 751 | 752 | var rotation = player.rotation(); 753 | 754 | player.rotation(rotation.x(Matrix.RotationY(Math.PI + angle).ensure4x4()).x(animationTransform)); 755 | 756 | renderParams.modelView = player.modelView(); 757 | renderParams.normalModelView = player.normalModelView(); 758 | if (spinball.visible) { 759 | spinball.render(renderParams, 0); 760 | } else { 761 | mesh.render(renderParams, Math.floor(player.frame)); 762 | } 763 | player.rotation(rotation); 764 | } 765 | 766 | player.speed = function(newSpeed) { 767 | if (newSpeed !== undefined) { 768 | speed = newSpeed; 769 | } 770 | return speed; 771 | } 772 | 773 | //player.isStatic = true; 774 | 775 | return player; 776 | } -------------------------------------------------------------------------------- /js/GraphicsEngine.js: -------------------------------------------------------------------------------- 1 | var GraphicsEngine = function(engine, canvas) { 2 | var graphicsEngine = {}; 3 | var gl; 4 | var glParams = {}//{premultipliedAlpha: false, alpha: false}; 5 | var activeShader = null; 6 | 7 | console.log("Initializing WebGl..."); 8 | 9 | // Try to initialize WebGL 10 | if (!(gl = canvas.getContext("webgl", glParams))) { 11 | // Initialization failed, try experimental mode 12 | if (gl = canvas.getContext("experimental-webgl", glParams)) { 13 | // Tell the user WebGL is running in experimental mode 14 | console.info("WebGL is running in experimental mode. Performance and stability may suffer.") 15 | } else { 16 | // Experimental mode failed, WebGL not supported or disabled 17 | throw new Error("Could not initialize WebGL. Are you running a modern browser with WebGL enabled?"); 18 | } 19 | } 20 | 21 | gl.enable(gl.DEPTH_TEST); 22 | // gl.enable(gl.CULL_FACE); 23 | // gl.cullFace(gl.FRONT); 24 | gl.depthFunc(gl.LESS); 25 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 26 | gl.enable(gl.BLEND); 27 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 28 | 29 | // expose the WebGL context 30 | graphicsEngine.gl = gl; 31 | 32 | graphicsEngine.clear = function(){ 33 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 34 | } 35 | 36 | graphicsEngine.clearColor = function(){ 37 | gl.clear(gl.COLOR_BUFFER_BIT); 38 | } 39 | 40 | graphicsEngine.clearDepth = function(){ 41 | gl.clear(gl.DEPTH_BUFFER_BIT); 42 | } 43 | 44 | 45 | graphicsEngine.width = function() { 46 | return canvas.width; 47 | } 48 | 49 | graphicsEngine.height = function() { 50 | return canvas.height; 51 | } 52 | 53 | graphicsEngine.aspectRatio = function() { 54 | return canvas.width / canvas.height; 55 | } 56 | 57 | graphicsEngine.Shader = function(shaderData, shaderName) { 58 | var shader = {} 59 | 60 | // Compile the vertex shader 61 | console.log("Compiling vertex shader..."); 62 | var vertexShader = gl.createShader(gl.VERTEX_SHADER); 63 | gl.shaderSource(vertexShader, shaderData.vertexShader); 64 | gl.compileShader(vertexShader); 65 | 66 | if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { 67 | throw new Error("Error compiling vertex shader: " + gl.getShaderInfoLog(vertexShader)); 68 | } 69 | 70 | // Compile the fragment shader 71 | console.log("Compiling fragment shader..."); 72 | var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); 73 | gl.shaderSource(fragmentShader, shaderData.fragmentShader); 74 | gl.compileShader(fragmentShader); 75 | 76 | if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { 77 | throw new Error("Error compiling fragment shader: " + gl.getShaderInfoLog(fragmentShader)); 78 | } 79 | 80 | // Link the program 81 | console.log("Linking shader program..."); 82 | var program = gl.createProgram(); 83 | gl.attachShader(program, fragmentShader); 84 | gl.attachShader(program, vertexShader); 85 | gl.linkProgram(program); 86 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 87 | throw new Error("Error linking shader program: " + gl.getProgramInfoLog(program)); 88 | } 89 | 90 | // Obtain uniform locations 91 | console.log("Extracting uniform locations..."); 92 | var uniforms = {}; 93 | shaderData.uniforms.forEach(function(uniform){ 94 | uniforms[uniform] = gl.getUniformLocation(program, uniform); 95 | if (!uniforms[uniform]) { 96 | console.warn("Nonexistent or unused uniform in the description of shader " + shaderName + ": " + uniform); 97 | } 98 | }); 99 | 100 | // Obtain attribute locations 101 | console.log("Extracting attribute locations..."); 102 | var attributes = {}; 103 | shaderData.attributes.forEach(function(attribute){ 104 | attributes[attribute] = gl.getAttribLocation(program, attribute); 105 | if (attributes[attribute] == -1) { 106 | console.warn("Nonexistent or unused attribute in the description of shader " + shaderName + ": " + attribute); 107 | } 108 | }); 109 | 110 | // Use the program 111 | shader.use = function() { 112 | if (activeShader != shader) { 113 | gl.useProgram(program); 114 | activeShader = shader; 115 | // console.log("ShaderSwap"); 116 | } 117 | } 118 | 119 | // Get a uniform location 120 | shader.uniform = function(name) { 121 | if (!uniforms[name]) 122 | throw new Error("Shader " + shaderName + " has no uniform named " + name); 123 | return uniforms[name]; 124 | } 125 | 126 | // Get an attribute location 127 | shader.attribute = function(name) { 128 | if (attributes[name] === undefined) 129 | throw new Error("Shader " + shaderName + " has no attribute named " + name); 130 | return attributes[name]; 131 | } 132 | 133 | shader.hasAttribute = function(name) { 134 | return attributes[name] != null; 135 | } 136 | 137 | shader.hasUniform = function(name) { 138 | return uniforms[name] != null; 139 | } 140 | return shader; 141 | } 142 | 143 | graphicsEngine.Mesh = function(meshData) { 144 | var mesh = {}; 145 | var surfaces = []; 146 | var __uniformArray = [null]; 147 | 148 | // bake the surfaces 149 | meshData.surfaces.forEach(function(surface){ 150 | var bakedSurface = {}; 151 | bakedSurface.name = surface.name; 152 | bakedSurface.frames = []; 153 | bakedSurface.shader = surface.shader; 154 | engine.resources.load(bakedSurface.shader); 155 | bakedSurface.samplers = surface.samplers; 156 | for (var name in bakedSurface.samplers) { 157 | engine.resources.load(bakedSurface.samplers[name]); 158 | } 159 | 160 | // No frames (single frame mode) 161 | if (!surface.frames) { 162 | // Create a single frame for baking 163 | surface.frames = [{normals: surface.normals, vertices: surface.vertices}]; 164 | } 165 | 166 | // bake the frames 167 | console.log("Baking vertices and normals..."); 168 | surface.frames.forEach(function(frame){ 169 | var bakedFrame = {}; 170 | 171 | // bake the vertices 172 | bakedFrame.vertices = new Float32Array(frame.vertices); 173 | bakedFrame.vertexBuffer = gl.createBuffer(); 174 | gl.bindBuffer(gl.ARRAY_BUFFER, bakedFrame.vertexBuffer); 175 | gl.bufferData(gl.ARRAY_BUFFER, bakedFrame.vertices, gl.STATIC_DRAW); 176 | 177 | // bake the normals 178 | bakedFrame.normals = new Float32Array(frame.normals); 179 | bakedFrame.normalBuffer = gl.createBuffer(); 180 | gl.bindBuffer(gl.ARRAY_BUFFER, bakedFrame.normalBuffer); 181 | gl.bufferData(gl.ARRAY_BUFFER, bakedFrame.normals, gl.STATIC_DRAW); 182 | 183 | // add the newly baked frame to the surface 184 | bakedSurface.frames.push(bakedFrame); 185 | }); 186 | 187 | // bake the texture coordinates 188 | console.log("Baking texture coordinates..."); 189 | bakedSurface.textureCoordinates = new Float32Array(surface.textureCoordinates) 190 | bakedSurface.textureCoordinateBuffer = gl.createBuffer(); 191 | gl.bindBuffer(gl.ARRAY_BUFFER, bakedSurface.textureCoordinateBuffer); 192 | gl.bufferData(gl.ARRAY_BUFFER, bakedSurface.textureCoordinates, gl.STATIC_DRAW); 193 | 194 | // add the newly baked surface to the mesh's surface list 195 | surfaces.push(bakedSurface); 196 | surfaces[bakedSurface.name] = bakedSurface; 197 | }); 198 | 199 | if (surfaces.length > 0) { 200 | mesh.render = function(renderParams, frame) { 201 | for (var i = 0; i < surfaces.length; i++) { 202 | mesh.renderSurface(renderParams, i, frame); 203 | } 204 | } 205 | } else { 206 | mesh.render = function(renderParams, frame) { 207 | mesh.renderSurface(renderParams, 0, frame); 208 | } 209 | } 210 | 211 | mesh.renderSurface = function(renderParams, surfaceIndex, frame) { 212 | 213 | var uniformsList = renderParams.uniforms; 214 | var surface = surfaces[surfaceIndex]; 215 | var shader = renderParams.shader ? renderParams.shader : engine.resources.get(surface.shader); 216 | 217 | shader.use(); 218 | 219 | if (!renderParams.cameraView.flat) { 220 | renderParams.cameraView.flat = renderParams.cameraView.flatten(); 221 | } 222 | gl.uniformMatrix4fv(shader.uniform("u_cameraView"), false, renderParams.cameraView.flat); 223 | if (!renderParams.modelView.flat) { 224 | renderParams.modelView.flat = renderParams.modelView.flatten(); 225 | } 226 | gl.uniformMatrix4fv(shader.uniform("u_modelView"), false, renderParams.modelView.flat); 227 | if (!renderParams.projection.flat) { 228 | renderParams.projection.flat = renderParams.projection.flatten(); 229 | } 230 | if (shader.hasUniform("u_projection")) { 231 | gl.uniformMatrix4fv(shader.uniform("u_projection"), false, renderParams.projection.flat); 232 | } 233 | 234 | if (shader.hasUniform("u_normalModelView")) { 235 | /*try { 236 | var normalModelViewFlat = 237 | (renderParams.modelView) 238 | .inverse() 239 | .transpose() 240 | .flatten(); 241 | gl.uniformMatrix4fv( 242 | shader.uniform("u_normalModelView"), 243 | false, 244 | normalModelViewFlat 245 | ); 246 | } catch (e) { 247 | // We could not invert the matrix for some reason (singular?)*/ 248 | if (!(renderParams.normalModelView.flat)) { 249 | renderParams.normalModelView.flat = renderParams.normalModelView.flatten(); 250 | } 251 | // console.log(renderParams.normalModelView.inspect()); 252 | gl.uniformMatrix4fv( 253 | shader.uniform("u_normalModelView"), 254 | false, 255 | renderParams.normalModelView.flatten() 256 | ); 257 | // } 258 | } 259 | 260 | if (shader.hasUniform("u_normalCameraView")) { 261 | try { 262 | var normalModelViewFlat = 263 | (renderParams.cameraView) 264 | .inverse() 265 | .transpose() 266 | .flatten(); 267 | gl.uniformMatrix4fv( 268 | shader.uniform("u_normalCameraView"), 269 | false, 270 | normalModelViewFlat 271 | ); 272 | } catch (e) { 273 | // We could not invert the matrix for some reason (singular?) 274 | gl.uniformMatrix4fv( 275 | shader.uniform("u_normalCameraView"), 276 | false, 277 | ((renderParams.cameraView)).flatten() 278 | ); 279 | } 280 | } 281 | 282 | //var textureNumber = 0; 283 | var unitsUsed = 0x0; 284 | for (var sampler in surface.samplers) { 285 | var texture = engine.resources.get(surface.samplers[sampler]); 286 | var textureUnit; 287 | if (unitsUsed & (1 << texture.lastUnit)) { 288 | textureUnit = 0; 289 | while(unitsUsed & (1 << textureUnit)) { 290 | textureUnit++; 291 | } 292 | } else { 293 | textureUnit = (texture.lastUnit << 0); 294 | } 295 | texture.lastUnit = textureUnit; 296 | unitsUsed |= (1 << textureUnit) 297 | // console.log(textureUnit); 298 | 299 | gl.activeTexture(gl.TEXTURE0 + textureUnit); 300 | gl.bindTexture(gl.TEXTURE_2D, texture); 301 | if (shader.hasUniform(sampler)) { 302 | gl.uniform1i(shader.uniform(sampler), textureUnit) 303 | } 304 | //textureNumber++; 305 | } 306 | /* for (var sampler in surface.samplers) { 307 | if (textureNumber < 2) { 308 | gl.activeTexture(gl.TEXTURE0 + textureNumber); 309 | gl.bindTexture(gl.TEXTURE_2D, engine.resources.get(surface.samplers[sampler])); 310 | if (shader.hasUniform(sampler)) { 311 | gl.uniform1i(shader.uniform(sampler), textureNumber) 312 | console.log(textureNumber); 313 | } 314 | lastTexture = engine.resources.get(surface.samplers[sampler]); 315 | textureNumber+=2; 316 | } 317 | }*/ 318 | /*for (var sampler in surface.samplers) { 319 | gl.activeTexture(gl.TEXTURE0 + textureNumber); 320 | gl.bindTexture(gl.TEXTURE_2D, engine.resources.get(surface.samplers[sampler])); 321 | if (shader.hasUniform(sampler)) { 322 | gl.uniform1i(shader.uniform(sampler), textureNumber) 323 | } 324 | lastTexture = engine.resources.get(surface.samplers[sampler]); 325 | textureNumber++; 326 | }*/ 327 | 328 | //gl.activeTexture(gl.TEXTURE0 + textureNumber); 329 | //gl.bindTexture(gl.TEXTURE_2D, lastTexture); 330 | //if (shader.hasUniform("u_diffuseMap")) { 331 | // gl.uniform1i(shader.uniform("u_diffuseMap"), textureNumber) 332 | //} 333 | //textureNumber++; 334 | 335 | /* 336 | gl.activeTexture(gl.TEXTURE0 + textureNumber); 337 | gl.bindTexture(gl.TEXTURE_2D, lastTexture); 338 | gl.uniform1i(shader.uniform("u_diffuseMap"), textureNumber) 339 | textureNumber++;*/ 340 | 341 | 342 | //if (uniforms) { 343 | //if (!(uniforms instanceof Array)) { 344 | // __uniformArray[0] = uniforms; 345 | // uniforms = __uniformArray; 346 | //} 347 | for (var uniformsListIndex in uniformsList) { 348 | var uniforms = uniformsList[uniformsListIndex]; 349 | for (var uniformName in uniforms) { 350 | var uniform = uniforms[uniformName]; 351 | if (!shader.hasUniform(uniformName)) 352 | continue; 353 | switch(true) { 354 | case uniform instanceof Matrix: 355 | //if (!uniform.isSquare()) { 356 | // throw new Error("Uniform matrix " + uniformName + " is not square."); 357 | //} 358 | //if (uniform.rows() > 4) { 359 | // throw new Error("Uniform matrix " + uniformName + " is too large."); 360 | //} 361 | if (!uniform.flat) { 362 | uniform.flat = uniform.flatten(); 363 | } 364 | gl["uniformMatrix"+uniform.elements.length+"fv"](shader.uniform(uniformName), false, uniform.flat); 365 | break; 366 | case uniform instanceof Vector: 367 | //if (uniform.dimensions() > 4) { 368 | // throw new Error("Uniform vector " + uniformName + " is too large."); 369 | //} 370 | if (!uniform.flat) { 371 | uniform.flat = uniform.flatten(); 372 | } 373 | gl["uniform"+uniform.elements.length+"fv"](shader.uniform(uniformName), uniform.flat); 374 | break; 375 | case (typeof uniform) == "number" || uniform instanceof Number: 376 | gl.uniform1f(shader.uniform(uniformName), uniform); 377 | break; 378 | default: 379 | if (!uniform) 380 | break; 381 | var textureUnit; 382 | if (unitsUsed & (1 << uniform.lastUnit)) { 383 | textureUnit = 0; 384 | while(unitsUsed & (1 << textureUnit)) { 385 | textureUnit++; 386 | } 387 | } else { 388 | textureUnit = (uniform.lastUnit << 0); 389 | } 390 | uniform.lastUnit = textureUnit; 391 | unitsUsed |= (1 << textureUnit) 392 | 393 | gl.activeTexture(gl.TEXTURE0 + textureUnit); 394 | gl.bindTexture(gl.TEXTURE_2D, uniform); 395 | gl.uniform1i(shader.uniform(uniformName), textureUnit) 396 | break; 397 | } 398 | } 399 | }; 400 | // console.log(textureNumber); 401 | //} 402 | 403 | gl.bindBuffer(gl.ARRAY_BUFFER, surface.frames[frame].vertexBuffer); 404 | gl.enableVertexAttribArray(shader.attribute("a_position")); 405 | gl.vertexAttribPointer(shader.attribute("a_position"), 3, gl.FLOAT, false, 0, 0); 406 | 407 | if (shader.hasAttribute("a_normal")) { 408 | gl.bindBuffer(gl.ARRAY_BUFFER, surface.frames[frame].normalBuffer); 409 | gl.enableVertexAttribArray(shader.attribute("a_normal")); 410 | gl.vertexAttribPointer(shader.attribute("a_normal"), 3, gl.FLOAT, false, 0, 0); 411 | } 412 | 413 | if (shader.hasAttribute("a_texCoords")) { 414 | gl.bindBuffer(gl.ARRAY_BUFFER, surface.textureCoordinateBuffer); 415 | gl.vertexAttribPointer(shader.attribute("a_texCoords"), 2, gl.FLOAT, false, 0, 0); 416 | gl.enableVertexAttribArray(shader.attribute("a_texCoords")); 417 | } 418 | 419 | gl.drawArrays(gl.TRIANGLES, 0, surface.frames[frame].vertices.length / 3); 420 | 421 | /*for (var i = 0; i < textureNumber; i++) { 422 | gl.activeTexture(gl.TEXTURE0 + i); 423 | gl.bindTexture(gl.TEXTURE_2D, null); 424 | }*/ 425 | 426 | // gl.flush(); 427 | } 428 | 429 | mesh.rayIntersect = function(surfaceIndex, point, direction, maxDist, minDist) { 430 | var Ox = point.elements[0]; // Point 431 | var Oy = point.elements[1]; 432 | var Oz = point.elements[2]; 433 | var Dx = direction.elements[0]; // Direction 434 | var Dy = direction.elements[1]; 435 | var Dz = direction.elements[2]; 436 | var surface = surfaces[surfaceIndex]; 437 | var vertices = surface.frames[0].vertices; 438 | var numVertices = vertices.length; 439 | var closestIntersect = Infinity; 440 | var closestNx = Infinity; 441 | var closestNy = Infinity; 442 | var closestNz = Infinity; 443 | for (var i = 0; i < numVertices; i+= 9) { 444 | var Ax = vertices[i]; // Vertex A 445 | var Ay = vertices[i+1]; 446 | var Az = vertices[i+2]; 447 | var Bx = vertices[i+3]; // Vertex B 448 | var By = vertices[i+4]; 449 | var Bz = vertices[i+5]; 450 | var Cx = vertices[i+6]; // Vertex C 451 | var Cy = vertices[i+7]; 452 | var Cz = vertices[i+8]; 453 | var ABx = Bx - Ax; // B - A 454 | var ABy = By - Ay; 455 | var ABz = Bz - Az; 456 | var ACx = Cx - Ax; // C - A 457 | var ACy = Cy - Ay; 458 | var ACz = Cz - Az; 459 | var Nx = -((ABy * ACz) - (ABz * ACy)); // Triangle Normal (Cross Product) 460 | var Ny = ((ABx * ACz) - (ABz * ACx)); 461 | var Nz = -((ABx * ACy) - (ABy * ACx)); 462 | var lowerDot = Dx * Nx + Dy * Ny + Dz * Nz; 463 | if (lowerDot >= 0) { 464 | continue; 465 | } 466 | var AOx = Ox - Ax; // O - A 467 | var AOy = Oy - Ay; 468 | var AOz = Oz - Az; 469 | var upperDot = AOx * Nx + AOy * Ny + AOz * Nz; // Point - Plane Distance 470 | var distance = -(upperDot/lowerDot); 471 | var Px, Py, Pz; // Point of intersection with the triangle plane 472 | var u, v; // Barycentric coordinates (w is implied) 473 | 474 | if (distance > maxDist) { // No need to find a collision this far 475 | continue; 476 | } 477 | if (distance < minDist) { // No need to find a collision this close 478 | continue; 479 | } 480 | if (distance > closestIntersect) { // No need to find a collision further 481 | continue; 482 | } 483 | 484 | // Figure out the dominant axis of the triangle normal 485 | var absNx = Nx > 0? Nx : -Nx; 486 | var absNy = Ny > 0? Ny : -Ny; 487 | var absNz = Nz > 0? Nz : -Nz; 488 | if (absNx > absNy) { 489 | if (absNx > absNz) { 490 | // x is the dominant normal axis 491 | Py = (Oy + distance * Dy) - Ay; 492 | Pz = (Oz + distance * Dz) - Az; 493 | u = (Pz*ACy - Py*ACz) / (ABz * ACy - ABy * ACz); 494 | if (u < 0) { 495 | continue; 496 | } 497 | v = (Pz*ABy - Py*ABz) / (ACz * ABy - ACy * ABz); 498 | } else { 499 | // z is the dominant normal axis 500 | Px = (Ox + distance * Dx) - Ax; 501 | Py = (Oy + distance * Dy) - Ay; 502 | u = (Py*ACx - Px*ACy) / (ABy * ACx - ABx * ACy); 503 | if (u < 0) { 504 | continue; 505 | } 506 | v = (Py*ABx - Px*ABy) / (ACy * ABx - ACx * ABy); 507 | } 508 | } else { 509 | if (absNy > absNz) { 510 | // y is the dominant axis 511 | Px = (Ox + distance * Dx) - Ax; 512 | Pz = (Oz + distance * Dz) - Az; 513 | u = (Pz*ACx - Px*ACz) / (ABz * ACx - ABx * ACz); 514 | if (u < 0) { 515 | continue; 516 | } 517 | v = (Pz*ABx - Px*ABz) / (ACz * ABx - ACx * ABz); 518 | } else { 519 | // z is the dominant axis 520 | Px = (Ox + distance * Dx) - Ax; 521 | Py = (Oy + distance * Dy) - Ay; 522 | u = (Py*ACx - Px*ACy) / (ABy * ACx - ABx * ACy); 523 | if (u < 0) { 524 | continue; 525 | } 526 | v = (Py*ABx - Px*ABy) / (ACy * ABx - ACx * ABy); 527 | } 528 | } 529 | 530 | if (v < 0) { 531 | continue; 532 | } 533 | if (u + v > 1) { 534 | continue; 535 | } 536 | // We are inside the triangle 537 | closestIntersect = distance; 538 | closestNx = Nx; 539 | closestNy = Ny; 540 | closestNz = Nz; 541 | 542 | // For some reason, this line of code is required for the function 543 | // to work on TraceMonkey (!) 544 | window.moreMagic = true; 545 | } 546 | 547 | return {distance: closestIntersect, normal: $V([closestNx, closestNy, closestNz])}; 548 | } 549 | 550 | return mesh; 551 | } 552 | 553 | graphicsEngine.Level = function(levelData) { 554 | var scaleFactor = 128.0; 555 | var scaleMatrix = $V([scaleFactor, scaleFactor, scaleFactor, 1.0]).toDiagonalMatrix(); 556 | var level = {} 557 | engine.resources.load(levelData.mesh); 558 | var shadowMap = null; 559 | var size = $V([levelData.data[0][0].length * scaleFactor, 560 | levelData.data.length * scaleFactor, 561 | levelData.data[0].length * scaleFactor]); 562 | 563 | var generateShadowMap = function() { 564 | var LIGHTMAP_WIDTH = 128; 565 | var LIGHTMAP_HEIGHT = 128; 566 | var canvas = document.createElement("canvas"); 567 | canvas.width = LIGHTMAP_WIDTH; 568 | canvas.height = LIGHTMAP_HEIGHT; 569 | var height = size.elements[1]; 570 | var depth = size.elements[2]; 571 | var width = size.elements[0]; 572 | var context = canvas.getContext("2d"); 573 | context.fillStyle = "white"; 574 | context.fillRect(0,0,LIGHTMAP_WIDTH,LIGHTMAP_HEIGHT); 575 | for (var x = 0; x < LIGHTMAP_WIDTH; x++) { 576 | for (var z = 0; z < LIGHTMAP_HEIGHT; z++) { 577 | var targetX = width * (x) / LIGHTMAP_WIDTH; 578 | var targetZ = depth * (z) / LIGHTMAP_HEIGHT; 579 | var intersect = level.rayIntersect($V([targetX,height,targetZ]), $V([0,-1,0]), height, 0); 580 | if (isFinite(intersect.distance)) { 581 | context.fillStyle = "rgba(0,0,0," + (intersect.distance / height) + ")" 582 | } else { 583 | context.fillStyle = "rgba(0,0,0,1.0)" 584 | } 585 | context.fillRect(x, z, 1, 1); 586 | } 587 | } 588 | // document.body.appendChild(canvas); 589 | // canvas.style.position = "absolute"; 590 | // canvas.style.top = "0"; 591 | // canvas.style.left = "0"; 592 | 593 | shadowMap = gl.createTexture(); 594 | gl.bindTexture(gl.TEXTURE_2D, shadowMap); 595 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas); 596 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 597 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 598 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 599 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 600 | } 601 | 602 | level.render = function(renderParams, cameraCenter) { 603 | //console.log(renderParams.modelView.eql(Matrix.I(4))); 604 | if (!shadowMap) 605 | generateShadowMap(); 606 | // renderParams.uniforms.push({ 607 | // u_lightMap: lightMap, 608 | // u_levelSize: size 609 | // }); 610 | var mesh = engine.resources.get(levelData.mesh); 611 | var modelView = renderParams.modelView; 612 | var minX = Math.floor(cameraCenter.elements[0] / scaleFactor) - 6; 613 | var minY = Math.floor(cameraCenter.elements[1] / scaleFactor) - 2; 614 | var minZ = Math.floor(cameraCenter.elements[2] / scaleFactor) - 6; 615 | var maxX = minX + 12; 616 | var maxY = minY + 5; 617 | var maxZ = minZ + 12; 618 | for (var y = minY; y < maxY; y ++) { 619 | if (levelData.data[y] === undefined) continue; 620 | for (var z = minZ; z < maxZ; z ++) { 621 | if (levelData.data[y][z] === undefined) continue; 622 | for (var x = minX; x < maxX; x ++) { 623 | if (levelData.data[y][z][x] === undefined) continue; 624 | if (levelData.data[y][z][x] !== null) { 625 | renderParams.modelView = modelView.x(scaleMatrix).x(Matrix.Translation($V([x,y,z]))); 626 | mesh.renderSurface(renderParams, levelData.data[y][z][x], 0); 627 | } 628 | } 629 | } 630 | } 631 | // renderParams.uniforms.pop(); 632 | } 633 | 634 | level.levelSize = function() { 635 | return size; 636 | } 637 | 638 | level.shadowMap = function() { 639 | return shadowMap; 640 | } 641 | 642 | level.rayIntersect = function(point, direction, maxDist, minDist) { 643 | var mesh = engine.resources.get(levelData.mesh); 644 | var Px = Math.floor(point.elements[0] / scaleFactor); 645 | var Py = Math.floor(point.elements[1] / scaleFactor); 646 | var Pz = Math.floor(point.elements[2] / scaleFactor); 647 | var P2x = Math.floor((point.elements[0] + direction.elements[0] * maxDist) / scaleFactor); 648 | var P2y = Math.floor((point.elements[1] + direction.elements[1] * maxDist) / scaleFactor); 649 | var P2z = Math.floor((point.elements[2] + direction.elements[2] * maxDist) / scaleFactor); 650 | if (P2x < Px) { 651 | var tmp = P2x; 652 | P2x = Px; 653 | Px = tmp; 654 | } 655 | if (P2y < Py) { 656 | var tmp = P2y; 657 | P2y = Py; 658 | Py = tmp; 659 | } 660 | if (P2z < Pz) { 661 | var tmp = P2z; 662 | P2z = Pz; 663 | Pz = tmp; 664 | } 665 | 666 | var numNeighboors = Math.ceil(maxDist/scaleFactor); 667 | var closestIntersect = Infinity; 668 | var closestResult = {distance: Infinity, normal: $V([Infinity, Infinity, Infinity])};; 669 | for (var y = Py; y <= P2y; y++) { 670 | if (levelData.data[y]) { 671 | for (var z = Pz; z <= P2z; z++) { 672 | if (levelData.data[y][z]) { 673 | for (var x = Px; x <= P2x; x++) { 674 | var surfaceIndex = levelData.data[y][z][x]; 675 | if (surfaceIndex != null) { 676 | var intersect = mesh.rayIntersect(surfaceIndex, $V([point.elements[0]/scaleFactor - x, point.elements[1]/scaleFactor - y, point.elements[2]/scaleFactor - z]), direction, maxDist/scaleFactor, minDist/scaleFactor) 677 | if (intersect.distance < closestIntersect) { 678 | closestResult = intersect; 679 | } 680 | } 681 | } 682 | } 683 | } 684 | } 685 | } 686 | closestResult.distance *= scaleFactor; 687 | return closestResult; 688 | } 689 | 690 | return level; 691 | } 692 | 693 | graphicsEngine.RenderTarget = function(width, height){ 694 | var renderTarget = {} 695 | var framebuffer; 696 | var target; 697 | var depth; 698 | var autoSize; 699 | 700 | var createFrameBuffer = function(width, height) { 701 | framebuffer = gl.createFramebuffer(); 702 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 703 | 704 | target = gl.createTexture(); 705 | gl.bindTexture(gl.TEXTURE_2D, target); 706 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 707 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 708 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 709 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 710 | // gl.generateMipmap(gl.TEXTURE_2D); 711 | // var textureStorage = new Int8Array(width * height * 4); 712 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 713 | 714 | depth = gl.createRenderbuffer(); 715 | gl.bindRenderbuffer(gl.RENDERBUFFER, depth); 716 | gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height); 717 | 718 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0); 719 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depth); 720 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 721 | gl.bindTexture(gl.TEXTURE_2D, null); 722 | gl.bindRenderbuffer(gl.RENDERBUFFER, null); 723 | } 724 | 725 | if (width !== undefined) { 726 | createFrameBuffer(width, height); 727 | autoSize = false; 728 | } else { 729 | autoSize = true; 730 | } 731 | 732 | renderTarget.activate = function() { 733 | if (autoSize) { 734 | if (width != canvas.width || height != canvas.height) { 735 | createFrameBuffer(canvas.width, canvas.height); 736 | width = canvas.width; 737 | height = canvas.height; 738 | } 739 | } 740 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 741 | gl.viewport(0, 0, width, height); 742 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) 743 | } 744 | 745 | renderTarget.deactivate = function() { 746 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 747 | gl.viewport(0, 0, canvas.width, canvas.height); 748 | } 749 | 750 | renderTarget.texture = function() { 751 | return target; 752 | } 753 | 754 | var vertices = null, texCoords, vertexBuffer, texCoordsBuffer, shader; 755 | 756 | renderTarget.renderToScreen = function() { 757 | if (!vertices) { 758 | vertices = new Float32Array( 759 | [-1.0, -1.0, 0.0, 760 | -1.0, 1.0, 0.0, 761 | 1.0, -1.0, 0.0, 762 | 763 | 1.0, 1.0, 0.0, 764 | -1.0, 1.0, 0.0, 765 | 1.0, -1.0, 0.0 766 | ]); 767 | texCoords = new Float32Array( 768 | [0.0, 0.0, 769 | 0.0, 1.0, 770 | 1.0, 0.0, 771 | 1.0, 1.0, 772 | 0.0, 1.0, 773 | 1.0, 0.0 774 | ]); 775 | 776 | vertexBuffer = gl.createBuffer(); 777 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 778 | gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 779 | 780 | texCoordsBuffer = gl.createBuffer(); 781 | gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsBuffer); 782 | gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); 783 | 784 | shader = engine.resources.get("shader/glow.jsonshader"); 785 | } 786 | 787 | shader.use(); 788 | 789 | gl.activeTexture(gl.TEXTURE0); 790 | gl.bindTexture(gl.TEXTURE_2D, target); 791 | gl.uniform1i(shader.uniform("u_image"), 0); 792 | 793 | gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsBuffer); 794 | gl.vertexAttribPointer(shader.attribute("a_texCoords"), 2, gl.FLOAT, false, 0, 0); 795 | gl.enableVertexAttribArray(shader.attribute("a_texCoords")); 796 | 797 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 798 | gl.vertexAttribPointer(shader.attribute("a_position"), 3, gl.FLOAT, false, 0, 0); 799 | gl.enableVertexAttribArray(shader.attribute("a_position")); 800 | 801 | gl.drawArrays(gl.TRIANGLES, 0, 6); 802 | } 803 | 804 | renderTarget.aspectRatio = function() { 805 | return width / height; 806 | } 807 | 808 | return renderTarget; 809 | } 810 | 811 | return graphicsEngine; 812 | } -------------------------------------------------------------------------------- /mesh/spinball.jsonmesh: -------------------------------------------------------------------------------- 1 | { 2 | "format": "JSON3D 1.0", 3 | "surfaces": [ 4 | { 5 | "name": "Sphere01", 6 | "shader": "shader/sonic.jsonshader", 7 | "samplers": { "u_diffuseMap": "texture/sonic.png" }, 8 | "frames": [ 9 | { 10 | "vertices": [13.8,0.0,0.0,12.7,0.0,6.1,12.7,2.3,5.7,13.8,0.0,0.0,12.7,2.3,5.7,12.7,4.3,4.3,13.8,0.0,0.0,12.7,4.3,4.3,12.7,5.7,2.3,13.8,0.0,0.0,12.7,5.7,2.3,12.7,6.1,0.0,13.8,0.0,0.0,12.7,6.1,0.0,12.7,5.7,-2.3,13.8,0.0,0.0,12.7,5.7,-2.3,12.7,4.3,-4.3,13.8,0.0,0.0,12.7,4.3,-4.3,12.7,2.3,-5.7,13.8,0.0,0.0,12.7,2.3,-5.7,12.7,0.0,-6.1,13.8,0.0,0.0,12.7,0.0,-6.1,12.7,-2.3,-5.7,13.8,0.0,0.0,12.7,-2.3,-5.7,12.7,-4.3,-4.3,13.8,0.0,0.0,12.7,-4.3,-4.3,12.7,-5.7,-2.3,13.8,0.0,0.0,12.7,-5.7,-2.3,12.7,-6.1,0.0,13.8,0.0,0.0,12.7,-6.1,0.0,12.7,-5.7,2.3,13.8,0.0,0.0,12.7,-5.7,2.3,12.7,-4.3,4.3,13.8,0.0,0.0,12.7,-4.3,4.3,12.7,-2.3,5.7,13.8,0.0,0.0,12.7,-2.3,5.7,12.7,0.0,6.1,12.7,0.0,6.1,9.7,0.0,11.3,9.7,4.3,10.4,12.7,0.0,6.1,9.7,4.3,10.4,12.7,2.3,5.7,12.7,2.3,5.7,9.7,4.3,10.4,9.7,8.0,8.0,12.7,2.3,5.7,9.7,8.0,8.0,12.7,4.3,4.3,12.7,4.3,4.3,9.7,8.0,8.0,9.7,10.4,4.3,12.7,4.3,4.3,9.7,10.4,4.3,12.7,5.7,2.3,12.7,5.7,2.3,9.7,10.4,4.3,9.7,11.3,0.0,12.7,5.7,2.3,9.7,11.3,0.0,12.7,6.1,0.0,12.7,6.1,0.0,9.7,11.3,0.0,9.7,10.4,-4.3,12.7,6.1,0.0,9.7,10.4,-4.3,12.7,5.7,-2.3,12.7,5.7,-2.3,9.7,10.4,-4.3,9.7,8.0,-8.0,12.7,5.7,-2.3,9.7,8.0,-8.0,12.7,4.3,-4.3,12.7,4.3,-4.3,9.7,8.0,-8.0,9.7,4.3,-10.4,12.7,4.3,-4.3,9.7,4.3,-10.4,12.7,2.3,-5.7,12.7,2.3,-5.7,9.7,4.3,-10.4,9.7,0.0,-11.3,12.7,2.3,-5.7,9.7,0.0,-11.3,12.7,0.0,-6.1,12.7,0.0,-6.1,9.7,0.0,-11.3,9.7,-4.3,-10.4,12.7,0.0,-6.1,9.7,-4.3,-10.4,12.7,-2.3,-5.7,12.7,-2.3,-5.7,9.7,-4.3,-10.4,9.7,-8.0,-8.0,12.7,-2.3,-5.7,9.7,-8.0,-8.0,12.7,-4.3,-4.3,12.7,-4.3,-4.3,9.7,-8.0,-8.0,9.7,-10.4,-4.3,12.7,-4.3,-4.3,9.7,-10.4,-4.3,12.7,-5.7,-2.3,12.7,-5.7,-2.3,9.7,-10.4,-4.3,9.7,-11.3,0.0,12.7,-5.7,-2.3,9.7,-11.3,0.0,12.7,-6.1,0.0,12.7,-6.1,0.0,9.7,-11.3,0.0,9.7,-10.4,4.3,12.7,-6.1,0.0,9.7,-10.4,4.3,12.7,-5.7,2.3,12.7,-5.7,2.3,9.7,-10.4,4.3,9.7,-8.0,8.0,12.7,-5.7,2.3,9.7,-8.0,8.0,12.7,-4.3,4.3,12.7,-4.3,4.3,9.7,-8.0,8.0,9.7,-4.3,10.4,12.7,-4.3,4.3,9.7,-4.3,10.4,12.7,-2.3,5.7,12.7,-2.3,5.7,9.7,-4.3,10.4,9.7,0.0,11.3,12.7,-2.3,5.7,9.7,0.0,11.3,12.7,0.0,6.1,9.7,0.0,11.3,5.3,0.0,14.8,5.3,5.7,13.7,9.7,0.0,11.3,5.3,5.7,13.7,9.7,4.3,10.4,9.7,4.3,10.4,5.3,5.7,13.7,5.3,10.4,10.4,9.7,4.3,10.4,5.3,10.4,10.4,9.7,8.0,8.0,9.7,8.0,8.0,5.3,10.4,10.4,5.3,13.7,5.7,9.7,8.0,8.0,5.3,13.7,5.7,9.7,10.4,4.3,9.7,10.4,4.3,5.3,13.7,5.7,5.3,14.8,0.0,9.7,10.4,4.3,5.3,14.8,0.0,9.7,11.3,0.0,9.7,11.3,0.0,5.3,14.8,0.0,5.3,13.7,-5.7,9.7,11.3,0.0,5.3,13.7,-5.7,9.7,10.4,-4.3,9.7,10.4,-4.3,5.3,13.7,-5.7,5.3,10.4,-10.4,9.7,10.4,-4.3,5.3,10.4,-10.4,9.7,8.0,-8.0,9.7,8.0,-8.0,5.3,10.4,-10.4,5.3,5.7,-13.7,9.7,8.0,-8.0,5.3,5.7,-13.7,9.7,4.3,-10.4,9.7,4.3,-10.4,5.3,5.7,-13.7,5.3,0.0,-14.8,9.7,4.3,-10.4,5.3,0.0,-14.8,9.7,0.0,-11.3,9.7,0.0,-11.3,5.3,0.0,-14.8,5.3,-5.7,-13.7,9.7,0.0,-11.3,5.3,-5.7,-13.7,9.7,-4.3,-10.4,9.7,-4.3,-10.4,5.3,-5.7,-13.7,5.3,-10.4,-10.4,9.7,-4.3,-10.4,5.3,-10.4,-10.4,9.7,-8.0,-8.0,9.7,-8.0,-8.0,5.3,-10.4,-10.4,5.3,-13.7,-5.7,9.7,-8.0,-8.0,5.3,-13.7,-5.7,9.7,-10.4,-4.3,9.7,-10.4,-4.3,5.3,-13.7,-5.7,5.3,-14.8,0.0,9.7,-10.4,-4.3,5.3,-14.8,0.0,9.7,-11.3,0.0,9.7,-11.3,0.0,5.3,-14.8,0.0,5.3,-13.7,5.7,9.7,-11.3,0.0,5.3,-13.7,5.7,9.7,-10.4,4.3,9.7,-10.4,4.3,5.3,-13.7,5.7,5.3,-10.4,10.4,9.7,-10.4,4.3,5.3,-10.4,10.4,9.7,-8.0,8.0,9.7,-8.0,8.0,5.3,-10.4,10.4,5.3,-5.7,13.7,9.7,-8.0,8.0,5.3,-5.7,13.7,9.7,-4.3,10.4,9.7,-4.3,10.4,5.3,-5.7,13.7,5.3,0.0,14.8,9.7,-4.3,10.4,5.3,0.0,14.8,9.7,0.0,11.3,5.3,0.0,14.8,0.0,0.0,16.0,0.0,6.1,14.8,5.3,0.0,14.8,0.0,6.1,14.8,5.3,5.7,13.7,5.3,5.7,13.7,0.0,6.1,14.8,0.0,11.3,11.3,5.3,5.7,13.7,0.0,11.3,11.3,5.3,10.4,10.4,5.3,10.4,10.4,0.0,11.3,11.3,0.0,14.8,6.1,5.3,10.4,10.4,0.0,14.8,6.1,5.3,13.7,5.7,5.3,13.7,5.7,0.0,14.8,6.1,0.0,16.0,0.0,5.3,13.7,5.7,0.0,16.0,0.0,5.3,14.8,0.0,5.3,14.8,0.0,0.0,16.0,0.0,0.0,14.8,-6.1,5.3,14.8,0.0,0.0,14.8,-6.1,5.3,13.7,-5.7,5.3,13.7,-5.7,0.0,14.8,-6.1,0.0,11.3,-11.3,5.3,13.7,-5.7,0.0,11.3,-11.3,5.3,10.4,-10.4,5.3,10.4,-10.4,0.0,11.3,-11.3,0.0,6.1,-14.8,5.3,10.4,-10.4,0.0,6.1,-14.8,5.3,5.7,-13.7,5.3,5.7,-13.7,0.0,6.1,-14.8,0.0,0.0,-16.0,5.3,5.7,-13.7,0.0,0.0,-16.0,5.3,0.0,-14.8,5.3,0.0,-14.8,0.0,0.0,-16.0,0.0,-6.1,-14.8,5.3,0.0,-14.8,0.0,-6.1,-14.8,5.3,-5.7,-13.7,5.3,-5.7,-13.7,0.0,-6.1,-14.8,0.0,-11.3,-11.3,5.3,-5.7,-13.7,0.0,-11.3,-11.3,5.3,-10.4,-10.4,5.3,-10.4,-10.4,0.0,-11.3,-11.3,0.0,-14.8,-6.1,5.3,-10.4,-10.4,0.0,-14.8,-6.1,5.3,-13.7,-5.7,5.3,-13.7,-5.7,0.0,-14.8,-6.1,0.0,-16.0,0.0,5.3,-13.7,-5.7,0.0,-16.0,0.0,5.3,-14.8,0.0,5.3,-14.8,0.0,0.0,-16.0,0.0,0.0,-14.8,6.1,5.3,-14.8,0.0,0.0,-14.8,6.1,5.3,-13.7,5.7,5.3,-13.7,5.7,0.0,-14.8,6.1,0.0,-11.3,11.3,5.3,-13.7,5.7,0.0,-11.3,11.3,5.3,-10.4,10.4,5.3,-10.4,10.4,0.0,-11.3,11.3,0.0,-6.1,14.8,5.3,-10.4,10.4,0.0,-6.1,14.8,5.3,-5.7,13.7,5.3,-5.7,13.7,0.0,-6.1,14.8,0.0,0.0,16.0,5.3,-5.7,13.7,0.0,0.0,16.0,5.3,0.0,14.8,0.0,0.0,16.0,-5.3,0.0,14.8,-5.3,5.7,13.7,0.0,0.0,16.0,-5.3,5.7,13.7,0.0,6.1,14.8,0.0,6.1,14.8,-5.3,5.7,13.7,-5.3,10.4,10.4,0.0,6.1,14.8,-5.3,10.4,10.4,0.0,11.3,11.3,0.0,11.3,11.3,-5.3,10.4,10.4,-5.3,13.7,5.7,0.0,11.3,11.3,-5.3,13.7,5.7,0.0,14.8,6.1,0.0,14.8,6.1,-5.3,13.7,5.7,-5.3,14.8,0.0,0.0,14.8,6.1,-5.3,14.8,0.0,0.0,16.0,0.0,0.0,16.0,0.0,-5.3,14.8,0.0,-5.3,13.7,-5.7,0.0,16.0,0.0,-5.3,13.7,-5.7,0.0,14.8,-6.1,0.0,14.8,-6.1,-5.3,13.7,-5.7,-5.3,10.4,-10.4,0.0,14.8,-6.1,-5.3,10.4,-10.4,0.0,11.3,-11.3,0.0,11.3,-11.3,-5.3,10.4,-10.4,-5.3,5.7,-13.7,0.0,11.3,-11.3,-5.3,5.7,-13.7,0.0,6.1,-14.8,0.0,6.1,-14.8,-5.3,5.7,-13.7,-5.3,0.0,-14.8,0.0,6.1,-14.8,-5.3,0.0,-14.8,0.0,0.0,-16.0,0.0,0.0,-16.0,-5.3,0.0,-14.8,-5.3,-5.7,-13.7,0.0,0.0,-16.0,-5.3,-5.7,-13.7,0.0,-6.1,-14.8,0.0,-6.1,-14.8,-5.3,-5.7,-13.7,-5.3,-10.4,-10.4,0.0,-6.1,-14.8,-5.3,-10.4,-10.4,0.0,-11.3,-11.3,0.0,-11.3,-11.3,-5.3,-10.4,-10.4,-5.3,-13.7,-5.7,0.0,-11.3,-11.3,-5.3,-13.7,-5.7,0.0,-14.8,-6.1,0.0,-14.8,-6.1,-5.3,-13.7,-5.7,-5.3,-14.8,0.0,0.0,-14.8,-6.1,-5.3,-14.8,0.0,0.0,-16.0,0.0,0.0,-16.0,0.0,-5.3,-14.8,0.0,-5.3,-13.7,5.7,0.0,-16.0,0.0,-5.3,-13.7,5.7,0.0,-14.8,6.1,0.0,-14.8,6.1,-5.3,-13.7,5.7,-5.3,-10.4,10.4,0.0,-14.8,6.1,-5.3,-10.4,10.4,0.0,-11.3,11.3,0.0,-11.3,11.3,-5.3,-10.4,10.4,-5.3,-5.7,13.7,0.0,-11.3,11.3,-5.3,-5.7,13.7,0.0,-6.1,14.8,0.0,-6.1,14.8,-5.3,-5.7,13.7,-5.3,0.0,14.8,0.0,-6.1,14.8,-5.3,0.0,14.8,0.0,0.0,16.0,-5.3,0.0,14.8,-9.7,0.0,11.3,-9.7,4.3,10.4,-5.3,0.0,14.8,-9.7,4.3,10.4,-5.3,5.7,13.7,-5.3,5.7,13.7,-9.7,4.3,10.4,-9.7,8.0,8.0,-5.3,5.7,13.7,-9.7,8.0,8.0,-5.3,10.4,10.4,-5.3,10.4,10.4,-9.7,8.0,8.0,-9.7,10.4,4.3,-5.3,10.4,10.4,-9.7,10.4,4.3,-5.3,13.7,5.7,-5.3,13.7,5.7,-9.7,10.4,4.3,-9.7,11.3,0.0,-5.3,13.7,5.7,-9.7,11.3,0.0,-5.3,14.8,0.0,-5.3,14.8,0.0,-9.7,11.3,0.0,-9.7,10.4,-4.3,-5.3,14.8,0.0,-9.7,10.4,-4.3,-5.3,13.7,-5.7,-5.3,13.7,-5.7,-9.7,10.4,-4.3,-9.7,8.0,-8.0,-5.3,13.7,-5.7,-9.7,8.0,-8.0,-5.3,10.4,-10.4,-5.3,10.4,-10.4,-9.7,8.0,-8.0,-9.7,4.3,-10.4,-5.3,10.4,-10.4,-9.7,4.3,-10.4,-5.3,5.7,-13.7,-5.3,5.7,-13.7,-9.7,4.3,-10.4,-9.7,0.0,-11.3,-5.3,5.7,-13.7,-9.7,0.0,-11.3,-5.3,0.0,-14.8,-5.3,0.0,-14.8,-9.7,0.0,-11.3,-9.7,-4.3,-10.4,-5.3,0.0,-14.8,-9.7,-4.3,-10.4,-5.3,-5.7,-13.7,-5.3,-5.7,-13.7,-9.7,-4.3,-10.4,-9.7,-8.0,-8.0,-5.3,-5.7,-13.7,-9.7,-8.0,-8.0,-5.3,-10.4,-10.4,-5.3,-10.4,-10.4,-9.7,-8.0,-8.0,-9.7,-10.4,-4.3,-5.3,-10.4,-10.4,-9.7,-10.4,-4.3,-5.3,-13.7,-5.7,-5.3,-13.7,-5.7,-9.7,-10.4,-4.3,-9.7,-11.3,0.0,-5.3,-13.7,-5.7,-9.7,-11.3,0.0,-5.3,-14.8,0.0,-5.3,-14.8,0.0,-9.7,-11.3,0.0,-9.7,-10.4,4.3,-5.3,-14.8,0.0,-9.7,-10.4,4.3,-5.3,-13.7,5.7,-5.3,-13.7,5.7,-9.7,-10.4,4.3,-9.7,-8.0,8.0,-5.3,-13.7,5.7,-9.7,-8.0,8.0,-5.3,-10.4,10.4,-5.3,-10.4,10.4,-9.7,-8.0,8.0,-9.7,-4.3,10.4,-5.3,-10.4,10.4,-9.7,-4.3,10.4,-5.3,-5.7,13.7,-5.3,-5.7,13.7,-9.7,-4.3,10.4,-9.7,0.0,11.3,-5.3,-5.7,13.7,-9.7,0.0,11.3,-5.3,0.0,14.8,-9.7,0.0,11.3,-12.7,0.0,6.1,-12.7,2.3,5.7,-9.7,0.0,11.3,-12.7,2.3,5.7,-9.7,4.3,10.4,-9.7,4.3,10.4,-12.7,2.3,5.7,-12.7,4.3,4.3,-9.7,4.3,10.4,-12.7,4.3,4.3,-9.7,8.0,8.0,-9.7,8.0,8.0,-12.7,4.3,4.3,-12.7,5.7,2.3,-9.7,8.0,8.0,-12.7,5.7,2.3,-9.7,10.4,4.3,-9.7,10.4,4.3,-12.7,5.7,2.3,-12.7,6.1,0.0,-9.7,10.4,4.3,-12.7,6.1,0.0,-9.7,11.3,0.0,-9.7,11.3,0.0,-12.7,6.1,0.0,-12.7,5.7,-2.3,-9.7,11.3,0.0,-12.7,5.7,-2.3,-9.7,10.4,-4.3,-9.7,10.4,-4.3,-12.7,5.7,-2.3,-12.7,4.3,-4.3,-9.7,10.4,-4.3,-12.7,4.3,-4.3,-9.7,8.0,-8.0,-9.7,8.0,-8.0,-12.7,4.3,-4.3,-12.7,2.3,-5.7,-9.7,8.0,-8.0,-12.7,2.3,-5.7,-9.7,4.3,-10.4,-9.7,4.3,-10.4,-12.7,2.3,-5.7,-12.7,0.0,-6.1,-9.7,4.3,-10.4,-12.7,0.0,-6.1,-9.7,0.0,-11.3,-9.7,0.0,-11.3,-12.7,0.0,-6.1,-12.7,-2.3,-5.7,-9.7,0.0,-11.3,-12.7,-2.3,-5.7,-9.7,-4.3,-10.4,-9.7,-4.3,-10.4,-12.7,-2.3,-5.7,-12.7,-4.3,-4.3,-9.7,-4.3,-10.4,-12.7,-4.3,-4.3,-9.7,-8.0,-8.0,-9.7,-8.0,-8.0,-12.7,-4.3,-4.3,-12.7,-5.7,-2.3,-9.7,-8.0,-8.0,-12.7,-5.7,-2.3,-9.7,-10.4,-4.3,-9.7,-10.4,-4.3,-12.7,-5.7,-2.3,-12.7,-6.1,0.0,-9.7,-10.4,-4.3,-12.7,-6.1,0.0,-9.7,-11.3,0.0,-9.7,-11.3,0.0,-12.7,-6.1,0.0,-12.7,-5.7,2.3,-9.7,-11.3,0.0,-12.7,-5.7,2.3,-9.7,-10.4,4.3,-9.7,-10.4,4.3,-12.7,-5.7,2.3,-12.7,-4.3,4.3,-9.7,-10.4,4.3,-12.7,-4.3,4.3,-9.7,-8.0,8.0,-9.7,-8.0,8.0,-12.7,-4.3,4.3,-12.7,-2.3,5.7,-9.7,-8.0,8.0,-12.7,-2.3,5.7,-9.7,-4.3,10.4,-9.7,-4.3,10.4,-12.7,-2.3,5.7,-12.7,0.0,6.1,-9.7,-4.3,10.4,-12.7,0.0,6.1,-9.7,0.0,11.3,-13.8,0.0,0.0,-12.7,2.3,5.7,-12.7,0.0,6.1,-13.8,0.0,0.0,-12.7,4.3,4.3,-12.7,2.3,5.7,-13.8,0.0,0.0,-12.7,5.7,2.3,-12.7,4.3,4.3,-13.8,0.0,0.0,-12.7,6.1,0.0,-12.7,5.7,2.3,-13.8,0.0,0.0,-12.7,5.7,-2.3,-12.7,6.1,0.0,-13.8,0.0,0.0,-12.7,4.3,-4.3,-12.7,5.7,-2.3,-13.8,0.0,0.0,-12.7,2.3,-5.7,-12.7,4.3,-4.3,-13.8,0.0,0.0,-12.7,0.0,-6.1,-12.7,2.3,-5.7,-13.8,0.0,0.0,-12.7,-2.3,-5.7,-12.7,0.0,-6.1,-13.8,0.0,0.0,-12.7,-4.3,-4.3,-12.7,-2.3,-5.7,-13.8,0.0,0.0,-12.7,-5.7,-2.3,-12.7,-4.3,-4.3,-13.8,0.0,0.0,-12.7,-6.1,0.0,-12.7,-5.7,-2.3,-13.8,0.0,0.0,-12.7,-5.7,2.3,-12.7,-6.1,0.0,-13.8,0.0,0.0,-12.7,-4.3,4.3,-12.7,-5.7,2.3,-13.8,0.0,0.0,-12.7,-2.3,5.7,-12.7,-4.3,4.3,-13.8,0.0,0.0,-12.7,0.0,6.1,-12.7,-2.3,5.7,], 11 | "normals": [1.00,0.02,0.00,0.88,0.04,0.47,0.87,0.24,0.44,1.00,0.02,0.00,0.87,0.24,0.44,0.87,0.38,0.31,1.00,0.02,0.00,0.87,0.38,0.31,0.86,0.49,0.15,1.00,0.02,0.00,0.86,0.49,0.15,0.86,0.51,0.00,1.00,0.02,0.00,0.86,0.51,0.00,0.87,0.45,-0.20,1.00,0.02,0.00,0.87,0.45,-0.20,0.88,0.33,-0.34,1.00,0.02,0.00,0.88,0.33,-0.34,0.88,0.17,-0.45,1.00,0.02,0.00,0.88,0.17,-0.45,0.89,-0.03,-0.45,1.00,0.02,0.00,0.89,-0.03,-0.45,0.88,-0.23,-0.42,1.00,0.02,0.00,0.88,-0.23,-0.42,0.88,-0.37,-0.29,1.00,0.02,0.00,0.88,-0.37,-0.29,0.88,-0.46,-0.13,1.00,0.02,0.00,0.88,-0.46,-0.13,0.88,-0.48,0.02,1.00,0.02,0.00,0.88,-0.48,0.02,0.87,-0.43,0.22,1.00,0.02,0.00,0.87,-0.43,0.22,0.87,-0.32,0.36,1.00,0.02,0.00,0.87,-0.32,0.36,0.87,-0.13,0.47,1.00,0.02,0.00,0.87,-0.13,0.47,0.88,0.04,0.47,0.88,0.04,0.47,0.65,0.04,0.76,0.63,0.33,0.70,0.88,0.04,0.47,0.63,0.33,0.70,0.87,0.24,0.44,0.87,0.24,0.44,0.63,0.33,0.70,0.63,0.57,0.52,0.87,0.24,0.44,0.63,0.57,0.52,0.87,0.38,0.31,0.87,0.38,0.31,0.63,0.57,0.52,0.62,0.74,0.26,0.87,0.38,0.31,0.62,0.74,0.26,0.86,0.49,0.15,0.86,0.49,0.15,0.62,0.74,0.26,0.62,0.79,0.00,0.86,0.49,0.15,0.62,0.79,0.00,0.86,0.51,0.00,0.86,0.51,0.00,0.62,0.79,0.00,0.64,0.70,-0.30,0.86,0.51,0.00,0.64,0.70,-0.30,0.87,0.45,-0.20,0.87,0.45,-0.20,0.64,0.70,-0.30,0.65,0.53,-0.54,0.87,0.45,-0.20,0.65,0.53,-0.54,0.88,0.33,-0.34,0.88,0.33,-0.34,0.65,0.53,-0.54,0.66,0.29,-0.70,0.88,0.33,-0.34,0.66,0.29,-0.70,0.88,0.17,-0.45,0.88,0.17,-0.45,0.66,0.29,-0.70,0.67,-0.01,-0.74,0.88,0.17,-0.45,0.67,-0.01,-0.74,0.89,-0.03,-0.45,0.89,-0.03,-0.45,0.67,-0.01,-0.74,0.65,-0.32,-0.69,0.89,-0.03,-0.45,0.65,-0.32,-0.69,0.88,-0.23,-0.42,0.88,-0.23,-0.42,0.65,-0.32,-0.69,0.65,-0.56,-0.51,0.88,-0.23,-0.42,0.65,-0.56,-0.51,0.88,-0.37,-0.29,0.88,-0.37,-0.29,0.65,-0.56,-0.51,0.65,-0.71,-0.25,0.88,-0.37,-0.29,0.65,-0.71,-0.25,0.88,-0.46,-0.13,0.88,-0.46,-0.13,0.65,-0.71,-0.25,0.65,-0.76,0.02,0.88,-0.46,-0.13,0.65,-0.76,0.02,0.88,-0.48,0.02,0.88,-0.48,0.02,0.65,-0.76,0.02,0.64,-0.70,0.32,0.88,-0.48,0.02,0.64,-0.70,0.32,0.87,-0.43,0.22,0.87,-0.43,0.22,0.64,-0.70,0.32,0.64,-0.52,0.56,0.87,-0.43,0.22,0.64,-0.52,0.56,0.87,-0.32,0.36,0.87,-0.32,0.36,0.64,-0.52,0.56,0.65,-0.25,0.72,0.87,-0.32,0.36,0.65,-0.25,0.72,0.87,-0.13,0.47,0.87,-0.13,0.47,0.65,-0.25,0.72,0.65,0.04,0.76,0.87,-0.13,0.47,0.65,0.04,0.76,0.88,0.04,0.47,0.65,0.04,0.76,0.33,0.02,0.94,0.33,0.38,0.87,0.65,0.04,0.76,0.33,0.38,0.87,0.63,0.33,0.70,0.63,0.33,0.70,0.33,0.38,0.87,0.32,0.69,0.65,0.63,0.33,0.70,0.32,0.69,0.65,0.63,0.57,0.52,0.63,0.57,0.52,0.32,0.69,0.65,0.33,0.88,0.34,0.63,0.57,0.52,0.33,0.88,0.34,0.62,0.74,0.26,0.62,0.74,0.26,0.33,0.88,0.34,0.31,0.95,0.00,0.62,0.74,0.26,0.31,0.95,0.00,0.62,0.79,0.00,0.62,0.79,0.00,0.31,0.95,0.00,0.34,0.87,-0.36,0.62,0.79,0.00,0.34,0.87,-0.36,0.64,0.70,-0.30,0.64,0.70,-0.30,0.34,0.87,-0.36,0.35,0.67,-0.66,0.64,0.70,-0.30,0.35,0.67,-0.66,0.65,0.53,-0.54,0.65,0.53,-0.54,0.35,0.67,-0.66,0.35,0.36,-0.87,0.65,0.53,-0.54,0.35,0.36,-0.87,0.66,0.29,-0.70,0.66,0.29,-0.70,0.35,0.36,-0.87,0.36,-0.01,-0.93,0.66,0.29,-0.70,0.36,-0.01,-0.93,0.67,-0.01,-0.74,0.67,-0.01,-0.74,0.36,-0.01,-0.93,0.35,-0.37,-0.86,0.67,-0.01,-0.74,0.35,-0.37,-0.86,0.65,-0.32,-0.69,0.65,-0.32,-0.69,0.35,-0.37,-0.86,0.34,-0.68,-0.65,0.65,-0.32,-0.69,0.34,-0.68,-0.65,0.65,-0.56,-0.51,0.65,-0.56,-0.51,0.34,-0.68,-0.65,0.34,-0.88,-0.34,0.65,-0.56,-0.51,0.34,-0.88,-0.34,0.65,-0.71,-0.25,0.65,-0.71,-0.25,0.34,-0.88,-0.34,0.35,-0.94,0.01,0.65,-0.71,-0.25,0.35,-0.94,0.01,0.65,-0.76,0.02,0.65,-0.76,0.02,0.35,-0.94,0.01,0.34,-0.86,0.38,0.65,-0.76,0.02,0.34,-0.86,0.38,0.64,-0.70,0.32,0.64,-0.70,0.32,0.34,-0.86,0.38,0.34,-0.64,0.69,0.64,-0.70,0.32,0.34,-0.64,0.69,0.64,-0.52,0.56,0.64,-0.52,0.56,0.34,-0.64,0.69,0.33,-0.34,0.88,0.64,-0.52,0.56,0.33,-0.34,0.88,0.65,-0.25,0.72,0.65,-0.25,0.72,0.33,-0.34,0.88,0.33,0.02,0.94,0.65,-0.25,0.72,0.33,0.02,0.94,0.65,0.04,0.76,0.33,0.02,0.94,0.02,0.02,1.00,0.02,0.40,0.92,0.33,0.02,0.94,0.02,0.40,0.92,0.33,0.38,0.87,0.33,0.38,0.87,0.02,0.40,0.92,0.01,0.72,0.69,0.33,0.38,0.87,0.01,0.72,0.69,0.32,0.69,0.65,0.32,0.69,0.65,0.01,0.72,0.69,0.01,0.93,0.36,0.32,0.69,0.65,0.01,0.93,0.36,0.33,0.88,0.34,0.33,0.88,0.34,0.01,0.93,0.36,-0.00,1.00,-0.00,0.33,0.88,0.34,-0.00,1.00,-0.00,0.31,0.95,0.00,0.31,0.95,0.00,-0.00,1.00,-0.00,0.02,0.93,-0.36,0.31,0.95,0.00,0.02,0.93,-0.36,0.34,0.87,-0.36,0.34,0.87,-0.36,0.02,0.93,-0.36,0.03,0.72,-0.69,0.34,0.87,-0.36,0.03,0.72,-0.69,0.35,0.67,-0.66,0.35,0.67,-0.66,0.03,0.72,-0.69,0.04,0.40,-0.92,0.35,0.67,-0.66,0.04,0.40,-0.92,0.35,0.36,-0.87,0.35,0.36,-0.87,0.04,0.40,-0.92,0.04,0.02,-1.00,0.35,0.36,-0.87,0.04,0.02,-1.00,0.36,-0.01,-0.93,0.36,-0.01,-0.93,0.04,0.02,-1.00,0.04,-0.37,-0.93,0.36,-0.01,-0.93,0.04,-0.37,-0.93,0.35,-0.37,-0.86,0.35,-0.37,-0.86,0.04,-0.37,-0.93,0.03,-0.70,-0.72,0.35,-0.37,-0.86,0.03,-0.70,-0.72,0.34,-0.68,-0.65,0.34,-0.68,-0.65,0.03,-0.70,-0.72,0.02,-0.92,-0.40,0.34,-0.68,-0.65,0.02,-0.92,-0.40,0.34,-0.88,-0.34,0.34,-0.88,-0.34,0.02,-0.92,-0.40,-0.00,-1.00,0.01,0.34,-0.88,-0.34,-0.00,-1.00,0.01,0.35,-0.94,0.01,0.35,-0.94,0.01,-0.00,-1.00,0.01,0.01,-0.92,0.40,0.35,-0.94,0.01,0.01,-0.92,0.40,0.34,-0.86,0.38,0.34,-0.86,0.38,0.01,-0.92,0.40,0.01,-0.70,0.72,0.34,-0.86,0.38,0.01,-0.70,0.72,0.34,-0.64,0.69,0.34,-0.64,0.69,0.01,-0.70,0.72,0.02,-0.37,0.93,0.34,-0.64,0.69,0.02,-0.37,0.93,0.33,-0.34,0.88,0.33,-0.34,0.88,0.02,-0.37,0.93,0.02,0.02,1.00,0.33,-0.34,0.88,0.02,0.02,1.00,0.33,0.02,0.94,0.02,0.02,1.00,-0.32,-0.01,0.95,-0.32,0.36,0.88,0.02,0.02,1.00,-0.32,0.36,0.88,0.02,0.40,0.92,0.02,0.40,0.92,-0.32,0.36,0.88,-0.31,0.67,0.68,0.02,0.40,0.92,-0.31,0.67,0.68,0.01,0.72,0.69,0.01,0.72,0.69,-0.31,0.67,0.68,-0.32,0.87,0.38,0.01,0.72,0.69,-0.32,0.87,0.38,0.01,0.93,0.36,0.01,0.93,0.36,-0.32,0.87,0.38,-0.31,0.95,0.02,0.01,0.93,0.36,-0.31,0.95,0.02,-0.00,1.00,-0.00,-0.00,1.00,-0.00,-0.31,0.95,0.02,-0.31,0.88,-0.36,-0.00,1.00,-0.00,-0.31,0.88,-0.36,0.02,0.93,-0.36,0.02,0.93,-0.36,-0.31,0.88,-0.36,-0.30,0.69,-0.66,0.02,0.93,-0.36,-0.30,0.69,-0.66,0.03,0.72,-0.69,0.03,0.72,-0.69,-0.30,0.69,-0.66,-0.30,0.38,-0.88,0.03,0.72,-0.69,-0.30,0.38,-0.88,0.04,0.40,-0.92,0.04,0.40,-0.92,-0.30,0.38,-0.88,-0.30,0.02,-0.95,0.04,0.40,-0.92,-0.30,0.02,-0.95,0.04,0.02,-1.00,0.04,0.02,-1.00,-0.30,0.02,-0.95,-0.30,-0.34,-0.89,0.04,0.02,-1.00,-0.30,-0.34,-0.89,0.04,-0.37,-0.93,0.04,-0.37,-0.93,-0.30,-0.34,-0.89,-0.30,-0.64,-0.71,0.04,-0.37,-0.93,-0.30,-0.64,-0.71,0.03,-0.70,-0.72,0.03,-0.70,-0.72,-0.30,-0.64,-0.71,-0.31,-0.86,-0.40,0.03,-0.70,-0.72,-0.31,-0.86,-0.40,0.02,-0.92,-0.40,0.02,-0.92,-0.40,-0.31,-0.86,-0.40,-0.35,-0.94,-0.03,0.02,-0.92,-0.40,-0.35,-0.94,-0.03,-0.00,-1.00,0.01,-0.00,-1.00,0.01,-0.35,-0.94,-0.03,-0.33,-0.88,0.36,-0.00,-1.00,0.01,-0.33,-0.88,0.36,0.01,-0.92,0.40,0.01,-0.92,0.40,-0.33,-0.88,0.36,-0.32,-0.68,0.66,0.01,-0.92,0.40,-0.32,-0.68,0.66,0.01,-0.70,0.72,0.01,-0.70,0.72,-0.32,-0.68,0.66,-0.32,-0.37,0.87,0.01,-0.70,0.72,-0.32,-0.37,0.87,0.02,-0.37,0.93,0.02,-0.37,0.93,-0.32,-0.37,0.87,-0.32,-0.01,0.95,0.02,-0.37,0.93,-0.32,-0.01,0.95,0.02,0.02,1.00,-0.32,-0.01,0.95,-0.62,-0.01,0.78,-0.61,0.29,0.74,-0.32,-0.01,0.95,-0.61,0.29,0.74,-0.32,0.36,0.88,-0.32,0.36,0.88,-0.61,0.29,0.74,-0.62,0.53,0.58,-0.32,0.36,0.88,-0.62,0.53,0.58,-0.31,0.67,0.68,-0.31,0.67,0.68,-0.62,0.53,0.58,-0.63,0.70,0.33,-0.31,0.67,0.68,-0.63,0.70,0.33,-0.32,0.87,0.38,-0.32,0.87,0.38,-0.63,0.70,0.33,-0.62,0.79,0.04,-0.32,0.87,0.38,-0.62,0.79,0.04,-0.31,0.95,0.02,-0.31,0.95,0.02,-0.62,0.79,0.04,-0.61,0.74,-0.28,-0.31,0.95,0.02,-0.61,0.74,-0.28,-0.31,0.88,-0.36,-0.31,0.88,-0.36,-0.61,0.74,-0.28,-0.61,0.57,-0.54,-0.31,0.88,-0.36,-0.61,0.57,-0.54,-0.30,0.69,-0.66,-0.30,0.69,-0.66,-0.61,0.57,-0.54,-0.60,0.33,-0.72,-0.30,0.69,-0.66,-0.60,0.33,-0.72,-0.30,0.38,-0.88,-0.30,0.38,-0.88,-0.60,0.33,-0.72,-0.60,0.04,-0.80,-0.30,0.38,-0.88,-0.60,0.04,-0.80,-0.30,0.02,-0.95,-0.30,0.02,-0.95,-0.60,0.04,-0.80,-0.60,-0.25,-0.76,-0.30,0.02,-0.95,-0.60,-0.25,-0.76,-0.30,-0.34,-0.89,-0.30,-0.34,-0.89,-0.60,-0.25,-0.76,-0.61,-0.52,-0.60,-0.30,-0.34,-0.89,-0.61,-0.52,-0.60,-0.30,-0.64,-0.71,-0.30,-0.64,-0.71,-0.61,-0.52,-0.60,-0.63,-0.70,-0.35,-0.30,-0.64,-0.71,-0.63,-0.70,-0.35,-0.31,-0.86,-0.40,-0.31,-0.86,-0.40,-0.63,-0.70,-0.35,-0.64,-0.76,-0.06,-0.31,-0.86,-0.40,-0.64,-0.76,-0.06,-0.35,-0.94,-0.03,-0.35,-0.94,-0.03,-0.64,-0.76,-0.06,-0.64,-0.71,0.28,-0.35,-0.94,-0.03,-0.64,-0.71,0.28,-0.33,-0.88,0.36,-0.33,-0.88,0.36,-0.64,-0.71,0.28,-0.63,-0.56,0.53,-0.33,-0.88,0.36,-0.63,-0.56,0.53,-0.32,-0.68,0.66,-0.32,-0.68,0.66,-0.63,-0.56,0.53,-0.63,-0.32,0.71,-0.32,-0.68,0.66,-0.63,-0.32,0.71,-0.32,-0.37,0.87,-0.32,-0.37,0.87,-0.63,-0.32,0.71,-0.62,-0.01,0.78,-0.32,-0.37,0.87,-0.62,-0.01,0.78,-0.32,-0.01,0.95,-0.62,-0.01,0.78,-0.86,-0.03,0.51,-0.86,0.17,0.48,-0.62,-0.01,0.78,-0.86,0.17,0.48,-0.61,0.29,0.74,-0.61,0.29,0.74,-0.86,0.17,0.48,-0.86,0.33,0.39,-0.61,0.29,0.74,-0.86,0.33,0.39,-0.62,0.53,0.58,-0.62,0.53,0.58,-0.86,0.33,0.39,-0.87,0.45,0.23,-0.62,0.53,0.58,-0.87,0.45,0.23,-0.63,0.70,0.33,-0.63,0.70,0.33,-0.87,0.45,0.23,-0.86,0.51,0.05,-0.63,0.70,0.33,-0.86,0.51,0.05,-0.62,0.79,0.04,-0.62,0.79,0.04,-0.86,0.51,0.05,-0.85,0.49,-0.18,-0.62,0.79,0.04,-0.85,0.49,-0.18,-0.61,0.74,-0.28,-0.61,0.74,-0.28,-0.85,0.49,-0.18,-0.86,0.38,-0.34,-0.61,0.74,-0.28,-0.86,0.38,-0.34,-0.61,0.57,-0.54,-0.61,0.57,-0.54,-0.86,0.38,-0.34,-0.85,0.24,-0.47,-0.61,0.57,-0.54,-0.85,0.24,-0.47,-0.60,0.33,-0.72,-0.60,0.33,-0.72,-0.85,0.24,-0.47,-0.85,0.04,-0.53,-0.60,0.33,-0.72,-0.85,0.04,-0.53,-0.60,0.04,-0.80,-0.60,0.04,-0.80,-0.85,0.04,-0.53,-0.86,-0.13,-0.50,-0.60,0.04,-0.80,-0.86,-0.13,-0.50,-0.60,-0.25,-0.76,-0.60,-0.25,-0.76,-0.86,-0.13,-0.50,-0.85,-0.32,-0.42,-0.60,-0.25,-0.76,-0.85,-0.32,-0.42,-0.61,-0.52,-0.60,-0.61,-0.52,-0.60,-0.85,-0.32,-0.42,-0.86,-0.43,-0.25,-0.61,-0.52,-0.60,-0.86,-0.43,-0.25,-0.63,-0.70,-0.35,-0.63,-0.70,-0.35,-0.86,-0.43,-0.25,-0.87,-0.48,-0.08,-0.63,-0.70,-0.35,-0.87,-0.48,-0.08,-0.64,-0.76,-0.06,-0.64,-0.76,-0.06,-0.87,-0.48,-0.08,-0.87,-0.46,0.16,-0.64,-0.76,-0.06,-0.87,-0.46,0.16,-0.64,-0.71,0.28,-0.64,-0.71,0.28,-0.87,-0.46,0.16,-0.87,-0.37,0.33,-0.64,-0.71,0.28,-0.87,-0.37,0.33,-0.63,-0.56,0.53,-0.63,-0.56,0.53,-0.87,-0.37,0.33,-0.86,-0.23,0.45,-0.63,-0.56,0.53,-0.86,-0.23,0.45,-0.63,-0.32,0.71,-0.63,-0.32,0.71,-0.86,-0.23,0.45,-0.86,-0.03,0.51,-0.63,-0.32,0.71,-0.86,-0.03,0.51,-0.62,-0.01,0.78,-1.00,0.02,-0.04,-0.86,0.17,0.48,-0.86,-0.03,0.51,-1.00,0.02,-0.04,-0.86,0.33,0.39,-0.86,0.17,0.48,-1.00,0.02,-0.04,-0.87,0.45,0.23,-0.86,0.33,0.39,-1.00,0.02,-0.04,-0.86,0.51,0.05,-0.87,0.45,0.23,-1.00,0.02,-0.04,-0.85,0.49,-0.18,-0.86,0.51,0.05,-1.00,0.02,-0.04,-0.86,0.38,-0.34,-0.85,0.49,-0.18,-1.00,0.02,-0.04,-0.85,0.24,-0.47,-0.86,0.38,-0.34,-1.00,0.02,-0.04,-0.85,0.04,-0.53,-0.85,0.24,-0.47,-1.00,0.02,-0.04,-0.86,-0.13,-0.50,-0.85,0.04,-0.53,-1.00,0.02,-0.04,-0.85,-0.32,-0.42,-0.86,-0.13,-0.50,-1.00,0.02,-0.04,-0.86,-0.43,-0.25,-0.85,-0.32,-0.42,-1.00,0.02,-0.04,-0.87,-0.48,-0.08,-0.86,-0.43,-0.25,-1.00,0.02,-0.04,-0.87,-0.46,0.16,-0.87,-0.48,-0.08,-1.00,0.02,-0.04,-0.87,-0.37,0.33,-0.87,-0.46,0.16,-1.00,0.02,-0.04,-0.86,-0.23,0.45,-0.87,-0.37,0.33,-1.00,0.02,-0.04,-0.86,-0.03,0.51,-0.86,-0.23,0.45,] 12 | }, 13 | ], 14 | "textureCoordinates": [0.000000,0.000000,0.000000,0.125000,0.062500,0.125000,0.062500,0.000000,0.062500,0.125000,0.125000,0.125000,0.125000,0.000000,0.125000,0.125000,0.187500,0.125000,0.187500,0.000000,0.187500,0.125000,0.250000,0.125000,0.250000,0.000000,0.250000,0.125000,0.312500,0.125000,0.312500,0.000000,0.312500,0.125000,0.375000,0.125000,0.375000,0.000000,0.375000,0.125000,0.437500,0.125000,0.437500,0.000000,0.437500,0.125000,0.500000,0.125000,0.500000,0.000000,0.500000,0.125000,0.562500,0.125000,0.562500,0.000000,0.562500,0.125000,0.625000,0.125000,0.625000,0.000000,0.625000,0.125000,0.687500,0.125000,0.687500,0.000000,0.687500,0.125000,0.750000,0.125000,0.750000,0.000000,0.750000,0.125000,0.812500,0.125000,0.812500,0.000000,0.812500,0.125000,0.875000,0.125000,0.875000,0.000000,0.875000,0.125000,0.937500,0.125000,0.937500,0.000000,0.937500,0.125000,1.000000,0.125000,0.000000,0.125000,0.000000,0.250000,0.062500,0.250000,0.000000,0.125000,0.062500,0.250000,0.062500,0.125000,0.062500,0.125000,0.062500,0.250000,0.125000,0.250000,0.062500,0.125000,0.125000,0.250000,0.125000,0.125000,0.125000,0.125000,0.125000,0.250000,0.187500,0.250000,0.125000,0.125000,0.187500,0.250000,0.187500,0.125000,0.187500,0.125000,0.187500,0.250000,0.250000,0.250000,0.187500,0.125000,0.250000,0.250000,0.250000,0.125000,0.250000,0.125000,0.250000,0.250000,0.312500,0.250000,0.250000,0.125000,0.312500,0.250000,0.312500,0.125000,0.312500,0.125000,0.312500,0.250000,0.375000,0.250000,0.312500,0.125000,0.375000,0.250000,0.375000,0.125000,0.375000,0.125000,0.375000,0.250000,0.437500,0.250000,0.375000,0.125000,0.437500,0.250000,0.437500,0.125000,0.437500,0.125000,0.437500,0.250000,0.500000,0.250000,0.437500,0.125000,0.500000,0.250000,0.500000,0.125000,0.500000,0.125000,0.500000,0.250000,0.562500,0.250000,0.500000,0.125000,0.562500,0.250000,0.562500,0.125000,0.562500,0.125000,0.562500,0.250000,0.625000,0.250000,0.562500,0.125000,0.625000,0.250000,0.625000,0.125000,0.625000,0.125000,0.625000,0.250000,0.687500,0.250000,0.625000,0.125000,0.687500,0.250000,0.687500,0.125000,0.687500,0.125000,0.687500,0.250000,0.750000,0.250000,0.687500,0.125000,0.750000,0.250000,0.750000,0.125000,0.750000,0.125000,0.750000,0.250000,0.812500,0.250000,0.750000,0.125000,0.812500,0.250000,0.812500,0.125000,0.812500,0.125000,0.812500,0.250000,0.875000,0.250000,0.812500,0.125000,0.875000,0.250000,0.875000,0.125000,0.875000,0.125000,0.875000,0.250000,0.937500,0.250000,0.875000,0.125000,0.937500,0.250000,0.937500,0.125000,0.937500,0.125000,0.937500,0.250000,1.000000,0.250000,0.937500,0.125000,1.000000,0.250000,1.000000,0.125000,0.000000,0.250000,0.000000,0.375000,0.062500,0.375000,0.000000,0.250000,0.062500,0.375000,0.062500,0.250000,0.062500,0.250000,0.062500,0.375000,0.125000,0.375000,0.062500,0.250000,0.125000,0.375000,0.125000,0.250000,0.125000,0.250000,0.125000,0.375000,0.187500,0.375000,0.125000,0.250000,0.187500,0.375000,0.187500,0.250000,0.187500,0.250000,0.187500,0.375000,0.250000,0.375000,0.187500,0.250000,0.250000,0.375000,0.250000,0.250000,0.250000,0.250000,0.250000,0.375000,0.312500,0.375000,0.250000,0.250000,0.312500,0.375000,0.312500,0.250000,0.312500,0.250000,0.312500,0.375000,0.375000,0.375000,0.312500,0.250000,0.375000,0.375000,0.375000,0.250000,0.375000,0.250000,0.375000,0.375000,0.437500,0.375000,0.375000,0.250000,0.437500,0.375000,0.437500,0.250000,0.437500,0.250000,0.437500,0.375000,0.500000,0.375000,0.437500,0.250000,0.500000,0.375000,0.500000,0.250000,0.500000,0.250000,0.500000,0.375000,0.562500,0.375000,0.500000,0.250000,0.562500,0.375000,0.562500,0.250000,0.562500,0.250000,0.562500,0.375000,0.625000,0.375000,0.562500,0.250000,0.625000,0.375000,0.625000,0.250000,0.625000,0.250000,0.625000,0.375000,0.687500,0.375000,0.625000,0.250000,0.687500,0.375000,0.687500,0.250000,0.687500,0.250000,0.687500,0.375000,0.750000,0.375000,0.687500,0.250000,0.750000,0.375000,0.750000,0.250000,0.750000,0.250000,0.750000,0.375000,0.812500,0.375000,0.750000,0.250000,0.812500,0.375000,0.812500,0.250000,0.812500,0.250000,0.812500,0.375000,0.875000,0.375000,0.812500,0.250000,0.875000,0.375000,0.875000,0.250000,0.875000,0.250000,0.875000,0.375000,0.937500,0.375000,0.875000,0.250000,0.937500,0.375000,0.937500,0.250000,0.937500,0.250000,0.937500,0.375000,1.000000,0.375000,0.937500,0.250000,1.000000,0.375000,1.000000,0.250000,0.000000,0.375000,0.000000,0.500000,0.062500,0.500000,0.000000,0.375000,0.062500,0.500000,0.062500,0.375000,0.062500,0.375000,0.062500,0.500000,0.125000,0.500000,0.062500,0.375000,0.125000,0.500000,0.125000,0.375000,0.125000,0.375000,0.125000,0.500000,0.187500,0.500000,0.125000,0.375000,0.187500,0.500000,0.187500,0.375000,0.187500,0.375000,0.187500,0.500000,0.250000,0.500000,0.187500,0.375000,0.250000,0.500000,0.250000,0.375000,0.250000,0.375000,0.250000,0.500000,0.312500,0.500000,0.250000,0.375000,0.312500,0.500000,0.312500,0.375000,0.312500,0.375000,0.312500,0.500000,0.375000,0.500000,0.312500,0.375000,0.375000,0.500000,0.375000,0.375000,0.375000,0.375000,0.375000,0.500000,0.437500,0.500000,0.375000,0.375000,0.437500,0.500000,0.437500,0.375000,0.437500,0.375000,0.437500,0.500000,0.500000,0.500000,0.437500,0.375000,0.500000,0.500000,0.500000,0.375000,0.500000,0.375000,0.500000,0.500000,0.562500,0.500000,0.500000,0.375000,0.562500,0.500000,0.562500,0.375000,0.562500,0.375000,0.562500,0.500000,0.625000,0.500000,0.562500,0.375000,0.625000,0.500000,0.625000,0.375000,0.625000,0.375000,0.625000,0.500000,0.687500,0.500000,0.625000,0.375000,0.687500,0.500000,0.687500,0.375000,0.687500,0.375000,0.687500,0.500000,0.750000,0.500000,0.687500,0.375000,0.750000,0.500000,0.750000,0.375000,0.750000,0.375000,0.750000,0.500000,0.812500,0.500000,0.750000,0.375000,0.812500,0.500000,0.812500,0.375000,0.812500,0.375000,0.812500,0.500000,0.875000,0.500000,0.812500,0.375000,0.875000,0.500000,0.875000,0.375000,0.875000,0.375000,0.875000,0.500000,0.937500,0.500000,0.875000,0.375000,0.937500,0.500000,0.937500,0.375000,0.937500,0.375000,0.937500,0.500000,1.000000,0.500000,0.937500,0.375000,1.000000,0.500000,1.000000,0.375000,0.000000,0.500000,0.000000,0.625000,0.062500,0.625000,0.000000,0.500000,0.062500,0.625000,0.062500,0.500000,0.062500,0.500000,0.062500,0.625000,0.125000,0.625000,0.062500,0.500000,0.125000,0.625000,0.125000,0.500000,0.125000,0.500000,0.125000,0.625000,0.187500,0.625000,0.125000,0.500000,0.187500,0.625000,0.187500,0.500000,0.187500,0.500000,0.187500,0.625000,0.250000,0.625000,0.187500,0.500000,0.250000,0.625000,0.250000,0.500000,0.250000,0.500000,0.250000,0.625000,0.312500,0.625000,0.250000,0.500000,0.312500,0.625000,0.312500,0.500000,0.312500,0.500000,0.312500,0.625000,0.375000,0.625000,0.312500,0.500000,0.375000,0.625000,0.375000,0.500000,0.375000,0.500000,0.375000,0.625000,0.437500,0.625000,0.375000,0.500000,0.437500,0.625000,0.437500,0.500000,0.437500,0.500000,0.437500,0.625000,0.500000,0.625000,0.437500,0.500000,0.500000,0.625000,0.500000,0.500000,0.500000,0.500000,0.500000,0.625000,0.562500,0.625000,0.500000,0.500000,0.562500,0.625000,0.562500,0.500000,0.562500,0.500000,0.562500,0.625000,0.625000,0.625000,0.562500,0.500000,0.625000,0.625000,0.625000,0.500000,0.625000,0.500000,0.625000,0.625000,0.687500,0.625000,0.625000,0.500000,0.687500,0.625000,0.687500,0.500000,0.687500,0.500000,0.687500,0.625000,0.750000,0.625000,0.687500,0.500000,0.750000,0.625000,0.750000,0.500000,0.750000,0.500000,0.750000,0.625000,0.812500,0.625000,0.750000,0.500000,0.812500,0.625000,0.812500,0.500000,0.812500,0.500000,0.812500,0.625000,0.875000,0.625000,0.812500,0.500000,0.875000,0.625000,0.875000,0.500000,0.875000,0.500000,0.875000,0.625000,0.937500,0.625000,0.875000,0.500000,0.937500,0.625000,0.937500,0.500000,0.937500,0.500000,0.937500,0.625000,1.000000,0.625000,0.937500,0.500000,1.000000,0.625000,1.000000,0.500000,0.000000,0.625000,0.000000,0.750000,0.062500,0.750000,0.000000,0.625000,0.062500,0.750000,0.062500,0.625000,0.062500,0.625000,0.062500,0.750000,0.125000,0.750000,0.062500,0.625000,0.125000,0.750000,0.125000,0.625000,0.125000,0.625000,0.125000,0.750000,0.187500,0.750000,0.125000,0.625000,0.187500,0.750000,0.187500,0.625000,0.187500,0.625000,0.187500,0.750000,0.250000,0.750000,0.187500,0.625000,0.250000,0.750000,0.250000,0.625000,0.250000,0.625000,0.250000,0.750000,0.312500,0.750000,0.250000,0.625000,0.312500,0.750000,0.312500,0.625000,0.312500,0.625000,0.312500,0.750000,0.375000,0.750000,0.312500,0.625000,0.375000,0.750000,0.375000,0.625000,0.375000,0.625000,0.375000,0.750000,0.437500,0.750000,0.375000,0.625000,0.437500,0.750000,0.437500,0.625000,0.437500,0.625000,0.437500,0.750000,0.500000,0.750000,0.437500,0.625000,0.500000,0.750000,0.500000,0.625000,0.500000,0.625000,0.500000,0.750000,0.562500,0.750000,0.500000,0.625000,0.562500,0.750000,0.562500,0.625000,0.562500,0.625000,0.562500,0.750000,0.625000,0.750000,0.562500,0.625000,0.625000,0.750000,0.625000,0.625000,0.625000,0.625000,0.625000,0.750000,0.687500,0.750000,0.625000,0.625000,0.687500,0.750000,0.687500,0.625000,0.687500,0.625000,0.687500,0.750000,0.750000,0.750000,0.687500,0.625000,0.750000,0.750000,0.750000,0.625000,0.750000,0.625000,0.750000,0.750000,0.812500,0.750000,0.750000,0.625000,0.812500,0.750000,0.812500,0.625000,0.812500,0.625000,0.812500,0.750000,0.875000,0.750000,0.812500,0.625000,0.875000,0.750000,0.875000,0.625000,0.875000,0.625000,0.875000,0.750000,0.937500,0.750000,0.875000,0.625000,0.937500,0.750000,0.937500,0.625000,0.937500,0.625000,0.937500,0.750000,1.000000,0.750000,0.937500,0.625000,1.000000,0.750000,1.000000,0.625000,0.000000,0.750000,0.000000,0.875000,0.062500,0.875000,0.000000,0.750000,0.062500,0.875000,0.062500,0.750000,0.062500,0.750000,0.062500,0.875000,0.125000,0.875000,0.062500,0.750000,0.125000,0.875000,0.125000,0.750000,0.125000,0.750000,0.125000,0.875000,0.187500,0.875000,0.125000,0.750000,0.187500,0.875000,0.187500,0.750000,0.187500,0.750000,0.187500,0.875000,0.250000,0.875000,0.187500,0.750000,0.250000,0.875000,0.250000,0.750000,0.250000,0.750000,0.250000,0.875000,0.312500,0.875000,0.250000,0.750000,0.312500,0.875000,0.312500,0.750000,0.312500,0.750000,0.312500,0.875000,0.375000,0.875000,0.312500,0.750000,0.375000,0.875000,0.375000,0.750000,0.375000,0.750000,0.375000,0.875000,0.437500,0.875000,0.375000,0.750000,0.437500,0.875000,0.437500,0.750000,0.437500,0.750000,0.437500,0.875000,0.500000,0.875000,0.437500,0.750000,0.500000,0.875000,0.500000,0.750000,0.500000,0.750000,0.500000,0.875000,0.562500,0.875000,0.500000,0.750000,0.562500,0.875000,0.562500,0.750000,0.562500,0.750000,0.562500,0.875000,0.625000,0.875000,0.562500,0.750000,0.625000,0.875000,0.625000,0.750000,0.625000,0.750000,0.625000,0.875000,0.687500,0.875000,0.625000,0.750000,0.687500,0.875000,0.687500,0.750000,0.687500,0.750000,0.687500,0.875000,0.750000,0.875000,0.687500,0.750000,0.750000,0.875000,0.750000,0.750000,0.750000,0.750000,0.750000,0.875000,0.812500,0.875000,0.750000,0.750000,0.812500,0.875000,0.812500,0.750000,0.812500,0.750000,0.812500,0.875000,0.875000,0.875000,0.812500,0.750000,0.875000,0.875000,0.875000,0.750000,0.875000,0.750000,0.875000,0.875000,0.937500,0.875000,0.875000,0.750000,0.937500,0.875000,0.937500,0.750000,0.937500,0.750000,0.937500,0.875000,1.000000,0.875000,0.937500,0.750000,1.000000,0.875000,1.000000,0.750000,0.000000,1.000000,0.062500,0.875000,0.000000,0.875000,0.062500,1.000000,0.125000,0.875000,0.062500,0.875000,0.125000,1.000000,0.187500,0.875000,0.125000,0.875000,0.187500,1.000000,0.250000,0.875000,0.187500,0.875000,0.250000,1.000000,0.312500,0.875000,0.250000,0.875000,0.312500,1.000000,0.375000,0.875000,0.312500,0.875000,0.375000,1.000000,0.437500,0.875000,0.375000,0.875000,0.437500,1.000000,0.500000,0.875000,0.437500,0.875000,0.500000,1.000000,0.562500,0.875000,0.500000,0.875000,0.562500,1.000000,0.625000,0.875000,0.562500,0.875000,0.625000,1.000000,0.687500,0.875000,0.625000,0.875000,0.687500,1.000000,0.750000,0.875000,0.687500,0.875000,0.750000,1.000000,0.812500,0.875000,0.750000,0.875000,0.812500,1.000000,0.875000,0.875000,0.812500,0.875000,0.875000,1.000000,0.937500,0.875000,0.875000,0.875000,0.937500,1.000000,1.000000,0.875000,0.937500,0.875000,] 15 | } 16 | ] 17 | } 18 | --------------------------------------------------------------------------------