package org.andengine.extension.debugdraw; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; import org.andengine.entity.Entity; import org.andengine.extension.physics.box2d.PhysicsConnector; import org.andengine.extension.physics.box2d.PhysicsWorld; import org.andengine.opengl.vbo.VertexBufferObjectManager; import org.andengine.util.adt.color.Color; import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.Fixture; import com.badlogic.gdx.physics.box2d.Joint; import com.badlogic.gdx.physics.box2d.Shape.Type; public class DebugRenderer extends Entity { private PhysicsWorld mWorld; private final VertexBufferObjectManager mVBO; // bodies to be rendered private HashMap<Body, RenderOfBody> mBodiesToBeRenderred = new HashMap<Body, RenderOfBody>(); private Set<RenderOfBody> mBodiesInactiveSet = new HashSet<RenderOfBody>(); private Set<RenderOfBody> mBodiesActiveSet = new HashSet<RenderOfBody>(); // joints to be rendered private HashMap<Joint, IRenderOfJoint> mJointsToBeRenderred = new HashMap<Joint, IRenderOfJoint>(); private Set<IRenderOfJoint> mJointsInactiveSet = new HashSet<IRenderOfJoint>(); private Set<IRenderOfJoint> mJointsActiveSet = new HashSet<IRenderOfJoint>(); private final float mJointMarkerSize; private boolean mDrawJoints = true; private boolean mDrawBodies = true; /** * To construct the renderer physical world is needed (to access physics) * and VBO (to construct visible representations) * @param world * @param pVBO */ public DebugRenderer(PhysicsWorld world, VertexBufferObjectManager pVBO) { this(world, pVBO, 5); } public DebugRenderer(PhysicsWorld world, VertexBufferObjectManager pVBO, float pJointMarkerSize) { super(); this.mWorld = world; this.mVBO = pVBO; this.mJointMarkerSize = pJointMarkerSize; } /** * This is where all the magic happens. Bodies representations are rendered. * Dead bodies (not being part of physical world anymore) are removed from * the rendering. */ @Override protected void onManagedUpdate(float pSecondsElapsed) { super.onManagedUpdate(pSecondsElapsed); // *** BODIES if (isDrawBodies()) { mBodiesActiveSet.clear(); mBodiesInactiveSet.clear(); Iterator<Body> iterator = mWorld.getBodies(); while (iterator.hasNext()) { Body body = iterator.next(); RenderOfBody renderOfBody; if (!mBodiesToBeRenderred.containsKey(body)) { renderOfBody = new RenderOfBody(body, mVBO); mBodiesToBeRenderred.put(body, renderOfBody); this.attachChild(renderOfBody); } else { renderOfBody = mBodiesToBeRenderred.get(body); } mBodiesActiveSet.add(renderOfBody); /** * This is where debug renders are moved to match body position. * These 4 lines probably have to be modified if you are not using new * GLES2-AnchorCenter branch of AE (i.e. you are using old GLES2 branch) */ renderOfBody.updateColor(); renderOfBody .setRotationCenter( body.getMassData().center.x * PhysicsConnector.PIXEL_TO_METER_RATIO_DEFAULT, body.getMassData().center.y * PhysicsConnector.PIXEL_TO_METER_RATIO_DEFAULT); renderOfBody.setRotation((float) (360 - body.getAngle() * (180 / Math.PI))); renderOfBody .setPosition( body.getPosition().x * PhysicsConnector.PIXEL_TO_METER_RATIO_DEFAULT, body.getPosition().y * PhysicsConnector.PIXEL_TO_METER_RATIO_DEFAULT); } /** * Get rid of all bodies that were not rendered in this iteration */ // inactive = renderred - active mBodiesInactiveSet.addAll(mBodiesToBeRenderred.values()); mBodiesInactiveSet.removeAll(mBodiesActiveSet); for (RenderOfBody killme : mBodiesInactiveSet) { this.detachChild(killme); } mBodiesToBeRenderred.values().removeAll(mBodiesInactiveSet); } // *** JOINTS if (isDrawJoints()) { mJointsActiveSet.clear(); mJointsInactiveSet.clear(); Iterator<Joint> iteratorJoints = mWorld.getJoints(); while (iteratorJoints.hasNext()) { Joint joint = iteratorJoints.next(); IRenderOfJoint renderOfJoint; if (!mJointsToBeRenderred.containsKey(joint)) { renderOfJoint = new RenderOfJointPolyline(joint, mVBO, mJointMarkerSize); mJointsToBeRenderred.put(joint, renderOfJoint); this.attachChild(renderOfJoint.getEntity()); } else { renderOfJoint = mJointsToBeRenderred.get(joint); } mJointsActiveSet.add(renderOfJoint); renderOfJoint.update(); renderOfJoint.getEntity().setColor( jointToColor(renderOfJoint.getJoint())); } /** * Get rid of all joints that were not rendered in this iteration */ // inactive = renderred - active mJointsInactiveSet.addAll(mJointsToBeRenderred.values()); mJointsInactiveSet.removeAll(mJointsActiveSet); for (IRenderOfJoint killme : mJointsInactiveSet) { this.detachChild(killme.getEntity()); } mJointsToBeRenderred.values().removeAll(mJointsInactiveSet); } } /** * Translates b2d Fixture to appropriate color, depending on body state/type * Modify to suit your needs * @param fixture * @return */ private static Color fixtureToColor(Fixture fixture) { if (fixture.isSensor()) { return Color.PINK; } else { Body body = fixture.getBody(); if (!body.isActive()) { return Color.BLACK; } else { if (!body.isAwake()) { return Color.RED; } else { switch (body.getType()) { case StaticBody: return Color.CYAN; case KinematicBody: return Color.WHITE; case DynamicBody: default: return Color.GREEN; } } } } } /** * Translates b2d Joint to appropriate color, depending on state/type * Modify to suit your needs * @param joint * @return */ private static Color jointToColor(Joint joint) { switch (joint.getType()) { case RevoluteJoint: case PrismaticJoint: case DistanceJoint: case PulleyJoint: case MouseJoint: case GearJoint: case WeldJoint: case FrictionJoint: return Color.WHITE; case Unknown: default: return Color.WHITE; } } /** * Physical body representation- it contains of multiple IRenderOfFixture * @author nazgee * */ private class RenderOfBody extends Entity { public LinkedList<IRenderOfFixture> mRenderFixtures = new LinkedList<IRenderOfFixture>(); public RenderOfBody(Body pBody, VertexBufferObjectManager pVBO) { ArrayList<Fixture> fixtures = pBody.getFixtureList(); /** * Spawn all IRenderOfFixture for this body that are out there, * and bind them to this RenderOfBody */ for (Fixture fixture : fixtures) { IRenderOfFixture renderOfFixture; if (fixture.getShape().getType() == Type.Circle) { renderOfFixture = new RenderOfCircleFixture(fixture, pVBO); } else { renderOfFixture = new RenderOfPolyFixture(fixture, pVBO); } updateColor(); mRenderFixtures.add(renderOfFixture); this.attachChild(renderOfFixture.getEntity()); } } public void updateColor() { for (IRenderOfFixture renderOfFix : mRenderFixtures) { renderOfFix.getEntity().setColor(fixtureToColor(renderOfFix.getFixture())); } } } public boolean isDrawJoints() { return mDrawJoints; } public void setDrawJoints(boolean mDrawJoints) { this.mDrawJoints = mDrawJoints; } public boolean isDrawBodies() { return mDrawBodies; } public void setDrawBodies(boolean mDrawBodies) { this.mDrawBodies = mDrawBodies; } }