jax 0.0.0.8 → 0.0.0.9

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