├── favicon.ico ├── favicon.png ├── .hgignore ├── .gitignore ├── src ├── shaders │ ├── clear.shader │ ├── drawAll.shader │ ├── vertexPoints.vshader │ ├── cameraRendererFast.shader │ ├── cameraRenderer.vshader │ ├── primitivePalette.shader │ ├── primitiveRenderer.shader │ ├── scroller.shader │ ├── stampPalette.shader │ ├── cameraRendererSimple.shader │ ├── drawCircle.shader │ ├── drawRect.shader │ ├── mixPalette.shader │ ├── mixRect.shader │ ├── cameraRendererTV.shader │ ├── cameraRenderer.shader │ ├── copyPasteRect.shader │ ├── intersectSpawn.shader │ └── shieldGenerator.shader ├── gl │ ├── World.js │ ├── Palette.js │ ├── GLHelper.js │ ├── Dish.js │ ├── ParticleSystem.js │ ├── Rule.js │ └── Reactor.js ├── Tests.js ├── CellSpace.js ├── CellSpace │ ├── State.js │ ├── Utils.js │ ├── Setup.js │ ├── GameLoop.js │ └── GUI.js ├── data │ └── FileStore.js ├── Utils.js ├── story │ └── StoryTeller.js └── EvoCell.js ├── lighttpdconfig ├── test.config └── test3001.config ├── README.md ├── TODO.txt ├── css └── style.css └── cellspace.html /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizard23/evocell/HEAD/favicon.ico -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wizard23/evocell/HEAD/favicon.png -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | ccc_rules/* 3 | patterns/* 4 | rules/* 5 | *.swp 6 | *~ 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rules/ 2 | r.js 3 | .idea/ 4 | cellspace.sublime-project 5 | cellspace.sublime-workspace 6 | *.js~ 7 | *.html~ 8 | -------------------------------------------------------------------------------- /src/shaders/clear.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | varying vec2 vTexCoord; 6 | 7 | uniform vec4 color; 8 | 9 | void main(void) { 10 | gl_FragColor = color; 11 | } 12 | -------------------------------------------------------------------------------- /src/shaders/drawAll.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | uniform sampler2D texFrame; 5 | uniform float state; 6 | 7 | varying vec2 vTexCoord; 8 | void main(void) { 9 | gl_FragColor = vec4(0., 0., 0., state); 10 | } 11 | -------------------------------------------------------------------------------- /src/shaders/vertexPoints.vshader: -------------------------------------------------------------------------------- 1 | attribute vec2 pointPos; 2 | uniform vec2 offset; 3 | //uniform float pointSize; 4 | 5 | void main() { 6 | gl_Position = vec4(pointPos + offset, 0.0, 1.0); 7 | //gl_Position = vec4(pointPos, 0.0, 1.0); 8 | gl_PointSize = 3.; 9 | } 10 | -------------------------------------------------------------------------------- /lighttpdconfig/test.config: -------------------------------------------------------------------------------- 1 | server.document-root = ".." 2 | 3 | server.port = 3000 4 | 5 | mimetype.assign = ( 6 | ".html" => "text/html", 7 | ".txt" => "text/plain", 8 | ".jpg" => "image/jpeg", 9 | ".png" => "image/png", 10 | ".css" => "text/css", 11 | ".js" => "text/javascript", 12 | ) 13 | -------------------------------------------------------------------------------- /lighttpdconfig/test3001.config: -------------------------------------------------------------------------------- 1 | server.document-root = ".." 2 | 3 | server.port = 3001 4 | 5 | mimetype.assign = ( 6 | ".html" => "text/html", 7 | ".txt" => "text/plain", 8 | ".jpg" => "image/jpeg", 9 | ".png" => "image/png", 10 | ".css" => "text/css", 11 | ".js" => "application/javascript", 12 | ) 13 | -------------------------------------------------------------------------------- /src/shaders/cameraRendererFast.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D texFrame2; 7 | uniform vec2 resolution; 8 | 9 | varying vec2 vTexCoord; 10 | 11 | const float borderW = 0.15; 12 | 13 | void main(void) { 14 | gl_FragColor = texture2D(texFrame, vTexCoord); 15 | } -------------------------------------------------------------------------------- /src/shaders/cameraRenderer.vshader: -------------------------------------------------------------------------------- 1 | attribute vec3 aPos; 2 | attribute vec2 aTexCoord; 3 | 4 | varying vec2 vTexCoord; 5 | 6 | uniform mat4 projectionMatrix; 7 | uniform mat4 modelViewMatrix; 8 | 9 | void main(void) { 10 | gl_Position = vec4(aPos, 1.); 11 | gl_Position = projectionMatrix * modelViewMatrix * gl_Position; 12 | vTexCoord = aTexCoord; 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | evocell 2 | ======= 3 | 4 | A HTML5/WebGL app to evolve & play around with ruletable based cellular automata 5 | 6 | Test the ALPHA version of the ca based space shooter: EvoCellSpace 7 | Very early draft of EvoCell Lab for evolving CAs: EvoCellLab 8 | -------------------------------------------------------------------------------- /src/shaders/primitivePalette.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | varying vec2 vTexCoord; 7 | void main(void) { 8 | vec4 color = texture2D(texFrame, vTexCoord); 9 | if (color.a > 0.) 10 | gl_FragColor = vec4(0, color.a*2600.0, color.a*color.a*color.a*8000000.0, 1.); 11 | else 12 | gl_FragColor = vec4(0., 0., 0., 0.); 13 | } 14 | -------------------------------------------------------------------------------- /src/shaders/primitiveRenderer.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform float scale; 7 | const float damp = 0.5; 8 | 9 | varying vec2 vTexCoord; 10 | void main(void) { 11 | vec4 color = texture2D(texFrame, vTexCoord); 12 | if (mod(gl_FragCoord.x, scale) < 1. || mod(gl_FragCoord.y, scale) < 1.) 13 | gl_FragColor = vec4(color.r*damp, color.g*damp, color.b*damp, 1.); 14 | else 15 | gl_FragColor = color; 16 | } 17 | -------------------------------------------------------------------------------- /src/shaders/scroller.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | 7 | uniform vec2 scroll; 8 | 9 | varying vec2 vTexCoord; 10 | void main(void) { 11 | vec2 pos = vTexCoord+scroll; 12 | //if (pos.x > 1.) pos.x -= 1.; 13 | //if (pos.x < 0.) pos.x += 1.; 14 | 15 | //if (pos.y > 1.) pos.y -= 1.; 16 | //if (pos.y < 0.) pos.y += 1.; 17 | 18 | //pos.x = mod(pos.x, 1.); 19 | //pos.y = mod(pos.y, 1.); 20 | 21 | vec4 color = texture2D(texFrame, pos); 22 | gl_FragColor = color; 23 | } -------------------------------------------------------------------------------- /src/shaders/stampPalette.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D texNew; 7 | uniform sampler2D texPalette; 8 | 9 | varying vec2 vTexCoord; 10 | void main(void) { 11 | vec4 color = texture2D(texNew, vTexCoord); 12 | vec4 mappedColor = texture2D(texPalette, vec2(color.a, 0.5)); 13 | vec4 oldColor = texture2D(texFrame, vTexCoord); 14 | 15 | if (mappedColor.a > 0.) 16 | { 17 | gl_FragColor = mappedColor; 18 | } 19 | else 20 | { 21 | gl_FragColor = oldColor; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/shaders/cameraRendererSimple.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D texFrame2; 7 | uniform vec2 resolution; 8 | 9 | varying vec2 vTexCoord; 10 | 11 | const float borderW = 0.15; 12 | 13 | void main(void) { 14 | vec4 color; 15 | color = texture2D(texFrame, vTexCoord); 16 | vec4 color2 = texture2D(texFrame2, vTexCoord); 17 | float damp = color2.a; 18 | damp = 3. * damp * damp; 19 | 20 | if (damp <= 0.) damp = 1.; 21 | 22 | gl_FragColor = vec4(damp * color.rgb, 1.); 23 | } -------------------------------------------------------------------------------- /src/shaders/drawCircle.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | 7 | uniform vec2 center; 8 | uniform float radius; 9 | uniform float state; 10 | 11 | varying vec2 vTexCoord; 12 | void main(void) { 13 | vec4 color = texture2D(texFrame, vTexCoord); 14 | 15 | float dx = center.x - gl_FragCoord.x; 16 | float dy = center.y - gl_FragCoord.y; 17 | float d = sqrt(dx*dx + dy*dy); 18 | 19 | if (d < radius) 20 | { 21 | gl_FragColor = vec4(0., 0., 0., state); 22 | } 23 | else 24 | { 25 | gl_FragColor = color; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/shaders/drawRect.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | 7 | uniform vec2 rectPos; 8 | uniform vec2 rectSize; 9 | uniform float state; 10 | 11 | varying vec2 vTexCoord; 12 | void main(void) { 13 | vec4 color = texture2D(texFrame, vTexCoord); 14 | 15 | if (gl_FragCoord.x >= rectPos.x && gl_FragCoord.x <= rectPos.x + rectSize.x && 16 | gl_FragCoord.y >= rectPos.y && gl_FragCoord.y <= rectPos.y + rectSize.y) 17 | { 18 | gl_FragColor = vec4(0., 0., 0., state); 19 | } 20 | else 21 | { 22 | gl_FragColor = color; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/shaders/mixPalette.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D texNew; 7 | uniform sampler2D texPalette; 8 | 9 | varying vec2 vTexCoord; 10 | void main(void) { 11 | vec4 color = texture2D(texNew, vTexCoord); 12 | vec4 mappedColor = texture2D(texPalette, vec2(color.a, 0.5)); 13 | vec4 oldColor = texture2D(texFrame, vTexCoord); 14 | 15 | if (mappedColor.a > 0.) 16 | { 17 | gl_FragColor = vec4((1.-mappedColor.a) * oldColor.rgb + mappedColor.a * mappedColor.rgb, 1.); 18 | } 19 | else 20 | { 21 | gl_FragColor = oldColor; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/shaders/mixRect.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | 7 | uniform vec2 rectPos; 8 | uniform vec2 rectSize; 9 | uniform vec4 color; 10 | 11 | varying vec2 vTexCoord; 12 | void main(void) { 13 | vec4 oldColor = texture2D(texFrame, vTexCoord); 14 | 15 | if (gl_FragCoord.x >= rectPos.x && gl_FragCoord.x <= rectPos.x + rectSize.x && 16 | gl_FragCoord.y >= rectPos.y && gl_FragCoord.y <= rectPos.y + rectSize.y) 17 | { 18 | gl_FragColor = color.a * color + (1. - color.a) * oldColor; 19 | } 20 | else 21 | { 22 | gl_FragColor = oldColor; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/gl/World.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var World = function(gl) { 4 | this.gl = gl; 5 | this.dishes = {}; 6 | this.shaders = {}; 7 | 8 | this.setup() 9 | this.loader = function(callback) { 10 | //callback(data); 11 | } 12 | 13 | this.mouseDown = function() { 14 | } 15 | this.stepInteract= function() { 16 | } 17 | this.step = function() { 18 | } 19 | this.render = function() { 20 | } 21 | 22 | } 23 | 24 | var SimpleWorld = function() { 25 | 26 | } 27 | 28 | var CellSpaceWorld = new EC.World({ 29 | dishes: { 30 | shipDish: {}, 31 | largerEnemyDish: {width = 1024, height: 512} 32 | 33 | shaders: 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/shaders/cameraRendererTV.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D texFrame2; 7 | uniform vec2 resolution; 8 | 9 | varying vec2 vTexCoord; 10 | 11 | const float borderW = 0.15; 12 | 13 | void main(void) { 14 | vec4 color; 15 | color = texture2D(texFrame, vTexCoord); 16 | vec4 color2 = texture2D(texFrame2, vTexCoord); 17 | float damp = color2.a; 18 | damp = 3. * damp * damp; 19 | 20 | if (damp <= 0.) damp = 1.; 21 | 22 | if (mod(gl_FragCoord.y, 6.) < 3.) { 23 | //damp *= 0.7; 24 | } 25 | else { 26 | damp = 1.; 27 | } 28 | 29 | gl_FragColor = vec4(damp * color.rgb, 1.); 30 | } -------------------------------------------------------------------------------- /src/shaders/cameraRenderer.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D texFrame2; 7 | uniform vec2 resolution; 8 | 9 | varying vec2 vTexCoord; 10 | 11 | const float borderW = 0.15; 12 | 13 | void main(void) { 14 | vec4 color; 15 | color = texture2D(texFrame, vTexCoord); 16 | 17 | float modX = mod(vTexCoord.x*resolution.x, 1.); 18 | float modY = mod(vTexCoord.y*resolution.y, 1.); 19 | if ( modX < borderW || modX > (1.-borderW) || modY < borderW || modY > (1.-borderW)) 20 | { 21 | vec4 color2 = texture2D(texFrame2, vTexCoord); 22 | float damp = color2.a; 23 | //damp /= 2.; 24 | //damp = 0.5; 25 | damp = 3. * damp * damp; 26 | gl_FragColor = vec4(color.r*damp, color.g*damp, color.b*damp, 1.); 27 | } 28 | else 29 | { 30 | //color = texture2D(texFrame, vTexCoord); 31 | gl_FragColor = color; 32 | } 33 | } -------------------------------------------------------------------------------- /src/shaders/copyPasteRect.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform vec2 destinationPos; 7 | uniform vec2 destinationSize; 8 | 9 | uniform sampler2D texSource; 10 | uniform vec2 sourcePos; 11 | uniform vec2 sourceRes; 12 | 13 | varying vec2 vTexCoord; 14 | void main(void) { 15 | vec4 oldColor = texture2D(texFrame, vTexCoord); 16 | //gl_FragColor = oldColor; 17 | 18 | // 19 | 20 | vec4 sourceColor = texture2D(texSource, (gl_FragCoord.xy - destinationPos + sourcePos)/sourceRes); 21 | 22 | 23 | if (gl_FragCoord.x >= destinationPos.x && gl_FragCoord.x <= destinationPos.x + destinationSize.x && 24 | gl_FragCoord.y >= destinationPos.y && gl_FragCoord.y <= destinationPos.y + destinationSize.y) 25 | { 26 | gl_FragColor = sourceColor; 27 | } 28 | else 29 | { 30 | gl_FragColor = vec4(0., 0., 0., 0.); //oldColor; 31 | gl_FragColor = oldColor; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/shaders/intersectSpawn.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | 5 | uniform sampler2D texFrame; 6 | uniform sampler2D tex1; 7 | uniform sampler2D tex2; 8 | 9 | // 10 | const float OP_REPLACE = 0.; 11 | const float OP_ADD = 1.; 12 | 13 | uniform float operation; 14 | uniform float state; 15 | 16 | //uniform float maximum; 17 | 18 | varying vec2 vTexCoord; 19 | void main(void) { 20 | vec4 color1 = texture2D(tex1, vTexCoord); 21 | vec4 color2 = texture2D(tex2, vTexCoord); 22 | vec4 oldColor = texture2D(texFrame, vTexCoord); 23 | float oldState = oldColor.w; 24 | 25 | if (color1.a > 0. && color2.a > 0.) 26 | { 27 | if (operation == OP_REPLACE) 28 | { 29 | gl_FragColor = vec4(0., 0., 0., state); 30 | } 31 | else if (operation == OP_ADD) 32 | { 33 | oldState += state; 34 | if (oldState < 0.) oldState = 0.; 35 | gl_FragColor = vec4(0., 0., 0., oldState); 36 | } 37 | } 38 | else 39 | { 40 | gl_FragColor = oldColor; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Tests.js: -------------------------------------------------------------------------------- 1 | 2 | // document.getElementById("saveGameState").addEventListener("click", function(evt) { 3 | 4 | // fileStore.addObject("gameStates", {id: "test23XXXX", state: {txt: "txt", nr: 123} }, 5 | // function() { alert("works" + 23); }, 6 | // function() { alert("NOT: " + 23); } 7 | // ); 8 | 9 | // }, false); 10 | 11 | // var works = ["yo", 12]; 12 | // var notworks = ["yo", 12]; 13 | 14 | // document.getElementById("saveGameState").addEventListener("click", function(evt) { 15 | // works = []; 16 | // notworks = []; 17 | // _.each(gameState, function(value, key) { 18 | // var clonedState = {}; 19 | // clonedState[key] = value; 20 | 21 | // try { 22 | // fileStore.addObject("gameStates", {id: "test23" + key, state: clonedState }, 23 | // function() { 24 | // // alert("works" + key); 25 | // }, 26 | // function() { 27 | // // alert("NOT: " + key); 28 | // } 29 | // ); 30 | 31 | // works.push("\"" + key + "\""); 32 | // } 33 | // catch (ex) { 34 | // //alert("Except: " + key); 35 | // notworks.push("\"" + key + "\""); 36 | // } 37 | 38 | // }); 39 | // alert(notworks); 40 | // }, false); -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | * State bekommt dishes und rules sub model 2 | ** paint generalize 3 | ** load rule (from db) 4 | ** import/export rule from db 5 | * Cleanup App by splitting CellSpace.js 6 | * variable initial probaility (optional param) 7 | 8 | useful git aliases (for ~/.bashrc or ~/.bash_profile): 9 | # Git shortcuts 10 | alias g='git' 11 | alias gs='git status | more' 12 | alias ga='git add ' 13 | alias gb='git branch ' 14 | alias gc='git commit' 15 | alias gcnv="git commit --no-verify" 16 | alias gd='git diff' 17 | alias go='git checkout ' 18 | alias gl='git log' 19 | alias gh='git log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short' 20 | 21 | 22 | OLD: 23 | ==== 24 | gl context wie geht man damit um? -> Reactor 25 | Klasen identifizieren 26 | 27 | CA stuff unabhaengig von canvas -> nope aber im reactor 28 | 29 | 30 | Usecases: 31 | 32 | ca = loadCA("myRule.ec23") 33 | ca.setSize(1024, 768); 34 | ca.clear(); 35 | ca.loadPattern("myPatern.ec23", 100, 100); 36 | ca.step(); 37 | ca.steps(100); 38 | 39 | 40 | palette = new CAPalette(); 41 | paltette.setColor(0, 255, 0, 0); 42 | 43 | caCanvas = new CACanvas(1500, 300); 44 | caCanvas.setPallette(palette); 45 | caCanvas.cellSize = 5; 46 | caCanvas.drawCA(ca, 100, 500); 47 | 48 | -------------------------------------------------------------------------------- /src/shaders/shieldGenerator.shader: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision highp float; 3 | #endif 4 | uniform sampler2D texShield; 5 | uniform sampler2D texTarget; 6 | uniform float width; 7 | uniform float height; 8 | 9 | varying vec2 vTexCoord; 10 | 11 | float dx = 1./width, dy=1./height; 12 | 13 | void main(void) { 14 | float aMax = 0.; 15 | float aMin = 1.; // smallest val larger 0 16 | float a; 17 | 18 | float selfA = texture2D(texShield, vTexCoord).a; 19 | float targetA = texture2D(texTarget, vTexCoord).a; 20 | 21 | if (targetA > 0.) 22 | { 23 | if (selfA > 0.) 24 | { 25 | gl_FragColor = vec4(0, 0., 0., selfA); 26 | } 27 | else 28 | { 29 | a = texture2D(texShield, vTexCoord + vec2(0, 0)).a; 30 | if (a > 0. && a < aMin) aMin = a; 31 | a = texture2D(texShield, vTexCoord + vec2(dx, 0)).a; 32 | if (a > 0. && a < aMin) aMin = a; 33 | a = texture2D(texShield, vTexCoord + vec2(-dx, 0)).a; 34 | if (a > 0. && a < aMin) aMin = a; 35 | a = texture2D(texShield, vTexCoord + vec2(0, dy)).a; 36 | if (a > 0. && a < aMin) aMin = a; 37 | a = texture2D(texShield, vTexCoord + vec2(0, -dy)).a; 38 | if (a > 0. && a < aMin) aMin = a; 39 | 40 | if (aMin == 1.) aMin = 0.; // no min was found 41 | 42 | gl_FragColor = vec4(0, 0., 0., aMin); 43 | } 44 | } 45 | else 46 | { 47 | gl_FragColor = vec4(0, 0., 0., 0); 48 | } 49 | } -------------------------------------------------------------------------------- /src/gl/Palette.js: -------------------------------------------------------------------------------- 1 | define(["gl/GLHelper"], function(glhelper) { 2 | // RulesTexture and Dishes brauchen gemeinsames Basisobjekt das eine Textur zurueckgibt, RuleTexture ist auch ein shader 3 | // public interface 4 | var Palette = function(reactor, colors) 5 | { 6 | this.reactor = reactor; 7 | this.gl = reactor.gl; 8 | 9 | 10 | this.colors = colors || []; 11 | }; 12 | 13 | Palette.prototype.setColor = function(index, color) 14 | { 15 | this.colors[index] = color; 16 | this.invalidateProgram(); 17 | }; 18 | 19 | Palette.prototype.generateColors = function(timeLine) { 20 | 21 | var oldIdx = -1; 22 | var oldCol = []; 23 | 24 | for (var colIdx in timeLine) { 25 | colIdx = parseInt(colIdx); 26 | var col = timeLine[colIdx]; 27 | 28 | if (oldIdx !== -1) { 29 | var n = colIdx - oldIdx; 30 | for (var curIdx = oldIdx; curIdx <= colIdx; curIdx++) 31 | { 32 | var a = (curIdx-oldIdx)/n; 33 | var ia = 1 - a; 34 | this.setColor(curIdx, [ia*oldCol[0]+a*col[0], ia*oldCol[1]+a*col[1], 35 | ia*oldCol[2]+a*col[2], ia*oldCol[3]+a*col[3]]); 36 | } 37 | } 38 | 39 | oldIdx = colIdx; 40 | oldCol = col; 41 | } 42 | }; 43 | 44 | Palette.prototype.getTexture = function() 45 | { 46 | if (!this.texture) { 47 | var pixels = []; 48 | for(var j = 0; j < 256; j++) // x axis 49 | { 50 | var color = this.colors[j]; 51 | if (color) 52 | { 53 | pixels.push(color[0]); 54 | pixels.push(color[1]); 55 | pixels.push(color[2]); 56 | pixels.push(color[3]); 57 | } 58 | else 59 | pixels = pixels.concat([0,1,0,1]); 60 | } 61 | 62 | this.texture = glhelper.createRGBATexture(this. gl, 256, 1, new Uint8Array(pixels)); 63 | } 64 | return this.texture; 65 | }; 66 | 67 | Palette.prototype.invalidateProgram = function() 68 | { 69 | this.ruleTexture = null; 70 | }; 71 | 72 | return Palette; 73 | }); 74 | -------------------------------------------------------------------------------- /src/gl/GLHelper.js: -------------------------------------------------------------------------------- 1 | define(function() { 2 | // used state frames 3 | // RGBA for now because ALPHA textures can not be rendered into 4 | function createRGBATexture(gl, width, height, pixels) 5 | { 6 | var texture = gl.createTexture(); 7 | gl.bindTexture(gl.TEXTURE_2D, texture); 8 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); 9 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 10 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 11 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 12 | 13 | var mode = (isPOT(width) && isPOT(height)) ? gl.REPEAT : gl.CLAMP_TO_EDGE; 14 | 15 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, mode); 16 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, mode); 17 | 18 | return texture; 19 | } 20 | 21 | function isPOT(nr) { 22 | var p = 1; 23 | while (p < nr) { 24 | p *= 2; 25 | } 26 | return p == nr; 27 | } 28 | 29 | // used by the ruletable 30 | // ALPHA only to save memory 31 | function createAlphaTexture(gl, width, height, pixels) 32 | { 33 | var texture = gl.createTexture(); 34 | gl.bindTexture(gl.TEXTURE_2D, texture); 35 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); 36 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, pixels); 37 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 38 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 39 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 40 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 41 | return texture; 42 | } 43 | 44 | function getShader(gl, shaderType, sourceText) 45 | { 46 | var shader; 47 | shader = gl.createShader(shaderType); 48 | gl.shaderSource(shader, sourceText); 49 | gl.compileShader(shader); 50 | if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0) 51 | { 52 | alert(gl.getShaderInfoLog(shader)); 53 | alert(sourceText); 54 | } 55 | return shader; 56 | } 57 | 58 | return { 59 | createRGBATexture: createRGBATexture, 60 | createAlphaTexture: createAlphaTexture, 61 | getShader: getShader, 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /src/CellSpace.js: -------------------------------------------------------------------------------- 1 | var perfStartedJSTime = performance.now(); 2 | 3 | require.config({ 4 | baseUrl: 'src', 5 | paths: { 6 | jquery: '../../res_evocell/src/libs/jquery-1.10.2', 7 | "jquery-ui": '../../res_evocell/src/libs/jquery-ui-1.10.4.custom', 8 | "underscore": "../../res_evocell/src/libs/underscore", 9 | backbone: "../../res_evocell/src/libs/backbone", 10 | knockback: "../../res_evocell/src/libs/knockback", 11 | knockout: "../../res_evocell/src/libs/knockout-3.0.0", 12 | meSpeak: "../../res_evocell/src/libs/mespeak/mespeak", 13 | three: "../../res_evocell/src/libs/three", 14 | "jquery-cycle": "../../res_evocell/src/libs/jquery.cycle.all", 15 | datgui: "../../res_evocell/src/libs/dat.gui.min", 16 | FileSaver: "../../res_evocell/src/libs/FileSaver", 17 | }, 18 | shim: { 19 | datgui: { 20 | exports: "dat", 21 | }, 22 | "jquery-ui": { 23 | exports: "$", 24 | deps: ['jquery', '../../res_evocell/src/libs/farbtastic'] 25 | }, 26 | "jquery-cycle": { 27 | deps: ["jquery-ui"] 28 | }, 29 | underscore : { 30 | exports: "_", 31 | }, 32 | backbone : { 33 | exports: "Backbone", 34 | deps: ['underscore'], 35 | }, 36 | knockback: { 37 | exports: "kb", 38 | deps: ["backbone"], 39 | }, 40 | knockout: { 41 | exports: "ko", 42 | }, 43 | meSpeak: { 44 | exports: "meSpeak", 45 | }, 46 | three: { 47 | exports: "THREE", 48 | }, 49 | } 50 | }); 51 | 52 | 53 | require([ 54 | "jquery-ui", "Utils", "EvoCell", "story/StoryTeller", "underscore", 55 | "backbone", "knockback", "knockout", "data/FileStore", "three", "datgui", 56 | "CellSpace/State", "CellSpace/Setup", "CellSpace/GameLoop", "CellSpace/GUI", "CellSpace/Utils"], 57 | function($, utils, EC, storyTeller,_ , Backbone, kb, ko, fileStore, THREE, dat, 58 | gameState, csSetup, csGame, csUI, csUtils) { 59 | "use strict"; 60 | 61 | gameState.perfRequireTime = performance.now(); 62 | gameState.perfStartedJSTime = perfStartedJSTime; 63 | 64 | // MAIN LOOP (must be less than 20 LOC :) 65 | 66 | // TODO: extract this in CellSpace.App which bundles State, Setup, UI, ... 67 | 68 | // TODO: should we not put this in backbone ready function? 69 | // is jquery ready better? 70 | //$(window).load(function(e) { 71 | var canvas = document.getElementById('c'); 72 | 73 | csSetup.setup(canvas, function () { 74 | csUI.setupGui(); 75 | 76 | gameState.mainLoop = new utils.AnimationLoop(0, function() { 77 | csUI.pollUserInteraction(); 78 | if (!gameState.pause || gameState.doOneStep) { 79 | csGame.step(); 80 | gameState.doOneStep = false; 81 | } 82 | csGame.render(); 83 | }); 84 | 85 | gameState.perfFinishedJSTime = performance.now(); 86 | csUtils.refreshGUI(["perfStartedJSTime","perfFinishedJSTime", "perfRequireTime"]); 87 | 88 | gameState.mainLoop.start(); 89 | }); 90 | //}); 91 | }); 92 | -------------------------------------------------------------------------------- /src/CellSpace/State.js: -------------------------------------------------------------------------------- 1 | define(["backbone", "three", "datgui", "Utils"], function(Backbone, THREE, dat, utils) { 2 | "use strict"; 3 | 4 | 5 | // // Define a Todo Model 6 | // var Layer = Backbone.Model.extend({ 7 | // // Default todo attribute values 8 | // defaults: { 9 | // name: "no name given", 10 | // palette: null, 11 | // rule: null, 12 | // dish: null, 13 | // } 14 | // }); 15 | 16 | 17 | //var Layers = 18 | 19 | // Instantiate the Todo Model with a title, with the completed attribute 20 | // defaulting to false 21 | // var myTodo = new Todo({ 22 | // title: 'Check attributes property of the logged models in the console.' 23 | // }); 24 | 25 | var resPath = "../res_evocell/"; 26 | var libPath = "../" + resPath + "src/libs/"; 27 | 28 | 29 | var gameState = { 30 | // APPSTATE /////////// 31 | canvas: null, 32 | reactor: null, 33 | gl: null, 34 | renderLoop: null, 35 | gui: new dat.GUI(), 36 | //fpsMonotor: null, 37 | keyboard: utils.keyboard, 38 | 39 | shaders: {}, 40 | dishes: {}, 41 | rules: {}, 42 | colors: {}, 43 | shots: null, 44 | 45 | pause: false, 46 | doOnestep: false, 47 | 48 | // GAMESTATE ////////// 49 | // shots 50 | maxParticles: 1000, 51 | shotSpeed: 3.4, 52 | frontShots: 3, 53 | frontShotAngle: 0.2, 54 | shotN: 8, 55 | bombFired: 0, 56 | bAngle: 0, // direction of bomb fire 57 | 58 | autoFireCounter: 0, 59 | autoFireOn: 0, 60 | lastMouseNDC: new THREE.Vector2(), 61 | 62 | gameW: 256, gameH: 256, 63 | screenW: window.innerWidth ? window.innerWidth : 1000, 64 | screenH: window.innerHeight ? window.innerHeight :800, 65 | 66 | randomDensity: 0.001, 67 | enemySpeed: 0.5, 68 | weaponExplosionParam: 3, 69 | 70 | zoom: 1/3, // cell/pixel ratio 71 | rot: 0, 72 | 73 | cameraAngle: 60 * (Math.PI/180), 74 | viewMatrix: new THREE.Matrix4(), 75 | projectionMatrix: new THREE.Matrix4(), 76 | 77 | enableScrolling: 1, 78 | renderer: "Simple", // "Cell", "TV", "Simple", "Fast" 79 | shipX: 0, shipY: 0, 80 | shipRadius: 3, 81 | shipDir: 0, 82 | shipSpeed: 0, 83 | shipSpeedX: 0, shipSpeedY: 0, // derived values 84 | 85 | scrollX: 0, scrollY: 0, 86 | parallaxX: 0, parallaxY: 0, 87 | playerEnergy: 1000, 88 | stepSize: 1.5, 89 | 90 | mouseMode: "shoot", 91 | // HACK: find better init solution 92 | cnt: 100, // used for executing dishes.enemy only every nth tep 93 | 94 | sndInit: new Audio(resPath + "sound/Digital_SFX_Set/laser3.mp3"), 95 | snd: new Audio(resPath + "sound/Digital_SFX_Set/laser6.mp3"), 96 | sndBomb: new Audio(resPath + "sound/Digital_SFX_Set/laser4.mp3"), 97 | sndHit: new Audio(resPath + "sound/Digital_SFX_Set/laser9.mp3"), 98 | sndHit2: new Audio(resPath + "sound/Digital_SFX_Set/laser9.mp3"), 99 | 100 | // game model 101 | civX: 0.1, 102 | civY: 0.1, 103 | civZ: 0.1, 104 | civW: 0.1, 105 | 106 | clipX: 0.1, 107 | clipY: 0.1, 108 | 109 | drawModel: new Backbone.Model({ 110 | availableRules: [], 111 | selectedRules: [], 112 | 113 | // TODO: should all be populated 114 | availableLayers: [], // gets populated 115 | availableStates: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], 116 | 117 | drawSizeX: 30, 118 | drawSizeY: 30, 119 | selectedDrawShape : "circle", 120 | selectedStates : [3], 121 | selectedLayers : ["enemy"], 122 | }), 123 | 124 | selection : { 125 | active: false, 126 | size: [0, 0], 127 | pos:[0, 0], 128 | downPos: [0, 0], 129 | lastPos: [0, 0], 130 | }, 131 | 132 | // Example: local resources in the same directory 133 | // resPath: "./" 134 | // resources in res_evocell repository 135 | resPath: resPath, 136 | libPath: libPath, 137 | 138 | // performance measures 139 | perfStartJSTime: -0.001, 140 | perfRequireTime: -0.001, 141 | perfFinishedJSTime: -0.001, 142 | showBuffer: false, 143 | showRule: false, 144 | }; 145 | 146 | return gameState; 147 | }); -------------------------------------------------------------------------------- /src/gl/Dish.js: -------------------------------------------------------------------------------- 1 | define(["gl/GLHelper"], function(glhelper) { 2 | // Rules and Dishes brauchen gemeinsames Basisobjekt das eine Textur zurueckgibt 3 | Dish = function(reactor, w, h) 4 | { 5 | this.gl = reactor.gl; 6 | this.setSize(w, h); 7 | } 8 | 9 | Dish.prototype.flip = function() 10 | { 11 | this.frameFlip = -this.frameFlip; 12 | } 13 | 14 | Dish.prototype.setSize = function(w, h) 15 | { 16 | if (!this.texture1) 17 | { 18 | this.gl.deleteTexture(this.texture1); 19 | } 20 | if (!this.texture2) 21 | { 22 | this.gl.deleteTexture(this.texture2); 23 | } 24 | 25 | if (!this.fb1) 26 | { 27 | this.gl.deleteFramebuffer(this.fb1); 28 | } 29 | if (!this.fb2) 30 | { 31 | this.gl.deleteFramebuffer(this.fb2); 32 | } 33 | 34 | this.width = w; 35 | this.height = h; 36 | 37 | this.texture1 = createDishTexture(this.gl, w, h, 0); 38 | this.texture2 = createDishTexture(this.gl, w, h, 0); 39 | 40 | this.fb1 = this.gl.createFramebuffer(); 41 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fb1); 42 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, this.texture1, 0); 43 | 44 | this.fb2 = this.gl.createFramebuffer(); 45 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fb2); 46 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, this.texture2, 0); 47 | 48 | this.frameFlip = 1; 49 | } 50 | 51 | Dish.prototype.setAll = function(state) { 52 | this.randomize(1, 0); // HACK!!!! TODO: fix clearshader! 53 | } 54 | 55 | Dish.prototype.randomize = function(nrStates, density) 56 | { 57 | var texture = createDishTextureRandom(this.gl, this.width, this.height, nrStates, density); 58 | var fb; 59 | 60 | if (this.frameFlip > 0) 61 | { 62 | this.gl.deleteTexture(this.texture1); 63 | fb = this.fb1; 64 | this.texture1 = texture; 65 | } 66 | else 67 | { 68 | this.gl.deleteTexture(this.texture2); 69 | fb = this.fb2; 70 | this.texture2 = texture; 71 | } 72 | 73 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, fb); 74 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, texture, 0); 75 | } 76 | 77 | Dish.prototype.getCurrentTexture = function() 78 | { 79 | return (this.frameFlip > 0) ? this.texture1 : this.texture2; 80 | } 81 | 82 | Dish.prototype.getCurrentFramebuffer = function() 83 | { 84 | return (this.frameFlip > 0) ? this.fb1 : this.fb2; 85 | } 86 | 87 | Dish.prototype.getNextFramebuffer = function() 88 | { 89 | return (this.frameFlip > 0) ? this.fb2 : this.fb1; 90 | } 91 | 92 | function createDishTexture(gl, width, height, state) 93 | { 94 | var pixels = []; 95 | for(var i = 0; i < height; i++) // y axis 96 | { 97 | for(var j = 0; j < width; j++) // x axis 98 | { 99 | pixels.push(0); 100 | pixels.push(0); 101 | pixels.push(0); 102 | pixels.push(state); 103 | } 104 | } 105 | // must be RGBA otherwise we cant use it as color attachment of the framebuffer 106 | return glhelper.createRGBATexture(gl, width, height, new Uint8Array(pixels)) 107 | } 108 | 109 | function createDishTextureRandom(gl, width, height, states, density) 110 | { 111 | var pixels = []; 112 | for(var i = 0; i < height; i++) // y axis 113 | { 114 | for(var j = 0; j < width; j++) // x axis 115 | { 116 | if (Math.random() < density) 117 | { 118 | pixels.push(0); 119 | pixels.push(0); 120 | pixels.push(0); 121 | pixels.push(Math.floor(Math.random()*states)); 122 | } 123 | else 124 | { 125 | pixels.push(0); 126 | pixels.push(0); 127 | pixels.push(0); 128 | pixels.push(0); 129 | } 130 | } 131 | } 132 | // must be RGBA otherwise we cant use it as color attachment of the framebuffer 133 | return glhelper.createRGBATexture(gl, width, height, new Uint8Array(pixels)) 134 | } 135 | 136 | /* 137 | function createFrameTextureFromPattern(gl, evoCellData, width, height) 138 | { 139 | var pixels = []; 140 | for(var i = 0; i < height; i++) // y axis 141 | { 142 | for(var j = 0; j < width; j++) // x axis 143 | { 144 | if (j < evoCellData.patternWidth && i < evoCellData.patternHeight) 145 | { 146 | pixels.push(evoCellData.patternData[i*evoCellData.patternWidth+j]); 147 | } 148 | else pixels.push(0); 149 | } 150 | } 151 | return createCATexture(gl, width, height, new Uint8Array(pixels)); 152 | } 153 | */ 154 | return Dish; 155 | }); 156 | 157 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family:sans-serif; 3 | background:rgba(0,0,0,1); 4 | font-size: 0.7em; 5 | } 6 | 7 | #toolContainer { 8 | xtransition-property: all; 9 | xtransition-duration: 2.5s; 10 | xtransition-timing-function: ease; 11 | xtransition-delay: 0s; 12 | 13 | overflow:hidden; 14 | width:30em; 15 | position: fixed; top: 0px;left: 0px; z-index:10; 16 | color: white; 17 | 18 | z-index: 120; 19 | padding: 0; 20 | margin: 0; 21 | } 22 | 23 | .toolWindow { 24 | transition-property: max-height; 25 | transition-duration: 0.5s; 26 | transition-timing-function: ease; 27 | transition-delay: 0s; 28 | overflow:hidden; 29 | max-height:2.7em; /*(padding == 2*0.4 zaehlen nicht -0.8em) 2em + 2 * 2* 0.1 paddig + 2* 0.2 margin unten */ 30 | color: white; 31 | background:rgba(255, 0, 204, .3); 32 | z-index: 125; 33 | margin: 0; 34 | padding: 0.4em; 35 | } 36 | 37 | /* 38 | #toolsMenu { 39 | padding: 0.2em; 40 | margin: 0.2em; 41 | border: 0px; 42 | position: fixed; bottom: 0px;right: 0px; z-index:2; 43 | color: white; width: 400px; 44 | background:rgba(0,0,0,1); 45 | } 46 | 47 | #toolsMenu a { 48 | 49 | } 50 | */ 51 | 52 | .toolMenuContent { 53 | background:rgba(0,0,0,0.85); 54 | margin: 0; 55 | padding: 0.4em; 56 | } 57 | 58 | 59 | .toolWindow:hover { 60 | background:rgba(255, 0, 204, .6); 61 | } 62 | 63 | .activeWindow { 64 | max-height: 1200px; 65 | background:rgba(255, 0, 204, .6); 66 | } 67 | /* 2 + 0.4 oben + 0.2 marg + 0.1pad = 2.7*/ 68 | .toolMenuHeader { 69 | background:rgba(0,0,0,0.8); 70 | margin: 0 0 0.2em 0; 71 | padding: 0.1em; 72 | cursor:pointer; 73 | font-size: 2em; 74 | 75 | background-image:url("../../res_evocell/images/icons/icons/plus.png"); 76 | background-size: 1.4em; 77 | background-repeat:no-repeat; 78 | background-position:right top; 79 | } 80 | 81 | /*.activeWindow:nth-child(1) {*/ 82 | .activeWindow .toolMenuHeader { 83 | background:rgba(0,0,0, .5); 84 | background-image:url("../../res_evocell/images/icons/icons/minus.png"); 85 | background-size: 1.4em; 86 | background-repeat:no-repeat; 87 | background-position:right top; 88 | } 89 | 90 | 91 | /*#aboutWindow {*/ 92 | /*transition-property: max-height;*/ 93 | /*transition-duration: 0.3s;*/ 94 | /*transition-timing-function: ease;*/ 95 | /*transition-delay: 0s;*/ 96 | 97 | /*overflow:hidden;*/ 98 | /*width: 100%;*/ 99 | /*max-height:4.2em;*/ 100 | /*color: white;*/ 101 | /*background:rgba(255,0,0,0.2);*/ 102 | 103 | /*z-index: 120; */ 104 | /*}*/ 105 | 106 | #aboutWindow:hover { 107 | max-height: 1200px; 108 | } 109 | 110 | 111 | .buttons { 112 | color: white; 113 | } 114 | 115 | .buttons:hover{ 116 | text-shadow: 0 0 1.0em yellow 117 | } 118 | 119 | .buttons:active{ 120 | color: black; 121 | } 122 | 123 | .storyElement { 124 | kjkjjposition: relative; 125 | display: none; 126 | color: white; 127 | } 128 | 129 | .xxxstoryElement h2 { 130 | padding: 5px 5px 5px 5px; 131 | position: absolute; 132 | bottom: 30px; 133 | right: 200px; 134 | width: 400px; 135 | color: white; 136 | background:rgba(0,0,0,0.7); 137 | 138 | display: inline; 139 | float: none; 140 | } 141 | 142 | .storyElement img { 143 | height: 75%; 144 | } 145 | 146 | 147 | select { 148 | font-size:9pt; 149 | letter-spacing:0.07em; 150 | color: white; 151 | background:transparent; 152 | border: solid 1px #808080; 153 | padding:3px; 154 | cursor:pointer; 155 | } 156 | select option:checked { 157 | } 158 | select option:nth-child(even) { } 159 | 160 | 161 | #centeredContainer { 162 | position: absolute; 163 | top: 50%; 164 | left: 0; 165 | width: 100%; 166 | margin-top: -10px; 167 | line-height: 20px; 168 | text-align: center; 169 | vertical-align: center; 170 | z-index: 110; 171 | background:rgba(255,255,255, 0); 172 | display: none; 173 | } 174 | 175 | #centeredMessage { 176 | padding: 100px; 177 | width:600px; 178 | background:rgba(255,0, 0, 0.8); 179 | display: inline-block; 180 | font-size: 3em; 181 | line-height: 1em; 182 | } 183 | 184 | .fileUpload { 185 | position: relative; 186 | overflow: hidden; 187 | margin: 10px; 188 | padding: 10px; 189 | } 190 | 191 | .fileUpload input.upload { 192 | position: absolute; 193 | top: 0; 194 | right: 0; 195 | margin: 0; 196 | padding: 0; 197 | font-size: 20px; 198 | cursor: pointer; 199 | opacity: 0; 200 | filter: alpha(opacity=0); 201 | } 202 | -------------------------------------------------------------------------------- /src/data/FileStore.js: -------------------------------------------------------------------------------- 1 | define(["underscore", "backbone"], function(_, bb) { 2 | var indexedDB = window.indexedDB; 3 | 4 | const dbName = "EvoCell"; 5 | var db = null; 6 | 7 | const ruleStoreName = "rules"; 8 | var ruleStore = null; 9 | 10 | var readyFns = []; 11 | 12 | var ready = function(fn) { 13 | if (db) fn(); 14 | else { 15 | readyFns.push(fn); 16 | } 17 | }; 18 | 19 | var request = indexedDB.open(dbName, 4); 20 | request.onerror = function(event) { 21 | alert("Oh no! Why didn't you allow my web app to use IndexedDB?! Cell is sad now!"); 22 | }; 23 | request.onsuccess = function(event) { 24 | db = event.target.result; 25 | 26 | _.each(readyFns, function(fn) { fn(); }); 27 | 28 | db.onerror = function(event) { 29 | // Generic error handler for all errors targeted at this database's requests! 30 | alert("Database error: " + event.target.errorCode); 31 | }; 32 | }; 33 | 34 | request.onupgradeneeded = function(event) { 35 | //alert("upgrade from: " + event.oldVersion + " to: " + event.newVersion); 36 | var db = event.target.result; 37 | 38 | if(!db.objectStoreNames.contains(ruleStoreName)) { 39 | var objectStore = db.createObjectStore(ruleStoreName, { keyPath: "name"}); 40 | } 41 | // else 42 | // { 43 | // db.deleteObjectStore(ruleStoreName); 44 | // } 45 | 46 | if(!db.objectStoreNames.contains("gameStates")) { 47 | db.createObjectStore("gameStates", { keyPath: "id"}); 48 | } 49 | // Create an index to search customers by name. We may have duplicates 50 | // so we can't use a unique index. 51 | //objectStore.createIndex("name", "name", { unique: true }); 52 | }; 53 | 54 | var addObject = function(storeName, data, oncomplete, onerror) { 55 | var transaction = db.transaction([storeName], "readwrite"); 56 | if (oncomplete) transaction.oncomplete = oncomplete; 57 | if (onerror) transaction.onerror = onerror; 58 | var objectStore = transaction.objectStore(storeName); 59 | var request = objectStore.add(data); 60 | request.onerror = function(event) { 61 | //alert("me so sorry, could not add: " + data + "\n" + storeName); 62 | }; 63 | request.onsuccess = function(event) { 64 | // dont call callback here call it later when transaction is done! 65 | }; 66 | }; 67 | 68 | var deleteObject = function(storeName, id, callback) { 69 | var transaction = db.transaction([storeName], "readwrite"); 70 | transaction.oncomplete = function(e) { 71 | if (callback) callback(); 72 | }; 73 | var request = transaction 74 | .objectStore(storeName) 75 | .delete(id); 76 | request.onerror = function(event) { 77 | alert("me so sorry, could not delte object id:" + id); 78 | }; 79 | request.onsuccess = function(event) { 80 | // It's gone! 81 | }; 82 | }; 83 | var getObject = function(storeName, id, callback) { 84 | var objectStore = db.transaction([storeName]).objectStore(storeName); 85 | var request = objectStore.get(id); 86 | request.onerror = function(event) { 87 | alert("me so sorry, could not load object with id name:" + id); 88 | }; 89 | request.onsuccess = function(event) { 90 | var result = event.target.result; 91 | if (!result) 92 | alert("Empty result for id: " + id); 93 | callback(result); 94 | }; 95 | }; 96 | var getAllObjects = function(storeName, callback, filter) { 97 | filter = filter || function(x) { return true; }; 98 | var objects = []; 99 | var objectStore = db.transaction(ruleStoreName).objectStore(storeName); 100 | 101 | objectStore.openCursor().onsuccess = function(event) { 102 | var cursor = event.target.result; 103 | if (cursor) { 104 | if (filter(cursor.value)) 105 | objects.push(cursor.value); 106 | cursor.continue(); 107 | } 108 | else { 109 | callback(objects); 110 | } 111 | }; 112 | }; 113 | 114 | var storeRule = function(name, ruleData, callback) { 115 | addObject(ruleStoreName, {name: name, ruleData: ruleData}, callback); 116 | }; 117 | 118 | var deleteRule = function(name, callback) { 119 | deleteObject(ruleStoreName, name, callback); 120 | }; 121 | 122 | var loadRule = function(name, callback) { 123 | getObject(ruleStoreName, name, callback); 124 | }; 125 | 126 | var loadAllRules = function(callback) { 127 | getAllObjects(ruleStoreName, callback); 128 | }; 129 | 130 | var loadAllRuleNames = function(callback) { 131 | getAllObjects(ruleStoreName, function(rules) { 132 | callback(_.map(rules, function(rule) { return rule.name; })); 133 | }); 134 | } ; 135 | 136 | return { 137 | addObject: addObject, 138 | deleteObject: deleteObject, 139 | getObject: getObject, 140 | getAllObjects: getAllObjects, 141 | 142 | // depricated "rule" functions use addObject, deleteObject, ... insted 143 | storeRule: storeRule, 144 | deleteRule: deleteRule, 145 | loadRule: loadRule, 146 | loadAllRules: loadAllRules, 147 | loadAllRuleNames: loadAllRuleNames, 148 | 149 | ready: ready, 150 | }; 151 | }); 152 | -------------------------------------------------------------------------------- /src/gl/ParticleSystem.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | var ParticleSystem = function(reactor, n, width, height) { 3 | this.reactor = reactor; 4 | var gl = reactor.gl; 5 | this.gl = gl; 6 | this.n = n; 7 | this.width = width; 8 | this.height = height; 9 | 10 | var pointAlive = new Uint8Array(n); 11 | var pointCoordinates = new Float32Array(2*n); 12 | var pointSpeeds = new Float32Array(2*n); 13 | 14 | this.pointCoordinates = pointCoordinates; 15 | this.pointSpeeds = pointSpeeds; 16 | //var pointAlive = new Array(n); 17 | 18 | for (var i = 0; i < n; i++) 19 | { 20 | pointAlive[i] = 1; 21 | pointSpeeds[2*i] = 0; 22 | pointSpeeds[2*i+1] = 0; 23 | pointCoordinates[2*i] = 10; // outside of bound 24 | pointCoordinates[2*i+1] = 0; 25 | } 26 | 27 | var pixelValues = new Uint8Array(width*height*4); // for colission detection 28 | this.pixelValues = pixelValues; 29 | 30 | var pointsBuffer = gl.createBuffer(); 31 | this.pointsBuffer = pointsBuffer; 32 | gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer); 33 | gl.bufferData(gl.ARRAY_BUFFER, pointCoordinates.byteLength, gl.STATIC_DRAW); 34 | gl.bufferSubData(gl.ARRAY_BUFFER, 0, pointCoordinates); 35 | }; 36 | 37 | ParticleSystem.prototype.draw = function(shader, dish, offsetx, offsety) { 38 | var gl = this.gl; 39 | gl.bindBuffer(gl.ARRAY_BUFFER, this.pointsBuffer); 40 | gl.bufferData(gl.ARRAY_BUFFER, this.pointCoordinates.byteLength, gl.STATIC_DRAW); 41 | gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.pointCoordinates); 42 | 43 | var psContext = this; 44 | this.reactor.applyShader(shader, dish.getCurrentFramebuffer(), false, function(gl, shader) { 45 | //gl.bindBuffer(gl.ARRAY_BUFFER, pointsBuffer); 46 | var pointPosLoc = gl.getAttribLocation(shader, "pointPos"); 47 | gl.enableVertexAttribArray(pointPosLoc); 48 | gl.vertexAttribPointer(pointPosLoc, 2, gl.FLOAT, gl.FALSE, 0, 0); 49 | 50 | // var pointAliveLoc = gl.getAttribLocation(shader, "pointAlive"); 51 | // gl.enableVertexAttribArray(pointAliveLoc); 52 | // gl.vertexAttribPointer(pointAliveLoc, 1, gl.UNSIGNED_BYTE, gl.FALSE, 0, 0); 53 | 54 | gl.uniform1f(gl.getUniformLocation(shader, "state"), 3/255); 55 | 56 | gl.uniform2f(gl.getUniformLocation(shader, "offset"), offsetx/psContext.width, offsety/psContext.height); 57 | 58 | gl.drawArrays(gl.POINTS,0, psContext.pointCoordinates.length/2); 59 | }); 60 | }; 61 | 62 | ParticleSystem.prototype.step = function() { 63 | // move 64 | for (var i = 0; i < this.n; i++) 65 | { 66 | this.pointCoordinates[2*i] += this.pointSpeeds[2*i]; 67 | this.pointCoordinates[2*i+1] += this.pointSpeeds[2*i+1]; 68 | } 69 | }; 70 | 71 | ParticleSystem.prototype.collide = function (dish, offsetx, offsety, cb, timeShift) { 72 | timeShift = timeShift || 0; 73 | 74 | var gl = this.gl; 75 | 76 | gl.bindFramebuffer(gl.FRAMEBUFFER, dish.getCurrentFramebuffer()); 77 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, dish.getCurrentTexture(), 0); 78 | gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, this.pixelValues); 79 | 80 | 81 | var soffsetx = offsetx / this.width; 82 | var soffsety = offsety / this.height; 83 | 84 | for (var i = 0; i < this.n; i++) 85 | { 86 | var pX = Math.round(offsetx + this.width*0.5*(this.pointCoordinates[2*i]+1 + timeShift * this.pointSpeeds[2*i])); 87 | var pY = Math.round(offsety + this.height*0.5*(this.pointCoordinates[2*i+1]+1 + timeShift * this.pointSpeeds[2*i+1])); 88 | 89 | if (pX >= 0 && pX < this.width && pY >= 0 && pY < this.height) { 90 | if (this.pixelValues[(pX+pY*this.width)*4 + 3] !== 0) { 91 | if (cb) cb([pX, pY]); 92 | this.pointCoordinates[2*i] = 10.0; // out of range 93 | this.pointCoordinates[2*i+1] = 10.0; 94 | this.pointSpeeds[2*i] = 0; 95 | this.pointSpeeds[2*i+1] = 0; 96 | } 97 | } 98 | } 99 | 100 | return this.pixelValues; 101 | }; 102 | 103 | var allocIdx = 0; 104 | ParticleSystem.prototype.allocateParticle = function(x, y, xs, ys) { 105 | var xScale = 2/this.width; 106 | var yScale = 2/this.height; 107 | 108 | x = xScale * x - 1; 109 | y = yScale * y - 1; 110 | xs = xScale * xs; 111 | ys = yScale * ys; 112 | 113 | for (var j = 0; j < this.n; j++) 114 | { 115 | var i = (j+allocIdx)%this.n; 116 | var xx = this.pointCoordinates[2*i]; 117 | var yy = this.pointCoordinates[2*i+1]; 118 | if (xx < -1 || xx > 1 || yy < -1 || yy > 1) 119 | { 120 | this.pointCoordinates[2*i] = x; 121 | this.pointCoordinates[2*i + 1] = y; 122 | 123 | this.pointSpeeds[2*i] = xs; 124 | this.pointSpeeds[2*i + 1] = ys; 125 | 126 | allocIdx = i+1; 127 | break; 128 | } 129 | } 130 | }; 131 | 132 | ParticleSystem.prototype.allocateSphere = function(n, x, y, s, angle, shipSpeedX, shipSpeedY) { 133 | this.allocateParticles(n, function(i, n) { 134 | return [x, y, 135 | s * Math.cos(angle+Math.PI*2*i/n) + shipSpeedX, 136 | s * Math.sin(angle+Math.PI*2*i/n) + shipSpeedY 137 | ]; 138 | }); 139 | }; 140 | 141 | ParticleSystem.prototype.allocateParticles = function(n, generatorFn) { 142 | for (var i = 0; i < n; i++) { 143 | var params = generatorFn(i, n); 144 | this.allocateParticle.apply(this, params); 145 | } 146 | }; 147 | 148 | return ParticleSystem; 149 | }); 150 | -------------------------------------------------------------------------------- /src/gl/Rule.js: -------------------------------------------------------------------------------- 1 | define(["gl/GLHelper"], function(glhelper) { 2 | // RulesTexture and Dishes brauchen gemeinsames Basisobjekt das eine Textur zurueckgibt, RuleTexture ist auch ein shader 3 | // public interface 4 | var Rule = function(reactor, ruleData, dish) 5 | { 6 | this.reactor = reactor; 7 | this.gl = reactor.gl; 8 | 9 | this.setRule(ruleData); 10 | // candy 11 | if (dish) 12 | this.setCompileSizeDish(dish); 13 | }; 14 | 15 | Rule.prototype.setRule = function(ruleData) 16 | { 17 | this.ruleData = ruleData; 18 | 19 | 20 | if (ruleData.containsRule) 21 | { 22 | var xN = Math.ceil(ruleData.nrNeighbours/2); 23 | var yN = Math.floor(ruleData.nrNeighbours/2); 24 | var width = Math.pow(ruleData.nrStates, xN); 25 | var height = Math.pow(ruleData.nrStates, yN); 26 | 27 | this.nrStates = ruleData.nrStates; 28 | this.ruleTexture = glhelper.createAlphaTexture(this.gl, width, height, ruleData.ruleTable); 29 | 30 | this.width = width; 31 | this.height = height; 32 | 33 | this.invalidateProgram(); 34 | } 35 | if (ruleData.containsPattern) 36 | { 37 | this.patternTexture = glhelper.createAlphaTexture(this.gl, 38 | ruleData.patternWidth, ruleData.patternHeight, ruleData.patternData); 39 | } 40 | }; 41 | 42 | Rule.prototype.getPatternTexture = function() { 43 | return this.patternTexture; 44 | }; 45 | 46 | // candy 47 | Rule.prototype.setCompileSizeDish = function(dish) { 48 | this.setCompileSize(dish.width, dish.height); 49 | }; 50 | 51 | Rule.prototype.setCompileSize = function(w, h) 52 | { 53 | this.compileWidth = w; 54 | this.compileHeight = h; 55 | 56 | this.invalidateProgram(); 57 | }; 58 | 59 | Rule.prototype.getTexture = function() 60 | { 61 | return this.ruleTexture; 62 | }; 63 | 64 | Rule.prototype.getProgram = function() 65 | { 66 | if (this.program == null) 67 | { 68 | if (this.compileWidth == null || this.compileHeight == null || this.ruleData == null) 69 | { 70 | throw "You have to set the width and height and ruledata of the Rule object to compile a shader"; 71 | } 72 | 73 | this.program = this.reactor.compileShader(getFragmentShaderSourceFromEvoCellData(this.gl, this.ruleData, this.compileWidth, this.compileHeight)); 74 | } 75 | 76 | return this.program; 77 | } 78 | 79 | // private 80 | 81 | Rule.prototype.invalidateProgram = function() 82 | { 83 | this.program = null; 84 | } 85 | 86 | ////////////////////////////////////////////////////////// 87 | // Static functions 88 | ////////////////////////////////////////////////////////// 89 | 90 | var EvoCellFragmentShaderTemplate = 91 | "#ifdef GL_ES\n" + 92 | "precision highp float;\n" + 93 | "#endif\n" + 94 | " uniform sampler2D texFrame;\n" + 95 | " uniform sampler2D texRule;\n" + 96 | " \n" + 97 | " varying vec2 vTexCoord;\n" + 98 | " const float dx = 1./%XRES%., dy=1./%YRES%.;\n" + 99 | " const float stateScale = 255.;\n" + 100 | " const float states = %STATES%.;\n" + 101 | " const float width = %WIDTH%, height = %HEIGHT%;\n" + 102 | " \n" + 103 | " \n" + 104 | "void main(void) {\n" + 105 | " float v; \n" + 106 | " float idx = 0.;\n" + 107 | " %XBLOCK% \n" + 108 | " \n" + 109 | " float idy = 0.;\n" + 110 | " %YBLOCK% \n" + 111 | " \n" + 112 | " vec2 vvvv=vec2(0.5/width + idx*stateScale/width, 0.5/height + idy*stateScale/height); \n" + 113 | " vec4 lookup = texture2D(texRule, vvvv);\n" + 114 | " gl_FragColor = vec4(0, 0., 0., lookup.a);\n" + 115 | //" gl_FragColor = vec4(0, 0., vTexCoord.x, vTexCoord.y);\n" + 116 | "}"; 117 | 118 | var getFragmentShaderSourceFromEvoCellData = function(gl, evoCellData, xres, yres) 119 | { 120 | var xblock = ""; 121 | var yblock = ""; 122 | var nInXBlock = Math.ceil(evoCellData.nrNeighbours/2); 123 | var vget = ""; 124 | var multiplier = ""; 125 | var widthExpr = ""; 126 | var heightExpr = ""; 127 | 128 | for (var nIndex = 0; nIndex < evoCellData.neighbourhood.length; nIndex++) 129 | { 130 | var neighbour = evoCellData.neighbourhood[nIndex]; 131 | 132 | vget = "v = texture2D(texFrame, vTexCoord + vec2(" + neighbour[0] + ".*dx, " + neighbour[1] + ".*dy)).a;\n"; 133 | 134 | 135 | if (nIndex < nInXBlock) 136 | { 137 | xblock += vget; 138 | xblock += "idx += " + multiplier + "v;\n"; 139 | 140 | if (widthExpr != "") 141 | widthExpr += "*"; 142 | widthExpr += "states"; 143 | } 144 | else 145 | { 146 | yblock += vget; 147 | yblock += "idy += " + multiplier + "v;\n"; 148 | 149 | if (heightExpr != "") 150 | heightExpr += "*"; 151 | heightExpr += "states"; 152 | } 153 | 154 | if (nIndex == nInXBlock-1) 155 | multiplier = ""; 156 | else 157 | multiplier += "states*"; 158 | } 159 | 160 | var shaderSource = EvoCellFragmentShaderTemplate; 161 | shaderSource = shaderSource.replace("%STATES%", evoCellData.nrStates); 162 | shaderSource = shaderSource.replace("%XRES%", xres); // width of the state texture 163 | shaderSource = shaderSource.replace("%YRES%", yres); // height of the state texture 164 | shaderSource = shaderSource.replace("%WIDTH%", widthExpr); // width of the rule texture 165 | shaderSource = shaderSource.replace("%HEIGHT%", heightExpr); // height of the rule texture 166 | shaderSource = shaderSource.replace("%XBLOCK%", xblock); 167 | shaderSource = shaderSource.replace("%YBLOCK%", yblock); 168 | 169 | //alert(shaderSource); 170 | return shaderSource; 171 | } 172 | 173 | 174 | 175 | return Rule; 176 | }); 177 | -------------------------------------------------------------------------------- /src/Utils.js: -------------------------------------------------------------------------------- 1 | define(["jquery", "FileSaver"], function($, saveAs) { 2 | function relMouseCoords(event){ 3 | var totalOffsetX = 0; 4 | var totalOffsetY = 0; 5 | var canvasX = 0; 6 | var canvasY = 0; 7 | var currentElement = this; 8 | 9 | do{ 10 | totalOffsetX += currentElement.offsetLeft; 11 | totalOffsetY += currentElement.offsetTop; 12 | } 13 | while(currentElement = currentElement.offsetParent) 14 | 15 | canvasX = event.pageX - totalOffsetX; 16 | canvasY = event.pageY - totalOffsetY; 17 | 18 | return {x:canvasX, y:canvasY} 19 | } 20 | HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords; 21 | 22 | var getNDCFromMouseEvent = function(canvas, evt, screenW, screenH) { 23 | var coords = canvas.relMouseCoords(evt); 24 | return new THREE.Vector2(coords.x/screenW, (screenH - coords.y)/screenH); 25 | }; 26 | 27 | 28 | // static 29 | var playSound = function(snd) { 30 | try { 31 | snd.currentTime=0; 32 | snd.play(); 33 | } catch(ex) {} 34 | }; 35 | 36 | // TODO: gamestate access, otherwise static 37 | // gets NDC (0 to 1) of clicked postion 38 | // itersects line form eye (0, 0, 0) to cliked position of a viewMatrix transformed plane in x/y plane 39 | // returns computed object coordinates (-1 to 1 for x and y, 0 for z) 40 | var intersectClick = function(clickedNDC, viewMatrix, camAH) { 41 | var invMV = new THREE.Matrix4().getInverse(viewMatrix); 42 | 43 | var planeNormal = new THREE.Vector4(0, 0, -1, 0); 44 | var planePoint = new THREE.Vector4(0, 0, 0, 1); 45 | 46 | // here the projection matrix is used or at least the cameraAngle 47 | var sf = Math.sin(camAH)/Math.cos(camAH); 48 | var lineDir = new THREE.Vector4(sf*(2*clickedNDC.x - 1), sf*(2*clickedNDC.y - 1), -1, 0); 49 | var linePoint = new THREE.Vector4(); 50 | 51 | planeNormal.applyMatrix4(viewMatrix); 52 | planePoint.applyMatrix4(viewMatrix); 53 | 54 | var a = new THREE.Vector4().subVectors(planePoint, linePoint).dot(planeNormal); 55 | var b = lineDir.dot(planeNormal); 56 | 57 | var pointPos = a / b; 58 | 59 | var point = new THREE.Vector4().addVectors(linePoint, lineDir.clone().multiplyScalar(pointPos)); 60 | var deltaPoint = point.clone().applyMatrix4(invMV); 61 | 62 | return deltaPoint; 63 | }; 64 | 65 | function getFromURL(url, responseType, cb) { 66 | var r = new XMLHttpRequest(); 67 | r.open("GET", url, true); 68 | // "arraybuffer", "blob", "document", "json", and "text". 69 | r.responseType = responseType; 70 | r.onload = function() { // XHR2 71 | if (cb) cb(r.response); // XHR2 72 | }; 73 | r.send(); 74 | } 75 | 76 | var requestAnimFrame = (function(){ 77 | return window.webkitRequestAnimationFrame || 78 | window.mozRequestAnimationFrame || 79 | function(callback, element){ setTimeout(callback, 1000 / 60); }; 80 | })(); 81 | 82 | // if ms == 0 use AbimationLoop otherwise fixed timing 83 | var AnimationLoop = function(ms, callback) { 84 | this.ms = ms; 85 | this.callback = callback; 86 | this.pauseRequested = true; 87 | }; 88 | 89 | AnimationLoop.prototype.start = function() { 90 | var loopContext = this; 91 | this.pauseRequested = false; 92 | var myFn = function() { 93 | // requestnext frame aspa according to: https://developer.mozilla.org/en-US/docs/Games/Anatomy 94 | if (!loopContext.pauseRequested) { 95 | if (loopContext.ms) 96 | setTimeout(myFn, loopContext.ms); 97 | else 98 | requestAnimFrame(myFn); 99 | 100 | loopContext.callback(); 101 | } 102 | }; 103 | myFn(); 104 | }; 105 | 106 | AnimationLoop.prototype.step = function() { 107 | this.callback(); 108 | } 109 | 110 | AnimationLoop.prototype.stop = function() { 111 | this.pauseRequested = true; 112 | } 113 | 114 | AnimationLoop.prototype.toggle = function() { 115 | if (this.pauseRequested) { 116 | this.start(); 117 | } 118 | else { 119 | this.stop(); 120 | } 121 | } 122 | 123 | var FPSMonitor = function(elementID) { 124 | this.frames = 0; 125 | 126 | var delayMs = 1000; 127 | var time; 128 | var monitorContext = this; 129 | 130 | function fr(){ 131 | var ti = new Date().getTime(); 132 | var fps = Math.round(1000*monitorContext.frames/(ti - time)); 133 | document.getElementById(elementID).innerHTML = fps.toString(); 134 | monitorContext.frames = 0; 135 | time = ti; 136 | } 137 | var timer = setInterval(fr, delayMs); 138 | time = new Date().getTime(); 139 | }; 140 | FPSMonitor.prototype.frameIncrease = function() { 141 | this.frames++; 142 | }; 143 | 144 | var ExtractFilename = function(url) {} 145 | 146 | var Keyboard = { 147 | _pressed: {}, 148 | 149 | LEFT: 37, 150 | UP: 38, 151 | RIGHT: 39, 152 | DOWN: 40, 153 | 154 | isPressed: function(keyCode) { 155 | return this._pressed[keyCode]; 156 | }, 157 | 158 | onKeydown: function(event) { 159 | this._pressed[event.keyCode] = true; 160 | }, 161 | 162 | onKeyup: function(event) { 163 | delete this._pressed[event.keyCode]; 164 | } 165 | }; 166 | function setupKeyboard() 167 | { 168 | window.addEventListener('keyup', function(event) { Keyboard.onKeyup(event); }, false); 169 | window.addEventListener('keydown', function(event) { Keyboard.onKeydown(event); }, false); 170 | } 171 | setupKeyboard(); 172 | 173 | 174 | return { 175 | AnimationLoop : AnimationLoop, 176 | FPSMonitor : FPSMonitor, 177 | 178 | getFromURL: getFromURL, 179 | saveAs: saveAs, 180 | 181 | playSound: playSound, 182 | intersectClick: intersectClick, 183 | getNDCFromMouseEvent: getNDCFromMouseEvent, 184 | 185 | keyboard : Keyboard 186 | }; 187 | }); 188 | -------------------------------------------------------------------------------- /src/CellSpace/Utils.js: -------------------------------------------------------------------------------- 1 | define([ 2 | "jquery-ui", "Utils", "EvoCell", "story/StoryTeller", "underscore", 3 | "backbone", "knockback", "knockout", "data/FileStore", "three", "datgui", 4 | "CellSpace/State"], 5 | function($, utils, EC, storyTeller,_ , Backbone, kb, ko, fileStore, THREE, dat, 6 | gameState) { 7 | "use strict"; 8 | 9 | /////// PUBLIC /////////// 10 | // access to gamestate 11 | var pollAutoFire = function() { 12 | if (gameState.autoFireOn) { 13 | if (gameState.autoFireCounter === 0) { 14 | var clickedPoint = intersectClick(gameState.lastMouseNDC); 15 | fireShotAt(gameState.gameW*(clickedPoint.x+1)/2, gameState.gameH*(clickedPoint.y+1)/2); 16 | 17 | gameState.autoFireCounter = 5; 18 | } 19 | else { 20 | gameState.autoFireCounter--; 21 | } 22 | } 23 | }; 24 | 25 | // TODO: gameState access 26 | var fireShotAt = function(tx, ty) { 27 | // spawn shot 28 | var dX = tx-gameState.shipX; 29 | var dY = ty-gameState.shipY; 30 | var dL = Math.sqrt(dX*dX+dY*dY); 31 | 32 | var sX = gameState.shotSpeed * dX/dL; 33 | var sY = gameState.shotSpeed * dY/dL; 34 | 35 | sX += gameState.shipSpeedX; 36 | sY += gameState.shipSpeedY; 37 | 38 | var aa = gameState.frontShots > 1 ? -gameState.frontShotAngle/2 : 0; 39 | 40 | for (var i = 0; i < gameState.frontShots; i++) { 41 | gameState.shots.allocateParticle(gameState.shipX-1*gameState.scrollX, gameState.shipY-1*gameState.scrollY, 42 | Math.cos(aa)*sX + Math.sin(aa)*sY, -Math.sin(aa)*sX + Math.cos(aa)*sY); 43 | 44 | if (gameState.frontShots > 1) 45 | aa += gameState.frontShotAngle/(gameState.frontShots-1); 46 | } 47 | 48 | utils.playSound(gameState.snd); 49 | }; 50 | 51 | // TODO: could be done in backbone via a conceptual model ;) 52 | var updateButtons = function() { 53 | if (gameState.pause) { 54 | document.getElementById("playPause").children[0].className = "fa fa-play fa-2x"; 55 | } 56 | else { 57 | document.getElementById("playPause").children[0].className = "fa fa-pause fa-2x"; 58 | } 59 | }; 60 | 61 | var refreshGUI = function(names) { 62 | names = names ? (names.length ? names : [names]) : []; 63 | 64 | var refresh = function(controller) { 65 | if (_.contains(names, controller.property)) { 66 | controller.updateDisplay(); 67 | } 68 | }; 69 | 70 | _.each(gameState.gui.__controllers, refresh); 71 | _.each(gameState.gui.__folders, function(folder) { 72 | _.each(folder.__controllers, refresh); 73 | }); 74 | }; 75 | 76 | var resetGame = function() { 77 | gameState.cnt = 0; 78 | gameState.dishes.enemy.randomize(gameState.rules.enemy.nrStates, gameState.randomDensity); 79 | gameState.dishes.background.randomize(gameState.rules.enemy.nrStates, 0.01); 80 | if (gameState.shipX < 0 || gameState.shipX > gameState.gameW || 81 | gameState.shipY < 0 || gameState.shipY > gameState.gameH) { 82 | gameState.shipX = gameState.gameW/2; 83 | gameState.shipY = gameState.gameH/2; 84 | } 85 | utils.playSound(gameState.sndInit); 86 | }; 87 | 88 | var gameStep = function() { 89 | gameState.pause = true; 90 | gameState.doOneStep = true; 91 | updateButtons(); 92 | }; 93 | 94 | var gamePlayPause = function() { 95 | gameState.pause = !gameState.pause; 96 | updateButtons(); 97 | }; 98 | 99 | var onScreenSizeChanged = function() { 100 | gameState.reactor.setRenderSize(gameState.screenW, gameState.screenH); 101 | refreshGUI(["screenW", "screenH"]); 102 | }; 103 | 104 | var onGameSizeChanged = function() { 105 | var reactor = gameState.reactor; 106 | 107 | reactor.setDefaultDishSize(gameState.gameW, gameState.gameH); 108 | 109 | // hack reinit shots 110 | gameState.shots = new EC.ParticleSystem(reactor, gameState.maxParticles, gameState.gameW, gameState.gameH); 111 | 112 | // reinit instead of resize (we lose state but who cares?) 113 | var dishes = gameState.dishes; 114 | dishes.enemy = reactor.compileDish(); 115 | dishes.enemyShield = reactor.compileDish(); 116 | dishes.background = reactor.compileDish(); 117 | dishes.ship = reactor.compileDish(); 118 | dishes.shipExplosion = reactor.compileDish(); 119 | dishes.weapon = reactor.compileDish(); 120 | dishes.weaponExplosion = reactor.compileDish(); 121 | dishes.copy = reactor.compileDish(); 122 | dishes.buffer = reactor.compileDish(); 123 | dishes.render = reactor.compileDish(); 124 | dishes.render2 = reactor.compileDish(); 125 | dishes.colliding = reactor.compileDish(); 126 | 127 | try 128 | { 129 | var rules = gameState.rules; 130 | rules.enemy.setCompileSizeDish(gameState.dishes.enemy); 131 | rules.background.setCompileSizeDish(gameState.dishes.background); 132 | rules.ship.setCompileSizeDish(gameState.dishes.ship); 133 | rules.weapon.setCompileSizeDish(gameState.dishes.enemy); 134 | rules.shipExplosion.setCompileSizeDish(gameState.dishes.background); 135 | rules.weaponExplosion.setCompileSizeDish(gameState.dishes.background); 136 | 137 | resetGame(); 138 | } 139 | catch (ex) { 140 | // FIXME: catch all is dirty hack store rule and compiled rule seperately (dishes, rules and ecfiles) 141 | } 142 | }; 143 | 144 | var refreshAvailableRules = function() { 145 | fileStore.loadAllRuleNames(function(names) { 146 | gameState.drawModel.set("availableRules", names); 147 | //alert(names); 148 | }); 149 | }; 150 | 151 | var refreshAvailableDishes = function() { 152 | gameState.drawModel.set("availableLayers", Object.keys(gameState.dishes)); 153 | }; 154 | 155 | // dir 156 | var zoom = function(dir) { 157 | var zoomSpeed = 1.005; 158 | gameState.zoom *= Math.pow(zoomSpeed, dir); 159 | refreshGUI(["zoom"]); 160 | }; 161 | 162 | ////// PTIVATE ////////////// 163 | 164 | return { 165 | pollAutoFire: pollAutoFire, 166 | fireShotAt: fireShotAt, 167 | zoom: zoom, 168 | updateButtons: updateButtons, 169 | refreshGUI: refreshGUI, 170 | resetGame: resetGame, 171 | gameStep: gameStep, 172 | gamePlayPause: gamePlayPause, 173 | 174 | onScreenSizeChanged: onScreenSizeChanged, 175 | onGameSizeChanged: onGameSizeChanged, 176 | 177 | refreshAvailableRules: refreshAvailableRules, 178 | refreshAvailableDishes: refreshAvailableDishes, 179 | }; 180 | }); -------------------------------------------------------------------------------- /cellspace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CellSpace (an EvoCell spaceshooter) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |