Destructible objs pt. 1
This commit is contained in:
parent
0e7e80ee79
commit
d6947a79d6
@ -77,6 +77,8 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gameview/client_session.cpp"
|
||||
"src/gameview/entityview.hpp"
|
||||
"src/gameview/entityview.cpp"
|
||||
"src/gameview/mapinstanceview.hpp"
|
||||
"src/gameview/mapinstanceview.cpp"
|
||||
"src/gameview/skinning_ubo.hpp"
|
||||
"src/gameview/skinning_ubo.cpp"
|
||||
"src/gameview/vehicleview.hpp"
|
||||
@ -120,6 +122,8 @@ set(SERVER_ONLY_SOURCES
|
||||
"src/game/entity.cpp"
|
||||
"src/game/game.hpp"
|
||||
"src/game/game.cpp"
|
||||
"src/game/mapinstance.hpp"
|
||||
"src/game/mapinstance.cpp"
|
||||
"src/game/npc_character.hpp"
|
||||
"src/game/npc_character.cpp"
|
||||
"src/game/openworld.hpp"
|
||||
|
||||
@ -2,9 +2,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "cache.hpp"
|
||||
#include "cmdfile.hpp"
|
||||
#include "utils/files.hpp"
|
||||
@ -65,7 +62,7 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
if (!chunk)
|
||||
throw std::runtime_error("static in map without chunk");
|
||||
|
||||
ChunkStaticObject obj;
|
||||
MapStaticObject obj;
|
||||
std::string model_name;
|
||||
iss >> model_name;
|
||||
|
||||
@ -90,7 +87,8 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
chunk->objs.push_back(std::move(obj));
|
||||
map->objs_.push_back(std::move(obj));
|
||||
chunk->num_objs++;
|
||||
}
|
||||
else if (command == "chunk")
|
||||
{
|
||||
@ -99,6 +97,8 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
iss >> coord.x >> coord.y;
|
||||
iss >> chunk->aabb.min.x >> chunk->aabb.min.y >> chunk->aabb.min.z;
|
||||
iss >> chunk->aabb.max.x >> chunk->aabb.max.y >> chunk->aabb.max.z;
|
||||
|
||||
chunk->first_obj = map->objs_.size();
|
||||
}
|
||||
else if (command == "surface")
|
||||
{
|
||||
@ -177,63 +177,3 @@ const assets::MapGraph* assets::Map::GetGraph(const std::string& name) const
|
||||
return &it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef CLIENT
|
||||
void assets::Map::Draw(const game::view::DrawArgs& args) const
|
||||
{
|
||||
if (!basemodel_ || !basemodel_->GetMesh())
|
||||
return;
|
||||
|
||||
const auto& mesh = *basemodel_->GetMesh();
|
||||
|
||||
const float max_dist = args.render_distance + 200.0f;
|
||||
const float max_dist2 = max_dist * max_dist;
|
||||
|
||||
for (auto& chunk : chunks_)
|
||||
{
|
||||
glm::vec3 center = (chunk.aabb.min + chunk.aabb.max) * 0.5f;
|
||||
if (glm::distance2(args.eye, center) > max_dist2)
|
||||
continue;
|
||||
|
||||
if (!args.frustum.IsAABBVisible(chunk.aabb))
|
||||
continue;
|
||||
|
||||
DrawChunk(args, mesh, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
void assets::Map::DrawChunk(const game::view::DrawArgs& args, const Mesh& basemesh, const Chunk& chunk) const
|
||||
{
|
||||
for (const auto& surface_range : chunk.surfaces)
|
||||
{
|
||||
auto& surface = basemesh.surfaces[surface_range.idx];
|
||||
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.first = surface_range.first;
|
||||
cmd.count = surface_range.count;
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
|
||||
for (const auto& obj : chunk.objs)
|
||||
{
|
||||
if (!obj.model || !obj.model->GetMesh())
|
||||
continue;
|
||||
|
||||
if (!args.frustum.IsAABBVisible(obj.aabb))
|
||||
continue;
|
||||
|
||||
const auto& surfaces = obj.model->GetMesh()->surfaces;
|
||||
|
||||
for (const auto& surface : surfaces)
|
||||
{
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &obj.node.matrix;
|
||||
// cmd.color_mod = glm::vec4(obj.color, 1.0f);
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CLIENT
|
||||
|
||||
@ -9,14 +9,10 @@
|
||||
#include "model.hpp"
|
||||
#include "utils/aabb.hpp"
|
||||
|
||||
#ifdef CLIENT
|
||||
#include "gameview/draw_args.hpp"
|
||||
#endif // CLIENT
|
||||
|
||||
namespace assets
|
||||
{
|
||||
|
||||
struct ChunkStaticObject
|
||||
struct MapStaticObject
|
||||
{
|
||||
game::TransformNode node;
|
||||
AABB3 aabb;
|
||||
@ -37,7 +33,8 @@ struct Chunk
|
||||
{
|
||||
AABB3 aabb;
|
||||
std::vector<ChunkSurfaceRange> surfaces;
|
||||
std::vector<ChunkStaticObject> objs;
|
||||
size_t first_obj = 0;
|
||||
size_t num_objs = 0;
|
||||
};
|
||||
|
||||
struct MapGraphNode
|
||||
@ -61,16 +58,13 @@ public:
|
||||
|
||||
const std::shared_ptr<const Model>& GetBaseModel() const { return basemodel_; }
|
||||
const std::vector<Chunk>& GetChunks() const { return chunks_; }
|
||||
const std::vector<MapStaticObject>& GetStaticObjects() const { return objs_; }
|
||||
const MapGraph* GetGraph(const std::string& name) const;
|
||||
|
||||
CLIENT_ONLY(void Draw(const game::view::DrawArgs& args) const;)
|
||||
|
||||
private:
|
||||
CLIENT_ONLY(void DrawChunk(const game::view::DrawArgs& args, const Mesh& basemesh, const Chunk& chunk) const;)
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Model> basemodel_;
|
||||
std::vector<Chunk> chunks_;
|
||||
std::vector<MapStaticObject> objs_;
|
||||
std::map<std::string, MapGraph> graphs_;
|
||||
};
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
||||
|
||||
CLIENT_ONLY(MeshBuilder mb(gfx::MF_NONE);)
|
||||
std::unique_ptr<btConvexHullShape> temp_hull;
|
||||
std::unique_ptr<btCompoundShape> compound;
|
||||
|
||||
LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) {
|
||||
if (command == "v")
|
||||
@ -129,6 +130,33 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
||||
model->skeleton_ = CacheManager::GetSkeleton("data/" + skel_name + ".sk");
|
||||
CLIENT_ONLY(mb.SetMeshFlag(gfx::MF_SKELETAL));
|
||||
}
|
||||
else if (command == "col")
|
||||
{
|
||||
std::string shape_type;
|
||||
Transform trans;
|
||||
float sy, sz;
|
||||
iss >> shape_type;
|
||||
ParseTransform(iss, trans);
|
||||
iss >> sy >> sz;
|
||||
glm::vec3 scale(trans.scale, sy, sz);
|
||||
trans.scale = 1.0f;
|
||||
|
||||
if (!compound)
|
||||
{
|
||||
compound = std::make_unique<btCompoundShape>();
|
||||
}
|
||||
|
||||
if (shape_type == "box")
|
||||
{
|
||||
auto box_shape = std::make_unique<btBoxShape>(btVector3(scale.x, scale.y, scale.z));
|
||||
compound->addChildShape(trans.ToBtTransform(), box_shape.get());
|
||||
model->subshapes_.push_back(std::move(box_shape));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown collision shape type: " + shape_type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown command in model file: " + command);
|
||||
@ -157,6 +185,10 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
||||
|
||||
model->cshape_ = std::make_unique<btConvexHullShape>((btScalar*)shape_hull->getVertexPointer(), shape_hull->numVertices(), sizeof(btVector3));
|
||||
}
|
||||
else
|
||||
{
|
||||
model->cshape_ = std::move(compound);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ public:
|
||||
private:
|
||||
std::unique_ptr<collision::TriangleMesh> cmesh_;
|
||||
// std::vector<ModelCollisionShape> cshapes_;
|
||||
std::vector<std::unique_ptr<btCollisionShape>> subshapes_;
|
||||
std::unique_ptr<btCollisionShape> cshape_;
|
||||
|
||||
std::shared_ptr<const Skeleton> skeleton_;
|
||||
|
||||
@ -10,47 +10,3 @@ collision::DynamicsWorld::DynamicsWorld()
|
||||
|
||||
bt_broadphase_.getOverlappingPairCache()->setInternalGhostPairCallback(&bt_ghost_pair_cb_);
|
||||
}
|
||||
|
||||
void collision::DynamicsWorld::AddMapCollision(std::shared_ptr<const assets::Map> map)
|
||||
{
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
map_ = std::move(map);
|
||||
|
||||
// add basemodel
|
||||
const auto& basemodel = map_->GetBaseModel();
|
||||
if (basemodel)
|
||||
{
|
||||
Transform identity;
|
||||
AddModelInstance(*basemodel, identity);
|
||||
}
|
||||
|
||||
// add static objects
|
||||
for (const auto& chunks = map_->GetChunks(); const auto& chunk : chunks)
|
||||
{
|
||||
for (const auto& obj : chunk.objs)
|
||||
{
|
||||
AddModelInstance(*obj.model, obj.node.local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collision::DynamicsWorld::AddModelInstance(const assets::Model& model, const Transform& trans)
|
||||
{
|
||||
if (auto cmesh = model.GetColMesh(); cmesh)
|
||||
{
|
||||
// create trimesh object
|
||||
btRigidBody::btRigidBodyConstructionInfo rbInfo(0.0f, nullptr, cmesh->GetShape(), btVector3(0,0,0));
|
||||
auto obj = std::make_unique<btRigidBody>(rbInfo);
|
||||
|
||||
// set transform
|
||||
obj->setWorldTransform(trans.ToBtTransform());
|
||||
|
||||
// add to world
|
||||
bt_world_.addRigidBody(obj.get());
|
||||
static_objs_.emplace_back(std::move(obj));
|
||||
}
|
||||
|
||||
// TODO: add shape
|
||||
}
|
||||
|
||||
@ -10,38 +10,16 @@
|
||||
namespace collision
|
||||
{
|
||||
|
||||
// struct StaticObjectInstance
|
||||
// {
|
||||
// std::unique_ptr<btCollisionShape> shape;
|
||||
// btRigidBody body;
|
||||
|
||||
// StaticObjectInstance(std::unique_ptr<btCollisionShape> shape) : body()
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
class DynamicsWorld
|
||||
{
|
||||
public:
|
||||
DynamicsWorld();
|
||||
|
||||
void AddMapCollision(std::shared_ptr<const assets::Map> map);
|
||||
|
||||
btDynamicsWorld& GetBtWorld() { return bt_world_; }
|
||||
const btDynamicsWorld& GetBtWorld() const { return bt_world_; }
|
||||
btVehicleRaycaster& GetVehicleRaycaster() { return bt_veh_raycaster_; }
|
||||
|
||||
private:
|
||||
void AddModelInstance(const assets::Model& model, const Transform& trans);
|
||||
|
||||
private:
|
||||
// this is BEFORE bt_world_!!!
|
||||
std::shared_ptr<const assets::Map> map_;
|
||||
std::vector<std::unique_ptr<btRigidBody>> static_objs_;
|
||||
// ^-----
|
||||
|
||||
btDefaultCollisionConfiguration bt_cfg_;
|
||||
btCollisionDispatcher bt_dispatcher_;
|
||||
btGhostPairCallback bt_ghost_pair_cb_;
|
||||
|
||||
16
src/collision/object_type.hpp
Normal file
16
src/collision/object_type.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace collision
|
||||
{
|
||||
|
||||
enum ObjectType
|
||||
{
|
||||
OT_UNDEFINED = 0,
|
||||
OT_MAP_STATIC = 1,
|
||||
OT_MAP_DESTRUCTIBLE = 2,
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
122
src/game/mapinstance.cpp
Normal file
122
src/game/mapinstance.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include "mapinstance.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "assets/cache.hpp"
|
||||
#include "collision/object_type.hpp"
|
||||
|
||||
game::MapInstance::MapInstance(collision::DynamicsWorld& world, std::string mapname)
|
||||
: world_(world), mapname_(std::move(mapname))
|
||||
{
|
||||
map_ = assets::CacheManager::GetMap("data/" + mapname_ + ".map");
|
||||
|
||||
// add basemodel col
|
||||
const auto& basemodel = map_->GetBaseModel();
|
||||
if (basemodel)
|
||||
{
|
||||
Transform identity;
|
||||
basemodel_col_ = std::make_unique<MapObjectCollision>(world_, basemodel, 0, identity, 0);
|
||||
}
|
||||
|
||||
// add static objects
|
||||
const auto& objs = map_->GetStaticObjects();
|
||||
obj_cols_.resize(objs.size());
|
||||
for (size_t i = 0; i < objs.size(); ++i)
|
||||
{
|
||||
SpawnObj(static_cast<net::ObjNum>(i));
|
||||
}
|
||||
}
|
||||
|
||||
void game::MapInstance::SpawnObj(net::ObjNum objnum)
|
||||
{
|
||||
if (objnum >= obj_cols_.size())
|
||||
throw std::runtime_error("Invalid object number");
|
||||
|
||||
size_t i = static_cast<size_t>(objnum);
|
||||
|
||||
if (obj_cols_[i])
|
||||
return; // already spawned
|
||||
|
||||
const auto& objs = map_->GetStaticObjects();
|
||||
obj_cols_[i] = std::make_unique<MapObjectCollision>(world_, objs[i].model, static_cast<net::ObjNum>(i), objs[i].node.local, MAPOBJ_DESTRUCTIBLE);
|
||||
}
|
||||
|
||||
std::unique_ptr<game::MapObjectCollision> game::MapInstance::DestroyObj(net::ObjNum objnum)
|
||||
{
|
||||
size_t i = static_cast<size_t>(objnum);
|
||||
if (i >= obj_cols_.size())
|
||||
throw std::runtime_error("Invalid object number");
|
||||
|
||||
auto obj = std::move(obj_cols_[i]);
|
||||
|
||||
if (obj)
|
||||
{
|
||||
obj->Break();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world,
|
||||
std::shared_ptr<const assets::Model> model, net::ObjNum num,
|
||||
const Transform& trans, MapObjectCollisionFlags flags)
|
||||
: world_(world), model_(std::move(model)), num_(num)
|
||||
{
|
||||
auto cshape = model_->GetColShape();
|
||||
auto cmesh = model_->GetColMesh();
|
||||
|
||||
const bool destructible = (flags & MAPOBJ_DESTRUCTIBLE) != 0 && cshape != nullptr;
|
||||
|
||||
collision::ObjectType obj_type = collision::OT_MAP_STATIC;
|
||||
|
||||
btVector3 local_inertia(0, 0, 0);
|
||||
float mass = 0.0f;
|
||||
|
||||
if (destructible)
|
||||
{
|
||||
obj_type = collision::OT_MAP_DESTRUCTIBLE;
|
||||
}
|
||||
|
||||
// prefer simple cshape which allow destruction
|
||||
if (cshape)
|
||||
{
|
||||
body_ = std::make_unique<btRigidBody>(
|
||||
btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, cshape, local_inertia));
|
||||
}
|
||||
else if (cmesh)
|
||||
{
|
||||
body_ = std::make_unique<btRigidBody>(
|
||||
btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, cmesh->GetShape(), local_inertia));
|
||||
}
|
||||
|
||||
body_->setWorldTransform(trans.ToBtTransform());
|
||||
body_->setUserIndex(static_cast<int>(obj_type));
|
||||
body_->setUserPointer(this);
|
||||
|
||||
// world_.GetBtWorld().addRigidBody(body_.get(), btBroadphaseProxy::StaticFilter, btBroadphaseProxy::AllFilter);
|
||||
world_.GetBtWorld().addRigidBody(body_.get());
|
||||
}
|
||||
|
||||
void game::MapObjectCollision::Break()
|
||||
{
|
||||
if (!body_)
|
||||
return;
|
||||
|
||||
// body_->setCollisionFlags(body_->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||
|
||||
float mass = 10.0f;
|
||||
btVector3 local_inertia(0, 0, 0);
|
||||
body_->getCollisionShape()->calculateLocalInertia(mass, local_inertia);
|
||||
|
||||
body_->setMassProps(mass, local_inertia);
|
||||
body_->forceActivationState(ACTIVE_TAG);
|
||||
|
||||
// set to undefined to avoid breaking again
|
||||
body_->setUserIndex(static_cast<int>(collision::OT_UNDEFINED));
|
||||
}
|
||||
|
||||
game::MapObjectCollision::~MapObjectCollision()
|
||||
{
|
||||
if (body_)
|
||||
world_.GetBtWorld().removeRigidBody(body_.get());
|
||||
}
|
||||
62
src/game/mapinstance.hpp
Normal file
62
src/game/mapinstance.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "assets/map.hpp"
|
||||
#include "collision/dynamicsworld.hpp"
|
||||
#include "net/defs.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
using MapObjectCollisionFlags = uint32_t;
|
||||
|
||||
enum MapObjectCollisionFlag : MapObjectCollisionFlags
|
||||
{
|
||||
MAPOBJ_DESTRUCTIBLE = 0x01,
|
||||
};
|
||||
|
||||
class MapObjectCollision
|
||||
{
|
||||
public:
|
||||
MapObjectCollision(collision::DynamicsWorld& world, std::shared_ptr<const assets::Model> model,
|
||||
net::ObjNum num, const Transform& trans, MapObjectCollisionFlags flags);
|
||||
DELETE_COPY_MOVE(MapObjectCollision)
|
||||
|
||||
void Break();
|
||||
|
||||
btRigidBody& GetBtBody() { return *body_; }
|
||||
net::ObjNum GetNum() const { return num_; }
|
||||
|
||||
~MapObjectCollision();
|
||||
|
||||
private:
|
||||
collision::DynamicsWorld& world_;
|
||||
std::shared_ptr<const assets::Model> model_;
|
||||
net::ObjNum num_;
|
||||
std::unique_ptr<btRigidBody> body_;
|
||||
};
|
||||
|
||||
class MapInstance
|
||||
{
|
||||
public:
|
||||
MapInstance(collision::DynamicsWorld& world, std::string mapname);
|
||||
|
||||
const assets::Map& GetMap() const { return *map_; }
|
||||
const std::string& GetName() const { return mapname_; }
|
||||
|
||||
void SpawnObj(net::ObjNum objnum);
|
||||
std::unique_ptr<MapObjectCollision> DestroyObj(net::ObjNum objnum);
|
||||
|
||||
private:
|
||||
|
||||
private:
|
||||
collision::DynamicsWorld& world_;
|
||||
std::string mapname_;
|
||||
std::shared_ptr<const assets::Map> map_;
|
||||
|
||||
std::unique_ptr<MapObjectCollision> basemodel_col_;
|
||||
std::vector<std::unique_ptr<MapObjectCollision>> obj_cols_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
#include "openworld.hpp"
|
||||
|
||||
#include <coroutine>
|
||||
#include <iostream>
|
||||
|
||||
#include "player.hpp"
|
||||
@ -24,6 +23,9 @@ game::OpenWorld::OpenWorld() : World("openworld")
|
||||
{
|
||||
SpawnBot();
|
||||
}
|
||||
|
||||
auto& veh = Spawn<game::DrivableVehicle>("twingo", glm::vec3{0.8f, 0.1f, 0.1f});
|
||||
veh.SetPosition({110.0f, 100.0f, 5.0f});
|
||||
}
|
||||
|
||||
void game::OpenWorld::Update(int64_t delta_time)
|
||||
|
||||
@ -38,15 +38,7 @@ void game::Player::Update()
|
||||
|
||||
if (world_)
|
||||
{
|
||||
if (cam_ent_)
|
||||
{
|
||||
auto cam_ent = world_->GetEntity(cam_ent_);
|
||||
if (cam_ent)
|
||||
{
|
||||
cull_pos_ = cam_ent->GetRoot().GetGlobalPosition();
|
||||
}
|
||||
}
|
||||
|
||||
SendWorldUpdateMsg();
|
||||
SyncEntities();
|
||||
}
|
||||
}
|
||||
@ -90,11 +82,30 @@ void game::Player::SendWorldMsg()
|
||||
{
|
||||
MSGDEBUG(std::cout << "seding CHWORLD" << std::endl;)
|
||||
auto msg = BeginMsg(net::MSG_CHWORLD);
|
||||
msg.Write(net::MapName(world_->GetMapName()));
|
||||
world_->SendInitData(*this, msg);
|
||||
}
|
||||
|
||||
void game::Player::SendWorldUpdateMsg()
|
||||
{
|
||||
if (!world_)
|
||||
return;
|
||||
|
||||
auto msg = BeginMsg(); // no CMD here, included in world payload
|
||||
msg.Write(world_->GetMsg());
|
||||
}
|
||||
|
||||
void game::Player::SyncEntities()
|
||||
{
|
||||
// update cull pos
|
||||
if (cam_ent_)
|
||||
{
|
||||
auto cam_ent = world_->GetEntity(cam_ent_);
|
||||
if (cam_ent)
|
||||
{
|
||||
cull_pos_ = cam_ent->GetRoot().GetGlobalPosition();
|
||||
}
|
||||
}
|
||||
|
||||
const auto& ents = world_->GetEntities();
|
||||
|
||||
auto ent_it = ents.begin();
|
||||
|
||||
@ -37,7 +37,9 @@ public:
|
||||
~Player();
|
||||
|
||||
private:
|
||||
// world sync
|
||||
void SendWorldMsg();
|
||||
void SendWorldUpdateMsg();
|
||||
|
||||
// entities sync
|
||||
void SyncEntities();
|
||||
|
||||
@ -89,7 +89,7 @@ game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& co
|
||||
}
|
||||
|
||||
auto& bt_world = world_.GetBtWorld();
|
||||
bt_world.addRigidBody(body_.get());
|
||||
bt_world.addRigidBody(body_.get(), btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::AllFilter);
|
||||
bt_world.addAction(vehicle_.get());
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,25 @@
|
||||
#include "world.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include "assets/cache.hpp"
|
||||
#include "utils/allocnum.hpp"
|
||||
#include "collision/object_type.hpp"
|
||||
|
||||
game::World::World(std::string mapname) : mapname_(std::move(mapname))
|
||||
game::World::World(std::string mapname) : map_(*this, std::move(mapname))
|
||||
{
|
||||
map_ = assets::CacheManager::GetMap("data/" + mapname_ + ".map");
|
||||
AddMapCollision(map_);
|
||||
}
|
||||
|
||||
void game::World::SendInitData(Player& player, net::OutMessage& msg)
|
||||
{
|
||||
msg.Write(net::MapName(map_.GetName()));
|
||||
|
||||
msg.Write<net::ObjCount>(destroyed_objs_.size());
|
||||
for (auto objnum : destroyed_objs_)
|
||||
{
|
||||
msg.Write(objnum);
|
||||
}
|
||||
}
|
||||
|
||||
net::EntNum game::World::GetNewEntnum()
|
||||
@ -38,6 +49,8 @@ void game::World::Update(int64_t delta_time)
|
||||
// GetBtWorld().stepSimulation(delta_s, 1, delta_s);
|
||||
GetBtWorld().stepSimulation(delta_s, 2, delta_s * 0.5f);
|
||||
|
||||
DetectDestructibleCollisions();
|
||||
|
||||
// update entities
|
||||
for (auto it = ents_.begin(); it != ents_.end();)
|
||||
{
|
||||
@ -68,3 +81,80 @@ game::Entity* game::World::GetEntity(net::EntNum entnum)
|
||||
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
void game::World::DetectDestructibleCollisions()
|
||||
{
|
||||
auto& bt_world = GetBtWorld();
|
||||
int numManifolds = bt_world.getDispatcher()->getNumManifolds();
|
||||
|
||||
static std::vector<net::ObjNum> to_destroy;
|
||||
to_destroy.clear();
|
||||
|
||||
// std::cout << "Checking " << numManifolds << " manifolds for destructible collisions..." << std::endl;
|
||||
for (int i = 0; i < numManifolds; i++)
|
||||
{
|
||||
btPersistentManifold* contactManifold = bt_world.getDispatcher()->getManifoldByIndexInternal(i);
|
||||
|
||||
const btRigidBody* bodyA = static_cast<const btRigidBody*>(contactManifold->getBody0());
|
||||
const btRigidBody* bodyB = static_cast<const btRigidBody*>(contactManifold->getBody1());
|
||||
|
||||
const btRigidBody* destructibleBody = nullptr;
|
||||
|
||||
if (bodyA->getUserIndex() == collision::OT_MAP_DESTRUCTIBLE)
|
||||
destructibleBody = bodyA;
|
||||
else if (bodyB->getUserIndex() == collision::OT_MAP_DESTRUCTIBLE)
|
||||
destructibleBody = bodyB;
|
||||
|
||||
if (!destructibleBody)
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < contactManifold->getNumContacts(); j++)
|
||||
{
|
||||
const float break_threshold = 3000.0f; // TODO: per-object threshold
|
||||
|
||||
btManifoldPoint& pt = contactManifold->getContactPoint(j);
|
||||
|
||||
if (pt.getAppliedImpulse() > break_threshold)
|
||||
{
|
||||
std::cout << "Destructible collision detected: impulse = " << pt.getAppliedImpulse() << std::endl;
|
||||
|
||||
MapObjectCollision* obj_col = static_cast<MapObjectCollision*>(destructibleBody->getUserPointer());
|
||||
to_destroy.push_back(obj_col->GetNum());
|
||||
|
||||
const btRigidBody* otherBody = (destructibleBody == bodyA) ? bodyB : bodyA;
|
||||
btRigidBody* otherBodyNonConst = const_cast<btRigidBody*>(otherBody);
|
||||
otherBodyNonConst->applyCentralImpulse(pt.m_normalWorldOnB * pt.getAppliedImpulse() * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// destroy objs outside the loop to avoid corruption of the manifold list
|
||||
for (auto objnum : to_destroy)
|
||||
{
|
||||
DestroyObject(objnum);
|
||||
}
|
||||
}
|
||||
|
||||
void game::World::DestroyObject(net::ObjNum objnum)
|
||||
{
|
||||
SendObjDestroyedMsg(objnum);
|
||||
destroyed_objs_.insert(objnum);
|
||||
|
||||
auto col = map_.DestroyObj(objnum);
|
||||
if (col)
|
||||
{
|
||||
DestructibleDestroyed(objnum, std::move(col));
|
||||
}
|
||||
}
|
||||
|
||||
void game::World::SendObjDestroyedMsg(net::ObjNum objnum)
|
||||
{
|
||||
auto msg = BeginMsg(net::MSG_OBJDESTROY);
|
||||
msg.Write(objnum);
|
||||
}
|
||||
|
||||
void game::World::SendObjRespawnedMsg(net::ObjNum objnum)
|
||||
{
|
||||
auto msg = BeginMsg(net::MSG_OBJRESPAWN);
|
||||
msg.Write(objnum);
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <set>
|
||||
|
||||
#include "assets/map.hpp"
|
||||
#include "mapinstance.hpp"
|
||||
#include "collision/dynamicsworld.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "net/defs.hpp"
|
||||
@ -11,12 +12,14 @@
|
||||
namespace game
|
||||
{
|
||||
|
||||
class World : public collision::DynamicsWorld
|
||||
class World : public collision::DynamicsWorld, public net::MsgProducer
|
||||
{
|
||||
public:
|
||||
World(std::string mapname);
|
||||
DELETE_COPY_MOVE(World)
|
||||
|
||||
void SendInitData(Player& player, net::OutMessage& msg);
|
||||
|
||||
// spawn entity of type T
|
||||
template <std::derived_from<Entity> T, typename... TArgs>
|
||||
T& Spawn(TArgs&&... args)
|
||||
@ -39,18 +42,29 @@ public:
|
||||
virtual void PlayerViewAnglesChanged(Player& player, float yaw, float pitch) {}
|
||||
virtual void PlayerLeft(Player& player) {}
|
||||
|
||||
virtual void DestructibleDestroyed(net::ObjNum num, std::unique_ptr<MapObjectCollision> col) {}
|
||||
|
||||
Entity* GetEntity(net::EntNum entnum);
|
||||
|
||||
const assets::Map& GetMap() const { return *map_; }
|
||||
const std::string& GetMapName() const { return mapname_; }
|
||||
const assets::Map& GetMap() const { return map_.GetMap(); }
|
||||
const std::string& GetMapName() const { return map_.GetName(); }
|
||||
const std::map<net::EntNum, std::unique_ptr<Entity>>& GetEntities() const { return ents_; }
|
||||
const int64_t& GetTime() const { return time_ms_; }
|
||||
|
||||
virtual ~World() = default;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const assets::Map> map_;
|
||||
std::string mapname_;
|
||||
void DetectDestructibleCollisions();
|
||||
|
||||
void DestroyObject(net::ObjNum objnum);
|
||||
|
||||
void SendObjDestroyedMsg(net::ObjNum objnum);
|
||||
void SendObjRespawnedMsg(net::ObjNum objnum);
|
||||
|
||||
private:
|
||||
MapInstance map_;
|
||||
std::set<net::ObjNum> destroyed_objs_;
|
||||
|
||||
std::map<net::EntNum, std::unique_ptr<Entity>> ents_;
|
||||
net::EntNum last_entnum_ = 0;
|
||||
|
||||
|
||||
@ -117,12 +117,14 @@ audio::Master& game::view::ClientSession::GetAudioMaster() const
|
||||
|
||||
bool game::view::ClientSession::ProcessWorldMsg(net::InMessage& msg)
|
||||
{
|
||||
net::MapName mapname;
|
||||
if (!msg.Read(mapname))
|
||||
try
|
||||
{
|
||||
world_ = std::make_unique<WorldView>(*this, msg);
|
||||
}
|
||||
catch (const EntityInitError&)
|
||||
{
|
||||
return false;
|
||||
|
||||
// TODO: pass mapname
|
||||
world_ = std::make_unique<WorldView>(*this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
94
src/gameview/mapinstanceview.cpp
Normal file
94
src/gameview/mapinstanceview.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "mapinstanceview.hpp"
|
||||
#include "assets/cache.hpp"
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
game::view::MapInstanceView::MapInstanceView(const std::string& map_name)
|
||||
{
|
||||
map_ = assets::CacheManager::GetMap("data/" + map_name + ".map");
|
||||
|
||||
objs_visible_.resize(map_->GetStaticObjects().size(), true);
|
||||
}
|
||||
|
||||
void game::view::MapInstanceView::Draw(const game::view::DrawArgs& args) const
|
||||
{
|
||||
const auto& basemodel = map_->GetBaseModel();
|
||||
|
||||
if (!basemodel || !basemodel->GetMesh())
|
||||
return;
|
||||
|
||||
const auto& mesh = *basemodel->GetMesh();
|
||||
|
||||
const float max_dist = args.render_distance + 200.0f;
|
||||
const float max_dist2 = max_dist * max_dist;
|
||||
|
||||
for (const auto& chunks = map_->GetChunks(); const auto& chunk : chunks)
|
||||
{
|
||||
glm::vec3 center = (chunk.aabb.min + chunk.aabb.max) * 0.5f;
|
||||
if (glm::distance2(args.eye, center) > max_dist2)
|
||||
continue;
|
||||
|
||||
if (!args.frustum.IsAABBVisible(chunk.aabb))
|
||||
continue;
|
||||
|
||||
DrawChunk(args, mesh, chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void game::view::MapInstanceView::EnableObj(net::ObjNum num, bool enable)
|
||||
{
|
||||
size_t i = static_cast<size_t>(num);
|
||||
if (i >= objs_visible_.size())
|
||||
return;
|
||||
|
||||
objs_visible_[i] = enable;
|
||||
}
|
||||
|
||||
void game::view::MapInstanceView::DrawChunk(const game::view::DrawArgs& args, const assets::Mesh& basemesh,
|
||||
const assets::Chunk& chunk) const
|
||||
{
|
||||
for (const auto& surface_range : chunk.surfaces)
|
||||
{
|
||||
auto& surface = basemesh.surfaces[surface_range.idx];
|
||||
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.first = surface_range.first;
|
||||
cmd.count = surface_range.count;
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
|
||||
const auto& objs = map_->GetStaticObjects();
|
||||
|
||||
for (size_t i = 0; i < chunk.num_objs; ++i)
|
||||
{
|
||||
size_t abs_i = chunk.first_obj + i;
|
||||
|
||||
if (abs_i >= objs.size())
|
||||
continue;
|
||||
|
||||
if (!objs_visible_[abs_i])
|
||||
continue;
|
||||
|
||||
const auto& obj = objs[abs_i];
|
||||
|
||||
if (!obj.model || !obj.model->GetMesh())
|
||||
continue;
|
||||
|
||||
if (!args.frustum.IsAABBVisible(obj.aabb))
|
||||
continue;
|
||||
|
||||
const auto& surfaces = obj.model->GetMesh()->surfaces;
|
||||
|
||||
for (const auto& surface : surfaces)
|
||||
{
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &obj.node.matrix;
|
||||
// cmd.color_mod = glm::vec4(obj.color, 1.0f);
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/gameview/mapinstanceview.hpp
Normal file
30
src/gameview/mapinstanceview.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "assets/map.hpp"
|
||||
#include "draw_args.hpp"
|
||||
#include "net/defs.hpp"
|
||||
|
||||
namespace game::view
|
||||
{
|
||||
|
||||
class MapInstanceView
|
||||
{
|
||||
public:
|
||||
MapInstanceView(const std::string& map_name);
|
||||
|
||||
void Draw(const game::view::DrawArgs& args) const;
|
||||
|
||||
void EnableObj(net::ObjNum num, bool enable);
|
||||
|
||||
private:
|
||||
void DrawChunk(const game::view::DrawArgs& args, const assets::Mesh& basemesh, const assets::Chunk& chunk) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const assets::Map> map_;
|
||||
std::vector<bool> objs_visible_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -6,12 +6,26 @@
|
||||
#include "vehicleview.hpp"
|
||||
#include "client_session.hpp"
|
||||
|
||||
game::view::WorldView::WorldView(ClientSession& session) :
|
||||
session_(session),
|
||||
audiomaster_(session_.GetAudioMaster())
|
||||
game::view::WorldView::WorldView(ClientSession& session, net::InMessage& msg) :
|
||||
session_(session), audiomaster_(session_.GetAudioMaster()), map_("openworld")
|
||||
{
|
||||
map_ = assets::CacheManager::GetMap("data/openworld.map");
|
||||
AddMapCollision(map_);
|
||||
net::MapName mapname;
|
||||
if (!msg.Read(mapname))
|
||||
throw EntityInitError();
|
||||
|
||||
// init destroyed objs
|
||||
net::ObjCount objcount;
|
||||
if (!msg.Read(objcount))
|
||||
throw EntityInitError();
|
||||
|
||||
for (net::ObjCount i = 0; i < objcount; ++i)
|
||||
{
|
||||
net::ObjNum objnum;
|
||||
if (!msg.Read(objnum))
|
||||
throw EntityInitError();
|
||||
|
||||
map_.EnableObj(objnum, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& msg)
|
||||
@ -27,6 +41,12 @@ bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& ms
|
||||
case net::MSG_ENTDESTROY:
|
||||
return ProcessEntDestroyMsg(msg);
|
||||
|
||||
case net::MSG_OBJDESTROY:
|
||||
return ProcessObjDestroyOrRespawnMsg(msg, false);
|
||||
|
||||
case net::MSG_OBJRESPAWN:
|
||||
return ProcessObjDestroyOrRespawnMsg(msg, true);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -44,8 +64,7 @@ void game::view::WorldView::Update(const UpdateInfo& info)
|
||||
|
||||
void game::view::WorldView::Draw(const DrawArgs& args) const
|
||||
{
|
||||
if (map_)
|
||||
map_->Draw(args);
|
||||
map_.Draw(args);
|
||||
|
||||
for (const auto& [entnum, ent] : ents_)
|
||||
{
|
||||
@ -150,3 +169,13 @@ bool game::view::WorldView::ProcessEntDestroyMsg(net::InMessage& msg)
|
||||
ents_.erase(entnum);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::WorldView::ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable)
|
||||
{
|
||||
net::ObjNum objnum;
|
||||
if (!msg.Read(objnum))
|
||||
return false;
|
||||
|
||||
map_.EnableObj(objnum, enable);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "net/inmessage.hpp"
|
||||
#include "collision/dynamicsworld.hpp"
|
||||
#include "entityview.hpp"
|
||||
#include "mapinstanceview.hpp"
|
||||
|
||||
namespace game::view
|
||||
{
|
||||
@ -15,7 +16,7 @@ class ClientSession;
|
||||
class WorldView : public collision::DynamicsWorld
|
||||
{
|
||||
public:
|
||||
WorldView(ClientSession& session);
|
||||
WorldView(ClientSession& session, net::InMessage& msg);
|
||||
|
||||
bool ProcessMsg(net::MessageType type, net::InMessage& msg);
|
||||
|
||||
@ -35,10 +36,12 @@ private:
|
||||
bool ProcessEntMsgMsg(net::InMessage& msg);
|
||||
bool ProcessEntDestroyMsg(net::InMessage& msg);
|
||||
|
||||
bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable);
|
||||
|
||||
private:
|
||||
ClientSession& session_;
|
||||
|
||||
std::shared_ptr<const assets::Map> map_;
|
||||
MapInstanceView map_;
|
||||
std::map<net::EntNum, std::unique_ptr<EntityView>> ents_;
|
||||
|
||||
float time_ = 0.0f;
|
||||
|
||||
@ -31,6 +31,9 @@ enum MessageType : uint8_t
|
||||
// CHWORLD <MapName>
|
||||
MSG_CHWORLD,
|
||||
|
||||
// CAM <EntNum>
|
||||
MSG_CAM,
|
||||
|
||||
/*~~~~~~~~ Entity ~~~~~~~~*/
|
||||
// ENTSPAWN <EntNum> <EntType> data...
|
||||
MSG_ENTSPAWN,
|
||||
@ -39,8 +42,12 @@ enum MessageType : uint8_t
|
||||
// ENTDESTROY <EntNum>
|
||||
MSG_ENTDESTROY,
|
||||
|
||||
// CAM <EntNum>
|
||||
MSG_CAM,
|
||||
/*~~~~~~~~ Destructibles ~~~~~~~~*/
|
||||
// OBJDESTROY <ObjNum>
|
||||
MSG_OBJDESTROY,
|
||||
// OBJRESPAWN <ObjNum>
|
||||
MSG_OBJRESPAWN,
|
||||
|
||||
|
||||
/*~~~~~~~~~~~~~~~~*/
|
||||
MSG_COUNT,
|
||||
@ -58,6 +65,7 @@ constexpr long long PI_D = 78256779;
|
||||
using ViewYawQ = Quantized<uint16_t, 0, 2 * PI_N, PI_D>;
|
||||
using ViewPitchQ = Quantized<uint16_t, -PI_N, PI_N, PI_D>;
|
||||
|
||||
// entities
|
||||
using EntNum = uint16_t;
|
||||
|
||||
enum EntType : uint8_t
|
||||
@ -107,4 +115,8 @@ using AnimTimeQ = Quantized<uint8_t, 0, 1>;
|
||||
using NumClothes = uint8_t;
|
||||
using ClothesName = FixedStr<32>;
|
||||
|
||||
// destructibles
|
||||
using ObjNum = uint16_t;
|
||||
using ObjCount = ObjNum;
|
||||
|
||||
} // namespace net
|
||||
Loading…
x
Reference in New Issue
Block a user