Added Particles ish, untested

This commit is contained in:
OusmBlueNinja 2025-04-20 20:25:13 -05:00
parent a5fd89168b
commit 549dac776a
15 changed files with 654 additions and 75 deletions

View File

@ -10,40 +10,40 @@ Collapsed=1
[Window][WindowOverViewport_11111111]
Pos=0,19
Size=1280,701
Size=1920,1158
Collapsed=0
[Window][Inspector]
Pos=0,421
Size=342,299
Pos=0,683
Size=342,494
Collapsed=0
DockId=0x0000000A,0
[Window][Scene Tree]
Pos=0,19
Size=342,400
Size=342,662
Collapsed=0
DockId=0x00000009,0
[Window][Viewport]
Pos=344,19
Size=936,78
Size=1576,535
Collapsed=0
DockId=0x0000000B,0
[Window][##MainMenuBar]
Size=1280,19
Size=1920,19
Collapsed=0
[Window][Performance Info]
Pos=1083,383
Size=197,118
Pos=1588,840
Size=332,118
Collapsed=0
DockId=0x00000003,0
[Window][Console]
Pos=344,383
Size=363,337
Pos=344,840
Size=612,337
Collapsed=0
DockId=0x0000000D,0
@ -54,8 +54,8 @@ Collapsed=0
DockId=0x0000000B,1
[Window][Profiler]
Pos=344,99
Size=936,282
Pos=344,556
Size=1576,282
Collapsed=0
DockId=0x0000000C,0
@ -78,19 +78,19 @@ Collapsed=0
DockId=0x00000005,1
[Window][Color Correction]
Pos=1083,503
Size=197,217
Pos=1588,960
Size=332,217
Collapsed=0
DockId=0x00000004,0
[Window][Asset Browser]
Pos=709,383
Size=372,337
Pos=958,840
Size=628,337
Collapsed=0
DockId=0x0000000E,0
[Docking][Data]
DockSpace ID=0x11111111 Window=0x1BBC0F80 Pos=0,19 Size=1280,701 Split=X
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=0x0000000A Parent=0x00000001 SizeRef=385,494 HiddenTabBar=1 Selected=0x36DC96AB

View File

@ -1,3 +1,2 @@
[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\core\utils\Profiler.cpp -o src\build\core\utils\Profiler.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\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.
[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
[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

View File

@ -0,0 +1,12 @@
#version 330 core
in vec4 vColor;
out vec4 FragColor;
void main() {
// Optional: kill particles with near-zero alpha (optional)
if (vColor.a < 0.01)
discard;
FragColor = vColor;
}

View File

@ -0,0 +1,28 @@
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 iPos;
layout (location = 2) in vec2 iSize;
layout (location = 3) in float iRot;
layout (location = 4) in vec4 iColor;
uniform vec2 uScreen;
out vec4 vColor;
void main() {
float cosR = cos(iRot);
float sinR = sin(iRot);
vec2 scaled = aPos * iSize;
vec2 rotated = vec2(
scaled.x * cosR - scaled.y * sinR,
scaled.x * sinR + scaled.y * cosR
);
vec2 finalPos = iPos + rotated;
gl_Position = vec4((finalPos / uScreen * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);
vColor = iColor;
}

View File

@ -0,0 +1,162 @@
#include "ParticleComponent.h"
#include "../Renderer.h" // Adjust if needed
#include <random>
#include <algorithm>
using namespace core::types;
static std::mt19937 rng{std::random_device{}()};
static float randRange(float a, float b)
{
return std::uniform_real_distribution<float>(a, b)(rng);
}
void ParticleComponent::SetEmitterSettings(const ParticleEmitterSettings &s)
{
settings = s;
}
void ParticleComponent::Emit()
{
for (int i = 0; i < settings.maxParticles; ++i)
SpawnParticle();
}
void ParticleComponent::Update(float dt)
{
emitAccumulator += dt;
float emitInterval = 1.0f / settings.emissionRate;
if (!settings.burst)
{
while (emitAccumulator >= emitInterval)
{
if ((int)particles.size() < settings.maxParticles)
SpawnParticle();
emitAccumulator -= emitInterval;
}
}
for (auto &p : particles)
{
p.life -= dt;
if (p.life > 0)
{
p.position += p.velocity * dt;
p.rotation += dt * 2.0f;
}
}
particles.erase(
std::remove_if(particles.begin(), particles.end(),
[](const Particle &p)
{ return p.life <= 0.0f; }),
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()
{
Particle p;
p.life = randRange(settings.lifeMin, settings.lifeMax);
p.size = randRange(settings.sizeMin, settings.sizeMax);
float angle = std::atan2(settings.direction.y, settings.direction.x) +
randRange(-settings.spread * 0.5f, settings.spread * 0.5f);
float speed = randRange(settings.speedMin, settings.speedMax);
p.velocity = Vec2(std::cos(angle), std::sin(angle)) * speed;
p.position = Vec2(0.0f, 0.0f);
p.rotation = randRange(0.0f, 6.28318f); // 2π
p.color = settings.startColor;
particles.push_back(p);
}
void ParticleComponent::Save(YAML::Emitter &out) const
{
out << YAML::Key << "ParticleComponent" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "maxParticles" << YAML::Value << settings.maxParticles;
out << YAML::Key << "emissionRate" << YAML::Value << settings.emissionRate;
out << YAML::Key << "lifeMin" << YAML::Value << settings.lifeMin;
out << YAML::Key << "lifeMax" << YAML::Value << settings.lifeMax;
out << YAML::Key << "sizeMin" << YAML::Value << settings.sizeMin;
out << YAML::Key << "sizeMax" << YAML::Value << settings.sizeMax;
out << YAML::Key << "speedMin" << YAML::Value << settings.speedMin;
out << YAML::Key << "speedMax" << YAML::Value << settings.speedMax;
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)
{
out << YAML::Flow << YAML::BeginSeq << c.r << c.g << c.b << c.a << YAML::EndSeq;
};
out << YAML::Key << "startColor" << YAML::Value;
writeColor(settings.startColor, out);
out << YAML::Key << "endColor" << YAML::Value;
writeColor(settings.endColor, out);
out << YAML::Key << "loop" << YAML::Value << settings.loop;
out << YAML::Key << "burst" << YAML::Value << settings.burst;
out << YAML::EndMap;
}
void ParticleComponent::Load(const YAML::Node &node)
{
if (!node["ParticleComponent"] || !node["ParticleComponent"].IsMap())
return;
const YAML::Node &comp = node["ParticleComponent"];
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)
{
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>();
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "Component.h"
#include "../Entitys/Object.h"
#include "../core/types/vec2.h"
#include "../core/types/vec4.h"
#include "../core/types/color.h"
#include <glm/glm.hpp>
#include <vector>
#include <string>
#include <yaml-cpp/yaml.h>
struct Particle
{
core::types::Vec2 position;
core::types::Vec2 velocity;
float life;
float size;
float rotation;
core::types::Color color;
};
struct ParticleEmitterSettings
{
int maxParticles = 1000;
float emissionRate = 100.0f; // particle/s
float lifeMin = 0.5f;
float lifeMax = 1.5f;
float sizeMin = 5.0f;
float sizeMax = 10.0f;
float speedMin = 100.0f;
float speedMax = 300.0f;
core::types::Vec2 direction = {1.0f, 0.0f};
float spread = 1.57f; // radians
core::types::Color startColor = {1, 1, 1, 1};
core::types::Color endColor = {1, 1, 1, 0};
bool loop = true;
bool burst = false;
};
class ParticleComponent : public Component
{
public:
ParticleComponent(Object *owner) : Component(owner) {}
std::string GetName() const override { return "ParticleComponent"; }
void Save(YAML::Emitter &out) const override;
void Load(const YAML::Node &node) override;
void SetEmitterSettings(const ParticleEmitterSettings &settings);
const ParticleEmitterSettings &GetEmitterSettings() const { return settings; }
const std::vector<Particle> &GetParticles() const { return particles; }
const ParticleEmitterSettings &GetSettings() const { return settings; }
void Emit();
void Update(float dt);
void Render();
private:
std::vector<Particle> particles;
ParticleEmitterSettings settings;
float emitAccumulator = 0.0f;
void SpawnParticle();
};

View File

@ -7,6 +7,7 @@
#include "components/TilemapComponent.h"
#include "components/ScriptComponent.h"
#include "components/PhysicsComponent.h"
#include "components/ParticleComponent.h"
#include "core/utils/FileDialog.h"
#include "core/utils/Logging.h"
@ -437,6 +438,7 @@ void Engine::Init()
m_scriptUpdates.reserve(256);
m_collectStack.reserve(1024);
m_physicsUpdates.reserve(1024);
m_particleUpdates.reserve(1024);
Logger::LogInfo("Initialized Engine");
}
@ -448,6 +450,7 @@ void Engine::collectObjects(bool playing, const glm::vec2 &camPos, float camZoom
m_collectStack.clear();
m_activeCamera = nullptr;
m_physicsUpdates.clear();
m_particleUpdates.clear(); // <-- Add this
const glm::vec2 screenSize = glm::vec2(Renderer::GetSize());
@ -481,6 +484,9 @@ 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
if (playing)
{
if (auto script = obj->GetComponent<ScriptComponent>())
@ -777,6 +783,7 @@ void Engine::Run()
m_Reserved_draws = 0;
m_toDraw.clear();
m_scriptUpdates.clear();
m_particleUpdates.clear();
profiler.EndEngineSection();
// profiler.BeginEngineSection("Draw Editor Grid");
@ -822,9 +829,11 @@ void Engine::Run()
}
profiler.EndSection();
profiler.BeginSection("Render");
for (auto *obj : m_toDraw)
{
// --- Sprite rendering ---
if (auto spritePtr = obj->GetComponent<SpriteComponent>())
{
profiler.BeginSection("Draw Sprite: " + obj->GetName());
@ -834,6 +843,27 @@ void Engine::Run()
cameraPos);
profiler.EndSection();
}
// --- Particle rendering ---
if (auto particle = obj->GetComponent<ParticleComponent>())
{
profiler.BeginSection("Draw Particles" + obj->GetName());
const auto &particles = particle->GetParticles();
const auto &settings = particle->GetSettings();
for (const auto &p : particles)
{
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);
}
profiler.EndSection();
}
}
profiler.EndSection();

View File

@ -9,6 +9,7 @@
class Object;
class ScriptComponent;
class PhysicsComponent;
class ParticleComponent;
class Engine
{
@ -39,5 +40,6 @@ private:
std::vector<std::shared_ptr<Object>> m_collectStack;
std::vector<PhysicsComponent*> m_physicsUpdates;
std::vector<ParticleComponent*> m_particleUpdates;
};

View File

@ -6,6 +6,8 @@
#include "../Components/TilemapComponent.h"
#include "../Components/TextComponent.h"
#include "../Components/ScriptComponent.h"
#include "../Components/ParticleComponent.h"
#include "../core/utils/Logging.h"
#include "../utils/UID.h"
@ -153,6 +155,8 @@ void Object::Load(const YAML::Node &node)
else if (type == "TilemapComponent") AddComponent<TilemapComponent>()->Load(compNode);
else if (type == "TextComponent") AddComponent<TextComponent>()->Load(compNode);
else if (type == "ScriptComponent") AddComponent<ScriptComponent>()->Load(compNode);
else if (type == "ParticleComponent") AddComponent<ParticleComponent>()->Load(compNode);
}
}

View File

@ -40,6 +40,12 @@ int Renderer::s_DrawCalls = 0;
int Renderer::s_LightsCount = 0;
std::unique_ptr<ColorCorrection> Renderer::s_ColorCorrection = nullptr;
GLuint Renderer::s_QuadVAO = 0;
GLuint Renderer::s_QuadVBO = 0;
GLuint Renderer::s_QuadInstanceVBO = 0;
Shader Renderer::s_UnlitQuadShader;
std::vector<QuadInstance> Renderer::s_QuadBatch;
std::vector<Light> Renderer::s_Lights;
std::vector<Renderer::Cluster> Renderer::s_Clusters;
@ -118,6 +124,52 @@ void Renderer::InitQuad()
glBindVertexArray(0);
}
void Renderer::InitQuadBatch()
{
float quadVerts[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, 0.5f};
glGenVertexArrays(1, &s_QuadVAO);
glBindVertexArray(s_QuadVAO);
glGenBuffers(1, &s_QuadVBO);
glBindBuffer(GL_ARRAY_BUFFER, s_QuadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVerts), quadVerts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); // aPos
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
glGenBuffers(1, &s_QuadInstanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, s_QuadInstanceVBO);
glBufferData(GL_ARRAY_BUFFER, MAX_QUADS * sizeof(QuadInstance), nullptr, GL_DYNAMIC_DRAW);
std::size_t offset = 0;
glEnableVertexAttribArray(1); // pos
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(QuadInstance), (void *)(offset));
glVertexAttribDivisor(1, 1);
offset += sizeof(glm::vec2);
glEnableVertexAttribArray(2); // size
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(QuadInstance), (void *)(offset));
glVertexAttribDivisor(2, 1);
offset += sizeof(glm::vec2);
glEnableVertexAttribArray(3); // rotation
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(QuadInstance), (void *)(offset));
glVertexAttribDivisor(3, 1);
offset += sizeof(float);
glEnableVertexAttribArray(4); // color
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(QuadInstance), (void *)(offset));
glVertexAttribDivisor(4, 1);
glBindVertexArray(0);
}
void Renderer::Init()
{
glGenFramebuffers(1, &fbo);
@ -154,6 +206,9 @@ void Renderer::Init()
blurShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/blur.frag");
compositeShader.LoadFromFile("src/assets/shaders/fullscreen.vert", "src/assets/shaders/composite.frag");
InitQuadBatch();
s_UnlitQuadShader.LoadFromFile("src/assets/shaders/unlit_quad.vert", "src/assets/shaders/unlit_quad.frag");
SetColorCorrection(std::make_unique<ColorCorrection>());
{
@ -366,6 +421,7 @@ void Renderer::End()
PROFILE_ENGINE_SCOPE("Renderer::End");
FlushSprites();
FlushQuads();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
@ -443,6 +499,17 @@ void Renderer::DrawTilemap(TilemapComponent *tilemap, const glm::vec2 &worldPos,
glBindVertexArray(0);
}
void Renderer::DrawQuad(const core::types::Vec2 &pos, const core::types::Vec2 &size, float rotation, const core::types::Color &color)
{
PROFILE_DEEP_SCOPE("Renderer::DrawQuad");
if (s_QuadBatch.size() >= MAX_QUADS)
FlushQuads();
s_QuadBatch.push_back({pos, size, rotation, color});
}
void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float zoom, glm::vec2 &CameraPos)
{
PROFILE_DEEP_SCOPE("DrawSprite");
@ -473,6 +540,37 @@ void Renderer::DrawSprite(SpriteComponent *sprite, const glm::vec2 &pos, float z
sortedDrawList.push_back(drawEntry);
}
void Renderer::FlushQuads()
{
PROFILE_ENGINE_SCOPE("Renderer::FlushQuads");
if (s_QuadBatch.empty())
return;
{
PROFILE_DEEP_SCOPE("Upload");
s_UnlitQuadShader.Use();
s_UnlitQuadShader.SetVec2("uScreen", glm::vec2(width, height));
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("DrawQuads");
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, static_cast<GLsizei>(s_QuadBatch.size()));
++s_DrawCalls;
}
glBindVertexArray(0);
s_QuadBatch.clear();
}
void Renderer::FlushSprites()
{
PROFILE_ENGINE_SCOPE("Renderer::FlushSprites");

View File

@ -9,9 +9,9 @@
#include "core/utils/EngineConfig.h"
#include "utils/Shader.h"
#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,41 +20,45 @@ 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;
};
class Renderer
{
struct QuadInstance {
core::types::Vec2 pos;
core::types::Vec2 size;
float rotation;
core::types::Color color;
};
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();
@ -62,14 +66,15 @@ public:
static int GetLightsCount();
static void SetColorCorrection(std::unique_ptr<ColorCorrection> correction);
static ColorCorrection *GetColorCorrection();
static ColorCorrection* GetColorCorrection();
static GLuint GetFinalTexture();
// Clustered lighting
static void UpdateClusterLights(); // Call once per frame after all lights added
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 FlushQuads();
private:
static std::vector<Light> s_Lights;
@ -82,16 +87,14 @@ 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 ---
// Clustered Lighting
static constexpr int CLUSTER_SIZE = 16;
static constexpr int MAX_LIGHTS_PER_CLUSTER = 32;
struct Cluster
{
struct Cluster {
std::vector<int> lightIndices;
};
@ -102,4 +105,9 @@ private:
static GLuint bloomFBO;
static GLuint bloomTexture;
static std::vector<QuadInstance> s_QuadBatch;
static GLuint s_QuadVAO, s_QuadVBO, s_QuadInstanceVBO;
static Shader s_UnlitQuadShader;
static constexpr size_t MAX_QUADS = 10000;
};

8
src/src/core/types/all.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "color.h"
#include "rect.h"
#include "vec2.h"
#include "vec3.h"
#include "vec4.h"
#include "vector.h"

View File

@ -1,17 +1,130 @@
#pragma once
#include <algorithm>
namespace core {
namespace types {
struct Color
namespace core
{
float r{1}, g{1}, b{1}, a{1};
namespace types
{
Color() = default;
Color(float _r, float _g, float _b, float _a = 1.0f)
: r(_r), g(_g), b(_b), a(_a)
{}
};
struct Color
{
float r{1}, g{1}, b{1}, a{1};
}
}
Color() = default;
Color(float _r, float _g, float _b, float _a = 1.0f)
: r(_r), g(_g), b(_b), a(_a) {}
// Add
Color operator+(const Color &other) const
{
return {r + other.r, g + other.g, b + other.b, a + other.a};
}
Color &operator+=(const Color &other)
{
r += other.r;
g += other.g;
b += other.b;
a += other.a;
return *this;
}
// Subtract
Color operator-(const Color &other) const
{
return {r - other.r, g - other.g, b - other.b, a - other.a};
}
Color &operator-=(const Color &other)
{
r -= other.r;
g -= other.g;
b -= other.b;
a -= other.a;
return *this;
}
// Multiply
Color operator*(const Color &other) const
{
return {r * other.r, g * other.g, b * other.b, a * other.a};
}
Color operator*(float scalar) const
{
return {r * scalar, g * scalar, b * scalar, a * scalar};
}
Color &operator*=(const Color &other)
{
r *= other.r;
g *= other.g;
b *= other.b;
a *= other.a;
return *this;
}
Color &operator*=(float scalar)
{
r *= scalar;
g *= scalar;
b *= scalar;
a *= scalar;
return *this;
}
// Divide
Color operator/(float scalar) const
{
float inv = 1.0f / scalar;
return {r * inv, g * inv, b * inv, a * inv};
}
Color &operator/=(float scalar)
{
float inv = 1.0f / scalar;
r *= inv;
g *= inv;
b *= inv;
a *= inv;
return *this;
}
// Equality
bool operator==(const Color &other) const
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
bool operator!=(const Color &other) const
{
return !(*this == other);
}
// Linear interpolation
static Color Lerp(const Color &a, const Color &b, float t)
{
return {
a.r + (b.r - a.r) * t,
a.g + (b.g - a.g) * t,
a.b + (b.b - a.b) * t,
a.a + (b.a - a.a) * t};
}
// Clamp all components between 0 and 1
void Clamp()
{
r = std::clamp(r, 0.0f, 1.0f);
g = std::clamp(g, 0.0f, 1.0f);
b = std::clamp(b, 0.0f, 1.0f);
a = std::clamp(a, 0.0f, 1.0f);
}
};
inline Color operator*(float scalar, const Color &c)
{
return c * scalar;
}
} // namespace types
} // namespace core

View File

@ -1,5 +1,6 @@
// core/types/vec2.h
#pragma once
#include <glm/glm.hpp>
namespace core
{
@ -8,27 +9,39 @@ namespace core
struct Vec2
{
float x{0}, y{0};
float x = 0.0f, y = 0.0f;
Vec2() = default;
Vec2(float _x, float _y) : x(_x), y(_y) {}
Vec2 &operator+=(const Vec2 &o)
Vec2 operator*(float s) const { return {x * s, y * s}; }
operator glm::vec2() const { return glm::vec2(x, y); }
Vec2 &operator*=(float scalar)
{
x += o.x;
y += o.y;
return *this;
}
Vec2 &operator-=(const Vec2 &o)
{
x -= o.x;
y -= o.y;
x *= scalar;
y *= scalar;
return *this;
}
friend Vec2 operator+(Vec2 a, const Vec2 &b) { return a += b; }
friend Vec2 operator-(Vec2 a, const Vec2 &b) { return a -= b; }
friend Vec2 operator*(float scalar, const Vec2 &v)
{
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;
return *this;
}
};
}
}
} // namespace types
} // namespace core

View File

@ -7,6 +7,7 @@
#include "../../components/TilemapComponent.h"
#include "../../components/ScriptComponent.h"
#include "../../components/PhysicsComponent.h"
#include "../../components/ParticleComponent.h"
void DrawInspectorUI(std::shared_ptr<Object> selected)
{
@ -70,7 +71,8 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
"CameraComponent",
"LightComponent",
"ScriptComponent",
"TilemapComponent"};
"TilemapComponent",
"ParticleComponent"};
static int selectedIndex = -1;
@ -102,6 +104,8 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
selected->AddComponent<ScriptComponent>();
else if (type == "TilemapComponent" && !selected->GetComponent<TilemapComponent>())
selected->AddComponent<TilemapComponent>();
else if (type == "ParticleComponent" && !selected->GetComponent<ParticleComponent>())
selected->AddComponent<ParticleComponent>();
}
if (auto sprite = selected->GetComponent<SpriteComponent>())
@ -139,7 +143,6 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (!path.empty())
sprite->SetTexture(path);
}
}
ImGui::SeparatorText("Normal Map");
{
@ -161,12 +164,10 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (!path.empty())
sprite->SetNormalMap(path);
}
}
ImGui::SeparatorText("Info");
// — Size Display & Remove —
glm::vec2 size = sprite->GetSize();
ImGui::Text("Size: %.0f × %.0f", size.x, size.y);
@ -250,6 +251,38 @@ void DrawInspectorUI(std::shared_ptr<Object> selected)
if (ImGui::Button("Remove ScriptComponent"))
selected->RemoveComponent<ScriptComponent>();
}
if (auto part = selected->GetComponent<ParticleComponent>())
{
ImGui::SeparatorText("Particle Component");
auto settings = part->GetEmitterSettings();
ImGui::DragInt("Max Particles", &settings.maxParticles, 10, 10, 10000);
ImGui::DragFloat("Emission Rate", &settings.emissionRate, 1.0f, 0.0f, 1000.0f);
ImGui::DragFloat("Life Min", &settings.lifeMin, 0.01f, 0.01f, 10.0f);
ImGui::DragFloat("Life Max", &settings.lifeMax, 0.01f, 0.01f, 10.0f);
ImGui::DragFloat("Size Min", &settings.sizeMin, 0.1f, 0.1f, 100.0f);
ImGui::DragFloat("Size Max", &settings.sizeMax, 0.1f, 0.1f, 100.0f);
ImGui::DragFloat("Speed Min", &settings.speedMin, 1.0f, 0.0f, 1000.0f);
ImGui::DragFloat("Speed Max", &settings.speedMax, 1.0f, 0.0f, 1000.0f);
ImGui::DragFloat2("Direction", &settings.direction.x, 0.01f);
ImGui::DragFloat("Spread", &settings.spread, 0.01f, 0.0f, 3.14f);
ImGui::ColorEdit4("Start Color", &settings.startColor.r);
ImGui::ColorEdit4("End Color", &settings.endColor.r);
ImGui::Checkbox("Loop", &settings.loop);
ImGui::Checkbox("Burst", &settings.burst);
part->SetEmitterSettings(settings);
if (ImGui::Button("Emit Once"))
part->Emit();
if (ImGui::Button("Remove ParticleComponent"))
selected->RemoveComponent<ParticleComponent>();
}
if (auto phys = selected->GetComponent<PhysicsComponent>())
{
ImGui::SeparatorText("Physics Component");