From f64af166c8cfe3ada0d2f3c0ec83bf06a5cc35fa Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Sat, 21 Jun 2025 07:52:24 +0200 Subject: [PATCH 01/26] Add BETA10 addresses for `LegoROI` and others (#1569) --------- Co-authored-by: jonschz --- LEGO1/lego/sources/anim/legoanim.cpp | 1 + LEGO1/lego/sources/roi/legoroi.cpp | 18 +++++++++++++- LEGO1/lego/sources/roi/legoroi.h | 11 +++++++-- LEGO1/realtime/orientableroi.cpp | 9 +++++++ LEGO1/realtime/orientableroi.h | 2 ++ LEGO1/realtime/roi.h | 20 ++++++++++++++++ LEGO1/viewmanager/viewroi.cpp | 35 ++++++++++++---------------- LEGO1/viewmanager/viewroi.h | 6 +++++ 8 files changed, 79 insertions(+), 23 deletions(-) diff --git a/LEGO1/lego/sources/anim/legoanim.cpp b/LEGO1/lego/sources/anim/legoanim.cpp index 3b8caafc..dc3c21da 100644 --- a/LEGO1/lego/sources/anim/legoanim.cpp +++ b/LEGO1/lego/sources/anim/legoanim.cpp @@ -725,6 +725,7 @@ void LegoAnimNodeData::SetName(LegoChar* p_name) } // FUNCTION: LEGO1 0x100a03c0 +// FUNCTION: BETA10 0x1017f254 LegoResult LegoAnimNodeData::CreateLocalTransform(LegoFloat p_time, Matrix4& p_matrix) { LegoU32 index; diff --git a/LEGO1/lego/sources/roi/legoroi.cpp b/LEGO1/lego/sources/roi/legoroi.cpp index dd72f7f0..97baaadf 100644 --- a/LEGO1/lego/sources/roi/legoroi.cpp +++ b/LEGO1/lego/sources/roi/legoroi.cpp @@ -58,8 +58,12 @@ ColorOverride g_colorOverride = NULL; TextureHandler g_textureHandler = NULL; // FUNCTION: LEGO1 0x100a81b0 -void LegoROI::FUN_100a81b0(const LegoChar* p_error, const LegoChar* p_name) +// FUNCTION: BETA10 0x101898c0 +// FUNCTION: ALPHA 0x100bb1c0 +void LegoROI::FUN_100a81b0(const LegoChar* p_error, ...) { + // Probably a printf-like debug function that was removed early. + // No known implementation in any of the binaries. } // FUNCTION: LEGO1 0x100a81c0 @@ -69,6 +73,7 @@ void LegoROI::configureLegoROI(int p_roiConfig) } // FUNCTION: LEGO1 0x100a81d0 +// FUNCTION: BETA10 0x101898e8 LegoROI::LegoROI(Tgl::Renderer* p_renderer) : ViewROI(p_renderer, NULL) { m_parentROI = NULL; @@ -77,6 +82,7 @@ LegoROI::LegoROI(Tgl::Renderer* p_renderer) : ViewROI(p_renderer, NULL) } // FUNCTION: LEGO1 0x100a82d0 +// FUNCTION: BETA10 0x10189994 LegoROI::LegoROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList) : ViewROI(p_renderer, p_lodList) { m_parentROI = NULL; @@ -85,6 +91,7 @@ LegoROI::LegoROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList) : ViewROI(p_ } // FUNCTION: LEGO1 0x100a83c0 +// FUNCTION: BETA10 0x10189a42 LegoROI::~LegoROI() { if (comp) { @@ -105,6 +112,7 @@ LegoROI::~LegoROI() } // FUNCTION: LEGO1 0x100a84a0 +// FUNCTION: BETA10 0x10189b99 LegoResult LegoROI::Read( OrientableROI* p_unk0xd4, Tgl::Renderer* p_renderer, @@ -337,6 +345,7 @@ LegoResult LegoROI::Read( } // FUNCTION: LEGO1 0x100a8cb0 +// FUNCTION: BETA10 0x1018a7e8 LegoResult LegoROI::CreateLocalTransform(LegoAnimNodeData* p_data, LegoTime p_time, Matrix4& p_matrix) { p_matrix.SetIdentity(); @@ -379,6 +388,7 @@ LegoROI* LegoROI::FindChildROI(const LegoChar* p_name, LegoROI* p_roi) } // FUNCTION: LEGO1 0x100a8da0 +// FUNCTION: BETA10 0x1018a9fb LegoResult LegoROI::ApplyAnimationTransformation( LegoTreeNode* p_node, const Matrix4& p_matrix, @@ -473,6 +483,7 @@ void LegoROI::FUN_100a8fd0(LegoTreeNode* p_node, Matrix4& p_matrix, LegoTime p_t } // FUNCTION: LEGO1 0x100a90f0 +// FUNCTION: BETA10 0x1018ada8 LegoResult LegoROI::SetFrame(LegoAnim* p_anim, LegoTime p_time) { LegoTreeNode* root = p_anim->GetRoot(); @@ -512,6 +523,7 @@ LegoResult LegoROI::SetLodColor(LegoFloat p_red, LegoFloat p_green, LegoFloat p_ } // FUNCTION: LEGO1 0x100a9210 +// FUNCTION: BETA10 0x1018af25 LegoResult LegoROI::SetTextureInfo(LegoTextureInfo* p_textureInfo) { LegoResult result = SUCCESS; @@ -735,6 +747,7 @@ LegoU32 LegoROI::FUN_100a9410( } // FUNCTION: LEGO1 0x100a9a50 +// FUNCTION: BETA10 0x1018bb6b TimeROI::TimeROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList, LegoTime p_time) : LegoROI(p_renderer, p_lodList) { m_time = p_time; @@ -760,6 +773,7 @@ void TimeROI::FUN_100a9b40(Matrix4& p_matrix, LegoTime p_time) } // FUNCTION: LEGO1 0x100a9bf0 +// FUNCTION: BETA10 0x1018bc93 LegoBool LegoROI::GetRGBAColor(const LegoChar* p_name, float& p_red, float& p_green, float& p_blue, float& p_alpha) { if (p_name == NULL) { @@ -850,12 +864,14 @@ void LegoROI::SetDisplayBB(int p_displayBB) } // FUNCTION: LEGO1 0x100aa340 +// FUNCTION: BETA10 0x1018cca0 float LegoROI::IntrinsicImportance() const { return .5; } // FUNCTION: LEGO1 0x100aa350 +// FUNCTION: BETA10 0x1018ccc0 void LegoROI::UpdateWorldBoundingVolumes() { CalcWorldBoundingVolumes(m_sphere, m_local2world, m_world_bounding_box, m_world_bounding_sphere); diff --git a/LEGO1/lego/sources/roi/legoroi.h b/LEGO1/lego/sources/roi/legoroi.h index 28119c72..216bb68d 100644 --- a/LEGO1/lego/sources/roi/legoroi.h +++ b/LEGO1/lego/sources/roi/legoroi.h @@ -17,6 +17,7 @@ class LegoTreeNode; struct LegoAnimActorEntry; // VTABLE: LEGO1 0x100dbe38 +// VTABLE: BETA10 0x101c3898 // SIZE 0x108 class LegoROI : public ViewROI { public: @@ -57,7 +58,7 @@ class LegoROI : public ViewROI { void SetDisplayBB(int p_displayBB); static LegoResult CreateLocalTransform(LegoAnimNodeData* p_data, LegoTime p_time, Matrix4& p_matrix); - static void FUN_100a81b0(const LegoChar* p_error, const LegoChar* p_name); + static void FUN_100a81b0(const LegoChar* p_error, ...); static void configureLegoROI(int p_roi); static void SetColorOverride(ColorOverride p_colorOverride); static LegoBool GetRGBAColor(const LegoChar* p_name, float& p_red, float& p_green, float& p_blue, float& p_alpha); @@ -86,6 +87,7 @@ class LegoROI : public ViewROI { void SetBoundingBox(const BoundingBox& p_box) { m_bounding_box = p_box; } // SYNTHETIC: LEGO1 0x100a82b0 + // SYNTHETIC: BETA10 0x1018c490 // LegoROI::`scalar deleting destructor' private: @@ -96,15 +98,20 @@ class LegoROI : public ViewROI { }; // VTABLE: LEGO1 0x100dbea8 +// VTABLE: BETA10 0x101c38d0 // SIZE 0x10c class TimeROI : public LegoROI { public: TimeROI(Tgl::Renderer* p_renderer, ViewLODList* p_lodList, LegoTime p_time); + void FUN_100a9b40(Matrix4& p_matrix, LegoTime p_time); + // SYNTHETIC: LEGO1 0x100a9ad0 + // SYNTHETIC: BETA10 0x1018c540 // TimeROI::`scalar deleting destructor' - void FUN_100a9b40(Matrix4& p_matrix, LegoTime p_time); + // SYNTHETIC: BETA10 0x1018c580 + // TimeROI::~TimeROI private: LegoTime m_time; // 0x108 diff --git a/LEGO1/realtime/orientableroi.cpp b/LEGO1/realtime/orientableroi.cpp index f479ca7d..92c9ac9e 100644 --- a/LEGO1/realtime/orientableroi.cpp +++ b/LEGO1/realtime/orientableroi.cpp @@ -108,6 +108,7 @@ void OrientableROI::SetLocal2World(const Matrix4& p_local2world) } // FUNCTION: LEGO1 0x100a5910 +// FUNCTION: BETA10 0x10167bac void OrientableROI::UpdateWorldData() { UpdateWorldBoundingVolumes(); @@ -115,6 +116,7 @@ void OrientableROI::UpdateWorldData() } // FUNCTION: LEGO1 0x100a5930 +// FUNCTION: BETA10 0x10167bd8 void OrientableROI::SetLocal2WorldWithWorldDataUpdate(const Matrix4& p_transform) { m_local2world = p_transform; @@ -123,6 +125,7 @@ void OrientableROI::SetLocal2WorldWithWorldDataUpdate(const Matrix4& p_transform } // FUNCTION: LEGO1 0x100a5960 +// FUNCTION: BETA10 0x10167c19 void OrientableROI::UpdateWorldDataWithTransform(const Matrix4& p_transform) { MxMatrix l_matrix(m_local2world); @@ -132,6 +135,7 @@ void OrientableROI::UpdateWorldDataWithTransform(const Matrix4& p_transform) } // FUNCTION: LEGO1 0x100a59b0 +// FUNCTION: BETA10 0x10167c6d void OrientableROI::UpdateWorldDataWithTransformAndChildren(const Matrix4& p_transform) { MxMatrix l_matrix(m_local2world); @@ -155,11 +159,13 @@ void OrientableROI::SetWorldVelocity(const Vector3& p_world_velocity) } // FUNCTION: LEGO1 0x100a5a50 +// FUNCTION: BETA10 0x10167d65 void OrientableROI::UpdateWorldVelocity() { } // FUNCTION: LEGO1 0x100a5a60 +// FUNCTION: BETA10 0x10167d7b void CalcWorldBoundingVolumes( const BoundingSphere& modelling_sphere, const Matrix4& local2world, @@ -186,18 +192,21 @@ void CalcWorldBoundingVolumes( } // FUNCTION: LEGO1 0x100a5d80 +// FUNCTION: BETA10 0x10168760 const float* OrientableROI::GetWorldVelocity() const { return m_world_velocity.GetData(); } // FUNCTION: LEGO1 0x100a5d90 +// FUNCTION: BETA10 0x10168790 const BoundingBox& OrientableROI::GetWorldBoundingBox() const { return m_world_bounding_box; } // FUNCTION: LEGO1 0x100a5da0 +// FUNCTION: BETA10 0x101687b0 const BoundingSphere& OrientableROI::GetWorldBoundingSphere() const { return m_world_bounding_sphere; diff --git a/LEGO1/realtime/orientableroi.h b/LEGO1/realtime/orientableroi.h index 031c5af7..c1354c50 100644 --- a/LEGO1/realtime/orientableroi.h +++ b/LEGO1/realtime/orientableroi.h @@ -23,6 +23,7 @@ class OrientableROI : public ROI { const BoundingSphere& GetWorldBoundingSphere() const override; // vtable+0x10 // FUNCTION: LEGO1 0x100a5db0 + // FUNCTION: BETA10 0x101687d0 virtual void WrappedUpdateWorldData() { UpdateWorldData(); } // vtable+0x14 virtual void UpdateWorldBoundingVolumes() = 0; // vtable+0x18 @@ -81,6 +82,7 @@ class OrientableROI : public ROI { // OrientableROI::`scalar deleting destructor' // SYNTHETIC: LEGO1 0x100aa2f0 +// SYNTHETIC: BETA10 0x10168600 // OrientableROI::~OrientableROI #endif // ORIENTABLEROI_H diff --git a/LEGO1/realtime/roi.h b/LEGO1/realtime/roi.h index 87cbe8f1..dd5b9993 100644 --- a/LEGO1/realtime/roi.h +++ b/LEGO1/realtime/roi.h @@ -15,9 +15,16 @@ // SIZE 0x28 class BoundingBox { public: + // The BETA10 matches may reference the wrong version + + // FUNCTION: BETA10 0x1004a7a0 const Vector3& Min() const { return min; } + Vector3& Min() { return min; } + + // FUNCTION: BETA10 0x1004a7c0 const Vector3& Max() const { return max; } + Vector3& Max() { return max; } private: @@ -31,14 +38,26 @@ class BoundingBox { // SIZE 0x18 class BoundingSphere { public: + // The BETA10 matches may reference the wrong version + + // FUNCTION: BETA10 0x1001fac0 const Vector3& Center() const { return center; } + + // FUNCTION: BETA10 0x100d55a0 Vector3& Center() { return center; } + + // FUNCTION: BETA10 0x1001fd30 const float& Radius() const { return radius; } + + // FUNCTION: BETA10 0x1001fae0 float& Radius() { return radius; } // SYNTHETIC: BETA10 0x1001fb90 // BoundingSphere::operator= + // SYNTHETIC: BETA10 0x1001fc50 + // BoundingSphere::BoundingSphere + private: Mx3DPointFloat center; // 0x00 float radius; // 0x14 @@ -136,6 +155,7 @@ class ROI { // list >::~list > // SYNTHETIC: LEGO1 0x100a5d50 +// SYNTHETIC: BETA10 0x101686a0 // ROI::~ROI #endif // ROI_H diff --git a/LEGO1/viewmanager/viewroi.cpp b/LEGO1/viewmanager/viewroi.cpp index 787e9078..05f636b6 100644 --- a/LEGO1/viewmanager/viewroi.cpp +++ b/LEGO1/viewmanager/viewroi.cpp @@ -16,65 +16,60 @@ float ViewROI::IntrinsicImportance() const } // for now // FUNCTION: LEGO1 0x100a9ec0 +// FUNCTION: BETA10 0x1018c740 Tgl::Group* ViewROI::GetGeometry() { return geometry; } // FUNCTION: LEGO1 0x100a9ed0 +// FUNCTION: BETA10 0x1018c760 const Tgl::Group* ViewROI::GetGeometry() const { return geometry; } // FUNCTION: LEGO1 0x100a9ee0 +// FUNCTION: BETA10 0x1018c780 void ViewROI::UpdateWorldDataWithTransformAndChildren(const Matrix4& parent2world) { OrientableROI::UpdateWorldDataWithTransformAndChildren(parent2world); + SetGeometryTransformation(); +} +// FUNCTION: BETA10 0x1018c7b0 +inline void ViewROI::SetGeometryTransformation() +{ if (geometry) { Tgl::FloatMatrix4 matrix; Matrix4 in(matrix); SETMAT4(in, m_local2world); - Tgl::Result result = geometry->SetTransformation(matrix); - // assert(Tgl::Succeeded(result)); + geometry->SetTransformation(matrix); } } // FUNCTION: LEGO1 0x100a9fc0 +// FUNCTION: BETA10 0x1018cad0 void ViewROI::UpdateWorldDataWithTransform(const Matrix4& p_transform) { OrientableROI::UpdateWorldDataWithTransform(p_transform); - if (geometry) { - Tgl::FloatMatrix4 matrix; - Matrix4 in(matrix); - SETMAT4(in, m_local2world); - geometry->SetTransformation(matrix); - } + SetGeometryTransformation(); } // FUNCTION: LEGO1 0x100aa0a0 +// FUNCTION: BETA10 0x1018cb00 void ViewROI::SetLocal2WorldWithWorldDataUpdate(const Matrix4& p_transform) { OrientableROI::SetLocal2WorldWithWorldDataUpdate(p_transform); - if (geometry) { - Tgl::FloatMatrix4 matrix; - Matrix4 in(matrix); - SETMAT4(in, m_local2world); - geometry->SetTransformation(matrix); - } + SetGeometryTransformation(); } // FUNCTION: LEGO1 0x100aa180 +// FUNCTION: BETA10 0x1018cb30 void ViewROI::UpdateWorldData() { OrientableROI::UpdateWorldData(); - if (geometry) { - Tgl::FloatMatrix4 matrix; - Matrix4 in(matrix); - SETMAT4(in, m_local2world); - geometry->SetTransformation(matrix); - } + SetGeometryTransformation(); } // FUNCTION: LEGO1 0x100aa500 diff --git a/LEGO1/viewmanager/viewroi.h b/LEGO1/viewmanager/viewroi.h index 63e82154..b7ba9564 100644 --- a/LEGO1/viewmanager/viewroi.h +++ b/LEGO1/viewmanager/viewroi.h @@ -13,6 +13,7 @@ */ // VTABLE: LEGO1 0x100dbe70 +// VTABLE: BETA10 0x101c3908 // SIZE 0xe4 class ViewROI : public OrientableROI { public: @@ -21,6 +22,7 @@ class ViewROI : public OrientableROI { c_lodLevelInvisible = -2, }; + // FUNCTION: BETA10 0x1018c5e0 ViewROI(Tgl::Renderer* pRenderer, ViewLODList* lodList) { SetLODList(lodList); @@ -29,6 +31,7 @@ class ViewROI : public OrientableROI { } // FUNCTION: LEGO1 0x100a9e20 + // FUNCTION: BETA10 0x1018c680 ~ViewROI() override { // SetLODList() will decrease refCount of LODList @@ -36,6 +39,7 @@ class ViewROI : public OrientableROI { delete geometry; } + // FUNCTION: BETA10 0x1007b540 void SetLODList(ViewLODList* lodList) { // ??? inherently type unsafe - kind of... because, now, ROI @@ -69,6 +73,8 @@ class ViewROI : public OrientableROI { protected: void UpdateWorldDataWithTransformAndChildren(const Matrix4& parent2world) override; // vtable+0x28 + void SetGeometryTransformation(); + Tgl::Group* geometry; // 0xdc int m_lodLevel; // 0xe0 }; From e3fc6fd13599082a8364d2e6c658e99813e41eba Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sun, 22 Jun 2025 00:32:30 +0200 Subject: [PATCH 02/26] Use enum from `LegoActor` to map between string and id (#1577) --- LEGO1/lego/legoomni/src/common/legovariables.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/LEGO1/lego/legoomni/src/common/legovariables.cpp b/LEGO1/lego/legoomni/src/common/legovariables.cpp index 68768e99..2c701074 100644 --- a/LEGO1/lego/legoomni/src/common/legovariables.cpp +++ b/LEGO1/lego/legoomni/src/common/legovariables.cpp @@ -1,6 +1,7 @@ #include "legovariables.h" #include "3dmanager/lego3dmanager.h" +#include "legoactor.h" #include "legogamestate.h" #include "legonavcontroller.h" #include "legovideomanager.h" @@ -157,18 +158,18 @@ void WhoAmIVariable::SetValue(const char* p_value) MxVariable::SetValue(p_value); if (!strcmpi(p_value, g_papa)) { - GameState()->SetActorId(3); + GameState()->SetActorId(LegoActor::c_papa); } else if (!strcmpi(p_value, g_mama)) { - GameState()->SetActorId(2); + GameState()->SetActorId(LegoActor::c_mama); } else if (!strcmpi(p_value, g_pepper)) { - GameState()->SetActorId(1); + GameState()->SetActorId(LegoActor::c_pepper); } else if (!strcmpi(p_value, g_nick)) { - GameState()->SetActorId(4); + GameState()->SetActorId(LegoActor::c_nick); } else if (!strcmpi(p_value, g_laura)) { - GameState()->SetActorId(5); + GameState()->SetActorId(LegoActor::c_laura); } } From 3678c97ec3b99a7be9d0efc4a13a15e05040a651 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 22 Jun 2025 03:29:37 +0200 Subject: [PATCH 03/26] Allow for building on slightly older SDL3 versions (#394) --- ISLE/isledebug.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ISLE/isledebug.cpp b/ISLE/isledebug.cpp index 79db85b9..d60307cc 100644 --- a/ISLE/isledebug.cpp +++ b/ISLE/isledebug.cpp @@ -183,7 +183,11 @@ void IsleDebug_Init() } g_videoPalette = SDL_CreateTexture(g_debugRenderer, SDL_PIXELFORMAT_RGBX32, SDL_TEXTUREACCESS_STREAMING, 16, 16); +#if SDL_VERSION_ATLEAST(3, 2, 0) SDL_SetTextureScaleMode(g_videoPalette, SDL_SCALEMODE_PIXELART); +#else + SDL_SetTextureScaleMode(g_videoPalette, SDL_SCALEMODE_NEAREST); +#endif if (!ImGui_ImplSDLRenderer3_Init(g_debugRenderer)) { g_debugEnabled = false; break; From 16db496832d8532fa7f5a44807fb3330cd18f2a4 Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Sun, 22 Jun 2025 08:22:25 +0200 Subject: [PATCH 04/26] Match `LegoGameState::History::WriteScoreHistory()`, clear unknowns (#1576) --------- Co-authored-by: jonschz --- LEGO1/lego/legoomni/include/legogamestate.h | 26 ++- .../legoomni/src/common/legogamestate.cpp | 181 +++++++++++------- 2 files changed, 134 insertions(+), 73 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legogamestate.h b/LEGO1/lego/legoomni/include/legogamestate.h index 1eec40a6..b81696fd 100644 --- a/LEGO1/lego/legoomni/include/legogamestate.h +++ b/LEGO1/lego/legoomni/include/legogamestate.h @@ -150,7 +150,18 @@ class LegoGameState { MxS16 m_totalScore; // 0x00 MxU8 m_scores[5][5]; // 0x02 Username m_name; // 0x1c - MxS16 m_unk0x2a; // 0x2a + MxS16 m_playerId; // 0x2a + + ScoreItem& operator=(const ScoreItem& p_other) + { + // MSVC auto-generates an operator=, but LegoGameState::WriteScoreHistory() has a much better match + // with a manual implementation. + m_totalScore = p_other.m_totalScore; + memcpy(m_scores, p_other.m_scores, sizeof(m_scores)); + m_name = p_other.m_name; + m_playerId = p_other.m_playerId; + return *this; + } }; // SIZE 0x372 @@ -158,7 +169,7 @@ class LegoGameState { History(); void WriteScoreHistory(); MxResult Serialize(LegoStorage* p_storage); - ScoreItem* FUN_1003cc90(Username* p_player, MxS16 p_unk0x24, MxS32& p_unk0x2c); + ScoreItem* FindPlayerInScoreHistory(Username* p_player, MxS16 p_unk0x24, MxS32& p_unk0x2c); // FUNCTION: BETA10 0x1002c2b0 MxS16 GetCount() { return m_count; } @@ -167,9 +178,12 @@ class LegoGameState { // FUNCTION: BETA10 0x1002c540 ScoreItem* GetScore(MxS32 p_index) { return p_index >= m_count ? NULL : &m_scores[p_index]; } - MxS16 m_count; // 0x00 - ScoreItem m_scores[20]; // 0x02 - MxS16 m_unk0x372; // 0x372 + MxS16 m_count; // 0x00 +#ifdef BETA10 + MxS16 m_indices[20]; // 0x02 +#endif + ScoreItem m_scores[20]; // 0x02 (0x22 for BETA10) + MxS16 m_nextPlayerId; // 0x372 (0x392 for BETA10) }; LegoGameState(); @@ -243,7 +257,7 @@ class LegoGameState { // TODO: Most likely getters/setters are not used according to BETA for the following members: public: - MxS16 m_unk0x24; // 0x24 + MxS16 m_currentPlayerId; // 0x24 MxS16 m_playerCount; // 0x26 Username m_players[9]; // 0x28 History m_history; // 0xa6 diff --git a/LEGO1/lego/legoomni/src/common/legogamestate.cpp b/LEGO1/lego/legoomni/src/common/legogamestate.cpp index 99648947..9c67340c 100644 --- a/LEGO1/lego/legoomni/src/common/legogamestate.cpp +++ b/LEGO1/lego/legoomni/src/common/legogamestate.cpp @@ -277,7 +277,7 @@ MxResult LegoGameState::Save(MxULong p_slot) } storage.WriteS32(0x1000c); - storage.WriteS16(m_unk0x24); + storage.WriteS16(m_currentPlayerId); storage.WriteU16(m_currentAct); storage.WriteU8(m_actorId); @@ -372,7 +372,7 @@ MxResult LegoGameState::Load(MxULong p_slot) goto done; } - storage.ReadS16(m_unk0x24); + storage.ReadS16(m_currentPlayerId); storage.ReadS16(actArea); SetCurrentAct((Act) actArea); @@ -615,8 +615,8 @@ MxResult LegoGameState::AddPlayer(Username& p_player) m_playerCount++; m_players[0].Set(p_player); - m_unk0x24 = m_history.m_unk0x372; - m_history.m_unk0x372 = m_unk0x24 + 1; + m_currentPlayerId = m_history.m_nextPlayerId; + m_history.m_nextPlayerId = m_currentPlayerId + 1; m_history.WriteScoreHistory(); SetCurrentAct(e_act1); @@ -1407,7 +1407,7 @@ MxResult LegoGameState::ScoreItem::Serialize(LegoStorage* p_storage) } m_name.Serialize(p_storage); - p_storage->ReadS16(m_unk0x2a); + p_storage->ReadS16(m_playerId); } else if (p_storage->IsWriteMode()) { p_storage->WriteS16(m_totalScore); @@ -1419,7 +1419,7 @@ MxResult LegoGameState::ScoreItem::Serialize(LegoStorage* p_storage) } m_name.Serialize(p_storage); - p_storage->WriteS16(m_unk0x2a); + p_storage->WriteS16(m_playerId); } return SUCCESS; @@ -1430,7 +1430,7 @@ MxResult LegoGameState::ScoreItem::Serialize(LegoStorage* p_storage) LegoGameState::History::History() { m_count = 0; - m_unk0x372 = 0; + m_nextPlayerId = 0; } // FUNCTION: LEGO1 0x1003c870 @@ -1441,83 +1441,130 @@ void LegoGameState::History::WriteScoreHistory() MxU8 scores[5][5]; InfocenterState* state = (InfocenterState*) GameState()->GetState("InfocenterState"); - if (state->m_letters[0]) { - JetskiRaceState* jetskiRaceState = (JetskiRaceState*) GameState()->GetState("JetskiRaceState"); - CarRaceState* carRaceState = (CarRaceState*) GameState()->GetState("CarRaceState"); - TowTrackMissionState* towTrackMissionState = - (TowTrackMissionState*) GameState()->GetState("TowTrackMissionState"); - PizzaMissionState* pizzaMissionState = (PizzaMissionState*) GameState()->GetState("PizzaMissionState"); - AmbulanceMissionState* ambulanceMissionState = - (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState"); - for (MxS32 actor = 1; actor <= 5; actor++) { - scores[0][actor - 1] = carRaceState ? carRaceState->GetState(actor)->GetHighScore() : 0; - totalScore += scores[0][actor - 1]; + if (!state->m_letters[0]) { + return; + } - scores[1][actor - 1] = jetskiRaceState ? jetskiRaceState->GetState(actor)->GetHighScore() : 0; - totalScore += scores[1][actor - 1]; + JetskiRaceState* jetskiRaceState = (JetskiRaceState*) GameState()->GetState("JetskiRaceState"); + CarRaceState* carRaceState = (CarRaceState*) GameState()->GetState("CarRaceState"); + TowTrackMissionState* towTrackMissionState = (TowTrackMissionState*) GameState()->GetState("TowTrackMissionState"); + PizzaMissionState* pizzaMissionState = (PizzaMissionState*) GameState()->GetState("PizzaMissionState"); + AmbulanceMissionState* ambulanceMissionState = + (AmbulanceMissionState*) GameState()->GetState("AmbulanceMissionState"); - scores[2][actor - 1] = pizzaMissionState ? pizzaMissionState->GetHighScore(actor) : 0; - totalScore += scores[2][actor - 1]; + for (MxS32 actor = 1; actor <= 5; actor++) { + scores[0][actor - 1] = carRaceState ? carRaceState->GetState(actor)->GetHighScore() : 0; + totalScore += scores[0][actor - 1]; - scores[3][actor - 1] = towTrackMissionState ? towTrackMissionState->GetHighScore(actor) : 0; - totalScore += scores[3][actor - 1]; +#ifdef BETA10 + // likely a bug in BETA10 + scores[1][actor - 1] = carRaceState ? carRaceState->GetState(actor)->GetHighScore() : 0; +#else + scores[1][actor - 1] = jetskiRaceState ? jetskiRaceState->GetState(actor)->GetHighScore() : 0; +#endif + totalScore += scores[1][actor - 1]; - scores[4][actor - 1] = ambulanceMissionState ? ambulanceMissionState->GetHighScore(actor) : 0; - totalScore += scores[4][actor - 1]; - } + scores[2][actor - 1] = pizzaMissionState ? pizzaMissionState->GetHighScore(actor) : 0; + totalScore += scores[2][actor - 1]; - MxS32 unk0x2c; - ScoreItem* p_scorehist = FUN_1003cc90(&GameState()->m_players[0], GameState()->m_unk0x24, unk0x2c); + scores[3][actor - 1] = towTrackMissionState ? towTrackMissionState->GetHighScore(actor) : 0; + totalScore += scores[3][actor - 1]; - if (p_scorehist != NULL) { - p_scorehist->m_totalScore = totalScore; - memcpy(p_scorehist->m_scores, scores, sizeof(p_scorehist->m_scores)); - } - else { - if (m_count < (MxS16) sizeOfArray(m_scores)) { - m_scores[m_count].m_totalScore = totalScore; - memcpy(m_scores[m_count].m_scores, scores, sizeof(m_scores[m_count].m_scores)); - m_scores[m_count].m_name = GameState()->m_players[0]; - m_scores[m_count].m_unk0x2a = GameState()->m_unk0x24; - m_count++; - } - else if (m_scores[19].m_totalScore <= totalScore) { - m_scores[19].m_totalScore = totalScore; - memcpy(m_scores[19].m_scores, scores, sizeof(m_scores[19].m_scores)); - m_scores[19].m_name = GameState()->m_players[0]; - m_scores[19].m_unk0x2a = GameState()->m_unk0x24; + scores[4][actor - 1] = ambulanceMissionState ? ambulanceMissionState->GetHighScore(actor) : 0; + totalScore += scores[4][actor - 1]; + } + + MxS32 playerScoreHistoryIndex; + ScoreItem* p_scorehist = + FindPlayerInScoreHistory(GameState()->m_players, GameState()->m_currentPlayerId, playerScoreHistoryIndex); + +#ifdef BETA10 + if (!p_scorehist) { + MxS32 playerScoreRank; + // LINE: BETA10 0x100870ee + for (playerScoreRank = 0; playerScoreRank < m_count; playerScoreRank++) { + if (totalScore > m_scores[m_indices[playerScoreRank]].m_totalScore) { + break; } } + // LINE: BETA10 0x1008713f + if (playerScoreRank < m_count) { + if (m_count < 20) { + playerScoreHistoryIndex = m_count++; + } + else { + playerScoreHistoryIndex = m_indices[19]; + } - MxU8 tmpScores[5][5]; - Username tmpPlayer; - MxS16 tmpUnk0x2a; + MxS32 max = m_count - 1; + for (MxS32 j = max; playerScoreRank < j; j--) { + m_indices[j - 1] = m_indices[j - 2]; + } - // TODO: Match bubble sort loops - for (MxS32 i = m_count - 1; i > 0; i--) { - for (MxS32 j = 1; j <= i; j++) { - if (m_scores[j - 1].m_totalScore < m_scores[j].m_totalScore) { - memcpy(tmpScores, m_scores[j - 1].m_scores, sizeof(tmpScores)); - tmpPlayer = m_scores[j - 1].m_name; - tmpUnk0x2a = m_scores[j - 1].m_unk0x2a; + m_indices[playerScoreRank] = playerScoreHistoryIndex; + p_scorehist = &m_scores[playerScoreHistoryIndex]; + } + else if (playerScoreRank < 20) { + m_indices[m_count] = m_count; + p_scorehist = &m_scores[m_count++]; + } + } + else if (p_scorehist->m_totalScore != totalScore) { + assert(totalScore > p_scorehist->m_totalScore); - memcpy(m_scores[j - 1].m_scores, m_scores[j].m_scores, sizeof(m_scores[j - 1].m_scores)); - m_scores[j - 1].m_name = m_scores[j].m_name; - m_scores[j - 1].m_unk0x2a = m_scores[j].m_unk0x2a; + for (MxS32 i = playerScoreHistoryIndex; i > 0 && m_indices[i - 1] < m_indices[i]; i--) { + MxU8 tmp = m_indices[i - 1]; + m_indices[i - 1] = m_indices[i]; + m_indices[i] = tmp; + } + } + if (p_scorehist) { + p_scorehist->m_totalScore = totalScore; + memcpy(p_scorehist->m_scores[0], scores[0], sizeof(scores)); + p_scorehist->m_name = GameState()->m_players[0]; + p_scorehist->m_playerId = GameState()->m_currentPlayerId; + } +#else + if (p_scorehist != NULL) { + p_scorehist->m_totalScore = totalScore; + memcpy(p_scorehist->m_scores, scores, sizeof(p_scorehist->m_scores)); + } + else { + if (m_count < (MxS16) sizeOfArray(m_scores)) { + assert(totalScore > p_scorehist->m_totalScore); - memcpy(m_scores[j].m_scores, tmpScores, sizeof(m_scores[j].m_scores)); - m_scores[j].m_name = tmpPlayer; - m_scores[j].m_unk0x2a = tmpUnk0x2a; - } + m_scores[m_count].m_totalScore = totalScore; + memcpy(m_scores[m_count].m_scores, scores, sizeof(m_scores[m_count].m_scores)); + m_scores[m_count].m_name = GameState()->m_players[0]; + m_scores[m_count].m_playerId = GameState()->m_currentPlayerId; + m_count++; + } + else if (m_scores[19].m_totalScore <= totalScore) { + m_scores[19].m_totalScore = totalScore; + memcpy(m_scores[19].m_scores, scores, sizeof(m_scores[19].m_scores)); + m_scores[19].m_name = GameState()->m_players[0]; + m_scores[19].m_playerId = GameState()->m_currentPlayerId; + } + } + + ScoreItem tmpItem; + + for (MxS32 i = m_count - 1; i >= 0; i--) { + for (MxS32 j = 1; j <= i; j++) { + if (m_scores[j].m_totalScore > m_scores[j - 1].m_totalScore) { + tmpItem = m_scores[j - 1]; + m_scores[j - 1] = m_scores[j]; + m_scores[j] = tmpItem; } } } +#endif } // FUNCTION: LEGO1 0x1003cc90 // FUNCTION: BETA10 0x1008732a -LegoGameState::ScoreItem* LegoGameState::History::FUN_1003cc90( +LegoGameState::ScoreItem* LegoGameState::History::FindPlayerInScoreHistory( LegoGameState::Username* p_player, MxS16 p_unk0x24, MxS32& p_unk0x2c @@ -1525,7 +1572,7 @@ LegoGameState::ScoreItem* LegoGameState::History::FUN_1003cc90( { MxS32 i = 0; for (; i < m_count; i++) { - if (!memcmp(p_player, &m_scores[i].m_name, sizeof(*p_player)) && m_scores[i].m_unk0x2a == p_unk0x24) { + if (!memcmp(p_player, &m_scores[i].m_name, sizeof(*p_player)) && m_scores[i].m_playerId == p_unk0x24) { break; } } @@ -1544,7 +1591,7 @@ LegoGameState::ScoreItem* LegoGameState::History::FUN_1003cc90( MxResult LegoGameState::History::Serialize(LegoStorage* p_storage) { if (p_storage->IsReadMode()) { - p_storage->ReadS16(m_unk0x372); + p_storage->ReadS16(m_nextPlayerId); p_storage->ReadS16(m_count); for (MxS16 i = 0; i < m_count; i++) { @@ -1554,7 +1601,7 @@ MxResult LegoGameState::History::Serialize(LegoStorage* p_storage) } } else if (p_storage->IsWriteMode()) { - p_storage->WriteS16(m_unk0x372); + p_storage->WriteS16(m_nextPlayerId); p_storage->WriteS16(m_count); for (MxS16 i = 0; i < m_count; i++) { From 2595537c4cbdaff80786beac32d51d0e10763347 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Sun, 22 Jun 2025 14:34:29 +0200 Subject: [PATCH 05/26] Clear unknowns in `LegoAnimActorEntry` and `LegoAnimKey` (#1580) --- .../src/build/legocarbuildpresenter.cpp | 4 ++-- .../legoomni/src/video/legoanimpresenter.cpp | 12 ++++++------ LEGO1/lego/sources/anim/legoanim.cpp | 8 ++++---- LEGO1/lego/sources/anim/legoanim.h | 18 +++++++++++++----- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp index 87c896dc..d6512c8b 100644 --- a/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp +++ b/LEGO1/lego/legoomni/src/build/legocarbuildpresenter.cpp @@ -577,10 +577,10 @@ void LegoCarBuildAnimPresenter::RotateAroundYAxis(MxFloat p_angle) newRotation.EqualsHamiltonProduct(currentRotation, additionalRotation); if (newRotation[3] < 0.9999) { - rotationKey->FUN_100739a0(TRUE); + rotationKey->SetActive(TRUE); } else { - rotationKey->FUN_100739a0(FALSE); + rotationKey->SetActive(FALSE); } m_platformAnimNodeData->GetRotationKey(0)->SetX(newRotation[0]); diff --git a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp index 54b1da70..852263fc 100644 --- a/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp +++ b/LEGO1/lego/legoomni/src/video/legoanimpresenter.cpp @@ -221,10 +221,10 @@ void LegoAnimPresenter::FUN_100692b0() for (LegoU32 i = 0; i < numActors; i++) { LegoChar* str = GetVariableOrIdentity(m_anim->GetActorName(i), NULL); - undefined4 unk0x04 = m_anim->GetActorUnknown0x04(i); + LegoU32 actorType = m_anim->GetActorType(i); LegoROI* roi = NULL; - if (unk0x04 == 2) { + if (actorType == LegoAnimActorEntry::e_actorType2) { LegoChar* src; if (str[0] == '*') { src = str + 1; @@ -239,7 +239,7 @@ void LegoAnimPresenter::FUN_100692b0() roi->SetVisibility(FALSE); } } - else if (unk0x04 == 4) { + else if (actorType == LegoAnimActorEntry::e_actorType4) { LegoChar* baseName = new LegoChar[strlen(str)]; strcpy(baseName, str + 1); strlwr(baseName); @@ -254,7 +254,7 @@ void LegoAnimPresenter::FUN_100692b0() delete[] baseName; delete[] und; } - else if (unk0x04 == 3) { + else if (actorType == LegoAnimActorEntry::e_actorType3) { LegoChar* lodName = new LegoChar[strlen(str)]; strcpy(lodName, str + 1); @@ -300,9 +300,9 @@ void LegoAnimPresenter::FUN_100695c0() for (LegoU32 i = 0; i < numActors; i++) { if (FUN_100698b0(rois, m_anim->GetActorName(i)) == FALSE) { - undefined4 unk0x04 = m_anim->GetActorUnknown0x04(i); + LegoU32 actorType = m_anim->GetActorType(i); - if (unk0x04 == 5 || unk0x04 == 6) { + if (actorType == LegoAnimActorEntry::e_actorType5 || actorType == LegoAnimActorEntry::e_actorType6) { LegoChar lodName[256]; const LegoChar* actorName = m_anim->GetActorName(i); diff --git a/LEGO1/lego/sources/anim/legoanim.cpp b/LEGO1/lego/sources/anim/legoanim.cpp index dc3c21da..8ec3ee2d 100644 --- a/LEGO1/lego/sources/anim/legoanim.cpp +++ b/LEGO1/lego/sources/anim/legoanim.cpp @@ -1063,7 +1063,7 @@ LegoResult LegoAnim::Read(LegoStorage* p_storage, LegoS32 p_parseScene) m_modelList[i].m_name[length] = '\0'; - if (p_storage->Read(&m_modelList[i].m_unk0x04, sizeof(undefined4)) != SUCCESS) { + if (p_storage->Read(&m_modelList[i].m_type, sizeof(LegoU32)) != SUCCESS) { goto done; } } @@ -1124,7 +1124,7 @@ LegoResult LegoAnim::Write(LegoStorage* p_storage) goto done; } - if (p_storage->Write(&m_modelList[i].m_unk0x04, sizeof(m_modelList[i].m_unk0x04)) != SUCCESS) { + if (p_storage->Write(&m_modelList[i].m_type, sizeof(m_modelList[i].m_type)) != SUCCESS) { goto done; } } @@ -1159,10 +1159,10 @@ const LegoChar* LegoAnim::GetActorName(LegoU32 p_index) // FUNCTION: LEGO1 0x100a0f40 // FUNCTION: BETA10 0x1018023c -undefined4 LegoAnim::GetActorUnknown0x04(LegoU32 p_index) +LegoU32 LegoAnim::GetActorType(LegoU32 p_index) { if (p_index < m_numActors) { - return m_modelList[p_index].m_unk0x04; + return m_modelList[p_index].m_type; } return 0; diff --git a/LEGO1/lego/sources/anim/legoanim.h b/LEGO1/lego/sources/anim/legoanim.h index 9b81b531..372128b5 100644 --- a/LEGO1/lego/sources/anim/legoanim.h +++ b/LEGO1/lego/sources/anim/legoanim.h @@ -30,9 +30,9 @@ class LegoAnimKey { LegoU32 ShouldSkipInterpolation() { return m_flags & c_skipInterpolation; } // FUNCTION: BETA10 0x100739a0 - void FUN_100739a0(MxS32 p_param) + void SetActive(MxS32 p_active) { - if (p_param) { + if (p_active) { m_flags |= c_active; } else { @@ -289,8 +289,16 @@ class LegoAnimNodeData : public LegoTreeNodeData { // SIZE 0x08 struct LegoAnimActorEntry { - LegoChar* m_name; // 0x00 - undefined4 m_unk0x04; // 0x04 + enum { + e_actorType2 = 2, + e_actorType3 = 3, + e_actorType4 = 4, + e_actorType5 = 5, + e_actorType6 = 6, + }; + + LegoChar* m_name; // 0x00 + LegoU32 m_type; // 0x04 }; // TODO: Possibly called `LegoCameraAnim(ation)`? @@ -338,7 +346,7 @@ class LegoAnim : public LegoTree { virtual LegoResult Read(LegoStorage* p_storage, LegoS32 p_parseScene); // vtable+0x10 const LegoChar* GetActorName(LegoU32 p_index); - undefined4 GetActorUnknown0x04(LegoU32 p_index); + LegoU32 GetActorType(LegoU32 p_index); // FUNCTION: BETA10 0x1005abf0 LegoAnimScene* GetCamAnim() { return m_camAnim; } From 5a628cafaadcef8950dccf2004f972dc848d3969 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 22 Jun 2025 17:07:56 +0200 Subject: [PATCH 06/26] No longer log file mapping (#396) --- LEGO1/omni/src/common/mxstring.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/LEGO1/omni/src/common/mxstring.cpp b/LEGO1/omni/src/common/mxstring.cpp index f0c84d02..e8167a0c 100644 --- a/LEGO1/omni/src/common/mxstring.cpp +++ b/LEGO1/omni/src/common/mxstring.cpp @@ -217,7 +217,6 @@ void MxString::MapPathToFilesystem(char* p_path) } else if (j == 1) { SDL_strlcpy(&p_path[i - 1], file.GetData(), file.GetLength() + 1); - SDL_Log("Resolved file path to %s", p_path); return true; } } From 0a5091531253edb05826fa12588d81e6219ee418 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 22 Jun 2025 08:55:36 -0700 Subject: [PATCH 07/26] Fix implicit signed char-ness (#1581) --- LEGO1/omni/src/video/flic.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/omni/src/video/flic.cpp b/LEGO1/omni/src/video/flic.cpp index afdebebc..d262e517 100644 --- a/LEGO1/omni/src/video/flic.cpp +++ b/LEGO1/omni/src/video/flic.cpp @@ -288,7 +288,7 @@ void DecodeBrun(LPBITMAPINFOHEADER p_bitmapHeader, BYTE* p_pixelData, BYTE* p_da while (--line >= 0) { short column = 0; data++; - char count = 0; + signed char count = 0; while ((column += count) < width2) { count = *data++; @@ -331,7 +331,7 @@ void DecodeLC(LPBITMAPINFOHEADER p_bitmapHeader, BYTE* p_pixelData, BYTE* p_data while (packets > 0) { column += *data++; // skip byte - char type = *((char*) data++); + signed char type = *((signed char*) data++); if (type < 0) { type = -type; @@ -417,7 +417,7 @@ void DecodeSS2(LPBITMAPINFOHEADER p_bitmapHeader, BYTE* p_pixelData, BYTE* p_dat // LINE: BETA10 0x1013e726 column += *data.byte++; // LINE: BETA10 0x1013e73a - short type = *(char*) data.byte++; + short type = *(signed char*) data.byte++; type += type; if (type >= 0) { From 92e0cb084514c1b51eb5673a92961b463c3d62ac Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 22 Jun 2025 09:34:05 -0700 Subject: [PATCH 08/26] Fix `JoystickIndex` ini default (#400) --- ISLE/isleapp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index 6c4c8e2f..e7791735 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -794,6 +794,7 @@ bool IsleApp::LoadConfig() return false; } + char buf[32]; dict = iniparser_load(iniConfig); iniparser_set(dict, "isle", NULL); @@ -810,12 +811,11 @@ bool IsleApp::LoadConfig() iniparser_set(dict, "isle:Music", m_useMusic ? "true" : "false"); iniparser_set(dict, "isle:UseJoystick", m_useJoystick ? "true" : "false"); - iniparser_set(dict, "isle:JoystickIndex", m_joystickIndex ? "true" : "false"); + iniparser_set(dict, "isle:JoystickIndex", SDL_itoa(m_joystickIndex, buf, 10)); iniparser_set(dict, "isle:Draw Cursor", m_drawCursor ? "true" : "false"); iniparser_set(dict, "isle:Back Buffers in Video RAM", "-1"); - char buf[32]; iniparser_set(dict, "isle:Island Quality", SDL_itoa(m_islandQuality, buf, 10)); iniparser_set(dict, "isle:Island Texture", SDL_itoa(m_islandTexture, buf, 10)); SDL_snprintf(buf, sizeof(buf), "%f", m_maxLod); From 8195d2b251b4b9436cabddc2b4c12e63ea35e88e Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 22 Jun 2025 09:48:09 -0700 Subject: [PATCH 09/26] Update legogamestate.cpp (#1582) --- LEGO1/lego/legoomni/src/common/legogamestate.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/LEGO1/lego/legoomni/src/common/legogamestate.cpp b/LEGO1/lego/legoomni/src/common/legogamestate.cpp index 9c67340c..0a2c452a 100644 --- a/LEGO1/lego/legoomni/src/common/legogamestate.cpp +++ b/LEGO1/lego/legoomni/src/common/legogamestate.cpp @@ -1532,8 +1532,6 @@ void LegoGameState::History::WriteScoreHistory() } else { if (m_count < (MxS16) sizeOfArray(m_scores)) { - assert(totalScore > p_scorehist->m_totalScore); - m_scores[m_count].m_totalScore = totalScore; memcpy(m_scores[m_count].m_scores, scores, sizeof(m_scores[m_count].m_scores)); m_scores[m_count].m_name = GameState()->m_players[0]; From 5a756f033b9a928134ece52f719aee2087765732 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 22 Jun 2025 09:49:06 -0700 Subject: [PATCH 10/26] Set `UseJoystick` to true by default (#397) --- ISLE/isleapp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISLE/isleapp.cpp b/ISLE/isleapp.cpp index e7791735..d1d1ec69 100644 --- a/ISLE/isleapp.cpp +++ b/ISLE/isleapp.cpp @@ -109,7 +109,7 @@ IsleApp::IsleApp() m_drawCursor = FALSE; m_use3dSound = TRUE; m_useMusic = TRUE; - m_useJoystick = FALSE; + m_useJoystick = TRUE; m_joystickIndex = 0; m_wideViewAngle = TRUE; m_islandQuality = 2; From 3698f4fb0cb93b0f69d3504bd25b9ed7134a1050 Mon Sep 17 00:00:00 2001 From: Danct12 Date: Mon, 23 Jun 2025 00:54:35 +0700 Subject: [PATCH 11/26] Bump version for SDL_SCALEMODE_PIXELART to 3.3.0 (#403) --- ISLE/isledebug.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISLE/isledebug.cpp b/ISLE/isledebug.cpp index d60307cc..5283cdbc 100644 --- a/ISLE/isledebug.cpp +++ b/ISLE/isledebug.cpp @@ -183,7 +183,7 @@ void IsleDebug_Init() } g_videoPalette = SDL_CreateTexture(g_debugRenderer, SDL_PIXELFORMAT_RGBX32, SDL_TEXTUREACCESS_STREAMING, 16, 16); -#if SDL_VERSION_ATLEAST(3, 2, 0) +#if SDL_VERSION_ATLEAST(3, 3, 0) SDL_SetTextureScaleMode(g_videoPalette, SDL_SCALEMODE_PIXELART); #else SDL_SetTextureScaleMode(g_videoPalette, SDL_SCALEMODE_NEAREST); From 0c66273c9fc65ef2df598b1f20ab16a2d01e0258 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 22 Jun 2025 22:04:21 +0200 Subject: [PATCH 12/26] Update README.md [skip ci] --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f22ba427..33dbd232 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Please note: this project is dedicated to achieving platform independence withou | macOS | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | | [Web](https://isle.pizza) | [![CI](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml/badge.svg)](https://github.com/isledecomp/isle-portable/actions/workflows/ci.yml) | +We are actively working to support more platforms. If you have experience with a particular platform, we encourage you to contribute to `isle-portable`. You can find a [list of ongoing efforts](https://github.com/isledecomp/isle-portable/wiki/Work%E2%80%90in%E2%80%90progress-ports) in our Wiki. + ### Library substitutions To achieve our goal of platform independence, we need to replace any Windows-only libraries with platform-independent alternatives. This ensures that our codebase remains versatile and compatible across various systems. The following table serves as an overview of major libraries / subsystems and their chosen replacements. For any significant changes or additions, it's recommended to discuss them with the team on the Matrix chat first to ensure consistency and alignment with our project's objectives. From 1677193635a650c4275fa73f73ecb7e7f573d825 Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:32:19 +0200 Subject: [PATCH 13/26] Add reference to `isle-portable` to README (#1579) * Add reference to `isle-portable` to README * Fix uppercase * Address review comments --------- Co-authored-by: jonschz --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c246e850..6d62e581 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ This is a functionally complete decompilation of LEGO Island (Version 1.1, English). It aims to be as accurate as possible, matching the recompiled instructions to the original machine code as much as possible. The goal is to provide a workable codebase that can be modified, improved, and ported to other platforms later on. +> **Note:** This repository is for decompilation only and its code is true to the original release. It will not compile for targets other than 32-bit Windows. For a modern adaptation of the LEGO Island codebase with native compatibility for all major platforms and the Web, see [isle-portable](https://github.com/isledecomp/isle-portable) instead. + ## Status From 1701117e6fc085daf74f5b85d46beed9cab27532 Mon Sep 17 00:00:00 2001 From: MS Date: Mon, 23 Jun 2025 10:24:13 -0400 Subject: [PATCH 14/26] Improve `MeshBuilderImpl::CreateMesh` (#1584) * CreateMesh * Effective match for Clone() --- LEGO1/tgl/d3drm/impl.h | 3 ++ LEGO1/tgl/d3drm/meshbuilder.cpp | 82 +++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/LEGO1/tgl/d3drm/impl.h b/LEGO1/tgl/d3drm/impl.h index 25a3a8a1..0d9851fa 100644 --- a/LEGO1/tgl/d3drm/impl.h +++ b/LEGO1/tgl/d3drm/impl.h @@ -866,4 +866,7 @@ inline D3DRMMATERIALMODE Translate(MaterialMode mode) // GLOBAL: LEGO1 0x100dd1e0 // IID_IDirect3DRMMeshBuilder +// GLOBAL: LEGO1 0x100dd1f0 +// IID_IDirect3DRMMesh + #endif diff --git a/LEGO1/tgl/d3drm/meshbuilder.cpp b/LEGO1/tgl/d3drm/meshbuilder.cpp index 3cabb29a..1f9da01a 100644 --- a/LEGO1/tgl/d3drm/meshbuilder.cpp +++ b/LEGO1/tgl/d3drm/meshbuilder.cpp @@ -15,6 +15,7 @@ void* MeshBuilderImpl::ImplementationDataPtr() } // FUNCTION: LEGO1 0x100a3840 +// FUNCTION: BETA10 0x1016ca40 Mesh* MeshBuilderImpl::CreateMesh( unsigned long faceCount, unsigned long vertexCount, @@ -26,6 +27,8 @@ Mesh* MeshBuilderImpl::CreateMesh( ShadingModel shadingModel ) { + assert(m_data); + MeshImpl* pMeshImpl = new MeshImpl; if (CreateMeshImpl( pMeshImpl, @@ -45,63 +48,67 @@ Mesh* MeshBuilderImpl::CreateMesh( return pMeshImpl; } +// FUNCTION: BETA10 0x1016fef0 inline Result CreateMesh( IDirect3DRMMesh* pD3DRM, - unsigned long faceCount, - unsigned long vertexCount, - float (*pPositions)[3], - float (*pNormals)[3], - float (*pTextureCoordinates)[2], - unsigned long (*pFaceIndices)[3], - unsigned long (*pTextureIndices)[3], + unsigned long p_numFaces, + unsigned long p_numVertices, + float(*p_positions), + float(*p_normals), + float(*p_textureCoordinates), + unsigned long (*p_faceIndices)[3], + unsigned long (*p_textureIndices)[3], ShadingModel shadingModel, MeshImpl::MeshDataType& rpMesh ) { - unsigned long* faceIndices = (unsigned long*) pFaceIndices; + unsigned short* faceIndices = (unsigned short*) p_faceIndices; D3DRMGROUPINDEX groupIndex = 0; - int count = faceCount * 3; - int index = 0; + int faceCount = p_numFaces * 3; + int count = 0; - unsigned int* fData = new unsigned int[count]; + unsigned int* fData = new unsigned int[faceCount]; - D3DRMVERTEX* vertices = new D3DRMVERTEX[vertexCount]; - memset(vertices, 0, sizeof(*vertices) * vertexCount); + D3DRMVERTEX* vertices = new D3DRMVERTEX[p_numVertices]; + memset(vertices, 0, sizeof(*vertices) * p_numVertices); rpMesh = new MeshImpl::MeshData; rpMesh->groupMesh = pD3DRM; - for (int i = 0; i < count; i++) { - if ((*((unsigned short*) &faceIndices[i] + 1) >> 0x0f) & 0x01) { - unsigned long j = *(unsigned short*) &faceIndices[i]; - vertices[index].position.x = pPositions[j][0]; - vertices[index].position.y = pPositions[j][1]; - vertices[index].position.z = pPositions[j][2]; - j = *((unsigned short*) &faceIndices[i] + 1) & MAXSHORT; - vertices[index].normal.x = pNormals[j][0]; - vertices[index].normal.y = pNormals[j][1]; - vertices[index].normal.z = pNormals[j][2]; + for (int i = 0; i < faceCount; i++) { + if (((faceIndices[2 * i + 1]) >> 0x0f) & 0x01) { + unsigned long j = 3 * faceIndices[2 * i]; + vertices[count].position.x = p_positions[j]; + vertices[count].position.y = p_positions[j + 1]; + vertices[count].position.z = p_positions[j + 2]; - if (pTextureIndices != NULL && pTextureCoordinates != NULL) { - j = ((unsigned long*) pTextureIndices)[i]; - vertices[index].tu = pTextureCoordinates[j][0]; - vertices[index].tv = pTextureCoordinates[j][1]; + int k = 3 * (faceIndices[2 * i + 1] & MAXSHORT); + vertices[count].normal.x = p_normals[k]; + vertices[count].normal.y = p_normals[k + 1]; + vertices[count].normal.z = p_normals[k + 2]; + + if (p_textureIndices != NULL && p_textureCoordinates != NULL) { + int kk = 2 * ((unsigned long*) p_textureIndices)[i]; + vertices[count].tu = p_textureCoordinates[kk]; + vertices[count].tv = p_textureCoordinates[kk + 1]; } - fData[i] = index; - index++; + fData[i] = count; + count++; } else { - fData[i] = *(unsigned short*) &faceIndices[i]; + fData[i] = faceIndices[2 * i]; } } + assert(count == (int) p_numVertices); + Result result; - result = ResultVal(pD3DRM->AddGroup(vertexCount, faceCount, 3, fData, &groupIndex)); + result = ResultVal(pD3DRM->AddGroup(p_numVertices, p_numFaces, 3, fData, &groupIndex)); if (Succeeded(result)) { rpMesh->groupIndex = groupIndex; - result = ResultVal(pD3DRM->SetVertices(groupIndex, 0, vertexCount, vertices)); + result = ResultVal(pD3DRM->SetVertices(groupIndex, 0, p_numVertices, vertices)); } if (!Succeeded(result)) { @@ -112,6 +119,7 @@ inline Result CreateMesh( } else { result = MeshSetTextureMappingMode(rpMesh, PerspectiveCorrect); + assert(Succeeded(result)); } if (fData != NULL) { @@ -125,6 +133,7 @@ inline Result CreateMesh( return result; } +// FUNCTION: BETA10 0x1016fe40 inline Result MeshBuilderImpl::CreateMeshImpl( MeshImpl* pMeshImpl, unsigned long faceCount, @@ -137,13 +146,16 @@ inline Result MeshBuilderImpl::CreateMeshImpl( ShadingModel shadingModel ) { + assert(m_data); + assert(!pMeshImpl->ImplementationData()); + return ::CreateMesh( m_data, faceCount, vertexCount, - pPositions, - pNormals, - pTextureCoordinates, + reinterpret_cast(pPositions), + reinterpret_cast(pNormals), + reinterpret_cast(pTextureCoordinates), pFaceIndices, pTextureIndices, shadingModel, From 19fee55333ee4a422f95f5279afcae0cef20d961 Mon Sep 17 00:00:00 2001 From: Danct12 Date: Mon, 23 Jun 2025 22:20:39 +0700 Subject: [PATCH 15/26] Shared library support (#404) This allows isle-portable to be built using shared libraries provided by the developer's distribution. --- CMakeLists.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 461e8378..334580c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,8 @@ message(STATUS "Internal miniwin: ${ISLE_MINIWIN}") message(STATUS "Isle debugging: ${ISLE_DEBUG}") message(STATUS "Compile shaders: ${ISLE_COMPILE_SHADERS}") +add_library(Isle::iniparser INTERFACE IMPORTED) + if (DOWNLOAD_DEPENDENCIES) # FetchContent downloads and configures dependencies message(STATUS "Fetching SDL3 and iniparser. This might take a while...") @@ -73,6 +75,7 @@ if (DOWNLOAD_DEPENDENCIES) set(BUILD_DOCS off) set(BUILD_SHARED_LIBS off) FetchContent_MakeAvailable(iniparser) + target_link_libraries(Isle::iniparser INTERFACE iniparser-static) endblock() else() # find_package looks for already-installed system packages. @@ -80,10 +83,8 @@ else() # to add search paths. find_package(SDL3 CONFIG REQUIRED) - find_package(iniparser CONFIG COMPONENTS static) - if (NOT TARGET iniparser-static) - find_package(iniparser REQUIRED MODULE COMPONENTS static) - endif() + find_package(iniparser REQUIRED CONFIG COMPONENTS shared) + target_link_libraries(Isle::iniparser INTERFACE iniparser-shared) endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -484,7 +485,7 @@ if (ISLE_BUILD_APP) target_link_libraries(isle PRIVATE $<$:DirectX5::DirectX5>) # Link SDL and iniparser - target_link_libraries(isle PRIVATE SDL3::SDL3 iniparser-static) + target_link_libraries(isle PRIVATE SDL3::SDL3 Isle::iniparser) # Allow unconditional include of miniwin/miniwindevice.h target_link_libraries(isle PRIVATE miniwin-headers) @@ -547,7 +548,7 @@ if (ISLE_BUILD_CONFIG) target_link_libraries(config PRIVATE DirectX5::DirectX5) endif() target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500) - target_link_libraries(config PRIVATE SDL3::SDL3 iniparser-static) + target_link_libraries(config PRIVATE SDL3::SDL3 Isle::iniparser) if (NOT ISLE_MINIWIN) target_link_libraries(config PRIVATE ddraw dxguid) endif() From edae5f58ec40a387139dc3dbf9c4dcf1ab6327e9 Mon Sep 17 00:00:00 2001 From: jonschz <17198703+jonschz@users.noreply.github.com> Date: Mon, 23 Jun 2025 20:58:16 +0200 Subject: [PATCH 16/26] Remove getters/setters that (likely) don't exist (#1583) Co-authored-by: jonschz --- LEGO1/lego/legoomni/include/legogamestate.h | 9 --------- LEGO1/lego/legoomni/src/common/legogamestate.cpp | 1 + LEGO1/lego/legoomni/src/worlds/act3.cpp | 6 +++--- LEGO1/lego/legoomni/src/worlds/infocenter.cpp | 12 ++++++------ LEGO1/lego/legoomni/src/worlds/isle.cpp | 2 +- LEGO1/lego/legoomni/src/worlds/legoact2.cpp | 6 +++--- 6 files changed, 14 insertions(+), 22 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legogamestate.h b/LEGO1/lego/legoomni/include/legogamestate.h index b81696fd..e5dd9a26 100644 --- a/LEGO1/lego/legoomni/include/legogamestate.h +++ b/LEGO1/lego/legoomni/include/legogamestate.h @@ -223,14 +223,8 @@ class LegoGameState { Act GetCurrentAct() { return m_currentAct; } Act GetLoadedAct() { return m_loadedAct; } - Area GetPreviousArea() { return m_previousArea; } - Area GetUnknown0x42c() { return m_unk0x42c; } - void SetDirty(MxBool p_isDirty) { m_isDirty = p_isDirty; } - void SetPreviousArea(Area p_previousArea) { m_previousArea = p_previousArea; } void SetActorId(MxU8 p_actorId) { m_actorId = p_actorId; } - Username* GetPlayersIndex(MxS32 p_index) { return &m_players[p_index]; } - MxS16 GetPlayerCount() { return m_playerCount; } LegoBackgroundColor* GetBackgroundColor() { return m_backgroundColor; } void SetCurrentAct(Act p_currentAct); @@ -254,14 +248,11 @@ class LegoGameState { LegoBackgroundColor* m_tempBackgroundColor; // 0x1c LegoFullScreenMovie* m_fullScreenMovie; // 0x20 - // TODO: Most likely getters/setters are not used according to BETA for the following members: - public: MxS16 m_currentPlayerId; // 0x24 MxS16 m_playerCount; // 0x26 Username m_players[9]; // 0x28 History m_history; // 0xa6 - undefined2 m_unk0x41a; // 0x41a JukeboxScript::Script m_jukeboxMusic; // 0x41c MxBool m_isDirty; // 0x420 Area m_currentArea; // 0x424 diff --git a/LEGO1/lego/legoomni/src/common/legogamestate.cpp b/LEGO1/lego/legoomni/src/common/legogamestate.cpp index 0a2c452a..1e53cb4f 100644 --- a/LEGO1/lego/legoomni/src/common/legogamestate.cpp +++ b/LEGO1/lego/legoomni/src/common/legogamestate.cpp @@ -1629,6 +1629,7 @@ void LegoGameState::SerializeScoreHistory(MxS16 p_flags) } // FUNCTION: LEGO1 0x1003cea0 +// FUNCTION: BETA10 0x10017840 void LegoGameState::SetCurrentAct(Act p_currentAct) { m_currentAct = p_currentAct; diff --git a/LEGO1/lego/legoomni/src/worlds/act3.cpp b/LEGO1/lego/legoomni/src/worlds/act3.cpp index 48aacd92..64ed8d6c 100644 --- a/LEGO1/lego/legoomni/src/worlds/act3.cpp +++ b/LEGO1/lego/legoomni/src/worlds/act3.cpp @@ -488,7 +488,7 @@ MxResult Act3::Create(MxDSAction& p_dsAction) case LegoGameState::e_act1: case LegoGameState::e_actNotFound: GameState()->StopArea(LegoGameState::e_undefined); - if (GameState()->GetPreviousArea() == LegoGameState::e_infomain) { + if (GameState()->m_previousArea == LegoGameState::e_infomain) { GameState()->StopArea(LegoGameState::e_isle); } } @@ -505,7 +505,7 @@ MxResult Act3::Create(MxDSAction& p_dsAction) GameState()->m_currentArea = LegoGameState::e_act3script; GameState()->SetCurrentAct(LegoGameState::e_act3); - GameState()->SetDirty(TRUE); + GameState()->m_isDirty = TRUE; } return result; @@ -883,7 +883,7 @@ void Act3::Enable(MxBool p_enable) FUN_10015820(FALSE, LegoOmni::c_disableInput | LegoOmni::c_disable3d | LegoOmni::c_clearScreen); PlayMusic(JukeboxScript::c_Act3Music); - GameState()->SetDirty(TRUE); + GameState()->m_isDirty = TRUE; if (m_time > 0) { MxFloat delta = Timer()->GetTime() - m_time - 100.0f; diff --git a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp index 8a41663e..90078e6d 100644 --- a/LEGO1/lego/legoomni/src/worlds/infocenter.cpp +++ b/LEGO1/lego/legoomni/src/worlds/infocenter.cpp @@ -219,7 +219,7 @@ MxResult Infocenter::Create(MxDSAction& p_dsAction) if (m_infocenterState->m_unk0x74 == 4) { LegoGameState* state = GameState(); - state->SetPreviousArea(GameState()->GetUnknown0x42c()); + state->m_previousArea = GameState()->m_unk0x42c; } InputManager()->Register(this); @@ -1020,13 +1020,13 @@ MxU8 Infocenter::HandleControl(LegoControlManagerNotificationParam& p_param) case InfomainScript::c_BigInfo_Ctl: switch (state->GetCurrentAct()) { case LegoGameState::e_act1: - if (state->GetPreviousArea()) { - switch (state->GetPreviousArea()) { + if (state->m_previousArea) { + switch (state->m_previousArea) { case LegoGameState::e_infodoor: case LegoGameState::e_regbook: case LegoGameState::e_infoscor: m_infocenterState->m_unk0x74 = 5; - m_destLocation = state->GetPreviousArea(); + m_destLocation = state->m_previousArea; actionToPlay = (InfomainScript::Script) m_infocenterState->GetNextLeaveDialogue(); m_radio.Stop(); InputManager()->DisableInputProcessing(); @@ -1081,7 +1081,7 @@ MxU8 Infocenter::HandleControl(LegoControlManagerNotificationParam& p_param) actionToPlay = GameState()->GetCurrentAct() != LegoGameState::e_act1 ? InfomainScript::c_GoTo_RegBook_Red : InfomainScript::c_GoTo_RegBook; m_radio.Stop(); - GameState()->m_unk0x42c = GameState()->GetPreviousArea(); + GameState()->m_unk0x42c = GameState()->m_previousArea; InputManager()->DisableInputProcessing(); break; case InfomainScript::c_Mama_Ctl: @@ -1390,7 +1390,7 @@ void Infocenter::Reset() AnimationManager()->Reset(FALSE); CharacterManager()->ReleaseAllActors(); GameState()->SetCurrentAct(LegoGameState::e_act1); - GameState()->SetPreviousArea(LegoGameState::e_undefined); + GameState()->m_previousArea = LegoGameState::e_undefined; GameState()->m_unk0x42c = LegoGameState::e_undefined; InitializeBitmaps(); diff --git a/LEGO1/lego/legoomni/src/worlds/isle.cpp b/LEGO1/lego/legoomni/src/worlds/isle.cpp index 1aa352fe..050aae97 100644 --- a/LEGO1/lego/legoomni/src/worlds/isle.cpp +++ b/LEGO1/lego/legoomni/src/worlds/isle.cpp @@ -121,7 +121,7 @@ MxResult Isle::Create(MxDSAction& p_dsAction) m_act1state = act1state; EnableAnimations(TRUE); - GameState()->SetDirty(TRUE); + GameState()->m_isDirty = TRUE; } return result; diff --git a/LEGO1/lego/legoomni/src/worlds/legoact2.cpp b/LEGO1/lego/legoomni/src/worlds/legoact2.cpp index 2b9dd80b..ddc97f1c 100644 --- a/LEGO1/lego/legoomni/src/worlds/legoact2.cpp +++ b/LEGO1/lego/legoomni/src/worlds/legoact2.cpp @@ -141,7 +141,7 @@ MxResult LegoAct2::Create(MxDSAction& p_dsAction) case LegoGameState::e_act1: case LegoGameState::e_actNotFound: GameState()->StopArea(LegoGameState::e_undefined); - if (GameState()->GetPreviousArea() == LegoGameState::e_infomain) { + if (GameState()->m_previousArea == LegoGameState::e_infomain) { GameState()->StopArea(LegoGameState::e_isle); } } @@ -149,7 +149,7 @@ MxResult LegoAct2::Create(MxDSAction& p_dsAction) GameState()->m_currentArea = LegoGameState::e_act2main; GameState()->SetCurrentAct(LegoGameState::e_act2); InputManager()->Register(this); - GameState()->SetDirty(TRUE); + GameState()->m_isDirty = TRUE; } return result; @@ -536,7 +536,7 @@ void LegoAct2::Enable(MxBool p_enable) ((IslePathActor*) m_pepper->GetEntity())->VTable0xec(m_unk0x10dc, m_unk0x1124, TRUE); - if (GameState()->GetPreviousArea() == LegoGameState::e_infomain) { + if (GameState()->m_previousArea == LegoGameState::e_infomain) { GameState()->StopArea(LegoGameState::e_infomain); } From c8bb0da5a747e660bf29faf3eaa76d868a590e0f Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Mon, 23 Jun 2025 23:04:52 +0200 Subject: [PATCH 17/26] Implement DrawFPS (#420) --- .../lego/legoomni/include/legovideomanager.h | 3 + .../legoomni/src/video/legovideomanager.cpp | 126 ++++++++++-------- miniwin/include/miniwin/windows.h | 1 - 3 files changed, 71 insertions(+), 59 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legovideomanager.h b/LEGO1/lego/legoomni/include/legovideomanager.h index e28c581d..6c12d863 100644 --- a/LEGO1/lego/legoomni/include/legovideomanager.h +++ b/LEGO1/lego/legoomni/include/legovideomanager.h @@ -78,6 +78,9 @@ class LegoVideoManager : public MxVideoManager { inline void DrawCursor(); + void DrawDigitToBuffer32(uint8_t* p_dst, int p_pitch, int p_x, int p_y, int p_digit, uint32_t p_color); + void DrawTextToSurface32(uint8_t* p_dst, int p_pitch, int p_x, int p_y, const char* p_text, uint32_t p_color); + Tgl::Renderer* m_renderer; // 0x64 Lego3DManager* m_3dManager; // 0x68 LegoROI* m_viewROI; // 0x6c diff --git a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp index 66a3b9b9..a0173224 100644 --- a/LEGO1/lego/legoomni/src/video/legovideomanager.cpp +++ b/LEGO1/lego/legoomni/src/video/legovideomanager.cpp @@ -406,37 +406,14 @@ inline void LegoVideoManager::DrawCursor() // FUNCTION: LEGO1 0x1007bbc0 void LegoVideoManager::DrawFPS() { - char zeros[8] = "0000.00"; - if (m_unk0x528 == NULL) { - m_arialFont = CreateFont( - 12, - 0, - 0, - 0, - FW_NORMAL, - FALSE, - FALSE, - FALSE, - ANSI_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - FF_DONTCARE | VARIABLE_PITCH, - "Arial" - ); + int width = 64; // Big enough for 9999.99 + int height = 16; - HDC dc = GetDC(NULL); - SelectObject(dc, m_arialFont); - GetTextExtentPoint(dc, zeros, strlen(zeros), &m_fpsSize); - ReleaseDC(NULL, dc); - - m_unk0x528 = m_displaySurface->FUN_100bc8b0(m_fpsSize.cx, m_fpsSize.cy); - SetRect(&m_fpsRect, 0, 0, m_fpsSize.cx, m_fpsSize.cy); + m_unk0x528 = m_displaySurface->FUN_100bc8b0(width, height); + SetRect(&m_fpsRect, 0, 0, width, height); if (m_unk0x528 == NULL) { - DeleteObject(m_arialFont); - m_arialFont = NULL; return; } @@ -450,9 +427,7 @@ void LegoVideoManager::DrawFPS() if (m_unk0x528->Lock(NULL, &surfaceDesc, DDLOCK_WAIT, NULL) != DD_OK) { m_unk0x528->Release(); - DeleteObject(m_arialFont); m_unk0x528 = NULL; - m_arialFont = NULL; } else { DWORD i; @@ -472,8 +447,7 @@ void LegoVideoManager::DrawFPS() if (Timer()->GetTime() > m_unk0x54c + 5000.f) { char buffer[32]; MxFloat time = (Timer()->GetTime() - m_unk0x54c) / 1000.0f; - MxS32 nb = sprintf(buffer, "%.02f", m_unk0x550 / time); - SDL_Log("%.02f", m_unk0x550 / time); + sprintf(buffer, "%.02f", m_unk0x550 / time); m_unk0x54c = Timer()->GetTime(); DDSURFACEDESC surfaceDesc; @@ -481,37 +455,13 @@ void LegoVideoManager::DrawFPS() surfaceDesc.dwSize = sizeof(surfaceDesc); if (m_unk0x528->Lock(NULL, &surfaceDesc, DDLOCK_WAIT, NULL) == DD_OK) { - DWORD i; - char* ptr = (char*) surfaceDesc.lpSurface; + memset(surfaceDesc.lpSurface, 0, surfaceDesc.lPitch * surfaceDesc.dwHeight); - for (i = 0; i < surfaceDesc.dwHeight; i++) { - memset(ptr, 0, surfaceDesc.lPitch); - ptr += surfaceDesc.lPitch; - } + DrawTextToSurface32((uint8_t*) surfaceDesc.lpSurface, surfaceDesc.lPitch, 0, 0, buffer, 0xFF0000FF); m_unk0x528->Unlock(surfaceDesc.lpSurface); + m_unk0x550 = 1.f; } - - HDC dc; - if (m_unk0x528->GetDC(&dc) != DD_OK) { - m_unk0x528->Release(); - m_unk0x528 = NULL; - DeleteObject(m_arialFont); - m_arialFont = NULL; - return; - } - - SelectObject(dc, m_arialFont); - SetTextColor(dc, RGB(255, 255, 0)); - SetBkColor(dc, RGB(0, 0, 0)); - SetBkMode(dc, OPAQUE); - GetTextExtentPoint32(dc, buffer, nb, &m_fpsSize); - - RECT rect; - SetRect(&rect, 0, 0, m_fpsSize.cx, m_fpsSize.cy); - ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, buffer, nb, NULL); - m_unk0x528->ReleaseDC(dc); - m_unk0x550 = 1.f; } else { m_unk0x550 += 1.f; @@ -826,3 +776,63 @@ MxResult LegoVideoManager::ConfigureD3DRM() return SUCCESS; } + +void LegoVideoManager::DrawDigitToBuffer32(uint8_t* p_dst, int p_pitch, int p_x, int p_y, int p_digit, uint32_t p_color) +{ + if (p_digit < 0 || p_digit > 9) { + return; + } + + uint32_t* pixels = (uint32_t*) p_dst; + int rowStride = p_pitch / 4; + + // 4x5 bitmap font + const uint8_t digitFont[5][10] = { + {0b1111, 0b0001, 0b1111, 0b1111, 0b1001, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111}, + {0b1001, 0b0001, 0b0001, 0b0001, 0b1001, 0b1000, 0b1000, 0b0001, 0b1001, 0b1001}, + {0b1001, 0b0001, 0b1111, 0b1111, 0b1111, 0b1111, 0b1111, 0b0010, 0b1111, 0b1111}, + {0b1001, 0b0001, 0b1000, 0b0001, 0b0001, 0b0001, 0b1001, 0b0010, 0b1001, 0b0001}, + {0b1111, 0b0001, 0b1111, 0b1111, 0b0001, 0b1111, 0b1111, 0b0100, 0b1111, 0b1111}, + }; + + for (int row = 0; row < 5; ++row) { + uint8_t bits = digitFont[row][p_digit]; + for (int col = 0; col < 5; ++col) { + if (bits & (1 << (4 - col))) { + for (int dy = 0; dy < 2; ++dy) { + for (int dx = 0; dx < 2; ++dx) { + pixels[(p_y + row * 2 + dy) * rowStride + (p_x + col * 2 + dx)] = p_color; + } + } + } + } + } +} + +void LegoVideoManager::DrawTextToSurface32( + uint8_t* p_dst, + int p_pitch, + int p_x, + int p_y, + const char* p_text, + uint32_t p_color +) +{ + while (*p_text) { + if (*p_text >= '0' && *p_text <= '9') { + DrawDigitToBuffer32(p_dst, p_pitch, p_x, p_y, *p_text - '0', p_color); + p_x += 10; + } + else if (*p_text == '.') { + uint32_t* pixels = (uint32_t*) p_dst; + int rowStride = p_pitch / 4; + for (int dy = 0; dy < 2; ++dy) { + for (int dx = 0; dx < 2; ++dx) { + pixels[(p_y + 10 + dy) * rowStride + (p_x + 2 + dx)] = p_color; + } + } + p_x += 4; + } + ++p_text; + } +} diff --git a/miniwin/include/miniwin/windows.h b/miniwin/include/miniwin/windows.h index bd54249d..83726998 100644 --- a/miniwin/include/miniwin/windows.h +++ b/miniwin/include/miniwin/windows.h @@ -46,7 +46,6 @@ #define CLIP_DEFAULT_PRECIS 0 #define DEFAULT_QUALITY 0 #define ETO_OPAQUE 0x0002 -#define FF_DONTCARE 0x00000000 #define RASTERCAPS 0x00000000 #define RC_PALETTE 0x0100 #define SIZEPALETTE 104 From 7f10f7ae147d874bd0332129362b4bd5040e8516 Mon Sep 17 00:00:00 2001 From: Fabian Neundorf Date: Mon, 23 Jun 2025 23:34:02 +0200 Subject: [PATCH 18/26] Clear unknown in `ModelDbModel` (#1586) --- LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp | 2 +- LEGO1/modeldb/modeldb.cpp | 2 +- LEGO1/modeldb/modeldb.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp b/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp index 7aa4220e..91c413e1 100644 --- a/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp +++ b/LEGO1/lego/legoomni/src/entity/legoworldpresenter.cpp @@ -393,7 +393,7 @@ MxResult LegoWorldPresenter::LoadWorldModel(ModelDbModel& p_model, FILE* p_wdbFi } modelPresenter.SetAction(&action); - modelPresenter.FUN_1007ff70(chunk, createdEntity, p_model.m_unk0x34, p_world); + modelPresenter.FUN_1007ff70(chunk, createdEntity, p_model.m_visible, p_world); delete[] buff; return SUCCESS; diff --git a/LEGO1/modeldb/modeldb.cpp b/LEGO1/modeldb/modeldb.cpp index c59e5c8e..82e673ec 100644 --- a/LEGO1/modeldb/modeldb.cpp +++ b/LEGO1/modeldb/modeldb.cpp @@ -52,7 +52,7 @@ MxResult ModelDbModel::Read(FILE* p_file) if (fread(&m_up, sizeof(float), 3, p_file) != 3) { return FAILURE; } - if (fread(&m_unk0x34, sizeof(undefined), 1, p_file) != 1) { + if (fread(&m_visible, sizeof(MxU8), 1, p_file) != 1) { return FAILURE; } diff --git a/LEGO1/modeldb/modeldb.h b/LEGO1/modeldb/modeldb.h index 02642926..b9091e1f 100644 --- a/LEGO1/modeldb/modeldb.h +++ b/LEGO1/modeldb/modeldb.h @@ -101,7 +101,7 @@ struct ModelDbModel { float m_location[3]; // 0x10 float m_direction[3]; // 0x1c float m_up[3]; // 0x28 - undefined m_unk0x34; // 0x34 + MxU8 m_visible; // 0x34 }; // SIZE 0x18 From ce85377f18a574998320845ce30af1c37b5f4efe Mon Sep 17 00:00:00 2001 From: MattKC <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:00:23 -0700 Subject: [PATCH 19/26] Create LICENSE (#1497) --- LICENSE | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0a041280 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. From ae632c23675e40981619cd4006f4852d8b9c5083 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 09:08:16 -0700 Subject: [PATCH 20/26] Merge: update meshbuilder.cpp --- LEGO1/tgl/d3drm/meshbuilder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/tgl/d3drm/meshbuilder.cpp b/LEGO1/tgl/d3drm/meshbuilder.cpp index d3bd0213..3a7e5e54 100644 --- a/LEGO1/tgl/d3drm/meshbuilder.cpp +++ b/LEGO1/tgl/d3drm/meshbuilder.cpp @@ -53,9 +53,9 @@ inline Result CreateMesh( IDirect3DRMMesh* pD3DRM, unsigned int p_numFaces, unsigned int p_numVertices, - float (*p_positions)[3], - float (*p_normals)[3], - float (*p_textureCoordinates)[2], + float (*p_positions), + float (*p_normals), + float (*p_textureCoordinates), unsigned int (*p_faceIndices)[3], unsigned int (*p_textureIndices)[3], ShadingModel shadingModel, From a9505f435ae145d648326daf51db058e2998fbbb Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 09:11:08 -0700 Subject: [PATCH 21/26] Fix clang-format --- LEGO1/tgl/d3drm/meshbuilder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LEGO1/tgl/d3drm/meshbuilder.cpp b/LEGO1/tgl/d3drm/meshbuilder.cpp index 3a7e5e54..9111e7fd 100644 --- a/LEGO1/tgl/d3drm/meshbuilder.cpp +++ b/LEGO1/tgl/d3drm/meshbuilder.cpp @@ -53,9 +53,9 @@ inline Result CreateMesh( IDirect3DRMMesh* pD3DRM, unsigned int p_numFaces, unsigned int p_numVertices, - float (*p_positions), - float (*p_normals), - float (*p_textureCoordinates), + float(*p_positions), + float(*p_normals), + float(*p_textureCoordinates), unsigned int (*p_faceIndices)[3], unsigned int (*p_textureIndices)[3], ShadingModel shadingModel, From 822a6a338c40c7b82b0c835ab26a749561ccfb75 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 09:23:32 -0700 Subject: [PATCH 22/26] Fix debug window --- ISLE/isledebug.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ISLE/isledebug.cpp b/ISLE/isledebug.cpp index 5283cdbc..a8950fa3 100644 --- a/ISLE/isledebug.cpp +++ b/ISLE/isledebug.cpp @@ -292,9 +292,9 @@ void IsleDebug_Render() ImGui::Text("Actor Name: %s", gameState->GetActorName()); ImGui::Text("Current act: %d", gameState->GetCurrentAct()); ImGui::Text("Loaded act: %d", gameState->GetLoadedAct()); - ImGui::Text("Previous area: %d", gameState->GetPreviousArea()); - ImGui::Text("Unknown 0x42c: %d", gameState->GetUnknown0x42c()); - ImGui::Value("Player count", gameState->GetPlayerCount()); + ImGui::Text("Previous area: %d", gameState->m_previousArea); + ImGui::Text("Unknown 0x42c: %d", gameState->m_unk0x42c); + ImGui::Value("Player count", gameState->m_playerCount); ImGui::TreePop(); } if (ImGui::TreeNode("Renderer")) { From 87b4c0412e6e9d61df090021c2bab294cbe5d281 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Tue, 24 Jun 2025 15:03:54 -0700 Subject: [PATCH 23/26] 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 24/26] 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 25/26] 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 26/26] 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; +};