1. The ragdoll begins in bindpose, with the thigh and shin bones lined up and on top of each other. Since this how the ragdoll is setup when its joints are attached, does this orientation represent a 0 degree angle?
Generally, yes. This isn't always true of constraints, though. They can be configured such that '0' is any desired state. Limits all of some kind of measurement basis attached to one entity (connection A by convention) and the thing which is measured attached to the other entity (connection B by convention). By changing the measurement basis or measurement target, the meaning of the angles can be changed.
The RevoluteJoint is a group of constraints whose constructor makes some assumptions on construction. It configures the limit with some reasonable defaults based on the initial relative state of the involved entities, though these guesses aren't always what is wanted.
2. Am I doing something inherently wrong when I am creating the joint and setting up the limits?
In a RevoluteJoint, the BallSocketJoint and RevoluteAngularJoint are active by default while the RevoluteMotor and RevoluteLimit are inactive by default. So, the limit must be explicitly activated (and the motor does not need to be explicitly deactivated). The MaxCorrectiveVelocity should probably be left at the default value, too, unless there's a reason to change it.
The MinimumAngle is larger than the MaximumAngle, so the allowed range wraps from Pi/2 all the way back around to the maximum angle (0 is equivalent to 2pi). That's 270 degrees and probably not what you are looking for in a knee joint.
If you wanted to change the meaning of the angles (i.e. make 90 degrees a different relative state than the default initialization's interpretation), you can change the measurement basis by changing the revoluteJoint.Limit.Basis and the measurement target by changing the revoluteJoint.Limit.TestAxis. The basis is attached to connection A and the test axis is attached to connection B.
The basis has two axes. The primary axis should be aligned with the RevoluteAngularJoint's axis of rotation. This primary axis is the normal of a plane. On that plane lies the basis's X axis; the X axis defines what 0 is. That is, if the test axis from connection B projects onto that measurement plane and the result is aligned with the X axis, the angle is 0. Negating the basis's primary axis negates the measured relative angle.
So, for example, here's a slightly modified version of the code you posted:
Code: Select all
var thigh = new Cylinder(new Vector3(0, 5, 5), 2, 0.8f, 5);
var shin = new Cylinder(new Vector3(0, 3, 5), 2, 0.8f, 5);
CollisionRules.AddRule(thigh, shin, CollisionRule.NoBroadPhase);
Space.Add(thigh);
Space.Add(shin);
var revoluteJoint = new RevoluteJoint(thigh, shin, thigh.Position + new Vector3(0, -1f, 0), new Vector3(1, 0, 0));
revoluteJoint.Limit.IsActive = true;
revoluteJoint.Limit.MaximumAngle = (float)Math.PI / 2;
revoluteJoint.Limit.MinimumAngle = 0;
revoluteJoint.Limit.Basis.SetWorldAxes(new Vector3(1, 0, 0), new Vector3(0, 0, 1));
revoluteJoint.Limit.TestAxis = new Vector3(0, 0, 1);
Space.Add(revoluteJoint);
By default, the constructor set (0,-1,0) to represent 0 on the measurement plane and set the test axis accordingly. That default is overridden in the above by setting the basis and test axis. I chose (0,0,1) to represent 0 on the measurement plane. The test axis is changed to (0,0,1) as well so that the limit is at 0 relative angle at initialization.
On the revoluteJoint.Limit.Basis.SetWorldAxes line, if I change the first parameter (the primary axis) to (-1,0,0) instead of (1,0,0),the limit flips and the knee can bend the opposite way.
I'd recommend playing around with it in the demos so you can get quickly isolate and visualize the behaviors.