Поворот персонажа через кватернион

S0Ft1k1337

Новичок
Автор темы
11
3
Всем привет!
Пытался повернуть персонажа на определенные игровые координаты через кватернион, но в результате получается полная вакханалия.

Большое спасибо @SR_team за открытые исходники

Сам кватернион:
struct Quaternion {
    float fW, fX, fY, fZ;
};

void SetQuaternion()( const Quaternion &quater ) {
    float SquarredQuaterW = 0.0f, SquarredQuaterX = 0.0f, SquarredQuaterY = 0.0f, SquarredQuaterZ = 0.0f;

    SquarredQuaterW = quater.fW * quater.fW;
    SquarredQuaterX = quater.fX * quater.fX;
    SquarredQuaterY = quater.fY * quater.fY;
    SquarredQuaterZ = quater.fZ * quater.fZ;
    right.fX        = SquarredQuaterX - SquarredQuaterY - SquarredQuaterZ + SquarredQuaterW;
    up.fY            = SquarredQuaterY - SquarredQuaterX - SquarredQuaterZ + SquarredQuaterW;
    at.fZ            = SquarredQuaterZ - ( SquarredQuaterY + SquarredQuaterX ) + SquarredQuaterW;

    float multXY = quater.fX * quater.fY;
    float multWZ = quater.fW * quater.fZ;
    up.fX         = multWZ + multXY + multWZ + multXY;
    right.fY     = multXY - multWZ + multXY - multWZ;

    float multXZ = quater.fX * quater.fZ;
    float multWY = quater.fW * quater.fY;
    at.fX         = multXZ - multWY + multXZ - multWY;
    right.fZ     = multWY + multXZ + multWY + multXZ;

    float multYZ = quater.fY * quater.fZ;
    float multWX = quater.fW * quater.fX;
    at.fY         = multWX + multYZ + multWX + multYZ;
    up.fZ         = multYZ - multWX + multYZ - multWX;
}

Quaternion GetQuaternion() {
    long double v13; // st7@1
    long double v14; // st7@3
    long double v15; // st5@5
    float        v16; // st6@5
    float        v17; // st7@5
    long double v18; // st6@7
    Quaternion    result;
    float        v23; // [sp+10h] [bp-20h]@7
    float        v24; // [sp+18h] [bp-18h]@9

    v13 = right.fX + up.fY + at.fZ + 1.0f;
    if ( v13 < 0.0f ) v13 = 0.0f;
    result.fW = (float)sqrt( v13 ) * 0.5f;
    v14          = right.fX + 1.0 - up.fY - at.fZ;
    if ( v14 < 0.0f ) v14 = 0.0f;
    v17 = (float)sqrt( v14 ) * 0.5f;
    v16 = 1.0f - right.fX;
    v15 = up.fY + v16 - at.fZ;
    if ( v15 < 0.0f ) v15 = 0.0f;
    v23 = (float)sqrt( v15 ) * 0.5f;
    v18 = v16 - up.fY + at.fZ;
    if ( v18 < 0.0f ) v18 = 0.0f;
    v24 = (float)sqrt( v18 ) * 0.5f;
    if ( result.fW < 0.0f ) result.fW = 0.0f;
    if ( v17 < 0.0f ) v17 = 0.0f;
    if ( v23 < 0.0f ) v23 = 0.0f;
    if ( v24 < 0.0f ) v24 = 0.0f;
    result.fX = (float)copysign( v17, at.fY - up.fZ );
    result.fY = (float)copysign( v23, right.fZ - at.fX );
    result.fZ = (float)copysign( v24, up.fX - right.fY );

    return result;
}

Функция поворота:
float GetAngleBetweenObjectsRad(float& x1, float& y1, float& x2, float& y2)
{
    float kx, ky;
    float t, a;

    kx = x2 - x1;
    ky = y2 - y1;
    if (kx == 0) kx = 0.00001f;
    t = kx / ky;
    if (t < 0) t = -t;

    a = (float)(180 * atan((float)t) / 3.1415);

    if ((kx <= 0) && (ky <= 0)) a = 180 - a;
    else if ((kx >= 0) && (ky >= 0)) a = 359.99999f - a;
    else if ((kx >= 0) && (ky <= 0)) a = -(180 - a);

    a = (a * 3.1415) / 180.0f;

    return a;
}

void RotateTo(CVector& vec)
{
    
    auto me = LOCAL_PLAYER->getPosition();
    float angle = -GetAngleBetweenObjectsRad(me.fX, me.fY, vec.fX, vec.fY);

    auto quat = GetQuaternion();
    quat.fX = cosf(angle / 2);
    quat.fW = sinf(angle / 2);
    SetQuaternion(quat);
}
 
  • Нравится
Реакции: Ya Zaregalsya

Vintik

Через тернии к звёздам
Проверенный
1,556
1,027
C++:
float GetAngleBetweenObjectsRad(float& x1, float& y1, float& x2, float& y2)
{
    float kx, ky;
    float t, a;

    kx = x2 - x1;
    ky = y2 - y1;
    if (kx == 0) kx = 0.00001f;
    t = kx / ky;
    if (t < 0) t = -t;

    a = (float)(180 * atan((float)t) / 3.1415);

    if ((kx <= 0) && (ky <= 0)) a = 180 - a;
    else if ((kx >= 0) && (ky >= 0)) a = 359.99999f - a;
    else if ((kx >= 0) && (ky <= 0)) a = -(180 - a);

    a = (a * 3.1415) / 180.0f;

    return a;
}
Если что, чтобы выглядело более красиво можно юзать atan2.
C++:
#include <cmath>

float GetAngleBetweenObjectsRad(float& x1, float& y1, float& x2, float& y2)
{
    return atan2(x2 - x1, y2 - y1);
}
Не очень понял что значит "через кватернион", можно же просто изменить угол поворота в системе XOY (heading).
 
Последнее редактирование:

S0Ft1k1337

Новичок
Автор темы
11
3
Не очень понял что значит "через кватернион", можно же просто изменить угол поворота в системе XOY (heading).
Можно конечно, но как ты его повернешь на игровые координаты мира
 

Vintik

Через тернии к звёздам
Проверенный
1,556
1,027
Можно конечно, но как ты его повернешь на игровые координаты мира
Очень просто, если я тебя верно понял.
C++:
#include <cmath>

float GetAngleBetweenObjectsRad(float& x1, float& y1, float& x2, float& y2)
{
    return atan2(x2 - x1, y2 - y1);
}

float TurnToCoordinates(DWORD& cped, float& x, float& y)
{
    DWORD mt = *(DWORD*)(cped + 0x14);
    float px = *(float*)(mt + 0x30), py = *(float*)(mt + 0x34);
    float heading = GetAngleBetweenObjectsRad(px, py, x, y);
    *(float*)(cped + 0x55C) = heading; // фактический угол поворота
    *(float*)(cped + 0x558) = heading; // чтобы не было "плавного поворота". если нужно плавно - удалить строку
}

void Main()
{
     // поворачиваем нашего игрока, чтобы он смотрел в сторону координат x = 100, y = 50
    DWORD myPlayer = *(DWORD*)(0xB6F5F0);
    TurnToCoordinates(myPlayer, 100, 50);
}
 
  • Нравится
Реакции: Ya Zaregalsya