diff --git a/src/webglu.js b/src/webglu.js
index c5b1766..bb251de 100644
--- a/src/webglu.js
+++ b/src/webglu.js
@@ -48,6 +48,12 @@ $W = {
/** The raw WebGL object for low level work. */
GL : null,
+ /** Simulated world */
+ world: {
+ /** Physical objects */
+ objects: []
+ },
+
/** Renderable objects */
objects : [],
@@ -905,6 +911,9 @@ $W = {
/** @namespace Utility functions. */
util:{
+ integrateEuler:function(fun, t, dt) {
+ return fun(t + dt);
+ },
/** XXX Not yet implemented. */
getPickRay : function(x, y) {
@@ -1431,7 +1440,31 @@ $W = {
}
}
}
+ },
+
+ /** @class A physical simulation.
+ */
+ Simulation:function() {
+ $W.anim.ProceduralAnimation.call(this); // subclass of ProceduralAnimation
+
+ /** Bounding sphere radius */
+ this.radius = 1;
+ /** Object mass */
+ this.mass = 1;
+ /** Object velocity */
+ this.velocity = Vector.Zero(3);
+ /** Angular velocity */
+ this.omega = Vector.Zero(3);
+
+ this.update = function(dt) {
+ // calculate forces
+ // integrate position/rotation
+ // update momentum
+ // calculate velocities
+ }
+
}
+
},
@@ -1504,8 +1537,84 @@ $W = {
},
// Classes
+ /** @class Quaternion implementation.
+ * based on reference implementation at
+ * http://3dengine.org/Quaternions
+ */
+ Quaternion:function() {
+ // Called as Quaternion(x, y, z, theta)
+ if (arguments.length == 4) {
+ var angle = (arguments[3] / 180.0) * Math.PI;
+ var result = Math.sin(angle / 2);
+ this.w = Math.cos(angle / 2);
+ this.x = arguments[0] * result;
+ this.y = arguments[1] * result;
+ this.z = arguments[2] * result;
+
+ // Called as Quaternion([w, x, y, z])
+ }else if (arguments[0] != undefined && arguments[0].length == 4) {
+ this.w = arguments[0][0];
+ this.x = arguments[0][1];
+ this.y = arguments[0][2];
+ this.z = arguments[0][3];
+
+ // Called as Quaternion()
+ }else {
+ this.w = 1;
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ this.matrix = function() {
+ var w = this.w;
+ var x = this.x;
+ var y = this.y;
+ var z = this.z;
+ var xx = x * x;
+ var yy = y * y;
+ var zz = z * z;
+ var xy = x * y;
+ var xz = x * z;
+ var xw = x * w;
+ var yz = y * z;
+ var yw = y * w;
+ var zw = z * w;
+
+ var m = [[],[],[],[]];
+
+ m[0][0] = 1 - 2 * (yy + zz);
+ m[0][1] = 2 * (xy + zw);
+ m[0][2] = 2 * (xz - yw);
+ m[0][3] = 0;
+ m[1][0] = 2 * (xy - zw);
+ m[1][1] = 1 - 2 * (xx + zz);
+ m[1][2] = 2 * (yz + xw);
+ m[1][3] = 0;
+ m[2][0] = 2 * (xz + yw);
+ m[2][1] = 2 * (yz - xw);
+ m[2][2] = 1 - 2 * (xx + yy);
+ m[2][3] = 0;
+ m[3][0] = 0;
+ m[3][1] = 0;
+ m[3][2] = 0;
+ m[3][3] = 1;
+
+ return $M(m);
+ }
+
+ this.multiply = function(q) {
+ var result = new $W.Quaternion();
+ result.w = this.w * q.w - this.x * q.x - this.y * q.y - this.z * q.z;
+ result.x = this.w * q.x + this.x * q.w + this.y * q.z - this.z * q.y;
+ result.y = this.w * q.y + this.y * q.w + this.z * q.x - this.x * q.z;
+ result.z = this.w * q.z + this.z * q.w + this.x * q.y - this.y * q.x;
+ return result;
+ }
+ },
/** @class Quaternion implementation XXX broken */
+ /*
Quaternion:function(m) {
this.vec = Vector.Zero(4);
@@ -1535,6 +1644,7 @@ $W = {
return result;
}
},
+ */
/** @class Common state representation (position, rotation, scale).
* A position, rotation, and scale.
diff --git a/tests/test.html b/tests/test.html
index d50fea3..5fa1253 100644
--- a/tests/test.html
+++ b/tests/test.html
@@ -12,7 +12,7 @@
-
+
@@ -73,6 +73,13 @@
WebGLUTestSuite.add(
new YT.TestCase({
name:'Shader Tests',
+ setUp : function() {
+ $W.initialize();
+ },
+
+ tearDown : function() {
+ $W.reset();
+ },
test_newProgram : function() {
@@ -84,6 +91,13 @@
WebGLUTestSuite.add(
new YT.TestCase({
name:'Object Tests',
+ setUp : function() {
+ $W.initialize();
+ },
+
+ tearDown : function() {
+ $W.reset();
+ },
test_newObject : function() {
@@ -94,6 +108,94 @@
})
);
+ WebGLUTestSuite.add(
+ new YT.TestCase({
+ name:'Math Tests',
+ setUp : function() {
+ $W.initialize();
+ },
+
+ tearDown : function() {
+ $W.reset();
+ },
+
+ test_newQuaternion : function() {
+ var q;
+
+ var error1 = "Quaternion(x, y, z, theta) failed";
+ var x1 = 1;
+ var y1 = 0;
+ var z1 = 0;
+ var theta = 90;
+ var expected_q1x = 0.7071;
+ var expected_q1y = 0;
+ var expected_q1z = 0;
+ var expected_q1w = 0.7071;
+
+ var error2 = "Quaternion([w, x, y, z]) failed";
+ var w2 = 0.1;
+ var x2 = 0.3;
+ var y2 = 0.3;
+ var z2 = 0.5;
+
+ var error3 = "Quaternion() failed";
+
+ // Test Quaternion(x, y, z, theta)
+ q = new $W.Quaternion(x1, y1, z1, theta);
+ assert.areEqual(expected_q1x, q.x.toPrecision(4), error1);
+ assert.areEqual(expected_q1y, q.y.toPrecision(4), error1);
+ assert.areEqual(expected_q1z, q.z.toPrecision(4), error1);
+ assert.areEqual(expected_q1w, q.w.toPrecision(4), error1);
+
+ // Test Quaternion([w, x, y, z])
+ q = new $W.Quaternion([w2, x2, y2, z2]);
+ assert.areEqual(w2, q.w, error2);
+ assert.areEqual(x2, q.x, error2);
+ assert.areEqual(y2, q.y, error2);
+ assert.areEqual(z2, q.z, error2);
+
+ // Test Quaternion()
+ q = new $W.Quaternion();
+ assert.areEqual(1, q.w, error3);
+ assert.areEqual(0, q.x, error3);
+ assert.areEqual(0, q.y, error3);
+ assert.areEqual(0, q.z, error3);
+ },
+
+ test_multQuaternions : function() {
+ var roll = 30;
+ var pitch = 45;
+ var heading = 30;
+ var expected_glMatrix =
+ [0.57322332027622536, 0.73919890801437749,
+ -0.3535533898605015, 0.0, -0.3535533898605015,
+ 0.61237244705783134, 0.70710677171312086, 0.0,
+ 0.73919890801437749, -0.28033009198934633,
+ 0.61237244705783134, 0.0, 0, 0, 0, 1.0]
+
+ var q1 = new $W.Quaternion(0, 0, 1, roll);
+ var q2 = new $W.Quaternion(1, 0, 0, pitch);
+ var q3 = new $W.Quaternion(0, 1, 0, heading);
+
+ var qr = (q1.multiply(q2)).multiply(q3);
+
+ var result_glMatrix = qr.matrix().elements.flatten();
+
+ console.log(expected_glMatrix);
+ console.log(result_glMatrix);
+
+ for (var i = 0; i < expected_glMatrix.length; i++) {
+ // 7 digit precision because reference values were
+ // calculated with Pi accurate to just 7 digits.
+ assert.areEqual(
+ expected_glMatrix[i].toPrecision(7),
+ result_glMatrix[i].toPrecision(7),
+ "Quaternion multiplation failure");
+ }
+ }
+ })
+ );
+
logger = new YAHOO.tool.TestLogger();
with(YAHOO.tool.TestRunner) {
//subscribe(TEST_FAIL_EVENT, handleTestResult);