GWin3D TM
Freeware*
Real-time 3D Graphics/Networking
/Media
C++ Class library for Windows

Programmer's Manual

Version 1.3
April 2008

GWin3D can be downloaded from http://coderboys.com



IMPORTANT:

* Except for certain portions, GWin3D and this document are Copyright (c) 2008 by Kelly Loum, all rights reserved. GWin3D also contains portions owned and copyrighted by others. You may use GWin3D, and distribute the DLL, without charge as long as you accept and obey the Terms. See http://coderboys.com for Pro version pricing and benefits.

Table of Contents


How to Use This Manual

Simply reading through this manual is not recommended! By far the fastest, most efficient, and least painful way to learn and develop with GWin3D is to read only the Introduction, Installation, and Creating Your First Program sections, take a look at the sample source code (see the samples directory), create a new bare-bones project, and then search the table of contents and this document for the subject of interest (for example, there are sections for getting you started in networking, 3D graphics, media, etc.).

Introduction to GWin3D

GWin3D is a C++ class library that greatly simplifies the development of Windows (Win32) applications that require any combination of high-performance dynamic 3D graphics, media, or network communications.

GWin3D has been designed with the following goals in mind:
For examples, with just a few lines of code you can set up a high-performance peer-to-peer network over the Internet or a LAN, broadcast to many other computers on a LAN, or easily and efficiently share blocks of data with other computers over the Internet or a LAN. You can play media files with a line or two of code. And with a few lines of code you can.load a 3D model file (created, for example, with the Blender freeware modeler) and display, alter, and move that model on the screen while GWin3D silently handles model file parsing, all the OpenGL calls, automatic level-of-detail selection, dynamic depth buffer optimization, out-of-view object culling, object hierarchy management, collision detection, infrequent lower-priority processing, and much more. Yet you can also completely override any part of that default behavior with your own code. So you need only understand something when you want to change, fix, or remove it.

You can use as little or as much of GWin3D as you like. You are not required to follow any application structure except any that might be a requirement of an API you happen to be using. For example, when using 3D graphics, you generally need to draw things in a given window (in a given "rendering context") from within a single thread. So you can use GWin3D along with other libraries that do require a certain program structure.

GWin3D is freeware. You can develop with GWin3D and distribute the DLL without charge as long as you obey and accept the terms. See the terms for complete restrictions, and see http://coderboys.com for current pricing and benefits of a Pro license.

System Requirements


Main Features


Installing the GWin3D SDK

Unzip the delivery if it is zipped. Preserve the directory tree specified in the zip file, if any. Where the tree is placed on your drive is not important.

Be sure you have the latest drivers for your video card. Additional multimedia capabilities are available if you install DirectX.



Creating Your First Program

To create an application, follow these rules:
See the sample VC++ source code in the samples directory.

As was said earlier, there is no required structure for your application except any that might be required by other APIs. For all GWin3D functionality except 3D graphics, this usually means simply creating an object and using it. For 3D graphics, OpenGL requires that all code that draws things in a given window (in a given "rendering context") be restricted to a single thread. (See the next section for discussion on this and an example of a bare-bones 3D graphics application.)

The GWin3D internal debugger is enabled by default. When the debugger detects a probable programming error, it prints a warning to a console window. It is wise to leave warnings enabled during development and fix the warnings as they appear. Even if your program apparently works in spite of an error or warning message from the debugger, note that some platforms are more forgiving than others. See the section on the Debugger.

The debugger also provides a mode that let's a user enter the debug console by using hotkeys. This mode is disabled by default. See the section on debugging for an example of how to enable this feature.

In a release, disable all except fatal debug messages (see gwSetDebug) because there is a very small performance hit when messages are enabled even if none are being printed.

There are some advantages to creating your program as a console application. The console makes a convenient user I/O point and will also be used as the debugger window. For example, if you create your program as a console application and run your program by entering the program name in an existing command shell console window, then any debugger warnings will remain on the screen even after the program terminates. You can also redirect program stdin and stdout with shell piping operators. Even for a console application, you can always close your console window in a release (with the Win32 FreeConsole function), so that the user is not confused by its presence.

It is best to avoid terminating your program with explicit calls to functions like exit() or TerminateProcess(). Some OpenGL and media drivers don't handle this well. Simply allow your top-most function to return. Likewise, a user closing a console window of a console program can cause an ungraceful exit. For example, some Video for Windows (VFW) drivers don't like it when a program exits ungracefully. If you must handle the possibility of a user closing the console window, then install a ctrl-break hook to force the exit to be graceful.


Creating an Application that Displays Dynamic 3D Scenes

There are many ways to design your program to get graphics content to appear in a window.

GWin3D provides a simple method shown by example, below. This method draws the scene periodically in the window even if there was no change in the scene content. This method works efficiently if typically something is changing in the scene every frame--which is usually the case with real-time 3D graphics. Since this can be wasteful when the scene contents are not changing between frames and other threads are in need of CPU, you might consider either setting the priority of the drawing thread lower than other threads (see the Win32 SetThreadPriority function), or adding code that doesn't bother drawing a frame if it is known that there is no reason to (realize that this method involves not only keeping track of whether scene content has changed, but also whether the window has been resized or an obscuring window has been removed).

Instead of using the below simple method to draw frames, you can also use the Windows conventional method to draw frames. The conventional way to update screen graphics in Windows is to place the actual drawing code in a WM_PAINT handler (in MFC, an OnPaint handler) and then let code elsewhere do the things necessary to change what will appear in the scene and then call the Win32 InvalidateRect() function, which causes the system to post a WM_PAINT message, which will cause the WM_PAINT handler to execute, and draw the scene. You can use this method if you like, but note any actual drawing to the window done from other code (like erasing the background of a CDialog object, for example) will conflict with the drawing of the 3D scene, and also that several of the automatic performance enhancements in GWin3D are based on the assumption that frames are being drawn relatively rapidly and periodically. This is because often it can't be known what drawing settings are optimal without having drawn the scene at least once. For example, the auto-LOD code and the auto clipping code calculate optimal values according to the last scene drawn. If you only update the window's contents when a change was made, when the window was resized, or when an obscuring window was moved away, (typical when code for drawing resides in a WM_PAINT handler), then the GWin3D automatic performance functionality will not work correctly. One remedy to this is to forcibly cause a WM_PAINT message to be posted periodically--for example, by calling InvalidateRect from within a periodic WM_TIMER handler. See the gwWindow constructor for more details on using the WM_PAINT approach. Also note that the WM_PAINT approach requires that gwWindow::PrepareForDrawing and gwWindow::FinishDrawing be called in the WM_PAINT handler (those functions are called automatically by GWin3D if the below method is used). Also, make sure the message pump that calls the WM_TIMER and WM_PAINT handlers is the same one that calls any other functions that do OpenGL things with the window--so that only one thread is using a given OpenGL rendering context.

Finally, no matter what drawing method you do choose to use, remember to obey the OpenGL requirement that everything that calls OpenGL functions for a given window (a given rendering context) occur in the same thread.

Below is an example of the simple drawing method provided by GWin3D. This is the entire code for a console program. It displays a spinning 3D object in a window. It requires that a VRML file named MyVrmlFile0.wrl exist in the default directory. See gwSprite::Load and the section on Creating 3D Objects for more information on creating such files. You can use this source as a starting point:

#include "GWin3D.h"

// this function draws one frame. it is called 20 times per second
void __fastcall UpdateFrameFunction( gwWindow *Win)
{
   // create a 3D object from a VRML model file
   // (since this is static, its created only the first time this function is called)
   static gwSprite MySprite("MyVrmlFile");

   // rotate the object
   MySprite.ModifyRotation (10,17,23);

   // draw the object in the window
   MySprite.Draw(Win);
}

void main(void)
{
   // initialize the GWin3D library
   gwInitLib();

   // create a 3D window and specify the frame-drawing function
   gwWindow MyWindow(NULL,UpdateFrameFunction,GW_BLOCK);
}


Finally, before getting heavily into 3D graphics development, be sure to read the short section on 3D Graphics Performance Considerations. Neglecting to tweak the several most important window and sprite variables related to performance can make the difference between a usable program and an unusable program. It certainly isn't necessary to implement what you read in that section into your first program, but it is important to know about them.

Selected Subjects


A Short Glossary of 3D Graphics Terms


Vertex

A point in space. Typically in reference to a point at which the line segments of a polygon meet. That is, a corner of a polygon. A corner of an object. Most visible objects are described as a set of vertices.

Polygon

A visible surface described by a set of vertices that mark its corners. A triangle is a polygon with three vertices, a quad is a polygon with four. One side of a polygon is a "face". Most of the objects you create will be made of polygons, although you can also make lines, points, and other visible things.

Ambient lighting

A 3D scene has one ambient light setting. You configure it with OpenGL commands. GWin3D initially sets the ambient light to a dim gray. The ambient light affects only the ambient material attribute on objects. The intensity of ambient lighting on the surface of a polygon is unrelated to the orientation of the polygon and the camera.

Diffuse lighting

Directional or point source lighting. You can have multiple directional or point light sources. Attach one of OpenGL's lights to a sprite via the gwSprite::Light member. Diffuse lighting affects only the diffuse material attribute on objects. Its intensity depends on the orientation of the polygon relative to the light.

Material

The coloration and shininess of an object and how the colors are affected by ambient and diffuse light sources. This is not to be confused with a texture, which is a 2D image that can be applied onto an object in order to give it apparent detail. An object can have a coloration and shininess without a texture. If a texture is also applied, the material affects the coloration and shininess of the texture. How the material affects the texture is configurable with OpenGL. See gwMaterial for more details.

Texture

A 2D image applied to the surface of an object. For this to work, each vertex of the object must have a texture coordinate associated with it, which is an X,Y coordinate of the 2D bitmap image that should be aligned with that vertex. Pixels across the surface of a polygon are interpolated from the texture coordinates specified for each vertex. GWin3D can automatically create texture coordinates for an object, and it supports dynamic textures and video textures.

Normal

In mathematics, the word "normal" means a vector that is perpendicular to a surface. In 3D graphics, "normal" means a vector that indicates at what direction light will cause a surface to be brightest. Normally they would mean the same thing. However, by defining a normal at some angle other than perpendicular, you can cause the illusion that a surface lies at a different angle. Each vertex of a polygon has a normal vector associated with it and the brightness across the surface of a polygon is interpolated from the normals of its vertices. So a single flat polygon can have a gradient of brightness across it giving the illusion of curvature. In this way an object composed of fewer polygons can still be made to look quite smooth by specifying vertex normals for a surface that really were curved. GWin3D can automatically create vertex normals for a model.

X-axis

The axis that extends right from the origin.

Y-axis

The axis that extends up from the origin.

Z-axis

The axis that extends forward from the origin.

Translation

Movement. The placing of something at a different location from its original location.

Rotation

The circular movement of each vertex of an object about the same axis.

Scale

A change in the width, height, and/or depth of an object.

Shear

A pulling of one side of an object in one direction, and the opposite side in the opposite direction, without rotation, such that the object is distorted rather than rotated.

Yaw

Rotation about the Y-axis

Pitch

Rotation about the X-axis, after any Yaw has been applied.

Roll

Rotation about the Z-axis, after any Pitch has been applied.

Euler angles

The pitch, yaw, and roll of an object, applied in that order.

Matrix

An array of 16 numbers that describes the position and orientation of a sprite. Specifically, a matrix describes a difference, or change, in the orientation (coordinate system) of one object from another. See the section on Introduction to Matrices in 3D Rendering.

Origin

The center of a coordinate system. The point in the coordinate system that is, by definition, untranslated.

Frame

In this document, 'Frame' means a complete still scene. It is analogous to a movie frame. A moving 3D scene is created by drawing successive frames--typically at about 15 to 70 times per second.

Creating 3D Objects for use as Sprites

There are three ways to create a 3D object to be used as a sprite:
The gwSprite::Load function supports a simplified subset of VRML 1.0, VRML97, and VRML 2.0 and ignores everything else it encounters in a VRML file. For performance reasons, individual sprites should be kept fairly simple. Complex objects should be constructed of multiple sprites (sprite trees). However, consider also that modern video cards can often draw thousands of polygons in the time it might take the CPU to execute the overhead of one gwSprite. Ideally we adjust sprite complexity so that neither the CPU nor GPU experiences idle time. Experimentation is recommended.

GWin3D does not accept compressed VRML files (if you have a compressed VRMl file you want to use, simply uncompress it with your favorite decompressor--you might have to temporarily rename the file extension to ".gz" to make this work). GWin3D also has certain VRML file-naming rules. See gwSprite::Load for details.

GWin3D honors texture coordinates that are specified in a VRML file. Some modelers do a poor job of creating texture coordinates or simply don’t output texture coordinates to the VRML file. To overcome this problem GWin3D has some fairly powerful auto-texturing capabilities. You can use certain flags of gwSprite::Load to override the texture coordinates in a VRML file or to generate texture coordinates when they are not present in the VRML file. See gwSprite::Load.

By default GWin3D ignores material, rotate, and translate statements in the VRML file since many modelers place these in a VRML file when it would be better, for performance reasons, to avoid them. You can tell GWin3D to honor these statements by specifying certain flags to the gwSprite::Load function, however you should consider avoiding them, if possible.

Remember that GWin3D sprites are a rather primitive object from a modeling point of view. Your philosophy should be to create only one primitive, single-textured, single material "sub" object per VRML file. In this way you will be more prone to create sprite trees to produce complexity and LODs rather than single complex sprites. This approach is important so that things like out-of-view detection, levels-of-detail, etc. work efficiently. Of course, if your application displays only a few sprites and you are assured that you will always have enough CPU/GPU to spare, then its perfectly OK to create a complex sprite. You also have the option of compiling a sprite tree (see the section on Compiled Sprite Trees ) to increase performance. Note, however, that GWin3D ignores references to texture filenames in the VRML file (you specify the sprite's texture with the Texture member), that a sprite may have only one texture, and that many modelers may not do a good job of saving a complex scene to a VRML file that can be properly parsed by GWin3D.

Of late, the only reasonable option for a freeware modeler is Blender.

Blender is a full-featured modeler. It is the recommended modeler for GWin3D.

Do not attempt to learn the interface by playing with it. The interface is not designed to be easy to learn by being simplistic. It is designed to be efficiently used by someone who has taken the ten minutes to read the quick start section describing the GUI. There are many hot keys not apparent by looking at the GUI. To use Blender you keep one hand on the keyboard and one on the mouse. If you don't know about the hotkeys you will only get frustrated thinking the Blender GUI is broken or poorly designed. Once the hot-keys are learned it is a Cadillac modeler with which you can work quickly and efficiently.

Also, take the time to get whatever documentation is available on the Blender site.

At the time of this writing Blender did not output texture coordinates to a VRML file (this may have changed). This is not a problem since GWin3D's internal texture coordinate generation is as good as or better than what a typical modeler might be capable of (see gwSprite::Load ).

Blender is open source, so it will likely stay current and competitive.

Blender outputs VRML 1.0 and Blender VRML files can be loaded by GWin3D.

Note: As always, be sure you have the latest drivers for your video card.

3D Graphics Performance Considerations

It certainly isn't necessary to implement what you read in this section into your first program, but it is important to know the following points exist.
The following code is an example of a way to limit the CPU/GPU used in your gwWindow 's PeriodicFunction. No more than about 80% of the CPU/GPU is used. You would use this code if you want highest levels of detail to be retained at the expense of frame update rate:

// This is at the top of the gwWindow 's PeriodicFunction:
gwTimer Elapsed;

// main body of the PeriodicFunction is here
...
...

Universe.Draw(Win);

// This code is at the bottom of the PeriodicFunction:

// Adjust the period to be a little more than how long it took this
// function to execute (this assumes Win->MaxElapsedTimeMs
// is very large so that highest LODs are always used):
Win->RealTimeTask->Period = Elapsed.Get()/800;

Introduction to Matrices in 3D Rendering

GWin3D makes it easy to set a sprite's position, orientation, scale, and shear relative to its parent sprite. It is not necessary to read or understand this section to use the GWin3D functions that do that. This section is provided solely for a deeper understanding of how GWin3D and OpenGL manage positioning and orientation of objects and their children.

In the real world, objects often have moving parts attached to them.

Such parts follow the position and orientation of the objects they are connected to, but may have their own position and orientation relative to that "parent" object, which may even change dynamically (from frame to frame).

Every object has a coordinate system that describes its orientation and position. When we say "object's coordinate system", we don't mean the coordinate system in which the object lives. That is the object's parent's coordinate system. Rather, we mean the coordinate system that is defined by the position and orientation of the object. In an object's coordinate system, the object resides at the origin, is undistorted, is pointing down the Z-axis, and its children are positioned relative to that.

The numbers describing an object's coordinate system are really describing how the position and orientation of that object differ from its parent's coordinate system.

There are many mathematical ways to describe such a difference. It could, for example, be described with a set of distances and angles.

We use matrices to describe such differences. A matrix is a set of 16 values that are held in a 4x4 array for mathematical reasons. The matrix holds three axes and a position.

All possible differences in one coordinate system from another can be described by defining the length and direction of the three axes and the position. Nine of the values in a matrix are used to define the three axes (three per axis) and three more entries are used to describe a position (X, Y, Z).

Fortunately, it is not at all necessary for a 3D developer to understand the meanings of the 16 values in the matrix . It is only necessary to understand that a matrix is a description of a difference in position, scale, shear , and rotation of one coordinate system from another, and that you multiply them together to create other matrices.

GWin3D supplies functions that set and alter the position, scale, shear, and rotation attributes of a matrix.

Matrices are used instead of a set of angles and distances because many underlying calculations are simplified and faster.

Multiplying a child's matrix by the parent's matrix yields a matrix that describes the child relative to the grandparent's coordinate system. That is, multiplying matrices collapses the tree of relative coordinate systems. Said another way, one coordinate system can be used to modify another.

Multiply all the matrices in a tree (hierarchy) and the result is a matrix that describes an object's position and orientation relative to the universe. This is needed because, although the application often finds it convenient to manage matrices that describe one object relative to another, the rendering engine can handle only matrices that describe objects relative to the universe. Also, the universe-relative matrix is often of use to the application when the application needs to know the absolute orientation and position of a child object. GWin3D internally collapses the tree of matrices (see gwSprite::Draw ) and also saves-off the absolute matrix (see gwSprite::LastMatrix ) for examination by the application each time the object is drawn.

The order two matrices are multiplied makes a difference. Multiply them one way and you modify a matrix relative to its own coordinate system, multiply them the other way and you modify the matrix relative to its parent's coordinate system.

Every Sprite has a matrix. This matrix describes the sprite's orientation and position relative to its parent sprite. Specifically, the gwSprite class is a child of the gwMatrix class. Other members of gwSprite specify what sprites are children (subsprites) to what. See gwSprite::AddSubSprite and gwSprite::RemoveSubSprite.

When you draw a sprite (see gwSprite::Draw ), the sprite and all its subsprites are drawn. That function first pushes the current matrix onto a stack (called the modelview stack) and then draws the sprite. Next, the gwSprite::Draw function calls the gwSprite::Draw function for each subsprite. After it draws all the subsprites, it pops the matrix and returns. When gwSprite::Draw calls itself recursively, it pushes the default matrix onto a stack and multiplies it by the subsprite's matrix. This new default matrix describes the subsprite relative to the universe.

There may be a time that the application needs to create a coordinate system (a matrix ) to independently modify the current matrix (see the glMultMatrixd OpenGL function) or a sprite's matrix (see gwMatrix::operator*= ). This operation might, for example, be performed in a gwSprite::PreDrawCallback function. Note that matrix multiplication is not commutative--the order it is performed makes a difference.

If all this sounds confusing, see the bare-bones source. Creating a sprite and moving it around is quite simple and just takes a few lines of code.

See the gwMatrix class for more information.

Level of Detail (LOD)

When a sprite is far away, drawing all its details would be a waste of CPU and GPU time, slowing down the entire application. It is better to draw a simplified form of the sprite when it is far away so that none of the CPU and GPU power is wasted drawing complexity that can't be seen, anyway.

Different versions of the same object that vary only in their complexity are called levels of detail (LOD). Which LOD should be drawn depends on the diameter of the object in the window, among other things.

The two ways that GWin3D can manage LODs are discussed below. You can also write your own code to manage LODs or to modify the way GWin3D handles LODs by placing it in a gwSprite:PreDrawCallback function.

Simple LODs

"Simple" LODs allow the distance to an object to vary by a factor of about 2000 and still maintain efficient use of CPU/GPU.

For higher distance ratios, use a combination of simple LODs and sprite tree LODs. Sprite tree LODs are described in the next section.

Simple LODs are implemented in GWin3D by creating a separate OpenGL display list for each LOD and assigning them to elements of the sprite's List array (see gwSprite::List ).

You can create such display lists programmatically by calling the right OpenGL functions. If you do this then be sure to set the gwSprite::Radius member to reflect the visible radius of the object.

You can also just create, using a freeware 3D modeling program like Blender, a set of VRML files (one for each LOD ) and let gwSprite::Load do everything automatically. gwSprite::Load creates OpenGL display lists from the VRML files and distributes them throughout the List array according to the number of polygons in each file. It also automatically loads the gwSprite::Radius member according to the largest VRML LOD loaded. There is also a gwSetDebug option that lets you watch this process.

You should experimentally adjust the gwSprite::LodScale member. gwSprite::LodScale controls at what distance LODs change.

When it comes time to draw the sprite, GWin3D will choose one of the OpenGL display list handles from the List array. This decision is based on the following:
The sprite's List array holds 11 elements. All else being equal, a level of detail in GWin3D increases each time the distance from the viewer to the objects is cut in half. Therefore each level of detail for a sprite should have roughly four times the number of polygons as the next lower level of detail (the goal is to maintain a constant polygon density on the screen for the sprite) up to the maximum number of polygons required to fully draw the object. Element 0 of the array holds the OpenGL display list handle for the highest detail, element 10 holds the handle for the lowest defined level of detail.

For the highest level of detail it is important not to use more polygons than are necessary.

Ideally the lowest level of detail is comprised of 0 polygons. That is, at great distances nothing is drawn of the sprite. If GWin3D decides, at draw time, the List element to use is greater than 10, then GWin3D draws nothing (because there is no List element greater than 10). Ideally element 10 of the List should have an OpenGL display list handle that draws 2 polygons, the next level (element 9 of List) 8 polygons, the next 32 polygons, and so on, but limited by the maximum number of polygons needed to fully draw the object. At this rate with the available 11 levels of detail you might make your highest LOD (element 0) with up to 2097152 polygons if the object were complex enough to merit it. However, you should consider breaking up such a sprite into a sprite tree when the polygon count of the LOD exceeds 2000.

Ideally you should create a separate version of the sprite for each level of detail up to the maximum detailed version. Creating LODs with a modeler that has a decimate function (like Blender) makes this easy. Once you have created your highest LOD, repeatedly decimate it to roughly one-fourth the polygons and save each as a VRML file until you have just a few polygons left.

If you aren't quite as concerned about saving CPU and GPU power, or you know you will always have power to spare (because, for example, you will never draw more than a few sprites), then you might use the same version of the object for several contiguous levels of detail, or just use one VRML file for all of them (the easiest and the least efficient way).

For example, if you have an object that can be fully described with 340 polygons and are not terribly concerned that you might waste a little CPU and GPU power, then you might create just three versions of the object. (If you use gwSprite::Load , then it will automatically distribute them optimally). The numbers of polygons for each level of detail might resemble those shown in the following table (Note: In this table, LOD 0 is the highest and 10 is the lowest defined display list, and the fictional "element 11" and beyond is the lowest LOD. This reverse numbering is done because GWin3D's gwSprite::LodIndex member works this way.)

Level of detail (where 0=highest) Maximum number of polygons Actual number of polygons (example)
0 2097152* 340
1 524288* 340
2 131072* 340
3 32768* 340
4 8192* 340
5 2048 340
6 512 340
7 128 340
8 32 40
9 8 8
10 2 0
(11 and beyond) 0 0

*Typically polygon counts over about 2000 really should be broken up into multiple sprites in a tree (see below).

"Simple" LOD handling has its limits, obviously. There may be objects you want to create that have a greater LOD ratio than 2000. For these you would use a combination of simple LODs and a sprite tree.

Note: it is not necessary for the application to manage texture LODs (mipmaps). These are handled internally by GWin3D, thanks to the GLU and OpenGL functions that do that.

Sprite tree LODs

You can create a sprite tree (see gwSprite::AddSubSprite ) to implement almost any range of LOD.

When the parent sprite is far away the subsprites are not drawn (see gwSprite::SubspriteLodLimit ) and the parent sprite is all that is needed to give the object a large-scale form. When the camera is close to the sprite, however, the subsprites are drawn and each subsprite has its own simple LOD that depends on how close it is to the camera. In this way when the viewer is very close to one part of the whole object, only that part is being drawn in detail and the other more distant parts are drawn with less detail.

Those subsprites, too, can have little subsprites upon their backs, ad infinitum (limited by your OpenGL implementation's modelview matrix stack depth, see below for more on this).

Note: The polygon count rules shown in the table in the " Simple LODs " section above should be applied to sprites individually, whether they are in a sprite tree or not. Do not apply the rule to the total polygons in a sprite tree.

When creating a sprite tree, it is sometimes useful to define the higher levels of detail of the parent sprite as invisible and let only the subsprites define the object when the viewer is very close. You might do this if there is considerable detail over the whole object and the parent sprite needs to be hidden to allow the child sprites to show the detail. When doing this, however, care must be taken so that there is not a range of distances at which both the parent and the subsprites are invisible. That is, when the viewer is approaching the object, the subsprites should appear before the parent sprite disappears. You can accomplish this by assigning the high LOD elements of the gwSprite::List array to zero. Be careful when messing with the List elements if the sprite "owns" them--that is, if the sprite is responsible for freeing them when it is destroyed. See gwSprite::List .

Another option is to design the sprite tree such that the parent sprite is always visible but subsprites add detail to the outside of the parent sprite. In this case, care must be taken to avoid the object appearing to grow in size (because the outer subsprites begin to appear) as one approaches the object.

See gwSprite::SubspriteLodLimit to control at what parent LOD subsprites begin to be drawn.

Remember that the LOD drawn is based in part on the size of the sprite as defined by its Radius member. When implementing a sprite tree as described above, remember that an LOD level of a smaller subsprite of the tree does not correspond to the same distance for the same LOD level of a larger subsprite, or the parent. In other words, you can't say to yourself, "I'll set these small subsprites so they appear at LOD 7, and set the big parent sprite to disappear at LOD 7". Those two LODs occur at different distances because the sprites are different sizes (have different Radius values). Said more succinctly, the level numbers do not correspond to distances, they correspond to the relative size of the sprite on the screen.

Remember also that the Radius member you set for a sprite must just encompass all its subsprites so that the automatic out-of-view detection and LOD selection works correctly in GWin3D.

The depth of a sprite tree is limited to the size of the OpenGL implementation's modelview matrix stack. Pretty much all OpenGL implementations have a modelview matrix stack depth of 32 (although some low-end or older implementations may only have 16). If both simple LODs and sprite tree LODs are used in a fairly efficient way on a system that has a stack depth of 32, this yields an LOD dynamic range of roughly 2^40.

If you want to make a sprite tree deeper than a system supports then you'll have to implement your own matrix stack and call the Draw function for each subsprite, yourself, and not let GWin3D do it. You would do this from within a gwSprite::PreDrawCallback or gwSprite::PostDrawCallback function. See gwSprite::Draw to see how the stack is used and when subsprites should be drawn.

Dynamically Loaded Sprite Trees

By dynamically loading and unloading sprite trees and their subsprites, you can implement infinite levels of detail as well as save system resources. You would need to do this if all the sprites of your virtual reality universe won't fit in memory at the same time.

You can implement sprite tree LODs as described above and also dynamically load and unload subsprites at lower LODs than they are visible, or you can implement dynamically loaded subsprites without sprite tree LODs as described above.

One way to implement dynamically loaded subsprites in a sprite is to specify an EarlyDrawCallback function for the sprite. This function would decide whether the subsprites of that sprite should be loaded or deleted.

Note that all of your sprite destructors should delete the sprite's subsprites.

Do not allow subsprites that will be dynamically loaded and unloaded to have multiple parents.

Do not change any sprite's SubspriteLodLimit from its default value. Doing so would sometimes prevent a sprite's EarlyDrawCallback function from being called, preventing a very low LOD sprite from having a chance to delete its subsprites.

The EarlyDrawCallback code you write should examine the current LodIndex and decide whether to load and add subsprites, remove and delete them, are leave things alone.

Be sure to delete sprites at a much higher LodIndex than their highest visible LodIndex (remember that the higher the LodIndex, the lower the level of detail ). For example, do this especially if you also implemented sprite tree LODs as described in the previous subsection.

You might want to load your subsprites at an LodIndex that is lower than the LodIndex at which you unload, if there is a probability that the camera may return to sprites it recently left (which is typical in many virtual reality games).

Mesh LODs

You can create a sprite tree of a mesh by creating a low-resolution parent, and creating successively higher resolution generations of smaller subsprites.

When creating such a sprite tree, just remember to take into account the fact that when you reduce the resolution of a mesh (for example, when you reduce the resolution of a heightfield image that will be converted to a mesh) that the physical size of the resulting mesh will be less than the size of the original. So you need to include a little extra all around your subsprites. Otherwise there will be seams between them.

For example, if a subsprite's horizontal resolution and vertical resolution are each twice that of the parent (this is the suggested ratio), then you'll need to include at least an extra half a polygon 's worth of mesh all around the child sprite in order to close the seam. Of course, the easiest thing to do is add a whole polygon (yes, they'll overlap a little--which is fine).


Working with Images

Use the gwImage class to load, manipulate, access, and save image files of type BMP, JPG, or JP2.

The gwTexture class let's you specify a gwImage object for use as a texture.

You can also use gwImage to get the pixels of an image for use as raw RGB data or a DIB.


Impostors and Billboards

Both impostors and billboards are objects automatically oriented to face the camera. See gwSprite::LockXOrientationToSprite and friends.

Typically a billboard is simply a flat panel that always faces the camera. It might have a text message on it, for example. There is no reason, however, that you can't make a billboard a 3D model that faces the camera.

To implement a flat billboard, create a panel or disk (see gwSprite::Create ) and make the gwSprite::LockXOrientationToSprite and friends point to the viewpoint sprite. Assign or load the gwSprite::Texture member to whatever you want on the billboard. Be sure gwSprite::ImpostorLodIndex is zero (it is by default) so the sprite points at the viewpoint even when it is close to the viewpoint.

An impostor is the same as a billboard except that it is a special form of a low level of detail (see the section on Levels of Detail ). It appears only beyond some distance from the camera and its purpose is to mimic at a great distance the appearance or at least the general coloration of the sprite's higher LODs.

An impostor can have the same texture as the higher LODs of the object, or it can have a separate texture (see gwSprite::ImpostorTexture ) that might be a dynamically created 2D snapshot of the object it is imitating, or it might just be a non-descript hazy image with generally the same coloring as the higher LOD texture.

You control at what LOD and beyond the impostor appears with the gwSprite::ImpostorLodIndex member.

The low LODs of a sprite that will become an impostor are typically flat surfaces, while the higher LODs are the 3D model. See the gwSprite::List array member, which holds the OpenGL display list handles for each LOD.

You might make a separate panel or disk sprite (see gwSprite::Create ) and use its low LOD List element values to assign to the low LOD elements of the sprite you want to use impostors at low LODs . When assigning gwSprite::List elements, remember to keep track of which sprite owns what display list handles. gwSprite::Load and gwSprite::Create set a flag internal to the sprite object telling it to free any handles in the gwSprite::List array when the gwSprite destructor is called. Assigning List elements without thought can result in some handles being freed when they shouldn't be and other handles neglecting to be freed when they should be. It often helps to instantiate separate sprites which are never destroyed and that own their display lists, and other sprite's List elements are assigned from the List elements of those template sprites.

A true impostor texture can be generated on the fly by telling OpenGL to render a view of the 3D object to a RAM buffer, and then that buffer is used as a texture. One may even create on the fly an alpha channel for the texture so that the impostor is transparent except where the texture depicts the object. This texture would be assigned to gwSprite::ImpostorTexture. GWin3D does not automatically generate such textures. Impostors that simply use a predefined texture for gwSprite::ImpostorTexture, or even the same texture that the high LODs of the sprite use (in which case it is not necessary to load or assign gwSprite::ImpostorTexture ) are often convincing at great distances and certainly easier to implement and less CPU/GPU intensive.


Textures

A texture is a 2D image applied to the surface of a sprite.

For textures to work, a texture image must be assigned to the sprite (see the gwSprite::Texture member) and each vertex of each polygon of the sprite must have a texture coordinate assigned to it (accomplished by assuring texture coordinates exist in the VRML file, by causing coordinates to be automatically generated by passing certain flags to gwSprite::Load, or by assigning them, yourself, with certain calls to OpenGL). Otherwise no texture will appear.

The advantages of textures are:
The disadvantage of textures is that they use up RAM in your graphics card. GWin3D does, however, internally create and manage mipmaps for far more efficient use of texture memory (via an internal call to gluBuild2DMipmaps). Therefore it is not necessary for the application to manage simpler textures for distant objects.

Textures can also be compressed to save GPU memory.

By default, GWin3D allows you to specify one texture per sprite (see gwSprite::Texture ), but you can put multiple texture commands in the sprite's display lists (see gwSprite::List ).

GWin3D can load texture coordinates from VRML files. It can also generate texture coordinates automatically. See gwSprite::Load.

Still textures are typically specified as a gwImage object. Textures that play a video are also easily created. See gwTexture::CreateFromDib, gwTexture::CreateFromRgb , and gwAvi.


Networking

The gwNetStreamClient class let's you connect to a standard server such as an HTTP, telnet, or FTP server. Such connections use the reliable but relatively slow SOCK_STREAM protocol. These connections are based on a server-client model.

Use the gwNetPackets and gwNetData classes to implement high-performance peer-to-peer communication over a LAN and across NAT routers (over the Internet).

The gwNetPackets class makes it easy to send and receive fast raw Ethernet packets between any two computers on a LAN or the Internet. You can also use this class to broadcast to multiple computers on a LAN.

The gwNetData class is a higher-level networking class that handles all the performance and reliability issues of sharing variables and blocks of data with other computers on a LAN or the Internet. The gwNetData class uses an existing gwNetPackets object to perform the data transfers. See the descriptions for the gwNetPackets and gwNetData classes for more information.

For performance, gwNetPackets uses raw UDP "datagram" packets instead of a TCP "stream" connection.

There is no question that networked real-time games (and many other applications that require low-latency networking) should use datagrams instead of stream connections. Datagrams have far greater performance. You should, however, be aware of three drawbacks of using datagram packets:
  1. Datagram packets sent in quick succession over the Internet may sometimes arrive in a different order than they were sent. However, this is not the case on a LAN because there is typically only one path packets can take between computers. (The gwNetData class solves this problem.)
  2. Sometimes datagram packets do not make it to the destination computer. This is because of packet collisions. (The gwNetData class solves this problem.)
  3. No single data chunk can be sent larger than 1448 bytes, and on dialup interfaces you should keep packet size (and thus maximum data block size) just below about 520 bytes so that your application is sure to work efficiently on ISPs that use a small MTU (of 576 bytes). (The gwNetData class makes it easy to work around this problem.)
The gwNetPackets class also allows you to broadcast packets to all computers on a LAN subnet--by specifying the last IP byte as 255. You cannot, however, broadcast packets on the Internet (because no sane Internet gateway would allow it!), nor does this class support multicast, simply because multicast is still not widely supported by ISPs and routers even in 2008.

A single gwNetPackets object opens a specified local interface (local network adapter) to receive packets destined to a certain port number. It also lets you send packets to any IP address and any port number using that interface.

It is easy to implement peer-to-peer communication over a LAN using gwNetPackets and gwNetData. However, there are certain things you must know in order to implement peer-to-peer communications over NAT routers (i.e. across the Internet). These things are addressed below.

Routing basics and Internet connections

All Ethernet packets contain the source IP address, source port number, destination IP address, and destination port number. You can find out what these are in a received packet (see gwNetPackets::Receive ).

Of course, the destination IP address and port number are necessary in the packet to let routers know where to send the packet, and once the packet has arrived in the destination computer, the destination port number tells that computer which application code to send it to (which open socket).

The source IP address, of course, let's the receiver know where the packet came from.

But what is the purpose of the source port number in the packet? Its purpose is to tell the destination computer what port the source computer is listening on for responses to that packet.

Whenever you receive a packet and then want to send a packet back along that same "channel", you should send them using the source port number and IP address you recently saw in packets received via that channel.

You may think that you can ignore the source port (or even IP address, in some cases) in incoming packets and simply send any responses to the IP address and port you know the application on that remote computer was really suppose to be using and listening on (what the application on that computer told it to listen to). This is actually OK when both computers are on the same LAN.

However, many routers will alter the source port and the source IP address in outgoing packets, expect incoming responses to that connection to use those values as their destination, and the router then translates them back to the correct values for the local network. NAT routers do this so that all the computers behind the NAT router will appear as just different ports on t