All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy