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;
else if (name == "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
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<btCompoundShape> compound;
bool current_collision = true;
collision::Material col_material = collision::PM_NONE;
LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) {
if (command == "v")
@ -91,7 +95,7 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
mb.AddTriangle(t);
)
if (current_collision && model->cmesh_)
if (model->cmesh_)
{
glm::vec3 p[3];
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;
iss >> pm_name;
if (pm_name == "none")
if (model->cmesh_)
{
current_collision = false;
}
else
{
current_collision = true;
if (model->cmesh_)
{
model->cmesh_->BeginMaterial(GetMaterialByName(pm_name));
}
model->cmesh_->BeginMaterial(GetMaterialByName(pm_name));
}
}
else if (command == "cpm")
{
std::string pm_name;
iss >> pm_name;
col_material = GetMaterialByName(pm_name);
}
else
{
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);
}
if (model->cshape_)
{
collision::SetShapeMaterial(*model->cshape_, col_material);
if (col_material != collision::PM_NONE)
model->cshape_is_bullet_target_ = true;
}
return model;
}

View File

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

View File

@ -9,6 +9,7 @@ namespace collision
enum Material : uint8_t
{
PM_NONE,
PM_STONE,
PM_DIRT,
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)
{
if (current_material_ == PM_NONE)
return;
btVector3 bt_v0(v0.x, v0.y, v0.z);
btVector3 bt_v1(v1.x, v1.y, v1.z);
btVector3 bt_v2(v2.x, v2.y, v2.z);

View File

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

View File

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

View File

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

View File

@ -38,6 +38,11 @@ void game::Vehicle::Update()
{
Super::Update();
if (physics_)
{
physics_->Update();
}
root_.UpdateMatrix();
flags_ = 0;
@ -99,7 +104,7 @@ void game::Vehicle::OnBulletHit(const game::BulletInfo& bullet, const btCollisio
if (!physics_)
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().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.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()
@ -735,4 +759,17 @@ game::VehiclePhysics::~VehiclePhysics()
auto& bt_world = world_.GetBtWorld();
bt_world.removeRigidBody(body_.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)
void Update();
btRigidBody& GetBtBody() { return *body_; }
collision::RaycastVehicle& GetBtVehicle() { return *vehicle_; }
~VehiclePhysics();
private:
void UpdateBulletHitboxTransform();
private:
collision::DynamicsWorld& world_;
collision::MotionState motion_;
std::unique_ptr<btRigidBody> body_;
std::unique_ptr<collision::RaycastVehicle> vehicle_;
std::unique_ptr<btCollisionObject> bullet_hitbox_;
};
class Vehicle : public Entity