Character clothes

This commit is contained in:
tovjemam 2026-02-12 18:42:28 +01:00
parent 8ad81bfb6a
commit 698bcafec0
6 changed files with 152 additions and 25 deletions

View File

@ -65,6 +65,14 @@ void game::Character::SendInitData(Player& player, net::OutMessage& msg) const
{
Super::SendInitData(player, msg);
// write clothes
msg.Write<net::NumClothes>(clothes_.size());
for (const auto& clothes : clothes_)
{
msg.Write(net::ClothesName(clothes.name));
net::WriteRGB(msg, clothes.color);
}
// write state against default
static const CharacterSyncState default_state;
size_t fields_pos = msg.Reserve<CharacterSyncFieldFlags>();
@ -113,6 +121,11 @@ void game::Character::SetPosition(const glm::vec3& position)
bt_ghost_.setWorldTransform(trans);
}
void game::Character::AddClothes(std::string name, const glm::vec3& color)
{
clothes_.emplace_back(std::move(name), color);
}
game::Character::~Character()
{
btDynamicsWorld& bt_world = world_.GetBtWorld();

View File

@ -33,6 +33,12 @@ struct CharacterInfo
CapsuleShape shape = CapsuleShape(0.3f, 0.75f);
};
struct CharacterClothes
{
std::string name;
glm::vec3 color;
};
class Character : public Entity
{
public:
@ -50,6 +56,8 @@ public:
void SetPosition(const glm::vec3& position);
void AddClothes(std::string name, const glm::vec3& color);
~Character() override;
private:
@ -85,6 +93,8 @@ private:
CharacterSyncState sync_[2];
size_t sync_current_ = 0;
std::vector<CharacterClothes> clothes_;
};
} // namespace game

View File

@ -233,6 +233,20 @@ void game::OpenWorld::RemoveVehicle(Player& player)
}
}
static glm::vec3 GetRandomColor()
{
glm::vec3 color;
// shittiest way to do it
for (int i = 0; i < 3; ++i)
{
net::ColorQ qcol;
qcol.value = rand() % 256;
color[i] = qcol.Decode();
}
return color;
}
void game::OpenWorld::SpawnCharacter(Player& player)
{
RemoveCharacter(player);
@ -242,6 +256,10 @@ void game::OpenWorld::SpawnCharacter(Player& player)
character.SetNametag("player (" + std::to_string(character.GetEntNum()) + ")");
character.SetPosition({ 100.0f, 100.0f, 5.0f });
// add clothes
character.AddClothes("tshirt", GetRandomColor());
character.AddClothes("shorts", GetRandomColor());
player.SetCamera(character.GetEntNum());
player_characters_[&player] = &character;
@ -531,19 +549,6 @@ static const char* GetRandomCarModel()
return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
}
static glm::vec3 GetRandomColor()
{
glm::vec3 color;
// shittiest way to do it
for (int i = 0; i < 3; ++i)
{
net::ColorQ qcol;
qcol.value = rand() % 256;
color[i] = qcol.Decode();
}
return color;
}
void game::OpenWorld::SpawnBot()
{

View File

@ -4,18 +4,37 @@
#include "net/utils.hpp"
#include "worldview.hpp"
game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg) : EntityView(world, msg), ubo_(sk_)
{
basemodel_ = assets::CacheManager::GetModel("data/human.mdl");
sk_ = SkeletonInstance(basemodel_->GetSkeleton(), &root_);
ubo_.Update();
ubo_valid_ = true;
// read clothes
net::NumClothes num_clothes = 0;
if (!msg.Read(num_clothes))
throw EntityInitError();
for (net::NumClothes i = 0; i < num_clothes; ++i)
{
net::ClothesName name;
glm::vec3 color;
if (!msg.Read(name) || !net::ReadRGB(msg, color))
throw EntityInitError();
AddClothes(name, color);
}
UpdateSurfaceMask();
// read initial state
if (!ReadState(msg))
throw EntityInitError();
states_[0] = states_[1]; // lerp from the read state to avoid jump
basemodel_ = assets::CacheManager::GetModel("data/human.mdl");
sk_ = SkeletonInstance(basemodel_->GetSkeleton(), &root_);
ubo_.Update();
ubo_valid_ = true;
}
bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage& msg)
@ -73,19 +92,37 @@ void game::view::CharacterView::Draw(const DrawArgs& args)
// args.dlist.AddBeam(p0, p1, 0xFF00EEEE, 0.01f);
//}
// draw human
// update skinning matrices
if (!ubo_valid_)
{
ubo_.Update();
ubo_valid_ = true;
}
const auto& mesh = *basemodel_->GetMesh();
for (const auto& surface : mesh.surfaces)
// draw clothes
for (const auto& clothes : clothes_)
{
const auto& mesh = *clothes.model->GetMesh();
for (const auto& surface : mesh.surfaces)
{
gfx::DrawSurfaceCmd cmd;
cmd.surface = &surface;
cmd.matrices = &root_.matrix;
cmd.skinning = &ubo_;
cmd.color = &clothes.color;
args.dlist.AddSurface(cmd);
}
}
// draw basemodel
const auto& mesh = *basemodel_->GetMesh();
for (size_t i = 0; i < mesh.surfaces.size(); ++i)
{
if (!(surfacemask_ & (1 << i))) // hidden by clothes?
continue;
gfx::DrawSurfaceCmd cmd;
cmd.surface = &surface;
cmd.surface = &mesh.surfaces[i];
cmd.matrices = &root_.matrix;
cmd.skinning = &ubo_;
args.dlist.AddSurface(cmd);
@ -156,3 +193,48 @@ bool game::view::CharacterView::ProcessUpdateMsg(net::InMessage& msg)
{
return ReadState(msg);
}
game::view::CharacterView::SurfaceMask game::view::CharacterView::GetSurfaceMask(const std::string& name)
{
const auto& surface_names = basemodel_->GetMesh()->surface_names;
auto it = surface_names.find(name);
if (it != surface_names.end())
{
return 1 << it->second;
}
return 0;
}
void game::view::CharacterView::UpdateSurfaceMask()
{
surfacemask_ = 0xFFFFFFFF;
for (const auto& clothes : clothes_)
{
surfacemask_ &= ~clothes.surfacemask;
}
}
void game::view::CharacterView::AddClothes(const std::string& name, const glm::vec3& color)
{
CharacterViewClothes c;
c.color = glm::vec4(color, 1.0f);
if (name == "tshirt")
{
c.model = assets::CacheManager::GetModel("data/tshirt.mdl");
c.surfacemask = GetSurfaceMask("upperbody");
}
else if (name == "shorts")
{
c.model = assets::CacheManager::GetModel("data/shorts.mdl");
c.surfacemask = GetSurfaceMask("upperlegs");
}
else
{
return; // unknown??
}
clothes_.emplace_back(std::move(c));
}

View File

@ -17,10 +17,18 @@ struct CharacterViewState
float loco_phase = 0.0f;
};
struct CharacterViewClothes
{
std::shared_ptr<const assets::Model> model;
glm::vec4 color = glm::vec4(1.0f);
uint32_t surfacemask = 0;
};
class CharacterView : public EntityView
{
public:
using Super = EntityView;
using SurfaceMask = uint32_t;
CharacterView(WorldView& world, net::InMessage& msg);
DELETE_COPY_MOVE(CharacterView)
@ -33,6 +41,11 @@ private:
bool ReadState(net::InMessage& msg);
bool ProcessUpdateMsg(net::InMessage& msg);
SurfaceMask GetSurfaceMask(const std::string& name);
void UpdateSurfaceMask();
void AddClothes(const std::string& name, const glm::vec3& color);
private:
float yaw_ = 0.0f;
@ -43,12 +56,13 @@ private:
CharacterAnimState animstate_;
uint32_t surfacemask_ = 0xFFFFFFFF;
std::vector<CharacterViewClothes> clothes_;
// sync
CharacterSyncState sync_;
CharacterViewState states_[2];
float update_time_ = 0.0f;
};
}

View File

@ -103,4 +103,7 @@ using NameTag = FixedStr<64>;
using AnimBlendQ = Quantized<uint8_t, 0, 1>;
using AnimTimeQ = Quantized<uint8_t, 0, 1>;
using NumClothes = uint8_t;
using ClothesName = FixedStr<32>;
} // namespace net