Virtual OS/2 International Consumer Education
VOICE Home Page: http://www.os2voice.org
January 2005

Newsletter Index
< Previous Page | Next Page >
Feature Index

editor@os2voice.org


Visual Programming with OpenGL and Innotek GCC C++

By Per Johansson © January 2005

Introduction

OpenGL is a visual programming environment for anything from pretty screen savers to simulating machines. With OpenGL you can run existing programs and also create your own. This article is about how to make OpenGL programs with our most current C++ compiler, Innotek GCC 3.2.2. It's not an introduction about OpenGL itself, rather about how you can use code samples and available tools to make OpenGL programs.

I'll use these tools to get started. OS/2 Warp 4 comes with some support for OpenGL, but the default version is 1.0. If I remember correctly, that version was left out of eComStation. I use version 1.1 for this article. This is the latest (last?) version for OS/2, but it's enough for this purpose. For newer development have a look at MesaGL (see below).

Runtime

In eComStation 1.1 you can install the OpenGL 1.1 runtime (latest filedate 1997-10-06) from the Applications CD. The installation consists of some DLL files that are copied to your X:\OS2\DLL directory.

API

The OS/2 Developer's Toolkit version 4.52, filedate 2001-10-04, ships with eComStation 1.1. It can be installed from CD #2, "Developer software." It contains the libraries glut.lib, libaux.lib and opengl.lib. It also contains source code and compiled programs. Unfortunately, all this stuff is OpenGL 1.0 and it won't work with the OpenGL 1.1 runtime. You can still use the source code recompiling it with the OpenGL 1.1 libraries. Furthermore, the Toolkit is made for IBM Visual Age C++ version 3 which means that you sometimes have modify the code to make it compile with GCC.

The 1.1 version of the OpenGL API (latest filedate 1997-10-06) is available from the Hobbes archive. Either opengl.zip, the original one from IBM, or opengl_gold_110.wpi that contains some modifications for the Watcom compiler, for instance. In any case, one header file, glut.h, needs to be changed to compile with GCC. In fact, I have seen a slightly newer official glut.h modified in the same way as I did, so the modifications seem OK to do. glut.h provides, for instance, functionality to manage windows so you don't have to do any PM programming.

To include the directory F:\oglgold\include instead of F:\oglgold\include\gl I changed the following lines:

#include <gl.h>
#include <glu.h>

to:

#include <GL/gl.h>
#include <GL/glu.h>

I also changed 20 function parameters, removing one APIENTRY (defined as _System, finally expanding to __attribute__((__system__))), to avoid the compiler error: "invalid type modifier within pointer declarator". I don't know what the problem is, but my programs work fine without this modifier.

extern void APIENTRY glutDisplayFunc (void (* APIENTRY)(void));

to:

extern void APIENTRY glutDisplayFunc (void (*)(void));

The corrected glut.h is available from here.

Innotek GCC 3.2.2

It's available from Innotek. I have used beta 4 CSD 1. It comes with an installer.

IBM ILINK 5.0

This is the linker that is used by default by GCC 3.2.2. I tried in vain to use the older alternative, IBM Linker 4.0 (or LINK386) that comes with OS/2, eComStation and the toolkit. I also tried the IBM C/C++ 3.6.5 linker. None of them worked, at least not in C++ mode. IBM Linker 5.0 (filedate 2003-10-07) is not easy to find; I got it from the Warpzilla build site. Installation is easy. Just unzip the file, and add its bin directory to your PATH and LIBPATH.

The First Program

When studying OpenGL programming, we used the book Computer Programming using Open GL, 2nd edition, by Francis S. Hill, ISBN 0-02-254856-8. The book contains many program samples that are available from its website. Here's the first program, FIG2_10.CPP, with my modifications:

/*
Removed #include <windows.h>
Removed #include <GL/Gl.h> since it's already included from glut.h
Changed #include <gl/glut.h> to #include <GL/glut.h> for Unix compatibility
Changed main return from void to int for ISO C/C++ compliance (an explicit
  return statement is not needed in main)
*/

/*
#include <windows.h>   // use as needed for your system
#include <gl/Gl.h>
#include <gl/glut.h>
*/
#include <GL/glut.h>
//<<<<<<<<<<<<<<<<<<<<<<< myInit >>>>>>>>>>>>>>>>>>>>
 void myInit(void)
 {
    glClearColor(1.0,1.0,1.0,0.0);        // set white background color
    glColor3f(0.0f, 0.0f, 0.0f);          // set the drawing color
        glPointSize(4.0);                 // a dot is 4 by 4 pixels
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluOrtho2D(0.0, 640.0, 0.0, 480.0);
}
//<<<<<<<<<<<<<<<<<<<<<<<< myDisplay >>>>>>>>>>>>>>>>>
void myDisplay(void)
{
        glClear(GL_COLOR_BUFFER_BIT);     // clear the screen
        glBegin(GL_POINTS);
                glVertex2i(100, 50);      // draw three points
                glVertex2i(100, 130);
                glVertex2i(150, 130);
        glEnd();
        glFlush();                        // send all output to display
}

//<<<<<<<<<<<<<<<<<<<<<<<< main >>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char** argv)
{
        glutInit(&argc, argv);            // initialize the toolkit
        glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // set display mode
        glutInitWindowSize(640,480);          // set window size
        glutInitWindowPosition(100, 150);     // set window position on screen
        glutCreateWindow("my first attempt"); // open the screen window
        glutDisplayFunc(myDisplay);           // register redraw function
        myInit();
        glutMainLoop();                       // go into a perpetual loop
}

To compile:

set path=F:\ILINK50\bin;%path%
set beginlibpath=F:\ILINK50\bin
g++ -Wall -ansi -pedantic -DOS2=1 -D__IBMC__=1 fig2_10.cpp -I\oglgold\INCLUDE   -lglut -lopengl -L/oglgold/lib  -Zomf -Zlinker /PM:PM

Run with the command fig2_10. You'll see a window with three dots.

Image of program fig2_10.exe

A Second Program

This is somewhat more advanced. It shows as (unrealistic) solar system with a sun and three planets rotating around it at different speeds. The viewpoint is placed in angle from the rotating plane so the orbits look elliptical. Perspective is used so the planets' sizes vary with distance. The planets are illuminated from the sun with some ambient lightning so you can see their dark sides too.

#include <GL/glut.h>
#include <cstdlib>

const GLfloat
        light_pos[]      = {0, 0, 0, 1} ,
        light_ambient[]  = {0.1, 0.1, 0.1, 1} ,
        light_diffuse[]  = {1, 1, 1, 1} ,
        light_specular[] = {1, 1, 1, 1} ;

static int iteration = 0;

static GLint
        slices = 30 ,
        stacks = 30 ;

void myCreateObjects(int iteration)
{
        GLfloat
                white[] = {1, 1, 1, 1},
                red[] = {1, 0, 0, 1},
                green[] = {0, 1, 0, 1},
                blue[] = {0, 1, 1, 1},
                spec_exp = 100;

        glPushAttrib(GL_LIGHTING_BIT);
        glDisable(GL_LIGHTING);
        glColor3ub(255, 255, 0);
        glutSolidSphere(5, slices, stacks);
        glPopAttrib();

        glMaterialfv(GL_FRONT, GL_AMBIENT, white);
        glMaterialfv(GL_FRONT, GL_SPECULAR, white);
        glMaterialf(GL_FRONT, GL_SHININESS, spec_exp);

        for (int i(1); i <=3; ++i) {
                glPushMatrix();
                glRotatef((iteration) * 3/i, 0, 1, 0);
                glTranslatef(i * 10, 0, 0);
                switch (i)
                {
                        case 1: glMaterialfv(GL_FRONT, GL_DIFFUSE, red) ;
                        break ;
                        case 2: glMaterialfv(GL_FRONT, GL_DIFFUSE, green) ;
                        break ;
                        case 3: glMaterialfv(GL_FRONT, GL_DIFFUSE, blue) ;
                        break ;
                        default: glMaterialfv(GL_FRONT, GL_DIFFUSE, white) ;
                }
                glutSolidSphere(2, slices, stacks);
                glPopMatrix();
        }
}

void myKeyHandler(unsigned char key, int x, int y)
{
        //While mouse over this window
        if (key == 'q') {
                exit(0);
        }
}

void myDisplay()
{
}

void myReshape(int w, int h)
{
        GLint border = 10 ;
        GLint size_of_curve ;
        GLint low_left_x, low_left_y ;
        if (w > h) {
                if (h < 2 * border) {
                        border = 0 ;
                } /* endif */
                size_of_curve = h - 2 * border ;
                low_left_x = static_cast<GLint>(0.5 * (w - size_of_curve)) ;
                low_left_y = border ;
        } else {
                if (w < 2 * border) {
                        border = 0 ;
                } /* endif */
                size_of_curve = w - 2 * border ;
                low_left_x = border ;
                low_left_y = static_cast<GLint>(0.5 * (h - size_of_curve)) ;
        } /* endif */
        glViewport(low_left_x, low_left_y, size_of_curve, size_of_curve) ;
}

void myMenuHandler(int id)
{
        if (id == 1) {
                slices = 4 ;
                stacks = 4 ;
                } else {
                        if (id == 2) {
                                slices = 30 ;
                                stacks = 30 ;
                                } else {
                                        if (id == 3) {
                                                exit(0);
                                        } else {
                        } /* endif */
                } /* endif */
        } /* endif */
}

void myIdle()
{
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        myCreateObjects(++iteration);
        glFlush();
        glutSwapBuffers();
        glutPostRedisplay();
}

void myInit()
{
        GLdouble aspect = 1;
        glClearColor(0.0, 0.0, 0.2, 0.0);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45, aspect, 50, -50);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        gluLookAt(0, 20, 80,  0, 0, 0,  0, 1, 0);

        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
        glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);

        glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
        glShadeModel(GL_SMOOTH);
        glEnable(GL_DEPTH_TEST);

}


int main(int argc, char** argv)
{
       glutInit(&argc, argv);
       glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
       glutInitWindowSize(640, 480);
       glutInitWindowPosition(100, 150);
       glutCreateWindow("Solar System");
       glutKeyboardFunc(myKeyHandler);
       glutDisplayFunc(myDisplay);
       glutReshapeFunc(myReshape);
       glutCreateMenu(myMenuHandler);
       glutAddMenuEntry("Fast", 1);
       glutAddMenuEntry("Slow", 2);
       glutAddMenuEntry("Quit", 3);
       glutAttachMenu(GLUT_RIGHT_BUTTON);
       glutIdleFunc(myIdle);
       myInit();
       glutMainLoop();
}

To compile:

g++ -ansi -pedantic -DOS2=1 -D__IBMC__=1 solar.cpp -I\oglgold\INCLUDE    -lglut -lopengl -L/oglgold/lib -Zomf -Zlinker /PM:PM

Image 1 of Solar system program
Image 2 of Solar system program

The OS/2 Toolkit

There are OpenGL samples in the directory \OS2TK45\samples\opengl. Most of them use the deprecated libaux library, but there are two GLUT programs, atlantis and test7 in the glutdemo directory. I can compile and run them by changing the OpenGL #include as shown above, although test7 does not run correctly - I haven't found out why yet.

More Information and Resources

Conclusion

I had a hard time getting started with OpenGL since I wanted to do it with GCC. Perhaps this article will help other aspiring programmers. It shows what you need to do to make simple code examples compile and run with the Innotek GCC compiler.

References:

Hobbes: http://hobbes.nmsu.edu
Innotek: http://www.innotek.de
Warpzilla build site: http://www.mozilla.org/ports/os2/gccsetup.html
OpenGL book programming samples: http://www.prenhall.com/hill/html/cg.html
EDM/2: http://www.edm2.com
Interview re. OpenGL: http://www.os2voice.org/VNL/past_issues/VNL0398H/vnewsf4.htm
Snow Storm Software: http://www.snowstormsoftware.com
Escape GL review: http://www.os2voice.org/VNL/past_issues/VNL1099H/vnewsn5.htm
MesaGL homepage: http://www.laser.ru/soft/WarpMesaGL


Per Johansson has already contributed a number of articles for the VOICE Newsletter. Visit his homepage at http://per.johansson.name.

Feature Index
editor@os2voice.org
< Previous Page | Newsletter Index | Next Page >
VOICE Home Page: http://www.os2voice.org