Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixate rotation (quaternion) #46

Closed
groe opened this issue Feb 6, 2013 · 12 comments
Closed

Fixate rotation (quaternion) #46

groe opened this issue Feb 6, 2013 · 12 comments

Comments

@groe
Copy link
Contributor

groe commented Feb 6, 2013

First of all: Great work! Performant physics in the browser, written in JS - that's truly impressive.

I am currently experimenting with cannon.js together with KickJS as a WebGL-based game engine and have a question regarding modeling of a scene.

TL;DR Is there a way to fixate the quaternion of an object (along an axis) in order to prevent it from falling over?

My cannon.js world currently consists of

  • gravity (z=-10)
  • one plane (ground)
  • a few quadratic boxes on the ground (radius 0.5, y=0.5), motion state KINEMATIC
  • a few quadratic boxes floating over the ground (radius 0.5, y=1.5), motion state KINEMATIC

Now I want to allow users to move (from first person) through the world. So I create a sphere with radius=0.5, y=0.5, motion state DYNAMIC (i.e. rolling on the ground) and bind it to the camera position (but not the rotation because I don't want the camera in fact to be rolling around). When they press the WASD/cursor-keys I increase the corresponding axis velocity of the sphere and push it in that direction. So far so good, everything works great: No running through boxes (collisions) and the player sphere drops from height by gravity. Awesome!

Now I want my player to be a little greater. I could just increase the sphere radius, but that would make him collide with the boxes from more distance too, so he can't get close to them anymore. So instead of the sphere I could use a rectangular box with height=2, width=1, depth=1. Or a cylinder with radius=1, height=2. However, when pushing that box/cylinder as the player moves It will fall down (and therefore not collide with the floating boxes anymore). And that's my problem right now.

Here are some things I have tried to work around it:

  1. Reset the quaternion of the player box/cylinder to [1,0,0,0] in a preStep/postStep-callback. Result: The player won't fall over any more, but walking is shaky with many "small jumps" (Could be because the physics calculation still applies the force in a way that lifts that whole thing up a little so it can get over the edge of the box, before being resetted by the afterStep/beforeStep-callback).
  2. Use a very slippery material and contact material between player box/cylinder and ground. Result: Player still falls over unfortunately. Also, thinking of uneven, tilted grounds that wouldn't work very well.
  3. Use a sphere for the player figure, as before. But to also detect collisions with the boxes that are floating OVER the sphere, create a second sphere of same dimensions that floats above the first sphere. Keep this second sphere in place by setting its position in a preStep/postStep-callback. Result: Collisions with the floating boxes will now be detected ("collide" event) but what then? Push around the original sphere (and the player) in the reverse direction of the collided object? Result is unstable and feels kinda quirky to implement the "push-away when collision appears" code again as it is already provided by cannon.js
  4. Same as (3.) but put both spheres in a compound. Result: The whole compound falls over (I could have thought of that, lol).

Is there a way to fixate the quaternion of an object (along an axis) in order to prevent it from falling over?

Since I don't know any other options I could try, I would really appreciate your help!

@schteppe
Copy link
Owner

schteppe commented Feb 6, 2013

Hello,
Thanks! It's very much appreciated!

First I assume you work with the dev branch version of Cannon.js.

What you need is a constraint to remove two rotational degrees of freedom for the body. There is no simple way of doing this right now but you could try adding a HingeConstraint and set the max- and minimum force of the positional parts to zero.
Like this:

hinge.equations.p2pNormal.maxForce=0;
hinge.equations.p2pTangent1.maxForce=0;
hinge.equations.p2pTangent2.maxForce=0;

...and the same for .minForce for them all.

A less complicated solution would be to set the inertia to zero. This will make it behave like a static body but only in the rotational aspect. Example :

body.inertia.set(0,0,0):
body.invInertia.set(0,0,0):

I have not tested any of these solutions yet, but they work inside my mind :-)
If you need a working example for the hinge solution, I could make a small demo for you.

Stefan

@groe
Copy link
Contributor Author

groe commented Feb 6, 2013

Thanks for your answer!

Oh, I haven't even noticed the dev branch yet - was using master. Switching now.

I just tried setting the inertia and invInertia to 0,0,0 (although I don't exactly understand how these are related) and it works like charm! Thanks a lot!

Wow, switching to dev changed quite a lot! Things seem to be running smoother and objects move/fall more realistically IMO and big stacks of boxes aren't shaky anymore, awesome! Also, it seems like KINEMATIC body velocities are now changed when DYNAMIC bodies collide with them. Are these the new semantics of the motion state KINEMATIC? So the only difference between DYNAMIC and KINEMATIC is gravity?

BTW: Any release plans for 0.5.0?

@schteppe
Copy link
Owner

schteppe commented Feb 6, 2013

Yeah - just need to fix a few things before the next release. I've done major restructuring regarding the solver, which created a domino effect of needed restructuring. I haven't really settled for a good abstraction of constraint equations yet, but perhaps I'll save that for a later release.
The new solver makes contacts more correct, and it should also be a bit faster.

The inertia and its inverse is calculated once using a given mass when creating the body. Should document this somewhere, and perhaps add an "updateMassProperties" function or similar.
Setting inertia to zero makes no sense, but setting the inverse diagonal entries to zero means that it's got infinite rotational mass, thus not possible to rotate. It is mainly the inverse that is used for computations.

(the inertia tensor should really be a matrix but it's been represented by vectors since the start, storing only the diagonal entries of that matrix. Reason: didn't plan to support bodies with off-diagonal inertia in the beginning).

Regarding dynamic and kinematic - I think you just discovered a bug there! I guess I forgot that detail when updating the solver. I'll get back to you when the bug is fixed.
The idea is that kinematic bodies should behave like static ones when interacting with other bodies, but their motion should be possible to control from a user perspective.
Static bodies are not movable at all, but they can collide with dynamic ones.
Only dynamic bodies should be affected by gravity :)

Thanks,
Stefan

@groe
Copy link
Contributor Author

groe commented Feb 6, 2013

Keep up the great work, Stefan!

@schteppe
Copy link
Owner

schteppe commented Feb 7, 2013

Hmm... Are you setting the mass of the kinematic bodies to zero? I think, if you do, the kinematicness will be fine :)
What is the intended interaction and movement the bodies you set to kinematic?

@groe
Copy link
Contributor Author

groe commented Feb 7, 2013

Oh, with mass 0 it works as expected - thanks!

Well, at the moment the objects should only behave STATIC and block the player from walking through. However, I might want to add movement to them later, so I thought it would be a good idea to use the KINEMATIC motion state already.

So, I guess this issue can be closed. :)

Oh, one more thing, maybe you have an idea: Is there an easy way to detect whether an object is currently standing on another object (i.e. "touching the ground")? I tried checking the y-velocity against 0 but that's not a very solid way (thinking of jumping and that moment when the object reaches the peak of the jump).

@schteppe
Copy link
Owner

schteppe commented Feb 8, 2013

Good :)

You can always check if the object is sharing a contact with some other object. This can be done by looping over all ContactEquations in world.contacts. First make sure the body pair (.bi and .bj) are correct, then possibly check which direction the contact normal (.ni) is. contact.ni is world oriented and points out of body contact.bi.

Just noticed that the world.contacts property is created once the first contact is constructed, when it really should be created from scratch. Just fixed that in commit d45b194

Another simpler solution: just check whether the object had a contact or not since the last jump. This is already implemented in the FPS demo. This solution is not the best (user can jump whenever any contact with it is generated), though it works.

@groe
Copy link
Contributor Author

groe commented Feb 9, 2013

Thanks for your help!

So contact.ni is the normal of whatever contact.bj collided with? Is there a chance one could get this normal as a part of the collide event (besides the object collided with, event.with)? That would be useful for several applications I think.

(I'm afraid iterating through all world.contacts each frame could be a performance killer.)

@schteppe
Copy link
Owner

Yes, correct!
I've added event.contact to the event object now, you should be able to get the normal via event.contact.ni.
See: 602c659

Stefan

@groe
Copy link
Contributor Author

groe commented Feb 13, 2013

Thank you very much, Stefan!

I'm sorry if I have to bother you one more time, but either there is a bug in the ni vector calculation thing or I got something wrong. When I collide with Box objects from above the normal (both event.contact.ni and world.contact[...].ni) are [0, 1, 0] and that's perfectly what I would expect (pointing upwards). However, when the player object (currently a Box) collides with a Plane (the ground) the collision normal is always [0, -1, 0], i.e. pointing downwards.

This is how I set up the plane:

plane = new CANNON.Plane();
planeBody = new CANNON.RigidBody(0, plane, groundMaterial);
planeBody.quaternion = new CANNON.Quaternion(-0.5, 0.5, 0.5, 0.5);
planeBody.position = new CANNON.Vec3(0, 0, 0);
world.add(planeBody);

Did I miss something here?

I really appreciate your support!

@schteppe
Copy link
Owner

No problemo :)
The first thing that I see is that quaternion. It may be correct but I just want to be sure. Use Quaternion.setFromAxisAngle() to make the orientation declaration more clear. I guess you want the plane to face in the +y direction? Then use planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2);.

Looking at the stacks demo (the box/plane scene), the normals are indeed pointing downwards into the plane. But since we have the box as contact.bi, it means that that contact.ni should be pointing out of the box, and it all makes sense. Just remember contact.ni always points out of the body contact.bi.
If you always want the normal to be pointing out of the ground plane, you can assure this by implementing something like this code below. We need to do this because we can't assume which body is .bi and which one is .bj.

var contact = event.contact;
var contactNormal = new CANNON.Vec3();
if(contact.bi instanceof CANNON.Plane)
    contact.ni.copy(contactNormal); // bi is the plane, normal is what we want
else
    contact.ni.negate(contactNormal); // bi is the box, need to flip the normal

I hope this helps :)

@groe
Copy link
Contributor Author

groe commented Feb 18, 2013

Setting the quaternion from an angle was my first approach but I decided to go the not-so-readable-way of setting it manually to ensure precision (thinking of a huge plane, dividing or even only using pi for the angle calculation might be an issue for increasing distances to the center of the plane).

You are right, I just had to check if contact.bi is either the player object or the colliding one. That works perfectly well now! (Btw, I guess its even easier and more reliable to compare if contact.ni.id == rigidBody.id).

Once again, thank you very much! (finally closing here ;))

@groe groe closed this as completed Feb 18, 2013
FXCarl pushed a commit to FXCarl/cannon.vehicle that referenced this issue Mar 3, 2021
Fix convex triangle pillars for heightfields with negative values
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants