Difference between revisions of "User:DavidJCobb/Rotation Library"

Fixed the problems!
imported>DavidJCobb
(errors found! will correct!)
imported>DavidJCobb
(Fixed the problems!)
Line 1: Line 1:
I've created a rotation library that makes it easier to work with rotation math in Skyrim. Among other things, it can position and rotate one object relative to another for you. [[User:DavidJCobb/Rotation Library Tutorial|This tutorial]] takes you through creating a basic demo of the library, and assumes that you've compiled the (interdependent) scripts below.
I've created a rotation library that makes it easier to work with rotation math in Skyrim. Among other things, it can position and rotate one object relative to another for you. [[User:DavidJCobb/Rotation Library Tutorial|This tutorial]] takes you through creating a basic demo of the library, and assumes that you've compiled the (interdependent) scripts below.


Please use different [[Scriptname|script names]] if you choose to use these libraries, so as to avoid collisions with any edits or enhancements that other users may make in other mods.
Please use different [[Scriptname|script names]] if you choose to use these libraries, so as to avoid collisions with any edits or enhancements that other users may make in other mods. You should probably strip out all of the Debug.Trace() statements as well.


The relative-position code was adapted from [[Spatial functions and snippets#Rotation matrix|code originally written by Chesko]].
The relative-position code was adapted from [[Spatial functions and snippets#Rotation matrix|code originally written by Chesko]].
Line 7: Line 7:
Other editors: feel free to add this to whatever Papyrus categories you feel are relevant. :)
Other editors: feel free to add this to whatever Papyrus categories you feel are relevant. :)


----
== Changelog ==
 
;8/26/2014
'''UPDATE: The library is currently known to be inaccurate in the following cases.''' Inaccuracies typically present at exact multiples of 90 and 180; these tend to be where mathematical "singularities" lie.
:*I appear to have successfully fixed issues that arose when converting rotation matrices to axis angles, when the resulting axis-angle had an angle of exactly 180.
 
* Euler (180, 0, 90) doesn't convert properly to axis-angle or quaternion.
 
As I developed this library specifically for a current project, I have every intention of investigating and correcting these issues as I find them. I'll try to set up a test of all 90/180 combinations as soon as I find the time.


== Main code ==
== Main code ==
Line 83: Line 79:
{Converts a rotation matrix to axis angle, returning [x, y, z, angle]. The angle is in degrees. Tailored for Skyrim (extrinsic left-handed ZYX Euler).}
{Converts a rotation matrix to axis angle, returning [x, y, z, angle]. The angle is in degrees. Tailored for Skyrim (extrinsic left-handed ZYX Euler).}
   Float[] fOutput = new Float[4]
   Float[] fOutput = new Float[4]
  ;
  ; Determine the angle.
  ;
  Float fTrace = MatrixTrace(afMatrix)
  fOutput[3] = Math.acos((fTrace - 1) / 2)
   ;
   ;
   ; Determine the axis.
   ; Determine the axis.
Line 89: Line 90:
   fOutput[1] = afMatrix[2] - afMatrix[6]
   fOutput[1] = afMatrix[2] - afMatrix[6]
   fOutput[2] = afMatrix[3] - afMatrix[1]
   fOutput[2] = afMatrix[3] - afMatrix[1]
  If fOutput[3] == 180
      ;
      ; A 180-degree angle tends to lead to a zero vector as our axis.
      ; There seems to be a way to correct that...
      ;
      ; Source for the math: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
      ; Source for the math: http://sourceforge.net/p/mjbworld/discussion/122133/thread/912b44f7
      ;
      fOutput[0] = Math.sqrt((afMatrix[0] + 1) / 2)
      fOutput[1] = Math.sqrt((afMatrix[4] + 1) / 2)
      fOutput[2] = Math.sqrt((afMatrix[8] + 1) / 2)
      ;
      ; We don't know the signs of the above terms. Per our second
      ; source, we can start to figure that out by finding the largest
      ; term...
      ;
      Int iLargestIndex = 0
      Float fTemporary = fOutput[0]
      If fTemporary < fOutput[1]
        fTemporary = fOutput[1]
        iLargestIndex = 1
      EndIf
      If fTemporary < fOutput[2]
        fTemporary = fOutput[2]
        iLargestIndex = 2
      EndIf
      Int iIterator = 0
      While iIterator < 3
        Int iIndex = (iLargestIndex - 1) * 3 + iIterator
        If iIterator != iLargestIndex
            fOutput[iIterator] = fOutput[iIterator] * Sign(afMatrix[iIndex])
        EndIf
        iIterator += 1
      EndWhile
  EndIf
   ;
   ;
   ; Normalize the axis.
   ; Normalize the axis.
Line 96: Line 132:
   Else
   Else
       ;
       ;
       ; Edge-case caused a zero vector! Try using the Z-axis instead.
       ; Edge-case caused a zero vector! Dumb fallback to the Z-axis.
      ;
      ; (This is known to happen when dealing with rotation matrices that
      ; describe the Euler rotations (0, 0, 0) or (0, 0, 180). In those
      ; cases, the angle computed below is 0 and 180 respectively, so...)
       ;
       ;
       fOutput[0] = 0
       fOutput[0] = 0
Line 106: Line 138:
       fOutput[2] = 1
       fOutput[2] = 1
   EndIf
   EndIf
  ;
  ; Determine the angle.
  ;
  Float fTrace = MatrixTrace(afMatrix)
  fOutput[3] = Math.acos((fTrace - 1) / 2)
   ;
   ;
   ; Done!
   ; Done!
Anonymous user