Adde Profiling and user Setting saving

This commit is contained in:
OusmBlueNinja 2025-04-18 17:23:59 -05:00
parent e409bed53f
commit e81041fa2b
21 changed files with 1101 additions and 319 deletions

View File

@ -10,24 +10,24 @@ Collapsed=1
[Window][WindowOverViewport_11111111]
Pos=0,19
Size=1920,1158
Size=1280,701
Collapsed=0
[Window][Inspector]
Pos=1530,19
Size=390,835
Pos=890,19
Size=390,292
Collapsed=0
DockId=0x00000005,0
DockId=0x0000000B,0
[Window][Scene Tree]
Pos=0,19
Size=263,1158
Size=376,366
Collapsed=0
DockId=0x00000001,0
DockId=0x00000009,0
[Window][Viewport]
Pos=265,19
Size=1263,786
Pos=378,19
Size=510,218
Collapsed=0
DockId=0x00000007,0
@ -36,16 +36,16 @@ Size=1280,19
Collapsed=0
[Window][Performance Info]
Pos=1530,856
Size=390,321
Pos=890,526
Size=390,194
Collapsed=0
DockId=0x00000006,0
[Window][Console]
Pos=265,807
Size=1263,370
Pos=0,387
Size=376,333
Collapsed=0
DockId=0x00000008,0
DockId=0x0000000A,0
[Window][Tilemap Editor]
Pos=265,19
@ -53,14 +53,48 @@ Size=1263,674
Collapsed=0
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]
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 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=0x00000001 Parent=0x00000003 SizeRef=263,701 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=623,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,329 CentralNode=1 Selected=0xC450F867
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,370 Selected=0xEA83D666
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=376,701 Split=Y Selected=0x12EF0F59
DockNode ID=0x00000009 Parent=0x00000001 SizeRef=376,605 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x0000000A Parent=0x00000001 SizeRef=376,551 HiddenTabBar=1 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=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

View File

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

View File

@ -1,3 +1,10 @@
[COMPILE] g++ -std=c++20 -Wall -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -IC:/msys64/mingw64/include -IC:\msys64\mingw64\lib\libyaml-cpp.a -Isrc\vendor\imgui -MMD -MP -c src\src\Components\ScriptComponent.cpp -o src\build\Components\ScriptComponent.o
[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
[RUN] Executed app.exe successfully.
[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
scene_name: lighting_test_2
scene_hash: a0ae96d593e1990a6f3944184fd6595738629af0dcb2d4cdd83011a91aaa738c
scene_hash: 8f8260f09292105727f7f8afe890565a9feeaa92a1ca6d7fa55790a5019c67d7
format_version: 1
objects:
- name: Tiles
uid: f5e01f7892874a67b662633650b41dbd
id: 3
position: [0, 0]
rotation: 0
layer: 0
visable: true
components: []
@ -15,6 +16,7 @@ objects:
uid: 7dc3bbf8affb4844ae3801f03857b904
id: 4
position: [0, 0]
rotation: 0
layer: 0
visable: true
components:
@ -27,6 +29,7 @@ objects:
uid: 13d8988343354e3c8a1f51c03ed40cda
id: 5
position: [1024, 0]
rotation: 0
layer: 0
visable: true
components:
@ -39,6 +42,7 @@ objects:
uid: cff28abe7e3b455ab9b756acc84cd2d7
id: 6
position: [0, 1024]
rotation: 0
layer: 0
visable: true
components:
@ -51,6 +55,7 @@ objects:
uid: 98967eb30e5b429b992766d8062b7c17
id: 7
position: [1024, 1024]
rotation: 0
layer: 0
visable: true
components:
@ -62,7 +67,8 @@ objects:
- name: Logo
uid: c4ce6f16dfb347b0ae0ac67f5881b243
id: 8
position: [2048, 0]
position: [2104, 56]
rotation: 0
layer: 0
visable: true
components:
@ -74,7 +80,8 @@ objects:
- name: Carbooon Fobar
uid: 5ea269572751401da6d86519d3513b7d
id: 9
position: [2559.30005, 1562]
position: [2048, 3070]
rotation: 0
layer: 1
visable: true
components:
@ -86,7 +93,8 @@ objects:
- name: Mud
uid: a36b71937ba349bd8e6414f75be9ee16
id: 10
position: [0, 2561.80005]
position: [0, 3070]
rotation: 0
layer: 0
visable: true
components:
@ -99,6 +107,7 @@ objects:
uid: 051b338a725a4076ad53ad8fa00c5f4e
id: 12
position: [-556, 951]
rotation: 0
layer: 0
visable: true
components: []
@ -107,6 +116,7 @@ objects:
uid: 6afde2dd47aa4557b6afb1a607c99dc8
id: 13
position: [512, 1024]
rotation: 0
layer: 2
visable: true
components:
@ -128,6 +138,7 @@ objects:
uid: 0f950d76d24b4dc18f54cab2c3aaaf9a
id: 14
position: [1024, 512]
rotation: 0
layer: 2
visable: true
components:
@ -149,6 +160,7 @@ objects:
uid: 09f722f51c7c4b0f98de3a0a16d127c4
id: 15
position: [250, 250]
rotation: 0
layer: 2
visable: true
components:
@ -170,6 +182,7 @@ objects:
uid: d4fb425522d84a8cbbd7d1415bcd93df
id: 16
position: [500, 500]
rotation: 0
layer: 0
visable: true
components:
@ -187,6 +200,7 @@ objects:
uid: 895c655f3dda4aec9f2a354c1276c53e
id: 14
position: [0, 0]
rotation: 0
layer: 0
visable: true
components:

View File

@ -6,6 +6,11 @@ out vec4 FragColor;
uniform sampler2D uTex;
uniform sampler2D uNormalMap;
uniform float uRotation;
uniform float uBrightness;
uniform float uSaturation;
uniform float uGamma;
#define MAX_LIGHTS 512
@ -17,16 +22,22 @@ uniform float uLightRadius[MAX_LIGHTS];
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)
discard;
// unpack normal map and convert from [0,1] to [-1,1]
vec3 n = texture(uNormalMap, vUV).rgb * 2.0 - 1.0;
// invert the green channel for OpenGL
vec3 n = texture(uNormalMap, rotatedUV).rgb * 2.0 - 1.0;
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);
for (int i = 0; i < uLightCount; ++i)
@ -37,9 +48,8 @@ void main()
if (dist < uLightRadius[i])
{
vec2 lightDir2D = normalize(lightVec);
vec3 lightDir = normalize(vec3(lightDir2D, 1.0)); // pseudo-3D
float attenuation = 1.0 - dist / uLightRadius[i];
vec3 lightDir = normalize(vec3(lightDir2D, 1.0));
float attenuation = smoothstep(uLightRadius[i], 0.0, dist);
float diff = max(dot(normal, lightDir), 0.0);
vec3 light = uLightColor[i] * diff * attenuation * uLightIntensity[i];
finalLight += light;
@ -47,5 +57,15 @@ void main()
}
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);
}

View File

@ -1,7 +1,7 @@
#version 430 core
#version 330 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aUV;
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUV;
out vec2 vUV;
out vec2 vFragScreenPos;
@ -9,17 +9,22 @@ out vec2 vFragScreenPos;
uniform vec2 uPos;
uniform vec2 uSize;
uniform vec2 uScreen;
uniform float uRotation;//r
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;
vFragScreenPos = finalPos;
vec2 worldPos = uPos + aPos * uSize;
vFragScreenPos = worldPos;
vec2 ndc = (worldPos / uScreen) * 2.0 - 1.0;
ndc.y *= -1.0;
gl_Position = vec4(ndc, 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
}

View File

@ -1,10 +1,15 @@
#version 430 core
#version 330 core
in vec2 vUV;
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)
discard;

View File

@ -1,13 +1,28 @@
#version 430 core
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUV;
out vec2 vUV;
out vec2 vFragScreenPos;
uniform vec2 uPos;
uniform vec2 uSize;
uniform vec2 uScreen;
void main() {
vec2 worldPos = aPos * uSize + uPos;
uniform float uRotation; // radians
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;
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

@ -10,6 +10,8 @@
#include "../Components/TextComponent.h"
#include "../Components/TilemapComponent.h"
#include "../utils/Profiler.h"
#include <lua.hpp>
#include <memory>
#include <cstring>
@ -74,6 +76,8 @@ static int Lua_DebugLua(lua_State *L)
// Component resolver
static Component *GetComponentByName(Object *obj, const std::string &type)
{
PROFILE_SCOPE("LUA_GetComponentByName");
if (type == "SpriteComponent")
return obj->GetComponent<SpriteComponent>().get();
if (type == "CameraComponent")
@ -92,6 +96,8 @@ static Component *GetComponentByName(Object *obj, const std::string &type)
// Object:GetComponent("Type")
static int Lua_Object_GetComponent(lua_State *L)
{
PROFILE_SCOPE("Lua_Object_GetComponent");
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
const char *type = luaL_checkstring(L, 2);
@ -106,6 +112,8 @@ static int Lua_Object_GetComponent(lua_State *L)
// Object:GetPosition()
static int Lua_Object_GetPosition(lua_State *L)
{
PROFILE_SCOPE("Lua_Object_GetPosition");
auto *wrapper = (LuaObjectWrapper *)luaL_checkudata(L, 1, LUA_OBJECT_MT);
glm::vec2 pos = wrapper->obj->GetLocalPosition();
@ -120,6 +128,8 @@ static int Lua_Object_GetPosition(lua_State *L)
// Object:SetPosition(Vector2)
static int Lua_Object_SetPosition(lua_State *L)
{
PROFILE_SCOPE("Lua_Object_SetPosition");
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});
@ -129,6 +139,8 @@ static int Lua_Object_SetPosition(lua_State *L)
// __index for Object
static int Lua_Object_Index(lua_State *L)
{
PROFILE_SCOPE("Lua_Object_Index");
const char *key = luaL_checkstring(L, 2);
lua_getfield(L, lua_upvalueindex(1), key);
return 1;
@ -163,6 +175,8 @@ static void PushObject(lua_State *L, Object *obj)
// Engine.GetObjectByTag(name)
static int Lua_GetObjectByTag(lua_State *L)
{
PROFILE_SCOPE("Lua_GetObjectByTag");
const char *name = luaL_checkstring(L, 1);
for (const auto &obj : objects)
{
@ -180,16 +194,22 @@ static int Lua_GetObjectByTag(lua_State *L)
// Vector2(x, y)
static int Lua_Vector2_New(lua_State *L)
{
LuaVector2 *vec = (LuaVector2 *)lua_newuserdata(L, sizeof(LuaVector2));
vec->x = (float)luaL_optnumber(L, 1, 0);
vec->y = (float)luaL_optnumber(L, 2, 0);
luaL_getmetatable(L, LUA_VECTOR2_MT);
lua_setmetatable(L, -2);
PROFILE_SCOPE("Lua_Vector2_New");
LuaVector2 *vec = static_cast<LuaVector2 *>(lua_newuserdata(L, sizeof(LuaVector2)));
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;
}
static int Lua_Vector2_Index(lua_State *L)
{
PROFILE_SCOPE("Lua_Vector2_Index");
auto *vec = (LuaVector2 *)luaL_checkudata(L, 1, LUA_VECTOR2_MT);
const char *key = luaL_checkstring(L, 2);
if (strcmp(key, "x") == 0)
@ -202,6 +222,8 @@ static int Lua_Vector2_Index(lua_State *L)
}
static int Lua_Vector2_NewIndex(lua_State *L)
{
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);
@ -214,6 +236,7 @@ static int Lua_Vector2_NewIndex(lua_State *L)
void RegisterVector2Type(lua_State *L)
{
luaL_newmetatable(L, LUA_VECTOR2_MT);
lua_pushcfunction(L, Lua_Vector2_Index);
lua_setfield(L, -2, "__index");
@ -286,6 +309,8 @@ void ScriptComponent::ReloadScript()
void ScriptComponent::OnUpdate(float dt)
{
PROFILE_SCOPE("ScriptComponent::OnUpdate");
if (!L)
return;
lua_getglobal(L, "OnUpdate");

View File

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

View File

@ -12,6 +12,7 @@
#include "utils/EngineConfig.h"
#include "utils/GameObjectsList.h"
#include "utils/Profiler.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
@ -24,6 +25,10 @@
#include <sstream>
#include <iomanip>
#include <vector>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <format>
#include <memory>
#include <filesystem>
#include <iostream>
@ -40,14 +45,15 @@ static glm::vec2 cameraPos = {0, 0};
static float cameraZoom = 1.0f;
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;
Engine::Engine()
{
Init();
}
@ -56,46 +62,251 @@ Engine::~Engine()
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)
{
// === Performance Window ===
static std::vector<float> fpsHistory;
static const int maxHistory = 100;
static float timer = 0;
static const int maxFpsHistory = 100;
static float fpsTimer = 0.0f;
float fps = 1.0f / deltaTime;
g_fps = fps;
if (fpsHistory.size() >= maxHistory)
if (fpsHistory.size() >= maxFpsHistory)
fpsHistory.erase(fpsHistory.begin());
if (timer >= 0.05)
if (fpsTimer >= 0.05f)
{
timer = 0;
fpsTimer = 0;
fpsHistory.push_back(fps);
}
timer += deltaTime;
fpsTimer += deltaTime;
ImGui::Begin("Performance Info", nullptr,
ImGuiWindowFlags_NoDecoration);
ImGui::Begin("Performance Info", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize);
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), "Performance");
ImGui::Separator();
ImGui::Text("FPS: %.1f", fps);
ImGui::Text("Delta Time: %.4f s", deltaTime);
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::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Renderer");
ImGui::Separator();
ImGui::Text("Draw Calls: %d", Renderer::GetDrawCallCount());
ImGui::Text("Scene Lights: %d/%d", Renderer::GetLightsCount(), g_engineConfig.gl_maxLight);
ImGui::Text("Draw Calls: %d", Renderer::GetDrawCallCount());
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("Camera Zoom: %.2f", cameraZoom);
ImGui::Text("Camera Pos: (%.1f, %.1f)", cameraPos.x, cameraPos.y);
ImGui::Text("Camera Zoom: %.2f", cameraZoom);
ImGui::Text("Camera Pos: (%.1f, %.1f)", cameraPos.x, cameraPos.y);
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()
{
glfwInit();
@ -108,6 +319,8 @@ void Engine::Init()
glfwSwapInterval(0); // No VSync
glewInit();
g_engineConfig.LoadFromFile();
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
@ -123,10 +336,16 @@ void Engine::Init()
objects.push_back(obj);
selected = obj;
Logger::LogVerbose("Resverving Objects");
m_toDraw.reserve(1024);
m_scriptUpdates.reserve(256);
m_collectStack.reserve(1024);
Logger::LogInfo("Initialized Engine");
}
void DrawInspectorUI(std::shared_ptr<Object> selected)
{
PROFILE_ENGINE_SCOPE("Engine::DrawInspectorUI");
ImGui::Begin("Inspector");
if (!selected)
@ -167,6 +386,10 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (ImGui::DragFloat2("Position", &pos.x, 0.1f))
selected->SetLocalPosition(pos);
float rotation = selected->GetLocalRotation();
if (ImGui::DragFloat("Rotation", &rotation, 1.0f))
selected->SetLocalRotation(rotation);
int layer = selected->layer;
if (ImGui::InputInt("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()
{
while (!glfwWindowShouldClose(window))
{
if (g_engineConfig.settings.profile_editor)
{
profiler.BeginFrame();
}
profiler.BeginEngineSection("glfwPollEvents");
glfwPollEvents();
profiler.EndEngineSection();
profiler.BeginEngineSection("NewFrame");
profiler.BeginEngineSection("ImGui_ImplOpenGL3_NewFrame");
ImGui_ImplOpenGL3_NewFrame();
profiler.EndEngineSection();
profiler.BeginEngineSection("ImGui_ImplGlfw_NewFrame");
ImGui_ImplGlfw_NewFrame();
profiler.EndEngineSection();
profiler.BeginEngineSection("ImGui::NewFrame");
ImGui::NewFrame();
profiler.EndEngineSection();
profiler.BeginEngineSection("ImGui::DockSpaceOverViewport");
ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()->ID);
profiler.EndEngineSection();
profiler.BeginEngineSection("glfwGetTime");
float currentTime = glfwGetTime();
static float lastTime = currentTime;
float deltaTime = currentTime - lastTime;
lastTime = currentTime;
profiler.EndEngineSection();
profiler.EndEngineSection();
profiler.BeginEngineSection("Engine::ShowDebugOverlay");
ShowDebugOverlay(deltaTime);
profiler.EndEngineSection();
profiler.BeginEngineSection("Logger::Draw");
Logger::Draw();
profiler.EndEngineSection();
profiler.BeginEngineSection("BeginMainMenuBar");
if (ImGui::BeginMainMenuBar())
{
@ -402,9 +710,24 @@ void Engine::Run()
if (ImGui::BeginMenu("Options"))
{
// simple checkbox never closes the menu when you click it
ImGui::Checkbox("Enable Lighting", &g_engineConfig.lighting_enabled);
ImGui::Checkbox("Enable Gizmos", &g_engineConfig.settings.draw_gizmos);
if (ImGui::BeginMenu("Settings"))
{
ImGui::Checkbox("Enable Lighting", &g_engineConfig.settings.lighting_enabled);
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();
}
@ -426,44 +749,53 @@ void Engine::Run()
ImGui::EndMainMenuBar();
}
profiler.EndEngineSection();
if (g_engineConfig.settings.show_color_correction_window)
ShowColorCorrectionWindow();
ImGui::Begin("Scene Tree");
if (ImGui::BeginPopupContextWindow("SceneTreeContext", ImGuiPopupFlags_MouseButtonRight))
{
if (ImGui::MenuItem("Create New"))
PROFILE_ENGINE_SCOPE("Engine::DrawSceneTree");
ImGui::Begin("Scene Tree");
if (ImGui::BeginPopupContextWindow("SceneTreeContext", ImGuiPopupFlags_MouseButtonRight))
{
auto obj = std::make_shared<Object>("NewObject");
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
if (ImGui::MenuItem("Create New"))
{
auto obj = std::make_shared<Object>("NewObject");
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
}
ImGui::Separator();
if (ImGui::MenuItem("Create New Sprite"))
{
auto obj = std::make_shared<Object>("NewSprite");
obj->AddComponent<SpriteComponent>();
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
}
if (ImGui::MenuItem("Create New Light"))
{
auto obj = std::make_shared<Object>("NewSprite");
obj->AddComponent<LightComponent>();
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
}
ImGui::EndPopup();
}
ImGui::Separator();
if (ImGui::MenuItem("Create New Sprite"))
{
auto obj = std::make_shared<Object>("NewSprite");
obj->AddComponent<SpriteComponent>();
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
}
if (ImGui::MenuItem("Create New Light"))
{
auto obj = std::make_shared<Object>("NewSprite");
obj->AddComponent<LightComponent>();
objects.push_back(obj);
selected = obj;
ImGui::OpenPopup("RenameObject");
}
ImGui::EndPopup();
for (auto &obj : objects)
if (!obj->GetParent()) // Only draw root nodes
DrawObjectNode(obj);
ImGui::End();
}
for (auto &obj : objects)
if (!obj->GetParent()) // Only draw root nodes
DrawObjectNode(obj);
ImGui::End();
DrawInspectorUI(selected);
// Viewport
@ -506,95 +838,92 @@ void Engine::Run()
}
}
// Resize
profiler.BeginEngineSection("Renderer::Resize");
Renderer::Resize((int)size.x, (int)size.y);
profiler.EndEngineSection();
// Begin
profiler.BeginEngineSection("Renderer::Begin");
Renderer::Begin();
profiler.EndEngineSection();
std::vector<std::shared_ptr<Object>> toDraw;
std::vector<ScriptComponent *> scriptUpdates;
toDraw.reserve(m_Reserved_draws);
m_Reserved_draws = 0; // Reset
Renderer::DrawEditorGrid(cameraPos, cameraZoom);
// clear arrays each frame…
toDraw.clear();
scriptUpdates.clear();
// Reserve (no profiling)
m_toDraw.reserve(m_Reserved_draws);
m_Reserved_draws = 0;
// recursive collector now skips invisible objects (and their children)
std::function<void(const std::shared_ptr<Object> &)> collect =
[&](const std::shared_ptr<Object> &obj)
// Draw Editor Grid
profiler.BeginEngineSection("Draw Editor Grid");
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
if (!obj->GetVisable())
return;
std::sort(m_toDraw.begin(), m_toDraw.end(),
[](auto const &a, auto const &b)
{
if (a->layer != b->layer)
return a->layer < b->layer;
return a->GetWorldPosition().y < b->GetWorldPosition().y;
});
}
profiler.EndEngineSection();
// still visible → collect for drawing
toDraw.push_back(obj);
m_Reserved_draws += 1;
if (!g_engineConfig.settings.profile_editor)
profiler.BeginFrame();
// 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);
m_OnUpdateCalls = 0;
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)
return a->layer < b->layer;
return a->GetWorldPosition().y < b->GetWorldPosition().y; });
// Run script updates
for (auto *script : scriptUpdates)
profiler.BeginSection("Script Updates");
for (auto *script : m_scriptUpdates)
{
profiler.BeginSection("Script: " + script->GetOwner()->GetName());
script->OnUpdate(deltaTime);
m_OnUpdateCalls++;
profiler.EndSection();
}
profiler.EndSection();
// Draw all sprites
for (const auto &obj : toDraw)
profiler.BeginSection("Render");
for (auto *obj : m_toDraw)
{
if (auto sprite = obj->GetComponent<SpriteComponent>())
if (auto spritePtr = obj->GetComponent<SpriteComponent>())
{
glm::vec2 worldPos = obj->GetWorldPosition();
Renderer::DrawSprite(sprite.get(), worldPos, cameraZoom, cameraPos);
profiler.BeginSection("Draw Sprite: " + obj->GetName());
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();
profiler.EndEngineSection();
// Display render target texture
GLuint texID = Renderer::GetRenderTexture();
ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0));
ImGui::End();
if (ImGui::BeginPopup("RenameObject"))
@ -627,7 +956,13 @@ void Engine::Run()
ImGui::EndPopup();
}
if (g_engineConfig.settings.profile_editor)
{
profiler.EndFrame(); // Finish frame
}
// ImGui render
ShowProfilerTimeline();
ImGui::Render();
int w, h;
glfwGetFramebufferSize(window, &w, &h);
@ -656,6 +991,8 @@ void Engine::Run()
void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj)
{
PROFILE_ENGINE_SCOPE("Engine::DrawObjectNode");
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
ImGuiTreeNodeFlags_SpanAvailWidth |
(obj == selected ? ImGuiTreeNodeFlags_Selected : 0);
@ -813,7 +1150,6 @@ void Engine::LoadScene(const std::string &path)
Logger::LogInfo("[LoadScene] Loaded scene: %s", root["scene_name"].as<std::string>().c_str());
}
void Engine::LoadState()
{
if (savedStateYAML.empty())
@ -827,14 +1163,14 @@ void Engine::LoadState()
{
objectArray = YAML::Load(savedStateYAML);
}
catch (const std::exception& e)
catch (const std::exception &e)
{
Logger::LogError("[LoadState] Failed to parse saved scene: %s", e.what());
return;
}
objects.clear();
for (const auto& node : objectArray)
for (const auto &node : objectArray)
{
auto obj = std::make_shared<Object>("[DefaultObject]");
obj->Load(node);
@ -849,7 +1185,7 @@ void Engine::SaveState()
YAML::Emitter sceneData;
sceneData << YAML::BeginSeq;
for (const auto& obj : objects)
for (const auto &obj : objects)
obj->Save(sceneData);
sceneData << YAML::EndSeq;
@ -857,12 +1193,10 @@ void Engine::SaveState()
Logger::LogVerbose("[SaveState] Scene serialized (%zu bytes)", savedStateYAML.size());
}
void Engine::Shutdown()
{
g_engineConfig.SaveToFile();
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
@ -870,4 +1204,5 @@ void Engine::Shutdown()
glfwTerminate();
std::filesystem::remove(tempScenePath);
}

View File

@ -1,9 +1,13 @@
#pragma once
#include <memory>
#include <fstream>
#include <string>
#include <vector> // ← for std::vector<>
#include <glm/vec2.hpp> // ← for glm::vec2
class Object;
class ScriptComponent;
class Engine
{
@ -16,13 +20,21 @@ public:
private:
void Init();
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 LoadScene(const std::string &path);
void ShowDebugOverlay(float deltaTime);
void collectObjects(bool playing, const glm::vec2& camPos, float camZoom);
void SaveState();
void LoadState();
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>
Object::Object(const std::string &name)
: name(name), localPosition(0.0f, 0.0f), uid(), visable(true) {}
: name(name), localPosition(0.0f, 0.0f), localRotationDeg(0.0f), uid(), visable(true) {}
Object::~Object() {}
@ -45,6 +45,41 @@ glm::vec2 Object::GetLocalPosition() const
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
{
return visable;
@ -55,21 +90,6 @@ void Object::SetVisable(bool 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; }
void Object::SetName(const std::string &n) { name = n; }
std::vector<std::shared_ptr<Object>> &Object::GetChildren() { return children; }
@ -79,16 +99,15 @@ void Object::Save(YAML::Emitter &out) const
{
Logger::LogVerbose("[LoadScene] Saving Object: [%s, %d]", name.c_str(), uid.id);
out << YAML::BeginMap;
out << YAML::Key << "name" << YAML::Value << name;
out << YAML::Key << "uid" << YAML::Value << uid.uuid;
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 << "visable" << YAML::Value << visable;
out << YAML::Key << "components" << YAML::Value << YAML::BeginSeq;
for (const auto &comp : components)
{
@ -108,71 +127,36 @@ void Object::Save(YAML::Emitter &out) const
void Object::Load(const YAML::Node &node)
{
name = node["name"].as<std::string>();
if (node["uid"])
uid.uuid = node["uid"].as<std::string>();
else
uid.uuid = GenerateUUID();
if (node["id"])
uid.id = node["id"].as<int>();
uid.uuid = node["uid"] ? node["uid"].as<std::string>() : GenerateUUID();
uid.id = node["id"] ? node["id"].as<int>() : 0;
auto pos = node["position"];
if (pos && pos.IsSequence() && pos.size() == 2)
{
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>();
localPosition = {pos[0].as<float>(), pos[1].as<float>()};
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);
components.clear();
if (node["components"])
{
for (const auto &compNode : node["components"])
{
std::string type = compNode["type"].as<std::string>();
Logger::LogVerbose("[LoadScene] Createing Component: %s", type.c_str());
if (type == "SpriteComponent")
{
auto comp = AddComponent<SpriteComponent>();
comp->Load(compNode);
}
else if (type == "CameraComponent")
{
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);
}
if (type == "SpriteComponent") AddComponent<SpriteComponent>()->Load(compNode);
else if (type == "CameraComponent") AddComponent<CameraComponent>()->Load(compNode);
else if (type == "LightComponent") AddComponent<LightComponent>()->Load(compNode);
else if (type == "TilemapComponent") AddComponent<TilemapComponent>()->Load(compNode);
else if (type == "TextComponent") AddComponent<TextComponent>()->Load(compNode);
else if (type == "ScriptComponent") AddComponent<ScriptComponent>()->Load(compNode);
}
}
children.clear();
if (node["children"])
{
for (const auto &childNode : node["children"])

View File

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

View File

@ -4,6 +4,8 @@
#include "utils/Logging.h"
#include "utils/EngineConfig.h"
#include "utils/utils.h"
#include "utils/Profiler.h"
#include "Entitys/Object.h"
#include "stb_image.h"
@ -25,6 +27,8 @@ int Renderer::width = 1280;
int Renderer::height = 720;
int Renderer::s_DrawCalls = 0;
int Renderer::s_LightsCount = 0;
std::unique_ptr<ColorCorrection> Renderer::s_ColorCorrection = nullptr;
std::vector<Light> Renderer::s_Lights;
@ -83,6 +87,9 @@ void Renderer::Init()
// Load unlit shader
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)
unsigned char flatNormal[3] = {128, 128, 255};
glGenTextures(1, &defaultNormalMap);
@ -96,6 +103,7 @@ void Renderer::Init()
void Renderer::Resize(int w, int h)
{
if (w == width && h == height)
return;
width = w;
@ -111,14 +119,26 @@ void Renderer::Resize(int w, int h)
void Renderer::Begin()
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, width, height);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
s_DrawCalls = 0;
ClearLights();
{
PROFILE_ENGINE_SCOPE("glBindFramebuffer");
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
{
glViewport(0, 0, width, height);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
{
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
{
s_DrawCalls = 0;
ClearLights();
}
}
void Renderer::End()
@ -128,12 +148,16 @@ void Renderer::End()
void Renderer::ClearLights()
{
PROFILE_ENGINE_SCOPE("Renderer::ClearLights");
s_Lights.clear();
s_LightsCount = 0;
}
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)
return;
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)
{
PROFILE_ENGINE_SCOPE("Renderer::DrawTilemap");
if (!tilemap || tilemap->GetAtlasPath().empty())
return;
@ -209,26 +235,32 @@ void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float z
Shader *shader = &unlitShader;
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;
useLighting = true;
}
shader->Use();
glm::vec2 size = sprite->GetSize();
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("uSize", size * zoom);
shader->SetVec2("uScreen", glm::vec2(width, height));
shader->SetFloat("uRotation", glm::radians(rotationDeg));
shader->SetInt("uTex", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, sprite->GetTextureID());
if (useLighting)
{
s_ColorCorrection->Upload(*shader);
shader->SetInt("uLightCount", static_cast<int>(s_Lights.size()));
for (size_t i = 0; i < s_Lights.size(); ++i)
{
@ -370,3 +402,11 @@ glm::ivec2 Renderer::GetSize()
{
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/SpriteComponent.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::vec3 color;
float intensity;
float radius;
};
class Renderer {
class Renderer
{
public:
static void Init();
static void Resize(int w, int h);
static void Begin();
static void End();
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 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 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 DrawEditorGrid(const glm::vec2& cameraPos, float zoom);
static void DrawEditorGrid(const glm::vec2 &cameraPos, float zoom);
// —— New gizmo functions ——
// Draws a colored line between two worldspace points.
static void DrawGizmoLine(
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 void DrawGizmoLine(const glm::vec2 &worldStart, const glm::vec2 &worldEnd, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
static void DrawGizmoRect(const glm::vec2 &worldPos, const glm::vec2 &size, const glm::vec3 &color, const glm::vec2 &cameraPos, float zoom);
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 glm::ivec2 GetSize();
static int GetDrawCallCount();
static int GetLightsCount();
static void SetColorCorrection(std::unique_ptr<ColorCorrection> correction);
static ColorCorrection *GetColorCorrection();
private:
static std::vector<Light> s_Lights;
static GLuint fbo, textureColorBuffer, rbo;
@ -73,5 +71,7 @@ private:
static GLuint shader, quadVAO, quadVBO;
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 <yaml-cpp/yaml.h>
#include <filesystem>
#include <fstream>
#include <Windows.h>
#include <shlobj.h> // SHGetFolderPathA
#include "Logging.h"
EngineConfig g_engineConfig {
.lighting_enabled = true,
EngineConfig g_engineConfig{
.version = "0.1.0",
.gl_version = "430",
.gl_maxLight = 512,
.settings{
.settings = {
.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
#include <string>
struct UserSettings{
struct UserSettings {
bool draw_gizmos;
bool profile_editor;
bool profile_enabled;
bool show_color_correction_window;
bool lighting_enabled;
};
struct EngineConfig {
bool lighting_enabled;
std::string version;
std::string gl_version;
int gl_maxLight;
UserSettings settings;
void SaveToFile();
void LoadFromFile();
};
extern EngineConfig g_engineConfig;

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)