| /* |
| * Copyright (c) 2009-2010 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package com.jme3.collision; |
| |
| import com.jme3.math.*; |
| |
| /** |
| * No longer public .. |
| * |
| * @author Kirill Vainer |
| */ |
| @Deprecated |
| class SweepSphere implements Collidable { |
| |
| private Vector3f velocity = new Vector3f(); |
| private Vector3f center = new Vector3f(); |
| private Vector3f dimension = new Vector3f(); |
| private Vector3f invDim = new Vector3f(); |
| |
| private final Triangle scaledTri = new Triangle(); |
| private final Plane triPlane = new Plane(); |
| private final Vector3f temp1 = new Vector3f(), |
| temp2 = new Vector3f(), |
| temp3 = new Vector3f(); |
| private final Vector3f sVelocity = new Vector3f(), |
| sCenter = new Vector3f(); |
| |
| public Vector3f getCenter() { |
| return center; |
| } |
| |
| public void setCenter(Vector3f center) { |
| this.center.set(center); |
| } |
| |
| public Vector3f getDimension() { |
| return dimension; |
| } |
| |
| public void setDimension(Vector3f dimension) { |
| this.dimension.set(dimension); |
| this.invDim.set(1,1,1).divideLocal(dimension); |
| } |
| |
| public void setDimension(float x, float y, float z){ |
| this.dimension.set(x,y,z); |
| this.invDim.set(1,1,1).divideLocal(dimension); |
| } |
| |
| public void setDimension(float dim){ |
| this.dimension.set(dim, dim, dim); |
| this.invDim.set(1,1,1).divideLocal(dimension); |
| } |
| |
| public Vector3f getVelocity() { |
| return velocity; |
| } |
| |
| public void setVelocity(Vector3f velocity) { |
| this.velocity.set(velocity); |
| } |
| |
| private boolean pointsOnSameSide(Vector3f p1, Vector3f p2, Vector3f line1, Vector3f line2) { |
| // V1 = (line2 - line1) x (p1 - line1) |
| // V2 = (p2 - line1) x (line2 - line1) |
| |
| temp1.set(line2).subtractLocal(line1); |
| temp3.set(temp1); |
| temp2.set(p1).subtractLocal(line1); |
| temp1.crossLocal(temp2); |
| |
| temp2.set(p2).subtractLocal(line1); |
| temp3.crossLocal(temp2); |
| |
| // V1 . V2 >= 0 |
| return temp1.dot(temp3) >= 0; |
| } |
| |
| private boolean isPointInTriangle(Vector3f point, AbstractTriangle tri) { |
| if (pointsOnSameSide(point, tri.get1(), tri.get2(), tri.get3()) |
| && pointsOnSameSide(point, tri.get2(), tri.get1(), tri.get3()) |
| && pointsOnSameSide(point, tri.get3(), tri.get1(), tri.get2())) |
| return true; |
| return false; |
| } |
| |
| private static float getLowestRoot(float a, float b, float c, float maxR) { |
| float determinant = b * b - 4f * a * c; |
| if (determinant < 0){ |
| return Float.NaN; |
| } |
| |
| float sqrtd = FastMath.sqrt(determinant); |
| float r1 = (-b - sqrtd) / (2f * a); |
| float r2 = (-b + sqrtd) / (2f * a); |
| |
| if (r1 > r2){ |
| float temp = r2; |
| r2 = r1; |
| r1 = temp; |
| } |
| |
| if (r1 > 0 && r1 < maxR){ |
| return r1; |
| } |
| |
| if (r2 > 0 && r2 < maxR){ |
| return r2; |
| } |
| |
| return Float.NaN; |
| } |
| |
| private float collideWithVertex(Vector3f sCenter, Vector3f sVelocity, |
| float velocitySquared, Vector3f vertex, float t) { |
| // A = velocity * velocity |
| // B = 2 * (velocity . (center - vertex)); |
| // C = ||vertex - center||^2 - 1f; |
| |
| temp1.set(sCenter).subtractLocal(vertex); |
| float a = velocitySquared; |
| float b = 2f * sVelocity.dot(temp1); |
| float c = temp1.negateLocal().lengthSquared() - 1f; |
| float newT = getLowestRoot(a, b, c, t); |
| |
| // float A = velocitySquared; |
| // float B = sCenter.subtract(vertex).dot(sVelocity) * 2f; |
| // float C = vertex.subtract(sCenter).lengthSquared() - 1f; |
| // |
| // float newT = getLowestRoot(A, B, C, Float.MAX_VALUE); |
| // if (newT > 1.0f) |
| // newT = Float.NaN; |
| |
| return newT; |
| } |
| |
| private float collideWithSegment(Vector3f sCenter, |
| Vector3f sVelocity, |
| float velocitySquared, |
| Vector3f l1, |
| Vector3f l2, |
| float t, |
| Vector3f store) { |
| Vector3f edge = temp1.set(l2).subtractLocal(l1); |
| Vector3f base = temp2.set(l1).subtractLocal(sCenter); |
| |
| float edgeSquared = edge.lengthSquared(); |
| float baseSquared = base.lengthSquared(); |
| |
| float EdotV = edge.dot(sVelocity); |
| float EdotB = edge.dot(base); |
| |
| float a = (edgeSquared * -velocitySquared) + EdotV * EdotV; |
| float b = (edgeSquared * 2f * sVelocity.dot(base)) |
| - (2f * EdotV * EdotB); |
| float c = (edgeSquared * (1f - baseSquared)) + EdotB * EdotB; |
| float newT = getLowestRoot(a, b, c, t); |
| if (!Float.isNaN(newT)){ |
| float f = (EdotV * newT - EdotB) / edgeSquared; |
| if (f >= 0f && f < 1f){ |
| store.scaleAdd(f, edge, l1); |
| return newT; |
| } |
| } |
| return Float.NaN; |
| } |
| |
| private CollisionResult collideWithTriangle(AbstractTriangle tri){ |
| // scale scaledTriangle based on dimension |
| scaledTri.get1().set(tri.get1()).multLocal(invDim); |
| scaledTri.get2().set(tri.get2()).multLocal(invDim); |
| scaledTri.get3().set(tri.get3()).multLocal(invDim); |
| // Vector3f sVelocity = velocity.mult(invDim); |
| // Vector3f sCenter = center.mult(invDim); |
| velocity.mult(invDim, sVelocity); |
| center.mult(invDim, sCenter); |
| |
| triPlane.setPlanePoints(scaledTri); |
| |
| float normalDotVelocity = triPlane.getNormal().dot(sVelocity); |
| // XXX: sVelocity.normalize() needed? |
| // back facing scaledTriangles not considered |
| if (normalDotVelocity > 0f) |
| return null; |
| |
| float t0, t1; |
| boolean embedded = false; |
| |
| float signedDistanceToPlane = triPlane.pseudoDistance(sCenter); |
| if (normalDotVelocity == 0.0f){ |
| // we are travelling exactly parrallel to the plane |
| if (FastMath.abs(signedDistanceToPlane) >= 1.0f){ |
| // no collision possible |
| return null; |
| }else{ |
| // we are embedded |
| t0 = 0; |
| t1 = 1; |
| embedded = true; |
| System.out.println("EMBEDDED"); |
| return null; |
| } |
| }else{ |
| t0 = (-1f - signedDistanceToPlane) / normalDotVelocity; |
| t1 = ( 1f - signedDistanceToPlane) / normalDotVelocity; |
| |
| if (t0 > t1){ |
| float tf = t1; |
| t1 = t0; |
| t0 = tf; |
| } |
| |
| if (t0 > 1.0f || t1 < 0.0f){ |
| // collision is out of this sVelocity range |
| return null; |
| } |
| |
| // clamp the interval to [0, 1] |
| t0 = Math.max(t0, 0.0f); |
| t1 = Math.min(t1, 1.0f); |
| } |
| |
| boolean foundCollision = false; |
| float minT = 1f; |
| |
| Vector3f contactPoint = new Vector3f(); |
| Vector3f contactNormal = new Vector3f(); |
| |
| // if (!embedded){ |
| // check against the inside of the scaledTriangle |
| // contactPoint = sCenter - p.normal + t0 * sVelocity |
| contactPoint.set(sVelocity); |
| contactPoint.multLocal(t0); |
| contactPoint.addLocal(sCenter); |
| contactPoint.subtractLocal(triPlane.getNormal()); |
| |
| // test to see if the collision is on a scaledTriangle interior |
| if (isPointInTriangle(contactPoint, scaledTri) && !embedded){ |
| foundCollision = true; |
| |
| minT = t0; |
| |
| // scale collision point back into R3 |
| contactPoint.multLocal(dimension); |
| contactNormal.set(velocity).multLocal(t0); |
| contactNormal.addLocal(center); |
| contactNormal.subtractLocal(contactPoint).normalizeLocal(); |
| |
| // contactNormal.set(triPlane.getNormal()); |
| |
| CollisionResult result = new CollisionResult(); |
| result.setContactPoint(contactPoint); |
| result.setContactNormal(contactNormal); |
| result.setDistance(minT * velocity.length()); |
| return result; |
| } |
| // } |
| |
| float velocitySquared = sVelocity.lengthSquared(); |
| |
| Vector3f v1 = scaledTri.get1(); |
| Vector3f v2 = scaledTri.get2(); |
| Vector3f v3 = scaledTri.get3(); |
| |
| // vertex 1 |
| float newT; |
| newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v1, minT); |
| if (!Float.isNaN(newT)){ |
| minT = newT; |
| contactPoint.set(v1); |
| foundCollision = true; |
| } |
| |
| // vertex 2 |
| newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v2, minT); |
| if (!Float.isNaN(newT)){ |
| minT = newT; |
| contactPoint.set(v2); |
| foundCollision = true; |
| } |
| |
| // vertex 3 |
| newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v3, minT); |
| if (!Float.isNaN(newT)){ |
| minT = newT; |
| contactPoint.set(v3); |
| foundCollision = true; |
| } |
| |
| // edge 1-2 |
| newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v1, v2, minT, contactPoint); |
| if (!Float.isNaN(newT)){ |
| minT = newT; |
| foundCollision = true; |
| } |
| |
| // edge 2-3 |
| newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v2, v3, minT, contactPoint); |
| if (!Float.isNaN(newT)){ |
| minT = newT; |
| foundCollision = true; |
| } |
| |
| // edge 3-1 |
| newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v3, v1, minT, contactPoint); |
| if (!Float.isNaN(newT)){ |
| minT = newT; |
| foundCollision = true; |
| } |
| |
| if (foundCollision){ |
| // compute contact normal based on minimum t |
| contactPoint.multLocal(dimension); |
| contactNormal.set(velocity).multLocal(t0); |
| contactNormal.addLocal(center); |
| contactNormal.subtractLocal(contactPoint).normalizeLocal(); |
| |
| CollisionResult result = new CollisionResult(); |
| result.setContactPoint(contactPoint); |
| result.setContactNormal(contactNormal); |
| result.setDistance(minT * velocity.length()); |
| |
| return result; |
| }else{ |
| return null; |
| } |
| } |
| |
| public CollisionResult collideWithSweepSphere(SweepSphere other){ |
| temp1.set(velocity).subtractLocal(other.velocity); |
| temp2.set(center).subtractLocal(other.center); |
| temp3.set(dimension).addLocal(other.dimension); |
| // delta V |
| // delta C |
| // Rsum |
| |
| float a = temp1.lengthSquared(); |
| float b = 2f * temp1.dot(temp2); |
| float c = temp2.lengthSquared() - temp3.getX() * temp3.getX(); |
| float t = getLowestRoot(a, b, c, 1); |
| |
| // no collision |
| if (Float.isNaN(t)) |
| return null; |
| |
| CollisionResult result = new CollisionResult(); |
| result.setDistance(velocity.length() * t); |
| |
| temp1.set(velocity).multLocal(t).addLocal(center); |
| temp2.set(other.velocity).multLocal(t).addLocal(other.center); |
| temp3.set(temp2).subtractLocal(temp1); |
| // temp3 contains center to other.center vector |
| |
| // normalize it to get normal |
| temp2.set(temp3).normalizeLocal(); |
| result.setContactNormal(new Vector3f(temp2)); |
| |
| // temp3 is contact point |
| temp3.set(temp2).multLocal(dimension).addLocal(temp1); |
| result.setContactPoint(new Vector3f(temp3)); |
| |
| return result; |
| } |
| |
| public static void main(String[] args){ |
| SweepSphere ss = new SweepSphere(); |
| ss.setCenter(Vector3f.ZERO); |
| ss.setDimension(1); |
| ss.setVelocity(new Vector3f(10, 10, 10)); |
| |
| SweepSphere ss2 = new SweepSphere(); |
| ss2.setCenter(new Vector3f(5, 5, 5)); |
| ss2.setDimension(1); |
| ss2.setVelocity(new Vector3f(-10, -10, -10)); |
| |
| CollisionResults cr = new CollisionResults(); |
| ss.collideWith(ss2, cr); |
| if (cr.size() > 0){ |
| CollisionResult c = cr.getClosestCollision(); |
| System.out.println("D = "+c.getDistance()); |
| System.out.println("P = "+c.getContactPoint()); |
| System.out.println("N = "+c.getContactNormal()); |
| } |
| } |
| |
| public int collideWith(Collidable other, CollisionResults results) |
| throws UnsupportedCollisionException { |
| if (other instanceof AbstractTriangle){ |
| AbstractTriangle tri = (AbstractTriangle) other; |
| CollisionResult result = collideWithTriangle(tri); |
| if (result != null){ |
| results.addCollision(result); |
| return 1; |
| } |
| return 0; |
| }else if (other instanceof SweepSphere){ |
| SweepSphere sph = (SweepSphere) other; |
| |
| CollisionResult result = collideWithSweepSphere(sph); |
| if (result != null){ |
| results.addCollision(result); |
| return 1; |
| } |
| return 0; |
| }else{ |
| throw new UnsupportedCollisionException(); |
| } |
| } |
| |
| } |