From 7ea00e2ed4f40eccb7833af84c0ee928e1c839e1 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Sun, 24 May 2026 23:14:29 +0200 Subject: [PATCH] Lights & vehicle lights --- src/assets/model.cpp | 4 ++ src/game/drivable_vehicle.cpp | 18 +++-- src/game/vehicle.cpp | 35 ++++++++-- src/game/vehicle.hpp | 6 ++ src/game/vehicle_sync.hpp | 11 +-- src/gameview/vehicleview.cpp | 86 ++++++++++++++++++++++- src/gameview/vehicleview.hpp | 32 ++++++++- src/gfx/draw_list.hpp | 38 +++++++--- src/gfx/light_cache.hpp | 26 +++++++ src/gfx/renderer.cpp | 115 +++++++++++++++++++++++++------ src/gfx/renderer.hpp | 9 ++- src/gfx/shader.cpp | 3 + src/gfx/shader.hpp | 3 + src/gfx/shader_common.hpp | 1 + src/gfx/shader_defs.hpp | 3 +- src/gfx/surface.hpp | 1 + src/gfx/surface_render_flags.hpp | 13 ++-- src/gfx/surface_shader.cpp | 35 +++++++++- src/gfx/surface_shader.hpp | 2 +- src/gfx/texture.cpp | 2 +- 20 files changed, 387 insertions(+), 56 deletions(-) create mode 100644 src/gfx/light_cache.hpp diff --git a/src/assets/model.cpp b/src/assets/model.cpp index 40c74be..5e859e5 100644 --- a/src/assets/model.cpp +++ b/src/assets/model.cpp @@ -112,6 +112,10 @@ std::shared_ptr assets::Model::LoadFromFile(const std::stri CLIENT_ONLY(sflags |= gfx::SF_OBJECT_COLOR;) CLIENT_ONLY(sflags |= gfx::SF_OBJECT_COLOR_MULT;) } + else if (flag == "+multicolor") + { + CLIENT_ONLY(sflags |= gfx::SF_MULTICOLOR;) + } else if (flag == "+blend") { std::string blend_str; diff --git a/src/game/drivable_vehicle.cpp b/src/game/drivable_vehicle.cpp index 237f510..7dfd7ec 100644 --- a/src/game/drivable_vehicle.cpp +++ b/src/game/drivable_vehicle.cpp @@ -61,11 +61,21 @@ bool game::DrivableVehicle::SetPassenger(uint32_t seat_idx, ControllableCharacte seat_info.occupant = character; - if (seat_idx == 0 && !character) + if (seat_idx == 0) { - // clear inputs - SetInputs(0); - SetSteering(false, 0.0f); + if (character) + { + SetLightsOn(true); + } + else + { + SetLightsOn(false); + + // clear inputs + SetInputs(0); + SetSteering(false, 0.0f); + } + } return true; diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index d58b73c..ee45042 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -44,6 +44,7 @@ void game::Vehicle::Update() UpdateCrash(); ProcessInput(); UpdateWheels(); + UpdateLights(); sync_current_ = 1 - sync_current_; UpdateSyncState(); @@ -177,23 +178,38 @@ void game::Vehicle::ProcessInput() const bool in_left = in_ & (1 << VIN_LEFT); const bool in_right = in_ & (1 << VIN_RIGHT); + bool active_braking = false; + bool active_gas = false; + if (in_forward) { if (speed < -1) + { breakingForce = maxBreakingForce; + active_braking = true; + } else + { engineForce = maxEngineForce; + active_gas = true; + } } if (in_backward) { if (speed > 1) + { breakingForce = maxBreakingForce; + active_braking = true; + } else + { engineForce = -maxEngineForce / 2; + active_gas = true; + } } // idle breaking - if (!in_forward && !in_backward) + if (!active_braking && !active_gas) { breakingForce = 20.0f; } @@ -272,11 +288,14 @@ void game::Vehicle::ProcessInput() } } - if (glm::abs(engineForce) > 0) + if (active_gas) flags_ |= VF_ACCELERATING; - if (glm::abs(breakingForce) > 0) - flags_ |= VF_BREAKING; + if (active_braking) + flags_ |= VF_BRAKING; + + if (active_gas && engineForce < 0.0f) + flags_ |= VF_REVERSING; const bool can_roll = wheels_on_ground_ <= (wheels_.size() / 2); @@ -380,6 +399,14 @@ void game::Vehicle::UpdateWheels() } } +void game::Vehicle::UpdateLights() +{ + if (lights_on_) + flags_ |= VF_LIGHTS_ON; + + // TODO: orange lights +} + void game::Vehicle::UpdateSyncState() { VehicleSyncState& state = sync_[sync_current_]; diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index 55f4974..3717e25 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -74,6 +74,9 @@ public: void SetSteering(bool analog, float value = 0.0f); + void SetLightsOn(bool lights_on) { lights_on_ = lights_on; } + bool GetLightsOn() const { return lights_on_; } + virtual void SetTuning(const VehicleTuning& tuning); const std::string& GetModelName() const { return tuning_.model; } @@ -87,6 +90,7 @@ private: void ProcessInput(); void UpdateCrash(); void UpdateWheels(); + void UpdateLights(); void UpdateSyncState(); VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const; @@ -135,6 +139,8 @@ private: size_t wheels_on_ground_ = 0; size_t can_roll_frames_ = 0; + bool lights_on_ = false; + }; } // namespace game \ No newline at end of file diff --git a/src/game/vehicle_sync.hpp b/src/game/vehicle_sync.hpp index c31bd5f..eee3b4c 100644 --- a/src/game/vehicle_sync.hpp +++ b/src/game/vehicle_sync.hpp @@ -13,10 +13,13 @@ using VehicleFlags = uint8_t; enum VehicleFlag : VehicleFlags { - VF_NONE, - VF_ACCELERATING = 0x01, - VF_BREAKING = 0x02, - VF_BROKENWINDOWS = 0x04, + VF_NONE = 0, + VF_ACCELERATING = 1, + VF_BRAKING = 2, + VF_BROKENWINDOWS = 4, + VF_LIGHTS_ON = 8, + VF_REVERSING = 16, + VF_ORANGE_LIGHTS_ON = 32, }; struct VehicleSyncState diff --git a/src/gameview/vehicleview.cpp b/src/gameview/vehicleview.cpp index 5d9f3d3..adfec02 100644 --- a/src/gameview/vehicleview.cpp +++ b/src/gameview/vehicleview.cpp @@ -4,6 +4,7 @@ #include "net/utils.hpp" #include "worldview.hpp" #include "utils/random.hpp" +#include "utils/math.hpp" #include #include @@ -18,6 +19,7 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh"); mesh_ = *model_->GetModel()->GetMesh(); InitMesh(); + InitHeadlights(); auto& modelwheels = model_->GetWheels(); wheels_.resize(modelwheels.size()); @@ -117,6 +119,8 @@ void game::view::VehicleView::Update(const UpdateInfo& info) mesh_.surfaces[idx].texture = assets::CacheManager::GetTexture("data/carbrokenwindows.png"); } } + + UpdateLights(info.delta_time); } void game::view::VehicleView::Draw(const DrawArgs& args) @@ -129,6 +133,7 @@ void game::view::VehicleView::Draw(const DrawArgs& args) cmd.surface = &surface; cmd.matrices = &root_.matrix; cmd.color = &colors_[0]; + cmd.num_colors = SD_MAX_COLORS; args.dlist.AddSurface(cmd); } @@ -157,6 +162,29 @@ void game::view::VehicleView::Draw(const DrawArgs& args) args.dlist.AddBeam(start, end, 0xFFFFFF00, 0.01f); args.dlist.AddBeam(end, end2, 0xFFFF00FF, 0.01f); } + + // headlights + if (headlights_factor_ >= 0.01f) + { + // light + auto light_pos = world_.CameraSweep(root_.GetGlobalPosition(), root_.matrix * glm::vec4(0.0f, 7.0f, 0.0f, 1.0f)); + args.dlist.AddLight(light_pos, glm::vec3(1.0f, 1.0f, 1.0f) * headlights_factor_, 5.0f); + + // cones + for (size_t i = 0; i < num_headlights; ++i) + { + auto& cone_surfaces = light_cone_mdl_->GetMesh()->surfaces; + + for (const auto& surface : cone_surfaces) + { + gfx::DrawSurfaceCmd cmd; + cmd.surface = &surface; + cmd.matrices = &light_cone_node_[i].matrix; + cmd.color = &headlight_cone_color_; + args.dlist.AddSurface(cmd); + } + } + } } void game::view::VehicleView::InitMesh() @@ -188,6 +216,8 @@ void game::view::VehicleView::InitMesh() bool game::view::VehicleView::ReadTuning(net::InMessage& msg) { + glm::vec4 recv_colors[4]; + // read colors for (size_t i = 0; i < 4; ++i) { @@ -196,7 +226,7 @@ bool game::view::VehicleView::ReadTuning(net::InMessage& msg) if (!net::ReadRGB(msg, color)) return false; - colors_[i] = glm::unpackUnorm4x8(color); + recv_colors[i] = glm::unpackUnorm4x8(color); } // read wheel models @@ -210,9 +240,12 @@ bool game::view::VehicleView::ReadTuning(net::InMessage& msg) std::string wheel_model_name = wheelmodel_fixed; wheels_[i].model = !wheel_model_name.empty() ? assets::CacheManager::GetModel("data/" + wheel_model_name + ".mdl") : model_->GetWheels()[i].model; - wheels_[i].color = colors_[1]; // TODO: dynamic?; + wheels_[i].color = recv_colors[1]; // TODO: dynamic?; } + colors_[VCS_PRIMARY] = recv_colors[0]; // primary + colors_[VCS_SECONDARY] = recv_colors[0]; // secondary + return true; } @@ -346,3 +379,52 @@ bool game::view::VehicleView::ProcessDeformMsg(net::InMessage& msg) } + +void game::view::VehicleView::InitHeadlights() +{ + for (size_t i = 0; i < 2; ++i) + { + std::string loc_name = std::string("headlight") + static_cast('0' + i); + auto loc = model_->GetLocation(loc_name); + + if (!loc) + break; + + if (!light_cone_mdl_) + { + light_cone_mdl_ = assets::CacheManager::GetModel("data/headlightcone.mdl"); + } + + light_cone_node_[i].parent = &root_; + light_cone_node_[i].local.position = loc->position; + + ++num_headlights; + } +} + +void game::view::VehicleView::UpdateLights(float delta_t) +{ + float max_delta = delta_t * 10.0f; + + MoveToward(headlights_factor_, (flags_ & VF_LIGHTS_ON) ? 1.0f : 0.0f, max_delta); + MoveToward(braking_lights_factor_, (flags_ & VF_BRAKING) ? 1.0f : 0.0f, max_delta); + MoveToward(orange_lights_factor_, (flags_ & VF_ORANGE_LIGHTS_ON) ? 1.0f : 0.0f, max_delta); + MoveToward(reverse_light_factor_, (flags_ & VF_REVERSING) ? 1.0f : 0.0f, max_delta); + + colors_[VCS_HEADLIGHTS] = glm::vec4(1.0f, 1.0f, 1.0f, headlights_factor_); + colors_[VCS_REAR_LIGHTS] = glm::vec4(1.0f, 1.0f, 1.0f, headlights_factor_ * 0.5f + braking_lights_factor_ * 1.0f); + colors_[VCS_BRAKING_LIGHTS] = glm::vec4(1.0f, 1.0f, 1.0f, braking_lights_factor_); + colors_[VCS_ORANGE_LIGHTS] = glm::vec4(1.0f, 1.0f, 1.0f, orange_lights_factor_); + colors_[VCS_REVERSE_LIGHT] = glm::vec4(1.0f, 1.0f, 1.0f, reverse_light_factor_); + + if (headlights_factor_ < 0.01f) + return; + + float intensity = headlights_factor_ * 0.2f; + headlight_cone_color_ = glm::vec4(intensity, intensity, intensity, 1.0f); + + for (size_t i = 0; i < num_headlights; ++i) + { + light_cone_node_[i].UpdateMatrix(); + } +} diff --git a/src/gameview/vehicleview.hpp b/src/gameview/vehicleview.hpp index 655aca3..5174281 100644 --- a/src/gameview/vehicleview.hpp +++ b/src/gameview/vehicleview.hpp @@ -31,6 +31,20 @@ struct VehicleDeformView VehicleDeformView(const gfx::DeformGridInfo& info) : grid(info), tex(std::make_shared(info)) {} }; +enum VehicleColorSlot +{ + VCS_PRIMARY, + VCS_SECONDARY, + + VCS_HEADLIGHTS, + VCS_REAR_LIGHTS, + VCS_BRAKING_LIGHTS, + VCS_ORANGE_LIGHTS, + VCS_REVERSE_LIGHT, + + VCS_RESERVED, +}; + class VehicleView : public EntityView { using Super = EntityView; @@ -52,10 +66,14 @@ private: bool ReadDeformSync(net::InMessage& msg); bool ProcessDeformMsg(net::InMessage& msg); + void InitHeadlights(); + + void UpdateLights(float delta_t); + private: std::shared_ptr model_; assets::Mesh mesh_; - glm::vec4 colors_[4]; + glm::vec4 colors_[SD_MAX_COLORS]; game::VehicleSyncState sync_; std::vector wheels_; @@ -71,6 +89,18 @@ private: bool windows_broken_ = false; std::unique_ptr deform_; std::vector> debug_deforms_; + + // lights + float headlights_factor_ = 0.0f; + float braking_lights_factor_ = 0.0f; + float orange_lights_factor_ = 0.0f; + float reverse_light_factor_ = 0.0f; + + size_t num_headlights = 0; + std::shared_ptr light_cone_mdl_; + TransformNode light_cone_node_[2]; + glm::vec4 headlight_cone_color_; + }; } \ No newline at end of file diff --git a/src/gfx/draw_list.hpp b/src/gfx/draw_list.hpp index 0fed964..567d98a 100644 --- a/src/gfx/draw_list.hpp +++ b/src/gfx/draw_list.hpp @@ -3,9 +3,10 @@ #include #include "assets/skeleton.hpp" +#include "light_cache.hpp" #include "surface.hpp" -#include "uniform_buffer.hpp" #include "surface_render_flags.hpp" +#include "uniform_buffer.hpp" namespace gfx { @@ -13,13 +14,26 @@ namespace gfx struct DrawSurfaceCmd { const Surface* surface = nullptr; - const glm::mat4* matrices = nullptr; // model matrix - const glm::vec4* color = nullptr; // optional tint - uint32_t first = 0; // first triangle index - uint32_t count = 0; // num triangles - float dist = 0.0f; // distance to camera - for transparnt sorting + const glm::mat4* matrices = nullptr; // model matrix + const glm::vec4* color = nullptr; // optional tint const UniformBuffer* skinning = nullptr; // skinning matrices for skeletal meshes + uint32_t first = 0; // first triangle index + uint32_t count = 0; // num triangles + float dist = 0.0f; // distance to camera - for transparnt sorting SurfaceRenderFlags rflags = 0; + uint8_t num_colors = 0; // >0 for multicolor, requires array of colors in "color" +}; + +struct DrawLightCmd +{ + glm::vec3 position = glm::vec3(0.0f); + glm::vec3 color = glm::vec3(1.0f); + float radius = 1.0f; + + DrawLightCmd(const glm::vec3& position, const glm::vec3& color, float radius) + : position(position), color(color), radius(radius) + { + } }; struct DrawBeamCmd @@ -31,8 +45,8 @@ struct DrawBeamCmd size_t num_segments; float max_offset; - DrawBeamCmd(const glm::vec3& start, const glm::vec3& end, uint32_t color, float radius, - size_t num_segments, float max_offset) + DrawBeamCmd(const glm::vec3& start, const glm::vec3& end, uint32_t color, float radius, size_t num_segments, + float max_offset) : start(start), end(end), color(color), radius(radius), num_segments(num_segments), max_offset(max_offset) { } @@ -49,11 +63,18 @@ struct DrawHudCmd struct DrawList { std::vector surfaces; + std::vector lights; std::vector beams; std::vector huds; void AddSurface(const DrawSurfaceCmd& cmd) { surfaces.emplace_back(cmd); } + void AddLight(const DrawLightCmd& cmd) { lights.emplace_back(cmd); } + void AddLight(const glm::vec3& position, const glm::vec3& color, float radius) + { + lights.emplace_back(position, color, radius); + } + void AddBeam(const DrawBeamCmd& cmd) { beams.emplace_back(cmd); } void AddBeam(const glm::vec3& start, const glm::vec3& end, uint32_t color = 0xFFFFFFFF, float radius = 0.1f, size_t num_segments = 1, float max_offset = 0.0f) @@ -66,6 +87,7 @@ struct DrawList void Clear() { surfaces.clear(); + lights.clear(); beams.clear(); huds.clear(); } diff --git a/src/gfx/light_cache.hpp b/src/gfx/light_cache.hpp new file mode 100644 index 0000000..a0fa6d7 --- /dev/null +++ b/src/gfx/light_cache.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include "shader_defs.hpp" + +namespace gfx +{ + +template +struct LightArray +{ + size_t num_lights = 0; + glm::vec3 positions[Size]; + glm::vec4 colors_rs[Size]; +}; + +struct LightCache : public LightArray +{ + glm::vec3 center = glm::vec3(0.0f); + size_t frame = 0; +}; + + +} \ No newline at end of file diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index bab0cab..bfd3fed 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -23,11 +23,14 @@ gfx::Renderer::Renderer() void gfx::Renderer::DrawList(gfx::DrawList& list, const DrawListParams& params) { + ++frame_; + current_shader_ = nullptr; glViewport(0, 0, params.screen_width, params.screen_height); glClearColor(params.env.clear_color.r, params.env.clear_color.g, params.env.clear_color.b, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + CreateLightGrid(list.lights); DrawSurfaceList(list.surfaces, params); DrawBeamList(list.beams, params); DrawHudList(list.huds, params); @@ -141,7 +144,47 @@ void gfx::Renderer::SetupSurfaceShader(SurfaceShader& sshader, const DrawListPar void gfx::Renderer::InvalidateSurfaceShader(SurfaceShader& sshader) { sshader.global_setup = false; - sshader.color = glm::vec4(-1.0f); // invalidate color + sshader.color = nullptr; +} + +static glm::u32vec3 GetLightCellCoords(const glm::vec3& pos) +{ + // grid cell size = 20m + // grid size = 1000x1000x1000 + return glm::clamp(glm::u32vec3(glm::floor(pos * 0.05f) + 500.0f), 0U, 1000U); +} + +static uint32_t HashLightGridPosition(const glm::u32vec3& coords) +{ + return (coords.x << 20) | (coords.y << 10); // | coords.z; +} + +void gfx::Renderer::CreateLightGrid(std::span lights) +{ + light_grid_.clear(); + + for (const auto& light : lights) + { + auto coords = GetLightCellCoords(light.position); + + for (uint32_t x = coords.x - 1; x <= coords.x + 1; ++x) + { + for (uint32_t y = coords.y - 1; y <= coords.y + 1; ++y) + { + auto hash = HashLightGridPosition(glm::u32vec3(x, y, 0)); + auto& cell = light_grid_[hash]; + + if (cell.num_lights >= LIGHT_GRID_CELL_LIGHTS) + continue; + + cell.positions[cell.num_lights] = light.position; + cell.colors_rs[cell.num_lights] = glm::vec4(light.color, light.radius); + + ++cell.num_lights; + } + } + } + } void gfx::Renderer::DrawSurfaceList(std::span list, const DrawListParams& params) @@ -170,6 +213,8 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi cmd.rflags |= SRF_OBJECT_COLOR; else if (cmd.surface->sflags & SF_OBJECT_COLOR) cmd.rflags |= SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND; + else if (cmd.surface->sflags & SF_MULTICOLOR) + cmd.rflags |= SRF_MULTICOLOR; } if (cmd.surface->sflags & SF_2SIDED) @@ -224,6 +269,7 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi const gfx::VertexArray* last_vao = nullptr; const gfx::UniformBuffer* last_skin = nullptr; const DeformTexture* last_deform = nullptr; + size_t last_numlights = -1; InvalidateShaders(); @@ -245,17 +291,6 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi for (const DrawSurfaceCmd& cmd : list) { const Surface* surface = cmd.surface; - - // // mesh flags - // const bool skeletal_flag = surface->mflags & MF_SKELETAL; - // // surface flags - //const bool twosided_flag = cmd.rflags & SRF_2SIDED; - // const bool blend_flag = surface->sflags & SF_BLEND; - // const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR; - // const bool object_color_mult_flag = surface->sflags & SF_OBJECT_COLOR_MULT; - // const bool deform_flag = surface->sflags & SF_DEFORM_GRID; - // const bool unlit_flag = surface->sflags & SF_UNLIT; - SurfaceRenderFlags rflags_diff = last_rflags ^ cmd.rflags; // sync 2sided @@ -279,24 +314,32 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi auto shader = sshader->shader.get(); + static const glm::mat4 identity(1.0f); + const glm::mat4* model = &identity; + // set model matrix if (cmd.matrices) { - glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]); - } - else - { // use identity if no matrix provided - static const glm::mat4 identity(1.0f); - glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]); + model = &cmd.matrices[0]; } + glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &(*model)[0][0]); + // sync color if (sshader->iflags & SIF_OBJECT_COLOR) { - if (sshader->color != *cmd.color) + if (sshader->color != cmd.color) { glUniform4fv(shader->U(SU_COLOR), 1, &(*cmd.color)[0]); - sshader->color = *cmd.color; + sshader->color = cmd.color; + } + } + else if (sshader->iflags & SIF_MULTICOLOR_DATA) + { + if (sshader->color != cmd.color && cmd.num_colors > 0) + { + glUniform4fv(shader->U(SU_COLOR), cmd.num_colors, &(*cmd.color)[0]); + sshader->color = cmd.color; } } @@ -324,6 +367,37 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } + // sync lights + if ((cmd.rflags & SRF_LIT)) + { + auto center = glm::vec3((*model)[3]); + auto it = light_grid_.find(HashLightGridPosition(GetLightCellCoords(center))); + + size_t numlights = 0; + + if (it != light_grid_.end()) + { + numlights = it->second.num_lights; + } + + if (numlights == 0) + { + if (last_numlights > 0) + { + glUniform1i(shader->U(SU_NUMLIGHTS), 0); + } + } + else + { + auto& lights = it->second; + glUniform1i(shader->U(SU_NUMLIGHTS), numlights); + glUniform3fv(shader->U(SU_LIGHT_POSITIONS), numlights, &lights.positions[0][0]); + glUniform4fv(shader->U(SU_LIGHT_COLORS_RS), numlights, &lights.colors_rs[0][0]); + } + + last_numlights = numlights; + } + // bind texture if ((sshader->iflags & SIF_COLOR_TEXTURE) && last_texture != surface->texture.get()) { @@ -517,3 +591,4 @@ void gfx::Renderer::DrawHudList(std::span queue, const DrawListParam glDrawElements(GL_TRIANGLES, static_cast(count), GL_UNSIGNED_INT, (void*)(first * sizeof(GLuint))); } } + diff --git a/src/gfx/renderer.hpp b/src/gfx/renderer.hpp index 6b92e69..b18e2fa 100644 --- a/src/gfx/renderer.hpp +++ b/src/gfx/renderer.hpp @@ -35,7 +35,7 @@ struct SurfaceShader // cached state to avoid redundant uniform updates which are expensive especially on WebGL bool global_setup = false; - glm::vec4 color = glm::vec4(-1.0f); // invalid to force initial setup + const glm::vec4* color = nullptr; }; class Renderer @@ -53,6 +53,8 @@ private: void SetupSurfaceShader(SurfaceShader& sshader, const DrawListParams& params); void InvalidateSurfaceShader(SurfaceShader& sshader); + void CreateLightGrid(std::span lights); + void DrawSurfaceList(std::span queue, const DrawListParams& params); void DrawBeamList(std::span queue, const DrawListParams& params); void DrawHudList(std::span queue, const DrawListParams& params); @@ -68,6 +70,11 @@ private: std::unique_ptr hud_shader_; const Shader* current_shader_ = nullptr; + + constexpr static size_t LIGHT_GRID_CELL_LIGHTS = SD_MAX_LIGHTS; + std::map> light_grid_; + + size_t frame_ = 0; }; } // namespace gfx \ No newline at end of file diff --git a/src/gfx/shader.cpp b/src/gfx/shader.cpp index dd1bbdd..aa66fa1 100644 --- a/src/gfx/shader.cpp +++ b/src/gfx/shader.cpp @@ -17,6 +17,9 @@ static const char* const s_uni_names[] = { "u_sun_color", // SU_SUN_COLOR "u_sun_direction", // SU_SUN_DIRECTION "u_fog", // SU_FOG + "u_num_lights", + "u_light_positions", + "u_light_colors_rs", }; // Vytvori shader z daneho zdroje diff --git a/src/gfx/shader.hpp b/src/gfx/shader.hpp index 41b841f..3519cf3 100644 --- a/src/gfx/shader.hpp +++ b/src/gfx/shader.hpp @@ -22,6 +22,9 @@ namespace gfx SU_SUN_COLOR, SU_SUN_DIRECTION, SU_FOG, + SU_NUMLIGHTS, + SU_LIGHT_POSITIONS, + SU_LIGHT_COLORS_RS, SU_COUNT }; diff --git a/src/gfx/shader_common.hpp b/src/gfx/shader_common.hpp index 97eac37..01fd0cc 100644 --- a/src/gfx/shader_common.hpp +++ b/src/gfx/shader_common.hpp @@ -20,6 +20,7 @@ #define SHADER_DEFS \ "#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \ "#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \ + "#define MAX_COLORS " STRINGIFY(SD_MAX_COLORS) "\n" \ "\n" #define SHADER_HEADER \ diff --git a/src/gfx/shader_defs.hpp b/src/gfx/shader_defs.hpp index 94d25ae..a27ad37 100644 --- a/src/gfx/shader_defs.hpp +++ b/src/gfx/shader_defs.hpp @@ -1,4 +1,5 @@ #pragma once -#define SD_MAX_LIGHTS 4 +#define SD_MAX_LIGHTS 8 #define SD_MAX_BONES 128 +#define SD_MAX_COLORS 8 \ No newline at end of file diff --git a/src/gfx/surface.hpp b/src/gfx/surface.hpp index 81e5edd..3359a7c 100644 --- a/src/gfx/surface.hpp +++ b/src/gfx/surface.hpp @@ -28,6 +28,7 @@ enum SurfaceFlag : SurfaceFlags SF_DEFORM_GRID = 0x10, // use deform grid SF_UNLIT = 0x20, // dont apply lighting SF_OBJECT_COLOR_MULT = 0x40, // object color multiplies instead of acting as background + SF_MULTICOLOR = 0x80, // multiple color slots encoded in alpha }; struct Surface diff --git a/src/gfx/surface_render_flags.hpp b/src/gfx/surface_render_flags.hpp index edc8b47..2512d80 100644 --- a/src/gfx/surface_render_flags.hpp +++ b/src/gfx/surface_render_flags.hpp @@ -17,15 +17,16 @@ enum SurfaceRenderFlag : SurfaceRenderFlags SRF_CULL_ALPHA = 4, SRF_OBJECT_COLOR = 8, SRF_OBJECT_COLOR_BACKGROUND = 16, - SRF_LIT = 32, - SRF_DEFORM = 64, - SRF_SKELETAL = 128, - SRF_TEXTURE = 256, + SRF_MULTICOLOR = 32, + SRF_LIT = 64, + SRF_DEFORM = 128, + SRF_SKELETAL = 256, + SRF_TEXTURE = 512, - SRF__SHADER = SRF_CULL_ALPHA | SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND | SRF_LIT | SRF_SKELETAL | SRF_DEFORM | SRF_TEXTURE, + SRF__SHADER = SRF_CULL_ALPHA | SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND | SRF_MULTICOLOR | SRF_LIT | SRF_SKELETAL | SRF_DEFORM | SRF_TEXTURE, // order affects visual result - SRF_BLEND = 512, + SRF_BLEND = 1024, }; diff --git a/src/gfx/surface_shader.cpp b/src/gfx/surface_shader.cpp index 0a76281..1ab2ef6 100644 --- a/src/gfx/surface_shader.cpp +++ b/src/gfx/surface_shader.cpp @@ -76,6 +76,26 @@ std::unique_ptr gfx::CreateSurfaceShader(SurfaceRenderFlags flags, input_flags |= SIF_OBJECT_COLOR; } + else if (flags & SRF_MULTICOLOR) + { + frag_uniforms += "uniform vec4 u_color[MAX_COLORS];\n"; + frag_main += R"GLSL( + int color_slot = clamp(int(o_color.a * 9.0), 0, MAX_COLORS); + o_color.a = 1.0; + + float emis = 0.0; + + if (color_slot < MAX_COLORS) + { + vec4 color = u_color[color_slot]; + o_color.rgb *= color.rgb; + emis = color.a; + } + + )GLSL"; + + input_flags |= SIF_MULTICOLOR_DATA; + } // alpha culling if (flags & SRF_CULL_ALPHA) @@ -119,8 +139,8 @@ std::unique_ptr gfx::CreateSurfaceShader(SurfaceRenderFlags flags, if (dist2 < light_radius * light_radius) { float dist = sqrt(dist2); float attenuation = 1.0 - (dist / light_radius); - float dot_term = max(dot(sector_normal, normalize(to_light)), 0.0); - color += light_color * dot_term * attenuation; + //float dot_term = max(dot(sector_normal, normalize(to_light)), 0.0); + color += light_color * attenuation; } } @@ -179,7 +199,16 @@ std::unique_ptr gfx::CreateSurfaceShader(SurfaceRenderFlags flags, input_flags |= SIF_DEFORM_DATA; } - frag_main += "o_color.rgb *= v_color;"; + if (flags & SRF_MULTICOLOR) + { + frag_main += "o_color.rgb *= mix(v_color, vec3(1.5), emis);"; + } + else + { + frag_main += "o_color.rgb *= v_color;"; + } + + vert_main = vert_pos_calc + vert_main; std::string vertex_src = SHADER_HEADER + vert_attributes + vert_uniforms + vert_outs + vert_funcs + "\nvoid main() {\n" + vert_main + "\n}\n"; diff --git a/src/gfx/surface_shader.hpp b/src/gfx/surface_shader.hpp index 77a5d2a..c85d364 100644 --- a/src/gfx/surface_shader.hpp +++ b/src/gfx/surface_shader.hpp @@ -17,7 +17,7 @@ enum SurfaceShaderInputFlag : SurfaceShaderInputFlags SIF_LIGHTING_DATA = 4, SIF_SKELETAL_DATA = 8, SIF_DEFORM_DATA = 16, - + SIF_MULTICOLOR_DATA = 32, }; std::unique_ptr CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags); diff --git a/src/gfx/texture.cpp b/src/gfx/texture.cpp index 3013134..91fb472 100644 --- a/src/gfx/texture.cpp +++ b/src/gfx/texture.cpp @@ -78,7 +78,7 @@ std::shared_ptr gfx::Texture::LoadFromFile(const std::string& file std::shared_ptr texture; try { - texture = std::make_shared(width, height, data, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, false, true); + texture = std::make_shared(width, height, data, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, false, false); } catch (const std::exception& e) { stbi_image_free(data);