Testing out a 3D Camera

This commit is contained in:
Richard Davey 2017-09-15 04:04:51 +01:00
parent fb81fa514b
commit 83d985b4b2
17 changed files with 2809 additions and 12 deletions

172
v3/src/camera3d/Camera3D.js Normal file
View file

@ -0,0 +1,172 @@
var Class = require('../utils/Class');
var util = require('./vecutil');
var Vector2 = require('./vecmath/Vector2');
var Vector3 = require('./vecmath/Vector3');
var Vector4 = require('./vecmath/Vector4');
var Matrix4 = require('./vecmath/Matrix4');
var tmpVec3 = new Vector3();
var tmpVec4 = new Vector4();
/**
* Abstract base class for cameras to implement.
* @class Camera
* @abstract
*/
var Camera3D = new Class({
initialize: function() {
this.direction = new Vector3(0, 0, -1);
this.up = new Vector3(0, 1, 0);
this.position = new Vector3();
this.projection = new Matrix4();
this.view = new Matrix4();
this.combined = new Matrix4();
this.invProjectionView = new Matrix4();
this.near = 1;
this.far = 100;
this.ray = {
origin: new Vector3(),
direction: new Vector3()
};
this.viewportWidth = 0;
this.viewportHeight = 0;
},
/**
* Sets the width and height of the viewport. Does not
* update any matrices.
*
* @method setViewport
* @param {Number} width the viewport width
* @param {Number} height the viewport height
*/
setViewport: function(width, height) {
this.viewportWidth = width;
this.viewportHeight = height;
},
/**
* Translates this camera by a specified Vector3 object
* or x, y, z parameters. Any undefined x y z values will
* default to zero, leaving that component unaffected.
*
* @param {[type]} vec [description]
* @return {[type]} [description]
*/
translate: function(x, y, z) {
if (typeof x === "object") {
this.position.x += x.x || 0;
this.position.y += x.y || 0;
this.position.z += x.z || 0;
} else {
this.position.x += x || 0;
this.position.y += y || 0;
this.position.z += z || 0;
}
},
lookAt: function(x, y, z) {
var dir = this.direction,
up = this.up;
if (typeof x === "object") {
dir.copy(x);
} else {
dir.set(x, y, z);
}
dir.sub(this.position).normalize();
//calculate right vector
tmpVec3.copy(dir).cross(up).normalize();
//calculate up vector
up.copy(tmpVec3).cross(dir).normalize();
},
rotate: function(radians, axis) {
util.rotate(this.direction, axis, radians);
util.rotate(this.up, axis, radians);
},
rotateAround: function(point, radians, axis) {
tmpVec.copy(point).sub(this.position);
this.translate(tmpVec);
this.rotate(radians, axis);
this.translate(tmpVec.negate());
},
project: function(vec, out) {
if (!out)
out = new Vector4();
//TODO: support viewport XY
var viewportWidth = this.viewportWidth,
viewportHeight = this.viewportHeight,
n = Camera3D.NEAR_RANGE,
f = Camera3D.FAR_RANGE;
// for useful Z and W values we should do the usual steps...
// clip space -> NDC -> window coords
//implicit 1.0 for w component
tmpVec4.set(vec.x, vec.y, vec.z, 1.0);
//transform into clip space
tmpVec4.transformMat4(this.combined);
//now into NDC
tmpVec4.x = tmpVec4.x / tmpVec4.w;
tmpVec4.y = tmpVec4.y / tmpVec4.w;
tmpVec4.z = tmpVec4.z / tmpVec4.w;
//and finally into window coordinates
out.x = viewportWidth / 2 * tmpVec4.x + (0 + viewportWidth / 2);
out.y = viewportHeight / 2 * tmpVec4.y + (0 + viewportHeight / 2);
out.z = (f - n) / 2 * tmpVec4.z + (f + n) / 2;
//if the out vector has a fourth component, we also store (1/clip.w)
//same idea as gl_FragCoord.w
if (out.w === 0 || out.w)
out.w = 1 / tmpVec4.w;
return out;
},
unproject: function(vec, out) {
if (!out)
out = new Vector3();
var viewport = tmpVec4.set(0, 0, this.viewportWidth, this.viewportHeight);
return out.copy(vec).unproject(viewport, this.invProjectionView);
},
getPickRay: function(x, y) {
var origin = this.ray.origin.set(x, y, 0),
direction = this.ray.direction.set(x, y, 1),
viewport = tmpVec4.set(0, 0, this.viewportWidth, this.viewportHeight),
mtx = this.invProjectionView;
origin.unproject(viewport, mtx);
direction.unproject(viewport, mtx);
direction.sub(origin).normalize();
return this.ray;
},
update: function() {
//left empty for subclasses
}
});
Camera3D.FAR_RANGE = 1.0;
Camera3D.NEAR_RANGE = 0.0;
module.exports = Camera3D;

View file

@ -0,0 +1,82 @@
var Class = require('../utils/Class');
var Vector3 = require('./vecmath/Vector3');
var Vector4 = require('./vecmath/Vector4');
var Matrix4 = require('./vecmath/Matrix4');
var Camera = require('./Camera3D');
var tmpVec3 = new Vector3();
var OrthographicCamera = new Class({
Extends: Camera,
zoom: {
set: function(v) {
if (typeof v !== 'number')
throw new Error("zoom must be a number");
this._zoom = v;
},
get: function() {
return this._zoom;
}
},
initialize: function(viewportWidth, viewportHeight) {
Camera.call(this);
this.viewportWidth = viewportWidth||0;
this.viewportHeight = viewportHeight||0;
this._zoom = 1.0;
this.near = 0;
this.update();
},
setToOrtho: function(yDown, viewportWidth, viewportHeight) {
var zoom = this.zoom;
viewportWidth = typeof viewportWidth === "number" ? viewportWidth : this.viewportWidth;
viewportHeight = typeof viewportHeight === "number" ? viewportHeight : this.viewportHeight;
this.up.set(0, yDown ? -1 : 1, 0);
this.direction.set(0, 0, yDown ? 1 : -1);
this.position.set(zoom * viewportWidth / 2, zoom * viewportHeight / 2, 0);
this.viewportWidth = viewportWidth;
this.viewportHeight = viewportHeight;
this.update();
},
update: function() {
//TODO: support x/y offset
var w = this.viewportWidth,
h = this.viewportHeight,
near = Math.abs(this.near),
far = Math.abs(this.far),
zoom = this.zoom;
if (w===0||h===0) {
//What to do here... hmm ?
return;
}
this.projection.ortho(
zoom * -w / 2, zoom * w / 2,
zoom * -h / 2, zoom * h / 2,
near, far);
//build the view matrix
tmpVec3.copy(this.position).add(this.direction);
this.view.lookAt(this.position, tmpVec3, this.up);
//projection * view matrix
this.combined.copy(this.projection).mul(this.view);
//invert combined matrix, used for unproject
this.invProjectionView.copy(this.combined).invert();
}
});
module.exports = OrthographicCamera;

View file

@ -0,0 +1,136 @@
var Class = require('../utils/Class');
var Matrix4 = require('./vecmath/Matrix4');
var Vector2 = require('./vecmath/Vector2');
var Vector3 = require('./vecmath/Vector3');
var Camera = require('./Camera3D');
var tmpVec3 = new Vector3();
var PerspectiveCamera = new Class({
Extends: Camera,
//fov in RADIANS!
initialize: function(fieldOfView, viewportWidth, viewportHeight) {
Camera.call(this);
this.viewportWidth = viewportWidth;
this.viewportHeight = viewportHeight;
this.fieldOfView = fieldOfView;
this.update();
},
update: function() {
var aspect = this.viewportWidth / this.viewportHeight;
//create a perspective matrix for our camera
this.projection.perspective(this.fieldOfView, aspect,
Math.abs(this.near), Math.abs(this.far));
//build the view matrix
tmpVec3.copy(this.position).add(this.direction);
this.view.lookAt(this.position, tmpVec3, this.up);
//projection * view matrix
this.combined.copy(this.projection).mul(this.view);
//invert combined matrix, used for unproject
this.invProjectionView.copy(this.combined).invert();
}
});
module.exports = PerspectiveCamera;
/*
TODO: Billboarding should be moved to a separate module.
updateBillboardMatrix: function() {
if (!dirvec) {
dirvec = new Vector3();
rightvec = new Vector3();
billboardMatrix = new Matrix4();
}
var dir = dirvec.set(this.direction).negate();
// Better view-aligned billboards might use this:
// var dir = tmp.set(camera.position).sub(p).normalize();
var right = rightvec.set(this.up).cross(dir).normalize();
var up = tmpVec3.set(dir).cross(right).normalize();
var out = billboardMatrix.val;
out[0] = right.x;
out[1] = right.y;
out[2] = right.z;
out[3] = 0;
out[4] = up.x;
out[5] = up.y;
out[6] = up.z;
out[7] = 0;
out[8] = dir.x;
out[9] = dir.y;
out[10] = dir.z;
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
this.billboardMatrixDirty = false;
},
* This is a utility function for canvas 3D rendering,
* which determines the "point size" of a camera-facing
* sprite billboard given its 3D world position
* (origin at center of sprite) and its world width
* and height in x/y.
*
* We place into the output Vector2 the scaled width
* and height. If no `out` is specified, a new Vector2
* will be created for convenience (this should be avoided
* in tight loops).
*
* @param {Vector3} vec the position of the 3D sprite
* @param {Vector2} size the x and y dimensions of the sprite
* @param {Vector2} out the result, scaled x and y dimensions in 3D space
* @return {Vector2} returns the out parameter, or a new Vector2 if none was given
getPointSize: function(vec, size, out) {
//TODO: optimize this with a simple distance calculation:
//https://developer.valvesoftware.com/wiki/Field_of_View
if (!out)
out = new Vector2();
if (this.billboardMatrixDirty)
this.updateBillboardMatrix();
var tmp = tmpVec3;
var dx = size.x/2;
var dy = size.y/2;
tmp.set(-dx, -dy, 0).transformMat4(billboardMatrix).add(vec);
this.project(tmp, tmp);
var tlx = tmp.x;
var tly = tmp.y;
tmp.set(dx, dy, 0).transformMat4(billboardMatrix).add(vec);
this.project(tmp, tmp);
var brx = tmp.x;
var bry = tmp.y;
var w = Math.abs(brx - tlx);
var h = Math.abs(bry - tly);
return out.set(w, h);
},
*/

6
v3/src/camera3d/index.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
vecutil: require('./vecutil'),
Camera3D: require('./Camera3D'),
PerspectiveCamera: require('./PerspectiveCamera'),
OrthographicCamera: require('./OrthographicCamera')
};

View file

@ -0,0 +1,312 @@
var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
function Matrix3(m) {
this.val = new ARRAY_TYPE(9);
if (m) { //assume Matrix3 with val
this.copy(m);
} else { //default to identity
this.idt();
}
}
var mat3 = Matrix3.prototype;
mat3.clone = function() {
return new Matrix3(this);
};
mat3.set = function(otherMat) {
return this.copy(otherMat);
};
mat3.copy = function(otherMat) {
var out = this.val,
a = otherMat.val;
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
return this;
};
mat3.fromMat4 = function(m) {
var a = m.val,
out = this.val;
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[4];
out[4] = a[5];
out[5] = a[6];
out[6] = a[8];
out[7] = a[9];
out[8] = a[10];
return this;
};
mat3.fromArray = function(a) {
var out = this.val;
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
return this;
};
mat3.identity = function() {
var out = this.val;
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 1;
out[5] = 0;
out[6] = 0;
out[7] = 0;
out[8] = 1;
return this;
};
mat3.transpose = function() {
var a = this.val,
a01 = a[1],
a02 = a[2],
a12 = a[5];
a[1] = a[3];
a[2] = a[6];
a[3] = a01;
a[5] = a[7];
a[6] = a02;
a[7] = a12;
return this;
};
mat3.invert = function() {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8],
b01 = a22 * a11 - a12 * a21,
b11 = -a22 * a10 + a12 * a20,
b21 = a21 * a10 - a11 * a20,
// Calculate the determinant
det = a00 * b01 + a01 * b11 + a02 * b21;
if (!det) {
return null;
}
det = 1.0 / det;
a[0] = b01 * det;
a[1] = (-a22 * a01 + a02 * a21) * det;
a[2] = (a12 * a01 - a02 * a11) * det;
a[3] = b11 * det;
a[4] = (a22 * a00 - a02 * a20) * det;
a[5] = (-a12 * a00 + a02 * a10) * det;
a[6] = b21 * det;
a[7] = (-a21 * a00 + a01 * a20) * det;
a[8] = (a11 * a00 - a01 * a10) * det;
return this;
};
mat3.adjoint = function() {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8];
a[0] = (a11 * a22 - a12 * a21);
a[1] = (a02 * a21 - a01 * a22);
a[2] = (a01 * a12 - a02 * a11);
a[3] = (a12 * a20 - a10 * a22);
a[4] = (a00 * a22 - a02 * a20);
a[5] = (a02 * a10 - a00 * a12);
a[6] = (a10 * a21 - a11 * a20);
a[7] = (a01 * a20 - a00 * a21);
a[8] = (a00 * a11 - a01 * a10);
return this;
};
mat3.determinant = function() {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8];
return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
};
mat3.multiply = function(otherMat) {
var a = this.val,
b = otherMat.val,
a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8],
b00 = b[0], b01 = b[1], b02 = b[2],
b10 = b[3], b11 = b[4], b12 = b[5],
b20 = b[6], b21 = b[7], b22 = b[8];
a[0] = b00 * a00 + b01 * a10 + b02 * a20;
a[1] = b00 * a01 + b01 * a11 + b02 * a21;
a[2] = b00 * a02 + b01 * a12 + b02 * a22;
a[3] = b10 * a00 + b11 * a10 + b12 * a20;
a[4] = b10 * a01 + b11 * a11 + b12 * a21;
a[5] = b10 * a02 + b11 * a12 + b12 * a22;
a[6] = b20 * a00 + b21 * a10 + b22 * a20;
a[7] = b20 * a01 + b21 * a11 + b22 * a21;
a[8] = b20 * a02 + b21 * a12 + b22 * a22;
return this;
};
mat3.translate = function(v) {
var a = this.val,
x = v.x, y = v.y;
a[6] = x * a[0] + y * a[3] + a[6];
a[7] = x * a[1] + y * a[4] + a[7];
a[8] = x * a[2] + y * a[5] + a[8];
return this;
};
mat3.rotate = function(rad) {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
s = Math.sin(rad),
c = Math.cos(rad);
a[0] = c * a00 + s * a10;
a[1] = c * a01 + s * a11;
a[2] = c * a02 + s * a12;
a[3] = c * a10 - s * a00;
a[4] = c * a11 - s * a01;
a[5] = c * a12 - s * a02;
return this;
};
mat3.scale = function(v) {
var a = this.val,
x = v.x,
y = v.y;
a[0] = x * a[0];
a[1] = x * a[1];
a[2] = x * a[2];
a[3] = y * a[3];
a[4] = y * a[4];
a[5] = y * a[5];
return this;
};
mat3.fromQuat = function(q) {
var x = q.x, y = q.y, z = q.z, w = q.w,
x2 = x + x,
y2 = y + y,
z2 = z + z,
xx = x * x2,
xy = x * y2,
xz = x * z2,
yy = y * y2,
yz = y * z2,
zz = z * z2,
wx = w * x2,
wy = w * y2,
wz = w * z2,
out = this.val;
out[0] = 1 - (yy + zz);
out[3] = xy + wz;
out[6] = xz - wy;
out[1] = xy - wz;
out[4] = 1 - (xx + zz);
out[7] = yz + wx;
out[2] = xz + wy;
out[5] = yz - wx;
out[8] = 1 - (xx + yy);
return this;
};
mat3.normalFromMat4 = function(m) {
var a = m.val,
out = this.val,
a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32,
// Calculate the determinant
det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
return this;
};
mat3.mul = mat3.multiply;
mat3.idt = mat3.identity;
//This is handy for Pool utilities, to "reset" a
//shared object to its default state
mat3.reset = mat3.idt;
mat3.toString = function() {
var a = this.val;
return 'Matrix3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
a[3] + ', ' + a[4] + ', ' + a[5] + ', ' +
a[6] + ', ' + a[7] + ', ' + a[8] + ')';
};
mat3.str = mat3.toString;
module.exports = Matrix3;

View file

@ -0,0 +1,690 @@
var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
var EPSILON = 0.000001;
function Matrix4(m) {
this.val = new ARRAY_TYPE(16);
if (m) { //assume Matrix4 with val
this.copy(m);
} else { //default to identity
this.idt();
}
}
var mat4 = Matrix4.prototype;
mat4.clone = function() {
return new Matrix4(this);
};
mat4.set = function(otherMat) {
return this.copy(otherMat);
};
mat4.copy = function(otherMat) {
var out = this.val,
a = otherMat.val;
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return this;
};
mat4.fromArray = function(a) {
var out = this.val;
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[4] = a[4];
out[5] = a[5];
out[6] = a[6];
out[7] = a[7];
out[8] = a[8];
out[9] = a[9];
out[10] = a[10];
out[11] = a[11];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
return this;
};
mat4.identity = function() {
var out = this.val;
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = 1;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 1;
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
return this;
};
mat4.transpose = function() {
var a = this.val,
a01 = a[1], a02 = a[2], a03 = a[3],
a12 = a[6], a13 = a[7],
a23 = a[11];
a[1] = a[4];
a[2] = a[8];
a[3] = a[12];
a[4] = a01;
a[6] = a[9];
a[7] = a[13];
a[8] = a02;
a[9] = a12;
a[11] = a[14];
a[12] = a03;
a[13] = a13;
a[14] = a23;
return this;
};
mat4.invert = function() {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32,
// Calculate the determinant
det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) {
return null;
}
det = 1.0 / det;
a[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
a[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
a[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
a[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
a[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
a[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
a[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
a[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
a[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
a[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
a[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
a[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
a[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
a[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
a[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
a[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return this;
};
mat4.adjoint = function() {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
a[0] = (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
a[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
a[2] = (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
a[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
a[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
a[5] = (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
a[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
a[7] = (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
a[8] = (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
a[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
a[10] = (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
a[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
a[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
a[13] = (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
a[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
a[15] = (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
return this;
};
mat4.determinant = function () {
var a = this.val,
a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32;
// Calculate the determinant
return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
};
mat4.multiply = function(otherMat) {
var a = this.val,
b = otherMat.val,
a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
// Cache only the current line of the second matrix
var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
a[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
a[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
a[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
a[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
a[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
a[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
a[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
a[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
a[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
a[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
a[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
a[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
a[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
a[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
a[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
a[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
return this;
};
mat4.translate = function(v) {
var x = v.x, y = v.y, z = v.z,
a = this.val;
a[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
a[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
a[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
a[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
return this;
};
mat4.scale = function(v) {
var x = v.x, y = v.y, z = v.z, a = this.val;
a[0] = a[0] * x;
a[1] = a[1] * x;
a[2] = a[2] * x;
a[3] = a[3] * x;
a[4] = a[4] * y;
a[5] = a[5] * y;
a[6] = a[6] * y;
a[7] = a[7] * y;
a[8] = a[8] * z;
a[9] = a[9] * z;
a[10] = a[10] * z;
a[11] = a[11] * z;
a[12] = a[12];
a[13] = a[13];
a[14] = a[14];
a[15] = a[15];
return this;
};
mat4.rotate = function (rad, axis) {
var a = this.val,
x = axis.x, y = axis.y, z = axis.z,
len = Math.sqrt(x * x + y * y + z * z),
s, c, t,
a00, a01, a02, a03,
a10, a11, a12, a13,
a20, a21, a22, a23,
b00, b01, b02,
b10, b11, b12,
b20, b21, b22;
if (Math.abs(len) < EPSILON) { return null; }
len = 1 / len;
x *= len;
y *= len;
z *= len;
s = Math.sin(rad);
c = Math.cos(rad);
t = 1 - c;
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
// Construct the elements of the rotation matrix
b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;
// Perform rotation-specific matrix multiplication
a[0] = a00 * b00 + a10 * b01 + a20 * b02;
a[1] = a01 * b00 + a11 * b01 + a21 * b02;
a[2] = a02 * b00 + a12 * b01 + a22 * b02;
a[3] = a03 * b00 + a13 * b01 + a23 * b02;
a[4] = a00 * b10 + a10 * b11 + a20 * b12;
a[5] = a01 * b10 + a11 * b11 + a21 * b12;
a[6] = a02 * b10 + a12 * b11 + a22 * b12;
a[7] = a03 * b10 + a13 * b11 + a23 * b12;
a[8] = a00 * b20 + a10 * b21 + a20 * b22;
a[9] = a01 * b20 + a11 * b21 + a21 * b22;
a[10] = a02 * b20 + a12 * b21 + a22 * b22;
a[11] = a03 * b20 + a13 * b21 + a23 * b22;
return this;
};
mat4.rotateX = function(rad) {
var a = this.val,
s = Math.sin(rad),
c = Math.cos(rad),
a10 = a[4],
a11 = a[5],
a12 = a[6],
a13 = a[7],
a20 = a[8],
a21 = a[9],
a22 = a[10],
a23 = a[11];
// Perform axis-specific matrix multiplication
a[4] = a10 * c + a20 * s;
a[5] = a11 * c + a21 * s;
a[6] = a12 * c + a22 * s;
a[7] = a13 * c + a23 * s;
a[8] = a20 * c - a10 * s;
a[9] = a21 * c - a11 * s;
a[10] = a22 * c - a12 * s;
a[11] = a23 * c - a13 * s;
return this;
};
mat4.rotateY = function(rad) {
var a = this.val,
s = Math.sin(rad),
c = Math.cos(rad),
a00 = a[0],
a01 = a[1],
a02 = a[2],
a03 = a[3],
a20 = a[8],
a21 = a[9],
a22 = a[10],
a23 = a[11];
// Perform axis-specific matrix multiplication
a[0] = a00 * c - a20 * s;
a[1] = a01 * c - a21 * s;
a[2] = a02 * c - a22 * s;
a[3] = a03 * c - a23 * s;
a[8] = a00 * s + a20 * c;
a[9] = a01 * s + a21 * c;
a[10] = a02 * s + a22 * c;
a[11] = a03 * s + a23 * c;
return this;
};
mat4.rotateZ = function (rad) {
var a = this.val,
s = Math.sin(rad),
c = Math.cos(rad),
a00 = a[0],
a01 = a[1],
a02 = a[2],
a03 = a[3],
a10 = a[4],
a11 = a[5],
a12 = a[6],
a13 = a[7];
// Perform axis-specific matrix multiplication
a[0] = a00 * c + a10 * s;
a[1] = a01 * c + a11 * s;
a[2] = a02 * c + a12 * s;
a[3] = a03 * c + a13 * s;
a[4] = a10 * c - a00 * s;
a[5] = a11 * c - a01 * s;
a[6] = a12 * c - a02 * s;
a[7] = a13 * c - a03 * s;
return this;
};
mat4.fromRotationTranslation = function (q, v) {
// Quaternion math
var out = this.val,
x = q.x, y = q.y, z = q.z, w = q.w,
x2 = x + x,
y2 = y + y,
z2 = z + z,
xx = x * x2,
xy = x * y2,
xz = x * z2,
yy = y * y2,
yz = y * z2,
zz = z * z2,
wx = w * x2,
wy = w * y2,
wz = w * z2;
out[0] = 1 - (yy + zz);
out[1] = xy + wz;
out[2] = xz - wy;
out[3] = 0;
out[4] = xy - wz;
out[5] = 1 - (xx + zz);
out[6] = yz + wx;
out[7] = 0;
out[8] = xz + wy;
out[9] = yz - wx;
out[10] = 1 - (xx + yy);
out[11] = 0;
out[12] = v.x;
out[13] = v.y;
out[14] = v.z;
out[15] = 1;
return this;
};
mat4.fromQuat = function (q) {
var out = this.val,
x = q.x, y = q.y, z = q.z, w = q.w,
x2 = x + x,
y2 = y + y,
z2 = z + z,
xx = x * x2,
xy = x * y2,
xz = x * z2,
yy = y * y2,
yz = y * z2,
zz = z * z2,
wx = w * x2,
wy = w * y2,
wz = w * z2;
out[0] = 1 - (yy + zz);
out[1] = xy + wz;
out[2] = xz - wy;
out[3] = 0;
out[4] = xy - wz;
out[5] = 1 - (xx + zz);
out[6] = yz + wx;
out[7] = 0;
out[8] = xz + wy;
out[9] = yz - wx;
out[10] = 1 - (xx + yy);
out[11] = 0;
out[12] = 0;
out[13] = 0;
out[14] = 0;
out[15] = 1;
return this;
};
/**
* Generates a frustum matrix with the given bounds
*
* @param {Number} left Left bound of the frustum
* @param {Number} right Right bound of the frustum
* @param {Number} bottom Bottom bound of the frustum
* @param {Number} top Top bound of the frustum
* @param {Number} near Near bound of the frustum
* @param {Number} far Far bound of the frustum
* @returns {Matrix4} this for chaining
*/
mat4.frustum = function (left, right, bottom, top, near, far) {
var out = this.val,
rl = 1 / (right - left),
tb = 1 / (top - bottom),
nf = 1 / (near - far);
out[0] = (near * 2) * rl;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = (near * 2) * tb;
out[6] = 0;
out[7] = 0;
out[8] = (right + left) * rl;
out[9] = (top + bottom) * tb;
out[10] = (far + near) * nf;
out[11] = -1;
out[12] = 0;
out[13] = 0;
out[14] = (far * near * 2) * nf;
out[15] = 0;
return this;
};
/**
* Generates a perspective projection matrix with the given bounds
*
* @param {number} fovy Vertical field of view in radians
* @param {number} aspect Aspect ratio. typically viewport width/height
* @param {number} near Near bound of the frustum
* @param {number} far Far bound of the frustum
* @returns {Matrix4} this for chaining
*/
mat4.perspective = function (fovy, aspect, near, far) {
var out = this.val,
f = 1.0 / Math.tan(fovy / 2),
nf = 1 / (near - far);
out[0] = f / aspect;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = f;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = (far + near) * nf;
out[11] = -1;
out[12] = 0;
out[13] = 0;
out[14] = (2 * far * near) * nf;
out[15] = 0;
return this;
};
/**
* Generates a orthogonal projection matrix with the given bounds
*
* @param {number} left Left bound of the frustum
* @param {number} right Right bound of the frustum
* @param {number} bottom Bottom bound of the frustum
* @param {number} top Top bound of the frustum
* @param {number} near Near bound of the frustum
* @param {number} far Far bound of the frustum
* @returns {Matrix4} this for chaining
*/
mat4.ortho = function (left, right, bottom, top, near, far) {
var out = this.val,
lr = left-right,
bt = bottom-top,
nf = near-far;
//avoid division by zero
lr = lr===0 ? lr : 1/lr;
bt = bt===0 ? bt : 1/bt;
nf = nf===0 ? nf : 1/nf;
out[0] = -2 * lr;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = -2 * bt;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 2 * nf;
out[11] = 0;
out[12] = (left + right) * lr;
out[13] = (top + bottom) * bt;
out[14] = (far + near) * nf;
out[15] = 1;
return this;
};
/**
* Generates a look-at matrix with the given eye position, focal point, and up axis
*
* @param {Vector3} eye Position of the viewer
* @param {Vector3} center Point the viewer is looking at
* @param {Vector3} up vec3 pointing up
* @returns {Matrix4} this for chaining
*/
mat4.lookAt = function (eye, center, up) {
var out = this.val,
x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
eyex = eye.x,
eyey = eye.y,
eyez = eye.z,
upx = up.x,
upy = up.y,
upz = up.z,
centerx = center.x,
centery = center.y,
centerz = center.z;
if (Math.abs(eyex - centerx) < EPSILON &&
Math.abs(eyey - centery) < EPSILON &&
Math.abs(eyez - centerz) < EPSILON) {
return this.identity();
}
z0 = eyex - centerx;
z1 = eyey - centery;
z2 = eyez - centerz;
len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
z0 *= len;
z1 *= len;
z2 *= len;
x0 = upy * z2 - upz * z1;
x1 = upz * z0 - upx * z2;
x2 = upx * z1 - upy * z0;
len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
if (!len) {
x0 = 0;
x1 = 0;
x2 = 0;
} else {
len = 1 / len;
x0 *= len;
x1 *= len;
x2 *= len;
}
y0 = z1 * x2 - z2 * x1;
y1 = z2 * x0 - z0 * x2;
y2 = z0 * x1 - z1 * x0;
len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
if (!len) {
y0 = 0;
y1 = 0;
y2 = 0;
} else {
len = 1 / len;
y0 *= len;
y1 *= len;
y2 *= len;
}
out[0] = x0;
out[1] = y0;
out[2] = z0;
out[3] = 0;
out[4] = x1;
out[5] = y1;
out[6] = z1;
out[7] = 0;
out[8] = x2;
out[9] = y2;
out[10] = z2;
out[11] = 0;
out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
out[15] = 1;
return this;
};
mat4.mul = mat4.multiply;
mat4.idt = mat4.identity;
//This is handy for Pool utilities, to "reset" a
//shared object to its default state
mat4.reset = mat4.idt;
mat4.toString = function () {
var a = this.val;
return 'Matrix4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +
a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
};
mat4.str = mat4.toString;
module.exports = Matrix4;

View file

@ -0,0 +1,281 @@
var Vector3 = require('./Vector3');
var Matrix3 = require('./Matrix3');
var common = require('./common');
//some shared 'private' arrays
var s_iNext = (typeof Int8Array !== 'undefined' ? new Int8Array([1,2,0]) : [1,2,0]);
var tmp = (typeof Float32Array !== 'undefined' ? new Float32Array([0,0,0]) : [0,0,0]);
var xUnitVec3 = new Vector3(1, 0, 0);
var yUnitVec3 = new Vector3(0, 1, 0);
var tmpvec = new Vector3();
var tmpMat3 = new Matrix3();
function Quaternion(x, y, z, w) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
this.z = x.z||0;
this.w = x.w||0;
} else {
this.x = x||0;
this.y = y||0;
this.z = z||0;
this.w = w||0;
}
}
var quat = Quaternion.prototype;
//mixin common functions
for (var k in common) {
quat[k] = common[k];
}
quat.rotationTo = function(a, b) {
var dot = a.x * b.x + a.y * b.y + a.z * b.z; //a.dot(b)
if (dot < -0.999999) {
if (tmpvec.copy(xUnitVec3).cross(a).len() < 0.000001)
tmpvec.copy(yUnitVec3).cross(a);
tmpvec.normalize();
return this.setAxisAngle(tmpvec, Math.PI);
} else if (dot > 0.999999) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
return this;
} else {
tmpvec.copy(a).cross(b);
this.x = tmpvec.x;
this.y = tmpvec.y;
this.z = tmpvec.z;
this.w = 1 + dot;
return this.normalize();
}
};
quat.setAxes = function(view, right, up) {
var m = tmpMat3.val;
m[0] = right.x;
m[3] = right.y;
m[6] = right.z;
m[1] = up.x;
m[4] = up.y;
m[7] = up.z;
m[2] = -view.x;
m[5] = -view.y;
m[8] = -view.z;
return this.fromMat3(tmpMat3).normalize();
};
quat.identity = function() {
this.x = this.y = this.z = 0;
this.w = 1;
return this;
};
quat.setAxisAngle = function(axis, rad) {
rad = rad * 0.5;
var s = Math.sin(rad);
this.x = s * axis.x;
this.y = s * axis.y;
this.z = s * axis.z;
this.w = Math.cos(rad);
return this;
};
quat.multiply = function(b) {
var ax = this.x, ay = this.y, az = this.z, aw = this.w,
bx = b.x, by = b.y, bz = b.z, bw = b.w;
this.x = ax * bw + aw * bx + ay * bz - az * by;
this.y = ay * bw + aw * by + az * bx - ax * bz;
this.z = az * bw + aw * bz + ax * by - ay * bx;
this.w = aw * bw - ax * bx - ay * by - az * bz;
return this;
};
quat.slerp = function (b, t) {
// benchmarks:
// http://jsperf.com/quaternion-slerp-implementations
var ax = this.x, ay = this.y, az = this.y, aw = this.y,
bx = b.x, by = b.y, bz = b.z, bw = b.w;
var omega, cosom, sinom, scale0, scale1;
// calc cosine
cosom = ax * bx + ay * by + az * bz + aw * bw;
// adjust signs (if necessary)
if ( cosom < 0.0 ) {
cosom = -cosom;
bx = - bx;
by = - by;
bz = - bz;
bw = - bw;
}
// calculate coefficients
if ( (1.0 - cosom) > 0.000001 ) {
// standard case (slerp)
omega = Math.acos(cosom);
sinom = Math.sin(omega);
scale0 = Math.sin((1.0 - t) * omega) / sinom;
scale1 = Math.sin(t * omega) / sinom;
} else {
// "from" and "to" quaternions are very close
// ... so we can do a linear interpolation
scale0 = 1.0 - t;
scale1 = t;
}
// calculate final values
this.x = scale0 * ax + scale1 * bx;
this.y = scale0 * ay + scale1 * by;
this.z = scale0 * az + scale1 * bz;
this.w = scale0 * aw + scale1 * bw;
return this;
};
quat.invert = function() {
var a0 = this.x, a1 = this.y, a2 = this.z, a3 = this.w,
dot = a0*a0 + a1*a1 + a2*a2 + a3*a3,
invDot = dot ? 1.0/dot : 0;
// TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
this.x = -a0*invDot;
this.y = -a1*invDot;
this.z = -a2*invDot;
this.w = a3*invDot;
return this;
};
quat.conjugate = function() {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
return this;
};
quat.rotateX = function (rad) {
rad *= 0.5;
var ax = this.x, ay = this.y, az = this.z, aw = this.w,
bx = Math.sin(rad), bw = Math.cos(rad);
this.x = ax * bw + aw * bx;
this.y = ay * bw + az * bx;
this.z = az * bw - ay * bx;
this.w = aw * bw - ax * bx;
return this;
};
quat.rotateY = function (rad) {
rad *= 0.5;
var ax = this.x, ay = this.y, az = this.z, aw = this.w,
by = Math.sin(rad), bw = Math.cos(rad);
this.x = ax * bw - az * by;
this.y = ay * bw + aw * by;
this.z = az * bw + ax * by;
this.w = aw * bw - ay * by;
return this;
};
quat.rotateZ = function (rad) {
rad *= 0.5;
var ax = this.x, ay = this.y, az = this.z, aw = this.w,
bz = Math.sin(rad), bw = Math.cos(rad);
this.x = ax * bw + ay * bz;
this.y = ay * bw - ax * bz;
this.z = az * bw + aw * bz;
this.w = aw * bw - az * bz;
return this;
};
quat.calculateW = function () {
var x = this.x, y = this.y, z = this.z;
this.x = x;
this.y = y;
this.z = z;
this.w = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
return this;
};
quat.fromMat3 = function(mat) {
// benchmarks:
// http://jsperf.com/typed-array-access-speed
// http://jsperf.com/conversion-of-3x3-matrix-to-quaternion
// Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
// article "Quaternion Calculus and Fast Animation".
var m = mat.val,
fTrace = m[0] + m[4] + m[8];
var fRoot;
if ( fTrace > 0.0 ) {
// |w| > 1/2, may as well choose w > 1/2
fRoot = Math.sqrt(fTrace + 1.0); // 2w
this.w = 0.5 * fRoot;
fRoot = 0.5/fRoot; // 1/(4w)
this.x = (m[7]-m[5])*fRoot;
this.y = (m[2]-m[6])*fRoot;
this.z = (m[3]-m[1])*fRoot;
} else {
// |w| <= 1/2
var i = 0;
if ( m[4] > m[0] )
i = 1;
if ( m[8] > m[i*3+i] )
i = 2;
var j = s_iNext[i];
var k = s_iNext[j];
//This isn't quite as clean without array access...
fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0);
tmp[i] = 0.5 * fRoot;
fRoot = 0.5 / fRoot;
tmp[j] = (m[j*3+i] + m[i*3+j]) * fRoot;
tmp[k] = (m[k*3+i] + m[i*3+k]) * fRoot;
this.x = tmp[0];
this.y = tmp[1];
this.z = tmp[2];
this.w = (m[k*3+j] - m[j*3+k]) * fRoot;
}
return this;
};
quat.idt = quat.identity;
quat.sub = quat.subtract;
quat.mul = quat.multiply;
quat.len = quat.length;
quat.lenSq = quat.lengthSq;
//This is handy for Pool utilities, to "reset" a
//shared object to its default state
quat.reset = quat.idt;
quat.toString = function() {
return 'Quaternion(' + this.x + ', ' + this.y + ', ' + this.z + ', ' + this.w + ')';
};
quat.str = quat.toString;
module.exports = Quaternion;

View file

@ -0,0 +1,201 @@
function Vector2(x, y) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
} else {
this.x = x||0;
this.y = y||0;
}
}
//shorthand it for better minification
var vec2 = Vector2.prototype;
/**
* Returns a new instance of Vector2 with
* this vector's components.
* @return {Vector2} a clone of this vector
*/
vec2.clone = function() {
return new Vector2(this.x, this.y);
};
/**
* Copies the x, y components from the specified
* Vector. Any undefined components from `otherVec`
* will default to zero.
*
* @param {otherVec} the other Vector2 to copy
* @return {Vector2} this, for chaining
*/
vec2.copy = function(otherVec) {
this.x = otherVec.x||0;
this.y = otherVec.y||0;
return this;
};
/**
* A convenience function to set the components of
* this vector as x and y. Falsy or undefined
* parameters will default to zero.
*
* You can also pass a vector object instead of
* individual components, to copy the object's components.
*
* @param {Number} x the x component
* @param {Number} y the y component
* @return {Vector2} this, for chaining
*/
vec2.set = function(x, y) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
} else {
this.x = x||0;
this.y = y||0;
}
return this;
};
vec2.add = function(v) {
this.x += v.x;
this.y += v.y;
return this;
};
vec2.subtract = function(v) {
this.x -= v.x;
this.y -= v.y;
return this;
};
vec2.multiply = function(v) {
this.x *= v.x;
this.y *= v.y;
return this;
};
vec2.scale = function(s) {
this.x *= s;
this.y *= s;
return this;
};
vec2.divide = function(v) {
this.x /= v.x;
this.y /= v.y;
return this;
};
vec2.negate = function() {
this.x = -this.x;
this.y = -this.y;
return this;
};
vec2.distance = function(v) {
var dx = v.x - this.x,
dy = v.y - this.y;
return Math.sqrt(dx*dx + dy*dy);
};
vec2.distanceSq = function(v) {
var dx = v.x - this.x,
dy = v.y - this.y;
return dx*dx + dy*dy;
};
vec2.length = function() {
var x = this.x,
y = this.y;
return Math.sqrt(x*x + y*y);
};
vec2.lengthSq = function() {
var x = this.x,
y = this.y;
return x*x + y*y;
};
vec2.normalize = function() {
var x = this.x,
y = this.y;
var len = x*x + y*y;
if (len > 0) {
len = 1 / Math.sqrt(len);
this.x = x*len;
this.y = y*len;
}
return this;
};
vec2.dot = function(v) {
return this.x * v.x + this.y * v.y;
};
//Unlike Vector3, this returns a scalar
//http://allenchou.net/2013/07/cross-product-of-2d-vectors/
vec2.cross = function(v) {
return this.x * v.y - this.y * v.x;
};
vec2.lerp = function(v, t) {
var ax = this.x,
ay = this.y;
t = t||0;
this.x = ax + t * (v.x - ax);
this.y = ay + t * (v.y - ay);
return this;
};
vec2.transformMat3 = function(mat) {
var x = this.x, y = this.y, m = mat.val;
this.x = m[0] * x + m[3] * y + m[6];
this.y = m[1] * x + m[4] * y + m[7];
return this;
};
vec2.transformMat4 = function(mat) {
var x = this.x,
y = this.y,
m = mat.val;
this.x = m[0] * x + m[4] * y + m[12];
this.y = m[1] * x + m[5] * y + m[13];
return this;
};
vec2.reset = function() {
this.x = 0;
this.y = 0;
return this;
};
vec2.sub = vec2.subtract;
vec2.mul = vec2.multiply;
vec2.div = vec2.divide;
vec2.dist = vec2.distance;
vec2.distSq = vec2.distanceSq;
vec2.len = vec2.length;
vec2.lenSq = vec2.lengthSq;
vec2.toString = function() {
return 'Vector2(' + this.x + ', ' + this.y + ')';
};
vec2.random = function(scale) {
scale = scale || 1.0;
var r = Math.random() * 2.0 * Math.PI;
this.x = Math.cos(r) * scale;
this.y = Math.sin(r) * scale;
return this;
};
vec2.str = vec2.toString;
module.exports = Vector2;

View file

@ -0,0 +1,286 @@
function Vector3(x, y, z) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
this.z = x.z||0;
} else {
this.x = x||0;
this.y = y||0;
this.z = z||0;
}
}
//shorthand it for better minification
var vec3 = Vector3.prototype;
vec3.clone = function() {
return new Vector3(this.x, this.y, this.z);
};
vec3.copy = function(otherVec) {
this.x = otherVec.x;
this.y = otherVec.y;
this.z = otherVec.z;
return this;
};
vec3.set = function(x, y, z) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
this.z = x.z||0;
} else {
this.x = x||0;
this.y = y||0;
this.z = z||0;
}
return this;
};
vec3.add = function(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
};
vec3.subtract = function(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
};
vec3.multiply = function(v) {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
return this;
};
vec3.scale = function(s) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
};
vec3.divide = function(v) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
return this;
};
vec3.negate = function() {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
return this;
};
vec3.distance = function(v) {
var dx = v.x - this.x,
dy = v.y - this.y,
dz = v.z - this.z;
return Math.sqrt(dx*dx + dy*dy + dz*dz);
};
vec3.distanceSq = function(v) {
var dx = v.x - this.x,
dy = v.y - this.y,
dz = v.z - this.z;
return dx*dx + dy*dy + dz*dz;
};
vec3.length = function() {
var x = this.x,
y = this.y,
z = this.z;
return Math.sqrt(x*x + y*y + z*z);
};
vec3.lengthSq = function() {
var x = this.x,
y = this.y,
z = this.z;
return x*x + y*y + z*z;
};
vec3.normalize = function() {
var x = this.x,
y = this.y,
z = this.z;
var len = x*x + y*y + z*z;
if (len > 0) {
len = 1 / Math.sqrt(len);
this.x = x*len;
this.y = y*len;
this.z = z*len;
}
return this;
};
vec3.dot = function(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
};
vec3.cross = function(v) {
var ax = this.x, ay = this.y, az = this.z,
bx = v.x, by = v.y, bz = v.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;
return this;
};
vec3.lerp = function(v, t) {
var ax = this.x,
ay = this.y,
az = this.z;
t = t||0;
this.x = ax + t * (v.x - ax);
this.y = ay + t * (v.y - ay);
this.z = az + t * (v.z - az);
return this;
};
vec3.transformMat4 = function(mat) {
var x = this.x, y = this.y, z = this.z, m = mat.val;
this.x = m[0] * x + m[4] * y + m[8] * z + m[12];
this.y = m[1] * x + m[5] * y + m[9] * z + m[13];
this.z = m[2] * x + m[6] * y + m[10] * z + m[14];
return this;
};
vec3.transformMat3 = function(mat) {
var x = this.x, y = this.y, z = this.z, m = mat.val;
this.x = x * m[0] + y * m[3] + z * m[6];
this.y = x * m[1] + y * m[4] + z * m[7];
this.z = x * m[2] + y * m[5] + z * m[8];
return this;
};
vec3.transformQuat = function(q) {
// benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations
var x = this.x, y = this.y, z = this.z,
qx = q.x, qy = q.y, qz = q.z, qw = q.w,
// calculate quat * vec
ix = qw * x + qy * z - qz * y,
iy = qw * y + qz * x - qx * z,
iz = qw * z + qx * y - qy * x,
iw = -qx * x - qy * y - qz * z;
// calculate result * inverse quat
this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return this;
};
/**
* Multiplies this Vector3 by the specified matrix,
* applying a W divide. This is useful for projection,
* e.g. unprojecting a 2D point into 3D space.
*
* @method prj
* @param {Matrix4} the 4x4 matrix to multiply with
* @return {Vector3} this object for chaining
*/
vec3.project = function(mat) {
var x = this.x,
y = this.y,
z = this.z,
m = mat.val,
a00 = m[0], a01 = m[1], a02 = m[2], a03 = m[3],
a10 = m[4], a11 = m[5], a12 = m[6], a13 = m[7],
a20 = m[8], a21 = m[9], a22 = m[10], a23 = m[11],
a30 = m[12], a31 = m[13], a32 = m[14], a33 = m[15];
var l_w = 1 / (x * a03 + y * a13 + z * a23 + a33);
this.x = (x * a00 + y * a10 + z * a20 + a30) * l_w;
this.y = (x * a01 + y * a11 + z * a21 + a31) * l_w;
this.z = (x * a02 + y * a12 + z * a22 + a32) * l_w;
return this;
};
/**
* Unproject this point from 2D space to 3D space.
* The point should have its x and y properties set to
* 2D screen space, and the z either at 0 (near plane)
* or 1 (far plane). The provided matrix is assumed to already
* be combined, i.e. projection * view * model.
*
* After this operation, this vector's (x, y, z) components will
* represent the unprojected 3D coordinate.
*
* @param {Vector4} viewport screen x, y, width and height in pixels
* @param {Matrix4} invProjectionView combined projection and view matrix
* @return {Vector3} this object, for chaining
*/
vec3.unproject = function(viewport, invProjectionView) {
var viewX = viewport.x,
viewY = viewport.y,
viewWidth = viewport.z,
viewHeight = viewport.w;
var x = this.x,
y = this.y,
z = this.z;
x = x - viewX;
y = viewHeight - y - 1;
y = y - viewY;
this.x = (2 * x) / viewWidth - 1;
this.y = (2 * y) / viewHeight - 1;
this.z = 2 * z - 1;
return this.project(invProjectionView);
};
vec3.random = function(scale) {
scale = scale || 1.0;
var r = Math.random() * 2.0 * Math.PI;
var z = (Math.random() * 2.0) - 1.0;
var zScale = Math.sqrt(1.0-z*z) * scale;
this.x = Math.cos(r) * zScale;
this.y = Math.sin(r) * zScale;
this.z = z * scale;
return this;
};
vec3.reset = function() {
this.x = 0;
this.y = 0;
this.z = 0;
return this;
};
vec3.sub = vec3.subtract;
vec3.mul = vec3.multiply;
vec3.div = vec3.divide;
vec3.dist = vec3.distance;
vec3.distSq = vec3.distanceSq;
vec3.len = vec3.length;
vec3.lenSq = vec3.lengthSq;
vec3.toString = function() {
return 'Vector3(' + this.x + ', ' + this.y + ', ' + this.z + ')';
};
vec3.str = vec3.toString;
module.exports = Vector3;

View file

@ -0,0 +1,138 @@
var common = require('./common');
function Vector4(x, y, z, w) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
this.z = x.z||0;
this.w = x.w||0;
} else {
this.x = x||0;
this.y = y||0;
this.z = z||0;
this.w = w||0;
}
}
//shorthand it for better minification
var vec4 = Vector4.prototype;
//mixin common functions
for (var k in common) {
vec4[k] = common[k];
}
vec4.clone = function() {
return new Vector4(this.x, this.y, this.z, this.w);
};
vec4.multiply = function(v) {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
this.w *= v.w;
return this;
};
vec4.divide = function(v) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
this.w /= v.w;
return this;
};
vec4.distance = function(v) {
var dx = v.x - this.x,
dy = v.y - this.y,
dz = v.z - this.z,
dw = v.w - this.w;
return Math.sqrt(dx*dx + dy*dy + dz*dz + dw*dw);
};
vec4.distanceSq = function(v) {
var dx = v.x - this.x,
dy = v.y - this.y,
dz = v.z - this.z,
dw = v.w - this.w;
return dx*dx + dy*dy + dz*dz + dw*dw;
};
vec4.negate = function() {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
this.w = -this.w;
return this;
};
vec4.transformMat4 = function(mat) {
var m = mat.val, x = this.x, y = this.y, z = this.z, w = this.w;
this.x = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
this.y = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
this.z = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
this.w = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
return this;
};
//// TODO: is this really the same as Vector3 ??
/// Also, what about this:
/// http://molecularmusings.wordpress.com/2013/05/24/a-faster-quaternion-vector-multiplication/
vec4.transformQuat = function(q) {
// benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations
var x = this.x, y = this.y, z = this.z,
qx = q.x, qy = q.y, qz = q.z, qw = q.w,
// calculate quat * vec
ix = qw * x + qy * z - qz * y,
iy = qw * y + qz * x - qx * z,
iz = qw * z + qx * y - qy * x,
iw = -qx * x - qy * y - qz * z;
// calculate result * inverse quat
this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return this;
};
vec4.random = function(scale) {
scale = scale || 1.0;
//Not spherical; should fix this for more uniform distribution
this.x = (Math.random() * 2 - 1) * scale;
this.y = (Math.random() * 2 - 1) * scale;
this.z = (Math.random() * 2 - 1) * scale;
this.w = (Math.random() * 2 - 1) * scale;
return this;
};
vec4.reset = function() {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 0;
return this;
};
vec4.sub = vec4.subtract;
vec4.mul = vec4.multiply;
vec4.div = vec4.divide;
vec4.dist = vec4.distance;
vec4.distSq = vec4.distanceSq;
vec4.len = vec4.length;
vec4.lenSq = vec4.lengthSq;
vec4.toString = function() {
return 'Vector4(' + this.x + ', ' + this.y + ', ' + this.z + ', ' + this.w + ')';
};
vec4.str = vec4.toString;
module.exports = Vector4;

View file

@ -0,0 +1,186 @@
//common vec4 functions
module.exports = {
/**
* Copies the x, y, z, w components from the specified
* Vector. Unlike most other operations, this function
* will default undefined components on `otherVec` to zero.
*
* @method copy
* @param {otherVec} the other Vector4 to copy
* @return {Vector} this, for chaining
*/
/**
* A convenience function to set the components of
* this vector as x, y, z, w. Falsy or undefined
* parameters will default to zero.
*
* You can also pass a vector object instead of
* individual components, to copy the object's components.
*
* @method set
* @param {Number} x the x component
* @param {Number} y the y component
* @param {Number} z the z component
* @param {Number} w the w component
* @return {Vector2} this, for chaining
*/
/**
* Adds the components of the other Vector4 to
* this vector.
*
* @method add
* @param {Vector4} otherVec other vector, right operand
* @return {Vector2} this, for chaining
*/
/**
* Subtracts the components of the other Vector4
* from this vector. Aliased as `sub()`
*
* @method subtract
* @param {Vector4} otherVec other vector, right operand
* @return {Vector2} this, for chaining
*/
/**
* Multiplies the components of this Vector4
* by a scalar amount.
*
* @method scale
* @param {Number} s the scale to multiply by
* @return {Vector4} this, for chaining
*/
/**
* Returns the magnitude (length) of this vector.
*
* Aliased as `len()`
*
* @method length
* @return {Number} the length of this vector
*/
/**
* Returns the squared magnitude (length) of this vector.
*
* Aliased as `lenSq()`
*
* @method lengthSq
* @return {Number} the squared length of this vector
*/
/**
* Normalizes this vector to a unit vector.
* @method normalize
* @return {Vector4} this, for chaining
*/
/**
* Returns the dot product of this vector
* and the specified Vector4.
*
* @method dot
* @return {Number} the dot product
*/
copy: function(otherVec) {
this.x = otherVec.x||0;
this.y = otherVec.y||0;
this.z = otherVec.z||0;
this.w = otherVec.w||0;
return this;
},
set: function(x, y, z, w) {
if (typeof x === "object") {
this.x = x.x||0;
this.y = x.y||0;
this.z = x.z||0;
this.w = x.w||0;
} else {
this.x = x||0;
this.y = y||0;
this.z = z||0;
this.w = w||0;
}
return this;
},
add: function(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
this.w += v.w;
return this;
},
subtract: function(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
this.w -= v.w;
return this;
},
scale: function(s) {
this.x *= s;
this.y *= s;
this.z *= s;
this.w *= s;
return this;
},
length: function() {
var x = this.x,
y = this.y,
z = this.z,
w = this.w;
return Math.sqrt(x*x + y*y + z*z + w*w);
},
lengthSq: function() {
var x = this.x,
y = this.y,
z = this.z,
w = this.w;
return x*x + y*y + z*z + w*w;
},
normalize: function() {
var x = this.x,
y = this.y,
z = this.z,
w = this.w;
var len = x*x + y*y + z*z + w*w;
if (len > 0) {
len = 1 / Math.sqrt(len);
this.x = x*len;
this.y = y*len;
this.z = z*len;
this.w = w*len;
}
return this;
},
dot: function(v) {
return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
},
lerp: function(v, t) {
var ax = this.x,
ay = this.y,
az = this.z,
aw = this.w;
t = t||0;
this.x = ax + t * (v.x - ax);
this.y = ay + t * (v.y - ay);
this.z = az + t * (v.z - az);
this.w = aw + t * (v.w - aw);
return this;
}
};

View file

@ -0,0 +1,8 @@
module.exports = {
Vector2: require('./Vector2'),
Vector3: require('./Vector3'),
Vector4: require('./Vector4'),
Matrix3: require('./Matrix3'),
Matrix4: require('./Matrix4'),
Quaternion: require('./Quaternion')
};

View file

@ -0,0 +1,34 @@
var Vector3 = require('./vecmath/Vector3');
var Matrix4 = require('./vecmath/Matrix4');
var Quaternion = require('./vecmath/Quaternion');
var tmpMat4 = new Matrix4();
var tmpQuat = new Quaternion();
var tmpVec3 = new Vector3();
var util = {};
/**
* Rotates a vector in place by axis angle.
*
* This is the same as transforming a point by an
* axis-angle quaternion, but it has higher precision.
*
* @param {Vector3} vec [description]
* @param {Vector3} axis [description]
* @param {float} radians [description]
* @return {Vector3} [description]
*/
util.rotate = function(vec, axis, radians) {
//set the quaternion to our axis angle
tmpQuat.setAxisAngle(axis, radians);
//create a rotation matrix from the axis angle
tmpMat4.fromRotationTranslation( tmpQuat, tmpVec3.set(0, 0, 0) );
//multiply our vector by the rotation matrix
return vec.transformMat4( tmpMat4 );
};
module.exports = util;

View file

@ -19,6 +19,8 @@ var Transform = {
x: 0,
y: 0,
z: 0,
w: 0,
depth: {
@ -109,13 +111,17 @@ var Transform = {
}
},
setPosition: function (x, y)
setPosition: function (x, y, z, w)
{
if (x === undefined) { x = 0; }
if (y === undefined) { y = x; }
if (z === undefined) { z = 0; }
if (w === undefined) { w = 0; }
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
},
@ -149,6 +155,24 @@ var Transform = {
return this;
},
setZ: function (value)
{
if (value === undefined) { value = 0; }
this.z = value;
return this;
},
setW: function (value)
{
if (value === undefined) { value = 0; }
this.w = value;
return this;
},
setDepth: function (value)
{
if (value === undefined) { value = 0; }

View file

@ -1,19 +1,36 @@
var Percent = function (a, b, base)
{
if (base === undefined) { base = 0; }
// Work out what % value is of the range between min and max.
// If max isn't given then you get the % of value to min.
// You can optionally specify an upperMax, which is a mid-way point in the range
// that represents 100%, after which the % starts to go down to zero again.
if (a > b || base > b)
var Percent = function (value, min, max, upperMax)
{
if (max === undefined) { max = min + 1; }
var percentage = (value - min) / (max - min);
if (percentage > 1)
{
return 1;
if (upperMax !== undefined)
{
percentage = ((upperMax - value)) / (upperMax - max);
if (percentage < 0)
{
percentage = 0;
}
}
else
{
percentage = 1;
}
}
else if (a < base || base > a)
else if (percentage < 0)
{
return 0;
}
else
{
return (a - base) / b;
percentage = 0;
}
return percentage;
};
module.exports = Percent;

221
v3/src/math/Vector2.js Normal file
View file

@ -0,0 +1,221 @@
// Based on vecmath lib by mattdesl https://github.com/mattdesl/vecmath and gl-matrix
var Class = require('../utils/Class');
var Vector2 = new Class({
initialize:
function Vector2 (x, y)
{
if (x === undefined) { x = 0; }
if (y === undefined) { y = 0; }
this.x = x;
this.y = y;
},
clone: function ()
{
return new Vector2(this.x, this.y);
},
copy: function (src)
{
this.x = src.x || 0;
this.y = src.y || 0;
return this;
},
set: function (x, y)
{
if (typeof x === 'object')
{
this.x = x.x || 0;
this.y = x.y || 0;
}
else
{
this.x = x;
this.y = y;
}
return this;
},
add: function (src)
{
this.x += src.x;
this.y += src.y;
return this;
},
subtract: function (src)
{
this.x -= src.x;
this.y -= src.y;
return this;
},
multiply: function (src)
{
this.x *= src.x;
this.y *= src.y;
return this;
},
scale: function (value)
{
this.x *= value;
this.y *= value;
return this;
},
divide: function (src)
{
this.x /= src.x;
this.y /= src.y;
return this;
},
negate: function ()
{
this.x = -this.x;
this.y = -this.y;
return this;
},
distance: function (src)
{
var dx = src.x - this.x;
var dy = src.y - this.y;
return Math.sqrt(dx * dx + dy * dy);
},
distanceSq: function (src)
{
var dx = src.x - this.x;
var dy = src.y - this.y;
return dx * dx + dy * dy;
},
length: function ()
{
var x = this.x;
var y = this.y;
return Math.sqrt(x * x + y * y);
},
lengthSq: function ()
{
var x = this.x;
var y = this.y;
return x * x + y * y;
},
normalize: function ()
{
var x = this.x;
var y = this.y;
var len = x * x + y * y;
if (len > 0)
{
len = 1 / Math.sqrt(len);
this.x = x * len;
this.y = y * len;
}
return this;
},
dot: function (src)
{
return this.x * src.x + this.y * src.y;
},
cross: function (src)
{
return this.x * src.y - this.y * src.x;
},
lerp: function (src, t)
{
if (t === undefined) { t = 0; }
var ax = this.x;
var ay = this.y;
this.x = ax + t * (src.x - ax);
this.y = ay + t * (src.y - ay);
return this;
},
transformMat3: function (mat)
{
var x = this.x;
var y = this.y;
var m = mat.val;
this.x = m[0] * x + m[3] * y + m[6];
this.y = m[1] * x + m[4] * y + m[7];
return this;
},
transformMat4: function (mat)
{
var x = this.x;
var y = this.y;
var m = mat.val;
this.x = m[0] * x + m[4] * y + m[12];
this.y = m[1] * x + m[5] * y + m[13];
return this;
},
reset: function ()
{
this.x = 0;
this.y = 0;
return this;
},
random: function (scale)
{
if (scale === undefined) { scale = 1; }
var r = Math.random() * 2 * Math.PI;
this.x = Math.cos(r) * scale;
this.y = Math.sin(r) * scale;
return this;
}
});
Vector2.sub = Vector2.subtract;
Vector2.mul = Vector2.multiply;
Vector2.div = Vector2.divide;
Vector2.dist = Vector2.distance;
Vector2.distSq = Vector2.distanceSq;
Vector2.len = Vector2.length;
Vector2.lenSq = Vector2.lengthSq;
module.exports = Vector2;

View file

@ -19,6 +19,9 @@ var Phaser = {
Create: require('./create/'),
Cameras3D: require('./camera3d/'),
VecMath: require('./camera3d/vecmath/'),
DOM: require('./dom/'),
Game: require('./boot/Game'),