/***********************************************************************
* 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 javax.media.opengl.GL;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Plane;
import org.mt4j.util.math.Vector3D;
import processing.core.PApplet;
import processing.core.PGraphics3D;
import processing.opengl.PGraphicsOpenGL;
/**
* The Class Frustum. Represents a camera frustum.
* Can be used to check whether a point or a sphere lies in the frustum (is visible).
* This is based off code from lighthouse3d.
* @author Christopher Ruff
*/
public class Frustum implements IFrustum{
// private static float ANG2RAD = (3.14159265358979323846f/360.0f); //FIXME warum /360 ? m�sste doch /180 sein?
private float ratio;
private float angle;
private float nearD;
private float farD;
private GL gl;
private Vector3D _tmpVec3 = new Vector3D();
private Vector3D _tmpVec2 = new Vector3D();
private Vector3D _tmpVec = new Vector3D();
private float sphereFactorY;
private float sphereFactorX;
private float nh;
private float nw;
private float fh;
private float fw;
//camera position and vectors
private Vector3D camPos;
private Vector3D Z;
private Vector3D X;
private Vector3D Y;
//Frustum planes points
public Vector3D ntl;
public Vector3D ntr;
public Vector3D nbl;
public Vector3D nbr;
public Vector3D ftl;
public Vector3D fbr;
public Vector3D ftr;
public Vector3D fbl;
private float tang;
public Plane[] planes;
private static final int TOP = 0;
private static final int BOTTOM = 1;
private static final int LEFT = 2;
private static final int RIGHT = 3;
private static final int NEARP = 4;
private static final int FARP = 5;
/**
* Instantiates a new frustum using the current camera values
* from the processing context.
*
*
* @param pa the pa
*/
public Frustum(PApplet pa){
if (MT4jSettings.getInstance().isOpenGlMode()){
this.gl = ((PGraphicsOpenGL)pa.g).gl;
}
camPos = new Vector3D();
Z = new Vector3D();
X = new Vector3D();
Y = new Vector3D();
planes = new Plane[]{
new Plane(Vector3D.ZERO_VECTOR,Vector3D.ZERO_VECTOR)
,new Plane(Vector3D.ZERO_VECTOR,Vector3D.ZERO_VECTOR)
,new Plane(Vector3D.ZERO_VECTOR,Vector3D.ZERO_VECTOR)
,new Plane(Vector3D.ZERO_VECTOR,Vector3D.ZERO_VECTOR)
,new Plane(Vector3D.ZERO_VECTOR,Vector3D.ZERO_VECTOR)
,new Plane(Vector3D.ZERO_VECTOR,Vector3D.ZERO_VECTOR)
};
PGraphics3D p3d = ((PGraphics3D)pa.g);
//This has to be called if the perspective is changed!!
this.setCamInternals(p3d.cameraFOV*0.5f, p3d.cameraAspect, p3d.cameraNear, p3d.cameraFar);
}
// #define m(col,row) m[row*4+col]
// void setFrustum(float m) {
// pl[NEARP].setCoefficients(m(2,0) + m(3,0),
// m(2,1) + m(3,1),
// m(2,2) + m(3,2),
// m(2,3) + m(3,3));
// pl[FARP].setCoefficients( -m(2,0) + m(3,0),
// -m(2,1) + m(3,1),
// -m(2,2) + m(3,2),
// -m(2,3) + m(3,3));
// pl[BOTTOM].setCoefficients(m(1,0) + m(3,0),
// m(1,1) + m(3,1),
// m(1,2) + m(3,2),
// m(1,3) + m(3,3));
// pl[TOP].setCoefficients( -m(1,0) + m(3,0),
// -m(1,1) + m(3,1),
// -m(1,2) + m(3,2),
// -m(1,3) + m(3,3));
// pl[LEFT].setCoefficients( m(0,0) + m(3,0),
// m(0,1) + m(3,1),
// m(0,2) + m(3,2),
// m(0,3) + m(3,3));
// pl[RIGHT].setCoefficients(-m(0,0) + m(3,0),
// -m(0,1) + m(3,1),
// -m(0,2) + m(3,2),
// -m(0,3) + m(3,3));
// }
//TODO immer wenn sich perspective �ndert
/**
* Sets the cam internals. Has to be called if perspective
* is changed.
*
* @param angle the angle
* @param ratio the ratio
* @param nearD the near d
* @param farD the far d
*/
public void setCamInternals(float angle, float ratio, float nearD, float farD) { //ERWARTET ANGLE WERTE IN RADIANS! (bei p5 cameraFov*0.5f)
// store the information
this.ratio = ratio;
this.angle = angle;
// this.angle = angle * ANG2RAD;
this.nearD = nearD;
this.farD = farD;
// compute width and height of the near and far plane sections
tang = ToolsMath.tan(this.angle); //(float) Math.tan(this.angle);
sphereFactorY = (1.0f/ToolsMath.cos(this.angle)); // (float) (1.0/Math.cos(this.angle));
float anglex = ToolsMath.atan(tang*ratio); //(float) Math.atan(tang*ratio);
sphereFactorX = (1.0f/ToolsMath.cos(anglex));//(float) (1.0f/Math.cos(anglex));
nh = nearD * tang;
nw = nh * ratio;
fh = farD * tang;
fw = fh * ratio;
}
/**
* Re/Sets the frustum to the specified camera values.
*
* @param camPos the camera position
* @param viewCenterPos the view center pos (look at)
* @param xUp the x up vector
* @param yUp the y up vector
* @param zUp the z up vector
*/
public void setCamDef(Vector3D camPos, Vector3D viewCenterPos, float xUp, float yUp, float zUp){// Vector3D u) {
this.camPos = camPos.getCopy();
_tmpVec2.setValues(this.camPos);
Z.setValues(_tmpVec2.subtractLocal(viewCenterPos));
Z.normalizeLocal();
// X axis of camera of given "up" vector and Z axis
// X.setValues(u.getCross(Z));
_tmpVec2.setXYZ(xUp, yUp, zUp);
X.setValues(_tmpVec2.crossLocal(Z));
X.normalizeLocal();
// the real "up" vector is the cross product of Z and X
// Y.setValues(Z.getCross(X));
_tmpVec2.setValues(Z);
Y.setValues(_tmpVec2.crossLocal(X));
// /*
// compute the center of the near and far planes - THE PLANES ARENT NEEDED FOR THE CALCULATIONS TO WORK!
Vector3D nc,fc;
nc = this.camPos.getSubtracted(Z.getScaled(nearD));
fc = this.camPos.getSubtracted(Z.getScaled(farD));
// compute the 8 corners of the frustum
Vector3D yScaledNh = Y.getScaled(nh);
Vector3D xScaledNw = X.getScaled(nw);
Vector3D yScaledfh = Y.getScaled(fh);
Vector3D xScaledfw = X.getScaled(fw);
ntl = nc.getAdded(yScaledNh).subtractLocal(xScaledNw);
ntr = nc.getAdded(yScaledNh).addLocal(xScaledNw);
nbl = nc.getSubtracted(yScaledNh).subtractLocal(xScaledNw);
nbr = nc.getSubtracted(yScaledNh).addLocal(xScaledNw);
ftl = fc.getAdded(yScaledfh).subtractLocal(xScaledfw);
fbr = fc.getSubtracted(yScaledfh).addLocal(xScaledfw);
ftr = fc.getAdded(yScaledfh).addLocal(xScaledfw);
fbl = fc.getSubtracted(yScaledfh).subtractLocal(xScaledfw);
// */
/*
System.out.println("NEAR TOP LEFT: " + ntl + " RIGHT: " + ntr);
// System.out.println("NEAR TOP RIGHT: " + ntr);
System.out.println("NEAR BOTTOM LEFT: " + nbl + " RIGHT: " + nbr);
// System.out.println("NEAR BOTTOM RIGHT: " + nbr);
System.out.println();
System.out.println("FAR TOP LEFT: " + ftl + " RIGHT: " + ftr);
// System.out.println("FAR BOTTOM RIGHT: " + fbr);
// System.out.println("FAR TOP RIGHT: " + ftr);
System.out.println("FAR BOTTOM LEFT: " + fbl + " BOTTOM RIGHT: " + fbr);
System.out.println();
*/
// compute the six planes
// the function set3Points asssumes that the points
// are given in counter clockwise order
// /*
//This computes the 6 frustum planes
planes[NEARP].reconstruct(nc ,Z.getScaled(-1));
planes[FARP].reconstruct(fc, Z);
Vector3D aux,normal;
aux = (nc.getAdded(yScaledNh)).subtractLocal(camPos);
normal = aux.crossLocal(X);
planes[TOP].reconstruct(nc.getAdded(yScaledNh), normal);
aux = (nc.getSubtracted(yScaledNh)).subtractLocal(camPos);
normal = X.getCross(aux);
planes[BOTTOM].reconstruct(nc.getSubtracted(yScaledNh), normal);
aux = (nc.getSubtracted(xScaledNw)).subtractLocal(camPos);
normal = aux.crossLocal(Y);
planes[LEFT].reconstruct(nc.getSubtracted(xScaledNw), normal);
aux = (nc.getAdded(xScaledNw)).subtractLocal(camPos);
normal = Y.getCross(aux);
planes[RIGHT].reconstruct(nc.getAdded(xScaledNw), normal);
// */
}
/* (non-Javadoc)
* @see util.camera.IFrustum#isPointInFrustum(util.math.Vector3D)
*/
public int isPointInFrustum(Vector3D p) {
float pcz,pcx,pcy,aux;
// // compute vector from camera position to p
//// Vector3D v = p-camPos;
// Vector3D v = p.getSubtracted(camPos);
//
// // compute and test the Z coordinate
//// pcz = v.innerProduct(-Z);
//// pcz = v.dot(Z.getScaled(-1)); //TODO cache?
// _tmpVec.setValues(Z);
// pcz = v.dot(_tmpVec.scaleLocal(-1));
//
// if (pcz > farD || pcz < nearD)
// return(OUTSIDE);
//
// // compute and test the Y coordinate
//// pcy = v.innerProduct(Y);
// pcy = v.dot(Y);
// aux = pcz * tang;
// if (pcy > aux || pcy < -aux)
// return(OUTSIDE);
//
// // compute and test the X coordinate
//// pcx = v.innerProduct(X);
// pcx = v.dot(X);
// aux = aux * ratio;
// if (pcx > aux || pcx < -aux)
// return(OUTSIDE);
//
// return(INSIDE);
// compute vector from camera position to p
_tmpVec.setValues(p);
_tmpVec2.setValues(_tmpVec.subtractLocal(camPos));
// compute and test the Z coordinate
_tmpVec.setValues(Z);
pcz = _tmpVec2.dot(_tmpVec.scaleLocal(-1));
if (pcz > farD || pcz < nearD)
return(OUTSIDE);
// compute and test the Y coordinate
pcy = _tmpVec2.dot(Y);
aux = pcz * tang;
if (pcy > aux || pcy < -aux)
return(OUTSIDE);
// compute and test the X coordinate
pcx = _tmpVec2.dot(X);
aux = aux * ratio;
if (pcx > aux || pcx < -aux)
return(OUTSIDE);
return(INSIDE);
}
/* (non-Javadoc)
* @see util.camera.IFrustum#isSphereInFrustum(util.math.Vector3D, float)
*/
public int isSphereInFrustum(Vector3D p, float radius) {
// float d1,d2;
// float az,ax,ay,zz1,zz2;
// int result = INSIDE;
//
//// Vector3D v = p-camPos;
// Vector3D v = p.getSubtracted(camPos);
//
// //TODO erst x,y checkan statt z da wir kaum in der tiefer operieren
//// az = v.innerProduct(-Z);
//// az = v.dot(Z.getScaled(-1));
// _tmpVec.setValues(Z);
// az = v.dot(_tmpVec.scaleLocal(-1));
//
// if (az > farD + radius || az < nearD-radius)
// return(OUTSIDE);
//
//// ax = v.innerProduct(X);
// ax = v.dot(X);
// zz1 = az * tang * ratio;
// d1 = sphereFactorX * radius;
// if (ax > zz1+d1 || ax < -zz1-d1)
// return(OUTSIDE);
//
//// ay = v.innerProduct(Y);
// ay = v.dot(Y);
// zz2 = az * tang;
// d2 = sphereFactorY * radius;
// if (ay > zz2+d2 || ay < -zz2-d2)
// return(OUTSIDE);
//
// if (az > farD - radius || az < nearD+radius)
// result = INTERSECT;
// if (ay > zz2-d2 || ay < -zz2+d2)
// result = INTERSECT;
// if (ax > zz1-d1 || ax < -zz1+d1)
// result = INTERSECT;
//
// return(result);
float d1,d2;
float az,ax,ay,zz1,zz2;
int result = INSIDE;
// Vector3D v = p-camPos;
// Vector3D v = p.getSubtracted(camPos);
_tmpVec3.setValues(p);
_tmpVec3.subtractLocal(camPos);
//TODO erst x,y checkan statt z da wir kaum in der tiefer operieren
// az = v.innerProduct(-Z);
// az = v.dot(Z.getScaled(-1));
_tmpVec.setValues(Z);
az = _tmpVec3.dot(_tmpVec.scaleLocal(-1));
if (az > farD + radius || az < nearD-radius)
return(OUTSIDE);
// ax = v.innerProduct(X);
ax = _tmpVec3.dot(X);
zz1 = az * tang * ratio;
d1 = sphereFactorX * radius;
if (ax > zz1+d1 || ax < -zz1-d1)
return(OUTSIDE);
// ay = v.innerProduct(Y);
ay = _tmpVec3.dot(Y);
zz2 = az * tang;
d2 = sphereFactorY * radius;
if (ay > zz2+d2 || ay < -zz2-d2)
return(OUTSIDE);
if (az > farD - radius || az < nearD+radius)
result = INTERSECT;
if (ay > zz2-d2 || ay < -zz2+d2)
result = INTERSECT;
if (ax > zz1-d1 || ax < -zz1+d1)
result = INTERSECT;
return(result);
}
public void drawPoints() {
gl.glBegin(GL.GL_POINTS);
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
gl.glEnd();
}
public void drawLines() {
gl.glBegin(GL.GL_LINE_LOOP);
//near plane
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
gl.glEnd();
gl.glBegin(GL.GL_LINE_LOOP);
//far plane
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
gl.glEnd();
gl.glBegin(GL.GL_LINE_LOOP);
//bottom plane
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
gl.glEnd();
gl.glBegin(GL.GL_LINE_LOOP);
//top plane
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
gl.glEnd();
gl.glBegin(GL.GL_LINE_LOOP);
//left plane
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
gl.glEnd();
gl.glBegin(GL.GL_LINE_LOOP);
// right plane
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
gl.glEnd();
}
public void drawPlanes() {
gl.glBegin(GL.GL_QUADS);
//near plane
gl.glColor4d(1.0f, 0.5f, 0.5f, 0.5f);
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
//far plane
gl.glColor4d(0.5f, 1.0f, 0.5f, 0.5f);
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
//bottom plane
gl.glColor4d(0.5f, 0.5f, 1.0f, 0.5f);
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
//top plane
gl.glColor4d(0.7f, 0.6f, 0.5f, 0.5f);
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
//left plane
gl.glColor4d(0.5f, 0.6f, 0.7f, 0.5f);
gl.glVertex3f(ntl.x,ntl.y,ntl.z);
gl.glVertex3f(nbl.x,nbl.y,nbl.z);
gl.glVertex3f(fbl.x,fbl.y,fbl.z);
gl.glVertex3f(ftl.x,ftl.y,ftl.z);
// right plane
gl.glColor4d(0.6f, 0.7f, 0.5f, 0.5f);
gl.glVertex3f(nbr.x,nbr.y,nbr.z);
gl.glVertex3f(ntr.x,ntr.y,ntr.z);
gl.glVertex3f(ftr.x,ftr.y,ftr.z);
gl.glVertex3f(fbr.x,fbr.y,fbr.z);
gl.glEnd();
}
public void drawNormals() {
Vector3D a,b;
gl.glBegin(GL.GL_LINES);
// near
a = (ntr .getAdded( ntl).getAdded(nbr).getAdded(nbl)).scaleLocal(0.25f);
b = a.getAdded(planes[NEARP].normal);
gl.glVertex3f(a.x,a.y,a.z);
gl.glVertex3f(b.x,b.y,b.z);
// far
a = (ftr .getAdded( ftl).getAdded(fbr).getAdded(fbl)).scaleLocal(0.25f);
b = a.getAdded(planes[FARP].normal);
gl.glVertex3f(a.x,a.y,a.z);
gl.glVertex3f(b.x,b.y,b.z);
// left
a = (ftl .getAdded( fbl).getAdded(nbl).getAdded(ntl)).scaleLocal(0.25f);
b = a.getAdded(planes[LEFT].normal);
gl.glVertex3f(a.x,a.y,a.z);
gl.glVertex3f(b.x,b.y,b.z);
// right
a = (ftr .getAdded( nbr).getAdded(fbr).getAdded(ntr)).scaleLocal(0.25f);
b = a.getAdded(planes[RIGHT].normal);
gl.glVertex3f(a.x,a.y,a.z);
gl.glVertex3f(b.x,b.y,b.z);
// top
a = (ftr .getAdded( ftl).getAdded(ntr).getAdded(ntl)).scaleLocal(0.25f);
b = a.getAdded(planes[TOP].normal);
gl.glVertex3f(a.x,a.y,a.z);
gl.glVertex3f(b.x,b.y,b.z);
// bottom
a = (fbr .getAdded( fbl).getAdded(nbr).getAdded(nbl)).scaleLocal(0.25f);
b = a.getAdded(planes[BOTTOM].normal);
gl.glVertex3f(a.x,a.y,a.z);
gl.glVertex3f(b.x,b.y,b.z);
gl.glEnd();
}
// public void printPlanes() {
//
// for (int i = 0; i < 6; i++) {
//
// pl[i].print();
// printf("\n");
// }
// }
}