stuff, profiler update. uk, particles

This commit is contained in:
OusmBlueNinja 2025-04-20 22:41:27 -05:00
parent 549dac776a
commit 494aeeb072
13 changed files with 884 additions and 17811 deletions

View File

@ -27,7 +27,7 @@ DockId=0x00000009,0
[Window][Viewport]
Pos=344,19
Size=1576,535
Size=1576,495
Collapsed=0
DockId=0x0000000B,0
@ -36,14 +36,14 @@ Size=1920,19
Collapsed=0
[Window][Performance Info]
Pos=1588,840
Size=332,118
Pos=1588,867
Size=332,310
Collapsed=0
DockId=0x00000003,0
DockId=0x00000006,0
[Window][Console]
Pos=344,840
Size=612,337
Pos=344,867
Size=612,310
Collapsed=0
DockId=0x0000000D,0
@ -54,8 +54,8 @@ Collapsed=0
DockId=0x0000000B,1
[Window][Profiler]
Pos=344,556
Size=1576,282
Pos=344,516
Size=1576,349
Collapsed=0
DockId=0x0000000C,0
@ -78,31 +78,29 @@ Collapsed=0
DockId=0x00000005,1
[Window][Color Correction]
Pos=1588,960
Size=332,217
Pos=0,19
Size=342,662
Collapsed=0
DockId=0x00000004,0
DockId=0x00000009,1
[Window][Asset Browser]
Pos=958,840
Size=628,337
Pos=958,867
Size=628,310
Collapsed=0
DockId=0x0000000E,0
[Docking][Data]
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1920,1158 Split=X
DockNode ID=0x00000001 Parent=0x11111111 SizeRef=342,701 Split=Y Selected=0x12EF0F59
DockNode ID=0x00000009 Parent=0x00000001 SizeRef=385,662 HiddenTabBar=1 Selected=0x12EF0F59
DockNode ID=0x00000009 Parent=0x00000001 SizeRef=385,662 Selected=0x12EF0F59
DockNode ID=0x0000000A Parent=0x00000001 SizeRef=385,494 HiddenTabBar=1 Selected=0x36DC96AB
DockNode ID=0x00000002 Parent=0x11111111 SizeRef=1576,701 Split=Y Selected=0xC450F867
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,819 Split=Y Selected=0xC450F867
DockNode ID=0x0000000B Parent=0x00000007 SizeRef=1576,535 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x0000000C Parent=0x00000007 SizeRef=1576,282 HiddenTabBar=1 Selected=0x9B5D3198
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,337 Split=X Selected=0x9B5D3198
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=737,481 Split=X Selected=0x9B5D3198
DockNode ID=0x0000000D Parent=0x00000005 SizeRef=363,248 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x0000000E Parent=0x00000005 SizeRef=372,248 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=197,481 Split=Y Selected=0x3FC1A724
DockNode ID=0x00000003 Parent=0x00000006 SizeRef=226,118 HiddenTabBar=1 Selected=0x3FC1A724
DockNode ID=0x00000004 Parent=0x00000006 SizeRef=226,217 HiddenTabBar=1 Selected=0xA873C17F
DockNode ID=0x00000007 Parent=0x00000002 SizeRef=606,846 Split=Y Selected=0xC450F867
DockNode ID=0x0000000B Parent=0x00000007 SizeRef=1576,495 CentralNode=1 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x0000000C Parent=0x00000007 SizeRef=1576,349 HiddenTabBar=1 Selected=0x9B5D3198
DockNode ID=0x00000008 Parent=0x00000002 SizeRef=606,310 Split=X Selected=0x9B5D3198
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=1242,481 Split=X Selected=0x9B5D3198
DockNode ID=0x0000000D Parent=0x00000005 SizeRef=612,248 HiddenTabBar=1 Selected=0xEA83D666
DockNode ID=0x0000000E Parent=0x00000005 SizeRef=628,248 HiddenTabBar=1 Selected=0x36AF052B
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=332,481 HiddenTabBar=1 Selected=0x3FC1A724

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,3 @@
[COMPILE] g++ -std=c++20 -Wall -g -Isrc/include -Isrc/include/lua -Isrc/vendor -Isrc/vendor/imgui -Isrc/vendor/box2d -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 -IC:/msys64/mingw64/include -IC:\msys64\mingw64\lib\libyaml-cpp.a -Isrc\vendor\imgui -MMD -MP -c src\src\Engine.cpp -o src\build\Engine.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\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\utils\AssetLoader.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\Logging.o src\build\core\utils\Profiler.o src\build\core\utils\utils.o src\build\editor\windows\AssetBrowser.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 -o src\build\app.exe -LC:\msys64\mingw64\lib -lglfw3 -lglew32 -lopengl32 -lgdi32 -lyaml-cpp -lcomdlg32 -lssl -lcrypto
[RUN] Executed app.exe successfully.

View File

@ -0,0 +1,81 @@
engine_version: 0.1.0
scene_name: Fire
scene_hash: 815bf9a2e3aaeb3f7898921c2f69042b5a314460409a7b2a6251719d479dd3f9
format_version: 1
objects:
- name: Ground
uid: 5c4cf2d6c88d44468a8ea6f9caf56740
id: 226
position: [0, 0]
rotation: 0
layer: 0
visable: true
components:
- type: SpriteComponent
texture: C:\Users\spenc\OneDrive\Pictures\textures\ganges_river_pebbles_diff_1k.png
normalMap: C:\Users\spenc\OneDrive\Pictures\textures\ganges_river_pebbles_nor_gl_1k.png
renderType: Lit
children: []
- name: Camera
uid: 143107e596db4a818088a81cda665c71
id: 227
position: [-900, 0]
rotation: 0
layer: 0
visable: true
components:
- type: CameraComponent
fov: 45
aspect: 1.76999998
zoom: 1
primary: true
children: []
- name: Light
uid: 84bffd029cd444f7b6220dc7d1b773d9
id: 232
position: [-1024, 0]
rotation: 0
layer: 0
visable: true
components:
- type: LightComponent
color:
- 1
- 0.0882352591
- 0
intensity: 0.349999994
radius: 999
falloff: 1
type: 0
children: []
- name: Fire
uid: bebfceeddc754dba94ea936feb49ebda
id: 233
position: [-1024, 277]
rotation: 0
layer: 0
visable: true
components:
- type: ParticleComponent
maxParticles: 1000
emissionRate: 100
lifeMin: 0.5
lifeMax: 1.5
sizeMin: 5
sizeMax: 75
speedMin: 100
speedMax: 660
direction: [0, -1]
spread: 0.800000012
startColor: [1, 0, 0, 1]
endColor: [0.882352948, 1, 0, 1]
loop: true
burst: false
children: []
color_correction:
brightness: 2
saturation: 2
gamma: 1.05999994
bloom: true
intensity: 1.40999997
threshold: 0.300000012

View File

@ -1,6 +1,6 @@
engine_version: 0.1.0
scene_name: lighting_test_2
scene_hash: f1652fd7fe4a93ec56d94e4c343609bbce35d5190f9badd04e281fa8404eeaed
scene_hash: 6622c33531e5f56eb45af8ba16579b92f107df240dda01c57391813a98dbb3cb
format_version: 1
objects:
- name: Tiles
@ -115,7 +115,7 @@ objects:
- name: Red
uid: 6afde2dd47aa4557b6afb1a607c99dc8
id: 13
position: [702.594421, 1407.01233]
position: [818.390869, 568.231567]
rotation: 0
layer: 2
visable: true
@ -133,7 +133,7 @@ objects:
- name: Green
uid: 0f950d76d24b4dc18f54cab2c3aaaf9a
id: 14
position: [853.004333, 554.148438]
position: [1521.5116, 1073.82153]
rotation: 0
layer: 2
visable: true
@ -151,7 +151,7 @@ objects:
- name: Blue
uid: 09f722f51c7c4b0f98de3a0a16d127c4
id: 15
position: [1516.40125, 1110.83923]
position: [732.097473, 1429.9469]
rotation: 0
layer: 2
visable: true
@ -209,6 +209,78 @@ objects:
zoom: 1
primary: true
children: []
- name: Flower Peddels
uid: 20b09854d73f4928860c608c7b143f3f
id: 16
position: [397, 852]
rotation: 0
layer: 0
visable: true
components:
- type: ParticleComponent
maxParticles: 1000
emissionRate: 100
lifeMin: 5
lifeMax: 10
sizeMin: 1
sizeMax: 10
speedMin: 100
speedMax: 300
direction: [0, 1]
spread: 1.57000005
startColor: [0, 0.705882311, 1, 1]
endColor: [0.792156875, 0, 1, 1]
loop: true
burst: false
children: []
- name: Rain
uid: 19253834f75d4e69a618c45ddb14e8b0
id: 81
position: [397, 852]
rotation: 0
layer: 0
visable: true
components:
- type: ParticleComponent
maxParticles: 1000
emissionRate: 1000
lifeMin: 0.5
lifeMax: 1.5
sizeMin: 5
sizeMax: 10
speedMin: 1000
speedMax: 3000
direction: [0, 1]
spread: 0.860000014
startColor: [0, 0.647058964, 1, 1]
endColor: [0, 0, 1, 0]
loop: true
burst: false
children: []
- name: Fire
uid: 995af3d194694309a490504eaee3ae92
id: 116
position: [458, 2244]
rotation: 0
layer: 0
visable: true
components:
- type: ParticleComponent
maxParticles: 1000
emissionRate: 251
lifeMin: 0.5
lifeMax: 1.5
sizeMin: 5
sizeMax: 98.3000031
speedMin: 100
speedMax: 500
direction: [0, -1]
spread: 0.419999987
startColor: [1, 0.941176474, 0, 1]
endColor: [1, 0, 0, 0]
loop: true
burst: false
children: []
color_correction:
brightness: 2
saturation: 2

View File

@ -55,19 +55,6 @@ void ParticleComponent::Update(float dt)
particles.end());
}
void ParticleComponent::Render()
{
for (const auto &p : particles)
{
float t = std::clamp(1.0f - (p.life / settings.lifeMax), 0.0f, 1.0f);
Color color = core::types::Color::Lerp(settings.startColor, settings.endColor, t);
Renderer::DrawQuad(
p.position,
core::types::Vec2(p.size, p.size),
p.rotation,
color);
}
}
void ParticleComponent::SpawnParticle()
{
@ -86,14 +73,10 @@ void ParticleComponent::SpawnParticle()
particles.push_back(p);
}
void ParticleComponent::Save(YAML::Emitter &out) const
void ParticleComponent::Save(YAML::Emitter& out) const
{
out << YAML::Key << "ParticleComponent" << YAML::Value << YAML::BeginMap;
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << "ParticleComponent";
out << YAML::Key << "maxParticles" << YAML::Value << settings.maxParticles;
out << YAML::Key << "emissionRate" << YAML::Value << settings.emissionRate;
@ -107,7 +90,7 @@ void ParticleComponent::Save(YAML::Emitter &out) const
out << YAML::Key << "direction" << YAML::Value << YAML::Flow << YAML::BeginSeq << settings.direction.x << settings.direction.y << YAML::EndSeq;
out << YAML::Key << "spread" << YAML::Value << settings.spread;
auto writeColor = [](const Color &c, YAML::Emitter &out)
auto writeColor = [](const Color& c, YAML::Emitter& out)
{
out << YAML::Flow << YAML::BeginSeq << c.r << c.g << c.b << c.a << YAML::EndSeq;
};
@ -123,40 +106,40 @@ void ParticleComponent::Save(YAML::Emitter &out) const
}
void ParticleComponent::Load(const YAML::Node &node)
void ParticleComponent::Load(const YAML::Node& node)
{
if (!node["ParticleComponent"] || !node["ParticleComponent"].IsMap())
if (!node || !node.IsMap())
return;
const YAML::Node &comp = node["ParticleComponent"];
auto vec2 = [](const YAML::Node &n)
auto vec2 = [](const YAML::Node& n)
{
if (!n || !n.IsSequence() || n.size() != 2)
return Vec2{};
return Vec2(n[0].as<float>(), n[1].as<float>());
};
auto color = [](const YAML::Node &n)
auto color = [](const YAML::Node& n)
{
if (!n || !n.IsSequence() || n.size() != 4)
return Color{};
return Color(n[0].as<float>(), n[1].as<float>(), n[2].as<float>(), n[3].as<float>());
};
if (comp["maxParticles"]) settings.maxParticles = comp["maxParticles"].as<int>();
if (comp["emissionRate"]) settings.emissionRate = comp["emissionRate"].as<float>();
if (comp["lifeMin"]) settings.lifeMin = comp["lifeMin"].as<float>();
if (comp["lifeMax"]) settings.lifeMax = comp["lifeMax"].as<float>();
if (comp["sizeMin"]) settings.sizeMin = comp["sizeMin"].as<float>();
if (comp["sizeMax"]) settings.sizeMax = comp["sizeMax"].as<float>();
if (comp["speedMin"]) settings.speedMin = comp["speedMin"].as<float>();
if (comp["speedMax"]) settings.speedMax = comp["speedMax"].as<float>();
if (comp["direction"]) settings.direction = vec2(comp["direction"]);
if (comp["spread"]) settings.spread = comp["spread"].as<float>();
if (comp["startColor"]) settings.startColor = color(comp["startColor"]);
if (comp["endColor"]) settings.endColor = color(comp["endColor"]);
if (comp["loop"]) settings.loop = comp["loop"].as<bool>();
if (comp["burst"]) settings.burst = comp["burst"].as<bool>();
settings.maxParticles = node["maxParticles"].as<int>(settings.maxParticles);
settings.emissionRate = node["emissionRate"].as<float>(settings.emissionRate);
settings.lifeMin = node["lifeMin"].as<float>(settings.lifeMin);
settings.lifeMax = node["lifeMax"].as<float>(settings.lifeMax);
settings.sizeMin = node["sizeMin"].as<float>(settings.sizeMin);
settings.sizeMax = node["sizeMax"].as<float>(settings.sizeMax);
settings.speedMin = node["speedMin"].as<float>(settings.speedMin);
settings.speedMax = node["speedMax"].as<float>(settings.speedMax);
if (node["direction"])
settings.direction = vec2(node["direction"]);
if (node["spread"])
settings.spread = node["spread"].as<float>();
if (node["startColor"])
settings.startColor = color(node["startColor"]);
if (node["endColor"])
settings.endColor = color(node["endColor"]);
if (node["loop"])
settings.loop = node["loop"].as<bool>();
if (node["burst"])
settings.burst = node["burst"].as<bool>();
}

View File

@ -58,7 +58,6 @@ public:
void Emit();
void Update(float dt);
void Render();
private:
std::vector<Particle> particles;

View File

@ -174,94 +174,167 @@ void DrawProfilerTimelineBars(const ProfileNode &node,
origin);
}
static ProfileNode GetAveragedFrameCopy(const ProfileNode &src)
inline int CountNodes(const ProfileNode &root) noexcept
{
ProfileNode averaged = src;
averaged.visualStartMs = src.startMs;
averaged.visualDurationMs = src.durationMs;
int count = 0;
std::vector<const ProfileNode *> stack;
stack.reserve(32);
stack.push_back(&root);
averaged.children.clear();
for (const auto &child : src.children)
averaged.children.push_back(GetAveragedFrameCopy(child));
while (!stack.empty())
{
const ProfileNode *node = stack.back();
stack.pop_back();
++count;
for (const auto &child : node->children)
stack.push_back(&child);
}
return count;
}
return averaged;
inline ProfileNode GetAveragedFrameCopy(const ProfileNode &src)
{
ProfileNode avg(src.name, src.startMs);
avg.durationMs = src.durationMs;
avg.visualStartMs = avg.startMs;
avg.visualDurationMs = avg.durationMs;
const auto &kids = src.children;
avg.children.reserve(kids.size());
for (const auto &c : kids)
avg.children.push_back(GetAveragedFrameCopy(c));
return avg;
}
void ShowProfilerTimeline()
{
static ProfileNode cachedAveragedFrame;
static double lastUpdateTime = 0.0;
static double updateInterval = 0.25;
static bool freezeView = false;
PROFILE_ENGINE_SCOPE("Engine::ShowProfilerTimeline");
const double now = ImGui::GetTime();
const ProfileNode *latest = profiler.GetLatestFrame();
if (!g_engineConfig.settings.profile_enabled)
{
if (ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar))
{
ImGui::Text("Profiling Disabled.");
ImGui::End();
}
return;
}
// Statics & timing
static ProfileNode cachedFrame;
static double lastUpdate = 0.0;
static float updateInterval = 0.25f;
static bool freezeView = false;
static float zoom = 1.0f;
double now = ImGui::GetTime();
auto latest = profiler.GetLatestFrame();
if (!freezeView && latest && now - lastUpdate >= updateInterval)
{
cachedFrame = GetAveragedFrameCopy(*latest);
lastUpdate = now;
}
if (cachedFrame.durationMs <= 0.0)
{
ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar);
ImGui::Text("Waiting for profiler data...");
ImGui::End();
return;
}
ImGui::Begin("Profiler", nullptr, ImGuiWindowFlags_NoScrollbar);
// --- Controls ---
if (g_engineConfig.settings.profile_enabled)
ImGui::Checkbox("Freeze View", &freezeView);
ImGui::SameLine();
ImGui::SetNextItemWidth(120.0f);
ImGui::DragFloat("Update Interval", &updateInterval, 0.05f, 0.05f, 5.0f, "%.2fs");
updateInterval = std::clamp(updateInterval, 0.05f, 5.0f);
ImGui::SameLine();
ImGui::DragFloat("Zoom", &zoom, 0.1f, 0.1f, 10.0f, "×%.1f");
zoom = std::max(0.1f, zoom);
ImGui::SameLine();
if (ImGui::Button("Export"))
{
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);
// Export JSON button
ImGui::SameLine();
if (ImGui::Button("Export"))
json root = SerializeProfileNode(cachedFrame);
std::ofstream out("profile_export.json");
if (out)
{
json root = SerializeProfileNode(cachedAveragedFrame);
std::ofstream out("profile_export.json");
if (out.is_open())
{
out << root.dump(4);
out.close();
Logger::LogInfo("Profiler exported to profile_export.json");
}
else
{
Logger::LogError("Failed to write profiler export.");
}
out << root.dump(4);
Logger::LogInfo("Profiler exported to profile_export.json");
}
if (!freezeView && latest && (now - lastUpdateTime) >= updateInterval)
else
{
cachedAveragedFrame = GetAveragedFrameCopy(*latest);
lastUpdateTime = now;
Logger::LogError("Failed to write profiler export.");
}
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
// Layout metrics
constexpr float rowH = 24.0f;
int rows = CountNodes(cachedFrame);
float avail = ImGui::GetContentRegionAvail().x;
float width = avail * zoom;
float height = std::min(rows * rowH, 400.0f);
// Begin child & get origin
ImGui::BeginChild("TimelineScroll", ImVec2(0, height), false, ImGuiWindowFlags_HorizontalScrollbar);
ImVec2 origin = ImGui::GetCursorScreenPos();
ImDrawList *draw = ImGui::GetWindowDrawList();
ImU32 gridCol = ImGui::GetColorU32(ImGuiCol_Border);
// Cache grid lines so we only recompute when dims change
struct Line
{
ImGui::Text("Profiling Disabled.");
ImVec2 a, b;
};
static float lastW = -1.0f;
static int lastR = -1;
static std::vector<Line> gridLines;
if (lastW != width || lastR != rows)
{
gridLines.clear();
// horizontal
for (int r = 0; r <= rows; ++r)
{
float y = r * rowH;
gridLines.push_back({{0, y}, {width, y}});
}
// vertical
float dx = width / 10.0f;
for (int i = 0; i <= 10; ++i)
{
float x = i * dx;
gridLines.push_back({{x, 0}, {x, rows * rowH}});
}
lastW = width;
lastR = rows;
}
// Draw cached grid
for (auto &ln : gridLines)
{
draw->AddLine(
{origin.x + ln.a.x, origin.y + ln.a.y},
{origin.x + ln.b.x, origin.y + ln.b.y},
gridCol);
}
// Bars
DrawProfilerTimelineBars(cachedFrame, 0, width, cachedFrame.visualDurationMs, origin);
// Ensure scrollable size
ImGui::Dummy(ImVec2(width, rows * rowH));
ImGui::EndChild();
ImGui::Text("Frame: %.2f ms | Rate: %.2fs",
cachedFrame.visualDurationMs,
updateInterval);
ImGui::End();
}
@ -485,7 +558,7 @@ void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom
}
if (auto particles = obj->GetComponent<ParticleComponent>())
m_particleUpdates.push_back(particles.get()); // <-- Collect particle components
m_particleUpdates.push_back(particles.get());
if (playing)
{
@ -517,46 +590,35 @@ void Engine::Run()
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.EndEngineSection(); // End NewFrame
profiler.BeginEngineSection("Engine::ShowDebugOverlay");
ShowDebugOverlay(deltaTime);
profiler.EndEngineSection();
profiler.BeginEngineSection("Logger::Draw");
Logger::Draw();
profiler.EndEngineSection();
@ -564,7 +626,6 @@ void Engine::Run()
if (ImGui::BeginMainMenuBar())
{
// — leftaligned items —
if (ImGui::Button(playing ? "Stop" : "Play"))
{
if (!playing)
@ -603,7 +664,6 @@ void Engine::Run()
if (ImGui::BeginMenu("Options"))
{
if (ImGui::BeginMenu("Settings"))
{
ImGui::Checkbox("Enable Lighting", &g_engineConfig.settings.lighting_enabled);
@ -615,7 +675,8 @@ void Engine::Run()
if (g_engineConfig.settings.profile_enabled)
{
ImGui::Checkbox("Profile Engine", &g_engineConfig.settings.profile_editor);
ImGui::Checkbox("Deep Profileing", &g_engineConfig.settings.profile_deep);
ImGui::Checkbox("Deep Profiling", &g_engineConfig.settings.profile_deep);
// ImGui::Checkbox("GPU Profiling", &g_engineConfig.settings.profile_gpu);
}
ImGui::EndMenu();
}
@ -631,24 +692,20 @@ void Engine::Run()
ImGui::EndMenu();
}
// — rightaligned version text —
const char *version_fmt = "Create Engine v%s";
// build the text
char buf[64];
std::snprintf(buf, sizeof(buf), version_fmt, g_engineConfig.version.c_str());
// calculate width of the text
float textWidth = ImGui::CalcTextSize(buf).x;
// move cursor to far right minus padding
float avail = ImGui::GetWindowSize().x - ImGui::GetCursorPosX();
ImGuiStyle &style = ImGui::GetStyle();
ImGui::SameLine(avail - textWidth - style.FramePadding.x);
ImGui::TextUnformatted(buf);
ImGui::EndMainMenuBar();
}
profiler.EndEngineSection();
profiler.EndEngineSection(); // End BeginMainMenuBar
if (g_engineConfig.settings.show_color_correction_window)
ShowColorCorrectionWindow();
@ -690,7 +747,7 @@ void Engine::Run()
}
for (auto &obj : objects)
if (!obj->GetParent()) // Only draw root nodes
if (!obj->GetParent())
DrawObjectNode(obj);
ImGui::End();
@ -698,44 +755,32 @@ void Engine::Run()
DrawInspectorUI(selected);
// Viewport
ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImVec2 size = ImGui::GetContentRegionAvail();
// Save/restore editor camera position/zoom on play toggle
static bool lastPlaying = false;
if (playing && !lastPlaying)
{
// Entering play mode save editor camera
editorCameraSavedPos = cameraPos;
editorCameraSavedZoom = cameraZoom;
editorCameraWasSaved = true;
}
else if (!playing && lastPlaying)
else if (!playing && lastPlaying && editorCameraWasSaved)
{
// Exiting play mode restore editor camera
if (editorCameraWasSaved)
{
cameraPos = editorCameraSavedPos;
cameraZoom = editorCameraSavedZoom;
editorCameraWasSaved = false;
}
cameraPos = editorCameraSavedPos;
cameraZoom = editorCameraSavedZoom;
editorCameraWasSaved = false;
}
lastPlaying = playing;
// Use active camera when playing
if (playing && m_activeCamera)
{
cameraPos = m_activeCamera->GetOwner()->GetWorldPosition();
cameraZoom = m_activeCamera->GetZoom();
}
// Use editor camera logic when not playing
if (!playing && ImGui::IsWindowHovered())
{
// Pan
if (ImGui::IsMouseDragging(ImGuiMouseButton_Right))
{
ImVec2 delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
@ -743,7 +788,6 @@ void Engine::Run()
cameraPos -= glm::vec2(delta.x, delta.y) / cameraZoom;
}
// Zoom
float wheel = ImGui::GetIO().MouseWheel;
if (wheel != 0.0f)
{
@ -768,12 +812,10 @@ 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();
@ -786,21 +828,14 @@ void Engine::Run()
m_particleUpdates.clear();
profiler.EndEngineSection();
// profiler.BeginEngineSection("Draw Editor Grid");
// Renderer::DrawEditorGrid(cameraPos, cameraZoom);
// profiler.EndEngineSection();
// Collect Objects
profiler.BeginEngineSection("Collect Objects");
collectObjects(playing, cameraPos, cameraZoom);
profiler.EndEngineSection();
profiler.BeginEngineSection("Renderer::UpdateClusterLights");
Renderer::UpdateClusterLights();
profiler.EndEngineSection();
// Sort Objects
profiler.BeginEngineSection("Sort Objects");
if (m_toDraw.size() > 1)
{
@ -814,40 +849,52 @@ void Engine::Run()
}
profiler.EndEngineSection();
if (!g_engineConfig.settings.profile_editor)
profiler.BeginFrame();
m_OnUpdateCalls = 0;
profiler.BeginSection("Script Updates");
if (!g_engineConfig.settings.profile_editor)
{
profiler.BeginFrame();
}
profiler.BeginSection("Updates");
profiler.BeginSection("Scripts");
for (auto *script : m_scriptUpdates)
{
profiler.BeginSection("Script: " + script->GetOwner()->GetName());
profiler.BeginSection("Object: " + script->GetOwner()->GetName());
script->OnUpdate(deltaTime);
m_OnUpdateCalls++;
profiler.EndSection();
}
profiler.EndSection();
profiler.BeginSection("Particles");
for (auto *script : m_particleUpdates)
{
profiler.BeginSection("Object: " + script->GetOwner()->GetName());
script->Update(deltaTime);
profiler.EndSection();
}
profiler.EndSection();
profiler.EndSection();
profiler.BeginSection("Render");
for (auto *obj : m_toDraw)
{
// --- Sprite rendering ---
const core::types::Vec2 worldPos = obj->GetWorldPosition();
if (auto spritePtr = obj->GetComponent<SpriteComponent>())
{
profiler.BeginSection("Draw Sprite: " + obj->GetName());
Renderer::DrawSprite(spritePtr.get(),
obj->GetWorldPosition(),
cameraZoom,
cameraPos);
Renderer::DrawSprite(spritePtr.get(), worldPos, cameraZoom, cameraPos);
profiler.EndSection();
}
// --- Particle rendering ---
if (auto particle = obj->GetComponent<ParticleComponent>())
{
profiler.BeginSection("Draw Particles" + obj->GetName());
profiler.BeginSection("Draw Particles: " + obj->GetName());
const auto &particles = particle->GetParticles();
const auto &settings = particle->GetSettings();
@ -855,11 +902,10 @@ void Engine::Run()
{
float t = std::clamp(1.0f - (p.life / settings.lifeMax), 0.0f, 1.0f);
core::types::Color color = core::types::Color::Lerp(settings.startColor, settings.endColor, t);
Renderer::DrawQuad(
p.position,
core::types::Vec2(p.size, p.size),
p.rotation,
color);
core::types::Vec2 pWorld = p.position + particle->GetOwner()->GetWorldPosition();
core::types::Vec2 size = core::types::Vec2(p.size, p.size);
Renderer::BatchQuad(pWorld, size, p.rotation, color, cameraPos, cameraZoom);
}
profiler.EndSection();
@ -867,80 +913,66 @@ void Engine::Run()
}
profiler.EndSection();
// Finish frame profiling
if (!g_engineConfig.settings.profile_editor)
{
profiler.EndFrame();
}
// End renderer
Renderer::End();
// Display render target texture
profiler.BeginSection("Engine::DrawGameWindow");
GLuint texID = Renderer::GetFinalTexture();
ImGui::Image((ImTextureID)(uintptr_t)texID, size, ImVec2(0, 1), ImVec2(1, 0));
ImGui::End();
profiler.EndEngineSection();
if (ImGui::BeginPopup("RenameObject"))
{
static char nameBuffer[128];
static bool once = true;
if (once && selected)
{
strcpy(nameBuffer, selected->GetName().c_str());
once = false;
}
ImGui::InputText("##rename", nameBuffer, sizeof(nameBuffer));
if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::Button("OK"))
{
if (selected)
selected->SetName(nameBuffer);
ImGui::CloseCurrentPopup();
once = true;
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
once = true;
}
ImGui::EndPopup();
}
if (g_engineConfig.settings.profile_editor)
{
profiler.EndFrame(); // Finish frame
}
// ImGui render
ShowProfilerTimeline();
ImGui::Render();
int w, h;
glfwGetFramebufferSize(window, &w, &h);
glViewport(0, 0, w, h);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
{
PROFILE_ENGINE_SCOPE("Finish Rendering");
profiler.BeginEngineSection("ImGui::Render");
ImGui::Render();
profiler.EndEngineSection();
int w, h;
profiler.BeginEngineSection("glfwGetFramebufferSize");
glfwGetFramebufferSize(window, &w, &h);
profiler.EndEngineSection();
glViewport(0, 0, w, h);
glClear(GL_COLOR_BUFFER_BIT);
profiler.BeginEngineSection("ImGui_ImplOpenGL3_RenderDrawData");
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
profiler.EndEngineSection();
profiler.BeginEngineSection("glfwSwapBuffers");
glfwSwapBuffers(window);
profiler.EndEngineSection();
}
for (auto &obj : pendingDeletion)
{
Logger::LogVerbose("Deleting Object From Que: '%s', [%d, %s]", obj->GetName().c_str(), obj->uid.id, obj->uid.uuid.c_str());
if (obj->GetParent())
{
obj->GetParent()->RemoveChild(obj.get());
}
else
{
objects.erase(std::remove_if(objects.begin(), objects.end(),
[&](const std::shared_ptr<Object> &o)
{ return o == obj; }),
objects.end());
}
}
pendingDeletion.clear();
if (g_engineConfig.settings.profile_editor)
{
profiler.EndFrame();
}
}
}
@ -960,11 +992,6 @@ void Engine::DrawObjectNode(const std::shared_ptr<Object> &obj)
// === Context Menu ===
if (ImGui::BeginPopupContextItem())
{
if (ImGui::MenuItem("Rename"))
{
selected = obj;
ImGui::OpenPopup("RenameObject");
}
if (ImGui::MenuItem("Delete"))
{
pendingDeletion.push_back(obj);
@ -1160,6 +1187,7 @@ void Engine::LoadScene(const std::string &path)
Logger::LogInfo("[LoadScene] Loaded scene: %s", root["scene_name"].as<std::string>().c_str());
}
void Engine::SaveState()
{
YAML::Emitter out;
@ -1214,6 +1242,7 @@ void Engine::LoadState()
{
auto obj = std::make_shared<Object>("[DefaultObject]");
obj->Load(node);
objects.push_back(obj);
}

View File

@ -1,3 +1,5 @@
#include "Renderer.h"
#include "Components/SpriteComponent.h"
#include "utils/Shader.h"
@ -46,6 +48,9 @@ GLuint Renderer::s_QuadInstanceVBO = 0;
Shader Renderer::s_UnlitQuadShader;
std::vector<QuadInstance> Renderer::s_QuadBatch;
void *Renderer::s_QuadMappedPtr = nullptr;
GLuint Renderer::s_QuadPersistentFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
std::vector<Light> Renderer::s_Lights;
std::vector<Renderer::Cluster> Renderer::s_Clusters;
@ -69,11 +74,11 @@ struct BatchedSprite
glm::vec2 screenPos;
glm::vec2 size;
float rotationRad;
glm::vec4 texCoords; // optional, if you support atlases
glm::vec4 texCoords;
GLuint textureID;
GLuint normalMapID;
SpriteComponent::RenderType renderType;
SpriteComponent *sprite; // optional: to access lighting per sprite if needed
SpriteComponent *sprite;
};
struct SortedDrawEntry
@ -144,7 +149,16 @@ void Renderer::InitQuadBatch()
glGenBuffers(1, &s_QuadInstanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, s_QuadInstanceVBO);
glBufferData(GL_ARRAY_BUFFER, MAX_QUADS * sizeof(QuadInstance), nullptr, GL_DYNAMIC_DRAW);
glGenBuffers(1, &s_QuadInstanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, s_QuadInstanceVBO);
glBufferStorage(GL_ARRAY_BUFFER, MAX_QUADS * sizeof(QuadInstance), nullptr, s_QuadPersistentFlags);
// Map once for persistent write access
s_QuadMappedPtr = glMapBufferRange(GL_ARRAY_BUFFER, 0, MAX_QUADS * sizeof(QuadInstance), s_QuadPersistentFlags);
if (!s_QuadMappedPtr)
{
Logger::LogError("Persistent VBO mapping failed!");
}
std::size_t offset = 0;
@ -273,6 +287,9 @@ void Renderer::UpdateClusterLights()
{
PROFILE_ENGINE_SCOPE("Renderer::UpdateClusterLights");
if (!g_engineConfig.settings.lighting_enabled)
return;
s_ClusterCols = (width + CLUSTER_SIZE - 1) / CLUSTER_SIZE;
s_ClusterRows = (height + CLUSTER_SIZE - 1) / CLUSTER_SIZE;
const int totalClusters = s_ClusterCols * s_ClusterRows;
@ -378,6 +395,7 @@ void Renderer::Resize(int w, int h)
width = w;
height = h;
// Resize all textures
glBindTexture(GL_TEXTURE_2D, textureColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
@ -508,7 +526,16 @@ void Renderer::DrawQuad(const core::types::Vec2 &pos, const core::types::Vec2 &s
s_QuadBatch.push_back({pos, size, rotation, color});
}
void Renderer::BatchQuad(const core::types::Vec2 &worldPos, const core::types::Vec2 &size, float rotation, const core::types::Color &color, const core::types::Vec2 &cameraPos, float zoom)
{
if (s_QuadBatch.size() >= MAX_QUADS)
FlushQuads();
core::types::Vec2 screenPos = (worldPos - cameraPos) * zoom + core::types::Vec2(width * 0.5f, height * 0.5f);
core::types::Vec2 finalSize = size * zoom;
s_QuadBatch.push_back({screenPos, finalSize, rotation, color});
}
void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float zoom, glm::vec2 &CameraPos)
{
@ -540,8 +567,6 @@ void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float z
sortedDrawList.push_back(drawEntry);
}
void Renderer::FlushQuads()
{
PROFILE_ENGINE_SCOPE("Renderer::FlushQuads");
@ -557,7 +582,16 @@ void Renderer::FlushQuads()
glBindVertexArray(s_QuadVAO);
glBindBuffer(GL_ARRAY_BUFFER, s_QuadInstanceVBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, s_QuadBatch.size() * sizeof(QuadInstance), s_QuadBatch.data());
{
PROFILE_DEEP_SCOPE("Upload");
s_UnlitQuadShader.Use();
s_UnlitQuadShader.SetVec2("uScreen", glm::vec2(width, height));
glBindVertexArray(s_QuadVAO);
// Write directly to mapped buffer
std::memcpy(s_QuadMappedPtr, s_QuadBatch.data(), s_QuadBatch.size() * sizeof(QuadInstance));
}
}
{
@ -768,7 +802,6 @@ GLuint Renderer::GetFinalTexture()
if (!s_ColorCorrection || !s_ColorCorrection->bloom)
return sceneTex;
// 1. Extract bright regions
glBindFramebuffer(GL_FRAMEBUFFER, extractFBO);
glViewport(0, 0, width, height);
glClearColor(0, 0, 0, 1);
@ -828,7 +861,7 @@ GLuint Renderer::GetRenderTexture()
return textureColorBuffer;
}
glm::ivec2 Renderer::GetSize()
core::types::Vec2 Renderer::GetSize()
{
return {width, height};
}

View File

@ -11,7 +11,8 @@
#include "core/utils/Profiler.h"
#include "core/types/all.h"
struct ColorCorrection {
struct ColorCorrection
{
float brightness = 1.0f;
float saturation = 1.0f;
float gamma = 1.0f;
@ -20,60 +21,65 @@ struct ColorCorrection {
float threshold = 0.2f;
float intensity = 1.2f;
void Upload(Shader& shader) const {
void Upload(Shader &shader) const
{
shader.SetFloat("uBrightness", brightness);
shader.SetFloat("uSaturation", saturation);
shader.SetFloat("uGamma", gamma);
}
};
struct Light {
struct Light
{
glm::vec2 screenPos;
glm::vec3 color;
float intensity;
float radius;
};
struct QuadInstance {
struct QuadInstance
{
core::types::Vec2 pos;
core::types::Vec2 size;
float rotation;
core::types::Color color;
};
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 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 void DrawEditorGrid(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 core::types::Vec2 GetSize();
static int GetDrawCallCount();
static int GetLightsCount();
static void SetColorCorrection(std::unique_ptr<ColorCorrection> correction);
static ColorCorrection* GetColorCorrection();
static ColorCorrection *GetColorCorrection();
static GLuint GetFinalTexture();
static void UpdateClusterLights();
static void FlushSprites();
static void InitQuadBatch();
static void DrawQuad(const core::types::Vec2& pos, const core::types::Vec2& size, float rotation, const core::types::Color& color);
static void DrawQuad(const core::types::Vec2 &pos, const core::types::Vec2 &size, float rotation, const core::types::Color &color);
static void BatchQuad(const core::types::Vec2 &worldPos, const core::types::Vec2 &size, float rotation, const core::types::Color &color, const core::types::Vec2 &cameraPos, float zoom);
static void FlushQuads();
private:
@ -87,14 +93,15 @@ 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;
// Clustered Lighting
static constexpr int CLUSTER_SIZE = 16;
static constexpr int MAX_LIGHTS_PER_CLUSTER = 32;
struct Cluster {
struct Cluster
{
std::vector<int> lightIndices;
};
@ -110,4 +117,7 @@ private:
static GLuint s_QuadVAO, s_QuadVBO, s_QuadInstanceVBO;
static Shader s_UnlitQuadShader;
static constexpr size_t MAX_QUADS = 10000;
static void *s_QuadMappedPtr;
static GLuint s_QuadPersistentFlags;
};

View File

@ -1,5 +1,7 @@
// core/types/vec2.h
#pragma once
#include <cmath>
#include <algorithm>
#include <ostream>
#include <glm/glm.hpp>
namespace core
@ -9,39 +11,110 @@ namespace core
struct Vec2
{
float x = 0.0f, y = 0.0f;
float x{0}, y{0};
// Constructors
Vec2() = default;
Vec2(float _x, float _y) : x(_x), y(_y) {}
Vec2(float x, float y) : x(x), y(y) {}
Vec2(float v) : x(v), y(v) {}
Vec2(const glm::vec2 &v) : x(v.x), y(v.y) {}
Vec2 operator*(float s) const { return {x * s, y * s}; }
operator glm::vec2() const { return glm::vec2(x, y); }
// Conversion to glm
operator glm::vec2() const { return {x, y}; }
// Arithmetic operators
Vec2 operator+(const Vec2 &rhs) const { return {x + rhs.x, y + rhs.y}; }
Vec2 operator-(const Vec2 &rhs) const { return {x - rhs.x, y - rhs.y}; }
Vec2 operator*(float scalar) const { return {x * scalar, y * scalar}; }
Vec2 operator/(float scalar) const { return {x / scalar, y / scalar}; }
Vec2 &operator+=(const Vec2 &rhs)
{
x += rhs.x;
y += rhs.y;
return *this;
}
Vec2 &operator-=(const Vec2 &rhs)
{
x -= rhs.x;
y -= rhs.y;
return *this;
}
Vec2 &operator*=(float scalar)
{
x *= scalar;
y *= scalar;
return *this;
}
friend Vec2 operator*(float scalar, const Vec2 &v)
Vec2 &operator/=(float scalar)
{
return v * scalar;
}
Vec2 operator+(const Vec2 &other) const
{
return {x + other.x, y + other.y};
}
Vec2 &operator+=(const Vec2 &other)
{
x += other.x;
y += other.y;
x /= scalar;
y /= scalar;
return *this;
}
// Unary operators
Vec2 operator-() const { return {-x, -y}; }
// Comparison
bool operator==(const Vec2 &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const Vec2 &rhs) const { return !(*this == rhs); }
// Length
float Length() const { return std::sqrt(x * x + y * y); }
float LengthSquared() const { return x * x + y * y; }
// Normalization
Vec2 Normalized() const
{
float len = Length();
return len != 0 ? *this / len : Vec2(0.0f);
}
void Normalize()
{
float len = Length();
if (len != 0)
{
x /= len;
y /= len;
}
}
// Dot product
float Dot(const Vec2 &rhs) const { return x * rhs.x + y * rhs.y; }
// Clamp
void Clamp(const Vec2 &min, const Vec2 &max)
{
x = std::max(min.x, std::min(x, max.x));
y = std::max(min.y, std::min(y, max.y));
}
// Lerp
static Vec2 Lerp(const Vec2 &a, const Vec2 &b, float t)
{
return a + (b - a) * t;
}
// Distance
static float Distance(const Vec2 &a, const Vec2 &b)
{
return (a - b).Length();
}
// Serialization helpers (optional)
friend std::ostream &operator<<(std::ostream &os, const Vec2 &v)
{
return os << "(" << v.x << ", " << v.y << ")";
}
};
// Scalar * Vec2 support
inline Vec2 operator*(float scalar, const Vec2 &v)
{
return v * scalar;
}
} // namespace types
} // namespace core

View File

@ -18,6 +18,7 @@ EngineConfig g_engineConfig{
.show_color_correction_window = false,
.lighting_enabled = true,
.profile_deep = false,
.profile_gpu = false,
}};
static std::filesystem::path GetUserSettingsPath()
@ -47,6 +48,8 @@ void EngineConfig::SaveToFile()
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::Key << "profile_deep" << YAML::Value << settings.profile_deep;
out << YAML::Key << "profile_gpu" << YAML::Value << settings.profile_gpu;
out << YAML::EndMap;
@ -77,4 +80,6 @@ void EngineConfig::LoadFromFile()
settings.lighting_enabled = root["lighting_enabled"].as<bool>();
if (root["profile_deep"])
settings.profile_deep = root["profile_deep"].as<bool>();
if (root["profile_gpu"])
settings.profile_gpu = root["profile_gpu"].as<bool>();
}

View File

@ -8,6 +8,7 @@ struct UserSettings {
bool show_color_correction_window;
bool lighting_enabled;
bool profile_deep;
bool profile_gpu;
};
struct EngineConfig {