diff --git a/MUNGA_L4/L4D3D.cpp b/MUNGA_L4/L4D3D.cpp index 5ca1274..86fde4c 100644 --- a/MUNGA_L4/L4D3D.cpp +++ b/MUNGA_L4/L4D3D.cpp @@ -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 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) diff --git a/MUNGA_L4/L4D3D.h b/MUNGA_L4/L4D3D.h index 30275c7..57b876f 100644 --- a/MUNGA_L4/L4D3D.h +++ b/MUNGA_L4/L4D3D.h @@ -111,6 +111,27 @@ public: // race loop tears the renderer down between missions). static void FlushTextureCache(); + //------------------------------------------------------------------ + // Pilot name plates. + // + // The Winners Circle signs ask for textures called player1..player8, + // which are not files - the renderer draws each pilot's callsign into + // a texture at run time. Loading them fails and the plates come out + // blank, so the draw ops that wanted them are noted here as the + // geometry loads, and bound to the real textures once the finishing + // order is known. + // + // Cleared with the texture cache: the entries point at objects that + // belong to the mission being torn down. + //------------------------------------------------------------------ + // Each plate is given a distinct marker texture as it loads. Without + // one they are eight identical untextured draw ops, and consolidation + // merges every static mesh by material - all eight plates would + // collapse into a single shared op that can only ever show one name. + enum { kNamePlateCount = 8 }; + static LPDIRECT3DTEXTURE9 NamePlateMarker(LPDIRECT3DDEVICE9 device, int plate); + static int NamePlateFor(LPDIRECT3DTEXTURE9 texture); + private: static d3d_OBJECT* LoadSpheres(LPDIRECT3DDEVICE9 device, char *fileName); @@ -150,6 +171,9 @@ private: static long mNextID; static stdext::hash_map< std::string , L4TEXOP > mTextureCache; + + // index 1..8; [0] unused so the index is the plate number + static LPDIRECT3DTEXTURE9 mNamePlateMarkers[kNamePlateCount + 1]; }; extern int gNumBatches; diff --git a/MUNGA_L4/L4VIDEO.cpp b/MUNGA_L4/L4VIDEO.cpp index 2f868d8..4bb91ce 100644 --- a/MUNGA_L4/L4VIDEO.cpp +++ b/MUNGA_L4/L4VIDEO.cpp @@ -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::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; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/MUNGA_L4/L4VIDEO.h b/MUNGA_L4/L4VIDEO.h index 6d0fa8d..c01fba5 100644 --- a/MUNGA_L4/L4VIDEO.h +++ b/MUNGA_L4/L4VIDEO.h @@ -357,6 +357,10 @@ public: void SortAndReloadNameBitmaps(); + // re-point the Winners Circle sign faces at the current callsign + // textures; the plates live in the consolidated static mesh + void BindNamePlates(); + void LoadNameBitmaps(); void LoadOrdinalBitmaps();