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; }