Compare commits

...

2 Commits

Author SHA1 Message Date
OusmBlueNinja
e81041fa2b Adde Profiling and user Setting saving 2025-04-18 17:23:59 -05:00
OusmBlueNinja
e409bed53f somthing, i forgoted 2025-04-18 11:50:25 -05:00
25 changed files with 1314 additions and 394 deletions

View File

@ -15,19 +15,19 @@ Collapsed=0
[Window][Inspector] [Window][Inspector]
Pos=890,19 Pos=890,19
Size=390,505 Size=390,292
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x0000000B,0
[Window][Scene Tree] [Window][Scene Tree]
Pos=0,19 Pos=0,19
Size=263,701 Size=376,366
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000009,0
[Window][Viewport] [Window][Viewport]
Pos=265,19 Pos=378,19
Size=623,329 Size=510,218
Collapsed=0 Collapsed=0
DockId=0x00000007,0 DockId=0x00000007,0
@ -42,10 +42,10 @@ Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][Console] [Window][Console]
Pos=265,350 Pos=0,387
Size=623,370 Size=376,333
Collapsed=0 Collapsed=0
DockId=0x00000008,0 DockId=0x0000000A,0
[Window][Tilemap Editor] [Window][Tilemap Editor]
Pos=265,19 Pos=265,19
@ -53,14 +53,48 @@ Size=1263,674
Collapsed=0 Collapsed=0
DockId=0x00000007,1 DockId=0x00000007,1
[Window][Profiler]
Pos=378,239
Size=510,481
Collapsed=0
DockId=0x00000008,0
[Window][Profiler Timeline]
Pos=265,69
Size=623,651
Collapsed=0
DockId=0x00000008,1
[Window][Profiler (Unity Style)]
Pos=265,430
Size=623,290
Collapsed=0
DockId=0x00000008,1
[Window][Profiler Timeline View]
Pos=265,526
Size=1263,651
Collapsed=0
DockId=0x00000008,1
[Window][Color Correction]
Pos=890,313
Size=390,211
Collapsed=0
DockId=0x0000000C,0
[Docking][Data] [Docking][Data]
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X
DockNode ID=0x00000003 Parent=0x11111111 SizeRef=888,1158 Split=X DockNode ID=0x00000003 Parent=0x11111111 SizeRef=888,1158 Split=X
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=263,701 HiddenTabBar=1 Selected=0x12EF0F59 DockNode ID=0x00000001 Parent=0x00000003 SizeRef=376,701 Split=Y Selected=0x12EF0F59
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=623,701 Split=Y Selected=0xC450F867 DockNode ID=0x00000009 Parent=0x00000001 SizeRef=376,605 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,329 CentralNode=1 Selected=0xC450F867 DockNode ID=0x0000000A Parent=0x00000001 SizeRef=376,551 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,370 Selected=0xEA83D666 DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1150,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,675 CentralNode=1 Selected=0xC450F867
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,481 Selected=0x9B5D3198
DockNode ID=0x00000004 Parent=0x11111111 SizeRef=390,1158 Split=Y Selected=0x36DC96AB DockNode ID=0x00000004 Parent=0x11111111 SizeRef=390,1158 Split=Y Selected=0x36DC96AB
DockNode ID=0x00000005 Parent=0x00000004 SizeRef=407,835 HiddenTabBar=1 Selected=0x36DC96AB DockNode ID=0x00000005 Parent=0x00000004 SizeRef=407,835 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000000B Parent=0x00000005 SizeRef=390,483 Selected=0x36DC96AB
DockNode ID=0x0000000C Parent=0x00000005 SizeRef=390,350 Selected=0xA873C17F
DockNode ID=0x00000006 Parent=0x00000004 SizeRef=407,321 HiddenTabBar=1 Selected=0x3FC1A724 DockNode ID=0x00000006 Parent=0x00000004 SizeRef=407,321 HiddenTabBar=1 Selected=0x3FC1A724

View File

@ -39,6 +39,7 @@ cxx: g++
cxxflags: cxxflags:
- -std=c++20 - -std=c++20
- -Wall - -Wall
- -g
# Auto-detect libraries and headers # Auto-detect libraries and headers
auto_libs: auto_libs:

View File

@ -1 +1,10 @@
[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\Entitys\Object.o src\build\utils\EngineConfig.o src\build\utils\ExceptionHandler.o src\build\utils\FileDialog.o src\build\utils\GameObjectsList.o src\build\utils\Logging.o src\build\utils\Shader.o src\build\utils\UID.o src\build\utils\utils.o src\build\imgui\imgui.o src\build\imgui\imgui_demo.o src\build\imgui\imgui_draw.o src\build\imgui\imgui_impl_glfw.o src\build\imgui\imgui_impl_opengl3.o src\build\imgui\imgui_tables.o src\build\imgui\imgui_widgets.o src\build\lapi.o src\build\lauxlib.o src\build\lbaselib.o src\build\lcode.o src\build\lcorolib.o src\build\lctype.o src\build\ldblib.o src\build\ldebug.o src\build\ldo.o src\build\ldump.o src\build\lfunc.o src\build\lgc.o src\build\linit.o src\build\liolib.o src\build\llex.o src\build\lmathlib.o src\build\lmem.o src\build\loadlib.o src\build\lobject.o src\build\lopcodes.o src\build\loslib.o src\build\lparser.o src\build\lstate.o src\build\lstring.o src\build\lstrlib.o src\build\ltable.o src\build\ltablib.o src\build\ltm.o src\build\lua.o src\build\luac.o src\build\lundump.o src\build\lutf8lib.o src\build\lvm.o src\build\lzio.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto [COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Engine.cpp -o src\build\Engine.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\main.cpp -o src\build\main.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Renderer.cpp -o src\build\Renderer.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\CameraComponent.cpp -o src\build\Components\CameraComponent.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\LightComponent.cpp -o src\build\Components\LightComponent.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\ScriptComponent.cpp -o src\build\Components\ScriptComponent.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\SpriteComponent.cpp -o src\build\Components\SpriteComponent.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\TextComonent.cpp -o src\build\Components\TextComonent.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Components\TilemapComponent.cpp -o src\build\Components\TilemapComponent.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\Entitys\Object.cpp -o src\build\Entitys\Object.o

16
src/assets/lua/slow.lua Normal file
View File

@ -0,0 +1,16 @@
Engine.DebugLua(true)
local function sleep (a)
local sec = tonumber(os.clock() + a);
while (os.clock() < sec) do
end
end
function OnInit()
end
function OnUpdate(dt)
sleep(0.001)
end

View File

@ -1,12 +1,13 @@
engine_version: 0.1.0 engine_version: 0.1.0
scene_name: lighting_test_2 scene_name: lighting_test_2
scene_hash: a0ae96d593e1990a6f3944184fd6595738629af0dcb2d4cdd83011a91aaa738c scene_hash: 8f8260f09292105727f7f8afe890565a9feeaa92a1ca6d7fa55790a5019c67d7
format_version: 1 format_version: 1
objects: objects:
- name: Tiles - name: Tiles
uid: f5e01f7892874a67b662633650b41dbd uid: f5e01f7892874a67b662633650b41dbd
id: 3 id: 3
position: [0, 0] position: [0, 0]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: [] components: []
@ -15,6 +16,7 @@ objects:
uid: 7dc3bbf8affb4844ae3801f03857b904 uid: 7dc3bbf8affb4844ae3801f03857b904
id: 4 id: 4
position: [0, 0] position: [0, 0]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -27,6 +29,7 @@ objects:
uid: 13d8988343354e3c8a1f51c03ed40cda uid: 13d8988343354e3c8a1f51c03ed40cda
id: 5 id: 5
position: [1024, 0] position: [1024, 0]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -39,6 +42,7 @@ objects:
uid: cff28abe7e3b455ab9b756acc84cd2d7 uid: cff28abe7e3b455ab9b756acc84cd2d7
id: 6 id: 6
position: [0, 1024] position: [0, 1024]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -51,6 +55,7 @@ objects:
uid: 98967eb30e5b429b992766d8062b7c17 uid: 98967eb30e5b429b992766d8062b7c17
id: 7 id: 7
position: [1024, 1024] position: [1024, 1024]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -62,7 +67,8 @@ objects:
- name: Logo - name: Logo
uid: c4ce6f16dfb347b0ae0ac67f5881b243 uid: c4ce6f16dfb347b0ae0ac67f5881b243
id: 8 id: 8
position: [2048, 0] position: [2104, 56]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -74,7 +80,8 @@ objects:
- name: Carbooon Fobar - name: Carbooon Fobar
uid: 5ea269572751401da6d86519d3513b7d uid: 5ea269572751401da6d86519d3513b7d
id: 9 id: 9
position: [2559.30005, 1562] position: [2048, 3070]
rotation: 0
layer: 1 layer: 1
visable: true visable: true
components: components:
@ -86,7 +93,8 @@ objects:
- name: Mud - name: Mud
uid: a36b71937ba349bd8e6414f75be9ee16 uid: a36b71937ba349bd8e6414f75be9ee16
id: 10 id: 10
position: [0, 2561.80005] position: [0, 3070]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -99,6 +107,7 @@ objects:
uid: 051b338a725a4076ad53ad8fa00c5f4e uid: 051b338a725a4076ad53ad8fa00c5f4e
id: 12 id: 12
position: [-556, 951] position: [-556, 951]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: [] components: []
@ -107,6 +116,7 @@ objects:
uid: 6afde2dd47aa4557b6afb1a607c99dc8 uid: 6afde2dd47aa4557b6afb1a607c99dc8
id: 13 id: 13
position: [512, 1024] position: [512, 1024]
rotation: 0
layer: 2 layer: 2
visable: true visable: true
components: components:
@ -128,6 +138,7 @@ objects:
uid: 0f950d76d24b4dc18f54cab2c3aaaf9a uid: 0f950d76d24b4dc18f54cab2c3aaaf9a
id: 14 id: 14
position: [1024, 512] position: [1024, 512]
rotation: 0
layer: 2 layer: 2
visable: true visable: true
components: components:
@ -149,6 +160,7 @@ objects:
uid: 09f722f51c7c4b0f98de3a0a16d127c4 uid: 09f722f51c7c4b0f98de3a0a16d127c4
id: 15 id: 15
position: [250, 250] position: [250, 250]
rotation: 0
layer: 2 layer: 2
visable: true visable: true
components: components:
@ -170,6 +182,7 @@ objects:
uid: d4fb425522d84a8cbbd7d1415bcd93df uid: d4fb425522d84a8cbbd7d1415bcd93df
id: 16 id: 16
position: [500, 500] position: [500, 500]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:
@ -187,6 +200,7 @@ objects:
uid: 895c655f3dda4aec9f2a354c1276c53e uid: 895c655f3dda4aec9f2a354c1276c53e
id: 14 id: 14
position: [0, 0] position: [0, 0]
rotation: 0
layer: 0 layer: 0
visable: true visable: true
components: components:

View File

@ -6,6 +6,11 @@ out vec4 FragColor;
uniform sampler2D uTex; uniform sampler2D uTex;
uniform sampler2D uNormalMap; uniform sampler2D uNormalMap;
uniform float uRotation;
uniform float uBrightness;
uniform float uSaturation;
uniform float uGamma;
#define MAX_LIGHTS 512 #define MAX_LIGHTS 512
@ -17,16 +22,22 @@ uniform float uLightRadius[MAX_LIGHTS];
void main() void main()
{ {
vec4 texColor = texture(uTex, vUV); // Rotate UV 90° clockwise
vec2 rotatedUV = vec2(vUV.y, 1.0 - vUV.x);
vec4 texColor = texture(uTex, rotatedUV);
if (texColor.a < 0.1) if (texColor.a < 0.1)
discard; discard;
// unpack normal map and convert from [0,1] to [-1,1] vec3 n = texture(uNormalMap, rotatedUV).rgb * 2.0 - 1.0;
vec3 n = texture(uNormalMap, vUV).rgb * 2.0 - 1.0;
// invert the green channel for OpenGL
n.y = -n.y; n.y = -n.y;
vec3 normal = normalize(n);
float c = cos(uRotation);
float s = sin(uRotation);
mat2 rot = mat2(c, -s, s, c);
n.xy = rot * n.xy;
vec3 normal = normalize(n);
vec3 finalLight = vec3(0.0); vec3 finalLight = vec3(0.0);
for (int i = 0; i < uLightCount; ++i) for (int i = 0; i < uLightCount; ++i)
@ -37,9 +48,8 @@ void main()
if (dist < uLightRadius[i]) if (dist < uLightRadius[i])
{ {
vec2 lightDir2D = normalize(lightVec); vec2 lightDir2D = normalize(lightVec);
vec3 lightDir = normalize(vec3(lightDir2D, 1.0)); // pseudo-3D vec3 lightDir = normalize(vec3(lightDir2D, 1.0));
float attenuation = smoothstep(uLightRadius[i], 0.0, dist);
float attenuation = 1.0 - dist / uLightRadius[i];
float diff = max(dot(normal, lightDir), 0.0); float diff = max(dot(normal, lightDir), 0.0);
vec3 light = uLightColor[i] * diff * attenuation * uLightIntensity[i]; vec3 light = uLightColor[i] * diff * attenuation * uLightIntensity[i];
finalLight += light; finalLight += light;
@ -47,5 +57,15 @@ void main()
} }
vec3 result = texColor.rgb * finalLight; vec3 result = texColor.rgb * finalLight;
result *= uBrightness;
float gray = dot(result, vec3(0.299, 0.587, 0.114));
result = mix(vec3(gray), result, uSaturation);
result = pow(result, vec3(1.0 / uGamma));
result = clamp(result, 0.0, 1.0);
FragColor = vec4(result, texColor.a); FragColor = vec4(result, texColor.a);
} }

View File

@ -1,7 +1,7 @@
#version 430 core #version 330 core
layout(location = 0) in vec2 aPos; layout (location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV; layout (location = 1) in vec2 aUV;
out vec2 vUV; out vec2 vUV;
out vec2 vFragScreenPos; out vec2 vFragScreenPos;
@ -9,17 +9,22 @@ out vec2 vFragScreenPos;
uniform vec2 uPos; uniform vec2 uPos;
uniform vec2 uSize; uniform vec2 uSize;
uniform vec2 uScreen; uniform vec2 uScreen;
uniform float uRotation;//r
void main() void main()
{ {
vec2 centered = aPos - vec2(0.5);
float s = cos(uRotation);
float c = sin(uRotation);
mat2 rot = mat2(c, -s, s, c);
vec2 rotated = rot * (centered * uSize);
vec2 finalPos = uPos + rotated;
vUV = aUV; vUV = aUV;
vFragScreenPos = finalPos;
vec2 worldPos = uPos + aPos * uSize; vec2 ndc = finalPos / uScreen * 2.0 - 1.0;
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); // Y-flip
vFragScreenPos = worldPos;
vec2 ndc = (worldPos / uScreen) * 2.0 - 1.0;
ndc.y *= -1.0;
gl_Position = vec4(ndc, 0.0, 1.0);
} }

View File

@ -1,10 +1,15 @@
#version 430 core #version 330 core
in vec2 vUV; in vec2 vUV;
out vec4 FragColor; out vec4 FragColor;
uniform sampler2D uTex;
void main() {
vec4 color = texture(uTex, vUV);
uniform sampler2D uTex;
void main()
{
vec2 rotatedUV = vec2(vUV.y, 1.0 - vUV.x);
vec4 color = texture(uTex, rotatedUV);
if (color.a < 0.01) if (color.a < 0.01)
discard; discard;

View File

@ -1,13 +1,28 @@
#version 430 core #version 330 core
layout (location = 0) in vec2 aPos; layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUV; layout (location = 1) in vec2 aUV;
out vec2 vUV; out vec2 vUV;
out vec2 vFragScreenPos;
uniform vec2 uPos; uniform vec2 uPos;
uniform vec2 uSize; uniform vec2 uSize;
uniform vec2 uScreen; uniform vec2 uScreen;
void main() { uniform float uRotation; // radians
vec2 worldPos = aPos * uSize + uPos;
void main()
{
vec2 centered = aPos - vec2(0.5);
float s = cos(uRotation);
float c = sin(uRotation);
mat2 rot = mat2(c, -s, s, c);
vec2 rotated = rot * (centered * uSize);
vec2 finalPos = uPos + rotated;
vFragScreenPos = finalPos;
vUV = aUV; vUV = aUV;
vec2 ndc = (worldPos / uScreen) * 2.0 - 1.0;
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); vec2 ndc = finalPos / uScreen * 2.0 - 1.0;
gl_Position = vec4(ndc.x, -ndc.y, 0.0, 1.0); // Y flip for OpenGL
} }

View File

@ -18,6 +18,7 @@ public:
virtual std::string GetName() const = 0; virtual std::string GetName() const = 0;
virtual void Save(YAML::Emitter& out) const = 0; virtual void Save(YAML::Emitter& out) const = 0;
virtual void Load(const YAML::Node& node) = 0; virtual void Load(const YAML::Node& node) = 0;

View File

@ -10,78 +10,114 @@
#include "../Components/TextComponent.h" #include "../Components/TextComponent.h"
#include "../Components/TilemapComponent.h" #include "../Components/TilemapComponent.h"
#include "../utils/Profiler.h"
#include <lua.hpp> #include <lua.hpp>
#include <memory> #include <memory>
#include <cstring> #include <cstring>
static bool luaDebugEnabled = false; static bool luaDebugEnabled = false;
static bool old_state = false;
struct LuaObjectWrapper { struct LuaObjectWrapper
Object* obj; {
Object *obj;
}; };
#define LUA_OBJECT_MT "LuaObjectMeta" #define LUA_OBJECT_MT "LuaObjectMeta"
struct LuaVector2 { struct LuaVector2
{
float x, y; float x, y;
}; };
#define LUA_VECTOR2_MT "LuaVector2Meta" #define LUA_VECTOR2_MT "LuaVector2Meta"
ScriptComponent::ScriptComponent(Object* owner) : Component(owner), L(nullptr) {} ScriptComponent::ScriptComponent(Object *owner) : Component(owner), L(nullptr) {}
ScriptComponent::~ScriptComponent() { if (L) lua_close(L); } ScriptComponent::~ScriptComponent()
{
if (L)
lua_close(L);
}
void ScriptComponent::SetScriptPath(const std::string& path) { void ScriptComponent::SetScriptPath(const std::string &path)
{
scriptPath = path; scriptPath = path;
ReloadScript(); ReloadScript();
} }
const std::string& ScriptComponent::GetScriptPath() const { return scriptPath; } const std::string &ScriptComponent::GetScriptPath() const { return scriptPath; }
// Logging bindings // Logging bindings
static int Lua_LogInfo(lua_State* L) { static int Lua_LogInfo(lua_State *L)
{
Logger::LogInfo("[Lua] %s", lua_tostring(L, 1)); Logger::LogInfo("[Lua] %s", lua_tostring(L, 1));
return 0; return 0;
} }
static int Lua_LogError(lua_State* L) { static int Lua_LogError(lua_State *L)
{
Logger::LogError("[Lua] %s", lua_tostring(L, 1)); Logger::LogError("[Lua] %s", lua_tostring(L, 1));
return 0; return 0;
} }
static int Lua_LogDebug(lua_State* L) { static int Lua_LogDebug(lua_State *L)
{
Logger::LogDebug("[Lua] %s", lua_tostring(L, 1)); Logger::LogDebug("[Lua] %s", lua_tostring(L, 1));
return 0; return 0;
} }
static int Lua_DebugLua(lua_State* L) { static int Lua_DebugLua(lua_State *L)
{
luaDebugEnabled = lua_toboolean(L, 1); luaDebugEnabled = lua_toboolean(L, 1);
Logger::LogInfo("[Lua] DebugLua = %s", luaDebugEnabled ? "true" : "false"); if (old_state != luaDebugEnabled)
{
Logger::LogInfo("[Lua] DebugLua(%s)", luaDebugEnabled ? "true" : "false");
}
old_state = luaDebugEnabled;
return 0; return 0;
} }
// Component resolver // Component resolver
static Component* GetComponentByName(Object* obj, const std::string& type) { static Component *GetComponentByName(Object *obj, const std::string &type)
if (type == "SpriteComponent") return obj->GetComponent<SpriteComponent>().get(); {
if (type == "CameraComponent") return obj->GetComponent<CameraComponent>().get(); PROFILE_SCOPE("LUA_GetComponentByName");
if (type == "LightComponent") return obj->GetComponent<LightComponent>().get();
if (type == "TilemapComponent") return obj->GetComponent<TilemapComponent>().get(); if (type == "SpriteComponent")
if (type == "TextComponent") return obj->GetComponent<TextComponent>().get(); return obj->GetComponent<SpriteComponent>().get();
if (type == "ScriptComponent") return obj->GetComponent<ScriptComponent>().get(); if (type == "CameraComponent")
return obj->GetComponent<CameraComponent>().get();
if (type == "LightComponent")
return obj->GetComponent<LightComponent>().get();
if (type == "TilemapComponent")
return obj->GetComponent<TilemapComponent>().get();
if (type == "TextComponent")
return obj->GetComponent<TextComponent>().get();
if (type == "ScriptComponent")
return obj->GetComponent<ScriptComponent>().get();
return nullptr; return nullptr;
} }
// Object:GetComponent("Type") // Object:GetComponent("Type")
static int Lua_Object_GetComponent(lua_State* L) { static int Lua_Object_GetComponent(lua_State *L)
auto* wrapper = (LuaObjectWrapper*)luaL_checkudata(L, 1, LUA_OBJECT_MT); {
const char* type = luaL_checkstring(L, 2); PROFILE_SCOPE("Lua_Object_GetComponent");
Component* comp = GetComponentByName(wrapper->obj, type); auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
if (comp) lua_pushlightuserdata(L, comp); const char *type = luaL_checkstring(L, 2);
else lua_pushnil(L);
Component *comp = GetComponentByName(wrapper->obj, type);
if (comp)
lua_pushlightuserdata(L, comp);
else
lua_pushnil(L);
return 1; return 1;
} }
// Object:GetPosition() // Object:GetPosition()
static int Lua_Object_GetPosition(lua_State* L) { static int Lua_Object_GetPosition(lua_State *L)
auto* wrapper = (LuaObjectWrapper*)luaL_checkudata(L, 1, LUA_OBJECT_MT); {
PROFILE_SCOPE("Lua_Object_GetPosition");
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
glm::vec2 pos = wrapper->obj->GetLocalPosition(); glm::vec2 pos = wrapper->obj->GetLocalPosition();
LuaVector2* vec = (LuaVector2*)lua_newuserdata(L, sizeof(LuaVector2)); LuaVector2 *vec = (LuaVector2 *)lua_newuserdata(L, sizeof(LuaVector2));
vec->x = pos.x; vec->x = pos.x;
vec->y = pos.y; vec->y = pos.y;
luaL_getmetatable(L, LUA_VECTOR2_MT); luaL_getmetatable(L, LUA_VECTOR2_MT);
@ -90,27 +126,37 @@ static int Lua_Object_GetPosition(lua_State* L) {
} }
// Object:SetPosition(Vector2) // Object:SetPosition(Vector2)
static int Lua_Object_SetPosition(lua_State* L) { static int Lua_Object_SetPosition(lua_State *L)
auto* wrapper = (LuaObjectWrapper*)luaL_checkudata(L, 1, LUA_OBJECT_MT); {
auto* vec = (LuaVector2*)luaL_checkudata(L, 2, LUA_VECTOR2_MT); PROFILE_SCOPE("Lua_Object_SetPosition");
wrapper->obj->SetLocalPosition({ vec->x, vec->y });
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
auto *vec = (LuaVector2 *)luaL_checkudata(L, 2, LUA_VECTOR2_MT);
wrapper->obj->SetLocalPosition({vec->x, vec->y});
return 0; return 0;
} }
// __index for Object // __index for Object
static int Lua_Object_Index(lua_State* L) { static int Lua_Object_Index(lua_State *L)
const char* key = luaL_checkstring(L, 2); {
PROFILE_SCOPE("Lua_Object_Index");
const char *key = luaL_checkstring(L, 2);
lua_getfield(L, lua_upvalueindex(1), key); lua_getfield(L, lua_upvalueindex(1), key);
return 1; return 1;
} }
void RegisterObjectType(lua_State* L) { void RegisterObjectType(lua_State *L)
{
luaL_newmetatable(L, LUA_OBJECT_MT); luaL_newmetatable(L, LUA_OBJECT_MT);
lua_newtable(L); // method table lua_newtable(L); // method table
lua_pushcfunction(L, Lua_Object_GetComponent); lua_setfield(L, -2, "GetComponent"); lua_pushcfunction(L, Lua_Object_GetComponent);
lua_pushcfunction(L, Lua_Object_GetPosition); lua_setfield(L, -2, "GetPosition"); lua_setfield(L, -2, "GetComponent");
lua_pushcfunction(L, Lua_Object_SetPosition); lua_setfield(L, -2, "SetPosition"); lua_pushcfunction(L, Lua_Object_GetPosition);
lua_setfield(L, -2, "GetPosition");
lua_pushcfunction(L, Lua_Object_SetPosition);
lua_setfield(L, -2, "SetPosition");
lua_pushcclosure(L, Lua_Object_Index, 1); lua_pushcclosure(L, Lua_Object_Index, 1);
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
@ -118,19 +164,25 @@ void RegisterObjectType(lua_State* L) {
lua_pop(L, 1); lua_pop(L, 1);
} }
static void PushObject(lua_State* L, Object* obj) { static void PushObject(lua_State *L, Object *obj)
auto* wrapper = (LuaObjectWrapper*)lua_newuserdata(L, sizeof(LuaObjectWrapper)); {
auto *wrapper = (LuaObjectWrapper *)lua_newuserdata(L, sizeof(LuaObjectWrapper));
wrapper->obj = obj; wrapper->obj = obj;
luaL_getmetatable(L, LUA_OBJECT_MT); luaL_getmetatable(L, LUA_OBJECT_MT);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
} }
// Engine.GetObjectByTag(name) // Engine.GetObjectByTag(name)
static int Lua_GetObjectByTag(lua_State* L) { static int Lua_GetObjectByTag(lua_State *L)
const char* name = luaL_checkstring(L, 1); {
for (const auto& obj : objects) { PROFILE_SCOPE("Lua_GetObjectByTag");
const char *name = luaL_checkstring(L, 1);
for (const auto &obj : objects)
{
auto found = FindByTagRecursive(obj, name); auto found = FindByTagRecursive(obj, name);
if (found) { if (found)
{
PushObject(L, found.get()); PushObject(L, found.get());
return 1; return 1;
} }
@ -140,57 +192,86 @@ static int Lua_GetObjectByTag(lua_State* L) {
} }
// Vector2(x, y) // Vector2(x, y)
static int Lua_Vector2_New(lua_State* L) { static int Lua_Vector2_New(lua_State *L)
LuaVector2* vec = (LuaVector2*)lua_newuserdata(L, sizeof(LuaVector2)); {
vec->x = (float)luaL_optnumber(L, 1, 0); PROFILE_SCOPE("Lua_Vector2_New");
vec->y = (float)luaL_optnumber(L, 2, 0);
luaL_getmetatable(L, LUA_VECTOR2_MT); LuaVector2 *vec = static_cast<LuaVector2 *>(lua_newuserdata(L, sizeof(LuaVector2)));
lua_setmetatable(L, -2);
int nargs = lua_gettop(L);
vec->x = nargs >= 1 ? (float)lua_tonumber(L, 1) : 0.0f;
vec->y = nargs >= 2 ? (float)lua_tonumber(L, 2) : 0.0f;
luaL_setmetatable(L, LUA_VECTOR2_MT);
return 1; return 1;
} }
static int Lua_Vector2_Index(lua_State* L) { static int Lua_Vector2_Index(lua_State *L)
auto* vec = (LuaVector2*)luaL_checkudata(L, 1, LUA_VECTOR2_MT); {
const char* key = luaL_checkstring(L, 2); PROFILE_SCOPE("Lua_Vector2_Index");
if (strcmp(key, "x") == 0) lua_pushnumber(L, vec->x);
else if (strcmp(key, "y") == 0) lua_pushnumber(L, vec->y); auto *vec = (LuaVector2 *)luaL_checkudata(L, 1, LUA_VECTOR2_MT);
else lua_pushnil(L); const char *key = luaL_checkstring(L, 2);
if (strcmp(key, "x") == 0)
lua_pushnumber(L, vec->x);
else if (strcmp(key, "y") == 0)
lua_pushnumber(L, vec->y);
else
lua_pushnil(L);
return 1; return 1;
} }
static int Lua_Vector2_NewIndex(lua_State* L) { static int Lua_Vector2_NewIndex(lua_State *L)
auto* vec = (LuaVector2*)luaL_checkudata(L, 1, LUA_VECTOR2_MT); {
const char* key = luaL_checkstring(L, 2); PROFILE_SCOPE("Lua_Vector2_NewIndex");
auto *vec = (LuaVector2 *)luaL_checkudata(L, 1, LUA_VECTOR2_MT);
const char *key = luaL_checkstring(L, 2);
float value = (float)luaL_checknumber(L, 3); float value = (float)luaL_checknumber(L, 3);
if (strcmp(key, "x") == 0) vec->x = value; if (strcmp(key, "x") == 0)
else if (strcmp(key, "y") == 0) vec->y = value; vec->x = value;
else if (strcmp(key, "y") == 0)
vec->y = value;
return 0; return 0;
} }
void RegisterVector2Type(lua_State* L) { void RegisterVector2Type(lua_State *L)
{
luaL_newmetatable(L, LUA_VECTOR2_MT); luaL_newmetatable(L, LUA_VECTOR2_MT);
lua_pushcfunction(L, Lua_Vector2_Index); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, Lua_Vector2_Index);
lua_pushcfunction(L, Lua_Vector2_NewIndex); lua_setfield(L, -2, "__newindex"); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, Lua_Vector2_NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1); lua_pop(L, 1);
lua_pushcfunction(L, Lua_Vector2_New); lua_pushcfunction(L, Lua_Vector2_New);
lua_setglobal(L, "Vector2"); lua_setglobal(L, "Vector2");
} }
void ScriptComponent::RegisterEngineBindings() { void ScriptComponent::RegisterEngineBindings()
{
lua_newtable(L); lua_newtable(L);
lua_pushcfunction(L, Lua_LogInfo); lua_setfield(L, -2, "LogInfo"); lua_pushcfunction(L, Lua_LogInfo);
lua_pushcfunction(L, Lua_LogError); lua_setfield(L, -2, "LogError"); lua_setfield(L, -2, "LogInfo");
lua_pushcfunction(L, Lua_LogDebug); lua_setfield(L, -2, "LogDebug"); lua_pushcfunction(L, Lua_LogError);
lua_pushcfunction(L, Lua_GetObjectByTag); lua_setfield(L, -2, "GetObjectByTag"); lua_setfield(L, -2, "LogError");
lua_pushcfunction(L, Lua_DebugLua); lua_setfield(L, -2, "DebugLua"); lua_pushcfunction(L, Lua_LogDebug);
lua_setfield(L, -2, "LogDebug");
lua_pushcfunction(L, Lua_GetObjectByTag);
lua_setfield(L, -2, "GetObjectByTag");
lua_pushcfunction(L, Lua_DebugLua);
lua_setfield(L, -2, "DebugLua");
lua_setglobal(L, "Engine"); lua_setglobal(L, "Engine");
} }
void ScriptComponent::ReloadScript() { void ScriptComponent::ReloadScript()
if (scriptPath.empty()) return; {
if (scriptPath.empty())
return;
if (L) lua_close(L); if (L)
lua_close(L);
L = luaL_newstate(); L = luaL_newstate();
luaL_openlibs(L); luaL_openlibs(L);
@ -198,54 +279,79 @@ void ScriptComponent::ReloadScript() {
RegisterVector2Type(L); RegisterVector2Type(L);
RegisterEngineBindings(); RegisterEngineBindings();
if (luaL_dofile(L, scriptPath.c_str())) { Logger::LogVerbose("[Lua] Loading Script from file.");
if (luaL_dofile(L, scriptPath.c_str()))
{
Logger::LogError("[Lua] %s", lua_tostring(L, -1)); Logger::LogError("[Lua] %s", lua_tostring(L, -1));
RecoverableError("Failed to load Lua script: " + scriptPath, Create::Exceptions::ComponentLoad).Handle(); RecoverableError("Failed to load Lua script: " + scriptPath, Create::Exceptions::ComponentLoad).Handle();
return; return;
} }
if (luaDebugEnabled)
{
Logger::LogVerbose("[Lua][call] OnInit()");
}
lua_getglobal(L, "OnInit"); lua_getglobal(L, "OnInit");
if (lua_isfunction(L, -1)) { if (lua_isfunction(L, -1))
if (lua_pcall(L, 0, 0, 0) != LUA_OK) { {
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
Logger::LogError("[Lua] %s", lua_tostring(L, -1)); Logger::LogError("[Lua] %s", lua_tostring(L, -1));
RecoverableError("OnInit failed: " + scriptPath, Create::Exceptions::ComponentLoad).Handle(); RecoverableError("OnInit failed: " + scriptPath, Create::Exceptions::ComponentLoad).Handle();
} }
} else { }
else
{
lua_pop(L, 1); lua_pop(L, 1);
} }
} }
void ScriptComponent::OnUpdate(float dt) { void ScriptComponent::OnUpdate(float dt)
if (!L) return; {
PROFILE_SCOPE("ScriptComponent::OnUpdate");
if (!L)
return;
lua_getglobal(L, "OnUpdate"); lua_getglobal(L, "OnUpdate");
if (lua_isfunction(L, -1)) { if (lua_isfunction(L, -1))
{
lua_pushnumber(L, dt); lua_pushnumber(L, dt);
if (lua_pcall(L, 1, 0, 0) != LUA_OK) { if (lua_pcall(L, 1, 0, 0) != LUA_OK)
{
Logger::LogError("[Lua] %s", lua_tostring(L, -1)); Logger::LogError("[Lua] %s", lua_tostring(L, -1));
RecoverableError("OnUpdate failed in: " + scriptPath, Create::Exceptions::ComponentLoad).Handle(); RecoverableError("OnUpdate failed in: " + scriptPath, Create::Exceptions::ComponentLoad).Handle();
} }
} else { }
else
{
lua_pop(L, 1); lua_pop(L, 1);
} }
} }
void ScriptComponent::Save(YAML::Emitter& out) const { void ScriptComponent::Save(YAML::Emitter &out) const
{
out << YAML::BeginMap; out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << "ScriptComponent"; out << YAML::Key << "type" << YAML::Value << "ScriptComponent";
out << YAML::Key << "scriptPath" << YAML::Value << scriptPath; out << YAML::Key << "scriptPath" << YAML::Value << scriptPath;
out << YAML::EndMap; out << YAML::EndMap;
} }
void ScriptComponent::Load(const YAML::Node& node) { void ScriptComponent::Load(const YAML::Node &node)
try { {
if (!node["scriptPath"]) { try
{
if (!node["scriptPath"])
{
RecoverableError("Missing 'scriptPath' in ScriptComponent", Create::Exceptions::MissingField).Handle(); RecoverableError("Missing 'scriptPath' in ScriptComponent", Create::Exceptions::MissingField).Handle();
return; return;
} }
scriptPath = node["scriptPath"].as<std::string>(); scriptPath = node["scriptPath"].as<std::string>();
ReloadScript(); ReloadScript();
} catch (const YAML::Exception& e) { }
catch (const YAML::Exception &e)
{
RecoverableError("YAML error in ScriptComponent::Load: " + std::string(e.what()), Create::Exceptions::ComponentLoad).Handle(); RecoverableError("YAML error in ScriptComponent::Load: " + std::string(e.what()), Create::Exceptions::ComponentLoad).Handle();
} }
} }

View File

@ -8,6 +8,8 @@
#include "../utils/utils.h" #include "../utils/utils.h"
struct ImageCacheEntry { struct ImageCacheEntry {
unsigned int textureID; unsigned int textureID;
glm::vec2 size; glm::vec2 size;

View File

@ -12,6 +12,7 @@
#include "utils/EngineConfig.h" #include "utils/EngineConfig.h"
#include "utils/GameObjectsList.h" #include "utils/GameObjectsList.h"
#include "utils/Profiler.h"
#include <GL/glew.h> #include <GL/glew.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
@ -24,6 +25,10 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <vector> #include <vector>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <format>
#include <memory> #include <memory>
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
@ -40,14 +45,15 @@ static glm::vec2 cameraPos = {0, 0};
static float cameraZoom = 1.0f; static float cameraZoom = 1.0f;
static ImVec2 lastMousePos = {}; static ImVec2 lastMousePos = {};
static const std::string tempScenePath = "__tmp_scene.yaml"; float g_fps = 0.0f;
static float g_fps = 0.0f; static const std::string tempScenePath = "__tmp_scene.yaml";
GLFWwindow *window = nullptr; GLFWwindow *window = nullptr;
Engine::Engine() Engine::Engine()
{ {
Init(); Init();
} }
@ -56,46 +62,251 @@ Engine::~Engine()
Shutdown(); Shutdown();
} }
void DrawProfilerTimelineBars(const ProfileNode &node,
int depth,
float baseOffsetX,
float frameDuration,
ImVec2 origin)
{
constexpr float rowHeight = 24.0f;
constexpr float textPadding = 5.0f;
// compute bar rect
float startRel = node.visualStartMs / frameDuration;
float durRel = node.visualDurationMs / frameDuration;
float x = origin.x + startRel * baseOffsetX;
float width = std::fmax(1.0f, durRel * baseOffsetX);
float y = origin.y + depth * rowHeight;
ImVec2 barMin(x, y);
ImVec2 barMax(x + width, y + rowHeight - 4.0f);
// draw the bar
size_t hash = std::hash<std::string>{}(node.name);
ImU32 color = ImColor::HSV((hash % 1000) / 1000.0f, 0.6f, 0.85f);
ImDrawList *draw = ImGui::GetWindowDrawList();
draw->AddRectFilled(barMin, barMax, color, 3.0f);
draw->AddRect(barMin, barMax, IM_COL32_BLACK, 3.0f);
// how much room we have for text
float availW = width - textPadding * 2;
const char *textToDraw = nullptr;
// 1) try the short "XX.XX ms"
char timeBuf[16];
int tn = std::snprintf(timeBuf, sizeof(timeBuf), "%.2f ms", node.visualDurationMs);
if (tn > 0)
{
ImVec2 sz = ImGui::CalcTextSize(timeBuf);
if (sz.x <= availW)
{
textToDraw = timeBuf;
}
}
// 2) only if the short fits, see if full fits
if (textToDraw == timeBuf)
{
char fullBuf[64];
int fn = std::snprintf(fullBuf, sizeof(fullBuf),
"%s (%.2f ms)",
node.name.c_str(),
node.visualDurationMs);
if (fn > 0)
{
ImVec2 fsz = ImGui::CalcTextSize(fullBuf);
if (fsz.x <= availW)
{
textToDraw = fullBuf;
}
}
}
// 3) draw if we decided on something
if (textToDraw)
{
draw->AddText(
ImVec2(barMin.x + textPadding,
barMin.y),
IM_COL32_WHITE,
textToDraw);
}
// tooltip
ImVec2 mouse = ImGui::GetMousePos();
if (mouse.x >= barMin.x && mouse.x <= barMax.x &&
mouse.y >= barMin.y && mouse.y <= barMax.y)
{
ImGui::SetTooltip(
"%s\nCurrent: %.3f ms\nSmoothed: %.3f ms",
node.name.c_str(),
node.durationMs,
node.visualDurationMs);
}
// recurse
for (auto &child : node.children)
DrawProfilerTimelineBars(child,
depth + 1,
baseOffsetX,
frameDuration,
origin);
}
static ProfileNode GetAveragedFrameCopy(const ProfileNode &src)
{
ProfileNode averaged = src;
averaged.visualStartMs = src.startMs;
averaged.visualDurationMs = src.durationMs;
averaged.children.clear();
for (const auto &child : src.children)
averaged.children.push_back(GetAveragedFrameCopy(child));
return averaged;
}
void ShowProfilerTimeline()
{
static ProfileNode cachedAveragedFrame;
static double lastUpdateTime = 0.0;
static double updateInterval = 0.25;
static bool freezeView = false;
const double now = ImGui::GetTime();
const ProfileNode *latest = profiler.GetLatestFrame();
ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar);
// --- Controls ---
if (g_engineConfig.settings.profile_enabled)
{
ImGui::Checkbox("Freeze View", &freezeView);
ImGui::SameLine();
ImGui::SetNextItemWidth(100);
float interval = static_cast<float>(updateInterval);
if (ImGui::DragFloat("Update Interval", &interval, 0.05f, 0.05f, 5.0f))
updateInterval = interval;
updateInterval = std::clamp(updateInterval, 0.05, 5.0);
if (!freezeView && latest && (now - lastUpdateTime) >= updateInterval)
{
cachedAveragedFrame = GetAveragedFrameCopy(*latest);
lastUpdateTime = now;
}
if (cachedAveragedFrame.durationMs <= 0.0f)
{
ImGui::Text("Waiting for profiler data...");
ImGui::End();
return;
}
const float rowHeight = 24.0f;
const float timelineHeight = 400.0f;
float frameDuration = cachedAveragedFrame.visualDurationMs;
float timelineWidth = ImGui::GetContentRegionAvail().x * 0.95f;
ImGui::BeginChild("TimelineScroll", ImVec2(0, timelineHeight));
ImVec2 origin = ImGui::GetCursorScreenPos();
DrawProfilerTimelineBars(cachedAveragedFrame, 0, timelineWidth, frameDuration, origin);
ImGui::Dummy(ImVec2(timelineWidth, 32 * rowHeight));
ImGui::EndChild();
ImGui::Text("Frame Duration (Avg): %.2f ms | View Update Rate: %.2fs", frameDuration, updateInterval);
}
else
{
ImGui::Text("Profiling Disabled.");
}
ImGui::End();
}
void Engine::ShowDebugOverlay(float deltaTime) void Engine::ShowDebugOverlay(float deltaTime)
{ {
// === Performance Window ===
static std::vector<float> fpsHistory; static std::vector<float> fpsHistory;
static const int maxHistory = 100; static const int maxFpsHistory = 100;
static float timer = 0; static float fpsTimer = 0.0f;
float fps = 1.0f / deltaTime; float fps = 1.0f / deltaTime;
g_fps = fps; g_fps = fps;
if (fpsHistory.size() >= maxHistory) if (fpsHistory.size() >= maxFpsHistory)
fpsHistory.erase(fpsHistory.begin()); fpsHistory.erase(fpsHistory.begin());
if (timer >= 0.05) if (fpsTimer >= 0.05f)
{ {
timer = 0; fpsTimer = 0;
fpsHistory.push_back(fps); fpsHistory.push_back(fps);
} }
timer += deltaTime; fpsTimer += deltaTime;
ImGui::Begin("Performance Info", nullptr, ImGui::Begin("Performance Info", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize);
ImGuiWindowFlags_NoDecoration);
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), "Performance"); ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), "Performance");
ImGui::Separator(); ImGui::Separator();
ImGui::Text("FPS: %.1f", fps); ImGui::Text("FPS: %.1f", fps);
ImGui::Text("Delta Time: %.4f s", deltaTime); ImGui::Text("Delta Time: %.4f s", deltaTime);
ImGui::Text("Frame Time: %.2f ms", deltaTime * 1000.0f); ImGui::Text("Frame Time: %.2f ms", deltaTime * 1000.0f);
ImGui::PlotLines("##FPS", fpsHistory.data(), fpsHistory.size(), 0, "FPS History", 0.0f, 144.0f, ImVec2(-1, 50));
if (!fpsHistory.empty())
{
float maxFps = *std::max_element(fpsHistory.begin(), fpsHistory.end());
maxFps = std::max(maxFps * 1.1f, 60.0f);
ImGui::PlotLines("##FPS", fpsHistory.data(), fpsHistory.size(), 0, "FPS History",
0.0f, maxFps, ImVec2(-1, 50));
}
else
{
ImGui::Text("FPS data unavailable");
}
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Renderer"); ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Renderer");
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Draw Calls: %d", Renderer::GetDrawCallCount()); ImGui::Text("Draw Calls: %d", Renderer::GetDrawCallCount());
ImGui::Text("Scene Lights: %d/%d", Renderer::GetLightsCount(), g_engineConfig.gl_maxLight); ImGui::Text("Lua OnUpdate Calls: %d", m_OnUpdateCalls);
ImGui::Text("Scene Lights: %d / %d", Renderer::GetLightsCount(), g_engineConfig.gl_maxLight);
ImGui::Text("Reserved Draws: %d", m_Reserved_draws); ImGui::Text("Reserved Draws: %d", m_Reserved_draws);
ImGui::Text("Camera Zoom: %.2f", cameraZoom); ImGui::Text("Camera Zoom: %.2f", cameraZoom);
ImGui::Text("Camera Pos: (%.1f, %.1f)", cameraPos.x, cameraPos.y); ImGui::Text("Camera Pos: (%.1f, %.1f)", cameraPos.x, cameraPos.y);
ImGui::End(); ImGui::End();
} }
void ShowColorCorrectionWindow()
{
PROFILE_ENGINE_SCOPE("Engine::ShowColorCorrectionWindow");
ColorCorrection* cc = Renderer::GetColorCorrection();
if (!cc)
return;
ImGui::Begin("Color Correction");
bool changed = false;
changed |= ImGui::SliderFloat("Brightness", &cc->brightness, 0.0f, 2.0f, "%.2f");
changed |= ImGui::SliderFloat("Saturation", &cc->saturation, 0.0f, 2.0f, "%.2f");
changed |= ImGui::SliderFloat("Gamma", &cc->gamma, 0.1f, 4.0f, "%.2f");
ImGui::Spacing();
if (ImGui::Button("Reset to Default"))
{
*cc = ColorCorrection();
}
ImGui::SameLine();
ImGui::End();
}
void Engine::Init() void Engine::Init()
{ {
glfwInit(); glfwInit();
@ -108,6 +319,8 @@ void Engine::Init()
glfwSwapInterval(0); // No VSync glfwSwapInterval(0); // No VSync
glewInit(); glewInit();
g_engineConfig.LoadFromFile();
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
@ -123,10 +336,16 @@ void Engine::Init()
objects.push_back(obj); objects.push_back(obj);
selected = obj; selected = obj;
Logger::LogVerbose("Resverving Objects");
m_toDraw.reserve(1024);
m_scriptUpdates.reserve(256);
m_collectStack.reserve(1024);
Logger::LogInfo("Initialized Engine"); Logger::LogInfo("Initialized Engine");
} }
void DrawInspectorUI(std::shared_ptr<Object> selected) void DrawInspectorUI(std::shared_ptr<Object> selected)
{ {
PROFILE_ENGINE_SCOPE("Engine::DrawInspectorUI");
ImGui::Begin("Inspector"); ImGui::Begin("Inspector");
if (!selected) if (!selected)
@ -167,6 +386,10 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (ImGui::DragFloat2("Position", &pos.x, 0.1f)) if (ImGui::DragFloat2("Position", &pos.x, 0.1f))
selected->SetLocalPosition(pos); selected->SetLocalPosition(pos);
float rotation = selected->GetLocalRotation();
if (ImGui::DragFloat("Rotation", &rotation, 1.0f))
selected->SetLocalRotation(rotation);
int layer = selected->layer; int layer = selected->layer;
if (ImGui::InputInt("Layer", &layer)) if (ImGui::InputInt("Layer", &layer))
selected->layer = layer; selected->layer = layer;
@ -342,24 +565,109 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
} }
} }
// Add this method to Engine:
void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom)
{
m_toDraw.clear();
m_scriptUpdates.clear();
m_collectStack.clear();
for (auto &root : objects)
if (!root->GetParent())
m_collectStack.push_back(root);
while (!m_collectStack.empty())
{
auto obj = m_collectStack.back();
m_collectStack.pop_back();
if (!obj->GetVisable())
continue;
m_toDraw.push_back(obj.get());
if (auto light = obj->GetComponent<LightComponent>())
{
glm::vec2 world = obj->GetWorldPosition();
glm::vec2 screen = (world - camPos) * camZoom + glm::vec2(Renderer::GetSize()) * 0.5f;
Renderer::AddLight(screen,
light->GetColor(),
light->GetIntensity(),
light->GetRadius() * camZoom);
}
if (playing)
{
if (auto script = obj->GetComponent<ScriptComponent>())
m_scriptUpdates.push_back(script.get());
}
for (auto &child : obj->GetChildren())
m_collectStack.push_back(child);
}
}
void Engine::Run() void Engine::Run()
{ {
while (!glfwWindowShouldClose(window)) while (!glfwWindowShouldClose(window))
{ {
if (g_engineConfig.settings.profile_editor)
{
profiler.BeginFrame();
}
profiler.BeginEngineSection("glfwPollEvents");
glfwPollEvents(); glfwPollEvents();
profiler.EndEngineSection();
profiler.BeginEngineSection("NewFrame");
profiler.BeginEngineSection("ImGui_ImplOpenGL3_NewFrame");
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();
profiler.EndEngineSection();
profiler.BeginEngineSection("ImGui_ImplGlfw_NewFrame");
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
profiler.EndEngineSection();
profiler.BeginEngineSection("ImGui::NewFrame");
ImGui::NewFrame(); ImGui::NewFrame();
profiler.EndEngineSection();
profiler.BeginEngineSection("ImGui::DockSpaceOverViewport");
ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID); ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID);
profiler.EndEngineSection();
profiler.BeginEngineSection("glfwGetTime");
float currentTime = glfwGetTime(); float currentTime = glfwGetTime();
static float lastTime = currentTime; static float lastTime = currentTime;
float deltaTime = currentTime - lastTime; float deltaTime = currentTime - lastTime;
lastTime = currentTime; lastTime = currentTime;
profiler.EndEngineSection();
profiler.EndEngineSection();
profiler.BeginEngineSection("Engine::ShowDebugOverlay");
ShowDebugOverlay(deltaTime); ShowDebugOverlay(deltaTime);
profiler.EndEngineSection();
profiler.BeginEngineSection("Logger::Draw");
Logger::Draw(); Logger::Draw();
profiler.EndEngineSection();
profiler.BeginEngineSection("BeginMainMenuBar");
if (ImGui::BeginMainMenuBar()) if (ImGui::BeginMainMenuBar())
{ {
@ -369,12 +677,12 @@ void Engine::Run()
if (!playing) if (!playing)
{ {
Logger::LogVerbose("[RestoreScene] Saving original scene"); Logger::LogVerbose("[RestoreScene] Saving original scene");
SaveScene(tempScenePath); SaveState();
} }
else else
{ {
Logger::LogVerbose("[RestoreScene] Reloading original scene"); Logger::LogVerbose("[RestoreScene] Reloading original scene");
LoadScene(tempScenePath); LoadState();
} }
selected = nullptr; selected = nullptr;
playing = !playing; playing = !playing;
@ -402,9 +710,24 @@ void Engine::Run()
if (ImGui::BeginMenu("Options")) if (ImGui::BeginMenu("Options"))
{ {
// simple checkbox never closes the menu when you click it
ImGui::Checkbox("Enable Lighting", &g_engineConfig.lighting_enabled); if (ImGui::BeginMenu("Settings"))
{
ImGui::Checkbox("Enable Lighting", &g_engineConfig.settings.lighting_enabled);
ImGui::Checkbox("Enable Gizmos", &g_engineConfig.settings.draw_gizmos); ImGui::Checkbox("Enable Gizmos", &g_engineConfig.settings.draw_gizmos);
ImGui::Checkbox("Profiling", &g_engineConfig.settings.profile_enabled);
if (g_engineConfig.settings.profile_enabled)
{
ImGui::Checkbox("Profile Engine", &g_engineConfig.settings.profile_editor);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Windows"))
{
ImGui::Checkbox("Color Correction", &g_engineConfig.settings.show_color_correction_window);
ImGui::EndMenu();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
@ -426,6 +749,14 @@ void Engine::Run()
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
profiler.EndEngineSection();
if (g_engineConfig.settings.show_color_correction_window)
ShowColorCorrectionWindow();
{
PROFILE_ENGINE_SCOPE("Engine::DrawSceneTree");
ImGui::Begin("Scene Tree"); ImGui::Begin("Scene Tree");
@ -463,6 +794,7 @@ void Engine::Run()
DrawObjectNode(obj); DrawObjectNode(obj);
ImGui::End(); ImGui::End();
}
DrawInspectorUI(selected); DrawInspectorUI(selected);
@ -506,95 +838,92 @@ void Engine::Run()
} }
} }
// Resize
profiler.BeginEngineSection("Renderer::Resize");
Renderer::Resize((int)size.x, (int)size.y); Renderer::Resize((int)size.x, (int)size.y);
profiler.EndEngineSection();
// Begin
profiler.BeginEngineSection("Renderer::Begin");
Renderer::Begin(); Renderer::Begin();
profiler.EndEngineSection();
std::vector<std::shared_ptr<Object>> toDraw; // Reserve (no profiling)
std::vector<ScriptComponent *> scriptUpdates; m_toDraw.reserve(m_Reserved_draws);
toDraw.reserve(m_Reserved_draws);
m_Reserved_draws = 0; // Reset
Renderer::DrawEditorGrid(cameraPos, cameraZoom);
// clear arrays each frame…
toDraw.clear();
scriptUpdates.clear();
m_Reserved_draws = 0; m_Reserved_draws = 0;
// recursive collector now skips invisible objects (and their children) // Draw Editor Grid
std::function<void(const std::shared_ptr<Object> &)> collect = profiler.BeginEngineSection("Draw Editor Grid");
[&](const std::shared_ptr<Object> &obj) Renderer::DrawEditorGrid(cameraPos, cameraZoom);
profiler.EndEngineSection();
// Clear temporary arrays
m_toDraw.clear();
m_scriptUpdates.clear();
m_Reserved_draws = 0;
// Collect Objects
profiler.BeginEngineSection("Collect Objects");
collectObjects(playing, cameraPos, cameraZoom);
profiler.EndEngineSection();
// Sort Objects
profiler.BeginEngineSection("Sort Objects");
if (m_toDraw.size() > 1)
{ {
// if this object isnt visible, skip it and its children std::sort(m_toDraw.begin(), m_toDraw.end(),
if (!obj->GetVisable()) [](auto const &a, auto const &b)
return;
// still visible → collect for drawing
toDraw.push_back(obj);
m_Reserved_draws += 1;
// Collect lights
if (auto light = obj->GetComponent<LightComponent>())
{
glm::vec2 world = obj->GetWorldPosition();
glm::vec2 screen = (world - cameraPos) * cameraZoom + glm::vec2(Renderer::GetSize().x * 0.5f,
Renderer::GetSize().y * 0.5f);
Renderer::AddLight(screen,
light->GetColor(),
light->GetIntensity(),
light->GetRadius() * cameraZoom);
Renderer::DrawGizmoCircle(
world,
light->GetRadius(),
64, // segments
light->GetColor(), // circle color
cameraPos,
cameraZoom);
}
// Collect scripts
if (playing)
{
if (auto script = obj->GetComponent<ScriptComponent>())
scriptUpdates.push_back(script.get());
}
// recurse into children
for (const auto &child : obj->GetChildren())
collect(child);
};
// Traverse only root objects
for (const auto &obj : objects)
if (!obj->GetParent())
collect(obj);
// Sort drawables by layer then Y
std::sort(toDraw.begin(), toDraw.end(), [](auto const &a, auto const &b)
{ {
if (a->layer != b->layer) if (a->layer != b->layer)
return a->layer < b->layer; return a->layer < b->layer;
return a->GetWorldPosition().y < b->GetWorldPosition().y; }); return a->GetWorldPosition().y < b->GetWorldPosition().y;
});
}
profiler.EndEngineSection();
// Run script updates if (!g_engineConfig.settings.profile_editor)
for (auto *script : scriptUpdates) profiler.BeginFrame();
m_OnUpdateCalls = 0;
profiler.BeginSection("Script Updates");
for (auto *script : m_scriptUpdates)
{
profiler.BeginSection("Script: " + script->GetOwner()->GetName());
script->OnUpdate(deltaTime); script->OnUpdate(deltaTime);
m_OnUpdateCalls++;
profiler.EndSection();
}
profiler.EndSection();
// Draw all sprites profiler.BeginSection("Render");
for (const auto &obj : toDraw) for (auto *obj : m_toDraw)
{ {
if (auto sprite = obj->GetComponent<SpriteComponent>()) if (auto spritePtr = obj->GetComponent<SpriteComponent>())
{ {
glm::vec2 worldPos = obj->GetWorldPosition(); profiler.BeginSection("Draw Sprite: " + obj->GetName());
Renderer::DrawSprite(sprite.get(), worldPos, cameraZoom, cameraPos); Renderer::DrawSprite(spritePtr.get(),
obj->GetWorldPosition(),
cameraZoom,
cameraPos);
profiler.EndSection();
} }
} }
profiler.EndSection();
// Finish frame profiling
if (!g_engineConfig.settings.profile_editor)
profiler.EndFrame();
// End renderer
profiler.BeginEngineSection("Renderer::End");
Renderer::End(); Renderer::End();
profiler.EndEngineSection();
// Display render target texture
GLuint texID = Renderer::GetRenderTexture(); GLuint texID = Renderer::GetRenderTexture();
ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0)); ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0));
ImGui::End(); ImGui::End();
if (ImGui::BeginPopup("RenameObject")) if (ImGui::BeginPopup("RenameObject"))
@ -627,7 +956,13 @@ void Engine::Run()
ImGui::EndPopup(); ImGui::EndPopup();
} }
if (g_engineConfig.settings.profile_editor)
{
profiler.EndFrame(); // Finish frame
}
// ImGui render // ImGui render
ShowProfilerTimeline();
ImGui::Render(); ImGui::Render();
int w, h; int w, h;
glfwGetFramebufferSize(window, &w, &h); glfwGetFramebufferSize(window, &w, &h);
@ -656,6 +991,8 @@ void Engine::Run()
void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj) void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj)
{ {
PROFILE_ENGINE_SCOPE("Engine::DrawObjectNode");
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanAvailWidth |
(obj == selected ? ImGuiTreeNodeFlags_Selected : 0); (obj == selected ? ImGuiTreeNodeFlags_Selected : 0);
@ -813,8 +1150,53 @@ void Engine::LoadScene(const std::string &path)
Logger::LogInfo("[LoadScene] Loaded scene: %s", root["scene_name"].as<std::string>().c_str()); Logger::LogInfo("[LoadScene] Loaded scene: %s", root["scene_name"].as<std::string>().c_str());
} }
void Engine::LoadState()
{
if (savedStateYAML.empty())
{
Logger::LogWarning("[LoadState] No scene state in memory.");
return;
}
YAML::Node objectArray;
try
{
objectArray = YAML::Load(savedStateYAML);
}
catch (const std::exception &e)
{
Logger::LogError("[LoadState] Failed to parse saved scene: %s", e.what());
return;
}
objects.clear();
for (const auto &node : objectArray)
{
auto obj = std::make_shared<Object>("[DefaultObject]");
obj->Load(node);
objects.push_back(obj);
}
Logger::LogVerbose("[LoadState] Scene restored");
}
void Engine::SaveState()
{
YAML::Emitter sceneData;
sceneData << YAML::BeginSeq;
for (const auto &obj : objects)
obj->Save(sceneData);
sceneData << YAML::EndSeq;
savedStateYAML = sceneData.c_str();
Logger::LogVerbose("[SaveState] Scene serialized (%zu bytes)", savedStateYAML.size());
}
void Engine::Shutdown() void Engine::Shutdown()
{ {
g_engineConfig.SaveToFile();
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown(); ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
@ -822,4 +1204,5 @@ void Engine::Shutdown()
glfwTerminate(); glfwTerminate();
std::filesystem::remove(tempScenePath); std::filesystem::remove(tempScenePath);
} }

View File

@ -1,30 +1,40 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <vector> // ← for std::vector<>
#include <glm/vec2.hpp> // ← for glm::vec2
class Object; class Object;
class ScriptComponent;
class Engine { class Engine
{
public: public:
Engine(); Engine();
~Engine(); ~Engine();
void Run(); void Run();
std::shared_ptr<Object> GetObjectByTag(const std::string &tag); std::shared_ptr<Object> GetObjectByTag(const std::string &tag);
private: private:
void Init(); void Init();
void Shutdown(); void Shutdown();
void DrawObjectNode(const std::shared_ptr<Object>& obj); // make sure this matches Engine.cpp void DrawObjectNode(const std::shared_ptr<Object> &obj);
void SaveScene(const std::string& path); void SaveScene(const std::string &path);
void LoadScene(const std::string& path); void LoadScene(const std::string &path);
void ShowDebugOverlay(float deltaTime); void ShowDebugOverlay(float deltaTime);
void collectObjects(bool playing, const glm::vec2& camPos, float camZoom);
void SaveState();
void LoadState();
int m_Reserved_draws; int m_Reserved_draws;
std::vector<Object *> m_toDraw;
std::vector<ScriptComponent *> m_scriptUpdates;
int m_OnUpdateCalls;
std::vector<std::shared_ptr<Object>> m_collectStack;
}; };

View File

@ -13,7 +13,7 @@
#include <algorithm> #include <algorithm>
Object::Object(const std::string &name) Object::Object(const std::string &name)
: name(name), localPosition(0.0f, 0.0f), uid() {} : name(name), localPosition(0.0f, 0.0f), localRotationDeg(0.0f), uid(), visable(true) {}
Object::~Object() {} Object::~Object() {}
@ -45,6 +45,41 @@ glm::vec2 Object::GetLocalPosition() const
return localPosition; return localPosition;
} }
void Object::SetLocalPosition(glm::vec2 pos)
{
localPosition = pos;
}
glm::vec2 Object::GetWorldPosition() const
{
if (parent)
{
float parentRotation = glm::radians(parent->GetWorldRotation());
glm::vec2 rotated = glm::rotate(localPosition, parentRotation);
return parent->GetWorldPosition() + rotated;
}
return localPosition;
}
float Object::GetLocalRotation() const
{
return localRotationDeg;
}
void Object::SetLocalRotation(float deg)
{
localRotationDeg = deg;
}
float Object::GetWorldRotation() const
{
if (parent)
return parent->GetWorldRotation() + localRotationDeg;
return localRotationDeg;
}
bool Object::GetVisable() const bool Object::GetVisable() const
{ {
return visable; return visable;
@ -55,19 +90,6 @@ void Object::SetVisable(bool state)
visable = state; visable = state;
} }
void Object::SetLocalPosition(glm::vec2 pos)
{
localPosition = pos;
}
glm::vec2 Object::GetWorldPosition() const
{
if (parent)
return parent->GetWorldPosition() + localPosition;
return localPosition;
}
const std::string &Object::GetName() const { return name; } const std::string &Object::GetName() const { return name; }
void Object::SetName(const std::string &n) { name = n; } void Object::SetName(const std::string &n) { name = n; }
std::vector<std::shared_ptr<Object>> &Object::GetChildren() { return children; } std::vector<std::shared_ptr<Object>> &Object::GetChildren() { return children; }
@ -77,16 +99,15 @@ void Object::Save(YAML::Emitter &out) const
{ {
Logger::LogVerbose("[LoadScene] Saving Object: [%s, %d]", name.c_str(), uid.id); Logger::LogVerbose("[LoadScene] Saving Object: [%s, %d]", name.c_str(), uid.id);
out << YAML::BeginMap; out << YAML::BeginMap;
out << YAML::Key << "name" << YAML::Value << name; out << YAML::Key << "name" << YAML::Value << name;
out << YAML::Key << "uid" << YAML::Value << uid.uuid; out << YAML::Key << "uid" << YAML::Value << uid.uuid;
out << YAML::Key << "id" << YAML::Value << uid.id; out << YAML::Key << "id" << YAML::Value << uid.id;
out << YAML::Key << "position" << YAML::Value << YAML::Flow << YAML::BeginSeq << localPosition.x << localPosition.y << YAML::EndSeq; out << YAML::Key << "position" << YAML::Flow << YAML::BeginSeq << localPosition.x << localPosition.y << YAML::EndSeq;
out << YAML::Key << "rotation" << YAML::Value << localRotationDeg;
out << YAML::Key << "layer" << YAML::Value << layer; out << YAML::Key << "layer" << YAML::Value << layer;
out << YAML::Key << "visable" << YAML::Value << visable; out << YAML::Key << "visable" << YAML::Value << visable;
out << YAML::Key << "components" << YAML::Value << YAML::BeginSeq; out << YAML::Key << "components" << YAML::Value << YAML::BeginSeq;
for (const auto &comp : components) for (const auto &comp : components)
{ {
@ -106,71 +127,36 @@ void Object::Save(YAML::Emitter &out) const
void Object::Load(const YAML::Node &node) void Object::Load(const YAML::Node &node)
{ {
name = node["name"].as<std::string>(); name = node["name"].as<std::string>();
uid.uuid = node["uid"] ? node["uid"].as<std::string>() : GenerateUUID();
if (node["uid"]) uid.id = node["id"] ? node["id"].as<int>() : 0;
uid.uuid = node["uid"].as<std::string>();
else
uid.uuid = GenerateUUID();
if (node["id"])
uid.id = node["id"].as<int>();
auto pos = node["position"]; auto pos = node["position"];
if (pos && pos.IsSequence() && pos.size() == 2) if (pos && pos.IsSequence() && pos.size() == 2)
{ localPosition = {pos[0].as<float>(), pos[1].as<float>()};
localPosition.x = pos[0].as<float>();
localPosition.y = pos[1].as<float>();
}
if (node["layer"])
layer = node["layer"].as<int>();
if (node["visable"])
visable = node["visable"].as<bool>();
localRotationDeg = node["rotation"] ? node["rotation"].as<float>() : 0.0f;
layer = node["layer"] ? node["layer"].as<int>() : 0;
visable = node["visable"] ? node["visable"].as<bool>() : true;
Logger::LogVerbose("[LoadScene] Loading Object: [%s, %d]", name.c_str(), uid.id); Logger::LogVerbose("[LoadScene] Loading Object: [%s, %d]", name.c_str(), uid.id);
components.clear();
if (node["components"]) if (node["components"])
{ {
for (const auto &compNode : node["components"]) for (const auto &compNode : node["components"])
{ {
std::string type = compNode["type"].as<std::string>(); std::string type = compNode["type"].as<std::string>();
Logger::LogVerbose("[LoadScene] Createing Component: %s", type.c_str());
if (type == "SpriteComponent") if (type == "SpriteComponent") AddComponent<SpriteComponent>()->Load(compNode);
{ else if (type == "CameraComponent") AddComponent<CameraComponent>()->Load(compNode);
auto comp = AddComponent<SpriteComponent>(); else if (type == "LightComponent") AddComponent<LightComponent>()->Load(compNode);
comp->Load(compNode); else if (type == "TilemapComponent") AddComponent<TilemapComponent>()->Load(compNode);
} else if (type == "TextComponent") AddComponent<TextComponent>()->Load(compNode);
else if (type == "CameraComponent") else if (type == "ScriptComponent") AddComponent<ScriptComponent>()->Load(compNode);
{
auto comp = AddComponent<CameraComponent>();
comp->Load(compNode);
}
else if (type == "LightComponent")
{
auto comp = AddComponent<LightComponent>();
comp->Load(compNode);
}
else if (type == "TilemapComponent")
{
auto comp = AddComponent<TilemapComponent>();
comp->Load(compNode);
}
else if (type == "TextComponent")
{
auto comp = AddComponent<TextComponent>();
comp->Load(compNode);
}
else if (type == "ScriptComponent")
{
auto comp = AddComponent<ScriptComponent>();
comp->Load(compNode);
}
} }
} }
children.clear();
if (node["children"]) if (node["children"])
{ {
for (const auto &childNode : node["children"]) for (const auto &childNode : node["children"])

View File

@ -3,7 +3,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
#include "../utils/UID.h" #include "../utils/UID.h"
@ -22,6 +25,10 @@ public:
void SetLocalPosition(glm::vec2 pos); void SetLocalPosition(glm::vec2 pos);
glm::vec2 GetWorldPosition() const; glm::vec2 GetWorldPosition() const;
float GetLocalRotation() const;
void SetLocalRotation(float deg);
float GetWorldRotation() const;
void SetParent(Object *parent); void SetParent(Object *parent);
Object *GetParent() const; Object *GetParent() const;
void AddChild(std::shared_ptr<Object> child); void AddChild(std::shared_ptr<Object> child);
@ -31,7 +38,6 @@ public:
bool GetVisable() const; bool GetVisable() const;
void SetVisable(bool state); void SetVisable(bool state);
template <typename T> template <typename T>
std::shared_ptr<T> GetComponent() const; std::shared_ptr<T> GetComponent() const;
template <typename T> template <typename T>
@ -46,9 +52,10 @@ public:
int layer = 0; int layer = 0;
private: private:
bool visable; bool visable = true;
std::string name; std::string name;
glm::vec2 localPosition; glm::vec2 localPosition{0.0f};
float localRotationDeg = 0.0f; // Rotation in degrees
Object *parent = nullptr; Object *parent = nullptr;
std::vector<std::shared_ptr<Object>> children; std::vector<std::shared_ptr<Object>> children;
std::vector<std::shared_ptr<Component>> components; std::vector<std::shared_ptr<Component>> components;
@ -58,10 +65,8 @@ template <typename T>
std::shared_ptr<T> Object::GetComponent() const std::shared_ptr<T> Object::GetComponent() const
{ {
for (const auto &comp : components) for (const auto &comp : components)
{
if (auto casted = std::dynamic_pointer_cast<T>(comp)) if (auto casted = std::dynamic_pointer_cast<T>(comp))
return casted; return casted;
}
return nullptr; return nullptr;
} }
@ -72,7 +77,7 @@ std::shared_ptr<T> Object::AddComponent()
if (existing) if (existing)
return existing; return existing;
std::shared_ptr<T> component = std::make_shared<T>(this); auto component = std::make_shared<T>(this);
components.push_back(component); components.push_back(component);
return component; return component;
} }

View File

@ -4,6 +4,8 @@
#include "utils/Logging.h" #include "utils/Logging.h"
#include "utils/EngineConfig.h" #include "utils/EngineConfig.h"
#include "utils/utils.h" #include "utils/utils.h"
#include "utils/Profiler.h"
#include "Entitys/Object.h"
#include "stb_image.h" #include "stb_image.h"
@ -25,6 +27,8 @@ int Renderer::width = 1280;
int Renderer::height = 720; int Renderer::height = 720;
int Renderer::s_DrawCalls = 0; int Renderer::s_DrawCalls = 0;
int Renderer::s_LightsCount = 0; int Renderer::s_LightsCount = 0;
std::unique_ptr<ColorCorrection> Renderer::s_ColorCorrection = nullptr;
std::vector<Light> Renderer::s_Lights; std::vector<Light> Renderer::s_Lights;
@ -83,6 +87,9 @@ void Renderer::Init()
// Load unlit shader // Load unlit shader
unlitShader.LoadFromFile("src/assets/shaders/unlit.vert", "src/assets/shaders/unlit.frag"); unlitShader.LoadFromFile("src/assets/shaders/unlit.vert", "src/assets/shaders/unlit.frag");
SetColorCorrection(std::make_unique<ColorCorrection>());
// Create a 1x1 flat normal map (RGB: 128,128,255) // Create a 1x1 flat normal map (RGB: 128,128,255)
unsigned char flatNormal[3] = {128, 128, 255}; unsigned char flatNormal[3] = {128, 128, 255};
glGenTextures(1, &defaultNormalMap); glGenTextures(1, &defaultNormalMap);
@ -96,6 +103,7 @@ void Renderer::Init()
void Renderer::Resize(int w, int h) void Renderer::Resize(int w, int h)
{ {
if (w == width && h == height) if (w == width && h == height)
return; return;
width = w; width = w;
@ -111,14 +119,26 @@ void Renderer::Resize(int w, int h)
void Renderer::Begin() void Renderer::Begin()
{ {
{
PROFILE_ENGINE_SCOPE("glBindFramebuffer");
glBindFramebuffer(GL_FRAMEBUFFER, fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
{
glViewport(0, 0, width, height); glViewport(0, 0, width, height);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
{
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
}
{
s_DrawCalls = 0; s_DrawCalls = 0;
ClearLights(); ClearLights();
}
} }
void Renderer::End() void Renderer::End()
@ -128,12 +148,16 @@ void Renderer::End()
void Renderer::ClearLights() void Renderer::ClearLights()
{ {
PROFILE_ENGINE_SCOPE("Renderer::ClearLights");
s_Lights.clear(); s_Lights.clear();
s_LightsCount = 0; s_LightsCount = 0;
} }
void Renderer::AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius) void Renderer::AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius)
{ {
PROFILE_ENGINE_SCOPE("Engine::AddLight");
if (s_Lights.size() >= g_engineConfig.gl_maxLight) if (s_Lights.size() >= g_engineConfig.gl_maxLight)
return; return;
s_Lights.push_back({screenPos, color, intensity, radius}); s_Lights.push_back({screenPos, color, intensity, radius});
@ -142,6 +166,8 @@ void Renderer::AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, floa
void Renderer::DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos) void Renderer::DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos)
{ {
PROFILE_ENGINE_SCOPE("Renderer::DrawTilemap");
if (!tilemap || tilemap->GetAtlasPath().empty()) if (!tilemap || tilemap->GetAtlasPath().empty())
return; return;
@ -209,26 +235,32 @@ void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float z
Shader *shader = &unlitShader; Shader *shader = &unlitShader;
bool useLighting = false; bool useLighting = false;
if (g_engineConfig.lighting_enabled && sprite->GetRenderType() == SpriteComponent::RenderType::Lit) if (g_engineConfig.settings.lighting_enabled && sprite->GetRenderType() == SpriteComponent::RenderType::Lit)
{ {
shader = &spriteShader; shader = &spriteShader;
useLighting = true; useLighting = true;
} }
shader->Use(); shader->Use();
glm::vec2 size = sprite->GetSize(); glm::vec2 size = sprite->GetSize();
glm::vec2 screenPos = (pos - CameraPos) * zoom + glm::vec2(width, height) * 0.5f - (size * zoom * 0.5f); glm::vec2 screenPos = (pos - CameraPos) * zoom + glm::vec2(width, height) * 0.5f - (size * zoom * 0.5f);
float rotationDeg = sprite->GetOwner()->GetWorldRotation();
shader->SetVec2("uPos", screenPos); shader->SetVec2("uPos", screenPos);
shader->SetVec2("uSize", size * zoom); shader->SetVec2("uSize", size * zoom);
shader->SetVec2("uScreen", glm::vec2(width, height)); shader->SetVec2("uScreen", glm::vec2(width, height));
shader->SetFloat("uRotation", glm::radians(rotationDeg));
shader->SetInt("uTex", 0); shader->SetInt("uTex", 0);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sprite->GetTextureID()); glBindTexture(GL_TEXTURE_2D, sprite->GetTextureID());
if (useLighting) if (useLighting)
{ {
s_ColorCorrection->Upload(*shader);
shader->SetInt("uLightCount", static_cast<int>(s_Lights.size())); shader->SetInt("uLightCount", static_cast<int>(s_Lights.size()));
for (size_t i = 0; i < s_Lights.size(); ++i) for (size_t i = 0; i < s_Lights.size(); ++i)
{ {
@ -370,3 +402,11 @@ glm::ivec2 Renderer::GetSize()
{ {
return {width, height}; return {width, height};
} }
void Renderer::SetColorCorrection(std::unique_ptr<ColorCorrection> correction)
{
s_ColorCorrection = std::move(correction);
}
ColorCorrection *Renderer::GetColorCorrection()
{
return s_ColorCorrection.get();
}

View File

@ -6,63 +6,61 @@
#include "Components/TilemapComponent.h" #include "Components/TilemapComponent.h"
#include "Components/SpriteComponent.h" #include "Components/SpriteComponent.h"
#include "utils/EngineConfig.h" #include "utils/EngineConfig.h"
#include "utils/Shader.h"
#include "utils/Profiler.h"
struct Light {
struct ColorCorrection {
float brightness = 1.0f;
float saturation = 1.0f;
float gamma = 1.0f;
void Upload(Shader& shader) const {
shader.SetFloat("uBrightness", brightness);
shader.SetFloat("uSaturation", saturation);
shader.SetFloat("uGamma", gamma);
}
};
struct Light
{
glm::vec2 screenPos; glm::vec2 screenPos;
glm::vec3 color; glm::vec3 color;
float intensity; float intensity;
float radius; float radius;
}; };
class Renderer { class Renderer
{
public: public:
static void Init(); static void Init();
static void Resize(int w, int h); static void Resize(int w, int h);
static void Begin(); static void Begin();
static void End(); static void End();
static void DrawSprite(SpriteComponent* sprite, const glm::vec2& pos, float zoom, glm::vec2& CameraPos); static void DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float zoom, glm::vec2 &CameraPos);
static void DrawTilemap(TilemapComponent* tilemap, const glm::vec2& worldPos, float zoom, const glm::vec2& cameraPos); static void DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos, float zoom, const glm::vec2 &cameraPos);
static void AddLight(const glm::vec2& screenPos, const glm::vec3& color, float intensity, float radius); static void AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius);
static void ClearLights(); static void ClearLights();
static void DrawEditorGrid(const glm::vec2& cameraPos, float zoom); static void DrawEditorGrid(const glm::vec2 &cameraPos, float zoom);
// —— New gizmo functions —— static void DrawGizmoLine(const glm::vec2 &worldStart, const glm::vec2 &worldEnd, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
// Draws a colored line between two worldspace points. static void DrawGizmoRect(const glm::vec2 &worldPos, const glm::vec2 &size, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
static void DrawGizmoLine( static void DrawGizmoCircle(const glm::vec2 &worldCenter, float radius, int segments, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
const glm::vec2& worldStart,
const glm::vec2& worldEnd,
const glm::vec3& color,
const glm::vec2& cameraPos,
float zoom
);
// Draws a colored axisaligned rectangle in world space.
static void DrawGizmoRect(
const glm::vec2& worldPos,
const glm::vec2& size,
const glm::vec3& color,
const glm::vec2& cameraPos,
float zoom
);
// Draws a colored circle (approximated by segments) in world space.
static void DrawGizmoCircle(
const glm::vec2& worldCenter,
float radius,
int segments,
const glm::vec3& color,
const glm::vec2& cameraPos,
float zoom
);
static GLuint GetRenderTexture(); static GLuint GetRenderTexture();
static glm::ivec2 GetSize(); static glm::ivec2 GetSize();
static int GetDrawCallCount(); static int GetDrawCallCount();
static int GetLightsCount(); static int GetLightsCount();
static void SetColorCorrection(std::unique_ptr<ColorCorrection> correction);
static ColorCorrection *GetColorCorrection();
private: private:
static std::vector<Light> s_Lights; static std::vector<Light> s_Lights;
static GLuint fbo, textureColorBuffer, rbo; static GLuint fbo, textureColorBuffer, rbo;
@ -73,5 +71,7 @@ private:
static GLuint shader, quadVAO, quadVBO; static GLuint shader, quadVAO, quadVBO;
static void InitQuad(); static void InitQuad();
static GLuint LoadShader(const char* vertexSrc, const char* fragmentSrc); static GLuint LoadShader(const char *vertexSrc, const char *fragmentSrc);
static std::unique_ptr<ColorCorrection> s_ColorCorrection;
}; };

View File

@ -1,14 +1,71 @@
// EngineConfig.cpp
#include "EngineConfig.h" #include "EngineConfig.h"
#include <yaml-cpp/yaml.h>
#include <filesystem>
#include <fstream>
#include <Windows.h>
#include <shlobj.h> // SHGetFolderPathA
#include "Logging.h"
EngineConfig g_engineConfig { EngineConfig g_engineConfig{
.lighting_enabled = true,
.version = "0.1.0", .version = "0.1.0",
.gl_version = "430", .gl_version = "430",
.gl_maxLight = 512, .gl_maxLight = 512,
.settings{ .settings = {
.draw_gizmos = true, .draw_gizmos = true,
}, .profile_editor = false,
.profile_enabled = true,
.show_color_correction_window = false,
.lighting_enabled = true
}
}; };
static std::filesystem::path GetUserSettingsPath() {
char userPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, userPath))) {
std::filesystem::path path = std::filesystem::path(userPath) / ".CreateEngine" / ".user_settings.yaml";
std::filesystem::create_directories(path.parent_path());
Logger::LogVerbose("Settings Path: %s", path.string().c_str());
return path;
}
return {};
}
void EngineConfig::SaveToFile() {
Logger::LogVerbose("Saving User Settings");
YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "draw_gizmos" << YAML::Value << settings.draw_gizmos;
out << YAML::Key << "profile_editor" << YAML::Value << settings.profile_editor;
out << YAML::Key << "profile_enabled" << YAML::Value << settings.profile_enabled;
out << YAML::Key << "show_color_correction_window" << YAML::Value << settings.show_color_correction_window;
out << YAML::Key << "lighting_enabled" << YAML::Value << settings.lighting_enabled;
out << YAML::EndMap;
std::ofstream fout(GetUserSettingsPath());
fout << out.c_str();
}
void EngineConfig::LoadFromFile() {
Logger::LogVerbose("Loading User Settings");
auto path = GetUserSettingsPath();
if (!std::filesystem::exists(path)) return;
YAML::Node root = YAML::LoadFile(path.string());
if (root["draw_gizmos"])
settings.draw_gizmos = root["draw_gizmos"].as<bool>();
if (root["profile_editor"])
settings.profile_editor = root["profile_editor"].as<bool>();
if (root["profile_enabled"])
settings.profile_enabled = root["profile_enabled"].as<bool>();
if (root["show_color_correction_window"])
settings.show_color_correction_window = root["show_color_correction_window"].as<bool>();
if (root["lighting_enabled"])
settings.lighting_enabled = root["lighting_enabled"].as<bool>();
}

View File

@ -1,17 +1,23 @@
#pragma once #pragma once
#include <string> #include <string>
struct UserSettings{ struct UserSettings {
bool draw_gizmos; bool draw_gizmos;
bool profile_editor;
bool profile_enabled;
bool show_color_correction_window;
bool lighting_enabled;
}; };
struct EngineConfig { struct EngineConfig {
bool lighting_enabled;
std::string version; std::string version;
std::string gl_version; std::string gl_version;
int gl_maxLight; int gl_maxLight;
UserSettings settings; UserSettings settings;
void SaveToFile();
void LoadFromFile();
}; };
extern EngineConfig g_engineConfig; extern EngineConfig g_engineConfig;

View File

@ -2,6 +2,8 @@
#include "../utils/Logging.h" #include "../utils/Logging.h"
std::vector<std::shared_ptr<Object>> objects; std::vector<std::shared_ptr<Object>> objects;
std::string savedStateYAML;
std::shared_ptr<Object> FindByTagRecursive(const std::shared_ptr<Object>& obj, const std::string& tag) { std::shared_ptr<Object> FindByTagRecursive(const std::shared_ptr<Object>& obj, const std::string& tag) {
if (obj->GetName() == tag) if (obj->GetName() == tag)

View File

@ -5,4 +5,6 @@
#include "../Entitys/Object.h" #include "../Entitys/Object.h"
extern std::vector<std::shared_ptr<Object>> objects; extern std::vector<std::shared_ptr<Object>> objects;
extern std::string savedStateYAML;
std::shared_ptr<Object> FindByTagRecursive(const std::shared_ptr<Object>& obj, const std::string& tag); std::shared_ptr<Object> FindByTagRecursive(const std::shared_ptr<Object>& obj, const std::string& tag);

View File

@ -67,7 +67,7 @@ void Logger::LogVA(Level level, const char *fmt, va_list args)
std::cout << GetAnsiColor(level) std::cout << GetAnsiColor(level)
<< "[Logger][" << ToString(level) << "] " << "[Logger][" << ToString(level) << "] "
<< buffer << "\033[0m" << std::endl; << buffer << "\033[0m" << "\n";
} }
void Logger::LogInfo(const char *fmt, ...) void Logger::LogInfo(const char *fmt, ...)

107
src/src/utils/Profiler.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "Profiler.h"
#include <cmath>
extern float g_fps;
extern EngineConfig g_engineConfig;
inline double Lerp(double a, double b, double t) {
return a + (b - a) * t;
}
static void SmoothVisualDurations(ProfileNode& node, double blendFactor) {
node.visualStartMs = Lerp(node.visualStartMs, node.startMs, blendFactor);
node.visualDurationMs = Lerp(node.visualDurationMs, node.durationMs, blendFactor);
for (auto& child : node.children)
SmoothVisualDurations(child, blendFactor);
}
void HierarchicalProfiler::BeginFrame() {
if (!g_engineConfig.settings.profile_enabled)
return;
root.startMs = 0.0;
root.durationMs = 0.0;
root.visualStartMs = 0.0;
root.visualDurationMs = 0.0;
root.children.clear();
currentStack.clear();
sectionStartTimes.clear();
currentStack.reserve(ProfilesLastFrame);
sectionStartTimes.reserve(ProfilesLastFrame);
startTime = Clock::now();
currentStack.push_back(&root);
ProfilesLastFrame = 0;
}
void HierarchicalProfiler::BeginSection(const std::string& name) {
if (!g_engineConfig.settings.profile_enabled)
return;
auto now = Clock::now();
double timeSinceStart = std::chrono::duration<double, std::milli>(now - startTime).count();
ProfileNode& parent = *currentStack.back();
parent.children.emplace_back(name, timeSinceStart);
currentStack.push_back(&parent.children.back());
sectionStartTimes.push_back(now);
ProfilesLastFrame++;
}
void HierarchicalProfiler::EndSection() {
if (!g_engineConfig.settings.profile_enabled)
return;
if (currentStack.size() <= 1 || sectionStartTimes.empty())
return;
auto now = Clock::now();
ProfileNode& node = *currentStack.back();
double duration = std::chrono::duration<double, std::milli>(now - sectionStartTimes.back()).count();
node.durationMs = duration;
currentStack.pop_back();
sectionStartTimes.pop_back();
}
void HierarchicalProfiler::BeginEngineSection(const std::string& name) {
if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor)
BeginSection(name);
}
void HierarchicalProfiler::EndEngineSection() {
if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor)
EndSection();
}
void HierarchicalProfiler::EndFrame() {
if (!g_engineConfig.settings.profile_enabled)
return;
root.durationMs = std::chrono::duration<double, std::milli>(Clock::now() - startTime).count();
if (frameHistory.empty())
root.visualDurationMs = root.durationMs;
double blendFactor = 1.0 - std::pow(0.01, 1.0 / g_fps);
SmoothVisualDurations(root, blendFactor);
frameHistory.push_back(root);
if (frameHistory.size() > maxFrames)
frameHistory.erase(frameHistory.begin());
}
const std::vector<ProfileNode>& HierarchicalProfiler::GetFrames() const {
return frameHistory;
}
const ProfileNode* HierarchicalProfiler::GetLatestFrame() const {
return frameHistory.empty() ? nullptr : &frameHistory.back();
}
HierarchicalProfiler profiler;

94
src/src/utils/Profiler.h Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#include <string>
#include <vector>
#include <chrono>
#include "EngineConfig.h"
struct ProfileNode
{
std::string name;
double startMs = 0.0;
double durationMs = 0.0;
double visualStartMs = 0.0;
double visualDurationMs = 0.0;
std::vector<ProfileNode> children;
ProfileNode() = default;
ProfileNode(const std::string &n, double start)
: name(n), startMs(start)
{
children.reserve(8);
}
};
class HierarchicalProfiler
{
public:
void BeginFrame();
void EndFrame();
void BeginSection(const std::string &name);
void EndSection();
void BeginEngineSection(const std::string &name);
void EndEngineSection();
const std::vector<ProfileNode> &GetFrames() const;
const ProfileNode *GetLatestFrame() const;
private:
using Clock = std::chrono::high_resolution_clock;
Clock::time_point startTime;
std::vector<Clock::time_point> sectionStartTimes;
std::vector<ProfileNode *> currentStack;
ProfileNode root{"Frame", 0.0};
std::vector<ProfileNode> frameHistory;
static constexpr size_t maxFrames = 30;
size_t ProfilesLastFrame = 128;
};
extern HierarchicalProfiler profiler;
// RAII Scoped Profiling (Zero-overhead when disabled)
struct ScopedProfile
{
ScopedProfile(const std::string &name)
{
if (g_engineConfig.settings.profile_enabled)
profiler.BeginSection(name);
}
~ScopedProfile()
{
if (g_engineConfig.settings.profile_enabled)
profiler.EndSection();
}
};
struct ScopedEngineProfile
{
ScopedEngineProfile(const std::string &name)
{
if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor)
profiler.BeginSection(name);
}
~ScopedEngineProfile()
{
if (g_engineConfig.settings.profile_enabled && g_engineConfig.settings.profile_editor)
profiler.EndSection();
}
};
#define CONCAT_IMPL(a, b) a##b
#define CONCAT(a, b) CONCAT_IMPL(a, b)
#define PROFILE_SCOPE(label) \
ScopedProfile CONCAT(_scopedProfile_, __LINE__)(label)
#define PROFILE_ENGINE_SCOPE(label) \
ScopedEngineProfile CONCAT(_scopedEngineProfile_, __LINE__)(label)