WinMain now wraps the engine block in a loop: when a front-end-launched mission ends under the local console, the setup screen comes back in the same process instead of exiting (the arcade relaunch-per-mission model). Replaces the CreateProcess self-respawn - required for Steam, where the lobby and sockets must survive across races. Second-cycle re-init crash fixed: d3d_OBJECT kept a static texture cache keyed by filename, so race 2 got IDirect3DTexture9 pointers created on race 1 destroyed device and died at first draw (DrawMesh AV). The cache is now flushed in ~DPLRenderer before the device is released, and ParticleEngine::Initialize drops particles left over from the previous mission. Verified: three consecutive 30s races in one PID, each stopped on time by the console with final scores collected. Also: L4CONSOLELEN env override for test-length races, and the console exposes MissionCompleted() for the loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
156 lines
4.0 KiB
C++
156 lines
4.0 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();
|
|
|
|
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;
|
|
};
|
|
|
|
extern int gNumBatches;
|