#pragma once #include #include #include #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 // GROUND-SHADOW list (task #49b): tshd proxies draw between the STATIC opaque // geometry (terrain) and the DYNAMIC opaque list (mech bodies) -- the classic // decal order. The feet then z-pass over the already-drawn shadow, so the // shadow's big terrain depth-bias can never paint OVER the mech. (The list is // drawn with pass id PASS_ALPHABLEND so d3d_OBJECT::DrawMesh's mIsShadow // state-block branch runs unchanged.) #define PASS_SHADOW 5 #define PASS_TOTAL_COUNT 6 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; // PORT (draw-cost fix): explicit index range for BGF-built meshes. D3DX // DrawSubset needs an attribute TABLE; our double-sided BGF indices make // GenerateAdjacency/OptimizeInplace fail silently, so DrawSubset fell back to // scanning the whole attribute buffer PER CALL (~44us x ~1400 calls = ~60ms/ // frame). The loader knows each batch's exact range -- draw it directly. int bgfStartIndex; // first index of the batch in the mesh IB int bgfPrimCount; // triangle count (0 = not a BGF batch -> DrawSubset) // PORT (authentic LODs): this batch's LOD viewing band [lodNear..lodFar) from // the BGF's 0x2046 headers. DrawMesh draws only the ops whose band contains // the camera->object distance (the IG board's distance LOD selection). A // zero-init op (lodFar==0) is treated as always-drawn (non-BGF fallback). float lodNear; float lodFar; // DIAG (BT_LOD_LOG): last band-gate visibility (0=unknown 1=visible 2=hidden) // so DrawMesh can log per-op FLIP events during movement (which piece blinks). int lastBandVisible; // PUNCH (dpl_Punchize, PATCH SV_SPECIAL): cutout punch-through -- the texture's // black texels were loaded with alpha 0; draw with alpha TEST in the opaque pass // (z-write preserved), rejecting hole texels. bool punchThrough; // VERTEX-ALPHA BLEND (BGF 0x0082/0x008A verts with authored alpha < 1): an // effect card (the FLAMEBIG flame fan). Drawn in the alpha-blend pass, // UNLIT, colour = texture x the authored per-vertex gradient, alpha = the // per-vertex fade (SRCALPHA/INVSRCALPHA). bool vertexAlphaBlend; // COCKPIT PUNCH STENCIL-CUT (task #55, i860-firmware-decoded): 0=normal, // 1=aperture MASK (drawn stencil-only, paired with the following hull op), // 2=HULL (drawn stencil-rejected under the mask = window cutouts). int copRole; // LAYER DEPTH BIAS (additive-LOD coplanar resolution; see bgfload.h): finer // layers get a slightly more negative normalized-depth bias so EXACTLY- // coplanar duplicated surfaces resolve to the detail layer instead of // venetian-blind z-fighting (the board's submission-order rule, in D3D terms). float lodDepthBias; }; 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; } // BT (task #20): mark the mech's *_tshd SHADOW-PROXY geometry -- the binary's // shadow is this flat silhouette posed by jointshadow/jointtshadow; it must // draw TRANSLUCENT dark in the blend pass (the port drew it opaque black). inline void SetIsShadow(int v) { mIsShadow = v; } inline int GetIsShadow() { return mIsShadow; } int mIsShadow; // DIAG (BT_LOD_LOG): model basename for flip telemetry (set by LoadObjectBGF; // MUST be initialized in BOTH ctors -- debug heap 0xCD fill reads as garbage). char mDbgName[24]; 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); // PORT (turn-hitch fix): bounding-sphere frustum culling. The 1995 renderer // drew EVERYTHING every frame (the IG board clipped in hardware); on D3D9 that // is ~2700 draw calls/frame = ~107ms CPU. The renderer sets the frame's view // frustum once (SetCullFrustum); Draw() then skips objects fully outside it. static void SetCullFrustum(const D3DXMATRIX *viewProj); // NULL disables for the frame static void SetCameraPosition(float x, float y, float z); // for LOD distance selection static void GetCameraPosition(float *x, float *y, float *z); // wreck-flame billboard yaw D3DXVECTOR3 mCullCenter; // model-space bounding sphere (computed at load) float mCullRadius; // <= 0 -> unknown, never culled // PORT (draw-cost fix): direct-draw path for BGF meshes (see L4DRAWOP). The // mesh's own VB/IB, cached AddRef'd at load; drawn with DrawIndexedPrimitive // per batch range instead of D3DX DrawSubset. NULL -> DrawSubset fallback. LPDIRECT3DVERTEXBUFFER9 mBgfVB; LPDIRECT3DINDEXBUFFER9 mBgfIB; UINT mBgfStride; UINT mBgfNumVerts; private: static d3d_OBJECT* LoadSpheres(LPDIRECT3DDEVICE9 device, char *fileName); // Builds a mesh-backed d3d_OBJECT directly from a VWE .BGF (pod ships .bgf, not .x). static d3d_OBJECT* LoadObjectBGF(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;