Vehicle deform

This commit is contained in:
tovjemam 2026-03-06 22:32:45 +01:00
parent 8369181e9a
commit 3e284af672
21 changed files with 510 additions and 22 deletions

View File

@ -27,6 +27,8 @@ set(COMMON_SOURCES
"src/collision/trianglemesh.cpp" "src/collision/trianglemesh.cpp"
"src/game/character_anim_state.hpp" "src/game/character_anim_state.hpp"
"src/game/character_anim_state.cpp" "src/game/character_anim_state.cpp"
"src/game/deform_grid.hpp"
"src/game/deform_grid.cpp"
"src/game/player_input.hpp" "src/game/player_input.hpp"
"src/game/simple_entity_sync.hpp" "src/game/simple_entity_sync.hpp"
"src/game/skeletoninstance.hpp" "src/game/skeletoninstance.hpp"
@ -90,6 +92,8 @@ set(CLIENT_ONLY_SOURCES
"src/gameview/worldview.cpp" "src/gameview/worldview.cpp"
"src/gfx/buffer_object.cpp" "src/gfx/buffer_object.cpp"
"src/gfx/buffer_object.hpp" "src/gfx/buffer_object.hpp"
"src/gfx/deform_texture.hpp"
"src/gfx/deform_texture.cpp"
"src/gfx/draw_list.hpp" "src/gfx/draw_list.hpp"
"src/gfx/frustum.hpp" "src/gfx/frustum.hpp"
"src/gfx/frustum.cpp" "src/gfx/frustum.cpp"

View File

@ -21,12 +21,19 @@ enum ObjectFlag : ObjectFlags
OF_NOTIFY_CONTACT = 0x02, OF_NOTIFY_CONTACT = 0x02,
}; };
struct ContactInfo
{
glm::vec3 pos;
glm::vec3 normal;
float impulse;
};
class ObjectCallback class ObjectCallback
{ {
public: public:
ObjectCallback() = default; ObjectCallback() = default;
virtual void OnContact(float impulse) {} virtual void OnContact(const ContactInfo& info) {}
virtual ~ObjectCallback() = default; virtual ~ObjectCallback() = default;
}; };

46
src/game/deform_grid.cpp Normal file
View File

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

41
src/game/deform_grid.hpp Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <vector>
#include <span>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtx/norm.hpp>
#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<glm::i8vec3> GetData() { return data_; }
std::span<const glm::i8vec3> 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<size_t>(pos.x + (pos.y + pos.z * info_.res.y) * info_.res.x); }
private:
const gfx::DeformGridInfo info_;
std::vector<glm::i8vec3> data_;
};
}

View File

@ -47,12 +47,12 @@ game::OpenWorld::OpenWorld() : World("openworld")
SpawnBot(); SpawnBot();
} }
auto& veh = Spawn<game::DrivableVehicle>("twingo", glm::vec3{0.8f, 0.1f, 0.1f}); auto& veh = Spawn<game::DrivableVehicle>("twingo", glm::vec3{1.0f, 0.8f, 0.1f});
veh.SetPosition({110.0f, 100.0f, 5.0f}); veh.SetPosition({110.0f, 100.0f, 5.0f});
constexpr size_t in_row = 20; 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] { Schedule(i * 40, [this, i] {
size_t col = i % in_row; size_t col = i % in_row;

View File

@ -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.addRigidBody(body_.get(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter);
bt_world.addAction(vehicle_.get()); 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<DeformGrid>(info);
Update(); Update();
} }
@ -127,20 +135,22 @@ void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const
size_t fields_pos = msg.Reserve<VehicleSyncFieldFlags>(); size_t fields_pos = msg.Reserve<VehicleSyncFieldFlags>();
auto fields = WriteState(msg, default_state); auto fields = WriteState(msg, default_state);
msg.WriteAt(fields_pos, fields); 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; return;
if (window_health_ > 0.0f) if (window_health_ > 0.0f)
{ {
window_health_ -= impulse; window_health_ -= info.impulse;
if (window_health_ <= 0.0f) // just broken 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) void game::Vehicle::SetInput(VehicleInputType type, bool enable)
@ -485,3 +500,56 @@ void game::Vehicle::SendUpdateMsg()
msg.WriteAt(fields_pos, fields); 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>();
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);
}

View File

@ -9,6 +9,7 @@
#include "entity.hpp" #include "entity.hpp"
#include "world.hpp" #include "world.hpp"
#include "vehicle_sync.hpp" #include "vehicle_sync.hpp"
#include "deform_grid.hpp"
namespace game namespace game
{ {
@ -41,7 +42,7 @@ public:
virtual void Update() override; virtual void Update() override;
virtual void SendInitData(Player& player, net::OutMessage& msg) const 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 SetInput(VehicleInputType type, bool enable);
void SetInputs(VehicleInputFlags inputs) { in_ = inputs; } void SetInputs(VehicleInputFlags inputs) { in_ = inputs; }
@ -65,9 +66,14 @@ private:
void UpdateCrash(); void UpdateCrash();
void UpdateWheels(); void UpdateWheels();
void UpdateSyncState(); void UpdateSyncState();
VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const; VehicleSyncFieldFlags WriteState(net::OutMessage& msg, const VehicleSyncState& base) const;
void SendUpdateMsg(); 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: private:
std::string model_name_; std::string model_name_;
std::shared_ptr<const assets::VehicleModel> model_; std::shared_ptr<const assets::VehicleModel> model_;
@ -95,6 +101,8 @@ private:
float crash_intensity_ = 0.0f; float crash_intensity_ = 0.0f;
size_t no_crash_frames_ = 0; size_t no_crash_frames_ = 0;
std::unique_ptr<DeformGrid> deformgrid_;
}; };
} // namespace game } // namespace game

View File

@ -105,7 +105,7 @@ void game::World::HandleContacts()
static std::vector<net::ObjNum> to_destroy; static std::vector<net::ObjNum> to_destroy;
to_destroy.clear(); 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::ObjectType type;
collision::ObjectFlags flags; collision::ObjectFlags flags;
collision::ObjectCallback* cb; collision::ObjectCallback* cb;
@ -113,7 +113,11 @@ void game::World::HandleContacts()
if (cb && (flags & collision::OF_NOTIFY_CONTACT)) 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)) if (type == collision::OT_MAP_OBJECT && (flags & collision::OF_DESTRUCTIBLE))
@ -122,10 +126,10 @@ void game::World::HandleContacts()
if (!col) if (!col)
return; return;
if (pt.getAppliedImpulse() > col->GetDestroyThreshold()) if (impulse > col->GetDestroyThreshold())
{ {
to_destroy.push_back(col->GetNum()); to_destroy.push_back(col->GetNum());
other_body->applyCentralImpulse(pt.m_normalWorldOnB * pt.getAppliedImpulse() * 0.5f); other_body->applyCentralImpulse(-normal * impulse * 0.5f);
} }
return; return;
@ -143,8 +147,8 @@ void game::World::HandleContacts()
for (int j = 0; j < contactManifold->getNumContacts(); j++) for (int j = 0; j < contactManifold->getNumContacts(); j++)
{ {
btManifoldPoint& pt = contactManifold->getContactPoint(j); btManifoldPoint& pt = contactManifold->getContactPoint(j);
ProcessContact(body0, body1, pt); ProcessContact(body0, body1, pt.m_localPointA, -pt.m_normalWorldOnB, pt.getAppliedImpulse());
ProcessContact(body1, body0, pt); ProcessContact(body1, body0, pt.m_localPointB, pt.m_normalWorldOnB, pt.getAppliedImpulse());
} }
} }

View File

@ -3,8 +3,10 @@
#include "assets/cache.hpp" #include "assets/cache.hpp"
#include "net/utils.hpp" #include "net/utils.hpp"
#include "worldview.hpp" #include "worldview.hpp"
#include "utils/random.hpp"
#include <iostream> #include <iostream>
#include <ranges>
game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg) game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
: EntityView(world, 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"); model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh");
mesh_ = *model_->GetModel()->GetMesh(); mesh_ = *model_->GetModel()->GetMesh();
InitMesh();
auto& modelwheels = model_->GetWheels(); auto& modelwheels = model_->GetWheels();
wheels_.resize(modelwheels.size()); wheels_.resize(modelwheels.size());
@ -30,6 +33,9 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
if (!ReadState(&msg)) if (!ReadState(&msg))
throw EntityInitError(); throw EntityInitError();
if (!ReadDeformSync(msg))
throw EntityInitError();
// init the other transform to identical // init the other transform to identical
root_trans_[0] = root_trans_[1]; root_trans_[0] = root_trans_[1];
root_.local = root_trans_[0]; root_.local = root_trans_[0];
@ -43,6 +49,8 @@ bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& m
{ {
switch (type) switch (type)
{ {
case net::EMSG_DEFORM:
return ProcessDeformMsg(msg);
default: default:
return Super::ProcessMsg(type, msg); return Super::ProcessMsg(type, msg);
} }
@ -141,6 +149,44 @@ void game::view::VehicleView::Draw(const DrawArgs& args)
args.dlist.AddSurface(cmd); 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<VehicleDeformView>(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) bool game::view::VehicleView::ReadState(net::InMessage* msg)
@ -218,3 +264,58 @@ bool game::view::VehicleView::ReadState(net::InMessage* msg)
} }
return true; 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<size_t>(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;
}

View File

@ -4,6 +4,7 @@
#include "assets/vehiclemdl.hpp" #include "assets/vehiclemdl.hpp"
#include "game/vehicle_sync.hpp" #include "game/vehicle_sync.hpp"
#include "game/deform_grid.hpp"
#include <chrono> #include <chrono>
@ -19,6 +20,14 @@ struct VehicleWheelViewInfo
float rotation = 0.0f; float rotation = 0.0f;
}; };
struct VehicleDeformView
{
DeformGrid grid;
std::shared_ptr<gfx::DeformTexture> tex;
VehicleDeformView(const gfx::DeformGridInfo& info) : grid(info), tex(std::make_shared<gfx::DeformTexture>(info)) {}
};
class VehicleView : public EntityView class VehicleView : public EntityView
{ {
using Super = EntityView; using Super = EntityView;
@ -32,8 +41,13 @@ public:
virtual void Draw(const DrawArgs& args) override; virtual void Draw(const DrawArgs& args) override;
private: private:
void InitMesh();
bool ReadState(net::InMessage* msg); bool ReadState(net::InMessage* msg);
bool ReadDeformSync(net::InMessage& msg);
bool ProcessDeformMsg(net::InMessage& msg);
private: private:
std::shared_ptr<const assets::VehicleModel> model_; std::shared_ptr<const assets::VehicleModel> model_;
assets::Mesh mesh_; assets::Mesh mesh_;
@ -51,6 +65,8 @@ private:
audio::SoundSource* snd_accel_src_ = nullptr; audio::SoundSource* snd_accel_src_ = nullptr;
bool windows_broken_ = false; bool windows_broken_ = false;
std::unique_ptr<VehicleDeformView> deform_;
std::vector<std::tuple<glm::vec3, glm::vec3>> debug_deforms_;
}; };
} }

View File

@ -0,0 +1,17 @@
#pragma once
#include <glm/glm.hpp>
namespace gfx
{
struct DeformGridInfo
{
glm::vec3 min;
glm::vec3 max;
glm::ivec3 res;
float max_offset;
};
}

View File

@ -0,0 +1,32 @@
#include "deform_texture.hpp"
#include <stdexcept>
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<const glm::i8vec3> 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_);
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <span>
#include <glm/glm.hpp>
#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<const glm::i8vec3> data);
const DeformGridInfo& GetInfo() const { return info_; }
GLuint GetId() const { return id_; }
~DeformTexture();
private:
const DeformGridInfo info_;
GLuint id_;
};
}

View File

@ -16,6 +16,7 @@ gfx::Renderer::Renderer()
{ {
ShaderSources::MakeShader(mesh_shader_.shader, SS_MESH_VERT, SS_MESH_FRAG); 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(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(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); ShaderSources::MakeShader(beam_shader_, SS_BEAM_VERT, SS_BEAM_FRAG);
@ -104,6 +105,7 @@ void gfx::Renderer::InvalidateShaders()
{ {
InvalidateMeshShader(mesh_shader_); InvalidateMeshShader(mesh_shader_);
InvalidateMeshShader(skel_mesh_shader_); InvalidateMeshShader(skel_mesh_shader_);
InvalidateMeshShader(deform_mesh_shader_);
} }
void gfx::Renderer::InvalidateMeshShader(MeshShader& mshader) void gfx::Renderer::InvalidateMeshShader(MeshShader& mshader)
@ -171,6 +173,7 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
const gfx::Texture* last_texture = nullptr; const gfx::Texture* last_texture = nullptr;
const gfx::VertexArray* last_vao = nullptr; const gfx::VertexArray* last_vao = nullptr;
const gfx::UniformBuffer<glm::mat4>* last_skin = nullptr; const gfx::UniformBuffer<glm::mat4>* last_skin = nullptr;
const DeformTexture* last_deform = nullptr;
InvalidateShaders(); InvalidateShaders();
// enable depth test // enable depth test
@ -199,6 +202,7 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
const bool twosided_flag = surface->sflags & SF_2SIDED; const bool twosided_flag = surface->sflags & SF_2SIDED;
const bool blend_flag = surface->sflags & SF_BLEND; const bool blend_flag = surface->sflags & SF_BLEND;
const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR; const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR;
const bool deform_flag = surface->sflags & SF_DEFORM_GRID;
// sync 2sided // sync 2sided
if (last_twosided != twosided_flag) if (last_twosided != twosided_flag)
@ -212,7 +216,7 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
} }
// select shader // 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); SetupMeshShader(mshader, params);
// set model matrix // set model matrix
@ -290,6 +294,7 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
if (last_texture != surface->texture.get()) if (last_texture != surface->texture.get())
{ {
GLuint tex_id = surface->texture ? surface->texture->GetId() : 0; GLuint tex_id = surface->texture ? surface->texture->GetId() : 0;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id); glBindTexture(GL_TEXTURE_2D, tex_id);
last_texture = surface->texture.get(); last_texture = surface->texture.get();
} }
@ -301,6 +306,24 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
last_skin = cmd.skinning; 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 // bind VAO
if (last_vao != surface->va.get()) if (last_vao != surface->va.get())
{ {
@ -428,6 +451,8 @@ void gfx::Renderer::DrawHudList(std::span<DrawHudCmd> queue, const DrawListParam
const gfx::Texture* last_texture = nullptr; const gfx::Texture* last_texture = nullptr;
const gfx::VertexArray* last_vao = nullptr; const gfx::VertexArray* last_vao = nullptr;
glActiveTexture(GL_TEXTURE0);
for (const auto& cmd : queue) for (const auto& cmd : queue)
{ {
if (!cmd.va || !cmd.texture) if (!cmd.va || !cmd.texture)

View File

@ -52,6 +52,7 @@ namespace gfx
private: private:
MeshShader mesh_shader_; MeshShader mesh_shader_;
MeshShader skel_mesh_shader_; MeshShader skel_mesh_shader_;
MeshShader deform_mesh_shader_;
std::unique_ptr<Shader> solid_shader_; std::unique_ptr<Shader> solid_shader_;
std::unique_ptr<BufferObject> beam_segments_vbo_; std::unique_ptr<BufferObject> beam_segments_vbo_;

View File

@ -11,6 +11,8 @@ static const char* const s_uni_names[] = {
"u_color", // SU_COLOR "u_color", // SU_COLOR
"u_flags", // SU_FLAGS "u_flags", // SU_FLAGS
"u_camera", // SU_CAMERA "u_camera", // SU_CAMERA
"u_deform_tex", // SU_DEFORM_TEX
"u_deform_info", // SU_DEFORM_INFO
}; };
// Vytvori shader z daneho zdroje // Vytvori shader z daneho zdroje
@ -93,6 +95,7 @@ void gfx::Shader::SetupBindings()
glUseProgram(m_id); glUseProgram(m_id);
glUniform1i(m_uni[SU_TEX], 0); glUniform1i(m_uni[SU_TEX], 0);
glUniform1i(m_uni[SU_DEFORM_TEX], 1);
// Bones UBO // Bones UBO
int ubo_index = glGetUniformBlockIndex(m_id, "Bones"); int ubo_index = glGetUniformBlockIndex(m_id, "Bones");

View File

@ -16,6 +16,8 @@ namespace gfx
SU_COLOR, SU_COLOR,
SU_FLAGS, SU_FLAGS,
SU_CAMERA, SU_CAMERA,
SU_DEFORM_TEX,
SU_DEFORM_INFO,
SU_COUNT SU_COUNT
}; };

View File

@ -212,6 +212,76 @@ void main() {
)GLSL", )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 // SS_SOLID_VERT
SHADER_HEADER SHADER_HEADER
R"GLSL( R"GLSL(

View File

@ -13,6 +13,9 @@ namespace gfx
SS_SKEL_MESH_VERT, SS_SKEL_MESH_VERT,
SS_SKEL_MESH_FRAG, SS_SKEL_MESH_FRAG,
SS_DEFORM_MESH_VERT,
SS_DEFORM_MESH_FRAG,
SS_SOLID_VERT, SS_SOLID_VERT,
SS_SOLID_FRAG, SS_SOLID_FRAG,

View File

@ -2,6 +2,7 @@
#include "texture.hpp" #include "texture.hpp"
#include "vertex_array.hpp" #include "vertex_array.hpp"
#include "deform_texture.hpp"
namespace gfx namespace gfx
{ {
@ -24,16 +25,18 @@ enum SurfaceFlag : SurfaceFlags
SF_BLEND = 0x02, // enable blending, disable depth write SF_BLEND = 0x02, // enable blending, disable depth write
SF_BLEND_ADDITIVE = 0x04, // use additive blending instead of opacity SF_BLEND_ADDITIVE = 0x04, // use additive blending instead of opacity
SF_OBJECT_COLOR = 0x08, // use object color for background instead of alpha culling SF_OBJECT_COLOR = 0x08, // use object color for background instead of alpha culling
SF_DEFORM_GRID = 0x10, // use deform grid
}; };
struct Surface struct Surface
{ {
std::shared_ptr<const gfx::Texture> texture; std::shared_ptr<const Texture> texture;
std::shared_ptr<const gfx::VertexArray> va; std::shared_ptr<const VertexArray> va;
size_t first = 0; // first triangle VA EBO size_t first = 0; // first triangle VA EBO
size_t count = 0; // number of triangles size_t count = 0; // number of triangles
MeshFlags mflags = MF_NONE; MeshFlags mflags = MF_NONE;
SurfaceFlags sflags = SF_NONE; SurfaceFlags sflags = SF_NONE;
std::shared_ptr<const DeformTexture> deform_tex;
}; };
} // namespace gfx } // namespace gfx

View File

@ -90,6 +90,7 @@ enum EntMsgType : uint8_t
EMSG_ATTACH, EMSG_ATTACH,
// EMSG_UPDATE, // deprecated // EMSG_UPDATE, // deprecated
EMSG_PLAYSOUND, EMSG_PLAYSOUND,
EMSG_DEFORM,
}; };
using PositionElemQ = Quantized<uint32_t, -10000, 10000, 1>; using PositionElemQ = Quantized<uint32_t, -10000, 10000, 1>;
@ -128,4 +129,6 @@ using ClothesName = FixedStr<32>;
using ObjNum = uint16_t; using ObjNum = uint16_t;
using ObjCount = ObjNum; using ObjCount = ObjNum;
using NumTexels = uint16_t;
} // namespace net } // namespace net