9.3 Polygons

Particularly in three dimensions, we very often want to represent our critters by colored polygons, perhaps by just one polygon, perhaps by a few, or perhaps by a whole mesh of them. Computer graphics systems draw polygons in an entirely different way from how they draw bitmaps. A bitmap is based on discrete pixel-by-pixel information, while a polygon is based on coordinates in continuous space. In drawing a polygon, we convert its space coordinates into pixel coordinates, use fill algorithms to color it in, and use line-drawing algorithms to draw its edges.

Windows has a built-in CDC::Polygon(POINT * vertices, int vertexcount) method which makes it easy to rapidly draw polygons on the screen. Our cPolygon class is designed to create polygon structures that can take advantage of this function call.

Initializing and decorating a polygon

We can create an empty polygon with the default polygon constructor cPolygon(), and then we can put some structure onto it by using one of our special mutators. Note that as usual you can leave out the trailing arguments which have default values defined.

void setRegularPolygon(int vertexcount); 
void setStarPolygon(int vertexcount, int step); 
void setRandomStarPolygon(int mincount, int maxcount); 
void setRandomRegularPolygon(int mincount, int maxcount); 
void setRandomAsteroidPolygon(int mincount = 5, int maxcount = 30, 
   Real spikiness = 0.3); 

The setRegularPolygon and setStarPolygon mutators produce polygons with a user-selected vertexcount. The step argument to the star polygon controls the kind of star that is drawn. In general, choosing a step smaller than the vertex count which has no divisors in common with the vertex count produces the nicest stars. At present the stars look good in cGraphicsMFC, but cGraphicsOpenGL still needs to be tweaked to draw them properly.

The setRandomRegularPolygon and the setRandomStarPolygon are methods for randomly making a regular or a star polygon with its vertex count and its radius within specified ranges.

The Spacewar game is something like the traditional Asteroids. So it will be useful to have some setRandomAsteroidPolygon to create random irregular polygons. To make the asteroid polygons look solid, we add their vertices in successive counterclockwise order ? if we add them out of order we'll get something like a star. Stars look nice if they're regular, but an irregular star just looks like a scribble. We use the spikiness parameter to control the difference between minimum radius and maximum radius used for the various asteroid vertices.

Another approach, which we use when we want a particular shape, is to use the cPolygon(n) constructor and then use n calls to setVertex (int n, cVector v) to build up the polygon a step at a time. Note that by default, we assume we have a closed polygon in which the last point is automatically connected to the first point.

Once we have a polygon, we can adjust its interior with various mutators that you find in polygon.h. One easy way to vary a polygon sprite is to call the randomize method with the MF_ flags defined in polygon.h. For instance the ppolygon->randomize(cPolygon::MF_COLOR) will randomize the ppolygon fill color, and the cPolygon::MF_ALL flag will randomize everything.

We can adjust the lines around the edges of the polygon with the _edged and _reallinewidth fields. The _reallinewidth field controls the ratio of the thickness of the line to the polygon's radius. A value of, say, 0.2 will give a fat line. We state this quantity as a real number ratio rather than a pixel width so that the polygon will still have the same appearance when drawn at different size scales. But if the converted pixel width of the line would be less than one, we still draw a line of pixel width one, assuming that _edged is TRUE. Drawing lines of width greater than one slows the Windows Polygon function down inordinately, so we recommend sticking to the default _reallinewidth of 0.0, which will produce a line one pixel in width, which is what Windows really 'prefers' to draw. (spritepolygon.h also has a #define for a name for 0.0 to use in this 'line width' context: LW_ONEPIXEL.)

As a non-standard extra, we can also draw dots at our polygon vertices, using the _dotted and _realdotradius fields. The _realdotradius field controls the ratio of the radius of the vertex dots of the polygon's radius. A value of, say, 0.2 will give fat dots. We state this quantity as a real number ratio rather than a pixel width so that the polygon will still have the same appearance when drawn at different size scales. The dots look quite nice; they are drawn after the polygon so they seem to sit on top of it. We have the option of filling the dots or not, and of selecting their fill colors. Dots aren't implemented for OpenGL.

A related class is cSpriteCircle, which is simply a cPolygon with some static int cSpriteCircle::CIRCLESLICES number of sides. If you don't make the 'circles' too big, a reasonable value for CIRCLESLICES is 16.

We could have implemented cSpriteCircle to have an imagedraw that calls something like an ellipse method, but it was quicker and easier to just treat the circles as many-sided polygons.

Polygons in 3D

There are some differences between the implementations of cGraphicsOpenGL and cGraphicsMFC::drawpolygon. As of August, 2002, the dots only show up with cGraphicsMFC, and the star-shaped polygons aren't as nicely drawn in cGraphicsOpenGL. These are 'bad' differences that could be fixed.

A 'good' difference between the two graphics implementations of drawpolygon is that, to enhance the three-dimensionality of the view with OpenGL, the cGraphicsOpenGL actually draws a polygon as a thick prism, that is, as a base polygon with vertical sides extending upwards to an identical cap polygon. The exact thickness of the prism can be controlled via the cSprite field Real _prismdz, and this field can in turn be controlled either directly or by setting the cCritter _defaultprismdz field before adding the sprite. By default the various child critter classes use the following _prismdz values.

Real cSprite::WALLPRISMDZ = 0.75; 
Real cSprite::PLAYERPRISMDZ = 0.5; 
Real cSprite::CRITTERPRISMDZ = 0.3; 
Real cSprite::BULLETPRISMDZ = 0.2; 
Real cSprite::MAXPRISMDZ = 1.0; 

Thus, if you open up Dambuilder in the 3D view, you'll see the walls as taller than the player, the player as taller than the other critters, and the bullets as the thinnest of all.

    Part I: Software Engineering and Computer Games
    Part II: Software Engineering and Computer Games Reference