88
99#include " StdInc.h"
1010#include " CGameSA.h"
11+ #include " CRenderWareSA.h"
1112#include " gamesa_renderware.h"
1213
14+ #include < map>
15+ #include < unordered_map>
16+ #include < unordered_set>
17+ #include < utility>
18+
1319extern CGameSA* pGame;
1420
15- //
16- // Info about the current state of a model's txd textures
17- //
18- class CModelTexturesInfo
21+ struct CModelTexturesInfo
1922{
20- public:
21- std::vector<RwTexture*> originalTextures;
23+ ushort usTxdId = 0 ;
24+ RwTexDictionary* pTxd = nullptr ;
25+ std::vector<RwTexture*> originalTextures;
2226 std::vector<SReplacementTextures*> usedByReplacements;
23- ushort usTxdId;
24- RwTexDictionary* pTxd;
2527};
2628
27- std::map<ushort, CModelTexturesInfo> ms_ModelTexturesInfoMap;
29+ static std::map<ushort, CModelTexturesInfo> ms_ModelTexturesInfoMap;
30+
31+ namespace
32+ {
33+ using TextureSwapMap = std::unordered_map<RwTexture*, RwTexture*>;
34+
35+ void ReplaceTextureInGeometry (RpGeometry* pGeometry, const TextureSwapMap& swapMap)
36+ {
37+ if (!pGeometry || swapMap.empty ())
38+ return ;
39+
40+ RpMaterials& materials = pGeometry->materials ;
41+ if (!materials.materials )
42+ return ;
43+
44+ for (int idx = 0 ; idx < materials.entries ; ++idx)
45+ {
46+ RpMaterial* pMaterial = materials.materials [idx];
47+ if (!pMaterial)
48+ continue ;
49+
50+ auto it = swapMap.find (pMaterial->texture );
51+ if (it != swapMap.end ())
52+ RpMaterialSetTexture (pMaterial, it->second );
53+ }
54+ }
55+
56+ bool ReplaceTextureInAtomicCB (RpAtomic* pAtomic, void * userData)
57+ {
58+ if (!pAtomic)
59+ return true ;
60+
61+ auto * swapMap = static_cast <TextureSwapMap*>(userData);
62+ if (!swapMap)
63+ return true ;
64+
65+ ReplaceTextureInGeometry (pAtomic->geometry , *swapMap);
66+ return true ;
67+ }
68+
69+ void ReplaceTextureInModel (CModelInfoSA* pModelInfo, TextureSwapMap& swapMap)
70+ {
71+ if (!pModelInfo || swapMap.empty ())
72+ return ;
73+
74+ RwObject* pRwObject = pModelInfo->GetRwObject ();
75+ if (!pRwObject)
76+ return ;
77+
78+ switch (pModelInfo->GetModelType ())
79+ {
80+ case eModelInfoType::ATOMIC:
81+ case eModelInfoType::TIME:
82+ case eModelInfoType::LOD_ATOMIC:
83+ {
84+ RpAtomic* pAtomic = reinterpret_cast <RpAtomic*>(pRwObject);
85+ if (pAtomic)
86+ ReplaceTextureInGeometry (pAtomic->geometry , swapMap);
87+ break ;
88+ }
89+
90+ case eModelInfoType::WEAPON:
91+ case eModelInfoType::CLUMP:
92+ case eModelInfoType::VEHICLE:
93+ case eModelInfoType::PED:
94+ case eModelInfoType::UNKNOWN:
95+ default :
96+ {
97+ RpClump* pClump = reinterpret_cast <RpClump*>(pRwObject);
98+ if (pClump)
99+ RpClumpForAllAtomics (pClump, ReplaceTextureInAtomicCB, &swapMap);
100+ break ;
101+ }
102+ }
103+ }
104+ }
28105
29106// //////////////////////////////////////////////////////////////
30107//
@@ -192,6 +269,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
192269 RwTexDictionaryRemoveTexture (pInfo->pTxd , pExistingTexture);
193270 }
194271
272+ perTxdInfo.replacedOriginals .push_back (pExistingTexture);
273+
195274 // Add the texture
196275 dassert (!RwTexDictionaryContainsTexture (pInfo->pTxd , pNewTexture));
197276 RwTexDictionaryAddTexture (pInfo->pTxd , pNewTexture);
@@ -226,6 +305,48 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
226305 dassert (MapFind (ms_ModelTexturesInfoMap, usTxdId));
227306 dassert (ListContains (pInfo->usedByReplacements , pReplacementTextures));
228307
308+ TextureSwapMap swapMap;
309+ swapMap.reserve (perTxdInfo.usingTextures .size ());
310+
311+ for (size_t idx = 0 ; idx < perTxdInfo.usingTextures .size (); ++idx)
312+ {
313+ RwTexture* pOldTexture = perTxdInfo.usingTextures [idx];
314+ if (!pOldTexture)
315+ continue ;
316+
317+ RwTexture* pOriginalTexture = (idx < perTxdInfo.replacedOriginals .size ()) ? perTxdInfo.replacedOriginals [idx] : nullptr ;
318+ swapMap[pOldTexture] = pOriginalTexture;
319+
320+ if (pOriginalTexture && !RwTexDictionaryContainsTexture (pInfo->pTxd , pOriginalTexture))
321+ RwTexDictionaryAddTexture (pInfo->pTxd , pOriginalTexture);
322+ }
323+
324+ if (!swapMap.empty ())
325+ {
326+ std::vector<CModelInfoSA*> targetModels;
327+ targetModels.reserve (pReplacementTextures->usedInModelIds .size ());
328+ std::unordered_set<CModelInfoSA*> seenModels;
329+ seenModels.reserve (pReplacementTextures->usedInModelIds .size ());
330+
331+ for (ushort modelId : pReplacementTextures->usedInModelIds )
332+ {
333+ CModelInfoSA* pModelInfo = dynamic_cast <CModelInfoSA*>(pGame->GetModelInfo (modelId));
334+ if (!pModelInfo)
335+ continue ;
336+
337+ if (pModelInfo->GetTextureDictionaryID () != perTxdInfo.usTxdId )
338+ continue ;
339+
340+ if (seenModels.insert (pModelInfo).second )
341+ targetModels.push_back (pModelInfo);
342+ }
343+
344+ for (CModelInfoSA* pModelInfo : targetModels)
345+ {
346+ ReplaceTextureInModel (pModelInfo, swapMap);
347+ }
348+ }
349+
229350 // Remove replacement textures
230351 for (uint i = 0 ; i < perTxdInfo.usingTextures .size (); i++)
231352 {
@@ -235,11 +356,14 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
235356 if (perTxdInfo.bTexturesAreCopies )
236357 {
237358 // Destroy the copy (but not the raster as that was not copied)
238- pOldTexture->raster = NULL ;
359+ std::exchange ( pOldTexture->raster , nullptr ) ;
239360 RwTextureDestroy (pOldTexture);
240361 }
241362 }
242363
364+ perTxdInfo.usingTextures .clear ();
365+ perTxdInfo.replacedOriginals .clear ();
366+
243367 // Ensure there are original named textures in the txd
244368 for (uint i = 0 ; i < pInfo->originalTextures .size (); i++)
245369 {
0 commit comments