|
This example shows how to create bezier patches.
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.lesson28;
import demos.common.GLDisplay;
public class Lesson28 {
public static void main(String[] args) {
GLDisplay neheGLDisplay = GLDisplay.createGLDisplay("Lesson 28: Bezier patches");
Renderer renderer = new Renderer();
InputHandler inputHandler = new InputHandler(renderer, neheGLDisplay);
neheGLDisplay.addGLEventListener(renderer);
neheGLDisplay.addKeyListener(inputHandler);
neheGLDisplay.start();
}
}
package demos.nehe.lesson28;
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 display) {
this.renderer = renderer;
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Increase # subdivisions");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Decrease # subdivisions");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
"Toggle control point display");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "Rotate left");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "Rotate right");
}
public void keyPressed(KeyEvent e) {
// Set flags
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
renderer.increaseDivisions();
break;
case KeyEvent.VK_DOWN:
renderer.decreaseDivisions();
break;
case KeyEvent.VK_SPACE:
renderer.toggleControlPoints();
break;
default:
processKeyEvent(e, true);
}
}
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
default:
// Unset flags
processKeyEvent(e, false);
}
}
private void processKeyEvent(KeyEvent e, boolean pressed) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
renderer.rotateLeft(pressed);
break;
case KeyEvent.VK_RIGHT:
renderer.rotateRight(pressed);
break;
}
}
}
package demos.nehe.lesson28;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import demos.common.TextureReader;
import java.io.IOException;
class Renderer implements GLEventListener {
private float rotz = 0.0f; // Rotation about the Z axis
private boolean rotateLeft, rotateRight;
// The bezier patch we're going to use (NEW)
private BezierPatch mybezier = new BezierPatch();
// Toggles displaying the control point grid (NEW)
private boolean showCPoints = true;
// Number of intrapolations (conrols poly resolution) (NEW)
private int divs = 7;
private GLU glu = new GLU();
public void increaseDivisions() {
divs++;
}
public void decreaseDivisions() {
divs = Math.max(1, divs - 1);
}
public void toggleControlPoints() {
showCPoints = !showCPoints;
}
public void rotateLeft(boolean rotateLeft) {
this.rotateLeft = rotateLeft;
}
public void rotateRight(boolean rotateRight) {
this.rotateRight = rotateRight;
}
private void initBezier(BezierPatch bezier) {
bezier.anchors[0][0] = new Point3D(-0.75, -0.75, -0.5); // set the bezier vertices
bezier.anchors[0][1] = new Point3D(-0.25, -0.75, 0.0);
bezier.anchors[0][2] = new Point3D(0.25, -0.75, 0.0);
bezier.anchors[0][3] = new Point3D(0.75, -0.75, -0.5);
bezier.anchors[1][0] = new Point3D(-0.75, -0.25, -0.75);
bezier.anchors[1][1] = new Point3D(-0.25, -0.25, 0.5);
bezier.anchors[1][2] = new Point3D(0.25, -0.25, 0.5);
bezier.anchors[1][3] = new Point3D(0.75, -0.25, -0.75);
bezier.anchors[2][0] = new Point3D(-0.75, 0.25, 0.0);
bezier.anchors[2][1] = new Point3D(-0.25, 0.25, -0.5);
bezier.anchors[2][2] = new Point3D(0.25, 0.25, -0.5);
bezier.anchors[2][3] = new Point3D(0.75, 0.25, 0.0);
bezier.anchors[3][0] = new Point3D(-0.75, 0.75, -0.5);
bezier.anchors[3][1] = new Point3D(-0.25, 0.75, -1.0);
bezier.anchors[3][2] = new Point3D(0.25, 0.75, -1.0);
bezier.anchors[3][3] = new Point3D(0.75, 0.75, -0.5);
bezier.dlBPatch = 0;
}
private void loadGLTexture(GL gl, String name, int[] textures)
throws IOException {
TextureReader.Texture texture = TextureReader.readTexture(name);
gl.glGenTextures(1, textures, 0); // Generate 1 texture
// Typical Texture Generation Using Data From The Bitmap
gl.glBindTexture(GL.GL_TEXTURE_2D, textures[0]);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, 3, texture.getWidth(),
texture.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE,
texture.getPixels());
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);
}
public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
gl.glEnable(GL.GL_TEXTURE_2D); // Enable Texture Mapping
gl.glShadeModel(GL.GL_SMOOTH); // Enable Smooth Shading
gl.glClearColor(0.05f, 0.05f, 0.05f, 0.5f); // Black Background
gl.glClearDepth(1.0f); // Depth Buffer Setup
gl.glEnable(GL.GL_DEPTH_TEST); // Enables Depth Testing
gl.glDepthFunc(GL.GL_LEQUAL); // The Type Of Depth Testing To Do
// Really Nice Perspective Calculations
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
int[] texture = new int[1];
try {
// Load the texture
loadGLTexture(gl, "demos/data/images/NeHe.png", texture);
} catch (IOException e) {
throw new RuntimeException(e);
}
mybezier.setTexture(texture[0]);
initBezier(mybezier); // Initialize the Bezier's control grid
}
public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
drawGLScene(gl);
update();
}
private void drawGLScene(GL gl) {
// Clear Screen And Depth Buffer
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); // Reset The Current Modelview Matrix
// Move Left 1.5 Units And Into The Screen 6.0
gl.glTranslatef(0.0f, 0.0f, -4.0f);
gl.glRotatef(-75.0f, 1.0f, 0.0f, 0.0f);
// Rotate The Triangle On The Z axis ( NEW )
gl.glRotatef(rotz, 0.0f, 0.0f, 1.0f);
mybezier.drawBezier(gl, divs);
if (showCPoints) { // If drawing the grid is toggled on
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glColor3f(1.0f, 0.0f, 0.0f);
for (int i = 0; i < 4; i++) { // draw the horizontal lines
gl.glBegin(GL.GL_LINE_STRIP);
for (int j = 0; j < 4; j++)
gl.glVertex3d(mybezier.anchors[i][j].x, mybezier.anchors[i][j].y,
mybezier.anchors[i][j].z);
gl.glEnd();
}
for (int i = 0; i < 4; i++) { // draw the vertical lines
gl.glBegin(GL.GL_LINE_STRIP);
for (int j = 0; j < 4; j++)
gl.glVertex3d(mybezier.anchors[j][i].x, mybezier.anchors[j][i].y,
mybezier.anchors[j][i].z);
gl.glEnd();
}
gl.glColor3f(1.0f, 1.0f, 1.0f);
gl.glEnable(GL.GL_TEXTURE_2D);
}
}
private void update() {
if (rotateLeft)
rotz -= 0.8f;
if (rotateRight)
rotz += 0.8f;
}
public void reshape(GLAutoDrawable drawable,
int xstart,
int ystart,
int width,
int height) {
GL gl = drawable.getGL();
height = (height == 0) ? 1 : height;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(45, (float) width / height, 1, 1000);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void displayChanged(GLAutoDrawable drawable,
boolean modeChanged,
boolean deviceChanged) {
}
private static class Point3D { // Structure for a 3-dimensional point (NEW)
public double x, y, z;
public Point3D() {
}
public Point3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Point3D add(Point3D q) {
return new Point3D(x + q.x, y + q.y, z + q.z);
}
public Point3D scale(double c) {
return new Point3D(x * c, y * c, z * c);
}
/**
* Calculates 3rd degree polynomial based on array of 4 points
* and a single variable (u) which is generally between 0 and 1
*/
public static Point3D bernstein(float u, Point3D[] p) {
Point3D a = p[0].scale(Math.pow(u, 3));
Point3D b = p[1].scale(3 * Math.pow(u, 2) * (1 - u));
Point3D c = p[2].scale(3 * u * Math.pow((1 - u), 2));
Point3D d = p[3].scale(Math.pow((1 - u), 3));
return a.add(b).add(c).add(d);
}
}
private static class BezierPatch { // Structure for a 3rd degree bezier patch (NEW)
public Point3D[][] anchors; // 4x4 grid of anchor points
private int dlBPatch; // Display List for Bezier Patch
private int[] texture = new int[1]; // Texture for the patch
private int divs = -1;
public BezierPatch() {
anchors = new Point3D[4][4];
for (int i = 0; i < anchors.length; i++) {
Point3D[] anchor = anchors[i];
for (int j = 0; j < anchor.length; j++) {
anchor[j] = new Point3D();
}
}
}
public void setTexture(int texture) {
this.texture[0] = texture;
}
public void drawBezier(GL gl, int divs) {
if (divs != this.divs)
dlBPatch = genBezier(gl, divs);
gl.glCallList(dlBPatch); // Call the Bezier's display list
}
/**
* Generates a display list based on the data in the patch
* and the number of divisions
*/
private int genBezier(GL gl, int divs) {
float py, px, pyold;
int drawlist = gl.glGenLists(1); // make the display list
Point3D[] temp = new Point3D[4];
Point3D[] last = new Point3D[divs + 1];
// array of points to mark the first line of polys
if (dlBPatch != 0) // get rid of any old display lists
gl.glDeleteLists(dlBPatch, 1);
temp[0] = anchors[0][3]; // the first derived curve (along x axis)
temp[1] = anchors[1][3];
temp[2] = anchors[2][3];
temp[3] = anchors[3][3];
for (int v = 0; v <= divs; v++) { // create the first line of points
px = v / ((float) divs); // percent along y axis
// use the 4 points from the derives curve to calculate the points
// along that curve
last[v] = Point3D.bernstein(px, temp);
}
gl.glNewList(drawlist, GL.GL_COMPILE); // Start a new display list
gl.glBindTexture(GL.GL_TEXTURE_2D, texture[0]); // Bind the texture
for (int u = 1; u <= divs; u++) {
py = ((float) u) / ((float) divs); // Percent along Y axis
pyold = ((float) u - 1.0f) / ((float) divs); // Percent along old Y axis
temp[0] = Point3D.bernstein(py, anchors[0]); // Calculate new bezier points
temp[1] = Point3D.bernstein(py, anchors[1]);
temp[2] = Point3D.bernstein(py, anchors[2]);
temp[3] = Point3D.bernstein(py, anchors[3]);
gl.glBegin(GL.GL_TRIANGLE_STRIP); // Begin a new triangle strip
for (int v = 0; v <= divs; v++) {
px = ((float) v) / ((float) divs); // Percent along the X axis
gl.glTexCoord2f(pyold, px); // Apply the old texture coords
gl.glVertex3d(last[v].x, last[v].y, last[v].z); // Old Point
last[v] = Point3D.bernstein(px, temp); // Generate new point
gl.glTexCoord2f(py, px); // Apply the new texture coords
gl.glVertex3d(last[v].x, last[v].y, last[v].z); // New Point
}
gl.glEnd(); // END the triangle srip
}
gl.glEndList(); // END the list
return drawlist; // Return the display list
}
}
}
|
Related Tips
|