My SixDofJoint behaves strangely on y-axis rotation.

In the game I am making I need to limit the movement of some objects to the XZ plane in such a way that Y-axis movement is prohibited and that rotation is only permitted along the Y axis. Slight deviations are ok, but should be minor and the objects should be returned to an allowed position/rotation as soon as possible in a stable way.

The way I went about this was to create a SixDofJoint that connected the object body with a invisible mass 0 RigidBodyControl.

The translation limits worked fine, by setting Upper/Lower linear limits like so:

planeJoint.setLinearLowerLimit(new Vector3f(Float.NEGATIVE_INFINITY, -0.01f, Float.NEGATIVE_INFINITY));

planeJoint.setLinearUpperLimit(new Vector3f(Float.POSITIVE_INFINITY, 0.01f, Float.POSITIVE_INFINITY));

I set the rotation similarity by setting

planeJoint.setAngularLowerLimit(new Vector3f(0, Float.NEGATIVE_INFINITY, 0));

planeJoint.setAngularUpperLimit(new Vector3f(0, Float.POSITIVE_INFINITY, 0));

This is when things started to get strange…

The joint works perfectly when the Y-axis-rotation is less than 1/2 PI (90 degrees) to both directions, but as soon as the object is rotated over that limit, it suddenly starts rotating wildly and is apparently affacted by a lot of strong opposing forces. Why is this happening?

I have tested to limit the other rotational axis instead, for instance so that only x-axis rotation is allowed, and this works fine, the object can rotate to any angle along that axis and the joint still behaves well, but for the y-axis something is different…?

If I disable all rotational limits and set them all to infinity, then the object can rotate just fine around the y-axis, but as soon as I prohibit either X or Z axis rotation, Y axis rotation starts behaving this way…

Any ideas?

Did you try this with native bullet too? It might be theres some quirks in parts of jbullet.

I did this a long time ago in c++ using Bullet (and Ogre3d), and I got it working then but I dont remember exactly how I did it.

But, I have found the problem, as it also says in the comments to the JBullet General6DofConstraint it will only accept angular limits in a certain range:

X: +/- PI

Y: +/- PI / 2

Z: +/- PI / 2

PI / 2 is the exact point where my joint started to misbehave…

I somewhat suck att linear algebra but my guess (motivated by nothing but intuition…) is that the algorithm that calculates if there is a violation of the limit or not uses euler angles that are unable to figure out what set of rotations that produced the position and thus believes that the y-rotation > PI / 2 is actually some combination of X and Z rotations, in which case they violate the limits, triggering a huge torque in an attempt to push the object back into position along some not so correct path.

Probably this is general bullet behaviour rather than a bug in jbullet… but it limits the uses of the 6-dof-constraint…

**Here is a workaround solution, if there are others trying to do the same thing:**

I used the SixDofJoint as above but only for the linear limits, thus keeping the object in the plane, but setting the angular limits to:

[java] planeJoint.setAngularLowerLimit(Vector3f.NEGATIVE_INFINITY);

planeJoint.setAngularUpperLimit(Vector3f.POSITIVE_INFINITY);[/java]

thus letting the object rotate freely.

Then I added this into the update method:

[java] // limiting the rotations to the XZ plane is all about keeping the y-axis pointing upwards

Vector3f[] axes = new Vector3f [] {new Vector3f(), new Vector3f(), new Vector3f()}; Vector3f devAxis = null;

ship.getLocalRotation().toAxes(axes);

// find how the local-y deviates from world-y

// taking the cross product gives the axis of the deviation,

// and the length of this vector also grows geometrically as the angle increases

// starting from 0

devAxis = axes[1].cross(Vector3f.UNIT_Y);

// if the local y does not align with world y…

if(devAxis.length() > 0) {

RigidBodyControl shipB = ship.getControl(RigidBodyControl.class);

// apply a corrective torque

shipB.applyTorque(devAxis.mult(5000.0f));

Vector3f av = shipB.getAngularVelocity();

// this prevents the object from going into a pendulum motion

shipB.setAngularVelocity(new Vector3f(0.9f * av.x, av.y, 0.9f * av.z));

}

[/java]

Actually, it also worked with only this:

[java] shipB.setAngularVelocity(new Vector3f(0, av.y, 0));

[/java]

but in my particular application it made the constraint too stiff, the above version using the cross-product to apply a corrective torque gave a more dynamic feel as it allows for the object to be pushed off slightly in a collision and then it will snap back smoothly.