Merge branch 'Rendering' into TextRendering
# Conflicts: # include/Engine/Rendering/RenderQueue.h # include/Engine/Rendering/RenderQueueFactory.h # include/Engine/Rendering/Renderer.h # include/Engine/Rendering/ShaderProgram.h # include/Game/Game.h # resources/Schema/Components.xsd # resources/Schema/Entities/Test.xml # resources/Schema/Types/Entity.xsd # src/Engine/Rendering/RenderQueueFactory.cpp # src/Engine/Rendering/Renderer.cpp # src/Game/Game.cpp
This commit is contained in:
@@ -62,7 +62,6 @@ file(GLOB SOURCE_FILES_Rendering_Util
|
||||
"${INCLUDE_PATH}/Rendering/Util/*.h"
|
||||
"Rendering/Util/*.cpp"
|
||||
)
|
||||
|
||||
source_group(Rendering FILES ${SOURCE_FILES_Rendering})
|
||||
source_group(Rendering\\Util FILES ${SOURCE_FILES_Rendering_Util})
|
||||
|
||||
@@ -72,16 +71,48 @@ file(GLOB SOURCE_FILES_GUI
|
||||
)
|
||||
source_group(GUI FILES ${SOURCE_FILES_GUI})
|
||||
|
||||
file(GLOB SOURCE_FILES_Collision
|
||||
"${INCLUDE_PATH}/Collision/*.h"
|
||||
"Collision/*.cpp"
|
||||
)
|
||||
source_group(Collision FILES ${SOURCE_FILES_Collision})
|
||||
|
||||
file(GLOB SOURCE_FILES_Editor
|
||||
"${INCLUDE_PATH}/Editor/*.h"
|
||||
"Editor/*.cpp"
|
||||
)
|
||||
source_group(Editor FILES ${SOURCE_FILES_Editor})
|
||||
|
||||
set(SOURCE_FILES
|
||||
${SOURCE_FILES_Core}
|
||||
${SOURCE_FILES_Core_Util}
|
||||
#${SOURCE_FILES_Input}
|
||||
${SOURCE_FILES_Input}
|
||||
${SOURCE_FILES_Network}
|
||||
${SOURCE_FILES_GUI}
|
||||
${SOURCE_FILES_Rendering}
|
||||
${SOURCE_FILES_Rendering_Util}
|
||||
${SOURCE_FILES_Collision}
|
||||
${SOURCE_FILES_Editor}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_draw.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/imgui/imgui_demo.cpp
|
||||
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_common.c
|
||||
)
|
||||
|
||||
# nativefiledialog
|
||||
if(WIN32)
|
||||
set(SOURCE_FILES ${SOURCE_FILES}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_win.cpp
|
||||
)
|
||||
endif()
|
||||
if(UNIX)
|
||||
set(SOURCE_FILES ${SOURCE_FILES}
|
||||
${CMAKE_SOURCE_DIR}/deps/include/nativefiledialog/nfd_gtk.c
|
||||
# TODO: Link with GTK+ here!
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
set(LIBRARIES
|
||||
${OPENGL_LIBRARIES}
|
||||
${GLEW_LIBRARIES}
|
||||
@@ -106,4 +137,4 @@ target_link_libraries(Engine
|
||||
${LIBRARIES}
|
||||
)
|
||||
#set_target_properties(Engine PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${INCLUDE_PATH}/PrecompiledHeader.h")
|
||||
#cotire(Engine)
|
||||
#cotire(Engine)
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "Collision/Collision.h"
|
||||
#include "Engine/GLM.h"
|
||||
#include "Core/World.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
namespace Collision
|
||||
{
|
||||
|
||||
//note: this one hasnt been delta adjusted like RayVsAABB has
|
||||
bool RayAABBIntr(const Ray& ray, const AABB& box)
|
||||
{
|
||||
glm::vec3 w = 75.0f * ray.Direction();
|
||||
glm::vec3 v = glm::abs(w);
|
||||
glm::vec3 c = ray.Origin() - box.Center() + w;
|
||||
glm::vec3 half = box.HalfSize();
|
||||
|
||||
if (abs(c.x) > v.x + half.x) {
|
||||
return false;
|
||||
}
|
||||
if (abs(c.y) > v.y + half.y) {
|
||||
return false;
|
||||
}
|
||||
if (abs(c.z) > v.z + half.z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (abs(c.y*w.z - c.z*w.y) > half.y*v.z + half.z*v.y) {
|
||||
return false;
|
||||
}
|
||||
if (abs(c.x*w.z - c.z*w.x) > half.x*v.z + half.z*v.x) {
|
||||
return false;
|
||||
}
|
||||
return !(abs(c.x*w.y - c.y*w.x) > half.x*v.y + half.y*v.x);
|
||||
}
|
||||
|
||||
bool RayVsAABB(const Ray& ray, const AABB& box)
|
||||
{
|
||||
float dummy;
|
||||
return RayVsAABB(ray, box, dummy);
|
||||
}
|
||||
|
||||
bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance)
|
||||
{
|
||||
glm::vec3 invdir = 1.0f / ray.Direction();
|
||||
glm::vec3 origin = ray.Origin();
|
||||
|
||||
float t1 = (box.MinCorner().x - origin.x)*invdir.x;
|
||||
float t2 = (box.MaxCorner().x - origin.x)*invdir.x;
|
||||
float t3 = (box.MinCorner().y - origin.y)*invdir.y;
|
||||
float t4 = (box.MaxCorner().y - origin.y)*invdir.y;
|
||||
float t5 = (box.MinCorner().z - origin.z)*invdir.z;
|
||||
float t6 = (box.MaxCorner().z - origin.z)*invdir.z;
|
||||
|
||||
float tmin = std::max(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6));
|
||||
float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6));
|
||||
|
||||
//if (tmax < 0 || tmin > tmax)
|
||||
//if tmin,tmax are almost the same (i.e. hitting exactly in the corner) then tmin might be slightly
|
||||
//greater than tmax becuase of floating-precision problems. fixed by adding a small delta to tmax
|
||||
if (tmax < 0 || tmin>(tmax + 0.0001f))
|
||||
return false;
|
||||
|
||||
outDistance = (tmin > 0) ? tmin : tmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b)
|
||||
{
|
||||
const glm::vec3& aCenter = a.Center();
|
||||
const glm::vec3& bCenter = b.Center();
|
||||
const glm::vec3& aHSize = a.HalfSize();
|
||||
const glm::vec3& bHSize = b.HalfSize();
|
||||
//Test will probably exit because of the X and Z axes more often, so test them first.
|
||||
if (abs(aCenter[0] - bCenter[0]) > (aHSize[0] + bHSize[0])) {
|
||||
return false;
|
||||
}
|
||||
if (abs(aCenter[2] - bCenter[2]) > (aHSize[2] + bHSize[2])) {
|
||||
return false;
|
||||
}
|
||||
return (abs(aCenter[1] - bCenter[1]) <= (aHSize[1] + bHSize[1]));
|
||||
}
|
||||
|
||||
bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation)
|
||||
{
|
||||
minimumTranslation = glm::vec3(0, 0, 0);
|
||||
const glm::vec3& aMax = a.MaxCorner();
|
||||
const glm::vec3& bMax = b.MaxCorner();
|
||||
const glm::vec3& aMin = a.MinCorner();
|
||||
const glm::vec3& bMin = b.MinCorner();
|
||||
const glm::vec3& bSize = b.Size();
|
||||
const glm::vec3& aSize = a.Size();
|
||||
float minOffset = INFINITY;
|
||||
float off;
|
||||
auto axisesIntersecting = glm::tvec3<bool, glm::highp>(false, false, false);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
off = bMax[i] - aMin[i];
|
||||
if (off > 0 && off < bSize[i] + aSize[i]) {
|
||||
if (off < minOffset) {
|
||||
minimumTranslation = glm::vec3();
|
||||
minimumTranslation[i] = minOffset = off;
|
||||
}
|
||||
axisesIntersecting[i] = true;
|
||||
}
|
||||
off = aMax[i] - bMin[i];
|
||||
if (off > 0 && off < bSize[i] + aSize[i]) {
|
||||
if (off < minOffset) {
|
||||
minOffset = off;
|
||||
minimumTranslation = glm::vec3();
|
||||
minimumTranslation[i] = -off;
|
||||
}
|
||||
axisesIntersecting[i] = true;
|
||||
}
|
||||
}
|
||||
return glm::all(axisesIntersecting);
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices)
|
||||
{
|
||||
for (int i = 0; i < modelIndices.size(); ++i) {
|
||||
glm::vec3 v0 = modelVertices[modelIndices[i]].Position;
|
||||
glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0
|
||||
glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0
|
||||
glm::vec3 m = ray.Origin() - v0;
|
||||
glm::vec3 MxE1 = glm::cross(m, e1);
|
||||
glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);
|
||||
float DetInv = glm::dot(e1, DxE2);
|
||||
if (std::abs(DetInv) < FLT_EPSILON) {
|
||||
continue;
|
||||
}
|
||||
DetInv = 1.0f / DetInv;
|
||||
float u = glm::dot(m, DxE2) * DetInv;
|
||||
float v = glm::dot(ray.Direction(), MxE1) * DetInv;
|
||||
//u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem
|
||||
if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) {
|
||||
continue;
|
||||
}
|
||||
//Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit.
|
||||
if (0 <= glm::dot(e2, MxE1) * DetInv) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
float& outDistance,
|
||||
float& outUCoord,
|
||||
float& outVCoord)
|
||||
{
|
||||
outDistance = INFINITY;
|
||||
bool hit = false;
|
||||
for (int i = 0; i < modelIndices.size(); ++i) {
|
||||
glm::vec3 v0 = modelVertices[modelIndices[i]].Position;
|
||||
glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0
|
||||
glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0
|
||||
glm::vec3 m = ray.Origin() - v0;
|
||||
glm::vec3 MxE1 = glm::cross(m, e1);
|
||||
glm::vec3 DxE2 = glm::cross(ray.Direction(), e2);//pVec
|
||||
float DetInv = glm::dot(e1, DxE2);
|
||||
if (std::abs(DetInv) < FLT_EPSILON) {
|
||||
continue;
|
||||
}
|
||||
DetInv = 1.0f / DetInv;
|
||||
float dist = glm::dot(e2, MxE1) * DetInv;
|
||||
if (dist >= outDistance) {
|
||||
continue;
|
||||
}
|
||||
float u = glm::dot(m, DxE2) * DetInv;
|
||||
float v = glm::dot(ray.Direction(), MxE1) * DetInv;
|
||||
|
||||
//u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem
|
||||
//If u and v are positive, u+v <= 1, dist is positive, and less than closest.
|
||||
if (0 <= (u + 0.001f) && 0 <= (v + 0.001f) && u + v <= 1 && 0 <= dist) {
|
||||
outDistance = dist;
|
||||
outUCoord = u;
|
||||
outVCoord = v;
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
glm::vec3& outHitPosition)
|
||||
{
|
||||
float u;
|
||||
float v;
|
||||
float dist;
|
||||
bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v);
|
||||
outHitPosition = ray.Origin() + dist * ray.Direction();
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon)
|
||||
{
|
||||
const glm::vec3& ma1 = first.MaxCorner();
|
||||
const glm::vec3& ma2 = first.MaxCorner();
|
||||
const glm::vec3& mi1 = second.MinCorner();
|
||||
const glm::vec3& mi2 = second.MinCorner();
|
||||
return (std::abs(ma1.x - ma2.x) < epsilon) &&
|
||||
(std::abs(mi1.x - mi2.x) < epsilon) &&
|
||||
(std::abs(ma1.z - ma2.z) < epsilon) &&
|
||||
(std::abs(mi1.z - mi2.z) < epsilon) &&
|
||||
(std::abs(ma1.y - ma2.y) < epsilon) &&
|
||||
(std::abs(mi1.y - mi2.y) < epsilon);
|
||||
}
|
||||
|
||||
bool attachAABBComponentFromModel(World* world, EntityID id)
|
||||
{
|
||||
if (!world->HasComponent(id, "Model")) {
|
||||
return false;
|
||||
}
|
||||
ComponentWrapper model = world->GetComponent(id, "Model");
|
||||
ComponentWrapper collision = world->AttachComponent(id, "AABB");
|
||||
Model* modelRes = ResourceManager::Load<Model>(model["Resource"]);
|
||||
if (modelRes == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = modelRes->m_Matrix;
|
||||
|
||||
glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY);
|
||||
glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY);
|
||||
for (const auto& v : modelRes->m_Vertices) {
|
||||
const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1);
|
||||
maxi.x = std::max(wPos.x, maxi.x);
|
||||
maxi.y = std::max(wPos.y, maxi.y);
|
||||
maxi.z = std::max(wPos.z, maxi.z);
|
||||
mini.x = std::min(wPos.x, mini.x);
|
||||
mini.y = std::min(wPos.y, mini.y);
|
||||
mini.z = std::min(wPos.z, mini.z);
|
||||
}
|
||||
collision["BoxCenter"] = 0.5f * (maxi + mini);
|
||||
collision["BoxSize"] = maxi - mini;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox)
|
||||
{
|
||||
ComponentWrapper& cTrans = world->GetComponent(AABBComponent.EntityID, "Transform");
|
||||
ComponentWrapper model = world->GetComponent(AABBComponent.EntityID, "Model");
|
||||
Model* modelRes = ResourceManager::Load<Model>(model["Resource"]);
|
||||
outBox.CreateFromCenter(AABBComponent["BoxCenter"], AABBComponent["BoxSize"]);
|
||||
glm::vec3 mini = outBox.MinCorner();
|
||||
glm::vec3 maxi = outBox.MaxCorner();
|
||||
|
||||
if (modelRes == nullptr) {
|
||||
return false;
|
||||
}
|
||||
glm::mat4 modelMatrix = modelRes->m_Matrix *
|
||||
glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) *
|
||||
glm::scale((glm::vec3)cTrans["Scale"]);
|
||||
|
||||
outBox = AABB(modelMatrix * glm::vec4(mini.x, mini.y, mini.z, 1),
|
||||
modelMatrix * glm::vec4(maxi.x, maxi.y, maxi.z, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetEntityBox(World* world, EntityID entity, AABB& outBox, bool forceBoxFromModel)
|
||||
{
|
||||
if (!world->HasComponent(entity, "AABB")) {
|
||||
if (forceBoxFromModel) {
|
||||
if (!attachAABBComponentFromModel(world, entity))
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ComponentWrapper& cBox = world->GetComponent(entity, "AABB");
|
||||
return GetEntityBox(world, cBox, outBox);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "Collision/Collision.h"
|
||||
#include "Collision/CollisionSystem.h"
|
||||
#include "Core/AABB.h"
|
||||
|
||||
void CollisionSystem::UpdateComponent(World * world, ComponentWrapper & cAABB, double dt)
|
||||
{
|
||||
//Right now, cAABB is a component attached to any entity that should be collideable.
|
||||
AABB thisBox;
|
||||
if (!Collision::GetEntityBox(world, cAABB, thisBox)) {
|
||||
return;
|
||||
}
|
||||
//Press 'Z' to enable/disable collision.
|
||||
if (zPress) {
|
||||
return;
|
||||
}
|
||||
//Here, mover should be an object that moves, currently only players.
|
||||
for (auto& mover : *world->GetComponents("Player")) {
|
||||
if (cAABB.EntityID == mover.EntityID) {
|
||||
continue;
|
||||
}
|
||||
AABB otherBox;
|
||||
if (!Collision::GetEntityBox(world, mover.EntityID, otherBox)) {
|
||||
continue;
|
||||
}
|
||||
glm::vec3 resolveTranslation;
|
||||
if (Collision::AABBVsAABB(otherBox, thisBox, resolveTranslation)) {
|
||||
ComponentWrapper& trans = world->GetComponent(mover.EntityID, "Transform");
|
||||
//TODO: Special treatment if both are movers.
|
||||
trans["Position"] = (glm::vec3)trans["Position"] + resolveTranslation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CollisionSystem::OnKeyUp(const Events::KeyUp & event)
|
||||
{
|
||||
if (event.KeyCode == GLFW_KEY_Z) {
|
||||
zPress = !zPress;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "Collision/TriggerSystem.h"
|
||||
#include "Collision/Collision.h"
|
||||
#include "Core/AABB.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
void TriggerSystem::UpdateComponent(World* world, ComponentWrapper& trigger, double dt)
|
||||
{
|
||||
//Currently only players can trigger things.
|
||||
auto players = world->GetComponents("Player");
|
||||
if (players == nullptr) {
|
||||
return;
|
||||
}
|
||||
EntityID tId = trigger.EntityID;
|
||||
AABB triggerBox;
|
||||
//The trigger *should* have a bounding box, or something, to test against so it can be triggered.
|
||||
if (!Collision::GetEntityBox(world, tId, triggerBox, true)) {
|
||||
return;
|
||||
}
|
||||
for (auto& pc : *players) {
|
||||
EntityID pId = pc.EntityID;
|
||||
AABB playerBox;
|
||||
//The player can't trigger anything without an AABB.
|
||||
if (!Collision::GetEntityBox(world, pId, playerBox, true)) {
|
||||
continue;
|
||||
}
|
||||
if (!Collision::AABBVsAABB(triggerBox, playerBox)) {
|
||||
//Entity is not touching the trigger,
|
||||
//Throw event if it was previously.
|
||||
if (throwLeaveIfWasInTrigger(m_EntitiesTouchingTrigger[tId], pId, tId)) {
|
||||
continue;
|
||||
}
|
||||
//This only occurs if the entity was completely inside the trigger one frame,
|
||||
//then completely outside the trigger, e.g. when dying and respawning.
|
||||
throwLeaveIfWasInTrigger(m_EntitiesCompletelyInTrigger[tId], pId, tId);
|
||||
} else {
|
||||
//Entity is at least touching the trigger.
|
||||
AABB completelyInsideBox;
|
||||
completelyInsideBox.CreateFromCenter(triggerBox.Center(), triggerBox.Size() - 2.0f * playerBox.Size());
|
||||
if (Collision::AABBVsAABB(completelyInsideBox, playerBox) &&
|
||||
glm::all(glm::greaterThan(triggerBox.Size(), playerBox.Size()))) {
|
||||
//Entity is completely inside the trigger.
|
||||
//If it was only touching before, it is erased.
|
||||
m_EntitiesTouchingTrigger[tId].erase(pId);
|
||||
std::unordered_set<EntityID>& completeSet = m_EntitiesCompletelyInTrigger[tId];
|
||||
if (completeSet.count(pId) == 0) {
|
||||
//If it wasn't completely in the trigger, throw Enter and add to the set.
|
||||
completeSet.insert(pId);
|
||||
publish<Events::TriggerEnter>(pId, tId);
|
||||
}
|
||||
} else {
|
||||
//Entity is only touching the trigger.
|
||||
std::unordered_set<EntityID>& touchSet = m_EntitiesTouchingTrigger[tId];
|
||||
std::unordered_set<EntityID>& completeSet = m_EntitiesCompletelyInTrigger[tId];
|
||||
const auto& it = completeSet.find(pId);
|
||||
//If it was completely inside before.
|
||||
if (it != completeSet.end()) {
|
||||
completeSet.erase(it);
|
||||
touchSet.insert(pId);
|
||||
//If it was completely outside before.
|
||||
} else if (touchSet.count(pId) == 0) {
|
||||
publish<Events::TriggerTouch>(pId, tId);
|
||||
touchSet.insert(pId);
|
||||
}
|
||||
//Else, it was touching the trigger last frame too and nothing is done.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TriggerSystem::throwLeaveIfWasInTrigger(std::unordered_set<EntityID>& triggerSet, EntityID pId, EntityID tId)
|
||||
{
|
||||
const auto& it = triggerSet.find(pId);
|
||||
if (it != triggerSet.end()) {
|
||||
//If it was in the trigger, but not anymore, throw leaveEvent and erase from the set.
|
||||
triggerSet.erase(it);
|
||||
publish<Events::TriggerLeave>(pId, tId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "Core/AABB.h"
|
||||
#include "Common.h"
|
||||
|
||||
AABB::AABB(const glm::vec3& minPos, const glm::vec3& maxPos)
|
||||
: m_MinCorner(minPos)
|
||||
, m_MaxCorner(maxPos)
|
||||
, m_Center(0.5f * (maxPos + minPos))
|
||||
, m_HalfSize(0.5f * (maxPos - minPos))
|
||||
{
|
||||
DEBUG_IF(glm::any(glm::lessThan(m_MaxCorner, m_MinCorner))) {
|
||||
LOG_WARNING("AABB maxCorner coordinates are not greater than minCorner");
|
||||
m_MaxCorner.x = glm::max(m_MaxCorner.x, m_MinCorner.x);
|
||||
m_MinCorner.x = glm::min(m_MaxCorner.x, m_MinCorner.x);
|
||||
m_MaxCorner.y = glm::max(m_MaxCorner.y, m_MinCorner.y);
|
||||
m_MinCorner.y = glm::min(m_MaxCorner.y, m_MinCorner.y);
|
||||
m_MaxCorner.z = glm::max(m_MaxCorner.z, m_MinCorner.z);
|
||||
m_MinCorner.z = glm::min(m_MaxCorner.z, m_MinCorner.z);
|
||||
}
|
||||
}
|
||||
|
||||
AABB::AABB(const glm::vec4& minPos, const glm::vec4& maxPos)
|
||||
: AABB(glm::vec3(minPos), glm::vec3(maxPos))
|
||||
{}
|
||||
|
||||
void AABB::CreateFromCenter(const glm::vec3& center, const glm::vec3& size)
|
||||
{
|
||||
m_Center = center;
|
||||
m_HalfSize = 0.5f * size;
|
||||
m_MinCorner = m_Center - m_HalfSize;
|
||||
m_MaxCorner = m_Center + m_HalfSize;
|
||||
}
|
||||
|
||||
AABB::~AABB()
|
||||
{}
|
||||
@@ -19,7 +19,7 @@ bool ComponentPoolForwardIterator::operator!=(const ComponentPoolForwardIterator
|
||||
return m_MemoryPoolIterator != other.m_MemoryPoolIterator;
|
||||
}
|
||||
|
||||
ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++(int)
|
||||
ComponentPoolForwardIterator ComponentPoolForwardIterator::operator++(int)
|
||||
{
|
||||
ComponentPoolForwardIterator copyIter(*this);
|
||||
operator++();
|
||||
@@ -50,10 +50,16 @@ ComponentWrapper ComponentPool::GetByEntity(EntityID ent)
|
||||
return ComponentWrapper(m_ComponentInfo, m_EntityToComponent.at(ent));
|
||||
}
|
||||
|
||||
|
||||
bool ComponentPool::KnowsEntity(EntityID ent)
|
||||
{
|
||||
return m_EntityToComponent.find(ent) != m_EntityToComponent.end();
|
||||
}
|
||||
|
||||
void ComponentPool::Delete(ComponentWrapper& wrapper)
|
||||
{
|
||||
m_EntityToComponent.erase(wrapper.EntityID);
|
||||
m_Pool.Free(wrapper.Data);
|
||||
m_Pool.Free(wrapper.Data - sizeof(EntityID));
|
||||
}
|
||||
|
||||
ComponentPool::iterator ComponentPool::begin() const
|
||||
|
||||
@@ -24,8 +24,11 @@ ConfigFile::ConfigFile(std::string path)
|
||||
if (boost::filesystem::exists(m_Path)) {
|
||||
try {
|
||||
boost::property_tree::ini_parser::read_ini(m_Path.string(), m_PTreeOverrides);
|
||||
for (auto& node : m_PTreeOverrides) {
|
||||
m_PTreeMerged.put_child(node.first, node.second);
|
||||
for (auto& topLevelNode : m_PTreeOverrides) {
|
||||
auto& mergedTopLevelNode = m_PTreeMerged.find(topLevelNode.first);
|
||||
for (auto& childOverrideNode : topLevelNode.second) {
|
||||
mergedTopLevelNode->second.put_child(childOverrideNode.first, childOverrideNode.second);
|
||||
}
|
||||
}
|
||||
} catch (boost::property_tree::ptree_error& e) {
|
||||
LOG_ERROR("Failed to parse \"%s\":\n%s", m_Path.filename().string().c_str(), e.what());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,481 +0,0 @@
|
||||
#include "Core/EntityXMLFile.h"
|
||||
#include "Core/World.h"
|
||||
|
||||
unsigned int EntityXMLFile::InstanceCount = 0;
|
||||
|
||||
EntityXMLFile::EntityXMLFile(std::string path)
|
||||
: m_EntityFile(path)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
if (InstanceCount == 0) {
|
||||
XMLPlatformUtils::Initialize();
|
||||
}
|
||||
InstanceCount++;
|
||||
|
||||
m_GrammarPool = new XMLGrammarPoolImpl();
|
||||
m_ErrorHandler = new EntityParserXMLErrorHandler();
|
||||
m_DOMParser = new XercesDOMParser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
|
||||
m_DOMParser->setErrorHandler(m_ErrorHandler);
|
||||
m_DOMParser->setDoNamespaces(true);
|
||||
m_DOMParser->setDoXInclude(true);
|
||||
m_DOMParser->setDoSchema(true);
|
||||
m_DOMParser->setValidationSchemaFullChecking(true);
|
||||
m_DOMParser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto);
|
||||
m_DOMParser->setValidationSchemaFullChecking(true);
|
||||
m_DOMParser->setValidationConstraintFatal(false);
|
||||
m_DOMParser->setIncludeIgnorableWhitespace(false);
|
||||
// Make sure schema grammar is kept after validation
|
||||
m_DOMParser->cacheGrammarFromParse(true);
|
||||
|
||||
// HACK: Use Sax2 parser instead so the entire DOM doesn't have to reside in memory
|
||||
m_DOMParser->parse(m_EntityFile.c_str());
|
||||
m_DOMDocument = m_DOMParser->getDocument();
|
||||
|
||||
// 1. Fill in ComponentInfo name, fields, default values and metadata from PSVI
|
||||
parseComponentInfo();
|
||||
// 2. Parse default value files for those components
|
||||
parseDefaults();
|
||||
// 3. Allocate component structures
|
||||
predictComponentAllocation();
|
||||
}
|
||||
|
||||
EntityXMLFile::~EntityXMLFile()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
if (m_DOMParser != nullptr) {
|
||||
delete m_DOMParser;
|
||||
}
|
||||
if (m_ErrorHandler != nullptr) {
|
||||
delete m_ErrorHandler;
|
||||
}
|
||||
if (m_GrammarPool != nullptr) {
|
||||
delete m_GrammarPool;
|
||||
}
|
||||
|
||||
InstanceCount--;
|
||||
if (InstanceCount == 0) {
|
||||
XMLPlatformUtils::Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::PopulateWorld(World* world)
|
||||
{
|
||||
for (auto& pair : m_ComponentInfo) {
|
||||
world->RegisterComponent(pair.second);
|
||||
}
|
||||
|
||||
// 4. Parse entity hierarchy
|
||||
auto root = m_DOMDocument->getDocumentElement();
|
||||
parseEntityGraph(world, root, 0);
|
||||
}
|
||||
|
||||
|
||||
void EntityXMLFile::preprocess(std::string inPath, std::string outPath)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
static const XMLCh gLS[] = { 'L', 'S', '\0' };
|
||||
DOMImplementationLS* di = static_cast<DOMImplementationLS*>(DOMImplementationRegistry::getDOMImplementation(gLS));
|
||||
|
||||
// Parse the file
|
||||
DOMLSParser* parser = di->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, nullptr);
|
||||
DOMConfiguration* config = parser->getDomConfig();
|
||||
config->setParameter(XMLUni::fgDOMNamespaces, true);
|
||||
config->setParameter(XMLUni::fgXercesSchema, true);
|
||||
config->setParameter(XMLUni::fgXercesHandleMultipleImports, true);
|
||||
config->setParameter(XMLUni::fgXercesSchemaFullChecking, true);
|
||||
config->setParameter(XMLUni::fgXercesDoXInclude, true);
|
||||
auto errHandler = new EntityPreprocessorXMLErrorHandler();
|
||||
config->setParameter(XMLUni::fgDOMErrorHandler, errHandler);
|
||||
|
||||
auto source = new LocalFileInputSource(XSTR(inPath.c_str()));
|
||||
Wrapper4InputSource* domSourceWrapper = new Wrapper4InputSource(source);
|
||||
DOMDocument* doc = parser->parse(dynamic_cast<DOMLSInput*>(domSourceWrapper));
|
||||
|
||||
// Serialize and output the new XML
|
||||
DOMLSSerializer* writer = di->createLSSerializer();
|
||||
DOMLSOutput* output = di->createLSOutput();
|
||||
XMLFormatTarget* formatTarget = new LocalFileFormatTarget(outPath.c_str());
|
||||
// TODO: MemBufFormatTarget* formatTarget = new MemBufFormatTarget()
|
||||
output->setByteStream(formatTarget);
|
||||
writer->write(doc, output);
|
||||
|
||||
delete formatTarget;
|
||||
output->release();
|
||||
writer->release();
|
||||
parser->release();
|
||||
}
|
||||
|
||||
void EntityXMLFile::parseComponentInfo()
|
||||
{
|
||||
using namespace xercesc;
|
||||
bool wasChanged;
|
||||
XSModel* xsModel = m_GrammarPool->getXSModel(wasChanged);
|
||||
|
||||
// Find component xsd element declarations
|
||||
std::cout << "Enumerating components..." << std::endl;
|
||||
// <xs:element name="ComponentName">
|
||||
auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION);
|
||||
for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) {
|
||||
auto element = static_cast<XSElementDeclaration*>(topLevelElements->item(i));
|
||||
|
||||
std::string nameSpace(XSTR(element->getNamespace()));
|
||||
if (nameSpace != "components") {
|
||||
continue;
|
||||
}
|
||||
|
||||
ComponentInfo compInfo;
|
||||
|
||||
// Name
|
||||
compInfo.Name = XSTR(element->getName());
|
||||
// Annotation
|
||||
auto componentAnnotation = element->getAnnotation();
|
||||
if (componentAnnotation != nullptr) {
|
||||
// Parse annotation XML
|
||||
char* annotationString = XMLString::transcode(componentAnnotation->getAnnotationString());
|
||||
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager, m_GrammarPool);
|
||||
parser.setErrorHandler(m_ErrorHandler);
|
||||
parser.parse(annotationInput);
|
||||
XMLString::release(&annotationString);
|
||||
auto doc = parser.getDocument();
|
||||
|
||||
// Add allocation estimation(s)
|
||||
auto allocationTags = doc->getElementsByTagName(XSTR("meta:allocation"));
|
||||
for (int i = 0; i < allocationTags->getLength(); ++i) {
|
||||
auto allocation = dynamic_cast<DOMElement*>(allocationTags->item(i));
|
||||
auto child = allocation->getFirstChild();
|
||||
if (child == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(child->getNodeValue(), XSValue::dt_integer, status);
|
||||
compInfo.Meta.Allocation += val->fData.fValue.f_int;
|
||||
}
|
||||
|
||||
// Save documentation string
|
||||
auto documentationTags = doc->getElementsByTagName(XSTR("xs:documentation"));
|
||||
if (documentationTags->getLength() != 0) {
|
||||
auto child = documentationTags->item(0)->getFirstChild();
|
||||
if (child != nullptr) {
|
||||
compInfo.Meta.Annotation = XSTR(child->getNodeValue());
|
||||
}
|
||||
}
|
||||
// TODO: Parse annotation string XML
|
||||
// compInfo.Meta.Allocation = ...
|
||||
} else {
|
||||
std::cout << "Warning: Component is missing an annotation!" << std::endl;
|
||||
}
|
||||
|
||||
// <xs:complexType>
|
||||
auto typeDefinition = element->getTypeDefinition();
|
||||
if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) {
|
||||
std::cerr << "Error: Type definition wasn't COMPLEX_TYPE! Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
|
||||
|
||||
// <xs:all>
|
||||
auto modelGroupParticle = complexTypeDefinition->getParticle();
|
||||
if (modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
|
||||
std::cerr << "Error: Model group particle wasn't TERM_MODELGROUP! Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
auto modelGroup = modelGroupParticle->getModelGroupTerm();
|
||||
|
||||
// <xs:element...
|
||||
// <xs:attribute...
|
||||
unsigned int fieldOffset = 0;
|
||||
auto particles = modelGroup->getParticles();
|
||||
for (unsigned int i = 0; i < particles->size(); ++i) {
|
||||
auto particle = particles->elementAt(i);
|
||||
if (particle->getTermType() != XSParticle::TERM_ELEMENT) {
|
||||
std::cerr << "Error: Particle wasn't TERM_ELEMENT! Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
auto elementDeclaration = particle->getElementTerm();
|
||||
|
||||
std::string name = XSTR(elementDeclaration->getName());
|
||||
std::string type = XSTR(elementDeclaration->getTypeDefinition()->getName());
|
||||
|
||||
size_t stride = getTypeStride(type);
|
||||
if (stride == 0) {
|
||||
std::cout << "Warning: Field \"" << name << "\" in component \"" << compInfo.Name << "\" uses unexpected field type \"" << type << "\". Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
compInfo.FieldTypes[name] = type;
|
||||
compInfo.FieldOffsets[name] = fieldOffset;
|
||||
fieldOffset += getTypeStride(type);
|
||||
}
|
||||
|
||||
compInfo.Meta.Stride = fieldOffset;
|
||||
m_ComponentInfo[compInfo.Name] = compInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::parseDefaults()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
for (auto& ci : m_ComponentInfo) {
|
||||
// Allocate memory for default values
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Meta.Stride]);
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Meta.Stride);
|
||||
|
||||
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
|
||||
parser.setErrorHandler(m_ErrorHandler);
|
||||
|
||||
std::string componentName = ci.first;
|
||||
LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
|
||||
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
|
||||
|
||||
parser.parse(defaultsFile.string().c_str());
|
||||
auto doc = parser.getDocument();
|
||||
if (doc == nullptr) {
|
||||
LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the node in the components namespace matching the component name
|
||||
std::string tagName = "c:" + componentName;
|
||||
auto rootNodes = doc->getElementsByTagName(XSTR(tagName.c_str()));
|
||||
if (rootNodes->getLength() == 0) {
|
||||
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
|
||||
continue;
|
||||
}
|
||||
auto componentElement = dynamic_cast<DOMElement*>(rootNodes->item(0));
|
||||
|
||||
// Fill the default value buffer with values
|
||||
for (auto& field : ci.second.FieldOffsets) {
|
||||
std::string fieldName = field.first;
|
||||
auto fieldNodes = componentElement->getElementsByTagName(XSTR(fieldName.c_str()));
|
||||
auto fieldNode = fieldNodes->item(0);
|
||||
if (fieldNode == nullptr) {
|
||||
LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str());
|
||||
continue;
|
||||
}
|
||||
auto fieldElement = dynamic_cast<DOMElement*>(fieldNode);
|
||||
|
||||
std::string fieldType = ci.second.FieldTypes.at(fieldName);
|
||||
unsigned int fieldOffset = ci.second.FieldOffsets.at(fieldName);
|
||||
writeData(fieldElement, fieldType, ci.second.Defaults.get() + fieldOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::predictComponentAllocation()
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
auto root = m_DOMDocument->getDocumentElement();
|
||||
|
||||
// Count static instances of components present in entity hierarchy
|
||||
auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*"));
|
||||
for (int i = 0; i < components->getLength(); ++i) {
|
||||
auto component = dynamic_cast<DOMElement*>(components->item(i));
|
||||
|
||||
std::string componentName = XSTR(component->getLocalName());
|
||||
auto& compInfo = m_ComponentInfo.at(componentName);
|
||||
compInfo.Meta.Allocation += 1;
|
||||
}
|
||||
|
||||
std::cout << "COMPONENT INFO" << std::endl;
|
||||
for (auto& pair : m_ComponentInfo) {
|
||||
ComponentInfo& ci = pair.second;
|
||||
std::cout << "Component: " << ci.Name << " (" << ci.Meta.Annotation << ")" << std::endl;
|
||||
std::cout << " Allocation: " << ci.Meta.Allocation << std::endl;
|
||||
std::cout << " Fields:" << std::endl;
|
||||
|
||||
// Calculate component size
|
||||
std::size_t stride = 0;
|
||||
// Add size of fields
|
||||
for (auto& field : ci.FieldTypes) {
|
||||
std::cout << " " << field.second << " " << field.first << " (" << getTypeStride(field.second) << " byte)" << std::endl;
|
||||
stride += getTypeStride(field.second);
|
||||
}
|
||||
std::cout << " Stride: " << ci.Meta.Stride << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::parseEntityGraph(World* world, xercesc::DOMElement* element, EntityID parentEntity)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
// Create entity
|
||||
EntityID entity = world->CreateEntity(parentEntity);
|
||||
LOG_DEBUG("Created entity %i, parent %i", entity, parentEntity);
|
||||
|
||||
// Add components
|
||||
auto components = m_DOMDocument->evaluate(XSTR("Components/*"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
|
||||
for (int i = 0; i < components->getSnapshotLength(); i++) {
|
||||
components->snapshotItem(i);
|
||||
auto componentElement = dynamic_cast<DOMElement*>(components->getNodeValue());
|
||||
std::string componentName = XSTR(componentElement->getLocalName());
|
||||
auto& ci = m_ComponentInfo.at(componentName);
|
||||
|
||||
// Attach the component
|
||||
auto c = world->AttachComponent(entity, componentName);
|
||||
LOG_DEBUG("Attached %s component", componentName.c_str());
|
||||
|
||||
// Write field data
|
||||
auto fields = componentElement->getChildNodes();
|
||||
for (int j = 0; j < fields->getLength(); ++j) {
|
||||
auto fieldNode = fields->item(j);
|
||||
auto nodeType = fieldNode->getNodeType();
|
||||
if (nodeType != DOMNode::ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
auto field = dynamic_cast<DOMElement*>(fields->item(j));
|
||||
//const XMLCh* value = fields->item(j)->getTextContent();
|
||||
std::string fieldName(XSTR(field->getLocalName()));
|
||||
if (ci.FieldTypes.find(fieldName) == ci.FieldTypes.end()) {
|
||||
std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string fieldType = ci.FieldTypes.at(fieldName);
|
||||
unsigned int fieldOffset = ci.FieldOffsets.at(fieldName);
|
||||
std::string fieldValue(XSTR(field->getTextContent()));
|
||||
LOG_DEBUG(" %s %s = %s", fieldType.c_str(), fieldName.c_str(), fieldValue.c_str());
|
||||
writeData(field, fieldType, c.Data + fieldOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse children
|
||||
auto children = m_DOMDocument->evaluate(XSTR("Children/Entity"), element, nullptr, DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nullptr);
|
||||
for (int i = 0; i < children->getSnapshotLength(); i++) {
|
||||
children->snapshotItem(i);
|
||||
parseEntityGraph(world, dynamic_cast<DOMElement*>(children->getNodeValue()), entity);
|
||||
}
|
||||
|
||||
//auto components = m_DOMDocument->getElementsByTagNameNS(XSTR("components"), XSTR("*"));
|
||||
//for (int i = 0; i < components->getLength(); ++i) {
|
||||
// auto component = dynamic_cast<DOMElement*>(components->item(i));
|
||||
|
||||
// std::string componentName = XSTR(component->getLocalName());
|
||||
// auto& compStore = m_ComponentStore.at(componentName);
|
||||
// auto& compInfo = compStore.Info;
|
||||
|
||||
// char* data = &compStore.Data[compStore.Size*compStore.Stride];
|
||||
// compStore.Size += 1;
|
||||
|
||||
// auto fields = component->getChildNodes();
|
||||
// for (int j = 0; j < fields->getLength(); ++j) {
|
||||
// auto field = fields->item(j);
|
||||
// auto nodeType = field->getNodeType();
|
||||
// if (nodeType != DOMNode::ELEMENT_NODE) {
|
||||
// continue;
|
||||
// }
|
||||
// //auto field = dynamic_cast<DOMElement*>(fields->item(j));
|
||||
// //const XMLCh* value = fields->item(j)->getTextContent();
|
||||
// std::string fieldName = XSTR(field->getLocalName());
|
||||
// if (compInfo.FieldTypes.find(fieldName) == compInfo.FieldTypes.end()) {
|
||||
// std::cout << "Warning: Component \"" << componentName << "\" contains invalid field \"" << fieldName << "\". Skipping." << std::endl;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// std::string fieldType = compInfo.FieldTypes.at(fieldName);
|
||||
// unsigned int fieldOffset = compInfo.FieldOffsets.at(fieldName);
|
||||
|
||||
// XSValue::DataType dataType = XSValue::getDataType(XSTR(fieldType.c_str()));
|
||||
// if (dataType == XSValue::DataType::dt_MAXCOUNT) {
|
||||
// // TODO:
|
||||
// continue;
|
||||
// }
|
||||
// if (dataType == XSValue::DataType::dt_string) {
|
||||
// char* str = XMLString::transcode(field->getTextContent());
|
||||
// std::string standardString(str);
|
||||
// XMLString::release(&str);
|
||||
// memcpy(&data[fieldOffset], reinterpret_cast<char*>(&standardString), getTypeStride(fieldType));
|
||||
// } else {
|
||||
// XSValue::Status status;
|
||||
// XSValue* val = XSValue::getActualValue(field->getTextContent(), dataType, status);
|
||||
// memcpy(&data[fieldOffset], reinterpret_cast<char*>(&val->fData.fValue), getTypeStride(fieldType));
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//auto entities = m_DOMDocument->getElementsByTagName(XSTR("Entity"));
|
||||
//for (int i = 0; i < entities->getLength(); ++i) {
|
||||
// auto entity = dynamic_cast<DOMElement*>(entities->item(i));
|
||||
|
||||
|
||||
// //entity->setIdAttribute()
|
||||
|
||||
// std::cout << "ENTITY " << i + 1 << std::endl;
|
||||
//}
|
||||
}
|
||||
|
||||
std::size_t EntityXMLFile::getTypeStride(std::string typeName)
|
||||
{
|
||||
std::map<std::string, size_t> typeStrides{
|
||||
{ "bool", sizeof(bool) },
|
||||
{ "int", sizeof(int) },
|
||||
{ "double", sizeof(double) },
|
||||
{ "string", sizeof(std::string) },
|
||||
{ "Vector", sizeof(glm::vec3) },
|
||||
{ "Quaternion", sizeof(glm::quat) },
|
||||
{ "Color", sizeof(glm::vec4) }
|
||||
};
|
||||
|
||||
auto it = typeStrides.find(typeName);
|
||||
return (it != typeStrides.end()) ? it->second : 0;
|
||||
}
|
||||
|
||||
float EntityXMLFile::getFloatAttribute(const xercesc::DOMElement* element, const char* attribute) const
|
||||
{
|
||||
using namespace xercesc;
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(element->getAttribute(XSTR(attribute)), xercesc::XSValue::DataType::dt_float, status);
|
||||
if (val == nullptr) {
|
||||
LOG_ERROR("Element \"%s\" doesn't have an \"%s\" attribute!", XSTR(element->getTagName()), attribute);
|
||||
return 0.f;
|
||||
} else {
|
||||
return val->fData.fValue.f_float;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityXMLFile::writeData(const xercesc::DOMElement* element, std::string typeName, char* outData)
|
||||
{
|
||||
using namespace xercesc;
|
||||
|
||||
XSValue::DataType dataType = XSValue::getDataType(XSTR(typeName.c_str()));
|
||||
if (dataType == XSValue::DataType::dt_MAXCOUNT) {
|
||||
if (typeName == "Vector") {
|
||||
glm::vec3 vec;
|
||||
vec.x = getFloatAttribute(element, "X");
|
||||
vec.y = getFloatAttribute(element, "Y");
|
||||
vec.z = getFloatAttribute(element, "Z");
|
||||
memcpy(outData, reinterpret_cast<char*>(&vec), getTypeStride(typeName));
|
||||
} else if (typeName == "Color") {
|
||||
glm::vec4 vec;
|
||||
vec.r = getFloatAttribute(element, "R");
|
||||
vec.g = getFloatAttribute(element, "G");
|
||||
vec.b = getFloatAttribute(element, "B");
|
||||
vec.a = getFloatAttribute(element, "A");
|
||||
memcpy(outData, reinterpret_cast<char*>(&vec), getTypeStride(typeName));
|
||||
} else if (typeName == "Quaternion") {
|
||||
glm::quat q;
|
||||
q.x = getFloatAttribute(element, "X");
|
||||
q.y = getFloatAttribute(element, "Y");
|
||||
q.z = getFloatAttribute(element, "Z");
|
||||
q.w = getFloatAttribute(element, "W");
|
||||
memcpy(outData, reinterpret_cast<char*>(&q), getTypeStride(typeName));
|
||||
}
|
||||
} else if (dataType == XSValue::DataType::dt_string) {
|
||||
char* str = XMLString::transcode(element->getTextContent());
|
||||
std::string standardString(str);
|
||||
new (outData) std::string(str);
|
||||
XMLString::release(&str);
|
||||
//memcpy(outData, reinterpret_cast<char*>(&standardString), getTypeStride(typeName));
|
||||
} else {
|
||||
XSValue::Status status;
|
||||
XSValue* val = XSValue::getActualValue(element->getTextContent(), dataType, status);
|
||||
memcpy(outData, reinterpret_cast<char*>(&val->fData.fValue), getTypeStride(typeName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
#include "Core/InputManager.h"
|
||||
|
||||
std::vector<unsigned int> InputManager::GLFWCharCallbackQueue;
|
||||
std::vector<std::pair<double, double>> InputManager::GLFWScrollCallbackQueue;
|
||||
std::vector<std::string> InputManager::GLFWDropCallbackQueue;
|
||||
|
||||
void InputManager::Initialize()
|
||||
{
|
||||
// TODO: Gamepad
|
||||
//m_LastGamepadAxisState = std::array<GamepadAxisState, XUSER_MAX_COUNT>();
|
||||
//m_LastGamepadButtonState = std::array<GamepadButtonState, XUSER_MAX_COUNT>();
|
||||
glfwSetCharCallback(m_GLFWWindow, &InputManager::GLFWCharCallback);
|
||||
glfwSetScrollCallback(m_GLFWWindow, &InputManager::GLFWScrollCallback);
|
||||
glfwSetDropCallback(m_GLFWWindow, &InputManager::GLFWDropCallback);
|
||||
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ELockMouse, &InputManager::OnLockMouse);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EUnlockMouse, &InputManager::OnUnlockMouse);
|
||||
@@ -36,6 +43,15 @@ void InputManager::Update(double dt)
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard text input
|
||||
for (unsigned int& c : GLFWCharCallbackQueue) {
|
||||
Events::KeyboardChar e;
|
||||
e.Timestamp = glfwGetTime();
|
||||
e.Char = c;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
GLFWCharCallbackQueue.clear();
|
||||
|
||||
// Mouse buttons
|
||||
for (int i = 0; i <= GLFW_MOUSE_BUTTON_LAST; ++i) {
|
||||
m_CurrentMouseState[i] = glfwGetMouseButton(m_GLFWWindow, i);
|
||||
@@ -73,6 +89,22 @@ void InputManager::Update(double dt)
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
// Mouse scroll
|
||||
for (auto& pair : GLFWScrollCallbackQueue) {
|
||||
Events::MouseScroll e;
|
||||
std::tie(e.DeltaX, e.DeltaY) = pair;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
GLFWScrollCallbackQueue.clear();
|
||||
|
||||
// File drop
|
||||
for (auto& path : GLFWDropCallbackQueue) {
|
||||
Events::FileDropped e;
|
||||
e.Path = path;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
GLFWDropCallbackQueue.clear();
|
||||
|
||||
// // Lock mouse while holding LMB
|
||||
// if (m_CurrentMouseState[GLFW_MOUSE_BUTTON_LEFT])
|
||||
// {
|
||||
@@ -196,6 +228,25 @@ void InputManager::PublishGamepadButtonIfChanged(int gamepadID, Gamepad::Button
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::GLFWCharCallback(GLFWwindow* window, unsigned int c)
|
||||
{
|
||||
GLFWCharCallbackQueue.push_back(c);
|
||||
}
|
||||
|
||||
|
||||
void InputManager::GLFWScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
GLFWScrollCallbackQueue.push_back(std::make_pair(xoffset, yoffset));
|
||||
}
|
||||
|
||||
|
||||
void InputManager::GLFWDropCallback(GLFWwindow* window, int count, const char* paths[])
|
||||
{
|
||||
for (int i = 0; i < count; i++) {
|
||||
GLFWDropCallbackQueue.push_back(std::string(paths[i]));
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::OnLockMouse(const Events::LockMouse &event)
|
||||
{
|
||||
m_MouseLocked = true;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -100,7 +100,7 @@ Resource* ResourceManager::Load(std::string resourceType, std::string resourceNa
|
||||
LOG_WARNING("Hot-loading resource \"%s\"", resourceName.c_str());
|
||||
}
|
||||
|
||||
return CreateResource(resourceType, resourceName, parent);
|
||||
return CreateResource(resourceType, resourceName, parent);
|
||||
}
|
||||
|
||||
Resource* ResourceManager::CreateResource(std::string resourceType, std::string resourceName, Resource* parent)
|
||||
@@ -112,10 +112,16 @@ Resource* ResourceManager::CreateResource(std::string resourceType, std::string
|
||||
}
|
||||
|
||||
// Call the factory function
|
||||
Resource* resource = facIt->second(resourceName);
|
||||
// Store IDs
|
||||
resource->TypeID = GetTypeID(resourceType);
|
||||
resource->ResourceID = GetNewResourceID(resource->TypeID);
|
||||
Resource* resource;
|
||||
try {
|
||||
resource = facIt->second(resourceName);
|
||||
// Store IDs
|
||||
resource->TypeID = GetTypeID(resourceType);
|
||||
resource->ResourceID = GetNewResourceID(resource->TypeID);
|
||||
} catch (const std::exception& e) {
|
||||
resource = nullptr;
|
||||
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
|
||||
}
|
||||
// Cache
|
||||
m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource;
|
||||
m_ResourceFromName[resourceName] = resource;
|
||||
|
||||
@@ -10,20 +10,65 @@ World::~World()
|
||||
EntityID World::CreateEntity(EntityID parent /*= 0*/)
|
||||
{
|
||||
EntityID newEntity = generateEntityID();
|
||||
m_EntityParents[newEntity] = parent;
|
||||
if (parent != 0) {
|
||||
m_EntityChildren.insert(std::make_pair(parent, newEntity));
|
||||
if (newEntity == parent) {
|
||||
LOG_WARNING("Invalid parent #%i of Entity#%i", newEntity, parent);
|
||||
parent = EntityID_Invalid;
|
||||
}
|
||||
m_EntityParents[newEntity] = parent;
|
||||
m_EntityChildren.insert(std::make_pair(parent, newEntity));
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
void World::DeleteEntity(EntityID entity)
|
||||
{
|
||||
// Delete components
|
||||
for (auto& pair : m_ComponentPools) {
|
||||
auto& pool = pair.second;
|
||||
if (pool->KnowsEntity(entity)) {
|
||||
auto& c = pool->GetByEntity(entity);
|
||||
pool->Delete(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through children
|
||||
std::vector<EntityID> childrenToDelete;
|
||||
auto children = m_EntityChildren.equal_range(entity);
|
||||
for (auto it = children.first; it != children.second; ++it) {
|
||||
childrenToDelete.push_back(it->second);
|
||||
}
|
||||
for (auto& child : childrenToDelete) {
|
||||
DeleteEntity(child);
|
||||
}
|
||||
|
||||
EntityID parent = m_EntityParents.at(entity);
|
||||
m_EntityParents.erase(entity);
|
||||
auto parentChildren = m_EntityChildren.equal_range(parent);
|
||||
for (auto it = parentChildren.first; it != parentChildren.second; ++it) {
|
||||
if (it->second == entity) {
|
||||
m_EntityChildren.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase potential name
|
||||
m_EntityNames.erase(entity);
|
||||
}
|
||||
|
||||
bool World::ValidEntity(EntityID entity) const
|
||||
{
|
||||
return m_EntityParents.find(entity) != m_EntityParents.end();
|
||||
}
|
||||
|
||||
void World::RegisterComponent(ComponentInfo& ci)
|
||||
{
|
||||
m_ComponentPools[ci.Name] = new ComponentPool(ci);
|
||||
if (m_ComponentPools.find(ci.Name) == m_ComponentPools.end()) {
|
||||
m_ComponentPools[ci.Name] = new ComponentPool(ci);
|
||||
}
|
||||
}
|
||||
|
||||
ComponentWrapper World::AttachComponent(EntityID entity, std::string componentType)
|
||||
{
|
||||
// TODO: Allocate dynamic pool if component isn't registered
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
const ComponentInfo& ci = pool->ComponentInfo();
|
||||
|
||||
@@ -35,24 +80,70 @@ ComponentWrapper World::AttachComponent(EntityID entity, std::string componentTy
|
||||
return c;
|
||||
}
|
||||
|
||||
bool World::HasComponent(EntityID entity, std::string componentType) const
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
return pool->KnowsEntity(entity);
|
||||
}
|
||||
|
||||
ComponentWrapper World::GetComponent(EntityID entity, std::string componentType)
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
return pool->GetByEntity(entity);
|
||||
}
|
||||
|
||||
void World::DeleteComponent(EntityID entity, std::string componentType)
|
||||
{
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
ComponentWrapper c = pool->GetByEntity(entity);
|
||||
return pool->Delete(c);
|
||||
}
|
||||
|
||||
const ComponentPool* World::GetComponents(std::string componentType)
|
||||
{
|
||||
auto it = m_ComponentPools.find(componentType);
|
||||
return (it != m_ComponentPools.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
|
||||
EntityID World::GetParent(EntityID entity)
|
||||
{
|
||||
return m_EntityParents.at(entity);
|
||||
}
|
||||
|
||||
void World::SetParent(EntityID entity, EntityID parent)
|
||||
{
|
||||
EntityID lastParent = m_EntityParents.at(entity);
|
||||
auto parentChildren = m_EntityChildren.equal_range(lastParent);
|
||||
for (auto it = parentChildren.first; it != parentChildren.second; it++) {
|
||||
if (it->second == entity) {
|
||||
m_EntityChildren.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_EntityParents[entity] = parent;
|
||||
m_EntityChildren.insert(std::make_pair(parent, entity));
|
||||
}
|
||||
|
||||
void World::SetName(EntityID entity, const std::string& name)
|
||||
{
|
||||
m_EntityNames[entity] = name;
|
||||
}
|
||||
|
||||
std::string World::GetName(EntityID entity) const
|
||||
{
|
||||
if (entity == EntityID_Invalid) {
|
||||
return "EntityID_Invalid";
|
||||
}
|
||||
|
||||
auto it = m_EntityNames.find(entity);
|
||||
if (it != m_EntityNames.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
EntityID World::generateEntityID()
|
||||
{
|
||||
// TODO: Make EntityID generation smarter
|
||||
|
||||
@@ -0,0 +1,713 @@
|
||||
#include "Editor/EditorSystem.h"
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
EditorSystem::EditorSystem(EventBroker* eventBroker, IRenderer* renderer)
|
||||
: ImpureSystem(eventBroker)
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_Enabled = config->Get<bool>("Debug.EditorEnabled", false);
|
||||
m_Visible = m_Enabled;
|
||||
m_DefaultEntityDir = boost::filesystem::path("Schema") / boost::filesystem::path("Entities");
|
||||
|
||||
if (!m_Enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &EditorSystem::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &EditorSystem::OnMousePress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &EditorSystem::OnMouseRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &EditorSystem::OnMouseMove);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EFileDropped, &EditorSystem::OnFileDropped);
|
||||
}
|
||||
|
||||
void EditorSystem::Update(World* world, double dt)
|
||||
{
|
||||
m_World = world;
|
||||
|
||||
if (!m_Enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_Visible) {
|
||||
return;
|
||||
}
|
||||
Picking();
|
||||
updateWidget();
|
||||
|
||||
drawUI(world, dt);
|
||||
|
||||
// Clear drop queue if it wasn't handled by any UI element
|
||||
if (!m_LastDroppedFile.empty()) {
|
||||
m_LastDroppedFile = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::filesystem::path EditorSystem::openDialog(boost::filesystem::path defaultPath)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
auto absolutePath = bfs::absolute(defaultPath);
|
||||
nfdchar_t* outPath = nullptr;
|
||||
nfdresult_t result = NFD_OpenDialog(NULL, absolutePath.string().c_str(), &outPath);
|
||||
if (result == NFD_ERROR) {
|
||||
LOG_ERROR("NFD Error: %s", NFD_GetError());
|
||||
return bfs::path();
|
||||
}
|
||||
|
||||
return bfs::absolute(outPath);
|
||||
}
|
||||
|
||||
boost::filesystem::path EditorSystem::saveDialog(boost::filesystem::path defaultPath)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
auto absolutePath = bfs::absolute(defaultPath);
|
||||
nfdchar_t* outPath = nullptr;
|
||||
nfdresult_t result = NFD_SaveDialog(NULL, absolutePath.string().c_str(), &outPath);
|
||||
if (result == NFD_ERROR) {
|
||||
LOG_ERROR("NFD Error: %s", NFD_GetError());
|
||||
return bfs::path();
|
||||
}
|
||||
|
||||
return bfs::absolute(outPath);
|
||||
}
|
||||
|
||||
bool EditorSystem::OnInputCommand(const Events::InputCommand& e)
|
||||
{
|
||||
if (e.Command == "ToggleEditor" && e.Value > 0) {
|
||||
m_Visible = !m_Visible;
|
||||
}
|
||||
|
||||
if (e.Command == "EditorToolMove" && e.Value > 0) {
|
||||
setWidgetMode(WidgetMode::Translate);
|
||||
}
|
||||
if (e.Command == "EditorToolRotate" && e.Value > 0) {
|
||||
setWidgetMode(WidgetMode::Rotate);
|
||||
}
|
||||
if (e.Command == "EditorToolScale" && e.Value > 0) {
|
||||
setWidgetMode(WidgetMode::Scale);
|
||||
}
|
||||
|
||||
if (e.Command == "EditorToggleTransformSpace" && e.Value > 0) {
|
||||
if (m_WidgetSpace == WidgetSpace::Global) {
|
||||
setWidgetSpace(WidgetSpace::Local);
|
||||
} else if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
setWidgetSpace(WidgetSpace::Global);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorSystem::OnMousePress(const Events::MousePress& e)
|
||||
{
|
||||
if (e.Button == GLFW_MOUSE_BUTTON_RIGHT) {
|
||||
m_PickingQueue.push_back(glm::vec2((int)e.X, (int)e.Y));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorSystem::OnMouseMove(const Events::MouseMove& e)
|
||||
{
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (m_Selection == EntityID_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (m_Selection == m_Widget) {
|
||||
return false;
|
||||
}
|
||||
// TODO: No widgets for root entity until widgets reside in thier own world,
|
||||
// or the widgets will move relative to the root entity being moved, which is WEEEIRD.
|
||||
if (m_Selection == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
glm::vec3 widgetOrientation = widgetTransform["Orientation"];
|
||||
glm::quat totalOrientation = m_Camera->Orientation() * glm::inverse(glm::quat(widgetOrientation));
|
||||
|
||||
int width;
|
||||
int height;
|
||||
glfwGetFramebufferSize(m_Renderer->Window(), &width, &height);
|
||||
Rectangle res(width, height);
|
||||
|
||||
glm::vec2 delta2(res.Width / 2.f + e.DeltaX, res.Height / 2.f + -e.DeltaY);
|
||||
glm::vec3 deltaWorld = ScreenCoords::ToWorldPos(
|
||||
delta2,
|
||||
m_WidgetPickingDepth,
|
||||
res,
|
||||
m_Camera->ProjectionMatrix(),
|
||||
glm::toMat4(glm::inverse(totalOrientation))
|
||||
);
|
||||
glm::vec3 origin = ScreenCoords::ToWorldPos(
|
||||
glm::vec2(res.Width / 2.f, res.Height / 2.f),
|
||||
m_WidgetPickingDepth,
|
||||
res,
|
||||
m_Camera->ProjectionMatrix(),
|
||||
glm::toMat4(glm::inverse(totalOrientation))
|
||||
);
|
||||
deltaWorld = deltaWorld - origin;
|
||||
glm::vec3 movement = deltaWorld * m_WidgetCurrentAxis;
|
||||
|
||||
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
|
||||
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
if (m_WidgetMode == WidgetMode::Translate) {
|
||||
if (m_WidgetSpace == WidgetSpace::Global) {
|
||||
EntityID parent = m_World->GetParent(m_Selection);
|
||||
glm::quat inverseParentOrientation;
|
||||
//if (parent != 0) {
|
||||
inverseParentOrientation = glm::inverse(Transform::AbsoluteOrientation(m_World, parent));
|
||||
//}
|
||||
(glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Position"] += inverseParentOrientation * movement;
|
||||
} else if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
(glm::vec3&)selectionTransform["Position"] += glm::quat((glm::vec3)selectionTransform["Orientation"]) * movement;
|
||||
}
|
||||
} else if (m_WidgetMode == WidgetMode::Rotate) {
|
||||
glm::vec3 finalMovement;
|
||||
finalMovement.x = -deltaWorld.y * m_WidgetCurrentAxis.x;
|
||||
finalMovement.y = deltaWorld.x * m_WidgetCurrentAxis.y;
|
||||
finalMovement.z = deltaWorld.y * m_WidgetCurrentAxis.z;
|
||||
if (m_WidgetSpace == WidgetSpace::Global) {
|
||||
EntityID parent = m_World->GetParent(m_Selection);
|
||||
glm::quat parentOrientation;
|
||||
//if (parent != 0) {
|
||||
// parentOrientation = RenderSystem::AbsoluteOrientation(m_World, parent);
|
||||
//}
|
||||
glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"];
|
||||
glm::quat currentOrientation = Transform::AbsoluteOrientation(m_World, m_Selection);
|
||||
//glm::quat currentOrientation = parentOrientation * glm::quat(selectionOrientation);
|
||||
glm::quat deltaOrientation(finalMovement);
|
||||
selectionOrientation = glm::eulerAngles(glm::inverse(parentOrientation) * (deltaOrientation * currentOrientation));
|
||||
} else if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
glm::vec3& selectionOrientation = m_World->GetComponent(m_Selection, "Transform")["Orientation"];
|
||||
glm::quat currentOrientation(selectionOrientation);
|
||||
glm::quat deltaOrientation(finalMovement);
|
||||
selectionOrientation = glm::eulerAngles(currentOrientation * deltaOrientation);
|
||||
}
|
||||
} else if (m_WidgetMode == WidgetMode::Scale) {
|
||||
glm::vec3& scaleX = m_World->GetComponent(m_WidgetX, "Transform")["Scale"];
|
||||
glm::vec3& scaleY = m_World->GetComponent(m_WidgetY, "Transform")["Scale"];
|
||||
glm::vec3& scaleZ = m_World->GetComponent(m_WidgetZ, "Transform")["Scale"];
|
||||
|
||||
if (m_WidgetCurrentAxis.x > 0 && m_WidgetCurrentAxis.y > 0 && m_WidgetCurrentAxis.z > 0) {
|
||||
float movementLength = glm::length(movement);
|
||||
float dot = glm::dot((glm::vec3)widgetOrientation, movement);
|
||||
movement = glm::vec3(movementLength) * glm::sign(dot);
|
||||
(glm::vec3&)m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] += movement;
|
||||
}
|
||||
if (m_WidgetCurrentAxis.x > 0) {
|
||||
scaleX.x += movement.x;
|
||||
}
|
||||
if (m_WidgetCurrentAxis.y > 0) {
|
||||
scaleY.y += movement.y;
|
||||
}
|
||||
if (m_WidgetCurrentAxis.z > 0) {
|
||||
scaleZ.z += movement.z;
|
||||
}
|
||||
(glm::vec3&)m_World->GetComponent(m_Selection, "Transform")["Scale"] += movement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*LOG_DEBUG("DELTA %f", e.DeltaX);
|
||||
if (e.X < 0) {
|
||||
glfwSetCursorPos(m_Renderer->Window(), width - 1, e.Y);
|
||||
}
|
||||
if (e.X >= width) {
|
||||
glfwSetCursorPos(m_Renderer->Window(), 0, e.Y);
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorSystem::OnMouseRelease(const Events::MouseRelease& e)
|
||||
{
|
||||
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
|
||||
m_WidgetCurrentAxis = glm::vec3(0.f);
|
||||
//setWidgetMode(m_WidgetMode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorSystem::Picking()
|
||||
{
|
||||
for (auto& pos : m_PickingQueue) {
|
||||
auto result = m_Renderer->Pick(pos);
|
||||
EntityID entity = result.Entity;
|
||||
if (glm::length2(m_WidgetCurrentAxis) > 0.f) {
|
||||
// ???
|
||||
} else {
|
||||
LOG_INFO("Selected %i", entity);
|
||||
if (entity != EntityID_Invalid) {
|
||||
EntityID parent = m_World->GetParent(entity);
|
||||
m_Camera = result.Camera;
|
||||
if (parent == m_Widget) {
|
||||
m_WidgetCurrentAxis = glm::vec3(
|
||||
(entity == m_WidgetX) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneY || entity == m_WidgetPlaneZ),
|
||||
(entity == m_WidgetY) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneZ),
|
||||
(entity == m_WidgetZ) || (entity == m_WidgetOrigin) || (entity == m_WidgetPlaneX || entity == m_WidgetPlaneY)
|
||||
);
|
||||
m_WidgetPickingDepth = result.Depth;
|
||||
//auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
//auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
//widgetTransform["Position"] = (glm::vec3)selectionTransform["Position"];
|
||||
} else {
|
||||
ImGui::SetActiveID(0, nullptr);
|
||||
if (m_WidgetMode == WidgetMode::None) {
|
||||
m_WidgetMode = WidgetMode::Translate;
|
||||
}
|
||||
setWidgetMode(m_WidgetMode);
|
||||
m_Selection = entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_PickingQueue.clear();
|
||||
};
|
||||
|
||||
bool EditorSystem::OnFileDropped(const Events::FileDropped& e)
|
||||
{
|
||||
m_LastDroppedFile = boost::filesystem::path(e.Path).lexically_relative(boost::filesystem::current_path()).string();
|
||||
std::replace(m_LastDroppedFile.begin(), m_LastDroppedFile.end(), '\\', '/');
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorSystem::createWidget()
|
||||
{
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
m_Widget = m_World->CreateEntity();
|
||||
m_World->AttachComponent(m_Widget, "Transform");
|
||||
m_WidgetX = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetX, "Transform");
|
||||
m_World->AttachComponent(m_WidgetX, "Model");
|
||||
m_WidgetPlaneX = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetPlaneX, "Transform");
|
||||
m_World->AttachComponent(m_WidgetPlaneX, "Model");
|
||||
m_World->GetComponent(m_WidgetPlaneX, "Model")["Resource"] = "Models/WidgetPlaneX.obj";
|
||||
m_WidgetY = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetY, "Transform");
|
||||
m_World->AttachComponent(m_WidgetY, "Model");
|
||||
m_WidgetPlaneY = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetPlaneY, "Transform");
|
||||
m_World->AttachComponent(m_WidgetPlaneY, "Model");
|
||||
m_World->GetComponent(m_WidgetPlaneY, "Model")["Resource"] = "Models/WidgetPlaneY.obj";
|
||||
m_WidgetZ = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetZ, "Transform");
|
||||
m_World->AttachComponent(m_WidgetZ, "Model");
|
||||
m_WidgetPlaneZ = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetPlaneZ, "Transform");
|
||||
m_World->AttachComponent(m_WidgetPlaneZ, "Model");
|
||||
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Resource"] = "Models/WidgetPlaneZ.obj";
|
||||
m_WidgetOrigin = m_World->CreateEntity(m_Widget);
|
||||
m_World->AttachComponent(m_WidgetOrigin, "Transform");
|
||||
m_World->AttachComponent(m_WidgetOrigin, "Model");
|
||||
setWidgetMode(WidgetMode::None);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSystem::updateWidget()
|
||||
{
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
return;
|
||||
}
|
||||
if (m_Selection == m_Widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
glm::vec3 selectionPosition = Transform::AbsolutePosition(m_World, m_Selection);
|
||||
widgetTransform["Position"] = selectionPosition;
|
||||
if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSystem::setWidgetMode(WidgetMode newMode)
|
||||
{
|
||||
if (m_Widget == EntityID_Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto widgetTransform = m_World->GetComponent(m_Widget, "Transform");
|
||||
widgetTransform["Orientation"] = glm::vec3(0.f);
|
||||
m_World->GetComponent(m_WidgetX, "Transform")["Scale"] = glm::vec3(1.f);
|
||||
m_World->GetComponent(m_WidgetPlaneX, "Model")["Visible"] = false;
|
||||
m_World->GetComponent(m_WidgetY, "Transform")["Scale"] = glm::vec3(1.f);
|
||||
m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = false;
|
||||
m_World->GetComponent(m_WidgetZ, "Transform")["Scale"] = glm::vec3(1.f);
|
||||
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = false;
|
||||
m_World->GetComponent(m_WidgetOrigin, "Transform")["Scale"] = glm::vec3(1.f);
|
||||
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = false;
|
||||
|
||||
if (newMode == WidgetMode::Translate) {
|
||||
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/TranslationWidgetX.obj";
|
||||
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/TranslationWidgetY.obj";
|
||||
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/TranslationWidgetZ.obj";
|
||||
// Temporarily disabled for local space until I can figure out what's wrong with the math
|
||||
if (m_WidgetSpace != WidgetSpace::Local) {
|
||||
m_World->GetComponent(m_WidgetPlaneX, "Model")["Visible"] = true;
|
||||
m_World->GetComponent(m_WidgetPlaneY, "Model")["Visible"] = true;
|
||||
m_World->GetComponent(m_WidgetPlaneZ, "Model")["Visible"] = true;
|
||||
}
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
|
||||
}
|
||||
}
|
||||
} else if (newMode == WidgetMode::Scale) {
|
||||
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/ScaleWidgetX.obj";
|
||||
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/ScaleWidgetY.obj";
|
||||
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/ScaleWidgetZ.obj";
|
||||
m_World->GetComponent(m_WidgetOrigin, "Model")["Visible"] = true;
|
||||
m_World->GetComponent(m_WidgetOrigin, "Model")["Resource"] = "Models/ScaleWidgetOrigin.obj";
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
|
||||
}
|
||||
} else if (newMode == WidgetMode::Rotate) {
|
||||
m_World->GetComponent(m_WidgetX, "Model")["Resource"] = "Models/RotationWidgetX.obj";
|
||||
m_World->GetComponent(m_WidgetY, "Model")["Resource"] = "Models/RotationWidgetY.obj";
|
||||
m_World->GetComponent(m_WidgetZ, "Model")["Resource"] = "Models/RotationWidgetZ.obj";
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto selectionTransform = m_World->GetComponent(m_Selection, "Transform");
|
||||
if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
widgetTransform["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(m_World, m_Selection));
|
||||
}
|
||||
}
|
||||
}
|
||||
m_WidgetMode = newMode;
|
||||
}
|
||||
|
||||
void EditorSystem::setWidgetSpace(WidgetSpace space)
|
||||
{
|
||||
m_WidgetSpace = space;
|
||||
setWidgetMode(m_WidgetMode);
|
||||
}
|
||||
|
||||
void EditorSystem::drawUI(World* world, double dt)
|
||||
{
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
ImGui::ShowTestWindow();
|
||||
//ImGui::ShowStyleEditor();
|
||||
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
//if (ImGui::MenuItem("New")) { }
|
||||
if (ImGui::MenuItem("Import", "Ctrl+O")) {
|
||||
fileImport(world);
|
||||
}
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) {
|
||||
fileSave(world);
|
||||
}
|
||||
if (ImGui::MenuItem("Save As...", "Ctrl+Shift+S")) {
|
||||
fileSaveAs(world);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Close Editor", "F1")) { }
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Move")) {
|
||||
setWidgetMode(WidgetMode::Translate);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Rotate")) {
|
||||
setWidgetMode(WidgetMode::Rotate);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Scale")) {
|
||||
setWidgetMode(WidgetMode::Scale);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (m_WidgetSpace == WidgetSpace::Global) {
|
||||
if (ImGui::Button("(Global)")) {
|
||||
setWidgetSpace(WidgetSpace::Local);
|
||||
}
|
||||
} else if (m_WidgetSpace == WidgetSpace::Local) {
|
||||
if (ImGui::Button("(Local)")) {
|
||||
setWidgetSpace(WidgetSpace::Global);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
std::string title = std::string("Components #") + std::to_string(m_Selection) + std::string("###Components");
|
||||
if (ImGui::Begin(title.c_str())) {
|
||||
if (m_Selection != EntityID_Invalid) {
|
||||
auto& pools = world->GetComponentPools();
|
||||
|
||||
std::vector<const char*> componentTypes;
|
||||
for (auto& pair : pools) {
|
||||
// Only add components the entity doesn't already have
|
||||
if (!pair.second->KnowsEntity(m_Selection)) {
|
||||
componentTypes.push_back(pair.first.c_str());
|
||||
}
|
||||
}
|
||||
int item = -1;
|
||||
ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() - 5.f);
|
||||
if (ImGui::Combo("", &item, componentTypes.data(), componentTypes.size())) {
|
||||
if (item != -1) {
|
||||
std::string chosenType = std::string(componentTypes.at(item));
|
||||
world->AttachComponent(m_Selection, chosenType);
|
||||
}
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
for (auto& pair : pools) {
|
||||
const std::string& componentType = pair.first;
|
||||
auto pool = pair.second;
|
||||
if (!pool->KnowsEntity(m_Selection)) {
|
||||
continue;
|
||||
}
|
||||
auto& ci = pool->ComponentInfo();
|
||||
|
||||
bool deletePressed = createDeleteButton(componentType);
|
||||
if (deletePressed) {
|
||||
world->DeleteComponent(m_Selection, componentType);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader(componentType.c_str())) {
|
||||
if (!ci.Meta.Annotation.empty()) {
|
||||
ImGui::Text(ci.Meta.Annotation.c_str());
|
||||
}
|
||||
|
||||
auto& component = world->GetComponent(m_Selection, componentType);
|
||||
for (auto& kv : ci.Fields) {
|
||||
const std::string& fieldName = kv.first;
|
||||
auto& field = kv.second;
|
||||
|
||||
ImGui::PushID(fieldName.c_str());
|
||||
if (field.Type == "Vector") {
|
||||
auto& val = component.Property<glm::vec3>(fieldName);
|
||||
if (fieldName == "Scale") {
|
||||
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, 0.f, std::numeric_limits<float>::max());
|
||||
} else if (fieldName == "Orientation") {
|
||||
glm::vec3 tempVal = glm::fmod(val, glm::vec3(glm::two_pi<float>()));
|
||||
if (ImGui::SliderFloat3("", glm::value_ptr(tempVal), 0.f, glm::two_pi<float>())) {
|
||||
val = tempVal;
|
||||
}
|
||||
} else {
|
||||
ImGui::DragFloat3("", glm::value_ptr(val), 0.1f, std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
|
||||
}
|
||||
} else if (field.Type == "Color") {
|
||||
auto& val = component.Property<glm::vec4>(fieldName);
|
||||
ImGui::ColorEdit4("", glm::value_ptr(val), true);
|
||||
} else if (field.Type == "string") {
|
||||
std::string& val = component.Property<std::string>(fieldName);
|
||||
char tempString[1024];
|
||||
memcpy(tempString, val.c_str(), std::min(val.length() + 1, sizeof(tempString)));
|
||||
if (ImGui::InputText("", tempString, sizeof(tempString))) {
|
||||
val = std::string(tempString);
|
||||
LOG_DEBUG("%s::%s changed!", componentType.c_str(), fieldName.c_str());
|
||||
}
|
||||
// DROP STUFF
|
||||
if (ImGui::IsItemHovered() && !m_LastDroppedFile.empty()) {
|
||||
val = m_LastDroppedFile;
|
||||
m_LastDroppedFile = "";
|
||||
}
|
||||
|
||||
} else if (field.Type == "double") {
|
||||
float tempVal = static_cast<float>(component.Property<double>(fieldName));
|
||||
if (ImGui::InputFloat("", &tempVal, 0.01f, 1.f)) {
|
||||
component.SetProperty(fieldName, static_cast<double>(tempVal));
|
||||
}
|
||||
} else if (field.Type == "bool") {
|
||||
auto& val = component.Property<bool>(fieldName);
|
||||
ImGui::Checkbox("", &val);
|
||||
} else {
|
||||
ImGui::TextDisabled(field.Type.c_str());
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(fieldName.c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("field annotation goes here");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (ImGui::Begin("Entities")) {
|
||||
auto entityChildren = world->GetEntityChildren();
|
||||
std::function<void(EntityID)> recurse = [&](EntityID parent) {
|
||||
auto range = entityChildren.equal_range(parent);
|
||||
for (auto it = range.first; it != range.second; it++) {
|
||||
if (createEntityNode(world, it->second)) {
|
||||
recurse(it->second);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
};
|
||||
recurse(EntityID_Invalid);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool EditorSystem::createEntityNode(World* world, EntityID entity)
|
||||
{
|
||||
// HACK: Don't show the widget entities in the entity tree
|
||||
if (entity == m_Widget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float width = ImGui::GetContentRegionAvailWidth();
|
||||
ImRect bb(pos + ImVec2(20, 0), pos + ImVec2(width, 13));
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
if (m_Selection == entity) {
|
||||
const ImU32 col = window->Color(ImGuiCol_HeaderActive);
|
||||
window->DrawList->AddRectFilled(bb.Min, bb.Max, col);
|
||||
}
|
||||
ImGuiID id = window->GetID((std::string("#SelectButton") + std::to_string(entity)).c_str());
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
if (ImGui::ButtonBehavior(bb, id, &hovered, &held)) {
|
||||
m_Selection = entity;
|
||||
}
|
||||
if (held) {
|
||||
ImVec2 entityDragDelta = ImGui::GetMouseDragDelta(0);
|
||||
if (std::abs(entityDragDelta.x) > 0 && std::abs(entityDragDelta.y) > 0) {
|
||||
if (m_UIDraggingEntity == EntityID_Invalid) {
|
||||
m_UIDraggingEntity = entity;
|
||||
LOG_DEBUG("Started drag of entity %i", m_UIDraggingEntity);
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImGui::GetIO().MousePos + ImVec2(20, 0));
|
||||
ImGui::Begin("Change parent", nullptr, ImVec2(0, 0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::Text("#%i", m_UIDraggingEntity);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetNextTreeNodeOpened(true, ImGuiSetCond_Once);
|
||||
std::string nodeTitle;
|
||||
const std::string& entityName = world->GetName(entity);
|
||||
if (!entityName.empty()) {
|
||||
nodeTitle = entityName;
|
||||
} else {
|
||||
nodeTitle = std::string("#") + std::to_string(entity);
|
||||
}
|
||||
if (ImGui::TreeNode(nodeTitle.c_str())) {
|
||||
if (m_UIDraggingEntity != EntityID_Invalid && ImGui::IsItemHoveredRect() && ImGui::IsMouseReleased(0)) {
|
||||
LOG_DEBUG("Changed parent of %i to %i", m_UIDraggingEntity, entity);
|
||||
changeParent(m_UIDraggingEntity, entity);
|
||||
m_UIDraggingEntity = EntityID_Invalid;
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem("item context menu")) {
|
||||
if (ImGui::Button("Add")) {
|
||||
EntityID newEntity = world->CreateEntity(entity);
|
||||
world->AttachComponent(newEntity, "Transform");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Delete")) {
|
||||
world->DeleteEntity(entity);
|
||||
ImGui::CloseCurrentPopup();
|
||||
if (!world->ValidEntity(m_Selection)) {
|
||||
m_Selection = EntityID_Invalid;
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorSystem::createDeleteButton(std::string componentType)
|
||||
{
|
||||
float width = ImGui::GetContentRegionAvailWidth();
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
auto pos = ImGui::GetCursorScreenPos() + ImVec2(width - 14.f, 1);
|
||||
ImRect bb = ImRect(pos, pos + ImVec2(14.f, 14.f));
|
||||
std::string idString = "#DELETE";
|
||||
idString += componentType;
|
||||
ImGuiID id = window->GetID(idString.c_str());
|
||||
bool hovered;
|
||||
bool held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held);
|
||||
//ImU32 col = window->Color((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton);
|
||||
ImU32 col = window->Color((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
|
||||
window->DrawList->AddCircleFilled(bb.GetCenter(), 7.f, col, 16);
|
||||
return pressed;
|
||||
}
|
||||
|
||||
void EditorSystem::changeParent(EntityID entity, EntityID newParent)
|
||||
{
|
||||
if (entity == newParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// An entity can't be a child to one of its own children
|
||||
auto children = m_World->GetEntityChildren().equal_range(entity);
|
||||
for (auto it = children.first; it != children.second; it++) {
|
||||
if (it->second == newParent) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_World->SetParent(entity, newParent);
|
||||
}
|
||||
|
||||
void EditorSystem::fileImport(World* world)
|
||||
{
|
||||
m_CurrentFile = openDialog(m_DefaultEntityDir);
|
||||
auto file = ResourceManager::Load<EntityFile>(m_CurrentFile.string());
|
||||
EntityFilePreprocessor fpp(file);
|
||||
fpp.RegisterComponents(world);
|
||||
EntityFileParser fp(file);
|
||||
fp.MergeEntities(world);
|
||||
createWidget();
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
void EditorSystem::fileSave(World* world)
|
||||
{
|
||||
if (boost::filesystem::exists(m_CurrentFile)) {
|
||||
// HACK: Delete the widgets so they don't appear in the saved file
|
||||
world->DeleteEntity(m_Widget);
|
||||
m_Widget = EntityID_Invalid;
|
||||
|
||||
EntityFileWriter writer(m_CurrentFile.string());
|
||||
writer.WriteWorld(world);
|
||||
|
||||
createWidget();
|
||||
} else {
|
||||
fileSaveAs(world);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSystem::fileSaveAs(World* world)
|
||||
{
|
||||
auto filePath = saveDialog(m_DefaultEntityDir);
|
||||
if (filePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: Delete the widgets so they don't appear in the saved file
|
||||
world->DeleteEntity(m_Widget);
|
||||
m_Widget = EntityID_Invalid;
|
||||
|
||||
EntityFileWriter writer(filePath.string());
|
||||
writer.WriteWorld(world);
|
||||
|
||||
createWidget();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "Input/InputProxy.h"
|
||||
#include "Input/InputHandler.h"
|
||||
|
||||
InputProxy::InputProxy(EventBroker* eventBroker)
|
||||
: m_EventBroker(eventBroker)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EBindOrigin, &InputProxy::OnBindOrigin);
|
||||
}
|
||||
|
||||
InputProxy::~InputProxy()
|
||||
{
|
||||
for (auto& handler : m_Handlers) {
|
||||
delete handler;
|
||||
}
|
||||
}
|
||||
|
||||
void InputProxy::LoadBindings(std::string file)
|
||||
{
|
||||
auto config = ResourceManager::Load<ConfigFile>(file);
|
||||
for (auto& origin : config->GetAll<std::string>("Bindings")) {
|
||||
Events::BindOrigin e;
|
||||
e.Origin = origin.first;
|
||||
e.Command = origin.second;
|
||||
e.Value = 1.f;
|
||||
if (!e.Command.empty()) {
|
||||
char prefix = e.Command.at(0);
|
||||
if (prefix == '+' || prefix == '-') {
|
||||
e.Command = e.Command.substr(1);
|
||||
if (prefix == '-') {
|
||||
e.Value *= -1.f;
|
||||
}
|
||||
}
|
||||
OnBindOrigin(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputProxy::Update(double dt)
|
||||
{
|
||||
m_EventBroker->Process<InputProxy>();
|
||||
m_EventBroker->Process<InputHandler>();
|
||||
for (auto& handler : m_Handlers) {
|
||||
handler->Update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
void InputProxy::Process()
|
||||
{
|
||||
for (auto& pair : m_CommandHandlers) {
|
||||
const std::string& command = pair.first;
|
||||
auto handlers = pair.second;
|
||||
m_CurrentCommandValues[command] = 0.f;
|
||||
for (auto& handler : handlers) {
|
||||
m_CurrentCommandValues[command] += handler->GetCommandValue(command);
|
||||
}
|
||||
|
||||
auto last = m_LastCommandValues.find(command);
|
||||
float currentValue = m_CurrentCommandValues[command];
|
||||
if (last == m_LastCommandValues.end() || last->second != currentValue) {
|
||||
Events::InputCommand e;
|
||||
e.PlayerID = -1;
|
||||
e.Command = command;
|
||||
e.Value = currentValue;
|
||||
m_EventBroker->Publish(e);
|
||||
LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID);
|
||||
m_LastCommandValues[command] = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate the input values of all unique commands published by input handlers
|
||||
for (auto& pair : m_CommandQueue) {
|
||||
Events::InputCommand e;
|
||||
e.PlayerID = pair.first.first;
|
||||
e.Command = pair.first.second;
|
||||
e.Value = 0;
|
||||
for (auto& value : pair.second) {
|
||||
e.Value += value;
|
||||
}
|
||||
//e.Value = std::max(-1.f, std::min(e.Value, 1.f));
|
||||
m_EventBroker->Publish(e);
|
||||
LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, e.PlayerID);
|
||||
}
|
||||
m_CommandQueue.clear();
|
||||
}
|
||||
|
||||
void InputProxy::Publish(const Events::InputCommand& e)
|
||||
{
|
||||
auto key = std::make_pair(e.PlayerID, e.Command);
|
||||
m_CommandQueue[key].push_back(e.Value);
|
||||
}
|
||||
|
||||
bool InputProxy::OnBindOrigin(const Events::BindOrigin& e)
|
||||
{
|
||||
bool originBound = false;
|
||||
for (auto& handler : m_Handlers) {
|
||||
bool result = handler->BindOrigin(e.Origin, e.Command, e.Value);
|
||||
if (result) {
|
||||
m_CommandHandlers[e.Command].insert(handler);
|
||||
m_LastCommandValues[e.Command] = 0.f;
|
||||
if (originBound) {
|
||||
LOG_WARNING("Multiple handlers responded to binding input origin \"%s\"!", e.Origin.c_str());
|
||||
}
|
||||
originBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!originBound) {
|
||||
LOG_ERROR("No input handler responded to binding input origin \"%s\"!", e.Origin.c_str());
|
||||
}
|
||||
|
||||
return originBound;
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Input/InputSystem.h"
|
||||
#include "Core/World.h"
|
||||
|
||||
void Systems::InputSystem::RegisterComponents(ComponentFactory* cf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Systems::InputSystem::Initialize()
|
||||
{
|
||||
// Subscribe to events
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &Systems::InputSystem::OnKeyDown);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &Systems::InputSystem::OnKeyUp);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &Systems::InputSystem::OnMousePress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &Systems::InputSystem::OnMouseRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EGamepadAxis, &Systems::InputSystem::OnGamepadAxis);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EGamepadButtonDown, &Systems::InputSystem::OnGamepadButtonDown);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EGamepadButtonUp, &Systems::InputSystem::OnGamepadButtonUp);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EBindKey, &Systems::InputSystem::OnBindKey);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EBindMouseButton, &Systems::InputSystem::OnBindMouseButton);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EBindGamepadAxis, &Systems::InputSystem::OnBindGamepadAxis);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EBindGamepadButton, &Systems::InputSystem::OnBindGamepadButton);
|
||||
}
|
||||
|
||||
void Systems::InputSystem::Update(double dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnKeyDown(const Events::KeyDown &event)
|
||||
{
|
||||
auto range = m_KeyBindings.equal_range(event.KeyCode);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandKeyboardValues[command][event.KeyCode] = value;
|
||||
PublishCommand(1, command, GetCommandTotalValue(command));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnKeyUp(const Events::KeyUp &event)
|
||||
{
|
||||
auto range = m_KeyBindings.equal_range(event.KeyCode);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandKeyboardValues[command][event.KeyCode] = 0;
|
||||
PublishCommand(1, command, GetCommandTotalValue(command));;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnMousePress(const Events::MousePress &event)
|
||||
{
|
||||
auto range = m_MouseButtonBindings.equal_range(event.Button);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandMouseButtonValues[command][event.Button] = value;
|
||||
PublishCommand(1, command, GetCommandTotalValue(command));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnMouseRelease(const Events::MouseRelease &event)
|
||||
{
|
||||
auto range = m_MouseButtonBindings.equal_range(event.Button);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandMouseButtonValues[command][event.Button] = 0;
|
||||
PublishCommand(1, command, GetCommandTotalValue(command));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnGamepadAxis(const Events::GamepadAxis &event)
|
||||
{
|
||||
auto range = m_GamepadAxisBindings.equal_range(event.Axis);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandGamepadAxisValues[command][event.Axis] = event.Value * value;
|
||||
PublishCommand(event.GamepadID + 1, command, GetCommandTotalValue(command));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnGamepadButtonDown(const Events::GamepadButtonDown &event)
|
||||
{
|
||||
auto range = m_GamepadButtonBindings.equal_range(event.Button);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandGamepadButtonValues[command][event.Button] = value;
|
||||
PublishCommand(event.GamepadID + 1, command, GetCommandTotalValue(command));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnGamepadButtonUp(const Events::GamepadButtonUp &event)
|
||||
{
|
||||
auto range = m_GamepadButtonBindings.equal_range(event.Button);
|
||||
for (auto bindingIt = range.first; bindingIt != range.second; bindingIt++) {
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = bindingIt->second;
|
||||
m_CommandGamepadButtonValues[command][event.Button] = 0;
|
||||
PublishCommand(event.GamepadID + 1, command, GetCommandTotalValue(command));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnBindKey(const Events::BindKey &event)
|
||||
{
|
||||
if (event.Command.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_KeyBindings.insert(std::make_pair(event.KeyCode, std::make_tuple(event.Command, event.Value)));
|
||||
LOG_DEBUG("Input: Bound key %i to %s", event.KeyCode, event.Command.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnBindMouseButton(const Events::BindMouseButton &event)
|
||||
{
|
||||
if (event.Command.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_MouseButtonBindings.insert(std::make_pair(event.Button, std::make_tuple(event.Command, event.Value)));
|
||||
LOG_DEBUG("Input: Bound mouse button %i to %s", event.Button, event.Command.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnBindGamepadAxis(const Events::BindGamepadAxis &event)
|
||||
{
|
||||
if (event.Command.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_GamepadAxisBindings.insert(std::make_pair(event.Axis, std::make_tuple(event.Command, event.Value)));
|
||||
LOG_DEBUG("Input: Bound gamepad axis %i to %s", event.Axis, event.Command.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Systems::InputSystem::OnBindGamepadButton(const Events::BindGamepadButton &event)
|
||||
{
|
||||
if (event.Command.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_GamepadButtonBindings.insert(std::make_pair(event.Button, std::make_tuple(event.Command, event.Value)));
|
||||
LOG_DEBUG("Input: Bound gamepad axis %i to %s", event.Button, event.Command.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float Systems::InputSystem::GetCommandTotalValue(std::string command)
|
||||
{
|
||||
float value = 0.f;
|
||||
|
||||
auto keyboardIt = m_CommandKeyboardValues.find(command);
|
||||
if (keyboardIt != m_CommandKeyboardValues.end()) {
|
||||
for (auto &key : keyboardIt->second) {
|
||||
value += key.second;
|
||||
}
|
||||
}
|
||||
|
||||
auto mouseButtonIt = m_CommandMouseButtonValues.find(command);
|
||||
if (mouseButtonIt != m_CommandMouseButtonValues.end()) {
|
||||
for (auto &button : mouseButtonIt->second) {
|
||||
value += button.second;
|
||||
}
|
||||
}
|
||||
|
||||
auto gamepadAxisIt = m_CommandGamepadAxisValues.find(command);
|
||||
if (gamepadAxisIt != m_CommandGamepadAxisValues.end()) {
|
||||
for (auto &axis : gamepadAxisIt->second) {
|
||||
value += axis.second;
|
||||
}
|
||||
}
|
||||
|
||||
auto gamepadButtonIt = m_CommandGamepadButtonValues.find(command);
|
||||
if (gamepadButtonIt != m_CommandGamepadButtonValues.end()) {
|
||||
for (auto &button : gamepadButtonIt->second) {
|
||||
value += button.second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::max(-1.f, std::min(value, 1.f));
|
||||
}
|
||||
|
||||
void Systems::InputSystem::PublishCommand(int playerID, std::string command, float value)
|
||||
{
|
||||
Events::InputCommand e;
|
||||
e.PlayerID = playerID;
|
||||
e.Command = command;
|
||||
e.Value = value;
|
||||
EventBroker->Publish(e);
|
||||
|
||||
LOG_DEBUG("Input: Published command %s=%f for player %i", e.Command.c_str(), e.Value, playerID);
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
#include "Input/KeyboardInputHandler.h"
|
||||
|
||||
KeyboardInputHandler::KeyboardInputHandler(EventBroker* eventBroker, InputProxy* inputProxy) : InputHandler(eventBroker, inputProxy)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &KeyboardInputHandler::OnKeyDown);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &KeyboardInputHandler::OnKeyUp);
|
||||
|
||||
m_OriginKeyCodes["Space"] = GLFW_KEY_SPACE;
|
||||
m_OriginKeyCodes["Apostrophe"] = GLFW_KEY_APOSTROPHE;
|
||||
m_OriginKeyCodes["Comma"] = GLFW_KEY_COMMA;
|
||||
m_OriginKeyCodes["Minus"] = GLFW_KEY_MINUS;
|
||||
m_OriginKeyCodes["Period"] = GLFW_KEY_PERIOD;
|
||||
m_OriginKeyCodes["Slash"] = GLFW_KEY_SLASH;
|
||||
m_OriginKeyCodes["0"] = GLFW_KEY_0;
|
||||
m_OriginKeyCodes["1"] = GLFW_KEY_1;
|
||||
m_OriginKeyCodes["2"] = GLFW_KEY_2;
|
||||
m_OriginKeyCodes["3"] = GLFW_KEY_3;
|
||||
m_OriginKeyCodes["4"] = GLFW_KEY_4;
|
||||
m_OriginKeyCodes["5"] = GLFW_KEY_5;
|
||||
m_OriginKeyCodes["6"] = GLFW_KEY_6;
|
||||
m_OriginKeyCodes["7"] = GLFW_KEY_7;
|
||||
m_OriginKeyCodes["8"] = GLFW_KEY_8;
|
||||
m_OriginKeyCodes["9"] = GLFW_KEY_9;
|
||||
m_OriginKeyCodes["Semicolon"] = GLFW_KEY_SEMICOLON;
|
||||
m_OriginKeyCodes["Equal"] = GLFW_KEY_EQUAL;
|
||||
m_OriginKeyCodes["A"] = GLFW_KEY_A;
|
||||
m_OriginKeyCodes["B"] = GLFW_KEY_B;
|
||||
m_OriginKeyCodes["C"] = GLFW_KEY_C;
|
||||
m_OriginKeyCodes["D"] = GLFW_KEY_D;
|
||||
m_OriginKeyCodes["E"] = GLFW_KEY_E;
|
||||
m_OriginKeyCodes["F"] = GLFW_KEY_F;
|
||||
m_OriginKeyCodes["G"] = GLFW_KEY_G;
|
||||
m_OriginKeyCodes["H"] = GLFW_KEY_H;
|
||||
m_OriginKeyCodes["I"] = GLFW_KEY_I;
|
||||
m_OriginKeyCodes["J"] = GLFW_KEY_J;
|
||||
m_OriginKeyCodes["K"] = GLFW_KEY_K;
|
||||
m_OriginKeyCodes["L"] = GLFW_KEY_L;
|
||||
m_OriginKeyCodes["M"] = GLFW_KEY_M;
|
||||
m_OriginKeyCodes["N"] = GLFW_KEY_N;
|
||||
m_OriginKeyCodes["O"] = GLFW_KEY_O;
|
||||
m_OriginKeyCodes["P"] = GLFW_KEY_P;
|
||||
m_OriginKeyCodes["Q"] = GLFW_KEY_Q;
|
||||
m_OriginKeyCodes["R"] = GLFW_KEY_R;
|
||||
m_OriginKeyCodes["S"] = GLFW_KEY_S;
|
||||
m_OriginKeyCodes["T"] = GLFW_KEY_T;
|
||||
m_OriginKeyCodes["U"] = GLFW_KEY_U;
|
||||
m_OriginKeyCodes["V"] = GLFW_KEY_V;
|
||||
m_OriginKeyCodes["W"] = GLFW_KEY_W;
|
||||
m_OriginKeyCodes["X"] = GLFW_KEY_X;
|
||||
m_OriginKeyCodes["Y"] = GLFW_KEY_Y;
|
||||
m_OriginKeyCodes["Z"] = GLFW_KEY_Z;
|
||||
m_OriginKeyCodes["LeftBracket"] = GLFW_KEY_LEFT_BRACKET;
|
||||
m_OriginKeyCodes["Backslash"] = GLFW_KEY_BACKSLASH;
|
||||
m_OriginKeyCodes["RightBracket"] = GLFW_KEY_RIGHT_BRACKET;
|
||||
m_OriginKeyCodes["Accent"] = GLFW_KEY_GRAVE_ACCENT;
|
||||
m_OriginKeyCodes["W1"] = GLFW_KEY_WORLD_1;
|
||||
m_OriginKeyCodes["W2"] = GLFW_KEY_WORLD_2;
|
||||
m_OriginKeyCodes["Escape"] = GLFW_KEY_ESCAPE;
|
||||
m_OriginKeyCodes["Enter"] = GLFW_KEY_ENTER;
|
||||
m_OriginKeyCodes["Tab"] = GLFW_KEY_TAB;
|
||||
m_OriginKeyCodes["Backspace"] = GLFW_KEY_BACKSPACE;
|
||||
m_OriginKeyCodes["Insert"] = GLFW_KEY_INSERT;
|
||||
m_OriginKeyCodes["Delete"] = GLFW_KEY_DELETE;
|
||||
m_OriginKeyCodes["Right"] = GLFW_KEY_RIGHT;
|
||||
m_OriginKeyCodes["Left"] = GLFW_KEY_LEFT;
|
||||
m_OriginKeyCodes["Down"] = GLFW_KEY_DOWN;
|
||||
m_OriginKeyCodes["Up"] = GLFW_KEY_UP;
|
||||
m_OriginKeyCodes["PgUp"] = GLFW_KEY_PAGE_UP;
|
||||
m_OriginKeyCodes["PgDn"] = GLFW_KEY_PAGE_DOWN;
|
||||
m_OriginKeyCodes["Home"] = GLFW_KEY_HOME;
|
||||
m_OriginKeyCodes["End"] = GLFW_KEY_END;
|
||||
m_OriginKeyCodes["CapsLock"] = GLFW_KEY_CAPS_LOCK;
|
||||
m_OriginKeyCodes["ScrollLock"] = GLFW_KEY_SCROLL_LOCK;
|
||||
m_OriginKeyCodes["NumLock"] = GLFW_KEY_NUM_LOCK;
|
||||
m_OriginKeyCodes["PrintScreen"] = GLFW_KEY_PRINT_SCREEN;
|
||||
m_OriginKeyCodes["Pause"] = GLFW_KEY_PAUSE;
|
||||
m_OriginKeyCodes["F1"] = GLFW_KEY_F1;
|
||||
m_OriginKeyCodes["F2"] = GLFW_KEY_F2;
|
||||
m_OriginKeyCodes["F3"] = GLFW_KEY_F3;
|
||||
m_OriginKeyCodes["F4"] = GLFW_KEY_F4;
|
||||
m_OriginKeyCodes["F5"] = GLFW_KEY_F5;
|
||||
m_OriginKeyCodes["F6"] = GLFW_KEY_F6;
|
||||
m_OriginKeyCodes["F7"] = GLFW_KEY_F7;
|
||||
m_OriginKeyCodes["F8"] = GLFW_KEY_F8;
|
||||
m_OriginKeyCodes["F9"] = GLFW_KEY_F9;
|
||||
m_OriginKeyCodes["F10"] = GLFW_KEY_F10;
|
||||
m_OriginKeyCodes["F11"] = GLFW_KEY_F11;
|
||||
m_OriginKeyCodes["F12"] = GLFW_KEY_F12;
|
||||
m_OriginKeyCodes["F13"] = GLFW_KEY_F13;
|
||||
m_OriginKeyCodes["F14"] = GLFW_KEY_F14;
|
||||
m_OriginKeyCodes["F15"] = GLFW_KEY_F15;
|
||||
m_OriginKeyCodes["F16"] = GLFW_KEY_F16;
|
||||
m_OriginKeyCodes["F17"] = GLFW_KEY_F17;
|
||||
m_OriginKeyCodes["F18"] = GLFW_KEY_F18;
|
||||
m_OriginKeyCodes["F19"] = GLFW_KEY_F19;
|
||||
m_OriginKeyCodes["F20"] = GLFW_KEY_F20;
|
||||
m_OriginKeyCodes["F21"] = GLFW_KEY_F21;
|
||||
m_OriginKeyCodes["F22"] = GLFW_KEY_F22;
|
||||
m_OriginKeyCodes["F23"] = GLFW_KEY_F23;
|
||||
m_OriginKeyCodes["F24"] = GLFW_KEY_F24;
|
||||
m_OriginKeyCodes["F25"] = GLFW_KEY_F25;
|
||||
m_OriginKeyCodes["KP0"] = GLFW_KEY_KP_0;
|
||||
m_OriginKeyCodes["KP1"] = GLFW_KEY_KP_1;
|
||||
m_OriginKeyCodes["KP2"] = GLFW_KEY_KP_2;
|
||||
m_OriginKeyCodes["KP3"] = GLFW_KEY_KP_3;
|
||||
m_OriginKeyCodes["KP4"] = GLFW_KEY_KP_4;
|
||||
m_OriginKeyCodes["KP5"] = GLFW_KEY_KP_5;
|
||||
m_OriginKeyCodes["KP6"] = GLFW_KEY_KP_6;
|
||||
m_OriginKeyCodes["KP7"] = GLFW_KEY_KP_7;
|
||||
m_OriginKeyCodes["KP8"] = GLFW_KEY_KP_8;
|
||||
m_OriginKeyCodes["KP9"] = GLFW_KEY_KP_9;
|
||||
m_OriginKeyCodes["KPDecimal"] = GLFW_KEY_KP_DECIMAL;
|
||||
m_OriginKeyCodes["KPDivide"] = GLFW_KEY_KP_DIVIDE;
|
||||
m_OriginKeyCodes["KPMultiply"] = GLFW_KEY_KP_MULTIPLY;
|
||||
m_OriginKeyCodes["KPSubtract"] = GLFW_KEY_KP_SUBTRACT;
|
||||
m_OriginKeyCodes["KPAdd"] = GLFW_KEY_KP_ADD;
|
||||
m_OriginKeyCodes["KPEnter"] = GLFW_KEY_KP_ENTER;
|
||||
m_OriginKeyCodes["KPEqual"] = GLFW_KEY_KP_EQUAL;
|
||||
m_OriginKeyCodes["LeftShift"] = GLFW_KEY_LEFT_SHIFT;
|
||||
m_OriginKeyCodes["LeftControl"] = GLFW_KEY_LEFT_CONTROL;
|
||||
m_OriginKeyCodes["LeftAlt"] = GLFW_KEY_LEFT_ALT;
|
||||
m_OriginKeyCodes["LeftSuper"] = GLFW_KEY_LEFT_SUPER;
|
||||
m_OriginKeyCodes["RightShift"] = GLFW_KEY_RIGHT_SHIFT;
|
||||
m_OriginKeyCodes["RightControl"] = GLFW_KEY_RIGHT_CONTROL;
|
||||
m_OriginKeyCodes["RightAlt"] = GLFW_KEY_RIGHT_ALT;
|
||||
m_OriginKeyCodes["RightSuper"] = GLFW_KEY_RIGHT_SUPER;
|
||||
m_OriginKeyCodes["Menu"] = GLFW_KEY_MENU;
|
||||
}
|
||||
|
||||
bool KeyboardInputHandler::BindOrigin(std::string origin, std::string command, float value)
|
||||
{
|
||||
auto originIt = m_OriginKeyCodes.find(origin);
|
||||
if (originIt == m_OriginKeyCodes.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int keyCode = originIt->second;
|
||||
m_KeyBindings[keyCode] = std::make_tuple(command, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyboardInputHandler::OnKeyDown(const Events::KeyDown& e)
|
||||
{
|
||||
auto it = m_KeyBindings.find(e.KeyCode);
|
||||
if (it == m_KeyBindings.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = it->second;
|
||||
m_CommandValues[command] += value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyboardInputHandler::OnKeyUp(const Events::KeyUp& e)
|
||||
{
|
||||
auto it = m_KeyBindings.find(e.KeyCode);
|
||||
if (it == m_KeyBindings.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = it->second;
|
||||
m_CommandValues[command] -= value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float KeyboardInputHandler::GetCommandValue(std::string command)
|
||||
{
|
||||
auto it = m_CommandValues.find(command);
|
||||
if (it != m_CommandValues.end()) {
|
||||
return m_CommandValues[command];
|
||||
} else {
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "Input/MouseInputHandler.h"
|
||||
|
||||
MouseInputHandler::MouseInputHandler(EventBroker* eventBroker, InputProxy* inputProxy)
|
||||
: InputHandler(eventBroker, inputProxy)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &MouseInputHandler::OnMousePress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &MouseInputHandler::OnMouseRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &MouseInputHandler::OnMouseMove);
|
||||
|
||||
m_OriginCodes["Mouse1"] = GLFW_MOUSE_BUTTON_1;
|
||||
m_OriginCodes["MouseLeft"] = GLFW_MOUSE_BUTTON_LEFT;
|
||||
m_OriginCodes["Mouse2"] = GLFW_MOUSE_BUTTON_2;
|
||||
m_OriginCodes["MouseRight"] = GLFW_MOUSE_BUTTON_RIGHT;
|
||||
m_OriginCodes["Mouse3"] = GLFW_MOUSE_BUTTON_3;
|
||||
m_OriginCodes["MouseMiddle"] = GLFW_MOUSE_BUTTON_MIDDLE;
|
||||
m_OriginCodes["Mouse4"] = GLFW_MOUSE_BUTTON_4;
|
||||
m_OriginCodes["Mouse5"] = GLFW_MOUSE_BUTTON_5;
|
||||
m_OriginCodes["Mouse6"] = GLFW_MOUSE_BUTTON_6;
|
||||
m_OriginCodes["Mouse7"] = GLFW_MOUSE_BUTTON_7;
|
||||
m_OriginCodes["Mouse8"] = GLFW_MOUSE_BUTTON_8;
|
||||
|
||||
m_OriginAxes["MouseX"] = 'X';
|
||||
m_OriginAxes["MouseY"] = 'Y';
|
||||
}
|
||||
|
||||
bool MouseInputHandler::BindOrigin(std::string origin, std::string command, float value)
|
||||
{
|
||||
auto originCode = m_OriginCodes.find(origin);
|
||||
if (originCode != m_OriginCodes.end()) {
|
||||
int code = originCode->second;
|
||||
m_Bindings[code] = std::make_tuple(command, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto originAxis = m_OriginAxes.find(origin);
|
||||
if (originAxis != m_OriginAxes.end()) {
|
||||
char axis = originAxis->second;
|
||||
float multiplier = 1.f;
|
||||
// Sensitivity
|
||||
multiplier *= ResourceManager::Load<ConfigFile>("Input.ini")->Get<float>("Mouse.Sensitivity", 1.f);
|
||||
if (axis == 'Y') {
|
||||
if (ResourceManager::Load<ConfigFile>("Input.ini")->Get<bool>("Mouse.InvertPitch", false)) {
|
||||
multiplier *= -1.f;
|
||||
}
|
||||
}
|
||||
m_Axes[axis] = std::make_tuple(command, value * multiplier);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float MouseInputHandler::GetCommandValue(std::string command)
|
||||
{
|
||||
auto it = m_CommandValues.find(command);
|
||||
if (it != m_CommandValues.end()) {
|
||||
return m_CommandValues[command];
|
||||
} else {
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
bool MouseInputHandler::OnMousePress(const Events::MousePress& e)
|
||||
{
|
||||
auto it = m_Bindings.find(e.Button);
|
||||
if (it == m_Bindings.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = it->second;
|
||||
m_CommandValues[command] += value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MouseInputHandler::OnMouseRelease(const Events::MouseRelease& e)
|
||||
{
|
||||
auto it = m_Bindings.find(e.Button);
|
||||
if (it == m_Bindings.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string command;
|
||||
float value;
|
||||
std::tie(command, value) = it->second;
|
||||
m_CommandValues[command] -= value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MouseInputHandler::OnMouseMove(const Events::MouseMove& e)
|
||||
{
|
||||
if (std::abs(e.DeltaX) > 0) {
|
||||
auto it = m_Axes.find('X');
|
||||
if (it != m_Axes.end()) {
|
||||
Events::InputCommand ic;
|
||||
ic.PlayerID = -1;
|
||||
std::tie(ic.Command, ic.Value) = it->second;
|
||||
ic.Value *= e.DeltaX;
|
||||
m_InputProxy->Publish(ic);
|
||||
}
|
||||
}
|
||||
|
||||
if (std::abs(e.DeltaY) > 0) {
|
||||
auto it = m_Axes.find('Y');
|
||||
if (it != m_Axes.end()) {
|
||||
Events::InputCommand ic;
|
||||
ic.PlayerID = -1;
|
||||
std::tie(ic.Command, ic.Value) = it->second;
|
||||
ic.Value *= e.DeltaY;
|
||||
m_InputProxy->Publish(ic);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MouseInputHandler::hasOrigin(std::string origin)
|
||||
{
|
||||
if (m_OriginCodes.find(origin) == m_OriginCodes.end()) {
|
||||
return false;
|
||||
}
|
||||
if (m_OriginAxes.find(origin) == m_OriginAxes.end()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,11 +1,334 @@
|
||||
#include "Network\Client.h"
|
||||
#include "Network/Client.h"
|
||||
|
||||
Client::Client()
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
|
||||
Client::Client(ConfigFile* config) : m_Socket(m_IOService)
|
||||
{
|
||||
|
||||
// Default is local host
|
||||
std::string address = config->Get<std::string>("Networking.Address", "127.0.0.1");
|
||||
int port = config->Get<int>("Networking.Port", 13);
|
||||
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
|
||||
// Set up network stream
|
||||
m_PlayerName = config->Get<std::string>("Networking.Name", "Raptorcopter");
|
||||
m_NextSnapshot.InputForward = "";
|
||||
m_NextSnapshot.InputRight = "";
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Client::Start(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
m_WasStarted = true;
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
|
||||
// Subscribe to events
|
||||
m_EInputCommand = decltype(m_EInputCommand)(std::bind(&Client::OnInputCommand, this, std::placeholders::_1));
|
||||
m_EventBroker->Subscribe(m_EInputCommand);
|
||||
|
||||
|
||||
//while (m_PlayerName.size() > 7) {
|
||||
// LOG_INFO("Please enter your name (No longer than 7 characters):");
|
||||
// std::cin >> m_PlayerName;
|
||||
//}
|
||||
m_Socket.connect(m_ReceiverEndpoint);
|
||||
LOG_INFO("I am client. BIP BOP");
|
||||
}
|
||||
|
||||
void Client::Update()
|
||||
{
|
||||
readFromServer();
|
||||
}
|
||||
|
||||
void Client::Close()
|
||||
{
|
||||
if (m_WasStarted) {
|
||||
disconnect();
|
||||
m_ThreadIsRunning = false;
|
||||
m_EventBroker->Unsubscribe(m_EInputCommand);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::readFromServer()
|
||||
{
|
||||
if (m_Socket.available()) {
|
||||
bytesRead = receive(readBuf, INPUTSIZE);
|
||||
if (bytesRead > 0) {
|
||||
Packet packet(readBuf, bytesRead);
|
||||
parseMessageType(packet);
|
||||
}
|
||||
}
|
||||
std::clock_t currentTime = std::clock();
|
||||
if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
if (isConnected()) {
|
||||
sendSnapshotToServer();
|
||||
}
|
||||
previousSnapshotMessage = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::sendSnapshotToServer()
|
||||
{
|
||||
// Reset previouse key state in snapshot.
|
||||
m_NextSnapshot.InputForward = "";
|
||||
m_NextSnapshot.InputRight = "";
|
||||
|
||||
auto player = m_World->GetComponent(m_PlayerDefinitions[m_PlayerID].EntityID, "Player");
|
||||
|
||||
|
||||
// See if any movement keys are down
|
||||
// We dont care if it's overwritten by later
|
||||
// if statement. Watcha gonna do, right!
|
||||
if (player["Forward"]) {
|
||||
m_NextSnapshot.InputForward = "+Forward";
|
||||
}
|
||||
if (player["Left"]) {
|
||||
m_NextSnapshot.InputRight = "-Right";
|
||||
}
|
||||
if (player["Back"]) {
|
||||
m_NextSnapshot.InputForward = "-Forward";
|
||||
}
|
||||
if (player["Right"]) {
|
||||
m_NextSnapshot.InputRight = "+Right";
|
||||
}
|
||||
|
||||
if (m_NextSnapshot.InputForward != "") {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString(m_NextSnapshot.InputForward);
|
||||
send(packet);
|
||||
} else {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString("0Forward");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
if (m_NextSnapshot.InputRight != "") {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString(m_NextSnapshot.InputRight);
|
||||
send(packet);
|
||||
} else {
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString("0Right");
|
||||
send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseMessageType(Packet& packet)
|
||||
{
|
||||
int messageType = packet.ReadPrimitive<int>();
|
||||
if (messageType == -1)
|
||||
return;
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
//IdentifyPacketLoss();
|
||||
|
||||
switch (static_cast<MessageType>(messageType)) {
|
||||
case MessageType::Connect:
|
||||
parseConnect(packet);
|
||||
break;
|
||||
case MessageType::ClientPing:
|
||||
parsePing();
|
||||
break;
|
||||
case MessageType::ServerPing:
|
||||
parseServerPing();
|
||||
break;
|
||||
case MessageType::Message:
|
||||
break;
|
||||
case MessageType::Snapshot:
|
||||
parseSnapshot(packet);
|
||||
break;
|
||||
case MessageType::Disconnect:
|
||||
break;
|
||||
case MessageType::Event:
|
||||
parseEventMessage(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseConnect(Packet& packet)
|
||||
{
|
||||
m_PlayerID = packet.ReadPrimitive<int>();
|
||||
LOG_INFO("%i: I am player: %i", m_PacketID, m_PlayerID);
|
||||
}
|
||||
|
||||
void Client::parsePing()
|
||||
{
|
||||
m_DurationOfPingTime = 1000 * (std::clock() - m_StartPingTime) / static_cast<double>(CLOCKS_PER_SEC);
|
||||
LOG_INFO("%i: response time with ctime(ms): %f", m_PacketID, m_DurationOfPingTime);
|
||||
}
|
||||
|
||||
void Client::parseServerPing()
|
||||
{
|
||||
Packet packet(MessageType::ServerPing, m_SendPacketID);
|
||||
packet.WriteString("Ping recieved");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::parseEventMessage(Packet& packet)
|
||||
{
|
||||
int Id = -1;
|
||||
std::string command = packet.ReadString();
|
||||
if (command.find("+Player") != std::string::npos) {
|
||||
Id = packet.ReadPrimitive<int>();
|
||||
// Sett Player name
|
||||
m_PlayerDefinitions[Id].Name = command.erase(0, 7);
|
||||
} else {
|
||||
LOG_INFO("%i: Event message: %s", m_PacketID, command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseSnapshot(Packet& packet)
|
||||
{
|
||||
std::string tempName;
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
// We're checking for empty name for now. This might not be the best way,
|
||||
// but it is to avoid sending redundant data.
|
||||
tempName = packet.ReadString();
|
||||
|
||||
|
||||
// Apply the position data read to the player entity
|
||||
// New player connected on the server side
|
||||
if (m_PlayerDefinitions[i].Name == "" && tempName != "") {
|
||||
m_PlayerDefinitions[i].Name = tempName;
|
||||
m_PlayerDefinitions[i].EntityID = createPlayer();
|
||||
} else if (m_PlayerDefinitions[i].Name != "" && tempName == "") {
|
||||
// Someone disconnected
|
||||
// TODO: Insert code here
|
||||
break;
|
||||
} else if (m_PlayerDefinitions[i].Name == "" && tempName == "") {
|
||||
// Not a connected player
|
||||
break;
|
||||
}
|
||||
if (m_PlayerDefinitions[i].EntityID != -1) {
|
||||
|
||||
// Move player to server position
|
||||
int dataSize = m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform").Info.Meta.Stride;
|
||||
memcpy(m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform").Data, packet.ReadData(dataSize), dataSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Client::receive(char* data, size_t length)
|
||||
{
|
||||
boost::system::error_code error;
|
||||
|
||||
int bytesReceived = m_Socket.receive_from(boost
|
||||
::asio::buffer((void*)data, length),
|
||||
m_ReceiverEndpoint,
|
||||
0, error);
|
||||
|
||||
if (error) {
|
||||
//LOG_ERROR("receive: %s", error.message().c_str());
|
||||
}
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
void Client::send(Packet& packet)
|
||||
{
|
||||
m_Socket.send_to(boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint, 0);
|
||||
}
|
||||
|
||||
void Client::connect()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString(m_PlayerName);
|
||||
m_StartPingTime = std::clock();
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::disconnect()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString("+Disconnect");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::ping()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString("Ping");
|
||||
m_StartPingTime = std::clock();
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Client::moveMessageHead(char*& data, size_t& length, size_t stepSize)
|
||||
{
|
||||
data += stepSize;
|
||||
length -= stepSize;
|
||||
}
|
||||
|
||||
bool Client::OnInputCommand(const Events::InputCommand & e)
|
||||
{
|
||||
if (isConnected()) {
|
||||
ComponentWrapper& player = m_World->GetComponent(m_PlayerDefinitions[m_PlayerID].EntityID, "Player");
|
||||
if (e.Command == "Forward") {
|
||||
if (e.Value > 0) {
|
||||
(bool&)player["Forward"] = true;
|
||||
(bool&)player["Back"] = false;
|
||||
} else if (e.Value < 0) {
|
||||
(bool&)player["Back"] = true;
|
||||
(bool&)player["Forward"] = false;
|
||||
} else {
|
||||
(bool&)player["Forward"] = false;
|
||||
(bool&)player["Back"] = false;
|
||||
}
|
||||
}
|
||||
if (e.Command == "Right") {
|
||||
if (e.Value > 0) {
|
||||
(bool&)player["Right"] = true;
|
||||
(bool&)player["Left"] = false;
|
||||
} else if (e.Value < 0) {
|
||||
(bool&)player["Left"] = true;
|
||||
(bool&)player["Right"] = false;
|
||||
} else {
|
||||
(bool&)player["Left"] = false;
|
||||
(bool&)player["Right"] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.Command == "ConnectToServer") { // Connect for now
|
||||
connect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Client::identifyPacketLoss()
|
||||
{
|
||||
// if no packets lost, difference should be equal to 1
|
||||
int difference = m_PacketID - m_PreviousPacketID;
|
||||
if (difference != 1) {
|
||||
LOG_INFO("%i Packet(s) were lost...", difference);
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::isConnected()
|
||||
{
|
||||
if (m_PlayerID != -1) {
|
||||
if (m_PlayerDefinitions[m_PlayerID].EntityID != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityID Client::createPlayer()
|
||||
{
|
||||
EntityID entityID = m_World->CreateEntity();
|
||||
ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform");
|
||||
ComponentWrapper model = m_World->AttachComponent(entityID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitSphere.obj";
|
||||
ComponentWrapper player = m_World->AttachComponent(entityID, "Player");
|
||||
return entityID;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "Network/Packet.h"
|
||||
|
||||
Packet::Packet(MessageType type, unsigned int& packetID)
|
||||
{
|
||||
m_Data = new char[m_MaxPacketSize];
|
||||
// Create message header
|
||||
// Add message type
|
||||
int messageType = static_cast<int>(type);
|
||||
Packet::WritePrimitive<int>(messageType);
|
||||
packetID = packetID % 1000; // Packet id modulos
|
||||
Packet::WritePrimitive<int>(packetID);
|
||||
packetID++;
|
||||
}
|
||||
|
||||
// Create message
|
||||
Packet::Packet(char* data, const int sizeOfPacket)
|
||||
{
|
||||
// Resize message
|
||||
m_MaxPacketSize = sizeOfPacket;
|
||||
// Copy data newly allocated memory
|
||||
m_Data = new char[sizeOfPacket];
|
||||
memcpy(m_Data, data, sizeOfPacket);
|
||||
m_Offset = sizeOfPacket;
|
||||
}
|
||||
|
||||
Packet::~Packet()
|
||||
{
|
||||
delete[] m_Data;
|
||||
}
|
||||
|
||||
void Packet::WriteString(std::string str)
|
||||
{
|
||||
// Message, add one extra byte for null terminator
|
||||
int sizeOfString = str.size() + 1;
|
||||
if (m_Offset + sizeOfString > m_MaxPacketSize) {
|
||||
LOG_WARNING("Package::WriteString(): Data size in packet exceeded maximum package size.\n");
|
||||
}
|
||||
memcpy(m_Data + m_Offset, str.data(), sizeOfString * sizeof(char));
|
||||
m_Offset += sizeOfString * sizeof(char);
|
||||
}
|
||||
|
||||
void Packet::WriteData(char * data, int sizeOfData)
|
||||
{
|
||||
if (m_Offset + sizeOfData > m_MaxPacketSize) {
|
||||
LOG_WARNING("Packet::WriteData(): Data size in packet exceeded maximum packet size.\n");
|
||||
}
|
||||
memcpy(m_Data + m_Offset, data, sizeOfData);
|
||||
m_Offset += sizeOfData;
|
||||
}
|
||||
|
||||
std::string Packet::ReadString()
|
||||
{
|
||||
std::string returnValue(m_Data + m_ReturnDataOffset);
|
||||
if (m_Offset < m_ReturnDataOffset + returnValue.size()) {
|
||||
LOG_WARNING("packet ReadString(): Oh no! You are trying to remove things outside my memory kingdom");
|
||||
return "PopFrontString Failed";
|
||||
}
|
||||
// +1 for null terminator.
|
||||
m_ReturnDataOffset += returnValue.size() + 1;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
char * Packet::ReadData(int SizeOfData)
|
||||
{
|
||||
if (m_Offset < m_ReturnDataOffset + SizeOfData) {
|
||||
LOG_WARNING("packet ReadData(): Oh no! You are trying to remove things outside my memory kingdom");
|
||||
return nullptr;
|
||||
}
|
||||
unsigned int oldReturnDataOffset = m_ReturnDataOffset;
|
||||
m_ReturnDataOffset += SizeOfData;
|
||||
return (m_Data + oldReturnDataOffset);
|
||||
}
|
||||
@@ -1,11 +1,355 @@
|
||||
#include "Network\Server.h"
|
||||
#include "Network/Server.h"
|
||||
|
||||
Server::Server()
|
||||
{
|
||||
|
||||
}
|
||||
Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 13))
|
||||
{ }
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
{ }
|
||||
|
||||
|
||||
void Server::Start(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
m_World = world;
|
||||
m_EventBroker = eventBroker;
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
m_StopTimes[i] = std::clock();
|
||||
}
|
||||
LOG_INFO("I am Server. BIP BOP\n");
|
||||
}
|
||||
|
||||
void Server::Update()
|
||||
{
|
||||
readFromClients();
|
||||
}
|
||||
|
||||
void Server::Close()
|
||||
{
|
||||
m_ThreadIsRunning = false;
|
||||
}
|
||||
|
||||
void Server::readFromClients()
|
||||
{
|
||||
// m_ThreadIsRunning might be unnecessary but the
|
||||
// program crashed if it executed m_Socket.available()
|
||||
// when closing the program.
|
||||
|
||||
if (m_Socket.available()) {
|
||||
try {
|
||||
bytesRead = receive(readBuffer, INPUTSIZE);
|
||||
Packet packet(readBuffer, bytesRead);
|
||||
parseMessageType(packet);
|
||||
} catch (const std::exception& err) {
|
||||
//LOG_ERROR("%i: Read from client crashed %s", m_PacketID, err.what());
|
||||
}
|
||||
|
||||
}
|
||||
std::clock_t currentTime = std::clock();
|
||||
// Send snapshot
|
||||
if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
sendSnapshot();
|
||||
previousSnapshotMessage = currentTime;
|
||||
}
|
||||
|
||||
// Send pings each
|
||||
if (intervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
sendPing();
|
||||
previousePingMessage = currentTime;
|
||||
}
|
||||
|
||||
// Time out logic
|
||||
if (checkTimeOutInterval < (1000 * (currentTime - timOutTimer) / (double)CLOCKS_PER_SEC)) {
|
||||
checkForTimeOuts();
|
||||
timOutTimer = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseMessageType(Packet& packet)
|
||||
{
|
||||
int messageType = packet.ReadPrimitive<int>(); // Read what type off message was sent from server
|
||||
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
//IdentifyPacketLoss();
|
||||
switch (static_cast<MessageType>(messageType)) {
|
||||
case MessageType::Connect:
|
||||
parseConnect(packet);
|
||||
break;
|
||||
case MessageType::ClientPing:
|
||||
//parseClientPing();
|
||||
break;
|
||||
case MessageType::ServerPing:
|
||||
parseServerPing();
|
||||
break;
|
||||
case MessageType::Message:
|
||||
break;
|
||||
case MessageType::Snapshot:
|
||||
parseSnapshot(packet);
|
||||
break;
|
||||
case MessageType::Disconnect:
|
||||
parseDisconnect();
|
||||
break;
|
||||
case MessageType::Event:
|
||||
parseEvent(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int Server::receive(char * data, size_t length)
|
||||
{
|
||||
length = m_Socket.receive_from(
|
||||
boost::asio::buffer((void*)data
|
||||
, length)
|
||||
, m_ReceiverEndpoint, 0);
|
||||
return length;
|
||||
}
|
||||
|
||||
void Server::send(Packet& packet, int playerID)
|
||||
{
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer(packet.Data(), packet.Size()),
|
||||
m_PlayerDefinitions[playerID].Endpoint,
|
||||
0);
|
||||
}
|
||||
|
||||
void Server::send(Packet & packet)
|
||||
{
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint,
|
||||
0);
|
||||
}
|
||||
|
||||
void Server::moveMessageHead(char *& data, size_t & length, size_t stepSize)
|
||||
{
|
||||
data += stepSize;
|
||||
length -= stepSize;
|
||||
}
|
||||
|
||||
void Server::broadcast(std::string message)
|
||||
{
|
||||
Packet packet(MessageType::Event, m_SendPacketID);
|
||||
packet.WriteString(message);
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
send(packet, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::broadcast(Packet& packet)
|
||||
{
|
||||
for (int i = 0; i < MAXCONNECTIONS; ++i) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
send(packet, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendSnapshot()
|
||||
{
|
||||
Packet packet(MessageType::Snapshot, m_SendPacketID);
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
|
||||
// Send an empty name if there is no player connected on this position.
|
||||
packet.WriteString(m_PlayerDefinitions[i].Name);
|
||||
|
||||
if (m_PlayerDefinitions[i].EntityID == -1) {
|
||||
continue;
|
||||
}
|
||||
// Pack transfrom component into data packet
|
||||
auto transform = m_World->GetComponent(m_PlayerDefinitions[i].EntityID, "Transform");
|
||||
packet.WriteData(transform.Data, transform.Info.Meta.Stride);
|
||||
}
|
||||
broadcast(packet);
|
||||
}
|
||||
|
||||
void Server::sendPing()
|
||||
{
|
||||
// Prints connected players ping
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
int ping = 1000 * (m_StopTimes[i] - m_StartPingTime) / static_cast<double>(CLOCKS_PER_SEC);
|
||||
LOG_INFO("%i: Player %i's ping: %i", m_PacketID, i, ping);
|
||||
}
|
||||
}
|
||||
|
||||
// Create ping message
|
||||
Packet packet(MessageType::ServerPing, m_SendPacketID);
|
||||
packet.WriteString("Ping from server");
|
||||
// Time message
|
||||
m_StartPingTime = std::clock();
|
||||
// Send message
|
||||
broadcast(packet);
|
||||
}
|
||||
|
||||
void Server::checkForTimeOuts()
|
||||
{
|
||||
int timeOutTimeMs = 5000;
|
||||
int startPing = 1000 * m_StartPingTime
|
||||
/ static_cast<double>(CLOCKS_PER_SEC);
|
||||
|
||||
for (size_t i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
int stopPing = 1000 * m_StopTimes[i]
|
||||
/ static_cast<double>(CLOCKS_PER_SEC);
|
||||
if (startPing > stopPing + timeOutTimeMs) {
|
||||
LOG_INFO("Player %i timed out!", i);
|
||||
disconnect(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::disconnect(int i)
|
||||
{
|
||||
broadcast("A player disconnected");
|
||||
LOG_INFO("Player %i disconnected/timed out", i);
|
||||
|
||||
// Remove enteties and stuff
|
||||
m_PlayerDefinitions[i].Endpoint = boost::asio::ip::udp::endpoint();
|
||||
m_PlayerDefinitions[i].EntityID = -1;
|
||||
m_PlayerDefinitions[i].Name = "";
|
||||
}
|
||||
|
||||
void Server::parseEvent(Packet& packet)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If no player matches the address return.
|
||||
if (i >= 8)
|
||||
return;
|
||||
|
||||
unsigned int entityId = m_PlayerDefinitions[i].EntityID;
|
||||
std::string eventString = packet.ReadString();
|
||||
if ("+Forward" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Forward"] = true;
|
||||
m_World->GetComponent(entityId, "Player")["Back"] = false;
|
||||
} else if ("-Forward" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Forward"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Back"] = true;
|
||||
} else if ("0Forward" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Forward"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Back"] = false;
|
||||
}
|
||||
if ("+Right" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Left"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Right"] = true;
|
||||
} else if ("-Right" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Right"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Left"] = true;
|
||||
} else if ("0Right" == eventString) {
|
||||
m_World->GetComponent(entityId, "Player")["Right"] = false;
|
||||
m_World->GetComponent(entityId, "Player")["Left"] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseConnect(Packet& packet)
|
||||
{
|
||||
LOG_INFO("Parsing connections");
|
||||
// Check if player is already connected
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == boost::asio::ip::address()) {
|
||||
// Create new player
|
||||
m_PlayerDefinitions[i].EntityID = createPlayer();
|
||||
m_PlayerDefinitions[i].Endpoint = m_ReceiverEndpoint;
|
||||
m_PlayerDefinitions[i].Name = packet.ReadString();
|
||||
|
||||
m_StopTimes[i] = std::clock();
|
||||
|
||||
LOG_INFO("Player \"%s\" connected on IP: %s", m_PlayerDefinitions[i].Name, m_PlayerDefinitions[i].Endpoint.address().to_string());
|
||||
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WritePrimitive<int>(i); // Player ID
|
||||
|
||||
send(packet, i);
|
||||
|
||||
// Send notification that a player has connected
|
||||
std::string str = m_PacketID + "Player " + m_PlayerDefinitions[i].Name + " connected on: "
|
||||
+ m_PlayerDefinitions[i].Endpoint.address().to_string();
|
||||
broadcast(str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseDisconnect()
|
||||
{
|
||||
LOG_INFO("%i: Parsing disconnect", m_PacketID);
|
||||
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
disconnect(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseClientPing()
|
||||
{
|
||||
LOG_INFO("%i: Parsing ping", m_PacketID);
|
||||
// Return ping
|
||||
Packet packet(MessageType::ClientPing, m_SendPacketID);
|
||||
packet.WriteString("Ping received");
|
||||
send(packet); // This dosen't work for multiple users
|
||||
}
|
||||
|
||||
void Server::parseServerPing()
|
||||
{
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
m_StopTimes[i] = std::clock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOT USED
|
||||
void Server::parseSnapshot(Packet& packet)
|
||||
{
|
||||
// Does no logic. Returns snapshot if client request one
|
||||
// The snapshot is not a real snapshot tho...
|
||||
for (int i = 0; i < MAXCONNECTIONS; i++) {
|
||||
if (m_PlayerDefinitions[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer("I'm sending a snapshot to you guys!"),
|
||||
m_PlayerDefinitions[i].Endpoint,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::identifyPacketLoss()
|
||||
{
|
||||
// if no packets lost, difference should be equal to 1
|
||||
int difference = m_PacketID - m_PreviousPacketID;
|
||||
if (difference != 1) {
|
||||
LOG_INFO("%i Packet(s) were lost...", difference);
|
||||
}
|
||||
}
|
||||
|
||||
EntityID Server::createPlayer()
|
||||
{
|
||||
EntityID entityID = m_World->CreateEntity();
|
||||
ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform");
|
||||
transform["Position"] = glm::vec3(-1.5f, 0.f, 0.f);
|
||||
ComponentWrapper model = m_World->AttachComponent(entityID, "Model");
|
||||
model["Resource"] = "Models/Core/UnitSphere.obj";
|
||||
model["Color"] = glm::vec4(rand()%255 / 255.f, rand()%255 / 255.f, rand() %255 / 255.f, 1.f);
|
||||
ComponentWrapper player = m_World->AttachComponent(entityID, "Player");
|
||||
return entityID;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,18 @@ void Camera::SetOrientation(glm::quat val)
|
||||
UpdateViewMatrix();
|
||||
}
|
||||
|
||||
|
||||
void Camera::SetProjectionMatrix(glm::mat4 val)
|
||||
{
|
||||
m_ProjectionMatrix = val;
|
||||
}
|
||||
|
||||
|
||||
void Camera::SetViewMatrix(glm::mat4 val)
|
||||
{
|
||||
m_ViewMatrix = val;
|
||||
}
|
||||
|
||||
//void Camera::Pitch(float val)
|
||||
//{
|
||||
// m_Pitch = val;
|
||||
@@ -64,15 +76,6 @@ void Camera::SetOrientation(glm::quat val)
|
||||
|
||||
void Camera::UpdateProjectionMatrix()
|
||||
{
|
||||
// m_ProjectionMatrix = glm::ortho(
|
||||
// -16.f,
|
||||
// 16.f,
|
||||
// -9.f,
|
||||
// 9.f,
|
||||
// m_NearClip,
|
||||
// m_FarClip
|
||||
// );
|
||||
|
||||
m_ProjectionMatrix = glm::perspective(m_FOV, m_AspectRatio, m_NearClip, m_FarClip);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "Rendering/DrawScenePass.h"
|
||||
|
||||
DrawScenePass::DrawScenePass(IRenderer* renderer)
|
||||
{
|
||||
m_Renderer = renderer;
|
||||
InitializeTextures();
|
||||
InitializeShaderPrograms();
|
||||
}
|
||||
|
||||
void DrawScenePass::InitializeTextures()
|
||||
{
|
||||
m_WhiteTexture = ResourceManager::Load<Texture>("Textures/Core/Blank.png");
|
||||
}
|
||||
|
||||
void DrawScenePass::InitializeShaderPrograms()
|
||||
{
|
||||
//Gör så att shaders är en resource, tex som texture classen. Konstruktorn måste vara privat.
|
||||
m_BasicForwardProgram = ResourceManager::Load<ShaderProgram>("#BasicForwardProgram");
|
||||
|
||||
m_BasicForwardProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/BasicForward.vert.glsl")));
|
||||
m_BasicForwardProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/BasicForward.frag.glsl")));
|
||||
m_BasicForwardProgram->Compile();
|
||||
m_BasicForwardProgram->Link();
|
||||
}
|
||||
|
||||
void DrawScenePass::Draw(RenderScene& scene)
|
||||
{
|
||||
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
GLERROR("Renderer::Draw PickingPass");
|
||||
|
||||
DrawScenePassState state = DrawScenePassState();
|
||||
|
||||
for (auto &job : scene.ForwardJobs) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
if (modelJob) {
|
||||
GLuint ShaderHandle = m_BasicForwardProgram->GetHandle();
|
||||
|
||||
m_BasicForwardProgram->Bind();
|
||||
//TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
|
||||
glUniform4fv(glGetUniformLocation(ShaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color));
|
||||
|
||||
//TODO: Renderer: bättre textur felhantering samt fler texturer stöd
|
||||
if (modelJob->DiffuseTexture != nullptr) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, modelJob->DiffuseTexture->m_Texture);
|
||||
} else {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, m_WhiteTexture->m_Texture);
|
||||
}
|
||||
|
||||
glBindVertexArray(modelJob->Model->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex);
|
||||
|
||||
//continue;
|
||||
}
|
||||
}
|
||||
|
||||
GLERROR("DrawScene Error");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "Rendering/DrawScenePassState.h"
|
||||
|
||||
|
||||
DrawScenePassState::DrawScenePassState()
|
||||
{
|
||||
GLERROR("---");
|
||||
BindFramebuffer(0);
|
||||
GLERROR("---");
|
||||
Enable(GL_DEPTH_TEST);
|
||||
Enable(GL_CULL_FACE);
|
||||
Enable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
// ClearColor(glm::vec4(255.f / 255, 163.f / 255, 176.f / 255, 0.f));
|
||||
// Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
DrawScenePassState::~DrawScenePassState()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -39,17 +39,10 @@ void DummyRenderer::Initialize()
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Create default camera
|
||||
m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f);
|
||||
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 0));
|
||||
if (m_Camera == nullptr) {
|
||||
m_Camera = m_DefaultCamera;
|
||||
}
|
||||
|
||||
glfwSwapInterval(m_VSYNC);
|
||||
}
|
||||
|
||||
void DummyRenderer::Draw(RenderQueueCollection& rq)
|
||||
void DummyRenderer::Draw(RenderFrame& rq)
|
||||
{
|
||||
glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
@@ -8,16 +8,19 @@ Font::Font(std::string path)
|
||||
|
||||
if (FT_Init_FreeType(&library)) {
|
||||
LOG_ERROR("FreeType error: init failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (FT_New_Face(library, path.c_str(), 0, &face)) {
|
||||
LOG_ERROR("FreeType error: loading font");
|
||||
return;
|
||||
}
|
||||
|
||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
|
||||
if (FT_Load_Char(face, 'X', FT_LOAD_RENDER)) {
|
||||
LOG_ERROR("FreeType error: loading char");
|
||||
return;
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
@@ -48,13 +48,21 @@ void FrameBuffer::Generate()
|
||||
switch ((*it)->m_ResourceType) {
|
||||
case GL_TEXTURE_2D:
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, (*it)->m_Attachment, (*it)->m_ResourceType, *(*it)->m_ResourceHandle, 0);
|
||||
GLERROR("FrameBuffer generate: glFramebufferTexture2D");
|
||||
|
||||
break;
|
||||
case GL_RENDERBUFFER:
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, (*it)->m_Attachment, (*it)->m_ResourceType, *(*it)->m_ResourceHandle);
|
||||
GLERROR("FrameBuffer generate: glFramebufferRenderbuffer");
|
||||
if ( (*it)->m_Attachment != GL_COLOR_ATTACHMENT0 ||
|
||||
(*it)->m_Attachment != GL_DEPTH_ATTACHMENT ||
|
||||
(*it)->m_Attachment != GL_STENCIL_ATTACHMENT)
|
||||
{
|
||||
LOG_ERROR("RenderBuffer Attachment not valid.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
GLERROR("FrameBuffer generate");
|
||||
|
||||
if ((*it)->m_Attachment != GL_DEPTH_ATTACHMENT) {
|
||||
attachments.push_back((*it)->m_Attachment);
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#include "Rendering/ImGuiRenderPass.h"
|
||||
|
||||
ImGuiRenderPass::ImGuiRenderPass(IRenderer* renderer, EventBroker* eventBroker)
|
||||
: m_Renderer(renderer)
|
||||
, m_EventBroker(eventBroker)
|
||||
{
|
||||
g_Window = renderer->Window();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
|
||||
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
|
||||
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
|
||||
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
|
||||
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
|
||||
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
|
||||
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
|
||||
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
|
||||
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
|
||||
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.Alpha = 1.f;
|
||||
style.WindowPadding = ImVec2(8.f, 7.f);
|
||||
style.WindowRounding = 4.f;
|
||||
style.ChildWindowRounding = 0.f;
|
||||
style.FramePadding = ImVec2(4.f, 2.f);
|
||||
style.FrameRounding = 2.f;
|
||||
style.ItemSpacing = ImVec2(6.f, 2.f);
|
||||
style.ItemInnerSpacing = ImVec2(3.f, 4.f);
|
||||
style.IndentSpacing = 16.f;
|
||||
style.ScrollbarSize = 12;
|
||||
style.ScrollbarRounding = 2.f;
|
||||
style.GrabMinSize = 13.f;
|
||||
style.GrabRounding = 3.f;
|
||||
|
||||
createDeviceObjects();
|
||||
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &ImGuiRenderPass::OnMousePress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &ImGuiRenderPass::OnMouseRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &ImGuiRenderPass::OnMouseMove);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseScroll, &ImGuiRenderPass::OnMouseScroll);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyDown, &ImGuiRenderPass::OnKeyDown);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &ImGuiRenderPass::OnKeyUp);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EKeyboardChar, &ImGuiRenderPass::OnKeyboardChar);
|
||||
|
||||
// Prime the first frame
|
||||
newFrame();
|
||||
}
|
||||
|
||||
void ImGuiRenderPass::Update(double dt)
|
||||
{
|
||||
g_DeltaTime = dt;
|
||||
}
|
||||
|
||||
void ImGuiRenderPass::Draw()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
|
||||
// Set up render state
|
||||
ImGuiRenderState state;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays)
|
||||
float fb_height = io.DisplaySize.y * io.DisplayFramebufferScale.y;
|
||||
draw_data->ScaleClipRects(io.DisplayFramebufferScale);
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
glViewport(0, 0, (GLsizei)io.DisplaySize.x, (GLsizei)io.DisplaySize.y);
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ -1.0f, 1.0f, 0.0f, 1.0f },
|
||||
};
|
||||
glUseProgram(g_ShaderHandle);
|
||||
glUniform1i(g_AttribLocationTex, 0);
|
||||
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
glBindVertexArray(g_VaoHandle);
|
||||
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++) {
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
const ImDrawIdx* idx_buffer_offset = 0;
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
|
||||
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.size() * sizeof(ImDrawVert), (GLvoid*)&cmd_list->VtxBuffer.front(), GL_STREAM_DRAW);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.size() * sizeof(ImDrawIdx), (GLvoid*)&cmd_list->IdxBuffer.front(), GL_STREAM_DRAW);
|
||||
|
||||
for (const ImDrawCmd* pcmd = cmd_list->CmdBuffer.begin(); pcmd != cmd_list->CmdBuffer.end(); pcmd++) {
|
||||
if (pcmd->UserCallback) {
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
|
||||
glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
|
||||
}
|
||||
idx_buffer_offset += pcmd->ElemCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Start next frame
|
||||
newFrame();
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnMousePress(const Events::MousePress& e)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MouseDown[e.Button] = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnMouseRelease(const Events::MouseRelease& e)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MouseDown[e.Button] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnMouseMove(const Events::MouseMove& e)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MousePos.x = e.X;
|
||||
io.MousePos.y = e.Y;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnMouseScroll(const Events::MouseScroll& e)
|
||||
{
|
||||
g_MouseWheel += (float)e.DeltaY;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnKeyDown(const Events::KeyDown& e)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.KeysDown[e.KeyCode] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnKeyUp(const Events::KeyUp& e)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.KeysDown[e.KeyCode] = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::OnKeyboardChar(const Events::KeyboardChar& e)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (e.Char > 0 && e.Char < 0x10000) {
|
||||
io.AddInputCharacter((unsigned short)e.Char);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::createDeviceObjects()
|
||||
{
|
||||
// Backup GL state
|
||||
GLint last_texture, last_array_buffer, last_vertex_array;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||
|
||||
const GLchar *vertex_shader =
|
||||
"#version 330\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"in vec2 Position;\n"
|
||||
"in vec2 UV;\n"
|
||||
"in vec4 Color;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader =
|
||||
"#version 330\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
g_ShaderHandle = glCreateProgram();
|
||||
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(g_VertHandle, 1, &vertex_shader, 0);
|
||||
glShaderSource(g_FragHandle, 1, &fragment_shader, 0);
|
||||
glCompileShader(g_VertHandle);
|
||||
glCompileShader(g_FragHandle);
|
||||
glAttachShader(g_ShaderHandle, g_VertHandle);
|
||||
glAttachShader(g_ShaderHandle, g_FragHandle);
|
||||
glLinkProgram(g_ShaderHandle);
|
||||
|
||||
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
|
||||
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
|
||||
g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
|
||||
g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
|
||||
g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
|
||||
|
||||
glGenBuffers(1, &g_VboHandle);
|
||||
glGenBuffers(1, &g_ElementsHandle);
|
||||
|
||||
glGenVertexArrays(1, &g_VaoHandle);
|
||||
glBindVertexArray(g_VaoHandle);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
|
||||
glEnableVertexAttribArray(g_AttribLocationPosition);
|
||||
glEnableVertexAttribArray(g_AttribLocationUV);
|
||||
glEnableVertexAttribArray(g_AttribLocationColor);
|
||||
|
||||
#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
|
||||
glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, uv));
|
||||
glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, col));
|
||||
#undef OFFSETOF
|
||||
|
||||
createFontsTexture();
|
||||
|
||||
// Restore modified GL state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
glBindVertexArray(last_vertex_array);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGuiRenderPass::createFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
io.Fonts->AddFontFromFileTTF("Fonts/DroidSans.ttf", 13.f);
|
||||
//io.Fonts->AddFontFromFileTTF("Fonts/ProggyClean.ttf", 13.f);
|
||||
//io.Fonts->AddFontFromFileTTF("Fonts/ProggyTiny.ttf", 10.f);
|
||||
//io.Fonts->AddFontFromFileTTF("Fonts/Karla-Regular.ttf", 15.0f);
|
||||
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader.
|
||||
|
||||
// Upload texture to graphics system
|
||||
GLint last_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGenTextures(1, &g_FontTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (void *)(intptr_t)g_FontTexture;
|
||||
|
||||
// Restore state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiRenderPass::newFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
int w, h;
|
||||
int display_w, display_h;
|
||||
glfwGetWindowSize(g_Window, &w, &h);
|
||||
glfwGetFramebufferSize(g_Window, &display_w, &display_h);
|
||||
io.DisplaySize = ImVec2((float)w, (float)h);
|
||||
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
|
||||
|
||||
io.DeltaTime = g_DeltaTime;
|
||||
|
||||
io.KeyCtrl = glfwGetKey(g_Window, GLFW_KEY_LEFT_CONTROL) || glfwGetKey(g_Window, GLFW_KEY_RIGHT_CONTROL);
|
||||
io.KeyShift = glfwGetKey(g_Window, GLFW_KEY_LEFT_SHIFT) || glfwGetKey(g_Window, GLFW_KEY_RIGHT_SHIFT);
|
||||
io.KeyAlt = glfwGetKey(g_Window, GLFW_KEY_LEFT_ALT) || glfwGetKey(g_Window, GLFW_KEY_RIGHT_ALT);
|
||||
|
||||
io.MouseWheel = g_MouseWheel;
|
||||
g_MouseWheel = 0;
|
||||
|
||||
m_EventBroker->Process<ImGuiRenderPass>();
|
||||
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "Rendering/PickingPass.h"
|
||||
|
||||
PickingPass::PickingPass(IRenderer* renderer, EventBroker* eb)
|
||||
{
|
||||
m_Renderer = renderer;
|
||||
m_EventBroker = eb;
|
||||
|
||||
InitializeTextures();
|
||||
InitializeFrameBuffers();
|
||||
InitializeShaderPrograms();
|
||||
}
|
||||
|
||||
PickingPass::~PickingPass()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PickingPass::InitializeTextures()
|
||||
{
|
||||
GenerateTexture(&m_PickingTexture, GL_CLAMP_TO_BORDER, GL_LINEAR,
|
||||
glm::vec2(m_Renderer->Resolution().Width, m_Renderer->Resolution().Height), GL_RG8, GL_RG, GL_UNSIGNED_BYTE);
|
||||
}
|
||||
|
||||
void PickingPass::InitializeFrameBuffers()
|
||||
{
|
||||
glGenRenderbuffers(1, &m_DepthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_Renderer->Resolution().Width, m_Renderer->Resolution().Height);
|
||||
|
||||
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new RenderBuffer(&m_DepthBuffer, GL_DEPTH_ATTACHMENT)));
|
||||
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_PickingTexture, GL_COLOR_ATTACHMENT0)));
|
||||
m_PickingBuffer.Generate();
|
||||
}
|
||||
|
||||
void PickingPass::InitializeShaderPrograms()
|
||||
{
|
||||
m_PickingProgram = ResourceManager::Load<ShaderProgram>("#PickingProgram");
|
||||
|
||||
m_PickingProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Picking.vert.glsl")));
|
||||
m_PickingProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Picking.frag.glsl")));
|
||||
m_PickingProgram->Compile();
|
||||
m_PickingProgram->BindFragDataLocation(0, "TextureFragment");
|
||||
m_PickingProgram->Link();
|
||||
}
|
||||
|
||||
void PickingPass::Draw(RenderScene& scene)
|
||||
{
|
||||
PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle());
|
||||
|
||||
|
||||
//TODO: Render: Add code for more jobs than modeljobs.
|
||||
|
||||
GLuint ShaderHandle = m_PickingProgram->GetHandle();
|
||||
m_PickingProgram->Bind();
|
||||
|
||||
|
||||
|
||||
m_Camera = scene.Camera;
|
||||
|
||||
for (auto &job : scene.ForwardJobs) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
|
||||
if (modelJob) {
|
||||
int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] };
|
||||
|
||||
PickingInfo pickInfo;
|
||||
pickInfo.Entity = modelJob->Entity;
|
||||
pickInfo.World = modelJob->World;
|
||||
pickInfo.Camera = scene.Camera;
|
||||
|
||||
auto color = m_EntityColors.find(std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera));
|
||||
if (color != m_EntityColors.end()) {
|
||||
pickColor[0] = color->second[0];
|
||||
pickColor[1] = color->second[1];
|
||||
} else {
|
||||
m_EntityColors[std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)] = glm::ivec2(pickColor[0], pickColor[1]);
|
||||
if (m_ColorCounter[0] > 255) {
|
||||
m_ColorCounter[0] = 0;
|
||||
m_ColorCounter[1]++;;
|
||||
} else {
|
||||
m_ColorCounter[0]++;;
|
||||
}
|
||||
}
|
||||
|
||||
m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo;
|
||||
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
|
||||
glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
|
||||
|
||||
glBindVertexArray(modelJob->Model->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, nullptr, modelJob->StartIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_PickingBuffer.Unbind();
|
||||
GLERROR("PickingPass Error");
|
||||
|
||||
|
||||
delete state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PickingPass::ClearPicking()
|
||||
{
|
||||
m_PickingColorsToEntity.clear();
|
||||
m_EntityColors.clear();
|
||||
m_ColorCounter[0] = 0;
|
||||
m_ColorCounter[1] = 0;
|
||||
|
||||
m_PickingBuffer.Bind();
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_PickingBuffer.Unbind();
|
||||
}
|
||||
|
||||
PickData PickingPass::Pick(glm::vec2 screenCoord)
|
||||
{
|
||||
int fbWidth;
|
||||
int fbHeight;
|
||||
glfwGetFramebufferSize(m_Renderer->Window(), &fbWidth, &fbHeight);
|
||||
|
||||
Rectangle resolution = Rectangle(fbWidth, fbHeight);
|
||||
PickData pickData;
|
||||
// Invert screen y coordinate
|
||||
screenCoord.y = resolution.Height - screenCoord.y;
|
||||
ScreenCoords::PixelData data = ScreenCoords::ToPixelData(screenCoord, &m_PickingBuffer, m_DepthBuffer);
|
||||
pickData.Depth = data.Depth;
|
||||
|
||||
PickingInfo pickInfo;
|
||||
|
||||
auto it = m_PickingColorsToEntity.find(glm::ivec2(data.Color[0], data.Color[1]));
|
||||
if (it != m_PickingColorsToEntity.end()) {
|
||||
pickInfo = it->second;
|
||||
} else {
|
||||
pickData.Entity = EntityID_Invalid;
|
||||
}
|
||||
pickData.Position = ScreenCoords::ToWorldPos(screenCoord.x, screenCoord.y, data.Depth, resolution, pickInfo.Camera->ProjectionMatrix(), pickInfo.Camera->ViewMatrix());
|
||||
|
||||
|
||||
pickData.Entity = pickInfo.Entity;
|
||||
pickData.Camera = pickInfo.Camera;
|
||||
pickData.World = pickInfo.World;
|
||||
return pickData;
|
||||
}
|
||||
|
||||
void PickingPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const
|
||||
{
|
||||
//TODO: Renderer: Make this in a sparate class
|
||||
glGenTextures(1, texture);
|
||||
glBindTexture(GL_TEXTURE_2D, *texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapping);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapping);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, dimensions.x, dimensions.y, 0, format, type, nullptr);//TODO: Renderer: Fix the precision and Resolution
|
||||
GLERROR("Texture initialization failed");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "Rendering/PickingPassState.h"
|
||||
|
||||
|
||||
PickingPassState::PickingPassState(GLuint frameBuffer)
|
||||
{
|
||||
GLERROR("---2");
|
||||
BindFramebuffer(frameBuffer);
|
||||
GLERROR("---3");
|
||||
Enable(GL_DEPTH_TEST);
|
||||
Enable(GL_CULL_FACE);
|
||||
|
||||
glm::vec4 clearColor = glm::vec4(0.f);
|
||||
//ClearColor(clearColor);
|
||||
//Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
PickingPassState::~PickingPassState()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ RawModel::RawModel(std::string fileName)
|
||||
if (scene == nullptr) {
|
||||
LOG_ERROR("Failed to load model \"%s\"", fileName.c_str());
|
||||
LOG_ERROR("Assimp error: %s", importer.GetErrorString());
|
||||
return;
|
||||
throw std::runtime_error("Failed to open model file.");
|
||||
}
|
||||
|
||||
auto m = scene->mRootNode->mTransformation;
|
||||
@@ -76,13 +76,18 @@ RawModel::RawModel(std::string fileName)
|
||||
}
|
||||
|
||||
// Material diffuse color
|
||||
aiColor4D diffuse;
|
||||
aiColor3D diffuse;
|
||||
material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);
|
||||
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, diffuse.a);
|
||||
float opacity;
|
||||
material->Get(AI_MATKEY_OPACITY, opacity);
|
||||
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity);
|
||||
|
||||
desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity);
|
||||
|
||||
// Material specular color
|
||||
aiColor4D specular;
|
||||
aiColor3D specular;
|
||||
material->Get(AI_MATKEY_COLOR_SPECULAR, specular);
|
||||
desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, specular.a);
|
||||
desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, 1.f);
|
||||
|
||||
m_Vertices.push_back(desc);
|
||||
}
|
||||
@@ -132,6 +137,7 @@ RawModel::RawModel(std::string fileName)
|
||||
matGroup.EndIndex = m_Indices.size() - 1;
|
||||
// Material shininess
|
||||
material->Get(AI_MATKEY_SHININESS, matGroup.Shininess);
|
||||
material->Get(AI_MATKEY_OPACITY, matGroup.Transparency);
|
||||
//LOG_DEBUG("Shininess: %f", matGroup.Shininess);
|
||||
// Diffuse texture
|
||||
//LOG_DEBUG("%i diffuse textures found", material->GetTextureCount(aiTextureType_DIFFUSE));
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
#include "Rendering/RenderQueueFactory.h"
|
||||
|
||||
|
||||
RenderQueueFactory::RenderQueueFactory()
|
||||
{
|
||||
m_RenderQueues = RenderQueueCollection();
|
||||
}
|
||||
|
||||
void RenderQueueFactory::Update(World* world)
|
||||
{
|
||||
m_RenderQueues.Clear();
|
||||
FillModels(world, &m_RenderQueues.Forward);
|
||||
FillLights(world, &m_RenderQueues.Lights);
|
||||
FillText(world, &m_RenderQueues.Text);
|
||||
}
|
||||
|
||||
glm::mat4 RenderQueueFactory::ModelMatrix(World* world, EntityID entity)
|
||||
{
|
||||
glm::vec3 position = AbsolutePosition(world, entity);
|
||||
glm::quat orientation = AbsoluteOrientation(world, entity);
|
||||
glm::vec3 scale = AbsoluteScale(world, entity);
|
||||
|
||||
glm::mat4 modelMatrix = glm::translate(glm::mat4(), position) * glm::toMat4(orientation) * glm::scale(scale);
|
||||
return modelMatrix;
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 RenderQueueFactory::AbsolutePosition(World* world, EntityID entity)
|
||||
{
|
||||
glm::vec3 position;
|
||||
|
||||
do {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
position += AbsoluteOrientation(world, entity) * (glm::vec3)transform["Position"];
|
||||
entity = world->GetParent(entity);
|
||||
} while (entity != 0);
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
glm::quat RenderQueueFactory::AbsoluteOrientation(World* world, EntityID entity)
|
||||
{
|
||||
glm::quat orientation;
|
||||
|
||||
do {
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
orientation = glm::quat((glm::vec3)transform["Orientation"]) * orientation;
|
||||
entity = world->GetParent(entity);
|
||||
} while (entity != 0);
|
||||
|
||||
return orientation;
|
||||
}
|
||||
|
||||
glm::vec3 RenderQueueFactory::AbsoluteScale(World* world, EntityID entity)
|
||||
{
|
||||
ComponentWrapper transform = world->GetComponent(entity, "Transform");
|
||||
glm::vec3 scale = (glm::vec3)transform["Scale"];
|
||||
|
||||
EntityID parent = world->GetParent(entity);
|
||||
if (parent != 0) {
|
||||
return AbsoluteScale(world, parent) * scale;
|
||||
} else {
|
||||
return scale;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderQueueFactory::FillModels(World* world, RenderQueue* renderQueue)
|
||||
{
|
||||
auto models = world->GetComponents("Model");
|
||||
if (models == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& modelC : *models) {
|
||||
|
||||
|
||||
|
||||
std::string resource = modelC["Resource"];
|
||||
if (resource.empty()) {
|
||||
continue;
|
||||
}
|
||||
glm::vec4 color = modelC["Color"];
|
||||
Model* model = ResourceManager::Load<Model>(resource);
|
||||
|
||||
for (auto texGroup : model->TextureGroups) {
|
||||
ModelJob job;
|
||||
job.TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0;
|
||||
job.DiffuseTexture = texGroup.Texture.get();
|
||||
job.NormalTexture = texGroup.NormalMap.get();
|
||||
job.SpecularTexture = texGroup.SpecularMap.get();
|
||||
job.Model = model;
|
||||
job.StartIndex = texGroup.StartIndex;
|
||||
job.EndIndex = texGroup.EndIndex;
|
||||
job.ModelMatrix = model->m_Matrix * ModelMatrix(world, modelC.EntityID);
|
||||
job.Color = color;
|
||||
|
||||
//TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this
|
||||
job.Entity = modelC.EntityID;
|
||||
|
||||
renderQueue->Add(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderQueueFactory::FillLights(World* world, RenderQueue* renderQueue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RenderQueueFactory::FillText(World* world, RenderQueue* renderQueue)
|
||||
{
|
||||
auto texts = world->GetComponents("Text");
|
||||
if (texts == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& textC : *texts) {
|
||||
|
||||
|
||||
std::string resource = textC["Resource"];
|
||||
if (resource.empty()) {
|
||||
continue;
|
||||
}
|
||||
Font* font = ResourceManager::Load<Font>(resource);
|
||||
|
||||
glm::vec4 color = textC["Color"];
|
||||
std::string content = textC["Content"];
|
||||
|
||||
TextJob job;
|
||||
job.Color = color;
|
||||
job.Content = content;
|
||||
job.Resource = font;
|
||||
job.ModelMatrix = ModelMatrix(world, textC.EntityID);
|
||||
|
||||
renderQueue->Add(job);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "Rendering/RenderState.h"
|
||||
|
||||
bool RenderState::Enable(GLenum cap)
|
||||
{
|
||||
if (glIsEnabled(cap)) {
|
||||
return false;
|
||||
}
|
||||
m_ResetFunctions.push_back(std::bind(glDisable, cap));
|
||||
glEnable(cap);
|
||||
return !GLERROR("RenderState::Enable");
|
||||
}
|
||||
|
||||
bool RenderState::Disable(GLenum cap)
|
||||
{
|
||||
if (!glIsEnabled(cap)) {
|
||||
return false;
|
||||
}
|
||||
m_ResetFunctions.push_back(std::bind(glEnable, cap));
|
||||
glDisable(cap);
|
||||
return !GLERROR("RenderState::Disable");
|
||||
}
|
||||
|
||||
bool RenderState::CullFace(GLenum mode)
|
||||
{
|
||||
if (!glIsEnabled(GL_CULL_FACE)) {
|
||||
LOG_ERROR("Setting GL_CULL_FACE without enabling it.");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLint original;
|
||||
glGetIntegerv(GL_CULL_FACE_MODE, &original);
|
||||
m_ResetFunctions.push_back(std::bind(glCullFace, original));
|
||||
glCullFace(mode);
|
||||
return !GLERROR("RenderState::CullFace");
|
||||
}
|
||||
|
||||
bool RenderState::ClearColor(glm::vec4 color)
|
||||
{
|
||||
GLfloat original[4];
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, &original[0]);
|
||||
m_ResetFunctions.push_back(std::bind(glClearColor, original[0], original[1], original[2], original[3]));
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
return !GLERROR("RenderState::ClearColor");
|
||||
}
|
||||
|
||||
bool RenderState::Clear(GLbitfield mask)
|
||||
{
|
||||
glClear(mask);
|
||||
return !GLERROR("RenderState::Clear");
|
||||
}
|
||||
|
||||
bool RenderState::BindFramebuffer(GLint framebuffer)
|
||||
{
|
||||
GLint originalRead;
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &originalRead);
|
||||
GLint originalDraw;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &originalDraw);
|
||||
m_ResetFunctions.push_back([originalRead, originalDraw]() {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, originalRead);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, originalDraw);
|
||||
});
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
return !GLERROR("RenderState::BindBuffer");
|
||||
}
|
||||
|
||||
|
||||
bool RenderState::BlendEquation(GLenum mode)
|
||||
{
|
||||
GLint originalRGB;
|
||||
glGetIntegerv(GL_BLEND_EQUATION_RGB, &originalRGB);
|
||||
GLint originalAlpha;
|
||||
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &originalAlpha);
|
||||
m_ResetFunctions.push_back(std::bind(glBlendEquationSeparate, originalRGB, originalAlpha));
|
||||
glBlendEquation(mode);
|
||||
return !GLERROR("RenderState::BlendEquation");
|
||||
}
|
||||
|
||||
bool RenderState::BlendFunc(GLenum sfactor, GLenum dfactor)
|
||||
{
|
||||
GLint originalSrcRGB;
|
||||
glGetIntegerv(GL_BLEND_SRC_RGB, &originalSrcRGB);
|
||||
GLint originalSrcAlpha;
|
||||
glGetIntegerv(GL_BLEND_SRC_ALPHA, &originalSrcAlpha);
|
||||
GLint originalDestRGB;
|
||||
glGetIntegerv(GL_BLEND_DST_RGB, &originalDestRGB);
|
||||
GLint originalDestAlpha;
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &originalDestAlpha);
|
||||
m_ResetFunctions.push_back(std::bind(glBlendFuncSeparate, originalSrcRGB, originalSrcAlpha, originalDestRGB, originalDestAlpha));
|
||||
glBlendFunc(sfactor, dfactor);
|
||||
return !GLERROR("RenderState::BlendFunc");
|
||||
}
|
||||
|
||||
RenderState::~RenderState()
|
||||
{
|
||||
for (auto& f : m_ResetFunctions) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "Rendering/RenderSystem.h"
|
||||
#include "Rendering/DebugCameraInputController.h"
|
||||
|
||||
RenderSystem::RenderSystem(EventBroker* eventBrokerer, const IRenderer* renderer, RenderFrame* renderFrame) :ImpureSystem(eventBrokerer)
|
||||
{
|
||||
m_Renderer = renderer;
|
||||
m_RenderFrame = renderFrame;
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand);
|
||||
|
||||
m_DefaultCamera = new Camera((float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height, glm::radians(45.f), 0.01f, 5000.f);
|
||||
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10));
|
||||
if (m_Camera == nullptr) {
|
||||
m_Camera = m_DefaultCamera;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderSystem::OnSetCamera(const Events::SetCamera &event)
|
||||
{
|
||||
auto cameras = m_World->GetComponents("Camera");
|
||||
|
||||
if (cameras != nullptr) {
|
||||
for (auto it = cameras->begin(); it != cameras->end(); it++) {
|
||||
if ((std::string)(*it)["Name"] == event.Name) {
|
||||
switchCamera((*it).EntityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderSystem::switchCamera(EntityID entity)
|
||||
{
|
||||
if(m_World->HasComponent(entity, "Camera")) {
|
||||
|
||||
if (m_CurrentCamera != EntityID_Invalid) {
|
||||
if (m_World->HasComponent(m_CurrentCamera, "Model")) {
|
||||
m_World->GetComponent(m_CurrentCamera, "Model")["Visible"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_World->HasComponent(entity, "Model")) {
|
||||
m_World->GetComponent(entity, "Model")["Visible"] = false;
|
||||
}
|
||||
m_CurrentCamera = entity;
|
||||
m_SwitchCamera = false;
|
||||
|
||||
} else {
|
||||
LOG_ERROR("Entity %i does not have a CameraComponent", entity);
|
||||
m_SwitchCamera = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderSystem::updateProjectionMatrix(ComponentWrapper& cameraComponent)
|
||||
{
|
||||
double fov = cameraComponent["FOV"];
|
||||
double aspectRatio = m_Renderer->Resolution().Width / m_Renderer->Resolution().Height;
|
||||
double nearClip = cameraComponent["NearClip"];
|
||||
double farClip = cameraComponent["FarClip"];
|
||||
|
||||
double fovY = atan(tan(glm::radians(fov)/2.0) * aspectRatio) * 2.0;
|
||||
m_ProjectionMatrix = glm::perspective(fovY, aspectRatio, nearClip, farClip);
|
||||
|
||||
m_Camera->SetFOV(fovY);
|
||||
m_Camera->SetAspectRatio(aspectRatio);
|
||||
m_Camera->SetNearClip(nearClip);
|
||||
m_Camera->SetFarClip(farClip);
|
||||
m_Camera->UpdateProjectionMatrix();
|
||||
}
|
||||
|
||||
void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
|
||||
{
|
||||
auto models = world->GetComponents("Model");
|
||||
if (models == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& modelComponent : *models) {
|
||||
bool visible = modelComponent["Visible"];
|
||||
if (!visible) {
|
||||
continue;
|
||||
}
|
||||
std::string resource = modelComponent["Resource"];
|
||||
if (resource.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Model* model = ResourceManager::Load<::Model>(resource);
|
||||
if (model == nullptr) {
|
||||
model = ResourceManager::Load<::Model>("Models/Core/Error.obj");
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world);
|
||||
|
||||
for (auto texGroup : model->TextureGroups) {
|
||||
std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(model, m_Camera, modelMatrix, texGroup, modelComponent, world));
|
||||
jobs.push_back(modelJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RenderSystem::fillText(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
|
||||
{
|
||||
auto texts = world->GetComponents("Text");
|
||||
if (texts == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& textComponent : *texts) {
|
||||
bool visible = textComponent["Visible"];
|
||||
if (!visible) {
|
||||
continue;
|
||||
}
|
||||
std::string resource = textComponent["Resource"];
|
||||
if (resource.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Font* font = ResourceManager::Load<Font>(textComponent["Resource"]);
|
||||
if (font == nullptr) {
|
||||
font = ResourceManager::Load<Font>("Fonts/DroidSans.ttf");
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 modelMatrix = Transform::ModelMatrix(textComponent.EntityID, world);
|
||||
std::shared_ptr<TextJob> modelJob = std::shared_ptr<TextJob>(new TextJob(modelMatrix, font, textComponent));
|
||||
jobs.push_back(modelJob);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderSystem::OnInputCommand(const Events::InputCommand& e)
|
||||
{
|
||||
if (e.Command == "SwitchCamera" && e.Value > 0) {
|
||||
m_SwitchCamera = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderSystem::Update(World* world, double dt)
|
||||
{
|
||||
m_World = world;
|
||||
m_EventBroker->Process<RenderSystem>();
|
||||
|
||||
updateCamera(world, dt);
|
||||
|
||||
//Only supports opaque geometry atm
|
||||
m_RenderFrame->Clear();
|
||||
|
||||
RenderScene rs;
|
||||
rs.Camera = m_Camera;
|
||||
rs.Viewport = Rectangle(1280, 720);
|
||||
fillModels(rs.ForwardJobs, world);
|
||||
fillText(rs.TextJobs, world);
|
||||
m_RenderFrame->Add(rs);
|
||||
|
||||
}
|
||||
|
||||
void RenderSystem::updateCamera(World* world, double dt)
|
||||
{
|
||||
|
||||
static DebugCameraInputController<RenderSystem> firstPersonInputController(m_EventBroker, -1);
|
||||
|
||||
if (m_SwitchCamera) {
|
||||
auto cameras = world->GetComponents("Camera");
|
||||
for (auto it = cameras->begin(); it != cameras->end(); it++) {
|
||||
if ((*it).EntityID == m_CurrentCamera) {
|
||||
it++;
|
||||
if (it != cameras->end()) {
|
||||
switchCamera((*it).EntityID);
|
||||
} else {
|
||||
switchCamera((*cameras->begin()).EntityID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
|
||||
firstPersonInputController.SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
|
||||
firstPersonInputController.SetPosition(cameraTransform["Position"]);
|
||||
|
||||
}
|
||||
|
||||
if (m_World->ValidEntity(m_CurrentCamera)) {
|
||||
if (world->HasComponent(m_CurrentCamera, "Camera") && world->HasComponent(m_CurrentCamera, "Transform")) {
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
|
||||
firstPersonInputController.Update(dt);
|
||||
(glm::vec3&)cameraTransform["Orientation"] = glm::eulerAngles(firstPersonInputController.Orientation());
|
||||
(glm::vec3&)cameraTransform["Position"] = firstPersonInputController.Position();
|
||||
|
||||
glm::vec3 position = Transform::AbsolutePosition(world, m_CurrentCamera);
|
||||
glm::quat orientation = Transform::AbsoluteOrientation(world, m_CurrentCamera);
|
||||
|
||||
m_Camera->SetPosition(position);
|
||||
m_Camera->SetOrientation(orientation);
|
||||
|
||||
updateProjectionMatrix(cameraComponent);
|
||||
|
||||
}
|
||||
} else {
|
||||
m_Camera = m_DefaultCamera;
|
||||
|
||||
auto cameras = world->GetComponents("Camera");
|
||||
if (cameras != nullptr) {
|
||||
if (cameras->begin() != cameras->end()) {
|
||||
ComponentWrapper& cameraC = *cameras->begin();
|
||||
switchCamera(cameraC.EntityID);
|
||||
|
||||
ComponentWrapper& cameraComponent = world->GetComponent(m_CurrentCamera, "Camera");
|
||||
ComponentWrapper& cameraTransform = world->GetComponent(m_CurrentCamera, "Transform");
|
||||
|
||||
firstPersonInputController.SetOrientation(glm::quat((glm::vec3)cameraTransform["Orientation"]));
|
||||
firstPersonInputController.SetPosition(cameraTransform["Position"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Camera->UpdateViewMatrix();
|
||||
}
|
||||
|
||||
@@ -3,18 +3,12 @@
|
||||
void Renderer::Initialize()
|
||||
{
|
||||
InitializeWindow();
|
||||
// Create default camera
|
||||
m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f);
|
||||
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10));
|
||||
if (m_Camera == nullptr) {
|
||||
m_Camera = m_DefaultCamera;
|
||||
}
|
||||
|
||||
InitializeRenderPasses();
|
||||
|
||||
glfwSwapInterval(m_VSYNC);
|
||||
InitializeShaders();
|
||||
InitializeTextures();
|
||||
InitializeFrameBuffers();
|
||||
|
||||
m_TextRenderer = new TextRenderer();
|
||||
m_TextRenderer->Initialize();
|
||||
|
||||
@@ -22,6 +16,17 @@ void Renderer::Initialize()
|
||||
m_ScreenQuad = ResourceManager::Load<Model>("Models/Core/ScreenQuad.obj");
|
||||
m_UnitQuad = ResourceManager::Load<Model>("Models/Core/UnitQuad.obj");
|
||||
m_UnitSphere = ResourceManager::Load<Model>("Models/Core/UnitSphere.obj");
|
||||
|
||||
m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker);
|
||||
|
||||
|
||||
// Create default camera
|
||||
m_DefaultCamera = new ::Camera((float)m_Resolution.Width / m_Resolution.Height, glm::radians(45.f), 0.01f, 5000.f);
|
||||
m_DefaultCamera->SetPosition(glm::vec3(0, 0, 10));
|
||||
if (m_Camera == nullptr) {
|
||||
m_Camera = m_DefaultCamera;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Renderer::InitializeWindow()
|
||||
@@ -66,79 +71,18 @@ void Renderer::InitializeWindow()
|
||||
|
||||
void Renderer::InitializeShaders()
|
||||
{
|
||||
m_BasicForwardProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/BasicForward.vert.glsl")));
|
||||
m_BasicForwardProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/BasicForward.frag.glsl")));
|
||||
m_BasicForwardProgram.Compile();
|
||||
m_BasicForwardProgram.Link();
|
||||
|
||||
m_PickingProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Picking.vert.glsl")));
|
||||
m_PickingProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Picking.frag.glsl")));
|
||||
m_PickingProgram.Compile();
|
||||
m_PickingProgram.BindFragDataLocation(0, "TextureFragment");
|
||||
m_PickingProgram.Link();
|
||||
|
||||
m_DrawScreenQuadProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/DrawScreenQuad.vert.glsl")));
|
||||
m_DrawScreenQuadProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/DrawScreenQuad.frag.glsl")));
|
||||
m_DrawScreenQuadProgram.Compile();
|
||||
m_DrawScreenQuadProgram.Link();
|
||||
m_BasicForwardProgram = ResourceManager::Load<ShaderProgram>("#m_BasicForwardProgram");
|
||||
|
||||
m_DrawScreenQuadProgram = ResourceManager::Load<ShaderProgram>("#DrawScreenQuadProgram");
|
||||
m_DrawScreenQuadProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/DrawScreenQuad.vert.glsl")));
|
||||
m_DrawScreenQuadProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/DrawScreenQuad.frag.glsl")));
|
||||
m_DrawScreenQuadProgram->Compile();
|
||||
m_DrawScreenQuadProgram->Link();
|
||||
}
|
||||
|
||||
void Renderer::InputUpdate(double dt)
|
||||
{
|
||||
glm::vec3 m_Position = m_Camera->Position();
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_O) == GLFW_PRESS)
|
||||
{
|
||||
m_Position = glm::vec3(0.f, 0.f, 5.f);
|
||||
}
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_W) == GLFW_PRESS)
|
||||
{
|
||||
m_Position += m_Camera->Forward() * m_CameraMoveSpeed * (float)dt;
|
||||
}
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_S) == GLFW_PRESS)
|
||||
{
|
||||
m_Position -= m_Camera->Forward() * m_CameraMoveSpeed * (float)dt;
|
||||
}
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_D) == GLFW_PRESS)
|
||||
{
|
||||
m_Position += m_Camera->Right() * m_CameraMoveSpeed * (float)dt;
|
||||
}
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_A) == GLFW_PRESS)
|
||||
{
|
||||
m_Position -= m_Camera->Right() * m_CameraMoveSpeed * (float)dt;
|
||||
}
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
|
||||
{
|
||||
m_CameraMoveSpeed = 5.f;
|
||||
}
|
||||
else {
|
||||
m_CameraMoveSpeed = 0.5f;
|
||||
}
|
||||
|
||||
|
||||
static double mousePosX, mousePosY;
|
||||
glfwGetCursorPos(m_Window, &mousePosX, &mousePosY);
|
||||
|
||||
if (glfwGetKey(m_Window, GLFW_KEY_SPACE) == GLFW_PRESS) {
|
||||
|
||||
|
||||
double deltaX, deltaY;
|
||||
deltaX = mousePosX - (float)Resolution().Width / 2;
|
||||
deltaY = mousePosY - (float)Resolution().Height / 2;
|
||||
|
||||
float rotationY = -deltaY / 300.f;
|
||||
float rotationX = -deltaX / 300.f;
|
||||
glm::quat orientation = m_Camera->Orientation();
|
||||
|
||||
|
||||
orientation = orientation * glm::angleAxis<float>(rotationY, glm::vec3(1, 0, 0));
|
||||
orientation = glm::angleAxis<float>(rotationX, glm::vec3(0, 1, 0)) * orientation;
|
||||
|
||||
m_Camera->SetOrientation(orientation);
|
||||
|
||||
glfwSetCursorPos(m_Window, Resolution().Width / 2, Resolution().Height / 2);
|
||||
}
|
||||
m_Camera->SetPosition(m_Position);
|
||||
|
||||
}
|
||||
|
||||
void Renderer::Update(double dt)
|
||||
@@ -146,136 +90,37 @@ void Renderer::Update(double dt)
|
||||
m_EventBroker->Process<Renderer>();
|
||||
InputUpdate(dt);
|
||||
m_TextRenderer->Update();
|
||||
|
||||
m_ImGuiRenderPass->Update(dt);
|
||||
}
|
||||
|
||||
void Renderer::Draw(RenderQueueCollection& rq)
|
||||
void Renderer::Draw(RenderFrame& frame)
|
||||
{
|
||||
//TODO: Renderer: Kanske borde vara längst upp i update.
|
||||
PickingPass(rq);
|
||||
// DrawScreenQuad(m_PickingTexture);
|
||||
|
||||
DrawScene(rq);
|
||||
m_TextRenderer->Draw(rq.Text,m_Camera->ProjectionMatrix(), m_Camera->ViewMatrix());
|
||||
glfwSwapBuffers(m_Window);
|
||||
}
|
||||
|
||||
void Renderer::DrawScene(RenderQueueCollection& rq)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
//TODO: Render: Clean up draw code
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
glClearColor(255.f / 255, 163.f / 255, 176.f / 255, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//TODO: Render: Add code for more jobs than modeljobs.
|
||||
for (auto &job : rq.Forward) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
if (modelJob) {
|
||||
GLuint ShaderHandle = m_BasicForwardProgram.GetHandle();
|
||||
m_PickingPass->ClearPicking();
|
||||
for (auto scene : frame.RenderScenes){
|
||||
m_Camera = scene->Camera; // remove renderer camera when Editor uses the render scene cameras.
|
||||
m_PickingPass->Draw(*scene);
|
||||
|
||||
m_BasicForwardProgram.Bind();
|
||||
//TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->ModelMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(m_Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(m_Camera->ProjectionMatrix()));
|
||||
glUniform4fv(glGetUniformLocation(ShaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color));
|
||||
|
||||
|
||||
//TODO: Renderer: bättre textur felhantering samt fler texturer stöd
|
||||
if (modelJob->DiffuseTexture != nullptr) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, modelJob->DiffuseTexture->m_Texture);
|
||||
} else {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, m_WhiteTexture->m_Texture);
|
||||
}
|
||||
m_DrawScenePass->Draw(*scene);
|
||||
GLERROR("Renderer::Draw m_DrawScenePass->Draw");
|
||||
|
||||
glBindVertexArray(modelJob->Model->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex);
|
||||
m_TextRenderer->Draw(*scene);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
GLERROR("DrawScene Error");
|
||||
|
||||
m_ImGuiRenderPass->Draw();
|
||||
glfwSwapBuffers(m_Window);
|
||||
}
|
||||
|
||||
void Renderer::PickingPass(RenderQueueCollection& rq)
|
||||
PickData Renderer::Pick(glm::vec2 screenCoord)
|
||||
{
|
||||
m_PickingColorsToEntity.clear();
|
||||
m_PickingBuffer.Bind();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
int r = 1;
|
||||
int g = 0;
|
||||
//TODO: Render: Add code for more jobs than modeljobs.
|
||||
|
||||
|
||||
GLuint ShaderHandle = m_PickingProgram.GetHandle();
|
||||
m_PickingProgram.Bind();
|
||||
|
||||
for (auto &job : rq.Forward) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
|
||||
if (modelJob) {
|
||||
//---------------
|
||||
//TODO: Renderer: IMPORTANT: Fixa detta så det inte loopar igenom listan varje frame.
|
||||
//---------------
|
||||
int pickColor[2] = { r, g };
|
||||
for (auto i : m_PickingColorsToEntity) {
|
||||
if(modelJob->Entity == i.second) {
|
||||
pickColor[0] = i.first.x;
|
||||
pickColor[1] = i.first.y;
|
||||
r -= 1;
|
||||
}
|
||||
}
|
||||
m_PickingColorsToEntity[glm::vec2(pickColor[0], pickColor[1])] = modelJob->Entity;
|
||||
|
||||
|
||||
//Render picking stuff
|
||||
//TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->ModelMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(m_Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(m_Camera->ProjectionMatrix()));
|
||||
glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
|
||||
|
||||
glBindVertexArray(modelJob->Model->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex);
|
||||
r += 1;
|
||||
if(r > 255) {
|
||||
r = 0;
|
||||
g += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_PickingBuffer.Unbind();
|
||||
GLERROR("PickingPass Error");
|
||||
|
||||
//Publish pick event every frame with the pick data that can be picked by the event
|
||||
Events::Picking pickEvent = Events::Picking(
|
||||
&m_PickingBuffer,
|
||||
&m_DepthBuffer,
|
||||
m_Camera->ProjectionMatrix(),
|
||||
m_Camera->ViewMatrix(),
|
||||
m_Resolution,
|
||||
&m_PickingColorsToEntity);
|
||||
|
||||
m_EventBroker->Publish(pickEvent);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
return m_PickingPass->Pick(screenCoord);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::DrawScreenQuad(GLuint textureToDraw)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
@@ -287,7 +132,7 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
|
||||
m_DrawScreenQuadProgram.Bind();
|
||||
m_DrawScreenQuadProgram->Bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, textureToDraw);
|
||||
|
||||
@@ -297,24 +142,10 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups[0].StartIndex);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::InitializeTextures()
|
||||
{
|
||||
m_ErrorTexture=ResourceManager::Load<Texture>("Textures/Core/ErrorTexture.png");
|
||||
m_WhiteTexture=ResourceManager::Load<Texture>("Textures/Core/Blank.png");
|
||||
/*
|
||||
glGenTextures(1, &m_PickingTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_PickingTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, m_Resolution.Width, m_Resolution.Height, 0, GL_RG, GL_FLOAT, NULL);//TODO: Renderer: Fix the precision and Resolution
|
||||
GLERROR("m_PickingTexture initialization failed");
|
||||
*/
|
||||
|
||||
GenerateTexture(&m_PickingTexture, GL_CLAMP_TO_BORDER, GL_LINEAR,
|
||||
glm::vec2(m_Resolution.Width, m_Resolution.Height), GL_RG8, GL_RG, GL_UNSIGNED_BYTE);
|
||||
}
|
||||
|
||||
void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type)
|
||||
@@ -329,15 +160,8 @@ void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filterin
|
||||
GLERROR("Texture initialization failed");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Renderer::InitializeFrameBuffers()//TODO: Renderer: Get this to a better location, as its really big
|
||||
void Renderer::InitializeRenderPasses()
|
||||
{
|
||||
glGenRenderbuffers(1, &m_DepthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_Resolution.Width, m_Resolution.Height);
|
||||
|
||||
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new RenderBuffer(&m_DepthBuffer, GL_DEPTH_ATTACHMENT)));
|
||||
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_PickingTexture, GL_COLOR_ATTACHMENT0)));
|
||||
m_PickingBuffer.Generate();
|
||||
}
|
||||
m_DrawScenePass = new DrawScenePass(this);
|
||||
m_PickingPass = new PickingPass(this, m_EventBroker);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,11 @@ void TextRenderer::Initialize()
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
m_TextProgram.AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Text.vert.glsl")));
|
||||
m_TextProgram.AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Text.frag.glsl")));
|
||||
m_TextProgram.Compile();
|
||||
m_TextProgram.Link();
|
||||
m_TextProgram = ResourceManager::Load<ShaderProgram>("#TextProgram");
|
||||
m_TextProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Text.vert.glsl")));
|
||||
m_TextProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Text.frag.glsl")));
|
||||
m_TextProgram->Compile();
|
||||
m_TextProgram->Link();
|
||||
}
|
||||
|
||||
void TextRenderer::Update()
|
||||
@@ -28,12 +29,12 @@ void TextRenderer::Update()
|
||||
|
||||
}
|
||||
|
||||
void TextRenderer::Draw(RenderQueue &rq, glm::mat4 projection, glm::mat4 view)
|
||||
void TextRenderer::Draw(RenderScene& scene)
|
||||
{
|
||||
for (auto &job : rq) {
|
||||
for (auto &job : scene.TextJobs) {
|
||||
auto textJob = std::dynamic_pointer_cast<TextJob>(job);
|
||||
if (textJob) {
|
||||
RenderText(textJob->Content, textJob->Resource, 0.01f, textJob->Color, textJob->ModelMatrix, projection, view);
|
||||
RenderText(textJob->Content, textJob->Resource, 0.01f, textJob->Color, textJob->Matrix, scene.Camera->ProjectionMatrix(), scene.Camera->ViewMatrix());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,11 +51,11 @@ void TextRenderer::RenderText(std::string text, Font* font, GLfloat scale, glm::
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
m_TextProgram.Bind();
|
||||
glUniform3f(glGetUniformLocation(m_TextProgram.GetHandle(), "textColor"), color.x, color.y, color.z);
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram.GetHandle(), "M"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram.GetHandle(), "V"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram.GetHandle(), "P"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
|
||||
m_TextProgram->Bind();
|
||||
glUniform3f(glGetUniformLocation(m_TextProgram->GetHandle(), "textColor"), color.x, color.y, color.z);
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram->GetHandle(), "M"), 1, GL_FALSE, glm::value_ptr(modelMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram->GetHandle(), "V"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(m_TextProgram->GetHandle(), "P"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindVertexArray(VAO);
|
||||
|
||||
|
||||
@@ -32,11 +32,10 @@ glm::vec3 ScreenCoords::ToWorldPos(glm::vec2 screenCoord, float depth, float scr
|
||||
ScreenCoords::PixelData ScreenCoords::ToPixelData(float x, float y, FrameBuffer* PickDataBuffer, GLuint DepthBuffer)
|
||||
{
|
||||
PickDataBuffer->Bind();
|
||||
unsigned char pdata[2];
|
||||
glReadPixels(x, y, 1, 1, GL_RG, GL_UNSIGNED_BYTE, &pdata);
|
||||
unsigned char pdata[3];
|
||||
glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pdata);
|
||||
PickDataBuffer->Unbind();
|
||||
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, DepthBuffer);
|
||||
float depthData;
|
||||
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depthData);
|
||||
|
||||
Reference in New Issue
Block a user