Drawing 2D Text and Tessellation

Please help,

I have a two part question -> not related but I wanted to only place one posting. I hope someone can help.

The first question (probably the easiest):
What is the quickest way to display a String/text to the screen with OpenGL (using GLUT)? I want the text to be stationary (global) and not move when the user rotates the rendered image.

The second question:
Currently I am tessellating a non-convex polygon but I don’t know how to place the correct normal for the lighting of the tessellated result. Can anybody tell me how this is done?

Below is a snippet of my code (in Java JOGL but the idea should carry):

tessellCallBack tessCallback = new tessellCallBack(gl, glu);
GLUtessellator tobj = glu.gluNewTess();
glu.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, tessCallback);
glu.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, tessCallback);
glu.gluTessCallback(tobj, GLU.GLU_TESS_END, tessCallback);
glu.gluTessCallback(tobj, GLU.GLU_TESS_ERROR, tessCallback);

glu.gluTessCallback(tobj, GLU.GLU_TESS_COMBINE, tessCallback);

...
// Where faces is a Vector of Face Objects
// that contain X, Y, Z and normal[3] 
for (int i = 0; i < faces.size(); i++) {
    double[][] outPts = face.getOutPtsArray();
    // Get the NORMAL for this Shape (FACE)
    norm = face.getNormal();
    gl.glNormal3f(norm[0], norm[1], norm[2]);
    glu.gluTessBeginPolygon(tobj, null);
    glu.gluTessBeginContour(tobj);
    for (int j = 0; j < outPts.length; j++) {
	glu.gluTessVertex(tobj, outPts[j], 0, outPts[j]);
    }// End of INNER FOR-LOOP
    glu.gluTessEndContour(tobj);
    glu.gluTessEndPolygon(tobj);
}// End of OUTER FOR-LOOP
glu.gluDeleteTess(tobj);
...
// Inner Class Implementing Callbacks for Tessellation
class tessellCallBack implements GLUtessellatorCallback {
  // I believe the problem is here since this is
  // called when vertices intersect but am unsure
  public void combine(double[] coords, Object[] data, float[] weight, Object[] outData) {
    double[] vertex = new double[3];
    vertex[0] = coords[0];
    vertex[1] = coords[1];
    vertex[2] = coords[2];outData[0] = vertex;
  }// End of combine()
}// End of Inner Tessellation Class

Thanks to anyone with any help/hints.

Originally posted by Richard145:
What is the quickest way to display a String/text to the screen with OpenGL (using GLUT)? I want the text to be stationary (global) and not move when the user rotates the rendered image.

Reset modelview matrix and set the projection matrix to orthographic with glOrtho() or gluOrtho2D(), then draw text with glutBitmapCharacter().


Currently I am tessellating a non-convex polygon but I don’t know how to place the correct normal for the lighting of the tessellated result. Can anybody tell me how this is done?

OpenGL tessellator assumes the vertices of a non-convex polygon are on the same plane (coplanar). Therefore, the new intersect vertices which are produced by tessellator have same normal as input non-convex polygon.

I am not sure that your question is to change manually the normal of input polygon. For this case, you can explicitly define the surface normal by using gluTessNormal(). The default normal value in tessellator is (0,0,0), which means tessellator will compute the normal from the given vertices and winding.

If you simply want to know the normal of the intersect vertex and you don’t know the normal of input polygon, then calculate the normal yourself in your combine callback. The second parameter of combine callback contains the coords of 4 neighbour vertices around the intersect vertex. Do cross-product with these 4 vertices (3 is enough).

Thank you for the reply, songho.

I tried the suggestion but now I get no image. Below is a copy of my code. I would like for the text to stay stationary regardless of the movement the displayed model is yielding, such as rotations.

public void display() {
    ...
    String text = "Hello World!";
    renderText(text, -5f, 0f);
}
...
private void renderText(String text, float x, float y) {
  glColor3f(1.0f, 0.0f, 0.0f);
  glRasterPos2f(x, y);
  glutBitmapString(GLUT.BITMAP_HELVETICA_18, text);
}

Can anyone help me?

Thank you.

Richard145,
Make sure you have reset modelview matrix and switch the projection matrix to orthogonal before drawing a text. And restore the previous projection and modelview matrix after drawing the text.

Thank you songho.

However, I am still not seeing anything. I will look further into it maybe it is something very simple that I am overlooking.

When I’m in doubt, I usually give the API documentation a good going over, just to be sure I didn’t miss a simple–yet easy to miss–detail.

Richard145,
I don’t see any matrix reconfigurations in your renderText() or display(). Could you post whole blocks of display() and renderText() function?

Okay.

public void display(GLAutoDrawable drawable) {
    GL gl = drawable.getGL();
    GLU glu = new GLU();

    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
    // Get the Radius and diameter for the STEP Model(s)
    double radius = this.stpObj.getBSphereRadius();
    double diameter = 2*radius;
// Calculate the zNear and zFar values for depth rendering
    double zNear = 1.0;
    double zFar = zNear + diameter;
    // Get the Center X, Y, and Z Values for the STEP Model(s)
    float cenX = this.stpObj.getCenterX();
    float cenY = this.stpObj.getCenterY();
    float cenZ = this.stpObj.getCenterZ();

    gl.glViewport(0, 0, this.WIDTH, this.HEIGHT);
    gl.glMatrixMode(GL.GL_PROJECTION);
    gl.glLoadIdentity();
    glu.gluPerspective(45.0f, 1.0f*this.WIDTH/this.HEIGHT, zNear, 2.0*zFar);
    // Put the Model(s) in the MIDDLE of the Viewing Volume
    gl.glTranslatef(0.0f, 0.0f, -(float)diameter/2.0f);
    gl.glMatrixMode(GL.GL_MODELVIEW);
    gl.glLoadIdentity();
    glu.gluLookAt(0.0f, 0.0f, diameter, cenX, cenY, cenZ, 0.0f, 1.0f, 0.0f);

    // Rotate around the X, Y, and Z axes
    gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f);
    gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f);
    gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f);

    // Draw the Model
    this.stpObj.draw(gl);

    // Call method to render the fileName to the current Screen
    renderFileName(inFile, gl, -5f, 0f);

    gl.glFlush();
}

For the renderFileName():

private void renderFileName(String fileName, GL gl, float x, float y) {
    GLUT glut = new GLUT();
    gl.glColor3f(1.0f, 0.0f, 0.0f);
    gl.glRasterPos2f(x, y);
    glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18, fileName);
}

The stpObj is a Class that contains all the data needed to render the Model and is read from an ASCII file. When I run the previous code, I can get the fileName to show but it rotates when I move the Model and I would like it to be stationary despite the Model movement. Also, I am using Java JNI OpenGL Bindings (JOGL) but the correlation is pretty much one-to-one for the functions used.

Once again, thank you for all your help.

Richard145,
I modified your renderFileName() a little bit, so, try the following code.

private void renderFileName(String fileName, GL gl, float x, float y) {
    // backup current modelview matrix
    gl.glPushMatrix();
    // reset modelview matrix
    gl.glLoadIdentity();

    // set to 2D orthogonal projection =======
    // switch to projection matrix
    gl.glMatrixMode(GL.GL_PROJECTION);
    // backup projection matrix
    gl.glPushMatrix();
    // reset projection matrix
    gl.glLoadIdentity();
    // set to orthogonal projection
    gl.glOrtho(0, this.WIDTH, 0, this.HEIGHT, -1.0, 1.0);

    // draw the text ============================
    GLUT glut = new GLUT();
    gl.glColor3f(1.0f, 0.0f, 0.0f);
    gl.glRasterPos2f(0, 0);
    glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18, fileName);
    //===========================================

    // restore previous projection matrix
    gl.glPopMatrix();

    // restore previous modelview matrix
    gl.glMatrixMode(GL.GL_MODELVIEW);
    gl.glPopMatrix();
}

Notice that I used glRasterPos2f(0,0). The text will be always shown at the bottom-left corner whatever the Model’s transform is. Since I set orthogonal projection with (0, with, 0, height), the origin(0,0) of 2D screen is the left-bottom corner. It means a negative value of glRasterPos() causes the text may be clipped out of the viewing volume.

+---------------+(W,H)
|               |
|               |
|               |
+---------------+
(0,0)           (W,0)

songho. What can I say - your code worked perfectly!!!

THANK YOU a MILLION Times!!! I owe you big time. I am fairly new to OpenGL and your help was very valuable.

I have one more question.

As I wrote earlier, I am tessellating a non-convex polygon for rendering. I have no problem doing this but what I would like to do is save the resulting tessellated Model as an array of floating-point values to an external file so that the user can open the file and not go through the process of tessellation again. I believe I should have to save the values out of the tessellation Class I have as a nested Class but am not sure. Does anyone have any ideas?

Thanks.

Some snippets of my code so far follow:

public void drawModel(GL gl) {
    gl.glColor3f(1.0f, 1.0f, 1.0f);
    GLU glu = new GLU();
    tessellCallBack tessCallback = new tessellCallBack(gl, glu);
    GLUtessellator tobj = glu.gluNewTess();
    glu.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, tessCallback);
    glu.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, tessCallback);
    glu.gluTessCallback(tobj, GLU.GLU_TESS_END, tessCallback);
    glu.gluTessCallback(tobj, GLU.GLU_TESS_ERROR, tessCallback);
    glu.gluTessCallback(tobj, GLU.GLU_TESS_COMBINE, tessCallback);
// Cycle through ALL current Set of FACES for this STEP Model
    for (int i = 0; i < faces.size(); i++) {
        face = (FaceData)faces.get(i);
        holes = face.getHoles();
        double[][] outPts = face.getOutPtsArray();
        glu.gluTessBeginPolygon(tobj, null);
        glu.gluTessBeginContour(tobj);
        for (int j = 0; j < outPts.length; j++) {
          glu.gluTessVertex(tobj, outPts[j], 0, outPts[j]);
        }
        glu.gluTessEndContour(tobj);
        // Cycle through any potential "holes" for this Face
        for (int j = 0; j < holes.size(); j++) {
            poly = (PolyData)holes.get(j);
            double[][] holePts = poly.getPtsArray();
            glu.gluTessBeginContour(tobj);
            for (int k = 0; k < holePts.length; k++) {
                glu.gluTessVertex(tobj, holePts[k], 0, holePts[k]);
            }
            glu.gluTessEndContour(tobj);
        }// End of INNER FOR-LOOP (Polygon "holes")
        // Finished rendering this Polygon
        glu.gluTessEndPolygon(tobj);
    }// End of OUTER FOR-LOOP
    glu.gluDeleteTess(tobj);

The nested Class that implements the GLUTessellator follows:

    class tessellCallBack implements GLUtessellatorCallback {
        private GL gl;
        private GLU glu;
        public tessellCallBack(GL gl, GLU glu) {
            this.gl = gl;
            this.glu = glu;
        }
        public void begin(int type) {
            gl.glBegin(type);
        }
        public void end() {
            gl.glEnd();
        }
        public void vertex(Object vertexData) {
            double[] pointer;
            if (vertexData instanceof double[]) {
                pointer = (double[]) vertexData;
        	if (pointer.length == 6)
                    gl.glColor3dv(pointer, 3);
                gl.glVertex3dv(pointer, 0);
            }
        }// End of vertex(Object)
        public void vertexData(Object vertexData, Object polygonData) {}
        public void combine(double[] coords, Object[] data, float[] weight, Object[] outData){
            double[] vertex = new double[3];
            vertex[0] = coords[0];
            vertex[1] = coords[1];
            vertex[2] = coords[2];
            outData[0] = vertex;
        }// End of combine(double[],Object[],float[],Object[])
}

Originally posted by Richard145:
I have no problem doing this but what I would like to do is save the resulting tessellated Model as an array of floating-point values to an external file so that the user can open the file and not go through the process of tessellation again.
Yes, it is doable if you slightly modify the 3 tessellator callback functions for GLU_TESS_BEGIN, GLU_TESS_END, and GLU_TESS_VERTEX. These callbacks are actually called to draw the final polygon after processing tessellation.

For instance;

  • First, when GLU_TESS_BEGIN callback is called, open the output file and write the primitive type of tesselated polygon.
  • Second, write each vertex coords to the file whenever GLU_TESS_VERTEX callback is called.
  • Finally, close the output file when GLU_TESS_END is called.

There is an example code in the following link. It actually prints out the tessellated OpenGL drawing calls to the console. Hope it give you an idea.
OpenGL Tessellation

Once again, thank you songho.
:slight_smile: You really know your stuff when it comes to OpenGL. The info you sent is exactly what I needed.