User Tools

Site Tools


opengl_20programming

OpenGL programming

by Richard Russell, August 2006

Introduction


This article explains how to interface with the OpenGL 3D graphics standard from BBC BASIC for Windows. It is not an OpenGL tutorial; you will need to refer to the OpenGL Reference Manual and the OpenGL Programming Guide in order to write OpenGL programs. OpenGL provides similar facilities to Microsoft's Direct3D (accessible from BBC BASIC using the D3DLIB library) but, being an open standard, it is easier to port OpenGL programs from or to other platforms.

Declarations


In order to use OpenGL from BBC BASIC you will need to access the OPENGL32.DLL library supplied with Windows. You should include code similar to the following in your program (e.g. in an initialisation routine):

        SYS "LoadLibrary", "OPENGL32.DLL" TO opengl%
        SYS "GetProcAddress", opengl%, "wglCreateContext" TO `wglCreateContext`
        SYS "GetProcAddress", opengl%, "wglDeleteContext" TO `wglDeleteContext`
        SYS "GetProcAddress", opengl%, "wglMakeCurrent"   TO `wglMakeCurrent`
        SYS "GetProcAddress", opengl%, "glClear"          TO `glClear`

Here only one standard OpenGL function glClear has been listed, but you should include every function that your program requires (the full list runs to more than 100 functions so you probably won't want to include every one). In this example each function address is assigned to a variable whose name is the function name enclosed in 'back quotes' (grave accent or CHR$96 characters). You need not follow this convention if you prefer not to, so long as you adhere to BBC BASIC's variable naming rules; for example you could use _glClear or glClear% as the variable name.

As well as the standard OpenGL functions you are quite likely to need one or more of the routines from the OpenGL Utility Library (GLU). These are contained in the file GLU32.DLL:

        SYS "LoadLibrary", "GLU32.DLL" TO glu%
        SYS "GetProcAddress", glu%, "gluPerspective" TO `gluPerspective`

Again only one function has been listed in this example; there are nearly 40 in all.

Note that in many cases there are two alternative functions, one accepting single-precision (32-bit) floating-point numbers and the other accepting double-precision (64-bit) floating-point numbers. For example there are two variants of the glTranslate function: glTranslatef and glTranslated. When you have a choice, and assuming you don't need the extra accuracy of the double version, it is slightly easier to pass single-precision floats (by value) from BBC BASIC for Windows. See below for more details.

Some functions offer even more options, for example the glColor3 function comes in 8 varieties with its parameters having different data types (float, double, byte, short, int, unsigned byte, unsigned short and unsigned int). Since the SYS statement only passes integers (signed 32-bit values) your code will be made most straightforward by choosing the glColor3i variant.

Initialisation


Once you have declared the OpenGL functions you need to use, you can start writing your program proper. The first step is to set up your main output window to the size you require. You can do that using any of the normal methods provided in BBC BASIC: the MODE statement, the VDU 23,22 command or via the Windows API using SYS.

The next step is to select the wanted pixel format, for example the number of bits-per-pixel for the colour and for the depth map. This is a two-stage process: you first request a preferred format using ChoosePixelFormat in response to which Windows offers the nearest match to that format available with the current configuration (graphics card etc.). Assuming the offered format is acceptable you then select that format using SetPixelFormat. To achieve this you will need to incorporate code similar to the following:

        _PFD_MAIN_PLANE = 0
        _PFD_TYPE_RGBA = 0
        _PFD_TYPE_COLORINDEX = 1
        _PFD_DOUBLEBUFFER = 1
        _PFD_DRAW_TO_WINDOW = 4
        _PFD_SUPPORT_OPENGL = &20
 
        DIM pfd{nSize{l&,h&}, nVersion{l&,h&}, dwFlags%, iPixelType&, cColorBits&, \
        \       cRedBits&, cRedShift&, cGreenBits&, cGreenShift&, cBlueBits&, cBlueShift&, \
        \       cAlphaBits&, cAlphaShift&, cAccumBits&, cAccumRedBits&, cAccumGreenBits&, \
        \       cAccumBlueBits&, cAccumAlphaBits&, cDepthBits&, cStencilBits&, cAuxBuffers&, \
        \       iLayerType&, bReserved&, dwLayerMask%, dwVisibleMask%, dwDamageMask%}
 
        pfd.nSize.l& = DIM(pfd{}) : REM sizeof(PIXELFORMATDESCRIPTOR)
        pfd.nVersion.l& = 1
        pfd.dwFlags% = _PFD_DRAW_TO_WINDOW OR _PFD_SUPPORT_OPENGL OR _PFD_DOUBLEBUFFER
        pfd.dwLayerMask% = _PFD_MAIN_PLANE
        pfd.iPixelType& = _PFD_TYPE_COLORINDEX
        pfd.cColorBits& = 8
        pfd.cDepthBits& = 16
 
        SYS "GetDC", @hwnd% TO ghDC%
 
        SYS "ChoosePixelFormat", ghDC%, pfd{} TO pixelformat%
        IF pixelformat% = 0 ERROR 100, "ChoosePixelFormat failed"
 
        SYS "SetPixelFormat", ghDC%, pixelformat%, pfd{} TO res%
        IF res% = 0 ERROR 100, "SetPixelFormat failed"
 
        SYS `wglCreateContext`, ghDC% TO ghRC%
        SYS `wglMakeCurrent`, ghDC%, ghRC%

This code requests an indexed (paletted) colour selection with 8 bits per pixel, i.e. 256 different colours in all. Alternatively it could have requested, for example, a pixel type of _PFD_TYPE_RGBA and a pfd.cColorBits& value of 24.

Cleanup


When you exit your program you should delete the rendering context and device context created above. You can do that using code similar to the following:

        DEF PROCcleanup
        ghRC% += 0 : IF ghRC% SYS `wglDeleteContext`, ghRC%  : ghRC% = 0
        ghDC% += 0 : IF ghDC% SYS "ReleaseDC", @hwnd%, ghDC% : ghDC% = 0
        ENDPROC

You should place this procedure out of the way, for example at the end of your program after the END statement.

You will want to incorporate ON ERROR and ON CLOSE statements to ensure that PROCcleanup is executed even if your program is terminated unexpectedly:

        ON CLOSE PROCcleanup : QUIT
        ON ERROR PROCcleanup : SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT

OpenGL code


Once you have executed the Windows and BBC BASIC specific code above, you can commence the OpenGL code proper. The main considerations in making OpenGL calls from BBC BASIC for Windows are related to passing floating-point values, since normally only integer values can be passed by value using the SYS statement.

To pass a single-precision (32-bit) floating-point value use the FN_f4 function in the D3DLIB library. For example when calling the glTranslatef function, which takes three single-precision floating-point values, you would use code similar to the following:

        SYS `glTranslatef`, FN_f4(x), FN_f4(y), FN_f4(z)

To pass a double-precision (64-bit) floating-point value use the FN_dl and FN_dh functions listed at the end of this article. For example when calling the glTranslated function, which takes three double-precision floating-point values, you would use code similar to the following:

        SYS `glTranslated`, FN_dl(x), FN_dh(x), FN_dl(y), FN_dh(y), FN_dl(z), FN_dh(z)

Note particularly that each double parameter must be passed as a pair of values, the first using FN_dl and the second using FN_dh. When there is a choice, passing single-precision values will usually be easier.

Some OpenGL functions require an array of values. In this case it is easier to pass a double-precision array, since BBC BASIC for Windows supports this data type natively (by using the # suffix). For example to call glLoadMatrixd you would use code similar to the following:

        DIM matrix#(3,3)
        matrix#() = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15
        matrix#() *= 1.0
        SYS `glLoadMatrixd`, ^matrix#(0,0)

Rendering


OpenGL under Windows uses a double-buffering scheme. Objects are first rendered to an off-screen bitmap and then, when the entire frame is complete, swapped onto the display. The rendering loop will typically be of the following form:

        _GL_DEPTH_BUFFER_BIT = &0100
        _GL_COLOR_BUFFER_BIT = &4000
 
        REPEAT
          WAIT 2
          SYS `glClear`, _GL_COLOR_BUFFER_BIT OR _GL_DEPTH_BUFFER_BIT
 
          REM OpenGL code to render the scene here...
 
          SYS "SwapBuffers", ghDC%
        UNTIL FALSE

Support functions


The functions listed below are used when passing double-precision floating-point values:

        REM Convert to 8-byte double (low 4 bytes)
        DEF FN_dl(A#)
        A#*=1.0#
        =!^A#
 
        REM Convert to 8-byte double (high 4 bytes)
        DEF FN_dh(A#)
        A#*=1.0#
        =!(^A#+4)
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
opengl_20programming.txt · Last modified: 2024/01/05 00:22 by 127.0.0.1