From 4d5b8353529f12656f0acae8a3ffb99057188fc4 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 20 Jan 2016 16:43:09 +0100 Subject: [PATCH 01/38] WIP triangles vs. AABB. --- .../Schema/Entities/ModelCollisionTest.xml | 49 +++++++++ src/Engine/Collision/Collision.cpp | 99 ++++++++++++++++--- src/Engine/Collision/CollisionSystem.cpp | 63 ++++++------ 3 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 resources/Schema/Entities/ModelCollisionTest.xml diff --git a/resources/Schema/Entities/ModelCollisionTest.xml b/resources/Schema/Entities/ModelCollisionTest.xml new file mode 100644 index 00000000..46746264 --- /dev/null +++ b/resources/Schema/Entities/ModelCollisionTest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + ../assets/Models/Core/UnitCube.obj + + + + + + + + + + + + + + + ../assets/Models/Core/UnitCube.obj + + + + + + + + + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index b30ae927..c351d9fa 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -199,30 +199,93 @@ bool RayVsModel(const Ray& ray, return hit; } -bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) +bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, glm::vec3& outResolutionVector) { - bool hit = false; - const glm::vec3& origin = box.Origin(); const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); + const glm::vec3& half = box.HalfSize(); - outResolutionVector.x = INFINITY; + const glm::vec3 triPos[] = { + v0, v1, v2 + }; - for (int i = 0; i < modelIndices.size(); ++i) { - glm::vec3 p = modelVertices[i].Position; + for (int ax = 0; ax < 3; ++ax) { + auto outsideMinPlane = glm::tvec3(false); + auto outsideMaxPlane = glm::tvec3(false); + for (int pos = 0; pos < 3; ++pos) { + outsideMinPlane[pos] = (min[ax] > triPos[pos][ax]); + outsideMaxPlane[pos] = (triPos[pos][ax] > max[ax]); + } + if (glm::all(outsideMinPlane) || glm::all(outsideMaxPlane)) { + return false; + } + } + return true; +} + +bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) +{ + bool hit = false; + for (int i = 0; i < modelIndices.size(); i += 3) { + glm::vec3 resVec; + hit = AABBvsTriangle( + box, + modelVertices[modelIndices[i]].Position, + modelVertices[modelIndices[i + 1]].Position, + modelVertices[modelIndices[i + 2]].Position, + resVec + ); + if (hit) { + break; + } + } + return hit; + //TODO: Remove old code. + + /* + auto hit = glm::tvec3(false); + const glm::vec3& origin = box.Origin(); + const glm::vec3& min = box.MinCorner(); + const glm::vec3& max = box.MaxCorner(); + const glm::vec3& half = box.HalfSize(); + + float minResolution = INFINITY; + outResolutionVector = glm::vec3(0.f); + //TODO: Don't need indices? Remove? + for (const auto& v : modelVertices) { + glm::vec3 p = v.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; + const glm::vec3 diffO = origin - p; + const glm::vec3 distO = glm::abs(diffO); + for (int axis = 0; axis < 3; ++axis) { + float penetration = half[axis] - distO[axis]; + if (penetration > 0){ + if (penetration < minResolution) { + minResolution = penetration; + outResolutionVector = glm::vec3(0.f); + if (p[axis] > origin[axis]) { + outResolutionVector[axis] = -penetration; + } else { + outResolutionVector[axis] = penetration; + } + } + hit[axis] = true; } - hit = true; } + + //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]) { @@ -236,7 +299,13 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model //} } - return hit; + //for (int axis = 0; axis < 3; ++axis) { + // if (glm::isinf(outResolutionVector[axis])) { + // outResolutionVector[axis] = 0.f; + // } + //} + + return glm::all(hit);*/ } bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon) diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index d841c75e..58e48357 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -1,6 +1,7 @@ #include "Collision/Collision.h" #include "Collision/CollisionSystem.h" #include "Core/AABB.h" +#include "Rendering/Model.h" void CollisionSystem::UpdateComponent(World* world, EntityWrapper& entity, ComponentWrapper& component, double dt) { @@ -22,40 +23,42 @@ void CollisionSystem::UpdateComponent(World* world, EntityWrapper& entity, Compo } // Collide against octree - std::vector octreeResult; - m_Octree->BoxesInSameRegion(*boundingBox, octreeResult); - for (auto& boxB : octreeResult) { - glm::vec3 resolutionVector; - if (Collision::IsSameBoxProbably(boxA, boxB)) { - continue; - } - if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { - (glm::vec3&)cTransform["Position"] += resolutionVector; - cPhysics["Velocity"] = glm::vec3(0, 0, 0); - } - } - - // 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; - // } - - // 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(cModel["Resource"]); + //std::vector octreeResult; + //m_Octree->BoxesInSameRegion(*boundingBox, octreeResult); + //for (auto& boxB : octreeResult) { // glm::vec3 resolutionVector; - // if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { + // if (Collision::IsSameBoxProbably(boxA, boxB)) { + // continue; + // } + // if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { // (glm::vec3&)cTransform["Position"] += resolutionVector; + // cPhysics["Velocity"] = glm::vec3(0, 0, 0); // } //} + + // 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 == EntityID(entity)) { + continue; + } + if (!world->HasComponent(cModel.EntityID, "Collidable")) { + continue; + } + + auto absPosition = Transform::AbsolutePosition(world, cModel.EntityID); + auto absOrientation = Transform::AbsoluteOrientation(world, cModel.EntityID); + auto absScale = Transform::AbsoluteScale(world, cModel.EntityID); + glm::mat4 modelMatrix = glm::translate(absPosition); // *glm::toMat4(absOrientation) * glm::scale(absScale); + + RawModel* model = ResourceManager::Load(cModel["Resource"]); + glm::vec3 resolutionVector; + if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { + //TODO: remove Temp. + (glm::vec3&)cTransform["Position"] = glm::vec3(2, 2, 2); + //(glm::vec3&)cTransform["Position"] += resolutionVector; + } + } } bool CollisionSystem::OnKeyUp(const Events::KeyUp & event) From de7c8e9234f89893265a6ce97a95b06268e6eb53 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Thu, 21 Jan 2016 17:10:36 +0100 Subject: [PATCH 02/38] WIP, collision detects too much, no resolving for most cases. --- include/Engine/Collision/Collision.h | 9 + include/Engine/Core/Transform.h | 1 + .../Schema/Entities/ModelCollisionTest.xml | 10 +- src/Engine/Collision/Collision.cpp | 256 ++++++++++++++++-- src/Engine/Collision/CollisionSystem.cpp | 6 +- src/Engine/Core/Transform.cpp | 4 + 6 files changed, 248 insertions(+), 38 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 148f688d..b32d190a 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -26,6 +26,15 @@ bool RayVsAABB(const Ray& ray, const AABB& box); //Return true if the ray hits the box, also outputs distance from ray origin to intersection point in [outDistance]. bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance); +//Return true if the ray hits the triangle, and the distance is less than outDistance. +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 = false); //Return true if the ray hits any of the triangles in the model. Stops checking when a hit is detected. bool RayVsModel(const Ray& ray, const std::vector& modelVertices, diff --git a/include/Engine/Core/Transform.h b/include/Engine/Core/Transform.h index 474a7bdb..b004f047 100644 --- a/include/Engine/Core/Transform.h +++ b/include/Engine/Core/Transform.h @@ -11,6 +11,7 @@ glm::vec3 AbsolutePosition(World* world, EntityID entity); glm::quat AbsoluteOrientation(World* world, EntityID entity); glm::vec3 AbsoluteScale(World* world, EntityID entity); glm::mat4 ModelMatrix(EntityID entity, World* world); +glm::vec3 TransformPoint(const glm::vec3& point, const glm::mat4& matrix); } diff --git a/resources/Schema/Entities/ModelCollisionTest.xml b/resources/Schema/Entities/ModelCollisionTest.xml index 46746264..fd26f0c5 100644 --- a/resources/Schema/Entities/ModelCollisionTest.xml +++ b/resources/Schema/Entities/ModelCollisionTest.xml @@ -11,8 +11,8 @@ - - + + @@ -22,10 +22,10 @@ ../assets/Models/Core/UnitCube.obj + - - + @@ -39,7 +39,7 @@ ../assets/Models/Core/UnitCube.obj - + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index c351d9fa..6794f5b9 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -146,6 +146,38 @@ bool RayVsModel(const Ray& ray, 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& modelVertices, const std::vector& modelIndices, @@ -157,26 +189,12 @@ bool RayVsModel(const Ray& ray, 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) { + glm::vec3 v1 = modelVertices[modelIndices[++i]].Position; + glm::vec3 v2 = modelVertices[modelIndices[++i]].Position; + float dist; + float u; + float v; + if (RayVsTriangle(ray, v0, v1, v2, dist, u, v)) { outDistance = dist; outUCoord = u; outVCoord = v; @@ -199,29 +217,205 @@ bool RayVsModel(const Ray& ray, return hit; } +constexpr inline int squareOf(float x) +{ + return x * x; +} + +constexpr inline int signNonZero(float x) +{ + return x < 0 ? -1 : 1; +} + +inline glm::vec3 signNonZero(const glm::vec3& x) +{ + glm::vec3 r; + for (int i = 0; i < 3; ++i) { + r[i] = signNonZero(x[i]); + } + return r; +} + +bool lineIntersectsBox(const AABB& box, const glm::vec3& v0, const glm::vec3& v1) +{ + const glm::vec3 edgevec = v1 - v0; + glm::vec3 edgevec_signs = signNonZero(edgevec); + + for (int i = 0; i < 3; ++i) + edgevec_signs[i] = signNonZero(edgevec[i]); + + /* + * Test the three cube faces on the v1-ward side of the cube-- + * if v0 is outside any of their planes then there is no intersection. + * Also test the three cube faces on the v0-ward side of the cube-- + * if v1 is outside any of their planes then there is no intersection. + */ + + for (int i = 0; i < 3; ++i) + { + if (v0[i] * edgevec_signs[i] > .5) return false; + if (v1[i] * edgevec_signs[i] < -.5) return false; + } + + /* + * Okay, that's the six easy faces of the rhombic dodecahedron + * out of the way. Six more to go. + * The remaining six planes bound an infinite hexagonal prism + * joining the petrie polygons (skew hexagons) of the two cubes + * centered at the endpoints. + */ + + for (int i = 0; i < 3; ++i) + { + float rhomb_normal_dot_v0, rhomb_normal_dot_cubedge; + + int iplus1 = (i + 1) % 3; + int iplus2 = (i + 2) % 3; + +#ifdef THE_EASY_TO_UNDERSTAND_WAY + + { + real rhomb_normal[3], cubedge_midpoint[3]; + + /* + * rhomb_normal = VXV3(edgevec, unit vector in direction i), + * being cavalier about which direction it's facing + */ + rhomb_normal[i] = 0; + rhomb_normal[iplus1] = edgevec[iplus2]; + rhomb_normal[iplus2] = -edgevec[iplus1]; + + /* + * We now are describing a plane parallel to + * both segment and the cube edge in question. + * if |DOT3(rhomb_normal, an arbitrary point on the segment)| > + * |DOT3(rhomb_normal, an arbitrary point on the cube edge in question| + * then the origin is outside this pair of opposite faces. + * (This is equivalent to saying that the line + * containing the segment is "outside" (i.e. further away from the + * origin than) the line containing the cube edge. + */ + + cubedge_midpoint[i] = 0; + cubedge_midpoint[iplus1] = edgevec_signs[iplus1] * .5; + cubedge_midpoint[iplus2] = -edgevec_signs[iplus2] * .5; + + rhomb_normal_dot_v0 = DOT3(rhomb_normal, v0); + rhomb_normal_dot_cubedge = DOT3(rhomb_normal, cubedge_midpoint); + } + +#else /* the efficient way */ + + rhomb_normal_dot_v0 = edgevec[iplus2] * v0[iplus1] + - edgevec[iplus1] * v0[iplus2]; + + rhomb_normal_dot_cubedge = .5 * + (edgevec[iplus2] * edgevec_signs[iplus1] + + edgevec[iplus1] * edgevec_signs[iplus2]); + +#endif /* the efficient way */ + + if (squareOf(rhomb_normal_dot_v0) > squareOf(rhomb_normal_dot_cubedge)) + return false; /* origin is outside this pair of opposite planes */ + } + return true; +} + +bool vectorHasLength(const glm::vec3& vec) +{ + return glm::all(glm::lessThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); +} + bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, glm::vec3& outResolutionVector) { + //Check so we don't have a zero area triangle when calculating the normal. + glm::vec3 triNormal = glm::cross(v1 - v0, v2 - v0); + if (vectorHasLength(triNormal)) { + return false; + } + const glm::vec3& origin = box.Origin(); + const glm::vec3& half = box.HalfSize(); const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); - const glm::vec3& half = box.HalfSize(); const glm::vec3 triPos[] = { v0, v1, v2 }; + auto insideAllPlanes = glm::tvec3(false); + //All triangle vertex points. + //Check if the triangle is completely outside or inside the box. for (int ax = 0; ax < 3; ++ax) { auto outsideMinPlane = glm::tvec3(false); auto outsideMaxPlane = glm::tvec3(false); + auto insidePlanes = glm::tvec3(false); for (int pos = 0; pos < 3; ++pos) { outsideMinPlane[pos] = (min[ax] > triPos[pos][ax]); outsideMaxPlane[pos] = (triPos[pos][ax] > max[ax]); + insidePlanes[pos] = !(outsideMinPlane[pos] || outsideMaxPlane[pos]); } if (glm::all(outsideMinPlane) || glm::all(outsideMaxPlane)) { return false; } + insideAllPlanes[ax] = glm::all(insidePlanes); } - return true; + if (glm::all(insideAllPlanes)) { + outResolutionVector = glm::vec3(0.f); + LOG_DEBUG("Triangle collision inside"); + return true; //TODO: Resolve. + } + + glm::vec3 triPosInBoxSpace[3]; + for (int i = 0; i < 3; ++i) { + triPosInBoxSpace[i] = (triPos[i] - origin) / (2.0f * half); + } + //All triangle lines. + //Check if each line on the triangle intersects any plane on the box. + for (int l = 0; l < 3; ++l) { + if (lineIntersectsBox(box, triPosInBoxSpace[l], triPosInBoxSpace[(l + 1) % 3])) { + outResolutionVector = glm::vec3(0.f); + LOG_DEBUG("Triangle collision lines"); + return true; //TODO: Resolve. + } + } + + //None of the polygon's edges intersects the cube, finally, check if any of the four + //cube diagonals intersect the interior of the polygon. + //If the polygon does intersect any of the cube diagonals, it will + //intersect the cube diagonal that comes + //closest to being perpendicular to the plane of the polygon. + + triNormal = glm::normalize(triNormal); + glm::vec3 diagonal = signNonZero(triNormal) * half; +#define EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK +#ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK + //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, v0 - origin) / glm::dot(triNormal, diagonal); + + //If intersection point between plane and diagonal is not within the box. + if (glm::abs(t) > 1) { + return false; + } + //glm::vec3 intersection = origin + t * diagonal; +#endif + + //Check if intersection point is on the triangle. + Ray ray(origin, diagonal); + float dist = INFINITY, u, v; + if (RayVsTriangle(ray, v0, v1, v2, dist, u, v, true)) { +#ifndef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK + if (glm::abs(dist) > glm::length(diagonal)) { + return false; + } +#endif + float dist = glm::dot(triNormal, origin + diagonal - v0); + outResolutionVector = dist * triNormal; + LOG_DEBUG("Triangle collision corner"); + return true; + } + return false; } bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) @@ -229,15 +423,19 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model bool hit = false; for (int i = 0; i < modelIndices.size(); i += 3) { glm::vec3 resVec; - hit = AABBvsTriangle( + if (AABBvsTriangle( box, - modelVertices[modelIndices[i]].Position, - modelVertices[modelIndices[i + 1]].Position, - modelVertices[modelIndices[i + 2]].Position, + Transform::TransformPoint(modelVertices[modelIndices[i]].Position, modelMatrix), + Transform::TransformPoint(modelVertices[modelIndices[i + 1]].Position, modelMatrix), + Transform::TransformPoint(modelVertices[modelIndices[i + 2]].Position, modelMatrix), resVec - ); - if (hit) { - break; + )) + { + hit = true; + //If resolution distance is smaller than previous, and is non-zero. + if (glm::length(resVec) < glm::length(outResolutionVector) && vectorHasLength(resVec)) { + outResolutionVector = resVec; + } } } return hit; diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 58e48357..abddce1f 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -49,14 +49,12 @@ void CollisionSystem::UpdateComponent(World* world, EntityWrapper& entity, Compo auto absPosition = Transform::AbsolutePosition(world, cModel.EntityID); auto absOrientation = Transform::AbsoluteOrientation(world, cModel.EntityID); auto absScale = Transform::AbsoluteScale(world, cModel.EntityID); - glm::mat4 modelMatrix = glm::translate(absPosition); // *glm::toMat4(absOrientation) * glm::scale(absScale); + glm::mat4 modelMatrix = glm::translate(absPosition) * glm::toMat4(absOrientation) * glm::scale(absScale); RawModel* model = ResourceManager::Load(cModel["Resource"]); glm::vec3 resolutionVector; if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { - //TODO: remove Temp. - (glm::vec3&)cTransform["Position"] = glm::vec3(2, 2, 2); - //(glm::vec3&)cTransform["Position"] += resolutionVector; + (glm::vec3&)cTransform["Position"] += resolutionVector; } } } diff --git a/src/Engine/Core/Transform.cpp b/src/Engine/Core/Transform.cpp index cbc405a3..054b1984 100644 --- a/src/Engine/Core/Transform.cpp +++ b/src/Engine/Core/Transform.cpp @@ -50,3 +50,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)); +} \ No newline at end of file From 52765cc85bccfc3f4eaddefefcb404350fdc125f Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 22 Jan 2016 11:22:56 +0100 Subject: [PATCH 03/38] TriangleVsBox detection seems to work in all cases, but cannot resolve all cases yet. Also added default ctor for Ray and removed compilation errors in CollisionSystem. --- include/Engine/Core/Ray.h | 4 + src/Engine/Collision/Collision.cpp | 177 ++--------------------- src/Engine/Collision/CollisionSystem.cpp | 12 +- 3 files changed, 25 insertions(+), 168 deletions(-) diff --git a/include/Engine/Core/Ray.h b/include/Engine/Core/Ray.h index a234a488..03bfa391 100644 --- a/include/Engine/Core/Ray.h +++ b/include/Engine/Core/Ray.h @@ -7,6 +7,10 @@ class Ray { public: + Ray() + : m_Origin(glm::vec3(0.f)) + , m_Direction(glm::vec3(0, 0, 1)) + {} Ray(const glm::vec3& origin, const glm::vec3& dir) : m_Origin(origin) , m_Direction(glm::normalize(dir)) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 6794f5b9..d643e8cc 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -217,11 +217,6 @@ bool RayVsModel(const Ray& ray, return hit; } -constexpr inline int squareOf(float x) -{ - return x * x; -} - constexpr inline int signNonZero(float x) { return x < 0 ? -1 : 1; @@ -236,101 +231,17 @@ inline glm::vec3 signNonZero(const glm::vec3& x) return r; } -bool lineIntersectsBox(const AABB& box, const glm::vec3& v0, const glm::vec3& v1) -{ - const glm::vec3 edgevec = v1 - v0; - glm::vec3 edgevec_signs = signNonZero(edgevec); - - for (int i = 0; i < 3; ++i) - edgevec_signs[i] = signNonZero(edgevec[i]); - - /* - * Test the three cube faces on the v1-ward side of the cube-- - * if v0 is outside any of their planes then there is no intersection. - * Also test the three cube faces on the v0-ward side of the cube-- - * if v1 is outside any of their planes then there is no intersection. - */ - - for (int i = 0; i < 3; ++i) - { - if (v0[i] * edgevec_signs[i] > .5) return false; - if (v1[i] * edgevec_signs[i] < -.5) return false; - } - - /* - * Okay, that's the six easy faces of the rhombic dodecahedron - * out of the way. Six more to go. - * The remaining six planes bound an infinite hexagonal prism - * joining the petrie polygons (skew hexagons) of the two cubes - * centered at the endpoints. - */ - - for (int i = 0; i < 3; ++i) - { - float rhomb_normal_dot_v0, rhomb_normal_dot_cubedge; - - int iplus1 = (i + 1) % 3; - int iplus2 = (i + 2) % 3; - -#ifdef THE_EASY_TO_UNDERSTAND_WAY - - { - real rhomb_normal[3], cubedge_midpoint[3]; - - /* - * rhomb_normal = VXV3(edgevec, unit vector in direction i), - * being cavalier about which direction it's facing - */ - rhomb_normal[i] = 0; - rhomb_normal[iplus1] = edgevec[iplus2]; - rhomb_normal[iplus2] = -edgevec[iplus1]; - - /* - * We now are describing a plane parallel to - * both segment and the cube edge in question. - * if |DOT3(rhomb_normal, an arbitrary point on the segment)| > - * |DOT3(rhomb_normal, an arbitrary point on the cube edge in question| - * then the origin is outside this pair of opposite faces. - * (This is equivalent to saying that the line - * containing the segment is "outside" (i.e. further away from the - * origin than) the line containing the cube edge. - */ - - cubedge_midpoint[i] = 0; - cubedge_midpoint[iplus1] = edgevec_signs[iplus1] * .5; - cubedge_midpoint[iplus2] = -edgevec_signs[iplus2] * .5; - - rhomb_normal_dot_v0 = DOT3(rhomb_normal, v0); - rhomb_normal_dot_cubedge = DOT3(rhomb_normal, cubedge_midpoint); - } - -#else /* the efficient way */ - - rhomb_normal_dot_v0 = edgevec[iplus2] * v0[iplus1] - - edgevec[iplus1] * v0[iplus2]; - - rhomb_normal_dot_cubedge = .5 * - (edgevec[iplus2] * edgevec_signs[iplus1] + - edgevec[iplus1] * edgevec_signs[iplus2]); - -#endif /* the efficient way */ - - if (squareOf(rhomb_normal_dot_v0) > squareOf(rhomb_normal_dot_cubedge)) - return false; /* origin is outside this pair of opposite planes */ - } - return true; -} bool vectorHasLength(const glm::vec3& vec) { - return glm::all(glm::lessThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); + return glm::any(glm::greaterThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); } bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, glm::vec3& outResolutionVector) { //Check so we don't have a zero area triangle when calculating the normal. glm::vec3 triNormal = glm::cross(v1 - v0, v2 - v0); - if (vectorHasLength(triNormal)) { + if (!vectorHasLength(triNormal)) { return false; } @@ -366,14 +277,15 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c return true; //TODO: Resolve. } - glm::vec3 triPosInBoxSpace[3]; - for (int i = 0; i < 3; ++i) { - triPosInBoxSpace[i] = (triPos[i] - origin) / (2.0f * half); - } + Ray ray; //All triangle lines. //Check if each line on the triangle intersects any plane on the box. for (int l = 0; l < 3; ++l) { - if (lineIntersectsBox(box, triPosInBoxSpace[l], triPosInBoxSpace[(l + 1) % 3])) { + glm::vec3 edge = triPos[(l + 1) % 3] - triPos[l]; + ray.SetOrigin(triPos[l]); + ray.SetDirection(edge); + float dist; + if (RayVsAABB(ray, box, dist) && dist <= glm::length(edge)) { outResolutionVector = glm::vec3(0.f); LOG_DEBUG("Triangle collision lines"); return true; //TODO: Resolve. @@ -402,7 +314,8 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c #endif //Check if intersection point is on the triangle. - Ray ray(origin, diagonal); + ray.SetOrigin(origin); + ray.SetDirection(diagonal); float dist = INFINITY, u, v; if (RayVsTriangle(ray, v0, v1, v2, dist, u, v, true)) { #ifndef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK @@ -421,6 +334,7 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) { bool hit = false; + outResolutionVector = glm::vec3(INFINITY); for (int i = 0; i < modelIndices.size(); i += 3) { glm::vec3 resVec; if (AABBvsTriangle( @@ -438,72 +352,11 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model } } } - return hit; - //TODO: Remove old code. - - /* - auto hit = glm::tvec3(false); - const glm::vec3& origin = box.Origin(); - const glm::vec3& min = box.MinCorner(); - const glm::vec3& max = box.MaxCorner(); - const glm::vec3& half = box.HalfSize(); - - float minResolution = INFINITY; - outResolutionVector = glm::vec3(0.f); - //TODO: Don't need indices? Remove? - for (const auto& v : modelVertices) { - glm::vec3 p = v.Position; - p = glm::vec3(modelMatrix * glm::vec4(p.x, p.y, p.z, 1)); - - const glm::vec3 diffO = origin - p; - const glm::vec3 distO = glm::abs(diffO); - for (int axis = 0; axis < 3; ++axis) { - float penetration = half[axis] - distO[axis]; - if (penetration > 0){ - if (penetration < minResolution) { - minResolution = penetration; - outResolutionVector = glm::vec3(0.f); - if (p[axis] > origin[axis]) { - outResolutionVector[axis] = -penetration; - } else { - outResolutionVector[axis] = penetration; - } - } - hit[axis] = true; - } - } - - //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; - // } - //} + //TODO: Unnecessary later, remove it. + if (glm::any(glm::isinf(outResolutionVector))) { + outResolutionVector = glm::vec3(0, 0, 0); } - - //for (int axis = 0; axis < 3; ++axis) { - // if (glm::isinf(outResolutionVector[axis])) { - // outResolutionVector[axis] = 0.f; - // } - //} - - return glm::all(hit);*/ + return hit; } bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon) diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 84608fbd..657cc90b 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -37,18 +37,18 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c //} // HACK: Temporarily collide against all collidable models since they're not in the octree yet - auto otherCollidables = world->GetComponents("Model"); + auto otherCollidables = m_World->GetComponents("Model"); for (auto& cModel : *otherCollidables) { - if (cModel.EntityID == EntityID(entity)) { + if (cModel.EntityID == entity.ID) { continue; } - if (!world->HasComponent(cModel.EntityID, "Collidable")) { + if (!m_World->HasComponent(cModel.EntityID, "Collidable")) { continue; } - auto absPosition = Transform::AbsolutePosition(world, cModel.EntityID); - auto absOrientation = Transform::AbsoluteOrientation(world, cModel.EntityID); - auto absScale = Transform::AbsoluteScale(world, cModel.EntityID); + auto absPosition = Transform::AbsolutePosition(m_World, cModel.EntityID); + auto absOrientation = Transform::AbsoluteOrientation(m_World, cModel.EntityID); + auto absScale = Transform::AbsoluteScale(m_World, cModel.EntityID); glm::mat4 modelMatrix = glm::translate(absPosition) * glm::toMat4(absOrientation) * glm::scale(absScale); RawModel* model = ResourceManager::Load(cModel["Resource"]); From 3955e1d60c1d388b39599c5eaa3c343d064bac85 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 22 Jan 2016 13:46:09 +0100 Subject: [PATCH 04/38] Can resolve collision when AABB corners cut through triangle. At least two more cases left to resolve. --- .../Schema/Entities/ModelCollisionTest.xml | 35 ++++++++++++++----- src/Engine/Collision/Collision.cpp | 8 ++--- src/Engine/Collision/CollisionSystem.cpp | 8 ++++- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/resources/Schema/Entities/ModelCollisionTest.xml b/resources/Schema/Entities/ModelCollisionTest.xml index fd26f0c5..f7a547f5 100644 --- a/resources/Schema/Entities/ModelCollisionTest.xml +++ b/resources/Schema/Entities/ModelCollisionTest.xml @@ -2,7 +2,9 @@ - + + + @@ -11,8 +13,8 @@ - - + + @@ -21,25 +23,40 @@ - ../assets/Models/Core/UnitCube.obj - + ../assets/Models/Core/UnitQuad.obj + - + + + - + + + + + ../assets/Models/Core/UnitQuad.obj + + + + + + + + + - ../assets/Models/Core/UnitCube.obj + - + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index d643e8cc..d174eb86 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -299,7 +299,7 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c //closest to being perpendicular to the plane of the polygon. triNormal = glm::normalize(triNormal); - glm::vec3 diagonal = signNonZero(triNormal) * half; + glm::vec3 diagonal = -signNonZero(triNormal) * half; #define EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK #ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK //The triangle plane contains all points P in dot(triNormal, P) == dot(triNormal, v0) @@ -323,9 +323,9 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c return false; } #endif - float dist = glm::dot(triNormal, origin + diagonal - v0); - outResolutionVector = dist * triNormal; - LOG_DEBUG("Triangle collision corner"); + //Distance between triangle plane, and the diagonal corner, multiplied by the normal. + //Signed distance, positive if on the same side as the normal. + outResolutionVector = -glm::dot(triNormal, origin + diagonal - v0) * triNormal; return true; } return false; diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 657cc90b..11d2a836 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -51,7 +51,13 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c auto absScale = Transform::AbsoluteScale(m_World, cModel.EntityID); glm::mat4 modelMatrix = glm::translate(absPosition) * glm::toMat4(absOrientation) * glm::scale(absScale); - RawModel* model = ResourceManager::Load(cModel["Resource"]); + RawModel* model; + try { + model = ResourceManager::Load(cModel["Resource"]); + } catch (const std::exception&) { + continue; + } + glm::vec3 resolutionVector; if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { (glm::vec3&)cTransform["Position"] += resolutionVector; From 1670fb4e48f4e9aa215512a7238ec49898074ba3 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 22 Jan 2016 14:45:50 +0100 Subject: [PATCH 05/38] Moved triangle vs ray code to method. --- include/Engine/Collision/Collision.h | 6 ++++ src/Engine/Collision/Collision.cpp | 54 +++++++++++++++++----------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index b32d190a..4e764905 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -26,6 +26,12 @@ bool RayVsAABB(const Ray& ray, const AABB& box); //Return true if the ray hits the box, also outputs distance from ray origin to intersection point in [outDistance]. bool RayVsAABB(const Ray& ray, const AABB& box, float& outDistance); +//Return true if the ray hits the triangle. +bool RayVsTriangle(const Ray& ray, + const glm::vec3& v0, + const glm::vec3& v1, + const glm::vec3& v2, + bool trueOnNegativeDistance = false); //Return true if the ray hits the triangle, and the distance is less than outDistance. bool RayVsTriangle(const Ray& ray, const glm::vec3& v0, diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index d174eb86..d44c97be 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -116,30 +116,41 @@ bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation) return glm::all(axisesIntersecting); } +bool RayVsTriangle(const Ray& ray, + const glm::vec3& v0, + const glm::vec3& v1, + const glm::vec3& v2, + 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); + 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 std::vector& modelVertices, const std::vector& modelIndices) { for (int i = 0; i < modelIndices.size(); ++i) { glm::vec3 v0 = modelVertices[modelIndices[i]].Position; - glm::vec3 e1 = modelVertices[modelIndices[++i]].Position - v0; //v1 - v0 - glm::vec3 e2 = modelVertices[modelIndices[++i]].Position - v0; //v2 - v0 - glm::vec3 m = ray.Origin() - v0; - glm::vec3 MxE1 = glm::cross(m, e1); - glm::vec3 DxE2 = glm::cross(ray.Direction(), e2); - float DetInv = glm::dot(e1, DxE2); - if (std::abs(DetInv) < FLT_EPSILON) { - continue; - } - DetInv = 1.0f / DetInv; - float u = glm::dot(m, DxE2) * DetInv; - float v = glm::dot(ray.Direction(), MxE1) * DetInv; - //u,v can be very close to 0 but still negative sometimes. added a deltafactor to compensate for that problem - if ((u + 0.001f) < 0 || (v + 0.001f) < 0 || 1 < u + v) { - continue; - } - //Here, u and v are positive, u+v <= 1, and if distance is positive - triangle is hit. - if (0 <= glm::dot(e2, MxE1) * DetInv) { + glm::vec3 v1 = modelVertices[modelIndices[++i]].Position; + glm::vec3 v2 = modelVertices[modelIndices[++i]].Position; + if (RayVsTriangle(ray, v0, v1, v2)) { return true; } } @@ -300,7 +311,7 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c triNormal = glm::normalize(triNormal); glm::vec3 diagonal = -signNonZero(triNormal) * half; -#define EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK +#define EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK //TODO: We should probably performance test with this on/off. #ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK //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. @@ -310,15 +321,16 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c if (glm::abs(t) > 1) { return false; } - //glm::vec3 intersection = origin + t * diagonal; #endif //Check if intersection point is on the triangle. ray.SetOrigin(origin); ray.SetDirection(diagonal); +#ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK + if (RayVsTriangle(ray, v0, v1, v2, true)) { +#else float dist = INFINITY, u, v; if (RayVsTriangle(ray, v0, v1, v2, dist, u, v, true)) { -#ifndef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK if (glm::abs(dist) > glm::length(diagonal)) { return false; } From 3974c649ab8297a8dbf4750643f474f761fa9182 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Sat, 23 Jan 2016 10:45:50 +0100 Subject: [PATCH 06/38] WIP on ModelBoxCollision. Octree works in Release mode. --- include/Engine/Collision/CollisionSystem.h | 7 -- include/Engine/Core/Octree.h | 2 +- .../Schema/Entities/ModelCollisionTest.xml | 6 +- src/Engine/Collision/Collision.cpp | 86 +++++++++++++------ src/Engine/Collision/CollisionSystem.cpp | 13 --- src/Engine/Core/Octree.cpp | 5 -- 6 files changed, 64 insertions(+), 55 deletions(-) diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h index 5f15a3d5..93ec3d76 100644 --- a/include/Engine/Collision/CollisionSystem.h +++ b/include/Engine/Collision/CollisionSystem.h @@ -17,20 +17,13 @@ public: : System(world, eventBroker) , PureSystem("Collidable") , m_Octree(octree) - , zPress(false) { - //TODO: Debug stuff, remove later. - EVENT_SUBSCRIBE_MEMBER(m_EKeyUp, &CollisionSystem::OnKeyUp); } virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override; private: Octree* m_Octree; - bool zPress; - - EventRelay m_EKeyUp; - bool OnKeyUp(const Events::KeyUp &event); }; #endif \ No newline at end of file diff --git a/include/Engine/Core/Octree.h b/include/Engine/Core/Octree.h index bbd27b3c..907d5736 100644 --- a/include/Engine/Core/Octree.h +++ b/include/Engine/Core/Octree.h @@ -111,7 +111,7 @@ struct Child std::vector& m_StaticObjectsRef; std::vector& m_DynamicObjectsRef; - inline bool hasChildren() const; + inline bool hasChildren() const { return m_Children[0] != nullptr; } int childIndexContainingPoint(const glm::vec3& point) const; std::vector childIndicesContainingBox(const AABB& box) const; }; diff --git a/resources/Schema/Entities/ModelCollisionTest.xml b/resources/Schema/Entities/ModelCollisionTest.xml index f7a547f5..f2a78549 100644 --- a/resources/Schema/Entities/ModelCollisionTest.xml +++ b/resources/Schema/Entities/ModelCollisionTest.xml @@ -23,7 +23,7 @@ - ../assets/Models/Core/UnitQuad.obj + ../assets/Models/Core/Tri.obj @@ -36,11 +36,11 @@ - ../assets/Models/Core/UnitQuad.obj + ../assets/Models/Core/Tri.obj - + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index d44c97be..f040ccd5 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -248,7 +248,12 @@ bool vectorHasLength(const glm::vec3& vec) return glm::any(glm::greaterThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); } -bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, glm::vec3& outResolutionVector) +bool AABBvsTriangle(const AABB& box, + const glm::vec3& v0, + const glm::vec3& v1, + const glm::vec3& v2, + glm::vec3& outVector, + int& lineHit) { //Check so we don't have a zero area triangle when calculating the normal. glm::vec3 triNormal = glm::cross(v1 - v0, v2 - v0); @@ -283,23 +288,22 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c insideAllPlanes[ax] = glm::all(insidePlanes); } if (glm::all(insideAllPlanes)) { - outResolutionVector = glm::vec3(0.f); - LOG_DEBUG("Triangle collision inside"); - return true; //TODO: Resolve. + return false; //If a triangle is completely inside the box, we call it a non-intersection. } + triNormal = glm::normalize(triNormal); Ray ray; - //All triangle lines. - //Check if each line on the triangle intersects any plane on the box. + //All triangle edges. + //Check if any edge on the triangle intersects the box. for (int l = 0; l < 3; ++l) { glm::vec3 edge = triPos[(l + 1) % 3] - triPos[l]; ray.SetOrigin(triPos[l]); ray.SetDirection(edge); float dist; if (RayVsAABB(ray, box, dist) && dist <= glm::length(edge)) { - outResolutionVector = glm::vec3(0.f); - LOG_DEBUG("Triangle collision lines"); - return true; //TODO: Resolve. + outVector = triNormal; + lineHit = l; + return true; } } @@ -309,7 +313,6 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c //intersect the cube diagonal that comes //closest to being perpendicular to the plane of the polygon. - triNormal = glm::normalize(triNormal); glm::vec3 diagonal = -signNonZero(triNormal) * half; #define EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK //TODO: We should probably performance test with this on/off. #ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK @@ -337,7 +340,7 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c #endif //Distance between triangle plane, and the diagonal corner, multiplied by the normal. //Signed distance, positive if on the same side as the normal. - outResolutionVector = -glm::dot(triNormal, origin + diagonal - v0) * triNormal; + outVector = -glm::dot(triNormal, origin + diagonal - v0) * triNormal; return true; } return false; @@ -345,28 +348,59 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, c bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) { + struct Triangle + { + glm::vec3 v0, v1, v2; + }; bool hit = false; - outResolutionVector = glm::vec3(INFINITY); + bool cornerHitTODO = false; + outResolutionVector = glm::vec3(0.f); + std::vector hitTriangles; + std::vector hitNormals; for (int i = 0; i < modelIndices.size(); i += 3) { - glm::vec3 resVec; - if (AABBvsTriangle( - box, - Transform::TransformPoint(modelVertices[modelIndices[i]].Position, modelMatrix), - Transform::TransformPoint(modelVertices[modelIndices[i + 1]].Position, modelMatrix), - Transform::TransformPoint(modelVertices[modelIndices[i + 2]].Position, modelMatrix), - resVec - )) + glm::vec3 outVec; + 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); + int lineHit = -1; + if (AABBvsTriangle(box, v0, v1, v2, outVec, lineHit)) { hit = true; - //If resolution distance is smaller than previous, and is non-zero. - if (glm::length(resVec) < glm::length(outResolutionVector) && vectorHasLength(resVec)) { - outResolutionVector = resVec; + if (lineHit == -1) { + outResolutionVector += outVec; + //TODO: We might be able to return here instead, having only convex geometry. + cornerHitTODO = true; + //return true; + } + else { + const glm::vec3 triPos[] = { + v0, v1, v2 + }; + glm::vec3 edge = triPos[(lineHit + 1) % 3] - triPos[lineHit]; + hitTriangles.push_back({v0, v1, v2}); + hitNormals.push_back(outVec); } } } - //TODO: Unnecessary later, remove it. - if (glm::any(glm::isinf(outResolutionVector))) { - outResolutionVector = glm::vec3(0, 0, 0); + if (hitTriangles.size() > 0) { + if (cornerHitTODO) { + LOG_DEBUG("Both edges and corners was hit on the same model."); + return true; + } + for (const glm::vec3& norm : hitNormals) { + outResolutionVector += norm; + } + //Normalize. + outResolutionVector /= hitNormals.size(); + const glm::vec3& origin = box.Origin(); + const glm::vec3& half = box.HalfSize(); + float maxDist = -10; + for (const Triangle& tri : hitTriangles) { + float d = glm::dot(outResolutionVector, 0.333f * (tri.v0 + tri.v1 + tri.v2) - origin); + maxDist = std::max(maxDist, d); + } + glm::vec3 tmp = glm::clamp(2.0f * outResolutionVector, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1)); + outResolutionVector *= maxDist + glm::length(tmp * half); } return hit; } diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 11d2a836..452a73ef 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -17,11 +17,6 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c ComponentWrapper& cTransform = entity["Transform"]; AABB& boxA = *boundingBox; - //Press 'Z' to enable/disable collision. - if (zPress) { - return; - } - // Collide against octree //std::vector octreeResult; //m_Octree->ObjectsInSameRegion(*boundingBox, octreeResult); @@ -64,11 +59,3 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c } } } - -bool CollisionSystem::OnKeyUp(const Events::KeyUp & event) -{ - if (event.KeyCode == GLFW_KEY_Z) { - zPress = !zPress; - } - return false; -} diff --git a/src/Engine/Core/Octree.cpp b/src/Engine/Core/Octree.cpp index d7feba45..f6e358c8 100644 --- a/src/Engine/Core/Octree.cpp +++ b/src/Engine/Core/Octree.cpp @@ -276,9 +276,4 @@ std::vector Child::childIndicesContainingBox(const AABB& box) const } } -inline bool Child::hasChildren() const -{ - return m_Children[0] != nullptr; -} - } \ No newline at end of file From 71404c882819a10a4016cf95b032f67736911d11 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Sat, 23 Jan 2016 14:01:54 +0100 Subject: [PATCH 07/38] Special case for collision vs ground (upward facing) triangles. --- .../Schema/Entities/CollisionTestLevel.xml | 186 ++++++------------ src/Engine/Collision/Collision.cpp | 92 ++++++--- src/Engine/Collision/CollisionSystem.cpp | 1 + 3 files changed, 134 insertions(+), 145 deletions(-) diff --git a/resources/Schema/Entities/CollisionTestLevel.xml b/resources/Schema/Entities/CollisionTestLevel.xml index d5e932c2..76a3b6e6 100644 --- a/resources/Schema/Entities/CollisionTestLevel.xml +++ b/resources/Schema/Entities/CollisionTestLevel.xml @@ -1,122 +1,66 @@ - + + - - - - - - - Models/DummyScene.obj - - - - - - - - - - - Models/Core/UnitSphere.obj - - - - - - - - - - - Models/RotationWidgetX.obj - - - - - - - - - - - - - - - Models/Core/UnitCube.obj - - - - - - - - - - - - - - - - - - - - - - Models/Core/UnitRaptor.obj - - - - - - - - - - - - 20 - - - - - - - - - - - - - Models/Core/UnitCube.obj - - - - - - - - - - - - - Models/Core/UnitCube.obj - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + ../assets\Models\Test/ObstacleCourse.obj + + + + + + + + + + + 0.46000027656555176 + + + + + + + + + + + + + ../assets\Models\Core\UnitCube.obj + + + false + + + + + + + + + + + + + + ../assets\Models\Core\colorbox.obj + + + + + + + + + + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index f040ccd5..d134a5d8 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -4,6 +4,7 @@ #include "Engine/GLM.h" #include "Core/World.h" #include "Rendering/Model.h" +#include "imgui/imgui.h" namespace Collision { @@ -248,12 +249,21 @@ bool vectorHasLength(const glm::vec3& vec) return glm::any(glm::greaterThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); } +enum BoxTriHit +{ + Line0 = 0, + Line1, + Line2, + Ground, + Corner +}; + bool AABBvsTriangle(const AABB& box, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, glm::vec3& outVector, - int& lineHit) + BoxTriHit& outHit) { //Check so we don't have a zero area triangle when calculating the normal. glm::vec3 triNormal = glm::cross(v1 - v0, v2 - v0); @@ -266,6 +276,23 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); + //Check if normal faces upwards, and if so, just push the box upwards on collision. + triNormal = glm::normalize(triNormal); + if (triNormal.y > 0.5f || true) { + //TODO: Optimize calculation since x, z is 0. + //Ray ray(glm::vec3(origin.x, origin.y + half.y, origin.z), glm::vec3(0, -1, 0)); + //float dist = INFINITY, u, v; + //if (RayVsTriangle(ray, v0, v1, v2, dist, u, v) && dist < 2.0f * half.y) { + Ray ray(origin, glm::vec3(0, -1, 0)); + float dist = INFINITY, u, v; + if (RayVsTriangle(ray, v0, v1, v2, dist, u, v) && dist < half.y) { + outVector = glm::vec3(0, half.y - dist, 0); + outHit = Ground; + return true; + } + //return false; //TODO: Uncomment? + } + const glm::vec3 triPos[] = { v0, v1, v2 }; @@ -291,7 +318,6 @@ bool AABBvsTriangle(const AABB& box, return false; //If a triangle is completely inside the box, we call it a non-intersection. } - triNormal = glm::normalize(triNormal); Ray ray; //All triangle edges. //Check if any edge on the triangle intersects the box. @@ -302,7 +328,7 @@ bool AABBvsTriangle(const AABB& box, float dist; if (RayVsAABB(ray, box, dist) && dist <= glm::length(edge)) { outVector = triNormal; - lineHit = l; + outHit = (BoxTriHit)l; return true; } } @@ -341,6 +367,7 @@ bool AABBvsTriangle(const AABB& box, //Distance between triangle plane, and the diagonal corner, multiplied by the normal. //Signed distance, positive if on the same side as the normal. outVector = -glm::dot(triNormal, origin + diagonal - v0) * triNormal; + outHit = Corner; return true; } return false; @@ -348,6 +375,7 @@ bool AABBvsTriangle(const AABB& box, bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) { + AABB newBox = box; struct Triangle { glm::vec3 v0, v1, v2; @@ -362,45 +390,61 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model 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); - int lineHit = -1; - if (AABBvsTriangle(box, v0, v1, v2, outVec, lineHit)) + BoxTriHit hitCase; + if (AABBvsTriangle(newBox, v0, v1, v2, outVec, hitCase)) { hit = true; - if (lineHit == -1) { - outResolutionVector += outVec; - //TODO: We might be able to return here instead, having only convex geometry. - cornerHitTODO = true; - //return true; - } - else { - const glm::vec3 triPos[] = { - v0, v1, v2 - }; - glm::vec3 edge = triPos[(lineHit + 1) % 3] - triPos[lineHit]; + switch (hitCase) { + case Collision::Line0: + case Collision::Line1: + case Collision::Line2: + //TODO: Resolve. + //const glm::vec3 triPos[] = { + // v0, v1, v2 + //}; + //glm::vec3 edge = triPos[(hitCase + 1) % 3] - triPos[hitCase]; hitTriangles.push_back({v0, v1, v2}); hitNormals.push_back(outVec); + ImGui::Text("triangle edge collision."); + break; + case Collision::Corner: + //TODO: We might be able to return here instead, having only convex geometry. + cornerHitTODO = true; + //outResolutionVector += outVec; + //return true; + ImGui::Text("triangle corner collision."); + outResolutionVector += outVec; + newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); + break; + case Collision::Ground: + default: + outResolutionVector += outVec; + newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); + ImGui::Text("triangle ground collision."); + break; } } } if (hitTriangles.size() > 0) { if (cornerHitTODO) { - LOG_DEBUG("Both edges and corners was hit on the same model."); + ImGui::Text("Both edges and corners was hit on the same model."); return true; } + glm::vec3 lineResolve(0.f); for (const glm::vec3& norm : hitNormals) { - outResolutionVector += norm; + lineResolve += norm; } //Normalize. - outResolutionVector /= hitNormals.size(); - const glm::vec3& origin = box.Origin(); - const glm::vec3& half = box.HalfSize(); + lineResolve /= hitNormals.size(); + const glm::vec3& origin = newBox.Origin(); + const glm::vec3& half = newBox.HalfSize(); float maxDist = -10; for (const Triangle& tri : hitTriangles) { - float d = glm::dot(outResolutionVector, 0.333f * (tri.v0 + tri.v1 + tri.v2) - origin); + float d = glm::dot(lineResolve, 0.333f * (tri.v0 + tri.v1 + tri.v2) - origin); maxDist = std::max(maxDist, d); } - glm::vec3 tmp = glm::clamp(2.0f * outResolutionVector, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1)); - outResolutionVector *= maxDist + glm::length(tmp * half); + glm::vec3 tmp = glm::clamp(2.0f * lineResolve, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1)); + outResolutionVector += lineResolve * (maxDist + glm::length(tmp * half)); } return hit; } diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 452a73ef..20854c12 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -56,6 +56,7 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c glm::vec3 resolutionVector; if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { (glm::vec3&)cTransform["Position"] += resolutionVector; + cPhysics["Velocity"] = glm::vec3(0, 0, 0); } } } From 6eeb3ea5248e2a0acfa72d81f65363deb24a7038 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Sat, 23 Jan 2016 17:50:35 +0100 Subject: [PATCH 08/38] WIP, Bug fix + nitpicks + optimizations. --- resources/Schema/Entities/CollisionTestLevel.xml | 3 ++- src/Engine/Collision/Collision.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/resources/Schema/Entities/CollisionTestLevel.xml b/resources/Schema/Entities/CollisionTestLevel.xml index 76a3b6e6..5c029cf3 100644 --- a/resources/Schema/Entities/CollisionTestLevel.xml +++ b/resources/Schema/Entities/CollisionTestLevel.xml @@ -42,7 +42,8 @@ - + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index d134a5d8..2b4b989e 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -278,8 +278,8 @@ bool AABBvsTriangle(const AABB& box, //Check if normal faces upwards, and if so, just push the box upwards on collision. triNormal = glm::normalize(triNormal); - if (triNormal.y > 0.5f || true) { - //TODO: Optimize calculation since x, z is 0. + if (triNormal.y > 0.5f) { + //TODO: Optimize calculation since x, z is 0, y is -1. //Ray ray(glm::vec3(origin.x, origin.y + half.y, origin.z), glm::vec3(0, -1, 0)); //float dist = INFINITY, u, v; //if (RayVsTriangle(ray, v0, v1, v2, dist, u, v) && dist < 2.0f * half.y) { @@ -290,7 +290,7 @@ bool AABBvsTriangle(const AABB& box, outHit = Ground; return true; } - //return false; //TODO: Uncomment? + return false; } const glm::vec3 triPos[] = { @@ -300,7 +300,8 @@ bool AABBvsTriangle(const AABB& box, auto insideAllPlanes = glm::tvec3(false); //All triangle vertex points. //Check if the triangle is completely outside or inside the box. - for (int ax = 0; ax < 3; ++ax) { + //First test x, then z, lastly y, since y is least likely to result in a early false. + for (int ax : { 0, 2, 1 }) { auto outsideMinPlane = glm::tvec3(false); auto outsideMaxPlane = glm::tvec3(false); auto insidePlanes = glm::tvec3(false); @@ -385,14 +386,13 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model outResolutionVector = glm::vec3(0.f); std::vector hitTriangles; std::vector hitNormals; - for (int i = 0; i < modelIndices.size(); i += 3) { + for (int i = 0; i < modelIndices.size(); ) { glm::vec3 outVec; 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); BoxTriHit hitCase; - if (AABBvsTriangle(newBox, v0, v1, v2, outVec, hitCase)) - { + if (AABBvsTriangle(newBox, v0, v1, v2, outVec, hitCase)) { hit = true; switch (hitCase) { case Collision::Line0: @@ -403,7 +403,7 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model // v0, v1, v2 //}; //glm::vec3 edge = triPos[(hitCase + 1) % 3] - triPos[hitCase]; - hitTriangles.push_back({v0, v1, v2}); + hitTriangles.push_back({ v0, v1, v2 }); hitNormals.push_back(outVec); ImGui::Text("triangle edge collision."); break; From e6224a4e96ab9d9d67c43810353098207927213d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Sat, 23 Jan 2016 18:31:27 +0100 Subject: [PATCH 09/38] WIP (not working), attempt at separating axis theorem stuff. --- src/Engine/Collision/Collision.cpp | 96 +++++------------------------- 1 file changed, 15 insertions(+), 81 deletions(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 2b4b989e..911b87bc 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -275,103 +275,37 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& half = box.HalfSize(); const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); - - //Check if normal faces upwards, and if so, just push the box upwards on collision. - triNormal = glm::normalize(triNormal); - if (triNormal.y > 0.5f) { - //TODO: Optimize calculation since x, z is 0, y is -1. - //Ray ray(glm::vec3(origin.x, origin.y + half.y, origin.z), glm::vec3(0, -1, 0)); - //float dist = INFINITY, u, v; - //if (RayVsTriangle(ray, v0, v1, v2, dist, u, v) && dist < 2.0f * half.y) { - Ray ray(origin, glm::vec3(0, -1, 0)); - float dist = INFINITY, u, v; - if (RayVsTriangle(ray, v0, v1, v2, dist, u, v) && dist < half.y) { - outVector = glm::vec3(0, half.y - dist, 0); - outHit = Ground; - return true; - } - return false; - } - const glm::vec3 triPos[] = { v0, v1, v2 }; - auto insideAllPlanes = glm::tvec3(false); - //All triangle vertex points. - //Check if the triangle is completely outside or inside the box. - //First test x, then z, lastly y, since y is least likely to result in a early false. - for (int ax : { 0, 2, 1 }) { - auto outsideMinPlane = glm::tvec3(false); - auto outsideMaxPlane = glm::tvec3(false); - auto insidePlanes = glm::tvec3(false); - for (int pos = 0; pos < 3; ++pos) { - outsideMinPlane[pos] = (min[ax] > triPos[pos][ax]); - outsideMaxPlane[pos] = (triPos[pos][ax] > max[ax]); - insidePlanes[pos] = !(outsideMinPlane[pos] || outsideMaxPlane[pos]); - } - if (glm::all(outsideMinPlane) || glm::all(outsideMaxPlane)) { - return false; - } - insideAllPlanes[ax] = glm::all(insidePlanes); - } - if (glm::all(insideAllPlanes)) { - return false; //If a triangle is completely inside the box, we call it a non-intersection. + for (int axis : {1, 0, 2}) { + //2D Triangle. + //for axis=0,1,2: 2d point takes from xy,xz,yx. + int dim1 = axis == 2 ? 0 : 1; //0,0,1 + int dim2 = axis == 0 ? 1 : 2; //1,2,2 + glm::vec2 t0(triPos[0][dim1], triPos[0][dim2]); + glm::vec2 t1(triPos[1][dim1], triPos[1][dim2]); + glm::vec2 t2(triPos[2][dim1], triPos[2][dim2]); + //Project tri, + //Project box, + //if projections don't overlap, return false. } - Ray ray; - //All triangle edges. - //Check if any edge on the triangle intersects the box. - for (int l = 0; l < 3; ++l) { - glm::vec3 edge = triPos[(l + 1) % 3] - triPos[l]; - ray.SetOrigin(triPos[l]); - ray.SetDirection(edge); - float dist; - if (RayVsAABB(ray, box, dist) && dist <= glm::length(edge)) { - outVector = triNormal; - outHit = (BoxTriHit)l; - return true; - } - } - - //None of the polygon's edges intersects the cube, finally, check if any of the four - //cube diagonals intersect the interior of the polygon. //If the polygon does intersect any of the cube diagonals, it will //intersect the cube diagonal that comes //closest to being perpendicular to the plane of the polygon. - + triNormal = glm::normalize(triNormal); glm::vec3 diagonal = -signNonZero(triNormal) * half; -#define EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK //TODO: We should probably performance test with this on/off. -#ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK //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, v0 - origin) / glm::dot(triNormal, diagonal); - - //If intersection point between plane and diagonal is not within the box. + //If intersection point between plane and diagonal is within the box. if (glm::abs(t) > 1) { return false; } -#endif - - //Check if intersection point is on the triangle. - ray.SetOrigin(origin); - ray.SetDirection(diagonal); -#ifdef EARLY_OUT_OR_MAYBE_JUST_EXTRA_WORK - if (RayVsTriangle(ray, v0, v1, v2, true)) { -#else - float dist = INFINITY, u, v; - if (RayVsTriangle(ray, v0, v1, v2, dist, u, v, true)) { - if (glm::abs(dist) > glm::length(diagonal)) { - return false; - } -#endif - //Distance between triangle plane, and the diagonal corner, multiplied by the normal. - //Signed distance, positive if on the same side as the normal. - outVector = -glm::dot(triNormal, origin + diagonal - v0) * triNormal; - outHit = Corner; - return true; - } - return false; + //TODO: Resolve it. + return true; } bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) From 910b3d10e4b18f20a1e1ed32430e077d3f351943 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Sun, 24 Jan 2016 15:32:10 +0100 Subject: [PATCH 10/38] WIP (false collisions), collision method expanded, not working. --- .../Schema/Entities/ModelCollisionTest.xml | 7 +- src/Engine/Collision/Collision.cpp | 184 +++++++++--------- 2 files changed, 97 insertions(+), 94 deletions(-) diff --git a/resources/Schema/Entities/ModelCollisionTest.xml b/resources/Schema/Entities/ModelCollisionTest.xml index f2a78549..c4a3f81b 100644 --- a/resources/Schema/Entities/ModelCollisionTest.xml +++ b/resources/Schema/Entities/ModelCollisionTest.xml @@ -27,9 +27,8 @@ - - - + + @@ -56,7 +55,7 @@ - + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 911b87bc..48a6a1e3 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -249,24 +249,70 @@ bool vectorHasLength(const glm::vec3& vec) return glm::any(glm::greaterThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); } -enum BoxTriHit +bool AARectangleVsTriangle(const glm::vec2& boxMin, + const glm::vec2& boxMax, + const std::array& triPos) { - Line0 = 0, - Line1, - Line2, - Ground, - Corner -}; + //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 (minTri > boxMax[ax] || maxTri < boxMin[ax]) { + return false; + } + } + //Project along triangle normals. + //Put edges into normal vector, make normals in the loop. + std::array triNormals = { + triPos[1] - triPos[0], + triPos[2] - triPos[1], + triPos[0] - triPos[2] + }; + std::array boxPos = { + boxMax, + glm::vec2(boxMax.x, boxMin.y), + glm::vec2(boxMin.x, boxMax.y), + boxMin + }; + for (auto& normal : triNormals) { + //Rotate edge to a normal. + normal = 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, minTri); + maxBox = std::max(dot, maxTri); + } + if (maxBox < minTri || minBox > maxTri) { + return false; + } + } + return true; +} + +//An array containing 3 int pairs { 0, 2 }, { 0, 1 }, { 1, 2 } +constexpr std::array, 3> DimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); bool AABBvsTriangle(const AABB& box, - const glm::vec3& v0, - const glm::vec3& v1, - const glm::vec3& v2, - glm::vec3& outVector, - BoxTriHit& outHit) + const std::array& triPos, + glm::vec3& outVector) { //Check so we don't have a zero area triangle when calculating the normal. - glm::vec3 triNormal = glm::cross(v1 - v0, v2 - v0); + glm::vec3 triNormal = glm::cross(triPos[1] - triPos[0], triPos[2] - triPos[0]); if (!vectorHasLength(triNormal)) { return false; } @@ -275,111 +321,69 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& half = box.HalfSize(); const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); - const glm::vec3 triPos[] = { - v0, v1, v2 - }; - - for (int axis : {1, 0, 2}) { + glm::tvec3 axisHit(false, false, false); + int i = 0; + for (std::pair dim : DimensionPairs) { //2D Triangle. //for axis=0,1,2: 2d point takes from xy,xz,yx. - int dim1 = axis == 2 ? 0 : 1; //0,0,1 - int dim2 = axis == 0 ? 1 : 2; //1,2,2 - glm::vec2 t0(triPos[0][dim1], triPos[0][dim2]); - glm::vec2 t1(triPos[1][dim1], triPos[1][dim2]); - glm::vec2 t2(triPos[2][dim1], triPos[2][dim2]); - //Project tri, - //Project box, + //Project triangle. + std::array 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]); //if projections don't overlap, return false. + if (!AARectangleVsTriangle(boxMin, boxMax, t2D)) { + //return false; + } else { + axisHit[i] = true; + ImGui::Text("Triangle collision: Box axis %s", i == 0 ? "y" : i == 1 ? "z" : "x"); + } + ++i; } - //If the polygon does intersect any of the cube diagonals, it will + //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 polygon. + //closest to being perpendicular to the plane of the triangle. triNormal = glm::normalize(triNormal); 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, v0 - origin) / glm::dot(triNormal, diagonal); + 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; + } else { + ImGui::Text("Triangle collision: Triangle axis."); + return glm::all(axisHit); } //TODO: Resolve it. + outVector = glm::vec3(0.f); return true; } bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) { AABB newBox = box; - struct Triangle - { - glm::vec3 v0, v1, v2; - }; bool hit = false; - bool cornerHitTODO = false; outResolutionVector = glm::vec3(0.f); - std::vector hitTriangles; - std::vector hitNormals; for (int i = 0; i < modelIndices.size(); ) { + std::array 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; - 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); - BoxTriHit hitCase; - if (AABBvsTriangle(newBox, v0, v1, v2, outVec, hitCase)) { + if (AABBvsTriangle(newBox, triVertices, outVec)) { + ImGui::Text("Triangle collision: True."); hit = true; - switch (hitCase) { - case Collision::Line0: - case Collision::Line1: - case Collision::Line2: - //TODO: Resolve. - //const glm::vec3 triPos[] = { - // v0, v1, v2 - //}; - //glm::vec3 edge = triPos[(hitCase + 1) % 3] - triPos[hitCase]; - hitTriangles.push_back({ v0, v1, v2 }); - hitNormals.push_back(outVec); - ImGui::Text("triangle edge collision."); - break; - case Collision::Corner: - //TODO: We might be able to return here instead, having only convex geometry. - cornerHitTODO = true; - //outResolutionVector += outVec; - //return true; - ImGui::Text("triangle corner collision."); - outResolutionVector += outVec; - newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); - break; - case Collision::Ground: - default: - outResolutionVector += outVec; - newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); - ImGui::Text("triangle ground collision."); - break; - } + outResolutionVector += outVec; + newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); } } - if (hitTriangles.size() > 0) { - if (cornerHitTODO) { - ImGui::Text("Both edges and corners was hit on the same model."); - return true; - } - glm::vec3 lineResolve(0.f); - for (const glm::vec3& norm : hitNormals) { - lineResolve += norm; - } - //Normalize. - lineResolve /= hitNormals.size(); - const glm::vec3& origin = newBox.Origin(); - const glm::vec3& half = newBox.HalfSize(); - float maxDist = -10; - for (const Triangle& tri : hitTriangles) { - float d = glm::dot(lineResolve, 0.333f * (tri.v0 + tri.v1 + tri.v2) - origin); - maxDist = std::max(maxDist, d); - } - glm::vec3 tmp = glm::clamp(2.0f * lineResolve, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1)); - outResolutionVector += lineResolve * (maxDist + glm::length(tmp * half)); - } return hit; } From f1343e2f82b14a74710b8b01f5e1926851c9967d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Sun, 24 Jan 2016 21:08:42 +0100 Subject: [PATCH 11/38] Collision may be working, kind of. --- src/Engine/Collision/Collision.cpp | 69 +++++++++++++++++++----------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 48a6a1e3..41ca2155 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -243,16 +243,19 @@ inline glm::vec3 signNonZero(const glm::vec3& x) return r; } - -bool vectorHasLength(const glm::vec3& vec) +template +bool vectorHasLength(const T& vec) { - return glm::any(glm::greaterThan(glm::abs(vec), glm::vec3(0.0001f, 0.0001f, 0.0001f))); + return glm::any(glm::greaterThan(glm::abs(vec), T(0.0001f))); } -bool AARectangleVsTriangle(const glm::vec2& boxMin, +bool rectangleVsTriangle(const glm::vec2& boxMin, const glm::vec2& boxMax, - const std::array& triPos) + const std::array& triPos, + glm::vec2& resolutionDirection, + float& resolutionDistance) { + resolutionDistance = INFINITY; //Project along box normals (coordinate axes, since it's axis-aligned). for (int ax = 0; ax < 2; ++ax) { float minTri = INFINITY; @@ -264,6 +267,12 @@ bool AARectangleVsTriangle(const glm::vec2& boxMin, if (minTri > boxMax[ax] || maxTri < boxMin[ax]) { return false; } + float push = std::min(maxTri - boxMin[ax], boxMax[ax] - minTri); + if (push < resolutionDistance) { + resolutionDistance = push; + resolutionDirection[1 - ax] = 0.f; + resolutionDirection[ax] = resolutionDistance; + } } //Project along triangle normals. //Put edges into normal vector, make normals in the loop. @@ -279,6 +288,9 @@ bool AARectangleVsTriangle(const glm::vec2& boxMin, boxMin }; for (auto& normal : triNormals) { + if (!vectorHasLength(normal)) { + continue; + } //Rotate edge to a normal. normal = glm::vec2(-normal.y, normal.x); //Project triangle onto the normal. @@ -294,18 +306,24 @@ bool AARectangleVsTriangle(const glm::vec2& boxMin, float maxBox = -INFINITY; for (const glm::vec2& point : boxPos) { float dot = glm::dot(normal, point); - minBox = std::min(dot, minTri); - maxBox = std::max(dot, maxTri); + minBox = std::min(dot, minBox); + maxBox = std::max(dot, maxBox); } if (maxBox < minTri || minBox > maxTri) { return false; } + //Here: maxBox > minTri && minBox < maxTri + float push = std::min(maxTri - minBox, maxBox - minTri); + if (push < resolutionDistance) { + resolutionDistance = push; + resolutionDirection = resolutionDistance * glm::normalize(normal); + } } return true; } //An array containing 3 int pairs { 0, 2 }, { 0, 1 }, { 1, 2 } -constexpr std::array, 3> DimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); +constexpr std::array, 3> dimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); bool AABBvsTriangle(const AABB& box, const std::array& triPos, @@ -321,11 +339,11 @@ bool AABBvsTriangle(const AABB& box, const glm::vec3& half = box.HalfSize(); const glm::vec3& min = box.MinCorner(); const glm::vec3& max = box.MaxCorner(); - glm::tvec3 axisHit(false, false, false); - int i = 0; - for (std::pair dim : DimensionPairs) { + float minimumTranslation = INFINITY; + + //For each projection in xy-, xz-, and yx-planes. + for (std::pair dim : dimensionPairs) { //2D Triangle. - //for axis=0,1,2: 2d point takes from xy,xz,yx. //Project triangle. std::array t2D = { glm::vec2(triPos[0][dim.first], triPos[0][dim.second]), @@ -335,33 +353,35 @@ bool AABBvsTriangle(const AABB& box, //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; //if projections don't overlap, return false. - if (!AARectangleVsTriangle(boxMin, boxMax, t2D)) { - //return false; - } else { - axisHit[i] = true; - ImGui::Text("Triangle collision: Box axis %s", i == 0 ? "y" : i == 1 ? "z" : "x"); + if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist)) { + return false; + } else if (resolutionDist < minimumTranslation) { + outVector = glm::vec3(0.f); + outVector[dim.first] = resolutionVector.x; + outVector[dim.second] = resolutionVector.y; + minimumTranslation = resolutionDist; } - ++i; } //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. triNormal = glm::normalize(triNormal); - glm::vec3 diagonal = -signNonZero(triNormal) * half; + 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; - } else { - ImGui::Text("Triangle collision: Triangle axis."); - return glm::all(axisHit); } - //TODO: Resolve it. - outVector = glm::vec3(0.f); + glm::vec3 cornerResolution = (1+t) * diagonal; + if (glm::length(cornerResolution) < minimumTranslation) { + outVector = cornerResolution; + } return true; } @@ -378,7 +398,6 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model }; glm::vec3 outVec; if (AABBvsTriangle(newBox, triVertices, outVec)) { - ImGui::Text("Triangle collision: True."); hit = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); From 3389b965eb888a8a5c8ebe33a91da574568a3d09 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 25 Jan 2016 10:55:05 +0100 Subject: [PATCH 12/38] Test on game level v1. --- .../Schema/Entities/LevelCollisionTest.xml | 43 ++++++++++++++++++ src/Engine/Collision/Collision.cpp | 44 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 resources/Schema/Entities/LevelCollisionTest.xml diff --git a/resources/Schema/Entities/LevelCollisionTest.xml b/resources/Schema/Entities/LevelCollisionTest.xml new file mode 100644 index 00000000..0feb9a11 --- /dev/null +++ b/resources/Schema/Entities/LevelCollisionTest.xml @@ -0,0 +1,43 @@ + + + + + + + ../assets\Models/MapVersion1.obj + + + + + + + + + + + + + ../assets/Models/Core/UnitCube.obj + + + false + + + + + + + + + + + + + + + + + + + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 41ca2155..7e9edb31 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -403,6 +403,50 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); } } + + //outResolutionVector = glm::vec3(INFINITY); + //std::stack testBoxes; + //std::stack resolveIndices; + //std::stack resolveVectors; + //resolveVectors.push(glm::vec3(0.f)); + //resolveIndices.push(0); + //testBoxes.push(box); + //do { + // int i = resolveIndices.top(); + // resolveIndices.pop(); + // while (i < modelIndices.size()) { + // std::array 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 test = AABBvsTriangle(testBoxes.top(), triVertices, outVec); + // if (test) { + // hit = true; + // //emplace? + // resolveIndices.push(i); + // testBoxes.push(AABB::FromOriginSize(box.Origin() + outVec, box.Size())); + // resolveVectors.top() += outVec; + // } + // } + // testBoxes.pop(); + // resolveVectors.push(glm::vec3(0.f)); + //} while (!resolveIndices.empty()); + //if (hit) { + // if (!resolveVectors.empty()) { + // while (!resolveVectors.empty()) { + // if (glm::length2(resolveVectors.top()) < glm::length2(outResolutionVector)) { + // outResolutionVector = resolveVectors.top(); + // } + // resolveVectors.pop(); + // } + // } else { + // //TODO: This won't happen. + // ImGui::Text("Collision, but not resolved."); + // } + // return true; + //} return hit; } From 6ea490e215775c1e11ae0b7bdee39396f83b543b Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 27 Jan 2016 10:34:26 +0100 Subject: [PATCH 13/38] Tried a horrible recursive method, extreme framedrop. --- include/Engine/Collision/Collision.h | 1 + .../Schema/Entities/LevelCollisionTest.xml | 6 +- src/Engine/Collision/Collision.cpp | 62 +++++++++++++++++-- src/Engine/Collision/CollisionSystem.cpp | 12 ++-- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 4e764905..6e2d679e 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -65,6 +65,7 @@ bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, + const glm::vec3& boxVelocity, glm::vec3& outResolutionVector); //Return true if the boxes are intersecting. diff --git a/resources/Schema/Entities/LevelCollisionTest.xml b/resources/Schema/Entities/LevelCollisionTest.xml index 0feb9a11..ce0fe405 100644 --- a/resources/Schema/Entities/LevelCollisionTest.xml +++ b/resources/Schema/Entities/LevelCollisionTest.xml @@ -6,9 +6,7 @@ ../assets\Models/MapVersion1.obj - - - + @@ -24,7 +22,7 @@ - + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 7e9edb31..96d908ff 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -327,11 +327,14 @@ constexpr std::array, 3> dimensionPairs({ std::pair& triPos, + const glm::vec3& boxVelocity, glm::vec3& outVector) { //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)) { + if (!vectorHasLength(triNormal) || glm::dot(triNormal, boxVelocity) > 0) { return false; } @@ -385,10 +388,18 @@ bool AABBvsTriangle(const AABB& box, return true; } -bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector) +bool AABBvsTriangles(const AABB& box, + const std::vector& modelVertices, + const std::vector& modelIndices, + const glm::mat4& modelMatrix, + const glm::vec3& boxVelocity, + glm::vec3& outResolutionVector, + int startIndex, + int recursiveDepth) { - AABB newBox = box; bool hit = false; + + AABB newBox = box; outResolutionVector = glm::vec3(0.f); for (int i = 0; i < modelIndices.size(); ) { std::array triVertices = { @@ -397,13 +408,38 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix) }; glm::vec3 outVec; - if (AABBvsTriangle(newBox, triVertices, outVec)) { + if (AABBvsTriangle(newBox, triVertices, boxVelocity, outVec)) { hit = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); } } + //outResolutionVector = glm::vec3(INFINITY); + //if (recursiveDepth > 2) { + // return true; + //} + //for (int i = startIndex; i < modelIndices.size(); ) { + // std::array 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; + // if (AABBvsTriangle(box, triVertices, boxVelocity, outVec) && glm::length2(outVec) < glm::length2(outResolutionVector)) { + // hit = true; + // glm::vec3 potentialResolution = outVec; + // const AABB& resolvedBox = AABB::FromOriginSize(box.Origin() + potentialResolution, box.Size()); + // if (AABBvsTriangles(resolvedBox, modelVertices, modelIndices, modelMatrix, boxVelocity, outVec, i, recursiveDepth+1)) { + // potentialResolution += outVec; + // if (glm::length2(potentialResolution) > glm::length2(outResolutionVector)) { + // continue; + // } + // } + // outResolutionVector = potentialResolution; + // } + //} + //outResolutionVector = glm::vec3(INFINITY); //std::stack testBoxes; //std::stack resolveIndices; @@ -449,6 +485,24 @@ bool AABBvsTriangles(const AABB& box, const std::vector& model //} return hit; } +bool AABBvsTriangles(const AABB& box, + const std::vector& modelVertices, + const std::vector& modelIndices, + const glm::mat4& modelMatrix, + const glm::vec3& boxVelocity, + glm::vec3& outResolutionVector) +{ + return AABBvsTriangles( + box, + modelVertices, + modelIndices, + modelMatrix, + boxVelocity, + outResolutionVector, + 0, + 0 + ); +} bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon) { diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 20854c12..612d0707 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -41,11 +41,6 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c continue; } - auto absPosition = Transform::AbsolutePosition(m_World, cModel.EntityID); - auto absOrientation = Transform::AbsoluteOrientation(m_World, cModel.EntityID); - auto absScale = Transform::AbsoluteScale(m_World, cModel.EntityID); - glm::mat4 modelMatrix = glm::translate(absPosition) * glm::toMat4(absOrientation) * glm::scale(absScale); - RawModel* model; try { model = ResourceManager::Load(cModel["Resource"]); @@ -53,8 +48,13 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c continue; } + auto absPosition = Transform::AbsolutePosition(m_World, cModel.EntityID); + auto absOrientation = Transform::AbsoluteOrientation(m_World, cModel.EntityID); + auto absScale = Transform::AbsoluteScale(m_World, cModel.EntityID); + glm::mat4 modelMatrix = glm::translate(absPosition) * glm::toMat4(absOrientation) * glm::scale(absScale); + glm::vec3 resolutionVector; - if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { + if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, (glm::vec3&)cPhysics["Velocity"], resolutionVector)) { (glm::vec3&)cTransform["Position"] += resolutionVector; cPhysics["Velocity"] = glm::vec3(0, 0, 0); } From 5cdf6db3404904ca799bed8d65e880739d4a6d26 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 27 Jan 2016 14:46:45 +0100 Subject: [PATCH 14/38] Modified HealthSystem so it actually publishes the DeathEvent. Modified EPlayerHealthPickup. Fixed 2 tests. Started work on PlayerDeathSystem --- include/Engine/Core/EPlayerDamage.h | 1 + include/Engine/Core/EPlayerHealthPickup.h | 4 +- include/Game/Systems/HealthSystem.h | 2 +- include/Game/Systems/PlayerDeathSystem.h | 39 ++++++++ resources/Schema/Entities/GameMap.xml | 4 +- src/Game/Game.cpp | 2 + src/Game/Systems/HealthSystem.cpp | 45 ++------- src/Game/Systems/PlayerDeathSystem.cpp | 116 ++++++++++++++++++++++ src/Tests/CollisionTest.cpp | 2 +- src/Tests/HealthSystemTest.cpp | 4 +- 10 files changed, 176 insertions(+), 43 deletions(-) create mode 100644 include/Game/Systems/PlayerDeathSystem.h create mode 100644 src/Game/Systems/PlayerDeathSystem.cpp diff --git a/include/Engine/Core/EPlayerDamage.h b/include/Engine/Core/EPlayerDamage.h index a7e135ce..8ba3907e 100644 --- a/include/Engine/Core/EPlayerDamage.h +++ b/include/Engine/Core/EPlayerDamage.h @@ -9,6 +9,7 @@ namespace Events struct PlayerDamage : Event { + //NOTE: this struct is missing information on what the damageSource is EntityWrapper Player; double Damage; }; diff --git a/include/Engine/Core/EPlayerHealthPickup.h b/include/Engine/Core/EPlayerHealthPickup.h index f3158f92..46ed76db 100644 --- a/include/Engine/Core/EPlayerHealthPickup.h +++ b/include/Engine/Core/EPlayerHealthPickup.h @@ -2,15 +2,15 @@ #define EPlayerHealthPickup_h__ #include "EventBroker.h" -#include "../Core/Entity.h" +#include "../Core/EntityWrapper.h" namespace Events { struct PlayerHealthPickup : Event { + EntityWrapper Player; double HealthAmount; - EntityID PlayerHealedID; }; } diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index 3b069349..46f24630 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -26,7 +26,7 @@ private: EventRelay m_EPlayerDamage; bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e); EventRelay m_EPlayerHealthPickup; - bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e); + bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e); //vector which will keep track of health changes std::vector> m_DeltaHealthVector; diff --git a/include/Game/Systems/PlayerDeathSystem.h b/include/Game/Systems/PlayerDeathSystem.h new file mode 100644 index 00000000..3f7268ca --- /dev/null +++ b/include/Game/Systems/PlayerDeathSystem.h @@ -0,0 +1,39 @@ +#ifndef PlayerDeathSystem_h__ +#define PlayerDeathSystem_h__ + +#include "Core/System.h" +#include "Input/EInputCommand.h" +#include "Systems/SpawnerSystem.h" +#include "Events/ESpawnerSpawn.h" +#include "Core/EPlayerSpawned.h" +#include "Rendering/ESetCamera.h" +#include "Core/ConfigFile.h" + +#include "Core/EPlayerDeath.h" +//tests +#include "Core/EPlayerDamage.h" + +class PlayerDeathSystem : public ImpureSystem +{ +public: + PlayerDeathSystem(World* world, EventBroker* eventBroker); + + virtual void Update(double dt) override; + +private: + struct SpawnRequest + { + int PlayerID; + ComponentInfo::EnumType Team; + }; + + bool m_NetworkEnabled = false; + std::vector m_SpawnRequests; + std::map m_PlayerEntities; + + EventRelay m_OnInputCommand; + bool OnInputCommand(const Events::InputCommand& e); + EventRelay m_OnPlayerDeath; + bool OnPlayerDeath(Events::PlayerDeath& e); +}; +#endif \ No newline at end of file diff --git a/resources/Schema/Entities/GameMap.xml b/resources/Schema/Entities/GameMap.xml index 97fd3f4d..b37f702d 100644 --- a/resources/Schema/Entities/GameMap.xml +++ b/resources/Schema/Entities/GameMap.xml @@ -139,7 +139,7 @@ - + @@ -201,7 +201,7 @@ - + diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index d13d62d4..b0c10a74 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -7,6 +7,7 @@ #include "Systems/PlayerMovementSystem.h" #include "Systems/SpawnerSystem.h" #include "Systems/PlayerSpawnSystem.h" +#include "Systems/PlayerDeathSystem.h" #include "Core/EntityFileWriter.h" #include "Game/Systems/CapturePointSystem.h" #include "Game/Systems/WeaponSystem.h" @@ -86,6 +87,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); m_SystemPipeline->AddSystem(updateOrderLevel); // Populate Octree with collidables diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 9e118070..26d38c69 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -11,38 +11,6 @@ HealthSystem::HealthSystem(World* m_World, EventBroker* eventBroker) void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) { - //if entityID of health is 9 then the players ID is also 9 (player,health are connected to the same entity) - double maxHealth = (double)component["MaxHealth"]; - - //process the DeltaHealthVector and change the entitys health accordingly - for (size_t i = m_DeltaHealthVector.size(); i > 0; i--) - { - auto deltaHP = m_DeltaHealthVector[i - 1]; - //if we have a healthchange for the current player and health is greater than 0, then apply it - if (std::get<0>(deltaHP) == component.EntityID && (double)component["Health"] > 0.0f) { - //get the deltaHP value from the tuple and make sure you dont get more than maxHealth - double newHealth = std::min((double)component["Health"] + (double)std::get<1>(deltaHP), maxHealth); - component["Health"] = newHealth; - m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + i - 1); - //check if health is <= 0 - if ((double)component["Health"] <= 0.0f) { - component["Health"] = 0.0; - //publish death event - Events::PlayerDeath e; - e.PlayerID = component.EntityID; - m_EventBroker->Publish(e); - //clear the remaining hpDeltas for the dead player - for (size_t j = m_DeltaHealthVector.size(); j > 0; j--) - { - if (std::get<0>(m_DeltaHealthVector[j - 1]) == component.EntityID) - m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + j - 1); - } - //delete the player and break the loop - m_World->DeleteEntity(entity.ID); - break; - } - } - } } bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) @@ -52,16 +20,23 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) health -= e.Damage; if (health <= 0.0) { + Events::PlayerDeath ePlayerDeath; + ePlayerDeath.PlayerID = e.Player.ID; + m_EventBroker->Publish(ePlayerDeath); + m_World->DeleteEntity(e.Player.ID); } return true; } -bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e) +bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e) { - //save the changed HP to a vector. it will be taken care of in UpdateComponent - m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerHealedID, e.HealthAmount)); + ComponentWrapper cHealth = e.Player["Health"]; + double& health = cHealth["Health"]; + //NOTE: its possible to get more than MaxHealth health + health += e.HealthAmount; + return true; } diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp new file mode 100644 index 00000000..dcee3a92 --- /dev/null +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -0,0 +1,116 @@ +#include "Systems/PlayerDeathSystem.h" + +PlayerDeathSystem::PlayerDeathSystem(World* m_World, EventBroker* eventBroker) + : System(m_World, eventBroker) +{ + EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerDeathSystem::OnInputCommand); + EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerDeathSystem::OnPlayerDeath); + m_NetworkEnabled = ResourceManager::Load("Config.ini")->Get("Networking.StartNetwork", false); +} + +void PlayerDeathSystem::Update(double dt) +{ + auto playerSpawns = m_World->GetComponents("PlayerSpawn"); + if (playerSpawns == nullptr) { + return; + } + + for (auto& req : m_SpawnRequests) { + for (auto& cPlayerSpawn : *playerSpawns) { + EntityWrapper spawner(m_World, cPlayerSpawn.EntityID); + if (!spawner.HasComponent("Spawner")) { + continue; + } + + // If the spawner has a team affiliation, check it + if (spawner.HasComponent("Team")) { + if ((int)spawner["Team"]["Team"] != req.Team) { + continue; + } + } + + // Spawn the player! + EntityWrapper player = SpawnerSystem::Spawn(spawner); + // Set the player team affiliation + player["Team"]["Team"] = req.Team; + + // Publish a PlayerSpawned event + Events::PlayerSpawned e; + e.PlayerID = req.PlayerID; + e.Player = player; + e.Spawner = spawner; + m_EventBroker->Publish(e); + + } + } + m_SpawnRequests.clear(); +} + +bool PlayerDeathSystem::OnInputCommand(const Events::InputCommand& e) +{ + //testing: Jump -> playerdamage + if (e.Command != "Jump") { + return false; + } + + // 0 = released + if (e.Value != 0) { + return false; + } + + auto players = m_World->GetComponents("Player"); + + for (auto& cPlayer : *players) { + EntityWrapper player(m_World, cPlayer.EntityID); + Events::PlayerDamage e; + e.Player = player; + e.Damage = 50; + m_EventBroker->Publish(e); + } + + + + return true; +} + +bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) +{ + //// When a player is actually spawned (since the actual spawning is handled on the server) + + //// Check if a player already exists + //if (m_PlayerEntities.count(e.PlayerID) != 0) { + // // TODO: Disallow infinite respawning here + // m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); + //} + + //// Store the player for future reference + //m_PlayerEntities[e.PlayerID] = e.Player; + + //// Set the camera to the correct entity + //EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); + //if (cameraEntity.Valid()) { + // Events::SetCamera e; + // e.CameraEntity = cameraEntity; + // m_EventBroker->Publish(e); + //} + + //// HACK: Set the player model color to team color + //EntityWrapper playerModel = e.Player.FirstChildByName("PlayerModel"); + //if (playerModel.Valid() && e.Player.HasComponent("Team")) { + // ComponentWrapper cTeam = e.Player["Team"]; + // ComponentWrapper cModel = playerModel["Model"]; + // if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Red")) { + // cModel["Color"] = glm::vec3(1.f, 0.f, 0.f); + // } else if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Blue")) { + // cModel["Color"] = glm::vec3(0.f, 0.25f, 1.f); + // } + //} + + //// TODO: Set the player name to whatever + //EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); + //if (playerName.Valid()) { + // playerName["Text"]["Content"] = e.PlayerName; + //} + + return true; +} \ No newline at end of file diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 67b6ce19..6cb6c88b 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -29,7 +29,7 @@ void RayTest(std::string fileName) { Ray ray(glm::vec3(-50, 0, 0), glm::vec3(1, 0, 0)); //using a - here, else we have to init the renderingsystem + //here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); auto unitBox = ResourceManager::Load(fileName); BOOST_REQUIRE(unitBox != nullptr); diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp index bdd9ba4b..6a434a9a 100644 --- a/src/Tests/HealthSystemTest.cpp +++ b/src/Tests/HealthSystemTest.cpp @@ -69,8 +69,8 @@ GameHealthSystemTest::GameHealthSystemTest() m_EventBroker->Publish(e3); //damage player with 50 Events::PlayerDamage e; - e.DamageAmount = 50.0f; - e.PlayerDamagedID = healthsID; + e.Damage = 50.0f; +// e.PlayerDamagedID = healthsID; m_EventBroker->Publish(e); //heal some other player with 40 Events::PlayerHealthPickup e2; From 7497ff39d2e3d73ed4a3d34b8345236e575b6cf9 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 27 Jan 2016 18:00:31 +0100 Subject: [PATCH 15/38] Simon added Copy method to ComponentWrapper. Its being used in PlayerDeathSystem to copy model,animation,transform. TODO: 3rd person camera, cleanup, tests --- include/Engine/Core/ComponentWrapper.h | 5 ++ include/Engine/Core/EPlayerDeath.h | 3 +- src/Game/Systems/HealthSystem.cpp | 4 +- src/Game/Systems/PlayerDeathSystem.cpp | 69 +++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/include/Engine/Core/ComponentWrapper.h b/include/Engine/Core/ComponentWrapper.h index f09bbfe3..1ca0347d 100644 --- a/include/Engine/Core/ComponentWrapper.h +++ b/include/Engine/Core/ComponentWrapper.h @@ -43,6 +43,11 @@ struct ComponentWrapper // Specialization for string literals template void SetField(std::string name, const char(&value)[N]) { Field(name) = std::string(value); } + + void Copy(ComponentWrapper& destination) + { + memcpy(destination.Data, this->Data, Info.Stride); + } struct SubscriptProxy { diff --git a/include/Engine/Core/EPlayerDeath.h b/include/Engine/Core/EPlayerDeath.h index 00ede5ed..53dfd013 100644 --- a/include/Engine/Core/EPlayerDeath.h +++ b/include/Engine/Core/EPlayerDeath.h @@ -2,7 +2,7 @@ #define EPlayerDeath_h__ #include "EventBroker.h" -#include "../Core/Entity.h" +#include "../Core/EntityWrapper.h" namespace Events { @@ -12,6 +12,7 @@ struct PlayerDeath : Event //KilledBy,KilledByWhat is optional for now. It might be used later in the playerlog-system EntityID KilledBy; EntityID PlayerID; + EntityWrapper Player; std::string KilledByWhat; }; diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 26d38c69..102a4be6 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -22,9 +22,9 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) if (health <= 0.0) { Events::PlayerDeath ePlayerDeath; ePlayerDeath.PlayerID = e.Player.ID; + ePlayerDeath.Player = e.Player; m_EventBroker->Publish(ePlayerDeath); - - m_World->DeleteEntity(e.Player.ID); + //m_World->DeleteEntity(e.Player.ID); } return true; diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index dcee3a92..ab20dbce 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -112,5 +112,72 @@ bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) // playerName["Text"]["Content"] = e.PlayerName; //} + //m_World->DeleteEntity(e.Player.ID); + + //koppla till en kamera modell för att explodera den + + auto cameras = m_World->GetComponents("Camera"); + + for (auto& someCamera : *cameras) { + EntityWrapper entity = EntityWrapper(m_World, someCamera.EntityID); + auto cameraID = entity.ID; + auto modelID = entity.FirstChildByName("HUD"); + auto weapID = entity.FirstChildByName("Weapon").ID; + + // auto modelID = m_World->GetComponent(e.PlayerID, "Camera"); + ComponentWrapper& explosionEffect = m_World->AttachComponent(weapID, "ExplosionEffect"); + ComponentWrapper& lifeTime = m_World->AttachComponent(weapID, "Lifetime"); + (glm::vec3)explosionEffect["ExplosionOrigin"] = glm::vec3(0, 0, 0); + //explosionEffect["ExplosionOrigin"] = e.Player["Transform"]["Position"]; + (double)explosionEffect["TimeSinceDeath"] = 0.0; + (double)explosionEffect["ExplosionDuration"] = 2.0; + explosionEffect["EndColor"] = glm::vec4(0, 0, 0, 1); + (bool)explosionEffect["Randomness"] = false; + (double)explosionEffect["RandomnessScalar"] = 1.0; + (glm::vec2)explosionEffect["Velocity"] = glm::vec2(1, 1); + (bool)explosionEffect["ColorByDistance"] = false; + (bool)explosionEffect["ExponentialAccelaration"] = false; + lifeTime["Lifetime"] = 2.0; + } + + + auto t = e.Player.FirstChildByName("PlayerModel"); + auto playerEntityModel = t["Model"]; + auto playerEntityAnimation = t["Animation"]; + auto playerEntityTransform = t["Transform"]; + //auto playerEntityModel = e.Player["PlayerModel"]; +// ComponentWrapper& playerEntityModel = e.Player.FirstChildByName("PlayerModel"); + + auto newEntity = m_World->CreateEntity(); + ComponentWrapper& newEntityModel = m_World->AttachComponent(newEntity, "Model"); + ComponentWrapper& newAnimationModel = m_World->AttachComponent(newEntity, "Animation"); + ComponentWrapper& newTransformModel = m_World->AttachComponent(newEntity, "Transform"); + + //ComponentWrapper& playerEntityModel = m_World->GetComponent(e.PlayerID, "PlayerModel"); + playerEntityModel.Copy(newEntityModel); + playerEntityAnimation.Copy(newAnimationModel); + playerEntityTransform.Copy(newTransformModel); + newAnimationModel["Speed"] = (double)0.0; + auto t2 = e.Player["Transform"]["Position"]; + newTransformModel["Position"] = (glm::vec3)e.Player["Transform"]["Position"]; + newTransformModel["Scale"] = glm::vec3(5, 5, 5); + //auto tr = m_World->GetComponent(newEntityModel.EntityID, "Transform"); + //tr["Position"] = glm::vec3(0, 50, 0); + + ComponentWrapper& explosionEffect = m_World->AttachComponent(newEntityModel.EntityID, "ExplosionEffect"); + ComponentWrapper& lifeTime = m_World->AttachComponent(newEntityModel.EntityID, "Lifetime"); + (glm::vec3)explosionEffect["ExplosionOrigin"] = (glm::vec3)e.Player["Transform"]["Position"]; + //explosionEffect["ExplosionOrigin"] = e.Player["Transform"]["Position"]; + (double)explosionEffect["TimeSinceDeath"] = 0.0; + (double)explosionEffect["ExplosionDuration"] = 8.0; + explosionEffect["EndColor"] = glm::vec4(0, 0, 0, 1); + (bool)explosionEffect["Randomness"] = false; + (double)explosionEffect["RandomnessScalar"] = 1.0; + (glm::vec2)explosionEffect["Velocity"] = glm::vec2(1, 1); + (bool)explosionEffect["ColorByDistance"] = false; + (bool)explosionEffect["ExponentialAccelaration"] = false; + lifeTime["Lifetime"] = 8.0; + return true; -} \ No newline at end of file +} +//on deathanim done -> del entity From df05f1909b08b37a3e02d86e1a73730d6b2b7f5a Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Thu, 28 Jan 2016 11:43:38 +0100 Subject: [PATCH 16/38] Completed the code to trigger the 3rd person camera on death. Cleaned up PlayerDeathSystem. Fixed HealthSystemTest. Removed the test for PlayerDeathSystem --- include/Game/Systems/PlayerDeathSystem.h | 18 +-- src/Game/Systems/HealthSystem.cpp | 2 +- src/Game/Systems/PlayerDeathSystem.cpp | 191 +++++------------------ src/Tests/HealthSystemTest.cpp | 22 ++- 4 files changed, 52 insertions(+), 181 deletions(-) diff --git a/include/Game/Systems/PlayerDeathSystem.h b/include/Game/Systems/PlayerDeathSystem.h index 3f7268ca..c59aea03 100644 --- a/include/Game/Systems/PlayerDeathSystem.h +++ b/include/Game/Systems/PlayerDeathSystem.h @@ -3,15 +3,11 @@ #include "Core/System.h" #include "Input/EInputCommand.h" -#include "Systems/SpawnerSystem.h" -#include "Events/ESpawnerSpawn.h" -#include "Core/EPlayerSpawned.h" +#include "GLM.h" #include "Rendering/ESetCamera.h" #include "Core/ConfigFile.h" #include "Core/EPlayerDeath.h" -//tests -#include "Core/EPlayerDamage.h" class PlayerDeathSystem : public ImpureSystem { @@ -21,18 +17,6 @@ public: virtual void Update(double dt) override; private: - struct SpawnRequest - { - int PlayerID; - ComponentInfo::EnumType Team; - }; - - bool m_NetworkEnabled = false; - std::vector m_SpawnRequests; - std::map m_PlayerEntities; - - EventRelay m_OnInputCommand; - bool OnInputCommand(const Events::InputCommand& e); EventRelay m_OnPlayerDeath; bool OnPlayerDeath(Events::PlayerDeath& e); }; diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 102a4be6..1b747046 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -24,7 +24,7 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) ePlayerDeath.PlayerID = e.Player.ID; ePlayerDeath.Player = e.Player; m_EventBroker->Publish(ePlayerDeath); - //m_World->DeleteEntity(e.Player.ID); + //Note: we will delete the entity in PlayerDeathSystem } return true; diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index ab20dbce..7085a23f 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -3,181 +3,72 @@ PlayerDeathSystem::PlayerDeathSystem(World* m_World, EventBroker* eventBroker) : System(m_World, eventBroker) { - EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerDeathSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerDeathSystem::OnPlayerDeath); - m_NetworkEnabled = ResourceManager::Load("Config.ini")->Get("Networking.StartNetwork", false); } void PlayerDeathSystem::Update(double dt) { - auto playerSpawns = m_World->GetComponents("PlayerSpawn"); - if (playerSpawns == nullptr) { - return; - } - - for (auto& req : m_SpawnRequests) { - for (auto& cPlayerSpawn : *playerSpawns) { - EntityWrapper spawner(m_World, cPlayerSpawn.EntityID); - if (!spawner.HasComponent("Spawner")) { - continue; - } - - // If the spawner has a team affiliation, check it - if (spawner.HasComponent("Team")) { - if ((int)spawner["Team"]["Team"] != req.Team) { - continue; - } - } - - // Spawn the player! - EntityWrapper player = SpawnerSystem::Spawn(spawner); - // Set the player team affiliation - player["Team"]["Team"] = req.Team; - - // Publish a PlayerSpawned event - Events::PlayerSpawned e; - e.PlayerID = req.PlayerID; - e.Player = player; - e.Spawner = spawner; - m_EventBroker->Publish(e); - - } - } - m_SpawnRequests.clear(); -} - -bool PlayerDeathSystem::OnInputCommand(const Events::InputCommand& e) -{ - //testing: Jump -> playerdamage - if (e.Command != "Jump") { - return false; - } - - // 0 = released - if (e.Value != 0) { - return false; - } - - auto players = m_World->GetComponents("Player"); - - for (auto& cPlayer : *players) { - EntityWrapper player(m_World, cPlayer.EntityID); - Events::PlayerDamage e; - e.Player = player; - e.Damage = 50; - m_EventBroker->Publish(e); - } - - - - return true; } bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) { - //// When a player is actually spawned (since the actual spawning is handled on the server) - - //// Check if a player already exists - //if (m_PlayerEntities.count(e.PlayerID) != 0) { - // // TODO: Disallow infinite respawning here - // m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); - //} - - //// Store the player for future reference - //m_PlayerEntities[e.PlayerID] = e.Player; - - //// Set the camera to the correct entity - //EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); - //if (cameraEntity.Valid()) { - // Events::SetCamera e; - // e.CameraEntity = cameraEntity; - // m_EventBroker->Publish(e); - //} - - //// HACK: Set the player model color to team color - //EntityWrapper playerModel = e.Player.FirstChildByName("PlayerModel"); - //if (playerModel.Valid() && e.Player.HasComponent("Team")) { - // ComponentWrapper cTeam = e.Player["Team"]; - // ComponentWrapper cModel = playerModel["Model"]; - // if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Red")) { - // cModel["Color"] = glm::vec3(1.f, 0.f, 0.f); - // } else if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Blue")) { - // cModel["Color"] = glm::vec3(0.f, 0.25f, 1.f); - // } - //} - - //// TODO: Set the player name to whatever - //EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); - //if (playerName.Valid()) { - // playerName["Text"]["Content"] = e.PlayerName; - //} - - //m_World->DeleteEntity(e.Player.ID); - - //koppla till en kamera modell för att explodera den - - auto cameras = m_World->GetComponents("Camera"); - - for (auto& someCamera : *cameras) { - EntityWrapper entity = EntityWrapper(m_World, someCamera.EntityID); - auto cameraID = entity.ID; - auto modelID = entity.FirstChildByName("HUD"); - auto weapID = entity.FirstChildByName("Weapon").ID; - - // auto modelID = m_World->GetComponent(e.PlayerID, "Camera"); - ComponentWrapper& explosionEffect = m_World->AttachComponent(weapID, "ExplosionEffect"); - ComponentWrapper& lifeTime = m_World->AttachComponent(weapID, "Lifetime"); - (glm::vec3)explosionEffect["ExplosionOrigin"] = glm::vec3(0, 0, 0); - //explosionEffect["ExplosionOrigin"] = e.Player["Transform"]["Position"]; - (double)explosionEffect["TimeSinceDeath"] = 0.0; - (double)explosionEffect["ExplosionDuration"] = 2.0; - explosionEffect["EndColor"] = glm::vec4(0, 0, 0, 1); - (bool)explosionEffect["Randomness"] = false; - (double)explosionEffect["RandomnessScalar"] = 1.0; - (glm::vec2)explosionEffect["Velocity"] = glm::vec2(1, 1); - (bool)explosionEffect["ColorByDistance"] = false; - (bool)explosionEffect["ExponentialAccelaration"] = false; - lifeTime["Lifetime"] = 2.0; - } - - - auto t = e.Player.FirstChildByName("PlayerModel"); - auto playerEntityModel = t["Model"]; - auto playerEntityAnimation = t["Animation"]; - auto playerEntityTransform = t["Transform"]; - //auto playerEntityModel = e.Player["PlayerModel"]; -// ComponentWrapper& playerEntityModel = e.Player.FirstChildByName("PlayerModel"); + //current components for player that we need + auto playerModelEWrapper = e.Player.FirstChildByName("PlayerModel"); + auto playerEntityModel = playerModelEWrapper["Model"]; + auto playerEntityAnimation = playerModelEWrapper["Animation"]; + auto playerEntityTransform = playerModelEWrapper["Transform"]; + //create new entity with those components + //graphics bug: model must have an animationcomponent to be able to display it auto newEntity = m_World->CreateEntity(); ComponentWrapper& newEntityModel = m_World->AttachComponent(newEntity, "Model"); ComponentWrapper& newAnimationModel = m_World->AttachComponent(newEntity, "Animation"); ComponentWrapper& newTransformModel = m_World->AttachComponent(newEntity, "Transform"); - - //ComponentWrapper& playerEntityModel = m_World->GetComponent(e.PlayerID, "PlayerModel"); playerEntityModel.Copy(newEntityModel); playerEntityAnimation.Copy(newAnimationModel); playerEntityTransform.Copy(newTransformModel); - newAnimationModel["Speed"] = (double)0.0; - auto t2 = e.Player["Transform"]["Position"]; - newTransformModel["Position"] = (glm::vec3)e.Player["Transform"]["Position"]; - newTransformModel["Scale"] = glm::vec3(5, 5, 5); - //auto tr = m_World->GetComponent(newEntityModel.EntityID, "Transform"); - //tr["Position"] = glm::vec3(0, 50, 0); - ComponentWrapper& explosionEffect = m_World->AttachComponent(newEntityModel.EntityID, "ExplosionEffect"); - ComponentWrapper& lifeTime = m_World->AttachComponent(newEntityModel.EntityID, "Lifetime"); + //change the animation speed and make sure the explosioneffect spawns at the players position + newAnimationModel["Speed"] = (double)0.0; + newEntityModel["Color"] = glm::vec4(1, 0, 0, 1); + newTransformModel["Position"] = (glm::vec3)e.Player["Transform"]["Position"]; + newTransformModel["Scale"] = glm::vec3(1, 1, 1); + + //add the explosion with a lifetime + ComponentWrapper& explosionEffect = m_World->AttachComponent(newEntity, "ExplosionEffect"); + ComponentWrapper& lifeTime = m_World->AttachComponent(newEntity, "Lifetime"); (glm::vec3)explosionEffect["ExplosionOrigin"] = (glm::vec3)e.Player["Transform"]["Position"]; - //explosionEffect["ExplosionOrigin"] = e.Player["Transform"]["Position"]; (double)explosionEffect["TimeSinceDeath"] = 0.0; (double)explosionEffect["ExplosionDuration"] = 8.0; - explosionEffect["EndColor"] = glm::vec4(0, 0, 0, 1); + (glm::vec4)explosionEffect["EndColor"] = glm::vec4(0, 0, 0, 1); (bool)explosionEffect["Randomness"] = false; (double)explosionEffect["RandomnessScalar"] = 1.0; - (glm::vec2)explosionEffect["Velocity"] = glm::vec2(1, 1); + (glm::vec2)explosionEffect["Velocity"] = glm::vec2(0.1f, 0.1f); (bool)explosionEffect["ColorByDistance"] = false; (bool)explosionEffect["ExponentialAccelaration"] = false; - lifeTime["Lifetime"] = 8.0; + lifeTime["Lifetime"] = (double)8.0; + //create a camera (with lifetime) slightly above the player and look down at the player + auto cameraEntity = m_World->CreateEntity(); + ComponentWrapper& thirdPersonCameraLifeTime = m_World->AttachComponent(cameraEntity, "Lifetime"); + ComponentWrapper& thirdPersonCamera = m_World->AttachComponent(cameraEntity, "Camera"); + ComponentWrapper& thirdPersonCameraTransform = m_World->AttachComponent(cameraEntity, "Transform"); + auto pos = (glm::vec3)e.Player["Transform"]["Position"]; + pos.y += 10.0f; + thirdPersonCameraTransform["Position"] = (glm::vec3)pos; + //http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/ + thirdPersonCameraTransform["Orientation"] = glm::vec3(-0.7f, 1.46f, 0.8f); + thirdPersonCamera["FOV"] = 120.0; + thirdPersonCamera["NearClip"] = 0.1; + thirdPersonCamera["FarClip"] = 10000.0; + thirdPersonCameraLifeTime["Lifetime"] = (double)1.0; + + //set 3rd person camera + Events::SetCamera eSetCamera; + eSetCamera.CameraEntity = EntityWrapper(m_World, cameraEntity); + m_EventBroker->Publish(eSetCamera); + + //on deathanim done -> del entity + m_World->DeleteEntity(e.Player.ID); return true; } -//on deathanim done -> del entity diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp index 6a434a9a..280b8d14 100644 --- a/src/Tests/HealthSystemTest.cpp +++ b/src/Tests/HealthSystemTest.cpp @@ -54,36 +54,32 @@ GameHealthSystemTest::GameHealthSystemTest() //The Test //create entity which has transform,player,model,health in it. i.e. is a player EntityID playerID = m_World->CreateEntity(); - ComponentWrapper transform = m_World->AttachComponent(playerID, "Transform"); - ComponentWrapper model = m_World->AttachComponent(playerID, "Model"); - model["Resource"] = "Models/Core/UnitSphere.mesh"; // 360NoScope UnitSphere ComponentWrapper player = m_World->AttachComponent(playerID, "Player"); ComponentWrapper health = m_World->AttachComponent(playerID, "Health"); healthsID = playerID; - double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"]; + + EntityID playerID2 = m_World->CreateEntity(); + ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); + ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); //heal player with 40 Events::PlayerHealthPickup e3; e3.HealthAmount = 40.0f; - e3.PlayerHealedID = healthsID; + e3.Player = EntityWrapper(m_World, player.EntityID); m_EventBroker->Publish(e3); + //damage player with 50 Events::PlayerDamage e; e.Damage = 50.0f; -// e.PlayerDamagedID = healthsID; + e.Player = EntityWrapper(m_World, player.EntityID); m_EventBroker->Publish(e); + //heal some other player with 40 Events::PlayerHealthPickup e2; e2.HealthAmount = 40.0f; - e2.PlayerHealedID = healthsID + 1; + e2.Player = EntityWrapper(m_World, player2.EntityID); m_EventBroker->Publish(e2); - EntityID playerID2 = m_World->CreateEntity(); - ComponentWrapper transform2 = m_World->AttachComponent(playerID2, "Transform"); - ComponentWrapper model2 = m_World->AttachComponent(playerID2, "Model"); - model2["Resource"] = "Models/Core/UnitSphere.mesh"; // 360NoScope UnitSphere - ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); - ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); //END TEST } From 76274c326e66beeae725b6638fd46b1d2814e364 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 1 Feb 2016 13:34:39 +0100 Subject: [PATCH 17/38] Fixed going through flat large triangles. Velocity affected by collision. --- include/Engine/Collision/Collision.h | 2 +- .../Schema/Entities/CollisionSpawnTest.xml | 164 ++++++++++++++ resources/Schema/Entities/GameMap.xml | 1 + .../Schema/Entities/LevelCollisionTest.xml | 4 +- src/Engine/Collision/Collision.cpp | 203 +++++++++--------- src/Engine/Collision/CollisionSystem.cpp | 5 +- 6 files changed, 267 insertions(+), 112 deletions(-) create mode 100644 resources/Schema/Entities/CollisionSpawnTest.xml diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index b8625b73..dfc71496 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -66,7 +66,7 @@ bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, - const glm::vec3& boxVelocity, + glm::vec3& boxVelocity, glm::vec3& outResolutionVector); //Return true if the boxes are intersecting. diff --git a/resources/Schema/Entities/CollisionSpawnTest.xml b/resources/Schema/Entities/CollisionSpawnTest.xml new file mode 100644 index 00000000..331beae6 --- /dev/null +++ b/resources/Schema/Entities/CollisionSpawnTest.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + ../assets/Models/Test/ObstacleCourse.mesh + + + + + + + + + + + 0.46000027656555176 + + + + + + + + + + + + + ../assets/Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/GameMap.xml b/resources/Schema/Entities/GameMap.xml index 97fd3f4d..e206d31f 100644 --- a/resources/Schema/Entities/GameMap.xml +++ b/resources/Schema/Entities/GameMap.xml @@ -8,6 +8,7 @@ + Models\MapVersion1.mesh diff --git a/resources/Schema/Entities/LevelCollisionTest.xml b/resources/Schema/Entities/LevelCollisionTest.xml index ce0fe405..3c3f3bab 100644 --- a/resources/Schema/Entities/LevelCollisionTest.xml +++ b/resources/Schema/Entities/LevelCollisionTest.xml @@ -4,7 +4,7 @@ - ../assets\Models/MapVersion1.obj + ../assets/Models/MapVersion1.mesh @@ -15,7 +15,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh false diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 96d908ff..8134bab7 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -253,8 +253,10 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, const glm::vec2& boxMax, const std::array& triPos, glm::vec2& resolutionDirection, - float& resolutionDistance) + float& resolutionDistance, + bool& pushedFromTriNormal) { + pushedFromTriNormal = false; resolutionDistance = INFINITY; //Project along box normals (coordinate axes, since it's axis-aligned). for (int ax = 0; ax < 2; ++ax) { @@ -264,14 +266,20 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, minTri = std::min(t[ax], minTri); maxTri = std::max(t[ax], maxTri); } - if (minTri > boxMax[ax] || maxTri < boxMin[ax]) { + if (boxMax[ax] <= minTri || maxTri <= boxMin[ax]) { return false; } - float push = std::min(maxTri - boxMin[ax], boxMax[ax] - minTri); - if (push < resolutionDistance) { - resolutionDistance = push; + + //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 absPush = abs(push); + if (absPush < resolutionDistance) { + resolutionDistance = absPush; resolutionDirection[1 - ax] = 0.f; - resolutionDirection[ax] = resolutionDistance; + resolutionDirection[ax] = push; } } //Project along triangle normals. @@ -292,7 +300,7 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, continue; } //Rotate edge to a normal. - normal = glm::vec2(-normal.y, normal.x); + normal = glm::normalize(glm::vec2(-normal.y, normal.x)); //Project triangle onto the normal. float minTri = INFINITY; float maxTri = -INFINITY; @@ -309,14 +317,20 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, minBox = std::min(dot, minBox); maxBox = std::max(dot, maxBox); } - if (maxBox < minTri || minBox > maxTri) { + if (maxBox <= minTri || maxTri <= minBox) { return false; } + //Here: maxBox > minTri && minBox < maxTri - float push = std::min(maxTri - minBox, maxBox - minTri); - if (push < resolutionDistance) { - resolutionDistance = push; - resolutionDirection = resolutionDistance * glm::normalize(normal); + //Left is negative. + float leftRes = minTri - maxBox; + float rightRes = maxTri - minBox; + float push = rightRes < -leftRes ? rightRes : leftRes; + float absPush = abs(push); + if (absPush < resolutionDistance) { + resolutionDistance = absPush; + resolutionDirection = push * normal; + pushedFromTriNormal = true; } } return true; @@ -326,18 +340,26 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, constexpr std::array, 3> dimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); bool AABBvsTriangle(const AABB& box, - const std::array& triPos, - const glm::vec3& boxVelocity, + const std::array& triPos, + const glm::vec3& wantDirection, + glm::vec3& boxVelocity, glm::vec3& outVector) { //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, boxVelocity) > 0) { + if (!vectorHasLength(triNormal) || (glm::dot(triNormal, boxVelocity) > 0) && vectorHasLength(boxVelocity)) { return false; } + enum BoxTriResolveCase + { + Vertex, + Line, + Corner + } resolveCase; + const glm::vec3& origin = box.Origin(); const glm::vec3& half = box.HalfSize(); const glm::vec3& min = box.MinCorner(); @@ -358,14 +380,16 @@ bool AABBvsTriangle(const AABB& box, 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)) { + if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) { return false; } else if (resolutionDist < minimumTranslation) { outVector = glm::vec3(0.f); outVector[dim.first] = resolutionVector.x; outVector[dim.second] = resolutionVector.y; minimumTranslation = resolutionDist; + resolveCase = pushedFromTriangleLine ? Line : Vertex; } } @@ -384,6 +408,56 @@ bool AABBvsTriangle(const AABB& box, glm::vec3 cornerResolution = (1+t) * diagonal; if (glm::length(cornerResolution) < minimumTranslation) { outVector = cornerResolution; + resolveCase = Corner; + } + + bool groundCollision = triNormal.y > 0.5f; + //ImGui::Text(groundCollision ? "Ground" : "Slope"); + + glm::vec3 projNorm; + switch (resolveCase) { + case Vertex: + { + int maxD = 0; + float maxResolution = 0.f; + for (int d = 0; d < 3; ++d) { + float resolve = glm::abs(outVector[d]); + if (resolve > maxResolution) { + maxResolution = resolve; + maxD = d; + } + } + boxVelocity[maxD] = 0.f; + return true; + } + case Line: + projNorm = glm::normalize(outVector); + break; + case Corner: + projNorm = triNormal; + break; + default: + break; + } + + //Project the velocity onto the normal of the hit line/face. + //w = v - *n, |n|==1. + boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + if (groundCollision) { + float len = glm::length2(boxVelocity); + if (len > 0.0001f) { + len = glm::sqrt(len); + boxVelocity = len * glm::normalize(wantDirection); + } + + len = glm::length(outVector); + float ang = glm::half_pi() - glm::acos(outVector.y / len); + if (len > 0.0000001f && ang > 0.0000001f) { + //ImGui::Text("ang=%f, len=%f, acos=%f, outY=%f", ang, len, glm::acos(outVector.y / len), outVector.y); + outVector.x = 0; + outVector.y = len / glm::sin(ang); + outVector.z = 0; + } } return true; } @@ -392,15 +466,16 @@ bool AABBvsTriangles(const AABB& box, const std::vector& modelVertices, const std::vector& modelIndices, const glm::mat4& modelMatrix, - const glm::vec3& boxVelocity, - glm::vec3& outResolutionVector, - int startIndex, - int recursiveDepth) + glm::vec3& boxVelocity, + glm::vec3& outResolutionVector) { bool hit = false; AABB newBox = box; outResolutionVector = glm::vec3(0.f); + glm::vec3 wantDirection(boxVelocity); + wantDirection.y = 0; + wantDirection = glm::normalize(wantDirection); for (int i = 0; i < modelIndices.size(); ) { std::array triVertices = { Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix), @@ -408,101 +483,15 @@ bool AABBvsTriangles(const AABB& box, Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix) }; glm::vec3 outVec; - if (AABBvsTriangle(newBox, triVertices, boxVelocity, outVec)) { + if (AABBvsTriangle(newBox, triVertices, wantDirection, boxVelocity, outVec)) { hit = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); } } - //outResolutionVector = glm::vec3(INFINITY); - //if (recursiveDepth > 2) { - // return true; - //} - //for (int i = startIndex; i < modelIndices.size(); ) { - // std::array 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; - // if (AABBvsTriangle(box, triVertices, boxVelocity, outVec) && glm::length2(outVec) < glm::length2(outResolutionVector)) { - // hit = true; - // glm::vec3 potentialResolution = outVec; - // const AABB& resolvedBox = AABB::FromOriginSize(box.Origin() + potentialResolution, box.Size()); - // if (AABBvsTriangles(resolvedBox, modelVertices, modelIndices, modelMatrix, boxVelocity, outVec, i, recursiveDepth+1)) { - // potentialResolution += outVec; - // if (glm::length2(potentialResolution) > glm::length2(outResolutionVector)) { - // continue; - // } - // } - // outResolutionVector = potentialResolution; - // } - //} - - //outResolutionVector = glm::vec3(INFINITY); - //std::stack testBoxes; - //std::stack resolveIndices; - //std::stack resolveVectors; - //resolveVectors.push(glm::vec3(0.f)); - //resolveIndices.push(0); - //testBoxes.push(box); - //do { - // int i = resolveIndices.top(); - // resolveIndices.pop(); - // while (i < modelIndices.size()) { - // std::array 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 test = AABBvsTriangle(testBoxes.top(), triVertices, outVec); - // if (test) { - // hit = true; - // //emplace? - // resolveIndices.push(i); - // testBoxes.push(AABB::FromOriginSize(box.Origin() + outVec, box.Size())); - // resolveVectors.top() += outVec; - // } - // } - // testBoxes.pop(); - // resolveVectors.push(glm::vec3(0.f)); - //} while (!resolveIndices.empty()); - //if (hit) { - // if (!resolveVectors.empty()) { - // while (!resolveVectors.empty()) { - // if (glm::length2(resolveVectors.top()) < glm::length2(outResolutionVector)) { - // outResolutionVector = resolveVectors.top(); - // } - // resolveVectors.pop(); - // } - // } else { - // //TODO: This won't happen. - // ImGui::Text("Collision, but not resolved."); - // } - // return true; - //} return hit; } -bool AABBvsTriangles(const AABB& box, - const std::vector& modelVertices, - const std::vector& modelIndices, - const glm::mat4& modelMatrix, - const glm::vec3& boxVelocity, - glm::vec3& outResolutionVector) -{ - return AABBvsTriangles( - box, - modelVertices, - modelIndices, - modelMatrix, - boxVelocity, - outResolutionVector, - 0, - 0 - ); -} bool IsSameBoxProbably(const AABB& first, const AABB& second, const float epsilon) { diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 8d43534c..81e76410 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -54,9 +54,10 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c glm::mat4 modelMatrix = glm::translate(absPosition) * glm::toMat4(absOrientation) * glm::scale(absScale); glm::vec3 resolutionVector; - if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, (glm::vec3&)cPhysics["Velocity"], resolutionVector)) { + glm::vec3 newVelocity = (glm::vec3)cPhysics["Velocity"]; + if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, newVelocity, resolutionVector)) { (glm::vec3&)cTransform["Position"] += resolutionVector; - cPhysics["Velocity"] = glm::vec3(0, 0, 0); + cPhysics["Velocity"] = newVelocity; } } } From 3cfbbbbf54c0365bb0244d1f7289970cea4efddb Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 1 Feb 2016 16:13:48 +0100 Subject: [PATCH 18/38] CollisionSystem now utilizes Octree for collision detection. --- resources/Schema/Components/AABB.xsd | 4 +-- resources/Schema/Entities/GameMap.xml | 3 ++ src/Engine/Collision/CollisionSystem.cpp | 44 +++++++++++------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/resources/Schema/Components/AABB.xsd b/resources/Schema/Components/AABB.xsd index daa633a6..7dec510f 100644 --- a/resources/Schema/Components/AABB.xsd +++ b/resources/Schema/Components/AABB.xsd @@ -7,10 +7,10 @@ - Middle point of the bounding box + Middle point of the bounding box, in model space - Size of the bounding box + Size of the bounding box, in model space diff --git a/resources/Schema/Entities/GameMap.xml b/resources/Schema/Entities/GameMap.xml index e206d31f..4d0a2716 100644 --- a/resources/Schema/Entities/GameMap.xml +++ b/resources/Schema/Entities/GameMap.xml @@ -8,6 +8,9 @@ + + + Models\MapVersion1.mesh diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 382238e7..846d38a7 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -1,6 +1,7 @@ #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) { @@ -25,33 +26,28 @@ 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(boxB.Entity["Model"]["Resource"]); + } catch (const std::exception&) { + continue; + } + + glm::mat4 modelMatrix = Transform::ModelMatrix(boxB.Entity); + + glm::vec3 newVelocity = (glm::vec3)cPhysics["Velocity"]; + if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, newVelocity, resolutionVector)) { + (glm::vec3&)cTransform["Position"] += resolutionVector; + cPhysics["Velocity"] = newVelocity; + } + } else if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { + //Enter here if boxB has no Model. (glm::vec3&)cTransform["Position"] += resolutionVector; if (resolutionVector.y > 0) { ((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; - // } - - // 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(cModel["Resource"]); - // glm::vec3 resolutionVector; - // if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, resolutionVector)) { - // (glm::vec3&)cTransform["Position"] += resolutionVector; - // } - //} -} \ No newline at end of file +} From 5a45f778e68498933486e77a5b2108170318938d Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 1 Feb 2016 17:01:58 +0100 Subject: [PATCH 19/38] Fixed so players don't teleport to (NaN, NaN, NaN) on spawning. --- include/Engine/Core/MemoryPool.h | 2 +- src/Engine/Collision/Collision.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/Engine/Core/MemoryPool.h b/include/Engine/Core/MemoryPool.h index 3a1cc069..f4073294 100644 --- a/include/Engine/Core/MemoryPool.h +++ b/include/Engine/Core/MemoryPool.h @@ -102,7 +102,7 @@ public: m_ExtraMemory.push_back((char*)malloc(m_Stride)); //We should preferably not enter here to avoid performance issues. Set more numMaxElements in constructor instead. if (!DisableMemoryPool::Value) { - LOG_WARNING("Allocated slots exceed Pool size, extra memory allocated dynamically. Pool size: %u, dynamic size: %u.", m_NumSlots, m_ExtraMemory.size()); + LOG_DEBUG("Allocated slots exceed Pool size, extra memory allocated dynamically. Pool size: %u, dynamic size: %u.", m_NumSlots, m_ExtraMemory.size()); } return m_ExtraMemory.back(); } diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 67ed6c99..a17e347c 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -412,7 +412,6 @@ bool AABBvsTriangle(const AABB& box, } bool groundCollision = triNormal.y > 0.5f; - //ImGui::Text(groundCollision ? "Ground" : "Slope"); glm::vec3 projNorm; switch (resolveCase) { @@ -446,14 +445,12 @@ bool AABBvsTriangle(const AABB& box, if (groundCollision) { float len = glm::length2(boxVelocity); if (len > 0.0001f) { - len = glm::sqrt(len); - boxVelocity = len * glm::normalize(wantDirection); + boxVelocity = glm::sqrt(len) * wantDirection; } len = glm::length(outVector); float ang = glm::half_pi() - glm::acos(outVector.y / len); if (len > 0.0000001f && ang > 0.0000001f) { - //ImGui::Text("ang=%f, len=%f, acos=%f, outY=%f", ang, len, glm::acos(outVector.y / len), outVector.y); outVector.x = 0; outVector.y = len / glm::sin(ang); outVector.z = 0; @@ -475,7 +472,9 @@ bool AABBvsTriangles(const AABB& box, outResolutionVector = glm::vec3(0.f); glm::vec3 wantDirection(boxVelocity); wantDirection.y = 0; - wantDirection = glm::normalize(wantDirection); + if (vectorHasLength(wantDirection)) { + wantDirection = glm::normalize(wantDirection); + } for (int i = 0; i < modelIndices.size(); ) { std::array triVertices = { Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix), From 30b1dab7640406c180de7f2ce5ef6c53de0319b4 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 1 Feb 2016 18:06:28 +0100 Subject: [PATCH 20/38] More efficient collision resolution case + comments. --- src/Engine/Collision/Collision.cpp | 43 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index a17e347c..edb6c9a9 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -336,6 +336,11 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, return true; } +constexpr float SlopeConstant(float degrees) +{ + return (1.0 - degrees / 90.f); +} + //An array containing 3 int pairs { 0, 2 }, { 0, 1 }, { 1, 2 } constexpr std::array, 3> dimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); @@ -355,9 +360,11 @@ bool AABBvsTriangle(const AABB& box, enum BoxTriResolveCase { - Vertex, - Line, - Corner + ResolveDimX, + ResolveDimY, + ResolveDimZ, + Line, //Box edge colliding with triangle line. + Corner //Box corner colliding with the triangle face. } resolveCase; const glm::vec3& origin = box.Origin(); @@ -389,7 +396,9 @@ bool AABBvsTriangle(const AABB& box, outVector[dim.first] = resolutionVector.x; outVector[dim.second] = resolutionVector.y; minimumTranslation = resolutionDist; - resolveCase = pushedFromTriangleLine ? Line : Vertex; + //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). + resolveCase = pushedFromTriangleLine ? Line : static_cast((abs(outVector[dim.first]) < 0.0001f) ? dim.second : dim.first); } } @@ -411,22 +420,15 @@ bool AABBvsTriangle(const AABB& box, resolveCase = Corner; } - bool groundCollision = triNormal.y > 0.5f; - glm::vec3 projNorm; switch (resolveCase) { - case Vertex: + case ResolveDimX: + case ResolveDimY: + case ResolveDimZ: { - int maxD = 0; - float maxResolution = 0.f; - for (int d = 0; d < 3; ++d) { - float resolve = glm::abs(outVector[d]); - if (resolve > maxResolution) { - maxResolution = resolve; - maxD = d; - } - } - boxVelocity[maxD] = 0.f; + //If we get here, the resolution is along one coordinate axis. + //set velocity to 0 in that dimension. + boxVelocity[resolveCase] = 0.f; return true; } case Line: @@ -442,12 +444,17 @@ bool AABBvsTriangle(const AABB& box, //Project the velocity onto the normal of the hit line/face. //w = v - *n, |n|==1. boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; - if (groundCollision) { + //If the collision was not on steep wall or similarly (e.g. walking on the ground), do special treatment. + //Magic value that makes condition correspond to: + //if the angle between horizon and the collision surface is less than 45 degrees. + if (projNorm.y > SlopeConstant(45.0f)) { + //Make sure the player keeps moving in their desired direction, just slower. float len = glm::length2(boxVelocity); if (len > 0.0001f) { boxVelocity = glm::sqrt(len) * wantDirection; } + //Ensure that the player always is moved upwards, instead of sliding down. len = glm::length(outVector); float ang = glm::half_pi() - glm::acos(outVector.y / len); if (len > 0.0000001f && ang > 0.0000001f) { From 563fd4082e090cb46e5eebc23095a2c86154e323 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Tue, 2 Feb 2016 12:10:39 +0100 Subject: [PATCH 21/38] Updated movement test for model collisions --- resources/Schema/Entities/MovementTest.xml | 67 ++-------------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index 1378f623..f5197830 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -6,22 +6,6 @@ - - - - - - Models/Core/UnitCube.mesh - - false - - - - - - - - @@ -81,6 +65,10 @@ + + + + Models/Test/ObstacleCourse.mesh @@ -88,51 +76,6 @@ - - - - - - Models/Core/UnitCube.mesh - false - - - - - - - - - - - - - - Models/Core/UnitCube.mesh - false - - - - - - - - - - - - - - Models/Core/UnitCube.mesh - false - - - - - - - - @@ -197,7 +140,7 @@ - + From 5e472cd69c9a24bc090211c7c15daf10ea6ee0bc Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Tue, 2 Feb 2016 17:18:12 +0100 Subject: [PATCH 22/38] DeathExplosion is now an XML file, changed PlayerDeathSystem accordingly. The effect should show as soon as the explosioneffect-fix is merged into master. TODO: remove the test --- include/Engine/Core/EPlayerDeath.h | 2 - include/Game/Systems/PlayerDeathSystem.h | 8 ++ .../PlayerDeathExplosionWithCamera.xml | 48 +++++++++ src/Game/Systems/HealthSystem.cpp | 1 - src/Game/Systems/PlayerDeathSystem.cpp | 102 +++++++++--------- src/Tests/CapturePointTest.cpp | 8 +- 6 files changed, 114 insertions(+), 55 deletions(-) create mode 100644 resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml diff --git a/include/Engine/Core/EPlayerDeath.h b/include/Engine/Core/EPlayerDeath.h index 53dfd013..363745a6 100644 --- a/include/Engine/Core/EPlayerDeath.h +++ b/include/Engine/Core/EPlayerDeath.h @@ -10,8 +10,6 @@ namespace Events struct PlayerDeath : Event { //KilledBy,KilledByWhat is optional for now. It might be used later in the playerlog-system - EntityID KilledBy; - EntityID PlayerID; EntityWrapper Player; std::string KilledByWhat; }; diff --git a/include/Game/Systems/PlayerDeathSystem.h b/include/Game/Systems/PlayerDeathSystem.h index c59aea03..4734947e 100644 --- a/include/Game/Systems/PlayerDeathSystem.h +++ b/include/Game/Systems/PlayerDeathSystem.h @@ -7,7 +7,11 @@ #include "Rendering/ESetCamera.h" #include "Core/ConfigFile.h" +#include "Core/EntityFile.h" +#include "Core/EntityFileParser.h" + #include "Core/EPlayerDeath.h" +#include "Core/EPlayerDamage.h" class PlayerDeathSystem : public ImpureSystem { @@ -19,5 +23,9 @@ public: private: EventRelay m_OnPlayerDeath; bool OnPlayerDeath(Events::PlayerDeath& e); + + EventRelay m_OnInputCommand; + bool OnInputCommand(const Events::InputCommand& e); + }; #endif \ No newline at end of file diff --git a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml new file mode 100644 index 00000000..bdebbfe7 --- /dev/null +++ b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml @@ -0,0 +1,48 @@ + + + + + + Hold Pos + + 0 + + + 8 + + + 0 + 8 + + + + Models/AssaultAnimated.mesh + + true + + + + + + + + + + + 1 + + + 120 + 0.1 + 10000 + + + + + + + + + + + diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 1b747046..18a15828 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -21,7 +21,6 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) if (health <= 0.0) { Events::PlayerDeath ePlayerDeath; - ePlayerDeath.PlayerID = e.Player.ID; ePlayerDeath.Player = e.Player; m_EventBroker->Publish(ePlayerDeath); //Note: we will delete the entity in PlayerDeathSystem diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index 7085a23f..d57caefc 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -3,69 +3,75 @@ PlayerDeathSystem::PlayerDeathSystem(World* m_World, EventBroker* eventBroker) : System(m_World, eventBroker) { + EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerDeathSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerDeathSystem::OnPlayerDeath); } +bool PlayerDeathSystem::OnInputCommand(const Events::InputCommand& e) +{ + //testing: Jump > playerdamage + if (e.Command != "Jump") { + return false; + + } + + // 0 = released + if (e.Value != 0) { + return false; + + } + + auto players = m_World->GetComponents("Player"); + + for (auto& cPlayer : *players) { + EntityWrapper player(m_World, cPlayer.EntityID); + Events::PlayerDamage e; + e.Player = player; + e.Damage = 50; + m_EventBroker->Publish(e); + + } + + + + return true; +} + void PlayerDeathSystem::Update(double dt) { } bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) { - //current components for player that we need - auto playerModelEWrapper = e.Player.FirstChildByName("PlayerModel"); - auto playerEntityModel = playerModelEWrapper["Model"]; - auto playerEntityAnimation = playerModelEWrapper["Animation"]; - auto playerEntityTransform = playerModelEWrapper["Transform"]; + //LOAD THE XML + auto deathEffect = ResourceManager::Load("Schema/Entities/PlayerDeathExplosionWithCamera.xml"); - //create new entity with those components - //graphics bug: model must have an animationcomponent to be able to display it - auto newEntity = m_World->CreateEntity(); - ComponentWrapper& newEntityModel = m_World->AttachComponent(newEntity, "Model"); - ComponentWrapper& newAnimationModel = m_World->AttachComponent(newEntity, "Animation"); - ComponentWrapper& newTransformModel = m_World->AttachComponent(newEntity, "Transform"); - playerEntityModel.Copy(newEntityModel); - playerEntityAnimation.Copy(newAnimationModel); - playerEntityTransform.Copy(newTransformModel); + EntityFileParser parser(deathEffect); + EntityID deathEffectID = parser.MergeEntities(m_World); + EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID); + + //current components for player that we need + auto playerModelEW = e.Player.FirstChildByName("PlayerModel"); + auto playerEntityModel = playerModelEW["Model"]; + auto playerEntityTransform = playerModelEW["Transform"]; + + //copy the data from player to new playermodel + playerEntityModel.Copy(deathEffectEW["Model"]); + playerEntityTransform.Copy(deathEffectEW["Transform"]); //change the animation speed and make sure the explosioneffect spawns at the players position - newAnimationModel["Speed"] = (double)0.0; - newEntityModel["Color"] = glm::vec4(1, 0, 0, 1); - newTransformModel["Position"] = (glm::vec3)e.Player["Transform"]["Position"]; - newTransformModel["Scale"] = glm::vec3(1, 1, 1); - - //add the explosion with a lifetime - ComponentWrapper& explosionEffect = m_World->AttachComponent(newEntity, "ExplosionEffect"); - ComponentWrapper& lifeTime = m_World->AttachComponent(newEntity, "Lifetime"); - (glm::vec3)explosionEffect["ExplosionOrigin"] = (glm::vec3)e.Player["Transform"]["Position"]; - (double)explosionEffect["TimeSinceDeath"] = 0.0; - (double)explosionEffect["ExplosionDuration"] = 8.0; - (glm::vec4)explosionEffect["EndColor"] = glm::vec4(0, 0, 0, 1); - (bool)explosionEffect["Randomness"] = false; - (double)explosionEffect["RandomnessScalar"] = 1.0; - (glm::vec2)explosionEffect["Velocity"] = glm::vec2(0.1f, 0.1f); - (bool)explosionEffect["ColorByDistance"] = false; - (bool)explosionEffect["ExponentialAccelaration"] = false; - lifeTime["Lifetime"] = (double)8.0; - - //create a camera (with lifetime) slightly above the player and look down at the player - auto cameraEntity = m_World->CreateEntity(); - ComponentWrapper& thirdPersonCameraLifeTime = m_World->AttachComponent(cameraEntity, "Lifetime"); - ComponentWrapper& thirdPersonCamera = m_World->AttachComponent(cameraEntity, "Camera"); - ComponentWrapper& thirdPersonCameraTransform = m_World->AttachComponent(cameraEntity, "Transform"); - auto pos = (glm::vec3)e.Player["Transform"]["Position"]; - pos.y += 10.0f; - thirdPersonCameraTransform["Position"] = (glm::vec3)pos; //http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/ - thirdPersonCameraTransform["Orientation"] = glm::vec3(-0.7f, 1.46f, 0.8f); - thirdPersonCamera["FOV"] = 120.0; - thirdPersonCamera["NearClip"] = 0.1; - thirdPersonCamera["FarClip"] = 10000.0; - thirdPersonCameraLifeTime["Lifetime"] = (double)1.0; + auto playerPosition = (glm::vec3)e.Player["Transform"]["Position"]; + deathEffectEW["Transform"]["Position"] = playerPosition; + deathEffectEW["ExplosionEffect"]["ExplosionOrigin"] = playerPosition; - //set 3rd person camera + //camera (with lifetime) slightly above the player and looking down at the player + //camera will be positioned just above the player + glm::vec3 cameraPosition = glm::vec3(0, 10, 0); + auto cam = deathEffectEW.FirstChildByName("Camera"); + cam["Transform"]["Position"] = cameraPosition; Events::SetCamera eSetCamera; - eSetCamera.CameraEntity = EntityWrapper(m_World, cameraEntity); + eSetCamera.CameraEntity = cam; m_EventBroker->Publish(eSetCamera); //on deathanim done -> del entity diff --git a/src/Tests/CapturePointTest.cpp b/src/Tests/CapturePointTest.cpp index ffb01030..2c7bf430 100644 --- a/src/Tests/CapturePointTest.cpp +++ b/src/Tests/CapturePointTest.cpp @@ -255,14 +255,14 @@ void CapturePointTest::TestSetup8() } void CapturePointTest::DoTouchEvent(EntityID whoDidSomething, EntityID onWhatObject) { Events::TriggerTouch touchEvent; - touchEvent.Entity = whoDidSomething; - touchEvent.Trigger = onWhatObject; + touchEvent.Entity = EntityWrapper(m_World, whoDidSomething); + touchEvent.Trigger = EntityWrapper(m_World, onWhatObject); m_EventBroker->Publish(touchEvent); } void CapturePointTest::DoLeaveEvent(EntityID whoDidSomething, EntityID onWhatObject) { Events::TriggerLeave leaveEvent; - leaveEvent.Entity = whoDidSomething; - leaveEvent.Trigger = onWhatObject; + leaveEvent.Entity = EntityWrapper(m_World, whoDidSomething); + leaveEvent.Trigger = EntityWrapper(m_World, onWhatObject); m_EventBroker->Publish(leaveEvent); } void CapturePointTest::TestSuccess1() { From affcc5305bc032b27eb3e50ac222144a0820bfc3 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 2 Feb 2016 17:19:59 +0100 Subject: [PATCH 23/38] Collision affects velocity correctly, no wierd ice cream sliding along walls. --- src/Engine/Collision/Collision.cpp | 48 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index edb6c9a9..81949cb5 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -344,9 +344,10 @@ constexpr float SlopeConstant(float degrees) //An array containing 3 int pairs { 0, 2 }, { 0, 1 }, { 1, 2 } constexpr std::array, 3> dimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); +//TODO: Prefer to move in y - if the resolution is small enough, value in physics comp. +//TODO: Walking down slopes correctly. bool AABBvsTriangle(const AABB& box, const std::array& triPos, - const glm::vec3& wantDirection, glm::vec3& boxVelocity, glm::vec3& outVector) { @@ -354,7 +355,7 @@ bool AABBvsTriangle(const AABB& box, //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, boxVelocity) > 0) && vectorHasLength(boxVelocity)) { + if (!vectorHasLength(triNormal) || (glm::dot(triNormal, boxVelocity) > 0)) { return false; } @@ -428,40 +429,56 @@ bool AABBvsTriangle(const AABB& box, { //If we get here, the resolution is along one coordinate axis. //set velocity to 0 in that dimension. + //projNorm = glm::vec3(0.f); + //projNorm[resolveCase] = 1; + //ImGui::Text(("Axis " + std::string(projNorm.y > SlopeConstant(45.0f) ? "Ground" : "Slope") + "collision proj=(%f,%f,%f)").c_str(), projNorm.x, projNorm.y, projNorm.z); boxVelocity[resolveCase] = 0.f; return true; } case Line: projNorm = glm::normalize(outVector); + //ImGui::Text(("Line " + std::string(projNorm.y > SlopeConstant(45.0f) ? "Ground" : "Slope") + "collision proj=(%f,%f,%f)").c_str(), projNorm.x, projNorm.y, projNorm.z); break; case Corner: projNorm = triNormal; + //ImGui::Text(("Corner " + std::string(projNorm.y > SlopeConstant(45.0f) ? "Ground" : "Slope") + "collision proj=(%f,%f,%f)").c_str(), projNorm.x, projNorm.y, projNorm.z); break; default: break; } - //Project the velocity onto the normal of the hit line/face. - //w = v - *n, |n|==1. - boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; //If the collision was not on steep wall or similarly (e.g. walking on the ground), do special treatment. //Magic value that makes condition correspond to: //if the angle between horizon and the collision surface is less than 45 degrees. if (projNorm.y > SlopeConstant(45.0f)) { - //Make sure the player keeps moving in their desired direction, just slower. - float len = glm::length2(boxVelocity); - if (len > 0.0001f) { - boxVelocity = glm::sqrt(len) * wantDirection; - } - //Ensure that the player always is moved upwards, instead of sliding down. - len = glm::length(outVector); + //Also zero the vertical velocity. + float len = glm::length(outVector); float ang = glm::half_pi() - glm::acos(outVector.y / len); if (len > 0.0000001f && ang > 0.0000001f) { outVector.x = 0; outVector.y = len / glm::sin(ang); outVector.z = 0; + boxVelocity.y = 0.f; } + } else if (projNorm.y > 0) { + //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 - *n, |n|==1. + boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + + //Ensure that the player will be pushed in the xz-plane. + //glm::vec3 moveDir(outVector); + //moveDir.y = 0; + //if (vectorHasLength(moveDir)) { + // moveDir = glm::normalize(moveDir); + // float len = glm::length(outVector); + // float dotMoveOut = moveDir.x * outVector.x + moveDir.z * outVector.z; + // float ang = glm::half_pi() - glm::acos(dotMoveOut / len); + // if (len > 0.0000001f && ang > 0.0000001f) { + // outVector = (len / glm::sin(ang)) * moveDir; + // } + //} } return true; } @@ -477,11 +494,6 @@ bool AABBvsTriangles(const AABB& box, AABB newBox = box; outResolutionVector = glm::vec3(0.f); - glm::vec3 wantDirection(boxVelocity); - wantDirection.y = 0; - if (vectorHasLength(wantDirection)) { - wantDirection = glm::normalize(wantDirection); - } for (int i = 0; i < modelIndices.size(); ) { std::array triVertices = { Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix), @@ -489,7 +501,7 @@ bool AABBvsTriangles(const AABB& box, Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix) }; glm::vec3 outVec; - if (AABBvsTriangle(newBox, triVertices, wantDirection, boxVelocity, outVec)) { + if (AABBvsTriangle(newBox, triVertices, boxVelocity, outVec)) { hit = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); From 214dad2afc23be62a5fee10e71374667daa32830 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 2 Feb 2016 17:25:59 +0100 Subject: [PATCH 24/38] Removed commented code. --- src/Engine/Collision/Collision.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 81949cb5..083af229 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -429,19 +429,14 @@ bool AABBvsTriangle(const AABB& box, { //If we get here, the resolution is along one coordinate axis. //set velocity to 0 in that dimension. - //projNorm = glm::vec3(0.f); - //projNorm[resolveCase] = 1; - //ImGui::Text(("Axis " + std::string(projNorm.y > SlopeConstant(45.0f) ? "Ground" : "Slope") + "collision proj=(%f,%f,%f)").c_str(), projNorm.x, projNorm.y, projNorm.z); boxVelocity[resolveCase] = 0.f; return true; } case Line: projNorm = glm::normalize(outVector); - //ImGui::Text(("Line " + std::string(projNorm.y > SlopeConstant(45.0f) ? "Ground" : "Slope") + "collision proj=(%f,%f,%f)").c_str(), projNorm.x, projNorm.y, projNorm.z); break; case Corner: projNorm = triNormal; - //ImGui::Text(("Corner " + std::string(projNorm.y > SlopeConstant(45.0f) ? "Ground" : "Slope") + "collision proj=(%f,%f,%f)").c_str(), projNorm.x, projNorm.y, projNorm.z); break; default: break; @@ -466,19 +461,6 @@ bool AABBvsTriangle(const AABB& box, //Project the velocity onto the normal of the hit line/face. //w = v - *n, |n|==1. boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; - - //Ensure that the player will be pushed in the xz-plane. - //glm::vec3 moveDir(outVector); - //moveDir.y = 0; - //if (vectorHasLength(moveDir)) { - // moveDir = glm::normalize(moveDir); - // float len = glm::length(outVector); - // float dotMoveOut = moveDir.x * outVector.x + moveDir.z * outVector.z; - // float ang = glm::half_pi() - glm::acos(dotMoveOut / len); - // if (len > 0.0000001f && ang > 0.0000001f) { - // outVector = (len / glm::sin(ang)) * moveDir; - // } - //} } return true; } From 2961f198b3e71546405343e34f7fa0e4a7175b4c Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Wed, 3 Feb 2016 11:48:22 +0100 Subject: [PATCH 25/38] PlayerDeathSystem is now working well. Changed so the model is oriented correctly. Changed so camera is behind the player. Copied the current animation and froze it. --- .../PlayerDeathExplosionWithCamera.xml | 5 ++- src/Game/Systems/PlayerDeathSystem.cpp | 37 ++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml index bdebbfe7..ff33476d 100644 --- a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml +++ b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml @@ -8,12 +8,13 @@ 0 - 8 + 4 0 - 8 + 4 + Models/AssaultAnimated.mesh diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index d57caefc..d6680928 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -43,38 +43,39 @@ void PlayerDeathSystem::Update(double dt) bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) { - //LOAD THE XML + //load the explosioneffect XML auto deathEffect = ResourceManager::Load("Schema/Entities/PlayerDeathExplosionWithCamera.xml"); EntityFileParser parser(deathEffect); EntityID deathEffectID = parser.MergeEntities(m_World); EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID); - - //current components for player that we need - auto playerModelEW = e.Player.FirstChildByName("PlayerModel"); - auto playerEntityModel = playerModelEW["Model"]; - auto playerEntityTransform = playerModelEW["Transform"]; - //copy the data from player to new playermodel + //components that we need from player + auto playerCamera = e.Player.FirstChildByName("Camera"); + auto playerEntityModel = e.Player.FirstChildByName("PlayerModel")["Model"]; + auto playerEntityAnimation = e.Player.FirstChildByName("PlayerModel")["Animation"]; + + //copy the data from player to explisioneffectmodel playerEntityModel.Copy(deathEffectEW["Model"]); - playerEntityTransform.Copy(deathEffectEW["Transform"]); + playerEntityAnimation.Copy(deathEffectEW["Animation"]); + //freeze the animation + deathEffectEW["Animation"]["Speed"] = 0.0; - //change the animation speed and make sure the explosioneffect spawns at the players position - //http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/ - auto playerPosition = (glm::vec3)e.Player["Transform"]["Position"]; - deathEffectEW["Transform"]["Position"] = playerPosition; - deathEffectEW["ExplosionEffect"]["ExplosionOrigin"] = playerPosition; + //copy the models position,orientation + deathEffectEW["Transform"]["Position"] = (glm::vec3)e.Player["Transform"]["Position"]; + deathEffectEW["Transform"]["Orientation"] = (glm::vec3)e.Player["Transform"]["Orientation"]; + //effect,camera is relative to playersPosition + deathEffectEW["ExplosionEffect"]["ExplosionOrigin"] = glm::vec3(0, 0, 0); - //camera (with lifetime) slightly above the player and looking down at the player - //camera will be positioned just above the player - glm::vec3 cameraPosition = glm::vec3(0, 10, 0); + //camera (with lifetime) behind the player auto cam = deathEffectEW.FirstChildByName("Camera"); - cam["Transform"]["Position"] = cameraPosition; + cam["Transform"]["Position"] = glm::vec3(0, 2.5f, 1.8f); + cam["Transform"]["Orientation"] = glm::vec3(5.655f, 0, 0); Events::SetCamera eSetCamera; eSetCamera.CameraEntity = cam; m_EventBroker->Publish(eSetCamera); - //on deathanim done -> del entity + //done -> del entity m_World->DeleteEntity(e.Player.ID); return true; } From 0bbaf1d57678e6e7745d4fe55466b3330a2f2afe Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Thu, 4 Feb 2016 10:04:44 +0100 Subject: [PATCH 26/38] Oops! Removed DebugCode... --- include/Game/Systems/PlayerDeathSystem.h | 5 ---- src/Game/Systems/PlayerDeathSystem.cpp | 31 ------------------------ 2 files changed, 36 deletions(-) diff --git a/include/Game/Systems/PlayerDeathSystem.h b/include/Game/Systems/PlayerDeathSystem.h index 4734947e..7d989dbf 100644 --- a/include/Game/Systems/PlayerDeathSystem.h +++ b/include/Game/Systems/PlayerDeathSystem.h @@ -11,7 +11,6 @@ #include "Core/EntityFileParser.h" #include "Core/EPlayerDeath.h" -#include "Core/EPlayerDamage.h" class PlayerDeathSystem : public ImpureSystem { @@ -23,9 +22,5 @@ public: private: EventRelay m_OnPlayerDeath; bool OnPlayerDeath(Events::PlayerDeath& e); - - EventRelay m_OnInputCommand; - bool OnInputCommand(const Events::InputCommand& e); - }; #endif \ No newline at end of file diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index d6680928..f7fe1ab5 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -3,40 +3,9 @@ PlayerDeathSystem::PlayerDeathSystem(World* m_World, EventBroker* eventBroker) : System(m_World, eventBroker) { - EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerDeathSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_OnPlayerDeath, &PlayerDeathSystem::OnPlayerDeath); } -bool PlayerDeathSystem::OnInputCommand(const Events::InputCommand& e) -{ - //testing: Jump > playerdamage - if (e.Command != "Jump") { - return false; - - } - - // 0 = released - if (e.Value != 0) { - return false; - - } - - auto players = m_World->GetComponents("Player"); - - for (auto& cPlayer : *players) { - EntityWrapper player(m_World, cPlayer.EntityID); - Events::PlayerDamage e; - e.Player = player; - e.Damage = 50; - m_EventBroker->Publish(e); - - } - - - - return true; -} - void PlayerDeathSystem::Update(double dt) { } From 6a23a987b36e4f0785b4ea07a3455781c8135a3c Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Thu, 4 Feb 2016 17:43:45 +0100 Subject: [PATCH 27/38] Started PickupSpawnSystem, added 2 events and a system --- include/Engine/Core/EPickupSpawned.h | 18 ++++++++++++++++++ include/Engine/Core/EPickupTaken.h | 19 +++++++++++++++++++ include/Game/Systems/PickupSpawnSystem.h | 17 +++++++++++++++++ resources/Schema/Components.xsd | 1 + resources/Schema/Components/PickupSpawn.xml | 2 ++ resources/Schema/Components/PickupSpawn.xsd | 11 +++++++++++ src/Game/Systems/PickupSpawnSystem.cpp | 1 + 7 files changed, 69 insertions(+) create mode 100644 include/Engine/Core/EPickupSpawned.h create mode 100644 include/Engine/Core/EPickupTaken.h create mode 100644 include/Game/Systems/PickupSpawnSystem.h create mode 100644 resources/Schema/Components/PickupSpawn.xml create mode 100644 resources/Schema/Components/PickupSpawn.xsd create mode 100644 src/Game/Systems/PickupSpawnSystem.cpp diff --git a/include/Engine/Core/EPickupSpawned.h b/include/Engine/Core/EPickupSpawned.h new file mode 100644 index 00000000..acb6776e --- /dev/null +++ b/include/Engine/Core/EPickupSpawned.h @@ -0,0 +1,18 @@ +#ifndef EPickupSpawned_h__ +#define EPickupSpawned_h__ + +#include "Core/Event.h" +#include "Core/EntityWrapper.h" + +namespace Events +{ + +struct PickupSpawned : Event +{ + EntityID PickupID; + EntityWrapper Spawner; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EPickupTaken.h b/include/Engine/Core/EPickupTaken.h new file mode 100644 index 00000000..e06fd974 --- /dev/null +++ b/include/Engine/Core/EPickupTaken.h @@ -0,0 +1,19 @@ +#ifndef EPickupTaken_h__ +#define EPickupTaken_h__ + +#include "Core/Event.h" +#include "Core/EntityWrapper.h" + +namespace Events +{ + +struct PickupTaken : Event +{ + EntityID PickupID; + EntityWrapper Spawner; + EntityWrapper Player; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Game/Systems/PickupSpawnSystem.h b/include/Game/Systems/PickupSpawnSystem.h new file mode 100644 index 00000000..99dad0aa --- /dev/null +++ b/include/Game/Systems/PickupSpawnSystem.h @@ -0,0 +1,17 @@ +#ifndef PickupSpawnSystem_h__ +#define PickupSpawnSystem_h__ + +#include "Core/System.h" +#include "Input/EInputCommand.h" +#include "Systems/SpawnerSystem.h" +#include "Events/ESpawnerSpawn.h" +#include "Core/EPlayerSpawned.h" +#include "Rendering/ESetCamera.h" +#include "Core/ConfigFile.h" +#include "Core/EPickupSpawned.h" + +class PickupSpawnSystem : public ImpureSystem +{ + +}; +#endif diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index ab46b0ea..445d96e7 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -29,4 +29,5 @@ + \ No newline at end of file diff --git a/resources/Schema/Components/PickupSpawn.xml b/resources/Schema/Components/PickupSpawn.xml new file mode 100644 index 00000000..f8ce1225 --- /dev/null +++ b/resources/Schema/Components/PickupSpawn.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/PickupSpawn.xsd b/resources/Schema/Components/PickupSpawn.xsd new file mode 100644 index 00000000..7ab90407 --- /dev/null +++ b/resources/Schema/Components/PickupSpawn.xsd @@ -0,0 +1,11 @@ + + + + + + + + Combined with a Spawner, defines a spawn point for a pickup + + + diff --git a/src/Game/Systems/PickupSpawnSystem.cpp b/src/Game/Systems/PickupSpawnSystem.cpp new file mode 100644 index 00000000..3b758764 --- /dev/null +++ b/src/Game/Systems/PickupSpawnSystem.cpp @@ -0,0 +1 @@ +#include "Systems/PickupSpawnSystem.h" From d61e6050938b751f82b7bfd1b58cd1f5628bf358 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 10:56:34 +0100 Subject: [PATCH 28/38] Can walk up "stair-steps". Cleanup WIP. Better performance in RectanglevsTriangle check. Added IsOnGround flag and VerticalStepHeight in Physics. Lotsa commented code saved for historical reasons, remove later. --- include/Engine/Collision/Collision.h | 2 + resources/Schema/Components/Physics.xml | 2 + resources/Schema/Components/Physics.xsd | 4 + src/Engine/Collision/Collision.cpp | 226 +++++++++++++++++----- src/Engine/Collision/CollisionSystem.cpp | 30 ++- src/Game/Systems/PlayerMovementSystem.cpp | 18 +- 6 files changed, 221 insertions(+), 61 deletions(-) diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 183c800c..6e4858b2 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -68,6 +68,8 @@ bool AABBvsTriangles(const AABB& box, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& boxVelocity, + float verticalStepHeight, + bool& isOnGround, glm::vec3& outResolutionVector); //Return true if the boxes are intersecting. diff --git a/resources/Schema/Components/Physics.xml b/resources/Schema/Components/Physics.xml index 9d1638fb..6cb73c75 100644 --- a/resources/Schema/Components/Physics.xml +++ b/resources/Schema/Components/Physics.xml @@ -2,4 +2,6 @@ true + false + 0.33 diff --git a/resources/Schema/Components/Physics.xsd b/resources/Schema/Components/Physics.xsd index 72037f48..7fed1fb5 100644 --- a/resources/Schema/Components/Physics.xsd +++ b/resources/Schema/Components/Physics.xsd @@ -13,6 +13,10 @@ m/s^2 + + + The largest height of a "stair-step" that can be walked over + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 083af229..7bb0d3c4 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -238,7 +238,7 @@ inline glm::vec3 signNonZero(const glm::vec3& x) { glm::vec3 r; for (int i = 0; i < 3; ++i) { - r[i] = signNonZero(x[i]); + r[i] = (float)signNonZero(x[i]); } return r; } @@ -253,11 +253,11 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, const glm::vec2& boxMax, const std::array& triPos, glm::vec2& resolutionDirection, - float& resolutionDistance, + float& resolutionDistanceSq, bool& pushedFromTriNormal) { pushedFromTriNormal = false; - resolutionDistance = INFINITY; + resolutionDistanceSq = INFINITY; //Project along box normals (coordinate axes, since it's axis-aligned). for (int ax = 0; ax < 2; ++ax) { float minTri = INFINITY; @@ -275,9 +275,11 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, float leftRes = minTri - boxMax[ax]; float rightRes = maxTri - boxMin[ax]; float push = rightRes < -leftRes ? rightRes : leftRes; - float absPush = abs(push); - if (absPush < resolutionDistance) { - resolutionDistance = absPush; + float absPushSq = abs(push); + absPushSq *= absPushSq; + + if (absPushSq < resolutionDistanceSq) { + resolutionDistanceSq = absPushSq; resolutionDirection[1 - ax] = 0.f; resolutionDirection[ax] = push; } @@ -326,9 +328,11 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, float leftRes = minTri - maxBox; float rightRes = maxTri - minBox; float push = rightRes < -leftRes ? rightRes : leftRes; - float absPush = abs(push); - if (absPush < resolutionDistance) { - resolutionDistance = absPush; + float absPushSq = abs(push); + absPushSq *= absPushSq; + + if (absPushSq < resolutionDistanceSq) { + resolutionDistanceSq = absPushSq; resolutionDirection = push * normal; pushedFromTriNormal = true; } @@ -338,26 +342,35 @@ bool rectangleVsTriangle(const glm::vec2& boxMin, constexpr float SlopeConstant(float degrees) { - return (1.0 - degrees / 90.f); + 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, 3> dimensionPairs({ std::pair(0, 2), std::pair(0, 1), std::pair(1, 2) }); -//TODO: Prefer to move in y - if the resolution is small enough, value in physics comp. -//TODO: Walking down slopes correctly. bool AABBvsTriangle(const AABB& box, const std::array& triPos, + const glm::vec3& originalBoxVelocity, + float verticalStepHeight, + bool& isOnGround, glm::vec3& boxVelocity, - glm::vec3& outVector) + glm::vec3& outResolution) { //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, boxVelocity) > 0)) { + if (!vectorHasLength(triNormal) || (glm::dot(triNormal, originalBoxVelocity) > 0)) { return false; } + triNormal = glm::normalize(triNormal); enum BoxTriResolveCase { @@ -366,13 +379,29 @@ bool AABBvsTriangle(const AABB& box, ResolveDimZ, Line, //Box edge colliding with triangle line. Corner //Box corner colliding with the triangle face. - } resolveCase; + }; + 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(); - float minimumTranslation = INFINITY; //For each projection in xy-, xz-, and yx-planes. for (std::pair dim : dimensionPairs) { @@ -392,21 +421,35 @@ bool AABBvsTriangle(const AABB& box, //if projections don't overlap, return false. if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) { return false; - } else if (resolutionDist < minimumTranslation) { - outVector = glm::vec3(0.f); - outVector[dim.first] = resolutionVector.x; - outVector[dim.second] = resolutionVector.y; - minimumTranslation = 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). - resolveCase = pushedFromTriangleLine ? Line : static_cast((abs(outVector[dim.first]) < 0.0001f) ? dim.second : dim.first); + } else { + //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((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((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. - triNormal = glm::normalize(triNormal); 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. @@ -416,24 +459,62 @@ bool AABBvsTriangle(const AABB& box, return false; } glm::vec3 cornerResolution = (1+t) * diagonal; - if (glm::length(cornerResolution) < minimumTranslation) { - outVector = cornerResolution; - resolveCase = Corner; + //Overwrite the smallest resolution if this 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; } - glm::vec3 projNorm; - switch (resolveCase) { - case ResolveDimX: + //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; + + //TODO: Debug stuff. + std::string dbg; + switch (bestResolve.Case) { case ResolveDimY: + case ResolveDimX: + case ResolveDimZ: + dbg = "Axis"; + break; + case Line: + dbg = "Line"; + break; + case Corner: + dbg = "Corner"; + break; + default: + break; + } + std::string outs = (takeUp ? "Resolve upwards " : "Resolve normal ") + dbg; + //TODO: End debug stuff. + + 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 that dimension. - boxVelocity[resolveCase] = 0.f; + //set velocity to 0 in y if it is along y-axis. + outs += isOnGround ? " ground" : " air"; + ImGui::Text(outs.c_str()); + LOG_INFO(outs.c_str()); return true; } case Line: - projNorm = glm::normalize(outVector); + projNorm = glm::normalize(outResolution); break; case Corner: projNorm = triNormal; @@ -442,26 +523,64 @@ bool AABBvsTriangle(const AABB& box, break; } - //If the collision was not on steep wall or similarly (e.g. walking on the ground), do special treatment. - //Magic value that makes condition correspond to: - //if the angle between horizon and the collision surface is less than 45 degrees. - if (projNorm.y > SlopeConstant(45.0f)) { + isOnGround = false; + //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. //Also zero the vertical velocity. - float len = glm::length(outVector); - float ang = glm::half_pi() - glm::acos(outVector.y / len); + float len = glm::length(outResolution); + float ang = glm::half_pi() - glm::acos(outResolution.y / len); if (len > 0.0000001f && ang > 0.0000001f) { - outVector.x = 0; - outVector.y = len / glm::sin(ang); - outVector.z = 0; - boxVelocity.y = 0.f; + outResolution.x = 0; + outResolution.y = len / glm::sin(ang); + outResolution.z = 0; } - } else if (projNorm.y > 0) { - //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 - *n, |n|==1. - boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + glm::vec3 projVel = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + if (abs(originalBoxVelocity.x) < 0.001f && abs(originalBoxVelocity.z) < 0.001f) { + boxVelocity.y = 0.f; + } else { + boxVelocity.y = std::min(projVel.y, 0.f); + } + + isOnGround = true; + } else if (projNorm.y > 0) { + //Enter here if the triangle is a steep slope, and it is not facing downwards. + //Ensure that the player will be pushed in the xz-plane. + //glm::vec3 moveDir(outResolution); + //moveDir.y = 0; + //if (vectorHasLength(moveDir)) { + // moveDir = glm::normalize(moveDir); + // float len = glm::length(outResolution); + // float dotMoveOut = moveDir.x * outResolution.x + moveDir.z * outResolution.z; + // float ang = glm::half_pi() - glm::acos(dotMoveOut / len); + // if (len > 0.0000001f && ang > 0.0000001f) { + // outResolution = (len / glm::sin(ang)) * moveDir; + // } + //} + //Project the velocity onto the normal of the hit line/face. + //w = v - *n, |n|==1. + //TODO: What do we want.. +#if 0 //Walk up steep walls. + glm::vec3 projVel = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + boxVelocity.y = std::min(projVel.y, 0.f); + isOnGround = true; +#elif 1 //"ice cream"-effect, air resistance + projected velocity. + if (!isOnGround) { + boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + } + isOnGround = false; +#elif 0 //"ice cream"-effect, ground friction (full control) + projected velocity. + if (!isOnGround) { + boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; + } + isOnGround = true; +#endif } + outs += isOnGround ? " ground" : " air"; + ImGui::Text(outs.c_str()); + LOG_INFO(outs.c_str()); return true; } @@ -470,12 +589,16 @@ bool AABBvsTriangles(const AABB& box, const std::vector& modelIndices, const glm::mat4& modelMatrix, glm::vec3& boxVelocity, + float verticalStepHeight, + bool& isOnGround, glm::vec3& outResolutionVector) { 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 triVertices = { Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix), @@ -483,13 +606,20 @@ bool AABBvsTriangles(const AABB& box, Transform::TransformPoint(modelVertices[modelIndices[i++]].Position, modelMatrix) }; glm::vec3 outVec; - if (AABBvsTriangle(newBox, triVertices, boxVelocity, outVec)) { + bool collideWithGround = isOnGround; + if (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec)) { hit = true; outResolutionVector += outVec; newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size()); + if (collideWithGround) { + everHitTheGround = isOnGround = true; + } } } + if (!everHitTheGround) { + isOnGround = false; + } return hit; } diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 846d38a7..e5c194d6 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -37,15 +37,33 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c glm::mat4 modelMatrix = Transform::ModelMatrix(boxB.Entity); - glm::vec3 newVelocity = (glm::vec3)cPhysics["Velocity"]; - if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, newVelocity, resolutionVector)) { - (glm::vec3&)cTransform["Position"] += resolutionVector; - cPhysics["Velocity"] = newVelocity; - } + glm::vec3 inOutVelocity = (glm::vec3)cPhysics["Velocity"]; + bool isOnGround = (bool)cPhysics["IsOnGround"]; + //glm::vec3 gravity = glm::vec3(0, 9.82f * dt, 0); + //TODO: glm::abs(inOutVelocity - gravity) doesn't work, because of velocity projection on ground normal. + //if (!isOnGround || glm::any(glm::greaterThan(glm::abs(inOutVelocity - gravity), glm::vec3(0.0001f)))) { + float verticalStepHeight = (float)(double)cPhysics["VerticalStepHeight"]; + if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, inOutVelocity, verticalStepHeight, isOnGround, resolutionVector)) { + glm::vec3 pos = (glm::vec3)cTransform["Position"] + resolutionVector; + //Hack: Force the position to be at discrete values after collision, removes jittering. + //constexpr float discreteValue = 1.0e2f; + //pos = glm::vec3(glm::ivec3(discreteValue * pos)) / discreteValue; + //pos = glm::round(discreteValue * pos) / discreteValue; + (glm::vec3&)cTransform["Position"] = pos; + cPhysics["Velocity"] = inOutVelocity; + (bool)cPhysics["IsOnGround"] = isOnGround; + } else { + (bool)cPhysics["IsOnGround"] = false; + } + //} else { + // (glm::vec3&)cTransform["Position"] -= gravity; + // cPhysics["Velocity"] = glm::vec3(0.f); + //} } else if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { //Enter here if boxB has no Model. (glm::vec3&)cTransform["Position"] += resolutionVector; - if (resolutionVector.y > 0) { + (bool)cPhysics["IsOnGround"] = resolutionVector.y > 0; + if ((bool)cPhysics["IsOnGround"]){ ((glm::vec3&)cPhysics["Velocity"]).y = 0.f; } } diff --git a/src/Game/Systems/PlayerMovementSystem.cpp b/src/Game/Systems/PlayerMovementSystem.cpp index 6eab02c5..92ddf549 100644 --- a/src/Game/Systems/PlayerMovementSystem.cpp +++ b/src/Game/Systems/PlayerMovementSystem.cpp @@ -50,11 +50,13 @@ void PlayerMovementSystem::Update(double dt) wishSpeed = playerMovementSpeed; } glm::vec3& velocity = cPhysics["Velocity"]; - ImGui::Text("velocity: (%f, %f, %f)", velocity.x, velocity.y, velocity.z); + bool isOnGround = (bool)cPhysics["IsOnGround"]; + ImGui::Text(isOnGround ? "On ground" : "In air"); + ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity)); glm::vec3 groundVelocity(0.f, 0.f, 0.f); - groundVelocity.x = glm::dot(velocity, glm::vec3(1.f, 0.f, 0.f)); - groundVelocity.z = glm::dot(velocity, glm::vec3(0.f, 0.f, 1.f)); - ImGui::Text("groundVelocity: (%f, %f, %f) |%f|", groundVelocity.x, groundVelocity.y, groundVelocity.z, glm::length(wishDirection)); + groundVelocity.x = velocity.x; + groundVelocity.z = velocity.z; + ImGui::Text("groundVelocity: (%f, %f, %f) |%f|", groundVelocity.x, groundVelocity.y, groundVelocity.z, glm::length(groundVelocity)); ImGui::Text("wishDirection: (%f, %f, %f) |%f|", wishDirection.x, wishDirection.y, wishDirection.z, glm::length(wishDirection)); float currentSpeedProj = glm::dot(groundVelocity, wishDirection); float addSpeed = wishSpeed - currentSpeedProj; @@ -67,7 +69,7 @@ void PlayerMovementSystem::Update(double dt) ImGui::InputFloat("accel", &accel); static float airAccel = 0.5f; ImGui::InputFloat("airAccel", &airAccel); - float actualAccel = (velocity.y != 0) ? airAccel : accel; + float actualAccel = isOnGround ? accel : airAccel; static float surfaceFriction = 5.f; ImGui::InputFloat("surfaceFriction", &surfaceFriction); float accelerationSpeed = actualAccel * (float)dt * wishSpeed * surfaceFriction; @@ -76,7 +78,8 @@ void PlayerMovementSystem::Update(double dt) ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity)); } - if (controller->Jumping() && !controller->Crouching() && velocity.y == 0.f) { + if (controller->Jumping() && !controller->Crouching() && isOnGround) { + (bool)cPhysics["IsOnGround"] = false; velocity.y += 4.f; } @@ -128,6 +131,7 @@ void PlayerMovementSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapp ComponentWrapper& cPhysics = entity["Physics"]; glm::vec3& velocity = cPhysics["Velocity"]; + bool isOnGround = (bool)cPhysics["IsOnGround"]; // Ground friction float speed = glm::length(velocity); @@ -135,7 +139,7 @@ void PlayerMovementSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapp ImGui::InputFloat("groundFriction", &groundFriction); static float airFriction = 0.f; ImGui::InputFloat("airFriction", &airFriction); - float friction = (velocity.y != 0) ? airFriction : groundFriction; + float friction = isOnGround ? groundFriction : airFriction; if (speed > 0) { float drop = speed * friction * (float)dt; float multiplier = glm::max(speed - drop, 0.f) / speed; From 4fe9e4f1c3586bb346684fe245a92a05adcae68a Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 14:38:08 +0100 Subject: [PATCH 29/38] Debug cleanup and some bug fixes. --- src/Engine/Collision/Collision.cpp | 68 ++---------------------- src/Engine/Collision/CollisionSystem.cpp | 28 +++------- 2 files changed, 13 insertions(+), 83 deletions(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 7bb0d3c4..533fdc9b 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -459,7 +459,7 @@ bool AABBvsTriangle(const AABB& box, return false; } glm::vec3 cornerResolution = (1+t) * diagonal; - //Overwrite the smallest resolution if this is smaller. + //Overwrite the smallest resolution if cornerResolution is smaller. float lenSq = glm::length2(cornerResolution); if (lenSq < resolveShortest.DistanceSq) { resolveShortest.Vector = cornerResolution; @@ -477,26 +477,6 @@ bool AABBvsTriangle(const AABB& box, Resolution& bestResolve = takeUp ? resolveUpwards : resolveShortest; outResolution = bestResolve.Vector; - //TODO: Debug stuff. - std::string dbg; - switch (bestResolve.Case) { - case ResolveDimY: - case ResolveDimX: - case ResolveDimZ: - dbg = "Axis"; - break; - case Line: - dbg = "Line"; - break; - case Corner: - dbg = "Corner"; - break; - default: - break; - } - std::string outs = (takeUp ? "Resolve upwards " : "Resolve normal ") + dbg; - //TODO: End debug stuff. - glm::vec3 projNorm; switch (bestResolve.Case) { case ResolveDimY: @@ -505,14 +485,9 @@ bool AABBvsTriangle(const AABB& box, 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. - outs += isOnGround ? " ground" : " air"; - ImGui::Text(outs.c_str()); - LOG_INFO(outs.c_str()); return true; - } case Line: projNorm = glm::normalize(outResolution); break; @@ -523,11 +498,9 @@ bool AABBvsTriangle(const AABB& box, break; } - isOnGround = false; //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. - //Also zero the vertical velocity. float len = glm::length(outResolution); float ang = glm::half_pi() - glm::acos(outResolution.y / len); if (len > 0.0000001f && ang > 0.0000001f) { @@ -535,52 +508,21 @@ bool AABBvsTriangle(const AABB& box, 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 - *n, |n|==1. - glm::vec3 projVel = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; - if (abs(originalBoxVelocity.x) < 0.001f && abs(originalBoxVelocity.z) < 0.001f) { - boxVelocity.y = 0.f; - } else { - boxVelocity.y = std::min(projVel.y, 0.f); - } - + boxVelocity.y = std::min(boxVelocity.y - glm::dot(boxVelocity, projNorm) * projNorm.y, 0.f); isOnGround = true; - } else if (projNorm.y > 0) { + } else { //Enter here if the triangle is a steep slope, and it is not facing downwards. - //Ensure that the player will be pushed in the xz-plane. - //glm::vec3 moveDir(outResolution); - //moveDir.y = 0; - //if (vectorHasLength(moveDir)) { - // moveDir = glm::normalize(moveDir); - // float len = glm::length(outResolution); - // float dotMoveOut = moveDir.x * outResolution.x + moveDir.z * outResolution.z; - // float ang = glm::half_pi() - glm::acos(dotMoveOut / len); - // if (len > 0.0000001f && ang > 0.0000001f) { - // outResolution = (len / glm::sin(ang)) * moveDir; - // } - //} //Project the velocity onto the normal of the hit line/face. //w = v - *n, |n|==1. - //TODO: What do we want.. -#if 0 //Walk up steep walls. - glm::vec3 projVel = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; - boxVelocity.y = std::min(projVel.y, 0.f); - isOnGround = true; -#elif 1 //"ice cream"-effect, air resistance + projected velocity. + //"ice cream"-effect, air resistance + projected velocity. if (!isOnGround) { boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; } isOnGround = false; -#elif 0 //"ice cream"-effect, ground friction (full control) + projected velocity. - if (!isOnGround) { - boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; - } - isOnGround = true; -#endif } - outs += isOnGround ? " ground" : " air"; - ImGui::Text(outs.c_str()); - LOG_INFO(outs.c_str()); return true; } diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index e5c194d6..b5d7a1f0 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -39,26 +39,14 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c glm::vec3 inOutVelocity = (glm::vec3)cPhysics["Velocity"]; bool isOnGround = (bool)cPhysics["IsOnGround"]; - //glm::vec3 gravity = glm::vec3(0, 9.82f * dt, 0); - //TODO: glm::abs(inOutVelocity - gravity) doesn't work, because of velocity projection on ground normal. - //if (!isOnGround || glm::any(glm::greaterThan(glm::abs(inOutVelocity - gravity), glm::vec3(0.0001f)))) { - float verticalStepHeight = (float)(double)cPhysics["VerticalStepHeight"]; - if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, inOutVelocity, verticalStepHeight, isOnGround, resolutionVector)) { - glm::vec3 pos = (glm::vec3)cTransform["Position"] + resolutionVector; - //Hack: Force the position to be at discrete values after collision, removes jittering. - //constexpr float discreteValue = 1.0e2f; - //pos = glm::vec3(glm::ivec3(discreteValue * pos)) / discreteValue; - //pos = glm::round(discreteValue * pos) / discreteValue; - (glm::vec3&)cTransform["Position"] = pos; - cPhysics["Velocity"] = inOutVelocity; - (bool)cPhysics["IsOnGround"] = isOnGround; - } else { - (bool)cPhysics["IsOnGround"] = false; - } - //} else { - // (glm::vec3&)cTransform["Position"] -= gravity; - // cPhysics["Velocity"] = glm::vec3(0.f); - //} + float verticalStepHeight = (float)(double)cPhysics["VerticalStepHeight"]; + if (Collision::AABBvsTriangles(boxA, model->m_Vertices, model->m_Indices, modelMatrix, inOutVelocity, verticalStepHeight, isOnGround, resolutionVector)) { + (glm::vec3&)cTransform["Position"] += resolutionVector; + cPhysics["Velocity"] = inOutVelocity; + (bool)cPhysics["IsOnGround"] = isOnGround; + } else { + (bool)cPhysics["IsOnGround"] = false; + } } else if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { //Enter here if boxB has no Model. (glm::vec3&)cTransform["Position"] += resolutionVector; From 428dca003daf6daa38d81e67a3a1c29727f42094 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 14:40:50 +0100 Subject: [PATCH 30/38] Tiny bug fix. --- src/Engine/Collision/Collision.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 533fdc9b..fe4eb5e0 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -521,7 +521,6 @@ bool AABBvsTriangle(const AABB& box, if (!isOnGround) { boxVelocity = boxVelocity - glm::dot(boxVelocity, projNorm) * projNorm; } - isOnGround = false; } return true; } From fe6fd0bbe35005f87f32dbd3853193423229c748 Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Fri, 5 Feb 2016 15:15:04 +0100 Subject: [PATCH 31/38] PickupSpawnSystem now spawns a new HealthPickup after the respawnTimer is up. Added HealthPickup.xml entity. Event PickupSpawned now gets published when a new HealthPickup spawns. Player now gains health as he picks up the HealthPickup. --- include/Engine/Core/EPickupSpawned.h | 2 +- include/Engine/Core/EPickupTaken.h | 19 -- include/Game/Systems/PickupSpawnSystem.h | 16 ++ resources/Schema/Components.xsd | 2 +- resources/Schema/Components/HealthPickup.xml | 4 + resources/Schema/Components/HealthPickup.xsd | 18 ++ resources/Schema/Components/PickupSpawn.xml | 2 - resources/Schema/Components/PickupSpawn.xsd | 11 - resources/Schema/Entities/CapturePoint.xml | 2 +- resources/Schema/Entities/HealthPickup.xml | 18 ++ .../Schema/Entities/HealthPickupTest.xml | 257 ++++++++++++++++++ src/Game/Game.cpp | 2 + src/Game/Systems/PickupSpawnSystem.cpp | 50 ++++ 13 files changed, 368 insertions(+), 35 deletions(-) delete mode 100644 include/Engine/Core/EPickupTaken.h create mode 100644 resources/Schema/Components/HealthPickup.xml create mode 100644 resources/Schema/Components/HealthPickup.xsd delete mode 100644 resources/Schema/Components/PickupSpawn.xml delete mode 100644 resources/Schema/Components/PickupSpawn.xsd create mode 100644 resources/Schema/Entities/HealthPickup.xml create mode 100644 resources/Schema/Entities/HealthPickupTest.xml diff --git a/include/Engine/Core/EPickupSpawned.h b/include/Engine/Core/EPickupSpawned.h index acb6776e..dc3bc632 100644 --- a/include/Engine/Core/EPickupSpawned.h +++ b/include/Engine/Core/EPickupSpawned.h @@ -10,7 +10,7 @@ namespace Events struct PickupSpawned : Event { EntityID PickupID; - EntityWrapper Spawner; + EntityWrapper Pickup; }; } diff --git a/include/Engine/Core/EPickupTaken.h b/include/Engine/Core/EPickupTaken.h deleted file mode 100644 index e06fd974..00000000 --- a/include/Engine/Core/EPickupTaken.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef EPickupTaken_h__ -#define EPickupTaken_h__ - -#include "Core/Event.h" -#include "Core/EntityWrapper.h" - -namespace Events -{ - -struct PickupTaken : Event -{ - EntityID PickupID; - EntityWrapper Spawner; - EntityWrapper Player; -}; - -} - -#endif \ No newline at end of file diff --git a/include/Game/Systems/PickupSpawnSystem.h b/include/Game/Systems/PickupSpawnSystem.h index 99dad0aa..8ae07265 100644 --- a/include/Game/Systems/PickupSpawnSystem.h +++ b/include/Game/Systems/PickupSpawnSystem.h @@ -9,9 +9,25 @@ #include "Rendering/ESetCamera.h" #include "Core/ConfigFile.h" #include "Core/EPickupSpawned.h" +#include "Core/EPlayerHealthPickup.h"; +#include "Engine/Collision/ETrigger.h" + +#include "Common.h" +#include class PickupSpawnSystem : public ImpureSystem { +public: + PickupSpawnSystem(World* world, EventBroker* eventBroker); + + virtual void Update(double dt) override; + +private: + EventRelay m_ETriggerTouch; + bool OnTriggerTouch(Events::TriggerTouch& e); + + std::vector> m_ETriggerTouchVector; + }; #endif diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 445d96e7..996475cf 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -29,5 +29,5 @@ - + \ No newline at end of file diff --git a/resources/Schema/Components/HealthPickup.xml b/resources/Schema/Components/HealthPickup.xml new file mode 100644 index 00000000..e9d0a23d --- /dev/null +++ b/resources/Schema/Components/HealthPickup.xml @@ -0,0 +1,4 @@ + + + 3 + \ No newline at end of file diff --git a/resources/Schema/Components/HealthPickup.xsd b/resources/Schema/Components/HealthPickup.xsd new file mode 100644 index 00000000..db9661a7 --- /dev/null +++ b/resources/Schema/Components/HealthPickup.xsd @@ -0,0 +1,18 @@ + + + + + + + + A Health Pickup + + + + + The respawn timer for a health pickup + + + + + diff --git a/resources/Schema/Components/PickupSpawn.xml b/resources/Schema/Components/PickupSpawn.xml deleted file mode 100644 index f8ce1225..00000000 --- a/resources/Schema/Components/PickupSpawn.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/resources/Schema/Components/PickupSpawn.xsd b/resources/Schema/Components/PickupSpawn.xsd deleted file mode 100644 index 7ab90407..00000000 --- a/resources/Schema/Components/PickupSpawn.xsd +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - Combined with a Spawner, defines a spawn point for a pickup - - - diff --git a/resources/Schema/Entities/CapturePoint.xml b/resources/Schema/Entities/CapturePoint.xml index 9b3c5036..9f33c4de 100644 --- a/resources/Schema/Entities/CapturePoint.xml +++ b/resources/Schema/Entities/CapturePoint.xml @@ -5,7 +5,7 @@ - C:\Users\123456\Workspace\TacticalZ\assets\Models\Core\UnitSphere.mesh + Models/Core/UnitSphere.mesh diff --git a/resources/Schema/Entities/HealthPickup.xml b/resources/Schema/Entities/HealthPickup.xml new file mode 100644 index 00000000..f1b787f6 --- /dev/null +++ b/resources/Schema/Entities/HealthPickup.xml @@ -0,0 +1,18 @@ + + + + + + + + Models/Core/UnitSphere.mesh + + + + + + + + + + diff --git a/resources/Schema/Entities/HealthPickupTest.xml b/resources/Schema/Entities/HealthPickupTest.xml new file mode 100644 index 00000000..6a368006 --- /dev/null +++ b/resources/Schema/Entities/HealthPickupTest.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + Models\MapVersion1.mesh + + + + + + + + + 2 + + + Models/DirectionalLightWidget.mesh + false + + + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index f5afcaeb..9e803e9e 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -10,6 +10,7 @@ #include "Systems/PlayerSpawnSystem.h" #include "Core/EntityFileWriter.h" #include "Game/Systems/CapturePointSystem.h" +#include "Game/Systems/PickupSpawnSystem.h" #include "Game/Systems/WeaponSystem.h" #include "Game/Systems/PlayerHUD.h" #include "Game/Systems/LifetimeSystem.h" @@ -91,6 +92,7 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); // Populate Octree with collidables ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision, "Collidable"); diff --git a/src/Game/Systems/PickupSpawnSystem.cpp b/src/Game/Systems/PickupSpawnSystem.cpp index 3b758764..28f0ca86 100644 --- a/src/Game/Systems/PickupSpawnSystem.cpp +++ b/src/Game/Systems/PickupSpawnSystem.cpp @@ -1 +1,51 @@ #include "Systems/PickupSpawnSystem.h" + +PickupSpawnSystem::PickupSpawnSystem(World* m_World, EventBroker* eventBroker) + : System(m_World, eventBroker) +{ + EVENT_SUBSCRIBE_MEMBER(m_ETriggerTouch, &PickupSpawnSystem::OnTriggerTouch); +} + +void PickupSpawnSystem::Update(double dt) +{ + for (auto &healthPickupPosition : m_ETriggerTouchVector) { + //set the double timer value (1) + std::get<1>(healthPickupPosition) -= dt; + if (std::get<1>(healthPickupPosition) < 0) { + //spawn and delete the vector item + auto entityFile = ResourceManager::Load("Schema/Entities/HealthPickup.xml"); + EntityFileParser parser(entityFile); + EntityID healthPickupID = parser.MergeEntities(m_World); + + //let the world know a pickup has spawned (graphics effects, etc) + Events::PickupSpawned ePickupSpawned; + ePickupSpawned.PickupID = healthPickupID; + ePickupSpawned.Pickup = EntityWrapper(m_World, healthPickupID); + m_EventBroker->Publish(ePickupSpawned); + + //erase the current element (healthPickupPosition) + m_ETriggerTouchVector.erase(std::remove(m_ETriggerTouchVector.begin(), m_ETriggerTouchVector.end(),healthPickupPosition), m_ETriggerTouchVector.end()); + break; + } + } +} + + +bool PickupSpawnSystem::OnTriggerTouch(Events::TriggerTouch& e) +{ + if (!e.Trigger.HasComponent("HealthPickup")) { + return false; + } + //personEntered = e.Entity, thingEntered = e.Trigger + Events::PlayerHealthPickup ePlayerHealthPickup; + ePlayerHealthPickup.HealthAmount = 30.0f; + ePlayerHealthPickup.PlayerHealedID = e.Entity.ID; + m_EventBroker->Publish(ePlayerHealthPickup); + + //copy the position to a vector -> respawntimer in ["HealthPickup"]["RespawnTimer"] + m_ETriggerTouchVector.push_back(std::make_tuple((glm::vec3)e.Trigger["Transform"]["Position"], e.Trigger["HealthPickup"]["RespawnTimer"])); + + //delete the healthpickup + m_World->DeleteEntity(e.Trigger.ID); + return true; +} From 7641ba49c751427fd419cafc59f86ab7a4b62052 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 15:16:24 +0100 Subject: [PATCH 32/38] Weapon doesn't explode in first person. --- resources/Schema/Entities/Player.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index fe24adcc..384ffb80 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -104,13 +104,6 @@ - - true - - 3.7999999523162842 - - true - Models/AssaultWeaponRed.mesh From 254239751188f582215b47cc287508723ca0998e Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Fri, 5 Feb 2016 15:35:51 +0100 Subject: [PATCH 33/38] Miscellaneous small fixes: For Tests, CapturePoint xml file, HealthSystem, DashAbility xml file. --- include/Engine/Core/EPlayerHealthPickup.h | 12 +++--- include/Game/Systems/HealthSystem.h | 2 +- resources/Schema/Components/DashAbility.xml | 4 +- resources/Schema/Entities/CapturePoint.xml | 2 +- resources/Schema/Entities/Player.xml | 7 ---- src/Game/Systems/HealthSystem.cpp | 41 +++------------------ src/Tests/CapturePointTest.cpp | 8 ++-- src/Tests/CollisionTest.cpp | 2 +- src/Tests/HealthSystemTest.cpp | 26 ++++++------- 9 files changed, 32 insertions(+), 72 deletions(-) diff --git a/include/Engine/Core/EPlayerHealthPickup.h b/include/Engine/Core/EPlayerHealthPickup.h index f3158f92..7d44e544 100644 --- a/include/Engine/Core/EPlayerHealthPickup.h +++ b/include/Engine/Core/EPlayerHealthPickup.h @@ -2,16 +2,16 @@ #define EPlayerHealthPickup_h__ #include "EventBroker.h" -#include "../Core/Entity.h" +#include "../Core/EntityWrapper.h" namespace Events { -struct PlayerHealthPickup : Event -{ - double HealthAmount; - EntityID PlayerHealedID; -}; + struct PlayerHealthPickup : Event + { + EntityWrapper Player; + double HealthAmount; + }; } diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index 3b069349..46f24630 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -26,7 +26,7 @@ private: EventRelay m_EPlayerDamage; bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e); EventRelay m_EPlayerHealthPickup; - bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e); + bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e); //vector which will keep track of health changes std::vector> m_DeltaHealthVector; diff --git a/resources/Schema/Components/DashAbility.xml b/resources/Schema/Components/DashAbility.xml index 25b9e19a..a313c447 100644 --- a/resources/Schema/Components/DashAbility.xml +++ b/resources/Schema/Components/DashAbility.xml @@ -1,4 +1,4 @@ - + 2.0 - \ No newline at end of file + \ No newline at end of file diff --git a/resources/Schema/Entities/CapturePoint.xml b/resources/Schema/Entities/CapturePoint.xml index 9b3c5036..9f33c4de 100644 --- a/resources/Schema/Entities/CapturePoint.xml +++ b/resources/Schema/Entities/CapturePoint.xml @@ -5,7 +5,7 @@ - C:\Users\123456\Workspace\TacticalZ\assets\Models\Core\UnitSphere.mesh + Models/Core/UnitSphere.mesh diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index fe24adcc..384ffb80 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -104,13 +104,6 @@ - - true - - 3.7999999523162842 - - true - Models/AssaultWeaponRed.mesh diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 9e118070..91cae148 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -11,38 +11,6 @@ HealthSystem::HealthSystem(World* m_World, EventBroker* eventBroker) void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) { - //if entityID of health is 9 then the players ID is also 9 (player,health are connected to the same entity) - double maxHealth = (double)component["MaxHealth"]; - - //process the DeltaHealthVector and change the entitys health accordingly - for (size_t i = m_DeltaHealthVector.size(); i > 0; i--) - { - auto deltaHP = m_DeltaHealthVector[i - 1]; - //if we have a healthchange for the current player and health is greater than 0, then apply it - if (std::get<0>(deltaHP) == component.EntityID && (double)component["Health"] > 0.0f) { - //get the deltaHP value from the tuple and make sure you dont get more than maxHealth - double newHealth = std::min((double)component["Health"] + (double)std::get<1>(deltaHP), maxHealth); - component["Health"] = newHealth; - m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + i - 1); - //check if health is <= 0 - if ((double)component["Health"] <= 0.0f) { - component["Health"] = 0.0; - //publish death event - Events::PlayerDeath e; - e.PlayerID = component.EntityID; - m_EventBroker->Publish(e); - //clear the remaining hpDeltas for the dead player - for (size_t j = m_DeltaHealthVector.size(); j > 0; j--) - { - if (std::get<0>(m_DeltaHealthVector[j - 1]) == component.EntityID) - m_DeltaHealthVector.erase(m_DeltaHealthVector.begin() + j - 1); - } - //delete the player and break the loop - m_World->DeleteEntity(entity.ID); - break; - } - } - } } bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) @@ -58,10 +26,13 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) return true; } -bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e) +bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e) { - //save the changed HP to a vector. it will be taken care of in UpdateComponent - m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerHealedID, e.HealthAmount)); + ComponentWrapper cHealth = e.Player["Health"]; + double& health = cHealth["Health"]; + //NOTE: its possible to get more than MaxHealth health + health += e.HealthAmount; + return true; } diff --git a/src/Tests/CapturePointTest.cpp b/src/Tests/CapturePointTest.cpp index ffb01030..2c7bf430 100644 --- a/src/Tests/CapturePointTest.cpp +++ b/src/Tests/CapturePointTest.cpp @@ -255,14 +255,14 @@ void CapturePointTest::TestSetup8() } void CapturePointTest::DoTouchEvent(EntityID whoDidSomething, EntityID onWhatObject) { Events::TriggerTouch touchEvent; - touchEvent.Entity = whoDidSomething; - touchEvent.Trigger = onWhatObject; + touchEvent.Entity = EntityWrapper(m_World, whoDidSomething); + touchEvent.Trigger = EntityWrapper(m_World, onWhatObject); m_EventBroker->Publish(touchEvent); } void CapturePointTest::DoLeaveEvent(EntityID whoDidSomething, EntityID onWhatObject) { Events::TriggerLeave leaveEvent; - leaveEvent.Entity = whoDidSomething; - leaveEvent.Trigger = onWhatObject; + leaveEvent.Entity = EntityWrapper(m_World, whoDidSomething); + leaveEvent.Trigger = EntityWrapper(m_World, onWhatObject); m_EventBroker->Publish(leaveEvent); } void CapturePointTest::TestSuccess1() { diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 67b6ce19..6cb6c88b 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -29,7 +29,7 @@ void RayTest(std::string fileName) { Ray ray(glm::vec3(-50, 0, 0), glm::vec3(1, 0, 0)); //using a - here, else we have to init the renderingsystem + //here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); auto unitBox = ResourceManager::Load(fileName); BOOST_REQUIRE(unitBox != nullptr); diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp index bdd9ba4b..36608f8f 100644 --- a/src/Tests/HealthSystemTest.cpp +++ b/src/Tests/HealthSystemTest.cpp @@ -48,42 +48,38 @@ GameHealthSystemTest::GameHealthSystemTest() fp.MergeEntities(m_World); // Create system pipeline - m_SystemPipeline = new SystemPipeline(m_World,m_EventBroker); + m_SystemPipeline = new SystemPipeline(m_World, m_EventBroker); m_SystemPipeline->AddSystem(0); //The Test //create entity which has transform,player,model,health in it. i.e. is a player EntityID playerID = m_World->CreateEntity(); - ComponentWrapper transform = m_World->AttachComponent(playerID, "Transform"); - ComponentWrapper model = m_World->AttachComponent(playerID, "Model"); - model["Resource"] = "Models/Core/UnitSphere.mesh"; // 360NoScope UnitSphere ComponentWrapper player = m_World->AttachComponent(playerID, "Player"); ComponentWrapper health = m_World->AttachComponent(playerID, "Health"); healthsID = playerID; - double currentHealth = (double)m_World->GetComponent(healthsID, "Health")["Health"]; + + EntityID playerID2 = m_World->CreateEntity(); + ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); + ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); //heal player with 40 Events::PlayerHealthPickup e3; e3.HealthAmount = 40.0f; - e3.PlayerHealedID = healthsID; + e3.Player = EntityWrapper(m_World, player.EntityID); m_EventBroker->Publish(e3); + //damage player with 50 Events::PlayerDamage e; - e.DamageAmount = 50.0f; - e.PlayerDamagedID = healthsID; + e.Damage = 50.0f; + e.Player = EntityWrapper(m_World, player.EntityID); m_EventBroker->Publish(e); + //heal some other player with 40 Events::PlayerHealthPickup e2; e2.HealthAmount = 40.0f; - e2.PlayerHealedID = healthsID + 1; + e2.Player = EntityWrapper(m_World, player2.EntityID); m_EventBroker->Publish(e2); - EntityID playerID2 = m_World->CreateEntity(); - ComponentWrapper transform2 = m_World->AttachComponent(playerID2, "Transform"); - ComponentWrapper model2 = m_World->AttachComponent(playerID2, "Model"); - model2["Resource"] = "Models/Core/UnitSphere.mesh"; // 360NoScope UnitSphere - ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); - ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); //END TEST } From 66f442f1afbe6e6a0ce76bb461d1d20fdd19368a Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Fri, 5 Feb 2016 16:13:59 +0100 Subject: [PATCH 34/38] Added HealthGain to HealthPickup component. Changed PickupSpawnSystem so it copies the values from the old to the new entity. --- include/Game/Systems/PickupSpawnSystem.h | 14 +++-------- resources/Schema/Components/HealthPickup.xml | 1 + resources/Schema/Components/HealthPickup.xsd | 3 +++ .../Schema/Entities/HealthPickupTest.xml | 14 ----------- src/Game/Systems/PickupSpawnSystem.cpp | 25 +++++++++++++------ 5 files changed, 26 insertions(+), 31 deletions(-) diff --git a/include/Game/Systems/PickupSpawnSystem.h b/include/Game/Systems/PickupSpawnSystem.h index 8ae07265..fbb2f082 100644 --- a/include/Game/Systems/PickupSpawnSystem.h +++ b/include/Game/Systems/PickupSpawnSystem.h @@ -2,16 +2,12 @@ #define PickupSpawnSystem_h__ #include "Core/System.h" -#include "Input/EInputCommand.h" -#include "Systems/SpawnerSystem.h" -#include "Events/ESpawnerSpawn.h" -#include "Core/EPlayerSpawned.h" -#include "Rendering/ESetCamera.h" -#include "Core/ConfigFile.h" +#include "Core/Transform.h" +#include "Core/ResourceManager.h" +#include "Core/EntityFileParser.h" #include "Core/EPickupSpawned.h" #include "Core/EPlayerHealthPickup.h"; #include "Engine/Collision/ETrigger.h" - #include "Common.h" #include @@ -26,8 +22,6 @@ private: EventRelay m_ETriggerTouch; bool OnTriggerTouch(Events::TriggerTouch& e); - std::vector> m_ETriggerTouchVector; - - + std::vector> m_ETriggerTouchVector; }; #endif diff --git a/resources/Schema/Components/HealthPickup.xml b/resources/Schema/Components/HealthPickup.xml index e9d0a23d..1c9ee0f4 100644 --- a/resources/Schema/Components/HealthPickup.xml +++ b/resources/Schema/Components/HealthPickup.xml @@ -1,4 +1,5 @@ 3 + 30 \ No newline at end of file diff --git a/resources/Schema/Components/HealthPickup.xsd b/resources/Schema/Components/HealthPickup.xsd index db9661a7..c5fae00f 100644 --- a/resources/Schema/Components/HealthPickup.xsd +++ b/resources/Schema/Components/HealthPickup.xsd @@ -12,6 +12,9 @@ The respawn timer for a health pickup + + How much health the player will get when he picks the healthPickup up + diff --git a/resources/Schema/Entities/HealthPickupTest.xml b/resources/Schema/Entities/HealthPickupTest.xml index 6a368006..97fd3f4d 100644 --- a/resources/Schema/Entities/HealthPickupTest.xml +++ b/resources/Schema/Entities/HealthPickupTest.xml @@ -238,20 +238,6 @@ - - - - - - Models/Core/UnitSphere.mesh - - - - - - - - diff --git a/src/Game/Systems/PickupSpawnSystem.cpp b/src/Game/Systems/PickupSpawnSystem.cpp index 28f0ca86..558aef14 100644 --- a/src/Game/Systems/PickupSpawnSystem.cpp +++ b/src/Game/Systems/PickupSpawnSystem.cpp @@ -9,9 +9,9 @@ PickupSpawnSystem::PickupSpawnSystem(World* m_World, EventBroker* eventBroker) void PickupSpawnSystem::Update(double dt) { for (auto &healthPickupPosition : m_ETriggerTouchVector) { - //set the double timer value (1) - std::get<1>(healthPickupPosition) -= dt; - if (std::get<1>(healthPickupPosition) < 0) { + //set the double timer value (value 3) + std::get<3>(healthPickupPosition) -= dt; + if (std::get<3>(healthPickupPosition) < 0) { //spawn and delete the vector item auto entityFile = ResourceManager::Load("Schema/Entities/HealthPickup.xml"); EntityFileParser parser(entityFile); @@ -23,8 +23,14 @@ void PickupSpawnSystem::Update(double dt) ePickupSpawned.Pickup = EntityWrapper(m_World, healthPickupID); m_EventBroker->Publish(ePickupSpawned); + //set values from the old entity to the new entity + auto& newHealthPickupEntity = EntityWrapper(m_World, healthPickupID); + newHealthPickupEntity["Transform"]["Position"] = std::get<0>(healthPickupPosition); + newHealthPickupEntity["HealthPickup"]["HealthGain"] = std::get<1>(healthPickupPosition); + newHealthPickupEntity["HealthPickup"]["RespawnTimer"] = std::get<2>(healthPickupPosition); + //erase the current element (healthPickupPosition) - m_ETriggerTouchVector.erase(std::remove(m_ETriggerTouchVector.begin(), m_ETriggerTouchVector.end(),healthPickupPosition), m_ETriggerTouchVector.end()); + m_ETriggerTouchVector.erase(std::remove(m_ETriggerTouchVector.begin(), m_ETriggerTouchVector.end(), healthPickupPosition), m_ETriggerTouchVector.end()); break; } } @@ -38,12 +44,17 @@ bool PickupSpawnSystem::OnTriggerTouch(Events::TriggerTouch& e) } //personEntered = e.Entity, thingEntered = e.Trigger Events::PlayerHealthPickup ePlayerHealthPickup; - ePlayerHealthPickup.HealthAmount = 30.0f; + ePlayerHealthPickup.HealthAmount = e.Trigger["HealthPickup"]["HealthGain"]; ePlayerHealthPickup.PlayerHealedID = e.Entity.ID; m_EventBroker->Publish(ePlayerHealthPickup); - //copy the position to a vector -> respawntimer in ["HealthPickup"]["RespawnTimer"] - m_ETriggerTouchVector.push_back(std::make_tuple((glm::vec3)e.Trigger["Transform"]["Position"], e.Trigger["HealthPickup"]["RespawnTimer"])); + //copy position, healthgain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object) + //we need to copy all values since each value can be different for each healthPickup + m_ETriggerTouchVector.push_back(std::make_tuple( + (glm::vec3)e.Trigger["Transform"]["Position"], + e.Trigger["HealthPickup"]["HealthGain"], + e.Trigger["HealthPickup"]["RespawnTimer"], + e.Trigger["HealthPickup"]["RespawnTimer"])); //delete the healthpickup m_World->DeleteEntity(e.Trigger.ID); From 59bdc614f2b28991b91cc1f2b0f871cefb28a1f4 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 17:34:19 +0100 Subject: [PATCH 35/38] Put SpecialAbility on left shift, and Sprint on left shift in DefaultInput. --- resources/DefaultInput.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/DefaultInput.ini b/resources/DefaultInput.ini index a3f7d166..589dfb3f 100644 --- a/resources/DefaultInput.ini +++ b/resources/DefaultInput.ini @@ -13,7 +13,8 @@ A=Right,-1 R=Reload Space=Jump LeftControl=Crouch -LeftShift=Sprint +RightShift=Sprint +LeftShift=SpecialAbility F1=ToggleEditor C=ConnectToServer N=SwitchToServer From 4103c878ec69399e256ab13c302ef5bb3390dabf Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Fri, 5 Feb 2016 18:05:46 +0100 Subject: [PATCH 36/38] Refactoring: Changed HealthPickupSystem to use a struct instead. Removed the Id in EPickupSpawned --- include/Engine/Core/EPickupSpawned.h | 1 - include/Game/Systems/PickupSpawnSystem.h | 10 +++- resources/Schema/Entities/HealthPickup.xml | 2 +- .../Schema/Entities/HealthPickupTest.xml | 51 +++++++++++++++++++ src/Game/Systems/PickupSpawnSystem.cpp | 24 ++++----- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/include/Engine/Core/EPickupSpawned.h b/include/Engine/Core/EPickupSpawned.h index dc3bc632..9f85913a 100644 --- a/include/Engine/Core/EPickupSpawned.h +++ b/include/Engine/Core/EPickupSpawned.h @@ -9,7 +9,6 @@ namespace Events struct PickupSpawned : Event { - EntityID PickupID; EntityWrapper Pickup; }; diff --git a/include/Game/Systems/PickupSpawnSystem.h b/include/Game/Systems/PickupSpawnSystem.h index fbb2f082..24cee2b9 100644 --- a/include/Game/Systems/PickupSpawnSystem.h +++ b/include/Game/Systems/PickupSpawnSystem.h @@ -6,7 +6,7 @@ #include "Core/ResourceManager.h" #include "Core/EntityFileParser.h" #include "Core/EPickupSpawned.h" -#include "Core/EPlayerHealthPickup.h"; +#include "Core/EPlayerHealthPickup.h" #include "Engine/Collision/ETrigger.h" #include "Common.h" #include @@ -22,6 +22,12 @@ private: EventRelay m_ETriggerTouch; bool OnTriggerTouch(Events::TriggerTouch& e); - std::vector> m_ETriggerTouchVector; + struct NewHealthPickup { + glm::vec3 Pos; + double HealthGain; + double RespawnTimer; + double DecreaseThisRespawnTimer; + }; + std::vector m_ETriggerTouchVector; }; #endif diff --git a/resources/Schema/Entities/HealthPickup.xml b/resources/Schema/Entities/HealthPickup.xml index f1b787f6..dfc6f938 100644 --- a/resources/Schema/Entities/HealthPickup.xml +++ b/resources/Schema/Entities/HealthPickup.xml @@ -8,7 +8,7 @@ Models/Core/UnitSphere.mesh - + diff --git a/resources/Schema/Entities/HealthPickupTest.xml b/resources/Schema/Entities/HealthPickupTest.xml index 97fd3f4d..2980fb51 100644 --- a/resources/Schema/Entities/HealthPickupTest.xml +++ b/resources/Schema/Entities/HealthPickupTest.xml @@ -238,6 +238,57 @@ + + + + + 1 + 22 + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + + + 1 + 22 + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + + + 4 + 44 + + + Models/Core/UnitSphere.mesh + + + + + + + + diff --git a/src/Game/Systems/PickupSpawnSystem.cpp b/src/Game/Systems/PickupSpawnSystem.cpp index 558aef14..783f7a62 100644 --- a/src/Game/Systems/PickupSpawnSystem.cpp +++ b/src/Game/Systems/PickupSpawnSystem.cpp @@ -8,10 +8,12 @@ PickupSpawnSystem::PickupSpawnSystem(World* m_World, EventBroker* eventBroker) void PickupSpawnSystem::Update(double dt) { - for (auto &healthPickupPosition : m_ETriggerTouchVector) { + for (auto it = m_ETriggerTouchVector.begin(); it != m_ETriggerTouchVector.end(); ++it) + { + auto& healthPickupPosition = *it; //set the double timer value (value 3) - std::get<3>(healthPickupPosition) -= dt; - if (std::get<3>(healthPickupPosition) < 0) { + healthPickupPosition.DecreaseThisRespawnTimer -= dt; + if (healthPickupPosition.DecreaseThisRespawnTimer < 0) { //spawn and delete the vector item auto entityFile = ResourceManager::Load("Schema/Entities/HealthPickup.xml"); EntityFileParser parser(entityFile); @@ -19,18 +21,17 @@ void PickupSpawnSystem::Update(double dt) //let the world know a pickup has spawned (graphics effects, etc) Events::PickupSpawned ePickupSpawned; - ePickupSpawned.PickupID = healthPickupID; ePickupSpawned.Pickup = EntityWrapper(m_World, healthPickupID); m_EventBroker->Publish(ePickupSpawned); //set values from the old entity to the new entity auto& newHealthPickupEntity = EntityWrapper(m_World, healthPickupID); - newHealthPickupEntity["Transform"]["Position"] = std::get<0>(healthPickupPosition); - newHealthPickupEntity["HealthPickup"]["HealthGain"] = std::get<1>(healthPickupPosition); - newHealthPickupEntity["HealthPickup"]["RespawnTimer"] = std::get<2>(healthPickupPosition); + newHealthPickupEntity["Transform"]["Position"] = healthPickupPosition.Pos; + newHealthPickupEntity["HealthPickup"]["HealthGain"] = healthPickupPosition.HealthGain; + newHealthPickupEntity["HealthPickup"]["RespawnTimer"] = healthPickupPosition.RespawnTimer; //erase the current element (healthPickupPosition) - m_ETriggerTouchVector.erase(std::remove(m_ETriggerTouchVector.begin(), m_ETriggerTouchVector.end(), healthPickupPosition), m_ETriggerTouchVector.end()); + m_ETriggerTouchVector.erase(it); break; } } @@ -50,11 +51,8 @@ bool PickupSpawnSystem::OnTriggerTouch(Events::TriggerTouch& e) //copy position, healthgain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object) //we need to copy all values since each value can be different for each healthPickup - m_ETriggerTouchVector.push_back(std::make_tuple( - (glm::vec3)e.Trigger["Transform"]["Position"], - e.Trigger["HealthPickup"]["HealthGain"], - e.Trigger["HealthPickup"]["RespawnTimer"], - e.Trigger["HealthPickup"]["RespawnTimer"])); + m_ETriggerTouchVector.push_back({ (glm::vec3)e.Trigger["Transform"]["Position"] ,e.Trigger["HealthPickup"]["HealthGain"], + e.Trigger["HealthPickup"]["RespawnTimer"],e.Trigger["HealthPickup"]["RespawnTimer"] }); //delete the healthpickup m_World->DeleteEntity(e.Trigger.ID); From ce228541689d695c7a09ef96527dc572a874193e Mon Sep 17 00:00:00 2001 From: verysecrethero Date: Mon, 8 Feb 2016 11:22:28 +0100 Subject: [PATCH 37/38] HealthPickups are now based on a percentage of player maxhealth. You can no longer pickup healthpacks if you are at max health. You can no longer gain more than maxHealth. --- include/Game/Systems/HealthSystem.h | 1 + resources/Schema/Components/HealthPickup.xsd | 2 +- src/Game/Systems/HealthSystem.cpp | 4 ++-- src/Game/Systems/PickupSpawnSystem.cpp | 10 ++++++++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index 46f24630..61456d5d 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -12,6 +12,7 @@ #include #include +#include class HealthSystem : public PureSystem { diff --git a/resources/Schema/Components/HealthPickup.xsd b/resources/Schema/Components/HealthPickup.xsd index c5fae00f..bf3b327c 100644 --- a/resources/Schema/Components/HealthPickup.xsd +++ b/resources/Schema/Components/HealthPickup.xsd @@ -13,7 +13,7 @@ The respawn timer for a health pickup - How much health the player will get when he picks the healthPickup up + How much percent max-health the player will get when he picks the healthPickup up diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 91cae148..94d98222 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -18,7 +18,7 @@ bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) ComponentWrapper cHealth = e.Player["Health"]; double& health = cHealth["Health"]; health -= e.Damage; - + if (health <= 0.0) { m_World->DeleteEntity(e.Player.ID); } @@ -30,8 +30,8 @@ bool HealthSystem::OnPlayerHealthPickup(Events::PlayerHealthPickup& e) { ComponentWrapper cHealth = e.Player["Health"]; double& health = cHealth["Health"]; - //NOTE: its possible to get more than MaxHealth health health += e.HealthAmount; + health = std::min(health, (double)cHealth["MaxHealth"]); return true; } diff --git a/src/Game/Systems/PickupSpawnSystem.cpp b/src/Game/Systems/PickupSpawnSystem.cpp index 783f7a62..e9f201c1 100644 --- a/src/Game/Systems/PickupSpawnSystem.cpp +++ b/src/Game/Systems/PickupSpawnSystem.cpp @@ -43,10 +43,16 @@ bool PickupSpawnSystem::OnTriggerTouch(Events::TriggerTouch& e) if (!e.Trigger.HasComponent("HealthPickup")) { return false; } + double healthGiven = 0.01*(double)e.Trigger["HealthPickup"]["HealthGain"] * (double)e.Entity["Health"]["MaxHealth"]; + //cant pick up healthpacks if you are already at MaxHealth + if ((double)e.Entity["Health"]["Health"] >= (double)e.Entity["Health"]["MaxHealth"]) { + return false; + } + //personEntered = e.Entity, thingEntered = e.Trigger Events::PlayerHealthPickup ePlayerHealthPickup; - ePlayerHealthPickup.HealthAmount = e.Trigger["HealthPickup"]["HealthGain"]; - ePlayerHealthPickup.PlayerHealedID = e.Entity.ID; + ePlayerHealthPickup.HealthAmount = healthGiven; + ePlayerHealthPickup.Player = e.Entity; m_EventBroker->Publish(ePlayerHealthPickup); //copy position, healthgain, respawntimer (twice since one of the values will be counted down to 0, the other will be set in the new object) From 99cbdd3c1e668b85603ed3fa07d5f9f84ff6f7fa Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Mon, 8 Feb 2016 11:39:29 +0100 Subject: [PATCH 38/38] Player death effect fixes --- include/Game/Systems/PlayerDeathSystem.h | 3 ++ include/Game/Systems/PlayerSpawnSystem.h | 1 + .../PlayerDeathExplosionWithCamera.xml | 30 +++++++---------- src/Game/Systems/PlayerDeathSystem.cpp | 33 +++++++++++-------- src/Game/Systems/PlayerSpawnSystem.cpp | 4 ++- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/include/Game/Systems/PlayerDeathSystem.h b/include/Game/Systems/PlayerDeathSystem.h index 7d989dbf..988dad57 100644 --- a/include/Game/Systems/PlayerDeathSystem.h +++ b/include/Game/Systems/PlayerDeathSystem.h @@ -22,5 +22,8 @@ public: private: EventRelay m_OnPlayerDeath; bool OnPlayerDeath(Events::PlayerDeath& e); + + void createDeathEffect(EntityWrapper player); + }; #endif \ No newline at end of file diff --git a/include/Game/Systems/PlayerSpawnSystem.h b/include/Game/Systems/PlayerSpawnSystem.h index b0ff1d79..f5aa7b82 100644 --- a/include/Game/Systems/PlayerSpawnSystem.h +++ b/include/Game/Systems/PlayerSpawnSystem.h @@ -3,6 +3,7 @@ #include "Systems/SpawnerSystem.h" #include "Events/ESpawnerSpawn.h" #include "Core/EPlayerSpawned.h" +#include "Core/EPlayerDeath.h" #include "Rendering/ESetCamera.h" #include "Core/ConfigFile.h" diff --git a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml index ff33476d..8a9f5e5b 100644 --- a/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml +++ b/resources/Schema/Entities/PlayerDeathExplosionWithCamera.xml @@ -4,17 +4,18 @@ Hold Pos - - 0 - 4 + 2.5 - 0 - 4 - - + true + + + true + 3 + + true Models/AssaultAnimated.mesh @@ -22,24 +23,17 @@ true - + - - 1 - - - 120 - 0.1 - 10000 - + - - + + diff --git a/src/Game/Systems/PlayerDeathSystem.cpp b/src/Game/Systems/PlayerDeathSystem.cpp index f7fe1ab5..60c31ce5 100644 --- a/src/Game/Systems/PlayerDeathSystem.cpp +++ b/src/Game/Systems/PlayerDeathSystem.cpp @@ -11,18 +11,31 @@ void PlayerDeathSystem::Update(double dt) } bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) +{ + if (!e.Player.Valid()) { + return false; + } + + createDeathEffect(e.Player); + + // Delete player + m_World->DeleteEntity(e.Player.ID); + + return true; +} + +void PlayerDeathSystem::createDeathEffect(EntityWrapper player) { //load the explosioneffect XML auto deathEffect = ResourceManager::Load("Schema/Entities/PlayerDeathExplosionWithCamera.xml"); - EntityFileParser parser(deathEffect); EntityID deathEffectID = parser.MergeEntities(m_World); EntityWrapper deathEffectEW = EntityWrapper(m_World, deathEffectID); //components that we need from player - auto playerCamera = e.Player.FirstChildByName("Camera"); - auto playerEntityModel = e.Player.FirstChildByName("PlayerModel")["Model"]; - auto playerEntityAnimation = e.Player.FirstChildByName("PlayerModel")["Animation"]; + auto playerCamera = player.FirstChildByName("Camera"); + auto playerEntityModel = player.FirstChildByName("PlayerModel")["Model"]; + auto playerEntityAnimation = player.FirstChildByName("PlayerModel")["Animation"]; //copy the data from player to explisioneffectmodel playerEntityModel.Copy(deathEffectEW["Model"]); @@ -31,20 +44,14 @@ bool PlayerDeathSystem::OnPlayerDeath(Events::PlayerDeath& e) deathEffectEW["Animation"]["Speed"] = 0.0; //copy the models position,orientation - deathEffectEW["Transform"]["Position"] = (glm::vec3)e.Player["Transform"]["Position"]; - deathEffectEW["Transform"]["Orientation"] = (glm::vec3)e.Player["Transform"]["Orientation"]; + deathEffectEW["Transform"]["Position"] = (glm::vec3)player["Transform"]["Position"]; + deathEffectEW["Transform"]["Orientation"] = (glm::vec3)player["Transform"]["Orientation"]; //effect,camera is relative to playersPosition - deathEffectEW["ExplosionEffect"]["ExplosionOrigin"] = glm::vec3(0, 0, 0); + //deathEffectEW["ExplosionEffect"]["ExplosionOrigin"] = glm::vec3(0, 0, 0); //camera (with lifetime) behind the player auto cam = deathEffectEW.FirstChildByName("Camera"); - cam["Transform"]["Position"] = glm::vec3(0, 2.5f, 1.8f); - cam["Transform"]["Orientation"] = glm::vec3(5.655f, 0, 0); Events::SetCamera eSetCamera; eSetCamera.CameraEntity = cam; m_EventBroker->Publish(eSetCamera); - - //done -> del entity - m_World->DeleteEntity(e.Player.ID); - return true; } diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index 7db6e8ff..5f46cce3 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -75,7 +75,9 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) // Check if a player already exists if (m_PlayerEntities.count(e.PlayerID) != 0) { // TODO: Disallow infinite respawning here - m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); + if (m_PlayerEntities[e.PlayerID].Valid()) { + m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); + } } // Store the player for future reference