Skip to content

Commit be2b2be

Browse files
committed
Improve texture/shader loading system
1 parent 6154954 commit be2b2be

File tree

6 files changed

+681
-109
lines changed

6 files changed

+681
-109
lines changed

Client/game_sa/CModelInfoSA.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,7 +1552,42 @@ bool CModelInfoSA::SetCustomModel(RpClump* pClump)
15521552
break;
15531553
}
15541554

1555-
m_pCustomClump = success ? pClump : nullptr;
1555+
if (success)
1556+
{
1557+
m_pCustomClump = pClump;
1558+
1559+
// Rebind texture pointers in the GAME's clump to current TXD textures.
1560+
// ReplaceModel clones the input clump, so we must rebind the ACTUAL clump the game is using.
1561+
// This is needed because the TXD may contain replacement textures (via engineImportTXD),
1562+
// but the DFF's materials still point to old/original textures from when it was loaded.
1563+
// Without this fix, shader texture replacement fails on custom DFF models.
1564+
eModelInfoType modelType = GetModelType();
1565+
switch (modelType)
1566+
{
1567+
case eModelInfoType::PED:
1568+
case eModelInfoType::WEAPON:
1569+
case eModelInfoType::VEHICLE:
1570+
case eModelInfoType::CLUMP:
1571+
case eModelInfoType::UNKNOWN:
1572+
{
1573+
RpClump* pGameClump = reinterpret_cast<RpClump*>(GetRwObject());
1574+
if (pGameClump && pGame)
1575+
{
1576+
CRenderWare* pRenderWare = pGame->GetRenderWare();
1577+
if (pRenderWare)
1578+
pRenderWare->RebindClumpTexturesToTxd(pGameClump, GetTextureDictionaryID());
1579+
}
1580+
break;
1581+
}
1582+
default:
1583+
break;
1584+
}
1585+
}
1586+
else
1587+
{
1588+
m_pCustomClump = nullptr;
1589+
}
1590+
15561591
return success;
15571592
}
15581593

@@ -1654,7 +1689,23 @@ void CModelInfoSA::MakeCustomModel()
16541689
// We have a custom model?
16551690
if (m_pCustomClump)
16561691
{
1657-
SetCustomModel(m_pCustomClump);
1692+
// Store and clear m_pCustomClump BEFORE calling SetCustomModel to prevent recursive calls.
1693+
// SetCustomModel may trigger LoadAllRequestedModels which can recursively call MakeCustomModel
1694+
// on the same model (via the streaming hook) if the custom DFF lacks embedded collision.
1695+
RpClump* pClumpToSet = m_pCustomClump;
1696+
m_pCustomClump = nullptr;
1697+
1698+
if (!SetCustomModel(pClumpToSet))
1699+
{
1700+
// SetCustomModel failed, restore the custom clump for retry on next stream-in
1701+
m_pCustomClump = pClumpToSet;
1702+
}
1703+
else
1704+
{
1705+
// Preserve the custom clump pointer for restream/retry paths
1706+
m_pCustomClump = pClumpToSet;
1707+
// Note: SetCustomModel now handles RebindClumpTexturesToTxd internally after successful replacement
1708+
}
16581709
}
16591710

16601711
// Custom collision model is not NULL and it's different from the original?

Client/game_sa/CRenderWareSA.ShaderSupport.cpp

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ void CRenderWareSA::PulseWorldTextureWatch()
224224
////////////////////////////////////////////////////////////////
225225
void CRenderWareSA::StreamingAddedTexture(ushort usTxdId, const SString& strTextureName, CD3DDUMMY* pD3DData)
226226
{
227+
// Skip textures without valid D3D data or name - shader matching requires both
228+
if (!pD3DData || strTextureName.empty())
229+
return;
230+
227231
STexInfo* pTexInfo = CreateTexInfo(usTxdId, strTextureName, pD3DData);
228232
OnTextureStreamIn(pTexInfo);
229233
}
@@ -258,6 +262,50 @@ void CRenderWareSA::StreamingRemovedTxd(ushort usTxdId)
258262
TIMING_CHECKPOINT("-StreamingRemovedTxd");
259263
}
260264

265+
////////////////////////////////////////////////////////////////
266+
//
267+
// CRenderWareSA::RemoveStreamingTexture
268+
//
269+
// Remove a single texture that was added via StreamingAddedTexture.
270+
// Finds the texture by matching TXD ID and D3D data pointer.
271+
//
272+
////////////////////////////////////////////////////////////////
273+
void CRenderWareSA::RemoveStreamingTexture(unsigned short usTxdId, CD3DDUMMY* pD3DData)
274+
{
275+
if (!pD3DData)
276+
return;
277+
278+
typedef std::multimap<ushort, STexInfo*>::iterator IterType;
279+
std::pair<IterType, IterType> range = m_TexInfoMap.equal_range(usTxdId);
280+
for (IterType iter = range.first; iter != range.second;)
281+
{
282+
STexInfo* pTexInfo = iter->second;
283+
if (pTexInfo && pTexInfo->pD3DData == pD3DData)
284+
{
285+
OnTextureStreamOut(pTexInfo);
286+
DestroyTexInfo(pTexInfo);
287+
m_TexInfoMap.erase(iter++);
288+
return; // Only one entry per D3D data
289+
}
290+
else
291+
++iter;
292+
}
293+
}
294+
295+
////////////////////////////////////////////////////////////////
296+
//
297+
// CRenderWareSA::IsTexInfoRegistered
298+
//
299+
// Check if a D3D data pointer is already registered in the shader system.
300+
//
301+
////////////////////////////////////////////////////////////////
302+
bool CRenderWareSA::IsTexInfoRegistered(CD3DDUMMY* pD3DData) const
303+
{
304+
if (!pD3DData)
305+
return false;
306+
return MapContains(m_D3DDataTexInfoMap, pD3DData);
307+
}
308+
261309
////////////////////////////////////////////////////////////////
262310
//
263311
// CRenderWareSA::ScriptAddedTxd
@@ -269,13 +317,28 @@ void CRenderWareSA::StreamingRemovedTxd(ushort usTxdId)
269317
void CRenderWareSA::ScriptAddedTxd(RwTexDictionary* pTxd)
270318
{
271319
TIMING_CHECKPOINT("+ScriptAddedTxd");
320+
321+
// Validate TXD pointer before iterating
322+
if (!pTxd || !SharedUtil::IsReadablePointer(pTxd, sizeof(RwTexDictionary)))
323+
{
324+
TIMING_CHECKPOINT("-ScriptAddedTxd");
325+
return;
326+
}
327+
272328
std::vector<RwTexture*> textureList;
273329
GetTxdTextures(textureList, pTxd);
274330
for (std::vector<RwTexture*>::iterator iter = textureList.begin(); iter != textureList.end(); iter++)
275331
{
276332
RwTexture* texture = *iter;
333+
if (!texture || !SharedUtil::IsReadablePointer(texture, sizeof(RwTexture)))
334+
continue;
335+
277336
const char* szTextureName = texture->name;
278-
CD3DDUMMY* pD3DData = texture->raster ? (CD3DDUMMY*)texture->raster->renderResource : NULL;
337+
CD3DDUMMY* pD3DData = (texture->raster && SharedUtil::IsReadablePointer(texture->raster, sizeof(RwRaster)))
338+
? (CD3DDUMMY*)texture->raster->renderResource : NULL;
339+
340+
if (!pD3DData || !szTextureName[0])
341+
continue;
279342

280343
// Added texture
281344
STexInfo* pTexInfo = CreateTexInfo(texture, szTextureName, pD3DData);
@@ -320,12 +383,21 @@ void CRenderWareSA::ScriptRemovedTexture(RwTexture* pTex)
320383
////////////////////////////////////////////////////////////////
321384
void CRenderWareSA::SpecialAddedTexture(RwTexture* texture, const char* szTextureName)
322385
{
386+
if (!texture || !SharedUtil::IsReadablePointer(texture, sizeof(RwTexture)))
387+
return;
388+
323389
if (!szTextureName)
324390
szTextureName = texture->name;
391+
if (!szTextureName || !szTextureName[0])
392+
return;
325393

326-
OutputDebug(SString("Adding special texture %s", szTextureName));
394+
CD3DDUMMY* pD3DData = (texture->raster && SharedUtil::IsReadablePointer(texture->raster, sizeof(RwRaster)))
395+
? (CD3DDUMMY*)texture->raster->renderResource : NULL;
327396

328-
CD3DDUMMY* pD3DData = texture->raster ? (CD3DDUMMY*)texture->raster->renderResource : NULL;
397+
if (!pD3DData)
398+
return;
399+
400+
OutputDebug(SString("Adding special texture %s", szTextureName));
329401

330402
// Added texture
331403
STexInfo* pTexInfo = CreateTexInfo(texture, szTextureName, pD3DData);

0 commit comments

Comments
 (0)