// This code example is created for educational purpose // by Thorsten Thormaehlen (contact: www.thormae.de). // It is distributed without any warranty. import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Random; import javax.imageio.ImageIO; import com.jogamp.opengl.GL2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.awt.GLCanvas; import com.jogamp.opengl.glu.GLU; import javax.swing.JFrame; import com.jogamp.opengl.util.FPSAnimator; class Renderer { private GLU glu = new GLU(); public float t = 0.0f; private int texID = 0; private float[] terrain; public void init(GLAutoDrawable d) { GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context gl.glEnable(GL2.GL_DEPTH_TEST); String[] filenames = new String[6]; filenames[0] = "deep_water.png"; filenames[1] = "shallow_water.png"; filenames[2] = "shore.png"; filenames[3] = "fields.png"; filenames[4] = "rocks.png"; filenames[5] = "snow.png"; texID = loadTexture3D(d, filenames); rebuildTerrain(); } public void resize(GLAutoDrawable d, int w, int h) { GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context gl.glViewport(0, 0, w, h); gl.glMatrixMode(GL2.GL_PROJECTION); gl.glLoadIdentity(); glu.gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0); } public void display(GLAutoDrawable d) { GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL2.GL_MODELVIEW); gl.glLoadIdentity(); // set camera glu.gluLookAt(1.5, -1.0, 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); // draw scene gl.glRotatef(t, 0.0f, 0.0f, 1.0f); drawTerrain(d); } public void rebuildTerrain() { //create random values int dim = 40; terrain = new float[dim*dim]; Random generator = new Random(); for(int r=0; r < dim*dim; r++) { terrain[r] = generator.nextFloat(); } if(true) { // generate smooth terrain values float[] smoothTerrain = new float[dim*dim]; for(int k=0; k < 5; k++){ float maxVal = 0.0f; float minVal = 1.0f; for(int x = 0; x < dim; x++) { for(int y = 0; y < dim; y++) { if(x == 0 || x == dim-1) terrain[x*dim+y] = 0.0f; else if (y == 0 || y == dim-1) terrain[x*dim+y] = 0.0f; else { float a = 0.0f; int counter = 0; for(int s=-1; s <= 1; s++) { for(int r=-1; r <= 1; r++) { a += terrain[(x+s)*dim+(y+r)]; counter++; } } float val = a / (float)counter; smoothTerrain[x*dim+y] = val; if(val > maxVal) maxVal = val; if(val < minVal) minVal = val; } } } for(int r=0; r < dim*dim; r++) terrain[r] = (smoothTerrain[r] - minVal) / (maxVal-minVal); } } } private void drawTerrain(GLAutoDrawable d) { GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context gl.glEnable(GL2.GL_TEXTURE_3D); gl.glBindTexture(GL2.GL_TEXTURE_3D, texID); gl.glColor3f(1.0f,0.0f,0.0f); int dim = (int)Math.sqrt(terrain.length); float maxHeight = 0.2f; float texHeight = 0.9f; for(int x = 1; x < dim; x++) { for(int y = 1; y < dim; y++) { gl.glBegin(GL2.GL_POLYGON); gl.glTexCoord3f((float)(x-1)/(float)(dim), (float)(y-1)/(float)(dim), terrain[(x-1)*dim+(y-1)]*texHeight); gl.glVertex3f((float)(x-1)/(float)(dim)-0.5f, (float)(y-1)/(float)(dim)-0.5f, terrain[(x-1)*dim+(y-1)]*maxHeight); gl.glTexCoord3f((float)(x)/(float)(dim), (float)(y-1)/(float)(dim), terrain[x*dim+(y-1)]*texHeight); gl.glVertex3f((float)(x)/(float)(dim)-0.5f, (float)(y-1)/(float)(dim)-0.5f, terrain[x*dim+(y-1)]*maxHeight); gl.glTexCoord3f((float)(x)/(float)(dim), (float)(y)/(float)(dim), terrain[x*dim+y]*texHeight); gl.glVertex3f((float)(x)/(float)(dim)-0.5f, (float)(y)/(float)(dim)-0.5f, terrain[x*dim+y]*maxHeight); gl.glTexCoord3f((float)(x-1)/(float)(dim), (float)(y)/(float)(dim), terrain[(x-1)*dim+y]*texHeight); gl.glVertex3f((float)(x-1)/(float)(dim)-0.5f, (float)(y)/(float)(dim)-0.5f, terrain[(x-1)*dim+y]*maxHeight); gl.glEnd(); } } gl.glDisable(GL2.GL_TEXTURE_3D); } // returns a valid textureID on success, otherwise 0 private int loadTexture3D(GLAutoDrawable d, String[] filenames) { GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context int width = 0; int height = 0; int depth = filenames.length; int level = 0; int border = 0; ByteBuffer buffer = null; // pack 2D images subsequently into a large 3D buffer for(int i=0; i < depth; i++) { try{ // open file FileInputStream fileInputStream = new FileInputStream(new File(filenames[i])); // read image BufferedImage bufferedImage = ImageIO.read(fileInputStream); fileInputStream.close(); width = bufferedImage.getWidth(); height = bufferedImage.getHeight(); // convert image to ByteBuffer int[] pixelIntData = new int[width * height]; bufferedImage.getRGB(0, 0, width, height, pixelIntData, 0, width); if(i==0) { //allocate memory buffer = ByteBuffer.allocateDirect(pixelIntData.length * 4 * depth); buffer.order(ByteOrder.nativeOrder()); } for(int k=0; k < pixelIntData.length; k++) { buffer.put((byte)(pixelIntData[k]>>> 16)); buffer.put((byte)(pixelIntData[k]>>> 8)); buffer.put((byte)pixelIntData[k]); buffer.put((byte)(pixelIntData[k]>>> 24)); } } catch( FileNotFoundException e) { System.out.println("Can not find texture data file " + filenames[i]); } catch(IOException e) { e.printStackTrace( ); } } if(buffer != null && width > 0 && height > 0) { buffer.rewind(); // data is aligned in byte order gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1); //request textureID final int[] textureID = new int[1]; gl.glGenTextures( 1, textureID, 0); // bind texture gl.glBindTexture(GL2.GL_TEXTURE_3D, textureID[0]); //parameters the define how to warp the texture gl.glTexParameteri(GL2.GL_TEXTURE_3D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_BORDER); gl.glTexParameteri(GL2.GL_TEXTURE_3D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_BORDER); gl.glTexParameteri(GL2.GL_TEXTURE_3D, GL2.GL_TEXTURE_WRAP_R, GL2.GL_CLAMP_TO_BORDER); float[] borderColor = {0.0f,0.0f,0.0f,1.0f}; gl.glTexParameterfv(GL2.GL_TEXTURE_3D, GL2.GL_TEXTURE_BORDER_COLOR, borderColor, 0); //define how to filter the texture gl.glTexParameteri (GL2.GL_TEXTURE_3D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR); gl.glTexParameteri (GL2.GL_TEXTURE_3D,GL2. GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR); //texture colors should replace the original color values gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE); //GL_MODULATE // specify the 2D texture map gl.glTexImage3D(GL2.GL_TEXTURE_3D, level, GL2.GL_RGB, width, height, depth, border, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, buffer); return textureID[0]; } return 0; } } class MyGui extends JFrame implements GLEventListener { private Renderer renderer; public void createGUI() { setTitle("Press 1 to generate a new random terrain"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); GLProfile glp = GLProfile.getDefault(); GLCapabilities caps = new GLCapabilities(glp); GLCanvas canvas = new GLCanvas(caps); setSize(320, 320); getContentPane().add(canvas); final FPSAnimator ani = new FPSAnimator(canvas, 60, true); canvas.addGLEventListener(this); setVisible(true); renderer = new Renderer(); canvas.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent event) { boolean redraw = false; switch(event.getKeyCode()) { case '1': renderer.rebuildTerrain(); redraw = true; break; } if(redraw) { // done automatically } } }); ani.start(); } @Override public void init(GLAutoDrawable d) { renderer.init(d); } @Override public void reshape(GLAutoDrawable d, int x, int y, int width, int height) { renderer.resize(d, width, height); } @Override public void display(GLAutoDrawable d) { float offset = 1.0f; renderer.t += offset; renderer.display(d); } @Override public void dispose(GLAutoDrawable d) { } } public class Texture3D { public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { MyGui myGUI = new MyGui(); myGUI.createGUI(); } }); } }