1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.jme3.collision; 34 35 import com.jme3.math.*; 36 37 /** 38 * No longer public .. 39 * 40 * @author Kirill Vainer 41 */ 42 @Deprecated 43 class SweepSphere implements Collidable { 44 45 private Vector3f velocity = new Vector3f(); 46 private Vector3f center = new Vector3f(); 47 private Vector3f dimension = new Vector3f(); 48 private Vector3f invDim = new Vector3f(); 49 50 private final Triangle scaledTri = new Triangle(); 51 private final Plane triPlane = new Plane(); 52 private final Vector3f temp1 = new Vector3f(), 53 temp2 = new Vector3f(), 54 temp3 = new Vector3f(); 55 private final Vector3f sVelocity = new Vector3f(), 56 sCenter = new Vector3f(); 57 getCenter()58 public Vector3f getCenter() { 59 return center; 60 } 61 setCenter(Vector3f center)62 public void setCenter(Vector3f center) { 63 this.center.set(center); 64 } 65 getDimension()66 public Vector3f getDimension() { 67 return dimension; 68 } 69 setDimension(Vector3f dimension)70 public void setDimension(Vector3f dimension) { 71 this.dimension.set(dimension); 72 this.invDim.set(1,1,1).divideLocal(dimension); 73 } 74 setDimension(float x, float y, float z)75 public void setDimension(float x, float y, float z){ 76 this.dimension.set(x,y,z); 77 this.invDim.set(1,1,1).divideLocal(dimension); 78 } 79 setDimension(float dim)80 public void setDimension(float dim){ 81 this.dimension.set(dim, dim, dim); 82 this.invDim.set(1,1,1).divideLocal(dimension); 83 } 84 getVelocity()85 public Vector3f getVelocity() { 86 return velocity; 87 } 88 setVelocity(Vector3f velocity)89 public void setVelocity(Vector3f velocity) { 90 this.velocity.set(velocity); 91 } 92 pointsOnSameSide(Vector3f p1, Vector3f p2, Vector3f line1, Vector3f line2)93 private boolean pointsOnSameSide(Vector3f p1, Vector3f p2, Vector3f line1, Vector3f line2) { 94 // V1 = (line2 - line1) x (p1 - line1) 95 // V2 = (p2 - line1) x (line2 - line1) 96 97 temp1.set(line2).subtractLocal(line1); 98 temp3.set(temp1); 99 temp2.set(p1).subtractLocal(line1); 100 temp1.crossLocal(temp2); 101 102 temp2.set(p2).subtractLocal(line1); 103 temp3.crossLocal(temp2); 104 105 // V1 . V2 >= 0 106 return temp1.dot(temp3) >= 0; 107 } 108 isPointInTriangle(Vector3f point, AbstractTriangle tri)109 private boolean isPointInTriangle(Vector3f point, AbstractTriangle tri) { 110 if (pointsOnSameSide(point, tri.get1(), tri.get2(), tri.get3()) 111 && pointsOnSameSide(point, tri.get2(), tri.get1(), tri.get3()) 112 && pointsOnSameSide(point, tri.get3(), tri.get1(), tri.get2())) 113 return true; 114 return false; 115 } 116 getLowestRoot(float a, float b, float c, float maxR)117 private static float getLowestRoot(float a, float b, float c, float maxR) { 118 float determinant = b * b - 4f * a * c; 119 if (determinant < 0){ 120 return Float.NaN; 121 } 122 123 float sqrtd = FastMath.sqrt(determinant); 124 float r1 = (-b - sqrtd) / (2f * a); 125 float r2 = (-b + sqrtd) / (2f * a); 126 127 if (r1 > r2){ 128 float temp = r2; 129 r2 = r1; 130 r1 = temp; 131 } 132 133 if (r1 > 0 && r1 < maxR){ 134 return r1; 135 } 136 137 if (r2 > 0 && r2 < maxR){ 138 return r2; 139 } 140 141 return Float.NaN; 142 } 143 collideWithVertex(Vector3f sCenter, Vector3f sVelocity, float velocitySquared, Vector3f vertex, float t)144 private float collideWithVertex(Vector3f sCenter, Vector3f sVelocity, 145 float velocitySquared, Vector3f vertex, float t) { 146 // A = velocity * velocity 147 // B = 2 * (velocity . (center - vertex)); 148 // C = ||vertex - center||^2 - 1f; 149 150 temp1.set(sCenter).subtractLocal(vertex); 151 float a = velocitySquared; 152 float b = 2f * sVelocity.dot(temp1); 153 float c = temp1.negateLocal().lengthSquared() - 1f; 154 float newT = getLowestRoot(a, b, c, t); 155 156 // float A = velocitySquared; 157 // float B = sCenter.subtract(vertex).dot(sVelocity) * 2f; 158 // float C = vertex.subtract(sCenter).lengthSquared() - 1f; 159 // 160 // float newT = getLowestRoot(A, B, C, Float.MAX_VALUE); 161 // if (newT > 1.0f) 162 // newT = Float.NaN; 163 164 return newT; 165 } 166 collideWithSegment(Vector3f sCenter, Vector3f sVelocity, float velocitySquared, Vector3f l1, Vector3f l2, float t, Vector3f store)167 private float collideWithSegment(Vector3f sCenter, 168 Vector3f sVelocity, 169 float velocitySquared, 170 Vector3f l1, 171 Vector3f l2, 172 float t, 173 Vector3f store) { 174 Vector3f edge = temp1.set(l2).subtractLocal(l1); 175 Vector3f base = temp2.set(l1).subtractLocal(sCenter); 176 177 float edgeSquared = edge.lengthSquared(); 178 float baseSquared = base.lengthSquared(); 179 180 float EdotV = edge.dot(sVelocity); 181 float EdotB = edge.dot(base); 182 183 float a = (edgeSquared * -velocitySquared) + EdotV * EdotV; 184 float b = (edgeSquared * 2f * sVelocity.dot(base)) 185 - (2f * EdotV * EdotB); 186 float c = (edgeSquared * (1f - baseSquared)) + EdotB * EdotB; 187 float newT = getLowestRoot(a, b, c, t); 188 if (!Float.isNaN(newT)){ 189 float f = (EdotV * newT - EdotB) / edgeSquared; 190 if (f >= 0f && f < 1f){ 191 store.scaleAdd(f, edge, l1); 192 return newT; 193 } 194 } 195 return Float.NaN; 196 } 197 collideWithTriangle(AbstractTriangle tri)198 private CollisionResult collideWithTriangle(AbstractTriangle tri){ 199 // scale scaledTriangle based on dimension 200 scaledTri.get1().set(tri.get1()).multLocal(invDim); 201 scaledTri.get2().set(tri.get2()).multLocal(invDim); 202 scaledTri.get3().set(tri.get3()).multLocal(invDim); 203 // Vector3f sVelocity = velocity.mult(invDim); 204 // Vector3f sCenter = center.mult(invDim); 205 velocity.mult(invDim, sVelocity); 206 center.mult(invDim, sCenter); 207 208 triPlane.setPlanePoints(scaledTri); 209 210 float normalDotVelocity = triPlane.getNormal().dot(sVelocity); 211 // XXX: sVelocity.normalize() needed? 212 // back facing scaledTriangles not considered 213 if (normalDotVelocity > 0f) 214 return null; 215 216 float t0, t1; 217 boolean embedded = false; 218 219 float signedDistanceToPlane = triPlane.pseudoDistance(sCenter); 220 if (normalDotVelocity == 0.0f){ 221 // we are travelling exactly parrallel to the plane 222 if (FastMath.abs(signedDistanceToPlane) >= 1.0f){ 223 // no collision possible 224 return null; 225 }else{ 226 // we are embedded 227 t0 = 0; 228 t1 = 1; 229 embedded = true; 230 System.out.println("EMBEDDED"); 231 return null; 232 } 233 }else{ 234 t0 = (-1f - signedDistanceToPlane) / normalDotVelocity; 235 t1 = ( 1f - signedDistanceToPlane) / normalDotVelocity; 236 237 if (t0 > t1){ 238 float tf = t1; 239 t1 = t0; 240 t0 = tf; 241 } 242 243 if (t0 > 1.0f || t1 < 0.0f){ 244 // collision is out of this sVelocity range 245 return null; 246 } 247 248 // clamp the interval to [0, 1] 249 t0 = Math.max(t0, 0.0f); 250 t1 = Math.min(t1, 1.0f); 251 } 252 253 boolean foundCollision = false; 254 float minT = 1f; 255 256 Vector3f contactPoint = new Vector3f(); 257 Vector3f contactNormal = new Vector3f(); 258 259 // if (!embedded){ 260 // check against the inside of the scaledTriangle 261 // contactPoint = sCenter - p.normal + t0 * sVelocity 262 contactPoint.set(sVelocity); 263 contactPoint.multLocal(t0); 264 contactPoint.addLocal(sCenter); 265 contactPoint.subtractLocal(triPlane.getNormal()); 266 267 // test to see if the collision is on a scaledTriangle interior 268 if (isPointInTriangle(contactPoint, scaledTri) && !embedded){ 269 foundCollision = true; 270 271 minT = t0; 272 273 // scale collision point back into R3 274 contactPoint.multLocal(dimension); 275 contactNormal.set(velocity).multLocal(t0); 276 contactNormal.addLocal(center); 277 contactNormal.subtractLocal(contactPoint).normalizeLocal(); 278 279 // contactNormal.set(triPlane.getNormal()); 280 281 CollisionResult result = new CollisionResult(); 282 result.setContactPoint(contactPoint); 283 result.setContactNormal(contactNormal); 284 result.setDistance(minT * velocity.length()); 285 return result; 286 } 287 // } 288 289 float velocitySquared = sVelocity.lengthSquared(); 290 291 Vector3f v1 = scaledTri.get1(); 292 Vector3f v2 = scaledTri.get2(); 293 Vector3f v3 = scaledTri.get3(); 294 295 // vertex 1 296 float newT; 297 newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v1, minT); 298 if (!Float.isNaN(newT)){ 299 minT = newT; 300 contactPoint.set(v1); 301 foundCollision = true; 302 } 303 304 // vertex 2 305 newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v2, minT); 306 if (!Float.isNaN(newT)){ 307 minT = newT; 308 contactPoint.set(v2); 309 foundCollision = true; 310 } 311 312 // vertex 3 313 newT = collideWithVertex(sCenter, sVelocity, velocitySquared, v3, minT); 314 if (!Float.isNaN(newT)){ 315 minT = newT; 316 contactPoint.set(v3); 317 foundCollision = true; 318 } 319 320 // edge 1-2 321 newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v1, v2, minT, contactPoint); 322 if (!Float.isNaN(newT)){ 323 minT = newT; 324 foundCollision = true; 325 } 326 327 // edge 2-3 328 newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v2, v3, minT, contactPoint); 329 if (!Float.isNaN(newT)){ 330 minT = newT; 331 foundCollision = true; 332 } 333 334 // edge 3-1 335 newT = collideWithSegment(sCenter, sVelocity, velocitySquared, v3, v1, minT, contactPoint); 336 if (!Float.isNaN(newT)){ 337 minT = newT; 338 foundCollision = true; 339 } 340 341 if (foundCollision){ 342 // compute contact normal based on minimum t 343 contactPoint.multLocal(dimension); 344 contactNormal.set(velocity).multLocal(t0); 345 contactNormal.addLocal(center); 346 contactNormal.subtractLocal(contactPoint).normalizeLocal(); 347 348 CollisionResult result = new CollisionResult(); 349 result.setContactPoint(contactPoint); 350 result.setContactNormal(contactNormal); 351 result.setDistance(minT * velocity.length()); 352 353 return result; 354 }else{ 355 return null; 356 } 357 } 358 collideWithSweepSphere(SweepSphere other)359 public CollisionResult collideWithSweepSphere(SweepSphere other){ 360 temp1.set(velocity).subtractLocal(other.velocity); 361 temp2.set(center).subtractLocal(other.center); 362 temp3.set(dimension).addLocal(other.dimension); 363 // delta V 364 // delta C 365 // Rsum 366 367 float a = temp1.lengthSquared(); 368 float b = 2f * temp1.dot(temp2); 369 float c = temp2.lengthSquared() - temp3.getX() * temp3.getX(); 370 float t = getLowestRoot(a, b, c, 1); 371 372 // no collision 373 if (Float.isNaN(t)) 374 return null; 375 376 CollisionResult result = new CollisionResult(); 377 result.setDistance(velocity.length() * t); 378 379 temp1.set(velocity).multLocal(t).addLocal(center); 380 temp2.set(other.velocity).multLocal(t).addLocal(other.center); 381 temp3.set(temp2).subtractLocal(temp1); 382 // temp3 contains center to other.center vector 383 384 // normalize it to get normal 385 temp2.set(temp3).normalizeLocal(); 386 result.setContactNormal(new Vector3f(temp2)); 387 388 // temp3 is contact point 389 temp3.set(temp2).multLocal(dimension).addLocal(temp1); 390 result.setContactPoint(new Vector3f(temp3)); 391 392 return result; 393 } 394 main(String[] args)395 public static void main(String[] args){ 396 SweepSphere ss = new SweepSphere(); 397 ss.setCenter(Vector3f.ZERO); 398 ss.setDimension(1); 399 ss.setVelocity(new Vector3f(10, 10, 10)); 400 401 SweepSphere ss2 = new SweepSphere(); 402 ss2.setCenter(new Vector3f(5, 5, 5)); 403 ss2.setDimension(1); 404 ss2.setVelocity(new Vector3f(-10, -10, -10)); 405 406 CollisionResults cr = new CollisionResults(); 407 ss.collideWith(ss2, cr); 408 if (cr.size() > 0){ 409 CollisionResult c = cr.getClosestCollision(); 410 System.out.println("D = "+c.getDistance()); 411 System.out.println("P = "+c.getContactPoint()); 412 System.out.println("N = "+c.getContactNormal()); 413 } 414 } 415 collideWith(Collidable other, CollisionResults results)416 public int collideWith(Collidable other, CollisionResults results) 417 throws UnsupportedCollisionException { 418 if (other instanceof AbstractTriangle){ 419 AbstractTriangle tri = (AbstractTriangle) other; 420 CollisionResult result = collideWithTriangle(tri); 421 if (result != null){ 422 results.addCollision(result); 423 return 1; 424 } 425 return 0; 426 }else if (other instanceof SweepSphere){ 427 SweepSphere sph = (SweepSphere) other; 428 429 CollisionResult result = collideWithSweepSphere(sph); 430 if (result != null){ 431 results.addCollision(result); 432 return 1; 433 } 434 return 0; 435 }else{ 436 throw new UnsupportedCollisionException(); 437 } 438 } 439 440 } 441