Page 1 of 1

Update an entity's constraint by itself?

Posted: Sun Dec 04, 2011 2:07 pm
by ala_56
Hi,

Basically I have an dynamic entity goes about the world, doing its thing freely, but every now and then I want to use a singleEntityAngularMotor in servo mode to control is orientation to reach a certain local angle.

My problem is the when the singleEntityAngularMotor is active, the entity's orientation becomes rigid and can't be influenced by anything else anymore.

Essentially, I want the entity to be completely dynamic, but I want to use the servo to apply what effectively becomes a local rotation.

I'm guessing I need to update the constraint on its own, but I'm not sure how. I've tried calling angularMotor.Update(dt), angularMotor.UpdateSolverActivity() and angularMotor.ExclusiveUpdate() but none off them seem to work.

Any suggestions?

Re: Update an entity's constraint by itself?

Posted: Sun Dec 04, 2011 4:21 pm
by ala_56
Ok, I think I've got what I need working.

The crucial bit was angularMotor.SolveIteration(), basically the only method I hadn't tried.

This is my solution:

//activate the constraint and give it info to work with
angularMotor.IsActive = true;
angularMotor.Settings.Servo.Goal = Quaternion.CreateFromRotationMatrix(ab);
angularMotor.Update(dt);

//for some reason, without these next three lines, the simulation eventually becomes erratic and jerky. If anyone can enlighten me as to why, that would be nice
angularMotor.EnterLock();
angularMotor.ExclusiveUpdate();
angularMotor.ExitLock();

//the missing piece of the puzzle
angularMotor.SolveIteration();

//deactive the constraint so it doesn't force the entity to become rigid
angularMotor.IsActive = false;

Re: Update an entity's constraint by itself?

Posted: Sun Dec 04, 2011 11:37 pm
by Norbo
//for some reason, without these next three lines, the simulation eventually becomes erratic and jerky. If anyone can enlighten me as to why, that would be nice
The prior Update method does most of the per-frame configuration work and can be executed in parallel safely. The exclusive update is an extension of the Update method for anything that needs to be done with exclusive access to the involved entities. The most common task in an exclusive update is warm starting the solving process. If a constraint is designed to use warm starting and the exclusive update is not used, then the wrong net impulses will be applied.

In addition, to work properly, the constraint must be solved with other constraints. Updating it in isolation externally will not be robust.

Updating externally won't really help the issue of the constraint controlling the motion either. The SingleEntityAngularMotor is designed to control angular velocity. If you want the object to have unaffected dynamic angular motion, then the SingleEntityAngularMotor should not be used.

If you want to turn the constraint off and on now and again, you can use the IsActive property externally without an issue. Though, while it is on, it will control the object's full angular motion.

I don't usually recommend it, but if the object has rotational symmetry around the axis of rotation (like a cylinder rotating around its length), then you might be able to just change the orientation discontinuously by setting the Orientation property directly to the new state.

This amounts to a teleportation which I usually advise against. If the object is symmetrical with respect to the rotation, you don't have to worry about the object rotating into penetration. The instantaneous change isn't kind to the persistent contact manifold, but it should be able to cope.

However, before bothering to apply such discontinuous motion, consider if it's really necessary. It sounds a bit like the orientation control is nonphysical, possibly for the sake of game logic. It may be a good idea to embrace that fully and just create a little bit of game logic which does exactly what you want without fiddling with the physics.

If faking it with game logic doesn't make sense, you could also use a springy control system that only works on a single degree of freedom. Compute the rotation you want and turn it into an axis-angle format. An entity's AngularVelocity is stored in the form of a rotation axis multiplied by the speed of rotation around that axis. So, by adding a part of the desired axis-angle rotation into the angular velocity, you can create a spring. Designing it such that it is critically or over damped may be a good idea.

Re: Update an entity's constraint by itself?

Posted: Mon Dec 05, 2011 2:30 pm
by ala_56
Thanks, I've still a lot to learn.

I tried manipulating the orientation directly but this produced undesirable effects. It's probably better for me to explain what I hope to accomplish. I want to create a motor bike. The entity in question is the main part of the bike, it's connected to the rear wheel and front steering. I want the main part to move with the wheels as they move along the track, hence why I need the orientation to be unconstrained, but I also want to be able to lean the entity from side to side for turning corners, hence why I want some control over its orientation.

Now, knowing this, which do you recommend: fake it using game logic? or "springy control system"? I'm leaning towards springy control system, but unsure how to implement it. Does the spring not need to be connected to something else?

You said: "the constraint must be solved with other constraints. Updating it in isolation externally will not be robust."
Does that mean if I use the current code, I must update the rear wheel and front steering in the same fashion?

Thanks for your help so far.

Re: Update an entity's constraint by itself?

Posted: Tue Dec 06, 2011 1:49 am
by Norbo
Now, knowing this, which do you recommend: fake it using game logic? or "springy control system"? I'm leaning towards springy control system, but unsure how to implement it. Does the spring not need to be connected to something else?
If the bike is a physical system, then I would lean towards the springy control system too. A real, physical spring would indeed need to be connected to something, but we can pretend we're implicitly connecting it to some absolute world frame and ignore that detail.

There's only one angular degree of freedom that needs to be influenced to handle leaning/rolling. The trick is determining the axis we will use to measure it and handling any corner cases that arise.

Here's one approach:

Code: Select all

//Find a vector perpendicular both to the Up vector and the bike's forward vector.
var xDirection = Vector3.Normalize(Vector3.Cross(bike.OrientationMatrix.Forward, Up)); //This needs NaN-protection.

//We would like the bike.OrientationMatrix.Right to align with our computed xDirection.
//We know by the way the xDirection was constructed that the bike's right vector
//and the xDirection are on the same plane, so we can compute some axis-angle
//rotation to perform a roll.
//Note that the axis is technically going to be parallel to the bike's forward direction, but the sign matters.
float angle = (float)Math.Acos(MathHelper.Clamp(Vector3.Dot(bike.OrientationMatrix.Right, xDirection), -1, 1));
angle *= (float)Math.Sign(Vector3.Dot(Vector3.Cross(bike.OrientationMatrix.Right, xDirection), bike.OrientationMatrix.Forward));

//Apply a corrective angular impulse to get closer to our goal.
if(Math.Abs(angle) > 0)
{
    //Create an angular spring as you would a regular spring.
    //Use a damped spring of the form SpringForce = Stiffness * Error - Damping * Velocity.
    //The 'velocity' term is going to be the speed of rotation around the controlled degree of freedom.
    //In this case, that means the bike's forward vector.
    //The angular velocity of an entity is stored as a vector representing the axis of rotation with magnitude equal to the rotation speed in radians.
    float currentVelocity = Vector3.Dot(bike.AngularVelocity, bike.OrientationMatrix.Forward);
    //Note that tuning the stiffness and damping coefficients can be tricky.  Going too high
    //can cause instability depending on the time step.  Play around with them for a while.
    bike.AngularMomentum += bike.OrientationMatrix.Forward * (stiffnessCoefficient * angle - dampingCoefficient * currentVelocity);
}
I typed that up in WordPad and didn't even attempt to compile or test it, so it may not work perfectly on the first try- but the general concept is there, hopefully without too many goofs :)

There do exist some problems with this approach. It acts on the world's reference frame, so flipping in the air or going through loops will behave oddly. You could disable the correction mechanism if the bike is not touching the ground.

To mitigate the problem and allow loops to work, you could use some information to define the current 'up' direction. One possibility is to shoot a ray cast down to the ground beneath the bike and use the hit normal. This has corner cases of its own since it relies on the content quite heavily.

Another option is to define meta data along the track which includes 'up' directions along the path. As the bike moves, the current 'up' direction interpolates between the metadata definitions. This gives you full control, but also requires full control- meaning it would take longer to create content.


On the other hand, if the bike is not a physical system, then faking it is probably better. When I say physical system, I mean something like a set of entities connected by constraints or a Vehicle with a couple of wheels. In any case, its motion is defined by physical behaviors as opposed to teleportation or tight external control. If the bike body is teleporting along to match arbitrary wheel positions, that would be nonphysical.
You said: "the constraint must be solved with other constraints. Updating it in isolation externally will not be robust."
Does that mean if I use the current code, I must update the rear wheel and front steering in the same fashion?
Not quite; all constraints must be solved together for robustness. That means contact constraints and everything else. Theoretically you could update all constraints outside of the engine, but there would be no point. It's much easier and safer to just give the constraint to the Space and let it automatically Just Work. :)

Re: Update an entity's constraint by itself?

Posted: Tue Dec 06, 2011 2:03 am
by Norbo
In addition to that chunk of code, I should mention you could probably get away with controlling the orientation using some two-body constraint that operates on one degree of freedom like the RevoluteMotor. The second connection can be set to null, which means it's connecting to a magical world frame object. It would take some effort to control the servo goal properly and you'd need to change the TestAxis to whatever 'Up' is in the case of the RevoluteMotor, but there should be a workable option somewhere in there.

Re: Update an entity's constraint by itself?

Posted: Thu Dec 08, 2011 4:38 pm
by ala_56
Thanks!

There's a lot of food for thought here, I'm gonna have to test a few of these ideas out. It might take me a while as I'm moving house at the minute. But I'll update my findings in case it might help someone else out.

Thanks again!