/*********************************************************************** * mt4j Copyright (c) 2008 - 2009 C.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 org.mt4j.util.camera; import org.mt4j.util.math.Matrix; import org.mt4j.util.math.Vector3D; import processing.core.PApplet; import processing.core.PGraphics3D; import processing.core.PMatrix3D; /** * The Class MTCamera. * @author Christopher Ruff */ public class MTCamera implements Icamera, IFrustum{ /** The pa. */ private PApplet pa; /** The view center pos. */ private Vector3D viewCenterPos; /** The cam pos. */ private Vector3D camPos; /** The x axis up. */ private float xAxisUp; /** The y axis up. */ private float yAxisUp; /** The z axis up. */ private float zAxisUp; /** The zoom min distance. */ private float zoomMinDistance; private Frustum frustum; private boolean dirty; private PMatrix3D cameraMat; private PMatrix3D cameraInvMat; private Matrix cameraMatrix; private Matrix cameraInvMatrix; private PGraphics3D p3d; /** * Instantiates a new mT camera. * * @param processingApplet the processing applet */ public MTCamera(PApplet processingApplet){ this((float)(processingApplet.width/2.0f), (float)(processingApplet.height/2.0f), (float)(processingApplet.height/2.0f) / PApplet.tan((float)(PApplet.PI*60.0f / 360.0f)), (float)(processingApplet.width/2.0f), (float)(processingApplet.height/2.0f), 0, 0, 1,0, processingApplet); } /** * Instantiates a new mT camera. * * @param cameraPosX the camera pos x * @param cameraPosY the camera pos y * @param cameraPosZ the camera pos z * @param camEyePosX the cam eye pos x * @param camEyePosY the cam eye pos y * @param camEyePosZ the cam eye pos z * @param xAxisUp the x axis up * @param yAxisUp the y axis up * @param zAxisUp the z axis up * @param processingApplet the processing applet */ public MTCamera(float cameraPosX, float cameraPosY, float cameraPosZ, float camEyePosX, float camEyePosY, float camEyePosZ, float xAxisUp, float yAxisUp, float zAxisUp, PApplet processingApplet){ this.pa = processingApplet; this.camPos = new Vector3D(cameraPosX, cameraPosY, cameraPosZ); this.viewCenterPos = new Vector3D(camEyePosX, camEyePosY, camEyePosZ); this.xAxisUp = xAxisUp; this.yAxisUp = yAxisUp; this.zAxisUp = zAxisUp; this.zoomMinDistance = 0; this.frustum = new Frustum(pa); this.frustum.setCamDef(this.getPosition(), this.getViewCenterPos(), xAxisUp, -yAxisUp, zAxisUp); //new Vector3D(xAxisUp, -yAxisUp, zAxisUp)); this.p3d = ((PGraphics3D)pa.g); this.dirty = true; this.cameraMat = new PMatrix3D(); this.cameraInvMat = new PMatrix3D(); this.cameraMatrix = new Matrix(); this.cameraInvMatrix = new Matrix(); } /** * Sets or updates the camera with the values specified in the camera. * <br> <b>Call this after changing any camera values to take effect!</b>. * <br><strong>Note:</strong> The current modelview matrices (=all transformations made up to this point) will be reset and replaced by the camera values! */ public void update(){ /* pa.camera(camPos.getX(), camPos.getY() , camPos.getZ(), //eyeposition viewCenterPos.getX(), viewCenterPos.getY(), viewCenterPos.getZ(), //view center xAxisUp, yAxisUp, zAxisUp); //which axis points up? this.frustum.setCamDef(this.getPosition(), this.getViewCenterPos(), xAxisUp, -yAxisUp, zAxisUp); //new Vector3D(xAxisUp, -yAxisUp, zAxisUp)); */ // /* if (this.dirty){ // System.out.println("Calc new camera"); this.calcCameraMatrix(camPos.getX(), camPos.getY() , camPos.getZ(), //eyeposition viewCenterPos.getX(), viewCenterPos.getY(), viewCenterPos.getZ(), //view center xAxisUp, yAxisUp, zAxisUp);//which axis points up? this.setCachedCamMatrices(); this.frustum.setCamDef(this.getPosition(), this.getViewCenterPos(), xAxisUp, -yAxisUp, zAxisUp); //new Vector3D(xAxisUp, -yAxisUp, zAxisUp)); }else{ // System.out.println("Use Cached"); this.setCachedCamMatrices(); } // */ } private void setCachedCamMatrices(){ Matrix m = this.cameraMatrix; Matrix mi = this.cameraInvMatrix; cameraMat.set( m.m00, m.m01, m.m02, m.m03, m.m10, m.m11, m.m12, m.m13, m.m20, m.m21, m.m22, m.m23, m.m30, m.m31, m.m32, m.m33); cameraInvMat.set( mi.m00, mi.m01, mi.m02, mi.m03, mi.m10, mi.m11, mi.m12, mi.m13, mi.m20, mi.m21, mi.m22, mi.m23, mi.m30, mi.m31, mi.m32, mi.m33); //cant also set cameraInv..not visible p3d.camera.set( m.m00, m.m01, m.m02, m.m03, m.m10, m.m11, m.m12, m.m13, m.m20, m.m21, m.m22, m.m23, m.m30, m.m31, m.m32, m.m33); //FIXME cannot set p5 cameraInv because its not visible..problem? p3d.modelview.set(cameraMat); p3d.modelviewInv.set(cameraInvMat); } private void calcCameraMatrix(float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ ) { /* float z0 = eyeX - centerX; float z1 = eyeY - centerY; float z2 = eyeZ - centerZ; float mag = FastMath.sqrt(z0*z0 + z1*z1 + z2*z2); if (mag != 0) { z0 /= mag; z1 /= mag; z2 /= mag; } float y0 = upX; float y1 = upY; float y2 = upZ; float x0 = y1*z2 - y2*z1; float x1 = -y0*z2 + y2*z0; float x2 = y0*z1 - y1*z0; y0 = z1*x2 - z2*x1; y1 = -z0*x2 + z2*x0; y2 = z0*x1 - z1*x0; mag = FastMath.sqrt(x0*x0 + x1*x1 + x2*x2); if (mag != 0) { x0 /= mag; x1 /= mag; x2 /= mag; } mag = FastMath.sqrt(y0*y0 + y1*y1 + y2*y2); if (mag != 0) { y0 /= mag; y1 /= mag; y2 /= mag; } try { //just does an apply to the main matrix, //since that'll be copied out on endCamera // cameraMat.set( // x0, x1, x2, 0, // y0, y1, y2, 0, // z0, z1, z2, 0, // 0, 0, 0, 1); // cameraMat.translate(-eyeX, -eyeY, -eyeZ); // this.cameraMatrix.set(new float[]{ // x0, x1, x2, -eyeX, // y0, y1, y2, -eyeY, // z0, z1, z2, -eyeZ, // 0, 0, 0, 1 // }); this.cameraMatrix.set(new float[]{ x0, x1, x2, 0, y0, y1, y2, 0, z0, z1, z2, 0, 0, 0, 0, 1 }); this.cameraMatrix.mult(Matrix.getTranslationMatrix(-eyeX, -eyeY, -eyeZ), this.cameraMatrix); System.out.println("My cammatrix: " + this.cameraMatrix); // cameraInvMat.reset(); // cameraInvMat.invApply( // x0, x1, x2, 0, // y0, y1, y2, 0, // z0, z1, z2, 0, // 0, 0, 0, 1); // cameraInvMat.translate(eyeX, eyeX, eyeZ); this.cameraInvMatrix = this.cameraMatrix.invert(this.cameraInvMatrix); // this.cameraInvMatrix.set(new float[]{ // x0, x1, x2, eyeX, // y0, y1, y2, eyeX, // z0, z1, z2, eyeZ, // 0, 0, 0, 1 // }); this.dirty = false; } catch (Exception e) { e.printStackTrace(); } */ // /* try { pa.camera(camPos.getX(), camPos.getY() , camPos.getZ(), //eyeposition viewCenterPos.getX(), viewCenterPos.getY(), viewCenterPos.getZ(), //view center xAxisUp, yAxisUp, zAxisUp); //which axis points up? this.cameraMatrix.set(new float[]{ p3d.modelview.m00, p3d.modelview.m01, p3d.modelview.m02, p3d.modelview.m03, p3d.modelview.m10, p3d.modelview.m11, p3d.modelview.m12, p3d.modelview.m13, p3d.modelview.m20, p3d.modelview.m21, p3d.modelview.m22, p3d.modelview.m23, p3d.modelview.m30, p3d.modelview.m31, p3d.modelview.m32, p3d.modelview.m33 }); // System.out.println("p5 camMatrix: " + this.cameraMatrix); this.cameraInvMatrix.set(new float[]{ p3d.modelviewInv.m00, p3d.modelviewInv.m01, p3d.modelviewInv.m02, p3d.modelviewInv.m03, p3d.modelviewInv.m10, p3d.modelviewInv.m11, p3d.modelviewInv.m12, p3d.modelviewInv.m13, p3d.modelviewInv.m20, p3d.modelviewInv.m21, p3d.modelviewInv.m22, p3d.modelviewInv.m23, p3d.modelviewInv.m30, p3d.modelviewInv.m31, p3d.modelviewInv.m32, p3d.modelviewInv.m33 }); this.dirty = false; } catch (Exception e) { e.printStackTrace(); } // */ } /** * Gets the camera matrix. * * @return the camera matrix */ public Matrix getCameraMatrix(){ if (this.dirty){ this.calcCameraMatrix(camPos.getX(), camPos.getY() , camPos.getZ(), //eyeposition viewCenterPos.getX(), viewCenterPos.getY(), viewCenterPos.getZ(), //view center xAxisUp, yAxisUp, zAxisUp);//which axis points up? this.setCachedCamMatrices(); this.frustum.setCamDef(this.getPosition(), this.getViewCenterPos(), xAxisUp, -yAxisUp, zAxisUp); //new Vector3D(xAxisUp, -yAxisUp, zAxisUp)); }else{ this.setCachedCamMatrices(); } return this.cameraMatrix; } /** * Gets the camera inv matrix. * * @return the camera inv matrix */ public Matrix getCameraInvMatrix(){ if (this.dirty){ this.calcCameraMatrix(camPos.getX(), camPos.getY() , camPos.getZ(), //eyeposition viewCenterPos.getX(), viewCenterPos.getY(), viewCenterPos.getZ(), //view center xAxisUp, yAxisUp, zAxisUp);//which axis points up? this.setCachedCamMatrices(); this.frustum.setCamDef(this.getPosition(), this.getViewCenterPos(), xAxisUp, -yAxisUp, zAxisUp); //new Vector3D(xAxisUp, -yAxisUp, zAxisUp)); }else{ this.setCachedCamMatrices(); } return this.cameraInvMatrix; } /** * Zooms from the camera to the eye location by the given factor. * * @param factor the factor */ public void zoomFactor(float factor){ factor = 1/factor; Vector3D dirToCamVect = camPos.getSubtracted(viewCenterPos); dirToCamVect.scaleLocal(factor); if (dirToCamVect.length() > zoomMinDistance){ Vector3D toCam = viewCenterPos.getAdded(dirToCamVect); camPos.setXYZ(toCam.getX(), toCam.getY(), toCam.getZ()); this.dirty = true; } } /** * changes the distance from the eye to the camera location by the given amount * negative values will increase the distance, positive values will decrease it. * * @param amount the amount */ public void zoomAmount(float amount){ amount*=-1; //Get direction vector from eye to camera Vector3D dirToCamVect = camPos.getSubtracted(viewCenterPos); //get the length of that vector float mag = dirToCamVect.length(); //normalize the vector dirToCamVect.normalizeLocal(); //scale the normalized vector with the original amount + the zoom amount dirToCamVect.scaleLocal(mag + amount); if (dirToCamVect.length() > zoomMinDistance){ //Get the Vector to the camera from origin Vector3D toCam = viewCenterPos.getAdded(dirToCamVect); //set the new camPos camPos.setXYZ(toCam.getX(), toCam.getY(), toCam.getZ()); this.dirty = true; } } /** * prevent zooming the cam to the center too close * set the minimal distance between the cam and the center. * * @param minDistance the min distance */ public void setZoomMinDistance(float minDistance){ this.zoomMinDistance = minDistance; } /** * Gets the zoom min distance. * * @return the zoom min distance */ public float getZoomMinDistance() { return zoomMinDistance; } // /** // * sets the position of the camera. // * // * @param x the x // * @param y the y // * @param z the z // */ // public void setCamPosition(float x, float y, float z){ // camPos.setXYZ(x, y, z); // this.dirty = true; // } /** * Move cam. * * @param directionX the direction x * @param directionY the direction y * @param directionZ the direction z */ public void moveCam(float directionX, float directionY, float directionZ){ camPos.setXYZ(camPos.getX() + directionX, camPos.getY() + directionY, camPos.getZ() + directionZ); this.dirty = true; } /** * sets the position of the center view point. * * @param x the x * @param y the y * @param z the z */ public void setViewCenterPosition(float x, float y, float z){ viewCenterPos.setXYZ(x, y, z); this.dirty = true; } /** * moves the view center location by the given values in the given directions. * * @param directionX the direction x * @param directionY the direction y * @param directionZ the direction z */ public void moveViewCenter(float directionX, float directionY, float directionZ){ viewCenterPos.setXYZ(viewCenterPos.getX() + directionX, viewCenterPos.getY() + directionY, viewCenterPos.getZ() + directionZ); this.dirty = true; } /** * moves both the view center and the camera location by the given values in the given directions. * * @param directionX the direction x * @param directionY the direction y * @param directionZ the direction z */ public void moveCamAndViewCenter(float directionX, float directionY, float directionZ){ moveCam(directionX, directionY, directionZ); moveViewCenter(directionX, directionY, directionZ); this.dirty = true; } /** * Gets the cam view center distance. * * @return the cam view center distance */ public float getCamViewCenterDistance(){ return Vector3D.distance(getPosition(), getViewCenterPos()); } /** * Reset to default. */ public void resetToDefault(){ this.camPos = new Vector3D((float)(pa.width/2.0), (float)(pa.height/2.0), (float)(pa.height/2.0) / PApplet.tan((float)(PApplet.PI*60.0 / 360.0))); this.viewCenterPos = new Vector3D((float)(pa.width/2.0), (float)(pa.height/2.0), 0) ; this.xAxisUp = 0; this.yAxisUp = 1; this.zAxisUp = 0; this.dirty = true; } /* (non-Javadoc) * @see util.camera.Icamera#getPosition() */ public Vector3D getPosition() { return new Vector3D(camPos); } /* (non-Javadoc) * @see util.camera.Icamera#setPosition(util.math.Vector3D) */ public void setPosition(Vector3D camPos) { this.camPos = camPos; this.dirty = true; } /* (non-Javadoc) * @see util.camera.Icamera#getViewCenterPos() */ public Vector3D getViewCenterPos() { return viewCenterPos; } /* (non-Javadoc) * @see util.camera.Icamera#setViewCenterPos(util.math.Vector3D) */ public void setViewCenterPos(Vector3D eyePos) { this.viewCenterPos = eyePos; this.dirty = true; } /** * Gets the x axis up. * * @return the x axis up */ public float getXAxisUp() { return xAxisUp; } /** * Sets the x axis up. * * @param axisUp the new x axis up */ public void setXAxisUp(float axisUp) { xAxisUp = axisUp; this.dirty = true; } /** * Gets the y axis up. * * @return the y axis up */ public float getYAxisUp() { return yAxisUp; } /** * Sets the y axis up. * * @param axisUp the new y axis up */ public void setYAxisUp(float axisUp) { yAxisUp = axisUp; this.dirty = true; } /** * Gets the z axis up. * * @return the z axis up */ public float getZAxisUp() { return zAxisUp; } /** * Sets the z axis up. * * @param axisUp the new z axis up */ public void setZAxisUp(float axisUp) { zAxisUp = axisUp; this.dirty = true; } public Frustum getFrustum() { return frustum; } public int isSphereInFrustum(Vector3D p, float radius){ return this.getFrustum().isSphereInFrustum(p, radius); } public int isPointInFrustum(Vector3D p) { return this.getFrustum().isPointInFrustum(p); } // public boolean contains(IBoundingShape bounds){ // if (bounds != null){ // // // }else{ // return true; // } // } //TODO setFrustum(float near, float far, float left, float right, float top, float bottom); //TODO setFrustumPerspective(float fovY, float aspect, float near, float far); }