/*
* Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner,
* Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain,
* Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter,
* Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann,
* Samuel Zweifel
*
* This file is part of Jukefox.
*
* Jukefox 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 any later version. Jukefox 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
* Jukefox. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.ethz.dcg.pancho3.view.tabs.opengl;
import java.util.LinkedList;
import ch.ethz.dcg.jukefox.commons.utils.AndroidUtils;
public class Camera {
@SuppressWarnings("unused")
private static final String TAG = Camera.class.getSimpleName();
protected static final float REAR_CLIPPING_PLANE = 200;
protected static final float FRONT_CLIPPING_PLANE = 3f;
private static final int KINETIC_SCROLL_BUFFER_SIZE = 7;
private float posX;
private float posY;
private float posZ;
private float borderMinX;
private float borderMinY;
private float borderMinZ;
private float borderMaxX;
private float borderMaxY;
private float borderMaxZ;
private float speedX;
private float speedY;
private float speedZ;
private float startSpeedX;
private float startSpeedY;
private float startSpeedZ;
// private long lastSetPositionTime;
private long startKineticMotionTime;
private long lastMovementTime;
private long maxMotionTime = 1000;
private LinkedList<KineticScrollEntry> kineticScrollBuffer;
private KineticScrollEntry kineticScrollStart;
private boolean grasped;
private class KineticScrollEntry {
private final long time;
private final float[] pos;
public KineticScrollEntry(float[] pos, long time) {
this.time = time;
this.pos = pos;
}
private float[] getPos() {
return pos;
}
private long getTime() {
return time;
}
}
public Camera() {
borderMaxY = REAR_CLIPPING_PLANE - 1;
borderMinY = FRONT_CLIPPING_PLANE + 1;
kineticScrollBuffer = new LinkedList<KineticScrollEntry>();
}
public void updatePosition(long currentTime) {
if (grasped) {
return;
}
float timeDiff = currentTime - lastMovementTime;
float startTimeDiff = currentTime - startKineticMotionTime;
lastMovementTime = currentTime;
float oldPosX = posX;
float oldPosY = posY;
float oldPosZ = posZ;
posX += speedX * timeDiff;
posY += speedY * timeDiff;
posZ += speedZ * timeDiff;
if (Float.isNaN(posX)) {
posX = oldPosX;
if (Float.isInfinite(posX) || Float.isNaN(posX)) {
posX = 0;
}
}
if (Float.isNaN(posY)) {
posY = oldPosY;
if (Float.isInfinite(posY) || Float.isNaN(posY)) {
posY = 0;
}
}
if (Float.isNaN(posZ)) {
posZ = oldPosZ;
if (Float.isInfinite(posZ) || Float.isNaN(posZ)) {
posZ = 0;
}
}
// Log.v(TAG, "Camera speed: x: " + speedX + " y: " + speedY + " z: " +
// speedZ);
// Log.v(TAG, "Camera position: x: " + posX + " y: " + posY + " z: " +
// posZ);
adjustPositionToBorders();
float damping = 1f - startTimeDiff / maxMotionTime;
damping = Math.max(0f, damping);
speedX = startSpeedX * 1f * damping;
speedY = startSpeedY * 1f * damping;
speedZ = startSpeedZ * 1f * damping;
// if (Math.abs(speedX) > 1) speedX = 0;
// if (Math.abs(speedY) > 1) speedY = 0;
// if (Math.abs(speedZ) > 1) speedZ = 0;
if (startTimeDiff > maxMotionTime) {
stopMotion();
}
}
private void adjustPositionToBorders() {
if (posX < borderMinX) {
posX = borderMinX;
}
if (posZ < borderMinZ) {
posZ = borderMinZ;
}
if (posX > borderMaxX) {
posX = borderMaxX;
}
if (posZ > borderMaxZ + 2 * FRONT_CLIPPING_PLANE) {
posZ = borderMaxZ + 2 * FRONT_CLIPPING_PLANE;
}
if (posY < borderMinY) {
posY = borderMinY;
}
if (posY > borderMaxY) {
posY = borderMaxY;
}
}
public void stopMotion() {
startSpeedX = 0;
startSpeedY = 0;
startSpeedZ = 0;
speedX = 0;
speedY = 0;
speedZ = 0;
kineticScrollBuffer.clear();
}
// public void setKineticSpeed(float speedX, float speedY, float speedZ) {
//
// startKineticMotionTime = System.currentTimeMillis();
//
// startSpeedX = speedX;
// startSpeedY = speedY;
// startSpeedZ = speedZ;
// this.speedX = speedX;
// this.speedY = speedY;
// this.speedZ = speedZ;
// }
public float getFrontClippingPlane() {
return FRONT_CLIPPING_PLANE;
}
public float getRearClippingPlane() {
return REAR_CLIPPING_PLANE;
}
public float getPosX() {
return posX;
}
public float getPosY() {
return posY;
}
public float getPosZ() {
return posZ;
}
public void setCameraPosition(float posX, float posY, float posZ, boolean kineticMovement) {
if (kineticMovement) {
updateKineticSpeed(posX, posY, posZ);
}
this.posX = posX;
this.posY = posY;
this.posZ = posZ;
adjustPositionToBorders();
}
private void updateKineticSpeed(float posX, float posY, float posZ) {
// long currentTime = System.currentTimeMillis();
// int timeDiff = (int) (currentTime-lastSetPositionTime);
// startSpeedX = (posX -this.posX)/timeDiff;
// startSpeedY = (posY -this.posY)/timeDiff;
// startSpeedZ = (posZ -this.posZ)/timeDiff;
// startKineticMotionTime = currentTime;
// lastSetPositionTime = currentTime;
// // Log.v(TAG, "Set kinetic speed to " + startSpeedX + " " +
// startSpeedZ);
long time = System.currentTimeMillis();
float[] pos = new float[] { posX, posY, posZ };
KineticScrollEntry kse = new KineticScrollEntry(pos, time);
if (kineticScrollBuffer.size() == 0) {
kineticScrollStart = kse;
}
kineticScrollBuffer.add(kse);
while (kineticScrollBuffer.size() > KINETIC_SCROLL_BUFFER_SIZE) {
kineticScrollBuffer.removeFirst();
}
KineticScrollEntry kseLast = kineticScrollBuffer.getLast();
KineticScrollEntry kseFirst = kineticScrollBuffer.getFirst();
float[] v1 = getSpeed(kineticScrollStart, kseLast);
float[] v2 = getSpeed(kseFirst, kseLast);
float absV1 = AndroidUtils.norm2(v1);
float absV2 = AndroidUtils.norm2(v2);
float[] v;
if (kineticScrollBuffer.size() >= 2) {
KineticScrollEntry kseSecondLast = kineticScrollBuffer.get(kineticScrollBuffer.size() - 2);
v = getSpeed(kseSecondLast, kseLast);
} else {
v = new float[] { 0, 0, 0 };
}
// float absV = (float)Math.sqrt(Math.pow(v1[0], 2) + Math.pow(v1[1],
// 2));
// Log.v(TAG, "absV: " + absV);
if (absV1 > 1.1 * absV2) {
startSpeedX = 0f;
startSpeedY = 0f;
startSpeedZ = 0f;
} else {
startSpeedX = v[0];
startSpeedY = v[1];
startSpeedZ = v[2];
}
startKineticMotionTime = time;
}
private float[] getSpeed(KineticScrollEntry kseStart, KineticScrollEntry kseEnd) {
int timeDiff = (int) (kseEnd.getTime() - kseStart.getTime());
float[] v = new float[3];
v[0] = (kseEnd.getPos()[0] - kseStart.getPos()[0]) / timeDiff;
v[1] = (kseEnd.getPos()[1] - kseStart.getPos()[1]) / timeDiff;
v[2] = (kseEnd.getPos()[2] - kseStart.getPos()[2]) / timeDiff;
return v;
}
public void setXBorders(float minX, float maxX) {
this.borderMinX = minX;
this.borderMaxX = maxX;
adjustPositionToBorders();
}
public void setYBorders(float minY, float maxY) {
this.borderMinY = minY;
this.borderMaxY = maxY;
adjustPositionToBorders();
}
public void setZBorders(float minZ, float maxZ) {
this.borderMinZ = minZ;
this.borderMaxZ = maxZ;
adjustPositionToBorders();
}
public boolean isGrasped() {
return grasped;
}
public void setGrasped(boolean grasped) {
this.grasped = grasped;
if (grasped) {
stopMotion();
}
}
}