/***********************************************************************
* 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.math;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.StringTokenizer;
import org.mt4j.AbstractMTApplication;
import org.mt4j.components.interfaces.IMTComponent3D;
import org.mt4j.components.visibleComponents.StyleInfo;
import org.mt4j.components.visibleComponents.shapes.AbstractShape;
import org.mt4j.components.visibleComponents.shapes.GeometryInfo;
import org.mt4j.input.inputData.InputCursor;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.PlatformUtil;
import org.mt4j.util.camera.IFrustum;
import org.mt4j.util.camera.Icamera;
import org.mt4j.util.opengl.GL10;
import org.mt4j.util.opengl.GL11Plus;
import org.mt4j.util.opengl.GLTexture;
import org.mt4j.util.opengl.GLTexture.TEXTURE_TARGET;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;
import processing.core.PMatrix3D;
/**
* Class containing mostly static convenience utility methods.
*
* @author Christopher Ruff
*/
public class Tools3D {
//Declared here and static so it wont have to be initialize at every call to unproject
/** The fb. */
private static FloatBuffer fb = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asFloatBuffer();
/** The fb un. */
private static FloatBuffer fbUn = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asFloatBuffer();
// /** The model. */
// private static DoubleBuffer model;
//
// /** The proj. */
// private static DoubleBuffer proj;
/** The model. */
private static FloatBuffer model;
/** The proj. */
private static FloatBuffer proj;
/** The view. */
private static IntBuffer view;
/** The win pos. */
private static DoubleBuffer winPos;
static{
// model = DoubleBuffer.allocate(16);
// proj = DoubleBuffer.allocate(16);
model = FloatBuffer.allocate(16);
proj = FloatBuffer.allocate(16);
view = IntBuffer.allocate(4);
winPos = DoubleBuffer.allocate(3);
}
/**
* Unprojects screen coordinates from 2D into 3D world space and returns a point that
* can be used to construct a ray form the camera to that point and check
* for intersections with objects.
* <p><b>NOTE</b>: if using openGL mode, the openGL context has to be valid at the time of calling this method.
*
* @param applet the applet
* @param camera the camera
* @param screenX the screen x
* @param screenY the screen y
*
* @return the vector3d
*/
public static Vector3D unprojectScreenCoords(PApplet applet, Icamera camera, float screenX, float screenY ){
Vector3D ret;
applet.pushMatrix();
camera.update();
ret = Tools3D.unprojectScreenCoords(applet, screenX, screenY);
applet.popMatrix();
return ret;
}
private static float[] result = new float[4];
private static float[] factor = new float[4];
private static PMatrix3D modelViewTmp = new PMatrix3D();
private static PMatrix3D projectionTmp = new PMatrix3D();
/**
* Unprojects screen coordinates from 2D into 3D world space and returns a point that
* can be used to construct a ray form the camera to that point and check
* for intersections with objects.
* <p><b>NOTE</b>: if using openGL mode, the openGL context has to be valid at the time of calling this method.
*
* @param applet processings PApplet object
* @param screenX x coordinate on the screen
* @param screenY y coordinate on the screen
*
* @return a point that lies on the line from the screen coordinates
* to the 3d world coordinates
*/
public static Vector3D unprojectScreenCoords(PApplet applet, float screenX, float screenY ){ //FIXME MAKE PRIVATE AGAIN!
Vector3D returnVect = new Vector3D(-999,-999,-999); //null?
// MT4jSettings.getInstance().setRendererMode(MT4jSettings.P3D_MODE);
// switch (MT4jSettings.getInstance().getRendererMode()) {
// case MT4jSettings.OPENGL_MODE:
// int viewport[] = new int[4];
// double[] proj = new double[16];
// double[] model = new double[16];
// double[] mousePosArr = new double[4];
//
// try{
// PGraphicsOpenGL pgl = ((PGraphicsOpenGL)applet.g);
// GL gl = pgl.beginGL();
// GLU glu = pgl.glu;
//
// gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
// gl.glGetDoublev(GL.GL_PROJECTION_MATRIX, proj, 0);
// gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, model, 0);
//
// /*
// System.out.println("OpenGL ProjectionMatrix: ");
// for (int i = 0; i < proj.length; i++) {
// double p = proj[i];
// System.out.print(p + ", ");
// //if (i%4 == 0 && i==3)
// if (i==3 || i== 7 || i== 11 || i==15) {
// System.out.println();
// }
// }
// */
//
// /*
// System.out.println("OpenGL ModelviewMatrix: ");
// for (int i = 0; i < model.length; i++) {
// double p = model[i];
// System.out.print(p + ", ");
// //if (i%4 == 0 && i==3)
// if (i==3 || i== 7 || i== 11 || i==15) {
// System.out.println();
// }
// }
// System.out.println();
// System.out.println("\n");
// */
//
// /*
// fbUn.clear();
// gl.glReadPixels((int)screenX, applet.height - (int)screenY, 1, 1, GL.GL_DEPTH_COMPONENT, GL.GL_FLOAT, fbUn);
// fbUn.rewind();
// glu.gluUnProject((double)screenX, applet.height - (double)screenY, (double)fbUn.get(0), model, 0, proj, 0, viewport, 0, mousePosArr, 0);
// */
//
// //FIXME test not using glReadpixel to get the depth at the location
// //instead we have to build a ray with the result, from the camera location going through the resulst and check for hits ourselves
// glu.gluUnProject((double)screenX, applet.height - (double)screenY, 0, model, 0, proj, 0, viewport, 0, mousePosArr, 0);
// pgl.endGL();
//
// returnVect = new Vector3D((float)mousePosArr[0], (float)mousePosArr[1], (float)mousePosArr[2]);
// }catch(Exception e){
// e.printStackTrace();
// //System.out.println("Use method getWorldForScreenCoords only when drawing with openGL! And dont put negative screen values in!");
// }
// break;
// case MT4jSettings.P3D_MODE:
// /*!
try{
float winZ = 1; //or read from depth buffer at that pixel! (but not available in Ogl ES)
// modelViewTmp.set(applet.g.getMatrix()); //FIXME creates a new PMatrix3D everytime :(
modelViewTmp.set(PlatformUtil.getModelView());
// PMatrix3D projectionM = new PMatrix3D(((PGraphics3D)applet.g).projection);
projectionTmp.set(PlatformUtil.getProjection());
//-> in dekstop version glScale(1,-1,1) is done every frame because in (Desktop) opengl
// 0,0 is on the down left corner instead of upper left
if (PlatformUtil.isAndroid()){
screenY = MT4jSettings.getInstance().getWindowHeight() - screenY;
}
projectionTmp.apply(modelViewTmp);
projectionTmp.invert(); //Expensive!
// factor[0] = ((2 * screenX) / applet.width) -1;
// factor[1] = ((2 * screenY) / applet.height) -1;
factor[0] = ((2 * screenX) / MT4jSettings.getInstance().getWindowWidth()) -1;
factor[1] = ((2 * screenY) / MT4jSettings.getInstance().getWindowHeight()) -1;
factor[2] = (2 * winZ) -1;
factor[3] = 1;
//Matrix mit Vector multiplizieren
projectionTmp.mult(factor, result);
//System.out.println("\nResult2: ");
result[0] /= result[3];
result[1] /= result[3];
result[2] /= result[3];
result[3] /= result[3];
//aus Result Vector3D machen
returnVect = new Vector3D(result[0],result[1],result[2]);
}catch(Exception e){
e.printStackTrace();
}
// break;
//// */
// default:
// break;
// }
// System.out.println("unprojected: " + returnVect);
return returnVect;
}
private static Vector3D unprojectScreenCoords(PApplet applet, float winX, float winY, float winZ){
PMatrix3D modelView = new PMatrix3D(applet.g.getMatrix());
PMatrix3D projectionM = new PMatrix3D(PlatformUtil.getProjection());
if (PlatformUtil.isAndroid()){
winY = MT4jSettings.getInstance().getWindowHeight() - winY;
}
projectionM.apply(modelView);
projectionM.invert();
float[] result = new float[4];
float[] factor = new float[]{ ((2 * winX) / MT4jSettings.getInstance().getWindowWidth()) -1,
((2 * winY) / MT4jSettings.getInstance().getWindowHeight()) -1, //screenH - y?
(2 * winZ) -1 ,
1,};
projectionM.mult(factor, result);
//System.out.println("\nResult2: ");
result[0] /= result[3];
result[1] /= result[3];
result[2] /= result[3];
result[3] /= result[3];
return new Vector3D(result[0],result[1],result[2]);
}
/**
* Gets the ray to pick this component.
*
* @param applet the applet
* @param component the component
* @param cursor the cursor
* @return the camera pick ray
*/
public static Ray getCameraPickRay(PApplet applet, IMTComponent3D component, InputCursor cursor){
return Tools3D.getCameraPickRay(applet, component.getViewingCamera(), cursor.getCurrentEvtPosX(), cursor.getCurrentEvtPosY());
}
/**
* Constructs a picking ray from the components viewing camera position
* through the specified screen coordinates.
* The viewing camera of the object may not be null!
* <p><b>NOTE</b>: the openGL context has to be valid at the time of calling this method.
*
* @param applet the applet
* @param component the component
* @param screenX the screen x
* @param screenY the screen y
*
* @return the pick ray
*/
public static Ray getCameraPickRay(PApplet applet, IMTComponent3D component, float screenX, float screenY ){
return Tools3D.getCameraPickRay(applet, component.getViewingCamera(), screenX, screenY);
}
/**
* Constructs a picking ray from the components viewing camera position
* through the specified screen coordinates.
* <p><b>NOTE</b>: the openGL context has to be valid at the time of calling this method.
*
* @param applet the applet
* @param screenX the screen x
* @param screenY the screen y
* @param camera the camera
*
* @return the pick ray
*/
public static Ray getCameraPickRay(PApplet applet, Icamera camera, float screenX, float screenY ){
Vector3D rayStartPoint = camera.getPosition();
Vector3D newPointInRayDir = Tools3D.unprojectScreenCoords(applet, camera, screenX, screenY);
return new Ray(rayStartPoint, newPointInRayDir);
/*
// Vector3D near = unprojectNew( applet, screenX, screenY, 0);
// Vector3D far = unprojectNew( applet, screenX, screenY, 1);
// System.out.println("Near: " + near);
// System.out.println("Far: " + far);
// return new Ray(near, far);
*/
}
// /**
// * Projects the given point to screenspace.
// * <br>Shows where on the screen the point in 3d-Space will appear according
// * to the current viewport, model and projection matrices.
// * <p><b>NOTE</b>: the openGL context has to be valid at the time of calling this method.
// *
// * @param gl the gl
// * @param glu the glu
// * @param point the point to project to the screen
// *
// * @return the vector3 d
// */
// public static Vector3D projectGL(GL10 gl, GLU glu, Vector3D point){
// return projectGL(gl, glu, point, null);
// }
//
// /**
// * Projects the given point to screenspace.
// * <br>Shows where on the screen the point in 3d-Space will appear according
// * to the current viewport, model and projection matrices.
// * <br><strong>Note</strong>: this method has to be called between a call to <code>processingApplet.beginGL()</code>
// * and <code>processingApplet.endGL()</code>
// * <p><b>NOTE</b>: the openGL context has to be valid at the time of calling this method.
// *
// * @param gl the gl
// * @param glu the glu
// * @param point the point
// * @param store the store - vector to store the result in or null to get a new vector
// *
// * @return the vector3 d
// */
// public static Vector3D projectGL(GL11 gl, GLU glu, Vector3D point, Vector3D store){
// if (store == null){
// store = new Vector3D();
// }
//
// model.clear();
//// gl.glGetDoublev(GL11.GL_MODELVIEW_MATRIX, model);
// gl.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, model);
//
// proj.clear();
//// gl.glGetDoublev(GL11.GL_PROJECTION_MATRIX, proj);
// gl.glGetFloatv(GL11.GL_PROJECTION_MATRIX, proj);
//
// view.clear();
// gl.glGetIntegerv(GL11.GL_VIEWPORT, view);
// float viewPortHeight = (float)view.get(3);
//
// winPos.clear();
// glu.gluProject(point.x, point.y, point.z, model, proj, view, winPos);
//
// winPos.rewind();
// float x = (float) winPos.get();
// float y = (float) winPos.get();
// y = viewPortHeight - y; // Subtract The Current Y Coordinate From The Screen Height.
//
// store.setXYZ(x, y, 0);
// return store;
//// return new Vector3D(x, y, 0);
// }
/**
* Projects the given 3D point to screenspace.
* <br>Shows where on the screen the point in 3d-Space will appear according
* to the supplied camera, viewport, and projection matrices.
* The modelview is temporarily changed to match the supplied camera matrix.
*
* @param applet the applet
* @param cam the cam
* @param point the point
*
* @return the vector3 d
*/
public static Vector3D project(PApplet applet, Icamera cam, Vector3D point){
Vector3D ret;
applet.pushMatrix();
cam.update();
ret = Tools3D.project(applet, point);
applet.popMatrix();
return ret;
}
/**
* Projects the given point to screenspace. Uses the current modelview, and projection matrices - so update
* them accordingly before calling!
* <br>Shows where on the screen the point in 3d-Space will appear according
* to the current viewport, model and projection matrices.
* <p><b>NOTE</b>: if using openGL mode, the openGL context has to be valid at the time of calling this method.
*
* @param applet the applet
* @param point the point
*
* @return a new projected vector3d
*/
public static Vector3D project(PApplet applet, Vector3D point){
// switch (MT4jSettings.getInstance().getRendererMode()) {
// case MT4jSettings.OPENGL_MODE:
// try{
// PGraphicsOpenGL pgl = ((PGraphicsOpenGL)applet.g);
//// GL gl = pgl.beginGL();
// GL10 gl = GraphicsUtil.beginGL();
// GLU glu = pgl.glu;
// Vector3D returnVect = projectGL(gl, glu, point);
//// pgl.endGL();
// GraphicsUtil.endGL();
// return returnVect;
// }catch(Exception e){
// e.printStackTrace();
// //System.out.println("Use method getWorldForScreenCoords only when drawing with openGL! And dont put negative screen values in!");
// }
// break;
// case MT4jSettings.P3D_MODE:
//// /*!
// try{
// float x = applet.screenX(point.x, point.y, point.z);
// float y = applet.screenY(point.x, point.y, point.z);
// float z = applet.screenZ(point.x, point.y, point.z);
// return new Vector3D(x,y,z);
// }catch(Exception e){
// e.printStackTrace();
// }
// break;
//// */
// default:
// return new Vector3D(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
// }
try{
float x = applet.screenX(point.x, point.y, point.z);
float y = 0;
if (PlatformUtil.isAndroid()){ //because android opengl isnt inverted..?
y = applet.screenY(point.x, (MT4jSettings.getInstance().getWindowHeight() - point.y) * -1, point.z); // applet.height - screenY
}else{
y = applet.screenY(point.x, point.y, point.z);
}
// float y = applet.screenY(point.x, point.y, point.z);
// float y = applet.screenY(point.x, -1 * point.y, point.z); // y = -1 * y;
// float y = applet.screenY(point.x, (applet.height - point.y) * -1, point.z); // applet.height - screenY
// y = applet.height - y;
//we have to use applet.height - point.y in android, and in the screenY method dont use *-1
// if (GraphicsUtil.isAndroid()){
// screenY = applet.height - screenY;
// }
float z = applet.screenZ(point.x, point.y, point.z);
return new Vector3D(x,y,z);
}catch(Exception e){
e.printStackTrace();
}
return new Vector3D(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
}
/**
* Start drawing in on TOP of everything drawn previously,
* also resets the camera, so that something drawn at 0,0,0
* will be drawn at the top left corner of the screen regardless
* of the camera used
*
* You could say this allows you to draw directly on the screen, and
* on top of everything else. (at the near clipping plane?)
*
* NOTE: you have to CALL endDrawOnTopStayOnScreen() if finished!
*
* @param pa the pa
*/
public static void beginDrawOnTopStayOnScreen(PApplet pa){
switch (MT4jSettings.getInstance().getRendererMode()) {
case MT4jSettings.OPENGL_MODE:
// GL gl = ((PGraphicsOpenGL)pa.g).gl;
GL10 gl = PlatformUtil.getGL();
gl.glDepthFunc(GL10.GL_ALWAYS); //turn off Z buffering
//reset to the default camera
pa.camera();
break;
case MT4jSettings.P3D_MODE:
//FIXME how to handle platform independent?
// for(int i=0;i<((PGraphics3D)pa.g).zbuffer.length;i++){
// ((PGraphics3D)pa.g).zbuffer[i]=Float.MAX_VALUE;
// }
pa.camera();
break;
default:
break;
}
}
/**
* Stop drawing in 2D after calling begin2D().
*
* @param pa the pa
* @param camera the camera
*/
public static void endDrawOnTopStayOnScreen(PApplet pa, Icamera camera){
switch (MT4jSettings.getInstance().getRendererMode()) {
case MT4jSettings.OPENGL_MODE:
// GL gl = ((PGraphicsOpenGL)pa.g).gl;
GL10 gl = PlatformUtil.getGL();
gl.glDepthFunc(GL10.GL_LEQUAL); //This is used by standart processing..
//Change camera back to current 3d camera
camera.update();
break;
case MT4jSettings.P3D_MODE:
camera.update();
break;
default:
break;
}
}
/**
* Allows to draw ontop of everything, regardless of the values in the z-buffer.
* To stop doing that, call <code>endDrawOnTop(PApplet pa)</code> .
*
* @param g the g
*/
public static void disableDepthBuffer(PGraphics g){
switch (MT4jSettings.getInstance().getRendererMode()) {
case MT4jSettings.OPENGL_MODE:
// GL gl = ((PGraphicsOpenGL)pa.g).gl;
// GL gl = ((PGraphicsOpenGL)g).gl;
GL10 gl = PlatformUtil.getGL();
GL11Plus plus = PlatformUtil.getGL11Plus();
if (plus != null){
plus.glPushAttrib(GL10.GL_DEPTH_BUFFER_BIT);//FIXME TEST
}
// gl.glPushAttrib(GL10.GL_DEPTH_BUFFER_BIT);//FIXME TEST
gl.glDepthFunc(GL10.GL_ALWAYS); //turn off Z buffering
break;
case MT4jSettings.P3D_MODE:
//FIXME how to handle platform independent?
// for(int i=0;i<((PGraphics3D)g).zbuffer.length;i++){
// ((PGraphics3D)g).zbuffer[i]=Float.MAX_VALUE;
// }
break;
default:
break;
}
}
/**
* End draw on top.
*
* @param g the g
*/
public static void restoreDepthBuffer(PGraphics g){
switch (MT4jSettings.getInstance().getRendererMode()) {
case MT4jSettings.OPENGL_MODE:
// GL gl = ((PGraphicsOpenGL)g).gl;
GL11Plus plus = PlatformUtil.getGL11Plus();
if (plus != null){
plus.glPopAttrib();
}
// gl.glDepthFunc(GL.GL_LEQUAL); //This is used by standart processing..
//FIXME TEST
// gl.glPopAttrib();
break;
case MT4jSettings.P3D_MODE:
break;
default:
break;
}
}
//////////////////////////////////////////////////////////
// OPENGL STUFF //
//////////////////////////////////////////////////////////
/**
* Prints some available openGL extensions to the console.
* <p><b>NOTE</b>: the openGL context has to be valid at the time of calling this method.
*
* @param pa the pa
*/
public static void printGLExtensions(PApplet pa){
if (!MT4jSettings.getInstance().isOpenGlMode())
return;
// GL gl =((PGraphicsOpenGL)pa.g).beginGL();
GL10 gl = PlatformUtil.getGL();
String ext = gl.glGetString(GL10.GL_EXTENSIONS);
StringTokenizer tok = new StringTokenizer( ext, " " );
while (tok.hasMoreTokens()) {
System.out.println(tok.nextToken());
}
int[] redBits = new int[1];
int[] greenBits = new int[1];
int[] blueBits = new int[1];
int[] alphaBits = new int[1];
int[] stencilBits = new int[1];
int[] depthBits = new int[1];
gl.glGetIntegerv(GL10.GL_RED_BITS, redBits,0);
gl.glGetIntegerv(GL10.GL_GREEN_BITS, greenBits,0);
gl.glGetIntegerv(GL10.GL_BLUE_BITS, blueBits,0);
gl.glGetIntegerv(GL10.GL_ALPHA_BITS, alphaBits,0);
gl.glGetIntegerv(GL10.GL_STENCIL_BITS, stencilBits,0);
gl.glGetIntegerv(GL10.GL_DEPTH_BITS, depthBits,0);
System.out.println("Red bits: " + redBits[0]);
System.out.println("Green bits: " + greenBits[0]);
System.out.println("Blue bits: " + blueBits[0]);
System.out.println("Alpha bits: " + blueBits[0]);
System.out.println("Depth Buffer bits: " + depthBits[0]);
System.out.println("Stencil Buffer bits: " + stencilBits[0]);
// ((PGraphicsOpenGL)pa.g).endGL();
PlatformUtil.endGL();
}
/**
* Check for gl error.
*
* @param gl the gl
*/
public static int getGLError(GL10 gl){
int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR){
System.out.println("GL Error: " + error);
}else{
// System.out.println("No gl error.");
}
return error;
}
/**
* Gets the openGL context.
* <br>NOTE: If you want to invoke any opengl drawing commands (or other commands influencing or depending on the current modelview matrix)
* you have to call GL <code>Tools3D.beginGL(PApplet pa)</code> instead!
* <br>NOTE: the openGL context is only valid and current when the rendering thread is the current thread.
* <br>
* This only gets the opengl context if started in opengl mode using the opengl renderer.
*
* @param pa the pa
*
* @return the gL
*/
public static GL10 getGL(PApplet pa){
return PlatformUtil.getGL();
}
public static GL10 getGL(PGraphics g){
return PlatformUtil.getGL();
}
/**
* Begin gl.
*
* @param pa the pa
* @return the gL
*/
public static GL10 beginGL(PApplet pa){
return PlatformUtil.beginGL();
}
/**
* Begin gl.
*
* @param g the g
* @return the gL
*/
public static GL10 beginGL(PGraphics g){
return PlatformUtil.beginGL();
}
/**
* End gl.
*
* @param pa the pa
*/
public static void endGL(PApplet pa){
PlatformUtil.endGL();
}
/**
* End gl.
*
* @param g the g
*/
public static void endGL(PGraphics g){
PlatformUtil.endGL();
}
/**
* Checks whether the given extension is supported by the current opengl context.
* <p><b>NOTE</b>: the openGL context has to be valid at the time of calling this method.
*
* @param pa the pa
* @param extensionName the extension name
*
* @return true, if checks if is gl extension supported
*/
public static boolean isGLExtensionSupported(PApplet pa, String extensionName){
if (!MT4jSettings.getInstance().isOpenGlMode())
return false;
// GL gl =((PGraphicsOpenGL)pa.g).gl;
GL11Plus gl = PlatformUtil.getGL11Plus();
if (gl != null){
boolean avail = gl.isExtensionAvailable(extensionName);
/*
String ext = gl.glGetString(GL.GL_EXTENSIONS);
*/
return(avail);
}else{
System.err.println("GL profile doesent support 'isExtensionAvailable' command.");
return false;
}
}
/**
* Checks whether non power of two texture dimensions are natively supported
* by the gfx hardware.
*
* @param pa the pa
*
* @return true, if supports non power of two texture
*/
public static boolean supportsNonPowerOfTwoTexture(PApplet pa){
boolean supports = false;
if ( Tools3D.isGLExtensionSupported(pa, "GL_TEXTURE_RECTANGLE_ARB")
|| Tools3D.isGLExtensionSupported(pa, "GL_ARB_texture_non_power_of_two")
|| Tools3D.isGLExtensionSupported(pa, "GL_ARB_texture_rectangle")
|| Tools3D.isGLExtensionSupported(pa, "GL_NV_texture_rectangle")
|| Tools3D.isGLExtensionSupported(pa, "GL_TEXTURE_RECTANGLE_EXT")
|| Tools3D.isGLExtensionSupported(pa, "GL_EXT_texture_rectangle")
){
supports = true;
}
return supports;
}
/**
* Sets the opengl vertical syncing on or off.
*
* @param pa the pa
* @param on the on
*/
public static void setVSyncing(PApplet pa, boolean on){
if (MT4jSettings.getInstance().getRendererMode() == MT4jSettings.OPENGL_MODE){
// GL gl = getGL(pa);
GL11Plus gl = PlatformUtil.getGL11Plus();
if (on){
gl.setSwapInterval(1);
}else{
gl.setSwapInterval(0);
}
}
}
public static void setLineSmoothEnabled(GL10 gl, boolean enable){
// /*
//DO this if we use multisampling and enable line_smooth from the beginning
//and use multisampling -> we turn off multisampling then before using line_smooth for best restult
if (enable){
if (MT4jSettings.getInstance().isMultiSampling()){
gl.glDisable(GL10.GL_MULTISAMPLE);
}
//TODO Eventually even dont do that since enabled form the beginning!
gl.glEnable(GL10.GL_LINE_SMOOTH);
}else{
if (MT4jSettings.getInstance().isMultiSampling()){
gl.glEnable(GL10.GL_MULTISAMPLE);
}
// gl.glDisable(GL.GL_LINE_SMOOTH); //Actually never disable line smooth
}
// */
//DO nothing if we use Multisampling but disable line_smooth from the beginning
// -> do all anti aliasing only through multisampling!
//
/*
if (enable){
if (MT4jSettings.getInstance().isMultiSampling()){
gl.glDisable(GL.GL_MULTISAMPLE);
}
//TODO Eventually even dont do that since enabled form the beginning!
gl.glEnable(GL.GL_LINE_SMOOTH);
}else{
if (MT4jSettings.getInstance().isMultiSampling()){
gl.glEnable(GL.GL_MULTISAMPLE);
}
// gl.glDisable(GL.GL_LINE_SMOOTH); //Actually never disable line smooth
}
*/
}
//////////////////////////////////////////////////////
// Generate Display Lists and get their IDs //
//////////////////////////////////////////////////////
/**
* Creates 2 displaylists for drawing static geometry very fast.
* Returns the IDs (names) of the display lists generated with the given info.
*
* @param pa the pa
* @param geometryInfo the geometry info
* @param useTexture the use texture
* @param texture the texture
* @param styleInfo the style info
*
* @return the int[]
*
* Returns the IDs (names) of the display lists generated with the given info.
*/
public static int[] generateDisplayLists(PApplet pa, GeometryInfo geometryInfo, boolean useTexture, PImage texture, StyleInfo styleInfo){
return generateDisplayLists(pa, styleInfo.getFillDrawMode(), geometryInfo, useTexture, texture, styleInfo.isDrawSmooth(), styleInfo.getStrokeWeight());
}
/**
* Returns the IDs (names) of the display lists generated with the given info.
*
* @param pa the pa
* @param fillDrawMode the fill draw mode
* @param geometryInfo the geometry info
* @param useTexture the use texture
* @param texture the texture
* @param drawSmooth the draw smooth
* @param strokeWeight the stroke weight
*
* @return int[2] array where [0] is the list of the fill
* and [1] the list of the outline drawing list
*/
public static int[] generateDisplayLists(PApplet pa, int fillDrawMode, GeometryInfo geometryInfo,
boolean useTexture, PImage texture, boolean drawSmooth, float strokeWeight
){
FloatBuffer tbuff = geometryInfo.getTexBuff();
FloatBuffer vertBuff = geometryInfo.getVertBuff();
FloatBuffer colorBuff = geometryInfo.getColorBuff();
FloatBuffer strokeColBuff = geometryInfo.getStrokeColBuff();
Buffer indexBuff = geometryInfo.getIndexBuff(); //null if not indexed
GL10 gl = PlatformUtil.beginGL();
GL11Plus gl11Plus = PlatformUtil.getGL11Plus();
//Generate new list IDs
int[] returnVal = new int[2];
int listIDFill = gl11Plus.glGenLists(1);
if (listIDFill == 0){
System.err.println("Failed to create fill display list");
returnVal[0] = -1;
returnVal[1] = -1;
return returnVal;
}
int listIDOutline = gl11Plus.glGenLists(1);
if (listIDOutline == 0){
System.err.println("Failed to create stroke display list");
returnVal[0] = -1;
returnVal[1] = -1;
return returnVal;
}
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertBuff);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuff);
//Default target
int textureTarget = GL10.GL_TEXTURE_2D;
/////// DO FILL LIST/////////////////////////////////
/////////
boolean textureDrawn = false;
int usedTextureID = -1;
if (useTexture
&& texture != null
&& texture instanceof GLTexture) //Bad for performance?
{
GLTexture tex = (GLTexture)texture;
textureTarget = tex.getTextureTarget();
//tells opengl which texture to reference in following calls from now on!
//the first parameter is eigher GL.GL_TEXTURE_2D or ..1D
gl.glEnable(textureTarget);
usedTextureID = tex.getTextureID();
gl.glBindTexture(textureTarget, tex.getTextureID());
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tbuff);
textureDrawn = true;
}
// Normals
if (geometryInfo.isContainsNormals()){
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glNormalPointer(GL10.GL_FLOAT, 0, geometryInfo.getNormalsBuff());
}
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuff);
// START recording display list and DRAW////////////////////
gl11Plus.glNewList(listIDFill, GL11Plus.GL_COMPILE);
if (textureDrawn){
gl.glEnable(textureTarget); //muss texture in der liste gebinded werden? anscheinend JA!
gl.glBindTexture(textureTarget, usedTextureID);
}
//DRAW with drawElements if geometry is indexed, else draw with drawArrays!
if (geometryInfo.isIndexed()){
gl.glDrawElements(fillDrawMode, indexBuff.capacity(), GL10.GL_UNSIGNED_SHORT, indexBuff); //limit() oder capacity()??
}else{
gl.glDrawArrays(fillDrawMode, 0, vertBuff.capacity()/3);
}
if (textureDrawn){
gl.glBindTexture(textureTarget, 0);
gl.glDisable(textureTarget);
}
gl11Plus.glEndList();
//// STOP recording display list and DRAW////////////////////
if (geometryInfo.isContainsNormals()){
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
if (textureDrawn){
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
returnVal[0] = listIDFill;
/////// DO OUTLINE LIST////////////////////////////
gl.glColorPointer(4, GL10.GL_FLOAT, 0, strokeColBuff);
//Start recording display list
gl11Plus.glNewList(listIDOutline, GL11Plus.GL_COMPILE);
// if (drawSmooth)
// gl.glEnable(GL.GL_LINE_SMOOTH);
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, true);
if (strokeWeight > 0)
gl.glLineWidth(strokeWeight);
//DRAW
if (geometryInfo.isIndexed()){
gl.glDrawElements(GL10.GL_LINE_STRIP, indexBuff.capacity(), GL10.GL_UNSIGNED_SHORT, indexBuff); ////indices.limit()?
}else{
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, vertBuff.capacity()/3);
}
// if (drawSmooth)
// gl.glDisable(GL.GL_LINE_SMOOTH);
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, false);
gl11Plus.glEndList();
returnVal[1] = listIDOutline;
////////////////////////////////////////////////////
//Disable client states
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
return returnVal;
}
//TODO make only 1 function gendisplaylist mit boolean generate outline/fill
/**
* Returns the ID (name) of the display list
* If you dont want to use a line stipple pattern, use '0' for the parameter.
*
* @param pa the pa
* @param vertBuff the vert buff
* @param strokeColBuff the stroke col buff
* @param indexBuff the index buff
* @param drawSmooth the draw smooth
* @param strokeWeight the stroke weight
* @param lineStipple the line stipple
*
* @return int id of outline drawing list
*/
public static int generateOutLineDisplayList(PApplet pa, FloatBuffer vertBuff, FloatBuffer strokeColBuff, IntBuffer indexBuff,
boolean drawSmooth, float strokeWeight, short lineStipple){
// GL gl = beginGL(pa.g);
GL10 gl = PlatformUtil.beginGL();
GL11Plus gl11Plus = PlatformUtil.getGL11Plus();
//Generate new list IDs
int returnVal = -1;
int listIDOutline = gl11Plus.glGenLists(1);
if (listIDOutline == 0){
System.err.println("Failed to create display list");
return returnVal;
}
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertBuff);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, strokeColBuff);
//Start recording display list
gl11Plus.glNewList(listIDOutline, GL11Plus.GL_COMPILE);
// if (drawSmooth)
// gl.glEnable(GL.GL_LINE_SMOOTH);
//FIXME TEST for multisample
Tools3D.setLineSmoothEnabled(gl, true);
if (strokeWeight > 0)
gl.glLineWidth(strokeWeight);
if (lineStipple != 0){
gl11Plus.glLineStipple(1, lineStipple);
gl.glEnable(GL11Plus.GL_LINE_STIPPLE);
}
if (indexBuff == null){
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, vertBuff.capacity()/3);
}else{
gl.glDrawElements(GL10.GL_LINE_STRIP, indexBuff.capacity(), GL10.GL_UNSIGNED_SHORT, indexBuff); ////indices.limit()?
}
//RESET LINE STIPPLE
if (lineStipple != 0)
gl.glDisable(GL11Plus.GL_LINE_STIPPLE);
// if (drawSmooth)
// gl.glDisable(GL.GL_LINE_SMOOTH);
//FIXME TEST for multisample
Tools3D.setLineSmoothEnabled(gl, false);
gl11Plus.glEndList();
returnVal = listIDOutline;
//Disable client states
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
// ((PGraphicsOpenGL)pa.g).endGL();
PlatformUtil.endGL();
return returnVal;
}
/**
* Reverses an array.
*
* @param b the b
* @return the vector3 d[]
*/
public static Vector3D[] reverse(Vector3D[] b) {
int left = 0; // index of leftmost element
int right = b.length-1; // index of rightmost element
while (left < right) {
// exchange the left and right elements
Vector3D temp = b[left];
b[left] = b[right];
b[right] = temp;
// move the bounds toward the center
left++;
right--;
}
return b;
}//endmethod reverse
/**
* Checks whether the given image is of power of 2 dimensions.
*
* @param image the image
*
* @return true, if checks if is power of two dimension
*/
public static boolean isPowerOfTwoDimension(PImage image){
return ToolsMath.isPowerOfTwo(image.width) && ToolsMath.isPowerOfTwo(image.height);
}
/**
* For non power of two textures, the texture coordinates
* have to be in the range from 0..texture_width instead of from 0.0 to 1.0.
* <br>So we try to scale the texture coords to the width/height of the texture
*
* @param texture the texture
* @param verts the verts
*/
public static void scaleTextureCoordsForRectModeFromNormalized(PImage texture, Vertex[] verts){
for (Vertex vertex : verts) {
if (vertex.getTexCoordU() <= 1.0f && vertex.getTexCoordU() >= 0.0f) {
vertex.setTexCoordU(vertex.getTexCoordU() * texture.width);
}
if (vertex.getTexCoordV() <= 1.0f && vertex.getTexCoordV() >= 0.0f) {
vertex.setTexCoordV(vertex.getTexCoordV() * texture.height);
}
}
}
/**
* projects a specific point on a plane with a specific depth
* @param gl
* @param point
* @param frustum
* @param z
* @return
*/
public static Vector3D projectPointToPlaneInPerspectiveMode(Vector3D point,IFrustum frustum,float z,AbstractMTApplication mtApp)
{
float heightOfPlaneAtZ = frustum.getHeightOfPlane(z);
float widthOfPlaneAtZ = frustum.getWidthOfPlane(z);
float heightOfPlaneAtPoint = frustum.getHeightOfPlane(point.z);
float widthOfPlaneAtPoint = frustum.getWidthOfPlane(point.z);
//float centerX = mtApp.width/2;
//float centerY = mtApp.height/2;
Vector3D ntl = frustum.getNearTopLeft();
//subtract getWidthofNearPlane, because frustum is upside down
float centerX = ntl.x - frustum.getWidthOfNearPlane() + frustum.getWidthOfNearPlane()/2f;
float centerY = ntl.y + frustum.getHeightOfNearPlane()/2f;
float percentWidth = (point.x - (centerX-(widthOfPlaneAtPoint/2.f)))/widthOfPlaneAtPoint;
float percentHeight = (point.y - (centerY-(heightOfPlaneAtPoint/2.f)))/heightOfPlaneAtPoint;
Vector3D projectedPoint = new Vector3D();
projectedPoint.x = (centerX - (widthOfPlaneAtZ/2.f))+widthOfPlaneAtZ*percentWidth;
projectedPoint.y = (centerY - (heightOfPlaneAtZ/2.f))+heightOfPlaneAtZ*percentHeight;
projectedPoint.z = z;
return projectedPoint;
}
public static boolean adaptTextureCoordsNPOT(AbstractShape shape, GLTexture tex){
if(!PlatformUtil.isNPOTTextureSupported()
&& !shape.getGeometryInfo().isTextureCoordsAdaptedNPOT()
&& !Tools3D.isPowerOfTwoDimension(tex)
&& ((GLTexture) tex).getTextureTargetEnum() == TEXTURE_TARGET.TEXTURE_2D
&& shape.getGeometryInfo().isTextureCoordsNormalized()
) {
GLTexture glt = (GLTexture) tex;
float maxU = (float)glt.width / (float)glt.glWidth;
float maxV = (float)glt.height / (float)glt.glHeight;
Vertex[] verts = shape.getVerticesLocal();
for (Vertex vertex : verts) {
// vertex.setTexCoordU( ( (vertex.x - upperLeftX) / width) * maxU);
// vertex.setTexCoordV( ( (vertex.y - upperLeftY) / height) * maxV);
vertex.setTexCoordU( vertex.getTexCoordU() * maxU);
vertex.setTexCoordV( vertex.getTexCoordV() * maxV);
// System.out.println("TexU:" + vertex.getTexCoordU() + " TexV:" + vertex.getTexCoordV());
}
shape.getGeometryInfo().updateTextureBuffer(shape.isUseVBOs());
shape.getGeometryInfo().setTextureCoordsAdaptedNPOT(true);
return true;
}
return false;
}
}