The bug
The physics ground represented by a horizontal line. The object is stationary, and seems to be pretending that the ground is lower. This is because the center of the Shape is incorrectly located, or because I am transforming it improperly.
This is a cube displayed with the same system, and the center at the center of the object. It acts correctly, but that may just be because of its symmetric nature.
Shapes appear to rotate around points which are not at their center of mass when I view a simulation involving a convex polygon and a static plane. I wrote the draw code myself, as shown below. More specifically, see the render function, which draws all objects in the Box2D world. One possible location for the bug is in the code where I'm transforming the Graphics object, and the other is when I'm creating and transforming the Slick2D Shape I will be drawing.
What is causing my graphics glitch?
Below is the source of the program in question.
IMPORTANT NOTE: This code is fully compilable and self-contained.
If you have jBox2D and Slick2D, you'll be able to compile and run it.
Note that to use jBox2D, you'll have to add some slf4j packages (slf4j-api and slf4j-nop if you just want it quick).
package junk;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.ShapeType;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.World;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Transform;
public class Junk extends BasicGame
{
World world;
int velocityIterations;
int positionIterations;
float pixelsPerMeter;
public Junk()
{
super("Hello World");
}
@Override
public void init(GameContainer gc) throws SlickException
{
velocityIterations = 10;
positionIterations = 10;
pixelsPerMeter = 30.f;
world = new World(new Vec2(0.f, -9.8f), true);
{
BodyDef dground = new BodyDef();
dground.active = true;
dground.position = new Vec2(0.f, 0.f);
dground.type = BodyType.STATIC;
Body bground = world.createBody(dground);
PolygonShape sground = new PolygonShape();
sground.setAsEdge(new Vec2(-30.f, 0.f), new Vec2(30.f, 0.f));
bground.createFixture(sground, 1000.f);
}
{
BodyDef dbox = new BodyDef();
dbox.active = true;
dbox.position = new Vec2(0.f, 10.f);
dbox.angle = MathUtils.PI / 3.f;
dbox.type = BodyType.DYNAMIC;
Body bbox = world.createBody(dbox);
PolygonShape sbox = new PolygonShape();
Vec2[] data =
{
new Vec2(-0.3f, -0.7f),
new Vec2(0.0f, -1.0f),
new Vec2(0.3f, -0.7f),
new Vec2(0.5f, 0.5f),
new Vec2(-0.5f, 0.5f)
};
sbox.set(data, 4);
//sbox.setAsBox(0.5f, 0.5f);
bbox.createFixture(sbox, 1.f);
bbox.setAngularVelocity(10.f);
}
}
@Override
public void update(GameContainer gc, int delta) throws SlickException
{
world.step((float)delta / 1000.f, velocityIterations, positionIterations);
}
@Override
public void render(GameContainer gc, Graphics g) throws SlickException
{
Body current = world.getBodyList();
Vec2 center = current.getLocalCenter();
while(current != null)
{
Vec2 pos = current.getPosition();
g.pushTransform();
g.translate(pos.x * pixelsPerMeter + (0.5f * gc.getWidth()),
-pos.y * pixelsPerMeter + (0.5f * gc.getHeight()));
Fixture f = current.getFixtureList();
while(f != null)
{
ShapeType type = f.getType();
switch(type)
{
case POLYGON:
{
PolygonShape shape = (PolygonShape)f.getShape();
Vec2[] verts = shape.getVertices();
int count = shape.getVertexCount();
Polygon p = new Polygon();
for(int i = 0; i < count; i++)
{
p.addPoint(verts[i].x, verts[i].y);
}
p.setCenterX(center.x);
p.setCenterY(center.y);
p = (Polygon)p.transform(Transform.createRotateTransform(current.getAngle() + MathUtils.PI, center.x, center.y));
p = (Polygon)p.transform(Transform.createScaleTransform(pixelsPerMeter, pixelsPerMeter));
g.draw(p);
break;
}
case CIRCLE:
{
CircleShape shape = (CircleShape)f.getShape();
}
default:
}
f = f.getNext();
}
g.popTransform();
current = current.getNext();
}
}
public static void main(String[] args) throws SlickException
{
AppGameContainer app = new AppGameContainer(new Junk());
app.setDisplayMode(800, 600, false);
//app.setShowFPS(false);
app.start();
}
}