Статья [Warface] Project To Screen Internal Rebuild (External)

  • 681
  • 315
Поскольку я начал писать свой экстернал под эту увлекательную игру, мне потребовался так называемый "World To Screen", который оказалось не так просто сделать в этой уникальной игре. Было решено ребилднуть W2S функу игры.
Собственно почему бы не поделиться с другими, поскольку ничего особенного в этой базовой функции нет.

Ребилд произведен с помощью так называемой "IDA Pro 8.3" и имеющимися данными в виде дампа ласт версии и PDB 2019.

Непосредственно ProjectToScreenInternal был ребилднут с ласт дампа, mathVec3Project же я украл с PDB 2019, поскольку ребилд с ласт версии не захотел работать (ну и ладно).

Ко всем функциям и оффсетам прилагается сигнатура по которой будет очень просто обновиться в будущем.

Потребуется класс CRenderer, а точнее некоторые переменные из него - матрица, и вьюпорт.
C++:
struct viewport_t {
  int x, y, z, w;
};

class c_renderer {
public:
  OFFSET(viewport_t, m_pViewPort(), 0x14B718); // BB 18 B7 14 00

public:
  /*
   * this->m_RP.m_TI[m_nCurThreadProcess].m_matView/m_matProj
   * @note: m_nCurThreadProcess is always 0, looks like anti-paste idk
   * 48 69 C8 28 03 00 00 0F 29 4C 24 70
   */
  cryengine_matrix4x4_t m_matViewTop() {
    auto m_matView =
        core->proc_read<std::uintptr_t>(core->proc_read<std::uintptr_t>(
            reinterpret_cast<std::uintptr_t>(this) + 0x328 + 0xF38));
    return core->proc_read<cryengine_matrix4x4_t>(m_matView); // m_matView->m_pTop
  }

  cryengine_matrix4x4_t m_matProjTop() {
    auto m_matProj =
        core->proc_read<std::uintptr_t>(core->proc_read<std::uintptr_t>(
            reinterpret_cast<std::uintptr_t>(this) + 0x328 + 0xF40));
    return core->proc_read<cryengine_matrix4x4_t>(m_matProj); // m_matProj->m_pTop
  }
};
Также структура матрицы и PTS.
C++:
struct cryengine_matrix4x4_t {
  float m00, m01, m02, m03;
  float m10, m11, m12, m13;
  float m20, m21, m22, m23;
  float m30, m31, m32, m33;

  cryengine_matrix4x4_t() = default;

  void set_identity() {
    m00 = 1;
    m01 = 0;
    m02 = 0;
    m03 = 0;
    m10 = 0;
    m11 = 1;
    m12 = 0;
    m13 = 0;
    m20 = 0;
    m21 = 0;
    m22 = 1;
    m23 = 0;
    m30 = 0;
    m31 = 0;
    m32 = 0;
    m33 = 1;
  }

  void sanitaze() {
    if (!std::isfinite(m00))
      m00 = 0.f;
    if (!std::isfinite(m01))
      m01 = 0.f;
    if (!std::isfinite(m02))
      m02 = 0.f;
    if (!std::isfinite(m03))
      m03 = 0.f;
    if (!std::isfinite(m10))
      m10 = 0.f;
    if (!std::isfinite(m11))
      m11 = 0.f;
    if (!std::isfinite(m12))
      m12 = 0.f;
    if (!std::isfinite(m13))
      m13 = 0.f;
    if (!std::isfinite(m20))
      m20 = 0.f;
    if (!std::isfinite(m21))
      m21 = 0.f;
    if (!std::isfinite(m22))
      m22 = 0.f;
    if (!std::isfinite(m23))
      m23 = 0.f;
    if (!std::isfinite(m30))
      m30 = 0.f;
    if (!std::isfinite(m31))
      m31 = 0.f;
    if (!std::isfinite(m32))
      m32 = 0.f;
    if (!std::isfinite(m33))
      m33 = 0.f;
  }
};

struct project_to_screen_t {
  float ptx, pty, ptz; // project_to_N
  float sx, sy, sz; // screen_N
};

Псевдо-коды функций.
C++:
char __fastcall ProjectToScreenInternal(__int64 a1, __int64 a2)
{
  _DWORD *v2; // rbx
  int v5; // ebp
  __int64 v6; // rbx
  DWORD CurrentThreadId; // eax
  bool v8; // al
  int v9; // xmm1_4
  int v10; // xmm0_4
  __int64 v11; // rbx
  int *v12; // rbx
  int v13; // r14d
  float *v14; // rax
  int v16[4]; // [rsp+30h] [rbp-78h] BYREF
  int v17[4]; // [rsp+40h] [rbp-68h] BYREF
  int v18[4]; // [rsp+50h] [rbp-58h] BYREF
  __int128 v19[4]; // [rsp+60h] [rbp-48h] BYREF

  v2 = *(_DWORD **)(a1 + 0x14B048);
  if ( GetCurrentThreadId() == v2[0x36] )
    v5 = v2[0xC];
  else
    v5 = v2[0xD];
  v6 = *(_QWORD *)(a1 + 0x14B048);
  CurrentThreadId = GetCurrentThreadId();
  v8 = CurrentThreadId == *(_DWORD *)(v6 + 0xDC) || CurrentThreadId == *(_DWORD *)(v6 + 0xD8);
  v9 = *(_DWORD *)(a2 + 4);
  v16[0] = *(_DWORD *)a2;
  v10 = *(_DWORD *)(a2 + 8);
  v11 = 0x14B718i64;
  if ( !v8 )
    v11 = 0x14B6D0i64;
  v16[1] = v9;
  v12 = (int *)(a1 + v11);
  v16[2] = v10;
  v19[0] = xmmword_141E74080;
  v13 = v12[2];
  v18[0] = *v12;
  v18[1] = v12[1];
  v18[3] = v12[3];
  v19[1] = xmmword_141E7EA80;
  v19[2] = xmmword_141E7EA90;
  v19[3] = xmmword_141E74100;
  v18[2] = v13;
  v14 = mathVec3Project(
          (float *)v17,
          (float *)v16,
          v18,
          **(float ***)(0x328i64 * v5 + a1 + 0xF40),
          **(float ***)(0x328i64 * v5 + a1 + 0xF38),
          (float *)v19);
  if ( v14 )
  {
    **(float **)(a2 + 0x10) = (float)(*(float *)v17 * *(float *)&dword_141E7404C) / (float)v13;
    **(float **)(a2 + 0x18) = (float)(*(float *)&v17[1] * *(float *)&dword_141E7404C) / (float)v12[3];
    **(_DWORD **)(a2 + 0x20) = v17[2];
    LOBYTE(v14) = 1;
  }
  return (char)v14;
}
C++:
Vec3_tpl<float> *__fastcall mathVec3Project(
        Vec3_tpl<float> *pvWin,
        const Vec3_tpl<float> *pvObj,
        const int *pViewport,
        const Matrix44_tpl<float,XMVec4A> *pProjection,
        const Matrix44_tpl<float,XMVec4A> *pView,
        const Matrix44_tpl<float,XMVec4A> *pWorld)
{
  float v6; // xmm7_4
  float v7; // xmm8_4
  float v8; // xmm3_4
  float v9; // xmm2_4
  float v10; // xmm7_4
  float v11; // xmm4_4
  float v12; // xmm5_4
  float v13; // xmm6_4
  float v14; // xmm8_4
  Vec3_tpl<float> *result; // rax
  float v16; // xmm0_4
  float v17; // xmm2_4

  v6 = (float)((float)((float)(pvObj->y * pWorld->m11) + (float)(pvObj->x * pWorld->m01))
             + (float)(pvObj->z * pWorld->m21))
     + pWorld->m31;
  v7 = (float)((float)((float)(v6 * pView->m10)
                     + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
                                             + (float)(pvObj->z * pWorld->m20))
                                     + pWorld->m30)
                             * pView->m00))
             + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
                                     + (float)(pvObj->z * pWorld->m22))
                             + pWorld->m32)
                     * pView->m20))
     + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
                             + (float)(pvObj->z * pWorld->m23))
                     + pWorld->m33)
             * pView->m30);
  v8 = (float)((float)((float)(v6 * pView->m11)
                     + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
                                             + (float)(pvObj->z * pWorld->m20))
                                     + pWorld->m30)
                             * pView->m01))
             + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
                                     + (float)(pvObj->z * pWorld->m22))
                             + pWorld->m32)
                     * pView->m21))
     + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
                             + (float)(pvObj->z * pWorld->m23))
                     + pWorld->m33)
             * pView->m31);
  v9 = (float)((float)((float)(v6 * pView->m12)
                     + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
                                             + (float)(pvObj->z * pWorld->m20))
                                     + pWorld->m30)
                             * pView->m02))
             + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
                                     + (float)(pvObj->z * pWorld->m22))
                             + pWorld->m32)
                     * pView->m22))
     + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
                             + (float)(pvObj->z * pWorld->m23))
                     + pWorld->m33)
             * pView->m32);
  v10 = (float)((float)((float)(v6 * pView->m13)
                      + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
                                              + (float)(pvObj->z * pWorld->m20))
                                      + pWorld->m30)
                              * pView->m03))
              + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
                                      + (float)(pvObj->z * pWorld->m22))
                              + pWorld->m32)
                      * pView->m23))
      + (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
                              + (float)(pvObj->z * pWorld->m23))
                      + pWorld->m33)
              * pView->m33);
  v11 = (float)((float)((float)(v8 * pProjection->m10) + (float)(v7 * pProjection->m00)) + (float)(v9 * pProjection->m20))
      + (float)(v10 * pProjection->m30);
  v12 = (float)((float)((float)(v8 * pProjection->m11) + (float)(v7 * pProjection->m01)) + (float)(v9 * pProjection->m21))
      + (float)(v10 * pProjection->m31);
  v13 = (float)((float)((float)(v7 * pProjection->m02) + (float)(v8 * pProjection->m12)) + (float)(v9 * pProjection->m22))
      + (float)(v10 * pProjection->m32);
  v14 = (float)((float)((float)(v7 * pProjection->m03) + (float)(v8 * pProjection->m13)) + (float)(v9 * pProjection->m23))
      + (float)(v10 * pProjection->m33);
  if ( v14 == 0.0 )
    return 0i64;
  result = pvWin;
  pvWin->x = (float)((float)((float)((float)(v11 / v14) + 1.0) * (float)pViewport[2]) * 0.5) + (float)*pViewport;
  v16 = (float)pViewport[1];
  v17 = (float)((float)(1.0 - (float)(v12 / v14)) * (float)pViewport[3]) * 0.5;
  pvWin->z = v13 / v14;
  pvWin->y = v17 + v16;
  return result;
}

Ребилд функций.
C++:
bool ProjectToScreenInternal_Rebuild(i_renderer *_this,
                                     project_to_screen_t &proj) {
  // @xref: 48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 56 48
  // 81 EC A0 00 00 00 48 8B 99

  int pViewport[4];             // [rsp+50h] [rbp-58h] BYREF

  fw::vec3_t pvWin{};
  fw::vec3_t pvObj = {proj.ptx, proj.pty, proj.ptz};

  auto m_pViewPort = _this->m_pViewPort();
  pViewport[0] = m_pViewPort.x;
  pViewport[1] = m_pViewPort.y;
  pViewport[2] = m_pViewPort.z;
  pViewport[3] = m_pViewPort.w;

  auto m_matView = _this->m_matViewTop();
  auto m_matProj = _this->m_matProjTop();
  cryengine_matrix4x4_t pWorld{};

  m_matView.sanitaze();
  m_matProj.sanitaze();
  pWorld.set_identity();

  fw::vec3_t result = mathVec3Project_Rebuild(pvWin, pvObj, pViewport, m_matProj,
                                   m_matView, pWorld);

  if (!result.empty()) {
    proj.sx = pvWin.x * 100.f / pViewport[2];
    proj.sy = pvWin.y * 100.f / pViewport[3];
    proj.sz = pvWin.z;
  }

  return !result.empty();
}
C++:
fw::vec3_t mathVec3Project_Rebuild(fw::vec3_t &pvWin, const fw::vec3_t &pvObj,
                                   const int pViewport[4],
                                   const cryengine_matrix4x4_t &pProjection,
                                   const cryengine_matrix4x4_t &pView,
                                   const cryengine_matrix4x4_t &pWorld) {
  // @xref: 48 83 EC 38 48 8B 44 24 68

  float v6;  // xmm7_4
  float v7;  // xmm8_4
  float v8;  // xmm3_4
  float v9;  // xmm2_4
  float v10; // xmm7_4
  float v11; // xmm4_4
  float v12; // xmm5_4
  float v13; // xmm6_4
  float v14; // xmm8_4

  /*
   * matrix calculation
   * i don't understand how to normal reverse this shit, so i just pasted this
   * magic pseudocode :/
   */
  {
    v6 = pvObj.y * pWorld.m11 + pvObj.x * pWorld.m01 + pvObj.z * pWorld.m21 +
         pWorld.m31;

    v7 = v6 * pView.m10 +
         (pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
          pWorld.m30) *
             pView.m00 +
         (pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
          pWorld.m32) *
             pView.m20 +
         (pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
          pWorld.m33) *
             pView.m30;
    v8 = v6 * pView.m11 +
         (pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
          pWorld.m30) *
             pView.m01 +
         (pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
          pWorld.m32) *
             pView.m21 +
         (pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
          pWorld.m33) *
             pView.m31;
    v9 = v6 * pView.m12 +
         (pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
          pWorld.m30) *
             pView.m02 +
         (pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
          pWorld.m32) *
             pView.m22 +
         (pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
          pWorld.m33) *
             pView.m32;
    v10 = v6 * pView.m13 +
          (pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
           pWorld.m30) *
              pView.m03 +
          (pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
           pWorld.m32) *
              pView.m23 +
          (pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
           pWorld.m33) *
              pView.m33;

    v11 = v8 * pProjection.m10 + v7 * pProjection.m00 + v9 * pProjection.m20 +
          v10 * pProjection.m30;
    v12 = v8 * pProjection.m11 + v7 * pProjection.m01 + v9 * pProjection.m21 +
          v10 * pProjection.m31;
    v13 = v7 * pProjection.m02 + v8 * pProjection.m12 + v9 * pProjection.m22 +
          v10 * pProjection.m32;
    v14 = v7 * pProjection.m03 + v8 * pProjection.m13 + v9 * pProjection.m23 +
          v10 * pProjection.m33;
  }

  if (v14 == 0.f)
    return {};

  pvWin.x = ((v11 / v14) + 1.f) * (static_cast<float>(pViewport[2]) * .5f) +
            static_cast<float>(pViewport[0]);
  pvWin.y = ((1.f - (v12 / v14)) * static_cast<float>(pViewport[3]) * .5f) +
            static_cast<float>(pViewport[1]);
  pvWin.z = v13 / v14;

  return pvWin;
}

Ну и сам World To Screen.
C++:
bool world_to_screen(const fw::vec3_t &in, fw::vec3_t &out) {
  /*
   * here you need to get the address of SSystemGlobalEnvironment structure and screen_size
   */
  {
    /* removed */
  }

  project_to_screen_t pts{in.x, in.y, in.z, out.x, out.y, out.z};
  if (!ProjectToScreenInternal_Rebuild(sge->pRenderer(), pts))
    return false;

  if (pts.sz > 1.f) // behind camera
    return false;

  out.x = pts.sx * (screen_size.x / 100.f);
  out.y = pts.sy * (screen_size.y / 100.f);

  return true;
}

Отмечу, что это просто ребилд, и код можно(и я бы сказал нужно) привести в более презентабельный вид.
Этот ребилд также можно использовать для интернал софтов, если есть такая необходимость, но я делал под свой экстернал.
 
Последнее редактирование:
  • 681
  • 315
Дополнение к теме.
Юзер с ЮЦ чутка дополнил мою тему там дав нормальный mathVec3Project с оригинального движка, и привел ProjectToScreenInternal_Rebuild в более лучший вид.
Можете брать, кому надо.
C++:
struct viewport_t {
  int x, y, w, h;
};

inline void mathVec4Transform(float out[4], const float m[16],
                              const float in[4]) {
#define M(row, col) m[col * 4 + row]
  out[0] =
      M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
  out[1] =
      M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
  out[2] =
      M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
  out[3] =
      M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}

inline float mathVec3Project(fw::vec3_t &pvWin, const fw::vec3_t &pvObj,
                             const viewport_t pViewport,
                             const cryengine_matrix4x4_t *pProjection,
                             const cryengine_matrix4x4_t *pView,
                             const cryengine_matrix4x4_t *pWorld) {
  // @xref
  // https://github.com/MergHQ/CRYENGINE/blob/8b63f61c6bb186fbee254b793775856468df47c5/Code/CryEngine/CryCommon/CryMath/Cry_XOptimise.h#L310

  fw::vec4_t in{}, out{};

  in.x = pvObj.x;
  in.y = pvObj.y;
  in.z = pvObj.z;
  in.w = 1.0f;

  mathVec4Transform(
      reinterpret_cast<float *>(&out),
      const_cast<float *>(reinterpret_cast<const float *>(pWorld)),
      reinterpret_cast<float *>(&in));
  mathVec4Transform(reinterpret_cast<float *>(&in),
                    const_cast<float *>(reinterpret_cast<const float *>(pView)),
                    reinterpret_cast<float *>(&out));
  mathVec4Transform(
      reinterpret_cast<float *>(&out),
      const_cast<float *>(reinterpret_cast<const float *>(pProjection)),
      reinterpret_cast<float *>(&in));

  if (out.w == 0.f)
    return 0.f;

  out.x /= out.w;
  out.y /= out.w;
  out.z /= out.w;

  pvWin.x = pViewport.x + (1.f + out.x) * pViewport.w / 2.f;
  pvWin.y = pViewport.y + (1.f - out.y) * pViewport.h / 2.f;
  pvWin.z = out.z;

  return out.w;
}
C++:
bool ProjectToScreenInternal_Rebuild(i_renderer *_this, float ptx, float pty,
                                     float ptz, float &sx, float &sy,
                                     float &sz) {
  // @xref: 48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 56 48
  // 81 EC A0 00 00 00 48 8B 99

  fw::vec3_t out{};
  fw::vec3_t in = {ptx, pty, ptz};

  auto viewport = _this->m_pViewPort();
  auto matrix_view = _this->m_matViewTop();
  auto matrix_projection = _this->m_matProjTop();
  cryengine_matrix4x4_t matrix_world{};

  matrix_view.sanitaze();
  matrix_projection.sanitaze();
  matrix_world.set_identity();

  if (mathVec3Project(out, in, viewport, &matrix_projection, &matrix_view,
                      &matrix_world) == 0.f)
    return false;

  sx = out.x * 100.f / viewport.w;
  sy = out.y * 100.f / viewport.h;
  sz = out.z;

  return true;
}
 
Сверху Снизу