jax 0.0.0.5 → 0.0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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