Added a TriggerSystem, seems to work as intended.
This commit is contained in:
@@ -37,6 +37,7 @@ bool RayVsModel(const Ray& ray,
|
|||||||
|
|
||||||
//Return true if the boxes are intersecting.
|
//Return true if the boxes are intersecting.
|
||||||
bool AABBVsAABB(const AABB& a, const AABB& b);
|
bool AABBVsAABB(const AABB& a, const AABB& b);
|
||||||
|
bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon = 0.0001f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ struct TriggerLeave : Event
|
|||||||
EntityID Trigger;
|
EntityID Trigger;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Thrown when an entity is completely inside a trigger. */
|
/** Thrown once, when an entity is completely inside a trigger. */
|
||||||
struct TriggerEnter : Event
|
struct TriggerEnter : Event
|
||||||
{
|
{
|
||||||
/** The id of the entity that entered the trigger. */
|
/** The id of the entity that entered the trigger. */
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include "Core/EventBroker.h"
|
#include "Core/EventBroker.h"
|
||||||
#include "ETrigger.h"
|
#include "ETrigger.h"
|
||||||
|
|
||||||
|
class AABB;
|
||||||
|
|
||||||
class TriggerSystem : public System
|
class TriggerSystem : public System
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -18,7 +20,21 @@ public:
|
|||||||
virtual void Update(World* world, ComponentWrapper& collision, double dt) override;
|
virtual void Update(World* world, ComponentWrapper& collision, double dt) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<EntityID, std::unordered_set<EntityID>> m_EntitiesInTrigger;
|
std::unordered_map<EntityID, std::unordered_set<EntityID>> m_EntitiesTouchingTrigger;
|
||||||
|
std::unordered_map<EntityID, std::unordered_set<EntityID>> m_EntitiesCompletelyInTrigger;
|
||||||
|
|
||||||
|
bool getEntityBox(World* world, EntityID id, AABB& outBox);
|
||||||
|
//True if leave event was thrown.
|
||||||
|
bool throwLeaveIfWasInTrigger(std::unordered_set<EntityID>& triggerSet, EntityID pId, EntityID tId);
|
||||||
|
void attachAABBComponentFromModel(World* world, EntityID id);
|
||||||
|
template<typename Event>
|
||||||
|
void publish(EntityID pId, EntityID tId)
|
||||||
|
{
|
||||||
|
Event e;
|
||||||
|
e.Trigger = tId;
|
||||||
|
e.Entity = pId;
|
||||||
|
m_EventBroker->Publish(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "Core/EventBroker.h"
|
#include "Core/EventBroker.h"
|
||||||
#include "Core/EKeyDown.h"
|
#include "Core/EKeyDown.h"
|
||||||
#include "Core/EKeyUp.h"
|
#include "Core/EKeyUp.h"
|
||||||
|
#include "Collision/ETrigger.h"
|
||||||
|
|
||||||
struct KeyInput
|
struct KeyInput
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,9 @@ public:
|
|||||||
{
|
{
|
||||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &PlayerSystem::OnKeyDown);
|
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &PlayerSystem::OnKeyDown);
|
||||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &PlayerSystem::OnKeyUp);
|
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &PlayerSystem::OnKeyUp);
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_ETouch, &PlayerSystem::OnTouch);
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EEnter, &PlayerSystem::OnEnter);
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_ELeave, &PlayerSystem::OnLeave);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Update(World* world, ComponentWrapper& player, double dt) override;
|
virtual void Update(World* world, ComponentWrapper& player, double dt) override;
|
||||||
@@ -39,6 +43,12 @@ private:
|
|||||||
bool OnKeyDown(const Events::KeyDown &event);
|
bool OnKeyDown(const Events::KeyDown &event);
|
||||||
EventRelay<PlayerSystem, Events::KeyUp> m_EKeyUp;
|
EventRelay<PlayerSystem, Events::KeyUp> m_EKeyUp;
|
||||||
bool OnKeyUp(const Events::KeyUp &event);
|
bool OnKeyUp(const Events::KeyUp &event);
|
||||||
|
EventRelay<PlayerSystem, Events::TriggerEnter> m_EEnter;
|
||||||
|
bool OnEnter(const Events::TriggerEnter &event);
|
||||||
|
EventRelay<PlayerSystem, Events::TriggerTouch> m_ETouch;
|
||||||
|
bool PlayerSystem::OnTouch(const Events::TriggerTouch &event);
|
||||||
|
EventRelay<PlayerSystem, Events::TriggerLeave> m_ELeave;
|
||||||
|
bool PlayerSystem::OnLeave(const Events::TriggerLeave &event);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -14,11 +14,13 @@
|
|||||||
<Components>
|
<Components>
|
||||||
<c:Transform>
|
<c:Transform>
|
||||||
<Position X="-1.5"/>
|
<Position X="-1.5"/>
|
||||||
<Scale X="1" Y="1" Z="1"/>
|
<Scale X="2" Y="2" Z="2"/>
|
||||||
</c:Transform>
|
</c:Transform>
|
||||||
<c:Model>
|
<c:Model>
|
||||||
<Resource>Models/ScaleWidget.obj</Resource>
|
<Resource>Models/ScaleWidget.obj</Resource>
|
||||||
</c:Model>
|
</c:Model>
|
||||||
|
<c:Trigger>
|
||||||
|
</c:Trigger>
|
||||||
</Components>
|
</Components>
|
||||||
</Entity>
|
</Entity>
|
||||||
<Entity>
|
<Entity>
|
||||||
@@ -42,6 +44,8 @@
|
|||||||
<c:Model>
|
<c:Model>
|
||||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||||
</c:Model>
|
</c:Model>
|
||||||
|
<c:AABB>
|
||||||
|
</c:AABB>
|
||||||
</Components>
|
</Components>
|
||||||
</Entity>
|
</Entity>
|
||||||
<Entity>
|
<Entity>
|
||||||
|
|||||||
@@ -154,4 +154,18 @@ bool RayVsModel(const Ray& ray,
|
|||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon)
|
||||||
|
{
|
||||||
|
const glm::vec3& ma1 = first.MaxCorner();
|
||||||
|
const glm::vec3& ma2 = first.MaxCorner();
|
||||||
|
const glm::vec3& mi1 = second.MinCorner();
|
||||||
|
const glm::vec3& mi2 = second.MinCorner();
|
||||||
|
return (std::abs(ma1.x - ma2.x) < epsilon) &&
|
||||||
|
(std::abs(mi1.x - mi2.x) < epsilon) &&
|
||||||
|
(std::abs(ma1.z - ma2.z) < epsilon) &&
|
||||||
|
(std::abs(mi1.z - mi2.z) < epsilon) &&
|
||||||
|
(std::abs(ma1.y - ma2.y) < epsilon) &&
|
||||||
|
(std::abs(mi1.y - mi2.y) < epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,123 @@
|
|||||||
#include "Collision/TriggerSystem.h"
|
#include "Collision/TriggerSystem.h"
|
||||||
|
#include "Collision/Collision.h"
|
||||||
#include "Core/AABB.h"
|
#include "Core/AABB.h"
|
||||||
|
#include "Rendering/Model.h"
|
||||||
|
|
||||||
void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt)
|
void TriggerSystem::Update(World* world, ComponentWrapper& trigger, double dt)
|
||||||
{
|
{
|
||||||
|
//Currently only players can trigger things.
|
||||||
auto players = world->GetComponents("Player");
|
auto players = world->GetComponents("Player");
|
||||||
if (players == nullptr) {
|
if (players == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//WTODO: Assumes box exists.
|
EntityID tId = trigger.EntityID;
|
||||||
ComponentWrapper& cBox = world->GetComponent(trigger.EntityID, "AABB");
|
AABB triggerBox;
|
||||||
AABB aabb;
|
//The trigger *should* have a bounding box, or something, to test against so it can be triggered.
|
||||||
aabb.CreateFromCenter(cBox["BoxCenter"], cBox["BoxSize"]);
|
if (!getEntityBox(world, tId, triggerBox)) {
|
||||||
for (auto& c : *players) {
|
return;
|
||||||
|
}
|
||||||
Events::TriggerEnter e;
|
for (auto& pc : *players) {
|
||||||
e.Trigger = trigger.EntityID;
|
EntityID pId = pc.EntityID;
|
||||||
e.Entity = 41;
|
AABB playerBox;
|
||||||
m_EventBroker->Publish(e);
|
//The player can't trigger anything without an AABB.
|
||||||
|
if (!getEntityBox(world, pId, playerBox)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!Collision::AABBVsAABB(triggerBox, playerBox)) {
|
||||||
|
//Entity is not touching the trigger,
|
||||||
|
//Throw event if it was previously.
|
||||||
|
if (throwLeaveIfWasInTrigger(m_EntitiesTouchingTrigger[tId], pId, tId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//This only occurs if the entity was completely inside the trigger one frame,
|
||||||
|
//then completely outside the trigger, e.g. when dying and respawning.
|
||||||
|
throwLeaveIfWasInTrigger(m_EntitiesCompletelyInTrigger[tId], pId, tId);
|
||||||
|
} else {
|
||||||
|
//Entity is at least touching the trigger.
|
||||||
|
AABB completelyInsideBox;
|
||||||
|
completelyInsideBox.CreateFromCenter(triggerBox.Center(), triggerBox.Size() - playerBox.Size());
|
||||||
|
if (Collision::AABBVsAABB(completelyInsideBox, playerBox)) {
|
||||||
|
//Entity is completely inside the trigger.
|
||||||
|
//If it was only touching before, it is erased.
|
||||||
|
m_EntitiesTouchingTrigger[tId].erase(pId);
|
||||||
|
std::unordered_set<EntityID>& completeSet = m_EntitiesCompletelyInTrigger[tId];
|
||||||
|
if (completeSet.count(pId) == 0) {
|
||||||
|
//If it wasn't completely in the trigger, throw Enter and add to the set.
|
||||||
|
completeSet.insert(pId);
|
||||||
|
publish<Events::TriggerEnter>(pId, tId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Entity is only touching the trigger.
|
||||||
|
std::unordered_set<EntityID>& touchSet = m_EntitiesTouchingTrigger[tId];
|
||||||
|
std::unordered_set<EntityID>& completeSet = m_EntitiesCompletelyInTrigger[tId];
|
||||||
|
const auto& it = completeSet.find(pId);
|
||||||
|
//If it was completely inside before.
|
||||||
|
if (it != completeSet.end()) {
|
||||||
|
completeSet.erase(it);
|
||||||
|
touchSet.insert(pId);
|
||||||
|
//If it was completely outside before.
|
||||||
|
} else if (touchSet.count(pId) == 0) {
|
||||||
|
publish<Events::TriggerTouch>(pId, tId);
|
||||||
|
touchSet.insert(pId);
|
||||||
|
}
|
||||||
|
//Else, it was touching the trigger last frame too and nothing is done.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TriggerSystem::getEntityBox(World* world, EntityID id, AABB& outBox)
|
||||||
|
{
|
||||||
|
//TODO: Improve checking if component exists. Remove try
|
||||||
|
bool retry;
|
||||||
|
do {
|
||||||
|
retry = false;
|
||||||
|
try {
|
||||||
|
ComponentWrapper& cBox = world->GetComponent(id, "AABB");
|
||||||
|
outBox.CreateFromCenter(cBox["BoxCenter"], cBox["BoxSize"]);
|
||||||
|
} catch (std::out_of_range e) {
|
||||||
|
retry = true;
|
||||||
|
attachAABBComponentFromModel(world, id);
|
||||||
|
}
|
||||||
|
} while (retry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set<EntityID>& triggerSet, EntityID pId, EntityID tId)
|
||||||
|
{
|
||||||
|
const auto& it = triggerSet.find(pId);
|
||||||
|
if (it != triggerSet.end()) {
|
||||||
|
//If it was in the trigger, but not anymore, throw leaveEvent and erase from the set.
|
||||||
|
triggerSet.erase(it);
|
||||||
|
publish<Events::TriggerLeave>(pId, tId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerSystem::attachAABBComponentFromModel(World* world, EntityID id)
|
||||||
|
{
|
||||||
|
ComponentWrapper model = world->GetComponent(id, "Model");
|
||||||
|
ComponentWrapper transform = world->GetComponent(id, "Transform");
|
||||||
|
ComponentWrapper collision = world->AttachComponent(id, "AABB");
|
||||||
|
Model* modelRes = ResourceManager::Load<Model>(model["Resource"]);
|
||||||
|
|
||||||
|
glm::mat4 modelMatrix = modelRes->m_Matrix *
|
||||||
|
glm::translate(glm::mat4(), (glm::vec3)transform["Position"]) *
|
||||||
|
glm::toMat4((glm::quat)transform["Orientation"]) *
|
||||||
|
glm::scale((glm::vec3)transform["Scale"]);
|
||||||
|
|
||||||
|
glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY);
|
||||||
|
glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY);
|
||||||
|
for (const auto& v : modelRes->m_Vertices) {
|
||||||
|
const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1);
|
||||||
|
maxi.x = std::max(wPos.x, maxi.x);
|
||||||
|
maxi.y = std::max(wPos.y, maxi.y);
|
||||||
|
maxi.z = std::max(wPos.z, maxi.z);
|
||||||
|
mini.x = std::min(wPos.x, mini.x);
|
||||||
|
mini.y = std::min(wPos.y, mini.y);
|
||||||
|
mini.z = std::min(wPos.z, mini.z);
|
||||||
|
}
|
||||||
|
collision["BoxCenter"] = 0.5f * (maxi + mini);
|
||||||
|
collision["BoxSize"] = maxi - mini;
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,16 +21,6 @@ bool isFirstLower(const ChildInfo& first, const ChildInfo& second)
|
|||||||
return first.Distance < second.Distance;
|
return first.Distance < second.Distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSameBoxProbably(const AABB& first, const AABB& second)
|
|
||||||
{
|
|
||||||
const float EPS = 0.0001f;
|
|
||||||
const auto& ma = first.MaxCorner();
|
|
||||||
const auto& mi = first.MinCorner();
|
|
||||||
return (std::abs(ma.x - mi.x) < EPS) &&
|
|
||||||
(std::abs(ma.z - mi.z) < EPS) &&
|
|
||||||
(std::abs(ma.y - mi.y) < EPS);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OctTree::OctTree()
|
OctTree::OctTree()
|
||||||
@@ -228,7 +218,7 @@ bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersect
|
|||||||
for (int i : m_DynamicObjIndices) {
|
for (int i : m_DynamicObjIndices) {
|
||||||
if (!m_DynamicObjectsRef[i].Checked) {
|
if (!m_DynamicObjectsRef[i].Checked) {
|
||||||
const AABB& objBox = m_DynamicObjectsRef[i].Box;
|
const AABB& objBox = m_DynamicObjectsRef[i].Box;
|
||||||
if (!isSameBoxProbably(boxToTest, objBox) &&
|
if (!Collision::IsSameBoxProbably(boxToTest, objBox) &&
|
||||||
Collision::AABBVsAABB(boxToTest, objBox)) {
|
Collision::AABBVsAABB(boxToTest, objBox)) {
|
||||||
outBoxIntersected = objBox;
|
outBoxIntersected = objBox;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -56,3 +56,21 @@ bool PlayerSystem::OnKeyUp(const Events::KeyUp & event)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PlayerSystem::OnTouch(const Events::TriggerTouch &event)
|
||||||
|
{
|
||||||
|
LOG_INFO("Player %i touched widget (entity %i).", event.Entity, event.Trigger);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerSystem::OnEnter(const Events::TriggerEnter &event)
|
||||||
|
{
|
||||||
|
LOG_INFO("Player %i entered widget (entity %i).", event.Entity, event.Trigger);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerSystem::OnLeave(const Events::TriggerLeave &event)
|
||||||
|
{
|
||||||
|
LOG_INFO("Player %i left widget (entity %i).", event.Entity, event.Trigger);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user