package org.mt4jx.util.extension3D.collision;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;
import org.mt4j.AbstractMTApplication;
import org.mt4j.components.MTCanvas;
import org.mt4j.components.MTComponent;
import org.mt4j.components.visibleComponents.shapes.mesh.MTTriangleMesh;
import org.mt4j.components.visibleComponents.widgets.MTOverlayContainer;
import org.mt4j.input.gestureAction.DefaultDragAction;
import org.mt4j.input.gestureAction.DefaultRotateAction;
import org.mt4j.input.gestureAction.DefaultScaleAction;
import org.mt4j.input.gestureAction.ICollisionAction;
import org.mt4j.input.gestureAction.Rotate3DAction;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragEvent;
import org.mt4j.input.inputProcessors.componentProcessors.rotate3DProcessor.Cluster3DExt;
import org.mt4j.input.inputProcessors.componentProcessors.rotate3DProcessor.Rotate3DEvent;
import org.mt4j.input.inputProcessors.componentProcessors.rotateProcessor.RotateEvent;
import org.mt4j.input.inputProcessors.componentProcessors.scaleProcessor.ScaleEvent;
import org.mt4j.sceneManagement.IPreDrawAction;
import org.mt4j.sceneManagement.Iscene;
import org.mt4j.util.math.Matrix;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.Vector3D;
import org.mt4jx.components.visibleComponents.widgets.MTDepthHelper;
import org.mt4jx.input.gestureAction.DefaultDepthAction;
import org.mt4jx.input.inputProcessors.componentProcessors.depthProcessor.DepthGestureEvent;
import org.mt4jx.util.extension3D.ComponentHelper;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.dispatch.CollisionWorld;
import com.bulletphysics.collision.narrowphase.PersistentManifold;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.extras.gimpact.GImpactMeshShape;
import com.bulletphysics.linearmath.Transform;
public class SimulatePreDrawAction implements IPreDrawAction {
private CollisionWorld collisionWorld;
private float currentTimeStep = 0.f;
private Iscene sceneRef;
private boolean debugPoint = false;
private CollisionManager collisionManager;
private AbstractMTApplication mtApp;
private HashMap<MTComponent,Transform> oldComponentMatrices = new HashMap<MTComponent,Transform>();
private HashMap<MTComponent,Vector3f> oldComponentScaling = new HashMap<MTComponent,Vector3f>();
private ArrayList<MTComponent> contactMap = new ArrayList<MTComponent>();
private MTCanvas canvas;
//TODO this class should be optimized. performance and structure
public SimulatePreDrawAction(CollisionWorld v_collisionWorld,CollisionManager v_collisionManager,MTCanvas v_canvas)
{
this.setCollisionWorld(v_collisionWorld);
this.collisionManager = v_collisionManager;
this.canvas = v_canvas;
}
public boolean isLoop() {
return true;
}
public void processAction() {
if(getCollisionWorld()!=null&&getCurrentTimeStep()!=0.f)
{
final Transform m = new Transform();
m.setIdentity();
if(getCollisionWorld()!=null)
{
for(int i=0;i<getCollisionWorld().getNumCollisionObjects();i++)
{
CollisionObject obj = getCollisionWorld().getCollisionObjectArray().get(i);
CollisionShape shape = obj.getCollisionShape();
MTComponent mesh = (MTComponent)collisionManager.getAssociatedComponent(obj);
if(mesh!=null)
{
Matrix mat = mesh.getGlobalMatrix();
Transform tf = new Transform();
Matrix4f mat4f = CollisionManager.convertMT4JMatrixToMatrix4f(mat);
tf.set(mat4f);
Transform oldTransform = new Transform();//save old values
obj.getWorldTransform(oldTransform);
//temp
Matrix4f mat1 = new Matrix4f();
oldTransform.getMatrix(mat1);
oldComponentMatrices.put(getFirstNonMTTriangleMeshParent(mesh), oldTransform);
//save current scaling for later undo
Vector3f oldScaleVec = new Vector3f();
obj.getCollisionShape().getLocalScaling(oldScaleVec);
oldComponentScaling.put(getFirstNonMTTriangleMeshParent(mesh),oldScaleVec);
//tf.basis.setScale(1.0f);
//tf.origin.set(vec);
Vector3f scale = new Vector3f(); //get scale value of global matrix
Vector3D xVec = new Vector3D(mat.m00,mat.m01,mat.m02);
Vector3D yVec = new Vector3D(mat.m10,mat.m11,mat.m12);
Vector3D zVec = new Vector3D(mat.m20,mat.m21,mat.m22);
scale.x = xVec.length();
scale.y = yVec.length();
scale.z = zVec.length();
float[] scaleVals = new float[3];
scale.get(scaleVals);
for(int a=0;a<3;a++)//get rotation value by extracting scalation
{
try {
float[] colvals = mat.getRow(a);
for(int j=0;j<3;j++)
{
colvals[j] = colvals[j] / scaleVals[a];
}
tf.basis.setRow(a,colvals);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
obj.getCollisionShape().setLocalScaling(scale);//apply scaling of world matrix to the collision shape, remember no scaling in rigid body
((GImpactMeshShape)obj.getCollisionShape()).updateBound();//has to be done that the collision shape bound is updated after scaling
obj.setWorldTransform(tf); //set world transform of object, only rotation and translation of MT4j world matrix
}
}
getCollisionWorld().performDiscreteCollisionDetection();//do bullet engine collision detection
//do ray intersection testing for objects which
//are translated in depth
//because it can be that if the velocity is higher than
//the size of the object that their is no collision.
//This is due to in the moment before the collision the
//object is completely in front of the other object and the moment after the
//object is completely after the other object. So the collision
//is missed. Thus we do a ray intersection test
//between the two transformation points
//TODO this works only for a bottom plane in z direction
//it should be extended to all other directions
for(int i=0;i<canvas.getChildren().length;i++)
{
if(canvas.getChildren()[i] instanceof MTDepthHelper)
{
MTDepthHelper helper = (MTDepthHelper) canvas.getChildren()[i];
MTComponent targetComp = (MTComponent) helper.getTargetComponent();
if(targetComp instanceof Cluster3DExt)
{
Cluster3DExt cl = (Cluster3DExt)targetComp;
MTComponent[] clChildren = cl.getChildren();
for(MTComponent children : clChildren)
{
Transform trans = oldComponentMatrices.get(children);
Vector3f vecOld = trans.origin;
Vector3D vecOldMt4j = new Vector3D(vecOld.x,vecOld.y,vecOld.z);
ArrayList<CollisionObject> objs = collisionManager.getAllObjectsForCollisionGroup(children);
if(objs.size()>0)
{
Transform out = new Transform();
objs.get(0).getWorldTransform(out);
Vector3f vecNew = out.origin;
Vector3D vecNewMt4j = new Vector3D(vecNew.x,vecNew.y,vecNew.z);
Ray ray = new Ray(vecOldMt4j,vecNewMt4j);
boolean clusterCollision = false;//determines if there is a cluster collision
CollisionObject collisionObjectOnCanvas = null; //object with which the cluster collides
for(int a=0;a<canvas.getChildren().length;a++)
{
if(canvas.getChildren()[a]!=children&&!(canvas.getChildren()[a] instanceof Cluster3DExt)&&!(canvas.getChildren()[a] instanceof MTOverlayContainer)&&(ComponentHelper.getCenterPointGlobal(canvas.getChildren()[a])!=null))
{
if(ComponentHelper.getCenterPointGlobal(canvas.getChildren()[a]).z<ray.getRayStartPoint().z&&ComponentHelper.getCenterPointGlobal(canvas.getChildren()[a]).z>ray.getPointInRayDirection().z)
{
Vector3D interSectionPos = ComponentHelper.getIntersectionGlobal(canvas.getChildren()[a],ray);
if(interSectionPos!=null)
{
clusterCollision = true;
collisionObjectOnCanvas = collisionManager.getAllObjectsForCollisionGroup((canvas.getChildren()[a])).get(0);
break;
}
}
}
}
//}
//in case of any of the children of the clusters has a collision
//prepare collision for all children
if(clusterCollision)
{
objectCollision(collisionObjectOnCanvas);
for(MTComponent comp : clChildren)
{
ArrayList<CollisionObject> compColObjs = collisionManager.getAllObjectsForCollisionGroup(comp);
objectCollision(compColObjs.get(0));//get only first collision object, collision of other objects will be done in objectCollision method
}
}
}
}
}else
{
Transform trans = oldComponentMatrices.get(targetComp);
Vector3f vecOld = trans.origin;
Vector3D vecOldMt4j = new Vector3D(vecOld.x,vecOld.y,vecOld.z);
ArrayList<CollisionObject> objs = collisionManager.getAllObjectsForCollisionGroup(targetComp);
if(objs.size()>0)
{
Transform out = new Transform();
objs.get(0).getWorldTransform(out);
Vector3f vecNew = out.origin;
Vector3D vecNewMt4j = new Vector3D(vecNew.x,vecNew.y,vecNew.z);
Ray ray = new Ray(vecOldMt4j,vecNewMt4j);
for(int a=0;a<canvas.getChildren().length;a++)
{
if(canvas.getChildren()[a]!=targetComp&&!(canvas.getChildren()[a] instanceof MTOverlayContainer)&&(ComponentHelper.getCenterPointGlobal(canvas.getChildren()[a])!=null))
{
if(ComponentHelper.getCenterPointGlobal(canvas.getChildren()[a]).z<ray.getRayStartPoint().z
&&ComponentHelper.getCenterPointGlobal(canvas.getChildren()[a]).z>ray.getPointInRayDirection().z)
{
Vector3D interSectionPos = ComponentHelper.getIntersectionGlobal(canvas.getChildren()[a],ray);
if(interSectionPos!=null)
{
objectCollision(objs.get(0));//get only first collision object, collision of other objects will be done in objectCollision method
objectCollision(collisionManager.getAllObjectsForCollisionGroup((canvas.getChildren()[a])).get(0));//the object with which it is colliding
}
}
}
}
}
}
}
}
//test if engine detected collisions and
//execute collision behaviour for colliding objects
int numManifolds = collisionWorld.getDispatcher().getNumManifolds();
boolean contact = false;
for(int a=0;a<numManifolds;a++)
{
PersistentManifold contactManifold = collisionWorld.getDispatcher().getManifoldByIndexInternal(a);
Cluster3DExt cl = isChildrenOfACluster(collisionManager.getAssociatedComponent((CollisionObject)contactManifold.getBody0()));
Cluster3DExt cl2 = isChildrenOfACluster(collisionManager.getAssociatedComponent((CollisionObject)contactManifold.getBody1()));
if(cl!=null)
{
performCollisionForClusterChildren(cl);
}else
{
objectCollision(contactManifold.getBody0());//set back collision object 1
}
if(cl2!=null)
{
performCollisionForClusterChildren(cl2);
}else
{
objectCollision(contactManifold.getBody1());//set back collision object 2
}
}
}
}
Iterator<MTComponent> iterGroups = collisionManager.getAllCollisionGroups().iterator();
/*if(contactMap.size()==0)
{
System.out.println("contact map empty");
}
else
{
System.out.println("contact map full");
}*/
while(iterGroups.hasNext())
{
MTComponent comp = iterGroups.next();
if(!contactMap.contains(comp))
{
//get MTCanvas to look for a Drag Helper Object
//if one is on the canvas and the target comp is our collision body pause the depth gesture
/*for(int i=0;i<canvas.getChildren().length;i++)
{
if(canvas.getChildren()[i] instanceof MTDepthHelper)
{
MTDepthHelper helper = (MTDepthHelper) canvas.getChildren()[i];
MTComponent targetComp = (MTComponent) helper.getTargetComponent();
if(targetComp==comp)
{
if(helper.getDepthProcessor().isGesturePaused())
{
System.out.println("resume");
helper.getDepthProcessor().resumeGesture();
}
}
}
}*/
for(int counterListener=0;counterListener<comp.getGestureListeners().length;counterListener++)
{
IGestureEventListener listener = comp.getGestureListeners()[counterListener];
if(listener instanceof ICollisionAction)
{
boolean gestureAbort = proofGestureAbort(listener,comp);
((ICollisionAction)listener).setGestureAborted(gestureAbort);
}
}
}
}
contactMap.clear();
}
private void performCollisionForClusterChildren(Cluster3DExt cl)
{
if(cl!=null)
{
for(MTComponent clusterChildren : cl.getChildren())
{
objectCollision(collisionManager.getAllObjectsForCollisionGroup(clusterChildren).get(0));
}
}
}
/**
* perform collision behaviour for a specific object
* 1. put object into a map
* 2. stop gesture actions
* 3. set back to old transform and scale
* @param body
*/
private void objectCollision(Object body)
{
if(body instanceof CollisionObject)
{
CollisionObject obj = (CollisionObject)body;
MTComponent comp = collisionManager.getAssociatedComponent(obj);
//add collision object to contact map, so gestures
//will not be resumed for this component
if(!contactMap.contains(getFirstNonMTTriangleMeshParent(comp)))
{
contactMap.add(getFirstNonMTTriangleMeshParent(comp));
}else
{
return;//everything has already been done for this object
}
for(int b=0;b<getFirstNonMTTriangleMeshParent(comp).getGestureListeners().length;b++)
{
if(comp.getParent().getGestureListeners()[b] instanceof org.mt4j.input.gestureAction.ICollisionAction)
{
org.mt4j.input.gestureAction.ICollisionAction dragAction = (org.mt4j.input.gestureAction.ICollisionAction)getFirstNonMTTriangleMeshParent(comp).getGestureListeners()[b];
dragAction.setGestureAborted(true);
}
}
//set back to old transform and scale
Transform oldTransform = oldComponentMatrices.get(getFirstNonMTTriangleMeshParent(comp));
Vector3f oldScale = oldComponentScaling.get(getFirstNonMTTriangleMeshParent(comp));
Iterator<CollisionObject> iter = collisionManager.getAllObjectsForCollisionGroup(getFirstNonMTTriangleMeshParent(comp)).iterator();
while(iter.hasNext())
{
CollisionObject colObj = iter.next();
colObj.setWorldTransform(oldTransform);
colObj.getCollisionShape().setLocalScaling(oldScale);
((GImpactMeshShape)colObj.getCollisionShape()).updateBound();//has to be done that the collision shape bound is updated after scaling
}
//oldTransform.basis.
Matrix oldMatrix = new Matrix();
try
{
for(int counter=0;counter<3;counter++)
{
float[] colVals = new float[4];
oldTransform.basis.getRow(counter, colVals);
oldMatrix.setRow(counter, colVals);
}
float[] translateVals = new float[4];
oldTransform.origin.get(translateVals);
oldMatrix.setColumn(3,translateVals);
Vector3f vec = new Vector3f();
//get the scaling for our matrix from the collision shape, because the
//rigid body must not have any scaling in his matrix
obj.getCollisionShape().getLocalScaling(vec);
Vector3D vecMT4J = new Vector3D(vec.x,vec.y,vec.z);
float scaleVals[] = new float[3];
vecMT4J.toArray(scaleVals);
//oldMatrix.scale(vecMT4J);
for(int i=0;i<3;i++)
{
float[] row = oldMatrix.getRow(i);
for(int a=0;a<3;a++)
{
row[a] = row[a]*scaleVals[i];
oldMatrix.setRow(i, row);
}
}
oldMatrix.m33 = 1f;
resetComponentMatrix(oldMatrix,getFirstNonMTTriangleMeshParent(comp));
}catch(Exception e)
{
e.printStackTrace();
}
}
//end - set back to old transform
}
private void resetComponentMatrix(Matrix mat,MTComponent comp)
{
//comp.getGlobalMatrix().set(mat);
//comp.getLocalMatrix().set(mat);
comp.setLocalMatrix(mat);
/*for(MTComponent childComp : comp.getChildren())
{
resetComponentMatrix(mat,childComp);
}*/
}
public void setCurrentTimeStep(float currentTimeStep) {
this.currentTimeStep = currentTimeStep;
}
public float getCurrentTimeStep() {
return currentTimeStep;
}
public void setCollisionWorld(CollisionWorld collisionWorld) {
this.collisionWorld = collisionWorld;
}
public CollisionWorld getCollisionWorld() {
return collisionWorld;
}
public void setDebugPoint(boolean debugPoint) {
this.debugPoint = debugPoint;
}
public boolean isDebugPoint() {
return debugPoint;
}
private MTComponent getFirstNonMTTriangleMeshParent(MTComponent comp)
{
if(!(comp instanceof MTTriangleMesh))
{
return comp;
}
return getFirstNonMTTriangleMeshParent(comp.getParent());
}
private Cluster3DExt isChildrenOfACluster(MTComponent comp)
{
if(comp instanceof Cluster3DExt)
{
return (Cluster3DExt)comp;
}
if(comp.getParent()==null)
{
return null;
}
return isChildrenOfACluster(comp.getParent());
}
/**
* proofs for specific actions if the gesture should be aborted
* @param gestureListener the Action which should be tested
* @return false if it should be aborted
*/
private boolean proofGestureAbort(IGestureEventListener gestureListener,MTComponent comp)
{
if(gestureListener instanceof DefaultDragAction)
{
DefaultDragAction act = (DefaultDragAction)gestureListener;
DragEvent lastEvent = (DragEvent) act.getLastEvent();
if(lastEvent!=null)
{
if (comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp, lastEvent.getDragCursor().getCurrentEvtPosX(), lastEvent.getDragCursor().getCurrentEvtPosY())) != null)
{
return false;
}
}
}
else if(gestureListener instanceof DefaultScaleAction)
{
DefaultScaleAction act = (DefaultScaleAction)gestureListener;
ScaleEvent lastEvent = (ScaleEvent)act.getLastEvent();
if(lastEvent!=null)
{
if (comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp,
lastEvent.getFirstCursor().getCurrentEvtPosX(), lastEvent.getFirstCursor().getCurrentEvtPosY())) != null ||
comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp,
lastEvent.getSecondCursor().getCurrentEvtPosX(), lastEvent.getSecondCursor().getCurrentEvtPosY())) != null)
{
return false;
}
}
}
else if(gestureListener instanceof DefaultRotateAction)
{
DefaultRotateAction act = (DefaultRotateAction)gestureListener;
RotateEvent lastEvent = (RotateEvent)act.getLastEvent();
if(lastEvent!=null)
{
if (comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp,
lastEvent.getFirstCursor().getCurrentEvtPosX(), lastEvent.getFirstCursor().getCurrentEvtPosY())) != null ||
comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp,
lastEvent.getSecondCursor().getCurrentEvtPosX(), lastEvent.getSecondCursor().getCurrentEvtPosY())) != null)
{
return false;
}
}
}
else if(gestureListener instanceof Rotate3DAction)
{
Rotate3DAction act = (Rotate3DAction)gestureListener;
Rotate3DEvent lastEvent = (Rotate3DEvent)act.getLastEvent();
if(lastEvent!=null)
{
if (comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp,
lastEvent.getFirstCursor().getCurrentEvtPosX(), lastEvent.getFirstCursor().getCurrentEvtPosY())) != null ||
comp.getIntersectionGlobal(Tools3D.getCameraPickRay(comp.getRenderer(), comp,
lastEvent.getSecondCursor().getCurrentEvtPosX(), lastEvent.getSecondCursor().getCurrentEvtPosY())) != null)
{
return false;
}
}
}else if(gestureListener instanceof DefaultDepthAction)
{
DefaultDepthAction act = (DefaultDepthAction)gestureListener;
DepthGestureEvent lastEvent = (DepthGestureEvent)act.getLastEvent();
if(lastEvent!=null)
{
return false;
}
}
return true;
}
}