Pilot callsigns on the Winners Circle plates

The plates beside each spot came out blank. They ask for textures called
player1..player8, which are not files - the renderer draws each pilot's
callsign into a texture at run time - so the load failed and left them
untextured. Nothing bound the two together.

SortAndReloadNameBitmaps already builds those textures indexed by
finishing place, which is exactly how the plates are numbered, so the
plate beside each spot wants mNameTextures[place]. Binding them is the
whole fix, but it takes two steps rather than one.

The plates have to survive mesh consolidation first. Static geometry is
merged with D3DXConcatenateMeshes and its draw ops deduped by material -
and eight failed texture loads leave eight identical untextured ops, so
all eight plates collapse into one that could only ever carry a single
name. Each plate now gets a distinct 1x1 marker texture as it loads,
which keeps it a subset of its own. The marker is never seen.

Then the binding runs against the consolidated mesh, not the objects the
plates were loaded from - by podium time those have been merged away and
are no longer drawn, which is why re-pointing them changed nothing.

Verified on a race: 8 plates found in the consolidated mesh, 1 bound,
and the winner's plate reads their callsign under their vehicle. One
bound of eight is right for a single-pod race - the rest of the places
have nobody in them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-27 14:40:53 -05:00
co-authored by Claude Fable 5
parent f31c8401c7
commit b97dcce3a2
4 changed files with 157 additions and 9 deletions
+78 -1
View File
@@ -13,6 +13,7 @@ L4TEXOP::WrapType d3d_OBJECT::mLastWrapV = L4TEXOP::WrapType::REPEAT;
bool d3d_OBJECT::mLastTexturingState = true;
long d3d_OBJECT::mNextID = 1;
stdext::hash_map<std::string, L4TEXOP> d3d_OBJECT::mTextureCache;
LPDIRECT3DTEXTURE9 d3d_OBJECT::mNamePlateMarkers[d3d_OBJECT::kNamePlateCount + 1] = { NULL };
void chgext(char *filePath, const char *newExtension)
{
@@ -156,7 +157,36 @@ d3d_OBJECT *d3d_OBJECT::LoadObject(LPDIRECT3DDEVICE9 device, char *fileName)
{
sprintf(textureFilename, "VIDEO\\%s", materials[i].pTextureFilename);
}
object->mDrawOps[i].texture = LoadTexture(device, textureFilename);
//
// playerN is not a file - it is a pilot's callsign, drawn into
// a texture at run time, so loading it always fails. Give the
// plate its marker texture instead: it keeps the eight sign
// faces as eight separate draw ops through consolidation, and
// the Winners Circle swaps the real callsign in later.
//
const char *base = strrchr(textureFilename, '\\');
base = (base != NULL) ? base + 1 : textureFilename;
int plate = 0;
if (_strnicmp(base, "player", 6) == 0 &&
base[6] >= '1' && base[6] <= '8')
{
plate = base[6] - '0';
}
if (plate != 0)
{
memset(&object->mDrawOps[i].texture, 0, sizeof(L4TEXOP));
object->mDrawOps[i].texture.texture =
NamePlateMarker(device, plate);
if (object->mDrawOps[i].texture.texture != NULL)
{
object->mDrawOps[i].texture.texture->AddRef();
}
}
else
{
object->mDrawOps[i].texture = LoadTexture(device, textureFilename);
}
}
if (nextDetailOp < numDetailOps && detailOps[nextDetailOp] == i)
@@ -286,6 +316,53 @@ void d3d_OBJECT::FlushTextureCache()
(*iter).second.texture->Release();
}
mTextureCache.clear();
for (int plate = 0; plate <= kNamePlateCount; ++plate)
{
if (mNamePlateMarkers[plate] != NULL)
{
mNamePlateMarkers[plate]->Release();
mNamePlateMarkers[plate] = NULL;
}
}
}
//
// A distinct 1x1 texture per plate, so the eight sign faces stay eight
// separate draw ops through mesh consolidation instead of merging into one.
// Its content never shows - the callsign replaces it before the podium.
//
LPDIRECT3DTEXTURE9 d3d_OBJECT::NamePlateMarker(LPDIRECT3DDEVICE9 device, int plate)
{
if (plate < 1 || plate > kNamePlateCount || device == NULL)
{
return NULL;
}
if (mNamePlateMarkers[plate] == NULL)
{
device->CreateTexture(1, 1, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED,
&mNamePlateMarkers[plate], NULL);
}
return mNamePlateMarkers[plate];
}
//
// Which plate a texture is the marker for, or 0 if it is not one.
//
int d3d_OBJECT::NamePlateFor(LPDIRECT3DTEXTURE9 texture)
{
if (texture == NULL)
{
return 0;
}
for (int plate = 1; plate <= kNamePlateCount; ++plate)
{
if (mNamePlateMarkers[plate] == texture)
{
return plate;
}
}
return 0;
}
d3d_OBJECT::d3d_OBJECT(LPDIRECT3DDEVICE9 device, int vertCount)