jax 0.0.0.8 → 0.0.0.9

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 (31) hide show
  1. data/CHANGELOG +22 -0
  2. data/Rakefile +2 -1
  3. data/builtin/shaders/functions/noise.ejs +0 -3
  4. data/guides/assets/javascripts/syntaxhighlighter/shBrushCpp.js +31 -3
  5. data/guides/jax_guides/common.rb +1 -1
  6. data/guides/source/index.html.erb +4 -4
  7. data/guides/source/shaders.textile +496 -2
  8. data/lib/jax/generators/app/templates/public/javascripts/jax.js +130 -94
  9. data/lib/jax/generators/app/templates/spec/javascripts/support/spec_helpers/jax_spec_environment_helper.js +33 -0
  10. data/lib/jax/generators/app/templates/spec/javascripts/support/spec_layout.html.erb +1 -13
  11. data/lib/jax/generators/shader/templates/fragment.ejs.tt +2 -3
  12. data/lib/jax/generators/shader/templates/spec.js.tt +4 -11
  13. data/lib/jax/routes.rb +0 -3
  14. data/lib/jax/tasks/rake.rb +4 -0
  15. data/lib/jax/version.rb +1 -1
  16. data/spec/example_app/app/shaders/blob/vertex.ejs +2 -0
  17. data/spec/example_app/spec/javascripts/shaders/blob_spec.js +5 -8
  18. data/spec/example_app/spec/javascripts/support/spec_helpers/jax_spec_environment_helper.js +33 -0
  19. data/spec/example_app/spec/javascripts/support/spec_layout.html.erb +1 -13
  20. data/spec/generators/app_generator_spec.rb +1 -0
  21. data/spec/javascripts/helpers/jax_spec_environment_helper.js +33 -0
  22. data/spec/javascripts/helpers/{SpecHelper.js → jax_spec_helper.js} +0 -0
  23. data/spec/javascripts/jax/core/utils_spec.js +21 -2
  24. data/spec/lib/jax/tasks/jax_rake_spec.rb +13 -0
  25. data/src/jax/context.js +1 -1
  26. data/src/jax/core/util.js +12 -5
  27. data/src/jax/mvc/model.js +1 -0
  28. data/src/jax/webgl/scene/light_manager.js +58 -45
  29. data/src/jax/webgl/scene/light_source.js +49 -4
  30. data/src/jax/webgl/world.js +12 -46
  31. metadata +11 -6
@@ -2256,18 +2256,25 @@ Jax.Util = {
2256
2256
 
2257
2257
  vectorize: function(data) {
2258
2258
  if (data) {
2259
- var res = vec3.create();
2259
+ var res = [];//vec3.create();
2260
2260
  if (typeof(data) == "string") {
2261
2261
  var components = data.split(/[,\s]+/);
2262
- if (components.length >= 3) {
2263
- for (var i = 0; i < 3; i++)
2262
+ if (components.length >= 2) {
2263
+ for (var i = 0; i < components.length; i++)
2264
2264
  res[i] = parseFloat(components[i]);
2265
2265
  }
2266
2266
  return res;
2267
2267
  }
2268
2268
  if (data.length && data.length >= 3) return vec3.set(data, res);
2269
- if ((res[0] = data.x) != undefined && (res[1] = data.y) != undefined && (res[2] = data.z) != undefined) return res;
2270
- if ((res[0] = data[0]) != undefined && (res[1] = data[1]) != undefined && (res[2] = data[2]) != undefined) return res;
2269
+ else if (data.length == 2) return [data[0], data[1]];
2270
+ if ((res[0] = data.x) != undefined && (res[1] = data.y) != undefined) {
2271
+ if (data.z != undefined) res[2] = data.z;
2272
+ return res;
2273
+ }
2274
+ if ((res[0] = data[0]) != undefined && (res[1] = data[1]) != undefined) {
2275
+ if (data[2] != undefined) res[2] = data[2];
2276
+ return res;
2277
+ }
2271
2278
  }
2272
2279
  throw new Error("Input argument for Jax.Util.vectorize not recognized: "+JSON.stringify(data));
2273
2280
  },
@@ -2783,6 +2790,7 @@ Jax.Helper = {
2783
2790
  },
2784
2791
 
2785
2792
  getBoundingCube: function() {
2793
+ if (!this.mesh) return {left:0,right:0,bottom:0,top:0,front:0,back:0,width:0,height:0,depth:0};
2786
2794
  if (!this.mesh.built) this.mesh.rebuild();
2787
2795
  return this.mesh.bounds;
2788
2796
  },
@@ -5334,6 +5342,9 @@ Jax.Scene.LightSource = (function() {
5334
5342
  data.color.specular= Jax.Util.colorize(data.color.specular);
5335
5343
  $super(data);
5336
5344
 
5345
+ var self = this;
5346
+ this.camera.addEventListener('matrixUpdated', function() { self.invalidate(); });
5347
+
5337
5348
  this.spotExponent = this.spot_exponent;
5338
5349
  delete this.spot_exponent;
5339
5350
 
@@ -5406,13 +5417,47 @@ Jax.Scene.LightSource = (function() {
5406
5417
  return this.shadowMatrix;
5407
5418
  },
5408
5419
 
5420
+ registerCaster: function(object) {
5421
+ var self = this;
5422
+ function updated() { self.invalidate(); }
5423
+ object.camera.addEventListener('matrixUpdated', updated);
5424
+ this.invalidate();
5425
+ },
5426
+
5427
+ unregisterCaster: function(object) {
5428
+ /* FIXME remove the shadowmap event listener from object's camera matrix */
5429
+ },
5430
+
5409
5431
  isShadowcaster: function() { return this.shadowcaster; },
5410
5432
 
5411
5433
  getDPShadowNear: function() { setupProjection(this); return this.camera.projection.near; },
5412
5434
 
5413
5435
  getDPShadowFar: function() { setupProjection(this); return this.camera.projection.far; },
5414
5436
 
5415
- updateShadowMap: function(context, sceneBoundingRadius, objects) {
5437
+ invalidate: function() { this.valid = false; },
5438
+
5439
+ render: function(context, objects, options) {
5440
+ if (!this.valid) {
5441
+ var real_pass = context.current_pass;
5442
+ /* shadowgen pass */
5443
+ context.current_pass = Jax.Scene.SHADOWMAP_PASS;
5444
+ this.updateShadowMap(context, this.boundingRadius, objects, options);
5445
+ this.valid = true;
5446
+ context.current_pass = real_pass;
5447
+ }
5448
+
5449
+ for (var j = 0; j < objects.length; j++) {
5450
+ options.model_index = j;
5451
+
5452
+ /* TODO optimization: see if objects[j] is even affected by this light (based on attenuation) */
5453
+ if (objects[j].isLit()) // it could be unlit but still in array if it casts a shadow
5454
+ objects[j].render(context, options);
5455
+ }
5456
+ },
5457
+
5458
+ updateShadowMap: function(context, sceneBoundingRadius, objects, render_options) {
5459
+ render_options = Jax.Util.normalizeOptions(render_options, {});
5460
+
5416
5461
  setupProjection(this);
5417
5462
 
5418
5463
  var self = this;
@@ -5442,6 +5487,9 @@ Jax.Scene.LightSource = (function() {
5442
5487
  context.glPolygonOffset(2.0, 2.0);
5443
5488
 
5444
5489
  context.pushMatrix(function() {
5490
+ render_options.direction = 1;
5491
+ render_options.material = paraboloid_depthmap;
5492
+
5445
5493
  self.framebuffers[0].bind(context, function() {
5446
5494
  self.framebuffers[0].viewport(context);
5447
5495
  context.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -5449,16 +5497,18 @@ Jax.Scene.LightSource = (function() {
5449
5497
  mat4.set(context.getInverseViewMatrix(), sm);
5450
5498
 
5451
5499
  for (var i = 0; i < objects.length; i++) {
5452
- objects[i].render(context, {material:paraboloid_depthmap, direction:1});
5500
+ objects[i].render(context, render_options);
5453
5501
  }
5454
5502
  });
5455
5503
 
5504
+ render_options.direction = -1;
5505
+
5456
5506
  self.framebuffers[1].bind(context, function() {
5457
5507
  self.framebuffers[1].viewport(context);
5458
5508
  context.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
5459
5509
  context.loadViewMatrix(self.camera.getTransformationMatrix());
5460
5510
  for (var i = 0; i < objects.length; i++) {
5461
- objects[i].render(context, {material:paraboloid_depthmap,direction:-1});
5511
+ objects[i].render(context, render_options);
5462
5512
  }
5463
5513
  });
5464
5514
 
@@ -5473,6 +5523,8 @@ Jax.Scene.LightSource = (function() {
5473
5523
  return;
5474
5524
  }
5475
5525
 
5526
+ render_options.material = "depthmap";
5527
+
5476
5528
  this.framebuffers[0].bind(context, function() {
5477
5529
  self.framebuffers[0].viewport(context);
5478
5530
  context.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -5490,7 +5542,7 @@ Jax.Scene.LightSource = (function() {
5490
5542
  context.glEnable(GL_POLYGON_OFFSET_FILL);
5491
5543
  context.glPolygonOffset(2.0, 2.0);
5492
5544
  for (var i = 0; i < objects.length; i++) {
5493
- objects[i].render(context, {material:'depthmap'});
5545
+ objects[i].render(context, render_options);
5494
5546
  }
5495
5547
  context.glDisable(GL_POLYGON_OFFSET_FILL);
5496
5548
  context.glEnable(GL_BLEND);
@@ -5528,12 +5580,64 @@ Jax.Scene.LightManager = (function() {
5528
5580
  initialize: function(context) {
5529
5581
  this.context = context;
5530
5582
  this._lights = [];
5583
+ this.objects = [];
5584
+ },
5585
+
5586
+ addObject: function(obj) {
5587
+ this.objects.push(obj);
5588
+ for (var j = 0; j < this._lights.length; j++)
5589
+ if (obj.isShadowCaster())
5590
+ this._lights[j].registerCaster(obj);
5591
+ this.recalculateBoundingRadius();
5592
+ },
5593
+
5594
+ removeObject: function(obj) {
5595
+ if (this.objects[obj]) {
5596
+ var o = this.objects[obj];
5597
+ this.objects.splice(obj, 1);
5598
+ for (var j = 0; j < this._lights.length; j++)
5599
+ if (o.isShadowCaster())
5600
+ this._lights[j].unregisterCaster(o);
5601
+ this.recalculateBoundingRadius();
5602
+ return o;
5603
+ }
5604
+ for (var i = 0; i < this.objects.length; i++)
5605
+ if (this.objects[i] == obj)
5606
+ return this.removeObject(obj);
5607
+ },
5608
+
5609
+ getShadowCasters: function() {
5610
+ var ret = [];
5611
+ for (var i = 0; i < this.objects.length; i++) {
5612
+ if (this.objects[i].isShadowCaster())
5613
+ ret.push(this.objects[i]);
5614
+ }
5615
+ return ret;
5531
5616
  },
5532
5617
 
5533
5618
  add: function(light) {
5534
5619
  if (this._lights.length == Jax.max_lights)
5535
5620
  throw new Error("Maximum number of light sources in a scene has been exceeded! Try removing some first.");
5621
+ for (var i = 0; i < this.objects.length; i++) {
5622
+ if (this.objects[i].isShadowCaster())
5623
+ light.registerCaster(this.objects[i]);
5624
+ }
5536
5625
  this._lights.push(light);
5626
+ light.boundingRadius = this.boundingRadius || 0;
5627
+ },
5628
+
5629
+ recalculateBoundingRadius: function() {
5630
+ var boundingRadius = null;
5631
+ var i, j;
5632
+ for (i = 0; i < this.objects.length; i++) {
5633
+ j = vec3.length(this.objects[i].camera.getPosition()) + this.objects[i].getBoundingSphereRadius();
5634
+ if (boundingRadius == null || boundingRadius < j)
5635
+ boundingRadius = j;
5636
+ }
5637
+ this.boundingRadius = boundingRadius = boundingRadius || 0;
5638
+
5639
+ for (i = 0; i < this._lights.length; i++)
5640
+ this._lights[i].boundingRadius = boundingRadius;
5537
5641
  },
5538
5642
 
5539
5643
  enable: function() { this.enabled = true; },
@@ -5560,52 +5664,17 @@ Jax.Scene.LightManager = (function() {
5560
5664
  return result;
5561
5665
  },
5562
5666
 
5563
- illuminate: function(context, objects, options) {
5667
+ illuminate: function(context, options) {
5564
5668
  options = Jax.Util.normalizeOptions(options, {});
5565
5669
 
5566
5670
  this.context.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5567
- for (var i = 0; i < this._lights.length; i++) {
5568
- for (var j = 0; j < objects.length; j++) {
5569
- this._current_light = i;
5570
- options.model_index = j;
5571
-
5572
- /* TODO optimization: see if objects[j] is even affected by this._lights[i] (based on attenuation) */
5573
- if (objects[j].isLit())
5574
- objects[j].render(context, options);
5575
- }
5576
- this.context.glBlendFunc(GL_ONE, GL_ONE);
5577
- }
5578
- delete this._current_light;
5579
- },
5580
-
5581
- ambient: function(context, objects, options) {
5582
- options = Jax.Util.normalizeOptions(options, {});
5583
-
5584
5671
  for (var i = 0; i < this._lights.length; i++) {
5585
5672
  this._current_light = i;
5586
- for (var j = 0; j < objects.length; j++) {
5587
- options.model_index = j;
5588
-
5589
- /* TODO optimization: see if objects[j] is even affected by this._lights[i] (based on attenuation) */
5590
- if (objects[j].isLit())
5591
- objects[j].render(context, options);
5592
- }
5593
- }
5594
- delete this._current_light;
5595
- },
5596
-
5597
- updateShadowMaps: function(context, objects) {
5598
- var boundingRadius = null;
5599
- var i, j;
5600
- for (i = 0; i < objects.length; i++) {
5601
- j = vec3.length(objects[i].camera.getPosition()) + objects[i].getBoundingSphereRadius();
5602
- if (boundingRadius == null || boundingRadius < j)
5603
- boundingRadius = j;
5673
+ this._lights[i].render(context, this.objects, options);
5674
+ if (i == 0) this.context.glBlendFunc(GL_ONE, GL_ONE);
5604
5675
  }
5605
- boundingRadius = boundingRadius || 0;
5606
5676
 
5607
- for (i = 0; i < this._lights.length; i++)
5608
- this._lights[i].updateShadowMap(context, boundingRadius, objects);
5677
+ delete this._current_light;
5609
5678
  },
5610
5679
 
5611
5680
  getDirection: function(index) { return this.getLight(index).getDirection(); },
@@ -5978,7 +6047,12 @@ Jax.World = (function() {
5978
6047
 
5979
6048
  addLightSource: function(light) { this.lighting.add(light); },
5980
6049
 
5981
- addObject: function(object) { this.objects.push(object); this.invalidate(); return object; },
6050
+ addObject: function(object) {
6051
+ this.objects.push(object);
6052
+ if (object.isLit() || object.isShadowCaster())
6053
+ this.lighting.addObject(object);
6054
+ return object;
6055
+ },
5982
6056
 
5983
6057
  getObject: function(index) { return this.objects[index]; },
5984
6058
 
@@ -5986,7 +6060,7 @@ Jax.World = (function() {
5986
6060
  if (this.objects[object_or_index]) {
5987
6061
  var obj = this.objects[object_or_index];
5988
6062
  this.objects.splice(object_or_index, 1);
5989
- this.invalidate();
6063
+ this.lighting.removeObject(obj);
5990
6064
  return obj;
5991
6065
  }
5992
6066
  else
@@ -5994,7 +6068,7 @@ Jax.World = (function() {
5994
6068
  if (this.objects[i] == object_or_index)
5995
6069
  {
5996
6070
  this.objects.splice(i, 1);
5997
- this.invalidate();
6071
+ this.lighting.removeObject(this.objects[i]);
5998
6072
  return this.objects[i];
5999
6073
  }
6000
6074
  },
@@ -6054,32 +6128,7 @@ Jax.World = (function() {
6054
6128
  return this.objects.length;
6055
6129
  },
6056
6130
 
6057
- invalidate: function() {
6058
- while (this.shadow_casters.length > 0) {
6059
- /* TODO we still need to unregister the camera event listener */
6060
- this.shadow_casters.pop();
6061
- }
6062
-
6063
- var updated = function() { self.shadowmaps_valid = false; };
6064
-
6065
- var i;
6066
- for (i = 0; i < this.objects.length; i++) {
6067
- var self = this;
6068
- if (this.objects[i].isShadowCaster()) {
6069
- this.objects[i].camera.addEventListener('matrixUpdated', updated);
6070
- this.shadow_casters.push(this.objects[i]);
6071
- }
6072
- }
6073
-
6074
- for (i = 0; i < this.lighting.count(); i++) {
6075
- var light = this.lighting.getLight(i);
6076
- if (light.isShadowcaster())
6077
- light.camera.addEventListener('matrixUpdated', updated);
6078
- }
6079
- this.shadowmaps_valid = false;
6080
- },
6081
-
6082
- getShadowCasters: function() { return this.shadow_casters; },
6131
+ getShadowCasters: function() { return this.lighting.getShadowCasters(); },//return this.shadow_casters; },
6083
6132
 
6084
6133
  render: function(options) {
6085
6134
  var i;
@@ -6091,29 +6140,16 @@ Jax.World = (function() {
6091
6140
  var unlit = Jax.Util.normalizeOptions(options, {unlit:true});
6092
6141
 
6093
6142
  if (this.lighting.isEnabled() && (!unlit.material || unlit.material.supportsLighting())) {
6094
- /* ambient pass */
6095
- /*
6096
- So.... I see a legit need for an ambient pass for A) unlit objects and B)
6097
- scene lighting. But jax doesn't yet support scene lighting so really only
6098
- unlit objects need an ambient pass. For lit objects, why not let
6099
- lighting take care of (ambient + diffuse + specular) all at once?
6100
- */
6143
+ /* ambient pass - unlit objects only because lit objects get ambient+diffuse+specular in one pass */
6101
6144
  for (i = 0; i < this.objects.length; i++)
6102
- if (!this.objects[i].lit) {
6145
+ if (!this.objects[i].isLit()) {
6103
6146
  unlit.model_index = i;
6104
6147
  this.objects[i].render(this.context, unlit);
6105
6148
  }
6106
6149
 
6107
- /* shadowgen pass */
6108
- this.context.current_pass = Jax.Scene.SHADOWMAP_PASS;
6109
- if (!this.shadowmaps_valid) {
6110
- this.lighting.updateShadowMaps(this.context, this.shadow_casters);
6111
- this.shadowmaps_valid = true;
6112
- }
6113
-
6114
6150
  /* illumination pass */
6115
6151
  this.context.current_pass = Jax.Scene.ILLUMINATION_PASS;
6116
- this.lighting.illuminate(this.context, this.objects, options);
6152
+ this.lighting.illuminate(this.context, options);
6117
6153
  } else {
6118
6154
  for (i = 0; i < this.objects.length; i++) {
6119
6155
  unlit.model_index = i;
@@ -6843,7 +6879,7 @@ Jax.Context = (function() {
6843
6879
  var error = this.glGetError();
6844
6880
  if (error != GL_NO_ERROR)
6845
6881
  {
6846
- var str = "GL error in "+this.canvas.id+": "+error;
6882
+ var str = "GL error in "+this.canvas.id+": "+error+" ("+Jax.Util.enumName(error)+")";
6847
6883
  error = new Error(str);
6848
6884
  var message = error;
6849
6885
  if (error.stack)
@@ -0,0 +1,33 @@
1
+ function setupJaxTestEnvironment() {
2
+ var jasmineEnv = jasmine.getEnv();
3
+
4
+ jsApiReporter = new jasmine.JsApiReporter();
5
+ var trivialReporter = new jasmine.TrivialReporter();
6
+
7
+ jasmineEnv.addReporter(jsApiReporter);
8
+ jasmineEnv.addReporter(trivialReporter);
9
+
10
+ jasmineEnv.specFilter = function(spec) {
11
+ return trivialReporter.specFilter(spec);
12
+ };
13
+
14
+ /*
15
+ Create a canvas element and add it to the document.
16
+ There's nothing special about this element.
17
+ */
18
+ var canvas = document.createElement("canvas");
19
+ canvas.setAttribute("id", "spec-canvas");
20
+ canvas.style.display = "none";
21
+ document.body.appendChild(canvas);
22
+
23
+ beforeEach(function() {
24
+ window.SPEC_CONTEXT = new Jax.Context(canvas);
25
+ });
26
+
27
+ afterEach(function() {
28
+ SPEC_CONTEXT.dispose();
29
+ })
30
+
31
+
32
+ jasmineEnv.execute();
33
+ }
@@ -14,20 +14,8 @@
14
14
  <script type="text/javascript">
15
15
  var jsApiReporter;
16
16
  (function() {
17
- var jasmineEnv = jasmine.getEnv();
18
-
19
- jsApiReporter = new jasmine.JsApiReporter();
20
- var trivialReporter = new jasmine.TrivialReporter();
21
-
22
- jasmineEnv.addReporter(jsApiReporter);
23
- jasmineEnv.addReporter(trivialReporter);
24
-
25
- jasmineEnv.specFilter = function(spec) {
26
- return trivialReporter.specFilter(spec);
27
- };
28
-
29
17
  window.onload = function() {
30
- jasmineEnv.execute();
18
+ setupJaxTestEnvironment();
31
19
 
32
20
  /*
33
21
  Create a canvas element and add it to the document.
@@ -1,7 +1,6 @@
1
1
  /*
2
- If you don't need the color attributes, use
3
- void main(void)
4
- to make your shader more efficient.
2
+ ambient, diffuse and specular represent the cumulative color to be eventually
3
+ written to +gl_FragColor+. You should output your colors into these vectors.
5
4
  */
6
5
  void main(inout vec4 ambient, inout vec4 diffuse, inout vec4 specular) {
7
6
 
@@ -1,20 +1,13 @@
1
1
  describe("Shader '<%=file_name%>'", function() {
2
- var context, material, mesh;
2
+ var material, mesh;
3
3
 
4
- beforeEach(function() {
5
- context = new Jax.Context('webgl-canvas');
6
- mesh = new Jax.Mesh.Quad();
7
- });
8
-
9
- /* dispose the context so it doesn't continue using
10
- resources after the tests have completed */
11
- afterEach(function() { context.dispose(); });
4
+ beforeEach(function() { mesh = new Jax.Mesh.Quad(); });
12
5
 
13
6
  describe("stand-alone", function() {
14
7
  beforeEach(function() { mesh.material = new Jax.Material.<%=class_name%>(); });
15
8
 
16
9
  it("should render without error", function() {
17
- expect(function() { mesh.render(context); }).not.toThrow();
10
+ expect(function() { mesh.render(SPEC_CONTEXT); }).not.toThrow();
18
11
  });
19
12
  });
20
13
 
@@ -26,7 +19,7 @@ describe("Shader '<%=file_name%>'", function() {
26
19
  });
27
20
 
28
21
  it("should render without error", function() {
29
- expect(function() { mesh.render(context); }).not.toThrow();
22
+ expect(function() { mesh.render(SPEC_CONTEXT); }).not.toThrow();
30
23
  });
31
24
  });
32
25
  });