Skip to content

Commit 5330721

Browse files
authored
Fix building crashes related to rotation (PR #4005)
1 parent 06e167b commit 5330721

File tree

12 files changed

+134
-47
lines changed

12 files changed

+134
-47
lines changed

Client/game_sa/CBuildingSA.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
#include "CBuildingSA.h"
1414
#include <game/CWorld.h>
1515
#include "CGameSA.h"
16+
#include "CMatrixLinkSA.h"
17+
#include "CDynamicPool.h"
1618

1719
extern CGameSA* pGame;
1820

21+
static CDynamicPool<CMatrixLinkSAInterface, PoolGrowAddStrategy<0, 500>> g_matrixPool{};
22+
1923
CBuildingSA::CBuildingSA(CBuildingSAInterface* pInterface)
2024
{
2125
SetInterface(pInterface);
@@ -65,3 +69,42 @@ void CBuildingSA::SetLod(CBuilding* pLod)
6569
}
6670
}
6771
}
72+
73+
void CBuildingSA::AllocateMatrix()
74+
{
75+
auto* newMatrix = g_matrixPool.AllocateItem();
76+
std::memset(newMatrix, 0, sizeof(CMatrixLinkSAInterface));
77+
newMatrix->SetTranslateOnly(m_pInterface->Placeable.m_transform.m_translate);
78+
79+
m_pInterface->Placeable.matrix = reinterpret_cast<CMatrix_Padded*>(newMatrix);
80+
}
81+
82+
void CBuildingSA::ReallocateMatrix()
83+
{
84+
if (!m_pInterface->HasMatrix())
85+
return;
86+
87+
auto* newMatrix = g_matrixPool.AllocateItem();
88+
std::memcpy(newMatrix, m_pInterface->Placeable.matrix, sizeof(CMatrixLinkSAInterface));
89+
newMatrix->m_pOwner = nullptr;
90+
newMatrix->m_pPrev = nullptr;
91+
newMatrix->m_pNext = nullptr;
92+
93+
m_pInterface->RemoveMatrix();
94+
m_pInterface->Placeable.matrix = reinterpret_cast<CMatrix_Padded*>(newMatrix);
95+
}
96+
97+
void CBuildingSA::RemoveAllocatedMatrix()
98+
{
99+
if (!m_pInterface->HasMatrix())
100+
return;
101+
102+
CMatrixLinkSAInterface* pMatrix = reinterpret_cast<CMatrixLinkSAInterface*>(m_pInterface->Placeable.matrix);
103+
104+
if (pMatrix->m_pOwner || (pMatrix->m_pNext && pMatrix->m_pPrev))
105+
return;
106+
107+
g_matrixPool.RemoveItem(reinterpret_cast<CMatrixLinkSAInterface*>(m_pInterface->Placeable.matrix));
108+
g_matrixPool.SetCapacity(0);
109+
m_pInterface->Placeable.matrix = nullptr;
110+
}

Client/game_sa/CBuildingSA.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ class CBuildingSA final : public virtual CBuilding, public virtual CEntitySA
2727
CBuildingSAInterface* GetBuildingInterface() { return static_cast<CBuildingSAInterface*>(GetInterface()); };
2828

2929
void SetLod(CBuilding* pLod) override;
30+
31+
void AllocateMatrix();
32+
void ReallocateMatrix();
33+
void RemoveAllocatedMatrix();
3034
};

Client/game_sa/CBuildingsPoolSA.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ CClientEntity* CBuildingsPoolSA::GetClientBuilding(CBuildingSAInterface* pGameIn
5959
return m_buildingPool.entities[poolIndex].pClientEntity;
6060
}
6161

62-
CBuilding* CBuildingsPoolSA::AddBuilding(CClientBuilding* pClientBuilding, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior)
62+
CBuilding* CBuildingsPoolSA::AddBuilding(CClientBuilding* pClientBuilding, uint16_t modelId, CVector* vPos, CVector* vRot, uint8_t interior)
6363
{
6464
if (!HasFreeBuildingSlot())
6565
return nullptr;
@@ -72,15 +72,12 @@ CBuilding* CBuildingsPoolSA::AddBuilding(CClientBuilding* pClientBuilding, uint1
7272
modelInfo->SetObjectPropertiesGroup(MODEL_PROPERTIES_GROUP_STATIC);
7373

7474
// Load building
75-
SFileObjectInstance instance;
75+
SFileObjectInstance instance{};
7676
instance.modelID = modelId;
7777
instance.lod = -1;
7878
instance.interiorID = interior;
7979
instance.position = *vPos;
80-
instance.rotation = *vRot;
81-
82-
// Fix strange SA rotation
83-
instance.rotation.fW = -instance.rotation.fW;
80+
instance.rotation = {};
8481

8582
auto pBuilding = static_cast<CBuildingSAInterface*>(CFileLoaderSA::LoadObjectInstance(&instance));
8683

@@ -98,6 +95,22 @@ CBuilding* CBuildingsPoolSA::AddBuilding(CClientBuilding* pClientBuilding, uint1
9895

9996
// Add building in world
10097
auto pBuildingSA = new CBuildingSA(pBuilding);
98+
99+
if (pBuilding->HasMatrix())
100+
{
101+
// Edge case for the traincross2 (1374) model
102+
// LoadObjectInstance allocates a matrix for the model
103+
// We need allocate our own matrix and put the old matrix in the original pool
104+
pBuildingSA->ReallocateMatrix();
105+
}
106+
else if (vRot->fX != 0 || vRot->fY != 0)
107+
{
108+
// Allocate matrix in our unlimited storage instead of using the shared pool.
109+
pBuildingSA->AllocateMatrix();
110+
}
111+
112+
pBuilding->SetOrientation(vRot->fX, vRot->fY, vRot->fZ);
113+
101114
pGame->GetWorld()->Add(pBuildingSA, CBuildingPool_Constructor);
102115

103116
// Add CBuildingSA object in pool
@@ -116,6 +129,10 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding)
116129
if (dwElementIndexInPool == UINT_MAX)
117130
return;
118131

132+
// Remove references to allocated matrix
133+
auto* pBuildingSA = m_buildingPool.entities[dwElementIndexInPool].pEntity;
134+
pBuildingSA->RemoveAllocatedMatrix();
135+
119136
// Remove building from cover list
120137
pGame->GetCoverManager()->RemoveCover(pInterface);
121138

@@ -128,23 +145,24 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding)
128145
// Remove building from world
129146
pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor);
130147

148+
std::uint16_t modelId = pInterface->m_nModelIndex;
149+
131150
// Call virtual destructor
132151
((void*(__thiscall*)(void*, char))pInterface->vtbl->SCALAR_DELETING_DESTRUCTOR)(pInterface, 0);
133152

134153
// Remove col reference
135-
auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex());
154+
auto modelInfo = pGame->GetModelInfo(modelId);
136155
modelInfo->RemoveColRef();
137156

157+
// Remove building from SA pool
158+
(*m_ppBuildingPoolInterface)->Release(dwElementIndexInPool);
159+
138160
// Remove from BuildingSA pool
139-
auto* pBuildingSA = m_buildingPool.entities[dwElementIndexInPool].pEntity;
140161
m_buildingPool.entities[dwElementIndexInPool] = {nullptr, nullptr};
141162

142163
// Delete it from memory
143164
delete pBuildingSA;
144165

145-
// Remove building from SA pool
146-
(*m_ppBuildingPoolInterface)->Release(dwElementIndexInPool);
147-
148166
// Decrease the count of elements in the pool
149167
--m_buildingPool.count;
150168
}

Client/game_sa/CBuildingsPoolSA.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CBuildingsPoolSA : public CBuildingsPool
2222
CBuildingsPoolSA();
2323
~CBuildingsPoolSA() = default;
2424

25-
CBuilding* AddBuilding(CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior);
25+
CBuilding* AddBuilding(CClientBuilding*, uint16_t modelId, CVector* vPos, CVector* vRot, uint8_t interior);
2626
void RemoveBuilding(CBuilding* pBuilding);
2727
bool HasFreeBuildingSlot();
2828

Client/game_sa/CEntitySA.cpp

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -213,21 +213,11 @@ void CEntitySA::Render()
213213
void CEntitySA::SetOrientation(float fX, float fY, float fZ)
214214
{
215215
pGame->GetWorld()->Remove(this, CEntity_SetOrientation);
216-
DWORD dwThis = (DWORD)m_pInterface;
217-
DWORD dwFunc = FUNC_SetOrientation;
218-
_asm
219-
{
220-
// ChrML: I've switched the X and Z at this level because that's how the real rotation
221-
// is. GTA has kinda swapped them in this function.
222216

223-
push fZ
224-
push fY
225-
push fX
226-
mov ecx, dwThis
227-
call dwFunc
228-
}
217+
m_pInterface->SetOrientation(fX, fY, fZ);
229218

230-
dwFunc = 0x446F90;
219+
DWORD dwThis = (DWORD)m_pInterface;
220+
DWORD dwFunc = 0x446F90;
231221
_asm
232222
{
233223
mov ecx, dwThis

Client/game_sa/CEntitySA.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#define FUNC_GetDistanceFromCentreOfMassToBaseOfModel 0x536BE0
2121

2222
#define FUNC_SetRwObjectAlpha 0x5332C0
23-
#define FUNC_SetOrientation 0x439A80
2423

2524
#define FUNC_CMatrix__ConvertToEulerAngles 0x59A840
2625
#define FUNC_CMatrix__ConvertFromEulerAngles 0x59AA40
@@ -246,6 +245,11 @@ class CEntitySAInterface
246245
((vtbl_DeleteRwObject)this->vtbl->DeleteRwObject)(this);
247246
};
248247

248+
void SetOrientation(float x, float y, float z) {
249+
using CPlacetable_SetOrientation = void(__thiscall*)(CEntitySAInterface * pEntity, float, float, float);
250+
((CPlacetable_SetOrientation)0x439A80)(this, x, y, z);
251+
}
252+
249253
void RemoveRWObjectWithReferencesCleanup() {
250254
DeleteRwObject();
251255
ResolveReferences();

Client/game_sa/CMatrixLinkSA.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CMatrixLinkSA.h
6+
*
7+
* Multi Theft Auto is available from https://www.multitheftauto.com/
8+
*
9+
*****************************************************************************/
10+
11+
#pragma once
12+
13+
#include "CMatrixSA.h"
14+
15+
class CPlaceableSAInterface;
16+
17+
class CMatrixLinkSAInterface : public CMatrixSAInterface
18+
{
19+
public:
20+
CMatrixLinkSAInterface() : CMatrixSAInterface{}, m_pOwner{}, m_pPrev{}, m_pNext{} {}
21+
22+
public:
23+
CPlaceableSAInterface* m_pOwner;
24+
CMatrixLinkSAInterface* m_pPrev;
25+
CMatrixLinkSAInterface* m_pNext;
26+
};

Client/game_sa/CMatrixSA.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CMatrixSA.cpp
6+
*
7+
* Multi Theft Auto is available from https://www.multitheftauto.com/
8+
*
9+
*****************************************************************************/
10+
111
#include "StdInc.h"
212
#include "CMatrixSA.h"
313

Client/game_sa/CMatrixSA.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CMatrixSA.h
6+
*
7+
* Multi Theft Auto is available from https://www.multitheftauto.com/
8+
*
9+
*****************************************************************************/
10+
111
#pragma once
212
#include "CVector.h"
313
#include "CRenderWareSA.h"
@@ -19,6 +29,7 @@ class CMatrixSAInterface
1929
RwMatrix* m_pAttachMatrix = nullptr;
2030
bool m_bOwnsAttachedMatrix = false; // do we need to delete attaching matrix at detaching
2131

32+
CMatrixSAInterface() : m_right{}, flags{}, m_forward{}, pad1{}, m_up{}, pad2{}, m_pos{}, pad3{} {};
2233
CMatrixSAInterface(CMatrixSAInterface const& matrix);
2334
CMatrixSAInterface(RwMatrix* matrix, bool temporary); // like previous + attach
2435
~CMatrixSAInterface(); // destructor detaches matrix if attached

Client/mods/deathmatch/logic/CClientBuilding.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,7 @@ void CClientBuilding::Create()
128128
if (m_bBeingDeleted)
129129
return;
130130

131-
CVector4D vRot4D;
132-
ConvertZXYEulersToQuaternion(m_vRot, vRot4D);
133-
134-
m_pBuilding = g_pGame->GetPools()->GetBuildingsPool().AddBuilding(this, m_usModelId, &m_vPos, &vRot4D, m_interior);
131+
m_pBuilding = g_pGame->GetPools()->GetBuildingsPool().AddBuilding(this, m_usModelId, &m_vPos, &m_vRot, m_interior);
135132

136133
if (!m_pBuilding)
137134
return;

0 commit comments

Comments
 (0)