From 8498c69af1b7428828ef5a03f120df851f87dde3 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Tue, 24 Jun 2025 22:38:08 +0200 Subject: [PATCH 01/22] Clear unknowns in `LegoPlantManager` (#1587) --- LEGO1/lego/legoomni/include/legoplantmanager.h | 12 ++++++------ .../lego/legoomni/src/common/legoplantmanager.cpp | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legoplantmanager.h b/LEGO1/lego/legoomni/include/legoplantmanager.h index 8a8c7902..91fe49c8 100644 --- a/LEGO1/lego/legoomni/include/legoplantmanager.h +++ b/LEGO1/lego/legoomni/include/legoplantmanager.h @@ -54,7 +54,7 @@ class LegoPlantManager : public MxCore { LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId); MxBool DecrementCounter(LegoEntity* p_entity); void ScheduleAnimation(LegoEntity* p_entity, MxLong p_length); - MxResult FUN_10026410(); + MxResult DetermineBoundaries(); void ClearCounters(); void SetInitialCounters(); @@ -77,11 +77,11 @@ class LegoPlantManager : public MxCore { static MxS32 g_maxMove[4]; static MxU32 g_maxSound; - LegoOmni::World m_worldId; // 0x08 - undefined m_unk0x0c; // 0x0c - AnimEntry* m_entries[5]; // 0x10 - MxS8 m_numEntries; // 0x24 - LegoWorld* m_world; // 0x28 + LegoOmni::World m_worldId; // 0x08 + MxBool m_boundariesDetermined; // 0x0c + AnimEntry* m_entries[5]; // 0x10 + MxS8 m_numEntries; // 0x24 + LegoWorld* m_world; // 0x28 }; #endif // LEGOPLANTMANAGER_H diff --git a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp index 734806fc..766edf79 100644 --- a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp @@ -83,7 +83,7 @@ void LegoPlantManager::Init() } m_worldId = LegoOmni::e_undefined; - m_unk0x0c = 0; + m_boundariesDetermined = FALSE; m_numEntries = 0; } @@ -98,7 +98,7 @@ void LegoPlantManager::LoadWorldInfo(LegoOmni::World p_worldId) CreatePlant(i, world, p_worldId); } - m_unk0x0c = 0; + m_boundariesDetermined = FALSE; } // FUNCTION: LEGO1 0x100263a0 @@ -119,12 +119,12 @@ void LegoPlantManager::Reset(LegoOmni::World p_worldId) } m_worldId = LegoOmni::e_undefined; - m_unk0x0c = 0; + m_boundariesDetermined = FALSE; } // FUNCTION: LEGO1 0x10026410 // FUNCTION: BETA10 0x100c50e9 -MxResult LegoPlantManager::FUN_10026410() +MxResult LegoPlantManager::DetermineBoundaries() { // similar to LegoBuildingManager::FUN_10030630() @@ -192,7 +192,7 @@ MxResult LegoPlantManager::FUN_10026410() } } - m_unk0x0c = TRUE; + m_boundariesDetermined = TRUE; return SUCCESS; } @@ -200,8 +200,8 @@ MxResult LegoPlantManager::FUN_10026410() // FUNCTION: BETA10 0x100c55e0 LegoPlantInfo* LegoPlantManager::GetInfoArray(MxS32& p_length) { - if (!m_unk0x0c) { - FUN_10026410(); + if (!m_boundariesDetermined) { + DetermineBoundaries(); } p_length = sizeOfArray(g_plantInfo); From 87b4c0412e6e9d61df090021c2bab294cbe5d281 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 15:03:54 -0700 Subject: [PATCH 02/22] Enhance compatibility of FetchFS (#424) --- ISLE/emscripten/libwasmfs_fetch.js.patch | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ISLE/emscripten/libwasmfs_fetch.js.patch b/ISLE/emscripten/libwasmfs_fetch.js.patch index 6c598e3f..eb16caf0 100644 --- a/ISLE/emscripten/libwasmfs_fetch.js.patch +++ b/ISLE/emscripten/libwasmfs_fetch.js.patch @@ -1,5 +1,5 @@ diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js -index e8c9f7e21..5c3a3dfbe 100644 +index e8c9f7e21..1c0eea957 100644 --- a/src/lib/libwasmfs_fetch.js +++ b/src/lib/libwasmfs_fetch.js @@ -38,36 +38,7 @@ addToLibrary({ @@ -49,7 +49,7 @@ index e8c9f7e21..5c3a3dfbe 100644 allPresent = false; break; } -@@ -90,16 +61,36 @@ addToLibrary({ +@@ -90,16 +61,37 @@ addToLibrary({ // one request for all the chunks we need, rather than one // request per chunk. var start = firstChunk * chunkSize; @@ -66,8 +66,10 @@ index e8c9f7e21..5c3a3dfbe 100644 if (!response.ok) { throw response; } +- var bytes = await response['bytes'](); + - var bytes = await response['bytes'](); ++ const buffer = await response.arrayBuffer(); ++ const bytes = new Uint8Array(buffer); + if (!(file in wasmFS$JSMemoryRanges)) { + var size = Math.max( + parseInt(response.headers.get('Content-Range').split('/')[1], 10), @@ -87,7 +89,7 @@ index e8c9f7e21..5c3a3dfbe 100644 return Promise.resolve(); } -@@ -164,6 +155,21 @@ addToLibrary({ +@@ -164,6 +156,21 @@ addToLibrary({ return wasmFS$JSMemoryRanges[file].size; }, }; From 8dbcaf6d6521b24ee9264b94a0598f702c75dffb Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 15:54:26 -0700 Subject: [PATCH 03/22] Allow running game without audio device (#426) * Allow running game without audio device * Remove dot * Add SDL error to message --- LEGO1/omni/src/audio/mxsoundmanager.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/LEGO1/omni/src/audio/mxsoundmanager.cpp b/LEGO1/omni/src/audio/mxsoundmanager.cpp index afb9b21e..f865ebf0 100644 --- a/LEGO1/omni/src/audio/mxsoundmanager.cpp +++ b/LEGO1/omni/src/audio/mxsoundmanager.cpp @@ -9,6 +9,8 @@ #include "mxticklethread.h" #include "mxwavepresenter.h" +#include + DECOMP_SIZE_ASSERT(MxSoundManager, 0x3c); // GLOBAL LEGO1 0x10101420 @@ -98,12 +100,17 @@ MxResult MxSoundManager::Create(MxU32 p_frequencyMS, MxBool p_createThread) spec.format = SDL_AUDIO_F32; spec.channels = ma_engine_get_channels(m_engine); - if ((m_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, &AudioStreamCallback, this)) == + if ((m_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, &AudioStreamCallback, this)) != NULL) { - goto done; + SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(m_stream)); + } + else { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Failed to open default audio device for playback: %s", + SDL_GetError() + ); } - - SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(m_stream)); if (p_createThread) { m_thread = new MxTickleThread(this, p_frequencyMS); From 6942016a76de98cdfa4e55c2c86ef9ccd6cb2e89 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 16:23:58 -0700 Subject: [PATCH 04/22] Fix LegoPlantManager::CreatePlant (#425) --- LEGO1/lego/legoomni/src/common/legoplantmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp index 1444571f..5385a86a 100644 --- a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp @@ -214,7 +214,7 @@ LegoEntity* LegoPlantManager::CreatePlant(MxS32 p_index, LegoWorld* p_world, Leg { LegoEntity* entity = NULL; - if (p_index < sizeOfArray(g_plantInfo)) { + if (p_worldId != LegoOmni::e_undefined && p_index < sizeOfArray(g_plantInfo)) { MxU32 world = 1 << (MxU8) p_worldId; if (g_plantInfo[p_index].m_worlds & world && g_plantInfo[p_index].m_counter != 0) { From b65284a7a07c1be3d5e120980c5a2ac94e58c565 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Wed, 25 Jun 2025 19:45:26 +0200 Subject: [PATCH 05/22] Move 2D drawing and upscaling to the renderer (#348) --- ISLE/isleapp.cpp | 2 + .../legoomni/src/common/legotextureinfo.cpp | 4 +- LEGO1/lego/legoomni/src/common/legoutils.cpp | 2 +- .../src/common/mxtransitionmanager.cpp | 16 +- LEGO1/lego/legoomni/src/worlds/score.cpp | 2 +- LEGO1/lego/sources/3dmanager/lego3dview.cpp | 2 +- LEGO1/lego/sources/3dmanager/lego3dview.h | 2 +- LEGO1/lego/sources/misc/legocontainer.cpp | 4 +- LEGO1/mxdirectx/mxdirect3d.cpp | 4 +- LEGO1/mxdirectx/mxdirectdraw.cpp | 4 +- LEGO1/omni/src/video/mxdisplaysurface.cpp | 18 +- LEGO1/tgl/d3drm/impl.h | 8 +- LEGO1/tgl/d3drm/view.cpp | 8 +- LEGO1/tgl/tgl.h | 4 +- LEGO1/viewmanager/viewmanager.cpp | 2 +- LEGO1/viewmanager/viewmanager.h | 2 +- miniwin/CMakeLists.txt | 4 + miniwin/include/miniwin/ddraw.h | 9 +- .../src/d3drm/backends/directx9/actual.cpp | 325 ++++++++---- miniwin/src/d3drm/backends/directx9/actual.h | 8 +- .../src/d3drm/backends/directx9/renderer.cpp | 67 ++- .../src/d3drm/backends/opengl1/renderer.cpp | 248 +++++---- .../src/d3drm/backends/opengles2/renderer.cpp | 277 ++++++---- .../src/d3drm/backends/sdl3gpu/renderer.cpp | 476 ++++++++++++++---- .../shaders/generated/SolidColor.frag.h | 152 +++--- .../sdl3gpu/shaders/src/SolidColor.frag.hlsl | 5 +- .../src/d3drm/backends/software/renderer.cpp | 122 ++++- miniwin/src/d3drm/d3drm.cpp | 18 +- miniwin/src/d3drm/d3drmdevice.cpp | 65 ++- miniwin/src/d3drm/d3drmtexture.cpp | 13 +- miniwin/src/d3drm/d3drmviewport.cpp | 70 +-- miniwin/src/ddraw/ddraw.cpp | 38 +- miniwin/src/ddraw/ddsurface.cpp | 22 + miniwin/src/ddraw/framebuffer.cpp | 110 ++-- miniwin/src/internal/d3drmdevice_impl.h | 11 +- miniwin/src/internal/d3drmmesh_impl.h | 2 +- miniwin/src/internal/d3drmrenderer.h | 23 +- miniwin/src/internal/d3drmrenderer_directx9.h | 10 +- miniwin/src/internal/d3drmrenderer_opengl1.h | 17 +- .../src/internal/d3drmrenderer_opengles2.h | 27 +- miniwin/src/internal/d3drmrenderer_sdl3gpu.h | 29 +- miniwin/src/internal/d3drmrenderer_software.h | 15 +- miniwin/src/internal/d3drmtexture_impl.h | 4 +- miniwin/src/internal/d3drmviewport_impl.h | 11 +- miniwin/src/internal/ddraw_impl.h | 9 +- miniwin/src/internal/ddsurface_impl.h | 3 + miniwin/src/internal/framebuffer_impl.h | 10 +- miniwin/src/internal/structs.h | 6 + 48 files changed, 1551 insertions(+), 739 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index d1d1ec69..b097eb3b 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -394,6 +394,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) #endif switch (event->type) { + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: case SDL_EVENT_MOUSE_MOTION: case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: @@ -658,6 +659,7 @@ MxResult IsleApp::SetupWindow() SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, WINDOW_TITLE); #ifdef MINIWIN SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); #endif window = SDL_CreateWindowWithProperties(props); diff --git a/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp b/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp index 46dad6dc..eb639d84 100644 --- a/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp +++ b/LEGO1/lego/legoomni/src/common/legotextureinfo.cpp @@ -83,7 +83,7 @@ LegoTextureInfo* LegoTextureInfo::Create(const char* p_name, LegoTexture* p_text memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - if (textureInfo->m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR, NULL) != DD_OK) { + if (textureInfo->m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL) != DD_OK) { goto done; } @@ -193,7 +193,7 @@ LegoResult LegoTextureInfo::LoadBits(const LegoU8* p_bits) memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - if (m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR, NULL) == DD_OK) { + if (m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL) == DD_OK) { MxU8* surface = (MxU8*) desc.lpSurface; const LegoU8* bits = p_bits; diff --git a/LEGO1/lego/legoomni/src/common/legoutils.cpp b/LEGO1/lego/legoomni/src/common/legoutils.cpp index 26948049..9d3f3c7d 100644 --- a/LEGO1/lego/legoomni/src/common/legoutils.cpp +++ b/LEGO1/lego/legoomni/src/common/legoutils.cpp @@ -708,7 +708,7 @@ void WriteDefaultTexture(LegoStorage* p_storage, const char* p_name) memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - if (surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR, NULL) == DD_OK) { + if (surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL) == DD_OK) { LegoImage* image = new LegoImage(desc.dwWidth, desc.dwHeight); if (image != NULL) { diff --git a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp index 6cd73584..b251c1c0 100644 --- a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp @@ -210,10 +210,10 @@ void MxTransitionManager::DissolveTransition() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (res == DDERR_SURFACELOST) { m_ddSurface->Restore(); - res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (res == DD_OK) { @@ -402,10 +402,10 @@ void MxTransitionManager::WipeDownTransition() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (res == DDERR_SURFACELOST) { m_ddSurface->Restore(); - res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (res == DD_OK) { @@ -439,10 +439,10 @@ void MxTransitionManager::WindowsTransition() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (res == DDERR_SURFACELOST) { m_ddSurface->Restore(); - res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (res == DD_OK) { @@ -483,10 +483,10 @@ void MxTransitionManager::BrokenTransition() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (res == DDERR_SURFACELOST) { m_ddSurface->Restore(); - res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + res = m_ddSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (res == DD_OK) { diff --git a/LEGO1/lego/legoomni/src/worlds/score.cpp b/LEGO1/lego/legoomni/src/worlds/score.cpp index ebeca836..69b4305a 100644 --- a/LEGO1/lego/legoomni/src/worlds/score.cpp +++ b/LEGO1/lego/legoomni/src/worlds/score.cpp @@ -254,7 +254,7 @@ void Score::Paint() memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - HRESULT result = cube->m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR, NULL); + HRESULT result = cube->m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL); if (result == DD_OK) { if (desc.lPitch != desc.dwWidth) { cube->m_surface->Unlock(desc.lpSurface); diff --git a/LEGO1/lego/sources/3dmanager/lego3dview.cpp b/LEGO1/lego/sources/3dmanager/lego3dview.cpp index 0a4779a4..1cc272e1 100644 --- a/LEGO1/lego/sources/3dmanager/lego3dview.cpp +++ b/LEGO1/lego/sources/3dmanager/lego3dview.cpp @@ -154,7 +154,7 @@ double Lego3DView::Render(double p_und) } // FUNCTION: LEGO1 0x100ab2b0 -ViewROI* Lego3DView::Pick(unsigned int x, unsigned int y) +ViewROI* Lego3DView::Pick(int x, int y) { return m_pViewManager->Pick(GetView(), x, y); } diff --git a/LEGO1/lego/sources/3dmanager/lego3dview.h b/LEGO1/lego/sources/3dmanager/lego3dview.h index baec8f43..4441b85e 100644 --- a/LEGO1/lego/sources/3dmanager/lego3dview.h +++ b/LEGO1/lego/sources/3dmanager/lego3dview.h @@ -27,7 +27,7 @@ class Lego3DView : public LegoView1 { double Render(double p_und); - ViewROI* Pick(unsigned int x, unsigned int y); + ViewROI* Pick(int x, int y); ViewROI* GetPointOfView(); ViewManager* GetViewManager(); diff --git a/LEGO1/lego/sources/misc/legocontainer.cpp b/LEGO1/lego/sources/misc/legocontainer.cpp index 6928a5c3..98836d6a 100644 --- a/LEGO1/lego/sources/misc/legocontainer.cpp +++ b/LEGO1/lego/sources/misc/legocontainer.cpp @@ -22,7 +22,7 @@ LegoTextureInfo* LegoTextureContainer::GetCached(LegoTextureInfo* p_textureInfo) memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - if (p_textureInfo->m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR, NULL) == DD_OK) { + if (p_textureInfo->m_surface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL) == DD_OK) { width = desc.dwWidth; height = desc.dwHeight; p_textureInfo->m_surface->Unlock(desc.lpSurface); @@ -35,7 +35,7 @@ LegoTextureInfo* LegoTextureContainer::GetCached(LegoTextureInfo* p_textureInfo) memset(&newDesc, 0, sizeof(newDesc)); newDesc.dwSize = sizeof(newDesc); - if (surface->Lock(NULL, &newDesc, DDLOCK_SURFACEMEMORYPTR, NULL) == DD_OK) { + if (surface->Lock(NULL, &newDesc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL) == DD_OK) { BOOL und = FALSE; if (newDesc.dwWidth == width && newDesc.dwHeight == height) { und = TRUE; diff --git a/LEGO1/mxdirectx/mxdirect3d.cpp b/LEGO1/mxdirectx/mxdirect3d.cpp index 9ce37cf5..d6043a2c 100644 --- a/LEGO1/mxdirectx/mxdirect3d.cpp +++ b/LEGO1/mxdirectx/mxdirect3d.cpp @@ -174,7 +174,7 @@ BOOL MxDirect3D::D3DSetMode() memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - if (backBuffer->Lock(NULL, &desc, DDLOCK_WAIT, NULL) == DD_OK) { + if (backBuffer->Lock(NULL, &desc, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL) == DD_OK) { unsigned char* surface = (unsigned char*) desc.lpSurface; for (int i = 0; i < mode.height; i++) { @@ -192,7 +192,7 @@ BOOL MxDirect3D::D3DSetMode() memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); - if (frontBuffer->Lock(NULL, &desc, DDLOCK_WAIT, NULL) == DD_OK) { + if (frontBuffer->Lock(NULL, &desc, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL) == DD_OK) { unsigned char* surface = (unsigned char*) desc.lpSurface; for (int i = 0; i < mode.height; i++) { diff --git a/LEGO1/mxdirectx/mxdirectdraw.cpp b/LEGO1/mxdirectx/mxdirectdraw.cpp index 6f3dfd78..104b6836 100644 --- a/LEGO1/mxdirectx/mxdirectdraw.cpp +++ b/LEGO1/mxdirectx/mxdirectdraw.cpp @@ -539,10 +539,10 @@ void MxDirectDraw::ClearBackBuffers() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - result = m_pBackBuffer->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + result = m_pBackBuffer->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (result == DDERR_SURFACELOST) { m_pBackBuffer->Restore(); - result = m_pBackBuffer->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + result = m_pBackBuffer->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (result != DD_OK) { diff --git a/LEGO1/omni/src/video/mxdisplaysurface.cpp b/LEGO1/omni/src/video/mxdisplaysurface.cpp index 9b7d321e..6c93a0e7 100644 --- a/LEGO1/omni/src/video/mxdisplaysurface.cpp +++ b/LEGO1/omni/src/video/mxdisplaysurface.cpp @@ -402,10 +402,10 @@ void MxDisplaySurface::VTable0x28( memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (hr == DDERR_SURFACELOST) { m_ddSurface2->Restore(); - hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (hr != DD_OK) { @@ -514,10 +514,10 @@ void MxDisplaySurface::VTable0x30( memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - HRESULT hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + HRESULT hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (hr == DDERR_SURFACELOST) { m_ddSurface2->Restore(); - hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL); + hr = m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (hr != DD_OK) { @@ -726,11 +726,11 @@ void MxDisplaySurface::VTable0x34(MxU8* p_pixels, MxS32 p_bpp, MxS32 p_width, Mx memset(&surfaceDesc, 0, sizeof(surfaceDesc)); surfaceDesc.dwSize = sizeof(surfaceDesc); - HRESULT result = m_ddSurface2->Lock(NULL, &surfaceDesc, DDLOCK_WAIT, NULL); + HRESULT result = m_ddSurface2->Lock(NULL, &surfaceDesc, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); if (result == DDERR_SURFACELOST) { m_ddSurface2->Restore(); - result = m_ddSurface2->Lock(NULL, &surfaceDesc, DDLOCK_WAIT, NULL); + result = m_ddSurface2->Lock(NULL, &surfaceDesc, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); } if (result == DD_OK) { @@ -784,7 +784,7 @@ void MxDisplaySurface::Display(MxS32 p_left, MxS32 p_top, MxS32 p_left2, MxS32 p DDSURFACEDESC ddsd; memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - if (m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) { + if (m_ddSurface2->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL) == DD_OK) { MxU8* surface = (MxU8*) ddsd.lpSurface; MxS32 height = m_videoParam.GetRect().GetHeight(); @@ -891,7 +891,7 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::VTable0x44( memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - if (surface->Lock(NULL, &ddsd, DDLOCK_WAIT, 0) != DD_OK) { + if (surface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, 0) != DD_OK) { surface->Release(); surface = NULL; } @@ -1067,7 +1067,7 @@ LPDIRECTDRAWSURFACE MxDisplaySurface::CreateCursorSurface() memset(&ddsd, 0, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); - if (newSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK) { + if (newSurface->Lock(NULL, &ddsd, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL) != DD_OK) { goto done; } else { diff --git a/LEGO1/tgl/d3drm/impl.h b/LEGO1/tgl/d3drm/impl.h index 5dccba8b..4c834521 100644 --- a/LEGO1/tgl/d3drm/impl.h +++ b/LEGO1/tgl/d3drm/impl.h @@ -252,8 +252,8 @@ class ViewImpl : public View { Result TransformWorldToScreen(const float world[3], float screen[4]) override; Result TransformScreenToWorld(const float screen[4], float world[3]) override; Result Pick( - unsigned int x, - unsigned int y, + int x, + int y, const Group** ppGroupsToPickFrom, int groupsToPickFromCount, const Group**& rppPickedGroups, @@ -277,8 +277,8 @@ class ViewImpl : public View { Result SetCamera(const CameraImpl& rCamera); Result Render(const GroupImpl& rScene); Result Pick( - unsigned int x, - unsigned int y, + int x, + int y, const GroupImpl** ppGroupsToPickFrom, int groupsToPickFromCount, const Group**& rppPickedGroups, diff --git a/LEGO1/tgl/d3drm/view.cpp b/LEGO1/tgl/d3drm/view.cpp index 4a000178..92e5e107 100644 --- a/LEGO1/tgl/d3drm/view.cpp +++ b/LEGO1/tgl/d3drm/view.cpp @@ -495,8 +495,8 @@ Result ViewImpl::ForceUpdate(unsigned int x, unsigned int y, unsigned int width, // FUNCTION: BETA10 0x101710f0 inline Result ViewImpl::Pick( - unsigned int x, - unsigned int y, + int x, + int y, const GroupImpl** ppGroupsToPickFrom, int groupsToPickFromCount, const Group**& rppPickedGroups, @@ -519,8 +519,8 @@ inline Result ViewImpl::Pick( // FUNCTION: LEGO1 0x100a30c0 // FUNCTION: BETA10 0x1016ee10 Result ViewImpl::Pick( - unsigned int x, - unsigned int y, + int x, + int y, const Group** ppGroupsToPickFrom, int groupsToPickFromCount, const Group**& rppPickedGroups, diff --git a/LEGO1/tgl/tgl.h b/LEGO1/tgl/tgl.h index aabe6c96..2f8ab645 100644 --- a/LEGO1/tgl/tgl.h +++ b/LEGO1/tgl/tgl.h @@ -257,8 +257,8 @@ class View : public Object { // output parameter // size of rppPickedGroups virtual Result Pick( - unsigned int x, - unsigned int y, + int x, + int y, const Group** ppGroupsToPickFrom, int groupsToPickFromCount, const Group**& rppPickedGroups, diff --git a/LEGO1/viewmanager/viewmanager.cpp b/LEGO1/viewmanager/viewmanager.cpp index 721ec247..9ec2da58 100644 --- a/LEGO1/viewmanager/viewmanager.cpp +++ b/LEGO1/viewmanager/viewmanager.cpp @@ -499,7 +499,7 @@ float ViewManager::ProjectedSize(const BoundingSphere& p_bounding_sphere) } // FUNCTION: LEGO1 0x100a6e00 -ViewROI* ViewManager::Pick(Tgl::View* p_view, unsigned int x, unsigned int y) +ViewROI* ViewManager::Pick(Tgl::View* p_view, int x, int y) { LPDIRECT3DRMPICKEDARRAY picked = NULL; ViewROI* result = NULL; diff --git a/LEGO1/viewmanager/viewmanager.h b/LEGO1/viewmanager/viewmanager.h index 2fdb9afb..ee068658 100644 --- a/LEGO1/viewmanager/viewmanager.h +++ b/LEGO1/viewmanager/viewmanager.h @@ -33,7 +33,7 @@ class ViewManager { void RemoveROIDetailFromScene(ViewROI* p_roi); void SetPOVSource(const OrientableROI* point_of_view); float ProjectedSize(const BoundingSphere& p_bounding_sphere); - ViewROI* Pick(Tgl::View* p_view, unsigned int x, unsigned int y); + ViewROI* Pick(Tgl::View* p_view, int x, int y); void SetResolution(int width, int height); void SetFrustrum(float fov, float front, float back); inline void ManageVisibilityAndDetailRecursively(ViewROI* p_from, int p_lodLevel); diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index 5017f456..a1939441 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -27,6 +27,10 @@ add_library(miniwin STATIC EXCLUDE_FROM_ALL src/d3drm/backends/software/renderer.cpp ) +target_compile_definitions(miniwin PRIVATE + $<$:DEBUG> +) + find_package(OpenGL) find_package(GLEW) if(OpenGL_FOUND AND GLEW_FOUND) diff --git a/miniwin/include/miniwin/ddraw.h b/miniwin/include/miniwin/ddraw.h index 55a95f8f..1e3fd17f 100644 --- a/miniwin/include/miniwin/ddraw.h +++ b/miniwin/include/miniwin/ddraw.h @@ -159,12 +159,15 @@ enum class DDBltFastFlags : uint32_t { }; ENABLE_BITMASK_OPERATORS(DDBltFastFlags) -#define DDLOCK_WAIT DDLockFlags::WAIT #define DDLOCK_SURFACEMEMORYPTR DDLockFlags::SURFACEMEMORYPTR +#define DDLOCK_WAIT DDLockFlags::WAIT +#define DDLOCK_WRITEONLY DDLockFlags::WRITEONLY enum class DDLockFlags : uint32_t { - SURFACEMEMORYPTR, - WAIT, + SURFACEMEMORYPTR = 0, + WAIT = 1 << 0, + WRITEONLY = 1 << 5, }; +ENABLE_BITMASK_OPERATORS(DDLockFlags) #define DDSCL_FULLSCREEN DDSCLFlags::FULLSCREEN #define DDSCL_ALLOWREBOOT DDSCLFlags::ALLOWREBOOT diff --git a/miniwin/src/d3drm/backends/directx9/actual.cpp b/miniwin/src/d3drm/backends/directx9/actual.cpp index 30a3360c..33dc2864 100644 --- a/miniwin/src/d3drm/backends/directx9/actual.cpp +++ b/miniwin/src/d3drm/backends/directx9/actual.cpp @@ -11,19 +11,22 @@ 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; +static int g_width; +static int g_height; +static int g_virtualWidth; +static int g_virtualHeight; +static bool g_hasScene = false; +static std::vector g_lights; +static Matrix4x4 g_projection; +static ViewportTransform g_viewportTransform; bool Actual_Initialize(void* hwnd, int width, int height) { g_hwnd = (HWND) hwnd; - m_width = width; - m_height = height; + g_width = width; + g_height = height; + g_virtualWidth = width; + g_virtualHeight = height; g_d3d = Direct3DCreate9(D3D_SDK_VERSION); if (!g_d3d) { return false; @@ -52,45 +55,17 @@ bool Actual_Initialize(void* hwnd, int width, int height) 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; @@ -103,12 +78,12 @@ void Actual_Shutdown() void Actual_PushLights(const BridgeSceneLight* lightsArray, size_t count) { - m_lights.assign(lightsArray, lightsArray + count); + g_lights.assign(lightsArray, lightsArray + count); } void Actual_SetProjection(const Matrix4x4* projection, float front, float back) { - memcpy(&m_projection, projection, sizeof(Matrix4x4)); + memcpy(&g_projection, projection, sizeof(Matrix4x4)); } IDirect3DTexture9* UploadSurfaceToD3DTexture(SDL_Surface* surface) @@ -188,18 +163,71 @@ void UploadMeshBuffers( cache.ibo->Unlock(); } -constexpr float PI = 3.14159265358979323846f; - -void SetupLights() +void Actual_Resize(int width, int height, const ViewportTransform& viewportTransform) { + g_width = width; + g_height = height; + g_viewportTransform = viewportTransform; + + 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; + g_device->Reset(&pp); +} + +void StartScene() +{ + if (g_hasScene) { + return; + } + g_device->BeginScene(); + g_hasScene = true; +} + +void Actual_Clear(float r, float g, float b) +{ + StartScene(); + + g_device->Clear( + 0, + nullptr, + D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, + D3DCOLOR_ARGB(255, static_cast(r * 255), static_cast(g * 255), static_cast(b * 255)), + 1.0f, + 0 + ); +} + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +uint32_t Actual_BeginFrame() +{ + StartScene(); + g_device->SetRenderState(D3DRS_LIGHTING, TRUE); + g_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); + g_device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + g_device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + + g_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + g_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + g_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); + for (DWORD i = 0; i < 8; ++i) { g_device->LightEnable(i, FALSE); } DWORD lightIdx = 0; - for (const auto& l : m_lights) { + for (const auto& l : g_lights) { if (lightIdx >= 8) { break; } @@ -235,30 +263,16 @@ void SetupLights() } light.Falloff = 1.0f; - light.Phi = PI; - light.Theta = PI / 2; + light.Phi = M_PI; + light.Theta = M_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(); + g_device->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1); return D3D_OK; } @@ -286,15 +300,19 @@ D3DMATRIX ToD3DMATRIX(const Matrix4x4& in) void Actual_SubmitDraw( const D3D9MeshCacheEntry* mesh, const Matrix4x4* modelViewMatrix, + const Matrix4x4* worldMatrix, + const Matrix4x4* viewMatrix, const Matrix3x3* normalMatrix, const Appearance* appearance, IDirect3DTexture9* texture ) { - D3DMATRIX worldView = ToD3DMATRIX(*modelViewMatrix); - g_device->SetTransform(D3DTS_WORLD, &worldView); - D3DMATRIX proj = ToD3DMATRIX(m_projection); + D3DMATRIX proj = ToD3DMATRIX(g_projection); g_device->SetTransform(D3DTS_PROJECTION, &proj); + D3DMATRIX view = ToD3DMATRIX(*viewMatrix); + g_device->SetTransform(D3DTS_VIEW, &view); + D3DMATRIX world = ToD3DMATRIX(*worldMatrix); + g_device->SetTransform(D3DTS_WORLD, &world); D3DMATERIAL9 mat = {}; mat.Diffuse.r = appearance->color.r / 255.0f; @@ -342,44 +360,149 @@ void Actual_SubmitDraw( g_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mesh->vertexCount, 0, mesh->indexCount / 3); } -uint32_t Actual_FinalizeFrame(void* pixels, int pitch) +uint32_t Actual_Flip() { 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; + g_hasScene = false; + return g_device->Present(nullptr, nullptr, nullptr, nullptr); +} + +void Actual_Draw2DImage(IDirect3DTexture9* texture, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + StartScene(); + + float left = -g_viewportTransform.offsetX / g_viewportTransform.scale; + float right = (g_width - g_viewportTransform.offsetX) / g_viewportTransform.scale; + float top = -g_viewportTransform.offsetY / g_viewportTransform.scale; + float bottom = (g_height - g_viewportTransform.offsetY) / g_viewportTransform.scale; + + auto virtualToScreenX = [&](float x) { return ((x - left) / (right - left)) * g_width; }; + auto virtualToScreenY = [&](float y) { return ((y - top) / (bottom - top)) * g_height; }; + + float x1_virtual = static_cast(dstRect.x); + float y1_virtual = static_cast(dstRect.y); + float x2_virtual = x1_virtual + dstRect.w; + float y2_virtual = y1_virtual + dstRect.h; + + float x1 = virtualToScreenX(x1_virtual); + float y1 = virtualToScreenY(y1_virtual); + float x2 = virtualToScreenX(x2_virtual); + float y2 = virtualToScreenY(y2_virtual); + + D3DMATRIX identity = + {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; + g_device->SetTransform(D3DTS_PROJECTION, &identity); + g_device->SetTransform(D3DTS_VIEW, &identity); + g_device->SetTransform(D3DTS_WORLD, &identity); + + g_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + g_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + + g_device->SetRenderState(D3DRS_ZENABLE, FALSE); + g_device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + g_device->SetRenderState(D3DRS_LIGHTING, FALSE); + g_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + g_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + g_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + 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); + + D3DSURFACE_DESC texDesc; + texture->GetLevelDesc(0, &texDesc); + float texW = static_cast(texDesc.Width); + float texH = static_cast(texDesc.Height); + + float u1 = static_cast(srcRect.x) / texW; + float v1 = static_cast(srcRect.y) / texH; + float u2 = static_cast(srcRect.x + srcRect.w) / texW; + float v2 = static_cast(srcRect.y + srcRect.h) / texH; + + struct Vertex { + float x, y, z, rhw; + float u, v; + }; + + Vertex quad[4] = { + {x1, y1, 0.0f, 1.0f, u1, v1}, + {x2, y1, 0.0f, 1.0f, u2, v1}, + {x2, y2, 0.0f, 1.0f, u2, v2}, + {x1, y2, 0.0f, 1.0f, u1, v2}, + }; + + g_device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1); + g_device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(Vertex)); +} + +uint32_t Actual_Download(SDL_Surface* target) +{ + IDirect3DSurface9* backBuffer; + HRESULT hr = g_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer); + if (FAILED(hr)) { + return hr; + } + + IDirect3DSurface9* sysMemSurface; + hr = g_device->CreateOffscreenPlainSurface( + g_width, + g_height, + D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, + &sysMemSurface, + nullptr + ); + if (FAILED(hr)) { + backBuffer->Release(); + return hr; + } + + hr = g_device->GetRenderTargetData(backBuffer, sysMemSurface); + backBuffer->Release(); + if (FAILED(hr)) { + sysMemSurface->Release(); + return hr; + } + + D3DLOCKED_RECT locked; + hr = sysMemSurface->LockRect(&locked, nullptr, D3DLOCK_READONLY); + if (FAILED(hr)) { + sysMemSurface->Release(); + return hr; + } + + SDL_Surface* srcSurface = + SDL_CreateSurfaceFrom(g_width, g_height, SDL_PIXELFORMAT_ARGB8888, locked.pBits, locked.Pitch); + if (!srcSurface) { + sysMemSurface->UnlockRect(); + sysMemSurface->Release(); + return E_FAIL; + } + + float srcAspect = static_cast(g_width) / g_height; + float dstAspect = static_cast(target->w) / target->h; + + SDL_Rect srcRect; + if (srcAspect > dstAspect) { + int cropWidth = static_cast(g_height * dstAspect); + srcRect = {(g_width - cropWidth) / 2, 0, cropWidth, g_height}; + } + else { + int cropHeight = static_cast(g_width / dstAspect); + srcRect = {0, (g_height - cropHeight) / 2, g_width, cropHeight}; + } + + if (SDL_BlitSurfaceScaled(srcSurface, &srcRect, target, nullptr, SDL_SCALEMODE_NEAREST)) { + SDL_DestroySurface(srcSurface); + sysMemSurface->UnlockRect(); + sysMemSurface->Release(); + return E_FAIL; + } + + SDL_DestroySurface(srcSurface); + sysMemSurface->UnlockRect(); + sysMemSurface->Release(); + + return D3D_OK; } diff --git a/miniwin/src/d3drm/backends/directx9/actual.h b/miniwin/src/d3drm/backends/directx9/actual.h index 9080878d..e3369ec9 100644 --- a/miniwin/src/d3drm/backends/directx9/actual.h +++ b/miniwin/src/d3drm/backends/directx9/actual.h @@ -69,8 +69,14 @@ void Actual_EnableTransparency(); void Actual_SubmitDraw( const D3D9MeshCacheEntry* mesh, const Matrix4x4* modelViewMatrix, + const Matrix4x4* worldMatrix, + const Matrix4x4* viewMatrix, const Matrix3x3* normalMatrix, const Appearance* appearance, IDirect3DTexture9* texture ); -uint32_t Actual_FinalizeFrame(void* pixels, int pitch); +void Actual_Resize(int width, int height, const ViewportTransform& viewportTransform); +void Actual_Clear(float r, float g, float b); +uint32_t Actual_Flip(); +void Actual_Draw2DImage(IDirect3DTexture9* texture, const SDL_Rect& srcRect, const SDL_Rect& dstRect); +uint32_t Actual_Download(SDL_Surface* target); diff --git a/miniwin/src/d3drm/backends/directx9/renderer.cpp b/miniwin/src/d3drm/backends/directx9/renderer.cpp index f0207041..574939f5 100644 --- a/miniwin/src/d3drm/backends/directx9/renderer.cpp +++ b/miniwin/src/d3drm/backends/directx9/renderer.cpp @@ -20,14 +20,18 @@ Direct3DRMRenderer* DirectX9Renderer::Create(DWORD width, DWORD height) return new DirectX9Renderer(width, height); } -DirectX9Renderer::DirectX9Renderer(DWORD width, DWORD height) : m_width(width), m_height(height) +DirectX9Renderer::DirectX9Renderer(DWORD width, DWORD height) { + m_width = width; + m_height = height; + m_virtualWidth = width; + m_virtualHeight = 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); + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); } DirectX9Renderer::~DirectX9Renderer() @@ -212,16 +216,6 @@ Uint32 DirectX9Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshG 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; @@ -253,6 +247,8 @@ void DirectX9Renderer::EnableTransparency() void DirectX9Renderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) @@ -261,17 +257,46 @@ void DirectX9Renderer::SubmitDraw( if (appearance.textureId != NO_TEXTURE_ID) { texture = m_textures[appearance.textureId].dxTexture; } - Actual_SubmitDraw(&m_meshs[meshId], &modelViewMatrix, &normalMatrix, &appearance, texture); + Actual_SubmitDraw( + &m_meshs[meshId], + &modelViewMatrix, + &worldMatrix, + &viewMatrix, + &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; + return DD_OK; +} + +void DirectX9Renderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + m_width = width; + m_height = height; + m_viewportTransform = viewportTransform; + Actual_Resize(width, height, viewportTransform); +} + +void DirectX9Renderer::Clear(float r, float g, float b) +{ + Actual_Clear(r, g, b); +} + +void DirectX9Renderer::Flip() +{ + Actual_Flip(); +} + +void DirectX9Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + Actual_Draw2DImage(m_textures[textureId].dxTexture, srcRect, dstRect); +} + +void DirectX9Renderer::Download(SDL_Surface* target) +{ + Actual_Download(target); } diff --git a/miniwin/src/d3drm/backends/opengl1/renderer.cpp b/miniwin/src/d3drm/backends/opengl1/renderer.cpp index 815d854b..cc415de8 100644 --- a/miniwin/src/d3drm/backends/opengl1/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengl1/renderer.cpp @@ -15,17 +15,24 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_Window* window = DDWindow; bool testWindow = false; if (!window) { - window = SDL_CreateWindow("OpenGL 1.2 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); + window = SDL_CreateWindow("OpenGL 1.1 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); + if (!window) { + SDL_Log("SDL_CreateWindow: %s", SDL_GetError()); + return nullptr; + } testWindow = true; } + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GLContext context = SDL_GL_CreateContext(window); if (!context) { + SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError()); if (testWindow) { SDL_DestroyWindow(window); } @@ -33,18 +40,16 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) } if (!SDL_GL_MakeCurrent(window, context)) { - return nullptr; - } - - GLenum err = glewInit(); - if (err != GLEW_OK) { + SDL_Log("SDL_GL_MakeCurrent: %s", SDL_GetError()); if (testWindow) { SDL_DestroyWindow(window); } return nullptr; } - if (!GLEW_EXT_framebuffer_object) { + GLenum err = glewInit(); + if (err != GLEW_OK) { + SDL_Log("glewInit: %s", glewGetErrorString(err)); if (testWindow) { SDL_DestroyWindow(window); } @@ -53,65 +58,37 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) glEnable(GL_CULL_FACE); glCullFace(GL_BACK); - glFrontFace(GL_CCW); - - // Setup FBO - GLuint fbo; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - - // Create color texture - GLuint colorTex; - glGenTextures(1, &colorTex); - glBindTexture(GL_TEXTURE_2D, colorTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0); - - // Create depth renderbuffer - GLuint depthRb; - glGenRenderbuffers(1, &depthRb); - glBindRenderbuffer(GL_RENDERBUFFER, depthRb); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRb); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return nullptr; - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glFrontFace(GL_CW); if (testWindow) { SDL_DestroyWindow(window); } - return new OpenGL1Renderer(width, height, context, fbo, colorTex, depthRb); + return new OpenGL1Renderer(width, height, context); } -OpenGL1Renderer::OpenGL1Renderer( - DWORD width, - DWORD height, - SDL_GLContext context, - GLuint fbo, - GLuint colorTex, - GLuint depthRb -) - : m_width(width), m_height(height), m_context(context), m_fbo(fbo), m_colorTex(colorTex), m_depthRb(depthRb) +OpenGL1Renderer::OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext context) : m_context(context) { - m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_ABGR8888); + m_width = width; + m_height = height; + m_virtualWidth = width; + m_virtualHeight = height; + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); m_useVBOs = GLEW_ARB_vertex_buffer_object; } OpenGL1Renderer::~OpenGL1Renderer() { SDL_DestroySurface(m_renderedImage); - glDeleteFramebuffers(1, &m_fbo); - glDeleteRenderbuffers(1, &m_depthRb); - glDeleteTextures(1, &m_colorTex); } void OpenGL1Renderer::PushLights(const SceneLight* lightsArray, size_t count) { + if (count > 8) { + SDL_Log("Unsupported number of lights (%d)", static_cast(count)); + count = 8; + } + m_lights.assign(lightsArray, lightsArray + count); } @@ -122,7 +99,6 @@ void OpenGL1Renderer::SetFrustumPlanes(const Plane* frustumPlanes) void OpenGL1Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); - m_projection[1][1] *= -1.0f; // OpenGL is upside down } struct TextureDestroyContextGL { @@ -161,14 +137,12 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) glGenTextures(1, &tex.glTextureId); glBindTexture(GL_TEXTURE_2D, tex.glTextureId); - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_ABGR8888); + SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); if (!surf) { return NO_TEXTURE_ID; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); SDL_DestroySurface(surf); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); tex.version = texture->m_version; } @@ -180,14 +154,12 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_ABGR8888); + SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); if (!surf) { return NO_TEXTURE_ID; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); SDL_DestroySurface(surf); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; @@ -260,7 +232,7 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup, bool useVBOs) glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); glBufferData(GL_ARRAY_BUFFER, cache.normals.size() * sizeof(D3DVECTOR), cache.normals.data(), GL_STATIC_DRAW); - if (meshGroup.texture != nullptr) { + if (meshGroup.texture) { glGenBuffers(1, &cache.vboTexcoords); glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); glBufferData( @@ -337,16 +309,6 @@ Uint32 OpenGL1Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGr return (Uint32) (m_meshs.size() - 1); } -DWORD OpenGL1Renderer::GetWidth() -{ - return m_width; -} - -DWORD OpenGL1Renderer::GetHeight() -{ - return m_height; -} - void OpenGL1Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { halDesc->dcmColorModel = D3DCOLORMODEL::RGB; @@ -362,14 +324,12 @@ void OpenGL1Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) const char* OpenGL1Renderer::GetName() { - return "OpenGL 1.2 HAL"; + return "OpenGL 1.1 HAL"; } HRESULT OpenGL1Renderer::BeginFrame() { - SDL_GL_MakeCurrent(DDWindow, m_context); - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); - glViewport(0, 0, m_width, m_height); + m_dirty = true; glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); @@ -378,16 +338,12 @@ HRESULT OpenGL1Renderer::BeginFrame() glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Disable all lights and reset global ambient for (int i = 0; i < 8; ++i) { glDisable(GL_LIGHT0 + i); } const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient); - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); // Setup lights @@ -462,15 +418,14 @@ void OpenGL1Renderer::EnableTransparency() void OpenGL1Renderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) { auto& mesh = m_meshs[meshId]; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadMatrixf(&modelViewMatrix[0][0]); glEnable(GL_NORMALIZE); @@ -496,6 +451,8 @@ void OpenGL1Renderer::SubmitDraw( // Bind texture if present if (appearance.textureId != NO_TEXTURE_ID) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); auto& tex = m_textures[appearance.textureId]; glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tex.glTextureId); @@ -542,11 +499,130 @@ void OpenGL1Renderer::SubmitDraw( HRESULT OpenGL1Renderer::FinalizeFrame() { - glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // Composite onto SDL backbuffer - SDL_BlitSurface(m_renderedImage, nullptr, DDBackBuffer, nullptr); - return DD_OK; } + +void OpenGL1Renderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + m_width = width; + m_height = height; + m_viewportTransform = viewportTransform; + SDL_DestroySurface(m_renderedImage); + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); + glViewport(0, 0, m_width, m_height); +} + +void OpenGL1Renderer::Clear(float r, float g, float b) +{ + m_dirty = true; + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glClearColor(r, g, b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void OpenGL1Renderer::Flip() +{ + if (m_dirty) { + SDL_GL_SwapWindow(DDWindow); + m_dirty = false; + } +} + +void OpenGL1Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + m_dirty = true; + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; + float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale; + float top = -m_viewportTransform.offsetY / m_viewportTransform.scale; + float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale; + glOrtho(left, right, bottom, top, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_textures[textureId].glTextureId); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + GLint boundTexture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); + + GLfloat texW, texH; + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texW); + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texH); + + float u1 = srcRect.x / texW; + float v1 = srcRect.y / texH; + float u2 = (srcRect.x + srcRect.w) / texW; + float v2 = (srcRect.y + srcRect.h) / texH; + + float x1 = (float) dstRect.x; + float y1 = (float) dstRect.y; + float x2 = x1 + dstRect.w; + float y2 = y1 + dstRect.h; + + glBegin(GL_QUADS); + glTexCoord2f(u1, v1); + glVertex2f(x1, y1); + glTexCoord2f(u2, v1); + glVertex2f(x2, y1); + glTexCoord2f(u2, v2); + glVertex2f(x2, y2); + glTexCoord2f(u1, v2); + glVertex2f(x1, y2); + glEnd(); + + // Restore state + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); +} + +void OpenGL1Renderer::Download(SDL_Surface* target) +{ + glFinish(); + glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); + + SDL_Rect srcRect = { + static_cast(m_viewportTransform.offsetX), + static_cast(m_viewportTransform.offsetY), + static_cast(target->w * m_viewportTransform.scale), + static_cast(target->h * m_viewportTransform.scale), + }; + + SDL_Surface* bufferClone = SDL_CreateSurface(target->w, target->h, SDL_PIXELFORMAT_RGBA32); + if (!bufferClone) { + SDL_Log("SDL_CreateSurface: %s", SDL_GetError()); + return; + } + + SDL_BlitSurfaceScaled(m_renderedImage, &srcRect, bufferClone, nullptr, SDL_SCALEMODE_NEAREST); + + // Flip image vertically into target + SDL_Rect rowSrc = {0, 0, bufferClone->w, 1}; + SDL_Rect rowDst = {0, 0, bufferClone->w, 1}; + for (int y = 0; y < bufferClone->h; ++y) { + rowSrc.y = y; + rowDst.y = bufferClone->h - 1 - y; + SDL_BlitSurface(bufferClone, &rowSrc, target, &rowDst); + } + + SDL_DestroySurface(bufferClone); +} diff --git a/miniwin/src/d3drm/backends/opengles2/renderer.cpp b/miniwin/src/d3drm/backends/opengles2/renderer.cpp index 94229cc7..3dbabda2 100644 --- a/miniwin/src/d3drm/backends/opengles2/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengles2/renderer.cpp @@ -41,6 +41,8 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) testWindow = true; } + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GLContext context = SDL_GL_CreateContext(window); if (!context) { if (testWindow) { @@ -50,51 +52,16 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) } if (!SDL_GL_MakeCurrent(window, context)) { + if (testWindow) { + SDL_DestroyWindow(window); + } return nullptr; } glDepthFunc(GL_LEQUAL); - glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); - glFrontFace(GL_CCW); - - // Setup FBO - GLuint fbo; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - - // Create color texture - GLuint colorTex; - glGenTextures(1, &colorTex); - glBindTexture(GL_TEXTURE_2D, colorTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0); - - // Create depth renderbuffer - GLuint depthRb; - glGenRenderbuffers(1, &depthRb); - glBindRenderbuffer(GL_RENDERBUFFER, depthRb); - const char* extensions = (const char*) glGetString(GL_EXTENSIONS); - if (extensions) { - if (strstr(extensions, "GL_OES_depth24")) { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, width, height); - } - else if (strstr(extensions, "GL_OES_depth32")) { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32_OES, width, height); - } - } - else { - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); - } - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRb); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return nullptr; - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glFrontFace(GL_CW); const char* vertexShaderSource = R"( attribute vec3 a_position; @@ -183,6 +150,7 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) if (u_useTexture != 0) { vec4 texel = texture2D(u_texture, v_texCoord); finalColor.rgb = clamp(texel.rgb * finalColor.rgb, 0.0, 1.0); + finalColor.a = texel.a; } gl_FragColor = finalColor; @@ -206,31 +174,23 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) SDL_DestroyWindow(window); } - return new OpenGLES2Renderer(width, height, context, fbo, colorTex, depthRb, shaderProgram); + return new OpenGLES2Renderer(width, height, context, shaderProgram); } -OpenGLES2Renderer::OpenGLES2Renderer( - DWORD width, - DWORD height, - SDL_GLContext context, - GLuint fbo, - GLuint colorTex, - GLuint depthRb, - GLuint shaderProgram -) - : m_width(width), m_height(height), m_context(context), m_fbo(fbo), m_colorTex(colorTex), m_depthRb(depthRb), - m_shaderProgram(shaderProgram) +OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint shaderProgram) + : m_context(context), m_shaderProgram(shaderProgram) { - m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_ABGR8888); + m_width = width; + m_height = height; + m_virtualWidth = width; + m_virtualHeight = height; + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); } OpenGLES2Renderer::~OpenGLES2Renderer() { SDL_DestroySurface(m_renderedImage); - glDeleteFramebuffers(1, &m_fbo); - glDeleteRenderbuffers(1, &m_depthRb); glDeleteProgram(m_shaderProgram); - glDeleteTextures(1, &m_colorTex); } void OpenGLES2Renderer::PushLights(const SceneLight* lightsArray, size_t count) @@ -250,7 +210,6 @@ void OpenGLES2Renderer::SetFrustumPlanes(const Plane* frustumPlanes) void OpenGLES2Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); - m_projection[1][1] *= -1.0f; // OpenGL is upside down } struct TextureDestroyContextGLS2 { @@ -289,14 +248,12 @@ Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture) glGenTextures(1, &tex.glTextureId); glBindTexture(GL_TEXTURE_2D, tex.glTextureId); - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_ABGR8888); + SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); if (!surf) { return NO_TEXTURE_ID; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); SDL_DestroySurface(surf); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); tex.version = texture->m_version; } @@ -308,14 +265,11 @@ Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture) glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); - SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_ABGR8888); + SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); if (!surf) { return NO_TEXTURE_ID; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); - SDL_DestroySurface(surf); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; @@ -323,12 +277,15 @@ Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture) tex.texture = texture; tex.version = texture->m_version; tex.glTextureId = texId; + tex.width = surf->w; + tex.height = surf->h; AddTextureDestroyCallback(i, texture); return i; } } - m_textures.push_back({texture, texture->m_version, texId}); + m_textures.push_back({texture, texture->m_version, texId, (uint16_t) surf->w, (uint16_t) surf->h}); + SDL_DestroySurface(surf); AddTextureDestroyCallback((Uint32) (m_textures.size() - 1), texture); return (Uint32) (m_textures.size() - 1); } @@ -366,7 +323,6 @@ GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup) return v.texCoord; }); } - std::vector positions(vertices.size()); std::transform(vertices.begin(), vertices.end(), positions.begin(), [](const D3DRMVERTEX& v) { return v.position; @@ -451,16 +407,6 @@ Uint32 OpenGLES2Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* mesh return (Uint32) (m_meshs.size() - 1); } -DWORD OpenGLES2Renderer::GetWidth() -{ - return m_width; -} - -DWORD OpenGLES2Renderer::GetHeight() -{ - return m_height; -} - void OpenGLES2Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { halDesc->dcmColorModel = D3DCOLORMODEL::RGB; @@ -490,9 +436,7 @@ const char* OpenGLES2Renderer::GetName() HRESULT OpenGLES2Renderer::BeginFrame() { - SDL_GL_MakeCurrent(DDWindow, m_context); - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); - glViewport(0, 0, m_width, m_height); + m_dirty = true; glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); @@ -500,9 +444,6 @@ HRESULT OpenGLES2Renderer::BeginFrame() glUseProgram(m_shaderProgram); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - SceneLightGLES2 lightData[3]; int lightCount = std::min(static_cast(m_lights.size()), 3); @@ -531,7 +472,6 @@ HRESULT OpenGLES2Renderer::BeginFrame() glUniform4fv(glGetUniformLocation(m_shaderProgram, (base + ".direction").c_str()), 1, lightData[i].direction); } glUniform1i(glGetUniformLocation(m_shaderProgram, "u_lightCount"), lightCount); - return DD_OK; } @@ -545,6 +485,8 @@ void OpenGLES2Renderer::EnableTransparency() void OpenGLES2Renderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) @@ -570,6 +512,8 @@ void OpenGLES2Renderer::SubmitDraw( glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_textures[appearance.textureId].glTextureId); glUniform1i(glGetUniformLocation(m_shaderProgram, "u_texture"), 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glUniform1i(glGetUniformLocation(m_shaderProgram, "u_useTexture"), 0); @@ -606,11 +550,168 @@ HRESULT OpenGLES2Renderer::FinalizeFrame() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glUseProgram(0); - glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // Composite onto SDL backbuffer - SDL_BlitSurface(m_renderedImage, nullptr, DDBackBuffer, nullptr); - return DD_OK; } + +void OpenGLES2Renderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + m_width = width; + m_height = height; + m_viewportTransform = viewportTransform; + SDL_DestroySurface(m_renderedImage); + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); + glViewport(0, 0, m_width, m_height); +} + +void OpenGLES2Renderer::Clear(float r, float g, float b) +{ + m_dirty = true; + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glClearColor(r, g, b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void OpenGLES2Renderer::Flip() +{ + if (m_dirty) { + SDL_GL_SwapWindow(DDWindow); + m_dirty = false; + } +} + +void CreateOrthoMatrix(float left, float right, float bottom, float top, D3DRMMATRIX4D& outMatrix) +{ + float near = -1.0f; + float far = 1.0f; + float rl = right - left; + float tb = top - bottom; + float fn = far - near; + + outMatrix[0][0] = 2.0f / rl; + outMatrix[0][1] = 0.0f; + outMatrix[0][2] = 0.0f; + outMatrix[0][3] = 0.0f; + + outMatrix[1][0] = 0.0f; + outMatrix[1][1] = 2.0f / tb; + outMatrix[1][2] = 0.0f; + outMatrix[1][3] = 0.0f; + + outMatrix[2][0] = 0.0f; + outMatrix[2][1] = 0.0f; + outMatrix[2][2] = -2.0f / fn; + outMatrix[2][3] = 0.0f; + + outMatrix[3][0] = -(right + left) / rl; + outMatrix[3][1] = -(top + bottom) / tb; + outMatrix[3][2] = -(far + near) / fn; + outMatrix[3][3] = 1.0f; +} + +void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + m_dirty = true; + + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + glUseProgram(m_shaderProgram); + + float color[] = {1.0f, 1.0f, 1.0f, 1.0f}; + float blank[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glUniform4fv(glGetUniformLocation(m_shaderProgram, "u_lights[0].color"), 1, color); + glUniform4fv(glGetUniformLocation(m_shaderProgram, "u_lights[0].position"), 1, blank); + glUniform4fv(glGetUniformLocation(m_shaderProgram, "u_lights[0].direction"), 1, blank); + glUniform1i(glGetUniformLocation(m_shaderProgram, "u_lightCount"), 1); + + glUniform4f(glGetUniformLocation(m_shaderProgram, "u_color"), 1.0f, 1.0f, 1.0f, 1.0f); + glUniform1f(glGetUniformLocation(m_shaderProgram, "u_shininess"), 0.0f); + + float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; + float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale; + float top = -m_viewportTransform.offsetY / m_viewportTransform.scale; + float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale; + + D3DRMMATRIX4D projection; + CreateOrthoMatrix(left, right, bottom, top, projection); + + D3DRMMATRIX4D identity = {{1.f, 0.f, 0.f, 0.f}, {0.f, 1.f, 0.f, 0.f}, {0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 0.f, 1.f}}; + glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, "u_modelViewMatrix"), 1, GL_FALSE, &identity[0][0]); + glUniformMatrix3fv(glGetUniformLocation(m_shaderProgram, "u_normalMatrix"), 1, GL_FALSE, &identity[0][0]); + glUniformMatrix4fv(glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"), 1, GL_FALSE, &projection[0][0]); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + glUniform1i(glGetUniformLocation(m_shaderProgram, "u_useTexture"), 1); + const GLES2TextureCacheEntry& texture = m_textures[textureId]; + glBindTexture(GL_TEXTURE_2D, texture.glTextureId); + glUniform1i(glGetUniformLocation(m_shaderProgram, "u_texture"), 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + float texW = texture.width; + float texH = texture.height; + + float u1 = srcRect.x / texW; + float v1 = srcRect.y / texH; + float u2 = (srcRect.x + srcRect.w) / texW; + float v2 = (srcRect.y + srcRect.h) / texH; + + float x1 = static_cast(dstRect.x); + float y1 = static_cast(dstRect.y); + float x2 = x1 + dstRect.w; + float y2 = y1 + dstRect.h; + + GLfloat vertices[] = {x1, y1, u1, v1, x2, y1, u2, v1, x1, y2, u1, v2, x2, y2, u2, v2}; + + GLint posLoc = glGetAttribLocation(m_shaderProgram, "a_position"); + GLint texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord"); + + glEnableVertexAttribArray(posLoc); + glEnableVertexAttribArray(texLoc); + + glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices); + glVertexAttribPointer(texLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices + 2); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(posLoc); + glDisableVertexAttribArray(texLoc); + + glBindTexture(GL_TEXTURE_2D, 0); + glUseProgram(0); +} + +void OpenGLES2Renderer::Download(SDL_Surface* target) +{ + glFinish(); + glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); + + SDL_Rect srcRect = { + static_cast(m_viewportTransform.offsetX), + static_cast(m_viewportTransform.offsetY), + static_cast(target->w * m_viewportTransform.scale), + static_cast(target->h * m_viewportTransform.scale), + }; + + SDL_Surface* bufferClone = SDL_CreateSurface(target->w, target->h, SDL_PIXELFORMAT_RGBA32); + if (!bufferClone) { + SDL_Log("SDL_CreateSurface: %s", SDL_GetError()); + return; + } + + SDL_BlitSurfaceScaled(m_renderedImage, &srcRect, bufferClone, nullptr, SDL_SCALEMODE_NEAREST); + + // Flip image vertically into target + SDL_Rect rowSrc = {0, 0, bufferClone->w, 1}; + SDL_Rect rowDst = {0, 0, bufferClone->w, 1}; + for (int y = 0; y < bufferClone->h; ++y) { + rowSrc.y = y; + rowDst.y = bufferClone->h - 1 - y; + SDL_BlitSurface(bufferClone, &rowSrc, target, &rowDst); + } + + SDL_DestroySurface(bufferClone); +} diff --git a/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp b/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp index ea182dc2..4ee5a35c 100644 --- a/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp +++ b/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp @@ -7,6 +7,7 @@ #include "miniwin.h" #include +#include #include struct ScopedSurface { @@ -91,7 +92,12 @@ struct ScopedShader { void release() { ptr = nullptr; } }; -static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device, bool depthWrite) +static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline( + SDL_GPUDevice* device, + SDL_Window* window, + bool depthTest, + bool depthWrite +) { const SDL_GPUShaderCreateInfo* vertexCreateInfo = GetVertexShaderCode(VertexShaderId::PositionColor, SDL_GetGPUShaderFormats(device)); @@ -141,8 +147,8 @@ static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device vertexInputState.num_vertex_attributes = SDL_arraysize(vertexAttrs); SDL_GPUColorTargetDescription colorTargets = {}; - colorTargets.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - if (depthWrite) { + colorTargets.format = SDL_GetGPUSwapchainTextureFormat(device, window); + if (depthTest && depthWrite) { colorTargets.blend_state.enable_blend = false; } else { @@ -170,7 +176,7 @@ static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device pipelineCreateInfo.rasterizer_state = rasterizerState; pipelineCreateInfo.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_GREATER; pipelineCreateInfo.depth_stencil_state.write_mask = 0xff; - pipelineCreateInfo.depth_stencil_state.enable_depth_test = true; + pipelineCreateInfo.depth_stencil_state.enable_depth_test = depthTest; pipelineCreateInfo.depth_stencil_state.enable_depth_write = depthWrite; pipelineCreateInfo.depth_stencil_state.enable_stencil_test = false; pipelineCreateInfo.target_info.color_target_descriptions = &colorTargets; @@ -178,16 +184,18 @@ static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device pipelineCreateInfo.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT; pipelineCreateInfo.target_info.has_depth_stencil_target = true; - SDL_GPUGraphicsPipeline* pipeline = SDL_CreateGPUGraphicsPipeline(device, &pipelineCreateInfo); - - return pipeline; + return SDL_CreateGPUGraphicsPipeline(device, &pipelineCreateInfo); } Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) { ScopedDevice device{SDL_CreateGPUDevice( SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, +#ifdef DEBUG true, +#else + false, +#endif nullptr )}; if (!device.ptr) { @@ -195,63 +203,63 @@ Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) return nullptr; } - ScopedPipeline opaquePipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, true)}; + SDL_Window* window = DDWindow; + bool testWindow = false; + if (!window) { + window = SDL_CreateWindow("SDL_GPU test", width, height, SDL_WINDOW_HIDDEN); + if (!window) { + SDL_Log("SDL_CreateWindow: %s", SDL_GetError()); + return nullptr; + } + testWindow = true; + } + + if (!SDL_ClaimWindowForGPUDevice(device.ptr, window)) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_ClaimWindowForGPUDevice: %s", SDL_GetError()); + if (testWindow) { + SDL_DestroyWindow(window); + } + return nullptr; + } + + ScopedPipeline opaquePipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, window, true, true)}; if (!opaquePipeline.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "InitializeGraphicsPipeline for opaquePipeline"); + if (testWindow) { + SDL_DestroyWindow(window); + } return nullptr; } - ScopedPipeline transparentPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, false)}; + ScopedPipeline transparentPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, window, true, false)}; if (!transparentPipeline.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "InitializeGraphicsPipeline for transparentPipeline"); + if (testWindow) { + SDL_DestroyWindow(window); + } return nullptr; } - SDL_GPUTextureCreateInfo textureInfo = {}; - textureInfo.type = SDL_GPU_TEXTURETYPE_2D; - textureInfo.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - textureInfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; - textureInfo.width = width; - textureInfo.height = height; - textureInfo.layer_count_or_depth = 1; - textureInfo.num_levels = 1; - textureInfo.sample_count = SDL_GPU_SAMPLECOUNT_1; - ScopedTexture transferTexture{device.ptr, SDL_CreateGPUTexture(device.ptr, &textureInfo)}; - if (!transferTexture.ptr) { - SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_CreateGPUTexture for backbuffer failed (%s)", SDL_GetError()); - return nullptr; - } - - SDL_GPUTextureCreateInfo depthTexInfo = textureInfo; - depthTexInfo.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT; - depthTexInfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; - ScopedTexture depthTexture{device.ptr, SDL_CreateGPUTexture(device.ptr, &depthTexInfo)}; - if (!depthTexture.ptr) { - SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_CreateGPUTexture for depth buffer (%s)", SDL_GetError()); - return nullptr; - } - - // Setup texture GPU-to-CPU transfer - SDL_GPUTransferBufferCreateInfo downloadBufferInfo = {}; - downloadBufferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD; - downloadBufferInfo.size = width * height * 4; - ScopedTransferBuffer downloadBuffer{device.ptr, SDL_CreateGPUTransferBuffer(device.ptr, &downloadBufferInfo)}; - if (!downloadBuffer.ptr) { - SDL_LogError( - LOG_CATEGORY_MINIWIN, - "SDL_CreateGPUTransferBuffer filed for download buffer (%s)", - SDL_GetError() - ); + ScopedPipeline uiPipeline{device.ptr, InitializeGraphicsPipeline(device.ptr, window, false, false)}; + if (!uiPipeline.ptr) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "InitializeGraphicsPipeline for uiPipeline"); + if (testWindow) { + SDL_DestroyWindow(window); + } return nullptr; } // Setup texture CPU-to-GPU transfer SDL_GPUTransferBufferCreateInfo uploadBufferInfo = {}; uploadBufferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; - uploadBufferInfo.size = 256 * 256 * 4; // Largest known game texture + int uploadBufferSize = 640 * 480 * 4; // Largest known game texture + uploadBufferInfo.size = uploadBufferSize; ScopedTransferBuffer uploadBuffer{device.ptr, SDL_CreateGPUTransferBuffer(device.ptr, &uploadBufferInfo)}; if (!uploadBuffer.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_CreateGPUTransferBuffer filed for upload buffer (%s)", SDL_GetError()); + if (testWindow) { + SDL_DestroyWindow(window); + } return nullptr; } @@ -265,31 +273,54 @@ Direct3DRMRenderer* Direct3DRMSDL3GPURenderer::Create(DWORD width, DWORD height) ScopedSampler sampler{device.ptr, SDL_CreateGPUSampler(device.ptr, &samplerInfo)}; if (!sampler.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "Failed to create sampler: %s", SDL_GetError()); + if (testWindow) { + SDL_DestroyWindow(window); + } return nullptr; } + SDL_GPUSamplerCreateInfo uiSamplerInfo = {}; + uiSamplerInfo.min_filter = SDL_GPU_FILTER_LINEAR; + uiSamplerInfo.mag_filter = SDL_GPU_FILTER_NEAREST; + uiSamplerInfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; + uiSamplerInfo.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + uiSamplerInfo.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + uiSamplerInfo.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + ScopedSampler uiSampler{device.ptr, SDL_CreateGPUSampler(device.ptr, &uiSamplerInfo)}; + if (!uiSampler.ptr) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "Failed to create sampler: %s", SDL_GetError()); + if (testWindow) { + SDL_DestroyWindow(window); + } + return nullptr; + } + + if (testWindow) { + SDL_ReleaseWindowFromGPUDevice(device.ptr, window); + SDL_DestroyWindow(window); + } + auto renderer = new Direct3DRMSDL3GPURenderer( width, height, device.ptr, opaquePipeline.ptr, transparentPipeline.ptr, - transferTexture.ptr, - depthTexture.ptr, + uiPipeline.ptr, sampler.ptr, + uiSampler.ptr, uploadBuffer.ptr, - downloadBuffer.ptr + uploadBufferSize ); // Release resources so they don't get cleaned up device.release(); opaquePipeline.release(); transparentPipeline.release(); - transferTexture.release(); - depthTexture.release(); + uiPipeline.release(); sampler.release(); + uiSampler.release(); uploadBuffer.release(); - downloadBuffer.release(); return renderer; } @@ -300,17 +331,21 @@ Direct3DRMSDL3GPURenderer::Direct3DRMSDL3GPURenderer( SDL_GPUDevice* device, SDL_GPUGraphicsPipeline* opaquePipeline, SDL_GPUGraphicsPipeline* transparentPipeline, - SDL_GPUTexture* transferTexture, - SDL_GPUTexture* depthTexture, + SDL_GPUGraphicsPipeline* uiPipeline, SDL_GPUSampler* sampler, + SDL_GPUSampler* uiSampler, SDL_GPUTransferBuffer* uploadBuffer, - SDL_GPUTransferBuffer* downloadBuffer + int uploadBufferSize ) - : m_width(width), m_height(height), m_device(device), m_opaquePipeline(opaquePipeline), - m_transparentPipeline(transparentPipeline), m_transferTexture(transferTexture), m_depthTexture(depthTexture), - m_sampler(sampler), m_uploadBuffer(uploadBuffer), m_downloadBuffer(downloadBuffer) + : m_device(device), m_opaquePipeline(opaquePipeline), m_transparentPipeline(transparentPipeline), + m_uiPipeline(uiPipeline), m_sampler(sampler), m_uiSampler(uiSampler), m_uploadBuffer(uploadBuffer), + m_uploadBufferSize(uploadBufferSize) { - SDL_Surface* dummySurface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_ABGR8888); + m_width = width; + m_height = height; + m_virtualWidth = width; + m_virtualHeight = height; + SDL_Surface* dummySurface = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA32); if (!dummySurface) { SDL_LogError(LOG_CATEGORY_MINIWIN, "Failed to create surface: %s", SDL_GetError()); return; @@ -324,35 +359,67 @@ Direct3DRMSDL3GPURenderer::Direct3DRMSDL3GPURenderer( SDL_UnlockSurface(dummySurface); m_dummyTexture = CreateTextureFromSurface(dummySurface); + if (!m_dummyTexture) { + SDL_DestroySurface(dummySurface); + SDL_LogError(LOG_CATEGORY_MINIWIN, "Failed to create surface: %s", SDL_GetError()); + return; + } SDL_DestroySurface(dummySurface); + + ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f}; + Resize(m_width, m_height, viewportTransform); + + m_uiMesh.vertices = { + {{0.0f, 0.0f, 0.0f}, {0, 0, -1}, {0.0f, 0.0f}}, + {{1.0f, 0.0f, 0.0f}, {0, 0, -1}, {1.0f, 0.0f}}, + {{1.0f, 1.0f, 0.0f}, {0, 0, -1}, {1.0f, 1.0f}}, + {{0.0f, 1.0f, 0.0f}, {0, 0, -1}, {0.0f, 1.0f}} + }; + m_uiMesh.indices = {0, 1, 2, 0, 2, 3}; + m_uiMeshCache = UploadMesh(m_uiMesh); } Direct3DRMSDL3GPURenderer::~Direct3DRMSDL3GPURenderer() { - SDL_ReleaseGPUTransferBuffer(m_device, m_downloadBuffer); + SDL_ReleaseGPUBuffer(m_device, m_uiMeshCache.vertexBuffer); + SDL_ReleaseGPUBuffer(m_device, m_uiMeshCache.indexBuffer); + if (DDWindow) { + SDL_ReleaseWindowFromGPUDevice(m_device, DDWindow); + } + if (m_downloadBuffer) { + SDL_ReleaseGPUTransferBuffer(m_device, m_downloadBuffer); + } if (m_uploadBuffer) { SDL_ReleaseGPUTransferBuffer(m_device, m_uploadBuffer); } SDL_ReleaseGPUSampler(m_device, m_sampler); - SDL_ReleaseGPUTexture(m_device, m_dummyTexture); - SDL_ReleaseGPUTexture(m_device, m_depthTexture); - SDL_ReleaseGPUTexture(m_device, m_transferTexture); + SDL_ReleaseGPUSampler(m_device, m_uiSampler); + if (m_dummyTexture) { + SDL_ReleaseGPUTexture(m_device, m_dummyTexture); + } + if (m_depthTexture) { + SDL_ReleaseGPUTexture(m_device, m_depthTexture); + } + if (m_transferTexture) { + SDL_ReleaseGPUTexture(m_device, m_transferTexture); + } SDL_ReleaseGPUGraphicsPipeline(m_device, m_opaquePipeline); SDL_ReleaseGPUGraphicsPipeline(m_device, m_transparentPipeline); + SDL_ReleaseGPUGraphicsPipeline(m_device, m_uiPipeline); if (m_uploadFence) { SDL_ReleaseGPUFence(m_device, m_uploadFence); } SDL_DestroyGPUDevice(m_device); } -void Direct3DRMSDL3GPURenderer::PushLights(const SceneLight* vertices, size_t count) +void Direct3DRMSDL3GPURenderer::PushLights(const SceneLight* lights, size_t count) { if (count > 3) { SDL_LogError(LOG_CATEGORY_MINIWIN, "Unsupported number of lights (%d)", static_cast(count)); count = 3; } int lightCount = std::min(static_cast(count), 3); - memcpy(&m_fragmentShadingData.lights, vertices, sizeof(SceneLight) * lightCount); + memcpy(&m_fragmentShadingData.lights, lights, sizeof(SceneLight) * lightCount); m_fragmentShadingData.lightCount = lightCount; } @@ -364,7 +431,7 @@ void Direct3DRMSDL3GPURenderer::SetProjection(const D3DRMMATRIX4D& projection, D { m_front = front; m_back = back; - memcpy(&m_uniforms.projection, projection, sizeof(D3DRMMATRIX4D)); + memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); } void Direct3DRMSDL3GPURenderer::WaitForPendingUpload() @@ -405,7 +472,7 @@ void Direct3DRMSDL3GPURenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRM SDL_GPUTexture* Direct3DRMSDL3GPURenderer::CreateTextureFromSurface(SDL_Surface* surface) { - ScopedSurface surf{SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ABGR8888)}; + ScopedSurface surf{SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32)}; if (!surf.ptr) { SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_ConvertSurface (%s)", SDL_GetError()); return nullptr; @@ -607,6 +674,7 @@ void Direct3DRMSDL3GPURenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMes auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_meshs[ctx->id]; SDL_ReleaseGPUBuffer(ctx->renderer->m_device, cache.vertexBuffer); + SDL_ReleaseGPUBuffer(ctx->renderer->m_device, cache.indexBuffer); cache.meshGroup = nullptr; delete ctx; }, @@ -621,6 +689,7 @@ Uint32 Direct3DRMSDL3GPURenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGro if (cache.meshGroup == meshGroup) { if (cache.version != meshGroup->version) { SDL_ReleaseGPUBuffer(m_device, cache.vertexBuffer); + SDL_ReleaseGPUBuffer(m_device, cache.indexBuffer); cache = std::move(UploadMesh(*meshGroup)); } return i; @@ -643,16 +712,6 @@ Uint32 Direct3DRMSDL3GPURenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGro return (Uint32) (m_meshs.size() - 1); } -DWORD Direct3DRMSDL3GPURenderer::GetWidth() -{ - return m_width; -} - -DWORD Direct3DRMSDL3GPURenderer::GetHeight() -{ - return m_height; -} - void Direct3DRMSDL3GPURenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { halDesc->dcmColorModel = D3DCOLORMODEL::RGB; @@ -708,32 +767,45 @@ SDL_GPUTransferBuffer* Direct3DRMSDL3GPURenderer::GetUploadBuffer(size_t size) return m_uploadBuffer; } -HRESULT Direct3DRMSDL3GPURenderer::BeginFrame() +void Direct3DRMSDL3GPURenderer::StartRenderPass(float r, float g, float b, bool clear) { - if (!DDBackBuffer) { - return DDERR_GENERIC; + m_cmdbuf = SDL_AcquireGPUCommandBuffer(m_device); + if (!m_cmdbuf) { + SDL_LogError( + LOG_CATEGORY_MINIWIN, + "SDL_AcquireGPUCommandBuffer in StartRenderPass failed (%s)", + SDL_GetError() + ); + return; } // Clear color and depth targets SDL_GPUColorTargetInfo colorTargetInfo = {}; colorTargetInfo.texture = m_transferTexture; - colorTargetInfo.clear_color = {0, 0, 0, 0}; - colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; + colorTargetInfo.clear_color = {r, g, b, 1.0f}; + colorTargetInfo.load_op = clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_DONT_CARE; SDL_GPUDepthStencilTargetInfo depthStencilTargetInfo = {}; depthStencilTargetInfo.texture = m_depthTexture; depthStencilTargetInfo.clear_depth = 0.0f; - depthStencilTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; + depthStencilTargetInfo.load_op = clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_DONT_CARE; depthStencilTargetInfo.store_op = SDL_GPU_STOREOP_STORE; - m_cmdbuf = SDL_AcquireGPUCommandBuffer(m_device); - if (!m_cmdbuf) { - SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_AcquireGPUCommandBuffer in BeginFrame failed (%s)", SDL_GetError()); - return DDERR_GENERIC; - } - m_renderPass = SDL_BeginGPURenderPass(m_cmdbuf, &colorTargetInfo, 1, &depthStencilTargetInfo); +} + +void Direct3DRMSDL3GPURenderer::Clear(float r, float g, float b) +{ + StartRenderPass(r, g, b, true); +} + +HRESULT Direct3DRMSDL3GPURenderer::BeginFrame() +{ + if (!m_renderPass) { + StartRenderPass(0, 0, 0, false); + } SDL_BindGPUGraphicsPipeline(m_renderPass, m_opaquePipeline); + memcpy(&m_uniforms.projection, m_projection, sizeof(D3DRMMATRIX4D)); return DD_OK; } @@ -746,6 +818,8 @@ void Direct3DRMSDL3GPURenderer::EnableTransparency() void Direct3DRMSDL3GPURenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) @@ -774,15 +848,210 @@ void Direct3DRMSDL3GPURenderer::SubmitDraw( HRESULT Direct3DRMSDL3GPURenderer::FinalizeFrame() { - SDL_EndGPURenderPass(m_renderPass); - m_renderPass = nullptr; + return DD_OK; +} + +void Direct3DRMSDL3GPURenderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + m_width = width; + m_height = height; + m_viewportTransform = viewportTransform; + + if (!DDWindow) { + return; + } + + if (m_transferTexture) { + SDL_ReleaseGPUTexture(m_device, m_transferTexture); + } + SDL_GPUTextureCreateInfo textureInfo = {}; + textureInfo.type = SDL_GPU_TEXTURETYPE_2D; + textureInfo.format = SDL_GetGPUSwapchainTextureFormat(m_device, DDWindow); + textureInfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER; + textureInfo.width = m_width; + textureInfo.height = m_height; + textureInfo.layer_count_or_depth = 1; + textureInfo.num_levels = 1; + textureInfo.sample_count = SDL_GPU_SAMPLECOUNT_1; + m_transferTexture = SDL_CreateGPUTexture(m_device, &textureInfo); + if (!m_transferTexture) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_CreateGPUTexture for backbuffer failed (%s)", SDL_GetError()); + return; + } + + if (m_depthTexture) { + SDL_ReleaseGPUTexture(m_device, m_depthTexture); + } + SDL_GPUTextureCreateInfo depthTexInfo = textureInfo; + depthTexInfo.format = SDL_GPU_TEXTUREFORMAT_D32_FLOAT; + depthTexInfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; + m_depthTexture = SDL_CreateGPUTexture(m_device, &depthTexInfo); + if (!m_depthTexture) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_CreateGPUTexture for depth buffer (%s)", SDL_GetError()); + return; + } + + // Setup texture GPU-to-CPU transfer + SDL_GPUTransferBufferCreateInfo downloadBufferInfo = {}; + downloadBufferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD; + downloadBufferInfo.size = + ((m_width - (m_viewportTransform.offsetX * 2)) * (m_height - (m_viewportTransform.offsetY * 2))) * 4; + m_downloadBuffer = SDL_CreateGPUTransferBuffer(m_device, &downloadBufferInfo); + if (!m_downloadBuffer) { + SDL_LogError( + LOG_CATEGORY_MINIWIN, + "SDL_CreateGPUTransferBuffer filed for download buffer (%s)", + SDL_GetError() + ); + return; + } +} + +void Direct3DRMSDL3GPURenderer::Flip() +{ + if (!m_cmdbuf) { + return; + } + if (m_renderPass) { + SDL_EndGPURenderPass(m_renderPass); + m_renderPass = nullptr; + } + + SDL_GPUTexture* swapchainTexture; + if (!SDL_WaitAndAcquireGPUSwapchainTexture(m_cmdbuf, DDWindow, &swapchainTexture, nullptr, nullptr) || + !swapchainTexture) { + SDL_Log("SDL_WaitAndAcquireGPUSwapchainTexture: %s", SDL_GetError()); + return; + } + + SDL_GPUBlitInfo blit = {}; + blit.source.texture = m_transferTexture; + blit.source.w = m_width; + blit.source.h = m_height; + blit.destination.texture = swapchainTexture; + blit.destination.w = m_width; + blit.destination.h = m_height; + blit.load_op = SDL_GPU_LOADOP_DONT_CARE; + blit.flip_mode = SDL_FLIP_NONE; + blit.filter = SDL_GPU_FILTER_NEAREST; + blit.cycle = false; + SDL_BlitGPUTexture(m_cmdbuf, &blit); + SDL_SubmitGPUCommandBuffer(m_cmdbuf); + m_cmdbuf = nullptr; +} + +// TODO use SDL_SetGPUScissor(SDL_GPURenderPass *render_pass, const SDL_Rect *scissor) when srcRect isn't 100% of +// texture + +void Create2DTransformMatrix( + const SDL_Rect& dstRect, + float scale, + float offsetX, + float offsetY, + D3DRMMATRIX4D& outMatrix +) +{ + float x = static_cast(dstRect.x) * scale + offsetX; + float y = static_cast(dstRect.y) * scale + offsetY; + float w = static_cast(dstRect.w) * scale; + float h = static_cast(dstRect.h) * scale; + + D3DVALUE tmp[4][4] = {{w, 0, 0, 0}, {0, h, 0, 0}, {0, 0, 1, 0}, {x, y, 0, 1}}; + memcpy(outMatrix, tmp, sizeof(tmp)); +} + +void CreateOrthographicProjection(float width, float height, D3DRMMATRIX4D& outProj) +{ + D3DVALUE tmp[4][4] = { + {2.0f / width, 0.0f, 0.0f, 0.0f}, + {0.0f, -2.0f / height, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {-1.0f, 1.0f, 0.0f, 1.0f} + }; + memcpy(outProj, tmp, sizeof(tmp)); +} + +void Direct3DRMSDL3GPURenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + if (!m_renderPass) { + StartRenderPass(0, 0, 0, false); + } + SDL_BindGPUGraphicsPipeline(m_renderPass, m_uiPipeline); + + const SDL3TextureCache& tex = m_textures[textureId]; + + auto surface = static_cast(tex.texture->m_surface); + float scaleX = static_cast(dstRect.w) / srcRect.w; + float scaleY = static_cast(dstRect.h) / srcRect.h; + SDL_Rect expandedDstRect = { + static_cast(std::round(dstRect.x - srcRect.x * scaleX)), + static_cast(std::round(dstRect.y - srcRect.y * scaleY)), + static_cast(std::round(static_cast(surface->m_surface->w) * scaleX)), + static_cast(std::round(static_cast(surface->m_surface->h) * scaleY)), + }; + + Create2DTransformMatrix( + expandedDstRect, + m_viewportTransform.scale, + m_viewportTransform.offsetX, + m_viewportTransform.offsetY, + m_uniforms.worldViewMatrix + ); + + CreateOrthographicProjection((float) m_width, (float) m_height, m_uniforms.projection); + + SceneLight fullBright = {{1, 1, 1, 1}, {0, 0, 0}, 0, {0, 0, 0}, 0}; + memcpy(&m_fragmentShadingData.lights, &fullBright, sizeof(SceneLight)); + m_fragmentShadingData.lightCount = 1; + m_fragmentShadingData.color = {0xff, 0xff, 0xff, 0xff}; + m_fragmentShadingData.shininess = 0.0f; + m_fragmentShadingData.useTexture = 1; + + SDL_GPUTextureSamplerBinding samplerBinding = {tex.gpuTexture, m_uiSampler}; + SDL_BindGPUFragmentSamplers(m_renderPass, 0, &samplerBinding, 1); + SDL_PushGPUVertexUniformData(m_cmdbuf, 0, &m_uniforms, sizeof(m_uniforms)); + SDL_PushGPUFragmentUniformData(m_cmdbuf, 0, &m_fragmentShadingData, sizeof(m_fragmentShadingData)); + SDL_GPUBufferBinding vertexBufferBinding = {m_uiMeshCache.vertexBuffer}; + SDL_BindGPUVertexBuffers(m_renderPass, 0, &vertexBufferBinding, 1); + SDL_GPUBufferBinding indexBufferBinding = {m_uiMeshCache.indexBuffer}; + SDL_BindGPUIndexBuffer(m_renderPass, &indexBufferBinding, SDL_GPU_INDEXELEMENTSIZE_16BIT); + + SDL_Rect scissor; + scissor.x = static_cast(std::round(dstRect.x * m_viewportTransform.scale + m_viewportTransform.offsetX)); + scissor.y = static_cast(std::round(dstRect.y * m_viewportTransform.scale + m_viewportTransform.offsetY)); + scissor.w = static_cast(std::round(dstRect.w * m_viewportTransform.scale)); + scissor.h = static_cast(std::round(dstRect.h * m_viewportTransform.scale)); + SDL_SetGPUScissor(m_renderPass, &scissor); + + SDL_DrawGPUIndexedPrimitives(m_renderPass, m_uiMeshCache.indexCount, 1, 0, 0, 0); + + SDL_Rect fullViewport = {0, 0, m_width, m_height}; + SDL_SetGPUScissor(m_renderPass, &fullViewport); +} + +void Direct3DRMSDL3GPURenderer::Download(SDL_Surface* target) +{ + if (!m_cmdbuf) { + StartRenderPass(0, 0, 0, false); + } + if (m_renderPass) { + SDL_EndGPURenderPass(m_renderPass); + m_renderPass = nullptr; + } + + const int offsetX = static_cast(m_viewportTransform.offsetX); + const int offsetY = static_cast(m_viewportTransform.offsetY); + const int width = m_width - offsetX * 2; + const int height = m_height - offsetY * 2; - // Download rendered image SDL_GPUTextureRegion region = {}; region.texture = m_transferTexture; - region.w = m_width; - region.h = m_height; + region.x = offsetX; + region.y = offsetY; + region.w = width; + region.h = height; region.d = 1; + SDL_GPUTextureTransferInfo transferInfo = {}; transferInfo.transfer_buffer = m_downloadBuffer; @@ -792,27 +1061,24 @@ HRESULT Direct3DRMSDL3GPURenderer::FinalizeFrame() WaitForPendingUpload(); - // Render the frame SDL_GPUFence* fence = SDL_SubmitGPUCommandBufferAndAcquireFence(m_cmdbuf); m_cmdbuf = nullptr; - if (!fence) { - return DDERR_GENERIC; + + if (!fence || !SDL_WaitForGPUFences(m_device, true, &fence, 1)) { + SDL_ReleaseGPUFence(m_device, fence); + return; } - bool success = SDL_WaitForGPUFences(m_device, true, &fence, 1); SDL_ReleaseGPUFence(m_device, fence); - if (!success) { - return DDERR_GENERIC; - } + void* downloadedData = SDL_MapGPUTransferBuffer(m_device, m_downloadBuffer, false); if (!downloadedData) { - return DDERR_GENERIC; + return; } SDL_Surface* renderedImage = - SDL_CreateSurfaceFrom(m_width, m_height, SDL_PIXELFORMAT_ABGR8888, downloadedData, m_width * 4); - SDL_BlitSurface(renderedImage, nullptr, DDBackBuffer, nullptr); + SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_ARGB8888, downloadedData, width * 4); + + SDL_BlitSurfaceScaled(renderedImage, nullptr, target, nullptr, SDL_SCALEMODE_NEAREST); SDL_DestroySurface(renderedImage); SDL_UnmapGPUTransferBuffer(m_device, m_downloadBuffer); - - return DD_OK; } diff --git a/miniwin/src/d3drm/backends/sdl3gpu/shaders/generated/SolidColor.frag.h b/miniwin/src/d3drm/backends/sdl3gpu/shaders/generated/SolidColor.frag.h index ae843a2b..ebdbe79d 100644 --- a/miniwin/src/d3drm/backends/sdl3gpu/shaders/generated/SolidColor.frag.h +++ b/miniwin/src/d3drm/backends/sdl3gpu/shaders/generated/SolidColor.frag.h @@ -10,9 +10,9 @@ // DXIL only makes sense on Windows platforms #if defined(SDL_PLATFORM_WINDOWS) -static const Uint8 SolidColor_frag_dxil[6640] = { - 0x44, 0x58, 0x42, 0x43, 0x91, 0x24, 0xbe, 0xd8, 0x43, 0x8d, 0x7f, 0xf4, 0x71, 0x2b, 0x59, 0x98, - 0x2d, 0x7f, 0x8d, 0xac, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x19, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, +static const Uint8 SolidColor_frag_dxil[6648] = { + 0x44, 0x58, 0x42, 0x43, 0xd8, 0xfe, 0x4c, 0xec, 0x17, 0x1b, 0x52, 0xee, 0xf7, 0xc8, 0x8d, 0xcf, + 0x47, 0x0e, 0xfe, 0xb7, 0x01, 0x00, 0x00, 0x00, 0xf8, 0x19, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x94, 0x0c, 0x00, 0x00, 0xb0, 0x0c, 0x00, 0x00, 0x53, 0x46, 0x49, 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, @@ -52,7 +52,7 @@ static const Uint8 SolidColor_frag_dxil[6640] = { 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x11, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x54, 0x41, 0x54, 0xc8, 0x09, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, @@ -113,9 +113,9 @@ static const Uint8 SolidColor_frag_dxil[6640] = { 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x22, 0x4a, 0x60, 0x04, 0xa0, 0x18, 0x8a, 0xa0, 0x24, 0xca, 0xa0, 0x80, 0x05, 0x0a, 0xac, 0x1c, 0x4a, 0xa3, 0x10, 0x0a, 0xa4, 0x80, 0x0a, 0x54, 0xa0, 0x50, 0x05, 0x0a, 0x56, 0xa0, 0x14, 0x0a, 0x57, 0xa0, 0x20, 0xca, 0xa3, 0x06, 0xca, 0x34, 0xa0, - 0x24, 0x07, 0xa8, 0x28, 0x89, 0x11, 0x80, 0x22, 0x28, 0x83, 0x42, 0x28, 0x90, 0x12, 0x29, 0x81, + 0x2c, 0x07, 0xa8, 0x28, 0x89, 0x11, 0x80, 0x22, 0x28, 0x83, 0x42, 0x28, 0x90, 0x12, 0x29, 0x81, 0x1a, 0xa0, 0xad, 0x06, 0x48, 0x9c, 0x01, 0xa0, 0x71, 0x06, 0x80, 0xca, 0x19, 0x00, 0x32, 0x67, - 0x00, 0x08, 0x9d, 0x01, 0xa0, 0x74, 0x2c, 0x09, 0x22, 0x8e, 0xe3, 0x00, 0x8e, 0x03, 0x00, 0x8e, + 0x00, 0x08, 0x9d, 0x01, 0xa0, 0x74, 0x2c, 0x09, 0x22, 0x8e, 0xe3, 0x00, 0x9e, 0x07, 0x00, 0x8e, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x03, 0x4c, 0x90, 0x46, 0x02, 0x13, 0xc4, 0x31, 0x20, 0xc3, 0x1b, 0x43, 0x81, 0x93, 0x4b, 0xb3, 0x0b, 0xa3, 0x2b, 0x4b, 0x01, 0x89, 0x71, 0xc1, 0x71, 0x81, 0x71, 0xa1, 0xb9, 0xc1, 0xc9, @@ -213,10 +213,10 @@ static const Uint8 SolidColor_frag_dxil[6640] = { 0xe3, 0x4b, 0x93, 0x13, 0x11, 0x28, 0x35, 0x3d, 0xd4, 0xe4, 0x17, 0xb7, 0x6d, 0x04, 0xcf, 0x70, 0xf9, 0xce, 0xe3, 0x53, 0x0d, 0x10, 0x61, 0x7e, 0x71, 0xdb, 0x06, 0x40, 0x30, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x58, 0x63, 0x6a, 0xf1, 0xca, 0xdc, 0xf6, 0x75, 0x3d, 0x1e, 0x21, 0x9a, 0xb8, 0xa2, 0x91, 0xc3, - 0x44, 0x58, 0x49, 0x4c, 0x38, 0x0d, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x4e, 0x03, 0x00, 0x00, - 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x0d, 0x00, 0x00, - 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0x45, 0x03, 0x00, 0x00, 0x0b, 0x82, 0x20, 0x00, + 0xeb, 0x0f, 0x40, 0x5f, 0x0e, 0x75, 0xec, 0xd9, 0x61, 0x1b, 0x68, 0x81, 0x54, 0xd3, 0xf4, 0x48, + 0x44, 0x58, 0x49, 0x4c, 0x40, 0x0d, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x50, 0x03, 0x00, 0x00, + 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x0d, 0x00, 0x00, + 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, 0x0b, 0x82, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, 0x06, 0x10, 0x32, 0x39, 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, 0x62, 0x80, 0x18, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0xc4, 0x10, 0x32, 0x14, 0x38, 0x08, 0x18, 0x4b, @@ -272,7 +272,7 @@ static const Uint8 SolidColor_frag_dxil[6640] = { 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x22, 0x4a, 0x60, 0x04, 0xa0, 0x20, 0x8a, 0xa1, 0x08, 0x4a, 0xa2, 0x0c, 0x0a, 0x58, 0xa0, 0x1c, 0xca, 0xa3, 0x06, 0xa8, 0x28, 0x89, 0x11, 0x80, 0x22, 0x28, 0x83, 0x42, 0x28, 0x90, 0x12, 0x29, 0x81, 0x1a, 0x20, 0x71, 0x06, 0x80, 0xcc, 0x19, 0x00, - 0x42, 0x67, 0x00, 0x28, 0x1d, 0x4b, 0x82, 0x88, 0xe3, 0x38, 0x80, 0xe3, 0x00, 0x80, 0xe3, 0x38, + 0x42, 0x67, 0x00, 0x28, 0x1d, 0x4b, 0x82, 0x88, 0xe3, 0x38, 0x80, 0xe7, 0x01, 0x80, 0xe3, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x18, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x1a, 0x03, 0x4c, 0x90, 0x46, 0x02, 0x13, 0xc4, 0x31, 0x20, 0xc3, 0x1b, 0x43, 0x81, 0x93, 0x4b, 0xb3, 0x0b, 0xa3, 0x2b, 0x4b, 0x01, 0x89, 0x71, 0xc1, 0x71, 0x81, 0x71, 0xa1, 0xb9, 0xc1, 0xc9, @@ -336,7 +336,7 @@ static const Uint8 SolidColor_frag_dxil[6640] = { 0x9b, 0x01, 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x12, 0xc0, 0x3c, 0x0b, 0xe1, 0x17, 0xb7, 0x6d, 0x02, 0xd5, 0x70, 0xf9, 0xce, 0xe3, 0x4b, 0x93, 0x13, 0x11, 0x28, 0x35, 0x3d, 0xd4, 0xe4, 0x17, 0xb7, 0x6d, 0x04, 0xcf, 0x70, 0xf9, 0xce, 0xe3, 0x53, 0x0d, 0x10, 0x61, 0x7e, 0x71, 0xdb, 0x06, 0x40, - 0x30, 0x00, 0xd2, 0x00, 0x61, 0x20, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x13, 0x04, 0x52, 0x2c, + 0x30, 0x00, 0xd2, 0x00, 0x61, 0x20, 0x00, 0x00, 0x66, 0x01, 0x00, 0x00, 0x13, 0x04, 0x52, 0x2c, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xb4, 0x8d, 0x00, 0x10, 0x51, 0x78, 0x05, 0x52, 0x30, 0xa5, 0x32, 0x03, 0x50, 0x76, 0x85, 0x50, 0x46, 0x85, 0x54, 0x6e, 0xa5, 0x50, 0x72, 0x25, 0x53, 0xfe, 0x03, 0xe5, 0x42, 0xc3, 0x0c, 0xc0, 0x18, 0x41, 0x48, 0x82, 0x21, 0xde, 0x8d, 0x11, @@ -413,25 +413,26 @@ static const Uint8 SolidColor_frag_dxil[6640] = { 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0x9c, 0xd8, 0x58, 0x8b, 0x21, 0x2c, 0x46, 0x13, 0xb8, 0x61, 0xb8, 0x21, 0x68, 0x0d, 0x30, 0x98, 0x65, 0x88, 0xa0, 0x60, 0xc4, 0xe0, 0x01, 0x40, 0x10, 0x0c, 0xa6, 0xd9, 0xb0, 0x0b, 0xb1, 0x08, 0x8b, 0x9d, 0xd0, 0x89, 0xb3, 0x38, 0x0b, 0xd7, 0x70, 0x0d, - 0xb9, 0x38, 0x8b, 0xd1, 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, 0xd1, 0x84, 0x41, 0xb0, 0x21, 0x91, - 0x8f, 0x0d, 0x89, 0x7c, 0x6c, 0x48, 0xe4, 0x33, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0xce, 0x6e, - 0xd8, 0xc5, 0xa0, 0x16, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xe0, 0xf0, 0xc6, 0x5d, 0x0c, 0x6b, - 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x4e, 0x6f, 0xe0, 0xc5, 0xc0, 0x16, 0x23, 0x06, 0x07, - 0x00, 0x82, 0x60, 0xe0, 0xf8, 0x06, 0x5e, 0x0c, 0x6e, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, - 0xce, 0x6f, 0xe4, 0xc5, 0xf0, 0x16, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xe0, 0x80, 0x87, 0x5e, - 0x0c, 0x70, 0x31, 0x4b, 0x10, 0x0d, 0x54, 0x0c, 0x06, 0x44, 0x06, 0xcf, 0x40, 0xc5, 0x60, 0x40, - 0x64, 0xf0, 0x0c, 0x54, 0x0c, 0x06, 0x44, 0x06, 0xcf, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, - 0xe7, 0xc1, 0x17, 0xbe, 0xe1, 0x1b, 0xb3, 0x31, 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x01, 0x72, - 0x1e, 0x7c, 0xe1, 0x1b, 0xbe, 0xd1, 0x1a, 0xc2, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, 0xe7, - 0xc1, 0x17, 0xbe, 0xe1, 0x1b, 0xb2, 0x11, 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x01, 0x72, 0x1e, - 0x7c, 0xe1, 0x1b, 0xbe, 0x11, 0x1b, 0x6a, 0x30, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, 0xc8, 0x79, - 0xf0, 0xc5, 0x6f, 0xf8, 0xc6, 0x6c, 0x98, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb9, 0x38, 0x8b, 0xd1, 0x84, 0x00, 0x18, 0x4d, 0x10, 0x82, 0xd1, 0x84, 0x41, 0x18, 0x4d, 0x20, + 0x06, 0x23, 0x14, 0xf9, 0x18, 0xa1, 0xc8, 0xc7, 0x08, 0x45, 0x3e, 0x23, 0x06, 0x07, 0x00, 0x82, + 0x60, 0xe0, 0xf0, 0xc6, 0x5d, 0x0c, 0x6b, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0x4e, 0x6f, + 0xe0, 0xc5, 0xc0, 0x16, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0xe0, 0xf8, 0x46, 0x5e, 0x0c, 0x6d, + 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, 0xce, 0x6f, 0xe4, 0xc5, 0xf0, 0x16, 0x23, 0x06, 0x07, + 0x00, 0x82, 0x60, 0xe0, 0x80, 0x87, 0x5e, 0x0c, 0x70, 0x31, 0x62, 0x70, 0x00, 0x20, 0x08, 0x06, + 0x4e, 0x78, 0xec, 0xc5, 0x10, 0x17, 0xb3, 0x04, 0xd1, 0x40, 0xc5, 0x60, 0x40, 0x66, 0xf0, 0x0c, + 0x54, 0x0c, 0x06, 0x64, 0x06, 0xcf, 0x40, 0xc5, 0x60, 0x40, 0x66, 0xf0, 0x0c, 0x54, 0x0c, 0x1a, + 0x64, 0x0b, 0xcf, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, 0xe9, 0xe1, 0x17, 0xe0, 0x01, 0x1e, + 0xb5, 0x41, 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x01, 0x92, 0x1e, 0x7e, 0x01, 0x1e, 0xe0, 0xf1, + 0x1a, 0xc3, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, 0xe9, 0xe1, 0x17, 0xe0, 0x01, 0x1e, 0xb4, + 0x21, 0x8c, 0x18, 0x24, 0x00, 0x08, 0x82, 0x01, 0x92, 0x1e, 0x7e, 0x01, 0x1e, 0xe0, 0x31, 0x1b, + 0xc1, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x20, 0xe9, 0xe1, 0x17, 0xe1, 0x01, 0x1e, 0xb5, 0x81, + 0x16, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; #endif // MSL only makes sense on Apple platforms #if defined(SDL_PLATFORM_APPLE) -static const Uint8 SolidColor_frag_msl[3333] = { +static const Uint8 SolidColor_frag_msl[3377] = { 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x64, 0x6c, 0x69, 0x62, 0x3e, 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x73, 0x69, 0x6d, 0x64, 0x2f, 0x73, 0x69, 0x6d, 0x64, 0x2e, 0x68, 0x3e, 0x0a, 0x0a, @@ -620,32 +621,35 @@ static const Uint8 SolidColor_frag_msl[3333] = { 0x78, 0x79, 0x7a, 0x29, 0x20, 0x2b, 0x20, 0x5f, 0x36, 0x32, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, - 0x33, 0x20, 0x5f, 0x31, 0x36, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, + 0x34, 0x20, 0x5f, 0x31, 0x37, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x68, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x55, 0x73, 0x65, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x5f, 0x31, 0x36, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, - 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x73, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, - 0x2e, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, - 0x31, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x31, 0x35, 0x31, 0x2c, 0x20, 0x66, - 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x33, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x36, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x31, - 0x35, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, - 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x30, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x5f, 0x31, - 0x36, 0x34, 0x2c, 0x20, 0x5f, 0x31, 0x34, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, - 0x75, 0x74, 0x2e, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x20, - 0x3d, 0x20, 0x67, 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x77, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, - 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x31, 0x36, 0x31, 0x20, 0x3d, + 0x20, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, + 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, 0x2e, 0x69, 0x6e, 0x5f, 0x76, + 0x61, 0x72, 0x5f, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x31, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x31, 0x37, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x34, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, + 0x28, 0x5f, 0x31, 0x36, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x31, 0x35, 0x31, + 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x2c, 0x20, 0x5f, 0x31, 0x36, + 0x31, 0x2e, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x5f, 0x31, 0x37, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, + 0x28, 0x5f, 0x31, 0x35, 0x31, 0x2c, 0x20, 0x5f, 0x31, 0x34, 0x36, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x5f, + 0x76, 0x61, 0x72, 0x5f, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x30, 0x20, 0x3d, + 0x20, 0x5f, 0x31, 0x37, 0x34, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, + 0x6c, 0x5f, 0x46, 0x72, 0x61, 0x67, 0x44, 0x65, 0x70, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x67, 0x6c, + 0x5f, 0x46, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x2e, 0x77, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x3b, 0x0a, 0x7d, 0x0a, + 0x0a, }; #endif -static const Uint8 SolidColor_frag_spirv[4492] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xab, 0x00, 0x00, 0x00, +static const Uint8 SolidColor_frag_spirv[4616] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, @@ -902,28 +906,36 @@ static const Uint8 SolidColor_frag_spirv[4492] = { 0x16, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xab, 0x00, 0x05, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, - 0x9a, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x9c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x57, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xa0, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4f, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x9b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, - 0x9b, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0xa4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, - 0xa7, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x38, 0x00, 0x00, 0x00, - 0xa9, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, - 0x12, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0xaa, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, + 0x9a, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x57, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xa1, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, + 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0xa1, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x12, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x27, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, + 0xf9, 0x00, 0x02, 0x00, 0x9b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x9d, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0x97, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, + 0x27, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x9b, 0x00, 0x00, 0x00, + 0xf8, 0x00, 0x02, 0x00, 0x9b, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x07, 0x00, 0x27, 0x00, 0x00, 0x00, + 0xae, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x38, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, }; diff --git a/miniwin/src/d3drm/backends/sdl3gpu/shaders/src/SolidColor.frag.hlsl b/miniwin/src/d3drm/backends/sdl3gpu/shaders/src/SolidColor.frag.hlsl index 7e9a0c6d..6a6608ba 100644 --- a/miniwin/src/d3drm/backends/sdl3gpu/shaders/src/SolidColor.frag.hlsl +++ b/miniwin/src/d3drm/backends/sdl3gpu/shaders/src/SolidColor.frag.hlsl @@ -72,8 +72,11 @@ FS_Output main(FS_Input input) if (UseTexture != 0) { float4 texel = Texture.Sample(Sampler, input.TexCoord); finalColor = saturate(texel.rgb * finalColor); + output.Color = float4(finalColor, texel.a); + } + else { + output.Color = float4(finalColor, Color.a); } - output.Color = float4(finalColor, Color.a); output.Depth = input.Position.w; return output; } diff --git a/miniwin/src/d3drm/backends/software/renderer.cpp b/miniwin/src/d3drm/backends/software/renderer.cpp index 563821b2..64adc93b 100644 --- a/miniwin/src/d3drm/backends/software/renderer.cpp +++ b/miniwin/src/d3drm/backends/software/renderer.cpp @@ -23,9 +23,22 @@ #include #endif -Direct3DRMSoftwareRenderer::Direct3DRMSoftwareRenderer(DWORD width, DWORD height) : m_width(width), m_height(height) +Direct3DRMSoftwareRenderer::Direct3DRMSoftwareRenderer(DWORD width, DWORD height) { - m_zBuffer.resize(m_width * m_height); + m_virtualWidth = width; + m_virtualHeight = height; + + m_renderer = SDL_CreateRenderer(DDWindow, NULL); + + ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f}; + Resize(width, height, viewportTransform); +} + +Direct3DRMSoftwareRenderer::~Direct3DRMSoftwareRenderer() +{ + SDL_DestroySurface(m_renderedImage); + SDL_DestroyTexture(m_uploadBuffer); + SDL_DestroyRenderer(m_renderer); } void Direct3DRMSoftwareRenderer::PushLights(const SceneLight* lights, size_t count) @@ -354,8 +367,8 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected( c2 = ApplyLighting(v2.position, v2.normal, appearance); } - Uint8* pixels = (Uint8*) DDBackBuffer->pixels; - int pitch = DDBackBuffer->pitch; + Uint8* pixels = (Uint8*) m_renderedImage->pixels; + int pitch = m_renderedImage->pitch; VertexXY verts[3] = { {p0.x, p0.y, p0.z, p0.w, c0, v0.texCoord.u, v0.texCoord.v}, @@ -553,7 +566,7 @@ Uint32 Direct3DRMSoftwareRenderer::GetTextureId(IDirect3DRMTexture* iTexture) if (texRef.version != texture->m_version) { // Update animated textures SDL_DestroySurface(texRef.cached); - texRef.cached = SDL_ConvertSurface(surface->m_surface, DDBackBuffer->format); + texRef.cached = SDL_ConvertSurface(surface->m_surface, m_renderedImage->format); SDL_LockSurface(texRef.cached); texRef.version = texture->m_version; } @@ -561,7 +574,7 @@ Uint32 Direct3DRMSoftwareRenderer::GetTextureId(IDirect3DRMTexture* iTexture) } } - SDL_Surface* convertedRender = SDL_ConvertSurface(surface->m_surface, DDBackBuffer->format); + SDL_Surface* convertedRender = SDL_ConvertSurface(surface->m_surface, m_renderedImage->format); SDL_LockSurface(convertedRender); // Reuse freed slot @@ -651,16 +664,6 @@ Uint32 Direct3DRMSoftwareRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGr return (Uint32) (m_meshs.size() - 1); } -DWORD Direct3DRMSoftwareRenderer::GetWidth() -{ - return m_width; -} - -DWORD Direct3DRMSoftwareRenderer::GetHeight() -{ - return m_height; -} - void Direct3DRMSoftwareRenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { memset(halDesc, 0, sizeof(D3DDEVICEDESC)); @@ -681,13 +684,13 @@ const char* Direct3DRMSoftwareRenderer::GetName() HRESULT Direct3DRMSoftwareRenderer::BeginFrame() { - if (!DDBackBuffer || !SDL_LockSurface(DDBackBuffer)) { + if (!m_renderedImage || !SDL_LockSurface(m_renderedImage)) { return DDERR_GENERIC; } ClearZBuffer(); - m_format = SDL_GetPixelFormatDetails(DDBackBuffer->format); - m_palette = SDL_GetSurfacePalette(DDBackBuffer); + m_format = SDL_GetPixelFormatDetails(m_renderedImage->format); + m_palette = SDL_GetSurfacePalette(m_renderedImage); m_bytesPerPixel = m_format->bits_per_pixel / 8; return DD_OK; @@ -700,6 +703,8 @@ void Direct3DRMSoftwareRenderer::EnableTransparency() void Direct3DRMSoftwareRenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) @@ -731,7 +736,84 @@ void Direct3DRMSoftwareRenderer::SubmitDraw( HRESULT Direct3DRMSoftwareRenderer::FinalizeFrame() { - SDL_UnlockSurface(DDBackBuffer); + SDL_UnlockSurface(m_renderedImage); return DD_OK; } + +void Direct3DRMSoftwareRenderer::Resize(int width, int height, const ViewportTransform& viewportTransform) +{ + m_viewportTransform = viewportTransform; + float aspect = static_cast(width) / height; + float virtualAspect = static_cast(m_virtualWidth) / m_virtualHeight; + + // Cap to virtual canvase for performance + if (aspect > virtualAspect) { + m_height = std::min(height, m_virtualHeight); + m_width = static_cast(m_height * aspect); + } + else { + m_width = std::min(width, m_virtualWidth); + m_height = static_cast(m_width / aspect); + } + + m_viewportTransform.scale = + std::min(static_cast(m_width) / m_virtualWidth, static_cast(m_height) / m_virtualHeight); + + m_viewportTransform.offsetX = (m_width - (m_virtualWidth * m_viewportTransform.scale)) / 2.0f; + m_viewportTransform.offsetY = (m_height - (m_virtualHeight * m_viewportTransform.scale)) / 2.0f; + + if (m_renderedImage) { + SDL_DestroySurface(m_renderedImage); + } + m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); + + if (m_uploadBuffer) { + SDL_DestroyTexture(m_uploadBuffer); + } + m_uploadBuffer = + SDL_CreateTexture(m_renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, m_width, m_height); + + m_zBuffer.resize(m_width * m_height); +} + +void Direct3DRMSoftwareRenderer::Clear(float r, float g, float b) +{ + SDL_Rect rect = {0, 0, m_renderedImage->w, m_renderedImage->h}; + const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_renderedImage->format); + Uint32 color = SDL_MapRGB(details, m_palette, r * 255, g * 255, b * 255); + SDL_FillSurfaceRect(m_renderedImage, &rect, color); +} + +void Direct3DRMSoftwareRenderer::Flip() +{ + SDL_UpdateTexture(m_uploadBuffer, nullptr, m_renderedImage->pixels, m_renderedImage->pitch); + SDL_RenderTexture(m_renderer, m_uploadBuffer, nullptr, nullptr); + SDL_RenderPresent(m_renderer); +} + +void Direct3DRMSoftwareRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) +{ + SDL_Surface* surface = m_textures[textureId].cached; + SDL_UnlockSurface(surface); + SDL_Rect centeredRect = { + static_cast(dstRect.x * m_viewportTransform.scale + m_viewportTransform.offsetX), + static_cast(dstRect.y * m_viewportTransform.scale + m_viewportTransform.offsetY), + static_cast(dstRect.w * m_viewportTransform.scale), + static_cast(dstRect.h * m_viewportTransform.scale), + }; + SDL_BlitSurfaceScaled(surface, &srcRect, m_renderedImage, ¢eredRect, SDL_SCALEMODE_LINEAR); + SDL_LockSurface(surface); +} + +void Direct3DRMSoftwareRenderer::Download(SDL_Surface* target) +{ + SDL_Rect srcRect = { + static_cast(m_viewportTransform.offsetX), + static_cast(m_viewportTransform.offsetY), + static_cast(m_virtualWidth * m_viewportTransform.scale), + static_cast(m_virtualHeight * m_viewportTransform.scale), + }; + + SDL_BlitSurfaceScaled(m_renderedImage, &srcRect, target, nullptr, SDL_SCALEMODE_LINEAR); +} diff --git a/miniwin/src/d3drm/d3drm.cpp b/miniwin/src/d3drm/d3drm.cpp index 01375d8c..e8df37b1 100644 --- a/miniwin/src/d3drm/d3drm.cpp +++ b/miniwin/src/d3drm/d3drm.cpp @@ -127,7 +127,7 @@ HRESULT Direct3DRMImpl::CreateDeviceFromD3D( { auto renderer = static_cast(d3dDevice); *outDevice = static_cast( - new Direct3DRMDevice2Impl(renderer->GetWidth(), renderer->GetHeight(), renderer) + new Direct3DRMDevice2Impl(renderer->GetVirtualWidth(), renderer->GetVirtualHeight(), renderer) ); return DD_OK; } @@ -143,26 +143,25 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface( DDSDesc.dwSize = sizeof(DDSURFACEDESC); surface->GetSurfaceDesc(&DDSDesc); - Direct3DRMRenderer* renderer; if (SDL_memcmp(&guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) { - renderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { - renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); } #ifdef USE_OPENGLES2 else if (SDL_memcmp(&guid, &OpenGLES2_GUID, sizeof(GUID)) == 0) { - renderer = OpenGLES2Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = OpenGLES2Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif #ifdef USE_OPENGL1 else if (SDL_memcmp(&guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { - renderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = 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); + DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif else { @@ -170,7 +169,7 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface( return E_NOINTERFACE; } *outDevice = - static_cast(new Direct3DRMDevice2Impl(DDSDesc.dwWidth, DDSDesc.dwHeight, renderer)); + static_cast(new Direct3DRMDevice2Impl(DDSDesc.dwWidth, DDSDesc.dwHeight, DDRenderer)); return DD_OK; } @@ -181,9 +180,8 @@ HRESULT Direct3DRMImpl::CreateTexture(D3DRMIMAGE* image, IDirect3DRMTexture2** o } HRESULT Direct3DRMImpl::CreateTextureFromSurface(LPDIRECTDRAWSURFACE surface, IDirect3DRMTexture2** outTexture) - { - *outTexture = static_cast(new Direct3DRMTextureImpl(surface)); + *outTexture = static_cast(new Direct3DRMTextureImpl(surface, true)); return DD_OK; } diff --git a/miniwin/src/d3drm/d3drmdevice.cpp b/miniwin/src/d3drm/d3drmdevice.cpp index be850823..f6b2f9f7 100644 --- a/miniwin/src/d3drm/d3drmdevice.cpp +++ b/miniwin/src/d3drm/d3drmdevice.cpp @@ -11,8 +11,9 @@ #include Direct3DRMDevice2Impl::Direct3DRMDevice2Impl(DWORD width, DWORD height, Direct3DRMRenderer* renderer) - : m_width(width), m_height(height), m_renderer(renderer), m_viewports(new Direct3DRMViewportArrayImpl) + : m_virtualWidth(width), m_virtualHeight(height), m_renderer(renderer), m_viewports(new Direct3DRMViewportArrayImpl) { + Resize(); } Direct3DRMDevice2Impl::~Direct3DRMDevice2Impl() @@ -43,16 +44,6 @@ HRESULT Direct3DRMDevice2Impl::QueryInterface(const GUID& riid, void** ppvObject return E_NOINTERFACE; } -DWORD Direct3DRMDevice2Impl::GetWidth() -{ - return m_width; -} - -DWORD Direct3DRMDevice2Impl::GetHeight() -{ - return m_height; -} - HRESULT Direct3DRMDevice2Impl::SetBufferCount(int count) { MINIWIN_NOT_IMPLEMENTED(); @@ -133,7 +124,9 @@ HRESULT Direct3DRMDevice2Impl::Update() HRESULT Direct3DRMDevice2Impl::AddViewport(IDirect3DRMViewport* viewport) { - return m_viewports->AddElement(viewport); + HRESULT status = m_viewports->AddElement(viewport); + Resize(); + return status; } HRESULT Direct3DRMDevice2Impl::GetViewports(IDirect3DRMViewportArray** ppViewportArray) @@ -143,7 +136,53 @@ HRESULT Direct3DRMDevice2Impl::GetViewports(IDirect3DRMViewportArray** ppViewpor return DD_OK; } +ViewportTransform CalculateViewportTransform(int virtualW, int virtualH, int windowW, int windowH) +{ + float scaleX = (float) windowW / virtualW; + float scaleY = (float) windowH / virtualH; + float scale = (scaleX < scaleY) ? scaleX : scaleY; + + float viewportW = virtualW * scale; + float viewportH = virtualH * scale; + + float offsetX = (windowW - viewportW) * 0.5f; + float offsetY = (windowH - viewportH) * 0.5f; + + return {scale, offsetX, offsetY}; +} + +void Direct3DRMDevice2Impl::Resize() +{ + int width, height; + SDL_GetWindowSizeInPixels(DDWindow, &width, &height); + m_viewportTransform = CalculateViewportTransform(m_virtualWidth, m_virtualHeight, width, height); + m_renderer->Resize(width, height, m_viewportTransform); + for (int i = 0; i < m_viewports->GetSize(); i++) { + IDirect3DRMViewport* viewport; + m_viewports->GetElement(i, &viewport); + static_cast(viewport)->UpdateProjectionMatrix(); + } +} + bool Direct3DRMDevice2Impl::ConvertEventToRenderCoordinates(SDL_Event* event) { - return m_renderer->ConvertEventToRenderCoordinates(event); + switch (event->type) { + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: { + Resize(); + break; + } + case SDL_EVENT_MOUSE_MOTION: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: { + int rawX = event->motion.x; + int rawY = event->motion.y; + float x = (rawX - m_viewportTransform.offsetX) / m_viewportTransform.scale; + float y = (rawY - m_viewportTransform.offsetY) / m_viewportTransform.scale; + event->motion.x = static_cast(x); + event->motion.y = static_cast(y); + break; + } break; + } + + return true; } diff --git a/miniwin/src/d3drm/d3drmtexture.cpp b/miniwin/src/d3drm/d3drmtexture.cpp index c0a2ad59..a5d63588 100644 --- a/miniwin/src/d3drm/d3drmtexture.cpp +++ b/miniwin/src/d3drm/d3drmtexture.cpp @@ -6,8 +6,19 @@ Direct3DRMTextureImpl::Direct3DRMTextureImpl(D3DRMIMAGE* image) MINIWIN_NOT_IMPLEMENTED(); } -Direct3DRMTextureImpl::Direct3DRMTextureImpl(IDirectDrawSurface* surface) : m_surface(surface) +Direct3DRMTextureImpl::Direct3DRMTextureImpl(IDirectDrawSurface* surface, bool holdsRef) + : m_surface(surface), m_holdsRef(holdsRef) { + if (holdsRef && m_surface) { + m_surface->AddRef(); + } +} + +Direct3DRMTextureImpl::~Direct3DRMTextureImpl() +{ + if (m_holdsRef && m_surface) { + m_surface->Release(); + } } HRESULT Direct3DRMTextureImpl::QueryInterface(const GUID& riid, void** ppvObject) diff --git a/miniwin/src/d3drm/d3drmviewport.cpp b/miniwin/src/d3drm/d3drmviewport.cpp index 933cbbcd..e3bb79d3 100644 --- a/miniwin/src/d3drm/d3drmviewport.cpp +++ b/miniwin/src/d3drm/d3drmviewport.cpp @@ -15,7 +15,7 @@ #include Direct3DRMViewportImpl::Direct3DRMViewportImpl(DWORD width, DWORD height, Direct3DRMRenderer* renderer) - : m_width(width), m_height(height), m_renderer(renderer) + : m_virtualWidth(width), m_virtualHeight(height), m_renderer(renderer) { } @@ -151,7 +151,7 @@ void Direct3DRMViewportImpl::CollectLightsFromFrame( void Direct3DRMViewportImpl::BuildViewFrustumPlanes() { - float aspect = (float) m_width / (float) m_height; + float aspect = (float) m_renderer->GetWidth() / (float) m_renderer->GetHeight(); float tanFovX = m_field; float tanFovY = m_field / aspect; @@ -268,18 +268,22 @@ void Direct3DRMViewportImpl::CollectMeshesFromFrame(IDirect3DRMFrame* frame, D3D if (appearance.color.a != 255) { m_deferredDraws.push_back( {m_renderer->GetMeshId(mesh, &meshGroup), + {}, {}, {}, appearance, CalculateDepth(m_viewProjectionwMatrix, worldMatrix)} ); memcpy(m_deferredDraws.back().modelViewMatrix, modelViewMatrix, sizeof(D3DRMMATRIX4D)); + memcpy(m_deferredDraws.back().worldMatrix, worldMatrix, sizeof(D3DRMMATRIX4D)); memcpy(m_deferredDraws.back().normalMatrix, worldMatrixInvert, sizeof(Matrix3x3)); } else { m_renderer->SubmitDraw( m_renderer->GetMeshId(mesh, &meshGroup), modelViewMatrix, + worldMatrix, + m_viewMatrix, worldMatrixInvert, appearance ); @@ -325,7 +329,14 @@ HRESULT Direct3DRMViewportImpl::RenderScene() ); m_renderer->EnableTransparency(); for (const DeferredDrawCommand& cmd : m_deferredDraws) { - m_renderer->SubmitDraw(cmd.meshId, cmd.modelViewMatrix, cmd.normalMatrix, cmd.appearance); + m_renderer->SubmitDraw( + cmd.meshId, + cmd.modelViewMatrix, + cmd.worldMatrix, + m_viewMatrix, + cmd.normalMatrix, + cmd.appearance + ); } m_deferredDraws.clear(); @@ -349,16 +360,14 @@ HRESULT Direct3DRMViewportImpl::ForceUpdate(int x, int y, int w, int h) HRESULT Direct3DRMViewportImpl::Clear() { - if (!DDBackBuffer) { + if (!m_renderer) { return DDERR_GENERIC; } uint8_t r = (m_backgroundColor >> 16) & 0xFF; uint8_t g = (m_backgroundColor >> 8) & 0xFF; uint8_t b = m_backgroundColor & 0xFF; - - Uint32 color = SDL_MapRGB(SDL_GetPixelFormatDetails(DDBackBuffer->format), nullptr, r, g, b); - SDL_FillSurfaceRect(DDBackBuffer, nullptr, color); + m_renderer->Clear(r / 255.0f, g / 255.0f, b / 255.0f); return DD_OK; } @@ -427,13 +436,24 @@ HRESULT Direct3DRMViewportImpl::SetField(D3DVALUE field) void Direct3DRMViewportImpl::UpdateProjectionMatrix() { - float aspect = (float) m_width / (float) m_height; - float f = m_front / m_field; + float virtualAspect = (float) m_virtualWidth / (float) m_virtualHeight; + float windowAspect = (float) m_renderer->GetWidth() / (float) m_renderer->GetHeight(); + + float base_f = m_front / m_field; + float f_v = base_f * virtualAspect; + float f_h = base_f; + if (windowAspect >= virtualAspect) { + f_h *= virtualAspect / windowAspect; + } + else { + f_v *= windowAspect / virtualAspect; + } + float depth = m_back - m_front; D3DRMMATRIX4D projection = { - {f, 0, 0, 0}, - {0, f * aspect, 0, 0}, + {f_h, 0, 0, 0}, + {0, f_v, 0, 0}, {0, 0, m_back / depth, 1}, {0, 0, (-m_front * m_back) / depth, 0}, }; @@ -442,8 +462,8 @@ void Direct3DRMViewportImpl::UpdateProjectionMatrix() m_renderer->SetProjection(projection, m_front, m_back); D3DRMMATRIX4D inverseProjectionMatrix = { - {1.0f / f, 0, 0, 0}, - {0, 1.0f / (f * aspect), 0, 0}, + {1.0f / f_h, 0, 0, 0}, + {0, 1.0f / f_v, 0, 0}, {0, 0, 0, depth / (-m_front * m_back)}, {0, 0, 1, -(m_back / depth) * depth / (-m_front * m_back)}, }; @@ -455,16 +475,6 @@ D3DVALUE Direct3DRMViewportImpl::GetField() return m_field; } -DWORD Direct3DRMViewportImpl::GetWidth() -{ - return m_width; -} - -DWORD Direct3DRMViewportImpl::GetHeight() -{ - return m_height; -} - inline float FromNDC(float ndcCoord, float dim) { return (ndcCoord * 0.5f + 0.5f) * dim; @@ -492,8 +502,8 @@ HRESULT Direct3DRMViewportImpl::Transform(D3DRMVECTOR4D* screen, D3DVECTOR* worl float ndcX = projVec.x * invW; float ndcY = projVec.y * invW; - screen->x = FromNDC(ndcX, m_width); - screen->y = FromNDC(-ndcY, m_height); // Y-flip + screen->x = FromNDC(ndcX, m_virtualWidth); + screen->y = FromNDC(-ndcY, m_virtualHeight); // Y-flip // Undo perspective divide for screen-space coords screen->x *= projVec.z; @@ -509,8 +519,8 @@ HRESULT Direct3DRMViewportImpl::InverseTransform(D3DVECTOR* world, D3DRMVECTOR4D float screenY = screen->y / screen->w; // Convert screen coordinates to NDC - float ndcX = screenX / m_width * 2.0f - 1.0f; - float ndcY = 1.0f - (screenY / m_height) * 2.0f; + float ndcX = screenX / m_virtualWidth * 2.0f - 1.0f; + float ndcY = 1.0f - (screenY / m_virtualHeight) * 2.0f; D3DRMVECTOR4D clipVec = {ndcX * screen->w, ndcY * screen->w, screen->z, screen->w}; @@ -753,13 +763,13 @@ HRESULT Direct3DRMViewportImpl::Pick(float x, float y, LPDIRECT3DRMPICKEDARRAY* Ray pickRay = BuildPickingRay( x, y, - m_width, - m_height, + m_virtualWidth, + m_virtualHeight, m_camera, m_front, m_back, m_field, - (float) m_width / (float) m_height + (float) m_virtualWidth / (float) m_virtualHeight ); std::function&)> recurse; diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index bd1541a3..2765a2a1 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -23,9 +23,7 @@ #include SDL_Window* DDWindow; -SDL_Surface* DDBackBuffer; -FrameBufferImpl* DDFrameBuffer; -SDL_Renderer* DDRenderer; +Direct3DRMRenderer* DDRenderer; HRESULT DirectDrawImpl::QueryInterface(const GUID& riid, void** ppvObject) { @@ -82,13 +80,13 @@ HRESULT DirectDrawImpl::CreateSurface( return DD_OK; } if ((lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE) == DDSCAPS_PRIMARYSURFACE) { - DDFrameBuffer = new FrameBufferImpl(); - *lplpDDSurface = static_cast(DDFrameBuffer); + m_frameBuffer = new FrameBufferImpl(m_virtualWidth, m_virtualHeight); + *lplpDDSurface = static_cast(m_frameBuffer); return DD_OK; } if ((lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_3DDEVICE) == DDSCAPS_3DDEVICE) { - DDFrameBuffer->AddRef(); - *lplpDDSurface = static_cast(DDFrameBuffer); + m_frameBuffer->AddRef(); + *lplpDDSurface = static_cast(m_frameBuffer); return DD_OK; } if ((lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_TEXTURE) == DDSCAPS_TEXTURE) { @@ -100,7 +98,7 @@ HRESULT DirectDrawImpl::CreateSurface( #ifdef MINIWIN_PIXELFORMAT format = MINIWIN_PIXELFORMAT; #else - format = SDL_PIXELFORMAT_RGBA8888; + format = SDL_PIXELFORMAT_RGBA32; #endif if ((lpDDSurfaceDesc->dwFlags & DDSD_PIXELFORMAT) == DDSD_PIXELFORMAT) { if ((lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB) == DDPF_RGB) { @@ -284,6 +282,12 @@ HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags) { SDL_Window* sdlWindow = reinterpret_cast(hWnd); + if (m_virtualWidth == 0 || m_virtualHeight == 0) { + if (!SDL_GetWindowSize(sdlWindow, &m_virtualWidth, &m_virtualHeight)) { + SDL_LogError(LOG_CATEGORY_MINIWIN, "SDL_GetWindowSizeInPixels: %s", SDL_GetError()); + } + } + if (sdlWindow) { bool fullscreen; if ((dwFlags & DDSCL_NORMAL) == DDSCL_NORMAL) { @@ -302,15 +306,14 @@ HRESULT DirectDrawImpl::SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags) #endif } DDWindow = sdlWindow; - DDRenderer = SDL_CreateRenderer(DDWindow, NULL); - SDL_PropertiesID prop = SDL_GetRendererProperties(DDRenderer); - SDL_SetRenderLogicalPresentation(DDRenderer, 640, 480, SDL_LOGICAL_PRESENTATION_LETTERBOX); } return DD_OK; } HRESULT DirectDrawImpl::SetDisplayMode(DWORD dwWidth, DWORD dwHeight, DWORD dwBPP) { + m_virtualWidth = dwWidth; + m_virtualHeight = dwHeight; return DD_OK; } @@ -325,33 +328,32 @@ HRESULT DirectDrawImpl::CreateDevice( DDSDesc.dwSize = sizeof(DDSURFACEDESC); pBackBuffer->GetSurfaceDesc(&DDSDesc); - Direct3DRMRenderer* renderer; if (SDL_memcmp(&guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) { - renderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #ifdef USE_OPENGLES2 else if (SDL_memcmp(&guid, &OpenGLES2_GUID, sizeof(GUID)) == 0) { - renderer = OpenGLES2Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = OpenGLES2Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif #ifdef USE_OPENGL1 else if (SDL_memcmp(&guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { - renderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = 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); + DDRenderer = DirectX9Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { - renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); + DDRenderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized"); return E_NOINTERFACE; } - *ppDirect3DDevice = static_cast(renderer); + *ppDirect3DDevice = static_cast(DDRenderer); return DD_OK; } diff --git a/miniwin/src/ddraw/ddsurface.cpp b/miniwin/src/ddraw/ddsurface.cpp index 59c4569a..de250dea 100644 --- a/miniwin/src/ddraw/ddsurface.cpp +++ b/miniwin/src/ddraw/ddsurface.cpp @@ -1,3 +1,4 @@ +#include "d3drmtexture_impl.h" #include "ddpalette_impl.h" #include "ddraw_impl.h" #include "ddsurface_impl.h" @@ -19,6 +20,9 @@ DirectDrawSurfaceImpl::~DirectDrawSurfaceImpl() if (m_palette) { m_palette->Release(); } + if (m_texture) { + m_texture->Release(); + } } // IUnknown interface @@ -69,6 +73,9 @@ HRESULT DirectDrawSurfaceImpl::Blt( if (blitSource != other->m_surface) { SDL_DestroySurface(blitSource); } + if (m_texture) { + m_texture->Changed(TRUE, FALSE); + } return DD_OK; } @@ -203,6 +210,10 @@ HRESULT DirectDrawSurfaceImpl::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) MINIWIN_NOT_IMPLEMENTED(); } + if (m_texture) { + m_texture->Changed(FALSE, TRUE); + } + if (m_palette) { m_palette->Release(); } @@ -216,5 +227,16 @@ HRESULT DirectDrawSurfaceImpl::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) HRESULT DirectDrawSurfaceImpl::Unlock(LPVOID lpSurfaceData) { SDL_UnlockSurface(m_surface); + if (m_texture) { + m_texture->Changed(TRUE, FALSE); + } return DD_OK; } + +IDirect3DRMTexture2* DirectDrawSurfaceImpl::ToTexture() +{ + if (!m_texture) { + m_texture = new Direct3DRMTextureImpl(this, false); + } + return m_texture; +} diff --git a/miniwin/src/ddraw/framebuffer.cpp b/miniwin/src/ddraw/framebuffer.cpp index eee8b7cb..eb21abef 100644 --- a/miniwin/src/ddraw/framebuffer.cpp +++ b/miniwin/src/ddraw/framebuffer.cpp @@ -6,21 +6,15 @@ #include -FrameBufferImpl::FrameBufferImpl() +FrameBufferImpl::FrameBufferImpl(DWORD virtualWidth, DWORD virtualHeight) + : m_virtualWidth(virtualWidth), m_virtualHeight(virtualHeight) { - int width, height; - SDL_GetRenderOutputSize(DDRenderer, &width, &height); - DDBackBuffer = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA8888); - if (!DDBackBuffer) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create surface: %s", SDL_GetError()); - } - m_uploadBuffer = - SDL_CreateTexture(DDRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height); + m_transferBuffer = new DirectDrawSurfaceImpl(m_virtualWidth, m_virtualHeight, SDL_PIXELFORMAT_RGBA32); } FrameBufferImpl::~FrameBufferImpl() { - SDL_DestroySurface(DDBackBuffer); + m_transferBuffer->Release(); if (m_palette) { m_palette->Release(); } @@ -51,44 +45,30 @@ HRESULT FrameBufferImpl::Blt( LPDDBLTFX lpDDBltFx ) { + if (!DDRenderer) { + return DDERR_GENERIC; + } if (dynamic_cast(lpDDSrcSurface) == this) { return Flip(nullptr, DDFLIP_WAIT); } if ((dwFlags & DDBLT_COLORFILL) == DDBLT_COLORFILL) { - SDL_Rect rect = {0, 0, DDBackBuffer->w, DDBackBuffer->h}; - const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(DDBackBuffer->format); Uint8 r = (lpDDBltFx->dwFillColor >> 16) & 0xFF; Uint8 g = (lpDDBltFx->dwFillColor >> 8) & 0xFF; Uint8 b = lpDDBltFx->dwFillColor & 0xFF; - DirectDrawPaletteImpl* ddPal = static_cast(m_palette); - SDL_Palette* sdlPalette = ddPal ? ddPal->m_palette : nullptr; - Uint32 color = SDL_MapRGB(details, sdlPalette, r, g, b); - SDL_FillSurfaceRect(DDBackBuffer, &rect, color); + DDRenderer->Clear(r / 255.0f, g / 255.0f, b / 255.0f); return DD_OK; } - auto other = static_cast(lpDDSrcSurface); - if (!other) { + auto surface = static_cast(lpDDSrcSurface); + if (!surface) { return DDERR_GENERIC; } - SDL_Rect srcRect = lpSrcRect ? ConvertRect(lpSrcRect) : SDL_Rect{0, 0, other->m_surface->w, other->m_surface->h}; - SDL_Rect dstRect = lpDestRect ? ConvertRect(lpDestRect) : SDL_Rect{0, 0, DDBackBuffer->w, DDBackBuffer->h}; + Uint32 textureId = DDRenderer->GetTextureId(surface->ToTexture()); + SDL_Rect srcRect = + lpSrcRect ? ConvertRect(lpSrcRect) : SDL_Rect{0, 0, surface->m_surface->w, surface->m_surface->h}; + SDL_Rect dstRect = + lpDestRect ? ConvertRect(lpDestRect) : SDL_Rect{0, 0, (int) m_virtualWidth, (int) m_virtualHeight}; + DDRenderer->Draw2DImage(textureId, srcRect, dstRect); - SDL_Surface* blitSource = other->m_surface; - - if (other->m_surface->format != DDBackBuffer->format) { - blitSource = SDL_ConvertSurface(other->m_surface, DDBackBuffer->format); - if (!blitSource) { - return DDERR_GENERIC; - } - } - - if (!SDL_BlitSurfaceScaled(blitSource, &srcRect, DDBackBuffer, &dstRect, SDL_SCALEMODE_NEAREST)) { - return DDERR_GENERIC; - } - - if (blitSource != other->m_surface) { - SDL_DestroySurface(blitSource); - } return DD_OK; } @@ -100,20 +80,20 @@ HRESULT FrameBufferImpl::BltFast( DDBltFastFlags dwTrans ) { - RECT destRect = { - (int) dwX, - (int) dwY, - (int) (lpSrcRect->right - lpSrcRect->left + dwX), - (int) (lpSrcRect->bottom - lpSrcRect->top + dwY) - }; + auto surface = static_cast(lpDDSrcSurface); + int width = lpSrcRect ? (lpSrcRect->right - lpSrcRect->left) : surface->m_surface->w; + int height = lpSrcRect ? (lpSrcRect->bottom - lpSrcRect->top) : surface->m_surface->h; + RECT destRect = {(int) dwX, (int) dwY, (int) (dwX + width), (int) (dwY + height)}; + return Blt(&destRect, lpDDSrcSurface, lpSrcRect, DDBLT_NONE, nullptr); } HRESULT FrameBufferImpl::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DDFlipFlags dwFlags) { - SDL_UpdateTexture(m_uploadBuffer, nullptr, DDBackBuffer->pixels, DDBackBuffer->pitch); - SDL_RenderTexture(DDRenderer, m_uploadBuffer, nullptr, nullptr); - SDL_RenderPresent(DDRenderer); + if (!DDRenderer) { + return DDERR_GENERIC; + } + DDRenderer->Flip(); return DD_OK; } @@ -146,7 +126,7 @@ HRESULT FrameBufferImpl::GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) { memset(lpDDPixelFormat, 0, sizeof(*lpDDPixelFormat)); lpDDPixelFormat->dwFlags = DDPF_RGB; - const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(DDBackBuffer->format); + const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_transferBuffer->m_surface->format); if (details->bits_per_pixel == 8) { lpDDPixelFormat->dwFlags |= DDPF_PALETTEINDEXED8; } @@ -163,25 +143,28 @@ HRESULT FrameBufferImpl::GetSurfaceDesc(LPDDSURFACEDESC lpDDSurfaceDesc) lpDDSurfaceDesc->dwFlags = DDSD_PIXELFORMAT; GetPixelFormat(&lpDDSurfaceDesc->ddpfPixelFormat); lpDDSurfaceDesc->dwFlags |= DDSD_WIDTH | DDSD_HEIGHT; - lpDDSurfaceDesc->dwWidth = DDBackBuffer->w; - lpDDSurfaceDesc->dwHeight = DDBackBuffer->h; - return DD_OK; -} - -HRESULT FrameBufferImpl::IsLost() -{ + lpDDSurfaceDesc->dwWidth = m_transferBuffer->m_surface->w; + lpDDSurfaceDesc->dwHeight = m_transferBuffer->m_surface->h; return DD_OK; } HRESULT FrameBufferImpl::Lock(LPRECT lpDestRect, DDSURFACEDESC* lpDDSurfaceDesc, DDLockFlags dwFlags, HANDLE hEvent) { - if (!SDL_LockSurface(DDBackBuffer)) { + if (!DDRenderer) { return DDERR_GENERIC; } + if ((dwFlags & DDLOCK_WRITEONLY) == DDLOCK_WRITEONLY) { + SDL_Rect rect = {0, 0, m_transferBuffer->m_surface->w, m_transferBuffer->m_surface->h}; + const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_transferBuffer->m_surface->format); + SDL_Palette* palette = m_palette ? static_cast(m_palette)->m_palette : nullptr; + Uint32 color = SDL_MapRGBA(details, palette, 0, 0, 0, 0); + SDL_FillSurfaceRect(m_transferBuffer->m_surface, &rect, color); + } + else { + DDRenderer->Download(m_transferBuffer->m_surface); + } - GetSurfaceDesc(lpDDSurfaceDesc); - lpDDSurfaceDesc->lpSurface = DDBackBuffer->pixels; - lpDDSurfaceDesc->lPitch = DDBackBuffer->pitch; + m_transferBuffer->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); return DD_OK; } @@ -198,11 +181,6 @@ HRESULT FrameBufferImpl::Restore() return DD_OK; } -HRESULT FrameBufferImpl::SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper) -{ - return DD_OK; -} - HRESULT FrameBufferImpl::SetColorKey(DDColorKeyFlags dwFlags, LPDDCOLORKEY lpDDColorKey) { MINIWIN_NOT_IMPLEMENTED(); @@ -211,7 +189,7 @@ HRESULT FrameBufferImpl::SetColorKey(DDColorKeyFlags dwFlags, LPDDCOLORKEY lpDDC HRESULT FrameBufferImpl::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) { - if (DDBackBuffer->format != SDL_PIXELFORMAT_INDEX8) { + if (m_transferBuffer->m_surface->format != SDL_PIXELFORMAT_INDEX8) { MINIWIN_NOT_IMPLEMENTED(); } @@ -220,13 +198,15 @@ HRESULT FrameBufferImpl::SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) } m_palette = lpDDPalette; - SDL_SetSurfacePalette(DDBackBuffer, ((DirectDrawPaletteImpl*) m_palette)->m_palette); + SDL_SetSurfacePalette(m_transferBuffer->m_surface, ((DirectDrawPaletteImpl*) m_palette)->m_palette); m_palette->AddRef(); return DD_OK; } HRESULT FrameBufferImpl::Unlock(LPVOID lpSurfaceData) { - SDL_UnlockSurface(DDBackBuffer); + m_transferBuffer->Unlock(lpSurfaceData); + BltFast(0, 0, m_transferBuffer, nullptr, DDBLTFAST_WAIT); + return DD_OK; } diff --git a/miniwin/src/internal/d3drmdevice_impl.h b/miniwin/src/internal/d3drmdevice_impl.h index ad30f75a..daab564a 100644 --- a/miniwin/src/internal/d3drmdevice_impl.h +++ b/miniwin/src/internal/d3drmdevice_impl.h @@ -11,8 +11,8 @@ struct Direct3DRMDevice2Impl : public Direct3DRMObjectBaseImpl vertices; std::vector indices; diff --git a/miniwin/src/internal/d3drmrenderer.h b/miniwin/src/internal/d3drmrenderer.h index e1e7e558..e76f0759 100644 --- a/miniwin/src/internal/d3drmrenderer.h +++ b/miniwin/src/internal/d3drmrenderer.h @@ -26,8 +26,6 @@ struct Plane { float d; }; -extern SDL_Renderer* DDRenderer; - class Direct3DRMRenderer : public IDirect3DDevice2 { public: virtual void PushLights(const SceneLight* vertices, size_t count) = 0; @@ -35,8 +33,10 @@ class Direct3DRMRenderer : public IDirect3DDevice2 { virtual void SetFrustumPlanes(const Plane* frustumPlanes) = 0; virtual Uint32 GetTextureId(IDirect3DRMTexture* texture) = 0; virtual Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) = 0; - virtual DWORD GetWidth() = 0; - virtual DWORD GetHeight() = 0; + int GetWidth() { return m_width; } + int GetHeight() { return m_height; } + int GetVirtualWidth() { return m_virtualWidth; } + int GetVirtualHeight() { return m_virtualHeight; } virtual void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) = 0; virtual const char* GetName() = 0; virtual HRESULT BeginFrame() = 0; @@ -44,13 +44,20 @@ class Direct3DRMRenderer : public IDirect3DDevice2 { virtual void SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) = 0; virtual HRESULT FinalizeFrame() = 0; + virtual void Resize(int width, int height, const ViewportTransform& viewportTransform) = 0; + virtual void Clear(float r, float g, float b) = 0; + virtual void Flip() = 0; + virtual void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) = 0; + virtual void Download(SDL_Surface* target) = 0; - bool ConvertEventToRenderCoordinates(SDL_Event* event) - { - return SDL_ConvertEventToRenderCoordinates(DDRenderer, event); - } +protected: + int m_width, m_height; + int m_virtualWidth, m_virtualHeight; + ViewportTransform m_viewportTransform; }; diff --git a/miniwin/src/internal/d3drmrenderer_directx9.h b/miniwin/src/internal/d3drmrenderer_directx9.h index 8df63424..b7badb03 100644 --- a/miniwin/src/internal/d3drmrenderer_directx9.h +++ b/miniwin/src/internal/d3drmrenderer_directx9.h @@ -20,8 +20,6 @@ class DirectX9Renderer : public Direct3DRMRenderer { 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; @@ -29,17 +27,23 @@ class DirectX9Renderer : public Direct3DRMRenderer { void SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) override; HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; + void Download(SDL_Surface* target) 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; diff --git a/miniwin/src/internal/d3drmrenderer_opengl1.h b/miniwin/src/internal/d3drmrenderer_opengl1.h index 763509d8..71c0ea82 100644 --- a/miniwin/src/internal/d3drmrenderer_opengl1.h +++ b/miniwin/src/internal/d3drmrenderer_opengl1.h @@ -42,7 +42,7 @@ struct GLMeshCacheEntry { class OpenGL1Renderer : public Direct3DRMRenderer { public: static Direct3DRMRenderer* Create(DWORD width, DWORD height); - OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint fbo, GLuint colorTex, GLuint depthRb); + OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext context); ~OpenGL1Renderer() override; void PushLights(const SceneLight* lightsArray, size_t count) override; @@ -50,8 +50,6 @@ class OpenGL1Renderer : public Direct3DRMRenderer { 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; @@ -59,10 +57,17 @@ class OpenGL1Renderer : public Direct3DRMRenderer { void SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) override; HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; + void Download(SDL_Surface* target) override; private: void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); @@ -72,13 +77,11 @@ class OpenGL1Renderer : public Direct3DRMRenderer { std::vector m_meshs; D3DRMMATRIX4D m_projection; SDL_Surface* m_renderedImage; - DWORD m_width, m_height; bool m_useVBOs; + bool m_dirty = false; std::vector m_lights; SDL_GLContext m_context; - GLuint m_fbo; - GLuint m_colorTex; - GLuint m_depthRb; + ViewportTransform m_viewportTransform; }; inline static void OpenGL1Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) diff --git a/miniwin/src/internal/d3drmrenderer_opengles2.h b/miniwin/src/internal/d3drmrenderer_opengles2.h index 94ff70ee..7ea1ed9a 100644 --- a/miniwin/src/internal/d3drmrenderer_opengles2.h +++ b/miniwin/src/internal/d3drmrenderer_opengles2.h @@ -14,6 +14,8 @@ struct GLES2TextureCacheEntry { IDirect3DRMTexture* texture; Uint32 version; GLuint glTextureId; + uint16_t width; + uint16_t height; }; struct GLES2MeshCacheEntry { @@ -31,15 +33,7 @@ struct GLES2MeshCacheEntry { class OpenGLES2Renderer : public Direct3DRMRenderer { public: static Direct3DRMRenderer* Create(DWORD width, DWORD height); - OpenGLES2Renderer( - DWORD width, - DWORD height, - SDL_GLContext context, - GLuint fbo, - GLuint colorTex, - GLuint vertexBuffer, - GLuint shaderProgram - ); + OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint shaderProgram); ~OpenGLES2Renderer() override; void PushLights(const SceneLight* lightsArray, size_t count) override; @@ -47,8 +41,6 @@ class OpenGLES2Renderer : public Direct3DRMRenderer { 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; @@ -56,10 +48,17 @@ class OpenGLES2Renderer : public Direct3DRMRenderer { void SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) override; HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; + void Download(SDL_Surface* target) override; private: void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); @@ -69,13 +68,11 @@ class OpenGLES2Renderer : public Direct3DRMRenderer { std::vector m_meshs; D3DRMMATRIX4D m_projection; SDL_Surface* m_renderedImage; - DWORD m_width, m_height; + bool m_dirty = false; std::vector m_lights; SDL_GLContext m_context; - GLuint m_fbo; - GLuint m_colorTex; - GLuint m_depthRb; GLuint m_shaderProgram; + ViewportTransform m_viewportTransform; }; inline static void OpenGLES2Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) diff --git a/miniwin/src/internal/d3drmrenderer_sdl3gpu.h b/miniwin/src/internal/d3drmrenderer_sdl3gpu.h index 3a20000e..25485b1f 100644 --- a/miniwin/src/internal/d3drmrenderer_sdl3gpu.h +++ b/miniwin/src/internal/d3drmrenderer_sdl3gpu.h @@ -51,8 +51,6 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - DWORD GetWidth() override; - DWORD GetHeight() override; void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; const char* GetName() override; HRESULT BeginFrame() override; @@ -60,10 +58,17 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { void SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) override; HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; + void Download(SDL_Surface* target) override; private: Direct3DRMSDL3GPURenderer( @@ -72,12 +77,13 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { SDL_GPUDevice* device, SDL_GPUGraphicsPipeline* opaquePipeline, SDL_GPUGraphicsPipeline* transparentPipeline, - SDL_GPUTexture* transferTexture, - SDL_GPUTexture* depthTexture, + SDL_GPUGraphicsPipeline* uiPipeline, SDL_GPUSampler* sampler, + SDL_GPUSampler* uiSampler, SDL_GPUTransferBuffer* uploadBuffer, - SDL_GPUTransferBuffer* downloadBuffer + int uploadBufferSize ); + void StartRenderPass(float r, float g, float b, bool clear); void WaitForPendingUpload(); void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); SDL_GPUTransferBuffer* GetUploadBuffer(size_t size); @@ -85,26 +91,29 @@ class Direct3DRMSDL3GPURenderer : public Direct3DRMRenderer { void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); SDL3MeshCache UploadMesh(const MeshGroup& meshGroup); - DWORD m_width; - DWORD m_height; + MeshGroup m_uiMesh; + SDL3MeshCache m_uiMeshCache; D3DVALUE m_front; D3DVALUE m_back; ViewportUniforms m_uniforms; FragmentShadingData m_fragmentShadingData; D3DDEVICEDESC m_desc; + D3DRMMATRIX4D m_projection; std::vector m_textures; std::vector m_meshs; SDL_GPUDevice* m_device; SDL_GPUGraphicsPipeline* m_opaquePipeline; SDL_GPUGraphicsPipeline* m_transparentPipeline; - SDL_GPUTexture* m_transferTexture; - SDL_GPUTexture* m_depthTexture; + SDL_GPUGraphicsPipeline* m_uiPipeline; + SDL_GPUTexture* m_transferTexture = nullptr; + SDL_GPUTexture* m_depthTexture = nullptr; SDL_GPUTexture* m_dummyTexture; int m_uploadBufferSize; SDL_GPUTransferBuffer* m_uploadBuffer; - SDL_GPUTransferBuffer* m_downloadBuffer; + SDL_GPUTransferBuffer* m_downloadBuffer = nullptr; SDL_GPUBuffer* m_vertexBuffer = nullptr; SDL_GPUSampler* m_sampler; + SDL_GPUSampler* m_uiSampler; SDL_GPUCommandBuffer* m_cmdbuf = nullptr; SDL_GPURenderPass* m_renderPass = nullptr; SDL_GPUFence* m_uploadFence = nullptr; diff --git a/miniwin/src/internal/d3drmrenderer_software.h b/miniwin/src/internal/d3drmrenderer_software.h index 6df2b4a4..444a80a3 100644 --- a/miniwin/src/internal/d3drmrenderer_software.h +++ b/miniwin/src/internal/d3drmrenderer_software.h @@ -27,13 +27,12 @@ struct MeshCache { class Direct3DRMSoftwareRenderer : public Direct3DRMRenderer { public: Direct3DRMSoftwareRenderer(DWORD width, DWORD height); + ~Direct3DRMSoftwareRenderer() override; void PushLights(const SceneLight* vertices, size_t count) override; Uint32 GetTextureId(IDirect3DRMTexture* texture) override; Uint32 GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetFrustumPlanes(const Plane* frustumPlanes) override; - DWORD GetWidth() override; - DWORD GetHeight() override; void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override; const char* GetName() override; HRESULT BeginFrame() override; @@ -41,10 +40,17 @@ class Direct3DRMSoftwareRenderer : public Direct3DRMRenderer { void SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, + const D3DRMMATRIX4D& worldMatrix, + const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) override; HRESULT FinalizeFrame() override; + void Resize(int width, int height, const ViewportTransform& viewportTransform) override; + void Clear(float r, float g, float b) override; + void Flip() override; + void Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) override; + void Download(SDL_Surface* target) override; private: void ClearZBuffer(); @@ -61,9 +67,10 @@ class Direct3DRMSoftwareRenderer : public Direct3DRMRenderer { void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); - DWORD m_width; - DWORD m_height; + SDL_Surface* m_renderedImage = nullptr; SDL_Palette* m_palette; + SDL_Texture* m_uploadBuffer = nullptr; + SDL_Renderer* m_renderer; const SDL_PixelFormatDetails* m_format; int m_bytesPerPixel; std::vector m_lights; diff --git a/miniwin/src/internal/d3drmtexture_impl.h b/miniwin/src/internal/d3drmtexture_impl.h index e81c3884..35ef7779 100644 --- a/miniwin/src/internal/d3drmtexture_impl.h +++ b/miniwin/src/internal/d3drmtexture_impl.h @@ -4,10 +4,12 @@ struct Direct3DRMTextureImpl : public Direct3DRMObjectBaseImpl { Direct3DRMTextureImpl(D3DRMIMAGE* image); - Direct3DRMTextureImpl(IDirectDrawSurface* surface); + Direct3DRMTextureImpl(IDirectDrawSurface* surface, bool holdsRef); + ~Direct3DRMTextureImpl() override; HRESULT QueryInterface(const GUID& riid, void** ppvObject) override; HRESULT Changed(BOOL pixels, BOOL palette) override; IDirectDrawSurface* m_surface = nullptr; Uint8 m_version = 0; + bool m_holdsRef; }; diff --git a/miniwin/src/internal/d3drmviewport_impl.h b/miniwin/src/internal/d3drmviewport_impl.h index c27af586..b23652d0 100644 --- a/miniwin/src/internal/d3drmviewport_impl.h +++ b/miniwin/src/internal/d3drmviewport_impl.h @@ -10,6 +10,7 @@ struct DeferredDrawCommand { DWORD meshId; D3DRMMATRIX4D modelViewMatrix; + D3DRMMATRIX4D worldMatrix; Matrix3x3 normalMatrix; Appearance appearance; float depth; @@ -36,24 +37,24 @@ struct Direct3DRMViewportImpl : public Direct3DRMObjectBaseImpl& lights); void CollectMeshesFromFrame(IDirect3DRMFrame* frame, D3DRMMATRIX4D parentMatrix); void BuildViewFrustumPlanes(); - void UpdateProjectionMatrix(); Direct3DRMRenderer* m_renderer; std::vector m_deferredDraws; D3DCOLOR m_backgroundColor = 0xFF000000; - DWORD m_width; - DWORD m_height; + DWORD m_virtualWidth; + DWORD m_virtualHeight; D3DRMMATRIX4D m_viewProjectionwMatrix; D3DRMMATRIX4D m_viewMatrix; D3DRMMATRIX4D m_projectionMatrix; diff --git a/miniwin/src/internal/ddraw_impl.h b/miniwin/src/internal/ddraw_impl.h index 7cefe2f6..f2604946 100644 --- a/miniwin/src/internal/ddraw_impl.h +++ b/miniwin/src/internal/ddraw_impl.h @@ -8,9 +8,7 @@ #include extern SDL_Window* DDWindow; -extern SDL_Surface* DDBackBuffer; -extern FrameBufferImpl* DDFrameBuffer; -extern SDL_Renderer* DDRenderer; +extern Direct3DRMRenderer* DDRenderer; inline static SDL_Rect ConvertRect(const RECT* r) { @@ -47,6 +45,11 @@ struct DirectDrawImpl : public IDirectDraw2, public IDirect3D2 { HRESULT CreateDevice(const GUID& guid, IDirectDrawSurface* pBackBuffer, IDirect3DDevice2** ppDirect3DDevice) override; HRESULT EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) override; + +private: + FrameBufferImpl* m_frameBuffer; + int m_virtualWidth = 0; + int m_virtualHeight = 0; }; HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context); diff --git a/miniwin/src/internal/ddsurface_impl.h b/miniwin/src/internal/ddsurface_impl.h index 4218e157..62fab18a 100644 --- a/miniwin/src/internal/ddsurface_impl.h +++ b/miniwin/src/internal/ddsurface_impl.h @@ -35,8 +35,11 @@ struct DirectDrawSurfaceImpl : public IDirectDrawSurface3 { HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) override; HRESULT Unlock(LPVOID lpSurfaceData) override; + IDirect3DRMTexture2* ToTexture(); + SDL_Surface* m_surface = nullptr; private: + IDirect3DRMTexture2* m_texture = nullptr; IDirectDrawPalette* m_palette = nullptr; }; diff --git a/miniwin/src/internal/framebuffer_impl.h b/miniwin/src/internal/framebuffer_impl.h index c2d9e3c8..5e40f7e8 100644 --- a/miniwin/src/internal/framebuffer_impl.h +++ b/miniwin/src/internal/framebuffer_impl.h @@ -5,7 +5,7 @@ #include struct FrameBufferImpl : public IDirectDrawSurface3 { - FrameBufferImpl(); + FrameBufferImpl(DWORD virtualWidth, DWORD virtualHeight); ~FrameBufferImpl() override; // IUnknown interface @@ -27,16 +27,18 @@ struct FrameBufferImpl : public IDirectDrawSurface3 { HRESULT GetPalette(LPDIRECTDRAWPALETTE* lplpDDPalette) override; HRESULT GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) override; HRESULT GetSurfaceDesc(DDSURFACEDESC* lpDDSurfaceDesc) override; - HRESULT IsLost() override; + HRESULT IsLost() override { return DD_OK; } HRESULT Lock(LPRECT lpDestRect, DDSURFACEDESC* lpDDSurfaceDesc, DDLockFlags dwFlags, HANDLE hEvent) override; HRESULT ReleaseDC(HDC hDC) override; HRESULT Restore() override; - HRESULT SetClipper(IDirectDrawClipper* lpDDClipper) override; + HRESULT SetClipper(IDirectDrawClipper* lpDDClipper) override { return DD_OK; } HRESULT SetColorKey(DDColorKeyFlags dwFlags, LPDDCOLORKEY lpDDColorKey) override; HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) override; HRESULT Unlock(LPVOID lpSurfaceData) override; private: - SDL_Texture* m_uploadBuffer; + uint32_t m_virtualWidth; + uint32_t m_virtualHeight; + DirectDrawSurfaceImpl* m_transferBuffer; IDirectDrawPalette* m_palette = nullptr; }; diff --git a/miniwin/src/internal/structs.h b/miniwin/src/internal/structs.h index 4f745098..43bda0ed 100644 --- a/miniwin/src/internal/structs.h +++ b/miniwin/src/internal/structs.h @@ -15,3 +15,9 @@ struct Appearance { uint32_t textureId; uint32_t flat; }; + +struct ViewportTransform { + float scale; + float offsetX; + float offsetY; +}; From dcacdcc71120fc91ec41cdaed85b6b510d861d29 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Thu, 26 Jun 2025 02:46:10 +0200 Subject: [PATCH 06/22] Fix d3drmmesh type error (#433) --- miniwin/src/d3drm/d3drmmesh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miniwin/src/d3drm/d3drmmesh.cpp b/miniwin/src/d3drm/d3drmmesh.cpp index 5d96ac86..4545036b 100644 --- a/miniwin/src/d3drm/d3drmmesh.cpp +++ b/miniwin/src/d3drm/d3drmmesh.cpp @@ -59,7 +59,7 @@ HRESULT Direct3DRMMeshImpl::AddGroup( MeshGroup group; group.vertexPerFace = vertexPerFace; - DWORD* src = faceBuffer; + unsigned int* src = faceBuffer; group.indices.assign(src, src + faceCount * vertexPerFace); m_groups.push_back(std::move(group)); From 7a92f532129d88d09c2c8c8fa90e794dfa8ce197 Mon Sep 17 00:00:00 2001 From: VoxelTek <53562267+VoxelTek@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:58:06 +1000 Subject: [PATCH 07/22] Revamp config tool, add tooltips and consolidate options. (#427) * Add tooltips, remove obsolete directory stuff In the config tool, the "Media Path" entry was removed, as it doesn't do anything in isle-portable as far as I know. The "Disk Path" and "CD Path" options were merged into one directory editing thing, where CD Path is specified and Disk Path is then derived from that. A checkbox to enable or disable full-screen was added. Tooltips were added to the config app, so now users can more easily tell what a setting in the config tool does. * Make clang-format happy * oops that's an extra semicolon I don't need * Remove obsolete full screen forcing Co-authored-by: Anders Jenbo * Update CONFIG/config.cpp Co-authored-by: Anders Jenbo --------- Co-authored-by: Anders Jenbo --- CONFIG/MainDlg.cpp | 110 ++++++++++---------------- CONFIG/MainDlg.h | 11 +-- CONFIG/config.cpp | 10 --- CONFIG/config.h | 1 - CONFIG/res/maindialog.ui | 165 +++++++++++++-------------------------- 5 files changed, 103 insertions(+), 194 deletions(-) diff --git a/CONFIG/MainDlg.cpp b/CONFIG/MainDlg.cpp index 46a2efe8..b90f1f66 100644 --- a/CONFIG/MainDlg.cpp +++ b/CONFIG/MainDlg.cpp @@ -56,18 +56,15 @@ CMainDialog::CMainDialog(QWidget* pParent) : QDialog(pParent) connect(m_ui->musicCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxMusic); connect(m_ui->sound3DCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckbox3DSound); connect(m_ui->joystickCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxJoystick); + connect(m_ui->fullscreenCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxFullscreen); connect(m_ui->okButton, &QPushButton::clicked, this, &CMainDialog::accept); connect(m_ui->cancelButton, &QPushButton::clicked, this, &CMainDialog::reject); - connect(m_ui->diskPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDiskPathDialog); - connect(m_ui->cdPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectCDPathDialog); - connect(m_ui->mediaPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectMediaPathDialog); + connect(m_ui->dataPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDataPathDialog); connect(m_ui->savePathOpen, &QPushButton::clicked, this, &CMainDialog::SelectSavePathDialog); - connect(m_ui->diskPath, &QLineEdit::textEdited, this, &CMainDialog::DiskPathEdited); - connect(m_ui->cdPath, &QLineEdit::textEdited, this, &CMainDialog::CDPathEdited); - connect(m_ui->mediaPath, &QLineEdit::textEdited, this, &CMainDialog::MediaPathEdited); - connect(m_ui->savePath, &QLineEdit::textEdited, this, &CMainDialog::SavePathEdited); + connect(m_ui->dataPath, &QLineEdit::editingFinished, this, &CMainDialog::DataPathEdited); + connect(m_ui->savePath, &QLineEdit::editingFinished, this, &CMainDialog::SavePathEdited); connect(m_ui->maxLoDSlider, &QSlider::valueChanged, this, &CMainDialog::MaxLoDChanged); connect(m_ui->maxActorsSlider, &QSlider::valueChanged, this, &CMainDialog::MaxActorsChanged); @@ -187,9 +184,8 @@ void CMainDialog::UpdateInterface() } m_ui->joystickCheckBox->setChecked(currentConfigApp->m_use_joystick); m_ui->musicCheckBox->setChecked(currentConfigApp->m_music); - m_ui->diskPath->setText(QString::fromStdString(currentConfigApp->m_base_path)); - m_ui->cdPath->setText(QString::fromStdString(currentConfigApp->m_cd_path)); - m_ui->mediaPath->setText(QString::fromStdString(currentConfigApp->m_media_path)); + m_ui->fullscreenCheckBox->setChecked(currentConfigApp->m_full_screen); + m_ui->dataPath->setText(QString::fromStdString(currentConfigApp->m_cd_path)); m_ui->savePath->setText(QString::fromStdString(currentConfigApp->m_save_path)); } @@ -266,54 +262,33 @@ void CMainDialog::OnCheckboxMusic(bool checked) UpdateInterface(); } -void CMainDialog::SelectDiskPathDialog() +void CMainDialog::OnCheckboxFullscreen(bool checked) { - QString disk_path = QString::fromStdString(currentConfigApp->m_base_path); - disk_path = QFileDialog::getExistingDirectory( - this, - tr("Open Directory"), - disk_path, - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks - ); - - if (disk_path.toStdString() != "") { - currentConfigApp->m_base_path = disk_path.toStdString(); - m_modified = true; - UpdateInterface(); - } + currentConfigApp->m_full_screen = checked; + m_modified = true; + UpdateInterface(); } -void CMainDialog::SelectCDPathDialog() +void CMainDialog::SelectDataPathDialog() { - QString cd_path = QString::fromStdString(currentConfigApp->m_cd_path); - cd_path = QFileDialog::getExistingDirectory( + QString data_path = QString::fromStdString(currentConfigApp->m_cd_path); + data_path = QFileDialog::getExistingDirectory( this, tr("Open Directory"), - cd_path, + data_path, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); - if (cd_path.toStdString() != "") { - currentConfigApp->m_cd_path = cd_path.toStdString(); - m_modified = true; - UpdateInterface(); - } -} + QDir data_dir = QDir(data_path); -void CMainDialog::SelectMediaPathDialog() -{ - QString media_path = QString::fromStdString(currentConfigApp->m_media_path); - media_path = QFileDialog::getExistingDirectory( - this, - tr("Open Directory"), - media_path, - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks - ); - if (media_path.toStdString() != "") { - currentConfigApp->m_media_path = media_path.toStdString(); + if (data_dir.exists()) { + currentConfigApp->m_cd_path = data_dir.absolutePath().toStdString(); + data_dir.cd(QString("DATA")); + data_dir.cd(QString("disk")); + currentConfigApp->m_base_path = data_dir.absolutePath().toStdString(); m_modified = true; - UpdateInterface(); } + UpdateInterface(); } void CMainDialog::SelectSavePathDialog() @@ -326,38 +301,39 @@ void CMainDialog::SelectSavePathDialog() QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks ); - if (save_path.toStdString() != "") { - currentConfigApp->m_save_path = save_path.toStdString(); + QDir save_dir = QDir(save_path); + + if (save_dir.exists()) { + currentConfigApp->m_save_path = save_dir.absolutePath().toStdString(); m_modified = true; - UpdateInterface(); } -} - -void CMainDialog::DiskPathEdited(const QString& text) -{ - currentConfigApp->m_base_path = text.toStdString(); - m_modified = true; UpdateInterface(); } -void CMainDialog::CDPathEdited(const QString& text) +void CMainDialog::DataPathEdited() { - currentConfigApp->m_cd_path = text.toStdString(); - m_modified = true; + QDir data_dir = QDir(m_ui->dataPath->text()); + + if (data_dir.exists()) { + currentConfigApp->m_cd_path = data_dir.absolutePath().toStdString(); + data_dir.cd(QString("DATA")); + data_dir.cd(QString("disk")); + currentConfigApp->m_base_path = data_dir.absolutePath().toStdString(); + m_modified = true; + } + UpdateInterface(); } -void CMainDialog::MediaPathEdited(const QString& text) +void CMainDialog::SavePathEdited() { - currentConfigApp->m_media_path = text.toStdString(); - m_modified = true; - UpdateInterface(); -} -void CMainDialog::SavePathEdited(const QString& text) -{ - currentConfigApp->m_save_path = text.toStdString(); - m_modified = true; + QDir save_dir = QDir(m_ui->savePath->text()); + + if (save_dir.exists()) { + currentConfigApp->m_save_path = save_dir.absolutePath().toStdString(); + m_modified = true; + } UpdateInterface(); } diff --git a/CONFIG/MainDlg.h b/CONFIG/MainDlg.h index b441248d..65782037 100644 --- a/CONFIG/MainDlg.h +++ b/CONFIG/MainDlg.h @@ -42,16 +42,13 @@ private slots: void OnRadiobuttonTextureHighQuality(bool checked); void OnCheckboxJoystick(bool checked); void OnCheckboxMusic(bool checked); + void OnCheckboxFullscreen(bool checked); void accept() override; void reject() override; - void SelectDiskPathDialog(); - void SelectCDPathDialog(); - void SelectMediaPathDialog(); + void SelectDataPathDialog(); void SelectSavePathDialog(); - void DiskPathEdited(const QString& text); - void CDPathEdited(const QString& text); - void MediaPathEdited(const QString& text); - void SavePathEdited(const QString& text); + void DataPathEdited(); + void SavePathEdited(); void MaxLoDChanged(int value); void MaxActorsChanged(int value); }; diff --git a/CONFIG/config.cpp b/CONFIG/config.cpp index 31e7032b..bd38e669 100644 --- a/CONFIG/config.cpp +++ b/CONFIG/config.cpp @@ -141,7 +141,6 @@ bool CConfigApp::ReadRegisterSettings() } m_base_path = iniparser_getstring(dict, "isle:diskpath", m_base_path.c_str()); m_cd_path = iniparser_getstring(dict, "isle:cdpath", m_cd_path.c_str()); - m_media_path = iniparser_getstring(dict, "isle:mediapath", m_media_path.c_str()); m_save_path = iniparser_getstring(dict, "isle:savepath", m_save_path.c_str()); m_display_bit_depth = iniparser_getint(dict, "isle:Display Bit Depth", -1); m_flip_surfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flip_surfaces); @@ -166,10 +165,6 @@ bool CConfigApp::ValidateSettings() { BOOL is_modified = FALSE; - if (!IsPrimaryDriver() && !m_full_screen) { - m_full_screen = TRUE; - is_modified = TRUE; - } if (IsDeviceInBasicRGBMode()) { if (m_3d_video_ram) { m_3d_video_ram = FALSE; @@ -203,10 +198,6 @@ bool CConfigApp::ValidateSettings() m_3d_video_ram = TRUE; is_modified = TRUE; } - if (!m_full_screen) { - m_full_screen = TRUE; - is_modified = TRUE; - } } if ((m_display_bit_depth != 8 && m_display_bit_depth != 16) && (m_display_bit_depth != 0 || m_full_screen)) { m_display_bit_depth = 16; @@ -302,7 +293,6 @@ void CConfigApp::WriteRegisterSettings() const } iniparser_set(dict, "isle:diskpath", m_base_path.c_str()); iniparser_set(dict, "isle:cdpath", m_cd_path.c_str()); - iniparser_set(dict, "isle:mediapath", m_media_path.c_str()); iniparser_set(dict, "isle:savepath", m_save_path.c_str()); SetIniInt(dict, "isle:Display Bit Depth", m_display_bit_depth); diff --git a/CONFIG/config.h b/CONFIG/config.h index 276dc755..a621655a 100644 --- a/CONFIG/config.h +++ b/CONFIG/config.h @@ -77,7 +77,6 @@ class CConfigApp { std::string m_iniPath; std::string m_base_path; std::string m_cd_path; - std::string m_media_path; std::string m_save_path; float m_max_lod; int m_max_actors; diff --git a/CONFIG/res/maindialog.ui b/CONFIG/res/maindialog.ui index ac69f782..78c5a174 100644 --- a/CONFIG/res/maindialog.ui +++ b/CONFIG/res/maindialog.ui @@ -7,7 +7,7 @@ 0 0 575 - 650 + 600 @@ -44,6 +44,9 @@ 16777215 + + Jaws. + @@ -76,13 +79,7 @@ 0 - - - - - - - + @@ -101,68 +98,8 @@ - - - - - 0 - 0 - - - - - 55 - 16777215 - - - - Open - - - - - - - - 0 - 0 - - - - - 55 - 16777215 - - - - Open - - - - - - - - 0 - 0 - - - - CD Path: - - - Qt::PlainText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - + 0 @@ -180,26 +117,7 @@ - - - - - 0 - 0 - - - - Disk Path: - - - Qt::PlainText - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - + @@ -218,8 +136,22 @@ - - + + + + Path to the game data files. Set this to the CD image root. + + + + + + + Folder where save files are kept. + + + + + 0 @@ -227,7 +159,7 @@ - Media Path: + Data Path: Qt::PlainText @@ -237,9 +169,6 @@ - - - @@ -266,6 +195,9 @@ 120 + + Set 3D model detail level. + Island Model Quality @@ -345,6 +277,9 @@ + + Set texture detail level. + Island Texture Quality @@ -422,16 +357,13 @@ - - - - 0 - - - 0 - + + + + Enable 3D positional audio effects. + 3D Sound @@ -439,6 +371,9 @@ + + Enable in-game background music. + Music @@ -446,11 +381,24 @@ + + Enable joystick and gamepad support for LEGO Island. + Use Joystick + + + + Toggle fullscreen display mode. + + + Fullscreen + + + @@ -462,6 +410,9 @@ 225 + + 3D graphics device used to render the game. + @@ -581,12 +532,8 @@ - diskPath - diskPathOpen - cdPath - cdPathOpen - mediaPath - mediaPathOpen + dataPath + dataPathOpen savePath savePathOpen textureQualityFastRadioButton From 7797729a3ff986de57186445e68ebb1600c6690a Mon Sep 17 00:00:00 2001 From: Carson <57198646+carson-coder@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:27:53 -0400 Subject: [PATCH 08/22] Add option to remove startup delay (#395) * Add startup delay option * Remove build option * Add comment back --- ISLE/isleapp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index b097eb3b..2ca33577 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -911,7 +911,7 @@ inline bool IsleApp::Tick() static MxLong g_lastFrameTime = 0; // GLOBAL: ISLE 0x4101bc - static MxS32 g_startupDelay = 200; + static MxS32 g_startupDelay = 1; if (IsleDebug_Paused() && IsleDebug_StepModeEnabled()) { IsleDebug_SetPaused(false); From 73bab247218fee0f6e5e555a08c6a88a33f105e5 Mon Sep 17 00:00:00 2001 From: VoxelTek <53562267+VoxelTek@users.noreply.github.com> Date: Fri, 27 Jun 2025 00:21:11 +1000 Subject: [PATCH 09/22] Add Launch Game button to config tool, rename executable to isle-config (#435) * Add Launch Game option to config tool * Rename executable from "config" to "isle-config" * Add error popup if unable to find game executable * Use one QMessageBox for both Win and *nix Create one QMessageBox object to use for both Windows and non-Windows platforms. Additionally, set all relevant text during creation of QMessageBox, and show Warning icon as part of message box. * Add tooltips to save, launch, and exit buttons * Change "Launch Game" to "Save and Launch" * Remove unnecessary Windows-specific code --- CMakeLists.txt | 38 +++++++++++++++++++------------------- CONFIG/MainDlg.cpp | 27 +++++++++++++++++++++++++++ CONFIG/MainDlg.h | 1 + CONFIG/res/maindialog.ui | 22 +++++++++++++++++++--- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 334580c0..1f7e0595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,7 +525,7 @@ endif() if (ISLE_BUILD_CONFIG) find_package(Qt6 REQUIRED COMPONENTS Core Widgets) qt_standard_project_setup() - qt_add_executable(config WIN32 + qt_add_executable(isle-config WIN32 LEGO1/mxdirectx/mxdirectxinfo.cpp LEGO1/mxdirectx/legodxinfo.cpp CONFIG/config.cpp @@ -535,22 +535,22 @@ if (ISLE_BUILD_CONFIG) CONFIG/res/config.rc CONFIG/res/config.qrc ) - target_link_libraries(config PRIVATE Qt6::Core Qt6::Widgets) - set_property(TARGET config PROPERTY AUTOMOC ON) - set_property(TARGET config PROPERTY AUTORCC ON) - set_property(TARGET config PROPERTY AUTOUIC ON) - set_property(TARGET config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res") - list(APPEND isle_targets config) - target_compile_definitions(config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG) - target_include_directories(config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1") - target_include_directories(config PUBLIC "$") + target_link_libraries(isle-config PRIVATE Qt6::Core Qt6::Widgets) + set_property(TARGET isle-config PROPERTY AUTOMOC ON) + set_property(TARGET isle-config PROPERTY AUTORCC ON) + set_property(TARGET isle-config PROPERTY AUTOUIC ON) + set_property(TARGET isle-config PROPERTY AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/CONFIG/res") + list(APPEND isle_targets isle-config) + target_compile_definitions(isle-config PRIVATE _AFXDLL MXDIRECTX_FOR_CONFIG) + target_include_directories(isle-config PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/LEGO1") + target_include_directories(isle-config PUBLIC "$") if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14) - target_link_libraries(config PRIVATE DirectX5::DirectX5) + target_link_libraries(isle-config PRIVATE DirectX5::DirectX5) endif() - target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500) - target_link_libraries(config PRIVATE SDL3::SDL3 Isle::iniparser) + target_compile_definitions(isle-config PRIVATE DIRECT3D_VERSION=0x500) + target_link_libraries(isle-config PRIVATE SDL3::SDL3 Isle::iniparser) if (NOT ISLE_MINIWIN) - target_link_libraries(config PRIVATE ddraw dxguid) + target_link_libraries(isle-config PRIVATE ddraw dxguid) endif() endif() @@ -564,8 +564,8 @@ if (MSVC) if (TARGET isle) target_compile_definitions(isle PRIVATE "_CRT_SECURE_NO_WARNINGS") endif() - if (TARGET config) - target_compile_definitions(config PRIVATE "_CRT_SECURE_NO_WARNINGS") + if (TARGET isle-config) + target_compile_definitions(isle-config PRIVATE "_CRT_SECURE_NO_WARNINGS") endif() endif() # Visual Studio 2017 version 15.7 needs "/Zc:__cplusplus" for __cplusplus @@ -574,8 +574,8 @@ if (MSVC) if (TARGET isle) target_compile_options(isle PRIVATE "-Zc:__cplusplus") endif() - if (TARGET config) - target_compile_options(config PRIVATE "-Zc:__cplusplus") + if (TARGET isle-config) + target_compile_options(isle-config PRIVATE "-Zc:__cplusplus") endif() endif() endif() @@ -633,7 +633,7 @@ install(TARGETS isle ${install_extra_targets} LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) if (ISLE_BUILD_CONFIG) - install(TARGETS config + install(TARGETS isle-config RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() diff --git a/CONFIG/MainDlg.cpp b/CONFIG/MainDlg.cpp index b90f1f66..e2c526f6 100644 --- a/CONFIG/MainDlg.cpp +++ b/CONFIG/MainDlg.cpp @@ -11,6 +11,8 @@ #include "res/resource.h" #include +#include +#include #include #include @@ -59,6 +61,7 @@ CMainDialog::CMainDialog(QWidget* pParent) : QDialog(pParent) connect(m_ui->fullscreenCheckBox, &QCheckBox::toggled, this, &CMainDialog::OnCheckboxFullscreen); connect(m_ui->okButton, &QPushButton::clicked, this, &CMainDialog::accept); connect(m_ui->cancelButton, &QPushButton::clicked, this, &CMainDialog::reject); + connect(m_ui->launchButton, &QPushButton::clicked, this, &CMainDialog::launch); connect(m_ui->dataPathOpen, &QPushButton::clicked, this, &CMainDialog::SelectDataPathDialog); connect(m_ui->savePathOpen, &QPushButton::clicked, this, &CMainDialog::SelectSavePathDialog); @@ -155,6 +158,30 @@ void CMainDialog::accept() QDialog::accept(); } +void CMainDialog::launch() +{ + if (m_modified) { + currentConfigApp->WriteRegisterSettings(); + } + + QDir::setCurrent(QCoreApplication::applicationDirPath()); + + QMessageBox msgBox = QMessageBox( + QMessageBox::Warning, + QString("Error!"), + QString("Unable to locate isle executable!"), + QMessageBox::Close + ); + + if (!QProcess::startDetached("./isle")) { // Check in isle-config directory + if (!QProcess::startDetached("isle")) { // Check in $PATH + msgBox.exec(); + } + } + + QDialog::accept(); +} + // FUNCTION: CONFIG 0x00404360 void CMainDialog::UpdateInterface() { diff --git a/CONFIG/MainDlg.h b/CONFIG/MainDlg.h index 65782037..372a1156 100644 --- a/CONFIG/MainDlg.h +++ b/CONFIG/MainDlg.h @@ -45,6 +45,7 @@ private slots: void OnCheckboxFullscreen(bool checked); void accept() override; void reject() override; + void launch(); void SelectDataPathDialog(); void SelectSavePathDialog(); void DataPathEdited(); diff --git a/CONFIG/res/maindialog.ui b/CONFIG/res/maindialog.ui index 78c5a174..c2760e7c 100644 --- a/CONFIG/res/maindialog.ui +++ b/CONFIG/res/maindialog.ui @@ -508,8 +508,11 @@ + + Save configuration and close the config tool. + - OK + Save and Exit true @@ -517,9 +520,22 @@ - + + + Save configuration and launch LEGO Island. + - Cancel + Save and Launch + + + + + + + Discard changed settings and close the config tool. + + + Exit without saving From a782c489809f7eeae99d8c4fd3509f3497dacdee Mon Sep 17 00:00:00 2001 From: Danct12 Date: Fri, 27 Jun 2025 00:41:46 +0700 Subject: [PATCH 10/22] miniwin: Use PRIu32 for printing uint32_t (#436) Some architecture uses different type for uint32_t. For example: x86_64 | aarch64 = unsigned int xtensa | riscv32 = long unsigned int arm = long unsigned int --- miniwin/src/ddraw/ddraw.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 2765a2a1..acf695eb 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -75,7 +76,7 @@ HRESULT DirectDrawImpl::CreateSurface( if ((lpDDSurfaceDesc->dwFlags & DDSD_ZBUFFERBITDEPTH) != DDSD_ZBUFFERBITDEPTH) { return DDERR_INVALIDPARAMS; } - SDL_Log("Todo: Set %dbit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth); + SDL_Log("Todo: Set %" PRIu32 "bit Z-Buffer", lpDDSurfaceDesc->dwZBufferBitDepth); *lplpDDSurface = static_cast(new DummySurfaceImpl); return DD_OK; } From b66d1e2f64547d29854f36275ffa9a0fb584f23d Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Thu, 26 Jun 2025 20:31:30 +0200 Subject: [PATCH 11/22] Clear unknowns in `Ambulance` and `AmbulanceMissionState` (#1588) --- LEGO1/lego/legoomni/include/ambulance.h | 46 +++++++---- LEGO1/lego/legoomni/src/actors/ambulance.cpp | 84 ++++++++++---------- LEGO1/lego/legoomni/src/worlds/isle.cpp | 6 +- 3 files changed, 74 insertions(+), 62 deletions(-) diff --git a/LEGO1/lego/legoomni/include/ambulance.h b/LEGO1/lego/legoomni/include/ambulance.h index 97e6ba38..cb6a396a 100644 --- a/LEGO1/lego/legoomni/include/ambulance.h +++ b/LEGO1/lego/legoomni/include/ambulance.h @@ -11,6 +11,12 @@ class MxEndActionNotificationParam; // SIZE 0x24 class AmbulanceMissionState : public LegoState { public: + enum { + e_ready = 0, + e_enteredAmbulance = 1, + e_prepareAmbulance = 2, + }; + AmbulanceMissionState(); // FUNCTION: LEGO1 0x10037440 @@ -125,18 +131,18 @@ class AmbulanceMissionState : public LegoState { // SYNTHETIC: LEGO1 0x100376c0 // AmbulanceMissionState::`scalar deleting destructor' - undefined4 m_unk0x08; // 0x08 - MxLong m_startTime; // 0x0c - MxS16 m_peScore; // 0x10 - MxS16 m_maScore; // 0x12 - MxS16 m_paScore; // 0x14 - MxS16 m_niScore; // 0x16 - MxS16 m_laScore; // 0x18 - MxS16 m_peHighScore; // 0x1a - MxS16 m_maHighScore; // 0x1c - MxS16 m_paHighScore; // 0x1e - MxS16 m_niHighScore; // 0x20 - MxS16 m_laHighScore; // 0x22 + MxU32 m_state; // 0x08 + MxLong m_startTime; // 0x0c + MxS16 m_peScore; // 0x10 + MxS16 m_maScore; // 0x12 + MxS16 m_paScore; // 0x14 + MxS16 m_niScore; // 0x16 + MxS16 m_laScore; // 0x18 + MxS16 m_peHighScore; // 0x1a + MxS16 m_maHighScore; // 0x1c + MxS16 m_paHighScore; // 0x1e + MxS16 m_niHighScore; // 0x20 + MxS16 m_laHighScore; // 0x22 }; // VTABLE: LEGO1 0x100d71a8 @@ -177,15 +183,21 @@ class Ambulance : public IslePathActor { virtual MxLong HandleEndAction(MxEndActionNotificationParam& p_param); // vtable+0xf4 void CreateState(); - void FUN_10036e60(); + void Init(); void ActivateSceneActions(); void StopActions(); - void FUN_10037250(); + void Reset(); // SYNTHETIC: LEGO1 0x10036130 // Ambulance::`scalar deleting destructor' private: + enum { + e_none = 0, + e_waiting = 1, + e_finished = 3, + }; + void PlayAnimation(IsleScript::Script p_objectId); void PlayFinalAnimation(IsleScript::Script p_objectId); void StopAction(IsleScript::Script p_objectId); @@ -196,9 +208,9 @@ class Ambulance : public IslePathActor { AmbulanceMissionState* m_state; // 0x164 MxS16 m_unk0x168; // 0x168 MxS16 m_actorId; // 0x16a - MxS16 m_unk0x16c; // 0x16c - MxS16 m_unk0x16e; // 0x16e - MxS16 m_unk0x170; // 0x170 + MxS16 m_atPoliceTask; // 0x16c + MxS16 m_atBeachTask; // 0x16e + MxS16 m_taskState; // 0x170 MxS16 m_unk0x172; // 0x172 IsleScript::Script m_lastAction; // 0x174 IsleScript::Script m_lastAnimation; // 0x178 diff --git a/LEGO1/lego/legoomni/src/actors/ambulance.cpp b/LEGO1/lego/legoomni/src/actors/ambulance.cpp index 22c1e701..95ca6975 100644 --- a/LEGO1/lego/legoomni/src/actors/ambulance.cpp +++ b/LEGO1/lego/legoomni/src/actors/ambulance.cpp @@ -37,9 +37,9 @@ Ambulance::Ambulance() m_state = NULL; m_unk0x168 = 0; m_actorId = -1; - m_unk0x16c = 0; - m_unk0x16e = 0; - m_unk0x170 = 0; + m_atPoliceTask = 0; + m_atBeachTask = 0; + m_taskState = Ambulance::e_none; m_lastAction = IsleScript::c_noneIsle; m_unk0x172 = 0; m_lastAnimation = IsleScript::c_noneIsle; @@ -70,7 +70,7 @@ MxResult Ambulance::Create(MxDSAction& p_dsAction) m_state = (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState"); if (!m_state) { m_state = new AmbulanceMissionState(); - m_state->m_unk0x08 = 0; + m_state->m_state = AmbulanceMissionState::e_ready; GameState()->RegisterState(m_state); } } @@ -170,25 +170,25 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) m_lastAction = IsleScript::c_noneIsle; } else if (objectId == IsleScript::c_hho027en_RunAnim) { - m_state->m_unk0x08 = 1; + m_state->m_state = AmbulanceMissionState::e_enteredAmbulance; CurrentWorld()->PlaceActor(UserActor()); HandleClick(); m_unk0x172 = 0; TickleManager()->RegisterClient(this, 40000); } else if (objectId == IsleScript::c_hpz047pe_RunAnim || objectId == IsleScript::c_hpz048pe_RunAnim || objectId == IsleScript::c_hpz049bd_RunAnim || objectId == IsleScript::c_hpz053pa_RunAnim) { - if (m_unk0x170 == 3) { + if (m_taskState == Ambulance::e_finished) { PlayAnimation(IsleScript::c_hpz055pa_RunAnim); - m_unk0x170 = 0; + m_taskState = Ambulance::e_none; } else { PlayAnimation(IsleScript::c_hpz053pa_RunAnim); } } else if (objectId == IsleScript::c_hpz050bd_RunAnim || objectId == IsleScript::c_hpz052ma_RunAnim) { - if (m_unk0x170 == 3) { + if (m_taskState == Ambulance::e_finished) { PlayAnimation(IsleScript::c_hpz057ma_RunAnim); - m_unk0x170 = 0; + m_taskState = Ambulance::e_none; } else { PlayAnimation(IsleScript::c_hpz052ma_RunAnim); @@ -201,18 +201,18 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) m_unk0x172 = 0; TickleManager()->RegisterClient(this, 40000); - if (m_unk0x16c != 0) { + if (m_atPoliceTask != 0) { StopActions(); } } else if (objectId == IsleScript::c_hps116bd_RunAnim || objectId == IsleScript::c_hps118re_RunAnim) { - if (objectId == IsleScript::c_hps116bd_RunAnim && m_unk0x170 != 3) { + if (objectId == IsleScript::c_hps116bd_RunAnim && m_taskState != Ambulance::e_finished) { PlayAction(IsleScript::c_Avo923In_PlayWav); } - if (m_unk0x170 == 3) { + if (m_taskState == Ambulance::e_finished) { PlayAnimation(IsleScript::c_hps117bd_RunAnim); - m_unk0x170 = 0; + m_taskState = Ambulance::e_none; } else { PlayAnimation(IsleScript::c_hps118re_RunAnim); @@ -225,12 +225,12 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) m_unk0x172 = 0; TickleManager()->RegisterClient(this, 40000); - if (m_unk0x16e != 0) { + if (m_atBeachTask != 0) { StopActions(); } } else if (objectId == IsleScript::c_hho142cl_RunAnim || objectId == IsleScript::c_hho143cl_RunAnim || objectId == IsleScript::c_hho144cl_RunAnim) { - FUN_10037250(); + Reset(); } } @@ -241,18 +241,18 @@ MxLong Ambulance::HandleEndAction(MxEndActionNotificationParam& p_param) // FUNCTION: BETA10 0x100230bf MxLong Ambulance::HandleButtonDown(LegoControlManagerNotificationParam& p_param) { - if (m_unk0x170 == 1) { + if (m_taskState == Ambulance::e_waiting) { LegoROI* roi = PickROI(p_param.GetX(), p_param.GetY()); if (roi != NULL && !strcmpi(roi->GetName(), "ps-gate")) { - m_unk0x170 = 3; + m_taskState = Ambulance::e_finished; return 1; } roi = PickRootROI(p_param.GetX(), p_param.GetY()); if (roi != NULL && !strcmpi(roi->GetName(), "gd")) { - m_unk0x170 = 3; + m_taskState = Ambulance::e_finished; return 1; } } @@ -270,9 +270,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) } if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && p_param.GetData() == 0x0b) { - if (m_unk0x16e != 0) { - if (m_unk0x16c != 0) { - m_state->m_unk0x08 = 2; + if (m_atBeachTask != 0) { + if (m_atPoliceTask != 0) { + m_state->m_state = AmbulanceMissionState::e_prepareAmbulance; if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); @@ -297,7 +297,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) return 0; } - if (m_unk0x16e != 0) { + if (m_atBeachTask != 0) { if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); } @@ -307,7 +307,7 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) } } - if (m_unk0x16c != 0) { + if (m_atPoliceTask != 0) { if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); } @@ -315,9 +315,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) PlayAction(IsleScript::c_Avo915In_PlayWav); } } - else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_unk0x16e == 0) { - m_unk0x16e = 1; - m_unk0x170 = 1; + else if (p_param.GetTrigger() == LegoPathStruct::c_s && p_param.GetData() == 0x131 && m_atBeachTask == 0) { + m_atBeachTask = 1; + m_taskState = Ambulance::e_waiting; if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); @@ -345,9 +345,9 @@ MxLong Ambulance::HandlePathStruct(LegoPathStructNotificationParam& p_param) break; } } - else if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 0x22 || p_param.GetData() == 0x23 || p_param.GetData() == 0x24) && m_unk0x16c == 0) { - m_unk0x16c = 1; - m_unk0x170 = 1; + else if (p_param.GetTrigger() == LegoPathStruct::c_camAnim && (p_param.GetData() == 0x22 || p_param.GetData() == 0x23 || p_param.GetData() == 0x24) && m_atPoliceTask == 0) { + m_atPoliceTask = 1; + m_taskState = Ambulance::e_waiting; if (m_lastAction != IsleScript::c_noneIsle) { InvokeAction(Extra::e_stop, *g_isleScript, m_lastAction, NULL); @@ -368,7 +368,7 @@ MxLong Ambulance::HandleClick() return 1; } - if (m_state->m_unk0x08 == 2) { + if (m_state->m_state == AmbulanceMissionState::e_prepareAmbulance) { return 1; } @@ -387,7 +387,7 @@ MxLong Ambulance::HandleClick() InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_AmbulanceDashboard, NULL); ControlManager()->Register(this); - if (m_state->m_unk0x08 == 1) { + if (m_state->m_state == AmbulanceMissionState::e_enteredAmbulance) { SpawnPlayer(LegoGameState::e_hospitalExited, TRUE, 0); m_state->m_startTime = Timer()->GetTime(); InvokeAction(Extra::e_start, *g_isleScript, IsleScript::c_pns018rd_RunAnim, NULL); @@ -398,9 +398,9 @@ MxLong Ambulance::HandleClick() // FUNCTION: LEGO1 0x10036e60 // FUNCTION: BETA10 0x100236bb -void Ambulance::FUN_10036e60() +void Ambulance::Init() { - m_state->m_unk0x08 = 2; + m_state->m_state = AmbulanceMissionState::e_prepareAmbulance; PlayAnimation(IsleScript::c_hho027en_RunAnim); m_lastAction = IsleScript::c_noneIsle; m_lastAnimation = IsleScript::c_noneIsle; @@ -411,7 +411,7 @@ void Ambulance::Exit() { GameState()->m_currentArea = LegoGameState::e_hospitalExterior; StopActions(); - FUN_10037250(); + Reset(); Leave(); } @@ -467,11 +467,11 @@ void Ambulance::ActivateSceneActions() { PlayMusic(JukeboxScript::c_Hospital_Music); - if (m_state->m_unk0x08 == 1) { - m_state->m_unk0x08 = 0; + if (m_state->m_state == AmbulanceMissionState::e_enteredAmbulance) { + m_state->m_state = AmbulanceMissionState::e_ready; PlayAction(IsleScript::c_ham033cl_PlayWav); } - else if (m_unk0x16c != 0 && m_unk0x16e != 0) { + else if (m_atPoliceTask != 0 && m_atBeachTask != 0) { IsleScript::Script objectId; switch (rand() % 2) { @@ -571,14 +571,14 @@ void Ambulance::StopActions() } // FUNCTION: LEGO1 0x10037250 -void Ambulance::FUN_10037250() +void Ambulance::Reset() { StopAction(m_lastAction); BackgroundAudioManager()->RaiseVolume(); ((Act1State*) GameState()->GetState("Act1State"))->m_unk0x018 = 0; - m_state->m_unk0x08 = 0; - m_unk0x16e = 0; - m_unk0x16c = 0; + m_state->m_state = AmbulanceMissionState::e_ready; + m_atBeachTask = 0; + m_atPoliceTask = 0; g_isleFlags |= Isle::c_playMusic; AnimationManager()->EnableCamAnims(TRUE); AnimationManager()->FUN_1005f6d0(TRUE); @@ -626,7 +626,7 @@ void Ambulance::PlayAction(IsleScript::Script p_objectId) // FUNCTION: LEGO1 0x100373a0 AmbulanceMissionState::AmbulanceMissionState() { - m_unk0x08 = 0; + m_state = AmbulanceMissionState::e_ready; m_startTime = 0; m_peScore = 0; m_maScore = 0; diff --git a/LEGO1/lego/legoomni/src/worlds/isle.cpp b/LEGO1/lego/legoomni/src/worlds/isle.cpp index 050aae97..2eb47874 100644 --- a/LEGO1/lego/legoomni/src/worlds/isle.cpp +++ b/LEGO1/lego/legoomni/src/worlds/isle.cpp @@ -810,7 +810,7 @@ void Isle::Enable(MxBool p_enable) AnimationManager()->EnableCamAnims(FALSE); g_isleFlags &= ~c_playMusic; - m_ambulance->FUN_10036e60(); + m_ambulance->Init(); break; case 11: m_act1state->m_unk0x018 = 0; @@ -1209,7 +1209,7 @@ MxBool Isle::Escape() case 10: if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) { m_ambulance->StopActions(); - m_ambulance->FUN_10037250(); + m_ambulance->Reset(); } break; } @@ -1250,7 +1250,7 @@ void Isle::FUN_10033350() if (m_act1state->m_unk0x018 == 10) { if (UserActor() != NULL && !UserActor()->IsA("Ambulance")) { m_ambulance->StopActions(); - m_ambulance->FUN_10037250(); + m_ambulance->Reset(); } } From 056064f9d4f4d9c69faae65012ff6de0c22f56e7 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Thu, 26 Jun 2025 22:35:04 +0200 Subject: [PATCH 12/22] Clear unknown in `LegoLOD` (#1590) The function does mostly the same as `SetTextureInfo` but does not explicitly set the color as well. --- LEGO1/lego/legoomni/src/common/legocharactermanager.cpp | 2 +- LEGO1/lego/sources/roi/legolod.cpp | 2 +- LEGO1/lego/sources/roi/legolod.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp index 485d3511..80b69efc 100644 --- a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp @@ -634,7 +634,7 @@ MxBool LegoCharacterManager::SetHeadTexture(LegoROI* p_roi, LegoTextureInfo* p_t LegoLOD* clone = lod->Clone(renderer); if (p_texture != NULL) { - clone->FUN_100aad70(p_texture); + clone->UpdateTextureInfo(p_texture); } dupLodList->PushBack(clone); diff --git a/LEGO1/lego/sources/roi/legolod.cpp b/LEGO1/lego/sources/roi/legolod.cpp index 2291a3e7..bf157b56 100644 --- a/LEGO1/lego/sources/roi/legolod.cpp +++ b/LEGO1/lego/sources/roi/legolod.cpp @@ -351,7 +351,7 @@ LegoResult LegoLOD::SetTextureInfo(LegoTextureInfo* p_textureInfo) } // FUNCTION: LEGO1 0x100aad70 -LegoResult LegoLOD::FUN_100aad70(LegoTextureInfo* p_textureInfo) +LegoResult LegoLOD::UpdateTextureInfo(LegoTextureInfo* p_textureInfo) { for (LegoU32 i = m_meshOffset; i < m_numMeshes; i++) { if (m_melems[i].m_textured) { diff --git a/LEGO1/lego/sources/roi/legolod.h b/LEGO1/lego/sources/roi/legolod.h index 114e377b..e787d377 100644 --- a/LEGO1/lego/sources/roi/legolod.h +++ b/LEGO1/lego/sources/roi/legolod.h @@ -31,7 +31,7 @@ class LegoLOD : public ViewLOD { LegoLOD* Clone(Tgl::Renderer* p_renderer); LegoResult SetColor(LegoFloat p_red, LegoFloat p_green, LegoFloat p_blue, LegoFloat p_alpha); LegoResult SetTextureInfo(LegoTextureInfo* p_textureInfo); - LegoResult FUN_100aad70(LegoTextureInfo* p_textureInfo); + LegoResult UpdateTextureInfo(LegoTextureInfo* p_textureInfo); void ClearMeshOffset(); LegoResult GetTextureInfo(LegoTextureInfo*& p_textureInfo); From a987595e1eea009ffd1b078a2108d60fdf35f295 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Thu, 26 Jun 2025 17:30:24 -0700 Subject: [PATCH 13/22] 1.0 compatibility (#434) * Make Infocenter compatible with 1.0 versions * Fix * Emscripten patch * Fix * Fix Emscripten patch * Window title * Naming * Don't exit car build automatically in 1.0 * Disable character selection by clicking icon in 1.0 * Remove obsolete includes --- ISLE/emscripten/libwasmfs_fetch.js.patch | 18 ++++- ISLE/isleapp.cpp | 29 ++++++++ ISLE/isleapp.h | 1 + LEGO1/lego/legoomni/include/legomain.h | 4 + .../lego/legoomni/src/build/legocarbuild.cpp | 2 +- LEGO1/lego/legoomni/src/main/legomain.cpp | 1 + LEGO1/lego/legoomni/src/worlds/infocenter.cpp | 74 +++++++++++-------- LEGO1/omni/include/mxstring.h | 8 +- 8 files changed, 100 insertions(+), 37 deletions(-) diff --git a/ISLE/emscripten/libwasmfs_fetch.js.patch b/ISLE/emscripten/libwasmfs_fetch.js.patch index eb16caf0..de8fab60 100644 --- a/ISLE/emscripten/libwasmfs_fetch.js.patch +++ b/ISLE/emscripten/libwasmfs_fetch.js.patch @@ -1,5 +1,5 @@ diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js -index e8c9f7e21..1c0eea957 100644 +index e8c9f7e21..caf1971d2 100644 --- a/src/lib/libwasmfs_fetch.js +++ b/src/lib/libwasmfs_fetch.js @@ -38,36 +38,7 @@ addToLibrary({ @@ -89,7 +89,21 @@ index e8c9f7e21..1c0eea957 100644 return Promise.resolve(); } -@@ -164,6 +156,21 @@ addToLibrary({ +@@ -156,14 +148,31 @@ addToLibrary({ + return readLength; + }, + getSize: async (file) => { +- try { +- await getFileRange(file, 0, 0); +- } catch (failedResponse) { +- return 0; ++ if (!(file in wasmFS$JSMemoryRanges)) { ++ try { ++ await getFileRange(file, undefined, undefined); ++ } catch (failedResponse) { ++ return 0; ++ } + } return wasmFS$JSMemoryRanges[file].size; }, }; diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 2ca33577..a5091ece 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -703,6 +703,7 @@ MxResult IsleApp::SetupWindow() return FAILURE; } + DetectGameVersion(); GameState()->SerializePlayersInfo(LegoStorage::c_read); GameState()->SerializeScoreHistory(LegoStorage::c_read); @@ -1098,6 +1099,34 @@ MxResult IsleApp::VerifyFilesystem() return SUCCESS; } +void IsleApp::DetectGameVersion() +{ + const char* file = "/lego/scripts/infocntr/infomain.si"; + SDL_PathInfo info; + bool success = false; + + MxString path = MxString(m_hdPath) + file; + path.MapPathToFilesystem(); + if (!(success = SDL_GetPathInfo(path.GetData(), &info))) { + path = MxString(m_cdPath) + file; + path.MapPathToFilesystem(); + success = SDL_GetPathInfo(path.GetData(), &info); + } + + assert(success); + + // File sizes of INFOMAIN.SI in English 1.0 and Japanese 1.0 + Lego()->SetVersion10(info.size == 58130432 || info.size == 57737216); + + if (Lego()->IsVersion10()) { + SDL_Log("Detected game version 1.0"); + SDL_SetWindowTitle(reinterpret_cast(m_windowHandle), "Lego Island"); + } + else { + SDL_Log("Detected game version 1.1"); + } +} + IDirect3DRMMiniwinDevice* GetD3DRMMiniwinDevice() { LegoVideoManager* videoManager = LegoOmni::GetInstance()->GetVideoManager(); diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index d76a291b..2766607f 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -56,6 +56,7 @@ class IsleApp { MxResult ParseArguments(int argc, char** argv); MxResult VerifyFilesystem(); + void DetectGameVersion(); private: char* m_hdPath; // 0x00 diff --git a/LEGO1/lego/legoomni/include/legomain.h b/LEGO1/lego/legoomni/include/legomain.h index fa9a91c5..c27be280 100644 --- a/LEGO1/lego/legoomni/include/legomain.h +++ b/LEGO1/lego/legoomni/include/legomain.h @@ -200,6 +200,9 @@ class LegoOmni : public MxOmni { SDL_PushEvent(&event); } + void SetVersion10(MxBool p_version10) { m_version10 = p_version10; } + MxBool IsVersion10() { return m_version10; } + // SYNTHETIC: LEGO1 0x10058b30 // LegoOmni::`scalar deleting destructor' @@ -221,6 +224,7 @@ class LegoOmni : public MxOmni { MxDSAction m_action; // 0xa0 MxBackgroundAudioManager* m_bkgAudioManager; // 0x134 MxTransitionManager* m_transitionManager; // 0x138 + MxBool m_version10; public: MxBool m_unk0x13c; // 0x13c diff --git a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp index e69415f6..fa0be996 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuild.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuild.cpp @@ -488,7 +488,7 @@ void LegoCarBuild::FUN_100236d0() m_unk0x110 = NULL; m_unk0x100 = 0; - if (m_animPresenter->AllPartsPlaced()) { + if (m_animPresenter->AllPartsPlaced() && !Lego()->IsVersion10()) { // Note the code duplication with LEGO1 0x10025ee0 switch (m_carId) { case 1: diff --git a/LEGO1/lego/legoomni/src/main/legomain.cpp b/LEGO1/lego/legoomni/src/main/legomain.cpp index 3b99f0b5..1695e1c3 100644 --- a/LEGO1/lego/legoomni/src/main/legomain.cpp +++ b/LEGO1/lego/legoomni/src/main/legomain.cpp @@ -76,6 +76,7 @@ void LegoOmni::Init() m_bkgAudioManager = NULL; m_unk0x13c = TRUE; m_transitionManager = NULL; + m_version10 = FALSE; } // FUNCTION: LEGO1 0x10058c30 diff --git a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp index f9bfb4eb..c007dc96 100644 --- a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp +++ b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp @@ -306,29 +306,32 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) if (!m_unk0x1d4) { PlayMusic(JukeboxScript::c_InformationCenter_Music); - GameState()->SetActor(m_selectedCharacter); - switch (m_selectedCharacter) { - case e_pepper: - PlayAction(InfomainScript::c_avo901in_RunAnim); - break; - case e_mama: - PlayAction(InfomainScript::c_avo902in_RunAnim); - break; - case e_papa: - PlayAction(InfomainScript::c_avo903in_RunAnim); - break; - case e_nick: - PlayAction(InfomainScript::c_avo904in_RunAnim); - break; - case e_laura: - PlayAction(InfomainScript::c_avo905in_RunAnim); - break; - default: - break; + if (!Lego()->IsVersion10()) { + GameState()->SetActor(m_selectedCharacter); + + switch (m_selectedCharacter) { + case e_pepper: + PlayAction(InfomainScript::c_avo901in_RunAnim); + break; + case e_mama: + PlayAction(InfomainScript::c_avo902in_RunAnim); + break; + case e_papa: + PlayAction(InfomainScript::c_avo903in_RunAnim); + break; + case e_nick: + PlayAction(InfomainScript::c_avo904in_RunAnim); + break; + case e_laura: + PlayAction(InfomainScript::c_avo905in_RunAnim); + break; + default: + break; + } + + UpdateFrameHot(TRUE); } - - UpdateFrameHot(TRUE); } } @@ -338,7 +341,7 @@ MxLong Infocenter::HandleEndAction(MxEndActionNotificationParam& p_param) return result; } - if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim) { + if (action->GetObjectId() == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) { ControlManager()->FUN_100293c0(InfomainScript::c_BigInfo_Ctl, action->GetAtomId().GetInternal(), 0); m_unk0x1d6 = 0; } @@ -478,7 +481,7 @@ void Infocenter::ReadyWorld() InfomainScript::Script script = m_infocenterState->GetNextReturnDialogue(); PlayAction(script); - if (script == InfomainScript::c_iicx26in_RunAnim) { + if (script == InfomainScript::c_iicx26in_RunAnim - Lego()->IsVersion10()) { m_unk0x1d6 = 1; } @@ -1186,13 +1189,13 @@ MxLong Infocenter::HandleNotification0(MxNotificationParam& p_param) m_currentInfomainScript == InfomainScript::c_Pepper_All_Movie || m_currentInfomainScript == InfomainScript::c_Nick_All_Movie || m_currentInfomainScript == InfomainScript::c_Laura_All_Movie || - m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav || - m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav) { + m_currentInfomainScript == InfomainScript::c_iic007ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ijs002ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_irt001ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ipz006ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_igs004ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_iho003ra_PlayWav - Lego()->IsVersion10() || + m_currentInfomainScript == InfomainScript::c_ips005ra_PlayWav - Lego()->IsVersion10()) { StopCurrentAction(); } } @@ -1506,6 +1509,17 @@ void Infocenter::StopCredits() // FUNCTION: BETA10 0x1002ee8c void Infocenter::PlayAction(InfomainScript::Script p_script) { + if (Lego()->IsVersion10()) { + if (p_script == InfomainScript::c_iicx18in_RunAnim) { + // Alternative dialogue after signing in (1.0 version) + p_script = InfomainScript::c_iic016in_RunAnim; + } + else if (p_script > InfomainScript::c_iicx18in_RunAnim) { + // Shift all other actions by 1 + p_script = (InfomainScript::Script)((int) p_script - 1); + } + } + MxDSAction action; action.SetObjectId(p_script); action.SetAtomId(*g_infomainScript); diff --git a/LEGO1/omni/include/mxstring.h b/LEGO1/omni/include/mxstring.h index eaa58181..b4b25262 100644 --- a/LEGO1/omni/include/mxstring.h +++ b/LEGO1/omni/include/mxstring.h @@ -20,10 +20,10 @@ class MxString : public MxCore { void ToLowerCase(); void MapPathToFilesystem() { MapPathToFilesystem(m_data); } - MxString& operator=(const MxString& p_str); - const MxString& operator=(const char* p_str); - MxString operator+(const MxString& p_str) const; - MxString operator+(const char* p_str) const; + LEGO1_EXPORT MxString& operator=(const MxString& p_str); + LEGO1_EXPORT const MxString& operator=(const char* p_str); + LEGO1_EXPORT MxString operator+(const MxString& p_str) const; + LEGO1_EXPORT MxString operator+(const char* p_str) const; LEGO1_EXPORT MxString& operator+=(const char* p_str); static void CharSwap(char* p_a, char* p_b); From 16a94c725caf3fc1d9aca50c685390ab78fb2b9a Mon Sep 17 00:00:00 2001 From: Korbo Date: Fri, 27 Jun 2025 18:08:45 -0500 Subject: [PATCH 14/22] Names for race related or adjacent functions and variables (#1592) * Names for race related or adjacent functions and variables * fix formatting * fix formatting --- LEGO1/lego/legoomni/include/legopathactor.h | 2 +- LEGO1/lego/legoomni/include/legoracers.h | 6 ++-- LEGO1/lego/legoomni/include/legoracespecial.h | 9 +++-- .../lego/legoomni/src/paths/legopathactor.cpp | 6 ++-- LEGO1/lego/legoomni/src/race/carrace.cpp | 8 ++--- LEGO1/lego/legoomni/src/race/jetskirace.cpp | 8 ++--- .../lego/legoomni/src/race/legoraceactor.cpp | 4 +-- LEGO1/lego/legoomni/src/race/legoracers.cpp | 34 +++++++++---------- .../legoomni/src/race/legoracespecial.cpp | 16 ++++----- 9 files changed, 48 insertions(+), 45 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legopathactor.h b/LEGO1/lego/legoomni/include/legopathactor.h index 6566c6fb..8df0e9db 100644 --- a/LEGO1/lego/legoomni/include/legopathactor.h +++ b/LEGO1/lego/legoomni/include/legopathactor.h @@ -13,7 +13,7 @@ struct LegoPathEdgeContainer; struct LegoOrientedEdge; class LegoWEEdge; -extern MxLong g_unk0x100f3308; +extern MxLong g_timeLastHitSoundPlayed; extern const char* g_strHIT_WALL_SOUND; // VTABLE: LEGO1 0x100d6e28 diff --git a/LEGO1/lego/legoomni/include/legoracers.h b/LEGO1/lego/legoomni/include/legoracers.h index 76d1071d..3c8ffe07 100644 --- a/LEGO1/lego/legoomni/include/legoracers.h +++ b/LEGO1/lego/legoomni/include/legoracers.h @@ -144,10 +144,10 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { MxResult VTable0x9c() override; // vtable+0x9c virtual void SetMaxLinearVelocity(float p_maxLinearVelocity); - virtual void FUN_10012ff0(float p_param); + virtual void KickCamera(float p_param); virtual MxU32 HandleSkeletonKicks(float p_param1); - static void FUN_10012de0(); + static void InitYouCantStopSound(); static void InitSoundIndices(); // SYNTHETIC: LEGO1 0x10014240 @@ -155,7 +155,7 @@ class LegoRaceCar : public LegoCarRaceActor, public LegoRaceMap { private: undefined m_userState; // 0x54 - float m_unk0x58; // 0x58 + float m_kickStart; // 0x58 Mx3DPointFloat m_unk0x5c; // 0x5c // Names verified by BETA10 0x100cb4a9 diff --git a/LEGO1/lego/legoomni/include/legoracespecial.h b/LEGO1/lego/legoomni/include/legoracespecial.h index 97fa7d9f..bfa38632 100644 --- a/LEGO1/lego/legoomni/include/legoracespecial.h +++ b/LEGO1/lego/legoomni/include/legoracespecial.h @@ -44,8 +44,11 @@ class LegoCarRaceActor : public virtual LegoRaceActor { Vector3& p_v3 ) override; // vtable+0x6c void Animate(float p_time) override; // vtable+0x70 - void SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOrientedEdge*& p_edge, float& p_unk0xe4) - override; // vtable+0x98 + void SwitchBoundary( + LegoPathBoundary*& p_boundary, + LegoOrientedEdge*& p_edge, + float& p_unk0xe4 + ) override; // vtable+0x98 MxResult VTable0x9c() override; // vtable+0x9c // LegoCarRaceActor vtable @@ -83,7 +86,7 @@ class LegoCarRaceActor : public virtual LegoRaceActor { protected: MxFloat m_unk0x08; // 0x08 - MxU8 m_unk0x0c; // 0x0c + MxU8 m_animState; // 0x0c // Could be a multiplier for the maximum speed when going straight MxFloat m_unk0x10; // 0x10 diff --git a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp index 292d8214..0a8f6878 100644 --- a/LEGO1/lego/legoomni/src/paths/legopathactor.cpp +++ b/LEGO1/lego/legoomni/src/paths/legopathactor.cpp @@ -36,7 +36,7 @@ const char* g_strHIT_WALL_SOUND = "HIT_WALL_SOUND"; // GLOBAL: LEGO1 0x100f3308 // GLOBAL: BETA10 0x101f1e1c -MxLong g_unk0x100f3308 = 0; +MxLong g_timeLastHitSoundPlayed = 0; // FUNCTION: LEGO1 0x1002d700 // FUNCTION: BETA10 0x100ae6e0 @@ -291,8 +291,8 @@ MxS32 LegoPathActor::VTable0x8c(float p_time, Matrix4& p_transform) if (m_boundary == oldBoundary) { MxLong time = Timer()->GetTime(); - if (time - g_unk0x100f3308 > 1000) { - g_unk0x100f3308 = time; + if (time - g_timeLastHitSoundPlayed > 1000) { + g_timeLastHitSoundPlayed = time; const char* var = VariableTable()->GetVariable(g_strHIT_WALL_SOUND); if (var && var[0] != 0) { diff --git a/LEGO1/lego/legoomni/src/race/carrace.cpp b/LEGO1/lego/legoomni/src/race/carrace.cpp index e775a3be..86c50b4a 100644 --- a/LEGO1/lego/legoomni/src/race/carrace.cpp +++ b/LEGO1/lego/legoomni/src/race/carrace.cpp @@ -244,7 +244,7 @@ MxLong CarRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_raceState->m_unk0x28 = 2; RaceState::Entry* raceState = m_raceState->GetState(GameState()->GetActorId()); @@ -346,7 +346,7 @@ MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param) VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_infomain; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); GameState()->GetBackgroundColor()->SetValue("reset"); @@ -358,7 +358,7 @@ MxLong CarRace::HandleClick(LegoEventNotificationParam& p_param) VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_carraceExterior; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); GameState()->GetBackgroundColor()->SetValue("reset"); @@ -422,7 +422,7 @@ MxBool CarRace::Escape() NavController()->SetDeadZone(NavController()->GetDefaultDeadZone()); NavController()->SetTrackDefault(1); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); GameState()->GetBackgroundColor()->SetValue("reset"); m_destLocation = LegoGameState::e_infomain; diff --git a/LEGO1/lego/legoomni/src/race/jetskirace.cpp b/LEGO1/lego/legoomni/src/race/jetskirace.cpp index d1de13e9..54ad806c 100644 --- a/LEGO1/lego/legoomni/src/race/jetskirace.cpp +++ b/LEGO1/lego/legoomni/src/race/jetskirace.cpp @@ -130,7 +130,7 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) m_act1State->m_unk0x018 = 0; VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_jetraceExterior; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); break; @@ -138,7 +138,7 @@ MxLong JetskiRace::HandleClick(LegoEventNotificationParam& p_param) m_act1State->m_unk0x018 = 0; VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_destLocation = LegoGameState::e_infomain; result = 1; TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); @@ -191,7 +191,7 @@ MxLong JetskiRace::HandlePathStruct(LegoPathStructNotificationParam& p_param) VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); m_raceState->m_unk0x28 = 2; RaceState::Entry* raceStateEntry = m_raceState->GetState(GameState()->GetActorId()); @@ -292,6 +292,6 @@ MxBool JetskiRace::Escape() VariableTable()->SetVariable(g_raceState, ""); VariableTable()->SetVariable(g_strHIT_WALL_SOUND, ""); m_destLocation = LegoGameState::e_infomain; - LegoRaceCar::FUN_10012de0(); + LegoRaceCar::InitYouCantStopSound(); return TRUE; } diff --git a/LEGO1/lego/legoomni/src/race/legoraceactor.cpp b/LEGO1/lego/legoomni/src/race/legoraceactor.cpp index 965ba326..f005d996 100644 --- a/LEGO1/lego/legoomni/src/race/legoraceactor.cpp +++ b/LEGO1/lego/legoomni/src/race/legoraceactor.cpp @@ -31,8 +31,8 @@ MxS32 LegoRaceActor::VTable0x68(Vector3& p_v1, Vector3& p_v2, Vector3& p_v3) if (m_userNavFlag && result) { MxLong time = Timer()->GetTime(); - if (time - g_unk0x100f3308 > 1000) { - g_unk0x100f3308 = time; + if (time - g_timeLastHitSoundPlayed > 1000) { + g_timeLastHitSoundPlayed = time; const char* soundKey = VariableTable()->GetVariable(g_strHIT_ACTOR_SOUND); if (soundKey && *soundKey) { diff --git a/LEGO1/lego/legoomni/src/race/legoracers.cpp b/LEGO1/lego/legoomni/src/race/legoracers.cpp index 39c0c0e6..0a9fa1e2 100644 --- a/LEGO1/lego/legoomni/src/race/legoracers.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracers.cpp @@ -175,7 +175,7 @@ LegoRaceCar::LegoRaceCar() m_skelKick1Anim = 0; m_skelKick2Anim = 0; m_unk0x5c.Clear(); - m_unk0x58 = 0; + m_kickStart = 0; m_kick1B = 0; m_kick2B = 0; NotificationManager()->Register(this); @@ -198,10 +198,10 @@ MxLong LegoRaceCar::Notify(MxParam& p_param) // Initialized at LEGO1 0x10012db0 // GLOBAL: LEGO1 0x10102af0 // GLOBAL: BETA10 0x102114c0 -Mx3DPointFloat g_unk0x10102af0 = Mx3DPointFloat(0.0f, 2.0f, 0.0f); +Mx3DPointFloat g_hitOffset = Mx3DPointFloat(0.0f, 2.0f, 0.0f); // FUNCTION: LEGO1 0x10012de0 -void LegoRaceCar::FUN_10012de0() +void LegoRaceCar::InitYouCantStopSound() { // Init to TRUE so we don't play "you can't stop in the middle of the race!" before the player ever moves g_playedYouCantStopSound = TRUE; @@ -226,7 +226,7 @@ void LegoRaceCar::InitSoundIndices() void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed) { if (!m_userNavFlag) { - if (!LegoCarRaceActor::m_unk0x0c) { + if (!LegoCarRaceActor::m_animState) { m_maxLinearVel = p_worldSpeed; } LegoAnimActor::SetWorldSpeed(p_worldSpeed); @@ -241,7 +241,7 @@ void LegoRaceCar::SetWorldSpeed(MxFloat p_worldSpeed) void LegoRaceCar::SetMaxLinearVelocity(float p_maxLinearVelocity) { if (p_maxLinearVelocity < 0) { - LegoCarRaceActor::m_unk0x0c = 2; + LegoCarRaceActor::m_animState = 2; m_maxLinearVel = 0; SetWorldSpeed(0); } @@ -296,7 +296,7 @@ void LegoRaceCar::ParseAction(char* p_extra) // FUNCTION: LEGO1 0x10012ff0 // FUNCTION: BETA10 0x100cb60e -void LegoRaceCar::FUN_10012ff0(float p_param) +void LegoRaceCar::KickCamera(float p_param) { LegoAnimActorStruct* a; // called `a` in BETA10 float deltaTime; @@ -312,7 +312,7 @@ void LegoRaceCar::FUN_10012ff0(float p_param) assert(a && a->GetAnimTreePtr() && a->GetAnimTreePtr()->GetCamAnim()); if (a->GetAnimTreePtr()) { - deltaTime = p_param - m_unk0x58; + deltaTime = p_param - m_kickStart; if (a->GetDuration() <= deltaTime || deltaTime < 0.0) { if (m_userState == LEGORACECAR_KICK1) { @@ -387,7 +387,7 @@ MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1) return FALSE; } - m_unk0x58 = p_param1; + m_kickStart = p_param1; SoundManager()->GetCacheSoundManager()->Play(g_soundSkel3, NULL, FALSE); return TRUE; @@ -398,7 +398,7 @@ MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1) void LegoRaceCar::Animate(float p_time) { if (m_userNavFlag && (m_userState == LEGORACECAR_KICK1 || m_userState == LEGORACECAR_KICK2)) { - FUN_10012ff0(p_time); + KickCamera(p_time); return; } @@ -410,7 +410,7 @@ void LegoRaceCar::Animate(float p_time) } } - if (LegoCarRaceActor::m_unk0x0c == 1) { + if (LegoCarRaceActor::m_animState == 1) { FUN_1005d4b0(); if (!m_userNavFlag) { @@ -468,7 +468,7 @@ MxResult LegoRaceCar::HitActor(LegoPathActor* p_actor, MxBool p_bool) assert(roi); matr = roi->GetLocal2World(); - Vector3(matr[3]) += g_unk0x10102af0; + Vector3(matr[3]) += g_hitOffset; roi->SetLocal2World(matr); p_actor->SetActorState(c_two); @@ -513,7 +513,7 @@ MxResult LegoRaceCar::HitActor(LegoPathActor* p_actor, MxBool p_bool) if (soundKey) { SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE); - g_timeLastRaceCarSoundPlayed = g_unk0x100f3308 = time; + g_timeLastRaceCarSoundPlayed = g_timeLastHitSoundPlayed = time; } } @@ -579,7 +579,7 @@ void LegoJetski::InitSoundIndices() void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed) { if (!m_userNavFlag) { - if (!LegoCarRaceActor::m_unk0x0c) { + if (!LegoCarRaceActor::m_animState) { m_maxLinearVel = p_worldSpeed; } LegoAnimActor::SetWorldSpeed(p_worldSpeed); @@ -594,7 +594,7 @@ void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed) void LegoJetski::FUN_100136f0(float p_worldSpeed) { if (p_worldSpeed < 0) { - LegoCarRaceActor::m_unk0x0c = 2; + LegoCarRaceActor::m_animState = 2; m_maxLinearVel = 0; SetWorldSpeed(0); } @@ -609,7 +609,7 @@ void LegoJetski::Animate(float p_time) { LegoJetskiRaceActor::Animate(p_time); - if (LegoCarRaceActor::m_unk0x0c == 1) { + if (LegoCarRaceActor::m_animState == 1) { FUN_1005d4b0(); if (!m_userNavFlag) { @@ -682,7 +682,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool) LegoROI* roi = p_actor->GetROI(); matr = roi->GetLocal2World(); - Vector3(matr[3]) += g_unk0x10102af0; + Vector3(matr[3]) += g_hitOffset; roi->SetLocal2World(matr); p_actor->SetActorState(c_two); @@ -711,7 +711,7 @@ MxResult LegoJetski::HitActor(LegoPathActor* p_actor, MxBool p_bool) if (soundKey) { SoundManager()->GetCacheSoundManager()->Play(soundKey, NULL, FALSE); - g_timeLastJetskiSoundPlayed = g_unk0x100f3308 = time; + g_timeLastJetskiSoundPlayed = g_timeLastHitSoundPlayed = time; } } diff --git a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp index 81f9255f..e599c633 100644 --- a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp @@ -44,7 +44,7 @@ LegoCarRaceActor::LegoCarRaceActor() { m_unk0x08 = 1.0f; m_unk0x70 = 0.0f; - m_unk0x0c = 0; + m_animState = 0; m_maxLinearVel = 0.0f; m_frequencyFactor = 1.0f; m_unk0x1c = 0; @@ -223,18 +223,18 @@ void LegoCarRaceActor::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoOriente // FUNCTION: BETA10 0x100cdbae void LegoCarRaceActor::Animate(float p_time) { - // m_unk0x0c is not an MxBool, there are places where it is set to 2 or higher - if (m_unk0x0c == 0) { + // m_animState is not an MxBool, there are places where it is set to 2 or higher + if (m_animState == 0) { const char* value = VariableTable()->GetVariable(g_raceState); if (strcmpi(value, g_racing) == 0) { - m_unk0x0c = 1; + m_animState = 1; m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } } - if (m_unk0x0c == 1) { + if (m_animState == 1) { LegoAnimActor::Animate(p_time); } } @@ -398,10 +398,10 @@ MxS32 LegoJetskiRaceActor::VTable0x1c(LegoPathBoundary* p_boundary, LegoEdge* p_ // FUNCTION: LEGO1 0x10081550 void LegoJetskiRaceActor::Animate(float p_time) { - if (m_unk0x0c == 0) { + if (m_animState == 0) { const LegoChar* raceState = VariableTable()->GetVariable(g_raceState); if (!stricmp(raceState, g_racing)) { - m_unk0x0c = 1; + m_animState = 1; m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } @@ -410,7 +410,7 @@ void LegoJetskiRaceActor::Animate(float p_time) } } - if (m_unk0x0c == 1) { + if (m_animState == 1) { LegoAnimActor::Animate(p_time); } } From 02dd261ca91467688a6e0ed1fbffa50e99300338 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Fri, 27 Jun 2025 16:15:08 -0700 Subject: [PATCH 15/22] Fix isle/master merge --- LEGO1/lego/legoomni/src/race/legoracespecial.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp index 6fa64058..7fe58c71 100644 --- a/LEGO1/lego/legoomni/src/race/legoracespecial.cpp +++ b/LEGO1/lego/legoomni/src/race/legoracespecial.cpp @@ -401,13 +401,8 @@ void LegoJetskiRaceActor::Animate(float p_time) { if (m_animState == 0) { const LegoChar* raceState = VariableTable()->GetVariable(g_raceState); -<<<<<<< HEAD if (!SDL_strcasecmp(raceState, g_racing)) { - m_unk0x0c = 1; -======= - if (!stricmp(raceState, g_racing)) { m_animState = 1; ->>>>>>> isle/master m_lastTime = p_time - 1.0f; m_unk0x1c = p_time; } From 89539a64f17cbd0c21c65aa66cf7af63ffaed924 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sat, 28 Jun 2025 16:28:13 +0200 Subject: [PATCH 16/22] Clear unknown 0x10 in `LegoEntity` (#1593) The naming is a bit weird, as only one bit is used so there are no other usages of this value. --- LEGO1/lego/legoomni/include/legoanimpresenter.h | 2 +- LEGO1/lego/legoomni/include/legoentity.h | 10 +++++----- LEGO1/lego/legoomni/src/entity/legoactor.cpp | 2 +- LEGO1/lego/legoomni/src/entity/legoentity.cpp | 8 ++++---- LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp | 2 +- LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp | 12 ++++++------ 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legoanimpresenter.h b/LEGO1/lego/legoomni/include/legoanimpresenter.h index 85917cbc..a3d6cd60 100644 --- a/LEGO1/lego/legoomni/include/legoanimpresenter.h +++ b/LEGO1/lego/legoomni/include/legoanimpresenter.h @@ -121,7 +121,7 @@ class LegoAnimPresenter : public MxVideoPresenter { void SubstituteVariables(); void FUN_1006b900(LegoAnim* p_anim, MxLong p_time, Matrix4* p_matrix); void FUN_1006b9a0(LegoAnim* p_anim, MxLong p_time, Matrix4* p_matrix); - void FUN_1006c8a0(MxBool p_bool); + void SetDisabled(MxBool p_disabled); LegoAnim* m_anim; // 0x64 LegoROI** m_roiMap; // 0x68 diff --git a/LEGO1/lego/legoomni/include/legoentity.h b/LEGO1/lego/legoomni/include/legoentity.h index eecf6867..2859b6ac 100644 --- a/LEGO1/lego/legoomni/include/legoentity.h +++ b/LEGO1/lego/legoomni/include/legoentity.h @@ -28,7 +28,7 @@ class LegoEntity : public MxEntity { }; enum { - c_altBit1 = 0x01 + c_disabled = 0x01 }; LegoEntity() { Init(); } @@ -83,7 +83,7 @@ class LegoEntity : public MxEntity { Mx3DPointFloat GetWorldUp(); Mx3DPointFloat GetWorldPosition(); - MxBool GetUnknown0x10IsSet(MxU8 p_flag) { return m_unk0x10 & p_flag; } + MxBool IsInteraction(MxU8 p_flag) { return m_interaction & p_flag; } MxBool GetFlagsIsSet(MxU8 p_flag) { return m_flags & p_flag; } MxU8 GetFlags() { return m_flags; } @@ -101,14 +101,14 @@ class LegoEntity : public MxEntity { void SetFlags(MxU8 p_flags) { m_flags = p_flags; } void SetFlag(MxU8 p_flag) { m_flags |= p_flag; } void ClearFlag(MxU8 p_flag) { m_flags &= ~p_flag; } - void SetUnknown0x10Flag(MxU8 p_flag) { m_unk0x10 |= p_flag; } - void ClearUnknown0x10Flag(MxU8 p_flag) { m_unk0x10 &= ~p_flag; } + void SetInteractionFlag(MxU8 p_flag) { m_interaction |= p_flag; } + void ClearInteractionFlag(MxU8 p_flag) { m_interaction &= ~p_flag; } protected: void Init(); void SetWorld(); - MxU8 m_unk0x10; // 0x10 + MxU8 m_interaction; // 0x10 MxU8 m_flags; // 0x11 Mx3DPointFloat m_worldLocation; // 0x14 Mx3DPointFloat m_worldDirection; // 0x28 diff --git a/LEGO1/lego/legoomni/src/entity/legoactor.cpp b/LEGO1/lego/legoomni/src/entity/legoactor.cpp index c1ce3f74..4b0428b8 100644 --- a/LEGO1/lego/legoomni/src/entity/legoactor.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoactor.cpp @@ -18,7 +18,7 @@ LegoActor::LegoActor() m_frequencyFactor = 0.0f; m_sound = NULL; m_unk0x70 = 0.0f; - m_unk0x10 = 0; + m_interaction = 0; m_actorId = 0; } diff --git a/LEGO1/lego/legoomni/src/entity/legoentity.cpp b/LEGO1/lego/legoomni/src/entity/legoentity.cpp index 467d65d8..b76da503 100644 --- a/LEGO1/lego/legoomni/src/entity/legoentity.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoentity.cpp @@ -29,7 +29,7 @@ void LegoEntity::Init() m_roi = NULL; m_cameraFlag = FALSE; m_siFile = NULL; - m_unk0x10 = 0; + m_interaction = 0; m_flags = 0; m_actionType = Extra::ActionType::e_unknown; m_targetEntityId = -1; @@ -265,7 +265,7 @@ void LegoEntity::ParseAction(char* p_extra) // FUNCTION: BETA10 0x1007ee87 void LegoEntity::ClickSound(MxBool p_und) { - if (!GetUnknown0x10IsSet(c_altBit1)) { + if (!IsInteraction(c_disabled)) { MxU32 objectId = 0; const char* name = m_roi->GetName(); @@ -297,7 +297,7 @@ void LegoEntity::ClickSound(MxBool p_und) // FUNCTION: BETA10 0x1007f062 void LegoEntity::ClickAnimation() { - if (!GetUnknown0x10IsSet(c_altBit1)) { + if (!IsInteraction(c_disabled)) { MxU32 objectId = 0; MxDSAction action; const char* name = m_roi->GetName(); @@ -329,7 +329,7 @@ void LegoEntity::ClickAnimation() action.SetObjectId(objectId); action.AppendExtra(strlen(extra) + 1, extra); LegoOmni::GetInstance()->GetAnimationManager()->StartEntityAction(action, this); - m_unk0x10 |= c_altBit1; + m_interaction |= c_disabled; } } } diff --git a/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp b/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp index 1c306c1b..fa8158da 100644 --- a/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp +++ b/LEGO1/lego/legoomni/src/entity/legonavcontroller.cpp @@ -691,7 +691,7 @@ MxLong LegoNavController::Notify(MxParam& p_param) for (MxS32 i = 0; i < numPlants; i++) { LegoEntity* entity = plantMgr->CreatePlant(i, NULL, LegoOmni::e_act1); - if (entity != NULL && !entity->GetUnknown0x10IsSet(LegoEntity::c_altBit1)) { + if (entity != NULL && !entity->IsInteraction(LegoEntity::c_disabled)) { LegoROI* roi = entity->GetROI(); if (roi != NULL && roi->GetVisibility()) { diff --git a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp index 852263fc..73918606 100644 --- a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp @@ -790,7 +790,7 @@ void LegoAnimPresenter::StartingTickle() } FUN_10069b10(); - FUN_1006c8a0(TRUE); + SetDisabled(TRUE); if (m_unk0x78 == NULL) { if (fabs(m_action->GetDirection()[0]) >= 0.00000047683716F || @@ -1090,7 +1090,7 @@ void LegoAnimPresenter::EndAction() } } - FUN_1006c8a0(FALSE); + SetDisabled(FALSE); FUN_1006ab70(); VTable0x90(); @@ -1151,18 +1151,18 @@ void LegoAnimPresenter::VTable0x90() } // FUNCTION: LEGO1 0x1006c8a0 -void LegoAnimPresenter::FUN_1006c8a0(MxBool p_bool) +void LegoAnimPresenter::SetDisabled(MxBool p_disabled) { if (m_roiMapSize != 0 && m_roiMap != NULL) { for (MxU32 i = 1; i <= m_roiMapSize; i++) { LegoEntity* entity = m_roiMap[i]->GetEntity(); if (entity != NULL) { - if (p_bool) { - entity->SetUnknown0x10Flag(LegoEntity::c_altBit1); + if (p_disabled) { + entity->SetInteractionFlag(LegoEntity::c_disabled); } else { - entity->ClearUnknown0x10Flag(LegoEntity::c_altBit1); + entity->ClearInteractionFlag(LegoEntity::c_disabled); } } } From 9dcc701fcb18eb84e7d57ae3454a9391decdba6d Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sat, 28 Jun 2025 20:32:09 +0200 Subject: [PATCH 17/22] Clear unknowns in GetSoundId (#1594) --- LEGO1/lego/legoomni/include/legobuildingmanager.h | 2 +- LEGO1/lego/legoomni/include/legocharactermanager.h | 2 +- LEGO1/lego/legoomni/include/legoentity.h | 14 +++++++------- LEGO1/lego/legoomni/include/legoplantmanager.h | 2 +- .../legoomni/src/common/legobuildingmanager.cpp | 12 ++++++------ .../legoomni/src/common/legocharactermanager.cpp | 12 ++++++------ .../lego/legoomni/src/common/legoplantmanager.cpp | 12 ++++++------ LEGO1/lego/legoomni/src/entity/legoentity.cpp | 8 ++++---- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legobuildingmanager.h b/LEGO1/lego/legoomni/include/legobuildingmanager.h index 3d251c98..c5a69309 100644 --- a/LEGO1/lego/legoomni/include/legobuildingmanager.h +++ b/LEGO1/lego/legoomni/include/legobuildingmanager.h @@ -78,7 +78,7 @@ class LegoBuildingManager : public MxCore { MxBool SwitchMove(LegoEntity* p_entity); MxBool SwitchMood(LegoEntity* p_entity); MxU32 GetAnimationId(LegoEntity* p_entity); - MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state); + MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood); MxBool DecrementCounter(LegoEntity* p_entity); MxBool DecrementCounter(MxS32 p_index); MxBool DecrementCounter(LegoBuildingInfo* p_data); diff --git a/LEGO1/lego/legoomni/include/legocharactermanager.h b/LEGO1/lego/legoomni/include/legocharactermanager.h index 174db01e..c5a0c242 100644 --- a/LEGO1/lego/legoomni/include/legocharactermanager.h +++ b/LEGO1/lego/legoomni/include/legocharactermanager.h @@ -86,7 +86,7 @@ class LegoCharacterManager { MxBool SwitchMove(LegoROI* p_roi); MxBool SwitchMood(LegoROI* p_roi); MxU32 GetAnimationId(LegoROI* p_roi); - MxU32 GetSoundId(LegoROI* p_roi, MxBool p_und); + MxU32 GetSoundId(LegoROI* p_roi, MxBool p_basedOnMood); MxU8 GetMood(LegoROI* p_roi); LegoROI* CreateAutoROI(const char* p_name, const char* p_lodName, MxBool p_createEntity); MxResult UpdateBoundingSphereAndBox(LegoROI* p_roi); diff --git a/LEGO1/lego/legoomni/include/legoentity.h b/LEGO1/lego/legoomni/include/legoentity.h index 2859b6ac..a6d9d9ee 100644 --- a/LEGO1/lego/legoomni/include/legoentity.h +++ b/LEGO1/lego/legoomni/include/legoentity.h @@ -68,13 +68,13 @@ class LegoEntity : public MxEntity { // FUNCTION: BETA10 0x10013260 virtual void SetWorldSpeed(MxFloat p_worldSpeed) { m_worldSpeed = p_worldSpeed; } // vtable+0x30 - virtual void ClickSound(MxBool p_und); // vtable+0x34 - virtual void ClickAnimation(); // vtable+0x38 - virtual void SwitchVariant(); // vtable+0x3c - virtual void SwitchSound(); // vtable+0x40 - virtual void SwitchMove(); // vtable+0x44 - virtual void SwitchColor(LegoROI* p_roi); // vtable+0x48 - virtual void SwitchMood(); // vtable+0x4c + virtual void ClickSound(MxBool p_basedOnMood); // vtable+0x34 + virtual void ClickAnimation(); // vtable+0x38 + virtual void SwitchVariant(); // vtable+0x3c + virtual void SwitchSound(); // vtable+0x40 + virtual void SwitchMove(); // vtable+0x44 + virtual void SwitchColor(LegoROI* p_roi); // vtable+0x48 + virtual void SwitchMood(); // vtable+0x4c void FUN_10010c30(); void SetType(MxU8 p_type); diff --git a/LEGO1/lego/legoomni/include/legoplantmanager.h b/LEGO1/lego/legoomni/include/legoplantmanager.h index 91fe49c8..c3b2c4e1 100644 --- a/LEGO1/lego/legoomni/include/legoplantmanager.h +++ b/LEGO1/lego/legoomni/include/legoplantmanager.h @@ -49,7 +49,7 @@ class LegoPlantManager : public MxCore { MxBool SwitchMove(LegoEntity* p_entity); MxBool SwitchMood(LegoEntity* p_entity); MxU32 GetAnimationId(LegoEntity* p_entity); - MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_state); + MxU32 GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood); LegoPlantInfo* GetInfoArray(MxS32& p_length); LegoEntity* CreatePlant(MxS32 p_index, LegoWorld* p_world, LegoOmni::World p_worldId); MxBool DecrementCounter(LegoEntity* p_entity); diff --git a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp index d46b8a49..1e4a8b82 100644 --- a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp @@ -199,10 +199,10 @@ LegoBuildingInfo g_buildingInfoInit[16] = { MxU32 LegoBuildingManager::g_maxSound = 6; // GLOBAL: LEGO1 0x100f373c -MxU32 g_unk0x100f373c = 0x3c; +MxU32 g_buildingSoundIdOffset = 0x3c; // GLOBAL: LEGO1 0x100f3740 -MxU32 g_unk0x100f3740 = 0x42; +MxU32 g_buildingSoundIdMoodOffset = 0x42; // clang-format off // GLOBAL: LEGO1 0x100f3788 @@ -548,7 +548,7 @@ MxU32 LegoBuildingManager::GetAnimationId(LegoEntity* p_entity) // FUNCTION: LEGO1 0x1002ff40 // FUNCTION: BETA10 0x10064398 -MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_state) +MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood) { LegoBuildingInfo* info = GetInfo(p_entity); @@ -556,12 +556,12 @@ MxU32 LegoBuildingManager::GetSoundId(LegoEntity* p_entity, MxBool p_state) return 0; } - if (p_state) { - return info->m_mood + g_unk0x100f3740; + if (p_basedOnMood) { + return info->m_mood + g_buildingSoundIdMoodOffset; } if (info != NULL) { - return info->m_sound + g_unk0x100f373c; + return info->m_sound + g_buildingSoundIdOffset; } return 0; diff --git a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp index 80b69efc..ff7cf23b 100644 --- a/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legocharactermanager.cpp @@ -37,10 +37,10 @@ MxU32 g_characterAnimationId = 10; char* LegoCharacterManager::g_customizeAnimFile = NULL; // GLOBAL: LEGO1 0x100fc4d8 -MxU32 g_soundIdOffset = 50; +MxU32 g_characterSoundIdOffset = 50; // GLOBAL: LEGO1 0x100fc4dc -MxU32 g_soundIdMoodOffset = 66; +MxU32 g_characterSoundIdMoodOffset = 66; // GLOBAL: LEGO1 0x100fc4e8 MxU32 g_headTextureCounter = 0; @@ -931,16 +931,16 @@ MxU32 LegoCharacterManager::GetAnimationId(LegoROI* p_roi) // FUNCTION: LEGO1 0x10085140 // FUNCTION: BETA10 0x10076855 -MxU32 LegoCharacterManager::GetSoundId(LegoROI* p_roi, MxBool p_und) +MxU32 LegoCharacterManager::GetSoundId(LegoROI* p_roi, MxBool p_basedOnMood) { LegoActorInfo* info = GetActorInfo(p_roi); - if (p_und) { - return info->m_mood + g_soundIdMoodOffset; + if (p_basedOnMood) { + return info->m_mood + g_characterSoundIdMoodOffset; } if (info != NULL) { - return info->m_sound + g_soundIdOffset; + return info->m_sound + g_characterSoundIdOffset; } else { return 0; diff --git a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp index 766edf79..4e8e2626 100644 --- a/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legoplantmanager.cpp @@ -40,10 +40,10 @@ MxU8 g_counters[] = {1, 2, 2, 3}; MxU32 LegoPlantManager::g_maxSound = 8; // GLOBAL: LEGO1 0x100f3160 -MxU32 g_unk0x100f3160 = 56; +MxU32 g_plantSoundIdOffset = 56; // GLOBAL: LEGO1 0x100f3164 -MxU32 g_unk0x100f3164 = 66; +MxU32 g_plantSoundIdMoodOffset = 66; // GLOBAL: LEGO1 0x100f3168 MxS32 LegoPlantManager::g_maxMove[4] = {3, 3, 3, 3}; @@ -513,16 +513,16 @@ MxU32 LegoPlantManager::GetAnimationId(LegoEntity* p_entity) // FUNCTION: LEGO1 0x10026ba0 // FUNCTION: BETA10 0x100c61ba -MxU32 LegoPlantManager::GetSoundId(LegoEntity* p_entity, MxBool p_state) +MxU32 LegoPlantManager::GetSoundId(LegoEntity* p_entity, MxBool p_basedOnMood) { LegoPlantInfo* info = GetInfo(p_entity); - if (p_state) { - return (info->m_mood & 1) + g_unk0x100f3164; + if (p_basedOnMood) { + return (info->m_mood & 1) + g_plantSoundIdMoodOffset; } if (info != NULL) { - return info->m_sound + g_unk0x100f3160; + return info->m_sound + g_plantSoundIdOffset; } return 0; diff --git a/LEGO1/lego/legoomni/src/entity/legoentity.cpp b/LEGO1/lego/legoomni/src/entity/legoentity.cpp index b76da503..9ac9aad6 100644 --- a/LEGO1/lego/legoomni/src/entity/legoentity.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoentity.cpp @@ -263,7 +263,7 @@ void LegoEntity::ParseAction(char* p_extra) // FUNCTION: LEGO1 0x10010f10 // FUNCTION: BETA10 0x1007ee87 -void LegoEntity::ClickSound(MxBool p_und) +void LegoEntity::ClickSound(MxBool p_basedOnMood) { if (!IsInteraction(c_disabled)) { MxU32 objectId = 0; @@ -271,15 +271,15 @@ void LegoEntity::ClickSound(MxBool p_und) switch (m_type) { case e_actor: - objectId = CharacterManager()->GetSoundId(m_roi, p_und); + objectId = CharacterManager()->GetSoundId(m_roi, p_basedOnMood); break; case e_unk1: break; case e_plant: - objectId = PlantManager()->GetSoundId(this, p_und); + objectId = PlantManager()->GetSoundId(this, p_basedOnMood); break; case e_building: - objectId = BuildingManager()->GetSoundId(this, p_und); + objectId = BuildingManager()->GetSoundId(this, p_basedOnMood); break; } From 0982038453babc6233710fea5318b1437516a188 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sun, 29 Jun 2025 00:38:48 +0200 Subject: [PATCH 18/22] Clear unknown in `Hospital` (#1595) --- LEGO1/lego/legoomni/include/hospital.h | 2 +- LEGO1/lego/legoomni/src/worlds/hospital.cpp | 22 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/LEGO1/lego/legoomni/include/hospital.h b/LEGO1/lego/legoomni/include/hospital.h index 4a136568..2a90764b 100644 --- a/LEGO1/lego/legoomni/include/hospital.h +++ b/LEGO1/lego/legoomni/include/hospital.h @@ -123,7 +123,7 @@ class Hospital : public LegoWorld { MxLong m_copLedAnimTimer; // 0x11c MxLong m_pizzaLedAnimTimer; // 0x120 MxLong m_time; // 0x124 - undefined m_unk0x128; // 0x128 + MxBool m_exited; // 0x128 }; #endif // HOSPITAL_H diff --git a/LEGO1/lego/legoomni/src/worlds/hospital.cpp b/LEGO1/lego/legoomni/src/worlds/hospital.cpp index 4aa4b5a8..dbb1c00c 100644 --- a/LEGO1/lego/legoomni/src/worlds/hospital.cpp +++ b/LEGO1/lego/legoomni/src/worlds/hospital.cpp @@ -46,7 +46,7 @@ Hospital::Hospital() m_flashingLeds = 0; m_copLedAnimTimer = 0; m_pizzaLedAnimTimer = 0; - m_unk0x128 = 0; + m_exited = FALSE; NotificationManager()->Register(this); } @@ -367,8 +367,8 @@ MxLong Hospital::HandleEndAction(MxEndActionNotificationParam& p_param) act1State = (Act1State*) GameState()->GetState("Act1State"); act1State->SetUnknown18(9); case HospitalState::e_exitToFront: - if (m_unk0x128 == 0) { - m_unk0x128 = 1; + if (m_exited == FALSE) { + m_exited = TRUE; m_destLocation = LegoGameState::e_hospitalExited; DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim); @@ -376,8 +376,8 @@ MxLong Hospital::HandleEndAction(MxEndActionNotificationParam& p_param) } break; case HospitalState::e_exitToInfocenter: - if (m_unk0x128 == 0) { - m_unk0x128 = 1; + if (m_exited == FALSE) { + m_exited = TRUE; m_destLocation = LegoGameState::e_infomain; DeleteObjects(&m_atomId, HospitalScript::c_hho002cl_RunAnim, HospitalScript::c_hho006cl_RunAnim); @@ -410,8 +410,8 @@ MxLong Hospital::HandleButtonDown(LegoControlManagerNotificationParam& p_param) m_interactionMode = 3; if (m_hospitalState->m_state == HospitalState::e_explainQuestShort) { - if (m_unk0x128 == 0) { - m_unk0x128 = 1; + if (m_exited == FALSE) { + m_exited = TRUE; TickleManager()->UnregisterClient(this); @@ -566,8 +566,8 @@ MxBool Hospital::HandleControl(LegoControlManagerNotificationParam& p_param) m_currentAction = HospitalScript::c_hho016cl_RunAnim; m_setWithCurrentAction = 1; } - else if (m_unk0x128 == 0) { - m_unk0x128 = 1; + else if (m_exited == FALSE) { + m_exited = TRUE; m_hospitalState->m_state = HospitalState::e_exitImmediately; m_destLocation = LegoGameState::e_infomain; @@ -587,8 +587,8 @@ MxBool Hospital::HandleControl(LegoControlManagerNotificationParam& p_param) m_currentAction = HospitalScript::c_hho016cl_RunAnim; m_setWithCurrentAction = 1; } - else if (m_unk0x128 == 0) { - m_unk0x128 = 1; + else if (m_exited == FALSE) { + m_exited = TRUE; m_hospitalState->m_state = HospitalState::e_exitImmediately; m_destLocation = LegoGameState::e_hospitalExited; From 020969c4831819dc766398140cbd9867e8b90a7b Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sat, 28 Jun 2025 17:49:46 -0700 Subject: [PATCH 19/22] Add transition type to ini (#441) --- ISLE/isleapp.cpp | 15 +++++++++++---- ISLE/isleapp.h | 2 ++ LEGO1/lego/legoomni/include/mxtransitionmanager.h | 2 ++ .../legoomni/src/common/mxtransitionmanager.cpp | 9 ++++++++- tools/ncc/skip.yml | 1 + 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index a5091ece..4ee1980f 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -96,11 +96,7 @@ IsleApp::IsleApp() m_cdPath = NULL; m_deviceId = NULL; m_savePath = NULL; -#ifdef __EMSCRIPTEN__ - m_fullScreen = FALSE; -#else m_fullScreen = TRUE; -#endif m_flipSurfaces = FALSE; m_backBuffersInVram = TRUE; m_using8bit = FALSE; @@ -140,6 +136,7 @@ IsleApp::IsleApp() m_iniPath = NULL; m_maxLod = RealtimeView::GetUserMaxLOD(); m_maxAllowedExtras = m_islandQuality <= 1 ? 10 : 20; + m_transitionType = MxTransitionManager::e_mosaic; } // FUNCTION: ISLE 0x4011a0 @@ -726,6 +723,7 @@ MxResult IsleApp::SetupWindow() LegoBuildingManager::configureLegoBuildingManager(m_islandQuality); LegoROI::configureLegoROI(iVar10); LegoAnimationManager::configureLegoAnimationManager(m_maxAllowedExtras); + MxTransitionManager::configureMxTransitionManager(m_transitionType); RealtimeView::SetUserMaxLOD(m_maxLod); if (LegoOmni::GetInstance()) { if (LegoOmni::GetInstance()->GetInputManager()) { @@ -824,6 +822,7 @@ bool IsleApp::LoadConfig() SDL_snprintf(buf, sizeof(buf), "%f", m_maxLod); iniparser_set(dict, "isle:Max LOD", buf); iniparser_set(dict, "isle:Max Allowed Extras", SDL_itoa(m_maxAllowedExtras, buf, 10)); + iniparser_set(dict, "isle:Transition Type", SDL_itoa(m_transitionType, buf, 10)); iniparser_dump_ini(dict, iniFP); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "New config written at '%s'", iniConfig); @@ -853,7 +852,13 @@ bool IsleApp::LoadConfig() strcpy(m_mediaPath, mediaPath); m_flipSurfaces = iniparser_getboolean(dict, "isle:Flip Surfaces", m_flipSurfaces); + +#ifdef __EMSCRIPTEN__ + m_fullScreen = FALSE; +#else m_fullScreen = iniparser_getboolean(dict, "isle:Full Screen", m_fullScreen); +#endif + m_wideViewAngle = iniparser_getboolean(dict, "isle:Wide View Angle", m_wideViewAngle); m_use3dSound = iniparser_getboolean(dict, "isle:3DSound", m_use3dSound); m_useMusic = iniparser_getboolean(dict, "isle:Music", m_useMusic); @@ -880,6 +885,8 @@ bool IsleApp::LoadConfig() m_islandTexture = iniparser_getint(dict, "isle:Island Texture", m_islandTexture); m_maxLod = iniparser_getdouble(dict, "isle:Max LOD", m_maxLod); m_maxAllowedExtras = iniparser_getint(dict, "isle:Max Allowed Extras", m_maxAllowedExtras); + m_transitionType = + (MxTransitionManager::TransitionType) iniparser_getint(dict, "isle:Transition Type", m_transitionType); const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL); if (deviceId != NULL) { diff --git a/ISLE/isleapp.h b/ISLE/isleapp.h index 2766607f..dd7bfdb1 100644 --- a/ISLE/isleapp.h +++ b/ISLE/isleapp.h @@ -3,6 +3,7 @@ #include "lego1_export.h" #include "legoutils.h" +#include "mxtransitionmanager.h" #include "mxtypes.h" #include "mxvideoparam.h" @@ -91,6 +92,7 @@ class IsleApp { char* m_iniPath; MxFloat m_maxLod; MxU32 m_maxAllowedExtras; + MxTransitionManager::TransitionType m_transitionType; }; extern IsleApp* g_isle; diff --git a/LEGO1/lego/legoomni/include/mxtransitionmanager.h b/LEGO1/lego/legoomni/include/mxtransitionmanager.h index 50b80055..dd9dc9ed 100644 --- a/LEGO1/lego/legoomni/include/mxtransitionmanager.h +++ b/LEGO1/lego/legoomni/include/mxtransitionmanager.h @@ -55,6 +55,8 @@ class MxTransitionManager : public MxCore { TransitionType GetTransitionType() { return m_mode; } + LEGO1_EXPORT static void configureMxTransitionManager(TransitionType p_transitionManagerConfig); + // SYNTHETIC: LEGO1 0x1004b9e0 // MxTransitionManager::`scalar deleting destructor' diff --git a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp index b251c1c0..d54563ab 100644 --- a/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/mxtransitionmanager.cpp @@ -16,6 +16,8 @@ DECOMP_SIZE_ASSERT(MxTransitionManager, 0x900) +MxTransitionManager::TransitionType g_transitionManagerConfig = MxTransitionManager::e_mosaic; + // GLOBAL: LEGO1 0x100f4378 RECT g_fullScreenRect = {0, 0, 640, 480}; @@ -105,7 +107,7 @@ MxResult MxTransitionManager::StartTransition( backgroundAudioManager->Stop(); } - m_mode = p_animationType; + m_mode = g_transitionManagerConfig; m_copyFlags.m_bit0 = p_doCopy; @@ -632,3 +634,8 @@ void MxTransitionManager::SetupCopyRect(LPDDSURFACEDESC p_ddsc) ); } } + +void MxTransitionManager::configureMxTransitionManager(TransitionType p_transitionManagerConfig) +{ + g_transitionManagerConfig = p_transitionManagerConfig; +} diff --git a/tools/ncc/skip.yml b/tools/ncc/skip.yml index 65de1ce7..c5ceef72 100644 --- a/tools/ncc/skip.yml +++ b/tools/ncc/skip.yml @@ -4,6 +4,7 @@ configureLegoModelPresenter(MxS32): 'DLL exported function' configureLegoPartPresenter(MxS32, MxS32): 'DLL exported function' configureLegoROI(int): 'DLL exported function' configureLegoWorldPresenter(MxS32): 'DLL exported function' +configureMxTransitionManager(TransitionType): 'DLL exported function' GetNoCD_SourceName(): 'DLL exported function' m_3dView: 'Allow this variable name' m_3dManager: 'Allow this variable name' From e87184b50227f8fb15957a573da7ca9a2b840fd6 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sun, 29 Jun 2025 23:47:09 +0800 Subject: [PATCH 20/22] Fix the OpenGL backends on non-glx Linux platforms (and remove GLEW dependency) (#446) * Work around issues with depth-buffer size on EGL-based platforms The OpenGL 1.1 and OpenGL ES 2.0 backends can break on EGL-based platforms, such as Wayland, or X11 with SDL_VIDEO_FORCE_EGL=1. One of the reasons for this (the other being glew on the GL1.1 backend) is that SDL/egl get very confused by the way we set OpenGL attributes, particularly SDL_GL_DEPTH_SIZE, resulting in SDL_GL_CreateContext() failing with EGL_BAD_MATCH. The exact cause of this is unknown, but it seems to be a combination of: - SDL_GL_SetAttribute() is supposed to be called _before_ the window is created, and we're calling it afterward. - Creating several test windows during the enumeration process, mixing and matching between OpenGL and OpenGL ES profiles. The "most correct" solution is probably to delay creating the game window until the backend creation process, rather than before the enumeration occurs. But that's a real refactor, which could cause other issues. Instead, set the 24-bit bit depth (which we've hardcoded anyway) before creating the window, and use SDL_GL_ResetAttributes() when creating backends. This seems to work here in all of the cases I was able to try (modulo the GLEW dependency, which is removed in the next patch). * miniwin: Remove GLEW dependency for OpenGL 1.1 GLEW normally backs directly onto glXGetProcAddress on Linux, which is broken on non-GLX setups, such as Wayland (but also X11 with EGL, and presumably KMSDRM). Replace it with manual calls to SDL_GL_GetProcAddress() for the VBO path. Note, however, that SDL_opengl.h includes "windows.h", so conflicts with the miniwin implementation, which breaks builds on windows. In order to work around this, we do what the Direct3D9 implementation does and push all of the OpenGL calls to a separate file, actual.cpp. Going forward, it may make sense to load _all_ OpenGL entry points via SDL, which would allow us to avoid linking directly with libGL/libOpenGL, and therefore eliminate the separate build dependency altogether, as well as allowing more runtime configurability as to the OpenGL library to load. (But that's definitely a bit uglier, and also useful very rarely.) --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- ISLE/isleapp.cpp | 1 + miniwin/CMakeLists.txt | 14 +- miniwin/src/d3drm/backends/opengl1/actual.cpp | 354 ++++++++++++++++++ miniwin/src/d3drm/backends/opengl1/actual.h | 88 +++++ .../src/d3drm/backends/opengl1/renderer.cpp | 298 ++------------- .../src/d3drm/backends/opengles2/renderer.cpp | 8 +- miniwin/src/internal/d3drmrenderer_opengl1.h | 32 +- 9 files changed, 494 insertions(+), 305 deletions(-) create mode 100644 miniwin/src/d3drm/backends/opengl1/actual.cpp create mode 100644 miniwin/src/d3drm/backends/opengl1/actual.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d688877..64c447e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: sudo apt-get update sudo apt-get install -y \ libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \ - libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev libglew-dev qt6-base-dev \ + libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev qt6-base-dev \ libasound2-dev - name: Install macOS dependencies (brew) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0e4c6cc9..ef8fdae2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: sudo apt-get update sudo apt-get install -y \ libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \ - libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev libglew-dev qt6-base-dev \ + libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev qt6-base-dev \ libasound2-dev - name: Install macOS dependencies (brew) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 4ee1980f..e700d5b2 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -657,6 +657,7 @@ MxResult IsleApp::SetupWindow() #ifdef MINIWIN SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); #endif window = SDL_CreateWindowWithProperties(props); diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index a1939441..508e685d 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -32,14 +32,16 @@ target_compile_definitions(miniwin PRIVATE ) find_package(OpenGL) -find_package(GLEW) -if(OpenGL_FOUND AND GLEW_FOUND) - message(STATUS "Found OpenGL and GLEW: enabling OpenGL 1.x renderer") - target_sources(miniwin PRIVATE src/d3drm/backends/opengl1/renderer.cpp) +if(OpenGL_FOUND) + message(STATUS "Found OpenGL: enabling OpenGL 1.x renderer") + target_sources(miniwin PRIVATE + src/d3drm/backends/opengl1/actual.cpp + src/d3drm/backends/opengl1/renderer.cpp + ) target_compile_definitions(miniwin PRIVATE USE_OPENGL1) - target_link_libraries(miniwin PRIVATE OpenGL::GL GLEW::GLEW) + target_link_libraries(miniwin PRIVATE OpenGL::GL) else() - message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL and GLEW") + message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL") endif() find_library(OPENGL_ES2_LIBRARY NAMES GLESv2) diff --git a/miniwin/src/d3drm/backends/opengl1/actual.cpp b/miniwin/src/d3drm/backends/opengl1/actual.cpp new file mode 100644 index 00000000..e1c56636 --- /dev/null +++ b/miniwin/src/d3drm/backends/opengl1/actual.cpp @@ -0,0 +1,354 @@ +// This file cannot include any minwin headers. + +#include "actual.h" + +#include "structs.h" + +#include +#include +#include +#include +#include + +// GL extension API functions. +bool g_useVBOs; +PFNGLGENBUFFERSPROC mwglGenBuffers; +PFNGLBINDBUFFERPROC mwglBindBuffer; +PFNGLBUFFERDATAPROC mwglBufferData; +PFNGLDELETEBUFFERSPROC mwglDeleteBuffers; + +void GL11_InitState() +{ + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CW); +} + +void GL11_LoadExtensions() +{ + g_useVBOs = SDL_GL_ExtensionSupported("GL_ARB_vertex_buffer_object"); + + if (g_useVBOs) { + // Load the required GL function pointers. + mwglGenBuffers = (PFNGLGENBUFFERSPROC) SDL_GL_GetProcAddress("glGenBuffersARB"); + mwglBindBuffer = (PFNGLBINDBUFFERPROC) SDL_GL_GetProcAddress("glBindBufferARB"); + mwglBufferData = (PFNGLBUFFERDATAPROC) SDL_GL_GetProcAddress("glBufferDataARB"); + mwglDeleteBuffers = (PFNGLDELETEBUFFERSPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB"); + } +} + +void GL11_DestroyTexture(GLuint texId) +{ + glDeleteTextures(1, &texId); +} + +GLuint GL11_UploadTextureData(void* pixels, int width, int height) +{ + GLuint texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + return texId; +} + +void GL11_UploadMesh(GLMeshCacheEntry& cache, bool hasTexture) +{ + if (g_useVBOs) { + mwglGenBuffers(1, &cache.vboPositions); + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboPositions); + mwglBufferData( + GL_ARRAY_BUFFER_ARB, + cache.positions.size() * sizeof(GL11_BridgeVector), + cache.positions.data(), + GL_STATIC_DRAW_ARB + ); + + mwglGenBuffers(1, &cache.vboNormals); + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboNormals); + mwglBufferData( + GL_ARRAY_BUFFER_ARB, + cache.normals.size() * sizeof(GL11_BridgeVector), + cache.normals.data(), + GL_STATIC_DRAW_ARB + ); + + if (hasTexture) { + mwglGenBuffers(1, &cache.vboTexcoords); + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, cache.vboTexcoords); + mwglBufferData( + GL_ARRAY_BUFFER_ARB, + cache.texcoords.size() * sizeof(GL11_BridgeTexCoord), + cache.texcoords.data(), + GL_STATIC_DRAW_ARB + ); + } + + mwglGenBuffers(1, &cache.ibo); + mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, cache.ibo); + mwglBufferData( + GL_ELEMENT_ARRAY_BUFFER_ARB, + cache.indices.size() * sizeof(cache.indices[0]), + cache.indices.data(), + GL_STATIC_DRAW_ARB + ); + } +} + +void GL11_DestroyMesh(GLMeshCacheEntry& cache) +{ + if (g_useVBOs) { + mwglDeleteBuffers(1, &cache.vboPositions); + mwglDeleteBuffers(1, &cache.vboNormals); + mwglDeleteBuffers(1, &cache.vboTexcoords); + mwglDeleteBuffers(1, &cache.ibo); + } +} + +void GL11_BeginFrame(const Matrix4x4* projection) +{ + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glEnable(GL_LIGHTING); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); + + // Disable all lights and reset global ambient + for (int i = 0; i < 8; ++i) { + glDisable(GL_LIGHT0 + i); + } + const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient); + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); + + // Projection and view + glMatrixMode(GL_PROJECTION); + glLoadMatrixf((const GLfloat*) projection); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void GL11_UploadLight(int lightIdx, GL11_BridgeSceneLight* l) +{ + // Setup light + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + GLenum lightId = GL_LIGHT0 + lightIdx++; + const FColor& c = l->color; + GLfloat col[4] = {c.r, c.g, c.b, c.a}; + const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; + + if (l->positional == 0.f && l->directional == 0.f) { + // Ambient light only + glLightfv(lightId, GL_AMBIENT, col); + const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; + glLightfv(lightId, GL_DIFFUSE, black); + glLightfv(lightId, GL_SPECULAR, black); + const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f}; + glLightfv(lightId, GL_POSITION, dummyPos); + } + else { + glLightfv(lightId, GL_AMBIENT, zeroAmbient); + glLightfv(lightId, GL_DIFFUSE, col); + if (l->directional == 1.0f) { + glLightfv(lightId, GL_SPECULAR, col); + } + else { + const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; + glLightfv(lightId, GL_SPECULAR, black); + } + + GLfloat pos[4]; + if (l->directional == 1.f) { + pos[0] = -l->direction.x; + pos[1] = -l->direction.y; + pos[2] = -l->direction.z; + pos[3] = 0.f; + } + else { + pos[0] = l->position.x; + pos[1] = l->position.y; + pos[2] = l->position.z; + pos[3] = 1.f; + } + glLightfv(lightId, GL_POSITION, pos); + } + glEnable(lightId); + + glPopMatrix(); +} + +void GL11_EnableTransparency() +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(GL_FALSE); +} + +#define NO_TEXTURE_ID 0xffffffff + +void GL11_SubmitDraw( + GLMeshCacheEntry& mesh, + const Matrix4x4& modelViewMatrix, + const Appearance& appearance, + GLuint texId +) +{ + glLoadMatrixf(&modelViewMatrix[0][0]); + glEnable(GL_NORMALIZE); + + glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a); + + if (appearance.shininess != 0.0f) { + GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f}; + glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec); + glMaterialf(GL_FRONT, GL_SHININESS, appearance.shininess); + } + else { + GLfloat noSpec[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec); + glMaterialf(GL_FRONT, GL_SHININESS, 0.0f); + } + + if (mesh.flat) { + glShadeModel(GL_FLAT); + } + else { + glShadeModel(GL_SMOOTH); + } + + // Bind texture if present + if (appearance.textureId != NO_TEXTURE_ID) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texId); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + else { + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + if (g_useVBOs) { + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboPositions); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboNormals); + glNormalPointer(GL_FLOAT, 0, nullptr); + + if (appearance.textureId != NO_TEXTURE_ID) { + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, mesh.vboTexcoords); + glTexCoordPointer(2, GL_FLOAT, 0, nullptr); + } + + mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, mesh.ibo); + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); + + mwglBindBuffer(GL_ARRAY_BUFFER_ARB, 0); + mwglBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + } + else { + glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data()); + glNormalPointer(GL_FLOAT, 0, mesh.normals.data()); + if (appearance.textureId != NO_TEXTURE_ID) { + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data()); + } + + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, mesh.indices.data()); + } + + glPopMatrix(); +} + +void GL11_Resize(int width, int height) +{ + glViewport(0, 0, width, height); +} + +void GL11_Clear(float r, float g, float b) +{ + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glClearColor(r, g, b, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void GL11_Draw2DImage( + GLuint texId, + const SDL_Rect& srcRect, + const SDL_Rect& dstRect, + float left, + float right, + float bottom, + float top +) +{ + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glOrtho(left, right, bottom, top, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texId); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + GLint boundTexture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); + + GLfloat texW, texH; + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texW); + glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texH); + + float u1 = srcRect.x / texW; + float v1 = srcRect.y / texH; + float u2 = (srcRect.x + srcRect.w) / texW; + float v2 = (srcRect.y + srcRect.h) / texH; + + float x1 = (float) dstRect.x; + float y1 = (float) dstRect.y; + float x2 = x1 + dstRect.w; + float y2 = y1 + dstRect.h; + + glBegin(GL_QUADS); + glTexCoord2f(u1, v1); + glVertex2f(x1, y1); + glTexCoord2f(u2, v1); + glVertex2f(x2, y1); + glTexCoord2f(u2, v2); + glVertex2f(x2, y2); + glTexCoord2f(u1, v2); + glVertex2f(x1, y2); + glEnd(); + + // Restore state + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); +} + +void GL11_Download(SDL_Surface* target) +{ + glFinish(); + glReadPixels(0, 0, target->w, target->h, GL_RGBA, GL_UNSIGNED_BYTE, target->pixels); +} diff --git a/miniwin/src/d3drm/backends/opengl1/actual.h b/miniwin/src/d3drm/backends/opengl1/actual.h new file mode 100644 index 00000000..8a4fb8e8 --- /dev/null +++ b/miniwin/src/d3drm/backends/opengl1/actual.h @@ -0,0 +1,88 @@ +#pragma once + +#include "structs.h" + +#include +#include +#include + +// We don't want to transitively include windows.h, but we need GLuint +typedef unsigned int GLuint; +struct IDirect3DRMTexture; +struct MeshGroup; + +typedef float Matrix4x4[4][4]; + +struct GL11_BridgeVector { + float x, y, z; +}; + +struct GL11_BridgeTexCoord { + float u, v; +}; + +struct GL11_BridgeSceneLight { + FColor color; + GL11_BridgeVector position; + float positional; + GL11_BridgeVector direction; + float directional; +}; + +struct GL11_BridgeSceneVertex { + GL11_BridgeVector position; + GL11_BridgeVector normal; + float tu, tv; +}; + +struct GLTextureCacheEntry { + IDirect3DRMTexture* texture; + Uint32 version; + GLuint glTextureId; +}; + +struct GLMeshCacheEntry { + const MeshGroup* meshGroup; + int version; + bool flat; + + // non-VBO cache + std::vector positions; + std::vector normals; + std::vector texcoords; + std::vector indices; + + // VBO cache + GLuint vboPositions; + GLuint vboNormals; + GLuint vboTexcoords; + GLuint ibo; +}; + +void GL11_InitState(); +void GL11_LoadExtensions(); +void GL11_DestroyTexture(GLuint texId); +GLuint GL11_UploadTextureData(void* pixels, int width, int height); +void GL11_UploadMesh(GLMeshCacheEntry& cache, bool hasTexture); +void GL11_DestroyMesh(GLMeshCacheEntry& cache); +void GL11_BeginFrame(const Matrix4x4* projection); +void GL11_UploadLight(int lightIdx, GL11_BridgeSceneLight* l); +void GL11_EnableTransparency(); +void GL11_SubmitDraw( + GLMeshCacheEntry& mesh, + const Matrix4x4& modelViewMatrix, + const Appearance& appearance, + GLuint texId +); +void GL11_Resize(int width, int height); +void GL11_Clear(float r, float g, float b); +void GL11_Draw2DImage( + GLuint texId, + const SDL_Rect& srcRect, + const SDL_Rect& dstRect, + float left, + float right, + float bottom, + float top +); +void GL11_Download(SDL_Surface* target); diff --git a/miniwin/src/d3drm/backends/opengl1/renderer.cpp b/miniwin/src/d3drm/backends/opengl1/renderer.cpp index cc415de8..a0b84a80 100644 --- a/miniwin/src/d3drm/backends/opengl1/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengl1/renderer.cpp @@ -1,5 +1,4 @@ -#include -// must come after GLEW +#include "actual.h" #include "d3drmrenderer_opengl1.h" #include "ddraw_impl.h" #include "ddsurface_impl.h" @@ -11,8 +10,20 @@ #include #include +static_assert(sizeof(Matrix4x4) == sizeof(D3DRMMATRIX4D), "Matrix4x4 is wrong size"); +static_assert(sizeof(GL11_BridgeVector) == sizeof(D3DVECTOR), "GL11_BridgeVector is wrong size"); +static_assert(sizeof(GL11_BridgeTexCoord) == sizeof(TexCoord), "GL11_BridgeTexCoord is wrong size"); +static_assert(sizeof(GL11_BridgeSceneLight) == sizeof(SceneLight), "GL11_BridgeSceneLight is wrong size"); +static_assert(sizeof(GL11_BridgeSceneVertex) == sizeof(D3DRMVERTEX), "GL11_BridgeSceneVertex is wrong size"); + Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) { + // We have to reset the attributes here after having enumerated the + // OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE + // call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH. + SDL_GL_ResetAttributes(); + // But ResetAttributes resets it to 16. + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); @@ -28,8 +39,6 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) testWindow = true; } - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GLContext context = SDL_GL_CreateContext(window); if (!context) { SDL_Log("SDL_GL_CreateContext: %s", SDL_GetError()); @@ -47,18 +56,7 @@ Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) return nullptr; } - GLenum err = glewInit(); - if (err != GLEW_OK) { - SDL_Log("glewInit: %s", glewGetErrorString(err)); - if (testWindow) { - SDL_DestroyWindow(window); - } - return nullptr; - } - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CW); + GL11_InitState(); if (testWindow) { SDL_DestroyWindow(window); @@ -74,7 +72,7 @@ OpenGL1Renderer::OpenGL1Renderer(DWORD width, DWORD height, SDL_GLContext contex m_virtualWidth = width; m_virtualHeight = height; m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); - m_useVBOs = GLEW_ARB_vertex_buffer_object; + GL11_LoadExtensions(); } OpenGL1Renderer::~OpenGL1Renderer() @@ -114,7 +112,7 @@ void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* t auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_textures[ctx->textureId]; if (cache.glTextureId != 0) { - glDeleteTextures(1, &cache.glTextureId); + GL11_DestroyTexture(cache.glTextureId); cache.glTextureId = 0; cache.texture = nullptr; } @@ -133,15 +131,13 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) auto& tex = m_textures[i]; if (tex.texture == texture) { if (tex.version != texture->m_version) { - glDeleteTextures(1, &tex.glTextureId); - glGenTextures(1, &tex.glTextureId); - glBindTexture(GL_TEXTURE_2D, tex.glTextureId); + GL11_DestroyTexture(tex.glTextureId); SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); if (!surf) { return NO_TEXTURE_ID; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + tex.glTextureId = GL11_UploadTextureData(surf->pixels, surf->w, surf->h); SDL_DestroySurface(surf); tex.version = texture->m_version; @@ -151,14 +147,12 @@ Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) } GLuint texId; - glGenTextures(1, &texId); - glBindTexture(GL_TEXTURE_2D, texId); SDL_Surface* surf = SDL_ConvertSurface(surface->m_surface, SDL_PIXELFORMAT_RGBA32); if (!surf) { return NO_TEXTURE_ID; } - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf->pixels); + texId = GL11_UploadTextureData(surf->pixels, surf->w, surf->h); SDL_DestroySurface(surf); for (Uint32 i = 0; i < m_textures.size(); ++i) { @@ -206,52 +200,19 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup, bool useVBOs) if (meshGroup.texture) { cache.texcoords.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.texcoords.begin(), [](const D3DRMVERTEX& v) { - return v.texCoord; + return GL11_BridgeTexCoord{v.texCoord.u, v.texCoord.v}; }); } cache.positions.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.positions.begin(), [](const D3DRMVERTEX& v) { - return v.position; + return GL11_BridgeVector{v.position.x, v.position.y, v.position.z}; }); cache.normals.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.normals.begin(), [](const D3DRMVERTEX& v) { - return v.normal; + return GL11_BridgeVector{v.normal.x, v.normal.y, v.normal.z}; }); - if (useVBOs) { - glGenBuffers(1, &cache.vboPositions); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); - glBufferData( - GL_ARRAY_BUFFER, - cache.positions.size() * sizeof(D3DVECTOR), - cache.positions.data(), - GL_STATIC_DRAW - ); - - glGenBuffers(1, &cache.vboNormals); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); - glBufferData(GL_ARRAY_BUFFER, cache.normals.size() * sizeof(D3DVECTOR), cache.normals.data(), GL_STATIC_DRAW); - - if (meshGroup.texture) { - glGenBuffers(1, &cache.vboTexcoords); - glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); - glBufferData( - GL_ARRAY_BUFFER, - cache.texcoords.size() * sizeof(TexCoord), - cache.texcoords.data(), - GL_STATIC_DRAW - ); - } - - glGenBuffers(1, &cache.ibo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo); - glBufferData( - GL_ELEMENT_ARRAY_BUFFER, - cache.indices.size() * sizeof(cache.indices[0]), - cache.indices.data(), - GL_STATIC_DRAW - ); - } + GL11_UploadMesh(cache, meshGroup.texture != nullptr); return cache; } @@ -269,12 +230,7 @@ void OpenGL1Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_meshs[ctx->id]; cache.meshGroup = nullptr; - if (ctx->renderer->m_useVBOs) { - glDeleteBuffers(1, &cache.vboPositions); - glDeleteBuffers(1, &cache.vboNormals); - glDeleteBuffers(1, &cache.vboTexcoords); - glDeleteBuffers(1, &cache.ibo); - } + GL11_DestroyMesh(cache); delete ctx; }, ctx @@ -329,90 +285,23 @@ const char* OpenGL1Renderer::GetName() HRESULT OpenGL1Renderer::BeginFrame() { - m_dirty = true; - - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glEnable(GL_LIGHTING); - glEnable(GL_COLOR_MATERIAL); - glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); - - // Disable all lights and reset global ambient - for (int i = 0; i < 8; ++i) { - glDisable(GL_LIGHT0 + i); - } - const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f}; - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient); - glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); - - // Setup lights - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); + GL11_BeginFrame((Matrix4x4*) &m_projection[0][0]); int lightIdx = 0; for (const auto& l : m_lights) { if (lightIdx > 7) { break; } - GLenum lightId = GL_LIGHT0 + lightIdx++; - const FColor& c = l.color; - GLfloat col[4] = {c.r, c.g, c.b, c.a}; + GL11_UploadLight(lightIdx, (GL11_BridgeSceneLight*) &l); - if (l.positional == 0.f && l.directional == 0.f) { - // Ambient light only - glLightfv(lightId, GL_AMBIENT, col); - const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; - glLightfv(lightId, GL_DIFFUSE, black); - glLightfv(lightId, GL_SPECULAR, black); - const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f}; - glLightfv(lightId, GL_POSITION, dummyPos); - } - else { - glLightfv(lightId, GL_AMBIENT, zeroAmbient); - glLightfv(lightId, GL_DIFFUSE, col); - if (l.directional == 1.0f) { - glLightfv(lightId, GL_SPECULAR, col); - } - else { - const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f}; - glLightfv(lightId, GL_SPECULAR, black); - } - - GLfloat pos[4]; - if (l.directional == 1.f) { - pos[0] = -l.direction.x; - pos[1] = -l.direction.y; - pos[2] = -l.direction.z; - pos[3] = 0.f; - } - else { - pos[0] = l.position.x; - pos[1] = l.position.y; - pos[2] = l.position.z; - pos[3] = 1.f; - } - glLightfv(lightId, GL_POSITION, pos); - } - glEnable(lightId); + lightIdx++; } - glPopMatrix(); - - // Projection and view - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(&m_projection[0][0]); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - return DD_OK; } void OpenGL1Renderer::EnableTransparency() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(GL_FALSE); + GL11_EnableTransparency(); } void OpenGL1Renderer::SubmitDraw( @@ -426,75 +315,14 @@ void OpenGL1Renderer::SubmitDraw( { auto& mesh = m_meshs[meshId]; - glLoadMatrixf(&modelViewMatrix[0][0]); - glEnable(GL_NORMALIZE); - - glColor4ub(appearance.color.r, appearance.color.g, appearance.color.b, appearance.color.a); - - if (appearance.shininess != 0.0f) { - GLfloat whiteSpec[] = {1.f, 1.f, 1.f, 1.f}; - glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec); - glMaterialf(GL_FRONT, GL_SHININESS, appearance.shininess); - } - else { - GLfloat noSpec[] = {0.0f, 0.0f, 0.0f, 0.0f}; - glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec); - glMaterialf(GL_FRONT, GL_SHININESS, 0.0f); - } - - if (mesh.flat) { - glShadeModel(GL_FLAT); - } - else { - glShadeModel(GL_SMOOTH); - } - // Bind texture if present if (appearance.textureId != NO_TEXTURE_ID) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); auto& tex = m_textures[appearance.textureId]; - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, tex.glTextureId); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); + GL11_SubmitDraw(mesh, modelViewMatrix, appearance, tex.glTextureId); } else { - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + GL11_SubmitDraw(mesh, modelViewMatrix, appearance, 0); } - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - if (m_useVBOs) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); - glVertexPointer(3, GL_FLOAT, 0, nullptr); - - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals); - glNormalPointer(GL_FLOAT, 0, nullptr); - - if (appearance.textureId != NO_TEXTURE_ID) { - glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords); - glTexCoordPointer(2, GL_FLOAT, 0, nullptr); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo); - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - else { - glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data()); - glNormalPointer(GL_FLOAT, 0, mesh.normals.data()); - if (appearance.textureId != NO_TEXTURE_ID) { - glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data()); - } - - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_SHORT, mesh.indices.data()); - } - - glPopMatrix(); } HRESULT OpenGL1Renderer::FinalizeFrame() @@ -509,16 +337,13 @@ void OpenGL1Renderer::Resize(int width, int height, const ViewportTransform& vie m_viewportTransform = viewportTransform; SDL_DestroySurface(m_renderedImage); m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); - glViewport(0, 0, m_width, m_height); + GL11_Resize(width, height); } void OpenGL1Renderer::Clear(float r, float g, float b) { m_dirty = true; - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glClearColor(r, g, b, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GL11_Clear(r, g, b); } void OpenGL1Renderer::Flip() @@ -532,73 +357,18 @@ void OpenGL1Renderer::Flip() void OpenGL1Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) { m_dirty = true; - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); float left = -m_viewportTransform.offsetX / m_viewportTransform.scale; float right = (m_width - m_viewportTransform.offsetX) / m_viewportTransform.scale; float top = -m_viewportTransform.offsetY / m_viewportTransform.scale; float bottom = (m_height - m_viewportTransform.offsetY) / m_viewportTransform.scale; - glOrtho(left, right, bottom, top, -1, 1); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glDisable(GL_LIGHTING); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, m_textures[textureId].glTextureId); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - GLint boundTexture = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); - - GLfloat texW, texH; - glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texW); - glGetTexLevelParameterfv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texH); - - float u1 = srcRect.x / texW; - float v1 = srcRect.y / texH; - float u2 = (srcRect.x + srcRect.w) / texW; - float v2 = (srcRect.y + srcRect.h) / texH; - - float x1 = (float) dstRect.x; - float y1 = (float) dstRect.y; - float x2 = x1 + dstRect.w; - float y2 = y1 + dstRect.h; - - glBegin(GL_QUADS); - glTexCoord2f(u1, v1); - glVertex2f(x1, y1); - glTexCoord2f(u2, v1); - glVertex2f(x2, y1); - glTexCoord2f(u2, v2); - glVertex2f(x2, y2); - glTexCoord2f(u1, v2); - glVertex2f(x1, y2); - glEnd(); - - // Restore state - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); + GL11_Draw2DImage(m_textures[textureId].glTextureId, srcRect, dstRect, left, right, bottom, top); } void OpenGL1Renderer::Download(SDL_Surface* target) { - glFinish(); - glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); + GL11_Download(m_renderedImage); SDL_Rect srcRect = { static_cast(m_viewportTransform.offsetX), diff --git a/miniwin/src/d3drm/backends/opengles2/renderer.cpp b/miniwin/src/d3drm/backends/opengles2/renderer.cpp index 3dbabda2..9305510a 100644 --- a/miniwin/src/d3drm/backends/opengles2/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengles2/renderer.cpp @@ -30,6 +30,12 @@ struct SceneLightGLES2 { Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) { + // We have to reset the attributes here after having enumerated the + // OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE + // call below when on an EGL-based backend, and crashes with EGL_BAD_MATCH. + SDL_GL_ResetAttributes(); + // But ResetAttributes resets it to 16. + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); @@ -41,8 +47,6 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) testWindow = true; } - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GLContext context = SDL_GL_CreateContext(window); if (!context) { if (testWindow) { diff --git a/miniwin/src/internal/d3drmrenderer_opengl1.h b/miniwin/src/internal/d3drmrenderer_opengl1.h index 71c0ea82..d80621a5 100644 --- a/miniwin/src/internal/d3drmrenderer_opengl1.h +++ b/miniwin/src/internal/d3drmrenderer_opengl1.h @@ -1,44 +1,14 @@ #pragma once - +#include "../d3drm/backends/opengl1/actual.h" #include "d3drmrenderer.h" #include "d3drmtexture_impl.h" #include "ddraw_impl.h" -#ifdef __APPLE__ -#include -#else -#include -#endif - #include #include DEFINE_GUID(OpenGL1_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03); -struct GLTextureCacheEntry { - IDirect3DRMTexture* texture; - Uint32 version; - GLuint glTextureId; -}; - -struct GLMeshCacheEntry { - const MeshGroup* meshGroup; - int version; - bool flat; - - // non-VBO cache - std::vector positions; - std::vector normals; - std::vector texcoords; - std::vector indices; - - // VBO cache - GLuint vboPositions; - GLuint vboNormals; - GLuint vboTexcoords; - GLuint ibo; -}; - class OpenGL1Renderer : public Direct3DRMRenderer { public: static Direct3DRMRenderer* Create(DWORD width, DWORD height); From aa825aeecf44e61a02a240d72986bf49e26315d3 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 29 Jun 2025 08:55:34 -0700 Subject: [PATCH 21/22] Add macro for switchable building index (#1596) --- LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp index 1e4a8b82..4ea96d2b 100644 --- a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp @@ -227,6 +227,8 @@ LegoBuildingInfo g_buildingInfo[16]; // GLOBAL: LEGO1 0x100f3748 MxS32 LegoBuildingManager::g_maxMove[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0}; +#define HAUS1_INDEX 12 + // FUNCTION: LEGO1 0x1002f8b0 void LegoBuildingManager::configureLegoBuildingManager(MxS32 p_buildingManagerConfig) { @@ -461,7 +463,7 @@ MxBool LegoBuildingManager::SwitchVariant(LegoEntity* p_entity) roi->SetVisibility(FALSE); info->m_variant = g_buildingInfoVariants[m_nextVariant]; - CreateBuilding(12, CurrentWorld()); + CreateBuilding(HAUS1_INDEX, CurrentWorld()); if (info->m_entity != NULL) { info->m_entity->GetROI()->SetVisibility(TRUE); From 77bbbfe2ca29c95bfc6f64cc9231b921c446c410 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 29 Jun 2025 09:15:42 -0700 Subject: [PATCH 22/22] (Pepper) Fix building variant switch bug (#451) * (Pepper) Fix building variant switch bug * Add comment --- LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp index 4ea96d2b..8dae5d61 100644 --- a/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp +++ b/LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp @@ -394,6 +394,9 @@ MxResult LegoBuildingManager::Read(LegoStorage* p_storage) m_nextVariant = 0; } + // Bugfix: allow Pepper to change variant building after save game load + g_buildingInfo[HAUS1_INDEX].m_variant = g_buildingInfoVariants[m_nextVariant]; + result = SUCCESS; done: