/*
* 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.procam.camera.Camera;
import fr.inria.papart.calibration.CameraConfiguration;
import fr.inria.papart.calibration.HomographyCalibration;
import fr.inria.papart.calibration.PlanarTouchCalibration;
import fr.inria.papart.procam.display.BaseDisplay;
import fr.inria.papart.procam.display.ARDisplay;
import org.bytedeco.javacpp.freenect;
import fr.inria.papart.calibration.PlaneAndProjectionCalibration;
import fr.inria.papart.calibration.PlaneCalibration;
import fr.inria.papart.calibration.ScreenConfiguration;
import fr.inria.papart.procam.camera.CameraFactory;
import fr.inria.papart.procam.camera.CameraOpenKinect;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.Set;
import org.reflections.Reflections;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PFont;
import processing.core.PMatrix3D;
import processing.core.PVector;
import toxi.geom.Plane;
/**
*
* @author jiii
*/
public class Papart {
public final static String folder = fr.inria.papart.procam.Utils.getPapartFolder();
public final static String calibrationFolder = folder + "/data/calibration/";
public final static String markerFolder = folder + "/data/markers/";
public static String cameraCalibName = "camera.xml";
public static String projectorCalibName = "projector.yaml";
public static String cameraCalib = calibrationFolder + cameraCalibName;
public static String projectorCalib = calibrationFolder + projectorCalibName;
public static String camCalibARtoolkit = calibrationFolder + "camera-projector.cal";
public static String kinectIRCalib = calibrationFolder + "calibration-kinect-IR.yaml";
public static String kinectRGBCalib = calibrationFolder + "calibration-kinect-RGB.yaml";
public static String kinectStereoCalib = calibrationFolder + "calibration-kinect-Stereo.xml";
public static String kinectTrackingCalib = "kinectTracking.xml";
public static String cameraProjExtrinsics = "camProjExtrinsics.xml";
public static String screenConfig = calibrationFolder + "screenConfiguration.xml";
public static String cameraConfig = calibrationFolder + "cameraConfiguration.xml";
public static String tablePosition = calibrationFolder + "tablePosition.xml";
public static String planeCalib = calibrationFolder + "PlaneCalibration.xml";
public static String homographyCalib = calibrationFolder + "HomographyCalibration.xml";
public static String planeAndProjectionCalib = calibrationFolder + "PlaneProjectionCalibration.xml";
public static String touchCalib = calibrationFolder + "Touch2DCalibration.xml";
public static String touchCalib3D = calibrationFolder + "Touch3DCalibration.xml";
public static String defaultFont = folder + "/data/Font/" + "GentiumBookBasic-48.vlw";
public int defaultFontSize = 12;
protected static Papart singleton = null;
protected float zNear = 10;
protected float zFar = 6000;
private final PApplet applet;
private final Class appletClass;
private boolean displayInitialized;
private boolean cameraInitialized;
private boolean touchInitialized;
private BaseDisplay display;
private ARDisplay arDisplay;
private Camera cameraTracking;
private PVector frameSize = new PVector();
private CameraOpenKinect cameraOpenKinect;
private boolean isWithoutCamera = false;
public CameraConfiguration cameraConfiguration;
public ScreenConfiguration screenConfiguration;
// TODO: find what to do with these...
/**
* Create the main PapARt object, look at the examples for how to use it.
*
* @param applet
*/
public Papart(Object applet) {
this.displayInitialized = false;
this.cameraInitialized = false;
this.touchInitialized = false;
this.applet = (PApplet) applet;
cameraConfiguration = getDefaultCameraConfiguration(this.applet);
screenConfiguration = getDefaultScreenConfiguration(this.applet);
this.appletClass = applet.getClass();
PFont font = this.applet.loadFont(defaultFont);
// TODO: singleton -> Better implementation.
if (Papart.singleton == null) {
Papart.singleton = this;
}
}
private static CameraConfiguration getDefaultCameraConfiguration(PApplet applet) {
CameraConfiguration config = new CameraConfiguration();
config.loadFrom(applet, cameraConfig);
return config;
}
private static ScreenConfiguration getDefaultScreenConfiguration(PApplet applet) {
ScreenConfiguration config = new ScreenConfiguration();
config.loadFrom(applet, screenConfig);
return config;
}
/**
* Start a see through AR application, the size is automatic.
*
* @param applet
* @return
*/
public static Papart seeThrough(PApplet applet) {
CameraConfiguration cameraConfiguration = getDefaultCameraConfiguration(applet);
Camera cameraTracking = CameraFactory.createCamera(
cameraConfiguration.getCameraType(),
cameraConfiguration.getCameraName());
cameraTracking.setParent(applet);
cameraTracking.setCalibration(cameraCalib);
Papart papart = new Papart(applet);
papart.frameSize.set(cameraTracking.width(), cameraTracking.height());
papart.shouldSetWindowSize = true;
papart.registerPost();
papart.initCamera();
return papart;
}
private boolean shouldSetWindowLocation = false;
private boolean shouldSetWindowSize = false;
private void registerPost() {
applet.registerMethod("post", this);
}
/**
* Places the window at the correct location if required, according to the
* configuration.
*
*/
public static void checkWindowLocation() {
Papart papart = getPapart();
if (papart != null && papart.shouldSetWindowLocation) {
papart.defaultFrameLocation();
papart.shouldSetWindowLocation = false;
}
if (papart != null && papart.shouldSetWindowSize) {
papart.setFrameSize();
papart.shouldSetWindowSize = true;
}
}
/**
* Does not draw anything, it used only to check the window location.
*/
public void post() {
checkWindowLocation();
applet.unregisterMethod("post", this);
}
/**
* Set the frame to default location.
*/
public void defaultFrameLocation() {
System.out.println("Setting the frame location...");
this.applet.frame.setLocation(screenConfiguration.getProjectionScreenOffsetX(),
screenConfiguration.getProjectionScreenOffsetY());
}
/**
* Set the frame to default location.
*/
public void setFrameSize() {
System.out.println("Trying to set the size of the frame...");
// this.applet.frame.setSize((int) frameSize.x, (int) frameSize.y);
this.applet.getSurface().setSize((int) frameSize.x, (int) frameSize.y);
}
protected static void removeFrameBorder(PApplet applet) {
if (!applet.g.isGL()) {
applet.frame.removeNotify();
applet.frame.setUndecorated(true);
applet.frame.addNotify();
}
}
/**
* Get the Papart singleton.
*
* @return
*/
public static Papart getPapart() {
return Papart.singleton;
}
public void saveCalibration(String fileName, PMatrix3D mat) {
HomographyCalibration.saveMatTo(applet, mat, Papart.calibrationFolder + fileName);
}
/**
* Get a calibration from sketchbook/libraries/PapARt/data/calibration
* folder.
*
* @param fileName
* @return null if the file does not exists.
*/
public PMatrix3D loadCalibration(String fileName) {
File f = new File(Papart.calibrationFolder + fileName);
if (f.exists()) {
return HomographyCalibration.getMatFrom(applet, Papart.calibrationFolder + fileName);
} else {
return null;
}
}
/**
* Save the position of a paperScreen as the default table location.
*
* @param paperScreen
*/
public void setTableLocation(PaperScreen paperScreen) {
HomographyCalibration.saveMatTo(applet, paperScreen.getLocation(), tablePosition);
}
/**
* Save the position of a matrix the default table location.
*
* @param mat
*/
public void setTableLocation(PMatrix3D mat) {
HomographyCalibration.saveMatTo(applet, mat, tablePosition);
}
/**
* The location of the table, warning it must be set once by
* setTablePosition.
*
* @return
*/
public PMatrix3D getTableLocation() {
return HomographyCalibration.getMatFrom(applet, tablePosition);
}
/**
* Work in progress function
*
* @return
*/
public PlaneCalibration getTablePlane() {
return PlaneCalibration.CreatePlaneCalibrationFrom(HomographyCalibration.getMatFrom(applet, tablePosition),
new PVector(100, 100));
}
/**
* Move a PaperScreen to the table location. After this, the paperScreen
* location is not updated anymore. To activate the tracking again use :
* paperScreen.useManualLocation(false); You can move the paperScreen
* according to its current location with the paperScreen.setLocation()
* methods.
*
* @param paperScreen
*/
public void moveToTablePosition(PaperScreen paperScreen) {
paperScreen.useManualLocation(true);
paperScreen.screen.setMainLocation(HomographyCalibration.getMatFrom(applet, tablePosition));
}
@Deprecated
public void initNoCamera(int quality) {
this.isWithoutCamera = true;
initNoCameraDisplay(quality);
}
public void initDebug() {
this.isWithoutCamera = true;
initDebugDisplay();
}
private void tryLoadExtrinsics() {
PMatrix3D extrinsics = loadCalibration(cameraProjExtrinsics);
if (extrinsics == null) {
System.out.println("loading default extrinsics. Could not find " + cameraProjExtrinsics + " .");
} else {
arDisplay.setExtrinsics(extrinsics);
}
}
public void initKinectCamera(float quality) {
assert (!cameraInitialized);
cameraTracking = CameraFactory.createCamera(Camera.Type.OPEN_KINECT, 0);
cameraTracking.setParent(applet);
cameraTracking.setCalibration(kinectRGBCalib);
((CameraOpenKinect) cameraTracking).getDepthCamera().setCalibration(kinectIRCalib);
cameraTracking.start();
cameraOpenKinect = (CameraOpenKinect) cameraTracking;
loadTracking(kinectRGBCalib);
cameraTracking.setThread();
initARDisplay(quality);
checkInitialization();
}
/**
* Initialize the default camera for object tracking.
*
* @see initCamera(String, int, float)
*/
public void initCamera() {
initCamera(cameraConfiguration.getCameraName(),
cameraConfiguration.getCameraType(), 1);
}
/**
* Initialize a camera for object tracking.
*
* @see initCamera(String, int, float)
*/
public void initCamera(String cameraNo, Camera.Type cameraType) {
initCamera(cameraNo, cameraType, 1);
}
public void initCamera(String cameraNo, Camera.Type cameraType, float quality) {
assert (!cameraInitialized);
cameraTracking = CameraFactory.createCamera(cameraType, cameraNo);
cameraTracking.setParent(applet);
cameraTracking.setCalibration(cameraCalib);
cameraTracking.start();
loadTracking(cameraCalib);
cameraTracking.setThread();
initARDisplay(quality);
checkInitialization();
}
private void initARDisplay(float quality) {
assert (this.cameraTracking != null && this.applet != null);
arDisplay = new ARDisplay(this.applet, cameraTracking);
arDisplay.setZNearFar(zNear, zFar);
arDisplay.setQuality(quality);
arDisplay.init();
this.display = arDisplay;
frameSize.set(arDisplay.getWidth(), arDisplay.getHeight());
displayInitialized = true;
}
@Deprecated
private void initNoCameraDisplay(float quality) {
initDebugDisplay();
}
private void initDebugDisplay() {
display = new BaseDisplay();
display.setFrameSize(applet.width, applet.height);
display.setDrawingSize(applet.width, applet.height);
display.init();
displayInitialized = true;
}
private void checkInitialization() {
assert (cameraTracking != null);
this.applet.registerMethod("dispose", this);
this.applet.registerMethod("stop", this);
}
private void loadTracking(String calibrationPath) {
// TODO: check if file exists !
Camera.convertARParams(this.applet, calibrationPath, camCalibARtoolkit);
cameraTracking.initMarkerDetection(camCalibARtoolkit);
// The camera view is handled in another thread;
cameraInitialized = true;
}
private final int depthFormat = freenect.FREENECT_DEPTH_MM;
private void loadDefaultCameraKinect() {
cameraOpenKinect = (CameraOpenKinect) CameraFactory.createCamera(Camera.Type.OPEN_KINECT, 0);
cameraOpenKinect.setParent(applet);
cameraOpenKinect.setCalibration(kinectRGBCalib);
cameraOpenKinect.getDepthCamera().setCalibration(kinectIRCalib);
cameraOpenKinect.getDepthCamera().setDepthFormat(depthFormat);
cameraOpenKinect.start();
cameraOpenKinect.setThread();
}
public PlanarTouchCalibration getDefaultTouchCalibration() {
PlanarTouchCalibration calib = new PlanarTouchCalibration();
calib.loadFrom(applet, Papart.touchCalib);
return calib;
}
public PlanarTouchCalibration getDefaultTouchCalibration3D() {
PlanarTouchCalibration calib = new PlanarTouchCalibration();
calib.loadFrom(applet, Papart.touchCalib3D);
return calib;
}
public void loadSketches() {
// Sketches are not within a package.
Reflections reflections = new Reflections("");
Set<Class<? extends PaperScreen>> paperScreenClasses = reflections.getSubTypesOf(PaperScreen.class);
for (Class<? extends PaperScreen> klass : paperScreenClasses) {
try {
Class[] ctorArgs2 = new Class[1];
ctorArgs2[0] = this.appletClass;
Constructor<? extends PaperScreen> constructor = klass.getDeclaredConstructor(ctorArgs2);
System.out.println("Starting a PaperScreen. " + klass.getName());
constructor.newInstance(this.appletClass.cast(this.applet));
} catch (Exception ex) {
System.out.println("Error loading PapartApp : " + klass.getName());
}
}
}
public void startTracking() {
if (this.cameraTracking == null) {
System.err.println("Start Tracking requires a Camera...");
return;
}
this.cameraTracking.trackSheets(true);
}
public void stop() {
this.dispose();
}
public void dispose() {
if (touchInitialized && cameraOpenKinect != null) {
cameraOpenKinect.close();
}
if (cameraInitialized && cameraTracking != null) {
try {
cameraTracking.close();
} catch (Exception e) {
System.err.println("Error closing the tracking camera" + e);
}
}
// System.out.println("Cameras closed.");
}
public BaseDisplay getDisplay() {
// assert (displayInitialized);
return this.display;
}
public void setDisplay(BaseDisplay display) {
// todo check this .
displayInitialized = true;
this.display = display;
}
public void setTrackingCamera(Camera camera) {
this.cameraTracking = camera;
if (camera == null) {
setNoTrackingCamera();
}
}
public void setNoTrackingCamera() {
this.isWithoutCamera = true;
}
public ARDisplay getARDisplay() {
// assert (displayInitialized);
return this.arDisplay;
}
public Camera getCameraTracking() {
// assert (cameraInitialized);
return this.cameraTracking;
}
public PVector getFrameSize() {
assert (this.frameSize != null);
return this.frameSize.get();
}
public boolean isWithoutCamera() {
return this.isWithoutCamera;
}
public CameraOpenKinect getKinectCamera() {
return this.cameraOpenKinect;
}
public PApplet getApplet() {
return applet;
}
}