Program Listing for File fracturer.cpp¶
↰ Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/physics/components/fracturer.cpp
)
#include <physics/components/fracturer.hpp>
#include <physics/data/physics_manifold.hpp>
#include <physics/physics_statics.hpp>
#include <physics/colliders/convexcollider.hpp>
#include <physics/data/identifier.hpp>
#include <physics/physics_statics.hpp>
namespace legion::physics
{
ecs::EcsRegistry* Fracturer::registry = nullptr;
void Fracturer::HandleFracture(physics_manifold& manifold, bool& manifoldValid,bool isfracturingA)
{
return;
OPTICK_EVENT();
if (!IsFractureConditionMet(manifold,isfracturingA) || manifold.contacts.empty()) { return; }
//log::debug("manifold invalidated");
manifoldValid = false;
math::vec3 impactPoint = GetImpactPointFromManifold(manifold);
float impactRadius = 0.2f;
auto fracturedEnt = isfracturingA ? manifold.entityA : manifold.entityB;
auto collider = isfracturingA ? manifold.colliderA : manifold.colliderB;
FractureParams params(impactPoint, 0.0f);
ExplodeEntity(fracturedEnt, params, collider);
}
void Fracturer::ExplodeEntity(ecs::entity_handle ownerEntity, const FractureParams& fractureParams, PhysicsCollider* entityCollider)
{
log::debug("------------------------------------- ExplodeEntity ---------------------------------------");
if (!entityCollider)
{
log::debug("colliders size {} "
, ownerEntity.read_component<physicsComponent>().colliders.size());
auto physicsComp = ownerEntity.get_component_handle<physicsComponent>().read();
entityCollider = physicsComp.colliders.at(0).get();
}
//ownerEntity.read_component<physicsComponent>().colliders.at(0)
auto [min, max] = entityCollider->GetMinMaxWorldAABB();
//-----------------------------------------------------------------------------------------------------------------------------//
//Generate a Voronoi Diagram, for now, the points are manually generated //
//-----------------------------------------------------------------------------------------------------------------------------//
std::vector<math::vec3> voronoiPoints;
QuadrantVoronoi(min, max, voronoiPoints);
//math::vec3 six = third + math::vec3(-0.2f, 0.0f, -0.0f);
//voronoiPoints.push_back(six);
for (auto point : voronoiPoints)
{/*
debug::drawLine(point,
point + math::vec3(0, 0.1f, 0), math::colors::magenta, 8.0f, FLT_MAX, true);*/
}
std::vector<std::vector<math::vec3>> groupedPoints(voronoiPoints.size());
GetVoronoiPoints(groupedPoints,
voronoiPoints, min, max);
//-----------------------------------------------------------------------------------------------------------------------------//
//Using the voronoi points, generate a set of colliders //
//-----------------------------------------------------------------------------------------------------------------------------//
std::vector<std::shared_ptr<ConvexCollider>> voronoiColliders;
InstantiateVoronoiColliders(voronoiColliders, groupedPoints);
std::vector< FracturerColliderToMeshPairing> colliderToMeshPairings;
//-----------------------------------------------------------------------------------------------------------------------------//
//From the mesh about to be fractured, get the pairs of colliders to Mesh //
//-----------------------------------------------------------------------------------------------------------------------------//
InstantiateColliderMeshPairingWithEntity(ownerEntity,
colliderToMeshPairings);
for (size_t i = 0; i < ownerEntity.child_count(); i++)
{
auto child = ownerEntity.get_child(i);
InstantiateColliderMeshPairingWithEntity(child,
colliderToMeshPairings);
}
std::vector<ecs::entity_handle> entitiesGenerated;
//-----------------------------------------------------------------------------------------------------------------------------//
//From the mesh about to be fractured, get the pairs of colliders to Mesh //
//-----------------------------------------------------------------------------------------------------------------------------//
GenerateFractureFragments(entitiesGenerated, colliderToMeshPairings,
voronoiColliders, ownerEntity);
auto originalRB = ownerEntity.get_component_handle<rigidbody>().read();
float fragmentMass = 1.0f / originalRB.inverseMass;
fragmentMass /= entitiesGenerated.size();
int colliderIter = 0;
for (auto ent : entitiesGenerated)
{
auto [posH, rotH, scaleH] = ent.get_component_handles<transform>();
math::mat4 trans = math::compose(scaleH.read(), rotH.read(), posH.read());
//generate hull
auto physicsCompHandle = ent.add_component<physicsComponent>();
auto physicsComp = physicsCompHandle.read();
auto meshFilter = ent.read_component<mesh_filter>();
auto convexCollider = physicsComp.ConstructConvexHull(meshFilter);
physicsCompHandle.write(physicsComp);
//add rigidbody
auto posHandle = ent.get_component_handle<position>();
auto rbH = ent.add_component<rigidbody>();
auto fragmentRB = rbH.read();
fragmentRB.globalCentreOfMass = posH.read();
//add force based on distance from explosion point
math::vec3 distanceFromCentroid = posH.read() - fractureParams.explosionCentroid ;
math::vec3 forceDir = math::normalize(distanceFromCentroid);
float forceAmount = (1.0f / (math::length(distanceFromCentroid))) * fractureParams.strength;
//crude estimation of explosion point
float smallestDot = std::numeric_limits<float>::max();
HalfEdgeFace* chosenFace = nullptr;
for (auto face : convexCollider->GetHalfEdgeFaces())
{
float currentDot = math::dot(forceDir, face->normal);
if (currentDot < smallestDot)
{
smallestDot = currentDot;
chosenFace = face;
}
}
math::vec3 explosionPoint = trans * math::vec4(chosenFace->centroid, 1);
fragmentRB.addForceAt(explosionPoint,forceDir * forceAmount);
rbH.write(fragmentRB);
colliderIter++;
}
registry->destroyEntity(ownerEntity );
fractureCount++;
}
void Fracturer::GetVoronoiPoints(std::vector<std::vector<math::vec3>>& groupedPoints,
std::vector<math::vec3>& voronoiPoints,math::vec3 min,math::vec3 max)
{
time::timer tick;
auto vectorList = PhysicsStatics::GenerateVoronoi(voronoiPoints, min.x, max.x, min.y, max.y, min.z, max.z, 1, 1, 1);
vectorList.pop_back();
//groupedPoints.reserve( voronoiPoints.size() );
for (std::vector<math::vec4>& vector : vectorList)
{
for (const math::vec4& position : vector)
{
int id = position.w;
//log::debug("position {} id {} ",math::to_string(math::vec3(position)), id);
groupedPoints.at(id).push_back(position);
}
}
}
void Fracturer::InstantiateVoronoiColliders(std::vector<std::shared_ptr<ConvexCollider>>& voronoiColliders
,std::vector<std::vector<math::vec3>>& groupedPoints)
{
time::timer tick;
int i = 1;
for (std::vector<math::vec3>& vector : groupedPoints)
{
//math::color debugColor =
// math::color(math::linearRand(0.0f, 0.3f), math::linearRand(0.0f, 0.3f), math::linearRand(0.0f, 0.3f));
math::vec3 debugOffset = math::vec3();
//math::mat4 transform =
// math::compose(math::vec3(1.0f), math::identity<math::quat>(), debugOffset);
for (math::vec3 vertex : vector)
{
//
math::vec3 vertPos = vertex + debugOffset;
//debug::user_projectDrawLine(vertPos, vertPos + math::vec3(0,0.5,0), debugColor,10.0f,FLT_MAX);
}
auto newCollider = std::make_shared<ConvexCollider>();
debugVectorcolliders.push_back(newCollider);
voronoiColliders.push_back(newCollider);
verticesList.push_back(vector);
newCollider->ConstructConvexHullWithVertices(vector);
//transforms.push_back(transform);
//newCollider->DrawColliderRepresentation(transform,math::colors::green,6.0f,FLT_MAX);
i++;
}
}
void Fracturer::GenerateFractureFragments(std::vector<ecs::entity_handle>& entitiesGenerated
, std::vector< FracturerColliderToMeshPairing>& colliderToMeshPairings
,std::vector< std::shared_ptr<ConvexCollider>>& voronoiColliders
,ecs::entity_handle fracturedEnt)
{
fast_time totalMeshSplitting = 0;
fast_time totalCollisionDetection = 0;
int fractureID = 0;
//for each instantiated convex collider
for (std::shared_ptr<ConvexCollider> instantiatedVoronoiCollider : voronoiColliders)
{
for (auto& meshToColliderPairing : colliderToMeshPairings)
{
//check if it collides with one of the colliders in the original physics component
physics_manifold manifold;
ConvexConvexCollisionInfo collisionInfo;
auto ownerEntity = meshToColliderPairing.meshSplitterPairing.
entity;
auto [posH, rotH, scaleH] = ownerEntity.get_component_handles<transform>();
auto transformB = math::compose(scaleH.read(), rotH.read(), posH.read());
time::timer convexConvexCollision;
/* PhysicsStatics::DetectConvexConvexCollision(instantiatedVoronoiCollider.get()
, meshToColliderPairing.colliderPair.get(), math::mat4(1.0f), transformB, collisionInfo, manifold);*/
manifold.isColliding = true;
totalCollisionDetection += convexConvexCollision.elapsedTime().milliseconds();
if (manifold.isColliding)
{
if (fractureID < 999) //2
{
//log::debug("-> Collision Found");
std::vector<MeshSplitParams> splittingParams;
meshToColliderPairing.GenerateSplittingParamsFromCollider(instantiatedVoronoiCollider, splittingParams);
//log::debug("splittingParams {} ", splittingParams.size());
for (size_t i = 0; i < splittingParams.size(); i++)
{
float interpolant = (float)i / splittingParams.size();
math::vec3 color = math::color(1, 0, 0) * interpolant;
/*debug::user_projectDrawLine(splittingParams.at(i).planePostion
, splittingParams.at(i).planePostion + splittingParams.at(i).planeNormal,
math::color(color.x, color.y, color.z, 1), 15.0f, FLT_MAX, true);*/
}
auto splitter = meshToColliderPairing.meshSplitterPairing.read();
time::timer meshSplitting;
splitter.MultipleSplitMesh(splittingParams, entitiesGenerated, true, -1);
totalMeshSplitting += meshSplitting.elapsedTime().milliseconds();
meshToColliderPairing.meshSplitterPairing.write(splitter);
}
}
fractureID++;
}
}
registry->destroyEntity(fracturedEnt);
}
void Fracturer::QuadrantVoronoi(math::vec3& min,math::vec3& max, std::vector<math::vec3>& voronoiPoints)
{
math::vec3 difference = max - min;
math::vec3 differenceQuadrant = difference / 4.0f;
math::vec3 first = min + differenceQuadrant;
voronoiPoints.push_back(first);
math::vec3 second = max - differenceQuadrant;
voronoiPoints.push_back(second);
math::vec3 third = max - (differenceQuadrant * 2);
voronoiPoints.push_back(third);
math::vec3 fourth = third + math::vec3(0.2f, 0, 0);
voronoiPoints.push_back(fourth);
math::vec3 fifth = third + math::vec3(0, -0.1f, 0);
voronoiPoints.push_back(fifth);
//math::vec3 sixth = min + differenceQuadrant + math::vec3(0.1, 0.25f, 0.1);
//voronoiPoints.push_back(sixth);
math::vec3 centroid = (min + max) / 2.0f;
int rand = math::linearRand(0, 5);
//log::debug("rand {} ",rand );
for (math::vec3& point : voronoiPoints)
{
math::vec3 vecFromCentroid = point - centroid;
vecFromCentroid = math::rotateY(vecFromCentroid, math::deg2rad(90.0f * rand));
point = centroid + vecFromCentroid;
}
}
void Fracturer::BalancedVoronoi(math::vec3& min, math::vec3& max, std::vector<math::vec3>& voronoiPoints)
{
math::vec3 difference = max - min;
math::vec3 first = min + difference * 0.1f;
voronoiPoints.push_back(first);
}
bool Fracturer::IsFractureConditionMet(physics_manifold& manifold, bool isfracturingA)
{
//TODO the user should be able to create their own fracture condition, but for now
//, limit it so something can only be fractured once
auto fractureInstigator = isfracturingA ? manifold.entityB : manifold.entityA;
bool isAtMomentumThreshold = false;
auto rbH =fractureInstigator.get_component_handle<rigidbody>();
if (rbH)
{
auto rb = rbH.read();
auto mass = (1.0f / rb.inverseMass);
isAtMomentumThreshold = math::length(rbH.read().velocity * mass) > 5.0f;
}
return fractureCount == 0 && isAtMomentumThreshold;
}
void Fracturer::InitializeVoronoi(ecs::component_handle<physicsComponent> physicsComponent)
{
//using all colliders inside the physicsComponent, create a bounding box
}
void Fracturer::InvestigateColliderToMeshPairing(ecs::entity_handle ent, std::vector<FracturerColliderToMeshPairing> colliderToMeshPairings)
{
ecs::component_handle<mesh_filter> meshFilterHandle = ent.get_component_handle<mesh_filter>();
ecs::component_handle<physicsComponent> physicsComponentHandle = ent.get_component_handle<physicsComponent>();
if (!meshFilterHandle || !physicsComponentHandle) { return; }
}
void Fracturer::InstantiateColliderMeshPairingWithEntity(ecs::entity_handle ent,
std::vector<FracturerColliderToMeshPairing>& colliderToMeshPairings)
{
//the fracturer is setup in a way that fragments are all stored in a different entity
auto physicsCompHandle = ent.get_component_handle<physics::physicsComponent>();
std::shared_ptr<ConvexCollider> convexCollider = nullptr;
assert(physicsCompHandle);
auto physicsCollider = physicsCompHandle.read().colliders.at(0);
if (physicsCompHandle)
{
convexCollider = std::dynamic_pointer_cast<ConvexCollider>(physicsCollider);
}
auto meshSplitterHandle = ent.get_component_handle<MeshSplitter>();
//assert(meshSplitterHandle);
if (meshSplitterHandle && convexCollider)
{
FracturerColliderToMeshPairing colliderToMeshPairing(convexCollider, meshSplitterHandle);
colliderToMeshPairings.push_back(colliderToMeshPairing);
}
}
math::vec3 Fracturer::GetImpactPointFromManifold(physics_manifold& manifold)
{
math::vec3 impactPoint = math::vec3();
for (auto& contact : manifold.contacts)
{
impactPoint += contact.RefWorldContact;
impactPoint += contact.IncWorldContact;
}
float numberOfContacts = manifold.contacts.size() * 2;
float mult = (1.0f / numberOfContacts);
impactPoint *= mult;
return impactPoint;
}
void FracturerColliderToMeshPairing::GenerateSplittingParamsFromCollider(std::shared_ptr<ConvexCollider> instantiatedCollider, std::vector<physics::MeshSplitParams>& meshSplitParams)
{
int until = 5;//convex bug at 3
int count = 0;
for (auto face : instantiatedCollider->GetHalfEdgeFaces())
{
//if (count > until) { continue; }
MeshSplitParams splitParam(face->centroid, face->normal);
meshSplitParams.push_back(splitParam);
count++;
}
}
}