/*
* Portions Copyright (C) 2003-2005 Sun Microsystems, Inc.
* All rights reserved.
*/
/*
*
* COPYRIGHT NVIDIA CORPORATION 2003. ALL RIGHTS RESERVED.
* BY ACCESSING OR USING THIS SOFTWARE, YOU AGREE TO:
*
* 1) ACKNOWLEDGE NVIDIA'S EXCLUSIVE OWNERSHIP OF ALL RIGHTS
* IN AND TO THE SOFTWARE;
*
* 2) NOT MAKE OR DISTRIBUTE COPIES OF THE SOFTWARE WITHOUT
* INCLUDING THIS NOTICE AND AGREEMENT;
*
* 3) ACKNOWLEDGE THAT TO THE MAXIMUM EXTENT PERMITTED BY
* APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND
* THAT NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES,
* EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED
* TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY
* SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES
* WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS
* OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
* INFORMATION, OR ANY OTHER PECUNIARY LOSS), INCLUDING ATTORNEYS'
* FEES, RELATING TO THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
*/
package demos.hwShadowmapsSimple;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.nio.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;
import net.java.games.jogl.*;
import net.java.games.jogl.util.*;
import demos.util.*;
import gleem.*;
import gleem.linalg.*;
/** This demo is a simple illustration of ARB_shadow and ARB_depth_texture.
Cass Everitt
12-11-00
hw_shadowmaps_simple (c) 2001 NVIDIA Corporation
Ported to Java by Kenneth Russell
*/
public class HWShadowmapsSimple implements GLEventListener {
public static void main(String[] args) {
final GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities());
HWShadowmapsSimple demo = new HWShadowmapsSimple();
canvas.addGLEventListener(demo);
demo.setDemoListener(new DemoListener() {
public void shutdownDemo() {
runExit();
}
public void repaint() {
canvas.repaint();
}
});
Frame frame = new Frame("ARB_shadow Shadows");
frame.setLayout(new BorderLayout());
canvas.setSize(512, 512);
frame.add(canvas, BorderLayout.CENTER);
frame.pack();
frame.show();
canvas.requestFocus();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
runExit();
}
});
}
public void setDemoListener(DemoListener listener) {
demoListener = listener;
}
//----------------------------------------------------------------------
// Internals only below this point
//
private DemoListener demoListener;
private GLPbuffer pbuffer;
private GLUT glut;
private float[] light_ambient = { 0, 0, 0, 0 };
private float[] light_intensity = { 1, 1, 1, 1 };
private float[] light_pos = { 0, 0, 0, 1 };
static class Tweak {
String name;
float val;
float incr;
Tweak(String name, float val, float incr) {
this.name = name;
this.val = val;
this.incr = incr;
}
};
private java.util.List/**/ tweaks = new ArrayList();
private static final int R_COORDINATE_SCALE = 0;
private static final int R_COORDINATE_BIAS = 1;
private static final int POLYGON_OFFSET_SCALE = 2;
private static final int POLYGON_OFFSET_BIAS = 3;
private int curr_tweak;
// Texture objects
private static final int TEX_SIZE = 1024;
private int decal;
private int light_image;
private int light_view_depth;
// Depth buffer format
private int depth_format;
private boolean fullyInitialized;
// Display mode
private static final int RENDER_SCENE_FROM_CAMERA_VIEW = 0;
private static final int RENDER_SCENE_FROM_CAMERA_VIEW_SHADOWED = 1;
private static final int RENDER_SCENE_FROM_LIGHT_VIEW = 2;
private static final int NUM_DISPLAY_MODES = 3;
private int displayMode = 1;
// Display lists
private int quad;
private int wirecube;
private int geometry;
// Shadowing light
private float lightshaper_fovy = 60.0f;
private float lightshaper_zNear = 0.5f;
private float lightshaper_zFar = 5.0f;
// Manipulators
private ExaminerViewer viewer;
private boolean doViewAll = true;
// private float zNear = 0.5f;
// private float zFar = 5.0f;
private float zNear = 0.5f;
private float zFar = 50.0f;
private HandleBoxManip object;
private HandleBoxManip spotlight;
private Mat4f cameraPerspective = new Mat4f();
private Mat4f cameraTransform = new Mat4f();
private Mat4f cameraInverseTransform = new Mat4f();
private Mat4f spotlightTransform = new Mat4f();
private Mat4f spotlightInverseTransform = new Mat4f();
private Mat4f objectTransform = new Mat4f();
public void init(GLAutoDrawable drawable) {
// Use debug pipeline
// drawable.setGL(new DebugGL(drawable.getGL()));
GL gl = drawable.getGL();
GLU glu = drawable.getGLU();
glut = new GLUT();
try {
checkExtension(gl, "GL_ARB_multitexture");
checkExtension(gl, "GL_ARB_depth_texture");
checkExtension(gl, "GL_ARB_shadow");
checkExtension(gl, "GL_ARB_pbuffer");
checkExtension(gl, "GL_ARB_pixel_format");
} catch (GLException e) {
e.printStackTrace();
throw(e);
}
gl.glClearColor(.5f, .5f, .5f, .5f);
decal = genTexture(gl);
gl.glBindTexture(GL.GL_TEXTURE_2D, decal);
BufferedImage img = readPNGImage("demos/data/images/decal_image.png");
makeRGBTexture(gl, glu, img, GL.GL_TEXTURE_2D, true);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
light_image = genTexture(gl);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_image);
img = readPNGImage("demos/data/images/nvlogo_spot.png");
makeRGBTexture(gl, glu, img, GL.GL_TEXTURE_2D, true);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
quad = gl.glGenLists(1);
gl.glNewList(quad, GL.GL_COMPILE);
gl.glPushMatrix();
gl.glRotatef(-90, 1, 0, 0);
gl.glScalef(4,4,4);
gl.glBegin(GL.GL_QUADS);
gl.glNormal3f(0, 0, 1);
gl.glVertex2f(-1, -1);
gl.glVertex2f(-1, 1);
gl.glVertex2f( 1, 1);
gl.glVertex2f( 1, -1);
gl.glEnd();
gl.glPopMatrix();
gl.glEndList();
wirecube = gl.glGenLists(1);
gl.glNewList(wirecube, GL.GL_COMPILE);
glut.glutWireCube(gl, 2);
gl.glEndList();
geometry = gl.glGenLists(1);
gl.glNewList(geometry, GL.GL_COMPILE);
gl.glPushMatrix();
glut.glutSolidTeapot(gl, 0.8f);
gl.glPopMatrix();
gl.glEndList();
gl.glEnable(GL.GL_LIGHT0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, light_ambient, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light_intensity, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light_intensity, 0);
gl.glEnable(GL.GL_DEPTH_TEST);
// init pbuffer
GLCapabilities caps = new GLCapabilities();
caps.setDoubleBuffered(false);
if (!GLDrawableFactory.getFactory().canCreateGLPbuffer(caps, TEX_SIZE, TEX_SIZE)) {
unavailableExtension("Can not create pbuffer");
}
if (pbuffer != null) {
pbuffer.destroy();
pbuffer = null;
}
pbuffer = GLDrawableFactory.getFactory().createGLPbuffer(caps, TEX_SIZE, TEX_SIZE, drawable.getContext());
pbuffer.addGLEventListener(new PbufferListener());
doViewAll = true;
// Register the window with the ManipManager
ManipManager manager = ManipManager.getManipManager();
manager.registerWindow(drawable);
object = new HandleBoxManip();
object.setTranslation(new Vec3f(0, 0.7f, 1.8f));
object.setGeometryScale(new Vec3f(0.7f, 0.7f, 0.7f));
manager.showManipInWindow(object, drawable);
spotlight = new HandleBoxManip();
spotlight.setScale(new Vec3f(0.5f, 0.5f, 0.5f));
spotlight.setTranslation(new Vec3f(-0.25f, 2.35f, 5.0f));
spotlight.setRotation(new Rotf(Vec3f.X_AXIS, (float) Math.toRadians(-30.0f)));
manager.showManipInWindow(spotlight, drawable);
viewer = new ExaminerViewer(MouseButtonHelper.numMouseButtons());
viewer.attach(drawable, new BSphereProvider() {
public BSphere getBoundingSphere() {
return new BSphere(object.getTranslation(), 2.0f);
}
});
viewer.setOrientation(new Rotf(Vec3f.Y_AXIS, (float) Math.toRadians(45.0f)).times
(new Rotf(Vec3f.X_AXIS, (float) Math.toRadians(-15.0f))));
viewer.setVertFOV((float) Math.toRadians(lightshaper_fovy / 2.0f));
viewer.setZNear(zNear);
viewer.setZFar(zFar);
float bias = 1/((float) Math.pow(2.0,16.0)-1);
tweaks.add(new Tweak("r coordinate scale", 0.5f, bias));
tweaks.add(new Tweak("r coordinate bias", 0.5f, bias));
tweaks.add(new Tweak("polygon offset scale", 2.5f, 0.5f));
tweaks.add(new Tweak("polygon offset bias", 10.0f, 1.0f));
drawable.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
dispatchKey(e.getKeyChar());
demoListener.repaint();
}
});
}
public void display(GLAutoDrawable drawable) {
viewer.update();
// Grab these values once per render to avoid multithreading
// issues with their values being changed by manipulation from
// the AWT thread during the render
CameraParameters params = viewer.getCameraParameters();
cameraPerspective.set(params.getProjectionMatrix());
cameraInverseTransform.set(params.getModelviewMatrix());
cameraTransform.set(cameraInverseTransform);
cameraTransform.invertRigid();
spotlightTransform.set(spotlight.getTransform());
spotlightInverseTransform.set(spotlightTransform);
spotlightInverseTransform.invertRigid();
objectTransform.set(object.getTransform());
if (displayMode == RENDER_SCENE_FROM_CAMERA_VIEW_SHADOWED || !fullyInitialized) {
if (pbuffer != null) {
pbuffer.display();
}
}
if (!fullyInitialized) {
// Repaint again later once everything is set up
demoListener.repaint();
return;
}
GL gl = drawable.getGL();
GLU glu = drawable.getGLU();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
if (doViewAll) {
viewer.viewAll(gl);
doViewAll = false;
// Immediately zap effects
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
// Schedule repaint to clean up first bogus frame
demoListener.repaint();
}
switch (displayMode) {
case RENDER_SCENE_FROM_CAMERA_VIEW: render_scene_from_camera_view(gl, glu, drawable, params); break;
case RENDER_SCENE_FROM_CAMERA_VIEW_SHADOWED: render_scene_from_camera_view_shadowed(gl, glu, drawable, params); break;
case RENDER_SCENE_FROM_LIGHT_VIEW: render_scene_from_light_view(gl, glu, drawable); break;
default: throw new RuntimeException("Illegal display mode " + displayMode);
}
}
// Unused routines
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
private void checkExtension(GL gl, String extensionName) {
if (!gl.isExtensionAvailable(extensionName)) {
String message = "Unable to initialize " + extensionName + " OpenGL extension";
unavailableExtension(message);
}
}
private void unavailableExtension(String message) {
JOptionPane.showMessageDialog(null, message, "Unavailable extension", JOptionPane.ERROR_MESSAGE);
throw new GLException(message);
}
private void dispatchKey(char k) {
switch (k) {
case 27:
case 'q':
demoListener.shutdownDemo();
break;
case 'v':
doViewAll = true;
System.err.println("Forcing viewAll()");
break;
case ' ':
displayMode = (displayMode + 1) % NUM_DISPLAY_MODES;
System.err.println("Switching to display mode " + displayMode);
break;
// FIXME: add more key behaviors from original demo
default:
break;
}
}
class PbufferListener implements GLEventListener {
public void init(GLAutoDrawable drawable) {
// Use debug pipeline
// drawable.setGL(new DebugGL(drawable.getGL()));
GL gl = drawable.getGL();
GLU glu = drawable.getGLU();
gl.glEnable(GL.GL_DEPTH_TEST);
int[] depth_bits = new int[1];
gl.glGetIntegerv(GL.GL_DEPTH_BITS, depth_bits, 0);
if (depth_bits[0] == 16) depth_format = GL.GL_DEPTH_COMPONENT16_ARB;
else depth_format = GL.GL_DEPTH_COMPONENT24_ARB;
light_view_depth = genTexture(gl);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_view_depth);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, depth_format, TEX_SIZE, TEX_SIZE, 0,
GL.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, (byte[]) null, 0);
set_light_view_texture_parameters(gl);
fullyInitialized = true;
}
public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
GLU glu = drawable.getGLU();
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glPolygonOffset(((Tweak) tweaks.get(POLYGON_OFFSET_SCALE)).val,
((Tweak) tweaks.get(POLYGON_OFFSET_BIAS)).val);
gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
render_scene_from_light_view(gl, glu, drawable);
gl.glDisable(GL.GL_POLYGON_OFFSET_FILL);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_view_depth);
// trying different ways of getting the depth info over
gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, TEX_SIZE, TEX_SIZE);
}
// Unused routines
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
}
private void set_light_view_texture_parameters(GL gl) {
gl.glBindTexture(GL.GL_TEXTURE_2D, light_view_depth);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_COMPARE_MODE_ARB, GL.GL_COMPARE_R_TO_TEXTURE_ARB);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_COMPARE_FUNC_ARB, GL.GL_LEQUAL);
}
private int genTexture(GL gl) {
int[] tmp = new int[1];
gl.glGenTextures(1, tmp, 0);
return tmp[0];
}
private BufferedImage readPNGImage(String resourceName) {
try {
// Note: use of BufferedInputStream works around 4764639/4892246
BufferedImage img = ImageIO.read(new BufferedInputStream(getClass().getClassLoader().getResourceAsStream(resourceName)));
if (img == null) {
throw new RuntimeException("Error reading resource " + resourceName);
}
return img;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void makeRGBTexture(GL gl, GLU glu, BufferedImage img, int target, boolean mipmapped) {
ByteBuffer dest = null;
switch (img.getType()) {
case BufferedImage.TYPE_3BYTE_BGR:
case BufferedImage.TYPE_CUSTOM: {
byte[] data = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
dest = ByteBuffer.allocateDirect(data.length);
dest.order(ByteOrder.nativeOrder());
dest.put(data, 0, data.length);
break;
}
case BufferedImage.TYPE_INT_RGB: {
int[] data = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
dest = ByteBuffer.allocateDirect(data.length * BufferUtils.SIZEOF_INT);
dest.order(ByteOrder.nativeOrder());
dest.asIntBuffer().put(data, 0, data.length);
break;
}
default:
throw new RuntimeException("Unsupported image type " + img.getType());
}
if (mipmapped) {
glu.gluBuild2DMipmaps(target, GL.GL_RGB8, img.getWidth(), img.getHeight(), GL.GL_RGB,
GL.GL_UNSIGNED_BYTE, dest);
} else {
gl.glTexImage2D(target, 0, GL.GL_RGB, img.getWidth(), img.getHeight(), 0,
GL.GL_RGB, GL.GL_UNSIGNED_BYTE, dest);
}
}
private void eye_linear_texgen(GL gl) {
Mat4f m = new Mat4f();
m.makeIdent();
set_texgen_planes(gl, GL.GL_EYE_PLANE, m);
gl.glTexGeni(GL.GL_S, GL.GL_TEXTURE_GEN_MODE, GL.GL_EYE_LINEAR);
gl.glTexGeni(GL.GL_T, GL.GL_TEXTURE_GEN_MODE, GL.GL_EYE_LINEAR);
gl.glTexGeni(GL.GL_R, GL.GL_TEXTURE_GEN_MODE, GL.GL_EYE_LINEAR);
gl.glTexGeni(GL.GL_Q, GL.GL_TEXTURE_GEN_MODE, GL.GL_EYE_LINEAR);
}
private void obj_linear_texgen(GL gl) {
Mat4f m = new Mat4f();
m.makeIdent();
set_texgen_planes(gl, GL.GL_OBJECT_PLANE, m);
gl.glTexGeni(GL.GL_S, GL.GL_TEXTURE_GEN_MODE, GL.GL_OBJECT_LINEAR);
gl.glTexGeni(GL.GL_T, GL.GL_TEXTURE_GEN_MODE, GL.GL_OBJECT_LINEAR);
gl.glTexGeni(GL.GL_R, GL.GL_TEXTURE_GEN_MODE, GL.GL_OBJECT_LINEAR);
gl.glTexGeni(GL.GL_Q, GL.GL_TEXTURE_GEN_MODE, GL.GL_OBJECT_LINEAR);
}
private void set_texgen_planes(GL gl, int plane_type, Mat4f m) {
int[] coord = {GL.GL_S, GL.GL_T, GL.GL_R, GL.GL_Q};
float[] row = new float[4];
for(int i = 0; i < 4; i++) {
getRow(m, i, row);
gl.glTexGenfv(coord[i], plane_type, row, 0);
}
}
private void texgen(GL gl, boolean enable) {
if(enable) {
gl.glEnable(GL.GL_TEXTURE_GEN_S);
gl.glEnable(GL.GL_TEXTURE_GEN_T);
gl.glEnable(GL.GL_TEXTURE_GEN_R);
gl.glEnable(GL.GL_TEXTURE_GEN_Q);
} else {
gl.glDisable(GL.GL_TEXTURE_GEN_S);
gl.glDisable(GL.GL_TEXTURE_GEN_T);
gl.glDisable(GL.GL_TEXTURE_GEN_R);
gl.glDisable(GL.GL_TEXTURE_GEN_Q);
}
}
private void render_light_frustum(GL gl) {
gl.glPushMatrix();
applyTransform(gl, cameraInverseTransform);
applyTransform(gl, spotlightTransform);
applyTransform(gl, perspectiveInverse(lightshaper_fovy, 1, lightshaper_zNear, lightshaper_zFar));
gl.glDisable(GL.GL_LIGHTING);
gl.glColor3f(1,1,0);
gl.glCallList(wirecube);
gl.glColor3f(1,1,1);
gl.glEnable(GL.GL_LIGHTING);
gl.glPopMatrix();
}
private void render_quad(GL gl) {
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
obj_linear_texgen(gl);
texgen(gl, true);
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glScalef(4,4,1);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glDisable(GL.GL_LIGHTING);
gl.glBindTexture(GL.GL_TEXTURE_2D, decal);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glCallList(quad);
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glEnable(GL.GL_LIGHTING);
texgen(gl, false);
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glMatrixMode(GL.GL_MODELVIEW);
}
private void render_scene(GL gl, Mat4f view, GLAutoDrawable drawable, CameraParameters params) {
gl.glColor3f(1,1,1);
gl.glPushMatrix();
Mat4f inverseView = new Mat4f(view);
inverseView.invertRigid();
applyTransform(gl, inverseView);
gl.glPushMatrix();
render_quad(gl);
applyTransform(gl, objectTransform);
gl.glEnable(GL.GL_LIGHTING);
gl.glCallList(geometry);
gl.glDisable(GL.GL_LIGHTING);
gl.glPopMatrix();
gl.glPopMatrix();
}
private void render_manipulators(GL gl, Mat4f view, GLAutoDrawable drawable, CameraParameters params) {
gl.glColor3f(1,1,1);
gl.glPushMatrix();
Mat4f inverseView = new Mat4f(view);
inverseView.invertRigid();
applyTransform(gl, inverseView);
if (params != null) {
ManipManager.getManipManager().updateCameraParameters(drawable, params);
ManipManager.getManipManager().render(drawable, gl);
}
gl.glPopMatrix();
}
private void render_scene_from_camera_view(GL gl, GLU glu, GLAutoDrawable drawable, CameraParameters params) {
// place light
gl.glPushMatrix();
gl.glLoadIdentity();
applyTransform(gl, cameraInverseTransform);
applyTransform(gl, spotlightTransform);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_pos, 0);
gl.glPopMatrix();
// spot image
gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
gl.glPushMatrix();
applyTransform(gl, cameraInverseTransform);
eye_linear_texgen(gl);
texgen(gl, true);
gl.glPopMatrix();
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(.5f, .5f, .5f);
gl.glScalef(.5f, .5f, .5f);
glu.gluPerspective(lightshaper_fovy, 1, lightshaper_zNear, lightshaper_zFar);
applyTransform(gl, spotlightInverseTransform);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_image);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glViewport(0, 0, drawable.getWidth(), drawable.getHeight());
applyTransform(gl, cameraPerspective);
gl.glMatrixMode(GL.GL_MODELVIEW);
render_scene(gl, cameraTransform, drawable, params);
gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
render_manipulators(gl, cameraTransform, drawable, params);
render_light_frustum(gl);
}
private void render_scene_from_camera_view_shadowed(GL gl, GLU glu, GLAutoDrawable drawable, CameraParameters params) {
// place light
gl.glPushMatrix();
gl.glLoadIdentity();
applyTransform(gl, cameraInverseTransform);
applyTransform(gl, spotlightTransform);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_pos, 0);
gl.glPopMatrix();
// spot image
gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
gl.glPushMatrix();
applyTransform(gl, cameraInverseTransform);
eye_linear_texgen(gl);
texgen(gl, true);
gl.glPopMatrix();
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(.5f, .5f, .5f);
gl.glScalef(.5f, .5f, .5f);
glu.gluPerspective(lightshaper_fovy, 1, lightshaper_zNear, lightshaper_zFar);
applyTransform(gl, spotlightInverseTransform);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_image);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
// depth compare
gl.glActiveTextureARB(GL.GL_TEXTURE2_ARB);
gl.glPushMatrix();
applyTransform(gl, cameraInverseTransform);
eye_linear_texgen(gl);
texgen(gl, true);
gl.glPopMatrix();
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(.5f, .5f, ((Tweak) tweaks.get(R_COORDINATE_SCALE)).val);
gl.glScalef(.5f, .5f, ((Tweak) tweaks.get(R_COORDINATE_BIAS)).val);
glu.gluPerspective(lightshaper_fovy, 1, lightshaper_zNear, lightshaper_zFar);
applyTransform(gl, spotlightInverseTransform);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_view_depth);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
gl.glViewport(0, 0, drawable.getWidth(), drawable.getHeight());
applyTransform(gl, cameraPerspective);
gl.glMatrixMode(GL.GL_MODELVIEW);
render_scene(gl, cameraTransform, drawable, params);
gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glActiveTextureARB(GL.GL_TEXTURE2_ARB);
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
render_manipulators(gl, cameraTransform, drawable, params);
render_light_frustum(gl);
}
private void largest_square_power_of_two_viewport(GL gl, GLAutoDrawable drawable) {
float min = Math.min(drawable.getWidth(), drawable.getHeight());
float log2min = (float) Math.log(min) / (float) Math.log(2.0);
float pow2 = (float) Math.floor(log2min);
int size = 1 << (int) pow2;
gl.glViewport(0, 0, size, size);
}
private void render_scene_from_light_view(GL gl, GLU glu, GLAutoDrawable drawable) {
// place light
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_pos, 0);
gl.glPopMatrix();
// spot image
gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
gl.glPushMatrix();
eye_linear_texgen(gl);
texgen(gl, true);
gl.glPopMatrix();
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(.5f, .5f, .5f);
gl.glScalef(.5f, .5f, .5f);
glu.gluPerspective(lightshaper_fovy, 1, lightshaper_zNear, lightshaper_zFar);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glBindTexture(GL.GL_TEXTURE_2D, light_image);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
gl.glViewport(0, 0, TEX_SIZE, TEX_SIZE);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(lightshaper_fovy, 1, lightshaper_zNear, lightshaper_zFar);
gl.glMatrixMode(GL.GL_MODELVIEW);
if (displayMode == RENDER_SCENE_FROM_LIGHT_VIEW)
largest_square_power_of_two_viewport(gl, drawable);
render_scene(gl, spotlightTransform, null, null);
gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
}
private static void getRow(Mat4f m, int row, float[] out) {
out[0] = m.get(row, 0);
out[1] = m.get(row, 1);
out[2] = m.get(row, 2);
out[3] = m.get(row, 3);
}
private static void applyTransform(GL gl, Mat4f xform) {
float[] data = new float[16];
xform.getColumnMajorData(data);
gl.glMultMatrixf(data, 0);
}
private static Mat4f perspectiveInverse(float fovy, float aspect, float zNear, float zFar) {
float tangent = (float) Math.tan(Math.toRadians(fovy / 2.0));
float y = tangent * zNear;
float x = aspect * y;
return frustumInverse(-x, x, -y, y, zNear, zFar);
}
private static Mat4f frustumInverse(float left, float right,
float bottom, float top,
float zNear, float zFar) {
Mat4f m = new Mat4f();
m.makeIdent();
m.set(0, 0, (right - left) / (2 * zNear));
m.set(0, 3, (right + left) / (2 * zNear));
m.set(1, 1, (top - bottom) / (2 * zNear));
m.set(1, 3, (top + bottom) / (2 * zNear));
m.set(2, 2, 0);
m.set(2, 3, -1);
m.set(3, 2, -(zFar - zNear) / (2 * zFar * zNear));
m.set(3, 3, (zFar + zNear) / (2 * zFar * zNear));
return m;
}
private static void runExit() {
// Note: calling System.exit() synchronously inside the draw,
// reshape or init callbacks can lead to deadlocks on certain
// platforms (in particular, X11) because the JAWT's locking
// routines cause a global AWT lock to be grabbed. Run the
// exit routine in another thread.
new Thread(new Runnable() {
public void run() {
System.exit(0);
}
}).start();
}
}