#pragma once // Untextured quads on the UI's Direct3D 7 device (the argument of the UI's // render methods, vf14). #include #include namespace game { constexpr int kDeviceDrawPrimitive = 25; // IDirect3DDevice7 vtable slots constexpr int kDeviceSetTexture = 35; constexpr int kDeviceSetRenderState = 20; constexpr int kDeviceGetRenderState = 21; constexpr DWORD kStateSourceBlend = 19; // D3DRENDERSTATETYPE constexpr DWORD kStateDestinationBlend = 20; constexpr DWORD kStateAlphaBlendEnable = 27; constexpr DWORD kBlendOne = 2; // D3DBLEND constexpr DWORD kTriangleStrip = 5; constexpr DWORD kFvfScreenColourTexture = 0x1c4; // XYZRHW | DIFFUSE | SPECULAR | TEX1 struct ScreenVertex { float x, y, z, rhw; uint32_t diffuse, specular; float u, v; }; // Draws (x0, y0)-(x1, y1) with `surface` (a texture's Direct3D surface, // game/ui/texture.h) from (u0, v0) to (u1, v1), tinted by `colour`. inline void DrawTextured(void* device, void* surface, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, uint32_t colour = 0xffffffff) { void** vtable = *static_cast(device); using SetTextureFn = HRESULT(__stdcall*)(void*, DWORD, void*); using DrawPrimitiveFn = HRESULT(__stdcall*)(void*, DWORD, DWORD, void*, DWORD, DWORD); reinterpret_cast(vtable[kDeviceSetTexture])(device, 0, surface); ScreenVertex quad[4] = {{x0, y0, 0, 1, colour, 0, u0, v0}, {x1, y0, 0, 1, colour, 0, u1, v0}, {x0, y1, 0, 1, colour, 0, u0, v1}, {x1, y1, 0, 1, colour, 0, u1, v1}}; reinterpret_cast(vtable[kDeviceDrawPrimitive])(device, kTriangleStrip, kFvfScreenColourTexture, quad, 4, 0); reinterpret_cast(vtable[kDeviceSetTexture])(device, 0, nullptr); } // DrawTextured added onto what is drawn (source and destination blend ONE): // for the effect textures, drawn on black without alpha. inline void DrawTexturedAdded(void* device, void* surface, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1) { void** vtable = *static_cast(device); using SetRenderStateFn = HRESULT(__stdcall*)(void*, DWORD, DWORD); using GetRenderStateFn = HRESULT(__stdcall*)(void*, DWORD, DWORD*); const auto set = reinterpret_cast(vtable[kDeviceSetRenderState]); const auto get = reinterpret_cast(vtable[kDeviceGetRenderState]); DWORD blending = 0, source = 0, destination = 0; get(device, kStateAlphaBlendEnable, &blending); get(device, kStateSourceBlend, &source); get(device, kStateDestinationBlend, &destination); set(device, kStateAlphaBlendEnable, TRUE); set(device, kStateSourceBlend, kBlendOne); set(device, kStateDestinationBlend, kBlendOne); DrawTextured(device, surface, x0, y0, x1, y1, u0, v0, u1, v1); set(device, kStateAlphaBlendEnable, blending); set(device, kStateSourceBlend, source); set(device, kStateDestinationBlend, destination); } // Fills (x0, y0)-(x1, y1), ARGB colours per corner: top left, top right, // bottom left, bottom right (blended between them). inline void FillCorners(void* device, float x0, float y0, float x1, float y1, uint32_t topLeft, uint32_t topRight, uint32_t bottomLeft, uint32_t bottomRight) { void** vtable = *static_cast(device); using SetTextureFn = HRESULT(__stdcall*)(void*, DWORD, void*); using DrawPrimitiveFn = HRESULT(__stdcall*)(void*, DWORD, DWORD, void*, DWORD, DWORD); reinterpret_cast(vtable[kDeviceSetTexture])(device, 0, nullptr); ScreenVertex quad[4] = {{x0, y0, 0, 1, topLeft, 0, 0, 0}, {x1, y0, 0, 1, topRight, 0, 1, 0}, {x0, y1, 0, 1, bottomLeft, 0, 0, 1}, {x1, y1, 0, 1, bottomRight, 0, 1, 1}}; reinterpret_cast(vtable[kDeviceDrawPrimitive])(device, kTriangleStrip, kFvfScreenColourTexture, quad, 4, 0); } // Untextured triangles, three vertices each (a triangle list), in batches the // device takes in one call. inline void FillTriangles(void* device, ScreenVertex* vertices, size_t count) { constexpr DWORD kTriangleList = 4; constexpr size_t kBatch = 3 * 10000; void** vtable = *static_cast(device); using SetTextureFn = HRESULT(__stdcall*)(void*, DWORD, void*); using DrawPrimitiveFn = HRESULT(__stdcall*)(void*, DWORD, DWORD, void*, DWORD, DWORD); reinterpret_cast(vtable[kDeviceSetTexture])(device, 0, nullptr); for (size_t first = 0; first < count; first += kBatch) { const size_t batch = count - first < kBatch ? count - first : kBatch; reinterpret_cast(vtable[kDeviceDrawPrimitive])( device, kTriangleList, kFvfScreenColourTexture, vertices + first, static_cast(batch), 0); } } // Fills (x0, y0)-(x1, y1) with the ARGB colour `color`. inline void FillRect(void* device, float x0, float y0, float x1, float y1, uint32_t color) { FillCorners(device, x0, y0, x1, y1, color, color, color, color); } // Top to bottom from `top` to `bottom`. inline void FillVertical(void* device, float x0, float y0, float x1, float y1, uint32_t top, uint32_t bottom) { FillCorners(device, x0, y0, x1, y1, top, top, bottom, bottom); } // Left to right from `left` to `right`. inline void FillHorizontal(void* device, float x0, float y0, float x1, float y1, uint32_t left, uint32_t right) { FillCorners(device, x0, y0, x1, y1, left, right, left, right); } } // namespace game