Rotating an object about local angles/axes

From Nexus Mods Wiki
Jump to: navigation, search

The functions SetAngle and Rotate rotate objects about the world axes. In order to apply local rotations, the angles need to be derived first. It is possible to rotate an object about local angles by first specifying the local angles, then extracting the world angles from the local rotation and applying those to the object.

Any local rotation is dependent on the order in which the individual rotations are applied. This is true because the local axes are dependent on each other.

Because of this, it is important to specify the axis order prior to calculating the world rotation.

An axis order of XYZ means that the X rotation is applied first, then the Y and lastly the Z rotation.


These declarations are needed:

;the object one wishes to rotate
  ref myObject

;specifes order of rotations.
  float axisOrder
  ;1 = xyz
  ;2 = xzy
  ;3 = yxz
  ;4 = yzx
  ;5 = zxy
  ;6 = zyx


;the local angles
  float localAngX
  float localAngY
  float localAngZ


;the world angles
  float worldAngX
  float worldAngY
  float worldAngZ


;sine and cosine of local x, y, z angles
  float sx
  float cx
  float sy
  float cy
  float sz
  float cz

;the cells of the rotation matrix
  float r11
  float r12
  float r13
  float r21
  float r22
  float r23
  float r31
  float r32
  float r33


The actual rotation code:

  set axisOrder to 3
  set sx to sin localAngX
  set cx to cos localAngX
  set sy to sin localAngY
  set cy to cos localAngY
  set sz to sin localAngZ
  set cz to cos localAngZ


;creation of rotation matrix in axis order
  if     (axisOrder == 1)  
    ;XYZ
    set r11 to ( cy *  cz)
    set r12 to ( cy * -sz)
    set r13 to (    sy   )
    set r21 to ( sx *  sy *  cz +  cx *  sz)
    set r22 to ( sx *  sy * -sz +  cx *  cz)
    set r23 to (-sx *  cy)
    set r31 to ( cx * -sy *  cz +  sx *  sz)
    set r32 to ( cx *  sy *  sz +  sx *  cz)
    set r33 to ( cx *  cy)

  elseif (axisOrder == 2)
    ;XZY
    set r11 to ( cz *  cy)
    set r12 to (   -sz   )
    set r13 to ( cz *  sy)
    set r21 to ( cx *  sz *  cy +  cx *  cz)
    set r22 to ( cx *  cz)
    set r23 to ( cx *  sz *  sy + -sx *  cy)
    set r31 to ( sx *  sz *  cy +  cx * -sy)
    set r32 to ( sx *  cz)
    set r33 to ( sx *  sz *  sy +  cx *  cy)

  elseif (axisOrder == 3)
    ;YXZ
    set r11 to ( cy *  cz +  sy *  sx *  sz)
    set r12 to ( cy * -sz +  sy *  sx *  cz)
    set r13 to ( sy *  cx)
    set r21 to ( cx *  sz)
    set r22 to ( cx *  cz)
    set r23 to (   -sx   )
    set r31 to (-sy *  cz +  cy *  sx *  sz)
    set r32 to ( sy *  sz +  cy *  sx *  cz)
    set r33 to ( cy *  cx)

  elseif (axisOrder == 4)
    ;YZX
    set r11 to ( cy *  cz)
    set r12 to ( cy * -sz *  cx +  sy *  sx)
    set r13 to ( cy * -sz * -sx +  sy *  cx)
    set r21 to (    sz   )
    set r22 to ( cz *  cx)
    set r23 to ( cz * -sx)
    set r31 to (-sy *  cz)
    set r32 to ( sy *  sz *  cx +  cy *  sx)
    set r33 to ( sy *  sz * -sx +  cy *  cx)

  elseif (axisOrder == 5)
    ;ZXY
    set r11 to ( cz *  cy +  sz *  sx * -sy)
    set r12 to (-sz *  cx)
    set r13 to ( cz *  sy +  sz *  sx *  cy)
    set r21 to ( sz *  cy +  cz *  sx *  sy)
    set r22 to ( cz *  cx)
    set r23 to ( sz *  sy +  cz * -sx *  cy)
    set r31 to ( cx * -sy)
    set r32 to (   sx    )
    set r33 to ( cx *  cy)
  
  else
    ;ZYX
    set r11 to ( cz *  cy)
    set r12 to (-sz *  cx +  cz *  sy *  sx)
    set r13 to ( sz *  sx +  cz *  sy *  cx)
    set r21 to ( sz *  cy)
    set r22 to ( cz *  cx +  sz *  sy *  sx)
    set r23 to ( cz * -sx +  sz *  sy *  cx)
    set r31 to (   -sy   )
    set r32 to ( cy *  sx)
    set r33 to ( cy *  cx)
  
  endif


;Extraction of worldangles from rotation matrix
  if     (r13 >  0.9998)
    ;positive gimbal lock

    set worldAngX to -ATan2 r32 r22
    set worldAngY to -90
    set worldAngZ to 0

  elseif (r13 < -0.9998)
    ;negative gimbal lock

    set worldAngX to -ATan2 r32 r22
    set worldAngY to 90
    set worldAngZ to 0

  else
    ;no gimbal lock

    set r23 to -r23
    set r12 to -r12
    set worldAngX to -ATan2 r23 r33
    set worldAngY to -ASin r13
    set worldAngZ to -ATan2 r12 r11

  endif


;apply extracted rotation
  myObject.setAngle x worldAngX
  myObject.setAngle y worldAngY
  myObject.setAngle z worldAngZ


Special Note

This code has been tested ingame and works reliably well. It is advided to pay special attention to the axis order.


Notes

  • Some types of object, specifically Actors, do not allow for rotation about all three axes. Other objects, such as activators have all three degrees of freedom.
  • Gimbal Lock is accounted for. Whenever two of the axes are orthogonal, becoming arbitrary, two of the axes (y and z) are reset to a more preferable orientation.
  • For a more in-depth discussion of the math behind this, see wikipedia.org and euclideanspace.com.


See Also