From 76832e975b7b207742d65bfd7321433511e818be Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Tue, 7 Jan 2025 03:06:30 +0000 Subject: [PATCH] [physicsbody] mesh topology will be analysed to automatically determine if the mesh requires a convex or a triangle shape, this removes the need for manual setup of physics objects and automatically makes all small/cheap objects interactable (not enabled yet) --- editor/Widgets/Properties.cpp | 7 +- runtime/Game/Game.cpp | 39 +++++---- runtime/World/Components/PhysicsBody.cpp | 107 ++++++++++++----------- runtime/World/Components/PhysicsBody.h | 1 - 4 files changed, 77 insertions(+), 77 deletions(-) diff --git a/editor/Widgets/Properties.cpp b/editor/Widgets/Properties.cpp index c99889734..891b24c9a 100644 --- a/editor/Widgets/Properties.cpp +++ b/editor/Widgets/Properties.cpp @@ -534,8 +534,7 @@ void Properties::ShowPhysicsBody(shared_ptr body) const "Capsule", "Cone", "Terrain", - "Mesh Convex Hull (Cheap)", - "Mesh (Expensive)" + "Mesh" }; ImGui::Text("Shape Type"); @@ -946,7 +945,7 @@ void Properties::ShowCamera(shared_ptr camera) const ImGui::SameLine(column_pos_x); ImGui::Checkbox("##camera_first_person_control", &first_person_control_enabled); ImGuiSp::tooltip("Enables first person control while holding down the right mouse button (or when a controller is connected)"); - //= MAP ============================================================================================================================================= + //= MAP ======================================================================================================================================================= if (aperture != camera->GetAperture()) camera->SetAperture(aperture); if (shutter_speed != camera->GetShutterSpeed()) camera->SetShutterSpeed(shutter_speed); if (iso != camera->GetIso()) camera->SetIso(iso); @@ -954,7 +953,7 @@ void Properties::ShowCamera(shared_ptr camera) const if (near_plane != camera->GetNearPlane()) camera->SetNearPlane(near_plane); if (far_plane != camera->GetFarPlane()) camera->SetFarPlane(far_plane); if (first_person_control_enabled != camera->GetFlag(CameraFlags::CanBeControlled)) camera->SetFlag(CameraFlags::CanBeControlled, first_person_control_enabled); - //=================================================================================================================================================== + //============================================================================================================================================================= } component_end(); } diff --git a/runtime/Game/Game.cpp b/runtime/Game/Game.cpp index 7cf7d8c35..991a06e85 100644 --- a/runtime/Game/Game.cpp +++ b/runtime/Game/Game.cpp @@ -100,8 +100,8 @@ namespace Spartan // add physics components shared_ptr physics_body = entity->AddComponent(); - physics_body->SetShapeType(PhysicsShape::StaticPlane); physics_body->SetMass(0.0f); + physics_body->SetShapeType(PhysicsShape::StaticPlane); } void create_camera(const Vector3& camera_position = Vector3(0.0f, 2.0f, -10.0f), const Vector3& camera_rotation = Vector3(0.0f, 0.0f, 0.0f)) @@ -113,9 +113,9 @@ namespace Spartan // add a physics body so that the camera can move through the environment in a physical manner PhysicsBody* physics_body = m_default_physics_body_camera->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Capsule); physics_body->SetMass(82.0f); physics_body->SetBoundingBox(Vector3(0.5f, 1.8f, 0.5f)); + physics_body->SetShapeType(PhysicsShape::Capsule); physics_body->SetRotationLock(true); // create the entity that will actual hold the camera component @@ -239,10 +239,11 @@ namespace Spartan // add physics body { PhysicsBody* physics_body = m_default_car->AddComponent().get(); - physics_body->SetBodyType(PhysicsBodyType::Vehicle); physics_body->SetCenterOfMass(Vector3(0.0f, 1.2f, 0.0f)); physics_body->SetBoundingBox(Vector3(3.0f, 1.9f, 7.0f)); physics_body->SetMass(960.0f); // http://www.j-garage.com/toyota/ae86.html + physics_body->SetBodyType(PhysicsBodyType::Vehicle); + physics_body->SetShapeType(PhysicsShape::Box); // disable car control (it's toggled via the gameplay code in Tick()) physics_body->GetCar()->SetControlEnabled(false); @@ -415,8 +416,8 @@ namespace Spartan // add physics components shared_ptr physics_body = entity->AddComponent(); - physics_body->SetShapeType(PhysicsShape::Box); physics_body->SetMass(15.0f); + physics_body->SetShapeType(PhysicsShape::Box); } // flight helmet @@ -437,8 +438,8 @@ namespace Spartan entity->SetScale(Vector3(0.3f, 0.3f, 0.3f)); PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::MeshConvexHull); physics_body->SetMass(8.0f); + physics_body->SetShapeType(PhysicsShape::Mesh); } // material ball @@ -452,8 +453,8 @@ namespace Spartan if (auto mesh_entity = entity->GetDescendantByName("Object_2")) { PhysicsBody* physics_body = mesh_entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::MeshConvexHull); physics_body->SetMass(8.0f); + physics_body->SetShapeType(PhysicsShape::Mesh); } } } @@ -565,9 +566,9 @@ namespace Spartan { // add physics so we can walk on it PhysicsBody* physics_body = m_default_terrain->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Terrain); physics_body->SetMass(0.0f); - + physics_body->SetShapeType(PhysicsShape::Terrain); + // water { shared_ptr water = World::CreateEntity(); @@ -626,10 +627,10 @@ namespace Spartan terrain->GenerateTransforms(&instances, 5000, TerrainProp::Tree); renderable->SetInstances(instances); - // make the bark collidable - shared_ptr rigid_body = bark->AddComponent(); - rigid_body->SetMass(0.0f); - rigid_body->SetShapeType(PhysicsShape::Box); + // make the bark collidable + shared_ptr physics_body = bark->AddComponent(); + physics_body->SetShapeType(PhysicsShape::Mesh); + physics_body->SetMass(0.0f); } if (Entity* branches = entity->GetDescendantByName("Branches")) @@ -671,7 +672,7 @@ namespace Spartan material->SetColor(Color::standard_white); material->SetTexture(MaterialTextureType::Color, "project\\terrain\\vegetation_plant_1\\ormbunke.png"); material->SetProperty(MaterialProperty::SubsurfaceScattering, 0.0f); - material->SetProperty(MaterialProperty::WindAnimation, 1.0f); + material->SetProperty(MaterialProperty::WindAnimation, 1.0f); material->SetProperty(MaterialProperty::WorldSpaceHeight, renderable->GetBoundingBox(BoundingBoxType::Transformed).GetSize().y); material->SetProperty(MaterialProperty::CullMode, static_cast(RHI_CullMode::None)); renderable->SetMaterial(material); @@ -804,8 +805,8 @@ namespace Spartan if (entity->GetComponent() != nullptr) { PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Mesh); physics_body->SetMass(0.0f); // static + physics_body->SetShapeType(PhysicsShape::Mesh); } } } @@ -838,8 +839,8 @@ namespace Spartan if (entity->IsActive() && entity->GetComponent() != nullptr) { PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Mesh); physics_body->SetMass(0.0f); // static + physics_body->SetShapeType(PhysicsShape::Mesh); } } } @@ -877,8 +878,8 @@ namespace Spartan if (entity->IsActive() && entity->GetComponent() != nullptr) { PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Mesh); physics_body->SetMass(0.0f); // static + physics_body->SetShapeType(PhysicsShape::Mesh); } } } @@ -906,8 +907,8 @@ namespace Spartan if (entity->GetComponent() != nullptr) { PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Mesh); physics_body->SetMass(0.0f); // static + physics_body->SetShapeType(PhysicsShape::Mesh); } } } @@ -934,8 +935,8 @@ namespace Spartan if (entity->GetComponent() != nullptr) { PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Mesh); physics_body->SetMass(0.0f); // static + physics_body->SetShapeType(PhysicsShape::Mesh); } } @@ -1058,8 +1059,8 @@ namespace Spartan if (entity->GetComponent() != nullptr) { PhysicsBody* physics_body = entity->AddComponent().get(); - physics_body->SetShapeType(PhysicsShape::Mesh); physics_body->SetMass(0.0f); // static + physics_body->SetShapeType(PhysicsShape::Mesh); } } } diff --git a/runtime/World/Components/PhysicsBody.cpp b/runtime/World/Components/PhysicsBody.cpp index 86e81b3cd..0d48f7ee6 100644 --- a/runtime/World/Components/PhysicsBody.cpp +++ b/runtime/World/Components/PhysicsBody.cpp @@ -85,7 +85,7 @@ namespace Spartan return transform; } - bool is_hollow(Renderable* renderable, const vector& vertices, const Vector3& scale) + bool can_a_capsule_enter(Renderable* renderable, const vector& vertices, const Vector3& scale) { // skip objects which are too small for a human to fit inside if (renderable->GetBoundingBox(BoundingBoxType::Transformed).Volume() < 1.0f) @@ -166,6 +166,7 @@ namespace Spartan PhysicsBody::PhysicsBody(Entity* entity) : Component(entity) { m_gravity = Physics::GetGravity(); + m_car = make_shared(); SP_REGISTER_ATTRIBUTE_VALUE_VALUE(m_mass, float); SP_REGISTER_ATTRIBUTE_VALUE_VALUE(m_friction, float); @@ -267,12 +268,7 @@ namespace Spartan void PhysicsBody::SetMass(float mass) { - mass = Helper::Max(mass, 0.0f); - if (mass != m_mass) - { - m_mass = mass; - AddBodyToWorld(); - } + m_mass = Helper::Max(mass, 0.0f); } void PhysicsBody::SetFriction(float friction) @@ -433,7 +429,12 @@ namespace Spartan void PhysicsBody::SetRotationLock(const Vector3& lock) { - if (!m_rigid_body || m_rotation_lock == lock) + if (!m_rigid_body) + { + SP_LOG_WARNING("This call needs to happen after SetShapeType()"); + } + + if (m_rotation_lock == lock) return; m_rotation_lock = lock; @@ -579,6 +580,7 @@ namespace Spartan // calculate inertia btVector3 inertia = btVector3(0, 0, 0); + if (m_mass != 0.0f) { // maintain any previous inertia (if any) if (m_rigid_body) @@ -589,7 +591,6 @@ namespace Spartan shape->calculateLocalInertia(m_mass, inertia); } - // remove and delete the old body RemoveBodyFromWorld(); // create rigid body @@ -601,7 +602,7 @@ namespace Spartan construction_info.m_restitution = m_restitution; construction_info.m_collisionShape = static_cast(m_shape); construction_info.m_localInertia = inertia; - construction_info.m_motionState = new MotionState(this); // we delete this manually later + construction_info.m_motionState = new MotionState(this); // RemoveBodyFromWorld() deletes this m_rigid_body = new btRigidBody(construction_info); rigid_body->setUserPointer(this); @@ -615,11 +616,6 @@ namespace Spartan if (m_body_type == PhysicsBodyType::Vehicle) { - if (!m_car) - { - m_car = make_unique(); - } - m_car->Create(rigid_body, m_entity_ptr); } @@ -711,8 +707,6 @@ namespace Spartan m_size.x = Helper::Clamp(m_size.x, Helper::SMALL_FLOAT, INFINITY); m_size.y = Helper::Clamp(m_size.y, Helper::SMALL_FLOAT, INFINITY); m_size.z = Helper::Clamp(m_size.z, Helper::SMALL_FLOAT, INFINITY); - - UpdateShape(); } void PhysicsBody::SetShapeType(PhysicsShape type) @@ -730,7 +724,6 @@ namespace Spartan return; m_body_type = type; - AddBodyToWorld(); } bool PhysicsBody::RayTraceIsGrounded() const @@ -816,6 +809,8 @@ namespace Spartan void PhysicsBody::UpdateShape() { + SP_ASSERT(m_shape_type != PhysicsShape::Max); + if (shape) { delete shape; @@ -826,7 +821,7 @@ namespace Spartan vector indices; vector vertices; shared_ptr renderable = nullptr; - if (m_shape_type == PhysicsShape::Mesh || m_shape_type == PhysicsShape::MeshConvexHull) + if (m_shape_type == PhysicsShape::Mesh) { // get renderable renderable = GetEntity()->GetComponent(); @@ -914,53 +909,59 @@ namespace Spartan case PhysicsShape::Mesh: { - btTriangleMesh* shape_local = new btTriangleMesh(); - for (uint32_t i = 0; i < static_cast(indices.size()); i += 3) - { - btVector3 vertex0(vertices[indices[i]].pos[0], vertices[indices[i]].pos[1], vertices[indices[i]].pos[2]); - btVector3 vertex1(vertices[indices[i + 1]].pos[0], vertices[indices[i + 1]].pos[1], vertices[indices[i + 1]].pos[2]); - btVector3 vertex2(vertices[indices[i + 2]].pos[0], vertices[indices[i + 2]].pos[1], vertices[indices[i + 2]].pos[2]); - shape_local->addTriangle(vertex0, vertex1, vertex2); - } - shape_local->setScaling(vector_to_bt(size)); + // determine how much detail is needed for this shape + const bool is_enterable = can_a_capsule_enter(renderable.get(), vertices, size); + const bool is_low_poly = vertices.size() < 1000; + const bool convex_hull = !is_enterable && !is_low_poly; - m_shape = new btBvhTriangleMeshShape(shape_local, true); - break; - } - - case PhysicsShape::MeshConvexHull: - { - // create - btConvexHullShape* shape_convex = new btConvexHullShape( - reinterpret_cast(&vertices[0]), - static_cast(vertices.size()), - static_cast(sizeof(RHI_Vertex_PosTexNorTan)) - ); - shape_convex->optimizeConvexHull(); - - // add to compound - btCompoundShape* shape_compound = new btCompoundShape(); - if (renderable->HasInstancing()) + if (convex_hull) { - for (uint32_t instance_index = 0; instance_index < renderable->GetInstanceCount(); instance_index++) + // create + btConvexHullShape* shape_convex = new btConvexHullShape( + reinterpret_cast(&vertices[0]), + static_cast(vertices.size()), + static_cast(sizeof(RHI_Vertex_PosTexNorTan)) + ); + shape_convex->optimizeConvexHull(); + + // add to compound + btCompoundShape* shape_compound = new btCompoundShape(); + if (renderable->HasInstancing()) + { + for (uint32_t instance_index = 0; instance_index < renderable->GetInstanceCount(); instance_index++) + { + Matrix world_transform = renderable->GetInstanceTransform(instance_index); + shape_compound->addChildShape(compute_transform(world_transform.GetTranslation(), world_transform.GetRotation(), world_transform.GetScale()), shape_convex); + } + } + else { - Matrix world_transform = renderable->GetInstanceTransform(instance_index); - shape_compound->addChildShape(compute_transform(world_transform.GetTranslation(), world_transform.GetRotation(), world_transform.GetScale()), shape_convex); + shape_compound->addChildShape(compute_transform(Vector3::Zero, Quaternion::Identity, size), shape_convex); } + + m_shape = shape_compound; } else { - shape_compound->addChildShape(compute_transform(Vector3::Zero, Quaternion::Identity, size), shape_convex); + btTriangleMesh* shape_local = new btTriangleMesh(); + for (uint32_t i = 0; i < static_cast(indices.size()); i += 3) + { + btVector3 vertex0(vertices[indices[i]].pos[0], vertices[indices[i]].pos[1], vertices[indices[i]].pos[2]); + btVector3 vertex1(vertices[indices[i + 1]].pos[0], vertices[indices[i + 1]].pos[1], vertices[indices[i + 1]].pos[2]); + btVector3 vertex2(vertices[indices[i + 2]].pos[0], vertices[indices[i + 2]].pos[1], vertices[indices[i + 2]].pos[2]); + shape_local->addTriangle(vertex0, vertex1, vertex2); + } + shape_local->setScaling(vector_to_bt(size)); + + m_shape = new btBvhTriangleMeshShape(shape_local, true); + m_mass = 0.0f; // btBvhTriangleMeshShape is static } - - m_shape = shape_compound; + break; } } static_cast(m_shape)->setUserPointer(this); - - // re-add the body to the world so it's re-created with the new shape AddBodyToWorld(); } } diff --git a/runtime/World/Components/PhysicsBody.h b/runtime/World/Components/PhysicsBody.h index 12c39f8fc..cc86436b1 100644 --- a/runtime/World/Components/PhysicsBody.h +++ b/runtime/World/Components/PhysicsBody.h @@ -56,7 +56,6 @@ namespace Spartan Capsule, Cone, Terrain, - MeshConvexHull, Mesh, Max };