package com.arretadogames.pilot.physics; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import org.jbox2d.callbacks.ContactImpulse; import org.jbox2d.callbacks.ContactListener; import org.jbox2d.collision.Manifold; import org.jbox2d.collision.shapes.ChainShape; import org.jbox2d.collision.shapes.CircleShape; import org.jbox2d.collision.shapes.EdgeShape; import org.jbox2d.collision.shapes.PolygonShape; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.Body; import org.jbox2d.dynamics.Fixture; import org.jbox2d.dynamics.World; import org.jbox2d.dynamics.contacts.Contact; import org.jbox2d.dynamics.joints.Joint; import android.graphics.Color; import com.arretadogames.pilot.config.GameSettings; import com.arretadogames.pilot.entities.Entity; import com.arretadogames.pilot.render.Renderable; import com.arretadogames.pilot.render.opengl.GLCanvas; import com.arretadogames.pilot.render.opengl.GLCircle; import com.arretadogames.pilot.util.Profiler; import com.arretadogames.pilot.util.Profiler.ProfileType; public class PhysicalWorld implements ContactListener, Renderable { private static PhysicalWorld gworld; World world; private Collection<Entity> deadEntities; private PhysicalWorld() { world = new World(new Vec2(0.0f,-6.0f)); world.setAutoClearForces(true); world.setContactListener(this); deadEntities = Collections.synchronizedCollection(new ArrayList<Entity>()); } public static PhysicalWorld getInstance() { if(gworld == null) { gworld = new PhysicalWorld(); } return gworld; } /** * Puts all bodies to sleep */ public void sleepAllEntities() { Body body = world.getBodyList(); while (body != null) { body.setAwake(false); body = body.getNext(); } } public void removeAll() { Body b = getWorld().getBodyList(); while (b != null) { // Remove Bodies getWorld().destroyBody(b); b = b.getNext(); } Joint j = getWorld().getJointList(); while (j != null) { // Remove Bodies getWorld().destroyJoint(j); j = j.getNext(); } } public World getWorld() { return world; } @Override public void beginContact(Contact contact) { Entity a = (Entity)contact.m_fixtureA.getBody().getUserData(); Entity b = (Entity)contact.m_fixtureB.getBody().getUserData(); a.beginContact(b, contact); b.beginContact(a, contact); } @Override public void endContact(Contact contact) { Entity a = (Entity)contact.m_fixtureA.getBody().getUserData(); Entity b = (Entity)contact.m_fixtureB.getBody().getUserData(); a.endContact(b, contact); b.endContact(a, contact); } @Override public void postSolve(Contact contact, ContactImpulse impulse) { Entity a = (Entity)contact.m_fixtureA.getBody().getUserData(); Entity b = (Entity)contact.m_fixtureB.getBody().getUserData(); a.postSolve(b, contact, impulse); b.postSolve(a, contact, impulse); } @Override public void preSolve(Contact contact, Manifold oldManifold) { Entity a = (Entity)contact.m_fixtureA.getBody().getUserData(); Entity b = (Entity)contact.m_fixtureB.getBody().getUserData(); a.preSolve(b, contact, oldManifold); b.preSolve(a, contact, oldManifold); } public void step(float timeElapsed) { Profiler.initTick(ProfileType.STEP); world.step(GameSettings.PHYSICS_TIMESTEP < 0 ? timeElapsed : GameSettings.PHYSICS_TIMESTEP, 16, 6); // 16, 6 Profiler.profileFromLastTick(ProfileType.STEP, "Box2D World Step Time"); } public void addDeadEntity(Entity e) { deadEntities.add(e); } public void clearDeadEntities(){ deadEntities.clear(); } public Iterator<Entity> getDeadEntities(){ return deadEntities.iterator(); } public void destroyEntity(Entity e){ e.destroyBody(); } @Override public void render(GLCanvas canvas, float timeElapsed) { //Render All bodies Body body = world.getBodyList(); while (body != null) { Fixture fixture = body.getFixtureList(); canvas.saveState(); while (fixture != null) { canvas.saveState(); switch (fixture.getShape().getType()) { case POLYGON: canvas.translatePhysics(body.getPosition().x, body.getPosition().y); canvas.rotate((float) (180 * (-body.getAngle()) / Math.PI)); drawPolygon(canvas, (PolygonShape) fixture.getShape()); break; case CIRCLE: CircleShape circleShape = (CircleShape) fixture.getShape(); canvas.translatePhysics(body.getPosition().x + circleShape.m_p.x, body.getPosition().y + circleShape.m_p.y); canvas.rotate((float) (180 * (-body.getAngle()) / Math.PI)); drawCircle(canvas, (CircleShape) fixture.getShape()); break; case CHAIN: drawChain(canvas, (ChainShape) fixture.getShape()); break; case EDGE: canvas.translatePhysics(body.getPosition().x, body.getPosition().y); canvas.rotate((float) (180 * (-body.getAngle()) / Math.PI)); drawEdge(canvas, (EdgeShape) fixture.getShape()); break; } canvas.restoreState(); fixture = fixture.getNext(); } canvas.restoreState(); body = body.getNext(); } } private Vec2[] auxVec = new Vec2[2]; private void drawEdge(GLCanvas canvas, EdgeShape shape) { auxVec[0] = shape.m_vertex1; auxVec[1] = shape.m_vertex2; canvas.drawLines(auxVec, 3, Color.YELLOW, false); } private void drawChain(GLCanvas canvas, ChainShape shape) { canvas.drawGroundLines(shape.m_vertices, shape.m_vertices.length, 3, Color.WHITE); } private void drawCircle(GLCanvas canvas, CircleShape shape) { new GLCircle(shape.m_radius * GLCanvas.physicsRatio).drawCircle(canvas, 0, 0, Color.YELLOW, 3, false); } private void drawPolygon(GLCanvas canvas, PolygonShape shape) { canvas.drawPhysicsLines(shape.getVertices(), shape.getVertexCount(), 3, Color.YELLOW, true, true); } }