[BUG] with Barycentric calculation in Ray

In all definitions I can find of Barycentric coordinates they:

  • Can not be negative numbers
  • Always sum to 1

If you take a look below where w1 & w2 are calculated, you’ll see a third number w0 that is commented out. This looks to be the Barycentric coordinates that are calculated in Ray.class. Even leaving w0 uncommented and only taking a look at w1 & w2, I’m thinking these are wrong, because they do indeed return negative numbers. As far as I can tell, the output always sums to one… but not in a nonsensical manor, like (for instance):

(w0, w1, w2)
(-11.157536, 11.193249, 0.96428657)

You’ll also notice in the comments above w1, w2 this:

// these weights can be used to determine
// interpolated values, such as texture coord.
// eg. texcoord s,t at intersection point:
// s = w0s0 + w1s1 + w2s2;
// t = w0
t0 + w1t1 + w2t2;

Has anyone actually tried doing this? because it WILL NOT produce even remotely close results in its current form.

Here is the method from Ray.class in question:
[java]
private boolean intersects(Vector3f v0, Vector3f v1, Vector3f v2,
Vector3f store, boolean doPlanar, boolean quad) {
TempVars vars = TempVars.get();

    Vector3f tempVa = vars.vect1,
            tempVb = vars.vect2,
            tempVc = vars.vect3,
            tempVd = vars.vect4;

    Vector3f diff = origin.subtract(v0, tempVa);
    Vector3f edge1 = v1.subtract(v0, tempVb);
    Vector3f edge2 = v2.subtract(v0, tempVc);
    Vector3f norm = edge1.cross(edge2, tempVd);

    float dirDotNorm = direction.dot(norm);
    float sign;
    if (dirDotNorm > FastMath.FLT_EPSILON) {
        sign = 1;
    } else if (dirDotNorm < -FastMath.FLT_EPSILON) {
        sign = -1f;
        dirDotNorm = -dirDotNorm;
    } else {
        // ray and triangle/quad are parallel
        vars.release();
        return false;
    }

    float dirDotDiffxEdge2 = sign * direction.dot(diff.cross(edge2, edge2));
    if (dirDotDiffxEdge2 >= 0.0f) {
        float dirDotEdge1xDiff = sign
                * direction.dot(edge1.crossLocal(diff));

        if (dirDotEdge1xDiff >= 0.0f) {
            if (!quad ? dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm : dirDotEdge1xDiff <= dirDotNorm) {
                float diffDotNorm = -sign * diff.dot(norm);
                if (diffDotNorm >= 0.0f) {
                    // this method always returns
                    vars.release();

                    // ray intersects triangle
                    // if storage vector is null, just return true,
                    if (store == null) {
                        return true;
                    }

                    // else fill in.
                    float inv = 1f / dirDotNorm;
                    float t = diffDotNorm * inv;
                    if (!doPlanar) {
                        store.set(origin).addLocal(direction.x * t,
                                direction.y * t, direction.z * t);
                    } else {
                        // these weights can be used to determine
                        // interpolated values, such as texture coord.
                        // eg. texcoord s,t at intersection point:
                        // s = w0*s0 + w1*s1 + w2*s2;
                        // t = w0*t0 + w1*t1 + w2*t2;
                        float w1 = dirDotDiffxEdge2 * inv;
                        float w2 = dirDotEdge1xDiff * inv;
                        //float w0 = 1.0f - w1 - w2;
                        store.set(t, w1, w2);
                    }
                    return true;
                }
            }
        }
    }
    vars.release();
    return false;
}

[/java]

I guess I should mention, when calling a local copy of this to examine the numbers:

I am setting doPlanar to true and quad to false, as well as passing in the origin and direction for the original ray, plus the v1, 2 and 3 from the closest contact.

Do you get true or false as a result in these cases?

…though I guess nothing is stored for the false branches anyway.

Sorry had a terribly busy day yesterday. This is a positive collision that fires off the second check and the second check (calling the local function) returns a positive collision as well. Both return the same contact point

I thought, from Momoko_Fan 's description, this was calculated prior to the returned contact point, but it doesn’t seem to be.

EDIT: Check that… that’s only if doPlanar is false. Otherwise it returns t, w1, w2.

I thought it may be useful to know how I am testing this:

New Project
Rotate the default cube 45, 45, 0
Add RawInputListener and use the picking tutorial code in the onMouseMotion method.

Nothing more than this

After doing a bit of research for other threads relating to resolving texture coordinates from ray casting, it seems this has been a repetitive question since early 2010 with no resolve on any of the threads. I would think that if this wasn’t an issue, that at least one of them would have had a positive resolution. This doesn’t mean that this is proof positive that the issue exists, just a bit strange that in 3+ years no one has successfully gotten this to work.

EDIT: Most of these threads revolved around the “Render Nifty to Texture” example and being able to interact with the rendered results.

Ok… here is a comparison of JME’s Barycentric coords as apposed to two other methods… they were a quick conversion, not taking into account creating new vectors, etc. Here are the results (you’ll notice JME’s differ from the other two)

[java]
JME: (0.008120418, 0.47548336, 0.5163962)
BC1: (0.49115217, 0.1251316, 0.38371623)
BC2: (0.49115217, 0.1251316, 0.38371623)

JME: (0.008120418, 0.47548336, 0.5163962)
BC1: (0.56668615, -0.40701914, 0.840333)
BC2: (0.56668615, -0.40701914, 0.840333)

JME: (0.008120418, 0.47548336, 0.5163962)
BC1: (0.5166718, -0.35700476, 0.840333)
BC2: (0.5166718, -0.35700476, 0.840333)

JME: (0.008120418, 0.47548336, 0.5163962)
BC1: (0.46665692, -0.3069899, 0.840333)
BC2: (0.46665692, -0.3069899, 0.840333)

JME: (0.008120418, 0.47548336, 0.5163962)
BC1: (0.27750123, 0.07378864, 0.64871013)
BC2: (0.27750123, 0.07378864, 0.64871013)
[/java]

And here is one of the methods… the other is a direct swipe of Direct3D’s D3DIntersectTriangle

[java]
public Vector3f getBarycentricCoords(Triangle tri, Vector3f cp) {
Vector3f ret = new Vector3f();
Vector3f v1 = new Vector3f(tri.get2()).subtractLocal(tri.get1());
Vector3f v2 = new Vector3f(tri.get3()).subtractLocal(tri.get1());
Vector3f v3 = new Vector3f(cp).subtractLocal(tri.get1());
float d00 = v1.dot(v1);
float d01 = v1.dot(v2);
float d11 = v2.dot(v2);
float d20 = v3.dot(v1);
float d21 = v3.dot(v2);
float denom = d00 * d11 - d01 * d01;
float v = (d11 * d20 - d01 * d21) / denom;
float w = (d00 * d21 - d01 * d20) / denom;
float u = 1.0f - v - w;
ret.set(u,v,w);
return ret;
}
[/java]

What’s oddest to me is that the results are always the same for every run of the JME one… like you are passing the same value in every time. Where as the others change for each run. Are you actually passing different parameters each time?

@pspeed
I didn’t notice that… I’m passing in the exact same vectors to each routine. If you want, I can print those first to verify and post the results.

I’ve also tried pulling the vectors directly out of the float buffer to verify that they are correct. All seems right there

EDIT: This is happening from mouse move event… so the input vectors would be rather close in the example above. Though, I don’t see anything in the code that resembles any sort of rounding.

@pspeed
Ok here are the results, I also printed out the check on the verts from both the collision result and pulling them from the float buffer using the contact triangle index:

[java]
Triangle vert 1: (-1.0, 1.0, -1.0)
Buffer vert 1: (-1.0, 1.0, -1.0)
Triangle vert 2: (-1.0, -1.0, -1.0)
Buffer vert 2: (-1.0, -1.0, -1.0)
Triangle vert 3: (-1.0, -1.0, 1.0)
Buffer vert 3: (-1.0, -1.0, 1.0)
Calling methods using: (-1.0, 1.0, -1.0), (-1.0, -1.0, -1.0), (-1.0, -1.0, 1.0)
JME: (0.66071385, 0.18589167, 0.15339448)
BC1: (0.68471265, -0.45522714, 0.7705145)
BC2: (0.68471265, -0.45522714, 0.7705145)

Triangle vert 1: (-1.0, 1.0, 1.0)
Buffer vert 1: (-1.0, 1.0, 1.0)
Triangle vert 2: (-1.0, 1.0, -1.0)
Buffer vert 2: (-1.0, 1.0, -1.0)
Triangle vert 3: (-1.0, -1.0, 1.0)
Buffer vert 3: (-1.0, -1.0, 1.0)
Calling methods using: (-1.0, 1.0, 1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0)
JME: (0.66071385, 0.18589167, 0.15339448)
BC1: (0.32707047, 0.0737896, 0.5991399)
BC2: (0.32707047, 0.0737896, 0.5991399)

Triangle vert 1: (-1.0, 1.0, 1.0)
Buffer vert 1: (-1.0, 1.0, 1.0)
Triangle vert 2: (-1.0, 1.0, -1.0)
Buffer vert 2: (-1.0, 1.0, -1.0)
Triangle vert 3: (-1.0, -1.0, 1.0)
Buffer vert 3: (-1.0, -1.0, 1.0)
Calling methods using: (-1.0, 1.0, 1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0)
JME: (0.66071385, 0.18589167, 0.15339448)
BC1: (0.27596706, 0.10795689, 0.61607605)
BC2: (0.27596706, 0.10795689, 0.61607605)

Triangle vert 1: (-1.0, 1.0, 1.0)
Buffer vert 1: (-1.0, 1.0, 1.0)
Triangle vert 2: (-1.0, 1.0, -1.0)
Buffer vert 2: (-1.0, 1.0, -1.0)
Triangle vert 3: (-1.0, -1.0, 1.0)
Buffer vert 3: (-1.0, -1.0, 1.0)
Calling methods using: (-1.0, 1.0, 1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0)
JME: (0.66071385, 0.18589167, 0.15339448)
BC1: (0.4296639, -0.04390526, 0.61424136)
BC2: (0.4296639, -0.04390526, 0.61424136)
[/java]

EDIT: JME & D3D methods both use the origin and direction from the ray, the other uses the contact point from the original collision.

…what is the ray in each of these cases?

Well… what I figure out thus far is, the reason the numbers returned by intersects appear to be the same is, it’s rare that it meets the conditions set up in the method to calculate the barycentric coords. And since the method requires you to pass in the store, it’s just print out the last successful run.

@pspeed said: ...what is the ray in each of these cases?

The ray origin and direction are being set using the code example from the picking test example. Let me grab it…

[java]
click2d.set(getInputManager().getCursorPosition());
tempV2.set(click2d);
click3d.set(getCamera().getWorldCoordinates(tempV2, 0f));
pickDir.set(getCamera().getWorldCoordinates(tempV2, 1f).subtractLocal(click3d).normalizeLocal());
pickRay.setOrigin(click3d);
pickRay.setDirection(pickDir);
rayResults.clear();
[/java]

@t0neg0d said: Well... what I figure out thus far is, the reason the numbers returned by intersects appear to be the same is, it's rare that it meets the conditions set up in the method to calculate the barycentric coords. And since the method requires you to pass in the store, it's just print out the last successful run.

…which means it isn’t really intersecting, right?.. which means… What is the Ray you are passing in?

If the ray is in world coordinates then I think the triangle would need to also be in world coordinates, etc…

@t0neg0d said: The ray origin and direction are being set using the code example from the picking test example. Let me grab it...

[java]
click2d.set(getInputManager().getCursorPosition());
tempV2.set(click2d);
click3d.set(getCamera().getWorldCoordinates(tempV2, 0f));
pickDir.set(getCamera().getWorldCoordinates(tempV2, 1f).subtractLocal(click3d).normalizeLocal());
pickRay.setOrigin(click3d);
pickRay.setDirection(pickDir);
rayResults.clear();
[/java]

It might be quicker if you show us the rest of the code, too… like the code where you transform the Ray into mesh space or transform the triangle into world space or whatever.

Oh… when I force the calc anyways (which should always be valid, since the collision has already happened), I get all sorts of whacked out numbers like:

[java]
Calling methods using: (-1.0, 1.0, 1.0), (-1.0, 1.0, -1.0), (-1.0, -1.0, 1.0)
JME: (-87.069176, 87.069176, 1.0000037)
BC1: (0.610375, -0.158741, 0.548366)
BC2: (0.610375, -0.158741, 0.548366)
(-61969.805, 0.0)
[/java]

@pspeed said: It might be quicker if you show us the rest of the code, too... like the code where you transform the Ray into mesh space or transform the triangle into world space or whatever.

Um… I feeling like a real fucking tool atm. I had commented out the transforms on the verts and forgot to uncomment them.
[java]
geom.getWorldTransform().transformVector(tri.get1(), v1);
geom.getWorldTransform().transformVector(tri.get2(), v2);
geom.getWorldTransform().transformVector(tri.get3(), v3);
[/java]

Guess what works properly now? Feel free to smack me at any point >.<

Glad you figured it out. This is why keyhole debugging is so frustrating on this end.

1 Like
@pspeed said: Glad you figured it out. This is why keyhole debugging is so frustrating on this end.

Sorry to have taken up anyones time with this. I had been bouncing around so much trying to figure out what I had done wrong while I was calling the wrong method in Ray, that I forgot to undo what I had undone after narrowing down the proper method (thinks to you in the first place). Hah… I should just pay you to write my code for me. lol