diff --git a/src/client/app.cpp b/src/client/app.cpp index 5d5a188..936a494 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -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 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.cam_pos = eye; game::view::DrawArgs draw_args(dlist_, params.view_proj, viewport_size_); world->Draw(draw_args); diff --git a/src/game/entity.hpp b/src/game/entity.hpp index 3836460..d0fd3ae 100644 --- a/src/game/entity.hpp +++ b/src/game/entity.hpp @@ -30,6 +30,9 @@ public: void Remove() { removed_ = true; } bool IsRemoved() const { return removed_; } + const Transform& GetRootTransform() const { return root_.local; } + float GetMaxDistance() const { return max_distance_; } + virtual ~Entity() = default; private: @@ -46,8 +49,11 @@ protected: const net::EntType viewtype_; TransformNode root_; + + float max_distance_ = 700.0f; std::string nametag_; + bool removed_ = false; }; diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp index 91ae68b..3ab15e5 100644 --- a/src/game/openworld.cpp +++ b/src/game/openworld.cpp @@ -94,7 +94,7 @@ game::OpenWorld::OpenWorld() : World("openworld") // } // spawn bots - for (size_t i = 0; i < 30; ++i) + for (size_t i = 0; i < 70; ++i) { SpawnBot(); } @@ -262,7 +262,16 @@ static float GetTurnAngle(const glm::vec3& pos, const glm::quat& rot, const glm: static void SelectNextNode(BotThinkState& s) { 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 s) @@ -480,13 +489,13 @@ void game::OpenWorld::SpawnBot() // auto color = glm::vec3{0.3f, 0.3f, 0.3f}; auto color = GetRandomColor(); auto& vehicle = Spawn(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}); auto think_state = std::make_shared(vehicle, *roads, start_node); BotThink(think_state); vehicle.Schedule(rand() % 500, [think_state]() { - BotNametagThink(think_state); + //BotNametagThink(think_state); } ); } diff --git a/src/game/player.cpp b/src/game/player.cpp index 56a124e..6a49ef7 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -1,9 +1,12 @@ #include "player.hpp" -#include "world.hpp" - #include +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include "world.hpp" + game::Player::Player(Game& game, std::string name) : game_(game), name_(std::move(name)) { game_.PlayerJoined(*this); @@ -30,7 +33,18 @@ void game::Player::Update() } if (world_) + { + if (cam_ent_) + { + auto cam_ent = world_->GetEntity(cam_ent_); + if (cam_ent) + { + cull_pos_ = cam_ent->GetRootTransform().position; + } + } + SyncEntities(); + } } void game::Player::SetWorld(std::shared_ptr world) @@ -49,6 +63,8 @@ void game::Player::SetWorld(std::shared_ptr world) void game::Player::SetCamera(net::EntNum entnum) { + cam_ent_ = entnum; + auto msg = BeginMsg(net::MSG_CAM); msg.Write(entnum); } @@ -121,7 +137,14 @@ void game::Player::SyncEntities() 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) diff --git a/src/game/player.hpp b/src/game/player.hpp index 761995a..afad8b7 100644 --- a/src/game/player.hpp +++ b/src/game/player.hpp @@ -59,6 +59,9 @@ private: std::set known_ents_; PlayerInputFlags in_ = 0; + + net::EntNum cam_ent_ = 0; + glm::vec3 cull_pos_ = glm::vec3(0.0f); }; } \ No newline at end of file diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index c7b76ca..5b0a037 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -57,7 +57,7 @@ game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& co { float wheelRadius = wheeldef.radius; - float friction = 2.0f; // 5.0f; + float friction = 3.0f; // 5.0f; float suspensionStiffness = 50.0f; // float suspensionDamping = 2.3f; // float suspensionCompression = 4.4f; diff --git a/src/gameview/client_session.cpp b/src/gameview/client_session.cpp index e805c97..633cf63 100644 --- a/src/gameview/client_session.cpp +++ b/src/gameview/client_session.cpp @@ -65,7 +65,7 @@ void game::view::ClientSession::Update(const UpdateInfo& 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); @@ -84,9 +84,8 @@ glm::mat4 game::view::ClientSession::GetViewMatrix() const float distance = 8.0f; - auto eye = center - dir * distance; - - return glm::lookAt(eye, center, glm::vec3(0, 0, 1)); + eye = center - dir * distance; + view = glm::lookAt(eye, center, glm::vec3(0, 0, 1)); } audio::Master& game::view::ClientSession::GetAudioMaster() const diff --git a/src/gameview/client_session.hpp b/src/gameview/client_session.hpp index 68f99cd..f80f351 100644 --- a/src/gameview/client_session.hpp +++ b/src/gameview/client_session.hpp @@ -27,7 +27,7 @@ public: const WorldView* GetWorld() const { return world_.get(); } - glm::mat4 GetViewMatrix() const; + void GetViewInfo(glm::vec3& eye, glm::mat4& view) const; audio::Master& GetAudioMaster() const; diff --git a/src/gameview/entityview.cpp b/src/gameview/entityview.cpp index 7027619..5680385 100644 --- a/src/gameview/entityview.cpp +++ b/src/gameview/entityview.cpp @@ -33,7 +33,23 @@ void game::view::EntityView::Update(const UpdateInfo& info) void game::view::EntityView::Draw(const DrawArgs& args) { // 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()) 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 - net::NameTag nametag; - if (!msg.Read(nametag)) - return false; + const float len = 5.0f; + static const uint32_t colors[] = {0xFF0000FF, 0xFF00FF00, 0xFFFF0000}; - nametag_ = nametag; - nametag_text_.SetText(nametag); + for (size_t i = 0; i < 3; i++) + { + 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); + } } diff --git a/src/gameview/entityview.hpp b/src/gameview/entityview.hpp index 8680482..ad4d304 100644 --- a/src/gameview/entityview.hpp +++ b/src/gameview/entityview.hpp @@ -48,6 +48,9 @@ public: private: bool ReadNametag(net::InMessage& msg); + void DrawNametag(const DrawArgs& args); + void DrawAxes(const DrawArgs& args); + protected: WorldView& world_; diff --git a/src/gameview/vehicleview.cpp b/src/gameview/vehicleview.cpp index c0543e6..bc86bbb 100644 --- a/src/gameview/vehicleview.cpp +++ b/src/gameview/vehicleview.cpp @@ -16,24 +16,25 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) 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(); wheels_.resize(modelwheels.size()); - + for (size_t i = 0; i < wheels_.size(); ++i) { wheels_[i].node.parent = &root_; } - + color_ = glm::vec4(color, 1.0f); - + if (!ReadState(msg)) throw EntityInitError(); - + + // init the other transform to identical + root_trans_[0] = root_trans_[1]; + snd_accel_ = assets::CacheManager::GetSound("data/auto.snd"); - + // sync state net::DecodePosition(sync_.pos, root_.local.position); net::DecodeRotation(sync_.rot, root_.local.rotation); diff --git a/src/gfx/draw_list.hpp b/src/gfx/draw_list.hpp index 9e7c7a3..df9e0de 100644 --- a/src/gfx/draw_list.hpp +++ b/src/gfx/draw_list.hpp @@ -19,6 +19,16 @@ struct DrawSurfaceCmd 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 { const VertexArray* va = nullptr; @@ -30,14 +40,17 @@ struct DrawHudCmd struct DrawList { std::vector surfaces; + std::vector beams; std::vector huds; 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 Clear() { surfaces.clear(); + beams.clear(); huds.clear(); } }; diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 7fbd27d..c7aacfc 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -18,6 +18,9 @@ gfx::Renderer::Renderer() 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(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) @@ -40,9 +43,61 @@ void gfx::Renderer::ClearDepth() void gfx::Renderer::DrawList(gfx::DrawList& list, const DrawListParams& params) { DrawSurfaceList(list.surfaces, params); + DrawBeamList(list.beams, 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(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(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() { InvalidateMeshShader(mesh_shader_); @@ -253,6 +308,82 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi } +static float GetRandomOffset(float max_offset) +{ + return (static_cast(rand()) / static_cast(RAND_MAX) - 0.5f) * 2.0f * max_offset; +} + +void gfx::Renderer::DrawBeamList(std::span queue, const DrawListParams& params) +{ + static std::vector segments; + static std::vector 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(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, ¶ms.view_proj[0][0]); + glUniform3fv(shader->U(gfx::SU_CAMERA), 1, ¶ms.cam_pos[0]); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, segments.size()); + + glBindVertexArray(0); + + glDepthMask(GL_TRUE); + +} + void gfx::Renderer::DrawHudList(std::span queue, const DrawListParams& params) { // cannot sort anything here, must be drawn in FIFO order for correct overlay diff --git a/src/gfx/renderer.hpp b/src/gfx/renderer.hpp index c5ab835..1280bfe 100644 --- a/src/gfx/renderer.hpp +++ b/src/gfx/renderer.hpp @@ -10,6 +10,7 @@ namespace gfx { struct DrawListParams { + glm::vec3 cam_pos; glm::mat4 view_proj; size_t screen_width = 0; size_t screen_height = 0; @@ -38,20 +39,30 @@ namespace gfx void DrawList(gfx::DrawList& list, const DrawListParams& params); private: - MeshShader mesh_shader_; - MeshShader skel_mesh_shader_; - std::unique_ptr solid_shader_; - std::unique_ptr hud_shader_; - - const Shader* current_shader_ = nullptr; + void SetupBeamVA(); void InvalidateShaders(); void InvalidateMeshShader(MeshShader& mshader); void SetupMeshShader(MeshShader& mshader, const DrawListParams& params); void DrawSurfaceList(std::span queue, const DrawListParams& params); + void DrawBeamList(std::span queue, const DrawListParams& params); void DrawHudList(std::span queue, const DrawListParams& params); + private: + MeshShader mesh_shader_; + MeshShader skel_mesh_shader_; + std::unique_ptr solid_shader_; + + std::unique_ptr beam_segments_vbo_; + std::unique_ptr beam_va_; + std::unique_ptr beam_shader_; + + std::unique_ptr hud_shader_; + + const Shader* current_shader_ = nullptr; + + }; } \ No newline at end of file diff --git a/src/gfx/shader.cpp b/src/gfx/shader.cpp index 89a31eb..1108abb 100644 --- a/src/gfx/shader.cpp +++ b/src/gfx/shader.cpp @@ -9,7 +9,8 @@ static const char* const s_uni_names[] = { "u_view_proj", // SU_VIEW_PROJ "u_tex", // SU_TEX "u_color", // SU_COLOR - "u_flags", // SU_FLAGS + "u_flags", // SU_FLAGS + "u_camera", // SU_CAMERA }; // Vytvori shader z daneho zdroje diff --git a/src/gfx/shader.hpp b/src/gfx/shader.hpp index a3b5427..363fc73 100644 --- a/src/gfx/shader.hpp +++ b/src/gfx/shader.hpp @@ -15,6 +15,7 @@ namespace gfx SU_TEX, SU_COLOR, SU_FLAGS, + SU_CAMERA, SU_COUNT }; diff --git a/src/gfx/shader_sources.cpp b/src/gfx/shader_sources.cpp index ba77b18..d12503c 100644 --- a/src/gfx/shader_sources.cpp +++ b/src/gfx/shader_sources.cpp @@ -265,6 +265,50 @@ void main() { )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 diff --git a/src/gfx/shader_sources.hpp b/src/gfx/shader_sources.hpp index a4f11d4..a7b7e03 100644 --- a/src/gfx/shader_sources.hpp +++ b/src/gfx/shader_sources.hpp @@ -19,6 +19,8 @@ namespace gfx SS_HUD_VERT, SS_HUD_FRAG, + SS_BEAM_VERT, + SS_BEAM_FRAG, }; class ShaderSources diff --git a/src/gfx/texture.cpp b/src/gfx/texture.cpp index 8772c92..3013134 100644 --- a/src/gfx/texture.cpp +++ b/src/gfx/texture.cpp @@ -50,6 +50,9 @@ gfx::Texture::Texture(GLuint width, GLuint height, const void* data, GLint inter if (mipmaps) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3); + glGenerateMipmap(GL_TEXTURE_2D); }