From 4d5b8353529f12656f0acae8a3ffb99057188fc4 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Wed, 20 Jan 2016 16:43:09 +0100 Subject: [PATCH 01/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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 76274c326e66beeae725b6638fd46b1d2814e364 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Mon, 1 Feb 2016 13:34:39 +0100 Subject: [PATCH 14/24] 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 15/24] 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 16/24] 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 17/24] 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 18/24] 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 affcc5305bc032b27eb3e50ac222144a0820bfc3 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Tue, 2 Feb 2016 17:19:59 +0100 Subject: [PATCH 19/24] 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 20/24] 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 d61e6050938b751f82b7bfd1b58cd1f5628bf358 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 10:56:34 +0100 Subject: [PATCH 21/24] 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 22/24] 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 23/24] 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 7641ba49c751427fd419cafc59f86ab7a4b62052 Mon Sep 17 00:00:00 2001 From: William Moberg Date: Fri, 5 Feb 2016 15:16:24 +0100 Subject: [PATCH 24/24] 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