package advanced.physics.physicsShapes;
import java.util.List;
import javax.media.opengl.glu.GLU;
import org.jbox2d.collision.shapes.PolygonDef;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;
import org.mt4j.MTApplication;
import org.mt4j.components.MTComponent;
import org.mt4j.components.bounds.BoundsArbitraryPlanarPolygon;
import org.mt4j.components.bounds.IBoundingShape;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.components.visibleComponents.shapes.MTRectangle;
import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleProcessor;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GluTrianglulator;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;
import advanced.physics.util.PhysicsHelper;
public class PhysicsRectangle extends MTRectangle implements IPhysicsComponent{
private float angle;
private boolean drawBounds;
private World world;
private Body body;
private float density;
private float friction;
private float restituion;
/*
PhysicsRectangle rect = new PhysicsRectangle(new Vector3D(400,200), 100,150, app, world, 0.6f, 0.4f, 0.2f, scale);
rect.setNoStroke(true);
PhysicsHelper.addDragJoint(world, rect, rect.getBody().isDynamic(), scale);
physObjGroup.addChild(rect);
*/
public PhysicsRectangle(
Vector3D centerPosition,
float width, float height, PApplet applet,
World world, float density, float friction, float restitution, float scale
) {
super(0, 0, PhysicsHelper.scaleDown(width, scale), PhysicsHelper.scaleDown(height, scale), applet);
this.angle = 0;
this.drawBounds = false;
this.world = world;
this.density = density;
this.friction = friction;
this.restituion = restitution;
Vector3D scaledPos = PhysicsHelper.scaleDown(centerPosition.getCopy(), scale);
// PhysicsHelper.scaleDown(centerPosition, scale);
// centerPosition.scaleLocal(1f/scale);
BodyDef dymBodyDef = new BodyDef();
// dymBodyDef.position = new Vec2(pos.x, pos.y);
dymBodyDef.position = new Vec2(scaledPos.x, scaledPos.y);//FIXME WORKS?
this.bodyDefB4CreationCallback(dymBodyDef);
this.body = world.createBody(dymBodyDef);
PolygonDef dymShapeDef = new PolygonDef();
dymShapeDef.setAsBox( (width/2f)/scale, (height/2f)/scale);
// dymShapeDef.setAsBox( (width/2f), (height/2f));
if (density != 0.0f){
dymShapeDef.density = density;
dymShapeDef.friction = friction;
dymShapeDef.restitution = restitution;
}
this.polyDefB4CreationCallback(dymShapeDef); //FIXME TEST
body.createShape(dymShapeDef);
body.setMassFromShapes();
//TEST
// theBody.setBullet(true);
this.setPositionGlobal(scaledPos);
body.setUserData(this);
this.setUserData("box2d", body);
this.setGestureAllowance(ScaleProcessor.class, false);
this.setGestureAllowance(RotateProcessor.class, false);
}
public PhysicsRectangle(PImage texture,
Vector3D pos,
PApplet applet,
Vertex[] physicsVertices,
World world, float density, float friction, float restitution, float scale
) {
super(texture, applet);
this.angle = 0;
this.drawBounds = false;
this.world = world;
this.density = density;
this.friction = friction;
this.restituion = restitution;
this.setGestureAllowance(ScaleProcessor.class, false);
this.setGestureAllowance(RotateProcessor.class, false);
//Scale shape vertices
// this.setGeometryInfo(new GeometryInfo(applet, Vertex.scaleVectorArray(this.getGeometryInfo().getVertices(), Vector3D.ZERO_VECTOR, 1f/scale, 1f/scale, 1)));
// this.setGeometryInfo(new GeometryInfo(applet, PhysicsHelper.scaleDown(this.getGeometryInfo().getVertices(), scale)));
this.setGeometryInfo(new GeometryInfo(applet, PhysicsHelper.scaleDown(Vertex.getDeepVertexArrayCopy(this.getGeometryInfo().getVertices()), scale)));
//Scale physics vertics
// Vertex.scaleVectorArray(bodyVerts, Vector3D.ZERO_VECTOR, 1f/scale, 1f/scale, 1);
PhysicsHelper.scaleDown(physicsVertices, scale);
Vector3D scaledPos = PhysicsHelper.scaleDown(pos.getCopy(), scale);
//Scale position
// pos.scaleLocal(1f/scale); //FIXME REALLY?
// PhysicsHelper.scaleDown(pos, scale);
this.setGestureAllowance(ScaleProcessor.class, false);
this.setGestureAllowance(RotateProcessor.class, false);
//Temporarily move the mesh so that we know where the calculated center of the body
//would be (the body takes the body.position as the center reference instead of a calculated center)
// //FIXME welchen centerpoint nehmen? -> kommt auch drauf an ob shape schon auf canvas war!?
//We have to do this because the anchor point ("position") of the pyhsics shape is the body.position
//but the anchor point of our shapes is the point returned from getCenterpoint..()
this.translate(scaledPos);
Vector3D realBodyCenter = this.getCenterPointGlobal(); //FIXME geht nur if detached from world //rename futurebodycenter?
//Reset position
this.translate(scaledPos.getScaled(-1));
//Now get the position where the global center will be after setting the shape at the desired position
this.setPositionGlobal(scaledPos);
Vector3D meshCenterAtPosition = this.getCenterPointGlobal();
//Compute the distance we would have to move the vertices for the body creation
//so that the body.position(center) is at the same position as our mesh center
Vector3D realBodyCenterToMeshCenter = meshCenterAtPosition.getSubtracted(realBodyCenter);
//System.out.println("Diff:" + realBodyCenterToMeshCenter);
//FIXME TEST Needed for level saving the local vertices at the right position
this.setUserData("realBodyCenterToMeshCenter", realBodyCenterToMeshCenter);
//Move the vertices so the body position is at the center of the shape
Vertex.translateVectorArray(physicsVertices, realBodyCenterToMeshCenter);
//Create vertex structure for creation of decomposition polygon (use the translated vertices)
float xArr[] = new float[physicsVertices.length];
float yArr[] = new float[physicsVertices.length];
for (int i = 0; i < physicsVertices.length; i++) {
Vertex v = physicsVertices[i];
xArr[i] = v.x;
yArr[i] = v.y;
}
//Create a polygon too see if its simple and eventually decompose it
org.jbox2d.util.nonconvex.Polygon myPoly = new org.jbox2d.util.nonconvex.Polygon(xArr, yArr);
//System.out.println("Polygon is simple! -> Using convex decomposition for physics shape and glu triangulated mesh for display!");
PolygonDef pd = new PolygonDef();
if (density != 0.0f){
pd.density = density;
pd.friction = friction;
pd.restitution = restituion;
}
//Create polygon body
BodyDef dymBodyDef = new BodyDef();
dymBodyDef.position = new Vec2(scaledPos.x, scaledPos.y);
this.bodyDefB4CreationCallback(dymBodyDef);
this.body = world.createBody(dymBodyDef);
this.polyDefB4CreationCallback(pd); //FIXME TEST
int success = org.jbox2d.util.nonconvex.Polygon.decomposeConvexAndAddTo(myPoly, body, pd);
if (success != -1){
System.out.println("-> Ear clipping SUCCESSFUL -> Using triangulated and polygonized shape for b2d.");
body.setMassFromShapes();
body.setUserData(this);
this.setUserData("box2d", body);
//Performance hit! but prevents object from sticking to another sometimes
// theBody.setBullet(true);
}else{
System.out.println("-> Ear clipping had an ERROR - trying again by triangulating shape for b2d with GLU-Triangulator");
GluTrianglulator triangulator = new GluTrianglulator(applet);
List<Vertex> physicsTris = triangulator.tesselate(physicsVertices, GLU.GLU_TESS_WINDING_NONZERO);
Vertex[] triangulatedBodyVerts = physicsTris.toArray(new Vertex[physicsTris.size()]);
//System.out.println("GLU tris created: " + triangulatedBodyVerts.length);
//Cap the max triangles - dont use anymore triangles for the physics body..
int cap = 400;
if (triangulatedBodyVerts.length > cap){
//System.err.println("OVER cap! -> capping!");
Vertex[] tmp = new Vertex[cap];
System.arraycopy(triangulatedBodyVerts, 0, tmp, 0, cap);
triangulatedBodyVerts = tmp;
}
//Create polygon body
world.destroyBody(body);
dymBodyDef = new BodyDef();
dymBodyDef.position = new Vec2(scaledPos.x, scaledPos.y);
this.bodyDefB4CreationCallback(dymBodyDef);
body = world.createBody(dymBodyDef);
for (int i = 0; i < triangulatedBodyVerts.length/3; i++) {
//Create polygon definition
PolygonDef polyDef = new PolygonDef();
if (density != 0.0f){
polyDef.density = density;
polyDef.friction = friction;
polyDef.restitution = restituion;
}
//Add triangle vertices
Vertex vertex1 = triangulatedBodyVerts[i*3];
Vertex vertex2 = triangulatedBodyVerts[i*3+1];
Vertex vertex3 = triangulatedBodyVerts[i*3+2];
polyDef.addVertex(new Vec2(vertex1.x, vertex1.y));
polyDef.addVertex(new Vec2(vertex2.x, vertex2.y));
polyDef.addVertex(new Vec2(vertex3.x, vertex3.y));
this.polyDefB4CreationCallback(pd); //FIXME TEST
//Add poly to body
body.createShape(polyDef);
}
body.setMassFromShapes();
//performance hit!?
//theBody.setBullet(true);
body.setUserData(this);
this.setUserData("box2d", body);
triangulator.deleteTess();
}
}
public void setPhysicsVertices(Vertex[] bodyVerts,
Vector3D pos,
float scale
){
if (this.body != null && this.world != null){
world.destroyBody(this.body);
}
//Scale physics vertics
// Vertex.scaleVectorArray(bodyVerts, Vector3D.ZERO_VECTOR, 1f/scale, 1f/scale, 1);
PhysicsHelper.scaleDown(bodyVerts, scale);
//Scale position
// pos.scaleLocal(1f/scale); //FIXME REALLY?
// PhysicsHelper.scaleDown(pos, scale);
Vector3D scaledPos = PhysicsHelper.scaleDown(pos.getCopy(), scale);
MTComponent parent = this.getParent();
this.removeFromParent();
//Temporarily move the mesh so that we know where the calculated center of the body
//would be (the body takes the body.position as the center reference instead of a calculated center)
// //FIXME welchen centerpoint nehmen? -> kommt auch drauf an ob shape schon auf canvas war!?
//We have to do this because the anchor point ("position") of the pyhsics shape is the body.position
//but the anchor point of our shapes is the point returned from getCenterpoint..()
this.translate(scaledPos);
Vector3D realBodyCenter = this.getCenterPointGlobal(); //FIXME geht nur if detached from world //rename futurebodycenter?
//Reset position
this.translate(scaledPos.getScaled(-1));
//Now get the position where the global center will be after setting the shape at the desired position
this.setPositionGlobal(scaledPos);
Vector3D meshCenterAtPosition = this.getCenterPointGlobal();
if (parent != null){
parent.addChild(this);//TODO add at same index
}
//Compute the distance we would have to move the vertices for the body creation
//so that the body.position(center) is at the same position as our mesh center
Vector3D realBodyCenterToMeshCenter = meshCenterAtPosition.getSubtracted(realBodyCenter);
//System.out.println("Diff:" + realBodyCenterToMeshCenter);
//Move the vertices so the body position is at the center of the shape
Vertex.translateVectorArray(bodyVerts, realBodyCenterToMeshCenter);
//FIXME TEST
this.setUserData("realBodyCenterToMeshCenter", realBodyCenterToMeshCenter);
//Create vertex structure for creation of decomposition polygon (use the translated vertices)
float xArr[] = new float[bodyVerts.length];
float yArr[] = new float[bodyVerts.length];
for (int i = 0; i < bodyVerts.length; i++) {
Vertex v = bodyVerts[i];
xArr[i] = v.x;
yArr[i] = v.y;
}
//Create a polygon too see if its simple and eventually decompose it
org.jbox2d.util.nonconvex.Polygon myPoly = new org.jbox2d.util.nonconvex.Polygon(xArr, yArr);
//System.out.println("Polygon is simple! -> Using convex decomposition for physics shape and glu triangulated mesh for display!");
PolygonDef pd = new PolygonDef();
if (density != 0.0f){
pd.density = density;
pd.friction = friction;
pd.restitution = restituion;
}
//Create polygon body
BodyDef dymBodyDef = new BodyDef();
dymBodyDef.position = new Vec2(scaledPos.x, scaledPos.y);
this.bodyDefB4CreationCallback(dymBodyDef);
this.body = world.createBody(dymBodyDef);
this.polyDefB4CreationCallback(pd); //FIXME TEST
int success = org.jbox2d.util.nonconvex.Polygon.decomposeConvexAndAddTo(myPoly, body, pd);
if (success != -1){
System.out.println("-> Ear clipping SUCCESSFUL -> Using triangulated and polygonized shape for b2d.");
body.setMassFromShapes();
body.setUserData(this);
this.setUserData("box2d", body);
//Performance hit! but prevents object from sticking to another sometimes
// theBody.setBullet(true);
}else{
System.out.println("-> Ear clipping had an ERROR - trying again by triangulating shape for b2d with GLU-Triangulator");
GluTrianglulator triangulator = new GluTrianglulator(this.getRenderer());
List<Vertex> physicsTris = triangulator.tesselate(bodyVerts, GLU.GLU_TESS_WINDING_NONZERO);
Vertex[] triangulatedBodyVerts = physicsTris.toArray(new Vertex[physicsTris.size()]);
//System.out.println("GLU tris created: " + triangulatedBodyVerts.length);
//Cap the max triangles - dont use anymore triangles for the physics body..
int cap = 400;
if (triangulatedBodyVerts.length > cap){
//System.err.println("OVER cap! -> capping!");
Vertex[] tmp = new Vertex[cap];
System.arraycopy(triangulatedBodyVerts, 0, tmp, 0, cap);
triangulatedBodyVerts = tmp;
}
//Create polygon body
world.destroyBody(body);
dymBodyDef = new BodyDef();
dymBodyDef.position = new Vec2(scaledPos.x, scaledPos.y);
this.bodyDefB4CreationCallback(dymBodyDef);
body = world.createBody(dymBodyDef);
for (int i = 0; i < triangulatedBodyVerts.length/3; i++) {
//Create polygon definition
PolygonDef polyDef = new PolygonDef();
if (density != 0.0f){
polyDef.density = density;
polyDef.friction = friction;
polyDef.restitution = restituion;
}
//Add triangle vertices
Vertex vertex1 = triangulatedBodyVerts[i*3];
Vertex vertex2 = triangulatedBodyVerts[i*3+1];
Vertex vertex3 = triangulatedBodyVerts[i*3+2];
polyDef.addVertex(new Vec2(vertex1.x, vertex1.y));
polyDef.addVertex(new Vec2(vertex2.x, vertex2.y));
polyDef.addVertex(new Vec2(vertex3.x, vertex3.y));
this.polyDefB4CreationCallback(polyDef); //FIXME TEST
//Add poly to body
body.createShape(polyDef);
}
body.setMassFromShapes();
//FIXME TEST - performance hit!?
//theBody.setBullet(true);
body.setUserData(this);
this.setUserData("box2d", body);
triangulator.deleteTess();
}
}
protected void polyDefB4CreationCallback(PolygonDef def){
}
protected void bodyDefB4CreationCallback(BodyDef def){
}
// private Vertex[] physicsVertices;
//
// private Vertex[] getPhysicsVertices(){
// return this.physicsVertices;
// }
//@Override
public void drawComponent(PGraphics g) {
super.drawComponent(g);
if (drawBounds){
IBoundingShape bounds = this.getBounds();
if (bounds instanceof BoundsArbitraryPlanarPolygon){
BoundsArbitraryPlanarPolygon bound = (BoundsArbitraryPlanarPolygon)bounds;
Vector3D[] boundVecs = bound.getVectorsLocal();
// app.noFill();
g.fill(100);
g.stroke(50);
g.beginShape();
for (int i = 0; i < boundVecs.length; i++) {
Vector3D v = boundVecs[i];
// app.vertex(v.x*scale, v.y*scale, v.z);
g.vertex(v.x, v.y, v.z);
}
g.endShape();
}
}
}
//@Override
public void rotateZGlobal(Vector3D rotationPoint, float degree) {
angle += degree;
super.rotateZGlobal(rotationPoint, degree);
}
public float getAngle() {
return angle;
}
public void setCenterRotation(float angle){
float degreeAngle = MTApplication.degrees(angle);
float oldAngle = this.getAngle();
float diff = degreeAngle-oldAngle;
//System.out.println("Old angle: " + oldAngle + " new angle:" + degreeAngle + " diff->" + diff);
this.rotateZGlobal(this.getCenterPointGlobal(), diff);
}
//@Override
protected void destroyComponent() {
super.destroyComponent();
boolean exists = false;
for (Body body = world.getBodyList(); body != null; body = body.getNext()) {
if (body.equals(this.body))
exists = true;//Delete later to avoid concurrent modification
}
if (exists)
world.destroyBody(body);
}
public World getWorld() {
return world;
}
public Body getBody() {
return body;
}
public float getDensity() {
return density;
}
public float getFriction() {
return friction;
}
public float getRestituion() {
return restituion;
}
}