lpic lpic - 1 month ago 48
Javascript Question

Physijs simple collision between meshes without gravity

i am using Physijs to determine static collision between my meshes. As i need to know what surfaces are intersecting.

i hacked a simple demo that seems to work.

currently i have to configure my scene to use gravity, which prevents me from position my meshes in any y position, as they start to fall or float.

is there is simple way to remove the gravity from the simulation, and just use the mesh collision detection?

--update---
i had to explicitly set the mass to each mesh to 0 rather than blank. With mass=0 gravity has no affect. great!

however meshes are not reporting a collision.
any ideas where i am going wrong?

thanks

-lp

Answer

You cannot use Physijs for collision detection alone. It just comes fully equipped with real-time physics simulation, based on the ammo.js library. When you set the mass of the meshes to 0, it made them static. They were then unresponsive to external forces, such as collision responses (i.e. the change of velocity applied on the mesh after the collision was detected) or gravity. Also, two static meshes that overlap each other do not fire a collision event.

Solution A: Use ammo.js directly

Ported from Bullet Physics, the library provides the necessary tools for generating physics simulations, or just detect collisions between defined shapes (which Physijs doesn't want us to see). Here's a snippet for detecting collision between 2 rigid spheres:

var bt_collision_configuration;
var bt_dispatcher;
var bt_broadphase;
var bt_collision_world;

var scene_size = 500;
var max_objects = 10; // Tweak this as needed

bt_collision_configuration = new Ammo.btDefaultCollisionConfiguration();
bt_dispatcher = new Ammo.btCollisionDispatcher(bt_collision_configuration);

var wmin = new Ammo.btVector3(-scene_size, -scene_size, -scene_size);
var wmax = new Ammo.btVector3(scene_size, scene_size, scene_size);

// This is one type of broadphase, Ammo.js has others that might be faster
bt_broadphase = new Ammo.bt32BitAxisSweep3(
        wmin, wmax, max_objects, 0, true /* disable raycast accelerator */);

bt_collision_world = new Ammo.btCollisionWorld(bt_dispatcher, bt_broadphase, bt_collision_configuration);

// Create two collision objects
var sphere_A = new Ammo.btCollisionObject();
var sphere_B = new Ammo.btCollisionObject();

// Move each to a specific location
sphere_A.getWorldTransform().setOrigin(new Ammo.btVector3(2, 1.5, 0));
sphere_B.getWorldTransform().setOrigin(new Ammo.btVector3(2, 0, 0));

// Create the sphere shape with a radius of 1
var sphere_shape = new Ammo.btSphereShape(1);

// Set the shape of each collision object
sphere_A.setCollisionShape(sphere_shape);
sphere_B.setCollisionShape(sphere_shape);

// Add the collision objects to our collision world
bt_collision_world.addCollisionObject(sphere_A);
bt_collision_world.addCollisionObject(sphere_B);

// Perform collision detection
bt_collision_world.performDiscreteCollisionDetection();

var numManifolds = bt_collision_world.getDispatcher().getNumManifolds();

// For each contact manifold
for(var i = 0; i < numManifolds; i++){
    var contactManifold = bt_collision_world.getDispatcher().getManifoldByIndexInternal(i);
    var obA = contactManifold.getBody0();
    var obB = contactManifold.getBody1();
    contactManifold.refreshContactPoints(obA.getWorldTransform(), obB.getWorldTransform());
    var numContacts = contactManifold.getNumContacts();

    // For each contact point in that manifold
    for(var j = 0; j < numContacts; j++){

        // Get the contact information
        var pt = contactManifold.getContactPoint(j);
        var ptA = pt.getPositionWorldOnA();
        var ptB = pt.getPositionWorldOnB();
        var ptdist = pt.getDistance();

        // Do whatever else you need with the information...
    }
}

// Oh yeah! Ammo.js wants us to deallocate
// the objects with 'Ammo.destroy(obj)'

I transformed this C++ code into its JS equivalent. There might have been some missing syntax, so you can check the Ammo.js API binding changes for anything that doesn't work.

Solution B: Use THREE's ray caster

The ray caster is less accurate, but can be more precise with the addition of extra vertex count in your shapes. Here's some code to detect collision between 2 boxes:

// General box mesh data
var boxGeometry = new THREE.CubeGeometry(100, 100, 20, 1, 1, 1);
var boxMaterial = new THREE.MeshBasicMaterial({color: 0x8888ff, wireframe: true});

// Create box that detects collision
var dcube = new THREE.Mesh(boxGeometry, boxMaterial);

// Create box to check collision with
var ocube = new THREE.Mesh(boxGeometry, boxMaterial);

// Create ray caster
var rcaster = new THREE.Raycaster(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0));

// Cast a ray through every vertex or extremity
for(var vi = 0, l = dcube.geometry.vertices.length; vi < l; vi++){      
    var glovert = dcube.geometry.vertices[vi].clone().applyMatrix4(dcube.matrix);

    var dirv = glovert.sub(dcube.position);

    // Setup ray caster
    rcaster.set(dcubeOrigin, dirv.clone().normalize());

    // Get collision result
    var hitResult = rcaster.intersectObject(ocube);

    // Check if collision is within range of other cube
    if(hitResult.length && hitResult[0].distance < dirv.length()){ 
        // There was a hit detected between dcube and ocube
    }
}

Check out these links for more information (and maybe their source code):