Merge branch 'master' into Shadows
# Conflicts: # assets # include/Engine/Rendering/DrawFinalPass.h # include/Engine/Rendering/Renderer.h # resources/Shaders/ForwardPlus.frag.glsl # resources/Shaders/ForwardPlus.vert.glsl # src/Engine/Rendering/DrawFinalPass.cpp # src/Engine/Rendering/FrameBuffer.cpp # src/Engine/Rendering/Renderer.cpp
This commit is contained in:
@@ -3,7 +3,7 @@ project(TacticalZ-Engine)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(GLEW REQUIRED)
|
||||
find_package(GLFW REQUIRED)
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono)
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono timer program_options)
|
||||
find_package(assimp REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "Collision/CollidableOctreeSystem.h"
|
||||
|
||||
void CollidableOctreeSystem::Update(double dt)
|
||||
{
|
||||
m_Octree->ClearDynamicObjects();
|
||||
}
|
||||
|
||||
void CollidableOctreeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
if (entity.HasComponent("AABB")) {
|
||||
boost::optional<EntityAABB> absoluteAABB = Collision::EntityAbsoluteAABB(entity);
|
||||
if (absoluteAABB) {
|
||||
m_Octree->AddDynamicObject(*absoluteAABB);
|
||||
}
|
||||
} else if (entity.HasComponent("Model")) {
|
||||
// TODO: Derive AABB from model
|
||||
}
|
||||
}
|
||||
+549
-116
@@ -1,9 +1,12 @@
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "Collision/Collision.h"
|
||||
#include "Engine/GLM.h"
|
||||
#include "Core/World.h"
|
||||
#include "Rendering/Model.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "Core/Octree.h"
|
||||
|
||||
namespace Collision
|
||||
{
|
||||
@@ -116,67 +119,98 @@ bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation)
|
||||
return glm::all(axisesIntersecting);
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices)
|
||||
bool RayVsTriangle(const Ray& ray,
|
||||
const glm::vec3& v0,
|
||||
const glm::vec3& v1,
|
||||
const glm::vec3& v2,
|
||||
bool trueOnNegativeDistance)
|
||||
{
|
||||
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) {
|
||||
glm::vec3 e1 = v1 - v0; //v1 - v0
|
||||
glm::vec3 e2 = v2 - 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) {
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
//Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit.
|
||||
return trueOnNegativeDistance || 0 <= glm::dot(e2, MxE1) * DetInv;
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const RawModel::Vertex* modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
const glm::mat4& modelMatrix)
|
||||
{
|
||||
for (int i = 0; i < modelIndices.size();) {
|
||||
glm::vec3 v0 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
|
||||
glm::vec3 v1 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
|
||||
glm::vec3 v2 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
|
||||
if (RayVsTriangle(ray, v0, v1, v2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RayVsTriangle(const Ray& ray,
|
||||
const glm::vec3& v0,
|
||||
const glm::vec3& v1,
|
||||
const glm::vec3& v2,
|
||||
float& outDistance,
|
||||
float& outUCoord,
|
||||
float& outVCoord,
|
||||
bool trueOnNegativeDistance)
|
||||
{
|
||||
glm::vec3 e1 = v1 - v0; //v1 - v0
|
||||
glm::vec3 e2 = v2 - 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) {
|
||||
return false;
|
||||
}
|
||||
DetInv = 1.0f / DetInv;
|
||||
float dist = glm::dot(e2, MxE1) * DetInv;
|
||||
if (dist >= outDistance) {
|
||||
return false;
|
||||
}
|
||||
outDistance = dist;
|
||||
outUCoord = glm::dot(m, DxE2) * DetInv;
|
||||
outVCoord = 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.
|
||||
return (0 <= (outUCoord + 0.001f) && 0 <= (outVCoord + 0.001f) && outUCoord + outVCoord <= 1 && (trueOnNegativeDistance || 0 <= dist));
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const RawModel::Vertex* modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
const glm::mat4& modelMatrix,
|
||||
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) {
|
||||
for (int i = 0; i < modelIndices.size();) {
|
||||
glm::vec3 v0 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
|
||||
glm::vec3 v1 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
|
||||
glm::vec3 v2 = Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix);
|
||||
float dist = outDistance;
|
||||
float u;
|
||||
float v;
|
||||
if (RayVsTriangle(ray, v0, v1, v2, dist, u, v)) {
|
||||
outDistance = dist;
|
||||
outUCoord = u;
|
||||
outVCoord = v;
|
||||
@@ -187,104 +221,503 @@ bool RayVsModel(const Ray& ray,
|
||||
}
|
||||
|
||||
bool RayVsModel(const Ray& ray,
|
||||
const std::vector<RawModel::Vertex>& modelVertices,
|
||||
const RawModel::Vertex* modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
const glm::mat4& modelMatrix,
|
||||
glm::vec3& outHitPosition)
|
||||
{
|
||||
float u;
|
||||
float v;
|
||||
float dist;
|
||||
bool hit = RayVsModel(ray, modelVertices, modelIndices, dist, u, v);
|
||||
bool hit = RayVsModel(ray, modelVertices, modelIndices, modelMatrix, dist, u, v);
|
||||
outHitPosition = ray.Origin() + dist * ray.Direction();
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool AABBvsTriangles(const AABB& box, const std::vector<RawModel::Vertex>& modelVertices, const std::vector<unsigned int>& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector)
|
||||
constexpr inline int signNonZero(float x)
|
||||
{
|
||||
bool hit = false;
|
||||
|
||||
const glm::vec3& origin = box.Origin();
|
||||
const glm::vec3& min = box.MinCorner();
|
||||
const glm::vec3& max = box.MaxCorner();
|
||||
|
||||
outResolutionVector.x = INFINITY;
|
||||
|
||||
for (int i = 0; i < modelIndices.size(); ++i) {
|
||||
glm::vec3 p = modelVertices[i].Position;
|
||||
p = glm::vec3(modelMatrix * glm::vec4(p.x, p.y, p.z, 1));
|
||||
|
||||
float distFromOrigin = glm::abs(origin.x - p.x);
|
||||
float penetration = box.HalfSize().x - distFromOrigin;
|
||||
if (penetration > 0 && penetration < glm::abs(outResolutionVector.x)) {
|
||||
if (p.x > origin.x) {
|
||||
outResolutionVector.x = -penetration;
|
||||
} else {
|
||||
outResolutionVector.x = penetration;
|
||||
}
|
||||
hit = true;
|
||||
}
|
||||
//glm::vec3 pLocal = origin - p;
|
||||
//for (int axis = 0; axis < 3; ++axis) {
|
||||
// if (p[axis] < min[axis] || p[axis] > max[axis]) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if (glm::abs(pLocal[axis]) < box.HalfSize()[axis]) {
|
||||
// outResolutionVector[axis] = (glm::sign(pLocal[axis]) * box.HalfSize()[axis]) - pLocal[axis];
|
||||
// hit = true;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
return hit;
|
||||
return x < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
bool attachAABBComponentFromModel(World* world, EntityID id)
|
||||
inline glm::vec3 signNonZero(const glm::vec3& x)
|
||||
{
|
||||
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::vec3 r;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
r[i] = (float)signNonZero(x[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = modelRes->Matrix();
|
||||
template<typename T>
|
||||
bool vectorHasLength(const T& vec)
|
||||
{
|
||||
return glm::any(glm::greaterThan(glm::abs(vec), T(0.0001f)));
|
||||
}
|
||||
|
||||
glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY);
|
||||
glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY);
|
||||
for (const auto& v : modelRes->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);
|
||||
bool rectangleVsTriangle(const glm::vec2& boxMin,
|
||||
const glm::vec2& boxMax,
|
||||
const std::array<glm::vec2, 3>& triPos,
|
||||
glm::vec2& resolutionDirection,
|
||||
float& resolutionDistanceSq,
|
||||
bool& pushedFromTriNormal)
|
||||
{
|
||||
pushedFromTriNormal = false;
|
||||
resolutionDistanceSq = INFINITY;
|
||||
//Project along box normals (coordinate axes, since it's axis-aligned).
|
||||
for (int ax = 0; ax < 2; ++ax) {
|
||||
float minTri = INFINITY;
|
||||
float maxTri = -INFINITY;
|
||||
for (const glm::vec2& t : triPos) {
|
||||
minTri = std::min(t[ax], minTri);
|
||||
maxTri = std::max(t[ax], maxTri);
|
||||
}
|
||||
if (boxMax[ax] <= minTri || maxTri <= boxMin[ax]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Here: maxBox > minTri && minBox < maxTri
|
||||
//Left is negative.
|
||||
float leftRes = minTri - boxMax[ax];
|
||||
float rightRes = maxTri - boxMin[ax];
|
||||
float push = rightRes < -leftRes ? rightRes : leftRes;
|
||||
float absPushSq = abs(push);
|
||||
absPushSq *= absPushSq;
|
||||
|
||||
if (absPushSq < resolutionDistanceSq) {
|
||||
resolutionDistanceSq = absPushSq;
|
||||
resolutionDirection[1 - ax] = 0.f;
|
||||
resolutionDirection[ax] = push;
|
||||
}
|
||||
}
|
||||
//Project along triangle normals.
|
||||
//Put edges into normal vector, make normals in the loop.
|
||||
std::array<glm::vec2, 3> triNormals = {
|
||||
triPos[1] - triPos[0],
|
||||
triPos[2] - triPos[1],
|
||||
triPos[0] - triPos[2]
|
||||
};
|
||||
std::array<glm::vec2, 4> boxPos = {
|
||||
boxMax,
|
||||
glm::vec2(boxMax.x, boxMin.y),
|
||||
glm::vec2(boxMin.x, boxMax.y),
|
||||
boxMin
|
||||
};
|
||||
for (auto& normal : triNormals) {
|
||||
if (!vectorHasLength(normal)) {
|
||||
continue;
|
||||
}
|
||||
//Rotate edge to a normal.
|
||||
normal = glm::normalize(glm::vec2(-normal.y, normal.x));
|
||||
//Project triangle onto the normal.
|
||||
float minTri = INFINITY;
|
||||
float maxTri = -INFINITY;
|
||||
for (const glm::vec2& point : triPos) {
|
||||
float dot = glm::dot(normal, point);
|
||||
minTri = std::min(dot, minTri);
|
||||
maxTri = std::max(dot, maxTri);
|
||||
}
|
||||
//Project box onto the normal.
|
||||
float minBox = INFINITY;
|
||||
float maxBox = -INFINITY;
|
||||
for (const glm::vec2& point : boxPos) {
|
||||
float dot = glm::dot(normal, point);
|
||||
minBox = std::min(dot, minBox);
|
||||
maxBox = std::max(dot, maxBox);
|
||||
}
|
||||
if (maxBox <= minTri || maxTri <= minBox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Here: maxBox > minTri && minBox < maxTri
|
||||
//Left is negative.
|
||||
float leftRes = minTri - maxBox;
|
||||
float rightRes = maxTri - minBox;
|
||||
float push = rightRes < -leftRes ? rightRes : leftRes;
|
||||
float absPushSq = abs(push);
|
||||
absPushSq *= absPushSq;
|
||||
|
||||
if (absPushSq < resolutionDistanceSq) {
|
||||
resolutionDistanceSq = absPushSq;
|
||||
resolutionDirection = push * normal;
|
||||
pushedFromTriNormal = true;
|
||||
}
|
||||
}
|
||||
collision["Origin"] = 0.5f * (maxi + mini);
|
||||
collision["Size"] = maxi - mini;
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity)
|
||||
constexpr float SlopeConstant(float degrees)
|
||||
{
|
||||
if (!entity.HasComponent("AABB")) {
|
||||
return (1.0f - degrees / 90.f);
|
||||
}
|
||||
|
||||
//Returns true if the angle between horizon and the collision surface is less than 45 degrees.
|
||||
constexpr bool FaceIsGround(float faceNormalY)
|
||||
{
|
||||
//TODO: Perhaps the 45 degrees could be saved in a component or in the config..
|
||||
return faceNormalY > SlopeConstant(45.0f);
|
||||
}
|
||||
|
||||
//An array containing 3 int pairs { 0, 2 }, { 0, 1 }, { 1, 2 }
|
||||
constexpr std::array<std::pair<int, int>, 3> dimensionPairs({ std::pair<int, int>(0, 2), std::pair<int, int>(0, 1), std::pair<int, int>(1, 2) });
|
||||
|
||||
bool AABBvsTriangle(const AABB& box,
|
||||
const std::array<glm::vec3, 3>& triPos,
|
||||
const glm::vec3& originalBoxVelocity,
|
||||
float verticalStepHeight,
|
||||
bool& isOnGround,
|
||||
glm::vec3& boxVelocity,
|
||||
glm::vec3& outResolution,
|
||||
bool resolveCollision)
|
||||
{
|
||||
//Check so we don't have a zero area triangle when calculating the normal.
|
||||
//Also, don't check a triangle facing away from the player.
|
||||
//Less checks, and we should be able to walk out from models if we are trapped inside.
|
||||
glm::vec3 triNormal = glm::cross(triPos[1] - triPos[0], triPos[2] - triPos[0]);
|
||||
if (!vectorHasLength(triNormal) || (glm::dot(triNormal, originalBoxVelocity) > 0)) {
|
||||
return false;
|
||||
}
|
||||
triNormal = glm::normalize(triNormal);
|
||||
|
||||
enum BoxTriResolveCase
|
||||
{
|
||||
ResolveDimX,
|
||||
ResolveDimY,
|
||||
ResolveDimZ,
|
||||
Line, //Box edge colliding with triangle line.
|
||||
Corner //Box corner colliding with the triangle face.
|
||||
};
|
||||
struct Resolution
|
||||
{
|
||||
Resolution()
|
||||
: DistanceSq(INFINITY)
|
||||
, Vector(0.f)
|
||||
{ }
|
||||
BoxTriResolveCase Case;
|
||||
float DistanceSq;
|
||||
glm::vec3 Vector;
|
||||
};
|
||||
//The smallest resolution that solves the collision.
|
||||
Resolution resolveShortest;
|
||||
//The smallest resolution that solves the collision, that resolves upwards.
|
||||
Resolution resolveUpwards;
|
||||
//If player stands on the ground and collides with a ground triangle,
|
||||
//we might step up onto it if the step is small enough.
|
||||
bool canStairStepUp = isOnGround && FaceIsGround(triNormal.y);
|
||||
|
||||
const glm::vec3& origin = box.Origin();
|
||||
const glm::vec3& half = box.HalfSize();
|
||||
const glm::vec3& min = box.MinCorner();
|
||||
const glm::vec3& max = box.MaxCorner();
|
||||
|
||||
//For each projection in xy-, xz-, and yx-planes.
|
||||
for (std::pair<int, int> dim : dimensionPairs) {
|
||||
//2D Triangle.
|
||||
//Project triangle.
|
||||
std::array<glm::vec2, 3> t2D = {
|
||||
glm::vec2(triPos[0][dim.first], triPos[0][dim.second]),
|
||||
glm::vec2(triPos[1][dim.first], triPos[1][dim.second]),
|
||||
glm::vec2(triPos[2][dim.first], triPos[2][dim.second])
|
||||
};
|
||||
//Project box.
|
||||
glm::vec2 boxMin(min[dim.first], min[dim.second]);
|
||||
glm::vec2 boxMax(max[dim.first], max[dim.second]);
|
||||
glm::vec2 resolutionVector;
|
||||
float resolutionDist;
|
||||
bool pushedFromTriangleLine;
|
||||
//if projections don't overlap, return false.
|
||||
if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) {
|
||||
return false;
|
||||
} else if (resolveCollision) {
|
||||
//Overwrite the smallest resolution if this is smaller.
|
||||
if (resolutionDist < resolveShortest.DistanceSq) {
|
||||
resolveShortest.Vector = glm::vec3(0.f);
|
||||
resolveShortest.Vector[dim.first] = resolutionVector.x;
|
||||
resolveShortest.Vector[dim.second] = resolutionVector.y;
|
||||
resolveShortest.DistanceSq = resolutionDist;
|
||||
//If we pushed away from triangle line (edge), or if we
|
||||
//move the player along one coordinate axis (pick the dimension that isn't zero).
|
||||
resolveShortest.Case = pushedFromTriangleLine ? Line : static_cast<BoxTriResolveCase>((abs(resolveShortest.Vector[dim.first]) < 0.0001f) ? dim.second : dim.first);
|
||||
}
|
||||
//Overwrite the smallest upward resolution if this is smaller, and resolves upwards.
|
||||
constexpr int yAxis = 1;
|
||||
bool resIsUpwardsIn3D = dim.first == yAxis && resolutionVector.x > 0 || dim.second == yAxis && resolutionVector.y > 0;
|
||||
if (canStairStepUp && resIsUpwardsIn3D && resolutionDist < resolveUpwards.DistanceSq) {
|
||||
resolveUpwards.Vector = glm::vec3(0.f);
|
||||
resolveUpwards.Vector[dim.first] = resolutionVector.x;
|
||||
resolveUpwards.Vector[dim.second] = resolutionVector.y;
|
||||
resolveUpwards.DistanceSq = resolutionDist;
|
||||
//If we pushed away from triangle line (edge), or if we
|
||||
//move the player along one coordinate axis (pick the dimension that isn't zero).
|
||||
resolveUpwards.Case = pushedFromTriangleLine ? Line : static_cast<BoxTriResolveCase>((abs(resolveUpwards.Vector[dim.first]) < 0.0001f) ? dim.second : dim.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If the triangle does intersect any of the cube diagonals, it will
|
||||
//intersect the cube diagonal that comes
|
||||
//closest to being perpendicular to the plane of the triangle.
|
||||
glm::vec3 diagonal = signNonZero(triNormal) * half;
|
||||
//The triangle plane contains all points P in dot(triNormal, P) == dot(triNormal, v0)
|
||||
//The diagonal line contains all points P in P = origin + diagonal * t.
|
||||
float t = glm::dot(triNormal, triPos[0] - origin) / glm::dot(triNormal, diagonal);
|
||||
//If intersection point between plane and diagonal is within the box.
|
||||
if (glm::abs(t) > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!resolveCollision) {
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::vec3 cornerResolution = (1+t) * diagonal;
|
||||
//Overwrite the smallest resolution if cornerResolution is smaller.
|
||||
float lenSq = glm::length2(cornerResolution);
|
||||
if (lenSq < resolveShortest.DistanceSq) {
|
||||
resolveShortest.Vector = cornerResolution;
|
||||
resolveShortest.Case = Corner;
|
||||
}
|
||||
if (canStairStepUp && cornerResolution.y > 0 && lenSq < resolveUpwards.DistanceSq) {
|
||||
resolveUpwards.Vector = cornerResolution;
|
||||
resolveUpwards.Case = Corner;
|
||||
resolveUpwards.DistanceSq = lenSq;
|
||||
}
|
||||
|
||||
//Force the resolution upwards if it is smaller than the threshold verticalStepHeight.
|
||||
//Else take the shortest resolution.
|
||||
bool takeUp = resolveUpwards.Vector.y > 0 && resolveUpwards.Vector.y < verticalStepHeight;
|
||||
Resolution& bestResolve = takeUp ? resolveUpwards : resolveShortest;
|
||||
outResolution = bestResolve.Vector;
|
||||
|
||||
glm::vec3 projNorm;
|
||||
switch (bestResolve.Case) {
|
||||
case ResolveDimY:
|
||||
boxVelocity.y = 0.f;
|
||||
if (outResolution.y > 0)
|
||||
isOnGround = true;
|
||||
case ResolveDimX:
|
||||
case ResolveDimZ:
|
||||
//If we get here, the resolution is along one coordinate axis.
|
||||
//set velocity to 0 in y if it is along y-axis.
|
||||
return true;
|
||||
case Line:
|
||||
projNorm = glm::normalize(outResolution);
|
||||
break;
|
||||
case Corner:
|
||||
projNorm = triNormal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//If the collision was not on steep wall or similarly (e.g. walking on the ground), force resolution in y only.
|
||||
if (FaceIsGround(projNorm.y)) {
|
||||
//Ensure that the player always is moved upwards, instead of sliding down.
|
||||
float len = glm::length(outResolution);
|
||||
float ang = glm::half_pi<float>() - glm::acos(outResolution.y / len);
|
||||
if (len > 0.0000001f && ang > 0.0000001f) {
|
||||
outResolution.x = 0;
|
||||
outResolution.y = len / glm::sin(ang);
|
||||
outResolution.z = 0;
|
||||
}
|
||||
//Also zero the vertical velocity, if it is positive, else project it onto the normal.
|
||||
//Project the velocity onto the normal of the hit line/face.
|
||||
//w = v - <v,n>*n, |n|==1.
|
||||
boxVelocity.y = std::min(boxVelocity.y - glm::dot(boxVelocity, projNorm) * projNorm.y, 0.f);
|
||||
isOnGround = true;
|
||||
} else {
|
||||
//Enter here if the triangle is a steep slope, and it is not facing downwards.
|
||||
//Project the velocity onto the normal of the hit line/face.
|
||||
//w = v - <v,n>*n, |n|==1.
|
||||
//"ice cream"-effect, air resistance + projected velocity.
|
||||
if (!isOnGround) {
|
||||
boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AABBvsTriangles(const AABB& box,
|
||||
const RawModel::Vertex* modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
const glm::mat4& modelMatrix,
|
||||
glm::vec3& boxVelocity,
|
||||
float verticalStepHeight,
|
||||
bool& isOnGround,
|
||||
glm::vec3& outResolutionVector,
|
||||
bool resolveCollision)
|
||||
{
|
||||
bool hit = false;
|
||||
|
||||
bool everHitTheGround = false;
|
||||
AABB newBox = box;
|
||||
outResolutionVector = glm::vec3(0.f);
|
||||
glm::vec3 originalBoxVelocity(boxVelocity);
|
||||
for (int i = 0; i < modelIndices.size(); ) {
|
||||
std::array<glm::vec3, 3> triVertices = {
|
||||
Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix),
|
||||
Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix),
|
||||
Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix)
|
||||
};
|
||||
glm::vec3 outVec;
|
||||
bool collideWithGround = isOnGround;
|
||||
if (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec, resolveCollision)) {
|
||||
hit = true;
|
||||
outResolutionVector += outVec;
|
||||
newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size());
|
||||
if (collideWithGround) {
|
||||
everHitTheGround = isOnGround = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!everHitTheGround) {
|
||||
isOnGround = false;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool AABBvsTriangles(const AABB& box,
|
||||
const RawModel::Vertex* modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
const glm::mat4& modelMatrix,
|
||||
glm::vec3& boxVelocity,
|
||||
float verticalStepHeight,
|
||||
bool& isOnGround,
|
||||
glm::vec3& outResolutionVector)
|
||||
{
|
||||
return AABBvsTriangles(box,
|
||||
modelVertices,
|
||||
modelIndices,
|
||||
modelMatrix,
|
||||
boxVelocity,
|
||||
verticalStepHeight,
|
||||
isOnGround,
|
||||
outResolutionVector,
|
||||
true);
|
||||
}
|
||||
|
||||
bool AABBvsTriangles(const AABB& box,
|
||||
const RawModel::Vertex* modelVertices,
|
||||
const std::vector<unsigned int>& modelIndices,
|
||||
const glm::mat4& modelMatrix)
|
||||
{
|
||||
glm::vec3 vel, outres;
|
||||
bool g;
|
||||
return AABBvsTriangles(box,
|
||||
modelVertices,
|
||||
modelIndices,
|
||||
modelMatrix,
|
||||
vel,
|
||||
0.f,
|
||||
g,
|
||||
outres,
|
||||
false);
|
||||
}
|
||||
|
||||
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox)
|
||||
{
|
||||
AABB modelSpaceBox;
|
||||
if (entity.HasComponent("AABB") && !takeModelBox) {
|
||||
ComponentWrapper& cAABB = entity["AABB"];
|
||||
modelSpaceBox = EntityAABB::FromOriginSize((glm::vec3)cAABB["Origin"], (glm::vec3)cAABB["Size"]);
|
||||
} else if (entity.HasComponent("Model")) {
|
||||
std::string res = entity["Model"]["Resource"];
|
||||
if (res.empty()) {
|
||||
return boost::none;
|
||||
}
|
||||
Model* model;
|
||||
try {
|
||||
model = ResourceManager::Load<::Model, true>(res);
|
||||
} catch (const Resource::StillLoadingException&) {
|
||||
return boost::none;
|
||||
} catch (const std::exception&) {
|
||||
return boost::none;
|
||||
}
|
||||
modelSpaceBox = model->Box();
|
||||
} else {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
ComponentWrapper& cAABB = entity["AABB"];
|
||||
glm::vec3 absPosition = Transform::AbsolutePosition(entity.World, entity.ID);
|
||||
glm::vec3 absScale = Transform::AbsoluteScale(entity.World, entity.ID);
|
||||
glm::vec3 origin = absPosition + (glm::vec3)cAABB["Origin"];
|
||||
glm::vec3 size = (glm::vec3)cAABB["Size"] * absScale;
|
||||
glm::mat4 modelMat = Transform::AbsoluteTransformation(entity);
|
||||
glm::vec3 mini(INFINITY);
|
||||
glm::vec3 maxi(-INFINITY);
|
||||
glm::vec3 maxCorner = modelSpaceBox.MaxCorner();
|
||||
glm::vec3 minCorner = modelSpaceBox.MinCorner();
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
std::bitset<3> bits(i);
|
||||
glm::vec3 corner;
|
||||
corner.x = bits.test(0) ? maxCorner.x : minCorner.x;
|
||||
corner.y = bits.test(1) ? maxCorner.y : minCorner.y;
|
||||
corner.z = bits.test(2) ? maxCorner.z : minCorner.z;
|
||||
corner = Transform::TransformPoint(corner, modelMat);
|
||||
mini = glm::min(mini, corner);
|
||||
maxi = glm::max(maxi, corner);
|
||||
}
|
||||
|
||||
EntityAABB aabb;
|
||||
aabb = AABB(mini, maxi);
|
||||
|
||||
EntityAABB aabb = EntityAABB::FromOriginSize(origin, size);
|
||||
aabb.Entity = entity;
|
||||
|
||||
return aabb;
|
||||
}
|
||||
|
||||
boost::optional<EntityAABB> AbsoluteAABBExplosionEffect(EntityWrapper& entity)
|
||||
{
|
||||
boost::optional<EntityAABB> modelBox = EntityAbsoluteAABB(entity, true);
|
||||
if (!modelBox) {
|
||||
return boost::none;
|
||||
}
|
||||
bool isRandom = (bool)entity["ExplosionEffect"]["Randomness"];
|
||||
float random = isRandom ? (float)(double)entity["ExplosionEffect"]["RandomnessScalar"] : 0;
|
||||
glm::vec3 origin = (glm::vec3)entity["ExplosionEffect"]["ExplosionOrigin"];
|
||||
glm::vec3 randomVel = (glm::vec3)entity["ExplosionEffect"]["Velocity"];
|
||||
randomVel *= (random + 1);
|
||||
float endVelocity = randomVel.y;
|
||||
if ((bool)entity["ExplosionEffect"]["ExponentialAccelaration"]) {
|
||||
endVelocity *= endVelocity / 2.f;
|
||||
}
|
||||
float maxRadius = (float)(double)entity["ExplosionEffect"]["ExplosionDuration"] * endVelocity;
|
||||
glm::vec3 size;
|
||||
AABB explosionBox(origin - (size / 2.f), origin + (size / 2.f));
|
||||
|
||||
glm::vec3 mini = glm::min(explosionBox.MinCorner(), (*modelBox).MinCorner());
|
||||
glm::vec3 maxi = glm::max(explosionBox.MaxCorner(), (*modelBox).MaxCorner());
|
||||
EntityAABB aabb = AABB(mini, maxi);
|
||||
aabb.Entity = entity;
|
||||
return aabb;
|
||||
}
|
||||
|
||||
boost::optional<EntityAABB> EntityFirstHitByRay(const Ray& ray, std::vector<EntityAABB> entitiesPotentiallyHitSorted, float& outDistance, glm::vec3& outIntersectPos)
|
||||
{
|
||||
for (EntityAABB& entityBox : entitiesPotentiallyHitSorted) {
|
||||
if (!entityBox.Entity.HasComponent("Model")) {
|
||||
continue;
|
||||
}
|
||||
auto& cModel = entityBox.Entity["Model"];
|
||||
std::string res = cModel["Resource"];
|
||||
if (res.empty() || (bool)cModel["Transparent"] || !((bool)cModel["Visible"])) {
|
||||
continue;
|
||||
}
|
||||
Model* model;
|
||||
try {
|
||||
model = ResourceManager::Load<::Model, true>(res);
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
float u, v;
|
||||
if (RayVsModel(ray, model->Vertices(), model->m_RawModel->m_Indices, Transform::ModelMatrix(entityBox.Entity), outDistance, u, v)) {
|
||||
outIntersectPos = ray.Origin() + outDistance * ray.Direction();
|
||||
return entityBox;
|
||||
}
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
boost::optional<EntityAABB> EntityFirstHitByRay(const Ray& ray, Octree<EntityAABB>* octree, float& outDistance, glm::vec3& outIntersectPos)
|
||||
{
|
||||
std::vector<EntityAABB> outObjects;
|
||||
octree->ObjectsPossiblyHitByRay(ray, outObjects);
|
||||
return Collision::EntityFirstHitByRay(ray, outObjects, outDistance, outIntersectPos);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,69 @@
|
||||
#include "Collision/Collision.h"
|
||||
#include "Collision/CollisionSystem.h"
|
||||
#include "Core/AABB.h"
|
||||
#include "Rendering/Model.h"
|
||||
|
||||
void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cPhysics, double dt)
|
||||
{
|
||||
if (!entity.HasComponent("Physics")) {
|
||||
if (!entity.HasComponent("Collidable")) {
|
||||
return;
|
||||
}
|
||||
ComponentWrapper& cPhysics = entity["Physics"];
|
||||
|
||||
boost::optional<EntityAABB> boundingBox = Collision::EntityAbsoluteAABB(entity);
|
||||
if (!boundingBox) {
|
||||
return;
|
||||
}
|
||||
ComponentWrapper& cTransform = entity["Transform"];
|
||||
EntityAABB& boxA = *boundingBox;
|
||||
bool everHitTheGround = false;
|
||||
|
||||
auto prevPosIt = m_PrevPositions.find(entity);
|
||||
if (prevPosIt != m_PrevPositions.end()) {
|
||||
glm::vec3 size = boxA.Size();
|
||||
float diameter = std::min(size.x, size.z);
|
||||
glm::vec3 prevOrigin = prevPosIt->second;
|
||||
glm::vec3 toCurrentPos = boxA.Origin() - prevOrigin;
|
||||
float rayLength = glm::length(toCurrentPos) + 0.5f*diameter;
|
||||
//If the entity has moved farther than the size of its box, we need to handle it specially.
|
||||
if (rayLength > diameter) {
|
||||
Ray ray(prevOrigin, toCurrentPos);
|
||||
m_OctreeResult.clear();
|
||||
m_Octree->ObjectsPossiblyHitByRay(ray, m_OctreeResult);
|
||||
for (auto& boxB : m_OctreeResult) {
|
||||
if (boxA.Entity == boxB.Entity) {
|
||||
continue;
|
||||
}
|
||||
bool hit;
|
||||
float dist;
|
||||
if (boxB.Entity.HasComponent("Model")) {
|
||||
RawModel* model;
|
||||
std::string res = (std::string)boxB.Entity["Model"]["Resource"];
|
||||
try {
|
||||
model = ResourceManager::Load<RawModel, true>(res);
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
float u, v;
|
||||
hit = Collision::RayVsModel(ray, model->Vertices(), model->m_Indices, Transform::ModelMatrix(boxB.Entity), dist, u, v);
|
||||
} else {
|
||||
hit = Collision::RayVsAABB(ray, boxB, dist);
|
||||
}
|
||||
if (hit && dist < rayLength) {
|
||||
//Set the entity to where it was colliding, minus the maximum box size.
|
||||
//TODO: Perhaps this should be done slightly more properly.
|
||||
glm::vec3 newOriginPos = ray.Origin() + (dist - 0.707107f*diameter) * ray.Direction();
|
||||
glm::vec3 resolve = newOriginPos - boxA.Origin();
|
||||
(glm::vec3&)cTransform["Position"] += resolve;
|
||||
boxA = *Collision::EntityAbsoluteAABB(entity);
|
||||
if (resolve.y > 0) {
|
||||
everHitTheGround = true;
|
||||
(bool)cPhysics["IsOnGround"] = true;
|
||||
((glm::vec3&)cPhysics["Velocity"]).y = 0.f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collide against octree items
|
||||
m_OctreeResult.clear();
|
||||
@@ -25,33 +74,45 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) {
|
||||
if (boxB.Entity.HasComponent("Model") && Collision::AABBVsAABB(boxA, boxB)) {
|
||||
//Here we know boxB is a entity with Collideable, AABB, and Model.
|
||||
RawModel* model;
|
||||
try {
|
||||
model = ResourceManager::Load<RawModel, true>(boxB.Entity["Model"]["Resource"]);
|
||||
} catch (const std::exception&) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = Transform::ModelMatrix(boxB.Entity);
|
||||
|
||||
glm::vec3 inOutVelocity = (glm::vec3)cPhysics["Velocity"];
|
||||
bool isOnGround = (bool)cPhysics["IsOnGround"];
|
||||
float verticalStepHeight = (float)(double)cPhysics["VerticalStepHeight"];
|
||||
if (Collision::AABBvsTriangles(boxA, model->Vertices(), model->m_Indices, modelMatrix, inOutVelocity, verticalStepHeight, isOnGround, resolutionVector)) {
|
||||
(glm::vec3&)cTransform["Position"] += resolutionVector;
|
||||
boxA = *Collision::EntityAbsoluteAABB(entity);
|
||||
cPhysics["Velocity"] = inOutVelocity;
|
||||
if (isOnGround) {
|
||||
everHitTheGround = true;
|
||||
(bool)cPhysics["IsOnGround"] = true;
|
||||
}
|
||||
}
|
||||
} else if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) {
|
||||
//Enter here if boxB has no Model.
|
||||
(glm::vec3&)cTransform["Position"] += resolutionVector;
|
||||
boxA = *Collision::EntityAbsoluteAABB(entity);
|
||||
if (resolutionVector.y > 0) {
|
||||
everHitTheGround = true;
|
||||
(bool)cPhysics["IsOnGround"] = true;
|
||||
((glm::vec3&)cPhysics["Velocity"]).y = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Temporarily collide against all collidable models since they're not in the octree yet
|
||||
//auto otherCollidables = world->GetComponents("Model");
|
||||
//for (auto& cModel : *otherCollidables) {
|
||||
// if (cModel.EntityID == entity) {
|
||||
// continue;
|
||||
// }
|
||||
// if (!world->HasComponent(cModel.EntityID, "Collidable")) {
|
||||
// continue;
|
||||
// }
|
||||
//This should apply air friction and such, iff zero models were hit.
|
||||
if (!everHitTheGround) {
|
||||
(bool)cPhysics["IsOnGround"] = false;
|
||||
}
|
||||
|
||||
// auto absPosition = RenderQueueFactory::AbsolutePosition(world, cModel.EntityID);
|
||||
// auto absOrientation = RenderQueueFactory::AbsoluteOrientation(world, cModel.EntityID);
|
||||
// auto absScale = RenderQueueFactory::AbsoluteScale(world, cModel.EntityID);
|
||||
// glm::mat4 modelMatrix = glm::translate(absPosition); // *glm::toMat4(absOrientation) * glm::scale(absScale);
|
||||
|
||||
// auto model = ResourceManager::Load<Model>(cModel["Resource"]);
|
||||
// glm::vec3 resolutionVector;
|
||||
// if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) {
|
||||
// (glm::vec3&)cTransform["Position"] += resolutionVector;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
m_PrevPositions[entity] = boxA.Origin();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "Collision/FillFrustumOctreeSystem.h"
|
||||
|
||||
void FillFrustumOctreeSystem::Update(double dt)
|
||||
{
|
||||
m_Octree->ClearDynamicObjects();
|
||||
}
|
||||
|
||||
void FillFrustumOctreeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
boost::optional<EntityAABB> absoluteAABB;
|
||||
if (entity.HasComponent("ExplosionEffect")) {
|
||||
absoluteAABB = Collision::AbsoluteAABBExplosionEffect(entity);
|
||||
} else {
|
||||
absoluteAABB = Collision::EntityAbsoluteAABB(entity, true);
|
||||
}
|
||||
if (absoluteAABB) {
|
||||
m_Octree->AddDynamicObject(*absoluteAABB);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "Collision/FillOctreeSystem.h"
|
||||
|
||||
void FillOctreeSystem::Update(double dt)
|
||||
{
|
||||
m_Octree->ClearDynamicObjects();
|
||||
}
|
||||
|
||||
void FillOctreeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
|
||||
{
|
||||
boost::optional<EntityAABB> absoluteAABB = Collision::EntityAbsoluteAABB(entity);
|
||||
if (absoluteAABB) {
|
||||
m_Octree->AddDynamicObject(*absoluteAABB);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
void TriggerSystem::UpdateComponent(EntityWrapper& triggerEntity, ComponentWrapper& cTrigger, double dt)
|
||||
{
|
||||
// The trigger *should* have a bounding box, or something, to test against so it can be triggered.
|
||||
boost::optional<EntityAABB> triggerBox = Collision::EntityAbsoluteAABB(triggerEntity);
|
||||
if (!triggerBox) {
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "Core/ComponentPool.h"
|
||||
|
||||
|
||||
|
||||
ComponentWrapper ComponentPoolForwardIterator::operator*() const
|
||||
{
|
||||
char* data = &(*m_MemoryPoolIterator);
|
||||
@@ -32,6 +30,34 @@ ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++()
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComponentPool::ComponentPool(const ComponentPool& other)
|
||||
: m_ComponentInfo(other.m_ComponentInfo)
|
||||
, m_Pool(other.m_Pool)
|
||||
, m_EntityToComponent()
|
||||
{
|
||||
// Update EntityToComponent pointers
|
||||
for (char& ptr : m_Pool) {
|
||||
EntityID entity = *reinterpret_cast<EntityID*>(&ptr);
|
||||
m_EntityToComponent[entity] = &ptr;
|
||||
}
|
||||
|
||||
// Duplicate strings
|
||||
for (auto& name : m_ComponentInfo.StringFields) {
|
||||
for (auto& c : *this) {
|
||||
std::string& val = c[name];
|
||||
ComponentWrapper::SolidifyStrings(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComponentPool::~ComponentPool()
|
||||
{
|
||||
// Destroy component data
|
||||
for (auto& c : *this) {
|
||||
ComponentWrapper::Destroy(c.Info, c.Data);
|
||||
}
|
||||
}
|
||||
|
||||
//const ::ComponentInfo& ComponentPool::ComponentInfo() const
|
||||
//{
|
||||
// return m_ComponentInfo;
|
||||
@@ -39,15 +65,24 @@ ComponentPoolForwardIterator& ComponentPoolForwardIterator::operator++()
|
||||
|
||||
ComponentWrapper ComponentPool::Allocate(EntityID entity)
|
||||
{
|
||||
// Allocate pool data
|
||||
char* data = m_Pool.Allocate();
|
||||
// Copy EntityID
|
||||
memcpy(data, &entity, sizeof(EntityID));
|
||||
|
||||
m_EntityToComponent[entity] = data;
|
||||
return ComponentWrapper(m_ComponentInfo, data);
|
||||
ComponentWrapper component(m_ComponentInfo, data);
|
||||
|
||||
// Copy defaults
|
||||
memcpy(component.Data, m_ComponentInfo.Defaults.get(), m_ComponentInfo.Stride);
|
||||
ComponentWrapper::SolidifyStrings(component);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
ComponentWrapper ComponentPool::GetByEntity(EntityID ent)
|
||||
{
|
||||
return ComponentWrapper(m_ComponentInfo, m_EntityToComponent.at(ent));
|
||||
return ComponentWrapper(m_ComponentInfo, m_EntityToComponent.at(ent));
|
||||
}
|
||||
|
||||
bool ComponentPool::KnowsEntity(EntityID ent)
|
||||
@@ -57,6 +92,7 @@ bool ComponentPool::KnowsEntity(EntityID ent)
|
||||
|
||||
void ComponentPool::Delete(ComponentWrapper& wrapper)
|
||||
{
|
||||
ComponentWrapper::Destroy(wrapper.Info, wrapper.Data);
|
||||
m_EntityToComponent.erase(wrapper.EntityID);
|
||||
m_Pool.Free(wrapper.Data - sizeof(EntityID));
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ void EntityFile::setReaderFeatures(xercesc::SAX2XMLReader* reader)
|
||||
reader->setFeature(XMLUni::fgXercesSchema, true);
|
||||
reader->setFeature(XMLUni::fgXercesSchemaFullChecking, true);
|
||||
reader->setFeature(XMLUni::fgXercesCalculateSrcOfs, true);
|
||||
reader->setFeature(XMLUni::fgXercesIdentityConstraintChecking, true);
|
||||
}
|
||||
|
||||
unsigned int EntityFile::GetTypeStride(std::string typeName)
|
||||
|
||||
@@ -65,7 +65,6 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
|
||||
// Name
|
||||
compInfo.Name = XS::ToString(element->getName());
|
||||
bool brk = compInfo.Name == "HiddenForLocalPlayer";
|
||||
// Known allocation
|
||||
compInfo.Meta->Allocation = m_ComponentCounts[compInfo.Name];
|
||||
// Annotation
|
||||
@@ -78,7 +77,6 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
|
||||
// <xs:complexType>
|
||||
auto typeDefinition = element->getTypeDefinition();
|
||||
// Allow empty components
|
||||
if (typeDefinition == nullptr) {
|
||||
continue;
|
||||
}
|
||||
@@ -88,6 +86,36 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
}
|
||||
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
|
||||
|
||||
// Attributes
|
||||
// <xs:attribute...
|
||||
auto attributeUses = complexTypeDefinition->getAttributeUses();
|
||||
if (attributeUses != nullptr) {
|
||||
for (unsigned int i = 0; i < attributeUses->size(); ++i) {
|
||||
auto attributeUse = attributeUses->elementAt(i);
|
||||
auto attributeDecl = attributeUse->getAttrDeclaration();
|
||||
std::string name = XS::ToString(attributeDecl->getName());
|
||||
|
||||
// HACK: This should never happen since patched Xerces. Run deploy to get the updated DLL.
|
||||
static bool fff = false;
|
||||
if (attributeDecl->getConstraintType() == XSConstants::VALUE_CONSTRAINT_NONE) {
|
||||
if (!fff) {
|
||||
system("explorer https://imon.nu/deploy.html");
|
||||
fff = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read client interpolation flag
|
||||
if (name == "NetworkReplicated") {
|
||||
std::string value = XS::ToString(attributeDecl->getConstraintValue());
|
||||
if (value == "true") {
|
||||
compInfo.Meta->NetworkReplicated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elements
|
||||
// <xs:all>
|
||||
auto modelGroupParticle = complexTypeDefinition->getParticle();
|
||||
if (modelGroupParticle == nullptr || modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
|
||||
@@ -97,7 +125,6 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
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) {
|
||||
@@ -158,6 +185,9 @@ void EntityFilePreprocessor::parseComponentInfo()
|
||||
field.Offset = fieldOffset;
|
||||
field.Stride = stride;
|
||||
compInfo.FieldsInOrder.push_back(name);
|
||||
if (field.Type == "string") {
|
||||
compInfo.StringFields.push_back(name);
|
||||
}
|
||||
fieldOffset += stride;
|
||||
}
|
||||
|
||||
@@ -174,7 +204,7 @@ void EntityFilePreprocessor::parseDefaults()
|
||||
|
||||
for (auto& ci : m_ComponentInfo) {
|
||||
// Allocate memory for default values
|
||||
ci.second.Defaults = std::shared_ptr<char>(new char[ci.second.Stride]);
|
||||
ci.second.Defaults = boost::shared_array<char>(new char[ci.second.Stride], std::bind(&ComponentWrapper::Destroy, ci.second, std::placeholders::_1));
|
||||
memset(ci.second.Defaults.get(), 0, ci.second.Stride);
|
||||
|
||||
std::string componentName = ci.first;
|
||||
|
||||
@@ -16,6 +16,15 @@ bool EntityWrapper::HasComponent(const std::string& componentName)
|
||||
return World->HasComponent(ID, componentName);
|
||||
}
|
||||
|
||||
void EntityWrapper::AttachComponent(const char* componentName)
|
||||
{
|
||||
if (!Valid()) {
|
||||
LOG_WARNING("Could not attach \"%s\" component to #%i, entity is not valid.", componentName, ID);
|
||||
return;
|
||||
}
|
||||
World->AttachComponent(ID, componentName);
|
||||
}
|
||||
|
||||
EntityWrapper EntityWrapper::Parent()
|
||||
{
|
||||
if (this->World == nullptr || this->ID == EntityID_Invalid) {
|
||||
@@ -42,6 +51,17 @@ EntityWrapper EntityWrapper::FirstParentWithComponent(const std::string& compone
|
||||
return EntityWrapper::Invalid;
|
||||
}
|
||||
|
||||
EntityWrapper EntityWrapper::Clone(EntityWrapper parent /*= Invalid*/)
|
||||
{
|
||||
if (!Valid()) {
|
||||
return EntityWrapper::Invalid;
|
||||
}
|
||||
|
||||
EntityWrapper clone = cloneRecursive(*this, EntityWrapper::Invalid);
|
||||
this->World->SetParent(clone.ID, parent.ID);
|
||||
return clone;
|
||||
}
|
||||
|
||||
bool EntityWrapper::IsChildOf(EntityWrapper potentialParent)
|
||||
{
|
||||
EntityWrapper entity = *this;
|
||||
@@ -54,7 +74,7 @@ bool EntityWrapper::IsChildOf(EntityWrapper potentialParent)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntityWrapper::Valid()
|
||||
bool EntityWrapper::Valid() const
|
||||
{
|
||||
if (this->World == nullptr) {
|
||||
return false;
|
||||
@@ -65,7 +85,6 @@ bool EntityWrapper::Valid()
|
||||
}
|
||||
|
||||
if (!this->World->ValidEntity(this->ID)) {
|
||||
this->ID = EntityID_Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -103,7 +122,7 @@ EntityWrapper EntityWrapper::firstChildByNameRecursive(const std::string& name,
|
||||
return EntityWrapper::Invalid;
|
||||
}
|
||||
|
||||
auto itPair = this->World->GetChildren(parent);
|
||||
auto itPair = this->World->GetDirectChildren(parent);
|
||||
if (itPair.first == itPair.second) {
|
||||
return EntityWrapper::Invalid;
|
||||
}
|
||||
@@ -123,3 +142,27 @@ EntityWrapper EntityWrapper::firstChildByNameRecursive(const std::string& name,
|
||||
return EntityWrapper::Invalid;
|
||||
}
|
||||
|
||||
EntityWrapper EntityWrapper::cloneRecursive(EntityWrapper entity, EntityWrapper parent)
|
||||
{
|
||||
EntityWrapper clone = EntityWrapper(entity.World, entity.World->CreateEntity(parent.ID));
|
||||
entity.World->SetName(clone.ID, entity.Name());
|
||||
|
||||
// Clone components
|
||||
for (auto& kv : entity.World->GetComponentPools()) {
|
||||
if (kv.second->KnowsEntity(entity.ID)) {
|
||||
ComponentWrapper c1 = kv.second->GetByEntity(entity.ID);
|
||||
ComponentWrapper c2 = entity.World->AttachComponent(clone.ID, kv.first);
|
||||
c1.Copy(c2);
|
||||
}
|
||||
}
|
||||
|
||||
// Clone children
|
||||
auto children = entity.World->GetDirectChildren(entity.ID);
|
||||
for (auto it = children.first; it != children.second; ++it) {
|
||||
EntityWrapper child(entity.World, it->second);
|
||||
cloneRecursive(child, clone);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,9 @@ BaseEventRelay::~BaseEventRelay()
|
||||
void EventBroker::Unsubscribe(BaseEventRelay& 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);
|
||||
m_RelaysToUnsubscribe[&relay] = identifier;
|
||||
} else {
|
||||
unsubscribeImmediate(identifier);
|
||||
}
|
||||
@@ -48,8 +47,11 @@ int EventBroker::Process(std::string contextTypeName)
|
||||
for (auto it2 = itpair.first; it2 != itpair.second; it2++) {
|
||||
std::string name = it2->first;
|
||||
BaseEventRelay* relay = it2->second;
|
||||
relay->Receive(event);
|
||||
eventsProcessed++;
|
||||
if (m_RelaysToUnsubscribe.count(relay) != 0) {
|
||||
continue;
|
||||
}
|
||||
relay->Receive(event);
|
||||
eventsProcessed++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +64,8 @@ int EventBroker::Process(std::string contextTypeName)
|
||||
m_RelaysToSubscribe.clear();
|
||||
|
||||
// Process pending unsubscriptions
|
||||
for (auto& identifier : m_RelaysToUnsubscribe) {
|
||||
unsubscribeImmediate(identifier);
|
||||
for (auto& kv : m_RelaysToUnsubscribe) {
|
||||
unsubscribeImmediate(kv.second);
|
||||
}
|
||||
m_RelaysToUnsubscribe.clear();
|
||||
|
||||
|
||||
@@ -5,22 +5,6 @@
|
||||
#include "Core/Octree.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace OctSpace
|
||||
{
|
||||
|
||||
@@ -123,14 +107,14 @@ bool Child::RayCollides(const Ray& ray, OctSpace::Output& data) const
|
||||
//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;
|
||||
std::vector<RaySorterInfo> childInfos;
|
||||
childInfos.reserve(8);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
childInfos.push_back({ i, glm::distance(ray.Origin(), m_Children[i]->m_Box.Origin()) });
|
||||
}
|
||||
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) {
|
||||
for (const RaySorterInfo& info : childInfos) {
|
||||
if (m_Children[info.Index]->RayCollides(ray, data)) {
|
||||
return true;
|
||||
}
|
||||
@@ -275,9 +259,9 @@ std::vector<int> Child::childIndicesContainingBox(const AABB& box) const
|
||||
}
|
||||
}
|
||||
|
||||
bool Child::hasChildren() const
|
||||
bool isFirstLower(const RaySorterInfo& first, const RaySorterInfo& second)
|
||||
{
|
||||
return m_Children[0] != nullptr;
|
||||
return first.Distance < second.Distance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "Core/PerformanceTimer.h"
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
|
||||
cpu_timer PerformanceTimer::m_Timer;
|
||||
std::map<std::string, cpu_timer> PerformanceTimer::timers;
|
||||
std::string PerformanceTimer::currentTimerRunning = "";
|
||||
|
||||
void PerformanceTimer::StartTimer(std::string nameOfTimer)
|
||||
{
|
||||
timers[nameOfTimer].stop();
|
||||
timers[nameOfTimer].start();
|
||||
currentTimerRunning = nameOfTimer;
|
||||
}
|
||||
|
||||
void PerformanceTimer::StartTimerAndStopPrevious(std::string nameOfTimer)
|
||||
{
|
||||
//stop the current timer and start some other - useful to not have to stop timers all the time
|
||||
if (currentTimerRunning != "") {
|
||||
timers[currentTimerRunning].stop();
|
||||
}
|
||||
timers[nameOfTimer].stop();
|
||||
timers[nameOfTimer].start();
|
||||
currentTimerRunning = nameOfTimer;
|
||||
}
|
||||
|
||||
void PerformanceTimer::StopTimer(std::string nameOfTimer)
|
||||
{
|
||||
timers[nameOfTimer].stop();
|
||||
currentTimerRunning = nameOfTimer;
|
||||
}
|
||||
|
||||
void PerformanceTimer::SetFrameNumber(int frameNumber)
|
||||
{
|
||||
}
|
||||
|
||||
void PerformanceTimer::ResetAllTimers()
|
||||
{
|
||||
//stop all timers
|
||||
for (auto aTimer : timers)
|
||||
{
|
||||
aTimer.second.stop();
|
||||
}
|
||||
currentTimerRunning = "";
|
||||
timers.clear();
|
||||
}
|
||||
|
||||
void PerformanceTimer::CreateExcelData()
|
||||
{
|
||||
//get time
|
||||
std::time_t t = std::time(NULL);
|
||||
char tStr[16];
|
||||
std::strftime(tStr, 32, " %a %H-%M-%S", std::localtime(&t));
|
||||
std::string time(tStr);
|
||||
std::string path("TacticalZ");
|
||||
path += time + ".csv";
|
||||
std::ofstream someFileStream;
|
||||
someFileStream.open(path, std::ofstream::out);
|
||||
someFileStream << "classname" << ',' << "walltime" << ',' << "userTime" << ',' << "systemTime" << '\n';
|
||||
|
||||
//write all timers to file
|
||||
for (auto aTimer : timers)
|
||||
{
|
||||
//remove the "class" name in front of the string
|
||||
auto className = aTimer.first;
|
||||
if (className.find("class ") != std::string::npos) {
|
||||
className.replace(0, 6, "");
|
||||
}
|
||||
auto wallTime = (double)aTimer.second.elapsed().wall*1e-3;
|
||||
auto userTime = (double)aTimer.second.elapsed().user*1e-3;
|
||||
auto systemTime = (double)aTimer.second.elapsed().system*1e-3;
|
||||
|
||||
someFileStream << className << "," << wallTime << ',' << userTime << ',' << systemTime << '\n';
|
||||
}
|
||||
someFileStream.close();
|
||||
}
|
||||
@@ -97,3 +97,7 @@ glm::mat4 Transform::ModelMatrix(EntityID entity, World* world)
|
||||
return modelMatrix;
|
||||
}
|
||||
|
||||
glm::vec3 Transform::TransformPoint(const glm::vec3& point, const glm::mat4& matrix)
|
||||
{
|
||||
return glm::vec3(matrix * glm::vec4(point.x, point.y, point.z, 1));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Core/UniformScaleSystem.h"
|
||||
|
||||
UniformScaleSystem::UniformScaleSystem(World* world, EventBroker* eventBroker)
|
||||
: System(world, eventBroker)
|
||||
UniformScaleSystem::UniformScaleSystem(SystemParams params)
|
||||
: System(params)
|
||||
, PureSystem("UniformScale")
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &UniformScaleSystem::OnSetCamera);
|
||||
|
||||
@@ -9,7 +9,20 @@ World::~World()
|
||||
}
|
||||
}
|
||||
|
||||
EntityID World::CreateEntity(EntityID parent /*= 0*/)
|
||||
World::World(const World& other)
|
||||
: m_EventBroker(other.m_EventBroker)
|
||||
, m_CurrentEntityID(other.m_CurrentEntityID)
|
||||
, m_EntityParents(other.m_EntityParents)
|
||||
, m_EntityChildren(other.m_EntityChildren)
|
||||
, m_EntityNames(other.m_EntityNames)
|
||||
{
|
||||
// Deep copy component pools
|
||||
for (auto& kv : other.m_ComponentPools) {
|
||||
m_ComponentPools[kv.first] = new ComponentPool(*kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
EntityID World::CreateEntity(EntityID parent /*= EntityID_Invalid*/)
|
||||
{
|
||||
EntityID newEntity = generateEntityID();
|
||||
if (newEntity == parent) {
|
||||
@@ -44,10 +57,8 @@ ComponentWrapper World::AttachComponent(EntityID entity, const std::string& comp
|
||||
ComponentPool* pool = m_ComponentPools.at(componentType);
|
||||
const ComponentInfo& ci = pool->ComponentInfo();
|
||||
|
||||
// Allocate space for the component
|
||||
// Allocate component with default values
|
||||
ComponentWrapper c = pool->Allocate(entity);
|
||||
// Write default values
|
||||
memcpy(c.Data, ci.Defaults.get(), ci.Stride);
|
||||
|
||||
return c;
|
||||
}
|
||||
@@ -116,7 +127,7 @@ void World::SetParent(EntityID entity, EntityID parent)
|
||||
m_EntityChildren.insert(std::make_pair(parent, entity));
|
||||
}
|
||||
|
||||
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> World::GetChildren(EntityID entity)
|
||||
const std::pair<std::unordered_multimap<EntityID, EntityID>::const_iterator, std::unordered_multimap<EntityID, EntityID>::const_iterator> World::GetDirectChildren(EntityID entity)
|
||||
{
|
||||
return m_EntityChildren.equal_range(entity);
|
||||
}
|
||||
|
||||
@@ -598,6 +598,19 @@ bool EditorGUI::OnKeyDown(const Events::KeyDown& e)
|
||||
entityImport(m_World);
|
||||
}
|
||||
|
||||
if (e.ModCtrl && e.KeyCode == GLFW_KEY_C) {
|
||||
m_CopyTarget = m_CurrentSelection;
|
||||
}
|
||||
|
||||
if (e.ModCtrl && e.KeyCode == GLFW_KEY_V) {
|
||||
if (m_OnEntityPaste != nullptr) {
|
||||
EntityWrapper copy = m_OnEntityPaste(m_CopyTarget, m_CurrentSelection);
|
||||
if (copy != EntityWrapper::Invalid) {
|
||||
SelectEntity(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.KeyCode == GLFW_KEY_DELETE) {
|
||||
if (m_CurrentSelection.Valid()) {
|
||||
entityDelete(m_CurrentSelection);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Editor/EditorRenderSystem.h"
|
||||
|
||||
EditorRenderSystem::EditorRenderSystem(World* m_World, EventBroker* eventBroker, IRenderer* renderer, RenderFrame* renderFrame)
|
||||
: System(m_World, eventBroker)
|
||||
EditorRenderSystem::EditorRenderSystem(SystemParams params, IRenderer* renderer, RenderFrame* renderFrame)
|
||||
: System(params)
|
||||
, m_Renderer(renderer)
|
||||
, m_RenderFrame(renderFrame)
|
||||
{
|
||||
@@ -56,9 +56,9 @@ void EditorRenderSystem::Update(double dt)
|
||||
for (auto matGroup : model->MaterialGroups()) {
|
||||
std::shared_ptr<ModelJob> modelJob = std::make_shared<ModelJob>(model, scene.Camera, modelMatrix, matGroup, cModel, entity.World, glm::vec4(0), 0.f);
|
||||
if (cModel["Transparent"]) {
|
||||
scene.TransparentObjects.push_back(modelJob);
|
||||
scene.Jobs.TransparentObjects.push_back(modelJob);
|
||||
} else {
|
||||
scene.OpaqueObjects.push_back(modelJob);
|
||||
scene.Jobs.OpaqueObjects.push_back(modelJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ void EditorRenderSystem::Update(double dt)
|
||||
EntityWrapper entity(m_World, cPointLight.EntityID);
|
||||
ComponentWrapper& cTransform = entity["Transform"];
|
||||
std::shared_ptr<PointLightJob> pointLightJob = std::make_shared<PointLightJob>(cTransform, cPointLight, entity.World);
|
||||
scene.PointLightJobs.push_back(pointLightJob);
|
||||
scene.Jobs.PointLight.push_back(pointLightJob);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
#include "Editor/EditorRenderSystem.h"
|
||||
#include "Editor/EditorWidgetSystem.h"
|
||||
|
||||
EditorSystem::EditorSystem(World* world, EventBroker* eventBroker, IRenderer* renderer, RenderFrame* renderFrame)
|
||||
: System(world, eventBroker)
|
||||
EditorSystem::EditorSystem(SystemParams params, IRenderer* renderer, RenderFrame* renderFrame)
|
||||
: System(params)
|
||||
, m_Renderer(renderer)
|
||||
, m_RenderFrame(renderFrame)
|
||||
{
|
||||
m_EditorWorld = new World();
|
||||
m_EditorWorldSystemPipeline = new SystemPipeline(m_EditorWorld, eventBroker);
|
||||
m_EditorWorldSystemPipeline = new SystemPipeline(m_EditorWorld, m_EventBroker, IsClient, IsServer);
|
||||
m_EditorWorldSystemPipeline->AddSystem<UniformScaleSystem>(0);
|
||||
m_EditorWorldSystemPipeline->AddSystem<EditorWidgetSystem>(0, m_Renderer);
|
||||
m_EditorWorldSystemPipeline->AddSystem<EditorRenderSystem>(1, m_Renderer, m_RenderFrame);
|
||||
@@ -30,6 +30,7 @@ EditorSystem::EditorSystem(World* world, EventBroker* eventBroker, IRenderer* re
|
||||
m_EditorGUI->SetEntityDeleteCallback(std::bind(&EditorSystem::OnEntityDelete, this, std::placeholders::_1));
|
||||
m_EditorGUI->SetEntityChangeParentCallback(std::bind(&EditorSystem::OnEntityChangeParent, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_EditorGUI->SetEntityChangeNameCallback(std::bind(&EditorSystem::OnEntityChangeName, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_EditorGUI->SetEntityPasteCallback(std::bind(&EditorSystem::OnEntityPaste, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_EditorGUI->SetComponentAttachCallback(std::bind(&EditorSystem::OnComponentAttach, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_EditorGUI->SetComponentDeleteCallback(std::bind(&EditorSystem::OnComponentDelete, this, std::placeholders::_1, std::placeholders::_2));
|
||||
m_EditorGUI->SetWidgetModeCallback(std::bind(&EditorSystem::setWidgetMode, this, std::placeholders::_1));
|
||||
@@ -42,8 +43,11 @@ EditorSystem::EditorSystem(World* world, EventBroker* eventBroker, IRenderer* re
|
||||
|
||||
m_EditorStats = new EditorStats();
|
||||
|
||||
m_Enabled = ResourceManager::Load<ConfigFile>("Config.ini")->Get<bool>("Debug.EditorEnabled", false);
|
||||
if (m_Enabled) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,9 +106,9 @@ void EditorSystem::Enable()
|
||||
}
|
||||
|
||||
// Pause the world we're editing
|
||||
Events::Pause ePause;
|
||||
ePause.World = m_World;
|
||||
m_EventBroker->Publish(ePause);
|
||||
//Events::Pause ePause;
|
||||
//ePause.World = m_World;
|
||||
//m_EventBroker->Publish(ePause);
|
||||
|
||||
m_Enabled = true;
|
||||
}
|
||||
@@ -159,6 +163,11 @@ void EditorSystem::OnEntityChangeName(EntityWrapper entity, const std::string& n
|
||||
}
|
||||
}
|
||||
|
||||
EntityWrapper EditorSystem::OnEntityPaste(EntityWrapper entityToCopy, EntityWrapper parent)
|
||||
{
|
||||
return entityToCopy.Clone(parent);
|
||||
}
|
||||
|
||||
void EditorSystem::OnComponentAttach(EntityWrapper entity, const std::string& componentType)
|
||||
{
|
||||
if (entity.Valid()) {
|
||||
@@ -224,6 +233,12 @@ bool EditorSystem::OnInputCommand(const Events::InputCommand& e)
|
||||
Enable();
|
||||
}
|
||||
}
|
||||
if (e.Command == "PerformanceTimingResetAllTimers" && e.Value > 0) {
|
||||
PerformanceTimer::ResetAllTimers();
|
||||
}
|
||||
if (e.Command == "PerformanceTimingCreateExcelData" && e.Value > 0) {
|
||||
PerformanceTimer::CreateExcelData();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Editor/EditorWidgetSystem.h"
|
||||
|
||||
EditorWidgetSystem::EditorWidgetSystem(World* world, EventBroker* eventBroker, IRenderer* renderer)
|
||||
: System(world, eventBroker)
|
||||
EditorWidgetSystem::EditorWidgetSystem(SystemParams params, IRenderer* renderer)
|
||||
: System(params)
|
||||
, PureSystem("EditorWidget")
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "GUI/ButtonSystem.h"
|
||||
|
||||
ButtonSystem::ButtonSystem(SystemParams params, IRenderer* renderer)
|
||||
: System(params)
|
||||
, PureSystem("Button")
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &ButtonSystem::OnMousePress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &ButtonSystem::OnMouseRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseLock, &ButtonSystem::OnMouseLock);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseUnlock, &ButtonSystem::OnMouseUnlock);
|
||||
}
|
||||
|
||||
|
||||
bool ButtonSystem::OnMouseLock(const Events::LockMouse& e)
|
||||
{
|
||||
m_MouseIsLocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ButtonSystem::OnMouseUnlock(const Events::UnlockMouse& e)
|
||||
{
|
||||
m_MouseIsLocked = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ButtonSystem::OnMousePress(const Events::MousePress& e)
|
||||
{
|
||||
if (e.Button == GLFW_MOUSE_BUTTON_1 && !m_MouseIsLocked) {
|
||||
m_PickData = m_Renderer->Pick(glm::vec2(e.X, e.Y));
|
||||
if (m_PickData.Entity != EntityID_Invalid && m_PickData.World == m_World) {
|
||||
if(m_World->HasComponent(m_PickData.Entity, "Button")) {
|
||||
//Entity is a button, save it and send pressed event.
|
||||
|
||||
m_PickEntity = EntityWrapper(m_World, m_PickData.Entity);
|
||||
|
||||
//You have clicked on a button entity, send pressed event.
|
||||
Events::ButtonPressed ePressed;
|
||||
ePressed.Entity = m_PickEntity;
|
||||
ePressed.EntityName = m_PickEntity.Name();
|
||||
m_EventBroker->Publish(ePressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ButtonSystem::OnMouseRelease(const Events::MouseRelease& e)
|
||||
{
|
||||
if(!m_MouseIsLocked) {
|
||||
//Mouse is not locked, send release event.
|
||||
m_PickData = m_Renderer->Pick(glm::vec2(e.X, e.Y));
|
||||
if(m_PickData.Entity != EntityID_Invalid && m_PickData.World == m_World) {
|
||||
EntityWrapper ent = EntityWrapper(m_World, m_PickData.Entity);
|
||||
|
||||
Events::ButtonReleased eReleased;
|
||||
eReleased.EntityName = m_PickEntity.Name();
|
||||
eReleased.Entity = m_PickEntity;
|
||||
m_EventBroker->Publish(eReleased);
|
||||
|
||||
if(m_World->HasComponent(m_PickData.Entity, "Button")) {
|
||||
if (ent == m_PickEntity) {
|
||||
//The entity you released the mouse button on is the same as you pressed it on. "Clicked"
|
||||
Events::ButtonClicked eClicked;
|
||||
eClicked.Entity = m_PickEntity;
|
||||
eClicked.EntityName = m_PickEntity.Name();
|
||||
m_EventBroker->Publish(eClicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ButtonSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cHealth, double dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "GUI/MainMenuSystem.h"
|
||||
|
||||
MainMenuSystem::MainMenuSystem(SystemParams params, IRenderer* renderer)
|
||||
: System(params)
|
||||
, ImpureSystem()
|
||||
, m_Renderer(renderer)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPressed, &MainMenuSystem::OnButtonPress);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EReleased, &MainMenuSystem::OnButtonRelease);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EClicked, &MainMenuSystem::OnButtonClick);
|
||||
}
|
||||
|
||||
void MainMenuSystem::Update(double dt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnButtonClick(const Events::ButtonClicked& e)
|
||||
{
|
||||
if(e.EntityName == "Play") {
|
||||
//Run play code
|
||||
} else if(e.EntityName == "Connect") {
|
||||
//Run connect code
|
||||
} else if(e.EntityName == "Host") {
|
||||
//Run host code
|
||||
} else if(e.EntityName == "Quit") {
|
||||
printf("No, you stay");
|
||||
} else if (e.EntityName == "Res1080") {
|
||||
glfwSetWindowSize(m_Renderer->Window(), 1920, 1080);
|
||||
printf("\n1080");
|
||||
} else if (e.EntityName == "Res720") {
|
||||
glfwSetWindowSize(m_Renderer->Window(), 1280, 720);
|
||||
glViewport(0, 0, 1280, 720);
|
||||
printf("\n720");
|
||||
} else if (e.EntityName == "Res480") {
|
||||
glfwSetWindowSize(m_Renderer->Window(), 854, 480);
|
||||
glViewport(0, 0, 854, 480);
|
||||
printf("\n480");
|
||||
} else if (e.EntityName == "FullScreen") {
|
||||
printf("No fullscreen for now");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnButtonRelease(const Events::ButtonReleased& e)
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainMenuSystem::OnButtonPress(const Events::ButtonPressed& e)
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+340
-134
@@ -1,84 +1,118 @@
|
||||
#include "Network/Client.h"
|
||||
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
|
||||
Client::Client(ConfigFile* config) : m_Socket(m_IOService)
|
||||
Client::Client(World* world, EventBroker* eventBroker)
|
||||
: Network(world, eventBroker)
|
||||
{
|
||||
Network::initialize();
|
||||
|
||||
// Asumes root node is EntityID_Invalid
|
||||
insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid);
|
||||
// Init timer
|
||||
m_TimeSinceSentInputs = std::clock();
|
||||
// Default is local host
|
||||
std::string address = config->Get<std::string>("Networking.Address", "127.0.0.1");
|
||||
int port = config->Get<int>("Networking.Port", 27666);
|
||||
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
|
||||
// Set up network stream
|
||||
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_PlayerName = config->Get<std::string>("Networking.Name", "Raptorcopter");
|
||||
m_SendInputIntervalMs = config->Get<int>("Networking.SendInputIntervalMs", 33);
|
||||
LOG_INFO("Client initialized");
|
||||
|
||||
m_ServerlistRequest.Connect(m_PlayerName, "192.168.1.255", 32554);
|
||||
}
|
||||
|
||||
Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr<SnapshotFilter> snapshotFilter)
|
||||
: Client(world, eventBroker)
|
||||
{
|
||||
m_SnapshotFilter = std::move(snapshotFilter);
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{ }
|
||||
|
||||
void Client::Start(World* world, EventBroker* eventBroker)
|
||||
// Need to call connect at start
|
||||
void Client::Connect(std::string address, int port)
|
||||
{
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
|
||||
// Subscribe to events
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Client::OnPlayerDamage);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Client::OnPlayerSpawned);
|
||||
|
||||
m_Socket.connect(m_ReceiverEndpoint);
|
||||
LOG_INFO("I am client. BIP BOP");
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EDoubleJump, &Client::OnDoubleJump);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESearchForServers, &Client::OnSearchForServers);
|
||||
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_Address = address;
|
||||
if (address.empty()) {
|
||||
m_Address = config->Get<std::string>("Networking.Address", "127.0.0.1");
|
||||
}
|
||||
m_Port = port;
|
||||
if (port == 0) {
|
||||
m_Port = config->Get<int>("Networking.Port", 27666);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::Update()
|
||||
{
|
||||
m_EventBroker->Process<Client>();
|
||||
readFromServer();
|
||||
while (m_Unreliable.IsSocketAvailable()) {
|
||||
// Packet will get real data in receive
|
||||
Packet packet(MessageType::Invalid);
|
||||
m_Unreliable.Receive(packet);
|
||||
if (packet.GetMessageType() == MessageType::Connect) {
|
||||
parseUDPConnect(packet);
|
||||
} else {
|
||||
parseMessageType(packet);
|
||||
}
|
||||
}
|
||||
while (m_Reliable.IsSocketAvailable()) {
|
||||
// Packet will get real data in receive
|
||||
Packet packet(MessageType::Invalid);
|
||||
m_Reliable.Receive(packet);
|
||||
if (packet.GetMessageType() == MessageType::Connect) {
|
||||
parseTCPConnect(packet);
|
||||
} else {
|
||||
parseMessageType(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (m_ServerlistRequest.IsSocketAvailable()) {
|
||||
Packet packet(MessageType::Invalid);
|
||||
m_ServerlistRequest.Receive(packet);
|
||||
if (packet.GetMessageType() == MessageType::ServerlistRequest) {
|
||||
parseServerlist(packet);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SearchingForServers) {
|
||||
if (m_SearchingTime < (1000* (std::clock() - m_StartSearchTime) / (double)CLOCKS_PER_SEC)) {
|
||||
m_SearchingForServers = false;
|
||||
displayServerlist();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_IsConnected) {
|
||||
hasServerTimedOut();
|
||||
// Don't sent 1 input in 1 packet, bunch em up.
|
||||
// Don't send 1 input in 1 packet, bunch em up.
|
||||
if (m_SendInputIntervalMs < (1000 * (std::clock() - m_TimeSinceSentInputs) / (double)CLOCKS_PER_SEC)) {
|
||||
sendInputCommands();
|
||||
m_TimeSinceSentInputs = std::clock();
|
||||
}
|
||||
// HACK: Send absolute player positions for now to avoid desync until we have reliable messages
|
||||
sendLocalPlayerTransform();
|
||||
}
|
||||
Network::Update();
|
||||
}
|
||||
|
||||
void Client::readFromServer()
|
||||
{
|
||||
while (m_Socket.available()) {
|
||||
bytesRead = receive(readBuf);
|
||||
if (bytesRead > 0) {
|
||||
Packet packet(readBuf, bytesRead);
|
||||
parseMessageType(packet);
|
||||
}
|
||||
hasServerTimedOut();
|
||||
}
|
||||
//Network::Update();
|
||||
}
|
||||
|
||||
void Client::parseMessageType(Packet& packet)
|
||||
{
|
||||
// Pop packetSize which is used by TCP Client to
|
||||
// create a packet of the correct size
|
||||
packet.ReadPrimitive<int>();
|
||||
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();
|
||||
//identifyPacketLoss();
|
||||
|
||||
switch (static_cast<MessageType>(messageType)) {
|
||||
case MessageType::Connect:
|
||||
parseConnect(packet);
|
||||
break;
|
||||
case MessageType::Ping:
|
||||
parsePing();
|
||||
break;
|
||||
@@ -104,17 +138,43 @@ void Client::parseMessageType(Packet& packet)
|
||||
case MessageType::ComponentDeleted:
|
||||
parseComponentDeletion(packet);
|
||||
break;
|
||||
case MessageType::OnPlayerDamage:
|
||||
parsePlayerDamage(packet);
|
||||
break;
|
||||
case MessageType::OnDoubleJump:
|
||||
parseDoubleJump(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseConnect(Packet& packet)
|
||||
void Client::parseUDPConnect(Packet& packet)
|
||||
{
|
||||
// Map ServerEntityID and your PlayerID
|
||||
LOG_INFO("I be connected PogChamp");
|
||||
}
|
||||
|
||||
void Client::parseTCPConnect(Packet& packet)
|
||||
{
|
||||
LOG_INFO("Received TCP connect from server");
|
||||
// Pop size of message int
|
||||
packet.ReadPrimitive<int>();
|
||||
int messageType = packet.ReadPrimitive<int>();
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
// parse player id and other stuff
|
||||
m_PlayerID = packet.ReadPrimitive<int>();
|
||||
m_PlayerID = packet.ReadPrimitive<int>();
|
||||
LOG_INFO("A Player connected");
|
||||
Packet UnreliablePacket(MessageType::Connect, m_SendPacketID);
|
||||
// Add player id and other stuff
|
||||
packet.WritePrimitive(m_PlayerID);
|
||||
m_Unreliable.Send(packet);
|
||||
LOG_INFO("Sent UDP Connect Server");
|
||||
}
|
||||
|
||||
void Client::parsePlayerConnected(Packet & packet)
|
||||
{
|
||||
// Map ServerEntityID and other player's PlayerID
|
||||
@@ -132,7 +192,23 @@ void Client::parsePing()
|
||||
|
||||
Packet packet(MessageType::Ping, m_SendPacketID);
|
||||
packet.WriteString("Ping recieved");
|
||||
send(packet);
|
||||
m_Reliable.Send(packet);
|
||||
}
|
||||
|
||||
|
||||
void Client::parseServerlist(Packet& packet)
|
||||
{
|
||||
// Pop size, message type, and ID
|
||||
packet.ReadPrimitive<int>();
|
||||
packet.ReadPrimitive<int>();
|
||||
packet.ReadPrimitive<int>();
|
||||
std::string address = packet.ReadString();
|
||||
int port = packet.ReadPrimitive<int>();
|
||||
std::string serverName = packet.ReadString();
|
||||
int playersConnected = packet.ReadPrimitive<int>();
|
||||
//TODO: This should not happen when a client is connected to a server
|
||||
|
||||
m_Serverlist.push_back({ address, port, serverName, playersConnected });
|
||||
}
|
||||
|
||||
void Client::parseKick()
|
||||
@@ -141,14 +217,41 @@ void Client::parseKick()
|
||||
m_IsConnected = false;
|
||||
}
|
||||
|
||||
void Client::parseSpawnEvents()
|
||||
{
|
||||
std::vector<Events::PlayerSpawned> tempSpawn;
|
||||
for (int i = 0; i < m_PlayerSpawnEvents.size(); i++) {
|
||||
Events::PlayerSpawned e;
|
||||
if (!serverClientMapsHasEntity(m_PlayerSpawnEvents.at(i).Player.ID)) {
|
||||
tempSpawn.push_back(m_PlayerSpawnEvents.at(i));
|
||||
continue;
|
||||
}
|
||||
e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(m_PlayerSpawnEvents.at(i).Player.ID));
|
||||
//e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID.at(m_PlayerSpawnEvents.at(i).Spawner.ID));
|
||||
e.PlayerID = -1;
|
||||
e.PlayerName = m_PlayerSpawnEvents.at(i).PlayerName;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
m_PlayerSpawnEvents = tempSpawn;
|
||||
// m_PlayerSpawnEvents.clear();
|
||||
}
|
||||
|
||||
void Client::parsePlayersSpawned(Packet& packet)
|
||||
{
|
||||
//Events::PlayerSpawned e;
|
||||
//e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive<EntityID>()]);
|
||||
//e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive<EntityID>()]);
|
||||
//e.PlayerID = -1;
|
||||
//e.PlayerName = packet.ReadString();
|
||||
//m_EventBroker->Publish(e);
|
||||
|
||||
Events::PlayerSpawned e;
|
||||
e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive<EntityID>()]);
|
||||
e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive<EntityID>()]);
|
||||
e.Player = EntityWrapper(m_World, packet.ReadPrimitive<EntityID>());
|
||||
e.Spawner = EntityWrapper(m_World, packet.ReadPrimitive<EntityID>());
|
||||
e.PlayerID = -1;
|
||||
e.PlayerName = packet.ReadString();
|
||||
m_EventBroker->Publish(e);
|
||||
m_PlayerSpawnEvents.push_back(e);
|
||||
parseSpawnEvents();
|
||||
}
|
||||
|
||||
void Client::parseEntityDeletion(Packet & packet)
|
||||
@@ -158,8 +261,14 @@ void Client::parseEntityDeletion(Packet & packet)
|
||||
if (m_ServerIDToClientID.find(entityToDelete) != m_ServerIDToClientID.end()) {
|
||||
EntityID localEntity = m_ServerIDToClientID.at(entityToDelete);
|
||||
if (m_World->ValidEntity(localEntity)) {
|
||||
m_World->DeleteEntity(localEntity);
|
||||
deleteFromServerClientMaps(entityToDelete, localEntity);
|
||||
if (m_World->HasComponent(localEntity,"Player")) {
|
||||
Events::PlayerDeath e;
|
||||
e.Player = EntityWrapper(m_World, localEntity);
|
||||
m_EventBroker->Publish(e);
|
||||
} else {
|
||||
m_World->DeleteEntity(localEntity);
|
||||
deleteFromServerClientMaps(entityToDelete, localEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,40 +282,83 @@ void Client::parseComponentDeletion(Packet & packet)
|
||||
}
|
||||
}
|
||||
|
||||
// Fields with strings will not work right now
|
||||
void Client::InterpolateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
|
||||
void Client::parseDoubleJump(Packet & packet)
|
||||
{
|
||||
int sizeOfFields = 0;
|
||||
for (auto field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
sizeOfFields += fieldInfo.Stride;
|
||||
EntityID serverID = packet.ReadPrimitive<EntityID>();
|
||||
if (!serverClientMapsHasEntity(serverID)) {
|
||||
return;
|
||||
}
|
||||
Events::DoubleJump e;
|
||||
e.entityID = m_ServerIDToClientID.at(serverID);
|
||||
// If player is local player do not publish to prevent infinite feedback loop
|
||||
if (e.entityID != m_LocalPlayer.ID) {
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
// Is the size correct?
|
||||
boost::shared_array<char> eventData(new char[componentInfo.Stride]);
|
||||
memcpy(eventData.get(), packet.ReadData(componentInfo.Stride), componentInfo.Stride);
|
||||
//Send event to interpolat system
|
||||
Events::Interpolate e;
|
||||
e.Entity = entityID;
|
||||
e.DataArray = eventData;
|
||||
m_EventBroker->Publish(e);
|
||||
|
||||
}
|
||||
|
||||
void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
|
||||
void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID)
|
||||
{
|
||||
for (auto field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
if (fieldInfo.Type == "string") {
|
||||
std::string& value = packet.ReadString();
|
||||
m_World->GetComponent(entityID, componentType)[fieldInfo.Name] = value;
|
||||
m_World->GetComponent(entityID, componentInfo.Name)[fieldInfo.Name] = value;
|
||||
} else {
|
||||
memcpy(m_World->GetComponent(entityID, componentType).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
|
||||
memcpy(m_World->GetComponent(entityID, componentInfo.Name).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedComponentWrapper Client::createSharedComponent(Packet& packet, EntityID entityID, const ComponentInfo& componentInfo)
|
||||
{
|
||||
// Create shared allocation
|
||||
char* data = new char[sizeof(EntityID) + componentInfo.Stride];
|
||||
// Copy entity ID to start of data buffer
|
||||
memcpy(data, &entityID, sizeof(EntityID));
|
||||
// Read and copy fields
|
||||
for (auto& field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
if (fieldInfo.Type == "string") {
|
||||
new (data + sizeof(EntityID) + fieldInfo.Offset) std::string(packet.ReadString());
|
||||
} else {
|
||||
memcpy(data + sizeof(EntityID) + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
|
||||
return SharedComponentWrapper(componentInfo, boost::shared_array<char>(data));
|
||||
}
|
||||
|
||||
void Client::ignoreFields(Packet& packet, const ComponentInfo& componentInfo)
|
||||
{
|
||||
for (auto field : componentInfo.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
|
||||
if (fieldInfo.Type == "string") {
|
||||
packet.ReadString();
|
||||
} else {
|
||||
packet.ReadData(fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::parseSnapshot(Packet& packet)
|
||||
{
|
||||
// Read input commands
|
||||
std::size_t numInputCommands = packet.ReadPrimitive<std::size_t>();
|
||||
for (std::size_t i = 0; i < numInputCommands; ++i) {
|
||||
Events::InputCommand e;
|
||||
e.PlayerID = packet.ReadPrimitive<EntityID>();
|
||||
EntityID player = packet.ReadPrimitive<EntityID>();
|
||||
std::string command = packet.ReadString();
|
||||
float value = packet.ReadPrimitive<float>();
|
||||
if (m_ServerIDToClientID.find(player) != m_ServerIDToClientID.end()) {
|
||||
e.Player = EntityWrapper(m_World, m_ServerIDToClientID.at(player));
|
||||
e.Command = command;
|
||||
e.Value = value;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Read world state
|
||||
while (packet.DataReadSize() < packet.Size()) {
|
||||
EntityID serverEntityID = packet.ReadPrimitive<EntityID>();
|
||||
EntityID serverParentID = packet.ReadPrimitive<EntityID>();
|
||||
@@ -214,26 +366,33 @@ void Client::parseSnapshot(Packet& packet)
|
||||
int ammountOfComponents = packet.ReadPrimitive<int>();
|
||||
for (int i = 0; i < ammountOfComponents; i++) {
|
||||
std::string componentType = packet.ReadString();
|
||||
ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
|
||||
const ComponentInfo& componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
|
||||
if (serverClientMapsHasEntity(serverEntityID)) {
|
||||
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
|
||||
EntityWrapper localEntity(m_World, localEntityID);
|
||||
// Update entity
|
||||
if (m_World->HasComponent(localEntityID, componentType)) {
|
||||
// Update component
|
||||
if (componentType == "Transform") {
|
||||
// Interpolate only transform components
|
||||
InterpolateFields(packet, componentInfo, localEntityID, componentType);
|
||||
} else if (componentType == "Physics" && m_World->HasComponent(localEntityID, "Player")) {
|
||||
// HACK: Ignore velocity of physics
|
||||
packet.ReadData(componentInfo.Stride);
|
||||
} else {
|
||||
// Set component values
|
||||
updateFields(packet, componentInfo, localEntityID, componentType);
|
||||
// TODO Fix memory leak here
|
||||
SharedComponentWrapper newComponent = createSharedComponent(packet, localEntityID, componentInfo);
|
||||
bool shouldApply = true;
|
||||
// Apply potential filter function
|
||||
if (m_SnapshotFilter != nullptr) {
|
||||
shouldApply = m_SnapshotFilter->FilterComponent(localEntity, newComponent);
|
||||
}
|
||||
if (shouldApply) {
|
||||
ComponentWrapper currentComponent = m_World->GetComponent(localEntityID, componentType);
|
||||
memcpy(currentComponent.Data, newComponent.Data, componentInfo.Stride);
|
||||
}
|
||||
|
||||
//if (localEntity != m_LocalPlayer && !localEntity.IsChildOf(m_LocalPlayer)) {
|
||||
// updateFields(packet, componentInfo, localEntityID);
|
||||
//} else {
|
||||
// ignoreFields(packet, componentInfo);
|
||||
//}
|
||||
} else {
|
||||
// Has entity but no component
|
||||
m_World->AttachComponent(localEntityID, componentType);
|
||||
updateFields(packet, componentInfo, localEntityID, componentType);
|
||||
updateFields(packet, componentInfo, localEntityID);
|
||||
}
|
||||
} else {
|
||||
// Create Entity and component
|
||||
@@ -241,78 +400,56 @@ void Client::parseSnapshot(Packet& packet)
|
||||
if (serverParentID == EntityID_Invalid) {
|
||||
newLocalEntityID = m_World->CreateEntity(EntityID_Invalid);
|
||||
} else {
|
||||
newLocalEntityID = m_World->CreateEntity(m_ServerIDToClientID.at(serverParentID));
|
||||
if (serverClientMapsHasEntity(serverParentID)) {
|
||||
newLocalEntityID = m_World->CreateEntity(m_ServerIDToClientID.at(serverParentID));
|
||||
} else {
|
||||
newLocalEntityID = m_World->CreateEntity(EntityID_Invalid);
|
||||
}
|
||||
}
|
||||
m_World->SetName(newLocalEntityID, serverEntityName);
|
||||
insertIntoServerClientMaps(serverEntityID, newLocalEntityID);
|
||||
m_World->AttachComponent(newLocalEntityID, componentType);
|
||||
updateFields(packet, componentInfo, newLocalEntityID, componentType);
|
||||
updateFields(packet, componentInfo, newLocalEntityID);
|
||||
}
|
||||
}
|
||||
// Parent logic
|
||||
// This should be enough beacause we know that the entities arives in pre-order (there will always be a parent)
|
||||
if (serverParentID != EntityID_Invalid) {
|
||||
if (serverParentID != EntityID_Invalid && serverClientMapsHasEntity(serverParentID)) {
|
||||
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
|
||||
m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID));
|
||||
if (m_World->GetParent(localEntityID) != m_ServerIDToClientID.at(serverParentID)) {
|
||||
m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Client::receive(char* data)
|
||||
{
|
||||
boost::system::error_code error;
|
||||
|
||||
size_t bytesReceived = m_Socket.receive_from(boost
|
||||
::asio::buffer((void*)data, INPUTSIZE),
|
||||
m_ReceiverEndpoint,
|
||||
0, error);
|
||||
// Network Debug data
|
||||
if (isReadingData) {
|
||||
m_NetworkData.TotalDataReceived += bytesReceived;
|
||||
m_NetworkData.DataReceivedThisInterval += bytesReceived;
|
||||
m_NetworkData.AmountOfMessagesReceived++;
|
||||
}
|
||||
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);
|
||||
// Network Debug data
|
||||
if (isReadingData) {
|
||||
m_NetworkData.TotalDataSent += packet.Size();
|
||||
m_NetworkData.DataSentThisInterval += packet.Size();
|
||||
m_NetworkData.AmountOfMessagesSent++;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::connect()
|
||||
{
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString(m_PlayerName);
|
||||
m_StartPingTime = std::clock();
|
||||
send(packet);
|
||||
parseSpawnEvents();
|
||||
}
|
||||
|
||||
void Client::disconnect()
|
||||
{
|
||||
m_IsConnected = false;
|
||||
m_PreviousPacketID = 0;
|
||||
m_PacketID = 0;
|
||||
Packet packet(MessageType::Disconnect, m_SendPacketID);
|
||||
send(packet);
|
||||
m_Reliable.Send(packet);
|
||||
m_Reliable.Disconnect();
|
||||
}
|
||||
|
||||
bool Client::OnInputCommand(const Events::InputCommand & e)
|
||||
{
|
||||
// TEMP
|
||||
if (e.Command == "SearchForServers" && e.Value > 0) {
|
||||
Events::SearchForServers e;
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
|
||||
if (e.PlayerID != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.Command == "ConnectToServer") { // Connect for now
|
||||
if (e.Value > 0) {
|
||||
connect();
|
||||
m_Reliable.Connect(m_PlayerName, m_Address, m_Port);
|
||||
m_Unreliable.Connect(m_PlayerName, m_Address, m_Port);
|
||||
}
|
||||
//LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID);
|
||||
return true;
|
||||
@@ -335,7 +472,9 @@ bool Client::OnInputCommand(const Events::InputCommand & e)
|
||||
m_SaveDataTimer = std::clock();
|
||||
}
|
||||
} else {
|
||||
m_InputCommandBuffer.push_back(e);
|
||||
if (m_IsConnected) {
|
||||
m_InputCommandBuffer.push_back(e);
|
||||
}
|
||||
//LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID);
|
||||
return true;
|
||||
}
|
||||
@@ -344,11 +483,22 @@ bool Client::OnInputCommand(const Events::InputCommand & e)
|
||||
|
||||
bool Client::OnPlayerDamage(const Events::PlayerDamage & e)
|
||||
{
|
||||
if (e.Inflictor != m_LocalPlayer) {
|
||||
return false;
|
||||
}
|
||||
// Could this happen?
|
||||
//if (!clientServerMapsHasEntity(e.Inflictor.ID)
|
||||
// || !clientServerMapsHasEntity(e.Victim.ID)) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
Packet packet(MessageType::OnPlayerDamage, m_SendPacketID);
|
||||
packet.WritePrimitive(m_ClientIDToServerID.at(e.Inflictor.ID));
|
||||
packet.WritePrimitive(m_ClientIDToServerID.at(e.Victim.ID));
|
||||
packet.WritePrimitive(e.Damage);
|
||||
packet.WritePrimitive(m_ClientIDToServerID.at(e.Player.ID));
|
||||
send(packet);
|
||||
return false;
|
||||
m_Reliable.Send(packet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e)
|
||||
@@ -359,23 +509,72 @@ bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Client::OnSearchForServers(const Events::SearchForServers& e)
|
||||
{
|
||||
m_SearchingForServers = true;
|
||||
m_StartSearchTime = std::clock();
|
||||
m_Serverlist.clear();
|
||||
LOG_INFO("Searching for LAN servers...\n");
|
||||
Packet packet(MessageType::ServerlistRequest);
|
||||
m_ServerlistRequest.Broadcast(packet, 13); // TODO: Config
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::parsePlayerDamage(Packet& packet)
|
||||
{
|
||||
Events::PlayerDamage e;
|
||||
PlayerID victimID = packet.ReadPrimitive<EntityID>();
|
||||
PlayerID inflictorID = packet.ReadPrimitive<EntityID>();
|
||||
if (!serverClientMapsHasEntity(victimID) || !serverClientMapsHasEntity(inflictorID)) {
|
||||
return;
|
||||
}
|
||||
e.Inflictor = EntityWrapper(m_World, m_ServerIDToClientID.at(victimID));
|
||||
e.Victim = EntityWrapper(m_World, m_ServerIDToClientID.at(inflictorID));
|
||||
e.Damage = packet.ReadPrimitive<double>();
|
||||
// Don't rebroadcast our own player damage events or we'll have an infinite loop!
|
||||
if (e.Inflictor != m_LocalPlayer) {
|
||||
m_EventBroker->Publish(e);
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::OnDoubleJump(Events::DoubleJump & e)
|
||||
{
|
||||
if (!clientServerMapsHasEntity(e.entityID) || e.entityID != m_LocalPlayer.ID) {
|
||||
return false;
|
||||
}
|
||||
Packet packet(MessageType::OnDoubleJump);
|
||||
packet.WritePrimitive(m_ClientIDToServerID.at(e.entityID));
|
||||
m_Reliable.Send(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::sendLocalPlayerTransform()
|
||||
{
|
||||
if (!m_LocalPlayer.Valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Packet packet(MessageType::PlayerTransform, m_SendPacketID);
|
||||
|
||||
ComponentWrapper cTransform = m_LocalPlayer["Transform"];
|
||||
glm::vec3& position = cTransform["Position"];
|
||||
glm::vec3& orientation = cTransform["Orientation"];
|
||||
Packet packet(MessageType::PlayerTransform, m_SendPacketID);
|
||||
packet.WritePrimitive(position.x);
|
||||
packet.WritePrimitive(position.y);
|
||||
packet.WritePrimitive(position.z);
|
||||
packet.WritePrimitive(orientation.x);
|
||||
packet.WritePrimitive(orientation.y);
|
||||
packet.WritePrimitive(orientation.z);
|
||||
send(packet);
|
||||
|
||||
bool hasAssaultWeapon = m_LocalPlayer.HasComponent("AssaultWeapon");
|
||||
packet.WritePrimitive(hasAssaultWeapon);
|
||||
if (hasAssaultWeapon) {
|
||||
ComponentWrapper cAssaultWeapon = m_LocalPlayer["AssaultWeapon"];
|
||||
packet.WritePrimitive((int)cAssaultWeapon["MagazineAmmo"]);
|
||||
packet.WritePrimitive((int)cAssaultWeapon["Ammo"]);
|
||||
}
|
||||
|
||||
m_Unreliable.Send(packet);
|
||||
}
|
||||
|
||||
void Client::identifyPacketLoss()
|
||||
@@ -387,17 +586,15 @@ void Client::identifyPacketLoss()
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::hasServerTimedOut()
|
||||
void Client::hasServerTimedOut()
|
||||
{
|
||||
// Time in ms
|
||||
double timeSincePing = 1000 * (std::clock() - m_StartPingTime) / static_cast<double>(CLOCKS_PER_SEC);
|
||||
if (timeSincePing > m_TimeoutMs) {
|
||||
// Clear everything and go to menu.
|
||||
LOG_INFO("Server has timed out, returning to menu, Beep Boop.");
|
||||
m_IsConnected = false;
|
||||
return true;
|
||||
disconnect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityID Client::createPlayer()
|
||||
@@ -418,7 +615,7 @@ void Client::sendInputCommands()
|
||||
packet.WriteString(m_InputCommandBuffer[i].Command);
|
||||
packet.WritePrimitive(m_InputCommandBuffer[i].Value);
|
||||
}
|
||||
send(packet);
|
||||
m_Reliable.Send(packet);
|
||||
m_InputCommandBuffer.clear();
|
||||
}
|
||||
}
|
||||
@@ -426,7 +623,17 @@ void Client::sendInputCommands()
|
||||
void Client::becomePlayer()
|
||||
{
|
||||
Packet packet = Packet(MessageType::BecomePlayer, m_SendPacketID);
|
||||
send(packet);
|
||||
m_Reliable.Send(packet);
|
||||
}
|
||||
|
||||
|
||||
void Client::displayServerlist()
|
||||
{
|
||||
LOG_INFO("This is a serverlist:\n");
|
||||
for (int i = 0; i < m_Serverlist.size(); i++) {
|
||||
ServerInfo si = m_Serverlist[i];
|
||||
LOG_INFO("%s:%i\t%s\t%i\n", si.Address.c_str(), si.Port, si.Name.c_str(), si.PlayersConnected);
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::clientServerMapsHasEntity(EntityID clientEntityID)
|
||||
@@ -457,7 +664,6 @@ void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID client
|
||||
{
|
||||
m_ServerIDToClientID.insert(std::make_pair(serverEntityID, clientEntityID));
|
||||
m_ClientIDToServerID.insert(std::make_pair(clientEntityID, serverEntityID));
|
||||
|
||||
}
|
||||
|
||||
void Client::deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID)
|
||||
|
||||
@@ -1,10 +1,34 @@
|
||||
#include "Network/Network.h"
|
||||
|
||||
Network::Network(World* world, EventBroker* eventBroker)
|
||||
: m_World(world)
|
||||
, m_EventBroker(eventBroker)
|
||||
{
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_MaxConnections = config->Get<int>("Networking.MaxConnections", 8);
|
||||
m_TimeoutMs = config->Get<int>("Networking.TimeoutMs", 20000);
|
||||
}
|
||||
|
||||
void Network::Update()
|
||||
{
|
||||
updateNetworkData();
|
||||
}
|
||||
|
||||
void Network::logSentData(int bytesSent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Network::logReceivedData(int bytesReceived)
|
||||
{
|
||||
// Network Debug data
|
||||
if (isReadingData) {
|
||||
m_NetworkData.TotalDataReceived += bytesReceived;
|
||||
m_NetworkData.DataReceivedThisInterval += bytesReceived;
|
||||
m_NetworkData.AmountOfMessagesReceived++;
|
||||
}
|
||||
}
|
||||
|
||||
void Network::saveToFile()
|
||||
{
|
||||
std::ofstream outfile;
|
||||
@@ -59,10 +83,3 @@ void Network::updateNetworkData()
|
||||
m_NetworkData.DataReceivedThisInterval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Network::initialize()
|
||||
{
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_MaxConnections = config->Get<int>("Networking.MaxConnections", 8);
|
||||
m_TimeoutMs = config->Get<int>("Networking.TimeoutMs", 20000);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "Network/NetworkClient.h"
|
||||
|
||||
NetworkClient::NetworkClient()
|
||||
{
|
||||
m_ReadBuffer = new char[m_BufferSize];
|
||||
}
|
||||
|
||||
NetworkClient::~NetworkClient()
|
||||
{
|
||||
delete[] m_ReadBuffer;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "Network/NetworkServer.h"
|
||||
|
||||
NetworkServer::NetworkServer()
|
||||
{
|
||||
m_ReadBuffer = new char[m_BufferSize];
|
||||
}
|
||||
|
||||
NetworkServer::~NetworkServer()
|
||||
{
|
||||
delete[] m_ReadBuffer;
|
||||
}
|
||||
@@ -34,10 +34,12 @@ void Packet::Init(MessageType type, unsigned int & packetID)
|
||||
m_ReturnDataOffset = 0;
|
||||
m_Offset = 0;
|
||||
// Create message header
|
||||
// allocate memory for size of packet(only used in tcp)
|
||||
WritePrimitive<int>(0);
|
||||
// Add message type
|
||||
int messageType = static_cast<int>(type);
|
||||
Packet::WritePrimitive<int>(messageType);
|
||||
Packet::WritePrimitive<int>(packetID);
|
||||
WritePrimitive<int>(messageType);
|
||||
WritePrimitive<int>(packetID);
|
||||
packetID++;
|
||||
m_HeaderSize = m_Offset;
|
||||
}
|
||||
@@ -56,9 +58,12 @@ void Packet::WriteString(const std::string& str)
|
||||
|
||||
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. New size is %i bytes\n", m_MaxPacketSize*2);
|
||||
resizeData();
|
||||
while (m_Offset + sizeOfData > m_MaxPacketSize) {
|
||||
resizeData();
|
||||
}
|
||||
}
|
||||
memcpy(m_Data + m_Offset, data, sizeOfData);
|
||||
m_Offset += sizeOfData;
|
||||
@@ -76,14 +81,35 @@ std::string Packet::ReadString()
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
char * Packet::ReadData(int SizeOfData)
|
||||
void Packet::ReconstructFromData(char * data, size_t sizeOfData)
|
||||
{
|
||||
if (m_Offset < m_ReturnDataOffset + SizeOfData) {
|
||||
if (sizeOfData > m_MaxPacketSize) {
|
||||
// Delete our data
|
||||
delete[] m_Data;
|
||||
// Set new max size
|
||||
m_MaxPacketSize = sizeOfData;
|
||||
m_Data = new char[m_MaxPacketSize];
|
||||
// while we resized the old data container.
|
||||
}
|
||||
memcpy(m_Data, data, sizeOfData);
|
||||
m_Offset = sizeOfData;
|
||||
|
||||
}
|
||||
|
||||
void Packet::UpdateSize()
|
||||
{
|
||||
int whatisoffset = m_Offset;
|
||||
memcpy(m_Data, &m_Offset, sizeof(int));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
size_t oldReturnDataOffset = m_ReturnDataOffset;
|
||||
m_ReturnDataOffset += SizeOfData;
|
||||
m_ReturnDataOffset += sizeOfData;
|
||||
return (m_Data + oldReturnDataOffset);
|
||||
}
|
||||
|
||||
@@ -91,25 +117,36 @@ void Packet::ChangePacketID(unsigned int & packetID)
|
||||
{
|
||||
packetID = packetID + 1;
|
||||
// Overwrite old PacketID
|
||||
memcpy(m_Data + sizeof(int), &packetID, sizeof(int));
|
||||
memcpy(m_Data + 2*sizeof(int), &packetID, sizeof(int));
|
||||
}
|
||||
|
||||
MessageType Packet::GetMessageType()
|
||||
{
|
||||
MessageType messagType;
|
||||
memcpy(&messagType, m_Data + sizeof(int), sizeof(int));
|
||||
return messagType;
|
||||
}
|
||||
|
||||
void Packet::resizeData()
|
||||
{
|
||||
resizeData(m_MaxPacketSize * 2);
|
||||
}
|
||||
|
||||
void Packet::resizeData(int size)
|
||||
{
|
||||
// Allocate memory to store our data in
|
||||
char* holdData = new char[m_MaxPacketSize];
|
||||
// Copy our data to the newly allocated memory
|
||||
memcpy(holdData, m_Data, m_Offset);
|
||||
// Increase max packet size
|
||||
m_MaxPacketSize = m_MaxPacketSize * 2;
|
||||
m_MaxPacketSize = size;
|
||||
// Delete our data
|
||||
delete m_Data;
|
||||
// Allocate twice the memory we had before
|
||||
delete[] m_Data;
|
||||
// Allocate memory
|
||||
m_Data = new char[m_MaxPacketSize];
|
||||
// Copy our data to new location
|
||||
memcpy(m_Data, holdData, m_Offset);
|
||||
// Delete the memory allocated to hold our data
|
||||
// while we resized the old data container.
|
||||
delete holdData;
|
||||
delete[] holdData;
|
||||
}
|
||||
|
||||
+378
-205
@@ -1,12 +1,25 @@
|
||||
#include "Network/Server.h"
|
||||
|
||||
Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666))
|
||||
Server::Server(World* world, EventBroker* eventBroker, int port)
|
||||
: Network(world, eventBroker)
|
||||
, m_ServerlistRequest(13)
|
||||
{
|
||||
Network::initialize();
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
snapshotInterval = 1000 * config->Get<float>("Networking.SnapshotInterval", 0.05f);
|
||||
pingIntervalMs = config->Get<float>("Networking.PingIntervalMs", 1000);
|
||||
// Subscribe to events
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Server::OnPlayerDamage);
|
||||
|
||||
// BindWW
|
||||
if (port == 0) {
|
||||
port = config->Get<float>("Networking.Port", 27666);
|
||||
}
|
||||
m_Port = port;
|
||||
LOG_INFO("Server initialized and bound to port %i", port);
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
@@ -14,46 +27,66 @@ Server::~Server()
|
||||
|
||||
}
|
||||
|
||||
void Server::Start(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
m_World = world;
|
||||
m_EventBroker = eventBroker;
|
||||
// Subscribe to events
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted);
|
||||
LOG_INFO("I am Server. BIP BOP\n");
|
||||
}
|
||||
|
||||
void Server::Update()
|
||||
{
|
||||
readFromClients();
|
||||
m_EventBroker->Process<Server>();
|
||||
if (isReadingData) {
|
||||
Network::Update();
|
||||
}
|
||||
m_Reliable.AcceptNewConnections(m_NextPlayerID, m_ConnectedPlayers);
|
||||
|
||||
}
|
||||
|
||||
void Server::readFromClients()
|
||||
{
|
||||
while (m_Socket.available()) {
|
||||
try {
|
||||
bytesRead = receive(readBuffer);
|
||||
Packet packet(readBuffer, bytesRead);
|
||||
parseMessageType(packet);
|
||||
} catch (const std::exception&) {
|
||||
//LOG_ERROR("%i: Read from client crashed %s", m_PacketID, err.what());
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
while (kv.second.TCPSocket->available()) {
|
||||
// Packet will get real data in receive
|
||||
Packet packet(MessageType::Invalid);
|
||||
m_Reliable.Receive(packet, kv.second);
|
||||
m_Address = kv.second.TCPSocket->remote_endpoint().address();
|
||||
m_Port = kv.second.TCPSocket->remote_endpoint().port();
|
||||
if (packet.GetMessageType() == MessageType::Connect) {
|
||||
parseTCPConnect(packet);
|
||||
} else {
|
||||
parseMessageType(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerDefinition pd;
|
||||
while (m_Unreliable.IsSocketAvailable()) {
|
||||
// Packet will get real data in receive
|
||||
Packet packet(MessageType::Invalid);
|
||||
m_Unreliable.Receive(packet, pd);
|
||||
m_Address = pd.Endpoint.address();
|
||||
m_Port = pd.Endpoint.port();
|
||||
if (packet.GetMessageType() == MessageType::Connect) {
|
||||
parseUDPConnect(packet);
|
||||
} else {
|
||||
parseMessageType(packet);
|
||||
}
|
||||
}
|
||||
|
||||
while (m_ServerlistRequest.IsSocketAvailable()) {
|
||||
Packet packet(MessageType::Invalid);
|
||||
PlayerDefinition localArea;
|
||||
localArea.Endpoint = boost::asio::ip::udp::endpoint();
|
||||
m_ServerlistRequest.Receive(packet, localArea);
|
||||
if (packet.GetMessageType() == MessageType::ServerlistRequest) {
|
||||
packet.ReadPrimitive<int>(); // Pop size
|
||||
packet.ReadPrimitive<int>(); // Pop MsgType
|
||||
packet.ReadPrimitive<int>(); // Pop packet ID
|
||||
int port = packet.ReadPrimitive<int>();
|
||||
std::string address = localArea.Endpoint.address().to_string();
|
||||
parseServerlistRequest(boost::asio::ip::udp::endpoint(boost::asio::ip::address().from_string(address), port));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if players have disconnected
|
||||
for (int i = 0; i < m_PlayersToDisconnect.size(); i++) {
|
||||
disconnect(m_PlayersToDisconnect.at(i));
|
||||
}
|
||||
m_PlayersToDisconnect.clear();
|
||||
|
||||
std::clock_t currentTime = std::clock();
|
||||
// Send snapshot
|
||||
if (snapshotInterval < (1000 * (currentTime - previousSnapshotMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
sendSnapshot();
|
||||
previousSnapshotMessage = currentTime;
|
||||
}
|
||||
|
||||
// Send pings each
|
||||
if (pingIntervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) {
|
||||
sendPing();
|
||||
@@ -65,19 +98,26 @@ void Server::readFromClients()
|
||||
checkForTimeOuts();
|
||||
timOutTimer = currentTime;
|
||||
}
|
||||
m_EventBroker->Process<Server>();
|
||||
if (isReadingData) {
|
||||
Network::Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseMessageType(Packet& packet)
|
||||
{
|
||||
int messageType = packet.ReadPrimitive<int>(); // Read what type off message was sent from server
|
||||
// Pop packetSize which is used by TCP Client to
|
||||
// create a packet of the correct size
|
||||
packet.ReadPrimitive<int>();
|
||||
|
||||
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);
|
||||
//parseConnect(packet);
|
||||
break;
|
||||
case MessageType::Ping:
|
||||
parsePing();
|
||||
@@ -98,65 +138,27 @@ void Server::parseMessageType(Packet& packet)
|
||||
case MessageType::PlayerTransform:
|
||||
parsePlayerTransform(packet);
|
||||
break;
|
||||
case MessageType::OnDoubleJump:
|
||||
parseDoubleJump(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Server::receive(char * data)
|
||||
{
|
||||
size_t length = m_Socket.receive_from(
|
||||
boost::asio::buffer((void*)data
|
||||
, INPUTSIZE)
|
||||
, m_ReceiverEndpoint, 0);
|
||||
// Network Debug data
|
||||
if (isReadingData) {
|
||||
m_NetworkData.TotalDataReceived += length;
|
||||
m_NetworkData.DataReceivedThisInterval += length;
|
||||
m_NetworkData.AmountOfMessagesReceived++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
void Server::send(PlayerID player, Packet& packet)
|
||||
{
|
||||
try {
|
||||
size_t bytesSent = m_Socket.send_to(
|
||||
boost::asio::buffer(packet.Data(), packet.Size()),
|
||||
m_ConnectedPlayers[player].Endpoint,
|
||||
0);
|
||||
// Network Debug data
|
||||
if (isReadingData) {
|
||||
m_NetworkData.TotalDataSent += packet.Size();
|
||||
m_NetworkData.DataSentThisInterval += packet.Size();
|
||||
m_NetworkData.AmountOfMessagesSent++;
|
||||
}
|
||||
} catch (const boost::system::system_error&) {
|
||||
// TODO: Clean up invalid endpoints out of m_ConnectedPlayers later
|
||||
m_ConnectedPlayers[player].Endpoint = boost::asio::ip::udp::endpoint();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::send(Packet & packet)
|
||||
{
|
||||
m_Socket.send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint,
|
||||
0);
|
||||
if (isReadingData) {
|
||||
// Network Debug data
|
||||
m_NetworkData.TotalDataSent += packet.Size();
|
||||
m_NetworkData.DataSentThisInterval += packet.Size();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::broadcast(Packet& packet)
|
||||
void Server::reliableBroadcast(Packet& packet)
|
||||
{
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
packet.ChangePacketID(kv.second.PacketID);
|
||||
send(kv.first, packet);
|
||||
m_Reliable.Send(packet, kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::unreliableBroadcast(Packet& packet)
|
||||
{
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
packet.ChangePacketID(kv.second.PacketID);
|
||||
m_Unreliable.Send(packet, kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,13 +166,75 @@ void Server::broadcast(Packet& packet)
|
||||
void Server::sendSnapshot()
|
||||
{
|
||||
Packet packet(MessageType::Snapshot);
|
||||
addChildrenToPacket(packet, EntityID_Invalid);
|
||||
broadcast(packet);
|
||||
addInputCommandsToPacket(packet);
|
||||
addPlayersToPacket(packet, EntityID_Invalid);
|
||||
unreliableBroadcast(packet);
|
||||
}
|
||||
|
||||
void Server::addInputCommandsToPacket(Packet& packet)
|
||||
{
|
||||
// Number of input commands
|
||||
packet.WritePrimitive(m_InputCommandsToBroadcast.size());
|
||||
for (auto& command : m_InputCommandsToBroadcast) {
|
||||
packet.WritePrimitive(command.PlayerID);
|
||||
packet.WritePrimitive(m_ConnectedPlayers.at(command.PlayerID).EntityID);
|
||||
packet.WriteString(command.Command);
|
||||
packet.WritePrimitive(command.Value);
|
||||
}
|
||||
m_InputCommandsToBroadcast.clear();
|
||||
}
|
||||
|
||||
void Server::addPlayersToPacket(Packet & packet, EntityID entityID)
|
||||
{
|
||||
auto itPair = m_World->GetDirectChildren(entityID);
|
||||
std::unordered_map<std::string, ComponentPool*> worldComponentPools = m_World->GetComponentPools();
|
||||
// Loop through every child
|
||||
for (auto it = itPair.first; it != itPair.second; it++) {
|
||||
EntityID childEntityID = it->second;
|
||||
// HACK: Only sync players for now, since the map turned out to be TOO LARGE to send in one snapshot and Simon's computer shits itself
|
||||
// HACK: Also checked CapturePointHUD for now. (this would get out of sync);
|
||||
EntityWrapper childEntity(m_World, childEntityID);
|
||||
if (shouldSendToClient(childEntity)) {
|
||||
// Write EntityID and parentsID and Entity name
|
||||
packet.WritePrimitive(childEntityID);
|
||||
packet.WritePrimitive(entityID);
|
||||
packet.WriteString(m_World->GetName(childEntityID));
|
||||
// Write components to child
|
||||
int numberOfComponents = 0;
|
||||
for (auto& i : worldComponentPools) {
|
||||
if (i.second->KnowsEntity(childEntityID)) {
|
||||
numberOfComponents++;
|
||||
}
|
||||
}
|
||||
// Write how many components should be read
|
||||
packet.WritePrimitive(numberOfComponents);
|
||||
for (auto& i : worldComponentPools) {
|
||||
// If the entity exist in the pool
|
||||
if (i.second->KnowsEntity(childEntityID)) {
|
||||
ComponentWrapper componentWrapper = i.second->GetByEntity(childEntityID);
|
||||
// ComponentType
|
||||
packet.WriteString(componentWrapper.Info.Name);
|
||||
// Loop through fields
|
||||
for (auto& componentField : componentWrapper.Info.FieldsInOrder) {
|
||||
ComponentInfo::Field_t fieldInfo = componentWrapper.Info.Fields.at(componentField);
|
||||
if (fieldInfo.Type == "string") {
|
||||
std::string& value = componentWrapper[componentField];
|
||||
packet.WriteString(value);
|
||||
} else {
|
||||
packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Go to to your children
|
||||
addPlayersToPacket(packet, childEntityID);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::addChildrenToPacket(Packet & packet, EntityID entityID)
|
||||
{
|
||||
auto itPair = m_World->GetChildren(entityID);
|
||||
auto itPair = m_World->GetDirectChildren(entityID);
|
||||
std::unordered_map<std::string, ComponentPool*> worldComponentPools = m_World->GetComponentPools();
|
||||
// Loop through every child
|
||||
for (auto it = itPair.first; it != itPair.second; it++) {
|
||||
@@ -226,24 +290,117 @@ void Server::sendPing()
|
||||
// Time message
|
||||
m_StartPingTime = std::clock();
|
||||
// Send message
|
||||
broadcast(packet);
|
||||
reliableBroadcast(packet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Server::checkForTimeOuts()
|
||||
{
|
||||
double startPing = 1000 * m_StartPingTime
|
||||
/ static_cast<double>(CLOCKS_PER_SEC);
|
||||
|
||||
for (int i = 0; i < m_ConnectedPlayers.size(); i++) {
|
||||
if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) {
|
||||
double stopPing = 1000 * m_ConnectedPlayers[i].StopTime /
|
||||
std::vector<PlayerID> playersToRemove;
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if (kv.second.TCPAddress != boost::asio::ip::address()) {
|
||||
int stopPing = 1000 * kv.second.StopTime /
|
||||
static_cast<double>(CLOCKS_PER_SEC);
|
||||
if (startPing > stopPing + m_TimeoutMs) {
|
||||
LOG_INFO("User %i timed out!", i);
|
||||
disconnect(i);
|
||||
LOG_INFO("User %i timed out!", kv.second.Name);
|
||||
playersToRemove.push_back(kv.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < playersToRemove.size(); i++) {
|
||||
disconnect(playersToRemove.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseUDPConnect(Packet & packet)
|
||||
{
|
||||
// Pop size of message int
|
||||
packet.ReadPrimitive<int>();
|
||||
int messageType = packet.ReadPrimitive<int>();
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
// parse player id and other stuff
|
||||
PlayerID playerID = packet.ReadPrimitive<int>();
|
||||
// Do something here?
|
||||
boost::asio::ip::udp::endpoint endpoint(m_Address, m_Port);
|
||||
m_ConnectedPlayers.at(playerID).Endpoint = endpoint;
|
||||
LOG_INFO("parseUDPConnect: Spectator \"%s\" connected on IP: %s", m_ConnectedPlayers.at(playerID).Name.c_str(), m_ConnectedPlayers.at(playerID).Endpoint.address().to_string().c_str());
|
||||
// Send a message to the player that connected
|
||||
Packet connnectPacket(MessageType::Connect, m_ConnectedPlayers.at(playerID).PacketID);
|
||||
m_Unreliable.Send(connnectPacket);
|
||||
LOG_INFO("UDP Connect sent to client");
|
||||
}
|
||||
|
||||
void Server::parseTCPConnect(Packet & packet)
|
||||
{
|
||||
// Pop size of message int
|
||||
packet.ReadPrimitive<int>();
|
||||
int messageType = packet.ReadPrimitive<int>();
|
||||
// Read packet ID
|
||||
m_PreviousPacketID = m_PacketID; // Set previous packet id
|
||||
m_PacketID = packet.ReadPrimitive<int>(); //Read new packet id
|
||||
|
||||
LOG_INFO("Parsing connections");
|
||||
// Check if player is already connected
|
||||
// Ska vara till lagd i TCPServer receive
|
||||
PlayerID playerID = GetPlayerIDFromEndpoint();
|
||||
if (playerID == -1) {
|
||||
return;
|
||||
}
|
||||
// Create a new player
|
||||
m_ConnectedPlayers.at(playerID).EntityID = 0; // Overlook this
|
||||
m_ConnectedPlayers.at(playerID).Name = packet.ReadString();
|
||||
m_ConnectedPlayers.at(playerID).PacketID = 0;
|
||||
m_ConnectedPlayers.at(playerID).StopTime = std::clock();
|
||||
m_ConnectedPlayers.at(playerID).TCPAddress = m_Address;
|
||||
m_ConnectedPlayers.at(playerID).TCPPort = m_Port;
|
||||
|
||||
LOG_INFO("parseTCPConnect: Spectator \"%s\" connected on IP: %s", m_ConnectedPlayers.at(playerID).Name.c_str(),
|
||||
m_ConnectedPlayers.at(playerID).TCPAddress.to_string().c_str());
|
||||
|
||||
// Send a message to the player that connected
|
||||
Packet connnectPacket(MessageType::Connect, m_ConnectedPlayers.at(playerID).PacketID);
|
||||
// Write playerID to packet
|
||||
connnectPacket.WritePrimitive(playerID);
|
||||
m_Reliable.Send(connnectPacket);
|
||||
|
||||
Packet firstSnapshot(MessageType::Snapshot);
|
||||
addInputCommandsToPacket(firstSnapshot);
|
||||
addChildrenToPacket(firstSnapshot, EntityID_Invalid);
|
||||
m_Reliable.Send(firstSnapshot);
|
||||
|
||||
// Send notification that a player has connected
|
||||
//Packet notificationPacket(MessageType::PlayerConnected);
|
||||
//broadcast(notificationPacket);
|
||||
}
|
||||
|
||||
void Server::parseDisconnect()
|
||||
{
|
||||
LOG_INFO("%i: Parsing disconnect", m_PacketID);
|
||||
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if (kv.second.TCPAddress == m_Address &&
|
||||
kv.second.TCPPort == m_Port) {
|
||||
m_PlayersToDisconnect.push_back(kv.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Server::parseServerlistRequest(boost::asio::ip::udp::endpoint endpoint)
|
||||
{
|
||||
Packet packet(MessageType::ServerlistRequest);
|
||||
packet.WriteString(m_Reliable.Address());
|
||||
packet.WritePrimitive<int>(m_Reliable.Port());
|
||||
packet.WriteString("SERVERNAME");
|
||||
packet.WritePrimitive<int>(m_ConnectedPlayers.size());
|
||||
m_ServerlistRequest.Send(packet);
|
||||
}
|
||||
|
||||
void Server::disconnect(PlayerID playerID)
|
||||
@@ -252,109 +409,28 @@ void Server::disconnect(PlayerID playerID)
|
||||
LOG_INFO("User %s disconnected/timed out", m_ConnectedPlayers[playerID].Name.c_str());
|
||||
// Remove enteties and stuff (When we can remove entity, remove it and tell clients to remove the copy they have)
|
||||
Events::PlayerDisconnected e;
|
||||
e.Entity = m_ConnectedPlayers[playerID].EntityID;
|
||||
e.Entity = m_ConnectedPlayers.at(playerID).EntityID;
|
||||
e.PlayerID = playerID;
|
||||
m_EventBroker->Publish(e);
|
||||
|
||||
//m_World->DeleteEntity(m_ConnectedPlayers[playerID].EntityID);
|
||||
m_ConnectedPlayers[playerID].TCPSocket->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
||||
m_ConnectedPlayers[playerID].TCPSocket->close();
|
||||
m_World->DeleteEntity(m_ConnectedPlayers[playerID].EntityID);
|
||||
m_ConnectedPlayers.erase(playerID);
|
||||
}
|
||||
|
||||
void Server::parseOnInputCommand(Packet& packet)
|
||||
{
|
||||
PlayerID player = -1;
|
||||
// Check which player it was who sent the message
|
||||
player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint);
|
||||
if (player != -1) {
|
||||
while (packet.DataReadSize() < packet.Size()) {
|
||||
Events::InputCommand e;
|
||||
e.Command = packet.ReadString();
|
||||
e.PlayerID = player; // Set correct player id
|
||||
e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID);
|
||||
e.Value = packet.ReadPrimitive<float>();
|
||||
m_EventBroker->Publish(e);
|
||||
//LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID);
|
||||
}
|
||||
}
|
||||
// Send disconnect to the other players.
|
||||
}
|
||||
|
||||
void Server::parseOnPlayerDamage(Packet & packet)
|
||||
{
|
||||
Events::PlayerDamage e;
|
||||
e.Inflictor = EntityWrapper(m_World, packet.ReadPrimitive<EntityID>());
|
||||
e.Victim = EntityWrapper(m_World, packet.ReadPrimitive<EntityID>());
|
||||
e.Damage = packet.ReadPrimitive<double>();
|
||||
e.Player = EntityWrapper(m_World, packet.ReadPrimitive<EntityID>());
|
||||
m_EventBroker->Publish(e);
|
||||
|
||||
//LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str());
|
||||
}
|
||||
|
||||
void Server::parseConnect(Packet& packet)
|
||||
{
|
||||
LOG_INFO("Parsing connections");
|
||||
// Check if player is already connected
|
||||
if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) {
|
||||
return;
|
||||
}
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() &&
|
||||
kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) {
|
||||
// Already connected
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Create a new player
|
||||
PlayerDefinition pd;
|
||||
pd.EntityID = 0; // Overlook this
|
||||
pd.Endpoint = m_ReceiverEndpoint;
|
||||
pd.Name = packet.ReadString();
|
||||
pd.PacketID = 0;
|
||||
pd.StopTime = std::clock();
|
||||
m_ConnectedPlayers[m_NextPlayerID++] = pd;
|
||||
LOG_INFO("Spectator \"%s\" connected on IP: %s", pd.Name.c_str(), pd.Endpoint.address().to_string().c_str());
|
||||
|
||||
// Send a message to the player that connected
|
||||
Packet connnectPacket(MessageType::Connect, pd.PacketID);
|
||||
send(connnectPacket);
|
||||
|
||||
// Send notification that a player has connected
|
||||
Packet notificationPacket(MessageType::PlayerConnected);
|
||||
broadcast(notificationPacket);
|
||||
}
|
||||
|
||||
void Server::parseDisconnect()
|
||||
{
|
||||
LOG_INFO("%i: Parsing disconnect", m_PacketID);
|
||||
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() &&
|
||||
kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) {
|
||||
disconnect(kv.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parseClientPing()
|
||||
{
|
||||
LOG_INFO("%i: Parsing ping", m_PacketID);
|
||||
PlayerID player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint);
|
||||
if (player == -1) {
|
||||
return;
|
||||
}
|
||||
// Return ping
|
||||
Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID);
|
||||
packet.WriteString("Ping received");
|
||||
send(packet);
|
||||
}
|
||||
|
||||
void Server::parsePing()
|
||||
{
|
||||
for (int i = 0; i < m_ConnectedPlayers.size(); i++) {
|
||||
if (m_ConnectedPlayers[i].Endpoint.address() == m_ReceiverEndpoint.address()) {
|
||||
m_ConnectedPlayers[i].StopTime = std::clock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::identifyPacketLoss()
|
||||
{
|
||||
// if no packets lost, difference should be equal to 1
|
||||
@@ -368,18 +444,7 @@ void Server::kick(PlayerID player)
|
||||
{
|
||||
disconnect(player);
|
||||
Packet packet = Packet(MessageType::Kick);
|
||||
send(packet);
|
||||
}
|
||||
|
||||
PlayerID Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint)
|
||||
{
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if (kv.second.Endpoint.address() == endpoint.address() &&
|
||||
kv.second.Endpoint.port() == endpoint.port()) {
|
||||
return kv.first;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
m_Reliable.Send(packet);
|
||||
}
|
||||
|
||||
bool Server::OnInputCommand(const Events::InputCommand & e)
|
||||
@@ -391,8 +456,7 @@ bool Server::OnInputCommand(const Events::InputCommand & e)
|
||||
}
|
||||
isReadingData = !isReadingData;
|
||||
m_SaveDataTimer = std::clock();
|
||||
}
|
||||
if (e.Command == "KickPlayer" && e.Value > 0) {
|
||||
} else if (e.Command == "KickPlayer" && e.Value > 0) {
|
||||
kick(0);
|
||||
}
|
||||
|
||||
@@ -408,7 +472,7 @@ bool Server::OnPlayerSpawned(const Events::PlayerSpawned & e)
|
||||
packet.WritePrimitive<EntityID>(e.Spawner.ID);
|
||||
// We don't send PlayerID here because it will always be set to -1
|
||||
packet.WriteString(m_ConnectedPlayers[e.PlayerID].Name);
|
||||
send(e.PlayerID, packet);
|
||||
m_Reliable.Send(packet, m_ConnectedPlayers[e.PlayerID]);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -417,7 +481,7 @@ bool Server::OnEntityDeleted(const Events::EntityDeleted & e)
|
||||
if (!e.Cascaded) {
|
||||
Packet packet = Packet(MessageType::EntityDeleted);
|
||||
packet.WritePrimitive<EntityID>(e.DeletedEntity);
|
||||
broadcast(packet);
|
||||
reliableBroadcast(packet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -425,16 +489,87 @@ bool Server::OnEntityDeleted(const Events::EntityDeleted & e)
|
||||
bool Server::OnComponentDeleted(const Events::ComponentDeleted & e)
|
||||
{
|
||||
if (!e.Cascaded) {
|
||||
Packet packet = Packet(MessageType::ComponentDeleted);
|
||||
packet.WritePrimitive<EntityID>(e.Entity);
|
||||
packet.WriteString(e.ComponentType);
|
||||
broadcast(packet);
|
||||
if (shouldSendToClient(EntityWrapper(m_World, e.Entity))) {
|
||||
Packet packet = Packet(MessageType::ComponentDeleted);
|
||||
packet.WritePrimitive<EntityID>(e.Entity);
|
||||
packet.WriteString(e.ComponentType);
|
||||
reliableBroadcast(packet);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Server::OnPlayerDamage(const Events::PlayerDamage& e)
|
||||
{
|
||||
Packet packet(MessageType::OnPlayerDamage);
|
||||
packet.WritePrimitive(e.Inflictor.ID);
|
||||
packet.WritePrimitive(e.Victim.ID);
|
||||
packet.WritePrimitive(e.Damage);
|
||||
reliableBroadcast(packet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::parseClientPing()
|
||||
{
|
||||
LOG_INFO("%i: Parsing ping", m_PacketID);
|
||||
PlayerID player = GetPlayerIDFromEndpoint();
|
||||
if (player == -1) {
|
||||
return;
|
||||
}
|
||||
// Return ping
|
||||
Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID);
|
||||
packet.WriteString("Ping received");
|
||||
m_Reliable.Send(packet);
|
||||
}
|
||||
|
||||
void Server::parsePing()
|
||||
{
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if (kv.second.TCPAddress == m_Address &&
|
||||
kv.second.TCPPort == m_Port
|
||||
|| (kv.second.Endpoint.address() == m_Address
|
||||
&& kv.second.Endpoint.port() == m_Port)) {
|
||||
kv.second.StopTime = std::clock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::parseDoubleJump(Packet & packet)
|
||||
{
|
||||
reliableBroadcast(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::parseOnInputCommand(Packet& packet)
|
||||
{
|
||||
PlayerID player = -1;
|
||||
// Check which player it was who sent the message
|
||||
player = GetPlayerIDFromEndpoint();
|
||||
if (player != -1) {
|
||||
while (packet.DataReadSize() < packet.Size()) {
|
||||
Events::InputCommand e;
|
||||
e.Command = packet.ReadString();
|
||||
e.PlayerID = player; // Set correct player id
|
||||
e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID);
|
||||
e.Value = packet.ReadPrimitive<float>();
|
||||
m_EventBroker->Publish(e);
|
||||
if (e.Command == "PrimaryFire" || e.Command == "Reload") {
|
||||
m_InputCommandsToBroadcast.push_back(e);
|
||||
}
|
||||
//LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Server::parsePlayerTransform(Packet& packet)
|
||||
{
|
||||
PlayerID playerID = GetPlayerIDFromEndpoint();
|
||||
if (playerID == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 position;
|
||||
glm::vec3 orientation;
|
||||
position.x = packet.ReadPrimitive<float>();
|
||||
@@ -444,11 +579,49 @@ void Server::parsePlayerTransform(Packet& packet)
|
||||
orientation.y = packet.ReadPrimitive<float>();
|
||||
orientation.z = packet.ReadPrimitive<float>();
|
||||
|
||||
PlayerID playerID = GetPlayerIDFromEndpoint(m_ReceiverEndpoint);
|
||||
EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID);
|
||||
bool hasAssaultWeapon = packet.ReadPrimitive<bool>();
|
||||
int magazineAmmo;
|
||||
int ammo;
|
||||
if (hasAssaultWeapon) {
|
||||
magazineAmmo = packet.ReadPrimitive<int>();
|
||||
ammo = packet.ReadPrimitive<int>();
|
||||
}
|
||||
|
||||
EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID);
|
||||
if (player.Valid()) {
|
||||
player["Transform"]["Position"] = position;
|
||||
player["Transform"]["Orientation"] = orientation;
|
||||
|
||||
if (hasAssaultWeapon) {
|
||||
player["AssaultWeapon"]["MagazineAmmo"] = magazineAmmo;
|
||||
player["AssaultWeapon"]["Ammo"] = ammo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::shouldSendToClient(EntityWrapper childEntity)
|
||||
{
|
||||
auto children = m_World->GetDirectChildren(childEntity.ID);
|
||||
for (auto it = children.first; it != children.second; it++) {
|
||||
EntityWrapper child(m_World, it->second);
|
||||
if(child.HasComponent("CapturePoint")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return childEntity.HasComponent("Player") || childEntity.FirstParentWithComponent("Player").Valid()
|
||||
|| childEntity.HasComponent("CapturePoint");
|
||||
}
|
||||
|
||||
PlayerID Server::GetPlayerIDFromEndpoint()
|
||||
{
|
||||
// check both tcp and udp connection
|
||||
for (auto& kv : m_ConnectedPlayers) {
|
||||
if ((kv.second.TCPAddress == m_Address
|
||||
&& kv.second.TCPPort == m_Port)
|
||||
|| (kv.second.Endpoint.address() == m_Address
|
||||
&& kv.second.Endpoint.port() == m_Port)) {
|
||||
return kv.first;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "Network/TCPClient.h"
|
||||
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
TCPClient::TCPClient()
|
||||
{
|
||||
}
|
||||
|
||||
TCPClient::~TCPClient()
|
||||
{
|
||||
}
|
||||
|
||||
void TCPClient::Connect(std::string playerName, std::string address, int port)
|
||||
{
|
||||
if (m_Socket) {
|
||||
if (m_IsConnected) {
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString(playerName);
|
||||
Send(packet);
|
||||
LOG_INFO("Connect message sent again!");
|
||||
}
|
||||
}
|
||||
else if (!m_IsConnected) {
|
||||
boost::system::error_code error = boost::asio::error::host_not_found;
|
||||
m_Endpoint = tcp::endpoint(boost::asio::ip::address::from_string(address), port);
|
||||
m_Socket = std::unique_ptr<tcp::socket>(new tcp::socket(m_IOService));
|
||||
m_Socket->connect(m_Endpoint, error);
|
||||
tcp::no_delay option(true);
|
||||
m_Socket->set_option(option);
|
||||
LOG_INFO(error.message().c_str());
|
||||
if (!error) {
|
||||
m_IsConnected = true;
|
||||
Packet packet(MessageType::Connect, m_SendPacketID);
|
||||
packet.WriteString(playerName);
|
||||
Send(packet);
|
||||
LOG_INFO("Connect message sent!");
|
||||
}
|
||||
// If error
|
||||
else {
|
||||
m_Socket->close();
|
||||
m_Socket = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCPClient::Disconnect()
|
||||
{
|
||||
if (!m_IsConnected) {
|
||||
return;
|
||||
}
|
||||
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
|
||||
m_Socket->close();
|
||||
m_Socket = nullptr;
|
||||
m_IsConnected = false;
|
||||
}
|
||||
|
||||
void TCPClient::Receive(Packet& packet)
|
||||
{
|
||||
size_t bytesRead = readBuffer();
|
||||
if (bytesRead > 0) {
|
||||
packet.ReconstructFromData(m_ReadBuffer, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
size_t TCPClient::readBuffer()
|
||||
{
|
||||
if (!m_Socket) {
|
||||
return 0;
|
||||
}
|
||||
boost::system::error_code error;
|
||||
// Read size of packet
|
||||
m_Socket->receive(boost
|
||||
::asio::buffer((void*)m_ReadBuffer, sizeof(int)),
|
||||
boost::asio::ip::tcp::socket::message_peek, error);
|
||||
unsigned int sizeOfPacket = 0;
|
||||
memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int));
|
||||
|
||||
// if the buffer is to small increase the size of it
|
||||
// TODO if message is huge 1 time the buffer will not decrease.
|
||||
if (sizeOfPacket > m_BufferSize) {
|
||||
delete[] m_ReadBuffer;
|
||||
m_ReadBuffer = new char[sizeOfPacket];
|
||||
m_BufferSize = sizeOfPacket;
|
||||
}
|
||||
// Read the rest of the message
|
||||
size_t bytesReceived = m_Socket->read_some(boost
|
||||
::asio::buffer((void*)(m_ReadBuffer), sizeOfPacket),
|
||||
error);
|
||||
if (error) {
|
||||
//LOG_ERROR("receive: %s", error.message().c_str());
|
||||
}
|
||||
if (sizeOfPacket > 1000000)
|
||||
LOG_WARNING("The packets received are bigger than 1MB");
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
void TCPClient::Send(Packet & packet)
|
||||
{
|
||||
if (!m_Socket) {
|
||||
LOG_WARNING("TCPClient::Send: Socket is null");
|
||||
return;
|
||||
}
|
||||
packet.UpdateSize();
|
||||
boost::system::error_code error;
|
||||
m_Socket->send(boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()), 0, error);
|
||||
}
|
||||
|
||||
bool TCPClient::IsSocketAvailable()
|
||||
{
|
||||
if (!m_Socket) {
|
||||
return false;
|
||||
}
|
||||
return m_Socket->available();
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "Network/TCPServer.h"
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
TCPServer::TCPServer()
|
||||
{
|
||||
acceptor = std::unique_ptr<tcp::acceptor>(new tcp::acceptor(m_IOService, tcp::endpoint(tcp::v4(), 27666)));
|
||||
// Make the acceptor non-blocking so we wont get stuck in AcceptNewConnections().
|
||||
acceptor->non_blocking(true);
|
||||
m_Port = GetPort();
|
||||
m_Address = GetAddress();
|
||||
}
|
||||
|
||||
TCPServer::~TCPServer()
|
||||
{ }
|
||||
|
||||
void TCPServer::AcceptNewConnections(int& nextPlayerID, std::map<PlayerID, PlayerDefinition>& connectedPlayers)
|
||||
{
|
||||
boost::system::error_code error;
|
||||
boost::shared_ptr<tcp::socket> newSocket = boost::shared_ptr<tcp::socket>(new tcp::socket(m_IOService));
|
||||
acceptor->accept(*newSocket, error);
|
||||
// If no error occured add new tcp connection
|
||||
if (!error) {
|
||||
// Add tcp socket to connections
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
newSocket->set_option(option);
|
||||
PlayerDefinition pd;
|
||||
pd.StopTime = std::clock();
|
||||
pd.TCPSocket = newSocket;
|
||||
pd.TCPAddress = newSocket.get()->remote_endpoint().address();
|
||||
pd.TCPPort = newSocket.get()->remote_endpoint().port();
|
||||
connectedPlayers[nextPlayerID++] = pd;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerID TCPServer::getPlayerIDFromEndpoint(const std::map<PlayerID, PlayerDefinition>& connectedPlayers,
|
||||
boost::asio::ip::address address, unsigned short port)
|
||||
{
|
||||
for (auto& kv : connectedPlayers) {
|
||||
if (kv.second.TCPAddress == address &&
|
||||
kv.second.TCPPort == port) {
|
||||
return kv.first;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TCPServer::Send(Packet & packet, PlayerDefinition & playerDefinition)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
try {
|
||||
int bytesSent = playerDefinition.TCPSocket->send(
|
||||
boost::asio::buffer(packet.Data(), packet.Size()),
|
||||
0);
|
||||
} catch (const boost::system::system_error& e) {
|
||||
// TODO: Clean up invalid endpoints out of m_ConnectedPlayers later
|
||||
playerDefinition.Endpoint = boost::asio::ip::udp::endpoint();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPServer::Send(Packet & packet)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
lastReceivedSocket->send(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
0);
|
||||
}
|
||||
|
||||
void TCPServer::Disconnect()
|
||||
{
|
||||
}
|
||||
|
||||
int TCPServer::GetPort()
|
||||
{
|
||||
return acceptor->local_endpoint().port();
|
||||
}
|
||||
|
||||
std::string TCPServer::GetAddress()
|
||||
{
|
||||
boost::asio::ip::tcp::resolver resolver(m_IOService);
|
||||
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), boost::asio::ip::host_name(), "");
|
||||
boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query);
|
||||
boost::asio::ip::tcp::endpoint endpoint = *it;
|
||||
return endpoint.address().to_string().c_str();
|
||||
}
|
||||
|
||||
void TCPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition)
|
||||
{
|
||||
int bytesRead = readBuffer(playerDefinition);
|
||||
if (bytesRead > 0) {
|
||||
packet.ReconstructFromData(m_ReadBuffer, bytesRead);
|
||||
}
|
||||
lastReceivedSocket = playerDefinition.TCPSocket;
|
||||
}
|
||||
|
||||
int TCPServer::readBuffer(PlayerDefinition & playerDefinition)
|
||||
{
|
||||
if (!playerDefinition.TCPSocket) {
|
||||
return 0;
|
||||
}
|
||||
boost::system::error_code error;
|
||||
// Read size of packet
|
||||
playerDefinition.TCPSocket->receive(boost
|
||||
::asio::buffer((void*)m_ReadBuffer, sizeof(int)),
|
||||
boost::asio::ip::tcp::socket::message_peek, error);
|
||||
unsigned int sizeOfPacket = 0;
|
||||
memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int));
|
||||
|
||||
// if the buffer is to small increase the size of it
|
||||
if (sizeOfPacket > m_BufferSize) {
|
||||
delete[] m_ReadBuffer;
|
||||
m_ReadBuffer = new char[sizeOfPacket];
|
||||
m_BufferSize = sizeOfPacket;
|
||||
}
|
||||
// Read the rest of the message
|
||||
size_t bytesReceived = playerDefinition.TCPSocket->read_some(boost
|
||||
::asio::buffer((void*)(m_ReadBuffer), sizeOfPacket),
|
||||
error);
|
||||
if (error) {
|
||||
//LOG_ERROR("receive: %s", error.message().c_str());
|
||||
}
|
||||
if (sizeOfPacket > 1000000)
|
||||
LOG_WARNING("The packets received are bigger than 1MB");
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#include "Network/UDPClient.h"
|
||||
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
UDPClient::UDPClient()
|
||||
{
|
||||
}
|
||||
|
||||
UDPClient::~UDPClient()
|
||||
{
|
||||
}
|
||||
|
||||
void UDPClient::Connect(std::string playerName, std::string address, int port)
|
||||
{
|
||||
if (m_Socket) {
|
||||
return;
|
||||
}
|
||||
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address().from_string(address), port);
|
||||
m_Socket = boost::shared_ptr<boost::asio::ip::udp::socket>(new boost::asio::ip::udp::socket(m_IOService));
|
||||
m_Socket->open(boost::asio::ip::udp::v4());
|
||||
}
|
||||
|
||||
void UDPClient::Disconnect()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UDPClient::Receive(Packet& packet)
|
||||
{
|
||||
int bytesRead = readBuffer();
|
||||
if (bytesRead > 0) {
|
||||
packet.ReconstructFromData(m_ReadBuffer, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
int UDPClient::readBuffer()
|
||||
{
|
||||
if (!m_Socket) {
|
||||
return 0;
|
||||
}
|
||||
boost::system::error_code error;
|
||||
// Read size of packet
|
||||
m_Socket->receive(boost
|
||||
::asio::buffer((void*)m_ReadBuffer, sizeof(int)),
|
||||
boost::asio::ip::udp::socket::message_peek, error);
|
||||
int sizeOfPacket = 0;
|
||||
memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int));
|
||||
|
||||
// if the buffer is to small increase the size of it
|
||||
if (sizeOfPacket > m_BufferSize) {
|
||||
delete[] m_ReadBuffer;
|
||||
m_ReadBuffer = new char[sizeOfPacket];
|
||||
m_BufferSize = sizeOfPacket;
|
||||
}
|
||||
|
||||
size_t availableData = m_Socket->available();
|
||||
// Read the rest of the message
|
||||
size_t bytesReceived = m_Socket->receive_from(boost
|
||||
::asio::buffer((void*)(m_ReadBuffer),
|
||||
sizeOfPacket),
|
||||
m_ReceiverEndpoint, 0, error);
|
||||
if (error) {
|
||||
//LOG_ERROR("receive: %s", error.message().c_str());
|
||||
}
|
||||
if (sizeOfPacket > 1000000)
|
||||
LOG_WARNING("The packets received are bigger than 1MB");
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
void UDPClient::Send(Packet& packet)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
m_Socket->send_to(boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint, 0);
|
||||
}
|
||||
|
||||
void UDPClient::Broadcast(Packet& packet, int port)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
m_Socket->set_option(boost::asio::socket_base::broadcast(true));
|
||||
m_Socket->send_to(boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
udp::endpoint(boost::asio::ip::address_v4().broadcast(), port)
|
||||
, 0);
|
||||
m_Socket->set_option(boost::asio::socket_base::broadcast(false));
|
||||
}
|
||||
|
||||
bool UDPClient::IsSocketAvailable()
|
||||
{
|
||||
if (!m_Socket) {
|
||||
return false;
|
||||
}
|
||||
return m_Socket->available();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "Network/UDPServer.h"
|
||||
|
||||
UDPServer::UDPServer()
|
||||
{
|
||||
m_Socket = std::unique_ptr<boost::asio::ip::udp::socket>(new boost::asio::ip::udp::socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666)));
|
||||
}
|
||||
|
||||
UDPServer::UDPServer(int port)
|
||||
{
|
||||
m_Socket = std::unique_ptr<boost::asio::ip::udp::socket>(new boost::asio::ip::udp::socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)));
|
||||
}
|
||||
|
||||
UDPServer::~UDPServer()
|
||||
{ }
|
||||
|
||||
void UDPServer::Send(Packet& packet, PlayerDefinition & playerDefinition)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
try {
|
||||
int bytesSent = m_Socket->send_to(
|
||||
boost::asio::buffer(packet.Data(), packet.Size()),
|
||||
playerDefinition.Endpoint,
|
||||
0);
|
||||
} catch (const boost::system::system_error& e) {
|
||||
// TODO: Clean up invalid endpoints out of m_ConnectedPlayers later
|
||||
playerDefinition.Endpoint = boost::asio::ip::udp::endpoint();
|
||||
}
|
||||
}
|
||||
// Send back to endpoint of received packet
|
||||
void UDPServer::Send(Packet & packet)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
m_Socket->send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
m_ReceiverEndpoint,
|
||||
0);
|
||||
}
|
||||
|
||||
// Broadcasting respond specific logic
|
||||
void UDPServer::Send(Packet & packet, boost::asio::ip::udp::endpoint endpoint)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
m_Socket->send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
endpoint,
|
||||
0);
|
||||
}
|
||||
|
||||
// Broadcasting
|
||||
void UDPServer::Broadcast(Packet & packet, int port)
|
||||
{
|
||||
packet.UpdateSize();
|
||||
m_Socket->set_option(boost::asio::socket_base::broadcast(true));
|
||||
m_Socket->send_to(
|
||||
boost::asio::buffer(
|
||||
packet.Data(),
|
||||
packet.Size()),
|
||||
boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4().broadcast(),port),
|
||||
0);
|
||||
m_Socket->set_option(boost::asio::socket_base::broadcast(false));
|
||||
}
|
||||
|
||||
void UDPServer::Receive(Packet & packet, PlayerDefinition & playerDefinition)
|
||||
{
|
||||
int bytesRead = readBuffer();
|
||||
if (bytesRead > 0) {
|
||||
packet.ReconstructFromData(m_ReadBuffer, bytesRead);
|
||||
}
|
||||
playerDefinition.Endpoint = m_ReceiverEndpoint;
|
||||
}
|
||||
|
||||
bool UDPServer::IsSocketAvailable()
|
||||
{
|
||||
return m_Socket->available();
|
||||
}
|
||||
|
||||
int UDPServer::readBuffer()
|
||||
{
|
||||
if (!m_Socket) {
|
||||
return 0;
|
||||
}
|
||||
int addasdasd = m_Socket->available();
|
||||
boost::system::error_code error;
|
||||
// Read size of packet
|
||||
m_Socket->receive_from(boost
|
||||
::asio::buffer((void*)m_ReadBuffer, sizeof(int)),
|
||||
m_ReceiverEndpoint, boost::asio::ip::udp::socket::message_peek, error);
|
||||
unsigned int sizeOfPacket = 0;
|
||||
memcpy(&sizeOfPacket, m_ReadBuffer, sizeof(int));
|
||||
|
||||
// if the buffer is to small increase the size of it
|
||||
if (sizeOfPacket > m_BufferSize) {
|
||||
delete[] m_ReadBuffer;
|
||||
m_ReadBuffer = new char[sizeOfPacket];
|
||||
m_BufferSize = sizeOfPacket;
|
||||
}
|
||||
|
||||
// Read the rest of the message
|
||||
size_t bytesReceived = m_Socket->receive_from(boost
|
||||
::asio::buffer((void*)(m_ReadBuffer),
|
||||
sizeOfPacket),
|
||||
m_ReceiverEndpoint, 0, error);
|
||||
if (error) {
|
||||
//LOG_ERROR("receive: %s", error.message().c_str());
|
||||
}
|
||||
if (sizeOfPacket > 1000000)
|
||||
LOG_WARNING("The packets received are bigger than 1MB");
|
||||
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
void UDPServer::AcceptNewConnections(int& nextPlayerID, std::map<PlayerID, PlayerDefinition>& connectedPlayers)
|
||||
{ }
|
||||
@@ -13,35 +13,59 @@ void AnimationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& a
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Skeleton* skeleton = model->m_RawModel->m_Skeleton;
|
||||
const Skeleton::Animation* animation = skeleton->GetAnimation(animationComponent["Name"]);
|
||||
|
||||
if(animation != nullptr) {
|
||||
double animationSpeed = (double)animationComponent["Speed"];
|
||||
|
||||
if (animationSpeed != 0.0) {
|
||||
double nextTime = (double)animationComponent["Time"] + animationSpeed * dt;
|
||||
|
||||
|
||||
if (!(bool)animationComponent["Loop"] && glm::abs(nextTime) > animation->Duration) {
|
||||
(double&)animationComponent["Time"] = glm::sign(nextTime) * animation->Duration;
|
||||
(double&)animationComponent["Speed"] = 0.0;
|
||||
Events::AnimationComplete e;
|
||||
e.Entity = entity;
|
||||
e.Name = (std::string)animationComponent["Name"];
|
||||
m_EventBroker->Publish(e);
|
||||
} else {
|
||||
if (glm::abs(nextTime) > animation->Duration) {
|
||||
(double&)animationComponent["Time"] = glm::abs(nextTime) - animation->Duration;
|
||||
} else {
|
||||
(double&)animationComponent["Time"] = nextTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(skeleton == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
const Skeleton::Animation* animation = skeleton->GetAnimation(animationComponent["AnimationName" + std::to_string(i)]);
|
||||
|
||||
if (animation == nullptr) {
|
||||
continue;;
|
||||
}
|
||||
|
||||
double animationSpeed = (double)animationComponent["Speed" + std::to_string(i)];
|
||||
|
||||
if (animationSpeed != 0.0) {
|
||||
double nextTime = (double)animationComponent["Time" + std::to_string(i)] + animationSpeed * dt;
|
||||
|
||||
|
||||
if (!(bool)animationComponent["Loop" + std::to_string(i)]) {
|
||||
if (nextTime > animation->Duration) {
|
||||
nextTime = animation->Duration;
|
||||
Events::AnimationComplete e;
|
||||
e.Entity = entity;
|
||||
e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)];
|
||||
m_EventBroker->Publish(e);
|
||||
} else if (nextTime < 0) {
|
||||
Events::AnimationComplete e;
|
||||
e.Entity = entity;
|
||||
e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)];
|
||||
m_EventBroker->Publish(e);
|
||||
nextTime = 0;
|
||||
}
|
||||
|
||||
(double&)animationComponent["Speed" + std::to_string(i)] = 0.0;
|
||||
|
||||
} else {
|
||||
if (nextTime > animation->Duration) {
|
||||
Events::AnimationComplete e;
|
||||
e.Entity = entity;
|
||||
e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)];
|
||||
m_EventBroker->Publish(e);
|
||||
nextTime -= animation->Duration;
|
||||
} else if (nextTime < 0) {
|
||||
Events::AnimationComplete e;
|
||||
e.Entity = entity;
|
||||
e.Name = (std::string)animationComponent["AnimationName" + std::to_string(i)];
|
||||
m_EventBroker->Publish(e);
|
||||
nextTime += animation->Duration;
|
||||
}
|
||||
}
|
||||
|
||||
(double&)animationComponent["Time" + std::to_string(i)] = nextTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "Rendering/BoneAttachmentSystem.h"
|
||||
|
||||
void BoneAttachmentSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& BoneAttachmentComponent, double dt)
|
||||
{
|
||||
|
||||
|
||||
if(!entity.HasComponent("Transform")) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto parent = entity.FirstParentWithComponent("Animation");
|
||||
if (!parent.HasComponent("Model")) {
|
||||
return;
|
||||
}
|
||||
Model* model;
|
||||
try {
|
||||
model = ResourceManager::Load<::Model, true>(parent["Model"]["Resource"]);
|
||||
} catch (const std::exception&) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!model->IsSkinned()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Skeleton* skeleton = model->m_RawModel->m_Skeleton;
|
||||
|
||||
if(skeleton == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int id = skeleton->GetBoneID(entity["BoneAttachment"]["BoneName"]);
|
||||
|
||||
if (id == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<::Skeleton::AnimationData> Animations;
|
||||
::Skeleton::AnimationOffset AnimationOffset;
|
||||
glm::mat4 boneTransform;
|
||||
|
||||
if (parent.HasComponent("Animation")) {
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
::Skeleton::AnimationData animationData;
|
||||
animationData.animation = model->m_RawModel->m_Skeleton->GetAnimation(parent["Animation"]["AnimationName" + std::to_string(i)]);
|
||||
if (animationData.animation == nullptr) {
|
||||
continue;
|
||||
}
|
||||
animationData.time = (double)parent["Animation"]["Time" + std::to_string(i)];
|
||||
animationData.weight = (double)parent["Animation"]["Weight" + std::to_string(i)];
|
||||
|
||||
Animations.push_back(animationData);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent.HasComponent("AnimationOffset")) {
|
||||
AnimationOffset.animation = model->m_RawModel->m_Skeleton->GetAnimation(parent["AnimationOffset"]["AnimationName"]);
|
||||
AnimationOffset.time = (double)parent["AnimationOffset"]["Time"];
|
||||
|
||||
if(AnimationOffset.animation != nullptr) {
|
||||
boneTransform = skeleton->GetBoneTransform(false, skeleton->Bones.at(id), Animations, AnimationOffset, glm::mat4(1));
|
||||
} else {
|
||||
boneTransform = skeleton->GetBoneTransform(false, skeleton->Bones.at(id), Animations, glm::mat4(1));
|
||||
}
|
||||
|
||||
} else {
|
||||
boneTransform = skeleton->GetBoneTransform(false, skeleton->Bones.at(id), Animations, glm::mat4(1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::vec3 scale;
|
||||
glm::quat rotation;
|
||||
glm::vec3 translation;
|
||||
glm::vec3 skew;
|
||||
glm::vec4 perspective;
|
||||
glm::decompose(boneTransform, scale, rotation, translation, skew, perspective);
|
||||
|
||||
glm::vec3 angles = glm::vec3(-glm::pitch(rotation), -glm::yaw(rotation), -glm::roll(rotation));
|
||||
/*
|
||||
|
||||
angles.y = asin(-boneTransform[0][2]);
|
||||
if (cos(angles.y) != 0) {
|
||||
angles.x = atan2(boneTransform[1][2], boneTransform[2][2]);
|
||||
angles.z = atan2(boneTransform[0][1], boneTransform[0][0]);
|
||||
} else {
|
||||
angles.x = atan2(-boneTransform[2][0], boneTransform[1][1]);
|
||||
angles.z = 0;
|
||||
}*/
|
||||
|
||||
if ((bool)entity["BoneAttachment"]["InheritPosition"]) {
|
||||
(glm::vec3&)entity["Transform"]["Position"] = translation + (glm::vec3)entity["BoneAttachment"]["PositionOffset"];
|
||||
}
|
||||
if ((bool)entity["BoneAttachment"]["InheritOrientation"]) {
|
||||
(glm::vec3&)entity["Transform"]["Orientation"] = angles + (glm::vec3)entity["BoneAttachment"]["OrientationOffset"];
|
||||
}
|
||||
if ((bool)entity["BoneAttachment"]["InheritScale"]) {
|
||||
(glm::vec3&)entity["Transform"]["Scale"] = scale * (glm::vec3)entity["BoneAttachment"]["ScaleOffset"];
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,13 @@ void Camera::SetViewMatrix(glm::mat4 val)
|
||||
m_ViewMatrix = val;
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 Camera::BillboardMatrix()
|
||||
{
|
||||
glm::mat4 matrix = glm::toMat4(m_Orientation);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
//void Camera::Pitch(float val)
|
||||
//{
|
||||
// m_Pitch = val;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "Rendering/CubeMapPass.h"
|
||||
|
||||
CubeMapPass::CubeMapPass(IRenderer* renderer)
|
||||
:m_Renderer(renderer)
|
||||
{
|
||||
LoadTextures("Nevada");
|
||||
}
|
||||
|
||||
void CubeMapPass::LoadTextures(std::string input)
|
||||
{
|
||||
if (m_PreviusCubeMapTexture != input) {
|
||||
m_CubeMapTextures.clear();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
std::string str;
|
||||
str = "Textures/Test/CubeMap/" + input + "/CubeMapTest0" + std::to_string(i) + ".png";
|
||||
Texture* img = ResourceManager::Load<Texture>(str);
|
||||
m_CubeMapTextures.push_back(img);
|
||||
}
|
||||
GenerateCubeMapTexture();
|
||||
m_PreviusCubeMapTexture = input;
|
||||
}
|
||||
}
|
||||
|
||||
void CubeMapPass::GenerateCubeMapTexture()
|
||||
{
|
||||
if (m_CubeMapTexture == -1) {
|
||||
glGenTextures(1, &m_CubeMapTexture);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, m_CubeMapTexture);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, m_CubeMapTextures[0]->Width, m_CubeMapTextures[0]->Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_CubeMapTextures[i]->Data);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
GLERROR("Generate Cubemap");
|
||||
}
|
||||
|
||||
@@ -13,22 +13,26 @@ DrawBloomPass::DrawBloomPass(IRenderer* renderer)
|
||||
|
||||
void DrawBloomPass::InitializeTextures()
|
||||
{
|
||||
m_WhiteTexture = ResourceManager::Load<Texture>("Textures/Core/White.png");
|
||||
m_WhiteTexture = CommonFunctions::LoadTexture("Textures/Core/White.png", false);
|
||||
}
|
||||
|
||||
void DrawBloomPass::InitializeShaderPrograms()
|
||||
{
|
||||
m_GaussianProgram_horiz = ResourceManager::Load<ShaderProgram>("##GaussianProgramHoriz");
|
||||
m_GaussianProgram_horiz->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Gaussian_horiz.vert.glsl")));
|
||||
m_GaussianProgram_horiz->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Gaussian_horiz.frag.glsl")));
|
||||
m_GaussianProgram_horiz->Compile();
|
||||
m_GaussianProgram_horiz->Link();
|
||||
if (m_GaussianProgram_horiz->GetHandle() == 0) {
|
||||
m_GaussianProgram_horiz->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Gaussian_horiz.vert.glsl")));
|
||||
m_GaussianProgram_horiz->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Gaussian_horiz.frag.glsl")));
|
||||
m_GaussianProgram_horiz->Compile();
|
||||
m_GaussianProgram_horiz->Link();
|
||||
}
|
||||
|
||||
m_GaussianProgram_vert = ResourceManager::Load<ShaderProgram>("##GaussianProgramVert");
|
||||
m_GaussianProgram_vert->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Gaussian_vert.vert.glsl")));
|
||||
m_GaussianProgram_vert->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Gaussian_vert.frag.glsl")));
|
||||
m_GaussianProgram_vert->Compile();
|
||||
m_GaussianProgram_vert->Link();
|
||||
m_GaussianProgram_vert = ResourceManager::Load<ShaderProgram>("##GaussianProgramVert");
|
||||
if (m_GaussianProgram_vert->GetHandle() == 0) {
|
||||
m_GaussianProgram_vert->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/Gaussian_vert.vert.glsl")));
|
||||
m_GaussianProgram_vert->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Gaussian_vert.frag.glsl")));
|
||||
m_GaussianProgram_vert->Compile();
|
||||
m_GaussianProgram_vert->Link();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +52,7 @@ void DrawBloomPass::InitializeBuffers()
|
||||
|
||||
void DrawBloomPass::ClearBuffer()
|
||||
{
|
||||
GLERROR("PRE");
|
||||
m_GaussianFrameBuffer_horiz.Bind();
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
@@ -56,6 +61,7 @@ void DrawBloomPass::ClearBuffer()
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_GaussianFrameBuffer_vert.Unbind();
|
||||
GLERROR("END");
|
||||
}
|
||||
|
||||
void DrawBloomPass::Draw(GLuint texture)
|
||||
@@ -71,15 +77,12 @@ void DrawBloomPass::Draw(GLuint texture)
|
||||
//Horizontal pass, first use the given texture then save it to the horizontal framebuffer.
|
||||
m_GaussianFrameBuffer_horiz.Bind();
|
||||
m_GaussianProgram_horiz->Bind();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
//Iterate some times to make it more gaussian.
|
||||
for (int i = 1; i < m_iterations; i++) {
|
||||
//Vertical pass
|
||||
@@ -90,9 +93,8 @@ void DrawBloomPass::Draw(GLuint texture)
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
//horizontal pass
|
||||
|
||||
m_GaussianFrameBuffer_horiz.Bind();
|
||||
@@ -102,8 +104,8 @@ void DrawBloomPass::Draw(GLuint texture)
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
}
|
||||
|
||||
//final vertical gaussian after the iterations are done
|
||||
@@ -112,15 +114,23 @@ void DrawBloomPass::Draw(GLuint texture)
|
||||
m_GaussianProgram_vert->Bind();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, m_GaussianTexture_horiz);
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
|
||||
GLERROR("DrawBloomPass::Draw: END");
|
||||
}
|
||||
|
||||
|
||||
void DrawBloomPass::OnWindowResize()
|
||||
{
|
||||
GenerateTexture(&m_GaussianTexture_vert, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT);
|
||||
m_GaussianFrameBuffer_vert.Generate();
|
||||
GenerateTexture(&m_GaussianTexture_horiz, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RGB16F, GL_RGB, GL_FLOAT);
|
||||
m_GaussianFrameBuffer_horiz.Generate();
|
||||
}
|
||||
|
||||
void DrawBloomPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const
|
||||
{
|
||||
glGenTextures(1, texture);
|
||||
|
||||
@@ -6,8 +6,6 @@ DrawColorCorrectionPass::DrawColorCorrectionPass(IRenderer* renderer)
|
||||
|
||||
m_ScreenQuad = ResourceManager::Load<Model>("Models/Core/ScreenQuad.mesh");
|
||||
|
||||
//m_Exposure = 0.4; //TODO: Renderer: Fixa så att denna går att ändra på genom komponent eller setting.
|
||||
|
||||
InitializeShaderPrograms();
|
||||
}
|
||||
|
||||
@@ -20,14 +18,14 @@ void DrawColorCorrectionPass::InitializeShaderPrograms()
|
||||
m_ColorCorrectionProgram->Link();
|
||||
}
|
||||
|
||||
void DrawColorCorrectionPass::Draw(GLuint sceneTexture, GLuint bloomTexture, GLfloat gamma, GLfloat exposure)
|
||||
void DrawColorCorrectionPass::Draw(GLuint sceneTexture, GLuint bloomTexture, GLuint sceneTextureLowRes, GLuint bloomTextureLowRes, GLfloat gamma, GLfloat exposure)
|
||||
{
|
||||
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
GLERROR("DrawScreenQuadPass::Draw: Pre");
|
||||
|
||||
DrawScreenQuadPassState state = DrawScreenQuadPassState();
|
||||
m_ColorCorrectionProgram->Bind();
|
||||
//glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glUniform1f(glGetUniformLocation(m_ColorCorrectionProgram->GetHandle(), "Exposure"), exposure);
|
||||
glUniform1f(glGetUniformLocation(m_ColorCorrectionProgram->GetHandle(), "Gamma"), gamma);
|
||||
|
||||
@@ -35,9 +33,13 @@ void DrawColorCorrectionPass::Draw(GLuint sceneTexture, GLuint bloomTexture, GLf
|
||||
glBindTexture(GL_TEXTURE_2D, sceneTexture);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, bloomTexture);
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, sceneTextureLowRes);
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
glBindTexture(GL_TEXTURE_2D, bloomTextureLowRes);
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
#include "Rendering/DrawFinalPassState.h"
|
||||
|
||||
|
||||
|
||||
DrawFinalPassState::DrawFinalPassState(GLuint frameBuffer)
|
||||
{
|
||||
BindFramebuffer(frameBuffer);
|
||||
@@ -8,6 +9,10 @@ DrawFinalPassState::DrawFinalPassState(GLuint frameBuffer)
|
||||
BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Enable(GL_DEPTH_TEST);
|
||||
Enable(GL_CULL_FACE);
|
||||
Enable(GL_STENCIL_TEST);
|
||||
StencilFunc(GL_NOTEQUAL, 1, 0xFF);
|
||||
StencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
StencilMask(0xFF);
|
||||
ClearColor(glm::vec4(0.f, 0.f, 0.f, 0.f));
|
||||
}
|
||||
|
||||
@@ -15,3 +20,20 @@ DrawFinalPassState::~DrawFinalPassState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DrawStencilState::DrawStencilState(GLuint frameBuffer)
|
||||
{
|
||||
BindFramebuffer(frameBuffer);
|
||||
Enable(GL_STENCIL_TEST);
|
||||
StencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
StencilFunc(GL_ALWAYS, 1, 0xFF);
|
||||
StencilMask(0xFF);
|
||||
Enable(GL_DEPTH_TEST);
|
||||
ClearColor(glm::vec4(0.f));
|
||||
}
|
||||
|
||||
DrawStencilState::~DrawStencilState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,6 @@ void DrawScreenQuadPass::Draw(GLuint texture)
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex +1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
}
|
||||
|
||||
@@ -53,10 +53,13 @@ void FrameBuffer::AddResource(std::shared_ptr<BufferResource> resource)
|
||||
|
||||
void FrameBuffer::Generate()
|
||||
{
|
||||
GLERROR("PRE");
|
||||
|
||||
std::vector<GLenum> attachments;
|
||||
|
||||
glGenFramebuffers(1, &m_BufferHandle);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_BufferHandle);
|
||||
GLERROR("1");
|
||||
|
||||
for (auto it = m_Resources.begin(); it != m_Resources.end(); it++) {
|
||||
switch ((*it)->m_ResourceType) {
|
||||
@@ -75,17 +78,28 @@ void FrameBuffer::Generate()
|
||||
GLERROR("FrameBuffer generate: GL_TEXTURE_2D_ARRAY");
|
||||
break;
|
||||
}
|
||||
|
||||
GLERROR("2");
|
||||
|
||||
if ((*it)->m_Attachment != GL_DEPTH_ATTACHMENT && (*it)->m_Attachment != GL_STENCIL_ATTACHMENT && (*it)->m_Attachment != GL_DEPTH_STENCIL_ATTACHMENT) {
|
||||
GLERROR("Attachment");
|
||||
|
||||
}
|
||||
|
||||
GLERROR("3");
|
||||
|
||||
|
||||
GLenum* bufferTextures = &attachments[0];
|
||||
glDrawBuffers(attachments.size(), bufferTextures);
|
||||
if (GLERROR("GLBufferAttachement error")) {
|
||||
printf(": AttachmentSize %i", attachments.size());
|
||||
}
|
||||
|
||||
if (GLenum frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG_ERROR("FrameBuffer incomplete: 0x%x\n", frameBufferStatus);
|
||||
GLERROR("Framebuffer incomplete");
|
||||
//LOG_ERROR("FrameBuffer incomplete: 0x%x\n", frameBufferStatus);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
GLERROR("END");
|
||||
|
||||
}
|
||||
|
||||
void FrameBuffer::Bind()
|
||||
|
||||
@@ -16,7 +16,7 @@ LightCullingPass::~LightCullingPass()
|
||||
|
||||
void LightCullingPass::GenerateNewFrustum(RenderScene& scene)
|
||||
{
|
||||
if (scene.PointLightJobs.size() == 0)
|
||||
if (scene.Jobs.PointLight.size() == 0)
|
||||
return;
|
||||
|
||||
GLERROR("CalculateFrustum Error: Pre");
|
||||
@@ -83,7 +83,7 @@ void LightCullingPass::FillLightList(RenderScene& scene)
|
||||
{
|
||||
m_LightSources.clear();
|
||||
|
||||
for(auto &job : scene.PointLightJobs) {
|
||||
for(auto &job : scene.Jobs.PointLight) {
|
||||
auto pointLightjob = std::dynamic_pointer_cast<PointLightJob>(job);
|
||||
if (pointLightjob) {
|
||||
LightSource p;
|
||||
@@ -97,7 +97,7 @@ void LightCullingPass::FillLightList(RenderScene& scene)
|
||||
m_LightSources.push_back(p);
|
||||
}
|
||||
}
|
||||
for(auto &job : scene.DirectionalLightJobs) {
|
||||
for(auto &job : scene.Jobs.DirectionalLight) {
|
||||
auto directionalLightJob = std::dynamic_pointer_cast<DirectionalLightJob>(job);
|
||||
if(directionalLightJob) {
|
||||
LightSource p;
|
||||
@@ -110,6 +110,34 @@ void LightCullingPass::FillLightList(RenderScene& scene)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LightCullingPass::OnWindowResize()
|
||||
{
|
||||
SetSSBOSizes();
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_FrustumSSBO);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(Frustum)*m_NumberOfTiles, nullptr, GL_DYNAMIC_COPY);
|
||||
GLERROR("m_FrustumSSBO");
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightSSBO);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LightSource) * 200, nullptr, GL_DYNAMIC_COPY);
|
||||
GLERROR("m_LightSSBO");
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightGridSSBO);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(LightGrid)*m_NumberOfTiles, nullptr, GL_DYNAMIC_COPY);
|
||||
GLERROR("m_LightGridSSBO");
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightOffsetSSBO);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(m_LightOffset), &m_LightOffset, GL_DYNAMIC_COPY);
|
||||
GLERROR("m_LightOffsetSSBO");
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_LightIndexSSBO);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float)*m_NumberOfTiles*MAX_LIGHTS_PER_TILE, m_LightIndex, GL_DYNAMIC_COPY);
|
||||
GLERROR("m_LightIndexSSBO");
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
|
||||
void LightCullingPass::InitializeSSBOs()
|
||||
{
|
||||
glGenBuffers(1, &m_FrustumSSBO);
|
||||
|
||||
@@ -5,26 +5,48 @@ Model::Model(std::string fileName)
|
||||
//Try loading the model asyncronously, if it throws any exceptions then let it propagate back to caller.
|
||||
m_RawModel = ResourceManager::Load<RawModel, true>(fileName);
|
||||
|
||||
for (auto& group : m_RawModel->MaterialGroups) {
|
||||
if (!group.TexturePath.empty()) {
|
||||
group.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.TexturePath));
|
||||
}
|
||||
if (!group.NormalMapPath.empty()) {
|
||||
group.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.NormalMapPath));
|
||||
}
|
||||
if (!group.SpecularMapPath.empty()) {
|
||||
group.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.SpecularMapPath));
|
||||
}
|
||||
if (!group.IncandescenceMapPath.empty()) {
|
||||
group.IncandescenceMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.IncandescenceMapPath));
|
||||
}
|
||||
for (auto& materialProperty : m_RawModel->m_Materials) {
|
||||
switch (materialProperty.type) {
|
||||
case RawModel::MaterialType::SingleTextures:
|
||||
{
|
||||
RawModel::MaterialSingleTextures* materialSingleTexture = static_cast<RawModel::MaterialSingleTextures*>(materialProperty.material);
|
||||
materialSingleTexture->ColorMap.Texture = CommonFunctions::LoadTexture(materialSingleTexture->ColorMap.TexturePath, false);
|
||||
materialSingleTexture->NormalMap.Texture = CommonFunctions::LoadTexture(materialSingleTexture->NormalMap.TexturePath, false);
|
||||
materialSingleTexture->SpecularMap.Texture = CommonFunctions::LoadTexture(materialSingleTexture->SpecularMap.TexturePath, false);
|
||||
materialSingleTexture->IncandescenceMap.Texture = CommonFunctions::LoadTexture(materialSingleTexture->IncandescenceMap.TexturePath, false);
|
||||
}
|
||||
break;
|
||||
case RawModel::MaterialType::SplatMapping:
|
||||
{
|
||||
RawModel::MaterialSplatMapping* materialSplatMapping = static_cast<RawModel::MaterialSplatMapping*>(materialProperty.material);
|
||||
materialSplatMapping->SplatMap.Texture = CommonFunctions::LoadTexture(materialSplatMapping->SplatMap.TexturePath, false);
|
||||
for (auto& texture : materialSplatMapping->ColorMaps)
|
||||
{
|
||||
texture.Texture = CommonFunctions::LoadTexture(texture.TexturePath, false);
|
||||
}
|
||||
for (auto& texture : materialSplatMapping->NormalMaps)
|
||||
{
|
||||
texture.Texture = CommonFunctions::LoadTexture(texture.TexturePath, false);
|
||||
}
|
||||
for (auto& texture : materialSplatMapping->SpecularMaps)
|
||||
{
|
||||
texture.Texture = CommonFunctions::LoadTexture(texture.TexturePath, false);
|
||||
}
|
||||
for (auto& texture : materialSplatMapping->IncandescenceMaps)
|
||||
{
|
||||
texture.Texture = CommonFunctions::LoadTexture(texture.TexturePath, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate GL buffers
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, m_RawModel->m_Vertices.size() * sizeof(RawModel::Vertex), &m_RawModel->m_Vertices[0], GL_STATIC_DRAW);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, m_RawModel->NumVertices() * m_RawModel->VertexSize(), m_RawModel->Vertices(), GL_STATIC_DRAW);
|
||||
|
||||
glGenBuffers(1, &ElementBuffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer);
|
||||
@@ -35,7 +57,13 @@ Model::Model(std::string fileName)
|
||||
GLERROR("GLEW: BufferFail4");
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
std::vector<int> structSizes = { 3, 3, 3, 3, 2, 4, 4 };
|
||||
std::vector<int> structSizes;
|
||||
if (m_RawModel->IsSkinned()) {
|
||||
structSizes = { 3, 3, 3, 3, 2, 4, 4 };
|
||||
} else {
|
||||
structSizes = { 3, 3, 3, 3, 2 };
|
||||
}
|
||||
|
||||
int stride = 0;
|
||||
for (int size : structSizes) {
|
||||
stride += size;
|
||||
@@ -49,8 +77,10 @@ Model::Model(std::string fileName)
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
if (m_RawModel->IsSkinned()) {
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++;
|
||||
}
|
||||
}
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
|
||||
@@ -59,11 +89,24 @@ Model::Model(std::string fileName)
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
glEnableVertexAttribArray(4);
|
||||
glEnableVertexAttribArray(5);
|
||||
glEnableVertexAttribArray(6);
|
||||
if (m_RawModel->IsSkinned()) {
|
||||
glEnableVertexAttribArray(5);
|
||||
glEnableVertexAttribArray(6);
|
||||
}
|
||||
GLERROR("GLEW: BufferFail5");
|
||||
|
||||
//CreateBuffers();
|
||||
|
||||
glm::vec3 mini(INFINITY);
|
||||
glm::vec3 maxi(-INFINITY);
|
||||
|
||||
for (unsigned int i = 0; i < m_RawModel->NumVertices(); i++) {
|
||||
const auto& v = m_RawModel->Vertices()[i];
|
||||
mini = glm::min(mini, v.Position);
|
||||
maxi = glm::max(maxi, v.Position);
|
||||
}
|
||||
|
||||
m_Box = AABB(mini, maxi);
|
||||
}
|
||||
|
||||
Model::~Model()
|
||||
|
||||
@@ -4,40 +4,35 @@ PNG::PNG(std::string path)
|
||||
{
|
||||
FILE* file = fopen(path.c_str(), "rb");
|
||||
if (!file) {
|
||||
LOG_ERROR("Failed to open texture file \"%s\": %s", path.c_str(), const_cast<const char*>(strerror(errno)));
|
||||
return;
|
||||
throw Resource::FailedLoadingException("Failed to open texture file.");
|
||||
}
|
||||
|
||||
png_byte header[8];
|
||||
fread(header, 1, 8, file);
|
||||
bool isPNG = !png_sig_cmp(header, 0, 8);
|
||||
if (!isPNG) {
|
||||
LOG_ERROR("Failed to load texture file \"%s\": File isn't PNG", path.c_str());
|
||||
fclose(file);
|
||||
return;
|
||||
throw Resource::FailedLoadingException("File is not PNG.");
|
||||
}
|
||||
|
||||
// Initialize libpng
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, (png_error_ptr)&PNG::pngErrorFunction, (png_error_ptr)&PNG::pngErrorFunction);
|
||||
if (!png_ptr) {
|
||||
LOG_ERROR("libpng: Failed to initialze png_struct");
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
fclose(file);
|
||||
return;
|
||||
throw Resource::FailedLoadingException("Failed to initialze png_struct.");
|
||||
}
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr) {
|
||||
LOG_ERROR("libpng: Failed to initialze png_info");
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
fclose(file);
|
||||
return;
|
||||
throw Resource::FailedLoadingException("Failed to initialze png_info.");
|
||||
}
|
||||
png_infop info_end_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_end_ptr) {
|
||||
LOG_ERROR("libpng: Failed to initialze second png_info");
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
fclose(file);
|
||||
return;
|
||||
throw Resource::FailedLoadingException("Failed to initialze second png_info.");
|
||||
}
|
||||
png_init_io(png_ptr, file);
|
||||
|
||||
@@ -51,8 +46,8 @@ PNG::PNG(std::string path)
|
||||
unsigned int width, height;
|
||||
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
||||
if (bit_depth != 8) {
|
||||
LOG_ERROR("libpng: Unsupported bit depth \"%i\" of image \"%s\", must be 8", bit_depth, path.c_str());
|
||||
return;
|
||||
throw Resource::FailedLoadingException("Unsupported bit depth. Must be 8");
|
||||
|
||||
}
|
||||
switch (color_type) {
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
@@ -60,8 +55,7 @@ PNG::PNG(std::string path)
|
||||
Format = Image::ImageFormat::RGBA;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("libpng: Unsupported color format \"%i\" of image \"%s\"", color_type, path.c_str());
|
||||
return;
|
||||
throw Resource::FailedLoadingException("Unsupported color format.");
|
||||
}
|
||||
|
||||
// Convert RGB to RGBA, since DirectX rather treat them all the same way
|
||||
|
||||
@@ -15,19 +15,20 @@ PickingPass::~PickingPass()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PickingPass::InitializeTextures()
|
||||
{
|
||||
GenerateTexture(&m_PickingTexture, GL_CLAMP_TO_BORDER, GL_LINEAR,
|
||||
glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_RG8, GL_RG, GL_UNSIGNED_BYTE);
|
||||
|
||||
GenerateTexture(&m_DepthBuffer, GL_CLAMP_TO_BORDER, GL_NEAREST,
|
||||
glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8);
|
||||
}
|
||||
|
||||
void PickingPass::InitializeFrameBuffers()
|
||||
{
|
||||
glGenRenderbuffers(1, &m_DepthBuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, m_DepthBuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().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_DepthBuffer, GL_DEPTH_ATTACHMENT)));
|
||||
m_PickingBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_PickingTexture, GL_COLOR_ATTACHMENT0)));
|
||||
m_PickingBuffer.Generate();
|
||||
}
|
||||
@@ -41,22 +42,34 @@ void PickingPass::InitializeShaderPrograms()
|
||||
m_PickingProgram->Compile();
|
||||
m_PickingProgram->BindFragDataLocation(0, "TextureFragment");
|
||||
m_PickingProgram->Link();
|
||||
|
||||
m_PickingSkinnedProgram = ResourceManager::Load<ShaderProgram>("#PickingSkinnedProgram");
|
||||
|
||||
m_PickingSkinnedProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/PickingSkinned.vert.glsl")));
|
||||
m_PickingSkinnedProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/Picking.frag.glsl")));
|
||||
m_PickingSkinnedProgram->Compile();
|
||||
m_PickingSkinnedProgram->BindFragDataLocation(0, "TextureFragment");
|
||||
m_PickingSkinnedProgram->Link();
|
||||
}
|
||||
|
||||
void PickingPass::Draw(RenderScene& scene)
|
||||
{
|
||||
GLERROR("PRE");
|
||||
PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle());
|
||||
|
||||
|
||||
//TODO: Render: Add code for more jobs than modeljobs.
|
||||
GLuint shaderHandle = m_PickingProgram->GetHandle();
|
||||
GLuint shaderSkinnedHandle = m_PickingSkinnedProgram->GetHandle();
|
||||
m_PickingProgram->Bind();
|
||||
|
||||
if (scene.ClearDepth) {
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
state->Disable(GL_DEPTH_TEST);
|
||||
state->DepthMask(GL_FALSE);
|
||||
}
|
||||
m_Camera = scene.Camera;
|
||||
|
||||
for (auto &job : scene.OpaqueObjects) {
|
||||
for (auto &job : scene.Jobs.OpaqueObjects) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
|
||||
if (modelJob) {
|
||||
@@ -83,17 +96,29 @@ void PickingPass::Draw(RenderScene& scene)
|
||||
|
||||
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])));
|
||||
if (modelJob->Model->IsSkinned()) {
|
||||
m_PickingSkinnedProgram->Bind();
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
|
||||
glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
|
||||
|
||||
if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) {
|
||||
if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) {
|
||||
|
||||
if (modelJob->Animation != nullptr) {
|
||||
std::vector<glm::mat4> frameBones = modelJob->Skeleton->GetFrameBones(*modelJob->Animation, modelJob->AnimationTime);
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0]));
|
||||
std::vector<glm::mat4> frameBones;
|
||||
if (modelJob->AnimationOffset.animation != nullptr) {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset);
|
||||
} else {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations);
|
||||
}
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0]));
|
||||
}
|
||||
} else {
|
||||
m_PickingProgram->Bind();
|
||||
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);
|
||||
@@ -102,7 +127,62 @@ void PickingPass::Draw(RenderScene& scene)
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &job : scene.TransparentObjects) {
|
||||
/* for (auto &job : scene.Jobs.TransparentObjects) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
|
||||
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] += 1;
|
||||
} else {
|
||||
m_ColorCounter[0] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo;
|
||||
|
||||
if (modelJob) {
|
||||
if (modelJob->Model->IsSkinned()) {
|
||||
m_PickingSkinnedProgram->Bind();
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
|
||||
glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
|
||||
|
||||
std::vector<glm::mat4> frameBones;
|
||||
if (modelJob->AnimationOffset.animation != nullptr) {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset);
|
||||
} else {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations);
|
||||
}
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0]));
|
||||
} else {
|
||||
m_PickingProgram->Bind();
|
||||
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);
|
||||
glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int)));
|
||||
}
|
||||
}*/
|
||||
|
||||
for (auto &job : scene.Jobs.OpaqueShieldedObjects) {
|
||||
auto modelJob = std::dynamic_pointer_cast<ModelJob>(job);
|
||||
|
||||
if (modelJob) {
|
||||
@@ -129,17 +209,27 @@ void PickingPass::Draw(RenderScene& scene)
|
||||
|
||||
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])));
|
||||
if (modelJob->Model->IsSkinned()) {
|
||||
m_PickingSkinnedProgram->Bind();
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
|
||||
glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
|
||||
|
||||
if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) {
|
||||
|
||||
if (modelJob->Animation != nullptr) {
|
||||
std::vector<glm::mat4> frameBones = modelJob->Skeleton->GetFrameBones(*modelJob->Animation, modelJob->AnimationTime);
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0]));
|
||||
std::vector<glm::mat4> frameBones;
|
||||
if (modelJob->AnimationOffset.animation != nullptr) {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset);
|
||||
} else {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations);
|
||||
}
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0]));
|
||||
|
||||
} else {
|
||||
m_PickingProgram->Bind();
|
||||
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);
|
||||
@@ -147,26 +237,145 @@ void PickingPass::Draw(RenderScene& scene)
|
||||
glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto& job : scene.Jobs.SpriteJob) {
|
||||
auto spriteJob = std::dynamic_pointer_cast<SpriteJob>(job);
|
||||
if (!spriteJob->Pickable) {
|
||||
continue;
|
||||
}
|
||||
RenderState jobState;
|
||||
|
||||
if (spriteJob) {
|
||||
if (spriteJob->Depth == 0) {
|
||||
jobState.Disable(GL_DEPTH_TEST);
|
||||
}
|
||||
int pickColor[2] = { m_ColorCounter[0], m_ColorCounter[1] };
|
||||
|
||||
PickingInfo pickInfo;
|
||||
pickInfo.Entity = spriteJob->Entity;
|
||||
pickInfo.World = spriteJob->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] += 1;
|
||||
} else {
|
||||
m_ColorCounter[0] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo;
|
||||
|
||||
m_PickingProgram->Bind();
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(spriteJob->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(spriteJob->Model->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, spriteJob->Model->ElementBuffer);
|
||||
glDrawElements(GL_TRIANGLES, spriteJob->EndIndex - spriteJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(spriteJob->StartIndex * sizeof(unsigned int)));
|
||||
}
|
||||
}
|
||||
|
||||
/* for (auto &job : scene.Jobs.TransparentShieldedObjects) {
|
||||
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] += 1;
|
||||
} else {
|
||||
m_ColorCounter[0] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo;
|
||||
|
||||
if (modelJob->Model->IsSkinned()) {
|
||||
m_PickingSkinnedProgram->Bind();
|
||||
|
||||
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix()));
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix()));
|
||||
glUniform2fv(glGetUniformLocation(shaderSkinnedHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1])));
|
||||
|
||||
if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) {
|
||||
|
||||
std::vector<glm::mat4> frameBones;
|
||||
if (modelJob->AnimationOffset.animation != nullptr) {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations, modelJob->AnimationOffset);
|
||||
} else {
|
||||
frameBones = modelJob->Skeleton->GetFrameBones(modelJob->Animations);
|
||||
}
|
||||
glUniformMatrix4fv(glGetUniformLocation(shaderSkinnedHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0]));
|
||||
|
||||
}
|
||||
} else {
|
||||
m_PickingProgram->Bind();
|
||||
|
||||
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);
|
||||
glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int)));
|
||||
}
|
||||
}*/
|
||||
|
||||
m_PickingBuffer.Unbind();
|
||||
GLERROR("PickingPass Error");
|
||||
|
||||
delete state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PickingPass::ClearPicking()
|
||||
{
|
||||
GLERROR("PRE");
|
||||
m_PickingColorsToEntity.clear();
|
||||
m_EntityColors.clear();
|
||||
m_ColorCounter[0] = 1;
|
||||
m_ColorCounter[1] = 0;
|
||||
m_ColorCounter[0] = 0;
|
||||
m_ColorCounter[1] = 1;
|
||||
|
||||
m_PickingBuffer.Bind();
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_PickingBuffer.Unbind();
|
||||
GLERROR("END");
|
||||
}
|
||||
|
||||
|
||||
void PickingPass::OnWindowResize()
|
||||
{
|
||||
InitializeTextures();
|
||||
m_PickingBuffer.Generate();
|
||||
}
|
||||
|
||||
PickData PickingPass::Pick(glm::vec2 screenCoord)
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
PickingPassState::PickingPassState(GLuint frameBuffer)
|
||||
{
|
||||
GLERROR("---2");
|
||||
GLERROR("PRE");
|
||||
BindFramebuffer(frameBuffer);
|
||||
GLERROR("---3");
|
||||
GLERROR("Bind Framebuffer");
|
||||
Enable(GL_DEPTH_TEST);
|
||||
Enable(GL_CULL_FACE);
|
||||
Disable(GL_BLEND);
|
||||
@@ -13,6 +13,7 @@ PickingPassState::PickingPassState(GLuint frameBuffer)
|
||||
glm::vec4 clearColor = glm::vec4(0.f);
|
||||
//ClearColor(clearColor);
|
||||
//Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
GLERROR("END");
|
||||
}
|
||||
|
||||
PickingPassState::~PickingPassState()
|
||||
|
||||
@@ -271,7 +271,7 @@ RawModelAssimp::RawModelAssimp(std::string fileName)
|
||||
skelAnim.Keyframes.push_back(animationFrame);
|
||||
}
|
||||
|
||||
m_Skeleton->Animations[animationName] = skelAnim;
|
||||
m_Skeleton->Animations[animationName1] = skelAnim;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,20 @@ void RawModelCustom::ReadMeshFile(std::string filePath)
|
||||
ReadMeshFileHeader(offset, fileData);
|
||||
ReadMesh(offset, fileData, fileByteSize);
|
||||
}
|
||||
delete fileData;
|
||||
delete[] fileData;
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadMeshFileHeader(std::size_t& offset, char* fileData)
|
||||
{
|
||||
#ifdef BOOST_LITTLE_ENDIAN
|
||||
m_Vertices.resize(static_cast<std::size_t>(*(unsigned int*)(fileData + offset)));
|
||||
hasSkin = *(bool*)(fileData + offset);
|
||||
offset += sizeof(bool);
|
||||
if (hasSkin) {
|
||||
m_SkinedVertices.resize(static_cast<std::size_t>(*(unsigned int*)(fileData + offset)));
|
||||
}
|
||||
else {
|
||||
m_Vertices.resize(static_cast<std::size_t>(*(unsigned int*)(fileData + offset)));
|
||||
}
|
||||
offset += sizeof(unsigned int);
|
||||
m_Indices.resize(static_cast<std::size_t>(*(unsigned int*)(fileData + offset)));
|
||||
offset += sizeof(unsigned int);
|
||||
@@ -57,12 +64,19 @@ void RawModelCustom::ReadMesh(std::size_t& offset, char* fileData, const unsigne
|
||||
void RawModelCustom::ReadVertices(std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
{
|
||||
#ifdef BOOST_LITTLE_ENDIAN
|
||||
if (offset + m_Vertices.size() * sizeof(Vertex) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading vertices failed");
|
||||
}
|
||||
|
||||
memcpy(&m_Vertices[0], fileData + offset, m_Vertices.size() * sizeof(Vertex));
|
||||
offset += m_Vertices.size() * sizeof(Vertex);
|
||||
if (hasSkin) {
|
||||
if (offset + m_SkinedVertices.size() * sizeof(SkinedVertex) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading skined vertices failed");
|
||||
}
|
||||
memcpy(&m_SkinedVertices[0], fileData + offset, m_SkinedVertices.size() * sizeof(SkinedVertex));
|
||||
offset += m_SkinedVertices.size() * sizeof(SkinedVertex);
|
||||
} else {
|
||||
if (offset + m_Vertices.size() * sizeof(Vertex) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading vertices failed");
|
||||
}
|
||||
memcpy(&m_Vertices[0], fileData + offset, m_Vertices.size() * sizeof(Vertex));
|
||||
offset += m_Vertices.size() * sizeof(Vertex);
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
@@ -102,14 +116,14 @@ void RawModelCustom::ReadMaterialFile(std::string filePath)
|
||||
if (fileByteSize > 0) {
|
||||
ReadMaterials(offset, fileData, fileByteSize);
|
||||
}
|
||||
delete fileData;
|
||||
delete[] fileData;
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadMaterials(std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
{
|
||||
#ifdef BOOST_LITTLE_ENDIAN
|
||||
unsigned int* numMaterials = (unsigned int*)(fileData);
|
||||
MaterialGroups.reserve(*numMaterials);
|
||||
m_Materials.reserve(*numMaterials);
|
||||
offset += sizeof(unsigned int);
|
||||
|
||||
for (unsigned int i = 0; i < *numMaterials; i++) {
|
||||
@@ -121,83 +135,150 @@ void RawModelCustom::ReadMaterials(std::size_t& offset, char* fileData, const un
|
||||
|
||||
void RawModelCustom::ReadMaterialSingle(std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
{
|
||||
MaterialGroup newMaterial;
|
||||
|
||||
MaterialProperties newMaterialProperty;
|
||||
#ifdef BOOST_LITTLE_ENDIAN
|
||||
|
||||
if (offset + sizeof(unsigned int) * 4 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material texture names length failed");
|
||||
}
|
||||
if (offset + sizeof(MaterialType) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material Type failed");
|
||||
}
|
||||
MaterialType type = *(MaterialType*)(fileData + offset);
|
||||
offset += sizeof(MaterialType);
|
||||
|
||||
unsigned int* nameLengths = (unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int) * 4;
|
||||
switch (type) {
|
||||
case MaterialType::Basic:
|
||||
newMaterialProperty.material = new MaterialBasic();
|
||||
ReadMaterialBasic(newMaterialProperty.material, offset, fileData, fileByteSize);
|
||||
break;
|
||||
case MaterialType::SplatMapping:
|
||||
newMaterialProperty.material = new MaterialSplatMapping();
|
||||
ReadMaterialSplatMapping(static_cast<MaterialSplatMapping*>(newMaterialProperty.material), offset, fileData, fileByteSize);
|
||||
break;
|
||||
case MaterialType::SingleTextures:
|
||||
newMaterialProperty.material = new MaterialSingleTextures();
|
||||
ReadMaterialSingleTexture(static_cast<MaterialSingleTextures*>(newMaterialProperty.material), offset, fileData, fileByteSize);
|
||||
break;
|
||||
default:
|
||||
throw Resource::FailedLoadingException("Material contains an unknown MaterialType");
|
||||
};
|
||||
|
||||
if (offset + sizeof(float) * 11 + sizeof(unsigned int) * 2 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material specular, reflection, color and start and end index values failed");
|
||||
}
|
||||
|
||||
newMaterial.SpecularExponent = *(float*)(fileData + offset);
|
||||
offset += sizeof(float);
|
||||
newMaterial.ReflectionFactor = *(float*)(fileData + offset);
|
||||
offset += sizeof(float);
|
||||
|
||||
memcpy(&newMaterial.DiffuseColor[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
memcpy(&newMaterial.SpecularColor[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
memcpy(&newMaterial.IncandescenceColor[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
|
||||
newMaterial.StartIndex = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
newMaterial.EndIndex = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
|
||||
if (nameLengths[0] > 0) {
|
||||
if (offset + nameLengths[0] > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material texture path failed");
|
||||
}
|
||||
|
||||
newMaterial.TexturePath = "Textures/";
|
||||
newMaterial.TexturePath += (fileData + offset);
|
||||
newMaterial.TexturePath += ".png";
|
||||
offset += nameLengths[0];
|
||||
}
|
||||
|
||||
if (nameLengths[1] > 0) {
|
||||
if (offset + nameLengths[1] > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material NormalMap path failed");
|
||||
}
|
||||
newMaterial.NormalMapPath = "Textures/";
|
||||
newMaterial.NormalMapPath += (fileData + offset);
|
||||
newMaterial.NormalMapPath += ".png";
|
||||
offset += nameLengths[1];
|
||||
}
|
||||
|
||||
if (nameLengths[2] > 0) {
|
||||
if (offset + nameLengths[2] > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material SpecularMap path failed");
|
||||
}
|
||||
newMaterial.SpecularMapPath = "Textures/";
|
||||
newMaterial.SpecularMapPath += (fileData + offset);
|
||||
newMaterial.SpecularMapPath += ".png";
|
||||
offset += nameLengths[2];
|
||||
}
|
||||
|
||||
if (nameLengths[3] > 0) {
|
||||
if (offset + nameLengths[3] > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material IncandescenceMap path failed");
|
||||
}
|
||||
newMaterial.IncandescenceMapPath = "Textures/";
|
||||
newMaterial.IncandescenceMapPath += (fileData + offset);
|
||||
newMaterial.IncandescenceMapPath += ".png";
|
||||
offset += nameLengths[3];
|
||||
}
|
||||
newMaterialProperty.type = type;
|
||||
|
||||
#else
|
||||
#endif
|
||||
|
||||
MaterialGroups.push_back(newMaterial);
|
||||
m_Materials.push_back(newMaterialProperty);
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadMaterialBasic(RawModelCustom::MaterialBasic* newMaterial, std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
{
|
||||
if (offset + sizeof(float) * 11 + sizeof(unsigned int) * 2 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material specular, reflection, color and start and end index values failed");
|
||||
}
|
||||
|
||||
newMaterial->SpecularExponent = *(float*)(fileData + offset);
|
||||
offset += sizeof(float);
|
||||
newMaterial->ReflectionFactor = *(float*)(fileData + offset);
|
||||
offset += sizeof(float);
|
||||
|
||||
memcpy(&newMaterial->DiffuseColor[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
memcpy(&newMaterial->SpecularColor[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
memcpy(&newMaterial->IncandescenceColor[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
|
||||
newMaterial->StartIndex = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
newMaterial->EndIndex = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadMaterialSingleTexture(RawModelCustom::MaterialSingleTextures* newMaterial, std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
{
|
||||
ReadMaterialBasic(newMaterial, offset, fileData, fileByteSize);
|
||||
if (offset + sizeof(unsigned char) * 4 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material NumOfMaps failed");
|
||||
}
|
||||
unsigned char numberOfMaps[4];
|
||||
memcpy(numberOfMaps, fileData + offset, sizeof(unsigned char) * 4);
|
||||
offset += sizeof(unsigned char) * 4;
|
||||
|
||||
if (numberOfMaps[0] > 0)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->ColorMap, offset, fileData, fileByteSize);
|
||||
}
|
||||
|
||||
if (numberOfMaps[1] > 0)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->SpecularMap, offset, fileData, fileByteSize);
|
||||
}
|
||||
|
||||
if (numberOfMaps[2] > 0)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->NormalMap, offset, fileData, fileByteSize);
|
||||
}
|
||||
|
||||
if (numberOfMaps[3] > 0)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->IncandescenceMap, offset, fileData, fileByteSize);
|
||||
}
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadMaterialSplatMapping(RawModelCustom::MaterialSplatMapping* newMaterial, std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
{
|
||||
ReadMaterialBasic(newMaterial, offset, fileData, fileByteSize);
|
||||
ReadMaterialTextureProperties(newMaterial->SplatMap, offset, fileData, fileByteSize);
|
||||
if (offset + sizeof(unsigned char) * 4 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material NumOfMaps failed");
|
||||
}
|
||||
|
||||
unsigned char numberOfMaps[4];
|
||||
memcpy(numberOfMaps, fileData + offset, sizeof(unsigned char) * 4);
|
||||
offset += sizeof(unsigned char) * 4;
|
||||
|
||||
newMaterial->ColorMaps.resize(numberOfMaps[0]);
|
||||
for (unsigned char i = 0; i < numberOfMaps[0]; i++)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->ColorMaps[i], offset, fileData, fileByteSize);
|
||||
}
|
||||
|
||||
newMaterial->SpecularMaps.resize(numberOfMaps[1]);
|
||||
for (unsigned char i = 0; i < numberOfMaps[1]; i++)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->SpecularMaps[i], offset, fileData, fileByteSize);
|
||||
}
|
||||
|
||||
newMaterial->NormalMaps.resize(numberOfMaps[2]);
|
||||
for (unsigned char i = 0; i < numberOfMaps[2]; i++)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->NormalMaps[i], offset, fileData, fileByteSize);
|
||||
}
|
||||
|
||||
newMaterial->IncandescenceMaps.resize(numberOfMaps[3]);
|
||||
for (unsigned char i = 0; i < numberOfMaps[3]; i++)
|
||||
{
|
||||
ReadMaterialTextureProperties(newMaterial->IncandescenceMaps[i], offset, fileData, fileByteSize);
|
||||
}
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadMaterialTextureProperties(RawModelCustom::TextureProperties& texture, std::size_t& offset, char* fileData, const unsigned int& fileByteSize) {
|
||||
unsigned int nameLength = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
|
||||
if (nameLength > 0) {
|
||||
if (offset + nameLength > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material texture path failed");
|
||||
}
|
||||
texture.TexturePath = "Textures/";
|
||||
texture.TexturePath += (fileData + offset);
|
||||
texture.TexturePath += ".png";
|
||||
offset += nameLength;
|
||||
if (offset + sizeof(glm::vec2) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading Material texture UVTiling failed");
|
||||
}
|
||||
memcpy(&texture.UVRepeat[0], fileData + offset, sizeof(glm::vec2));
|
||||
}
|
||||
offset += sizeof(glm::vec2);
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadAnimationFile(std::string filePath)
|
||||
@@ -207,7 +288,9 @@ void RawModelCustom::ReadAnimationFile(std::string filePath)
|
||||
std::ifstream in(filePath.c_str(), std::ios_base::binary | std::ios_base::ate);
|
||||
|
||||
if (!in.is_open()) {
|
||||
//throw Resource::FailedLoadingException("Open animation file failed");
|
||||
if (hasSkin) {
|
||||
throw Resource::FailedLoadingException("Open animation file for a skinned mesh failed, unknown stuff will happen");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -233,7 +316,7 @@ void RawModelCustom::ReadAnimationFile(std::string filePath)
|
||||
ReadAnimationBindPoses(offset, fileData, fileByteSize);
|
||||
ReadAnimationClips(offset, fileData, fileByteSize, numAnimations);
|
||||
}
|
||||
delete fileData;
|
||||
delete[] fileData;
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadAnimationBindPoses(std::size_t& offset, char* fileData, const unsigned int& fileByteSize)
|
||||
@@ -318,32 +401,43 @@ void RawModelCustom::ReadAnimationClipSingle(std::size_t& offset, char* fileData
|
||||
if (offset + sizeof(float) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationClip duration failed");
|
||||
}
|
||||
|
||||
newAnimation.Duration = *(float*)(fileData + offset);
|
||||
offset += sizeof(float);
|
||||
|
||||
if (offset + sizeof(unsigned int) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationClip NrOfKeyframes failed");
|
||||
throw Resource::FailedLoadingException("Reading AnimationClip numberOfJointFrames failed");
|
||||
}
|
||||
unsigned int nrOfKeyframes = *(unsigned int*)(fileData + offset);
|
||||
unsigned int numberOfJointFrames = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
|
||||
if (offset + sizeof(unsigned int) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationClip NrOfJoints failed");
|
||||
}
|
||||
unsigned int nrOfJoints = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
for (unsigned int i = 0; i < numberOfJointFrames; i++) {
|
||||
if (offset + sizeof(unsigned int) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationClip JointID failed");
|
||||
}
|
||||
int jointID = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
|
||||
newAnimation.Keyframes.reserve(nrOfKeyframes);
|
||||
for (unsigned int i = 0; i < nrOfKeyframes; i++) {
|
||||
ReadAnimationKeyFrame(offset, fileData, fileByteSize, nrOfJoints, newAnimation);
|
||||
if (offset + sizeof(unsigned int) > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationClip numberOFKeyFrames failed");
|
||||
}
|
||||
unsigned int numberOFKeyFrames = *(unsigned int*)(fileData + offset);
|
||||
offset += sizeof(unsigned int);
|
||||
|
||||
if (numberOFKeyFrames > 0) {
|
||||
newAnimation.JointAnimations[jointID].reserve(numberOFKeyFrames);
|
||||
|
||||
for (unsigned int j = 0; j < numberOFKeyFrames; j++) {
|
||||
ReadAnimationKeyFrame(offset, fileData, fileByteSize, newAnimation.JointAnimations[jointID]);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Skeleton->Animations[newAnimation.Name] = newAnimation;
|
||||
//m_Skeleton->Animations[newAnimation.Name].KeyFrameAmount = nrOfKeyframes;
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
|
||||
void RawModelCustom::ReadAnimationKeyFrame(std::size_t& offset, char* fileData, const unsigned int& fileByteSize, unsigned int numberOfJoints, Skeleton::Animation& animation)
|
||||
void RawModelCustom::ReadAnimationKeyFrame(std::size_t& offset, char* fileData, const unsigned int& fileByteSize, std::vector<Skeleton::Animation::Keyframe>& animation)
|
||||
{
|
||||
Skeleton::Animation::Keyframe newKeyFrame;
|
||||
|
||||
@@ -359,17 +453,27 @@ void RawModelCustom::ReadAnimationKeyFrame(std::size_t& offset, char* fileData,
|
||||
newKeyFrame.Time = *(float*)(fileData + offset);
|
||||
offset += sizeof(float);
|
||||
|
||||
if (offset + sizeof(Skeleton::Animation::Keyframe::BoneProperty) * numberOfJoints> fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationKeyFrame joints failed");
|
||||
if (offset + sizeof(float) * 3 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationKeyFrame Position failed");
|
||||
}
|
||||
memcpy(&newKeyFrame.BoneProperties.Position[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
|
||||
Skeleton::Animation::Keyframe::BoneProperty newBone;
|
||||
for (unsigned int i = 0; i < numberOfJoints; i++) {
|
||||
memcpy(&newBone, (fileData + offset), sizeof(Skeleton::Animation::Keyframe::BoneProperty));
|
||||
offset += sizeof(Skeleton::Animation::Keyframe::BoneProperty);
|
||||
newKeyFrame.BoneProperties[newBone.ID] = newBone;
|
||||
if (offset + sizeof(float) * 4 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationKeyFrame Rotation failed");
|
||||
}
|
||||
animation.Keyframes.push_back(newKeyFrame);
|
||||
memcpy(&newKeyFrame.BoneProperties.Rotation[0], fileData + offset, sizeof(float) * 4);
|
||||
offset += sizeof(float) * 4;
|
||||
|
||||
if (offset + sizeof(float) * 3 > fileByteSize) {
|
||||
throw Resource::FailedLoadingException("Reading AnimationKeyFrame Scale failed");
|
||||
}
|
||||
memcpy(&newKeyFrame.BoneProperties.Scale[0], fileData + offset, sizeof(float) * 3);
|
||||
offset += sizeof(float) * 3;
|
||||
|
||||
|
||||
animation.push_back(newKeyFrame);
|
||||
|
||||
}
|
||||
|
||||
RawModelCustom::~RawModelCustom()
|
||||
@@ -377,6 +481,9 @@ RawModelCustom::~RawModelCustom()
|
||||
if (m_Skeleton != nullptr) {
|
||||
delete m_Skeleton;
|
||||
}
|
||||
for (auto material : m_Materials) {
|
||||
delete material.material;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -6,9 +6,10 @@ bool RenderState::Enable(GLenum cap)
|
||||
//LOG_WARNING("Trying to enable somthing that is already enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ResetFunctions.push_back(std::bind(glDisable, cap));
|
||||
glEnable(cap);
|
||||
return !GLERROR("RenderState::Enable");
|
||||
return !GLERROR("Enable");
|
||||
}
|
||||
|
||||
bool RenderState::Disable(GLenum cap)
|
||||
@@ -16,9 +17,10 @@ bool RenderState::Disable(GLenum cap)
|
||||
if (!glIsEnabled(cap)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ResetFunctions.push_back(std::bind(glEnable, cap));
|
||||
glDisable(cap);
|
||||
return !GLERROR("RenderState::Disable");
|
||||
return !GLERROR("Disable");
|
||||
}
|
||||
|
||||
bool RenderState::CullFace(GLenum mode)
|
||||
@@ -32,7 +34,7 @@ bool RenderState::CullFace(GLenum mode)
|
||||
glGetIntegerv(GL_CULL_FACE_MODE, &original);
|
||||
m_ResetFunctions.push_back(std::bind(glCullFace, original));
|
||||
glCullFace(mode);
|
||||
return !GLERROR("RenderState::CullFace");
|
||||
return !GLERROR("CullFace");
|
||||
}
|
||||
|
||||
bool RenderState::ClearColor(glm::vec4 color)
|
||||
@@ -41,7 +43,7 @@ bool RenderState::ClearColor(glm::vec4 color)
|
||||
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");
|
||||
return !GLERROR("ClearColor");
|
||||
}
|
||||
|
||||
bool RenderState::BindFramebuffer(GLint framebuffer)
|
||||
@@ -55,7 +57,7 @@ bool RenderState::BindFramebuffer(GLint framebuffer)
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, originalDraw);
|
||||
});
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
return !GLERROR("RenderState::BindBuffer");
|
||||
return !GLERROR("BindBuffer");
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +69,7 @@ bool RenderState::BlendEquation(GLenum mode)
|
||||
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &originalAlpha);
|
||||
m_ResetFunctions.push_back(std::bind(glBlendEquationSeparate, originalRGB, originalAlpha));
|
||||
glBlendEquation(mode);
|
||||
return !GLERROR("RenderState::BlendEquation");
|
||||
return !GLERROR("BlendEquation");
|
||||
}
|
||||
|
||||
bool RenderState::BlendFunc(GLenum sfactor, GLenum dfactor)
|
||||
@@ -82,7 +84,46 @@ bool RenderState::BlendFunc(GLenum sfactor, GLenum dfactor)
|
||||
glGetIntegerv(GL_BLEND_DST_ALPHA, &originalDestAlpha);
|
||||
m_ResetFunctions.push_back(std::bind(glBlendFuncSeparate, originalSrcRGB, originalSrcAlpha, originalDestRGB, originalDestAlpha));
|
||||
glBlendFunc(sfactor, dfactor);
|
||||
return !GLERROR("RenderState::BlendFunc");
|
||||
return !GLERROR("BlendFunc");
|
||||
}
|
||||
|
||||
|
||||
bool RenderState::StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
|
||||
{
|
||||
GLint originalSFail;
|
||||
glGetIntegerv(GL_STENCIL_FAIL, &originalSFail);
|
||||
GLint originalDPFail;
|
||||
glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &originalDPFail);
|
||||
GLint originalDPPass;
|
||||
glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &originalDPPass);
|
||||
m_ResetFunctions.push_back(std::bind(glStencilOp, originalSFail, originalDPFail, originalDPPass));
|
||||
glStencilOp(sfail, dpfail, dppass);
|
||||
return !GLERROR("StencilOp");
|
||||
}
|
||||
|
||||
|
||||
bool RenderState::StencilFunc(GLenum func, GLint ref, GLuint mask)
|
||||
{
|
||||
GLint originalFunc;
|
||||
glGetIntegerv(GL_STENCIL_FUNC, &originalFunc);
|
||||
GLint originalRef;
|
||||
glGetIntegerv(GL_STENCIL_REF, &originalRef);
|
||||
GLint originalMask;
|
||||
glGetIntegerv(GL_STENCIL_VALUE_MASK, &originalMask);
|
||||
m_ResetFunctions.push_back(std::bind(glStencilFunc, originalFunc, originalRef, originalMask));
|
||||
glStencilFunc(func, ref, mask);
|
||||
return !GLERROR("StencilFunc");
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool RenderState::StencilMask(GLuint mask)
|
||||
{
|
||||
GLint originalMask;
|
||||
glGetIntegerv(GL_STENCIL_WRITEMASK, &originalMask);
|
||||
m_ResetFunctions.push_back(std::bind(glStencilMask, mask));
|
||||
glStencilMask(mask);
|
||||
return !GLERROR("StencilMask");
|
||||
}
|
||||
|
||||
bool RenderState::DepthMask(GLboolean flag)
|
||||
@@ -91,12 +132,12 @@ bool RenderState::DepthMask(GLboolean flag)
|
||||
glGetBooleanv(GL_DEPTH_WRITEMASK, &original);
|
||||
m_ResetFunctions.push_back(std::bind(glDepthMask, original));
|
||||
glDepthMask(flag);
|
||||
return !GLERROR("RenderState::DepthMask");
|
||||
return !GLERROR("DepthMask");
|
||||
}
|
||||
|
||||
RenderState::~RenderState()
|
||||
{
|
||||
for (auto& f : m_ResetFunctions) {
|
||||
for (auto& f : boost::adaptors::reverse(m_ResetFunctions)) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "Rendering/RenderSystem.h"
|
||||
#include "Collision/Collision.h"
|
||||
#include "Core/Frustum.h"
|
||||
|
||||
RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRenderer* renderer, RenderFrame* renderFrame)
|
||||
: System(world, eventBroker)
|
||||
RenderSystem::RenderSystem(SystemParams params, const IRenderer* renderer, RenderFrame* renderFrame, Octree<EntityAABB>* frustumCullOctree)
|
||||
: System(params)
|
||||
, m_Renderer(renderer)
|
||||
, m_RenderFrame(renderFrame)
|
||||
, m_World(world)
|
||||
, m_Octree(frustumCullOctree)
|
||||
{
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand);
|
||||
@@ -31,6 +33,160 @@ bool RenderSystem::OnSetCamera(Events::SetCamera& e)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void RenderSystem::fillSprites(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
|
||||
{
|
||||
auto sprites = world->GetComponents("Sprite");
|
||||
if (sprites == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& cSprite : *sprites) {
|
||||
bool visible = cSprite["Visible"];
|
||||
if (!visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityWrapper entity(world, cSprite.EntityID);
|
||||
if (!isEntityVisible(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix;
|
||||
|
||||
// See a sprite is an SpriteIndicator
|
||||
bool isIndicator = false;
|
||||
if (world->HasComponent(entity.ID, "SpriteIndicator"))
|
||||
{
|
||||
auto indicator = entity["SpriteIndicator"];
|
||||
|
||||
float minScale = (float)(double)indicator["MinScale"];
|
||||
bool hasTeam = indicator["VisibleForSingleTeamOnly"];
|
||||
isIndicator = true;
|
||||
glm::vec3 pos = Transform::AbsolutePosition(entity);
|
||||
|
||||
|
||||
EntityWrapper entityTeam;
|
||||
if (hasTeam && (entity.HasComponent("Team") || entity.FirstParentWithComponent("Team").Valid()) && m_LocalPlayer.World != nullptr) {
|
||||
if (!entity.HasComponent("Team")) {
|
||||
entityTeam = entity.FirstParentWithComponent("Team");
|
||||
}
|
||||
else {
|
||||
entityTeam = entity;
|
||||
}
|
||||
|
||||
ComponentWrapper& entityTeamComponent = entityTeam["Team"];
|
||||
ComponentWrapper& localComponent = m_LocalPlayer["Team"];
|
||||
int entityTeamInt = entityTeamComponent["Team"];
|
||||
int localComponentInt = localComponent["Team"];
|
||||
int SpectatorInt = localComponent["Team"].Enum("Spectator");
|
||||
if (entityTeamInt != localComponentInt && localComponentInt != SpectatorInt) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Code for check if sprite is inside or outside of screen
|
||||
//glm::vec4 projectedPos = m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix() * glm::vec4(pos, 1.0f);
|
||||
//projectedPos /= projectedPos.w;
|
||||
//// Check if inside of outside of screen.
|
||||
//if (projectedPos.x < -1.0f || projectedPos.x > 1.0f || projectedPos.y < -1.0f || projectedPos.y > 1.0f) {
|
||||
// // is outside of screen
|
||||
//} else {
|
||||
// // is inside of screen
|
||||
//}
|
||||
|
||||
|
||||
glm::vec3 zAxis = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 normal = pos - m_Camera->Position();
|
||||
|
||||
//float distance = glm::length(normal);
|
||||
//if (distance < minDistance) {
|
||||
// pos = pos - glm::normalize(normal) * (distance - minDistance);
|
||||
//} else if (distance > maxDistance) {
|
||||
// pos = pos - glm::normalize(normal) * (distance - maxDistance);
|
||||
//}
|
||||
normal.y = 0;
|
||||
normal = glm::normalize(normal);
|
||||
glm::vec3 right = glm::cross(normal, zAxis);
|
||||
glm::vec3 up = glm::cross(right, normal);
|
||||
|
||||
modelMatrix[0][0] = right.x;
|
||||
modelMatrix[0][1] = right.y;
|
||||
modelMatrix[0][2] = right.z;
|
||||
modelMatrix[0][3] = 0.0f;
|
||||
|
||||
modelMatrix[1][0] = zAxis.x;
|
||||
modelMatrix[1][1] = zAxis.y;
|
||||
modelMatrix[1][2] = zAxis.z;
|
||||
modelMatrix[1][3] = 0.0f;
|
||||
|
||||
modelMatrix[2][0] = normal.x;
|
||||
modelMatrix[2][1] = normal.y;
|
||||
modelMatrix[2][2] = normal.z;
|
||||
modelMatrix[2][3] = 0.0f;
|
||||
|
||||
modelMatrix[3][0] = pos.x;
|
||||
modelMatrix[3][1] = pos.y;
|
||||
modelMatrix[3][2] = pos.z;
|
||||
modelMatrix[3][3] = 1.0f;
|
||||
|
||||
glm::mat4 tranformationMatrix = modelMatrix * glm::scale(Transform::AbsoluteScale(entity));
|
||||
glm::vec4 tmp = tranformationMatrix * glm::vec4(glm::vec3(0.5, 0.5, 0), 1.0f);
|
||||
glm::vec2 projectedTopRight = m_Camera->WorldToScreen(glm::vec3(tmp), m_Renderer->GetViewportSize());
|
||||
tmp = tranformationMatrix * glm::vec4(glm::vec3(-0.5, -0.5, 0), 1.0f);
|
||||
glm::vec2 projectedBottomLeft = m_Camera->WorldToScreen(glm::vec3(tmp), m_Renderer->GetViewportSize());
|
||||
|
||||
float diag = glm::length(projectedBottomLeft - projectedTopRight);
|
||||
if (diag < minScale) {
|
||||
tranformationMatrix = tranformationMatrix * glm::scale(glm::vec3(minScale / diag, minScale / diag, minScale / diag));
|
||||
}
|
||||
modelMatrix = tranformationMatrix;
|
||||
}
|
||||
else {
|
||||
modelMatrix = Transform::ModelMatrix(entity.ID, world);
|
||||
}
|
||||
|
||||
|
||||
std::string diffuseResource = cSprite["DiffuseTexture"];
|
||||
std::string glowResource = cSprite["GlowMap"];
|
||||
bool depthSorted = cSprite["DepthSort"];
|
||||
if (diffuseResource.empty() && glowResource.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float fillPercentage = 0.f;
|
||||
glm::vec4 fillColor = glm::vec4(0);
|
||||
if (world->HasComponent(entity.ID, "Fill")) {
|
||||
auto fillComponent = world->GetComponent(entity.ID, "Fill");
|
||||
fillPercentage = (float)(double)fillComponent["Percentage"];
|
||||
fillColor = (glm::vec4)fillComponent["Color"];
|
||||
}
|
||||
|
||||
std::shared_ptr<SpriteJob> spriteJob = std::shared_ptr<SpriteJob>(new SpriteJob(cSprite, m_Camera, modelMatrix, world, fillColor, fillPercentage, depthSorted, isIndicator));
|
||||
|
||||
jobs.push_back(spriteJob);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderSystem::isEntityVisible(EntityWrapper& entity)
|
||||
{
|
||||
// Only render children of a camera if that camera is currently active
|
||||
if (isChildOfACamera(entity) && !isChildOfCurrentCamera(entity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide things parented to local player if they have the HiddenFromLocalPlayer component
|
||||
bool outOfBodyExperience = ResourceManager::Load<ConfigFile>("Config.ini")->Get<bool>("Debug.OutOfBodyExperience", false);
|
||||
if (
|
||||
(entity.HasComponent("HiddenForLocalPlayer") || entity.FirstParentWithComponent("HiddenForLocalPlayer").Valid())
|
||||
&& (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer))
|
||||
&& !outOfBodyExperience
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderSystem::isChildOfACamera(EntityWrapper entity)
|
||||
{
|
||||
return entity.FirstParentWithComponent("Camera").Valid();
|
||||
@@ -40,14 +196,15 @@ bool RenderSystem::isChildOfCurrentCamera(EntityWrapper entity)
|
||||
return entity == m_CurrentCamera || entity.IsChildOf(m_CurrentCamera);
|
||||
}
|
||||
|
||||
void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& opaqueJobs, std::list<std::shared_ptr<RenderJob>>& transparentJobs)
|
||||
void RenderSystem::fillModels(RenderScene::Queues &Jobs)
|
||||
{
|
||||
auto models = m_World->GetComponents("Model");
|
||||
if (models == nullptr) {
|
||||
return;
|
||||
}
|
||||
Frustum frustum(m_Camera->ProjectionMatrix() * m_Camera->ViewMatrix());
|
||||
std::vector<EntityAABB> seenEntities;
|
||||
m_Octree->ObjectsInFrustum(frustum, seenEntities);
|
||||
|
||||
for (auto& cModel : *models) {
|
||||
for (auto& seenEntity : seenEntities) {
|
||||
EntityWrapper entity = seenEntity.Entity;
|
||||
ComponentWrapper cModel = entity["Model"];
|
||||
bool visible = cModel["Visible"];
|
||||
if (!visible) {
|
||||
continue;
|
||||
@@ -57,15 +214,7 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& opaqueJobs,
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityWrapper entity(m_World, cModel.EntityID);
|
||||
|
||||
// Only render children of a camera if that camera is currently active
|
||||
if (isChildOfACamera(entity) && !isChildOfCurrentCamera(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Hide things parented to local player if they have the HiddenFromLocalPlayer component
|
||||
if (entity.HasComponent("HiddenForLocalPlayer") && (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer))) {
|
||||
if (!isEntityVisible(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -92,7 +241,9 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& opaqueJobs,
|
||||
}
|
||||
|
||||
glm::mat4 modelMatrix = Transform::ModelMatrix(cModel.EntityID, m_World);
|
||||
//Loop through all materialgroups of a model
|
||||
for (auto matGroup : model->MaterialGroups()) {
|
||||
//If the model has an explosioneffect component, we will add an explosioneffectjob
|
||||
if (m_World->HasComponent(cModel.EntityID, "ExplosionEffect")) {
|
||||
auto explosionEffectComponent = m_World->GetComponent(cModel.EntityID, "ExplosionEffect");
|
||||
std::shared_ptr<ExplosionEffectJob> explosionEffectJob = std::shared_ptr<ExplosionEffectJob>(new ExplosionEffectJob(
|
||||
@@ -106,14 +257,33 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& opaqueJobs,
|
||||
fillColor,
|
||||
fillPercentage
|
||||
));
|
||||
if(explosionEffectJob->Color.a != 1.f || explosionEffectJob->EndColor.a != 1.f || explosionEffectJob->DiffuseColor.a != 1.f) {
|
||||
cModel["Transparent"] = true;
|
||||
}
|
||||
if (m_World->HasComponent(cModel.EntityID, "Shield")){
|
||||
explosionEffectJob->CalculateHash();
|
||||
Jobs.ShieldObjects.push_back(explosionEffectJob);
|
||||
} else if (m_World->HasComponent(cModel.EntityID, "Shielded")
|
||||
|| m_World->HasComponent(cModel.EntityID, "Player")) {
|
||||
|
||||
if (cModel["Transparent"]) {
|
||||
transparentJobs.push_back(explosionEffectJob);
|
||||
if (explosionEffectJob->Color.a != 1.f || explosionEffectJob->EndColor.a != 1.f || explosionEffectJob->DiffuseColor.a != 1.f) {
|
||||
cModel["Transparent"] = true;
|
||||
}
|
||||
|
||||
if (cModel["Transparent"]) {
|
||||
Jobs.TransparentShieldedObjects.push_back(explosionEffectJob);
|
||||
} else {
|
||||
explosionEffectJob->CalculateHash();
|
||||
Jobs.OpaqueShieldedObjects.push_back(explosionEffectJob);
|
||||
}
|
||||
} else {
|
||||
opaqueJobs.push_back(explosionEffectJob);
|
||||
if (explosionEffectJob->Color.a != 1.f || explosionEffectJob->EndColor.a != 1.f || explosionEffectJob->DiffuseColor.a != 1.f) {
|
||||
cModel["Transparent"] = true;
|
||||
}
|
||||
|
||||
if (cModel["Transparent"]) {
|
||||
Jobs.TransparentObjects.push_back(explosionEffectJob);
|
||||
} else {
|
||||
explosionEffectJob->CalculateHash();
|
||||
Jobs.OpaqueObjects.push_back(explosionEffectJob);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::shared_ptr<ModelJob> modelJob = std::shared_ptr<ModelJob>(new ModelJob(
|
||||
@@ -126,13 +296,33 @@ void RenderSystem::fillModels(std::list<std::shared_ptr<RenderJob>>& opaqueJobs,
|
||||
fillColor,
|
||||
fillPercentage
|
||||
));
|
||||
if (modelJob->Color.a != 1.f || modelJob->DiffuseColor.a != 1.f) {
|
||||
cModel["Transparent"] = true;
|
||||
}
|
||||
if (cModel["Transparent"]) {
|
||||
transparentJobs.push_back(modelJob);
|
||||
if (m_World->HasComponent(cModel.EntityID, "Shield")) {
|
||||
modelJob->CalculateHash();
|
||||
Jobs.ShieldObjects.push_back(modelJob);
|
||||
} else if (m_World->HasComponent(cModel.EntityID, "Shielded")
|
||||
|| m_World->HasComponent(cModel.EntityID, "Player")) {
|
||||
|
||||
if (modelJob->Color.a != 1.f || modelJob->DiffuseColor.a != 1.f) {
|
||||
cModel["Transparent"] = true;
|
||||
}
|
||||
|
||||
if (cModel["Transparent"]) {
|
||||
Jobs.TransparentShieldedObjects.push_back(modelJob);
|
||||
} else {
|
||||
modelJob->CalculateHash();
|
||||
Jobs.OpaqueShieldedObjects.push_back(modelJob);
|
||||
}
|
||||
} else {
|
||||
opaqueJobs.push_back(modelJob);
|
||||
if (modelJob->Color.a != 1.f || modelJob->DiffuseColor.a != 1.f) {
|
||||
cModel["Transparent"] = true;
|
||||
}
|
||||
|
||||
if (cModel["Transparent"]) {
|
||||
Jobs.TransparentObjects.push_back(modelJob);
|
||||
} else {
|
||||
modelJob->CalculateHash();
|
||||
Jobs.OpaqueObjects.push_back(modelJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +357,6 @@ void RenderSystem::fillPointLights(std::list<std::shared_ptr<RenderJob>>& jobs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RenderSystem::fillDirectionalLights(std::list<std::shared_ptr<RenderJob>>& jobs, World* world)
|
||||
{
|
||||
auto directionalLights = world->GetComponents("DirectionalLight");
|
||||
@@ -189,14 +378,13 @@ void RenderSystem::fillDirectionalLights(std::list<std::shared_ptr<RenderJob>>&
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
@@ -207,6 +395,11 @@ void RenderSystem::fillText(std::list<std::shared_ptr<RenderJob>>& jobs, World*
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityWrapper entity(world, textComponent.EntityID);
|
||||
if (!isEntityVisible(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Font* font;
|
||||
try {
|
||||
font = ResourceManager::Load<Font>(resource);
|
||||
@@ -251,10 +444,13 @@ void RenderSystem::Update(double dt)
|
||||
scene.AmbientColor = (glm::vec4)(*cSceneLight->begin())["AmbientColor"];
|
||||
}
|
||||
|
||||
fillModels(scene.OpaqueObjects, scene.TransparentObjects);
|
||||
fillPointLights(scene.PointLightJobs, m_World);
|
||||
fillDirectionalLights(scene.DirectionalLightJobs, m_World);
|
||||
fillText(scene.TextJobs, m_World);
|
||||
fillModels(scene.Jobs);
|
||||
fillPointLights(scene.Jobs.PointLight, m_World);
|
||||
//TODO: Make sure all objects needed are also sorted.
|
||||
scene.Jobs.OpaqueObjects.sort();
|
||||
fillSprites(scene.Jobs.SpriteJob, m_World);
|
||||
fillDirectionalLights(scene.Jobs.DirectionalLight, m_World);
|
||||
fillText(scene.Jobs.Text, m_World);
|
||||
m_RenderFrame->Add(scene);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "Rendering/Renderer.h"
|
||||
|
||||
std::unordered_map<GLFWwindow*, Renderer*> Renderer::m_WindowToRenderer;
|
||||
|
||||
void Renderer::Initialize()
|
||||
{
|
||||
InitializeWindow();
|
||||
@@ -12,7 +14,6 @@ void Renderer::Initialize()
|
||||
m_TextPass = new TextPass();
|
||||
m_TextPass->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");*/
|
||||
@@ -20,6 +21,18 @@ void Renderer::Initialize()
|
||||
m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker);
|
||||
}
|
||||
|
||||
void Renderer::glfwFrameBufferCallback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
glViewport(0, 0, width, height);
|
||||
Renderer* currentRenderer = m_WindowToRenderer[window];
|
||||
currentRenderer->m_ViewportSize = Rectangle(width, height);
|
||||
currentRenderer->m_DrawFinalPass->OnWindowResize();
|
||||
currentRenderer->m_LightCullingPass->OnWindowResize();
|
||||
currentRenderer->m_PickingPass->OnWindowResize();
|
||||
currentRenderer->m_DrawBloomPass->OnWindowResize();
|
||||
currentRenderer->m_SSAOPass->OnWindowResize();
|
||||
}
|
||||
|
||||
void Renderer::InitializeWindow()
|
||||
{
|
||||
// Initialize GLFW
|
||||
@@ -39,6 +52,7 @@ void Renderer::InitializeWindow()
|
||||
LOG_ERROR("GLFW: Failed to create window");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
glfwSetFramebufferSizeCallback(m_Window, &glfwFrameBufferCallback);
|
||||
glfwMakeContextCurrent(m_Window);
|
||||
|
||||
// GL version info
|
||||
@@ -51,7 +65,7 @@ void Renderer::InitializeWindow()
|
||||
ss << " DEBUG";
|
||||
#endif
|
||||
LOG_INFO(ss.str().c_str());
|
||||
glfwSetWindowTitle(m_Window, ss.str().c_str());
|
||||
SetWindowTitle(ss.str());
|
||||
|
||||
// Initialize GLEW
|
||||
if (glewInit() != GLEW_OK) {
|
||||
@@ -59,8 +73,10 @@ void Renderer::InitializeWindow()
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
m_WindowToRenderer[m_Window] = this;
|
||||
|
||||
int windowSize[2];
|
||||
glfwGetWindowSize(m_Window, &windowSize[0], &windowSize[1]);
|
||||
glfwGetFramebufferSize(m_Window, &windowSize[0], &windowSize[1]);
|
||||
m_ViewportSize = Rectangle(windowSize[0], windowSize[1]);
|
||||
}
|
||||
|
||||
@@ -85,37 +101,83 @@ void Renderer::Update(double dt)
|
||||
|
||||
void Renderer::Draw(RenderFrame& frame)
|
||||
{
|
||||
ImGui::Combo("Draw textures", &m_DebugTextureToDraw, "Final\0Scene\0Bloom\0Gaussian\0Picking");
|
||||
GLERROR("PRE");
|
||||
ImGui::Combo("Draw textures", &m_DebugTextureToDraw, "Final\0Scene\0Bloom\0SceneLowRes\0BloomLowRes\0Gaussian\0Picking\0Ambient Occlusion");
|
||||
ImGui::Combo("CubeMap", &m_CubeMapTexture, "Nevada(512)\0Sky(1024)");
|
||||
if(m_CubeMapTexture == 0) {
|
||||
m_CubeMapPass->LoadTextures("Nevada");
|
||||
} else if (m_CubeMapTexture == 1) {
|
||||
m_CubeMapPass->LoadTextures("Sky");
|
||||
}
|
||||
|
||||
ImGui::SliderFloat("SSAO sample radius", &m_SSAO_Radius, 0.01f, 5.0f);
|
||||
ImGui::SliderFloat("SSAO bias", &m_SSAO_Bias, 0.0f, 0.1f);
|
||||
ImGui::SliderFloat("SSAO contrast", &m_SSAO_Contrast, 0.0f, 10.0f);
|
||||
ImGui::SliderFloat("SSAO IntensityScale", &m_SSAO_IntensityScale, 0.0f, 10.0f);
|
||||
ImGui::SliderInt("SSAO Number of Samples", &m_SSAO_NumOfSamples, 2, 100);
|
||||
ImGui::SliderInt("SSAO Number of Turns", &m_SSAO_NumOfTurns, 0, 50);
|
||||
m_SSAOPass->Setting(m_SSAO_Radius, m_SSAO_Bias, m_SSAO_Contrast, m_SSAO_IntensityScale, m_SSAO_NumOfSamples, m_SSAO_NumOfTurns);
|
||||
GLERROR("SSAO Settings");
|
||||
//clear buffer 0
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
//Clear other buffers
|
||||
PerformanceTimer::StartTimer("Renderer-ClearBuffers");
|
||||
m_PickingPass->ClearPicking();
|
||||
m_DrawFinalPass->ClearBuffer();
|
||||
m_DrawBloomPass->ClearBuffer();
|
||||
m_ShadowPass->ClearBuffer();
|
||||
|
||||
m_SSAOPass->ClearBuffer();
|
||||
PerformanceTimer::StopTimer("Renderer-ClearBuffers");
|
||||
GLERROR("ClearBuffers");
|
||||
for (auto scene : frame.RenderScenes) {
|
||||
PerformanceTimer::StartTimer("Renderer-Depth");
|
||||
m_PickingPass->Draw(*scene);
|
||||
GLERROR("Drawing pickingpass");
|
||||
PerformanceTimer::StopTimer("Renderer-Depth");
|
||||
}
|
||||
PerformanceTimer::StartTimer("AO generation");
|
||||
m_SSAOPass->Draw(m_PickingPass->DepthBuffer(), frame.RenderScenes.front()->Camera);
|
||||
GLuint ao = m_SSAOPass->SSAOTexture();
|
||||
PerformanceTimer::StopTimer("AO generation");
|
||||
for (auto scene : frame.RenderScenes){
|
||||
|
||||
PerformanceTimer::StartTimer("Renderer-Drawing PickingPass");
|
||||
SortRenderJobsByDepth(*scene);
|
||||
m_PickingPass->Draw(*scene);
|
||||
GLERROR("SortByDepth");
|
||||
m_ShadowPass->Draw(*scene);
|
||||
PerformanceTimer::StartTimerAndStopPrevious("Renderer-Generate Frustrums");
|
||||
m_LightCullingPass->GenerateNewFrustum(*scene);
|
||||
GLERROR("Generate frustums");
|
||||
PerformanceTimer::StartTimerAndStopPrevious("Renderer-Filling Light List");
|
||||
m_LightCullingPass->FillLightList(*scene);
|
||||
GLERROR("Filling light list");
|
||||
PerformanceTimer::StartTimerAndStopPrevious("Renderer-Light Culling");
|
||||
m_LightCullingPass->CullLights(*scene);
|
||||
m_DrawFinalPass->Draw(*scene);
|
||||
GLERROR("LightCulling");
|
||||
m_DrawFinalPass->Draw(*scene, ao);
|
||||
PerformanceTimer::StartTimerAndStopPrevious("Renderer-Draw Geometry+Light");
|
||||
GLERROR("Draw Geometry+Light");
|
||||
//m_DrawScenePass->Draw(*scene);
|
||||
|
||||
GLERROR("Renderer::Draw m_DrawScenePass->Draw");
|
||||
|
||||
PerformanceTimer::StartTimerAndStopPrevious("Renderer-Draw Text");
|
||||
m_TextPass->Draw(*scene, *m_DrawFinalPass->FinalPassFrameBuffer());
|
||||
GLERROR("Draw Text");
|
||||
PerformanceTimer::StopTimer("Renderer-Draw Text");
|
||||
}
|
||||
|
||||
}
|
||||
PerformanceTimer::StartTimer("Renderer-Draw Bloom");
|
||||
m_DrawBloomPass->Draw(m_DrawFinalPass->BloomTexture());
|
||||
PerformanceTimer::StopTimer("Renderer-Draw Bloom");
|
||||
|
||||
if (m_DebugTextureToDraw == 0) {
|
||||
m_DrawColorCorrectionPass->Draw(m_DrawFinalPass->SceneTexture(), m_DrawBloomPass->GaussianTexture(), frame.Gamma, frame.Exposure);
|
||||
PerformanceTimer::StartTimer("Renderer-Color Correction Pass");
|
||||
m_DrawColorCorrectionPass->Draw(m_DrawFinalPass->SceneTexture(), m_DrawBloomPass->GaussianTexture(), m_DrawFinalPass->SceneTextureLowRes(), m_DrawFinalPass->BloomTextureLowRes(), frame.Gamma, frame.Exposure);
|
||||
PerformanceTimer::StopTimer("Renderer-Color Correction Pass");
|
||||
}
|
||||
|
||||
PerformanceTimer::StartTimer("Renderer-Misc Debug Draws");
|
||||
if (m_DebugTextureToDraw == 1) {
|
||||
m_DrawScreenQuadPass->Draw(m_DrawFinalPass->SceneTexture());
|
||||
}
|
||||
@@ -123,14 +185,27 @@ void Renderer::Draw(RenderFrame& frame)
|
||||
m_DrawScreenQuadPass->Draw(m_DrawFinalPass->BloomTexture());
|
||||
}
|
||||
if (m_DebugTextureToDraw == 3) {
|
||||
m_DrawScreenQuadPass->Draw(m_DrawBloomPass->GaussianTexture());
|
||||
m_DrawScreenQuadPass->Draw(m_DrawFinalPass->SceneTextureLowRes());
|
||||
}
|
||||
if (m_DebugTextureToDraw == 4) {
|
||||
m_DrawScreenQuadPass->Draw(m_DrawFinalPass->BloomTextureLowRes());
|
||||
}
|
||||
if (m_DebugTextureToDraw == 5) {
|
||||
m_DrawScreenQuadPass->Draw(m_DrawBloomPass->GaussianTexture());
|
||||
}
|
||||
if (m_DebugTextureToDraw == 6) {
|
||||
m_DrawScreenQuadPass->Draw(m_PickingPass->PickingTexture());
|
||||
}
|
||||
if (m_DebugTextureToDraw == 7) {
|
||||
m_DrawScreenQuadPass->Draw(m_SSAOPass->SSAOTexture());
|
||||
}
|
||||
PerformanceTimer::StopTimer("Renderer-Misc Debug Draws");
|
||||
|
||||
PerformanceTimer::StartTimer("Renderer-ImGuiRenderPass");
|
||||
m_ImGuiRenderPass->Draw();
|
||||
glfwSwapBuffers(m_Window);
|
||||
GLERROR("Imgui draw");
|
||||
glfwSwapBuffers(m_Window);
|
||||
PerformanceTimer::StopTimer("Renderer-ImGuiRenderPass");
|
||||
}
|
||||
|
||||
PickData Renderer::Pick(glm::vec2 screenCoord)
|
||||
@@ -140,15 +215,17 @@ PickData Renderer::Pick(glm::vec2 screenCoord)
|
||||
|
||||
void Renderer::InitializeTextures()
|
||||
{
|
||||
m_ErrorTexture = ResourceManager::Load<Texture>("Textures/Core/ErrorTexture.png");
|
||||
m_WhiteTexture = ResourceManager::Load<Texture>("Textures/Core/White.png");
|
||||
m_ErrorTexture = CommonFunctions::LoadTexture("Textures/Core/ErrorTexture.png", false);
|
||||
m_WhiteTexture = CommonFunctions::LoadTexture("Textures/Core/White.png", false);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::SortRenderJobsByDepth(RenderScene &scene)
|
||||
{
|
||||
//Sort all forward jobs so transparency is good.
|
||||
scene.TransparentObjects.sort(Renderer::DepthSort);
|
||||
scene.Jobs.TransparentObjects.sort(Renderer::DepthSort);
|
||||
scene.Jobs.SpriteJob.sort(Renderer::DepthSort);
|
||||
scene.Jobs.Text.sort(Renderer::DepthSort);
|
||||
}
|
||||
|
||||
void Renderer::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type)
|
||||
@@ -168,8 +245,10 @@ void Renderer::InitializeRenderPasses()
|
||||
m_PickingPass = new PickingPass(this, m_EventBroker);
|
||||
m_LightCullingPass = new LightCullingPass(this);
|
||||
m_ShadowPass = new ShadowPass(this);
|
||||
m_DrawFinalPass = new DrawFinalPass(this, m_LightCullingPass, m_ShadowPass);
|
||||
m_CubeMapPass = new CubeMapPass(this);
|
||||
m_DrawFinalPass = new DrawFinalPass(this, m_LightCullingPass, m_CubeMapPass, m_ShadowPass);
|
||||
m_DrawScreenQuadPass = new DrawScreenQuadPass(this);
|
||||
m_DrawBloomPass = new DrawBloomPass(this);
|
||||
m_DrawColorCorrectionPass = new DrawColorCorrectionPass(this);
|
||||
m_SSAOPass = new SSAOPass(this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "Rendering/SSAOPass.h"
|
||||
|
||||
SSAOPass::SSAOPass(IRenderer* renderer)
|
||||
{
|
||||
m_Renderer = renderer;
|
||||
|
||||
m_ScreenQuad = ResourceManager::Load<Model>("Models/Core/ScreenQuad.mesh");
|
||||
|
||||
InitializeTexture();
|
||||
InitializeBuffer();
|
||||
InitializeShaderProgram();
|
||||
Setting(0.1f, 0.012f, 1.0f, 1.0f, 13, 7);
|
||||
|
||||
m_DrawBloomPass = new DrawBloomPass(renderer);
|
||||
}
|
||||
|
||||
void SSAOPass::InitializeShaderProgram()
|
||||
{
|
||||
m_SSAOProgram = ResourceManager::Load<ShaderProgram>("##SSAOProgram");
|
||||
m_SSAOProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/SSAO.vert.glsl")));
|
||||
m_SSAOProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/SSAO.frag.glsl")));
|
||||
m_SSAOProgram->Compile();
|
||||
m_SSAOProgram->Link();
|
||||
|
||||
m_SSAOViewSpaceZProgram = ResourceManager::Load<ShaderProgram>("##SSAOViewSpaceZProgram");
|
||||
m_SSAOViewSpaceZProgram->AddShader(std::shared_ptr<Shader>(new VertexShader("Shaders/SSAO.vert.glsl")));
|
||||
m_SSAOViewSpaceZProgram->AddShader(std::shared_ptr<Shader>(new FragmentShader("Shaders/SSAOViewSpaceZ.frag.glsl")));
|
||||
m_SSAOViewSpaceZProgram->Compile();
|
||||
m_SSAOViewSpaceZProgram->Link();
|
||||
}
|
||||
|
||||
void SSAOPass::InitializeTexture() {
|
||||
GenerateTexture(&m_SSAOTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_R8, GL_RED, GL_FLOAT);
|
||||
GenerateTexture(&m_SSAOViewSpaceZTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_R32F, GL_RED, GL_FLOAT);
|
||||
}
|
||||
|
||||
void SSAOPass::InitializeBuffer()
|
||||
{
|
||||
m_SSAOFramBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_SSAOTexture, GL_COLOR_ATTACHMENT0)));
|
||||
m_SSAOFramBuffer.Generate();
|
||||
|
||||
m_SSAOViewSpaceZFramBuffer.AddResource(std::shared_ptr<BufferResource>(new Texture2D(&m_SSAOViewSpaceZTexture, GL_COLOR_ATTACHMENT0)));
|
||||
m_SSAOViewSpaceZFramBuffer.Generate();
|
||||
}
|
||||
|
||||
void SSAOPass::ClearBuffer()
|
||||
{
|
||||
m_SSAOFramBuffer.Bind();
|
||||
glClearColor(1.f, 1.f, 1.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_SSAOFramBuffer.Unbind();
|
||||
|
||||
m_SSAOViewSpaceZFramBuffer.Bind();
|
||||
glClearColor(1.f, 1.f, 1.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
m_SSAOViewSpaceZFramBuffer.Unbind();
|
||||
}
|
||||
|
||||
void SSAOPass::Setting(float radius, float bias, float contrast, float intensityScale, int numOfSamples, int NumOfTurns) {
|
||||
m_Radius = radius;
|
||||
m_Bias = bias;
|
||||
m_Contrast = contrast;
|
||||
m_IntensityScale = intensityScale;
|
||||
m_NumOfSamples = numOfSamples;
|
||||
m_NumOfTurns = NumOfTurns;
|
||||
}
|
||||
|
||||
void SSAOPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const
|
||||
{
|
||||
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);
|
||||
GLERROR("Texture initialization failed");
|
||||
}
|
||||
|
||||
void SSAOPass::Draw(GLuint depthBuffer, Camera* camera)
|
||||
{
|
||||
SSAOPassState state;
|
||||
GLuint viewSpaceZPShaderHandle = m_SSAOViewSpaceZProgram->GetHandle();
|
||||
GLuint SSAOShaderHandle = m_SSAOProgram->GetHandle();
|
||||
|
||||
m_SSAOViewSpaceZFramBuffer.Bind();
|
||||
m_SSAOViewSpaceZProgram->Bind();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, depthBuffer);
|
||||
glm::vec3 clipInfo = glm::vec3(
|
||||
(camera->NearClip() * camera->FarClip()),
|
||||
(camera->NearClip() - camera->FarClip()),
|
||||
(camera->FarClip())
|
||||
);
|
||||
/*glm::vec3 clipInfo = glm::vec3(
|
||||
(camera->NearClip()),
|
||||
(-1.0f),
|
||||
(+1.0f)
|
||||
);*/
|
||||
glUniform3fv(glGetUniformLocation(viewSpaceZPShaderHandle, "ClipInfo"), 1, glm::value_ptr(clipInfo));
|
||||
|
||||
glBindVertexArray(m_ScreenQuad->VAO);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex + 1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
|
||||
glm::vec4 projInfo = glm::vec4(
|
||||
((1.0 - camera->ProjectionMatrix()[0][2]) / camera->ProjectionMatrix()[0][0]),
|
||||
(-2.0 / (m_Renderer->GetViewportSize().Width * camera->ProjectionMatrix()[0][0])),
|
||||
((1.0 + camera->ProjectionMatrix()[1][2]) / camera->ProjectionMatrix()[1][1]),
|
||||
(-2.0 / (m_Renderer->GetViewportSize().Height * camera->ProjectionMatrix()[1][1]))
|
||||
);
|
||||
|
||||
|
||||
m_SSAOFramBuffer.Bind();
|
||||
m_SSAOProgram->Bind();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, m_SSAOViewSpaceZTexture);
|
||||
|
||||
// How many pixel there are in a 1m long object 1m away from the camera
|
||||
glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uProjScale"), m_Renderer->GetViewportSize().Height / (-2.0f * glm::tan(camera->FOV() * 0.5f)));
|
||||
|
||||
glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uRadius"), m_Radius);
|
||||
glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uBias"), m_Bias);
|
||||
glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uContrast"), m_Contrast);
|
||||
glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uIntensityScale"), m_IntensityScale);
|
||||
glUniform1i(glGetUniformLocation(SSAOShaderHandle, "uNumOfSamples"), m_NumOfSamples);
|
||||
glUniform1i(glGetUniformLocation(SSAOShaderHandle, "uNumOfTurns"), m_NumOfTurns);;
|
||||
|
||||
glUniform4fv(glGetUniformLocation(SSAOShaderHandle, "uProjInfo"), 1, glm::value_ptr(projInfo));
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].material->EndIndex - m_ScreenQuad->MaterialGroups()[0].material->StartIndex + 1
|
||||
, GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].material->StartIndex);
|
||||
|
||||
m_DrawBloomPass->ClearBuffer();
|
||||
m_DrawBloomPass->Draw(m_SSAOTexture);
|
||||
}
|
||||
|
||||
void SSAOPass::OnWindowResize() {
|
||||
m_DrawBloomPass->OnWindowResize();
|
||||
InitializeTexture();
|
||||
m_SSAOFramBuffer.Generate();
|
||||
m_SSAOViewSpaceZFramBuffer.Generate();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "Rendering/SSAOPassState.h"
|
||||
|
||||
|
||||
SSAOPassState::SSAOPassState()
|
||||
{
|
||||
//BindFramebuffer(0);
|
||||
Disable(GL_BLEND);
|
||||
Disable(GL_DEPTH_TEST);
|
||||
Disable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
SSAOPassState::~SSAOPassState()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -98,8 +98,7 @@ void ShaderProgram::AddShader(std::shared_ptr<Shader> shader)
|
||||
|
||||
void ShaderProgram::Compile()
|
||||
{
|
||||
if (m_ShaderProgramHandle == 0)
|
||||
{
|
||||
if (m_ShaderProgramHandle == 0) {
|
||||
m_ShaderProgramHandle = glCreateProgram();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ Skeleton::~Skeleton()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Skeleton::Animation* Skeleton::GetAnimation(std::string name)
|
||||
{
|
||||
auto it = Animations.find(name);
|
||||
@@ -39,66 +41,649 @@ const Skeleton::Animation* Skeleton::GetAnimation(std::string name)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<glm::mat4> Skeleton::GetFrameBones(const Animation& animation, double time, bool noRootMotion /*= false*/)
|
||||
std::vector<glm::mat4> Skeleton::GetFrameBones(std::vector<AnimationData> animations, bool noRootMotion /*= false*/)
|
||||
{
|
||||
// HACK: Animation wrap-around
|
||||
while (time < 0) {
|
||||
time += animation.Duration;
|
||||
}
|
||||
while (time > animation.Duration) {
|
||||
time -= animation.Duration;
|
||||
}
|
||||
if (animations.size() <= 0) {
|
||||
std::vector<glm::mat4> finalMatrices;
|
||||
for (auto& b : Bones) {
|
||||
finalMatrices.push_back(glm::mat4(1));//b.second->OffsetMatrix);
|
||||
}
|
||||
return finalMatrices;
|
||||
}
|
||||
|
||||
int currentKeyframeIndex = GetKeyframe(animation, time);
|
||||
std::map<int, glm::mat4> frameBones;
|
||||
AccumulateBoneTransforms(true, animations, frameBones, RootBone, glm::mat4(1));
|
||||
|
||||
const Animation::Keyframe& currentFrame = animation.Keyframes[currentKeyframeIndex];
|
||||
const Animation::Keyframe& nextFrame = animation.Keyframes[(currentKeyframeIndex + 1) % animation.Keyframes.size()];
|
||||
double alpha = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
|
||||
//auto animationFrame = Animations[""].Keyframes[frame];
|
||||
std::map<int, glm::mat4> frameBones;
|
||||
AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, static_cast<float>(alpha), frameBones, RootBone, glm::mat4(1));
|
||||
|
||||
std::vector<glm::mat4> finalMatrices;
|
||||
for (auto &kv : frameBones) {
|
||||
finalMatrices.push_back(kv.second);
|
||||
}
|
||||
return finalMatrices;
|
||||
std::vector<glm::mat4> finalMatrices;
|
||||
for (auto &kv : frameBones) {
|
||||
finalMatrices.push_back(kv.second);
|
||||
}
|
||||
return finalMatrices;
|
||||
}
|
||||
|
||||
void Skeleton::AccumulateBoneTransforms(bool noRootMotion, const Animation::Keyframe ¤tFrame, const Animation::Keyframe &nextFrame, float progress, std::map<int, glm::mat4> &boneMatrices, const Bone* bone, glm::mat4 parentMatrix)
|
||||
std::vector<glm::mat4> Skeleton::GetFrameBones(std::vector<AnimationData> animations, AnimationOffset animationOffset, bool noRootMotion /*= false*/)
|
||||
{
|
||||
if (animations.size() <= 0 || animationOffset.animation == nullptr) {
|
||||
std::vector<glm::mat4> finalMatrices;
|
||||
for (auto& b : Bones) {
|
||||
finalMatrices.push_back(glm::mat4(1));//b.second->OffsetMatrix);
|
||||
}
|
||||
return finalMatrices;
|
||||
}
|
||||
|
||||
|
||||
std::map<int, glm::mat4> frameBones;
|
||||
AccumulateBoneTransforms(true, animations, animationOffset, frameBones, RootBone, glm::mat4(1));
|
||||
|
||||
std::vector<glm::mat4> finalMatrices;
|
||||
for (auto &kv : frameBones) {
|
||||
finalMatrices.push_back(kv.second);
|
||||
}
|
||||
return finalMatrices;
|
||||
}
|
||||
|
||||
void Skeleton::AccumulateBoneTransforms(bool noRootMotion, std::vector<AnimationData> animations, std::map<int, glm::mat4>& boneMatrices, const Bone* bone, glm::mat4 parentMatrix)
|
||||
{
|
||||
glm::mat4 boneMatrix;
|
||||
std::vector<JointFrameTransform> JointTransforms;
|
||||
|
||||
for (const AnimationData animationData : animations) {
|
||||
const Animation* animation = animationData.animation;
|
||||
const float time = animationData.time;
|
||||
|
||||
JointFrameTransform jointTransform;
|
||||
jointTransform.Weight = animationData.weight;;
|
||||
|
||||
if (animation->JointAnimations.find(bone->ID) != animation->JointAnimations.end()) {
|
||||
std::vector<Animation::Keyframe> boneKeyFrames = animation->JointAnimations.at(bone->ID);
|
||||
|
||||
Animation::Keyframe currentFrame;
|
||||
Animation::Keyframe nextFrame;
|
||||
|
||||
if (boneKeyFrames.size() > 1) { // 2+ keyframes for the current bone
|
||||
for (int index = boneKeyFrames.size()-1; index >= 0; index--) { // find the bone keyframes that surrounds the current frame
|
||||
if (time >= boneKeyFrames.at(index).Time) {
|
||||
currentFrame = boneKeyFrames.at(index);
|
||||
nextFrame = boneKeyFrames.at((index + 1) % boneKeyFrames.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float progress;
|
||||
|
||||
if (nextFrame.Index == 0) {
|
||||
nextFrame = currentFrame;
|
||||
progress = (time - currentFrame.Time) / (animation->Duration - currentFrame.Time);
|
||||
} else {
|
||||
progress = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (progress > 1.0f || progress < 0.0f) {
|
||||
progress = glm::clamp(progress, 0.0f, 1.0f);
|
||||
}
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties;
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties;
|
||||
|
||||
jointTransform.PositionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
jointTransform.RotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
jointTransform.ScaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
// Flag for no root motion
|
||||
if (bone == RootBone && noRootMotion) {
|
||||
jointTransform.PositionInterp.x = 0;
|
||||
jointTransform.PositionInterp.z = 0;
|
||||
}
|
||||
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
} else { // 1 keyframes for the current bone
|
||||
currentFrame = boneKeyFrames.at(0);
|
||||
jointTransform.PositionInterp = currentFrame.BoneProperties.Position;
|
||||
jointTransform.RotationInterp = currentFrame.BoneProperties.Rotation;
|
||||
jointTransform.ScaleInterp = currentFrame.BoneProperties.Scale;
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
}
|
||||
} else { // 0 keyframes for the current bone
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (JointTransforms.size() <= 0) {
|
||||
if (bone->Parent) {
|
||||
boneMatrix = parentMatrix * glm::inverse(bone->OffsetMatrix) * bone->Parent->OffsetMatrix;
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
} else {
|
||||
boneMatrix = glm::inverse(bone->OffsetMatrix);
|
||||
boneMatrices[bone->ID] = parentMatrix;
|
||||
}
|
||||
} else if (JointTransforms.size() == 1) {
|
||||
boneMatrix = parentMatrix *(glm::translate(JointTransforms.at(0).PositionInterp) * glm::toMat4(JointTransforms.at(0).RotationInterp) * glm::scale(JointTransforms.at(0).ScaleInterp));
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
} else {
|
||||
|
||||
glm::vec3 finalPosInterp;
|
||||
glm::quat finalRotInterp;
|
||||
glm::vec3 finalScaleInterp;
|
||||
float totalWeight = 0;
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
totalWeight += jointTransform.Weight;
|
||||
}
|
||||
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
if (jointTransform.Weight == 1.0f) {
|
||||
finalPosInterp = jointTransform.PositionInterp;
|
||||
finalRotInterp = jointTransform.RotationInterp;
|
||||
finalScaleInterp = jointTransform.ScaleInterp;
|
||||
break;
|
||||
} else {
|
||||
finalPosInterp += jointTransform.PositionInterp * (jointTransform.Weight/totalWeight);
|
||||
finalRotInterp *= glm::slerp(glm::quat(), jointTransform.RotationInterp, (jointTransform.Weight/totalWeight));
|
||||
finalScaleInterp += jointTransform.ScaleInterp * (jointTransform.Weight/totalWeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boneMatrix = parentMatrix *(glm::translate(finalPosInterp) * glm::toMat4(finalRotInterp) * glm::scale(finalScaleInterp));
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (auto &child : bone->Children) {
|
||||
AccumulateBoneTransforms(noRootMotion, animations, boneMatrices, child, boneMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
void Skeleton::AccumulateBoneTransforms(bool noRootMotion, std::vector<AnimationData> animations, AnimationOffset animationOffset, std::map<int, glm::mat4>& boneMatrices, const Bone* bone, glm::mat4 parentMatrix)
|
||||
{
|
||||
glm::mat4 boneMatrix;
|
||||
|
||||
if (currentFrame.BoneProperties.find(bone->ID) != currentFrame.BoneProperties.end() || nextFrame.BoneProperties.find(bone->ID) != nextFrame.BoneProperties.end()) {
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties.at(bone->ID);
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties.at(bone->ID);
|
||||
std::vector<JointFrameTransform> JointTransforms;
|
||||
|
||||
glm::vec3 positionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
glm::quat rotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
glm::vec3 scaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
for (const AnimationData animationData : animations) {
|
||||
const Animation* animation = animationData.animation;
|
||||
const float time = animationData.time;
|
||||
|
||||
// Flag for no root motion
|
||||
if (bone == RootBone && noRootMotion) {
|
||||
positionInterp.x = 0;
|
||||
positionInterp.z = 0;
|
||||
}
|
||||
JointFrameTransform jointTransform;
|
||||
jointTransform.Weight = animationData.weight;;
|
||||
|
||||
if (animation->JointAnimations.find(bone->ID) != animation->JointAnimations.end()) {
|
||||
std::vector<Animation::Keyframe> boneKeyFrames = animation->JointAnimations.at(bone->ID);
|
||||
|
||||
Animation::Keyframe currentFrame;
|
||||
Animation::Keyframe nextFrame;
|
||||
|
||||
if (boneKeyFrames.size() > 1) { // 2+ keyframes for the current bone
|
||||
for (int index = boneKeyFrames.size()-1; index >= 0; index--) { // find the bone keyframes that surrounds the current frame
|
||||
if (time >= boneKeyFrames.at(index).Time) {
|
||||
currentFrame = boneKeyFrames.at(index);
|
||||
nextFrame = boneKeyFrames.at((index + 1) % boneKeyFrames.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float progress;
|
||||
|
||||
if (nextFrame.Index == 0) {
|
||||
nextFrame = currentFrame;
|
||||
progress = (time - currentFrame.Time) / (animation->Duration - currentFrame.Time);
|
||||
} else {
|
||||
progress = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
|
||||
}
|
||||
|
||||
|
||||
boneMatrix = parentMatrix * (glm::translate(positionInterp) * glm::toMat4(rotationInterp) * glm::scale(scaleInterp));
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
} else {
|
||||
if (bone->Parent) {
|
||||
boneMatrix = parentMatrix; // * glm::inverse(bone->OffsetMatrix);
|
||||
if (progress > 1.0f || progress < 0.0f) {
|
||||
progress = glm::clamp(progress, 0.0f, 1.0f);
|
||||
}
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties;
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties;
|
||||
|
||||
jointTransform.PositionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
jointTransform.RotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
jointTransform.ScaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
// Flag for no root motion
|
||||
if (bone == RootBone && noRootMotion) {
|
||||
jointTransform.PositionInterp.x = 0;
|
||||
jointTransform.PositionInterp.z = 0;
|
||||
}
|
||||
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
} else { // 1 keyframes for the current bone
|
||||
currentFrame = boneKeyFrames.at(0);
|
||||
jointTransform.PositionInterp = currentFrame.BoneProperties.Position;
|
||||
jointTransform.RotationInterp = currentFrame.BoneProperties.Rotation;
|
||||
jointTransform.ScaleInterp = currentFrame.BoneProperties.Scale;
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
}
|
||||
} else { // 0 keyframes for the current bone
|
||||
|
||||
}
|
||||
boneMatrices[bone->ID] = boneMatrix; // * bone->OffsetMatrix;
|
||||
}
|
||||
|
||||
for (auto &child : bone->Children) {
|
||||
std::string name = child->Name;
|
||||
AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, progress, boneMatrices, child, boneMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 offset = GetOffsetTransform(bone, animationOffset);
|
||||
|
||||
if (JointTransforms.size() == 0) {
|
||||
if (bone->Parent) {
|
||||
if (offset != glm::mat4(1)) {
|
||||
boneMatrix = parentMatrix * offset;// *((glm::inverse(bone->OffsetMatrix) * bone->Parent->OffsetMatrix));
|
||||
} else {
|
||||
boneMatrix = parentMatrix *((glm::inverse(bone->OffsetMatrix) * bone->Parent->OffsetMatrix));
|
||||
|
||||
}
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
} else {
|
||||
boneMatrix = offset * glm::inverse(bone->OffsetMatrix);
|
||||
boneMatrices[bone->ID] = parentMatrix;
|
||||
}
|
||||
} else {
|
||||
|
||||
glm::vec3 finalPosInterp;
|
||||
glm::quat finalRotInterp;
|
||||
glm::vec3 finalScaleInterp;
|
||||
float totalWeight = 0;
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
totalWeight += jointTransform.Weight;
|
||||
}
|
||||
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
if (jointTransform.Weight == 1.0f) {
|
||||
finalPosInterp = jointTransform.PositionInterp;
|
||||
finalRotInterp = jointTransform.RotationInterp;
|
||||
finalScaleInterp = jointTransform.ScaleInterp;
|
||||
break;
|
||||
} else {
|
||||
finalPosInterp += jointTransform.PositionInterp * (jointTransform.Weight/totalWeight);
|
||||
finalRotInterp *= glm::slerp(glm::quat(), jointTransform.RotationInterp, (jointTransform.Weight/totalWeight));
|
||||
finalScaleInterp += jointTransform.ScaleInterp * (jointTransform.Weight/totalWeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (offset != glm::mat4(1)) {
|
||||
boneMatrix = parentMatrix * ((glm::translate(finalPosInterp) * glm::toMat4(finalRotInterp) * glm::scale(finalScaleInterp)) + offset);
|
||||
} else {
|
||||
boneMatrix = parentMatrix * (glm::translate(finalPosInterp) * glm::toMat4(finalRotInterp) * glm::scale(finalScaleInterp));
|
||||
}
|
||||
|
||||
boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix;
|
||||
}
|
||||
|
||||
for (auto &child : bone->Children) {
|
||||
AccumulateBoneTransforms(noRootMotion, animations, animationOffset, boneMatrices, child, boneMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 Skeleton::GetOffsetTransform(const Bone* bone, AnimationOffset animationOffset)
|
||||
{
|
||||
const Animation* animation = animationOffset.animation;
|
||||
float time = animationOffset.time;
|
||||
|
||||
glm::vec3 position = glm::vec3(0);
|
||||
glm::quat rotation = glm::quat();
|
||||
glm::vec3 scale = glm::vec3(1);
|
||||
|
||||
if (animation->JointAnimations.find(bone->ID) != animation->JointAnimations.end()) {
|
||||
std::vector<Animation::Keyframe> boneKeyFrames = animation->JointAnimations.at(bone->ID);
|
||||
|
||||
Animation::Keyframe currentFrame;
|
||||
Animation::Keyframe nextFrame;
|
||||
|
||||
if (boneKeyFrames.size() > 1) { // 2+ keyframes for the current bone
|
||||
for (int index = boneKeyFrames.size()-1; index >= 0; index--) { // find the bone keyframes that surrounds the current frame
|
||||
if (time >= boneKeyFrames.at(index).Time) {
|
||||
currentFrame = boneKeyFrames.at(index);
|
||||
nextFrame = boneKeyFrames.at((index + 1) % boneKeyFrames.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float progress;
|
||||
|
||||
if (nextFrame.Index == 0) {
|
||||
nextFrame = currentFrame;
|
||||
progress = (time - currentFrame.Time) / (animation->Duration - currentFrame.Time);
|
||||
} else {
|
||||
progress = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
|
||||
}
|
||||
|
||||
progress = glm::clamp(progress, 0.0f, 1.0f);
|
||||
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties;
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties;
|
||||
|
||||
position = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
rotation = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
scale = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
|
||||
} else { // 1 keyframes for the current bone
|
||||
currentFrame = boneKeyFrames.at(0);
|
||||
position = currentFrame.BoneProperties.Position;
|
||||
rotation = currentFrame.BoneProperties.Rotation;
|
||||
scale = currentFrame.BoneProperties.Scale;
|
||||
}
|
||||
}
|
||||
|
||||
return (glm::translate(position) * glm::toMat4(rotation) * glm::scale(scale));
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 Skeleton::GetBoneTransform(const Bone* bone, const Animation* animation, float time, glm::mat4 childMatrix)
|
||||
{
|
||||
glm::mat4 boneMatrix;
|
||||
|
||||
Animation::Keyframe currentFrame;
|
||||
Animation::Keyframe nextFrame;
|
||||
|
||||
if (animation->JointAnimations.find(bone->ID) != animation->JointAnimations.end()) {
|
||||
std::vector<Animation::Keyframe> boneKeyFrames = animation->JointAnimations.at(bone->ID);
|
||||
|
||||
if (boneKeyFrames.size() > 1) { // 2+ keyframes for the current bone
|
||||
for (int index = boneKeyFrames.size()-1; index >= 0; index--) { // find the bone keyframes that surrounds the current frame
|
||||
if (time >= boneKeyFrames.at(index).Time) {
|
||||
currentFrame = boneKeyFrames.at(index);
|
||||
nextFrame = boneKeyFrames.at((index + 1) % boneKeyFrames.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float progress;
|
||||
|
||||
if (nextFrame.Index == 0) {
|
||||
nextFrame = currentFrame;
|
||||
progress = (time - currentFrame.Time) / (animation->Duration - currentFrame.Time);
|
||||
} else {
|
||||
progress = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
}
|
||||
|
||||
if (progress > 1.0f || progress < 0.0f) {
|
||||
LOG_INFO("Progress %f", progress);
|
||||
progress = glm::clamp(progress, 0.0f, 1.0f);
|
||||
}
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties;
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties;
|
||||
|
||||
glm::vec3 positionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
glm::quat rotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
glm::vec3 scaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
boneMatrix = (glm::translate(positionInterp) * glm::toMat4(rotationInterp) * glm::scale(scaleInterp)) * childMatrix;
|
||||
|
||||
} else { // 1 keyframes for the current bone
|
||||
currentFrame = boneKeyFrames.at(0);
|
||||
boneMatrix = (glm::translate(currentFrame.BoneProperties.Position) * glm::toMat4(currentFrame.BoneProperties.Rotation) * glm::scale(currentFrame.BoneProperties.Scale)) * childMatrix;
|
||||
|
||||
}
|
||||
} else { // 0 keyframes for the current bone
|
||||
if (bone->Parent) {
|
||||
boneMatrix = bone->Parent->OffsetMatrix * glm::inverse(bone->OffsetMatrix) * childMatrix;
|
||||
} else {
|
||||
boneMatrix = glm::inverse(bone->OffsetMatrix) * childMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
if (bone->Parent) {
|
||||
return GetBoneTransform(bone->Parent, animation, time, boneMatrix);
|
||||
} else {
|
||||
return boneMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 Skeleton::GetBoneTransform(bool noRootMotion, const Bone* bone, std::vector<AnimationData> animations, AnimationOffset animationOffset, glm::mat4 childMatrix)
|
||||
{
|
||||
glm::mat4 boneMatrix;
|
||||
|
||||
std::vector<JointFrameTransform> JointTransforms;
|
||||
|
||||
for (const AnimationData animationData : animations) {
|
||||
const Animation* animation = animationData.animation;
|
||||
const float time = animationData.time;
|
||||
|
||||
JointFrameTransform jointTransform;
|
||||
jointTransform.Weight = animationData.weight;;
|
||||
|
||||
if (animation->JointAnimations.find(bone->ID) != animation->JointAnimations.end()) {
|
||||
std::vector<Animation::Keyframe> boneKeyFrames = animation->JointAnimations.at(bone->ID);
|
||||
|
||||
Animation::Keyframe currentFrame;
|
||||
Animation::Keyframe nextFrame;
|
||||
|
||||
if (boneKeyFrames.size() > 1) { // 2+ keyframes for the current bone
|
||||
for (int index = boneKeyFrames.size()-1; index >= 0; index--) { // find the bone keyframes that surrounds the current frame
|
||||
if (time >= boneKeyFrames.at(index).Time) {
|
||||
currentFrame = boneKeyFrames.at(index);
|
||||
nextFrame = boneKeyFrames.at((index + 1) % boneKeyFrames.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float progress;
|
||||
|
||||
if (nextFrame.Index == 0) {
|
||||
nextFrame = currentFrame;
|
||||
progress = (time - currentFrame.Time) / (animation->Duration - currentFrame.Time);
|
||||
} else {
|
||||
progress = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (progress > 1.0f || progress < 0.0f) {
|
||||
progress = glm::clamp(progress, 0.0f, 1.0f);
|
||||
}
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties;
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties;
|
||||
|
||||
jointTransform.PositionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
jointTransform.RotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
jointTransform.ScaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
// Flag for no root motion
|
||||
if (bone == RootBone && noRootMotion) {
|
||||
jointTransform.PositionInterp.x = 0;
|
||||
jointTransform.PositionInterp.z = 0;
|
||||
}
|
||||
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
} else { // 1 keyframes for the current bone
|
||||
currentFrame = boneKeyFrames.at(0);
|
||||
jointTransform.PositionInterp = currentFrame.BoneProperties.Position;
|
||||
jointTransform.RotationInterp = currentFrame.BoneProperties.Rotation;
|
||||
jointTransform.ScaleInterp = currentFrame.BoneProperties.Scale;
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
}
|
||||
} else { // 0 keyframes for the current bone
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 offset = GetOffsetTransform(bone, animationOffset);
|
||||
|
||||
if (JointTransforms.size() == 0) {
|
||||
if (bone->Parent) {
|
||||
if (offset != glm::mat4(1)) {
|
||||
boneMatrix = offset * childMatrix;// *((glm::inverse(bone->OffsetMatrix) * bone->Parent->OffsetMatrix));
|
||||
} else {
|
||||
boneMatrix = ((glm::inverse(bone->OffsetMatrix) * bone->Parent->OffsetMatrix)) * childMatrix;
|
||||
}
|
||||
} else {
|
||||
boneMatrix = offset * glm::inverse(bone->OffsetMatrix);
|
||||
}
|
||||
} else {
|
||||
|
||||
glm::vec3 finalPosInterp;
|
||||
glm::quat finalRotInterp;
|
||||
glm::vec3 finalScaleInterp;
|
||||
float totalWeight = 0;
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
totalWeight += jointTransform.Weight;
|
||||
}
|
||||
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
if (jointTransform.Weight == 1.0f) {
|
||||
finalPosInterp = jointTransform.PositionInterp;
|
||||
finalRotInterp = jointTransform.RotationInterp;
|
||||
finalScaleInterp = jointTransform.ScaleInterp;
|
||||
break;
|
||||
} else {
|
||||
finalPosInterp += jointTransform.PositionInterp * (jointTransform.Weight/totalWeight);
|
||||
finalRotInterp *= glm::slerp(glm::quat(), jointTransform.RotationInterp, (jointTransform.Weight/totalWeight));
|
||||
finalScaleInterp += jointTransform.ScaleInterp * (jointTransform.Weight/totalWeight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (offset != glm::mat4(1)) {
|
||||
boneMatrix = ((glm::translate(finalPosInterp) * glm::toMat4(finalRotInterp) * glm::scale(finalScaleInterp)) + offset) * childMatrix;
|
||||
} else {
|
||||
boneMatrix = (glm::translate(finalPosInterp) * glm::toMat4(finalRotInterp) * glm::scale(finalScaleInterp)) * childMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
if (bone->Parent != nullptr) {
|
||||
return GetBoneTransform(noRootMotion, bone->Parent, animations, animationOffset, boneMatrix);
|
||||
} else {
|
||||
return boneMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 Skeleton::GetBoneTransform(bool noRootMotion, const Bone* bone, std::vector<AnimationData> animations, glm::mat4 childMatrix)
|
||||
{
|
||||
glm::mat4 boneMatrix;
|
||||
std::vector<JointFrameTransform> JointTransforms;
|
||||
|
||||
for (const AnimationData animationData : animations) {
|
||||
const Animation* animation = animationData.animation;
|
||||
const float time = animationData.time;
|
||||
|
||||
JointFrameTransform jointTransform;
|
||||
jointTransform.Weight = animationData.weight;;
|
||||
|
||||
if (animation->JointAnimations.find(bone->ID) != animation->JointAnimations.end()) {
|
||||
std::vector<Animation::Keyframe> boneKeyFrames = animation->JointAnimations.at(bone->ID);
|
||||
|
||||
Animation::Keyframe currentFrame;
|
||||
Animation::Keyframe nextFrame;
|
||||
|
||||
if (boneKeyFrames.size() > 1) { // 2+ keyframes for the current bone
|
||||
for (int index = boneKeyFrames.size()-1; index >= 0; index--) { // find the bone keyframes that surrounds the current frame
|
||||
if (time >= boneKeyFrames.at(index).Time) {
|
||||
currentFrame = boneKeyFrames.at(index);
|
||||
nextFrame = boneKeyFrames.at((index + 1) % boneKeyFrames.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float progress;
|
||||
|
||||
if (nextFrame.Index == 0) {
|
||||
nextFrame = currentFrame;
|
||||
progress = (time - currentFrame.Time) / (animation->Duration - currentFrame.Time);
|
||||
} else {
|
||||
progress = (time - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);
|
||||
}
|
||||
|
||||
|
||||
if (progress > 1.0f || progress < 0.0f) {
|
||||
progress = glm::clamp(progress, 0.0f, 1.0f);
|
||||
}
|
||||
Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties;
|
||||
Animation::Keyframe::BoneProperty nextBoneProperty = nextFrame.BoneProperties;
|
||||
|
||||
jointTransform.PositionInterp = currentBoneProperty.Position * (1.f - progress) + nextBoneProperty.Position * progress;
|
||||
jointTransform.RotationInterp = glm::slerp(currentBoneProperty.Rotation, nextBoneProperty.Rotation, progress);
|
||||
jointTransform.ScaleInterp = currentBoneProperty.Scale * (1.f - progress) + nextBoneProperty.Scale * progress;
|
||||
|
||||
// Flag for no root motion
|
||||
if (bone == RootBone && noRootMotion) {
|
||||
jointTransform.PositionInterp.x = 0;
|
||||
jointTransform.PositionInterp.z = 0;
|
||||
}
|
||||
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
} else { // 1 keyframes for the current bone
|
||||
currentFrame = boneKeyFrames.at(0);
|
||||
jointTransform.PositionInterp = currentFrame.BoneProperties.Position;
|
||||
jointTransform.RotationInterp = currentFrame.BoneProperties.Rotation;
|
||||
jointTransform.ScaleInterp = currentFrame.BoneProperties.Scale;
|
||||
JointTransforms.push_back(jointTransform);
|
||||
|
||||
}
|
||||
} else { // 0 keyframes for the current bone
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (JointTransforms.size() <= 0) {
|
||||
if (bone->Parent) {
|
||||
boneMatrix = glm::inverse(bone->OffsetMatrix) * bone->Parent->OffsetMatrix * childMatrix;
|
||||
} else {
|
||||
boneMatrix = glm::inverse(bone->OffsetMatrix) * childMatrix;
|
||||
}
|
||||
} else if (JointTransforms.size() == 1) {
|
||||
boneMatrix = (glm::translate(JointTransforms.at(0).PositionInterp) * glm::toMat4(JointTransforms.at(0).RotationInterp) * glm::scale(JointTransforms.at(0).ScaleInterp)) * childMatrix;
|
||||
} else {
|
||||
|
||||
glm::vec3 finalPosInterp;
|
||||
glm::quat finalRotInterp;
|
||||
glm::vec3 finalScaleInterp;
|
||||
float totalWeight = 0;
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
totalWeight += jointTransform.Weight;
|
||||
}
|
||||
|
||||
|
||||
for (JointFrameTransform jointTransform : JointTransforms) {
|
||||
if (jointTransform.Weight == 1.0f) {
|
||||
finalPosInterp = jointTransform.PositionInterp;
|
||||
finalRotInterp = jointTransform.RotationInterp;
|
||||
finalScaleInterp = jointTransform.ScaleInterp;
|
||||
break;
|
||||
} else {
|
||||
finalPosInterp += jointTransform.PositionInterp * (jointTransform.Weight/totalWeight);
|
||||
finalRotInterp *= glm::slerp(glm::quat(), jointTransform.RotationInterp, (jointTransform.Weight/totalWeight));
|
||||
finalScaleInterp += jointTransform.ScaleInterp * (jointTransform.Weight/totalWeight);
|
||||
}
|
||||
}
|
||||
|
||||
boneMatrix = (glm::translate(finalPosInterp) * glm::toMat4(finalRotInterp) * glm::scale(finalScaleInterp)) * childMatrix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (bone->Parent != nullptr) {
|
||||
return GetBoneTransform(noRootMotion, bone->Parent, animations, boneMatrix);
|
||||
} else {
|
||||
return boneMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
int Skeleton::GetBoneID(std::string name)
|
||||
@@ -134,11 +719,13 @@ void Skeleton::PrintSkeleton(const Bone* bone, int depthCount)
|
||||
|
||||
int Skeleton::GetKeyframe(const Animation& animation, double time)
|
||||
{
|
||||
|
||||
/*
|
||||
if (time < 0) {
|
||||
time = 0;
|
||||
}
|
||||
if (time >= animation.Duration) {
|
||||
return animation.Keyframes.size() - 1;
|
||||
return animation..size() - 1;
|
||||
}
|
||||
|
||||
for (int keyframe = 0; keyframe < animation.Keyframes.size(); ++keyframe) {
|
||||
@@ -146,6 +733,8 @@ int Skeleton::GetKeyframe(const Animation& animation, double time)
|
||||
return (keyframe - 1) % animation.Keyframes.size();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ void TextPass::Draw(RenderScene& scene, FrameBuffer& frameBuffer)
|
||||
{
|
||||
GLERROR("Derp1");
|
||||
TextPassState* state = new TextPassState(frameBuffer.GetHandle());
|
||||
for (auto &job : scene.TextJobs) {
|
||||
for (auto &job : scene.Jobs.Text) {
|
||||
auto textJob = std::dynamic_pointer_cast<TextJob>(job);
|
||||
if (textJob) {
|
||||
|
||||
|
||||
@@ -2,21 +2,26 @@
|
||||
|
||||
Texture::Texture(std::string path)
|
||||
{
|
||||
PNG image(path);
|
||||
PNG* img = ResourceManager::Load<PNG, true>(path); //TODO: Make this threaded. Catch exeptions in all other load places.
|
||||
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
image = PNG("Textures/Core/ErrorTexture.png");
|
||||
if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
|
||||
LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
//PNG image(path);
|
||||
|
||||
this->Width = image.Width;
|
||||
this->Height = image.Height;
|
||||
//if (img->Width == 0 && img->Height == 0 || img->Format == Image::ImageFormat::Unknown) {
|
||||
// //image = PNG("Textures/Core/ErrorTexture.png");
|
||||
// //return; // Temporary fix to remove crash
|
||||
|
||||
// if (img->Width == 0 && img->Height == 0 || img->Format == Image::ImageFormat::Unknown) {
|
||||
// LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
|
||||
this->Width = img->Width;
|
||||
this->Height = img->Height;
|
||||
this->Data = img->Data;
|
||||
|
||||
GLint format;
|
||||
switch (image.Format) {
|
||||
switch (img->Format) {
|
||||
case Image::ImageFormat::RGB:
|
||||
format = GL_RGB;
|
||||
break;
|
||||
@@ -24,15 +29,17 @@ Texture::Texture(std::string path)
|
||||
format = GL_RGBA;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Construct the OpenGL texture
|
||||
glGenTextures(1, &m_Texture);
|
||||
glBindTexture(GL_TEXTURE_2D, m_Texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, img->Width, img->Height, 0, format, GL_UNSIGNED_BYTE, img->Data);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
GLERROR("Texture load");
|
||||
}
|
||||
|
||||
@@ -1,2 +1,19 @@
|
||||
#include "Rendering/Util/CommonFunctions.h"
|
||||
|
||||
Texture* CommonFunctions::LoadTexture(std::string path, bool threaded)
|
||||
{
|
||||
Texture* img;
|
||||
try {
|
||||
if(threaded) {
|
||||
img = ResourceManager::Load<Texture, true>(path);
|
||||
} else {
|
||||
img = ResourceManager::Load<Texture, false>(path);
|
||||
}
|
||||
} catch (const Resource::StillLoadingException&) {
|
||||
img = ResourceManager::Load<Texture>("Textures/Core/ErrorTexture.png");
|
||||
} catch (const std::exception&) {
|
||||
img = nullptr;
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
#include "Sound/SoundManager.h"
|
||||
|
||||
SoundManager::SoundManager(World* world, EventBroker* eventBroker)
|
||||
{
|
||||
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
m_BGMVolumeChannel = config->Get<float>("Sound.BGMVolume", 1.f);
|
||||
m_SFXVolumeChannel = config->Get<float>("Sound.SFXVolume", 1.f);
|
||||
|
||||
initOpenAL();
|
||||
alSpeedOfSound(340.29f);
|
||||
alDistanceModel(AL_LINEAR_DISTANCE);
|
||||
alDopplerFactor(1);
|
||||
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnEntity, &SoundManager::OnPlaySoundOnEntity);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnPosition, &SoundManager::OnPlaySoundOnPosition);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayBackgroundMusic, &SoundManager::OnPlayBackgroundMusic);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EStopSound, &SoundManager::OnStopSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPauseSound, &SoundManager::OnPauseSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EContinueSound, &SoundManager::OnContinueSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetBGMGain, &SoundManager::OnSetBGMGain);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetSFXGain, &SoundManager::OnSetSFXGain);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPause, &SoundManager::OnPause);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EResume, &SoundManager::OnResume);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EComponentAttached, &SoundManager::OnComponentAttached);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &SoundManager::OnPlayerSpawned);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayQueueOnEntity, &SoundManager::OnPlayQueueOnEntity);
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
stopEmitters(); // Stopps emitters
|
||||
deleteInactiveEmitters(); // Deletes stopped emitters
|
||||
// Delete entities
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
m_World->DeleteEntity((*it).first);
|
||||
}
|
||||
m_Sources.clear();
|
||||
|
||||
alcDestroyContext(m_ALCcontext);
|
||||
alcCloseDevice(m_ALCdevice);
|
||||
}
|
||||
|
||||
void SoundManager::stopEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
if (getSourceState(it->second->ALsource) == AL_PLAYING) {
|
||||
stopSound(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::Update(double dt)
|
||||
{
|
||||
m_EventBroker->Process<SoundManager>();
|
||||
deleteInactiveEmitters(); // can be optimized with "EEntityDeleted"
|
||||
updateEmitters(dt);
|
||||
updateListener(dt);
|
||||
|
||||
// Editor debug info
|
||||
ImGui::SliderFloat("BGM", &m_BGMVolumeChannel, 0.0f, 1.0f, "%.3f", 1.0f);
|
||||
ImGui::SliderFloat("SFX", &m_SFXVolumeChannel, 0.0f, 1.0f, "%.3f", 1.0f);
|
||||
}
|
||||
|
||||
void SoundManager::deleteInactiveEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end();) {
|
||||
if (m_World->ValidEntity(it->first)
|
||||
&& m_World->HasComponent(it->first, "SoundEmitter")) {
|
||||
if (getSourceState(it->second->ALsource) != AL_STOPPED) {
|
||||
// Nothing to see here, move along
|
||||
it++;
|
||||
continue;
|
||||
} else {
|
||||
// Sound has been stopped / finished playing.
|
||||
alDeleteBuffers(1, &it->second->ALsource);
|
||||
alDeleteSources(1, &it->second->ALsource);
|
||||
m_World->DeleteEntity(it->first);
|
||||
delete it->second;
|
||||
it = m_Sources.erase(it);
|
||||
}
|
||||
} else {
|
||||
// Entity / Component has been removed
|
||||
stopSound(it->second);
|
||||
alDeleteBuffers(1, &it->second->ALsource);
|
||||
alDeleteSources(1, &it->second->ALsource);
|
||||
delete it->second;
|
||||
it = m_Sources.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::updateEmitters(double dt)
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
// Get previous pos
|
||||
if (!m_World->ValidEntity(it->first)) {
|
||||
return;
|
||||
}
|
||||
if (!m_World->HasComponent(it->first, "SoundEmitter"))
|
||||
return;
|
||||
|
||||
glm::vec3 previousPos;
|
||||
alGetSource3f(it->second->ALsource, AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z);
|
||||
// Get next pos
|
||||
if (!m_World->HasComponent(it->first, "Transform"))
|
||||
return;
|
||||
if (!m_World->ValidEntity(m_World->GetParent(it->first))) {
|
||||
return;
|
||||
}
|
||||
glm::vec3 nextPos = Transform::AbsolutePosition(m_World, it->first);
|
||||
// Calculate velocity
|
||||
glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt;
|
||||
setSourcePos(it->second->ALsource, nextPos);
|
||||
setSourceVel(it->second->ALsource, velocity);
|
||||
|
||||
auto emitter = m_World->GetComponent(it->first, "SoundEmitter");
|
||||
setSoundProperties(it->second, &emitter);
|
||||
|
||||
// Path changed
|
||||
if (it->second->SoundResource->Path() != (std::string)emitter["FilePath"]) {
|
||||
it->second->SoundResource = ResourceManager::Load<Sound>((std::string)emitter["FilePath"]);
|
||||
if (it->second->SoundResource->Buffer() != 0) {
|
||||
playSound(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::updateListener(double dt)
|
||||
{
|
||||
// Should only be one listener.
|
||||
auto listenerComponents = m_World->GetComponents("Listener");
|
||||
if (listenerComponents == nullptr || !m_LocalPlayer.Valid()) {
|
||||
return;
|
||||
}
|
||||
for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) {
|
||||
EntityWrapper listener(m_World, (*it).EntityID);
|
||||
if (!listener.Valid()) {
|
||||
break;
|
||||
}
|
||||
if (listener.IsChildOf(m_LocalPlayer) || listener == m_LocalPlayer) {
|
||||
glm::vec3 previousPos;
|
||||
alGetListener3f(AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos
|
||||
glm::vec3 nextPos = Transform::AbsolutePosition(listener); // Get next (current) pos
|
||||
glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; // Calculate velocity
|
||||
setListenerPos(nextPos);
|
||||
setListenerVel(velocity);
|
||||
setListenerOri(glm::eulerAngles(Transform::AbsoluteOrientation(listener)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Source* SoundManager::createSource(std::string filePath)
|
||||
{
|
||||
ALuint alSource;
|
||||
alGenSources((ALuint)1, &alSource);
|
||||
alSourcef(alSource, AL_REFERENCE_DISTANCE, 1.0);
|
||||
alSourcef(alSource, AL_MAX_DISTANCE, FLT_MAX);
|
||||
Source* source = new Source();
|
||||
source->ALsource = alSource;
|
||||
source->SoundResource = ResourceManager::Load<Sound>(filePath);
|
||||
return source;
|
||||
}
|
||||
|
||||
void SoundManager::playSound(Source* source)
|
||||
{
|
||||
alSourcei(source->ALsource, AL_BUFFER, source->SoundResource->Buffer());
|
||||
alSourcePlay(source->ALsource);
|
||||
}
|
||||
|
||||
void SoundManager::playQueue(QueuedBuffers qb)
|
||||
{
|
||||
for (int i = 0; i < qb.second.size(); i++) {
|
||||
alSourceQueueBuffers(qb.first, 1, &qb.second[i]);
|
||||
}
|
||||
alSourcePlay(qb.first);
|
||||
}
|
||||
|
||||
void SoundManager::stopSound(Source* source)
|
||||
{
|
||||
alSourceStop(source->ALsource);
|
||||
}
|
||||
|
||||
bool SoundManager::OnPlaySoundOnEntity(const Events::PlaySoundOnEntity & e)
|
||||
{
|
||||
Source* source = createSource(e.FilePath);
|
||||
source->Type = SoundType::SFX;
|
||||
EntityID child = m_World->CreateEntity(e.EmitterID);
|
||||
m_World->AttachComponent(child, "Transform");
|
||||
m_World->AttachComponent(child, "SoundEmitter");
|
||||
m_Sources[child] = source;
|
||||
playSound(source);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundManager::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e)
|
||||
{
|
||||
Source* source = createSource(e.FilePath);
|
||||
auto emitterID = m_World->CreateEntity();
|
||||
auto transform = m_World->AttachComponent(emitterID, "Transform");
|
||||
(glm::vec3&)transform["Position"] = e.Position;
|
||||
auto emitter = m_World->AttachComponent(emitterID, "SoundEmitter");
|
||||
(float&)(double)emitter["Gain"] = e.Gain;
|
||||
(float&)(double)emitter["Pitch"] = e.Pitch;
|
||||
(bool&)emitter["Loop"] = e.Loop;
|
||||
(float&)(double)emitter["MaxDistance"] = e.MaxDistance;
|
||||
(float&)(double)emitter["RollOffFactor"] = e.RollOffFactor;
|
||||
(float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance;
|
||||
source->Type = SoundType::SFX;
|
||||
m_Sources[emitterID] = source;
|
||||
playSound(source);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnPauseSound(const Events::PauseSound & e)
|
||||
{
|
||||
alSourcePause(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnStopSound(const Events::StopSound & e)
|
||||
{
|
||||
alSourceStop(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnContinueSound(const Events::ContinueSound & e)
|
||||
{
|
||||
alSourcePlay(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnPlayBackgroundMusic(const Events::PlayBackgroundMusic & e)
|
||||
{
|
||||
auto listenerComponents = m_World->GetComponents("Listener");
|
||||
for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) {
|
||||
if ((*it).EntityID != m_LocalPlayer.ID) {
|
||||
break;
|
||||
}
|
||||
auto emitterChild = m_World->CreateEntity((*it).EntityID);
|
||||
auto emitter = m_World->AttachComponent(emitterChild, "SoundEmitter");
|
||||
(bool&)emitter["Loop"] = true;
|
||||
(std::string&)emitter["FilePath"] = e.FilePath;
|
||||
m_World->AttachComponent(emitterChild, "Transform");
|
||||
Source* source = createSource(e.FilePath);
|
||||
source->Type = SoundType::BGM;
|
||||
setSoundProperties(source, &emitter);
|
||||
m_Sources[emitterChild] = source;
|
||||
playSound(source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnSetBGMGain(const Events::SetBGMGain & e)
|
||||
{
|
||||
m_BGMVolumeChannel = e.Gain;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnSetSFXGain(const Events::SetSFXGain & e)
|
||||
{
|
||||
m_SFXVolumeChannel = e.Gain;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundManager::OnComponentAttached(const Events::ComponentAttached & e)
|
||||
{
|
||||
if (e.Component.Info.Name == "SoundEmitter") {
|
||||
auto component = m_World->GetComponent(e.Entity.ID, "SoundEmitter");
|
||||
Source* source = createSource(component["FilePath"]);
|
||||
m_Sources[e.Entity.ID] = source;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundManager::OnPause(const Events::Pause & e)
|
||||
{
|
||||
for (auto it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
alSourcePause(it->second->ALsource);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundManager::OnResume(const Events::Resume &e)
|
||||
{
|
||||
for (auto it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
alSourcePlay(it->second->ALsource);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SoundManager::OnPlayerSpawned(const Events::PlayerSpawned &e)
|
||||
{
|
||||
if (e.PlayerID == -1) { // Local player
|
||||
m_LocalPlayer = e.Player;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SoundManager::OnPlayQueueOnEntity(const Events::PlayQueueOnEntity &e)
|
||||
{
|
||||
Source* source = createSource(*e.FilePaths.begin());
|
||||
std::vector<ALuint> buffers;
|
||||
buffers.push_back(source->SoundResource->Buffer());
|
||||
source->Type = SoundType::BGM;
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for (it = e.FilePaths.begin() + 1; it != e.FilePaths.end(); it++) {
|
||||
buffers.push_back(ResourceManager::Load<Sound>(*it)->Buffer());
|
||||
}
|
||||
playQueue(QueuedBuffers(source->ALsource, buffers));
|
||||
return true;
|
||||
}
|
||||
|
||||
ALenum SoundManager::getSourceState(ALuint source)
|
||||
{
|
||||
ALenum state;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
void SoundManager::setGain(Source * source, float gain)
|
||||
{
|
||||
alSourcef(source->ALsource, AL_GAIN, gain);
|
||||
}
|
||||
|
||||
void SoundManager::setSoundProperties(Source* source, ComponentWrapper* soundComponent)
|
||||
{
|
||||
float gain = (source->Type == SoundType::SFX) ? m_SFXVolumeChannel : m_BGMVolumeChannel;
|
||||
alSourcef(source->ALsource, AL_GAIN, (float)(double)(*soundComponent)["Gain"] * gain);
|
||||
alSourcef(source->ALsource, AL_PITCH, (float)(double)(*soundComponent)["Pitch"]);
|
||||
alSourcei(source->ALsource, AL_LOOPING, (int)(bool)(*soundComponent)["Loop"]); // YOLO
|
||||
alSourcef(source->ALsource, AL_MAX_DISTANCE, (float)(double)(*soundComponent)["MaxDistance"]);
|
||||
alSourcef(source->ALsource, AL_ROLLOFF_FACTOR, (float)(double)(*soundComponent)["RollOffFactor"]);
|
||||
alSourcef(source->ALsource, AL_REFERENCE_DISTANCE, (float)(double)(*soundComponent)["ReferenceDistance"]);
|
||||
}
|
||||
|
||||
void SoundManager::initOpenAL()
|
||||
{
|
||||
// Initialize OpenAL
|
||||
m_ALCdevice = alcOpenDevice(nullptr);
|
||||
if (m_ALCdevice != nullptr) {
|
||||
m_ALCcontext = alcCreateContext(m_ALCdevice, nullptr);
|
||||
alcMakeContextCurrent(m_ALCcontext);
|
||||
} else {
|
||||
LOG_ERROR("OpenAL failed to initialize.");
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::setListenerOri(glm::vec3 ori)
|
||||
{
|
||||
// Calculate forward and up vector.
|
||||
glm::vec3 forward = glm::vec3(0.0, 0.0, -1.0);
|
||||
forward = glm::rotateX(forward, ori.x);
|
||||
forward = glm::rotateY(forward, ori.y);
|
||||
forward = glm::rotateZ(forward, ori.z);
|
||||
glm::normalize(forward);
|
||||
glm::vec3 up = glm::vec3(0.0, 1.0, 0.0);
|
||||
up = glm::rotateX(up, ori.x);
|
||||
up = glm::rotateY(up, ori.y);
|
||||
up = glm::rotateZ(up, ori.z);
|
||||
glm::normalize(up);
|
||||
ALfloat lOri[6] = { forward.x, forward.y, forward.z, up.x, up.y, up.z };
|
||||
alListenerfv(AL_ORIENTATION, lOri);
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
#include "Sound/SoundSystem.h"
|
||||
|
||||
SoundSystem::SoundSystem(World* world, EventBroker* eventBroker, bool editorMode)
|
||||
{
|
||||
m_EventBroker = eventBroker;
|
||||
m_World = world;
|
||||
m_EditorEnabled = editorMode;
|
||||
|
||||
initOpenAL();
|
||||
|
||||
alSpeedOfSound(340.29f);
|
||||
alDistanceModel(AL_LINEAR_DISTANCE);
|
||||
alDopplerFactor(1);
|
||||
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnEntity, &SoundSystem::OnPlaySoundOnEntity);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlaySoundOnPosition, &SoundSystem::OnPlaySoundOnPosition);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPlayBackgroundMusic, &SoundSystem::OnPlayBackgroundMusic);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EStopSound, &SoundSystem::OnStopSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EPauseSound, &SoundSystem::OnPauseSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_EContinueSound, &SoundSystem::OnContinueSound);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetBGMGain, &SoundSystem::OnSetBGMGain);
|
||||
EVENT_SUBSCRIBE_MEMBER(m_ESetSFXGain, &SoundSystem::OnSetSFXGain);
|
||||
}
|
||||
|
||||
SoundSystem::~SoundSystem()
|
||||
{
|
||||
stopEmitters(); // Stopps emitters
|
||||
deleteInactiveEmitters(); // Deletes stopped emitters
|
||||
// Delete entities
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
m_World->DeleteEntity((*it).first);
|
||||
}
|
||||
m_Sources.clear();
|
||||
|
||||
alcDestroyContext(m_ALCcontext);
|
||||
alcCloseDevice(m_ALCdevice);
|
||||
}
|
||||
|
||||
void SoundSystem::stopEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
if (getSourceState(it->second->ALsource) == AL_PLAYING) {
|
||||
stopSound(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::Update(double dt)
|
||||
{
|
||||
m_EventBroker->Process<SoundSystem>();
|
||||
addNewEmitters(dt); // can be optimized with "EEntityCreated"
|
||||
deleteInactiveEmitters(); // can be optimized with "EEntityDeleted"
|
||||
updateEmitters( dt);
|
||||
updateListener( dt);
|
||||
}
|
||||
|
||||
void SoundSystem::deleteInactiveEmitters()
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end();) {
|
||||
if (m_World->ValidEntity(it->first)
|
||||
&& m_World->HasComponent(it->first, "SoundEmitter")) {
|
||||
if (getSourceState(it->second->ALsource) != AL_STOPPED) {
|
||||
// Nothing to see here, move along
|
||||
it++;
|
||||
continue;
|
||||
} else {
|
||||
// Sound has been stopped / finished playing.
|
||||
alDeleteBuffers(1, &it->second->ALsource);
|
||||
alDeleteSources(1, &it->second->ALsource);
|
||||
m_World->DeleteEntity(it->first);
|
||||
delete it->second;
|
||||
it = m_Sources.erase(it);
|
||||
}
|
||||
} else {
|
||||
// Entity / Component has been removed
|
||||
stopSound((*it).second);
|
||||
alDeleteBuffers(1, &it->second->ALsource);
|
||||
alDeleteSources(1, &it->second->ALsource);
|
||||
delete it->second;
|
||||
it = m_Sources.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::addNewEmitters(double dt)
|
||||
{
|
||||
auto emitterComponents = m_World->GetComponents("SoundEmitter");
|
||||
if (emitterComponents == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = emitterComponents->begin(); it != emitterComponents->end(); it++) {
|
||||
EntityID emitter = (*it).EntityID;
|
||||
std::unordered_map<EntityID, Source*>::iterator source;
|
||||
source = m_Sources.find(emitter);
|
||||
if (source == m_Sources.end()) { // Did not exist, add it
|
||||
Source* source = createSource((std::string)(*it)["FilePath"]);
|
||||
m_Sources[emitter] = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::updateEmitters(double dt)
|
||||
{
|
||||
std::unordered_map<EntityID, Source*>::iterator it;
|
||||
for (it = m_Sources.begin(); it != m_Sources.end(); it++) {
|
||||
// Get previous pos
|
||||
glm::vec3 previousPos;
|
||||
alGetSource3f(it->second->ALsource, AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z);
|
||||
// Get next pos
|
||||
glm::vec3 nextPos = Transform::AbsolutePosition(m_World, it->first);
|
||||
// Calculate velocity
|
||||
glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt;
|
||||
setSourcePos(it->second->ALsource, nextPos);
|
||||
setSourceVel(it->second->ALsource, velocity);
|
||||
float gain;
|
||||
if (it->second->Type == SoundType::SFX) {
|
||||
gain = m_SFXVolumeChannel;
|
||||
} else if (it->second->Type == SoundType::BGM) {
|
||||
gain = m_BGMVolumeChannel;
|
||||
}
|
||||
auto emitter = m_World->GetComponent(it->first, "SoundEmitter");
|
||||
setSoundProperties(it->second->ALsource, &emitter);
|
||||
|
||||
// To make an emitter play when spawned in editor mode
|
||||
if (m_EditorEnabled) {
|
||||
// Path changed
|
||||
if (it->second->SoundResource->Path() != (std::string)emitter["FilePath"]) {
|
||||
it->second->SoundResource = ResourceManager::Load<Sound>((std::string)emitter["FilePath"]);
|
||||
if (it->second->SoundResource->Buffer() != 0) {
|
||||
playSound(it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundSystem::updateListener(double dt)
|
||||
{
|
||||
// Should only be one listener.
|
||||
auto listenerComponents = m_World->GetComponents("Listener");
|
||||
if (listenerComponents == nullptr) {
|
||||
return;
|
||||
}
|
||||
for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) {
|
||||
EntityID listener = (*it).EntityID;
|
||||
glm::vec3 previousPos;
|
||||
alGetListener3f(AL_POSITION, &previousPos.x, &previousPos.y, &previousPos.z); // Get previous pos
|
||||
glm::vec3 nextPos = Transform::AbsolutePosition(m_World, listener); // Get next (current) pos
|
||||
glm::vec3 velocity = glm::vec3(nextPos - previousPos) / (float)dt; // Calculate velocity
|
||||
setListenerPos(nextPos);
|
||||
setListenerVel(velocity);
|
||||
setListenerOri(glm::eulerAngles(Transform::AbsoluteOrientation(m_World, listener)));
|
||||
}
|
||||
}
|
||||
|
||||
Source* SoundSystem::createSource(std::string filePath)
|
||||
{
|
||||
ALuint alSource;
|
||||
alGenSources((ALuint)1, &alSource);
|
||||
alSourcef(alSource, AL_REFERENCE_DISTANCE, 1.0);
|
||||
alSourcef(alSource, AL_MAX_DISTANCE, FLT_MAX);
|
||||
Source* source = new Source();
|
||||
source->ALsource = alSource;
|
||||
source->SoundResource = ResourceManager::Load<Sound>(filePath);
|
||||
return source;
|
||||
}
|
||||
|
||||
void SoundSystem::playSound(Source* source)
|
||||
{
|
||||
alSourcei(source->ALsource, AL_BUFFER, source->SoundResource->Buffer());
|
||||
alSourcePlay(source->ALsource);
|
||||
}
|
||||
|
||||
void SoundSystem::stopSound(Source* source)
|
||||
{
|
||||
alSourceStop(source->ALsource);
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPlaySoundOnEntity(const Events::PlaySoundOnEntity & e)
|
||||
{
|
||||
Source* source = createSource(e.FilePath);
|
||||
source->Type = SoundType::SFX;
|
||||
m_Sources[e.EmitterID] = source;
|
||||
playSound(source);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e)
|
||||
{
|
||||
Source* source = createSource(e.FilePath);
|
||||
auto emitterID = m_World->CreateEntity();
|
||||
auto transform = m_World->AttachComponent(emitterID, "Transform");
|
||||
(glm::vec3&)transform["Position"] = e.Position;
|
||||
auto emitter = m_World->AttachComponent(emitterID, "SoundEmitter");
|
||||
(float&)(double)emitter["Gain"] = e.Gain;
|
||||
(float&)(double)emitter["Pitch"] = e.Pitch;
|
||||
(bool&)emitter["Loop"] = e.Loop;
|
||||
(float&)(double)emitter["MaxDistance"] = e.MaxDistance;
|
||||
(float&)(double)emitter["RollOffFactor"] = e.RollOffFactor;
|
||||
(float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance;
|
||||
auto model = m_World->AttachComponent(emitterID, "Model");
|
||||
(std::string&)model["Resource"] = "Models/Core/UnitCube.mesh"; // 360NoScope UnitCube
|
||||
source->Type = SoundType::SFX;
|
||||
m_Sources[emitterID] = source;
|
||||
playSound(source);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPauseSound(const Events::PauseSound & e)
|
||||
{
|
||||
alSourcePause(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnStopSound(const Events::StopSound & e)
|
||||
{
|
||||
alSourceStop(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnContinueSound(const Events::ContinueSound & e)
|
||||
{
|
||||
alSourcePlay(m_Sources[e.EmitterID]->ALsource);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnPlayBackgroundMusic(const Events::PlayBackgroundMusic & e)
|
||||
{
|
||||
auto listenerComponents = m_World->GetComponents("Listener");
|
||||
for (auto it = listenerComponents->begin(); it != listenerComponents->end(); it++) {
|
||||
auto emitterChild = m_World->CreateEntity((*it).EntityID);
|
||||
auto emitter = m_World->AttachComponent(emitterChild, "SoundEmitter");
|
||||
(bool&)emitter["Loop"] = true;
|
||||
(std::string&)emitter["FilePath"] = e.FilePath;
|
||||
m_World->AttachComponent(emitterChild, "Transform");
|
||||
Source* source = createSource(e.FilePath);
|
||||
source->Type = SoundType::BGM;
|
||||
m_Sources[emitterChild] = source;
|
||||
playSound(source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnSetBGMGain(const Events::SetBGMGain & e)
|
||||
{
|
||||
m_BGMVolumeChannel = e.Gain;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SoundSystem::OnSetSFXGain(const Events::SetSFXGain & e)
|
||||
{
|
||||
m_SFXVolumeChannel = e.Gain;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundSystem::setListenerOri(glm::vec3 ori)
|
||||
{
|
||||
// Calculate forward and up vector.
|
||||
glm::vec3 forward = glm::vec3(0.0, 0.0, -1.0);
|
||||
forward = glm::rotateX(forward, ori.x);
|
||||
forward = glm::rotateY(forward, ori.y);
|
||||
forward = glm::rotateZ(forward, ori.z);
|
||||
glm::normalize(forward);
|
||||
glm::vec3 up = glm::vec3(0.0, 1.0, 0.0);
|
||||
up = glm::rotateX(up, ori.x);
|
||||
up = glm::rotateY(up, ori.y);
|
||||
up = glm::rotateZ(up, ori.z);
|
||||
glm::normalize(up);
|
||||
ALfloat lOri[6] = { forward.x, forward.y, forward.z, up.x, up.y, up.z };
|
||||
alListenerfv(AL_ORIENTATION, lOri);
|
||||
}
|
||||
|
||||
ALenum SoundSystem::getSourceState(ALuint source)
|
||||
{
|
||||
ALenum state;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
return state;
|
||||
}
|
||||
|
||||
void SoundSystem::setGain(Source * source, float gain)
|
||||
{
|
||||
alSourcef(source->ALsource, AL_GAIN, gain);
|
||||
}
|
||||
|
||||
void SoundSystem::setSoundProperties(ALuint source, ComponentWrapper* soundComponent)
|
||||
{
|
||||
alSourcef(source, AL_GAIN, (float)(double)(*soundComponent)["Gain"]);
|
||||
alSourcef(source, AL_PITCH, (float)(double)(*soundComponent)["Pitch"]);
|
||||
alSourcei(source, AL_LOOPING, (int)(bool)(*soundComponent)["Loop"]); // YOLO
|
||||
alSourcef(source, AL_MAX_DISTANCE, (float)(double)(*soundComponent)["MaxDistance"]);
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, (float)(double)(*soundComponent)["RollOffFactor"]);
|
||||
alSourcef(source, AL_REFERENCE_DISTANCE, (float)(double)(*soundComponent)["ReferenceDistance"]);
|
||||
}
|
||||
|
||||
void SoundSystem::initOpenAL()
|
||||
{
|
||||
// Initialize OpenAL
|
||||
m_ALCdevice = alcOpenDevice(nullptr);
|
||||
if (m_ALCdevice != nullptr) {
|
||||
m_ALCcontext = alcCreateContext(m_ALCdevice, nullptr);
|
||||
alcMakeContextCurrent(m_ALCcontext);
|
||||
} else {
|
||||
LOG_ERROR("OpenAL failed to initialize.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user