Fixed bug with collision tags not behaving as intended

Why the damn hell must react's system be so confusing??
This commit is contained in:
Diren D Bharwani 2023-02-03 01:37:45 +08:00
parent 931f6fe5e2
commit b6d91d5492
15 changed files with 499 additions and 92 deletions

View File

@ -1,16 +1,16 @@
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
11 12
12 13
13 14
14 15
15 16
0 1 3
1 2 65535
2 3 65534
3 4 65534
4 5 65534
5 6 65534
6 7 65534
7 8 65534
8 9 65534
9 10 65534
10 11 65534
11 12 65534
12 13 65534
13 14 65534
14 15 65534
15 16 65534

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 2, y: 0, z: 0}
Rotate: {x: 0, y: 0, z: 0}
Translate: {x: 2, y: 2, z: 0}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
RigidBody Component:
@ -58,8 +58,8 @@
NumberOfChildren: 0
Components:
Transform Component:
Translate: {x: 0, y: -3, z: 0}
Rotate: {x: 0, y: 0, z: 0}
Translate: {x: 0, y: 0, z: 0}
Rotate: {x: -0, y: 0, z: -0}
Scale: {x: 1, y: 1, z: 1}
IsActive: true
RigidBody Component:
@ -79,7 +79,7 @@
Collider Component:
Colliders:
- Is Trigger: false
Collision Tag: 1
Collision Tag: 3
Type: Sphere
Radius: 1
Friction: 0.400000006
@ -88,7 +88,7 @@
Position Offset: {x: 0, y: 0, z: 0}
Rotation Offset: {x: 0, y: 0, z: 0}
- Is Trigger: false
Collision Tag: 1
Collision Tag: 2
Type: Box
Half Extents: {x: 1, y: 1, z: 1}
Friction: 0.400000006

View File

@ -14,18 +14,36 @@
// Primary Header
#include "SHCollisionTagMatrix.h"
#include "Tools/Utilities/SHUtilities.h"
namespace SHADE
{
/*-----------------------------------------------------------------------------------*/
/* Static Data Member Definitions */
/*-----------------------------------------------------------------------------------*/
SHCollisionTag SHCollisionTagMatrix::collisionTags[SHCollisionTag::NUM_LAYERS];
bool SHCollisionTagMatrix::dirty = true;
SHCollisionTag SHCollisionTagMatrix::collisionTags[SHCollisionTag::NUM_LAYERS];
/*-----------------------------------------------------------------------------------*/
/* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/
bool SHCollisionTagMatrix::IsDirty() noexcept
{
// Check if any collision tag is dirty
for (auto& tag : collisionTags)
{
if (tag.IsDirty())
{
dirty = true;
break;
}
}
return dirty;
}
const std::string& SHCollisionTagMatrix::GetTagName(int tagIndex)
{
if (tagIndex < 0 || tagIndex > SHCollisionTag::NUM_LAYERS)
@ -91,6 +109,8 @@ namespace SHADE
if (collisionTag.GetName() != tagName)
continue;
dirty = true;
collisionTag = newTag;
return;
}
@ -105,6 +125,8 @@ namespace SHADE
if (collisionTag.GetName() != tagName)
continue;
dirty = true;
collisionTag.SetMask(mask);
return;
}
@ -125,6 +147,8 @@ namespace SHADE
if (tagIndex < 0 || tagIndex > SHCollisionTag::NUM_LAYERS)
throw std::invalid_argument("Index out of range!");
dirty = true;
collisionTags[tagIndex] = newTag;
}
@ -133,6 +157,8 @@ namespace SHADE
if (tagIndex < 0 || tagIndex > SHCollisionTag::NUM_LAYERS)
throw std::invalid_argument("Index out of range!");
dirty = true;
collisionTags[tagIndex].SetMask(mask);
}
@ -145,8 +171,9 @@ namespace SHADE
/**
* I HATE FILE IO
*
* Each line in the file should be "index<space>tag name".
* If the line fails to follow this format, use the default tag name (index + 1)
* Each line in the file should be "index<space>tag name<space>mask".
* If the line fails to follow this format, use the default tag name (index + 1) and default mask.
* If no mask was read, use a default mask.
*/
// Populate tag names with default
@ -187,18 +214,40 @@ namespace SHADE
{
SHLOG_ERROR
(
"Collision tag file line {} does not match the required format of 'index<space>tag name'. Default tag used for index {}"
"Collision tag file line {} does not match the required format of 'index<space>tag name<space>mask'. Default tag used for index {}"
, linesRead + 1
, tagIndex
)
// Use default
collisionTags[tagIndex].SetName(std::to_string(tagIndex + 1));
collisionTags[tagIndex].SetMask(SHUtilities::ConvertEnum(SHCollisionTag::Layer::ALL));
continue;
}
collisionTags[tagIndex].SetName(tagName);
// Next element is the mask value
std::string maskString;
ss >> maskString;
uint16_t mask = std::numeric_limits<uint16_t>::max();
if (maskString.empty())
{
SHLOG_ERROR
(
"Collision tag file line {} does not match the required format of 'index<space>tag name<space>mask'. Default mask used for index {}"
, linesRead + 1
, tagIndex
)
}
else
{
mask = static_cast<uint16_t>(std::stoi(maskString));
}
collisionTags[tagIndex].SetMask(mask);
ss.clear();
}
@ -215,9 +264,19 @@ namespace SHADE
return;
}
// Index Name Mask
for (int i = 0; i < SHCollisionTag::NUM_LAYERS; ++i)
collisionTagNamesFile << i << " " << collisionTags[i].GetName() << std::endl;
collisionTagNamesFile << i << " " << collisionTags[i].GetName() << " " << collisionTags[i].GetMask() << std::endl;
collisionTagNamesFile.close();
}
void SHCollisionTagMatrix::Clear() noexcept
{
dirty = false;
for (auto& tag : collisionTags)
tag.dirty = false;
}
} // namespace SHADE

View File

@ -29,6 +29,7 @@ namespace SHADE
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] static bool IsDirty () noexcept;
[[nodiscard]] static const std::string& GetTagName (int tagIndex);
[[nodiscard]] static int GetTagIndex (const std::string& tagName) noexcept;
@ -39,29 +40,36 @@ namespace SHADE
/* Setter Functions */
/*---------------------------------------------------------------------------------*/
static void SetTagName (const std::string& oldTagName, const std::string& newTagName) noexcept;
static void SetTag (const std::string& tagName, const SHCollisionTag& newTag) noexcept;
static void SetTag (const std::string& tagName, uint16_t mask) noexcept;
static void SetTagName (const std::string& oldTagName, const std::string& newTagName) noexcept;
static void SetTag (const std::string& tagName, const SHCollisionTag& newTag) noexcept;
static void SetTag (const std::string& tagName, uint16_t mask) noexcept;
// Unsafe Setters: Can throw exceptions
static void SetTagName (int tagIndex, const std::string& newTagName);
static void SetTag (int tagIndex, const SHCollisionTag& newTag);
static void SetTag (int tagIndex, uint16_t mask);
static void SetTagName (int tagIndex, const std::string& newTagName);
static void SetTag (int tagIndex, const SHCollisionTag& newTag);
static void SetTag (int tagIndex, uint16_t mask);
/*---------------------------------------------------------------------------------*/
/* Function Members */
/*---------------------------------------------------------------------------------*/
static void Init (const std::filesystem::path& tagNameFilePath) noexcept;
static void Exit (const std::filesystem::path& tagNameFilePath) noexcept;
static void Init (const std::filesystem::path& tagNameFilePath) noexcept;
static void Exit (const std::filesystem::path& tagNameFilePath) noexcept;
/**
* @brief
* Clears the dirty flag.
*/
static void Clear () noexcept;
private:
/*---------------------------------------------------------------------------------*/
/* Data Members */
/*---------------------------------------------------------------------------------*/
static bool dirty;
static SHCollisionTag collisionTags[SHCollisionTag::NUM_LAYERS];
};
}

View File

@ -23,15 +23,18 @@ namespace SHADE
/*-----------------------------------------------------------------------------------*/
SHCollisionTag::SHCollisionTag() noexcept
: mask { SHUtilities::ConvertEnum(Layer::ALL) }
: dirty { true }
, mask { SHUtilities::ConvertEnum(Layer::ALL) }
{}
SHCollisionTag::SHCollisionTag(uint16_t _mask) noexcept
: mask { _mask }
: dirty { true }
, mask { _mask }
{}
SHCollisionTag::SHCollisionTag(Layer layer) noexcept
: mask { SHUtilities::ConvertEnum(layer) }
: dirty { true }
, mask { SHUtilities::ConvertEnum(layer) }
{}
/*-----------------------------------------------------------------------------------*/
@ -57,6 +60,11 @@ namespace SHADE
/* Getter Function Definitions */
/*-----------------------------------------------------------------------------------*/
bool SHCollisionTag::IsDirty() const noexcept
{
return dirty;
}
uint16_t SHCollisionTag::GetMask() const noexcept
{
return mask;
@ -86,6 +94,7 @@ namespace SHADE
void SHCollisionTag::SetMask(uint16_t newMask) noexcept
{
dirty = true;
mask = newMask;
}
@ -96,6 +105,8 @@ namespace SHADE
void SHCollisionTag::SetLayerState(Layer layer, bool state) noexcept
{
dirty = true;
const auto VALUE = SHUtilities::ConvertEnum(layer);
state ? mask |= VALUE : mask &= ~(VALUE);
}
@ -105,6 +116,8 @@ namespace SHADE
if (layerIndex < 0 || layerIndex > NUM_LAYERS)
throw std::invalid_argument("Index out of range!");
dirty = true;
const auto VALUE = 1U << layerIndex;
state ? mask |= (VALUE) : mask &= ~(VALUE);
}

View File

@ -23,6 +23,13 @@ namespace SHADE
class SH_API SHCollisionTag
{
private:
/*---------------------------------------------------------------------------------*/
/* Friends */
/*---------------------------------------------------------------------------------*/
friend class SHCollisionTagMatrix;
public:
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
@ -82,6 +89,7 @@ namespace SHADE
/* Getter Functions */
/*---------------------------------------------------------------------------------*/
[[nodiscard]] bool IsDirty () const noexcept;
[[nodiscard]] uint16_t GetMask () const noexcept;
[[nodiscard]] const std::string& GetName () const noexcept;
[[nodiscard]] bool GetLayerState (Layer layer) const noexcept;
@ -101,6 +109,7 @@ namespace SHADE
/* Data Members */
/*---------------------------------------------------------------------------------*/
bool dirty;
uint16_t mask;
std::string name;
};

View File

@ -102,11 +102,7 @@ namespace SHADE
const SHTransform& PARENT_TRANSFORM = collider->GetTransform();
SetScale(PARENT_TRANSFORM.scale);
if (rp3dCollider)
{
const rp3d::Transform OFFSETS{ positionOffset, SHQuaternion::FromEuler(rotationOffset) };
rp3dCollider->setLocalToBodyTransform(OFFSETS);
}
SHCollisionShape::Update();
}

View File

@ -118,6 +118,9 @@ namespace SHADE
void SHCollisionShape::SetCollisionTag(SHCollisionTag* newCollisionTag) noexcept
{
collisionTag = newCollisionTag;
if (rp3dCollider)
rp3dCollider->setCollideWithMaskBits(collisionTag->GetMask());
}
void SHCollisionShape::SetFriction(float friction) noexcept
@ -184,6 +187,13 @@ namespace SHADE
/* Public Member Function Definitions */
/*-----------------------------------------------------------------------------------*/
void SHCollisionShape::UpdateCollisionTags() noexcept
{
if (collisionTag->IsDirty())
rp3dCollider->setCollideWithMaskBits(collisionTag->GetMask());
}
void SHCollisionShape::Update() noexcept
{
if (rp3dCollider)

View File

@ -123,6 +123,12 @@ namespace SHADE
/* Member Functions */
/*---------------------------------------------------------------------------------*/
/**
* @brief
* Re-sets the collision tags if it is dirty.
*/
void UpdateCollisionTags() noexcept;
/**
* @brief
* Computes the transform of the shape.

View File

@ -105,11 +105,7 @@ namespace SHADE
const float SPHERE_SCALE = std::fabs(SHMath::Max({ PARENT_TRANSFORM.scale.x, PARENT_TRANSFORM.scale.y, PARENT_TRANSFORM.scale.z }));
SetScale(SPHERE_SCALE);
if (rp3dCollider)
{
const rp3d::Transform OFFSETS{ positionOffset, SHQuaternion::FromEuler(rotationOffset) };
rp3dCollider->setLocalToBodyTransform(OFFSETS);
}
SHCollisionShape::Update();
}

View File

@ -15,6 +15,7 @@
// Project Headers
#include "Math/Transform/SHTransformComponent.h"
#include "Physics/Collision/CollisionTags/SHCollisionTagMatrix.h"
#include "Physics/Collision/Shapes/SHSphere.h"
#include "Physics/Collision/Shapes/SHBox.h"
#include "Physics/Interface/SHColliderComponent.h"
@ -385,6 +386,11 @@ namespace SHADE
}
default: break;
}
collisionShape->SetMaterial(collisionShape->GetMaterial());
const auto& COLLISION_TAG = collisionShape->GetCollisionTag();
collisionShape->SetCollisionTag(SHCollisionTagMatrix::GetTag(COLLISION_TAG.GetName()));
}
physicsObject->body->updateMassPropertiesFromColliders();

View File

@ -272,6 +272,12 @@ namespace SHADE
shape->Update();
}
void SHColliderComponent::UpdateCollisionTags() noexcept
{
for (auto& shape : shapes)
shape->UpdateCollisionTags();
}
} // namespace SHADE

View File

@ -158,6 +158,12 @@ namespace SHADE
*/
void Update () noexcept;
/**
* @brief
* Re-sets any dirty collision tags on collision shapes.
*/
void UpdateCollisionTags () noexcept;
private:
/*---------------------------------------------------------------------------------*/

View File

@ -36,6 +36,9 @@ namespace SHADE
{
auto* physicsSystem = reinterpret_cast<SHPhysicsSystem*>(GetSystem());
// Update colliders since collision tag has changed
const bool UPDATE_COLLISION_TAGS = SHCollisionTagMatrix::IsDirty();
// Get all physics objects & sync transforms
auto& physicsObjects = physicsSystem->objectManager.GetPhysicsObjects();
for (auto& [entityID, physicsObject] : physicsObjects)
@ -44,52 +47,61 @@ namespace SHADE
// Assume transform is always active
const bool UPDATE_TRANSFORM = TRANSFORM_COMPONENT && TRANSFORM_COMPONENT->HasChanged();
if (!UPDATE_TRANSFORM)
continue;
// We assume that all engine components and physics object components have been successfully linked
if (!physicsObject.body)
continue;
// Set body transform
const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition();
const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation();
auto* shadeBody = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(entityID);
auto* shadeCollider = SHComponentManager::GetComponent_s<SHColliderComponent>(entityID);
const rp3d::Transform NEW_TRANSFORM{ WORLD_POS, WORLD_ROT };
physicsObject.body->setTransform(NEW_TRANSFORM);
// Sync rigid body active states if one exists
if (auto* shadeBody = SHComponentManager::GetComponent_s<SHRigidBodyComponent>(entityID); shadeBody)
if (UPDATE_TRANSFORM)
{
const bool SHADE_BODY_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive<SHRigidBodyComponent>(entityID);
const bool RP3D_BODY_ACTIVE = physicsObject.body->isActive();
// Set body transform
const SHVec3& WORLD_POS = TRANSFORM_COMPONENT->GetWorldPosition();
const SHQuaternion& WORLD_ROT = TRANSFORM_COMPONENT->GetWorldOrientation();
if (SHADE_BODY_ACTIVE != RP3D_BODY_ACTIVE)
physicsObject.body->setIsActive(SHADE_BODY_ACTIVE);
const rp3d::Transform NEW_TRANSFORM{ WORLD_POS, WORLD_ROT };
physicsObject.body->setTransform(NEW_TRANSFORM);
shadeBody->position = WORLD_POS;
shadeBody->orientation = WORLD_ROT;
// Sync rigid body active states if one exists
if (shadeBody)
{
const bool SHADE_BODY_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive<SHRigidBodyComponent>(entityID);
const bool RP3D_BODY_ACTIVE = physicsObject.body->isActive();
if (SHADE_BODY_ACTIVE != RP3D_BODY_ACTIVE)
physicsObject.body->setIsActive(SHADE_BODY_ACTIVE);
shadeBody->position = WORLD_POS;
shadeBody->orientation = WORLD_ROT;
}
// Sync collider active states if one exists
if (shadeCollider)
{
const bool SHADE_COLLIDER_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive<SHColliderComponent>(entityID);
const bool RP3D_COLLIDERS_ACTIVE = shadeCollider->flags & SHColliderComponent::ACTIVE_FLAG;
// Modify the static body's active state
// The collision listener & raycaster will handle culling inactive colliders.
if (SHADE_COLLIDER_ACTIVE != RP3D_COLLIDERS_ACTIVE)
physicsObject.body->setIsActive(SHADE_COLLIDER_ACTIVE);
shadeCollider->transform.position = WORLD_POS;
shadeCollider->transform.orientation = WORLD_ROT;
shadeCollider->transform.scale = TRANSFORM_COMPONENT->GetWorldScale();
shadeCollider->Update();
}
}
// Sync collider active states if one exists
if (auto* shadeCollider = SHComponentManager::GetComponent_s<SHColliderComponent>(entityID); shadeCollider)
{
const bool SHADE_COLLIDER_ACTIVE = SHSceneManager::CheckNodeAndComponentsActive<SHColliderComponent>(entityID);
const bool RP3D_COLLIDERS_ACTIVE = shadeCollider->flags & SHColliderComponent::ACTIVE_FLAG;
// Modify the static body's active state
// The collision listener & raycaster will handle culling inactive colliders.
if (SHADE_COLLIDER_ACTIVE != RP3D_COLLIDERS_ACTIVE)
physicsObject.body->setIsActive(SHADE_COLLIDER_ACTIVE);
shadeCollider->transform.position = WORLD_POS;
shadeCollider->transform.orientation = WORLD_ROT;
shadeCollider->transform.scale = TRANSFORM_COMPONENT->GetWorldScale();
shadeCollider->Update();
}
if (UPDATE_COLLISION_TAGS && shadeCollider)
shadeCollider->UpdateCollisionTags();
}
// Clear collision tag dirty flags
SHCollisionTagMatrix::Clear();
}
} // namespace SHADE