diff options
Diffstat (limited to 'physi_js/physijs_worker.js')
| -rw-r--r-- | physi_js/physijs_worker.js | 1415 |
1 files changed, 1415 insertions, 0 deletions
diff --git a/physi_js/physijs_worker.js b/physi_js/physijs_worker.js new file mode 100644 index 0000000..a6c3811 --- /dev/null +++ b/physi_js/physijs_worker.js @@ -0,0 +1,1415 @@ +'use strict'; +var + transferableMessage = self.webkitPostMessage || self.postMessage, + + // enum + MESSAGE_TYPES = { + WORLDREPORT: 0, + COLLISIONREPORT: 1, + VEHICLEREPORT: 2, + CONSTRAINTREPORT: 3 + }, + + // temp variables + _object, + _vector, + _transform, + + // functions + public_functions = {}, + getShapeFromCache, + setShapeCache, + createShape, + reportWorld, + reportVehicles, + reportCollisions, + reportConstraints, + + // world variables + fixedTimeStep, // used when calling stepSimulation + rateLimit, // sets whether or not to sync the simulation rate with fixedTimeStep + last_simulation_time, + last_simulation_duration = 0, + world, + transform, + _vec3_1, + _vec3_2, + _vec3_3, + _quat, + // private cache + _objects = {}, + _vehicles = {}, + _constraints = {}, + _materials = {}, + _objects_ammo = {}, + _num_objects = 0, + _num_wheels = 0, + _num_constraints = 0, + _object_shapes = {}, + + // The following objects are to track objects that ammo.js doesn't clean + // up. All are cleaned up when they're corresponding body is destroyed. + // Unfortunately, it's very difficult to get at these objects from the + // body, so we have to track them ourselves. + _motion_states = {}, + // Don't need to worry about it for cached shapes. + _noncached_shapes = {}, + // A body with a compound shape always has a regular shape as well, so we + // have track them separately. + _compound_shapes = {}, + + // object reporting + REPORT_CHUNKSIZE, // report array is increased in increments of this chunk size + + WORLDREPORT_ITEMSIZE = 14, // how many float values each reported item needs + worldreport, + + COLLISIONREPORT_ITEMSIZE = 5, // one float for each object id, and a Vec3 contact normal + collisionreport, + + VEHICLEREPORT_ITEMSIZE = 9, // vehicle id, wheel index, 3 for position, 4 for rotation + vehiclereport, + + CONSTRAINTREPORT_ITEMSIZE = 6, // constraint id, offset object, offset, applied impulse + constraintreport; + +var ab = new ArrayBuffer( 1 ); + +transferableMessage( ab, [ab] ); +var SUPPORT_TRANSFERABLE = ( ab.byteLength === 0 ); + +getShapeFromCache = function ( cache_key ) { + if ( _object_shapes[ cache_key ] !== undefined ) { + return _object_shapes[ cache_key ]; + } + return null; +}; + +setShapeCache = function ( cache_key, shape ) { + _object_shapes[ cache_key ] = shape; +} + +createShape = function( description ) { + var cache_key, shape; + + _transform.setIdentity(); + switch ( description.type ) { + case 'plane': + cache_key = 'plane_' + description.normal.x + '_' + description.normal.y + '_' + description.normal.z; + if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { + _vec3_1.setX(description.normal.x); + _vec3_1.setY(description.normal.y); + _vec3_1.setZ(description.normal.z); + shape = new Ammo.btStaticPlaneShape(_vec3_1, 0 ); + setShapeCache( cache_key, shape ); + } + break; + + case 'box': + cache_key = 'box_' + description.width + '_' + description.height + '_' + description.depth; + if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { + _vec3_1.setX(description.width / 2); + _vec3_1.setY(description.height / 2); + _vec3_1.setZ(description.depth / 2); + shape = new Ammo.btBoxShape(_vec3_1); + setShapeCache( cache_key, shape ); + } + break; + + case 'sphere': + cache_key = 'sphere_' + description.radius; + if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { + shape = new Ammo.btSphereShape( description.radius ); + setShapeCache( cache_key, shape ); + } + break; + + case 'cylinder': + cache_key = 'cylinder_' + description.width + '_' + description.height + '_' + description.depth; + if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { + _vec3_1.setX(description.width / 2); + _vec3_1.setY(description.height / 2); + _vec3_1.setZ(description.depth / 2); + shape = new Ammo.btCylinderShape(_vec3_1); + setShapeCache( cache_key, shape ); + } + break; + + case 'capsule': + cache_key = 'capsule_' + description.radius + '_' + description.height; + if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { + // In Bullet, capsule height excludes the end spheres + shape = new Ammo.btCapsuleShape( description.radius, description.height - 2 * description.radius ); + setShapeCache( cache_key, shape ); + } + break; + + case 'cone': + cache_key = 'cone_' + description.radius + '_' + description.height; + if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { + shape = new Ammo.btConeShape( description.radius, description.height ); + setShapeCache( cache_key, shape ); + } + break; + + case 'concave': + var i, triangle, triangle_mesh = new Ammo.btTriangleMesh; + if (!description.triangles.length) return false + + for ( i = 0; i < description.triangles.length; i++ ) { + triangle = description.triangles[i]; + + _vec3_1.setX(triangle[0].x); + _vec3_1.setY(triangle[0].y); + _vec3_1.setZ(triangle[0].z); + + _vec3_2.setX(triangle[1].x); + _vec3_2.setY(triangle[1].y); + _vec3_2.setZ(triangle[1].z); + + _vec3_3.setX(triangle[2].x); + _vec3_3.setY(triangle[2].y); + _vec3_3.setZ(triangle[2].z); + + triangle_mesh.addTriangle( + _vec3_1, + _vec3_2, + _vec3_3, + true + ); + } + + shape = new Ammo.btBvhTriangleMeshShape( + triangle_mesh, + true, + true + ); + _noncached_shapes[description.id] = shape; + break; + + case 'convex': + var i, point, shape = new Ammo.btConvexHullShape; + for ( i = 0; i < description.points.length; i++ ) { + point = description.points[i]; + + _vec3_1.setX(point.x); + _vec3_1.setY(point.y); + _vec3_1.setZ(point.z); + + shape.addPoint(_vec3_1); + + } + _noncached_shapes[description.id] = shape; + break; + + case 'heightfield': + + var ptr = Ammo.allocate(4 * description.xpts * description.ypts, "float", Ammo.ALLOC_NORMAL); + + for (var f = 0; f < description.points.length; f++) { + Ammo.setValue(ptr + f, description.points[f] , 'float'); + } + + shape = new Ammo.btHeightfieldTerrainShape( + description.xpts, + description.ypts, + ptr, + 1, + -description.absMaxHeight, + description.absMaxHeight, + 2, + 0, + false + ); + + _vec3_1.setX(description.xsize/(description.xpts - 1)); + _vec3_1.setY(description.ysize/(description.ypts - 1)); + _vec3_1.setZ(1); + + shape.setLocalScaling(_vec3_1); + _noncached_shapes[description.id] = shape; + break; + + default: + // Not recognized + return; + break; + } + + return shape; +}; + +public_functions.init = function( params ) { + importScripts( params.ammo ); + + _transform = new Ammo.btTransform; + _vec3_1 = new Ammo.btVector3(0,0,0); + _vec3_2 = new Ammo.btVector3(0,0,0); + _vec3_3 = new Ammo.btVector3(0,0,0); + _quat = new Ammo.btQuaternion(0,0,0,0); + + REPORT_CHUNKSIZE = params.reportsize || 50; + if ( SUPPORT_TRANSFERABLE ) { + // Transferable messages are supported, take advantage of them with TypedArrays + worldreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object + collisionreport = new Float32Array(2 + REPORT_CHUNKSIZE * COLLISIONREPORT_ITEMSIZE); // message id + # of collisions to report + chunk size * # of values per object + vehiclereport = new Float32Array(2 + REPORT_CHUNKSIZE * VEHICLEREPORT_ITEMSIZE); // message id + # of vehicles to report + chunk size * # of values per object + constraintreport = new Float32Array(2 + REPORT_CHUNKSIZE * CONSTRAINTREPORT_ITEMSIZE); // message id + # of constraints to report + chunk size * # of values per object + } else { + // Transferable messages are not supported, send data as normal arrays + worldreport = []; + collisionreport = []; + vehiclereport = []; + constraintreport = []; + } + worldreport[0] = MESSAGE_TYPES.WORLDREPORT; + collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT; + vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT; + constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT; + + var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration, + dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ), + solver = new Ammo.btSequentialImpulseConstraintSolver, + broadphase; + + if ( !params.broadphase ) params.broadphase = { type: 'dynamic' }; + switch ( params.broadphase.type ) { + case 'sweepprune': + + _vec3_1.setX(params.broadphase.aabbmin.x); + _vec3_1.setY(params.broadphase.aabbmin.y); + _vec3_1.setZ(params.broadphase.aabbmin.z); + + _vec3_2.setX(params.broadphase.aabbmax.x); + _vec3_2.setY(params.broadphase.aabbmax.y); + _vec3_2.setZ(params.broadphase.aabbmax.z); + + broadphase = new Ammo.btAxisSweep3( + _vec3_1, + _vec3_2 + ); + + break; + + case 'dynamic': + default: + broadphase = new Ammo.btDbvtBroadphase; + break; + } + + world = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration ); + + fixedTimeStep = params.fixedTimeStep; + rateLimit = params.rateLimit; + + transferableMessage({ cmd: 'worldReady' }); +}; + +public_functions.registerMaterial = function( description ) { + _materials[ description.id ] = description; +}; + +public_functions.unRegisterMaterial = function( description ) { + delete _materials[ description.id ]; +}; + +public_functions.setFixedTimeStep = function( description ) { + fixedTimeStep = description; +}; + +public_functions.setGravity = function( description ) { + _vec3_1.setX(description.x); + _vec3_1.setY(description.y); + _vec3_1.setZ(description.z); + world.setGravity(_vec3_1); +}; + +public_functions.addObject = function( description ) { + + var i, + localInertia, shape, motionState, rbInfo, body; + +shape = createShape( description ); +if (!shape) return +// If there are children then this is a compound shape +if ( description.children ) { + var compound_shape = new Ammo.btCompoundShape, _child; + compound_shape.addChildShape( _transform, shape ); + + for ( i = 0; i < description.children.length; i++ ) { + _child = description.children[i]; + + var trans = new Ammo.btTransform; + trans.setIdentity(); + + _vec3_1.setX(_child.position_offset.x); + _vec3_1.setY(_child.position_offset.y); + _vec3_1.setZ(_child.position_offset.z); + trans.setOrigin(_vec3_1); + + _quat.setX(_child.rotation.x); + _quat.setY(_child.rotation.y); + _quat.setZ(_child.rotation.z); + _quat.setW(_child.rotation.w); + trans.setRotation(_quat); + + shape = createShape( description.children[i] ); + compound_shape.addChildShape( trans, shape ); + Ammo.destroy(trans); + } + + shape = compound_shape; + _compound_shapes[ description.id ] = shape; + } + _vec3_1.setX(0); + _vec3_1.setY(0); + _vec3_1.setZ(0); + shape.calculateLocalInertia( description.mass, _vec3_1 ); + + _transform.setIdentity(); + + _vec3_2.setX(description.position.x); + _vec3_2.setY(description.position.y); + _vec3_2.setZ(description.position.z); + _transform.setOrigin(_vec3_2); + + _quat.setX(description.rotation.x); + _quat.setY(description.rotation.y); + _quat.setZ(description.rotation.z); + _quat.setW(description.rotation.w); + _transform.setRotation(_quat); + + motionState = new Ammo.btDefaultMotionState( _transform ); // #TODO: btDefaultMotionState supports center of mass offset as second argument - implement + rbInfo = new Ammo.btRigidBodyConstructionInfo( description.mass, motionState, shape, _vec3_1 ); + + if ( description.materialId !== undefined ) { + rbInfo.set_m_friction( _materials[ description.materialId ].friction ); + rbInfo.set_m_restitution( _materials[ description.materialId ].restitution ); + } + + body = new Ammo.btRigidBody( rbInfo ); + Ammo.destroy(rbInfo); + + if ( typeof description.collision_flags !== 'undefined' ) { + body.setCollisionFlags( description.collision_flags ); + } + + world.addRigidBody( body ); + + body.id = description.id; + _objects[ body.id ] = body; + _motion_states[ body.id ] = motionState; + + var ptr = body.a != undefined ? body.a : body.ptr; + _objects_ammo[ptr] = body.id; + _num_objects++; + + transferableMessage({ cmd: 'objectReady', params: body.id }); +}; + +public_functions.addVehicle = function( description ) { + var vehicle_tuning = new Ammo.btVehicleTuning(), + vehicle; + + vehicle_tuning.set_m_suspensionStiffness( description.suspension_stiffness ); + vehicle_tuning.set_m_suspensionCompression( description.suspension_compression ); + vehicle_tuning.set_m_suspensionDamping( description.suspension_damping ); + vehicle_tuning.set_m_maxSuspensionTravelCm( description.max_suspension_travel ); + vehicle_tuning.set_m_maxSuspensionForce( description.max_suspension_force ); + + vehicle = new Ammo.btRaycastVehicle( vehicle_tuning, _objects[ description.rigidBody ], new Ammo.btDefaultVehicleRaycaster( world ) ); + vehicle.tuning = vehicle_tuning; + + _objects[ description.rigidBody ].setActivationState( 4 ); + vehicle.setCoordinateSystem( 0, 1, 2 ); + + world.addVehicle( vehicle ); + _vehicles[ description.id ] = vehicle; +}; +public_functions.removeVehicle = function( description ) { + delete _vehicles[ description.id ]; +}; + +public_functions.addWheel = function( description ) { + if ( _vehicles[description.id] !== undefined ) { + var tuning = _vehicles[description.id].tuning; + if ( description.tuning !== undefined ) { + tuning = new Ammo.btVehicleTuning(); + tuning.set_m_suspensionStiffness( description.tuning.suspension_stiffness ); + tuning.set_m_suspensionCompression( description.tuning.suspension_compression ); + tuning.set_m_suspensionDamping( description.tuning.suspension_damping ); + tuning.set_m_maxSuspensionTravelCm( description.tuning.max_suspension_travel ); + tuning.set_m_maxSuspensionForce( description.tuning.max_suspension_force ); + } + + _vec3_1.setX(description.connection_point.x); + _vec3_1.setY(description.connection_point.y); + _vec3_1.setZ(description.connection_point.z); + + _vec3_2.setX(description.wheel_direction.x); + _vec3_2.setY(description.wheel_direction.y); + _vec3_2.setZ(description.wheel_direction.z); + + _vec3_3.setX(description.wheel_axle.x); + _vec3_3.setY(description.wheel_axle.y); + _vec3_3.setZ(description.wheel_axle.z); + + _vehicles[description.id].addWheel( + _vec3_1, + _vec3_2, + _vec3_3, + description.suspension_rest_length, + description.wheel_radius, + tuning, + description.is_front_wheel + ); + } + + _num_wheels++; + + if ( SUPPORT_TRANSFERABLE ) { + vehiclereport = new Float32Array(1 + _num_wheels * VEHICLEREPORT_ITEMSIZE); // message id & ( # of objects to report * # of values per object ) + vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT; + } else { + vehiclereport = [ MESSAGE_TYPES.VEHICLEREPORT ]; + } +}; + +public_functions.setSteering = function( details ) { + if ( _vehicles[details.id] !== undefined ) { + _vehicles[details.id].setSteeringValue( details.steering, details.wheel ); + } +}; +public_functions.setBrake = function( details ) { + if ( _vehicles[details.id] !== undefined ) { + _vehicles[details.id].setBrake( details.brake, details.wheel ); + } +}; +public_functions.applyEngineForce = function( details ) { + if ( _vehicles[details.id] !== undefined ) { + _vehicles[details.id].applyEngineForce( details.force, details.wheel ); + } +}; + +public_functions.removeObject = function( details ) { + world.removeRigidBody( _objects[details.id] ); + Ammo.destroy(_objects[details.id]); + Ammo.destroy(_motion_states[details.id]); + if (_compound_shapes[details.id]) Ammo.destroy(_compound_shapes[details.id]); + if (_noncached_shapes[details.id]) Ammo.destroy(_noncached_shapes[details.id]); + var ptr = _objects[details.id].a != undefined ? _objects[details.id].a : _objects[details.id].ptr; + delete _objects_ammo[ptr]; + delete _objects[details.id]; + delete _motion_states[details.id]; + if (_compound_shapes[details.id]) delete _compound_shapes[details.id]; + if (_noncached_shapes[details.id]) delete _noncached_shapes[details.id]; + _num_objects--; +}; + +public_functions.updateTransform = function( details ) { + _object = _objects[details.id]; + _object.getMotionState().getWorldTransform( _transform ); + + if ( details.pos ) { + _vec3_1.setX(details.pos.x); + _vec3_1.setY(details.pos.y); + _vec3_1.setZ(details.pos.z); + _transform.setOrigin(_vec3_1); + } + + if ( details.quat ) { + _quat.setX(details.quat.x); + _quat.setY(details.quat.y); + _quat.setZ(details.quat.z); + _quat.setW(details.quat.w); + _transform.setRotation(_quat); + } + + _object.setWorldTransform( _transform ); + _object.activate(); +}; + +public_functions.updateMass = function( details ) { + // #TODO: changing a static object into dynamic is buggy + _object = _objects[details.id]; + + // Per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=&f=9&t=3663#p13816 + world.removeRigidBody( _object ); + + _vec3_1.setX(0); + _vec3_1.setY(0); + _vec3_1.setZ(0); + + _object.setMassProps( details.mass, _vec3_1 ); + world.addRigidBody( _object ); + _object.activate(); +}; + +public_functions.applyCentralImpulse = function ( details ) { + + _vec3_1.setX(details.x); + _vec3_1.setY(details.y); + _vec3_1.setZ(details.z); + + _objects[details.id].applyCentralImpulse(_vec3_1); + _objects[details.id].activate(); +}; + +public_functions.applyImpulse = function ( details ) { + + _vec3_1.setX(details.impulse_x); + _vec3_1.setY(details.impulse_y); + _vec3_1.setZ(details.impulse_z); + + _vec3_2.setX(details.x); + _vec3_2.setY(details.y); + _vec3_2.setZ(details.z); + + _objects[details.id].applyImpulse( + _vec3_1, + _vec3_2 + ); + _objects[details.id].activate(); +}; + +public_functions.applyTorque = function ( details ) { + + _vec3_1.setX(details.torque_x); + _vec3_1.setY(details.torque_y); + _vec3_1.setZ(details.torque_z); + + _objects[details.id].applyTorque( + _vec3_1 + ); + _objects[details.id].activate(); +}; + +public_functions.applyCentralForce = function ( details ) { + + _vec3_1.setX(details.x); + _vec3_1.setY(details.y); + _vec3_1.setZ(details.z); + + _objects[details.id].applyCentralForce(_vec3_1); + _objects[details.id].activate(); +}; + +public_functions.applyForce = function ( details ) { + + _vec3_1.setX(details.force_x); + _vec3_1.setY(details.force_y); + _vec3_1.setZ(details.force_z); + + _vec3_2.setX(details.x); + _vec3_2.setY(details.y); + _vec3_2.setZ(details.z); + + _objects[details.id].applyForce( + _vec3_1, + _vec3_2 + ); + _objects[details.id].activate(); +}; + +public_functions.onSimulationResume = function( params ) { + last_simulation_time = Date.now(); +}; + +public_functions.setAngularVelocity = function ( details ) { + + _vec3_1.setX(details.x); + _vec3_1.setY(details.y); + _vec3_1.setZ(details.z); + + _objects[details.id].setAngularVelocity( + _vec3_1 + ); + _objects[details.id].activate(); +}; + +public_functions.setLinearVelocity = function ( details ) { + + _vec3_1.setX(details.x); + _vec3_1.setY(details.y); + _vec3_1.setZ(details.z); + + _objects[details.id].setLinearVelocity( + _vec3_1 + ); + _objects[details.id].activate(); +}; + +public_functions.setAngularFactor = function ( details ) { + + _vec3_1.setX(details.x); + _vec3_1.setY(details.y); + _vec3_1.setZ(details.z); + + _objects[details.id].setAngularFactor( + _vec3_1 + ); +}; + +public_functions.setLinearFactor = function ( details ) { + + _vec3_1.setX(details.x); + _vec3_1.setY(details.y); + _vec3_1.setZ(details.z); + + _objects[details.id].setLinearFactor( + _vec3_1 + ); +}; + +public_functions.setDamping = function ( details ) { + _objects[details.id].setDamping( details.linear, details.angular ); +}; + +public_functions.setCcdMotionThreshold = function ( details ) { + _objects[details.id].setCcdMotionThreshold( details.threshold ); +}; + +public_functions.setCcdSweptSphereRadius = function ( details ) { + _objects[details.id].setCcdSweptSphereRadius( details.radius ); +}; + +public_functions.addConstraint = function ( details ) { + var constraint; + + switch ( details.type ) { + + case 'point': + if ( details.objectb === undefined ) { + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + constraint = new Ammo.btPoint2PointConstraint( + _objects[ details.objecta ], + _vec3_1 + ); + } else { + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + _vec3_2.setX(details.positionb.x); + _vec3_2.setY(details.positionb.y); + _vec3_2.setZ(details.positionb.z); + + constraint = new Ammo.btPoint2PointConstraint( + _objects[ details.objecta ], + _objects[ details.objectb ], + _vec3_1, + _vec3_2 + ); + } + break; + + case 'hinge': + if ( details.objectb === undefined ) { + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + _vec3_2.setX(details.axis.x); + _vec3_2.setY(details.axis.y); + _vec3_2.setZ(details.axis.z); + + constraint = new Ammo.btHingeConstraint( + _objects[ details.objecta ], + _vec3_1, + _vec3_2 + ); + } else { + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + _vec3_2.setX(details.positionb.x); + _vec3_2.setY(details.positionb.y); + _vec3_2.setZ(details.positionb.z); + + _vec3_3.setX(details.axis.x); + _vec3_3.setY(details.axis.y); + _vec3_3.setZ(details.axis.z); + + constraint = new Ammo.btHingeConstraint( + _objects[ details.objecta ], + _objects[ details.objectb ], + _vec3_1, + _vec3_2, + _vec3_3, + _vec3_3 + ); + } + break; + + case 'slider': + var transforma, transformb, rotation; + + transforma = new Ammo.btTransform(); + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + transforma.setOrigin(_vec3_1); + + var rotation = transforma.getRotation(); + rotation.setEuler( details.axis.x, details.axis.y, details.axis.z ); + transforma.setRotation( rotation ); + + if ( details.objectb ) { + transformb = new Ammo.btTransform(); + + _vec3_2.setX(details.positionb.x); + _vec3_2.setY(details.positionb.y); + _vec3_2.setZ(details.positionb.z); + + transformb.setOrigin(_vec3_2); + + rotation = transformb.getRotation(); + rotation.setEuler( details.axis.x, details.axis.y, details.axis.z ); + transformb.setRotation( rotation ); + + constraint = new Ammo.btSliderConstraint( + _objects[ details.objecta ], + _objects[ details.objectb ], + transforma, + transformb, + true + ); + } else { + constraint = new Ammo.btSliderConstraint( + _objects[ details.objecta ], + transforma, + true + ); + } + + Ammo.destroy(transforma); + if (transformb != undefined) { + Ammo.destroy(transformb); + } + break; + + case 'conetwist': + var transforma, transformb; + + transforma = new Ammo.btTransform(); + transforma.setIdentity(); + + transformb = new Ammo.btTransform(); + transformb.setIdentity(); + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + _vec3_2.setX(details.positionb.x); + _vec3_2.setY(details.positionb.y); + _vec3_2.setZ(details.positionb.z); + + transforma.setOrigin(_vec3_1); + transformb.setOrigin(_vec3_2); + + var rotation = transforma.getRotation(); + rotation.setEulerZYX( -details.axisa.z, -details.axisa.y, -details.axisa.x ); + transforma.setRotation( rotation ); + + rotation = transformb.getRotation(); + rotation.setEulerZYX( -details.axisb.z, -details.axisb.y, -details.axisb.x ); + transformb.setRotation( rotation ); + + constraint = new Ammo.btConeTwistConstraint( + _objects[ details.objecta ], + _objects[ details.objectb ], + transforma, + transformb + ); + + constraint.setLimit( Math.PI, 0, Math.PI ); + + Ammo.destroy(transforma); + Ammo.destroy(transformb); + + break; + + case 'dof': + var transforma, transformb, rotation; + + transforma = new Ammo.btTransform(); + transforma.setIdentity(); + + _vec3_1.setX(details.positiona.x); + _vec3_1.setY(details.positiona.y); + _vec3_1.setZ(details.positiona.z); + + transforma.setOrigin(_vec3_1 ); + + rotation = transforma.getRotation(); + rotation.setEulerZYX( -details.axisa.z, -details.axisa.y, -details.axisa.x ); + transforma.setRotation( rotation ); + + if ( details.objectb ) { + transformb = new Ammo.btTransform(); + transformb.setIdentity(); + + _vec3_2.setX(details.positionb.x); + _vec3_2.setY(details.positionb.y); + _vec3_2.setZ(details.positionb.z); + + transformb.setOrigin(_vec3_2); + + rotation = transformb.getRotation(); + rotation.setEulerZYX( -details.axisb.z, -details.axisb.y, -details.axisb.x ); + transformb.setRotation( rotation ); + + constraint = new Ammo.btGeneric6DofConstraint( + _objects[ details.objecta ], + _objects[ details.objectb ], + transforma, + transformb + ); + } else { + constraint = new Ammo.btGeneric6DofConstraint( + _objects[ details.objecta ], + transforma + ); + } + Ammo.destroy(transforma); + if (transformb != undefined) { + Ammo.destroy(transformb); + } + break; + + default: + return; + + }; + + world.addConstraint( constraint ); + + constraint.enableFeedback(); + _constraints[ details.id ] = constraint; + _num_constraints++; + + if ( SUPPORT_TRANSFERABLE ) { + constraintreport = new Float32Array(1 + _num_constraints * CONSTRAINTREPORT_ITEMSIZE); // message id & ( # of objects to report * # of values per object ) + constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT; + } else { + constraintreport = [ MESSAGE_TYPES.CONSTRAINTREPORT ]; + } +}; + +public_functions.removeConstraint = function( details ) { + var constraint = _constraints[ details.id ]; + if ( constraint !== undefined ) { + world.removeConstraint( constraint ); + delete _constraints[ details.id ]; + _num_constraints--; + } +}; + +public_functions.constraint_setBreakingImpulseThreshold = function( details ) { + var constraint = _constraints[ details.id ]; + if ( constraint !== undefind ) { + constraint.setBreakingImpulseThreshold( details.threshold ); + } +}; + +public_functions.simulate = function simulate( params ) { + if ( world ) { + params = params || {}; + + if ( !params.timeStep ) { + if ( last_simulation_time ) { + params.timeStep = 0; + while ( params.timeStep + last_simulation_duration <= fixedTimeStep ) { + params.timeStep = ( Date.now() - last_simulation_time ) / 1000; // time since last simulation + } + } else { + params.timeStep = fixedTimeStep; // handle first frame + } + } else { + if ( params.timeStep < fixedTimeStep ) { + params.timeStep = fixedTimeStep; + } + } + + params.maxSubSteps = params.maxSubSteps || Math.ceil( params.timeStep / fixedTimeStep ); // If maxSubSteps is not defined, keep the simulation fully up to date + + last_simulation_duration = Date.now(); + world.stepSimulation( params.timeStep, params.maxSubSteps, fixedTimeStep ); + + reportVehicles(); + reportCollisions(); + reportConstraints(); + reportWorld(); + + last_simulation_duration = ( Date.now() - last_simulation_duration ) / 1000; + last_simulation_time = Date.now(); + } +}; + + +// Constraint functions +public_functions.hinge_setLimits = function( params ) { + _constraints[ params.constraint ].setLimit( params.low, params.high, 0, params.bias_factor, params.relaxation_factor ); +}; +public_functions.hinge_enableAngularMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.enableAngularMotor( true, params.velocity, params.acceleration ); + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.hinge_disableMotor = function( params ) { + _constraints[ params.constraint ].enableMotor( false ); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; + +public_functions.slider_setLimits = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setLowerLinLimit( params.lin_lower || 0 ); + constraint.setUpperLinLimit( params.lin_upper || 0 ); + + constraint.setLowerAngLimit( params.ang_lower || 0 ); + constraint.setUpperAngLimit( params.ang_upper || 0 ); +}; +public_functions.slider_setRestitution = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setSoftnessLimLin( params.linear || 0 ); + constraint.setSoftnessLimAng( params.angular || 0 ); +}; +public_functions.slider_enableLinearMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setTargetLinMotorVelocity( params.velocity ); + constraint.setMaxLinMotorForce( params.acceleration ); + constraint.setPoweredLinMotor( true ); + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.slider_disableLinearMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setPoweredLinMotor( false ); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.slider_enableAngularMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setTargetAngMotorVelocity( params.velocity ); + constraint.setMaxAngMotorForce( params.acceleration ); + constraint.setPoweredAngMotor( true ); + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.slider_disableAngularMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setPoweredAngMotor( false ); + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; + +public_functions.conetwist_setLimit = function( params ) { + _constraints[ params.constraint ].setLimit( params.z, params.y, params.x ); // ZYX order +}; +public_functions.conetwist_enableMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.enableMotor( true ); + constraint.getRigidBodyA().activate(); + constraint.getRigidBodyB().activate(); +}; +public_functions.conetwist_setMaxMotorImpulse = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.setMaxMotorImpulse( params.max_impulse ); + constraint.getRigidBodyA().activate(); + constraint.getRigidBodyB().activate(); +}; +public_functions.conetwist_setMotorTarget = function( params ) { + var constraint = _constraints[ params.constraint ]; + + _quat.setX(params.x); + _quat.setY(params.y); + _quat.setZ(params.z); + _quat.setW(params.w); + + constraint.setMotorTarget(_quat); + + constraint.getRigidBodyA().activate(); + constraint.getRigidBodyB().activate(); +}; +public_functions.conetwist_disableMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + constraint.enableMotor( false ); + constraint.getRigidBodyA().activate(); + constraint.getRigidBodyB().activate(); +}; + +public_functions.dof_setLinearLowerLimit = function( params ) { + var constraint = _constraints[ params.constraint ]; + + _vec3_1.setX(params.x); + _vec3_1.setY(params.y); + _vec3_1.setZ(params.z); + + constraint.setLinearLowerLimit(_vec3_1); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.dof_setLinearUpperLimit = function( params ) { + var constraint = _constraints[ params.constraint ]; + + _vec3_1.setX(params.x); + _vec3_1.setY(params.y); + _vec3_1.setZ(params.z); + + constraint.setLinearUpperLimit(_vec3_1); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.dof_setAngularLowerLimit = function( params ) { + var constraint = _constraints[ params.constraint ]; + + _vec3_1.setX(params.x); + _vec3_1.setY(params.y); + _vec3_1.setZ(params.z); + + constraint.setAngularLowerLimit(_vec3_1); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.dof_setAngularUpperLimit = function( params ) { + var constraint = _constraints[ params.constraint ]; + + _vec3_1.setX(params.x); + _vec3_1.setY(params.y); + _vec3_1.setZ(params.z); + + constraint.setAngularUpperLimit(_vec3_1); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.dof_enableAngularMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + + var motor = constraint.getRotationalLimitMotor( params.which ); + motor.set_m_enableMotor( true ); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.dof_configureAngularMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + + var motor = constraint.getRotationalLimitMotor( params.which ); + + motor.set_m_loLimit( params.low_angle ); + motor.set_m_hiLimit( params.high_angle ); + motor.set_m_targetVelocity( params.velocity ); + motor.set_m_maxMotorForce( params.max_force ); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; +public_functions.dof_disableAngularMotor = function( params ) { + var constraint = _constraints[ params.constraint ]; + + var motor = constraint.getRotationalLimitMotor( params.which ); + motor.set_m_enableMotor( false ); + + constraint.getRigidBodyA().activate(); + if ( constraint.getRigidBodyB() ) { + constraint.getRigidBodyB().activate(); + } +}; + +reportWorld = function() { + var index, object, + transform, origin, rotation, + offset = 0, + i = 0; + + if ( SUPPORT_TRANSFERABLE ) { + if ( worldreport.length < 2 + _num_objects * WORLDREPORT_ITEMSIZE ) { + worldreport = new Float32Array( + 2 + // message id & # objects in report + ( Math.ceil( _num_objects / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * WORLDREPORT_ITEMSIZE // # of values needed * item size + ); + worldreport[0] = MESSAGE_TYPES.WORLDREPORT; + } + } + + worldreport[1] = _num_objects; // record how many objects we're reporting on + + //for ( i = 0; i < worldreport[1]; i++ ) { + for ( index in _objects ) { + if ( _objects.hasOwnProperty( index ) ) { + object = _objects[index]; + + // #TODO: we can't use center of mass transform when center of mass can change, + // but getMotionState().getWorldTransform() screws up on objects that have been moved + //object.getMotionState().getWorldTransform( transform ); + transform = object.getCenterOfMassTransform(); + + origin = transform.getOrigin(); + rotation = transform.getRotation(); + + // add values to report + offset = 2 + (i++) * WORLDREPORT_ITEMSIZE; + + worldreport[ offset ] = object.id; + + worldreport[ offset + 1 ] = origin.x(); + worldreport[ offset + 2 ] = origin.y(); + worldreport[ offset + 3 ] = origin.z(); + + worldreport[ offset + 4 ] = rotation.x(); + worldreport[ offset + 5 ] = rotation.y(); + worldreport[ offset + 6 ] = rotation.z(); + worldreport[ offset + 7 ] = rotation.w(); + + _vector = object.getLinearVelocity(); + worldreport[ offset + 8 ] = _vector.x(); + worldreport[ offset + 9 ] = _vector.y(); + worldreport[ offset + 10 ] = _vector.z(); + + _vector = object.getAngularVelocity(); + worldreport[ offset + 11 ] = _vector.x(); + worldreport[ offset + 12 ] = _vector.y(); + worldreport[ offset + 13 ] = _vector.z(); + } + } + + + if ( SUPPORT_TRANSFERABLE ) { + transferableMessage( worldreport.buffer, [worldreport.buffer] ); + } else { + transferableMessage( worldreport ); + } + +}; + +reportCollisions = function() { + var i, offset, + dp = world.getDispatcher(), + num = dp.getNumManifolds(), + manifold, num_contacts, j, pt, + _collided = false; + + if ( SUPPORT_TRANSFERABLE ) { + if ( collisionreport.length < 2 + num * COLLISIONREPORT_ITEMSIZE ) { + collisionreport = new Float32Array( + 2 + // message id & # objects in report + ( Math.ceil( _num_objects / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * COLLISIONREPORT_ITEMSIZE // # of values needed * item size + ); + collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT; + } + } + + collisionreport[1] = 0; // how many collisions we're reporting on + + for ( i = 0; i < num; i++ ) { + manifold = dp.getManifoldByIndexInternal( i ); + + num_contacts = manifold.getNumContacts(); + if ( num_contacts === 0 ) { + continue; + } + + for ( j = 0; j < num_contacts; j++ ) { + pt = manifold.getContactPoint( j ); + //if ( pt.getDistance() < 0 ) { + offset = 2 + (collisionreport[1]++) * COLLISIONREPORT_ITEMSIZE; + collisionreport[ offset ] = _objects_ammo[ manifold.getBody0() ]; + collisionreport[ offset + 1 ] = _objects_ammo[ manifold.getBody1() ]; + + _vector = pt.get_m_normalWorldOnB(); + collisionreport[ offset + 2 ] = _vector.x(); + collisionreport[ offset + 3 ] = _vector.y(); + collisionreport[ offset + 4 ] = _vector.z(); + break; + //} + + transferableMessage( _objects_ammo ); + + } + } + + + if ( SUPPORT_TRANSFERABLE ) { + transferableMessage( collisionreport.buffer, [collisionreport.buffer] ); + } else { + transferableMessage( collisionreport ); + } +}; + +reportVehicles = function() { + var index, vehicle, + transform, origin, rotation, + offset = 0, + i = 0, j = 0; + + if ( SUPPORT_TRANSFERABLE ) { + if ( vehiclereport.length < 2 + _num_wheels * VEHICLEREPORT_ITEMSIZE ) { + vehiclereport = new Float32Array( + 2 + // message id & # objects in report + ( Math.ceil( _num_wheels / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * VEHICLEREPORT_ITEMSIZE // # of values needed * item size + ); + vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT; + } + } + + for ( index in _vehicles ) { + if ( _vehicles.hasOwnProperty( index ) ) { + vehicle = _vehicles[index]; + + for ( j = 0; j < vehicle.getNumWheels(); j++ ) { + + //vehicle.updateWheelTransform( j, true ); + + //transform = vehicle.getWheelTransformWS( j ); + transform = vehicle.getWheelInfo( j ).get_m_worldTransform(); + + origin = transform.getOrigin(); + rotation = transform.getRotation(); + + // add values to report + offset = 1 + (i++) * VEHICLEREPORT_ITEMSIZE; + + vehiclereport[ offset ] = index; + vehiclereport[ offset + 1 ] = j; + + vehiclereport[ offset + 2 ] = origin.x(); + vehiclereport[ offset + 3 ] = origin.y(); + vehiclereport[ offset + 4 ] = origin.z(); + + vehiclereport[ offset + 5 ] = rotation.x(); + vehiclereport[ offset + 6 ] = rotation.y(); + vehiclereport[ offset + 7 ] = rotation.z(); + vehiclereport[ offset + 8 ] = rotation.w(); + + } + + } + } + + if ( j !== 0 ) { + if ( SUPPORT_TRANSFERABLE ) { + transferableMessage( vehiclereport.buffer, [vehiclereport.buffer] ); + } else { + transferableMessage( vehiclereport ); + } + } +}; + +reportConstraints = function() { + var index, constraint, + offset_body, + transform, origin, + offset = 0, + i = 0; + + if ( SUPPORT_TRANSFERABLE ) { + if ( constraintreport.length < 2 + _num_constraints * CONSTRAINTREPORT_ITEMSIZE ) { + constraintreport = new Float32Array( + 2 + // message id & # objects in report + ( Math.ceil( _num_constraints / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * CONSTRAINTREPORT_ITEMSIZE // # of values needed * item size + ); + constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT; + } + } + + for ( index in _constraints ) { + if ( _constraints.hasOwnProperty( index ) ) { + constraint = _constraints[index]; + offset_body = constraint.getRigidBodyA(); + transform = constraint.getFrameOffsetA(); + origin = transform.getOrigin(); + + // add values to report + offset = 1 + (i++) * CONSTRAINTREPORT_ITEMSIZE; + + constraintreport[ offset ] = index; + constraintreport[ offset + 1 ] = offset_body.id; + constraintreport[ offset + 2 ] = origin.getX(); + constraintreport[ offset + 3 ] = origin.getY(); + constraintreport[ offset + 4 ] = origin.getZ(); + constraintreport[ offset + 5 ] = constraint.getAppliedImpulse(); + } + } + + + if ( i !== 0 ) { + if ( SUPPORT_TRANSFERABLE ) { + transferableMessage( constraintreport.buffer, [constraintreport.buffer] ); + } else { + transferableMessage( constraintreport ); + } + } + +}; + +self.onmessage = function( event ) { + + if ( event.data instanceof Float32Array ) { + // transferable object + + switch ( event.data[0] ) { + case MESSAGE_TYPES.WORLDREPORT: + worldreport = new Float32Array( event.data ); + break; + + case MESSAGE_TYPES.COLLISIONREPORT: + collisionreport = new Float32Array( event.data ); + break; + + case MESSAGE_TYPES.VEHICLEREPORT: + vehiclereport = new Float32Array( event.data ); + break; + + case MESSAGE_TYPES.CONSTRAINTREPORT: + constraintreport = new Float32Array( event.data ); + break; + } + + return; + } + + if ( event.data.cmd && public_functions[event.data.cmd] ) { + //if ( event.data.params.id !== undefined && _objects[event.data.params.id] === undefined && event.data.cmd !== 'addObject' && event.data.cmd !== 'registerMaterial' ) return; + public_functions[event.data.cmd]( event.data.params ); + } + +}; |
