When GetAngularVelocity is called, if Ent.Orientation isn't Identity, it'll find an AngularVelocity to make it Identity. So, it "snaps" to identity.Norbo wrote: I don't know exactly what you mean by snapping to identity. Having a pre-existing rotation should not cause any issues by itself. Do note, however, that rotating the entire WorldTransform will also transform the translation. If you only want to handle orientation, it's best to use the orientation properties. The WorldTransform's read value will update automatically when the orientation properties are changed.
Rotate with an offset
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
Re: Rotate with an offset
GetAngularVelocity, as implemented in the EntityRotator, has no knowledge of an entity's orientation. It is given a start/end state and the time it should take to rotate between them, and outputs the angular velocity to accomplish the rotation.
If GetAngularVelocity(entity.Orientation, Quaternion.Identity, dt) was used, you would indeed get a velocity which, if applied to the entity, would get to the identity quaternion in one frame- but that's expected behavior.
If GetAngularVelocity(entity.Orientation, Quaternion.Identity, dt) was used, you would indeed get a velocity which, if applied to the entity, would get to the identity quaternion in one frame- but that's expected behavior.
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
I know that, but how do I get around it? I'm using Cylinders, but they don't seem to be able to be created with a built-in orientation that would be done by modifying the points used to create it (or whatever it does behind the scenes). Eg, if you have a boundingbox that has the corners (20, 10, 10) and (10, 20, 10) you could rotate it by changing the corners to (10, 20, 10) and (20, 10, 10). With Cylinders, I haven't found a way to do that, so I have to set Orientation, and then this happens.Norbo wrote:GetAngularVelocity, as implemented in the EntityRotator, has no knowledge of an entity's orientation. It is given a start/end state and the time it should take to rotate between them, and outputs the angular velocity to accomplish the rotation.
If GetAngularVelocity(entity.Orientation, Quaternion.Identity, dt) was used, you would indeed get a velocity which, if applied to the entity, would get to the identity quaternion in one frame- but that's expected behavior.
Also, I must not have my LinearVelocity part right, because everything rapidly spirals to the origin. It looks like:
Code: Select all
Vector3 offset, endpoint;
Quaternion q = Quaternion.CreateFromYawPitchRoll(radiansToRotate.Y, radiansToRotate.X, radiansToRotate.Z);
foreach(BaseModel m in modelList)
{
offset = m.ModelPosition - centerOfRotation;
endpoint = Vector3.Transform(m.Ent.Position, q);
m.Ent.LinearVelocity = Vector3.Cross(m.Ent.AngularVelocity, offset) +
((endpoint - m.Ent.Position) * (float)(0.2 / gameTime.ElapsedGameTime.TotalSeconds));
}
Re: Rotate with an offset
Two nonexhaustive possible options:I know that, but how do I get around it? I'm using Cylinders, but they don't seem to be able to be created with a built-in orientation that would be done by modifying the points used to create it (or whatever it does behind the scenes). Eg, if you have a boundingbox that has the corners (20, 10, 10) and (10, 20, 10) you could rotate it by changing the corners to (10, 20, 10) and (20, 10, 10). With Cylinders, I haven't found a way to do that, so I have to set Orientation, and then this happens.
-Specify the curve with correct orientations relative to the initial configuration.
-Specify the curve with whatever base orientation, but compute a new orientation using a local rotation and the entity's current orientation that is then passed into the GetAngularVelocity method as the start. The local rotation would perform whatever fixed rotation is necessary to use the values in the curve (such as 'rotate around Z by 90 degrees'). That is, instead of passing in entity.Orientation, pass in Quaternion.Concatenate(localRotation, entity.Orientation) as the start value.
Remove the position correction term for now. Ensure the core velocity calculation is behaving as you expect before complicating it. Without position correction, it should drift slowly away from the rotation origin while orbiting.Also, I must not have my LinearVelocity part right, because everything rapidly spirals to the origin. It looks like:
Once the core behavior is verified, try to reintroduce position correction.
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
Using the first option, I would have to have a curve for every object I'm transforming (in this case, 21). The second looks like a better choice, but how would I get localRotation? Is it entity.Orientation * nextCurveControlPoint (or vice versa) or something similar?Norbo wrote: Two nonexhaustive possible options:
-Specify the curve with correct orientations relative to the initial configuration.
-Specify the curve with whatever base orientation, but compute a new orientation using a local rotation and the entity's current orientation that is then passed into the GetAngularVelocity method as the start. The local rotation would perform whatever fixed rotation is necessary to use the values in the curve (such as 'rotate around Z by 90 degrees'). That is, instead of passing in entity.Orientation, pass in Quaternion.Concatenate(localRotation, entity.Orientation) as the start value.
EDIT: I tried localRotation = m.Ent.Orientation * m.OriginalOrientation (OriginalOrientation is a Quaternion that says where the model needs to return to upon reset, so it's the, well, original modified orientation), and that works until about 45 degrees, where it just flickers for eternity.
It was cause of the lack of waiting. I changed the Transform part toNorbo wrote: Remove the position correction term for now. Ensure the core velocity calculation is behaving as you expect before complicating it. Without position correction, it should drift slowly away from the rotation origin while orbiting.
Once the core behavior is verified, try to reintroduce position correction.
Code: Select all
if((pathTime * 1000) % MachineNumber > MachineNumber
&& (pathTime * 1000) % MachineNumber + deactivationTime < MachineNumber + deactivationTime)
endpoint = Vector3.Transform(m.Ent.Position, q);
else
endpoint = m.Ent.Position;
EDIT: Actually, it won't be. Rotation when a box touches a tube is not even close to correct, and I'm not sure why. However, I'll solve that after I solve this.
Re: Rotate with an offset
The local rotation is the rotation that takes the entity in its Identity configuration to the desired starting point. For example, a cylinder starts with its length aligned with the world up vector. If you wanted to use the cylinder in a curve which assumed the objects were lengthwise aligned with the world X axis to begin with, then the localRotation would be a rotation around the Z axis by 90 degrees.The second looks like a better choice, but how would I get localRotation? Is it entity.Orientation * nextCurveControlPoint (or vice versa) or something similar?
The localRotation does not change during the simulation. Its purpose is to transform the initial configuration of the entity into something your curve can use directly.
Also, beware of quaternion multiplication. In XNA, Quaternion.Multiply and the * operator do the opposite order you might expect relative to Matrix.Multiply and the Matrix * operator. The Quaternion.Concatenate method has the same order as the Matrix.Multiply method (transform1 followed by transform2).
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
So, then, localRotation would be the opposite of OriginalOrientation...? That would be Quaternion.Identity / OriginalOrientation, right? That works, kinda sorta. As time goes on, it gets more and more erratic, especially with the ones that don't have Identity for OriginalOrientation. So, let me resay what I heard. I'm looking for the rotation that negates the rotation of OriginalOrientation, right? I have to compute this on the fly, or else specify it for every model I need to use it with, which would be a pain. So, if I have Identity * OriginalOrientation (Or OriginalOrientation * identity, whichever. For this case, I'll treat multiplication as commutive if I'm returning the absolute value, because I'm not worried about signs right now. If I get a rotation in the opposite direction, I'll just switch the terms), and I want to make that Identity again, I multiply by Identity / OriginalOrientation and get Identity / Identity, which is Identity. ...Right? It seems like it would work, but it's flickering and getting really erratic and all kinds of odd stuff.Norbo wrote: The local rotation is the rotation that takes the entity in its Identity configuration to the desired starting point. For example, a cylinder starts with its length aligned with the world up vector. If you wanted to use the cylinder in a curve which assumed the objects were lengthwise aligned with the world X axis to begin with, then the localRotation would be a rotation around the Z axis by 90 degrees.
Specifying a LocalRotation of Quaternion.CreateFromAxisAngle(Vector3.UnitZ, -MathHelper.PiOver2) for the pre-rotated models and using Quaternion.Identity for the others causes an identical effect. This leads me to believe that Identity / OriginalOrientation is the correct answer, but there is some form of rounding error or something similar. (In this case, LocalRotation is (0, 0, -0.7071068, 0.7071068) and Orientation is (0, 0, 0.7071068, 0.7071068), so that seems right.) If I use MathHelper.PiOver2 without the negative, the pre-rotated tubes disappear and eventually "Function does not accept NaN values" is thrown.
I dun get eet.
Re: Rotate with an offset
There seems to be an inferential gap and I'm not immediately sure how to span it to answer the right questions. So instead, I'll cast a wide net. Some of this is repeated information, but extra context may help solidify conceptual connections.
In local space, they have a specific starting configuration based on what kind of entity they are. Cylinders' lengths are aligned with the local Y axis. This doesn't mean there is a non-identity rotation somewhere. That's just how the object is when the orientation is identity.
The localRotation's purpose is to pre-rotate an entity from its base configuration to the desired starting configuration. It creates a new space that will be considered the entity's local orientation for the purpose of later rotation. So, instead of setting the entity's Orientation directly to the curve's value, start with the local rotation to bring it into the new local space. Then, apply the rotation defined by the curve's value.
Instead, stick with explicit, direct operations. For example, instead of relying on the * operator (which is equivalent to the Quaternion.Multiply method), use Quaternion.Concatenate explicitly to avoid confusion with multiplication order. Instead of thinking in 'divides,' concatenate the conjugate of a quaternion. Quaternions that represent orientations should have unit length and conjugating an orientation quaternion is the same as inverting the quaternion. So, to 'concatenate the conjugate of a quaternion' is to multiply by the inverse as far as orientation quaternions are concerned.
This may look more complicated at first, but it allows you to think fully in terms of one-directional simple rotations occurring in sequence, rather than dealing with sneaky multiplication order issues and keeping in mind exactly what a 'divide' operation does with quaternions.
Reversing the multiplication order does not result in a negated quaternion. Try taking an object and doing a 45 degree rotation around X, then Z. Compare it to a 45 degree rotation around Z, then X. There do exist relationships between the two orders, but the results are not the same and the difference is not a simple negation in general.
While it might seem easy enough to switch the terms later if you encounter a problem when you are only handling a single multiplication between two quaternions, things get complicated fast. Once you start dealing with multiple rotations, tons of hard to understand behaviors will start showing up. It's tempting to start swapping multiplication orders at random in an effort to fix it, but that way lies madness.
It's best to build one brick at a time on a solid foundation.
localRotation is not the opposite of the 'original orientation.' All entities are created with Quaternion.Identity as an orientation to begin with.So, then, localRotation would be the opposite of OriginalOrientation...?
In local space, they have a specific starting configuration based on what kind of entity they are. Cylinders' lengths are aligned with the local Y axis. This doesn't mean there is a non-identity rotation somewhere. That's just how the object is when the orientation is identity.
The localRotation's purpose is to pre-rotate an entity from its base configuration to the desired starting configuration. It creates a new space that will be considered the entity's local orientation for the purpose of later rotation. So, instead of setting the entity's Orientation directly to the curve's value, start with the local rotation to bring it into the new local space. Then, apply the rotation defined by the curve's value.
I would recommend staying away from operator overloads for now, especially with quaternions in XNA.That would be Quaternion.Identity / OriginalOrientation, right?
Instead, stick with explicit, direct operations. For example, instead of relying on the * operator (which is equivalent to the Quaternion.Multiply method), use Quaternion.Concatenate explicitly to avoid confusion with multiplication order. Instead of thinking in 'divides,' concatenate the conjugate of a quaternion. Quaternions that represent orientations should have unit length and conjugating an orientation quaternion is the same as inverting the quaternion. So, to 'concatenate the conjugate of a quaternion' is to multiply by the inverse as far as orientation quaternions are concerned.
This may look more complicated at first, but it allows you to think fully in terms of one-directional simple rotations occurring in sequence, rather than dealing with sneaky multiplication order issues and keeping in mind exactly what a 'divide' operation does with quaternions.
It would not be computed on the fly. The localRotation does not change over time. It is a fixed value for a type of objects. Yes, you must define it for each type of object. Computing it on the fly doesn't make sense because it's a static value for each type that must be manually defined based on the local space configuration of the type. Any valid 'computation' you could do on the fly would just be a redundant version of one you could run at initialization.I have to compute this on the fly, or else specify it for every model I need to use it with, which would be a pain.
Quaternion multiplication/concatenation is not commutative. Quaternion multiplication/concatenation is not a dot product that returns a signed scalar; multiplication/concatenation results in another quaternion.So, if I have Identity * OriginalOrientation (Or OriginalOrientation * identity, whichever. For this case, I'll treat multiplication as commutive if I'm returning the absolute value, because I'm not worried about signs right now. If I get a rotation in the opposite direction, I'll just switch the terms)
Reversing the multiplication order does not result in a negated quaternion. Try taking an object and doing a 45 degree rotation around X, then Z. Compare it to a 45 degree rotation around Z, then X. There do exist relationships between the two orders, but the results are not the same and the difference is not a simple negation in general.
While it might seem easy enough to switch the terms later if you encounter a problem when you are only handling a single multiplication between two quaternions, things get complicated fast. Once you start dealing with multiple rotations, tons of hard to understand behaviors will start showing up. It's tempting to start swapping multiplication orders at random in an effort to fix it, but that way lies madness.
It's best to build one brick at a time on a solid foundation.
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
I had all the Quaternion math right, and I mostly understand the basics of Quaternions now. I found the source of my strange twitching. I was adding the previous frame's AngularVelocity to the new result, in the hopes that when a box touched the roller it would preserve the rolling velocity and add the new one. I was wrong, and all it did was cause the twitching. How would I go about making sure that tubes first rotate around local Down each frame (if they're rotating from collision), and then rotate from GetAngularVelocity (if they're rotating from timing)? I'm thinking multiplication is probably the answer, but would I multiply the first argument of GetAngularVelocity or would I multiply the result of GetAngularVelocity? My first guess would be to Concantenate a Quaternion with the rotation representing 2pi around Down with the current first argument, but that makes things go all over the place. Could I do two GetAngularVelocites, and add them...? No, that doesn't work either. Do you have any idea how I could use AngularVelocity to first represent a rotation around Down and then around the thing I have already?
Re: Rotate with an offset
One of my previous posts in this thread shows how to construct an angular velocity from any rotation or starting and ending orientations. The sample code given rotates an object around world up and a local axis. It will need some adaptation, of course, but the core idea is there.Do you have any idea how I could use AngularVelocity to first represent a rotation around Down and then around the thing I have already?
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
Ah, okay. I think it would be working, except the LinearVelocity stuff is making it jitter and do all kinda of funky stuff. Is there a way to, like... Take the LinearVelocity from the machine rotation and subtract the roller rotation? Would that even work? Here's what I have right now:Norbo wrote:One of my previous posts in this thread shows how to construct an angular velocity from any rotation or starting and ending orientations. The sample code given rotates an object around world up and a local axis. It will need some adaptation, of course, but the core idea is there.Do you have any idea how I could use AngularVelocity to first represent a rotation around Down and then around the thing I have already?
Code: Select all
foreach(Tube t in tubeList)
{
if(t.Rotating)
t.Ent.AngularVelocity = GetAngularVelocity(
Quaternion.Concatenate(Quaternion.Conjugate(t.OriginalOrientation), t.Ent.Orientation),
Quaternion.Concatenate(
Quaternion.CreateFromAxisAngle(t.Ent.OrientationMatrix.Down, 2 * BaseGame.space.TimeStepSettings.TimeStepDuration),
curve.Evaluate(pathTime)),
BaseGame.space.TimeStepSettings.TimeStepDuration);
else
t.Ent.AngularVelocity = GetAngularVelocity(
Quaternion.Concatenate(Quaternion.Conjugate(t.OriginalOrientation), t.Ent.Orientation),
curve.Evaluate(pathTime),
BaseGame.space.TimeStepSettings.TimeStepDuration);
offset = t.ModelPosition - centerOfRotation;
if((pathTime * 1000) % MachineNumber > MachineNumber &&
(pathTime * 1000) % MachineNumber + deactivationTime < MachineNumber + deactivationTime)
endpoint = Vector3.Transform(t.Ent.Position, radiansToRotateQ);
else
endpoint = t.Ent.Position;
t.Ent.LinearVelocity = Vector3.Cross(t.Ent.AngularVelocity, offset) +
((endpoint - t.Ent.Position) * 0.2f / (float)BaseGame.space.TimeStepSettings.TimeStepDuration);
}
Re: Rotate with an offset
I'd need a more focused question before I can provide a good answer. There's lots of unknowns floating around right now and I wouldn't feel comfortable picking one of the possible interpretations and spending an hour constructing a solution for it 

-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
Alright, fair enough. What are the unknowns? I'll try to explain them as best I can.Norbo wrote:I'd need a more focused question before I can provide a good answer. There's lots of unknowns floating around right now and I wouldn't feel comfortable picking one of the possible interpretations and spending an hour constructing a solution for it
Re: Rotate with an offset
The best thing to do would be to ask the smallest possible questions that require the least context. That way I can answer unambiguously without worrying about whatever else is going on. "Big" questions are those which rely on a lot of context, such as a single question talking about the design of velocity control in general.
If it's difficult to create a small, targeted question, the best backup is to provide precise context. When dealing with complicated physics and velocity manipulation and behavior issues, it is almost impossible to explain clearly using words alone. Clear diagrams and recorded videos of problems would be extremely helpful. In the extreme case, runnable debuggable samples are even more helpful- they leave no room for ambiguity.
As for what specific details I'm missing, I can't really say. I just know there are things I don't know, and I know I need to know them to help effectively
It would be nice to have a one-stop summary of the ideal result (with diagrams), the current relevant state (diagrams/videos would be helpful), and an explanation of in what specific parts are going wrong (pointing to times in a video showing the issue, etc.). Then, specific, targeted questions built on that context could be asked and safely answered. Some things might be redundant with earlier posts, but it seems like things have changed enough over time that even the repeated data would be helpful in clarifying things.
The narrower the focus, the better.
If it's difficult to create a small, targeted question, the best backup is to provide precise context. When dealing with complicated physics and velocity manipulation and behavior issues, it is almost impossible to explain clearly using words alone. Clear diagrams and recorded videos of problems would be extremely helpful. In the extreme case, runnable debuggable samples are even more helpful- they leave no room for ambiguity.
As for what specific details I'm missing, I can't really say. I just know there are things I don't know, and I know I need to know them to help effectively

It would be nice to have a one-stop summary of the ideal result (with diagrams), the current relevant state (diagrams/videos would be helpful), and an explanation of in what specific parts are going wrong (pointing to times in a video showing the issue, etc.). Then, specific, targeted questions built on that context could be asked and safely answered. Some things might be redundant with earlier posts, but it seems like things have changed enough over time that even the repeated data would be helpful in clarifying things.
The narrower the focus, the better.
-
- Posts: 172
- Joined: Sat Sep 24, 2011 7:31 am
Re: Rotate with an offset
Alright. I'll make a video of it tomorrow, and link you to it.