Building a 3D Portal Engine - Issue 02 - Graphics Output Under Windows by (08 January 1999) | Return to The Archives

Introduction

Welcome back! In the second issue of this series of 3D coding documents, we will discuss things like screen access under Windows. Besides that, some basic information is given on double buffering, pixel plotting and that sort of stuff. This is definitely NOT Phantom's favorite subject, so lets get on with it. In the old days, there was DOS. And in DOS, there was mode 13. Once initialized, the unsuspecting coder could write to video memory, starting at a fixed address, A0000 if I am correct. All this didn't block the computer, and worked on virtually every VGA card in the world. The mode allowed for output of 256 color graphics, and that was just fine, if you did it well.
But then something really bad happened. SVGA cards started to appear everywhere, and to make things worse, computers became fast enough to do true-color graphics. From that moment on, nothing was certain anymore. You had 15 bit graphics (for some VERY odd reason someone decided to ignore a whole bit), 16 bit graphics, 24 bit, 32 bit, and random orderings of the color components in these arrays of bits. You could for instance encounter a card that did RGB, but also BGR, or RGBA... So the VESA standard was invented. This made things bearable again.
As you know, as soon as a standard pops up, there's always $omebody el$e (Micro$oft) who thinks that it can be done better. So, Windows was 'presented' to the world (or shoved down our throats, just what you prefer). From that moment on, you were never certain what your processor was doing at a particular moment. It could be doing your application, but it could also switch to another 'important' application at a critical point in time for your demo... Things get worse when you surrender to Micro$oft and start coding 'native' applications: Drawing to a window is a disaster, requires a 'lock', and is even slower.
OK, enough ranting. Lets try to live with what we have. I always use a buffer, because it looks so much like the old mode 13. When I have such a buffer, I try to get it to a window as efficently as possible. This is an absolutely neccessary but uninteresting step. My favorite 'toolkit' for this is MGL: A rather huge lib from SciTech, the makers of UNIVBE, a.k.a. the 'Display Doctor'. This library allows you to create a window relatively easely, and to get a pointer to it. It still requires the 'lock' however, wich means that you can't debug very nicely: During the 'lock', there are no screen updates. This can be partially bypassed by using your own buffer and copying it to the window once you're done. The lock is in this case only required during the copying process, and that's usually not a big problem. There's a small snag about the MGL library: It does much more than just displaying windows. It can for example also draw lines and polygons, and it contains OpenGL stuff. Because of this unneccessary load, you have to link a 1.5Mb library to your program just to open a window... So, there's something easier, wich I wish to introduce to you. It's called 'PTC', wich is an abbreviation of 'Prometheus True Color', or something like that. It's a really simple Direct X wrapper, and it allows you to do full-screen graphics output at various bitdepths and resolutions, and nothing more. Therefore, the library is quite small. Opening the display requires only a couple of (easy to understand and remember) commands. After that, you have full access to the display. One problem remains: The dreadful lock. This can again be bypassed using the buffer copy, however.
A big advantage of PTC that I have to mention is platform independency: PTC is also available for Linux, BeOS and some other OS's. So, if your program uses PTC for it's graphics output, you are merely 'using' Windows, rather than being forced to think towards it. It makes a future move to another OS easier too, since your code will work again with minor modifications.
So, here's a bit of homework for you: Go to http://www.gaffer.org, and fetch the PTC library. Have a look at some of the example programs, you will notice that they are really extremely simple. Once you've done that, we can proceed.
Here's a small PTC application to play with:

    #include "ptc.h"
     #include <stdlib.h>

     int APIENTRY WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,
             LPSTR lpCmdLine,int nCmdShow)
     {   try
         {   Format format(32,0x00FF0000,0x0000ff00,0x000000ff);
     	  Console console;
     	  console.open("demo",320,200,format);
             Surface surface(320,200,format);
     	   int32* pixels=(int32*)new int32[320*200];
    	   while (!console.key())
             {   int32* surf=(int32*)surface.lock();
       		 for (int i=0; i<(320*200); i++)
    		 	 *(surf+i)=*(pixels+i);
    		 surface.unlock();
                 surface.copy(console);
                 console.update();
             }
             return 0;
         }
         catch (Error &error)
         {   error.report();
         }
     }

An explanation: Most of the stuff here will probably be quite clear. I've added a couple of lines for the buffer stuff: First I declare a buffer of the same size as the PTC display, wich is copied every frame during a 'lock' to the PTC surface. This keeps the time that the surface is actually locked as short as possible, preventing hang-ups for buggy code.
OK, that's it for now. You should now have two ways to access the screen, more or less easily: Via MGL or via PTC. If you wish, you can try my windowed output that I use in Focus: Just copy and paste my stuff. Focus can be found btw at http://www.geocities.com/CapeCanaveral/5402. Check the 'system.cpp' file, it contains display initialization stuff for both MGL and PTC. The sample application shows you how to initialize the display using my system.cpp. Some changes for your own application are probably neccessary, since this was specifically designed for Focus.
During the rest of this series, I will completely ignore the Windows stuff. I'll just assume that you've somehow managed to get a buffer to draw in, so that we can focus on the interesting stuff: Hardcore-3D.

Article Series:

> - Building a 3D Portal Engine - Issue 01 - Introduction
> - Building a 3D Portal Engine - Issue 02 - Graphics Output Under Windows
> - Building a 3D Portal Engine - Issue 03 - 3D Matrix Math
> - Building a 3D Portal Engine - Issue 04 - Data Structures For 3D Graphics
> - Building a 3D Portal Engine - Issue 05 - Coding A Wireframe Cube
> - Building a 3D Portal Engine - Issue 06 - Hidden Surface Removal
> - Building a 3D Portal Engine - Issue 07 - 2D & 3D Clipping: Sutherland-Hodgeman
> - Building a 3D Portal Engine - Issue 08 - Polygon Filling
> - Building a 3D Portal Engine - Issue 09 - 2D Portal Rendering
> - Building a 3D Portal Engine - Issue 10 - Intermezzo - 8/15/16/32 Bit Color Mixing
> - Building a 3D Portal Engine - Issue 11 - 3D Portal Rendering
> - Building a 3D Portal Engine - Issue 12 - Collision Detection (Guest Writer)
> - Building a 3D Portal Engine - Issue 13 - More Portal Features
> - Building a 3D Portal Engine - Issue 14 - 3D Engine Architecture
> - Building a 3D Portal Engine - Issue 15 - Space Partitioning, Octrees, And BSPs
> - Building a 3D Portal Engine - Issue 16 - More On Portals
> - Building a 3D Portal Engine - Issue 17 - End Of Transmission

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.