/**
* Represents a rigid body
*
* @class RigidBody
* @constructor
* @param shape
* @param mass {Number}
*/
Goblin.RigidBody = (function() {
var body_count = 0;
return function( shape, mass ) {
/**
* goblin ID of the body
*
* @property id
* @type {Number}
*/
this.id = body_count++;
/**
* shape definition for this rigid body
*
* @property shape
*/
this.shape = shape;
/**
* axis-aligned bounding box enclosing this body
*
* @property aabb
* @type {AABB}
*/
this.aabb = new Goblin.AABB();
/**
* the rigid body's mass
*
* @property mass
* @type {Number}
* @default Infinity
*/
this.mass = mass || Infinity;
/**
* the rigid body's current position
*
* @property position
* @type {vec3}
* @default [ 0, 0, 0 ]
*/
this.position = new Goblin.Vector3();
/**
* rotation of the rigid body
*
* @type {quat4}
*/
this.rotation = new Goblin.Quaternion( 0, 0, 0, 1 );
/**
* the rigid body's current linear velocity
*
* @property linear_velocity
* @type {vec3}
* @default [ 0, 0, 0 ]
*/
this.linear_velocity = new Goblin.Vector3();
/**
* the rigid body's current angular velocity
*
* @property angular_velocity
* @type {vec3}
* @default [ 0, 0, 0 ]
*/
this.angular_velocity = new Goblin.Vector3();
/**
* transformation matrix transforming points from object space to world space
*
* @property transform
* @type {mat4}
*/
this.transform = new Goblin.Matrix4();
this.transform.identity();
/**
* transformation matrix transforming points from world space to object space
*
* @property transform_inverse
* @type {mat4}
*/
this.transform_inverse = new Goblin.Matrix4();
this.transform_inverse.identity();
this.inertiaTensor = shape.getInertiaTensor( mass );
this.inverseInertiaTensor = new Goblin.Matrix3();
this.inertiaTensor.invertInto( this.inverseInertiaTensor );
this.inertiaTensorWorldFrame = new Goblin.Matrix3();
this.inverseInertiaTensorWorldFrame = new Goblin.Matrix3();
/**
* the rigid body's current acceleration
*
* @property acceleration
* @type {vec3}
* @default [ 0, 0, 0 ]
*/
this.acceleration = new Goblin.Vector3();
/**
* amount of restitution this object has
*
* @property restitution
* @type {Number}
* @default 0.1
*/
this.restitution = 0.1;
/**
* amount of friction this object has
*
* @property friction
* @type {Number}
* @default 0.5
*/
this.friction = 0.5;
/**
* the rigid body's custom gravity
*
* @property gravity
* @type {vec3}
* @default null
* @private
*/
this.gravity = null;
/**
* proportion of linear velocity lost per second ( 0.0 - 1.0 )
*
* @property linear_damping
* @type {Number}
*/
this.linear_damping = 0;
/**
* proportion of angular velocity lost per second ( 0.0 - 1.0 )
*
* @property angular_damping
* @type {Number}
*/
this.angular_damping = 0;
/**
* the world to which the rigid body has been added,
* this is set when the rigid body is added to a world
*
* @property world
* @type {Goblin.World}
* @default null
*/
this.world = null;
/**
* all resultant force accumulated by the rigid body
* this force is applied in the next occurring integration
*
* @property accumulated_force
* @type {vec3}
* @default [ 0, 0, 0 ]
* @private
*/
this.accumulated_force = new Goblin.Vector3();
/**
* All resultant torque accumulated by the rigid body
* this torque is applied in the next occurring integration
*
* @property accumulated_force
* @type {vec3}
* @default [ 0, 0, 0 ]
* @private
*/
this.accumulated_torque = new Goblin.Vector3();
// Used by the constraint solver to determine what impulse needs to be added to the body
this.push_velocity = new Goblin.Vector3();
this.turn_velocity = new Goblin.Vector3();
this.solver_impulse = new Float64Array( 6 );
// Set default derived values
this.updateDerived();
this.listeners = {};
};
})();
Goblin.EventEmitter.apply( Goblin.RigidBody );
/**
* Given `direction`, find the point in this body which is the most extreme in that direction.
* This support point is calculated in world coordinates and stored in the second parameter `support_point`
*
* @method findSupportPoint
* @param direction {vec3} direction to use in finding the support point
* @param support_point {vec3} vec3 variable which will contain the supporting point after calling this method
*/
Goblin.RigidBody.prototype.findSupportPoint = (function(){
var local_direction = new Goblin.Vector3();
return function( direction, support_point ) {
// Convert direction into local frame for the shape
this.transform_inverse.rotateVector3Into( direction, local_direction );
this.shape.findSupportPoint( local_direction, support_point );
// Convert from the shape's local coordinates to world coordinates
this.transform.transformVector3( support_point );
};
})();
/**
* Checks if a ray segment intersects with the object
*
* @method rayIntersect
* @property ray_start {vec3} start point of the segment
* @property ray_end {vec3{ end point of the segment
* @property intersection_list {Array} array to append intersection to
*/
Goblin.RigidBody.prototype.rayIntersect = (function(){
var local_start = new Goblin.Vector3(),
local_end = new Goblin.Vector3();
return function( ray_start, ray_end, intersection_list ) {
// transform start & end into local coordinates
this.transform_inverse.transformVector3Into( ray_start, local_start );
this.transform_inverse.transformVector3Into( ray_end, local_end );
// Intersect with shape
var intersection = this.shape.rayIntersect( local_start, local_end );
if ( intersection != null ) {
intersection.object = this; // change from the shape to the body
this.transform.transformVector3( intersection.point ); // transform shape's local coordinates to the body's world coordinates
// Rotate intersection normal
this.transform.rotateVector3( intersection.normal );
intersection_list.push( intersection );
}
};
})();
/**
* Updates the rigid body's position, velocity, and acceleration
*
* @method integrate
* @param timestep {Number} time, in seconds, to use in integration
*/
Goblin.RigidBody.prototype.integrate = function( timestep ) {
if ( this.mass === Infinity ) {
return;
}
var invmass = 1 / this.mass;
// Add accumulated linear force
_tmp_vec3_1.scaleVector( this.accumulated_force, invmass );
this.linear_velocity.add( _tmp_vec3_1 );
// Add accumulated angular force
this.inverseInertiaTensorWorldFrame.transformVector3Into( this.accumulated_torque, _tmp_vec3_1 );
this.angular_velocity.add( _tmp_vec3_1 );
// Apply damping
this.linear_velocity.scale( Math.pow( 1 - this.linear_damping, timestep ) );
this.angular_velocity.scale( Math.pow( 1 - this.angular_damping, timestep ) );
// Update position
_tmp_vec3_1.scaleVector( this.linear_velocity, timestep );
this.position.add( _tmp_vec3_1 );
// Update rotation
_tmp_quat4_1.x = this.angular_velocity.x * timestep;
_tmp_quat4_1.y = this.angular_velocity.y * timestep;
_tmp_quat4_1.z = this.angular_velocity.z * timestep;
_tmp_quat4_1.w = 0;
_tmp_quat4_1.multiply( this.rotation );
var half_dt = 0.5;
this.rotation.x += half_dt * _tmp_quat4_1.x;
this.rotation.y += half_dt * _tmp_quat4_1.y;
this.rotation.z += half_dt * _tmp_quat4_1.z;
this.rotation.w += half_dt * _tmp_quat4_1.w;
this.rotation.normalize();
// Clear accumulated forces
this.accumulated_force.x = this.accumulated_force.y = this.accumulated_force.z = 0;
this.accumulated_torque.x = this.accumulated_torque.y = this.accumulated_torque.z = 0;
this.solver_impulse[0] = this.solver_impulse[1] = this.solver_impulse[2] = this.solver_impulse[3] = this.solver_impulse[4] = this.solver_impulse[5] = 0;
this.push_velocity.x = this.push_velocity.y = this.push_velocity.z = 0;
this.turn_velocity.x = this.turn_velocity.y = this.turn_velocity.z = 0;
};
/**
* Sets a custom gravity value for this rigid_body
*
* @method setGravity
* @param x {Number} gravity to apply on x axis
* @param y {Number} gravity to apply on y axis
* @param z {Number} gravity to apply on z axis
*/
Goblin.RigidBody.prototype.setGravity = function( x, y, z ) {
if ( this.gravity ) {
this.gravity.x = x;
this.gravity.y = y;
this.gravity.z = z;
} else {
this.gravity = new Goblin.Vector3( x, y, z );
}
};
/**
* Directly adds linear velocity to the body
*
* @method applyImpulse
* @param impulse {vec3} linear velocity to add to the body
*/
Goblin.RigidBody.prototype.applyImpulse = function( impulse ) {
this.linear_velocity.add( impulse );
};
/**
* Adds a force to the rigid_body which will be used only for the next integration
*
* @method applyForce
* @param force {vec3} force to apply to the rigid_body
*/
Goblin.RigidBody.prototype.applyForce = function( force ) {
this.accumulated_force.add( force );
};
/**
* Applies the vector `force` at world coordinate `point`
*
* @method applyForceAtWorldPoint
* @param force {vec3} Force to apply
* @param point {vec3} world coordinates where force originates
*/
Goblin.RigidBody.prototype.applyForceAtWorldPoint = function( force, point ) {
_tmp_vec3_1.copy( point );
_tmp_vec3_1.subtract( this.position );
_tmp_vec3_1.cross( force );
this.accumulated_force.add( force );
this.accumulated_torque.add( _tmp_vec3_1 );
};
/**
* Applies vector `force` to body at position `point` in body's frame
*
* @method applyForceAtLocalPoint
* @param force {vec3} Force to apply
* @param point {vec3} local frame coordinates where force originates
*/
Goblin.RigidBody.prototype.applyForceAtLocalPoint = function( force, point ) {
this.transform.transformVector3Into( point, _tmp_vec3_1 );
this.applyForceAtWorldPoint( force, _vec3 );
};
Goblin.RigidBody.prototype.getVelocityInLocalPoint = function( point, out ) {
if ( this.mass === Infinity ) {
out.set( 0, 0, 0 );
} else {
out.copy( this.angular_velocity );
out.cross( point );
out.add( this.linear_velocity );
}
};
/**
* Sets the rigid body's transformation matrix to the current position and rotation
*
* @method updateDerived
*/
Goblin.RigidBody.prototype.updateDerived = function() {
// normalize rotation
this.rotation.normalize();
// update this.transform and this.transform_inverse
this.transform.makeTransform( this.rotation, this.position );
this.transform.invertInto( this.transform_inverse );
// Update the world frame inertia tensor and inverse
if ( this.mass !== Infinity ) {
_tmp_mat3_1.fromMatrix4( this.transform_inverse );
_tmp_mat3_1.transposeInto( _tmp_mat3_2 );
_tmp_mat3_2.multiply( this.inertiaTensor );
this.inertiaTensorWorldFrame.multiplyFrom( _tmp_mat3_2, _tmp_mat3_1 );
this.inertiaTensorWorldFrame.invertInto( this.inverseInertiaTensorWorldFrame );
}
// Update AABB
this.aabb.transform( this.shape.aabb, this.transform );
};