diff options
Diffstat (limited to 'physi_js/physi.js')
| -rw-r--r-- | physi_js/physi.js | 1403 |
1 files changed, 1403 insertions, 0 deletions
diff --git a/physi_js/physi.js b/physi_js/physi.js new file mode 100644 index 0000000..9d5b716 --- /dev/null +++ b/physi_js/physi.js @@ -0,0 +1,1403 @@ +window.Physijs = (function() { + 'use strict'; + + var SUPPORT_TRANSFERABLE, + _is_simulating = false, + _Physijs = Physijs, // used for noConflict method + Physijs = {}, // object assigned to window.Physijs + Eventable, // class to provide simple event methods + getObjectId, // returns a unique ID for a Physijs mesh object + getEulerXYZFromQuaternion, getQuatertionFromEuler, + convertWorldPositionToObject, // Converts a world-space position to object-space + addObjectChildren, + + _temp1, _temp2, + _temp_vector3_1 = new THREE.Vector3, + _temp_vector3_2 = new THREE.Vector3, + _temp_matrix4_1 = new THREE.Matrix4, + _quaternion_1 = new THREE.Quaternion, + + // constants + MESSAGE_TYPES = { + WORLDREPORT: 0, + COLLISIONREPORT: 1, + VEHICLEREPORT: 2, + CONSTRAINTREPORT: 3 + }, + REPORT_ITEMSIZE = 14, + COLLISIONREPORT_ITEMSIZE = 5, + VEHICLEREPORT_ITEMSIZE = 9, + CONSTRAINTREPORT_ITEMSIZE = 6; + + Physijs.scripts = {}; + + Eventable = function() { + this._eventListeners = {}; + }; + Eventable.prototype.addEventListener = function( event_name, callback ) { + if ( !this._eventListeners.hasOwnProperty( event_name ) ) { + this._eventListeners[event_name] = []; + } + this._eventListeners[event_name].push( callback ); + }; + Eventable.prototype.removeEventListener = function( event_name, callback ) { + var index; + + if ( !this._eventListeners.hasOwnProperty( event_name ) ) return false; + + if ( (index = this._eventListeners[event_name].indexOf( callback )) >= 0 ) { + this._eventListeners[event_name].splice( index, 1 ); + return true; + } + + return false; + }; + Eventable.prototype.dispatchEvent = function( event_name ) { + var i, + parameters = Array.prototype.splice.call( arguments, 1 ); + + if ( this._eventListeners.hasOwnProperty( event_name ) ) { + for ( i = 0; i < this._eventListeners[event_name].length; i++ ) { + this._eventListeners[event_name][i].apply( this, parameters ); + } + } + }; + Eventable.make = function( obj ) { + obj.prototype.addEventListener = Eventable.prototype.addEventListener; + obj.prototype.removeEventListener = Eventable.prototype.removeEventListener; + obj.prototype.dispatchEvent = Eventable.prototype.dispatchEvent; + }; + + getObjectId = (function() { + var _id = 1; + return function() { + return _id++; + }; + })(); + + getEulerXYZFromQuaternion = function ( x, y, z, w ) { + return new THREE.Vector3( + Math.atan2( 2 * ( x * w - y * z ), ( w * w - x * x - y * y + z * z ) ), + Math.asin( 2 * ( x * z + y * w ) ), + Math.atan2( 2 * ( z * w - x * y ), ( w * w + x * x - y * y - z * z ) ) + ); + }; + + getQuatertionFromEuler = function( x, y, z ) { + var c1, s1, c2, s2, c3, s3, c1c2, s1s2; + c1 = Math.cos( y ); + s1 = Math.sin( y ); + c2 = Math.cos( -z ); + s2 = Math.sin( -z ); + c3 = Math.cos( x ); + s3 = Math.sin( x ); + + c1c2 = c1 * c2; + s1s2 = s1 * s2; + + return { + w: c1c2 * c3 - s1s2 * s3, + x: c1c2 * s3 + s1s2 * c3, + y: s1 * c2 * c3 + c1 * s2 * s3, + z: c1 * s2 * c3 - s1 * c2 * s3 + }; + }; + + convertWorldPositionToObject = function( position, object ) { + _temp_matrix4_1.identity(); // reset temp matrix + + // Set the temp matrix's rotation to the object's rotation + _temp_matrix4_1.identity().makeRotationFromQuaternion( object.quaternion ); + + // Invert rotation matrix in order to "unrotate" a point back to object space + _temp_matrix4_1.getInverse( _temp_matrix4_1 ); + + // Yay! Temp vars! + _temp_vector3_1.copy( position ); + _temp_vector3_2.copy( object.position ); + + // Apply the rotation + + return _temp_vector3_1.sub( _temp_vector3_2 ).applyMatrix4( _temp_matrix4_1 ); + }; + + + + // Physijs.noConflict + Physijs.noConflict = function() { + window.Physijs = _Physijs; + return Physijs; + }; + + + // Physijs.createMaterial + Physijs.createMaterial = function( material, friction, restitution ) { + var physijs_material = function(){}; + physijs_material.prototype = material; + physijs_material = new physijs_material; + + physijs_material._physijs = { + id: material.id, + friction: friction === undefined ? .8 : friction, + restitution: restitution === undefined ? .2 : restitution + }; + + return physijs_material; + }; + + + // Constraints + Physijs.PointConstraint = function( objecta, objectb, position ) { + if ( position === undefined ) { + position = objectb; + objectb = undefined; + } + + this.type = 'point'; + this.appliedImpulse = 0; + this.id = getObjectId(); + this.objecta = objecta._physijs.id; + this.positiona = convertWorldPositionToObject( position, objecta ).clone(); + + if ( objectb ) { + this.objectb = objectb._physijs.id; + this.positionb = convertWorldPositionToObject( position, objectb ).clone(); + } + }; + Physijs.PointConstraint.prototype.getDefinition = function() { + return { + type: this.type, + id: this.id, + objecta: this.objecta, + objectb: this.objectb, + positiona: this.positiona, + positionb: this.positionb + }; + }; + + Physijs.HingeConstraint = function( objecta, objectb, position, axis ) { + if ( axis === undefined ) { + axis = position; + position = objectb; + objectb = undefined; + } + + this.type = 'hinge'; + this.appliedImpulse = 0; + this.id = getObjectId(); + this.scene = objecta.parent; + this.objecta = objecta._physijs.id; + this.positiona = convertWorldPositionToObject( position, objecta ).clone(); + this.position = position.clone(); + this.axis = axis; + + if ( objectb ) { + this.objectb = objectb._physijs.id; + this.positionb = convertWorldPositionToObject( position, objectb ).clone(); + } + }; + Physijs.HingeConstraint.prototype.getDefinition = function() { + return { + type: this.type, + id: this.id, + objecta: this.objecta, + objectb: this.objectb, + positiona: this.positiona, + positionb: this.positionb, + axis: this.axis + }; + }; + /* + * low = minimum angle in radians + * high = maximum angle in radians + * bias_factor = applied as a factor to constraint error + * relaxation_factor = controls bounce (0.0 == no bounce) + */ + Physijs.HingeConstraint.prototype.setLimits = function( low, high, bias_factor, relaxation_factor ) { + this.scene.execute( 'hinge_setLimits', { constraint: this.id, low: low, high: high, bias_factor: bias_factor, relaxation_factor: relaxation_factor } ); + }; + Physijs.HingeConstraint.prototype.enableAngularMotor = function( velocity, acceleration ) { + this.scene.execute( 'hinge_enableAngularMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } ); + }; + Physijs.HingeConstraint.prototype.disableMotor = function( velocity, acceleration ) { + this.scene.execute( 'hinge_disableMotor', { constraint: this.id } ); + }; + + Physijs.SliderConstraint = function( objecta, objectb, position, axis ) { + if ( axis === undefined ) { + axis = position; + position = objectb; + objectb = undefined; + } + + this.type = 'slider'; + this.appliedImpulse = 0; + this.id = getObjectId(); + this.scene = objecta.parent; + this.objecta = objecta._physijs.id; + this.positiona = convertWorldPositionToObject( position, objecta ).clone(); + this.axis = axis; + + if ( objectb ) { + this.objectb = objectb._physijs.id; + this.positionb = convertWorldPositionToObject( position, objectb ).clone(); + } + }; + Physijs.SliderConstraint.prototype.getDefinition = function() { + return { + type: this.type, + id: this.id, + objecta: this.objecta, + objectb: this.objectb, + positiona: this.positiona, + positionb: this.positionb, + axis: this.axis + }; + }; + Physijs.SliderConstraint.prototype.setLimits = function( lin_lower, lin_upper, ang_lower, ang_upper ) { + this.scene.execute( 'slider_setLimits', { constraint: this.id, lin_lower: lin_lower, lin_upper: lin_upper, ang_lower: ang_lower, ang_upper: ang_upper } ); + }; + Physijs.SliderConstraint.prototype.setRestitution = function( linear, angular ) { + this.scene.execute( + 'slider_setRestitution', + { + constraint: this.id, + linear: linear, + angular: angular + } + ); + }; + Physijs.SliderConstraint.prototype.enableLinearMotor = function( velocity, acceleration) { + this.scene.execute( 'slider_enableLinearMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } ); + }; + Physijs.SliderConstraint.prototype.disableLinearMotor = function() { + this.scene.execute( 'slider_disableLinearMotor', { constraint: this.id } ); + }; + Physijs.SliderConstraint.prototype.enableAngularMotor = function( velocity, acceleration ) { + this.scene.execute( 'slider_enableAngularMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } ); + }; + Physijs.SliderConstraint.prototype.disableAngularMotor = function() { + this.scene.execute( 'slider_disableAngularMotor', { constraint: this.id } ); + }; + + Physijs.ConeTwistConstraint = function( objecta, objectb, position ) { + if ( position === undefined ) { + throw 'Both objects must be defined in a ConeTwistConstraint.'; + } + this.type = 'conetwist'; + this.appliedImpulse = 0; + this.id = getObjectId(); + this.scene = objecta.parent; + this.objecta = objecta._physijs.id; + this.positiona = convertWorldPositionToObject( position, objecta ).clone(); + this.objectb = objectb._physijs.id; + this.positionb = convertWorldPositionToObject( position, objectb ).clone(); + this.axisa = { x: objecta.rotation.x, y: objecta.rotation.y, z: objecta.rotation.z }; + this.axisb = { x: objectb.rotation.x, y: objectb.rotation.y, z: objectb.rotation.z }; + }; + Physijs.ConeTwistConstraint.prototype.getDefinition = function() { + return { + type: this.type, + id: this.id, + objecta: this.objecta, + objectb: this.objectb, + positiona: this.positiona, + positionb: this.positionb, + axisa: this.axisa, + axisb: this.axisb + }; + }; + Physijs.ConeTwistConstraint.prototype.setLimit = function( x, y, z ) { + this.scene.execute( 'conetwist_setLimit', { constraint: this.id, x: x, y: y, z: z } ); + }; + Physijs.ConeTwistConstraint.prototype.enableMotor = function() { + this.scene.execute( 'conetwist_enableMotor', { constraint: this.id } ); + }; + Physijs.ConeTwistConstraint.prototype.setMaxMotorImpulse = function( max_impulse ) { + this.scene.execute( 'conetwist_setMaxMotorImpulse', { constraint: this.id, max_impulse: max_impulse } ); + }; + Physijs.ConeTwistConstraint.prototype.setMotorTarget = function( target ) { + if ( target instanceof THREE.Vector3 ) { + target = new THREE.Quaternion().setFromEuler( new THREE.Euler( target.x, target.y, target.z ) ); + } else if ( target instanceof THREE.Euler ) { + target = new THREE.Quaternion().setFromEuler( target ); + } else if ( target instanceof THREE.Matrix4 ) { + target = new THREE.Quaternion().setFromRotationMatrix( target ); + } + this.scene.execute( 'conetwist_setMotorTarget', { constraint: this.id, x: target.x, y: target.y, z: target.z, w: target.w } ); + }; + Physijs.ConeTwistConstraint.prototype.disableMotor = function() { + this.scene.execute( 'conetwist_disableMotor', { constraint: this.id } ); + }; + + Physijs.DOFConstraint = function( objecta, objectb, position ) { + if ( position === undefined ) { + position = objectb; + objectb = undefined; + } + this.type = 'dof'; + this.appliedImpulse = 0; + this.id = getObjectId(); + this.scene = objecta.parent; + this.objecta = objecta._physijs.id; + this.positiona = convertWorldPositionToObject( position, objecta ).clone(); + this.axisa = { x: objecta.rotation.x, y: objecta.rotation.y, z: objecta.rotation.z }; + + if ( objectb ) { + this.objectb = objectb._physijs.id; + this.positionb = convertWorldPositionToObject( position, objectb ).clone(); + this.axisb = { x: objectb.rotation.x, y: objectb.rotation.y, z: objectb.rotation.z }; + } + }; + Physijs.DOFConstraint.prototype.getDefinition = function() { + return { + type: this.type, + id: this.id, + objecta: this.objecta, + objectb: this.objectb, + positiona: this.positiona, + positionb: this.positionb, + axisa: this.axisa, + axisb: this.axisb + }; + }; + Physijs.DOFConstraint.prototype.setLinearLowerLimit = function( limit ) { + this.scene.execute( 'dof_setLinearLowerLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); + }; + Physijs.DOFConstraint.prototype.setLinearUpperLimit = function( limit ) { + this.scene.execute( 'dof_setLinearUpperLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); + }; + Physijs.DOFConstraint.prototype.setAngularLowerLimit = function( limit ) { + this.scene.execute( 'dof_setAngularLowerLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); + }; + Physijs.DOFConstraint.prototype.setAngularUpperLimit = function( limit ) { + this.scene.execute( 'dof_setAngularUpperLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); + }; + Physijs.DOFConstraint.prototype.enableAngularMotor = function( which ) { + this.scene.execute( 'dof_enableAngularMotor', { constraint: this.id, which: which } ); + }; + Physijs.DOFConstraint.prototype.configureAngularMotor = function( which, low_angle, high_angle, velocity, max_force ) { + this.scene.execute( 'dof_configureAngularMotor', { constraint: this.id, which: which, low_angle: low_angle, high_angle: high_angle, velocity: velocity, max_force: max_force } ); + }; + Physijs.DOFConstraint.prototype.disableAngularMotor = function( which ) { + this.scene.execute( 'dof_disableAngularMotor', { constraint: this.id, which: which } ); + }; + + // Physijs.Scene + Physijs.Scene = function( params ) { + var self = this; + + Eventable.call( this ); + THREE.Scene.call( this ); + + this._worker = new Worker( Physijs.scripts.worker || 'physijs_worker.js' ); + this._worker.transferableMessage = this._worker.webkitPostMessage || this._worker.postMessage; + this._materials_ref_counts = {}; + this._objects = {}; + this._vehicles = {}; + this._constraints = {}; + + var ab = new ArrayBuffer( 1 ); + this._worker.transferableMessage( ab, [ab] ); + SUPPORT_TRANSFERABLE = ( ab.byteLength === 0 ); + + this._worker.onmessage = function ( event ) { + var _temp, + data = event.data; + + if ( data instanceof ArrayBuffer && data.byteLength !== 1 ) { // byteLength === 1 is the worker making a SUPPORT_TRANSFERABLE test + data = new Float32Array( data ); + } + + if ( data instanceof Float32Array ) { + + // transferable object + switch ( data[0] ) { + case MESSAGE_TYPES.WORLDREPORT: + self._updateScene( data ); + break; + + case MESSAGE_TYPES.COLLISIONREPORT: + self._updateCollisions( data ); + break; + + case MESSAGE_TYPES.VEHICLEREPORT: + self._updateVehicles( data ); + break; + + case MESSAGE_TYPES.CONSTRAINTREPORT: + self._updateConstraints( data ); + break; + } + + } else { + + if ( data.cmd ) { + + // non-transferable object + switch ( data.cmd ) { + case 'objectReady': + _temp = data.params; + if ( self._objects[ _temp ] ) { + self._objects[ _temp ].dispatchEvent( 'ready' ); + } + break; + + case 'worldReady': + self.dispatchEvent( 'ready' ); + break; + + case 'vehicle': + window.test = data; + break; + + default: + // Do nothing, just show the message + console.debug('Received: ' + data.cmd); + console.dir(data.params); + break; + } + + } else { + + switch ( data[0] ) { + case MESSAGE_TYPES.WORLDREPORT: + self._updateScene( data ); + break; + + case MESSAGE_TYPES.COLLISIONREPORT: + self._updateCollisions( data ); + break; + + case MESSAGE_TYPES.VEHICLEREPORT: + self._updateVehicles( data ); + break; + + case MESSAGE_TYPES.CONSTRAINTREPORT: + self._updateConstraints( data ); + break; + } + + } + + } + }; + + + params = params || {}; + params.ammo = Physijs.scripts.ammo || 'ammo.js'; + params.fixedTimeStep = params.fixedTimeStep || 1 / 60; + params.rateLimit = params.rateLimit || true; + this.execute( 'init', params ); + }; + Physijs.Scene.prototype = new THREE.Scene; + Physijs.Scene.prototype.constructor = Physijs.Scene; + Eventable.make( Physijs.Scene ); + + Physijs.Scene.prototype._updateScene = function( data ) { + var num_objects = data[1], + object, + i, offset; + + for ( i = 0; i < num_objects; i++ ) { + offset = 2 + i * REPORT_ITEMSIZE; + object = this._objects[ data[ offset ] ]; + + if ( object === undefined ) { + continue; + } + + if ( object.__dirtyPosition === false ) { + object.position.set( + data[ offset + 1 ], + data[ offset + 2 ], + data[ offset + 3 ] + ); + } + + if ( object.__dirtyRotation === false ) { + object.quaternion.set( + data[ offset + 4 ], + data[ offset + 5 ], + data[ offset + 6 ], + data[ offset + 7 ] + ); + } + + object._physijs.linearVelocity.set( + data[ offset + 8 ], + data[ offset + 9 ], + data[ offset + 10 ] + ); + + object._physijs.angularVelocity.set( + data[ offset + 11 ], + data[ offset + 12 ], + data[ offset + 13 ] + ); + + } + + if ( SUPPORT_TRANSFERABLE ) { + // Give the typed array back to the worker + this._worker.transferableMessage( data.buffer, [data.buffer] ); + } + + _is_simulating = false; + this.dispatchEvent( 'update' ); + }; + + Physijs.Scene.prototype._updateVehicles = function( data ) { + var vehicle, wheel, + i, offset; + + for ( i = 0; i < ( data.length - 1 ) / VEHICLEREPORT_ITEMSIZE; i++ ) { + offset = 1 + i * VEHICLEREPORT_ITEMSIZE; + vehicle = this._vehicles[ data[ offset ] ]; + + if ( vehicle === undefined ) { + continue; + } + + wheel = vehicle.wheels[ data[ offset + 1 ] ]; + + wheel.position.set( + data[ offset + 2 ], + data[ offset + 3 ], + data[ offset + 4 ] + ); + + wheel.quaternion.set( + data[ offset + 5 ], + data[ offset + 6 ], + data[ offset + 7 ], + data[ offset + 8 ] + ); + } + + if ( SUPPORT_TRANSFERABLE ) { + // Give the typed array back to the worker + this._worker.transferableMessage( data.buffer, [data.buffer] ); + } + }; + + Physijs.Scene.prototype._updateConstraints = function( data ) { + var constraint, object, + i, offset; + + for ( i = 0; i < ( data.length - 1 ) / CONSTRAINTREPORT_ITEMSIZE; i++ ) { + offset = 1 + i * CONSTRAINTREPORT_ITEMSIZE; + constraint = this._constraints[ data[ offset ] ]; + object = this._objects[ data[ offset + 1 ] ]; + + if ( constraint === undefined || object === undefined ) { + continue; + } + + _temp_vector3_1.set( + data[ offset + 2 ], + data[ offset + 3 ], + data[ offset + 4 ] + ); + _temp_matrix4_1.extractRotation( object.matrix ); + _temp_vector3_1.applyMatrix4( _temp_matrix4_1 ); + + constraint.positiona.addVectors( object.position, _temp_vector3_1 ); + constraint.appliedImpulse = data[ offset + 5 ] ; + } + + if ( SUPPORT_TRANSFERABLE ) { + // Give the typed array back to the worker + this._worker.transferableMessage( data.buffer, [data.buffer] ); + } + }; + + Physijs.Scene.prototype._updateCollisions = function( data ) { + /** + * #TODO + * This is probably the worst way ever to handle collisions. The inherent evilness is a residual + * effect from the previous version's evilness which mutated when switching to transferable objects. + * + * If you feel inclined to make this better, please do so. + */ + + var i, j, offset, object, object2, id1, id2, + collisions = {}, normal_offsets = {}; + + // Build collision manifest + for ( i = 0; i < data[1]; i++ ) { + offset = 2 + i * COLLISIONREPORT_ITEMSIZE; + object = data[ offset ]; + object2 = data[ offset + 1 ]; + + normal_offsets[ object + '-' + object2 ] = offset + 2; + normal_offsets[ object2 + '-' + object ] = -1 * ( offset + 2 ); + + // Register collisions for both the object colliding and the object being collided with + if ( !collisions[ object ] ) collisions[ object ] = []; + collisions[ object ].push( object2 ); + + if ( !collisions[ object2 ] ) collisions[ object2 ] = []; + collisions[ object2 ].push( object ); + } + + // Deal with collisions + for ( id1 in this._objects ) { + if ( !this._objects.hasOwnProperty( id1 ) ) continue; + object = this._objects[ id1 ]; + + // If object touches anything, ... + if ( collisions[ id1 ] ) { + + // Clean up touches array + for ( j = 0; j < object._physijs.touches.length; j++ ) { + if ( collisions[ id1 ].indexOf( object._physijs.touches[j] ) === -1 ) { + object._physijs.touches.splice( j--, 1 ); + } + } + + // Handle each colliding object + for ( j = 0; j < collisions[ id1 ].length; j++ ) { + id2 = collisions[ id1 ][ j ]; + object2 = this._objects[ id2 ]; + + if ( object2 ) { + // If object was not already touching object2, notify object + if ( object._physijs.touches.indexOf( id2 ) === -1 ) { + object._physijs.touches.push( id2 ); + + _temp_vector3_1.subVectors( object.getLinearVelocity(), object2.getLinearVelocity() ); + _temp1 = _temp_vector3_1.clone(); + + _temp_vector3_1.subVectors( object.getAngularVelocity(), object2.getAngularVelocity() ); + _temp2 = _temp_vector3_1.clone(); + + var normal_offset = normal_offsets[ object._physijs.id + '-' + object2._physijs.id ]; + if ( normal_offset > 0 ) { + _temp_vector3_1.set( + -data[ normal_offset ], + -data[ normal_offset + 1 ], + -data[ normal_offset + 2 ] + ); + } else { + normal_offset *= -1; + _temp_vector3_1.set( + data[ normal_offset ], + data[ normal_offset + 1 ], + data[ normal_offset + 2 ] + ); + } + + object.dispatchEvent( 'collision', object2, _temp1, _temp2, _temp_vector3_1 ); + } + } + } + + } else { + + // not touching other objects + object._physijs.touches.length = 0; + + } + + } + + this.collisions = collisions; + + if ( SUPPORT_TRANSFERABLE ) { + // Give the typed array back to the worker + this._worker.transferableMessage( data.buffer, [data.buffer] ); + } + }; + + Physijs.Scene.prototype.addConstraint = function ( constraint, show_marker ) { + this._constraints[ constraint.id ] = constraint; + this.execute( 'addConstraint', constraint.getDefinition() ); + + if ( show_marker ) { + var marker; + + switch ( constraint.type ) { + case 'point': + marker = new THREE.Mesh( + new THREE.SphereGeometry( 1.5 ), + new THREE.MeshNormalMaterial + ); + marker.position.copy( constraint.positiona ); + this._objects[ constraint.objecta ].add( marker ); + break; + + case 'hinge': + marker = new THREE.Mesh( + new THREE.SphereGeometry( 1.5 ), + new THREE.MeshNormalMaterial + ); + marker.position.copy( constraint.positiona ); + this._objects[ constraint.objecta ].add( marker ); + break; + + case 'slider': + marker = new THREE.Mesh( + new THREE.BoxGeometry( 10, 1, 1 ), + new THREE.MeshNormalMaterial + ); + marker.position.copy( constraint.positiona ); + // This rotation isn't right if all three axis are non-0 values + // TODO: change marker's rotation order to ZYX + marker.rotation.set( + constraint.axis.y, // yes, y and + constraint.axis.x, // x axis are swapped + constraint.axis.z + ); + this._objects[ constraint.objecta ].add( marker ); + break; + + case 'conetwist': + marker = new THREE.Mesh( + new THREE.SphereGeometry( 1.5 ), + new THREE.MeshNormalMaterial + ); + marker.position.copy( constraint.positiona ); + this._objects[ constraint.objecta ].add( marker ); + break; + + case 'dof': + marker = new THREE.Mesh( + new THREE.SphereGeometry( 1.5 ), + new THREE.MeshNormalMaterial + ); + marker.position.copy( constraint.positiona ); + this._objects[ constraint.objecta ].add( marker ); + break; + } + } + + return constraint; + }; + + Physijs.Scene.prototype.onSimulationResume = function() { + this.execute( 'onSimulationResume', { } ); + }; + + Physijs.Scene.prototype.removeConstraint = function( constraint ) { + if ( this._constraints[constraint.id ] !== undefined ) { + this.execute( 'removeConstraint', { id: constraint.id } ); + delete this._constraints[ constraint.id ]; + } + }; + + Physijs.Scene.prototype.execute = function( cmd, params ) { + this._worker.postMessage({ cmd: cmd, params: params }); + }; + + addObjectChildren = function( parent, object ) { + var i; + + for ( i = 0; i < object.children.length; i++ ) { + if ( object.children[i]._physijs ) { + object.children[i].updateMatrix(); + object.children[i].updateMatrixWorld(); + + _temp_vector3_1.setFromMatrixPosition( object.children[i].matrixWorld ); + _quaternion_1.setFromRotationMatrix( object.children[i].matrixWorld ); + + object.children[i]._physijs.position_offset = { + x: _temp_vector3_1.x, + y: _temp_vector3_1.y, + z: _temp_vector3_1.z + }; + + object.children[i]._physijs.rotation = { + x: _quaternion_1.x, + y: _quaternion_1.y, + z: _quaternion_1.z, + w: _quaternion_1.w + }; + + parent._physijs.children.push( object.children[i]._physijs ); + } + + addObjectChildren( parent, object.children[i] ); + } + }; + + Physijs.Scene.prototype.add = function( object ) { + THREE.Mesh.prototype.add.call( this, object ); + + if ( object._physijs ) { + + object.world = this; + + if ( object instanceof Physijs.Vehicle ) { + + this.add( object.mesh ); + this._vehicles[ object._physijs.id ] = object; + this.execute( 'addVehicle', object._physijs ); + + } else { + + object.__dirtyPosition = false; + object.__dirtyRotation = false; + this._objects[object._physijs.id] = object; + + if ( object.children.length ) { + object._physijs.children = []; + addObjectChildren( object, object ); + } + + if ( object.material._physijs ) { + if ( !this._materials_ref_counts.hasOwnProperty( object.material._physijs.id ) ) { + this.execute( 'registerMaterial', object.material._physijs ); + object._physijs.materialId = object.material._physijs.id; + this._materials_ref_counts[object.material._physijs.id] = 1; + } else { + this._materials_ref_counts[object.material._physijs.id]++; + } + } + + // Object starting position + rotation + object._physijs.position = { x: object.position.x, y: object.position.y, z: object.position.z }; + object._physijs.rotation = { x: object.quaternion.x, y: object.quaternion.y, z: object.quaternion.z, w: object.quaternion.w }; + + // Check for scaling + var mass_scaling = new THREE.Vector3( 1, 1, 1 ); + if ( object._physijs.width ) { + object._physijs.width *= object.scale.x; + } + if ( object._physijs.height ) { + object._physijs.height *= object.scale.y; + } + if ( object._physijs.depth ) { + object._physijs.depth *= object.scale.z; + } + + this.execute( 'addObject', object._physijs ); + + } + } + }; + + Physijs.Scene.prototype.remove = function( object ) { + if ( object instanceof Physijs.Vehicle ) { + this.execute( 'removeVehicle', { id: object._physijs.id } ); + while( object.wheels.length ) { + this.remove( object.wheels.pop() ); + } + this.remove( object.mesh ); + delete this._vehicles[ object._physijs.id ]; + } else { + THREE.Mesh.prototype.remove.call( this, object ); + if ( object._physijs ) { + delete this._objects[object._physijs.id]; + this.execute( 'removeObject', { id: object._physijs.id } ); + } + } + if ( object.material && object.material._physijs && this._materials_ref_counts.hasOwnProperty( object.material._physijs.id ) ) { + this._materials_ref_counts[object.material._physijs.id]--; + if(this._materials_ref_counts[object.material._physijs.id] == 0) { + this.execute( 'unRegisterMaterial', object.material._physijs ); + delete this._materials_ref_counts[object.material._physijs.id]; + } + } + }; + + Physijs.Scene.prototype.setFixedTimeStep = function( fixedTimeStep ) { + if ( fixedTimeStep ) { + this.execute( 'setFixedTimeStep', fixedTimeStep ); + } + }; + + Physijs.Scene.prototype.setGravity = function( gravity ) { + if ( gravity ) { + this.execute( 'setGravity', gravity ); + } + }; + + Physijs.Scene.prototype.simulate = function( timeStep, maxSubSteps ) { + var object_id, object, update; + + if ( _is_simulating ) { + return false; + } + + _is_simulating = true; + + for ( object_id in this._objects ) { + if ( !this._objects.hasOwnProperty( object_id ) ) continue; + + object = this._objects[object_id]; + + if ( object.__dirtyPosition || object.__dirtyRotation ) { + update = { id: object._physijs.id }; + + if ( object.__dirtyPosition ) { + update.pos = { x: object.position.x, y: object.position.y, z: object.position.z }; + object.__dirtyPosition = false; + } + + if ( object.__dirtyRotation ) { + update.quat = { x: object.quaternion.x, y: object.quaternion.y, z: object.quaternion.z, w: object.quaternion.w }; + object.__dirtyRotation = false; + } + + this.execute( 'updateTransform', update ); + } + } + + this.execute( 'simulate', { timeStep: timeStep, maxSubSteps: maxSubSteps } ); + + return true; + }; + + + // Phsijs.Mesh + Physijs.Mesh = function ( geometry, material, mass ) { + var index; + + if ( !geometry ) { + return; + } + + Eventable.call( this ); + THREE.Mesh.call( this, geometry, material ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + this._physijs = { + type: null, + id: getObjectId(), + mass: mass || 0, + touches: [], + linearVelocity: new THREE.Vector3, + angularVelocity: new THREE.Vector3 + }; + }; + Physijs.Mesh.prototype = new THREE.Mesh; + Physijs.Mesh.prototype.constructor = Physijs.Mesh; + Eventable.make( Physijs.Mesh ); + + // Physijs.Mesh.mass + Physijs.Mesh.prototype.__defineGetter__('mass', function() { + return this._physijs.mass; + }); + Physijs.Mesh.prototype.__defineSetter__('mass', function( mass ) { + this._physijs.mass = mass; + if ( this.world ) { + this.world.execute( 'updateMass', { id: this._physijs.id, mass: mass } ); + } + }); + + // Physijs.Mesh.applyCentralImpulse + Physijs.Mesh.prototype.applyCentralImpulse = function ( force ) { + if ( this.world ) { + this.world.execute( 'applyCentralImpulse', { id: this._physijs.id, x: force.x, y: force.y, z: force.z } ); + } + }; + + // Physijs.Mesh.applyImpulse + Physijs.Mesh.prototype.applyImpulse = function ( force, offset ) { + if ( this.world ) { + this.world.execute( 'applyImpulse', { id: this._physijs.id, impulse_x: force.x, impulse_y: force.y, impulse_z: force.z, x: offset.x, y: offset.y, z: offset.z } ); + } + }; + + // Physijs.Mesh.applyTorque + Physijs.Mesh.prototype.applyTorque = function ( force ) { + if ( this.world ) { + this.world.execute( 'applyTorque', { id: this._physijs.id, torque_x: force.x, torque_y: force.y, torque_z: force.z } ); + } + }; + + // Physijs.Mesh.applyCentralForce + Physijs.Mesh.prototype.applyCentralForce = function ( force ) { + if ( this.world ) { + this.world.execute( 'applyCentralForce', { id: this._physijs.id, x: force.x, y: force.y, z: force.z } ); + } + }; + + // Physijs.Mesh.applyForce + Physijs.Mesh.prototype.applyForce = function ( force, offset ) { + if ( this.world ) { + this.world.execute( 'applyForce', { id: this._physijs.id, force_x: force.x, force_y : force.y, force_z : force.z, x: offset.x, y: offset.y, z: offset.z } ); + } + }; + + // Physijs.Mesh.getAngularVelocity + Physijs.Mesh.prototype.getAngularVelocity = function () { + return this._physijs.angularVelocity; + }; + + // Physijs.Mesh.setAngularVelocity + Physijs.Mesh.prototype.setAngularVelocity = function ( velocity ) { + if ( this.world ) { + this.world.execute( 'setAngularVelocity', { id: this._physijs.id, x: velocity.x, y: velocity.y, z: velocity.z } ); + } + }; + + // Physijs.Mesh.getLinearVelocity + Physijs.Mesh.prototype.getLinearVelocity = function () { + return this._physijs.linearVelocity; + }; + + // Physijs.Mesh.setLinearVelocity + Physijs.Mesh.prototype.setLinearVelocity = function ( velocity ) { + if ( this.world ) { + this.world.execute( 'setLinearVelocity', { id: this._physijs.id, x: velocity.x, y: velocity.y, z: velocity.z } ); + } + }; + + // Physijs.Mesh.setAngularFactor + Physijs.Mesh.prototype.setAngularFactor = function ( factor ) { + if ( this.world ) { + this.world.execute( 'setAngularFactor', { id: this._physijs.id, x: factor.x, y: factor.y, z: factor.z } ); + } + }; + + // Physijs.Mesh.setLinearFactor + Physijs.Mesh.prototype.setLinearFactor = function ( factor ) { + if ( this.world ) { + this.world.execute( 'setLinearFactor', { id: this._physijs.id, x: factor.x, y: factor.y, z: factor.z } ); + } + }; + + // Physijs.Mesh.setDamping + Physijs.Mesh.prototype.setDamping = function ( linear, angular ) { + if ( this.world ) { + this.world.execute( 'setDamping', { id: this._physijs.id, linear: linear, angular: angular } ); + } + }; + + // Physijs.Mesh.setCcdMotionThreshold + Physijs.Mesh.prototype.setCcdMotionThreshold = function ( threshold ) { + if ( this.world ) { + this.world.execute( 'setCcdMotionThreshold', { id: this._physijs.id, threshold: threshold } ); + } + }; + + // Physijs.Mesh.setCcdSweptSphereRadius + Physijs.Mesh.prototype.setCcdSweptSphereRadius = function ( radius ) { + if ( this.world ) { + this.world.execute( 'setCcdSweptSphereRadius', { id: this._physijs.id, radius: radius } ); + } + }; + + + // Physijs.PlaneMesh + Physijs.PlaneMesh = function ( geometry, material, mass ) { + var width, height; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + + this._physijs.type = 'plane'; + this._physijs.normal = geometry.faces[0].normal.clone(); + this._physijs.mass = (typeof mass === 'undefined') ? width * height : mass; + }; + Physijs.PlaneMesh.prototype = new Physijs.Mesh; + Physijs.PlaneMesh.prototype.constructor = Physijs.PlaneMesh; + + // Physijs.HeightfieldMesh + Physijs.HeightfieldMesh = function ( geometry, material, mass, xdiv, ydiv) { + Physijs.Mesh.call( this, geometry, material, mass ); + + this._physijs.type = 'heightfield'; + this._physijs.xsize = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + this._physijs.ysize = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + this._physijs.xpts = (typeof xdiv === 'undefined') ? Math.sqrt(geometry.vertices.length) : xdiv + 1; + this._physijs.ypts = (typeof ydiv === 'undefined') ? Math.sqrt(geometry.vertices.length) : ydiv + 1; + // note - this assumes our plane geometry is square, unless we pass in specific xdiv and ydiv + this._physijs.absMaxHeight = Math.max(geometry.boundingBox.max.z,Math.abs(geometry.boundingBox.min.z)); + + var points = []; + + var a, b; + for ( var i = 0; i < geometry.vertices.length; i++ ) { + + a = i % this._physijs.xpts; + b = Math.round( ( i / this._physijs.xpts ) - ( (i % this._physijs.xpts) / this._physijs.xpts ) ); + points[i] = geometry.vertices[ a + ( ( this._physijs.ypts - b - 1 ) * this._physijs.ypts ) ].z; + + //points[i] = geometry.vertices[i]; + } + + this._physijs.points = points; + }; + Physijs.HeightfieldMesh.prototype = new Physijs.Mesh; + Physijs.HeightfieldMesh.prototype.constructor = Physijs.HeightfieldMesh; + + // Physijs.BoxMesh + Physijs.BoxMesh = function( geometry, material, mass ) { + var width, height, depth; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; + + this._physijs.type = 'box'; + this._physijs.width = width; + this._physijs.height = height; + this._physijs.depth = depth; + this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; + }; + Physijs.BoxMesh.prototype = new Physijs.Mesh; + Physijs.BoxMesh.prototype.constructor = Physijs.BoxMesh; + + + // Physijs.SphereMesh + Physijs.SphereMesh = function( geometry, material, mass ) { + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingSphere ) { + geometry.computeBoundingSphere(); + } + + this._physijs.type = 'sphere'; + this._physijs.radius = geometry.boundingSphere.radius; + this._physijs.mass = (typeof mass === 'undefined') ? (4/3) * Math.PI * Math.pow(this._physijs.radius, 3) : mass; + }; + Physijs.SphereMesh.prototype = new Physijs.Mesh; + Physijs.SphereMesh.prototype.constructor = Physijs.SphereMesh; + + + // Physijs.CylinderMesh + Physijs.CylinderMesh = function( geometry, material, mass ) { + var width, height, depth; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; + + this._physijs.type = 'cylinder'; + this._physijs.width = width; + this._physijs.height = height; + this._physijs.depth = depth; + this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; + }; + Physijs.CylinderMesh.prototype = new Physijs.Mesh; + Physijs.CylinderMesh.prototype.constructor = Physijs.CylinderMesh; + + + // Physijs.CapsuleMesh + Physijs.CapsuleMesh = function( geometry, material, mass ) { + var width, height, depth; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; + + this._physijs.type = 'capsule'; + this._physijs.radius = Math.max(width / 2, depth / 2); + this._physijs.height = height; + this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; + }; + Physijs.CapsuleMesh.prototype = new Physijs.Mesh; + Physijs.CapsuleMesh.prototype.constructor = Physijs.CapsuleMesh; + + + // Physijs.ConeMesh + Physijs.ConeMesh = function( geometry, material, mass ) { + var width, height, depth; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + + this._physijs.type = 'cone'; + this._physijs.radius = width / 2; + this._physijs.height = height; + this._physijs.mass = (typeof mass === 'undefined') ? width * height : mass; + }; + Physijs.ConeMesh.prototype = new Physijs.Mesh; + Physijs.ConeMesh.prototype.constructor = Physijs.ConeMesh; + + + // Physijs.ConcaveMesh + Physijs.ConcaveMesh = function( geometry, material, mass ) { + var i, + width, height, depth, + vertices, face, triangles = []; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + vertices = geometry.vertices; + + for ( i = 0; i < geometry.faces.length; i++ ) { + face = geometry.faces[i]; + if ( face instanceof THREE.Face3) { + + triangles.push([ + { x: vertices[face.a].x, y: vertices[face.a].y, z: vertices[face.a].z }, + { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z }, + { x: vertices[face.c].x, y: vertices[face.c].y, z: vertices[face.c].z } + ]); + + } else if ( face instanceof THREE.Face4 ) { + + triangles.push([ + { x: vertices[face.a].x, y: vertices[face.a].y, z: vertices[face.a].z }, + { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z }, + { x: vertices[face.d].x, y: vertices[face.d].y, z: vertices[face.d].z } + ]); + triangles.push([ + { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z }, + { x: vertices[face.c].x, y: vertices[face.c].y, z: vertices[face.c].z }, + { x: vertices[face.d].x, y: vertices[face.d].y, z: vertices[face.d].z } + ]); + + } + } + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; + + this._physijs.type = 'concave'; + this._physijs.triangles = triangles; + this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; + }; + Physijs.ConcaveMesh.prototype = new Physijs.Mesh; + Physijs.ConcaveMesh.prototype.constructor = Physijs.ConcaveMesh; + + + // Physijs.ConvexMesh + Physijs.ConvexMesh = function( geometry, material, mass ) { + var i, + width, height, depth, + points = []; + + Physijs.Mesh.call( this, geometry, material, mass ); + + if ( !geometry.boundingBox ) { + geometry.computeBoundingBox(); + } + + for ( i = 0; i < geometry.vertices.length; i++ ) { + points.push({ + x: geometry.vertices[i].x, + y: geometry.vertices[i].y, + z: geometry.vertices[i].z + }); + } + + + width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; + + this._physijs.type = 'convex'; + this._physijs.points = points; + this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; + }; + Physijs.ConvexMesh.prototype = new Physijs.Mesh; + Physijs.ConvexMesh.prototype.constructor = Physijs.ConvexMesh; + + + // Physijs.Vehicle + Physijs.Vehicle = function( mesh, tuning ) { + tuning = tuning || new Physijs.VehicleTuning; + this.mesh = mesh; + this.wheels = []; + this._physijs = { + id: getObjectId(), + rigidBody: mesh._physijs.id, + suspension_stiffness: tuning.suspension_stiffness, + suspension_compression: tuning.suspension_compression, + suspension_damping: tuning.suspension_damping, + max_suspension_travel: tuning.max_suspension_travel, + friction_slip: tuning.friction_slip, + max_suspension_force: tuning.max_suspension_force + }; + }; + Physijs.Vehicle.prototype.addWheel = function( wheel_geometry, wheel_material, connection_point, wheel_direction, wheel_axle, suspension_rest_length, wheel_radius, is_front_wheel, tuning ) { + var wheel = new THREE.Mesh( wheel_geometry, wheel_material ); + wheel.castShadow = wheel.receiveShadow = true; + wheel.position.copy( wheel_direction ).multiplyScalar( suspension_rest_length / 100 ).add( connection_point ); + this.world.add( wheel ); + this.wheels.push( wheel ); + + this.world.execute( 'addWheel', { + id: this._physijs.id, + connection_point: { x: connection_point.x, y: connection_point.y, z: connection_point.z }, + wheel_direction: { x: wheel_direction.x, y: wheel_direction.y, z: wheel_direction.z }, + wheel_axle: { x: wheel_axle.x, y: wheel_axle.y, z: wheel_axle.z }, + suspension_rest_length: suspension_rest_length, + wheel_radius: wheel_radius, + is_front_wheel: is_front_wheel, + tuning: tuning + }); + }; + Physijs.Vehicle.prototype.setSteering = function( amount, wheel ) { + if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) { + this.world.execute( 'setSteering', { id: this._physijs.id, wheel: wheel, steering: amount } ); + } else if ( this.wheels.length > 0 ) { + for ( var i = 0; i < this.wheels.length; i++ ) { + this.world.execute( 'setSteering', { id: this._physijs.id, wheel: i, steering: amount } ); + } + } + }; + Physijs.Vehicle.prototype.setBrake = function( amount, wheel ) { + if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) { + this.world.execute( 'setBrake', { id: this._physijs.id, wheel: wheel, brake: amount } ); + } else if ( this.wheels.length > 0 ) { + for ( var i = 0; i < this.wheels.length; i++ ) { + this.world.execute( 'setBrake', { id: this._physijs.id, wheel: i, brake: amount } ); + } + } + }; + Physijs.Vehicle.prototype.applyEngineForce = function( amount, wheel ) { + if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) { + this.world.execute( 'applyEngineForce', { id: this._physijs.id, wheel: wheel, force: amount } ); + } else if ( this.wheels.length > 0 ) { + for ( var i = 0; i < this.wheels.length; i++ ) { + this.world.execute( 'applyEngineForce', { id: this._physijs.id, wheel: i, force: amount } ); + } + } + }; + + // Physijs.VehicleTuning + Physijs.VehicleTuning = function( suspension_stiffness, suspension_compression, suspension_damping, max_suspension_travel, friction_slip, max_suspension_force ) { + this.suspension_stiffness = suspension_stiffness !== undefined ? suspension_stiffness : 5.88; + this.suspension_compression = suspension_compression !== undefined ? suspension_compression : 0.83; + this.suspension_damping = suspension_damping !== undefined ? suspension_damping : 0.88; + this.max_suspension_travel = max_suspension_travel !== undefined ? max_suspension_travel : 500; + this.friction_slip = friction_slip !== undefined ? friction_slip : 10.5; + this.max_suspension_force = max_suspension_force !== undefined ? max_suspension_force : 6000; + }; + + return Physijs; +})(); |
