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
data/src/jax/core/math.js CHANGED
@@ -1,5 +1,8 @@
1
- // If an epsilon isn't defined, define it. This is used for fuzzy equality with floats,
2
- // because of floating point imprecision.
1
+ /**
2
+ * Math.EPSILON = 0.00001
3
+ * This is a very small number, used for testing fuzzy equality with floats due to
4
+ * floating point imprecision.
5
+ **/
3
6
  Math.EPSILON = Math.EPSILON || 0.00001;
4
7
 
5
8
  /**
@@ -15,14 +18,26 @@ Math.radToDeg = Math.radToDeg || function(rad) {
15
18
  return rad * 180.0 / Math.PI;
16
19
  };
17
20
 
18
- /**
21
+ /** alias of: Math.radToDeg
19
22
  * Math.radToDeg(rad) -> Number
23
+ * Helper to convert radians to degrees.
24
+ **/
25
+ Math.rad2deg = Math.rad2deg || Math.radToDeg;
26
+
27
+ /**
28
+ * Math.degToRad(deg) -> Number
20
29
  * Helper to convert degrees to radians.
21
30
  **/
22
31
  Math.degToRad = Math.degToRad || function(deg) {
23
32
  return deg * Math.PI / 180.0;
24
33
  };
25
34
 
35
+ /** alias of: Math.degToRad
36
+ * Math.deg2rad(deg) -> Number
37
+ * Helper to convert degrees to radians.
38
+ **/
39
+ Math.deg2rad = Math.deg2rad || Math.degToRad;
40
+
26
41
  /**
27
42
  * Math.equalish(a, b) -> Boolean
28
43
  * Arguments can be either scalar or vector, but must be of the same type.
@@ -2,7 +2,8 @@
2
2
  //= require "../prototype/class"
3
3
 
4
4
  /**
5
- * Jax.IDENTITY_MATRIX
5
+ * Jax.IDENTITY_MATRIX = mat4.identity(mat4.create())
6
+ *
6
7
  * A cache of the identity matrix so that we're not constantly allocating identities.
7
8
  **/
8
9
  Jax.IDENTITY_MATRIX = mat4.identity(mat4.create());
@@ -18,7 +19,7 @@ Jax.IDENTITY_MATRIX = mat4.identity(mat4.create());
18
19
  * itself is returned instead of a copy of the instance. That gives you the power to make changes
19
20
  * directly to the matrix, instead of doing them via the stack. For instance, instead of calling
20
21
  * Jax.MatrixStack#loadMatrix(), you could just as easily call mat4#set() using one of the
21
- * matrices here as an argument. However, in doing so you will lose the auto-updating of other
22
+ * matrices here as an argument. *However*, in doing so you will lose the auto-updating of other
22
23
  * matrices, so you must be very careful about how you modify matrices.
23
24
  *
24
25
  * For example, it would be very easy to use mat4.multiply() to change the model matrix. In doing
@@ -307,4 +308,4 @@ Jax.MatrixStack = (function() {
307
308
  this.loadProjectionMatrix(Jax.IDENTITY_MATRIX); // there's no known data about the viewport at this time.
308
309
  }
309
310
  });
310
- })();
311
+ })();
data/src/jax/core/util.js CHANGED
@@ -3,6 +3,21 @@
3
3
  * Contains general-purpose utility and helper functions
4
4
  **/
5
5
  Jax.Util = {
6
+ /**
7
+ * Jax.Util.decodePickingColor(red, green, blue, alpha) -> Number
8
+ *
9
+ * Performs the reverse of the 'picking' shader by restoring a number
10
+ * that was previously encoded into the four color channels.
11
+ **/
12
+ decodePickingColor: function(red, green, blue, alpha) {
13
+ /* blue is a key. It is always max. So if it's 1, we're dealing with floats; else, bytes. */
14
+ if (blue == 1.0) {
15
+ red *= 255;
16
+ green *= 255;
17
+ }
18
+ return red * 256 + green;
19
+ },
20
+
6
21
  /**
7
22
  * Jax.Util.vectorize(object) -> vec3
8
23
  * - object (Object): any Object
data/src/jax/events.js CHANGED
@@ -1,9 +1,15 @@
1
+ /**
2
+ * Jax.EVENT_METHODS -> Object
3
+ * Contains event handling methods which are added to +Jax.Context+.
4
+ **/
1
5
  Jax.EVENT_METHODS = (function() {
2
6
  function getCumulativeOffset(element) {
3
7
  var valueT = 0, valueL = 0;
4
8
  do {
5
9
  valueT += element.offsetTop || 0;
6
10
  valueL += element.offsetLeft || 0;
11
+ valueT += Jax.Compatibility.offsetTop;
12
+ valueL += Jax.Compatibility.offsetLeft;
7
13
  element = element.offsetParent;
8
14
  } while (element);
9
15
 
@@ -28,11 +34,69 @@ Jax.EVENT_METHODS = (function() {
28
34
 
29
35
  return evt;
30
36
  }
37
+
38
+ function fixEvent(event) {
39
+ // this borrowed from jQuery
40
+ // store a copy of the original event object
41
+ // and "clone" to set read-only properties
42
+ var originalEvent = event;
43
+ event = {type:originalEvent.type};
44
+ var props = "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" ");
45
+
46
+ for ( var i = props.length, prop; i; ) {
47
+ prop = props[ --i ];
48
+ event[ prop ] = originalEvent[ prop ];
49
+ }
50
+
51
+ // Fix target property, if necessary
52
+ if ( !event.target ) {
53
+ // Fixes #1925 where srcElement might not be defined either
54
+ event.target = event.srcElement || document;
55
+ }
56
+
57
+ // check if target is a textnode (safari)
58
+ if ( event.target.nodeType === 3 ) {
59
+ event.target = event.target.parentNode;
60
+ }
61
+
62
+ // Add relatedTarget, if necessary
63
+ if ( !event.relatedTarget && event.fromElement ) {
64
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
65
+ }
66
+
67
+ // Calculate pageX/Y if missing and clientX/Y available
68
+ if ( event.pageX == null && event.clientX != null ) {
69
+ var eventDocument = event.target.ownerDocument || document,
70
+ doc = eventDocument.documentElement,
71
+ body = eventDocument.body;
72
+
73
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
74
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
75
+ }
76
+
77
+ // Add which for key events
78
+ if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
79
+ event.which = event.charCode != null ? event.charCode : event.keyCode;
80
+ }
81
+
82
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
83
+ if ( !event.metaKey && event.ctrlKey ) {
84
+ event.metaKey = event.ctrlKey;
85
+ }
86
+
87
+ // Add which for click: 1 === left; 2 === middle; 3 === right
88
+ // Note: button is not normalized, so don't use it
89
+ if ( !event.which && event.button !== undefined ) {
90
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
91
+ }
92
+
93
+ return event;
94
+ }
31
95
 
32
96
  function buildMouseEvent(self, evt) {
33
97
  var mouse = self.mouse;
34
98
 
35
- evt = evt || window.event || {};
99
+ evt = fixEvent(evt || window.event);
36
100
  evt.context = self;
37
101
  evt.canvas = self.canvas;
38
102
  evt.offsetx = mouse.x;
@@ -43,19 +107,10 @@ Jax.EVENT_METHODS = (function() {
43
107
  mouse.offsety = evt.offsety || 0;
44
108
 
45
109
  var cumulativeOffset = getCumulativeOffset(self.canvas);
46
- mouse.x = evt.clientX - cumulativeOffset[0];
47
- mouse.y = evt.clientY - cumulativeOffset[1];
110
+ mouse.x = evt.pageX - cumulativeOffset[0];
111
+ mouse.y = evt.pageY - cumulativeOffset[1];
48
112
  mouse.y = self.canvas.height - mouse.y; // invert y
49
113
 
50
- // add scroll offsets
51
- if (window.pageXOffset) {
52
- mouse.x += window.pageXOffset;
53
- mouse.y += window.pageYOffset;
54
- } else {
55
- mouse.x += document.body.scrollLeft;
56
- mouse.y += document.body.scrollTop;
57
- }
58
-
59
114
  // calculate differences, useful for checking movement relative to last position
60
115
  mouse.diffx = mouse.x - mouse.offsetx;
61
116
  mouse.diffy = mouse.y - mouse.offsety;
data/src/jax/geometry.js CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Jax.Geometry
3
+ * Namespace containing geometric classes and functions
4
+ **/
1
5
  Jax.Geometry = {};
2
6
 
3
- //= require "geometry/plane"
7
+ //= require "geometry/plane"
@@ -1,3 +1,15 @@
1
+ /**
2
+ * class Jax.Geometry.Plane
3
+ *
4
+ * Represents a plane in 3 dimensions.
5
+ *
6
+ * Examples:
7
+ *
8
+ * new Jax.Geometry.Plane([0,0,0], [0,1,0], [1,0,0]);
9
+ * new Jax.Geometry.Plane([[0,0,0], [0,1,0], [1,0,0]]);
10
+ * new Jax.Geometry.Plane();
11
+ *
12
+ **/
1
13
  Jax.Geometry.Plane = (function() {
2
14
  function innerProduct(a, x, y, z) {
3
15
  return (a[0]*x + a[1]*y + a[2]*z);
@@ -8,6 +20,18 @@ Jax.Geometry.Plane = (function() {
8
20
  if (points) this.set.apply(this, arguments);
9
21
  },
10
22
 
23
+ /**
24
+ * Jax.Geometry.Plane#set(points) -> Jax.Geometry.Plane
25
+ * - points (Array): an array of 3 vectors.
26
+ * Jax.Geometry.Plane#set(point0, point1, point2) -> Jax.Geometry.Plane
27
+ * - point0 (vec3): the first of 3 vectors.
28
+ * - point1 (vec3): the second of 3 vectors.
29
+ * - point2 (vec3): the third of 3 vectors.
30
+ *
31
+ * Sets this plane's coefficients based off of a set of three 3D points.
32
+ *
33
+ * This plane is returned.
34
+ **/
11
35
  set: function(points) {
12
36
  if (arguments.length == 3) points = [arguments[0], arguments[1], arguments[2]];
13
37
 
@@ -18,22 +42,35 @@ Jax.Geometry.Plane = (function() {
18
42
  vec3.cross(this.normal, vec, this.normal);
19
43
  vec3.normalize(this.normal);
20
44
 
21
- // vec3.subtract(points[1], points[0], this.normal);
22
- // vec3.cross(this.normal, vec3.subtract(points[2], points[0], vec3.create()));
23
- // vec3.normalize(this.normal);
24
- // this.normal = (points[1].minus(points[0])).cross(points[2].minus(points[0])).normalize();
25
45
  this.point = points[1];
26
46
  this.d = -innerProduct(this.normal, this.point[0], this.point[1], this.point[2]);
47
+
48
+ return this;
27
49
  },
28
50
 
51
+ /**
52
+ * Jax.Geometry.Plane#setCoefficients(a, b, c, d) -> Jax.Geometry.Plane
53
+ *
54
+ * Sets the four coefficients A, B, C, D for this plane.
55
+ *
56
+ * Returns this plane.
57
+ **/
29
58
  setCoefficients: function(a, b, c, d) {
30
59
  var len = Math.sqrt(a*a+b*b+c*c);
31
60
  this.normal[0] = a/len;
32
61
  this.normal[1] = b/len;
33
62
  this.normal[2] = c/len;
34
63
  this.d = d/len;
64
+ return this;
35
65
  },
36
66
 
67
+ /**
68
+ * Jax.Geometry.Plane#distance(point) -> Number
69
+ * - point (vec3): A 3D vector. Can be any type with values for indices [0..2] (e.g. an Array).
70
+ *
71
+ * Given a 3D point, returns the distance from this plane to the point. The point is expected
72
+ * to lie in the same 3D space as this plane.
73
+ **/
37
74
  distance: function(point)
38
75
  {
39
76
  var x, y, z;
@@ -43,9 +80,26 @@ Jax.Geometry.Plane = (function() {
43
80
  return this.d + innerProduct(this.normal, x, y, z);
44
81
  },
45
82
 
83
+ /**
84
+ * Jax.Geometry.Plane#whereis(point) -> Number
85
+ * - point (vec3): A 3D vector. Can be any type with values for indices [0..2] (e.g. an Array).
86
+ *
87
+ * Given a point in 3D space, returns one of the following values based on the position
88
+ * of the point relative to this plane:
89
+ *
90
+ * Jax.Geometry.Plane.FRONT
91
+ * Jax.Geometry.Plane.BACK
92
+ * Jax.Geometry.Plane.INTERSECT
93
+ *
94
+ * FRONT represents a point lying somewhere in the direction of the plane's normal.
95
+ * BACK represents the opposite, and INTERSECT represents a point lying directly
96
+ * parallel to this plane.
97
+ *
98
+ * The point is expected to lie in the same 3D space as this plane.
99
+ **/
46
100
  whereis: function(point)
47
101
  {
48
- if (arguments.length == 3) points = [arguments[0], arguments[1], arguments[2]];
102
+ if (arguments.length == 3) point = [arguments[0], arguments[1], arguments[2]];
49
103
  var d = this.distance(point);
50
104
  if (d > 0) return Jax.Geometry.Plane.FRONT;
51
105
  if (d < 0) return Jax.Geometry.Plane.BACK;
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * class Jax.Controller
3
+ *
3
4
  * Controllers are a major component of the Jax framework, because they are in
4
5
  * charge of receiving input from the user, setting up a scene, tearing it down,
5
6
  * and deciding when is the right time to transition to a different controller.
@@ -48,6 +49,7 @@
48
49
  * explicitly trigger other actions by calling them directly. They differ from their
49
50
  * corresponding views (see Jax.View) in this way, as a view is rendered many times
50
51
  * -- up to a target rate of 60 passes per second.
52
+ *
51
53
  **/
52
54
  (function() {
53
55
  var protected_instance_method_names = [
@@ -69,6 +71,15 @@
69
71
  }
70
72
 
71
73
  return Jax.Class.create({
74
+ /**
75
+ * Jax.Controller#fireAction(action_name) -> Jax.Controller
76
+ *
77
+ * Erases the results of the last action, then calls the specified action. If it doesn't exist,
78
+ * an error is raised. Finally, unless the action redirects to a different action or renders
79
+ * a different action directly, the specified action becomes the focus of the current view.
80
+ *
81
+ * Returns this controller.
82
+ **/
72
83
  fireAction: function(action_name) {
73
84
  this.eraseResult();
74
85
  this.action_name = action_name;
@@ -79,16 +90,38 @@
79
90
 
80
91
  if (!this.rendered_or_redirected)
81
92
  setViewKey(this);
93
+ return this;
82
94
  },
83
95
 
96
+ /**
97
+ * Jax.Controller#eraseResults() -> Jax.Controller
98
+ *
99
+ * Erases the results of the most recent render action. That is, whether or not it rendered
100
+ * a different action or caused a redirect to a different action or controller is reset, and
101
+ * the current view is set to +null+, indicating that no view will be rendered.
102
+ *
103
+ * Returns this controller.
104
+ **/
84
105
  eraseResult: function() {
85
106
  this.rendered_or_redirected = false;
86
107
  this.view_key = null;
108
+ return this;
87
109
  }
88
110
  });
89
111
  })();
90
112
 
91
113
  var controller_class_methods = {
114
+ /**
115
+ * Jax.Controller.invoke(action_name, context) -> Jax.Controller
116
+ * - action_name (String): The name of the action to fire after initialization
117
+ * - context (Jax.Context): The context to attach to the instantiated controller
118
+ *
119
+ * Creates a new instance of the specified controller, sets up its references to
120
+ * +this.context+, +this.world+, and so on; and finally, fires the action given by
121
+ * +action_name+.
122
+ *
123
+ * Returns the newly-constructed controller.
124
+ **/
92
125
  invoke: function(action_name, context) {
93
126
  var instance = new this();
94
127
  instance.context = context;
@@ -138,6 +171,11 @@
138
171
  if (inner) klass = Jax.Class.create(superclass, inner);
139
172
  else klass = Jax.Class.create(Jax.Controller, superclass);
140
173
 
174
+ /**
175
+ * Jax.Controller.getControllerName() -> String
176
+ *
177
+ * Returns the name of the controller in question, as it is represented in +Jax.routes+.
178
+ **/
141
179
  Object.extend(klass, controller_class_methods);
142
180
  Object.extend(klass, { getControllerName: function() { return controller_name; } });
143
181
  klass.addMethods({getControllerName: function() { return controller_name; } });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * class Jax.Helper
3
+ *
4
+ * Jax Helpers are glorified mixins. They contain a set of methods which can then be
5
+ * included into any Jax class.
6
+ *
7
+ * Defining a helper is easy, and looks a bit like this:
8
+ *
9
+ * var HelloHelper = Jax.Helper.create({
10
+ * sayHi: function(name) { alert("Hello, "+name+"!"); }
11
+ * });
12
+ *
13
+ * Once defined, using helpers is particularly simple. In any Jax class, simply define
14
+ * a +helpers+ function that returns an array of helpers:
15
+ *
16
+ * var Liaison = Jax.Class.create({
17
+ * helpers: function() { return [HelloHelper]; }
18
+ * });
19
+ *
20
+ * Now the +Liaison+ class will include the +sayHi+ method which can be called just like
21
+ * any other method:
22
+ *
23
+ * var l = new Liaison();
24
+ * l.sayHi("World");
25
+ * //=> Hello, World!
26
+ *
27
+ **/
28
+ Jax.Helper = {
29
+ instances: [],
30
+
31
+ create: function(methods) {
32
+ Jax.Helper.instances.push(methods);
33
+ return methods;
34
+ }
35
+ };
@@ -0,0 +1,301 @@
1
+ /**
2
+ * class Jax.Model
3
+ *
4
+ * Models encapsulate virtually all business logic. If a Monster knows how to attack, the logic
5
+ * that makes it do so is held within the +Monster+ model. If the terrain has trees and other
6
+ * vegetation, the +Terrain+ model is responsible for setting that up.
7
+ *
8
+ * While controllers generally have only high-level code such as initial scene set-up and
9
+ * processing of user input, models handle nearly everything else.
10
+ *
11
+ * Models also contain the actual game data. Jax further splits models into two components: the
12
+ * Model itself and its Resources.
13
+ *
14
+ * Define a model like so:
15
+ *
16
+ * var Monster = Jax.Model.create({
17
+ * after_initialize: function() {
18
+ * // code to be run automatically after a monster is instantiated
19
+ * },
20
+ *
21
+ * update: function(time) {
22
+ * // code to be run automatically every few milliseconds
23
+ * },
24
+ *
25
+ * dealDamageTo: function(otherModel) {
26
+ * // custom method, called only when you invoke it
27
+ * otherModel.takeDamage(this.attack_damage);
28
+ * }
29
+ * });
30
+ *
31
+ * Once defined, you can add data easily:
32
+ *
33
+ * Monster.addResources({"ogre": {
34
+ * hit_points: 100,
35
+ * attack_damage: 75
36
+ * }});
37
+ *
38
+ * You can instantiate a model whose resources already exist like so:
39
+ *
40
+ * var ogre = Monster.find("ogre");
41
+ *
42
+ * Note that subsequent calls to +Model.find+ will return unique objects. For instance, the
43
+ * following code will add 3 separate "ogres" to the world:
44
+ *
45
+ * world.addObject(Monster.find("ogre"));
46
+ * world.addObject(Monster.find("ogre"));
47
+ * world.addObject(Monster.find("ogre"));
48
+ *
49
+ **/
50
+ (function() {
51
+ function initProperties(self, data) {
52
+ // to make sure sub-properties of data are standalone objects, so that the original data can't be tainted
53
+ data = Jax.Util.normalizeOptions(data, {});
54
+
55
+ var attribute;
56
+
57
+ if (data) {
58
+ for (attribute in data) {
59
+ switch(attribute) {
60
+ case 'position': self.camera.setPosition(Jax.Util.vectorize(data[attribute])); break;
61
+ case 'direction': self.camera.orient(Jax.Util.vectorize(data[attribute])); break;
62
+ case 'mesh':
63
+ if (data[attribute].isKindOf(Jax.Mesh)) self.mesh = data[attribute];
64
+ else throw new Error("Unexpected value for mesh:\n\n"+JSON.stringify(data[attribute]));
65
+ break;
66
+ default:
67
+ self[attribute] = data[attribute];
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ Jax.Model = (function() {
74
+ return Jax.Class.create({
75
+ /**
76
+ * new Jax.Model(data)
77
+ * - data: a set of attributes to be assigned to this instance of the model.
78
+ *
79
+ * Anything can be in the data, or you may supply no data at all to instantiate
80
+ * a model with its default attributes.
81
+ *
82
+ * The following attributes have special meanings:
83
+ *
84
+ * * +position+ : sets the position of this model in world coordinates.
85
+ * * +direction+ : sets the direction this model is facing, in world coordinates.
86
+ * * +mesh+: an instance of +Jax.Mesh+
87
+ * * +shadow_caster+ : true or false; specifies whether this model can cast shadows.
88
+ * * +lit+ : true or false; specifies whether this model is affected by nearby lights.
89
+ *
90
+ **/
91
+ initialize: function(data) {
92
+ this.camera = new Jax.Camera();
93
+
94
+ initProperties(this, Jax.Model.default_properties);
95
+ if (this._klass && this._klass.resources)
96
+ initProperties(this, this._klass.resources['default']);
97
+ initProperties(this, data);
98
+
99
+ if (this.after_initialize) this.after_initialize();
100
+ },
101
+
102
+ /**
103
+ * Jax.Model#isShadowCaster() -> Boolean
104
+ *
105
+ * Returns true if this model casts shadows upon other models in the scene. Note that
106
+ * even if true, shadows will only be cast upon models which utilize +Jax.Material+s that support
107
+ * both the +Lighting+ and +ShadowMap+ effects.
108
+ *
109
+ **/
110
+ isShadowCaster: function() { return this.shadow_caster; },
111
+
112
+ /**
113
+ * Jax.Model#render(context) -> undefined
114
+ *
115
+ * Renders this model with the given context. If the model doesn't have a mesh,
116
+ * nothing is rendered.
117
+ **/
118
+ render: function(context, options) {
119
+ if (this.mesh)
120
+ {
121
+ var self = this;
122
+ context.pushMatrix(function() {
123
+ context.multModelMatrix(self.camera.getTransformationMatrix());
124
+ self.mesh.render(context, options);
125
+ });
126
+ }
127
+ },
128
+
129
+ /**
130
+ * Jax.Model#getBoundingCube() -> Object
131
+ *
132
+ * Returns an object describing the cubic dimensions of this model.
133
+ *
134
+ * Example:
135
+ *
136
+ * var bounds = new Jax.Model({mesh:new Jax.Mesh.Cube()}).getBoundingCube();
137
+ * // 'bounds' contains the following:
138
+ * {
139
+ * left: -0.5,
140
+ * right: 0.5,
141
+ * bottom: -0.5,
142
+ * top: 0.5,
143
+ * front: 0.5,
144
+ * back: -0.5,
145
+ * width: 1.0,
146
+ * height: 1.0,
147
+ * depth: 1.0
148
+ * }
149
+ *
150
+ **/
151
+ getBoundingCube: function() {
152
+ if (!this.mesh.built) this.mesh.rebuild();
153
+ return this.mesh.bounds;
154
+ },
155
+
156
+ /**
157
+ * Jax.Model#getBoundingSphereRadius() -> Number
158
+ *
159
+ * A sphere can be defined with two values: a position and a radius. A model's
160
+ * position is always known via its camera (see +Jax.Model#camera+).
161
+ *
162
+ * This method returns the radius of its bounding sphere. A bounding sphere is
163
+ * guaranteed to contain the furthest-away point from the model's position. It is less
164
+ * accurate than +Jax.Model#getBoundingCube+, but using it is much faster.
165
+ *
166
+ **/
167
+ getBoundingSphereRadius: function() {
168
+ var b = this.getBoundingCube();
169
+ return Math.max(b.width, Math.max(b.height, b.depth));
170
+ },
171
+
172
+ /**
173
+ * Jax.Model#dispose() -> undefined
174
+ *
175
+ * Disposes of this model and its mesh.
176
+ *
177
+ * Note that both models and meshes _can_ be reused after disposal; they'll just
178
+ * be silently re-initialized. This means it is safe to dispose of models while
179
+ * they are still being used (although this is slow and not recommended if at all
180
+ * avoidable).
181
+ **/
182
+ dispose: function() {
183
+ if (this.mesh)
184
+ this.mesh.dispose();
185
+ },
186
+
187
+ /**
188
+ * Jax.Model#isLit() -> Boolean
189
+ *
190
+ * Returns true if this model can be lit by other light sources. Note that even
191
+ * if this is true, the +Jax.Material+ used by its +Jax.Mesh+ must support the
192
+ * +Lighting+ effect in order to actually perform the lighting effect.
193
+ *
194
+ **/
195
+ isLit: function() {
196
+ return this.lit;
197
+ },
198
+
199
+ /**
200
+ * Jax.Model#inspect() -> String
201
+ *
202
+ * Returns the JSON representation of the attributes in this model.
203
+ * Unlike JSON.stringify(), this method will omit function definitions so
204
+ * that only actual data elements are returned in the resulting JSON string.
205
+ *
206
+ **/
207
+ inspect: function() {
208
+ result = {};
209
+ for (var i in this)
210
+ if (!Object.isFunction(this[i]) && i != "_klass")
211
+ result[i] = this[i];
212
+ return JSON.stringify(result);
213
+ }
214
+ });
215
+ })();
216
+
217
+ var model_class_methods = {
218
+ /**
219
+ * Jax.Model.find(id) -> Jax.Model
220
+ * - id (String): the unique identifier of the model to be found
221
+ *
222
+ * Finds the resource with the specified name, instantiates its model and returns it.
223
+ *
224
+ * Note that this is a class method of the model in question, and not of Jax.Model itself.
225
+ *
226
+ * For example, this would be correct:
227
+ *
228
+ * Character.find('bad_guy')
229
+ *
230
+ * while this would be *incorrect*:
231
+ *
232
+ * Jax.Model.find('bad_guy')
233
+ *
234
+ **/
235
+ find: function(id) {
236
+ for (var resource_id in this.resources) {
237
+ if (id == resource_id)
238
+ return new this(this.resources[id]);
239
+ }
240
+ throw new Error("Resource '"+id+"' not found!");
241
+ },
242
+
243
+ /**
244
+ * Jax.Model.addResources(resources) -> undefined
245
+ * - resources (Object): the resources to be added
246
+ *
247
+ * Adds the resources to the specified model. These resources can then be found using
248
+ * Jax.Model.find(id).
249
+ *
250
+ * Note that this is a class method of the model in question, and not of Jax.Model itself.
251
+ *
252
+ * For example, this would be correct:
253
+ *
254
+ * Character.addResources({'bad_guy': {...}})
255
+ *
256
+ * while this would be *incorrect*:
257
+ *
258
+ * Jax.Model.addResources({'bad_guy': {...}})
259
+ *
260
+ **/
261
+ addResources: function(resources) {
262
+ this.resources = this.resources || {};
263
+ for (var id in resources)
264
+ if (this.resources[id]) throw new Error("Duplicate resource ID: "+id);
265
+ else this.resources[id] = resources[id];
266
+ }
267
+ };
268
+
269
+ Jax.Model.default_properties = {
270
+ lit: true,
271
+ shadow_caster: true
272
+ };
273
+
274
+ /**
275
+ * Jax.Model.create(inner) -> klass<Jax.Model>
276
+ * - inner (Object) - a set of methods the class will contain.
277
+ * Jax.Model.create(superclass, inner) -> klass<Jax.Model>
278
+ * - superclass (Jax.Model) - an optional superclass. Defaults to +Jax.Model+.
279
+ * - inner (Object) - a set of methods the class will contain.
280
+ *
281
+ * Creates a new Jax class inheriting from Jax.Model. If a superclass is given,
282
+ * the model will inherit from the given superclass instead. The superclass is,
283
+ * in turn, expected to be a subclass of Jax.Model.
284
+ *
285
+ * Examples:
286
+ *
287
+ * var Person = Jax.Class.create({ ... });
288
+ * var Colin = Jax.Class.create(Person, { ... });
289
+ *
290
+ **/
291
+ Jax.Model.create = function(superclass, inner) {
292
+ var klass;
293
+ if (inner) klass = Jax.Class.create(superclass, inner);
294
+ else klass = Jax.Class.create(Jax.Model, superclass);
295
+
296
+ klass.addMethods({_klass:klass});
297
+
298
+ Object.extend(klass, model_class_methods);
299
+ return klass;
300
+ };
301
+ })();