/*********************************************************************** * mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***********************************************************************/ package org.mt4j.components.visibleComponents.shapes.mesh; import org.mt4j.util.math.ToolsMath; import org.mt4j.util.math.Ray; import org.mt4j.util.math.ToolsGeometry; import org.mt4j.util.math.Vector3D; import org.mt4j.util.math.Vertex; /** * A triangle class used in meshes. * * @author Christopher Ruff */ public class Triangle { /** The v0. */ public Vertex v0; /** The v1. */ public Vertex v1; /** The v2. */ public Vertex v2; //Pointers into the vertex array /** The P0. */ public int P0; /** The P1. */ public int P1; /** The P2. */ public int P2; /** The normal. */ private Vector3D normal; /** The normal unnormalized. */ private Vector3D normalUnnormalized; /** The center. */ private Vector3D center; /** The normal dirty. */ private boolean normalDirty; /** The center dirty. */ private boolean centerDirty; // private boolean useLocalObjectSpace; /** * The Constructor. * * @param v0 the v0 * @param v1 the v1 * @param v2 the v2 * @param p0 the index p0 * @param p1 the index p1 * @param p2 the index p2 */ public Triangle(Vertex v0, Vertex v1, Vertex v2, int p0, int p1, int p2) { this.v0 = v0; this.v1 = v1; this.v2 = v2; this.P0 = p0; this.P1 = p1; this.P2 = p2; this.normalDirty = true; // this.normal = this.getNormalLocal(); this.normal = null; //lazily initialize them to save memory this.centerDirty = true; // this.center = this.getCenterPointLocal(); this.center = null; // this.useLocalObjectSpace = true; } /** * Sets the. * * @param v0 the v0 * @param v1 the v1 * @param v2 the v2 */ public void set(Vertex v0, Vertex v1, Vertex v2){ this.v0 = v0; this.v1 = v1; this.v2 = v2; this.normalDirty = true; this.centerDirty = true; } /** * Gets the normal obj space. * * @return the normal obj space * * the normal vector of the plane of the triangle */ public Vector3D getNormalLocal(){ this.calcNormal(); return this.normal; } /** * Calc normal. */ private void calcNormal(){ if (normalDirty || this.normal == null || this.normalUnnormalized == null){ this.normal = ToolsGeometry.getNormal(v0, v1, v2, false); //Get unnormalized normal for triangle intersection tests (So degenerate tris get detected) this.normalUnnormalized = normal.getCopy(); this.normal.normalizeLocal(); normalDirty = false; } } /** * Gets the center point local. * * @return the center point local */ public Vector3D getCenterPointLocal(){ this.calcCenterLocal(); return this.center; } /** * Calc center local. */ private void calcCenterLocal(){ if (centerDirty || center == null){ center = this.v0.getCopy(); center.addLocal(v1); center.addLocal(v2); center.scaleLocal(ToolsMath.ONE_THIRD); centerDirty = false; } } /** * Tests if the given ray intersects this triangle. * Returns the intersection point or null if there is none. * * @param r the r * * @return the ray triangle intersection * * intersection vector */ public Vector3D getRayTriangleIntersection(Ray r){ //Update unnormalized normal if neccessary this.calcNormal(); return ToolsGeometry.getRayTriangleIntersection(r, v0, v1, v2, this.normalUnnormalized); } /** * Checks if point vector is inside the triangle created by the points a, b * and c. These points will create a plane and the point checked will have * to be on this plane in the region between a,b,c. * * Note: The triangle must be defined in clockwise order a,b,c * * @param p the p * * @return true, if point is in triangle. */ public boolean containsPoint(Vector3D p) { Vector3D a = p.getSubtracted(v0); a.normalizeLocal(); Vector3D b = p.getSubtracted(v1); b.normalizeLocal(); Vector3D c = p.getSubtracted(v2); c.normalizeLocal(); double total_angles = Math.acos(a.dot(b)); total_angles += Math.acos(b.dot(c)); total_angles += Math.acos(c.dot(a)); return (ToolsMath.abs((float) total_angles - ToolsMath.TWO_PI) <= 0.005f); } /** * Finds and returns the closest point on any of the triangle edges to the * point given. * * @param p point to check * * @return closest point */ public Vector3D getClosestVertexTo(Vector3D p) { Vector3D Rab = getClosestVecToVecOnSegment(p, v0, v1); Vector3D Rbc = getClosestVecToVecOnSegment(p, v1, v2); Vector3D Rca = getClosestVecToVecOnSegment(p, v2, v0); float dAB = p.getSubtracted(Rab).lengthSquared(); float dBC = p.getSubtracted(Rbc).lengthSquared(); float dCA = p.getSubtracted(Rca).lengthSquared(); float min = dAB; Vector3D result = Rab; if (dBC < min) { min = dBC; result = Rbc; } if (dCA < min){ result = Rca; } return result; } /** * Computes the the point on the surface of * triangle closest to the given vector. * * From Real-Time Collision Detection by Christer Ericson, published by * Morgan Kaufmann Publishers, Copyright 2005 Elsevier Inc * * @param p the p * * @return closest point on triangle (result may also be one of v0, v1 or v2) */ public Vector3D getClosestPointOnSurface(Vector3D p) { Vector3D ab = v1.getSubtracted(v0); Vector3D ac = v2.getSubtracted(v0); Vector3D bc = v2.getSubtracted(v1); Vector3D pa = p.getSubtracted(v0); Vector3D pb = p.getSubtracted(v1); Vector3D pc = p.getSubtracted(v2); Vector3D ap = v0.getSubtracted(p); Vector3D bp = v1.getSubtracted(p); Vector3D cp = v2.getSubtracted(p); // Compute parametric position s for projection P' of P on AB, // P' = A + s*AB, s = snom/(snom+sdenom) float snom = pa.dot(ab); float sdenom = pb.dot(v0.getSubtracted(v1)); // Compute parametric position t for projection P' of P on AC, // P' = A + t*AC, s = tnom/(tnom+tdenom) float tnom = pa.dot(ac); float tdenom = pc.dot(v0.getSubtracted(v2)); if (snom <= 0.0f && tnom <= 0.0f) return v0; // Vertex region early out // Compute parametric position u for projection P' of P on BC, // P' = B + u*BC, u = unom/(unom+udenom) float unom = pb.dot(bc); float udenom = pc.dot(v1.getSubtracted(v2)); if (sdenom <= 0.0f && unom <= 0.0f) return v1; // Vertex region early out if (tdenom <= 0.0f && udenom <= 0.0f) return v2; // Vertex region early out // P is outside (or on) AB if the triple scalar product [N PA PB] <= 0 Vector3D n = ab.getCross(ac); float vc = n.dot(ap.crossLocal(bp)); // If P outside AB and within feature region of AB, // return projection of P onto AB if (vc <= 0.0f && snom >= 0.0f && sdenom >= 0.0f) { // return a + snom / (snom + sdenom) * ab; return v0.getAdded(ab.scaleLocal(snom / (snom + sdenom))); } // P is outside (or on) BC if the triple scalar product [N PB PC] <= 0 float va = n.dot(bp.crossLocal(cp)); // If P outside BC and within feature region of BC, // return projection of P onto BC if (va <= 0.0f && unom >= 0.0f && udenom >= 0.0f) { // return b + unom / (unom + udenom) * bc; return v1.getAdded(bc.scaleLocal(unom / (unom + udenom))); } // P is outside (or on) CA if the triple scalar product [N PC PA] <= 0 float vb = n.dot(cp.crossLocal(ap)); // If P outside CA and within feature region of CA, // return projection of P onto CA if (vb <= 0.0f && tnom >= 0.0f && tdenom >= 0.0f) { // return a + tnom / (tnom + tdenom) * ac; return v0.getAdded(ac.scaleLocal(tnom / (tnom + tdenom))); } // P must project inside face region. Compute Q using barycentric // coordinates float u = va / (va + vb + vc); float v = vb / (va + vb + vc); float w = 1.0f - u - v; // = vc / (va + vb + vc) // return u * a + v * b + w * c; return v0.getScaled(u).addLocal(v1.getScaled(v)).addLocal(v2.getScaled(w)); } /** * Checks if is clockwise in xy. * * @return true, if is clockwise in xy */ public boolean isClockwiseInXY() { return Triangle.isClockwiseInXY(v0, v1, v2); } /** * Checks if is clockwise in xz. * * @return true, if is clockwise in xz */ public boolean isClockwiseInXZ() { return Triangle.isClockwiseInXY(v0, v1, v2); } /** * Checks if is clockwise in yz. * * @return true, if is clockwise in yz */ public boolean isClockwiseInYZ() { return Triangle.isClockwiseInXY(v0, v1, v2); } /** * Checks if is clockwise in xy. * * @param v0 the v0 * @param v1 the v1 * @param v2 the v2 * * @return true, if is clockwise in xy */ public static boolean isClockwiseInXY(Vector3D v0, Vector3D v1, Vector3D v2) { float determ = (v1.x - v0.x) * (v2.y - v0.y) - (v2.x - v0.x) * (v1.y - v0.y); return (determ < 0.0); } /** * Checks if is clockwise in xz. * * @param v0 the v0 * @param v1 the v1 * @param v2 the v2 * * @return true, if is clockwise in xz */ public static boolean isClockwiseInXZ(Vector3D v0, Vector3D v1, Vector3D v2) { float determ = (v1.x - v0.x) * (v2.z - v0.z) - (v2.x - v0.x) * (v1.z - v0.z); return (determ < 0.0); } /** * Checks if is clockwise in yz. * * @param v0 the v0 * @param v1 the v1 * @param v2 the v2 * * @return true, if is clockwise in yz */ public static boolean isClockwiseInYZ(Vector3D v0, Vector3D v1, Vector3D v2) { float determ = (v1.y - v0.y) * (v2.z - v0.z) - (v2.y - v0.y) * (v1.z - v0.z); return (determ < 0.0); } /** * Gets the closest vec to vec on segment. * * @param p the point to find the closest point on the segment for * @param segmentStart start point of line segment * @param segmentEnd end point of line segment * * @return closest point on the line segment a -> b */ private Vector3D getClosestVecToVecOnSegment(Vector3D p, Vector3D segmentStart, Vector3D segmentEnd) { Vector3D c = p.getSubtracted(segmentStart); Vector3D v = segmentEnd.getSubtracted(segmentStart); float d = v.length(); v.normalizeLocal(); float t = v.dot(c); // Check to see if t is beyond the extents of the line segment if (t < 0.0f) { return segmentStart; } if (t > d) { return segmentEnd; } // Return the point between 'a' and 'b' // set length of V to t. V is normalized so this is easy v.scaleLocal(t); return segmentStart.getAdded(v); } /* code rewritten to do tests on the sign of the determinant */ /* the division is before the test of the sign of the det */ // int intersect_triangle2( // double orig[3], // double dir[3], // double vert0[3], // double vert1[3], // double vert2[3], // double *t, // double *u, // double *v // ){ // double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; // double det,inv_det; // // /* find vectors for two edges sharing vert0 */ // SUB(edge1, vert1, vert0); // SUB(edge2, vert2, vert0); // // /* begin calculating determinant - also used to calculate U parameter */ // CROSS(pvec, dir, edge2); // // /* if determinant is near zero, ray lies in plane of triangle */ // det = DOT(edge1, pvec); // // /* calculate distance from vert0 to ray origin */ // SUB(tvec, orig, vert0); // inv_det = 1.0 / det; // // if (det > EPSILON) // { // /* calculate U parameter and test bounds */ // *u = DOT(tvec, pvec); // if (*u < 0.0 || *u > det) // return 0; // // /* prepare to test V parameter */ // CROSS(qvec, tvec, edge1); // // /* calculate V parameter and test bounds */ // *v = DOT(dir, qvec); // if (*v < 0.0 || *u + *v > det) // return 0; // // } // else if(det < -EPSILON) // { // /* calculate U parameter and test bounds */ // *u = DOT(tvec, pvec); // if (*u > 0.0 || *u < det) // return 0; // // /* prepare to test V parameter */ // CROSS(qvec, tvec, edge1); // // /* calculate V parameter and test bounds */ // *v = DOT(dir, qvec) ; // if (*v > 0.0 || *u + *v < det) // return 0; // } // else return 0; /* ray is parallell to the plane of the triangle */ // // /* calculate t, ray intersects triangle */ // *t = DOT(edge2, qvec) * inv_det; // (*u) *= inv_det; // (*v) *= inv_det; // // return 1; // } }