Compare commits
10 Commits
308c39b8f8
...
08029fdd2e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08029fdd2e | ||
|
|
b7411d4611 | ||
|
|
2df4ba3297 | ||
|
|
20f3c023dd | ||
|
|
b6e866c729 | ||
|
|
530ea1902b | ||
|
|
2d3f36594f | ||
|
|
7ea00e2ed4 | ||
|
|
0b3b1d09ab | ||
|
|
3bb458e0ab |
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@ out/
|
||||
.gradle/
|
||||
/android/app/src/main/assets/
|
||||
*.lnk
|
||||
CMakeSettings.json
|
||||
todo.txt
|
||||
|
||||
@ -105,11 +105,15 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gfx/frustum.cpp"
|
||||
"src/gfx/renderer.hpp"
|
||||
"src/gfx/renderer.cpp"
|
||||
"src/gfx/shader_common.hpp"
|
||||
"src/gfx/shader_defs.hpp"
|
||||
"src/gfx/shader_sources.hpp"
|
||||
"src/gfx/shader_sources.cpp"
|
||||
"src/gfx/shader.hpp"
|
||||
"src/gfx/shader.cpp"
|
||||
"src/gfx/surface_render_flags.hpp"
|
||||
"src/gfx/surface_shader.hpp"
|
||||
"src/gfx/surface_shader.cpp"
|
||||
"src/gfx/surface.hpp"
|
||||
"src/gfx/texture.cpp"
|
||||
"src/gfx/texture.hpp"
|
||||
|
||||
@ -41,6 +41,15 @@ const assets::MapGraph* assets::Map::GetGraph(const std::string& name) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::span<const assets::MapLocation> assets::Map::GetLocations(const std::string& name) const
|
||||
{
|
||||
auto it = locations_.find(name);
|
||||
if (it == locations_.end())
|
||||
return std::span<const MapLocation>();
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// MapLoader
|
||||
|
||||
assets::MapLoader::MapLoader(const std::string& filename)
|
||||
@ -281,6 +290,13 @@ void assets::MapLoader::LoadStructs()
|
||||
|
||||
graph_edges.emplace_back(from_idx, to_idx);
|
||||
}
|
||||
else if (command == "loc")
|
||||
{
|
||||
std::string loc_name;
|
||||
iss >> loc_name;
|
||||
Transform& trans = map_->locations_[loc_name].emplace_back().transform;
|
||||
ParseTransform(iss, trans);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -51,6 +51,11 @@ struct MapGraph
|
||||
std::vector<size_t> nbs;
|
||||
};
|
||||
|
||||
struct MapLocation
|
||||
{
|
||||
Transform transform;
|
||||
};
|
||||
|
||||
class Map
|
||||
{
|
||||
public:
|
||||
@ -61,12 +66,14 @@ public:
|
||||
const std::vector<Chunk>& GetChunks() const { return chunks_; }
|
||||
const std::vector<MapStaticObject>& GetStaticObjects() const { return objs_; }
|
||||
const MapGraph* GetGraph(const std::string& name) const;
|
||||
std::span<const MapLocation> GetLocations(const std::string& name) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Model> basemodel_;
|
||||
std::vector<Chunk> chunks_;
|
||||
std::vector<MapStaticObject> objs_;
|
||||
std::map<std::string, MapGraph> graphs_;
|
||||
std::map<std::string, std::vector<MapLocation>> locations_;
|
||||
|
||||
friend class MapLoader;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -428,21 +428,21 @@ static void Main() {
|
||||
#endif
|
||||
SDL_GL_SetSwapInterval(0);
|
||||
|
||||
auto frame_dur = std::chrono::milliseconds(5);
|
||||
//auto frame_dur = std::chrono::milliseconds(0);
|
||||
|
||||
while (!s_quit)
|
||||
{
|
||||
auto t_start = std::chrono::steady_clock::now();
|
||||
//auto t_start = std::chrono::steady_clock::now();
|
||||
|
||||
Frame();
|
||||
|
||||
auto t_next = t_start + frame_dur;
|
||||
auto t_now = std::chrono::steady_clock::now();
|
||||
//auto t_next = t_start + frame_dur;
|
||||
//auto t_now = std::chrono::steady_clock::now();
|
||||
|
||||
if (t_now < t_next)
|
||||
{
|
||||
std::this_thread::sleep_for(t_next - t_now);
|
||||
}
|
||||
//if (t_now < t_next)
|
||||
//{
|
||||
//std::this_thread::sleep_for(t_next - t_now);
|
||||
//}
|
||||
}
|
||||
|
||||
s_app.reset();
|
||||
|
||||
@ -17,9 +17,10 @@ using ObjectFlags = int;
|
||||
|
||||
enum ObjectFlag : ObjectFlags
|
||||
{
|
||||
OF_DESTRUCTIBLE = 0x01,
|
||||
OF_NOTIFY_CONTACT = 0x02,
|
||||
OF_USABLE = 0x04,
|
||||
OF_DESTRUCTIBLE = 1,
|
||||
OF_NOTIFY_CONTACT = 2,
|
||||
OF_USABLE = 4,
|
||||
OF_DESTRUCTING = 8,
|
||||
};
|
||||
|
||||
struct ContactInfo
|
||||
|
||||
@ -8,6 +8,15 @@ game::DrivableVehicle::DrivableVehicle(World& world, const VehicleTuning& tuning
|
||||
OnPhysicsChanged();
|
||||
}
|
||||
|
||||
void game::DrivableVehicle::Update()
|
||||
{
|
||||
float daytime = world_.GetDayTime();
|
||||
SetLightsOn(seats_[0].occupant && (daytime < 6.0f || daytime > 18.0f));
|
||||
|
||||
Super::Update();
|
||||
|
||||
}
|
||||
|
||||
void game::DrivableVehicle::SetTuning(const VehicleTuning& tuning)
|
||||
{
|
||||
Super::SetTuning(tuning);
|
||||
@ -61,11 +70,15 @@ 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)
|
||||
{
|
||||
// clear inputs
|
||||
SetInputs(0);
|
||||
SetSteering(false, 0.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@ -20,6 +20,8 @@ public:
|
||||
|
||||
DrivableVehicle(World& world, const VehicleTuning& tuning);
|
||||
|
||||
virtual void Update() override;
|
||||
|
||||
virtual void SetTuning(const VehicleTuning& tuning) override;
|
||||
|
||||
virtual void OnPhysicsChanged() override;
|
||||
|
||||
@ -51,7 +51,7 @@ void game::Marker::SetUseTarget(const std::string& name, MarkerQueryCallback que
|
||||
{
|
||||
query_obj_ = std::make_unique<btCollisionObject>();
|
||||
|
||||
static btSphereShape query_sphere(1.0f);
|
||||
static btSphereShape query_sphere(0.01f);
|
||||
query_obj_->setCollisionShape(&query_sphere);
|
||||
query_obj_->setWorldTransform(root_.local.ToBtTransform());
|
||||
query_obj_->setCollisionFlags(btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||
|
||||
@ -82,7 +82,10 @@ game::OpenWorld::OpenWorld(Game& game) : EnterableWorld("openworld"), game_(game
|
||||
|
||||
daytime_offset_ = static_cast<float>(rand() % 24);
|
||||
|
||||
CreateTuningGarage(glm::vec3(0.0f, 0.0f, 0.0f), 0.0f);
|
||||
for (auto locs = GetMap().GetLocations("tuning"); const auto& loc : locs)
|
||||
{
|
||||
CreateTuningGarage(loc.transform.position, glm::eulerAngles(loc.transform.rotation).x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -90,7 +93,8 @@ void game::OpenWorld::Update(int64_t delta_time)
|
||||
{
|
||||
Super::Update(delta_time);
|
||||
|
||||
const float timespeed = 0.05f;
|
||||
const float day_seconds_irl = 1800.0f;
|
||||
const float timespeed = 24.0f / day_seconds_irl;
|
||||
SetDayTime(static_cast<float>(GetTime()) * 0.001f * timespeed + daytime_offset_);
|
||||
}
|
||||
|
||||
|
||||
@ -309,7 +309,23 @@ bool game::Player::ProcessMenuActionMsg(net::InMessage& msg)
|
||||
return false;
|
||||
|
||||
if (!remote_menu_ || remote_menu_->GetId() != id)
|
||||
return true; // not illegal, might be just a late message
|
||||
{
|
||||
// not illegal, might be just a late message
|
||||
// need to skip specific amount of bytes :((((
|
||||
switch (type)
|
||||
{
|
||||
case net::MA_CLICK:
|
||||
return msg.Skip(sizeof(net::MenuItemId));
|
||||
case net::MA_SELECT:
|
||||
return msg.Skip(sizeof(net::MenuItemId) + sizeof(net::MenuSelectDir));
|
||||
case net::MA_HOVER:
|
||||
return msg.Skip(sizeof(net::MenuItemId));
|
||||
case net::MA_EXIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return remote_menu_->ProcessActionMsg(msg, type);
|
||||
}
|
||||
|
||||
@ -50,11 +50,19 @@ bool game::RemoteMenu::ProcessActionMsg(net::InMessage& msg, net::MenuActionType
|
||||
return ProcessSelectMsg(msg);
|
||||
case net::MA_HOVER:
|
||||
return ProcessHoverMsg(msg);
|
||||
case net::MA_EXIT:
|
||||
return ProcessExitMsg(msg);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void game::RemoteMenu::SetHoveredIdx(int hovered)
|
||||
{
|
||||
hovered_ = hovered;
|
||||
synced_ = false;
|
||||
}
|
||||
|
||||
void game::RemoteMenu::Update()
|
||||
{
|
||||
if (synced_ && items_synced_)
|
||||
@ -66,6 +74,7 @@ void game::RemoteMenu::Update()
|
||||
auto msg = BeginMenuMsg(net::MMSG_UPDATE);
|
||||
msg.Write(net::MenuTitle(title_));
|
||||
msg.Write<net::MenuItemCount>(items_.size());
|
||||
msg.Write<net::MenuItemId>(hovered_);
|
||||
for (auto& item : items_)
|
||||
{
|
||||
msg.Write(item->type_);
|
||||
@ -124,7 +133,7 @@ bool game::RemoteMenu::ProcessClickMsg(net::InMessage& msg)
|
||||
if (!msg.Read(id))
|
||||
return false;
|
||||
|
||||
if (id > items_.size())
|
||||
if (id >= items_.size())
|
||||
return true; // not illegal
|
||||
|
||||
auto& cb = items_[id]->on_click_;
|
||||
@ -142,7 +151,7 @@ bool game::RemoteMenu::ProcessSelectMsg(net::InMessage& msg)
|
||||
if (!msg.Read(id) || !msg.Read(dir))
|
||||
return false;
|
||||
|
||||
if (id > items_.size())
|
||||
if (id >= items_.size())
|
||||
return true; // not illegal
|
||||
|
||||
int idir = dir ? 1 : -1;
|
||||
@ -161,7 +170,7 @@ bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg)
|
||||
if (!msg.Read(id))
|
||||
return false;
|
||||
|
||||
if (id > items_.size())
|
||||
if (id >= items_.size())
|
||||
return true; // not illegal
|
||||
|
||||
if (id == hovered_)
|
||||
@ -175,3 +184,10 @@ bool game::RemoteMenu::ProcessHoverMsg(net::InMessage& msg)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::RemoteMenu::ProcessExitMsg(net::InMessage& msg)
|
||||
{
|
||||
if (on_exit_)
|
||||
on_exit_();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ class RemoteMenuItem;
|
||||
using RemoteMenuClickCallback = std::function<void()>;
|
||||
using RemoteMenuHoveredCallback = std::function<void()>;
|
||||
using RemoteMenuSelectCallback = std::function<void(int)>;
|
||||
using RemoteMenuExitCallback = std::function<void()>;
|
||||
|
||||
class RemoteMenuItem
|
||||
{
|
||||
@ -37,6 +38,7 @@ public:
|
||||
void SetOnSelect(RemoteMenuSelectCallback on_select) { on_select_ = std::move(on_select); }
|
||||
void SetOnHovered(RemoteMenuHoveredCallback on_hovered) { on_hovered_ = std::move(on_hovered); }
|
||||
|
||||
|
||||
private:
|
||||
RemoteMenu& menu_;
|
||||
RemoteMenuItemType type_ = RM_LABEL;
|
||||
@ -63,6 +65,10 @@ public:
|
||||
|
||||
bool ProcessActionMsg(net::InMessage& msg, net::MenuActionType type);
|
||||
|
||||
void SetHoveredIdx(int hovered);
|
||||
|
||||
void SetOnExit(RemoteMenuExitCallback on_exit) { on_exit_ = std::move(on_exit); }
|
||||
|
||||
void Update();
|
||||
|
||||
private:
|
||||
@ -72,6 +78,7 @@ private:
|
||||
bool ProcessClickMsg(net::InMessage& msg);
|
||||
bool ProcessSelectMsg(net::InMessage& msg);
|
||||
bool ProcessHoverMsg(net::InMessage& msg);
|
||||
bool ProcessExitMsg(net::InMessage& msg);
|
||||
|
||||
private:
|
||||
net::MenuId id_;
|
||||
@ -79,7 +86,9 @@ private:
|
||||
bool synced_ = false;
|
||||
bool items_synced_ = false;
|
||||
std::vector<std::unique_ptr<RemoteMenuItem>> items_;
|
||||
int hovered_ = -1;
|
||||
int hovered_ = 0;
|
||||
|
||||
RemoteMenuExitCallback on_exit_;
|
||||
|
||||
friend class RemoteMenuItem;
|
||||
};
|
||||
|
||||
@ -53,171 +53,150 @@ const std::string& game::TuningWorld::GetOccupantName() const
|
||||
void game::TuningWorld::Setup()
|
||||
{
|
||||
tuning_ = vehicle_->GetTuning();
|
||||
// UpdateTuningVals();
|
||||
DisplayTuningMenu();
|
||||
OpenMainTuningMenu();
|
||||
}
|
||||
|
||||
// void game::TuningWorld::UpdateTuningVals()
|
||||
// {
|
||||
// tun_primary_color_ = ColorU32ToU8Vec3(tuning_.primary_color);
|
||||
|
||||
// tun_wheel_idx_ = tuning_.wheels_idx;
|
||||
// tun_wheel_color_ = ColorU32ToU8Vec3(tuning_.wheel_color);
|
||||
// }
|
||||
|
||||
// void game::TuningWorld::UpdateTuning()
|
||||
// {
|
||||
// tuning_.primary_color = ColorU8Vec3ToU32(tun_primary_color_);
|
||||
|
||||
// tuning_.wheels_idx = tun_wheel_idx_;
|
||||
// tuning_.wheel_color = ColorU8Vec3ToU32(tun_wheel_color_);
|
||||
|
||||
// vehicle_->SetTuning(tuning_);
|
||||
// }
|
||||
|
||||
// static void AddColorChannelSlider(game::RemoteMenu& menu, uint8_t& ch, std::string name, std::function<void()> on_change)
|
||||
// {
|
||||
// auto& slider = menu.AddItem(game::RM_SELECT, std::move(name));
|
||||
|
||||
// auto on_select = [&slider, &ch, on_change = std::move(on_change)] (int dir) {
|
||||
// int new_val = ch + dir * 5;
|
||||
// if (new_val < 0)
|
||||
// ch = 0;
|
||||
// else if (new_val > 255)
|
||||
// ch = 255;
|
||||
// else
|
||||
// ch = new_val;
|
||||
|
||||
// slider.SetSelection(std::to_string(ch));
|
||||
// on_change();
|
||||
// };
|
||||
|
||||
// on_select(0);
|
||||
// slider.SetOnSelect(on_select);
|
||||
// }
|
||||
|
||||
// static void AddColorSliders(game::RemoteMenu& menu, glm::u8vec3& color, std::string name, std::function<void()> on_change)
|
||||
// {
|
||||
// AddColorChannelSlider(menu, color.r, name + " ^f00R", on_change);
|
||||
// AddColorChannelSlider(menu, color.g, name + " ^0f0G", on_change);
|
||||
// AddColorChannelSlider(menu, color.b, name + " ^00fB", on_change);
|
||||
// }
|
||||
|
||||
// static void AddWheelTypeSlider(game::RemoteMenu& menu, const assets::VehicleTuningList& tuning_list, size_t& idx, std::function<void()> on_change)
|
||||
// {
|
||||
// auto& slider = menu.AddItem(game::RM_SELECT, "kola");
|
||||
|
||||
// auto on_select = [&slider, &idx, &tuning_list, on_change = std::move(on_change)] (int dir) {
|
||||
// auto& wheels = tuning_list.wheels;
|
||||
|
||||
// if (dir < 0 && idx == 0)
|
||||
// return;
|
||||
|
||||
// if (dir > 0 && (idx + 1) >= wheels.size())
|
||||
// return;
|
||||
|
||||
// idx += dir;
|
||||
|
||||
// slider.SetSelection(tuning_list.wheels[idx].displayname);
|
||||
// on_change();
|
||||
// };
|
||||
|
||||
// on_select(0);
|
||||
// slider.SetOnSelect(on_select);
|
||||
// }
|
||||
|
||||
|
||||
void game::TuningWorld::AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group)
|
||||
void game::TuningWorld::OpenMainTuningMenu()
|
||||
{
|
||||
size_t num_parts = group.parts.size();
|
||||
if (num_parts == 0)
|
||||
return;
|
||||
CloseMenu();
|
||||
|
||||
struct SliderState
|
||||
{
|
||||
const VehicleTuningGroup* group;
|
||||
int idx = 0;
|
||||
std::vector<std::string> ids;
|
||||
};
|
||||
|
||||
std::string current_part;
|
||||
|
||||
auto current_it = tuning_.parts.find(group.id);
|
||||
if (current_it != tuning_.parts.end())
|
||||
{
|
||||
current_part = current_it->second;
|
||||
}
|
||||
|
||||
auto state = std::make_shared<SliderState>();
|
||||
state->group = &group;
|
||||
|
||||
state->ids.reserve(num_parts);
|
||||
size_t i = 0;
|
||||
for (const auto& part : group.parts)
|
||||
{
|
||||
state->ids.push_back(part.first);
|
||||
|
||||
if (part.first == current_part)
|
||||
state->idx = i;
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
auto& slider = menu.AddItem(game::RM_SELECT, group.displayname);
|
||||
|
||||
auto on_select = [&slider, this, state](int dir) mutable {
|
||||
if (dir < 0 && state->idx == 0)
|
||||
state->idx = state->ids.size() - 1;
|
||||
else if (dir > 0 && (state->idx + 1) >= state->ids.size())
|
||||
state->idx = 0;
|
||||
else
|
||||
state->idx += dir;
|
||||
|
||||
auto& part_id = state->ids[state->idx];
|
||||
tuning_.parts[state->group->id] = part_id;
|
||||
slider.SetSelection(state->group->parts.at(part_id).displayname);
|
||||
|
||||
if (dir != 0)
|
||||
vehicle_->SetTuning(tuning_);
|
||||
|
||||
};
|
||||
|
||||
on_select(0);
|
||||
slider.SetOnSelect(on_select);
|
||||
}
|
||||
|
||||
void game::TuningWorld::DisplayTuningMenu()
|
||||
{
|
||||
// display menu
|
||||
auto& menu = player_->DisplayMenu("tuning");
|
||||
menu_ = &menu;
|
||||
|
||||
// auto on_change = [this] { UpdateTuning(); };
|
||||
|
||||
// AddColorSliders(menu, tun_primary_color_, "primární", on_change);
|
||||
|
||||
// if (!tuning_list_->wheels.empty())
|
||||
// {
|
||||
// AddWheelTypeSlider(menu, *tuning_list_, tun_wheel_idx_, on_change);
|
||||
// AddColorSliders(menu, tun_wheel_color_, "kola", on_change);
|
||||
// }
|
||||
size_t i = 0;
|
||||
|
||||
for (const auto& group : tuning_list_->groups)
|
||||
{
|
||||
AddTuningGroupSelect(menu, group);
|
||||
auto& btn = menu.AddItem(game::RM_BUTTON, group.displayname);
|
||||
|
||||
btn.SetOnClick([this, i, &group]() {
|
||||
main_menu_hover_ = i;
|
||||
OpenGroupMenu(group);
|
||||
});
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
auto& exit_btn = menu.AddItem(RM_BUTTON, "vylézt");
|
||||
exit_btn.SetOnClick([this] {
|
||||
menu.SetOnExit([this]() {
|
||||
Reset();
|
||||
});
|
||||
|
||||
menu.SetHoveredIdx(main_menu_hover_);
|
||||
}
|
||||
|
||||
void game::TuningWorld::OpenGroupMenu(const VehicleTuningGroup& group)
|
||||
{
|
||||
CloseMenu();
|
||||
|
||||
preview_tuning_ = tuning_;
|
||||
|
||||
auto& menu = player_->DisplayMenu(group.displayname);
|
||||
menu_ = &menu;
|
||||
|
||||
int current_idx = -1;
|
||||
int i = 0;
|
||||
|
||||
std::string state_text;
|
||||
|
||||
for (const auto& [part_id, part] : group.parts)
|
||||
{
|
||||
auto& btn = menu.AddItem(RM_BUTTON, part.displayname);
|
||||
|
||||
btn.SetOnHovered([this, &group, &part_id]() {
|
||||
preview_tuning_.parts[group.id] = part_id;
|
||||
vehicle_->SetTuning(preview_tuning_);
|
||||
});
|
||||
|
||||
btn.SetOnClick([this, &btn, &group_id = group.id, &part_id]() {
|
||||
std::string prev_part_id = GetCurrentPartId(group_id);
|
||||
|
||||
if (prev_part_id == part_id)
|
||||
return;
|
||||
|
||||
tuning_.parts[group_id] = part_id; // apply
|
||||
|
||||
std::string state_text;
|
||||
|
||||
if (mounted_part_menu_item_)
|
||||
{
|
||||
GetPartState(group_id, prev_part_id, &state_text);
|
||||
mounted_part_menu_item_->SetSelection(state_text);
|
||||
}
|
||||
|
||||
GetPartState(group_id, part_id, &state_text);
|
||||
btn.SetSelection(state_text);
|
||||
|
||||
mounted_part_menu_item_ = &btn;
|
||||
});
|
||||
|
||||
if (GetPartState(group.id, part_id, &state_text))
|
||||
{
|
||||
current_idx = i;
|
||||
mounted_part_menu_item_ = &btn;
|
||||
}
|
||||
|
||||
btn.SetSelection(state_text);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (current_idx >= 0)
|
||||
{
|
||||
menu.SetHoveredIdx(current_idx);
|
||||
}
|
||||
|
||||
menu.SetOnExit([this]() {
|
||||
mounted_part_menu_item_ = nullptr;
|
||||
vehicle_->SetTuning(tuning_); // undo preview
|
||||
OpenMainTuningMenu();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
std::string game::TuningWorld::GetCurrentPartId(const std::string& group_id)
|
||||
{
|
||||
auto part_it = tuning_.parts.find(group_id);
|
||||
if (part_it != tuning_.parts.end())
|
||||
{
|
||||
return part_it->second;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool game::TuningWorld::GetPartState(const std::string& group_name, const std::string& part_id,
|
||||
std::string* state_text)
|
||||
{
|
||||
bool mounted = part_id == GetCurrentPartId(group_name);
|
||||
|
||||
if (state_text)
|
||||
{
|
||||
*state_text = mounted ? "aktuální" : "0 Kč";
|
||||
}
|
||||
|
||||
return mounted;
|
||||
}
|
||||
|
||||
|
||||
void game::TuningWorld::CloseMenu()
|
||||
{
|
||||
if (!menu_)
|
||||
return;
|
||||
|
||||
player_->CloseMenu(*menu_);
|
||||
menu_ = nullptr;
|
||||
}
|
||||
|
||||
void game::TuningWorld::Reset()
|
||||
{
|
||||
if (player_)
|
||||
{
|
||||
player_->CloseMenu(*menu_);
|
||||
CloseMenu();
|
||||
|
||||
// make sure to undo preview tuning
|
||||
vehicle_->SetTuning(tuning_);
|
||||
|
||||
game_.MovePlayerToWorld(*player_, exit_world_, true, exit_pos_, exit_yaw_);
|
||||
|
||||
if (exit_cb_)
|
||||
@ -228,4 +207,5 @@ void game::TuningWorld::Reset()
|
||||
vehicle_ = nullptr;
|
||||
tuning_list_ = nullptr;
|
||||
menu_ = nullptr;
|
||||
main_menu_hover_ = 0;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "enterable_world.hpp"
|
||||
#include "drivable_vehicle.hpp"
|
||||
#include "remote_menu.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -29,7 +30,12 @@ private:
|
||||
void Setup();
|
||||
|
||||
void AddTuningGroupSelect(game::RemoteMenu& menu, const VehicleTuningGroup& group);
|
||||
void DisplayTuningMenu();
|
||||
|
||||
void OpenMainTuningMenu();
|
||||
void OpenGroupMenu(const VehicleTuningGroup& group);
|
||||
std::string GetCurrentPartId(const std::string& group_id);
|
||||
bool GetPartState(const std::string& group_id, const std::string& part_id, std::string* state_text); // mounted?
|
||||
void CloseMenu();
|
||||
|
||||
void Reset();
|
||||
|
||||
@ -46,7 +52,12 @@ private:
|
||||
|
||||
game::RemoteMenu* menu_ = nullptr;
|
||||
|
||||
size_t main_menu_hover_ = 0;
|
||||
|
||||
VehicleTuning tuning_;
|
||||
VehicleTuning preview_tuning_;
|
||||
|
||||
RemoteMenuItem* mounted_part_menu_item_ = nullptr;
|
||||
|
||||
std::function<void()> exit_cb_;
|
||||
|
||||
|
||||
@ -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_];
|
||||
@ -606,7 +633,7 @@ game::VehiclePhysics::VehiclePhysics(collision::DynamicsWorld& world, Transform&
|
||||
body_ = std::make_unique<btRigidBody>(rb_info);
|
||||
// body_->setActivationState(DISABLE_DEACTIVATION);
|
||||
|
||||
collision::SetObjectInfo(body_.get(), collision::OT_ENTITY, collision::OF_NOTIFY_CONTACT, &obj_cb);
|
||||
collision::SetObjectInfo(body_.get(), collision::OT_ENTITY, collision::OF_NOTIFY_CONTACT | collision::OF_DESTRUCTING, &obj_cb);
|
||||
|
||||
// setup vehicle
|
||||
btRaycastVehicle::btVehicleTuning bt_tuning;
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -108,8 +108,17 @@ struct UseTargetAabbCallback : public btBroadphaseAabbCallback
|
||||
const game::UseTarget* best_target = nullptr;
|
||||
float best_dist = std::numeric_limits<float>::max();
|
||||
game::UseTargetQueryResult& best_res;
|
||||
float radius = 2.0f;
|
||||
|
||||
UseTargetAabbCallback(game::PlayerCharacter& character, game::UseTargetQueryResult& res) : character(character), pos(character.GetRoot().GetGlobalPosition()), best_res(res) {}
|
||||
UseTargetAabbCallback(game::PlayerCharacter& character, game::UseTargetQueryResult& res) : character(character), pos(character.GetRoot().GetGlobalPosition()), best_res(res)
|
||||
{
|
||||
auto vehicle = character.GetVehicle();
|
||||
if (vehicle)
|
||||
{
|
||||
radius = 5.0f;
|
||||
pos = vehicle->GetRoot().GetGlobalPosition();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool process(const btBroadphaseProxy* proxy)
|
||||
{
|
||||
@ -134,7 +143,7 @@ struct UseTargetAabbCallback : public btBroadphaseAabbCallback
|
||||
glm::vec3 pos_world = matrix * glm::vec4(target.position, 1.0f);
|
||||
|
||||
float dist = glm::distance(pos, pos_world);
|
||||
if (dist < 2.0f && dist < best_dist)
|
||||
if (dist < radius && dist < best_dist)
|
||||
{
|
||||
game::UseTargetQueryResult res{};
|
||||
if (!usable->QueryUseTarget(character, target.id, res))
|
||||
@ -189,6 +198,14 @@ void game::World::HandleContacts()
|
||||
|
||||
if (type == collision::OT_MAP_OBJECT && (flags & collision::OF_DESTRUCTIBLE))
|
||||
{
|
||||
collision::ObjectType other_type;
|
||||
collision::ObjectFlags other_flags;
|
||||
collision::ObjectCallback* other_cb;
|
||||
collision::GetObjectInfo(other_body, other_type, other_flags, other_cb);
|
||||
|
||||
if ((other_flags & collision::OF_DESTRUCTING) == 0)
|
||||
return;
|
||||
|
||||
auto col = dynamic_cast<MapObjectCollision*>(cb);
|
||||
if (!col)
|
||||
return;
|
||||
|
||||
@ -51,7 +51,7 @@ bool game::view::MarkerView::Init(net::InMessage& msg)
|
||||
|
||||
if (marker_type_ == MARKER_FOOT || marker_type_ == MARKER_VEHICLE)
|
||||
{
|
||||
model_ = assets::CacheManager::GetModel("data/marker.mdl");
|
||||
model_ = assets::CacheManager::GetModel("data/marker_tuning.mdl");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@ -23,8 +23,9 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MenuTitle title;
|
||||
net::MenuItemCount itemcount;
|
||||
net::MenuItemId hovered_idx;
|
||||
|
||||
if (!msg.Read(title) || !msg.Read(itemcount))
|
||||
if (!msg.Read(title) || !msg.Read(itemcount) || !msg.Read(hovered_idx))
|
||||
return false;
|
||||
|
||||
SetTitle(title);
|
||||
@ -44,6 +45,7 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
||||
case RM_BUTTON:
|
||||
{
|
||||
auto& btn = Add<gui::ButtonMenuItem>(text);
|
||||
btn.SetText2(selection);
|
||||
btn.SetClickCallback([this, i] { OnItemClick(i); });
|
||||
break;
|
||||
}
|
||||
@ -63,6 +65,7 @@ bool game::view::RemoteMenuView::ProcessUpdateMsg(net::InMessage& msg)
|
||||
|
||||
}
|
||||
|
||||
SetFocusedItemIndex(hovered_idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -105,11 +108,18 @@ bool game::view::RemoteMenuView::ProcessItemUpdateSelectionMsg(net::InMessage& m
|
||||
|
||||
auto& item = GetItem(idx);
|
||||
|
||||
auto select = dynamic_cast<gui::SelectMenuItem*>(&item);
|
||||
if (!select)
|
||||
return false;
|
||||
if (auto select = dynamic_cast<gui::SelectMenuItem*>(&item); select)
|
||||
{
|
||||
select->SetSelectionText(selection);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto btn = dynamic_cast<gui::ButtonMenuItem*>(&item); btn)
|
||||
{
|
||||
btn->SetText2(selection);
|
||||
return true;
|
||||
}
|
||||
|
||||
select->SetSelectionText(selection);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -135,6 +145,11 @@ void game::view::RemoteMenuView::OnFocusChanged()
|
||||
msg.Write<net::MenuItemId>(GetFocusedItemIndex());
|
||||
}
|
||||
|
||||
void game::view::RemoteMenuView::OnExit()
|
||||
{
|
||||
BeginActionMsg(net::MA_EXIT);
|
||||
}
|
||||
|
||||
net::OutMessage game::view::RemoteMenuView::BeginActionMsg(net::MenuActionType type)
|
||||
{
|
||||
auto msg = session_.BeginMsg(net::MSG_MENUACTION);
|
||||
|
||||
@ -32,6 +32,7 @@ private:
|
||||
|
||||
protected:
|
||||
void OnFocusChanged() override;
|
||||
void OnExit() override;
|
||||
|
||||
net::OutMessage BeginActionMsg(net::MenuActionType type);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@ -3,7 +3,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "assets/skeleton.hpp"
|
||||
#include "light_cache.hpp"
|
||||
#include "surface.hpp"
|
||||
#include "surface_render_flags.hpp"
|
||||
#include "uniform_buffer.hpp"
|
||||
|
||||
namespace gfx
|
||||
@ -12,12 +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
|
||||
@ -29,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)
|
||||
{
|
||||
}
|
||||
@ -47,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)
|
||||
@ -64,6 +87,7 @@ struct DrawList
|
||||
void Clear()
|
||||
{
|
||||
surfaces.clear();
|
||||
lights.clear();
|
||||
beams.clear();
|
||||
huds.clear();
|
||||
}
|
||||
|
||||
26
src/gfx/light_cache.hpp
Normal file
26
src/gfx/light_cache.hpp
Normal 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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@ -14,9 +14,6 @@
|
||||
|
||||
gfx::Renderer::Renderer()
|
||||
{
|
||||
ShaderSources::MakeShader(mesh_shader_.shader, SS_MESH_VERT, SS_MESH_FRAG);
|
||||
ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG);
|
||||
ShaderSources::MakeShader(deform_mesh_shader_.shader, SS_DEFORM_MESH_VERT, SS_DEFORM_MESH_FRAG);
|
||||
ShaderSources::MakeShader(solid_shader_, SS_SOLID_VERT, SS_SOLID_FRAG);
|
||||
ShaderSources::MakeShader(hud_shader_, SS_HUD_VERT, SS_HUD_FRAG);
|
||||
ShaderSources::MakeShader(beam_shader_, SS_BEAM_VERT, SS_BEAM_FRAG);
|
||||
@ -26,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);
|
||||
@ -89,20 +89,32 @@ void gfx::Renderer::SetupBeamVA()
|
||||
|
||||
void gfx::Renderer::InvalidateShaders()
|
||||
{
|
||||
InvalidateMeshShader(mesh_shader_);
|
||||
InvalidateMeshShader(skel_mesh_shader_);
|
||||
InvalidateMeshShader(deform_mesh_shader_);
|
||||
// invalidate surface shaders
|
||||
for (auto& [flags, sshader] : surface_shaders_)
|
||||
{
|
||||
InvalidateSurfaceShader(sshader);
|
||||
}
|
||||
}
|
||||
|
||||
void gfx::Renderer::InvalidateMeshShader(MeshShader& mshader)
|
||||
gfx::SurfaceShader& gfx::Renderer::GetSurfaceShader(SurfaceRenderFlags flags)
|
||||
{
|
||||
mshader.global_setup = false;
|
||||
mshader.color = glm::vec4(-1.0f); // invalidate color
|
||||
auto it = surface_shaders_.find(flags);
|
||||
|
||||
// not yet generated
|
||||
if (it == surface_shaders_.end())
|
||||
{
|
||||
SurfaceShader& sshader = surface_shaders_[flags];
|
||||
sshader.shader = CreateSurfaceShader(flags, sshader.iflags);
|
||||
|
||||
return sshader;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& params)
|
||||
void gfx::Renderer::SetupSurfaceShader(SurfaceShader& sshader, const DrawListParams& params)
|
||||
{
|
||||
const Shader& shader = *mshader.shader;
|
||||
const Shader& shader = *sshader.shader;
|
||||
|
||||
if (current_shader_ != &shader)
|
||||
{
|
||||
@ -110,7 +122,7 @@ void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& p
|
||||
current_shader_ = &shader;
|
||||
}
|
||||
|
||||
if (mshader.global_setup)
|
||||
if (sshader.global_setup)
|
||||
{
|
||||
return; // Global uniforms are already set up
|
||||
}
|
||||
@ -118,23 +130,107 @@ void gfx::Renderer::SetupMeshShader(MeshShader& mshader, const DrawListParams& p
|
||||
glUniformMatrix4fv(shader.U(gfx::SU_VIEW_PROJ), 1, GL_FALSE, ¶ms.view_proj[0][0]);
|
||||
|
||||
// setup lighting
|
||||
glUniform3fv(shader.U(gfx::SU_AMBIENT_LIGHT), 1, ¶ms.env.ambient_light[0]);
|
||||
glUniform3fv(shader.U(gfx::SU_SUN_COLOR), 1, ¶ms.env.sun_color[0]);
|
||||
glUniform3fv(shader.U(gfx::SU_SUN_DIRECTION), 1, ¶ms.env.sun_direction[0]);
|
||||
glUniform4fv(shader.U(gfx::SU_FOG), 1, ¶ms.env.fog[0]);
|
||||
if (sshader.iflags & SIF_LIGHTING_DATA)
|
||||
{
|
||||
glUniform3fv(shader.U(gfx::SU_AMBIENT_LIGHT), 1, ¶ms.env.ambient_light[0]);
|
||||
glUniform3fv(shader.U(gfx::SU_SUN_COLOR), 1, ¶ms.env.sun_color[0]);
|
||||
glUniform3fv(shader.U(gfx::SU_SUN_DIRECTION), 1, ¶ms.env.sun_direction[0]);
|
||||
// glUniform4fv(shader.U(gfx::SU_FOG), 1, ¶ms.env.fog[0]);
|
||||
}
|
||||
|
||||
sshader.global_setup = true;
|
||||
}
|
||||
|
||||
void gfx::Renderer::InvalidateSurfaceShader(SurfaceShader& sshader)
|
||||
{
|
||||
sshader.global_setup = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mshader.global_setup = true;
|
||||
}
|
||||
|
||||
void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawListParams& params)
|
||||
{
|
||||
// determine render flags
|
||||
for (auto& cmd : list)
|
||||
{
|
||||
if (cmd.surface->sflags & SF_BLEND)
|
||||
cmd.rflags |= SRF_BLEND;
|
||||
|
||||
if (cmd.surface->texture)
|
||||
cmd.rflags |= SRF_TEXTURE;
|
||||
|
||||
if ((cmd.surface->mflags & MF_SKELETAL) && cmd.skinning)
|
||||
cmd.rflags |= SRF_SKELETAL;
|
||||
|
||||
if ((cmd.surface->sflags & SF_DEFORM_GRID) && cmd.surface->deform_tex)
|
||||
cmd.rflags |= SRF_DEFORM;
|
||||
|
||||
if ((cmd.surface->sflags & SF_UNLIT) == 0)
|
||||
cmd.rflags |= SRF_LIT;
|
||||
|
||||
if (cmd.color)
|
||||
{
|
||||
if (cmd.surface->sflags & SF_OBJECT_COLOR_MULT)
|
||||
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)
|
||||
cmd.rflags |= SRF_2SIDED;
|
||||
|
||||
if (cmd.surface->sflags & SF_BLEND_ADDITIVE)
|
||||
cmd.rflags |= SRF_BLEND_ADDITIVE;
|
||||
|
||||
if ((cmd.surface->sflags & (SF_BLEND | SF_OBJECT_COLOR)) == 0)
|
||||
cmd.rflags |= SRF_CULL_ALPHA;
|
||||
}
|
||||
|
||||
// sort the list to minimize state changes
|
||||
std::ranges::sort(list, [](const DrawSurfaceCmd& a, const DrawSurfaceCmd& b) {
|
||||
const Surface* sa = a.surface;
|
||||
const Surface* sb = b.surface;
|
||||
|
||||
const bool blend_a = sa->sflags & SF_BLEND;
|
||||
const bool blend_b = sb->sflags & SF_BLEND;
|
||||
const bool blend_a = a.rflags & SRF_BLEND;
|
||||
const bool blend_b = b.rflags & SRF_BLEND;
|
||||
|
||||
if (blend_a != blend_b)
|
||||
return blend_b; // opaque first
|
||||
@ -144,9 +240,15 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
return a.dist > b.dist; // do not optimize blended, sort by distance instead
|
||||
}
|
||||
|
||||
if (sa == sb)
|
||||
if (a.surface == b.surface)
|
||||
return false;
|
||||
|
||||
if (auto cmp = a.rflags <=> b.rflags; cmp != 0)
|
||||
return cmp < 0;
|
||||
|
||||
const auto sa = a.surface;
|
||||
const auto sb = a.surface;
|
||||
|
||||
if (auto cmp = sa->texture <=> sb->texture; cmp != 0)
|
||||
return cmp < 0;
|
||||
|
||||
@ -162,12 +264,15 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
glActiveTexture(GL_TEXTURE0); // for all future bindings
|
||||
|
||||
// cache to eliminate fake state changes
|
||||
SurfaceShader* sshader = nullptr;
|
||||
const gfx::Texture* last_texture = nullptr;
|
||||
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();
|
||||
|
||||
|
||||
// enable depth test
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
@ -175,80 +280,73 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
// reset face culling
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
bool last_twosided = false;
|
||||
|
||||
// reset blending
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
bool last_blend = false;
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // set to opacity blending default
|
||||
bool last_blend_additive = false;
|
||||
|
||||
SurfaceRenderFlags last_rflags = 0;
|
||||
|
||||
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 = surface->sflags & SF_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
|
||||
if (last_twosided != twosided_flag)
|
||||
if (rflags_diff & SRF_2SIDED)
|
||||
{
|
||||
if (twosided_flag)
|
||||
if (cmd.rflags & SRF_2SIDED)
|
||||
glDisable(GL_CULL_FACE);
|
||||
else
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
last_twosided = twosided_flag;
|
||||
}
|
||||
|
||||
// select shader
|
||||
MeshShader& mshader = skeletal_flag ? skel_mesh_shader_ : (deform_flag ? deform_mesh_shader_ : mesh_shader_);
|
||||
SetupMeshShader(mshader, params);
|
||||
// setup shader
|
||||
SurfaceRenderFlags last_shader_rflags = last_rflags & SRF__SHADER;
|
||||
SurfaceRenderFlags shader_rflags = cmd.rflags & SRF__SHADER;
|
||||
|
||||
if (last_shader_rflags != shader_rflags || !sshader)
|
||||
{
|
||||
sshader = &GetSurfaceShader(shader_rflags);
|
||||
SetupSurfaceShader(*sshader, params);
|
||||
}
|
||||
|
||||
auto shader = sshader->shader.get();
|
||||
|
||||
static const glm::mat4 identity(1.0f);
|
||||
const glm::mat4* model = &identity;
|
||||
|
||||
// set model matrix
|
||||
if (cmd.matrices)
|
||||
{
|
||||
glUniformMatrix4fv(mshader.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(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]);
|
||||
model = &cmd.matrices[0];
|
||||
}
|
||||
|
||||
// set color
|
||||
int shflags = SHF_CULL_ALPHA;
|
||||
glUniformMatrix4fv(shader->U(SU_MODEL), 1, GL_FALSE, &(*model)[0][0]);
|
||||
|
||||
glm::vec4 color = glm::vec4(1.0f);
|
||||
if (object_color_flag && cmd.color)
|
||||
// sync color
|
||||
if (sshader->iflags & SIF_OBJECT_COLOR)
|
||||
{
|
||||
// use object color and disable alpha cull
|
||||
|
||||
if (!object_color_mult_flag)
|
||||
if (sshader->color != cmd.color)
|
||||
{
|
||||
shflags &= ~SHF_CULL_ALPHA;
|
||||
shflags |= SHF_BACKGROUND;
|
||||
glUniform4fv(shader->U(SU_COLOR), 1, &(*cmd.color)[0]);
|
||||
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;
|
||||
}
|
||||
|
||||
color = glm::vec4(*cmd.color);
|
||||
}
|
||||
|
||||
// check unlit
|
||||
if (unlit_flag)
|
||||
shflags |= SHF_UNLIT;
|
||||
|
||||
// sync blending
|
||||
if (blend_flag != last_blend)
|
||||
if (rflags_diff & SRF_BLEND)
|
||||
{
|
||||
if (blend_flag)
|
||||
if (cmd.rflags & SRF_BLEND)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glDepthMask(GL_FALSE);
|
||||
@ -258,43 +356,50 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
glDisable(GL_BLEND);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
last_blend = blend_flag;
|
||||
}
|
||||
|
||||
// sync blending type
|
||||
if (blend_flag)
|
||||
if ((cmd.rflags & SRF_BLEND) && (rflags_diff & SRF_BLEND_ADDITIVE))
|
||||
{
|
||||
shflags &= ~SHF_CULL_ALPHA;
|
||||
if (cmd.rflags & SRF_BLEND_ADDITIVE)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
else
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
const bool blend_additive = surface->sflags & SF_BLEND_ADDITIVE;
|
||||
if (blend_additive != last_blend_additive)
|
||||
// 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())
|
||||
{
|
||||
if (blend_additive)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
else
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
numlights = it->second.num_lights;
|
||||
}
|
||||
|
||||
last_blend_additive = blend_additive;
|
||||
}
|
||||
|
||||
// sync cull_alpha
|
||||
if (mshader.flags != shflags)
|
||||
{
|
||||
glUniform1i(mshader.shader->U(SU_FLAGS), shflags);
|
||||
mshader.flags = shflags;
|
||||
}
|
||||
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]);
|
||||
}
|
||||
|
||||
// sync color
|
||||
if (mshader.color != color)
|
||||
{
|
||||
glUniform4fv(mshader.shader->U(SU_COLOR), 1, &color[0]);
|
||||
mshader.color = color;
|
||||
last_numlights = numlights;
|
||||
}
|
||||
|
||||
// bind texture
|
||||
if (last_texture != surface->texture.get())
|
||||
if ((sshader->iflags & SIF_COLOR_TEXTURE) && last_texture != surface->texture.get())
|
||||
{
|
||||
GLuint tex_id = surface->texture ? surface->texture->GetId() : 0;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
@ -303,14 +408,14 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
}
|
||||
|
||||
// bind skinning UBO
|
||||
if (cmd.skinning && last_skin != cmd.skinning)
|
||||
if ((sshader->iflags & SIF_SKELETAL_DATA) && last_skin != cmd.skinning)
|
||||
{
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, cmd.skinning->GetId());
|
||||
last_skin = cmd.skinning;
|
||||
}
|
||||
|
||||
// bind deform texture
|
||||
if (deform_flag && surface->deform_tex.get() != last_deform)
|
||||
if ((sshader->iflags & SIF_DEFORM_DATA) && surface->deform_tex.get() != last_deform)
|
||||
{
|
||||
const auto& deform_tex = *surface->deform_tex;
|
||||
GLuint tex_id = deform_tex.GetId();
|
||||
@ -324,7 +429,7 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
deform_info_mat[0] = deform_info.min;
|
||||
deform_info_mat[1] = deform_info.max;
|
||||
deform_info_mat[2] = glm::vec3(deform_info.max_offset, 0.0f, 0.0f);
|
||||
glUniformMatrix3fv(mshader.shader->U(SU_DEFORM_INFO), 1, GL_FALSE, &deform_info_mat[0][0]);
|
||||
glUniformMatrix3fv(shader->U(SU_DEFORM_INFO), 1, GL_FALSE, &deform_info_mat[0][0]);
|
||||
}
|
||||
|
||||
// bind VAO
|
||||
@ -340,6 +445,8 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
// draw
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(num_tris * 3U), GL_UNSIGNED_INT,
|
||||
(void*)(first_tri * 3U * sizeof(GLuint)));
|
||||
|
||||
last_rflags = cmd.rflags;
|
||||
}
|
||||
|
||||
// reset this as it is rare and other stuff might not reset this
|
||||
@ -484,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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include "draw_list.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "surface_shader.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
@ -27,14 +28,14 @@ struct DrawListParams
|
||||
size_t screen_height = 0;
|
||||
};
|
||||
|
||||
struct MeshShader
|
||||
struct SurfaceShader
|
||||
{
|
||||
std::unique_ptr<Shader> shader;
|
||||
SurfaceShaderInputFlags iflags;
|
||||
|
||||
// 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
|
||||
int flags = 0;
|
||||
const glm::vec4* color = nullptr;
|
||||
};
|
||||
|
||||
class Renderer
|
||||
@ -47,17 +48,19 @@ private:
|
||||
void SetupBeamVA();
|
||||
|
||||
void InvalidateShaders();
|
||||
void InvalidateMeshShader(MeshShader& mshader);
|
||||
void SetupMeshShader(MeshShader& mshader, const DrawListParams& params);
|
||||
|
||||
SurfaceShader& GetSurfaceShader(SurfaceRenderFlags flags);
|
||||
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);
|
||||
|
||||
private:
|
||||
MeshShader mesh_shader_;
|
||||
MeshShader skel_mesh_shader_;
|
||||
MeshShader deform_mesh_shader_;
|
||||
std::map<SurfaceRenderFlags, SurfaceShader> surface_shaders_;
|
||||
std::unique_ptr<Shader> solid_shader_;
|
||||
|
||||
std::unique_ptr<BufferObject> beam_segments_vbo_;
|
||||
@ -67,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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
28
src/gfx/shader_common.hpp
Normal file
28
src/gfx/shader_common.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "shader_defs.hpp"
|
||||
#include "client/gl.hpp"
|
||||
|
||||
#ifndef PG_GLES
|
||||
#define GLSL_VERSION \
|
||||
"#version 330 core\n" \
|
||||
"\n"
|
||||
#else
|
||||
#define GLSL_VERSION \
|
||||
"#version 300 es\n" \
|
||||
"precision highp float;\n" \
|
||||
"\n"
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_HELPER(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||
|
||||
#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 \
|
||||
GLSL_VERSION \
|
||||
SHADER_DEFS
|
||||
@ -1,8 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define SD_MAX_LIGHTS 4
|
||||
#define SD_MAX_LIGHTS 8
|
||||
#define SD_MAX_BONES 128
|
||||
|
||||
#define SHF_CULL_ALPHA 1
|
||||
#define SHF_BACKGROUND 2
|
||||
#define SHF_UNLIT 4
|
||||
#define SD_MAX_COLORS 8
|
||||
@ -1,304 +1,9 @@
|
||||
#include "shader_sources.hpp"
|
||||
#include "shader_defs.hpp"
|
||||
#include "client/gl.hpp"
|
||||
|
||||
#ifndef PG_GLES
|
||||
#define GLSL_VERSION \
|
||||
"#version 330 core\n" \
|
||||
"\n"
|
||||
#else
|
||||
#define GLSL_VERSION \
|
||||
"#version 300 es\n" \
|
||||
"precision highp float;\n" \
|
||||
"\n"
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_HELPER(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||
|
||||
#define SHADER_DEFS \
|
||||
"#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \
|
||||
"#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \
|
||||
"#define SHF_CULL_ALPHA " STRINGIFY(SHF_CULL_ALPHA) "\n" \
|
||||
"#define SHF_BACKGROUND " STRINGIFY(SHF_BACKGROUND) "\n" \
|
||||
"#define SHF_UNLIT " STRINGIFY(SHF_UNLIT) "\n" \
|
||||
"\n"
|
||||
|
||||
#define SHADER_HEADER \
|
||||
GLSL_VERSION \
|
||||
SHADER_DEFS
|
||||
|
||||
#define MESH_MATRICES_GLSL R"GLSL(
|
||||
uniform mat4 u_view_proj;
|
||||
uniform mat4 u_model; // Transform matrix
|
||||
)GLSL"
|
||||
|
||||
#define LIGHT_MATRICES_GLSL R"GLSL(
|
||||
uniform vec3 u_ambient_light;
|
||||
uniform vec3 u_sun_direction;
|
||||
uniform vec3 u_sun_color;
|
||||
uniform vec4 u_fog;
|
||||
|
||||
uniform int u_flags;
|
||||
|
||||
uniform int u_num_lights;
|
||||
uniform vec3 u_light_positions[MAX_LIGHTS];
|
||||
uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius
|
||||
)GLSL"
|
||||
|
||||
#define COMPUTE_LIGHTS_GLSL R"GLSL(
|
||||
// Example sun values (can later be uniforms)
|
||||
//vec3 u_sun_direction = normalize(vec3(0.3, 0.5, -0.8)); // direction from which sunlight comes
|
||||
//vec3 u_sun_color = vec3(1.0, 0.95, 0.7) * 0.9; // warm sunlight color
|
||||
|
||||
//uniform vec3 u_ambient_light;
|
||||
|
||||
vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal)
|
||||
{
|
||||
if ((u_flags & SHF_UNLIT) > 0)
|
||||
return vec3(1.0);
|
||||
|
||||
// Base ambient
|
||||
vec3 color = u_ambient_light; //vec3(0.5, 0.5, 0.5) * 0.9; // u_ambient_light
|
||||
|
||||
// Sunlight contribution
|
||||
float sun_dot = max(dot(sector_normal, -u_sun_direction), 0.0);
|
||||
color += u_sun_color * sun_dot;
|
||||
|
||||
// Point lights
|
||||
for (int i = 0; i < u_num_lights; ++i) {
|
||||
vec3 light_pos = u_light_positions[i];
|
||||
vec3 light_color = u_light_colors_rs[i].rgb;
|
||||
float light_radius = u_light_colors_rs[i].a;
|
||||
|
||||
vec3 to_light = light_pos - sector_pos;
|
||||
float dist2 = dot(to_light, to_light);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
)GLSL"
|
||||
#include "shader_common.hpp"
|
||||
|
||||
// Zdrojove kody shaderu
|
||||
static const char* const s_srcs[] = {
|
||||
|
||||
// SS_MESH_VERT
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 2) in vec4 a_color;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
|
||||
)GLSL"
|
||||
MESH_MATRICES_GLSL
|
||||
LIGHT_MATRICES_GLSL
|
||||
COMPUTE_LIGHTS_GLSL
|
||||
R"GLSL(
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_color;
|
||||
|
||||
void main() {
|
||||
vec4 world_pos = u_model * vec4(a_pos, 1.0);
|
||||
vec3 world_normal = normalize(mat3(u_model) * a_normal);
|
||||
gl_Position = u_view_proj * world_pos;
|
||||
|
||||
v_uv = a_uv;
|
||||
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
// SS_MESH_FRAG
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in vec2 v_uv;
|
||||
in vec3 v_color;
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
uniform vec4 u_color;
|
||||
uniform int u_flags;
|
||||
|
||||
layout (location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = vec4(texture(u_tex, v_uv));
|
||||
|
||||
if ((u_flags & SHF_BACKGROUND) > 0)
|
||||
{
|
||||
o_color = mix(u_color, o_color, o_color.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
o_color *= u_color;
|
||||
}
|
||||
|
||||
if ((u_flags & SHF_CULL_ALPHA) > 0)
|
||||
{
|
||||
if (o_color.a < 0.5)
|
||||
discard;
|
||||
}
|
||||
|
||||
o_color.rgb *= v_color; // Apply vertex color
|
||||
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
)GLSL",
|
||||
|
||||
// SS_SKEL_MESH_VERT
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 2) in vec4 a_color;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
layout (location = 5) in ivec4 a_bone_ids;
|
||||
layout (location = 6) in vec4 a_bone_weights;
|
||||
|
||||
layout (std140) uniform Bones {
|
||||
mat4 u_bone_matrices[MAX_BONES];
|
||||
};
|
||||
|
||||
)GLSL"
|
||||
MESH_MATRICES_GLSL
|
||||
LIGHT_MATRICES_GLSL
|
||||
COMPUTE_LIGHTS_GLSL
|
||||
R"GLSL(
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_color;
|
||||
|
||||
void main() {
|
||||
mat4 bone_transform = mat4(0.0);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int bone_id = a_bone_ids[i];
|
||||
if (bone_id >= 0) {
|
||||
bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
vec4 world_pos = bone_transform * vec4(a_pos, 1.0);
|
||||
vec3 world_normal = normalize(mat3(bone_transform) * a_normal);
|
||||
gl_Position = u_view_proj * world_pos;
|
||||
|
||||
v_uv = a_uv;
|
||||
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
// SS_SKEL_MESH_FRAG
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in vec2 v_uv;
|
||||
in vec3 v_color;
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
uniform vec4 u_color;
|
||||
uniform int u_flags;
|
||||
|
||||
layout (location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = vec4(texture(u_tex, v_uv));
|
||||
|
||||
if ((u_flags & SHF_BACKGROUND) > 0)
|
||||
{
|
||||
o_color = mix(u_color, o_color, o_color.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
o_color *= u_color;
|
||||
}
|
||||
|
||||
if ((u_flags & SHF_CULL_ALPHA) > 0)
|
||||
{
|
||||
if (o_color.a < 0.5)
|
||||
discard;
|
||||
}
|
||||
|
||||
o_color.rgb *= v_color; // Apply vertex color
|
||||
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
)GLSL",
|
||||
|
||||
// SS_DEFORM_MESH_VERT
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 2) in vec4 a_color;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
|
||||
)GLSL"
|
||||
MESH_MATRICES_GLSL
|
||||
LIGHT_MATRICES_GLSL
|
||||
COMPUTE_LIGHTS_GLSL
|
||||
R"GLSL(
|
||||
|
||||
uniform mediump sampler3D u_deform_tex;
|
||||
uniform mat3 u_deform_info;
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec3 v_color;
|
||||
|
||||
void main() {
|
||||
vec3 deform_pos = (a_pos - u_deform_info[0]) / (u_deform_info[1] - u_deform_info[0]);
|
||||
vec3 pos = a_pos + texture(u_deform_tex, deform_pos).xyz * u_deform_info[2].x;
|
||||
//vec3 pos = a_pos + u_deform_info[0] * u_deform_info[2].x;
|
||||
//vec3 pos = a_pos + vec3(0.0, 0.0, 1.0);
|
||||
|
||||
vec4 world_pos = u_model * vec4(pos, 1.0);
|
||||
vec3 world_normal = normalize(mat3(u_model) * a_normal);
|
||||
gl_Position = u_view_proj * world_pos;
|
||||
|
||||
v_uv = a_uv;
|
||||
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
// SS_DEFORM_MESH_FRAG
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in vec2 v_uv;
|
||||
in vec3 v_color;
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
uniform vec4 u_color;
|
||||
uniform int u_flags;
|
||||
|
||||
layout (location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = vec4(texture(u_tex, v_uv));
|
||||
|
||||
if ((u_flags & SHF_BACKGROUND) > 0)
|
||||
{
|
||||
o_color = mix(u_color, o_color, o_color.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
o_color *= u_color;
|
||||
}
|
||||
|
||||
if ((u_flags & SHF_CULL_ALPHA) > 0)
|
||||
{
|
||||
if (o_color.a < 0.5)
|
||||
discard;
|
||||
}
|
||||
|
||||
o_color.rgb *= v_color; // Apply vertex color
|
||||
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
)GLSL",
|
||||
|
||||
// SS_SOLID_VERT
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
|
||||
@ -7,15 +7,6 @@ namespace gfx
|
||||
{
|
||||
enum ShaderSource
|
||||
{
|
||||
SS_MESH_VERT,
|
||||
SS_MESH_FRAG,
|
||||
|
||||
SS_SKEL_MESH_VERT,
|
||||
SS_SKEL_MESH_FRAG,
|
||||
|
||||
SS_DEFORM_MESH_VERT,
|
||||
SS_DEFORM_MESH_FRAG,
|
||||
|
||||
SS_SOLID_VERT,
|
||||
SS_SOLID_FRAG,
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
33
src/gfx/surface_render_flags.hpp
Normal file
33
src/gfx/surface_render_flags.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
using SurfaceRenderFlags = uint16_t;
|
||||
|
||||
enum SurfaceRenderFlag : SurfaceRenderFlags
|
||||
{
|
||||
// dont require shader switch
|
||||
SRF_BLEND_ADDITIVE = 1,
|
||||
SRF_2SIDED = 2,
|
||||
|
||||
// require shader switch
|
||||
SRF_CULL_ALPHA = 4,
|
||||
SRF_OBJECT_COLOR = 8,
|
||||
SRF_OBJECT_COLOR_BACKGROUND = 16,
|
||||
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_MULTICOLOR | SRF_LIT | SRF_SKELETAL | SRF_DEFORM | SRF_TEXTURE,
|
||||
|
||||
// order affects visual result
|
||||
SRF_BLEND = 1024,
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
218
src/gfx/surface_shader.cpp
Normal file
218
src/gfx/surface_shader.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
#include "surface_shader.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "shader_common.hpp"
|
||||
|
||||
std::unique_ptr<gfx::Shader> gfx::CreateSurfaceShader(SurfaceRenderFlags flags, SurfaceShaderInputFlags& input_flags)
|
||||
{
|
||||
std::string vert_attributes, vert_uniforms, vert_outs, vert_funcs, vert_pos_calc, vert_main;
|
||||
std::string frag_ins, frag_uniforms, frag_funcs, frag_main;
|
||||
|
||||
// default unlit untextured skeleton
|
||||
|
||||
vert_attributes = R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 2) in vec4 a_color;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
)GLSL";
|
||||
|
||||
vert_uniforms = R"GLSL(
|
||||
uniform mat4 u_view_proj;
|
||||
uniform mat4 u_model;
|
||||
)GLSL";
|
||||
|
||||
vert_outs = R"GLSL(
|
||||
out vec3 v_color;
|
||||
)GLSL";
|
||||
|
||||
vert_pos_calc = R"GLSL(
|
||||
vec4 world_pos = u_model * vec4(a_pos, 1.0);
|
||||
vec3 world_normal = normalize(mat3(u_model) * a_normal);
|
||||
)GLSL";
|
||||
|
||||
vert_main = R"GLSL(
|
||||
gl_Position = u_view_proj * world_pos;
|
||||
v_color = a_color.rgb;
|
||||
)GLSL";
|
||||
|
||||
frag_ins = R"GLSL(
|
||||
in vec3 v_color;
|
||||
)GLSL";
|
||||
|
||||
frag_main = R"GLSL(
|
||||
o_color = vec4(1.0);
|
||||
)GLSL";
|
||||
|
||||
input_flags = 0;
|
||||
|
||||
// color texture
|
||||
if (flags & SRF_TEXTURE)
|
||||
{
|
||||
vert_outs += "out vec2 v_uv;\n";
|
||||
vert_main += "v_uv = a_uv;\n";
|
||||
|
||||
frag_ins += "in vec2 v_uv;\n";
|
||||
frag_uniforms += "uniform sampler2D u_tex;\n";
|
||||
frag_main += "o_color *= texture(u_tex, v_uv);\n";
|
||||
|
||||
input_flags |= SIF_COLOR_TEXTURE;
|
||||
}
|
||||
|
||||
// any object color type
|
||||
if (flags & (SRF_OBJECT_COLOR | SRF_OBJECT_COLOR_BACKGROUND))
|
||||
{
|
||||
frag_uniforms += "uniform vec4 u_color;\n";
|
||||
|
||||
if (flags & SRF_OBJECT_COLOR_BACKGROUND)
|
||||
{
|
||||
frag_main += "o_color = mix(u_color, o_color, o_color.a);\n";
|
||||
}
|
||||
else // just multiply
|
||||
{
|
||||
frag_main += "o_color *= u_color;\n";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
frag_main += "if (o_color.a < 0.5) discard;\n";
|
||||
}
|
||||
|
||||
// lighting
|
||||
if (flags & SRF_LIT)
|
||||
{
|
||||
vert_uniforms += R"GLSL(
|
||||
// global
|
||||
uniform vec3 u_ambient_light;
|
||||
uniform vec3 u_sun_direction;
|
||||
uniform vec3 u_sun_color;
|
||||
|
||||
// local
|
||||
uniform int u_num_lights;
|
||||
uniform vec3 u_light_positions[MAX_LIGHTS];
|
||||
uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius
|
||||
)GLSL";
|
||||
|
||||
vert_funcs += R"GLSL(
|
||||
vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal)
|
||||
{
|
||||
// Base ambient
|
||||
vec3 color = u_ambient_light;
|
||||
|
||||
// Sunlight contribution
|
||||
float sun_dot = max(dot(sector_normal, -u_sun_direction), 0.0);
|
||||
color += u_sun_color * sun_dot;
|
||||
|
||||
// Point lights
|
||||
for (int i = 0; i < u_num_lights; ++i) {
|
||||
vec3 light_pos = u_light_positions[i];
|
||||
vec3 light_color = u_light_colors_rs[i].rgb;
|
||||
float light_radius = u_light_colors_rs[i].a;
|
||||
|
||||
vec3 to_light = light_pos - sector_pos;
|
||||
float dist2 = dot(to_light, to_light);
|
||||
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 * attenuation;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
vert_main += "v_color *= ComputeLights(world_pos.xyz, world_normal);\n";
|
||||
|
||||
input_flags |= SIF_LIGHTING_DATA;
|
||||
}
|
||||
|
||||
if (flags & SRF_SKELETAL) // skeletal deform
|
||||
{
|
||||
vert_attributes += R"GLSL(
|
||||
layout (location = 5) in ivec4 a_bone_ids;
|
||||
layout (location = 6) in vec4 a_bone_weights;
|
||||
)GLSL";
|
||||
|
||||
vert_uniforms += R"GLSL(
|
||||
layout (std140) uniform Bones {
|
||||
mat4 u_bone_matrices[MAX_BONES];
|
||||
};
|
||||
)GLSL";
|
||||
|
||||
vert_pos_calc = R"GLSL(
|
||||
mat4 bone_transform = mat4(0.0);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int bone_id = a_bone_ids[i];
|
||||
if (bone_id >= 0) {
|
||||
bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
vec4 world_pos = bone_transform * vec4(a_pos, 1.0);
|
||||
vec3 world_normal = normalize(mat3(bone_transform) * a_normal);
|
||||
)GLSL";
|
||||
|
||||
input_flags |= SIF_SKELETAL_DATA;
|
||||
}
|
||||
else if (flags & SRF_DEFORM) // grid deform
|
||||
{
|
||||
vert_uniforms += R"GLSL(
|
||||
uniform mediump sampler3D u_deform_tex;
|
||||
uniform mat3 u_deform_info;
|
||||
)GLSL";
|
||||
|
||||
vert_pos_calc = R"GLSL(
|
||||
vec3 deform_pos = (a_pos - u_deform_info[0]) / (u_deform_info[1] - u_deform_info[0]);
|
||||
vec3 pos = a_pos + texture(u_deform_tex, deform_pos).xyz * u_deform_info[2].x;
|
||||
|
||||
vec4 world_pos = u_model * vec4(pos, 1.0);
|
||||
vec3 world_normal = normalize(mat3(u_model) * a_normal);
|
||||
)GLSL";
|
||||
|
||||
input_flags |= SIF_DEFORM_DATA;
|
||||
}
|
||||
|
||||
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";
|
||||
std::string fragment_src = SHADER_HEADER + frag_ins + frag_uniforms + "\nlayout (location = 0) out vec4 o_color;\n" + frag_funcs + "\nvoid main() {\n" + frag_main + "\n}\n";
|
||||
|
||||
return std::make_unique<Shader>(vertex_src.c_str(), fragment_src.c_str());
|
||||
}
|
||||
25
src/gfx/surface_shader.hpp
Normal file
25
src/gfx/surface_shader.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "shader.hpp"
|
||||
#include "surface_render_flags.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
|
||||
using SurfaceShaderInputFlags = uint8_t;
|
||||
|
||||
enum SurfaceShaderInputFlag : SurfaceShaderInputFlags
|
||||
{
|
||||
SIF_COLOR_TEXTURE = 1,
|
||||
SIF_OBJECT_COLOR = 2,
|
||||
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);
|
||||
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/files.hpp"
|
||||
#include "assets/cmdfile.hpp"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
@ -76,9 +77,27 @@ std::shared_ptr<gfx::Texture> gfx::Texture::LoadFromFile(const std::string& file
|
||||
throw std::runtime_error("Failed to load texture from file: " + filename);
|
||||
}
|
||||
|
||||
bool mipmaps = true;
|
||||
bool linear = false;
|
||||
|
||||
std::string config_path = filename + ".cfg";
|
||||
if (fs::FileExists(config_path))
|
||||
{
|
||||
assets::LoadCMDFile(config_path, [&](const std::string& command, std::istringstream& iss) {
|
||||
if (command == "nomipmaps")
|
||||
{
|
||||
mipmaps = false;
|
||||
}
|
||||
else if (command == "linear")
|
||||
{
|
||||
linear = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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, linear, mipmaps);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
stbi_image_free(data);
|
||||
|
||||
111
src/gui/menu.cpp
111
src/gui/menu.cpp
@ -10,6 +10,7 @@ void gui::Menu::Clear()
|
||||
{
|
||||
items_.clear();
|
||||
focus_ = 0;
|
||||
UpdateScroll();
|
||||
}
|
||||
|
||||
void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
||||
@ -23,15 +24,37 @@ void gui::Menu::Draw(Context& ctx, const glm::vec2& pos) const
|
||||
ctx.DrawRect(pos, pos + title_size, 0x55000000);
|
||||
ctx.DrawTextAligned(title_, pos + title_size * 0.5f, glm::vec2(-0.5f));
|
||||
|
||||
auto res_itemsize = itemsize_;
|
||||
|
||||
// draw scrollbar
|
||||
if (items_.size() > max_items_)
|
||||
{
|
||||
glm::vec2 scrollbar_size(5.0f, size.y - title_size.y);
|
||||
glm::vec2 scrollbar_pos(pos.x + size.x - scrollbar_size.x, pos.y + title_size.y);
|
||||
|
||||
// scrollbar background
|
||||
ctx.DrawRect(scrollbar_pos, scrollbar_pos + scrollbar_size, 0x55000000);
|
||||
|
||||
// scroll box
|
||||
float y0 = static_cast<float>(scroll_) / static_cast<float>(items_.size());
|
||||
float y1 = y0 + static_cast<float>(max_items_) / static_cast<float>(items_.size());
|
||||
glm::vec2 scrollbox_p0(scrollbar_pos.x, scrollbar_pos.y + y0 * scrollbar_size.y);
|
||||
glm::vec2 scrollbox_p1(scrollbar_pos.x + scrollbar_size.x, scrollbar_pos.y + y1 * scrollbar_size.y);
|
||||
ctx.DrawRect(scrollbox_p0, scrollbox_p1, 0x44FFFFFF);
|
||||
|
||||
res_itemsize.x -= scrollbar_size.x;
|
||||
}
|
||||
|
||||
// draw items
|
||||
DrawMenuItemArgs args(ctx);
|
||||
args.size = itemsize_;
|
||||
args.size = res_itemsize;
|
||||
args.pos.x = pos.x;
|
||||
|
||||
for (size_t i = 0; i < items_.size(); ++i)
|
||||
size_t end = glm::min(items_.size(), scroll_ + max_items_);
|
||||
for (size_t i = scroll_; i < end; ++i)
|
||||
{
|
||||
args.focused = focus_ == i;
|
||||
args.pos.y = pos.y + menu_title_height + static_cast<float>(i) * itemsize_.y;
|
||||
args.pos.y = pos.y + menu_title_height + static_cast<float>(i - scroll_) * res_itemsize.y;
|
||||
|
||||
items_[i]->Draw(args);
|
||||
}
|
||||
@ -49,6 +72,10 @@ void gui::Menu::Input(MenuInput in)
|
||||
SwitchFocus(1);
|
||||
break;
|
||||
|
||||
case MI_BACK:
|
||||
OnExit();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!items_.empty())
|
||||
items_[focus_]->Input(in);
|
||||
@ -61,9 +88,18 @@ void gui::Menu::SetTitle(std::string title)
|
||||
title_ = std::move(title);
|
||||
}
|
||||
|
||||
void gui::Menu::SetFocusedItemIndex(size_t idx)
|
||||
{
|
||||
if (idx >= items_.size())
|
||||
return;
|
||||
|
||||
focus_ = idx;
|
||||
UpdateScroll();
|
||||
}
|
||||
|
||||
glm::vec2 gui::Menu::MeasureSize() const
|
||||
{
|
||||
return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast<float>(items_.size()));
|
||||
return glm::vec2(itemsize_.x, menu_title_height + itemsize_.y * static_cast<float>(glm::min(items_.size(), max_items_)));
|
||||
}
|
||||
|
||||
void gui::Menu::SwitchFocus(int dir)
|
||||
@ -81,9 +117,34 @@ void gui::Menu::SwitchFocus(int dir)
|
||||
if (focus_ != old_focus)
|
||||
{
|
||||
OnFocusChanged();
|
||||
UpdateScroll();
|
||||
}
|
||||
}
|
||||
|
||||
void gui::Menu::UpdateScroll()
|
||||
{
|
||||
const size_t num_items = items_.size();
|
||||
|
||||
if (scroll_ + max_items_ > num_items)
|
||||
{
|
||||
if (num_items > max_items_)
|
||||
scroll_ = num_items - max_items_;
|
||||
else
|
||||
scroll_ = 0;
|
||||
}
|
||||
|
||||
if (focus_ >= scroll_ + max_items_)
|
||||
{
|
||||
scroll_ = focus_ - max_items_ + 1;
|
||||
}
|
||||
else if (focus_ < scroll_)
|
||||
{
|
||||
scroll_ = focus_;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// ButtonMenuItem
|
||||
|
||||
gui::ButtonMenuItem::ButtonMenuItem(std::string text)
|
||||
@ -94,8 +155,36 @@ gui::ButtonMenuItem::ButtonMenuItem(std::string text)
|
||||
void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const
|
||||
{
|
||||
Super::Draw(args);
|
||||
glm::vec2 center = args.pos + glm::vec2(10.0f, args.size.y * 0.5f);
|
||||
args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), args.focused ? COLOR_FOCUSED : COLOR_INACTIVE);
|
||||
|
||||
uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE;
|
||||
uint32_t arrow_color = 0xFFFFFFFF;
|
||||
|
||||
const float padding = 30.0f;
|
||||
glm::vec2 text_pos = args.pos + glm::vec2(padding, args.size.y * 0.5f);
|
||||
|
||||
// text 1
|
||||
auto text_size = args.ctx.MeasureText(text_);
|
||||
args.ctx.DrawText(text_, text_pos + text_size * glm::vec2(0.0f, -0.5f), text_color);
|
||||
|
||||
// text 2
|
||||
if (!text2_.empty())
|
||||
{
|
||||
glm::vec2 text2_pos = args.pos + glm::vec2(args.size.x - padding, args.size.y * 0.5f);
|
||||
args.ctx.DrawTextAligned(text2_, text2_pos, glm::vec2(-1.0f, -0.5f), text_color);
|
||||
}
|
||||
|
||||
// hover arrows
|
||||
if (args.focused)
|
||||
{
|
||||
auto arrow_size = args.ctx.MeasureText("» ");
|
||||
|
||||
// left arrow
|
||||
args.ctx.DrawText("» ", text_pos + arrow_size * glm::vec2(-1.0f, -0.5f), arrow_color);
|
||||
|
||||
// right arrow
|
||||
glm::vec2 right_arrow_pos(text2_.empty() ? (text_pos.x + text_size.x) : (args.pos.x + args.size.x - padding), text_pos.y);
|
||||
args.ctx.DrawText(" «", right_arrow_pos + arrow_size * glm::vec2(0.0f, -0.5f), arrow_color);
|
||||
}
|
||||
}
|
||||
|
||||
void gui::ButtonMenuItem::Input(MenuInput in)
|
||||
@ -118,16 +207,18 @@ gui::SelectMenuItem::SelectMenuItem(std::string text)
|
||||
|
||||
void gui::SelectMenuItem::Draw(const DrawMenuItemArgs& args) const
|
||||
{
|
||||
Super::Draw(args);
|
||||
// Super::Draw(args);
|
||||
uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE;
|
||||
uint32_t arrow_color = 0xFFFFFFFF;
|
||||
|
||||
glm::vec2 center = args.pos + glm::vec2(10.0f, args.size.y * 0.5f);
|
||||
args.ctx.DrawTextAligned(text_, center, glm::vec2(0.0f, -0.5f), text_color);
|
||||
|
||||
auto text_size = args.ctx.MeasureText(select_text_);
|
||||
|
||||
glm::vec2 cursor = args.pos + glm::vec2(args.size.x - 10.0f, args.size.y * 0.5f);
|
||||
cursor.y -= text_size.y * 0.5f; // centered
|
||||
|
||||
uint32_t text_color = args.focused ? COLOR_FOCUSED : COLOR_INACTIVE;
|
||||
uint32_t arrow_color = 0xFFFFFFFF;
|
||||
|
||||
float arrow_width = 0.0f;
|
||||
if (args.focused)
|
||||
{
|
||||
|
||||
@ -52,6 +52,7 @@ public:
|
||||
void SetTitle(std::string title);
|
||||
void SetItemSize(const glm::vec2& itemsize) { itemsize_ = itemsize; }
|
||||
|
||||
void SetFocusedItemIndex(size_t idx);
|
||||
size_t GetFocusedItemIndex() const { return focus_; }
|
||||
|
||||
size_t GetNumItems() const { return items_.size(); }
|
||||
@ -61,15 +62,20 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void OnFocusChanged() {}
|
||||
virtual void OnExit() {}
|
||||
|
||||
private:
|
||||
void SwitchFocus(int dir);
|
||||
|
||||
void UpdateScroll();
|
||||
|
||||
private:
|
||||
std::string title_;
|
||||
std::vector<std::unique_ptr<MenuItem>> items_;
|
||||
glm::vec2 itemsize_ = glm::vec2(300.0f, 40.0f);
|
||||
glm::vec2 itemsize_ = glm::vec2(400.0f, 35.0f);
|
||||
size_t focus_ = 0;
|
||||
size_t max_items_ = 15;
|
||||
size_t scroll_ = 0;
|
||||
|
||||
};
|
||||
|
||||
@ -106,11 +112,15 @@ public:
|
||||
void SetClickCallback(std::function<void()> click_cb);
|
||||
|
||||
void SetText(std::string text) { text_ = std::move(text); }
|
||||
void SetText2(std::string text2) { text2_ = std::move(text2); }
|
||||
|
||||
virtual ~ButtonMenuItem() = default;
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::string text_;
|
||||
std::string text2_;
|
||||
|
||||
private:
|
||||
std::function<void()> click_cb_;
|
||||
};
|
||||
|
||||
|
||||
@ -166,6 +166,7 @@ enum MenuMessageType
|
||||
MMSG_UPDATE,
|
||||
MMSG_ITEM_UPDATE_TEXT,
|
||||
MMSG_ITEM_UPDATE_SELECTION,
|
||||
MMSG_SET_HOVER,
|
||||
MMSG_CLOSE,
|
||||
};
|
||||
|
||||
@ -185,6 +186,7 @@ enum MenuActionType
|
||||
MA_CLICK,
|
||||
MA_SELECT,
|
||||
MA_HOVER,
|
||||
MA_EXIT,
|
||||
};
|
||||
|
||||
using MenuSelectDir = uint8_t; // 0=left, 1=right
|
||||
|
||||
@ -131,6 +131,15 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Skip(size_t count)
|
||||
{
|
||||
if (!CheckAvail(count))
|
||||
return false;
|
||||
|
||||
ptr_ += count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SubMessage(InMessage& sub, size_t n)
|
||||
{
|
||||
if (!CheckAvail(n))
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
|
||||
int main()
|
||||
{
|
||||
srand(time(NULL));
|
||||
|
||||
try
|
||||
{
|
||||
sv::Server server(11200);
|
||||
|
||||
@ -17,8 +17,6 @@ void sv::Server::Run()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
auto t_start = std::chrono::steady_clock::now();
|
||||
auto t_next = t_start;
|
||||
auto t_prev = t_start;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user