1
\$\begingroup\$

I'm having some issues clamping a 3d to barycentric triangle. I've searched for some time now and keep seeing the same results, that (u,v,w) should be clamped between 0 and 1. But when I clamp those values, I'm getting very weird results as it saying it's always intersecting. Maybe I'm just clamping the wrong values or what I'm doing wrong.

The issue is that I'm projecting a point on a normal from a given triangle, then testing if the point is inside the triangle, if it is, test if it's inside the sphere. Everything works great except when the sphere center is no longer above/in front of the triangle, so the spheres edge can pass through the edge of the triangle beings that the spheres center is no longer above it. Any help with how I'm supposed to clamp the position inside of the triangle would be much appreciated.

Here is a snippet of the triangle/sphere intersect method:

public boolean CollidesWithMesh(MeshCollider other)
{
    float sphereRadius = gameObject.scale * (bounds.Scale().Max()) / 2f;
    boolean collided = false;
    RawMesh mesh = other.gameObject.meshRenderer.mesh.rawMesh;

    for(int i = 0; i < mesh.triangles.length; i++)
    {
        //Store the verts used in the triangle
        int first = i++;
        Vector3 vert1 = mesh.vertices[(int) mesh.triangles[first]].Add(other.gameObject.position);
        Vector3 vert2 = mesh.vertices[(int) mesh.triangles[i++]].Add(other.gameObject.position);
        Vector3 vert3 = mesh.vertices[(int) mesh.triangles[i]].Add(other.gameObject.position);

        //Project a point along the normal closest to the spheres center
        Vector3 normal = mesh.normals[(int) mesh.triangles[first]];
        float dot = Vector3.Dot(normal, gameObject.position.Subtract(vert1));
        Vector3 d = normal.Multiply(dot);
        Vector3 point = gameObject.position.Subtract(d);

        //If the point is not inside the triangle, continue
        Vector3 v0 = vert3.Subtract(vert1);
        Vector3 v1 = vert2.Subtract(vert1);
        Vector3 v2 = point.Subtract(vert1);
        float dot00 = Vector3.Dot(v0, v0);
        float dot01 = Vector3.Dot(v0, v1);
        float dot02 = Vector3.Dot(v0, v2);
        float dot11 = Vector3.Dot(v1, v1);
        float dot12 = Vector3.Dot(v1, v2);
        float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
        float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
        float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
        if(!((u >= 0.0) && (v >= 0.0) && (u + v <= 1))) continue;

        //If the sphere does not encapsulates the point, continue
        if(!Contains(point)) continue;

        //Respond to the collision
        Vector3 responseNormal = point.Subtract(gameObject.position).Normalized().Multiply(sphereRadius - (point.Subtract(gameObject.position)).Length());
        Vector3.Subtract(gameObject.position, responseNormal);
        if(normal.y > 0.75) isGrounded = true;
        if(normal.y < -0.75) hitCeiling = true;
        collided = true;
    }
    return collided;
}
\$\endgroup\$
1
\$\begingroup\$

Had a few things backwards. After a couple days of research and toying with the code I finally got it working. Here is the finished method:

public boolean CollidesWithMesh(MeshCollider other)
{
    float sphereRadius = gameObject.scale * (bounds.Scale().Max()) / 2f;
    boolean collided = false;
    RawMesh mesh = other.gameObject.meshRenderer.mesh.rawMesh;

    for(int i = 0; i < mesh.triangles.length; i++)
    {

        //Store the verts used in the triangle
        int first = i++;
        Vector3 normal = mesh.normals[(int) mesh.triangles[first]];
        Vector3 vert1 = mesh.vertices[(int) mesh.triangles[first]].Add(other.gameObject.position);
        Vector3 vert2 = mesh.vertices[(int) mesh.triangles[i++]].Add(other.gameObject.position);
        Vector3 vert3 = mesh.vertices[(int) mesh.triangles[i]].Add(other.gameObject.position);

        //Calculate and clamp the barycentric coordinates
        Vector3 v21 = vert2.Subtract(vert1);
        Vector3 v31 = vert3.Subtract(vert1);
        Vector3 v1 = gameObject.position.Subtract(vert1);
        float dot00 = Vector3.Dot(v21, v21);
        float dot01 = Vector3.Dot(v21, v31);
        float dot11 = Vector3.Dot(v31, v31);
        float dot20 = Vector3.Dot(v1, v21);
        float dot21 = Vector3.Dot(v1, v31);
        float denom = dot00 * dot11 - dot01 * dot01;
        float v = (dot11 * dot20 - dot01 * dot21) / denom;
        float w = (dot00 * dot21 - dot01 * dot20) / denom;
        float u = 1.0f - v - w;
        if(u < 0)
        {
            Vector3 v32 = vert3.Subtract(vert2);
            float t = Vector3.Dot(gameObject.position.Subtract(vert2), v32) / (Vector3.Dot(v32, v32));
            t = Mathf.Clamp(t, 0, 1);
            u = 0.0f; v = 1.0f - t; w = t;
        }
        else if(v < 0)
        {
            Vector3 v13 = vert1.Subtract(vert3);
            float t = Vector3.Dot(gameObject.position.Subtract(vert3), v13) / (Vector3.Dot(v13, v13));
            t = Mathf.Clamp(t, 0, 1);
            u = t; v = 0.0f; w = 1.0f - t;
        }
        else if(w < 0)
        {
            float t = dot20 / dot00;
            t = Mathf.Clamp(t, 0, 1);
            u = 1.0f - t; v = t; w = 0.0f;
        }

        //Convert the barycentric coordinates to world space
        Vector3 point = new Vector3((u * vert1.x) + (v * vert2.x) + (w * vert3.x), (u * vert1.y) + (v * vert2.y) + (w * vert3.y), (u * vert1.z) + (v * vert2.z) + (w * vert3.z));

        //If the sphere does not encapsulates the point, continue
        if(!Contains(point)) continue;

        //Respond to the collision
        Vector3 responseNormal = point.Subtract(gameObject.position).Normalized().Multiply(sphereRadius - (point.Subtract(gameObject.position)).Length());
        Vector3.Subtract(gameObject.position, responseNormal);
        if(normal.y > 0.75) isGrounded = true;
        if(normal.y < -0.75) hitCeiling = true;
        collided = true;

    }
    return collided;
}
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.