/*******************************************************************************
* Copyright (c) 2011, Daniel Murphy
* 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 the <organization> 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 DANIEL MURPHY 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 org.jbox2d.dynamics;
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.broadphase.DynamicTreeNode;
import org.jbox2d.collision.shapes.MassData;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeType;
import org.jbox2d.common.Transform;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.contacts.Contact;
import org.jbox2d.dynamics.contacts.ContactEdge;
// updated to rev 100
// thread-safe pooling
/**
* 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 b2Body::CreateFixture.
* @warning you cannot reuse fixtures.
*
* @author daniel
*/
public class Fixture {
public final AABB m_aabb = new AABB();
public float m_density;
public Fixture m_next;
public Body m_body;
public Shape m_shape;
public float m_friction;
public float m_restitution;
public DynamicTreeNode m_proxy;
public final Filter m_filter;
public boolean m_isSensor;
public Object m_userData;
public Fixture(){
m_userData = null;
m_body = null;
m_next = null;
m_proxy = null;
m_shape = null;
m_filter = new Filter();
}
/**
* 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 m_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
*/
public Shape getShape(){
return m_shape;
}
/**
* Is this fixture a sensor (non-solid)?
* @return the true if the shape is a sensor.
* @return
*/
public boolean isSensor(){
return m_isSensor;
}
/**
* Set if this fixture is a sensor.
* @param sensor
*/
public void setSensor(boolean sensor){
m_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.
* @param filter
*/
public void setFilterData(final Filter filter){
m_filter.set(filter);
if(m_body == null){
return;
}
// Flag associated contacts for filtering.
ContactEdge edge = m_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;
}
}
/**
* Get the contact filtering data.
* @return
*/
public Filter getFilterData(){
return m_filter;
}
/**
* Get the parent body of this fixture. This is NULL if the fixture is not attached.
* @return the parent body.
* @return
*/
public Body getBody(){
return m_body;
}
/**
* Get the next fixture in the parent body's fixture list.
* @return the next shape.
* @return
*/
public Fixture getNext(){
return m_next;
}
public void setDensity(float density){
assert(density >= 0f);
m_density = density;
}
public float getDensity(){
return m_density;
}
/**
* Get the user data that was assigned in the fixture definition. Use this to
* store your application specific data.
* @return
*/
public Object getUserData(){
return m_userData;
}
/**
* Set the user data. Use this to store your application specific data.
* @param data
*/
public void setUserData(Object data){
m_userData = data;
}
/**
* Test a point for containment in this fixture. This only works for convex shapes.
* @param xf the shape world transform.
* @param p a point in world coordinates.
* @param p
* @return
*/
public boolean testPoint(final Vec2 p){
return m_shape.testPoint( m_body.m_xf, p);
}
/**
* Cast a ray against this shape.
* @param output the ray-cast results.
* @param input the ray-cast input parameters.
* @param output
* @param input
*/
public boolean raycast(RayCastOutput output, RayCastInput input){
return m_shape.raycast( output, input, m_body.m_xf);
}
/**
* 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.
* @return
*/
public void getMassData(MassData massData){
m_shape.computeMass(massData, m_density);
}
/**
* Get the coefficient of friction.
* @return
*/
public float getFriction(){
return m_friction;
}
/**
* Set the coefficient of friction.
* @param friction
*/
public void setFriction(float friction){
m_friction = friction;
}
/**
* Get the coefficient of restitution.
* @return
*/
public float getRestitution(){
return m_restitution;
}
/**
* Set the coefficient of restitution.
* @param restitution
*/
public void setRestitution(float restitution){
m_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
*/
public AABB getAABB(){
return m_aabb;
}
// We need separation create/destroy functions from the constructor/destructor because
// the destructor cannot access the allocator (no destructor arguments allowed by C++).
public void create(Body body, FixtureDef def){
m_userData = def.userData;
m_friction = def.friction;
m_restitution = def.restitution;
m_body = body;
m_next = null;
m_filter.set(def.filter);
m_isSensor = def.isSensor;
m_shape = def.shape.clone();
m_density = def.density;
}
public void destroy(){
// The proxy must be destroyed before calling this.
assert(m_proxy == null);
// Free the child shape.
// yeah woo jvm
// TODO djm should I pool this then?
m_shape = null;
}
// These support body activation/deactivation.
public void createProxy(BroadPhase broadPhase, final Transform xf){
assert(m_proxy == null);
// Create proxy in the broad-phase.
m_shape.computeAABB( m_aabb, xf);
m_proxy = broadPhase.createProxy( m_aabb, this);
}
/**
* Internal method
* @param broadPhase
*/
public void destroyProxy(BroadPhase broadPhase){
if(m_proxy == null){
return;
}
broadPhase.destroyProxy( m_proxy);
m_proxy = null;
}
private final AABB pool1 = new AABB();
private final AABB pool2 = new AABB();
/**
* Internal method
* @param broadPhase
* @param xf1
* @param xf2
*/
protected void synchronize(BroadPhase broadPhase, final Transform transform1, final Transform transform2){
if(m_proxy == null){
return;
}
// Compute an AABB that covers the swept shape (may miss some rotation effect).
// AABB aabb1 = tlaabb1.get();
// AABB aabb2 = tlaabb2.get();
//
// m_shape.computeAABB( aabb1, transform1);
// m_shape.computeAABB( aabb2, transform2);
//
// m_aabb.combine(aabb1, aabb2);
//
// Vec2 disp = tldisp.get();
// disp.set( transform2.position).subLocal(transform1.position);
//
// broadPhase.moveProxy( m_proxy, m_aabb, disp);
m_shape.computeAABB( pool1, transform1);
m_shape.computeAABB( pool2, transform2);
m_aabb.lowerBound.x = pool1.lowerBound.x < pool2.lowerBound.x ? pool1.lowerBound.x : pool2.lowerBound.x;
m_aabb.lowerBound.y = pool1.lowerBound.y < pool2.lowerBound.y ? pool1.lowerBound.y : pool2.lowerBound.y;
m_aabb.upperBound.x = pool1.upperBound.x > pool2.upperBound.x ? pool1.upperBound.x : pool2.upperBound.x;
m_aabb.upperBound.y = pool1.upperBound.y > pool2.upperBound.y ? pool1.upperBound.y : pool2.upperBound.y;
final Vec2 disp = pool1.lowerBound; // just use this vec for pooling
disp.x = transform2.position.x - transform1.position.x;
disp.y = transform2.position.y - transform1.position.y;
broadPhase.moveProxy( m_proxy, m_aabb, disp);
}
}