-1

I'm developing a game engine for a university project and I can't get the collision detection system to work. I've found this paper that explains an algorithm for triangle-triangle collision detection created by Chaman-Leopoldj, but somehow I can't implement it. I know it is a bit long but the algorithm can be found at pages 8 and 22-24

here is the code I wrote:

this is the wrapper function

bool Octree::triangleTriangleIntersection(glm::vec3 A, glm::vec3 B, glm::vec3 C, glm::vec3 P, glm::vec3 Q, glm::vec3 R) {
glm::vec3 U = B - A;
glm::vec3 V = C - A;
glm::vec3 S = Q - P;
glm::vec3 T = R - P;
glm::vec3 AP = P - A;

float sigma = dot(U * V, U * V);
glm::vec3 alpha = (S * (U * V)) / sigma;
glm::vec3 beta = (T * (U * V)) / sigma;
glm::vec3 gamma = (AP * (U * V)) / sigma;

float alphau = dot(alpha, U);
float alphav = dot(alpha, V);
float alphauv = dot(alpha, U - V);
float gammau = dot(gamma, U);
float gammav = dot(gamma, V);
float gammauv = dot(gamma, U - V);
float betau = dot(beta, U);
float betav = dot(beta, V);
float betauv = dot(beta, U - V);

float Xm, XM, Sm = 0, SM = 1;
float Ym, YM, Tm = 0, TM = 1;

if (findSolution_x(-gammau, alphau, betau, 1 - gammau, -1 - gammav, alphav, betav, -gammav, Xm, XM)) {
    if (Xm > Sm) Sm = Xm;
    if (XM < SM) SM = XM;
}
else {
    return false;
}
if (findSolution_x(-gammau, alphau, betau, 1 - gammau, -gammauv, alphauv, betauv, 1 - gammauv, Xm, XM)) {
    if (Xm > Sm) Sm = Xm;
    if (XM < SM) SM = XM;
}
else {
    return false;
}
if (findSolution_x(-1 - gammav, alphav, betav, -gammav, -gammauv, alphauv, betauv, 1 - gammauv, Xm, XM)) {
    if (Xm > Sm) Sm = Xm;
    if (XM < SM) SM = XM;
}
else {
    return false;
}
if (Sm > SM)
    return false;
else {
    float delta = (SM - Sm) / 20;
    for (float s = Sm; s <= SM; s += delta) {
        if (findSolution_y(-gammau, alphau, betau, 1 - gammau, -1 - gammav, alphav, betav, -gammav, s, Ym, YM)) {
            if (Ym > Tm) Tm = Ym;
            if (YM < TM) TM = YM;
        }
        else {
            return false;
        }
        if (findSolution_y(-gammau, alphau, betau, 1 - gammau, -gammauv, alphauv, betauv, 1 - gammauv, s, Ym, YM)) {
            if (Ym > Tm) Tm = Ym;
            if (YM < TM) TM = YM;
        }
        else {
            return false;
        }
        if (findSolution_y(-1 - gammav, alphav, betav, -gammav, -gammauv, alphauv, betauv, 1 - gammauv, s, Ym, YM)) {
            if (Ym > Tm) Tm = Ym;
            if (YM < TM) TM = YM;
        }
        else {
            return false;
        }
        if (Tm > TM)
            return false;
        else
            return true;
    }
}

return false;}

solve_x

bool Octree::findSolution_x(float m, float a, float b, float n, float M, float A, float B, float N, float& Xm, float& XM) {

const float epsilon = 0.00001;

float denom = (a*B- A* b);

float Sm1, SM1;

Sm1 = (m* B- N* b);
SM1 = (n* B- M* b);

if (b< 0 || B< 0) {
    Sm1 *= -1;
    SM1 *= -1;
}

Sm1 /= denom;
SM1 /= denom;

float Sm1Rounded = round(Sm1);
float SM1Rounded = round(SM1);

if (abs(Sm1Rounded - Sm1 <= epsilon)) Sm1 = Sm1Rounded;
if (abs(SM1Rounded - SM1 <= epsilon)) SM1 = SM1Rounded;

Xm = Sm1;
XM = SM1;

if (denom == 0) {
    Xm *= -1;
}

return true;}

solve_y

bool Octree::findSolution_y(float m, float a, float b, float n, float M, float A, float B, float N, float x, float& Ym, float& YM) {

const float epsilon = 0.00001;

float Sm1, SM1, Sm2, SM2;

Sm1 = m- (a* x);
Sm2 = M- (A* x);
SM1 = n- (a* x);
SM2 = N- (A* x);

if (b< 0 || B< 0) {
    Sm1 *= -1;
    SM1 *= -1;
    Sm2 *= -1;
    SM2 *= -1;
}

if (Sm1 > SM1 || Sm2 > SM2) return false;

Sm1 /= b;
SM1 /= b;
Sm2 /= B;
SM2 /= B;

float Sm1Rounded = round(Sm1);
float SM1Rounded = round(SM1);
float Sm2Rounded = round(Sm2);
float SM2Rounded = round(SM2);

if (abs(Sm1Rounded - Sm1 <= epsilon)) Sm1 = Sm1Rounded;
if (abs(SM1Rounded - SM1 <= epsilon)) SM1 = SM1Rounded;
if (abs(Sm2Rounded - Sm2 <= epsilon)) Sm2 = Sm2Rounded;
if (abs(SM2Rounded - SM2 <= epsilon)) SM2 = SM2Rounded;

if (param2 > 0 && param6 > 0) {
    Sm1 >= Sm2 ? Ym = Sm1 : Ym = Sm2;
    SM1 >= SM2 ? YM = SM2 : YM = SM1;
}
else if (param2 > 0) {
    Ym = Sm1;
    YM = SM1;
}
else if (param6 > 0) {
    Ym = Sm2;
    YM = SM2;
}

return true;}

I suspect I've put wrong conditions in one of my ifs but I just followed the guide lines of the paper so I really don't know. Hope you guys can help me.

EDIT: the epsilon is needed to round values below certain error. this is a problem deriving from assimp not reading values of OBJs properly, turning a 1.000000 into 1.0000045 for example.

theguga96
  • 1
  • 1
  • 1
    Please explain what you mean with "somehow I can't implement it". What is wrong with the code you have? – 463035818_is_not_an_ai Aug 16 '19 at 08:53
  • 4
    Welcome to Stack Overflow. Please read [the help pages](http://stackoverflow.com/help), take [the SO tour](http://stackoverflow.com/tour), read about [how to ask good questions](http://stackoverflow.com/help/how-to-ask), as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Aug 16 '19 at 08:54
  • it just returns false every time i run the game, although I can see collisions in my scene – theguga96 Aug 16 '19 at 08:56
  • 5
    no offense, but asking to review code that has a function that takes `param0` till `param7` is not really nice. If I was working with you I would refuse to go any further before that is fixed. Give them meaningful names, structure them. Just saying.... – 463035818_is_not_an_ai Aug 16 '19 at 09:00
  • 1
    What did you observe in debugger? Concerning rounding issues, these will be always there, especially because `float` is still preferred for vertex data (as there is usually a lot of that). If you don't try to simulate "galaxy systems", that's usually tolerable. (Or in other words, you should be aware of that and don't test too much "on edge".) – Scheff's Cat Aug 16 '19 at 09:01
  • @formerlyknownas_463035818 i see your point i'll rename them as the paper did so it should be easier. – theguga96 Aug 16 '19 at 09:02
  • @Scheff ok i'll remove those checks. for what I've seen in the debugger, I don't know what I've should seen. I mean I've did the math by hand the results are correct . I think the problem are my ifs... now that I think of it, I din't check the results when a collision should occurr. I'll do it now – theguga96 Aug 16 '19 at 09:11
  • 1
    hm sorry for insisting, but I think you didnt get my point. Good names tell you something about the meaning of the variable, `m`,`a`,... is just as meaningful as `param0`, `param1`, though I dont want to get on your nerves, the real problem is of course something else...(though it would be easier to find if the code would be more readable) – 463035818_is_not_an_ai Aug 16 '19 at 09:11
  • @formerlyknownas_463035818 ok i did it. the letters in lowercase are the coeff of the first inequality and those in uppercase are the coeff for the second inequality. – theguga96 Aug 16 '19 at 09:14
  • then may I suggest you to declare a `struct coeffs { float a,b,m,n; }` (what do those numbers mean? name them accordingly) and pass something like `coeffs_for_first_inequality` and `coeffs_for_second_inequality` to the function. Using proper names saves you a lot of explaining. In the best case only by reading the code one can understand it. Note that the original authors spend a >20 pages paper to explain their code, so probably they didnt care to much to make the code itself readable, but you can safe lots of explanation by making the code selfdocumenting – 463035818_is_not_an_ai Aug 16 '19 at 09:17
  • 1
    The code is now broken. There are still `param2` etc. in `findSolution_y`. I also question why there is collision detection or triangle intersection code inside the `Octree` class - that's totally outside the business of an octree. Beyond that, there is not much we can do here other than look for typos. To debug this, you should create a very simple scenario, do the math from the paper **by hand** for this scenario, and then step through your code in a debugger to see where it deviates from your by-hand math (and thus the paper). That's where the bug is. – Max Langhof Aug 16 '19 at 09:21
  • 3
    Set up some test-cases with known triangles both colliding and not. Single-step the code in your debugger. Fix the problems. – Richard Critten Aug 16 '19 at 09:27
  • @formerlyknownas_463035818 I often use single letter variables in such cases. They correspond to formulas in some articles, my own ones when I invented the algorithm, or third-party if I'm implementing someone's else. Math formulas don't became higher quality with longer variables, the opposite is true, they become unreadable because names are too long. It's easier to correlate C++ with PDF when C++ variable names are matching the math symbols in PDF. – Soonts Aug 16 '19 at 11:29
  • @Soonts I understand your reasoning but I dont agree. Code is not a mathematical formula. I use single letter variable names myself all the time in code, but only if (!) their scope is limited to a couple of lines eg in a function that is 3 lines long. For a function that is this long there is no good excuse to use cryptic names on the interface (if you insist on single letters for writing formulas you can still use references, as in `auto& x = some_meaningful_but_long_name;` to have the formula dense and readable) – 463035818_is_not_an_ai Aug 16 '19 at 11:32
  • @Soonts also consider that when implementing a general mathematical formula often `x` is indeed the most meaningful name you can give. This is however not the case here, where eg `m` definitely can have a better name – 463035818_is_not_an_ai Aug 16 '19 at 11:34
  • Hello new contributor. Ill be gentle :D So, i will presume that you have basic linear algebra knowledge. Intersection between triangles is defined by 2 elements, intersection between base planes of the triangles which is an determinant, and type of intersection ( point or line ). To define an plane interception normals of the coord planes must be such that normals behind those planes isn't collinear - if this condition is true, there is intersection somewhere. Then you can calculate angle of plane normals and see the type of intersection. – Danilo Aug 16 '19 at 12:29
  • Listen there are really a lot of unknown variables in your code, please, do try to comment it at least or structure it so we can know what is what. Other than that, i really can't help you more. Even diving into paper and code will not help me understand your implementation. So, here is what i can do to help you until you re edit your post. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection So as long as `dot( -Lab, Cross( xo, yo) )` is not zero , you have intersection. – Danilo Aug 16 '19 at 12:34

1 Answers1

1

I'm not going to try to debug your code for you, and someone is going to downvote me for an incomplete answer, but I'm going to offer some basic advice.

This is basic advice on debugging something this big. In my opinion, you need to set up a simple test. Write a tiny program that links with your code. Create your two triangles manually that you know collide, and then see if your code detects it.

No? Figure out HOW they collide and HOW you should have detected it, and then add print statements to your code where it should be colliding, and see why it isn't catching it.

What you might want to do is use some paper. Lay out a couple of triangles and then manually (no computer involved) step through the code you're using and see if it makes sense.

And if it doesn't, come up with your own code. I think you could define triangle collision as:

  1. If any segment of T1 intersects with any segment of T2. You should be able to figure out a way of testing if two line segments intersect, and then just run all segments of T1 against T2.

  2. OR if one triangle is entirely encapsulated inside the other. A big triangle and a little triangle.

This is part of the joy and frustration of coding -- learning to understand the algorithms you're using.

Joseph Larson
  • 8,530
  • 1
  • 19
  • 36