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
+51 -8
View File
@@ -7056,13 +7056,8 @@ void
//
// Give the pilot a vehicle to look at. Their own is built insideEntity - a
// cockpit and no hull - so on the podium they would be the one empty spot.
// Drop those renderables and build the entity again with Disconnected_Eye
// set, which is what makes NotifyOfNewInterestingEntity choose outsideEntity.
//
// Tearing renderables down and rebuilding is the same path the interest
// manager takes every time something drops out of range mid-race, so it is
// well travelled. The eye renderable goes with them, which is fine: the
// presentation camera supplies the view once the podium is up.
// outsideEntity is what builds the exterior, and the renderables for it are
// added to what is already there.
//
void
DPLRenderer::ShowViewpointFromOutside()
@@ -7172,8 +7167,56 @@ void DPLRenderer::SortAndReloadNameBitmaps()
LoadBitSliceTexture(name_bitmap, mNameTextures[index]);
}
}
LoadOrdinalBitmaps();
BindNamePlates();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Put the callsigns on the Winners Circle plates.
//
// mNameTextures is indexed by finishing place (rank + 1), which is how the
// signs are numbered, so the plate beside each spot names whoever is standing
// on it. The plates are static geometry and have already been merged into the
// consolidated mesh by now, so it is the consolidated draw ops that have to be
// re-pointed - the objects they were built from are no longer drawn.
//
void DPLRenderer::BindNamePlates()
{
int name_count = (int) (sizeof(mNameTextures) / sizeof(mNameTextures[0]));
int bound = 0;
int found = 0;
std::list<d3d_OBJECT*>::const_iterator iter;
for (iter = mConsolidatedStaticObjects.begin();
iter != mConsolidatedStaticObjects.end(); ++iter)
{
d3d_OBJECT *object = *iter;
if (object == NULL)
{
continue;
}
for (int op = 0; op < object->GetDrawOpCount(); ++op)
{
L4DRAWOP *draw_op = object->GetDrawOp(op);
int plate = d3d_OBJECT::NamePlateFor(draw_op->texture.texture);
if (plate == 0)
{
continue;
}
++found;
if (plate < name_count && mNameTextures[plate] != NULL)
{
draw_op->texture.texture = mNameTextures[plate];
++bound;
}
}
}
DEBUG_STREAM << "NamePlates: " << bound << " of " << found
<< " plates given a callsign\n" << std::flush;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~