diff --git a/Client/game_sa/CModelInfoSA.cpp b/Client/game_sa/CModelInfoSA.cpp index da81fb4f23..b9a72e0caa 100644 --- a/Client/game_sa/CModelInfoSA.cpp +++ b/Client/game_sa/CModelInfoSA.cpp @@ -2100,3 +2100,32 @@ bool CModelInfoSA::ForceUnload() return true; } + +bool CModelInfoSA::Render(CMatrix& matrix) +{ + CBaseModelInfoSAInterface* pModelInfoSAInterface = GetInterface(); + RwObject* pRwObject = pModelInfoSAInterface->pRwObject; + // RwEngineInstance->dOpenDevice.fpRenderStateSet(rwRENDERSTATEALPHATESTFUNCTIONREF, 100u); + RwFrame* pFrame = RpGetFrame(pRwObject); + RwFrameSetIdentity(pFrame); + RwMatrix rwMatrix; + rwMatrix.right = (RwV3d&)matrix.vRight; + rwMatrix.up = (RwV3d&)matrix.vFront; + rwMatrix.at = (RwV3d&)matrix.vUp; + rwMatrix.pos = (RwV3d&)matrix.vPos; + RwFrameTransform(pFrame, &rwMatrix, rwCOMBINEREPLACE); + RwFrameUpdateObjects(pFrame); + //RwFrameRotate(pFrame, &yaxis, rotation.fX, RwOpCombineType::rwCOMBINEREPLACE); + + if (pRwObject->type == RP_TYPE_ATOMIC) + { + RpAtomic* pRpAtomic = reinterpret_cast(pRwObject); + pRpAtomic->renderCallback(pRpAtomic); + } + else + { + RpClump* pRpClump = reinterpret_cast(pRwObject); + RpClumpRender(pRpClump); + } + return true; +} diff --git a/Client/game_sa/CModelInfoSA.h b/Client/game_sa/CModelInfoSA.h index b46818d72b..ed48c7a455 100644 --- a/Client/game_sa/CModelInfoSA.h +++ b/Client/game_sa/CModelInfoSA.h @@ -465,6 +465,9 @@ class CModelInfoSA : public CModelInfo // Vehicle towing functions bool IsTowableBy(CModelInfo* towingModel) override; + // It can crash when called in wrong moment, be careful. Good moment is when all objects are rendered. + bool Render(CMatrix& matrix); + private: void CopyStreamingInfoFromModel(ushort usCopyFromModelID); void RwSetSupportedUpgrades(RwFrame* parent, DWORD dwModel); diff --git a/Client/game_sa/CWorldSA.cpp b/Client/game_sa/CWorldSA.cpp index b946b7e3c1..4d65d9865e 100644 --- a/Client/game_sa/CWorldSA.cpp +++ b/Client/game_sa/CWorldSA.cpp @@ -84,10 +84,23 @@ bool CWorldSA::ResetSurfaceInfo(short sSurfaceID) void HOOK_FallenPeds(); void HOOK_FallenCars(); +DWORD CONTINUE_HookDefaultRender = 0x7491C5; + +void _declspec(naked) HookDefaultRender() +{ + _asm + { + push esi + mov esi, [esp+4+4] + jmp CONTINUE_HookDefaultRender + } +} + void CWorldSA::InstallHooks() { HookInstall(0x565CB0, (DWORD)HOOK_FallenPeds, 5); HookInstall(0x565E80, (DWORD)HOOK_FallenCars, 5); + HookInstall(0x7491C0, (DWORD)HookDefaultRender, 5); } DWORD CONTINUE_CWorld_FallenPeds = 0x00565CBA; diff --git a/Client/game_sa/gamesa_renderware.h b/Client/game_sa/gamesa_renderware.h index e2f2d9a472..594c203b59 100644 --- a/Client/game_sa/gamesa_renderware.h +++ b/Client/game_sa/gamesa_renderware.h @@ -38,6 +38,7 @@ typedef RwFrame*(__cdecl* RwFrameAddChild_t)(RwFrame* parent, RwFrame* child); typedef RwFrame*(__cdecl* RwFrameRemoveChild_t)(RwFrame* child); typedef RwFrame*(__cdecl* RwFrameForAllObjects_t)(RwFrame* frame, void* callback, void* data); typedef RwFrame*(__cdecl* RwFrameTranslate_t)(RwFrame* frame, const RwV3d* v, RwTransformOrder order); +typedef RwFrame*(__cdecl* RwFrameTransform_t)(RwFrame* frame, const RwMatrix* m, RwOpCombineType combine); typedef RwFrame*(__cdecl* RwFrameScale_t)(RwFrame* frame, const RwV3d* v, RwTransformOrder order); typedef RwFrame*(__cdecl* RwFrameUpdateObjects_t)(RwFrame*); typedef RwFrame*(__cdecl* RwFrameCreate_t)(); @@ -69,6 +70,7 @@ typedef RwTexture*(__cdecl* RwTexDictionaryAddTexture_t)(RwTexDictionary* dict, typedef RwTexDictionary*(__cdecl* RwTexDictionaryGetCurrent_t)(); typedef RwTexture*(__cdecl* RwTexDictionaryFindNamedTexture_t)(RwTexDictionary* dict, const char* name); typedef void(__cdecl* RpPrtStdGlobalDataSetStreamEmbedded_t)(void* value); +typedef RpClump*(__cdecl* RpClumpRender_t)(RpClump* clump); typedef RpWorld*(__cdecl* RpWorldAddAtomic_t)(RpWorld* world, RpAtomic* atomic); typedef RpWorld*(__cdecl* RpWorldAddClump_t)(RpWorld* world, RpClump* clump); typedef RpWorld*(__cdecl* RpWorldAddLight_t)(RpWorld* world, RpLight* light); @@ -125,6 +127,7 @@ RWFUNC(RwStreamSkip_t RwStreamSkip, (RwStreamSkip_t)0xDEAD) RWFUNC(RpClumpDestroy_t RpClumpDestroy, (RpClumpDestroy_t)0xDEAD) RWFUNC(RpClumpGetNumAtomics_t RpClumpGetNumAtomics, (RpClumpGetNumAtomics_t)0xDEAD) RWFUNC(RwFrameTranslate_t RwFrameTranslate, (RwFrameTranslate_t)0xDEAD) +RWFUNC(RwFrameTransform_t RwFrameTransform, (RwFrameTransform_t)0xDEAD) RWFUNC(RpClumpForAllAtomics_t RpClumpForAllAtomics, (RpClumpForAllAtomics_t)0xDEAD) RWFUNC(RwFrameAddChild_t RwFrameAddChild, (RwFrameAddChild_t)0xDEAD) RWFUNC(RpClumpAddAtomic_t RpClumpAddAtomic, (RpClumpAddAtomic_t)0xDEAD) @@ -138,6 +141,7 @@ RWFUNC(RwTexDictionaryAddTexture_t RwTexDictionaryAddTexture, (RwTexDictionaryAd RWFUNC(RwTexDictionaryStreamWrite_t RwTexDictionaryStreamWrite, (RwTexDictionaryStreamWrite_t)0xDEAD) RWFUNC(rwD3D9NativeTextureRead_t rwD3D9NativeTextureRead, (rwD3D9NativeTextureRead_t)0xDEAD) RWFUNC(RpPrtStdGlobalDataSetStreamEmbedded_t RpPrtStdGlobalDataSetStreamEmbedded, (RpPrtStdGlobalDataSetStreamEmbedded_t)0xDEAD) +RWFUNC(RpClumpRender_t RpClumpRender, (RpClumpRender_t)0xDEAD) RWFUNC(RpClumpRemoveAtomic_t RpClumpRemoveAtomic, (RpClumpRemoveAtomic_t)0xDEAD) RWFUNC(RpAtomicClone_t RpAtomicClone, (RpAtomicClone_t)0xDEAD) RWFUNC(RwTexDictionaryFindNamedTexture_t RwTexDictionaryFindNamedTexture, (RwTexDictionaryFindNamedTexture_t)0xDEAD) diff --git a/Client/game_sa/gamesa_renderware.hpp b/Client/game_sa/gamesa_renderware.hpp index 2e5386095c..a70ba53766 100644 --- a/Client/game_sa/gamesa_renderware.hpp +++ b/Client/game_sa/gamesa_renderware.hpp @@ -26,6 +26,7 @@ void InitRwFunctions(eGameVersion version) RwStreamSkip = (RwStreamSkip_t)0x007ECD40; // check RpClumpDestroy = (RpClumpDestroy_t)0x0074A360; RpClumpGetNumAtomics = (RpClumpGetNumAtomics_t)0x00749930; + RwFrameTransform = (RwFrameTransform_t)0x007F0F70; RwFrameTranslate = (RwFrameTranslate_t)0x007F0E70; RpClumpForAllAtomics = (RpClumpForAllAtomics_t)0x00749BC0; RwFrameAddChild = (RwFrameAddChild_t)0x007F0B40; @@ -40,6 +41,7 @@ void InitRwFunctions(eGameVersion version) RwTexDictionaryStreamWrite = (RwTexDictionaryStreamWrite_t)0x00804A30; rwD3D9NativeTextureRead = (rwD3D9NativeTextureRead_t)0x004CD820; RpPrtStdGlobalDataSetStreamEmbedded = (RpPrtStdGlobalDataSetStreamEmbedded_t)0x0041B350; + RpClumpRender = (RpClumpRender_t)0x00749B20; RpClumpRemoveAtomic = (RpClumpRemoveAtomic_t)0x0074A510; RpAtomicClone = (RpAtomicClone_t)0x00749EB0; RwTexDictionaryFindNamedTexture = (RwTexDictionaryFindNamedTexture_t)0x007F3A30; @@ -123,6 +125,7 @@ void InitRwFunctions(eGameVersion version) RwStreamSkip = (RwStreamSkip_t)0x007ECD00; RpClumpDestroy = (RpClumpDestroy_t)0x0074A310; RpClumpGetNumAtomics = (RpClumpGetNumAtomics_t)0x007498E0; + RwFrameTransform = (RwFrameTransform_t)0x007F0F70; RwFrameTranslate = (RwFrameTranslate_t)0x007F0E30; RpClumpForAllAtomics = (RpClumpForAllAtomics_t)0x00749B70; RwFrameAddChild = (RwFrameAddChild_t)0x007F0B00; @@ -137,6 +140,7 @@ void InitRwFunctions(eGameVersion version) RwTexDictionaryStreamWrite = (RwTexDictionaryStreamWrite_t)0x008049F0; rwD3D9NativeTextureRead = (rwD3D9NativeTextureRead_t)0x004CD820; RpPrtStdGlobalDataSetStreamEmbedded = (RpPrtStdGlobalDataSetStreamEmbedded_t)0x0041B350; + RpClumpRender = (RpClumpRender_t)0x00749B20; RpClumpRemoveAtomic = (RpClumpRemoveAtomic_t)0x0074A4C0; RpAtomicClone = (RpAtomicClone_t)0x00749E60; RwTexDictionaryFindNamedTexture = (RwTexDictionaryFindNamedTexture_t)0x007F39F0; diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index c94ca60e6d..6aad8d6aff 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -3559,8 +3559,36 @@ void CClientGame::StaticGameRunNamedAnimDestructorHandler(class CTaskSimpleRunNa g_pClientGame->GameRunNamedAnimDestructorHandler(pTask); } +void CClientGame::EnqueueModelToRender(SModelToRender modelToRender) +{ + m_vecModelsToRender.push_back(modelToRender); +} + +void CClientGame::GameEntityRenderHandler(CEntitySAInterface* pGameEntity) +{ + if (m_vecModelsToRender.empty()) + return; + + for (auto& extraEntity : m_vecModelsToRender) + { + auto modelInfo = g_pGame->GetModelInfo(extraEntity.usModel); + if (modelInfo == nullptr) + continue; + if (!modelInfo->IsLoaded()) + modelInfo->Request(EModelRequestType::BLOCKING, "Lua::DxDrawModel3D"); + if (!modelInfo->IsLoaded()) + continue; + + modelInfo->Render(extraEntity.matrix); + } + + m_vecModelsToRender.clear(); +} + void CClientGame::StaticGameEntityRenderHandler(CEntitySAInterface* pGameEntity) { + g_pClientGame->GameEntityRenderHandler(pGameEntity); + if (pGameEntity) { CPools* pPools = g_pGame->GetPools(); @@ -3698,6 +3726,11 @@ void CClientGame::ProjectileInitiateHandler(CClientProjectile* pProjectile) void CClientGame::Render3DStuffHandler() { + //auto modelInfo = g_pGame->GetModelInfo(1632); + //if (!modelInfo->IsLoaded()) + // return; + + //modelInfo->Render(CVector(0,30,6.0f)); } void CClientGame::PreRenderSkyHandler() diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 5f9ed80574..ebb3601a89 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -71,6 +71,12 @@ struct SMiscGameSettings bool bAllowShotgunDamageFix; }; +struct SModelToRender +{ + uint16_t usModel; + CMatrix matrix; +}; + class CClientGame { friend class CPacketHandler; @@ -585,6 +591,7 @@ class CClientGame void TaskSimpleBeHitHandler(CPedSAInterface* pPedAttacker, ePedPieceTypes hitBodyPart, int hitBodySide, int weaponId); AnimationId DrivebyAnimationHandler(AnimationId animGroup, AssocGroupId animId); void AudioZoneRadioSwitchHandler(DWORD dwStationID); + void GameEntityRenderHandler(CEntitySAInterface* pGameEntity); static bool StaticProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); bool ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); @@ -637,6 +644,7 @@ class CClientGame void PedStepHandler(CPedSAInterface* pPed, bool bFoot); void VehicleWeaponHitHandler(SVehicleWeaponHitEvent& event); + void EnqueueModelToRender(SModelToRender modelToRender); private: eStatus m_Status; @@ -812,6 +820,8 @@ class CClientGame bool m_bShowCollision; bool m_bShowSound; + std::vector m_vecModelsToRender; + // Debug class. Empty in release. public: CFoo m_Foo; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp index c4d9f40dfa..8ac3e1b381 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDrawingDefs.cpp @@ -19,6 +19,22 @@ #define MIN_CLIENT_REQ_DXSETRENDERTARGET_CALL_RESTRICTIONS "1.3.0-9.04431" extern bool g_bAllowAspectRatioAdjustment; +bool DxDrawModel3D(uint16_t usModel, CVector position, CVector rotation, std::optional scale) +{ + CModelInfo* pModelInfo = g_pGame->GetModelInfo(usModel); + if (pModelInfo->GetModelType() == eModelInfoType::VEHICLE || pModelInfo->GetModelType() == eModelInfoType::PED) + { + throw std::invalid_argument("Vehicle and Ped models are not allowed"); + } + SModelToRender modelToRender; + modelToRender.usModel = usModel; + ConvertDegreesToRadians(rotation); + CMatrix matrix(position, rotation, scale.value_or(CVector(1.0f, 1.0f, 1.0f))); + modelToRender.matrix = matrix; + g_pClientGame->EnqueueModelToRender(modelToRender); + return true; +} + void CLuaDrawingDefs::LoadFunctions() { constexpr static const std::pair functions[]{ @@ -64,6 +80,7 @@ void CLuaDrawingDefs::LoadFunctions() {"dxSetAspectRatioAdjustmentEnabled", DxSetAspectRatioAdjustmentEnabled}, {"dxIsAspectRatioAdjustmentEnabled", DxIsAspectRatioAdjustmentEnabled}, {"dxSetTextureEdge", DxSetTextureEdge}, + {"dxDrawModel3D", ArgumentParser}, }; // Add functions diff --git a/Client/multiplayer_sa/CMultiplayerSA.cpp b/Client/multiplayer_sa/CMultiplayerSA.cpp index f3f2c63f41..b1ac220ddd 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA.cpp @@ -295,6 +295,7 @@ const DWORD RETURN_Idle_CWorld_ProcessPedsAfterPreRender = 0x53EA08; #define HOOKPOS_CWeapon__TakePhotograph 0x73C26E #define HOOKPOS_CCollision__CheckCameraCollisionObjects 0x41AB8E +#define HOOKPOS_CRenderer__RenderFadingInEntities_MID 0x55320E CPed* pContextSwitchedPed = 0; CVector vecCenterOfWorld; @@ -542,6 +543,7 @@ void HOOK_CAutomobile__dmgDrawCarCollidingParticles(); void HOOK_CWeapon__TakePhotograph(); void HOOK_CCollision__CheckCameraCollisionObjects(); +void HOOK_CRenderer__RenderFadingInEntities_MID(); CMultiplayerSA::CMultiplayerSA() { @@ -749,6 +751,7 @@ void CMultiplayerSA::InitHooks() HookInstall(HOOKPOS_CWeapon__TakePhotograph, (DWORD)HOOK_CWeapon__TakePhotograph, 3 + 2); HookInstall(HOOKPOS_CCollision__CheckCameraCollisionObjects, (DWORD)HOOK_CCollision__CheckCameraCollisionObjects, 6 + 4); + HookInstall(HOOKPOS_CRenderer__RenderFadingInEntities_MID, (DWORD)HOOK_CRenderer__RenderFadingInEntities_MID, 5); // Disable GTA setting g_bGotFocus to false when we minimize MemSet((void*)ADDR_GotFocus, 0x90, 10); @@ -3050,7 +3053,7 @@ void _declspec(naked) HOOK_Render3DStuff() { pushad } - if (m_pRender3DStuffHandler) m_pRender3DStuffHandler(); + //if (m_pRender3DStuffHandler) m_pRender3DStuffHandler(); _asm { @@ -7024,6 +7027,24 @@ bool CanEntityCollideWithCamera(CEntitySAInterface* pEntity) return true; } +constexpr DWORD _Z17SetAmbientColoursv = 0x735D30; + +constexpr DWORD CONTINUE_CRenderer__RenderFadingInEntities_MID = 0x553213; +void _declspec(naked) HOOK_CRenderer__RenderFadingInEntities_MID() +{ + _asm { + call _Z17SetAmbientColoursv + pushad + } + + if (m_pRender3DStuffHandler) m_pRender3DStuffHandler(); + + _asm { + popad + jmp CONTINUE_CRenderer__RenderFadingInEntities_MID + } +} + void _declspec(naked) HOOK_CCollision__CheckCameraCollisionObjects() { _asm diff --git a/Client/sdk/game/CModelInfo.h b/Client/sdk/game/CModelInfo.h index ede6ca59cc..5ce83d9768 100644 --- a/Client/sdk/game/CModelInfo.h +++ b/Client/sdk/game/CModelInfo.h @@ -18,6 +18,7 @@ class CBaseModelInfoSAInterface; class CColModel; class CPedModelInfo; +class CMatrix; struct RpClump; struct RwObject; @@ -246,4 +247,6 @@ class CModelInfo virtual bool IsTowableBy(CModelInfo* towingModel) = 0; virtual unsigned int GetParentID() = 0; + + virtual bool Render(CMatrix& transform) = 0; };