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.
- data/CHANGELOG +26 -1
- data/Rakefile +3 -1
- data/builtin/shaders/picking/common.ejs +2 -0
- data/builtin/shaders/picking/fragment.ejs +4 -0
- data/builtin/shaders/picking/material.js +14 -0
- data/builtin/shaders/picking/vertex.ejs +24 -0
- data/lib/jax/generators/app/templates/public/javascripts/jax.js +289 -681
- data/lib/jax/generators/app/templates/spec/javascripts/support/spec_layout.html.erb +55 -2
- data/lib/jax/generators/shader/templates/common.ejs.tt +2 -2
- data/lib/jax/packager/sprockets_template.rb +1 -4
- data/lib/jax/routes.rb +3 -0
- data/lib/jax/version.rb +1 -1
- data/spec/example_app/app/controllers/noise_controller.js +37 -5
- data/spec/example_app/app/controllers/picking_controller.js +32 -0
- data/spec/example_app/app/helpers/picking_helper.js +3 -0
- data/spec/example_app/app/models/blob.js +38 -0
- data/spec/example_app/app/resources/blobs/default.yml +2 -0
- data/spec/example_app/app/resources/materials/blob.yml +2 -2
- data/spec/example_app/app/shaders/blob/common.ejs +8 -13
- data/spec/example_app/app/shaders/blob/fragment.ejs +1 -1
- data/spec/example_app/app/shaders/blob/material.js +15 -12
- data/spec/example_app/app/shaders/blob/vertex.ejs +33 -8
- data/spec/example_app/app/views/picking/index.js +4 -0
- data/spec/example_app/config/routes.rb +1 -0
- data/spec/example_app/spec/javascripts/controllers/picking_controller_spec.js +11 -0
- data/spec/example_app/spec/javascripts/helpers/picking_helper_spec.js +12 -0
- data/spec/example_app/spec/javascripts/models/blob_spec.js +11 -0
- data/spec/example_app/spec/javascripts/support/spec_layout.html.erb +40 -2
- data/spec/javascripts/jax/model_spec.js +10 -0
- data/spec/javascripts/jax/world_spec.js +74 -1
- data/spec/javascripts/shaders/preprocessor_spec.js +35 -0
- data/src/constants.yml +1 -1
- data/src/jax.js +30 -8
- data/src/jax/anim_frame.js +6 -2
- data/src/jax/builtin/meshes/cube.js +22 -1
- data/src/jax/builtin/meshes/plane.js +27 -0
- data/src/jax/builtin/meshes/quad.js +7 -1
- data/src/jax/builtin/meshes/sphere.js +20 -1
- data/src/jax/builtin/meshes/teapot.js +10 -0
- data/src/jax/builtin/meshes/torus.js +18 -0
- data/src/jax/compatibility.js +165 -2
- data/src/jax/context.js +176 -9
- data/src/jax/core.js +6 -3
- data/src/jax/core/math.js +18 -3
- data/src/jax/core/matrix_stack.js +4 -3
- data/src/jax/core/util.js +15 -0
- data/src/jax/events.js +67 -12
- data/src/jax/geometry.js +5 -1
- data/src/jax/geometry/plane.js +59 -5
- data/src/jax/{controller.js → mvc/controller.js} +38 -0
- data/src/jax/mvc/helper.js +35 -0
- data/src/jax/mvc/model.js +301 -0
- data/src/jax/{route_set.js → mvc/route_set.js} +0 -0
- data/src/jax/{view.js → mvc/view.js} +6 -0
- data/src/jax/{view_manager.js → mvc/view_manager.js} +1 -0
- data/src/jax/noise.js +13 -0
- data/src/jax/prototype/class.js +3 -0
- data/src/jax/prototype/extensions.js +26 -8
- data/src/jax/webgl/camera.js +6 -6
- data/src/jax/webgl/core/framebuffer.js +4 -4
- data/src/jax/webgl/material.js +16 -0
- data/src/jax/webgl/mesh.js +19 -4
- data/src/jax/webgl/scene/light_manager.js +8 -0
- data/src/jax/webgl/scene/light_source.js +3 -3
- data/src/jax/webgl/shader.js +4 -2
- data/src/jax/webgl/shader_chain.js +1 -0
- data/src/jax/webgl/world.js +157 -6
- data/vendor/glmatrix/glMatrix.js +365 -408
- metadata +32 -10
- data/src/jax/helper.js +0 -8
- data/src/jax/model.js +0 -163
data/src/jax/core/math.js
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
2
|
-
|
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
|
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.
|
47
|
-
mouse.y = evt.
|
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
data/src/jax/geometry/plane.js
CHANGED
@@ -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)
|
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
|
+
})();
|