Did so much i forgot all i did

This commit is contained in:
OusmBlueNinja 2025-05-05 22:28:17 -05:00
parent 11bad26b38
commit e03729b42b
27 changed files with 6235 additions and 207 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=1553,19
Size=367,659
Pos=913,19
Size=367,202
Collapsed=0
DockId=0x00000018,0
[Window][Scene Tree]
Pos=0,19
Size=342,575
Size=342,356
Collapsed=0
DockId=0x00000003,0
DockId=0x0000000F,0
[Window][Viewport]
Pos=344,19
Size=1207,659
Size=567,202
Collapsed=0
DockId=0x00000017,0
@ -36,14 +36,14 @@ Size=1280,19
Collapsed=0
[Window][Performance Info]
Pos=1430,881
Size=490,296
Pos=1094,223
Size=186,497
Collapsed=0
DockId=0x0000000F,0
DockId=0x00000016,0
[Window][Console]
Pos=344,680
Size=1497,326
Pos=344,223
Size=715,203
Collapsed=0
DockId=0x00000013,0
@ -54,8 +54,8 @@ Collapsed=0
DockId=0x00000017,1
[Window][Profiler]
Pos=344,1008
Size=1497,169
Pos=344,428
Size=715,292
Collapsed=0
DockId=0x00000014,0
@ -78,19 +78,18 @@ Collapsed=0
DockId=0x00000015,1
[Window][Color Correction]
Pos=1669,881
Size=251,296
Pos=1196,320
Size=317,227
Collapsed=0
DockId=0x00000010,0
[Window][Asset Browser]
Pos=0,596
Size=342,581
Pos=0,658
Size=342,519
Collapsed=0
DockId=0x00000004,0
[Window][Confirm Deletion]
Pos=797,551
Pos=787,416
Size=325,75
Collapsed=0
@ -113,8 +112,8 @@ Collapsed=0
DockId=0x0000000E,0
[Window][Audio Output]
Pos=1843,680
Size=77,497
Pos=1061,223
Size=31,497
Collapsed=0
DockId=0x00000012,0
@ -124,25 +123,31 @@ Size=277,150
Collapsed=0
DockId=0x0000000D,0
[Window][Resources]
Pos=0,377
Size=342,343
Collapsed=0
DockId=0x00000010,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=0x00000005 Parent=0x11111111 SizeRef=989,1158 Split=X
DockNode ID=0x00000001 Parent=0x00000005 SizeRef=342,701 Split=Y Selected=0x12EF0F59
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,575 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,581 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=342,637 Split=Y Selected=0x12EF0F59
DockNode ID=0x0000000F Parent=0x00000003 SizeRef=342,356 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000010 Parent=0x00000003 SizeRef=342,343 HiddenTabBar=1 Selected=0x30401527
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=342,519 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1484,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,659 Split=X Selected=0xC450F867
DockNode ID=0x00000017 Parent=0x00000007 SizeRef=1207,860 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x00000018 Parent=0x00000007 SizeRef=367,860 HiddenTabBar=1 Selected=0x36DC96AB
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,497 Split=X Selected=0xEA83D666
DockNode ID=0x00000015 Parent=0x00000008 SizeRef=1291,172 Split=X Selected=0xEA83D666
DockNode ID=0x00000011 Parent=0x00000015 SizeRef=1497,168 Split=Y Selected=0x9B5D3198
DockNode ID=0x00000013 Parent=0x00000011 SizeRef=1449,326 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=1449,169 HiddenTabBar=1 Selected=0x9B5D3198
DockNode ID=0x00000012 Parent=0x00000015 SizeRef=77,168 HiddenTabBar=1 Selected=0x56009A08
DockNode ID=0x00000016 Parent=0x00000008 SizeRef=283,172 Split=X Selected=0x56009A08
DockNode ID=0x0000000F Parent=0x00000016 SizeRef=140,296 HiddenTabBar=1 Selected=0x3FC1A724
DockNode ID=0x00000010 Parent=0x00000016 SizeRef=148,296 HiddenTabBar=1 Selected=0xA873C17F
DockNode ID=0x00000015 Parent=0x00000008 SizeRef=1260,172 Split=X Selected=0xEA83D666
DockNode ID=0x00000011 Parent=0x00000015 SizeRef=715,168 Split=Y Selected=0x9B5D3198
DockNode ID=0x00000013 Parent=0x00000011 SizeRef=1449,203 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=1449,292 HiddenTabBar=1 Selected=0x9B5D3198
DockNode ID=0x00000012 Parent=0x00000015 SizeRef=31,168 HiddenTabBar=1 Selected=0x56009A08
DockNode ID=0x00000016 Parent=0x00000008 SizeRef=314,172 HiddenTabBar=1 Selected=0x3FC1A724
DockNode ID=0x00000006 Parent=0x11111111 SizeRef=289,1158 Split=Y Selected=0x36DC96AB
DockNode ID=0x00000009 Parent=0x00000006 SizeRef=449,488 Split=Y Selected=0x36DC96AB
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=449,556 Split=Y Selected=0x36DC96AB

View File

@ -1,5 +1,2 @@
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -Isrc/vendor/xxhash -Isrc/vendor/miniaudio -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\utils\Shader.cpp -o src\build\utils\Shader.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -Isrc/vendor/xxhash -Isrc/vendor/miniaudio -IC:/msys64/mingw64/include -Isrc\vendor\imgui -IC:\msys64\mingw64\lib\libyaml-cpp.a -MMD -MP -c src\src\core\utils\utils.cpp -o src\build\core\utils\utils.o
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -Isrc/vendor/xxhash -Isrc/vendor/miniaudio -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 -Isrc/vendor/box2d -Isrc/vendor/xxhash -Isrc/vendor/miniaudio -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
[LINK] g++ src\build\Engine.o src\build\main.o src\build\Renderer.o src\build\Components\AnimationComponent.o src\build\Components\AudioPlayerComponent.o src\build\Components\CameraComponent.o src\build\Components\LightComponent.o src\build\Components\ParticleComponent.o src\build\Components\PhysicsComponent.o src\build\Components\ScriptComponent.o src\build\Components\SpriteComponent.o src\build\Components\TextComonent.o src\build\Components\TilemapComponent.o src\build\core\audio\AudioEngine.o src\build\core\utils\AssetManager.o src\build\core\utils\EngineConfig.o src\build\core\utils\ExceptionHandler.o src\build\core\utils\FileDialog.o src\build\core\utils\input.o src\build\core\utils\LoadingWindow.o src\build\core\utils\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\Texture.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.o src\build\editor\windows\AudioInfo.o src\build\editor\windows\Inspector.o src\build\Entitys\Object.o src\build\utils\GameObjectsList.o src\build\utils\Shader.o src\build\utils\UID.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 src\build\imgui.o src\build\imgui_demo.o src\build\imgui_draw.o src\build\imgui_impl_glfw.o src\build\imgui_impl_opengl3.o src\build\imgui_tables.o src\build\imgui_widgets.o src\build\aabb.o src\build\arena_allocator.o src\build\array.o src\build\bitset.o src\build\body.o src\build\broad_phase.o src\build\constraint_graph.o src\build\contact.o src\build\contact_solver.o src\build\core.o src\build\distance.o src\build\distance_joint.o src\build\dynamic_tree.o src\build\geometry.o src\build\hull.o src\build\id_pool.o src\build\island.o src\build\joint.o src\build\manifold.o src\build\math_functions.o src\build\motor_joint.o src\build\mouse_joint.o src\build\mover.o src\build\prismatic_joint.o src\build\revolute_joint.o src\build\sensor.o src\build\shape.o src\build\solver.o src\build\solver_set.o src\build\table.o src\build\timer.o src\build\types.o src\build\weld_joint.o src\build\wheel_joint.o src\build\world.o src\build\xxhash.o src\build\miniaudio.o -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto -ldbghelp
[RUN] Executed app.exe successfully.

View File

@ -0,0 +1,34 @@
local obj = nil
local light = nil
local hue = 0.0
function HSVtoRGB(h, s, v)
local i = math.floor(h * 6)
local f = h * 6 - i
local p = v * (1 - s)
local q = v * (1 - f * s)
local t = v * (1 - (1 - f) * s)
i = i % 6
if i == 0 then return Vector3(v, t, p) end
if i == 1 then return Vector3(q, v, p) end
if i == 2 then return Vector3(p, v, t) end
if i == 3 then return Vector3(p, q, v) end
if i == 4 then return Vector3(t, p, v) end
if i == 5 then return Vector3(v, p, q) end
end
function OnInit()
obj = Engine.GetObjectByTag("Light")
if obj then
light = obj:GetComponent("LightComponent")
end
end
function OnUpdate(dt)
if not light then return end
hue = (hue + dt * 0.2) % 1.0
light:SetColor(HSVtoRGB(hue, 1.0, 1.0))
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1614
src/assets/scenes/lots.cene Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
#version 330 core
in vec2 TexCoords;
out vec4 FragColor;
uniform sampler2D uTexture;
uniform vec4 outlineColor = vec4(1.0, 1.0, 0.0, 1.0); // Yellow outline
uniform float threshold = 0.1; // Alpha threshold
uniform float outlineWidth = 1.0 / 256.0; // Adjust based on resolution
void main()
{
float alpha = texture(uTexture, TexCoords).a;
// If we're fully inside the texture, draw normally
if (alpha > threshold)
{
FragColor = texture(uTexture, TexCoords);
return;
}
// Sample neighbors to detect edges
bool isEdge = false;
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
if (x == 0 && y == 0) continue;
vec2 offset = vec2(x, y) * outlineWidth;
float neighborAlpha = texture(uTexture, TexCoords + offset).a;
if (neighborAlpha > threshold)
{
isEdge = true;
break;
}
}
if (isEdge) break;
}
if (isEdge)
FragColor = outlineColor;
else
discard;
}

View File

@ -0,0 +1,13 @@
#version 330 core
layout(location = 0) in vec3 aPos; // vertex position
layout(location = 1) in vec2 aTexCoord; // texture coordinate
uniform mat4 uMVP; // Model-View-Projection matrix
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoord;
gl_Position = uMVP * vec4(aPos, 1.0);
}

View File

@ -22,8 +22,6 @@ uniform int uClusterHeight;
uniform int uClusterCols;
uniform int uMaxLightsPerCluster;
#define MAX_LIGHTS 512
uniform vec2 uLightPos[MAX_LIGHTS];
uniform vec3 uLightColor[MAX_LIGHTS];
@ -36,13 +34,15 @@ layout(std430, binding = 1) readonly buffer ClusterLightBuffer {
void main()
{
// Flip UV to match expected orientation
vec2 rotatedUV = mix(uUVMin, uUVMax, vec2(vUV.y, 1.0 - vUV.x));
// Sample texture color
vec4 texColor = texture(uTex, rotatedUV);
if (texColor.a < 0.1)
discard;
// Sample and transform normal
vec3 n = texture(uNormalMap, rotatedUV).rgb * 2.0 - 1.0;
n.y = -n.y;
@ -54,6 +54,7 @@ void main()
vec3 normal = normalize(n);
vec3 finalLight = vec3(0.0);
// Determine cluster index
int cx = int(vFragScreenPos.x) / uClusterWidth;
int cy = int(vFragScreenPos.y) / uClusterHeight;
int clusterIndex = cy * uClusterCols + cx;
@ -75,13 +76,16 @@ void main()
}
}
vec3 result = texColor.rgb * finalLight;
// Apply lighting and multiply by alpha to suppress gray halo
vec3 litColor = texColor.rgb * finalLight * texColor.a;
result *= uBrightness;
// Post-processing
litColor *= uBrightness;
float gray = dot(result, vec3(0.299, 0.587, 0.114));
result = mix(vec3(gray), result, uSaturation);
float gray = dot(litColor, vec3(0.299, 0.587, 0.114));
litColor = mix(vec3(gray), litColor, uSaturation);
result = pow(result, vec3(1.0 / uGamma));
FragColor = vec4(clamp(result, 0.0, 1.0), texColor.a);
litColor = pow(litColor, vec3(1.0 / uGamma));
FragColor = vec4(clamp(litColor, 0.0, 1.0), texColor.a);
}

View File

@ -9,15 +9,14 @@ uniform vec2 uUVMax;
void main()
{
// Rotate UV 90 degrees to the right: (x, y) → (y, 1 - x)
vec2 rotatedUV = vec2(vUV.y, 1.0 - vUV.x);
// Interpolate UVs within atlas range
vec2 uv = mix(uUVMin, uUVMax, rotatedUV);
vec4 color = texture(uTex, uv);
if (color.a < 0.01)
discard;
FragColor = color;
}

View File

@ -17,6 +17,8 @@
#include "../core/utils/input.h"
#include "../core/types/vec2.h"
#include "../core/types/vec3.h"
#include <lua.hpp>
#include <memory>
@ -39,14 +41,36 @@ struct LuaVector2
{
float x, y;
};
#define LUA_VECTOR2_MT "LuaVector2Meta"
struct LuaVector3
{
float x, y, z;
};
#define LUA_ANIMATION_MT "LuaAnimationMeta"
#define LUA_VECTOR3_MT "LuaVector3Meta"
#define LUA_VECTOR2_MT "LuaVector2Meta"
struct LuaAnimationWrapper
{
AnimationComponent *comp;
};
struct LuaLightWrapper
{
LightComponent *comp;
};
#define LUA_LIGHT_MT "LuaLightMeta"
#define LUA_ANIMATION_MT "LuaAnimationMeta"
static core::types::Vec3 GetLuaVector3(lua_State* L, int index);
ScriptComponent::ScriptComponent(Object *owner) : Component(owner), L(nullptr) {}
ScriptComponent::~ScriptComponent()
@ -153,7 +177,6 @@ static Component *GetComponentByName(Object *obj, const std::string &type)
return nullptr;
}
static int Lua_GetMousePos(lua_State *L)
{
PROFILE_DEEP_SCOPE("Engine::GetMousePos");
@ -189,7 +212,21 @@ static int Lua_Object_GetComponent(lua_State *L)
lua_pushnil(L);
return 1;
}
// fallback for other components
if (strcmp(type, "LightComponent") == 0)
{
auto compPtr = wrapper->obj->GetComponent<LightComponent>();
if (compPtr)
{
LuaLightWrapper *lw = (LuaLightWrapper *)lua_newuserdata(L, sizeof(LuaLightWrapper));
lw->comp = compPtr.get();
luaL_getmetatable(L, LUA_LIGHT_MT);
lua_setmetatable(L, -2);
return 1;
}
lua_pushnil(L);
return 1;
}
Component *comp = GetComponentByName(wrapper->obj, type);
lua_pushlightuserdata(L, comp ? comp : nullptr);
return 1;
@ -268,6 +305,123 @@ static int Lua_GetObjectByTag(lua_State *L)
return 1;
}
static LightComponent *CheckLight(lua_State *L)
{
return ((LuaLightWrapper *)luaL_checkudata(L, 1, LUA_LIGHT_MT))->comp;
}
static int Lua_Light_GetColor(lua_State *L)
{
auto *light = CheckLight(L);
glm::vec3 c = light->GetColor();
LuaVector3 *vec = (LuaVector3 *)lua_newuserdata(L, sizeof(LuaVector3));
vec->x = c.x; vec->y = c.y; vec->z = c.z;
luaL_getmetatable(L, LUA_VECTOR3_MT);
lua_setmetatable(L, -2);
return 1;
}
static int Lua_Light_SetColor(lua_State *L)
{
auto* light = CheckLight(L);
core::types::Vec3 color = GetLuaVector3(L, 2);
light->SetColor(color);
return 0;
}
static int Lua_Light_GetIntensity(lua_State *L)
{
lua_pushnumber(L, CheckLight(L)->GetIntensity());
return 1;
}
static int Lua_Light_SetIntensity(lua_State *L)
{
CheckLight(L)->SetIntensity(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
static int Lua_Light_GetRadius(lua_State *L)
{
lua_pushnumber(L, CheckLight(L)->GetRadius());
return 1;
}
static int Lua_Light_SetRadius(lua_State *L)
{
CheckLight(L)->SetRadius(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
static int Lua_Light_GetFalloff(lua_State *L)
{
lua_pushnumber(L, CheckLight(L)->GetFalloff());
return 1;
}
static int Lua_Light_SetFalloff(lua_State *L)
{
CheckLight(L)->SetFalloff(static_cast<float>(luaL_checknumber(L, 2)));
return 0;
}
static int Lua_Light_GetType(lua_State *L)
{
lua_pushinteger(L, CheckLight(L)->GetType());
return 1;
}
static int Lua_Light_SetType(lua_State *L)
{
CheckLight(L)->SetType(luaL_checkinteger(L, 2));
return 0;
}
static int Lua_Light_Index(lua_State *L)
{
luaL_getmetatable(L, LUA_LIGHT_MT);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
return 1;
}
void RegisterLightType(lua_State *L)
{
luaL_newmetatable(L, LUA_LIGHT_MT);
lua_pushcfunction(L, Lua_Light_GetColor); lua_setfield(L, -2, "GetColor");
lua_pushcfunction(L, Lua_Light_SetColor); lua_setfield(L, -2, "SetColor");
lua_pushcfunction(L, Lua_Light_GetIntensity); lua_setfield(L, -2, "GetIntensity");
lua_pushcfunction(L, Lua_Light_SetIntensity); lua_setfield(L, -2, "SetIntensity");
lua_pushcfunction(L, Lua_Light_GetRadius); lua_setfield(L, -2, "GetRadius");
lua_pushcfunction(L, Lua_Light_SetRadius); lua_setfield(L, -2, "SetRadius");
lua_pushcfunction(L, Lua_Light_GetFalloff); lua_setfield(L, -2, "GetFalloff");
lua_pushcfunction(L, Lua_Light_SetFalloff); lua_setfield(L, -2, "SetFalloff");
lua_pushcfunction(L, Lua_Light_GetType); lua_setfield(L, -2, "GetType");
lua_pushcfunction(L, Lua_Light_SetType); lua_setfield(L, -2, "SetType");
lua_pushcfunction(L, Lua_Light_Index);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
// --- AnimationComponent Lua bindings ---
static AnimationComponent *CheckAnimationComponent(lua_State *L, int idx)
{
@ -347,8 +501,6 @@ static int Lua_Animation_SetEndFrame(lua_State *L)
return 0;
}
static int Lua_Animation_Index(lua_State *L)
{
// stack: [1]=userdata, [2]=key
@ -513,6 +665,102 @@ static int Lua_KeyDown(lua_State *L)
return 1;
}
static core::types::Vec3 GetLuaVector3(lua_State* L, int index)
{
if (luaL_testudata(L, index, LUA_VECTOR3_MT))
{
auto* vec = static_cast<LuaVector3*>(lua_touserdata(L, index));
return core::types::Vec3(vec->x, vec->y, vec->z);
}
else if (lua_istable(L, index))
{
core::types::Vec3 result;
static const char* keys1[3] = { "x", "y", "z" };
static const char* keys2[3] = { "r", "g", "b" };
const char** keys = keys1;
lua_pushstring(L, "x");
lua_rawget(L, index);
if (lua_isnil(L, -1)) {
keys = keys2;
lua_pop(L, 1);
}
for (int i = 0; i < 3; ++i)
{
lua_pushstring(L, keys[i]);
lua_rawget(L, index);
result[i] = static_cast<float>(lua_tonumber(L, -1));
lua_pop(L, 1);
}
return result;
}
luaL_error(L, "Expected Vector3 userdata or table with x/y/z or r/g/b");
return core::types::Vec3(0, 0, 0);
}
static int Lua_Vector3_New(lua_State *L)
{
PROFILE_DEEP_SCOPE("Vector3()");
LuaVector3 *vec = (LuaVector3 *)lua_newuserdata(L, sizeof(LuaVector3));
int nargs = lua_gettop(L);
vec->x = nargs >= 1 ? static_cast<float>(lua_tonumber(L, 1)) : 0.0f;
vec->y = nargs >= 2 ? static_cast<float>(lua_tonumber(L, 2)) : 0.0f;
vec->z = nargs >= 3 ? static_cast<float>(lua_tonumber(L, 3)) : 0.0f;
luaL_setmetatable(L, LUA_VECTOR3_MT);
return 1;
}
static int Lua_Vector3_Index(lua_State *L)
{
auto *vec = (LuaVector3 *)luaL_checkudata(L, 1, LUA_VECTOR3_MT);
const char *key = lua_tostring(L, 2);
if (!strcmp(key, "x"))
lua_pushnumber(L, vec->x);
else if (!strcmp(key, "y"))
lua_pushnumber(L, vec->y);
else if (!strcmp(key, "z"))
lua_pushnumber(L, vec->z);
else
lua_pushnil(L);
return 1;
}
static int Lua_Vector3_NewIndex(lua_State *L)
{
auto *vec = (LuaVector3 *)luaL_checkudata(L, 1, LUA_VECTOR3_MT);
const char *key = lua_tostring(L, 2);
float value = static_cast<float>(luaL_checknumber(L, 3));
if (!strcmp(key, "x"))
vec->x = value;
else if (!strcmp(key, "y"))
vec->y = value;
else if (!strcmp(key, "z"))
vec->z = value;
return 0;
}
void RegisterVector3Type(lua_State *L)
{
luaL_newmetatable(L, LUA_VECTOR3_MT);
lua_pushcfunction(L, Lua_Vector3_Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, Lua_Vector3_NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_pushcfunction(L, Lua_Vector3_New);
lua_setglobal(L, "Vector3");
}
static int Lua_Vector2_New(lua_State *L)
{
PROFILE_DEEP_SCOPE("Vector2()");
@ -603,7 +851,7 @@ void RegisterAnimationType(lua_State *L)
lua_setfield(L, -2, "GetEndFrame");
lua_pushcfunction(L, Lua_Animation_SetEndFrame);
lua_setfield(L, -2, "SetEndFrame");
// __index
lua_pushcfunction(L, Lua_Animation_Index);
lua_setfield(L, -2, "__index");
@ -632,8 +880,6 @@ void ScriptComponent::RegisterEngineBindings()
lua_setfield(L, -2, "GetMousePos");
lua_setglobal(L, "Engine");
}
void ScriptComponent::ReloadScript()
@ -651,8 +897,10 @@ void ScriptComponent::ReloadScript()
RegisterObjectType(L);
RegisterVector2Type(L);
RegisterVector3Type(L);
RegisterAnimationType(L);
RegisterEngineBindings();
RegisterLightType(L);
keycode_loader(L);

View File

@ -381,7 +381,7 @@ void Engine::ShowDebugOverlay(float deltaTime)
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",
ImGui::PlotLines("##FPS", fpsHistory.data(), fpsHistory.size(), 0, "FPS",
0.0f, maxFps, ImVec2(-1, 50));
}
else
@ -537,8 +537,6 @@ void Engine::Init()
m_animationsUpdates.reserve(512); // ~500 animated objects (characters, FX, etc.)
Logger::LogOk("Engine Core");
}
core::types::Vec2 ScreenToWorld(const core::types::Vec2 &screenPos, const core::types::Vec2 &viewportSize, const core::types::Vec2 &cameraPos, float zoom)
@ -563,7 +561,7 @@ void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom
const glm::vec2 screenSize = glm::vec2(Renderer::GetSize());
for (auto &root : objects)
if (!root->GetParent())
if (root && !root->GetParent())
m_collectStack.push_back(root);
while (!m_collectStack.empty())
@ -820,7 +818,7 @@ void Engine::Run()
}
for (auto &obj : objects)
if (!obj->GetParent())
if (obj && !obj->GetParent())
DrawObjectNode(obj);
ImGui::End();
@ -1152,26 +1150,51 @@ void Engine::Run()
}
}
if (ImGui::BeginDragDropTarget())
static std::shared_ptr<Object> previewObj = nullptr;
static uint64_t previewUAID = 0;
const ImGuiPayload *payload = ImGui::GetDragDropPayload();
bool draggingTexture = payload && payload->IsDataType("ASSET_TEXTURE") && payload->DataSize == sizeof(uint64_t);
bool mouseHeld = ImGui::IsMouseDown(ImGuiMouseButton_Left);
bool mouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
bool hoveringViewport = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
// While dragging and hovering viewport
if (draggingTexture && mouseHeld && hoveringViewport)
{
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("ASSET_TEXTURE"))
uint64_t uaid = *(const uint64_t *)payload->Data;
const auto *asset = AssetManager::GetAssetByID(uaid);
if (asset && asset->type == AssetType::Image)
{
if (payload->DataSize == sizeof(uint64_t))
if (!previewObj)
{
uint64_t uaid = *(const uint64_t *)payload->Data;
glm::vec2 worldPos = ScreenToWorld(screenMousePos, viewportSize, cameraPos, cameraZoom);
auto obj = std::make_shared<Object>("New Sprite");
obj->SetLocalPosition(worldPos);
auto sprite = obj->AddComponent<SpriteComponent>();
sprite->SetTexture(uaid);
objects.push_back(obj);
previewObj = std::make_shared<Object>(asset->filename);
previewObj->AddComponent<SpriteComponent>()->SetTexture(uaid);
objects.push_back(previewObj);
previewUAID = uaid;
}
glm::vec2 worldPos = ScreenToWorld(screenMousePos, viewportSize, cameraPos, cameraZoom);
previewObj->SetLocalPosition(worldPos);
selected = previewObj;
}
ImGui::EndDragDropTarget();
}
else if (previewObj && mouseReleased && hoveringViewport)
{
previewObj = nullptr;
previewUAID = 0;
}
else if (previewObj && (!hoveringViewport || !draggingTexture))
{
auto it = std::find(objects.begin(), objects.end(), previewObj);
if (it != objects.end())
objects.erase(it);
previewObj = nullptr;
selected = previewObj;
previewUAID = 0;
}
ImGui::End();
@ -1227,26 +1250,27 @@ void Engine::Run()
}
}
void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj)
void Engine::DrawObjectNode(const std::shared_ptr<Object>& obj)
{
if (!obj)
return;
PROFILE_ENGINE_SCOPE("Engine::DrawObjectNode");
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
ImGuiTreeNodeFlags_SpanAvailWidth |
ImGuiTreeNodeFlags_DefaultOpen |
(obj == selected ? ImGuiTreeNodeFlags_Selected : 0);
bool open = ImGui::TreeNodeEx((void *)(intptr_t)obj->uid.id, flags, "%s", obj->GetName().c_str());
bool open = ImGui::TreeNodeEx((void*)(intptr_t)obj->uid.id, flags, "%s", obj->GetName().c_str());
if (ImGui::IsItemClicked())
selected = obj;
// === Context Menu ===
if (ImGui::BeginPopupContextItem())
{
if (ImGui::MenuItem("Delete"))
{
pendingDeletion.push_back(obj);
}
if (ImGui::MenuItem("Create Child"))
{
auto child = std::make_shared<Object>("NewObject");
@ -1257,58 +1281,59 @@ void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj)
ImGui::EndPopup();
}
// === Drag Source ===
if (ImGui::BeginDragDropSource())
{
ImGui::SetDragDropPayload("OBJECT", &obj, sizeof(obj));
ImGui::Text("Move: %s", obj->GetName().c_str());
auto dragRef = obj;
ImGui::SetDragDropPayload("OBJECT", &dragRef, sizeof(dragRef));
ImGui::Text("%s", obj->GetName().c_str());
ImGui::EndDragDropSource();
}
// === Drop Target ===
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("OBJECT"))
if (auto payload = ImGui::AcceptDragDropPayload("OBJECT"))
{
auto dragged = *(std::shared_ptr<Object> *)payload->Data;
auto dragged = *static_cast<std::shared_ptr<Object>*>(payload->Data);
if (dragged != obj && dragged)
if (dragged && dragged != obj)
{
Object *oldParent = dragged->GetParent();
if (oldParent)
{
oldParent->RemoveChild(dragged.get());
for (const auto &candidate : objects)
bool valid = true;
for (Object* a = obj.get(); a; a = a->GetParent())
if (a == dragged.get())
{
if (candidate.get() == oldParent)
{
pendingDeletion.push_back(candidate);
break;
}
valid = false;
break;
}
}
else
{
objects.erase(std::remove(objects.begin(), objects.end(), dragged), objects.end());
}
obj->AddChild(dragged);
if (valid)
{
if (auto oldP = dragged->GetParent())
oldP->RemoveChild(dragged.get());
else
objects.erase(std::remove(objects.begin(), objects.end(), dragged), objects.end());
obj->AddChild(dragged);
dragged->SetWorldPosition(
dragged->GetWorldPosition() - obj->GetWorldPosition()
);
}
}
}
ImGui::EndDragDropTarget();
}
// === Children ===
if (open)
{
for (auto &child : obj->GetChildren())
DrawObjectNode(child);
for (auto& child : obj->GetChildren())
if (child)
DrawObjectNode(child);
ImGui::TreePop();
}
}
bool VerifySceneHash(const YAML::Node &root)
{
if (!root["scene_hash"] || !root["objects"])
@ -1422,6 +1447,7 @@ void Engine::LoadScene(const std::string &path)
loadingUI.Update(currentStep, currentDetail, 0.1f);
objects.clear();
Object::usedIDs.clear();
Logger::LogDebug("[LoadScene] Recreating Objects");
currentStep = "Creating Scene Objects";

View File

@ -10,16 +10,35 @@
#include "../Components/AnimationComponent.h"
#include "../Components/AudioPlayerComponent.h"
#include "../core/utils/Logging.h"
#include "../utils/UID.h"
#include <algorithm>
Object::Object(const std::string &name)
: name(name), localPosition(0.0f, 0.0f), localRotationDeg(0.0f), uid(), visable(true) {}
static constexpr float PI = 3.14159265358979323846f;
std::unordered_set<int> Object::usedIDs;
Object::Object(const std::string& name)
: name(name), localPosition(0.0f, 0.0f), localRotationDeg(0.0f), uid(), visable(true)
{
if (usedIDs.count(uid.id))
{
Logger::LogWarning("Constructor: Duplicate ID %d detected. Assigning a new ID.", uid.id);
uid.id = GetNextAvailableID();
}
usedIDs.insert(uid.id);
parent = nullptr;
}
Object::~Object()
{
usedIDs.erase(uid.id);
}
Object::~Object() {}
void Object::SetParent(Object *newParent)
{
@ -65,6 +84,30 @@ glm::vec2 Object::GetWorldPosition() const
return localPosition;
}
void Object::SetWorldPosition(const core::types::Vec2& worldPos) {
if (Object* parent = GetParent()) {
core::types::Vec2 parentPos = parent->GetWorldPosition();
core::types::Vec2 offset = worldPos - parentPos;
float parentDeg = parent->GetWorldRotation();
float rad = -parentDeg * (PI / 180.0f);
float c = std::cos(rad);
float s = std::sin(rad);
core::types::Vec2 local{
offset.x * c - offset.y * s,
offset.x * s + offset.y * c
};
SetLocalPosition(local);
}
else {
SetLocalPosition(worldPos);
}
}
float Object::GetLocalRotation() const
{
return localRotationDeg;
@ -75,6 +118,13 @@ void Object::SetLocalRotation(float deg)
localRotationDeg = deg;
}
float Object::GetWorldScale() const
{
return 1.0f;
}
float Object::GetWorldRotation() const
{
if (parent)
@ -95,7 +145,16 @@ void Object::SetVisable(bool state)
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; }
Object *Object::GetParent() const { return parent; }
Object* Object::GetParent() const
{
if (!this)
{
Logger::LogError("Object::GetParent() called on nullptr");
return nullptr;
}
return parent;
}
void Object::Save(YAML::Emitter &out) const
{
@ -128,9 +187,11 @@ void Object::Save(YAML::Emitter &out) const
void Object::Load(const YAML::Node &node)
{
name = node["name"].as<std::string>();
uid.uuid = node["uid"] ? node["uid"].as<std::string>() : GenerateUUID();
uid.id = node["id"] ? node["id"].as<int>() : 0;
int loadedID = node["id"] ? node["id"].as<int>() : 0;
auto pos = node["position"];
if (pos && pos.IsSequence() && pos.size() == 2)

View File

@ -9,6 +9,13 @@
#include <yaml-cpp/yaml.h>
#include "../utils/UID.h"
#include <unordered_set>
#include "../core/types/all.h"
class Component;
@ -28,6 +35,10 @@ public:
float GetLocalRotation() const;
void SetLocalRotation(float deg);
float GetWorldRotation() const;
void SetWorldPosition(const core::types::Vec2& worldPos);
float GetWorldScale() const;
void SetParent(Object *parent);
Object *GetParent() const;
@ -51,6 +62,9 @@ public:
UID uid;
int layer = 0;
static std::unordered_set<int> usedIDs;
private:
bool visable = true;
std::string name;
@ -59,6 +73,15 @@ private:
Object *parent = nullptr;
std::vector<std::shared_ptr<Object>> children;
std::vector<std::shared_ptr<Component>> components;
static int GetNextAvailableID()
{
static int nextID = 1;
while (usedIDs.count(nextID))
++nextID;
return nextID;
}
};
template <typename T>

View File

@ -26,6 +26,7 @@ static Shader tilemapShader;
static Shader extractShader;
static Shader blurShader;
static Shader compositeShader;
static Shader s_OutlineShader;
static GLuint extractFBO, extractTexture;
static GLuint blurFBO_H, blurTexture_H;
@ -195,9 +196,9 @@ void Renderer::InitQuadBatch()
void Renderer::Init()
{
Logger::LogVerbose("[Renderer] Create Primary");
// Main framebuffer
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
@ -220,41 +221,34 @@ void Renderer::Init()
InitQuad();
// Load lit shader
// Load shaders
spriteShader.LoadFromFile("src/assets/shaders/sprite.vert", "src/assets/shaders/sprite.frag");
unlitShader.LoadFromFile("src/assets/shaders/unlit.vert", "src/assets/shaders/unlit.frag");
tilemapShader.LoadFromFile("src/assets/shaders/tilemap.vert", "src/assets/shaders/tilemap.frag");
// Core renderers
{
spriteShader.LoadFromFile("src/assets/shaders/sprite.vert", "src/assets/shaders/sprite.frag");
unlitShader.LoadFromFile("src/assets/shaders/unlit.vert", "src/assets/shaders/unlit.frag");
tilemapShader.LoadFromFile("src/assets/shaders/tilemap.vert", "src/assets/shaders/tilemap.frag");
// Bloom pipeline
extractShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/extract.frag");
blurShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/blur.frag");
compositeShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/composite.frag");
// Bloom pipeline
extractShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/extract.frag");
blurShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/blur.frag");
compositeShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/composite.frag");
s_UnlitQuadShader.LoadFromFile("src/assets/shaders/unlit_quad.vert", "src/assets/shaders/unlit_quad.frag");
s_OutlineShader.LoadFromFile("src/assets/shaders/outline.vert", "src/assets/shaders/outline.frag");
s_UnlitQuadShader.LoadFromFile("src/assets/shaders/unlit_quad.vert", "src/assets/shaders/unlit_quad.frag");
Logger::LogOk("Shader Core");
}
Logger::LogOk("Shader Core");
InitQuadBatch();
Logger::LogVerbose("[Renderer] Color Correction Init");
SetColorCorrection(std::make_unique<ColorCorrection>());
{
Logger::LogVerbose("Renderer::InitLightUniforms(%d)", g_engineConfig.gl_maxLight);
InitLightUniforms(g_engineConfig.gl_maxLight);
}
Logger::LogVerbose("Renderer::InitLightUniforms(%d)", g_engineConfig.gl_maxLight);
InitLightUniforms(g_engineConfig.gl_maxLight);
glGenBuffers(1, &s_ClusterSSBO);
Logger::LogVerbose("[Renderer] Bloom Init");
// Bloom extract
glGenFramebuffers(1, &extractFBO);
glBindFramebuffer(GL_FRAMEBUFFER, extractFBO);
glGenTextures(1, &extractTexture);
@ -264,6 +258,7 @@ void Renderer::Init()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, extractTexture, 0);
// Horizontal blur
glGenFramebuffers(1, &blurFBO_H);
glBindFramebuffer(GL_FRAMEBUFFER, blurFBO_H);
glGenTextures(1, &blurTexture_H);
@ -273,6 +268,7 @@ void Renderer::Init()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurTexture_H, 0);
// Vertical blur
glGenFramebuffers(1, &blurFBO_V);
glBindFramebuffer(GL_FRAMEBUFFER, blurFBO_V);
glGenTextures(1, &blurTexture_V);
@ -282,7 +278,7 @@ void Renderer::Init()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blurTexture_V, 0);
// Final composite target
// Final bloom composite
glGenFramebuffers(1, &bloomFBO);
glBindFramebuffer(GL_FRAMEBUFFER, bloomFBO);
glGenTextures(1, &bloomTexture);
@ -295,7 +291,7 @@ void Renderer::Init()
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Logger::LogVerbose("[Renderer] Creating Default Normal");
unsigned char flatNormal[3] = {128, 128, 255};
glGenTextures(1, &defaultNormalMap);
glBindTexture(GL_TEXTURE_2D, defaultNormalMap);
@ -513,6 +509,7 @@ void Renderer::DrawTilemap(TilemapComponent *tilemap,
int gx, gy;
TilemapComponent::UnpackCoord(key, gx, gy);
// world→screen position
glm::vec2 worldPos = {gx * tileSize.x, gy * tileSize.y};
glm::vec2 screenPos = (worldPos - cameraPos) * zoom + screenCenter;
@ -520,8 +517,9 @@ void Renderer::DrawTilemap(TilemapComponent *tilemap,
// quad size in screenspace
glm::vec2 finalSize = tileSize * zoom;
// fetch UVs (already handles flipping for you)
auto uvRect = atlas->GetFrameUVRect(tileIndex);
const core::types::Vec2 uvMin = atlas->GetFrameUV(tileIndex);
const core::types::Vec2 uvMax = uvMin + atlas->GetFrameSizeUV();
// batch that sprite
BatchedSprite entry{};
@ -533,8 +531,8 @@ void Renderer::DrawTilemap(TilemapComponent *tilemap,
entry.renderType = RenderType::Unlit;
entry.sprite = nullptr;
entry.texCoords = glm::vec4(
uvRect.min.x, uvRect.min.y,
uvRect.max.x, uvRect.max.y);
uvMin.x, uvMin.y,
uvMax.x, uvMax.y);
SortedDrawEntry drawEntry{};
drawEntry.sprite = entry;

View File

@ -22,7 +22,7 @@ struct ColorCorrection
float saturation = 1.0f;
float gamma = 1.0f;
bool bloom = true;
bool bloom = false;
float threshold = 1.0f;
float intensity = 1.2f;
@ -54,14 +54,18 @@ struct QuadInstance
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& pos, float zoom, const glm::vec2& cameraPos);
static void DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &pos, float zoom, const glm::vec2 &cameraPos);
static void AddLight(const glm::vec2 &screenPos, const glm::vec3 &color, float intensity, float radius);
static void ClearLights();
@ -105,6 +109,7 @@ public:
static void FlushQuads();
private:
static std::vector<Light> s_Lights;
@ -119,10 +124,11 @@ private:
static GLuint LoadShader(const char *vertexSrc, const char *fragmentSrc);
static std::unique_ptr<ColorCorrection> s_ColorCorrection;
// Clustered Lighting
static constexpr int CLUSTER_SIZE = 16;
static constexpr int MAX_LIGHTS_PER_CLUSTER = 32;
struct Cluster
{
std::vector<int> lightIndices;
@ -143,4 +149,5 @@ private:
static void *s_QuadMappedPtr;
static GLuint s_QuadPersistentFlags;
};

View File

@ -157,20 +157,14 @@ void AudioEngine::SetVolume(uint64_t uaid, float v) {
if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) {
ma_sound_set_volume(it->second, v);
}
else
{
Logger::LogError("[AudioEngine] Invalid Asset ID");
}
}
void AudioEngine::SetLooping(uint64_t uaid, bool loop) {
if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) {
ma_sound_set_looping(it->second, loop ? MA_TRUE : MA_FALSE);
}
else
{
Logger::LogError("[AudioEngine] Invalid Asset ID");
}
}
void AudioEngine::Cleanup(uint64_t uaid) {

View File

@ -27,6 +27,8 @@ namespace core
Vec3 operator*(const Vec3 &rhs) const { return {x * rhs.x, y * rhs.y, z * rhs.z}; }
Vec3 &operator+=(const Vec3 &rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; }
Vec3 &operator-=(const Vec3 &rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; }
Vec3 &operator*=(float scalar) { x *= scalar; y *= scalar; z *= scalar; return *this; }
@ -89,9 +91,29 @@ namespace core
{
return os << "(" << v.x << ", " << v.y << ", " << v.z << ")";
}
inline float& operator[](int i) {
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
default: throw std::out_of_range("Vec3 index out of range");
}
}
inline const float& operator[](int i) const {
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
default: throw std::out_of_range("Vec3 index out of range");
}
}
};
inline Vec3 operator*(float scalar, const Vec3 &v) { return v * scalar; }
} // namespace types
} // namespace core

View File

@ -3,6 +3,7 @@
#include <fstream>
#include <iostream>
#include "../audio/AudioEngine.h"
#include "LoadingWindow.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
@ -368,8 +369,18 @@ void AssetManager::Load(const YAML::Node &node)
if (!node)
return;
LoadingWindow loadingUI;
loadingUI.Create("Loading Assets");
const size_t total = node.size();
size_t index = 0;
for (const auto &item : node)
{
uint64_t uaid = item["uaid"].as<uint64_t>();
std::string path = item["path"].as<std::string>();
AssetType type = static_cast<AssetType>(item["type"].as<int>());
@ -382,6 +393,9 @@ void AssetManager::Load(const YAML::Node &node)
else
continue;
float progress = static_cast<float>(index) / total;
loadingUI.Update("Loading Asset", path, progress);
asset->uaid = uaid;
asset->path = path;
asset->filename = item["filename"] ? item["filename"].as<std::string>() : GetFilenameFromPath(path);
@ -402,7 +416,11 @@ void AssetManager::Load(const YAML::Node &node)
LoadImageInternal(path, uaid);
else if (type == AssetType::Audio)
LoadAudioInternal(path, uaid);
++index;
}
loadingUI.Destroy();
}
void ImageAssetInfo::Save(YAML::Emitter &out) const

View File

@ -9,7 +9,7 @@
#include <GL/glew.h>
#include "miniaudio.h"
enum class AssetType { Image, Audio };
enum class AssetType { Image, Audio, Unknown };
struct AssetInfo
{

View File

@ -7,8 +7,11 @@
#include <string>
#include <filesystem>
#include "FileDialog.h"
#include <vector>
//TODO: Make this all memroy safe.
// Correctly null-terminated filter strings
static std::unordered_map<FileDialogType, const char*> filters = {
{ FileDialogType::Images, "Image Files\0*.png;*.jpg;*.jpeg;*.bmp;*.tga;*.gif;*.dds\0All Files\0*.*\0" },
{ FileDialogType::Scenes, "CreateScene Files\0*.cene;*.cscene;*.yaml\0All Files\0*.*\0" },
@ -101,3 +104,44 @@ std::string CreateFileDialog(FileDialogType type) {
return "";
}
std::vector<std::string> OpenMultipleFilesDialog(FileDialogType type) {
const size_t bufferSize = 32768;
std::vector<char> fileBuffer(bufferSize, 0);
OPENFILENAMEA ofn = {};
ofn.lStructSize = sizeof(ofn);
const char* filterStr = filters.count(type) ? filters[type] : filters[FileDialogType::All];
ofn.lpstrFilter = filterStr;
ofn.lpstrFile = fileBuffer.data();
ofn.nMaxFile = static_cast<DWORD>(fileBuffer.size());
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
OFN_NOCHANGEDIR | OFN_ALLOWMULTISELECT | OFN_EXPLORER;
auto originalPath = std::filesystem::current_path();
bool result = GetOpenFileNameA(&ofn);
std::filesystem::current_path(originalPath);
std::vector<std::string> selectedFiles;
if (!result || fileBuffer[0] == '\0')
return selectedFiles;
std::string directory(fileBuffer.data());
char* p = fileBuffer.data() + directory.size() + 1;
if (*p == '\0') {
selectedFiles.push_back(directory);
} else {
while (*p != '\0') {
std::string filename(p);
std::filesystem::path fullPath = std::filesystem::path(directory) / filename;
selectedFiles.push_back(fullPath.string());
p += filename.size() + 1;
}
}
return selectedFiles;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <string>
#include <vector>
enum class FileDialogType {
Images,
@ -16,4 +18,6 @@ enum class FileDialogType {
std::string OpenFileDialog(FileDialogType type);
std::string SaveFileDialog(FileDialogType type);
std::string CreateFileDialog(FileDialogType type);
std::vector<std::string> OpenMultipleFilesDialog(FileDialogType type);

View File

@ -4,25 +4,29 @@
#include <algorithm>
static const wchar_t* kWindowClass = L"LoadingWndClass";
static LoadingWindow* g_instance = nullptr;
void LoadingWindow::Create(const std::string& title)
{
g_instance = this;
windowTitle = std::wstring(title.begin(), title.end());
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = kWindowClass;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
// Register the window class (once)
static bool registered = false;
if (!registered)
{
WNDCLASS wc = {};
wc.lpfnWndProc = LoadingWindow::ThunkProc;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = kWindowClass;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
registered = true;
}
hwnd = CreateWindowEx(
0, kWindowClass, windowTitle.c_str(),
WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 200,
nullptr, nullptr, GetModuleHandle(nullptr), nullptr
nullptr, nullptr, GetModuleHandle(nullptr), this
);
ShowWindow(hwnd, SW_SHOWNORMAL);
@ -53,12 +57,28 @@ void LoadingWindow::Destroy()
DestroyWindow(hwnd);
hwnd = nullptr;
}
UnregisterClass(kWindowClass, GetModuleHandle(nullptr));
}
LRESULT CALLBACK LoadingWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
LRESULT CALLBACK LoadingWindow::ThunkProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_PAINT && g_instance)
if (msg == WM_NCCREATE)
{
// Store 'this' in GWLP_USERDATA
CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
return TRUE;
}
LoadingWindow* self = reinterpret_cast<LoadingWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
if (self)
return self->WndProc(hWnd, msg, wParam, lParam);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT LoadingWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
@ -66,8 +86,8 @@ LRESULT CALLBACK LoadingWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPAR
RECT r;
GetClientRect(hWnd, &r);
std::wstring step = std::wstring(g_instance->currentStep.begin(), g_instance->currentStep.end());
std::wstring detail = std::wstring(g_instance->currentDetail.begin(), g_instance->currentDetail.end());
std::wstring step = std::wstring(currentStep.begin(), currentStep.end());
std::wstring detail = std::wstring(currentDetail.begin(), currentDetail.end());
SetBkMode(hdc, TRANSPARENT);
SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
@ -87,7 +107,7 @@ LRESULT CALLBACK LoadingWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPAR
DeleteObject(bg);
// Progress bar fill
int barWidth = static_cast<int>((progressRect.right - progressRect.left) * g_instance->currentProgress);
int barWidth = static_cast<int>((progressRect.right - progressRect.left) * currentProgress);
RECT fillRect = { progressRect.left, progressRect.top, progressRect.left + barWidth, progressRect.bottom };
HBRUSH fill = CreateSolidBrush(RGB(70, 140, 255));
FillRect(hdc, &fillRect, fill);

View File

@ -5,7 +5,7 @@
class LoadingWindow {
public:
void Create(const std::string& title = "Loading...");
void Update(const std::string& step, const std::string& detail, float progress); // NEW
void Update(const std::string& step, const std::string& detail, float progress);
void Destroy();
private:
@ -15,5 +15,9 @@ private:
std::string currentDetail;
float currentProgress = 0.0f;
static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Static thunk to route Windows messages to the correct instance
static LRESULT CALLBACK ThunkProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Actual message handler for this instance
LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};

View File

@ -5,8 +5,13 @@
#include "../../core/utils/AssetManager.h"
#include "../../Renderer.h"
#include "../../core/utils/Logging.h"
#include "../../core/utils/LoadingWindow.h"
static std::string assetSearchQuery;
static bool sortAscending = true;
static int sortMode = 0; // 0 = Name, 1 = Type, 2 = UAID
static bool showImages = true;
static bool showAudio = true;
void ShowAssetBrowser()
{
@ -15,33 +20,95 @@ void ShowAssetBrowser()
if (!g_engineConfig.settings.show_asset_window)
return;
ImGui::Begin("Asset Browser");
ImGui::Begin("Resources");
if (ImGui::Button("Load Image"))
// Top bar layout
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 4));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 6));
// Load Buttons
if (ImGui::Button("Load Images"))
{
std::string path = OpenFileDialog(FileDialogType::Images);
if (!path.empty())
AssetManager::LoadAssetAsync(path, AssetType::Image);
std::vector<std::string> paths = OpenMultipleFilesDialog(FileDialogType::Images);
if (!paths.empty())
{
LoadingWindow loader;
loader.Create("Loading Images...");
for (size_t i = 0; i < paths.size(); ++i)
{
const std::string &path = paths[i];
loader.Update("Loading Image", path, static_cast<float>(i + 1) / paths.size());
AssetManager::LoadAssetAsync(path, AssetType::Image);
}
loader.Destroy();
}
}
ImGui::SameLine();
if (ImGui::Button("Load Audio"))
{
std::string path = OpenFileDialog(FileDialogType::Audio);
if (!path.empty())
AssetManager::LoadAssetAsync(path, AssetType::Audio);
std::vector<std::string> paths = OpenMultipleFilesDialog(FileDialogType::Audio);
if (!paths.empty())
{
LoadingWindow loader;
loader.Create("Loading Audio...");
for (size_t i = 0; i < paths.size(); ++i)
{
const std::string &path = paths[i];
loader.Update("Loading Audio", path, static_cast<float>(i + 1) / paths.size());
AssetManager::LoadAssetAsync(path, AssetType::Audio);
}
loader.Destroy();
}
}
ImGui::Separator();
ImGui::SameLine();
if (ImGui::Button("Filters / Sort"))
ImGui::OpenPopup("AssetBrowserFilterPopup");
// Filters Popup
if (ImGui::BeginPopup("AssetBrowserFilterPopup"))
{
ImGui::Text("Sort By:");
if (ImGui::Selectable("Name", sortMode == 0))
sortMode = 0;
if (ImGui::Selectable("Type", sortMode == 1))
sortMode = 1;
if (ImGui::Selectable("UAID", sortMode == 2))
sortMode = 2;
ImGui::Checkbox("Ascending", &sortAscending);
ImGui::Separator();
ImGui::Text("Type Filters:");
ImGui::Checkbox("Images", &showImages);
ImGui::Checkbox("Audio", &showAudio);
ImGui::EndPopup();
}
ImGui::PopStyleVar(2);
// Search field (spaced below buttons)
ImGui::Spacing();
ImGui::Spacing();
char buffer[256] = {};
strncpy(buffer, assetSearchQuery.c_str(), sizeof(buffer) - 1);
if (ImGui::InputTextWithHint("##Search", "Search...", buffer, sizeof(buffer)))
ImGui::SetNextItemWidth(-1);
if (ImGui::InputTextWithHint("##Search", "Search", buffer, sizeof(buffer)))
{
assetSearchQuery = buffer;
}
ImGui::Separator();
ImGui::BeginChild("##AssetScroll", ImVec2(0, 0), true);
@ -56,25 +123,44 @@ void ShowAssetBrowser()
ImGui::Columns(columns, nullptr, false);
int idx = 0;
for (const auto &[uaid, baseAsset] : AssetManager::GetAllAssets())
// Collect, filter and sort assets
std::vector<const AssetInfo *> sortedAssets;
for (const auto &[uaid, asset] : AssetManager::GetAllAssets())
{
if (!baseAsset || !baseAsset->loaded)
if (!asset || !asset->loaded)
continue;
if (!assetSearchQuery.empty() &&
baseAsset->filename.find(assetSearchQuery) == std::string::npos &&
baseAsset->path.find(assetSearchQuery) == std::string::npos)
{
asset->filename.find(assetSearchQuery) == std::string::npos &&
asset->path.find(assetSearchQuery) == std::string::npos)
continue;
}
if ((asset->type == AssetType::Image && !showImages) ||
(asset->type == AssetType::Audio && !showAudio))
continue;
sortedAssets.push_back(asset.get());
}
std::sort(sortedAssets.begin(), sortedAssets.end(), [](const AssetInfo *a, const AssetInfo *b)
{
switch (sortMode)
{
case 0: return sortAscending ? a->filename < b->filename : a->filename > b->filename;
case 1: return sortAscending ? a->type < b->type : a->type > b->type;
case 2: return sortAscending ? a->uaid < b->uaid : a->uaid > b->uaid;
default: return true;
} });
int idx = 0;
for (const AssetInfo *baseAsset : sortedAssets)
{
ImGui::PushID(idx++);
std::string displayName = GetFilenameFromPath(baseAsset->path);
if (baseAsset->type == AssetType::Image)
{
const auto *imageAsset = dynamic_cast<const ImageAssetInfo *>(baseAsset.get());
const auto *imageAsset = dynamic_cast<const ImageAssetInfo *>(baseAsset);
if (!imageAsset)
{
ImGui::PopID();
@ -86,27 +172,23 @@ void ShowAssetBrowser()
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
{
ImGui::SetDragDropPayload("ASSET_TEXTURE", &uaid, sizeof(uint64_t));
ImGui::Image((ImTextureID)(intptr_t)imageAsset->textureID,
ImVec2(thumbnailSize * 2, thumbnailSize * 2));
ImGui::SetDragDropPayload("ASSET_TEXTURE", &baseAsset->uaid, sizeof(uint64_t));
ImGui::Text("%s", displayName.c_str());
ImGui::EndDragDropSource();
}
}
else if (baseAsset->type == AssetType::Audio)
{
std::string buttonLabel = baseAsset->filetype.empty() ? "Audio" : (baseAsset->filetype);
std::string buttonLabel = baseAsset->filetype.empty() ? "Audio" : baseAsset->filetype;
ImGui::Button(buttonLabel.c_str(), ImVec2(thumbnailSize, thumbnailSize));
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
{
ImGui::SetDragDropPayload("ASSET_AUDIO", &uaid, sizeof(uint64_t));
ImGui::Text("Audio: %s", displayName.c_str());
ImGui::SetDragDropPayload("ASSET_AUDIO", &baseAsset->uaid, sizeof(uint64_t));
ImGui::Text("%s", displayName.c_str());
ImGui::EndDragDropSource();
}
}
else
{
ImGui::PopID();
@ -121,7 +203,7 @@ void ShowAssetBrowser()
if (baseAsset->type == AssetType::Image)
{
const auto *imageAsset = dynamic_cast<const ImageAssetInfo *>(baseAsset.get());
const auto *imageAsset = dynamic_cast<const ImageAssetInfo *>(baseAsset);
if (imageAsset)
{
ImGui::Text("Texture ID: %u", imageAsset->textureID);
@ -141,7 +223,7 @@ void ShowAssetBrowser()
if (ImGui::MenuItem("Unload"))
{
Logger::LogInfo("[AssetBrowser] Unloaded: %s", baseAsset->path.c_str());
AssetManager::UnloadAsset(uaid);
AssetManager::UnloadAsset(baseAsset->uaid);
ImGui::EndPopup();
ImGui::PopID();
continue;

View File

@ -738,10 +738,12 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (ImGui::Button("Stop"))
audio->Stop();
// Looping
static bool loop = false;
loop = ImGui::Checkbox("Loop", &loop);
audio->SetLooping(loop);
if (ImGui::Checkbox("Loop", &loop))
{
audio->SetLooping(loop);
}
// Volume
static float volume = 1.0f;

View File

@ -24,6 +24,8 @@ std::string GenerateUUID() {
return ss.str();
}
UID::UID()
: id(nextID++), uuid(GenerateUUID()) {}