WIP, collision detects too much, no resolving for most cases.

This commit is contained in:
William Moberg
2016-01-21 17:10:36 +01:00
parent 4d5b835352
commit de7c8e9234
6 changed files with 248 additions and 38 deletions
+227 -29
View File
@@ -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<RawModel::Vertex>& modelVertices,
const std::vector<unsigned int>& 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<bool>(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<bool>(false);
auto outsideMaxPlane = glm::tvec3<bool>(false);
auto insidePlanes = glm::tvec3<bool>(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<RawModel::Vertex>& modelVertices, const std::vector<unsigned int>& modelIndices, const glm::mat4& modelMatrix, glm::vec3& outResolutionVector)
@@ -229,15 +423,19 @@ bool AABBvsTriangles(const AABB& box, const std::vector<RawModel::Vertex>& 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;