Skip to content

Commit 7b07305

Browse files
committed
Refactor parts of texture system against white textures #2
1 parent 8039836 commit 7b07305

2 files changed

Lines changed: 122 additions & 14 deletions

File tree

Client/game_sa/CRenderWareSA.TextureReplacing.cpp

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -738,17 +738,24 @@ namespace
738738
id = pSlot->usParentIndex;
739739
}
740740

741+
bool bChainComplete = true;
741742
for (std::size_t i = chainLen; i > 0; --i)
742743
{
743744
RwTexDictionary* pChainTxd = CTxdStore_GetTxd(chain[i - 1]);
744745
if (pChainTxd)
745746
MergeCachedTxdTextureMap(chain[i - 1], pChainTxd, targetTxdMap);
747+
else
748+
bChainComplete = false;
746749
}
747750

748751
if (ShouldUseVehicleTxdFallback(usModelId))
749752
AddVehicleTxdFallback(targetTxdMap);
750753

751-
if (!targetTxdMap.empty())
754+
// Only accept a non-empty map when every ancestor in the chain
755+
// was loaded. A partial chain can produce a map that looks valid
756+
// but is missing textures from unloaded ancestors, which leads
757+
// to white surfaces after rebind.
758+
if (!targetTxdMap.empty() && bChainComplete)
752759
return true;
753760

754761
TextureNameSet modelTextureNames;
@@ -3943,11 +3950,14 @@ namespace
39433950
}
39443951

39453952
TxdTextureMap txdTextureMap;
3953+
bool bChainComplete = true;
39463954
for (std::size_t i = chainLen; i > 0; --i)
39473955
{
39483956
RwTexDictionary* pChainTxd = CTxdStore_GetTxd(chain[i - 1]);
39493957
if (pChainTxd)
39503958
MergeCachedTxdTextureMap(chain[i - 1], pChainTxd, txdTextureMap);
3959+
else
3960+
bChainComplete = false;
39513961
}
39523962

39533963
bool bNeedVehicleFallback = (usFirstParentId != static_cast<unsigned short>(-1)) && TxdChainContainsVehicleTxd(usFirstParentId);
@@ -3971,6 +3981,16 @@ namespace
39713981
if (bNeedVehicleFallback)
39723982
AddVehicleTxdFallback(txdTextureMap);
39733983

3984+
// Skip rebind when any ancestor in the chain was not loaded.
3985+
// A partial map would replace some textures but leave others
3986+
// as white until the missing ancestors stream in.
3987+
if (!bChainComplete)
3988+
{
3989+
AddReportLog(9402, SString("RebindLoadedModelToCurrentTxd: incomplete TXD chain for model %u TXD %u (chain length %u)", pModelInfo->GetModel(),
3990+
usTxdId, static_cast<unsigned int>(chainLen)));
3991+
return;
3992+
}
3993+
39743994
if (txdTextureMap.empty())
39753995
{
39763996
AddReportLog(9402, SString("RebindLoadedModelToCurrentTxd: empty texture map for model %u TXD %u (chain length %u)", pModelInfo->GetModel(),
@@ -4233,12 +4253,45 @@ void CRenderWareSA::ProcessPendingIsolatedModels(bool bBlockingParentLoad)
42334253
pModelInfo->SetTextureDictionaryID(childTxdId);
42344254
}
42354255

4236-
RwTexDictionary* pChildTxd = CTxdStore_GetTxd(childTxdId);
4237-
TxdTextureMap txdTextureMap;
4238-
if (pParentTxd)
4239-
MergeCachedTxdTextureMap(parentTxdId, pParentTxd, txdTextureMap);
4240-
if (pChildTxd)
4241-
MergeCachedTxdTextureMap(childTxdId, pChildTxd, txdTextureMap);
4256+
// Walk the full TXD parent chain from childTxdId so all loaded
4257+
// ancestors contribute textures, not just the immediate parent.
4258+
TxdTextureMap txdTextureMap;
4259+
{
4260+
constexpr std::size_t kMaxChainDepth = 16;
4261+
unsigned short chainIds[kMaxChainDepth];
4262+
std::size_t chainLen = 0;
4263+
4264+
for (unsigned short id = childTxdId; chainLen < kMaxChainDepth;)
4265+
{
4266+
bool bCycle = false;
4267+
for (std::size_t j = 0; j < chainLen; ++j)
4268+
{
4269+
if (chainIds[j] == id)
4270+
{
4271+
bCycle = true;
4272+
break;
4273+
}
4274+
}
4275+
if (bCycle)
4276+
break;
4277+
4278+
chainIds[chainLen++] = id;
4279+
4280+
CTextureDictonarySAInterface* pChainSlot = pTxdPoolSA->GetTextureDictonarySlot(id);
4281+
if (!pChainSlot || pChainSlot->usParentIndex == static_cast<unsigned short>(-1))
4282+
break;
4283+
4284+
id = pChainSlot->usParentIndex;
4285+
}
4286+
4287+
// Root-to-child order so child textures override ancestors on name conflict.
4288+
for (std::size_t i = chainLen; i > 0; --i)
4289+
{
4290+
RwTexDictionary* pChainTxd = CTxdStore_GetTxd(chainIds[i - 1]);
4291+
if (pChainTxd)
4292+
MergeCachedTxdTextureMap(chainIds[i - 1], pChainTxd, txdTextureMap);
4293+
}
4294+
}
42424295

42434296
bool bNeedVehicleFallback = TxdChainContainsVehicleTxd(parentTxdId);
42444297
if (!bNeedVehicleFallback && pModelInfo)
@@ -4420,12 +4473,45 @@ void CRenderWareSA::ProcessPendingIsolatedModels(bool bBlockingParentLoad)
44204473
pModelInfo->SetTextureDictionaryID(childTxdId);
44214474
}
44224475

4423-
RwTexDictionary* pChildTxd = CTxdStore_GetTxd(childTxdId);
4424-
TxdTextureMap txdTextureMap;
4425-
if (pParentTxd)
4426-
MergeCachedTxdTextureMap(parentTxdId, pParentTxd, txdTextureMap);
4427-
if (pChildTxd)
4428-
MergeCachedTxdTextureMap(childTxdId, pChildTxd, txdTextureMap);
4476+
// Walk the full TXD parent chain from childTxdId so all loaded
4477+
// ancestors contribute textures, not just the immediate parent.
4478+
TxdTextureMap txdTextureMap;
4479+
{
4480+
constexpr std::size_t kMaxChainDepth = 16;
4481+
unsigned short chainIds[kMaxChainDepth];
4482+
std::size_t chainLen = 0;
4483+
4484+
for (unsigned short id = childTxdId; chainLen < kMaxChainDepth;)
4485+
{
4486+
bool bCycle = false;
4487+
for (std::size_t j = 0; j < chainLen; ++j)
4488+
{
4489+
if (chainIds[j] == id)
4490+
{
4491+
bCycle = true;
4492+
break;
4493+
}
4494+
}
4495+
if (bCycle)
4496+
break;
4497+
4498+
chainIds[chainLen++] = id;
4499+
4500+
CTextureDictonarySAInterface* pChainSlot = pTxdPoolSA->GetTextureDictonarySlot(id);
4501+
if (!pChainSlot || pChainSlot->usParentIndex == static_cast<unsigned short>(-1))
4502+
break;
4503+
4504+
id = pChainSlot->usParentIndex;
4505+
}
4506+
4507+
// Root-to-child order so child textures override ancestors on name conflict.
4508+
for (std::size_t i = chainLen; i > 0; --i)
4509+
{
4510+
RwTexDictionary* pChainTxd = CTxdStore_GetTxd(chainIds[i - 1]);
4511+
if (pChainTxd)
4512+
MergeCachedTxdTextureMap(chainIds[i - 1], pChainTxd, txdTextureMap);
4513+
}
4514+
}
44294515

44304516
bool bNeedVehicleFallback = TxdChainContainsVehicleTxd(parentTxdId);
44314517
if (!bNeedVehicleFallback && pModelInfo)
@@ -6967,10 +7053,22 @@ void CRenderWareSA::ModelInfoTXDRemoveTextures(SReplacementTextures* pReplacemen
69677053
g_GlobalMtaRasterCache.insert(pMaster->raster);
69687054
}
69697055

7056+
// Re-resolve loaded models to their current TXD so geometry materiale
7057+
// no longer reference stale replacement textures from the old session.
7058+
for (const unsigned short usModelId : pReplacementTextures->usedInModelIds)
7059+
{
7060+
auto* pModelInfo = static_cast<CModelInfoSA*>(pGame->GetModelInfo(usModelId));
7061+
if (!pModelInfo)
7062+
continue;
7063+
7064+
const unsigned short usTxdId = pModelInfo->GetTextureDictionaryID();
7065+
RebindLoadedModelToCurrentTxd(pModelInfo, usTxdId);
7066+
}
7067+
69707068
pReplacementTextures->textures.clear();
69717069
pReplacementTextures->perTxdList.clear();
69727070
pReplacementTextures->usedInTxdIds.clear();
6973-
pReplacementTextures->usedInModelIds.clear();
7071+
// Keep usedInModelIds for the caller's restream pass, matching the normal cleanup path.
69747072
pReplacementTextures->bHasRequestedSpace = false;
69757073
return;
69767074
}

Client/game_sa/CRenderWareSA.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,11 +701,15 @@ namespace
701701
}
702702

703703
// Iter from root (outermost parent) to child so child textures win on name conflict.
704+
bool bChainComplete = true;
704705
for (std::size_t i = chainLen; i > 0; --i)
705706
{
706707
RwTexDictionary* pChainTxd = CTxdStore_GetTxd(chain[i - 1]);
707708
if (!pChainTxd)
709+
{
710+
bChainComplete = false;
708711
continue;
712+
}
709713

710714
std::vector<RwTexture*> txdTextures;
711715
CRenderWareSA::GetTxdTextures(txdTextures, pChainTxd);
@@ -721,6 +725,12 @@ namespace
721725
}
722726
}
723727

728+
// A partial chain means some ancestors were not loaded, so the map
729+
// is missing their textures. Return empty to prevent partial rebinds
730+
// that leave unresolved textures white.
731+
if (!bChainComplete)
732+
result.clear();
733+
724734
return result;
725735
}
726736

0 commit comments

Comments
 (0)