Destructible objects pt. 2
This commit is contained in:
parent
d6947a79d6
commit
986cbc12a6
@ -28,6 +28,7 @@ set(COMMON_SOURCES
|
|||||||
"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/player_input.hpp"
|
"src/game/player_input.hpp"
|
||||||
|
"src/game/simple_entity_sync.hpp"
|
||||||
"src/game/skeletoninstance.hpp"
|
"src/game/skeletoninstance.hpp"
|
||||||
"src/game/skeletoninstance.cpp"
|
"src/game/skeletoninstance.cpp"
|
||||||
"src/game/transform_node.hpp"
|
"src/game/transform_node.hpp"
|
||||||
@ -79,6 +80,8 @@ set(CLIENT_ONLY_SOURCES
|
|||||||
"src/gameview/entityview.cpp"
|
"src/gameview/entityview.cpp"
|
||||||
"src/gameview/mapinstanceview.hpp"
|
"src/gameview/mapinstanceview.hpp"
|
||||||
"src/gameview/mapinstanceview.cpp"
|
"src/gameview/mapinstanceview.cpp"
|
||||||
|
"src/gameview/simple_entity_view.hpp"
|
||||||
|
"src/gameview/simple_entity_view.cpp"
|
||||||
"src/gameview/skinning_ubo.hpp"
|
"src/gameview/skinning_ubo.hpp"
|
||||||
"src/gameview/skinning_ubo.cpp"
|
"src/gameview/skinning_ubo.cpp"
|
||||||
"src/gameview/vehicleview.hpp"
|
"src/gameview/vehicleview.hpp"
|
||||||
@ -116,6 +119,8 @@ set(SERVER_ONLY_SOURCES
|
|||||||
"src/game/character.cpp"
|
"src/game/character.cpp"
|
||||||
"src/game/controllable_character.hpp"
|
"src/game/controllable_character.hpp"
|
||||||
"src/game/controllable_character.cpp"
|
"src/game/controllable_character.cpp"
|
||||||
|
"src/game/destroyed_object.hpp"
|
||||||
|
"src/game/destroyed_object.cpp"
|
||||||
"src/game/drivable_vehicle.hpp"
|
"src/game/drivable_vehicle.hpp"
|
||||||
"src/game/drivable_vehicle.cpp"
|
"src/game/drivable_vehicle.cpp"
|
||||||
"src/game/entity.hpp"
|
"src/game/entity.hpp"
|
||||||
@ -132,6 +137,8 @@ set(SERVER_ONLY_SOURCES
|
|||||||
"src/game/player_character.cpp"
|
"src/game/player_character.cpp"
|
||||||
"src/game/player.hpp"
|
"src/game/player.hpp"
|
||||||
"src/game/player.cpp"
|
"src/game/player.cpp"
|
||||||
|
"src/game/simple_entity.hpp"
|
||||||
|
"src/game/simple_entity.cpp"
|
||||||
"src/game/usable.hpp"
|
"src/game/usable.hpp"
|
||||||
"src/game/vehicle.hpp"
|
"src/game/vehicle.hpp"
|
||||||
"src/game/vehicle.cpp"
|
"src/game/vehicle.cpp"
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::string& filename)
|
std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::string& filename)
|
||||||
{
|
{
|
||||||
auto model = std::make_shared<Model>();
|
auto model = std::make_shared<Model>();
|
||||||
|
model->name_ = filename; // TODO: name not filename
|
||||||
std::vector<glm::vec3> vert_pos; // rember for collision trimesh
|
std::vector<glm::vec3> vert_pos; // rember for collision trimesh
|
||||||
|
|
||||||
CLIENT_ONLY(MeshBuilder mb(gfx::MF_NONE);)
|
CLIENT_ONLY(MeshBuilder mb(gfx::MF_NONE);)
|
||||||
@ -45,7 +46,12 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
|||||||
vert_pos.emplace_back(pos);
|
vert_pos.emplace_back(pos);
|
||||||
|
|
||||||
if (temp_hull)
|
if (temp_hull)
|
||||||
temp_hull->addPoint(btVector3(pos.x, pos.y, pos.z), false);
|
{
|
||||||
|
auto offset_pos = pos - model->col_offset_;
|
||||||
|
|
||||||
|
temp_hull->addPoint(btVector3(offset_pos.x, offset_pos.y, offset_pos.z), false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
model->aabb_.AddPoint(pos);
|
model->aabb_.AddPoint(pos);
|
||||||
}
|
}
|
||||||
@ -65,8 +71,17 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
|||||||
|
|
||||||
if (model->cmesh_)
|
if (model->cmesh_)
|
||||||
{
|
{
|
||||||
// FIXME: possible index segfault
|
glm::vec3 p[3];
|
||||||
model->cmesh_->AddTriangle(vert_pos[indices[0]], vert_pos[indices[1]], vert_pos[indices[2]]);
|
for (size_t i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
size_t index = indices[i];
|
||||||
|
if (index >= vert_pos.size())
|
||||||
|
throw std::runtime_error("Vertex index out of bounds in model");
|
||||||
|
|
||||||
|
p[i] = vert_pos[index] - model->col_offset_;
|
||||||
|
}
|
||||||
|
|
||||||
|
model->cmesh_->AddTriangle(p[0], p[1], p[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command == "surface")
|
else if (command == "surface")
|
||||||
@ -141,6 +156,8 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
|||||||
glm::vec3 scale(trans.scale, sy, sz);
|
glm::vec3 scale(trans.scale, sy, sz);
|
||||||
trans.scale = 1.0f;
|
trans.scale = 1.0f;
|
||||||
|
|
||||||
|
trans.position -= model->col_offset_; // apply offset
|
||||||
|
|
||||||
if (!compound)
|
if (!compound)
|
||||||
{
|
{
|
||||||
compound = std::make_unique<btCompoundShape>();
|
compound = std::make_unique<btCompoundShape>();
|
||||||
@ -157,6 +174,12 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
|||||||
throw std::runtime_error("Unknown collision shape type: " + shape_type);
|
throw std::runtime_error("Unknown collision shape type: " + shape_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (command == "centerofmass")
|
||||||
|
{
|
||||||
|
glm::vec3 com;
|
||||||
|
iss >> com.x >> com.y >> com.z;
|
||||||
|
model->col_offset_ = com;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unknown command in model file: " + command);
|
throw std::runtime_error("Unknown command in model file: " + command);
|
||||||
|
|||||||
@ -41,6 +41,9 @@ public:
|
|||||||
Model() = default;
|
Model() = default;
|
||||||
static std::shared_ptr<const Model> LoadFromFile(const std::string& filename);
|
static std::shared_ptr<const Model> LoadFromFile(const std::string& filename);
|
||||||
|
|
||||||
|
const std::string& GetName() const { return name_; }
|
||||||
|
|
||||||
|
const glm::vec3& GetColOffset() const { return col_offset_; }
|
||||||
const collision::TriangleMesh* GetColMesh() const { return cmesh_.get(); }
|
const collision::TriangleMesh* GetColMesh() const { return cmesh_.get(); }
|
||||||
btCollisionShape* GetColShape() const { return cshape_.get(); }
|
btCollisionShape* GetColShape() const { return cshape_.get(); }
|
||||||
|
|
||||||
@ -49,6 +52,8 @@ public:
|
|||||||
const AABB3& GetAABB() const { return aabb_; }
|
const AABB3& GetAABB() const { return aabb_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string name_;
|
||||||
|
glm::vec3 col_offset_ = glm::vec3(0.0f);
|
||||||
std::unique_ptr<collision::TriangleMesh> cmesh_;
|
std::unique_ptr<collision::TriangleMesh> cmesh_;
|
||||||
// std::vector<ModelCollisionShape> cshapes_;
|
// std::vector<ModelCollisionShape> cshapes_;
|
||||||
std::vector<std::unique_ptr<btCollisionShape>> subshapes_;
|
std::vector<std::unique_ptr<btCollisionShape>> subshapes_;
|
||||||
|
|||||||
22
src/game/destroyed_object.cpp
Normal file
22
src/game/destroyed_object.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "destroyed_object.hpp"
|
||||||
|
|
||||||
|
game::DestroyedObject::DestroyedObject(World& world, std::unique_ptr<MapObjectCollision> col)
|
||||||
|
: Super(world, col->GetModel()->GetName()), col_(std::move(col))
|
||||||
|
{
|
||||||
|
// remove after 30s
|
||||||
|
Schedule(30000, [this]()
|
||||||
|
{
|
||||||
|
Remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::DestroyedObject::UpdatePreSync()
|
||||||
|
{
|
||||||
|
if (!col_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// sync transform with the physics body
|
||||||
|
col_->GetModelTransform(root_.local);
|
||||||
|
|
||||||
|
root_.UpdateMatrix();
|
||||||
|
}
|
||||||
22
src/game/destroyed_object.hpp
Normal file
22
src/game/destroyed_object.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "simple_entity.hpp"
|
||||||
|
#include "mapinstance.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
|
||||||
|
class DestroyedObject : public SimpleEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Super = SimpleEntity;
|
||||||
|
|
||||||
|
DestroyedObject(World& world, std::unique_ptr<MapObjectCollision> col);
|
||||||
|
|
||||||
|
virtual void UpdatePreSync() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<MapObjectCollision> col_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace game
|
||||||
@ -89,7 +89,10 @@ game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world,
|
|||||||
btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, cmesh->GetShape(), local_inertia));
|
btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, cmesh->GetShape(), local_inertia));
|
||||||
}
|
}
|
||||||
|
|
||||||
body_->setWorldTransform(trans.ToBtTransform());
|
auto offset_trans = trans;
|
||||||
|
offset_trans.position += trans.rotation * model_->GetColOffset();
|
||||||
|
|
||||||
|
body_->setWorldTransform(offset_trans.ToBtTransform());
|
||||||
body_->setUserIndex(static_cast<int>(obj_type));
|
body_->setUserIndex(static_cast<int>(obj_type));
|
||||||
body_->setUserPointer(this);
|
body_->setUserPointer(this);
|
||||||
|
|
||||||
@ -102,17 +105,28 @@ void game::MapObjectCollision::Break()
|
|||||||
if (!body_)
|
if (!body_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// body_->setCollisionFlags(body_->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
|
btCollisionShape* shape = body_->getCollisionShape();
|
||||||
|
|
||||||
float mass = 10.0f;
|
float mass = 10.0f;
|
||||||
btVector3 local_inertia(0, 0, 0);
|
btVector3 local_inertia(0, 0, 0);
|
||||||
body_->getCollisionShape()->calculateLocalInertia(mass, local_inertia);
|
shape->calculateLocalInertia(mass, local_inertia);
|
||||||
|
|
||||||
body_->setMassProps(mass, local_inertia);
|
btTransform trans = body_->getWorldTransform();
|
||||||
body_->forceActivationState(ACTIVE_TAG);
|
|
||||||
|
|
||||||
// set to undefined to avoid breaking again
|
// remove old
|
||||||
|
world_.GetBtWorld().removeRigidBody(body_.get());
|
||||||
|
body_.reset();
|
||||||
|
|
||||||
|
// make new
|
||||||
|
body_ = std::make_unique<btRigidBody>(btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, shape, local_inertia));
|
||||||
|
body_->setWorldTransform(trans);
|
||||||
body_->setUserIndex(static_cast<int>(collision::OT_UNDEFINED));
|
body_->setUserIndex(static_cast<int>(collision::OT_UNDEFINED));
|
||||||
|
world_.GetBtWorld().addRigidBody(body_.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::MapObjectCollision::GetModelTransform(Transform& trans) const
|
||||||
|
{
|
||||||
|
trans.SetBtTransform(body_->getWorldTransform());
|
||||||
|
trans.position -= trans.rotation * model_->GetColOffset(); // unapply offset
|
||||||
}
|
}
|
||||||
|
|
||||||
game::MapObjectCollision::~MapObjectCollision()
|
game::MapObjectCollision::~MapObjectCollision()
|
||||||
|
|||||||
@ -23,8 +23,12 @@ public:
|
|||||||
|
|
||||||
void Break();
|
void Break();
|
||||||
|
|
||||||
|
void GetModelTransform(Transform& trans) const;
|
||||||
|
|
||||||
|
const std::shared_ptr<const assets::Model>& GetModel() const { return model_; }
|
||||||
btRigidBody& GetBtBody() { return *body_; }
|
btRigidBody& GetBtBody() { return *body_; }
|
||||||
net::ObjNum GetNum() const { return num_; }
|
net::ObjNum GetNum() const { return num_; }
|
||||||
|
|
||||||
|
|
||||||
~MapObjectCollision();
|
~MapObjectCollision();
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "player_character.hpp"
|
#include "player_character.hpp"
|
||||||
#include "npc_character.hpp"
|
#include "npc_character.hpp"
|
||||||
#include "drivable_vehicle.hpp"
|
#include "drivable_vehicle.hpp"
|
||||||
|
#include "destroyed_object.hpp"
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
@ -77,6 +78,16 @@ void game::OpenWorld::PlayerLeft(Player& player)
|
|||||||
RemovePlayerCharacter(player);
|
RemovePlayerCharacter(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::OpenWorld::DestructibleDestroyed(net::ObjNum num, std::unique_ptr<MapObjectCollision> col)
|
||||||
|
{
|
||||||
|
auto& destroyed_obj = Spawn<DestroyedObject>(std::move(col));
|
||||||
|
|
||||||
|
// Schedule(100000, [this, objnum = num]()
|
||||||
|
// {
|
||||||
|
// RespawnObj(objnum);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::pair<game::Usable&, const game::UseTarget&>> game::OpenWorld::GetBestUseTarget(const glm::vec3& pos) const
|
std::optional<std::pair<game::Usable&, const game::UseTarget&>> game::OpenWorld::GetBestUseTarget(const glm::vec3& pos) const
|
||||||
{
|
{
|
||||||
std::optional<std::pair<Usable*, const UseTarget*>> best_target;
|
std::optional<std::pair<Usable*, const UseTarget*>> best_target;
|
||||||
|
|||||||
@ -25,6 +25,8 @@ public:
|
|||||||
virtual void PlayerViewAnglesChanged(Player& player, float yaw, float pitch) override;
|
virtual void PlayerViewAnglesChanged(Player& player, float yaw, float pitch) override;
|
||||||
virtual void PlayerLeft(Player& player) override;
|
virtual void PlayerLeft(Player& player) override;
|
||||||
|
|
||||||
|
virtual void DestructibleDestroyed(net::ObjNum num, std::unique_ptr<MapObjectCollision> col) override;
|
||||||
|
|
||||||
std::optional<std::pair<Usable&, const UseTarget&>> GetBestUseTarget(const glm::vec3& pos) const;
|
std::optional<std::pair<Usable&, const UseTarget&>> GetBestUseTarget(const glm::vec3& pos) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
81
src/game/simple_entity.cpp
Normal file
81
src/game/simple_entity.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "simple_entity.hpp"
|
||||||
|
#include "net/utils.hpp"
|
||||||
|
|
||||||
|
game::SimpleEntity::SimpleEntity(World& world, const std::string& modelname) : Super(world, net::ET_SIMPLE), modelname_(modelname)
|
||||||
|
{
|
||||||
|
UpdateSyncState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::SimpleEntity::SendInitData(Player& player, net::OutMessage& msg) const
|
||||||
|
{
|
||||||
|
Super::SendInitData(player, msg);
|
||||||
|
|
||||||
|
msg.Write(net::ModelName(modelname_));
|
||||||
|
|
||||||
|
// write state against default
|
||||||
|
static const SimpleEntitySyncState default_state;
|
||||||
|
size_t fields_pos = msg.Reserve<SimpleEntitySyncFieldFlags>();
|
||||||
|
auto fields = WriteState(msg, default_state);
|
||||||
|
msg.WriteAt(fields_pos, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::SimpleEntity::Update()
|
||||||
|
{
|
||||||
|
Super::Update();
|
||||||
|
|
||||||
|
UpdatePreSync(); // chance to update state before sent
|
||||||
|
|
||||||
|
sync_current_ = 1 - sync_current_;
|
||||||
|
UpdateSyncState();
|
||||||
|
SendUpdateMsg();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::SimpleEntity::UpdateSyncState()
|
||||||
|
{
|
||||||
|
SimpleEntitySyncState& state = sync_[sync_current_];
|
||||||
|
|
||||||
|
net::EncodePosition(root_.local.position, state.pos);
|
||||||
|
net::EncodeRotation(root_.local.rotation, state.rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::SimpleEntitySyncFieldFlags game::SimpleEntity::WriteState(net::OutMessage& msg, const SimpleEntitySyncState& base) const
|
||||||
|
{
|
||||||
|
SimpleEntitySyncFieldFlags fields = 0;
|
||||||
|
const SimpleEntitySyncState& curr = sync_[sync_current_];
|
||||||
|
|
||||||
|
if (curr.pos.x.value != base.pos.x.value ||
|
||||||
|
curr.pos.y.value != base.pos.y.value ||
|
||||||
|
curr.pos.z.value != base.pos.z.value)
|
||||||
|
{
|
||||||
|
fields |= SESF_POSITION;
|
||||||
|
|
||||||
|
net::WriteDelta(msg, curr.pos.x, base.pos.x);
|
||||||
|
net::WriteDelta(msg, curr.pos.y, base.pos.y);
|
||||||
|
net::WriteDelta(msg, curr.pos.z, base.pos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr.rot.x.value != base.rot.x.value ||
|
||||||
|
curr.rot.y.value != base.rot.y.value ||
|
||||||
|
curr.rot.z.value != base.rot.z.value)
|
||||||
|
{
|
||||||
|
fields |= SESF_ROTATION;
|
||||||
|
|
||||||
|
net::WriteDelta(msg, curr.rot.x, base.rot.x);
|
||||||
|
net::WriteDelta(msg, curr.rot.y, base.rot.y);
|
||||||
|
net::WriteDelta(msg, curr.rot.z, base.rot.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::SimpleEntity::SendUpdateMsg()
|
||||||
|
{
|
||||||
|
auto msg = BeginEntMsg(net::EMSG_UPDATE);
|
||||||
|
|
||||||
|
// write state against previous
|
||||||
|
const SimpleEntitySyncState& prev = sync_[1 - sync_current_];
|
||||||
|
size_t fields_pos = msg.Reserve<SimpleEntitySyncFieldFlags>();
|
||||||
|
auto fields = WriteState(msg, prev);
|
||||||
|
msg.WriteAt(fields_pos, fields);
|
||||||
|
}
|
||||||
32
src/game/simple_entity.hpp
Normal file
32
src/game/simple_entity.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "entity.hpp"
|
||||||
|
#include "simple_entity_sync.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
|
||||||
|
class SimpleEntity : public Entity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Super = Entity;
|
||||||
|
|
||||||
|
SimpleEntity(World& world, const std::string& modelname);
|
||||||
|
|
||||||
|
virtual void SendInitData(Player& player, net::OutMessage& msg) const override;
|
||||||
|
virtual void Update() override;
|
||||||
|
|
||||||
|
virtual void UpdatePreSync() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateSyncState();
|
||||||
|
SimpleEntitySyncFieldFlags WriteState(net::OutMessage& msg, const SimpleEntitySyncState& base) const;
|
||||||
|
void SendUpdateMsg();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string modelname_;
|
||||||
|
|
||||||
|
SimpleEntitySyncState sync_[2];
|
||||||
|
size_t sync_current_ = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace game
|
||||||
23
src/game/simple_entity_sync.hpp
Normal file
23
src/game/simple_entity_sync.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "net/defs.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
|
||||||
|
struct SimpleEntitySyncState
|
||||||
|
{
|
||||||
|
net::PositionQ pos;
|
||||||
|
net::QuatQ rot;
|
||||||
|
};
|
||||||
|
|
||||||
|
using SimpleEntitySyncFieldFlags = uint8_t;
|
||||||
|
|
||||||
|
enum SimpleEntitySyncFieldFlag
|
||||||
|
{
|
||||||
|
SESF_POSITION = 0x01,
|
||||||
|
SESF_ROTATION = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@
|
|||||||
#include "utils/allocnum.hpp"
|
#include "utils/allocnum.hpp"
|
||||||
#include "collision/object_type.hpp"
|
#include "collision/object_type.hpp"
|
||||||
|
|
||||||
game::World::World(std::string mapname) : map_(*this, std::move(mapname))
|
game::World::World(std::string mapname) : Scheduler(time_ms_), map_(*this, std::move(mapname))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +51,8 @@ void game::World::Update(int64_t delta_time)
|
|||||||
|
|
||||||
DetectDestructibleCollisions();
|
DetectDestructibleCollisions();
|
||||||
|
|
||||||
|
RunTasks();
|
||||||
|
|
||||||
// update entities
|
// update entities
|
||||||
for (auto it = ents_.begin(); it != ents_.end();)
|
for (auto it = ents_.begin(); it != ents_.end();)
|
||||||
{
|
{
|
||||||
@ -61,10 +63,13 @@ void game::World::Update(int64_t delta_time)
|
|||||||
else
|
else
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::World::FinishFrame()
|
void game::World::FinishFrame()
|
||||||
{
|
{
|
||||||
|
ResetMsg();
|
||||||
|
|
||||||
// reset ent msgs
|
// reset ent msgs
|
||||||
for (auto& [entnum, ent] : ents_)
|
for (auto& [entnum, ent] : ents_)
|
||||||
{
|
{
|
||||||
@ -82,6 +87,15 @@ game::Entity* game::World::GetEntity(net::EntNum entnum)
|
|||||||
return it->second.get();
|
return it->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::World::RespawnObj(net::ObjNum objnum)
|
||||||
|
{
|
||||||
|
if (destroyed_objs_.erase(objnum) > 0)
|
||||||
|
{
|
||||||
|
map_.SpawnObj(objnum);
|
||||||
|
SendObjRespawnedMsg(objnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void game::World::DetectDestructibleCollisions()
|
void game::World::DetectDestructibleCollisions()
|
||||||
{
|
{
|
||||||
auto& bt_world = GetBtWorld();
|
auto& bt_world = GetBtWorld();
|
||||||
@ -110,7 +124,7 @@ void game::World::DetectDestructibleCollisions()
|
|||||||
|
|
||||||
for (int j = 0; j < contactManifold->getNumContacts(); j++)
|
for (int j = 0; j < contactManifold->getNumContacts(); j++)
|
||||||
{
|
{
|
||||||
const float break_threshold = 3000.0f; // TODO: per-object threshold
|
const float break_threshold = 100.0f; // TODO: per-object threshold
|
||||||
|
|
||||||
btManifoldPoint& pt = contactManifold->getContactPoint(j);
|
btManifoldPoint& pt = contactManifold->getContactPoint(j);
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
|
|
||||||
class World : public collision::DynamicsWorld, public net::MsgProducer
|
class World : public collision::DynamicsWorld, public net::MsgProducer, public Scheduler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
World(std::string mapname);
|
World(std::string mapname);
|
||||||
@ -46,6 +46,8 @@ public:
|
|||||||
|
|
||||||
Entity* GetEntity(net::EntNum entnum);
|
Entity* GetEntity(net::EntNum entnum);
|
||||||
|
|
||||||
|
void RespawnObj(net::ObjNum objnum);
|
||||||
|
|
||||||
const assets::Map& GetMap() const { return map_.GetMap(); }
|
const assets::Map& GetMap() const { return map_.GetMap(); }
|
||||||
const std::string& GetMapName() const { return map_.GetName(); }
|
const std::string& GetMapName() const { return map_.GetName(); }
|
||||||
const std::map<net::EntNum, std::unique_ptr<Entity>>& GetEntities() const { return ents_; }
|
const std::map<net::EntNum, std::unique_ptr<Entity>>& GetEntities() const { return ents_; }
|
||||||
|
|||||||
108
src/gameview/simple_entity_view.cpp
Normal file
108
src/gameview/simple_entity_view.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "simple_entity_view.hpp"
|
||||||
|
#include "net/defs.hpp"
|
||||||
|
#include "assets/cache.hpp"
|
||||||
|
#include "worldview.hpp"
|
||||||
|
#include "net/utils.hpp"
|
||||||
|
|
||||||
|
game::view::SimpleEntityView::SimpleEntityView(WorldView& world, net::InMessage& msg) : Super(world, msg)
|
||||||
|
{
|
||||||
|
net::ModelName modelname;
|
||||||
|
if (!msg.Read(modelname))
|
||||||
|
throw EntityInitError();
|
||||||
|
|
||||||
|
if (modelname.len > 0)
|
||||||
|
{
|
||||||
|
model_ = assets::CacheManager::GetModel(std::string(modelname));
|
||||||
|
if (!model_)
|
||||||
|
throw EntityInitError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadState(msg))
|
||||||
|
throw EntityInitError();
|
||||||
|
|
||||||
|
states_[0] = states_[1]; // lerp from the read state to avoid jump
|
||||||
|
|
||||||
|
radius_ = 20.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game::view::SimpleEntityView::ProcessMsg(net::EntMsgType type, net::InMessage& msg)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case net::EMSG_UPDATE:
|
||||||
|
return ProcessUpdateMsg(msg);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Super::ProcessMsg(type, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::view::SimpleEntityView::Update(const UpdateInfo& info)
|
||||||
|
{
|
||||||
|
Super::Update(info);
|
||||||
|
|
||||||
|
// interpolate states
|
||||||
|
float tps = 25.0f;
|
||||||
|
float t = (info.time - update_time_) * tps * 0.8f; // assume some jitter, interpolate for longer
|
||||||
|
t = glm::clamp(t, 0.0f, 2.0f);
|
||||||
|
|
||||||
|
root_.local = Transform::Lerp(states_[0].trans, states_[1].trans, t);
|
||||||
|
root_.UpdateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::view::SimpleEntityView::Draw(const DrawArgs& args)
|
||||||
|
{
|
||||||
|
Super::Draw(args);
|
||||||
|
|
||||||
|
if (!model_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto& mesh = *model_->GetMesh();
|
||||||
|
for (const auto& surface : mesh.surfaces)
|
||||||
|
{
|
||||||
|
gfx::DrawSurfaceCmd cmd;
|
||||||
|
cmd.surface = &surface;
|
||||||
|
cmd.matrices = &root_.matrix;
|
||||||
|
args.dlist.AddSurface(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game::view::SimpleEntityView::ReadState(net::InMessage& msg)
|
||||||
|
{
|
||||||
|
update_time_ = world_.GetTime();
|
||||||
|
|
||||||
|
// init lerp start state
|
||||||
|
states_[0].trans = root_.local;
|
||||||
|
|
||||||
|
auto& new_state = states_[1];
|
||||||
|
|
||||||
|
// parse state delta
|
||||||
|
SimpleEntitySyncFieldFlags fields;
|
||||||
|
if (!msg.Read(fields))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// pos
|
||||||
|
if (fields & SESF_POSITION)
|
||||||
|
{
|
||||||
|
if (!net::ReadDelta(msg, sync_.pos.x) || !net::ReadDelta(msg, sync_.pos.y) || !net::ReadDelta(msg, sync_.pos.z))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
net::DecodePosition(sync_.pos, new_state.trans.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rot
|
||||||
|
if (fields & SESF_ROTATION)
|
||||||
|
{
|
||||||
|
if (!net::ReadDelta(msg, sync_.rot.x) || !net::ReadDelta(msg, sync_.rot.y) || !net::ReadDelta(msg, sync_.rot.z))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
net::DecodeRotation(sync_.rot, new_state.trans.rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game::view::SimpleEntityView::ProcessUpdateMsg(net::InMessage& msg)
|
||||||
|
{
|
||||||
|
return ReadState(msg);
|
||||||
|
}
|
||||||
41
src/gameview/simple_entity_view.hpp
Normal file
41
src/gameview/simple_entity_view.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "assets/model.hpp"
|
||||||
|
#include "entityview.hpp"
|
||||||
|
#include "game/simple_entity_sync.hpp"
|
||||||
|
|
||||||
|
namespace game::view
|
||||||
|
{
|
||||||
|
|
||||||
|
struct SimpleEntityViewState
|
||||||
|
{
|
||||||
|
Transform trans;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimpleEntityView : public EntityView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Super = EntityView;
|
||||||
|
|
||||||
|
SimpleEntityView(WorldView& world, net::InMessage& msg);
|
||||||
|
|
||||||
|
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override;
|
||||||
|
virtual void Update(const UpdateInfo& info) override;
|
||||||
|
virtual void Draw(const DrawArgs& args) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool ReadState(net::InMessage& msg);
|
||||||
|
|
||||||
|
bool ProcessUpdateMsg(net::InMessage& msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<const assets::Model> model_;
|
||||||
|
|
||||||
|
SimpleEntitySyncState sync_;
|
||||||
|
SimpleEntityViewState states_[2];
|
||||||
|
float update_time_ = 0.0f;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "assets/cache.hpp"
|
#include "assets/cache.hpp"
|
||||||
|
|
||||||
|
#include "simple_entity_view.hpp"
|
||||||
#include "characterview.hpp"
|
#include "characterview.hpp"
|
||||||
#include "vehicleview.hpp"
|
#include "vehicleview.hpp"
|
||||||
#include "client_session.hpp"
|
#include "client_session.hpp"
|
||||||
@ -123,6 +124,10 @@ bool game::view::WorldView::ProcessEntSpawnMsg(net::InMessage& msg)
|
|||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
case net::ET_SIMPLE:
|
||||||
|
entslot = std::make_unique<SimpleEntityView>(*this, msg);
|
||||||
|
break;
|
||||||
|
|
||||||
case net::ET_CHARACTER:
|
case net::ET_CHARACTER:
|
||||||
entslot = std::make_unique<CharacterView>(*this, msg);
|
entslot = std::make_unique<CharacterView>(*this, msg);
|
||||||
break;
|
break;
|
||||||
@ -132,6 +137,7 @@ bool game::view::WorldView::ProcessEntSpawnMsg(net::InMessage& msg)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
ents_.erase(entnum);
|
||||||
return false; // unknown type
|
return false; // unknown type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,9 +72,10 @@ enum EntType : uint8_t
|
|||||||
{
|
{
|
||||||
ET_NONE,
|
ET_NONE,
|
||||||
|
|
||||||
|
ET_SIMPLE,
|
||||||
ET_CHARACTER,
|
ET_CHARACTER,
|
||||||
ET_VEHICLE,
|
ET_VEHICLE,
|
||||||
|
|
||||||
ET_COUNT,
|
ET_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user