From 3e284af672217045948d9df9981eebf76f18f02d Mon Sep 17 00:00:00 2001 From: tovjemam Date: Fri, 6 Mar 2026 22:32:45 +0100 Subject: [PATCH] Vehicle deform --- CMakeLists.txt | 4 ++ src/collision/object_info.hpp | 9 ++- src/game/deform_grid.cpp | 46 +++++++++++++++ src/game/deform_grid.hpp | 41 +++++++++++++ src/game/openworld.cpp | 4 +- src/game/vehicle.cpp | 78 +++++++++++++++++++++++-- src/game/vehicle.hpp | 10 +++- src/game/world.cpp | 16 ++++-- src/gameview/vehicleview.cpp | 105 +++++++++++++++++++++++++++++++++- src/gameview/vehicleview.hpp | 16 ++++++ src/gfx/deform_grid_info.hpp | 17 ++++++ src/gfx/deform_texture.cpp | 32 +++++++++++ src/gfx/deform_texture.hpp | 34 +++++++++++ src/gfx/renderer.cpp | 31 +++++++++- src/gfx/renderer.hpp | 1 + src/gfx/shader.cpp | 3 + src/gfx/shader.hpp | 2 + src/gfx/shader_sources.cpp | 70 +++++++++++++++++++++++ src/gfx/shader_sources.hpp | 3 + src/gfx/surface.hpp | 7 ++- src/net/defs.hpp | 3 + 21 files changed, 510 insertions(+), 22 deletions(-) create mode 100644 src/game/deform_grid.cpp create mode 100644 src/game/deform_grid.hpp create mode 100644 src/gfx/deform_grid_info.hpp create mode 100644 src/gfx/deform_texture.cpp create mode 100644 src/gfx/deform_texture.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f4b44da..78d2adb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ set(COMMON_SOURCES "src/collision/trianglemesh.cpp" "src/game/character_anim_state.hpp" "src/game/character_anim_state.cpp" + "src/game/deform_grid.hpp" + "src/game/deform_grid.cpp" "src/game/player_input.hpp" "src/game/simple_entity_sync.hpp" "src/game/skeletoninstance.hpp" @@ -90,6 +92,8 @@ set(CLIENT_ONLY_SOURCES "src/gameview/worldview.cpp" "src/gfx/buffer_object.cpp" "src/gfx/buffer_object.hpp" + "src/gfx/deform_texture.hpp" + "src/gfx/deform_texture.cpp" "src/gfx/draw_list.hpp" "src/gfx/frustum.hpp" "src/gfx/frustum.cpp" diff --git a/src/collision/object_info.hpp b/src/collision/object_info.hpp index 36fbaf1..bbb3c3b 100644 --- a/src/collision/object_info.hpp +++ b/src/collision/object_info.hpp @@ -21,12 +21,19 @@ enum ObjectFlag : ObjectFlags OF_NOTIFY_CONTACT = 0x02, }; +struct ContactInfo +{ + glm::vec3 pos; + glm::vec3 normal; + float impulse; +}; + class ObjectCallback { public: ObjectCallback() = default; - virtual void OnContact(float impulse) {} + virtual void OnContact(const ContactInfo& info) {} virtual ~ObjectCallback() = default; }; diff --git a/src/game/deform_grid.cpp b/src/game/deform_grid.cpp new file mode 100644 index 0000000..692a2d7 --- /dev/null +++ b/src/game/deform_grid.cpp @@ -0,0 +1,46 @@ +#include "deform_grid.hpp" + +game::DeformGrid::DeformGrid(const gfx::DeformGridInfo& info) + : info_(info), data_(info_.res.x * info_.res.y * info_.res.z, glm::i8vec3(0)) +{ + +} + +void game::DeformGrid::ApplyImpulse(const glm::vec3& pos, const glm::vec3& impulse, float radius) +{ + glm::vec3 step = (info_.max - info_.min) / glm::vec3(info_.res); + + glm::ivec3 ipos; + + // TODO: make more efficient + for (ipos.z = 0; ipos.z < info_.res.z; ++ipos.z) + { + for (ipos.y = 0; ipos.y < info_.res.y; ++ipos.y) + { + for (ipos.x = 0; ipos.x < info_.res.x; ++ipos.x) + { + glm::vec3 texel_pos = info_.min + glm::vec3(ipos) * step; + + if (glm::distance2(pos, texel_pos) < radius * radius) + { + size_t idx = GetTexelIndex(ipos); + glm::vec3 offset = UnpackOffset(data_[idx]); + offset += impulse; + data_[idx] = PackOffset(offset); + } + } + } + } + + +} + +glm::i8vec3 game::DeformGrid::PackOffset(const glm::vec3& offset) +{ + return glm::i8vec3(glm::round(glm::clamp(offset / info_.max_offset, -1.0f, 1.0f) * 127.0f)); +} + +glm::vec3 game::DeformGrid::UnpackOffset(const glm::i8vec3& packed) +{ + return glm::clamp(glm::vec3(packed) * 0.0078740157480315f, -1.0f, 1.0f) * info_.max_offset; +} diff --git a/src/game/deform_grid.hpp b/src/game/deform_grid.hpp new file mode 100644 index 0000000..4761c8c --- /dev/null +++ b/src/game/deform_grid.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#define GLM_ENABLE_EXPERIMENTAL +#include +#include + +#include "gfx/deform_grid_info.hpp" + +namespace game +{ + +class DeformGrid +{ +public: + DeformGrid(const gfx::DeformGridInfo& info); + + void ApplyImpulse(const glm::vec3& pos, const glm::vec3& impulse, float radius); + + const gfx::DeformGridInfo& GetInfo() const { return info_; } + + std::span GetData() { return data_; } + std::span GetData() const { return data_; } + +private: + glm::i8vec3 PackOffset(const glm::vec3& offset); + glm::vec3 UnpackOffset(const glm::i8vec3& packed); + + size_t GetTexelIndex(const glm::ivec3& pos) const { return static_cast(pos.x + (pos.y + pos.z * info_.res.y) * info_.res.x); } + +private: + const gfx::DeformGridInfo info_; + std::vector data_; + +}; + + + +} \ No newline at end of file diff --git a/src/game/openworld.cpp b/src/game/openworld.cpp index 0f1c677..0800bfd 100644 --- a/src/game/openworld.cpp +++ b/src/game/openworld.cpp @@ -47,12 +47,12 @@ game::OpenWorld::OpenWorld() : World("openworld") SpawnBot(); } - auto& veh = Spawn("twingo", glm::vec3{0.8f, 0.1f, 0.1f}); + auto& veh = Spawn("twingo", glm::vec3{1.0f, 0.8f, 0.1f}); veh.SetPosition({110.0f, 100.0f, 5.0f}); constexpr size_t in_row = 20; - for (size_t i = 0; i < 3000; ++i) + for (size_t i = 0; i < 1500; ++i) { Schedule(i * 40, [this, i] { size_t col = i % in_row; diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index 43d6980..35b7eb4 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -95,6 +95,14 @@ game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& co bt_world.addRigidBody(body_.get(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter); bt_world.addAction(vehicle_.get()); + // init deform + gfx::DeformGridInfo info{}; + info.min = glm::vec3(-1.0f, -2.5f, 0.10f); + info.max = glm::vec3(1.0f, 2.0f, 1.8f); + info.res = glm::ivec3(8, 16, 8); + info.max_offset = 0.1f; + deformgrid_ = std::make_unique(info); + Update(); } @@ -127,20 +135,22 @@ void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const size_t fields_pos = msg.Reserve(); auto fields = WriteState(msg, default_state); msg.WriteAt(fields_pos, fields); + + WriteDeformSync(msg); } -void game::Vehicle::OnContact(float impulse) +void game::Vehicle::OnContact(const collision::ContactInfo& info) { - Super::OnContact(impulse); + Super::OnContact(info); - crash_intensity_ += impulse; + crash_intensity_ += info.impulse; - if (impulse < 1000.0f) + if (info.impulse < 1000.0f) return; if (window_health_ > 0.0f) { - window_health_ -= impulse; + window_health_ -= info.impulse; if (window_health_ <= 0.0f) // just broken { @@ -148,6 +158,11 @@ void game::Vehicle::OnContact(float impulse) } } + if (window_health_ <= 0.0f) + { + Deform(info.pos, -glm::normalize(info.normal) * 0.1f, 1.0f); + } + } void game::Vehicle::SetInput(VehicleInputType type, bool enable) @@ -485,3 +500,56 @@ void game::Vehicle::SendUpdateMsg() msg.WriteAt(fields_pos, fields); } + +void game::Vehicle::WriteDeformSync(net::OutMessage& msg) const +{ + const auto texels = deformgrid_->GetData(); + + auto numtexels_pos = msg.Reserve(); + net::NumTexels numtexels = 0; + + size_t last = 0; + + for (size_t i = 0; i < texels.size(); ++i) + { + if (texels[i] == glm::i8vec3(0)) + continue; // unchanged, no write + + auto diff = i - last; + msg.WriteVarInt(diff); + + for (size_t j = 0; j < 3; ++j) + { + msg.Write(texels[i][j]); + } + + last = i; + ++numtexels; + } + + msg.WriteAt(numtexels_pos, numtexels); +} + +void game::Vehicle::Deform(const glm::vec3& pos, const glm::vec3& deform, float radius) +{ + net::PositionQ pos_q; + net::PositionQ deform_q; + net::EncodePosition(pos, pos_q); + net::EncodePosition(deform, deform_q); + + SendDeformMsg(pos_q, deform_q); + + // defeorm locally + glm::vec3 new_pos, new_deform; + net::DecodePosition(pos_q, new_pos); + net::DecodePosition(deform_q, new_deform); + + deformgrid_->ApplyImpulse(new_pos, new_deform, 0.3f); +} + +void game::Vehicle::SendDeformMsg(const net::PositionQ& pos, const net::PositionQ& deform) +{ + auto msg = BeginEntMsg(net::EMSG_DEFORM); + net::WritePositionQ(msg, pos); + net::WritePositionQ(msg, deform); +} diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index bf46de1..2818fd8 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -9,6 +9,7 @@ #include "entity.hpp" #include "world.hpp" #include "vehicle_sync.hpp" +#include "deform_grid.hpp" namespace game { @@ -41,7 +42,7 @@ public: virtual void Update() override; virtual void SendInitData(Player& player, net::OutMessage& msg) const override; - virtual void OnContact(float impulse) override; + virtual void OnContact(const collision::ContactInfo& info) override; void SetInput(VehicleInputType type, bool enable); void SetInputs(VehicleInputFlags inputs) { in_ = inputs; } @@ -65,9 +66,14 @@ private: void UpdateCrash(); void UpdateWheels(); void UpdateSyncState(); + VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const; void SendUpdateMsg(); + void WriteDeformSync(net::OutMessage& msg) const; + void Deform(const glm::vec3& pos, const glm::vec3& deform, float radius); + void SendDeformMsg(const net::PositionQ& pos, const net::PositionQ& deform); + private: std::string model_name_; std::shared_ptr model_; @@ -95,6 +101,8 @@ private: float crash_intensity_ = 0.0f; size_t no_crash_frames_ = 0; + + std::unique_ptr deformgrid_; }; } // namespace game \ No newline at end of file diff --git a/src/game/world.cpp b/src/game/world.cpp index 4c300c0..740d7e8 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -105,7 +105,7 @@ void game::World::HandleContacts() static std::vector to_destroy; to_destroy.clear(); - auto ProcessContact = [&](btRigidBody* body, btRigidBody* other_body, btManifoldPoint& pt) { + auto ProcessContact = [&](btRigidBody* body, btRigidBody* other_body, const btVector3& pos, const btVector3& normal, float impulse) { collision::ObjectType type; collision::ObjectFlags flags; collision::ObjectCallback* cb; @@ -113,7 +113,11 @@ void game::World::HandleContacts() if (cb && (flags & collision::OF_NOTIFY_CONTACT)) { - cb->OnContact(pt.getAppliedImpulse()); + collision::ContactInfo info; + info.pos = glm::vec3(pos.x(), pos.y(), pos.z()); + info.normal = glm::vec3(normal.x(), normal.y(), normal.z()); + info.impulse = impulse; + cb->OnContact(info); } if (type == collision::OT_MAP_OBJECT && (flags & collision::OF_DESTRUCTIBLE)) @@ -122,10 +126,10 @@ void game::World::HandleContacts() if (!col) return; - if (pt.getAppliedImpulse() > col->GetDestroyThreshold()) + if (impulse > col->GetDestroyThreshold()) { to_destroy.push_back(col->GetNum()); - other_body->applyCentralImpulse(pt.m_normalWorldOnB * pt.getAppliedImpulse() * 0.5f); + other_body->applyCentralImpulse(-normal * impulse * 0.5f); } return; @@ -143,8 +147,8 @@ void game::World::HandleContacts() for (int j = 0; j < contactManifold->getNumContacts(); j++) { btManifoldPoint& pt = contactManifold->getContactPoint(j); - ProcessContact(body0, body1, pt); - ProcessContact(body1, body0, pt); + ProcessContact(body0, body1, pt.m_localPointA, -pt.m_normalWorldOnB, pt.getAppliedImpulse()); + ProcessContact(body1, body0, pt.m_localPointB, pt.m_normalWorldOnB, pt.getAppliedImpulse()); } } diff --git a/src/gameview/vehicleview.cpp b/src/gameview/vehicleview.cpp index de391c7..9d4a4ae 100644 --- a/src/gameview/vehicleview.cpp +++ b/src/gameview/vehicleview.cpp @@ -3,8 +3,10 @@ #include "assets/cache.hpp" #include "net/utils.hpp" #include "worldview.hpp" +#include "utils/random.hpp" #include +#include game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) : EntityView(world, msg) @@ -16,6 +18,7 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh"); mesh_ = *model_->GetModel()->GetMesh(); + InitMesh(); auto& modelwheels = model_->GetWheels(); wheels_.resize(modelwheels.size()); @@ -29,7 +32,10 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) if (!ReadState(&msg)) throw EntityInitError(); - + + if (!ReadDeformSync(msg)) + throw EntityInitError(); + // init the other transform to identical root_trans_[0] = root_trans_[1]; root_.local = root_trans_[0]; @@ -43,6 +49,8 @@ bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& m { switch (type) { + case net::EMSG_DEFORM: + return ProcessDeformMsg(msg); default: return Super::ProcessMsg(type, msg); } @@ -141,6 +149,44 @@ void game::view::VehicleView::Draw(const DrawArgs& args) args.dlist.AddSurface(cmd); } } + + // temp deforms + for (const auto& [pos, deform] : debug_deforms_) + { + glm::vec3 start = root_.matrix * glm::vec4(pos, 1.0f); + glm::vec3 end = root_.matrix * glm::vec4(pos + deform, 1.0f); + glm::vec3 end2 = end + glm::vec3(0.0f, 0.0f, 0.1f); + + args.dlist.AddBeam(start, end, 0xFFFFFF00, 0.01f); + args.dlist.AddBeam(end, end2, 0xFFFF00FF, 0.01f); + } +} + +void game::view::VehicleView::InitMesh() +{ + gfx::DeformGridInfo info{}; + info.min = glm::vec3(-1.0f, -2.5f, 0.10f); + info.max = glm::vec3(1.0f, 2.0f, 1.8f); + info.res = glm::ivec3(8, 16, 8); + info.max_offset = 0.1f; + deform_ = std::make_unique(info); + + for (auto& surface : mesh_.surfaces) + { + surface.deform_tex = deform_->tex; + surface.sflags |= gfx::SF_DEFORM_GRID; + } + + // for (size_t i = 0; i < 20; ++i) + // { + // glm::vec3 pos(RandomFloat(-1.0f, 1.0f), RandomFloat(-2.0f, 2.0f), RandomFloat(0.0f, 2.0f)); + // glm::vec3 impulse(RandomFloat(-1.0f, 1.0f), RandomFloat(-1.0f, 1.0f), RandomFloat(-1.0f, 1.0f)); + // impulse *= 0.05f; + + // deform_->grid.ApplyImpulse(pos, impulse, 1.0f); + // } + + deform_->tex->SetData(deform_->grid.GetData()); } bool game::view::VehicleView::ReadState(net::InMessage* msg) @@ -192,7 +238,7 @@ bool game::view::VehicleView::ReadState(net::InMessage* msg) return false; } - + float steering = sync_.steering.Decode(); // wheels @@ -218,3 +264,58 @@ bool game::view::VehicleView::ReadState(net::InMessage* msg) } return true; } + +bool game::view::VehicleView::ReadDeformSync(net::InMessage& msg) +{ + net::NumTexels numtexels; + if (!msg.Read(numtexels)) + return false; + + auto texels = deform_->grid.GetData(); + std::ranges::fill(texels, glm::i8vec3(0)); + + size_t current = 0; + for (size_t i = 0; i < numtexels; ++i) + { + int64_t diff; + if (!msg.ReadVarInt(diff)) + return false; + + current += static_cast(diff); + + if (current >= texels.size()) + return false; + + auto& texel = texels[current]; + for (size_t j = 0; j < 3; ++j) + { + if (!msg.Read(texel[j])) + return false; + } + } + + deform_->tex->SetData(deform_->grid.GetData()); + + return true; +} + +bool game::view::VehicleView::ProcessDeformMsg(net::InMessage& msg) +{ + net::PositionQ pos_q, deform_q; + if (!net::ReadPositionQ(msg, pos_q) || !net::ReadPositionQ(msg, deform_q)) + return false; + + + glm::vec3 pos, deform; + net::DecodePosition(pos_q, pos); + net::DecodePosition(deform_q, deform); + + deform_->grid.ApplyImpulse(pos, deform, 0.3f); + deform_->tex->SetData(deform_->grid.GetData()); + + //debug_deforms_.emplace_back(std::make_tuple(pos, deform)); + + return true; + + +} diff --git a/src/gameview/vehicleview.hpp b/src/gameview/vehicleview.hpp index d6d76ca..cf23484 100644 --- a/src/gameview/vehicleview.hpp +++ b/src/gameview/vehicleview.hpp @@ -4,6 +4,7 @@ #include "assets/vehiclemdl.hpp" #include "game/vehicle_sync.hpp" +#include "game/deform_grid.hpp" #include @@ -19,6 +20,14 @@ struct VehicleWheelViewInfo float rotation = 0.0f; }; +struct VehicleDeformView +{ + DeformGrid grid; + std::shared_ptr tex; + + VehicleDeformView(const gfx::DeformGridInfo& info) : grid(info), tex(std::make_shared(info)) {} +}; + class VehicleView : public EntityView { using Super = EntityView; @@ -32,8 +41,13 @@ public: virtual void Draw(const DrawArgs& args) override; private: + void InitMesh(); + bool ReadState(net::InMessage* msg); + bool ReadDeformSync(net::InMessage& msg); + bool ProcessDeformMsg(net::InMessage& msg); + private: std::shared_ptr model_; assets::Mesh mesh_; @@ -51,6 +65,8 @@ private: audio::SoundSource* snd_accel_src_ = nullptr; bool windows_broken_ = false; + std::unique_ptr deform_; + std::vector> debug_deforms_; }; } \ No newline at end of file diff --git a/src/gfx/deform_grid_info.hpp b/src/gfx/deform_grid_info.hpp new file mode 100644 index 0000000..0d96235 --- /dev/null +++ b/src/gfx/deform_grid_info.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace gfx +{ + +struct DeformGridInfo +{ + glm::vec3 min; + glm::vec3 max; + glm::ivec3 res; + float max_offset; +}; + + +} \ No newline at end of file diff --git a/src/gfx/deform_texture.cpp b/src/gfx/deform_texture.cpp new file mode 100644 index 0000000..8e6c818 --- /dev/null +++ b/src/gfx/deform_texture.cpp @@ -0,0 +1,32 @@ +#include "deform_texture.hpp" + +#include + +gfx::DeformTexture::DeformTexture(const DeformGridInfo& info) : info_(info) +{ + glGenTextures(1, &id_); + + if (!id_) + throw std::runtime_error("Nelze vytvorit texturu!"); + + glBindTexture(GL_TEXTURE_3D, id_); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_3D, 0); +} + +void gfx::DeformTexture::SetData(std::span data) +{ + glBindTexture(GL_TEXTURE_3D, id_); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB8_SNORM, info_.res.x, info_.res.y, info_.res.z, 0, GL_RGB, GL_BYTE, data.data()); +} + +gfx::DeformTexture::~DeformTexture() +{ + glDeleteTextures(1, &id_); +} diff --git a/src/gfx/deform_texture.hpp b/src/gfx/deform_texture.hpp new file mode 100644 index 0000000..1de8906 --- /dev/null +++ b/src/gfx/deform_texture.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "client/gl.hpp" +#include "utils/defs.hpp" +#include "deform_grid_info.hpp" + +namespace gfx +{ + +class DeformTexture +{ +public: + DeformTexture(const DeformGridInfo& info); + DELETE_COPY_MOVE(DeformTexture) + + void SetData(std::span data); + + const DeformGridInfo& GetInfo() const { return info_; } + GLuint GetId() const { return id_; } + + ~DeformTexture(); + +private: + const DeformGridInfo info_; + GLuint id_; + +}; + + + + +} \ No newline at end of file diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index ecc144b..cc33f9c 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -16,6 +16,7 @@ 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); @@ -104,6 +105,7 @@ void gfx::Renderer::InvalidateShaders() { InvalidateMeshShader(mesh_shader_); InvalidateMeshShader(skel_mesh_shader_); + InvalidateMeshShader(deform_mesh_shader_); } void gfx::Renderer::InvalidateMeshShader(MeshShader& mshader) @@ -171,6 +173,7 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi const gfx::Texture* last_texture = nullptr; const gfx::VertexArray* last_vao = nullptr; const gfx::UniformBuffer* last_skin = nullptr; + const DeformTexture* last_deform = nullptr; InvalidateShaders(); // enable depth test @@ -199,6 +202,7 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi 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 deform_flag = surface->sflags & SF_DEFORM_GRID; // sync 2sided if (last_twosided != twosided_flag) @@ -212,7 +216,7 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi } // select shader - MeshShader& mshader = skeletal_flag ? skel_mesh_shader_ : mesh_shader_; + MeshShader& mshader = skeletal_flag ? skel_mesh_shader_ : (deform_flag ? deform_mesh_shader_ : mesh_shader_); SetupMeshShader(mshader, params); // set model matrix @@ -290,16 +294,35 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi if (last_texture != surface->texture.get()) { GLuint tex_id = surface->texture ? surface->texture->GetId() : 0; + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex_id); last_texture = surface->texture.get(); } - + // bind skinning UBO if (cmd.skinning && last_skin != cmd.skinning) { - glBindBufferBase(GL_UNIFORM_BUFFER, 0, cmd.skinning->GetId()); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, cmd.skinning->GetId()); last_skin = cmd.skinning; } + + // bind deform texture + if (deform_flag && surface->deform_tex.get() != last_deform) + { + const auto& deform_tex = *surface->deform_tex; + GLuint tex_id = deform_tex.GetId(); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_3D, tex_id); + last_deform = &deform_tex; + + // update deform tex info + const auto& deform_info = deform_tex.GetInfo(); + glm::mat3 deform_info_mat; + 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]); + } // bind VAO if (last_vao != surface->va.get()) @@ -428,6 +451,8 @@ void gfx::Renderer::DrawHudList(std::span queue, const DrawListParam const gfx::Texture* last_texture = nullptr; const gfx::VertexArray* last_vao = nullptr; + glActiveTexture(GL_TEXTURE0); + for (const auto& cmd : queue) { if (!cmd.va || !cmd.texture) diff --git a/src/gfx/renderer.hpp b/src/gfx/renderer.hpp index 1280bfe..9d0b0fd 100644 --- a/src/gfx/renderer.hpp +++ b/src/gfx/renderer.hpp @@ -52,6 +52,7 @@ namespace gfx private: MeshShader mesh_shader_; MeshShader skel_mesh_shader_; + MeshShader deform_mesh_shader_; std::unique_ptr solid_shader_; std::unique_ptr beam_segments_vbo_; diff --git a/src/gfx/shader.cpp b/src/gfx/shader.cpp index 1108abb..49414ad 100644 --- a/src/gfx/shader.cpp +++ b/src/gfx/shader.cpp @@ -11,6 +11,8 @@ static const char* const s_uni_names[] = { "u_color", // SU_COLOR "u_flags", // SU_FLAGS "u_camera", // SU_CAMERA + "u_deform_tex", // SU_DEFORM_TEX + "u_deform_info", // SU_DEFORM_INFO }; // Vytvori shader z daneho zdroje @@ -93,6 +95,7 @@ void gfx::Shader::SetupBindings() glUseProgram(m_id); glUniform1i(m_uni[SU_TEX], 0); + glUniform1i(m_uni[SU_DEFORM_TEX], 1); // Bones UBO int ubo_index = glGetUniformBlockIndex(m_id, "Bones"); diff --git a/src/gfx/shader.hpp b/src/gfx/shader.hpp index 363fc73..f74e9c4 100644 --- a/src/gfx/shader.hpp +++ b/src/gfx/shader.hpp @@ -16,6 +16,8 @@ namespace gfx SU_COLOR, SU_FLAGS, SU_CAMERA, + SU_DEFORM_TEX, + SU_DEFORM_INFO, SU_COUNT }; diff --git a/src/gfx/shader_sources.cpp b/src/gfx/shader_sources.cpp index e1821ec..85d7c94 100644 --- a/src/gfx/shader_sources.cpp +++ b/src/gfx/shader_sources.cpp @@ -212,6 +212,76 @@ void main() { )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 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; + +#define SHF_CULL_ALPHA 1 +#define SHF_BACKGROUND 2 + +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_CULL_ALPHA) > 0) + { + if (o_color.a < 0.5) + discard; + } + else if ((u_flags & SHF_BACKGROUND) > 0) + { + // blend with bg + o_color = mix(u_color, o_color, o_color.a); + } + + 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( diff --git a/src/gfx/shader_sources.hpp b/src/gfx/shader_sources.hpp index a7b7e03..4a89f7a 100644 --- a/src/gfx/shader_sources.hpp +++ b/src/gfx/shader_sources.hpp @@ -13,6 +13,9 @@ namespace gfx SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG, + SS_DEFORM_MESH_VERT, + SS_DEFORM_MESH_FRAG, + SS_SOLID_VERT, SS_SOLID_FRAG, diff --git a/src/gfx/surface.hpp b/src/gfx/surface.hpp index 8193f4d..4a3d190 100644 --- a/src/gfx/surface.hpp +++ b/src/gfx/surface.hpp @@ -2,6 +2,7 @@ #include "texture.hpp" #include "vertex_array.hpp" +#include "deform_texture.hpp" namespace gfx { @@ -24,16 +25,18 @@ enum SurfaceFlag : SurfaceFlags SF_BLEND = 0x02, // enable blending, disable depth write SF_BLEND_ADDITIVE = 0x04, // use additive blending instead of opacity SF_OBJECT_COLOR = 0x08, // use object color for background instead of alpha culling + SF_DEFORM_GRID = 0x10, // use deform grid }; struct Surface { - std::shared_ptr texture; - std::shared_ptr va; + std::shared_ptr texture; + std::shared_ptr va; size_t first = 0; // first triangle VA EBO size_t count = 0; // number of triangles MeshFlags mflags = MF_NONE; SurfaceFlags sflags = SF_NONE; + std::shared_ptr deform_tex; }; } // namespace gfx \ No newline at end of file diff --git a/src/net/defs.hpp b/src/net/defs.hpp index d90b8bc..6a8d97a 100644 --- a/src/net/defs.hpp +++ b/src/net/defs.hpp @@ -90,6 +90,7 @@ enum EntMsgType : uint8_t EMSG_ATTACH, // EMSG_UPDATE, // deprecated EMSG_PLAYSOUND, + EMSG_DEFORM, }; using PositionElemQ = Quantized; @@ -128,4 +129,6 @@ using ClothesName = FixedStr<32>; using ObjNum = uint16_t; using ObjCount = ObjNum; +using NumTexels = uint16_t; + } // namespace net \ No newline at end of file