Files
RP412/MUNGA_L4/L4D3D.h
T
CydandClaude Fable 5 b97dcce3a2 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>
2026-07-27 14:40:53 -05:00

180 lines
5.2 KiB
C++

#pragma once
#include <D3DX9.h>
#include <hash_map>
#include <string>
#define L4VERTEX_FVF (D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE | D3DFVF_NORMAL)
#define L4POINTVERTEX_FVF (D3DFVF_XYZ)
#define L4VERTEX_2D_FVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)
#define L4VERTEX_2D_TEX_FVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
#define PASS_OPAQUE 0
#define PASS_DECAL 1
#define PASS_ALPHABLEND 2
#define PASS_SPHERE 3
#define PASS_SKY 4
#define PASS_TOTAL_COUNT 5
struct L4VERTEX
{
float x, y, z;
float nx, ny, nz;
DWORD color;
float u, v;
};
struct L4POINTVERTEX
{
float x, y, z;
};
struct L4VERTEX_2D
{
float x, y, z, rhw;
DWORD color;
};
struct L4VERTEX_2D_TEX
{
float x, y, z, rhw;
DWORD color;
float u, v;
};
struct L4TEXOP
{
enum WrapType
{
REPEAT,
CLAMP,
SELECT
} wrap_u, wrap_v;
LPDIRECT3DTEXTURE9 texture;
bool doScroll;
float scrollUDelta;
float scrollVDelta;
};
struct L4RAMP
{
float r0, g0, b0;
float r1, g1, b1;
};
struct L4DRAWOP
{
D3DMATERIAL9 material;
L4TEXOP texture;
bool alphaTest;
bool drawAsDecal;
bool drawAsSky;
};
class d3d_OBJECT
{
public:
d3d_OBJECT(LPDIRECT3DDEVICE9 device, int vertCount);
d3d_OBJECT(LPDIRECT3DDEVICE9 device, LPD3DXMESH mesh, DWORD *adjacencyBuffer, int drawOpCount);
~d3d_OBJECT();
inline LPDIRECT3DDEVICE9 GetDevice() { return mDevice; }
inline int GetVertCount() { return mVertCount; }
inline int GetDrawOpCount() { return mDrawOpCount; }
inline L4DRAWOP* GetDrawOp(int index) { return &(mDrawOps[index]); }
void Draw(int pass, const D3DXMATRIX *viewTransform, Time targetRenderFrame);
inline void SetRadius(float radius) { mRadius = radius; }
inline void SetOrigin(L4POINTVERTEX origin) { mOrigin = origin; }
inline void SetImmune(bool immune) { mImmune = immune; }
inline void SetLocalToWorld(D3DXMATRIX localToWorld) { mLocalToWorld = localToWorld; }
inline float GetRadius() { return mRadius; }
inline L4POINTVERTEX GetOrigin() { return mOrigin; }
inline bool GetImmune() { return mImmune; }
inline D3DXMATRIX GetLocalToWorld() { return mLocalToWorld; }
inline LPD3DXMESH GetMesh() { return mMesh; }
inline d3d_OBJECT* GetNext(int pass) { return mNext[pass + 1]; }
inline d3d_OBJECT* GetPrevious( int pass) { return mPrev[pass + 1]; }
inline void SetNext(d3d_OBJECT *next, int pass) { mNext[pass + 1] = next; }
inline void SetPrevious(d3d_OBJECT *previous, int pass) { mPrev[pass + 1] = previous; }
static void ResetState(LPDIRECT3DDEVICE9 device);
static d3d_OBJECT* LoadObject(LPDIRECT3DDEVICE9 device, char *fileName);
static L4TEXOP LoadTexture(LPDIRECT3DDEVICE9 device, const char *fileName);
// Cached textures are bound to the device that created them - the
// cache MUST be flushed when that device goes away (the single-binary
// 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);
LPDIRECT3DDEVICE9 mDevice;
inline void SetVB(LPDIRECT3DVERTEXBUFFER9 buffer) { mVB = buffer; }
void DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time targetRenderFrame);
void DrawSpheres(int pass, const D3DXMATRIX *viewTransform);
long mID;
// vertex based
int mVertCount;
LPDIRECT3DVERTEXBUFFER9 mVB;
// mesh based
LPD3DXMESH mMesh;
int mDrawOpCount;
L4DRAWOP* mDrawOps;
bool mImmune;
D3DXMATRIX mLocalToWorld;
L4POINTVERTEX mOrigin;
float mRadius;
d3d_OBJECT *mNext[PASS_TOTAL_COUNT + 1];
d3d_OBJECT *mPrev[PASS_TOTAL_COUNT + 1];
void SetTextureScrolling(const L4TEXOP *texture, Time targetRenderFrame);
void SetTextureAddressing(L4TEXOP::WrapType wrap_u, L4TEXOP::WrapType wrap_v);
void SetTexture(LPDIRECT3DTEXTURE9 texture);
// state information for optimizing renderstate changes
static bool mLastTextureScrollState;
static L4TEXOP::WrapType mLastWrapU;
static L4TEXOP::WrapType mLastWrapV;
static bool mLastTexturingState;
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;