Skip to content

Commit d351e60

Browse files
Copilotriccardobl
andcommitted
Add barycentric coordinate support for ray vs. triangle collision
Agent-Logs-Url: https://github.com/jMonkeyEngine/jmonkeyengine/sessions/75a9c930-942a-438a-b24e-9cc4c7daa5a2 Co-authored-by: riccardobl <4943530+riccardobl@users.noreply.github.com>
1 parent 07b6f17 commit d351e60

4 files changed

Lines changed: 350 additions & 4 deletions

File tree

jme3-core/src/main/java/com/jme3/collision/CollisionResult.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
package com.jme3.collision;
3333

3434
import com.jme3.math.Triangle;
35+
import com.jme3.math.Vector2f;
3536
import com.jme3.math.Vector3f;
3637
import com.jme3.scene.Geometry;
3738
import com.jme3.scene.Mesh;
@@ -50,6 +51,7 @@ public class CollisionResult implements Comparable<CollisionResult> {
5051
private Vector3f contactNormal;
5152
private float distance;
5253
private int triangleIndex;
54+
private Vector2f contactBaryCoords;
5355

5456
public CollisionResult(Geometry geometry, Vector3f contactPoint, float distance, int triangleIndex) {
5557
this.geometry = geometry;
@@ -86,6 +88,10 @@ public void setTriangleIndex(int index) {
8688
this.triangleIndex = index;
8789
}
8890

91+
public void setContactBaryCoords(Vector2f baryCoords) {
92+
this.contactBaryCoords = baryCoords;
93+
}
94+
8995
public Triangle getTriangle(Triangle store) {
9096
if (store == null)
9197
store = new Triangle();
@@ -135,13 +141,28 @@ public int getTriangleIndex() {
135141
return triangleIndex;
136142
}
137143

144+
/**
145+
* Returns the barycentric coordinates of the contact point within the
146+
* hit triangle, or null if barycentric coordinates are not available.
147+
*
148+
* <p>The returned vector stores (u, v) where u is the weight of the
149+
* second triangle vertex, v is the weight of the third triangle vertex,
150+
* and the weight of the first triangle vertex is (1 - u - v).
151+
*
152+
* @return the barycentric (u, v) coordinates, or null
153+
*/
154+
public Vector2f getContactBaryCoords() {
155+
return contactBaryCoords;
156+
}
157+
138158
@Override
139159
public String toString() {
140160
return "CollisionResult[geometry=" + geometry
141161
+ ", contactPoint=" + contactPoint
142162
+ ", contactNormal=" + contactNormal
143163
+ ", distance=" + distance
144164
+ ", triangleIndex=" + triangleIndex
165+
+ ", contactBaryCoords=" + contactBaryCoords
145166
+ "]";
146167
}
147168
}

jme3-core/src/main/java/com/jme3/collision/bih/BIHNode.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.jme3.math.Matrix4f;
4040
import com.jme3.math.Ray;
4141
import com.jme3.math.Triangle;
42+
import com.jme3.math.Vector2f;
4243
import com.jme3.math.Vector3f;
4344
import com.jme3.util.TempVars;
4445
import java.io.IOException;
@@ -401,13 +402,14 @@ public final int intersectWhere(Ray r,
401402
for (int i = node.leftIndex; i <= node.rightIndex; i++) {
402403
tree.getTriangle(i, v1, v2, v3);
403404

404-
float t = r.intersects(v1, v2, v3);
405+
Vector2f baryCoords = new Vector2f();
406+
float t = r.intersects(v1, v2, v3, baryCoords);
405407
if (!Float.isInfinite(t)) {
406408
if (worldMatrix != null) {
407409
worldMatrix.mult(v1, v1);
408410
worldMatrix.mult(v2, v2);
409411
worldMatrix.mult(v3, v3);
410-
float t_world = new Ray(o, d).intersects(v1, v2, v3);
412+
float t_world = new Ray(o, d).intersects(v1, v2, v3, baryCoords);
411413
t = t_world;
412414
}
413415

@@ -422,6 +424,7 @@ public final int intersectWhere(Ray r,
422424
CollisionResult cr = new CollisionResult(contactPoint, worldSpaceDist);
423425
cr.setContactNormal(contactNormal);
424426
cr.setTriangleIndex(tree.getTriangleIndex(i));
427+
cr.setContactBaryCoords(baryCoords);
425428
results.addCollision(cr);
426429
cols++;
427430
}

jme3-core/src/main/java/com/jme3/math/Ray.java

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,90 @@ public float intersects(Vector3f v0, Vector3f v1, Vector3f v2) {
336336
return Float.POSITIVE_INFINITY;
337337
}
338338

339+
/**
340+
* Test for an intersection between the ray and the given triangle and,
341+
* if an intersection exists, store the barycentric coordinates of the
342+
* contact point in <code>baryCoords</code>.
343+
*
344+
* <p>The stored (u, v) barycentric coordinates mean: u is the weight
345+
* of <code>v1</code>, v is the weight of <code>v2</code>, and
346+
* (1&nbsp;-&nbsp;u&nbsp;-&nbsp;v) is the weight of <code>v0</code>.
347+
*
348+
* @param v0 first vertex of the triangle (not null, unaffected)
349+
* @param v1 second vertex of the triangle (not null, unaffected)
350+
* @param v2 third vertex of the triangle (not null, unaffected)
351+
* @param baryCoords storage for the barycentric (u, v) coordinates of the
352+
* contact point (modified on hit, may be null)
353+
* @return the distance along the ray to the intersection, or
354+
* {@link Float#POSITIVE_INFINITY} if there is no intersection
355+
*/
356+
public float intersects(Vector3f v0, Vector3f v1, Vector3f v2, Vector2f baryCoords) {
357+
float edge1X = v1.x - v0.x;
358+
float edge1Y = v1.y - v0.y;
359+
float edge1Z = v1.z - v0.z;
360+
361+
float edge2X = v2.x - v0.x;
362+
float edge2Y = v2.y - v0.y;
363+
float edge2Z = v2.z - v0.z;
364+
365+
float normX = ((edge1Y * edge2Z) - (edge1Z * edge2Y));
366+
float normY = ((edge1Z * edge2X) - (edge1X * edge2Z));
367+
float normZ = ((edge1X * edge2Y) - (edge1Y * edge2X));
368+
369+
float dirDotNorm = direction.x * normX + direction.y * normY + direction.z * normZ;
370+
371+
float diffX = origin.x - v0.x;
372+
float diffY = origin.y - v0.y;
373+
float diffZ = origin.z - v0.z;
374+
375+
float sign;
376+
if (dirDotNorm > FastMath.FLT_EPSILON) {
377+
sign = 1;
378+
} else if (dirDotNorm < -FastMath.FLT_EPSILON) {
379+
sign = -1f;
380+
dirDotNorm = -dirDotNorm;
381+
} else {
382+
// ray and triangle/quad are parallel
383+
return Float.POSITIVE_INFINITY;
384+
}
385+
386+
float diffEdge2X = ((diffY * edge2Z) - (diffZ * edge2Y));
387+
float diffEdge2Y = ((diffZ * edge2X) - (diffX * edge2Z));
388+
float diffEdge2Z = ((diffX * edge2Y) - (diffY * edge2X));
389+
390+
float dirDotDiffxEdge2 = sign * (direction.x * diffEdge2X
391+
+ direction.y * diffEdge2Y
392+
+ direction.z * diffEdge2Z);
393+
394+
if (dirDotDiffxEdge2 >= 0.0f) {
395+
diffEdge2X = ((edge1Y * diffZ) - (edge1Z * diffY));
396+
diffEdge2Y = ((edge1Z * diffX) - (edge1X * diffZ));
397+
diffEdge2Z = ((edge1X * diffY) - (edge1Y * diffX));
398+
399+
float dirDotEdge1xDiff = sign * (direction.x * diffEdge2X
400+
+ direction.y * diffEdge2Y
401+
+ direction.z * diffEdge2Z);
402+
403+
if (dirDotEdge1xDiff >= 0.0f) {
404+
if (dirDotDiffxEdge2 + dirDotEdge1xDiff <= dirDotNorm) {
405+
float diffDotNorm = -sign * (diffX * normX + diffY * normY + diffZ * normZ);
406+
if (diffDotNorm >= 0.0f) {
407+
// ray intersects triangle
408+
float inv = 1f / dirDotNorm;
409+
float t = diffDotNorm * inv;
410+
if (baryCoords != null) {
411+
baryCoords.set(dirDotDiffxEdge2 * inv,
412+
dirDotEdge1xDiff * inv);
413+
}
414+
return t;
415+
}
416+
}
417+
}
418+
}
419+
420+
return Float.POSITIVE_INFINITY;
421+
}
422+
339423
/**
340424
* <code>intersectWherePlanar</code> determines if the Ray intersects a
341425
* quad defined by the specified points and if so it stores the point of
@@ -393,13 +477,16 @@ public int collideWith(Collidable other, CollisionResults results) {
393477
return bv.collideWith(this, results);
394478
} else if (other instanceof AbstractTriangle) {
395479
AbstractTriangle tri = (AbstractTriangle) other;
396-
float d = intersects(tri.get1(), tri.get2(), tri.get3());
480+
Vector2f baryCoords = new Vector2f();
481+
float d = intersects(tri.get1(), tri.get2(), tri.get3(), baryCoords);
397482
if (Float.isInfinite(d) || Float.isNaN(d)) {
398483
return 0;
399484
}
400485

401486
Vector3f point = new Vector3f(direction).multLocal(d).addLocal(origin);
402-
results.addCollision(new CollisionResult(point, d));
487+
CollisionResult cr = new CollisionResult(point, d);
488+
cr.setContactBaryCoords(baryCoords);
489+
results.addCollision(cr);
403490
return 1;
404491
} else {
405492
throw new UnsupportedCollisionException();

0 commit comments

Comments
 (0)