chandlerprall / Physijs

Physics plugin for Three.js
MIT License
2.77k stars 455 forks source link

Character animation with physijs #261

Open eddyuk opened 8 years ago

eddyuk commented 8 years ago

How would one apply physics on animated model such as collision, impulse etc.? I can't find anything on a subject in google.

ghost commented 8 years ago

@eddyuk,

The fact is that you probably have a very complex model. And Physijs does not have any real support for dynamic concave meshes (the documentation only said static if I remember correctly).

One solution, if you really wanted to keep the complexity of your mesh, would be to completely reconstruct your physical mesh (with Physijs.ConcaveMesh) so that it matches the one in your rendering window every frame. But that's a lot of work for a computer, not to mention all the wasted memory that will cause massive lag spikes during runtime. Plus, this would only work for collision detection and not response (unless, of course, you prefer making your custom forces).

Another way, which decreases physics accuracy but provides enough functionality, would be to encapsulate it into a Physijs.ConvexMesh. It's the closest thing to a concave mesh while supporting collision detection and response. However, you would need to reconstruct it, but you can just set its position or velocity when you want to move it yourself without affecting shape (remember to use the .__dirtyPosition flag).

Finally, you could use a combination of THREE.js raycasting and some wrapper Physijs objects:

var raycaster = new THREE.Raycaster();

// Note: 1st parameter is three.js model (mesh),
// 2nd parameter is a physijs mesh that's going to collide with the model
function createModelCollider(model, obj){
  var crosses = [];
  var collider = new Physijs.SphereMesh(new THREE.SphereGeometry(1, 1, 1), fakeMaterial, 0);
  collider.visible = false;

  // Must find where to place collider object onto model using raycasting
  raycaster.set(obj.position.clone(), obj.getLinearVelocity().clone().normalize());
  crosses = raycaster.intersectObject(model, false);

  if(crosses.length){
    collider.position.copy(crosses[0].point);
  } else {
    return null;
  }

  // Object returned must be added to scene (can be removed after frame ends)
  return collider;
}

After that, all you need to do is wait for the collision event between the collider and the specified mesh. Tweak a few velocities from the callback, apply them to your model, and voila! Sure, this does sound like the 1st solution (because you are making your own forces) but you don't have to completely recreate your mesh. Plus, you can choose - even before it gets simulated - what collides with the model and what doesn't.

eddyuk commented 8 years ago

Thanks for the response! Actually, I am learning Three and Physi js and have in mind the aspect of game use case and trying logically to put it together with sample codes. So, was wondering if I have animated character, like running, jumping etc. I can use physijs for that in order to keep it on surface (terrain, floor or whatever) and not going through solid objects. I saw approach using raycast for that, but still don't entirely understand how to collaborate between physijs and threejs objects. probably will take time.

ghost commented 8 years ago

As I said before, you can't really use a reserved physics mesh for an entire character because that takes too many resources that could be spent elsewhere. You're gonna have to think what kind of obstacles are going to constrain your character and what kind of behaviour your avatar is going to have based on the size and shape of those obstacles, because trying to dump everything on the lap of a physics engine and waiting for results will end badly, even if it is running on a web worker.

The concept of raycasting is simple: throw a ray starting from said point in space in said direction (a normalized vector). The ray will go on forever (depending on implementation), testing for intersections with any objects it was given to evaluate. All you need to do is use the THREE.Raycaster object and configure it (set the origin to the object's position and the direction to its normalized linear velocity) to test for the objects you need. If it hits anything, its return value, an array, should be filled with 1 or more objects, describing the collision. All you need to do is create a Physijs object at wherever the collision with the model occured and add it to the scene. It will act as a "crash pad" for the object that will collide. The THREE.js documentation should help you out a little more.

I've also remembered another good way of implementing your model into the physical world: BVH (Bounding Volume Hierarchy). I haven't really looked into it, but the general idea is simple enough. At the base of your "tree", you have your model, with its bounding box. Now, you have 2 "branches" that split your model into 2 bounding volumes (a bounding box that encapsulates multiple objects). Then, each branch has 2 more branches that split into 4 branches that split into 8 branches... you get the idea. It reduces the number of checks onto your model for raycasting, or it can be used just like that for collision detection without rays. After all, if the tree isn't hit, why check all the branches? There are many ways of generating it, but a common one I've seen is an octree (I have honestly no idea what that is, I have to read about it). The Wiki explains it here. There is also an example of it at work with an animated model and physics based from Oimo.js here.