diff --git a/CMake/cmake-toolchain-mingw64-x86_64.cmake b/CMake/cmake-toolchain-mingw64-x86_64.cmake new file mode 100644 index 00000000..8bf43669 --- /dev/null +++ b/CMake/cmake-toolchain-mingw64-x86_64.cmake @@ -0,0 +1,18 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86_64) + +find_program(CMAKE_C_COMPILER NAMES x86_64-w64-mingw32-gcc) +find_program(CMAKE_CXX_COMPILER NAMES x86_64-w64-mingw32-g++) +find_program(CMAKE_RC_COMPILER NAMES x86_64-w64-mingw32-windres windres) + +if(NOT CMAKE_C_COMPILER) + message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") +endif() + +if(NOT CMAKE_CXX_COMPILER) + message(FATAL_ERROR "Failed to find CMAKE_CXX_COMPILER.") +endif() + +if(NOT CMAKE_RC_COMPILER) + message(FATAL_ERROR "Failed to find CMAKE_RC_COMPILER.") +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d561e1e..4d514ef6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ if (NOT MINGW) set(NOT_MINGW ON) else() set(NOT_MINGW OFF) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -static-libgcc -static-libstdc++") endif() find_program(SDL_SHADERCROSS_BIN NAMES "shadercross") diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index b12aae52..5017f456 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -48,6 +48,14 @@ else() message(STATUS "🧩 OpenGL ES 2.x support not enabled") endif() +if(WIN32) + target_sources(miniwin PRIVATE + src/d3drm/backends/directx9/actual.cpp + src/d3drm/backends/directx9/renderer.cpp + ) + target_link_libraries(miniwin PRIVATE d3d9) +endif() + target_compile_definitions(miniwin PUBLIC MINIWIN) target_include_directories(miniwin diff --git a/miniwin/include/miniwin/dinput.h b/miniwin/include/miniwin/dinput.h index 7767abc7..ff885caa 100644 --- a/miniwin/include/miniwin/dinput.h +++ b/miniwin/include/miniwin/dinput.h @@ -1,6 +1,4 @@ #pragma once -#include "miniwin/windows.h" - // --- Typedefs --- typedef struct IDirectInputA* LPDIRECTINPUTA; diff --git a/miniwin/include/miniwin/miniwindevice.h b/miniwin/include/miniwin/miniwindevice.h index 48f1f93a..00329393 100644 --- a/miniwin/include/miniwin/miniwindevice.h +++ b/miniwin/include/miniwin/miniwindevice.h @@ -1,5 +1,7 @@ #pragma once +#include + DEFINE_GUID(IID_IDirect3DRMMiniwinDevice, 0x6eb09673, 0x8d30, 0x4d8a, 0x8d, 0x81, 0x34, 0xea, 0x69, 0x30, 0x12, 0x01); struct IDirect3DRMMiniwinDevice : virtual public IUnknown { diff --git a/miniwin/src/d3drm/backends/directx9/actual.cpp b/miniwin/src/d3drm/backends/directx9/actual.cpp new file mode 100644 index 00000000..30a3360c --- /dev/null +++ b/miniwin/src/d3drm/backends/directx9/actual.cpp @@ -0,0 +1,385 @@ +#include "actual.h" + +#include "structs.h" + +#include +#include +#include +#include + +// Global D3D9 state +static LPDIRECT3D9 g_d3d; +static LPDIRECT3DDEVICE9 g_device; +static HWND g_hwnd; +static LPDIRECT3DTEXTURE9 g_renderTargetTex; +static LPDIRECT3DSURFACE9 g_renderTargetSurf; +static LPDIRECT3DSURFACE9 g_oldRenderTarget; +static int m_width; +static int m_height; +static std::vector m_lights; +static Matrix4x4 m_projection; + +bool Actual_Initialize(void* hwnd, int width, int height) +{ + g_hwnd = (HWND) hwnd; + m_width = width; + m_height = height; + g_d3d = Direct3DCreate9(D3D_SDK_VERSION); + if (!g_d3d) { + return false; + } + + D3DPRESENT_PARAMETERS pp = {}; + pp.Windowed = TRUE; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.hDeviceWindow = g_hwnd; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.BackBufferWidth = width; + pp.BackBufferHeight = height; + pp.EnableAutoDepthStencil = TRUE; + pp.AutoDepthStencilFormat = D3DFMT_D24S8; + + HRESULT hr = g_d3d->CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + g_hwnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, + &pp, + &g_device + ); + if (FAILED(hr)) { + g_d3d->Release(); + return false; + } + + hr = g_device->CreateTexture( + width, + height, + 1, + D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, + &g_renderTargetTex, + nullptr + ); + if (FAILED(hr)) { + g_device->Release(); + g_d3d->Release(); + return false; + } + + hr = g_renderTargetTex->GetSurfaceLevel(0, &g_renderTargetSurf); + if (FAILED(hr)) { + g_renderTargetTex->Release(); + g_device->Release(); + g_d3d->Release(); + return false; + } + + g_device->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1); + + return true; +} + +void Actual_Shutdown() +{ + if (g_renderTargetSurf) { + g_renderTargetSurf->Release(); + g_renderTargetSurf = nullptr; + } + if (g_renderTargetTex) { + g_renderTargetTex->Release(); + g_renderTargetTex = nullptr; + } + if (g_device) { + g_device->Release(); + g_device = nullptr; + } + if (g_d3d) { + g_d3d->Release(); + g_d3d = nullptr; + } +} + +void Actual_PushLights(const BridgeSceneLight* lightsArray, size_t count) +{ + m_lights.assign(lightsArray, lightsArray + count); +} + +void Actual_SetProjection(const Matrix4x4* projection, float front, float back) +{ + memcpy(&m_projection, projection, sizeof(Matrix4x4)); +} + +IDirect3DTexture9* UploadSurfaceToD3DTexture(SDL_Surface* surface) +{ + IDirect3DTexture9* texture; + + HRESULT hr = + g_device->CreateTexture(surface->w, surface->h, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture, nullptr); + + if (FAILED(hr)) { + return nullptr; + } + + SDL_Surface* conv = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); + if (!conv) { + texture->Release(); + return nullptr; + } + + D3DLOCKED_RECT lockedRect; + texture->LockRect(0, &lockedRect, nullptr, 0); + + for (int y = 0; y < conv->h; ++y) { + memcpy( + (uint8_t*) lockedRect.pBits + y * lockedRect.Pitch, + (uint8_t*) conv->pixels + y * conv->pitch, + conv->w * 4 + ); + } + + texture->UnlockRect(0); + SDL_DestroySurface(conv); + + return texture; +} + +void ReleaseD3DTexture(IDirect3DTexture9* texture) +{ + texture->Release(); +} + +void ReleaseD3DVertexBuffer(IDirect3DVertexBuffer9* buffer) +{ + buffer->Release(); +} + +void ReleaseD3DIndexBuffer(IDirect3DIndexBuffer9* buffer) +{ + buffer->Release(); +} + +void UploadMeshBuffers( + const void* vertices, + int vertexSize, + const uint16_t* indices, + int indexSize, + D3D9MeshCacheEntry& cache +) +{ + g_device->CreateVertexBuffer( + vertexSize, + 0, + D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1, + D3DPOOL_MANAGED, + &cache.vbo, + nullptr + ); + void* vbData; + cache.vbo->Lock(0, 0, &vbData, 0); + memcpy(vbData, vertices, vertexSize); + cache.vbo->Unlock(); + + g_device->CreateIndexBuffer(indexSize, 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &cache.ibo, nullptr); + void* ibData; + cache.ibo->Lock(0, 0, &ibData, 0); + memcpy(ibData, indices, indexSize); + cache.ibo->Unlock(); +} + +constexpr float PI = 3.14159265358979323846f; + +void SetupLights() +{ + g_device->SetRenderState(D3DRS_LIGHTING, TRUE); + + for (DWORD i = 0; i < 8; ++i) { + g_device->LightEnable(i, FALSE); + } + + DWORD lightIdx = 0; + for (const auto& l : m_lights) { + if (lightIdx >= 8) { + break; + } + + const FColor c = l.color; + + if (l.directional != 1.0f && l.positional != 1.0f) { + g_device->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_COLORVALUE(c.r, c.g, c.b, 1.0f)); + continue; + } + + D3DLIGHT9 light = {}; + light.Type = (l.directional == 1.0f) ? D3DLIGHT_DIRECTIONAL : D3DLIGHT_POINT; + + light.Ambient = {0, 0, 0, 0}; + light.Diffuse = {c.r, c.g, c.b, c.a}; + + if (light.Type == D3DLIGHT_DIRECTIONAL) { + light.Direction.x = -l.direction.x; + light.Direction.y = -l.direction.y; + light.Direction.z = -l.direction.z; + light.Specular = {0, 0, 0, 0}; + } + else if (light.Type == D3DLIGHT_POINT) { + light.Specular = {1, 1, 1, 1}; + light.Position.x = l.position.x; + light.Position.y = l.position.y; + light.Position.z = l.position.z; + light.Range = 1000.0f; + light.Attenuation0 = 1.0f; + light.Attenuation1 = 0.0f; + light.Attenuation2 = 0.0f; + } + + light.Falloff = 1.0f; + light.Phi = PI; + light.Theta = PI / 2; + + if (SUCCEEDED(g_device->SetLight(lightIdx, &light))) { + g_device->LightEnable(lightIdx, TRUE); + } + ++lightIdx; + } +} + +uint32_t Actual_BeginFrame() +{ + g_device->GetRenderTarget(0, &g_oldRenderTarget); + g_device->SetRenderTarget(0, g_renderTargetSurf); + + g_device->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0); + + g_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); + g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + g_device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + + g_device->BeginScene(); + + SetupLights(); + + return D3D_OK; +} + +void Actual_EnableTransparency() +{ + g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + g_device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); +} + +D3DMATRIX ToD3DMATRIX(const Matrix4x4& in) +{ + D3DMATRIX out; + for (int row = 0; row < 4; ++row) { + out.m[row][0] = static_cast(in[row][0]); + out.m[row][1] = static_cast(in[row][1]); + out.m[row][2] = static_cast(in[row][2]); + out.m[row][3] = static_cast(in[row][3]); + } + return out; +} + +void Actual_SubmitDraw( + const D3D9MeshCacheEntry* mesh, + const Matrix4x4* modelViewMatrix, + const Matrix3x3* normalMatrix, + const Appearance* appearance, + IDirect3DTexture9* texture +) +{ + D3DMATRIX worldView = ToD3DMATRIX(*modelViewMatrix); + g_device->SetTransform(D3DTS_WORLD, &worldView); + D3DMATRIX proj = ToD3DMATRIX(m_projection); + g_device->SetTransform(D3DTS_PROJECTION, &proj); + + D3DMATERIAL9 mat = {}; + mat.Diffuse.r = appearance->color.r / 255.0f; + mat.Diffuse.g = appearance->color.g / 255.0f; + mat.Diffuse.b = appearance->color.b / 255.0f; + mat.Diffuse.a = appearance->color.a / 255.0f; + mat.Ambient = mat.Diffuse; + + if (appearance->shininess != 0) { + g_device->SetRenderState(D3DRS_SPECULARENABLE, TRUE); + mat.Specular.r = 1.0f; + mat.Specular.g = 1.0f; + mat.Specular.b = 1.0f; + mat.Specular.a = 1.0f; + mat.Power = appearance->shininess; + } + else { + g_device->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + mat.Specular.r = 0.0f; + mat.Specular.g = 0.0f; + mat.Specular.b = 0.0f; + mat.Specular.a = 0.0f; + mat.Power = 0.0f; + } + + g_device->SetMaterial(&mat); + + if (texture) { + g_device->SetTexture(0, texture); + g_device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + g_device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + g_device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + g_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + g_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + g_device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + } + else { + g_device->SetTexture(0, nullptr); + } + + g_device->SetRenderState(D3DRS_SHADEMODE, mesh->flat ? D3DSHADE_FLAT : D3DSHADE_GOURAUD); + + g_device->SetStreamSource(0, mesh->vbo, 0, sizeof(BridgeSceneVertex)); + g_device->SetIndices(mesh->ibo); + g_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh->vertexCount, 0, mesh->indexCount / 3); +} + +uint32_t Actual_FinalizeFrame(void* pixels, int pitch) +{ + g_device->EndScene(); + + g_device->SetRenderTarget(0, g_oldRenderTarget); + g_oldRenderTarget->Release(); + + LPDIRECT3DSURFACE9 sysMemSurf; + HRESULT hr = + g_device + ->CreateOffscreenPlainSurface(m_width, m_height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &sysMemSurf, nullptr); + if (FAILED(hr)) { + return hr; + } + + hr = g_device->GetRenderTargetData(g_renderTargetSurf, sysMemSurf); + if (FAILED(hr)) { + sysMemSurf->Release(); + return hr; + } + + D3DLOCKED_RECT lockedRect; + hr = sysMemSurf->LockRect(&lockedRect, nullptr, D3DLOCK_READONLY); + if (FAILED(hr)) { + sysMemSurf->Release(); + return hr; + } + + BYTE* src = static_cast(lockedRect.pBits); + BYTE* dst = static_cast(pixels); + int copyWidth = m_width * 4; + + for (int y = 0; y < m_height; y++) { + memcpy(dst + y * pitch, src + y * lockedRect.Pitch, copyWidth); + } + + sysMemSurf->UnlockRect(); + sysMemSurf->Release(); + + return hr; +} diff --git a/miniwin/src/d3drm/backends/directx9/actual.h b/miniwin/src/d3drm/backends/directx9/actual.h new file mode 100644 index 00000000..9080878d --- /dev/null +++ b/miniwin/src/d3drm/backends/directx9/actual.h @@ -0,0 +1,76 @@ +#pragma once + +#include "structs.h" + +#include +#include +#include + +typedef float Matrix4x4[4][4]; + +struct BridgeVector { + float x, y, z; +}; + +struct BridgeSceneLight { + FColor color; + BridgeVector position; + float positional; + BridgeVector direction; + float directional; +}; + +struct BridgeSceneVertex { + BridgeVector position; + BridgeVector normal; + float tu, tv; +}; + +struct IDirect3DRMTexture; +struct IDirect3DTexture9; +struct IDirect3DVertexBuffer9; +struct IDirect3DIndexBuffer9; +struct MeshGroup; + +struct D3D9TextureCacheEntry { + IDirect3DRMTexture* texture; + uint32_t version; + IDirect3DTexture9* dxTexture; +}; + +struct D3D9MeshCacheEntry { + const MeshGroup* meshGroup; + uint32_t version; + bool flat; + + IDirect3DVertexBuffer9* vbo; + uint32_t vertexCount; + IDirect3DIndexBuffer9* ibo; + uint32_t indexCount; +}; + +bool Actual_Initialize(void* hwnd, int width, int height); +void Actual_Shutdown(); +void Actual_PushLights(const BridgeSceneLight* lightsArray, size_t count); +void Actual_SetProjection(const Matrix4x4* projection, float front, float back); +IDirect3DTexture9* UploadSurfaceToD3DTexture(SDL_Surface* surface); +void ReleaseD3DTexture(IDirect3DTexture9* dxTexture); +void ReleaseD3DVertexBuffer(IDirect3DVertexBuffer9* buffer); +void ReleaseD3DIndexBuffer(IDirect3DIndexBuffer9* buffer); +void UploadMeshBuffers( + const void* vertices, + int vertexSize, + const uint16_t* indices, + int indexSize, + D3D9MeshCacheEntry& cache +); +uint32_t Actual_BeginFrame(); +void Actual_EnableTransparency(); +void Actual_SubmitDraw( + const D3D9MeshCacheEntry* mesh, + const Matrix4x4* modelViewMatrix, + const Matrix3x3* normalMatrix, + const Appearance* appearance, + IDirect3DTexture9* texture +); +uint32_t Actual_FinalizeFrame(void* pixels, int pitch); diff --git a/miniwin/src/d3drm/backends/directx9/renderer.cpp b/miniwin/src/d3drm/backends/directx9/renderer.cpp new file mode 100644 index 00000000..f0207041 --- /dev/null +++ b/miniwin/src/d3drm/backends/directx9/renderer.cpp @@ -0,0 +1,277 @@ +#include "d3drmrenderer_directx9.h" +#include "ddraw_impl.h" +#include "ddsurface_impl.h" +#include "mathutils.h" +#include "meshutils.h" +#include "structs.h" + +#include +#include +#include +#include + +static_assert(sizeof(Matrix4x4) == sizeof(D3DRMMATRIX4D), "Matrix4x4 is wrong size"); +static_assert(sizeof(BridgeVector) == sizeof(D3DVECTOR), "BridgeVector is wrong size"); +static_assert(sizeof(BridgeSceneLight) == sizeof(SceneLight), "BridgeSceneLight is wrong size"); +static_assert(sizeof(BridgeSceneVertex) == sizeof(D3DRMVERTEX), "BridgeSceneVertex is wrong size"); + +Direct3DRMRenderer* DirectX9Renderer::Create(DWORD width, DWORD height) +{ + return new DirectX9Renderer(width, height); +} + +DirectX9Renderer::DirectX9Renderer(DWORD width, DWORD height) : m_width(width), m_height(height) +{ + Actual_Initialize( + SDL_GetPointerProperty(SDL_GetWindowProperties(DDWindow), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL), + width, + height + ); + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_ARGB8888); +} + +DirectX9Renderer::~DirectX9Renderer() +{ + Actual_Shutdown(); +} + +void DirectX9Renderer::PushLights(const SceneLight* lightsArray, size_t count) +{ + Actual_PushLights((BridgeSceneLight*) lightsArray, count); +} + +void DirectX9Renderer::SetFrustumPlanes(const Plane* frustumPlanes) +{ +} + +void DirectX9Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) +{ + Actual_SetProjection(&projection, front, back); +} + +struct TextureDestroyContextDX9 { + DirectX9Renderer* renderer; + Uint32 textureId; +}; + +void DirectX9Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) +{ + auto* ctx = new TextureDestroyContextDX9{this, id}; + texture->AddDestroyCallback( + [](IDirect3DRMObject* obj, void* arg) { + auto* ctx = static_cast(arg); + auto& cache = ctx->renderer->m_textures[ctx->textureId]; + if (cache.dxTexture) { + ReleaseD3DTexture(cache.dxTexture); + cache.dxTexture = nullptr; + cache.texture = nullptr; + } + delete ctx; + }, + ctx + ); +} + +Uint32 DirectX9Renderer::GetTextureId(IDirect3DRMTexture* iTexture) +{ + auto texture = static_cast(iTexture); + auto surface = static_cast(texture->m_surface); + + for (Uint32 i = 0; i < m_textures.size(); ++i) { + auto& tex = m_textures[i]; + if (tex.texture == texture) { + if (tex.version != texture->m_version) { + if (tex.dxTexture) { + ReleaseD3DTexture(tex.dxTexture); + tex.dxTexture = nullptr; + } + tex.dxTexture = UploadSurfaceToD3DTexture(surface->m_surface); + if (!tex.dxTexture) { + return NO_TEXTURE_ID; + } + tex.version = texture->m_version; + } + return i; + } + } + + IDirect3DTexture9* newTex = UploadSurfaceToD3DTexture(surface->m_surface); + if (!newTex) { + return NO_TEXTURE_ID; + } + + for (Uint32 i = 0; i < m_textures.size(); ++i) { + auto& tex = m_textures[i]; + if (!tex.texture) { + tex.texture = texture; + tex.version = texture->m_version; + tex.dxTexture = newTex; + AddTextureDestroyCallback(i, texture); + return i; + } + } + + m_textures.push_back({texture, texture->m_version, newTex}); + AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); + return (Uint32) (m_textures.size() - 1); +} + +D3D9MeshCacheEntry UploadD3D9Mesh(const MeshGroup& meshGroup) +{ + D3D9MeshCacheEntry cache; + cache.meshGroup = &meshGroup; + cache.version = meshGroup.version; + cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT; + + std::vector vertices; + std::vector indices; + + if (cache.flat) { + FlattenSurfaces( + meshGroup.vertices.data(), + meshGroup.vertices.size(), + meshGroup.indices.data(), + meshGroup.indices.size(), + meshGroup.texture != nullptr, + vertices, + indices + ); + } + else { + vertices = meshGroup.vertices; + indices.resize(meshGroup.indices.size()); + std::transform(meshGroup.indices.begin(), meshGroup.indices.end(), indices.begin(), [](DWORD i) { + return static_cast(i); + }); + } + + cache.indexCount = indices.size(); + cache.vertexCount = vertices.size(); + + UploadMeshBuffers( + vertices.data(), + vertices.size() * sizeof(D3DRMVERTEX), + indices.data(), + indices.size() * sizeof(uint16_t), + cache + ); + + return cache; +} + +struct D3D9MeshDestroyContext { + DirectX9Renderer* renderer; + Uint32 id; +}; + +void DirectX9Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) +{ + auto* ctx = new D3D9MeshDestroyContext{this, id}; + mesh->AddDestroyCallback( + [](IDirect3DRMObject*, void* arg) { + auto* ctx = static_cast(arg); + auto& cache = ctx->renderer->m_meshs[ctx->id]; + if (cache.vbo) { + ReleaseD3DVertexBuffer(cache.vbo); + cache.vbo = nullptr; + } + if (cache.ibo) { + ReleaseD3DIndexBuffer(cache.ibo); + cache.ibo = nullptr; + } + cache.meshGroup = nullptr; + + delete ctx; + }, + ctx + ); +} + +Uint32 DirectX9Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) +{ + for (Uint32 i = 0; i < m_meshs.size(); ++i) { + auto& cache = m_meshs[i]; + if (cache.meshGroup == meshGroup) { + if (cache.version != meshGroup->version) { + cache = UploadD3D9Mesh(*meshGroup); + } + return i; + } + } + + auto newCache = UploadD3D9Mesh(*meshGroup); + + for (Uint32 i = 0; i < m_meshs.size(); ++i) { + if (!m_meshs[i].meshGroup) { + m_meshs[i] = std::move(newCache); + return i; + } + } + + m_meshs.push_back(std::move(newCache)); + return static_cast(m_meshs.size() - 1); +} + +DWORD DirectX9Renderer::GetWidth() +{ + return m_width; +} + +DWORD DirectX9Renderer::GetHeight() +{ + return m_height; +} + +void DirectX9Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) +{ + halDesc->dcmColorModel = D3DCOLORMODEL::RGB; + halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; + halDesc->dwDeviceZBufferBitDepth = DDBD_24; + helDesc->dwDeviceRenderBitDepth = DDBD_32; + halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; + halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; + halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; + + memset(helDesc, 0, sizeof(D3DDEVICEDESC)); +} + +const char* DirectX9Renderer::GetName() +{ + return "DirectX 9 HAL"; +} + +HRESULT DirectX9Renderer::BeginFrame() +{ + return Actual_BeginFrame(); +} + +void DirectX9Renderer::EnableTransparency() +{ + Actual_EnableTransparency(); +} + +void DirectX9Renderer::SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const Matrix3x3& normalMatrix, + const Appearance& appearance +) +{ + IDirect3DTexture9* texture = nullptr; + if (appearance.textureId != NO_TEXTURE_ID) { + texture = m_textures[appearance.textureId].dxTexture; + } + Actual_SubmitDraw(&m_meshs[meshId], &modelViewMatrix, &normalMatrix, &appearance, texture); +} + +HRESULT DirectX9Renderer::FinalizeFrame() +{ + HRESULT hr = Actual_FinalizeFrame(m_renderedImage->pixels, m_renderedImage->pitch); + if (hr != DD_OK) { + return hr; + } + + // Composite onto SDL backbuffer + SDL_BlitSurface(m_renderedImage, nullptr, DDBackBuffer, nullptr); + return hr; +} diff --git a/miniwin/src/d3drm/d3drm.cpp b/miniwin/src/d3drm/d3drm.cpp index f819a94c..01375d8c 100644 --- a/miniwin/src/d3drm/d3drm.cpp +++ b/miniwin/src/d3drm/d3drm.cpp @@ -13,6 +13,9 @@ #ifdef USE_OPENGLES2 #include "d3drmrenderer_opengles2.h" #endif +#ifdef _WIN32 +#include "d3drmrenderer_directx9.h" +#endif #include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_software.h" #include "d3drmtexture_impl.h" @@ -156,6 +159,11 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface( else if (SDL_memcmp(&guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { renderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } +#endif +#ifdef _WIN32 + else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) { + renderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + } #endif else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized"); diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index ca27d049..bd1541a3 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -4,6 +4,9 @@ #ifdef USE_OPENGLES2 #include "d3drmrenderer_opengles2.h" #endif +#ifdef _WIN32 +#include "d3drmrenderer_directx9.h" +#endif #include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_software.h" #include "ddpalette_impl.h" @@ -225,6 +228,9 @@ HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) #endif #ifdef USE_OPENGL1 OpenGL1Renderer_EnumDevice(cb, ctx); +#endif +#ifdef _WIN32 + DirectX9Renderer_EnumDevice(cb, ctx); #endif Direct3DRMSoftware_EnumDevice(cb, ctx); @@ -332,6 +338,11 @@ HRESULT DirectDrawImpl::CreateDevice( else if (SDL_memcmp(&guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { renderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } +#endif +#ifdef _WIN32 + else if (SDL_memcmp(&guid, &DirectX9_GUID, sizeof(GUID)) == 0) { + renderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + } #endif else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); diff --git a/miniwin/src/internal/d3drmrenderer.h b/miniwin/src/internal/d3drmrenderer.h index 16ca4b8b..e1e7e558 100644 --- a/miniwin/src/internal/d3drmrenderer.h +++ b/miniwin/src/internal/d3drmrenderer.h @@ -4,6 +4,7 @@ #include "mathutils.h" #include "miniwin/d3drm.h" #include "miniwin/miniwindevice.h" +#include "structs.h" #include @@ -11,17 +12,6 @@ static_assert(sizeof(D3DRMVERTEX) == 32); -struct Appearance { - SDL_Color color; - float shininess; - Uint32 textureId; - Uint32 flat; -}; - -struct FColor { - float r, g, b, a; -}; - struct SceneLight { FColor color; D3DVECTOR position; diff --git a/miniwin/src/internal/d3drmrenderer_directx9.h b/miniwin/src/internal/d3drmrenderer_directx9.h new file mode 100644 index 00000000..8df63424 --- /dev/null +++ b/miniwin/src/internal/d3drmrenderer_directx9.h @@ -0,0 +1,55 @@ +#pragma once + +#include "../d3drm/backends/directx9/actual.h" +#include "d3drmrenderer.h" +#include "d3drmtexture_impl.h" +#include "ddraw_impl.h" + +#include + +DEFINE_GUID(DirectX9_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05); + +class DirectX9Renderer : public Direct3DRMRenderer { +public: + static Direct3DRMRenderer* Create(DWORD width, DWORD height); + DirectX9Renderer(DWORD width, DWORD height); + ~DirectX9Renderer() override; + + void PushLights(const SceneLight* lightsArray, size_t count) override; + void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; + void SetFrustumPlanes(const Plane* frustumPlanes) override; + Uint32 GetTextureId(IDirect3DRMTexture* texture) override; + Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; + DWORD GetWidth() override; + DWORD GetHeight() override; + void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; + const char* GetName() override; + HRESULT BeginFrame() override; + void EnableTransparency() override; + void SubmitDraw( + DWORD meshId, + const D3DRMMATRIX4D& modelViewMatrix, + const Matrix3x3& normalMatrix, + const Appearance& appearance + ) override; + HRESULT FinalizeFrame() override; + +private: + void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); + void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); + + SDL_Surface* m_renderedImage; + DWORD m_width, m_height; + std::vector m_lights; + std::vector m_meshs; + std::vector m_textures; +}; + +inline static void DirectX9Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) +{ + Direct3DRMRenderer* device = DirectX9Renderer::Create(640, 480); + if (device) { + EnumDevice(cb, ctx, device, DirectX9_GUID); + delete device; + } +} diff --git a/miniwin/src/internal/structs.h b/miniwin/src/internal/structs.h new file mode 100644 index 00000000..4f745098 --- /dev/null +++ b/miniwin/src/internal/structs.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +typedef float Matrix3x3[3][3]; + +struct FColor { + float r, g, b, a; +}; + +struct Appearance { + SDL_Color color; + float shininess; + uint32_t textureId; + uint32_t flat; +};