diff --git a/.vs/CMakeWorkspaceSettings.json b/.vs/CMakeWorkspaceSettings.json new file mode 100644 index 0000000..05acd8c --- /dev/null +++ b/.vs/CMakeWorkspaceSettings.json @@ -0,0 +1,3 @@ +{ + "enableCMake": true +} \ No newline at end of file diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json index 8f0d733..22a4bcd 100644 --- a/.vs/ProjectSettings.json +++ b/.vs/ProjectSettings.json @@ -1,3 +1,3 @@ { - "CurrentProjectSetting": "x64-Debug" + "CurrentProjectSetting": "x64-Release" } \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json index d36133c..c802e0b 100644 --- a/.vs/VSWorkspaceState.json +++ b/.vs/VSWorkspaceState.json @@ -1,6 +1,5 @@ { "DevContainersInfoBarHidden": true, - "ActiveTargetSystem": "Local Machine", "OutputFoldersPerTargetSystem": { "Local Machine": [ "out\\build\\x64-Debug", @@ -11,6 +10,23 @@ "WSL: Arch": [ "out\\build\\x64-Debug", "out\\install\\x64-Debug" + ], + "WSL: Ubuntu-22.04": [ + "out\\build\\x64-Debug", + "out\\install\\x64-Debug", + "out\\build\\x64-Release", + "out\\install\\x64-Release" ] - } + }, + "ActiveTargetSystem": "Local Machine", + "ExpandedNodes": [ + "\\sandbox", + "\\sandbox\\src", + "\\src", + "\\src\\Renderer", + "\\src\\Scene", + "\\src\\Scripting" + ], + "SelectedNode": "\\CMakeLists.txt", + "PreviewInSolutionExplorer": false } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ef7865..92290b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(ENGINE_SOURCES src/Renderer/Buffer.cpp src/Renderer/OpenGLBuffer.cpp src/Renderer/Renderer.cpp + src/Renderer/Renderer2D.cpp src/Shader/Shader.cpp src/ImGui/ImGuiLayer.cpp src/Renderer/VertexArray.cpp @@ -84,6 +85,8 @@ set(ENGINE_SOURCES external/stb_image/src/stb_image.cpp src/Scripting/LuaScriptSystem.cpp src/Scene/Scene.cpp + src/Shader/ShaderLibrary.cpp + src/Shader/ShaderFactory.cpp ${IMGUI_SOURCES} ) @@ -94,14 +97,14 @@ target_include_directories(voxel-engine PUBLIC ${CMAKE_SOURCE_DIR}/src ${IMGUI_DIR} ${IMGUI_DIR}/backends + ${CMAKE_SOURCE_DIR}/build/vcpkg_installed/x86-windows/include ${CMAKE_SOURCE_DIR}/external/stb_image/src ${CMAKE_SOURCE_DIR}/external/json ${CMAKE_SOURCE_DIR}/external/glad/include + ${CMAKE_SOURCE_DIR}/build/vcpkg_installed/x86-windows/include ${CMAKE_SOURCE_DIR}/external/sol2 ) -include_directories("/usr/include/lua5.4") - target_link_libraries(voxel-engine PUBLIC glfw OpenGL::GL diff --git a/assets/shaders/batch_renderer_2d.frag b/assets/shaders/batch_renderer_2d.frag new file mode 100644 index 0000000..195f00e --- /dev/null +++ b/assets/shaders/batch_renderer_2d.frag @@ -0,0 +1,16 @@ +#version 330 core + +in vec4 v_Color; +in vec2 v_TexCoord; +in float v_TexIndex; +in float v_TilingFactor; + +uniform sampler2D u_Textures[32]; + +out vec4 FragColor; + +void main() { + vec4 texColor = v_Color; + texColor *= texture(u_Textures[int(v_TexIndex)], v_TexCoord * v_TilingFactor); + FragColor = texColor; +} diff --git a/assets/shaders/batch_renderer_2d.vert b/assets/shaders/batch_renderer_2d.vert new file mode 100644 index 0000000..8e55c90 --- /dev/null +++ b/assets/shaders/batch_renderer_2d.vert @@ -0,0 +1,22 @@ +#version 330 core + +layout (location = 0) in vec3 a_Position; +layout (location = 1) in vec4 a_Color; +layout (location = 2) in vec2 a_TexCoord; +layout (location = 3) in float a_TexIndex; +layout (location = 4) in float a_TilingFactor; + +uniform mat4 u_ViewProjection; + +out vec4 v_Color; +out vec2 v_TexCoord; +out float v_TexIndex; +out float v_TilingFactor; + +void main() { + v_Color = a_Color; + v_TexCoord = a_TexCoord; + v_TexIndex = a_TexIndex; + v_TilingFactor = a_TilingFactor; + gl_Position = u_ViewProjection * vec4(a_Position, 1.0); +} diff --git a/imgui.ini b/imgui.ini index 3324961..d107605 100644 --- a/imgui.ini +++ b/imgui.ini @@ -35,7 +35,7 @@ Collapsed=0 [Window][Stats] Pos=35,54 -Size=300,357 +Size=300,426 Collapsed=0 DockId=0x00000002,0 @@ -51,8 +51,8 @@ Collapsed=0 DockId=0x00000003,0 [Window][Recent Events] -Pos=1098,15 -Size=180,265 +Pos=528,38 +Size=367,265 Collapsed=0 DockId=0x00000006,0 @@ -63,7 +63,17 @@ Collapsed=0 DockId=0x00000005,0 [Window][Lua Console] -Pos=388,23 +Pos=377,394 +Size=520,600 +Collapsed=0 + +[Window][Renderer Stats] +Pos=8,51 +Size=158,106 +Collapsed=0 + +[Window][LuaConsole##Window] +Pos=403,332 Size=520,600 Collapsed=0 @@ -75,7 +85,7 @@ Column 0 Sort=0v DockNode ID=0x00000001 Pos=35,54 Size=300,426 Split=Y DockNode ID=0x00000002 Parent=0x00000001 SizeRef=300,289 Selected=0x590DF097 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=300,54 Selected=0xAC3EC402 -DockNode ID=0x00000004 Pos=911,15 Size=367,265 Split=X +DockNode ID=0x00000004 Pos=528,38 Size=367,265 Split=X DockNode ID=0x00000005 Parent=0x00000004 SizeRef=75,236 Selected=0x5B89BA50 DockNode ID=0x00000006 Parent=0x00000004 SizeRef=73,236 Selected=0x1A7C4B8A diff --git a/sandbox/assets/scripts/engine.lua b/sandbox/assets/scripts/engine.lua index ecea499..c5e3071 100644 --- a/sandbox/assets/scripts/engine.lua +++ b/sandbox/assets/scripts/engine.lua @@ -1,50 +1,77 @@ +---* NOTE: all numbers in Lua are floats ---@meta ---@class engine +--- Terrain ---@field setTerrainHeight fun(height: number) # Sets the base height for terrain generation ---@field generateTerrainMesh fun(seed: number) # Regenerates terrain with given seed ----@field setClearColor fun(r: number, g: number, b: number, a: number) # Sets the renderer clear color ----@field isKeyPressed fun(keycode: number): boolean # Checks if a key is pressed ----@field getMousePosition fun(): number, number # Gets current mouse position +--- Logging ---@field trace fun(message: string) # Logs a trace message ---@field log fun(message: string) # Logs an info message ---@field warn fun(message: string) # Logs an warn message ---@field error fun(message: string) # Logs an error message ---@field fatal fun(message: string) # Logs an error message and terminates the application +--- Debug ---@field profileFunction fun(name: string) # Profiles the current function ---@field profileScope fun(name: string) # Profiles the current function & assigns a name +--- Script System ---@field loadScript fun(filepath: string): boolean # Loads and executes a Lua script file ----@field setCameraPosition fun(x: number, y: number, z: number) # Sets camera position ----@field setCameraRotation fun(pitch: number, yaw: number) # Sets camera rotation ----@field getCameraPosition fun(): number, number, number # Gets camera position ----@field moveCameraForward fun(deltaTime: number) # Move camera forward ----@field moveCameraBackward fun(deltaTime: number) # Move camera backward ----@field moveCameraLeft fun(deltaTime: number) # Move camera left ----@field moveCameraRight fun(deltaTime: number) # Move camera right ----@field moveCameraUp fun(deltaTime: number) # Move camera up ----@field moveCameraDown fun(deltaTime: number) # Move camera down ----@field rotateCameraWithMouse fun(xOffset: number, yOffset: number, sensitivity: number) # Rotate camera with mouse +--- Window ---@field setViewport fun(x: number, y: number, width: number, height: number) # Sets the viewport dimensions +---@field setClearColor fun(r: number, g: number, b: number, a: number) # Sets the renderer clear color +---Camera Setup ---@field setCameraType fun(type: string) # Sets the camera type ("orthographic" or "perspective") ---@field getCameraType fun(): string # Gets the current camera type +--- Input ---@field isMouseButtonPressed fun(button: number): boolean # Checks if a mouse button is pressed +---@field isKeyPressed fun(keycode: number): boolean # Checks if a key is pressed ---@field setMouseSensitivity fun(sensitivity: number) # Sets mouse sensitivity ---@field getMouseSensitivity fun(): number # Gets current mouse sensitivity +---@field getMousePosition fun(): number, number # Gets current mouse position +--- Movement ---@field setMovementSpeed fun(speed: number) # Sets movement speed ---@field getMovementSpeed fun(): number # Gets current movement speed +--- Camera Properties ---@field toggleCameraControls fun() # Toggles camera controls on/off ---@field toggleMovementLock fun() # Toggles movement lock ---@field toggleSmoothCamera fun() # Toggles smooth camera movement +--- Camera +---@field setCameraPosition fun(x: number, y: number, z: number) # Sets camera position +---@field setCameraRotation fun(pitch: number, yaw: number) # Sets camera rotation +---@field getCameraPosition fun(): number, number, number # Gets camera position +---@field moveCameraForward fun(deltaTime: number) # Move camera forward +---@field moveCameraBackward fun(deltaTime: number) # Move camera backward +---@field moveCameraLeft fun(deltaTime: number) # Move camera left +---@field moveCameraRight fun(deltaTime: number) # Move camera right +---@field moveCameraUp fun(deltaTime: number) # Move camera up +---@field moveCameraDown fun(deltaTime: number) # Move camera down +---@field rotateCameraWithMouse fun(xOffset: number, yOffset: number, sensitivity: number) # Rotate camera with mouse +--- ImGui Controls ---@field showTransformControls fun(show: boolean) # Shows/hides transform controls window ---@field showProfiler fun(show: boolean) # Shows/hides profiler window ---@field showRendererSettings fun(show: boolean) # Shows/hides renderer settings window ---@field showEventDebugger fun(show: boolean) # Shows/hides event debugger window ---@field showTerrainControls fun(show: boolean) # Shows/hides terrain controls window ---@field showFPSCounter fun(show: boolean) # Shows/hides FPS counter +--- Scene Management ---@field createScene fun(name: string): boolean # Creates a new scene with the given name ---@field setActiveScene fun(name: string): boolean # Sets the active scene by name ---@field deleteScene fun(name: string): boolean # Deletes a scene by name ---@field getActiveSceneName fun(): string # Gets the name of the active scene +--- Render System +---@field setRenderType fun(type: string) # Sets the renderer (2D or 3D) +---@field getRenderType fun(): string # Gets the renderer type (2D or 3D) +---@field getObject fun(name: string) # Gets an object by name +---@field is3D fun(): boolean # Checks if the current scene is 3D +--- 2D Render System +---@field renderer2DInitialize fun() # Creates a 2D renderer instance +---@field renderer2DBeginScene fun() # Begins a scene +---@field renderer2DEndScene fun() # Ends a scene +---@field drawQuad fun(x: number, y: number, width: number, height: number, r: number, g: number, b: number, a: number) # Draw a single quad +---@field drawTexturedQuad fun(x: number, y: number, width: number, height: number, texture, tiling_factor: number) # Draw a single quad with a texture +---@field createCheckerTexture fun() # Create a checkered texture from quads +--- 3D Render System +---@field createCube fun(scene: string) # Creates a cube in the scene engine = {} -- Key code constants @@ -246,3 +273,37 @@ function engine.deleteScene(name) end ---Gets the name of the active scene ---@return string name The name of the active scene (empty string if no active scene) function engine.getActiveSceneName() end + +--- Sets the renderer type (2D or 3D) +--- @param type string 2D or 3D +function setRenderType(type) end + +--- Gets the renderer type (2D or 3D) +--- @return type string +function getRenderType() end + +--- Starts a scene with camera as context +function engine.renderer2d_begin_scene() end + +--- Ends the current scene +function engine.renderer2d_end_scene() end + +--- Draws a single quad +--- @param x number The x coordinate of the quad +--- @param y number The y coordinate of the quad +--- @param width number The width of the quad +--- @param height number The height of the quad +--- @param r number The red value of the quad +--- @param g number The greem value of the quad +--- @param b number The blue value of the quad +--- @param a number The alpha value of the quad +function engine.draw_quad(x, y, width, height, r, g, b, a) end + +--- Draws a single textured quad +--- @param x number The x coordinate of the quad +--- @param y number The y coordinate of the quad +--- @param width number The width of the quad +--- @param height number The height of the quad +--- @param texture string The texture to use +--- @param tiling_factor number The tiling_factor +function engine.draw_textured_quad(x, y, width, height, texture, tiling_factor) end diff --git a/sandbox/assets/scripts/init.lua b/sandbox/assets/scripts/init.lua index bffd47f..16dc5e4 100644 --- a/sandbox/assets/scripts/init.lua +++ b/sandbox/assets/scripts/init.lua @@ -1,6 +1,4 @@ -engine.trace("Starting init.lua execution") - --- Create build directory structure first +-- Create build directory structure local buildDir = "build/assets/scripts" local scriptDir = "sandbox/assets/scripts" @@ -29,7 +27,7 @@ local function copyScript(name) end if not isPathSafe(name) then - engine.error(string.format("Invalid script name (potential path traversal): %s", name)) + engine.warn(string.format("Invalid script name (potential path traversal): %s", name)) return false end @@ -38,7 +36,7 @@ local function copyScript(name) local source, sourceErr = io.open(sourcePath, "rb") if not source then - engine.error(string.format("Could not open source script ", sourcePath, ": ", sourceErr)) + engine.warn(string.format("Could not open source script ", sourcePath, ": ", sourceErr)) return false end @@ -47,7 +45,7 @@ local function copyScript(name) local size = source:seek("end") source:seek("set") if size > MAX_FILE_SIZE then - engine.error(string.format("Script %s is too large (%d bytes > %d bytes limit)", + engine.warn(string.format("Script %s is too large (%d bytes > %d bytes limit)", sourcePath, size, MAX_FILE_SIZE)) closeFiles(source) return false @@ -55,27 +53,26 @@ local function copyScript(name) local content = source:read("*all") if not content then - engine.error(string.format("Failed to read from ", sourcePath)) + engine.warn(string.format("Failed to read from ", sourcePath)) closeFiles(source) return false end local dest, destErr = io.open(destPath, "wb") if not dest then - engine.error(string.format("Failed to create destination file", destPath, ": ", destErr)) + engine.warn(string.format("Failed to create destination file", destPath, ": ", destErr)) closeFiles(source) return false end local success = dest:write(content) if not success then - engine.error(string.format("Failed to write to ", destPath)) + engine.warn(string.format("Failed to write to ", destPath)) closeFiles(source, dest) return false end closeFiles(source, dest) - engine.trace(string.format("Successfully copied: ", name)) return true end @@ -83,7 +80,7 @@ end local scripts = { "main.lua", "engine.lua" } for _, script in ipairs(scripts) do if not copyScript(script) then - engine.error("Failed to copy " .. script) + engine.warn("Failed to copy " .. script) return end end diff --git a/sandbox/assets/scripts/main.lua b/sandbox/assets/scripts/main.lua index 7b1925d..d1bc02a 100644 --- a/sandbox/assets/scripts/main.lua +++ b/sandbox/assets/scripts/main.lua @@ -1,40 +1,250 @@ +-- Add initialization guard to prevent multiple executions +if _G.initialized then return end +_G.initialized = true if not engine then engine.fatal("Engine API not available") end -local debuggingEnabled = true +local USE_3D = false --- Set up UI and debug options -if debuggingEnabled then - engine.log("Debug mode enabled") - engine.showFPSCounter(true) - engine.showRendererSettings(true) - engine.showTerrainControls(true) -end +local textures = { + checkerTexture = nil +} --- Initialize scene system -local function initializeScenes() - -- Create and set up main scene +local function initialize2DScene() + engine.log("[Lua]: Initializing 2D renderer...") + engine.setRenderType("2d") + -- Basic scene setup without additional features if not engine.createScene("MainScene") then - engine.error("Failed to create main scene") + engine.error("[Lua]: Failed to create MainScene") return false end if not engine.setActiveScene("MainScene") then - engine.error("Failed to set main scene as active") + engine.error("[Lua]: Failed to set active scene") + return false + end + + engine.log("abs") + engine.renderer2DInitialize() + engine.log("instalised 2d") + engine.setCameraType("orthographic") + engine.log("set cam") + engine.setCameraPosition(0, 0, 0) + + engine.log("[Lua]: 2D setup complete.") + return true +end + +local function initialize3DScene() + engine.log("[Lua]: Initializing 3D renderer...") + engine.setRenderType("3d") + if not engine.createScene("Test3DScene") then + engine.error("[Lua]: Failed to create Test3DScene") + return false + end + + if not engine.setActiveScene("Test3DScene") then + engine.error("[Lua]: Failed to set active scene") + return false + end + + -- Configure camera only after scene is set up + if not engine.setCameraType("perspective") then + engine.error("[Lua]: Failed to set camera type") return false end - -- Set up camera and terrain - engine.setCameraType("perspective") - engine.setCameraPosition(0, 10, 10) - engine.setCameraRotation(-45, 0) - engine.setClearColor(0.2, 0.3, 0.3, 1.0) + if not engine.setCameraPosition(0, 5, -10) then + engine.error("[Lua]: Failed to set camera position") + return false + end + local cube = engine.createCube("TestCube") + if cube then + cube.transform:SetPosition(0, 0, 0) + cube.transform:SetScale(1, 1, 1) + engine.log("[Lua]: Created test cube") + else + engine.error("[Lua]: Failed to create cube") + return false + end + + engine.log("[Lua]: 3D setup complete.") return true end --- Initialize game -if not initializeScenes() then - engine.error("Scene initialization failed") +if not _G.sceneInitialized then + if USE_3D then + if not initialize3DScene() then + engine.error("[Lua]: 3D scene initialization failed") + return + end + else + if not initialize2DScene() then + engine.error("[Lua]: 2D scene initialization failed") + return + end + end + _G.sceneInitialized = true end + +-- Debug setup +engine.log("[Lua]: Debug mode enabled.") +engine.showFPSCounter(true) + +local colors = { + { 1.0, 0.0, 0.0, 1.0 }, -- Red + { 0.0, 1.0, 0.0, 1.0 }, -- Green + { 0.0, 0.0, 1.0, 1.0 }, -- Blue + { 1.0, 1.0, 0.0, 1.0 }, -- Yellow + { 1.0, 0.0, 1.0, 1.0 }, -- Magenta + { 0.0, 1.0, 1.0, 1.0 } -- Cyan +} + +local function drawCheckerboard(rows, cols, size, time) + local startX = -(cols * size) / 2 + local startY = -(rows * size) / 2 + + for row = 0, rows - 1 do + for col = 0, cols - 1 do + local x = startX + (col * size) + local y = startY + (row * size) + + -- Create animated color selection based on position and time + local colorIndex = ((row + col + math.floor(time)) % #colors) + 1 + local color = colors[colorIndex] + + -- Draw the checker square + engine.drawQuad( + x + (size / 2), -- Center position X + y + (size / 2), -- Center position Y + size * 0.95, -- Slightly smaller than cell size to create grid effect + size * 0.95, -- Same for height + color[1], color[2], color[3], color[4] + ) + end + end +end + +-- Add animation utilities +local animations = { + time = 0, + rotatingQuads = { + { x = -0.8, y = 0.8, size = 0.15, rotation = 0 }, + { x = 0.8, y = 0.8, size = 0.15, rotation = math.pi / 4 }, + { x = 0.8, y = -0.8, size = 0.15, rotation = math.pi / 2 }, + { x = -0.8, y = -0.8, size = 0.15, rotation = math.pi * 3 / 4 } + }, + bouncingQuads = { + { x = 0, y = 0.8, size = 0.1, baseY = 0.8 }, + { x = 0.3, y = 0.8, size = 0.1, baseY = 0.8 }, + { x = -0.3, y = 0.8, size = 0.1, baseY = 0.8 } + } +} + +local function drawAnimatedQuads(time) + -- Draw rotating quads + for _, quad in ipairs(animations.rotatingQuads) do + quad.rotation = quad.rotation + time * 2 + engine.drawQuad( + quad.x, + quad.y, + quad.size, + quad.size, + 1.0, 1.0, 1.0, 1.0 -- White color + ) + end + + -- Draw bouncing quads with different phases + for i, quad in ipairs(animations.bouncingQuads) do + local phase = time * 4 + (i * math.pi / 4) + local yOffset = math.sin(phase) * 0.2 + engine.drawQuad( + quad.x, + quad.baseY + yOffset, + quad.size, + quad.size, + 1.0, 0.5, 0.0, 1.0 -- Orange color + ) + end +end + +local function drawTexturedQuads() + -- Only create texture once + if not textures.checkerTexture then + textures.checkerTexture = engine.createCheckerTexture() + end + + -- Draw textured quads with different tiling factors + local baseX = -0.5 + local tilingFactors = { 1.0, 2.0, 4.0 } + + for i, tiling in ipairs(tilingFactors) do + engine.drawTexturedQuad( + baseX + (i * 0.5), + 0, + 0.3, + 0.3, + textures.checkerTexture, + tiling + ) + end +end + +-- Combined update function for both modes +function UpdateScene() + if USE_3D then + if engine.getActiveSceneName() == "Test3DScene" then + local time = os.clock() + local radius = 10.0 + engine.setCameraPosition( + math.sin(time) * radius, + 5, + math.cos(time) * radius + ) + engine.setCameraRotation(0, -time) + + local cube = engine.getObject("TestCube") + if cube then + local rotation = cube.transform:GetRotation() + cube.transform:SetRotation( + rotation.x + 0.01, + rotation.y + 0.02, + rotation.z + 0.03 + ) + end + end + else + if engine.getActiveSceneName() == "MainScene" then + if not engine.renderer2DBeginScene then + engine.error("renderer2DBeginScene not available") + return + end + engine.renderer2DBeginScene() + + -- Get current time for animation + local time = os.clock() + drawCheckerboard(8, 8, 0.2, time) + + drawAnimatedQuads(time) + + drawTexturedQuads() + + if not engine.renderer2DEndScene then + engine.error("renderer2DEndScene not available") + return + end + engine.renderer2DEndScene() + end + end +end + +local function createCheckerTexture() + if not textures.checkerTexture then + textures.checkerTexture = engine.createCheckerTexture() + end + return textures.checkerTexture +end + +createCheckerTexture() diff --git a/sandbox/src/SandboxApp.cpp b/sandbox/src/SandboxApp.cpp index c828b83..e707411 100644 --- a/sandbox/src/SandboxApp.cpp +++ b/sandbox/src/SandboxApp.cpp @@ -5,6 +5,13 @@ #include "../src/Scene/SceneManager.h" #include "../src/Scripting/LuaScriptSystem.h" +#include "Camera/OrthographicCamera.h" +#include "Renderer/Renderer2D.h" + +// Add these constants before SandboxApp constructor +static constexpr ImVec2 INITIAL_WINDOW_SIZE(520, 600); +static constexpr size_t MAX_INPUT_BUFFER_SIZE = 256; +static constexpr size_t MAX_COMMAND_HISTORY = 100; /** * @brief Constructor for the SandboxApp, initializing core systems and setting up the Lua console. @@ -21,27 +28,19 @@ * @throws None Initialization failures are logged but do not throw exceptions */ SandboxApp::SandboxApp() : Application() { + // Create script system first Engine::LuaScriptSystem* scriptSystem = GetScriptSystem(); if (!scriptSystem) { LOG_ERROR("Script system not available!"); return; } - auto activeScene = Engine::SceneManager::Get().GetActiveScene(); - if (!activeScene) { - LOG_ERROR("No active scene available!"); + // Execute main.lua which will set up rendering type + if (!scriptSystem->ExecuteFile("main.lua")) { + LOG_ERROR("Failed to execute main.lua!"); return; } - // Get terrain system from active scene - m_TerrainSystem = activeScene->GetTerrainSystem(); - if (!m_TerrainSystem) { - LOG_ERROR("Failed to get terrain system from active scene!"); - return; - } - - m_TerrainSystem->Initialize(GetRenderer()); - // Register console toggle if (auto* inputSystem = GetInputSystem()) { inputSystem->RegisterKeyCallback(GLFW_KEY_GRAVE_ACCENT, [this](int action) { @@ -52,27 +51,6 @@ SandboxApp::SandboxApp() : Application() { } } -/** - * @brief Renders the Lua Console using ImGui, providing an interactive command input and history display. - * - * This method creates an ImGui window titled "Lua Console" with a scrollable command history region - * and a command input field. It supports: - * - Displaying previous command history - * - Navigating command history using up and down arrow keys - * - Executing Lua commands - * - Handling command input and focus management - * - * @note The console window size is set to 520x600 pixels on first use. - * @note Commands containing "Error" are filtered from history navigation. - * @note The input buffer is cleared after command execution. - * - * @see ExecuteCommand() - */ -// Constants for Lua console -static constexpr ImVec2 INITIAL_WINDOW_SIZE(520, 600); -static constexpr size_t MAX_INPUT_BUFFER_SIZE = 256; -static constexpr size_t MAX_COMMAND_HISTORY = 100; - void SandboxApp::OnImGuiRender() { ImGui::SetNextWindowSize(INITIAL_WINDOW_SIZE, ImGuiCond_FirstUseEver); if (ImGui::Begin("Lua Console", &m_ShowConsole)) { diff --git a/sandbox/src/SandboxApp.h b/sandbox/src/SandboxApp.h index 3d2360e..8a344e6 100644 --- a/sandbox/src/SandboxApp.h +++ b/sandbox/src/SandboxApp.h @@ -2,7 +2,6 @@ #include "Application.h" #include "EntryPoint.h" -#include "TerrainSystem/TerrainSystem.h" class SandboxApp : public Engine::Application { public: @@ -10,12 +9,14 @@ class SandboxApp : public Engine::Application { virtual void OnImGuiRender() override; private: - Engine::TerrainSystem* m_TerrainSystem = nullptr; + // 2D Rendering members + std::shared_ptr m_Camera; + + // Console members bool m_ShowConsole = false; std::string m_CommandBuffer; std::vector m_CommandHistory; int m_HistoryIndex = -1; void ExecuteCommand(const std::string& command); - void HandleConsoleInput(); }; diff --git a/src/Application.cpp b/src/Application.cpp index 1c1e26f..8c210aa 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -20,7 +20,7 @@ #include "UI/ImGuiOverlay.h" // Shader system -#include "Shader/DefaultShaders.h" +#include "Scene/Scene.h" #include "Shader/ShaderHotReload.h" /** @@ -111,9 +111,8 @@ namespace Engine { AssetManager::Get().PreloadFrequentAssets(); m_InputSystem = std::make_unique(m_Window.get(), *m_Renderer); m_ImGuiOverlay = std::make_unique(m_Window.get()); - + InitializeToggleStates(); - DefaultShaders::PreloadShaders(); m_TerrainSystem = nullptr; } @@ -313,23 +312,31 @@ namespace Engine { PROFILE_FUNCTION(); m_Renderer->Clear({0.1f, 0.1f, 0.1f, 1.0f}); - - SceneManager::Get().Render(*m_Renderer); - m_ImGuiLayer->Begin(); - + if (m_ImGuiEnabled) { - static RenderObject dummyRenderObject; - m_ImGuiOverlay->OnRender(dummyRenderObject, m_ShowFPSCounter, - m_FPSCounter.GetCurrentFPS(), - m_FPSCounter.GetAverageFPS(), - m_FPSCounter.GetFrameTime(), - m_FPSCounter.GetOnePercentLow(), - m_FPSCounter.GetOnePercentHigh()); + // Always show these controls m_ImGuiOverlay->RenderProfiler(); - m_ImGuiOverlay->RenderRendererSettings(); m_ImGuiOverlay->RenderEventDebugger(); - m_ImGuiOverlay->RenderTerrainControls(); // <-- Added call + + if (m_ShowFPSCounter) { + static RenderObject dummyRenderObject; + m_ImGuiOverlay->OnRender( + dummyRenderObject, m_ShowFPSCounter, m_FPSCounter.GetCurrentFPS(), + m_FPSCounter.GetAverageFPS(), m_FPSCounter.GetFrameTime(), + m_FPSCounter.GetOnePercentLow(), m_FPSCounter.GetOnePercentHigh()); + } + + // Only show these controls in 3D mode + if (m_RenderType == RenderType::Render3D) { + m_ImGuiOverlay->RenderRendererSettings(); + m_ImGuiOverlay->RenderTerrainControls(); + } + } + + // Update the Scene + if (m_ScriptSystem) { + m_ScriptSystem->CallGlobalFunction("UpdateScene"); } } @@ -405,4 +412,57 @@ namespace Engine { it->second.currentValue = value; } } + + void Application::ConfigureForRenderType() { + auto activeScene = SceneManager::Get().GetActiveScene(); + if (!activeScene) return; + + switch (m_RenderType) { + case RenderType::Render2D: + if (m_TerrainSystem) { + m_TerrainSystem->Shutdown(); + m_TerrainSystem = nullptr; + } + activeScene->SetCameraType(CameraType::Orthographic); + break; + + case RenderType::Render3D: + if (!m_TerrainSystem && activeScene) { + m_TerrainSystem = activeScene->GetTerrainSystem(); + if (m_TerrainSystem) { + m_TerrainSystem->Initialize(GetRenderer()); + } + } + activeScene->SetCameraType(CameraType::Perspective); + break; + } + } + + void Application::ConfigureCamera() { + if (!m_Renderer) { + LOG_ERROR("Renderer not initialized in ConfigureCamera"); + return; + } + + Renderer::CameraType rendererCamType = static_cast(m_CameraType); + LOG_TRACE_CONCAT("Camera type set to: ", + (rendererCamType == Renderer::CameraType::Orthographic ? "orthographic" + : "perspective")); + m_Renderer->SetCameraType(rendererCamType); + + // Create cameras if they don't exist + if (m_CameraType == CameraType::Perspective) { + if (!m_Renderer->GetPerspectiveCamera()) { + m_Renderer->SetPerspectiveCamera( + std::make_shared(45.0f, 1280.0f / 720.0f)); + } + m_Renderer->GetPerspectiveCamera()->SetPosition({0.0f, 5.0f, -10.0f}); + } else { + if (!m_Renderer->GetCamera()) { + m_Renderer->SetCamera( + std::make_shared(-1.6f, 1.6f, -0.9f, 0.9f)); + } + m_Renderer->GetCamera()->SetPosition({0.0f, 0.0f, 0.0f}); + } + } } \ No newline at end of file diff --git a/src/Application.h b/src/Application.h index 0879485..d7d778b 100644 --- a/src/Application.h +++ b/src/Application.h @@ -2,6 +2,7 @@ #include +#include "Camera/CameraTypes.h" #include "Core/AssetManager.h" #include "Core/FPSCounter.h" #include "ImGui/ImGuiLayer.h" @@ -15,185 +16,187 @@ #include "UI/ImGuiOverlay.h" #include "Window/Window.h" +namespace Engine { +enum class CameraType; // Forward declare + +enum class RenderType { Render2D, Render3D }; + /** - * @namespace Engine - * @brief Core engine namespace containing main application and utility classes - * - * The Engine namespace encapsulates all core functionality of the voxel engine including: - * - Application management and game loop - * - Window handling - * - Rendering systems - * - Input processing - * - Scene management - * - Terrain generation - * - * This namespace provides the foundation for building voxel-based games and applications - * by managing the lifecycle, resources, and core systems needed for rendering and interaction. - * - * Usage example: - * @code - * class Game : public Engine::Application { - * // Game implementation - * }; - * @endcode + * @brief Stores state for key toggle functionality */ -namespace Engine { +struct KeyToggleState { + bool previousState = false; ///< Previous key state + float pressStartTime = 0.0f; ///< Time when key was pressed + bool currentValue = true; ///< Current toggle state +}; + +/** + * @brief Main application class handling window, rendering and game loop + */ +class Application { + public: + Application(); + virtual ~Application(); + + /** + * @brief Main application loop + */ + void Run(); + + unsigned int indicies; + + Renderer& GetRenderer() { return *m_Renderer; } + + LuaScriptSystem* GetScriptSystem() { return m_ScriptSystem.get(); } + + static Application& Get() { return *s_Instance; } + InputSystem* GetInputSystem() { return m_InputSystem.get(); } + + virtual void OnImGuiRender() {} + + const std::string& GetAssetPath() const { return m_AssetPath; } + + ImGuiOverlay* GetImGuiOverlay() { return m_ImGuiOverlay.get(); } + + void SetRenderType(RenderType type) { + m_RenderType = type; + ConfigureForRenderType(); + } + RenderType GetRenderType() const { return m_RenderType; } + + void SetCameraType(CameraType type) { + m_CameraType = type; + ConfigureCamera(); + } + CameraType GetCameraType() const { return m_CameraType; } + + protected: + /** + * @brief Initialize the application window + * @param title Window title + * @param width Window width + * @param height Window height + */ + void InitWindow(const char* title = "Voxel Engine", int width = 1280, int height = 720); + + /** + * @brief Clean up window resources + */ + void ShutdownWindow(); + + /** + * @brief Begin a new frame + */ + void BeginScene(); + + /** + * @brief End the current frame + */ + void EndScene(); + + /** + * @brief Present the rendered frame + */ + void Present(); + + /** + * @brief Set the viewport dimensions + */ + void SetViewport(int x, int y, int width, int height) { + m_Renderer->SetViewport(x, y, width, height); + } + + /** + * @brief Process pending events + */ + void ProcessEvents(); + + void ConfigureForRenderType(); + + std::unique_ptr m_Renderer; + Engine::TerrainSystem* m_TerrainSystem = nullptr; + + private: + /** + * @brief Handle key toggle state changes + * @param key GLFW key code + * @param currentTime Current time + * @return bool True if toggle state changed + */ + bool HandleKeyToggle(int key, float currentTime); + + /** + * @brief Initialize toggle states for keys + */ + void InitializeToggleStates(); + + /** + * @brief Add a new key toggle state + * @param key GLFW key code + * @param defaultValue Initial toggle state + */ + void AddToggleState(int key, bool defaultValue = false); + + /** + * @brief Remove a key toggle state + * @param key GLFW key code + */ + void RemoveToggleState(int key); + /** - * @brief Stores state for key toggle functionality + * @brief Get current toggle state for a key + * @param key GLFW key code + * @return bool Current toggle state */ - struct KeyToggleState { - bool previousState = false; ///< Previous key state - float pressStartTime = 0.0f; ///< Time when key was pressed - bool currentValue = true; ///< Current toggle state - }; + bool GetToggleState(int key) const; /** - * @brief Main application class handling window, rendering and game loop + * @brief Set toggle state for a key + * @param key GLFW key code + * @param value New toggle state */ - class Application { - public: - Application(); - virtual ~Application(); - - /** - * @brief Main application loop - */ - void Run(); - - unsigned int indicies; - - Renderer& GetRenderer() { return *m_Renderer; } - - LuaScriptSystem* GetScriptSystem() { return m_ScriptSystem.get(); } - - static Application& Get() { return *s_Instance; } - InputSystem* GetInputSystem() { return m_InputSystem.get(); } - - virtual void OnImGuiRender() {} - - const std::string& GetAssetPath() const { return m_AssetPath; } - - ImGuiOverlay* GetImGuiOverlay() { return m_ImGuiOverlay.get(); } - - protected: - /** - * @brief Initialize the application window - * @param title Window title - * @param width Window width - * @param height Window height - */ - void InitWindow(const char* title = "Voxel Engine", int width = 1280, int height = 720); - - /** - * @brief Clean up window resources - */ - void ShutdownWindow(); - - /** - * @brief Begin a new frame - */ - void BeginScene(); - - /** - * @brief End the current frame - */ - void EndScene(); - - /** - * @brief Present the rendered frame - */ - void Present(); - - /** - * @brief Set the viewport dimensions - */ - void SetViewport(int x, int y, int width, int height) { - m_Renderer->SetViewport(x, y, width, height); - } - - /** - * @brief Process pending events - */ - void ProcessEvents(); - - std::unique_ptr m_Renderer; - Engine::TerrainSystem* m_TerrainSystem = nullptr; - - private: - /** - * @brief Handle key toggle state changes - * @param key GLFW key code - * @param currentTime Current time - * @return bool True if toggle state changed - */ - bool HandleKeyToggle(int key, float currentTime); - - /** - * @brief Initialize toggle states for keys - */ - void InitializeToggleStates(); - - /** - * @brief Add a new key toggle state - * @param key GLFW key code - * @param defaultValue Initial toggle state - */ - void AddToggleState(int key, bool defaultValue = false); - - /** - * @brief Remove a key toggle state - * @param key GLFW key code - */ - void RemoveToggleState(int key); - - /** - * @brief Get current toggle state for a key - * @param key GLFW key code - * @return bool Current toggle state - */ - bool GetToggleState(int key) const; - - /** - * @brief Set toggle state for a key - * @param key GLFW key code - * @param value New toggle state - */ - void SetToggleState(int key, bool value); - - bool m_Running = true; - bool m_ImGuiEnabled = true; - - float m_DebounceTime = 0.3f; - - std::unordered_map m_KeyToggles; - - const float MAX_TOGGLE_HOLD_TIME = 1.5f; - - std::unique_ptr m_Window; - std::unique_ptr m_ImGuiLayer; - std::unique_ptr m_InputSystem; - - // Rendering objects - std::vector> m_RenderableObjects; - - // FPS tracking members - bool m_ShowFPSCounter = true; - FPSCounter m_FPSCounter; - - std::unique_ptr m_ImGuiOverlay; - - void UpdateFPSCounter(float deltaTime); - - std::unique_ptr m_Light; - - // scripting - std::unique_ptr m_ScriptSystem; - - static Application* s_Instance; - - std::string m_AssetPath = "../sandbox/assets/"; // Base path for all assets - }; - - // To be defined by client application - Application* CreateApplication(); -} \ No newline at end of file + void SetToggleState(int key, bool value); + + bool m_Running = true; + bool m_ImGuiEnabled = true; + + float m_DebounceTime = 0.3f; + + std::unordered_map m_KeyToggles; + + const float MAX_TOGGLE_HOLD_TIME = 1.5f; + + std::unique_ptr m_Window; + std::unique_ptr m_ImGuiLayer; + std::unique_ptr m_InputSystem; + + // Rendering objects + std::vector> m_RenderableObjects; + + // FPS tracking members + bool m_ShowFPSCounter = true; + FPSCounter m_FPSCounter; + + std::unique_ptr m_ImGuiOverlay; + + void UpdateFPSCounter(float deltaTime); + + std::unique_ptr m_Light; + + // scripting + std::unique_ptr m_ScriptSystem; + + static Application* s_Instance; + + std::string m_AssetPath = "../sandbox/assets/"; // Base path for all assets + + RenderType m_RenderType = RenderType::Render3D; + + CameraType m_CameraType = CameraType::Perspective; + + void ConfigureCamera(); +}; + +// To be defined by client application +Application* CreateApplication(); +} // namespace Engine \ No newline at end of file diff --git a/src/Camera/CameraTypes.h b/src/Camera/CameraTypes.h new file mode 100644 index 0000000..e39a01e --- /dev/null +++ b/src/Camera/CameraTypes.h @@ -0,0 +1,6 @@ + +#pragma once + +namespace Engine { +enum class CameraType { Orthographic, Perspective }; +} // namespace Engine \ No newline at end of file diff --git a/src/Core/AssetManager.h b/src/Core/AssetManager.h index 6eff346..34046f1 100644 --- a/src/Core/AssetManager.h +++ b/src/Core/AssetManager.h @@ -1,9 +1,13 @@ #pragma once #include + #include -#include "Resource.h" + +#include "../Renderer/Buffer.h" #include "../Renderer/Texture.h" +#include "../Renderer/VertexArray.h" +#include "Resource.h" namespace Engine { @@ -150,6 +154,66 @@ class AssetManager { */ void UpdateMemoryUsage(size_t delta) { m_TotalMemoryUsage += delta; } + /** + * @brief Get or create a cube mesh + * @return Shared pointer to the cube mesh + */ + std::shared_ptr GetOrCreateCubeMesh() { + static std::shared_ptr cubeMesh; + if (!cubeMesh) { + float vertices[] = { + // Front face + -0.5f, + -0.5f, + 0.5f, + 0.5f, + -0.5f, + 0.5f, + 0.5f, + 0.5f, + 0.5f, + -0.5f, + 0.5f, + 0.5f, + // Back face + -0.5f, + -0.5f, + -0.5f, + 0.5f, + -0.5f, + -0.5f, + 0.5f, + 0.5f, + -0.5f, + -0.5f, + 0.5f, + -0.5f, + }; + + uint32_t indices[] = { + 0, 1, 2, 2, 3, 0, // Front + 1, 5, 6, 6, 2, 1, // Right + 5, 4, 7, 7, 6, 5, // Back + 4, 0, 3, 3, 7, 4, // Left + 3, 2, 6, 6, 7, 3, // Top + 4, 5, 1, 1, 0, 4 // Bottom + }; + + cubeMesh = std::shared_ptr(VertexArray::Create()); + auto vb = + std::shared_ptr(VertexBuffer::Create(vertices, sizeof(vertices))); + auto ib = std::shared_ptr( + IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t))); + + BufferLayout layout = {{ShaderDataType::Float3, "aPosition"}}; + + vb->SetLayout(layout); + cubeMesh->AddVertexBuffer(vb); + cubeMesh->SetIndexBuffer(ib); + } + return cubeMesh; + } + private: AssetManager() = default; ~AssetManager() { UnloadAll(); } diff --git a/src/Core/Transform.h b/src/Core/Transform.h index 89a63dc..83b91ed 100644 --- a/src/Core/Transform.h +++ b/src/Core/Transform.h @@ -1,34 +1,51 @@ #pragma once - #include namespace Engine { - /** - * @brief Represents a 3D transformation with position, rotation, and scale - * - * Provides functionality to handle 3D transformations and generate model matrices - * for rendering purposes. - */ - struct Transform { - /** @brief Position in 3D space */ - glm::vec3 position = glm::vec3(0.0f); - /** @brief Rotation in radians around each axis (x, y, z) */ - glm::vec3 rotation = glm::vec3(0.0f); - /** @brief Scale factors for each axis */ - glm::vec3 scale = glm::vec3(1.0f); - - /** - * @brief Generates a model matrix from the transform components - * @return Combined transformation matrix in the order: scale -> rotate -> translate - */ - glm::mat4 GetModelMatrix() const { - glm::mat4 model = glm::mat4(1.0f); - model = glm::translate(model, position); - model = glm::rotate(model, rotation.x, glm::vec3(1.0f, 0.0f, 0.0f)); - model = glm::rotate(model, rotation.y, glm::vec3(0.0f, 1.0f, 0.0f)); - model = glm::rotate(model, rotation.z, glm::vec3(0.0f, 0.0f, 1.0f)); - model = glm::scale(model, scale); - return model; - } +class Transform { + public: + struct TransformData { + glm::vec3 position{0.0f}; + glm::vec3 rotation{0.0f}; + glm::vec3 scale{1.0f}; }; -} + + Transform() = default; + + TransformData& GetData() { return m_Data; } + const TransformData& GetData() const { return m_Data; } + + // Position methods + void SetPosition(float x, float y, float z) { m_Data.position = {x, y, z}; } + void SetPosition(const glm::vec3& position) { m_Data.position = position; } + glm::vec3 GetPosition() const { return m_Data.position; } + + // Rotation methods + void SetRotation(float x, float y, float z) { m_Data.rotation = {x, y, z}; } + void SetRotation(const glm::vec3& rotation) { m_Data.rotation = rotation; } + glm::vec3 GetRotation() const { return m_Data.rotation; } + + // Scale methods + void SetScale(float x, float y, float z) { m_Data.scale = {x, y, z}; } + void SetScale(const glm::vec3& scale) { m_Data.scale = scale; } + glm::vec3 GetScale() const { return m_Data.scale; } + + // Single GetModelMatrix implementation + glm::mat4 GetModelMatrix() const { + if (m_Data.scale == glm::vec3(0.0f)) { + return glm::mat4(1.0f); // Return identity if invalid scale + } + + glm::mat4 model = glm::mat4(1.0f); + model = glm::translate(model, m_Data.position); + model = glm::rotate(model, glm::radians(m_Data.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); + model = glm::rotate(model, glm::radians(m_Data.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); + model = glm::rotate(model, glm::radians(m_Data.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + model = glm::scale(model, m_Data.scale); + return model; + } + + private: + TransformData m_Data; +}; +} // namespace Engine \ No newline at end of file diff --git a/src/Core/Utils/Logging.h b/src/Core/Utils/Logging.h index 40d7c9b..7967d9e 100644 --- a/src/Core/Utils/Logging.h +++ b/src/Core/Utils/Logging.h @@ -51,6 +51,125 @@ std::string ToString(const std::unique_ptr& ptr) { inline std::string ToString(const Window& window); +#ifdef _WIN32 +/** + * @brief Singleton logger class for application-wide logging + */ +class Logger { + public: + /** @return Reference to the logger instance */ + static Logger& Get() { + static Logger instance; + return instance; + } + + /** + * @brief Logs a concatenated message + * @tparam Args Types of arguments to concatenate + * @param level Severity level of the message + * @param args Arguments to concatenate into message + */ + template + void LogConcat(LogLevel level, Args&&... args) { + std::stringstream ss; + int dummy[] = {0, ((ss << std::forward(args)), 0)...}; + (void)dummy; + Log(level, ss.str()); + } + + /** + * @brief Logs a formatted message + * @tparam Args Types of format arguments + * @param level Severity level of the message + * @param format Format string + * @param args Format arguments + */ + template + void LogFormat(LogLevel level, const char* format, Args... args) { + Log(level, format); // For now, just output the format string + } + + void Log(LogLevel level, const std::string& message) { + std::time_t now = std::time(nullptr); + char buffer[32]; + std::strftime(buffer, 32, "%c", std::localtime(&now)); + std::string timestamp(buffer); + + std::string levelStr; + switch (level) { + case LogLevel::Trace: + levelStr = "TRACE"; + break; + case LogLevel::Info: + levelStr = "INFO"; + break; + case LogLevel::Warn: + levelStr = "WARN"; + break; + case LogLevel::Error: + levelStr = "ERROR"; + break; + case LogLevel::Fatal: + levelStr = "FATAL"; + break; + } + + std::cout << "[" << timestamp << "] [" << levelStr << "]: " << message << std::endl; + } + + /** + * @brief Logs a variable's name and value + * @tparam T Type of the variable + * @param level Severity level of the message + * @param varName Name of the variable + * @param value Value of the variable + */ + template + void LogValue(LogLevel level, const char* varName, const T& value) { + std::stringstream ss; + ss << varName << " = " << ToString(value); + Log(level, ss.str()); + } + + /** + * @brief Logs multiple variables' names and values + * @tparam Args Types of the variables + * @param level Severity level of the message + * @param args Pairs of variable names and values + */ + template + void LogValues(LogLevel level, Args&&... args) { + std::stringstream ss; + (LogValueImpl(ss, std::forward(args)), ...); + Log(level, ss.str()); + } + + private: + template + void LogValueImpl(std::stringstream& ss, const char* name, const T& value) { + ss << name << " = " << ToString(value) << ", "; + } + + Logger() = default; +}; + +#define LOG_TRACE_CONCAT(...) Engine::Logger::Get().LogConcat(Engine::LogLevel::Trace, __VA_ARGS__) +#define LOG_INFO_CONCAT(...) Engine::Logger::Get().LogConcat(Engine::LogLevel::Info, __VA_ARGS__) +#define LOG_WARN_CONCAT(...) Engine::Logger::Get().LogConcat(Engine::LogLevel::Warn, __VA_ARGS__) +#define LOG_ERROR_CONCAT(...) Engine::Logger::Get().LogConcat(Engine::LogLevel::Error, __VA_ARGS__) +#define LOG_FATAL_CONCAT(...) Engine::Logger::Get().LogConcat(Engine::LogLevel::Fatal, __VA_ARGS__) + +#define LOG_TRACE(...) Engine::Logger::Get().LogFormat(Engine::LogLevel::Trace, __VA_ARGS__) +#define LOG_INFO(...) Engine::Logger::Get().LogFormat(Engine::LogLevel::Info, __VA_ARGS__) +#define LOG_WARN(...) Engine::Logger::Get().LogFormat(Engine::LogLevel::Warn, __VA_ARGS__) +#define LOG_ERROR(...) Engine::Logger::Get().LogFormat(Engine::LogLevel::Error, __VA_ARGS__) +#define LOG_FATAL(...) Engine::Logger::Get().LogConcat(Engine::LogLevel::Fatal, __VA_ARGS__) + +// New convenience macros +#define LOG_VAR(level, name, value) Engine::Logger::Get().LogValue(level, #name, value) +#define LOG_VARS(level, ...) Engine::Logger::Get().LogValues(level, __VA_ARGS__) + +#else namespace LogColors { constexpr const char* Reset = "\033[0m"; constexpr const char* Yellow = "\033[33m"; @@ -83,19 +202,19 @@ class Logger { return instance; } - /** - * @brief Logs a concatenated message - * @tparam Args Types of arguments to concatenate - * @param level Severity level of the message - * @param args Arguments to concatenate into message - */ - template - void LogConcat(LogLevel level, Args&&... args) { - std::stringstream ss; - int dummy[] = {0, ((ss << std::forward(args)), 0)...}; - (void)dummy; - Log(level, ss.str()); - } + /** + * @brief Logs a concatenated message + * @tparam Args Types of arguments to concatenate + * @param level Severity level of the message + * @param args Arguments to concatenate into message + */ + template + void LogConcat(LogLevel level, Args&&... args) { + std::stringstream ss; + int dummy[] = {0, ((ss << std::forward(args)), 0)...}; + (void)dummy; + Log(level, ss.str()); + } /** * @brief Logs a formatted message @@ -195,4 +314,5 @@ class Logger { // New convenience macros #define LOG_VAR(level, name, value) Engine::Logger::Get().LogValue(level, #name, value) #define LOG_VARS(level, ...) Engine::Logger::Get().LogValues(level, __VA_ARGS__) -} // namespace Engine \ No newline at end of file +#endif +} // namespace Engine diff --git a/src/Input/InputSystem.h b/src/Input/InputSystem.h index 382b286..ea9a371 100644 --- a/src/Input/InputSystem.h +++ b/src/Input/InputSystem.h @@ -1,147 +1,148 @@ #pragma once +#include #include -#include "Window/Window.h" + #include "Events/KeyEvent.h" #include "Events/MouseEvent.h" #include "Renderer/Renderer.h" -#include +#include "Window/Window.h" namespace Engine { +/** + * @brief Handles all input processing for the application + * + * Manages keyboard and mouse input, camera controls, and input event handling. + * Provides an interface between GLFW input and the game systems. + */ +class InputSystem { + public: + /** + * @brief Constructs an input system + * @param window Pointer to the application window + * @param renderer Reference to the renderer + */ + InputSystem(Window* window, Renderer& renderer); + + /** + * @brief Updates input state and handles continuous input + * @param deltaTime Time elapsed since last update + */ + void Update(float deltaTime); + + /** + * @brief Processes input events + * @param e Event to process + */ + void OnEvent(Event& e); + + /** + * @brief Checks if a key is currently pressed + * @param keycode GLFW key code to check + * @return true if key is pressed + */ + + bool IsKeyPressed(int keycode) const; + + /** + * @brief Checks if a mouse button is currently pressed + * @param button GLFW mouse button code to check + * @return true if button is pressed + */ + bool IsMouseButtonPressed(int button) const; + + /** + * @brief Gets the current mouse cursor position + * @return Pair of x,y coordinates + */ + std::pair GetMousePosition() const; + + /** + * @brief Sets mouse sensitivity + * @param sensitivity New sensitivity value + */ + void SetSensitivity(float sensitivity) { m_MouseSensitivity = sensitivity; } + + /** @return Current mouse sensitivity */ + float GetSensitivity() const { return m_MouseSensitivity; } + + // Add new control methods + void ToggleCameraControls() { m_CameraEnabled = !m_CameraEnabled; } + void ToggleSmoothCamera() { m_SmoothCamera = !m_SmoothCamera; } + void SetMovementSpeed(float speed) { m_MovementSpeed = speed; } + float GetMovementSpeed() const { return m_MovementSpeed; } + void ToggleMovementLock() { m_MovementLocked = !m_MovementLocked; } + + // Key callback system + using KeyCallback = std::function; + void RegisterKeyCallback(int key, KeyCallback callback); + + // Key event handling + void OnKey(int key, int scancode, int action, int mods); + + private: + /** + * @brief Handles mouse movement input + * @param e Mouse moved event + */ + void HandleMouseMovement(const MouseMovedEvent& e); + + /** + * @brief Processes keyboard input + * @param deltaTime Time elapsed since last update + */ + void HandleKeyInput(float deltaTime); + /** - * @brief Handles all input processing for the application - * - * Manages keyboard and mouse input, camera controls, and input event handling. - * Provides an interface between GLFW input and the game systems. + * @brief Updates camera position based on input + * @param deltaTime Time elapsed since last update */ - class InputSystem { - public: - /** - * @brief Constructs an input system - * @param window Pointer to the application window - * @param renderer Reference to the renderer - */ - InputSystem(Window* window, Renderer& renderer); - - /** - * @brief Updates input state and handles continuous input - * @param deltaTime Time elapsed since last update - */ - void Update(float deltaTime); - - /** - * @brief Processes input events - * @param e Event to process - */ - void OnEvent(Event& e); - - /** - * @brief Checks if a key is currently pressed - * @param keycode GLFW key code to check - * @return true if key is pressed - */ - - bool IsKeyPressed(int keycode) const; - - /** - * @brief Checks if a mouse button is currently pressed - * @param button GLFW mouse button code to check - * @return true if button is pressed - */ - bool IsMouseButtonPressed(int button) const; - - /** - * @brief Gets the current mouse cursor position - * @return Pair of x,y coordinates - */ - std::pair GetMousePosition() const; - - /** - * @brief Sets mouse sensitivity - * @param sensitivity New sensitivity value - */ - void SetSensitivity(float sensitivity) { m_MouseSensitivity = sensitivity; } - - /** @return Current mouse sensitivity */ - float GetSensitivity() const { return m_MouseSensitivity; } - - // Add new control methods - void ToggleCameraControls() { m_CameraEnabled = !m_CameraEnabled; } - void ToggleSmoothCamera() { m_SmoothCamera = !m_SmoothCamera; } - void SetMovementSpeed(float speed) { m_MovementSpeed = speed; } - float GetMovementSpeed() const { return m_MovementSpeed; } - void ToggleMovementLock() { m_MovementLocked = !m_MovementLocked; } - - // Key callback system - using KeyCallback = std::function; - void RegisterKeyCallback(int key, KeyCallback callback); - - // Key event handling - void OnKey(int key, int scancode, int action, int mods); - - private: - /** - * @brief Handles mouse movement input - * @param e Mouse moved event - */ - void HandleMouseMovement(const MouseMovedEvent& e); - - /** - * @brief Processes keyboard input - * @param deltaTime Time elapsed since last update - */ - void HandleKeyInput(float deltaTime); - - /** - * @brief Updates camera position based on input - * @param deltaTime Time elapsed since last update - */ - void HandleCameraMovement(float deltaTime); - - Window* m_Window; ///< Pointer to application window - Renderer& m_Renderer; ///< Reference to renderer - - bool m_FirstMouse = true; ///< First mouse input flag - float m_LastMouseX = 0.0f; ///< Last mouse X position - float m_LastMouseY = 0.0f; ///< Last mouse Y position - float m_MouseSensitivity = 0.1f; ///< Mouse movement sensitivity - - // Camera control flags - bool m_CameraEnabled = true; ///< Toggle for camera controls - bool m_SmoothCamera = true; ///< Toggle for smooth camera movement - - // Movement timing - const float FIXED_TIMESTEP = 1.0f/60.0f; ///< 60Hz update rate - float m_MovementAccumulator = 0.0f; ///< Time accumulator for movement updates - - // Movement settings - float m_MovementSpeed = 250.0f; ///< Base movement speed (blocks per second) - float m_SprintMultiplier = 2.0f; ///< Sprint speed multiplier - float m_SlowMultiplier = 1.0f; ///< Slow movement multiplier - - // Smooth movement variables - glm::vec3 m_TargetPosition{0.0f}; ///< Target position for smooth movement - glm::vec3 m_CurrentVelocity{0.0f}; ///< Current velocity for smooth damping - float m_SmoothTime = 0.1f; ///< Smoothing time factor - - // Input handling methods - void HandleSpeedModifiers(float& speed); - void HandleSensitivityAdjustment(); - void SmoothDamp(glm::vec3& current, const glm::vec3& target, - glm::vec3& velocity, float smoothTime, float deltaTime); - - // Key state tracking - bool m_LastVPressed = false; ///< Previous frame V key state - bool m_LastBPressed = false; ///< Previous frame B key state - bool m_MovementLocked = false; ///< Global movement lock flag - bool m_LastEscPressed = false; ///< Previous frame ESC key state - - // Mouse cursor control - bool m_CursorLocked = false; ///< Is cursor locked to window center - void UpdateCursorState(); ///< Updates cursor visibility and lock state - - // Internal key event processing - void OnKeyEvent(int key, int action); - - std::unordered_map m_KeyCallbacks; - }; -} \ No newline at end of file + void HandleCameraMovement(float deltaTime); + + Window* m_Window; ///< Pointer to application window + Renderer& m_Renderer; ///< Reference to renderer + + bool m_FirstMouse = true; ///< First mouse input flag + float m_LastMouseX = 0.0f; ///< Last mouse X position + float m_LastMouseY = 0.0f; ///< Last mouse Y position + float m_MouseSensitivity = 0.1f; ///< Mouse movement sensitivity + + // Camera control flags + bool m_CameraEnabled = true; ///< Toggle for camera controls + bool m_SmoothCamera = true; ///< Toggle for smooth camera movement + + // Movement timing + const float FIXED_TIMESTEP = 1.0f / 60.0f; ///< 60Hz update rate + float m_MovementAccumulator = 0.0f; ///< Time accumulator for movement updates + + // Movement settings + float m_MovementSpeed = 250.0f; ///< Base movement speed (blocks per second) + float m_SprintMultiplier = 2.0f; ///< Sprint speed multiplier + float m_SlowMultiplier = 1.0f; ///< Slow movement multiplier + + // Smooth movement variables + glm::vec3 m_TargetPosition{0.0f}; ///< Target position for smooth movement + glm::vec3 m_CurrentVelocity{0.0f}; ///< Current velocity for smooth damping + float m_SmoothTime = 0.1f; ///< Smoothing time factor + + // Input handling methods + void HandleSpeedModifiers(float& speed); + void HandleSensitivityAdjustment(); + void SmoothDamp(glm::vec3& current, const glm::vec3& target, glm::vec3& velocity, + float smoothTime, float deltaTime); + + // Key state tracking + bool m_LastVPressed = false; ///< Previous frame V key state + bool m_LastBPressed = false; ///< Previous frame B key state + bool m_MovementLocked = false; ///< Global movement lock flag + bool m_LastEscPressed = false; ///< Previous frame ESC key state + + // Mouse cursor control + bool m_CursorLocked = false; ///< Is cursor locked to window center + void UpdateCursorState(); ///< Updates cursor visibility and lock state + + // Internal key event processing + void OnKeyEvent(int key, int action); + + std::unordered_map m_KeyCallbacks; +}; +} // namespace Engine \ No newline at end of file diff --git a/src/Renderer/BatchRenderer2D.h b/src/Renderer/BatchRenderer2D.h new file mode 100644 index 0000000..2e7411c --- /dev/null +++ b/src/Renderer/BatchRenderer2D.h @@ -0,0 +1,70 @@ + +#pragma once +#include + +#include "Camera/OrthographicCamera.h" +#include "Material.h" +#include "VertexArray.h" + +namespace Engine { + +struct BatchVertex { + glm::vec3 Position; + glm::vec4 Color; + glm::vec2 TexCoord; + float TexIndex; + float TilingFactor; +}; + +class BatchRenderer2D { + public: + static const uint32_t MaxQuads = 100000; + static const uint32_t MaxVertices = MaxQuads * 4; + static const uint32_t MaxIndices = MaxQuads * 6; + static const uint32_t MaxTextureSlots = 32; + + BatchRenderer2D(); + ~BatchRenderer2D(); + + void Initialize(); + void Shutdown(); + + void BeginScene(const std::shared_ptr& camera); + void EndScene(); + + void DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color); + void DrawQuad(const glm::vec3& position, const glm::vec2& size, + const std::shared_ptr& texture, float tilingFactor = 1.0f, + const glm::vec4& tintColor = glm::vec4(1.0f)); + + private: + void StartBatch(); + void NextBatch(); + void Flush(); + + std::shared_ptr m_QuadVA; + std::shared_ptr m_QuadVB; + std::shared_ptr m_Material; + std::shared_ptr m_WhiteTexture; + + uint32_t m_QuadIndexCount = 0; + BatchVertex* m_VertexBufferBase = nullptr; + BatchVertex* m_VertexBufferPtr = nullptr; + + std::array, MaxTextureSlots> m_TextureSlots; + uint32_t m_TextureSlotIndex = 1; + + glm::vec4 m_QuadVertexPositions[4]; + + struct Statistics { + uint32_t DrawCalls = 0; + uint32_t QuadCount = 0; + + void Reset() { + DrawCalls = 0; + QuadCount = 0; + } + } m_Stats; +}; + +} // namespace Engine \ No newline at end of file diff --git a/src/Renderer/IRenderer.h b/src/Renderer/IRenderer.h new file mode 100644 index 0000000..ee66601 --- /dev/null +++ b/src/Renderer/IRenderer.h @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace Engine { +class IRenderer { + public: + virtual ~IRenderer() = default; + + virtual void Initialize() = 0; + virtual void Shutdown() = 0; + virtual void BeginScene() = 0; + virtual void EndScene() = 0; + virtual void Flush() = 0; + + struct Statistics { + uint32_t DrawCalls = 0; + uint32_t VertexCount = 0; + uint32_t IndexCount = 0; + + void Reset() { memset(this, 0, sizeof(Statistics)); } + }; + + virtual Statistics GetStats() const = 0; + virtual void ResetStats() = 0; +}; +} // namespace Engine diff --git a/src/Renderer/Renderer2D.cpp b/src/Renderer/Renderer2D.cpp new file mode 100644 index 0000000..76a10ad --- /dev/null +++ b/src/Renderer/Renderer2D.cpp @@ -0,0 +1,234 @@ +#include "Renderer2D.h" + +#include + +#include "../Camera/OrthographicCamera.h" +#include "../Shader/Shader.h" +#include "Material.h" +#include "Shader/ShaderLibrary.h" +#include "VertexArray.h" + +namespace Engine { +struct QuadVertex { + glm::vec3 Position; + glm::vec4 Color; + glm::vec2 TexCoord; + float TexIndex; + float TilingFactor; +}; + +struct Renderer2DData { + static const uint32_t MaxQuads = 100000; + static const uint32_t MaxVertices = MaxQuads * 4; + static const uint32_t MaxIndices = MaxQuads * 6; + static const uint32_t MaxTextureSlots = 32; + + std::shared_ptr QuadVertexArray; + std::shared_ptr QuadVertexBuffer; + std::shared_ptr TextureMaterial; + std::shared_ptr WhiteTexture; + + uint32_t QuadIndexCount = 0; + QuadVertex* QuadVertexBufferBase = nullptr; + QuadVertex* QuadVertexBufferPtr = nullptr; + + std::array, MaxTextureSlots> TextureSlots; + uint32_t TextureSlotIndex = 1; // 0 = white texture + + glm::vec4 QuadVertexPositions[4]; +}; + +static Renderer2DData s_Data; + +void Renderer2D::Initialize() { + // Create vertex array + s_Data.QuadVertexArray = std::shared_ptr(VertexArray::Create()); + + // Create vertex buffer + s_Data.QuadVertexBuffer = std::shared_ptr( + VertexBuffer::Create(nullptr, s_Data.MaxVertices * sizeof(QuadVertex))); + + // Set buffer layout + BufferLayout layout = {{ShaderDataType::Float3, "a_Position"}, + {ShaderDataType::Float4, "a_Color"}, + {ShaderDataType::Float2, "a_TexCoord"}, + {ShaderDataType::Float, "a_TexIndex"}, + {ShaderDataType::Float, "a_TilingFactor"}}; + s_Data.QuadVertexBuffer->SetLayout(layout); + s_Data.QuadVertexArray->AddVertexBuffer(s_Data.QuadVertexBuffer); + + // Create and set index buffer + uint32_t* quadIndices = new uint32_t[s_Data.MaxIndices]; + uint32_t offset = 0; + for (uint32_t i = 0; i < s_Data.MaxIndices; i += 6) { + quadIndices[i + 0] = offset + 0; + quadIndices[i + 1] = offset + 1; + quadIndices[i + 2] = offset + 2; + quadIndices[i + 3] = offset + 2; + quadIndices[i + 4] = offset + 3; + quadIndices[i + 5] = offset + 0; + offset += 4; + } + + auto quadIB = std::shared_ptr(IndexBuffer::Create(quadIndices, s_Data.MaxIndices)); + s_Data.QuadVertexArray->SetIndexBuffer(quadIB); + delete[] quadIndices; + + // Get the batch renderer shader + auto shader = ShaderLibrary::CreateBatchRenderer2DShader(); + if (!shader) { + LOG_ERROR("Failed to create BatchRenderer2D shader"); + return; + } + + // Create material with shader + s_Data.TextureMaterial = std::make_shared(shader); + + // Configure texture sampling properties + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + // Create white texture + s_Data.WhiteTexture = std::make_shared(1, 1); + uint8_t whiteTextureData[4] = {255, 255, 255, 255}; + s_Data.WhiteTexture->SetData(whiteTextureData, sizeof(whiteTextureData)); + + // Initialize texture slots + s_Data.TextureSlots[0] = s_Data.WhiteTexture; + + // Create quad vertices + s_Data.QuadVertexPositions[0] = {-0.5f, -0.5f, 0.0f, 1.0f}; + s_Data.QuadVertexPositions[1] = {0.5f, -0.5f, 0.0f, 1.0f}; + s_Data.QuadVertexPositions[2] = {0.5f, 0.5f, 0.0f, 1.0f}; + s_Data.QuadVertexPositions[3] = {-0.5f, 0.5f, 0.0f, 1.0f}; + + // Allocate vertex buffer + s_Data.QuadVertexBufferBase = new QuadVertex[s_Data.MaxVertices]; +} + +void Renderer2D::Shutdown() { delete[] s_Data.QuadVertexBufferBase; } + +void Renderer2D::BeginScene() { + // Default implementation for IRenderer interface +} + +void Renderer2D::BeginScene(const std::shared_ptr& camera) { + // Ensure material and shader exist + if (!s_Data.TextureMaterial || !s_Data.TextureMaterial->GetShader()) { + return; + } + + s_Data.TextureMaterial->GetShader()->Bind(); + s_Data.TextureMaterial->GetShader()->SetMat4("u_ViewProjection", + camera->GetViewProjectionMatrix()); + StartBatch(); +} + +void Renderer2D::EndScene() { Flush(); } + +void Renderer2D::StartBatch() { + s_Data.QuadIndexCount = 0; + s_Data.QuadVertexBufferPtr = s_Data.QuadVertexBufferBase; + s_Data.TextureSlotIndex = 1; +} + +void Renderer2D::Flush() { + if (s_Data.QuadIndexCount == 0) return; + + uint32_t dataSize = + (uint32_t)((uint8_t*)s_Data.QuadVertexBufferPtr - (uint8_t*)s_Data.QuadVertexBufferBase); + s_Data.QuadVertexBuffer->Bind(); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, s_Data.QuadVertexBufferBase); + + // Bind textures + for (uint32_t i = 0; i < s_Data.TextureSlotIndex; i++) s_Data.TextureSlots[i]->Bind(i); + + s_Data.TextureMaterial->Bind(); + s_Data.QuadVertexArray->Bind(); + glDrawElements(GL_TRIANGLES, s_Data.QuadIndexCount, GL_UNSIGNED_INT, nullptr); + + // Update stats + m_Stats.DrawCalls++; + m_Stats.IndexCount += s_Data.QuadIndexCount; + m_Stats.VertexCount += s_Data.QuadIndexCount * 4 / 6; // Convert indices to vertices +} + +void Renderer2D::DrawQuad(const glm::vec2& position, const glm::vec2& size, + const glm::vec4& color) { + DrawQuad({position.x, position.y, 0.0f}, size, color); +} + +void Renderer2D::DrawQuad(const glm::vec3& position, const glm::vec2& size, + const glm::vec4& color) { + if (s_Data.QuadIndexCount >= Renderer2DData::MaxIndices) NextBatch(); + + const float texIndex = 0.0f; // White Texture + const float tilingFactor = 1.0f; + + glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * + glm::scale(glm::mat4(1.0f), {size.x, size.y, 1.0f}); + + // Add vertices to buffer + for (size_t i = 0; i < 4; i++) { + s_Data.QuadVertexBufferPtr->Position = transform * s_Data.QuadVertexPositions[i]; + s_Data.QuadVertexBufferPtr->Color = color; + s_Data.QuadVertexBufferPtr->TexCoord = {(i == 1 || i == 2) ? 1.0f : 0.0f, + (i == 2 || i == 3) ? 1.0f : 0.0f}; + s_Data.QuadVertexBufferPtr->TexIndex = texIndex; + s_Data.QuadVertexBufferPtr->TilingFactor = tilingFactor; + s_Data.QuadVertexBufferPtr++; + } + + s_Data.QuadIndexCount += 6; +} + +void Renderer2D::DrawQuad(const glm::vec2& position, const glm::vec2& size, + const std::shared_ptr& texture, float tilingFactor, + const glm::vec4& tintColor) { + DrawQuad({position.x, position.y, 0.0f}, size, texture, tilingFactor, tintColor); +} + +void Renderer2D::DrawQuad(const glm::vec3& position, const glm::vec2& size, + const std::shared_ptr& texture, float tilingFactor, + const glm::vec4& tintColor) { + if (s_Data.QuadIndexCount >= Renderer2DData::MaxIndices) NextBatch(); + + float textureIndex = 0.0f; + for (uint32_t i = 1; i < s_Data.TextureSlotIndex; i++) { + if (s_Data.TextureSlots[i] == texture) { + textureIndex = (float)i; + break; + } + } + + if (textureIndex == 0.0f) { + if (s_Data.TextureSlotIndex >= Renderer2DData::MaxTextureSlots) NextBatch(); + + textureIndex = (float)s_Data.TextureSlotIndex; + s_Data.TextureSlots[s_Data.TextureSlotIndex] = texture; + s_Data.TextureSlotIndex++; + } + + glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * + glm::scale(glm::mat4(1.0f), {size.x, size.y, 1.0f}); + + for (size_t i = 0; i < 4; i++) { + s_Data.QuadVertexBufferPtr->Position = transform * s_Data.QuadVertexPositions[i]; + s_Data.QuadVertexBufferPtr->Color = tintColor; + s_Data.QuadVertexBufferPtr->TexCoord = {(i == 1 || i == 2) ? 1.0f : 0.0f, + (i == 2 || i == 3) ? 1.0f : 0.0f}; + s_Data.QuadVertexBufferPtr->TexIndex = textureIndex; + s_Data.QuadVertexBufferPtr->TilingFactor = tilingFactor; + s_Data.QuadVertexBufferPtr++; + } + + s_Data.QuadIndexCount += 6; +} + +void Renderer2D::NextBatch() { + Flush(); + StartBatch(); +} +} // namespace Engine diff --git a/src/Renderer/Renderer2D.h b/src/Renderer/Renderer2D.h new file mode 100644 index 0000000..a38eb00 --- /dev/null +++ b/src/Renderer/Renderer2D.h @@ -0,0 +1,49 @@ +#pragma once +#include + +#include "Camera/OrthographicCamera.h" +#include "IRenderer.h" +#include "RenderObject.h" +#include "Renderer/Material.h" + +namespace Engine { + +/** + * @brief Specialized renderer for 2D graphics with batching support + */ +class Renderer2D : public IRenderer { + public: + static Renderer2D& Get() { + static Renderer2D instance; + return instance; + } + + // IRenderer interface implementation + void Initialize() override; + void Shutdown() override; + void BeginScene() override; + void EndScene() override; + void Flush() override; + Statistics GetStats() const override { return m_Stats; } + void ResetStats() override { m_Stats.Reset(); } + + // 2D-specific methods + void BeginScene(const std::shared_ptr& camera); + void DrawQuad(const glm::vec2& position, const glm::vec2& size, const glm::vec4& color); + void DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color); + void DrawQuad(const glm::vec2& position, const glm::vec2& size, + const std::shared_ptr& texture, float tilingFactor = 1.0f, + const glm::vec4& tintColor = glm::vec4(1.0f)); + void DrawQuad(const glm::vec3& position, const glm::vec2& size, + const std::shared_ptr& texture, float tilingFactor = 1.0f, + const glm::vec4& tintColor = glm::vec4(1.0f)); + + private: + Renderer2D() = default; // Private constructor + void StartBatch(); + void NextBatch(); + + Statistics m_Stats; // Added statistics member + std::shared_ptr m_Material; // Material reference +}; +} // namespace Engine diff --git a/src/Renderer/Texture.h b/src/Renderer/Texture.h index 6fb3ee8..70f160d 100644 --- a/src/Renderer/Texture.h +++ b/src/Renderer/Texture.h @@ -1,7 +1,13 @@ #pragma once -#include "../Core/Resource.h" -#include + +// clang-format off #include +#include +// clang-format on + +#include + +#include "../Core/Resource.h" namespace Engine { @@ -91,6 +97,26 @@ class Texture : public Resource { */ uint32_t GetRendererID() const { return m_RendererID; } + /** + * @brief Sets raw texture data + * @param data Pointer to pixel data (must be RGBA format) + * @param size Size of data in bytes (must be width * height * 4) + */ + virtual void SetData(void* data, uint32_t size) { + if (!data) { + LOG_ERROR("Null data pointer provided to SetData"); + return; + } + ASSERT(size == m_Width * m_Height * 4 && "Data must be entire texture in RGBA format!"); + Bind(); + GLenum error; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, + data); + if ((error = glGetError()) != GL_NO_ERROR) { + LOG_ERROR("OpenGL error in SetData: ", error); + } + } + private: uint32_t m_RendererID = 0; ///< OpenGL texture handle int m_Width = 0, m_Height = 0; ///< Texture dimensions diff --git a/src/Scene/Scene.cpp b/src/Scene/Scene.cpp index 834bfab..d1e3168 100644 --- a/src/Scene/Scene.cpp +++ b/src/Scene/Scene.cpp @@ -17,8 +17,6 @@ namespace Engine { * using other methods like AddChild() or setting transforms. */ -SceneObject::SceneObject(const std::string &name) : name(name) {} - /** * @brief Adds a child SceneObject to the current object's hierarchy. * @@ -27,12 +25,9 @@ SceneObject::SceneObject(const std::string &name) : name(name) {} * and the child is added to the current object's list of children. * * @param child A shared pointer to the SceneObject to be added as a child. - * @note The method uses shared_from_this() to create a shared pointer reference to the current object. + * @note The method uses shared_from_this() to create a shared pointer reference to the current + * object. */ -void SceneObject::AddChild(const std::shared_ptr &child) { - child->parent = shared_from_this(); - children.push_back(child); -} /** * @brief Computes the world transformation matrix for the scene object. @@ -45,13 +40,6 @@ void SceneObject::AddChild(const std::shared_ptr &child) { * @note The transformation is computed hierarchically, multiplying the parent's world * transform with the object's local transform to preserve scene graph hierarchy. */ -glm::mat4 SceneObject::GetWorldTransform() const { - glm::mat4 worldTransform = transform.GetModelMatrix(); - if (auto parentPtr = parent.lock()) { - worldTransform = parentPtr->GetWorldTransform() * worldTransform; - } - return worldTransform; -} /** * @brief Constructs a new Scene with the specified name. @@ -69,8 +57,7 @@ glm::mat4 SceneObject::GetWorldTransform() const { Scene::Scene(const std::string &name) : m_Name(name) { m_RootObject = std::make_shared("Root"); - m_TerrainSystem = std::make_unique(); - LOG_TRACE("Created scene: ", name); + LOG_TRACE_CONCAT("Created scene: ", m_Name); // Fix: Use _CONCAT for string concatenation } /** @@ -121,23 +108,20 @@ Scene::~Scene() { } /** - * @brief Initializes the scene and its terrain system. + * @brief Initializes the scene, creating a TerrainSystem if enabled. * - * This method is responsible for setting up the scene's terrain system. If no terrain system - * exists, it creates a new one. If both a terrain system and a renderer are available, - * the terrain system is initialized with the renderer. - * - * @note Logs a trace message indicating the scene initialization. - * @note Creates a terrain system if it does not already exist. - * @note Initializes the terrain system with the renderer if both are available. + * Logs initialization. If terrain is enabled and no TerrainSystem exists, + * creates a new one and initializes it with the renderer. */ void Scene::OnCreate() { LOG_TRACE("Initializing scene: ", m_Name); - if (!m_TerrainSystem) { - m_TerrainSystem = std::make_unique(); + // Only create terrain if it was explicitly enabled and requested + if (m_EnableTerrain && !m_TerrainSystem) { + CreateTerrain(); } + // Initialize existing terrain if we have one and a renderer if (m_TerrainSystem && m_Renderer) { m_TerrainSystem->Initialize(*m_Renderer); } @@ -228,20 +212,30 @@ void Scene::OnRender(Renderer &renderer) { * @note The rendering is performed using the object's world transformation matrix. */ void Scene::RenderObject(const std::shared_ptr &object, Renderer &renderer) { - if (!object) return; - - if (object->vertexArray && object->material) { - PreprocessedRenderCommand cmd; - cmd.vertexArray = object->vertexArray; - cmd.material = object->material; - cmd.modelMatrix = object->GetWorldTransform(); + if (object->GetMesh() && object->GetMaterial()) { + RenderCommand cmd; + cmd.vertexArray = object->GetMesh(); + cmd.material = object->GetMaterial(); + cmd.transformMatrix = object->GetWorldTransform(); cmd.primitiveType = GL_TRIANGLES; - - renderer.Submit(cmd.vertexArray, cmd.material, cmd.modelMatrix); + renderer.Submit(cmd.vertexArray, cmd.material, cmd.transformMatrix); } for (const auto &child : object->children) { - RenderObject(child, renderer); + if (child) { + RenderObject(child, renderer); + } + } +} + +bool Scene::CreateTerrain() { + if (!m_TerrainSystem) { + m_TerrainSystem = std::make_unique(); + m_EnableTerrain = true; + if (m_Renderer) { + m_TerrainSystem->Initialize(*m_Renderer); + } } + return m_TerrainSystem != nullptr; } } // namespace Engine \ No newline at end of file diff --git a/src/Scene/Scene.h b/src/Scene/Scene.h index cd7df8c..2ef8172 100644 --- a/src/Scene/Scene.h +++ b/src/Scene/Scene.h @@ -1,108 +1,111 @@ #pragma once #include -#include "../Renderer/Renderer.h" + +#include "../Camera/CameraTypes.h" #include "../Camera/OrthographicCamera.h" +#include "../Renderer/RenderableObject.h" +#include "../Renderer/Renderer.h" +#include "../Scene/SceneObject.h" +#include "../TerrainSystem/TerrainSystem.h" namespace Engine { -class SceneObject; -class Renderer; -class TerrainSystem; // Forward declaration - -/** - * @brief Represents an object in the scene hierarchy - * - * Provides transformation, rendering capabilities and parent-child relationships - * for objects in the scene graph. - */ -class SceneObject : public std::enable_shared_from_this { +class Scene { public: - std::string name; ///< Object identifier - Transform transform; ///< Local transformation - std::shared_ptr vertexArray; ///< Mesh data - std::shared_ptr material; ///< Rendering material - std::vector> children; ///< Child objects - std::weak_ptr parent; ///< Parent object + /** + * @brief Constructs a new Scene with the specified name. + * + * Initializes the scene by creating a root SceneObject and logging creation. + * Terrain is created only if enabled during OnCreate(). + * + * @param name A string representing the name of the scene. + */ + explicit Scene(const std::string &name = "Scene"); + virtual ~Scene(); + + /** @brief Called when scene is first created */ + virtual void OnCreate(); // Declaration only + + /** @brief Called when scene becomes active */ + virtual void OnActivate(); // Declaration only + + /** @brief Called when scene becomes inactive */ + virtual void OnDeactivate(); // Declaration only + + /** @brief Called every frame to update scene */ + virtual void OnUpdate(float deltaTime); // Declaration only + + /** @brief Called every frame to render scene */ + virtual void OnRender(Renderer &renderer); // Declaration only /** - * @brief Creates a scene object - * @param name Identifier for the object + * @brief Creates a new object in the scene + * @param name Object identifier + * @return Pointer to created object */ - explicit SceneObject(const std::string &name = "Object"); - virtual ~SceneObject() = default; + std::shared_ptr CreateObject(const std::string &name = "Object"); + + /** @return Scene name */ + const std::string &GetName() const { return m_Name; } /** - * @brief Adds a child object to this object - * @param child Object to add as child + * @brief Get the TerrainSystem instance + * @return TerrainSystem* Pointer to the TerrainSystem */ - void AddChild(const std::shared_ptr &child); + TerrainSystem *GetTerrainSystem() const { return m_TerrainSystem.get(); } // Added getter + + void SetCameraType(CameraType type) { + m_CameraType = type; + LOG_TRACE("Camera type set to: ", + type == CameraType::Orthographic ? "Orthographic" : "Perspective"); + } + + CameraType GetCameraType() const { return m_CameraType; } + + void SetTerrainEnabled(bool enabled) { m_EnableTerrain = enabled; } + bool IsTerrainEnabled() const { return m_EnableTerrain; } /** - * @brief Calculates the world transformation matrix - * @return Combined transformation of object and all parents + * @brief Creates and initializes a terrain system for the scene + * @return True if terrain was created or already exists */ - glm::mat4 GetWorldTransform() const; - }; + bool CreateTerrain(); // Declaration only /** - * @brief Base class for game scenes - * - * Manages a hierarchy of objects and provides lifecycle events - * for scene management. + * @brief Find a scene object by name + * @param name Name of object to find + * @return Shared pointer to found object or nullptr + */ + std::shared_ptr GetObject(const std::string &name) const { + for (const auto &obj : m_Objects) { + if (obj && obj->name == name) { + return obj; + } + } + return nullptr; + } + + void AddObject(const std::shared_ptr &object) { + if (object) { + m_Objects.push_back(object); + } + } + + protected: + /** + * @brief Renders an object and its children + * @param object Object to render + * @param renderer Renderer to use */ - class Scene : public std::enable_shared_from_this { - public: - /** - * @brief Creates a new scene - * @param name Scene identifier - */ - explicit Scene(const std::string &name = "Scene"); - virtual ~Scene(); - - /** @brief Called when scene is first created */ - virtual void OnCreate(); // Declaration only - - /** @brief Called when scene becomes active */ - virtual void OnActivate(); // Declaration only - - /** @brief Called when scene becomes inactive */ - virtual void OnDeactivate(); // Declaration only - - /** @brief Called every frame to update scene */ - virtual void OnUpdate(float deltaTime); // Declaration only - - /** @brief Called every frame to render scene */ - virtual void OnRender(Renderer &renderer); // Declaration only - - /** - * @brief Creates a new object in the scene - * @param name Object identifier - * @return Pointer to created object - */ - std::shared_ptr CreateObject(const std::string &name = "Object"); - - /** @return Scene name */ - const std::string &GetName() const { return m_Name; } - - /** - * @brief Get the TerrainSystem instance - * @return TerrainSystem* Pointer to the TerrainSystem - */ - TerrainSystem *GetTerrainSystem() const { return m_TerrainSystem.get(); } // Added getter - - protected: - /** - * @brief Renders an object and its children - * @param object Object to render - * @param renderer Renderer to use - */ - void RenderObject(const std::shared_ptr &object, Renderer &renderer); - - private: - std::string m_Name; ///< Scene identifier - std::shared_ptr m_RootObject; ///< Root of scene hierarchy - std::unique_ptr m_TerrainSystem; ///< Terrain management system - std::vector> m_Objects; // Container for all scene objects - Renderer *m_Renderer = nullptr; // Pointer to renderer instance - }; + void RenderObject(const std::shared_ptr &object, Renderer &renderer); + + private: + std::string m_Name; ///< Scene identifier + std::shared_ptr m_RootObject; ///< Root of scene hierarchy + std::unique_ptr m_TerrainSystem; ///< Terrain management system + std::vector> m_Objects; // Container for all scene objects + Renderer *m_Renderer = nullptr; // Pointer to renderer instance + CameraType m_CameraType = CameraType::Orthographic; + bool m_EnableTerrain = false; // New flag +}; } \ No newline at end of file diff --git a/src/Scene/SceneManager.h b/src/Scene/SceneManager.h index b3ab41b..408887b 100644 --- a/src/Scene/SceneManager.h +++ b/src/Scene/SceneManager.h @@ -19,14 +19,13 @@ class SceneManager { void SetActiveScene(const std::string& name) { std::lock_guard lock(sceneMutex); - LOG_TRACE("SetActiveScene called with name = ", name); if (scenes.find(name) != scenes.end()) { if (activeScene) { activeScene->OnDeactivate(); } activeScene = scenes[name]; activeScene->OnActivate(); - LOG_INFO_CONCAT("Activated scene: ", name); + LOG_TRACE_CONCAT("Activated scene: ", name); } else { LOG_ERROR_CONCAT("Failed to set active scene: ", name, " (not found)"); } diff --git a/src/Scene/SceneObject.h b/src/Scene/SceneObject.h new file mode 100644 index 0000000..59c8f77 --- /dev/null +++ b/src/Scene/SceneObject.h @@ -0,0 +1,49 @@ +#pragma once +#include + +#include "../Core/Transform.h" +#include "../Renderer/Material.h" +#include "../Renderer/RenderableObject.h" +#include "../Renderer/VertexArray.h" + +namespace Engine { + +class SceneObject : public RenderableObject { + public: + SceneObject(const std::string& objectName = "Object") : name(objectName) {} + virtual ~SceneObject() = default; + + void OnRender(Renderer& renderer) override { + if (m_Mesh && m_Material) { + renderer.Submit(m_Mesh, m_Material, m_Transform.GetModelMatrix()); + } + // Render children + for (const auto& child : children) { + if (child) child->OnRender(renderer); + } + } + + void AddChild(const std::shared_ptr& child) { children.push_back(child); } + + // Fix: Return by value instead of reference + glm::mat4 GetWorldTransform() const { return m_Transform.GetModelMatrix(); } + + void SetMesh(const std::shared_ptr& mesh) { m_Mesh = mesh; } + void SetMaterial(const std::shared_ptr& material) { m_Material = material; } + + const std::shared_ptr& GetMesh() const { return m_Mesh; } + const std::shared_ptr& GetMaterial() const { return m_Material; } + + Transform& GetTransform() { return m_Transform; } + const Transform& GetTransform() const { return m_Transform; } + + std::string name; + std::vector> children; + + protected: + Transform m_Transform; + std::shared_ptr m_Mesh; + std::shared_ptr m_Material; +}; + +} // namespace Engine \ No newline at end of file diff --git a/src/Scripting/LuaScriptSystem.cpp b/src/Scripting/LuaScriptSystem.cpp index feb4021..b72fa74 100644 --- a/src/Scripting/LuaScriptSystem.cpp +++ b/src/Scripting/LuaScriptSystem.cpp @@ -4,16 +4,23 @@ #include "../Application.h" #include "../Core/FileSystem.h" +#include "../Core/Transform.h" #include "../Events/KeyEvent.h" #include "../Events/MouseEvent.h" #include "../Scene/Scene.h" #include "../Scene/SceneManager.h" +#include "../Scene/SceneObject.h" +#include "Renderer/Renderer2D.h" +#include "Shader/ShaderLibrary.h" namespace Engine { +// Forward declare DefaultShaders +class DefaultShaders; + /** * @brief Constructs a new LuaScriptSystem instance and initializes the Lua state. - * - * Creates a unique pointer to a Lua state and opens standard Lua libraries to prepare + * + * Creates a unique pointer to a Lua state and opens standard Lua libraries to prepare * the scripting environment. The opened libraries include: * - Base library (standard Lua functions) * - Package library (module loading) @@ -22,8 +29,8 @@ namespace Engine { * - Table library (table operations) * - IO library (input/output operations) * - OS library (operating system interactions) - * - * @note This constructor sets up a fully functional Lua scripting environment ready + * + * @note This constructor sets up a fully functional Lua scripting environment ready * for engine integration and script execution. */ LuaScriptSystem::LuaScriptSystem() : m_LuaState(std::make_unique()) { @@ -33,13 +40,13 @@ LuaScriptSystem::LuaScriptSystem() : m_LuaState(std::make_unique()) /** * @brief Default destructor for the LuaScriptSystem. - * - * Cleans up the LuaScriptSystem instance, allowing for proper resource management - * when the object goes out of scope or is explicitly deleted. The default implementation - * ensures that any resources managed by the base class or compiler-generated destructors + * + * Cleans up the LuaScriptSystem instance, allowing for proper resource management + * when the object goes out of scope or is explicitly deleted. The default implementation + * ensures that any resources managed by the base class or compiler-generated destructors * are properly released. - * - * @note This destructor is defaulted, meaning the compiler will generate the default + * + * @note This destructor is defaulted, meaning the compiler will generate the default * implementation for destroying the LuaScriptSystem object. */ LuaScriptSystem::~LuaScriptSystem() = default; @@ -60,7 +67,7 @@ void LuaScriptSystem::Initialize() { RegisterEngineAPI(); } /** * @brief Registers the engine's API functions for Lua scripting. * - * This method creates a named table 'engine' in the Lua state and populates it with + * This method creates a named table 'engine' in the Lua state and populates it with * various functions that allow Lua scripts to interact with different engine systems. * * The registered API includes functionality for: @@ -73,10 +80,10 @@ void LuaScriptSystem::Initialize() { RegisterEngineAPI(); } * - Camera control * - ImGui overlay controls * - * @note Functions are registered as lambda functions that interact with various + * @note Functions are registered as lambda functions that interact with various * engine systems like SceneManager, Renderer, Application, and ImGuiOverlay. * - * @warning Some functions have safety checks to prevent operations on null systems + * @warning Some functions have safety checks to prevent operations on null systems * or inactive scenes, with appropriate error logging. * * @see LuaScriptSystem @@ -84,9 +91,40 @@ void LuaScriptSystem::Initialize() { RegisterEngineAPI(); } * @see Renderer * @see Application */ + void LuaScriptSystem::RegisterEngineAPI() { auto engine = m_LuaState->create_named_table("engine"); + // Add glm::vec3 type registration + m_LuaState->new_usertype( + "vec3", sol::constructors(), + "x", &glm::vec3::x, "y", &glm::vec3::y, "z", &glm::vec3::z); + + // Update Transform registration with proper type handling + m_LuaState->new_usertype( + "Transform", sol::constructors(), + + // Position methods + "SetPosition", + sol::overload( + static_cast(&Transform::SetPosition), + static_cast(&Transform::SetPosition)), + + // Rotation methods + "SetRotation", + sol::overload( + static_cast(&Transform::SetRotation), + static_cast(&Transform::SetRotation)), + + // Scale methods + "SetScale", + sol::overload(static_cast(&Transform::SetScale), + static_cast(&Transform::SetScale)), + + // Getters + "GetPosition", &Transform::GetPosition, "GetRotation", &Transform::GetRotation, "GetScale", + &Transform::GetScale, "GetModelMatrix", &Transform::GetModelMatrix); + // Terrain API engine.set_function("setTerrainHeight", [this](float height) { LOG_TRACE_CONCAT("[Lua] setTerrainHeight called with height = ", height); @@ -129,10 +167,11 @@ void LuaScriptSystem::RegisterEngineAPI() { engine.set_function("setCameraType", [this](const std::string& type) { if (type == "orthographic") { - Renderer::Get().SetCameraType(Renderer::CameraType::Orthographic); + Application::Get().SetCameraType(CameraType::Orthographic); } else if (type == "perspective") { - Renderer::Get().SetCameraType(Renderer::CameraType::Perspective); + Application::Get().SetCameraType(CameraType::Perspective); } + return true; }); engine.set_function("getCameraType", []() -> std::string { @@ -140,6 +179,23 @@ void LuaScriptSystem::RegisterEngineAPI() { return (type == Renderer::CameraType::Orthographic) ? "orthographic" : "perspective"; }); + // Renderer type control + engine.set_function("setRenderType", [](const std::string& type) { + if (type == "2d") { + Application::Get().SetRenderType(RenderType::Render2D); + } else if (type == "3d") { + Application::Get().SetRenderType(RenderType::Render3D); + } + }); + + engine.set_function("getRenderType", []() -> std::string { + return Application::Get().GetRenderType() == RenderType::Render2D ? "2d" : "3d"; + }); + + engine.set_function("is3D", []() -> bool { + return Application::Get().GetRenderType() == RenderType::Render3D; + }); + // Scene API engine.set_function("createScene", [](const std::string& name) -> bool { LOG_TRACE_CONCAT("[Lua] createScene called with name = ", name); @@ -252,6 +308,7 @@ void LuaScriptSystem::RegisterEngineAPI() { } else { renderer.GetCamera()->SetPosition({x, y, z}); } + return true; }); engine.set_function("setCameraRotation", [](float pitch, float yaw) { @@ -368,6 +425,91 @@ void LuaScriptSystem::RegisterEngineAPI() { keyCodes["A"] = GLFW_KEY_A; keyCodes["S"] = GLFW_KEY_S; keyCodes["D"] = GLFW_KEY_D; + + // Register 2D rendering functions + engine.set_function("renderer2DBeginScene", []() { + auto& renderer = Engine::Renderer2D::Get(); + auto camera = std::make_shared(-1.6f, 1.6f, -0.9f, 0.9f); + renderer.BeginScene(camera); + }); + + engine.set_function("renderer2DEndScene", []() { Engine::Renderer2D::Get().EndScene(); }); + + engine.set_function("drawQuad", [](float x, float y, float width, float height, float r, + float g, float b, float a) { + Engine::Renderer2D::Get().DrawQuad({x, y}, {width, height}, {r, g, b, a}); + }); + + engine.set_function("drawTexturedQuad", [](float x, float y, float width, float height, + const std::shared_ptr& texture, + float tilingFactor) { + Engine::Renderer2D::Get().DrawQuad({x, y}, {width, height}, texture, tilingFactor); + }); + + engine.set_function("createCheckerTexture", []() { + const int width = 8, height = 8; + uint8_t data[width * height * 4]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int index = (y * width + x) * 4; + if ((x + y) % 2 == 0) { + data[index + 0] = 255; + data[index + 1] = 255; + data[index + 2] = 255; + data[index + 3] = 255; + } else { + data[index + 0] = 0; + data[index + 1] = 0; + data[index + 2] = 0; + data[index + 3] = 255; + } + } + } + auto texture = std::make_shared(width, height); + texture->SetData(data, sizeof(data)); + return texture; + }); + + // Add renderer initialization function + engine.set_function("renderer2DInitialize", []() { Engine::Renderer2D::Get().Initialize(); }); + + // Fix SceneObject registration - use the proper sol::property syntax + m_LuaState->new_usertype( + "SceneObject", sol::constructors(), "name", + &SceneObject::name, + // Fix: Use proper getter/setter pair for transform property + "transform", + sol::property([](SceneObject& obj) -> Transform& { return obj.GetTransform(); }), "SetMesh", + &SceneObject::SetMesh, "SetMaterial", &SceneObject::SetMaterial, "GetMesh", + &SceneObject::GetMesh, "GetMaterial", &SceneObject::GetMaterial); + + // Keep only one createCube function implementation + engine.set_function("createCube", [](const std::string& name) -> std::shared_ptr { + auto scene = SceneManager::Get().GetActiveScene(); + if (!scene) { + LOG_ERROR("No active scene to create cube in"); + return nullptr; + } + + auto cube = scene->CreateObject(name); + if (cube) { + cube->SetMesh(AssetManager::Get().GetOrCreateCubeMesh()); + auto shader = ShaderLibrary::CreateBasicShader(); + if (shader) { + auto material = std::make_shared(shader); + material->SetVector4("u_Color", glm::vec4(1.0f)); + cube->SetMaterial(material); + } + } + return cube; + }); + + // Fix getObject function + engine.set_function("getObject", [](const std::string& name) -> std::shared_ptr { + auto scene = SceneManager::Get().GetActiveScene(); + if (!scene) return nullptr; + return std::dynamic_pointer_cast(scene->GetObject(name)); + }); } /** @@ -458,4 +600,16 @@ bool LuaScriptSystem::ExecuteFile(const std::string& originalPath) { return true; } -} // namespace Engine \ No newline at end of file + +void LuaScriptSystem::CallGlobalFunction(const std::string& functionName) { + sol::protected_function fn = (*m_LuaState)[functionName]; + if (!fn.valid()) { + return; + } + + sol::protected_function_result result = fn(); + if (!result.valid()) { + LOG_WARN("Failed to call global function: ", functionName); + } +} +} // namespace Engine diff --git a/src/Scripting/LuaScriptSystem.h b/src/Scripting/LuaScriptSystem.h index 89cdd2c..f875afa 100644 --- a/src/Scripting/LuaScriptSystem.h +++ b/src/Scripting/LuaScriptSystem.h @@ -34,6 +34,8 @@ class LuaScriptSystem { /// @return true if execution succeeded, false otherwise bool ExecuteFile(const std::string& filepath); + void CallGlobalFunction(const std::string& functionName); + private: std::unique_ptr m_LuaState; diff --git a/src/Shader/DefaultShaders.h b/src/Shader/DefaultShaders.h deleted file mode 100644 index 28fce45..0000000 --- a/src/Shader/DefaultShaders.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include "Shader.h" -#include "../Core/AssetManager.h" - -namespace Engine { - /** - * @brief Collection of predefined shader programs - * - * Provides easy access to commonly used shader programs - * with predefined vertex and fragment shader combinations. - */ - namespace DefaultShaders { - /** - * @brief Preloads frequently used shaders - */ - static void PreloadShaders() { - // These are prime candidates for caching & hot-reloading - std::vector> frequentShaders = { - {"assets/shaders/basic_mvp.vert", "assets/shaders/color.frag"}, - {"assets/shaders/basic_mvp.vert", "assets/shaders/simple_color.frag"}, - {"assets/shaders/textured.vert", "assets/shaders/textured.frag"} - }; - - for (const auto& [vert, frag] : frequentShaders) { - auto shader = Shader::CreateFromFiles(vert, frag); - if (shader) { - // Cache the shader and enable hot-reloading in debug builds - AssetManager::Get().MarkAsFrequentlyUsed(vert + ";" + frag); - } - } - } - - /** - * @brief Loads basic MVP transformation shader - * @return Shader for basic vertex transformation - */ - static std::shared_ptr LoadBasicShader() { - return std::shared_ptr(Shader::CreateFromFiles( - "assets/shaders/basic_mvp.vert", - "assets/shaders/color.frag" - )); - } - - /** - * @brief Loads simple color shader - * @return Shader for solid color rendering - */ - static std::shared_ptr LoadSimpleColorShader() { - return std::shared_ptr(Shader::CreateFromFiles( - "assets/shaders/basic_mvp.vert", - "assets/shaders/simple_color.frag" - )); - } - - /** - * @brief Loads textured rendering shader - * @return Shader for textured objects - */ - static std::shared_ptr LoadTexturedShader() { - return std::shared_ptr(Shader::CreateFromFiles( - "assets/shaders/textured.vert", - "assets/shaders/textured.frag" - )); - } - - /** - * @brief Loads pixel art shader - * @return Shader for pixel art style rendering - */ - static std::shared_ptr LoadPixelShader() { - return std::shared_ptr(Shader::CreateFromFiles( - "assets/shaders/textured.vert", // Reuse the textured vertex shader - "assets/shaders/pixel.frag" - )); - } - - /** - * @brief Loads wave dissolve effect shader - * @return Shader for wave dissolve transitions - */ - static std::shared_ptr LoadWaveDissolveShader() { - return std::shared_ptr(Shader::CreateFromFiles( - "assets/shaders/textured.vert", - "assets/shaders/wave_dissolve.frag" - )); - } - - /** - * @brief Loads gaussian blur shader - * @return Shader for blur post-processing - */ - static std::shared_ptr LoadBlurShader() { - return std::shared_ptr(Shader::CreateFromFiles( - "assets/shaders/textured.vert", - "assets/shaders/blur.frag" - )); - } - - /** - * @brief Loads terrain rendering shader - * @return Shader for terrain visualization - */ - static std::shared_ptr LoadTerrainShader() { - return LoadTexturedShader(); - } - } -} diff --git a/src/Shader/Shader.cpp b/src/Shader/Shader.cpp index 8ff6596..059b113 100644 --- a/src/Shader/Shader.cpp +++ b/src/Shader/Shader.cpp @@ -177,8 +177,8 @@ namespace Engine { } std::shared_ptr Shader::CreateFromSource(const char* vertexSrc, const char* fragmentSrc) { - auto shader = std::shared_ptr(new Shader()); // Use new instead of make_shared - if (!shader->LoadFromSource(vertexSrc, fragmentSrc)) { + auto shader = std::make_shared(vertexSrc, fragmentSrc); + if (!shader->GetProgram()) { return nullptr; } return shader; diff --git a/src/Shader/ShaderBuilder.h b/src/Shader/ShaderBuilder.h new file mode 100644 index 0000000..e69de29 diff --git a/src/Shader/ShaderFactory.cpp b/src/Shader/ShaderFactory.cpp new file mode 100644 index 0000000..e5eb6ca --- /dev/null +++ b/src/Shader/ShaderFactory.cpp @@ -0,0 +1,38 @@ +#include "ShaderFactory.h" + +namespace Engine { + +ShaderFactory::ShaderFactory() { m_Version = "#version 330 core\n"; } + +ShaderFactory& ShaderFactory::AddVertexInput(const std::string& input) { + m_VertexInputs += input + "\n"; + return *this; +} + +ShaderFactory& ShaderFactory::AddFragmentOutput(const std::string& output) { + m_FragmentOutputs += output + "\n"; + return *this; +} + +ShaderFactory& ShaderFactory::AddUniform(const std::string& uniform) { + m_Uniforms += uniform + "\n"; + return *this; +} + +ShaderFactory& ShaderFactory::SetVertexMain(const std::string& mainCode) { + m_VertexMain = mainCode; + return *this; +} + +ShaderFactory& ShaderFactory::SetFragmentMain(const std::string& mainCode) { + m_FragmentMain = mainCode; + return *this; +} + +std::shared_ptr ShaderFactory::Build() { + std::string vertexSrc = m_Version + m_VertexInputs + m_Uniforms + m_VertexMain; + std::string fragmentSrc = m_Version + m_FragmentOutputs + m_Uniforms + m_FragmentMain; + return Shader::CreateFromSource(vertexSrc.c_str(), fragmentSrc.c_str()); +} + +} // namespace Engine diff --git a/src/Shader/ShaderFactory.h b/src/Shader/ShaderFactory.h new file mode 100644 index 0000000..48e7d3b --- /dev/null +++ b/src/Shader/ShaderFactory.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Shader.h" + +namespace Engine { + +class ShaderFactory { + public: + ShaderFactory(); + ShaderFactory& AddVertexInput(const std::string& input); + ShaderFactory& AddFragmentOutput(const std::string& output); + ShaderFactory& AddUniform(const std::string& uniform); + ShaderFactory& SetVertexMain(const std::string& mainCode); + ShaderFactory& SetFragmentMain(const std::string& mainCode); + std::shared_ptr Build(); + + private: + std::string m_Version; + std::string m_VertexInputs; + std::string m_FragmentOutputs; + std::string m_Uniforms; + std::string m_VertexMain; + std::string m_FragmentMain; +}; + +} // namespace Engine diff --git a/src/Shader/ShaderLibrary.cpp b/src/Shader/ShaderLibrary.cpp new file mode 100644 index 0000000..538fac4 --- /dev/null +++ b/src/Shader/ShaderLibrary.cpp @@ -0,0 +1,94 @@ +#include "ShaderLibrary.h" + +#include "ShaderFactory.h" + +namespace Engine { + +// Initialize static members +std::unordered_map> ShaderLibrary::s_Shaders; + +std::shared_ptr ShaderLibrary::CreateBasicShader() { + const std::string name = "basic"; + if (Exists(name)) return Get(name); + + auto shader = Load(name, "assets/shaders/basic.vert", "assets/shaders/basic.frag"); +#ifdef ENGINE_DEBUG + shader->EnableHotReload("assets/shaders/basic.vert", "assets/shaders/basic.frag"); +#endif + return shader; +} + +std::shared_ptr ShaderLibrary::CreateColorShader() { + const std::string name = "color"; + if (Exists(name)) return Get(name); + + auto shader = Load(name, "assets/shaders/basic.vert", "assets/shaders/simple_color.frag"); +#ifdef ENGINE_DEBUG + shader->EnableHotReload("assets/shaders/basic.vert", "assets/shaders/simple_color.frag"); +#endif + return shader; +} + +std::shared_ptr ShaderLibrary::CreateTextureShader() { + const std::string name = "texture"; + if (Exists(name)) return Get(name); + + auto shader = Load(name, "assets/shaders/texture.vert", "assets/shaders/texture.frag"); +#ifdef ENGINE_DEBUG + shader->EnableHotReload("assets/shaders/texture.vert", "assets/shaders/texture.frag"); +#endif + return shader; +} + +std::shared_ptr ShaderLibrary::CreateBatchRenderer2DShader() { + const std::string name = "batch_renderer_2d"; + if (Exists(name)) return Get(name); + + auto shader = Load(name, "assets/shaders/batch_renderer_2d.vert", + "assets/shaders/batch_renderer_2d.frag"); +#ifdef ENGINE_DEBUG + shader->EnableHotReload("assets/shaders/batch_renderer_2d.vert", + "assets/shaders/batch_renderer_2d.frag"); +#endif + return shader; +} + +std::shared_ptr ShaderLibrary::Load(const std::string& name, const std::string& vertexPath, + const std::string& fragmentPath) { + auto shader = Shader::CreateFromFiles(vertexPath, fragmentPath); + if (!shader) { + LOG_ERROR("Failed to load shader from files: ", vertexPath, " and ", fragmentPath); + return nullptr; + } + s_Shaders[name] = shader; + return shader; +} + +std::shared_ptr ShaderLibrary::LoadFromSource(const std::string& name, + const std::string& vertexSrc, + const std::string& fragmentSrc) { + auto shader = Shader::CreateFromSource(vertexSrc.c_str(), fragmentSrc.c_str()); + if (!shader) { + LOG_ERROR("Failed to create shader from source for: ", name); + return nullptr; + } + s_Shaders[name] = shader; + return shader; +} + +std::shared_ptr ShaderLibrary::Get(const std::string& name) { + auto it = s_Shaders.find(name); + if (it == s_Shaders.end()) { + LOG_ERROR("Shader not found: ", name); + return nullptr; + } + return it->second; +} + +bool ShaderLibrary::Exists(const std::string& name) { + return s_Shaders.find(name) != s_Shaders.end(); +} + +std::shared_ptr ShaderLibrary::LoadTexturedShader() { return CreateTextureShader(); } + +} // namespace Engine \ No newline at end of file diff --git a/src/Shader/ShaderLibrary.h b/src/Shader/ShaderLibrary.h new file mode 100644 index 0000000..669e1d2 --- /dev/null +++ b/src/Shader/ShaderLibrary.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "Shader.h" + +namespace Engine { + +class ShaderLibrary { + public: + static std::shared_ptr Load(const std::string& name, const std::string& vertexPath, + const std::string& fragmentPath); + static std::shared_ptr LoadFromSource(const std::string& name, + const std::string& vertexSrc, + const std::string& fragmentSrc); + static std::shared_ptr Get(const std::string& name); + static bool Exists(const std::string& name); + + // Factory methods for built-in shaders + static std::shared_ptr CreateBasicShader(); + static std::shared_ptr CreateColorShader(); + static std::shared_ptr CreateTextureShader(); + static std::shared_ptr CreateBatchRenderer2DShader(); + static std::shared_ptr LoadTexturedShader(); + + private: + static std::unordered_map> s_Shaders; +}; + +} // namespace Engine \ No newline at end of file diff --git a/src/TerrainSystem/TerrainSystem.cpp b/src/TerrainSystem/TerrainSystem.cpp index 622a0da..58b16c5 100644 --- a/src/TerrainSystem/TerrainSystem.cpp +++ b/src/TerrainSystem/TerrainSystem.cpp @@ -12,7 +12,7 @@ #include "BlockTypes.h" #include "Core/AssetManager.h" #include "Renderer/MeshTemplates.h" -#include "Shader/DefaultShaders.h" +#include "Shader/ShaderLibrary.h" namespace Engine { /** @@ -21,29 +21,34 @@ namespace Engine { * Initializes terrain with random seed, loads textures and shaders, * and generates initial terrain mesh. */ - TerrainSystem::TerrainSystem() - : m_NoiseGen(std::random_device{}()) - { - // Load terrain texture - m_TerrainTexture = Texture::Create("assets/textures/terrain_atlas.png"); - - m_TerrainShader = DefaultShaders::LoadTexturedShader(); - m_TerrainMaterial = std::make_shared(m_TerrainShader); - m_TerrainMaterial->SetTexture("u_Texture", m_TerrainTexture); - m_TerrainMaterial->SetVector4("u_Color", glm::vec4(1.0f)); - - // Update transform to better initial values - m_TerrainTransform.position = glm::vec3(-8.0f, -10.0f, -8.0f); - m_TerrainTransform.scale = glm::vec3(1.0f); +TerrainSystem::TerrainSystem() : m_NoiseGen(std::random_device{}()) { + // Load terrain texture + m_TerrainTexture = Texture::Create("assets/textures/terrain_atlas.png"); - // Set default terrain parameters - m_BaseHeight = 0.0f; - m_HeightScale = 20.0f; - m_NoiseScale = 4.0f; + // Get shader with proper error handling - GenerateMesh(); + m_TerrainShader = ShaderLibrary::LoadTexturedShader(); + if (!m_TerrainShader) { + LOG_ERROR("Failed to load textured shader for terrain"); + return; } + m_TerrainMaterial = std::make_shared(m_TerrainShader); + m_TerrainMaterial->SetTexture("u_Texture", m_TerrainTexture); + m_TerrainMaterial->SetVector4("u_Color", glm::vec4(1.0f)); + + // Update transform to better initial values + m_TerrainTransform.SetPosition(-8.0f, -10.0f, -8.0f); + m_TerrainTransform.SetScale(1.0f, 1.0f, 1.0f); + + // Set default terrain parameters + m_BaseHeight = 0.0f; + m_HeightScale = 20.0f; + m_NoiseScale = 4.0f; + + GenerateMesh(); +} + // Core functionality void TerrainSystem::Initialize(Renderer& renderer) { // Already initialized in constructor, but could be used for renderer-specific setup @@ -63,10 +68,10 @@ namespace Engine { void TerrainSystem::Update(float deltaTime) { static bool logged = false; if (!logged) { - LOG_TRACE_CONCAT("Terrain Transform - Position: (", m_TerrainTransform.position.x, ", ", - m_TerrainTransform.position.y, ", ", m_TerrainTransform.position.z, - ") Scale: (", m_TerrainTransform.scale.x, ", ", - m_TerrainTransform.scale.y, ", ", m_TerrainTransform.scale.z, ")"); + auto pos = m_TerrainTransform.GetPosition(); + auto scale = m_TerrainTransform.GetScale(); + LOG_TRACE_CONCAT("Terrain Transform - Position: (", pos.x, ", ", pos.y, ", ", pos.z, + ") Scale: (", scale.x, ", ", scale.y, ", ", scale.z, ")"); logged = true; } } diff --git a/src/TerrainSystem/TerrainSystem.h b/src/TerrainSystem/TerrainSystem.h index 4bc3843..3e53b60 100644 --- a/src/TerrainSystem/TerrainSystem.h +++ b/src/TerrainSystem/TerrainSystem.h @@ -9,6 +9,7 @@ #include "Noise/ValueNoise/ValueNoise.h" #include "Noise/VoidNoise/VoidNoise.h" #include "Renderer/Material.h" +#include "Renderer/RenderObject.h" #include "Renderer/Renderer.h" #include "Renderer/VertexArray.h" #include "VoxelTerrain.h" @@ -73,6 +74,13 @@ namespace Engine { void GenerateMesh(); void GenerateMesh(uint32_t seed); + void Shutdown() { + // Clean up terrain resources + m_TerrainMesh.reset(); + m_TerrainMaterial.reset(); + m_IsInitialized = false; + } + private: std::unique_ptr m_Terrain; ///< Voxel data container std::shared_ptr m_TerrainVA; ///< Terrain vertex array @@ -86,6 +94,9 @@ namespace Engine { float m_HeightScale = 32.0f; ///< Height variation scale float m_NoiseScale = 0.05f; ///< Noise variation scale + bool m_IsInitialized = false; + std::shared_ptr m_TerrainMesh; + private: NoiseGenerator m_NoiseGen; }; diff --git a/src/UI/ImGuiOverlay.cpp b/src/UI/ImGuiOverlay.cpp index 8c942d4..dca7812 100644 --- a/src/UI/ImGuiOverlay.cpp +++ b/src/UI/ImGuiOverlay.cpp @@ -139,16 +139,15 @@ namespace Engine { * @note Controls use ImGui drag float widgets with a sensitivity of 0.1 units per drag * @note Position, rotation, and scale are modified directly on the object's transform */ - void ImGuiOverlay::RenderTransformControls(RenderObject& renderObject) { - if (!m_ShowTransformControls) return; - if (ImGui::Begin("Transform Controls")) { - auto& transform = renderObject.GetTransform(); - - ImGui::DragFloat3("Position", &transform.position[0], 0.1f); - ImGui::DragFloat3("Rotation", &transform.rotation[0], 0.1f); - ImGui::DragFloat3("Scale", &transform.scale[0], 0.1f); + void ImGuiOverlay::RenderTransformControls(Engine::RenderObject& object) { + auto& transform = object.GetTransform(); + auto& data = transform.GetData(); // Get mutable reference to transform data + + if (ImGui::CollapsingHeader("Transform")) { + ImGui::DragFloat3("Position", &data.position[0], 0.1f); + ImGui::DragFloat3("Rotation", &data.rotation[0], 0.1f); + ImGui::DragFloat3("Scale", &data.scale[0], 0.1f); } - ImGui::End(); } /**