/*
* Copyright Inria and Bordeaux University.
* Author Jeremy Laviole. jeremy.laviole@inria.fr
* PapAR project is the open-source version of the
* PapARt project. License is LGPLv3, distributed with the sources.
* This project can also distributed with standard commercial
* licence for closed-sources projects.
*/
package fr.inria.papart.procam;
import fr.inria.papart.calibration.HomographyCalibration;
import fr.inria.papart.calibration.HomographyCreator;
import fr.inria.papart.procam.camera.Camera;
import static fr.inria.papart.procam.Utils.toVec;
import processing.core.PApplet;
import processing.core.PMatrix3D;
import processing.core.PVector;
import processing.opengl.PGraphicsOpenGL;
import toxi.geom.*;
/**
* This class implements a virtual screen. The position of the screen has to be
* passed. It no longers handles a camera.
*
* @author jeremylaviole
*/
public class Screen {
private PApplet parent;
// The current graphics
private PGraphicsOpenGL thisGraphics;
// Position holding...
private PMatrix3D initPosM = null;
// Either one, or the other is unique to this object.
// The other one is unique to the camera/markerboard couple.
private float[] posFloat;
private PMatrix3D transformation = new PMatrix3D(); // init to avoid nullPointerExceptions
private PMatrix3D secondTransformation = null;
private boolean isFloatArrayUpdating;
////////////
private PVector size;
private float scale;
protected Plane plane = new Plane();
private static final int nbPaperPosRender = 4;
private final PVector[] paperPosCorners3D = new PVector[nbPaperPosRender];
// TODO: update this again
private HomographyCreator homography;
protected HomographyCalibration worldToScreen;
public float halfEyeDist = 10; // 2cm
private boolean isDrawing = true;
private boolean isOpenGL = false;
public Screen(PApplet parent, PVector size, float scale) {
this(parent, size, scale, false, 1);
}
public Screen(PApplet parent, PVector size, float scale, boolean useAA, int AAValue) {
this.size = size.get();
this.scale = scale;
this.parent = parent;
initHomography();
}
// Get the texture to display...
public PGraphicsOpenGL getTexture() {
return getGraphics();
}
public PGraphicsOpenGL getGraphics() {
if (thisGraphics == null) {
thisGraphics = (PGraphicsOpenGL) parent.createGraphics((int) (size.x * scale), (int) (size.y * scale), PApplet.OPENGL);
}
return thisGraphics;
}
// The board must be registered with the camera.
/**
* **
* @deprecated
*
*/
// public void setAutoUpdatePos(Camera camera, MarkerBoard board) {
// if (!camera.tracks(board)) {
// camera.trackMarkerBoard(board);
// }
//
// isFloatArrayUpdating = board.useFloatArray();
// if (this.isFloatArrayUpdating) {
// posFloat = board.getTransfo(camera);
// transformation = new PMatrix3D();
// } else {
//// System.out.println("Getting the original transfo");
//
// transformation = board.getTransfoMat(camera);
// posFloat = new float[12];
// }
// }
public boolean isOpenGL() {
return isOpenGL;
}
public boolean isDrawing() {
return isDrawing;
}
public void setDrawing(boolean isDrawing) {
this.isDrawing = isDrawing;
}
public PVector getSize() {
return size;
}
public int getDrawSizeX() {
return (int) (size.x * scale);
}
public int getDrawSizeY() {
return (int) (size.y * scale);
}
/**
* Set the main position (override tracking system). Use only after the call
* of paperScreen.useManualLocation(false);
*
* @param position
*/
public void setMainLocation(PMatrix3D position) {
transformation.set(position);
}
/**
* Set a second transformation applied after tracking transform.
*
* @param tr
*/
public void setTransformation(PMatrix3D tr) {
if (secondTransformation == null) {
this.secondTransformation = new PMatrix3D(tr);
} else {
this.secondTransformation.set(tr);
}
}
/**
* Set an additional translation (replace the second transformation)
*
* @param tr
*/
public void setTranslation(PVector tr) {
setTranslation(tr.x, tr.y, tr.z);
}
/**
* Set an additional translation (replace the second transformation)
*
* @param x
* @param y
* @param z
*/
public void setTranslation(float x, float y, float z) {
if (secondTransformation == null) {
secondTransformation = new PMatrix3D();
}
secondTransformation.reset();
secondTransformation.translate(x, y, z);
}
/**
* @deprecated @return
*/
public PMatrix3D getPosition() {
return getLocation();
}
/**
* Get a copy of the overall transform (after tracking and second
* transform).
*
* @return
*/
public PMatrix3D getLocation() {
if (secondTransformation == null) {
return transformation.get();
} else {
PMatrix3D combinedTransfos = transformation.get();
combinedTransfos.apply(secondTransformation);
return combinedTransfos;
}
}
public float getScale() {
return this.scale;
}
/**
* update the internals of the screen to match the tracking.
*/
public void updatePos(Camera camera, MarkerBoard board) {
transformation.set(board.getTransfoMat(camera));
}
public void computeScreenPosTransform() {
///////////////////// PLANE COMPUTATION //////////////////
PMatrix3D mat = this.getLocation();
paperPosCorners3D[0] = new PVector(mat.m03, mat.m13, mat.m23);
mat.translate(size.x, 0, 0);
paperPosCorners3D[1] = new PVector(mat.m03, mat.m13, mat.m23);
mat.translate(0, size.y, 0);
paperPosCorners3D[2] = new PVector(mat.m03, mat.m13, mat.m23);
mat.translate(-size.x, 0, 0);
paperPosCorners3D[3] = new PVector(mat.m03, mat.m13, mat.m23);
plane = new Plane(new Triangle3D(toVec(paperPosCorners3D[0]), toVec(paperPosCorners3D[1]), toVec(paperPosCorners3D[2])));
homography.addPoint(paperPosCorners3D[0], new PVector(0, 0));
homography.addPoint(paperPosCorners3D[1], new PVector(1, 0));
homography.addPoint(paperPosCorners3D[2], new PVector(1, 1));
homography.addPoint(paperPosCorners3D[3], new PVector(0, 1));
worldToScreen = homography.getHomography();
}
public PVector[] getCornerPos() {
computeScreenPosTransform();
return paperPosCorners3D;
}
////////////////// 3D SPACE TO PAPER HOMOGRAPHY ///////////////
// Version 2.0 : (0,0) is the top-left corner.
private void initHomography() {
homography = new HomographyCreator(3, 2, 4);
}
public void initDraw(PVector userPos) {
initDraw(userPos, 40, 5000);
}
public void initDraw(PVector userPos, float nearPlane, float farPlane) {
initDraw(userPos, nearPlane, farPlane, false, false, true);
}
// TODO: optionnal args.
public void initDraw(PVector userPos, float nearPlane, float farPlane, boolean isAnaglyph, boolean isLeft, boolean isOnly) {
PGraphicsOpenGL graphics = getGraphics();
if (initPosM == null) {
this.isOpenGL = true;
// Transformation Camera -> Marker
initPosM = this.getLocation();
initPosM.translate(this.getDrawSizeX() / 2, this.getDrawSizeY() / 2);
// All is relative to the paper's center. not the corner.
initPosM.scale(-1, 1, -1);
}
// get the current transformation...
PMatrix3D newPos = this.getLocation();
newPos.translate(this.getDrawSizeX() / 2, this.getDrawSizeY() / 2);
newPos.scale(-1, 1, -1);
newPos.invert();
newPos.apply(initPosM);
PVector user = new PVector();
if (isAnaglyph && isLeft) {
userPos.add(-halfEyeDist * 2, 0, 0);
}
newPos.mult(userPos, user);
PVector paperCameraPos = user;
// Camera must look perpendicular to the screen.
graphics.camera(paperCameraPos.x, paperCameraPos.y, paperCameraPos.z,
paperCameraPos.x, paperCameraPos.y, 0,
0, 1, 0);
// http://www.gamedev.net/topic/597564-view-and-projection-matrices-for-vr-window-using-head-tracking/
float nearFactor = nearPlane / paperCameraPos.z;
float left = nearFactor * (-size.x / 2f - paperCameraPos.x);
float right = nearFactor * (size.x / 2f - paperCameraPos.x);
float top = nearFactor * (size.y / 2f - paperCameraPos.y);
float bottom = nearFactor * (-size.y / 2f - paperCameraPos.y);
graphics.frustum(left, right, bottom, top, nearPlane, farPlane);
graphics.projection.m11 = -graphics.projection.m11;
// No detection?
if (transformation.m03 == 0 && transformation.m13 == 0 && transformation.m23 == 0) {
resetPos();
}
}
public void endDrawPerspective() {
PGraphicsOpenGL graphics = getGraphics();
graphics.perspective();
graphics.camera();
graphics.projection.m11 = -graphics.projection.m11;
}
public void resetPos() {
initPosM = null;
}
public float getHalfEyeDist() {
return halfEyeDist;
}
public void setHalfEyeDist(float halfEyeDist) {
this.halfEyeDist = halfEyeDist;
}
public Plane getPlane() {
return plane;
}
public HomographyCalibration getWorldToScreen() {
return worldToScreen;
}
}