package com.indignado.logicbricks.systems; import com.badlogic.ashley.core.ComponentMapper; import com.badlogic.ashley.core.Entity; import com.badlogic.ashley.core.Family; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.*; import com.indignado.logicbricks.components.BuoyancyComponent; import com.indignado.logicbricks.config.Settings; import com.indignado.logicbricks.utils.B2ShapeExtensions; import com.indignado.logicbricks.utils.Log; /** * @author Rubentxu */ public class BuoyancySystem extends LogicBrickSystem implements ContactListener { public static final float EPSILON = 1.1920928955078125E-7f; private static Vector2 c1 = new Vector2(); private static Vector2 c2 = new Vector2(); private ComponentMapper<BuoyancyComponent> bm; private Vector2 areac = new Vector2(0, 0); private Vector2 massc = new Vector2(0, 0); private Vector2 sc = new Vector2(0, 0); private float area = 0; private float mass = 0; private float sarea = 0; private float shapeDensity = 0; public BuoyancySystem() { super(Family.all(BuoyancyComponent.class).get(), 4); bm = ComponentMapper.getFor(BuoyancyComponent.class); } private void resetValues() { c1.set(0, 0); c2.set(0, 0); areac.set(0, 0); massc.set(0, 0); sc.set(0, 0); area = 0; mass = 0; sarea = 0; shapeDensity = 0; } @Override public void processEntity(Entity entity, float deltaTime) { if (Settings.DEBUG_ENTITY != null) tag = Log.tagEntity(this.getClass().getSimpleName(), entity); applyToFixture(bm.get(entity)); } private <T extends Shape> boolean applyToFixture(BuoyancyComponent buoyancy) { for (Body body : buoyancy.bodyList) { if (!body.isAwake() || !body.getType().equals(BodyDef.BodyType.DynamicBody)) continue; resetValues(); for (Fixture fixture : body.getFixtureList()) { switch (fixture.getShape().getType()) { case Circle: sarea = B2ShapeExtensions.computeSubmergedArea((CircleShape) fixture.getShape(), buoyancy.normal, buoyancy.offset, fixture.getBody().getTransform(), sc); break; case Polygon: sarea = B2ShapeExtensions.computeSubmergedArea((PolygonShape) fixture.getShape(), buoyancy.normal, buoyancy.offset, fixture.getBody().getTransform(), sc); } area += sarea; areac.x += sarea * sc.x; areac.y += sarea * sc.y; if (buoyancy.useDensity) { //TODO: Expose density publicly shapeDensity = fixture.getDensity(); } else { shapeDensity = 1; } mass += sarea * shapeDensity; massc.x += sarea * sc.x * shapeDensity; massc.y += sarea * sc.y * shapeDensity; } areac.x /= area; areac.y /= area; massc.x /= mass; massc.y /= mass; if (area < EPSILON) continue; //Buoyancy Vector2 buoyancyForce = buoyancy.gravity.cpy().scl(-buoyancy.density * area); body.applyForce(buoyancyForce, massc, true); Log.debug(tag, "ApplyForce buoyancyForce %s", buoyancyForce); //Linear drag Vector2 dragForce = body.getLinearVelocityFromWorldPoint(areac).sub(buoyancy.velocity); dragForce.scl(-buoyancy.linearDrag * area); body.applyForce(dragForce, areac, true); Log.debug(tag, "ApplyForce dragforce %s", dragForce); //Angular drag body.applyTorque(-body.getInertia() / body.getMass() * area * body.getAngularVelocity() * buoyancy.angularDrag, true); Log.debug(tag, "Velocity %s", body.getLinearVelocity()); } return true; } @Override public void beginContact(Contact contact) { BuoyancyComponent buoyancy; Entity entityA = (Entity) contact.getFixtureA().getBody().getUserData(); Entity entityB = (Entity) contact.getFixtureB().getBody().getUserData(); buoyancy = entityA.getComponent(BuoyancyComponent.class); if (buoyancy != null) { Body body = contact.getFixtureB().getBody(); buoyancy.bodyList.add(body); Log.debug(tag, "Begin Contact body %s", body.getPosition()); return; } buoyancy = entityB.getComponent(BuoyancyComponent.class); if (buoyancy != null) { Body body = contact.getFixtureA().getBody(); buoyancy.bodyList.add(body); Log.debug(tag, "Begin Contact body %s", body.getPosition()); } } @Override public void endContact(Contact contact) { BuoyancyComponent buoyancy; Entity entityA = (Entity) contact.getFixtureA().getBody().getUserData(); Entity entityB = (Entity) contact.getFixtureB().getBody().getUserData(); buoyancy = entityA.getComponent(BuoyancyComponent.class); if (buoyancy != null) { Body body = contact.getFixtureB().getBody(); buoyancy.bodyList.removeValue(body, true); Log.debug(tag, "End Contact body %s", body.getPosition()); return; } buoyancy = entityB.getComponent(BuoyancyComponent.class); if (buoyancy != null) { Body body = contact.getFixtureA().getBody(); buoyancy.bodyList.removeValue(body, true); Log.debug(tag, "End Contact body %s", body.getPosition()); } } @Override public void preSolve(Contact contact, Manifold oldManifold) { } @Override public void postSolve(Contact contact, ContactImpulse impulse) { } }