package com.pixldevelopment.terminal; import com.pixldevelopment.wastelandengine.other.math.Vector.Vector3; /** * A ray and triangle intersection test example. * * @author simengangstad * @since 11.10.13 */ public class RayTriangleIntersectionTest { /** * Main staring point. * * @param args The run arguments. */ public static void main(String[] args) { new RayTriangleIntersectionTest(); } /** * Initializes the RayTriangleIntersectionTest. */ public RayTriangleIntersectionTest() { Vector3 v1 = new Vector3(-1.0f, 0.0f, 0.0f); Vector3 v2 = new Vector3(1.0f, 0.0f, 0.0f); Vector3 v3 = new Vector3(0.0f, 1.0f, 0.0f); System.out.println(this.checkIntersection(v1, v2, v3, new Ray(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(0.0f, 0.0f, -1.0f)))); } /** * Checks intersection between a ray and a triangle. * * @param v1 The first vertex in the triangle. * @param v2 The second vertex in the triangle. * @param v3 The third vertex in the triangle. * @param ray The ray. * * @return If the ray intersects the triangle. */ private boolean checkIntersection(Vector3 v1, Vector3 v2, Vector3 v3, Ray ray) { float t = ray.rayCast(v1, v2, v3); if (t == Float.MAX_VALUE) { // Ray didn't hit anything. return false; } else { // Hit! return true; } } /** * A ray, has a origin and a direction. */ private class Ray { /** * The origin and direction of the ray. */ private Vector3 origin, direction; /** * Initializes the ray with a origin and a direction. * * @param origin The origin. * @param direction the direction. */ public Ray(Vector3 origin, Vector3 direction) { this.origin = origin; this.direction = direction; } /** * @return The origin of the Ray. */ public Vector3 getOrigin() { return this.origin; } /** * Sets the origin. * * @param origin The new origin. */ public void setOrigin(Vector3 origin) { if (!this.origin.equals(origin)) { this.origin = origin; } } /** * @return The direction of the Ray. */ public Vector3 getDirection() { return this.direction; } /** * Sets the direction. * * @param direction The new direction. */ public void setDirection(Vector3 direction) { if (!this.direction.equals(direction)) { this.direction = direction; } } /** * Casts a ray from the origin and across the direction and checks if it hits a triangle. * * @param v1 The first vertex in the triangle. * @param v2 The second vertex in the triangle. * @param v3 The third vertex in the triangle. * * @return The t, how far the triangle is from the origin (in the equation point = origin + t(direction)). Returns Float.MAX_VALUE if no intersection. */ public float rayCast(Vector3 v1, Vector3 v2, Vector3 v3) { float t; if ((t = this.intersectsTriangle(v1, v2, v3)) > 0.0f) // If we hit something, then return the t. { return t; } else // If we didn't hit something, return a value that indicates that we didn't hit something. { return Float.MAX_VALUE; } } /** * Checks if the point is "inside" the given vertices. * * @param v1 First vertex. * @param v2 Second vertex. * @param point The point. * @param normal The normal. * * @return If it's inside. */ private boolean isInside(Vector3 v1, Vector3 v2, Vector3 point, Vector3 normal) { float t0 = (((v2.getY() - v1.getY()) * (point.getZ() - v1.getZ())) - ((point.getY() - v1.getY()) * (v2.getZ() - v1.getZ()))); float t1 = (((v2.getZ() - v1.getZ()) * (point.getX() - v1.getX())) - ((point.getZ() - v1.getZ()) * (v2.getX() - v1.getX()))); float t2 = (((v2.getX() - v1.getX()) * (point.getY() - v1.getY())) - ((point.getX() - v1.getX()) * (v2.getY() - v1.getY()))); float dot = t0 * normal.getX() + t1 * normal.getY() + t2 * normal.getZ(); if(dot < 0.0f) { return false; } else { return true; } } /** * Checks if the ray intersects a triangle. * * @param v1 First vertex. * @param v2 Second vertex. * @param v3 Third vertex. * * @return The t, how far the triangle is from the origin (in the equation point = origin + t(direction)). */ private float intersectsTriangle(Vector3 v1, Vector3 v2, Vector3 v3) { Vector3 normal = new Vector3().computeNormal(v1, v2, v3); float dot = normal.dotProduct(this.direction); if(dot < 0.0f) // Ray and triangle is not parallel. { float t = -(normal.getX() * (this.origin.getX() - v1.getX()) + normal.getY() * (this.origin.getY() - v1.getY()) + normal.getZ() * (this.origin.getZ() - v1.getZ())) / dot; if(t < 0.0f) { return 0.0f; } Vector3 point = new Vector3(this.direction.multiply(t).add(this.origin)); if(this.isInside(v1, v2, point, normal) && this.isInside(v2, v3, point, normal) && this.isInside(v3, v1, point, normal)) { // P is inside triangle. return t; } return 0.0f; } return 0.0f; } } }