blob: 9d43f8909ca8ba8f2c4b0ed0906ebd078a7260f9 [file] [log] [blame]
/*
* 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();
}
}
}