From 45cf603c834dc59d91ea147f732452d405687058 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Mon, 15 Jun 2026 15:26:33 +0200 Subject: [PATCH] Vehicle & destructible bullet hitboxes --- src/assets/model.cpp | 34 ++++++++++++++++++----------- src/assets/model.hpp | 2 ++ src/collision/shape_info.hpp | 1 + src/collision/trianglemesh.cpp | 3 +++ src/collision/trianglemesh.hpp | 2 +- src/game/mapinstance.cpp | 12 +++++++++-- src/game/mapinstance.hpp | 2 ++ src/game/vehicle.cpp | 39 +++++++++++++++++++++++++++++++++- src/game/vehicle.hpp | 6 ++++++ 9 files changed, 85 insertions(+), 16 deletions(-) diff --git a/src/assets/model.cpp b/src/assets/model.cpp index efac44a..3b96e89 100644 --- a/src/assets/model.cpp +++ b/src/assets/model.cpp @@ -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 assets::Model::LoadFromFile(const std::stri std::unique_ptr temp_hull; std::unique_ptr 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 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 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 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; } diff --git a/src/assets/model.hpp b/src/assets/model.hpp index d41e24a..db39103 100644 --- a/src/assets/model.hpp +++ b/src/assets/model.hpp @@ -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& GetSkeleton() const { return skeleton_; } CLIENT_ONLY(const std::shared_ptr& GetMesh() const { return mesh_; }) @@ -60,6 +61,7 @@ private: // std::vector cshapes_; std::vector> subshapes_; std::unique_ptr cshape_; + bool cshape_is_bullet_target_ = false; std::shared_ptr skeleton_; CLIENT_ONLY(std::shared_ptr mesh_;); diff --git a/src/collision/shape_info.hpp b/src/collision/shape_info.hpp index 9b8064b..5496bea 100644 --- a/src/collision/shape_info.hpp +++ b/src/collision/shape_info.hpp @@ -9,6 +9,7 @@ namespace collision enum Material : uint8_t { + PM_NONE, PM_STONE, PM_DIRT, PM_GRASS, diff --git a/src/collision/trianglemesh.cpp b/src/collision/trianglemesh.cpp index 1b63c86..ff2acfd 100644 --- a/src/collision/trianglemesh.cpp +++ b/src/collision/trianglemesh.cpp @@ -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); diff --git a/src/collision/trianglemesh.hpp b/src/collision/trianglemesh.hpp index 48e338b..1fb90fb 100644 --- a/src/collision/trianglemesh.hpp +++ b/src/collision/trianglemesh.hpp @@ -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 bt_shape_; std::vector tri_materials_; diff --git a/src/game/mapinstance.cpp b/src/game/mapinstance.cpp index e2f6b51..0c232b5 100644 --- a/src/game/mapinstance.cpp +++ b/src/game/mapinstance.cpp @@ -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::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 diff --git a/src/game/mapinstance.hpp b/src/game/mapinstance.hpp index ffe2454..0b3ed4d 100644 --- a/src/game/mapinstance.hpp +++ b/src/game/mapinstance.hpp @@ -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: diff --git a/src/game/vehicle.cpp b/src/game/vehicle.cpp index 1ef949a..8404f7b 100644 --- a/src/game/vehicle.cpp +++ b/src/game/vehicle.cpp @@ -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(); + 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()); } diff --git a/src/game/vehicle.hpp b/src/game/vehicle.hpp index 36a729b..e4f8a47 100644 --- a/src/game/vehicle.hpp +++ b/src/game/vehicle.hpp @@ -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 body_; std::unique_ptr vehicle_; + std::unique_ptr bullet_hitbox_; }; class Vehicle : public Entity