Lights & vehicle lights

This commit is contained in:
tovjemam 2026-05-24 23:14:29 +02:00
parent 0b3b1d09ab
commit 7ea00e2ed4
20 changed files with 387 additions and 56 deletions

View File

@ -112,6 +112,10 @@ std::shared_ptr<const assets::Model> 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;

View File

@ -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;

View File

@ -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_];

View File

@ -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

View File

@ -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

View File

@ -4,6 +4,7 @@
#include "net/utils.hpp"
#include "worldview.hpp"
#include "utils/random.hpp"
#include "utils/math.hpp"
#include <iostream>
#include <ranges>
@ -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<char>('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();
}
}

View File

@ -31,6 +31,20 @@ struct VehicleDeformView
VehicleDeformView(const gfx::DeformGridInfo& info) : grid(info), tex(std::make_shared<gfx::DeformTexture>(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<const assets::VehicleModel> model_;
assets::Mesh mesh_;
glm::vec4 colors_[4];
glm::vec4 colors_[SD_MAX_COLORS];
game::VehicleSyncState sync_;
std::vector<VehicleWheelViewInfo> wheels_;
@ -71,6 +89,18 @@ private:
bool windows_broken_ = false;
std::unique_ptr<VehicleDeformView> deform_;
std::vector<std::tuple<glm::vec3, glm::vec3>> 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<const assets::Model> light_cone_mdl_;
TransformNode light_cone_node_[2];
glm::vec4 headlight_cone_color_;
};
}

View File

@ -3,9 +3,10 @@
#include <vector>
#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<glm::mat4>* 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<DrawSurfaceCmd> surfaces;
std::vector<DrawLightCmd> lights;
std::vector<DrawBeamCmd> beams;
std::vector<DrawHudCmd> 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();
}

26
src/gfx/light_cache.hpp Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <glm/glm.hpp>
#include "shader_defs.hpp"
namespace gfx
{
template <size_t Size>
struct LightArray
{
size_t num_lights = 0;
glm::vec3 positions[Size];
glm::vec4 colors_rs[Size];
};
struct LightCache : public LightArray<SD_MAX_LIGHTS>
{
glm::vec3 center = glm::vec3(0.0f);
size_t frame = 0;
};
}

View File

@ -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<DrawLightCmd> 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<DrawSurfaceCmd> list, const DrawListParams& params)
@ -170,6 +213,8 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> 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<DrawSurfaceCmd> list, const DrawLi
const gfx::VertexArray* last_vao = nullptr;
const gfx::UniformBuffer<glm::mat4>* last_skin = nullptr;
const DeformTexture* last_deform = nullptr;
size_t last_numlights = -1;
InvalidateShaders();
@ -245,17 +291,6 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> 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<DrawSurfaceCmd> 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<DrawSurfaceCmd> 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<DrawHudCmd> queue, const DrawListParam
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(count), GL_UNSIGNED_INT, (void*)(first * sizeof(GLuint)));
}
}

View File

@ -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<DrawLightCmd> lights);
void DrawSurfaceList(std::span<DrawSurfaceCmd> queue, const DrawListParams& params);
void DrawBeamList(std::span<DrawBeamCmd> queue, const DrawListParams& params);
void DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params);
@ -68,6 +70,11 @@ private:
std::unique_ptr<Shader> hud_shader_;
const Shader* current_shader_ = nullptr;
constexpr static size_t LIGHT_GRID_CELL_LIGHTS = SD_MAX_LIGHTS;
std::map<uint32_t, LightArray<LIGHT_GRID_CELL_LIGHTS>> light_grid_;
size_t frame_ = 0;
};
} // namespace gfx

View File

@ -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

View File

@ -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
};

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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,
};

View File

@ -76,6 +76,26 @@ std::unique_ptr<gfx::Shader> 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::Shader> 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::Shader> 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";

View File

@ -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<Shader> CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags);

View File

@ -78,7 +78,7 @@ std::shared_ptr<gfx::Texture> gfx::Texture::LoadFromFile(const std::string& file
std::shared_ptr<Texture> texture;
try {
texture = std::make_shared<Texture>(width, height, data, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, false, true);
texture = std::make_shared<Texture>(width, height, data, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, false, false);
}
catch (const std::exception& e) {
stbi_image_free(data);