A new way of interpolating: rotator problems

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

Astyanax

GotoState('Coding');
I'm working on a new way of interpolating (for use in a map): you do follow a predefined path, but you're free to look around and up & down.

The interpolating part works fine, but I'm having one problem:
There are 2 rotators at work while a playerpawn is interpolating: the rotation of the path and the ViewRotation of the playerpawn. So the flyby has given the playerpawn a rotation (Pitch,Yaw,Roll) and now the playerpawn decides to look to the right. This means I have to 'add Yaw' relative to his rotation. However Yaw is executed before Pitch and Roll, so adding Yaw directly to his Rotation will not rotate him relatively! (I hope this makes sense :hmm:.) Same problem goes for relatively adding Pitch.

I probably have to convert his rotation to a vector (GetAxes()??) first and then 'do something to add the Yaw', and finally convert it back to a rotator (OrthoRotation()??) to get the relative rotation.

Can somebody help me out here?
 
Hey Asty

not sure if i answered last time, but yeah if you can get any use out of those custom actors at all, great!

Dont' know the answer to your question but i believe the order of the rotator components becomes critical in some situations, and can result in the "collapse" of one of the dimensions (if you go from a rotator to a vector then back to a rotator i think you lose the roll component). Might take it with a grain of salt but there's an interesting description of the situation (quaternions, Euler angles) here:

http://micheaux.etc.cmu.edu/~cort/coyote210/Docs/rotations.html
 

Astyanax

GotoState('Coding');
I've waited a few days, but navboy ( :tup: ) seems to be the only one responding to my calls for help... :(


Good link! It covers my problem exactly, but... they say I have to use quaternions to solve my rotation problem. QuaterWHAT?? Quaternions, four-dimensional vector quantities! OH MY GOD what did I get myself into?! :eek: I've tried to read most of the calculations with quaternions, but thinking in 4D gives me a headache... :rolleyes:

/me takes an aspirin and continues reading...
 

tarquin

design is flawed
Oct 11, 2000
3,945
0
36
UK
www.planetunreal.com
Take a look at the code for my Extruder brushbuilder.

I coded quaternion rotation for that.
Admittedly, there's still a problem when the path heads straight down the z-axis, but I gave up at that point so I don't know if it's a bug or inherent in the maths.

Anyway, the script has functions to convert to & from quaternions.

(if you take a look you may even spot the z-axis bug, it's probably something obvious. :D )

and boom! there's another good reason for not having a code obfuscator!

BTW: it may help to think of quaternions as an extension of complex numbers, not as vectors. Q and R^4 are fairly different beasts.
 

Astyanax

GotoState('Coding');
Hey tarquin, I've looked at your extruder code before, but I didn't understand much of it, because I've only found out about quaternions 2 days ago... :B

I found some nice piece of UScript here with algorithms for rotating a player using quaternions.

I've just looked into your extruder code again and I saw some very interesting stuff: Struct quat/matrix3x3, new * operators, RotationToQuat, QuatToMatrix...

Can I use algorithms from your extruder that are applicable to my interpolation script? Your extruder script begins with "you may NOT re-use or alter or recompile this code without my express permission", so hereby I ask your permission. :) Of course you'll get full credit (which I was already going to give as I've made lots of brushes in my map with your extruder).

I noticed that you make (almost) no use of rotators. Do you have any idea how to make that RotationToQuat algorithm eat rotators instead of vectors?
 

tarquin

design is flawed
Oct 11, 2000
3,945
0
36
UK
www.planetunreal.com
I've put all the code for quaternion rotation on the Unreal Wiki at:

http://www.imahosting.com/unrealwiki/cgi-bin/wiki.cgi?Quaternion_Rotation

Note that there is a bug that crops up in 180 degree rotation. The page at http://micheaux.etc.cmu.edu/~cort/coyote210/Docs/rotations.html mentions buggy behaviour as well.

I've missed a function off that list too.... :rolleyes: And according to the above link, normalization of the quaternion isn't required for a small number of multiplications.

The stuff on that page isn't technically mine since they're plain maths functions. In fact, consider that page of the Unreal Wiki to be the open repository for UScript quaternion rotation -- help yourself & if you improve on it please make edits.

I guess I should probably make a proper announcement of the Wiki site on this board. :D
 

Astyanax

GotoState('Coding');
Still working on the code... I'm having trouble converting a quaternion back into an Euler angle (searching the web now for algorithms).

tarquin, you mentioned there's a bug in your extruder maths. I have found a bug, but I don't know if this is causing the error.

Code:
final operator(16) quat * ( quat Q1 , quat Q2 )
 {
  local vector V1 , V2 , Vp ;
  local quat Qp ;
  V1 = eVect( Q1.X , Q1.Y , Q1.Z ) ;
  V2 = eVect( Q2.X , Q2.Y , Q2.Z ) ;
  Qp.W = Q1.W * Q2.W - ( V1 dot V2 ) ;
  Vp = ( Q1.W * V2 ) + ( Q2.W * V1 ) - ( V1 cross V2 ) ;
  Qp.X = Vp.X ;
  Qp.Y = Vp.Y ;
  Qp.Z = Vp.Z ;
  return Qp ;
 }

"- (V1 cross V2)" should be "+ (V1 cross V2)" or "- (V2 cross V1)".
 

tarquin

design is flawed
Oct 11, 2000
3,945
0
36
UK
www.planetunreal.com
I think that it's a minus because of Unreal's left-handed coordinate system.
I can never remember how the cross operator behaves in UnrealScript -- is (1,0,0) x (0,1,0) = (0,0,1) or (0,0,-1) ?
The first makes sense from the point of view of the matrix determinant way of calculating cross, the second from the definition that A,B,AxB form a RH set.

Anyway, I've just tried changing the sign in the Extruder script: the bug is still there & in fact it's made worse.

BTW the bug occurs when the path goes straight down the z axis to start, eg:
(0,0,0)
(0,0,-1)
(1,0,-1)

PS. just checked. It's (0,0,1) :) -- ie Unreal takes cross to be dependent on the co-ordinate system.
 
Last edited:

Astyanax

GotoState('Coding');
Hmmm, I guess you were right after all then... :)

Left-handed, right-handed... it's very annoying when you find a good algorithm on the 'net which doesn't appear to work in Unreal, because it uses a different coordinate system. :mad: Also all algorithms to convert and extract Euler angles to and from quaternions are based on the assumption that you first calculate Pitch, then Yaw, then Roll. But Unreal first calculates Yaw, then Pitch, then Roll. :mad: I'm now trying to construct my own algorithms by analogy with the ones I found, but using Unreal's order of calculating.
 

TaoPaiPai

Commisaire Van Loc
Jun 13, 2000
1,626
0
0
Matnik
Visit site
To avoid the Euler problem and if you want to just access the yaw of the player you can use the Rotation variable instead of the viewrotation because the mesh itself doesn't really change pitch during the game
..Only problem is that rotation doesn't always correspond to viewrotation.
(I know it's not applicable in the particular case we're talking about :( ...)
 

tarquin

design is flawed
Oct 11, 2000
3,945
0
36
UK
www.planetunreal.com
Originally posted by Astyanax
Hmmm, I guess you were right after all then... :)

I'm not sure I am now... :con: the more I think about it, the more I wonder. If (1,0,0) cross (0,1,0) = (0,0,1) in Unreal, then that function should return the correct answer for the quaternion rotation.
I last worked on last Summer, so I can't really remember. I think the quaternion rotation was glitchy so I figured that was the bug, and the minus sign helped... but thinking about it now I think I might have fixed the wrong thing.
 

Astyanax

GotoState('Coding');
Originally posted by tarquin
I'm not sure I am now... :con: the more I think about it, the more I wonder. If (1,0,0) cross (0,1,0) = (0,0,1) in Unreal, then that function should return the correct answer for the quaternion rotation.
I last worked on last Summer, so I can't really remember. I think the quaternion rotation was glitchy so I figured that was the bug, and the minus sign helped... but thinking about it now I think I might have fixed the wrong thing.

You don't have to use dot and cross product of vectors to multiply quaternions. Check out what I found on the 'net:

Since a unit quaternion represents an orientation in 3D space, the multiplication of two unit quaternions will result in another unit quaternion that represents the combined rotation. Amazing, but it's true.

Given two unit quaternions

Q1=(w1, x1, y1, z1);
Q2=(w2, x2, y2, z2);

A combined rotation of unit two quaternions is achieved by

Q1 * Q2 =( w1.w2 - v1.v2, w1.v2 + w2.v1 + v1*v2)

where

v1= (x1, y1, z1)
v2 = (x2, y2, z2)

and both . and * are the standard vector dot and cross product.

However an optimization can be made by rearranging the terms to produce

w = w1w2 - x1x2 - y1y2 - z1z2
x = w1x2 + x1w2 + y1z2 - z1y2
y = w1y2 + y1w2 + z1x2 - x1z2
z = w1z2 + z1w2 + x1y2 - y1x2

Of course, the resultant unit quaternion can be converted to other representations just like the two original unit quaternions. This is the real beauty of quaternions - the multiplication of two unit quaternions in 4D space solves gimbal lock because the unit quaternions lie on a sphere.

Be aware that the order of multiplication is important. Quaternion multiplication is not commutative, meaning

q1 * q2 does not equal q2 * q1

I've tested the optimization using a rotation matrix: first I made my test script convert some Euler angle to a rotation matrix and printed it. Then I converted the Euler angle to a quaternion using:
Qpitch = [ cos(pitch/2), sin(pitch/2), 0, 0 ]
Qyaw = [ cos(yaw/2), 0, sin(yaw/2), 0 ]
Qroll = [ cos(roll/2), 0, 0, sin(roll/2) ]
Q = Qyaw * Qpitch * Qroll (using the above optimization for *)
Then I converted quaternion Q to a rotation matrix and printed it. The result: for any given Euler angle the 2 printed rotation matrices were identical!

So now I have a working RotatorToQuat(..) function and a working quaternion multiplication operator. Only thing left now is a way to convert a quaternion back to an Euler angle. Problem is however that I have to use acos, asin and/or atan. But all 3 function always return a value in a range of one times Pi; while Pitch, Yaw and Roll have a range of two times Pi. What to do? :(
 

Astyanax

GotoState('Coding');
Originally posted by TaoPaiPai
To avoid the Euler problem and if you want to just access the yaw of the player you can use the Rotation variable instead of the viewrotation because the mesh itself doesn't really change pitch during the game
..Only problem is that rotation doesn't always correspond to viewrotation.
(I know it's not applicable in the particular case we're talking about :( ...)

Well you're partially right:
Rotation.Yaw = ViewRotation.Yaw (ViewRotation controls Rotation)
ViewRotation.Roll = Rotation.Roll (Rotation controls ViewRotation)
ViewRotation.Pitch = independent, but it does affect the animation of the mesh (you can see a player look up and down without actually rotating)
 

tarquin

design is flawed
Oct 11, 2000
3,945
0
36
UK
www.planetunreal.com
yeah, it's pretty much a mathematical co-incidence that a piece of the quaternion multiplication formula looks like the vector cross product.

The inverse trig stuff might well be the cause of the bug in the Extruder. Being out by pi can flip the sign of the result, so it could make sense that making the cross part negative would partially cure it.

There are inverse trig functions here:
http://www.imahosting.com/unrealwiki/cgi-bin/wiki.cgi?Useful_Maths_Functions

but my brain hurts too much to work out their range :(
 

usaar33

Un1337
Mar 25, 2000
808
0
0
Unknown
www.UsAaR33.com
hmm..

Well, this is my quat system I will be using for ONP that I got from a book. Not even compiled :p

Code:
struct Quaternion
{
  var () float n; //scalar
  var () vector v; //vector
};

static final function Quaternion Quad(float a, float b, float c, float d){  //quaternion constructor
   local Quaternion Temp;
   Temp.n=a;
   Temp.v.x=b;
   Temp.v.y=c;
   Temp.v.z=d;
   return Temp;
}

static final operator(16) Quaternion  * ( Quaternion A, Quaternion B ){   //mult quads
  return Quad(
    A.n*B.n-A.v.x*B.v.x-A.v.y*B.v.y-A.v.z*B.v.z,
    A.n*B.v.x+A.v.x*B.n+A.v.y*B.v.z-A.v.z*B.v.y,
    A.n*B.v.y+A.v.y*B.n+A.v.z*B.v.x-A.v.x*B.v.z,
    A.n*B.v.z+A.v.z*B.n+A.v.x*B.v.y-A.v.y*B.v.x
  );
}

static final operator(16) Quaternion  * ( Quaternion A, Vector B ){   //mult quad by vector
  return Quad(
    -(A.v.x*B.x+A.v.y*B.y+A.v.z*B.z),
    A.n*B.x+A.v.y*B.z-A.v.z*B.y,
    A.n*B.y+A.v.z*B.x-A.v.x*B.z,
    A.n*B.z+A.v.x*B.y-A.v.y*B.x
  );
}
static final operator(16) Quaternion  * ( Vector B, Quaternion A ){   //mult quad by vector (other order
  return A*B;
}
static final operator(16) Quaternion  * ( Quaternion A, float B ){   //mult quads by scalar
return Quad(
    A.n*B,
    A.v.x*B,
    A.v.y*B,
    A.v.z*B
  );
}

static final operator(16) Quaternion  * ( float B, Quaternion A ){   //mult quads by scalar (other order)
  return A*B;
}

static final operator(20) Quaternion  + ( Quaternion A, Quaternion B ){   //add quads
 return Quad(
    A.n+B.n,
    A.v.x+B.V.x,
    A.v.y+B.V.y,
    A.v.z+B.V.z
  );
}

static final operator(20) Quaternion  - ( Quaternion A, Quaternion B ){   //subtracts quads
  return Quad(
    A.n-B.n,
    A.v.x-B.V.x,
    A.v.y-B.V.y,
    A.v.z-B.V.z
  );
}

static final operator(34) Quaternion  += ( out Quaternion A, Quaternion B ){   //add quads to
  A.n-=B.n;
  A.v.x-=B.V.x;
  A.v.y-=B.V.y;
  A.v.z-=B.V.z;

}

static final operator(34) Quaternion  -= ( out Quaternion A, Quaternion B ){   //subtracts quads from
  A.n-=B.n;
  A.v.x-=B.V.x;
  A.v.y-=B.V.y;
  A.v.z-=B.V.z;
}

static final operator(34) Quaternion  *= ( out Quaternion A, float B ){   //mult by scalar
  A.n*=B;
  A.v.x*=B;
  A.v.y*=B;
  A.v.z*=B;
}

static final operator(34) Quaternion  /= ( out Quaternion A, float B ){   //divide by scalar
  A.n/=B;
  A.v.x/=B;
  A.v.y/=B;
  A.v.z/=B;
}

static final preoperator  Quaternion  ~  ( Quaternion A ){      //conjugate (negative of vector part)
  return Quad(A.n,-v.x,-v.y,-v.z);
}

static final function float QSizeSquared(Quaternion A){ //get size (faster)
   return Square(A.n)+Square(Q.v.x)+Square(Q.v.y)+Square(Q.v.z);
}

static final function float QSize(Quaternion A){ //get size  (magnitude)
   return sqrt(QSizeSquared(A));
}

static final function Quaternion QNormal(Quaternion A){ //normalize
   return A/=QSize(A);
}

static final function float QGetAngle(Quaternion A){ //get angle about quad vector axis
   return 2*acos(A.n);
}

static final function vector QGetAxis(Quaternion A){ //get unit vector along rot.
   return Normal(A.v);
}

static final function Quaternion QRotate(Quaternion A, Quaternion B){ //rotate A by B
   return A*B*(~A);
}

static final function Vector QVRotate(Quaternion A, Vector B){ //rotate vector B by quaternion B
   return (A*B*(~A)).V;
}

static final function Quaternion ROTtoQuat(Rotator A){
  local float pitch;  //converted to radians.
  local float yaw;
  local float roll;
  local float cyaw, cpitch, croll, syaw, spitch, sroll;  //multiplies
  local float cyawcpitch, syawspitch, cyawspitch, syawcpitch;

  pitch=A.pitch/UURot;
  yaw=A.yaw/UURot;
  roll=A.roll/UURot;

  cpitch=cos(0.5*pitch);
  cyaw=cos(0.5*yaw);
  croll=cos(0.5*roll);
  spitch=sin(0.5*pitch);
  syaw=sin(0.5*yaw);
  sroll=sin(0.5*roll);

  cyawcpitch=cyaw*cpitch;
  syawspitch=syaw*spitch;
  cyawspitch=cyaw*spitch;
  syawcpitch=syaw*cpitch;

  return Quad(
    cyawcpitch * croll + syawspitch * sroll,
    cyawcpitch * sroll - syawspitch * croll,
    cyawspitch * croll + syawcpitch * sroll,
    syawcpitch * croll - cyawspitch * sroll
  );
}

static final function Rotator QuatToRot(Quaternion q){
 local float r11, r21, r31, r32, r33, r12, r13;
 local float q00, q11, q22, q33;
 local float tmp;

 q00 = Square (q.n);
 q11 = Square (q.v.x);
 q22 = Square (q.v.y);
 q33 = Square (q.v.z);

 r11 = q00 + q11 - q22 -q33;
 r21 = 2 * (q.v.x*q.v.y +  q.n*q.v.z);
 r31 = 2 * (q.v.x*q.v.z - q.n*q.v.y);
 r32 = 2 * (q.v.y*q.v.z + q.n*q.v.x);
 r33 = q00 - q11 - q22 +q33;

 tmp = abs(r31);
 if (tmp>(999999.0/1000000.0)){ //uscript sux with float consts...
    r12 = 2 * (q.v.x*q.v.y - q.n*q.v.z);
    r13 = 2 * (q.v.x*q.v.z + q.n*q.v.y);
    return Rot(-(pi/2)*(r31/temp)*uurot, atan2(-r12,-r31*r13)*uurot, 0);
 }
 return Rot(asin(-r31)*uurot, atan2(r21, r11)*uurot, atan2(r32,r33)*uurot);
}

Probably won't compile knowing me, but oh well :p

And it does require my inverse trig functions (at the link tarq posted)
 

Astyanax

GotoState('Coding');
Re: hmm..

Originally posted by usaar33
Well, this is my quat system I will be using for ONP that I got from a book. Not even compiled :p

(code)

Probably won't compile knowing me, but oh well :p

And it does require my inverse trig functions (at the link tarq posted)

WOW that's some nice collection of algorithms for quats! :tup:

But I got my algorithms working just before I read your code. :cool: I can finally add a rotator relative to another one! I'll search through your code to see if I can find any algorithms that are better and faster than the ones I'm currently using.

Just wondering... Unreal calculates rotators in the order Yaw->Pitch->Roll, but all algorithms about convertion between rotators and quaternions I found on the 'net are using the order Pitch->Yaw->Roll. This seems to be the standard. If your book follows the 'standard' as well, then some functions in your code may produce wrong answers!
 

usaar33

Un1337
Mar 25, 2000
808
0
0
Unknown
www.UsAaR33.com
Re: Re: hmm..

Originally posted by Astyanax


WOW that's some nice collection of algorithms for quats! :tup:

But I got my algorithms working just before I read your code. :cool: I can finally add a rotator relative to another one! I'll search through your code to see if I can find any algorithms that are better and faster than the ones I'm currently using.

Just wondering... Unreal calculates rotators in the order Yaw->Pitch->Roll, but all algorithms about convertion between rotators and quaternions I found on the 'net are using the order Pitch->Yaw->Roll. This seems to be the standard. If your book follows the 'standard' as well, then some functions in your code may produce wrong answers!

I always believed unreal used the standard pitch-yaw-and roll system as well?????

And I also thought it used a right-handed system as well.