org.jbox2d.dynamics.contacts.Contact Maven / Gradle / Ivy
The newest version!
/*
* The MIT License (MIT)
*
* FXGL - JavaFX Game Library
*
* Copyright (c) 2015-2017 AlmasB ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.jbox2d.dynamics.contacts;
import org.jbox2d.callbacks.ContactListener;
import org.jbox2d.collision.ContactID;
import org.jbox2d.collision.Manifold;
import org.jbox2d.collision.ManifoldPoint;
import org.jbox2d.collision.WorldManifold;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.JBoxUtils;
import org.jbox2d.common.Transform;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.pooling.IWorldPool;
/**
* The class manages contact between two shapes. A contact exists for each overlapping AABB in the
* broad-phase (except if filtered). Therefore a contact object may exist that has no contact
* points.
*
* @author daniel
*/
public abstract class Contact {
// Flags stored in m_flags
// Used when crawling contact graph when forming islands.
public static final int ISLAND_FLAG = 0x0001;
// Set when the shapes are touching.
public static final int TOUCHING_FLAG = 0x0002;
// This contact can be disabled (by user)
public static final int ENABLED_FLAG = 0x0004;
// This contact needs filtering because a fixture filter was changed.
public static final int FILTER_FLAG = 0x0008;
// This bullet contact had a TOI event
public static final int BULLET_HIT_FLAG = 0x0010;
public static final int TOI_FLAG = 0x0020;
public int m_flags;
// World pool and list pointers.
public Contact m_prev;
public Contact m_next;
// Nodes for connecting bodies.
public ContactEdge m_nodeA = null;
public ContactEdge m_nodeB = null;
public Fixture m_fixtureA;
public Fixture m_fixtureB;
public int m_indexA;
public int m_indexB;
public final Manifold m_manifold;
public float m_toiCount;
public float m_toi;
public float m_friction;
public float m_restitution;
public float m_tangentSpeed;
protected final IWorldPool pool;
protected Contact(IWorldPool argPool) {
m_fixtureA = null;
m_fixtureB = null;
m_nodeA = new ContactEdge();
m_nodeB = new ContactEdge();
m_manifold = new Manifold();
pool = argPool;
}
/** initialization for pooling */
public void init(Fixture fA, int indexA, Fixture fB, int indexB) {
m_flags = ENABLED_FLAG;
m_fixtureA = fA;
m_fixtureB = fB;
m_indexA = indexA;
m_indexB = indexB;
m_manifold.pointCount = 0;
m_prev = null;
m_next = null;
m_nodeA.contact = null;
m_nodeA.prev = null;
m_nodeA.next = null;
m_nodeA.other = null;
m_nodeB.contact = null;
m_nodeB.prev = null;
m_nodeB.next = null;
m_nodeB.other = null;
m_toiCount = 0;
m_friction = Contact.mixFriction(fA.getFriction(), fB.getFriction());
m_restitution = Contact.mixRestitution(fA.getRestitution(), fB.getRestitution());
m_tangentSpeed = 0;
}
/**
* Get the contact manifold. Do not set the point count to zero. Instead call Disable.
*/
public Manifold getManifold() {
return m_manifold;
}
/**
* Get the world manifold.
*/
public void getWorldManifold(WorldManifold worldManifold) {
final Body bodyA = m_fixtureA.getBody();
final Body bodyB = m_fixtureB.getBody();
final Shape shapeA = m_fixtureA.getShape();
final Shape shapeB = m_fixtureB.getShape();
worldManifold.initialize(m_manifold, bodyA.getTransform(), shapeA.getRadius(),
bodyB.getTransform(), shapeB.getRadius());
}
/**
* Is this contact touching
*
* @return
*/
public boolean isTouching() {
return (m_flags & TOUCHING_FLAG) == TOUCHING_FLAG;
}
/**
* Enable/disable this contact. This can be used inside the pre-solve contact listener. The
* contact is only disabled for the current time step (or sub-step in continuous collisions).
*
* @param flag
*/
public void setEnabled(boolean flag) {
if (flag) {
m_flags |= ENABLED_FLAG;
} else {
m_flags &= ~ENABLED_FLAG;
}
}
/**
* Has this contact been disabled?
*
* @return
*/
public boolean isEnabled() {
return (m_flags & ENABLED_FLAG) == ENABLED_FLAG;
}
/**
* Get the next contact in the world's contact list.
*
* @return
*/
public Contact getNext() {
return m_next;
}
/**
* Get the first fixture in this contact.
*
* @return
*/
public Fixture getFixtureA() {
return m_fixtureA;
}
public int getChildIndexA() {
return m_indexA;
}
/**
* Get the second fixture in this contact.
*
* @return
*/
public Fixture getFixtureB() {
return m_fixtureB;
}
public int getChildIndexB() {
return m_indexB;
}
public void setFriction(float friction) {
m_friction = friction;
}
public float getFriction() {
return m_friction;
}
public void resetFriction() {
m_friction = Contact.mixFriction(m_fixtureA.getFriction(), m_fixtureB.getFriction());
}
public void setRestitution(float restitution) {
m_restitution = restitution;
}
public float getRestitution() {
return m_restitution;
}
public void resetRestitution() {
m_restitution = Contact.mixRestitution(m_fixtureA.getRestitution(), m_fixtureB.getRestitution());
}
public void setTangentSpeed(float speed) {
m_tangentSpeed = speed;
}
public float getTangentSpeed() {
return m_tangentSpeed;
}
public abstract void evaluate(Manifold manifold, Transform xfA, Transform xfB);
/**
* Flag this contact for filtering. Filtering will occur the next time step.
*/
public void flagForFiltering() {
m_flags |= FILTER_FLAG;
}
// djm pooling
private final Manifold oldManifold = new Manifold();
public void update(ContactListener listener) {
oldManifold.set(m_manifold);
// Re-enable this contact.
m_flags |= ENABLED_FLAG;
boolean touching = false;
boolean wasTouching = (m_flags & TOUCHING_FLAG) == TOUCHING_FLAG;
boolean sensorA = m_fixtureA.isSensor();
boolean sensorB = m_fixtureB.isSensor();
boolean sensor = sensorA || sensorB;
Body bodyA = m_fixtureA.getBody();
Body bodyB = m_fixtureB.getBody();
Transform xfA = bodyA.getTransform();
Transform xfB = bodyB.getTransform();
// log.debug("TransformA: "+xfA);
// log.debug("TransformB: "+xfB);
if (sensor) {
Shape shapeA = m_fixtureA.getShape();
Shape shapeB = m_fixtureB.getShape();
touching = pool.getCollision().testOverlap(shapeA, m_indexA, shapeB, m_indexB, xfA, xfB);
// Sensors don't generate manifolds.
m_manifold.pointCount = 0;
} else {
evaluate(m_manifold, xfA, xfB);
touching = m_manifold.pointCount > 0;
// Match old contact ids to new contact ids and copy the
// stored impulses to warm start the solver.
for (int i = 0; i < m_manifold.pointCount; ++i) {
ManifoldPoint mp2 = m_manifold.points[i];
mp2.normalImpulse = 0.0f;
mp2.tangentImpulse = 0.0f;
ContactID id2 = mp2.id;
for (int j = 0; j < oldManifold.pointCount; ++j) {
ManifoldPoint mp1 = oldManifold.points[j];
if (mp1.id.isEqual(id2)) {
mp2.normalImpulse = mp1.normalImpulse;
mp2.tangentImpulse = mp1.tangentImpulse;
break;
}
}
}
if (touching != wasTouching) {
bodyA.setAwake(true);
bodyB.setAwake(true);
}
}
if (touching) {
m_flags |= TOUCHING_FLAG;
} else {
m_flags &= ~TOUCHING_FLAG;
}
if (listener == null) {
return;
}
if (!wasTouching && touching) {
listener.beginContact(this);
}
if (wasTouching && !touching) {
listener.endContact(this);
}
if (!sensor && touching) {
listener.preSolve(this, oldManifold);
}
}
/**
* Friction mixing law. The idea is to allow either fixture to drive the restitution to zero. For
* example, anything slides on ice.
*
* @param friction1
* @param friction2
* @return
*/
public static final float mixFriction(float friction1, float friction2) {
return JBoxUtils.sqrt(friction1 * friction2);
}
/**
* Restitution mixing law. The idea is allow for anything to bounce off an inelastic surface. For
* example, a superball bounces on anything.
*
* @param restitution1
* @param restitution2
* @return
*/
public static final float mixRestitution(float restitution1, float restitution2) {
return restitution1 > restitution2 ? restitution1 : restitution2;
}
}