From 8c2c5e52e0a1d78d36743270866ef5827aa8cd4e Mon Sep 17 00:00:00 2001 From: J08nY Date: Sat, 16 Apr 2016 15:34:14 +0200 Subject: first deployment --- physi_js/physi.js | 1403 +++++++++++++++++++++++++++++++++++++++++++ physi_js/physijs.d.ts | 250 ++++++++ physi_js/physijs_worker.js | 1415 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 3068 insertions(+) create mode 100644 physi_js/physi.js create mode 100644 physi_js/physijs.d.ts create mode 100644 physi_js/physijs_worker.js (limited to 'physi_js') 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 +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + + +/// + +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 ); + } + +}; -- cgit v1.2.3-70-g09d2