Beam rendering, max entity distance and other stuff

This commit is contained in:
tovjemam 2026-02-01 00:17:36 +01:00
parent 4b04a77e7e
commit 0cc61ec358
19 changed files with 315 additions and 36 deletions

View File

@ -82,9 +82,12 @@ void App::Frame()
{ {
// glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f, 0.0f, 1.0f)); // glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f); glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f);
glm::mat4 view = session_->GetViewMatrix(); glm::vec3 eye;
glm::mat4 view;
session_->GetViewInfo(eye, view);
params.view_proj = proj * view; params.view_proj = proj * view;
params.cam_pos = eye;
game::view::DrawArgs draw_args(dlist_, params.view_proj, viewport_size_); game::view::DrawArgs draw_args(dlist_, params.view_proj, viewport_size_);
world->Draw(draw_args); world->Draw(draw_args);

View File

@ -30,6 +30,9 @@ public:
void Remove() { removed_ = true; } void Remove() { removed_ = true; }
bool IsRemoved() const { return removed_; } bool IsRemoved() const { return removed_; }
const Transform& GetRootTransform() const { return root_.local; }
float GetMaxDistance() const { return max_distance_; }
virtual ~Entity() = default; virtual ~Entity() = default;
private: private:
@ -46,8 +49,11 @@ protected:
const net::EntType viewtype_; const net::EntType viewtype_;
TransformNode root_; TransformNode root_;
float max_distance_ = 700.0f;
std::string nametag_; std::string nametag_;
bool removed_ = false; bool removed_ = false;
}; };

View File

@ -94,7 +94,7 @@ game::OpenWorld::OpenWorld() : World("openworld")
// } // }
// spawn bots // spawn bots
for (size_t i = 0; i < 30; ++i) for (size_t i = 0; i < 70; ++i)
{ {
SpawnBot(); SpawnBot();
} }
@ -262,7 +262,16 @@ static float GetTurnAngle(const glm::vec3& pos, const glm::quat& rot, const glm:
static void SelectNextNode(BotThinkState& s) static void SelectNextNode(BotThinkState& s)
{ {
size_t node = s.path.back(); size_t node = s.path.back();
s.path.push_back(s.roads.nbs[s.roads.nodes[node].nbs + (rand() % s.roads.nodes[node].num_nbs)]); size_t num_nbs = s.roads.nodes[node].num_nbs;
if (num_nbs < 1)
{
const auto& pos = s.roads.nodes[node].position;
std::cout << "node " << node << " has no neighbors!!!1 position: " << pos.x << " " << pos.y << " " << pos.z << std::endl;
throw std::runtime_error("no neighbors");
}
s.path.push_back(s.roads.nbs[s.roads.nodes[node].nbs + (rand() % num_nbs)]);
} }
static void BotThink(std::shared_ptr<BotThinkState> s) static void BotThink(std::shared_ptr<BotThinkState> s)
@ -480,13 +489,13 @@ void game::OpenWorld::SpawnBot()
// auto color = glm::vec3{0.3f, 0.3f, 0.3f}; // auto color = glm::vec3{0.3f, 0.3f, 0.3f};
auto color = GetRandomColor(); auto color = GetRandomColor();
auto& vehicle = Spawn<Vehicle>(GetRandomCarModel(), color); auto& vehicle = Spawn<Vehicle>(GetRandomCarModel(), color);
vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")"); //vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")");
vehicle.SetPosition(roads->nodes[start_node].position + glm::vec3{0.0f, 0.0f, 5.0f}); vehicle.SetPosition(roads->nodes[start_node].position + glm::vec3{0.0f, 0.0f, 5.0f});
auto think_state = std::make_shared<BotThinkState>(vehicle, *roads, start_node); auto think_state = std::make_shared<BotThinkState>(vehicle, *roads, start_node);
BotThink(think_state); BotThink(think_state);
vehicle.Schedule(rand() % 500, [think_state]() { vehicle.Schedule(rand() % 500, [think_state]() {
BotNametagThink(think_state); //BotNametagThink(think_state);
} ); } );
} }

View File

@ -1,9 +1,12 @@
#include "player.hpp" #include "player.hpp"
#include "world.hpp"
#include <iostream> #include <iostream>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/norm.hpp>
#include "world.hpp"
game::Player::Player(Game& game, std::string name) : game_(game), name_(std::move(name)) game::Player::Player(Game& game, std::string name) : game_(game), name_(std::move(name))
{ {
game_.PlayerJoined(*this); game_.PlayerJoined(*this);
@ -30,7 +33,18 @@ void game::Player::Update()
} }
if (world_) if (world_)
{
if (cam_ent_)
{
auto cam_ent = world_->GetEntity(cam_ent_);
if (cam_ent)
{
cull_pos_ = cam_ent->GetRootTransform().position;
}
}
SyncEntities(); SyncEntities();
}
} }
void game::Player::SetWorld(std::shared_ptr<World> world) void game::Player::SetWorld(std::shared_ptr<World> world)
@ -49,6 +63,8 @@ void game::Player::SetWorld(std::shared_ptr<World> world)
void game::Player::SetCamera(net::EntNum entnum) void game::Player::SetCamera(net::EntNum entnum)
{ {
cam_ent_ = entnum;
auto msg = BeginMsg(net::MSG_CAM); auto msg = BeginMsg(net::MSG_CAM);
msg.Write(entnum); msg.Write(entnum);
} }
@ -121,7 +137,14 @@ void game::Player::SyncEntities()
bool game::Player::ShouldSeeEntity(const Entity& entity) const bool game::Player::ShouldSeeEntity(const Entity& entity) const
{ {
return true; // TODO: check distance? // max distance check
float max_dist = entity.GetMaxDistance();
if (glm::distance2(entity.GetRootTransform().position, cull_pos_) > (max_dist * max_dist))
return false;
// TODO: custom callback
return true;
} }
void game::Player::SendInitEntity(const Entity& entity) void game::Player::SendInitEntity(const Entity& entity)

View File

@ -59,6 +59,9 @@ private:
std::set<net::EntNum> known_ents_; std::set<net::EntNum> known_ents_;
PlayerInputFlags in_ = 0; PlayerInputFlags in_ = 0;
net::EntNum cam_ent_ = 0;
glm::vec3 cull_pos_ = glm::vec3(0.0f);
}; };
} }

View File

@ -57,7 +57,7 @@ game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& co
{ {
float wheelRadius = wheeldef.radius; float wheelRadius = wheeldef.radius;
float friction = 2.0f; // 5.0f; float friction = 3.0f; // 5.0f;
float suspensionStiffness = 50.0f; float suspensionStiffness = 50.0f;
// float suspensionDamping = 2.3f; // float suspensionDamping = 2.3f;
// float suspensionCompression = 4.4f; // float suspensionCompression = 4.4f;

View File

@ -65,7 +65,7 @@ void game::view::ClientSession::Update(const UpdateInfo& info)
world_->Update(info); world_->Update(info);
} }
glm::mat4 game::view::ClientSession::GetViewMatrix() const void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) const
{ {
glm::vec3 center(0.0f, 0.0f, 2.5f); glm::vec3 center(0.0f, 0.0f, 2.5f);
@ -84,9 +84,8 @@ glm::mat4 game::view::ClientSession::GetViewMatrix() const
float distance = 8.0f; float distance = 8.0f;
auto eye = center - dir * distance; eye = center - dir * distance;
view = glm::lookAt(eye, center, glm::vec3(0, 0, 1));
return glm::lookAt(eye, center, glm::vec3(0, 0, 1));
} }
audio::Master& game::view::ClientSession::GetAudioMaster() const audio::Master& game::view::ClientSession::GetAudioMaster() const

View File

@ -27,7 +27,7 @@ public:
const WorldView* GetWorld() const { return world_.get(); } const WorldView* GetWorld() const { return world_.get(); }
glm::mat4 GetViewMatrix() const; void GetViewInfo(glm::vec3& eye, glm::mat4& view) const;
audio::Master& GetAudioMaster() const; audio::Master& GetAudioMaster() const;

View File

@ -33,7 +33,23 @@ void game::view::EntityView::Update(const UpdateInfo& info)
void game::view::EntityView::Draw(const DrawArgs& args) void game::view::EntityView::Draw(const DrawArgs& args)
{ {
// std::cout << "TODO draw entity nametag: " << nametag_ << std::endl; // std::cout << "TODO draw entity nametag: " << nametag_ << std::endl;
DrawNametag(args);
//DrawAxes(args);
}
bool game::view::EntityView::ReadNametag(net::InMessage& msg)
{
// read nametag
net::NameTag nametag;
if (!msg.Read(nametag))
return false;
nametag_ = nametag;
nametag_text_.SetText(nametag);
}
void game::view::EntityView::DrawNametag(const DrawArgs& args)
{
if (nametag_.empty()) if (nametag_.empty())
return; return;
@ -68,13 +84,23 @@ void game::view::EntityView::Draw(const DrawArgs& args)
} }
bool game::view::EntityView::ReadNametag(net::InMessage& msg) void game::view::EntityView::DrawAxes(const DrawArgs& args)
{ {
// read nametag const float len = 5.0f;
net::NameTag nametag; static const uint32_t colors[] = {0xFF0000FF, 0xFF00FF00, 0xFFFF0000};
if (!msg.Read(nametag))
return false;
nametag_ = nametag; for (size_t i = 0; i < 3; i++)
nametag_text_.SetText(nametag); {
glm::vec3 end(0.0f);
end[i] = len;
gfx::DrawBeamCmd cmd;
cmd.start = glm::vec3(root_.matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
cmd.end = glm::vec3(root_.matrix * glm::vec4(end, 1.0f));
cmd.color = colors[i];
cmd.radius = 0.05f;
//cmd.num_segments = 10;
//cmd.max_offset = 0.1f;
args.dlist.AddBeam(cmd);
}
} }

View File

@ -48,6 +48,9 @@ public:
private: private:
bool ReadNametag(net::InMessage& msg); bool ReadNametag(net::InMessage& msg);
void DrawNametag(const DrawArgs& args);
void DrawAxes(const DrawArgs& args);
protected: protected:
WorldView& world_; WorldView& world_;

View File

@ -16,8 +16,6 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh"); model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh");
// init the other transform to identical
root_trans_[0] = root_trans_[1];
auto& modelwheels = model_->GetWheels(); auto& modelwheels = model_->GetWheels();
wheels_.resize(modelwheels.size()); wheels_.resize(modelwheels.size());
@ -32,6 +30,9 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
if (!ReadState(msg)) if (!ReadState(msg))
throw EntityInitError(); throw EntityInitError();
// init the other transform to identical
root_trans_[0] = root_trans_[1];
snd_accel_ = assets::CacheManager::GetSound("data/auto.snd"); snd_accel_ = assets::CacheManager::GetSound("data/auto.snd");
// sync state // sync state

View File

@ -19,6 +19,16 @@ struct DrawSurfaceCmd
float dist = 0.0f; // distance to camera - for transparnt sorting float dist = 0.0f; // distance to camera - for transparnt sorting
}; };
struct DrawBeamCmd
{
glm::vec3 start;
glm::vec3 end;
uint32_t color = 0xFFFFFFFF;
float radius = 0.1f;
size_t num_segments = 1;
float max_offset = 0.0f;
};
struct DrawHudCmd struct DrawHudCmd
{ {
const VertexArray* va = nullptr; const VertexArray* va = nullptr;
@ -30,14 +40,17 @@ struct DrawHudCmd
struct DrawList struct DrawList
{ {
std::vector<DrawSurfaceCmd> surfaces; std::vector<DrawSurfaceCmd> surfaces;
std::vector<DrawBeamCmd> beams;
std::vector<DrawHudCmd> huds; std::vector<DrawHudCmd> huds;
void AddSurface(const DrawSurfaceCmd& cmd) { surfaces.emplace_back(cmd); } void AddSurface(const DrawSurfaceCmd& cmd) { surfaces.emplace_back(cmd); }
void AddBeam(const DrawBeamCmd& cmd) { beams.emplace_back(cmd); }
void AddHUD(const DrawHudCmd& cmd) { huds.emplace_back(cmd); } void AddHUD(const DrawHudCmd& cmd) { huds.emplace_back(cmd); }
void Clear() void Clear()
{ {
surfaces.clear(); surfaces.clear();
beams.clear();
huds.clear(); huds.clear();
} }
}; };

View File

@ -18,6 +18,9 @@ gfx::Renderer::Renderer()
ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG); ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG);
ShaderSources::MakeShader(solid_shader_, SS_SOLID_VERT, SS_SOLID_FRAG); ShaderSources::MakeShader(solid_shader_, SS_SOLID_VERT, SS_SOLID_FRAG);
ShaderSources::MakeShader(hud_shader_, SS_HUD_VERT, SS_HUD_FRAG); ShaderSources::MakeShader(hud_shader_, SS_HUD_VERT, SS_HUD_FRAG);
ShaderSources::MakeShader(beam_shader_, SS_BEAM_VERT, SS_BEAM_FRAG);
SetupBeamVA();
} }
void gfx::Renderer::Begin(size_t width, size_t height) void gfx::Renderer::Begin(size_t width, size_t height)
@ -40,9 +43,61 @@ void gfx::Renderer::ClearDepth()
void gfx::Renderer::DrawList(gfx::DrawList& list, const DrawListParams& params) void gfx::Renderer::DrawList(gfx::DrawList& list, const DrawListParams& params)
{ {
DrawSurfaceList(list.surfaces, params); DrawSurfaceList(list.surfaces, params);
DrawBeamList(list.beams, params);
DrawHudList(list.huds, params); DrawHudList(list.huds, params);
} }
struct BeamSegment
{
glm::vec3 p0;
uint32_t color;
glm::vec3 p1;
float radius;
};
void gfx::Renderer::SetupBeamVA()
{
beam_va_ = std::make_unique<VertexArray>(VA_POSITION, 0);
static const float quad_points[] = {
0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
beam_va_->SetVBOData(quad_points, sizeof(quad_points));
// create points buffer
glBindVertexArray(beam_va_->GetVAOId());
beam_segments_vbo_ = std::make_unique<BufferObject>(GL_ARRAY_BUFFER, GL_STREAM_DRAW);
beam_segments_vbo_->Bind();
constexpr size_t STRIDE = sizeof(BeamSegment);
// p0
glEnableVertexAttribArray(10);
glVertexAttribPointer(10, 3, GL_FLOAT, GL_FALSE, STRIDE, (const void*)offsetof(BeamSegment, p0));
glVertexAttribDivisor(10, 1);
// color
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, STRIDE, (const void*)offsetof(BeamSegment, color));
glVertexAttribDivisor(2, 1);
// p1
glEnableVertexAttribArray(11);
glVertexAttribPointer(11, 3, GL_FLOAT, GL_FALSE, STRIDE, (const void*)offsetof(BeamSegment, p1));
glVertexAttribDivisor(11, 1);
// radius
glEnableVertexAttribArray(12);
glVertexAttribPointer(12, 1, GL_FLOAT, GL_FALSE, STRIDE, (const void*)offsetof(BeamSegment, radius));
glVertexAttribDivisor(12, 1);
glBindVertexArray(0);
}
void gfx::Renderer::InvalidateShaders() void gfx::Renderer::InvalidateShaders()
{ {
InvalidateMeshShader(mesh_shader_); InvalidateMeshShader(mesh_shader_);
@ -253,6 +308,82 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
} }
static float GetRandomOffset(float max_offset)
{
return (static_cast<float>(rand()) / static_cast<float>(RAND_MAX) - 0.5f) * 2.0f * max_offset;
}
void gfx::Renderer::DrawBeamList(std::span<DrawBeamCmd> queue, const DrawListParams& params)
{
static std::vector<BeamSegment> segments;
static std::vector<glm::vec3> points;
segments.clear();
for (const auto& cmd : queue)
{
if (cmd.num_segments < 1)
continue;
points.resize(cmd.num_segments + 1);
const glm::vec3 seg_step = (cmd.end - cmd.start) / static_cast<float>(cmd.num_segments);
glm::vec3 pos = cmd.start;
for (size_t i = 0; i <= cmd.num_segments; ++i)
{
points[i] = pos;
pos += seg_step;
}
if (cmd.max_offset > 0.0f)
{
for (auto& p : points)
{
p.x += GetRandomOffset(cmd.max_offset);
p.y += GetRandomOffset(cmd.max_offset);
p.z += GetRandomOffset(cmd.max_offset);
}
}
for (size_t i = 1; i < points.size(); ++i)
{
auto& segment = segments.emplace_back();
segment.p0 = points[i - 1];
segment.p1 = points[i];
segment.color = cmd.color;
segment.radius = cmd.radius;
}
}
if (segments.empty())
return;
glBindVertexArray(beam_va_->GetVAOId());
beam_segments_vbo_->SetData(segments.data(), segments.size() * sizeof(segments[0]));
Shader* shader = beam_shader_.get();
glUseProgram(shader->GetId());
current_shader_ = shader;
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // ADDITIVE blend
glUniformMatrix4fv(shader->U(gfx::SU_VIEW_PROJ), 1, GL_FALSE, &params.view_proj[0][0]);
glUniform3fv(shader->U(gfx::SU_CAMERA), 1, &params.cam_pos[0]);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, segments.size());
glBindVertexArray(0);
glDepthMask(GL_TRUE);
}
void gfx::Renderer::DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params) void gfx::Renderer::DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params)
{ {
// cannot sort anything here, must be drawn in FIFO order for correct overlay // cannot sort anything here, must be drawn in FIFO order for correct overlay

View File

@ -10,6 +10,7 @@ namespace gfx
{ {
struct DrawListParams struct DrawListParams
{ {
glm::vec3 cam_pos;
glm::mat4 view_proj; glm::mat4 view_proj;
size_t screen_width = 0; size_t screen_width = 0;
size_t screen_height = 0; size_t screen_height = 0;
@ -38,20 +39,30 @@ namespace gfx
void DrawList(gfx::DrawList& list, const DrawListParams& params); void DrawList(gfx::DrawList& list, const DrawListParams& params);
private: private:
MeshShader mesh_shader_; void SetupBeamVA();
MeshShader skel_mesh_shader_;
std::unique_ptr<Shader> solid_shader_;
std::unique_ptr<Shader> hud_shader_;
const Shader* current_shader_ = nullptr;
void InvalidateShaders(); void InvalidateShaders();
void InvalidateMeshShader(MeshShader& mshader); void InvalidateMeshShader(MeshShader& mshader);
void SetupMeshShader(MeshShader& mshader, const DrawListParams& params); void SetupMeshShader(MeshShader& mshader, const DrawListParams& params);
void DrawSurfaceList(std::span<DrawSurfaceCmd> queue, const DrawListParams& params); 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); void DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params);
private:
MeshShader mesh_shader_;
MeshShader skel_mesh_shader_;
std::unique_ptr<Shader> solid_shader_;
std::unique_ptr<BufferObject> beam_segments_vbo_;
std::unique_ptr<VertexArray> beam_va_;
std::unique_ptr<Shader> beam_shader_;
std::unique_ptr<Shader> hud_shader_;
const Shader* current_shader_ = nullptr;
}; };
} }

View File

@ -10,6 +10,7 @@ static const char* const s_uni_names[] = {
"u_tex", // SU_TEX "u_tex", // SU_TEX
"u_color", // SU_COLOR "u_color", // SU_COLOR
"u_flags", // SU_FLAGS "u_flags", // SU_FLAGS
"u_camera", // SU_CAMERA
}; };
// Vytvori shader z daneho zdroje // Vytvori shader z daneho zdroje

View File

@ -15,6 +15,7 @@ namespace gfx
SU_TEX, SU_TEX,
SU_COLOR, SU_COLOR,
SU_FLAGS, SU_FLAGS,
SU_CAMERA,
SU_COUNT SU_COUNT
}; };

View File

@ -265,6 +265,50 @@ void main() {
)GLSL", )GLSL",
// SS_BEAM_VERT
SHADER_HEADER
R"GLSL(
layout (location = 0) in vec3 a_pos;
// instance
layout (location = 2) in vec4 a_color;
layout (location = 10) in vec3 a_p0;
layout (location = 11) in vec3 a_p1;
layout (location = 12) in float a_radius;
uniform mat4 u_view_proj;
uniform vec3 u_camera;
out vec4 v_color;
void main() {
vec3 p = mix(a_p0, a_p1, a_pos.y);
vec3 seg_dir = a_p1 - a_p0;
vec3 cam_dir = u_camera - p;
vec3 cross_dir = normalize(cross(seg_dir, cam_dir));
p += cross_dir * a_radius * (a_pos.x - 0.5) * 2.0;
gl_Position = u_view_proj * vec4(p, 1.0);
v_color = a_color;
}
)GLSL",
// SS_BEAM_FRAG
SHADER_HEADER
R"GLSL(
in vec4 v_color;
layout (location = 0) out vec4 o_color;
void main() {
o_color = v_color;
}
)GLSL",
}; };
// Vrati zdrojovy kod shaderu // Vrati zdrojovy kod shaderu

View File

@ -19,6 +19,8 @@ namespace gfx
SS_HUD_VERT, SS_HUD_VERT,
SS_HUD_FRAG, SS_HUD_FRAG,
SS_BEAM_VERT,
SS_BEAM_FRAG,
}; };
class ShaderSources class ShaderSources

View File

@ -50,6 +50,9 @@ gfx::Texture::Texture(GLuint width, GLuint height, const void* data, GLint inter
if (mipmaps) if (mipmaps)
{ {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
} }