Notes on the GLU polygon tesselation facility implemented by Bogdan Sikorski...



The tesselation module is provided under the same terms as the Mesa
package.

This is the first release of polygon tesselation code for Mesa.
It was written during my very little free time, so lets name it:
"its not perfect". If someone hates pointers, don't look at the code.
I preffer dynamic allocation versus static. But _all_ ideas, suggestions,
bug reports and fixes are welcome (if You want, also flames). I am aware
that many things could have been written using better techniques, but time
that I could devote to this library was very limited. It is not well commented,
excuse me. Also I am thinking of continuing working on this code to improve,
fix and polish it. And make it as compliant as possible to the OpenGL, so
software ports from OpenGL to Mesa will work correctly. If You know of any
differences in behaviour, expected input/output between Mesa tesselation library
and OpenGL, please send me a note. I explain later on why I am not
confident with this code.

I tried to be fully compliant with the OpenGL routines. By "tried" I mean that
up to my knowledge it behaves as OpenGL tesselation routines. Just recently
I began to experiment with OpenGL (actually only Mesa), and also have
no access to any machine providing official implementation of OpenGL,
nor access to books (particulary Addison-Wesley publications). Thus my
knowledge on how the original tesselation code works, what kind of data
it expects etc. is based _only_ on the publicly available documentation
provided by SGI. Namely:

* "The OpenGL Graphics System Utility Library" by K.P.Smith
	(Silicon Graphics, 1992)
* "The OpenGL Graphics Interface" by M.Segal and K.Akeley
	(Silicon Graphics, 19??)
* "OpenGL and X, Part 1: Introduction" by M.J.Kilgard
	(Silicon Graphics, 1994)
* "OpenGL and X, Part 2: Using OpenGL with Xlib" by M.J.Kilgard
	(Silicon Graphics, 1994)
* "OpenGL Graphics with the X Window System" by P.Karlton
	(Silicon Graphics, 1993)
* Online Docs - Appendix C of OpenGL Programming Guide, Polygon Tesselation
	 (partial text cut and sent by e-mail)


The tesselation routines use slightly different prototypes than the ones
specified in the mentioned above publications. The _only_ differences are
the enumeration types which are not GLenum, but are GLUenum. So the
implemented routines have following prototypes:

GLUtringulatorObj *gluNewTess(void);

void gluTessCallback(GLUtriangulatorObj *,GLUenum,void (*)());
                                          ^^^^^^^
void gluBeginPolygon(GLUtriangulatorObj *);

void gluTessVertex(GLUtriangulatorObj *,GLdouble [3],void *);

void gluNextContour(GLUtriangulatorObj *,GLUenum);
                                         ^^^^^^^
void gluEndPolygon(GLUtriangulatorObj *);

const GLubyte *gluErrorString(GLUenum);
                              ^^^^^^^
	prototypes for callback functions:

void <begin>(GLUenum);
             ^^^^^^^
void <edgeFlag>(GLboolean);
void <vertex>(void *);
void <end>(void);
void <error>(GLUenum);
             ^^^^^^^

The begin callback will be called only with GLU_TRIANGLES. No support
for traingle fans or strips yet.	 

In case of errors an internal error variable is set to the appropiate 
error enum values (GLU_TESS_ERROR?). Initially it is set to GLU_NO_ERROR.
The OpenGL library provides 8 error conditions, the tesselation code
of Mesa provides 9. They are:

GLU_TESS_ERROR1: missing gluEndPolygon                  /* same as OpenGL */
GLU_TESS_ERROR2: missing gluBeginPolygon                /* same as OpenGL */
GLU_TESS_ERROR3: misoriented contour                    /* not used in Mesa
                         in OpenGL is bad orientation or intersecting edges */
GLU_TESS_ERROR4: vertex/edge intersection               /* same as OpenGL */
GLU_TESS_ERROR5: misoriented or self-intersecting loops /* same as OpenGL */
GLU_TESS_ERROR6: coincident vertices                    /* same as OpenGL */
GLU_TESS_ERROR7: colinear vertices                      /* OpenGL's illegal data */
GLU_TESS_ERROR8: intersecting edges                     /* same as OpenGL */
GLU_TESS_ERROR9: not coplanar contours                  /* new for Mesa */

The Mesa tesselation code ignores all data and calls after detecting an error
codition. This means that a _new_ tesselation object must be used for further
triangulations. Maybe this is too restrictive, and will be lifted in
future versions.

The tesselation code completely ignores the type parameter passed in
gluNextContour. It also doesn't check if the passed parameter is a legal
enum value - ignores silently (maybe at least this should be checked).
The reason I chose this behaviour is based on what I read in the 
beforementioned documents. I cite:

"....
void gluNextContour(GLUtriangulatorObj *tessobj, GLenum type);

Marks the beginning of the next contour when multiple contours make up the
boundary of the polygon to be tessellated. type can be GLU_EXTERIOR,
GLU_INTERIOR, GLU_CCW, GLU_CW, or GLU_UNKNOWN. These serve only as 
to the tessellation. If you get them right, the tessellation might
go faster. If you get them wrong, they're ignored, and the tesselation still
works.
....."

I hope You agree with me that my decision was correct. Mesa tesselation
_always_ checks by itself the interrelations between contours. Just as if
all contours were specified with the type GLU_UNKNOWN.

One of OpenGL's policy is not to check all error conditions - rely sometimes
that the user "got things right". This is justified, since exhausting
error checking is timeconsuming, and would significantly slow down
a correct application. The Mesa tesselation code assumes only _one_ condition
when triangulating - all vertices in a contour are planar. This is _not_
checked for correctness. Trying to tesselate such objects will lead to
unpredictable output.

And now we arrive to the moment where I would like to list the required
(but checked for) conditions for triangulation, as well as summarize the
library:

* all contours in a single tesselation cycle _must_ be coplanar - if not
	an error is raised (and if provided a call to the error callback
	is made)
* the contours can be passed in _any_ order, exteriors and holes can be
	intermixed within a tesselation cycle and the correct hierarchy
	will be determined by the library; thus specifying first holes then
	exteriors, then holes within holes form a valid input.
* a hole within a hole is consider to be a yet another exterior contour
* multiple exterior contours (polygons) can be tesselated in one cycle;
	_but_ this significantly degrades performance since many tests will be
	performed for every contour pair; if You want triangulation to be fast
	tesselate a single polygon (with possible holes) one at a time.
* orientation of exterior contours is arbitray, but if it has holes,
	all interior holes of this particular exterior contour _must_ have an
	opposite orientation.
* the output triangles have the same orientation as the exterior contour
	that forms them
* each triangle is "enclosed" within the begin and end callbacks;
	this is not efficent, but was made on purpose; so if triangulation
	results in 2 triangles the following callbacks will be made in such
	order:
	<begin>(GLU_TRAINGLES)
	<vertex>(...) /* 3 vertices of first triangle */
	<vertex>(...)
	<vertex>(...)
	<end>()
	<begin>(GLU_TRAINGLES)
	<vertex>(...) /* 3 vertices of second triangle */
	<vertex>(...)
	<vertex>(...)
	<end>()
	Of course only when begin, vertex, and end callback were provided,
	otherwise no output is done (actually tesselation does not take place).
* You will notice that some output traingles are very "thin"; there
	exist possible several ways to traingulate a polygon, but "smart" code
	avoiding such cases would require time to write, and will impact on
	execution speed.
* like OpenGL, no new vertices are introduced during triangulation
* if the edgeflag callback is provided it will be called whenever
	the just-about-to be output vertex begins a different type of edge
	than the previous vertices; always before the first output a call
	is made with GL_TRUE, to allow synchronization.
* all intermediate computations are done using GLdouble type, and comparisons
	are biased with a precision value (EPSILON defined in tess.h)
* the point_in_poly function is my adaptation of code from the
	comp.graphics.alg newsgroup FAQ (originally written by Mr. Wm. Randolph
	Franklin, modified by Scott Anguish).
* the edge_edge_intersect test is also an adopted code from comp.graphics.alg
	newsgroup FAQ
* the general idea for traingulation used in this library is described in
	the book "Computational Geometry in C" by Joseph O'Rourke.


Excuse my English, its not my mother tongue. I should be available for some
time uner the following e-mail address. But For how long I am not certain.
Once I am settled in my new place, I'll post on the Mesa mailing list
my new address.

(PS: today is my last day of work here, I'm changing my job).

Bogdan. ( bogdan@dia.unisa.it )

Apr 28, 1995.