import glmodel.*; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.*; // Native IO buffers: LWJGL uses these to efficiently exchange data with system memory import org.lwjgl.opengl.*; import org.lwjgl.input.*; import org.lwjgl.util.glu.*; /** * GLART_8_text.java * * Demonstrates text by drawing characters textured onto quads. OpenGL * does not have any font or text capabilities, but we can create * text by texturing images of text onto quads. * * A texture image (font_tahoma.png) contains a full character set * arranged in order by ascii value. * * The buildFont() function creates a set of quads, each textured * with a character from the character set image. The textured quads * are stored as display lists for faster rendering. * * The glPrint() function draws text by matching each character in * a text string with the correct display list, then running callList() * to draw that character. glPrint() calls setOrthoOn() to set the * projection mode to ortho, and disables the depth test so text draws * on top of other geometry. Some other settings that matter: * * light -- disable lighting (glDisable(GL_LIGHTING)) * texture -- be sure to glEnable(GL_TEXTURE_2D) * blend -- enable blending so background of text will be transparent * glEnable(GL_BLEND) * glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) */ public class GLART_8_text { private boolean done = false; private final String windowTitle = "Text drawn over a scene"; private DisplayMode displayMode; private float rotation = 0; private GL_Mesh obj; // color and position of light source float lightDiffuse[] = { .7f, .7f, .65f, 1f }; // direct light float lightSpecular[] = { .7f, .7f, .65f, 1f }; // highlight float lightAmbient[] = { .7f, .7f, .7f, 1f }; // scattered light float lightPosition[] = { -4f, 4f, 6, 0f }; // For Text: character set display list "base" and Font texture int fontListBase = -1; // Base Display List For The character set int fontTextureHandle = -1; // Texture handle for character set image /** * Main function just creates and runs the application. */ public static void main(String args[]) { GLART_8_text app = new GLART_8_text(); app.run(); } /** * Initialize the app, then sit in a render loop until done==true. */ public void run() { try { init(); while (!done) { mainloop(); render(); Display.update(); } cleanup(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } } /** * Initialize the environment * @throws Exception */ private void init() throws Exception { initDisplay(); initGL(); // Load the Teapot mesh GL_3DS_Importer import3DS = new GL_3DS_Importer(); InputStream in3ds = getInputStream("models/teapotT.3DS"); obj = import3DS.importFromStream(in3ds); } /** * Create an OpenGL display, in this case a fullscreen window. * @throws Exception */ private void initDisplay() throws Exception { // get all possible display resolutions DisplayMode d[] = Display.getAvailableDisplayModes(); // find a resolution we like for (int i = 0; i < d.length; i++) { if (d[i].getWidth() == 800 && d[i].getHeight() == 600 && d[i].getBitsPerPixel() == 32) { displayMode = d[i]; break; } } // set the display to the resolution we picked Display.setDisplayMode(displayMode); Display.setTitle(windowTitle); Display.setFullscreen(false); // create the window Display.create(); System.out.println("GLApp.initDisplay(): Current display mode is " + displayMode); } /** * Initialize OpenGL * */ private void initGL() { // Select the Projection Matrix (controls perspective) GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glLoadIdentity(); // Reset The Projection Matrix // Define perspective GLU.gluPerspective( 45.0f, // Field Of View (float)displayMode.getWidth() / (float)displayMode.getHeight(), // aspect ratio 0.1f, // near Z clipping plane 1000.0f); // far Z clipping plane // Select The Modelview Matrix (controls model orientation) GL11.glMatrixMode(GL11.GL_MODELVIEW); // make sure OpenGL correctly layers objects GL11.glEnable(GL11.GL_DEPTH_TEST); // turn lighting on (does not create a light) GL11.glEnable(GL11.GL_LIGHTING); // Create a light setLight( GL11.GL_LIGHT1, lightDiffuse, lightAmbient, lightSpecular, lightPosition ); // set the background color GL11.glClearColor(0f, 0f, 0f, 1); //=========================================================== // For text rendering //=========================================================== // Enable blending so that text background is transparent GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); // Turn texturing on (text is a texture so we need this on) GL11.glEnable(GL11.GL_TEXTURE_2D); // prepare character set for text rendering buildFont("images/font_tahoma.png", 12); } /** * Handle keyboard input. Just check for escape key or user * clicking to close the window. */ private void mainloop() { if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) { // Escape is pressed done = true; } if(Display.isCloseRequested()) { // Window is closed done = true; } } /** * Render the scene. */ private void render() { rotation += .1f; // Clear screen and depth buffer GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); // Select The Modelview Matrix (controls model orientation) GL11.glMatrixMode(GL11.GL_MODELVIEW); // reset the coordinate system to center of screen GL11.glLoadIdentity(); // Where is the 'eye' GLU.gluLookAt( 0f, 4f, 6f, // eye position 0f, 2.5f, 0f, // target to look at 0f, 1f, 0f); // which way is up // shift back GL11.glTranslatef(0,0,-4); // rotate scene GL11.glRotatef(rotation, 0,1,0); // draw the mesh renderMesh(obj,0); GL11.glDisable(GL11.GL_LIGHTING); // Font color GL11.glColor4f(1,1,1,1); // write some text glPrint(20,20,0,"The model contains " + obj.numTriangles + " triangles"); GL11.glEnable(GL11.GL_LIGHTING); } float tileFactorHoriz = 1f; float tileFactorVert = 1f; /** * Render mesh with normals and texture coordinates. Loops through * all triangles in the mesh object. * * Several triangles may refer to the same vertex, but each face * can have different normals for that vertex. This allows for * sharp edges between faces. * * @param o mesh object to render */ public void renderMesh(GL_Mesh o, int textureHandle) { GL_Triangle t; GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle); GL11.glBegin(GL11.GL_TRIANGLES); for (int j = 0; j < o.triangles.length; j++) { // draw all triangles in object t = o.triangles[j]; GL11.glTexCoord2f(tileFactorHoriz*t.uvw1.x, tileFactorVert*t.uvw1.y); GL11.glNormal3f(t.norm1.x, t.norm1.y, t.norm1.z); GL11.glVertex3f( (float)t.p1.pos.x, (float)t.p1.pos.y, (float)t.p1.pos.z); GL11.glTexCoord2f(tileFactorHoriz*t.uvw2.x, tileFactorVert*t.uvw2.y); GL11.glNormal3f(t.norm2.x, t.norm2.y, t.norm2.z); GL11.glVertex3f( (float)t.p2.pos.x, (float)t.p2.pos.y, (float)t.p2.pos.z); GL11.glTexCoord2f(tileFactorHoriz*t.uvw3.x, tileFactorVert*t.uvw3.y); GL11.glNormal3f(t.norm3.x, t.norm3.y, t.norm3.z); GL11.glVertex3f( (float)t.p3.pos.x, (float)t.p3.pos.y, (float)t.p3.pos.z); } GL11.glEnd(); } /** * Cleanup all the resources. * */ private void cleanup() { // clean up opengl context Display.destroy(); destroyFont(); } /** * Simple way to setup a light. Uses same color for direct light (diffuse), * reflected highlight (specular) and scattered light (ambient). Ambient * color is darkened to 1/4 of the light color. * @param GLLightHandle * @param color * @param position */ public static void setLight(int GLLightHandle, float[] color, float[] position ) { float[] ambientLight = {color[0]/4f, color[1]/4f, color[2]/4f, color[3]/4f}; FloatBuffer lightColor = allocFloats(color); FloatBuffer ambientColor = allocFloats(ambientLight); FloatBuffer ltPosition = allocFloats(position); GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, lightColor); // color of the direct illumination GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, lightColor); // color of the highlight (same as direct light) GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ambientColor); // color of the scattered light (darker) GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition); GL11.glEnable(GLLightHandle); // Enable the light (GL_LIGHT1 - 7) } /** * Set the color of a 'positional' light (a light that has a specific * position within the scene).
*
* Params:
* an OpenGL light number (GL11.GL_LIGHT1),
* 'Diffuse': color of direct light from this source,
* 'Ambient': color of scattered light from this source
* 'Specular': color of this light reflected off a surface,
* position.
*/ public static void setLight( int GLLightHandle, float[] diffuseLightColor, float[] ambientLightColor, float[] specularLightColor, float[] position ) { FloatBuffer ltDiffuse = allocFloats(diffuseLightColor); FloatBuffer ltAmbient = allocFloats(ambientLightColor); FloatBuffer ltSpecular = allocFloats(specularLightColor); FloatBuffer ltPosition = allocFloats(position); GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, ltDiffuse); // color of the direct illumination GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ltAmbient); // color of the reflected light GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, ltSpecular); // color of the highlight (same as direct light) GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition); GL11.glEnable(GLLightHandle); // Enable the light (GL_LIGHT1 - 7) //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .005F); // how light beam drops off } /** * Set the color of the Global Ambient Light. Affects all objects in * scene regardless of their placement. */ public static void setAmbientLight(float[] ambientLightColor) { FloatBuffer ltAmbient = allocFloats(ambientLightColor); GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, ltAmbient); } public static final int SIZE_FLOAT = 4; public static FloatBuffer allocFloats(int howmany) { return ByteBuffer.allocateDirect(howmany * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer(); } public static FloatBuffer allocFloats(float[] floatarray) { FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer(); fb.put(floatarray).flip(); return fb; } //======================================================================== // Functions to build a character set and draw text strings. // // Example: // buildFont("Font_tahoma.png"); // ... // glPrint(100, 100, 0, "Here's some text"); // ... // destroyFont(); // cleanup //======================================================================== /** * Build a character set from the given texture image. * * @param charSetImage texture image containing 256 characters in a 16x16 grid * @param fontWidth how many pixels to allow per character on screen * * @see destroyFont() */ public void buildFont(String charSetImage, int fontWidth) { // make texture from image GLImage textureImg = new GLImage(charSetImage); fontTextureHandle = makeTexture(textureImg); // build character set as call list of 256 textured quads buildFont(fontTextureHandle, fontWidth); } /** * Build the character set display list from the given texture. Creates * one quad for each character, with one letter textured onto each quad. * Assumes the texture is a 256x256 image containing every * character of the charset arranged in a 16x16 grid. Each character * is 16x16 pixels. Call destroyFont() to release the display list memory. * * Should be in ORTHO (2D) mode to render text (see setOrtho()). * * Special thanks to NeHe and Giuseppe D'Agata for the "2D Texture Font" * tutorial (http://nehe.gamedev.net). * * @param charSetImage texture image containing 256 characters in a 16x16 grid * @param fontWidth how many pixels to allow per character on screen * * @see destroyFont() */ public void buildFont(int fontTxtrHandle, int fontWidth) { float cx, cy; fontListBase = GL11.glGenLists(256); // Creating 256 Display Lists for (int i = 0; i < 256; i++) { cx = (float) (i % 16) / 16f; // X Texture Coord Of Character (0 - 1.0) cy = (float) (i / 16) / 16f; // Y Texture Coord Of Character (0 - 1.0) GL11.glNewList(fontListBase + i, GL11.GL_COMPILE); // Start Building A List GL11.glBegin(GL11.GL_QUADS); // Use A 16x16 pixel Quad For Each Character GL11.glTexCoord2f(cx, 1 - cy - 0.0625f); // Texture Coord (Bottom Left) GL11.glVertex2i(0, 0); GL11.glTexCoord2f(cx + 0.0625f, 1 - cy - 0.0625f); // Texture Coord (Bottom Right) GL11.glVertex2i(16, 0); GL11.glTexCoord2f(cx + 0.0625f, 1 - cy); // Texture Coord (Top Right) GL11.glVertex2i(16, 16); GL11.glTexCoord2f(cx, 1 - cy); // Texture Coord (Top Left) GL11.glVertex2i(0, 16); GL11.glEnd(); // Done Building Our Quad (Character) GL11.glTranslatef(fontWidth, 0, 0); // Move To The Right Of The Character GL11.glEndList(); // Done Building The Display List } // Loop Until All 256 Are Built } /** * Clean up the allocated display lists for the character set. */ public void destroyFont() { if (fontListBase != -1) { GL11.glDeleteLists(fontListBase,256); fontListBase = -1; } } /** * Render a text string onto the screen, using the character set created * by buildCharSet(). */ public void glPrint(int x, int y, int set, String msg) { int offset = fontListBase - 32 + (128 * set); if (fontListBase == -1 || fontTextureHandle == -1) { System.out.println("glPrint(): character set has not been created -- run buildFont() first"); return; } if (msg != null) { // enable the charset texture GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle); // prepare to render in 2D setOrthoOn(); // draw the text GL11.glTranslatef(x, y, 0); // Position The Text (in pixels coords) for(int i=0; i