Skip to content

Commit

Permalink
[physicsbody] mesh topology will be analysed to automatically determi…
Browse files Browse the repository at this point in the history
…ne 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)
  • Loading branch information
PanosK92 committed Jan 7, 2025
1 parent f9239e3 commit 76832e9
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 77 deletions.
7 changes: 3 additions & 4 deletions editor/Widgets/Properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,7 @@ void Properties::ShowPhysicsBody(shared_ptr<PhysicsBody> body) const
"Capsule",
"Cone",
"Terrain",
"Mesh Convex Hull (Cheap)",
"Mesh (Expensive)"
"Mesh"
};

ImGui::Text("Shape Type");
Expand Down Expand Up @@ -946,15 +945,15 @@ void Properties::ShowCamera(shared_ptr<Camera> 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);
if (fov != camera->GetFovHorizontalDeg()) camera->SetFovHorizontalDeg(fov);
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();
}
Expand Down
39 changes: 20 additions & 19 deletions runtime/Game/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ namespace Spartan

// add physics components
shared_ptr<PhysicsBody> physics_body = entity->AddComponent<PhysicsBody>();
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))
Expand All @@ -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<PhysicsBody>().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
Expand Down Expand Up @@ -239,10 +239,11 @@ namespace Spartan
// add physics body
{
PhysicsBody* physics_body = m_default_car->AddComponent<PhysicsBody>().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);
Expand Down Expand Up @@ -415,8 +416,8 @@ namespace Spartan

// add physics components
shared_ptr<PhysicsBody> physics_body = entity->AddComponent<PhysicsBody>();
physics_body->SetShapeType(PhysicsShape::Box);
physics_body->SetMass(15.0f);
physics_body->SetShapeType(PhysicsShape::Box);
}

// flight helmet
Expand All @@ -437,8 +438,8 @@ namespace Spartan
entity->SetScale(Vector3(0.3f, 0.3f, 0.3f));

PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::MeshConvexHull);
physics_body->SetMass(8.0f);
physics_body->SetShapeType(PhysicsShape::Mesh);
}

// material ball
Expand All @@ -452,8 +453,8 @@ namespace Spartan
if (auto mesh_entity = entity->GetDescendantByName("Object_2"))
{
PhysicsBody* physics_body = mesh_entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::MeshConvexHull);
physics_body->SetMass(8.0f);
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}
}
Expand Down Expand Up @@ -565,9 +566,9 @@ namespace Spartan
{
// add physics so we can walk on it
PhysicsBody* physics_body = m_default_terrain->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Terrain);
physics_body->SetMass(0.0f);

physics_body->SetShapeType(PhysicsShape::Terrain);

// water
{
shared_ptr<Entity> water = World::CreateEntity();
Expand Down Expand Up @@ -626,10 +627,10 @@ namespace Spartan
terrain->GenerateTransforms(&instances, 5000, TerrainProp::Tree);
renderable->SetInstances(instances);

// make the bark collidable
shared_ptr<PhysicsBody> rigid_body = bark->AddComponent<PhysicsBody>();
rigid_body->SetMass(0.0f);
rigid_body->SetShapeType(PhysicsShape::Box);
// make the bark collidable
shared_ptr<PhysicsBody> physics_body = bark->AddComponent<PhysicsBody>();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f);
}

if (Entity* branches = entity->GetDescendantByName("Branches"))
Expand Down Expand Up @@ -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<float>(RHI_CullMode::None));
renderable->SetMaterial(material);
Expand Down Expand Up @@ -804,8 +805,8 @@ namespace Spartan
if (entity->GetComponent<Renderable>() != nullptr)
{
PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f); // static
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}
}
Expand Down Expand Up @@ -838,8 +839,8 @@ namespace Spartan
if (entity->IsActive() && entity->GetComponent<Renderable>() != nullptr)
{
PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f); // static
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}
}
Expand Down Expand Up @@ -877,8 +878,8 @@ namespace Spartan
if (entity->IsActive() && entity->GetComponent<Renderable>() != nullptr)
{
PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f); // static
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}
}
Expand Down Expand Up @@ -906,8 +907,8 @@ namespace Spartan
if (entity->GetComponent<Renderable>() != nullptr)
{
PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f); // static
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}
}
Expand All @@ -934,8 +935,8 @@ namespace Spartan
if (entity->GetComponent<Renderable>() != nullptr)
{
PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f); // static
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}

Expand Down Expand Up @@ -1058,8 +1059,8 @@ namespace Spartan
if (entity->GetComponent<Renderable>() != nullptr)
{
PhysicsBody* physics_body = entity->AddComponent<PhysicsBody>().get();
physics_body->SetShapeType(PhysicsShape::Mesh);
physics_body->SetMass(0.0f); // static
physics_body->SetShapeType(PhysicsShape::Mesh);
}
}
}
Expand Down
107 changes: 54 additions & 53 deletions runtime/World/Components/PhysicsBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace Spartan
return transform;
}

bool is_hollow(Renderable* renderable, const vector<RHI_Vertex_PosTexNorTan>& vertices, const Vector3& scale)
bool can_a_capsule_enter(Renderable* renderable, const vector<RHI_Vertex_PosTexNorTan>& vertices, const Vector3& scale)
{
// skip objects which are too small for a human to fit inside
if (renderable->GetBoundingBox(BoundingBoxType::Transformed).Volume() < 1.0f)
Expand Down Expand Up @@ -166,6 +166,7 @@ namespace Spartan
PhysicsBody::PhysicsBody(Entity* entity) : Component(entity)
{
m_gravity = Physics::GetGravity();
m_car = make_shared<Car>();

SP_REGISTER_ATTRIBUTE_VALUE_VALUE(m_mass, float);
SP_REGISTER_ATTRIBUTE_VALUE_VALUE(m_friction, float);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -589,7 +591,6 @@ namespace Spartan
shape->calculateLocalInertia(m_mass, inertia);
}

// remove and delete the old body
RemoveBodyFromWorld();

// create rigid body
Expand All @@ -601,7 +602,7 @@ namespace Spartan
construction_info.m_restitution = m_restitution;
construction_info.m_collisionShape = static_cast<btCollisionShape*>(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);
Expand All @@ -615,11 +616,6 @@ namespace Spartan

if (m_body_type == PhysicsBodyType::Vehicle)
{
if (!m_car)
{
m_car = make_unique<Car>();
}

m_car->Create(rigid_body, m_entity_ptr);
}

Expand Down Expand Up @@ -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)
Expand All @@ -730,7 +724,6 @@ namespace Spartan
return;

m_body_type = type;
AddBodyToWorld();
}

bool PhysicsBody::RayTraceIsGrounded() const
Expand Down Expand Up @@ -816,6 +809,8 @@ namespace Spartan

void PhysicsBody::UpdateShape()
{
SP_ASSERT(m_shape_type != PhysicsShape::Max);

if (shape)
{
delete shape;
Expand All @@ -826,7 +821,7 @@ namespace Spartan
vector<uint32_t> indices;
vector<RHI_Vertex_PosTexNorTan> vertices;
shared_ptr<Renderable> renderable = nullptr;
if (m_shape_type == PhysicsShape::Mesh || m_shape_type == PhysicsShape::MeshConvexHull)
if (m_shape_type == PhysicsShape::Mesh)
{
// get renderable
renderable = GetEntity()->GetComponent<Renderable>();
Expand Down Expand Up @@ -914,53 +909,59 @@ namespace Spartan

case PhysicsShape::Mesh:
{
btTriangleMesh* shape_local = new btTriangleMesh();
for (uint32_t i = 0; i < static_cast<uint32_t>(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<btScalar*>(&vertices[0]),
static_cast<uint32_t>(vertices.size()),
static_cast<uint32_t>(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<btScalar*>(&vertices[0]),
static_cast<uint32_t>(vertices.size()),
static_cast<uint32_t>(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<uint32_t>(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<btCollisionShape*>(m_shape)->setUserPointer(this);

// re-add the body to the world so it's re-created with the new shape
AddBodyToWorld();
}
}
Loading

0 comments on commit 76832e9

Please sign in to comment.