org.jbox2d.dynamics.Fixture 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;
import com.almasb.fxgl.core.math.Vec2;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.RayCastInput;
import org.jbox2d.collision.RayCastOutput;
import org.jbox2d.collision.broadphase.BroadPhase;
import org.jbox2d.collision.shapes.MassData;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeType;
import org.jbox2d.common.JBoxUtils;
import org.jbox2d.common.Transform;
import org.jbox2d.dynamics.contacts.Contact;
import org.jbox2d.dynamics.contacts.ContactEdge;
/**
* A fixture is used to attach a shape to a body for collision detection. A fixture inherits its
* transform from its parent. Fixtures hold additional non-geometric data such as friction,
* collision filters, etc. Fixtures are created via Body::CreateFixture.
* Note: you cannot reuse fixtures.
*
* @author daniel
*/
public class Fixture {
private final Body body;
private final Shape shape;
private final Filter filter = new Filter();
private Object userData = null;
private float density = 0;
private float friction = 0;
private float restitution = 0;
private boolean isSensor = false;
public FixtureProxy[] m_proxies;
private int proxyCount = 0;
Fixture(Body body, FixtureDef def) {
this.body = body;
userData = def.getUserData();
friction = def.getFriction();
restitution = def.getRestitution();
density = def.getDensity();
isSensor = def.isSensor();
filter.set(def.getFilter());
shape = def.getShape().clone();
// Reserve proxy space
int childCount = shape.getChildCount();
m_proxies = new FixtureProxy[childCount];
for (int i = 0; i < childCount; i++) {
m_proxies[i] = new FixtureProxy();
m_proxies[i].fixture = null;
m_proxies[i].proxyId = BroadPhase.NULL_PROXY;
}
if (m_proxies.length < childCount) {
FixtureProxy[] old = m_proxies;
int newLen = JBoxUtils.max(old.length * 2, childCount);
m_proxies = new FixtureProxy[newLen];
System.arraycopy(old, 0, m_proxies, 0, old.length);
for (int i = 0; i < newLen; i++) {
if (i >= old.length) {
m_proxies[i] = new FixtureProxy();
}
m_proxies[i].fixture = null;
m_proxies[i].proxyId = BroadPhase.NULL_PROXY;
}
}
proxyCount = 0;
}
/**
* Get the type of the child shape. You can use this to down cast to the concrete shape.
*
* @return the shape type
*/
public ShapeType getType() {
return shape.getType();
}
/**
* Get the child shape. You can modify the child shape, however you should not change the number
* of vertices because this will crash some collision caching mechanisms.
*
* @return child shape
*/
public Shape getShape() {
return shape;
}
/**
* Is this fixture a sensor (non-solid)?
*
* @return true if the shape is a sensor.
*/
public boolean isSensor() {
return isSensor;
}
/**
* Set if this fixture is a sensor.
*
* @param sensor sensor flag
*/
public void setSensor(boolean sensor) {
if (sensor != isSensor) {
body.setAwake(true);
isSensor = sensor;
}
}
/**
* Set the contact filtering data. This is an expensive operation and should not be called
* frequently. This will not update contacts until the next time step when either parent body is
* awake. This automatically calls refilter.
*
* @param filter filter
*/
public void setFilterData(final Filter filter) {
this.filter.set(filter);
refilter();
}
/**
* @return the contact filtering data
*/
public Filter getFilterData() {
return filter;
}
public int getProxyCount() {
return proxyCount;
}
public FixtureProxy[] getProxies() {
return m_proxies;
}
/**
* Call this if you want to establish collision that was previously disabled by
* ContactFilter::ShouldCollide.
*/
public void refilter() {
// Flag associated contacts for filtering.
ContactEdge edge = body.getContactList();
while (edge != null) {
Contact contact = edge.contact;
Fixture fixtureA = contact.getFixtureA();
Fixture fixtureB = contact.getFixtureB();
if (fixtureA == this || fixtureB == this) {
contact.flagForFiltering();
}
edge = edge.next;
}
World world = body.getWorld();
if (world == null) {
return;
}
// Touch each proxy so that new pairs may be created
BroadPhase broadPhase = world.m_contactManager.m_broadPhase;
for (int i = 0; i < proxyCount; ++i) {
broadPhase.touchProxy(m_proxies[i].proxyId);
}
}
/**
* Get the parent body of this fixture.
*
* @return the parent body
*/
public Body getBody() {
return body;
}
public void setDensity(float density) {
assert (density >= 0f);
this.density = density;
}
/**
* @return density
*/
public float getDensity() {
return density;
}
/**
* Get the user data that was assigned in the fixture definition. Use this to store your
* application specific data.
*
* @return user data
*/
public Object getUserData() {
return userData;
}
/**
* Set the user data. Use this to store your application specific data.
*
* @param data user data
*/
public void setUserData(Object data) {
userData = data;
}
/**
* Test a point for containment in this fixture. This only works for convex shapes.
*
* @param p a point in world coordinates
*/
public boolean containsPoint(final Vec2 p) {
return shape.testPoint(body.m_xf, p);
}
/**
* Cast a ray against this shape.
*
* @param output the ray-cast results
* @param input the ray-cast input parameters
*/
public boolean raycast(RayCastOutput output, RayCastInput input, int childIndex) {
return shape.raycast(output, input, body.m_xf, childIndex);
}
/**
* Get the mass data for this fixture. The mass data is based on the density and the shape. The
* rotational inertia is about the shape's origin.
*/
public void getMassData(MassData massData) {
shape.computeMass(massData, density);
}
/**
* @return the coefficient of friction
*/
public float getFriction() {
return friction;
}
/**
* Set the coefficient of friction. This will NOT change the friction of existing contacts.
*
* @param friction friction
*/
public void setFriction(float friction) {
this.friction = friction;
}
/**
* @return the coefficient of restitution
*/
public float getRestitution() {
return restitution;
}
/**
* Set the coefficient of restitution. This will NOT change the restitution of existing
* contacts.
*
* @param restitution restitution
*/
public void setRestitution(float restitution) {
this.restitution = restitution;
}
/**
* Get the fixture's AABB. This AABB may be enlarge and/or stale. If you need a more accurate
* AABB, compute it using the shape and the body transform.
*
* @return AABB
*/
public AABB getAABB(int childIndex) {
assert (childIndex >= 0 && childIndex < proxyCount);
return m_proxies[childIndex].aabb;
}
/**
* Compute the distance from this fixture.
*
* @param p a point in world coordinates.
* @return distance
*/
public float computeDistance(Vec2 p, int childIndex, Vec2 normalOut) {
return shape.computeDistanceToOut(body.getTransform(), p, childIndex, normalOut);
}
void destroy() {
// The proxies must be destroyed before calling this.
assert (proxyCount == 0);
m_proxies = null;
}
// These support body activation/deactivation.
public void createProxies(BroadPhase broadPhase, final Transform xf) {
assert (proxyCount == 0);
// Create proxies in the broad-phase.
proxyCount = shape.getChildCount();
for (int i = 0; i < proxyCount; ++i) {
FixtureProxy proxy = m_proxies[i];
shape.computeAABB(proxy.aabb, xf, i);
proxy.proxyId = broadPhase.createProxy(proxy.aabb, proxy);
proxy.fixture = this;
proxy.childIndex = i;
}
}
/**
* @param broadPhase broad phase
*/
void destroyProxies(BroadPhase broadPhase) {
// Destroy proxies in the broad-phase.
for (int i = 0; i < proxyCount; ++i) {
FixtureProxy proxy = m_proxies[i];
broadPhase.destroyProxy(proxy.proxyId);
proxy.proxyId = BroadPhase.NULL_PROXY;
}
proxyCount = 0;
}
private final AABB pool1 = new AABB();
private final AABB pool2 = new AABB();
private final Vec2 displacement = new Vec2();
/**
* @param broadPhase broad phase
* @param transform1 xf1
* @param transform2 xf2
*/
void synchronize(BroadPhase broadPhase, final Transform transform1,
final Transform transform2) {
if (proxyCount == 0) {
return;
}
for (int i = 0; i < proxyCount; ++i) {
FixtureProxy proxy = m_proxies[i];
// Compute an AABB that covers the swept shape (may miss some rotation effect).
final AABB aabb1 = pool1;
final AABB aab = pool2;
shape.computeAABB(aabb1, transform1, proxy.childIndex);
shape.computeAABB(aab, transform2, proxy.childIndex);
proxy.aabb.lowerBound.x =
aabb1.lowerBound.x < aab.lowerBound.x ? aabb1.lowerBound.x : aab.lowerBound.x;
proxy.aabb.lowerBound.y =
aabb1.lowerBound.y < aab.lowerBound.y ? aabb1.lowerBound.y : aab.lowerBound.y;
proxy.aabb.upperBound.x =
aabb1.upperBound.x > aab.upperBound.x ? aabb1.upperBound.x : aab.upperBound.x;
proxy.aabb.upperBound.y =
aabb1.upperBound.y > aab.upperBound.y ? aabb1.upperBound.y : aab.upperBound.y;
displacement.x = transform2.p.x - transform1.p.x;
displacement.y = transform2.p.y - transform1.p.y;
broadPhase.moveProxy(proxy.proxyId, proxy.aabb, displacement);
}
}
}