Permalink
| 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; | |
| })(); |