Vehicle & destructible bullet hitboxes

This commit is contained in:
tovjemam 2026-06-15 15:26:33 +02:00
parent 8c52678e79
commit 45cf603c83
9 changed files with 85 additions and 16 deletions

View File

@ -21,6 +21,10 @@ static collision::Material GetMaterialByName(const std::string& name)
return collision::PM_GLASS; return collision::PM_GLASS;
else if (name == "flesh") else if (name == "flesh")
return collision::PM_FLESH; return collision::PM_FLESH;
else if (name == "car") // TODO: make new material for cars
return collision::PM_METAL;
else if (name == "carwindow")
return collision::PM_NONE;
else else
return collision::PM_STONE; return collision::PM_STONE;
} }
@ -35,7 +39,7 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
std::unique_ptr<btConvexHullShape> temp_hull; std::unique_ptr<btConvexHullShape> temp_hull;
std::unique_ptr<btCompoundShape> compound; std::unique_ptr<btCompoundShape> compound;
bool current_collision = true; collision::Material col_material = collision::PM_NONE;
LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) { LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) {
if (command == "v") if (command == "v")
@ -91,7 +95,7 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
mb.AddTriangle(t); mb.AddTriangle(t);
) )
if (current_collision && model->cmesh_) if (model->cmesh_)
{ {
glm::vec3 p[3]; glm::vec3 p[3];
for (size_t i = 0; i < 3; ++i) for (size_t i = 0; i < 3; ++i)
@ -225,19 +229,18 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
{ {
std::string pm_name; std::string pm_name;
iss >> pm_name; iss >> pm_name;
if (pm_name == "none")
if (model->cmesh_)
{ {
current_collision = false; model->cmesh_->BeginMaterial(GetMaterialByName(pm_name));
}
else
{
current_collision = true;
if (model->cmesh_)
{
model->cmesh_->BeginMaterial(GetMaterialByName(pm_name));
}
} }
} }
else if (command == "cpm")
{
std::string pm_name;
iss >> pm_name;
col_material = GetMaterialByName(pm_name);
}
else else
{ {
throw std::runtime_error("Unknown command in model file: " + command); throw std::runtime_error("Unknown command in model file: " + command);
@ -268,6 +271,13 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
model->cshape_ = std::move(compound); model->cshape_ = std::move(compound);
} }
if (model->cshape_)
{
collision::SetShapeMaterial(*model->cshape_, col_material);
if (col_material != collision::PM_NONE)
model->cshape_is_bullet_target_ = true;
}
return model; return model;
} }

View File

@ -45,6 +45,7 @@ public:
const glm::vec3& GetColOffset() const { return col_offset_; } 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(); }
bool IsColShapeBulletTarget() const { return cshape_is_bullet_target_; }
const std::shared_ptr<const Skeleton>& GetSkeleton() const { return skeleton_; } const std::shared_ptr<const Skeleton>& GetSkeleton() const { return skeleton_; }
CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; }) CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; })
@ -60,6 +61,7 @@ private:
// std::vector<ModelCollisionShape> cshapes_; // std::vector<ModelCollisionShape> cshapes_;
std::vector<std::unique_ptr<btCollisionShape>> subshapes_; std::vector<std::unique_ptr<btCollisionShape>> subshapes_;
std::unique_ptr<btCollisionShape> cshape_; std::unique_ptr<btCollisionShape> cshape_;
bool cshape_is_bullet_target_ = false;
std::shared_ptr<const Skeleton> skeleton_; std::shared_ptr<const Skeleton> skeleton_;
CLIENT_ONLY(std::shared_ptr<const Mesh> mesh_;); CLIENT_ONLY(std::shared_ptr<const Mesh> mesh_;);

View File

@ -9,6 +9,7 @@ namespace collision
enum Material : uint8_t enum Material : uint8_t
{ {
PM_NONE,
PM_STONE, PM_STONE,
PM_DIRT, PM_DIRT,
PM_GRASS, PM_GRASS,

View File

@ -11,6 +11,9 @@ void collision::TriangleMesh::BeginMaterial(Material material)
void collision::TriangleMesh::AddTriangle(const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2) void collision::TriangleMesh::AddTriangle(const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2)
{ {
if (current_material_ == PM_NONE)
return;
btVector3 bt_v0(v0.x, v0.y, v0.z); btVector3 bt_v0(v0.x, v0.y, v0.z);
btVector3 bt_v1(v1.x, v1.y, v1.z); btVector3 bt_v1(v1.x, v1.y, v1.z);
btVector3 bt_v2(v2.x, v2.y, v2.z); btVector3 bt_v2(v2.x, v2.y, v2.z);

View File

@ -26,7 +26,7 @@ public:
btBvhTriangleMeshShape* GetShape() const { return bt_shape_.get(); } btBvhTriangleMeshShape* GetShape() const { return bt_shape_.get(); }
private: private:
Material current_material_ = PM_STONE; Material current_material_ = PM_NONE;
btTriangleMesh bt_mesh_; btTriangleMesh bt_mesh_;
std::unique_ptr<btBvhTriangleMeshShape> bt_shape_; std::unique_ptr<btBvhTriangleMeshShape> bt_shape_;
std::vector<Material> tri_materials_; std::vector<Material> tri_materials_;

View File

@ -76,11 +76,19 @@ game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world,
model_->GetParamFloat("destr_th", destr_th_); model_->GetParamFloat("destr_th", destr_th_);
} }
col_group_ = collision::OG_DEFAULT;
col_mask_ = collision::OG_ALL;
// prefer simple cshape which allow destruction // prefer simple cshape which allow destruction
if (cshape) if (cshape)
{ {
body_ = std::make_unique<btRigidBody>( body_ = std::make_unique<btRigidBody>(
btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, cshape, local_inertia)); btRigidBody::btRigidBodyConstructionInfo(mass, nullptr, cshape, local_inertia));
if (!model_->IsColShapeBulletTarget())
{
col_mask_ &= ~collision::OG_PROJECTILE;
}
} }
else if (cmesh) else if (cmesh)
{ {
@ -96,7 +104,7 @@ game::MapObjectCollision::MapObjectCollision(collision::DynamicsWorld& world,
collision::SetObjectInfo(body_.get(), collision::OT_MAP_OBJECT, oflags, this); collision::SetObjectInfo(body_.get(), collision::OT_MAP_OBJECT, oflags, this);
// world_.GetBtWorld().addRigidBody(body_.get(), btBroadphaseProxy::StaticFilter, btBroadphaseProxy::AllFilter); // world_.GetBtWorld().addRigidBody(body_.get(), btBroadphaseProxy::StaticFilter, btBroadphaseProxy::AllFilter);
world_.GetBtWorld().addRigidBody(body_.get()); world_.GetBtWorld().addRigidBody(body_.get(), col_group_, col_mask_);
} }
void game::MapObjectCollision::Break() void game::MapObjectCollision::Break()
@ -122,7 +130,7 @@ void game::MapObjectCollision::Break()
collision::SetObjectInfo(body_.get(), collision::OT_UNDEFINED, 0, this); collision::SetObjectInfo(body_.get(), collision::OT_UNDEFINED, 0, this);
world_.GetBtWorld().addRigidBody(body_.get()); world_.GetBtWorld().addRigidBody(body_.get(), col_group_, col_mask_);
} }
void game::MapObjectCollision::GetModelTransform(Transform& trans) const void game::MapObjectCollision::GetModelTransform(Transform& trans) const

View File

@ -31,6 +31,8 @@ public:
net::ObjNum GetNum() const { return num_; } net::ObjNum GetNum() const { return num_; }
float GetDestroyThreshold() const { return destr_th_; } float GetDestroyThreshold() const { return destr_th_; }
int col_group_ = 0, col_mask_ = 0;
virtual ~MapObjectCollision() override; virtual ~MapObjectCollision() override;
private: private:

View File

@ -38,6 +38,11 @@ void game::Vehicle::Update()
{ {
Super::Update(); Super::Update();
if (physics_)
{
physics_->Update();
}
root_.UpdateMatrix(); root_.UpdateMatrix();
flags_ = 0; flags_ = 0;
@ -99,7 +104,7 @@ void game::Vehicle::OnBulletHit(const game::BulletInfo& bullet, const btCollisio
if (!physics_) if (!physics_)
return; return;
auto impulse = glm::normalize(bullet.end - bullet.start) * 10000.0f; auto impulse = glm::normalize(bullet.end - bullet.start) * 100.0f;
physics_->GetBtBody().activate(); physics_->GetBtBody().activate();
physics_->GetBtBody().applyCentralImpulse(btVector3(impulse.x, impulse.y, impulse.z)); physics_->GetBtBody().applyCentralImpulse(btVector3(impulse.x, impulse.y, impulse.z));
} }
@ -728,6 +733,25 @@ game::VehiclePhysics::VehiclePhysics(collision::DynamicsWorld& world, Transform&
bt_world.addRigidBody(body_.get(), collision::OG_DEFAULT, ~collision::OG_PROJECTILE); bt_world.addRigidBody(body_.get(), collision::OG_DEFAULT, ~collision::OG_PROJECTILE);
bt_world.addAction(vehicle_.get()); bt_world.addAction(vehicle_.get());
// make bullet hitbox
auto col_mesh = model.GetModel()->GetColMesh();
if (col_mesh)
{
bullet_hitbox_ = std::make_unique<btCollisionObject>();
bullet_hitbox_->setCollisionShape(col_mesh->GetShape());
collision::SetObjectInfo(bullet_hitbox_.get(), collision::OT_ENTITY, 0, &obj_cb);
bt_world.addCollisionObject(bullet_hitbox_.get(), collision::OG_DEFAULT, collision::OG_PROJECTILE);
UpdateBulletHitboxTransform();
}
}
void game::VehiclePhysics::Update()
{
UpdateBulletHitboxTransform();
} }
game::VehiclePhysics::~VehiclePhysics() game::VehiclePhysics::~VehiclePhysics()
@ -735,4 +759,17 @@ game::VehiclePhysics::~VehiclePhysics()
auto& bt_world = world_.GetBtWorld(); auto& bt_world = world_.GetBtWorld();
bt_world.removeRigidBody(body_.get()); bt_world.removeRigidBody(body_.get());
bt_world.removeAction(vehicle_.get()); bt_world.removeAction(vehicle_.get());
if (bullet_hitbox_)
{
bt_world.removeCollisionObject(bullet_hitbox_.get());
}
}
void game::VehiclePhysics::UpdateBulletHitboxTransform()
{
if (!bullet_hitbox_)
return;
bullet_hitbox_->setWorldTransform(body_->getWorldTransform());
} }

View File

@ -41,16 +41,22 @@ public:
DELETE_COPY_MOVE(VehiclePhysics) DELETE_COPY_MOVE(VehiclePhysics)
void Update();
btRigidBody& GetBtBody() { return *body_; } btRigidBody& GetBtBody() { return *body_; }
collision::RaycastVehicle& GetBtVehicle() { return *vehicle_; } collision::RaycastVehicle& GetBtVehicle() { return *vehicle_; }
~VehiclePhysics(); ~VehiclePhysics();
private:
void UpdateBulletHitboxTransform();
private: private:
collision::DynamicsWorld& world_; collision::DynamicsWorld& world_;
collision::MotionState motion_; collision::MotionState motion_;
std::unique_ptr<btRigidBody> body_; std::unique_ptr<btRigidBody> body_;
std::unique_ptr<collision::RaycastVehicle> vehicle_; std::unique_ptr<collision::RaycastVehicle> vehicle_;
std::unique_ptr<btCollisionObject> bullet_hitbox_;
}; };
class Vehicle : public Entity class Vehicle : public Entity