2

I'm having some troubles converting quaternions into euler angles.

I've followed this C++ code found on Wikipedia and I think I did a pretty good job replicating it in assembly, the problem is that my code converts the WXYZ values to ZYX instead of the XYZ order.

C++ code: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Source_Code_2

(The provided asm code only calculates the Roll axis)

//ROLL
finit
fld dword ptr [WRot]
fld dword ptr [ZRot]
fmulp st(1),st(0)
fld dword ptr [XRot]
fld dword ptr [YRot]
fmulp st(1),st(0)
faddp st(1),st(0)
mov dword ptr [Math],40000000 //2
fld dword ptr [Math]
fxch
fmulp st(1),st(0)
fstp dword ptr [sinr_cosp] //sinr_cosp

finit
fld dword ptr [YRot]
fld dword ptr [YRot]
fmulp st(1),st(0)
fld dword ptr [ZRot]
fld dword ptr [ZRot]
fmulp st(1),st(0)
faddp st(1),st(0)
mov dword ptr [Math],40000000 //2
fld dword ptr [Math]
fxch
fmulp st(1),st(0)
mov dword ptr [Math],3F800000 //1
fld dword ptr [Math]
fxch
fsubp st(1),st(0)
fstp dword ptr [cosr_cosp] //cosr_cosp

finit
fld dword ptr [sinr_cosp]
fld dword ptr [cosr_cosp]
fpatan
mov dword ptr [Math],42652EE1 //57.29577951308232
fld dword ptr [Math]
fxch
fmulp st(1),st(0)
fstp dword ptr [Roll] //Roll value

Using https://quaternions.online/ I've set the following values like this:

w:  0.549
x:  0.747
y:  0.092
z:  0.363

When applying, the XYZ order it should output:

x:  100.000
y:  40.000
z:  20.000

But my code produces:

x:  98.527
y: -26.213
z:  36.644

How could I fix that? Wher can I find a proper working C++ code as reference for converting Quat to XYZ?

I don't think it's a problem with memory allocation, every floating value is 8byte apart from one another.

I've also found this interesting reply https://stackoverflow.com/a/27496984/13741865 which shows the different possible cases, but for me it's a bit complicated to understand as it doesn't have comments and I would love to know which formula is applied to which rotation (for the XYZ case).

I'm still in my early development in assembly and I might not have a decent enough knowlege of the terminology.

I hope I've added every possible detail, I thank you in advance for your attention.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Germ
  • 117
  • 6
  • 3
    Is there any reason why you wouldn't use SSE registers? Using the x87 FPU instruction set seems about the hardest way to go about this in assembler. – Brett Hale Sep 02 '20 at 12:51
  • All registers in the region I'm trying to modify are used and I've noticed some crashes when storing and restoring their original state, using the fpu works really well for me! – Germ Sep 02 '20 at 13:19
  • 3
    You ought to be able to single-step the code and compare its values with what you expect mathematically. Have you got a reasonable debugging setup to single-step assembly? If not, that is the first thing you should figure out anyway. – Nate Eldredge Sep 02 '20 at 21:51
  • 3
    Have you compiled and run the C++ code, and checked whether it matches what you expect? That would narrow down whether the issue is a bug or different convention in the original C++ code, or a problem with the way you converted that code to assembly. And if the former, it will be a lot easier to debug the C++ code. – Nate Eldredge Sep 03 '20 at 00:47

2 Answers2

2

The problem was the formula itself, the souce code provided in Wikipedia was made for the ZYX order (it was commented on the code above and I didn't noticed it).

What I did is take my time to read the answer provided here https://stackoverflow.com/a/27496984/13741865 , analyze the code and fix the one I've made.

This gives the right output

finit
fld dword ptr [WRot]
fld dword ptr [WRot]
fmulp
fld dword ptr [XRot]
fld dword ptr [XRot]
fmulp
faddp
fld dword ptr [YRot]
fld dword ptr [YRot]
fmulp
fsubp
fld dword ptr [ZRot]
fld dword ptr [ZRot]
fmulp
fsubp
fld dword ptr [XRot]
fld dword ptr [YRot]
fmulp
fld dword ptr [WRot]
fld dword ptr [ZRot]
fmulp
fsubp
mov dword ptr [Math],0 //hex of 0
fld dword ptr [Math]
mov dword ptr [Math],40000000 //hex of 2
fld dword ptr [Math]
fsubp
fxch
fmulp
fxch
fpatan
mov dword ptr [Math],42652ee1 //hex of 57.29577951308232
fld dword ptr [Math]
fxch
fmulp
fstp dword [Roll] //Roll value

Thanks everyone

Germ
  • 117
  • 6
  • 4
    Your code is longer than it needs to be. To square something, don't load it twice, just `fld` once and `fmul st(0)` like a normal person (or a compiler). Also, normally keep constants like `2.0` in memory instead of using mov-immediate to store to a temporary. Also, `fldz` to load a zero. And if you want to multiply by `-2.0`, use `fchs` to negate and `fadd st(0)` to double the top-of-stack value. – Peter Cordes Sep 04 '20 at 00:24
1

Maybe it is just the quaternion convention in the algorithm that is different from what you expected. Try feeding your algorithm the conjugate of the quaternion. I.e., reverse the signs of the Xrot, Yrot, and Zrot inputs. Unfortunately, it has been my experience that most quaternion information posted online rarely discusses the assumed quaternion convention for the algorithms posted (including the quaternions.online link you mention). It is entirely possible that the website you coded from assumes a different convention from the quaternions.online website.

James Tursa
  • 2,242
  • 8
  • 9