jax 0.0.0.5 → 0.0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/CHANGELOG +26 -1
  2. data/Rakefile +3 -1
  3. data/builtin/shaders/picking/common.ejs +2 -0
  4. data/builtin/shaders/picking/fragment.ejs +4 -0
  5. data/builtin/shaders/picking/material.js +14 -0
  6. data/builtin/shaders/picking/vertex.ejs +24 -0
  7. data/lib/jax/generators/app/templates/public/javascripts/jax.js +289 -681
  8. data/lib/jax/generators/app/templates/spec/javascripts/support/spec_layout.html.erb +55 -2
  9. data/lib/jax/generators/shader/templates/common.ejs.tt +2 -2
  10. data/lib/jax/packager/sprockets_template.rb +1 -4
  11. data/lib/jax/routes.rb +3 -0
  12. data/lib/jax/version.rb +1 -1
  13. data/spec/example_app/app/controllers/noise_controller.js +37 -5
  14. data/spec/example_app/app/controllers/picking_controller.js +32 -0
  15. data/spec/example_app/app/helpers/picking_helper.js +3 -0
  16. data/spec/example_app/app/models/blob.js +38 -0
  17. data/spec/example_app/app/resources/blobs/default.yml +2 -0
  18. data/spec/example_app/app/resources/materials/blob.yml +2 -2
  19. data/spec/example_app/app/shaders/blob/common.ejs +8 -13
  20. data/spec/example_app/app/shaders/blob/fragment.ejs +1 -1
  21. data/spec/example_app/app/shaders/blob/material.js +15 -12
  22. data/spec/example_app/app/shaders/blob/vertex.ejs +33 -8
  23. data/spec/example_app/app/views/picking/index.js +4 -0
  24. data/spec/example_app/config/routes.rb +1 -0
  25. data/spec/example_app/spec/javascripts/controllers/picking_controller_spec.js +11 -0
  26. data/spec/example_app/spec/javascripts/helpers/picking_helper_spec.js +12 -0
  27. data/spec/example_app/spec/javascripts/models/blob_spec.js +11 -0
  28. data/spec/example_app/spec/javascripts/support/spec_layout.html.erb +40 -2
  29. data/spec/javascripts/jax/model_spec.js +10 -0
  30. data/spec/javascripts/jax/world_spec.js +74 -1
  31. data/spec/javascripts/shaders/preprocessor_spec.js +35 -0
  32. data/src/constants.yml +1 -1
  33. data/src/jax.js +30 -8
  34. data/src/jax/anim_frame.js +6 -2
  35. data/src/jax/builtin/meshes/cube.js +22 -1
  36. data/src/jax/builtin/meshes/plane.js +27 -0
  37. data/src/jax/builtin/meshes/quad.js +7 -1
  38. data/src/jax/builtin/meshes/sphere.js +20 -1
  39. data/src/jax/builtin/meshes/teapot.js +10 -0
  40. data/src/jax/builtin/meshes/torus.js +18 -0
  41. data/src/jax/compatibility.js +165 -2
  42. data/src/jax/context.js +176 -9
  43. data/src/jax/core.js +6 -3
  44. data/src/jax/core/math.js +18 -3
  45. data/src/jax/core/matrix_stack.js +4 -3
  46. data/src/jax/core/util.js +15 -0
  47. data/src/jax/events.js +67 -12
  48. data/src/jax/geometry.js +5 -1
  49. data/src/jax/geometry/plane.js +59 -5
  50. data/src/jax/{controller.js → mvc/controller.js} +38 -0
  51. data/src/jax/mvc/helper.js +35 -0
  52. data/src/jax/mvc/model.js +301 -0
  53. data/src/jax/{route_set.js → mvc/route_set.js} +0 -0
  54. data/src/jax/{view.js → mvc/view.js} +6 -0
  55. data/src/jax/{view_manager.js → mvc/view_manager.js} +1 -0
  56. data/src/jax/noise.js +13 -0
  57. data/src/jax/prototype/class.js +3 -0
  58. data/src/jax/prototype/extensions.js +26 -8
  59. data/src/jax/webgl/camera.js +6 -6
  60. data/src/jax/webgl/core/framebuffer.js +4 -4
  61. data/src/jax/webgl/material.js +16 -0
  62. data/src/jax/webgl/mesh.js +19 -4
  63. data/src/jax/webgl/scene/light_manager.js +8 -0
  64. data/src/jax/webgl/scene/light_source.js +3 -3
  65. data/src/jax/webgl/shader.js +4 -2
  66. data/src/jax/webgl/shader_chain.js +1 -0
  67. data/src/jax/webgl/world.js +157 -6
  68. data/vendor/glmatrix/glMatrix.js +365 -408
  69. metadata +32 -10
  70. data/src/jax/helper.js +0 -8
  71. data/src/jax/model.js +0 -163
File without changes
@@ -30,8 +30,14 @@ Jax.View = (function() {
30
30
  this.view_func = view_func;
31
31
  },
32
32
 
33
+ /**
34
+ * Jax.View#render() -> Jax.View
35
+ *
36
+ * Renders this View. Returns itself.
37
+ **/
33
38
  render: function() {
34
39
  this.view_func();
40
+ return this;
35
41
  }
36
42
  });
37
43
  })();
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * class Jax.ViewManager
3
3
  *
4
+ * Maintains a registry of all Jax views and the paths to them.
4
5
  **/
5
6
  Jax.ViewManager = (function() {
6
7
  return Jax.Class.create({
data/src/jax/noise.js CHANGED
@@ -1,3 +1,16 @@
1
+ /**
2
+ * class Jax.Noise
3
+ * Constructs several textures to be used in vertex shaders involving Perlin noise.
4
+ *
5
+ * Example:
6
+ *
7
+ * var noise = new Jax.Noise();
8
+ * var program = new Jax.Shader.Program({...});
9
+ * program.manifest.set('perm', noise.perm);
10
+ * program.manifest.set('simplex', noise.simplex);
11
+ * program.manifest.set('grad', noise.grad);
12
+ *
13
+ **/
1
14
  Jax.Noise = (function() {
2
15
  var perm/*[256]*/ = [151,160,137,91,90,15,
3
16
  131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
@@ -1,5 +1,8 @@
1
1
  //= require "core"
2
2
 
3
+ /**
4
+ * Jax.Class
5
+ **/
3
6
  Jax.Class = (function() {
4
7
  var emptyFunction = function() { };
5
8
 
@@ -1,5 +1,3 @@
1
- /* My own custom extensions to Prototype */
2
-
3
1
  //= require "class"
4
2
 
5
3
  (function() {
@@ -112,12 +110,32 @@
112
110
  return klass;
113
111
  };
114
112
 
115
- /*
116
- class method #delegate
117
- usages:
118
- klass.delegate(/^(get|load|mult)(.*)Matrix$/).into("matrix_stack");
119
- klass.delegate("getProjectionMatrix", "loadModelMatrix").into("matrix_stack");
120
- */
113
+ /**
114
+ * Jax.Class.delegate() -> undefined
115
+ *
116
+ * This is a class method of all Jax classes.
117
+ *
118
+ * Delegates one or more methods into properties of the class. For instance,
119
+ * the following:
120
+ *
121
+ * MyClass.delegate("sayHello").into("person");
122
+ *
123
+ * will create a +sayHello+ method in the +MyClass+ class that internally calls
124
+ *
125
+ * this.person.sayHello(...)
126
+ *
127
+ * and returns the results.
128
+ *
129
+ * There are several other variants:
130
+ *
131
+ * klass.delegate(/regular expression/).into("property_name");
132
+ * // delegates any method name in +property_name+ that matches the expression
133
+ *
134
+ * klass.delegate("one", "two").into("property_name");
135
+ * // delegates both 'one' and 'two' methods into +property_name+
136
+ *
137
+ *
138
+ **/
121
139
  Jax.Class.Methods.delegate = function() {
122
140
  return new Delegator(this, arguments);
123
141
  };
@@ -361,12 +361,12 @@ Jax.Camera = (function() {
361
361
  },
362
362
 
363
363
  /**
364
- * Jax.Camera#getModelViewMatrix() -> mat4
364
+ * Jax.Camera#getTransformationMatrix() -> mat4
365
365
  *
366
- * Returns the ModelView matrix. This matrix represents the camera's position and
366
+ * Returns the transformation matrix. This matrix represents the camera's position and
367
367
  * orientation in the world.
368
368
  **/
369
- getModelViewMatrix: function() { return this.matrices.mv; },
369
+ getTransformationMatrix: function() { return this.matrices.mv; },
370
370
 
371
371
  /**
372
372
  * Jax.Camera#getProjectionMatrix() -> mat4
@@ -377,16 +377,16 @@ Jax.Camera = (function() {
377
377
  getProjectionMatrix: function() { return this.matrices.p; },
378
378
 
379
379
  /**
380
- * Jax.Camera#getNormalMatrix() -> mat4
380
+ * Jax.Camera#getNormalMatrix() -> mat3
381
381
  *
382
382
  * Returns the normal matrix, which is defined as the transpose of the inverse of the
383
- * ModelView matrix.
383
+ * transformation matrix.
384
384
  *
385
385
  * This matrix is commonly used in lighting calculations.
386
386
  **/
387
387
  getNormalMatrix: function() {
388
388
  if (!this.normal_matrix_up_to_date) {
389
- mat4.toInverseMat3(this.getModelViewMatrix(), this.matrices.n);
389
+ mat4.toInverseMat3(this.getTransformationMatrix(), this.matrices.n);
390
390
  mat3.transpose(this.matrices.n);
391
391
  }
392
392
  this.normal_matrix_up_to_date = true;
@@ -114,12 +114,12 @@ Jax.Framebuffer = (function() {
114
114
  *
115
115
  * * color: optionally, in place of a colors array, a single color format as above. If both
116
116
  * _color_ and _colors_ are specified, _color_ is simply added to the _colors_ array.
117
- * * depth: true if a depth attachment is required, false otherwise.
118
- * * stencil: true if a stencil attachment is required, false otherwise.
117
+ * * depth: true if a depth attachment is required, false otherwise. Defaults to false.
118
+ * * stencil: true if a stencil attachment is required, false otherwise. Defaults to false.
119
119
  * * width: the width of the render and color buffers. All render and color buffers for a given
120
- * framebuffer must have the same width. Defaults to 2048.
120
+ * framebuffer must have the same width. Defaults to 512.
121
121
  * * height: the height of the render and color buffers. All render and color buffers for a given
122
- * framebuffer must have the same height. Defaults to 2048.
122
+ * framebuffer must have the same height. Defaults to 512.
123
123
  *
124
124
  **/
125
125
  initialize: function(options) {
@@ -83,6 +83,20 @@ Jax.Material = (function() {
83
83
  }
84
84
  },
85
85
 
86
+ /**
87
+ * Jax.Material#supportsLighting() -> Boolean
88
+ *
89
+ * Returns true if this material supports lighting effects.
90
+ **/
91
+ supportsLighting: function() {
92
+ if (this.getName() == "lighting")
93
+ return true;
94
+ for (var i = 0; i < this.layers.length; i++)
95
+ if (this.layers[i].getName() == "lighting")
96
+ return true;
97
+ return false;
98
+ },
99
+
86
100
  getName: function() { return this.name; },
87
101
 
88
102
  addTextureLayer: function(tex) {
@@ -369,8 +383,10 @@ Jax.Material.addResources = function(resources) {
369
383
  //= require "../../../builtin/shaders/depthmap/material"
370
384
  //= require "../../../builtin/shaders/paraboloid/material"
371
385
  //= require "../../../builtin/shaders/fog/material"
386
+ //= require "../../../builtin/shaders/picking/material"
372
387
 
373
388
  Jax.Material.create("basic");
374
389
  Jax.Material.create("default", {default_shader:'basic'});
375
390
  Jax.Material.create("depthmap", {default_shader:"depthmap"});
376
391
  Jax.Material.create("paraboloid-depthmap", {type:"Paraboloid",default_shader:"paraboloid",layers:[{type:"Depthmap"}]});
392
+ Jax.Material.create("picking", {type:"Picking"});
@@ -225,6 +225,24 @@ Jax.Mesh = (function() {
225
225
  if (!this.draw_mode)
226
226
  this.draw_mode = GL_TRIANGLES;
227
227
  },
228
+
229
+ /**
230
+ * Jax.Mesh#setColor(red, green, blue, alpha) -> Jax.Mesh
231
+ * Sets the color of this mesh. This will set the color at each vertex, regardless
232
+ * of the original color of that vertex. The result will be that the entire mesh
233
+ * takes on the specified color (not just a particular vertex).
234
+ **/
235
+ setColor: function(red, green, blue, alpha) {
236
+ var buf = this.getColorBuffer();
237
+ for (var i = 0; i < buf.js.length; i += 4) {
238
+ buf.js[i] = red;
239
+ buf.js[i+1] = green;
240
+ buf.js[i+2] = blue;
241
+ buf.js[i+3] = alpha;
242
+ }
243
+ buf.refresh();
244
+ return this;
245
+ },
228
246
 
229
247
  /**
230
248
  * Jax.Mesh#dispose() -> undefined
@@ -302,14 +320,11 @@ Jax.Mesh = (function() {
302
320
  var result = Jax.Util.normalizeOptions(options, {
303
321
  material: this.material,
304
322
  default_material: this.default_material,
305
- // default_shader: this.default_shader,
306
- // shader: this.shader,
307
323
  draw_mode: this.draw_mode || GL_TRIANGLES
308
324
  });
309
325
 
310
326
  if (!result.material) result.material = result.default_material;
311
- // if (!result.shader) result.shader = result.default_shader;
312
-
327
+
313
328
  result.material = findMaterial(result.material);
314
329
 
315
330
  return result;
@@ -38,11 +38,15 @@ Jax.Scene.LightManager = (function() {
38
38
  },
39
39
 
40
40
  illuminate: function(context, objects, options) {
41
+ options = Jax.Util.normalizeOptions(options, {});
42
+
41
43
  // Use alpha blending for the first pass, and additive blending for subsequent passes.
42
44
  this.context.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
43
45
  for (var i = 0; i < this._lights.length; i++) {
44
46
  for (var j = 0; j < objects.length; j++) {
45
47
  this._current_light = i;
48
+ options.model_index = j;
49
+
46
50
  /* TODO optimization: see if objects[j] is even affected by this._lights[i] (based on attenuation) */
47
51
  if (objects[j].isLit())
48
52
  objects[j].render(context, options);
@@ -53,9 +57,13 @@ Jax.Scene.LightManager = (function() {
53
57
  },
54
58
 
55
59
  ambient: function(context, objects, options) {
60
+ options = Jax.Util.normalizeOptions(options, {});
61
+
56
62
  for (var i = 0; i < this._lights.length; i++) {
57
63
  this._current_light = i;
58
64
  for (var j = 0; j < objects.length; j++) {
65
+ options.model_index = j;
66
+
59
67
  /* TODO optimization: see if objects[j] is even affected by this._lights[i] (based on attenuation) */
60
68
  if (objects[j].isLit())
61
69
  objects[j].render(context, options);
@@ -167,7 +167,7 @@ Jax.Scene.LightSource = (function() {
167
167
  // front paraboloid
168
168
  self.framebuffers[0].viewport(context);
169
169
  context.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
170
- context.loadViewMatrix(self.camera.getModelViewMatrix());
170
+ context.loadViewMatrix(self.camera.getTransformationMatrix());
171
171
  mat4.set(context.getInverseViewMatrix(), sm);
172
172
 
173
173
  for (var i = 0; i < objects.length; i++) {
@@ -179,7 +179,7 @@ Jax.Scene.LightSource = (function() {
179
179
  // back paraboloid
180
180
  self.framebuffers[1].viewport(context);
181
181
  context.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
182
- context.loadViewMatrix(self.camera.getModelViewMatrix());
182
+ context.loadViewMatrix(self.camera.getTransformationMatrix());
183
183
  for (var i = 0; i < objects.length; i++) {
184
184
  objects[i].render(context, {material:paraboloid_depthmap,direction:-1});
185
185
  }
@@ -203,7 +203,7 @@ Jax.Scene.LightSource = (function() {
203
203
  context.pushMatrix(function() {
204
204
  self.framebuffers[0].viewport(context);
205
205
  context.matrix_stack.loadProjectionMatrix(self.camera.getProjectionMatrix());
206
- context.matrix_stack.loadViewMatrix(self.camera.getModelViewMatrix());
206
+ context.matrix_stack.loadViewMatrix(self.camera.getTransformationMatrix());
207
207
  mat4.identity(sm);
208
208
  sm[0] = sm[5] = sm[10] = sm[12] = sm[13] = sm[14] = 0.5;
209
209
  mat4.multiply(sm, context.getModelViewProjectionMatrix());
@@ -103,12 +103,13 @@ Jax.Shader = (function() {
103
103
  for (var i in map) {
104
104
  if (map[i].shared) {
105
105
  if (options.skip_global_definitions && options.skip_global_definitions.indexOf(map[i].full_name) != -1) {
106
- var rx = new RegExp("(shared\\s+)?"+map[i].scope+"\\s*"+map[i].type+"[^;]*?"+map[i].full_name+"[^;]*?;\n?", "g");
106
+ var rx = new RegExp("(shared\\s+)"+map[i].scope+"\\s*"+map[i].type+"[^;]*?"+map[i].full_name+"[^;]*?;\n?", "g");
107
107
  source = source.replace(rx, "");
108
108
  }
109
109
  // else source = source.replace(rx, map[i].scope+" "+map[i].type+" "+map[i].full_name+";\n");
110
110
  }
111
111
  }
112
+
112
113
  return source.replace(/shared\s+/, '');
113
114
  }
114
115
 
@@ -121,6 +122,7 @@ Jax.Shader = (function() {
121
122
  source = applyExports(self, options, source);
122
123
  source = applyImports(self, options, source);
123
124
  source = mangleUniformsAndAttributes(self, options, source);
125
+
124
126
  return source;
125
127
  }
126
128
 
@@ -234,7 +236,7 @@ Jax.Shader = (function() {
234
236
  (this.getRawSource(options, 'fragment') || "");
235
237
 
236
238
  // if it's not a "shared" uniform, mangle its name.
237
- var rx = new RegExp("(^|\\n|\\s+)((shared\\s+)?)(uniform|attribute|varying) (\\w+) ((?!"+prefix+")[^;]*);"), result;
239
+ var rx = new RegExp("(^|\\n|\\s+)((shared\\s+)?)(uniform|attribute|varying)\\s+(\\w+)\\s+((?!"+prefix+")[^;]*);"), result;
238
240
  while (result = rx.exec(source)) {
239
241
  var shared = /shared/.test(result[2]);
240
242
  var scope = result[4];
@@ -230,6 +230,7 @@ Jax.ShaderChain = (function() {
230
230
  else map[variable.full_name] = variable;
231
231
  }
232
232
  }
233
+
233
234
  return map;
234
235
  },
235
236
 
@@ -1,6 +1,23 @@
1
1
  //= require "scene"
2
2
 
3
+ /**
4
+ * class Jax.World
5
+ *
6
+ * A +Jax.World+ represents a scene in the graphics engine. All objects to be rendered (or at least,
7
+ * all objects that you do not want to manually control!) should be added to the world. Each instance
8
+ * of +Jax.Context+ has its own +Jax.World+, and the currently-active +Jax.World+ is delegated into
9
+ * controllers and views as the +this.world+ property.
10
+ *
11
+ **/
3
12
  Jax.World = (function() {
13
+ function getPickBuffer(self) {
14
+ if (self.pickBuffer) return self.pickBuffer;
15
+ return self.pickBuffer = new Jax.Framebuffer({
16
+ width:self.context.canvas.width,
17
+ height:self.context.canvas.height
18
+ });
19
+ }
20
+
4
21
  return Jax.Class.create({
5
22
  initialize: function(context) {
6
23
  this.context = context;
@@ -9,12 +26,38 @@ Jax.World = (function() {
9
26
  this.shadow_casters = [];
10
27
  },
11
28
 
29
+ /**
30
+ * Jax.World#addLightSource(light) -> Jax.Scene.LightSource
31
+ * - light (Jax.Scene.LightSource) : the instance of Jax.Scene.LightSource to add to this world.
32
+ *
33
+ * Adds the light to the world and then returns the light itself unchanged.
34
+ **/
12
35
  addLightSource: function(light) { this.lighting.add(light); },
13
36
 
37
+ /**
38
+ * Jax.World#addObject(object) -> Jax.Model
39
+ * - object (Jax.Model) : the instance of Jax.Model to add to this world.
40
+ *
41
+ * Adds the model to the world and then returns the model itself unchanged.
42
+ *
43
+ **/
14
44
  addObject: function(object) { this.objects.push(object); this.invalidate(); return object; },
15
45
 
46
+ /**
47
+ * Jax.World#getObject(index) -> Jax.Model
48
+ * - index (Number) : the world index of this object
49
+ *
50
+ * Returns the object with the specified world index, or undefined.
51
+ **/
16
52
  getObject: function(index) { return this.objects[index]; },
17
53
 
54
+ /**
55
+ * Jax.World#removeObject(object_or_index) -> Jax.Model
56
+ * - object_or_index (Number|Jax.Model) : the model instance to remove, or its world index
57
+ *
58
+ * If the model or its index cannot be found, nothing happens and the return value is undefined.
59
+ * Otherwise, the object is removed from this World and then returned.
60
+ **/
18
61
  removeObject: function(object_or_index) {
19
62
  if (this.objects[object_or_index]) {
20
63
  var obj = this.objects[object_or_index];
@@ -32,6 +75,110 @@ Jax.World = (function() {
32
75
  }
33
76
  },
34
77
 
78
+ /**
79
+ * Jax.World#pickRegionalIndices(x1, y1, x2, y2[, ary]) -> Array
80
+ * - x1 (Number): the screen X coordinate of the first corner of the 2D rectangle within which to pick
81
+ * - y1 (Number): the screen Y coordinate of the first corner of the 2D rectangle within which to pick
82
+ * - x2 (Number): the screen X coordinate of the second corner of the 2D rectangle within which to pick
83
+ * - y2 (Number): the screen Y coordinate of the second corner of the 2D rectangle within which to pick
84
+ * - ary (Array): an optional array to populate. A new one will be created if this is not specified.
85
+ * (Note: the array's contents will be cleared.)
86
+ *
87
+ * Picks all visible object indices within the specified rectangular regions and returns them as elements in
88
+ * an array.
89
+ *
90
+ * An object's index matches its position in the world's object list. That is, the following code is
91
+ * valid:
92
+ *
93
+ * this.world.getObjects(this.world.pickRegionalIDs(0,0, 100,100));
94
+ *
95
+ **/
96
+ pickRegionalIndices: function(x1, y1, x2, y2, ary) {
97
+ var w = Math.abs(x2 - x1), h = Math.abs(y2 - y1);
98
+ if (ary) ary.clear();
99
+ else ary = new Array();
100
+ var world = this, pickBuffer = getPickBuffer(this), context = this.context;
101
+ var data = new Uint8Array(w*h*4);
102
+
103
+ pickBuffer.bind(context, function() {
104
+ pickBuffer.viewport(context);
105
+ context.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
106
+ context.glDisable(GL_BLEND);
107
+ world.render({material:"picking"});
108
+ // read it back in
109
+ context.glReadPixels(x1, y1, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
110
+ if (data.data) data = data.data;
111
+ });
112
+
113
+ // restore the visible viewport and blending
114
+ context.glViewport(0, 0, context.canvas.width, context.canvas.height);
115
+ context.glEnable(GL_BLEND);
116
+
117
+ var index;
118
+ for (var i = 2; i < data.length; i += 4) {
119
+ if (data[i] > 0) { // blue key exists, we've found an object
120
+ index = Jax.Util.decodePickingColor(data[i-2], data[i-1], data[i], data[i+1]);
121
+ if (index != undefined) {
122
+ ary.push(index);
123
+ }
124
+ }
125
+ }
126
+
127
+ return ary;
128
+ },
129
+
130
+ /**
131
+ * Jax.World#pickRegion(x1, y1, x2, y2) -> Array
132
+ * - x1 (Number): the screen X coordinate of the first corner of the 2D rectangle within which to pick
133
+ * - y1 (Number): the screen Y coordinate of the first corner of the 2D rectangle within which to pick
134
+ * - x2 (Number): the screen X coordinate of the second corner of the 2D rectangle within which to pick
135
+ * - y2 (Number): the screen Y coordinate of the second corner of the 2D rectangle within which to pick
136
+ * - ary (Array): an optional array to populate. A new one will be created if this is not specified.
137
+ * (Note: the array's contents will be cleared.)
138
+ *
139
+ * Picks all visible objects within the specified rectangular regions and returns them as elements in
140
+ * an array.
141
+ **/
142
+ pickRegion: function(x1, y1, x2, y2, ary) {
143
+ var result = this.pickRegionalIndices(x1, y1, x2, y2);
144
+ for (var i = 0; i < result.length; i++)
145
+ result[i] = this.getObject(i);
146
+ return result;
147
+ },
148
+
149
+ /**
150
+ * Jax.World#pickIndex(x, y) -> Number | undefined
151
+ * - x (Number): the screen X coordinate to pick at
152
+ * - y (Number): the screen Y coordinate to pick at
153
+ *
154
+ * Picks the visible object at the specified position and returns its index as used by
155
+ * +Jax.World#getObject+. If no object is visible at the given position, the special value
156
+ * +undefined+ is returned.
157
+ **/
158
+ pickIndex: function(x, y) {
159
+ this._pick_ary = this._pick_ary || new Array();
160
+ return this.pickRegionalIndices(x, y, x+1, y+1, this._pick_ary)[0];
161
+ },
162
+
163
+ /**
164
+ * Jax.World#pick(x, y) -> Jax.Model | undefined
165
+ * - x (Number): the screen X coordinate to pick at
166
+ * - y (Number): the screen Y coordinate to pick at
167
+ *
168
+ * Picks the visible object at the specified position and returns it. If no object is visible
169
+ * at the given position, the special value +undefined+ is returned.
170
+ **/
171
+ pick: function(x, y) {
172
+ var index = this.pickIndex(x, y);
173
+ if (index != undefined)
174
+ return this.getObject(index);
175
+ return index;
176
+ },
177
+
178
+ /**
179
+ * Jax.World#countObjects() -> Number
180
+ * Returns the number of objects currently registered with this World.
181
+ **/
35
182
  countObjects: function() {
36
183
  return this.objects.length;
37
184
  },
@@ -63,16 +210,16 @@ Jax.World = (function() {
63
210
 
64
211
  getShadowCasters: function() { return this.shadow_casters; },
65
212
 
66
- render: function() {
213
+ render: function(options) {
67
214
  var i;
68
215
 
69
216
  /* this.current_pass is used by the material */
70
217
 
71
218
  this.context.current_pass = Jax.Scene.AMBIENT_PASS;
72
219
  this.context.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
73
- var unlit = {unlit:true};
220
+ var unlit = Jax.Util.normalizeOptions(options, {unlit:true});
74
221
 
75
- if (this.lighting.isEnabled()) {
222
+ if (this.lighting.isEnabled() && (!unlit.material || unlit.material.supportsLighting())) {
76
223
  /* ambient pass */
77
224
  /*
78
225
  So.... I see a legit need for an ambient pass for A) unlit objects and B)
@@ -81,8 +228,10 @@ Jax.World = (function() {
81
228
  lighting take care of (ambient + diffuse + specular) all at once?
82
229
  */
83
230
  for (i = 0; i < this.objects.length; i++)
84
- if (!this.objects[i].lit)
231
+ if (!this.objects[i].lit) {
232
+ unlit.model_index = i;
85
233
  this.objects[i].render(this.context, unlit);
234
+ }
86
235
  // this.lighting.ambient(this.context, this.objects);
87
236
 
88
237
  /* shadowgen pass */
@@ -94,10 +243,12 @@ Jax.World = (function() {
94
243
 
95
244
  /* illumination pass */
96
245
  this.context.current_pass = Jax.Scene.ILLUMINATION_PASS;
97
- this.lighting.illuminate(this.context, this.objects);
246
+ this.lighting.illuminate(this.context, this.objects, options);
98
247
  } else {
99
- for (i = 0; i < this.objects.length; i++)
248
+ for (i = 0; i < this.objects.length; i++) {
249
+ unlit.model_index = i;
100
250
  this.objects[i].render(this.context, unlit);
251
+ }
101
252
  }
102
253
  },
103
254