javascriptphysijs

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


Solution

  • 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):