|
This example shows how to create shadows with JOGL.
This is the Java port of the one of the NeHe OpenGL tutorials.
You can get complete IntelliJ IDEA project structure (all source, resources, build script, …) by downloading the source distribution from here.
The original post of the programmer who ported the examples can be found here.
package demos.nehe.lesson27;
/*--. .-"-.
/ o_O / O o \
\_ (__\ \_ v _/
// \\ // \\
(( )) (( ))
¤¤¤¤¤¤¤¤¤¤¤¤¤¤--""---""--¤¤¤¤--""---""--¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
¤ ||| ||| ¤
¤ | | ¤
¤ ¤
¤ Programmer:Abdul Bezrati ¤
¤ Program :Nehe's 27th lesson port to JOGL ¤
¤ Comments :None ¤
¤ _______ ¤
¤ /` _____ `\;,
This e-mail address is being protected from spam bots, you need JavaScript enabled to view it
¤
¤ (__(^===^)__)';, ___ ¤
¤ / ::: \ ,; /^ ^\ ¤
¤ | ::: | ,;' ( Ö Ö ) ¤
¤¤¤'._______.'`¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ --°oOo--(_)--oOo°--¤¤*/
import demos.common.GLDisplay;
/**
* @author Abdul Bezrati
*/
public class Lesson27 {
public static void main(String[] args) {
GLDisplay neheGLDisplay = GLDisplay.createGLDisplay("Lesson 27: Shadows");
Renderer renderer = new Renderer();
InputHandler inputHandler = new InputHandler(renderer, neheGLDisplay);
neheGLDisplay.addGLEventListener(renderer);
neheGLDisplay.addKeyListener(inputHandler);
neheGLDisplay.start();
}
}
package demos.nehe.lesson27;
import demos.common.GLDisplay;
import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
class InputHandler extends KeyAdapter {
private Renderer renderer;
public InputHandler(Renderer renderer, GLDisplay glDisplay) {
this.renderer = renderer;
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
"Decrease X-axis rotation speed");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
"Increase X-axis rotation speed");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
"Decrease Y-axis rotation speed");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
"Increase Y-axis rotation speed");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_L, 0),
"Move light right");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_J, 0),
"Move light left");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_I, 0),
"Move light up");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_K, 0),
"Move light down");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_O, 0),
"Move light forward");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_U, 0),
"Move light backward");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_D, 0),
"Move ball right");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "Move ball left");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "Move ball up");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "Move ball down");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_E, 0), "Move ball forward");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0), "Move ball backward");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD6, 0), "Move cross right");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD4, 0), "Move cross left");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD8, 0), "Move cross up");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD5, 0), "Move cross down");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD9, 0), "Move cross forward");
glDisplay.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD7, 0), "Move cross backward");
}
public void keyPressed(KeyEvent e) {
processKeyEvent(e, true);
}
public void keyReleased(KeyEvent e) {
processKeyEvent(e, false);
}
private void processKeyEvent(KeyEvent e, boolean pressed) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
renderer.decreaseXspeed(pressed);
break;
case KeyEvent.VK_DOWN:
renderer.increaseXspeed(pressed);
break;
case KeyEvent.VK_RIGHT:
renderer.increaseYspeed(pressed);
break;
case KeyEvent.VK_LEFT:
renderer.decreaseYspeed(pressed);
break;
// Adjust Light's Position
case KeyEvent.VK_L:
renderer.translateLightRight(pressed); // 'L' Moves Light Right
break;
case KeyEvent.VK_J:
renderer.translateLightLeft(pressed); // 'J' Moves Light Left
break;
case KeyEvent.VK_I:
renderer.translateLightUp(pressed); // 'I' Moves Light Up
break;
case KeyEvent.VK_K:
renderer.translateLightDown(pressed); // 'K' Moves Light Down
break;
case KeyEvent.VK_O:
// 'O' Moves Light Toward Viewer
renderer.translateLightForward(pressed);
break;
case KeyEvent.VK_U:
// 'U' Moves Light Away From Viewer
renderer.translateLightBackward(pressed);
break;
// Adjust Object's Position
case KeyEvent.VK_NUMPAD6:
// 'Numpad6' Move Object Right
renderer.translateObjectRight(pressed);
break;
case KeyEvent.VK_NUMPAD4:
// 'Numpad4' Move Object Left
renderer.translateObjectLeft(pressed);
break;
case KeyEvent.VK_NUMPAD8:
renderer.translateObjectUp(pressed); // 'Numpad8' Move Object Up
break;
case KeyEvent.VK_NUMPAD5:
// 'Numpad5' Move Object Down
renderer.translateObjectDown(pressed);
break;
case KeyEvent.VK_NUMPAD9:
// 'Numpad9' Move Object Toward Viewer
renderer.translateObjectForward(pressed);
break;
case KeyEvent.VK_NUMPAD7:
// 'Numpad7' Move Object Away From Viewer
renderer.translateObjectBackward(pressed);
break;
// Adjust Ball's Position
case KeyEvent.VK_D:
renderer.translateSphereRight(pressed); // 'D' Move Ball Right
break;
case KeyEvent.VK_A:
renderer.translateSphereLeft(pressed); // 'A' Move Ball Left
break;
case KeyEvent.VK_W:
renderer.translateSphereUp(pressed); // 'W' Move Ball Up
break;
case KeyEvent.VK_S:
renderer.translateSphereDown(pressed); // 'S' Move Ball Down
break;
case KeyEvent.VK_E:
// 'E' Move Ball Toward Viewer
renderer.translateSphereForward(pressed);
break;
case KeyEvent.VK_Q:
// 'Q' Move Ball Away From Viewer
renderer.translateSphereBackward(pressed);
break;
}
}
}
package demos.nehe.lesson27;
import demos.common.ResourceRetriever;
import javax.media.opengl.GL;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;
class Object3D {
private int nPlanes, nPoints;
private Point points[] = new Point[100];
private Plane planes[] = new Plane[100];
public Object3D() {
for (int i = 0; i < 100; i++) {
points[i] = new Point();
planes[i] = new Plane();
}
}
// load object
public static Object3D readObject(String st) throws IOException {
StringBuffer data = new StringBuffer();
InputStream inputStream = ResourceRetriever.getResourceAsStream(st);
int info;
while ((info = inputStream.read()) != -1)
data.append((char) info);
inputStream.close();
Object3D o = new Object3D();
//points
StringTokenizer tokenizer = new StringTokenizer(data.toString());
o.nPoints = Integer.parseInt(tokenizer.nextToken());
for (int i = 1; i <= o.nPoints; i++) {
o.points[i].x = Float.parseFloat(tokenizer.nextToken());
o.points[i].y = Float.parseFloat(tokenizer.nextToken());
o.points[i].z = Float.parseFloat(tokenizer.nextToken());
}
//planes
o.nPlanes = Integer.parseInt(tokenizer.nextToken());
for (int i = 0; i < o.nPlanes; i++) {
o.planes[i].p[0] = Integer.parseInt(tokenizer.nextToken());
o.planes[i].p[1] = Integer.parseInt(tokenizer.nextToken());
o.planes[i].p[2] = Integer.parseInt(tokenizer.nextToken());
o.planes[i].normals[0].x = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[0].y = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[0].z = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[1].x = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[1].y = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[1].z = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[2].x = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[2].y = Float.parseFloat(tokenizer.nextToken());
o.planes[i].normals[2].z = Float.parseFloat(tokenizer.nextToken());
}
o.setConnectivity(); // Set Face To Face Connectivity
o.calcPlanes();
return o;
}
// connectivity procedure - based on Gamasutra's article
// hard to explain here
public void setConnectivity() {
for (int i = 0; i < nPlanes - 1; i++)
for (int j = i + 1; j < nPlanes; j++)
for (int ki = 0; ki < 3; ki++)
if (planes[i].neigh[ki] == 0) {
for (int kj = 0; kj < 3; kj++) {
int p1i = ki;
int p1j = kj;
int p2i = (ki + 1) % 3;
int p2j = (kj + 1) % 3;
p1i = planes[i].p[p1i];
p2i = planes[i].p[p2i];
p1j = planes[j].p[p1j];
p2j = planes[j].p[p2j];
int P1i = ((p1i + p2i) - Math.abs(p1i - p2i)) / 2;
int P2i = ((p1i + p2i) + Math.abs(p1i - p2i)) / 2;
int P1j = ((p1j + p2j) - Math.abs(p1j - p2j)) / 2;
int P2j = ((p1j + p2j) + Math.abs(p1j - p2j)) / 2;
if ((P1i == P1j) && (P2i == P2j)) { //they are neighbours
planes[i].neigh[ki] = j + 1;
planes[j].neigh[kj] = i + 1;
}
}
}
}
private void calcPlanes() {
for (int i = 0; i < nPlanes; i++) // Loop Through All Object Planes
calcPlane(planes[i]); // Compute Plane Equations For All Faces
}
// function for computing a plane equation given 3 points
private void calcPlane(Plane plane) {
Point v[] = new Point[4];
for (int i = 0; i < 3; i++) {
v[i + 1] = new Point();
v[i + 1].x = points[plane.p[i]].x;
v[i + 1].y = points[plane.p[i]].y;
v[i + 1].z = points[plane.p[i]].z;
}
plane.PlaneEq.a = v[1].y * (v[2].z - v[3].z) + v[2].y * (v[3].z - v[1].z)
+ v[3].y * (v[1].z - v[2].z);
plane.PlaneEq.b = v[1].z * (v[2].x - v[3].x) + v[2].z * (v[3].x - v[1].x)
+ v[3].z * (v[1].x - v[2].x);
plane.PlaneEq.c = v[1].x * (v[2].y - v[3].y) + v[2].x * (v[3].y - v[1].y)
+ v[3].x * (v[1].y - v[2].y);
plane.PlaneEq.d = -(v[1].x * (v[2].y * v[3].z - v[3].y * v[2].z) +
v[2].x * (v[3].y * v[1].z - v[1].y * v[3].z) +
v[3].x * (v[1].y * v[2].z - v[2].y * v[1].z));
}
// procedure for drawing the object - very simple
public void draw(GL gl) {
gl.glBegin(GL.GL_TRIANGLES);
for (int i = 0; i < nPlanes; i++) {
for (int j = 0; j < 3; j++) {
gl.glNormal3f(planes[i].normals[j].x,
planes[i].normals[j].y,
planes[i].normals[j].z);
gl.glVertex3f(points[planes[i].p[j]].x,
points[planes[i].p[j]].y,
points[planes[i].p[j]].z);
}
}
gl.glEnd();
}
public void castShadow(GL gl, float[] lp) {
Point v1 = new Point();
Point v2 = new Point();
//set visual parameter
for (int i = 0; i < nPlanes; i++) {
// chech to see if light is in front or behind the plane (face plane)
float side = planes[i].PlaneEq.a * lp[0] +
planes[i].PlaneEq.b * lp[1] +
planes[i].PlaneEq.c * lp[2] +
planes[i].PlaneEq.d * lp[3];
if (side > 0)
planes[i].visible = true;
else
planes[i].visible = false;
}
gl.glDisable(GL.GL_LIGHTING);
gl.glDepthMask(false);
gl.glDepthFunc(GL.GL_LEQUAL);
gl.glEnable(GL.GL_STENCIL_TEST);
gl.glColorMask(false, false, false, false);
gl.glStencilFunc(GL.GL_ALWAYS, 1, 0xffffffff);
// first pass, stencil operation increases stencil value
gl.glFrontFace(GL.GL_CCW);
gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_INCR);
for (int i = 0; i < nPlanes; i++) {
if (planes[i].visible)
for (int j = 0; j < 3; j++) {
int k = planes[i].neigh[j];
if ((k == 0) || (!planes[k - 1].visible)) {
// here we have an edge, we must draw a polygon
int p1 = planes[i].p[j];
int jj = (j + 1) % 3;
int p2 = planes[i].p[jj];
//calculate the length of the vector
v1.x = (points[p1].x - lp[0]) * 100;
v1.y = (points[p1].y - lp[1]) * 100;
v1.z = (points[p1].z - lp[2]) * 100;
v2.x = (points[p2].x - lp[0]) * | |