/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package advanced.physics.util;
import java.util.ArrayList;
import java.util.List;
import javax.media.opengl.glu.GLU;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.EdgeChainDef;
import org.jbox2d.collision.shapes.EdgeShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;
import org.jbox2d.dynamics.joints.Joint;
import org.jbox2d.dynamics.joints.JointType;
import org.jbox2d.dynamics.joints.MouseJoint;
import org.jbox2d.dynamics.joints.MouseJointDef;
import org.jbox2d.util.nonconvex.Polygon;
import org.mt4j.MTApplication;
import org.mt4j.components.MTComponent;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.MTGestureEvent;
import org.mt4j.input.inputProcessors.componentProcessors.AbstractComponentProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragEvent;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragProcessor;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.MultipleDragProcessor;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GLMaterial;
import org.mt4j.util.opengl.GluTrianglulator;
import advanced.physics.physicsShapes.PhysicsRectangle;
import processing.core.PApplet;
public class PhysicsHelper {
public static MouseJoint createDragJoint(World world, Body body, float x, float y){
MouseJointDef mjd = new MouseJointDef();
mjd.body1 = body; // Not used, avoid a NPE
mjd.body2 = body;
mjd.target = new Vec2(x, y);
// mjd.target = new Vec2(x/scale, y/scale);
// mjd.maxForce = 8000.0f * body.m_mass;
// mjd.maxForce = 99000.0f * body.m_mass;
// mjd.maxForce = 99000.0f * body.m_mass;
// mjd.maxForce = Float.MAX_VALUE; //Too big values will result in erratic behaviour with more than 1 mousejoint on a component
mjd.maxForce = 90000.0f * body.m_mass;
return (MouseJoint) world.createJoint(mjd);
}
// /*
public static void removeDragJoints(Body body){
// MouseJoint mouseJoint = (MouseJoint) comp.getUserData("mouseJoint");
// if (mouseJoint != null){
// world.destroyJoint(mouseJoint);
// }
//FIXME this doesent remove the userData that pointed to the mousejoint!
for (Joint joint = body.getWorld().getJointList(); joint != null; joint = joint.getNext()) {
JointType type = joint.getType();
switch (type) {
case MOUSE_JOINT:
MouseJoint mj = (MouseJoint)joint;
if (body.equals(mj.getBody1()) || body.equals(mj.getBody2())){
body.getWorld().destroyJoint(mj);
}
break;
default:
break;
}
}
}
// */
public static void addDragJoint(World world, MTComponent comp, boolean isDynamic, float scale){
final float worldScale = scale;
final World theWorld = world;
if (isDynamic){
//DYNAMIC BODIES MAKE MOUSEJOINTS
comp.removeAllGestureEventListeners(DragProcessor.class);
comp.registerInputProcessor(new MultipleDragProcessor(comp.getRenderer()));
comp.addGestureListener(MultipleDragProcessor.class, new IGestureEventListener() {
// comp.addGestureListener(DragProcessor.class, new IGestureEventListener() {
//@Override
public boolean processGestureEvent(MTGestureEvent ge) {
DragEvent de = (DragEvent)ge;
try{
MTComponent comp = (MTComponent)de.getTargetComponent();
Body body = (Body)comp.getUserData("box2d");
MouseJoint mouseJoint;
Vector3D to = new Vector3D(de.getTo());
//Un-scale position from mt4j to box2d
PhysicsHelper.scaleDown(to, worldScale);
//System.out.println("MouseJoint To: " + to);
long cursorID = de.getDragCursor().getId();
switch (de.getId()) {
case DragEvent.GESTURE_DETECTED:
comp.sendToFront();
body.wakeUp();
mouseJoint = createDragJoint(theWorld, body, to.x, to.y);
comp.setUserData("mouseJoint" + cursorID, mouseJoint);
break;
case DragEvent.GESTURE_UPDATED:
mouseJoint = (MouseJoint) comp.getUserData("mouseJoint" + cursorID);
if (mouseJoint != null){
mouseJoint.setTarget(new Vec2(to.x, to.y));
}
break;
case DragEvent.GESTURE_ENDED:
mouseJoint = (MouseJoint) comp.getUserData("mouseJoint" + cursorID);
if (mouseJoint != null){
comp.setUserData("mouseJoint" + cursorID, null);
// theWorld.destroyJoint(mouseJoint);
//Only destroy the joint if it isnt already (go through joint list and check)
for (Joint joint = theWorld.getJointList(); joint != null; joint = joint.getNext()) {
JointType type = joint.getType();
switch (type) {
case MOUSE_JOINT:
MouseJoint mj = (MouseJoint)joint;
if (body.equals(mj.getBody1()) || body.equals(mj.getBody2())){
// theWorld.destroyJoint(mj);
if (mj.equals(mouseJoint)) {
theWorld.destroyJoint(mj);
}
}
break;
default:
break;
}
}
}
mouseJoint = null;
break;
default:
break;
}
}catch (Exception e) {
System.err.println(e.getMessage());
}
return true;
}
});
}else{
comp.removeAllGestureEventListeners(DragProcessor.class);
boolean hasDragProcessor = false;
AbstractComponentProcessor[] p = comp.getInputProcessors();
for (int i = 0; i < p.length; i++) {
AbstractComponentProcessor abstractComponentProcessor = p[i];
if (abstractComponentProcessor instanceof DragProcessor) {
hasDragProcessor = true;
}
}
if (!hasDragProcessor){
comp.registerInputProcessor(new DragProcessor(comp.getRenderer()));
}
//For static bodies just alter the transform of the body
comp.addGestureListener(DragProcessor.class, new IGestureEventListener() {
//@Override
public boolean processGestureEvent(MTGestureEvent ge) {
DragEvent de = (DragEvent)ge;
Vector3D dir = PhysicsHelper.scaleDown(new Vector3D(de.getTranslationVect()), worldScale);
try{
MTComponent comp = (MTComponent)de.getTargetComponent();
Body body = (Body)comp.getUserData("box2d");
body.setXForm(
new Vec2(body.getPosition().x + dir.x, body.getPosition().y + dir.y),
body.getAngle());
switch (de.getId()) {
case DragEvent.GESTURE_DETECTED:
comp.sendToFront();
body.wakeUp();
break;
case DragEvent.GESTURE_UPDATED:
case DragEvent.GESTURE_ENDED:
default:
break;
}
}catch (Exception e) {
System.err.println(e.getMessage());
}
return true;
}
});
}
}
public static List<Vertex> triangulateEarClips(List<Vertex> vertices){
org.jbox2d.util.nonconvex.Triangle[] tri = getEarClipTriangles(vertices);
List<Vertex> tris = new ArrayList<Vertex>();
for (int i = 0; i < tri.length; i++) {
org.jbox2d.util.nonconvex.Triangle triangle = tri[i];
tris.add(new Vertex(triangle.x[0], triangle.y[0],0));
tris.add(new Vertex(triangle.x[1], triangle.y[1],0));
tris.add(new Vertex(triangle.x[2], triangle.y[2],0));
}
return tris;
}
public static org.jbox2d.util.nonconvex.Triangle[] getEarClipTriangles(List<Vertex> vertices){
org.jbox2d.util.nonconvex.Triangle[] tri = null;
float[] xCoords = new float[vertices.size()];
float[] yCoords = new float[vertices.size()];
for (int i = 0; i < vertices.size(); i++) {
xCoords[i] = vertices.get((vertices.size()-1)-i).x;
yCoords[i] = vertices.get((vertices.size()-1)-i).y;
}
//tri = earClipper.triangulatePolygon(xCoords, yCoords, vertices.size());
org.jbox2d.util.nonconvex.Triangle[] triangulated = new org.jbox2d.util.nonconvex.Triangle[vertices.size() - 2];
for (int i=0; i<triangulated.length; ++i) {
triangulated[i] = new org.jbox2d.util.nonconvex.Triangle();
}
Polygon.triangulatePolygon(xCoords, yCoords, vertices.size(), triangulated);
//Try reversed order
if (tri == null){
System.err.println("Null! trying reversed!");
for (int i = 0; i < vertices.size(); i++) {
xCoords[i] = vertices.get(i).x;
yCoords[i] = vertices.get(i).y;
}
// tri = earClipper.triangulatePolygon(xCoords, yCoords, vertices.size());
Polygon.triangulatePolygon(xCoords, yCoords, vertices.size(), triangulated);
}
return tri;
}
public static List<Vertex> triangulateGLU(MTApplication app, List<Vertex> vertices){
System.err.println("Trying glu triangulation..");
GluTrianglulator triangulator = new GluTrianglulator(app);
Vertex[] vertexArray = vertices.toArray(new Vertex[vertices.size()]);
List<Vertex> triVerts = triangulator.tesselate(vertexArray, GLU.GLU_TESS_WINDING_NONZERO);
return triVerts;
}
public static float scaleDown(float distance, float physicsScale){
return distance /physicsScale;
}
public static float scaleUp(float distance, float physicsScale){
return distance * physicsScale;
}
public static Vertex[] scaleDown(Vertex[] vertices, float physicsScale){
return Vertex.scaleVectorArray(vertices, Vector3D.ZERO_VECTOR, 1f/physicsScale, 1f/physicsScale, 1);
}
public static Vertex[] scaleUp(Vertex[] vertices, float physicsScale){
return Vertex.scaleVectorArray(vertices, Vector3D.ZERO_VECTOR, physicsScale, physicsScale, 1);
}
public static Vector3D[] scaleDown(Vector3D[] vertices, float physicsScale){
return Vector3D.scaleVectorArray(vertices, Vector3D.ZERO_VECTOR, 1f/physicsScale, 1f/physicsScale, 1);
}
public static Vector3D[] scaleUp(Vector3D[] vertices, float physicsScale){
return Vector3D.scaleVectorArray(vertices, Vector3D.ZERO_VECTOR, physicsScale, physicsScale, 1);
}
public static Vector3D scaleDown(Vector3D vec, float physicsScale){
return vec.scaleLocal(1f/physicsScale);
}
public static Vector3D scaleUp(Vector3D vec, float physicsScale){
return vec.scaleLocal(physicsScale);
}
public static GLMaterial createDefaultGLMaterial(PApplet app){
//Set up a material
GLMaterial material = new GLMaterial(Tools3D.getGL(app));
material.setAmbient(new float[]{ .2f, .2f, .2f, 1f });
material.setDiffuse(new float[]{ .8f, .8f, .8f, 1f } );
material.setEmission(new float[]{ .0f, .0f, .0f, 1f });
material.setSpecular(new float[]{ 1.0f, 1.0f, 1.0f, 1f }); // almost white: very reflective
material.setShininess(110);// 0=no shine, 127=max shine
return material;
}
/**
* Draw physics debug.
*/
public static void drawDebugPhysics(PApplet app, World world, float scale){
app.fill(180, 190);
app.stroke(140, 190);
app.strokeWeight(1);
app.pushMatrix();
// this.getSceneCam().update();
app.scale(scale, scale);
for (Body body = world.getBodyList(); body != null; body = body.getNext()) {
Shape shape;
for (shape = body.getShapeList(); shape != null; shape = shape.getNext()) {
switch (shape.getType()) {
case POLYGON_SHAPE:
app.beginShape();
PolygonShape poly = (PolygonShape)shape;
int count = poly.getVertexCount();
Vec2[] verts = poly.getVertices();
for(int i = 0; i < count; i++) {
Vec2 vert = body.getWorldLocation(verts[i]);
app.vertex(vert.x , vert.y );
}
app.endShape();
break;
case EDGE_SHAPE:
EdgeShape edge = (EdgeShape)shape;
Vec2 v1 = body.getWorldLocation(edge.getVertex1());
Vec2 v2 = body.getWorldLocation(edge.getVertex2());
app.beginShape();
app.vertex(v1.x , v1.y );
app.vertex(v2.x , v2.y );
app.endShape();
// app.line(v1.x , v1.y ,v2.x , v2.y );
/*
EdgeShape next = edge;
while (next.getNextEdge() != null){
next = edge.getNextEdge();
Vec2 v11 = body.getWorldLocation(next.getVertex1());
Vec2 v22 = body.getWorldLocation(next.getVertex2());
app.beginShape();
app.vertex(v11.x , v11.y );
app.vertex(v22.x , v22.y );
app.endShape();
}
*/
break;
case CIRCLE_SHAPE:
CircleShape circle = (CircleShape)shape;
float radius = circle.getRadius();
Vec2 c = body.getWorldLocation(circle.getLocalPosition());
app.ellipseMode(PApplet.CENTER);
app.ellipse(c.x, c.y, radius+2, radius+2);
break;
default:
break;
}
}
}
app.popMatrix();
}
public static void addScreenBoundaries(PApplet app, World world, MTComponent futureParent, float scale){
// CREATE SCREEN BORDERS \\
//Left border
float borderWidth = 50f;
float borderHeight = app.height;
Vector3D pos = new Vector3D(-(borderWidth/2f) , app.height/2f);
PhysicsRectangle borderLeft = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderLeft.setName("borderLeft");
futureParent.addChild(borderLeft);
//Right border
pos = new Vector3D(app.width + (borderWidth/2), app.height/2);
PhysicsRectangle borderRight = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderRight.setName("borderRight");
futureParent.addChild(borderRight);
//Top border
borderWidth = app.width;
borderHeight = 50f;
pos = new Vector3D(app.width/2, -(borderHeight/2));
PhysicsRectangle borderTop = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderTop.setName("borderTop");
futureParent.addChild(borderTop);
//Bottom border
pos = new Vector3D(app.width/2 , app.height + (borderHeight/2));
PhysicsRectangle borderBottom = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderBottom.setName("borderBottom");
futureParent.addChild(borderBottom);
}
/**
* Adds an edgeShape around the world with a slight offset so
* that every shape hits the edge first before hitting the world boundaries.
*
* @param app the app
* @param world the world
* @param scale the scale
*
* @return the edge body
*/
public static Body addWorldEdgeBoundaries(PApplet app, World world, float scale){
AABB wAABB = world.getWorldAABB();
Vec2 l = new Vec2(wAABB.lowerBound);
Vec2 u = new Vec2(wAABB.upperBound);
float worldWidth = u.x - l.x;
float worldHeight = u.y - l.y;
float offset = 1f; //kleiner als world machen
BodyDef dymBodyDef = new BodyDef();
dymBodyDef.position = new Vec2(0 , 0 );
// dymBodyDef.position = new Vec2(worldWidth*0.5f , worldHeight*0.5f);
Body theBody = world.createBody(dymBodyDef);
EdgeChainDef myEdges = new EdgeChainDef();
//CCW so edge points inwards
myEdges.addVertex(new Vec2(l.x + offset , l.y + worldHeight - offset*2));
myEdges.addVertex(new Vec2(l.x + worldWidth - offset*2, l.y + worldHeight - offset*2));
myEdges.addVertex(new Vec2(l.x + worldWidth - offset*2, l.y + offset));
myEdges.addVertex(new Vec2(l.x + offset , l.y + offset));
myEdges.setIsLoop(true);
myEdges.friction = 2.0f;
// myEdges.density = 1.0f;
myEdges.density = 0.0f;
myEdges.isSensor = true;
theBody.createShape(myEdges);
theBody.setMassFromShapes();
return theBody;
}
}