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