summaryrefslogtreecommitdiff
path: root/physi_js
diff options
context:
space:
mode:
Diffstat (limited to 'physi_js')
-rw-r--r--physi_js/physi.js1403
-rw-r--r--physi_js/physijs.d.ts250
-rw-r--r--physi_js/physijs_worker.js1415
3 files changed, 3068 insertions, 0 deletions
diff --git a/physi_js/physi.js b/physi_js/physi.js
new file mode 100644
index 0000000..9d5b716
--- /dev/null
+++ b/physi_js/physi.js
@@ -0,0 +1,1403 @@
+window.Physijs = (function() {
+ 'use strict';
+
+ var SUPPORT_TRANSFERABLE,
+ _is_simulating = false,
+ _Physijs = Physijs, // used for noConflict method
+ Physijs = {}, // object assigned to window.Physijs
+ Eventable, // class to provide simple event methods
+ getObjectId, // returns a unique ID for a Physijs mesh object
+ getEulerXYZFromQuaternion, getQuatertionFromEuler,
+ convertWorldPositionToObject, // Converts a world-space position to object-space
+ addObjectChildren,
+
+ _temp1, _temp2,
+ _temp_vector3_1 = new THREE.Vector3,
+ _temp_vector3_2 = new THREE.Vector3,
+ _temp_matrix4_1 = new THREE.Matrix4,
+ _quaternion_1 = new THREE.Quaternion,
+
+ // constants
+ MESSAGE_TYPES = {
+ WORLDREPORT: 0,
+ COLLISIONREPORT: 1,
+ VEHICLEREPORT: 2,
+ CONSTRAINTREPORT: 3
+ },
+ REPORT_ITEMSIZE = 14,
+ COLLISIONREPORT_ITEMSIZE = 5,
+ VEHICLEREPORT_ITEMSIZE = 9,
+ CONSTRAINTREPORT_ITEMSIZE = 6;
+
+ Physijs.scripts = {};
+
+ Eventable = function() {
+ this._eventListeners = {};
+ };
+ Eventable.prototype.addEventListener = function( event_name, callback ) {
+ if ( !this._eventListeners.hasOwnProperty( event_name ) ) {
+ this._eventListeners[event_name] = [];
+ }
+ this._eventListeners[event_name].push( callback );
+ };
+ Eventable.prototype.removeEventListener = function( event_name, callback ) {
+ var index;
+
+ if ( !this._eventListeners.hasOwnProperty( event_name ) ) return false;
+
+ if ( (index = this._eventListeners[event_name].indexOf( callback )) >= 0 ) {
+ this._eventListeners[event_name].splice( index, 1 );
+ return true;
+ }
+
+ return false;
+ };
+ Eventable.prototype.dispatchEvent = function( event_name ) {
+ var i,
+ parameters = Array.prototype.splice.call( arguments, 1 );
+
+ if ( this._eventListeners.hasOwnProperty( event_name ) ) {
+ for ( i = 0; i < this._eventListeners[event_name].length; i++ ) {
+ this._eventListeners[event_name][i].apply( this, parameters );
+ }
+ }
+ };
+ Eventable.make = function( obj ) {
+ obj.prototype.addEventListener = Eventable.prototype.addEventListener;
+ obj.prototype.removeEventListener = Eventable.prototype.removeEventListener;
+ obj.prototype.dispatchEvent = Eventable.prototype.dispatchEvent;
+ };
+
+ getObjectId = (function() {
+ var _id = 1;
+ return function() {
+ return _id++;
+ };
+ })();
+
+ getEulerXYZFromQuaternion = function ( x, y, z, w ) {
+ return new THREE.Vector3(
+ Math.atan2( 2 * ( x * w - y * z ), ( w * w - x * x - y * y + z * z ) ),
+ Math.asin( 2 * ( x * z + y * w ) ),
+ Math.atan2( 2 * ( z * w - x * y ), ( w * w + x * x - y * y - z * z ) )
+ );
+ };
+
+ getQuatertionFromEuler = function( x, y, z ) {
+ var c1, s1, c2, s2, c3, s3, c1c2, s1s2;
+ c1 = Math.cos( y );
+ s1 = Math.sin( y );
+ c2 = Math.cos( -z );
+ s2 = Math.sin( -z );
+ c3 = Math.cos( x );
+ s3 = Math.sin( x );
+
+ c1c2 = c1 * c2;
+ s1s2 = s1 * s2;
+
+ return {
+ w: c1c2 * c3 - s1s2 * s3,
+ x: c1c2 * s3 + s1s2 * c3,
+ y: s1 * c2 * c3 + c1 * s2 * s3,
+ z: c1 * s2 * c3 - s1 * c2 * s3
+ };
+ };
+
+ convertWorldPositionToObject = function( position, object ) {
+ _temp_matrix4_1.identity(); // reset temp matrix
+
+ // Set the temp matrix's rotation to the object's rotation
+ _temp_matrix4_1.identity().makeRotationFromQuaternion( object.quaternion );
+
+ // Invert rotation matrix in order to "unrotate" a point back to object space
+ _temp_matrix4_1.getInverse( _temp_matrix4_1 );
+
+ // Yay! Temp vars!
+ _temp_vector3_1.copy( position );
+ _temp_vector3_2.copy( object.position );
+
+ // Apply the rotation
+
+ return _temp_vector3_1.sub( _temp_vector3_2 ).applyMatrix4( _temp_matrix4_1 );
+ };
+
+
+
+ // Physijs.noConflict
+ Physijs.noConflict = function() {
+ window.Physijs = _Physijs;
+ return Physijs;
+ };
+
+
+ // Physijs.createMaterial
+ Physijs.createMaterial = function( material, friction, restitution ) {
+ var physijs_material = function(){};
+ physijs_material.prototype = material;
+ physijs_material = new physijs_material;
+
+ physijs_material._physijs = {
+ id: material.id,
+ friction: friction === undefined ? .8 : friction,
+ restitution: restitution === undefined ? .2 : restitution
+ };
+
+ return physijs_material;
+ };
+
+
+ // Constraints
+ Physijs.PointConstraint = function( objecta, objectb, position ) {
+ if ( position === undefined ) {
+ position = objectb;
+ objectb = undefined;
+ }
+
+ this.type = 'point';
+ this.appliedImpulse = 0;
+ this.id = getObjectId();
+ this.objecta = objecta._physijs.id;
+ this.positiona = convertWorldPositionToObject( position, objecta ).clone();
+
+ if ( objectb ) {
+ this.objectb = objectb._physijs.id;
+ this.positionb = convertWorldPositionToObject( position, objectb ).clone();
+ }
+ };
+ Physijs.PointConstraint.prototype.getDefinition = function() {
+ return {
+ type: this.type,
+ id: this.id,
+ objecta: this.objecta,
+ objectb: this.objectb,
+ positiona: this.positiona,
+ positionb: this.positionb
+ };
+ };
+
+ Physijs.HingeConstraint = function( objecta, objectb, position, axis ) {
+ if ( axis === undefined ) {
+ axis = position;
+ position = objectb;
+ objectb = undefined;
+ }
+
+ this.type = 'hinge';
+ this.appliedImpulse = 0;
+ this.id = getObjectId();
+ this.scene = objecta.parent;
+ this.objecta = objecta._physijs.id;
+ this.positiona = convertWorldPositionToObject( position, objecta ).clone();
+ this.position = position.clone();
+ this.axis = axis;
+
+ if ( objectb ) {
+ this.objectb = objectb._physijs.id;
+ this.positionb = convertWorldPositionToObject( position, objectb ).clone();
+ }
+ };
+ Physijs.HingeConstraint.prototype.getDefinition = function() {
+ return {
+ type: this.type,
+ id: this.id,
+ objecta: this.objecta,
+ objectb: this.objectb,
+ positiona: this.positiona,
+ positionb: this.positionb,
+ axis: this.axis
+ };
+ };
+ /*
+ * low = minimum angle in radians
+ * high = maximum angle in radians
+ * bias_factor = applied as a factor to constraint error
+ * relaxation_factor = controls bounce (0.0 == no bounce)
+ */
+ Physijs.HingeConstraint.prototype.setLimits = function( low, high, bias_factor, relaxation_factor ) {
+ this.scene.execute( 'hinge_setLimits', { constraint: this.id, low: low, high: high, bias_factor: bias_factor, relaxation_factor: relaxation_factor } );
+ };
+ Physijs.HingeConstraint.prototype.enableAngularMotor = function( velocity, acceleration ) {
+ this.scene.execute( 'hinge_enableAngularMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } );
+ };
+ Physijs.HingeConstraint.prototype.disableMotor = function( velocity, acceleration ) {
+ this.scene.execute( 'hinge_disableMotor', { constraint: this.id } );
+ };
+
+ Physijs.SliderConstraint = function( objecta, objectb, position, axis ) {
+ if ( axis === undefined ) {
+ axis = position;
+ position = objectb;
+ objectb = undefined;
+ }
+
+ this.type = 'slider';
+ this.appliedImpulse = 0;
+ this.id = getObjectId();
+ this.scene = objecta.parent;
+ this.objecta = objecta._physijs.id;
+ this.positiona = convertWorldPositionToObject( position, objecta ).clone();
+ this.axis = axis;
+
+ if ( objectb ) {
+ this.objectb = objectb._physijs.id;
+ this.positionb = convertWorldPositionToObject( position, objectb ).clone();
+ }
+ };
+ Physijs.SliderConstraint.prototype.getDefinition = function() {
+ return {
+ type: this.type,
+ id: this.id,
+ objecta: this.objecta,
+ objectb: this.objectb,
+ positiona: this.positiona,
+ positionb: this.positionb,
+ axis: this.axis
+ };
+ };
+ Physijs.SliderConstraint.prototype.setLimits = function( lin_lower, lin_upper, ang_lower, ang_upper ) {
+ this.scene.execute( 'slider_setLimits', { constraint: this.id, lin_lower: lin_lower, lin_upper: lin_upper, ang_lower: ang_lower, ang_upper: ang_upper } );
+ };
+ Physijs.SliderConstraint.prototype.setRestitution = function( linear, angular ) {
+ this.scene.execute(
+ 'slider_setRestitution',
+ {
+ constraint: this.id,
+ linear: linear,
+ angular: angular
+ }
+ );
+ };
+ Physijs.SliderConstraint.prototype.enableLinearMotor = function( velocity, acceleration) {
+ this.scene.execute( 'slider_enableLinearMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } );
+ };
+ Physijs.SliderConstraint.prototype.disableLinearMotor = function() {
+ this.scene.execute( 'slider_disableLinearMotor', { constraint: this.id } );
+ };
+ Physijs.SliderConstraint.prototype.enableAngularMotor = function( velocity, acceleration ) {
+ this.scene.execute( 'slider_enableAngularMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } );
+ };
+ Physijs.SliderConstraint.prototype.disableAngularMotor = function() {
+ this.scene.execute( 'slider_disableAngularMotor', { constraint: this.id } );
+ };
+
+ Physijs.ConeTwistConstraint = function( objecta, objectb, position ) {
+ if ( position === undefined ) {
+ throw 'Both objects must be defined in a ConeTwistConstraint.';
+ }
+ this.type = 'conetwist';
+ this.appliedImpulse = 0;
+ this.id = getObjectId();
+ this.scene = objecta.parent;
+ this.objecta = objecta._physijs.id;
+ this.positiona = convertWorldPositionToObject( position, objecta ).clone();
+ this.objectb = objectb._physijs.id;
+ this.positionb = convertWorldPositionToObject( position, objectb ).clone();
+ this.axisa = { x: objecta.rotation.x, y: objecta.rotation.y, z: objecta.rotation.z };
+ this.axisb = { x: objectb.rotation.x, y: objectb.rotation.y, z: objectb.rotation.z };
+ };
+ Physijs.ConeTwistConstraint.prototype.getDefinition = function() {
+ return {
+ type: this.type,
+ id: this.id,
+ objecta: this.objecta,
+ objectb: this.objectb,
+ positiona: this.positiona,
+ positionb: this.positionb,
+ axisa: this.axisa,
+ axisb: this.axisb
+ };
+ };
+ Physijs.ConeTwistConstraint.prototype.setLimit = function( x, y, z ) {
+ this.scene.execute( 'conetwist_setLimit', { constraint: this.id, x: x, y: y, z: z } );
+ };
+ Physijs.ConeTwistConstraint.prototype.enableMotor = function() {
+ this.scene.execute( 'conetwist_enableMotor', { constraint: this.id } );
+ };
+ Physijs.ConeTwistConstraint.prototype.setMaxMotorImpulse = function( max_impulse ) {
+ this.scene.execute( 'conetwist_setMaxMotorImpulse', { constraint: this.id, max_impulse: max_impulse } );
+ };
+ Physijs.ConeTwistConstraint.prototype.setMotorTarget = function( target ) {
+ if ( target instanceof THREE.Vector3 ) {
+ target = new THREE.Quaternion().setFromEuler( new THREE.Euler( target.x, target.y, target.z ) );
+ } else if ( target instanceof THREE.Euler ) {
+ target = new THREE.Quaternion().setFromEuler( target );
+ } else if ( target instanceof THREE.Matrix4 ) {
+ target = new THREE.Quaternion().setFromRotationMatrix( target );
+ }
+ this.scene.execute( 'conetwist_setMotorTarget', { constraint: this.id, x: target.x, y: target.y, z: target.z, w: target.w } );
+ };
+ Physijs.ConeTwistConstraint.prototype.disableMotor = function() {
+ this.scene.execute( 'conetwist_disableMotor', { constraint: this.id } );
+ };
+
+ Physijs.DOFConstraint = function( objecta, objectb, position ) {
+ if ( position === undefined ) {
+ position = objectb;
+ objectb = undefined;
+ }
+ this.type = 'dof';
+ this.appliedImpulse = 0;
+ this.id = getObjectId();
+ this.scene = objecta.parent;
+ this.objecta = objecta._physijs.id;
+ this.positiona = convertWorldPositionToObject( position, objecta ).clone();
+ this.axisa = { x: objecta.rotation.x, y: objecta.rotation.y, z: objecta.rotation.z };
+
+ if ( objectb ) {
+ this.objectb = objectb._physijs.id;
+ this.positionb = convertWorldPositionToObject( position, objectb ).clone();
+ this.axisb = { x: objectb.rotation.x, y: objectb.rotation.y, z: objectb.rotation.z };
+ }
+ };
+ Physijs.DOFConstraint.prototype.getDefinition = function() {
+ return {
+ type: this.type,
+ id: this.id,
+ objecta: this.objecta,
+ objectb: this.objectb,
+ positiona: this.positiona,
+ positionb: this.positionb,
+ axisa: this.axisa,
+ axisb: this.axisb
+ };
+ };
+ Physijs.DOFConstraint.prototype.setLinearLowerLimit = function( limit ) {
+ this.scene.execute( 'dof_setLinearLowerLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } );
+ };
+ Physijs.DOFConstraint.prototype.setLinearUpperLimit = function( limit ) {
+ this.scene.execute( 'dof_setLinearUpperLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } );
+ };
+ Physijs.DOFConstraint.prototype.setAngularLowerLimit = function( limit ) {
+ this.scene.execute( 'dof_setAngularLowerLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } );
+ };
+ Physijs.DOFConstraint.prototype.setAngularUpperLimit = function( limit ) {
+ this.scene.execute( 'dof_setAngularUpperLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } );
+ };
+ Physijs.DOFConstraint.prototype.enableAngularMotor = function( which ) {
+ this.scene.execute( 'dof_enableAngularMotor', { constraint: this.id, which: which } );
+ };
+ Physijs.DOFConstraint.prototype.configureAngularMotor = function( which, low_angle, high_angle, velocity, max_force ) {
+ this.scene.execute( 'dof_configureAngularMotor', { constraint: this.id, which: which, low_angle: low_angle, high_angle: high_angle, velocity: velocity, max_force: max_force } );
+ };
+ Physijs.DOFConstraint.prototype.disableAngularMotor = function( which ) {
+ this.scene.execute( 'dof_disableAngularMotor', { constraint: this.id, which: which } );
+ };
+
+ // Physijs.Scene
+ Physijs.Scene = function( params ) {
+ var self = this;
+
+ Eventable.call( this );
+ THREE.Scene.call( this );
+
+ this._worker = new Worker( Physijs.scripts.worker || 'physijs_worker.js' );
+ this._worker.transferableMessage = this._worker.webkitPostMessage || this._worker.postMessage;
+ this._materials_ref_counts = {};
+ this._objects = {};
+ this._vehicles = {};
+ this._constraints = {};
+
+ var ab = new ArrayBuffer( 1 );
+ this._worker.transferableMessage( ab, [ab] );
+ SUPPORT_TRANSFERABLE = ( ab.byteLength === 0 );
+
+ this._worker.onmessage = function ( event ) {
+ var _temp,
+ data = event.data;
+
+ if ( data instanceof ArrayBuffer && data.byteLength !== 1 ) { // byteLength === 1 is the worker making a SUPPORT_TRANSFERABLE test
+ data = new Float32Array( data );
+ }
+
+ if ( data instanceof Float32Array ) {
+
+ // transferable object
+ switch ( data[0] ) {
+ case MESSAGE_TYPES.WORLDREPORT:
+ self._updateScene( data );
+ break;
+
+ case MESSAGE_TYPES.COLLISIONREPORT:
+ self._updateCollisions( data );
+ break;
+
+ case MESSAGE_TYPES.VEHICLEREPORT:
+ self._updateVehicles( data );
+ break;
+
+ case MESSAGE_TYPES.CONSTRAINTREPORT:
+ self._updateConstraints( data );
+ break;
+ }
+
+ } else {
+
+ if ( data.cmd ) {
+
+ // non-transferable object
+ switch ( data.cmd ) {
+ case 'objectReady':
+ _temp = data.params;
+ if ( self._objects[ _temp ] ) {
+ self._objects[ _temp ].dispatchEvent( 'ready' );
+ }
+ break;
+
+ case 'worldReady':
+ self.dispatchEvent( 'ready' );
+ break;
+
+ case 'vehicle':
+ window.test = data;
+ break;
+
+ default:
+ // Do nothing, just show the message
+ console.debug('Received: ' + data.cmd);
+ console.dir(data.params);
+ break;
+ }
+
+ } else {
+
+ switch ( data[0] ) {
+ case MESSAGE_TYPES.WORLDREPORT:
+ self._updateScene( data );
+ break;
+
+ case MESSAGE_TYPES.COLLISIONREPORT:
+ self._updateCollisions( data );
+ break;
+
+ case MESSAGE_TYPES.VEHICLEREPORT:
+ self._updateVehicles( data );
+ break;
+
+ case MESSAGE_TYPES.CONSTRAINTREPORT:
+ self._updateConstraints( data );
+ break;
+ }
+
+ }
+
+ }
+ };
+
+
+ params = params || {};
+ params.ammo = Physijs.scripts.ammo || 'ammo.js';
+ params.fixedTimeStep = params.fixedTimeStep || 1 / 60;
+ params.rateLimit = params.rateLimit || true;
+ this.execute( 'init', params );
+ };
+ Physijs.Scene.prototype = new THREE.Scene;
+ Physijs.Scene.prototype.constructor = Physijs.Scene;
+ Eventable.make( Physijs.Scene );
+
+ Physijs.Scene.prototype._updateScene = function( data ) {
+ var num_objects = data[1],
+ object,
+ i, offset;
+
+ for ( i = 0; i < num_objects; i++ ) {
+ offset = 2 + i * REPORT_ITEMSIZE;
+ object = this._objects[ data[ offset ] ];
+
+ if ( object === undefined ) {
+ continue;
+ }
+
+ if ( object.__dirtyPosition === false ) {
+ object.position.set(
+ data[ offset + 1 ],
+ data[ offset + 2 ],
+ data[ offset + 3 ]
+ );
+ }
+
+ if ( object.__dirtyRotation === false ) {
+ object.quaternion.set(
+ data[ offset + 4 ],
+ data[ offset + 5 ],
+ data[ offset + 6 ],
+ data[ offset + 7 ]
+ );
+ }
+
+ object._physijs.linearVelocity.set(
+ data[ offset + 8 ],
+ data[ offset + 9 ],
+ data[ offset + 10 ]
+ );
+
+ object._physijs.angularVelocity.set(
+ data[ offset + 11 ],
+ data[ offset + 12 ],
+ data[ offset + 13 ]
+ );
+
+ }
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ // Give the typed array back to the worker
+ this._worker.transferableMessage( data.buffer, [data.buffer] );
+ }
+
+ _is_simulating = false;
+ this.dispatchEvent( 'update' );
+ };
+
+ Physijs.Scene.prototype._updateVehicles = function( data ) {
+ var vehicle, wheel,
+ i, offset;
+
+ for ( i = 0; i < ( data.length - 1 ) / VEHICLEREPORT_ITEMSIZE; i++ ) {
+ offset = 1 + i * VEHICLEREPORT_ITEMSIZE;
+ vehicle = this._vehicles[ data[ offset ] ];
+
+ if ( vehicle === undefined ) {
+ continue;
+ }
+
+ wheel = vehicle.wheels[ data[ offset + 1 ] ];
+
+ wheel.position.set(
+ data[ offset + 2 ],
+ data[ offset + 3 ],
+ data[ offset + 4 ]
+ );
+
+ wheel.quaternion.set(
+ data[ offset + 5 ],
+ data[ offset + 6 ],
+ data[ offset + 7 ],
+ data[ offset + 8 ]
+ );
+ }
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ // Give the typed array back to the worker
+ this._worker.transferableMessage( data.buffer, [data.buffer] );
+ }
+ };
+
+ Physijs.Scene.prototype._updateConstraints = function( data ) {
+ var constraint, object,
+ i, offset;
+
+ for ( i = 0; i < ( data.length - 1 ) / CONSTRAINTREPORT_ITEMSIZE; i++ ) {
+ offset = 1 + i * CONSTRAINTREPORT_ITEMSIZE;
+ constraint = this._constraints[ data[ offset ] ];
+ object = this._objects[ data[ offset + 1 ] ];
+
+ if ( constraint === undefined || object === undefined ) {
+ continue;
+ }
+
+ _temp_vector3_1.set(
+ data[ offset + 2 ],
+ data[ offset + 3 ],
+ data[ offset + 4 ]
+ );
+ _temp_matrix4_1.extractRotation( object.matrix );
+ _temp_vector3_1.applyMatrix4( _temp_matrix4_1 );
+
+ constraint.positiona.addVectors( object.position, _temp_vector3_1 );
+ constraint.appliedImpulse = data[ offset + 5 ] ;
+ }
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ // Give the typed array back to the worker
+ this._worker.transferableMessage( data.buffer, [data.buffer] );
+ }
+ };
+
+ Physijs.Scene.prototype._updateCollisions = function( data ) {
+ /**
+ * #TODO
+ * This is probably the worst way ever to handle collisions. The inherent evilness is a residual
+ * effect from the previous version's evilness which mutated when switching to transferable objects.
+ *
+ * If you feel inclined to make this better, please do so.
+ */
+
+ var i, j, offset, object, object2, id1, id2,
+ collisions = {}, normal_offsets = {};
+
+ // Build collision manifest
+ for ( i = 0; i < data[1]; i++ ) {
+ offset = 2 + i * COLLISIONREPORT_ITEMSIZE;
+ object = data[ offset ];
+ object2 = data[ offset + 1 ];
+
+ normal_offsets[ object + '-' + object2 ] = offset + 2;
+ normal_offsets[ object2 + '-' + object ] = -1 * ( offset + 2 );
+
+ // Register collisions for both the object colliding and the object being collided with
+ if ( !collisions[ object ] ) collisions[ object ] = [];
+ collisions[ object ].push( object2 );
+
+ if ( !collisions[ object2 ] ) collisions[ object2 ] = [];
+ collisions[ object2 ].push( object );
+ }
+
+ // Deal with collisions
+ for ( id1 in this._objects ) {
+ if ( !this._objects.hasOwnProperty( id1 ) ) continue;
+ object = this._objects[ id1 ];
+
+ // If object touches anything, ...
+ if ( collisions[ id1 ] ) {
+
+ // Clean up touches array
+ for ( j = 0; j < object._physijs.touches.length; j++ ) {
+ if ( collisions[ id1 ].indexOf( object._physijs.touches[j] ) === -1 ) {
+ object._physijs.touches.splice( j--, 1 );
+ }
+ }
+
+ // Handle each colliding object
+ for ( j = 0; j < collisions[ id1 ].length; j++ ) {
+ id2 = collisions[ id1 ][ j ];
+ object2 = this._objects[ id2 ];
+
+ if ( object2 ) {
+ // If object was not already touching object2, notify object
+ if ( object._physijs.touches.indexOf( id2 ) === -1 ) {
+ object._physijs.touches.push( id2 );
+
+ _temp_vector3_1.subVectors( object.getLinearVelocity(), object2.getLinearVelocity() );
+ _temp1 = _temp_vector3_1.clone();
+
+ _temp_vector3_1.subVectors( object.getAngularVelocity(), object2.getAngularVelocity() );
+ _temp2 = _temp_vector3_1.clone();
+
+ var normal_offset = normal_offsets[ object._physijs.id + '-' + object2._physijs.id ];
+ if ( normal_offset > 0 ) {
+ _temp_vector3_1.set(
+ -data[ normal_offset ],
+ -data[ normal_offset + 1 ],
+ -data[ normal_offset + 2 ]
+ );
+ } else {
+ normal_offset *= -1;
+ _temp_vector3_1.set(
+ data[ normal_offset ],
+ data[ normal_offset + 1 ],
+ data[ normal_offset + 2 ]
+ );
+ }
+
+ object.dispatchEvent( 'collision', object2, _temp1, _temp2, _temp_vector3_1 );
+ }
+ }
+ }
+
+ } else {
+
+ // not touching other objects
+ object._physijs.touches.length = 0;
+
+ }
+
+ }
+
+ this.collisions = collisions;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ // Give the typed array back to the worker
+ this._worker.transferableMessage( data.buffer, [data.buffer] );
+ }
+ };
+
+ Physijs.Scene.prototype.addConstraint = function ( constraint, show_marker ) {
+ this._constraints[ constraint.id ] = constraint;
+ this.execute( 'addConstraint', constraint.getDefinition() );
+
+ if ( show_marker ) {
+ var marker;
+
+ switch ( constraint.type ) {
+ case 'point':
+ marker = new THREE.Mesh(
+ new THREE.SphereGeometry( 1.5 ),
+ new THREE.MeshNormalMaterial
+ );
+ marker.position.copy( constraint.positiona );
+ this._objects[ constraint.objecta ].add( marker );
+ break;
+
+ case 'hinge':
+ marker = new THREE.Mesh(
+ new THREE.SphereGeometry( 1.5 ),
+ new THREE.MeshNormalMaterial
+ );
+ marker.position.copy( constraint.positiona );
+ this._objects[ constraint.objecta ].add( marker );
+ break;
+
+ case 'slider':
+ marker = new THREE.Mesh(
+ new THREE.BoxGeometry( 10, 1, 1 ),
+ new THREE.MeshNormalMaterial
+ );
+ marker.position.copy( constraint.positiona );
+ // This rotation isn't right if all three axis are non-0 values
+ // TODO: change marker's rotation order to ZYX
+ marker.rotation.set(
+ constraint.axis.y, // yes, y and
+ constraint.axis.x, // x axis are swapped
+ constraint.axis.z
+ );
+ this._objects[ constraint.objecta ].add( marker );
+ break;
+
+ case 'conetwist':
+ marker = new THREE.Mesh(
+ new THREE.SphereGeometry( 1.5 ),
+ new THREE.MeshNormalMaterial
+ );
+ marker.position.copy( constraint.positiona );
+ this._objects[ constraint.objecta ].add( marker );
+ break;
+
+ case 'dof':
+ marker = new THREE.Mesh(
+ new THREE.SphereGeometry( 1.5 ),
+ new THREE.MeshNormalMaterial
+ );
+ marker.position.copy( constraint.positiona );
+ this._objects[ constraint.objecta ].add( marker );
+ break;
+ }
+ }
+
+ return constraint;
+ };
+
+ Physijs.Scene.prototype.onSimulationResume = function() {
+ this.execute( 'onSimulationResume', { } );
+ };
+
+ Physijs.Scene.prototype.removeConstraint = function( constraint ) {
+ if ( this._constraints[constraint.id ] !== undefined ) {
+ this.execute( 'removeConstraint', { id: constraint.id } );
+ delete this._constraints[ constraint.id ];
+ }
+ };
+
+ Physijs.Scene.prototype.execute = function( cmd, params ) {
+ this._worker.postMessage({ cmd: cmd, params: params });
+ };
+
+ addObjectChildren = function( parent, object ) {
+ var i;
+
+ for ( i = 0; i < object.children.length; i++ ) {
+ if ( object.children[i]._physijs ) {
+ object.children[i].updateMatrix();
+ object.children[i].updateMatrixWorld();
+
+ _temp_vector3_1.setFromMatrixPosition( object.children[i].matrixWorld );
+ _quaternion_1.setFromRotationMatrix( object.children[i].matrixWorld );
+
+ object.children[i]._physijs.position_offset = {
+ x: _temp_vector3_1.x,
+ y: _temp_vector3_1.y,
+ z: _temp_vector3_1.z
+ };
+
+ object.children[i]._physijs.rotation = {
+ x: _quaternion_1.x,
+ y: _quaternion_1.y,
+ z: _quaternion_1.z,
+ w: _quaternion_1.w
+ };
+
+ parent._physijs.children.push( object.children[i]._physijs );
+ }
+
+ addObjectChildren( parent, object.children[i] );
+ }
+ };
+
+ Physijs.Scene.prototype.add = function( object ) {
+ THREE.Mesh.prototype.add.call( this, object );
+
+ if ( object._physijs ) {
+
+ object.world = this;
+
+ if ( object instanceof Physijs.Vehicle ) {
+
+ this.add( object.mesh );
+ this._vehicles[ object._physijs.id ] = object;
+ this.execute( 'addVehicle', object._physijs );
+
+ } else {
+
+ object.__dirtyPosition = false;
+ object.__dirtyRotation = false;
+ this._objects[object._physijs.id] = object;
+
+ if ( object.children.length ) {
+ object._physijs.children = [];
+ addObjectChildren( object, object );
+ }
+
+ if ( object.material._physijs ) {
+ if ( !this._materials_ref_counts.hasOwnProperty( object.material._physijs.id ) ) {
+ this.execute( 'registerMaterial', object.material._physijs );
+ object._physijs.materialId = object.material._physijs.id;
+ this._materials_ref_counts[object.material._physijs.id] = 1;
+ } else {
+ this._materials_ref_counts[object.material._physijs.id]++;
+ }
+ }
+
+ // Object starting position + rotation
+ object._physijs.position = { x: object.position.x, y: object.position.y, z: object.position.z };
+ object._physijs.rotation = { x: object.quaternion.x, y: object.quaternion.y, z: object.quaternion.z, w: object.quaternion.w };
+
+ // Check for scaling
+ var mass_scaling = new THREE.Vector3( 1, 1, 1 );
+ if ( object._physijs.width ) {
+ object._physijs.width *= object.scale.x;
+ }
+ if ( object._physijs.height ) {
+ object._physijs.height *= object.scale.y;
+ }
+ if ( object._physijs.depth ) {
+ object._physijs.depth *= object.scale.z;
+ }
+
+ this.execute( 'addObject', object._physijs );
+
+ }
+ }
+ };
+
+ Physijs.Scene.prototype.remove = function( object ) {
+ if ( object instanceof Physijs.Vehicle ) {
+ this.execute( 'removeVehicle', { id: object._physijs.id } );
+ while( object.wheels.length ) {
+ this.remove( object.wheels.pop() );
+ }
+ this.remove( object.mesh );
+ delete this._vehicles[ object._physijs.id ];
+ } else {
+ THREE.Mesh.prototype.remove.call( this, object );
+ if ( object._physijs ) {
+ delete this._objects[object._physijs.id];
+ this.execute( 'removeObject', { id: object._physijs.id } );
+ }
+ }
+ if ( object.material && object.material._physijs && this._materials_ref_counts.hasOwnProperty( object.material._physijs.id ) ) {
+ this._materials_ref_counts[object.material._physijs.id]--;
+ if(this._materials_ref_counts[object.material._physijs.id] == 0) {
+ this.execute( 'unRegisterMaterial', object.material._physijs );
+ delete this._materials_ref_counts[object.material._physijs.id];
+ }
+ }
+ };
+
+ Physijs.Scene.prototype.setFixedTimeStep = function( fixedTimeStep ) {
+ if ( fixedTimeStep ) {
+ this.execute( 'setFixedTimeStep', fixedTimeStep );
+ }
+ };
+
+ Physijs.Scene.prototype.setGravity = function( gravity ) {
+ if ( gravity ) {
+ this.execute( 'setGravity', gravity );
+ }
+ };
+
+ Physijs.Scene.prototype.simulate = function( timeStep, maxSubSteps ) {
+ var object_id, object, update;
+
+ if ( _is_simulating ) {
+ return false;
+ }
+
+ _is_simulating = true;
+
+ for ( object_id in this._objects ) {
+ if ( !this._objects.hasOwnProperty( object_id ) ) continue;
+
+ object = this._objects[object_id];
+
+ if ( object.__dirtyPosition || object.__dirtyRotation ) {
+ update = { id: object._physijs.id };
+
+ if ( object.__dirtyPosition ) {
+ update.pos = { x: object.position.x, y: object.position.y, z: object.position.z };
+ object.__dirtyPosition = false;
+ }
+
+ if ( object.__dirtyRotation ) {
+ update.quat = { x: object.quaternion.x, y: object.quaternion.y, z: object.quaternion.z, w: object.quaternion.w };
+ object.__dirtyRotation = false;
+ }
+
+ this.execute( 'updateTransform', update );
+ }
+ }
+
+ this.execute( 'simulate', { timeStep: timeStep, maxSubSteps: maxSubSteps } );
+
+ return true;
+ };
+
+
+ // Phsijs.Mesh
+ Physijs.Mesh = function ( geometry, material, mass ) {
+ var index;
+
+ if ( !geometry ) {
+ return;
+ }
+
+ Eventable.call( this );
+ THREE.Mesh.call( this, geometry, material );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ this._physijs = {
+ type: null,
+ id: getObjectId(),
+ mass: mass || 0,
+ touches: [],
+ linearVelocity: new THREE.Vector3,
+ angularVelocity: new THREE.Vector3
+ };
+ };
+ Physijs.Mesh.prototype = new THREE.Mesh;
+ Physijs.Mesh.prototype.constructor = Physijs.Mesh;
+ Eventable.make( Physijs.Mesh );
+
+ // Physijs.Mesh.mass
+ Physijs.Mesh.prototype.__defineGetter__('mass', function() {
+ return this._physijs.mass;
+ });
+ Physijs.Mesh.prototype.__defineSetter__('mass', function( mass ) {
+ this._physijs.mass = mass;
+ if ( this.world ) {
+ this.world.execute( 'updateMass', { id: this._physijs.id, mass: mass } );
+ }
+ });
+
+ // Physijs.Mesh.applyCentralImpulse
+ Physijs.Mesh.prototype.applyCentralImpulse = function ( force ) {
+ if ( this.world ) {
+ this.world.execute( 'applyCentralImpulse', { id: this._physijs.id, x: force.x, y: force.y, z: force.z } );
+ }
+ };
+
+ // Physijs.Mesh.applyImpulse
+ Physijs.Mesh.prototype.applyImpulse = function ( force, offset ) {
+ if ( this.world ) {
+ this.world.execute( 'applyImpulse', { id: this._physijs.id, impulse_x: force.x, impulse_y: force.y, impulse_z: force.z, x: offset.x, y: offset.y, z: offset.z } );
+ }
+ };
+
+ // Physijs.Mesh.applyTorque
+ Physijs.Mesh.prototype.applyTorque = function ( force ) {
+ if ( this.world ) {
+ this.world.execute( 'applyTorque', { id: this._physijs.id, torque_x: force.x, torque_y: force.y, torque_z: force.z } );
+ }
+ };
+
+ // Physijs.Mesh.applyCentralForce
+ Physijs.Mesh.prototype.applyCentralForce = function ( force ) {
+ if ( this.world ) {
+ this.world.execute( 'applyCentralForce', { id: this._physijs.id, x: force.x, y: force.y, z: force.z } );
+ }
+ };
+
+ // Physijs.Mesh.applyForce
+ Physijs.Mesh.prototype.applyForce = function ( force, offset ) {
+ if ( this.world ) {
+ this.world.execute( 'applyForce', { id: this._physijs.id, force_x: force.x, force_y : force.y, force_z : force.z, x: offset.x, y: offset.y, z: offset.z } );
+ }
+ };
+
+ // Physijs.Mesh.getAngularVelocity
+ Physijs.Mesh.prototype.getAngularVelocity = function () {
+ return this._physijs.angularVelocity;
+ };
+
+ // Physijs.Mesh.setAngularVelocity
+ Physijs.Mesh.prototype.setAngularVelocity = function ( velocity ) {
+ if ( this.world ) {
+ this.world.execute( 'setAngularVelocity', { id: this._physijs.id, x: velocity.x, y: velocity.y, z: velocity.z } );
+ }
+ };
+
+ // Physijs.Mesh.getLinearVelocity
+ Physijs.Mesh.prototype.getLinearVelocity = function () {
+ return this._physijs.linearVelocity;
+ };
+
+ // Physijs.Mesh.setLinearVelocity
+ Physijs.Mesh.prototype.setLinearVelocity = function ( velocity ) {
+ if ( this.world ) {
+ this.world.execute( 'setLinearVelocity', { id: this._physijs.id, x: velocity.x, y: velocity.y, z: velocity.z } );
+ }
+ };
+
+ // Physijs.Mesh.setAngularFactor
+ Physijs.Mesh.prototype.setAngularFactor = function ( factor ) {
+ if ( this.world ) {
+ this.world.execute( 'setAngularFactor', { id: this._physijs.id, x: factor.x, y: factor.y, z: factor.z } );
+ }
+ };
+
+ // Physijs.Mesh.setLinearFactor
+ Physijs.Mesh.prototype.setLinearFactor = function ( factor ) {
+ if ( this.world ) {
+ this.world.execute( 'setLinearFactor', { id: this._physijs.id, x: factor.x, y: factor.y, z: factor.z } );
+ }
+ };
+
+ // Physijs.Mesh.setDamping
+ Physijs.Mesh.prototype.setDamping = function ( linear, angular ) {
+ if ( this.world ) {
+ this.world.execute( 'setDamping', { id: this._physijs.id, linear: linear, angular: angular } );
+ }
+ };
+
+ // Physijs.Mesh.setCcdMotionThreshold
+ Physijs.Mesh.prototype.setCcdMotionThreshold = function ( threshold ) {
+ if ( this.world ) {
+ this.world.execute( 'setCcdMotionThreshold', { id: this._physijs.id, threshold: threshold } );
+ }
+ };
+
+ // Physijs.Mesh.setCcdSweptSphereRadius
+ Physijs.Mesh.prototype.setCcdSweptSphereRadius = function ( radius ) {
+ if ( this.world ) {
+ this.world.execute( 'setCcdSweptSphereRadius', { id: this._physijs.id, radius: radius } );
+ }
+ };
+
+
+ // Physijs.PlaneMesh
+ Physijs.PlaneMesh = function ( geometry, material, mass ) {
+ var width, height;
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+
+ this._physijs.type = 'plane';
+ this._physijs.normal = geometry.faces[0].normal.clone();
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height : mass;
+ };
+ Physijs.PlaneMesh.prototype = new Physijs.Mesh;
+ Physijs.PlaneMesh.prototype.constructor = Physijs.PlaneMesh;
+
+ // Physijs.HeightfieldMesh
+ Physijs.HeightfieldMesh = function ( geometry, material, mass, xdiv, ydiv) {
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ this._physijs.type = 'heightfield';
+ this._physijs.xsize = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ this._physijs.ysize = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+ this._physijs.xpts = (typeof xdiv === 'undefined') ? Math.sqrt(geometry.vertices.length) : xdiv + 1;
+ this._physijs.ypts = (typeof ydiv === 'undefined') ? Math.sqrt(geometry.vertices.length) : ydiv + 1;
+ // note - this assumes our plane geometry is square, unless we pass in specific xdiv and ydiv
+ this._physijs.absMaxHeight = Math.max(geometry.boundingBox.max.z,Math.abs(geometry.boundingBox.min.z));
+
+ var points = [];
+
+ var a, b;
+ for ( var i = 0; i < geometry.vertices.length; i++ ) {
+
+ a = i % this._physijs.xpts;
+ b = Math.round( ( i / this._physijs.xpts ) - ( (i % this._physijs.xpts) / this._physijs.xpts ) );
+ points[i] = geometry.vertices[ a + ( ( this._physijs.ypts - b - 1 ) * this._physijs.ypts ) ].z;
+
+ //points[i] = geometry.vertices[i];
+ }
+
+ this._physijs.points = points;
+ };
+ Physijs.HeightfieldMesh.prototype = new Physijs.Mesh;
+ Physijs.HeightfieldMesh.prototype.constructor = Physijs.HeightfieldMesh;
+
+ // Physijs.BoxMesh
+ Physijs.BoxMesh = function( geometry, material, mass ) {
+ var width, height, depth;
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+ depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
+
+ this._physijs.type = 'box';
+ this._physijs.width = width;
+ this._physijs.height = height;
+ this._physijs.depth = depth;
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass;
+ };
+ Physijs.BoxMesh.prototype = new Physijs.Mesh;
+ Physijs.BoxMesh.prototype.constructor = Physijs.BoxMesh;
+
+
+ // Physijs.SphereMesh
+ Physijs.SphereMesh = function( geometry, material, mass ) {
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingSphere ) {
+ geometry.computeBoundingSphere();
+ }
+
+ this._physijs.type = 'sphere';
+ this._physijs.radius = geometry.boundingSphere.radius;
+ this._physijs.mass = (typeof mass === 'undefined') ? (4/3) * Math.PI * Math.pow(this._physijs.radius, 3) : mass;
+ };
+ Physijs.SphereMesh.prototype = new Physijs.Mesh;
+ Physijs.SphereMesh.prototype.constructor = Physijs.SphereMesh;
+
+
+ // Physijs.CylinderMesh
+ Physijs.CylinderMesh = function( geometry, material, mass ) {
+ var width, height, depth;
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+ depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
+
+ this._physijs.type = 'cylinder';
+ this._physijs.width = width;
+ this._physijs.height = height;
+ this._physijs.depth = depth;
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass;
+ };
+ Physijs.CylinderMesh.prototype = new Physijs.Mesh;
+ Physijs.CylinderMesh.prototype.constructor = Physijs.CylinderMesh;
+
+
+ // Physijs.CapsuleMesh
+ Physijs.CapsuleMesh = function( geometry, material, mass ) {
+ var width, height, depth;
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+ depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
+
+ this._physijs.type = 'capsule';
+ this._physijs.radius = Math.max(width / 2, depth / 2);
+ this._physijs.height = height;
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass;
+ };
+ Physijs.CapsuleMesh.prototype = new Physijs.Mesh;
+ Physijs.CapsuleMesh.prototype.constructor = Physijs.CapsuleMesh;
+
+
+ // Physijs.ConeMesh
+ Physijs.ConeMesh = function( geometry, material, mass ) {
+ var width, height, depth;
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+
+ this._physijs.type = 'cone';
+ this._physijs.radius = width / 2;
+ this._physijs.height = height;
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height : mass;
+ };
+ Physijs.ConeMesh.prototype = new Physijs.Mesh;
+ Physijs.ConeMesh.prototype.constructor = Physijs.ConeMesh;
+
+
+ // Physijs.ConcaveMesh
+ Physijs.ConcaveMesh = function( geometry, material, mass ) {
+ var i,
+ width, height, depth,
+ vertices, face, triangles = [];
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ vertices = geometry.vertices;
+
+ for ( i = 0; i < geometry.faces.length; i++ ) {
+ face = geometry.faces[i];
+ if ( face instanceof THREE.Face3) {
+
+ triangles.push([
+ { x: vertices[face.a].x, y: vertices[face.a].y, z: vertices[face.a].z },
+ { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z },
+ { x: vertices[face.c].x, y: vertices[face.c].y, z: vertices[face.c].z }
+ ]);
+
+ } else if ( face instanceof THREE.Face4 ) {
+
+ triangles.push([
+ { x: vertices[face.a].x, y: vertices[face.a].y, z: vertices[face.a].z },
+ { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z },
+ { x: vertices[face.d].x, y: vertices[face.d].y, z: vertices[face.d].z }
+ ]);
+ triangles.push([
+ { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z },
+ { x: vertices[face.c].x, y: vertices[face.c].y, z: vertices[face.c].z },
+ { x: vertices[face.d].x, y: vertices[face.d].y, z: vertices[face.d].z }
+ ]);
+
+ }
+ }
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+ depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
+
+ this._physijs.type = 'concave';
+ this._physijs.triangles = triangles;
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass;
+ };
+ Physijs.ConcaveMesh.prototype = new Physijs.Mesh;
+ Physijs.ConcaveMesh.prototype.constructor = Physijs.ConcaveMesh;
+
+
+ // Physijs.ConvexMesh
+ Physijs.ConvexMesh = function( geometry, material, mass ) {
+ var i,
+ width, height, depth,
+ points = [];
+
+ Physijs.Mesh.call( this, geometry, material, mass );
+
+ if ( !geometry.boundingBox ) {
+ geometry.computeBoundingBox();
+ }
+
+ for ( i = 0; i < geometry.vertices.length; i++ ) {
+ points.push({
+ x: geometry.vertices[i].x,
+ y: geometry.vertices[i].y,
+ z: geometry.vertices[i].z
+ });
+ }
+
+
+ width = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
+ height = geometry.boundingBox.max.y - geometry.boundingBox.min.y;
+ depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z;
+
+ this._physijs.type = 'convex';
+ this._physijs.points = points;
+ this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass;
+ };
+ Physijs.ConvexMesh.prototype = new Physijs.Mesh;
+ Physijs.ConvexMesh.prototype.constructor = Physijs.ConvexMesh;
+
+
+ // Physijs.Vehicle
+ Physijs.Vehicle = function( mesh, tuning ) {
+ tuning = tuning || new Physijs.VehicleTuning;
+ this.mesh = mesh;
+ this.wheels = [];
+ this._physijs = {
+ id: getObjectId(),
+ rigidBody: mesh._physijs.id,
+ suspension_stiffness: tuning.suspension_stiffness,
+ suspension_compression: tuning.suspension_compression,
+ suspension_damping: tuning.suspension_damping,
+ max_suspension_travel: tuning.max_suspension_travel,
+ friction_slip: tuning.friction_slip,
+ max_suspension_force: tuning.max_suspension_force
+ };
+ };
+ Physijs.Vehicle.prototype.addWheel = function( wheel_geometry, wheel_material, connection_point, wheel_direction, wheel_axle, suspension_rest_length, wheel_radius, is_front_wheel, tuning ) {
+ var wheel = new THREE.Mesh( wheel_geometry, wheel_material );
+ wheel.castShadow = wheel.receiveShadow = true;
+ wheel.position.copy( wheel_direction ).multiplyScalar( suspension_rest_length / 100 ).add( connection_point );
+ this.world.add( wheel );
+ this.wheels.push( wheel );
+
+ this.world.execute( 'addWheel', {
+ id: this._physijs.id,
+ connection_point: { x: connection_point.x, y: connection_point.y, z: connection_point.z },
+ wheel_direction: { x: wheel_direction.x, y: wheel_direction.y, z: wheel_direction.z },
+ wheel_axle: { x: wheel_axle.x, y: wheel_axle.y, z: wheel_axle.z },
+ suspension_rest_length: suspension_rest_length,
+ wheel_radius: wheel_radius,
+ is_front_wheel: is_front_wheel,
+ tuning: tuning
+ });
+ };
+ Physijs.Vehicle.prototype.setSteering = function( amount, wheel ) {
+ if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) {
+ this.world.execute( 'setSteering', { id: this._physijs.id, wheel: wheel, steering: amount } );
+ } else if ( this.wheels.length > 0 ) {
+ for ( var i = 0; i < this.wheels.length; i++ ) {
+ this.world.execute( 'setSteering', { id: this._physijs.id, wheel: i, steering: amount } );
+ }
+ }
+ };
+ Physijs.Vehicle.prototype.setBrake = function( amount, wheel ) {
+ if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) {
+ this.world.execute( 'setBrake', { id: this._physijs.id, wheel: wheel, brake: amount } );
+ } else if ( this.wheels.length > 0 ) {
+ for ( var i = 0; i < this.wheels.length; i++ ) {
+ this.world.execute( 'setBrake', { id: this._physijs.id, wheel: i, brake: amount } );
+ }
+ }
+ };
+ Physijs.Vehicle.prototype.applyEngineForce = function( amount, wheel ) {
+ if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) {
+ this.world.execute( 'applyEngineForce', { id: this._physijs.id, wheel: wheel, force: amount } );
+ } else if ( this.wheels.length > 0 ) {
+ for ( var i = 0; i < this.wheels.length; i++ ) {
+ this.world.execute( 'applyEngineForce', { id: this._physijs.id, wheel: i, force: amount } );
+ }
+ }
+ };
+
+ // Physijs.VehicleTuning
+ Physijs.VehicleTuning = function( suspension_stiffness, suspension_compression, suspension_damping, max_suspension_travel, friction_slip, max_suspension_force ) {
+ this.suspension_stiffness = suspension_stiffness !== undefined ? suspension_stiffness : 5.88;
+ this.suspension_compression = suspension_compression !== undefined ? suspension_compression : 0.83;
+ this.suspension_damping = suspension_damping !== undefined ? suspension_damping : 0.88;
+ this.max_suspension_travel = max_suspension_travel !== undefined ? max_suspension_travel : 500;
+ this.friction_slip = friction_slip !== undefined ? friction_slip : 10.5;
+ this.max_suspension_force = max_suspension_force !== undefined ? max_suspension_force : 6000;
+ };
+
+ return Physijs;
+})();
diff --git a/physi_js/physijs.d.ts b/physi_js/physijs.d.ts
new file mode 100644
index 0000000..29bb3d4
--- /dev/null
+++ b/physi_js/physijs.d.ts
@@ -0,0 +1,250 @@
+// Type definitions for Physijs
+// Project: http://chandlerprall.github.io/Physijs/
+// Definitions by: Satoru Kimura <https://github.com/gyohk>
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+
+
+/// <reference path="../three_js/ts/three.d.ts" />
+
+declare namespace Physijs {
+ export function noConflict():Object;
+ export function createMaterial(material: THREE.Material, friction?: number, restitution?: number): Material;
+
+ export interface Material extends THREE.Material{
+ _physijs: {
+ id: number;
+ friction: number;
+ restriction: number
+ };
+ }
+
+ export interface Constraint {
+ getDefinition(): any;
+ }
+
+ export interface PointConstraintDefinition {
+ type: string;
+ id: number;
+ objecta: THREE.Object3D;
+ objectb: THREE.Object3D;
+ positiona: THREE.Vector3;
+ positionb: THREE.Vector3;
+ }
+
+ export class PointConstraint implements Constraint {
+ constructor(objecta: THREE.Object3D, objectb: THREE.Object3D, position?: THREE.Vector3);
+
+ getDefinition(): PointConstraintDefinition;
+ }
+
+ export interface HingeConstraintDefinition {
+ type: string;
+ id: number;
+ objecta: THREE.Object3D;
+ objectb: THREE.Object3D;
+ positiona: THREE.Vector3;
+ positionb: THREE.Vector3;
+ axis: THREE.Vector3;
+ }
+
+ export class HingeConstraint implements Constraint {
+ constructor(objecta: THREE.Object3D, objectb: THREE.Object3D, position: THREE.Vector3, axis?: THREE.Vector3);
+
+ getDefinition(): HingeConstraintDefinition;
+ setLimits( low: number, high: number, bias_factor: number, relaxation_factor: number ): void;
+ enableAngularMotor( velocity: number, acceleration: number ): void;
+ disableMotor(): void;
+ }
+
+ export interface SliderConstraintDefinition {
+ type: string;
+ id: number;
+ objecta: THREE.Object3D;
+ objectb: THREE.Object3D;
+ positiona: THREE.Vector3;
+ positionb: THREE.Vector3;
+ axis: THREE.Vector3;
+ }
+
+ export class SliderConstraint implements Constraint {
+ constructor(objecta: THREE.Object3D, objectb: THREE.Object3D, position: THREE.Vector3, axis?: THREE.Vector3);
+
+ getDefinition(): SliderConstraintDefinition;
+ setLimits( lin_lower: number, lin_upper: number, ang_lower: number, ang_upper: number ): void;
+ setRestitution( linear: number, angular: number ): void;
+ enableLinearMotor( velocity: number, acceleration: number): void;
+ disableLinearMotor(): void;
+ enableAngularMotor( velocity: number, acceleration: number ): void;
+ disableAngularMotor(): void;
+ }
+
+ export interface ConeTwistConstraintDefinition {
+ type: string;
+ id: number;
+ objecta: THREE.Object3D;
+ objectb: THREE.Object3D;
+ positiona: THREE.Vector3;
+ positionb: THREE.Vector3;
+ axisa: THREE.Vector3;
+ axisb: THREE.Vector3;
+ }
+
+ export class ConeTwistConstraint implements Constraint {
+ constructor(objecta: THREE.Object3D, objectb: THREE.Object3D, position: THREE.Vector3);
+
+ getDefinition(): ConeTwistConstraintDefinition;
+ setLimit( x: number, y: number, z: number ): void;
+ enableMotor(): void;
+ setMaxMotorImpulse( max_impulse: number ): void;
+ setMotorTarget( target: THREE.Vector3 ): void;
+ setMotorTarget( target: THREE.Euler ): void;
+ setMotorTarget( target: THREE.Matrix4 ): void;
+ disableMotor(): void;
+
+ }
+
+ export interface DOFConstraintDefinition {
+ type: string;
+ id: number;
+ objecta: THREE.Object3D;
+ objectb: THREE.Object3D;
+ positiona: THREE.Vector3;
+ positionb: THREE.Vector3;
+ axisa: THREE.Vector3;
+ axisb: THREE.Vector3;
+ }
+
+ export class DOFConstraint implements Constraint {
+ constructor(objecta: THREE.Object3D, objectb: THREE.Object3D, position?: THREE.Vector3);
+
+ getDefinition(): DOFConstraintDefinition;
+ setLinearLowerLimit(limit: THREE.Vector3): void;
+ setLinearUpperLimit(limit: THREE.Vector3): void;
+ setAngularLowerLimit(limit: THREE.Vector3): void;
+ setAngularUpperLimit(limit: THREE.Vector3): void;
+ enableAngularMotor( which: number ): void;
+ configureAngularMotor( which: number, low_angle: number, high_angle: number, velocity: number, max_force: number ): void;
+ disableAngularMotor( which: number ): void;
+ }
+ export var scripts: {
+ worker: string;
+ ammo: string;
+ };
+
+ export interface SceneParameters {
+ ammo?: string;
+ fixedTimeStep?: number;
+ rateLimit?: boolean;
+ }
+
+ export class Scene extends THREE.Scene {
+ constructor(param?: SceneParameters);
+
+ addConstraint(constraint:Constraint, show_marker?:boolean):void;
+ onSimulationResume():void;
+ removeConstraint(constraint:Constraint):void;
+ execute(cmd:string, params:any):void;
+ add(object:THREE.Object3D):void;
+ remove(object:THREE.Object3D):void;
+ setFixedTimeStep(fixedTimeStep:number):void;
+ setGravity(gravity:number):void;
+ simulate(timeStep?:number, maxSubSteps?:number):boolean;
+
+
+ // Eventable mixins
+ addEventListener( event_name: string, callback: (event: any) => void ): void;
+ removeEventListener( event_name: string, callback: (event: any) => void): void;
+ dispatchEvent( event_name: string ): void;
+
+ // (extends from Object3D)
+ dispatchEvent( event: { type: string; target: any; } ): void;
+ }
+
+ export class Mesh extends THREE.Mesh {
+ constructor(geometry:THREE.Geometry, material?:THREE.Material, mass?:number);
+
+ applyCentralImpulse(force:THREE.Vector3):void;
+ applyImpulse(force:THREE.Vector3, offset:THREE.Vector3):void;
+ applyCentralForce(force:THREE.Vector3):void;
+ applyForce(force:THREE.Vector3, offset:THREE.Vector3):void;
+ getAngularVelocity():THREE.Vector3;
+ setAngularVelocity(velocity:THREE.Vector3):void;
+ getLinearVelocity():THREE.Vector3;
+ setLinearVelocity(velocity:THREE.Vector3):void;
+ setAngularFactor(factor:THREE.Vector3):void;
+ setLinearFactor(factor:THREE.Vector3):void;
+ setDamping(linear:number, angular:number):void;
+ setCcdMotionThreshold(threshold:number):void;
+ setCcdSweptSphereRadius(radius:number):void;
+
+
+ // Eventable mixins
+ addEventListener( event_name: string, callback: (event: any) => void ): void;
+ removeEventListener( event_name: string, callback: (event: any) => void): void;
+ dispatchEvent( event_name: string ): void;
+
+ // (extends from Object3D)
+ dispatchEvent( event: { type: string; target: any; } ): void;
+ }
+
+ export class PlaneMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+
+ }
+
+ export class HeightfieldMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number, xdiv?:number, ydiv?:number);
+ }
+
+ export class BoxMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class SphereMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class CylinderMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class CapsuleMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class ConeMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class ConcaveMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class ConvexMesh extends Mesh {
+ constructor(geometry:THREE.Geometry, material:THREE.Material, mass?:number);
+ }
+
+ export class Vehicle {
+ constructor(mesh:Mesh, tuning?:VehicleTuning);
+
+ mesh:THREE.Mesh;
+ wheels:THREE.Mesh[];
+
+ addWheel(wheel_geometry:THREE.Geometry, wheel_material:THREE.Material, connection_point:THREE.Vector3, wheel_direction:THREE.Vector3, wheel_axle:THREE.Vector3, suspension_rest_length:number, wheel_radius:number, is_front_wheel:boolean, tuning?:VehicleTuning): void;
+ setSteering(amount: number, wheel?: THREE.Mesh): void;
+ setBrake(amount: number, wheel?: THREE.Mesh): void;
+ applyEngineForce(amount: number, wheel?: THREE.Mesh): void;
+ }
+
+ export class VehicleTuning {
+ constructor(suspension_stiffness?:number, suspension_compression?:number, suspension_damping?:number, max_suspension_travel?:number, friction_slip?:number, max_suspension_force?:number);
+
+ suspension_stiffness:number;
+ suspension_compression:number;
+ suspension_damping:number;
+ max_suspension_travel:number;
+ friction_slip:number;
+ max_suspension_force:number;
+ }
+}
+
diff --git a/physi_js/physijs_worker.js b/physi_js/physijs_worker.js
new file mode 100644
index 0000000..a6c3811
--- /dev/null
+++ b/physi_js/physijs_worker.js
@@ -0,0 +1,1415 @@
+'use strict';
+var
+ transferableMessage = self.webkitPostMessage || self.postMessage,
+
+ // enum
+ MESSAGE_TYPES = {
+ WORLDREPORT: 0,
+ COLLISIONREPORT: 1,
+ VEHICLEREPORT: 2,
+ CONSTRAINTREPORT: 3
+ },
+
+ // temp variables
+ _object,
+ _vector,
+ _transform,
+
+ // functions
+ public_functions = {},
+ getShapeFromCache,
+ setShapeCache,
+ createShape,
+ reportWorld,
+ reportVehicles,
+ reportCollisions,
+ reportConstraints,
+
+ // world variables
+ fixedTimeStep, // used when calling stepSimulation
+ rateLimit, // sets whether or not to sync the simulation rate with fixedTimeStep
+ last_simulation_time,
+ last_simulation_duration = 0,
+ world,
+ transform,
+ _vec3_1,
+ _vec3_2,
+ _vec3_3,
+ _quat,
+ // private cache
+ _objects = {},
+ _vehicles = {},
+ _constraints = {},
+ _materials = {},
+ _objects_ammo = {},
+ _num_objects = 0,
+ _num_wheels = 0,
+ _num_constraints = 0,
+ _object_shapes = {},
+
+ // The following objects are to track objects that ammo.js doesn't clean
+ // up. All are cleaned up when they're corresponding body is destroyed.
+ // Unfortunately, it's very difficult to get at these objects from the
+ // body, so we have to track them ourselves.
+ _motion_states = {},
+ // Don't need to worry about it for cached shapes.
+ _noncached_shapes = {},
+ // A body with a compound shape always has a regular shape as well, so we
+ // have track them separately.
+ _compound_shapes = {},
+
+ // object reporting
+ REPORT_CHUNKSIZE, // report array is increased in increments of this chunk size
+
+ WORLDREPORT_ITEMSIZE = 14, // how many float values each reported item needs
+ worldreport,
+
+ COLLISIONREPORT_ITEMSIZE = 5, // one float for each object id, and a Vec3 contact normal
+ collisionreport,
+
+ VEHICLEREPORT_ITEMSIZE = 9, // vehicle id, wheel index, 3 for position, 4 for rotation
+ vehiclereport,
+
+ CONSTRAINTREPORT_ITEMSIZE = 6, // constraint id, offset object, offset, applied impulse
+ constraintreport;
+
+var ab = new ArrayBuffer( 1 );
+
+transferableMessage( ab, [ab] );
+var SUPPORT_TRANSFERABLE = ( ab.byteLength === 0 );
+
+getShapeFromCache = function ( cache_key ) {
+ if ( _object_shapes[ cache_key ] !== undefined ) {
+ return _object_shapes[ cache_key ];
+ }
+ return null;
+};
+
+setShapeCache = function ( cache_key, shape ) {
+ _object_shapes[ cache_key ] = shape;
+}
+
+createShape = function( description ) {
+ var cache_key, shape;
+
+ _transform.setIdentity();
+ switch ( description.type ) {
+ case 'plane':
+ cache_key = 'plane_' + description.normal.x + '_' + description.normal.y + '_' + description.normal.z;
+ if ( ( shape = getShapeFromCache( cache_key ) ) === null ) {
+ _vec3_1.setX(description.normal.x);
+ _vec3_1.setY(description.normal.y);
+ _vec3_1.setZ(description.normal.z);
+ shape = new Ammo.btStaticPlaneShape(_vec3_1, 0 );
+ setShapeCache( cache_key, shape );
+ }
+ break;
+
+ case 'box':
+ cache_key = 'box_' + description.width + '_' + description.height + '_' + description.depth;
+ if ( ( shape = getShapeFromCache( cache_key ) ) === null ) {
+ _vec3_1.setX(description.width / 2);
+ _vec3_1.setY(description.height / 2);
+ _vec3_1.setZ(description.depth / 2);
+ shape = new Ammo.btBoxShape(_vec3_1);
+ setShapeCache( cache_key, shape );
+ }
+ break;
+
+ case 'sphere':
+ cache_key = 'sphere_' + description.radius;
+ if ( ( shape = getShapeFromCache( cache_key ) ) === null ) {
+ shape = new Ammo.btSphereShape( description.radius );
+ setShapeCache( cache_key, shape );
+ }
+ break;
+
+ case 'cylinder':
+ cache_key = 'cylinder_' + description.width + '_' + description.height + '_' + description.depth;
+ if ( ( shape = getShapeFromCache( cache_key ) ) === null ) {
+ _vec3_1.setX(description.width / 2);
+ _vec3_1.setY(description.height / 2);
+ _vec3_1.setZ(description.depth / 2);
+ shape = new Ammo.btCylinderShape(_vec3_1);
+ setShapeCache( cache_key, shape );
+ }
+ break;
+
+ case 'capsule':
+ cache_key = 'capsule_' + description.radius + '_' + description.height;
+ if ( ( shape = getShapeFromCache( cache_key ) ) === null ) {
+ // In Bullet, capsule height excludes the end spheres
+ shape = new Ammo.btCapsuleShape( description.radius, description.height - 2 * description.radius );
+ setShapeCache( cache_key, shape );
+ }
+ break;
+
+ case 'cone':
+ cache_key = 'cone_' + description.radius + '_' + description.height;
+ if ( ( shape = getShapeFromCache( cache_key ) ) === null ) {
+ shape = new Ammo.btConeShape( description.radius, description.height );
+ setShapeCache( cache_key, shape );
+ }
+ break;
+
+ case 'concave':
+ var i, triangle, triangle_mesh = new Ammo.btTriangleMesh;
+ if (!description.triangles.length) return false
+
+ for ( i = 0; i < description.triangles.length; i++ ) {
+ triangle = description.triangles[i];
+
+ _vec3_1.setX(triangle[0].x);
+ _vec3_1.setY(triangle[0].y);
+ _vec3_1.setZ(triangle[0].z);
+
+ _vec3_2.setX(triangle[1].x);
+ _vec3_2.setY(triangle[1].y);
+ _vec3_2.setZ(triangle[1].z);
+
+ _vec3_3.setX(triangle[2].x);
+ _vec3_3.setY(triangle[2].y);
+ _vec3_3.setZ(triangle[2].z);
+
+ triangle_mesh.addTriangle(
+ _vec3_1,
+ _vec3_2,
+ _vec3_3,
+ true
+ );
+ }
+
+ shape = new Ammo.btBvhTriangleMeshShape(
+ triangle_mesh,
+ true,
+ true
+ );
+ _noncached_shapes[description.id] = shape;
+ break;
+
+ case 'convex':
+ var i, point, shape = new Ammo.btConvexHullShape;
+ for ( i = 0; i < description.points.length; i++ ) {
+ point = description.points[i];
+
+ _vec3_1.setX(point.x);
+ _vec3_1.setY(point.y);
+ _vec3_1.setZ(point.z);
+
+ shape.addPoint(_vec3_1);
+
+ }
+ _noncached_shapes[description.id] = shape;
+ break;
+
+ case 'heightfield':
+
+ var ptr = Ammo.allocate(4 * description.xpts * description.ypts, "float", Ammo.ALLOC_NORMAL);
+
+ for (var f = 0; f < description.points.length; f++) {
+ Ammo.setValue(ptr + f, description.points[f] , 'float');
+ }
+
+ shape = new Ammo.btHeightfieldTerrainShape(
+ description.xpts,
+ description.ypts,
+ ptr,
+ 1,
+ -description.absMaxHeight,
+ description.absMaxHeight,
+ 2,
+ 0,
+ false
+ );
+
+ _vec3_1.setX(description.xsize/(description.xpts - 1));
+ _vec3_1.setY(description.ysize/(description.ypts - 1));
+ _vec3_1.setZ(1);
+
+ shape.setLocalScaling(_vec3_1);
+ _noncached_shapes[description.id] = shape;
+ break;
+
+ default:
+ // Not recognized
+ return;
+ break;
+ }
+
+ return shape;
+};
+
+public_functions.init = function( params ) {
+ importScripts( params.ammo );
+
+ _transform = new Ammo.btTransform;
+ _vec3_1 = new Ammo.btVector3(0,0,0);
+ _vec3_2 = new Ammo.btVector3(0,0,0);
+ _vec3_3 = new Ammo.btVector3(0,0,0);
+ _quat = new Ammo.btQuaternion(0,0,0,0);
+
+ REPORT_CHUNKSIZE = params.reportsize || 50;
+ if ( SUPPORT_TRANSFERABLE ) {
+ // Transferable messages are supported, take advantage of them with TypedArrays
+ worldreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object
+ collisionreport = new Float32Array(2 + REPORT_CHUNKSIZE * COLLISIONREPORT_ITEMSIZE); // message id + # of collisions to report + chunk size * # of values per object
+ vehiclereport = new Float32Array(2 + REPORT_CHUNKSIZE * VEHICLEREPORT_ITEMSIZE); // message id + # of vehicles to report + chunk size * # of values per object
+ constraintreport = new Float32Array(2 + REPORT_CHUNKSIZE * CONSTRAINTREPORT_ITEMSIZE); // message id + # of constraints to report + chunk size * # of values per object
+ } else {
+ // Transferable messages are not supported, send data as normal arrays
+ worldreport = [];
+ collisionreport = [];
+ vehiclereport = [];
+ constraintreport = [];
+ }
+ worldreport[0] = MESSAGE_TYPES.WORLDREPORT;
+ collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT;
+ vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT;
+ constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT;
+
+ var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration,
+ dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ),
+ solver = new Ammo.btSequentialImpulseConstraintSolver,
+ broadphase;
+
+ if ( !params.broadphase ) params.broadphase = { type: 'dynamic' };
+ switch ( params.broadphase.type ) {
+ case 'sweepprune':
+
+ _vec3_1.setX(params.broadphase.aabbmin.x);
+ _vec3_1.setY(params.broadphase.aabbmin.y);
+ _vec3_1.setZ(params.broadphase.aabbmin.z);
+
+ _vec3_2.setX(params.broadphase.aabbmax.x);
+ _vec3_2.setY(params.broadphase.aabbmax.y);
+ _vec3_2.setZ(params.broadphase.aabbmax.z);
+
+ broadphase = new Ammo.btAxisSweep3(
+ _vec3_1,
+ _vec3_2
+ );
+
+ break;
+
+ case 'dynamic':
+ default:
+ broadphase = new Ammo.btDbvtBroadphase;
+ break;
+ }
+
+ world = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration );
+
+ fixedTimeStep = params.fixedTimeStep;
+ rateLimit = params.rateLimit;
+
+ transferableMessage({ cmd: 'worldReady' });
+};
+
+public_functions.registerMaterial = function( description ) {
+ _materials[ description.id ] = description;
+};
+
+public_functions.unRegisterMaterial = function( description ) {
+ delete _materials[ description.id ];
+};
+
+public_functions.setFixedTimeStep = function( description ) {
+ fixedTimeStep = description;
+};
+
+public_functions.setGravity = function( description ) {
+ _vec3_1.setX(description.x);
+ _vec3_1.setY(description.y);
+ _vec3_1.setZ(description.z);
+ world.setGravity(_vec3_1);
+};
+
+public_functions.addObject = function( description ) {
+
+ var i,
+ localInertia, shape, motionState, rbInfo, body;
+
+shape = createShape( description );
+if (!shape) return
+// If there are children then this is a compound shape
+if ( description.children ) {
+ var compound_shape = new Ammo.btCompoundShape, _child;
+ compound_shape.addChildShape( _transform, shape );
+
+ for ( i = 0; i < description.children.length; i++ ) {
+ _child = description.children[i];
+
+ var trans = new Ammo.btTransform;
+ trans.setIdentity();
+
+ _vec3_1.setX(_child.position_offset.x);
+ _vec3_1.setY(_child.position_offset.y);
+ _vec3_1.setZ(_child.position_offset.z);
+ trans.setOrigin(_vec3_1);
+
+ _quat.setX(_child.rotation.x);
+ _quat.setY(_child.rotation.y);
+ _quat.setZ(_child.rotation.z);
+ _quat.setW(_child.rotation.w);
+ trans.setRotation(_quat);
+
+ shape = createShape( description.children[i] );
+ compound_shape.addChildShape( trans, shape );
+ Ammo.destroy(trans);
+ }
+
+ shape = compound_shape;
+ _compound_shapes[ description.id ] = shape;
+ }
+ _vec3_1.setX(0);
+ _vec3_1.setY(0);
+ _vec3_1.setZ(0);
+ shape.calculateLocalInertia( description.mass, _vec3_1 );
+
+ _transform.setIdentity();
+
+ _vec3_2.setX(description.position.x);
+ _vec3_2.setY(description.position.y);
+ _vec3_2.setZ(description.position.z);
+ _transform.setOrigin(_vec3_2);
+
+ _quat.setX(description.rotation.x);
+ _quat.setY(description.rotation.y);
+ _quat.setZ(description.rotation.z);
+ _quat.setW(description.rotation.w);
+ _transform.setRotation(_quat);
+
+ motionState = new Ammo.btDefaultMotionState( _transform ); // #TODO: btDefaultMotionState supports center of mass offset as second argument - implement
+ rbInfo = new Ammo.btRigidBodyConstructionInfo( description.mass, motionState, shape, _vec3_1 );
+
+ if ( description.materialId !== undefined ) {
+ rbInfo.set_m_friction( _materials[ description.materialId ].friction );
+ rbInfo.set_m_restitution( _materials[ description.materialId ].restitution );
+ }
+
+ body = new Ammo.btRigidBody( rbInfo );
+ Ammo.destroy(rbInfo);
+
+ if ( typeof description.collision_flags !== 'undefined' ) {
+ body.setCollisionFlags( description.collision_flags );
+ }
+
+ world.addRigidBody( body );
+
+ body.id = description.id;
+ _objects[ body.id ] = body;
+ _motion_states[ body.id ] = motionState;
+
+ var ptr = body.a != undefined ? body.a : body.ptr;
+ _objects_ammo[ptr] = body.id;
+ _num_objects++;
+
+ transferableMessage({ cmd: 'objectReady', params: body.id });
+};
+
+public_functions.addVehicle = function( description ) {
+ var vehicle_tuning = new Ammo.btVehicleTuning(),
+ vehicle;
+
+ vehicle_tuning.set_m_suspensionStiffness( description.suspension_stiffness );
+ vehicle_tuning.set_m_suspensionCompression( description.suspension_compression );
+ vehicle_tuning.set_m_suspensionDamping( description.suspension_damping );
+ vehicle_tuning.set_m_maxSuspensionTravelCm( description.max_suspension_travel );
+ vehicle_tuning.set_m_maxSuspensionForce( description.max_suspension_force );
+
+ vehicle = new Ammo.btRaycastVehicle( vehicle_tuning, _objects[ description.rigidBody ], new Ammo.btDefaultVehicleRaycaster( world ) );
+ vehicle.tuning = vehicle_tuning;
+
+ _objects[ description.rigidBody ].setActivationState( 4 );
+ vehicle.setCoordinateSystem( 0, 1, 2 );
+
+ world.addVehicle( vehicle );
+ _vehicles[ description.id ] = vehicle;
+};
+public_functions.removeVehicle = function( description ) {
+ delete _vehicles[ description.id ];
+};
+
+public_functions.addWheel = function( description ) {
+ if ( _vehicles[description.id] !== undefined ) {
+ var tuning = _vehicles[description.id].tuning;
+ if ( description.tuning !== undefined ) {
+ tuning = new Ammo.btVehicleTuning();
+ tuning.set_m_suspensionStiffness( description.tuning.suspension_stiffness );
+ tuning.set_m_suspensionCompression( description.tuning.suspension_compression );
+ tuning.set_m_suspensionDamping( description.tuning.suspension_damping );
+ tuning.set_m_maxSuspensionTravelCm( description.tuning.max_suspension_travel );
+ tuning.set_m_maxSuspensionForce( description.tuning.max_suspension_force );
+ }
+
+ _vec3_1.setX(description.connection_point.x);
+ _vec3_1.setY(description.connection_point.y);
+ _vec3_1.setZ(description.connection_point.z);
+
+ _vec3_2.setX(description.wheel_direction.x);
+ _vec3_2.setY(description.wheel_direction.y);
+ _vec3_2.setZ(description.wheel_direction.z);
+
+ _vec3_3.setX(description.wheel_axle.x);
+ _vec3_3.setY(description.wheel_axle.y);
+ _vec3_3.setZ(description.wheel_axle.z);
+
+ _vehicles[description.id].addWheel(
+ _vec3_1,
+ _vec3_2,
+ _vec3_3,
+ description.suspension_rest_length,
+ description.wheel_radius,
+ tuning,
+ description.is_front_wheel
+ );
+ }
+
+ _num_wheels++;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ vehiclereport = new Float32Array(1 + _num_wheels * VEHICLEREPORT_ITEMSIZE); // message id & ( # of objects to report * # of values per object )
+ vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT;
+ } else {
+ vehiclereport = [ MESSAGE_TYPES.VEHICLEREPORT ];
+ }
+};
+
+public_functions.setSteering = function( details ) {
+ if ( _vehicles[details.id] !== undefined ) {
+ _vehicles[details.id].setSteeringValue( details.steering, details.wheel );
+ }
+};
+public_functions.setBrake = function( details ) {
+ if ( _vehicles[details.id] !== undefined ) {
+ _vehicles[details.id].setBrake( details.brake, details.wheel );
+ }
+};
+public_functions.applyEngineForce = function( details ) {
+ if ( _vehicles[details.id] !== undefined ) {
+ _vehicles[details.id].applyEngineForce( details.force, details.wheel );
+ }
+};
+
+public_functions.removeObject = function( details ) {
+ world.removeRigidBody( _objects[details.id] );
+ Ammo.destroy(_objects[details.id]);
+ Ammo.destroy(_motion_states[details.id]);
+ if (_compound_shapes[details.id]) Ammo.destroy(_compound_shapes[details.id]);
+ if (_noncached_shapes[details.id]) Ammo.destroy(_noncached_shapes[details.id]);
+ var ptr = _objects[details.id].a != undefined ? _objects[details.id].a : _objects[details.id].ptr;
+ delete _objects_ammo[ptr];
+ delete _objects[details.id];
+ delete _motion_states[details.id];
+ if (_compound_shapes[details.id]) delete _compound_shapes[details.id];
+ if (_noncached_shapes[details.id]) delete _noncached_shapes[details.id];
+ _num_objects--;
+};
+
+public_functions.updateTransform = function( details ) {
+ _object = _objects[details.id];
+ _object.getMotionState().getWorldTransform( _transform );
+
+ if ( details.pos ) {
+ _vec3_1.setX(details.pos.x);
+ _vec3_1.setY(details.pos.y);
+ _vec3_1.setZ(details.pos.z);
+ _transform.setOrigin(_vec3_1);
+ }
+
+ if ( details.quat ) {
+ _quat.setX(details.quat.x);
+ _quat.setY(details.quat.y);
+ _quat.setZ(details.quat.z);
+ _quat.setW(details.quat.w);
+ _transform.setRotation(_quat);
+ }
+
+ _object.setWorldTransform( _transform );
+ _object.activate();
+};
+
+public_functions.updateMass = function( details ) {
+ // #TODO: changing a static object into dynamic is buggy
+ _object = _objects[details.id];
+
+ // Per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=&f=9&t=3663#p13816
+ world.removeRigidBody( _object );
+
+ _vec3_1.setX(0);
+ _vec3_1.setY(0);
+ _vec3_1.setZ(0);
+
+ _object.setMassProps( details.mass, _vec3_1 );
+ world.addRigidBody( _object );
+ _object.activate();
+};
+
+public_functions.applyCentralImpulse = function ( details ) {
+
+ _vec3_1.setX(details.x);
+ _vec3_1.setY(details.y);
+ _vec3_1.setZ(details.z);
+
+ _objects[details.id].applyCentralImpulse(_vec3_1);
+ _objects[details.id].activate();
+};
+
+public_functions.applyImpulse = function ( details ) {
+
+ _vec3_1.setX(details.impulse_x);
+ _vec3_1.setY(details.impulse_y);
+ _vec3_1.setZ(details.impulse_z);
+
+ _vec3_2.setX(details.x);
+ _vec3_2.setY(details.y);
+ _vec3_2.setZ(details.z);
+
+ _objects[details.id].applyImpulse(
+ _vec3_1,
+ _vec3_2
+ );
+ _objects[details.id].activate();
+};
+
+public_functions.applyTorque = function ( details ) {
+
+ _vec3_1.setX(details.torque_x);
+ _vec3_1.setY(details.torque_y);
+ _vec3_1.setZ(details.torque_z);
+
+ _objects[details.id].applyTorque(
+ _vec3_1
+ );
+ _objects[details.id].activate();
+};
+
+public_functions.applyCentralForce = function ( details ) {
+
+ _vec3_1.setX(details.x);
+ _vec3_1.setY(details.y);
+ _vec3_1.setZ(details.z);
+
+ _objects[details.id].applyCentralForce(_vec3_1);
+ _objects[details.id].activate();
+};
+
+public_functions.applyForce = function ( details ) {
+
+ _vec3_1.setX(details.force_x);
+ _vec3_1.setY(details.force_y);
+ _vec3_1.setZ(details.force_z);
+
+ _vec3_2.setX(details.x);
+ _vec3_2.setY(details.y);
+ _vec3_2.setZ(details.z);
+
+ _objects[details.id].applyForce(
+ _vec3_1,
+ _vec3_2
+ );
+ _objects[details.id].activate();
+};
+
+public_functions.onSimulationResume = function( params ) {
+ last_simulation_time = Date.now();
+};
+
+public_functions.setAngularVelocity = function ( details ) {
+
+ _vec3_1.setX(details.x);
+ _vec3_1.setY(details.y);
+ _vec3_1.setZ(details.z);
+
+ _objects[details.id].setAngularVelocity(
+ _vec3_1
+ );
+ _objects[details.id].activate();
+};
+
+public_functions.setLinearVelocity = function ( details ) {
+
+ _vec3_1.setX(details.x);
+ _vec3_1.setY(details.y);
+ _vec3_1.setZ(details.z);
+
+ _objects[details.id].setLinearVelocity(
+ _vec3_1
+ );
+ _objects[details.id].activate();
+};
+
+public_functions.setAngularFactor = function ( details ) {
+
+ _vec3_1.setX(details.x);
+ _vec3_1.setY(details.y);
+ _vec3_1.setZ(details.z);
+
+ _objects[details.id].setAngularFactor(
+ _vec3_1
+ );
+};
+
+public_functions.setLinearFactor = function ( details ) {
+
+ _vec3_1.setX(details.x);
+ _vec3_1.setY(details.y);
+ _vec3_1.setZ(details.z);
+
+ _objects[details.id].setLinearFactor(
+ _vec3_1
+ );
+};
+
+public_functions.setDamping = function ( details ) {
+ _objects[details.id].setDamping( details.linear, details.angular );
+};
+
+public_functions.setCcdMotionThreshold = function ( details ) {
+ _objects[details.id].setCcdMotionThreshold( details.threshold );
+};
+
+public_functions.setCcdSweptSphereRadius = function ( details ) {
+ _objects[details.id].setCcdSweptSphereRadius( details.radius );
+};
+
+public_functions.addConstraint = function ( details ) {
+ var constraint;
+
+ switch ( details.type ) {
+
+ case 'point':
+ if ( details.objectb === undefined ) {
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ constraint = new Ammo.btPoint2PointConstraint(
+ _objects[ details.objecta ],
+ _vec3_1
+ );
+ } else {
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ _vec3_2.setX(details.positionb.x);
+ _vec3_2.setY(details.positionb.y);
+ _vec3_2.setZ(details.positionb.z);
+
+ constraint = new Ammo.btPoint2PointConstraint(
+ _objects[ details.objecta ],
+ _objects[ details.objectb ],
+ _vec3_1,
+ _vec3_2
+ );
+ }
+ break;
+
+ case 'hinge':
+ if ( details.objectb === undefined ) {
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ _vec3_2.setX(details.axis.x);
+ _vec3_2.setY(details.axis.y);
+ _vec3_2.setZ(details.axis.z);
+
+ constraint = new Ammo.btHingeConstraint(
+ _objects[ details.objecta ],
+ _vec3_1,
+ _vec3_2
+ );
+ } else {
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ _vec3_2.setX(details.positionb.x);
+ _vec3_2.setY(details.positionb.y);
+ _vec3_2.setZ(details.positionb.z);
+
+ _vec3_3.setX(details.axis.x);
+ _vec3_3.setY(details.axis.y);
+ _vec3_3.setZ(details.axis.z);
+
+ constraint = new Ammo.btHingeConstraint(
+ _objects[ details.objecta ],
+ _objects[ details.objectb ],
+ _vec3_1,
+ _vec3_2,
+ _vec3_3,
+ _vec3_3
+ );
+ }
+ break;
+
+ case 'slider':
+ var transforma, transformb, rotation;
+
+ transforma = new Ammo.btTransform();
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ transforma.setOrigin(_vec3_1);
+
+ var rotation = transforma.getRotation();
+ rotation.setEuler( details.axis.x, details.axis.y, details.axis.z );
+ transforma.setRotation( rotation );
+
+ if ( details.objectb ) {
+ transformb = new Ammo.btTransform();
+
+ _vec3_2.setX(details.positionb.x);
+ _vec3_2.setY(details.positionb.y);
+ _vec3_2.setZ(details.positionb.z);
+
+ transformb.setOrigin(_vec3_2);
+
+ rotation = transformb.getRotation();
+ rotation.setEuler( details.axis.x, details.axis.y, details.axis.z );
+ transformb.setRotation( rotation );
+
+ constraint = new Ammo.btSliderConstraint(
+ _objects[ details.objecta ],
+ _objects[ details.objectb ],
+ transforma,
+ transformb,
+ true
+ );
+ } else {
+ constraint = new Ammo.btSliderConstraint(
+ _objects[ details.objecta ],
+ transforma,
+ true
+ );
+ }
+
+ Ammo.destroy(transforma);
+ if (transformb != undefined) {
+ Ammo.destroy(transformb);
+ }
+ break;
+
+ case 'conetwist':
+ var transforma, transformb;
+
+ transforma = new Ammo.btTransform();
+ transforma.setIdentity();
+
+ transformb = new Ammo.btTransform();
+ transformb.setIdentity();
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ _vec3_2.setX(details.positionb.x);
+ _vec3_2.setY(details.positionb.y);
+ _vec3_2.setZ(details.positionb.z);
+
+ transforma.setOrigin(_vec3_1);
+ transformb.setOrigin(_vec3_2);
+
+ var rotation = transforma.getRotation();
+ rotation.setEulerZYX( -details.axisa.z, -details.axisa.y, -details.axisa.x );
+ transforma.setRotation( rotation );
+
+ rotation = transformb.getRotation();
+ rotation.setEulerZYX( -details.axisb.z, -details.axisb.y, -details.axisb.x );
+ transformb.setRotation( rotation );
+
+ constraint = new Ammo.btConeTwistConstraint(
+ _objects[ details.objecta ],
+ _objects[ details.objectb ],
+ transforma,
+ transformb
+ );
+
+ constraint.setLimit( Math.PI, 0, Math.PI );
+
+ Ammo.destroy(transforma);
+ Ammo.destroy(transformb);
+
+ break;
+
+ case 'dof':
+ var transforma, transformb, rotation;
+
+ transforma = new Ammo.btTransform();
+ transforma.setIdentity();
+
+ _vec3_1.setX(details.positiona.x);
+ _vec3_1.setY(details.positiona.y);
+ _vec3_1.setZ(details.positiona.z);
+
+ transforma.setOrigin(_vec3_1 );
+
+ rotation = transforma.getRotation();
+ rotation.setEulerZYX( -details.axisa.z, -details.axisa.y, -details.axisa.x );
+ transforma.setRotation( rotation );
+
+ if ( details.objectb ) {
+ transformb = new Ammo.btTransform();
+ transformb.setIdentity();
+
+ _vec3_2.setX(details.positionb.x);
+ _vec3_2.setY(details.positionb.y);
+ _vec3_2.setZ(details.positionb.z);
+
+ transformb.setOrigin(_vec3_2);
+
+ rotation = transformb.getRotation();
+ rotation.setEulerZYX( -details.axisb.z, -details.axisb.y, -details.axisb.x );
+ transformb.setRotation( rotation );
+
+ constraint = new Ammo.btGeneric6DofConstraint(
+ _objects[ details.objecta ],
+ _objects[ details.objectb ],
+ transforma,
+ transformb
+ );
+ } else {
+ constraint = new Ammo.btGeneric6DofConstraint(
+ _objects[ details.objecta ],
+ transforma
+ );
+ }
+ Ammo.destroy(transforma);
+ if (transformb != undefined) {
+ Ammo.destroy(transformb);
+ }
+ break;
+
+ default:
+ return;
+
+ };
+
+ world.addConstraint( constraint );
+
+ constraint.enableFeedback();
+ _constraints[ details.id ] = constraint;
+ _num_constraints++;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ constraintreport = new Float32Array(1 + _num_constraints * CONSTRAINTREPORT_ITEMSIZE); // message id & ( # of objects to report * # of values per object )
+ constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT;
+ } else {
+ constraintreport = [ MESSAGE_TYPES.CONSTRAINTREPORT ];
+ }
+};
+
+public_functions.removeConstraint = function( details ) {
+ var constraint = _constraints[ details.id ];
+ if ( constraint !== undefined ) {
+ world.removeConstraint( constraint );
+ delete _constraints[ details.id ];
+ _num_constraints--;
+ }
+};
+
+public_functions.constraint_setBreakingImpulseThreshold = function( details ) {
+ var constraint = _constraints[ details.id ];
+ if ( constraint !== undefind ) {
+ constraint.setBreakingImpulseThreshold( details.threshold );
+ }
+};
+
+public_functions.simulate = function simulate( params ) {
+ if ( world ) {
+ params = params || {};
+
+ if ( !params.timeStep ) {
+ if ( last_simulation_time ) {
+ params.timeStep = 0;
+ while ( params.timeStep + last_simulation_duration <= fixedTimeStep ) {
+ params.timeStep = ( Date.now() - last_simulation_time ) / 1000; // time since last simulation
+ }
+ } else {
+ params.timeStep = fixedTimeStep; // handle first frame
+ }
+ } else {
+ if ( params.timeStep < fixedTimeStep ) {
+ params.timeStep = fixedTimeStep;
+ }
+ }
+
+ params.maxSubSteps = params.maxSubSteps || Math.ceil( params.timeStep / fixedTimeStep ); // If maxSubSteps is not defined, keep the simulation fully up to date
+
+ last_simulation_duration = Date.now();
+ world.stepSimulation( params.timeStep, params.maxSubSteps, fixedTimeStep );
+
+ reportVehicles();
+ reportCollisions();
+ reportConstraints();
+ reportWorld();
+
+ last_simulation_duration = ( Date.now() - last_simulation_duration ) / 1000;
+ last_simulation_time = Date.now();
+ }
+};
+
+
+// Constraint functions
+public_functions.hinge_setLimits = function( params ) {
+ _constraints[ params.constraint ].setLimit( params.low, params.high, 0, params.bias_factor, params.relaxation_factor );
+};
+public_functions.hinge_enableAngularMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.enableAngularMotor( true, params.velocity, params.acceleration );
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.hinge_disableMotor = function( params ) {
+ _constraints[ params.constraint ].enableMotor( false );
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+
+public_functions.slider_setLimits = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setLowerLinLimit( params.lin_lower || 0 );
+ constraint.setUpperLinLimit( params.lin_upper || 0 );
+
+ constraint.setLowerAngLimit( params.ang_lower || 0 );
+ constraint.setUpperAngLimit( params.ang_upper || 0 );
+};
+public_functions.slider_setRestitution = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setSoftnessLimLin( params.linear || 0 );
+ constraint.setSoftnessLimAng( params.angular || 0 );
+};
+public_functions.slider_enableLinearMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setTargetLinMotorVelocity( params.velocity );
+ constraint.setMaxLinMotorForce( params.acceleration );
+ constraint.setPoweredLinMotor( true );
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.slider_disableLinearMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setPoweredLinMotor( false );
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.slider_enableAngularMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setTargetAngMotorVelocity( params.velocity );
+ constraint.setMaxAngMotorForce( params.acceleration );
+ constraint.setPoweredAngMotor( true );
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.slider_disableAngularMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setPoweredAngMotor( false );
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+
+public_functions.conetwist_setLimit = function( params ) {
+ _constraints[ params.constraint ].setLimit( params.z, params.y, params.x ); // ZYX order
+};
+public_functions.conetwist_enableMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.enableMotor( true );
+ constraint.getRigidBodyA().activate();
+ constraint.getRigidBodyB().activate();
+};
+public_functions.conetwist_setMaxMotorImpulse = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.setMaxMotorImpulse( params.max_impulse );
+ constraint.getRigidBodyA().activate();
+ constraint.getRigidBodyB().activate();
+};
+public_functions.conetwist_setMotorTarget = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ _quat.setX(params.x);
+ _quat.setY(params.y);
+ _quat.setZ(params.z);
+ _quat.setW(params.w);
+
+ constraint.setMotorTarget(_quat);
+
+ constraint.getRigidBodyA().activate();
+ constraint.getRigidBodyB().activate();
+};
+public_functions.conetwist_disableMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+ constraint.enableMotor( false );
+ constraint.getRigidBodyA().activate();
+ constraint.getRigidBodyB().activate();
+};
+
+public_functions.dof_setLinearLowerLimit = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ _vec3_1.setX(params.x);
+ _vec3_1.setY(params.y);
+ _vec3_1.setZ(params.z);
+
+ constraint.setLinearLowerLimit(_vec3_1);
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.dof_setLinearUpperLimit = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ _vec3_1.setX(params.x);
+ _vec3_1.setY(params.y);
+ _vec3_1.setZ(params.z);
+
+ constraint.setLinearUpperLimit(_vec3_1);
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.dof_setAngularLowerLimit = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ _vec3_1.setX(params.x);
+ _vec3_1.setY(params.y);
+ _vec3_1.setZ(params.z);
+
+ constraint.setAngularLowerLimit(_vec3_1);
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.dof_setAngularUpperLimit = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ _vec3_1.setX(params.x);
+ _vec3_1.setY(params.y);
+ _vec3_1.setZ(params.z);
+
+ constraint.setAngularUpperLimit(_vec3_1);
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.dof_enableAngularMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ var motor = constraint.getRotationalLimitMotor( params.which );
+ motor.set_m_enableMotor( true );
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.dof_configureAngularMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ var motor = constraint.getRotationalLimitMotor( params.which );
+
+ motor.set_m_loLimit( params.low_angle );
+ motor.set_m_hiLimit( params.high_angle );
+ motor.set_m_targetVelocity( params.velocity );
+ motor.set_m_maxMotorForce( params.max_force );
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+public_functions.dof_disableAngularMotor = function( params ) {
+ var constraint = _constraints[ params.constraint ];
+
+ var motor = constraint.getRotationalLimitMotor( params.which );
+ motor.set_m_enableMotor( false );
+
+ constraint.getRigidBodyA().activate();
+ if ( constraint.getRigidBodyB() ) {
+ constraint.getRigidBodyB().activate();
+ }
+};
+
+reportWorld = function() {
+ var index, object,
+ transform, origin, rotation,
+ offset = 0,
+ i = 0;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ if ( worldreport.length < 2 + _num_objects * WORLDREPORT_ITEMSIZE ) {
+ worldreport = new Float32Array(
+ 2 + // message id & # objects in report
+ ( Math.ceil( _num_objects / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * WORLDREPORT_ITEMSIZE // # of values needed * item size
+ );
+ worldreport[0] = MESSAGE_TYPES.WORLDREPORT;
+ }
+ }
+
+ worldreport[1] = _num_objects; // record how many objects we're reporting on
+
+ //for ( i = 0; i < worldreport[1]; i++ ) {
+ for ( index in _objects ) {
+ if ( _objects.hasOwnProperty( index ) ) {
+ object = _objects[index];
+
+ // #TODO: we can't use center of mass transform when center of mass can change,
+ // but getMotionState().getWorldTransform() screws up on objects that have been moved
+ //object.getMotionState().getWorldTransform( transform );
+ transform = object.getCenterOfMassTransform();
+
+ origin = transform.getOrigin();
+ rotation = transform.getRotation();
+
+ // add values to report
+ offset = 2 + (i++) * WORLDREPORT_ITEMSIZE;
+
+ worldreport[ offset ] = object.id;
+
+ worldreport[ offset + 1 ] = origin.x();
+ worldreport[ offset + 2 ] = origin.y();
+ worldreport[ offset + 3 ] = origin.z();
+
+ worldreport[ offset + 4 ] = rotation.x();
+ worldreport[ offset + 5 ] = rotation.y();
+ worldreport[ offset + 6 ] = rotation.z();
+ worldreport[ offset + 7 ] = rotation.w();
+
+ _vector = object.getLinearVelocity();
+ worldreport[ offset + 8 ] = _vector.x();
+ worldreport[ offset + 9 ] = _vector.y();
+ worldreport[ offset + 10 ] = _vector.z();
+
+ _vector = object.getAngularVelocity();
+ worldreport[ offset + 11 ] = _vector.x();
+ worldreport[ offset + 12 ] = _vector.y();
+ worldreport[ offset + 13 ] = _vector.z();
+ }
+ }
+
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ transferableMessage( worldreport.buffer, [worldreport.buffer] );
+ } else {
+ transferableMessage( worldreport );
+ }
+
+};
+
+reportCollisions = function() {
+ var i, offset,
+ dp = world.getDispatcher(),
+ num = dp.getNumManifolds(),
+ manifold, num_contacts, j, pt,
+ _collided = false;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ if ( collisionreport.length < 2 + num * COLLISIONREPORT_ITEMSIZE ) {
+ collisionreport = new Float32Array(
+ 2 + // message id & # objects in report
+ ( Math.ceil( _num_objects / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * COLLISIONREPORT_ITEMSIZE // # of values needed * item size
+ );
+ collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT;
+ }
+ }
+
+ collisionreport[1] = 0; // how many collisions we're reporting on
+
+ for ( i = 0; i < num; i++ ) {
+ manifold = dp.getManifoldByIndexInternal( i );
+
+ num_contacts = manifold.getNumContacts();
+ if ( num_contacts === 0 ) {
+ continue;
+ }
+
+ for ( j = 0; j < num_contacts; j++ ) {
+ pt = manifold.getContactPoint( j );
+ //if ( pt.getDistance() < 0 ) {
+ offset = 2 + (collisionreport[1]++) * COLLISIONREPORT_ITEMSIZE;
+ collisionreport[ offset ] = _objects_ammo[ manifold.getBody0() ];
+ collisionreport[ offset + 1 ] = _objects_ammo[ manifold.getBody1() ];
+
+ _vector = pt.get_m_normalWorldOnB();
+ collisionreport[ offset + 2 ] = _vector.x();
+ collisionreport[ offset + 3 ] = _vector.y();
+ collisionreport[ offset + 4 ] = _vector.z();
+ break;
+ //}
+
+ transferableMessage( _objects_ammo );
+
+ }
+ }
+
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ transferableMessage( collisionreport.buffer, [collisionreport.buffer] );
+ } else {
+ transferableMessage( collisionreport );
+ }
+};
+
+reportVehicles = function() {
+ var index, vehicle,
+ transform, origin, rotation,
+ offset = 0,
+ i = 0, j = 0;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ if ( vehiclereport.length < 2 + _num_wheels * VEHICLEREPORT_ITEMSIZE ) {
+ vehiclereport = new Float32Array(
+ 2 + // message id & # objects in report
+ ( Math.ceil( _num_wheels / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * VEHICLEREPORT_ITEMSIZE // # of values needed * item size
+ );
+ vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT;
+ }
+ }
+
+ for ( index in _vehicles ) {
+ if ( _vehicles.hasOwnProperty( index ) ) {
+ vehicle = _vehicles[index];
+
+ for ( j = 0; j < vehicle.getNumWheels(); j++ ) {
+
+ //vehicle.updateWheelTransform( j, true );
+
+ //transform = vehicle.getWheelTransformWS( j );
+ transform = vehicle.getWheelInfo( j ).get_m_worldTransform();
+
+ origin = transform.getOrigin();
+ rotation = transform.getRotation();
+
+ // add values to report
+ offset = 1 + (i++) * VEHICLEREPORT_ITEMSIZE;
+
+ vehiclereport[ offset ] = index;
+ vehiclereport[ offset + 1 ] = j;
+
+ vehiclereport[ offset + 2 ] = origin.x();
+ vehiclereport[ offset + 3 ] = origin.y();
+ vehiclereport[ offset + 4 ] = origin.z();
+
+ vehiclereport[ offset + 5 ] = rotation.x();
+ vehiclereport[ offset + 6 ] = rotation.y();
+ vehiclereport[ offset + 7 ] = rotation.z();
+ vehiclereport[ offset + 8 ] = rotation.w();
+
+ }
+
+ }
+ }
+
+ if ( j !== 0 ) {
+ if ( SUPPORT_TRANSFERABLE ) {
+ transferableMessage( vehiclereport.buffer, [vehiclereport.buffer] );
+ } else {
+ transferableMessage( vehiclereport );
+ }
+ }
+};
+
+reportConstraints = function() {
+ var index, constraint,
+ offset_body,
+ transform, origin,
+ offset = 0,
+ i = 0;
+
+ if ( SUPPORT_TRANSFERABLE ) {
+ if ( constraintreport.length < 2 + _num_constraints * CONSTRAINTREPORT_ITEMSIZE ) {
+ constraintreport = new Float32Array(
+ 2 + // message id & # objects in report
+ ( Math.ceil( _num_constraints / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * CONSTRAINTREPORT_ITEMSIZE // # of values needed * item size
+ );
+ constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT;
+ }
+ }
+
+ for ( index in _constraints ) {
+ if ( _constraints.hasOwnProperty( index ) ) {
+ constraint = _constraints[index];
+ offset_body = constraint.getRigidBodyA();
+ transform = constraint.getFrameOffsetA();
+ origin = transform.getOrigin();
+
+ // add values to report
+ offset = 1 + (i++) * CONSTRAINTREPORT_ITEMSIZE;
+
+ constraintreport[ offset ] = index;
+ constraintreport[ offset + 1 ] = offset_body.id;
+ constraintreport[ offset + 2 ] = origin.getX();
+ constraintreport[ offset + 3 ] = origin.getY();
+ constraintreport[ offset + 4 ] = origin.getZ();
+ constraintreport[ offset + 5 ] = constraint.getAppliedImpulse();
+ }
+ }
+
+
+ if ( i !== 0 ) {
+ if ( SUPPORT_TRANSFERABLE ) {
+ transferableMessage( constraintreport.buffer, [constraintreport.buffer] );
+ } else {
+ transferableMessage( constraintreport );
+ }
+ }
+
+};
+
+self.onmessage = function( event ) {
+
+ if ( event.data instanceof Float32Array ) {
+ // transferable object
+
+ switch ( event.data[0] ) {
+ case MESSAGE_TYPES.WORLDREPORT:
+ worldreport = new Float32Array( event.data );
+ break;
+
+ case MESSAGE_TYPES.COLLISIONREPORT:
+ collisionreport = new Float32Array( event.data );
+ break;
+
+ case MESSAGE_TYPES.VEHICLEREPORT:
+ vehiclereport = new Float32Array( event.data );
+ break;
+
+ case MESSAGE_TYPES.CONSTRAINTREPORT:
+ constraintreport = new Float32Array( event.data );
+ break;
+ }
+
+ return;
+ }
+
+ if ( event.data.cmd && public_functions[event.data.cmd] ) {
+ //if ( event.data.params.id !== undefined && _objects[event.data.params.id] === undefined && event.data.cmd !== 'addObject' && event.data.cmd !== 'registerMaterial' ) return;
+ public_functions[event.data.cmd]( event.data.params );
+ }
+
+};