From 86ed9346aae14c19a301d9e4a70301f6954440ac Mon Sep 17 00:00:00 2001 From: Isuru Samarathunga Date: Tue, 21 Oct 2025 10:44:11 +0530 Subject: [PATCH] Optimized Renderer --- .../Imp/CPP/Components/CameraComponent.cpp | 2 +- .../Imp/CPP/Components/TextureComponent.cpp | 6 +- .../Imp/CPP/Components/UIButtonComponent.cpp | 65 ++++ .../Imp/CPP/Components/UIImageComponent.cpp | 52 ++++ .../Imp/CPP/Components/UILabelComponent.cpp | 3 +- Engine/Src/Imp/CPP/Engine.cpp | 20 +- Engine/Src/Imp/CPP/InputManager.cpp | 66 +++- Engine/Src/Imp/CPP/InternalEngine.cpp | 5 + Engine/Src/Imp/CPP/Renderer/Renderer.cpp | 287 +++++++----------- Engine/Src/Imp/CPP/ResourceManager.cpp | 25 +- Engine/Src/Imp/CPP/SceneManager.cpp | 3 - Engine/Src/Imp/CPP/UI.cpp | 4 +- Engine/Src/Imp/HPP/InputManager.hpp | 5 + Engine/Src/Imp/HPP/Renderer/Renderer.hpp | 51 ++-- Engine/Src/Imp/HPP/ResourceManager.hpp | 2 +- .../IAEngine/Components/UIButtonComponent.hpp | 79 +++++ .../IAEngine/Components/UIImageComponent.hpp | 54 ++++ .../IAEngine/Components/UILabelComponent.hpp | 8 +- Engine/Src/Inc/IAEngine/Engine.hpp | 14 +- Engine/Src/Inc/IAEngine/Scene.hpp | 6 - Resources/HUD/Button.png | Bin 0 -> 16477 bytes 21 files changed, 516 insertions(+), 241 deletions(-) create mode 100644 Resources/HUD/Button.png diff --git a/Engine/Src/Imp/CPP/Components/CameraComponent.cpp b/Engine/Src/Imp/CPP/Components/CameraComponent.cpp index 89fe9a7..0f31cb2 100644 --- a/Engine/Src/Imp/CPP/Components/CameraComponent.cpp +++ b/Engine/Src/Imp/CPP/Components/CameraComponent.cpp @@ -39,7 +39,7 @@ namespace ia::iae CONST Mat4 *CameraComponent::GetViewMatrix() { - const auto pos = (m_node->GetPosition() + m_positionOffset) * Engine::GetRendererScalingFactor(); + const auto pos = (m_node->GetPosition() + m_positionOffset) * Engine::GetSceneScalingFactor(); m_viewMatrix = glm::lookAtLH(glm::vec3{pos, -2.0f}, {pos, 0.0f}, {0.0f, 1.0f, 0.0f}); return &m_viewMatrix; } diff --git a/Engine/Src/Imp/CPP/Components/TextureComponent.cpp b/Engine/Src/Imp/CPP/Components/TextureComponent.cpp index c8161a7..2c84036 100644 --- a/Engine/Src/Imp/CPP/Components/TextureComponent.cpp +++ b/Engine/Src/Imp/CPP/Components/TextureComponent.cpp @@ -35,15 +35,15 @@ namespace ia::iae m_textureExtent = {t.x, t.y}; m_drawnSize = m_node->GetScale() * m_textureExtent * m_scaleOffset; - Engine::SetRenderState_Texture(m_texture); Engine::SetRenderState_FlippedH(m_isFlippedH); Engine::SetRenderState_FlippedV(m_isFlippedV); Engine::SetRenderState_ColorOverlay(m_colorOverlay); Engine::SetRenderState_TextureOffset(m_textureOffset); Engine::SetRenderState_CameraRelative(m_isCameraRelative); - Engine::SetRenderState_Transform(m_node->GetPosition() + m_positionOffset, m_node->GetScale() * Vec2{t2.x, t2.y} * m_scaleOffset, m_node->GetRotation() + m_rotationOffset); - Engine::DrawGeometry(Engine::GetGeometry_Quad(), m_node->Layer(), m_node->SortIndex()); + Engine::DrawGeometry(Engine::GetGeometry_Quad(), m_texture, m_node->GetPosition() + m_positionOffset, + m_node->GetScale() * Vec2{t2.x, t2.y} * m_scaleOffset, + m_node->GetRotation() + m_rotationOffset, m_node->Layer(), m_node->SortIndex()); } VOID TextureComponent::DebugDraw() diff --git a/Engine/Src/Imp/CPP/Components/UIButtonComponent.cpp b/Engine/Src/Imp/CPP/Components/UIButtonComponent.cpp index e69de29..dbab2d9 100644 --- a/Engine/Src/Imp/CPP/Components/UIButtonComponent.cpp +++ b/Engine/Src/Imp/CPP/Components/UIButtonComponent.cpp @@ -0,0 +1,65 @@ +// IAEngine: 2D Game Engine by IA +// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include + +namespace ia::iae +{ + UIButtonComponent::UIButtonComponent(IN UINode *node) : UIImageComponent(node) + { + } + + VOID UIButtonComponent::Draw() + { + UIImageComponent::Draw(); + + if (m_label.length()) + { + Engine::SetRenderState_FlippedH(false); + Engine::SetRenderState_FlippedV(false); + Engine::SetRenderState_ColorOverlay(m_labelColor); + Engine::SetRenderState_TextureOffset({}); + Engine::SetRenderState_CameraRelative(false); + Engine::DrawText(m_label, m_node->GetPosition() + m_labelPosition, m_node->GetScale() * m_labelSize, + m_node->GetRotation(), 0xFF, 0); + } + } + + VOID UIButtonComponent::DebugDraw() + { + } + + VOID UIButtonComponent::Update() + { + if (Engine::Input_IsPointerDown(m_node->GetPosition(), m_node->GetPosition() + DrawnSize())) + { + if (m_onDownCallback) + m_onDownCallback(); + } + + if (Engine::Input_DidPointerClick(m_node->GetPosition(), m_node->GetPosition() + DrawnSize())) + { + if (m_onClickCallback) + m_onClickCallback(); + } + } + + VOID UIButtonComponent::FixedUpdate() + { + } +} // namespace ia::iae \ No newline at end of file diff --git a/Engine/Src/Imp/CPP/Components/UIImageComponent.cpp b/Engine/Src/Imp/CPP/Components/UIImageComponent.cpp index e69de29..0252dd4 100644 --- a/Engine/Src/Imp/CPP/Components/UIImageComponent.cpp +++ b/Engine/Src/Imp/CPP/Components/UIImageComponent.cpp @@ -0,0 +1,52 @@ +// IAEngine: 2D Game Engine by IA +// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include + +namespace ia::iae +{ + UIImageComponent::UIImageComponent(IN UINode *node) : IUIComponent(node) + { + } + + VOID UIImageComponent::Draw() + { + const auto t = Engine::GetImageOriginalExtent(m_image); + m_drawnSize = {m_node->GetScale() * t.x, m_node->GetScale() * t.y}; + + Engine::SetRenderState_FlippedH(false); + Engine::SetRenderState_FlippedV(false); + Engine::SetRenderState_ColorOverlay({0xFF, 0xFF, 0xFF, 0xFF}); + Engine::SetRenderState_TextureOffset({}); + Engine::SetRenderState_CameraRelative(false); + + Engine::DrawGeometry(Engine::GetGeometry_Quad(), m_image, m_node->GetPosition(), m_drawnSize, m_node->GetRotation(), 0xFF, 0); + } + + VOID UIImageComponent::DebugDraw() + { + } + + VOID UIImageComponent::Update() + { + } + + VOID UIImageComponent::FixedUpdate() + { + } +} // namespace ia::iae \ No newline at end of file diff --git a/Engine/Src/Imp/CPP/Components/UILabelComponent.cpp b/Engine/Src/Imp/CPP/Components/UILabelComponent.cpp index f42ee8b..e229223 100644 --- a/Engine/Src/Imp/CPP/Components/UILabelComponent.cpp +++ b/Engine/Src/Imp/CPP/Components/UILabelComponent.cpp @@ -26,10 +26,9 @@ namespace ia::iae VOID UILabelComponent::Draw() { - Engine::SetRenderState_Texture(0); Engine::SetRenderState_FlippedH(false); Engine::SetRenderState_FlippedV(false); - Engine::SetRenderState_ColorOverlay({0xFF, 0xFF, 0xFF, 0xFF}); + Engine::SetRenderState_ColorOverlay(m_color); Engine::SetRenderState_TextureOffset({}); Engine::SetRenderState_CameraRelative(false); Engine::DrawText(m_label, m_node->GetPosition(), m_node->GetScale(), m_node->GetRotation(), 0xFF, 0); diff --git a/Engine/Src/Imp/CPP/Engine.cpp b/Engine/Src/Imp/CPP/Engine.cpp index 598c285..c54afda 100644 --- a/Engine/Src/Imp/CPP/Engine.cpp +++ b/Engine/Src/Imp/CPP/Engine.cpp @@ -24,10 +24,15 @@ #include +#include + namespace ia::iae { EXTERN SDL_Window *g_windowHandle; - SIZE_T g_resourceNameCounter = 1; + SIZE_T g_resourceNameCounter {1}; + + Vec2 g_sceneScalingFactor{1.0f}; + Vec2 g_sceneDesignViewport{1.0f}; BOOL Engine::IsDebugMode() { @@ -96,8 +101,12 @@ namespace ia::iae { const auto data = ReadBinaryAsset(path); const auto handle = CreateImage(name, data.data(), data.size()); + if (!resizeToWidth || !resizeToHeight) + return handle; const auto extent = GetImageExtent(handle); - return (resizeToWidth && resizeToHeight) ? RescaleImage(handle, {(FLOAT32)resizeToWidth/(FLOAT32)extent.x, (FLOAT32)resizeToHeight/(FLOAT32)extent.y}) : handle; + const auto newHandle = ResourceManager::RescaleImage( + handle, {(FLOAT32) resizeToWidth / (FLOAT32) extent.x, (FLOAT32) resizeToHeight / (FLOAT32) extent.y}, true); + return newHandle; } Handle Engine::CreateSoundFromFile(IN CONST String &name, IN CONST String &path) @@ -108,11 +117,16 @@ namespace ia::iae Handle Engine::RescaleImage(IN CONST String &name, IN Vec2 factor) { - return RescaleImage(GetImage(name), factor); + return ResourceManager::RescaleImage(GetImage(name), factor); } String Engine::GetUniqueResourceName() { return BuildString("__res_", g_resourceNameCounter++); } + + Vec2 Engine::GetSceneScalingFactor() + { + return g_sceneScalingFactor; + } } // namespace ia::iae diff --git a/Engine/Src/Imp/CPP/InputManager.cpp b/Engine/Src/Imp/CPP/InputManager.cpp index a959bd1..fb20d5e 100644 --- a/Engine/Src/Imp/CPP/InputManager.cpp +++ b/Engine/Src/Imp/CPP/InputManager.cpp @@ -24,6 +24,8 @@ namespace ia::iae BOOL InputManager::s_keys[256]; BOOL InputManager::s_prevKeys[256]; Vec2 InputManager::s_pointerPosition{}; + BOOL InputManager::s_pointerState{}; + BOOL InputManager::s_pointerPrevState{}; InputManager::KeyboardGamePadMapping InputManager::s_keyboardGamePadMapping{}; BOOL InputManager::s_buttonA{}; @@ -48,6 +50,7 @@ namespace ia::iae VOID InputManager::OnSDLEvent(IN SDL_Event *event) { + s_pointerPrevState = s_pointerState; memcpy(s_prevKeys, s_keys, sizeof(s_prevKeys)); switch (event->type) { @@ -59,10 +62,21 @@ namespace ia::iae s_keys[event->key.scancode] = false; break; + case SDL_EVENT_FINGER_MOTION: case SDL_EVENT_MOUSE_MOTION: s_pointerPosition = {event->motion.x, event->motion.y}; break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_FINGER_DOWN: + s_pointerState = true; + break; + + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_FINGER_UP: + s_pointerState = false; + break; + default: break; } @@ -78,6 +92,11 @@ namespace ia::iae SDL_StopTextInput(g_windowHandle); } + BOOL InputManager::IsPointerDown() + { + return s_pointerState; + } + Vec2 InputManager::GetPointerPosition() { return s_pointerPosition; @@ -178,16 +197,16 @@ namespace ia::iae { if (s_onScreenGamePadEnabled) { - //Engine::SetRenderState_Texture(0); - //Engine::SetRenderState_FlippedH(false); - //Engine::SetRenderState_FlippedV(false); - //Engine::SetRenderState_ColorOverlay({0xFF, 0xFF, 0xFF, 0xFF}); - //Engine::SetRenderState_TextureOffset({0, 0}); - //Engine::SetRenderState_CameraRelative(false); - //Engine::SetRenderState_Transform({300.0f, 500.0f}, - // {100.0f, 100.0f}, - // 0); - //Engine::DrawGeometry(Engine::GetGeometry_Circle(), 0xFF, 0); + // Engine::SetRenderState_Texture(0); + // Engine::SetRenderState_FlippedH(false); + // Engine::SetRenderState_FlippedV(false); + // Engine::SetRenderState_ColorOverlay({0xFF, 0xFF, 0xFF, 0xFF}); + // Engine::SetRenderState_TextureOffset({0, 0}); + // Engine::SetRenderState_CameraRelative(false); + // Engine::SetRenderState_Transform({300.0f, 500.0f}, + // {100.0f, 100.0f}, + // 0); + // Engine::DrawGeometry(Engine::GetGeometry_Circle(), 0xFF, 0); } } @@ -216,6 +235,18 @@ namespace ia::iae IsKeyDown(s_keyboardGamePadMapping.AxisRight) + IsKeyDown(s_keyboardGamePadMapping.AxisLeft) * -1; } } + + BOOL InputManager::IsPointerDown(IN CONST Vec2 &start, IN CONST Vec2 &end) + { + if(!s_pointerState) return false; + return (s_pointerPosition.x >= start.x) && (s_pointerPosition.x <= end.x) && (s_pointerPosition.y >= start.y) && + (s_pointerPosition.y <= end.y); + } + + BOOL InputManager::DidPointerClick(IN CONST Vec2 &start, IN CONST Vec2 &end) + { + return IsPointerDown(start, end) && !s_pointerPrevState; + } } // namespace ia::iae namespace ia::iae @@ -235,6 +266,21 @@ namespace ia::iae return InputManager::GetPointerPosition(); } + BOOL Engine::Input_IsPointerDown() + { + return InputManager::IsPointerDown(); + } + + BOOL Engine::Input_IsPointerDown(IN CONST Vec2 &start, IN CONST Vec2 &end) + { + return InputManager::IsPointerDown(start, end); + } + + BOOL Engine::Input_DidPointerClick(IN CONST Vec2 &start, IN CONST Vec2 &end) + { + return InputManager::DidPointerClick(start, end); + } + BOOL Engine::Input_IsKeyDown(IN InputKey key) { return InputManager::IsKeyDown(key); diff --git a/Engine/Src/Imp/CPP/InternalEngine.cpp b/Engine/Src/Imp/CPP/InternalEngine.cpp index e0e026b..6b0bc47 100644 --- a/Engine/Src/Imp/CPP/InternalEngine.cpp +++ b/Engine/Src/Imp/CPP/InternalEngine.cpp @@ -42,10 +42,15 @@ namespace ia::iae SDL_Window *g_windowHandle; + EXTERN Vec2 g_sceneDesignViewport; + VOID __Internal_Engine::Initialize() { const auto config = Game_GetConfigRequest(); + g_sceneDesignViewport.x = config->DesignWidth; + g_sceneDesignViewport.y = config->DesignHeight; + IAE_LOG_INFO("Booting IAEngine for ", g_gameName); SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeRight"); diff --git a/Engine/Src/Imp/CPP/Renderer/Renderer.cpp b/Engine/Src/Imp/CPP/Renderer/Renderer.cpp index c5f2fd5..82cde09 100644 --- a/Engine/Src/Imp/CPP/Renderer/Renderer.cpp +++ b/Engine/Src/Imp/CPP/Renderer/Renderer.cpp @@ -39,11 +39,7 @@ namespace ia::iae Renderer::State Renderer::s_state{}; SDL_GPUDevice *Renderer::s_gpuDevice{}; - SDL_GPUTexture *Renderer::s_renderTargetSceneColor{}; - SDL_GPUTexture *Renderer::s_renderTargetDebugDrawColor{}; - Pipeline *Renderer::s_geometryPipeline{}; - Pipeline *Renderer::s_postprocessPipeline{}; Renderer::Geometry *Renderer::s_quadGeometry{}; Renderer::Geometry *Renderer::s_circleGeometry{}; @@ -54,10 +50,12 @@ namespace ia::iae SDL_GPUCommandBuffer *Renderer::s_activeCommandBuffer{}; SDL_GPUColorTargetInfo Renderer::s_colorTargetInfo{}; class CameraComponent *Renderer::s_activeCamera{}; + BOOL Renderer::s_ySortingEnabled{false}; + SDL_Rect Renderer::s_defaultScissor{}; + SDL_GPUViewport Renderer::s_defaultViewport{}; - SDL_Rect Renderer::s_scissor{0, 0, 0, 0}; - SDL_GPUViewport Renderer::s_activeViewport{}; - Vec2 Renderer::s_sceneScaleFactor{1.0f, 1.0f}; + EXTERN Vec2 g_sceneScalingFactor; + EXTERN Vec2 g_sceneDesignViewport; VOID Renderer::Initialize() { @@ -81,7 +79,7 @@ namespace ia::iae SDL_GetWindowSizeInPixels(g_windowHandle, &s_screenWidth, &s_screenHeight); OnScreenResize(s_screenWidth, s_screenHeight); - // Initialize Pipelines + // Initialize Pipeline s_geometryPipeline = new Pipeline( Pipeline::StageDesc{ .SourceData = SHADER_SOURCE_GEOMETRY_VERT, @@ -96,20 +94,6 @@ namespace ia::iae .UniformBufferCount = 1, }, true, true); - s_postprocessPipeline = new Pipeline( - Pipeline::StageDesc{ - .SourceData = SHADER_SOURCE_POSTPROCESS_VERT, - .SourceLength = sizeof(SHADER_SOURCE_POSTPROCESS_VERT), - .SamplerCount = 0, - .UniformBufferCount = 0, - }, - Pipeline::StageDesc{ - .SourceData = SHADER_SOURCE_POSTPROCESS_FRAG, - .SourceLength = sizeof(SHADER_SOURCE_POSTPROCESS_FRAG), - .SamplerCount = 2, - .UniformBufferCount = 0, - }, - false, false); DebugDraw::Initialize(); @@ -256,10 +240,6 @@ namespace ia::iae DebugDraw::Terminate(); delete s_geometryPipeline; - delete s_postprocessPipeline; - - GPUResourceManager::DestroyTexture(s_renderTargetSceneColor); - GPUResourceManager::DestroyTexture(s_renderTargetDebugDrawColor); GPUResourceManager::Terminate(); @@ -267,6 +247,8 @@ namespace ia::iae SDL_DestroyGPUDevice(s_gpuDevice); } + ImDrawData *g_imDrawData{}; + VOID Renderer::BeginFrame() { s_drawEntries.clear(); @@ -274,18 +256,50 @@ namespace ia::iae if (!(s_activeCommandBuffer = SDL_AcquireGPUCommandBuffer(s_gpuDevice))) THROW_UNKNOWN("Failed to acquire SDL GPU command buffer: ", SDL_GetError()); + SDL_GPUTexture *swapChainTexture{}; + if (!SDL_WaitAndAcquireGPUSwapchainTexture(s_activeCommandBuffer, g_windowHandle, &swapChainTexture, + (PUINT32) &s_screenWidth, (PUINT32) &s_screenHeight)) + THROW_UNKNOWN("Failed to acquire SDL GPU Swapchain texture: ", SDL_GetError()); + + if (!swapChainTexture) + return; + + DebugDraw::Render(); + g_imDrawData = ImGui::GetDrawData(); + ImGui_ImplSDLGPU3_PrepareDrawData(g_imDrawData, s_activeCommandBuffer); + const auto clearColor = WorldManager::GetActiveScene()->BackgroundColor().GetAsFloatVec(); s_colorTargetInfo.clear_color = SDL_FColor{clearColor.x, clearColor.y, clearColor.z, 1.0f}; - s_colorTargetInfo.texture = s_renderTargetSceneColor; + s_colorTargetInfo.texture = swapChainTexture; s_activeRenderPass = SDL_BeginGPURenderPass(s_activeCommandBuffer, &s_colorTargetInfo, 1, nullptr); SDL_BindGPUGraphicsPipeline(s_activeRenderPass, s_geometryPipeline->GetHandle()); SDL_PushGPUVertexUniformData(s_activeCommandBuffer, 0, s_activeCamera ? s_activeCamera->GetProjectionMatrix() : &IdentityMatrix, sizeof(Mat4)); + + s_state.Scissor = s_defaultScissor; } VOID Renderer::EndFrame() + { + std::sort(s_drawEntries.begin(), s_drawEntries.end(), [](IN CONST DrawEntry &a, IN CONST DrawEntry &b) { + if (a.Layer != b.Layer) + return a.Layer < b.Layer; + return a.SortIndex < b.SortIndex; + }); + + for (const auto &t : s_drawEntries) + Draw(t); + + ImGui_ImplSDLGPU3_RenderDrawData(g_imDrawData, s_activeCommandBuffer, s_activeRenderPass); + + SDL_EndGPURenderPass(s_activeRenderPass); + + SDL_SubmitGPUCommandBuffer(s_activeCommandBuffer); + } + + VOID Renderer::Draw(IN CONST DrawEntry &t) { #pragma pack(push, 1) @@ -299,78 +313,39 @@ namespace ia::iae #pragma pack(pop) - std::sort(s_drawEntries.begin(), s_drawEntries.end(), [](IN CONST DrawEntry &a, IN CONST DrawEntry &b) { - if (a.Layer != b.Layer) - return a.Layer < b.Layer; - return a.SortIndex < b.SortIndex; - }); + Vec2 position = t.DrawState.Position * g_sceneScalingFactor; + Vec2 scale = t.DrawState.Scale * g_sceneScalingFactor; - for (const auto &t : s_drawEntries) - { - SDL_PushGPUVertexUniformData( - s_activeCommandBuffer, 1, - (s_activeCamera && t.DrawState.CameraRelative) ? s_activeCamera->GetViewMatrix() : &IdentityMatrix, - sizeof(Mat4)); + Mat4 modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3{position.x, position.y, 0}); + modelMatrix = glm::rotate(modelMatrix, t.DrawState.Rotation, glm::vec3(0.0f, 0.0f, 1.0f)); + modelMatrix = glm::scale(modelMatrix, glm::vec3{scale.x, scale.y, 1.0f}); - SDL_PushGPUVertexUniformData(Renderer::s_activeCommandBuffer, 2, &t.DrawState.ModelMatrix, sizeof(Mat4)); + SDL_PushGPUVertexUniformData(s_activeCommandBuffer, 1, + (s_activeCamera && t.DrawState.CameraRelative) ? s_activeCamera->GetViewMatrix() + : &IdentityMatrix, + sizeof(Mat4)); - s_fragmentUniform.ColorOverlay = t.DrawState.ColorOverlay.GetAsFloatVec(); - s_fragmentUniform.FlippedH = t.DrawState.FlippedH; - s_fragmentUniform.FlippedV = t.DrawState.FlippedV; - s_fragmentUniform.TextureOffset = t.DrawState.TextureOffset; - SDL_GPUTextureSamplerBinding textureBinding{.texture = t.DrawState.ActiveTexture, - .sampler = ((t.DrawState.TextureOffset.x <= FLOAT32_EPSILON) && - (t.DrawState.TextureOffset.y <= FLOAT32_EPSILON)) - ? GPUResourceManager::GetSampler_LinearClamp() - : GPUResourceManager::GetSampler_LinearRepeat()}; - SDL_BindGPUFragmentSamplers(s_activeRenderPass, 0, &textureBinding, 1); - SDL_PushGPUFragmentUniformData(s_activeCommandBuffer, 0, &s_fragmentUniform, sizeof(s_fragmentUniform)); + SDL_PushGPUVertexUniformData(Renderer::s_activeCommandBuffer, 2, &modelMatrix, sizeof(Mat4)); - // SDL_SetGPUScissor(s_activeRenderPass, &s_scissor); - // SDL_SetGPUViewport(s_activeRenderPass, &s_activeViewport); + s_fragmentUniform.ColorOverlay = t.DrawState.ColorOverlay.GetAsFloatVec(); + s_fragmentUniform.FlippedH = t.DrawState.FlippedH; + s_fragmentUniform.FlippedV = t.DrawState.FlippedV; + s_fragmentUniform.TextureOffset = t.DrawState.TextureOffset; + SDL_GPUTextureSamplerBinding textureBinding{.texture = t.DrawState.ActiveTexture, + .sampler = ((t.DrawState.TextureOffset.x <= FLOAT32_EPSILON) && + (t.DrawState.TextureOffset.y <= FLOAT32_EPSILON)) + ? GPUResourceManager::GetSampler_LinearClamp() + : GPUResourceManager::GetSampler_LinearRepeat()}; + SDL_BindGPUFragmentSamplers(s_activeRenderPass, 0, &textureBinding, 1); + SDL_PushGPUFragmentUniformData(s_activeCommandBuffer, 0, &s_fragmentUniform, sizeof(s_fragmentUniform)); - SDL_GPUBufferBinding bufferBindings[] = {{.buffer = t.GeometryHandle->VertexBuffer, .offset = 0}, - {.buffer = t.GeometryHandle->IndexBuffer, .offset = 0}}; - SDL_BindGPUVertexBuffers(s_activeRenderPass, 0, bufferBindings, 1); - SDL_BindGPUIndexBuffer(s_activeRenderPass, &bufferBindings[1], SDL_GPU_INDEXELEMENTSIZE_32BIT); - SDL_DrawGPUIndexedPrimitives(s_activeRenderPass, t.GeometryHandle->IndexCount, 1, 0, 0, 0); - } + SDL_SetGPUScissor(s_activeRenderPass, &t.DrawState.Scissor); - SDL_EndGPURenderPass(s_activeRenderPass); - - DebugDraw::Render(); - const auto imDrawData = ImGui::GetDrawData(); - ImGui_ImplSDLGPU3_PrepareDrawData(imDrawData, s_activeCommandBuffer); - s_colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; - s_colorTargetInfo.clear_color = SDL_FColor{0.0f, 0.0f, 0.0f, 0.0f}; - s_colorTargetInfo.texture = s_renderTargetDebugDrawColor; - s_activeRenderPass = SDL_BeginGPURenderPass(s_activeCommandBuffer, &s_colorTargetInfo, 1, nullptr); - ImGui_ImplSDLGPU3_RenderDrawData(imDrawData, s_activeCommandBuffer, s_activeRenderPass); - SDL_EndGPURenderPass(s_activeRenderPass); - - SDL_GPUTexture *swapChainTexture{}; - if (!SDL_WaitAndAcquireGPUSwapchainTexture(s_activeCommandBuffer, g_windowHandle, &swapChainTexture, - (PUINT32) &s_screenWidth, (PUINT32) &s_screenHeight)) - THROW_UNKNOWN("Failed to acquire SDL GPU Swapchain texture: ", SDL_GetError()); - - if (!swapChainTexture) - return; - - s_colorTargetInfo.clear_color = SDL_FColor{1.0f, 1.0f, 1.0f, 1.0f}; - s_colorTargetInfo.load_op = SDL_GPU_LOADOP_CLEAR; - s_colorTargetInfo.texture = swapChainTexture; - - s_activeRenderPass = SDL_BeginGPURenderPass(s_activeCommandBuffer, &s_colorTargetInfo, 1, nullptr); - SDL_BindGPUGraphicsPipeline(s_activeRenderPass, s_postprocessPipeline->GetHandle()); - SDL_GPUTextureSamplerBinding textureBindings[2] = { - {.texture = s_renderTargetSceneColor, .sampler = GPUResourceManager::GetSampler_LinearClamp()}, - {.texture = s_renderTargetDebugDrawColor, .sampler = GPUResourceManager::GetSampler_LinearClamp()}, - }; - SDL_BindGPUFragmentSamplers(s_activeRenderPass, 0, textureBindings, 2); - SDL_DrawGPUPrimitives(s_activeRenderPass, 6, 1, 0, 0); - SDL_EndGPURenderPass(s_activeRenderPass); - - SDL_SubmitGPUCommandBuffer(s_activeCommandBuffer); + SDL_GPUBufferBinding bufferBindings[] = {{.buffer = t.GeometryHandle->VertexBuffer, .offset = 0}, + {.buffer = t.GeometryHandle->IndexBuffer, .offset = 0}}; + SDL_BindGPUVertexBuffers(s_activeRenderPass, 0, bufferBindings, 1); + SDL_BindGPUIndexBuffer(s_activeRenderPass, &bufferBindings[1], SDL_GPU_INDEXELEMENTSIZE_32BIT); + SDL_DrawGPUIndexedPrimitives(s_activeRenderPass, t.GeometryHandle->IndexCount, 1, 0, 0, 0); } VOID Renderer::OnScreenResize(IN INT32 newWidth, IN INT32 newHeight) @@ -378,38 +353,21 @@ namespace ia::iae s_screenWidth = newWidth; s_screenHeight = newHeight; - if (s_renderTargetSceneColor) - GPUResourceManager::DestroyTexture(s_renderTargetSceneColor); - if (s_renderTargetDebugDrawColor) - GPUResourceManager::DestroyTexture(s_renderTargetDebugDrawColor); + s_defaultScissor = {0, 0, newWidth, newHeight}; - s_renderTargetSceneColor = GPUResourceManager::CreateTexture( - SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER, s_screenWidth, s_screenHeight, nullptr, - SDL_GetGPUSwapchainTextureFormat(s_gpuDevice, g_windowHandle)); - s_renderTargetDebugDrawColor = GPUResourceManager::CreateTexture( - SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER, s_screenWidth, s_screenHeight, nullptr, - SDL_GetGPUSwapchainTextureFormat(s_gpuDevice, g_windowHandle)); - - s_scissor = {0, 0, newWidth, newHeight}; - - s_activeViewport.x = 0; - s_activeViewport.y = 0; - s_activeViewport.w = newWidth; - s_activeViewport.h = newHeight; - s_activeViewport.min_depth = 0.0f; - s_activeViewport.max_depth = 1.0f; + s_defaultViewport.x = 0; + s_defaultViewport.y = 0; + s_defaultViewport.w = newWidth; + s_defaultViewport.h = newHeight; + s_defaultViewport.min_depth = 0.0f; + s_defaultViewport.max_depth = 1.0f; if (s_activeCamera) s_activeCamera->SetViewport(newWidth, newHeight); - const auto activeScene = WorldManager::GetActiveScene(); - if (activeScene) - { - const auto sceneViewport = activeScene->Viewport(); - s_sceneScaleFactor = {(FLOAT32) newWidth / (FLOAT32) sceneViewport.x, - (FLOAT32) newHeight / (FLOAT32) sceneViewport.y}; - IAE_LOG_INFO("Updated Scene Scale Factor: (", s_sceneScaleFactor.x, ", ", s_sceneScaleFactor.y, ")"); - } + g_sceneScalingFactor = {(FLOAT32) newWidth / g_sceneDesignViewport.x, + (FLOAT32) newHeight / g_sceneDesignViewport.y}; + IAE_LOG_INFO("Updated Scene Scale Factor: (", g_sceneScalingFactor.x, ", ", g_sceneScalingFactor.y, ")"); } SDL_GPUTextureFormat Renderer::GetRenderTargetFormat() @@ -436,36 +394,38 @@ namespace ia::iae delete handle; } - VOID Renderer::DrawGeometry(IN Geometry *handle, IN UINT8 layer, IN UINT16 sortIndex) + VOID Renderer::DrawGeometry(IN Geometry *handle, IN SDL_GPUTexture *texture, IN Vec2 position, IN Vec2 scale, + IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex) { - const auto t = (UINT16) (sortIndex + ((UINT16) s_state.PositionY * s_state.YSortingEnabled)); + s_state.ActiveTexture = texture; + s_state.Position = position; + s_state.Rotation = rotation; + s_state.Scale = scale; s_drawEntries.pushBack( - DrawEntry{.Layer = layer, .SortIndex = t, .DrawState = s_state, .GeometryHandle = handle}); + DrawEntry{.Layer = layer, + .SortIndex = (UINT16) (sortIndex + ((UINT16) s_state.Position.y * s_ySortingEnabled)), + .DrawState = s_state, + .GeometryHandle = handle}); } VOID Renderer::DrawText(IN CONST String &text, IN Vec2 position, IN FLOAT32 scale, IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex) { + const auto t = (UINT16) (sortIndex + ((UINT16) s_state.Position.y * s_ySortingEnabled)); const auto &font = FontManager::GetFont("Roboto"); - const auto t = (UINT16) (sortIndex + ((UINT16) s_state.PositionY * s_state.YSortingEnabled)); - Vec3 p{}; - Vec3 s{}; for (const auto &c : text) { if (!c) break; + const auto glyph = font.Chars[(UINT8) c]; - p = Vec3(position.x, position.y, 0.0f) + - Vec3(glyph.Bearing.x, glyph.Size.y - glyph.Bearing.y, 0.0f) * scale; - p.y += (16 - glyph.Size.y) * scale; - s = Vec3(scale * glyph.Size.x, scale * glyph.Size.y, 1.0f); - s_state.ActiveTexture = glyph.Texture; - s_state.PositionY = position.y; - s_state.ModelMatrix = glm::translate(glm::mat4(1.0f), p); - s_state.ModelMatrix = glm::rotate(s_state.ModelMatrix, rotation, glm::vec3(0.0f, 0.0f, 1.0f)); - s_state.ModelMatrix = glm::scale(s_state.ModelMatrix, s); + s_state.Position = position + Vec2(glyph.Bearing.x, glyph.Size.y - glyph.Bearing.y) * scale; + s_state.Position.y += (16 - glyph.Size.y) * scale; + s_state.Rotation = rotation; + s_state.Scale = Vec2(scale * glyph.Size.x, scale * glyph.Size.y); + s_drawEntries.pushBack( DrawEntry{.Layer = layer, .SortIndex = t, .DrawState = s_state, .GeometryHandle = s_quadGeometry}); @@ -502,11 +462,6 @@ namespace ia::iae return Renderer::s_activeCamera; } - Vec2 Engine::GetRendererScalingFactor() - { - return Renderer::s_sceneScaleFactor; - } - Handle Engine::CreateGeometry(IN CONST Vector &vertices, IN CONST Vector &indices) { return (Handle) Renderer::CreateGeometry(vertices, indices); @@ -517,9 +472,11 @@ namespace ia::iae Renderer::DestroyGeometry((Renderer::Geometry *) geometry); } - VOID Engine::DrawGeometry(IN Handle handle, IN UINT8 layer, IN UINT16 sortIndex) + VOID Engine::DrawGeometry(IN Handle handle, IN Handle texture, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation, + IN UINT8 layer, IN UINT16 sortIndex) { - Renderer::DrawGeometry((Renderer::Geometry *) handle, layer, sortIndex); + Renderer::DrawGeometry((Renderer::Geometry *) handle, ResourceManager::GetTextureFromImage(texture), position, + scale, rotation, layer, sortIndex); } VOID Engine::DrawText(IN CONST String &text, IN Vec2 position, IN FLOAT32 scale, IN FLOAT32 rotation, @@ -528,10 +485,24 @@ namespace ia::iae Renderer::DrawText(text, position, scale, rotation, layer, sortIndex); } + VOID Engine::DrawQuad(IN Vec2 position, IN Handle texture, IN Vec2 scale, IN FLOAT32 rotation, IN UINT8 layer, + IN UINT16 sortIndex) + { + Renderer::DrawGeometry(Renderer::s_quadGeometry, ResourceManager::GetTextureFromImage(texture), position, scale, + rotation, layer, sortIndex); + } + + VOID Engine::DrawCircle(IN Vec2 position, IN Handle texture, IN FLOAT32 radius, IN FLOAT32 rotation, IN UINT8 layer, + IN UINT16 sortIndex) + { + Renderer::DrawGeometry(Renderer::s_circleGeometry, ResourceManager::GetTextureFromImage(texture), position, + {radius, radius}, rotation, layer, sortIndex); + } + VOID Engine::SetRenderState_Scissor(IN IVec4 rect) { - Renderer::s_scissor = rect.z ? SDL_Rect{0, 0, Renderer::s_screenWidth, Renderer::s_screenHeight} - : SDL_Rect{rect.x, rect.y, rect.z, rect.w}; + Renderer::s_state.Scissor = rect.z ? SDL_Rect{0, 0, Renderer::s_screenWidth, Renderer::s_screenHeight} + : SDL_Rect{rect.x, rect.y, rect.z, rect.w}; } VOID Engine::SetRenderState_FlippedH(IN BOOL value) @@ -559,35 +530,9 @@ namespace ia::iae Renderer::s_state.CameraRelative = value; } - VOID Engine::SetRenderState_Texture(IN Handle image) - { - Renderer::s_state.ActiveTexture = ResourceManager::GetTextureFromImage(image); - } - - VOID Engine::SetRenderState_Transform(IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation) - { - position *= Renderer::s_sceneScaleFactor; - scale *= Renderer::s_sceneScaleFactor; - - Renderer::s_state.PositionY = position.y; - - Renderer::s_state.ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3{position.x, position.y, 0}); - Renderer::s_state.ModelMatrix = - glm::rotate(Renderer::s_state.ModelMatrix, rotation, glm::vec3(0.0f, 0.0f, 1.0f)); - Renderer::s_state.ModelMatrix = glm::scale(Renderer::s_state.ModelMatrix, glm::vec3{scale.x, scale.y, 1.0f}); - } - - VOID Engine::SetRenderState_TransformUI(IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation) - { - Renderer::s_state.ModelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3{position.x, position.y, 0}); - Renderer::s_state.ModelMatrix = - glm::rotate(Renderer::s_state.ModelMatrix, rotation, glm::vec3(0.0f, 0.0f, 1.0f)); - Renderer::s_state.ModelMatrix = glm::scale(Renderer::s_state.ModelMatrix, glm::vec3{scale.x, scale.y, 1.0f}); - } - VOID Engine::SetRenderState_YSortingEnabled(IN BOOL value) { - Renderer::s_state.YSortingEnabled = value; + Renderer::s_ySortingEnabled = value; } Handle Engine::GetGeometry_Quad() diff --git a/Engine/Src/Imp/CPP/ResourceManager.cpp b/Engine/Src/Imp/CPP/ResourceManager.cpp index 46363d9..b81d085 100644 --- a/Engine/Src/Imp/CPP/ResourceManager.cpp +++ b/Engine/Src/Imp/CPP/ResourceManager.cpp @@ -61,8 +61,9 @@ namespace ia::iae Handle ResourceManager::CreateImage(IN CONST String &name, IN PCUINT8 rgbaData, IN INT32 width, IN INT32 height) { - const auto texture = GPUResourceManager::CreateTexture(SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, width, height, rgbaData, - SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, true); + const auto texture = + GPUResourceManager::CreateTexture(SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, width, + height, rgbaData, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, true); s_imageHandles.pushBack(ImageResource{.OriginalWidth = width, .OriginalHeight = height, .OriginalPixelData = new UINT8[width * height * 4], @@ -137,7 +138,7 @@ namespace ia::iae return {t.OriginalWidth, t.OriginalHeight}; } - Handle ResourceManager::RescaleImage(IN Handle image, IN Vec2 factor) + Handle ResourceManager::RescaleImage(IN Handle image, IN Vec2 factor, IN BOOL makeOriginal) { if (!s_imageHandles[image].OriginalPixelData) return image; @@ -145,21 +146,29 @@ namespace ia::iae const auto newWidth = (INT32) (s_imageHandles[image].OriginalWidth * factor.x); const auto newHeight = (INT32) (s_imageHandles[image].OriginalHeight * factor.y); - if (!newWidth || !newHeight || - ((newWidth <= s_imageHandles[image].OriginalWidth) && (newHeight <= s_imageHandles[image].OriginalHeight))) + if (!newWidth || !newHeight) return image; const auto newPixelData = stbir_resize_uint8_linear(s_imageHandles[image].OriginalPixelData, s_imageHandles[image].OriginalWidth, s_imageHandles[image].OriginalHeight, s_imageHandles[image].OriginalWidth * 4, nullptr, newWidth, newHeight, newWidth * 4, stbir_pixel_layout::STBIR_RGBA); - const auto texture = GPUResourceManager::CreateTexture(SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, newWidth, newHeight, - newPixelData, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, true); + const auto texture = GPUResourceManager::CreateTexture( + SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET, newWidth, newHeight, newPixelData, + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, true); GPUResourceManager::DestroyTexture(s_imageHandles[image].Handle); s_imageHandles[image].Handle = texture; s_imageHandles[image].Width = newWidth; s_imageHandles[image].Height = newHeight; - free(newPixelData); + if (!makeOriginal) + { + free(newPixelData); + return image; + } + free(s_imageHandles[image].OriginalPixelData); + s_imageHandles[image].OriginalPixelData = newPixelData; + s_imageHandles[image].OriginalWidth = newWidth; + s_imageHandles[image].OriginalHeight = newHeight; return image; } diff --git a/Engine/Src/Imp/CPP/SceneManager.cpp b/Engine/Src/Imp/CPP/SceneManager.cpp index 2ab3b88..303ff1f 100644 --- a/Engine/Src/Imp/CPP/SceneManager.cpp +++ b/Engine/Src/Imp/CPP/SceneManager.cpp @@ -101,9 +101,6 @@ namespace ia::iae { auto t = propRoot.child("Extent"); scene->Extent() = Vec2{t.attribute("width").as_float(), t.attribute("height").as_float()}; - - t = propRoot.child("Viewport"); - scene->Viewport() = Vec2{t.attribute("width").as_float(), t.attribute("height").as_float()}; } // Process Nodes diff --git a/Engine/Src/Imp/CPP/UI.cpp b/Engine/Src/Imp/CPP/UI.cpp index 37cd05b..d2ed7aa 100644 --- a/Engine/Src/Imp/CPP/UI.cpp +++ b/Engine/Src/Imp/CPP/UI.cpp @@ -589,15 +589,13 @@ namespace ia::iae Rml::TextureHandle texture) { - Engine::SetRenderState_Texture((Handle) texture); Engine::SetRenderState_FlippedH(false); Engine::SetRenderState_FlippedV(false); Engine::SetRenderState_ColorOverlay({255, 255, 255, 255}); Engine::SetRenderState_TextureOffset({0, 0}); Engine::SetRenderState_CameraRelative(false); - Engine::SetRenderState_Transform({translation.x, translation.y}, {1.0f, 1.0f}, 0); - Engine::DrawGeometry((Handle) geometry, 0xFF, 0); + Engine::DrawGeometry((Handle) geometry, (Handle) texture, {translation.x, translation.y}, {1.0f, 1.0f}, 0, 0xFF, 0); } void RmlUIRenderInterface::ReleaseGeometry(Rml::CompiledGeometryHandle geometry) diff --git a/Engine/Src/Imp/HPP/InputManager.hpp b/Engine/Src/Imp/HPP/InputManager.hpp index b9868c3..feac4ab 100644 --- a/Engine/Src/Imp/HPP/InputManager.hpp +++ b/Engine/Src/Imp/HPP/InputManager.hpp @@ -41,7 +41,10 @@ namespace ia::iae STATIC VOID SwitchModeToText(); STATIC VOID SwitchModeToAction(); + STATIC BOOL IsPointerDown(); STATIC Vec2 GetPointerPosition(); + STATIC BOOL IsPointerDown(IN CONST Vec2& start, IN CONST Vec2& end); + STATIC BOOL DidPointerClick(IN CONST Vec2& start, IN CONST Vec2& end); STATIC BOOL IsKeyDown(IN InputKey key); STATIC BOOL WasKeyPressed(IN InputKey key); @@ -74,6 +77,8 @@ namespace ia::iae STATIC INT16 s_horizontalAxis; STATIC BOOL s_keys[256]; STATIC BOOL s_prevKeys[256]; + STATIC BOOL s_pointerState; + STATIC BOOL s_pointerPrevState; STATIC Vec2 s_pointerPosition; STATIC BOOL s_onScreenGamePadEnabled; STATIC BOOL s_keyboardGamePadEnabled; diff --git a/Engine/Src/Imp/HPP/Renderer/Renderer.hpp b/Engine/Src/Imp/HPP/Renderer/Renderer.hpp index 30460ac..22863ec 100644 --- a/Engine/Src/Imp/HPP/Renderer/Renderer.hpp +++ b/Engine/Src/Imp/HPP/Renderer/Renderer.hpp @@ -16,17 +16,14 @@ #pragma once -#include #include +#include namespace ia::iae { class Renderer { public: - STATIC CONSTEXPR FLOAT32 MIN_DEPTH = -2097152.0f; - STATIC CONSTEXPR FLOAT32 MAX_DEPTH = 2097152.0f; - struct Geometry { INT32 IndexCount{}; @@ -39,12 +36,16 @@ namespace ia::iae BOOL FlippedH{false}; BOOL FlippedV{false}; BOOL CameraRelative{true}; - BOOL YSortingEnabled{false}; Color ColorOverlay{}; Vec2 TextureOffset{0.0f, 0.0f}; - SDL_GPUTexture* ActiveTexture{nullptr}; - Mat4 ModelMatrix{1.0f}; - FLOAT32 PositionY{}; + + Vec2 Position{}; + Vec2 Scale{}; + FLOAT32 Rotation; + + SDL_Rect Scissor{}; + + SDL_GPUTexture *ActiveTexture{nullptr}; }; struct DrawEntry @@ -52,7 +53,7 @@ namespace ia::iae UINT8 Layer{}; UINT16 SortIndex{}; State DrawState{}; - Geometry* GeometryHandle{}; + Geometry *GeometryHandle{}; }; public: @@ -67,11 +68,13 @@ namespace ia::iae STATIC VOID OnScreenResize(IN INT32 newWidth, IN INT32 newHeight); public: - STATIC Geometry* CreateGeometry(IN CONST Vector &vertices, IN CONST Vector &indices); - STATIC VOID DestroyGeometry(IN Geometry* handle); + STATIC Geometry *CreateGeometry(IN CONST Vector &vertices, IN CONST Vector &indices); + STATIC VOID DestroyGeometry(IN Geometry *handle); - STATIC VOID DrawGeometry(IN Geometry* handle, IN UINT8 layer, IN UINT16 sortIndex); - STATIC VOID DrawText(IN CONST String& text, IN Vec2 position, IN FLOAT32 scale, IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex); + STATIC VOID DrawGeometry(IN Geometry *handle, IN SDL_GPUTexture* texture, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation, + IN UINT8 layer, IN UINT16 sortIndex); + STATIC VOID DrawText(IN CONST String &text, IN Vec2 position, IN FLOAT32 scale, IN FLOAT32 rotation, + IN UINT8 layer, IN UINT16 sortIndex); STATIC SDL_GPUTextureFormat GetRenderTargetFormat(); @@ -80,25 +83,25 @@ namespace ia::iae return s_gpuDevice; } + private: + STATIC VOID Draw(IN CONST DrawEntry &entity); + private: STATIC State s_state; STATIC INT32 s_screenWidth; STATIC INT32 s_screenHeight; STATIC SDL_GPUDevice *s_gpuDevice; - STATIC SDL_GPUTexture *s_renderTargetSceneColor; - STATIC SDL_GPUTexture *s_renderTargetDebugDrawColor; - STATIC Pipeline* s_geometryPipeline; - STATIC Pipeline* s_postprocessPipeline; - STATIC Geometry* s_quadGeometry; - STATIC Geometry* s_circleGeometry; + STATIC Pipeline *s_geometryPipeline; + STATIC Geometry *s_quadGeometry; + STATIC Geometry *s_circleGeometry; STATIC Vector s_drawEntries; - STATIC SDL_GPURenderPass* s_activeRenderPass; - STATIC SDL_GPUCommandBuffer* s_activeCommandBuffer; + STATIC SDL_GPURenderPass *s_activeRenderPass; + STATIC SDL_GPUCommandBuffer *s_activeCommandBuffer; STATIC SDL_GPUColorTargetInfo s_colorTargetInfo; STATIC class CameraComponent *s_activeCamera; - STATIC SDL_Rect s_scissor; - STATIC SDL_GPUViewport s_activeViewport; - STATIC Vec2 s_sceneScaleFactor; + STATIC BOOL s_ySortingEnabled; + STATIC SDL_Rect s_defaultScissor; + STATIC SDL_GPUViewport s_defaultViewport; friend class Engine; }; diff --git a/Engine/Src/Imp/HPP/ResourceManager.hpp b/Engine/Src/Imp/HPP/ResourceManager.hpp index f88969e..529a7b1 100644 --- a/Engine/Src/Imp/HPP/ResourceManager.hpp +++ b/Engine/Src/Imp/HPP/ResourceManager.hpp @@ -53,7 +53,7 @@ namespace ia::iae STATIC IVec2 GetImageExtent(IN Handle image); STATIC IVec2 GetImageOriginalExtent(IN Handle image); STATIC VOID RescaleAllImages(IN Vec2 factor); - STATIC Handle RescaleImage(IN Handle image, IN Vec2 factor); + STATIC Handle RescaleImage(IN Handle image, IN Vec2 factor, IN BOOL makeOriginal = false); STATIC Handle CombineImages(IN CONST Vector &images, IN INT32 unitWidth, IN INT32 unitHeight, IN INT32 unitCountX, IN INT32 unitCountY); diff --git a/Engine/Src/Inc/IAEngine/Components/UIButtonComponent.hpp b/Engine/Src/Inc/IAEngine/Components/UIButtonComponent.hpp index e69de29..69056dc 100644 --- a/Engine/Src/Inc/IAEngine/Components/UIButtonComponent.hpp +++ b/Engine/Src/Inc/IAEngine/Components/UIButtonComponent.hpp @@ -0,0 +1,79 @@ +// IAEngine: 2D Game Engine by IA +// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +namespace ia::iae +{ + class UIButtonComponent : public UIImageComponent + { + public: + UIButtonComponent(IN UINode *node); + + String &Label() + { + return m_label; + } + + CONST String &Label() CONST + { + return m_label; + } + + Vec2 &LabelPosition() + { + return m_labelPosition; + } + + FLOAT32 &LabelSize() + { + return m_labelSize; + } + + Color &LabelColor() + { + return m_labelColor; + } + + public: + VOID SetOnDownCallback(IN std::function callback) + { + m_onDownCallback = callback; + } + + VOID SetOnClickCallback(IN std::function callback) + { + m_onClickCallback = callback; + } + + public: + VOID Draw(); + VOID DebugDraw(); + + VOID Update(); + VOID FixedUpdate(); + + private: + String m_label{}; + Vec2 m_labelPosition{}; + FLOAT32 m_labelSize{1.0f}; + Color m_labelColor{0xFF, 0xFF, 0xFF, 0xFF}; + std::function m_onDownCallback; + std::function m_onClickCallback; + }; +} // namespace ia::iae \ No newline at end of file diff --git a/Engine/Src/Inc/IAEngine/Components/UIImageComponent.hpp b/Engine/Src/Inc/IAEngine/Components/UIImageComponent.hpp index e69de29..0565719 100644 --- a/Engine/Src/Inc/IAEngine/Components/UIImageComponent.hpp +++ b/Engine/Src/Inc/IAEngine/Components/UIImageComponent.hpp @@ -0,0 +1,54 @@ +// IAEngine: 2D Game Engine by IA +// Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +namespace ia::iae +{ + class UIImageComponent : public IUIComponent + { + public: + UIImageComponent(IN UINode *node); + + Handle &Image() + { + return m_image; + } + + CONST Handle &Image() CONST + { + return m_image; + } + + CONST Vec2 &DrawnSize() CONST + { + return m_drawnSize; + } + + public: + VOID Draw(); + VOID DebugDraw(); + + VOID Update(); + VOID FixedUpdate(); + + private: + Handle m_image{}; + Vec2 m_drawnSize{}; + }; +} // namespace ia::iae \ No newline at end of file diff --git a/Engine/Src/Inc/IAEngine/Components/UILabelComponent.hpp b/Engine/Src/Inc/IAEngine/Components/UILabelComponent.hpp index 1dd8a11..180d7c6 100644 --- a/Engine/Src/Inc/IAEngine/Components/UILabelComponent.hpp +++ b/Engine/Src/Inc/IAEngine/Components/UILabelComponent.hpp @@ -23,7 +23,7 @@ namespace ia::iae class UILabelComponent : public IUIComponent { public: - UILabelComponent(IN UINode* node); + UILabelComponent(IN UINode *node); String &Label() { @@ -35,6 +35,11 @@ namespace ia::iae return m_label; } + struct Color &Color() + { + return m_color; + } + public: VOID Draw(); VOID DebugDraw(); @@ -44,5 +49,6 @@ namespace ia::iae private: String m_label{}; + struct Color m_color{0xFF, 0xFF, 0xFF, 0xFF}; }; } // namespace ia::iae \ No newline at end of file diff --git a/Engine/Src/Inc/IAEngine/Engine.hpp b/Engine/Src/Inc/IAEngine/Engine.hpp index 3eb62b4..f919da9 100644 --- a/Engine/Src/Inc/IAEngine/Engine.hpp +++ b/Engine/Src/Inc/IAEngine/Engine.hpp @@ -47,10 +47,12 @@ namespace ia::iae STATIC Handle GetGeometry_Circle(); STATIC Handle CreateGeometry(IN CONST Vector &vertices, IN CONST Vector &indices); STATIC VOID DestroyGeometry(IN Handle geometry); - STATIC VOID DrawGeometry(IN Handle handle, IN UINT8 layer, IN UINT16 sortIndex); STATIC IVec2 GetDisplayExtent(); STATIC FLOAT32 GetDisplayAspectRatio(); STATIC VOID ResizeDisplay(IN INT32 newWidth, IN INT32 newHeight); + STATIC VOID DrawGeometry(IN Handle geometry, IN Handle texture, IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex); + STATIC VOID DrawQuad(IN Vec2 position, IN Handle texture, IN Vec2 scale, IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex); + STATIC VOID DrawCircle(IN Vec2 position, IN Handle texture, IN FLOAT32 radius, IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex); STATIC VOID DrawText(IN CONST String& text, IN Vec2 position, IN FLOAT32 scale, IN FLOAT32 rotation, IN UINT8 layer, IN UINT16 sortIndex); // Renderer State Functions @@ -61,10 +63,7 @@ namespace ia::iae STATIC VOID SetRenderState_YSortingEnabled(IN BOOL value); STATIC VOID SetRenderState_ColorOverlay(IN Color color); STATIC VOID SetRenderState_CameraRelative(IN BOOL value); - STATIC VOID SetRenderState_Texture(IN Handle image); - STATIC VOID SetRenderState_Transform(IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation); - STATIC VOID SetRenderState_TransformUI(IN Vec2 position, IN Vec2 scale, IN FLOAT32 rotation); - STATIC Vec2 GetRendererScalingFactor(); + STATIC Vec2 GetSceneScalingFactor(); // Debug Draw Functions STATIC VOID DebugDraw_SetColor(IN Color color); @@ -107,7 +106,12 @@ namespace ia::iae // Input Functions STATIC VOID Input_SwitchModeToText(); STATIC VOID Input_SwitchModeToAction(); + + STATIC BOOL Input_IsPointerDown(); STATIC Vec2 Input_GetPointerPosition(); + STATIC BOOL Input_DidPointerClick(IN CONST Vec2& start, IN CONST Vec2& end); + STATIC BOOL Input_IsPointerDown(IN CONST Vec2& start, IN CONST Vec2& end); + STATIC BOOL Input_IsKeyDown(IN InputKey key); STATIC BOOL Input_WasKeyPressed(IN InputKey key); STATIC BOOL Input_WasKeyReleased(IN InputKey key); diff --git a/Engine/Src/Inc/IAEngine/Scene.hpp b/Engine/Src/Inc/IAEngine/Scene.hpp index 2ffbdcd..d2ba591 100644 --- a/Engine/Src/Inc/IAEngine/Scene.hpp +++ b/Engine/Src/Inc/IAEngine/Scene.hpp @@ -48,11 +48,6 @@ namespace ia::iae return m_extent; } - IVec2 &Viewport() - { - return m_viewport; - } - Color &BackgroundColor() { return m_backgroundColor; @@ -62,7 +57,6 @@ namespace ia::iae String m_uiMarkup{}; String m_uiMarkupStyles{}; IVec2 m_extent{100, 100}; - IVec2 m_viewport{100, 100}; CameraComponent *m_camera{}; Color m_backgroundColor{0, 0, 0, 255}; Map> m_nodes; diff --git a/Resources/HUD/Button.png b/Resources/HUD/Button.png new file mode 100644 index 0000000000000000000000000000000000000000..58c42a32c65d27eef8fbebdd5add7407c09db5b6 GIT binary patch literal 16477 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 DOm`XS literal 0 HcmV?d00001