/* * JBox2D - A Java Port of Erin Catto's Box2D * * JBox2D homepage: http://jbox2d.sourceforge.net/ * Box2D homepage: http://www.box2d.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ package org.jbox2d.collision; import org.jbox2d.collision.shapes.Shape; import org.jbox2d.common.MathUtils; import org.jbox2d.common.Settings; import org.jbox2d.common.Sweep; import org.jbox2d.common.Vec2; import org.jbox2d.common.XForm; import org.jbox2d.pooling.SingletonPool; import org.jbox2d.pooling.TLVec2; import org.jbox2d.pooling.TLXForm; //updated to rev 142 of b2TimeOfImpact.cpp /** Handles conservative advancement to compute time of impact between shapes. */ public class TOI { // This algorithm uses conservative advancement to compute the time of // impact (TOI) of two shapes. // Refs: Bullet, Young Kim // djm pooling private static final TLXForm tlxf1 = new TLXForm(); private static final TLXForm tlxf2 = new TLXForm(); private static final TLVec2 tlP1 = new TLVec2(); private static final TLVec2 tlP2 = new TLVec2(); /** * Compute the time when two shapes begin to touch or touch at a closer distance. * <BR><BR><em>Warning</em>: the sweeps must have the same time interval. * @return the fraction between [0,1] in which the shapes first touch. * fraction=0 means the shapes begin touching/overlapped, and fraction=1 means the shapes don't touch. */ public static final float timeOfImpact(final Shape shape1, final Sweep sweep1, final Shape shape2, final Sweep sweep2) { final XForm xf1 = tlxf1.get(); final XForm xf2 = tlxf2.get(); final Vec2 p1 = tlP1.get(); final Vec2 p2 = tlP2.get(); final float r1 = shape1.getSweepRadius(); final float r2 = shape2.getSweepRadius(); assert(sweep1.t0 == sweep2.t0); assert(1.0f - sweep1.t0 > Settings.EPSILON); final float t0 = sweep1.t0; // INLINED //Vec2 v1 = sweep1.c.sub(sweep1.c0); //Vec2 v2 = sweep2.c.sub(sweep2.c0); //Vec2 v = v1.sub(v2); final float vx = (sweep1.c.x - sweep1.c0.x) - (sweep2.c.x - sweep2.c0.x); final float vy = (sweep1.c.y - sweep1.c0.y) - (sweep2.c.y - sweep2.c0.y); final float omega1 = sweep1.a - sweep1.a0; final float omega2 = sweep2.a - sweep2.a0; float alpha = 0.0f; final int k_maxIterations = 20; // TODO_ERIN b2Settings int iter = 0; float distance = 0.0f; float targetDistance = 0.0f; while(true){ final float t = (1.0f - alpha) * t0 + alpha; sweep1.getXForm(xf1, t); sweep2.getXForm(xf2, t); // Get the distance between shapes. distance = SingletonPool.getDistance().distance(p1, p2, shape1, xf1, shape2, xf2); //System.out.println("Distance: "+distance + " alpha: "+alpha); if (iter == 0) { // Compute a reasonable target distance to give some breathing room // for conservative advancement. if (distance > 2.0f * Settings.toiSlop) { targetDistance = 1.5f * Settings.toiSlop; } else { targetDistance = MathUtils.max(0.05f * Settings.toiSlop, distance - 0.5f * Settings.toiSlop); } } if (distance - targetDistance < 0.05f * Settings.toiSlop || iter == k_maxIterations) { //if (distance-targetDistance < 0) System.out.println("dist error: "+ (distance-targetDistance) + " toiSlop: "+Settings.toiSlop + " iter: "+iter); break; } // INLINED //normal = p2.sub(p1); //normal.normalize(); float normalx = p2.x - p1.x; float normaly = p2.y - p1.y; final float lenSqrd = normalx * normalx + normaly * normaly; if (lenSqrd >= Settings.EPSILON*Settings.EPSILON) { final float length = MathUtils.sqrt(lenSqrd); final float invLength = 1.0f / length; normalx *= invLength; normaly *= invLength; } // Compute upper bound on remaining movement. // INLINED //float approachVelocityBound = Vec2.dot(normal, v) + MathUtils.abs(omega1) * r1 + MathUtils.abs(omega2) * r2; final float approachVelocityBound = (normalx * vx + normaly * vy) + MathUtils.abs(omega1) * r1 + MathUtils.abs(omega2) * r2; //System.out.println("avb: "+approachVelocityBound); //System.out.println("Normal" + normal); if (MathUtils.abs(approachVelocityBound) < Settings.EPSILON) { alpha = 1.0f; break; } // Get the conservative time increment. Don't advance all the way. final float dAlpha = (distance - targetDistance) / approachVelocityBound; //float32 dt = (distance - 0.5f * b2_linearSlop) / approachVelocityBound; final float newAlpha = alpha + dAlpha; // The shapes may be moving apart or a safe distance apart. if (newAlpha < 0.0f || 1.0f < newAlpha) { alpha = 1.0f; break; } // Ensure significant advancement. if (newAlpha < (1.0f + 100.0f * Settings.EPSILON) * alpha) { break; } alpha = newAlpha; ++iter; } return alpha; } }