Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Client/game_sa/CModelInfoSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,9 @@ void CModelInfoSA::SetIdeFlag(eModelIdeFlag eIdeFlag, bool bState)
case eModelIdeFlag::DISABLE_BACKFACE_CULLING:
m_pInterface->bIsBackfaceCulled = !bState;
break;
case eModelIdeFlag::REALLY_DRAW_LAST:
SetRenderingAfterScene(bState);
break;
default:
break;
}
Expand Down Expand Up @@ -723,6 +726,8 @@ bool CModelInfoSA::GetIdeFlag(eModelIdeFlag eIdeFlag)
return m_pInterface->bDontCollideWithFlyer;
case eModelIdeFlag::DISABLE_BACKFACE_CULLING:
return !m_pInterface->bIsBackfaceCulled;
case eModelIdeFlag::REALLY_DRAW_LAST:
return ShouldRenderAfterScene();
default:
return false;
}
Expand Down
5 changes: 5 additions & 0 deletions Client/game_sa/CModelInfoSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,8 @@ class CModelInfoSA : public CModelInfo
static std::map<unsigned short, int> ms_DefaultTxdIDMap;
SVehicleSupportedUpgrades m_ModelSupportedUpgrades;

bool m_renderAfterScene{false};

public:
CModelInfoSA();

Expand Down Expand Up @@ -495,6 +497,9 @@ class CModelInfoSA : public CModelInfo
bool IsDynamic() { return m_pInterface ? m_pInterface->usDynamicIndex != MODEL_PROPERTIES_GROUP_STATIC : false; };
bool IsDamageableAtomic() override;

bool ShouldRenderAfterScene() const noexcept override { return m_renderAfterScene; }
void SetRenderingAfterScene(bool shouldRenderAfterScene) noexcept override { m_renderAfterScene = shouldRenderAfterScene; }

static bool IsVehicleModel(std::uint32_t model) noexcept;

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ ADD_ENUM(eModelIdeFlag::IS_TAG, "is_tag")
ADD_ENUM(eModelIdeFlag::DISABLE_BACKFACE_CULLING, "disable_backface_culling")
ADD_ENUM(eModelIdeFlag::IS_BREAKABLE_STATUE, "is_breakable_statue")
ADD_ENUM(eModelIdeFlag::IS_CRANE, "is_crane")
ADD_ENUM(eModelIdeFlag::REALLY_DRAW_LAST, "draw_after_scene")
IMPLEMENT_ENUM_CLASS_END("model-ide-flag")

// https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dformat
Expand Down
88 changes: 88 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA_Rendering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,84 @@ static void __declspec(naked) HOOK_CRenderer_EverythingBarRoads()
// clang-format on
}

struct AlphaObjectInfo
{
void* atomic;
void* callback;
float distance;
};

static bool InsertEntityIntoSortedList(CEntitySAInterface* entity, float distance)
{
if (auto* modelInfo = pGameInterface->GetModelInfo(entity->m_nModelIndex); modelInfo && modelInfo->ShouldRenderAfterScene())
{
CBaseModelInfoSAInterface* baseModelInfo = modelInfo->GetInterface();
CColModelSAInterface* col = baseModelInfo ? baseModelInfo->pColModel : nullptr;

if (col)
{
auto boundBox = col->m_bounds;
const CVector& camPos = *(CVector*)0xB76870;
CVector closestPoint;

closestPoint.fX = std::max(boundBox.m_vecMin.fX, std::min(camPos.fX, boundBox.m_vecMax.fX));
closestPoint.fY = std::max(boundBox.m_vecMin.fY, std::min(camPos.fY, boundBox.m_vecMax.fY));
closestPoint.fZ = std::max(boundBox.m_vecMin.fZ, std::min(camPos.fZ, boundBox.m_vecMax.fZ));

distance = (camPos - closestPoint).Length();
}

// CVisibilityPlugins::RenderEntity as callback
AlphaObjectInfo inf = {entity, (void*)0x732B40, distance};

// Call CLinkList::InsertSorted for m_alphaReallyDrawLastList
return ((bool(__thiscall*)(void*, AlphaObjectInfo*))0x733910)((void*)0xC881D0, &inf);
}

// Call CVisibilityPlugins::InsertEntityIntoSortedList
// Insert into m_alphaList
return ((bool(__cdecl*)(CEntitySAInterface*, float))0x00734570)(entity, distance);
}

static void RenderEntityAfterScene(CEntitySAInterface* entity, CBaseModelInfoSAInterface* modelInfoInterface)
{
if (!modelInfoInterface) [[unlikely]]
return;

// RwEngineInstance->dOpenDevice.fpRenderStateGet/fpRenderStateSet
DWORD engine = *(DWORD*)0xC97B24;
auto fpSet = reinterpret_cast<BOOL(__cdecl*)(DWORD, void*)>(*(DWORD*)(engine + 0x20));
Comment on lines +975 to +976

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is temporary. It should be cleaned up in another PR, ideally by finishing the RwGlobals definitions.


entity->bImBeingRendered = true;

if (!entity->bBackfaceCulled)
fpSet(0x14 /*rwRENDERSTATECULLMODE*/, (void*)1 /*rwCULLMODECULLNONE*/);

bool resetColours = entity->SetupLighting();
entity->Render();
entity->RemoveLighting(resetColours);

if (!entity->bBackfaceCulled)
fpSet(0x14 /*rwRENDERSTATECULLMODE*/, (void*)2 /*rwCULLMODECULLBACK*/);

entity->bImBeingRendered = false;
}

static void TryRenderOneNonRoad(CEntitySAInterface* entity)
{
if (entity && (entity->nType == ENTITY_TYPE_OBJECT || entity->nType == ENTITY_TYPE_BUILDING) && entity->m_pRwObject)
{
if (auto* modelInfo = pGameInterface->GetModelInfo(entity->m_nModelIndex); modelInfo && modelInfo->ShouldRenderAfterScene())
{
RenderEntityAfterScene(entity, modelInfo->GetInterface());
return;
}
}

// Call CRenderer::RenderOneNonRoad
((void(__cdecl*)(CEntitySAInterface*))0x553260)(entity);
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// CMultiplayerSA::InitHooks_Rendering
Expand All @@ -945,6 +1023,16 @@ void CMultiplayerSA::InitHooks_Rendering()
MemPut<BYTE>(0x5D9A91, VEHICLE_SPECULAR_LIGHT_SLOT); // RwD3D9EnableLight(TRUE)
MemPut<BYTE>(0x5D9F1F, VEHICLE_SPECULAR_LIGHT_SLOT); // RwD3D9EnableLight(FALSE)

HookInstallCall(0x5534D1, (DWORD)InsertEntityIntoSortedList); // CRenderer::AddEntityToRenderList
HookInstallCall(0x55350C, (DWORD)InsertEntityIntoSortedList); // CRenderer::AddEntityToRenderList
HookInstallCall(0x553C45, (DWORD)InsertEntityIntoSortedList); // CRenderer::RenderEverythingBarRoads
HookInstallCall(0x55455A, (DWORD)InsertEntityIntoSortedList); // CRenderer::SetupEntityVisibility
HookInstallCall(0x732C48, (DWORD)TryRenderOneNonRoad); // CVisibilityPlugins::RenderEntity

// TODO fix entity brightness flickering when entering fade out phase
// HookInstallCall(0x732BD7, (DWORD)RenderFadingAtomic); // CVisibilityPlugins::RenderEntity
// HookInstallCall(0x732BDE, (DWORD)RenderFadingClump); // CVisibilityPlugins::RenderEntity
Comment on lines +1032 to +1034

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be fixed in a separate PR, as it requires overloading GetModelInfo to return CModelInfoSA based on the interface. I don't want to introduce too much into this PR and would rather keep the focus mainly on the bugfix.


EZHookInstall(CallIdle);
EZHookInstall(CEntity_Render);
EZHookInstall(CEntity_RenderOneNonRoad);
Expand Down
1 change: 1 addition & 0 deletions Client/multiplayer_sa/StdInc.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
#include "..\game_sa\CPedSA.h"
#include "..\game_sa\CProjectileSA.h"
#include "..\game_sa\TaskAttackSA.h"
#include "../game_sa/CColModelSA.h"

extern CMultiplayerSA* pMultiplayer;
6 changes: 6 additions & 0 deletions Client/sdk/game/CModelInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ enum class eModelIdeFlag
DISABLE_BACKFACE_CULLING,
IS_BREAKABLE_STATUE,
IS_CRANE,

// Our custom flag
REALLY_DRAW_LAST,
};

enum class eModelInfoType : unsigned char
Expand Down Expand Up @@ -256,4 +259,7 @@ class CModelInfo
virtual unsigned int GetParentID() = 0;
virtual bool IsDynamic() = 0;
virtual bool IsDamageableAtomic() = 0;

virtual bool ShouldRenderAfterScene() const noexcept = 0;
virtual void SetRenderingAfterScene(bool shouldRenderAfterScene) noexcept = 0;
};
Loading