Ray collision results in infinite distances

I have spotted the place where the infinity occurs the first time. at Line Nbr. 408 inside the BIHNode.java. It happens here:

        // a leaf
        for (int i = node.leftIndex; i <= node.rightIndex; i++) {
            tree.getTriangle(i, v1, v2, v3);

            float t = r.intersects(v1, v2, v3);
            if (!Float.isInfinite(t)) {
                if (worldMatrix != null) {
                    worldMatrix.mult(v1, v1);
                    worldMatrix.mult(v2, v2);
                    worldMatrix.mult(v3, v3);
                    float t_world = new Ray(o, d).intersects(v1, v2, v3);
                    t = t_world;
                }

The first ray intersection at line 402 seems to succeed with
t = (float) 63.74776
but then the world matrix does the transformation on the triangle vertices:

worldMatrix = (com.jme3.math.Matrix4f) Matrix4f [ 42.2509 -19.02441. 107.99919 0.0
15.904092 56.847427. 30.440855. 0.0
-39.521152 2.5380936. 127.70876. 0.0
0.0 0.0 0.0 1.0
]
the transformed triangle vertices are
v1 = (com.jme3.math.Vector3f) (-69.03165, -63.077835, -56.384964)
v2 = (com.jme3.math.Vector3f) (-61.490913, -63.448433, -46.023514)
v3 = (com.jme3.math.Vector3f) (-58.02646, -64.74267, -48.644794)

After this the ray collision is repeated:
o = (com.jme3.math.Vector3f) (1655.2566, 4107.8115, 1836.2129)
d = (com.jme3.math.Vector3f) (-0.351738, -0.85287726, -0.38584113)

the local output of the triangle check leads to:
t_world = (float) Infinity

Strange: The check before the transformation passes while the check after the transformation fails.

Regards,
Harry

Yes. Sorry. Give me a short amount of time and I’ll write the test case.

By the way: How can I post pictures? Currently it seems to be impossible to do so (error message).

We all use imgur since it has the best features, but any external imagehost really. Im gonna have to fight that demon sooner or later.

Well. I proved us wrong. There’s nothing wrong with anything I can find. It fires a ray in the direction of the cursor, and another one inside the shape. The shape rotates around, and the ground is bumpy. Feel free to let me know if there’s anything else I can do to expose a bug.

https://gist.github.com/jayfella/2683d763b8aa19b4206048bc4d67f51a

  1. The ray is fired to the target from a very distant point (with respect to the size of the geometry which is being hit). so the triangles seem to be very small from the rays origin.

  2. It is quite difficult to reproduce the error for me because I often have to wait several minutes until it happens (randomly).

  3. This happens only for about one frame so it is quite impossible to observe at high FPS with the “naked eye”.

  4. It seems to happen more frequently when the edge of the geometry is being hit (if the geometry is just striped/slightliy touched by the ray).

  5. Maybe this could be also imortant: I do not perform collision checks on node level. I do it only on geometry level. Before performing collision checks on a spacecraft or other object I filter out all non collideable geometries (thruster flames, etc…). Then I store all geometries for collision check within a Map<Spatial, List> where the keys are the spaceships and the lists contain the geometries for collision for each spaceships (The advantage: If the corresponding list is empty no checks are done on this object). Then I check on geometry level directly and store the results in a CollisionResults object.

     public static void checkRayCollision(Ray ray, Map<Spatial, List<Geometry>> potentialTargetList, CollisionResults collisionAccumulator) {
             potentialTargetList.entrySet()
             .stream()
             .forEach(entry -> entry.getValue().forEach(geometry -> {
    
         geometry.collideWith(ray, collisionAccumulator);
     }));
    }
    

The method which is called by the gun inside the updateLoop:

   /**
   * This method updates the collision results.
   *
   * @param ray
   * @return
   */
   private void checkForRayCollisionsAndUpdateResults(Ray ray) {

    results.clear();
    Map<Spatial, List<Geometry>> colliderList = getPossibleVictims();
    Collisions.checkRayCollision(ray, colliderList, results);
   }

I will continues further investigations in my software components.

Except you have exactly all of the parameters you need to setup a test. Position the geometry where it was on the failure, set the ray to what it was on failure, etc… it will happen every time.

Hello guys,

Sorry for the delay. As promised I had deeper investigations on the topic and
I think I have finally found the issue:

After exactly reconstructing the scene and reproducing the collision (@pspeed: Thank you :slight_smile: ) I found out that BIHNode checks the same triangle intersection at line 402 and 408 in local/world coordinate systems. This should lead to the same result but it doesn’t.


A slight difference is visible in inside Ray.java Line 309 which leads to completely different results:
Calling float t = r.intersects(v1, v2, v3); at Line 402 the following behavior occurs:
dirDotDiffxEdge2 = 4.5776367e-5 and the condition >= 0 is satisfied ==> A final distance is returned

At Line 408 the following behavior occurs:
After the transformation the SAME triangle intersection check leads to a different result
dirDotDiffxEdge2 = -0.0078125 and the condition >= 0 is no longer satisfied ==> Infinite distance is returned.

Diving into the values the triangle has the following vertices (before transformation)
v1 -0.65000045, 1.0, -2.0000002
v2 -1.0000004 0.5 -2.00000005
v3 -1.0000019 0.49999958 1.9999995

and after transformation
v1 12.32338 17.959595 -8.236347
v2 15.893312 13.268167 -9.816245
v3 -16.281456 -10.361429 -12.351325


Preliminary workaround:
I changed my ray collision method so that it transforms the collided geometry to the coordinate system origin if the collision result is infinity. Goal is to minimize the transformation overhead inside the BIHNode class for eliminating the rounding issues. Then the difference between Line 402 and 408 becomes sufficiently small. Then I repeat the collision check once more (ugly but it worked quite well for me).

public static void checkRayCollision(Ray ray, Map<Spatial, List<Geometry>> potentialTargetList, CollisionResults collisionAccumulator) {
    potentialTargetList.entrySet()
            .stream()
            .forEach(entry -> entry.getValue().forEach(geometry -> {
        CollisionResults collisionResultsTmp = new CollisionResults();
        geometry.collideWith(ray, collisionResultsTmp);
        boolean invalidResults = false;
        for (CollisionResult cTmp : collisionResultsTmp) {
            if (Float.isInfinite(cTmp.getDistance())) {
                invalidResults = true;
            }
        }
        // If collision check fails we go to Plan B:
        if (invalidResults) {
            Quaternion toIdentRotTmp = geometry.getWorldRotation().inverse();
            Vector3f toIdentShiftTmp = geometry.getWorldTranslation().negate();

            Vector3f originTf = toIdentRotTmp.mult(ray.getOrigin().add(toIdentShiftTmp));
            Vector3f dirTf = toIdentRotTmp.mult(ray.getDirection().add(toIdentShiftTmp));
            geometry.move(toIdentShiftTmp);
            geometry.rotate(toIdentRotTmp);
            System.out.println(geometry + " " + geometry.getWorldRotation() + " " + geometry.getWorldTranslation());

            Ray rayInv = new Ray(originTf, dirTf);

            geometry.collideWith(rayInv, collisionResultsTmp);

            for (CollisionResult colRes : collisionResultsTmp) {
                colRes.setContactPoint(toIdentRotTmp.inverse().mult(colRes.getContactPoint()).subtract(toIdentShiftTmp));
                colRes.setContactNormal(toIdentRotTmp.inverse().mult(colRes.getContactNormal()).subtract(toIdentShiftTmp));
            }
            geometry.rotate(toIdentRotTmp.inverse());
            geometry.move(toIdentShiftTmp.negate());
            System.out.println(geometry + " " + geometry.getWorldRotation() + " " + geometry.getWorldTranslation());
        }
        for (CollisionResult colRes : collisionResultsTmp) {
            if (Float.isFinite(colRes.getDistance())) {
                collisionAccumulator.addCollision(colRes);
            }
        }
    }));
}

I hope I could be of help with these results.

Regards,
Harry

Wow, that is one teeny tiny triangle.

Good sleuthing, by the way. I’m not sure if I can do anything with the information but it’s gold quality info.

Edit: curious, what is that triangle a part of?

Hello guys,

the triangle is a part of the ships hull and is not that small than it appears in the collision transformation.

https://imgur.com/ieG7EMV

compared to non wireframe

Imgur

Regards, Harry

You might extract the triangle and draw it in red. If you’ve only determined which triangle visually then it might be a different one in actuality… since we are in the realm where the math is clearly messing up.

I know that after transformation it seems big enough… but it’s likely the error is actually on the teeny tiny untransformed triangle that detects the hit incorrectly.