Compare commits
201 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef9ce7932a | |||
| beb0967c3b | |||
| 0e0a361a38 | |||
| 6ebc4fc784 | |||
| 7fb71f17b2 | |||
| e52c0c9633 | |||
| 578ca13f79 | |||
| 4048ce6bd4 | |||
| c4642cc530 | |||
| 32ebc061b4 | |||
| a1ed79dfbf | |||
| 638c236ceb | |||
| f67f22c82d | |||
| f3ff433bf7 | |||
| 85ff86a2fe | |||
| fa8775a72e | |||
| 49884d7e60 | |||
| 34cbbb3eb1 | |||
| 0ae6c42df8 | |||
| 3678cceb1c | |||
| de8461e257 | |||
| c133223bac | |||
| 2f6d4fcd0b | |||
| a550b6077e | |||
| e0cc25dbac | |||
| 00bc55370d | |||
| 330f08b926 | |||
| a0255f1b31 | |||
| a858dab8b6 | |||
| b1ae96f737 | |||
| 7dcd75fccc | |||
| 1e5e24a036 | |||
| 6d649e4d35 | |||
| 5c1a93021b | |||
| ad43cc44c3 | |||
| 002e1d8c5a | |||
| 30db0b10fd | |||
| 97f8be1915 | |||
| 0eb51a0fea | |||
| 13f7fb2561 | |||
| 0b714e593f | |||
| 8beddca89a | |||
| 5e0fe975f3 | |||
| db5b6ad519 | |||
| a26c8f2a24 | |||
| 076030a252 | |||
| 0f59b863ee | |||
| d0927373a3 | |||
| 46ae2d39af | |||
| 000adb198f | |||
| 51a2da9cc0 | |||
| 0f0a1ceca7 | |||
| 50822d09fc | |||
| 3e2ffdb85b | |||
| 5160a45b44 | |||
| 03d5f0dd67 | |||
| 4bb4e29515 | |||
| a6a08f48bb | |||
| ba831f8a3c | |||
| 4c29e071af | |||
| 2cb5441b1c | |||
| 97a0d97b0b | |||
| a23d69af9a | |||
| 425f3ca3dd | |||
| 6d1cbd928e | |||
| 6da5fd1263 | |||
| 6eebce05af | |||
| 58a9d38e26 | |||
| 50ccf96268 | |||
| cf0ff9bfe5 | |||
| d10df6ae09 | |||
| 99b76d7ae1 | |||
| a2d55feed1 | |||
| e74b2b6874 | |||
| 3f602c9d63 | |||
| 082b3fd3df | |||
| 04a01104b5 | |||
| 9b348a7d97 | |||
| 889e0bb3d3 | |||
| 1669649186 | |||
| ed82a0939a | |||
| e2417be8c7 | |||
| 472a8db81a | |||
| e940c1d604 | |||
| 478c692315 | |||
| 170610bcb1 | |||
| 454ed0032b | |||
| 1910ab705b | |||
| 0a8a9e5099 | |||
| f9508657f5 | |||
| b698a0d36f | |||
| 1229a99af4 | |||
| 8e431c8edd | |||
| 948f6d5de7 | |||
| ae7d4b9097 | |||
| e6fa9df3a5 | |||
| 59095c2ce5 | |||
| 3f77da4a07 | |||
| 7f8e78c8cd | |||
| bac0a1de26 | |||
| bf82bbb5d5 | |||
| e52052b431 | |||
| 8d9ff2b6dc | |||
| 63db236dfa | |||
| 5fce7490a9 | |||
| 8abef7024d | |||
| bf08ffe392 | |||
| 5f5cab1171 | |||
| 8d87841691 | |||
| 11e37ca898 | |||
| 55aad493ff | |||
| 9a7d77f29d | |||
| 36ae451ea6 | |||
| b3bb0e543f | |||
| 81034420a9 | |||
| 15442c8c75 | |||
| 350038057d | |||
| 23c3f0709b | |||
| 01d7388f91 | |||
| 1ceea91f4a | |||
| 4195baa5dc | |||
| 37f9ab2322 | |||
| d8b1f4e514 | |||
| 6a3335315a | |||
| c651face4c | |||
| 641a43b2ec | |||
| 58952725e9 | |||
| de044dabd3 | |||
| ce7b26ccac | |||
| 0d8f7acb7e | |||
| 99543a300d | |||
| f377d9ba70 | |||
| d22e59f28f | |||
| 7804d42fea | |||
| 9a8f5293af | |||
| e535f629bc | |||
| c60e968836 | |||
| bc8a922b77 | |||
| 663aca2641 | |||
| 4de613814b | |||
| e580540143 | |||
| 0807a7d0ae | |||
| ba150a264c | |||
| b650eb95df | |||
| 8949db5456 | |||
| 14bcb7ea0d | |||
| 519178dfba | |||
| 0e6353dd79 | |||
| ed5cd392ab | |||
| bad42fae1c | |||
| 247ff09743 | |||
| 2a35c3055e | |||
| ce41421dbb | |||
| 1d0810db10 | |||
| c85bb2eef7 | |||
| 75402b0f93 | |||
| f83e217638 | |||
| 58db7b755a | |||
| a8c0a1f294 | |||
| 9f54a08536 | |||
| 24dada149f | |||
| a7fb77d998 | |||
| 32d2aa6a93 | |||
| d1880754f5 | |||
| 27b44d2511 | |||
| 94adaf8ae3 | |||
| 6dc5112c26 | |||
| e92119c99e | |||
| c48c9e1f86 | |||
| 7ac2aaef15 | |||
| 06f56d13d1 | |||
| 8f55e90d2d | |||
| b5a010a517 | |||
| b87e543420 | |||
| fa3816e55d | |||
| ee41721a75 | |||
| 648ba502bb | |||
| 2c993e82d6 | |||
| cbbb7deaea | |||
| de5bdef58c | |||
| 000ad6358f | |||
| 04a8eeebe8 | |||
| d8d63f804a | |||
| 955c9ce534 | |||
| e8d23f3b3f | |||
| 89aa8c44b0 | |||
| f0e407bec2 | |||
| 8e21b1fe35 | |||
| 17133b316a | |||
| 28a290600b | |||
| af5b3a9302 | |||
| abb61d2cd6 | |||
| 2f13f5da7a | |||
| 464650c370 | |||
| af294f78b7 | |||
| c840183467 | |||
| 1f99d46a8d | |||
| fd99562c7a | |||
| c698149bdd | |||
| fa0ace8c68 | |||
| 1d19c6901e |
@@ -0,0 +1,59 @@
|
||||
#ifndef Collision_h__
|
||||
#define Collision_h__
|
||||
|
||||
//NOTE: Collision.h needs to be #included before <GLFW/glfw3.h>,
|
||||
//because Collision #includes "RawModel.h", which has "Texture.h", which has "OpenGL.h" which must be #included first
|
||||
//or you will get "fatal error C1189: #error: gl.h included before glew.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Core/Ray.h"
|
||||
#include "Core/AABB.h"
|
||||
#include "Engine/Rendering/RawModel.h"
|
||||
#include "Core/Entity.h"
|
||||
|
||||
class World;
|
||||
struct ComponentWrapper;
|
||||
|
||||
namespace Collision
|
||||
{
|
||||
//Return true if the ray hits the box.
|
||||
bool RayAABBIntr(const Ray& ray, const AABB& box);
|
||||
bool RayVsAABB(const Ray& ray, const AABB& box);
|
||||
//Return true if the ray hits the box, also outputs distance from ray origin to intersection point in [outDistance].
|
||||
bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance);
|
||||
|
||||
//Return true if the ray hits any of the triangles in the model. Stops checking when a hit is detected.
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices);
|
||||
//Return true if the ray hits any of the triangles in the model.
|
||||
//Also returns the position of the intersection point. Will loop through all the whole model indices.
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
glm::vec3& outHitPosition);
|
||||
//Return true if the ray hits any of the triangles in the model.
|
||||
//Also returns the distance from the ray origin to the closest
|
||||
//intersection point, and the barycentric u,v-coordinates. Will loop through all the whole model indices.
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
float& outDistance,
|
||||
float& outUCoord,
|
||||
float& outVCoord);
|
||||
|
||||
//Return true if the boxes are intersecting.
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b);
|
||||
//Return true if the boxes are intersecting.
|
||||
//Also outputs the minimum translation that box [a] would need in order to resolve collision.
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation);
|
||||
bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon = 0.0001f);
|
||||
|
||||
//Returns true if the entity has a boundingbox. Outputs the aabb in [outBox].
|
||||
bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFromModel = false);
|
||||
bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef CollisionSystem_h__
|
||||
#define CollisionSystem_h__
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/common.hpp>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/EKeyUp.h"
|
||||
|
||||
class CollisionSystem : public PureSystem
|
||||
{
|
||||
public:
|
||||
CollisionSystem(EventBroker* eventBroker)
|
||||
: PureSystem(eventBroker, "AABB")
|
||||
, zPress(false)
|
||||
{
|
||||
//TODO: Debug stuff, remove later.
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &CollisionSystem::OnKeyUp);
|
||||
}
|
||||
|
||||
virtual void UpdateComponent(World* world, ComponentWrapper& cAABB, double dt) override;
|
||||
|
||||
private:
|
||||
bool zPress;
|
||||
EventRelay<CollisionSystem, Events::KeyUp> m_EKeyUp;
|
||||
bool OnKeyUp(const Events::KeyUp &event);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef Events_TriggerEnter_h__
|
||||
#define Events_TriggerEnter_h__
|
||||
|
||||
#include "../Core/EventBroker.h"
|
||||
#include "../Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
|
||||
/** Thrown once, when an entity is only touching a trigger. */
|
||||
struct TriggerTouch : Event
|
||||
{
|
||||
/** The id of the entity that touches the trigger. */
|
||||
EntityID Entity;
|
||||
/** The id of the trigger entity. */
|
||||
EntityID Trigger;
|
||||
};
|
||||
|
||||
/** Thrown once, when an entity has completely left a trigger. */
|
||||
struct TriggerLeave : Event
|
||||
{
|
||||
/** The id of the entity that left the trigger. */
|
||||
EntityID Entity;
|
||||
/** The id of the trigger entity. */
|
||||
EntityID Trigger;
|
||||
};
|
||||
|
||||
/** Thrown once, when an entity is completely contained inside a trigger. */
|
||||
struct TriggerEnter : Event
|
||||
{
|
||||
/** The id of the entity that entered the trigger. */
|
||||
EntityID Entity;
|
||||
/** The id of the trigger entity. */
|
||||
EntityID Trigger;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef TriggerSystem_h__
|
||||
#define TriggerSystem_h__
|
||||
|
||||
#include <glm/common.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "Core/System.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "ETrigger.h"
|
||||
|
||||
class AABB;
|
||||
|
||||
class TriggerSystem : public PureSystem
|
||||
{
|
||||
public:
|
||||
TriggerSystem(EventBroker* eventBroker)
|
||||
: PureSystem(eventBroker, "Trigger")
|
||||
{}
|
||||
|
||||
virtual void UpdateComponent(World* world, ComponentWrapper& collision, double dt) override;
|
||||
|
||||
private:
|
||||
std::unordered_map<EntityID, std::unordered_set<EntityID>> m_EntitiesTouchingTrigger;
|
||||
std::unordered_map<EntityID, std::unordered_set<EntityID>> m_EntitiesCompletelyInTrigger;
|
||||
|
||||
//True if leave event was thrown.
|
||||
bool throwLeaveIfWasInTrigger(std::unordered_set<EntityID>& triggerSet, EntityID pId, EntityID tId);
|
||||
template<typename Event>
|
||||
void publish(EntityID pId, EntityID tId)
|
||||
{
|
||||
Event e;
|
||||
e.Trigger = tId;
|
||||
e.Entity = pId;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,4 +4,5 @@
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Core/Util/Logging.h"
|
||||
#include "Core/Util/Logging.h"
|
||||
#include "Core/Util/IfDebug.h"
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef AABB_h__
|
||||
#define AABB_h__
|
||||
|
||||
#include "../GLM.h"
|
||||
|
||||
class AABB
|
||||
{
|
||||
public:
|
||||
AABB() = default;
|
||||
//No checks are made. Values in minPos must be less than values in maxPos, i.e. min.x < max.x, etc.
|
||||
AABB(const glm::vec3& minPos, const glm::vec3& maxPos);
|
||||
AABB(const glm::vec4& minPos, const glm::vec4& maxPos);
|
||||
//No checks are made. Size must consist of non-negative numbers.
|
||||
virtual void CreateFromCenter(const glm::vec3& center, const glm::vec3& size);
|
||||
virtual ~AABB();
|
||||
|
||||
const glm::vec3& MinCorner() const { return m_MinCorner; }
|
||||
const glm::vec3& MaxCorner() const { return m_MaxCorner; }
|
||||
const glm::vec3& Center() const { return m_Center; }
|
||||
const glm::vec3 Size() const { return 2.0f * m_HalfSize; }
|
||||
const glm::vec3& HalfSize() const { return m_HalfSize; }
|
||||
private:
|
||||
glm::vec3 m_MinCorner;
|
||||
glm::vec3 m_MaxCorner;
|
||||
glm::vec3 m_Center;
|
||||
glm::vec3 m_HalfSize;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
~ComponentPoolForwardIterator() = default;
|
||||
ComponentPoolForwardIterator& operator=(const ComponentPoolForwardIterator& other) = default;
|
||||
ComponentPoolForwardIterator& operator++();
|
||||
ComponentPoolForwardIterator& operator++(int);
|
||||
ComponentPoolForwardIterator operator++(int);
|
||||
bool operator!=(const ComponentPoolForwardIterator& other) const;
|
||||
bool operator==(const ComponentPoolForwardIterator& other) const;
|
||||
ComponentWrapper operator*() const;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef EPlayerDamage_h__
|
||||
#define EPlayerDamage_h__
|
||||
|
||||
#include "EventBroker.h"
|
||||
#include "../Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
|
||||
struct PlayerDamage : Event
|
||||
{
|
||||
double DamageAmount;
|
||||
EntityID PlayerDamagedID;
|
||||
//optional TypeOfDamage
|
||||
std::string TypeOfDamage;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef EPlayerDeath_h__
|
||||
#define EPlayerDeath_h__
|
||||
|
||||
#include "EventBroker.h"
|
||||
#include "../Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
|
||||
struct PlayerDeath : Event
|
||||
{
|
||||
//KilledBy,KilledByWhat is optional for now. It might be used later in the playerlog-system
|
||||
EntityID KilledBy;
|
||||
EntityID PlayerID;
|
||||
std::string KilledByWhat;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef EPlayerHealthPickup_h__
|
||||
#define EPlayerHealthPickup_h__
|
||||
|
||||
#include "EventBroker.h"
|
||||
#include "../Core/Entity.h"
|
||||
|
||||
namespace Events
|
||||
{
|
||||
|
||||
struct PlayerHealthPickup : Event
|
||||
{
|
||||
double HealthAmount;
|
||||
EntityID PlayerHealedID;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -13,6 +13,8 @@
|
||||
relay = decltype(relay)(std::bind(handler, this, std::placeholders::_1)); \
|
||||
m_EventBroker->Subscribe(relay);
|
||||
|
||||
typedef unsigned int EventID;
|
||||
|
||||
class EventBroker;
|
||||
|
||||
class BaseEventRelay
|
||||
@@ -31,6 +33,7 @@ public:
|
||||
virtual bool Receive(const std::shared_ptr<Event> event) = 0;
|
||||
|
||||
protected:
|
||||
EventID m_EventID;
|
||||
std::string m_ContextTypeName;
|
||||
std::string m_EventTypeName;
|
||||
EventBroker* m_Broker;
|
||||
@@ -95,6 +98,7 @@ public:
|
||||
|
||||
private:
|
||||
bool m_IsProcessing = false;
|
||||
EventID m_NextEventID = 0;
|
||||
|
||||
typedef std::string ContextTypeName_t; // typeid(ContextType).name()
|
||||
typedef std::string EventTypeName_t; // typeid(EventType).name()
|
||||
@@ -103,14 +107,14 @@ private:
|
||||
typedef std::unordered_map<ContextTypeName_t, EventRelays_t> ContextRelays_t;
|
||||
ContextRelays_t m_ContextRelays;
|
||||
std::vector<BaseEventRelay*> m_RelaysToSubscribe;
|
||||
std::vector<BaseEventRelay*> m_RelaysToUnsubscribe;
|
||||
std::vector<std::tuple<EventID, ContextTypeName_t, EventTypeName_t>> m_RelaysToUnsubscribe;
|
||||
|
||||
typedef std::list<std::pair<EventTypeName_t, std::shared_ptr<Event>>> EventQueue_t;
|
||||
std::shared_ptr<EventQueue_t> m_EventQueueRead;
|
||||
std::shared_ptr<EventQueue_t> m_EventQueueWrite;
|
||||
|
||||
void subscribeImmediate(BaseEventRelay& relay);
|
||||
void unsubscribeImmediate(BaseEventRelay& relay);
|
||||
void unsubscribeImmediate(std::tuple<EventID, ContextTypeName_t, EventTypeName_t> identifier);
|
||||
};
|
||||
|
||||
template <typename EventType>
|
||||
|
||||
@@ -253,7 +253,7 @@ public:
|
||||
}
|
||||
|
||||
//Postfix increment i.e. iter++. Prefer pre-increment (++iter) for efficiency.
|
||||
MemoryPoolForwardIterator& operator++(int)
|
||||
MemoryPoolForwardIterator operator++(int)
|
||||
{
|
||||
MemoryPoolForwardIterator<T> copyIter(*this);
|
||||
operator++();
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifndef OctTree_h__
|
||||
#define OctTree_h__
|
||||
|
||||
#include "Core/AABB.h"
|
||||
|
||||
class Ray;
|
||||
|
||||
class OctTree
|
||||
{
|
||||
public:
|
||||
struct Output
|
||||
{
|
||||
float CollideDistance;
|
||||
};
|
||||
|
||||
OctTree();
|
||||
~OctTree();
|
||||
//For the root OctTree, [octTreeBounds] should be a box containing the entire level.
|
||||
OctTree(const AABB& octTreeBounds, int subDivisions);
|
||||
|
||||
//We cannot copy the OctTree as of now, because of the recursive dynamic allocation.
|
||||
//Define these if the OctTree suddenly needs to be copied, think of the children OctChild* ptrs.
|
||||
OctTree(const OctTree& other) = delete;
|
||||
OctTree(const OctTree&& other) = delete;
|
||||
OctTree& operator= (const OctTree& other) = delete;
|
||||
//Add a dynamic object (one that moves around) into the tree.
|
||||
void AddDynamicObject(const AABB& box);
|
||||
//Add a static object (that does not move) into the tree.
|
||||
void AddStaticObject(const AABB& box);
|
||||
//Get the boxes that are in the same area as the input [box], the boxes are put in [outBoxes].
|
||||
void BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes);
|
||||
//Empty the tree of all objects, static and dynamic.
|
||||
void ClearObjects();
|
||||
//Empty the tree of all dynamic objects. Static objects remain in the tree.
|
||||
void ClearDynamicObjects();
|
||||
|
||||
//Returns true if the ray collides with something in the tree. Result is written to [data].
|
||||
bool RayCollides(const Ray& ray, Output& data);
|
||||
//Returns true if the box collides with something in the tree.
|
||||
//On collision with a box, that box is written to [outBoxIntersected].
|
||||
//Note: More efficient than calling BoxesInSameRegion from outside and testing there.
|
||||
bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected);
|
||||
|
||||
private:
|
||||
struct OctChild; //Fwd declaration;
|
||||
struct ContainedObject
|
||||
{
|
||||
ContainedObject()
|
||||
: Box(AABB())
|
||||
, Checked(false)
|
||||
{}
|
||||
ContainedObject(AABB box)
|
||||
: Box(box)
|
||||
, Checked(false)
|
||||
{}
|
||||
AABB Box;
|
||||
bool Checked;
|
||||
};
|
||||
OctChild* m_Root;
|
||||
std::vector<ContainedObject> m_StaticObjects;
|
||||
std::vector<ContainedObject> m_DynamicObjects;
|
||||
|
||||
bool m_UpdatedOnce;
|
||||
unsigned int m_BoxID;
|
||||
glm::vec3 m_PrevPos;
|
||||
glm::quat m_PrevOri;
|
||||
|
||||
void falsifyObjectChecks();
|
||||
|
||||
struct OctChild
|
||||
{
|
||||
~OctChild();
|
||||
OctChild(const AABB& octTreeBounds,
|
||||
int subDivisions,
|
||||
std::vector<OctTree::ContainedObject>& staticObjects,
|
||||
std::vector<OctTree::ContainedObject>& dynamicObjects);
|
||||
OctChild(const OctChild& other) = delete;
|
||||
OctChild(const OctChild&& other) = delete;
|
||||
OctChild& operator= (const OctChild& other) = delete;
|
||||
void AddDynamicObject(const AABB& box);
|
||||
void AddStaticObject(const AABB& box);
|
||||
void BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes) const;
|
||||
void ClearObjects();
|
||||
void ClearDynamicObjects();
|
||||
bool RayCollides(const Ray& ray, Output& data) const;
|
||||
bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const;
|
||||
|
||||
OctChild* m_Children[8];
|
||||
//Indices into the lists in OctTree.
|
||||
std::vector<int> m_StaticObjIndices;
|
||||
std::vector<int> m_DynamicObjIndices;
|
||||
AABB m_Box;
|
||||
//Reference to the lists in OctTree.
|
||||
std::vector<OctTree::ContainedObject>& m_StaticObjectsRef;
|
||||
std::vector<OctTree::ContainedObject>& m_DynamicObjectsRef;
|
||||
|
||||
inline bool hasChildren() const;
|
||||
int childIndexContainingPoint(const glm::vec3& point) const;
|
||||
std::vector<int> childIndicesContainingBox(const AABB& box) const;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef Ray_h__
|
||||
#define Ray_h__
|
||||
|
||||
#include "../GLM.h"
|
||||
#include "Common.h"
|
||||
|
||||
class Ray
|
||||
{
|
||||
public:
|
||||
Ray(const glm::vec3& origin, const glm::vec3& dir)
|
||||
: m_Origin(origin)
|
||||
, m_Direction(glm::normalize(dir))
|
||||
{
|
||||
DEBUG_IF(true) {
|
||||
if (glm::any(glm::isnan(m_Direction))) {
|
||||
LOG_WARNING("Ray Direction was set to the zero-vector, expect unknown side effects and/or crashes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
const glm::vec3& Origin() const { return m_Origin; }
|
||||
const glm::vec3& Direction() const { return m_Direction; }
|
||||
//Sets the ray origin at parameter.
|
||||
void SetOrigin(const glm::vec3& origin) { m_Origin = origin; }
|
||||
//Normalizes the parameter and sets direction to it.
|
||||
void SetDirection(const glm::vec3& direction) { m_Direction = glm::normalize(direction); }
|
||||
private:
|
||||
glm::vec3 m_Origin;
|
||||
glm::vec3 m_Direction;
|
||||
};
|
||||
|
||||
#endif // Ray_h__
|
||||
@@ -7,10 +7,13 @@
|
||||
|
||||
class System
|
||||
{
|
||||
friend class SystemPipeline;
|
||||
|
||||
protected:
|
||||
System(EventBroker* eventBroker)
|
||||
: m_EventBroker(eventBroker)
|
||||
{ }
|
||||
virtual ~System() = default;
|
||||
|
||||
EventBroker* m_EventBroker;
|
||||
};
|
||||
@@ -24,6 +27,7 @@ protected:
|
||||
: System(eventBroker)
|
||||
, m_ComponentType(componentType)
|
||||
{ }
|
||||
virtual ~PureSystem() = default;
|
||||
|
||||
const std::string m_ComponentType;
|
||||
|
||||
@@ -38,6 +42,7 @@ protected:
|
||||
ImpureSystem(EventBroker* eventBroker)
|
||||
: System(eventBroker)
|
||||
{ }
|
||||
virtual ~ImpureSystem() = default;
|
||||
|
||||
virtual void Update(World* world, double dt) = 0;
|
||||
};
|
||||
|
||||
@@ -14,23 +14,28 @@ public:
|
||||
{ }
|
||||
~SystemPipeline()
|
||||
{
|
||||
for (auto& pair : m_PureSystems) {
|
||||
for (auto& system : pair.second) {
|
||||
delete system;
|
||||
for (UnorderedSystems& group : m_OrderedSystemGroups) {
|
||||
for (auto& pair : group.Systems) {
|
||||
delete pair.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename... Arguments>
|
||||
void AddSystem(Arguments... args)
|
||||
//All systems with orderlevel 0 will be updated first, then 1, 2, etc.
|
||||
void AddSystem(int updateOrderLevel, Arguments... args)
|
||||
{
|
||||
if (updateOrderLevel + 1 > m_OrderedSystemGroups.size()) {
|
||||
m_OrderedSystemGroups.resize(updateOrderLevel + 1);
|
||||
}
|
||||
UnorderedSystems& group = m_OrderedSystemGroups[updateOrderLevel];
|
||||
System* system = new T(m_EventBroker, args...);
|
||||
m_Systems[typeid(T).name()] = system;
|
||||
group.Systems[typeid(T).name()] = system;
|
||||
|
||||
if (std::is_base_of<PureSystem, T>::value) {
|
||||
PureSystem* pureSystem = static_cast<PureSystem*>(system);
|
||||
if (!pureSystem->m_ComponentType.empty()) {
|
||||
m_PureSystems[pureSystem->m_ComponentType].push_back(pureSystem);
|
||||
group.PureSystems[pureSystem->m_ComponentType].push_back(pureSystem);
|
||||
} else {
|
||||
LOG_ERROR("Failed to add pure system \"%s\": Missing component type!", typeid(T).name());
|
||||
}
|
||||
@@ -38,41 +43,47 @@ public:
|
||||
|
||||
if (std::is_base_of<ImpureSystem, T>::value) {
|
||||
ImpureSystem* impureSystem = static_cast<ImpureSystem*>(system);
|
||||
m_ImpureSystems.push_back(impureSystem);
|
||||
group.ImpureSystems.push_back(impureSystem);
|
||||
}
|
||||
}
|
||||
|
||||
void Update(World* world, double dt)
|
||||
{
|
||||
// Process events
|
||||
for (auto& pair : m_Systems) {
|
||||
m_EventBroker->Process(pair.first);
|
||||
}
|
||||
|
||||
// Update
|
||||
for (auto& pair : m_PureSystems) {
|
||||
const std::string& componentName = pair.first;
|
||||
auto& systems = pair.second;
|
||||
const ComponentPool* pool = world->GetComponents(componentName);
|
||||
if (pool == nullptr) {
|
||||
continue;
|
||||
for (UnorderedSystems& group : m_OrderedSystemGroups) {
|
||||
// Process events
|
||||
for (auto& pair : group.Systems) {
|
||||
m_EventBroker->Process(pair.first);
|
||||
}
|
||||
for (auto& component : *pool) {
|
||||
for (auto& system : systems) {
|
||||
system->UpdateComponent(world, component, dt);
|
||||
|
||||
// Update
|
||||
for (auto& pair : group.PureSystems) {
|
||||
const std::string& componentName = pair.first;
|
||||
auto& systems = pair.second;
|
||||
const ComponentPool* pool = world->GetComponents(componentName);
|
||||
if (pool == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (auto& component : *pool) {
|
||||
for (auto& system : systems) {
|
||||
system->UpdateComponent(world, component, dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& system : m_ImpureSystems) {
|
||||
system->Update(world, dt);
|
||||
for (auto& system : group.ImpureSystems) {
|
||||
system->Update(world, dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EventBroker* m_EventBroker;
|
||||
std::map<std::string, System*> m_Systems;
|
||||
std::map<std::string, std::vector<PureSystem*>> m_PureSystems;
|
||||
std::vector<ImpureSystem*> m_ImpureSystems;
|
||||
struct UnorderedSystems
|
||||
{
|
||||
std::map<std::string, System*> Systems;
|
||||
std::map<std::string, std::vector<PureSystem*>> PureSystems;
|
||||
std::vector<ImpureSystem*> ImpureSystems;
|
||||
};
|
||||
std::vector<UnorderedSystems> m_OrderedSystemGroups;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
// Example:
|
||||
// DEBUG_IF(condition) {
|
||||
// // This code is executed only in debug mode and if condition is true.
|
||||
// }
|
||||
// NOTE: condition statement is not executed at all in release mode.
|
||||
#ifndef DEBUG_IF
|
||||
#ifndef DEBUG
|
||||
#define DEBUG_IF(c) if(c)
|
||||
#else
|
||||
#define DEBUG_IF(c) if(false)
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,14 +1,84 @@
|
||||
#ifndef Client_h__
|
||||
#define Client_h__
|
||||
|
||||
#include <boost\asio.hpp>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
class Client
|
||||
#include <glm/common.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "Network/Network.h"
|
||||
#include "Network/MessageType.h"
|
||||
#include "Network/PlayerDefinition.h"
|
||||
#include "Network/SnapshotDefinitions.h"
|
||||
#include "Core/World.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/ConfigFile.h"
|
||||
#include "Input/EInputCommand.h"
|
||||
|
||||
class Client : public Network
|
||||
{
|
||||
Client();
|
||||
~Client();
|
||||
public:
|
||||
Client(ConfigFile* config);
|
||||
~Client();
|
||||
void Start(World* world, EventBroker* eventBroker) override;
|
||||
void Update() override;
|
||||
void Close();
|
||||
private:
|
||||
// Assio UDP logic
|
||||
boost::asio::ip::udp::endpoint m_ReceiverEndpoint;
|
||||
boost::asio::io_service m_IOService;
|
||||
boost::asio::ip::udp::socket m_Socket;
|
||||
|
||||
// Sending message to server logic
|
||||
int bytesRead = -1;
|
||||
char readBuf[1024] = { 0 };
|
||||
int snapshotInterval = 33;
|
||||
std::clock_t previousSnapshotMessage = std::clock();
|
||||
|
||||
// Packet loss logic
|
||||
unsigned int m_PacketID = 0;
|
||||
unsigned int m_PreviousPacketID = 0;
|
||||
unsigned int m_SendPacketID = 0;
|
||||
|
||||
// Game logic
|
||||
World* m_World;
|
||||
std::string m_PlayerName;
|
||||
int m_PlayerID = -1;
|
||||
|
||||
// Network logic
|
||||
PlayerDefinition m_PlayerDefinitions[MAXCONNECTIONS];
|
||||
SnapshotDefinitions m_NextSnapshot;
|
||||
bool m_ThreadIsRunning = true;
|
||||
double m_DurationOfPingTime;
|
||||
std::clock_t m_StartPingTime;
|
||||
// Use to check if we should send disconnect message
|
||||
// if game is turned of by closing window.
|
||||
bool m_WasStarted = false;
|
||||
|
||||
// Private member functions
|
||||
void readFromServer();
|
||||
void sendSnapshotToServer();
|
||||
int receive(char* data, size_t length);
|
||||
void send(Packet& packet);
|
||||
void connect();
|
||||
void disconnect();
|
||||
void ping();
|
||||
void moveMessageHead(char*& data, size_t& length, size_t stepSize);
|
||||
void parseMessageType(Packet& packet);
|
||||
void parseEventMessage(Packet& packet);
|
||||
void parseConnect(Packet& packet);
|
||||
void parsePing();
|
||||
void parseServerPing();
|
||||
void parseSnapshot(Packet& packet);
|
||||
void identifyPacketLoss();
|
||||
bool isConnected();
|
||||
EntityID createPlayer();
|
||||
|
||||
// Events
|
||||
EventBroker* m_EventBroker;
|
||||
EventRelay<Client, Events::InputCommand> m_EInputCommand;
|
||||
bool OnInputCommand(const Events::InputCommand &e);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef MessageType_h__
|
||||
#define MessageType_h__
|
||||
|
||||
// Message types used by both server and client.
|
||||
// Used to determine what type of message was sent.
|
||||
enum class MessageType
|
||||
{
|
||||
Connect,
|
||||
Disconnect,
|
||||
ClientPing,
|
||||
ServerPing,
|
||||
Message,
|
||||
Snapshot,
|
||||
Event,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef Network_h__
|
||||
#define Network_h__
|
||||
|
||||
#include "Core/World.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Network/Packet.h"
|
||||
|
||||
#define MAXCONNECTIONS 8
|
||||
#define INPUTSIZE 128
|
||||
|
||||
class Network
|
||||
{
|
||||
public:
|
||||
virtual ~Network() { };
|
||||
virtual void Start(World* m_world, EventBroker *eventBroker) = 0;
|
||||
virtual void Update() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef Packet_h__
|
||||
#define Packet_h__
|
||||
|
||||
#include <string>
|
||||
#include "Network/MessageType.h"
|
||||
#include "Core/Util/Logging.h"
|
||||
|
||||
// Defines the
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
// arg1: Type of message (Connect, Disconnect...)
|
||||
// arg2: PacketID for identifying packet loss.
|
||||
Packet(MessageType type, unsigned int& packetID);
|
||||
// Used to create packet from already existing data buffer.
|
||||
Packet(char* data, const int sizeOfPacket);
|
||||
|
||||
~Packet();
|
||||
// Add primitive types like int, float, char...
|
||||
template<typename T>
|
||||
void WritePrimitive(T val)
|
||||
{
|
||||
// Check if we are trying to add more than the package can fit.
|
||||
if (m_MaxPacketSize < m_Offset + sizeof(T)) {
|
||||
LOG_WARNING("Packet AddPrimitive(): You are trying to add more than we have allocated for!");
|
||||
}
|
||||
memcpy(m_Data + m_Offset, &val, sizeof(T));
|
||||
m_Offset += sizeof(T);
|
||||
}
|
||||
// Pops the first element as if it was a primitive.
|
||||
template<typename T>
|
||||
T ReadPrimitive()
|
||||
{
|
||||
if (m_Offset < m_ReturnDataOffset + sizeof(T)) {
|
||||
LOG_WARNING("Packet PopFrontPrimitive(): You are trying to remove more than what exists in this packet!");
|
||||
return -1;
|
||||
}
|
||||
T returnValue;
|
||||
memcpy(&returnValue, m_Data + m_ReturnDataOffset, sizeof(T));
|
||||
m_ReturnDataOffset += sizeof(T);
|
||||
return returnValue;
|
||||
}
|
||||
// Add a string to the message
|
||||
void WriteString(std::string str);
|
||||
// Add data to the message
|
||||
void WriteData(char* data, int sizeOfData);
|
||||
// Pops the first element as if it was a string.
|
||||
std::string ReadString();
|
||||
char* ReadData(int SizeOfData);
|
||||
|
||||
int Size() { return m_Offset; };
|
||||
char* Data() { return m_Data; };
|
||||
|
||||
private:
|
||||
char* m_Data;
|
||||
unsigned int m_ReturnDataOffset = 0;
|
||||
int m_Offset = 0;
|
||||
unsigned int m_MaxPacketSize = 128;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef PlayerDefinition_h__
|
||||
#define PlayerDefinition_h__
|
||||
#include <string>
|
||||
|
||||
struct PlayerDefinition {
|
||||
int EntityID = -1;
|
||||
std::string Name = "";
|
||||
boost::asio::ip::udp::endpoint Endpoint;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,12 +1,85 @@
|
||||
#ifndef Server_h__
|
||||
#define Server_h__
|
||||
|
||||
#include <boost\asio.hpp>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
class Server
|
||||
#include <glm/common.hpp>
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
|
||||
#include "Network/MessageType.h"
|
||||
#include "Network/PlayerDefinition.h"
|
||||
#include "Core/World.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Network/Network.h"
|
||||
|
||||
class Server : public Network
|
||||
{
|
||||
Server();
|
||||
~Server();
|
||||
public:
|
||||
Server();
|
||||
~Server();
|
||||
void Start(World* m_world, EventBroker *eventBroker) override;
|
||||
void Update() override;
|
||||
void Close();
|
||||
|
||||
private:
|
||||
// UDP logic
|
||||
boost::asio::ip::udp::endpoint m_ReceiverEndpoint;
|
||||
boost::asio::io_service m_IOService;
|
||||
boost::asio::ip::udp::socket m_Socket;
|
||||
PlayerDefinition m_PlayerDefinitions[MAXCONNECTIONS];
|
||||
|
||||
// Sending messages to client logic
|
||||
char readBuffer[1024] = { 0 };
|
||||
int bytesRead = 0;
|
||||
// time for previouse message
|
||||
std::clock_t previousePingMessage = std::clock();
|
||||
std::clock_t previousSnapshotMessage = std::clock();
|
||||
std::clock_t timOutTimer = std::clock();
|
||||
// How often we send messages (milliseconds)
|
||||
int intervalMs = 1000;
|
||||
int snapshotInterval = 50;
|
||||
int checkTimeOutInterval = 100;
|
||||
|
||||
//Timers
|
||||
std::clock_t m_StartPingTime;
|
||||
std::clock_t m_StopTimes[8];
|
||||
|
||||
// Game logic
|
||||
World* m_World;
|
||||
EventBroker* m_EventBroker;
|
||||
// vec.size() = ammount of players to create, stores playerID's
|
||||
std::vector<unsigned int> m_PlayersToCreate;
|
||||
|
||||
// Packet loss logic
|
||||
unsigned int m_PacketID;
|
||||
unsigned int m_PreviousPacketID;
|
||||
unsigned int m_SendPacketID;
|
||||
|
||||
// Close logic
|
||||
bool m_ThreadIsRunning = true;
|
||||
|
||||
// Private member functions
|
||||
int receive(char* data, size_t length);
|
||||
void readFromClients();
|
||||
void send(Packet& packet, int playerID);
|
||||
void send(Packet& packet);
|
||||
void moveMessageHead(char*& data, size_t& length, size_t stepSize);
|
||||
void broadcast(std::string message);
|
||||
void broadcast(Packet& packet);
|
||||
void sendSnapshot();
|
||||
void sendPing();
|
||||
void checkForTimeOuts();
|
||||
void disconnect(int i);
|
||||
void parseMessageType(Packet& packet);
|
||||
void parseEvent(Packet& packet);
|
||||
void parseConnect(Packet& packet);
|
||||
void parseDisconnect();
|
||||
void parseClientPing();
|
||||
void parseServerPing();
|
||||
void parseSnapshot(Packet& packet);
|
||||
void identifyPacketLoss();
|
||||
EntityID createPlayer();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef SnapshotDefinitions_h__
|
||||
#define SnapshotDefinitions_h__
|
||||
|
||||
struct SnapshotDefinitions
|
||||
{
|
||||
// "+Forward" is 8 characters * sizeof(char) = 8
|
||||
std::string InputForward;
|
||||
// "+Right" is 6 characters * sizeof(char) = 6
|
||||
std::string InputRight;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
virtual void Draw(RenderQueueCollection& rq) = 0;
|
||||
|
||||
protected:
|
||||
Rectangle m_Resolution = Rectangle(1280, 720);
|
||||
Rectangle m_Resolution = Rectangle::Rectangle(1280, 720);
|
||||
bool m_Fullscreen = false;
|
||||
bool m_VSYNC = false;
|
||||
int m_GLVersion[2];
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "../Core/World.h"
|
||||
#include "PickingPass.h"
|
||||
#include "DrawScenePass.h"
|
||||
#include "DebugCameraInputController.h"
|
||||
|
||||
|
||||
#define TILE_SIZE 16
|
||||
@@ -45,6 +46,8 @@ private:
|
||||
//----------------------Variables----------------------//
|
||||
EventBroker* m_EventBroker;
|
||||
|
||||
std::shared_ptr<DebugCameraInputController<Renderer>> m_DebugCameraInputController;
|
||||
|
||||
Texture* m_ErrorTexture;
|
||||
Texture* m_WhiteTexture;
|
||||
float m_CameraMoveSpeed;
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
#include "PlayerSystem.h"
|
||||
#include "Editor/EditorSystem.h"
|
||||
|
||||
// Network
|
||||
#include <boost/thread.hpp>
|
||||
#include "Network/Network.h"
|
||||
#include "Network/Server.h"
|
||||
#include "Network/Client.h"
|
||||
|
||||
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
@@ -39,12 +46,21 @@ private:
|
||||
World* m_World;
|
||||
SystemPipeline* m_SystemPipeline;
|
||||
RenderQueueFactory* m_RenderQueueFactory;
|
||||
// Network variables
|
||||
boost::thread m_NetworkThread;
|
||||
|
||||
// Network methods
|
||||
void networkFunction();
|
||||
Network* m_ClientOrServer;
|
||||
bool m_IsClientOrServer = false;
|
||||
|
||||
EventRelay<Game, Events::InputCommand> m_EInputCommand;
|
||||
bool debugOnInputCommand(const Events::InputCommand& e);
|
||||
|
||||
void debugInitialize();
|
||||
void debugTick(double dt);
|
||||
EventRelay<Client, Events::KeyDown> m_EKeyDown;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef HealthSystem_h__
|
||||
#define HealthSystem_h__
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/common.hpp>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core\EPlayerDamage.h";
|
||||
#include "Core\EPlayerHealthPickup.h";
|
||||
#include "Core\EPlayerDeath.h";
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
class HealthSystem : public PureSystem
|
||||
{
|
||||
public:
|
||||
HealthSystem(EventBroker* eventBroker);
|
||||
|
||||
//updatecomponent
|
||||
virtual void UpdateComponent(World* world, ComponentWrapper& health, double dt) override;
|
||||
|
||||
private:
|
||||
//methods which will take care of specific events
|
||||
EventRelay<HealthSystem, Events::PlayerDamage> m_EPlayerDamage;
|
||||
bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e);
|
||||
EventRelay<HealthSystem, Events::PlayerHealthPickup> m_EPlayerHealthPickup;
|
||||
bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e);
|
||||
|
||||
//vector which will keep track of health changes
|
||||
std::vector<std::tuple<EntityID, double>> m_DeltaHealthVector;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
+10
-21
@@ -6,17 +6,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Core/EKeyDown.h"
|
||||
#include "Core/EKeyUp.h"
|
||||
|
||||
struct KeyInput
|
||||
{
|
||||
bool Forward = false;
|
||||
bool Left = false;
|
||||
bool Back = false;
|
||||
bool Right = false;
|
||||
};
|
||||
#include "Collision/ETrigger.h"
|
||||
|
||||
class PlayerSystem : public PureSystem
|
||||
{
|
||||
@@ -24,21 +14,20 @@ public:
|
||||
PlayerSystem(EventBroker* eventBroker)
|
||||
: PureSystem(eventBroker, "Player")
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &PlayerSystem::OnKeyDown);
|
||||
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 UpdateComponent(World* world, ComponentWrapper& player, double dt) override;
|
||||
|
||||
private:
|
||||
float m_Speed = 5;
|
||||
glm::vec3 m_Direction;
|
||||
KeyInput input;
|
||||
|
||||
EventRelay<PlayerSystem, Events::KeyDown> m_EKeyDown;
|
||||
bool OnKeyDown(const Events::KeyDown &event);
|
||||
EventRelay<PlayerSystem, Events::KeyUp> m_EKeyUp;
|
||||
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
|
||||
@@ -3,9 +3,17 @@ LogLevel=1
|
||||
LoadMap=
|
||||
EditorEnabled=false
|
||||
|
||||
|
||||
[Video]
|
||||
Fullscreen=false
|
||||
VSYNC=false
|
||||
Width=1280
|
||||
Height=720
|
||||
FOV=45
|
||||
FOV=45
|
||||
|
||||
[Networking]
|
||||
StartNetwork=false
|
||||
IsServer=false
|
||||
Name=Bob
|
||||
Address=127.0.0.1
|
||||
Port=13
|
||||
@@ -18,4 +18,7 @@ F1=ToggleEditor
|
||||
1=EditorToolMove
|
||||
2=EditorToolRotate
|
||||
3=EditorToolScale
|
||||
X=EditorToggleTransformSpace
|
||||
X=EditorToggleTransformSpace
|
||||
C=ConnectToServer
|
||||
N=SwitchToServer
|
||||
M=SwitchToClient
|
||||
@@ -6,4 +6,7 @@
|
||||
<xs:include schemaLocation="Components/Test.xsd"/>
|
||||
<xs:include schemaLocation="Components/RaptorCopter.xsd"/>
|
||||
<xs:include schemaLocation="Components/Player.xsd"/>
|
||||
<xs:include schemaLocation="Components/AABB.xsd"/>
|
||||
<xs:include schemaLocation="Components/Trigger.xsd"/>
|
||||
<xs:include schemaLocation="Components/Health.xsd"/>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,4 @@
|
||||
<c:AABB>
|
||||
<BoxCenter X="0" Y="0" Z="0"/>
|
||||
<BoxSize X="1" Y="1" Z="1"/>
|
||||
</c:AABB>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="types">
|
||||
<xs:import schemaLocation="../Types.xsd" namespace="types"/>
|
||||
|
||||
<xs:element name="AABB">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="BoxCenter" type="t:Vector" minOccurs="0"/>
|
||||
<xs:element name="BoxSize" type="t:Vector" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,4 @@
|
||||
<c:Health>
|
||||
<Health>100</Health>
|
||||
<MaxHealth>100</MaxHealth>
|
||||
</c:Health>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="types">
|
||||
<xs:import schemaLocation="../Types.xsd" namespace="types"/>
|
||||
|
||||
<xs:element name="Health">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="Health" type="t:double" minOccurs="0"/>
|
||||
<xs:element name="MaxHealth" type="t:double" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -1,3 +1,7 @@
|
||||
<c:Player>
|
||||
<Velocity X="0" Y="0" Z="0"/>
|
||||
<Forward>false</Forward>
|
||||
<Left>false</Left>
|
||||
<Back>false</Back>
|
||||
<Right>false</Right>
|
||||
</c:Player>
|
||||
@@ -7,6 +7,10 @@
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="Velocity" type="t:Vector" minOccurs="0"/>
|
||||
<xs:element name="Forward" type="t:bool" minOccurs="0"/>
|
||||
<xs:element name="Left" type="t:bool" minOccurs="0"/>
|
||||
<xs:element name="Back" type="t:bool" minOccurs="0"/>
|
||||
<xs:element name="Right" type="t:bool" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<c:Trigger>
|
||||
</c:Trigger>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:t="types">
|
||||
<xs:import schemaLocation="../Types.xsd" namespace="types"/>
|
||||
|
||||
<xs:element name="Trigger">
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude" xsi:noNamespaceSchemaLocation="../Types/Entity.xsd" xmlns:c="components">
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Orientation X="0" Y="0" Z="0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/DummyScene.obj</Resource>
|
||||
</c:Model>
|
||||
</Components>
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="-1.5"/>
|
||||
<Scale X="1" Y="1" Z="1"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/ScaleWidget.obj</Resource>
|
||||
</c:Model>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="1.5"/>
|
||||
<Scale X="2" Y="2" Z="2"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/RotationWidgetX.obj</Resource>
|
||||
</c:Model>
|
||||
<c:Trigger>
|
||||
</c:Trigger>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Player>
|
||||
<Velocity X="0" Y="0" Z="0"/>
|
||||
</c:Player>
|
||||
<c:Transform>
|
||||
<Position X="2.5"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
</c:Model>
|
||||
<c:AABB>
|
||||
</c:AABB>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="-0"/>
|
||||
<Scale X="1" Y="1" Z="1"/>
|
||||
</c:Transform>
|
||||
<!--<c:Move>
|
||||
<Speed>1</Speed>
|
||||
<Direction X="-1"/>
|
||||
<Rotation Y="3.14"/>
|
||||
</c:Move>-->
|
||||
</Components>
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0"/>
|
||||
<Orientation X="0.0" Y="0" Z="1.0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitRaptor.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="-0.01" Y="0.55"/>
|
||||
<Orientation X="0" Y="0" Z="-1"/>
|
||||
</c:Transform>
|
||||
<c:RaptorCopter>
|
||||
<Speed>20</Speed>
|
||||
<Axis Y="1"/>
|
||||
</c:RaptorCopter>
|
||||
</Components>
|
||||
<Children>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0"/>
|
||||
<Scale X="1.7" Y="0.03" Z="0.1"/>
|
||||
<Orientation X="0" Y="0" Z="0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="0" Y="0"/>
|
||||
<Scale X="1.7" Y="0.03" Z="0.1"/>
|
||||
<Orientation X="0" Y="1.57" Z="0"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
<Color R="1" G="0.4" B="0.8"/>
|
||||
</c:Model>
|
||||
</Components>
|
||||
</Entity>
|
||||
</Children>
|
||||
</Entity>
|
||||
</Children>
|
||||
</Entity>
|
||||
</Children>
|
||||
</Entity>
|
||||
</Children>
|
||||
</Entity>
|
||||
@@ -25,13 +25,16 @@
|
||||
<Components>
|
||||
<c:Transform>
|
||||
<Position X="1.5"/>
|
||||
<Scale X="2" Y="2" Z="2"/>
|
||||
</c:Transform>
|
||||
<c:Model>
|
||||
<Resource>Models/RotationWidget.obj</Resource>
|
||||
</c:Model>
|
||||
<c:Trigger>
|
||||
</c:Trigger>
|
||||
</Components>
|
||||
</Entity>
|
||||
<Entity>
|
||||
<!--<Entity>
|
||||
<Components>
|
||||
<c:Player>
|
||||
<Velocity X="0" Y="0" Z="0"/>
|
||||
@@ -42,8 +45,10 @@
|
||||
<c:Model>
|
||||
<Resource>Models/Core/UnitCube.obj</Resource>
|
||||
</c:Model>
|
||||
<c:AABB>
|
||||
</c:AABB>
|
||||
</Components>
|
||||
</Entity>
|
||||
</Entity>-->
|
||||
<Entity>
|
||||
<Components>
|
||||
<c:Transform>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<xs:element ref="c:Test" minOccurs="0"/>
|
||||
<xs:element ref="c:RaptorCopter" minOccurs="0"/>
|
||||
<xs:element ref="c:Player" minOccurs="0"/>
|
||||
<xs:element ref="c:Health" minOccurs="0"/>
|
||||
</xs:all>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
@@ -69,6 +69,12 @@ file(GLOB SOURCE_FILES_GUI
|
||||
)
|
||||
source_group(GUI FILES ${SOURCE_FILES_GUI})
|
||||
|
||||
file(GLOB SOURCE_FILES_Collision
|
||||
"${INCLUDE_PATH}/Collision/*.h"
|
||||
"Collision/*.cpp"
|
||||
)
|
||||
source_group(Collision FILES ${SOURCE_FILES_Collision})
|
||||
|
||||
file(GLOB SOURCE_FILES_Editor
|
||||
"${INCLUDE_PATH}/Editor/*.h"
|
||||
"Editor/*.cpp"
|
||||
@@ -83,6 +89,7 @@ set(SOURCE_FILES
|
||||
${SOURCE_FILES_GUI}
|
||||
${SOURCE_FILES_Rendering}
|
||||
${SOURCE_FILES_Rendering_Util}
|
||||
${SOURCE_FILES_Collision}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_draw.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_demo.cpp
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "Collision/Collision.h"
|
||||
#include "Engine/GLM.h"
|
||||
#include "Core/World.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
namespace Collision
|
||||
{
|
||||
|
||||
//note: this one hasnt been delta adjusted like RayVsAABB has
|
||||
bool RayAABBIntr(const Ray& ray, const AABB& box)
|
||||
{
|
||||
glm::vec3 w = 75.0f * ray.Direction();
|
||||
glm::vec3 v = glm::abs(w);
|
||||
glm::vec3 c = ray.Origin() - box.Center() + w;
|
||||
glm::vec3 half = box.HalfSize();
|
||||
|
||||
if (abs(c.x) > v.x + half.x) {
|
||||
return false;
|
||||
}
|
||||
if (abs(c.y) > v.y + half.y) {
|
||||
return false;
|
||||
}
|
||||
if (abs(c.z) > v.z + half.z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) {
|
||||
return false;
|
||||
}
|
||||
if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) {
|
||||
return false;
|
||||
}
|
||||
return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x);
|
||||
}
|
||||
|
||||
bool RayVsAABB(const Ray& ray, const AABB& box)
|
||||
{
|
||||
float dummy;
|
||||
return RayVsAABB(ray, box, dummy);
|
||||
}
|
||||
|
||||
bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance)
|
||||
{
|
||||
glm::vec3 invdir = 1.0f / ray.Direction();
|
||||
glm::vec3 origin = ray.Origin();
|
||||
|
||||
float t1 = (box.MinCorner().x - origin.x)*invdir.x;
|
||||
float t2 = (box.MaxCorner().x - origin.x)*invdir.x;
|
||||
float t3 = (box.MinCorner().y - origin.y)*invdir.y;
|
||||
float t4 = (box.MaxCorner().y - origin.y)*invdir.y;
|
||||
float t5 = (box.MinCorner().z - origin.z)*invdir.z;
|
||||
float t6 = (box.MaxCorner().z - origin.z)*invdir.z;
|
||||
|
||||
float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6));
|
||||
float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6));
|
||||
|
||||
//if (tmax < 0 || tmin > tmax)
|
||||
//if tmin,tmax are almost the same (i.e. hitting exactly in the corner) then tmin might be slightly
|
||||
//greater than tmax becuase of floating-precision problems. fixed by adding a small delta to tmax
|
||||
if (tmax < 0 || tmin>(tmax + 0.0001f))
|
||||
return false;
|
||||
|
||||
outDistance = (tmin > 0) ? tmin : tmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b)
|
||||
{
|
||||
const glm::vec3& aCenter = a.Center();
|
||||
const glm::vec3& bCenter = b.Center();
|
||||
const glm::vec3& aHSize = a.HalfSize();
|
||||
const glm::vec3& bHSize = b.HalfSize();
|
||||
//Test will probably exit because of the X and Z axes more often, so test them first.
|
||||
if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) {
|
||||
return false;
|
||||
}
|
||||
if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) {
|
||||
return false;
|
||||
}
|
||||
return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1]));
|
||||
}
|
||||
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation)
|
||||
{
|
||||
minimumTranslation = glm::vec3(0, 0, 0);
|
||||
const glm::vec3& aMax = a.MaxCorner();
|
||||
const glm::vec3& bMax = b.MaxCorner();
|
||||
const glm::vec3& aMin = a.MinCorner();
|
||||
const glm::vec3& bMin = b.MinCorner();
|
||||
const glm::vec3& bSize = b.Size();
|
||||
const glm::vec3& aSize = a.Size();
|
||||
float minOffset = INFINITY;
|
||||
float off;
|
||||
auto axisesIntersecting = glm::tvec3<bool, glm::highp>(false, false, false);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
off = bMax[i] - aMin[i];
|
||||
if (off > 0 && off < bSize[i] + aSize[i]) {
|
||||
if (off < minOffset) {
|
||||
minimumTranslation = glm::vec3();
|
||||
minimumTranslation[i] = minOffset = off;
|
||||
}
|
||||
axisesIntersecting[i] = true;
|
||||
}
|
||||
off = aMax[i] - bMin[i];
|
||||
if (off > 0 && off < bSize[i] + aSize[i]) {
|
||||
if (off < minOffset) {
|
||||
minOffset = off;
|
||||
minimumTranslation = glm::vec3();
|
||||
minimumTranslation[i] = -off;
|
||||
}
|
||||
axisesIntersecting[i] = true;
|
||||
}
|
||||
}
|
||||
return glm::all(axisesIntersecting);
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices)
|
||||
{
|
||||
for (int i = 0; i < modelIndices.size(); ++i) {
|
||||
glm::vec3 v0 = modelVertices[modelIndices[i]].Position;
|
||||
glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0
|
||||
glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0
|
||||
glm::vec3 m = ray.Origin() - v0;
|
||||
glm::vec3 MxE1 = glm::cross(m, e1);
|
||||
glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);
|
||||
float DetInv = glm::dot(e1, DxE2);
|
||||
if (std::abs(DetInv) < FLT_EPSILON) {
|
||||
continue;
|
||||
}
|
||||
DetInv = 1.0f / DetInv;
|
||||
float u = glm::dot(m, DxE2) * DetInv;
|
||||
float v = glm::dot(ray.Direction(), MxE1) * DetInv;
|
||||
//u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem
|
||||
if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) {
|
||||
continue;
|
||||
}
|
||||
//Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit.
|
||||
if (0 <= glm::dot(e2, MxE1) * DetInv) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
float& outDistance,
|
||||
float& outUCoord,
|
||||
float& outVCoord)
|
||||
{
|
||||
outDistance = INFINITY;
|
||||
bool hit = false;
|
||||
for (int i = 0; i < modelIndices.size(); ++i) {
|
||||
glm::vec3 v0 = modelVertices[modelIndices[i]].Position;
|
||||
glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0
|
||||
glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0
|
||||
glm::vec3 m = ray.Origin() - v0;
|
||||
glm::vec3 MxE1 = glm::cross(m, e1);
|
||||
glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);//pVec
|
||||
float DetInv = glm::dot(e1, DxE2);
|
||||
if (std::abs(DetInv) < FLT_EPSILON) {
|
||||
continue;
|
||||
}
|
||||
DetInv = 1.0f / DetInv;
|
||||
float dist = glm::dot(e2, MxE1) * DetInv;
|
||||
if (dist >= outDistance) {
|
||||
continue;
|
||||
}
|
||||
float u = glm::dot(m, DxE2) * DetInv;
|
||||
float v = glm::dot(ray.Direction(), MxE1) * DetInv;
|
||||
|
||||
//u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem
|
||||
//If u and v are positive, u+v <= 1, dist is positive, and less than closest.
|
||||
if (0 <= (u + 0.001f) && 0 <= (v + 0.001f) && u + v <= 1 && 0 <= dist) {
|
||||
outDistance = dist;
|
||||
outUCoord = u;
|
||||
outVCoord = v;
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
glm::vec3& outHitPosition)
|
||||
{
|
||||
float u;
|
||||
float v;
|
||||
float dist;
|
||||
bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v);
|
||||
outHitPosition = ray.Origin() + dist * ray.Direction();
|
||||
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);
|
||||
}
|
||||
|
||||
bool attachAABBComponentFromModel(World* world, EntityID id)
|
||||
{
|
||||
if (!world->HasComponent(id, "Model")) {
|
||||
return false;
|
||||
}
|
||||
ComponentWrapper model = world->GetComponent(id, "Model");
|
||||
ComponentWrapper collision = world->AttachComponent(id, "AABB");
|
||||
Model* modelRes = ResourceManager::Load<Model>(model["Resource"]);
|
||||
if (modelRes == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = modelRes->m_Matrix;
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox)
|
||||
{
|
||||
ComponentWrapper& cTrans = world->GetComponent(AABBComponent.EntityID, "Transform");
|
||||
ComponentWrapper model = world->GetComponent(AABBComponent.EntityID, "Model");
|
||||
Model* modelRes = ResourceManager::Load<Model>(model["Resource"]);
|
||||
outBox.CreateFromCenter(AABBComponent["BoxCenter"], AABBComponent["BoxSize"]);
|
||||
glm::vec3 mini = outBox.MinCorner();
|
||||
glm::vec3 maxi = outBox.MaxCorner();
|
||||
|
||||
if (modelRes == nullptr) {
|
||||
return false;
|
||||
}
|
||||
glm::mat4 modelMatrix = modelRes->m_Matrix *
|
||||
glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) *
|
||||
glm::scale((glm::vec3)cTrans["Scale"]);
|
||||
|
||||
outBox = AABB(modelMatrix * glm::vec4(mini.x, mini.y, mini.z, 1),
|
||||
modelMatrix * glm::vec4(maxi.x, maxi.y, maxi.z, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFromModel)
|
||||
{
|
||||
if (!world->HasComponent(entity, "AABB")) {
|
||||
if (forceBoxFromModel) {
|
||||
if (!attachAABBComponentFromModel(world, entity))
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ComponentWrapper& cBox = world->GetComponent(entity, "AABB");
|
||||
return GetEntityBox(world, cBox, outBox);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "Collision/Collision.h"
|
||||
#include "Collision/CollisionSystem.h"
|
||||
#include "Core/AABB.h"
|
||||
|
||||
void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, double dt)
|
||||
{
|
||||
//Right now, cAABB is a component attached to any entity that should be collideable.
|
||||
AABB thisBox;
|
||||
if (!Collision::GetEntityBox(world, cAABB, thisBox)) {
|
||||
return;
|
||||
}
|
||||
//Press 'Z' to enable/disable collision.
|
||||
if (zPress) {
|
||||
return;
|
||||
}
|
||||
//Here, mover should be an object that moves, currently only players.
|
||||
for (auto& mover : *world->GetComponents("Player")) {
|
||||
if (cAABB.EntityID == mover.EntityID) {
|
||||
continue;
|
||||
}
|
||||
AABB otherBox;
|
||||
if (!Collision::GetEntityBox(world, mover.EntityID, otherBox)) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 resolveTranslation;
|
||||
if (Collision::AABBVsAABB(otherBox, thisBox, resolveTranslation)) {
|
||||
ComponentWrapper& trans = world->GetComponent(mover.EntityID, "Transform");
|
||||
//TODO: Special treatment if both are movers.
|
||||
trans["Position"] = (glm::vec3)trans["Position"] + resolveTranslation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CollisionSystem::OnKeyUp(const Events::KeyUp & event)
|
||||
{
|
||||
if (event.KeyCode == GLFW_KEY_Z) {
|
||||
zPress = !zPress;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "Collision/TriggerSystem.h"
|
||||
#include "Collision/Collision.h"
|
||||
#include "Core/AABB.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
void TriggerSystem::UpdateComponent(World* world, ComponentWrapper& trigger, double dt)
|
||||
{
|
||||
//Currently only players can trigger things.
|
||||
auto players = world->GetComponents("Player");
|
||||
if (players == nullptr) {
|
||||
return;
|
||||
}
|
||||
EntityID tId = trigger.EntityID;
|
||||
AABB triggerBox;
|
||||
//The trigger *should* have a bounding box, or something, to test against so it can be triggered.
|
||||
if (!Collision::GetEntityBox(world, tId, triggerBox, true)) {
|
||||
return;
|
||||
}
|
||||
for (auto& pc : *players) {
|
||||
EntityID pId = pc.EntityID;
|
||||
AABB playerBox;
|
||||
//The player can't trigger anything without an AABB.
|
||||
if (!Collision::GetEntityBox(world, pId, playerBox, true)) {
|
||||
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() - 2.0f * playerBox.Size());
|
||||
if (Collision::AABBVsAABB(completelyInsideBox, playerBox) &&
|
||||
glm::all(glm::greaterThan(triggerBox.Size(), playerBox.Size()))) {
|
||||
//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::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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "Core/AABB.h"
|
||||
#include "Common.h"
|
||||
|
||||
AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos)
|
||||
: m_MinCorner(minPos)
|
||||
, m_MaxCorner(maxPos)
|
||||
, m_Center(0.5f * (maxPos + minPos))
|
||||
, m_HalfSize(0.5f * (maxPos - minPos))
|
||||
{
|
||||
DEBUG_IF(glm::any(glm::lessThan(m_MaxCorner, m_MinCorner))) {
|
||||
LOG_WARNING("AABB maxCorner coordinates are not greater than minCorner");
|
||||
m_MaxCorner.x = glm::max(m_MaxCorner.x, m_MinCorner.x);
|
||||
m_MinCorner.x = glm::min(m_MaxCorner.x, m_MinCorner.x);
|
||||
m_MaxCorner.y = glm::max(m_MaxCorner.y, m_MinCorner.y);
|
||||
m_MinCorner.y = glm::min(m_MaxCorner.y, m_MinCorner.y);
|
||||
m_MaxCorner.z = glm::max(m_MaxCorner.z, m_MinCorner.z);
|
||||
m_MinCorner.z = glm::min(m_MaxCorner.z, m_MinCorner.z);
|
||||
}
|
||||
}
|
||||
|
||||
AABB::AABB(const glm::vec4& minPos, const glm::vec4& maxPos)
|
||||
: AABB(glm::vec3(minPos), glm::vec3(maxPos))
|
||||
{}
|
||||
|
||||
void AABB::CreateFromCenter(const glm::vec3& center, const glm::vec3& size)
|
||||
{
|
||||
m_Center = center;
|
||||
m_HalfSize = 0.5f * size;
|
||||
m_MinCorner = m_Center - m_HalfSize;
|
||||
m_MaxCorner = m_Center + m_HalfSize;
|
||||
}
|
||||
|
||||
AABB::~AABB()
|
||||
{}
|
||||
@@ -19,7 +19,7 @@ bool ComponentPoolForwardIterator::operator!=(const ComponentPoolForwardIterator
|
||||
return m_MemoryPoolIterator != other.m_MemoryPoolIterator;
|
||||
}
|
||||
|
||||
ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++(int)
|
||||
ComponentPoolForwardIterator ComponentPoolForwardIterator::operator++(int)
|
||||
{
|
||||
ComponentPoolForwardIterator copyIter(*this);
|
||||
operator++();
|
||||
|
||||
@@ -2,21 +2,24 @@
|
||||
|
||||
BaseEventRelay::~BaseEventRelay()
|
||||
{
|
||||
if (m_Broker != nullptr) {
|
||||
if (m_Broker != nullptr) {
|
||||
m_Broker->Unsubscribe(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventBroker::Unsubscribe(BaseEventRelay &relay) // ?
|
||||
void EventBroker::Unsubscribe(BaseEventRelay& relay) // ?
|
||||
{
|
||||
if (m_IsProcessing) {
|
||||
m_RelaysToUnsubscribe.push_back(&relay);
|
||||
} else {
|
||||
unsubscribeImmediate(relay);
|
||||
}
|
||||
auto identifier = std::make_tuple(relay.m_EventID, relay.m_ContextTypeName, relay.m_EventTypeName);
|
||||
|
||||
relay.m_Broker = nullptr;
|
||||
if (m_IsProcessing) {
|
||||
m_RelaysToUnsubscribe.push_back(identifier);
|
||||
} else {
|
||||
unsubscribeImmediate(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
void EventBroker::Subscribe(BaseEventRelay &relay)
|
||||
void EventBroker::Subscribe(BaseEventRelay& relay)
|
||||
{
|
||||
if (m_IsProcessing) {
|
||||
m_RelaysToSubscribe.push_back(&relay);
|
||||
@@ -38,12 +41,11 @@ int EventBroker::Process(std::string contextTypeName)
|
||||
|
||||
int eventsProcessed = 0;
|
||||
for (auto &pair : *m_EventQueueRead) {
|
||||
std::string &eventTypeName = pair.first;
|
||||
std::string& eventTypeName = pair.first;
|
||||
std::shared_ptr<Event> event = pair.second;
|
||||
|
||||
auto itpair = relays.equal_range(eventTypeName);
|
||||
for (auto it2 = itpair.first; it2 != itpair.second; it2++)
|
||||
{
|
||||
for (auto it2 = itpair.first; it2 != itpair.second; it2++) {
|
||||
std::string name = it2->first;
|
||||
BaseEventRelay* relay = it2->second;
|
||||
relay->Receive(event);
|
||||
@@ -60,8 +62,8 @@ int EventBroker::Process(std::string contextTypeName)
|
||||
m_RelaysToSubscribe.clear();
|
||||
|
||||
// Process pending unsubscriptions
|
||||
for (auto& r : m_RelaysToUnsubscribe) {
|
||||
unsubscribeImmediate(*r);
|
||||
for (auto& identifier : m_RelaysToUnsubscribe) {
|
||||
unsubscribeImmediate(identifier);
|
||||
}
|
||||
m_RelaysToUnsubscribe.clear();
|
||||
|
||||
@@ -81,21 +83,26 @@ void EventBroker::Clear()
|
||||
void EventBroker::subscribeImmediate(BaseEventRelay& relay)
|
||||
{
|
||||
relay.m_Broker = this;
|
||||
relay.m_EventID = m_NextEventID++;
|
||||
m_ContextRelays[relay.m_ContextTypeName].insert(std::make_pair(relay.m_EventTypeName, &relay));
|
||||
}
|
||||
|
||||
void EventBroker::unsubscribeImmediate(BaseEventRelay& relay)
|
||||
void EventBroker::unsubscribeImmediate(std::tuple<EventID, ContextTypeName_t, EventTypeName_t> identifier)
|
||||
{
|
||||
auto contextIt = m_ContextRelays.find(relay.m_ContextTypeName);
|
||||
EventID eventID;
|
||||
ContextTypeName_t contextTypeName;
|
||||
EventTypeName_t eventTypeName;
|
||||
std::tie(eventID, contextTypeName, eventTypeName) = identifier;
|
||||
|
||||
auto contextIt = m_ContextRelays.find(contextTypeName);
|
||||
if (contextIt == m_ContextRelays.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto eventRelays = contextIt->second;
|
||||
auto itpair = eventRelays.equal_range(relay.m_EventTypeName);
|
||||
auto itpair = eventRelays.equal_range(eventTypeName);
|
||||
for (auto it = itpair.first; it != itpair.second; ++it) {
|
||||
if (it->second == &relay) {
|
||||
relay.m_Broker = nullptr;
|
||||
if (it->second->m_EventID == eventID) {
|
||||
eventRelays.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "Core/OctTree.h"
|
||||
#include "Collision/Collision.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
//To be able to sort nodes based on distance to ray origin.
|
||||
struct ChildInfo
|
||||
{
|
||||
int Index;
|
||||
float Distance;
|
||||
};
|
||||
|
||||
bool isFirstLower(const ChildInfo& first, const ChildInfo& second)
|
||||
{
|
||||
return first.Distance < second.Distance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OctTree::OctTree()
|
||||
: OctTree(AABB(), 0)
|
||||
{}
|
||||
|
||||
OctTree::OctTree(const AABB& octTreeBounds, int subDivisions)
|
||||
: m_Root(new OctChild(octTreeBounds, subDivisions, m_StaticObjects, m_DynamicObjects))
|
||||
, m_UpdatedOnce(false)
|
||||
{}
|
||||
|
||||
OctTree::~OctTree()
|
||||
{
|
||||
delete m_Root;
|
||||
}
|
||||
|
||||
void OctTree::AddDynamicObject(const AABB& box)
|
||||
{
|
||||
m_Root->AddDynamicObject(box);
|
||||
m_DynamicObjects.push_back(box);
|
||||
}
|
||||
|
||||
void OctTree::AddStaticObject(const AABB& box)
|
||||
{
|
||||
m_Root->AddStaticObject(box);
|
||||
m_StaticObjects.push_back(box);
|
||||
}
|
||||
|
||||
void OctTree::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes)
|
||||
{
|
||||
falsifyObjectChecks();
|
||||
m_Root->BoxesInSameRegion(box, outBoxes);
|
||||
}
|
||||
|
||||
void OctTree::ClearObjects()
|
||||
{
|
||||
m_StaticObjects.clear();
|
||||
m_DynamicObjects.clear();
|
||||
m_Root->ClearObjects();
|
||||
}
|
||||
|
||||
void OctTree::ClearDynamicObjects()
|
||||
{
|
||||
m_DynamicObjects.clear();
|
||||
m_Root->ClearDynamicObjects();
|
||||
}
|
||||
|
||||
bool OctTree::RayCollides(const Ray& ray, Output& data)
|
||||
{
|
||||
falsifyObjectChecks();
|
||||
data.CollideDistance = -1;
|
||||
return m_Root->RayCollides(ray, data);
|
||||
}
|
||||
|
||||
bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected)
|
||||
{
|
||||
falsifyObjectChecks();
|
||||
return m_Root->BoxCollides(boxToTest, outBoxIntersected);
|
||||
}
|
||||
|
||||
void OctTree::falsifyObjectChecks()
|
||||
{
|
||||
for (auto& obj : m_StaticObjects) {
|
||||
obj.Checked = false;
|
||||
}
|
||||
for (auto& obj : m_DynamicObjects) {
|
||||
obj.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
OctTree::OctChild::OctChild(const AABB& octTreeBounds,
|
||||
int subDivisions,
|
||||
std::vector<ContainedObject>& staticObjects,
|
||||
std::vector<ContainedObject>& dynamicObjects)
|
||||
: m_Box(octTreeBounds)
|
||||
, m_StaticObjectsRef(staticObjects)
|
||||
, m_DynamicObjectsRef(dynamicObjects)
|
||||
{
|
||||
if (subDivisions == 0) {
|
||||
for (OctChild*& c : m_Children) {
|
||||
c = nullptr;
|
||||
}
|
||||
} else {
|
||||
--subDivisions;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
glm::vec3 minPos, maxPos;
|
||||
const glm::vec3& parentMin = m_Box.MinCorner();
|
||||
const glm::vec3& parentMax = m_Box.MaxCorner();
|
||||
const glm::vec3& parentCenter = m_Box.Center();
|
||||
std::bitset<3> bits(i);
|
||||
//If child is 4,5,6,7.
|
||||
if (bits.test(2)) {
|
||||
minPos.x = parentCenter.x;
|
||||
maxPos.x = parentMax.x;
|
||||
} else {
|
||||
minPos.x = parentMin.x;
|
||||
maxPos.x = parentCenter.x;
|
||||
}
|
||||
|
||||
//If child is 2,3,6,7
|
||||
if (bits.test(1)) {
|
||||
minPos.y = parentCenter.y;
|
||||
maxPos.y = parentMax.y;
|
||||
} else {
|
||||
minPos.y = parentMin.y;
|
||||
maxPos.y = parentCenter.y;
|
||||
}
|
||||
//If child is 1,3,5,7
|
||||
if (bits.test(0)) {
|
||||
minPos.z = parentCenter.z;
|
||||
maxPos.z = parentMax.z;
|
||||
} else {
|
||||
minPos.z = parentMin.z;
|
||||
maxPos.z = parentCenter.z;
|
||||
}
|
||||
m_Children[i] = new OctChild(AABB(minPos, maxPos), subDivisions, m_StaticObjectsRef, m_DynamicObjectsRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OctTree::OctChild::~OctChild()
|
||||
{
|
||||
for (OctChild*& c : m_Children) {
|
||||
if (c != nullptr) {
|
||||
delete c;
|
||||
c = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OctTree::OctChild::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (int i : childIndicesContainingBox(boxToTest)) {
|
||||
if (m_Children[i]->BoxCollides(boxToTest, outBoxIntersected))
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (int i : m_StaticObjIndices) {
|
||||
if (!m_StaticObjectsRef[i].Checked) {
|
||||
const AABB& objBox = m_StaticObjectsRef[i].Box;
|
||||
if (Collision::AABBVsAABB(boxToTest, objBox)) {
|
||||
outBoxIntersected = objBox;
|
||||
return true;
|
||||
}
|
||||
m_StaticObjectsRef[i].Checked = true;
|
||||
}
|
||||
}
|
||||
for (int i : m_DynamicObjIndices) {
|
||||
if (!m_DynamicObjectsRef[i].Checked) {
|
||||
const AABB& objBox = m_DynamicObjectsRef[i].Box;
|
||||
if (!Collision::IsSameBoxProbably(boxToTest, objBox) &&
|
||||
Collision::AABBVsAABB(boxToTest, objBox)) {
|
||||
outBoxIntersected = objBox;
|
||||
return true;
|
||||
}
|
||||
m_DynamicObjectsRef[i].Checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctTree::OctChild::RayCollides(const Ray& ray, Output& data) const
|
||||
{
|
||||
//If the node AABB is missed, everything it contains is missed.
|
||||
if (Collision::RayAABBIntr(ray, m_Box)) {
|
||||
//If the ray shoots the tree, and it is a parent to 8 children :o
|
||||
if (hasChildren()) {
|
||||
//Sort children according to their distance from the ray origin.
|
||||
std::vector<ChildInfo> childInfos;
|
||||
childInfos.reserve(8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Center()) });
|
||||
}
|
||||
std::sort(childInfos.begin(), childInfos.end(), isFirstLower);
|
||||
//Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit.
|
||||
for (const ChildInfo& info : childInfos) {
|
||||
if (m_Children[info.Index]->RayCollides(ray, data)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Check against boxes in the node.
|
||||
float minDist = INFINITY;
|
||||
bool intersected = false;
|
||||
for (int i : m_StaticObjIndices) {
|
||||
float dist;
|
||||
//If we haven't tested against this object before, and the ray hits.
|
||||
if (!m_StaticObjectsRef[i].Checked &&
|
||||
Collision::RayVsAABB(ray, m_StaticObjectsRef[i].Box, dist)) {
|
||||
minDist = std::min(dist, minDist);
|
||||
intersected = true;
|
||||
}
|
||||
m_StaticObjectsRef[i].Checked = true;
|
||||
}
|
||||
for (int i : m_DynamicObjIndices) {
|
||||
float dist;
|
||||
//If we haven't tested against this object before, and the ray hits.
|
||||
if (!m_DynamicObjectsRef[i].Checked &&
|
||||
Collision::RayVsAABB(ray, m_DynamicObjectsRef[i].Box, dist)) {
|
||||
minDist = std::min(dist, minDist);
|
||||
intersected = true;
|
||||
}
|
||||
m_DynamicObjectsRef[i].Checked = true;
|
||||
}
|
||||
|
||||
data.CollideDistance = minDist;
|
||||
return intersected;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void OctTree::OctChild::AddDynamicObject(const AABB& box)
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
m_Children[i]->AddDynamicObject(box);
|
||||
}
|
||||
} else {
|
||||
//Since it hasn't been added yet to the real object list, the index is after the last =size.
|
||||
m_DynamicObjIndices.push_back((int)m_DynamicObjectsRef.size());
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::AddStaticObject(const AABB& box)
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
m_Children[i]->AddStaticObject(box);
|
||||
}
|
||||
} else {
|
||||
//Since it hasn't been added yet to the real object list, the index is after the last =size.
|
||||
m_StaticObjIndices.push_back((int)m_StaticObjectsRef.size());
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes) const
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
m_Children[i]->BoxesInSameRegion(box, outBoxes);
|
||||
}
|
||||
} else {
|
||||
size_t startIndex = outBoxes.size();
|
||||
int numDuplicates = 0;
|
||||
outBoxes.resize(outBoxes.size() + m_StaticObjIndices.size() + m_DynamicObjIndices.size());
|
||||
for (size_t i = 0; i < m_StaticObjIndices.size(); ++i){
|
||||
ContainedObject& obj = m_StaticObjectsRef[m_StaticObjIndices[i]];
|
||||
if (obj.Checked) {
|
||||
++numDuplicates;
|
||||
} else {
|
||||
obj.Checked = true;
|
||||
outBoxes[startIndex + i - numDuplicates] = obj.Box;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < m_DynamicObjIndices.size(); ++i) {
|
||||
ContainedObject& obj = m_DynamicObjectsRef[m_DynamicObjIndices[i]];
|
||||
if (obj.Checked) {
|
||||
++numDuplicates;
|
||||
} else {
|
||||
obj.Checked = true;
|
||||
outBoxes[startIndex + i - numDuplicates] = obj.Box;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < numDuplicates; ++i) {
|
||||
outBoxes.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::ClearObjects()
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (OctChild*& c : m_Children) {
|
||||
c->ClearObjects();
|
||||
}
|
||||
} else {
|
||||
m_DynamicObjIndices.clear();
|
||||
m_StaticObjIndices.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::OctChild::ClearDynamicObjects()
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (OctChild*& c : m_Children) {
|
||||
c->ClearObjects();
|
||||
}
|
||||
} else {
|
||||
m_DynamicObjIndices.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//: 3 7
|
||||
//:
|
||||
//: 2 6
|
||||
//: |
|
||||
//: 1 5 \ y
|
||||
//: z
|
||||
//: 0 4 0 x-->
|
||||
//
|
||||
// child: 0 1 2 3 4 5 6 7
|
||||
// x : - - - - + + + +
|
||||
// y : - - + + - - + +
|
||||
// z : - + - + - + - +
|
||||
int OctTree::OctChild::childIndexContainingPoint(const glm::vec3& point) const
|
||||
{
|
||||
const glm::vec3& c = m_Box.Center();
|
||||
return (1 << 2) * (point.x >= c.x) | (1 << 1) * (point.y >= c.y) | (point.z >= c.z);
|
||||
}
|
||||
|
||||
std::vector<int> OctTree::OctChild::childIndicesContainingBox(const AABB& box) const
|
||||
{
|
||||
int minInd = childIndexContainingPoint(box.MinCorner());
|
||||
int maxInd = childIndexContainingPoint(box.MaxCorner());
|
||||
//Because of the predictable ordering of the child indices,
|
||||
//the number of bits set when xor:ing the indices will determine the number of children containing the box.
|
||||
std::bitset<3> bits(minInd ^ maxInd);
|
||||
switch (bits.count()) {
|
||||
//Box contained completely in one child.
|
||||
case 0:
|
||||
return{ minInd };
|
||||
//Two children.
|
||||
case 1:
|
||||
return{ minInd, maxInd };
|
||||
//Four children.
|
||||
case 2:
|
||||
{
|
||||
std::vector<int> ret;
|
||||
//Bit-hax to calculate the correct 4 children containing the box.
|
||||
//This works because of the childrens index determine what part of
|
||||
//the dimensions they are responsible for (which octant).
|
||||
bits.flip();
|
||||
//At this point the bits necessarily have exactly one bit set.
|
||||
for (int c = 0; c < 8; ++c) {
|
||||
//If the child index have the same bit set as the bits, add box to it.
|
||||
if (bits.to_ulong() & c) {
|
||||
ret.push_back(c);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case 3: //Eight children.
|
||||
return{ 0,1,2,3,4,5,6,7 };
|
||||
default:
|
||||
return std::vector<int>();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool OctTree::OctChild::hasChildren() const
|
||||
{
|
||||
return m_Children[0] != nullptr;
|
||||
}
|
||||
@@ -1,11 +1,334 @@
|
||||
#include "Network\Client.h"
|
||||
#include "Network/Client.h"
|
||||
|
||||
Client::Client()
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
|
||||
Client::Client(ConfigFile* config) : m_Socket(m_IOService)
|
||||
{
|
||||
|
||||
// Default is local host
|
||||
std::string address = config->Get<std::string>("Networking.Address", "127.0.0.1");
|
||||
int port = config->Get<int>("Networking.Port", 13);
|
||||
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
|
||||
// Set up network stream
|
||||
m_PlayerName = config->Get<std::string>("Networking.Name", "Raptorcopter");
|
||||
m_NextSnapshot.InputForward = "";
|
||||
m_NextSnapshot.InputRight = "";
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Client::Start(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
m_WasStarted = true;
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
|
||||
// Subscribe to events
|
||||
m_EInputCommand = decltype(m_EInputCommand)(std::bind(&Client::OnInputCommand, this, std::placeholders::_1));
|
||||
m_EventBroker->Subscribe(m_EInputCommand);
|
||||
|
||||
|
||||
//while (m_PlayerName.size() > 7) {
|
||||
// LOG_INFO("Please enter your name (No longer than 7 characters):");
|
||||
// std::cin >> m_PlayerName;
|
||||
//}
|
||||
m_Socket.connect(m_ReceiverEndpoint);
|
||||
LOG_INFO("I am client. BIP BOP");
|
||||
}
|
||||
|
||||
void Client::Update()
|
||||
{
|
||||
readFromServer();
|
||||
}
|
||||
|
||||
void Client::Close()
|
||||
{
|
||||
if (m_WasStarted) {
|
||||
disconnect();
|
||||
m_ThreadIsRunning = false;
|
||||
m_EventBroker->Unsubscribe(m_EInputCommand);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::readFromServer()
|
||||
{
|
||||
if (m_Socket.available()) {
|
||||
bytesRead = receive(readBuf, INPUTSIZE);
|
||||
if (bytesRead > 0) {
|
||||
Packet packet(readBuf, bytesRead);
|
||||
parseMessageType(packet);
|
||||
}
|
||||
}
|
||||
std::clock_t currentTime = std::clock();
|
||||
if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
if (isConnected()) {
|
||||
sendSnapshotToServer();
|
||||
}
|
||||
previousSnapshotMessage = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::sendSnapshotToServer()
|
||||
{
|
||||
// Reset previouse key state in snapshot.
|
||||
m_NextSnapshot.InputForward = "";
|
||||
m_NextSnapshot.InputRight = "";
|
||||
|
||||
auto player = m_World->GetComponent(m_PlayerDefinitions[m_PlayerID].EntityID, "Player");
|
||||
|
||||
|
||||
// See if any movement keys are down
|
||||
// We dont care if it's overwritten by later
|
||||
// if statement. Watcha gonna do, right!
|
||||
if (player["Forward"]) {
|
||||
m_NextSnapshot.InputForward = "+Forward";
|
||||
}
|
||||
if (player["Left"]) {
|
||||
m_NextSnapshot.InputRight = "-Right";
|
||||
}
|
||||
if (player["Back"]) {
|
||||
m_NextSnapshot.InputForward = "-Forward";
|
||||
}
|
||||
if (player["Right"]) {
|
||||
m_NextSnapshot.InputRight = "+Right";
|
||||
}
|
||||
|
||||
if (m_NextSnapshot.InputForward != "") {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString(m_NextSnapshot.InputForward);
|
||||
send(packet);
|
||||
} else {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString("0Forward");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
if (m_NextSnapshot.InputRight != "") {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString(m_NextSnapshot.InputRight);
|
||||
send(packet);
|
||||
} else {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString("0Right");
|
||||
send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseMessageType(Packet& packet)
|
||||
{
|
||||
int messageType = packet.ReadPrimitive<int>();
|
||||
if (messageType == -1)
|
||||
return;
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
//IdentifyPacketLoss();
|
||||
|
||||
switch (static_cast<MessageType>(messageType)) {
|
||||
case MessageType::Connect:
|
||||
parseConnect(packet);
|
||||
break;
|
||||
case MessageType::ClientPing:
|
||||
parsePing();
|
||||
break;
|
||||
case MessageType::ServerPing:
|
||||
parseServerPing();
|
||||
break;
|
||||
case MessageType::Message:
|
||||
break;
|
||||
case MessageType::Snapshot:
|
||||
parseSnapshot(packet);
|
||||
break;
|
||||
case MessageType::Disconnect:
|
||||
break;
|
||||
case MessageType::Event:
|
||||
parseEventMessage(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseConnect(Packet& packet)
|
||||
{
|
||||
m_PlayerID = packet.ReadPrimitive<int>();
|
||||
LOG_INFO("%i: I am player: %i", m_PacketID, m_PlayerID);
|
||||
}
|
||||
|
||||
void Client::parsePing()
|
||||
{
|
||||
m_DurationOfPingTime = 1000 * (std::clock() - m_StartPingTime) / static_cast<double>(CLOCKS_PER_SEC);
|
||||
LOG_INFO("%i: response time with ctime(ms): %f", m_PacketID, m_DurationOfPingTime);
|
||||
}
|
||||
|
||||
void Client::parseServerPing()
|
||||
{
|
||||
Packet packet(MessageType::ServerPing, m_SendPacketID);
|
||||
packet.WriteString("Ping recieved");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::parseEventMessage(Packet& packet)
|
||||
{
|
||||
int Id = -1;
|
||||
std::string command = packet.ReadString();
|
||||
if (command.find("+Player") != std::string::npos) {
|
||||
Id = packet.ReadPrimitive<int>();
|
||||
// Sett Player name
|
||||
m_PlayerDefinitions[Id].Name = command.erase(0, 7);
|
||||
} else {
|
||||
LOG_INFO("%i: Event message: %s", m_PacketID, command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseSnapshot(Packet& packet)
|
||||
{
|
||||
std::string tempName;
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
// We're checking for empty name for now. This might not be the best way,
|
||||
// but it is to avoid sending redundant data.
|
||||
tempName = packet.ReadString();
|
||||
|
||||
|
||||
// Apply the position data read to the player entity
|
||||
// New player connected on the server side
|
||||
if (m_PlayerDefinitions[i].Name == "" && tempName != "") {
|
||||
m_PlayerDefinitions[i].Name = tempName;
|
||||
m_PlayerDefinitions[i].EntityID = createPlayer();
|
||||
} else if (m_PlayerDefinitions[i].Name != "" && tempName == "") {
|
||||
// Someone disconnected
|
||||
// TODO: Insert code here
|
||||
break;
|
||||
} else if (m_PlayerDefinitions[i].Name == "" && tempName == "") {
|
||||
// Not a connected player
|
||||
break;
|
||||
}
|
||||
if (m_PlayerDefinitions[i].EntityID != -1) {
|
||||
|
||||
// Move player to server position
|
||||
int dataSize = m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform").Info.Meta.Stride;
|
||||
memcpy(m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform").Data, packet.ReadData(dataSize), dataSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Client::receive(char* data, size_t length)
|
||||
{
|
||||
boost::system::error_code error;
|
||||
|
||||
int bytesReceived = m_Socket.receive_from(boost
|
||||
::asio::buffer((void*)data, length),
|
||||
m_ReceiverEndpoint,
|
||||
0, error);
|
||||
|
||||
if (error) {
|
||||
//LOG_ERROR("receive: %s", error.message().c_str());
|
||||
}
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
void Client::send(Packet& packet)
|
||||
{
|
||||
m_Socket.send_to(boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint, 0);
|
||||
}
|
||||
|
||||
void Client::connect()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString(m_PlayerName);
|
||||
m_StartPingTime = std::clock();
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::disconnect()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString("+Disconnect");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::ping()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString("Ping");
|
||||
m_StartPingTime = std::clock();
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::moveMessageHead(char*& data, size_t& length, size_t stepSize)
|
||||
{
|
||||
data += stepSize;
|
||||
length -= stepSize;
|
||||
}
|
||||
|
||||
bool Client::OnInputCommand(const Events::InputCommand & e)
|
||||
{
|
||||
if (isConnected()) {
|
||||
ComponentWrapper& player = m_World->GetComponent(m_PlayerDefinitions[m_PlayerID].EntityID, "Player");
|
||||
if (e.Command == "Forward") {
|
||||
if (e.Value > 0) {
|
||||
(bool&)player["Forward"] = true;
|
||||
(bool&)player["Back"] = false;
|
||||
} else if (e.Value < 0) {
|
||||
(bool&)player["Back"] = true;
|
||||
(bool&)player["Forward"] = false;
|
||||
} else {
|
||||
(bool&)player["Forward"] = false;
|
||||
(bool&)player["Back"] = false;
|
||||
}
|
||||
}
|
||||
if (e.Command == "Right") {
|
||||
if (e.Value > 0) {
|
||||
(bool&)player["Right"] = true;
|
||||
(bool&)player["Left"] = false;
|
||||
} else if (e.Value < 0) {
|
||||
(bool&)player["Left"] = true;
|
||||
(bool&)player["Right"] = false;
|
||||
} else {
|
||||
(bool&)player["Left"] = false;
|
||||
(bool&)player["Right"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.Command == "ConnectToServer") { // Connect for now
|
||||
connect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Client::identifyPacketLoss()
|
||||
{
|
||||
// if no packets lost, difference should be equal to 1
|
||||
int difference = m_PacketID - m_PreviousPacketID;
|
||||
if (difference != 1) {
|
||||
LOG_INFO("%i Packet(s) were lost...", difference);
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::isConnected()
|
||||
{
|
||||
if (m_PlayerID != -1) {
|
||||
if (m_PlayerDefinitions[m_PlayerID].EntityID != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityID Client::createPlayer()
|
||||
{
|
||||
EntityID entityID = m_World->CreateEntity();
|
||||
ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform");
|
||||
ComponentWrapper model = m_World->AttachComponent(entityID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitSphere.obj";
|
||||
ComponentWrapper player = m_World->AttachComponent(entityID, "Player");
|
||||
return entityID;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "Network/Packet.h"
|
||||
|
||||
Packet::Packet(MessageType type, unsigned int& packetID)
|
||||
{
|
||||
m_Data = new char[m_MaxPacketSize];
|
||||
// Create message header
|
||||
// Add message type
|
||||
int messageType = static_cast<int>(type);
|
||||
Packet::WritePrimitive<int>(messageType);
|
||||
packetID = packetID % 1000; // Packet id modulos
|
||||
Packet::WritePrimitive<int>(packetID);
|
||||
packetID++;
|
||||
}
|
||||
|
||||
// Create message
|
||||
Packet::Packet(char* data, const int sizeOfPacket)
|
||||
{
|
||||
// Resize message
|
||||
m_MaxPacketSize = sizeOfPacket;
|
||||
// Copy data newly allocated memory
|
||||
m_Data = new char[sizeOfPacket];
|
||||
memcpy(m_Data, data, sizeOfPacket);
|
||||
m_Offset = sizeOfPacket;
|
||||
}
|
||||
|
||||
Packet::~Packet()
|
||||
{
|
||||
delete[] m_Data;
|
||||
}
|
||||
|
||||
void Packet::WriteString(std::string str)
|
||||
{
|
||||
// Message, add one extra byte for null terminator
|
||||
int sizeOfString = str.size() + 1;
|
||||
if (m_Offset + sizeOfString > m_MaxPacketSize) {
|
||||
LOG_WARNING("Package::WriteString(): Data size in packet exceeded maximum package size.\n");
|
||||
}
|
||||
memcpy(m_Data + m_Offset, str.data(), sizeOfString * sizeof(char));
|
||||
m_Offset += sizeOfString * sizeof(char);
|
||||
}
|
||||
|
||||
void Packet::WriteData(char * data, int sizeOfData)
|
||||
{
|
||||
if (m_Offset + sizeOfData > m_MaxPacketSize) {
|
||||
LOG_WARNING("Packet::WriteData(): Data size in packet exceeded maximum packet size.\n");
|
||||
}
|
||||
memcpy(m_Data + m_Offset, data, sizeOfData);
|
||||
m_Offset += sizeOfData;
|
||||
}
|
||||
|
||||
std::string Packet::ReadString()
|
||||
{
|
||||
std::string returnValue(m_Data + m_ReturnDataOffset);
|
||||
if (m_Offset < m_ReturnDataOffset + returnValue.size()) {
|
||||
LOG_WARNING("packet ReadString(): Oh no! You are trying to remove things outside my memory kingdom");
|
||||
return "PopFrontString Failed";
|
||||
}
|
||||
// +1 for null terminator.
|
||||
m_ReturnDataOffset += returnValue.size() + 1;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
char * Packet::ReadData(int SizeOfData)
|
||||
{
|
||||
if (m_Offset < m_ReturnDataOffset + SizeOfData) {
|
||||
LOG_WARNING("packet ReadData(): Oh no! You are trying to remove things outside my memory kingdom");
|
||||
return nullptr;
|
||||
}
|
||||
unsigned int oldReturnDataOffset = m_ReturnDataOffset;
|
||||
m_ReturnDataOffset += SizeOfData;
|
||||
return (m_Data + oldReturnDataOffset);
|
||||
}
|
||||
@@ -1,11 +1,355 @@
|
||||
#include "Network\Server.h"
|
||||
#include "Network/Server.h"
|
||||
|
||||
Server::Server()
|
||||
{
|
||||
|
||||
}
|
||||
Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 13))
|
||||
{ }
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
{ }
|
||||
|
||||
|
||||
void Server::Start(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
m_World = world;
|
||||
m_EventBroker = eventBroker;
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
m_StopTimes[i] = std::clock();
|
||||
}
|
||||
LOG_INFO("I am Server. BIP BOP\n");
|
||||
}
|
||||
|
||||
void Server::Update()
|
||||
{
|
||||
readFromClients();
|
||||
}
|
||||
|
||||
void Server::Close()
|
||||
{
|
||||
m_ThreadIsRunning = false;
|
||||
}
|
||||
|
||||
void Server::readFromClients()
|
||||
{
|
||||
// m_ThreadIsRunning might be unnecessary but the
|
||||
// program crashed if it executed m_Socket.available()
|
||||
// when closing the program.
|
||||
|
||||
if (m_Socket.available()) {
|
||||
try {
|
||||
bytesRead = receive(readBuffer, INPUTSIZE);
|
||||
Packet packet(readBuffer, bytesRead);
|
||||
parseMessageType(packet);
|
||||
} catch (const std::exception& err) {
|
||||
//LOG_ERROR("%i: Read from client crashed %s", m_PacketID, err.what());
|
||||
}
|
||||
|
||||
}
|
||||
std::clock_t currentTime = std::clock();
|
||||
// Send snapshot
|
||||
if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
sendSnapshot();
|
||||
previousSnapshotMessage = currentTime;
|
||||
}
|
||||
|
||||
// Send pings each
|
||||
if (intervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
sendPing();
|
||||
previousePingMessage = currentTime;
|
||||
}
|
||||
|
||||
// Time out logic
|
||||
if (checkTimeOutInterval < (1000 * (currentTime - timOutTimer) / (double)CLOCKS_PER_SEC)) {
|
||||
checkForTimeOuts();
|
||||
timOutTimer = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseMessageType(Packet& packet)
|
||||
{
|
||||
int messageType = packet.ReadPrimitive<int>(); // Read what type off message was sent from server
|
||||
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
//IdentifyPacketLoss();
|
||||
switch (static_cast<MessageType>(messageType)) {
|
||||
case MessageType::Connect:
|
||||
parseConnect(packet);
|
||||
break;
|
||||
case MessageType::ClientPing:
|
||||
//parseClientPing();
|
||||
break;
|
||||
case MessageType::ServerPing:
|
||||
parseServerPing();
|
||||
break;
|
||||
case MessageType::Message:
|
||||
break;
|
||||
case MessageType::Snapshot:
|
||||
parseSnapshot(packet);
|
||||
break;
|
||||
case MessageType::Disconnect:
|
||||
parseDisconnect();
|
||||
break;
|
||||
case MessageType::Event:
|
||||
parseEvent(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int Server::receive(char * data, size_t length)
|
||||
{
|
||||
length = m_Socket.receive_from(
|
||||
boost::asio::buffer((void*)data
|
||||
, length)
|
||||
, m_ReceiverEndpoint, 0);
|
||||
return length;
|
||||
}
|
||||
|
||||
void Server::send(Packet& packet, int playerID)
|
||||
{
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer(packet.Data(), packet.Size()),
|
||||
m_PlayerDefinitions[playerID].Endpoint,
|
||||
0);
|
||||
}
|
||||
|
||||
void Server::send(Packet & packet)
|
||||
{
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint,
|
||||
0);
|
||||
}
|
||||
|
||||
void Server::moveMessageHead(char *& data, size_t & length, size_t stepSize)
|
||||
{
|
||||
data += stepSize;
|
||||
length -= stepSize;
|
||||
}
|
||||
|
||||
void Server::broadcast(std::string message)
|
||||
{
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString(message);
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
send(packet, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::broadcast(Packet& packet)
|
||||
{
|
||||
for (int i = 0; i < MAXCONNECTIONS; ++i) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
send(packet, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendSnapshot()
|
||||
{
|
||||
Packet packet(MessageType::Snapshot, m_SendPacketID);
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
|
||||
// Send an empty name if there is no player connected on this position.
|
||||
packet.WriteString(m_PlayerDefinitions[i].Name);
|
||||
|
||||
if (m_PlayerDefinitions[i].EntityID == -1) {
|
||||
continue;
|
||||
}
|
||||
// Pack transfrom component into data packet
|
||||
auto transform = m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform");
|
||||
packet.WriteData(transform.Data, transform.Info.Meta.Stride);
|
||||
}
|
||||
broadcast(packet);
|
||||
}
|
||||
|
||||
void Server::sendPing()
|
||||
{
|
||||
// Prints connected players ping
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
int ping = 1000 * (m_StopTimes[i] - m_StartPingTime) / static_cast<double>(CLOCKS_PER_SEC);
|
||||
LOG_INFO("%i: Player %i's ping: %i", m_PacketID, i, ping);
|
||||
}
|
||||
}
|
||||
|
||||
// Create ping message
|
||||
Packet packet(MessageType::ServerPing, m_SendPacketID);
|
||||
packet.WriteString("Ping from server");
|
||||
// Time message
|
||||
m_StartPingTime = std::clock();
|
||||
// Send message
|
||||
broadcast(packet);
|
||||
}
|
||||
|
||||
void Server::checkForTimeOuts()
|
||||
{
|
||||
int timeOutTimeMs = 5000;
|
||||
int startPing = 1000 * m_StartPingTime
|
||||
/ static_cast<double>(CLOCKS_PER_SEC);
|
||||
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
int stopPing = 1000 * m_StopTimes[i]
|
||||
/ static_cast<double>(CLOCKS_PER_SEC);
|
||||
if (startPing > stopPing + timeOutTimeMs) {
|
||||
LOG_INFO("Player %i timed out!", i);
|
||||
disconnect(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::disconnect(int i)
|
||||
{
|
||||
broadcast("A player disconnected");
|
||||
LOG_INFO("Player %i disconnected/timed out", i);
|
||||
|
||||
// Remove enteties and stuff
|
||||
m_PlayerDefinitions[i].Endpoint = boost::asio::ip::udp::endpoint();
|
||||
m_PlayerDefinitions[i].EntityID = -1;
|
||||
m_PlayerDefinitions[i].Name = "";
|
||||
}
|
||||
|
||||
void Server::parseEvent(Packet& packet)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If no player matches the address return.
|
||||
if (i >= 8)
|
||||
return;
|
||||
|
||||
unsigned int entityId = m_PlayerDefinitions[i].EntityID;
|
||||
std::string eventString = packet.ReadString();
|
||||
if ("+Forward" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Forward"] = true;
|
||||
m_World->GetComponent(entityId, "Player")["Back"] = false;
|
||||
} else if ("-Forward" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Forward"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Back"] = true;
|
||||
} else if ("0Forward" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Forward"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Back"] = false;
|
||||
}
|
||||
if ("+Right" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Left"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Right"] = true;
|
||||
} else if ("-Right" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Right"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Left"] = true;
|
||||
} else if ("0Right" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Right"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Left"] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseConnect(Packet& packet)
|
||||
{
|
||||
LOG_INFO("Parsing connections");
|
||||
// Check if player is already connected
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == boost::asio::ip::address()) {
|
||||
// Create new player
|
||||
m_PlayerDefinitions[i].EntityID = createPlayer();
|
||||
m_PlayerDefinitions[i].Endpoint = m_ReceiverEndpoint;
|
||||
m_PlayerDefinitions[i].Name = packet.ReadString();
|
||||
|
||||
m_StopTimes[i] = std::clock();
|
||||
|
||||
LOG_INFO("Player \"%s\" connected on IP: %s", m_PlayerDefinitions[i].Name, m_PlayerDefinitions[i].Endpoint.address().to_string());
|
||||
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WritePrimitive<int>(i); // Player ID
|
||||
|
||||
send(packet, i);
|
||||
|
||||
// Send notification that a player has connected
|
||||
std::string str = m_PacketID + "Player " + m_PlayerDefinitions[i].Name + " connected on: "
|
||||
+ m_PlayerDefinitions[i].Endpoint.address().to_string();
|
||||
broadcast(str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseDisconnect()
|
||||
{
|
||||
LOG_INFO("%i: Parsing disconnect", m_PacketID);
|
||||
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
disconnect(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseClientPing()
|
||||
{
|
||||
LOG_INFO("%i: Parsing ping", m_PacketID);
|
||||
// Return ping
|
||||
Packet packet(MessageType::ClientPing, m_SendPacketID);
|
||||
packet.WriteString("Ping received");
|
||||
send(packet); // This dosen't work for multiple users
|
||||
}
|
||||
|
||||
void Server::parseServerPing()
|
||||
{
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
m_StopTimes[i] = std::clock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOT USED
|
||||
void Server::parseSnapshot(Packet& packet)
|
||||
{
|
||||
// Does no logic. Returns snapshot if client request one
|
||||
// The snapshot is not a real snapshot tho...
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer("I'm sending a snapshot to you guys!"),
|
||||
m_PlayerDefinitions[i].Endpoint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::identifyPacketLoss()
|
||||
{
|
||||
// if no packets lost, difference should be equal to 1
|
||||
int difference = m_PacketID - m_PreviousPacketID;
|
||||
if (difference != 1) {
|
||||
LOG_INFO("%i Packet(s) were lost...", difference);
|
||||
}
|
||||
}
|
||||
|
||||
EntityID Server::createPlayer()
|
||||
{
|
||||
EntityID entityID = m_World->CreateEntity();
|
||||
ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform");
|
||||
transform["Position"] = glm::vec3(-1.5f, 0.f, 0.f);
|
||||
ComponentWrapper model = m_World->AttachComponent(entityID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitSphere.obj";
|
||||
model["Color"] = glm::vec4(rand()%255 / 255.f, rand()%255 / 255.f, rand() %255 / 255.f, 1.f);
|
||||
ComponentWrapper player = m_World->AttachComponent(entityID, "Player");
|
||||
return entityID;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "Rendering/Renderer.h"
|
||||
#include "Rendering/DebugCameraInputController.h"
|
||||
|
||||
void Renderer::Initialize()
|
||||
{
|
||||
@@ -10,6 +9,7 @@ void Renderer::Initialize()
|
||||
if (m_Camera == nullptr) {
|
||||
m_Camera = m_DefaultCamera;
|
||||
}
|
||||
m_DebugCameraInputController = std::make_shared<DebugCameraInputController<Renderer>>(m_EventBroker, -1);
|
||||
TEMPCreateLights();
|
||||
InitializeRenderPasses();
|
||||
|
||||
@@ -89,8 +89,6 @@ void Renderer::InitializeShaders()
|
||||
|
||||
void Renderer::InputUpdate(double dt)
|
||||
{
|
||||
static DebugCameraInputController<Renderer> firstPersonInputController(m_EventBroker, -1);
|
||||
|
||||
glm::vec3 m_Position = m_Camera->Position();
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_O) == GLFW_PRESS)
|
||||
{
|
||||
@@ -120,9 +118,9 @@ void Renderer::InputUpdate(double dt)
|
||||
m_CameraMoveSpeed = 0.5f;
|
||||
}
|
||||
|
||||
firstPersonInputController.Update(dt);
|
||||
m_Camera->SetOrientation(firstPersonInputController.Orientation());
|
||||
m_Camera->SetPosition(firstPersonInputController.Position());
|
||||
m_DebugCameraInputController->Update(dt);
|
||||
m_Camera->SetOrientation(m_DebugCameraInputController->Orientation());
|
||||
m_Camera->SetPosition(m_DebugCameraInputController->Position());
|
||||
}
|
||||
|
||||
void Renderer::Update(double dt)
|
||||
|
||||
@@ -19,6 +19,7 @@ file(GLOB SOURCE_FILES
|
||||
set(SOURCE_FILES
|
||||
${SOURCE_FILES}
|
||||
"Game.cpp"
|
||||
"HealthSystem.cpp"
|
||||
"PlayerSystem.cpp"
|
||||
)
|
||||
|
||||
|
||||
+53
-28
@@ -1,4 +1,7 @@
|
||||
#include "Game.h"
|
||||
#include "Collision/TriggerSystem.h"
|
||||
#include "Collision/CollisionSystem.h"
|
||||
#include "Game/HealthSystem.h"
|
||||
|
||||
Game::Game(int argc, char* argv[])
|
||||
{
|
||||
@@ -20,7 +23,7 @@ Game::Game(int argc, char* argv[])
|
||||
m_Renderer = new Renderer(m_EventBroker);
|
||||
m_Renderer->SetFullscreen(m_Config->Get<bool>("Video.Fullscreen", false));
|
||||
m_Renderer->SetVSYNC(m_Config->Get<bool>("Video.VSYNC", false));
|
||||
m_Renderer->SetResolution(Rectangle(
|
||||
m_Renderer->SetResolution(Rectangle::Rectangle(
|
||||
0,
|
||||
0,
|
||||
m_Config->Get<int>("Video.Width", 1280),
|
||||
@@ -50,18 +53,36 @@ Game::Game(int argc, char* argv[])
|
||||
|
||||
// Create system pipeline
|
||||
m_SystemPipeline = new SystemPipeline(m_EventBroker);
|
||||
m_SystemPipeline->AddSystem<RaptorCopterSystem>();
|
||||
m_SystemPipeline->AddSystem<PlayerSystem>();
|
||||
m_SystemPipeline->AddSystem<EditorSystem>(m_Renderer);
|
||||
|
||||
//All systems with orderlevel 0 will be updated first.
|
||||
unsigned int updateOrderLevel = 0;
|
||||
m_SystemPipeline->AddSystem<RaptorCopterSystem>(updateOrderLevel);
|
||||
m_SystemPipeline->AddSystem<PlayerSystem>(updateOrderLevel);
|
||||
m_SystemPipeline->AddSystem<EditorSystem>(updateOrderLevel, m_Renderer);
|
||||
m_SystemPipeline->AddSystem<HealthSystem>(updateOrderLevel);
|
||||
|
||||
//Collision and TriggerSystem should update after player.
|
||||
++updateOrderLevel;
|
||||
m_SystemPipeline->AddSystem<CollisionSystem>(updateOrderLevel);
|
||||
m_SystemPipeline->AddSystem<TriggerSystem>(updateOrderLevel);
|
||||
|
||||
// Invoke network
|
||||
if (m_Config->Get<bool>("Networking.StartNetwork", false)) {
|
||||
//boost::thread workerThread(&Game::networkFunction, this);
|
||||
networkFunction();
|
||||
}
|
||||
m_LastTime = glfwGetTime();
|
||||
|
||||
debugInitialize();
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete m_SystemPipeline;
|
||||
delete m_World;
|
||||
delete m_FrameStack;
|
||||
delete m_InputProxy;
|
||||
delete m_InputManager;
|
||||
delete m_Renderer;
|
||||
delete m_RenderQueueFactory;
|
||||
delete m_EventBroker;
|
||||
}
|
||||
|
||||
@@ -83,10 +104,15 @@ void Game::Tick()
|
||||
m_InputProxy->Process();
|
||||
m_EventBroker->Swap();
|
||||
|
||||
// Update network
|
||||
if (m_IsClientOrServer) {
|
||||
m_ClientOrServer->Update();
|
||||
}
|
||||
|
||||
// Iterate through systems and update world!
|
||||
m_SystemPipeline->Update(m_World, dt);
|
||||
debugTick(dt);
|
||||
m_Renderer->Update(dt);
|
||||
m_EventBroker->Process<Client>();
|
||||
|
||||
m_RenderQueueFactory->Update(m_World);
|
||||
GLERROR("Game::Tick m_RenderQueueFactory->Update");
|
||||
@@ -96,28 +122,27 @@ void Game::Tick()
|
||||
m_EventBroker->Clear();
|
||||
}
|
||||
|
||||
|
||||
bool Game::debugOnInputCommand(const Events::InputCommand& e)
|
||||
{
|
||||
if (e.Command == "DebugReload" && e.Value == 1) {
|
||||
std::string mapToLoad = m_Config->Get<std::string>("Debug.LoadMap", "");
|
||||
if (!mapToLoad.empty()) {
|
||||
delete m_World;
|
||||
m_World = new World();
|
||||
ResourceManager::Release("EntityXMLFile", mapToLoad);
|
||||
ResourceManager::Load<EntityXMLFile>(mapToLoad)->PopulateWorld(m_World);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Game::debugInitialize()
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Game::debugOnInputCommand);
|
||||
}
|
||||
|
||||
void Game::debugTick(double dt)
|
||||
{
|
||||
m_EventBroker->Process<Game>();
|
||||
}
|
||||
|
||||
void Game::networkFunction()
|
||||
{
|
||||
bool isServer = m_Config->Get<bool>("Networking.IsServer", false);
|
||||
if (!isServer) {
|
||||
m_IsClientOrServer = true;
|
||||
m_ClientOrServer = new Client(m_Config);
|
||||
}
|
||||
if (isServer) {
|
||||
m_IsClientOrServer = true;
|
||||
m_ClientOrServer = new Server();
|
||||
}
|
||||
m_ClientOrServer->Start(m_World, m_EventBroker);
|
||||
// I don't think we are reaching this part of the code right now.
|
||||
// ~Game() is not called if the game is exited by closing console windows
|
||||
// When server or client is done set it to false.
|
||||
//m_IsClientOrServer = false;
|
||||
// Destroy it
|
||||
//delete m_ClientOrServer;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "HealthSystem.h"
|
||||
#include <algorithm>
|
||||
|
||||
HealthSystem::HealthSystem(EventBroker* eventBroker)
|
||||
: PureSystem(eventBroker, "Health")
|
||||
{
|
||||
//subscribe/listenTo playerdamage,healthpickup events (using the eventBroker)
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &HealthSystem::OnPlayerDamaged);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerHealthPickup, &HealthSystem::OnPlayerHealthPickup);
|
||||
}
|
||||
|
||||
void HealthSystem::UpdateComponent(World *world, ComponentWrapper &health, double dt)
|
||||
{
|
||||
//if entityID of health is 9 then the players ID is also 9 (player,health are connected to the same entity)
|
||||
ComponentWrapper player = world->GetComponent(health.EntityID, "Player");
|
||||
double maxHealth = (double)health["MaxHealth"];
|
||||
|
||||
//process the DeltaHealthVector and change the entitys health accordingly
|
||||
for (size_t i = m_DeltaHealthVector.size(); i > 0; i--)
|
||||
{
|
||||
auto deltaHP = m_DeltaHealthVector[i - 1];
|
||||
//if we have a healthchange for the current player, then apply it
|
||||
if (std::get<0>(deltaHP) == player.EntityID) {
|
||||
//get the deltaHP value from the tuple and make sure you dont get more than maxHealth
|
||||
double newHealth = std::min((double)health["Health"] + (double)std::get<1>(deltaHP), maxHealth);
|
||||
health["Health"] = newHealth;
|
||||
m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
//check if health is <= 0
|
||||
if ((double)health["Health"] <= 0.0f) {
|
||||
//publish death event
|
||||
Events::PlayerDeath e;
|
||||
e.PlayerID = player.EntityID;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e)
|
||||
{
|
||||
//save the changed HP to a vector. it will be taken care of in UpdateComponent
|
||||
m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerDamagedID, -e.DamageAmount));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e)
|
||||
{
|
||||
//save the changed HP to a vector. it will be taken care of in UpdateComponent
|
||||
m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerHealedID, e.HealthAmount));
|
||||
return true;
|
||||
}
|
||||
+31
-46
@@ -2,56 +2,41 @@
|
||||
|
||||
void PlayerSystem::UpdateComponent(World * world, ComponentWrapper & player, double dt)
|
||||
{
|
||||
if (input.Forward) {
|
||||
m_Direction.z = -1;
|
||||
} else if (input.Back) {
|
||||
m_Direction.z = 1;
|
||||
} else {
|
||||
m_Direction.z = 0;
|
||||
player["Velocity"] = glm::vec3(0.f, 0.f, 0.f);
|
||||
if ((bool&)player["Forward"] == true) {
|
||||
((glm::vec3&)player["Velocity"]).z = m_Speed * float(dt) * -1;
|
||||
|
||||
}
|
||||
if (input.Left) {
|
||||
m_Direction.x = -1;
|
||||
} else if (input.Right) {
|
||||
m_Direction.x = 1;
|
||||
} else {
|
||||
m_Direction.x = 0;
|
||||
if ((bool&)player["Left"] == true) {
|
||||
((glm::vec3&)player["Velocity"]).x = m_Speed * float(dt) * -1;
|
||||
}
|
||||
if ((bool&)player["Back"] == true) {
|
||||
((glm::vec3&)player["Velocity"]).z = m_Speed * float(dt);
|
||||
}
|
||||
if ((bool&)player["Right"] == true) {
|
||||
((glm::vec3&)player["Velocity"]).x = m_Speed * float(dt);
|
||||
}
|
||||
|
||||
if ((glm::vec3)player["Velocity"] != glm::vec3(0.f)) {
|
||||
ComponentWrapper& transform = world->GetComponent(player.EntityID, "Transform");
|
||||
(glm::vec3&)transform["Position"] += (glm::vec3)player["Velocity"];
|
||||
}
|
||||
m_EventBroker->Process<PlayerSystem>();
|
||||
ComponentWrapper& transform = world->GetComponent(player.EntityID, "Transform");
|
||||
(glm::vec3&)player["Velocity"] = m_Speed * float(dt) * m_Direction;
|
||||
(glm::vec3&)transform["Position"] += (glm::vec3)player["Velocity"];
|
||||
}
|
||||
|
||||
bool PlayerSystem::OnKeyDown(const Events::KeyDown & event)
|
||||
bool PlayerSystem::OnTouch(const Events::TriggerTouch &event)
|
||||
{
|
||||
if (event.KeyCode == GLFW_KEY_W) {
|
||||
input.Forward = true;
|
||||
}
|
||||
if (event.KeyCode == GLFW_KEY_A) {
|
||||
input.Left = true;
|
||||
}
|
||||
if (event.KeyCode == GLFW_KEY_S) {
|
||||
input.Back = true;
|
||||
}
|
||||
if (event.KeyCode == GLFW_KEY_D) {
|
||||
input.Right = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerSystem::OnKeyUp(const Events::KeyUp & event)
|
||||
{
|
||||
if (event.KeyCode == GLFW_KEY_W) {
|
||||
input.Forward = false;
|
||||
}
|
||||
if (event.KeyCode == GLFW_KEY_A) {
|
||||
input.Left = false;
|
||||
}
|
||||
if (event.KeyCode == GLFW_KEY_S) {
|
||||
input.Back = false;
|
||||
}
|
||||
if (event.KeyCode == GLFW_KEY_D) {
|
||||
input.Right = false;
|
||||
}
|
||||
LOG_INFO("Player entity %i touched widget (entity %i).", event.Entity, event.Trigger);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayerSystem::OnEnter(const Events::TriggerEnter &event)
|
||||
{
|
||||
LOG_INFO("Player entity %i entered widget (entity %i).", event.Entity, event.Trigger);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlayerSystem::OnLeave(const Events::TriggerLeave &event)
|
||||
{
|
||||
LOG_INFO("Player entity %i left widget (entity %i).", event.Entity, event.Trigger);
|
||||
return false;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ include_directories(
|
||||
)
|
||||
|
||||
file(GLOB SOURCE_FILES
|
||||
"*.h"
|
||||
"*.cpp"
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
//#define BOOST_TEST_MODULE collTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/execution_monitor.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
#include "Engine/Collision/Collision.h"
|
||||
#include "Engine/Core/AABB.h"
|
||||
#include "Engine/Core/Ray.h"
|
||||
#include <stdlib.h>//srand
|
||||
#include "Engine/Core/OctTree.h"
|
||||
//vs model
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
//ray vs model
|
||||
#include "Engine\Core\ResourceManager.h"
|
||||
#include "Engine\Rendering\Model.h"
|
||||
#include "Engine\Core\Ray.h"
|
||||
|
||||
//vs memleaks
|
||||
//#define _CRTDBG_MAP_ALLOC
|
||||
//#include <stdlib.h>
|
||||
//#include <crtdbg.h>
|
||||
//#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
|
||||
//#define new DEBUG_CLIENTBLOCK
|
||||
|
||||
void RayTest(std::string fileName) {
|
||||
//simple box test
|
||||
Ray ray(glm::vec3(-50, 0, 0), glm::vec3(1, 0, 0));
|
||||
//using a rawmodel here, else we have to init the renderingsystem
|
||||
ResourceManager::RegisterType<RawModel>("RawModel");
|
||||
auto unitBox = ResourceManager::Load<RawModel>(fileName);
|
||||
BOOST_REQUIRE(unitBox != nullptr);
|
||||
bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices);
|
||||
BOOST_CHECK(hit);
|
||||
ray.SetDirection(glm::vec3(-1, 0, 0));
|
||||
hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices);
|
||||
BOOST_CHECK(!hit);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(collisionTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(collisionTest)
|
||||
{
|
||||
//memleak
|
||||
int* globalLeak = new int[5];
|
||||
|
||||
//fixed seed
|
||||
srand(2);
|
||||
AABB someAABB;
|
||||
glm::vec3 minPos;
|
||||
glm::vec3 maxPos;
|
||||
bool z;
|
||||
int test = 0;
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
Ray ray(
|
||||
glm::vec3(rand() % 100, rand() % 100, rand() % 100),
|
||||
glm::vec3(rand() % 100, rand() % 100, rand() % 100)
|
||||
);
|
||||
minPos.x = rand() % 100;
|
||||
minPos.y = rand() % 100;
|
||||
minPos.z = rand() % 100;
|
||||
maxPos.x = rand() % 100;
|
||||
maxPos.y = rand() % 100;
|
||||
maxPos.z = rand() % 100;
|
||||
|
||||
someAABB = AABB(minPos, maxPos);
|
||||
z = Collision::RayVsAABB(ray, someAABB);
|
||||
if (z) ++test;
|
||||
}
|
||||
BOOST_CHECK(test >= 0);
|
||||
|
||||
//_CrtDumpMemoryLeaks();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(collisionTest2)
|
||||
{
|
||||
//fixed seed
|
||||
srand(2);
|
||||
AABB someAABB;
|
||||
glm::vec3 minPos;
|
||||
glm::vec3 maxPos;
|
||||
bool z;
|
||||
int test = 0;
|
||||
for (size_t i = 0; i < 1000000; i++)
|
||||
{
|
||||
Ray ray(
|
||||
glm::vec3(rand() % 100, rand() % 100, rand() % 100),
|
||||
glm::vec3(rand() % 100, rand() % 100, rand() % 100)
|
||||
);
|
||||
minPos.x = rand() % 100;
|
||||
minPos.y = rand() % 100;
|
||||
minPos.z = rand() % 100;
|
||||
maxPos.x = rand() % 100;
|
||||
maxPos.y = rand() % 100;
|
||||
maxPos.z = rand() % 100;
|
||||
|
||||
someAABB = AABB(minPos, maxPos);
|
||||
z = Collision::RayAABBIntr(ray, someAABB);
|
||||
if (z) ++test;
|
||||
}
|
||||
BOOST_CHECK(test >= 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rayVsModelTest)
|
||||
{
|
||||
//simple box test
|
||||
RayTest("Models/Core/UnitCube.obj");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rayVsModelTest2)
|
||||
{
|
||||
//advanced test, this will check so rayVSAABB and rayVsModel(with boxmodel) gives the same result (hit/miss)
|
||||
//testing with different seeds
|
||||
// srand(7676762);
|
||||
// srand(7676462);
|
||||
// srand(7462);
|
||||
srand(72);
|
||||
AABB someAABB;
|
||||
glm::vec3 minPos;
|
||||
glm::vec3 maxPos;
|
||||
bool z;
|
||||
int test = 0;
|
||||
//min/max is the same as the rawmodels boundaries ofcourse
|
||||
minPos = glm::vec3(-0.5f, -0.5f, -0.5f);
|
||||
maxPos = glm::vec3(0.5f, 0.5f, 0.5f);
|
||||
someAABB = AABB(minPos, maxPos);
|
||||
//using a rawmodel here, else we have to init the renderingsystem
|
||||
ResourceManager::RegisterType<RawModel>("RawModel");
|
||||
auto unitBox = ResourceManager::Load<RawModel>("Models/Core/UnitCube.obj");
|
||||
BOOST_CHECK(unitBox != nullptr);
|
||||
|
||||
for (size_t i = 0; i < 1000000; i++)
|
||||
{
|
||||
Ray ray(
|
||||
glm::vec3(-2, 0, 0),
|
||||
glm::vec3(rand() % 100, rand() % 100, rand() % 100)
|
||||
);
|
||||
//if we normalize the ray.direction when its 0,0,0 then we get nan,nan,nan - thus we have this check to prevent that
|
||||
if (glm::any(glm::isnan(ray.Direction())))
|
||||
continue;
|
||||
|
||||
z = Collision::RayVsAABB(ray, someAABB);
|
||||
if (z) {
|
||||
//hit
|
||||
bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices);
|
||||
if (!hit) {
|
||||
//if rayvsaabb hit but rayvvmodel didnt hit, we get to here
|
||||
glm::vec3 outtttttttt;
|
||||
hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt);
|
||||
hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices);
|
||||
}
|
||||
else {
|
||||
hit = hit;
|
||||
}
|
||||
BOOST_CHECK(hit);
|
||||
}
|
||||
////breakpoint test
|
||||
//if (!z) {
|
||||
// z = z;
|
||||
//}
|
||||
//
|
||||
bool hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices);
|
||||
////breakpoint test
|
||||
//if (!hit) {
|
||||
// hit = hit;
|
||||
//}
|
||||
if (hit) {
|
||||
//hit
|
||||
z = Collision::RayVsAABB(ray, someAABB);
|
||||
if (!z) {
|
||||
//if rayvsmodel hit but rayvsaabb didnt hit then we get to here
|
||||
z = Collision::RayVsAABB(ray, someAABB);
|
||||
glm::vec3 outtttttttt;
|
||||
hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices, outtttttttt);
|
||||
hit = Collision::RayVsModel(ray, unitBox->m_Vertices, unitBox->m_Indices);
|
||||
}
|
||||
else {
|
||||
z = z;
|
||||
}
|
||||
BOOST_CHECK(hit);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(rayVsModelTest3)
|
||||
{
|
||||
//simple test
|
||||
RayTest("Models/Core/UnitSphere.obj");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rayVsModelTest4)
|
||||
{
|
||||
//simple test
|
||||
RayTest("Models/Core/UnitCylinder.obj");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rayVsModelTest5)
|
||||
{
|
||||
//simple test
|
||||
RayTest("Models/Core/UnitRaptor.obj");
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(octTest)
|
||||
{
|
||||
glm::vec3 mini = glm::vec3(-1, -1, -1);
|
||||
glm::vec3 maxi = glm::vec3(1, 1, 1);
|
||||
OctTree tree(AABB(mini, maxi), 2);
|
||||
tree.AddDynamicObject(AABB(mini, -0.9f*maxi));
|
||||
OctTree::Output data;
|
||||
glm::vec3 origin = 3.0f * mini;
|
||||
bool rayIntersected = tree.RayCollides(Ray(origin , mini - origin), data);
|
||||
BOOST_CHECK(rayIntersected);
|
||||
tree.ClearDynamicObjects();
|
||||
rayIntersected = tree.RayCollides(Ray(origin, mini - origin), data);
|
||||
BOOST_CHECK(!rayIntersected);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/execution_monitor.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
#include <stdlib.h>//srand
|
||||
|
||||
//#define private public
|
||||
#include "Engine\Core\ConfigFile.h"
|
||||
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <crtdbg.h>
|
||||
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
|
||||
#define new DEBUG_CLIENTBLOCK
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(confTest)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(configFileTest)
|
||||
{
|
||||
//note: this ConfigFileclass currently has memleaks!
|
||||
|
||||
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
|
||||
auto m_Config = ResourceManager::Load<ConfigFile>("ConfigTest.ini");
|
||||
|
||||
//bägge måste vara av samma typ, T typen är string
|
||||
//http://www.boost.org/doc/libs/1_42_0/doc/html/boost_propertytree/tutorial.html
|
||||
//"Note that we construct the path to the value by separating the individual keys with dots"
|
||||
|
||||
//get from tree tests
|
||||
auto getSomething = m_Config->Get("Test.Test1", 0);
|
||||
BOOST_CHECK(getSomething == 423);
|
||||
|
||||
auto getSomething2 = m_Config->Get("fsdfdsfd.T", std::string(""));
|
||||
BOOST_CHECK(getSomething2 == "\"gfdjakflsdl!\"");
|
||||
|
||||
//set/get tests
|
||||
m_Config->Set("Test.4321", 123);
|
||||
auto getSomething3 = m_Config->Get("Test.4321", 0);
|
||||
BOOST_CHECK(getSomething3 == 123);
|
||||
|
||||
m_Config->Set("3_2_1_0_5", "t454j54hj5k32");
|
||||
auto getSomething4 = m_Config->Get("3_2_1_0_5", std::string(""));
|
||||
BOOST_CHECK(getSomething4 == "t454j54hj5k32");
|
||||
|
||||
//***check so outputwindow says: EE: Failed to find "DefaultConfigTestNotExists.ini"! Relying on hardcoded default values!
|
||||
auto m_Config2 = ResourceManager::Load<ConfigFile>("ConfigTestNotExists.ini");
|
||||
|
||||
//set value/savetodisk/load/checkvalue...
|
||||
m_Config->SaveToDisk();
|
||||
m_Config->Set("Test.4321", 145);
|
||||
m_Config->SaveToDisk();
|
||||
auto m_Config3 = ResourceManager::Load<ConfigFile>("ConfigTest.ini");
|
||||
auto getSomething5 = m_Config->Get("Test.4321", 0);
|
||||
BOOST_CHECK(getSomething5 == 145);
|
||||
|
||||
//***check so outputwindow says: EE: Failed to parse "DefaultConfigTestFailed.ini"
|
||||
//***check so outputwindow says: EE: Failed to parse "ConfigTestFailed.ini":
|
||||
auto m_Config4 = ResourceManager::Load<ConfigFile>("ConfigTestFailed.ini");
|
||||
|
||||
//reload,onchildreload unimplemented
|
||||
|
||||
//NOTE:still massive amount of memoryleaks from this method
|
||||
_CrtDumpMemoryLeaks();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef EVENTFIXTURE_H
|
||||
#define EVENTFIXTURE_H
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "Core\EventBroker.h"
|
||||
|
||||
template <typename EventType>
|
||||
struct EventFixture
|
||||
{
|
||||
EventFixture()
|
||||
{
|
||||
this->ventBroker = new EventBroker();
|
||||
m_EEventType = decltype(m_EEventType)(std::bind(&EventFixture::OnEvent, this, std::placeholders::_1));
|
||||
this->ventBroker->Subscribe(m_EEventType);
|
||||
Run();
|
||||
Check();
|
||||
}
|
||||
~EventFixture()
|
||||
{
|
||||
this->ventBroker->Unsubscribe(m_EEventType);
|
||||
delete this->ventBroker;
|
||||
}
|
||||
|
||||
EventBroker* ventBroker = nullptr;
|
||||
EventRelay<EventFixture, EventType> m_EEventType;
|
||||
bool m_EventRecieved = false;
|
||||
EventType Before;
|
||||
EventType After;
|
||||
|
||||
bool OnEvent(const EventType& event)
|
||||
{
|
||||
m_EventRecieved = true;
|
||||
After = event;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
// Publish the event
|
||||
this->ventBroker->Publish(Before);
|
||||
// Clear to swap buffers
|
||||
this->ventBroker->Swap();
|
||||
// Process the event
|
||||
this->ventBroker->template Process<EventFixture>();
|
||||
}
|
||||
|
||||
void Check()
|
||||
{
|
||||
BOOST_CHECK(m_EventRecieved);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "EventFixture.h"
|
||||
|
||||
struct ETestEvent : public Event
|
||||
{
|
||||
int Int = 5;
|
||||
float Float = 1.33333f;
|
||||
double Double = 1.33333;
|
||||
std::string String = "Hello World";
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(EventBrokerTest)
|
||||
{
|
||||
EventFixture<ETestEvent> f;
|
||||
BOOST_CHECK(f.Before.Int == f.After.Int);
|
||||
BOOST_CHECK_CLOSE(f.Before.Float, f.After.Float, 0.00001f);
|
||||
BOOST_CHECK_CLOSE(f.Before.Double, f.After.Double, 0.00001f);
|
||||
BOOST_CHECK(f.Before.String == f.After.String);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
|
||||
#include "HealthSystemTest.h"
|
||||
#include "Game/HealthSystem.h"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(HealthSystemSuite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(HealthSystemTest)
|
||||
{
|
||||
//this tests 2 healthevents and the healthsystem
|
||||
GameHealthSystemTest game;
|
||||
//100 loops will be more than enough to do the test
|
||||
int loops = 100;
|
||||
bool success = false;
|
||||
while (loops > 0) {
|
||||
game.Tick();
|
||||
if (game.TestSucceeded) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
loops--;
|
||||
}
|
||||
//The system will process the events, hence it will take a while before we can read anything
|
||||
BOOST_TEST(success);
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
GameHealthSystemTest::GameHealthSystemTest()
|
||||
{
|
||||
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
|
||||
ResourceManager::RegisterType<EntityXMLFile>("EntityXMLFile");
|
||||
|
||||
m_Config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get<int>("Debug.LogLevel", 1));
|
||||
|
||||
// Create the core event broker
|
||||
m_EventBroker = new EventBroker();
|
||||
|
||||
// Create a world
|
||||
m_World = new World();
|
||||
std::string mapToLoad = m_Config->Get<std::string>("Debug.LoadMap", "");
|
||||
if (!mapToLoad.empty()) {
|
||||
ResourceManager::Load<EntityXMLFile>(mapToLoad)->PopulateWorld(m_World);
|
||||
}
|
||||
|
||||
// Create system pipeline
|
||||
m_SystemPipeline = new SystemPipeline(m_EventBroker);
|
||||
m_SystemPipeline->AddSystem<PlayerSystem>(0);
|
||||
m_SystemPipeline->AddSystem<HealthSystem>(0);
|
||||
|
||||
//The Test
|
||||
//create entity which has transorm,player,model,health in it. i.e. is a player
|
||||
EntityID playerID = m_World->CreateEntity();
|
||||
ComponentWrapper transform = m_World->AttachComponent(playerID, "Transform");
|
||||
ComponentWrapper model = m_World->AttachComponent(playerID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitSphere.obj";
|
||||
ComponentWrapper player = m_World->AttachComponent(playerID, "Player");
|
||||
ComponentWrapper health = m_World->AttachComponent(playerID, "Health");
|
||||
healthsID = playerID;
|
||||
double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"];
|
||||
|
||||
//heal player with 40
|
||||
Events::PlayerHealthPickup e3;
|
||||
e3.HealthAmount = 40.0f;
|
||||
e3.PlayerHealedID = healthsID;
|
||||
m_EventBroker->Publish(e3);
|
||||
//damage player with 50
|
||||
Events::PlayerDamage e;
|
||||
e.DamageAmount = 50.0f;
|
||||
e.PlayerDamagedID = healthsID;
|
||||
m_EventBroker->Publish(e);
|
||||
//heal some other player with 40
|
||||
Events::PlayerHealthPickup e2;
|
||||
e2.HealthAmount = 40.0f;
|
||||
e2.PlayerHealedID = healthsID+1;
|
||||
m_EventBroker->Publish(e2);
|
||||
|
||||
EntityID playerID2 = m_World->CreateEntity();
|
||||
ComponentWrapper transform2 = m_World->AttachComponent(playerID2, "Transform");
|
||||
ComponentWrapper model2 = m_World->AttachComponent(playerID2, "Model");
|
||||
model2["Resource"] = "Models/Core/UnitSphere.obj";
|
||||
ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player");
|
||||
ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health");
|
||||
//END TEST
|
||||
}
|
||||
|
||||
GameHealthSystemTest::~GameHealthSystemTest()
|
||||
{
|
||||
delete m_SystemPipeline;
|
||||
delete m_World;
|
||||
delete m_EventBroker;
|
||||
}
|
||||
|
||||
void GameHealthSystemTest::Tick()
|
||||
{
|
||||
glfwPollEvents();
|
||||
|
||||
double currentTime = glfwGetTime();
|
||||
double dt = currentTime - m_LastTime;
|
||||
m_LastTime = currentTime;
|
||||
|
||||
// Iterate through systems and update world!
|
||||
m_SystemPipeline->Update(m_World, dt);
|
||||
|
||||
m_EventBroker->Swap();
|
||||
m_EventBroker->Clear();
|
||||
|
||||
//if health reaches 90 then we know the test has succeeded (start with 100hp, remove 50hp, add 40hp)
|
||||
double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"];
|
||||
if (currentHealth==90)
|
||||
TestSucceeded = true;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef HealthTest_h__
|
||||
#define HealthTest_h__
|
||||
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "Core/ConfigFile.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Rendering/Renderer.h"
|
||||
#include "Core/InputManager.h"
|
||||
#include "GUI/Frame.h"
|
||||
#include "Core/World.h"
|
||||
#include "Rendering/RenderQueueFactory.h"
|
||||
#include "Input/InputProxy.h"
|
||||
#include "Input/KeyboardInputHandler.h"
|
||||
#include "Input/MouseInputHandler.h"
|
||||
#include "Core/EKeyDown.h"
|
||||
#include "Core/EntityXMLFile.h"
|
||||
#include "Core/SystemPipeline.h"
|
||||
#include "RaptorCopterSystem.h"
|
||||
#include "PlayerSystem.h"
|
||||
#include "Editor/EditorSystem.h"
|
||||
|
||||
class GameHealthSystemTest
|
||||
{
|
||||
public:
|
||||
GameHealthSystemTest();
|
||||
~GameHealthSystemTest();
|
||||
|
||||
void Tick();
|
||||
bool TestSucceeded = false;
|
||||
|
||||
private:
|
||||
double m_LastTime;
|
||||
ConfigFile* m_Config = nullptr;
|
||||
EventBroker* m_EventBroker;
|
||||
World* m_World;
|
||||
SystemPipeline* m_SystemPipeline;
|
||||
int healthsID;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "Engine\Core\InputManager.h"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(inputManagerTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inputManagerTest)
|
||||
{
|
||||
//already tested eventbroker so inputManager is indirectly already tested
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -0,0 +1,476 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
#include "Engine/Core/ObjectPool.h"
|
||||
#include <ctime>
|
||||
|
||||
struct S
|
||||
{
|
||||
S() = default;
|
||||
S(int i, float ff) : k(i), f(ff) { }
|
||||
~S() { }
|
||||
int k;
|
||||
float f;
|
||||
};
|
||||
|
||||
//BOOST_GLOBAL_FIXTURE(S);
|
||||
BOOST_AUTO_TEST_SUITE(memProtoTypeTestSuite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testPool)
|
||||
{
|
||||
ObjectPool<S> pool(32);//32 true/false values = 32 slots
|
||||
BOOST_CHECK(pool.empty() == true);
|
||||
|
||||
const size_t size = 12;//12 platser i structen addresses, som håller en int, en float vardera
|
||||
S* addresses[size];
|
||||
addresses[0] = pool.New(7, 0.035f);
|
||||
//"Not empty after allocating one element."
|
||||
BOOST_CHECK(!pool.empty());
|
||||
|
||||
//"Element created correctly with k==7"
|
||||
BOOST_CHECK(addresses[0]->k == 7);
|
||||
//"Element created correctly with f==0.035f"
|
||||
BOOST_CHECK_CLOSE_FRACTION(addresses[0]->f, 0.035f, 0.0001f);
|
||||
addresses[0]->k = 5;
|
||||
BOOST_CHECK(addresses[0]->k == 5);
|
||||
|
||||
//"Empty after delete"
|
||||
pool.Delete(addresses[0]);
|
||||
BOOST_CHECK(pool.empty());
|
||||
|
||||
addresses[0] = pool.New(7, 0.035f);
|
||||
addresses[1] = pool.New(5, 0.035f);
|
||||
pool.Delete(addresses[1]);
|
||||
BOOST_CHECK(!pool.empty());
|
||||
pool.Delete(addresses[0]);
|
||||
BOOST_CHECK(pool.empty());
|
||||
|
||||
//INT32_MAX, FLT_MAX test
|
||||
addresses[0] = pool.New(INT32_MAX, FLT_MAX);
|
||||
BOOST_CHECK(!pool.empty());
|
||||
BOOST_CHECK(addresses[0]->k == INT32_MAX);
|
||||
BOOST_CHECK_CLOSE_FRACTION(addresses[0]->f, FLT_MAX, 0.0001f);
|
||||
}
|
||||
/*
|
||||
BOOST_AUTO_TEST_CASE(testPoolArray)
|
||||
{
|
||||
ObjectPool<S> pool(32);
|
||||
S* addresses;
|
||||
|
||||
//Add array size 5 to pool."
|
||||
addresses = pool.NewArray(5);// <-> addresses = new S[5];
|
||||
addresses[0] = S(12, 0.030f);
|
||||
addresses[1] = S(13, 0.031f);
|
||||
addresses[2] = S(14, 0.032f);
|
||||
addresses[3] = S(15, 0.033f);
|
||||
addresses[4] = S(16, 0.034f);
|
||||
|
||||
//"Not empty after allocating
|
||||
BOOST_CHECK(!pool.empty());
|
||||
//"Element created correctly with k==12"
|
||||
BOOST_CHECK(addresses->k == 12);
|
||||
//"Element created correctly with f==0.030f"
|
||||
BOOST_CHECK_CLOSE_FRACTION(addresses->f, 0.030f, 0.0001f);
|
||||
|
||||
//add a few other structs so it becomes bigger than the original size (32),
|
||||
//which means it must push back the rest of the values into a vector
|
||||
S* test2, *test3, *test4, *test5;
|
||||
test2 = pool.NewArray(5);// <-> test2 = new S[5];
|
||||
test3 = pool.NewArray(40);//+40
|
||||
test4 = pool.NewArray(40);//+40
|
||||
test5 = pool.NewArray(40);//+40=120
|
||||
BOOST_CHECK(pool.ExtraSize() == 120);
|
||||
BOOST_CHECK(pool.PoolSize() == 10);
|
||||
BOOST_CHECK(pool.size() == 120 + 10);
|
||||
|
||||
//testar "perfekt delete", dvs bryr mig inte om att testa att deleta bara 38 om storleken egentligen är 40 osv
|
||||
pool.DeleteArray(test2, 5);//callar destructorn på test2 också
|
||||
pool.DeleteArray(test3, 40);
|
||||
|
||||
pool.DeleteArray(addresses, 5);
|
||||
|
||||
//add / del array
|
||||
S* another = pool.NewArray(64);
|
||||
for (int i = 0; i < 64; ++i)
|
||||
another[i] = S(i, 0.1f*i);
|
||||
pool.DeleteArray(another, 64);
|
||||
}
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(testIterationNormal)
|
||||
{
|
||||
//extra vector check
|
||||
S* test4, *test5;
|
||||
ObjectPool<S> pool(4);
|
||||
test4 = pool.New();
|
||||
test5 = pool.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : pool)
|
||||
o.k = 14;
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test4[i].k == 14);
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test5[i].k == 14);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testOutOfScopeDelete)
|
||||
{
|
||||
//extra vector check
|
||||
S* test4, *test5;
|
||||
{
|
||||
ObjectPool<S> pool(4);
|
||||
test4 = pool.New();
|
||||
test5 = pool.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : pool)
|
||||
o.k = 14;
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test4[i].k == 14);
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test5[i].k == 14);
|
||||
}
|
||||
//pool goes out of scope here, and thus the test4 values become undefined (memory is killed at out of scope)
|
||||
BOOST_CHECK(test4[0].k != 14);
|
||||
BOOST_CHECK(test5[0].k != 15);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testIterationOneExtra)
|
||||
{
|
||||
//extra vector check
|
||||
ObjectPool<S> pool(1);
|
||||
S* test4, *test5;
|
||||
test4 = pool.New();
|
||||
test5 = pool.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : pool)
|
||||
o.k = 14;
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test4[i].k == 14);
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test5[i].k == 14);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testIterationTwoExtra)
|
||||
{
|
||||
//extra vector check
|
||||
ObjectPool<S> pool(1);
|
||||
S* test4, *test5;
|
||||
test4 = pool.New();
|
||||
test5 = pool.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : pool)
|
||||
o.k = 14;
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test4[i].k == 14);
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test5[i].k == 14);
|
||||
}
|
||||
/*
|
||||
BOOST_AUTO_TEST_CASE(releaseModeTest_RandomAllocateDeallocate)
|
||||
{
|
||||
//run this in releasemode
|
||||
struct I
|
||||
{
|
||||
I() = default;
|
||||
I(size_t i, size_t ff) : k(i), f(ff) { }
|
||||
~I() { }
|
||||
size_t k;
|
||||
size_t f;
|
||||
};
|
||||
srand((unsigned int)time(nullptr));
|
||||
|
||||
const size_t SIZE = 128;
|
||||
ObjectPool<I> pool(SIZE);
|
||||
I* addresses[SIZE];
|
||||
std::vector<bool> allocated(SIZE, false);
|
||||
std::vector<size_t> arrSizes(SIZE, 0);
|
||||
size_t slotsAlloced = 0;
|
||||
size_t superCount = 0;
|
||||
size_t slot;
|
||||
size_t i;
|
||||
|
||||
while (superCount++ < 1000) {
|
||||
if (rand() % 2 == 0) {
|
||||
i = 0;
|
||||
//ta slumpmässig slot som inte är allokerad
|
||||
do {
|
||||
slot = (size_t)((SIZE - 1) * ((float)rand() / RAND_MAX));
|
||||
} while (allocated[slot] && ++i < 512);
|
||||
|
||||
if (i < 512) {
|
||||
arrSizes[slot] = 1 + (size_t)((24 - 1) * ((float)rand() / RAND_MAX));
|
||||
addresses[slot] = pool.NewArray(arrSizes[slot]);
|
||||
for (size_t a = 0; a < arrSizes[slot]; ++a)
|
||||
addresses[slot][a] = I(slot, a);
|
||||
allocated[slot] = true;
|
||||
++slotsAlloced;
|
||||
}
|
||||
}
|
||||
//Deallocate
|
||||
else {
|
||||
i = 0;
|
||||
//ta slumpmässig slot som är allokerad
|
||||
do {
|
||||
slot = (size_t)((SIZE - 1) * ((float)rand() / RAND_MAX));
|
||||
} while (!allocated[slot] && ++i < 512);
|
||||
|
||||
if (i < 512) {
|
||||
pool.DeleteArray(addresses[slot], arrSizes[slot]);
|
||||
arrSizes[slot] = 0;
|
||||
allocated[slot] = false;
|
||||
--slotsAlloced;
|
||||
}
|
||||
}
|
||||
//Check content.
|
||||
for (size_t a = 0; a < SIZE; ++a) {
|
||||
if (allocated[a]) {
|
||||
for (size_t e = 0; e < arrSizes[a]; ++e) {
|
||||
BOOST_CHECK(!(addresses[a][e].k != a || addresses[a][e].f != e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testConstructors)
|
||||
{
|
||||
//http://stackoverflow.com/questions/357929/is-it-important-to-unit-test-a-constructor
|
||||
//"If your constructor has, for example, an if (condition), you need to test both flows (true,false).
|
||||
//If your constructor does some kind of job before setting. You should check the job is done"
|
||||
|
||||
//testing the constructors with different T values and a small check so size is initialized to 0
|
||||
MemoryPool<int> memPoolI;
|
||||
BOOST_CHECK(memPoolI.empty());
|
||||
BOOST_CHECK(memPoolI.size() == 0);
|
||||
MemoryPool<float> memPoolF;
|
||||
BOOST_CHECK(memPoolF.empty());
|
||||
BOOST_CHECK(memPoolF.size() == 0);
|
||||
MemoryPool<double> memPoolD;
|
||||
BOOST_CHECK(memPoolD.empty());
|
||||
BOOST_CHECK(memPoolD.size() == 0);
|
||||
MemoryPool<S> memPoolS;
|
||||
BOOST_CHECK(memPoolS.empty());
|
||||
BOOST_CHECK(memPoolS.size() == 0);
|
||||
|
||||
ObjectPool<int> objPoolI(64);
|
||||
BOOST_CHECK(objPoolI.empty());
|
||||
BOOST_CHECK(objPoolI.size() == 0);
|
||||
ObjectPool<float> objPoolF(32);
|
||||
BOOST_CHECK(objPoolF.empty());
|
||||
BOOST_CHECK(objPoolF.size() == 0);
|
||||
ObjectPool<double> objPoolD(16);
|
||||
BOOST_CHECK(objPoolD.empty());
|
||||
BOOST_CHECK(objPoolD.size() == 0);
|
||||
ObjectPool<S> objPoolS(128);
|
||||
BOOST_CHECK(objPoolS.empty());
|
||||
BOOST_CHECK(objPoolS.size() == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testOperators)
|
||||
{
|
||||
ObjectPool<S> pool(100);
|
||||
// S* s[12] = pool.NewArray(12);
|
||||
S* s[12];
|
||||
s[0] = pool.New();
|
||||
s[11] = pool.New();
|
||||
|
||||
s[0]->k = 2;
|
||||
s[11]->k = 3;
|
||||
//testing operators: ++i,!=
|
||||
auto& iter = pool.begin();
|
||||
for (iter; iter != pool.end(); ++iter) {
|
||||
//testing operators:*,==
|
||||
auto dereferencedIterator = *iter;
|
||||
if (iter == pool.begin()) {
|
||||
BOOST_CHECK(dereferencedIterator.k == 2);
|
||||
}
|
||||
if (iter == pool.end()) {
|
||||
BOOST_CHECK(dereferencedIterator.k == 3);
|
||||
}
|
||||
//testing operators:->
|
||||
iter->k += 2;
|
||||
}
|
||||
BOOST_CHECK(s[0]->k == 4);
|
||||
BOOST_CHECK(s[11]->k == 5);
|
||||
BOOST_CHECK(iter == pool.end());
|
||||
|
||||
//testing operators:i++
|
||||
s[0]->k = 2;
|
||||
s[11]->k = 2;
|
||||
for (auto& iter = pool.begin(); iter != pool.end(); iter++)
|
||||
iter->k += 2;
|
||||
BOOST_CHECK(s[0]->k == 4);
|
||||
BOOST_CHECK(s[11]->k == 4);
|
||||
}
|
||||
/*
|
||||
BOOST_AUTO_TEST_CASE(testBranchFree)
|
||||
{
|
||||
//testing Free , which is the only untested
|
||||
//via delete/deletearray
|
||||
|
||||
//1. no extra memory delete
|
||||
ObjectPool<S> pool(32);//32 true/false values = 32 slots
|
||||
S* addresses[12];
|
||||
addresses[0] = pool.New(7, 0.035f);
|
||||
pool.Delete(addresses[0]);
|
||||
BOOST_CHECK(pool.empty());
|
||||
|
||||
//1b. no extra memory deleteArray
|
||||
ObjectPool<S> pool1b(32);//32 true/false values = 32 slots
|
||||
S* test1b;
|
||||
test1b = pool1b.NewArray(5);// <-> test2 = new S[5];
|
||||
BOOST_CHECK(pool1b.size() == 5);
|
||||
pool1b.DeleteArray(test1b, 5);//callar destructorn på test2 också
|
||||
BOOST_CHECK(pool1b.empty());
|
||||
|
||||
//2. extra memory delete
|
||||
ObjectPool<S> pool2(2);
|
||||
S* addresses2[12];
|
||||
addresses2[0] = pool2.New(7, 0.035f);
|
||||
addresses2[1] = pool2.New(7, 0.035f);
|
||||
addresses2[2] = pool2.New(7, 0.035f);
|
||||
addresses2[3] = pool2.New(7, 0.035f);
|
||||
addresses2[4] = pool2.New(7, 0.035f);
|
||||
BOOST_CHECK(pool2.size() == 5);
|
||||
pool2.Delete(addresses2[0]);
|
||||
BOOST_CHECK(pool2.size() == 4);
|
||||
pool2.Delete(addresses2[1]);
|
||||
BOOST_CHECK(pool2.size() == 3);
|
||||
pool2.Delete(addresses2[2]);
|
||||
BOOST_CHECK(pool2.size() == 2);
|
||||
pool2.Delete(addresses2[3]);
|
||||
BOOST_CHECK(pool2.size() == 1);
|
||||
pool2.Delete(addresses2[4]);
|
||||
BOOST_CHECK(pool2.empty());
|
||||
//reverse delete
|
||||
addresses2[0] = pool2.New(7, 0.035f);
|
||||
addresses2[1] = pool2.New(7, 0.035f);
|
||||
addresses2[2] = pool2.New(7, 0.035f);
|
||||
addresses2[3] = pool2.New(7, 0.035f);
|
||||
addresses2[4] = pool2.New(7, 0.035f);
|
||||
BOOST_CHECK(pool2.size() == 5);
|
||||
pool2.Delete(addresses2[4]);
|
||||
BOOST_CHECK(pool2.size() == 4);
|
||||
pool2.Delete(addresses2[3]);
|
||||
BOOST_CHECK(pool2.size() == 3);
|
||||
pool2.Delete(addresses2[2]);
|
||||
BOOST_CHECK(pool2.size() == 2);
|
||||
pool2.Delete(addresses2[1]);
|
||||
BOOST_CHECK(pool2.size() == 1);
|
||||
pool2.Delete(addresses2[0]);
|
||||
BOOST_CHECK(pool2.empty());
|
||||
|
||||
//2b. extra memory deleteArray
|
||||
ObjectPool<S> pool2b(32);//32 true/false values = 32 slots
|
||||
S* test2b,*test2bb;
|
||||
test2b = pool2b.NewArray(5);// <-> test2 = new S[5];
|
||||
BOOST_CHECK(pool2b.size() == 5);
|
||||
test2bb = pool2b.NewArray(40);// <-> test2 = new S[5];
|
||||
BOOST_CHECK(pool2b.size() == 45);
|
||||
pool2b.DeleteArray(test2b, 5);//callar destructorn på test2 också
|
||||
BOOST_CHECK(pool2b.size() == 40);
|
||||
pool2b.DeleteArray(test2bb, 40);//callar destructorn på test2 också
|
||||
BOOST_CHECK(pool2b.empty());
|
||||
}
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(testBranchAllocate)
|
||||
{
|
||||
//1 slot else many slots
|
||||
//see testBranchFree
|
||||
|
||||
//out of mem vs not out of mem allocate
|
||||
//see testBranchFree
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(testEdgeCase)
|
||||
{
|
||||
//test with a very small pool
|
||||
ObjectPool<S> pool(1);
|
||||
BOOST_CHECK(pool.empty());
|
||||
S* test4;
|
||||
test4 = pool.New(7, 0.035f);
|
||||
BOOST_CHECK(!pool.empty());
|
||||
BOOST_CHECK(test4->k == 7);
|
||||
BOOST_CHECK_CLOSE_FRACTION(test4->f, 0.035f, 0.0001f);
|
||||
|
||||
//test with a very small pool and array, iterating
|
||||
ObjectPool<S> poolA(1);
|
||||
S* test5;
|
||||
test5 = poolA.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : poolA)
|
||||
o.k = 14;
|
||||
for (size_t i = 0; i < 1; ++i)
|
||||
BOOST_CHECK(test5[i].k == 14);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testBadlyAlignedData)
|
||||
{
|
||||
//small test with non-aligned data 4+1bytes
|
||||
struct S
|
||||
{
|
||||
S() = default;
|
||||
S(float f, char c) : m_f(f), m_c(c) { }
|
||||
~S() { }
|
||||
float m_f;
|
||||
char m_c;
|
||||
};
|
||||
MemoryPool<S> memPoolS;
|
||||
BOOST_CHECK(memPoolS.empty());
|
||||
BOOST_CHECK(memPoolS.size() == 0);
|
||||
ObjectPool<S> objPoolS(64);
|
||||
BOOST_CHECK(objPoolS.empty());
|
||||
BOOST_CHECK(objPoolS.size() == 0);
|
||||
S* test4;
|
||||
test4 = objPoolS.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : objPoolS) {
|
||||
o.m_c = 'v';
|
||||
o.m_f = 0.15534543f;
|
||||
}
|
||||
for (size_t i = 0; i < 1; ++i) {
|
||||
BOOST_CHECK(test4[i].m_c == 'v');
|
||||
BOOST_CHECK_CLOSE_FRACTION(test4[i].m_f, 0.15534543f, 0.0001f);
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(testBadlyAlignedData2)
|
||||
{
|
||||
//small test with non-aligned data 1+1+1bytes
|
||||
struct S
|
||||
{
|
||||
S() = default;
|
||||
S(char c, char c2, char c3) : m_c(c), m_c2(c2), m_c3(c3) { }
|
||||
~S() { }
|
||||
char m_c;
|
||||
char m_c2;
|
||||
char m_c3;
|
||||
};
|
||||
MemoryPool<S> memPoolS;
|
||||
BOOST_CHECK(memPoolS.empty());
|
||||
BOOST_CHECK(memPoolS.size() == 0);
|
||||
ObjectPool<S> objPoolS(64);
|
||||
BOOST_CHECK(objPoolS.empty());
|
||||
BOOST_CHECK(objPoolS.size() == 0);
|
||||
S* test4;
|
||||
test4 = objPoolS.New();
|
||||
//Check so iterate over pool doesn't throw compile-time errors.
|
||||
for (auto &o : objPoolS) {
|
||||
o.m_c = 'v';
|
||||
o.m_c2 = 'w';
|
||||
o.m_c3 = 'x';
|
||||
}
|
||||
for (size_t i = 0; i < 1; ++i) {
|
||||
BOOST_CHECK(test4[i].m_c == 'v');
|
||||
BOOST_CHECK(test4[i].m_c2 == 'w');
|
||||
BOOST_CHECK(test4[i].m_c3 == 'x');
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(testWrongData)
|
||||
{
|
||||
}
|
||||
BOOST_AUTO_TEST_CASE(testFillDeleteFillAgain) {
|
||||
//already done in BOOST_AUTO_TEST_CASE(releaseModeTest_RandomAllocateDeallocate)
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -0,0 +1,160 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
#include <stdlib.h>//srand
|
||||
|
||||
#include "Engine/Core/OctTree.h"
|
||||
#include "Engine/Core/Ray.h"
|
||||
#include "OldOctTree.h"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(octTreeTestsW)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octSameRegionTest)
|
||||
{
|
||||
glm::vec3 mini = glm::vec3(-1, -1, -1);
|
||||
glm::vec3 maxi = glm::vec3(1, 1, 1);
|
||||
OctTree tree(AABB(mini, maxi), 2);
|
||||
AABB firstQuadrant(mini, 0.8f*mini);
|
||||
tree.AddStaticObject(firstQuadrant);
|
||||
AABB testBox(0.9f*mini, 0.8f*mini);
|
||||
std::vector<AABB> region;
|
||||
tree.BoxesInSameRegion(testBox, region);
|
||||
BOOST_REQUIRE(region.size() == 1);
|
||||
AABB& box = region[0];
|
||||
BOOST_CHECK_CLOSE_FRACTION(box.Center().x, firstQuadrant.Center().x, 0.00001f);
|
||||
BOOST_CHECK_CLOSE_FRACTION(box.Center().y, firstQuadrant.Center().y, 0.00001f);
|
||||
BOOST_CHECK_CLOSE_FRACTION(box.Center().z, firstQuadrant.Center().z, 0.00001f);
|
||||
BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().x, firstQuadrant.HalfSize().x, 0.00001f);
|
||||
BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().y, firstQuadrant.HalfSize().y, 0.00001f);
|
||||
BOOST_CHECK_CLOSE_FRACTION(box.HalfSize().z, firstQuadrant.HalfSize().z, 0.00001f);
|
||||
}
|
||||
|
||||
const int LEVEL_BOUNDS = 500;
|
||||
const int MAXSIZE = 50;
|
||||
const int BOXES = 400;
|
||||
const int NUM_DYNAMICS = 0;
|
||||
const int NUM_STATICS = BOXES - NUM_DYNAMICS;
|
||||
const int SEED = 6548;
|
||||
const int TEST_FRAMES = 300;
|
||||
const int NUM_FUNCTION_LOOPS = 25;
|
||||
const int TESTS = 0; //10
|
||||
|
||||
template<typename Tree>
|
||||
void RegionTest(Tree& tree)
|
||||
{
|
||||
AABB aabb;
|
||||
aabb.CreateFromCenter(glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS),
|
||||
glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE));
|
||||
std::vector<AABB> outVec;
|
||||
tree.BoxesInSameRegion(aabb, outVec);
|
||||
}
|
||||
|
||||
template<typename Tree>
|
||||
void RayTest(Tree& tree)
|
||||
{
|
||||
Tree::Output data;
|
||||
glm::vec3 rayStart = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS);
|
||||
glm::vec3 rayEnd = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS);
|
||||
tree.RayCollides({ rayStart , glm::normalize(rayEnd - rayStart) }, data);
|
||||
}
|
||||
|
||||
template<typename Tree>
|
||||
void BoxTest(Tree& tree)
|
||||
{
|
||||
AABB outBox;
|
||||
AABB aabb;
|
||||
aabb.CreateFromCenter(glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS),
|
||||
glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE));
|
||||
tree.BoxCollides(aabb, outBox);
|
||||
}
|
||||
|
||||
template<typename Tree>
|
||||
void NopTest(Tree& tree)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template<typename Tree, typename TestFunction>
|
||||
void TestLoop(TestFunction xTest)
|
||||
{
|
||||
srand(SEED);
|
||||
glm::vec3 mini = glm::vec3(0, 0, 0);
|
||||
glm::vec3 maxi = glm::vec3(LEVEL_BOUNDS, LEVEL_BOUNDS, LEVEL_BOUNDS);
|
||||
Tree tree(AABB(mini, maxi), 3);
|
||||
AABB aabb;
|
||||
glm::vec3 center;
|
||||
glm::vec3 size;
|
||||
for (int t = 0; t < TESTS; ++t) {
|
||||
for (int i = 0; i < NUM_STATICS; ++i) {
|
||||
center = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS);
|
||||
size = glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE);
|
||||
aabb.CreateFromCenter(center, size);
|
||||
tree.AddStaticObject(aabb);
|
||||
}
|
||||
for (int fr = 0; fr < TEST_FRAMES; ++fr) {
|
||||
for (int i = 0; i < NUM_DYNAMICS; ++i) {
|
||||
center = glm::vec3(rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS, rand() % LEVEL_BOUNDS);
|
||||
size = glm::vec3(rand() % MAXSIZE, rand() % MAXSIZE, rand() % MAXSIZE);
|
||||
aabb.CreateFromCenter(center, size);
|
||||
tree.AddDynamicObject(aabb);
|
||||
}
|
||||
|
||||
for (int fl = 0; fl < NUM_FUNCTION_LOOPS; ++fl) {
|
||||
xTest(tree);
|
||||
}
|
||||
|
||||
tree.ClearDynamicObjects();
|
||||
}
|
||||
tree.ClearObjects();
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octRegionPerfTestWithDuplicates)
|
||||
{
|
||||
TestLoop<Old::OctTree>(RegionTest<Old::OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octRegionPerfTestNoDuplicates)
|
||||
{
|
||||
TestLoop<OctTree>(RegionTest<OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octBoxPerfTestWithDuplicates)
|
||||
{
|
||||
TestLoop<Old::OctTree>(BoxTest<Old::OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octBoxPerfTestNoDuplicates)
|
||||
{
|
||||
TestLoop<OctTree>(BoxTest<OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octRayPerfTestWithDuplicates)
|
||||
{
|
||||
TestLoop<Old::OctTree>(RayTest<Old::OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octRayPerfTestNoDuplicates)
|
||||
{
|
||||
TestLoop<OctTree>(RayTest<OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octNopPerfTestWithDuplicates)
|
||||
{
|
||||
TestLoop<Old::OctTree>(NopTest<Old::OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octNopPerfTestNoDuplicates)
|
||||
{
|
||||
TestLoop<OctTree>(NopTest<OctTree>);
|
||||
BOOST_CHECK(true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
#include <stdlib.h>//srand
|
||||
|
||||
//#define private public//HACK! Needed for white box testing
|
||||
//#include "Engine/Core/OctTree.h"
|
||||
//#include "OldOctTree.h"
|
||||
//friend class and refactoringIntoNewClass is some extra work and needs to be updated when the original class is updated, and can contain bugs that
|
||||
//isnt in the original class
|
||||
//Reflection-inspection seems to be only available for C#
|
||||
//http://stackoverflow.com/questions/6778496/how-to-do-unit-testing-on-private-members-and-methods-of-c-classes
|
||||
//http://stackoverflow.com/questions/3676664/unit-testing-of-private-methods
|
||||
|
||||
#include "OctTreeTestGameClass.h"
|
||||
|
||||
#define private public//HACK! Needed for white box testing
|
||||
#include <Engine\Core\OctTree.h>
|
||||
//else we would have to "open up" the octTree class more with get/sets, public methods, etc. which is not good encapsulation-wise
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(octTreeTestsA)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octTreeTest)
|
||||
{
|
||||
//white box testing
|
||||
//http://softwaretestingfundamentals.com/differences-between-black-box-testing-and-white-box-testing/
|
||||
//http://technologyconversations.com/2013/12/11/black-box-vs-white-box-testing/
|
||||
|
||||
//simple AABB constructor check
|
||||
auto minCorner = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
auto maxCorner = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
auto someAABB = AABB(minCorner, maxCorner);
|
||||
BOOST_CHECK(someAABB.MinCorner() == minCorner);
|
||||
BOOST_CHECK(someAABB.MaxCorner() == maxCorner);
|
||||
BOOST_CHECK(someAABB.Center() == 0.5f * (minCorner + maxCorner));
|
||||
|
||||
//simple destructor check in the end, just look for memleaks, then it didnt clear the AABB structure
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(octTreeTest2)
|
||||
{
|
||||
//octtree draw etc
|
||||
Game game(0, nullptr);
|
||||
while (game.Running()) {
|
||||
game.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "OctTreeTestGameClass.h"
|
||||
|
||||
Game::Game(int argc, char* argv[]) : someOctTree(AABB(-0.5f*worldSize, 0.5f*worldSize), 2)
|
||||
{
|
||||
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
|
||||
ResourceManager::RegisterType<Model>("Model");
|
||||
ResourceManager::RegisterType<Texture>("Texture");
|
||||
ResourceManager::RegisterType<EntityXMLFile>("EntityXMLFile");
|
||||
ResourceManager::RegisterType<ShaderProgram>("ShaderProgram");
|
||||
|
||||
m_Config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get<int>("Debug.LogLevel", 1));
|
||||
|
||||
// Create the core event broker
|
||||
m_EventBroker = new EventBroker();
|
||||
|
||||
m_RenderQueueFactory = new RenderQueueFactory();
|
||||
|
||||
// Create the renderer
|
||||
m_Renderer = new Renderer(m_EventBroker);
|
||||
m_Renderer->SetFullscreen(m_Config->Get<bool>("Video.Fullscreen", false));
|
||||
m_Renderer->SetVSYNC(m_Config->Get<bool>("Video.VSYNC", false));
|
||||
m_Renderer->SetResolution(Rectangle(
|
||||
0,
|
||||
0,
|
||||
m_Config->Get<int>("Video.Width", 1280),
|
||||
m_Config->Get<int>("Video.Height", 720)
|
||||
));
|
||||
m_Renderer->Initialize();
|
||||
m_Renderer->Camera()->SetFOV(glm::radians(m_Config->Get<float>("Video.FOV", 90.f)));
|
||||
|
||||
// Create input manager
|
||||
m_InputManager = new InputManager(m_Renderer->Window(), m_EventBroker);
|
||||
m_InputProxy = new InputProxy(m_EventBroker);
|
||||
m_InputProxy->AddHandler<KeyboardInputHandler>();
|
||||
m_InputProxy->AddHandler<MouseInputHandler>();
|
||||
m_InputProxy->LoadBindings("Input.ini");
|
||||
|
||||
// Create the root level GUI frame
|
||||
m_FrameStack = new GUI::Frame(m_EventBroker);
|
||||
m_FrameStack->Width = m_Renderer->Resolution().Width;
|
||||
m_FrameStack->Height = m_Renderer->Resolution().Height;
|
||||
|
||||
// Create a TEST WORLD
|
||||
m_World = new HardcodedTestWorld();
|
||||
|
||||
m_SystemPipeline = new SystemPipeline(m_EventBroker);
|
||||
m_SystemPipeline->AddSystem<PlayerSystem>(0);
|
||||
|
||||
m_LastTime = glfwGetTime();
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
delete m_FrameStack;
|
||||
delete m_EventBroker;
|
||||
}
|
||||
|
||||
void Game::Tick()
|
||||
{
|
||||
double currentTime = glfwGetTime();
|
||||
double dt = currentTime - m_LastTime;
|
||||
m_LastTime = currentTime;
|
||||
|
||||
// Handle input in a weird looking but responsive way
|
||||
m_EventBroker->Process<InputManager>();
|
||||
m_EventBroker->Swap();
|
||||
m_InputManager->Update(dt);
|
||||
m_EventBroker->Swap();
|
||||
m_InputProxy->Update(dt);
|
||||
m_EventBroker->Swap();
|
||||
m_InputProxy->Process();
|
||||
m_EventBroker->Swap();
|
||||
|
||||
#define TEST1
|
||||
//this draws the octTree and you can set the cube inside it and see what boxes in the tree that it belongs to
|
||||
#ifdef TEST1
|
||||
if (!m_UpdatedOnce) {
|
||||
m_UpdatedOnce = true;
|
||||
m_World->createTestEntitiesTest1();
|
||||
}
|
||||
|
||||
//add/move the trigger box
|
||||
auto pos = m_Renderer->Camera()->Forward() + m_Renderer->Camera()->Position();
|
||||
AABB boxi;
|
||||
boxi.CreateFromCenter(pos, maxPos - minPos);
|
||||
frameCounter++;
|
||||
if (frameCounter > 1) {
|
||||
m_World->someOctTree.ClearDynamicObjects();
|
||||
m_World->someOctTree.AddDynamicObject(boxi);
|
||||
frameCounter = 0;
|
||||
}
|
||||
ComponentWrapper transform = m_World->GetComponent(m_World->anotherBoxTransformId, "Transform");
|
||||
transform["Position"] = boxi.Center();
|
||||
|
||||
//check all children again in the tree if they have a box in them or not, and colormark them if they do
|
||||
//contentboxarna får man ut - inte childboxarna!
|
||||
std::vector<int> boxIndex;
|
||||
boxIndex = m_World->someOctTree.m_Root->childIndicesContainingBox(boxi);
|
||||
|
||||
for (auto& oneLinkedObject : m_World->linkOM)
|
||||
{
|
||||
ComponentWrapper model = m_World->GetComponent(oneLinkedObject.entId, "Model");
|
||||
model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (oneLinkedObject.child->m_DynamicObjIndices.size() != 0) {
|
||||
model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
//next check if the childIndicesContainingBox method returns the correct boxes
|
||||
//REQUIRED: childIndicesContainingBox must be public to test this!
|
||||
for each (auto someBoxIndex in boxIndex)
|
||||
{
|
||||
glm::vec3 pos = m_World->someOctTree.m_Root->m_Children[someBoxIndex]->m_Box.Center();
|
||||
if (abs(pos.x - oneLinkedObject.posxyz.x) < 0.005f &&
|
||||
abs(pos.y - oneLinkedObject.posxyz.y) < 0.005f &&
|
||||
abs(pos.z - oneLinkedObject.posxyz.z) < 0.005f) {
|
||||
model["Color"] = glm::vec4(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
m_RenderQueueFactory->Update(m_World);
|
||||
|
||||
//wireframe
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
#endif
|
||||
//this tests AABB vs AABB collision and AABB vs OctTree with AABB in it
|
||||
#ifdef TEST2
|
||||
|
||||
//only add 1 for now...
|
||||
//grey box
|
||||
|
||||
const glm::vec4 redCol = glm::vec4(1, 0.2f, 0, 1);
|
||||
const glm::vec4 greenCol = glm::vec4(0.1f, 1.0f, 0.25f, 1);
|
||||
const glm::vec3 boxSize = 0.1f*glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
AABB aabb;
|
||||
aabb.CreateFromCenter(glm::vec3(0, 2.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
if (m_UpdatedOnce) {
|
||||
//auto test = someOctTree.childIndicesContainingBox(aabb);
|
||||
std::vector<AABB> test2;
|
||||
someOctTree.BoxesInSameRegion(aabb, test2);
|
||||
}
|
||||
if (!m_UpdatedOnce) {
|
||||
m_UpdatedOnce = true;
|
||||
someOctTree.AddStaticObject(aabb);
|
||||
//create the "small red box"
|
||||
m_BoxID = m_World->CreateEntity();
|
||||
ComponentWrapper transform = m_World->AttachComponent(m_BoxID, "Transform");
|
||||
transform["Scale"] = boxSize;
|
||||
ComponentWrapper model = m_World->AttachComponent(m_BoxID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitBox.obj";
|
||||
m_World->createTestEntitiesTest2();
|
||||
}
|
||||
|
||||
//red box
|
||||
AABB redBox;
|
||||
auto boxPos = m_Renderer->Camera()->Position() + 1.2f*m_Renderer->Camera()->Forward();
|
||||
redBox.CreateFromCenter(boxPos, boxSize);
|
||||
ComponentWrapper transform = m_World->GetComponent(m_BoxID, "Transform");
|
||||
transform["Position"] = boxPos;
|
||||
ComponentWrapper model = m_World->GetComponent(m_BoxID, "Model");
|
||||
//this checks AABB vs an AABB in the octTree
|
||||
if (someOctTree.BoxCollides(redBox, AABB())) {
|
||||
//this checks AABB vs AABB
|
||||
//if (Collision::AABBVsAABB(redBox, aabb)) {
|
||||
//m_Renderer->Camera()->SetPosition(m_PrevPos);
|
||||
//m_Renderer->Camera()->SetOrientation(m_PrevOri);
|
||||
model["Color"] = greenCol;
|
||||
}
|
||||
else {
|
||||
model["Color"] = redCol;
|
||||
}
|
||||
|
||||
m_PrevPos = m_Renderer->Camera()->Position();
|
||||
m_PrevOri = m_Renderer->Camera()->Orientation();
|
||||
|
||||
m_RenderQueueFactory->Update(m_World);
|
||||
#endif
|
||||
|
||||
// Iterate through systems and update world!
|
||||
m_SystemPipeline->Update(m_World, dt);
|
||||
m_Renderer->Update(dt);
|
||||
|
||||
m_RenderQueueFactory->Update(m_World);
|
||||
GLERROR("Game::Tick m_RenderQueueFactory->Update");
|
||||
m_Renderer->Draw(m_RenderQueueFactory->RenderQueues());
|
||||
GLERROR("Game::Tick m_Renderer->Draw");
|
||||
m_EventBroker->Swap();
|
||||
m_EventBroker->Clear();
|
||||
|
||||
glfwPollEvents();
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef Game_h__
|
||||
#define Game_h__
|
||||
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "Core/ConfigFile.h"
|
||||
#include "Core/EventBroker.h"
|
||||
#include "Rendering/Renderer.h"
|
||||
#include "Core/InputManager.h"
|
||||
#include "GUI/Frame.h"
|
||||
#include "Core/World.h"
|
||||
#include "Rendering/RenderQueueFactory.h"
|
||||
#include "Input/InputProxy.h"
|
||||
#include "Input/KeyboardInputHandler.h"
|
||||
#include "Input/MouseInputHandler.h"
|
||||
#include "Core/EKeyDown.h"
|
||||
#include "Core/EntityXMLFile.h"
|
||||
#include "Core/SystemPipeline.h"
|
||||
#include "RaptorCopterSystem.h"
|
||||
#include "PlayerSystem.h"
|
||||
#include "Editor/EditorSystem.h"
|
||||
|
||||
#include "OctTreeTestHardCodedTestWorld.h"
|
||||
#include "Collision/Collision.h"
|
||||
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
Game(int argc, char* argv[]);
|
||||
~Game();
|
||||
|
||||
bool Running() const { return !glfwWindowShouldClose(m_Renderer->Window()); }
|
||||
void Tick();
|
||||
|
||||
private:
|
||||
double m_LastTime;
|
||||
ConfigFile* m_Config = nullptr;
|
||||
EventBroker* m_EventBroker;
|
||||
IRenderer* m_Renderer;
|
||||
InputManager* m_InputManager;
|
||||
GUI::Frame* m_FrameStack;
|
||||
HardcodedTestWorld* m_World;
|
||||
RenderQueueFactory* m_RenderQueueFactory;
|
||||
InputProxy* m_InputProxy;
|
||||
SystemPipeline* m_SystemPipeline;
|
||||
|
||||
//Test1
|
||||
int frameCounter = 0;
|
||||
glm::vec3 minPos = glm::vec3(0.1f, 0.1f, 0.1f);
|
||||
glm::vec3 maxPos = glm::vec3(0.2f, 0.2f, 0.2f);
|
||||
|
||||
//Test2
|
||||
bool m_UpdatedOnce = false;
|
||||
unsigned int m_BoxID;
|
||||
glm::vec3 m_PrevPos;
|
||||
glm::quat m_PrevOri;
|
||||
|
||||
glm::vec3 worldSize = glm::vec3(50, 50, 50);
|
||||
OctTree someOctTree;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
//#define BOOST_TEST_MODULE collTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/execution_monitor.hpp>
|
||||
using boost::unit_test_framework::test_suite;
|
||||
using boost::unit_test_framework::test_case;
|
||||
#include "Engine/Collision/Collision.h"
|
||||
#include "Engine/Core/AABB.h"
|
||||
#include "Engine/Core/Ray.h"
|
||||
#include <stdlib.h>//srand
|
||||
#include "Engine/Core/OctTree.h"
|
||||
|
||||
//vs memleaks
|
||||
//#define _CRTDBG_MAP_ALLOC
|
||||
//#include <stdlib.h>
|
||||
//#include <crtdbg.h>
|
||||
//#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
|
||||
//#define new DEBUG_CLIENTBLOCK
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(cTest)
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
#include <boost/any.hpp>
|
||||
#include "GLM.h"
|
||||
#include "Core/World.h"
|
||||
#include "Core/Util/Any.h"
|
||||
|
||||
#include <vector>
|
||||
//last!
|
||||
//#include "OldOctTree.h"
|
||||
#define private public
|
||||
#include <Engine\Core\OctTree.h>
|
||||
|
||||
class HardcodedTestWorld : public World
|
||||
{
|
||||
public:
|
||||
struct LinkOctTreeAndModel {
|
||||
EntityID entId;
|
||||
OctTree::OctChild* child;
|
||||
glm::vec3 posxyz;
|
||||
LinkOctTreeAndModel(EntityID eId, OctTree::OctChild* ch, glm::vec3 pos)
|
||||
{
|
||||
entId = eId;
|
||||
child = ch;
|
||||
posxyz = pos;
|
||||
}
|
||||
};
|
||||
EntityID anotherBoxTransformId;
|
||||
std::vector<LinkOctTreeAndModel> linkOM;
|
||||
OctTree someOctTree;
|
||||
|
||||
//constructor
|
||||
HardcodedTestWorld()
|
||||
: World()
|
||||
, someOctTree(AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f)), 2)
|
||||
{
|
||||
registerTestComponents();
|
||||
//createTestEntities();
|
||||
}
|
||||
|
||||
private:
|
||||
void registerTestComponents()
|
||||
{
|
||||
ComponentWrapperFactory f;
|
||||
|
||||
|
||||
f = ComponentWrapperFactory("Test");
|
||||
f.AddProperty("TestInteger", 1337);
|
||||
f.AddProperty("TestFloat", 13.37f);
|
||||
f.AddProperty("TestString", std::string("Carlito"));
|
||||
RegisterComponent(f);
|
||||
|
||||
f = ComponentWrapperFactory("Debug");
|
||||
f.AddProperty("Name", std::string("Unnamed"));
|
||||
RegisterComponent(f);
|
||||
|
||||
f = ComponentWrapperFactory("Transform");
|
||||
f.AddProperty("Position", glm::vec3(0.f, 0.f, 0.f));
|
||||
f.AddProperty("Orientation", glm::quat());
|
||||
f.AddProperty("Scale", glm::vec3(1.f, 1.f, 1.f));
|
||||
RegisterComponent(f);
|
||||
|
||||
f = ComponentWrapperFactory("Model");
|
||||
f.AddProperty("Resource", std::string());
|
||||
f.AddProperty("Color", glm::vec4(1.f, 1.f, 1.f, 1.f));
|
||||
f.AddProperty("Visible", true);
|
||||
RegisterComponent(f);
|
||||
}
|
||||
|
||||
void createTestEntitiesTest1()
|
||||
{
|
||||
World& world = *this;
|
||||
EntityID tempId;
|
||||
//add octTree
|
||||
{
|
||||
//copy of mainbox
|
||||
auto someAABB = AABB(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
//draw main box first
|
||||
AddBoxModel(someAABB.Center(), someAABB.HalfSize().x, someOctTree.m_Root, tempId);
|
||||
|
||||
//add anotherbox in octTree
|
||||
auto anotherBox = AABB(glm::vec3(0.1f, 0.1f, 0.1f), glm::vec3(0.2f, 0.2f, 0.2f));
|
||||
//note: have to delete the box in the tree first, since were trying to move the box
|
||||
someOctTree.AddDynamicObject(anotherBox);
|
||||
|
||||
//draw anotherbox and save it in anotherBoxTransformId
|
||||
AddBoxModel(anotherBox.Center(), anotherBox.HalfSize().x, someOctTree.m_Root, anotherBoxTransformId);
|
||||
|
||||
//draw the octTree
|
||||
for (size_t j = 0; j < 8; j++)
|
||||
{
|
||||
AddBoxModel(someOctTree.m_Root->m_Children[j]->m_Box.Center(),
|
||||
someOctTree.m_Root->m_Children[j]->m_Box.HalfSize().x, someOctTree.m_Root->m_Children[j], tempId);
|
||||
|
||||
auto someChild = someOctTree.m_Root->m_Children[j];
|
||||
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
AddBoxModel(someChild->m_Children[i]->m_Box.Center(),
|
||||
someChild->m_Children[i]->m_Box.HalfSize().x, someChild->m_Children[i], tempId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}//end CreateEnt
|
||||
|
||||
void createTestEntitiesTest2()
|
||||
{
|
||||
World& world = *this;
|
||||
|
||||
EntityID entityCollisionBox = world.CreateEntity();
|
||||
ComponentWrapper transform = world.AttachComponent(entityCollisionBox, "Transform");
|
||||
transform["Position"] = glm::vec3(0.f, 2.f, 0.f);
|
||||
ComponentWrapper model = world.AttachComponent(entityCollisionBox, "Model");
|
||||
model["Resource"] = "Models/Core/UnitBox.obj";
|
||||
}
|
||||
|
||||
void AddBoxModel(const glm::vec3 ¢er, const float &halfSize, OctTree::OctChild* child, EntityID &outEntityId) {
|
||||
World& world = *this;
|
||||
|
||||
EntityID entityDummyScene = world.CreateEntity();
|
||||
outEntityId = entityDummyScene;
|
||||
ComponentWrapper transform = world.AttachComponent(entityDummyScene, "Transform");
|
||||
transform["Position"] = center;
|
||||
transform["Scale"] = glm::vec3(1.0f, 1.0f, 1.0f)*halfSize*2.0f*0.97f;
|
||||
ComponentWrapper model = world.AttachComponent(entityDummyScene, "Model");
|
||||
model["Resource"] = "Models/Core/UnitBox.obj";
|
||||
model["Color"] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
if (child->m_DynamicObjIndices.size() != 0)
|
||||
model["Color"] = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
linkOM.emplace_back(entityDummyScene, child, center);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,328 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "OldOctTree.h"
|
||||
#include "Collision/Collision.h"
|
||||
#include "Core/World.h"
|
||||
#include "Rendering/Camera.h"
|
||||
|
||||
namespace Old
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
//To be able to sort nodes based on distance to ray origin.
|
||||
struct ChildInfo
|
||||
{
|
||||
int Index;
|
||||
float Distance;
|
||||
};
|
||||
|
||||
bool isFirstLower(const ChildInfo& first, const ChildInfo& second)
|
||||
{
|
||||
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(AABB(), 0)
|
||||
{}
|
||||
|
||||
OctTree::OctTree(const AABB& octTreeBounds, int subDivisions)
|
||||
: m_Box(octTreeBounds)
|
||||
, m_UpdatedOnce(false)
|
||||
{
|
||||
if (subDivisions == 0) {
|
||||
for (OctTree*& c : m_Children) {
|
||||
c = nullptr;
|
||||
}
|
||||
} else {
|
||||
--subDivisions;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
glm::vec3 minPos, maxPos;
|
||||
const glm::vec3& parentMin = m_Box.MinCorner();
|
||||
const glm::vec3& parentMax = m_Box.MaxCorner();
|
||||
const glm::vec3& parentCenter = m_Box.Center();
|
||||
std::bitset<3> bits(i);
|
||||
//If child is 4,5,6,7.
|
||||
if (bits.test(2)) {
|
||||
minPos.x = parentCenter.x;
|
||||
maxPos.x = parentMax.x;
|
||||
} else {
|
||||
minPos.x = parentMin.x;
|
||||
maxPos.x = parentCenter.x;
|
||||
}
|
||||
|
||||
//If child is 2,3,6,7
|
||||
if (bits.test(1)) {
|
||||
minPos.y = parentCenter.y;
|
||||
maxPos.y = parentMax.y;
|
||||
} else {
|
||||
minPos.y = parentMin.y;
|
||||
maxPos.y = parentCenter.y;
|
||||
}
|
||||
//If child is 1,3,5,7
|
||||
if (bits.test(0)) {
|
||||
minPos.z = parentCenter.z;
|
||||
maxPos.z = parentMax.z;
|
||||
} else {
|
||||
minPos.z = parentMin.z;
|
||||
maxPos.z = parentCenter.z;
|
||||
}
|
||||
m_Children[i] = new OctTree(AABB(minPos, maxPos), subDivisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OctTree::~OctTree()
|
||||
{
|
||||
for (OctTree*& c : m_Children) {
|
||||
if (c != nullptr) {
|
||||
delete c;
|
||||
c = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::Update(float dt, World* world, Camera* cam)
|
||||
{
|
||||
AABB aabb;
|
||||
for (ComponentWrapper& c : *world->GetComponents("Collision")) {
|
||||
aabb.CreateFromCenter(c["BoxCenter"], c["BoxSize"]);
|
||||
AddStaticObject(aabb);
|
||||
}
|
||||
const glm::vec4 redCol = glm::vec4(1, 0.2f, 0, 1);
|
||||
const glm::vec4 greenCol = glm::vec4(0.1f, 1.0f, 0.25f, 1);
|
||||
const glm::vec3 boxSize = 0.1f*glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
if (!m_UpdatedOnce) {
|
||||
m_BoxID = world->CreateEntity();
|
||||
ComponentWrapper transform = world->AttachComponent(m_BoxID, "Transform");
|
||||
transform["Scale"] = boxSize;
|
||||
ComponentWrapper model = world->AttachComponent(m_BoxID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitBox.obj";
|
||||
m_UpdatedOnce = true;
|
||||
}
|
||||
|
||||
AABB box;
|
||||
auto boxPos = cam->Position() + 1.2f*cam->Forward();
|
||||
box.CreateFromCenter(boxPos, boxSize);
|
||||
ComponentWrapper transform = world->GetComponent(m_BoxID, "Transform");
|
||||
transform["Position"] = boxPos;
|
||||
ComponentWrapper model = world->GetComponent(m_BoxID, "Model");
|
||||
//if (BoxCollides(box, AABB())) {
|
||||
if (Collision::AABBVsAABB(box, aabb)) {
|
||||
cam->SetPosition(m_PrevPos);
|
||||
cam->SetOrientation(m_PrevOri);
|
||||
model["Color"] = greenCol;
|
||||
} else {
|
||||
model["Color"] = redCol;
|
||||
}
|
||||
|
||||
m_PrevPos = cam->Position();
|
||||
m_PrevOri = cam->Orientation();
|
||||
ClearObjects();
|
||||
}
|
||||
|
||||
bool OctTree::BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (int i : childIndicesContainingBox(boxToTest)) {
|
||||
if (m_Children[i]->BoxCollides(boxToTest, outBoxIntersected))
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (const auto& obj : m_StaticObjects) {
|
||||
if (Collision::AABBVsAABB(boxToTest, obj)) {
|
||||
outBoxIntersected = obj;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const auto& obj : m_DynamicObjects) {
|
||||
//If there is a collision and it is not testing against itself.
|
||||
if (!isSameBoxProbably(boxToTest, obj) &&
|
||||
Collision::AABBVsAABB(boxToTest, obj)) {
|
||||
outBoxIntersected = obj;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OctTree::RayCollides(const Ray& ray, Output& data) const
|
||||
{
|
||||
//If the node AABB is missed, everything it contains is missed.
|
||||
if (Collision::RayAABBIntr(ray, m_Box)) {
|
||||
//If the ray shoots the tree, and it is a parent to 8 children :o
|
||||
if (hasChildren()) {
|
||||
//Sort children according to their distance from the ray origin.
|
||||
std::vector<ChildInfo> childInfos;
|
||||
childInfos.reserve(8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Center()) });
|
||||
}
|
||||
std::sort(childInfos.begin(), childInfos.end(), isFirstLower);
|
||||
//Loop through the children, starting with the one closest to the ray origin. I.e the first to be hit.
|
||||
for (const ChildInfo& info : childInfos) {
|
||||
if (m_Children[info.Index]->RayCollides(ray, data)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Check against boxes in the node.
|
||||
float minDist = INFINITY;
|
||||
bool intersected = false;
|
||||
for (const auto& obj : m_StaticObjects) {
|
||||
float dist;
|
||||
if (Collision::RayVsAABB(ray, obj, dist)) {
|
||||
minDist = std::min(dist, minDist);
|
||||
intersected = true;
|
||||
}
|
||||
}
|
||||
for (const auto& obj : m_DynamicObjects) {
|
||||
float dist;
|
||||
if (Collision::RayVsAABB(ray, obj, dist)) {
|
||||
minDist = std::min(dist, minDist);
|
||||
intersected = true;
|
||||
}
|
||||
}
|
||||
|
||||
data.CollideDistance = minDist;
|
||||
return intersected;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void OctTree::AddDynamicObject(const AABB& box)
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
m_Children[i]->AddDynamicObject(box);
|
||||
}
|
||||
} else {
|
||||
m_DynamicObjects.push_back(box);
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::AddStaticObject(const AABB& box)
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
m_Children[i]->AddStaticObject(box);
|
||||
}
|
||||
} else {
|
||||
m_StaticObjects.push_back(box);
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes) const
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (auto i : childIndicesContainingBox(box)) {
|
||||
m_Children[i]->BoxesInSameRegion(box, outBoxes);
|
||||
}
|
||||
} else {
|
||||
outBoxes.insert(outBoxes.end(), m_StaticObjects.begin(), m_StaticObjects.end());
|
||||
outBoxes.insert(outBoxes.end(), m_DynamicObjects.begin(), m_DynamicObjects.end());
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::ClearObjects()
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (OctTree*& c : m_Children) {
|
||||
c->ClearObjects();
|
||||
}
|
||||
} else {
|
||||
m_DynamicObjects.clear();
|
||||
m_StaticObjects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void OctTree::ClearDynamicObjects()
|
||||
{
|
||||
if (hasChildren()) {
|
||||
for (OctTree*& c : m_Children) {
|
||||
c->ClearObjects();
|
||||
}
|
||||
} else {
|
||||
m_DynamicObjects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//: 3 7
|
||||
//:
|
||||
//: 2 6
|
||||
//: |
|
||||
//: 1 5 \ y
|
||||
//: z
|
||||
//: 0 4 0 x-->
|
||||
//
|
||||
// child: 0 1 2 3 4 5 6 7
|
||||
// x : - - - - + + + +
|
||||
// y : - - + + - - + +
|
||||
// z : - + - + - + - +
|
||||
int OctTree::childIndexContainingPoint(const glm::vec3& point) const
|
||||
{
|
||||
const glm::vec3& c = m_Box.Center();
|
||||
return (1 << 2) * (point.x >= c.x) | (1 << 1) * (point.y >= c.y) | (point.z >= c.z);
|
||||
}
|
||||
|
||||
std::vector<int> OctTree::childIndicesContainingBox(const AABB& box) const
|
||||
{
|
||||
int minInd = childIndexContainingPoint(box.MinCorner());
|
||||
int maxInd = childIndexContainingPoint(box.MaxCorner());
|
||||
//Because of the predictable ordering of the child indices,
|
||||
//the number of bits set when xor:ing the indices will determine the number of children containing the box.
|
||||
std::bitset<3> bits(minInd ^ maxInd);
|
||||
switch (bits.count()) {
|
||||
//Box contained completely in one child.
|
||||
case 0:
|
||||
return{ minInd };
|
||||
//Two children.
|
||||
case 1:
|
||||
return{ minInd, maxInd };
|
||||
//Four children.
|
||||
case 2:
|
||||
{
|
||||
std::vector<int> ret;
|
||||
//Bit-hax to calculate the correct 4 children containing the box.
|
||||
//This works because of the childrens index determine what part of
|
||||
//the dimensions they are responsible for (which octant).
|
||||
bits.flip();
|
||||
//At this point the bits necessarily have exactly one bit set.
|
||||
for (int c = 0; c < 8; ++c) {
|
||||
//If the child index have the same bit set as the bits, add box to it.
|
||||
if (bits.to_ulong() & c) {
|
||||
ret.push_back(c);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case 3: //Eight children.
|
||||
return{ 0,1,2,3,4,5,6,7 };
|
||||
default:
|
||||
return std::vector<int>();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool OctTree::hasChildren() const
|
||||
{
|
||||
return m_Children[0] != nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef OldOctTree_h__
|
||||
#define OldOctTree_h__
|
||||
|
||||
#include "Core/AABB.h"
|
||||
|
||||
class Ray;
|
||||
class World;
|
||||
class Camera;
|
||||
|
||||
namespace Old
|
||||
{
|
||||
|
||||
class OctTree
|
||||
{
|
||||
public:
|
||||
struct Output
|
||||
{
|
||||
float CollideDistance;
|
||||
};
|
||||
OctTree();
|
||||
~OctTree();
|
||||
//For the root OctTree, [octTreeBounds] should be a box containing the entire level.
|
||||
OctTree(const AABB& octTreeBounds, int subDivisions);
|
||||
|
||||
//We should only ever need one OctTree in the game, and it should not need to be copied.
|
||||
//Define these if the OctTree suddenly needs to be copied, think of the children OctTree* ptrs.
|
||||
OctTree(const OctTree& other) = delete;
|
||||
OctTree(const OctTree&& other) = delete;
|
||||
OctTree& operator= (const OctTree& other) = delete;
|
||||
|
||||
void AddDynamicObject(const AABB& box);
|
||||
void AddStaticObject(const AABB& box);
|
||||
|
||||
void BoxesInSameRegion(const AABB& box, std::vector<AABB>& outBoxes) const;
|
||||
|
||||
void ClearObjects();
|
||||
void ClearDynamicObjects();
|
||||
|
||||
//Collision test function.
|
||||
void Update(float dt, World* world, Camera* cam);
|
||||
//Returns true if the ray collides with something in the tree. Result is written to [data].
|
||||
bool RayCollides(const Ray& ray, Output& data) const;
|
||||
//Returns true if the box collides with something in the tree.
|
||||
//On collision with a box, that box is written to [outBoxIntersected].
|
||||
//Note: More efficient than calling BoxesInSameRegion from outside and testing there.
|
||||
bool BoxCollides(const AABB& boxToTest, AABB& outBoxIntersected) const;
|
||||
|
||||
private:
|
||||
OctTree* m_Children[8];
|
||||
std::vector<AABB> m_StaticObjects;
|
||||
std::vector<AABB> m_DynamicObjects;
|
||||
AABB m_Box;
|
||||
|
||||
bool m_UpdatedOnce;
|
||||
unsigned int m_BoxID;
|
||||
glm::vec3 m_PrevPos;
|
||||
glm::quat m_PrevOri;
|
||||
|
||||
inline bool hasChildren() const;
|
||||
int childIndexContainingPoint(const glm::vec3& point) const;
|
||||
std::vector<int> childIndicesContainingBox(const AABB& box) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "Core/World.h"
|
||||
|
||||
//private->public hack doesnt work, tons of link errors
|
||||
//so there is currently no good way to test this class
|
||||
//#define private public
|
||||
#include "Core/ResourceManager.h"
|
||||
#include "Core/ConfigFile.h"
|
||||
#include "Rendering/Renderer.h"
|
||||
#include "Core/EntityXMLFile.h"
|
||||
#include "Engine\Rendering\Texture.h"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(resourceManagerTests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(resourceManagerTest)
|
||||
{
|
||||
World m_World;
|
||||
|
||||
//private static metoder/variabler
|
||||
|
||||
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
|
||||
BOOST_CHECK(!ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini"));
|
||||
auto m_Config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
BOOST_CHECK(ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini"));
|
||||
ResourceManager::Release("ConfigFile", "Config.ini");
|
||||
BOOST_CHECK(!ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini"));
|
||||
|
||||
//configfile without register
|
||||
//check so output says "EE failed to load: type not registered..."
|
||||
auto m_ScreenQuadNoRegister = ResourceManager::Load<Model>("Models/Core/ScreenQuad.obj");
|
||||
BOOST_CHECK(!ResourceManager::IsResourceLoaded("Model", "Models/Core/ScreenQuad.obj"));
|
||||
|
||||
//there is no error feedback to check if you try to release the wrong resources - hence that cant be tested either
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(WorldTestMultipleAllocations, * utf::tolerance(0.00001))
|
||||
|
||||
// Loop through them and check data
|
||||
int i = 0;
|
||||
for (auto& c : w.GetComponents("Test")) {
|
||||
for (auto& c : *w.GetComponents("Test")) {
|
||||
BOOST_TEST((int)c["TestInteger"] == i);
|
||||
i++;
|
||||
}
|
||||
|
||||
+1
-2
@@ -86,8 +86,7 @@ T* ClassType::PublicMemberFunction(bool value)
|
||||
|
||||
if (m_PrivateMember2->PublicMember == 1) {
|
||||
return new T();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user