Set rotation of PhysicsBody (Havok) programmatically

Hello!

(I’m a novice when it comes to graphics programming, so please bear with me. I’m a pretty experienced web dev, but my only brush with 3d graphics is a basics course in university over 5 years ago.)

I’m building a sort of a Legoman type of character, whose legs rotate forward and backward, with their pivot points close to the waist/torso.

This image shows how the Legoman’s legs should rotate:

I’d like to control the rotation of the legs programmatically, because I want them to move in relation to the waist/torso ONLY if I press a key (as opposed to them flapping around whenever they hit something). I also want to enable physics for them, i.e., they should collide with other meshes, and when they hit something they should transmit the impulse/force to the rest of the Legoman, if that makes sense.

My approach was to create a PhysicsBody with MotionType “DYNAMIC” for the legs, and connect them with a 6dof constraint to the waist/torso. I was then hoping to control the rotation with leg.physicsBody.transformNode.setTargetTransform(), e.g., I’m expecting the following code to add 1 degree of rotation to the leg:

const transformNode = leg.physicsBody.transformNode;
const position = transformNode.position;
const rotation = transformNode.rotationQuaternion;
const rotationToAdd = BABYLON.Quaternion.RotationYawPitchRoll(new BABYLON.Vector3(0, Math.PI / 180, 0)); // This might be the wrong way to add 1 degree of pitch to a quaternion, but I've tried multiple other ways too
const targetRotation = rotation.multiply(rotationToAdd);
leg.physicsBody.setTargetTransform(position, targetRotation);

However, instead of moving the leg by adding only one degree to the rotation, the leg starts spinning around for a while, as if the target rotation was something more. I’m thinking it could be caused by me messing up the quaternion computations (I don’t really remember how they should work), but I’ve tried multiple different variations and nothing seems to fix it. Therefore, I’m also considering that maybe there’s a bug in the PhysicsBody.setTargetTransform() method when you use it for setting rotation?

I’ve created a small playground to demonstrate the issue (kinda like the Legoman’s hip socket, and the leg rotating around it): https://playground.babylonjs.com/#LFP8R2#2 . Upon pressing a, the box should rotate around the sphere by 1 degree (or if you hold the a key pressed, it should slowly rotate until you lift the key). However, it just keeps on spinning for quite a while.

Please let me know if I’m doing something wrong here, and how I should fix it. Feel free to suggest alternative ways to design the legs too, I’m definitely not very sold on my own approach!

Let me add @carolhmj and @Cedric our physix experts to see what would be best to do here :slight_smile:

1 Like

Thank you! My experiments with PhysicsMotionType.DYNAMIC have been fruitless so far, so I started going down the path of using bones for joints and attaching collider meshes to them with PhysicsMotionType.ANIMATED. That way I’m hoping to at least detect collisions, which I can use to manually compute velocity and angle velocity for all pieces, and then transform the vertices accordingly. Due to my inexperience, I was hoping that I could outsource most of this to the physics engine though.

In any case, I’m very interested in learning how a more seasoned dev would approach this project!

@eoin gave me an explanation that, because the center of mass of the box is in its geometrical center, applying an angular impulse to it also applies a linear velocity to keep the constraint satisfied. And that contributes to adding more angular velocity, correct, Eoin?
And there is another point that, when adding a velocity through setTargetTransform, that velocity will not be removed when the body has reached the target, it will simply continue with that velocity.

1 Like

Interesting, thanks for sharing. I guess the setTargetTransform route is a dead end then, and I should move forward with calculating the velocities manually.

I wouldn’t quite say that setTargetTransform is a dead-end, but there’s a few different ways to achieve the result you want, each with pros/cons and choosing the right one depends on your exact requirements.

If it’s important that the character always follows the animation exactly, then using a motion type of ANIMATED is the way to go; however, if you want the character to be pushed around by other objects, then DYNAMIC is the right choice. You can, of course, use a combination of these, too. For example, you might make the torso ANIMATED but keep the limbs as DYNAMIC, which would enable you to keep the character upright and in the right position, while allowing the rest of the character to react physically.

In terms of actually driving to the pose you want, there’s two options:

1: If you’re using at least one DYNAMIC body, you can use a motor on the joint. The motor allows you to set a target position/orientation or velocity on the joint, as well as a max force. The motor will try to make the relative transform of the constraint pivots match the particular target.

2: You can set the velocities directly, either with setTargetTransform or a combination of setLinearVelocity and setAngularVelocity. Under the hood, setTargetTransform will calculate a velocity to reach the target, so both are essentially the same; depending on where you’re getting your animation data, one API might be easier to use than the other. In addition, if you want the velocity to be zero-ed at the end of the frame, you can either set the velocity yourself or set a high (say, 1e10) linear and angular damping on the body – again, depends on how you want to feed animation data in, and how you want the character to interact with the rest of the environment.

In the playground you shared, the physics is behaving correctly, though I can see how it might be a little unintutive. You’ve got a scenario like this, with the box’s origin represented by the arrows and the constraint pivots as purple circles:

When you calculate a target, you’re keeping the same position, but calculating a new orientation:

Inside setTargetTransofm, this will add an angular velocity to the body. But this velocity causes the constraint to be violated – the purple circles no longer line up. So, when the physics engine steps, it needs to also add a linear velocity to make the pivots line up (now with the linear/angular velocity drawn as yellow arrows):

With zero damping and when you’re no longer setting the target, the body will continue with that velocity, so you can see how setting the target in that manner can cause the body to be moving in an unintuitive manner. On the other hand, if you continue to drive to the target in the same way, your target calculation will accumulate this additional velocity from constraint solving.

So, definitely one thing to do is to calculate the target transform correctly - i.e. you’ll need to calculate the target position as well as the orientation. This is a lot easier once you have actual animation data, however, in a playground with a procedural animation like this, you might find it easier to change how the body is described. You could put the origin of the body at the same position as the pivot, and then add the collider with an additional transform:

With a setup like this, the body’s position won’t be affected by angular velocity, so you could drive to the target in the way the playground is already doing.

4 Likes

Thank you for the suggestions, Eoin! I’ll test them out when I get a chance to get back to this project.