Merge remote-tracking branch 'origin/master' into Rendering

# Conflicts:
#	include/Engine/Editor/EditorSystem.h
#	include/Engine/Rendering/EPicking.h
#	include/Engine/Rendering/Renderer.h
#	include/Game/Game.h
#	resources/Schema/Components.xsd
#	resources/Schema/Entities/Test.xml
#	src/Engine/Editor/EditorSystem.cpp
#	src/Engine/Rendering/RawModel.cpp
#	src/Engine/Rendering/RenderQueueFactory.cpp
#	src/Engine/Rendering/Renderer.cpp
#	src/Game/Game.cpp
This commit is contained in:
viktorljung
2016-01-12 16:13:20 +01:00
110 changed files with 6424 additions and 1129 deletions
+23 -1
View File
@@ -69,6 +69,12 @@ file(GLOB SOURCE_FILES_GUI
)
source_group(GUI FILES ${SOURCE_FILES_GUI})
file(GLOB SOURCE_FILES_Collision
"${INCLUDE_PATH}/Collision/*.h"
"Collision/*.cpp"
)
source_group(Collision FILES ${SOURCE_FILES_Collision})
file(GLOB SOURCE_FILES_Editor
"${INCLUDE_PATH}/Editor/*.h"
"Editor/*.cpp"
@@ -83,12 +89,28 @@ set(SOURCE_FILES
${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
${SOURCE_FILES_Editor}
${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}
+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()
{}
+1 -1
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++();
+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)));
}
}
}
}
-495
View File
@@ -1,495 +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) },
{ "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;
}
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_double, status);
if (val == nullptr) {
LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", XSTR(element->getTagName()), attribute);
return 0.f;
} else {
return static_cast<float>(val->fData.fValue.f_double);
}
}
void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string typeName, char* outData)
{
using namespace xercesc;
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 (typeName == "float") {
XSValue::Status status;
XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_float, status);
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue.f_float), getTypeStride(typeName));
} else if (typeName == "double") {
XSValue::Status status;
XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_double, status);
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue.f_double), getTypeStride(typeName));
} else if (typeName == "bool") {
XSValue::Status status;
XSValue* val = XSValue::getActualValue(element->getTextContent(), xercesc::XSValue::DataType::dt_boolean, status);
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue.f_bool), getTypeStride(typeName));
} else {
XSValue::DataType dataType = XSValue::getDataType(XSTR(typeName.c_str()));
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));
LOG_WARNING("Unknown native data type: %s", typeName.c_str());
}
}
}
+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;
}
+18
View File
@@ -2,6 +2,7 @@
std::vector<unsigned int> InputManager::GLFWCharCallbackQueue;
std::vector<std::pair<double, double>> InputManager::GLFWScrollCallbackQueue;
std::vector<std::string> InputManager::GLFWDropCallbackQueue;
void InputManager::Initialize()
{
@@ -10,6 +11,7 @@ void InputManager::Initialize()
//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);
@@ -95,6 +97,14 @@ void InputManager::Update(double dt)
}
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])
// {
@@ -229,6 +239,14 @@ void InputManager::GLFWScrollCallback(GLFWwindow* window, double xoffset, double
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
+36 -7
View File
@@ -10,12 +10,15 @@ World::~World()
EntityID World::CreateEntity(EntityID parent /*= 0*/)
{
EntityID newEntity = generateEntityID();
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
@@ -46,15 +49,26 @@ void World::DeleteEntity(EntityID entity)
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();
@@ -66,8 +80,7 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy
return c;
}
bool World::HasComponent(EntityID entity, std::string componentType)
bool World::HasComponent(EntityID entity, std::string componentType) const
{
ComponentPool* pool = m_ComponentPools.at(componentType);
return pool->KnowsEntity(entity);
@@ -79,7 +92,6 @@ ComponentWrapper World::GetComponent(EntityID entity, std::string componentType)
return pool->GetByEntity(entity);
}
void World::DeleteComponent(EntityID entity, std::string componentType)
{
ComponentPool* pool = m_ComponentPools.at(componentType);
@@ -93,13 +105,11 @@ const ComponentPool* World::GetComponents(std::string 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);
@@ -115,6 +125,25 @@ void World::SetParent(EntityID entity, EntityID 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
+301 -119
View File
@@ -9,6 +9,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* 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;
@@ -18,6 +19,7 @@ EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer)
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)
@@ -35,6 +37,40 @@ void EditorSystem::Update(World* world, double dt)
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)
@@ -74,14 +110,24 @@ bool EditorSystem::OnMousePress(const Events::MousePress& e)
bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
{
if (m_Widget == 0) {
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_Renderer->Camera()->Orientation() * glm::inverse(glm::quat(widgetOrientation));
glm::quat totalOrientation = m_Renderer->Camera()->Orientation() * glm::inverse(glm::quat(widgetOrientation));
int width;
int height;
@@ -112,9 +158,9 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
if (m_WidgetSpace == WidgetSpace::Global) {
EntityID parent = m_World->GetParent(m_Selection);
glm::quat inverseParentOrientation;
if (parent != 0) {
//if (parent != 0) {
inverseParentOrientation = glm::inverse(RenderSystem::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");
@@ -128,12 +174,12 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
if (m_WidgetSpace == WidgetSpace::Global) {
EntityID parent = m_World->GetParent(m_Selection);
glm::quat parentOrientation;
if (parent != 0) {
parentOrientation = RenderSystem::AbsoluteOrientation(m_World, parent);
}
//if (parent != 0) {
// parentOrientation = RenderSystem::AbsoluteOrientation(m_World, parent);
//}
glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"];
//glm::quat currentOrientation = RenderQueueFactory::AbsoluteOrientation(m_World, m_Selection);
glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation);
glm::quat currentOrientation = RenderSystem::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) {
@@ -146,6 +192,13 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
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;
}
@@ -155,14 +208,19 @@ bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
if (m_WidgetCurrentAxis.z > 0) {
scaleZ.z += movement.z;
}
if (m_WidgetCurrentAxis.x > 0 && m_WidgetCurrentAxis.y > 0 && m_WidgetCurrentAxis.z > 0) {
float max = glm::max(scaleX.x, glm::max(scaleY.y, scaleZ.z));
(glm::vec3&)m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] = glm::vec3(max);
}
(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;
}
@@ -182,15 +240,16 @@ void EditorSystem::Picking()
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 != 0) {
if (entity != EntityID_Invalid) {
EntityID parent = m_World->GetParent(entity);
if (parent == m_Widget) {
m_WidgetCurrentAxis = glm::vec3(
(entity == m_WidgetX) || (entity == m_WidgetOrigin),
(entity == m_WidgetY) || (entity == m_WidgetOrigin),
(entity == m_WidgetZ) || (entity == m_WidgetOrigin)
(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;
@@ -199,39 +258,68 @@ void EditorSystem::Picking()
//widgetTransform["Position"] = (glm::vec3)selectionTransform["Position"];
} else {
ImGui::SetActiveID(0, nullptr);
m_Selection = entity;
if (m_WidgetMode == WidgetMode::None) {
m_WidgetMode = WidgetMode::Translate;
}
setWidgetMode(m_WidgetMode);
m_Selection = entity;
}
} else {
m_Selection = 0;
}
}
}
m_PickingQueue.clear();
};
void EditorSystem::updateWidget()
bool EditorSystem::OnFileDropped(const Events::FileDropped& e)
{
if (m_Widget == 0) {
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::Translate);
setWidgetMode(WidgetMode::None);
}
}
void EditorSystem::updateWidget()
{
if (m_Widget == EntityID_Invalid) {
return;
}
if (m_Selection == m_Widget) {
return;
}
if (m_Selection != 0) {
if (m_Selection != EntityID_Invalid) {
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
glm::vec3 selectionPosition = RenderSystem::AbsolutePosition(m_World, m_Selection);
widgetTransform["Position"] = selectionPosition;
@@ -243,23 +331,32 @@ void EditorSystem::updateWidget()
void EditorSystem::setWidgetMode(WidgetMode newMode)
{
if (m_Widget == 0) {
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";
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false;
if (m_Selection != 0) {
// 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(RenderSystem::AbsoluteOrientation(m_World, m_Selection));
@@ -271,16 +368,15 @@ void EditorSystem::setWidgetMode(WidgetMode newMode)
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 != 0) {
if (m_Selection != EntityID_Invalid) {
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
widgetTransform["Orientation"] = (glm::vec3)selectionTransform["Orientation"];
widgetTransform["Orientation"] = glm::eulerAngles(RenderSystem::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";
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false;
if (m_Selection != 0) {
if (m_Selection != EntityID_Invalid) {
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
if (m_WidgetSpace == WidgetSpace::Local) {
widgetTransform["Orientation"] = glm::eulerAngles(RenderSystem::AbsoluteOrientation(m_World, m_Selection));
@@ -290,7 +386,6 @@ void EditorSystem::setWidgetMode(WidgetMode newMode)
m_WidgetMode = newMode;
}
void EditorSystem::setWidgetSpace(WidgetSpace space)
{
m_WidgetSpace = space;
@@ -299,16 +394,23 @@ void EditorSystem::setWidgetSpace(WidgetSpace space)
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("Open", "Ctrl+O")) { }
if (ImGui::MenuItem("Save", "Ctrl+S")) { }
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) { }
//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")) { }
@@ -343,7 +445,7 @@ void EditorSystem::drawUI(World* world, double dt)
std::string title = std::string("Components #") + std::to_string(m_Selection) + std::string("###Components");
if (ImGui::Begin(title.c_str())) {
if (m_Selection != 0) {
if (m_Selection != EntityID_Invalid) {
auto& pools = world->GetComponentPools();
std::vector<const char*> componentTypes;
@@ -383,41 +485,57 @@ void EditorSystem::drawUI(World* world, double dt)
}
auto& component = world->GetComponent(m_Selection, componentType);
for (auto& pair : ci.FieldTypes) {
const std::string& field = pair.first;
const std::string& type = pair.second;
for (auto& kv : ci.Fields) {
const std::string& fieldName = kv.first;
auto& field = kv.second;
if (type == "Vector") {
auto& val = component.Property<glm::vec3>(field);
if (field == "Scale") {
ImGui::DragFloat3(field.c_str(), glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits<float>::max());
} else if (field == "Orientation") {
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(field.c_str(), glm::value_ptr(tempVal), 0.f, glm::two_pi<float>())) {
if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi<float>())) {
val = tempVal;
}
} else {
ImGui::DragFloat3(field.c_str(), glm::value_ptr(val), 0.1f, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
}
} else if (type == "Color") {
auto& val = component.Property<glm::vec4>(field);
ImGui::ColorEdit4(field.c_str(), glm::value_ptr(val), true);
} else if (type == "string") {
std::string& val = component.Property<std::string>(field);
} 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(field.c_str(), tempString, sizeof(tempString))) {
if (ImGui::InputText("", tempString, sizeof(tempString))) {
val = std::string(tempString);
LOG_DEBUG("%s::%s changed!", componentType.c_str(), field.c_str());
LOG_DEBUG("%s::%s changed!", componentType.c_str(), fieldName.c_str());
}
} else if (type == "double") {
float tempVal = static_cast<float>(component.Property<double>(field));
if (ImGui::InputFloat(field.c_str(), &tempVal, 0.01f, 1.f)) {
component.SetProperty(field, static_cast<double>(tempVal));
// DROP STUFF
if (ImGui::IsItemHovered() && !m_LastDroppedFile.empty()) {
val = m_LastDroppedFile;
m_LastDroppedFile = "";
}
} else if (type == "bool") {
auto& val = component.Property<bool>(field);
ImGui::Checkbox(field.c_str(), &val);
} 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");
}
}
}
@@ -427,74 +545,93 @@ void EditorSystem::drawUI(World* world, double dt)
}
ImGui::End();
if (ImGui::Begin("Entitites")) {
static EntityID draggingEntity = 0;
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++) {
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 == it->second) {
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(it->second)).c_str());
bool hovered = false;
bool held = false;
if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) {
m_Selection = it->second;
}
if (held) {
ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0);
if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) {
if (draggingEntity == 0) {
draggingEntity = it->second;
LOG_DEBUG("Started drag of entity %i", draggingEntity);
}
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", draggingEntity);
ImGui::End();
}
}
ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once);
if (ImGui::TreeNode((std::string("#") + std::to_string(it->second)).c_str())) {
if (draggingEntity != 0 && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) {
LOG_DEBUG("Changed parent of %i to %i", draggingEntity, it->second);
changeParent(draggingEntity, it->second);
draggingEntity = 0;
}
if (ImGui::BeginPopupContextItem("item context menu")) {
if (ImGui::Button("Add")) {
EntityID entity = world->CreateEntity(it->second);
world->AttachComponent(entity, "Transform");
}
ImGui::SameLine();
if (ImGui::Button("Delete")) {
world->DeleteEntity(it->second);
ImGui::CloseCurrentPopup();
if (m_Selection == it->second) {
m_Selection = 0;
}
}
ImGui::EndPopup();
}
if (createEntityNode(world, it->second)) {
recurse(it->second);
ImGui::TreePop();
}
}
};
recurse(0);
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();
@@ -529,3 +666,48 @@ void EditorSystem::changeParent(EntityID entity, EntityID newParent)
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();
}
+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;
}
+1 -1
View File
@@ -117,7 +117,7 @@ PickData PickingPass::Pick(glm::vec2 screenCoord)
if (it != m_PickingColorsToEntity.end()) {
pickData.Entity = it->second;
} else {
pickData.Entity = 0;
pickData.Entity = EntityID_Invalid;
}
pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, resolution, m_Camera->ProjectionMatrix(), m_Camera->ViewMatrix());
+4 -4
View File
@@ -76,18 +76,18 @@ RawModel::RawModel(std::string fileName)
}
// Material diffuse color
aiColor4D diffuse;
aiColor3D diffuse;
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
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);
}
+56 -51
View File
@@ -33,8 +33,11 @@ bool RenderSystem::OnSetCamera(const Events::SetCamera &event)
void RenderSystem::switchCamera(EntityID entity)
{
if(m_World->HasComponent(entity, "Camera")) {
if (m_World->HasComponent(m_CurrentCamera, "Model")) {
m_World->GetComponent(m_CurrentCamera, "Model")["Visible"] = true;
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")) {
@@ -75,16 +78,12 @@ glm::vec3 RenderSystem::AbsolutePosition(World* world, EntityID entity)
{
glm::vec3 position;
do {
while (entity != EntityID_Invalid) {
ComponentWrapper transform = world->GetComponent(entity, "Transform");
EntityID parent = world->GetParent(entity);
if (parent != 0) {
position += AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"];
} else {
position += (glm::vec3)transform["Position"];
}
position += AbsoluteOrientation(world, parent) * (glm::vec3)transform["Position"];
entity = parent;
} while (entity != 0);
}
return position;
}
@@ -93,11 +92,11 @@ glm::quat RenderSystem::AbsoluteOrientation(World* world, EntityID entity)
{
glm::quat orientation;
do {
while (entity != EntityID_Invalid) {
ComponentWrapper transform = world->GetComponent(entity, "Transform");
orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation;
entity = world->GetParent(entity);
} while (entity != 0);
}
return orientation;
}
@@ -106,28 +105,28 @@ glm::vec3 RenderSystem::AbsoluteScale(World* world, EntityID entity)
{
glm::vec3 scale(1.f);
do {
while (entity != EntityID_Invalid) {
ComponentWrapper transform = world->GetComponent(entity, "Transform");
scale *= (glm::vec3)transform["Scale"];
entity = world->GetParent(entity);
} while (entity != 0);
}
return scale;
}
glm::mat4 RenderSystem::ModelMatrix(EntityID entity)
glm::mat4 RenderSystem::ModelMatrix(EntityID entity, World* world)
{
glm::vec3 position = AbsolutePosition(m_World, entity);
glm::quat orientation = AbsoluteOrientation(m_World, entity);
glm::vec3 scale = AbsoluteScale(m_World, 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;
}
void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs)
void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
{
auto models = m_World->GetComponents("Model");
auto models = world->GetComponents("Model");
if (models == nullptr) {
return;
}
@@ -147,7 +146,7 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs)
model = ResourceManager::Load<::Model>("Models/Core/Error.obj");
}
glm::mat4 modelMatrix = ModelMatrix(modelComponent.EntityID);
glm::mat4 modelMatrix = ModelMatrix(modelComponent.EntityID, world);
glm::mat4 matrix = m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix() * (model->m_Matrix * modelMatrix);
for (auto texGroup : model->TextureGroups) {
@@ -179,19 +178,18 @@ void RenderSystem::Update(World* world, double dt)
RenderScene* rs = new RenderScene();
rs->Camera = m_Camera;
rs->ViewPort = Rectangle(1280, 720);
fillModels(rs->ForwardJobs);
fillModels(rs->ForwardJobs, world);
m_RenderFrame->Add(*rs);
delete rs;
}
void RenderSystem::updateCamera(World* world, double dt)
{
static DebugCameraInputController<RenderSystem> firstPersonInputController(m_EventBroker, -1);
static DebugCameraInputController<RenderSystem> firstPersonInputController(m_EventBroker, -1);
if (m_SwitchCamera) {
auto cameras = world->GetComponents("Camera");
if (cameras != nullptr) {
if (m_SwitchCamera) {
auto cameras = world->GetComponents("Camera");
for (auto it = cameras->begin(); it != cameras->end(); it++) {
if ((*it).EntityID == m_CurrentCamera) {
it++;
@@ -208,36 +206,43 @@ void RenderSystem::updateCamera(World* world, double dt)
firstPersonInputController.SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
firstPersonInputController.SetPosition(cameraTransform["Position"]);
}
}
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");
if (world->GetParent(m_CurrentCamera) == 1) { // world is entity 1, is this ok?
firstPersonInputController.Update(dt);
(glm::vec3&)cameraTransform["Orientation"] = glm::eulerAngles(firstPersonInputController.Orientation());
(glm::vec3&)cameraTransform["Position"] = firstPersonInputController.Position();
}
glm::vec3 position = AbsolutePosition(world, m_CurrentCamera);
glm::quat orientation = AbsoluteOrientation(world, m_CurrentCamera);
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");
m_Camera->SetPosition(position);
m_Camera->SetOrientation(orientation);
firstPersonInputController.Update(dt);
(glm::vec3&)cameraTransform["Orientation"] = glm::eulerAngles(firstPersonInputController.Orientation());
(glm::vec3&)cameraTransform["Position"] = firstPersonInputController.Position();
updateProjectionMatrix(cameraComponent);
m_Camera->UpdateViewMatrix();
} else {
m_Camera = m_DefaultCamera;
glm::vec3 position = AbsolutePosition(world, m_CurrentCamera);
glm::quat orientation = AbsoluteOrientation(world, m_CurrentCamera);
auto cameras = world->GetComponents("Camera");
if (cameras != nullptr) {
ComponentWrapper& cameraC = *cameras->begin();
switchCamera(cameraC.EntityID);
world->GetComponent(m_CurrentCamera, "Model")["Visible"] = false;
m_Camera->SetPosition(position);
m_Camera->SetOrientation(orientation);
updateProjectionMatrix(cameraComponent);
m_Camera->UpdateViewMatrix();
}
} 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"]);
}
}
}
}
}
-1
View File
@@ -1,5 +1,4 @@
#include "Rendering/Renderer.h"
#include "Rendering/DebugCameraInputController.h"
void Renderer::Initialize()
{