SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Filterscripts > Includes

Reply
 
Thread Tools Display Modes
Old 30/05/2016, 10:01 PM   #1
IllidanS4
Huge Clucker
 
IllidanS4's Avatar
 
Join Date: Feb 2013
Posts: 286
Reputation: 109
Default i_quat 1.4 Quaternions and spatial geometry

i_quat
Download
Notice: GetVehicleRotationQuat may return incorrect results for vehicles which are unoccupied. Better check if there's a driver inside, if you want the correct rotation.

What does this do?
This library will make your spatial geometry dreams come true! It contains various functions to convert between relative and absolute coordinates and rotation, as well as Euler and quaternion conversion and basic quaternion manipulation.

What the sprunk is a quaternion?
SA-MP uses mostly rotation expressed in Euler angles (XYZ). However, one function (and more with YSF) uses a different method of rotation - quaternions. A quaternion (unit quaternion, specifically, also known as a versor) is a four-component "number" which represents a 3D rotation. It has many advantages over Euler angles, including unambiguity, easy composition and decomposition, and painless-to-implement operations. A quaternion represents an axis, specified by a unit vector, and a rotation around that axis, instead of 3 axes like in Euler. Algebraically, quaternions are an extension of complex numbers.

Changelog
1.4 - added GetQuatInverse (was in the native list, but without an actual implementation), corrected GetQuaternionAngles to account for gimbal lock (was returning imprecise values near the poles), and added a bunch of utility macros.
1.3 added GetVehicleRotationQuatFixed which returns correct values for unoccupied vehicles. GetVehicleMatrix must be provided by a plugin.
1.2 trigonometric functions won't return NaN for invalid values, rather they clamp their arguments to (-1; 1).
1.1 fixed math operations.
1.0 initial release.

What are the functions?

The most useful functions
GetVehicleRotation(vehicleid, &Float:x, &Float:y, &Float:z)
Returns a vehicle's rotation in Euler angles

The following bunch of functions are meant for conversion between absolute and relative coordinates. By absolute I don't mean world coordinates, but simply coordinates unattached to any body. A body can be an object, vehicle or player, which can have objects attached to. The body is represented with a quaternion or Euler angles specifying its rotation. The second parameter is an input, either a 3D vector (XYZ offsets) or an Euler or quaternion rotation. The third parameter is the output of the transformation.

The first part of the function's name is what will be converted, the second part is whether from relative to absolute or vice versa, and optinally it can end with Quat, meaning the rotation of the body is specified with a quaternion, otherwise with Euler angles.
VectorRelToAbsQuat(Float:q[4], Float:v1[3], Float:v2[3])
Converts a vector in coordinates relative to a body with rotation specified by a quaternion to a vector in absolute coordinates
RotationRelToAbsQuat(Float:q[4], Float:r1[3], Float:r2[3])
Converts an Euler rotation relative to a body with rotation specified by a quaternion to an absolute rotation
stock
QuaternionRelToAbsQuat(Float:q[4], Float:q1[4], Float:q2[4])
Converts a quaternion rotation relative to a body with rotation specified by a quaternion to an absolute rotation
VectorRelToAbs(Float:r[3], Float:v1[3], Float:v2[3])
Converts a vector in coordinates relative to a body with rotation specified by Euler angles to a vector in absolute coordinates
RotationRelToAbs(Float:r[3], Float:r1[3], Float:r2[3])
Converts an Euler rotation relative to a body with rotation specified by Euler angles to an absolute rotation
stock
QuaternionRelToAbs(Float:r[3], Float:q1[4], Float:q2[4])
Converts a quaternion rotation relative to a body with rotation specified by Euler angles to an absolute rotation
stock
VectorAbsToRelQuat(Float:q[4], Float:v1[3], Float:v2[3])
Converts a vector in absolute coordinates to a vector in coordinates relative to a body with rotation specified by a quaternion
RotationAbsToRelQuat(Float:q[4], Float:r1[3], Float:r2[3])
Converts an Euler rotation to a rotation relative to a body with rotation specified by a quaternion
QuaternionAbsToRelQuat(Float:q[4], Float:q1[4], Float:q2[4])
Converts an absolute quaternion rotation to a rotation relative to a body with rotation specified by Euler angles
VectorAbsToRel(Float:r[3], Float:v1[3], Float:v2[3])
Converts a vector in absolute coordinates to a vector in coordinates relative to a body with rotation specified by Euler angles
RotationAbsToRel(Float:r[3], Float:r1[3], Float:r2[3])
Converts an Euler rotation to a rotation relative to a body with rotation specified by Euler angles
QuaternionAbsToRel(Float:r[3], Float:q1[4], Float:q2[4])
Converts an absolute quaternion rotation to a rotation relative to a body with rotation specified by Euler angles

Quaternion operations
IsValidQuaternion(Float:q[4])
Checks if a quaternion is a valid rotation quaternion
GetQuaternionAngles(Float:w, Float:x, Float:y, Float:z, &Float:xa, &Float:ya, &Float:za)
Returns a set of Euler angles from a quaternion
GetRotationQuaternion(Float:x, Float:y, Float:z, &Float:qw, &Float:qx, &Float:qy, &Float:qz)
Creates a quaternion from Euler angles
GetQuaternionVector(Float:qw, Float:qx, Float:qy, Float:qz, &Float:x, &Float:y, &Float:z)
Returns the vector component of a quaternion
GetQuaternionAngle(Float:w, &Float:a)
Returns the angle component of a quaternion
RotateVectorQuat(Float:v1[3], Float:q[4], Float:v2[3])
Rotates a vector with a specified quaternion performing a conjugation v2 = q v1 q*
GetQuatConjugate(Float:q1[4], Float:q2[4])
Returns the conjugate of a quaternion, with all non-real component inversed
GetQuatInverse(Float:q1[4], Float:q2[4])
Returns the inverse of a quaternion, that is the conjugate divided by the norm squared.
GetQuatProduct(Float:q1[4], Float:q2[4], Float:q3[4])
Returns the Hamilton product of two quaternions

There are also macros to help you working with arrays, like SET_A3 to copy one array (size 3) to another, ADD_A3 to add two arrays, or MUL_A3 to multiply an array with a number. To expand an array when passing its values as parameters, you can use the EXP_A3 macro.

What can this be used for?
  • Editing attached objects. Normally, you can't use EditObject on an attached object, like neons on a vehicle for example, but if you obtain its attached offsets and rotation, you can use VectorRelToAbsQuat and RotationRelToAbsQuat to "remove" the vehicle's rotation from them and then simply detach the object (attach it to INVALID_VEHICLE_ID) and set its position to that. After you're done editing, repeat the process backwards, this time with VectorAbsToRelQuat and RotationAbsToRelQuat:
    Code:
    stock UnattachObject(objId, veh) //Unattachs the object from a vehicle
    {
        new Float:q[4];
        GetVehicleRotationQuat(veh, q[0], q[1], q[2], q[3]);
        if(!IsValidQuaternion(q)) return; //Vehicles left unoccupied for some time will return wrong values
        
        new Float:a[3], Float:ar[3];
        GetObjectAttachedOffset(objId, a[0], a[1], a[2], ar[0], ar[1], ar[2]); //requires YSF
        
        VectorRelToAbsQuat(q, a, a); //a is now a world-relative vector
        RotationRelToAbsQuat(q, ar, ar); //ar is now a world-relative rotation
        
        new Float:x, Float:y, Float:z;
        GetVehiclePos(veh, x, y, z);
        
        AttachObjectToVehicle(objId, INVALID_VEHICLE_ID, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
        SetObjectPos(objId, x+a[0], y+a[1], z+a[2]);
        SetObjectRot(objId, ar[0], ar[1], ar[2]);
    }
    
    stock ReattachObject(objId, veh) //Reattachs the object to a vehicle
    {
        new Float:q[4];
        GetVehicleRotationQuat(veh, q[0], q[1], q[2], q[3]);
        if(!IsValidQuaternion(q)) return INVALID_VEHICLE_ID;
        
        new Float:a[3], Float:ar[3];
        GetObjectPos(objId, a[0], a[1], a[2]);
        GetObjectRot(objId, ar[0], ar[1], ar[2]);
        
        new Float:x, Float:y, Float:z;
        GetVehiclePos(veh, x, y, z);
        a[0] -= x;
        a[1] -= y;
        a[2] -= z;
        
        VectorAbsToRelQuat(q, a, a); //a is now a vehicle-relative vector
        RotationAbsToRelQuat(q, ar, ar); //a is now a vehicle-relative rotation
        
        AttachObjectToVehicle(objId, veh, a[0], a[1], a[2], ar[0], ar[1], ar[2]);
    }
  • Boosting vehicle forward:
    Code:
    new veh = GetPlayerVehicleID(playerid);
    new Float:q[4];
    GetVehicleRotationQuat(veh, q[0], q[1], q[2], q[3]);
    new Float:v[3];
    v[1] = 10.0; //10 m/s forward
    VectorRelToAbsQuat(q, v, v); //v is now an absolute vector, usable in the world coordinate system
    SetVehicleVelocity(veh, v[0], v[1], v[2]);
  • If you are building a movable (space)ship with attached objects in a map editor, you can compute their relative offsets and rotations with VectorAbsToRel and RotationAbsToRel, specifying the ship's body rotation.
  • And much more...

How did you come up with all this maths stuff?
Well, for my mapping/filming/freeroam/anything server, a possibility to edit an attached object would have had been always neat for mappers. However, I had struggled with correctly converting the Euler angles to quaternion and vice-versa, and many formulas I've found on the internet were simply incorrect. The reason is rather unusual ordering and orientation of the axes and angles in GTA. Finally, thanks to brilliant people on math@stackexchange, I was able to get the correct conversion formula and begin to implement all the functions.
__________________
PawnPlus
YSF
Yet Another Lua Plugin
Long Function Names
 i_quat.inc 

kingsofsa.cz:8888 running Cinematic Mode

Last edited by IllidanS4; 13/10/2018 at 09:26 PM.
IllidanS4 is online now   Reply With Quote
Old 30/05/2016, 10:48 PM   #2
PrO.GameR
Gangsta
 
PrO.GameR's Avatar
 
Join Date: Oct 2012
Posts: 731
Reputation: 121
Default Re: i_quat - Quaternions and spatial geometry

I would suggest you to add the very main usage it has as the example, turning a position into a vehicle's offset, people around tend to get very confused when decent examples are not offered, using all the functions provided wrong. seeing how you actually use a combination of these to turn say an object position into a vehicle's offsets to attach it to the vehicle tends to bring more casual usages of the include.

Great job either way tho, well done.
__________________
Blueberry Prison Roleplay will be back soon!
Follow the forums for more information about opening day.

Forums
PrO.GameR is offline   Reply With Quote
Old 31/05/2016, 09:10 AM   #3
IllidanS4
Huge Clucker
 
IllidanS4's Avatar
 
Join Date: Feb 2013
Posts: 286
Reputation: 109
Default Re: i_quat - Quaternions and spatial geometry

Thanks for the tip, check the first example.
__________________
PawnPlus
YSF
Yet Another Lua Plugin
Long Function Names
 i_quat.inc 

kingsofsa.cz:8888 running Cinematic Mode
IllidanS4 is online now   Reply With Quote
Old 05/06/2016, 04:44 PM   #4
TwisT3R
Little Clucker
 
TwisT3R's Avatar
 
Join Date: Mar 2012
Location: Hungary
Posts: 24
Reputation: 16
Default Re: i_quat - Quaternions and spatial geometry

Hi!
Looks like ReattachObject does not works fine.
https://www.*******.com/watch?v=pxh1oGxGxyE
This happens when i try to use it.
Code:
http://pastebin.com/wNAiPqtA

If the problem is with my code, please help me fix it, because i dont know how to use quaternions..
Thanks!
__________________
TwisT3R is offline   Reply With Quote
Old 05/06/2016, 05:25 PM   #5
IllidanS4
Huge Clucker
 
IllidanS4's Avatar
 
Join Date: Feb 2013
Posts: 286
Reputation: 109
Default Re: i_quat - Quaternions and spatial geometry

Oh I am sorry, I forgot to include a warning - if the vehicle is left unoccupied, the quaternion returned by GetVehicleRotationQuat will be incorrect. IsValidQuaternion tries to compensate for it, but even some invalid quaternions are technically valid, and the object will be reattached with incorrect offsets. I bet it's a meter under the vehicle. Anyway, you simply have to be in the vehicle to correctly reattach the object. Disable the engine to move the camera without driving forward.
__________________
PawnPlus
YSF
Yet Another Lua Plugin
Long Function Names
 i_quat.inc 

kingsofsa.cz:8888 running Cinematic Mode
IllidanS4 is online now   Reply With Quote
Old 05/06/2016, 05:58 PM   #6
TwisT3R
Little Clucker
 
TwisT3R's Avatar
 
Join Date: Mar 2012
Location: Hungary
Posts: 24
Reputation: 16
Default Re: i_quat - Quaternions and spatial geometry

As you said, now, i was in the car and i turned off the engine.
I have a new code this time. Works almost well but when i press the save button, the object shows up randomly as you can see in the video below.
https://*********/z6a6rGzT7GI

http://pastebin.com/xfh4jaXN
This time i used your original ReattachObject without Dynamic objects...
__________________
TwisT3R is offline   Reply With Quote
Old 05/06/2016, 07:40 PM   #7
IllidanS4
Huge Clucker
 
IllidanS4's Avatar
 
Join Date: Feb 2013
Posts: 286
Reputation: 109
Default Re: i_quat - Quaternions and spatial geometry

Sorry, I see I've made a mistake. When I was "fixing" RotateVectorQuat to be more mathematically accurate, I forgot to update the other functions. Script updated to 1.1, please check it. I hope its all okay now.
__________________
PawnPlus
YSF
Yet Another Lua Plugin
Long Function Names
 i_quat.inc 

kingsofsa.cz:8888 running Cinematic Mode
IllidanS4 is online now   Reply With Quote
Old 05/06/2016, 10:25 PM   #8
TwisT3R
Little Clucker
 
TwisT3R's Avatar
 
Join Date: Mar 2012
Location: Hungary
Posts: 24
Reputation: 16
Default Re: i_quat - Quaternions and spatial geometry

Finally it worked. Thank you

https://www.*******.com/watch?v=ylbB...ature=*********
__________________
TwisT3R is offline   Reply With Quote
Old 05/01/2017, 10:12 PM   #9
IllidanS4
Huge Clucker
 
IllidanS4's Avatar
 
Join Date: Feb 2013
Posts: 286
Reputation: 109
Default Re: i_quat - Quaternions and spatial geometry

Added GetVehicleRotationQuatFixed which returns correct values for unoccupied vehicles. GetVehicleMatrix must be provided by a plugin, like YSF. If you are using VehicleMatrix.inc, it could fix a couple of cases, but it will still be mostly garbage values. A plugin is required to read the matrix from the server's memory.

Why GetVehicleMatrix? Internally, the server stores the vehicle's rotation as a 33 rotation matrix, the same way as the game does. For some reason, this matrix is not exposed to scripts, only the conversion to a quaternion. Unfortunately, unoccupied vehicle updates damage the third row of the matrix and invalidate the values, changing them to near-zeros (mostly denormal floats). I have no idea why that is, I guess some sort of a game/server bug. The internal conversion to the quaternion sadly uses the third row (though it doesn't have to, two rows of the matrix are completely sufficient), returning complete bogus in case it was damaged.

Now that I know the third row is actually redundant and is just a cross product of the other rows, if I can obtain the internal matrix, just fixing the third row via this method is sufficient and produces correct quaternions. Due to the imprecise floating point calculations, the reconstructed row is a bit inaccurate, but still really close to the correct one. It should fix 100 % of wrong quaternions.
__________________
PawnPlus
YSF
Yet Another Lua Plugin
Long Function Names
 i_quat.inc 

kingsofsa.cz:8888 running Cinematic Mode

Last edited by IllidanS4; 06/02/2017 at 06:37 PM.
IllidanS4 is online now   Reply With Quote
Old 01/10/2017, 10:26 PM   #10
IllidanS4
Huge Clucker
 
IllidanS4's Avatar
 
Join Date: Feb 2013
Posts: 286
Reputation: 109
Default Re: i_quat - Quaternions and spatial geometry

New version!

Aside from some useful macros to make working with arrays easier (check the main topic or the source code), I have corrected the core function of this include - GetQuaternionAngles.

Near the poles or singularities of the rotation system, it returned incorrect values, mainly due to float rounding errors. Unfortunately, this requires an arbitrary value (so-called epsilon) to be used to specify the limit where the calculation should be corrected (that's why quaternions are better, you don't have to handle this mess).

I have empirically measured the epsilon to be about 0.000002, but if you want to change it, simply #define QUAT_FLOAT_EPSILON with the new value. It should be positive number not larger than 1, but reasonably small. The smaller the epsilon, the higher chance of returning incorrect results. The larger the epsilon, the more imprecise the results for normal cases.

If you expect "random" quaternions, you don't have to worry about this.
__________________
PawnPlus
YSF
Yet Another Lua Plugin
Long Function Names
 i_quat.inc 

kingsofsa.cz:8888 running Cinematic Mode
IllidanS4 is online now   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Automatic House Replacement (One last issue to resolve - QUATERNIONS!) [HLF]Southclaw Discussion 25 12/08/2012 02:32 PM


All times are GMT. The time now is 02:39 PM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.