Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Use only one global variable for all static objects.

Rationale:
* Step on fewer toes if used as one library among many on a page.
* Closer to being suitable for loading into a more controlled
  module-using environment such as Node.js.
* Clarify dependencies among components.
* I think I heard non-global variables are slightly faster.
  • Loading branch information...
commit c9e2ba6729bac02823d40eb2fa654c2b6d422e26 1 parent 0ab2897
@kpreid authored
View
15 audio.js
@@ -1,12 +1,15 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var CubesAudio = (function () {
+(function () {
+ var ID_EMPTY = cubes.Blockset.ID_EMPTY;
+ var ID_LIMIT = cubes.Blockset.ID_LIMIT;
+
var lAudioContext = typeof AudioContext !== "undefined" ? AudioContext
: typeof webkitAudioContext !== "undefined" ? webkitAudioContext : null;
var supported = !!lAudioContext;
- function CubesAudio(config) {
+ function Audio(config) {
var context;
if (supported) {
context = new lAudioContext(); /* feature test point */
@@ -41,7 +44,7 @@ var CubesAudio = (function () {
function readBlock() {
// Find volumes of material in the block
counts = [];
- for (var i = 0; i < Blockset.ID_LIMIT; i++) counts.push(0);
+ for (var i = 0; i < ID_LIMIT; i++) counts.push(0);
var raw = blockWorld.raw;
for (var i = raw.length - 1; i >= 0; i--) {
counts[raw[i]]++;
@@ -61,7 +64,7 @@ var CubesAudio = (function () {
var totalAmp = 0;
var color = [];
- for (var value = Blockset.ID_EMPTY + 1; value < Blockset.ID_LIMIT; value++) {
+ for (var value = ID_EMPTY + 1; value < ID_LIMIT; value++) {
var count = counts[value];
if (count == 0) continue;
@@ -176,7 +179,7 @@ var CubesAudio = (function () {
}
});
}
- CubesAudio.supported = supported;
+ Audio.supported = supported;
- return CubesAudio;
+ cubes.Audio = Object.freeze(Audio);
}());
View
138 block-render.js
@@ -8,81 +8,87 @@
// believe that it is obviously the authors' intent to make this code free to
// use.
-// Renders single blocks from a world.
-
-function BlockRenderer(blockset, renderer, resolution) {
+(function () {
"use strict";
- var gl = renderer.context;
-
- var singleBlockWorld = new World([1,1,1], blockset);
- singleBlockWorld.s(0,0,0,1);
- var singleBlockR = new WorldRenderer(singleBlockWorld, function () { return [0,0,0]; }, renderer, null, function (){}, false);
-
- var rttFramebuffer = gl.createFramebuffer();
- gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
- rttFramebuffer.width = resolution;
- rttFramebuffer.height = resolution;
-
- var renderbuffer1 = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer1);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, rttFramebuffer.width, rttFramebuffer.height);
- gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer1);
-
- var renderbuffer2 = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer2);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, rttFramebuffer.width, rttFramebuffer.height);
- gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer2);
-
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.bindRenderbuffer(gl.RENDERBUFFER, null);
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ var World = cubes.World;
+ var WorldRenderer = cubes.WorldRenderer;
- function blockToImageData(blockID, context2d) {
- gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
-
- gl.viewport(0, 0, rttFramebuffer.width, rttFramebuffer.height);
- gl.clearColor(0,0,0,0);
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ // Renders single blocks from a world.
+ function BlockRenderer(blockset, renderer, resolution) {
+ var gl = renderer.context;
- var restoreView = renderer.saveView();
- renderer.setExposure(1.0);
- renderer.setViewToBlock();
- singleBlockWorld.s(0,0,0,blockID);
- singleBlockWorld.rawLighting[0] = singleBlockWorld.lightOutside;
- singleBlockR.updateSomeChunks();
- singleBlockR.draw();
+ var singleBlockWorld = new World([1,1,1], blockset);
+ singleBlockWorld.s(0,0,0,1);
+ var singleBlockR = new WorldRenderer(singleBlockWorld, function () { return [0,0,0]; }, renderer, null, function (){}, false);
- // restore stuff (except for framebuffer which we're about to read)
- gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
- restoreView();
+ var rttFramebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
+ rttFramebuffer.width = resolution;
+ rttFramebuffer.height = resolution;
- var imageData = context2d.createImageData(rttFramebuffer.width, rttFramebuffer.height);
- var arrayC = imageData.data;
- var arrayGL = new Uint8Array(rttFramebuffer.width * rttFramebuffer.height * 4);
- gl.readPixels(0, 0, rttFramebuffer.width, rttFramebuffer.height, gl.RGBA, gl.UNSIGNED_BYTE, arrayGL);
+ var renderbuffer1 = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer1);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, rttFramebuffer.width, rttFramebuffer.height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer1);
- // copy into canvas data and flip y
- var h = rttFramebuffer.height;
- var w = rttFramebuffer.width * 4; // width in bytes
- for (var y = h; y--; y >= 0) {
- var nyl = (h - y) * w;
- var pyl = y * w;
- for (var i = w - 1; i >= 0; i--)
- arrayC[nyl + i] = arrayGL[pyl + i];
- }
+ var renderbuffer2 = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer2);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, rttFramebuffer.width, rttFramebuffer.height);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer2);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- return imageData;
+ function blockToImageData(blockID, context2d) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, rttFramebuffer);
+
+ gl.viewport(0, 0, rttFramebuffer.width, rttFramebuffer.height);
+ gl.clearColor(0,0,0,0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ var restoreView = renderer.saveView();
+ renderer.setExposure(1.0);
+ renderer.setViewToBlock();
+ singleBlockWorld.s(0,0,0,blockID);
+ singleBlockWorld.rawLighting[0] = singleBlockWorld.lightOutside;
+ singleBlockR.updateSomeChunks();
+ singleBlockR.draw();
+
+ // restore stuff (except for framebuffer which we're about to read)
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ restoreView();
+
+ var imageData = context2d.createImageData(rttFramebuffer.width, rttFramebuffer.height);
+ var arrayC = imageData.data;
+ var arrayGL = new Uint8Array(rttFramebuffer.width * rttFramebuffer.height * 4);
+ gl.readPixels(0, 0, rttFramebuffer.width, rttFramebuffer.height, gl.RGBA, gl.UNSIGNED_BYTE, arrayGL);
+
+ // copy into canvas data and flip y
+ var h = rttFramebuffer.height;
+ var w = rttFramebuffer.width * 4; // width in bytes
+ for (var y = h; y--; y >= 0) {
+ var nyl = (h - y) * w;
+ var pyl = y * w;
+ for (var i = w - 1; i >= 0; i--)
+ arrayC[nyl + i] = arrayGL[pyl + i];
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+ return imageData;
+ }
+
+ this.blockToImageData = blockToImageData;
+ this.deleteResources = function () {
+ rttFramebuffer = null;
+ gl.deleteRenderbuffer(renderbuffer1);
+ gl.deleteRenderbuffer(renderbuffer2);
+ gl.deleteFramebuffer(rttFramebuffer);
+ singleBlockR.deleteResources();
+ };
}
- this.blockToImageData = blockToImageData;
- this.deleteResources = function () {
- rttFramebuffer = null;
- gl.deleteRenderbuffer(renderbuffer1);
- gl.deleteRenderbuffer(renderbuffer2);
- gl.deleteFramebuffer(rttFramebuffer);
- singleBlockR.deleteResources();
- };
-}
+ cubes.BlockRenderer = BlockRenderer;
+}());
View
27 blockset.js
@@ -1,9 +1,21 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var BlockType = (function () {
+(function () {
"use strict";
+ var CatchupQueue = cubes.util.CatchupQueue;
+ var Cell = cubes.storage.Cell;
+ var Circuit = cubes.Circuit;
+ var CubeRotation = cubes.util.CubeRotation;
+ var max = Math.max;
+ var min = Math.min;
+ var mod = cubes.util.mod;
+ var Notifier = cubes.util.Notifier;
+ var ObjectMap = cubes.util.ObjectMap;
+ var Persister = cubes.storage.Persister;
+ var World = cubes.World;
+
function noop() {}
// Global non-persistent serial numbers for block types, used in the sound render queue.
@@ -191,14 +203,7 @@ var BlockType = (function () {
return self;
};
- return Object.freeze(BlockType);
-}());
-
-var Blockset = (function () {
- "use strict";
-
- var max = Math.max;
- var min = Math.min;
+ cubes.BlockType = Object.freeze(BlockType);
// Texture parameters
var TILE_MAPPINGS = [
@@ -654,7 +659,7 @@ var Blockset = (function () {
var blockIconsW = [];
var blockIconsR = [];
var toRerenderIcon = appearanceChangedQueue.getHead();
- var iconRenderer = new BlockRenderer(blockset, renderer, blockRenderRes);
+ var iconRenderer = new cubes./* late lookup */BlockRenderer(blockset, renderer, blockRenderRes);
var iconCanvas = document.createElement("canvas");
var iconCtx = iconCanvas.getContext('2d');
iconCanvas.width = iconCanvas.height = blockRenderRes;
@@ -958,5 +963,5 @@ var Blockset = (function () {
}
}
- return Object.freeze(Blockset);
+ cubes.Blockset = Object.freeze(Blockset);
}());
View
9 body.js
@@ -1,9 +1,14 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var Body = (function () {
+(function () {
"use strict";
+ var AAB = cubes.util.AAB;
+ var CubeRotation = cubes.util.CubeRotation;
+ var IntVectorMap = cubes.util.IntVectorMap;
+ var measuring = cubes.measuring;
+
// physics constants
var GRAVITY = 20; // cubes/s^2
var MAX_STEP_UP = 0.57; // cubes
@@ -214,5 +219,5 @@ var Body = (function () {
}
}
- return Body;
+ cubes.Body = Object.freeze(Body);
}());
View
16 circuit.js
@@ -1,8 +1,20 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var Circuit = (function () {
+(function () {
"use strict";
+
+ var AAB = cubes.util.AAB;
+ var CubeRotation = cubes.util.CubeRotation;
+ var mod = cubes.util.mod;
+ var UNIT_PX = cubes.util.UNIT_PX;
+ var UNIT_PY = cubes.util.UNIT_PY;
+ var UNIT_PZ = cubes.util.UNIT_PZ;
+ var UNIT_NX = cubes.util.UNIT_NX;
+ var UNIT_NY = cubes.util.UNIT_NY;
+ var UNIT_NZ = cubes.util.UNIT_NZ;
+ var ZEROVEC = cubes.util.ZEROVEC;
+
var DEBUG_WIRE = false;
// These are slice'd because the circuit code does foo[aDirection] a lot, so we want the toString() behavior of real JS arrays. TODO: Review whether it would be better to use symbol strings (e.g. "px", "py", ...) or numbers for directions.
@@ -703,5 +715,5 @@ var Circuit = (function () {
});
};
- return Object.freeze(Circuit);
+ cubes.Circuit = Object.freeze(Circuit);
}());;
View
7 config.js
@@ -3,9 +3,12 @@
// Global option structure.
-var CubesConfig = (function () {
+(function () {
"use strict";
+ var Input = cubes.Input;
+ var PersistentCell = cubes.storage.PersistentCell;
+
function Config(storage, storagePrefix) {
var config = this;
@@ -45,5 +48,5 @@ var CubesConfig = (function () {
defineOption("currentTopWorld", "string", "Untitled");
}
- return Config;
+ cubes.Config = Object.freeze(Config);
}());
View
10 cubes.html
@@ -16,16 +16,18 @@
<script type="text/javascript" src="deps/gl-matrix/gl-matrix.js"></script>
<script type="text/javascript" src="deps/game-shim/game-shim.js"></script>
+ <script type="text/javascript" src="module-initial.js"></script>
+
<!-- Framework -->
<script type="text/javascript" src="util.js"></script>
<script type="text/javascript" src="storage.js"></script>
<script type="text/javascript" src="measuring.js"></script>
<!-- Game components -->
+ <script type="text/javascript" src="circuit.js"></script>
<script type="text/javascript" src="blockset.js"></script>
<script type="text/javascript" src="world.js"></script>
<script type="text/javascript" src="world-gen.js"></script>
- <script type="text/javascript" src="circuit.js"></script>
<script type="text/javascript" src="renderer.js"></script>
<script type="text/javascript" src="world-render.js"></script>
<script type="text/javascript" src="block-render.js"></script>
@@ -41,7 +43,7 @@
This does not actually hook up the game to the UI or load the world; that is done by main.start() which is done onload.
-->
<script type="text/javascript">
- var main = new CubesMain("./", 1/60 /* timestep */, localStorage);
+ var main = new cubes.Main("./", 1/60 /* timestep */, localStorage);
</script>
</head>
<body class="ui-mode-hidden" onload='(function () {
@@ -250,7 +252,7 @@
<script>main.config.cubeParticles.bindControl("cubeParticles");</script>
<div class="optionline"><label><input type="checkbox" id="enableSound"> Sound</label></div>
<script>
- document.getElementById("enableSound").disabled = !CubesAudio.supported;
+ document.getElementById("enableSound").disabled = !cubes.Audio.supported;
main.config.sound.bindControl("enableSound");
</script>
<div class="optionline"><label><input type="checkbox" id="fsaa"> Fake 2× FSAA (I have fillrate to burn)</label></div>
@@ -268,7 +270,7 @@
</tbody>
</table>
<script type="text/javascript">
- new CubesControlBindingUI(main.config.controls, document.getElementById("controls-config"));
+ new cubes.ControlBindingUI(main.config.controls, document.getElementById("controls-config"));
</script>
<p>To change a control binding, click on the current one and then press the key or mouse button you wish to use instead. To add an additional binding, click the “…”. To remove a binding, click on it and click the “×” button.</p>
View
4 devel/bindings-testbed.html
@@ -19,6 +19,8 @@
<link rel="stylesheet" href="../style.css" type="text/css">
<script type="text/javascript" src="../deps/gl-matrix/gl-matrix.js"></script>
+ <script type="text/javascript" src="../deps/game-shim/game-shim.js"></script>
+ <script type="text/javascript" src="../module-initial.js"></script>
<script type="text/javascript" src="../util.js"></script>
<script type="text/javascript" src="../storage.js"></script>
<script type="text/javascript" src="../input.js"></script>
@@ -36,7 +38,7 @@
</tbody>
</table>
<script type="text/javascript">
- new CubesControlBindingUI(new Cell("test", Input.defaultBindings), document.getElementById("controls-config"));
+ new cubes.ControlBindingUI(new cubes.storage.Cell("test", cubes.Input.defaultBindings), document.getElementById("controls-config"));
</script>
</div>
View
4 devel/measuring-testbed.html
@@ -12,6 +12,7 @@
<script type="text/javascript" src="../deps/gl-matrix/gl-matrix.js"></script>
<script type="text/javascript" src="../deps/game-shim/game-shim.js"></script>
+ <script type="text/javascript" src="../module-initial.js"></script>
<script type="text/javascript" src="../util.js"></script>
<script type="text/javascript" src="../storage.js"></script>
<script type="text/javascript" src="../measuring.js"></script>
@@ -19,7 +20,8 @@
<body>
<script type="text/javascript">
-
+ var measuring = cubes.measuring;
+
// Fast callbacks
// Source: http://dbaron.org/log/20100309-faster-timeouts
(function() {
View
30 input.js
@@ -1,11 +1,15 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var CubesControlBindingUI;
-var Input;
(function () {
"use strict";
+ var Circuit = cubes.Circuit;
+ var exponentialStep = cubes.util.exponentialStep;
+ var mkelement = cubes.util.mkelement;
+ var signum = cubes.util.signum;
+ var WorldGen = cubes.WorldGen;
+
function parseEvent(ev) {
switch (ev.type) {
case "keydown":
@@ -392,7 +396,7 @@ var Input;
})
}
- function Input_(config, eventReceiver, playerInput, hud, renderer, focusCell, save) {
+ function Input(config, eventReceiver, playerInput, hud, renderer, focusCell, save) {
var interfaceMode;
var expectingPointerLock = false;
@@ -450,7 +454,7 @@ var Input;
var heldCommands = {};
function resetHeldControls() {
heldControls = {};
- Object.keys(Input_.commands).forEach(function (k) {
+ Object.keys(Input.commands).forEach(function (k) {
commandState[k] = {
command: commandFunctions[k],
controlCount: 0,
@@ -463,7 +467,7 @@ var Input;
// Construct commands augmented with implementation functions
var commandFunctions = {};
function deffunbase(name) {
- var command = Input_.commands[name];
+ var command = Input.commands[name];
if (!command) throw new Error("inconsistent table");
var cmdWithFunc = commandFunctions[name] = Object.create(command);
cmdWithFunc.name = name;
@@ -1150,16 +1154,16 @@ var Input;
switchMode(interfaceMode);
}
- Input_.commands = {};
- Input_.defaultBindings = [];
+ Input.commands = {};
+ Input.defaultBindings = [];
function defcmd(name, label, bindings, repeat, rd) {
- Input_.commands[name] = {
+ Input.commands[name] = {
label: label,
repeatPeriod: repeat,
repeatDelay: rd || 0
}
bindings.forEach(function (control) {
- Input_.defaultBindings.push([name, control])
+ Input.defaultBindings.push([name, control])
});
}
defcmd("useTool" , "Place block", [["mouse", 1]], 1/4); // TODO derive from movement speed
@@ -1187,9 +1191,9 @@ var Input;
defcmd("subdatumDec" , "Subdatum −1" , [["key", "Z".charCodeAt(0)]], 1/20, 1/4);
defcmd("subdatumInc" , "Subdatum +1" , [["key", "X".charCodeAt(0)]], 1/20, 1/4);
defcmd("editBlockset" , "Edit blockset", [["key", "B".charCodeAt(0)]]);
- Object.freeze(Input_.commands);
- Object.freeze(Input_.defaultBindings); // should be recursive
+ Object.freeze(Input.commands);
+ Object.freeze(Input.defaultBindings); // should be recursive
- CubesControlBindingUI = ControlBindingUI;
- Input = Input_;
+ cubes.ControlBindingUI = Object.freeze(ControlBindingUI);
+ cubes.Input = Object.freeze(Input);
}());
View
37 main.js
@@ -9,7 +9,25 @@
// Main loop scheduling, scene drawing, performance statistics, etc.
-var CubesMain = (function () {
+(function () {
+ // TODO should be strict mode
+
+ var Audio = cubes.Audio;
+ var Blockset = cubes.Blockset;
+ var BlockType = cubes.BlockType;
+ var Cell = cubes.storage.Cell;
+ var Config = cubes.Config;
+ var dynamicText = cubes.util.dynamicText;
+ var Input = cubes.Input;
+ var measuring = cubes.measuring;
+ var mkelement = cubes.util.mkelement;
+ var ObjectUI = cubes.ObjectUI;
+ var PersistencePool = cubes.storage.PersistencePool;
+ var Player = cubes.Player;
+ var ProgressBar = cubes.util.ProgressBar;
+ var Renderer = cubes.Renderer;
+ var testSettersWork = cubes.util.testSettersWork;
+ var World = cubes.World;
function padRight(string, length) {
string = String(string);
@@ -17,10 +35,10 @@ var CubesMain = (function () {
}
// rootURL should be the directory containing this script (unfortunately not directly available).
- function CubesMain(rootURL, timestep, storage) {
+ function Main(rootURL, timestep, storage) {
var main = this;
- var config = new CubesConfig(storage, "cubes.option.");
+ var config = new Config(storage, "cubes.option.");
var persistencePool = new PersistencePool(storage, "cubes.object."); // note: storage may be undefined, pool will be a stub
@@ -33,7 +51,7 @@ var CubesMain = (function () {
var theCanvas;
var renderer;
- var audio = new CubesAudio(config);
+ var audio = new Audio(config);
// HTML elements and other UI pieces
var sceneInfo;
@@ -50,13 +68,12 @@ var CubesMain = (function () {
return true;
});
- var objectUI = new CubesObjectUI(persistencePool);
+ var objectUI = new ObjectUI(persistencePool);
// Game state, etc. objects
var player;
var topWorldC = new Cell("topWorld", null);
var input;
- var audio = new CubesAudio(config);
var readyToDraw = false;
@@ -481,10 +498,10 @@ var CubesMain = (function () {
var world = getOrDefaultOrMake(config.currentTopWorld.get(), "Default World", function () {
var blockset = getOrDefaultOrMake(config.generate_blockset.get(), "Default Blockset", function () {
startupMessage(" Creating default blockset...");
- return WorldGen.newDefaultBlockset(Math.round(config.generate_tileSize.get()));
+ return cubes.WorldGen.newDefaultBlockset(Math.round(config.generate_tileSize.get()));
});
startupMessage(" Creating overworld...");
- return generateWorlds(config, blockset);
+ return cubes.generateWorlds(config, blockset);
});
main.setTopWorld(world);
@@ -526,7 +543,7 @@ var CubesMain = (function () {
};
this.regenerate = function () {
- var world = generateWorlds(config, persistencePool.get(config.generate_blockset.get()));
+ var world = cubes.generateWorlds(config, persistencePool.get(config.generate_blockset.get()));
persistencePool.persist(world, config.generate_name.get());
this.setTopWorld(world);
};
@@ -575,5 +592,5 @@ var CubesMain = (function () {
this.player = null;
}
- return CubesMain;
+ cubes.Main = Main;
}());
View
8 measuring.js
@@ -1,12 +1,14 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var measuring = (function () {
+(function () {
"use strict";
- var measuring = {};
+ var measuring = cubes.measuring = {};
var max = Math.max;
var min = Math.min;
+ var mkelement = cubes.util.mkelement;
+ var PersistentCell = cubes.storage.PersistentCell;
function numberWithCommas(x) {
// source: http://stackoverflow.com/a/2901298/99692
@@ -268,5 +270,5 @@ var measuring = (function () {
])
]);
- return measuring;
+ // Object.freeze(measuring);
}());
View
4 module-initial.js
@@ -0,0 +1,4 @@
+// Copyright 2012 Kevin Reid under the terms of the MIT License as detailed
+// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
+
+var cubes = {};
View
16 player.js
@@ -1,9 +1,19 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var Player = (function () {
+(function () {
"use strict";
+ var AAB = cubes.util.AAB;
+ var Blockset = cubes.Blockset;
+ var Body = cubes.Body;
+ var CubeRotation = cubes.util.CubeRotation;
+ var exponentialStep = cubes.util.exponentialStep;
+ var mod = cubes.util.mod;
+ var Notifier = cubes.util.Notifier;
+ var World = cubes.World;
+ var WorldRenderer = cubes.WorldRenderer;
+
// physics constants
var WALKING_SPEED = 4; // cubes/s
var FLYING_SPEED = 10; // cubes/s
@@ -493,5 +503,5 @@ var Player = (function () {
Player.aabb = playerAABB;
- return Player;
-}());;
+ cubes.Player = Object.freeze(Player);
+}());
View
16 renderer.js
@@ -23,9 +23,17 @@ This is what you can assume/should do:
Texture 3: Local lighting
*/
-var Renderer = (function () {
+(function () {
"use strict";
+ var CubeRotation = cubes.util.CubeRotation;
+ var fetchResource = cubes.util.fetchResource;
+ var fixedmultiplyVec3 = cubes.util.fixedmultiplyVec3;
+ var IntVectorMap = cubes.util.IntVectorMap;
+ var mod = cubes.util.mod;
+ var Player = cubes.Player;
+ var prepareProgram = cubes.util.prepareProgram;
+
var DEBUG_GL = false;
// Attributes which are bound in order to give consistent locations across all shader programs.
@@ -89,7 +97,7 @@ var Renderer = (function () {
SMOOTH_LIGHTING: config.smoothLighting.get(),
BUMP_MAPPING: config.bumpMapping.get(),
CUBE_PARTICLES: config.cubeParticles.get(),
- LIGHT_TEXTURE_SIZE: WorldRenderer.LIGHT_TEXTURE_SIZE
+ LIGHT_TEXTURE_SIZE: cubes/* for late binding */.WorldRenderer.LIGHT_TEXTURE_SIZE
};
blockProgramSetup = prepareProgram(gl, decls, permanentAttribs,
@@ -204,7 +212,7 @@ var Renderer = (function () {
var fov = config.fov.get();
var aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight;
- var nearestApproachToPlayer = Player.aabb.minimumRadius();
+ var nearestApproachToPlayer = cubes.Player.aabb.minimumRadius(); // TODO should get this from creator
var nearPlane = nearestApproachToPlayer
/ Math.sqrt(1 + Math.pow(Math.tan(fov/180*Math.PI/2), 2)
* (Math.pow(aspectRatio, 2) + 1));
@@ -937,5 +945,5 @@ var Renderer = (function () {
}
};
- return Object.freeze(Renderer);
+ cubes.Renderer = Object.freeze(Renderer);
}());
View
721 storage.js
@@ -3,387 +3,402 @@
// This file contains implementation of persistent storage.
-var SERIAL_TYPE_NAME = "()"; // TODO local variable
-
-function cyclicSerialize(root, typeNameFunc, getName) {
+(function () {
"use strict";
- if (!getName) getName = function () { return null; };
- var seen = [];
- function serialize(obj) {
- // named objects
- var name = getName(obj);
- if (typeof name === "string") {
- return name;
- }
-
- // break cycles
- var i;
- for (i = 0; i < seen.length; i++) {
- if (seen[i] === obj) // TODO use WeakMap if available
- return i;
+ var storage = cubes.storage = {};
+
+ var DirtyQueue = cubes.util.DirtyQueue;
+ var Notifier = cubes.util.Notifier;
+
+ var SERIAL_TYPE_NAME = "()";
+
+ function cyclicSerialize(root, typeNameFunc, getName) {
+ "use strict";
+ if (!getName) getName = function () { return null; };
+ var seen = [];
+ function serialize(obj) {
+ // named objects
+ var name = getName(obj);
+ if (typeof name === "string") {
+ return name;
+ }
+
+ // break cycles
+ var i;
+ for (i = 0; i < seen.length; i++) {
+ if (seen[i] === obj) // TODO use WeakMap if available
+ return i;
+ }
+
+ // regular serialization
+ seen.push(obj);
+ var json = obj.serialize(serialize);
+ json["#"] = i;
+ return json;
}
-
- // regular serialization
- seen.push(obj);
- var json = obj.serialize(serialize);
- json["#"] = i;
- return json;
+ serialize.setUnserializer = function (json, constructor) {
+ var name = typeNameFunc(constructor);
+ if (name !== null) {
+ json[SERIAL_TYPE_NAME] = name;
+ } else {
+ throw new Error("Don't know how to serialize the constructor " + constructor);
+ }
+ };
+ return serialize(root);
}
- serialize.setUnserializer = function (json, constructor) {
- var name = typeNameFunc(constructor);
- if (name !== null) {
- json[SERIAL_TYPE_NAME] = name;
- } else {
- throw new Error("Don't know how to serialize the constructor " + constructor);
- }
- };
- return serialize(root);
-}
+ storage.cyclicSerialize = cyclicSerialize;
-function cyclicUnserialize(json, unserializers, lookupName) {
- "use strict";
- if (!lookupName) lookupName = function () { throw new Error("got name w/ no lookup function"); };
- var seen = [];
+ function cyclicUnserialize(json, unserializers, lookupName) {
+ "use strict";
+ if (!lookupName) lookupName = function () { throw new Error("got name w/ no lookup function"); };
+ var seen = [];
- function findConstructor(json) {
- var typename = json[SERIAL_TYPE_NAME];
- if (Object.prototype.hasOwnProperty.call(unserializers, typename))
- return unserializers[typename];
- throw new Error("Don't know how to unserialize type name: " + typename);
- }
-
- function unserialize(json) {
- if (typeof json === "number" && json >= 0) {
- return seen[json];
- } else if (typeof json === "string") {
- return lookupName(json);
- } else if (typeof json === "object") {
- return seen[+(json["#"])] = findConstructor(json).unserialize(json, unserialize);
- } else {
- throw new Error("Don't know how to unserialize from a " + typeof json);
+ function findConstructor(json) {
+ var typename = json[SERIAL_TYPE_NAME];
+ if (Object.prototype.hasOwnProperty.call(unserializers, typename))
+ return unserializers[typename];
+ throw new Error("Don't know how to unserialize type name: " + typename);
}
- }
- return unserialize(json);
-}
-
-function Cell(label, initialValue) {
- "use strict";
-
- var value = initialValue;
-
- var notifier = new Notifier(label);
- var notify = notifier.notify;
-
- function get() {
- return value;
- }
- function set(newV) {
- value = newV;
- notify("changed", newV);
- }
- this.get = get;
- this.set = set;
- this.listen = notifier.listen;
- this.readOnly = Object.create(Cell.prototype);
- this.readOnly.get = get;
- this.readOnly.listen = notifier.listen;
- Object.freeze(this.readOnly);
-}
-// Returns a function to trigger the function now.
-Cell.prototype.whenChanged = function (func) {
- var interest = true;
- this.listen({
- interest: function () { return interest; },
- changed: function () { interest = func.apply(null, arguments); }
- });
- var self = this;
- return function () { interest = func(self.get()); };
-};
-Cell.prototype.nowAndWhenChanged = function (func) {
- this.whenChanged(func)();
-};
-
-function PersistentCell(storage, storageName, type, defaultValue) {
- "use strict";
- Cell.call(this, storageName, defaultValue);
-
- this.type = type;
- var bareSet = this.set;
- this.set = function (newV) {
- bareSet(newV);
- storage.setItem(storageName, JSON.stringify(newV));
+ function unserialize(json) {
+ if (typeof json === "number" && json >= 0) {
+ return seen[json];
+ } else if (typeof json === "string") {
+ return lookupName(json);
+ } else if (typeof json === "object") {
+ return seen[+(json["#"])] = findConstructor(json).unserialize(json, unserialize);
+ } else {
+ throw new Error("Don't know how to unserialize from a " + typeof json);
+ }
+ }
+ return unserialize(json);
}
- this.setToDefault = function () { this.set(defaultValue); };
+ storage.cyclicUnserialize = cyclicUnserialize;
- var valueString = storage.getItem(storageName);
- if (valueString !== null) {
- var value;
- try {
- value = JSON.parse(valueString);
- } catch (e) {
- if (typeof console !== "undefined")
- console.error("Failed to parse stored value " + storageName + ":", e);
+ function Cell(label, initialValue) {
+ var value = initialValue;
+
+ var notifier = new Notifier(label);
+ var notify = notifier.notify;
+
+ function get() {
+ return value;
}
- if (typeof value !== type) {
- if (typeof console !== "undefined")
- console.error("Stored value " + storageName + " not a " + type + ":", value);
+ function set(newV) {
+ value = newV;
+ notify("changed", newV);
}
- this.set(value); // canonicalize/overwrite
+
+ this.get = get;
+ this.set = set;
+ this.listen = notifier.listen;
+ this.readOnly = Object.create(Cell.prototype);
+ this.readOnly.get = get;
+ this.readOnly.listen = notifier.listen;
+ Object.freeze(this.readOnly);
}
-}
-PersistentCell.prototype = Object.create(Cell.prototype);
-PersistentCell.prototype.bindControl = function (id) {
- var elem = document.getElementById(id);
- var self = this;
+ // Returns a function to trigger the function now.
+ Cell.prototype.whenChanged = function (func) {
+ var interest = true;
+ this.listen({
+ interest: function () { return interest; },
+ changed: function () { interest = func.apply(null, arguments); }
+ });
+ var self = this;
+ return function () { interest = func(self.get()); };
+ };
+ Cell.prototype.nowAndWhenChanged = function (func) {
+ this.whenChanged(func)();
+ };
+ storage.Cell = Cell;
- var listener;
- switch (elem.type == "text" ? "T"+self.type : "E"+elem.type) {
- case "Echeckbox":
- listener = function(value) {
- elem.checked = value;
+ function PersistentCell(storage, storageName, type, defaultValue) {
+ "use strict";
+ Cell.call(this, storageName, defaultValue);
+
+ this.type = type;
+ var bareSet = this.set;
+ this.set = function (newV) {
+ bareSet(newV);
+ storage.setItem(storageName, JSON.stringify(newV));
+ }
+ this.setToDefault = function () { this.set(defaultValue); };
+
+ var valueString = storage.getItem(storageName);
+ if (valueString !== null) {
+ var value;
+ try {
+ value = JSON.parse(valueString);
+ } catch (e) {
+ if (typeof console !== "undefined")
+ console.error("Failed to parse stored value " + storageName + ":", e);
}
- elem.onchange = function () {
- self.set(elem.checked);
- return true;
- };
- break;
- case "Erange":
- listener = function(value) {
- elem.value = value;
- };
- elem.onchange = function () {
- self.set(parseFloat(elem.value));
- return true;
- };
- break;
- case "Tstring":
- case "Eselect-one":
- listener = function(value) {
- elem.value = value;
- };
- elem.onchange = function () {
- self.set(elem.value);
- return true;
- };
- break;
- case "Enumber":
- case "Tnumber":
- listener = function(value) {
- elem.value = value;
- };
- elem.onchange = function () {
- // TODO: Should be parseFloat iff the step is not an integer
- self.set(parseInt(elem.value, 10));
- return true;
- };
- break;
- default:
- console.warn("Insufficient information to bind control", id, "(input type ", elem.type, ", value type", self.type, ")");
- listener = function(value) {
- elem.value = value;
- };
- elem.disabled = true;
- }
-
- this.listen({
- interest: function () { return true; },
- changed: listener
- });
- listener(this.get());
-};
-
-function PersistencePool(storage, objectPrefix) {
- "use strict";
- var pool = this;
-
- // constants
- var hop = Object.prototype.hasOwnProperty;
-
- // global state
- var currentlyLiveObjects = {};
- var dirtyQueue = new DirtyQueue();
- var notifier = new Notifier();
-
- var status = new Cell("PersistencePool("+objectPrefix+").status", 0);
- function updateStatus() {
- status.set(dirtyQueue.size());
- }
-
- function handleDirty(name) {
- if (hop.call(currentlyLiveObjects, name)) {
- currentlyLiveObjects[name].persistence.commit(); // TODO: spoofable (harmlessly)
+ if (typeof value !== type) {
+ if (typeof console !== "undefined")
+ console.error("Stored value " + storageName + " not a " + type + ":", value);
+ }
+ this.set(value); // canonicalize/overwrite
}
}
-
- function register(object, name) {
- object.persistence._registerName(pool, name);
- currentlyLiveObjects[name] = object;
- }
-
- this.flushAsync = function () {
- var currentlyDirty = [];
- var n = dirtyQueue.size();
- function loop() {
- if (n-- <= 0) return; // avoid inf loop if things are constantly dirty
- var name = dirtyQueue.dequeue();
- if (name !== null) {
- handleDirty(name);
- setTimeout(loop, 0);
- }
- updateStatus();
+ PersistentCell.prototype = Object.create(Cell.prototype);
+ PersistentCell.prototype.bindControl = function (id) {
+ var elem = document.getElementById(id);
+ var self = this;
+
+ var listener;
+ switch (elem.type == "text" ? "T"+self.type : "E"+elem.type) {
+ case "Echeckbox":
+ listener = function(value) {
+ elem.checked = value;
+ }
+ elem.onchange = function () {
+ self.set(elem.checked);
+ return true;
+ };
+ break;
+ case "Erange":
+ listener = function(value) {
+ elem.value = value;
+ };
+ elem.onchange = function () {
+ self.set(parseFloat(elem.value));
+ return true;
+ };
+ break;
+ case "Tstring":
+ case "Eselect-one":
+ listener = function(value) {
+ elem.value = value;
+ };
+ elem.onchange = function () {
+ self.set(elem.value);
+ return true;
+ };
+ break;
+ case "Enumber":
+ case "Tnumber":
+ listener = function(value) {
+ elem.value = value;
+ };
+ elem.onchange = function () {
+ // TODO: Should be parseFloat iff the step is not an integer
+ self.set(parseInt(elem.value, 10));
+ return true;
+ };
+ break;
+ default:
+ console.warn("Insufficient information to bind control", id, "(input type ", elem.type, ", value type", self.type, ")");
+ listener = function(value) {
+ elem.value = value;
+ };
+ elem.disabled = true;
}
- setTimeout(loop, 0);
+
+ this.listen({
+ interest: function () { return true; },
+ changed: listener
+ });
+ listener(this.get());
};
- this.flushNow = function () {
- var name;
- while ((name = dirtyQueue.dequeue()) !== null) {
- handleDirty(name);
+ storage.PersistentCell = PersistentCell;
+
+ function PersistencePool(storage, objectPrefix) {
+ "use strict";
+ var pool = this;
+
+ // constants
+ var hop = Object.prototype.hasOwnProperty;
+
+ // global state
+ var currentlyLiveObjects = {};
+ var dirtyQueue = new DirtyQueue();
+ var notifier = new Notifier();
+
+ var status = new Cell("PersistencePool("+objectPrefix+").status", 0);
+ function updateStatus() {
+ status.set(dirtyQueue.size());
}
- updateStatus();
- };
- this.get = function (name) {
- if (hop.call(currentlyLiveObjects, name)) {
- console.log("Persister: already live", name);
- return currentlyLiveObjects[name];
+
+ function handleDirty(name) {
+ if (hop.call(currentlyLiveObjects, name)) {
+ currentlyLiveObjects[name].persistence.commit(); // TODO: spoofable (harmlessly)
+ }
+ }
+
+ function register(object, name) {
+ object.persistence._registerName(pool, name);
+ currentlyLiveObjects[name] = object;
}
- var data = storage.getItem(objectPrefix + name);
- if (data === null) {
- console.log("Persister: no object for", name);
- return null;
- } else {
- console.log("Persister: retrieving", name);
- var object = cyclicUnserialize(JSON.parse(data), Persister.types, function (name) {
- var obj = pool.get(name);
- if (obj) {
- return obj;
- } else {
- throw new Error("Serialized object contained reference to missing object: " + name);
+
+ this.flushAsync = function () {
+ var currentlyDirty = [];
+ var n = dirtyQueue.size();
+ function loop() {
+ if (n-- <= 0) return; // avoid inf loop if things are constantly dirty
+ var name = dirtyQueue.dequeue();
+ if (name !== null) {
+ handleDirty(name);
+ setTimeout(loop, 0);
}
- });
+ updateStatus();
+ }
+ setTimeout(loop, 0);
+ };
+ this.flushNow = function () {
+ var name;
+ while ((name = dirtyQueue.dequeue()) !== null) {
+ handleDirty(name);
+ }
+ updateStatus();
+ };
+ this.get = function (name) {
+ if (hop.call(currentlyLiveObjects, name)) {
+ console.log("Persister: already live", name);
+ return currentlyLiveObjects[name];
+ }
+ var data = storage.getItem(objectPrefix + name);
+ if (data === null) {
+ console.log("Persister: no object for", name);
+ return null;
+ } else {
+ console.log("Persister: retrieving", name);
+ var object = cyclicUnserialize(JSON.parse(data), Persister.types, function (name) {
+ var obj = pool.get(name);
+ if (obj) {
+ return obj;
+ } else {
+ throw new Error("Serialized object contained reference to missing object: " + name);
+ }
+ });
+ register(object, name);
+ return object;
+ }
+ };
+ this.getIfLive = function (name) {
+ return hop.call(currentlyLiveObjects, name) ? currentlyLiveObjects[name] : null;
+ };
+ this.getSize = function (name) {
+ return storage.getItem(objectPrefix + name).length;
+ };
+ this.has = function (name) {
+ return this.available &&
+ (hop.call(currentlyLiveObjects, name) || storage.getItem(objectPrefix + name) !== null);
+ };
+ this.forEach = function (f) {
+ // TODO Instead of this expensive unserialize-and-inspect, examine the db on startup and cache
+ for (var i = storage.length - 1; i >= 0; i--) {
+ var key = storage.key(i);
+ if (key.length >= objectPrefix.length && key.substring(0, objectPrefix.length) == objectPrefix) {
+ f(key.substring(objectPrefix.length),
+ Persister.types[JSON.parse(storage.getItem(key))[SERIAL_TYPE_NAME]]);
+ }
+ }
+ };
+ this.persist = function (object, name) {
+ if (pool.has(name)) {
+ throw new Error("The name " + newName + " is already in use.");
+ }
+ if (!pool.available) {
+ throw new Error("localStorage not supported by this browser; persistence not available");
+ }
+ var existingName = pool.getObjectName(object);
+ if (existingName !== null) {
+ if (existingName === name) return;
+ throw new Error("This object already has the name " + existingName);
+ }
+ if (object.persistence._getPool() && object.persistence._getPool() !== pool) {
+ throw new Error("This object is already in a different pool.");
+ }
register(object, name);
- return object;
- }
- };
- this.getIfLive = function (name) {
- return hop.call(currentlyLiveObjects, name) ? currentlyLiveObjects[name] : null;
- };
- this.getSize = function (name) {
- return storage.getItem(objectPrefix + name).length;
- };
- this.has = function (name) {
- return this.available &&
- (hop.call(currentlyLiveObjects, name) || storage.getItem(objectPrefix + name) !== null);
- };
- this.forEach = function (f) {
- // TODO Instead of this expensive unserialize-and-inspect, examine the db on startup and cache
- for (var i = storage.length - 1; i >= 0; i--) {
- var key = storage.key(i);
- if (key.length >= objectPrefix.length && key.substring(0, objectPrefix.length) == objectPrefix) {
- f(key.substring(objectPrefix.length),
- Persister.types[JSON.parse(storage.getItem(key))[SERIAL_TYPE_NAME]]);
+ // TODO should take the persister, not the object, so we aren't assuming .persistence is correct
+ object.persistence.dirty();
+ object.persistence.commit(); // TODO all we really need to do here is ensure that it appears in the forEach list; this is just a kludge for that.
+ notifier.notify("added", name);
+ console.log("Persister: persisted", name);
+ };
+ this._write = function (name, data) { // TODO internal
+ storage.setItem(objectPrefix + name, data);
+ };
+ this.ephemeralize = function (name) {
+ console.log("Persister: ephemeralized", name);
+ storage.removeItem(objectPrefix + name);
+ if (Object.prototype.hasOwnProperty.call(currentlyLiveObjects, name)) {
+ var object = currentlyLiveObjects[name];
+ delete currentlyLiveObjects[name];
+ object.persistence._registerName(null, null);
}
+ notifier.notify("deleted", name);
+ };
+ this._dirty = function (name) {
+ dirtyQueue.enqueue(name);
+ updateStatus();
}
- };
- this.persist = function (object, name) {
- if (pool.has(name)) {
- throw new Error("The name " + newName + " is already in use.");
- }
- if (!pool.available) {
- throw new Error("localStorage not supported by this browser; persistence not available");
- }
- var existingName = pool.getObjectName(object);
- if (existingName !== null) {
- if (existingName === name) return;
- throw new Error("This object already has the name " + existingName);
- }
- if (object.persistence._getPool() && object.persistence._getPool() !== pool) {
- throw new Error("This object is already in a different pool.");
- }
- register(object, name);
- // TODO should take the persister, not the object, so we aren't assuming .persistence is correct
- object.persistence.dirty();
- object.persistence.commit(); // TODO all we really need to do here is ensure that it appears in the forEach list; this is just a kludge for that.
- notifier.notify("added", name);
- console.log("Persister: persisted", name);
- };
- this._write = function (name, data) { // TODO internal
- storage.setItem(objectPrefix + name, data);
- };
- this.ephemeralize = function (name) {
- console.log("Persister: ephemeralized", name);
- storage.removeItem(objectPrefix + name);
- if (Object.prototype.hasOwnProperty.call(currentlyLiveObjects, name)) {
- var object = currentlyLiveObjects[name];
- delete currentlyLiveObjects[name];
- object.persistence._registerName(null, null);
- }
- notifier.notify("deleted", name);
- };
- this._dirty = function (name) {
- dirtyQueue.enqueue(name);
- updateStatus();
- }
- this.listen = notifier.listen;
- this.available = !!storage;
- this.status = status.readOnly;
-
- Object.freeze(this);
-}
-PersistencePool.prototype.getObjectName = function (object) {
- var p = object.persistence;
- return p && p._getPool() === this ? p._getName() : null;
-};
+ this.listen = notifier.listen;
+ this.available = !!storage;
+ this.status = status.readOnly;
-function Persister(object) {
- "use strict";
-
- var persister = this;
- var pool = null;
- var name = null;
- var dirty = false;
+ Object.freeze(this);
+ }
+ PersistencePool.prototype.getObjectName = function (object) {
+ var p = object.persistence;
+ return p && p._getPool() === this ? p._getName() : null;
+ };
+ storage.PersistencePool = PersistencePool;
- function getObjName(obj) {
- if (obj && obj !== object) {
- return pool.getObjectName(obj);
+ function Persister(object) {
+ "use strict";
+
+ var persister = this;
+ var pool = null;
+ var name = null;
+ var dirty = false;
+
+ function getObjName(obj) {
+ if (obj && obj !== object) {
+ return pool.getObjectName(obj);
+ }
}
+
+ this._registerName = function (newPool, newName) { // TODO internal, should not be published
+ pool = newPool;
+ name = newName;
+ };
+ this._getPool = function () { return pool; };
+ this._getName = function () { return name; };
+ this.dirty = function () {
+ if (name !== null && !dirty) {
+ console.log("Persister: dirtied", name);
+ dirty = true;
+ pool._dirty(name);
+ }
+ };
+ this.commit = function () {
+ if (name === null) return;
+ if (!dirty) {
+ console.log("Persister: not writing clean", name);
+ return;
+ } else {
+ console.log("Persister: writing dirty", name);
+ pool._write(name, JSON.stringify(cyclicSerialize(object, Persister.findType, getObjName)));
+ dirty = false;
+ }
+ };
}
+ Persister.types = {}; // TODO global mutable state
- this._registerName = function (newPool, newName) { // TODO internal, should not be published
- pool = newPool;
- name = newName;
- };
- this._getPool = function () { return pool; };
- this._getName = function () { return name; };
- this.dirty = function () {
- if (name !== null && !dirty) {
- console.log("Persister: dirtied", name);
- dirty = true;
- pool._dirty(name);
- }
- };
- this.commit = function () {
- if (name === null) return;
- if (!dirty) {
- console.log("Persister: not writing clean", name);
- return;
- } else {
- console.log("Persister: writing dirty", name);
- pool._write(name, JSON.stringify(cyclicSerialize(object, Persister.findType, getObjName)));
- dirty = false;
+ // TODO kludge
+ Persister.findType = function (constructor) {
+ var ts = Persister.types;
+ for (var k in ts) {
+ if (ts[k] === constructor
+ && Object.prototype.hasOwnProperty.call(ts, k)) {
+ return k;
+ }
}
+ return null;
};
-}
-Persister.types = {}; // TODO global mutable state
-
-// TODO kludge
-Persister.findType = function (constructor) {
- var ts = Persister.types;
- for (var k in ts) {
- if (ts[k] === constructor
- && Object.prototype.hasOwnProperty.call(ts, k)) {
- return k;
- }
- }
- return null;
-};
+
+ storage.Persister = Persister;
+
+ Object.freeze(storage);
+}());
View
3  test/index.html
@@ -16,13 +16,14 @@
<script type="text/javascript" src="../deps/webgl-utils.js"></script>
<script type="text/javascript" src="../deps/gl-matrix/gl-matrix.js"></script>
<script type="text/javascript" src="../deps/game-shim/game-shim.js"></script>
+ <script type="text/javascript" src="../module-initial.js"></script>
<script type="text/javascript" src="../util.js"></script>
<script type="text/javascript" src="../storage.js"></script>
<script type="text/javascript" src="../measuring.js"></script>
+ <script type="text/javascript" src="../circuit.js"></script>
<script type="text/javascript" src="../blockset.js"></script>
<script type="text/javascript" src="../world.js"></script>
<script type="text/javascript" src="../world-gen.js"></script>
- <script type="text/javascript" src="../circuit.js"></script>
<script type="text/javascript" src="../renderer.js"></script>
<script type="text/javascript" src="../world-render.js"></script>
<script type="text/javascript" src="../block-render.js"></script>
View
19 test/test-blocks.js
@@ -2,6 +2,16 @@
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
describe("BlockType", function () {
+ "use strict";
+
+ var Blockset = cubes.Blockset;
+ var BlockType = cubes.BlockType;
+ var Circuit = cubes.Circuit;
+ var cyclicSerialize = cubes.storage.cyclicSerialize;
+ var cyclicUnserialize = cubes.storage.cyclicUnserialize;
+ var Persister = cubes.storage.Persister;
+ var World = cubes.World;
+
it("should persist block type attributes", function () {
var type = new BlockType([1,0,1,1], new World([1, 1, 1], new Blockset([])));
type.automaticRotations = [1,2];
@@ -21,6 +31,13 @@ describe("BlockType", function () {
});
describe("Blockset", function () {
+ "use strict";
+
+ var Blockset = cubes.Blockset;
+ var BlockType = cubes.BlockType;
+ var PersistencePool = cubes.storage.PersistencePool;
+ var World = cubes.World;
+
it("should know when it is dirty", function () {
sessionStorage.clear();
var pool = new PersistencePool(sessionStorage, "Blockset-dirty-test.");
@@ -34,7 +51,7 @@ describe("Blockset", function () {
// adding block types
var btc = new BlockType([1,1,1,1], null);
- var btw = new BlockType(null, new World([16,16,16], WorldGen.colorBlocks(2,2,2)));
+ var btw = new BlockType(null, new World([16,16,16], cubes.WorldGen.colorBlocks(2,2,2)));
blockset.add(btc);
blockset.add(btw);
expect(pool.status.get()).toEqual(1);
View
23 test/test-circuits.js
@@ -2,8 +2,29 @@
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
describe("Circuit", function() {
+ "use strict";
+
+ var AAB = cubes.util.AAB;
+ var BlockType = cubes.BlockType;
+ var Body = cubes.Body;
+ var Cell = cubes.storage.Cell;
+ var Circuit = cubes.Circuit;
+ var CubeRotation = cubes.util.CubeRotation;
+ var cyclicSerialize = cubes.storage.cyclicSerialize;
+ var cyclicUnserialize = cubes.storage.cyclicUnserialize;
+ var Persister = cubes.storage.Persister;
+ var UNIT_PX = cubes.util.UNIT_PX;
+ var UNIT_PY = cubes.util.UNIT_PY;
+ var UNIT_PZ = cubes.util.UNIT_PZ;
+ var UNIT_NX = cubes.util.UNIT_NX;
+ var UNIT_NY = cubes.util.UNIT_NY;
+ var UNIT_NZ = cubes.util.UNIT_NZ;
+ var World = cubes.World;
+ var WorldGen = cubes.WorldGen;
+ var ZEROVEC = cubes.util.ZEROVEC;
+
var TS = 10;
-
+
var t;
beforeEach(function () {
t = makeTester();
View
10 test/test-main.js
@@ -1,7 +1,13 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-describe("CubesMain", function() {
+describe("Main", function() {
+ "use strict";
+
+ var Blockset = cubes.Blockset;
+ var Main = cubes.Main;
+ var World = cubes.World;
+
var main, done, parts;
function stubElem() {
@@ -12,7 +18,7 @@ describe("CubesMain", function() {
// We are using sessionStorage as a temporary Storage object for testing.
sessionStorage.clear();
- main = new CubesMain(TEST_URL_ROOT, 1/60, sessionStorage);
+ main = new Main(TEST_URL_ROOT, 1/60, sessionStorage);
}
function doStart(spec) {
View
8 test/test-storage.js
@@ -2,6 +2,8 @@
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
describe("Cell", function () {
+ var Cell = cubes.storage.Cell;
+
it("whenChanged should handle interest correctly", function () {
var cell = new Cell("test1", 0);
@@ -27,6 +29,12 @@ describe("Cell", function () {
})
describe("Persister", function () {
+ "use strict";
+
+ var Blockset = cubes.Blockset;
+ var PersistencePool = cubes.storage.PersistencePool;
+ var World = cubes.World;
+
it("should preserve references to other persistent objects", function () {
// using existing types rather than ones invented for test because Persister.types is a global currently.
sessionStorage.clear();
View
7 test/test-util.js
@@ -1,7 +1,9 @@
// Copyright 2011-2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-describe("CubeRotation", function() {
+describe("CubeRotation", function () {
+ var CubeRotation = cubes.util.CubeRotation;
+
beforeEach(function() {
this.addMatchers({
toEqualVector: function(expected) {
@@ -76,5 +78,4 @@ describe("CubeRotation", function() {
testNamedRot("z90", [-2,1,3]);
testNamedRot("z180", [-1,-2,3]);
testNamedRot("z270", [2,-1,3]);
-
-});
+});
View
4 test/test-world-gen.js
@@ -2,6 +2,10 @@
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
describe("WorldGen", function() {
+ "use strict";
+
+ var WorldGen = cubes.WorldGen;
+
var TS = 8;
it("should add logic blocks idempotently", function () {
View
10 test/test-world-render.js
@@ -2,6 +2,14 @@
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
describe("WorldRenderer", function() {
+ "use strict";
+
+ var Blockset = cubes.Blockset;
+ var Config = cubes.Config;
+ var Renderer = cubes.Renderer;
+ var World = cubes.World;
+ var WorldRenderer = cubes.WorldRenderer;
+
var shaders, renderer, world, wrend;
function scheduleDraw() {}
@@ -10,7 +18,7 @@ describe("WorldRenderer", function() {
beforeEach(function () {
sessionStorage.clear();
var canvas = document.createElement("canvas");
- var config = new CubesConfig(sessionStorage, "cubes-test-dummy.option.");
+ var config = new Config(sessionStorage, "cubes-test-dummy.option.");
Renderer.fetchShaders("../", function (s) {
if (s === null) throw new Error("shader download failed");
shaders = s;
View
4 test/test-world.js
@@ -2,6 +2,10 @@
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
describe("World", function() {
+ var Blockset = cubes.Blockset;
+ var BlockType = cubes.BlockType;
+ var World = cubes.World;
+
it("should terminate an infinite raycast", function () {
var world = new World([1, 1000, 1000], new Blockset([]));
var t0 = Date.now();
View
7 ui-2d.js
@@ -1,11 +1,12 @@
// Copyright 2012 Kevin Reid under the terms of the MIT License as detailed
// in the accompanying file README.md or <http://opensource.org/licenses/MIT>.
-var CubesObjectUI;
-
(function () {
"use strict";
+ var mkelement = cubes.util.mkelement;
+ var World = cubes.World;
+
function ObjectUI(persistencePool) {
var ui = this;
@@ -260,5 +261,5 @@ var CubesObjectUI;
this.refocus();
};
- CubesObjectUI = ObjectUI;
+ cubes.ObjectUI = Object.freeze(ObjectUI);
})();
View
1,351 util.js
@@ -8,753 +8,754 @@
// believe that it is obviously the authors' intent to make this code free to
// use.
-function testSettersWork() {
+(function () {
"use strict";
- try {
- var y = 0;
- var o = Object.freeze({
- set x(v) { y = v; }
- });
- o.x = 43;
- return y === 43;
- } catch (e) {
- return false;
- }
-}
-
-function mod(value, modulus) {
- "use strict";
- return (value % modulus + modulus) % modulus;
-}
-
-function deadzone(value, radius) {
- "use strict";
- if (value < 0) {
- return -deadzone(-value, radius);
- } else if (value < radius) {
- return 0;
- } else {
- return value - radius;
- }
-}
-
-function signum(x) {
- "use strict";
- return x > 0 ? 1 : x < 0 ? -1 : 0;
-}
-
-function exponentialStep(val0, zeroOffset, timestep, constant, cutoff) {
- var decayDerivative = constant * Math.exp(constant * timestep);
- var val1 = val0 + (val0 - zeroOffset) * decayDerivative * timestep;
- if (Math.abs(val1 - zeroOffset) < cutoff) {
- return zeroOffset;
- } else {
- return val1;
- }
-}
-
-function fixedmultiplyVec3(matrix, vector) {
- "use strict";
- // glMatrix's multiplyVec3 doesn't work if the implicit fourth OUTPUT is not 1, so doesn't work for matrices such as inverted projection matrices
- var four = [vector[0], vector[1], vector[2], 1];
- mat4.multiplyVec4(matrix, four);
- vector[0] = four[0]/four[3];
- vector[1] = four[1]/four[3];
- vector[2] = four[2]/four[3];
- return vector;
-}
-
-var ZEROVEC = vec3.createFrom(0,0,0);
-var UNIT_PX = vec3.createFrom(1,0,0);
-var UNIT_PY = vec3.createFrom(0,1,0);
-var UNIT_PZ = vec3.createFrom(0,0,1);
-var UNIT_NX = vec3.createFrom(-1,0,0);
-var UNIT_NY = vec3.createFrom(0,-1,0);
-var UNIT_NZ = vec3.createFrom(0,0,-1);
-
-// Helper for constructing DOM.
-function mkelement(name, classes/* , child, child, ... */) {
- var element = document.createElement(name);
- element.className = classes;
- for (var i = mkelement.length; i < arguments.length; i++) {
- var childDes = arguments[i];
- if (typeof childDes === "string") {
- childDes = document.createTextNode(childDes);
- }
- element.appendChild(childDes);
- }
- return element;
-}
-
-function prepareShader(gl, type, sources, declarations) {
- // See note in license statement at the top of this file.
- "use strict";
-
- var strings = [];
- for (var prop in declarations) {
- var value = declarations[prop];
- if (typeof value == "boolean") {
- value = value ? 1 : 0; // GLSL preprocessor doesn't do booleans
+ var util = cubes.util = {};
+
+ function testSettersWork() {
+ try {
+ var y = 0;
+ var o = Object.freeze({
+ set x(v) { y = v; }
+ });
+ o.x = 43;
+ return y === 43;
+ } catch (e) {
+ return false;
}
- strings.push("#define ", prop, " (", value, ")\n");
}
- sources.forEach(function (text, index) {
- strings.push("#line 1 ", index.toString(), "\n", text);
- });
+ util.testSettersWork = testSettersWork;
- var shader = gl.createShader(type);
-
- gl.shaderSource(shader, strings.join(""));
- gl.compileShader(shader);
-
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
- if (typeof console !== "undefined") console.log("Shader text:\n" + strings.join(""));
- throw new Error(gl.getShaderInfoLog(shader));
+ function mod(value, modulus) {
+ return (value % modulus + modulus) % modulus;
}
+ util.mod = mod;
- return shader;
-}
-
-function prepareProgram(gl, declarations, boundAttribLocations, vertexSources, fragmentSources) {
- // See note in license statement at the top of this file.
- "use strict";
-
- var vertexShader = prepareShader(gl, gl.VERTEX_SHADER, vertexSources, declarations);
- var fragmentShader = prepareShader(gl, gl.FRAGMENT_SHADER, fragmentSources, declarations);
-
- var program = gl.createProgram();
-
- gl.attachShader(program, vertexShader);
- gl.attachShader(program, fragmentShader);
-
- for (var attribName in boundAttribLocations) {
- var index = boundAttribLocations[attribName];
- if (typeof index === "number") {
- gl.bindAttribLocation(program, index, attribName);
+ function deadzone(value, radius) {
+ if (value < 0) {
+ return -deadzone(-value, radius);
+ } else if (value < radius) {
+ return 0;
} else {
- if (typeof console !== "undefined") {
- console.warn("Enumerable non-number", attribName, "in boundAttribLocations object", boundAttribLocations);
- }
+ return value - radius;
}
}
+ util.deadzone = deadzone;
- gl.linkProgram(program);
-
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
- throw new Error(gl.getProgramInfoLog(program));
+ function signum(x) {
+ return x > 0 ? 1 : x < 0 ? -1 : 0;
}
+ util.signum = signum;
- var attribs = Object.create(boundAttribLocations);
- for (var i = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES) - 1; i >= 0; i--) {
- var name = gl.getActiveAttrib(program, i).name;
- attribs[name] = gl.getAttribLocation(program, name);
- }
- var uniforms = {};
- for (var i = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS) - 1; i >= 0; i--) {
- var name = gl.getActiveUniform(program, i).name;
- uniforms[name] = gl.getUniformLocation(program, name);
+ function exponentialStep(val0, zeroOffset, timestep, constant, cutoff) {
+ var decayDerivative = constant * Math.exp(constant * timestep);
+ var val1 = val0 + (val0 - zeroOffset) * decayDerivative * timestep;
+ if (Math.abs(val1 - zeroOffset) < cutoff) {
+ return zeroOffset;
+ } else {
+ return val1;
+ }
}
-
- return {
- program: program,
- attribs: attribs,
- uniforms: uniforms
- };
-}
-
-// Axis-Aligned Box data type. (We'd usually say Axis-Aligned Bounding Box, but that's what it's being used for, not what it does.
-var AAB = (function () {
- "use strict";
-
- function AAB(lx,hx,ly,hy,lz,hz) {
- // Data properties are named numerically so that code can be written generically across dimensions.
- // TODO: Check that l < h? Do we want to require that?
- this[0] = lx;
- this[1] = hx;
- this[2] = ly;
- this[3] = hy;
- this[4] = lz;
- this[5] = hz;
+ util.exponentialStep = exponentialStep;
+
+ function fixedmultiplyVec3(matrix, vector) {
+ // glMatrix's multiplyVec3 doesn't work if the implicit fourth OUTPUT is not 1, so doesn't work for matrices such as inverted projection matrices
+ var four = [vector[0], vector[1], vector[2], 1];
+ mat4.multiplyVec4(matrix, four);
+ vector[0] = four[0]/four[3];
+ vector[1] = four[1]/four[3];
+ vector[2] = four[2]/four[3];
+ return vector;
}
-
- // Convenience for looking up a face by indexes:
- // dim -- axis: x=0 y=1 z=2
- // dir -- face: low=0 high=1
- AAB.prototype.get = function (dim, dir) {
- return this[dim*2+dir];
- };
-
- // Intersection test
- AAB.prototype.intersects = function (other) {
- for (var dim = 0; dim < 3; dim++)
- if (this[dim*2] < other[dim*2+1] || a2[dim*2+1] < a1[dim*2])
- return false;
- return true;
- };
-
- // The AABB of two AABs
- AAB.prototype.boundingUnion = function (other) {
- var out = new AAB();
- for (var dimb = 0; dimb < 6; dimb += 2) {
- out[dimb ] = Math.min(this[dimb ], other[dimb ]);
- out[dimb+1] = Math.max(this[dimb+1], other[dimb+1]);
+ util.fixedmultiplyVec3 = fixedmultiplyVec3;
+
+ var ZEROVEC = util.ZEROVEC = vec3.createFrom(0,0,0);
+ var UNIT_PX = util.UNIT_PX = vec3.createFrom(1,0,0);
+ var UNIT_PY = util.UNIT_PY = vec3.createFrom(0,1,0);
+ var UNIT_PZ = util.UNIT_PZ = vec3.createFrom(0,0,1);
+ var UNIT_NX = util.UNIT_NX = vec3.createFrom(-1,0,0);
+ var UNIT_NY = util.UNIT_NY = vec3.createFrom(0,-1,0);
+ var UNIT_NZ = util.UNIT_NZ = vec3.createFrom(0,0,-1);
+
+ // Helper for constructing DOM.
+ function mkelement(name, classes/* , child, child, ... */) {
+ var element = document.createElement(name);
+ element.className = classes;
+ for (var i = mkelement.length; i < arguments.length; i++) {
+ var childDes = arguments[i];
+ if (typeof childDes === "string") {
+ childDes = document.createTextNode(childDes);
+ }
+ element.appendChild(childDes);
}
- return out;
- };
-
- // Return this AAB translated by the specified offset
- AAB.prototype.translate = function (offset) {
- return new AAB(offset[0] + this[0],
- offset[0] + this[1],
- offset[1] + this[2],
- offset[1] + this[3],
- offset[2] + this[4],
- offset[2] + this[5]);
- };
-
- AAB.prototype.scale = function (scale) {
- return new AAB(scale * this[0],
- scale * this[1],
- scale * this[2],
- scale * this[3],
- scale * this[4],
- scale * this[5]);
- };
-
- AAB.prototype.rotate = function (rot) {
- var v0 = [this[0], this[2], this[4]]; rot.transformPoint(v0, v0);
- var v1 = [this[1], this[3], this[5]]; rot.transformPoint(v1, v1);
- return new AAB(Math.min(v0[0], v1[0]),
- Math.max(v0[0], v1[0]),
- Math.min(v0[1], v1[1]),
- Math.max(v0[1], v1[1]),
- Math.min(v0[2], v1[2]),
- Math.max(v0[2], v1[2]));
- };
-
- // The distance from the origin to the closest point not in this AAB.
- // Probably not useful unless this AAB contains the origin.
- AAB.prototype.minimumRadius = function () {
- return Math.max(0, Math.min(-this[0], this[1],
- -this[2], this[3],
- -this[4], this[5]));
- };
-
- // Create the AAB whose negative corner is at the given point.
- AAB.unitCube = function (point) {
- return new AAB(point[0], point[0] + 1,
- point[1], point[1] + 1,
- point[2], point[2] + 1);
- };
-
- return Object.freeze(AAB);
-}());
-
-// Given an element, replace its contents with a text node and return that, so that the element's text can be updated by setting the .data property of the result.
-function dynamicText(elem) {
- "use strict";
- while (elem.firstChild) elem.removeChild(elem.firstChild);
- var textNode = document.createTextNode("");
- elem.appendChild(textNode);
- textNode.data = "";
- return textNode;
-}
-
-// Utilities for working with those rotations (and improper rotations) which
-// are symmetries of the cube.
-var CubeRotation = (function () {
- "use strict";
-
- var RANGE = 64; // contains some duplicates due to "120-ness" having 3 possibilities in 2 bits
-
- function computeSymmetry(code, size, vec, result) {
- // Contributed by Jack Schmidt; see: <http://math.stackexchange.com/questions/78573/what-is-a-natural-way-to-enumerate-the-symmetries-of-a-cube>
-
- var x = vec[0];
- var y = vec[1];
- var z = vec[2];
-
- var t;
- // Peel off the "are we a reflection?" bit
- if( code & 32 ) { t=x; x=y; y=t; }
- // Peel off the "do we swap the tetrahedrons?" bit
- if( code & 16 ) { t=x; x=y; y=t; z=size-z; }
- // Now we are in tetrahedral group, peel off the "120-ness"
- switch( (code & (4+8) ) >> 2 ) {
- case 0: break;
- case 1: t=x; x=y; y=z; z=t; break;
- case 2: t=z; z=y; y=x; x=t; break;
- case 3: /* redundant w/ 0 */ break;
- }
- // Now we are in the Klein four group, peel off the "180-ness"
- switch( code & (1+2) ) {
- case 0: break;
- case 1: x=size-x; y=size-y; break;
- case 2: y=size-y; z=size-z; break;
- case 3: z=size-z; x=size-x; break;
- }
- if (!result) result = vec3.create();
- result[0] = x;
- result[1] = y;
- result[2] = z;
- return result;
- }
-
- function CubeRotation(code) {
- this.code = code;
- this.isReflection = code & 32;
- this._compositions = [];
-
- // Pre-rotated unit vectors
- this.px = this.transformVector(UNIT_PX);
- this.py = this.transformVector(UNIT_PY);
- this.pz = this.transformVector(UNIT_PZ);
- this.nx = this.transformVector(UNIT_NX);
- this.ny = this.transformVector(UNIT_NY);
- this.nz = this.transformVector(UNIT_NZ);
+ return element;
}
- CubeRotation.prototype.after = function (firstRotation) {
- return this._compositions[firstRotation.code];
- };
- CubeRotation.prototype.transformVector = function (vec, result) {
- // TODO: Find out if using a matrix is faster.
- return computeSymmetry(this.code, 0, vec, result);
- };
- CubeRotation.prototype.transformPoint = function (vec, result) {
- return computeSymmetry(this.code, 1, vec, result);
- };
+ util.mkelement = mkelement;
- // "Static methods"
- CubeRotation.canonicalCode = function (code) {
- code = mod(code | 0, RANGE);
- if ((code & (4+8)) >> 2 === 3) code &= ~(4+8); // remove nonexistent 4th case
- return code;
- };
- // Among the specified rotations, choose the one which minimizes the angle between the rotated 'toRotate' and 'direction'.
- CubeRotation.nearestToDirection = function (target, toRotate, rotations) {
- var cosine = -Infinity;
- var best = null;
- var vecbuf = vec3.create();
- for (var i = 0; i < rotations.length; i++) {
- var ia = vec3.dot(target, rotations[i].transformVector(toRotate, vecbuf));
- if (ia > cosine) {
- cosine = ia;
- best = rotations[i];
+ function prepareShader(gl, type, sources, declarations) {
<