Merge branch 'Rendering' into TextRendering

# Conflicts:
#	include/Engine/Rendering/RenderQueue.h
#	include/Engine/Rendering/RenderQueueFactory.h
#	include/Engine/Rendering/Renderer.h
#	include/Engine/Rendering/ShaderProgram.h
#	include/Game/Game.h
#	resources/Schema/Components.xsd
#	resources/Schema/Entities/Test.xml
#	resources/Schema/Types/Entity.xsd
#	src/Engine/Rendering/RenderQueueFactory.cpp
#	src/Engine/Rendering/Renderer.cpp
#	src/Game/Game.cpp
This commit is contained in:
viktorljung
2016-01-15 13:23:42 +01:00
184 changed files with 9816 additions and 2121 deletions
+34 -3
View File
@@ -62,7 +62,6 @@ file(GLOB SOURCE_FILES_Rendering_Util
"${INCLUDE_PATH}/Rendering/Util/*.h"
"Rendering/Util/*.cpp"
)
source_group(Rendering FILES ${SOURCE_FILES_Rendering})
source_group(Rendering\\Util FILES ${SOURCE_FILES_Rendering_Util})
@@ -72,16 +71,48 @@ 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"
)
source_group(Editor FILES ${SOURCE_FILES_Editor})
set(SOURCE_FILES
${SOURCE_FILES_Core}
${SOURCE_FILES_Core_Util}
#${SOURCE_FILES_Input}
${SOURCE_FILES_Input}
${SOURCE_FILES_Network}
${SOURCE_FILES_GUI}
${SOURCE_FILES_Rendering}
${SOURCE_FILES_Rendering_Util}
${SOURCE_FILES_Collision}
${SOURCE_FILES_Editor}
${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
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_common.c
)
# nativefiledialog
if(WIN32)
set(SOURCE_FILES ${SOURCE_FILES}
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_win.cpp
)
endif()
if(UNIX)
set(SOURCE_FILES ${SOURCE_FILES}
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_gtk.c
# TODO: Link with GTK+ here!
)
endif()
set(LIBRARIES
${OPENGL_LIBRARIES}
${GLEW_LIBRARIES}
@@ -106,4 +137,4 @@ target_link_libraries(Engine
${LIBRARIES}
)
#set_target_properties(Engine PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${INCLUDE_PATH}/PrecompiledHeader.h")
#cotire(Engine)
#cotire(Engine)
+282
View File
@@ -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);
}
}
+40
View File
@@ -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;
}
+81
View File
@@ -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;
}
+34
View File
@@ -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()
{}
+8 -2
View File
@@ -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++();
@@ -50,10 +50,16 @@ ComponentWrapper ComponentPool::GetByEntity(EntityID ent)
return ComponentWrapper(m_ComponentInfo, m_EntityToComponent.at(ent));
}
bool ComponentPool::KnowsEntity(EntityID ent)
{
return m_EntityToComponent.find(ent) != m_EntityToComponent.end();
}
void ComponentPool::Delete(ComponentWrapper& wrapper)
{
m_EntityToComponent.erase(wrapper.EntityID);
m_Pool.Free(wrapper.Data);
m_Pool.Free(wrapper.Data - sizeof(EntityID));
}
ComponentPool::iterator ComponentPool::begin() const
+5 -2
View File
@@ -24,8 +24,11 @@ ConfigFile::ConfigFile(std::string path)
if (boost::filesystem::exists(m_Path)) {
try {
boost::property_tree::ini_parser::read_ini(m_Path.string(), m_PTreeOverrides);
for (auto& node : m_PTreeOverrides) {
m_PTreeMerged.put_child(node.first, node.second);
for (auto& topLevelNode : m_PTreeOverrides) {
auto& mergedTopLevelNode = m_PTreeMerged.find(topLevelNode.first);
for (auto& childOverrideNode : topLevelNode.second) {
mergedTopLevelNode->second.put_child(childOverrideNode.first, childOverrideNode.second);
}
}
} catch (boost::property_tree::ptree_error& e) {
LOG_ERROR("Failed to parse \"%s\":\n%s", m_Path.filename().string().c_str(), e.what());
+95
View File
@@ -0,0 +1,95 @@
#include "Core/EntityFile.h"
EntityFile::EntityFile(boost::filesystem::path path)
: m_FilePath(path)
{
using namespace xercesc;
XMLPlatformUtils::Initialize();
m_GrammarPool = new XMLGrammarPoolImpl();
m_SAX2XMLReader = XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
}
EntityFile::~EntityFile()
{
delete m_SAX2XMLReader;
delete m_GrammarPool;
xercesc::XMLPlatformUtils::Terminate();
}
void EntityFile::Parse(const EntityFileHandler* handler) const
{
using namespace xercesc;
EntityFileSAXHandler saxHandler(handler, nullptr);
m_SAX2XMLReader->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true);
m_SAX2XMLReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
m_SAX2XMLReader->setContentHandler(&saxHandler);
m_SAX2XMLReader->setErrorHandler(&saxHandler);
m_SAX2XMLReader->setDeclarationHandler(&saxHandler);
m_SAX2XMLReader->parse(m_FilePath.string().c_str());
}
std::size_t EntityFile::GetTypeStride(std::string typeName)
{
std::map<std::string, size_t> typeStrides{
{ "bool", sizeof(bool) },
{ "int", sizeof(int) },
{ "float", sizeof(float) },
{ "double", sizeof(double) },
{ "string", sizeof(std::string) },
{ "Vector", sizeof(glm::vec3) },
{ "Quaternion", sizeof(glm::quat) },
{ "Color", sizeof(glm::vec4) }
};
auto it = typeStrides.find(typeName);
return (it != typeStrides.end()) ? it->second : 0;
}
void EntityFile::WriteAttributeData(char* outData, const ComponentInfo::Field_t& field, const std::map<std::string, std::string>& attributes)
{
if (field.Type == "Vector") {
glm::vec3 vec;
vec.x = boost::lexical_cast<float>(attributes.at("X"));
vec.y = boost::lexical_cast<float>(attributes.at("Y"));
vec.z = boost::lexical_cast<float>(attributes.at("Z"));
memcpy(outData, reinterpret_cast<char*>(&vec), field.Stride);
} else if (field.Type == "Color") {
glm::vec4 vec;
vec.r = boost::lexical_cast<float>(attributes.at("R"));
vec.g = boost::lexical_cast<float>(attributes.at("G"));
vec.b = boost::lexical_cast<float>(attributes.at("B"));
vec.a = boost::lexical_cast<float>(attributes.at("A"));
memcpy(outData, reinterpret_cast<char*>(&vec), field.Stride);
} else if (field.Type == "Quaternion") {
glm::quat q;
q.x = boost::lexical_cast<float>(attributes.at("X"));
q.y = boost::lexical_cast<float>(attributes.at("Y"));
q.z = boost::lexical_cast<float>(attributes.at("Z"));
q.w = boost::lexical_cast<float>(attributes.at("W"));
memcpy(outData, reinterpret_cast<char*>(&q), field.Stride);
} else if (!attributes.empty()) {
LOG_WARNING("%i attributes not handled by any type conversion!", attributes.size());
}
}
void EntityFile::WriteValueData(char* outData, const ComponentInfo::Field_t& field, const char* valueData)
{
if (field.Type == "int") {
int value = boost::lexical_cast<int>(valueData);
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
} else if (field.Type == "float") {
float value = boost::lexical_cast<float>(valueData);
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
} else if (field.Type == "double") {
double value = boost::lexical_cast<double>(valueData);
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
} else if (field.Type == "bool") {
bool value = (valueData[0] == 't'); // Lazy bool evaluation
memcpy(outData, reinterpret_cast<char*>(&value), field.Stride);
} else if (field.Type == "string") {
new (outData) std::string(valueData);
} else {
LOG_WARNING("Unknown value data type: %s", field.Type.c_str());
}
}
+61
View File
@@ -0,0 +1,61 @@
#include "Core/EntityFileParser.h"
EntityFileParser::EntityFileParser(const EntityFile* entityFile)
: m_EntityFile(entityFile)
{
m_Handler.SetStartEntityCallback(std::bind(&EntityFileParser::onStartEntity, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_Handler.SetStartComponentCallback(std::bind(&EntityFileParser::onStartComponent, this, std::placeholders::_1, std::placeholders::_2));
m_Handler.SetStartFieldCallback(std::bind(&EntityFileParser::onStartComponentField, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
m_Handler.SetStartFieldDataCallback(std::bind(&EntityFileParser::onFieldData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
void EntityFileParser::MergeEntities(World* world)
{
m_World = world;
m_EntityIDMapper[0] = 0;
m_EntityFile->Parse(&m_Handler);
}
void EntityFileParser::onStartEntity(EntityID entity, EntityID parent, const std::string& name)
{
EntityID realParent = m_EntityIDMapper.at(parent);
EntityID realEntity = m_World->CreateEntity(realParent);
if (!name.empty()) {
m_World->SetName(realEntity, name);
}
m_EntityIDMapper[entity] = realEntity;
LOG_DEBUG("Created entity #%i (%i) with parent %i (%i)", entity, realEntity, parent, realParent);
}
void EntityFileParser::onStartComponent(EntityID entity, const std::string& component)
{
EntityID realEntity = m_EntityIDMapper.at(entity);
m_World->AttachComponent(realEntity, component);
LOG_DEBUG("Attached component of type \"%s\" to entity #%i (%i)", component.c_str(), entity, realEntity);
}
void EntityFileParser::onStartComponentField(EntityID entity, const std::string& componentType, const std::string& fieldName, const std::map<std::string, std::string>& attributes)
{
EntityID realEntity = m_EntityIDMapper.at(entity);
ComponentWrapper component = m_World->GetComponent(realEntity, componentType);
auto& field = component.Info.Fields.at(fieldName);
LOG_DEBUG("Field \"%s\" type \"%s\"", fieldName.c_str(), field.Type.c_str());
LOG_DEBUG("Attributes:");
for (auto& kv : attributes) {
LOG_DEBUG("\t%s = %s", kv.first.c_str(), kv.second.c_str());
}
char* data = component.Data + field.Offset;
EntityFile::WriteAttributeData(data, field, attributes);
}
void EntityFileParser::onFieldData(EntityID entity, const std::string& componentType, const std::string& fieldName, const char* fieldData)
{
EntityID realEntity = m_EntityIDMapper.at(entity);
ComponentWrapper component = m_World->GetComponent(realEntity, componentType);
auto& field = component.Info.Fields.at(fieldName);
char* data = component.Data + field.Offset;
EntityFile::WriteValueData(data, field, fieldData);
}
+229
View File
@@ -0,0 +1,229 @@
#include "Core/EntityFilePreprocessor.h"
EntityFilePreprocessor::EntityFilePreprocessor(const EntityFile* entityFile)
: m_EntityFile(entityFile)
{
EntityFileHandler handler;
handler.SetStartComponentCallback(std::bind(&EntityFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2));
m_EntityFile->Parse(&handler);
LOG_DEBUG("___ COMPONENT DEFINITIONS ___");
for (auto& kv : m_ComponentCounts) {
LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second);
}
parseComponentInfo();
for (auto& kv : m_ComponentInfo) {
auto& info = kv.second;
LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta.Annotation.c_str());
LOG_DEBUG("Stride: %i", info.Meta.Stride);
LOG_DEBUG("Allocation: %i", info.Meta.Allocation);
for (auto& kv : info.Fields) {
auto& field = kv.second;
LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type, kv.first.c_str());
}
}
parseDefaults();
}
void EntityFilePreprocessor::RegisterComponents(World* world)
{
for (auto& kv : m_ComponentInfo) {
world->RegisterComponent(kv.second);
}
}
void EntityFilePreprocessor::onStartComponent(EntityID entity, std::string type)
{
//LOG_DEBUG("Component: %s", type.c_str());
m_ComponentCounts[type]++;
}
void EntityFilePreprocessor::parseComponentInfo()
{
using namespace xercesc;
EntityFileXMLErrorHandler errorHandler;
auto grammarPool = m_EntityFile->GrammarPool();
bool whateverTheFuckThisIs;
auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs);
// Find component xsd element declarations
std::cout << "Enumerating components..." << std::endl;
// <xs:element name="ComponentName">
auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION);
for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) {
auto element = static_cast<XSElementDeclaration*>(topLevelElements->item(i));
std::string nameSpace = XS::ToString(element->getNamespace());
if (nameSpace != "components") {
continue;
}
ComponentInfo compInfo;
// Name
compInfo.Name = XS::ToString(element->getName());
// Known allocation
compInfo.Meta.Allocation = m_ComponentCounts[compInfo.Name];
// Annotation
auto componentAnnotation = element->getAnnotation();
if (componentAnnotation != nullptr) {
// Parse annotation XML
char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString());
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, grammarPool);
parser.setErrorHandler(&errorHandler);
parser.parse(annotationInput);
XMLString::release(&annotationString);
auto doc = parser.getDocument();
// TODO: Add allocation estimations from external file on map-to-map basis
// Add allocation estimation(s)
//auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation"));
//for (int i = 0; i < allocationTags->getLength(); ++i) {
// auto allocation = dynamic_cast<DOMElement*>(allocationTags->item(i));
// auto child = allocation->getFirstChild();
// if (child == nullptr) {
// continue;
// }
// XSValue::Status status;
// XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status);
// compInfo.Meta.Allocation += val->fData.fValue.f_int;
//}
// Save documentation string
auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation"));
if (documentationTags->getLength() != 0) {
auto child = documentationTags->item(0)->getFirstChild();
if (child != nullptr) {
compInfo.Meta.Annotation = XS::ToString(child->getNodeValue());
}
}
} else {
LOG_WARNING("Component is missing an annotation!");
}
// <xs:complexType>
auto typeDefinition = element->getTypeDefinition();
if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) {
LOG_ERROR("Type definition wasn't COMPLEX_TYPE! Skipping.");
continue;
}
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
// <xs:all>
auto modelGroupParticle = complexTypeDefinition->getParticle();
if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
LOG_ERROR("Model group particle wasn't TERM_MODELGROUP! Skipping.");
continue;
}
auto modelGroup = modelGroupParticle->getModelGroupTerm();
// <xs:element...
// <xs:attribute...
unsigned int fieldOffset = 0;
auto particles = modelGroup->getParticles();
for (unsigned int i = 0; i < particles->size(); ++i) {
auto particle = particles->elementAt(i);
if (particle->getTermType() != XSParticle::TERM_ELEMENT) {
LOG_ERROR("Particle wasn't TERM_ELEMENT! Skipping.");
continue;
}
auto elementDeclaration = particle->getElementTerm();
std::string name = XS::ToString(elementDeclaration->getName());
std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName());
size_t stride = EntityFile::GetTypeStride(type);
if (stride == 0) {
std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl;
continue;
}
auto& field = compInfo.Fields[name];
field.Type = type;
field.Offset = fieldOffset;
field.Stride = stride;
compInfo.FieldsInOrder.push_back(&field);
fieldOffset += stride;
}
compInfo.Meta.Stride = fieldOffset;
m_ComponentInfo[compInfo.Name] = compInfo;
}
}
void EntityFilePreprocessor::parseDefaults()
{
using namespace xercesc;
EntityFileXMLErrorHandler errorHandler;
for (auto& ci : m_ComponentInfo) {
// Allocate memory for default values
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Meta.Stride]);
memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride);
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
parser.setErrorHandler(&errorHandler);
std::string componentName = ci.first;
LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
parser.parse(defaultsFile.string().c_str());
auto doc = parser.getDocument();
if (doc == nullptr) {
LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str());
continue;
}
// Find the node in the components namespace matching the component name
std::string tagName = "c:" + componentName;
auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName));
if (rootNodes->getLength() == 0) {
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
continue;
}
auto componentElement = dynamic_cast<DOMElement*>(rootNodes->item(0));
// Fill the default value buffer with values
for (auto& kv : ci.second.Fields) {
std::string fieldName = kv.first;
auto& field = kv.second;
auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName));
auto fieldNode = fieldNodes->item(0);
if (fieldNode == nullptr) {
LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str());
continue;
}
auto fieldElement = dynamic_cast<DOMElement*>(fieldNode);
char* data = ci.second.Defaults.get() + field.Offset;
// Handle potential field attributes
if (fieldElement->hasAttributes()) {
std::map<std::string, std::string> attributes;
auto attributeMap = fieldElement->getAttributes();
for (int i = 0; i < attributeMap->getLength(); ++i) {
auto attribItem = attributeMap->item(i);
attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue());
}
EntityFile::WriteAttributeData(data, field, attributes);
}
// Handle potential field values
auto childNode = fieldElement->getFirstChild();
if (childNode != nullptr && childNode->getNodeType() == DOMNode::TEXT_NODE) {
char* cstrValue = XMLString::transcode(childNode->getNodeValue());
EntityFile::WriteValueData(data, field, cstrValue);
XMLString::release(&cstrValue);
}
}
}
}
+140
View File
@@ -0,0 +1,140 @@
#include "Core/EntityFileWriter.h"
#define X(str) XS::ToXMLCh(str)
void EntityFileWriter::WriteWorld(World* world)
{
WriteEntity(world, 0);
}
void EntityFileWriter::WriteEntity(World* world, EntityID entity)
{
using namespace xercesc;
DOMDocument* doc = m_DOMImplementation->createDocument(nullptr, X("Entity"), nullptr);
DOMElement* root = doc->getDocumentElement();
root->setAttribute(X("xmlns:xsi"), X("http://www.w3.org/2001/XMLSchema-instance"));
root->setAttribute(X("xsi:noNamespaceSchemaLocation"), X("../Types/Entity.xsd"));
root->setAttribute(X("xmlns:c"), X("components"));
const std::string& name = world->GetName(entity);
if (!name.empty()) {
root->setAttribute(X("name"), X(name));
}
DOMElement* componentsElement = doc->createElement(X("Components"));
root->appendChild(componentsElement);
appentEntityComponents(componentsElement, world, entity);
DOMElement* childrenElement = doc->createElement(X("Children"));
root->appendChild(childrenElement);
appendEntityChildren(childrenElement, world, entity);
try {
LocalFileFormatTarget* target = new LocalFileFormatTarget(X(m_FilePath.string()));
DOMLSOutput* output = static_cast<DOMImplementationLS*>(m_DOMImplementation)->createLSOutput();
output->setByteStream(target);
m_DOMLSSerializer->write(doc, output);
delete target;
} catch (const std::runtime_error& e) {
LOG_ERROR("Failed to save \"%s\": %s", m_FilePath.c_str(), e.what());
}
doc->release();
}
void EntityFileWriter::appendEntityChildren(xercesc::DOMElement* parentElement, const World* world, EntityID entity)
{
using namespace xercesc;
DOMDocument* doc = parentElement->getOwnerDocument();
auto childrenRange = world->GetEntityChildren().equal_range(entity);
for (auto it = childrenRange.first; it != childrenRange.second; ++it) {
EntityID childEntity = it->second;
DOMElement* entityElement = doc->createElement(X("Entity"));
const std::string& name = world->GetName(childEntity);
if (!name.empty()) {
entityElement->setAttribute(X("name"), X(name));
}
parentElement->appendChild(entityElement);
DOMElement* componentsElement = doc->createElement(X("Components"));
entityElement->appendChild(componentsElement);
appentEntityComponents(componentsElement, world, childEntity);
DOMElement* childrenElement = doc->createElement(X("Children"));
entityElement->appendChild(childrenElement);
appendEntityChildren(childrenElement, world, childEntity);
}
}
void EntityFileWriter::appentEntityComponents(xercesc::DOMElement* parentElement, const World* world, EntityID entity)
{
using namespace xercesc;
DOMDocument* doc = parentElement->getOwnerDocument();
auto& componentPools = world->GetComponentPools();
// Step through all component pools to get an entity's components
// HACK: This is sloooow.
for (auto& kv : componentPools) {
const std::string& componentName = kv.first;
if (!world->HasComponent(entity, componentName)) {
continue;
}
std::string qualifiedComponentName = "c:" + componentName;
DOMElement* componentElement = doc->createElement(X(qualifiedComponentName));
parentElement->appendChild(componentElement);
ComponentWrapper c = kv.second->GetByEntity(entity);
for (auto& kv : c.Info.Fields) {
std::string fieldName = kv.first;
auto& field = kv.second;
// Ignore fields that are equal to the default
// HACK: This is probably sloooooow, but it's okay.
if (memcmp(c.Data + field.Offset, c.Info.Defaults.get() + field.Offset, field.Stride) == 0) {
continue;
}
DOMElement* fieldElement = doc->createElement(X(fieldName));
componentElement->appendChild(fieldElement);
if (field.Type == "Vector") {
const glm::vec3& vec = c[fieldName];
fieldElement->setAttribute(X("X"), X(boost::lexical_cast<std::string>(vec.x)));
fieldElement->setAttribute(X("Y"), X(boost::lexical_cast<std::string>(vec.y)));
fieldElement->setAttribute(X("Z"), X(boost::lexical_cast<std::string>(vec.z)));
} else if (field.Type == "Color") {
const glm::vec4& vec = c[fieldName];
fieldElement->setAttribute(X("R"), X(boost::lexical_cast<std::string>(vec.r)));
fieldElement->setAttribute(X("G"), X(boost::lexical_cast<std::string>(vec.g)));
fieldElement->setAttribute(X("B"), X(boost::lexical_cast<std::string>(vec.b)));
fieldElement->setAttribute(X("A"), X(boost::lexical_cast<std::string>(vec.a)));
} else if (field.Type == "Quaternion") {
const glm::quat& q = c[fieldName];
fieldElement->setAttribute(X("X"), X(boost::lexical_cast<std::string>(q.x)));
fieldElement->setAttribute(X("Y"), X(boost::lexical_cast<std::string>(q.y)));
fieldElement->setAttribute(X("Z"), X(boost::lexical_cast<std::string>(q.z)));
fieldElement->setAttribute(X("W"), X(boost::lexical_cast<std::string>(q.w)));
} else if (field.Type == "int") {
const int& value = c[fieldName];
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
} else if (field.Type == "float") {
const float& value = c[fieldName];
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
} else if (field.Type == "double") {
const double& value = c[fieldName];
fieldElement->appendChild(doc->createTextNode(X(boost::lexical_cast<std::string>(value))));
} else if (field.Type == "bool") {
const bool& value = c[fieldName];
if (value) {
fieldElement->appendChild(doc->createTextNode(X("true")));
} else {
fieldElement->appendChild(doc->createTextNode(X("false")));
}
} else if (field.Type == "string") {
const std::string& value = c[fieldName];
fieldElement->appendChild(doc->createTextNode(X(value)));
}
}
}
}
-481
View File
@@ -1,481 +0,0 @@
#include "Core/EntityXMLFile.h"
#include "Core/World.h"
unsigned int EntityXMLFile::InstanceCount = 0;
EntityXMLFile::EntityXMLFile(std::string path)
: m_EntityFile(path)
{
using namespace xercesc;
if (InstanceCount == 0) {
XMLPlatformUtils::Initialize();
}
InstanceCount++;
m_GrammarPool = new XMLGrammarPoolImpl();
m_ErrorHandler = new EntityParserXMLErrorHandler();
m_DOMParser = new XercesDOMParser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
m_DOMParser->setErrorHandler(m_ErrorHandler);
m_DOMParser->setDoNamespaces(true);
m_DOMParser->setDoXInclude(true);
m_DOMParser->setDoSchema(true);
m_DOMParser->setValidationSchemaFullChecking(true);
m_DOMParser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto);
m_DOMParser->setValidationSchemaFullChecking(true);
m_DOMParser->setValidationConstraintFatal(false);
m_DOMParser->setIncludeIgnorableWhitespace(false);
// Make sure schema grammar is kept after validation
m_DOMParser->cacheGrammarFromParse(true);
// HACK: Use Sax2 parser instead so the entire DOM doesn't have to reside in memory
m_DOMParser->parse(m_EntityFile.c_str());
m_DOMDocument = m_DOMParser->getDocument();
// 1. Fill in ComponentInfo name, fields, default values and metadata from PSVI
parseComponentInfo();
// 2. Parse default value files for those components
parseDefaults();
// 3. Allocate component structures
predictComponentAllocation();
}
EntityXMLFile::~EntityXMLFile()
{
using namespace xercesc;
if (m_DOMParser != nullptr) {
delete m_DOMParser;
}
if (m_ErrorHandler != nullptr) {
delete m_ErrorHandler;
}
if (m_GrammarPool != nullptr) {
delete m_GrammarPool;
}
InstanceCount--;
if (InstanceCount == 0) {
XMLPlatformUtils::Terminate();
}
}
void EntityXMLFile::PopulateWorld(World* world)
{
for (auto& pair : m_ComponentInfo) {
world->RegisterComponent(pair.second);
}
// 4. Parse entity hierarchy
auto root = m_DOMDocument->getDocumentElement();
parseEntityGraph(world, root, 0);
}
void EntityXMLFile::preprocess(std::string inPath, std::string outPath)
{
using namespace xercesc;
static const XMLCh gLS[] = { 'L', 'S', '\0' };
DOMImplementationLS* di = static_cast<DOMImplementationLS*>(DOMImplementationRegistry::getDOMImplementation(gLS));
// Parse the file
DOMLSParser* parser = di->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, nullptr);
DOMConfiguration* config = parser->getDomConfig();
config->setParameter(XMLUni::fgDOMNamespaces, true);
config->setParameter(XMLUni::fgXercesSchema, true);
config->setParameter(XMLUni::fgXercesHandleMultipleImports, true);
config->setParameter(XMLUni::fgXercesSchemaFullChecking, true);
config->setParameter(XMLUni::fgXercesDoXInclude, true);
auto errHandler = new EntityPreprocessorXMLErrorHandler();
config->setParameter(XMLUni::fgDOMErrorHandler, errHandler);
auto source = new LocalFileInputSource(XSTR(inPath.c_str()));
Wrapper4InputSource* domSourceWrapper = new Wrapper4InputSource(source);
DOMDocument* doc = parser->parse(dynamic_cast<DOMLSInput*>(domSourceWrapper));
// Serialize and output the new XML
DOMLSSerializer* writer = di->createLSSerializer();
DOMLSOutput* output = di->createLSOutput();
XMLFormatTarget* formatTarget = new LocalFileFormatTarget(outPath.c_str());
// TODO: MemBufFormatTarget* formatTarget = new MemBufFormatTarget()
output->setByteStream(formatTarget);
writer->write(doc, output);
delete formatTarget;
output->release();
writer->release();
parser->release();
}
void EntityXMLFile::parseComponentInfo()
{
using namespace xercesc;
bool wasChanged;
XSModel* xsModel = m_GrammarPool->getXSModel(wasChanged);
// Find component xsd element declarations
std::cout << "Enumerating components..." << std::endl;
// <xs:element name="ComponentName">
auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION);
for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) {
auto element = static_cast<XSElementDeclaration*>(topLevelElements->item(i));
std::string nameSpace(XSTR(element->getNamespace()));
if (nameSpace != "components") {
continue;
}
ComponentInfo compInfo;
// Name
compInfo.Name = XSTR(element->getName());
// Annotation
auto componentAnnotation = element->getAnnotation();
if (componentAnnotation != nullptr) {
// Parse annotation XML
char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString());
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
parser.setErrorHandler(m_ErrorHandler);
parser.parse(annotationInput);
XMLString::release(&annotationString);
auto doc = parser.getDocument();
// Add allocation estimation(s)
auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation"));
for (int i = 0; i < allocationTags->getLength(); ++i) {
auto allocation = dynamic_cast<DOMElement*>(allocationTags->item(i));
auto child = allocation->getFirstChild();
if (child == nullptr) {
continue;
}
XSValue::Status status;
XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status);
compInfo.Meta.Allocation += val->fData.fValue.f_int;
}
// Save documentation string
auto documentationTags = doc->getElementsByTagName(XSTR("xs:documentation"));
if (documentationTags->getLength() != 0) {
auto child = documentationTags->item(0)->getFirstChild();
if (child != nullptr) {
compInfo.Meta.Annotation = XSTR(child->getNodeValue());
}
}
// TODO: Parse annotation string XML
// compInfo.Meta.Allocation = ...
} else {
std::cout << "Warning: Component is missing an annotation!" << std::endl;
}
// <xs:complexType>
auto typeDefinition = element->getTypeDefinition();
if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) {
std::cerr << "Error: Type definition wasn't COMPLEX_TYPE! Skipping." << std::endl;
continue;
}
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
// <xs:all>
auto modelGroupParticle = complexTypeDefinition->getParticle();
if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
std::cerr << "Error: Model group particle wasn't TERM_MODELGROUP! Skipping." << std::endl;
continue;
}
auto modelGroup = modelGroupParticle->getModelGroupTerm();
// <xs:element...
// <xs:attribute...
unsigned int fieldOffset = 0;
auto particles = modelGroup->getParticles();
for (unsigned int i = 0; i < particles->size(); ++i) {
auto particle = particles->elementAt(i);
if (particle->getTermType() != XSParticle::TERM_ELEMENT) {
std::cerr << "Error: Particle wasn't TERM_ELEMENT! Skipping." << std::endl;
continue;
}
auto elementDeclaration = particle->getElementTerm();
std::string name = XSTR(elementDeclaration->getName());
std::string type = XSTR(elementDeclaration->getTypeDefinition()->getName());
size_t stride = getTypeStride(type);
if (stride == 0) {
std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl;
continue;
}
compInfo.FieldTypes[name] = type;
compInfo.FieldOffsets[name] = fieldOffset;
fieldOffset += getTypeStride(type);
}
compInfo.Meta.Stride = fieldOffset;
m_ComponentInfo[compInfo.Name] = compInfo;
}
}
void EntityXMLFile::parseDefaults()
{
using namespace xercesc;
for (auto& ci : m_ComponentInfo) {
// Allocate memory for default values
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Meta.Stride]);
memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride);
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
parser.setErrorHandler(m_ErrorHandler);
std::string componentName = ci.first;
LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
parser.parse(defaultsFile.string().c_str());
auto doc = parser.getDocument();
if (doc == nullptr) {
LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str());
continue;
}
// Find the node in the components namespace matching the component name
std::string tagName = "c:" + componentName;
auto rootNodes = doc->getElementsByTagName(XSTR(tagName.c_str()));
if (rootNodes->getLength() == 0) {
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
continue;
}
auto componentElement = dynamic_cast<DOMElement*>(rootNodes->item(0));
// Fill the default value buffer with values
for (auto& field : ci.second.FieldOffsets) {
std::string fieldName = field.first;
auto fieldNodes = componentElement->getElementsByTagName(XSTR(fieldName.c_str()));
auto fieldNode = fieldNodes->item(0);
if (fieldNode == nullptr) {
LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str());
continue;
}
auto fieldElement = dynamic_cast<DOMElement*>(fieldNode);
std::string fieldType = ci.second.FieldTypes.at(fieldName);
unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName);
writeData(fieldElement, fieldType, ci.second.Defaults.get() + fieldOffset);
}
}
}
void EntityXMLFile::predictComponentAllocation()
{
using namespace xercesc;
auto root = m_DOMDocument->getDocumentElement();
// Count static instances of components present in entity hierarchy
auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*"));
for (int i = 0; i < components->getLength(); ++i) {
auto component = dynamic_cast<DOMElement*>(components->item(i));
std::string componentName = XSTR(component->getLocalName());
auto& compInfo = m_ComponentInfo.at(componentName);
compInfo.Meta.Allocation += 1;
}
std::cout << "COMPONENT INFO" << std::endl;
for (auto& pair : m_ComponentInfo) {
ComponentInfo& ci = pair.second;
std::cout << "Component: " << ci.Name << " (" << ci.Meta.Annotation << ")" << std::endl;
std::cout << " Allocation: " << ci.Meta.Allocation << std::endl;
std::cout << " Fields:" << std::endl;
// Calculate component size
std::size_t stride = 0;
// Add size of fields
for (auto& field : ci.FieldTypes) {
std::cout << " " << field.second << " " << field.first << " (" << getTypeStride(field.second) << " byte)" << std::endl;
stride += getTypeStride(field.second);
}
std::cout << " Stride: " << ci.Meta.Stride << std::endl;
}
}
void EntityXMLFile::parseEntityGraph(World* world, xercesc::DOMElement* element, EntityID parentEntity)
{
using namespace xercesc;
// Create entity
EntityID entity = world->CreateEntity(parentEntity);
LOG_DEBUG("Created entity %i, parent %i", entity, parentEntity);
// Add components
auto components = m_DOMDocument->evaluate(XSTR("Components/*"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
for (int i = 0; i < components->getSnapshotLength(); i++) {
components->snapshotItem(i);
auto componentElement = dynamic_cast<DOMElement*>(components->getNodeValue());
std::string componentName = XSTR(componentElement->getLocalName());
auto& ci = m_ComponentInfo.at(componentName);
// Attach the component
auto c = world->AttachComponent(entity, componentName);
LOG_DEBUG("Attached %s component", componentName.c_str());
// Write field data
auto fields = componentElement->getChildNodes();
for (int j = 0; j < fields->getLength(); ++j) {
auto fieldNode = fields->item(j);
auto nodeType = fieldNode->getNodeType();
if (nodeType != DOMNode::ELEMENT_NODE) {
continue;
}
auto field = dynamic_cast<DOMElement*>(fields->item(j));
//const XMLCh* value = fields->item(j)->getTextContent();
std::string fieldName(XSTR(field->getLocalName()));
if (ci.FieldTypes.find(fieldName) == ci.FieldTypes.end()) {
std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl;
continue;
}
std::string fieldType = ci.FieldTypes.at(fieldName);
unsigned int fieldOffset = ci.FieldOffsets.at(fieldName);
std::string fieldValue(XSTR(field->getTextContent()));
LOG_DEBUG(" %s %s = %s", fieldType.c_str(), fieldName.c_str(), fieldValue.c_str());
writeData(field, fieldType, c.Data + fieldOffset);
}
}
// Recurse children
auto children = m_DOMDocument->evaluate(XSTR("Children/Entity"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
for (int i = 0; i < children->getSnapshotLength(); i++) {
children->snapshotItem(i);
parseEntityGraph(world, dynamic_cast<DOMElement*>(children->getNodeValue()), entity);
}
//auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*"));
//for (int i = 0; i < components->getLength(); ++i) {
// auto component = dynamic_cast<DOMElement*>(components->item(i));
// std::string componentName = XSTR(component->getLocalName());
// auto& compStore = m_ComponentStore.at(componentName);
// auto& compInfo = compStore.Info;
// char* data = &compStore.Data[compStore.Size*compStore.Stride];
// compStore.Size += 1;
// auto fields = component->getChildNodes();
// for (int j = 0; j < fields->getLength(); ++j) {
// auto field = fields->item(j);
// auto nodeType = field->getNodeType();
// if (nodeType != DOMNode::ELEMENT_NODE) {
// continue;
// }
// //auto field = dynamic_cast<DOMElement*>(fields->item(j));
// //const XMLCh* value = fields->item(j)->getTextContent();
// std::string fieldName = XSTR(field->getLocalName());
// if (compInfo.FieldTypes.find(fieldName) == compInfo.FieldTypes.end()) {
// std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl;
// continue;
// }
// std::string fieldType = compInfo.FieldTypes.at(fieldName);
// unsigned int fieldOffset = compInfo.FieldOffsets.at(fieldName);
// XSValue::DataType dataType = XSValue::getDataType(XSTR(fieldType.c_str()));
// if (dataType == XSValue::DataType::dt_MAXCOUNT) {
// // TODO:
// continue;
// }
// if (dataType == XSValue::DataType::dt_string) {
// char* str = XMLString::transcode(field->getTextContent());
// std::string standardString(str);
// XMLString::release(&str);
// memcpy(&data[fieldOffset], reinterpret_cast<char*>(&standardString), getTypeStride(fieldType));
// } else {
// XSValue::Status status;
// XSValue* val = XSValue::getActualValue(field->getTextContent(), dataType, status);
// memcpy(&data[fieldOffset], reinterpret_cast<char*>(&val->fData.fValue), getTypeStride(fieldType));
// }
// }
//}
//auto entities = m_DOMDocument->getElementsByTagName(XSTR("Entity"));
//for (int i = 0; i < entities->getLength(); ++i) {
// auto entity = dynamic_cast<DOMElement*>(entities->item(i));
// //entity->setIdAttribute()
// std::cout << "ENTITY " << i + 1 << std::endl;
//}
}
std::size_t EntityXMLFile::getTypeStride(std::string typeName)
{
std::map<std::string, size_t> typeStrides{
{ "bool", sizeof(bool) },
{ "int", sizeof(int) },
{ "double", sizeof(double) },
{ "string", sizeof(std::string) },
{ "Vector", sizeof(glm::vec3) },
{ "Quaternion", sizeof(glm::quat) },
{ "Color", sizeof(glm::vec4) }
};
auto it = typeStrides.find(typeName);
return (it != typeStrides.end()) ? it->second : 0;
}
float EntityXMLFile::getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) const
{
using namespace xercesc;
XSValue::Status status;
XSValue* val = XSValue::getActualValue(element->getAttribute(XSTR(attribute)), xercesc::XSValue::DataType::dt_float, status);
if (val == nullptr) {
LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", XSTR(element->getTagName()), attribute);
return 0.f;
} else {
return val->fData.fValue.f_float;
}
}
void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string typeName, char* outData)
{
using namespace xercesc;
XSValue::DataType dataType = XSValue::getDataType(XSTR(typeName.c_str()));
if (dataType == XSValue::DataType::dt_MAXCOUNT) {
if (typeName == "Vector") {
glm::vec3 vec;
vec.x = getFloatAttribute(element, "X");
vec.y = getFloatAttribute(element, "Y");
vec.z = getFloatAttribute(element, "Z");
memcpy(outData, reinterpret_cast<char*>(&vec), getTypeStride(typeName));
} else if (typeName == "Color") {
glm::vec4 vec;
vec.r = getFloatAttribute(element, "R");
vec.g = getFloatAttribute(element, "G");
vec.b = getFloatAttribute(element, "B");
vec.a = getFloatAttribute(element, "A");
memcpy(outData, reinterpret_cast<char*>(&vec), getTypeStride(typeName));
} else if (typeName == "Quaternion") {
glm::quat q;
q.x = getFloatAttribute(element, "X");
q.y = getFloatAttribute(element, "Y");
q.z = getFloatAttribute(element, "Z");
q.w = getFloatAttribute(element, "W");
memcpy(outData, reinterpret_cast<char*>(&q), getTypeStride(typeName));
}
} else if (dataType == XSValue::DataType::dt_string) {
char* str = XMLString::transcode(element->getTextContent());
std::string standardString(str);
new (outData) std::string(str);
XMLString::release(&str);
//memcpy(outData, reinterpret_cast<char*>(&standardString), getTypeStride(typeName));
} else {
XSValue::Status status;
XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status);
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue), getTypeStride(typeName));
}
}
+26 -19
View File
@@ -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;
}
+51
View File
@@ -1,10 +1,17 @@
#include "Core/InputManager.h"
std::vector<unsigned int> InputManager::GLFWCharCallbackQueue;
std::vector<std::pair<double, double>> InputManager::GLFWScrollCallbackQueue;
std::vector<std::string> InputManager::GLFWDropCallbackQueue;
void InputManager::Initialize()
{
// TODO: Gamepad
//m_LastGamepadAxisState = std::array<GamepadAxisState, XUSER_MAX_COUNT>();
//m_LastGamepadButtonState = std::array<GamepadButtonState, XUSER_MAX_COUNT>();
glfwSetCharCallback(m_GLFWWindow, &InputManager::GLFWCharCallback);
glfwSetScrollCallback(m_GLFWWindow, &InputManager::GLFWScrollCallback);
glfwSetDropCallback(m_GLFWWindow, &InputManager::GLFWDropCallback);
EVENT_SUBSCRIBE_MEMBER(m_ELockMouse, &InputManager::OnLockMouse);
EVENT_SUBSCRIBE_MEMBER(m_EUnlockMouse, &InputManager::OnUnlockMouse);
@@ -36,6 +43,15 @@ void InputManager::Update(double dt)
}
}
// Keyboard text input
for (unsigned int& c : GLFWCharCallbackQueue) {
Events::KeyboardChar e;
e.Timestamp = glfwGetTime();
e.Char = c;
m_EventBroker->Publish(e);
}
GLFWCharCallbackQueue.clear();
// Mouse buttons
for (int i = 0; i <= GLFW_MOUSE_BUTTON_LAST; ++i) {
m_CurrentMouseState[i] = glfwGetMouseButton(m_GLFWWindow, i);
@@ -73,6 +89,22 @@ void InputManager::Update(double dt)
m_EventBroker->Publish(e);
}
// Mouse scroll
for (auto& pair : GLFWScrollCallbackQueue) {
Events::MouseScroll e;
std::tie(e.DeltaX, e.DeltaY) = pair;
m_EventBroker->Publish(e);
}
GLFWScrollCallbackQueue.clear();
// File drop
for (auto& path : GLFWDropCallbackQueue) {
Events::FileDropped e;
e.Path = path;
m_EventBroker->Publish(e);
}
GLFWDropCallbackQueue.clear();
// // Lock mouse while holding LMB
// if (m_CurrentMouseState[GLFW_MOUSE_BUTTON_LEFT])
// {
@@ -196,6 +228,25 @@ void InputManager::PublishGamepadButtonIfChanged(int gamepadID, Gamepad::Button
}
}
void InputManager::GLFWCharCallback(GLFWwindow* window, unsigned int c)
{
GLFWCharCallbackQueue.push_back(c);
}
void InputManager::GLFWScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
GLFWScrollCallbackQueue.push_back(std::make_pair(xoffset, yoffset));
}
void InputManager::GLFWDropCallback(GLFWwindow* window, int count, const char* paths[])
{
for (int i = 0; i < count; i++) {
GLFWDropCallbackQueue.push_back(std::string(paths[i]));
}
}
bool InputManager::OnLockMouse(const Events::LockMouse &event)
{
m_MouseLocked = true;
+377
View File
@@ -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;
}
View File
+11 -5
View File
@@ -100,7 +100,7 @@ Resource* ResourceManager::Load(std::string resourceType, std::string resourceNa
LOG_WARNING("Hot-loading resource \"%s\"", resourceName.c_str());
}
return CreateResource(resourceType, resourceName, parent);
return CreateResource(resourceType, resourceName, parent);
}
Resource* ResourceManager::CreateResource(std::string resourceType, std::string resourceName, Resource* parent)
@@ -112,10 +112,16 @@ Resource* ResourceManager::CreateResource(std::string resourceType, std::string
}
// Call the factory function
Resource* resource = facIt->second(resourceName);
// Store IDs
resource->TypeID = GetTypeID(resourceType);
resource->ResourceID = GetNewResourceID(resource->TypeID);
Resource* resource;
try {
resource = facIt->second(resourceName);
// Store IDs
resource->TypeID = GetTypeID(resourceType);
resource->ResourceID = GetNewResourceID(resource->TypeID);
} catch (const std::exception& e) {
resource = nullptr;
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
}
// Cache
m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource;
m_ResourceFromName[resourceName] = resource;
+96 -5
View File
@@ -10,20 +10,65 @@ World::~World()
EntityID World::CreateEntity(EntityID parent /*= 0*/)
{
EntityID newEntity = generateEntityID();
m_EntityParents[newEntity] = parent;
if (parent != 0) {
m_EntityChildren.insert(std::make_pair(parent, newEntity));
if (newEntity == parent) {
LOG_WARNING("Invalid parent #%i of Entity#%i", newEntity, parent);
parent = EntityID_Invalid;
}
m_EntityParents[newEntity] = parent;
m_EntityChildren.insert(std::make_pair(parent, newEntity));
return newEntity;
}
void World::DeleteEntity(EntityID entity)
{
// Delete components
for (auto& pair : m_ComponentPools) {
auto& pool = pair.second;
if (pool->KnowsEntity(entity)) {
auto& c = pool->GetByEntity(entity);
pool->Delete(c);
}
}
// Loop through children
std::vector<EntityID> childrenToDelete;
auto children = m_EntityChildren.equal_range(entity);
for (auto it = children.first; it != children.second; ++it) {
childrenToDelete.push_back(it->second);
}
for (auto& child : childrenToDelete) {
DeleteEntity(child);
}
EntityID parent = m_EntityParents.at(entity);
m_EntityParents.erase(entity);
auto parentChildren = m_EntityChildren.equal_range(parent);
for (auto it = parentChildren.first; it != parentChildren.second; ++it) {
if (it->second == entity) {
m_EntityChildren.erase(it);
break;
}
}
// Erase potential name
m_EntityNames.erase(entity);
}
bool World::ValidEntity(EntityID entity) const
{
return m_EntityParents.find(entity) != m_EntityParents.end();
}
void World::RegisterComponent(ComponentInfo& ci)
{
m_ComponentPools[ci.Name] = new ComponentPool(ci);
if (m_ComponentPools.find(ci.Name) == m_ComponentPools.end()) {
m_ComponentPools[ci.Name] = new ComponentPool(ci);
}
}
ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType)
{
// TODO: Allocate dynamic pool if component isn't registered
ComponentPool* pool = m_ComponentPools.at(componentType);
const ComponentInfo& ci = pool->ComponentInfo();
@@ -35,24 +80,70 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy
return c;
}
bool World::HasComponent(EntityID entity, std::string componentType) const
{
ComponentPool* pool = m_ComponentPools.at(componentType);
return pool->KnowsEntity(entity);
}
ComponentWrapper World::GetComponent(EntityID entity, std::string componentType)
{
ComponentPool* pool = m_ComponentPools.at(componentType);
return pool->GetByEntity(entity);
}
void World::DeleteComponent(EntityID entity, std::string componentType)
{
ComponentPool* pool = m_ComponentPools.at(componentType);
ComponentWrapper c = pool->GetByEntity(entity);
return pool->Delete(c);
}
const ComponentPool* World::GetComponents(std::string componentType)
{
auto it = m_ComponentPools.find(componentType);
return (it != m_ComponentPools.end()) ? it->second : nullptr;
}
EntityID World::GetParent(EntityID entity)
{
return m_EntityParents.at(entity);
}
void World::SetParent(EntityID entity, EntityID parent)
{
EntityID lastParent = m_EntityParents.at(entity);
auto parentChildren = m_EntityChildren.equal_range(lastParent);
for (auto it = parentChildren.first; it != parentChildren.second; it++) {
if (it->second == entity) {
m_EntityChildren.erase(it);
break;
}
}
m_EntityParents[entity] = parent;
m_EntityChildren.insert(std::make_pair(parent, entity));
}
void World::SetName(EntityID entity, const std::string& name)
{
m_EntityNames[entity] = name;
}
std::string World::GetName(EntityID entity) const
{
if (entity == EntityID_Invalid) {
return "EntityID_Invalid";
}
auto it = m_EntityNames.find(entity);
if (it != m_EntityNames.end()) {
return it->second;
} else {
return std::string();
}
}
EntityID World::generateEntityID()
{
// TODO: Make EntityID generation smarter
+713
View File
@@ -0,0 +1,713 @@
#include "Editor/EditorSystem.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui/imgui_internal.h>
EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer)
: ImpureSystem(eventBroker)
, m_Renderer(renderer)
{
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
m_Enabled = config->Get<bool>("Debug.EditorEnabled", false);
m_Visible = m_Enabled;
m_DefaultEntityDir = boost::filesystem::path("Schema") / boost::filesystem::path("Entities");
if (!m_Enabled) {
return;
}
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &EditorSystem::OnInputCommand);
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &EditorSystem::OnMousePress);
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &EditorSystem::OnMouseRelease);
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &EditorSystem::OnMouseMove);
EVENT_SUBSCRIBE_MEMBER(m_EFileDropped, &EditorSystem::OnFileDropped);
}
void EditorSystem::Update(World* world, double dt)
{
m_World = world;
if (!m_Enabled) {
return;
}
if (!m_Visible) {
return;
}
Picking();
updateWidget();
drawUI(world, dt);
// Clear drop queue if it wasn't handled by any UI element
if (!m_LastDroppedFile.empty()) {
m_LastDroppedFile = "";
}
}
boost::filesystem::path EditorSystem::openDialog(boost::filesystem::path defaultPath)
{
namespace bfs = boost::filesystem;
auto absolutePath = bfs::absolute(defaultPath);
nfdchar_t* outPath = nullptr;
nfdresult_t result = NFD_OpenDialog(NULL, absolutePath.string().c_str(), &outPath);
if (result == NFD_ERROR) {
LOG_ERROR("NFD Error: %s", NFD_GetError());
return bfs::path();
}
return bfs::absolute(outPath);
}
boost::filesystem::path EditorSystem::saveDialog(boost::filesystem::path defaultPath)
{
namespace bfs = boost::filesystem;
auto absolutePath = bfs::absolute(defaultPath);
nfdchar_t* outPath = nullptr;
nfdresult_t result = NFD_SaveDialog(NULL, absolutePath.string().c_str(), &outPath);
if (result == NFD_ERROR) {
LOG_ERROR("NFD Error: %s", NFD_GetError());
return bfs::path();
}
return bfs::absolute(outPath);
}
bool EditorSystem::OnInputCommand(const Events::InputCommand& e)
{
if (e.Command == "ToggleEditor" && e.Value > 0) {
m_Visible = !m_Visible;
}
if (e.Command == "EditorToolMove" && e.Value > 0) {
setWidgetMode(WidgetMode::Translate);
}
if (e.Command == "EditorToolRotate" && e.Value > 0) {
setWidgetMode(WidgetMode::Rotate);
}
if (e.Command == "EditorToolScale" && e.Value > 0) {
setWidgetMode(WidgetMode::Scale);
}
if (e.Command == "EditorToggleTransformSpace" && e.Value > 0) {
if (m_WidgetSpace == WidgetSpace::Global) {
setWidgetSpace(WidgetSpace::Local);
} else if (m_WidgetSpace == WidgetSpace::Local) {
setWidgetSpace(WidgetSpace::Global);
}
}
return true;
}
bool EditorSystem::OnMousePress(const Events::MousePress& e)
{
if (e.Button == GLFW_MOUSE_BUTTON_RIGHT) {
m_PickingQueue.push_back(glm::vec2((int)e.X, (int)e.Y));
}
return true;
}
bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
{
if (m_Widget == EntityID_Invalid) {
return false;
}
if (m_Selection == EntityID_Invalid) {
return false;
}
if (m_Selection == m_Widget) {
return false;
}
// TODO: No widgets for root entity until widgets reside in thier own world,
// or the widgets will move relative to the root entity being moved, which is WEEEIRD.
if (m_Selection == 0) {
return false;
}
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
glm::vec3 widgetOrientation = widgetTransform["Orientation"];
glm::quat totalOrientation = m_Camera->Orientation() * glm::inverse(glm::quat(widgetOrientation));
int width;
int height;
glfwGetFramebufferSize(m_Renderer->Window(), &width, &height);
Rectangle res(width, height);
glm::vec2 delta2(res.Width / 2.f + e.DeltaX, res.Height / 2.f + -e.DeltaY);
glm::vec3 deltaWorld = ScreenCoords::ToWorldPos(
delta2,
m_WidgetPickingDepth,
res,
m_Camera->ProjectionMatrix(),
glm::toMat4(glm::inverse(totalOrientation))
);
glm::vec3 origin = ScreenCoords::ToWorldPos(
glm::vec2(res.Width / 2.f, res.Height / 2.f),
m_WidgetPickingDepth,
res,
m_Camera->ProjectionMatrix(),
glm::toMat4(glm::inverse(totalOrientation))
);
deltaWorld = deltaWorld - origin;
glm::vec3 movement = deltaWorld * m_WidgetCurrentAxis;
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
if (m_WidgetMode == WidgetMode::Translate) {
if (m_WidgetSpace == WidgetSpace::Global) {
EntityID parent = m_World->GetParent(m_Selection);
glm::quat inverseParentOrientation;
//if (parent != 0) {
inverseParentOrientation = glm::inverse(Transform::AbsoluteOrientation(m_World, parent));
//}
(glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Position"] += inverseParentOrientation * movement;
} else if (m_WidgetSpace == WidgetSpace::Local) {
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
(glm::vec3&)selectionTransform["Position"] += glm::quat((glm::vec3)selectionTransform["Orientation"]) * movement;
}
} else if (m_WidgetMode == WidgetMode::Rotate) {
glm::vec3 finalMovement;
finalMovement.x = -deltaWorld.y * m_WidgetCurrentAxis.x;
finalMovement.y = deltaWorld.x * m_WidgetCurrentAxis.y;
finalMovement.z = deltaWorld.y * m_WidgetCurrentAxis.z;
if (m_WidgetSpace == WidgetSpace::Global) {
EntityID parent = m_World->GetParent(m_Selection);
glm::quat parentOrientation;
//if (parent != 0) {
// parentOrientation = RenderSystem::AbsoluteOrientation(m_World, parent);
//}
glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"];
glm::quat currentOrientation = Transform::AbsoluteOrientation(m_World, m_Selection);
//glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation);
glm::quat deltaOrientation(finalMovement);
selectionOrientation = glm::eulerAngles(glm::inverse(parentOrientation) * (deltaOrientation * currentOrientation));
} else if (m_WidgetSpace == WidgetSpace::Local) {
glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"];
glm::quat currentOrientation(selectionOrientation);
glm::quat deltaOrientation(finalMovement);
selectionOrientation = glm::eulerAngles(currentOrientation * deltaOrientation);
}
} else if (m_WidgetMode == WidgetMode::Scale) {
glm::vec3& scaleX = m_World->GetComponent(m_WidgetX, "Transform")["Scale"];
glm::vec3& scaleY = m_World->GetComponent(m_WidgetY, "Transform")["Scale"];
glm::vec3& scaleZ = m_World->GetComponent(m_WidgetZ, "Transform")["Scale"];
if (m_WidgetCurrentAxis.x > 0 && m_WidgetCurrentAxis.y > 0 && m_WidgetCurrentAxis.z > 0) {
float movementLength = glm::length(movement);
float dot = glm::dot((glm::vec3)widgetOrientation, movement);
movement = glm::vec3(movementLength) * glm::sign(dot);
(glm::vec3&)m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] += movement;
}
if (m_WidgetCurrentAxis.x > 0) {
scaleX.x += movement.x;
}
if (m_WidgetCurrentAxis.y > 0) {
scaleY.y += movement.y;
}
if (m_WidgetCurrentAxis.z > 0) {
scaleZ.z += movement.z;
}
(glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Scale"] += movement;
}
}
/*LOG_DEBUG("DELTA %f", e.DeltaX);
if (e.X < 0) {
glfwSetCursorPos(m_Renderer->Window(), width - 1, e.Y);
}
if (e.X >= width) {
glfwSetCursorPos(m_Renderer->Window(), 0, e.Y);
}*/
return true;
}
bool EditorSystem::OnMouseRelease(const Events::MouseRelease& e)
{
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
m_WidgetCurrentAxis = glm::vec3(0.f);
//setWidgetMode(m_WidgetMode);
}
return true;
}
void EditorSystem::Picking()
{
for (auto& pos : m_PickingQueue) {
auto result = m_Renderer->Pick(pos);
EntityID entity = result.Entity;
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
// ???
} else {
LOG_INFO("Selected %i", entity);
if (entity != EntityID_Invalid) {
EntityID parent = m_World->GetParent(entity);
m_Camera = result.Camera;
if (parent == m_Widget) {
m_WidgetCurrentAxis = glm::vec3(
(entity == m_WidgetX) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneY || entity == m_WidgetPlaneZ),
(entity == m_WidgetY) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneZ),
(entity == m_WidgetZ) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneY)
);
m_WidgetPickingDepth = result.Depth;
//auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
//auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
//widgetTransform["Position"] = (glm::vec3)selectionTransform["Position"];
} else {
ImGui::SetActiveID(0, nullptr);
if (m_WidgetMode == WidgetMode::None) {
m_WidgetMode = WidgetMode::Translate;
}
setWidgetMode(m_WidgetMode);
m_Selection = entity;
}
}
}
}
m_PickingQueue.clear();
};
bool EditorSystem::OnFileDropped(const Events::FileDropped& e)
{
m_LastDroppedFile = boost::filesystem::path(e.Path).lexically_relative(boost::filesystem::current_path()).string();
std::replace(m_LastDroppedFile.begin(), m_LastDroppedFile.end(), '\\', '/');
return true;
}
void EditorSystem::createWidget()
{
if (m_Widget == EntityID_Invalid) {
m_Widget = m_World->CreateEntity();
m_World->AttachComponent(m_Widget, "Transform");
m_WidgetX = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetX, "Transform");
m_World->AttachComponent(m_WidgetX, "Model");
m_WidgetPlaneX = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetPlaneX, "Transform");
m_World->AttachComponent(m_WidgetPlaneX, "Model");
m_World->GetComponent(m_WidgetPlaneX, "Model")["Resource"] = "Models/WidgetPlaneX.obj";
m_WidgetY = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetY, "Transform");
m_World->AttachComponent(m_WidgetY, "Model");
m_WidgetPlaneY = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetPlaneY, "Transform");
m_World->AttachComponent(m_WidgetPlaneY, "Model");
m_World->GetComponent(m_WidgetPlaneY, "Model")["Resource"] = "Models/WidgetPlaneY.obj";
m_WidgetZ = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetZ, "Transform");
m_World->AttachComponent(m_WidgetZ, "Model");
m_WidgetPlaneZ = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetPlaneZ, "Transform");
m_World->AttachComponent(m_WidgetPlaneZ, "Model");
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Resource"] = "Models/WidgetPlaneZ.obj";
m_WidgetOrigin = m_World->CreateEntity(m_Widget);
m_World->AttachComponent(m_WidgetOrigin, "Transform");
m_World->AttachComponent(m_WidgetOrigin, "Model");
setWidgetMode(WidgetMode::None);
}
}
void EditorSystem::updateWidget()
{
if (m_Widget == EntityID_Invalid) {
return;
}
if (m_Selection == m_Widget) {
return;
}
if (m_Selection != EntityID_Invalid) {
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
glm::vec3 selectionPosition = Transform::AbsolutePosition(m_World, m_Selection);
widgetTransform["Position"] = selectionPosition;
if (m_WidgetSpace == WidgetSpace::Local) {
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
}
}
}
void EditorSystem::setWidgetMode(WidgetMode newMode)
{
if (m_Widget == EntityID_Invalid) {
return;
}
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
widgetTransform["Orientation"] = glm::vec3(0.f);
m_World->GetComponent(m_WidgetX, "Transform")["Scale"] = glm::vec3(1.f);
m_World->GetComponent(m_WidgetPlaneX, "Model")["Visible"] = false;
m_World->GetComponent(m_WidgetY, "Transform")["Scale"] = glm::vec3(1.f);
m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = false;
m_World->GetComponent(m_WidgetZ, "Transform")["Scale"] = glm::vec3(1.f);
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = false;
m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] = glm::vec3(1.f);
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false;
if (newMode == WidgetMode::Translate) {
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/TranslationWidgetX.obj";
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/TranslationWidgetY.obj";
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/TranslationWidgetZ.obj";
// Temporarily disabled for local space until I can figure out what's wrong with the math
if (m_WidgetSpace != WidgetSpace::Local) {
m_World->GetComponent(m_WidgetPlaneX, "Model")["Visible"] = true;
m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = true;
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = true;
}
if (m_Selection != EntityID_Invalid) {
if (m_WidgetSpace == WidgetSpace::Local) {
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
}
}
} else if (newMode == WidgetMode::Scale) {
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/ScaleWidgetX.obj";
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/ScaleWidgetY.obj";
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/ScaleWidgetZ.obj";
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = true;
m_World->GetComponent(m_WidgetOrigin, "Model")["Resource"] = "Models/ScaleWidgetOrigin.obj";
if (m_Selection != EntityID_Invalid) {
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
}
} else if (newMode == WidgetMode::Rotate) {
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/RotationWidgetX.obj";
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/RotationWidgetY.obj";
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/RotationWidgetZ.obj";
if (m_Selection != EntityID_Invalid) {
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
if (m_WidgetSpace == WidgetSpace::Local) {
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
}
}
}
m_WidgetMode = newMode;
}
void EditorSystem::setWidgetSpace(WidgetSpace space)
{
m_WidgetSpace = space;
setWidgetMode(m_WidgetMode);
}
void EditorSystem::drawUI(World* world, double dt)
{
namespace bfs = boost::filesystem;
ImGui::ShowTestWindow();
//ImGui::ShowStyleEditor();
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
//if (ImGui::MenuItem("New")) { }
if (ImGui::MenuItem("Import", "Ctrl+O")) {
fileImport(world);
}
if (ImGui::MenuItem("Save", "Ctrl+S")) {
fileSave(world);
}
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) {
fileSaveAs(world);
}
ImGui::Separator();
if (ImGui::MenuItem("Close Editor", "F1")) { }
ImGui::EndMenu();
}
ImGui::SameLine();
if (ImGui::Button("Move")) {
setWidgetMode(WidgetMode::Translate);
}
ImGui::SameLine();
if (ImGui::Button("Rotate")) {
setWidgetMode(WidgetMode::Rotate);
}
ImGui::SameLine();
if (ImGui::Button("Scale")) {
setWidgetMode(WidgetMode::Scale);
}
ImGui::SameLine();
if (m_WidgetSpace == WidgetSpace::Global) {
if (ImGui::Button("(Global)")) {
setWidgetSpace(WidgetSpace::Local);
}
} else if (m_WidgetSpace == WidgetSpace::Local) {
if (ImGui::Button("(Local)")) {
setWidgetSpace(WidgetSpace::Global);
}
}
ImGui::EndMainMenuBar();
}
std::string title = std::string("Components #") + std::to_string(m_Selection) + std::string("###Components");
if (ImGui::Begin(title.c_str())) {
if (m_Selection != EntityID_Invalid) {
auto& pools = world->GetComponentPools();
std::vector<const char*> componentTypes;
for (auto& pair : pools) {
// Only add components the entity doesn't already have
if (!pair.second->KnowsEntity(m_Selection)) {
componentTypes.push_back(pair.first.c_str());
}
}
int item = -1;
ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() - 5.f);
if (ImGui::Combo("", &item, componentTypes.data(), componentTypes.size())) {
if (item != -1) {
std::string chosenType = std::string(componentTypes.at(item));
world->AttachComponent(m_Selection, chosenType);
}
}
ImGui::PopItemWidth();
for (auto& pair : pools) {
const std::string& componentType = pair.first;
auto pool = pair.second;
if (!pool->KnowsEntity(m_Selection)) {
continue;
}
auto& ci = pool->ComponentInfo();
bool deletePressed = createDeleteButton(componentType);
if (deletePressed) {
world->DeleteComponent(m_Selection, componentType);
continue;
}
if (ImGui::CollapsingHeader(componentType.c_str())) {
if (!ci.Meta.Annotation.empty()) {
ImGui::Text(ci.Meta.Annotation.c_str());
}
auto& component = world->GetComponent(m_Selection, componentType);
for (auto& kv : ci.Fields) {
const std::string& fieldName = kv.first;
auto& field = kv.second;
ImGui::PushID(fieldName.c_str());
if (field.Type == "Vector") {
auto& val = component.Property<glm::vec3>(fieldName);
if (fieldName == "Scale") {
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits<float>::max());
} else if (fieldName == "Orientation") {
glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi<float>()));
if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi<float>())) {
val = tempVal;
}
} else {
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
}
} else if (field.Type == "Color") {
auto& val = component.Property<glm::vec4>(fieldName);
ImGui::ColorEdit4("", glm::value_ptr(val), true);
} else if (field.Type == "string") {
std::string& val = component.Property<std::string>(fieldName);
char tempString[1024];
memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString)));
if (ImGui::InputText("", tempString, sizeof(tempString))) {
val = std::string(tempString);
LOG_DEBUG("%s::%s changed!", componentType.c_str(), fieldName.c_str());
}
// DROP STUFF
if (ImGui::IsItemHovered() && !m_LastDroppedFile.empty()) {
val = m_LastDroppedFile;
m_LastDroppedFile = "";
}
} else if (field.Type == "double") {
float tempVal = static_cast<float>(component.Property<double>(fieldName));
if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) {
component.SetProperty(fieldName, static_cast<double>(tempVal));
}
} else if (field.Type == "bool") {
auto& val = component.Property<bool>(fieldName);
ImGui::Checkbox("", &val);
} else {
ImGui::TextDisabled(field.Type.c_str());
}
ImGui::PopID();
ImGui::SameLine();
ImGui::Text(fieldName.c_str());
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("field annotation goes here");
}
}
}
}
}
}
ImGui::End();
if (ImGui::Begin("Entities")) {
auto entityChildren = world->GetEntityChildren();
std::function<void(EntityID)> recurse = [&](EntityID parent) {
auto range = entityChildren.equal_range(parent);
for (auto it = range.first; it != range.second; it++) {
if (createEntityNode(world, it->second)) {
recurse(it->second);
ImGui::TreePop();
}
}
};
recurse(EntityID_Invalid);
}
ImGui::End();
}
bool EditorSystem::createEntityNode(World* world, EntityID entity)
{
// HACK: Don't show the widget entities in the entity tree
if (entity == m_Widget) {
return false;
}
ImVec2 pos = ImGui::GetCursorScreenPos();
float width = ImGui::GetContentRegionAvailWidth();
ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13));
auto window = ImGui::GetCurrentWindow();
if (m_Selection == entity) {
const ImU32 col = window->Color(ImGuiCol_HeaderActive);
window->DrawList->AddRectFilled(bb.Min, bb.Max, col);
}
ImGuiID id = window->GetID((std::string("#SelectButton") + std::to_string(entity)).c_str());
bool hovered = false;
bool held = false;
if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) {
m_Selection = entity;
}
if (held) {
ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0);
if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) {
if (m_UIDraggingEntity == EntityID_Invalid) {
m_UIDraggingEntity = entity;
LOG_DEBUG("Started drag of entity %i", m_UIDraggingEntity);
}
ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0));
ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings);
ImGui::Text("#%i", m_UIDraggingEntity);
ImGui::End();
}
}
ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once);
std::string nodeTitle;
const std::string& entityName = world->GetName(entity);
if (!entityName.empty()) {
nodeTitle = entityName;
} else {
nodeTitle = std::string("#") + std::to_string(entity);
}
if (ImGui::TreeNode(nodeTitle.c_str())) {
if (m_UIDraggingEntity != EntityID_Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) {
LOG_DEBUG("Changed parent of %i to %i", m_UIDraggingEntity, entity);
changeParent(m_UIDraggingEntity, entity);
m_UIDraggingEntity = EntityID_Invalid;
}
if (ImGui::BeginPopupContextItem("item context menu")) {
if (ImGui::Button("Add")) {
EntityID newEntity = world->CreateEntity(entity);
world->AttachComponent(newEntity, "Transform");
}
ImGui::SameLine();
if (ImGui::Button("Delete")) {
world->DeleteEntity(entity);
ImGui::CloseCurrentPopup();
if (!world->ValidEntity(m_Selection)) {
m_Selection = EntityID_Invalid;
}
}
ImGui::EndPopup();
}
return true;
} else {
return false;
}
}
bool EditorSystem::createDeleteButton(std::string componentType)
{
float width = ImGui::GetContentRegionAvailWidth();
ImGuiWindow* window = ImGui::GetCurrentWindow();
auto pos = ImGui::GetCursorScreenPos() + ImVec2(width - 14.f, 1);
ImRect bb = ImRect(pos, pos + ImVec2(14.f, 14.f));
std::string idString = "#DELETE";
idString += componentType;
ImGuiID id = window->GetID(idString.c_str());
bool hovered;
bool held;
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held);
//ImU32 col = window->Color((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
ImU32 col = window->Color((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
window->DrawList->AddCircleFilled(bb.GetCenter(), 7.f, col, 16);
return pressed;
}
void EditorSystem::changeParent(EntityID entity, EntityID newParent)
{
if (entity == newParent) {
return;
}
// An entity can't be a child to one of its own children
auto children = m_World->GetEntityChildren().equal_range(entity);
for (auto it = children.first; it != children.second; it++) {
if (it->second == newParent) {
return;
}
}
m_World->SetParent(entity, newParent);
}
void EditorSystem::fileImport(World* world)
{
m_CurrentFile = openDialog(m_DefaultEntityDir);
auto file = ResourceManager::Load<EntityFile>(m_CurrentFile.string());
EntityFilePreprocessor fpp(file);
fpp.RegisterComponents(world);
EntityFileParser fp(file);
fp.MergeEntities(world);
createWidget();
updateWidget();
}
void EditorSystem::fileSave(World* world)
{
if (boost::filesystem::exists(m_CurrentFile)) {
// HACK: Delete the widgets so they don't appear in the saved file
world->DeleteEntity(m_Widget);
m_Widget = EntityID_Invalid;
EntityFileWriter writer(m_CurrentFile.string());
writer.WriteWorld(world);
createWidget();
} else {
fileSaveAs(world);
}
}
void EditorSystem::fileSaveAs(World* world)
{
auto filePath = saveDialog(m_DefaultEntityDir);
if (filePath.empty()) {
return;
}
// HACK: Delete the widgets so they don't appear in the saved file
world->DeleteEntity(m_Widget);
m_Widget = EntityID_Invalid;
EntityFileWriter writer(filePath.string());
writer.WriteWorld(world);
createWidget();
}
+112
View File
@@ -0,0 +1,112 @@
#include "Input/InputProxy.h"
#include "Input/InputHandler.h"
InputProxy::InputProxy(EventBroker* eventBroker)
: m_EventBroker(eventBroker)
{
EVENT_SUBSCRIBE_MEMBER(m_EBindOrigin, &InputProxy::OnBindOrigin);
}
InputProxy::~InputProxy()
{
for (auto& handler : m_Handlers) {
delete handler;
}
}
void InputProxy::LoadBindings(std::string file)
{
auto config = ResourceManager::Load<ConfigFile>(file);
for (auto& origin : config->GetAll<std::string>("Bindings")) {
Events::BindOrigin e;
e.Origin = origin.first;
e.Command = origin.second;
e.Value = 1.f;
if (!e.Command.empty()) {
char prefix = e.Command.at(0);
if (prefix == '+' || prefix == '-') {
e.Command = e.Command.substr(1);
if (prefix == '-') {
e.Value *= -1.f;
}
}
OnBindOrigin(e);
}
}
}
void InputProxy::Update(double dt)
{
m_EventBroker->Process<InputProxy>();
m_EventBroker->Process<InputHandler>();
for (auto& handler : m_Handlers) {
handler->Update(dt);
}
}
void InputProxy::Process()
{
for (auto& pair : m_CommandHandlers) {
const std::string& command = pair.first;
auto handlers = pair.second;
m_CurrentCommandValues[command] = 0.f;
for (auto& handler : handlers) {
m_CurrentCommandValues[command] += handler->GetCommandValue(command);
}
auto last = m_LastCommandValues.find(command);
float currentValue = m_CurrentCommandValues[command];
if (last == m_LastCommandValues.end() || last->second != currentValue) {
Events::InputCommand e;
e.PlayerID = -1;
e.Command = command;
e.Value = currentValue;
m_EventBroker->Publish(e);
LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID);
m_LastCommandValues[command] = currentValue;
}
}
// Accumulate the input values of all unique commands published by input handlers
for (auto& pair : m_CommandQueue) {
Events::InputCommand e;
e.PlayerID = pair.first.first;
e.Command = pair.first.second;
e.Value = 0;
for (auto& value : pair.second) {
e.Value += value;
}
//e.Value = std::max(-1.f, std::min(e.Value, 1.f));
m_EventBroker->Publish(e);
LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID);
}
m_CommandQueue.clear();
}
void InputProxy::Publish(const Events::InputCommand& e)
{
auto key = std::make_pair(e.PlayerID, e.Command);
m_CommandQueue[key].push_back(e.Value);
}
bool InputProxy::OnBindOrigin(const Events::BindOrigin& e)
{
bool originBound = false;
for (auto& handler : m_Handlers) {
bool result = handler->BindOrigin(e.Origin, e.Command, e.Value);
if (result) {
m_CommandHandlers[e.Command].insert(handler);
m_LastCommandValues[e.Command] = 0.f;
if (originBound) {
LOG_WARNING("Multiple handlers responded to binding input origin \"%s\"!", e.Origin.c_str());
}
originBound = true;
}
}
if (!originBound) {
LOG_ERROR("No input handler responded to binding input origin \"%s\"!", e.Origin.c_str());
}
return originBound;
}
-221
View File
@@ -1,221 +0,0 @@
#include "PrecompiledHeader.h"
#include "Input/InputSystem.h"
#include "Core/World.h"
void Systems::InputSystem::RegisterComponents(ComponentFactory* cf)
{
}
void Systems::InputSystem::Initialize()
{
// Subscribe to events
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &Systems::InputSystem::OnKeyDown);
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &Systems::InputSystem::OnKeyUp);
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &Systems::InputSystem::OnMousePress);
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &Systems::InputSystem::OnMouseRelease);
EVENT_SUBSCRIBE_MEMBER(m_EGamepadAxis, &Systems::InputSystem::OnGamepadAxis);
EVENT_SUBSCRIBE_MEMBER(m_EGamepadButtonDown, &Systems::InputSystem::OnGamepadButtonDown);
EVENT_SUBSCRIBE_MEMBER(m_EGamepadButtonUp, &Systems::InputSystem::OnGamepadButtonUp);
EVENT_SUBSCRIBE_MEMBER(m_EBindKey, &Systems::InputSystem::OnBindKey);
EVENT_SUBSCRIBE_MEMBER(m_EBindMouseButton, &Systems::InputSystem::OnBindMouseButton);
EVENT_SUBSCRIBE_MEMBER(m_EBindGamepadAxis, &Systems::InputSystem::OnBindGamepadAxis);
EVENT_SUBSCRIBE_MEMBER(m_EBindGamepadButton, &Systems::InputSystem::OnBindGamepadButton);
}
void Systems::InputSystem::Update(double dt)
{
}
bool Systems::InputSystem::OnKeyDown(const Events::KeyDown &event)
{
auto range = m_KeyBindings.equal_range(event.KeyCode);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandKeyboardValues[command][event.KeyCode] = value;
PublishCommand(1, command, GetCommandTotalValue(command));
}
return true;
}
bool Systems::InputSystem::OnKeyUp(const Events::KeyUp &event)
{
auto range = m_KeyBindings.equal_range(event.KeyCode);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandKeyboardValues[command][event.KeyCode] = 0;
PublishCommand(1, command, GetCommandTotalValue(command));;
}
return true;
}
bool Systems::InputSystem::OnMousePress(const Events::MousePress &event)
{
auto range = m_MouseButtonBindings.equal_range(event.Button);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandMouseButtonValues[command][event.Button] = value;
PublishCommand(1, command, GetCommandTotalValue(command));
}
return true;
}
bool Systems::InputSystem::OnMouseRelease(const Events::MouseRelease &event)
{
auto range = m_MouseButtonBindings.equal_range(event.Button);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandMouseButtonValues[command][event.Button] = 0;
PublishCommand(1, command, GetCommandTotalValue(command));
}
return true;
}
bool Systems::InputSystem::OnGamepadAxis(const Events::GamepadAxis &event)
{
auto range = m_GamepadAxisBindings.equal_range(event.Axis);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandGamepadAxisValues[command][event.Axis] = event.Value * value;
PublishCommand(event.GamepadID + 1, command, GetCommandTotalValue(command));
}
return true;
}
bool Systems::InputSystem::OnGamepadButtonDown(const Events::GamepadButtonDown &event)
{
auto range = m_GamepadButtonBindings.equal_range(event.Button);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandGamepadButtonValues[command][event.Button] = value;
PublishCommand(event.GamepadID + 1, command, GetCommandTotalValue(command));
}
return true;
}
bool Systems::InputSystem::OnGamepadButtonUp(const Events::GamepadButtonUp &event)
{
auto range = m_GamepadButtonBindings.equal_range(event.Button);
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
std::string command;
float value;
std::tie(command, value) = bindingIt->second;
m_CommandGamepadButtonValues[command][event.Button] = 0;
PublishCommand(event.GamepadID + 1, command, GetCommandTotalValue(command));
}
return true;
}
bool Systems::InputSystem::OnBindKey(const Events::BindKey &event)
{
if (event.Command.empty()) {
return false;
}
m_KeyBindings.insert(std::make_pair(event.KeyCode, std::make_tuple(event.Command, event.Value)));
LOG_DEBUG("Input: Bound key %i to %s", event.KeyCode, event.Command.c_str());
return true;
}
bool Systems::InputSystem::OnBindMouseButton(const Events::BindMouseButton &event)
{
if (event.Command.empty()) {
return false;
}
m_MouseButtonBindings.insert(std::make_pair(event.Button, std::make_tuple(event.Command, event.Value)));
LOG_DEBUG("Input: Bound mouse button %i to %s", event.Button, event.Command.c_str());
return true;
}
bool Systems::InputSystem::OnBindGamepadAxis(const Events::BindGamepadAxis &event)
{
if (event.Command.empty()) {
return false;
}
m_GamepadAxisBindings.insert(std::make_pair(event.Axis, std::make_tuple(event.Command, event.Value)));
LOG_DEBUG("Input: Bound gamepad axis %i to %s", event.Axis, event.Command.c_str());
return true;
}
bool Systems::InputSystem::OnBindGamepadButton(const Events::BindGamepadButton &event)
{
if (event.Command.empty()) {
return false;
}
m_GamepadButtonBindings.insert(std::make_pair(event.Button, std::make_tuple(event.Command, event.Value)));
LOG_DEBUG("Input: Bound gamepad axis %i to %s", event.Button, event.Command.c_str());
return true;
}
float Systems::InputSystem::GetCommandTotalValue(std::string command)
{
float value = 0.f;
auto keyboardIt = m_CommandKeyboardValues.find(command);
if (keyboardIt != m_CommandKeyboardValues.end()) {
for (auto &key : keyboardIt->second) {
value += key.second;
}
}
auto mouseButtonIt = m_CommandMouseButtonValues.find(command);
if (mouseButtonIt != m_CommandMouseButtonValues.end()) {
for (auto &button : mouseButtonIt->second) {
value += button.second;
}
}
auto gamepadAxisIt = m_CommandGamepadAxisValues.find(command);
if (gamepadAxisIt != m_CommandGamepadAxisValues.end()) {
for (auto &axis : gamepadAxisIt->second) {
value += axis.second;
}
}
auto gamepadButtonIt = m_CommandGamepadButtonValues.find(command);
if (gamepadButtonIt != m_CommandGamepadButtonValues.end()) {
for (auto &button : gamepadButtonIt->second) {
value += button.second;
}
}
return std::max(-1.f, std::min(value, 1.f));
}
void Systems::InputSystem::PublishCommand(int playerID, std::string command, float value)
{
Events::InputCommand e;
e.PlayerID = playerID;
e.Command = command;
e.Value = value;
EventBroker->Publish(e);
LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, playerID);
}
+181
View File
@@ -0,0 +1,181 @@
#include "Input/KeyboardInputHandler.h"
KeyboardInputHandler::KeyboardInputHandler(EventBroker* eventBroker, InputProxy* inputProxy) : InputHandler(eventBroker, inputProxy)
{
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &KeyboardInputHandler::OnKeyDown);
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &KeyboardInputHandler::OnKeyUp);
m_OriginKeyCodes["Space"] = GLFW_KEY_SPACE;
m_OriginKeyCodes["Apostrophe"] = GLFW_KEY_APOSTROPHE;
m_OriginKeyCodes["Comma"] = GLFW_KEY_COMMA;
m_OriginKeyCodes["Minus"] = GLFW_KEY_MINUS;
m_OriginKeyCodes["Period"] = GLFW_KEY_PERIOD;
m_OriginKeyCodes["Slash"] = GLFW_KEY_SLASH;
m_OriginKeyCodes["0"] = GLFW_KEY_0;
m_OriginKeyCodes["1"] = GLFW_KEY_1;
m_OriginKeyCodes["2"] = GLFW_KEY_2;
m_OriginKeyCodes["3"] = GLFW_KEY_3;
m_OriginKeyCodes["4"] = GLFW_KEY_4;
m_OriginKeyCodes["5"] = GLFW_KEY_5;
m_OriginKeyCodes["6"] = GLFW_KEY_6;
m_OriginKeyCodes["7"] = GLFW_KEY_7;
m_OriginKeyCodes["8"] = GLFW_KEY_8;
m_OriginKeyCodes["9"] = GLFW_KEY_9;
m_OriginKeyCodes["Semicolon"] = GLFW_KEY_SEMICOLON;
m_OriginKeyCodes["Equal"] = GLFW_KEY_EQUAL;
m_OriginKeyCodes["A"] = GLFW_KEY_A;
m_OriginKeyCodes["B"] = GLFW_KEY_B;
m_OriginKeyCodes["C"] = GLFW_KEY_C;
m_OriginKeyCodes["D"] = GLFW_KEY_D;
m_OriginKeyCodes["E"] = GLFW_KEY_E;
m_OriginKeyCodes["F"] = GLFW_KEY_F;
m_OriginKeyCodes["G"] = GLFW_KEY_G;
m_OriginKeyCodes["H"] = GLFW_KEY_H;
m_OriginKeyCodes["I"] = GLFW_KEY_I;
m_OriginKeyCodes["J"] = GLFW_KEY_J;
m_OriginKeyCodes["K"] = GLFW_KEY_K;
m_OriginKeyCodes["L"] = GLFW_KEY_L;
m_OriginKeyCodes["M"] = GLFW_KEY_M;
m_OriginKeyCodes["N"] = GLFW_KEY_N;
m_OriginKeyCodes["O"] = GLFW_KEY_O;
m_OriginKeyCodes["P"] = GLFW_KEY_P;
m_OriginKeyCodes["Q"] = GLFW_KEY_Q;
m_OriginKeyCodes["R"] = GLFW_KEY_R;
m_OriginKeyCodes["S"] = GLFW_KEY_S;
m_OriginKeyCodes["T"] = GLFW_KEY_T;
m_OriginKeyCodes["U"] = GLFW_KEY_U;
m_OriginKeyCodes["V"] = GLFW_KEY_V;
m_OriginKeyCodes["W"] = GLFW_KEY_W;
m_OriginKeyCodes["X"] = GLFW_KEY_X;
m_OriginKeyCodes["Y"] = GLFW_KEY_Y;
m_OriginKeyCodes["Z"] = GLFW_KEY_Z;
m_OriginKeyCodes["LeftBracket"] = GLFW_KEY_LEFT_BRACKET;
m_OriginKeyCodes["Backslash"] = GLFW_KEY_BACKSLASH;
m_OriginKeyCodes["RightBracket"] = GLFW_KEY_RIGHT_BRACKET;
m_OriginKeyCodes["Accent"] = GLFW_KEY_GRAVE_ACCENT;
m_OriginKeyCodes["W1"] = GLFW_KEY_WORLD_1;
m_OriginKeyCodes["W2"] = GLFW_KEY_WORLD_2;
m_OriginKeyCodes["Escape"] = GLFW_KEY_ESCAPE;
m_OriginKeyCodes["Enter"] = GLFW_KEY_ENTER;
m_OriginKeyCodes["Tab"] = GLFW_KEY_TAB;
m_OriginKeyCodes["Backspace"] = GLFW_KEY_BACKSPACE;
m_OriginKeyCodes["Insert"] = GLFW_KEY_INSERT;
m_OriginKeyCodes["Delete"] = GLFW_KEY_DELETE;
m_OriginKeyCodes["Right"] = GLFW_KEY_RIGHT;
m_OriginKeyCodes["Left"] = GLFW_KEY_LEFT;
m_OriginKeyCodes["Down"] = GLFW_KEY_DOWN;
m_OriginKeyCodes["Up"] = GLFW_KEY_UP;
m_OriginKeyCodes["PgUp"] = GLFW_KEY_PAGE_UP;
m_OriginKeyCodes["PgDn"] = GLFW_KEY_PAGE_DOWN;
m_OriginKeyCodes["Home"] = GLFW_KEY_HOME;
m_OriginKeyCodes["End"] = GLFW_KEY_END;
m_OriginKeyCodes["CapsLock"] = GLFW_KEY_CAPS_LOCK;
m_OriginKeyCodes["ScrollLock"] = GLFW_KEY_SCROLL_LOCK;
m_OriginKeyCodes["NumLock"] = GLFW_KEY_NUM_LOCK;
m_OriginKeyCodes["PrintScreen"] = GLFW_KEY_PRINT_SCREEN;
m_OriginKeyCodes["Pause"] = GLFW_KEY_PAUSE;
m_OriginKeyCodes["F1"] = GLFW_KEY_F1;
m_OriginKeyCodes["F2"] = GLFW_KEY_F2;
m_OriginKeyCodes["F3"] = GLFW_KEY_F3;
m_OriginKeyCodes["F4"] = GLFW_KEY_F4;
m_OriginKeyCodes["F5"] = GLFW_KEY_F5;
m_OriginKeyCodes["F6"] = GLFW_KEY_F6;
m_OriginKeyCodes["F7"] = GLFW_KEY_F7;
m_OriginKeyCodes["F8"] = GLFW_KEY_F8;
m_OriginKeyCodes["F9"] = GLFW_KEY_F9;
m_OriginKeyCodes["F10"] = GLFW_KEY_F10;
m_OriginKeyCodes["F11"] = GLFW_KEY_F11;
m_OriginKeyCodes["F12"] = GLFW_KEY_F12;
m_OriginKeyCodes["F13"] = GLFW_KEY_F13;
m_OriginKeyCodes["F14"] = GLFW_KEY_F14;
m_OriginKeyCodes["F15"] = GLFW_KEY_F15;
m_OriginKeyCodes["F16"] = GLFW_KEY_F16;
m_OriginKeyCodes["F17"] = GLFW_KEY_F17;
m_OriginKeyCodes["F18"] = GLFW_KEY_F18;
m_OriginKeyCodes["F19"] = GLFW_KEY_F19;
m_OriginKeyCodes["F20"] = GLFW_KEY_F20;
m_OriginKeyCodes["F21"] = GLFW_KEY_F21;
m_OriginKeyCodes["F22"] = GLFW_KEY_F22;
m_OriginKeyCodes["F23"] = GLFW_KEY_F23;
m_OriginKeyCodes["F24"] = GLFW_KEY_F24;
m_OriginKeyCodes["F25"] = GLFW_KEY_F25;
m_OriginKeyCodes["KP0"] = GLFW_KEY_KP_0;
m_OriginKeyCodes["KP1"] = GLFW_KEY_KP_1;
m_OriginKeyCodes["KP2"] = GLFW_KEY_KP_2;
m_OriginKeyCodes["KP3"] = GLFW_KEY_KP_3;
m_OriginKeyCodes["KP4"] = GLFW_KEY_KP_4;
m_OriginKeyCodes["KP5"] = GLFW_KEY_KP_5;
m_OriginKeyCodes["KP6"] = GLFW_KEY_KP_6;
m_OriginKeyCodes["KP7"] = GLFW_KEY_KP_7;
m_OriginKeyCodes["KP8"] = GLFW_KEY_KP_8;
m_OriginKeyCodes["KP9"] = GLFW_KEY_KP_9;
m_OriginKeyCodes["KPDecimal"] = GLFW_KEY_KP_DECIMAL;
m_OriginKeyCodes["KPDivide"] = GLFW_KEY_KP_DIVIDE;
m_OriginKeyCodes["KPMultiply"] = GLFW_KEY_KP_MULTIPLY;
m_OriginKeyCodes["KPSubtract"] = GLFW_KEY_KP_SUBTRACT;
m_OriginKeyCodes["KPAdd"] = GLFW_KEY_KP_ADD;
m_OriginKeyCodes["KPEnter"] = GLFW_KEY_KP_ENTER;
m_OriginKeyCodes["KPEqual"] = GLFW_KEY_KP_EQUAL;
m_OriginKeyCodes["LeftShift"] = GLFW_KEY_LEFT_SHIFT;
m_OriginKeyCodes["LeftControl"] = GLFW_KEY_LEFT_CONTROL;
m_OriginKeyCodes["LeftAlt"] = GLFW_KEY_LEFT_ALT;
m_OriginKeyCodes["LeftSuper"] = GLFW_KEY_LEFT_SUPER;
m_OriginKeyCodes["RightShift"] = GLFW_KEY_RIGHT_SHIFT;
m_OriginKeyCodes["RightControl"] = GLFW_KEY_RIGHT_CONTROL;
m_OriginKeyCodes["RightAlt"] = GLFW_KEY_RIGHT_ALT;
m_OriginKeyCodes["RightSuper"] = GLFW_KEY_RIGHT_SUPER;
m_OriginKeyCodes["Menu"] = GLFW_KEY_MENU;
}
bool KeyboardInputHandler::BindOrigin(std::string origin, std::string command, float value)
{
auto originIt = m_OriginKeyCodes.find(origin);
if (originIt == m_OriginKeyCodes.end()) {
return false;
}
int keyCode = originIt->second;
m_KeyBindings[keyCode] = std::make_tuple(command, value);
return true;
}
bool KeyboardInputHandler::OnKeyDown(const Events::KeyDown& e)
{
auto it = m_KeyBindings.find(e.KeyCode);
if (it == m_KeyBindings.end()) {
return false;
}
std::string command;
float value;
std::tie(command, value) = it->second;
m_CommandValues[command] += value;
return true;
}
bool KeyboardInputHandler::OnKeyUp(const Events::KeyUp& e)
{
auto it = m_KeyBindings.find(e.KeyCode);
if (it == m_KeyBindings.end()) {
return false;
}
std::string command;
float value;
std::tie(command, value) = it->second;
m_CommandValues[command] -= value;
return true;
}
float KeyboardInputHandler::GetCommandValue(std::string command)
{
auto it = m_CommandValues.find(command);
if (it != m_CommandValues.end()) {
return m_CommandValues[command];
} else {
return 0.f;
}
}
+129
View File
@@ -0,0 +1,129 @@
#include "Input/MouseInputHandler.h"
MouseInputHandler::MouseInputHandler(EventBroker* eventBroker, InputProxy* inputProxy)
: InputHandler(eventBroker, inputProxy)
{
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &MouseInputHandler::OnMousePress);
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &MouseInputHandler::OnMouseRelease);
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &MouseInputHandler::OnMouseMove);
m_OriginCodes["Mouse1"] = GLFW_MOUSE_BUTTON_1;
m_OriginCodes["MouseLeft"] = GLFW_MOUSE_BUTTON_LEFT;
m_OriginCodes["Mouse2"] = GLFW_MOUSE_BUTTON_2;
m_OriginCodes["MouseRight"] = GLFW_MOUSE_BUTTON_RIGHT;
m_OriginCodes["Mouse3"] = GLFW_MOUSE_BUTTON_3;
m_OriginCodes["MouseMiddle"] = GLFW_MOUSE_BUTTON_MIDDLE;
m_OriginCodes["Mouse4"] = GLFW_MOUSE_BUTTON_4;
m_OriginCodes["Mouse5"] = GLFW_MOUSE_BUTTON_5;
m_OriginCodes["Mouse6"] = GLFW_MOUSE_BUTTON_6;
m_OriginCodes["Mouse7"] = GLFW_MOUSE_BUTTON_7;
m_OriginCodes["Mouse8"] = GLFW_MOUSE_BUTTON_8;
m_OriginAxes["MouseX"] = 'X';
m_OriginAxes["MouseY"] = 'Y';
}
bool MouseInputHandler::BindOrigin(std::string origin, std::string command, float value)
{
auto originCode = m_OriginCodes.find(origin);
if (originCode != m_OriginCodes.end()) {
int code = originCode->second;
m_Bindings[code] = std::make_tuple(command, value);
return true;
}
auto originAxis = m_OriginAxes.find(origin);
if (originAxis != m_OriginAxes.end()) {
char axis = originAxis->second;
float multiplier = 1.f;
// Sensitivity
multiplier *= ResourceManager::Load<ConfigFile>("Input.ini")->Get<float>("Mouse.Sensitivity", 1.f);
if (axis == 'Y') {
if (ResourceManager::Load<ConfigFile>("Input.ini")->Get<bool>("Mouse.InvertPitch", false)) {
multiplier *= -1.f;
}
}
m_Axes[axis] = std::make_tuple(command, value * multiplier);
return true;
}
return false;
}
float MouseInputHandler::GetCommandValue(std::string command)
{
auto it = m_CommandValues.find(command);
if (it != m_CommandValues.end()) {
return m_CommandValues[command];
} else {
return 0.f;
}
}
bool MouseInputHandler::OnMousePress(const Events::MousePress& e)
{
auto it = m_Bindings.find(e.Button);
if (it == m_Bindings.end()) {
return false;
}
std::string command;
float value;
std::tie(command, value) = it->second;
m_CommandValues[command] += value;
return true;
}
bool MouseInputHandler::OnMouseRelease(const Events::MouseRelease& e)
{
auto it = m_Bindings.find(e.Button);
if (it == m_Bindings.end()) {
return false;
}
std::string command;
float value;
std::tie(command, value) = it->second;
m_CommandValues[command] -= value;
return true;
}
bool MouseInputHandler::OnMouseMove(const Events::MouseMove& e)
{
if (std::abs(e.DeltaX) > 0) {
auto it = m_Axes.find('X');
if (it != m_Axes.end()) {
Events::InputCommand ic;
ic.PlayerID = -1;
std::tie(ic.Command, ic.Value) = it->second;
ic.Value *= e.DeltaX;
m_InputProxy->Publish(ic);
}
}
if (std::abs(e.DeltaY) > 0) {
auto it = m_Axes.find('Y');
if (it != m_Axes.end()) {
Events::InputCommand ic;
ic.PlayerID = -1;
std::tie(ic.Command, ic.Value) = it->second;
ic.Value *= e.DeltaY;
m_InputProxy->Publish(ic);
}
}
return true;
}
bool MouseInputHandler::hasOrigin(std::string origin)
{
if (m_OriginCodes.find(origin) == m_OriginCodes.end()) {
return false;
}
if (m_OriginAxes.find(origin) == m_OriginAxes.end()) {
return false;
}
return true;
}
+326 -3
View File
@@ -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;
}
+72
View File
@@ -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);
}
+350 -6
View File
@@ -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;
}
+12 -9
View File
@@ -50,6 +50,18 @@ void Camera::SetOrientation(glm::quat val)
UpdateViewMatrix();
}
void Camera::SetProjectionMatrix(glm::mat4 val)
{
m_ProjectionMatrix = val;
}
void Camera::SetViewMatrix(glm::mat4 val)
{
m_ViewMatrix = val;
}
//void Camera::Pitch(float val)
//{
// m_Pitch = val;
@@ -64,15 +76,6 @@ void Camera::SetOrientation(glm::quat val)
void Camera::UpdateProjectionMatrix()
{
// m_ProjectionMatrix = glm::ortho(
// -16.f,
// 16.f,
// -9.f,
// 9.f,
// m_NearClip,
// m_FarClip
// );
m_ProjectionMatrix = glm::perspective(m_FOV, m_AspectRatio, m_NearClip, m_FarClip);
}
+63
View File
@@ -0,0 +1,63 @@
#include "Rendering/DrawScenePass.h"
DrawScenePass::DrawScenePass(IRenderer* renderer)
{
m_Renderer = renderer;
InitializeTextures();
InitializeShaderPrograms();
}
void DrawScenePass::InitializeTextures()
{
m_WhiteTexture = ResourceManager::Load<Texture>("Textures/Core/Blank.png");
}
void DrawScenePass::InitializeShaderPrograms()
{
//Gör så att shaders är en resource, tex som texture classen. Konstruktorn måste vara privat.
m_BasicForwardProgram = ResourceManager::Load<ShaderProgram>("#BasicForwardProgram");
m_BasicForwardProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/BasicForward.vert.glsl")));
m_BasicForwardProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/BasicForward.frag.glsl")));
m_BasicForwardProgram->Compile();
m_BasicForwardProgram->Link();
}
void DrawScenePass::Draw(RenderScene& scene)
{
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLERROR("Renderer::Draw PickingPass");
DrawScenePassState state = DrawScenePassState();
for (auto &job : scene.ForwardJobs) {
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
if (modelJob) {
GLuint ShaderHandle = m_BasicForwardProgram->GetHandle();
m_BasicForwardProgram->Bind();
//TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
glUniform4fv(glGetUniformLocation(ShaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color));
//TODO: Renderer: bättre textur felhantering samt fler texturer stöd
if (modelJob->DiffuseTexture != nullptr) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, modelJob->DiffuseTexture->m_Texture);
} else {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_WhiteTexture->m_Texture);
}
glBindVertexArray(modelJob->Model->VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex);
//continue;
}
}
GLERROR("DrawScene Error");
}
@@ -0,0 +1,20 @@
#include "Rendering/DrawScenePassState.h"
DrawScenePassState::DrawScenePassState()
{
GLERROR("---");
BindFramebuffer(0);
GLERROR("---");
Enable(GL_DEPTH_TEST);
Enable(GL_CULL_FACE);
Enable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// ClearColor(glm::vec4(255.f / 255, 163.f / 255, 176.f / 255, 0.f));
// Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
DrawScenePassState::~DrawScenePassState()
{
}
+1 -8
View File
@@ -39,17 +39,10 @@ void DummyRenderer::Initialize()
exit(EXIT_FAILURE);
}
// Create default camera
m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f);
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 0));
if (m_Camera == nullptr) {
m_Camera = m_DefaultCamera;
}
glfwSwapInterval(m_VSYNC);
}
void DummyRenderer::Draw(RenderQueueCollection& rq)
void DummyRenderer::Draw(RenderFrame& rq)
{
glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
+3
View File
@@ -8,16 +8,19 @@ Font::Font(std::string path)
if (FT_Init_FreeType(&library)) {
LOG_ERROR("FreeType error: init failed");
return;
}
if (FT_New_Face(library, path.c_str(), 0, &face)) {
LOG_ERROR("FreeType error: loading font");
return;
}
FT_Set_Pixel_Sizes(face, 0, 48);
if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
LOG_ERROR("FreeType error: loading char");
return;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+9 -1
View File
@@ -48,13 +48,21 @@ void FrameBuffer::Generate()
switch ((*it)->m_ResourceType) {
case GL_TEXTURE_2D:
glFramebufferTexture2D(GL_FRAMEBUFFER, (*it)->m_Attachment, (*it)->m_ResourceType, *(*it)->m_ResourceHandle, 0);
GLERROR("FrameBuffer generate: glFramebufferTexture2D");
break;
case GL_RENDERBUFFER:
glFramebufferRenderbuffer(GL_FRAMEBUFFER, (*it)->m_Attachment, (*it)->m_ResourceType, *(*it)->m_ResourceHandle);
GLERROR("FrameBuffer generate: glFramebufferRenderbuffer");
if ( (*it)->m_Attachment != GL_COLOR_ATTACHMENT0 ||
(*it)->m_Attachment != GL_DEPTH_ATTACHMENT ||
(*it)->m_Attachment != GL_STENCIL_ATTACHMENT)
{
LOG_ERROR("RenderBuffer Attachment not valid.");
}
break;
}
GLERROR("FrameBuffer generate");
if ((*it)->m_Attachment != GL_DEPTH_ATTACHMENT) {
attachments.push_back((*it)->m_Attachment);
+307
View File
@@ -0,0 +1,307 @@
#include "Rendering/ImGuiRenderPass.h"
ImGuiRenderPass::ImGuiRenderPass(IRenderer* renderer, EventBroker* eventBroker)
: m_Renderer(renderer)
, m_EventBroker(eventBroker)
{
g_Window = renderer->Window();
ImGuiIO& io = ImGui::GetIO();
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
ImGuiStyle& style = ImGui::GetStyle();
style.Alpha = 1.f;
style.WindowPadding = ImVec2(8.f, 7.f);
style.WindowRounding = 4.f;
style.ChildWindowRounding = 0.f;
style.FramePadding = ImVec2(4.f, 2.f);
style.FrameRounding = 2.f;
style.ItemSpacing = ImVec2(6.f, 2.f);
style.ItemInnerSpacing = ImVec2(3.f, 4.f);
style.IndentSpacing = 16.f;
style.ScrollbarSize = 12;
style.ScrollbarRounding = 2.f;
style.GrabMinSize = 13.f;
style.GrabRounding = 3.f;
createDeviceObjects();
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &ImGuiRenderPass::OnMousePress);
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &ImGuiRenderPass::OnMouseRelease);
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &ImGuiRenderPass::OnMouseMove);
EVENT_SUBSCRIBE_MEMBER(m_EMouseScroll, &ImGuiRenderPass::OnMouseScroll);
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &ImGuiRenderPass::OnKeyDown);
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &ImGuiRenderPass::OnKeyUp);
EVENT_SUBSCRIBE_MEMBER(m_EKeyboardChar, &ImGuiRenderPass::OnKeyboardChar);
// Prime the first frame
newFrame();
}
void ImGuiRenderPass::Update(double dt)
{
g_DeltaTime = dt;
}
void ImGuiRenderPass::Draw()
{
ImGuiIO& io = ImGui::GetIO();
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();
// Set up render state
ImGuiRenderState state;
glActiveTexture(GL_TEXTURE0);
// Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays)
float fb_height = io.DisplaySize.y * io.DisplayFramebufferScale.y;
draw_data->ScaleClipRects(io.DisplayFramebufferScale);
// Setup viewport, orthographic projection matrix
glViewport(0, 0, (GLsizei)io.DisplaySize.x, (GLsizei)io.DisplaySize.y);
const float ortho_projection[4][4] =
{
{ 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ -1.0f, 1.0f, 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
glBindVertexArray(g_VaoHandle);
for (int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawIdx* idx_buffer_offset = 0;
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.size() * sizeof(ImDrawVert), (GLvoid*)&cmd_list->VtxBuffer.front(), GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.size() * sizeof(ImDrawIdx), (GLvoid*)&cmd_list->IdxBuffer.front(), GL_STREAM_DRAW);
for (const ImDrawCmd* pcmd = cmd_list->CmdBuffer.begin(); pcmd != cmd_list->CmdBuffer.end(); pcmd++) {
if (pcmd->UserCallback) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
}
idx_buffer_offset += pcmd->ElemCount;
}
}
// Start next frame
newFrame();
}
bool ImGuiRenderPass::OnMousePress(const Events::MousePress& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[e.Button] = true;
return false;
}
bool ImGuiRenderPass::OnMouseRelease(const Events::MouseRelease& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MouseDown[e.Button] = false;
return false;
}
bool ImGuiRenderPass::OnMouseMove(const Events::MouseMove& e)
{
ImGuiIO& io = ImGui::GetIO();
io.MousePos.x = e.X;
io.MousePos.y = e.Y;
return true;
}
bool ImGuiRenderPass::OnMouseScroll(const Events::MouseScroll& e)
{
g_MouseWheel += (float)e.DeltaY;
return true;
}
bool ImGuiRenderPass::OnKeyDown(const Events::KeyDown& e)
{
ImGuiIO& io = ImGui::GetIO();
io.KeysDown[e.KeyCode] = true;
return true;
}
bool ImGuiRenderPass::OnKeyUp(const Events::KeyUp& e)
{
ImGuiIO& io = ImGui::GetIO();
io.KeysDown[e.KeyCode] = false;
return true;
}
bool ImGuiRenderPass::OnKeyboardChar(const Events::KeyboardChar& e)
{
ImGuiIO& io = ImGui::GetIO();
if (e.Char > 0 && e.Char < 0x10000) {
io.AddInputCharacter((unsigned short)e.Char);
return true;
} else {
return false;
}
}
bool ImGuiRenderPass::createDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer, last_vertex_array;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
const GLchar *vertex_shader =
"#version 330\n"
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader =
"#version 330\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
"}\n";
g_ShaderHandle = glCreateProgram();
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_VertHandle, 1, &vertex_shader, 0);
glShaderSource(g_FragHandle, 1, &fragment_shader, 0);
glCompileShader(g_VertHandle);
glCompileShader(g_FragHandle);
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
glGenVertexArrays(1, &g_VaoHandle);
glBindVertexArray(g_VaoHandle);
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glEnableVertexAttribArray(g_AttribLocationPosition);
glEnableVertexAttribArray(g_AttribLocationUV);
glEnableVertexAttribArray(g_AttribLocationColor);
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, col));
#undef OFFSETOF
createFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBindVertexArray(last_vertex_array);
return true;
}
bool ImGuiRenderPass::createFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("Fonts/DroidSans.ttf", 13.f);
//io.Fonts->AddFontFromFileTTF("Fonts/ProggyClean.ttf", 13.f);
//io.Fonts->AddFontFromFileTTF("Fonts/ProggyTiny.ttf", 10.f);
//io.Fonts->AddFontFromFileTTF("Fonts/Karla-Regular.ttf", 15.0f);
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (void *)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGuiRenderPass::newFrame()
{
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
glfwGetWindowSize(g_Window, &w, &h);
glfwGetFramebufferSize(g_Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
io.DeltaTime = g_DeltaTime;
io.KeyCtrl = glfwGetKey(g_Window, GLFW_KEY_LEFT_CONTROL) || glfwGetKey(g_Window, GLFW_KEY_RIGHT_CONTROL);
io.KeyShift = glfwGetKey(g_Window, GLFW_KEY_LEFT_SHIFT) || glfwGetKey(g_Window, GLFW_KEY_RIGHT_SHIFT);
io.KeyAlt = glfwGetKey(g_Window, GLFW_KEY_LEFT_ALT) || glfwGetKey(g_Window, GLFW_KEY_RIGHT_ALT);
io.MouseWheel = g_MouseWheel;
g_MouseWheel = 0;
m_EventBroker->Process<ImGuiRenderPass>();
ImGui::NewFrame();
}
+162
View File
@@ -0,0 +1,162 @@
#include "Rendering/PickingPass.h"
PickingPass::PickingPass(IRenderer* renderer, EventBroker* eb)
{
m_Renderer = renderer;
m_EventBroker = eb;
InitializeTextures();
InitializeFrameBuffers();
InitializeShaderPrograms();
}
PickingPass::~PickingPass()
{
}
void PickingPass::InitializeTextures()
{
GenerateTexture(&m_PickingTexture, GL_CLAMP_TO_BORDER, GL_LINEAR,
glm::vec2(m_Renderer->Resolution().Width, m_Renderer->Resolution().Height), GL_RG8, GL_RG, GL_UNSIGNED_BYTE);
}
void PickingPass::InitializeFrameBuffers()
{
glGenRenderbuffers(1, &m_DepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_Renderer->Resolution().Width, m_Renderer->Resolution().Height);
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new RenderBuffer(&m_DepthBuffer, GL_DEPTH_ATTACHMENT)));
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_PickingTexture, GL_COLOR_ATTACHMENT0)));
m_PickingBuffer.Generate();
}
void PickingPass::InitializeShaderPrograms()
{
m_PickingProgram = ResourceManager::Load<ShaderProgram>("#PickingProgram");
m_PickingProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Picking.vert.glsl")));
m_PickingProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Picking.frag.glsl")));
m_PickingProgram->Compile();
m_PickingProgram->BindFragDataLocation(0, "TextureFragment");
m_PickingProgram->Link();
}
void PickingPass::Draw(RenderScene& scene)
{
PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle());
//TODO: Render: Add code for more jobs than modeljobs.
GLuint ShaderHandle = m_PickingProgram->GetHandle();
m_PickingProgram->Bind();
m_Camera = scene.Camera;
for (auto &job : scene.ForwardJobs) {
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
if (modelJob) {
int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] };
PickingInfo pickInfo;
pickInfo.Entity = modelJob->Entity;
pickInfo.World = modelJob->World;
pickInfo.Camera = scene.Camera;
auto color = m_EntityColors.find(std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera));
if (color != m_EntityColors.end()) {
pickColor[0] = color->second[0];
pickColor[1] = color->second[1];
} else {
m_EntityColors[std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)] = glm::ivec2(pickColor[0], pickColor[1]);
if (m_ColorCounter[0] > 255) {
m_ColorCounter[0] = 0;
m_ColorCounter[1]++;;
} else {
m_ColorCounter[0]++;;
}
}
m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo;
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
glBindVertexArray(modelJob->Model->VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, nullptr, modelJob->StartIndex);
}
}
m_PickingBuffer.Unbind();
GLERROR("PickingPass Error");
delete state;
}
void PickingPass::ClearPicking()
{
m_PickingColorsToEntity.clear();
m_EntityColors.clear();
m_ColorCounter[0] = 0;
m_ColorCounter[1] = 0;
m_PickingBuffer.Bind();
glClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_PickingBuffer.Unbind();
}
PickData PickingPass::Pick(glm::vec2 screenCoord)
{
int fbWidth;
int fbHeight;
glfwGetFramebufferSize(m_Renderer->Window(), &fbWidth, &fbHeight);
Rectangle resolution = Rectangle(fbWidth, fbHeight);
PickData pickData;
// Invert screen y coordinate
screenCoord.y = resolution.Height - screenCoord.y;
ScreenCoords::PixelData data = ScreenCoords::ToPixelData(screenCoord, &m_PickingBuffer, m_DepthBuffer);
pickData.Depth = data.Depth;
PickingInfo pickInfo;
auto it = m_PickingColorsToEntity.find(glm::ivec2(data.Color[0], data.Color[1]));
if (it != m_PickingColorsToEntity.end()) {
pickInfo = it->second;
} else {
pickData.Entity = EntityID_Invalid;
}
pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, resolution, pickInfo.Camera->ProjectionMatrix(), pickInfo.Camera->ViewMatrix());
pickData.Entity = pickInfo.Entity;
pickData.Camera = pickInfo.Camera;
pickData.World = pickInfo.World;
return pickData;
}
void PickingPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const
{
//TODO: Renderer: Make this in a sparate class
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, *texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapping);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapping);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, dimensions.x, dimensions.y, 0, format, type, nullptr);//TODO: Renderer: Fix the precision and Resolution
GLERROR("Texture initialization failed");
}
+20
View File
@@ -0,0 +1,20 @@
#include "Rendering/PickingPassState.h"
PickingPassState::PickingPassState(GLuint frameBuffer)
{
GLERROR("---2");
BindFramebuffer(frameBuffer);
GLERROR("---3");
Enable(GL_DEPTH_TEST);
Enable(GL_CULL_FACE);
glm::vec4 clearColor = glm::vec4(0.f);
//ClearColor(clearColor);
//Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
PickingPassState::~PickingPassState()
{
}
+11 -5
View File
@@ -8,7 +8,7 @@ RawModel::RawModel(std::string fileName)
if (scene == nullptr) {
LOG_ERROR("Failed to load model \"%s\"", fileName.c_str());
LOG_ERROR("Assimp error: %s", importer.GetErrorString());
return;
throw std::runtime_error("Failed to open model file.");
}
auto m = scene->mRootNode->mTransformation;
@@ -76,13 +76,18 @@ RawModel::RawModel(std::string fileName)
}
// Material diffuse color
aiColor4D diffuse;
aiColor3D diffuse;
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a);
float opacity;
material->Get(AI_MATKEY_OPACITY, opacity);
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity);
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity);
// Material specular color
aiColor4D specular;
aiColor3D specular;
material->Get(AI_MATKEY_COLOR_SPECULAR, specular);
desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, specular.a);
desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, 1.f);
m_Vertices.push_back(desc);
}
@@ -132,6 +137,7 @@ RawModel::RawModel(std::string fileName)
matGroup.EndIndex = m_Indices.size() - 1;
// Material shininess
material->Get(AI_MATKEY_SHININESS, matGroup.Shininess);
material->Get(AI_MATKEY_OPACITY, matGroup.Transparency);
//LOG_DEBUG("Shininess: %f", matGroup.Shininess);
// Diffuse texture
//LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE));
-138
View File
@@ -1,138 +0,0 @@
#include "Rendering/RenderQueueFactory.h"
RenderQueueFactory::RenderQueueFactory()
{
m_RenderQueues = RenderQueueCollection();
}
void RenderQueueFactory::Update(World* world)
{
m_RenderQueues.Clear();
FillModels(world, &m_RenderQueues.Forward);
FillLights(world, &m_RenderQueues.Lights);
FillText(world, &m_RenderQueues.Text);
}
glm::mat4 RenderQueueFactory::ModelMatrix(World* world, EntityID entity)
{
glm::vec3 position = AbsolutePosition(world, entity);
glm::quat orientation = AbsoluteOrientation(world, entity);
glm::vec3 scale = AbsoluteScale(world, entity);
glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(orientation) * glm::scale(scale);
return modelMatrix;
}
glm::vec3 RenderQueueFactory::AbsolutePosition(World* world, EntityID entity)
{
glm::vec3 position;
do {
ComponentWrapper transform = world->GetComponent(entity, "Transform");
position += AbsoluteOrientation(world, entity) * (glm::vec3)transform["Position"];
entity = world->GetParent(entity);
} while (entity != 0);
return position;
}
glm::quat RenderQueueFactory::AbsoluteOrientation(World* world, EntityID entity)
{
glm::quat orientation;
do {
ComponentWrapper transform = world->GetComponent(entity, "Transform");
orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation;
entity = world->GetParent(entity);
} while (entity != 0);
return orientation;
}
glm::vec3 RenderQueueFactory::AbsoluteScale(World* world, EntityID entity)
{
ComponentWrapper transform = world->GetComponent(entity, "Transform");
glm::vec3 scale = (glm::vec3)transform["Scale"];
EntityID parent = world->GetParent(entity);
if (parent != 0) {
return AbsoluteScale(world, parent) * scale;
} else {
return scale;
}
}
void RenderQueueFactory::FillModels(World* world, RenderQueue* renderQueue)
{
auto models = world->GetComponents("Model");
if (models == nullptr) {
return;
}
for (auto& modelC : *models) {
std::string resource = modelC["Resource"];
if (resource.empty()) {
continue;
}
glm::vec4 color = modelC["Color"];
Model* model = ResourceManager::Load<Model>(resource);
for (auto texGroup : model->TextureGroups) {
ModelJob job;
job.TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0;
job.DiffuseTexture = texGroup.Texture.get();
job.NormalTexture = texGroup.NormalMap.get();
job.SpecularTexture = texGroup.SpecularMap.get();
job.Model = model;
job.StartIndex = texGroup.StartIndex;
job.EndIndex = texGroup.EndIndex;
job.ModelMatrix = model->m_Matrix * ModelMatrix(world, modelC.EntityID);
job.Color = color;
//TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this
job.Entity = modelC.EntityID;
renderQueue->Add(job);
}
}
}
void RenderQueueFactory::FillLights(World* world, RenderQueue* renderQueue)
{
}
void RenderQueueFactory::FillText(World* world, RenderQueue* renderQueue)
{
auto texts = world->GetComponents("Text");
if (texts == nullptr) {
return;
}
for (auto& textC : *texts) {
std::string resource = textC["Resource"];
if (resource.empty()) {
continue;
}
Font* font = ResourceManager::Load<Font>(resource);
glm::vec4 color = textC["Color"];
std::string content = textC["Content"];
TextJob job;
job.Color = color;
job.Content = content;
job.Resource = font;
job.ModelMatrix = ModelMatrix(world, textC.EntityID);
renderQueue->Add(job);
}
}
+99
View File
@@ -0,0 +1,99 @@
#include "Rendering/RenderState.h"
bool RenderState::Enable(GLenum cap)
{
if (glIsEnabled(cap)) {
return false;
}
m_ResetFunctions.push_back(std::bind(glDisable, cap));
glEnable(cap);
return !GLERROR("RenderState::Enable");
}
bool RenderState::Disable(GLenum cap)
{
if (!glIsEnabled(cap)) {
return false;
}
m_ResetFunctions.push_back(std::bind(glEnable, cap));
glDisable(cap);
return !GLERROR("RenderState::Disable");
}
bool RenderState::CullFace(GLenum mode)
{
if (!glIsEnabled(GL_CULL_FACE)) {
LOG_ERROR("Setting GL_CULL_FACE without enabling it.");
return false;
}
GLint original;
glGetIntegerv(GL_CULL_FACE_MODE, &original);
m_ResetFunctions.push_back(std::bind(glCullFace, original));
glCullFace(mode);
return !GLERROR("RenderState::CullFace");
}
bool RenderState::ClearColor(glm::vec4 color)
{
GLfloat original[4];
glGetFloatv(GL_COLOR_CLEAR_VALUE, &original[0]);
m_ResetFunctions.push_back(std::bind(glClearColor, original[0], original[1], original[2], original[3]));
glClearColor(color.r, color.g, color.b, color.a);
return !GLERROR("RenderState::ClearColor");
}
bool RenderState::Clear(GLbitfield mask)
{
glClear(mask);
return !GLERROR("RenderState::Clear");
}
bool RenderState::BindFramebuffer(GLint framebuffer)
{
GLint originalRead;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &originalRead);
GLint originalDraw;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &originalDraw);
m_ResetFunctions.push_back([originalRead, originalDraw]() {
glBindFramebuffer(GL_READ_FRAMEBUFFER, originalRead);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, originalDraw);
});
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
return !GLERROR("RenderState::BindBuffer");
}
bool RenderState::BlendEquation(GLenum mode)
{
GLint originalRGB;
glGetIntegerv(GL_BLEND_EQUATION_RGB, &originalRGB);
GLint originalAlpha;
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &originalAlpha);
m_ResetFunctions.push_back(std::bind(glBlendEquationSeparate, originalRGB, originalAlpha));
glBlendEquation(mode);
return !GLERROR("RenderState::BlendEquation");
}
bool RenderState::BlendFunc(GLenum sfactor, GLenum dfactor)
{
GLint originalSrcRGB;
glGetIntegerv(GL_BLEND_SRC_RGB, &originalSrcRGB);
GLint originalSrcAlpha;
glGetIntegerv(GL_BLEND_SRC_ALPHA, &originalSrcAlpha);
GLint originalDestRGB;
glGetIntegerv(GL_BLEND_DST_RGB, &originalDestRGB);
GLint originalDestAlpha;
glGetIntegerv(GL_BLEND_DST_ALPHA, &originalDestAlpha);
m_ResetFunctions.push_back(std::bind(glBlendFuncSeparate, originalSrcRGB, originalSrcAlpha, originalDestRGB, originalDestAlpha));
glBlendFunc(sfactor, dfactor);
return !GLERROR("RenderState::BlendFunc");
}
RenderState::~RenderState()
{
for (auto& f : m_ResetFunctions) {
f();
}
}
+226
View File
@@ -0,0 +1,226 @@
#include "Rendering/RenderSystem.h"
#include "Rendering/DebugCameraInputController.h"
RenderSystem::RenderSystem(EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame) :ImpureSystem(eventBrokerer)
{
m_Renderer = renderer;
m_RenderFrame = renderFrame;
EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera);
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand);
m_DefaultCamera = new Camera((float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height, glm::radians(45.f), 0.01f, 5000.f);
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10));
if (m_Camera == nullptr) {
m_Camera = m_DefaultCamera;
}
}
bool RenderSystem::OnSetCamera(const Events::SetCamera &event)
{
auto cameras = m_World->GetComponents("Camera");
if (cameras != nullptr) {
for (auto it = cameras->begin(); it != cameras->end(); it++) {
if ((std::string)(*it)["Name"] == event.Name) {
switchCamera((*it).EntityID);
}
}
}
return true;
}
void RenderSystem::switchCamera(EntityID entity)
{
if(m_World->HasComponent(entity, "Camera")) {
if (m_CurrentCamera != EntityID_Invalid) {
if (m_World->HasComponent(m_CurrentCamera, "Model")) {
m_World->GetComponent(m_CurrentCamera, "Model")["Visible"] = true;
}
}
if (m_World->HasComponent(entity, "Model")) {
m_World->GetComponent(entity, "Model")["Visible"] = false;
}
m_CurrentCamera = entity;
m_SwitchCamera = false;
} else {
LOG_ERROR("Entity %i does not have a CameraComponent", entity);
m_SwitchCamera = false;
}
}
void RenderSystem::updateProjectionMatrix(ComponentWrapper& cameraComponent)
{
double fov = cameraComponent["FOV"];
double aspectRatio = m_Renderer->Resolution().Width / m_Renderer->Resolution().Height;
double nearClip = cameraComponent["NearClip"];
double farClip = cameraComponent["FarClip"];
double fovY = atan(tan(glm::radians(fov)/2.0) * aspectRatio) * 2.0;
m_ProjectionMatrix = glm::perspective(fovY, aspectRatio, nearClip, farClip);
m_Camera->SetFOV(fovY);
m_Camera->SetAspectRatio(aspectRatio);
m_Camera->SetNearClip(nearClip);
m_Camera->SetFarClip(farClip);
m_Camera->UpdateProjectionMatrix();
}
void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
{
auto models = world->GetComponents("Model");
if (models == nullptr) {
return;
}
for (auto& modelComponent : *models) {
bool visible = modelComponent["Visible"];
if (!visible) {
continue;
}
std::string resource = modelComponent["Resource"];
if (resource.empty()) {
continue;
}
Model* model = ResourceManager::Load<::Model>(resource);
if (model == nullptr) {
model = ResourceManager::Load<::Model>("Models/Core/Error.obj");
}
glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world);
for (auto texGroup : model->TextureGroups) {
std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(model, m_Camera, modelMatrix, texGroup, modelComponent, world));
jobs.push_back(modelJob);
}
}
}
void RenderSystem::fillText(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
{
auto texts = world->GetComponents("Text");
if (texts == nullptr) {
return;
}
for (auto& textComponent : *texts) {
bool visible = textComponent["Visible"];
if (!visible) {
continue;
}
std::string resource = textComponent["Resource"];
if (resource.empty()) {
continue;
}
Font* font = ResourceManager::Load<Font>(textComponent["Resource"]);
if (font == nullptr) {
font = ResourceManager::Load<Font>("Fonts/DroidSans.ttf");
}
glm::mat4 modelMatrix = Transform::ModelMatrix(textComponent.EntityID, world);
std::shared_ptr<TextJob> modelJob = std::shared_ptr<TextJob>(new TextJob(modelMatrix, font, textComponent));
jobs.push_back(modelJob);
}
}
bool RenderSystem::OnInputCommand(const Events::InputCommand& e)
{
if (e.Command == "SwitchCamera" && e.Value > 0) {
m_SwitchCamera = true;
return true;
} else {
return false;
}
}
void RenderSystem::Update(World* world, double dt)
{
m_World = world;
m_EventBroker->Process<RenderSystem>();
updateCamera(world, dt);
//Only supports opaque geometry atm
m_RenderFrame->Clear();
RenderScene rs;
rs.Camera = m_Camera;
rs.Viewport = Rectangle(1280, 720);
fillModels(rs.ForwardJobs, world);
fillText(rs.TextJobs, world);
m_RenderFrame->Add(rs);
}
void RenderSystem::updateCamera(World* world, double dt)
{
static DebugCameraInputController<RenderSystem> firstPersonInputController(m_EventBroker, -1);
if (m_SwitchCamera) {
auto cameras = world->GetComponents("Camera");
for (auto it = cameras->begin(); it != cameras->end(); it++) {
if ((*it).EntityID == m_CurrentCamera) {
it++;
if (it != cameras->end()) {
switchCamera((*it).EntityID);
} else {
switchCamera((*cameras->begin()).EntityID);
}
break;
}
}
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
firstPersonInputController.SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
firstPersonInputController.SetPosition(cameraTransform["Position"]);
}
if (m_World->ValidEntity(m_CurrentCamera)) {
if (world->HasComponent(m_CurrentCamera, "Camera") && world->HasComponent(m_CurrentCamera, "Transform")) {
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
firstPersonInputController.Update(dt);
(glm::vec3&)cameraTransform["Orientation"] = glm::eulerAngles(firstPersonInputController.Orientation());
(glm::vec3&)cameraTransform["Position"] = firstPersonInputController.Position();
glm::vec3 position = Transform::AbsolutePosition(world, m_CurrentCamera);
glm::quat orientation = Transform::AbsoluteOrientation(world, m_CurrentCamera);
m_Camera->SetPosition(position);
m_Camera->SetOrientation(orientation);
updateProjectionMatrix(cameraComponent);
}
} else {
m_Camera = m_DefaultCamera;
auto cameras = world->GetComponents("Camera");
if (cameras != nullptr) {
if (cameras->begin() != cameras->end()) {
ComponentWrapper& cameraC = *cameras->begin();
switchCamera(cameraC.EntityID);
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
firstPersonInputController.SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
firstPersonInputController.SetPosition(cameraTransform["Position"]);
}
}
}
m_Camera->UpdateViewMatrix();
}
+40 -216
View File
@@ -3,18 +3,12 @@
void Renderer::Initialize()
{
InitializeWindow();
// Create default camera
m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f);
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10));
if (m_Camera == nullptr) {
m_Camera = m_DefaultCamera;
}
InitializeRenderPasses();
glfwSwapInterval(m_VSYNC);
InitializeShaders();
InitializeTextures();
InitializeFrameBuffers();
m_TextRenderer = new TextRenderer();
m_TextRenderer->Initialize();
@@ -22,6 +16,17 @@ void Renderer::Initialize()
m_ScreenQuad = ResourceManager::Load<Model>("Models/Core/ScreenQuad.obj");
m_UnitQuad = ResourceManager::Load<Model>("Models/Core/UnitQuad.obj");
m_UnitSphere = ResourceManager::Load<Model>("Models/Core/UnitSphere.obj");
m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker);
// Create default camera
m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f);
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10));
if (m_Camera == nullptr) {
m_Camera = m_DefaultCamera;
}
}
void Renderer::InitializeWindow()
@@ -66,79 +71,18 @@ void Renderer::InitializeWindow()
void Renderer::InitializeShaders()
{
m_BasicForwardProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/BasicForward.vert.glsl")));
m_BasicForwardProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/BasicForward.frag.glsl")));
m_BasicForwardProgram.Compile();
m_BasicForwardProgram.Link();
m_PickingProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Picking.vert.glsl")));
m_PickingProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Picking.frag.glsl")));
m_PickingProgram.Compile();
m_PickingProgram.BindFragDataLocation(0, "TextureFragment");
m_PickingProgram.Link();
m_DrawScreenQuadProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/DrawScreenQuad.vert.glsl")));
m_DrawScreenQuadProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/DrawScreenQuad.frag.glsl")));
m_DrawScreenQuadProgram.Compile();
m_DrawScreenQuadProgram.Link();
m_BasicForwardProgram = ResourceManager::Load<ShaderProgram>("#m_BasicForwardProgram");
m_DrawScreenQuadProgram = ResourceManager::Load<ShaderProgram>("#DrawScreenQuadProgram");
m_DrawScreenQuadProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/DrawScreenQuad.vert.glsl")));
m_DrawScreenQuadProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/DrawScreenQuad.frag.glsl")));
m_DrawScreenQuadProgram->Compile();
m_DrawScreenQuadProgram->Link();
}
void Renderer::InputUpdate(double dt)
{
glm::vec3 m_Position = m_Camera->Position();
if (glfwGetKey(m_Window, GLFW_KEY_O) == GLFW_PRESS)
{
m_Position = glm::vec3(0.f, 0.f, 5.f);
}
if (glfwGetKey(m_Window, GLFW_KEY_W) == GLFW_PRESS)
{
m_Position += m_Camera->Forward() * m_CameraMoveSpeed * (float)dt;
}
if (glfwGetKey(m_Window, GLFW_KEY_S) == GLFW_PRESS)
{
m_Position -= m_Camera->Forward() * m_CameraMoveSpeed * (float)dt;
}
if (glfwGetKey(m_Window, GLFW_KEY_D) == GLFW_PRESS)
{
m_Position += m_Camera->Right() * m_CameraMoveSpeed * (float)dt;
}
if (glfwGetKey(m_Window, GLFW_KEY_A) == GLFW_PRESS)
{
m_Position -= m_Camera->Right() * m_CameraMoveSpeed * (float)dt;
}
if (glfwGetKey(m_Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
{
m_CameraMoveSpeed = 5.f;
}
else {
m_CameraMoveSpeed = 0.5f;
}
static double mousePosX, mousePosY;
glfwGetCursorPos(m_Window, &mousePosX, &mousePosY);
if (glfwGetKey(m_Window, GLFW_KEY_SPACE) == GLFW_PRESS) {
double deltaX, deltaY;
deltaX = mousePosX - (float)Resolution().Width / 2;
deltaY = mousePosY - (float)Resolution().Height / 2;
float rotationY = -deltaY / 300.f;
float rotationX = -deltaX / 300.f;
glm::quat orientation = m_Camera->Orientation();
orientation = orientation * glm::angleAxis<float>(rotationY, glm::vec3(1, 0, 0));
orientation = glm::angleAxis<float>(rotationX, glm::vec3(0, 1, 0)) * orientation;
m_Camera->SetOrientation(orientation);
glfwSetCursorPos(m_Window, Resolution().Width / 2, Resolution().Height / 2);
}
m_Camera->SetPosition(m_Position);
}
void Renderer::Update(double dt)
@@ -146,136 +90,37 @@ void Renderer::Update(double dt)
m_EventBroker->Process<Renderer>();
InputUpdate(dt);
m_TextRenderer->Update();
m_ImGuiRenderPass->Update(dt);
}
void Renderer::Draw(RenderQueueCollection& rq)
void Renderer::Draw(RenderFrame& frame)
{
//TODO: Renderer: Kanske borde vara längst upp i update.
PickingPass(rq);
// DrawScreenQuad(m_PickingTexture);
DrawScene(rq);
m_TextRenderer->Draw(rq.Text,m_Camera->ProjectionMatrix(), m_Camera->ViewMatrix());
glfwSwapBuffers(m_Window);
}
void Renderer::DrawScene(RenderQueueCollection& rq)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//TODO: Render: Clean up draw code
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//TODO: Render: Add code for more jobs than modeljobs.
for (auto &job : rq.Forward) {
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
if (modelJob) {
GLuint ShaderHandle = m_BasicForwardProgram.GetHandle();
m_PickingPass->ClearPicking();
for (auto scene : frame.RenderScenes){
m_Camera = scene->Camera; // remove renderer camera when Editor uses the render scene cameras.
m_PickingPass->Draw(*scene);
m_BasicForwardProgram.Bind();
//TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->ModelMatrix));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(m_Camera->ViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(m_Camera->ProjectionMatrix()));
glUniform4fv(glGetUniformLocation(ShaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color));
//TODO: Renderer: bättre textur felhantering samt fler texturer stöd
if (modelJob->DiffuseTexture != nullptr) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, modelJob->DiffuseTexture->m_Texture);
} else {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_WhiteTexture->m_Texture);
}
m_DrawScenePass->Draw(*scene);
GLERROR("Renderer::Draw m_DrawScenePass->Draw");
glBindVertexArray(modelJob->Model->VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex);
m_TextRenderer->Draw(*scene);
continue;
}
}
GLERROR("DrawScene Error");
m_ImGuiRenderPass->Draw();
glfwSwapBuffers(m_Window);
}
void Renderer::PickingPass(RenderQueueCollection& rq)
PickData Renderer::Pick(glm::vec2 screenCoord)
{
m_PickingColorsToEntity.clear();
m_PickingBuffer.Bind();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int r = 1;
int g = 0;
//TODO: Render: Add code for more jobs than modeljobs.
GLuint ShaderHandle = m_PickingProgram.GetHandle();
m_PickingProgram.Bind();
for (auto &job : rq.Forward) {
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
if (modelJob) {
//---------------
//TODO: Renderer: IMPORTANT: Fixa detta så det inte loopar igenom listan varje frame.
//---------------
int pickColor[2] = { r, g };
for (auto i : m_PickingColorsToEntity) {
if(modelJob->Entity == i.second) {
pickColor[0] = i.first.x;
pickColor[1] = i.first.y;
r -= 1;
}
}
m_PickingColorsToEntity[glm::vec2(pickColor[0], pickColor[1])] = modelJob->Entity;
//Render picking stuff
//TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->ModelMatrix));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(m_Camera->ViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(m_Camera->ProjectionMatrix()));
glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
glBindVertexArray(modelJob->Model->VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex);
r += 1;
if(r > 255) {
r = 0;
g += 1;
}
}
}
m_PickingBuffer.Unbind();
GLERROR("PickingPass Error");
//Publish pick event every frame with the pick data that can be picked by the event
Events::Picking pickEvent = Events::Picking(
&m_PickingBuffer,
&m_DepthBuffer,
m_Camera->ProjectionMatrix(),
m_Camera->ViewMatrix(),
m_Resolution,
&m_PickingColorsToEntity);
m_EventBroker->Publish(pickEvent);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return m_PickingPass->Pick(screenCoord);
}
void Renderer::DrawScreenQuad(GLuint textureToDraw)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -287,7 +132,7 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
glClear(GL_COLOR_BUFFER_BIT);
m_DrawScreenQuadProgram.Bind();
m_DrawScreenQuadProgram->Bind();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureToDraw);
@@ -297,24 +142,10 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
, GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups[0].StartIndex);
}
void Renderer::InitializeTextures()
{
m_ErrorTexture=ResourceManager::Load<Texture>("Textures/Core/ErrorTexture.png");
m_WhiteTexture=ResourceManager::Load<Texture>("Textures/Core/Blank.png");
/*
glGenTextures(1, &m_PickingTexture);
glBindTexture(GL_TEXTURE_2D, m_PickingTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, m_Resolution.Width, m_Resolution.Height, 0, GL_RG, GL_FLOAT, NULL);//TODO: Renderer: Fix the precision and Resolution
GLERROR("m_PickingTexture initialization failed");
*/
GenerateTexture(&m_PickingTexture, GL_CLAMP_TO_BORDER, GL_LINEAR,
glm::vec2(m_Resolution.Width, m_Resolution.Height), GL_RG8, GL_RG, GL_UNSIGNED_BYTE);
}
void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type)
@@ -329,15 +160,8 @@ void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filterin
GLERROR("Texture initialization failed");
}
void Renderer::InitializeFrameBuffers()//TODO: Renderer: Get this to a better location, as its really big
void Renderer::InitializeRenderPasses()
{
glGenRenderbuffers(1, &m_DepthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_Resolution.Width, m_Resolution.Height);
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new RenderBuffer(&m_DepthBuffer, GL_DEPTH_ATTACHMENT)));
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_PickingTexture, GL_COLOR_ATTACHMENT0)));
m_PickingBuffer.Generate();
}
m_DrawScenePass = new DrawScenePass(this);
m_PickingPass = new PickingPass(this, m_EventBroker);
}
+13 -12
View File
@@ -17,10 +17,11 @@ void TextRenderer::Initialize()
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
m_TextProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Text.vert.glsl")));
m_TextProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Text.frag.glsl")));
m_TextProgram.Compile();
m_TextProgram.Link();
m_TextProgram = ResourceManager::Load<ShaderProgram>("#TextProgram");
m_TextProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Text.vert.glsl")));
m_TextProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Text.frag.glsl")));
m_TextProgram->Compile();
m_TextProgram->Link();
}
void TextRenderer::Update()
@@ -28,12 +29,12 @@ void TextRenderer::Update()
}
void TextRenderer::Draw(RenderQueue &rq, glm::mat4 projection, glm::mat4 view)
void TextRenderer::Draw(RenderScene& scene)
{
for (auto &job : rq) {
for (auto &job : scene.TextJobs) {
auto textJob = std::dynamic_pointer_cast<TextJob>(job);
if (textJob) {
RenderText(textJob->Content, textJob->Resource, 0.01f, textJob->Color, textJob->ModelMatrix, projection, view);
RenderText(textJob->Content, textJob->Resource, 0.01f, textJob->Color, textJob->Matrix, scene.Camera->ProjectionMatrix(), scene.Camera->ViewMatrix());
}
}
}
@@ -50,11 +51,11 @@ void TextRenderer::RenderText(std::string text, Font* font, GLfloat scale, glm::
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_TextProgram.Bind();
glUniform3f(glGetUniformLocation(m_TextProgram.GetHandle(), "textColor"), color.x, color.y, color.z);
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram.GetHandle(), "M"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram.GetHandle(), "V"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram.GetHandle(), "P"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
m_TextProgram->Bind();
glUniform3f(glGetUniformLocation(m_TextProgram->GetHandle(), "textColor"), color.x, color.y, color.z);
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram->GetHandle(), "M"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram->GetHandle(), "V"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram->GetHandle(), "P"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(VAO);
+2 -3
View File
@@ -32,11 +32,10 @@ glm::vec3 ScreenCoords::ToWorldPos(glm::vec2 screenCoord, float depth, float scr
ScreenCoords::PixelData ScreenCoords::ToPixelData(float x, float y, FrameBuffer* PickDataBuffer, GLuint DepthBuffer)
{
PickDataBuffer->Bind();
unsigned char pdata[2];
glReadPixels(x, y, 1, 1, GL_RG, GL_UNSIGNED_BYTE, &pdata);
unsigned char pdata[3];
glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pdata);
PickDataBuffer->Unbind();
glBindFramebuffer(GL_FRAMEBUFFER, DepthBuffer);
float depthData;
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depthData);
+2
View File
@@ -19,6 +19,8 @@ file(GLOB SOURCE_FILES
set(SOURCE_FILES
${SOURCE_FILES}
"Game.cpp"
"HealthSystem.cpp"
"PlayerSystem.cpp"
)
set(LIBRARIES
+118 -69
View File
@@ -1,109 +1,158 @@
#include "Game.h"
#include "Collision/TriggerSystem.h"
#include "Collision/CollisionSystem.h"
#include "Game/HealthSystem.h"
#include "Core/EntityFileWriter.h"
Game::Game(int argc, char* argv[])
{
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
ResourceManager::RegisterType<Model>("Model");
ResourceManager::RegisterType<Texture>("Texture");
ResourceManager::RegisterType<EntityXMLFile>("EntityXMLFile");
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
ResourceManager::RegisterType<Model>("Model");
ResourceManager::RegisterType<Texture>("Texture");
ResourceManager::RegisterType<ShaderProgram>("ShaderProgram");
ResourceManager::RegisterType<EntityFile>("EntityFile");
ResourceManager::RegisterType<Font>("FontFile");
m_Config = ResourceManager::Load<ConfigFile>("Config.ini");
LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get<int>("Debug.LogLevel", 1));
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 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();
// Create the renderer
m_Renderer = new Renderer(m_EventBroker, m_World);
m_Renderer->SetFullscreen(m_Config->Get<bool>("Video.Fullscreen", false));
m_Renderer->SetVSYNC(m_Config->Get<bool>("Video.VSYNC", false));
m_Renderer->SetResolution(Rectangle::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);
// 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 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 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);
auto file = ResourceManager::Load<EntityFile>(mapToLoad);
EntityFilePreprocessor fpp(file);
fpp.RegisterComponents(m_World);
EntityFileParser fp(file);
fp.MergeEntities(m_World);
}
m_RenderFrame = new RenderFrame();
// Create system pipeline
m_SystemPipeline = new SystemPipeline(m_EventBroker);
m_SystemPipeline->AddSystem<RaptorCopterSystem>();
m_LastTime = glfwGetTime();
//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);
testIntialize();
//Collision and TriggerSystem should update after player.
++updateOrderLevel;
m_SystemPipeline->AddSystem<CollisionSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<TriggerSystem>(updateOrderLevel);
++updateOrderLevel;
m_SystemPipeline->AddSystem<RenderSystem>(updateOrderLevel, m_Renderer, m_RenderFrame);
// Invoke network
if (m_Config->Get<bool>("Networking.StartNetwork", false)) {
//boost::thread workerThread(&Game::networkFunction, this);
networkFunction();
}
m_LastTime = glfwGetTime();
}
Game::~Game()
{
delete m_FrameStack;
delete m_EventBroker;
delete m_SystemPipeline;
delete m_World;
delete m_FrameStack;
delete m_InputProxy;
delete m_InputManager;
delete m_RenderFrame;
delete m_Renderer;
delete m_EventBroker;
}
void Game::Tick()
{
double currentTime = glfwGetTime();
double dt = currentTime - m_LastTime;
m_LastTime = currentTime;
glfwPollEvents();
m_EventBroker->Swap();
m_InputManager->Update(dt);
m_EventBroker->Swap();
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();
// Update network
if (m_IsClientOrServer) {
m_ClientOrServer->Update();
}
// Iterate through systems and update world!
m_SystemPipeline->Update(m_World, dt);
testTick(dt);
m_Renderer->Update(dt);
m_EventBroker->Process<Client>();
m_RenderQueueFactory->Update(m_World);
m_Renderer->Draw(m_RenderQueueFactory->RenderQueues());
m_EventBroker->Swap();
m_EventBroker->Clear();
glfwPollEvents();
GLERROR("Game::Tick m_RenderQueueFactory->Update");
m_Renderer->Draw(*m_RenderFrame);
GLERROR("Game::Tick m_Renderer->Draw");
m_EventBroker->Swap();
m_EventBroker->Clear();
}
bool Game::testOnKeyUp(const Events::KeyUp& e)
{
if (e.KeyCode == GLFW_KEY_R) {
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::testIntialize()
{
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &Game::testOnKeyUp);
}
void Game::testTick(double dt)
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;
}
+59
View File
@@ -0,0 +1,59 @@
#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 and health is greater than 0, then apply it
if (std::get<0>(deltaHP) == player.EntityID && (double)health["Health"] > 0.0f) {
//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);
//clear the remaining hpDeltas for the dead player
for (size_t j = m_DeltaHealthVector.size(); j > 0; j--)
{
if (std::get<0>(m_DeltaHealthVector[j - 1]) == player.EntityID)
m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + j - 1);
}
//break the loop if the player is dead
break;
}
}
}
}
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;
}
+42
View File
@@ -0,0 +1,42 @@
#include "PlayerSystem.h"
void PlayerSystem::UpdateComponent(World * world, ComponentWrapper & player, double dt)
{
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 ((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"];
}
}
bool PlayerSystem::OnTouch(const Events::TriggerTouch &event)
{
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;
}
+1
View File
@@ -12,6 +12,7 @@ include_directories(
)
file(GLOB SOURCE_FILES
"*.h"
"*.cpp"
)
+220
View File
@@ -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()
+66
View File
@@ -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()
+54
View File
@@ -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
+19
View File
@@ -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);
}
+114
View File
@@ -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;
}
+40
View File
@@ -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
+12
View File
@@ -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()
+476
View File
@@ -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()
+160
View File
@@ -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()
+49
View File
@@ -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()
+193
View File
@@ -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();
}
+62
View File
@@ -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
+21
View File
@@ -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()
+134
View File
@@ -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 &center, 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);
}
};
+328
View File
@@ -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;
}
}
+66
View File
@@ -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
+35
View File
@@ -0,0 +1,35 @@
#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 "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()
+1 -1
View File
@@ -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++;
}