OpenGL Frequently Asked Questions [3/3]


From           pho@dilow.asd.sgi.com (Paul Ho)
Organization   Silicon Graphics Inc.
Date           14 Jul 1997 00:13:31 GMT
Keywords       OpenGL FAQ
Newsgroups     comp.graphics.api.opengl
Message-ID     <5qbqvb$5eb$4@murrow.corp.sgi.com>

------
Q_3_01:  Where can I find OpenGL source code examples? For instance,
	 where is an example which combines OpenGL with Motif, using
	 the Motif widget?

A:
    You can get the source code examples which are found in the OpenGL
    Programming Guide via anonymous, public ftp from
    ftp://sgigate.sgi.com/pub/opengl/opengl.tar.Z

    Mark Kilgard has created an ftp site for source code, which is part
    of his articles in the X Journal magazine. This includes the GLUT
    toolkit (version 2.0) and OpenGL with Motif examples. The
    directory is ftp://sgigate.sgi.com/pub/opengl/xjournal

    Some contributed source code of useful tools for developing OpenGL
    code can be found on ftp://sgigate.sgi.com/pub/opengl/contrib
    Source code found here includes:
	isfast--compares the performance of OpenGL states 
	samples--more OpenGL program examples
	toogl--helps port IRIS GL code to OpenGL
	xglinfo--display information on X visuals extended for OpenGL
	xscope--examines OpenGL protocol sent to an extended X server 

    Nate Robins <ndr@pobox.com> has a web page that contains OpenGL
    information. In particular, source for the Windows NT/95 version of
    GLUT. The site is http://www.cs.utah.edu/~narobins/opengl.html.

------
Q_3_02:  How do I contribute OpenGL code examples to a publicly
         accessible archive?

A:
    To contribute to the public OpenGL archive, send mail to
    opengl-contrib@sgi.com. Your mail should contain:

	The material to be archived or instructions for obtaining it.
	An announcement suitable for posting to comp.graphics.opengl.

    SGI will place the material in the opengl/contrib directory on
    sgigate.sgi.com and post the announcement to this newsgroup.

    To retrieve something from the archive, use anonymous ftp to
    sgigate.sgi.com. Once connected, cd to the directory OpenGL.
    (Case is significant.) Currently there are two subdirectories:

	doc - Manual pages for OpenGL and related libraries.
	contrib - Contributions from the public.

    Note that all contributions are distributed as-is; neither SGI nor
    the other companies on the OpenGL Architecture Review Board make
    any legally valid claims about the robustness or usefulness of this
    software.

    If you do not have access to anonymous ftp, consider using an
    "ftp-by-mail" server. For information on one such server, send
    mail to ftpmail@decwrl.dec.com with a message body containing only
    the word "help".

------
Q_3_03:  What is the GLUT toolkit? Where do I get it?

A:
    GLUT is a portable toolkit which performs window and event 
    operations to support OpenGL rendering.

    GLUT version 2.0 has:
      o window functions, including multiple windows for OpenGL rendering
      o callback driven event processing
      o sophisticated input devices, including dials and buttons box, 
        tablet, Spaceball(TM)
      o idle routines and timers
      o a simple cascading pop-up menu facility
      o routines to generate wire and solid objects
      o bitmap and stroke fonts
      o request and queries for multisample and stereo windows
      o OpenGL extension query support

    The version 2 functionality is fully backward compatible with the
    version 1 functionality.

    The specification, source code (including FORTRAN bindings), and
    articles for GLUT (Graphics Library Utility Toolkit) is in: URL
    ftp://sgigate.sgi.com/pub/opengl/xjournal/GLUT

    This distribution of GLUT should compile on:
      o DEC Alpha workstation running OSF/1 with Open3D layered product
      o IBM RS/6000 workstations running AIX with OpenGL support
      o SGI workstation running IRIX 5.2 or higher supporting OpenGL
      o Template Graphics Software's OpenGL for Sun workstations
      o Mesa 1.1 for Unix workstations.

------
Q_3_04:  What is the relationship between IRIS GL and OpenGL?
         Is OpenGL source code or binary code compatible with IRIS GL?

A:
    IRIS GL is the predecessor to OpenGL. After other implementors had
    experience trying to port the IRIS GL to their own machines, it was
    learned that the IRIS GL was too tied to a specific window system
    or hardware. Based upon consultations with several implementors,
    OpenGL is much more platform independent.

    IRIS GL is being maintained and bugs will be fixed, but SGI will no
    longer add enhancements. OpenGL is now the strategic interface for
    3-D computer graphics.

    OpenGL code is neither binary nor source code compatible with IRIS
    GL code. It was decided to bite the bullet at this time to make
    OpenGL incompatible with IRIS GL and fix EVERYTHING that made IRIS
    GL difficult to port or use. For example, the gl prefix has been
    added to every command: glVertex(), glColor(), etc.

------
Q_3_05:  Why should I port my IRIS GL application to OpenGL?

A:
    SGI will be maintaining the old IRIS GL, but not enhancing it.
    OpenGL is the API of choice on all new SGI machines.

    OpenGL has no subsets. You can use the same functionality on all
    machines from SGI or from other vendors.

    OpenGL is better integrated with the X Window System than the old
    IRIS GL. For example, you can mix OpenGL and X or Display
    PostScript drawing operations in the same window.

    The OpenGL naming scheme, argument list conventions, and rendering
    semantics are cleaner than those of IRIS GL. This should make
    OpenGL code easier to understand and maintain.

------
Q_3_06: How much work is it to convert an IRIS GL program to OpenGL?
        What are the major differences between them?

A: from Mason Woo (woo@sgi.com) and Debbie Herrington (debbie@portable.com)

    There is a fair amount of work, most of which is in substituting
    for window management or input handling routines, for which the
    equivalents are not OpenGL, but the local window system, such as
    the X Windows System or Windows NT.

    To help ease the way, port to "mixed model" right away, mixing the
    X Window System calls to open and manage windows, cursors, and
    color maps and read events of the window system, mouse and
    keyboard. You can do that now with IRIS GL, if you are running
    IRIX 4.0.

    In the X Window System, display mode choices (such as single or
    double buffering, color index or RGBA mode) must be declared before
    the window is initially opened. You may also substitute for other
    IRIS GL routines, such as using a OSF/Motif menu system, in place
    of the IRIS GL pop-up menus. You should use glXUseXFont(),
    whenever you were using the font manager with IRIS GL.

    OpenGL uses standard, predictable naming conventions, which
    required that all names have been changed from IRIS GL. The OpenGL
    naming scheme uses unique prefixes, suffixes, and capitalization to
    help prevent potential conflicts among application, system, and
    library routine names.

    And all routine names have changed, at least, minimally; for
    example: ortho() is now glOrtho().

    Tables for states such as lighting or line and polygon stipples
    will be gone. Instead of using a def/set or def/bind sequence to
    load a table, you turn on the state with glEnable() and also
    declare the current values for that state.

    Colors are best stored as floating point values, scaled from 0.0 to
    1.0 (0% to 100%). Alpha is fully integrated in the RGBA mode and
    at least source alpha will be available on all OpenGL
    implementations. OpenGL will not arbitrarily limit the number of
    bits per color to 8. Clearing the contents of buffers no longer
    uses the current color, but a special "clearing" color for each
    buffer (color, depth, stencil, and accumulation).

    The transformation matrix has changed. In OpenGL, there is no
    single matrix mode. Matrices are now column-major and are
    post-multiplied, although that does NOT change the calling order of
    these routines from IRIS GL to OpenGL. OpenGL's glRotate*() now
    allows for a rotation around an arbitrary axis, not just the x, y,
    and z axes. lookat() of IRIS GL is now gluLookAt(), which takes an
    up vector value, not merely a twist. There is no polarview() in
    OpenGL, but a series of glRotate*()s and glTranslate*()s can do the
    same thing.

    There are no separate depth cueing routines in OpenGL. Use linear fog.

    Feedback and selection (picking) return values, which are different
    from those found on any IRIS GL implementation. For selection and
    picking, depth values will be returned for each hit. In OpenGL,
    feedback and selection will now be standardized on all hardware
    platforms.

------
Q_3_07:  When using Xlib, how do I create a borderless window?

A: from blythe@sgi.com (David Blythe)

    Essentially you can create the window with override-redirect (see
    man xcreatewindow) which is the sledgehammer approach or you can
    change the _MOTIF_WM_HINTS property to tell the window manager to
    leave your windows undecorated.

    from alex@eagle.hd.hac.com (Alex Madarasz)

    Also of note is that the window manager decorations of any client
    can be turned off by putting something like the following in the
    .Xdefaults file in your home directory - assuming you aren't
    overriding them in your app:

    4Dwm*ClientAppOrClassName*clientDecoration: none

    ( see the 4Dwm / mwm man pages for a full description of this resource )

    ( you must restart the window manager or logout/login for 4Dwm
      resources to take effect )

    "none" will remove all of the window manager decorations - border,
    title bar etc.

------
Q_3_08:  How do I switch between single buffer and double buffer mode?

A: from mjk@sgi.com (Mark Kilgard) and blythe@sgi.com (David Blythe)

    When using OpenGL with X, switching between a double buffered and
    single buffered window can be accomplished by creating a
    "container" X window and creating two subwindows, one with a double
    buffer visual, the other with a single buffer visual. Make sure
    the subwindows are each the full size of their parent window.

    You can then use XRaiseWindow or XLowerWindow to change the
    stacking order of the two subwindows to switch between double
    buffering and single buffering.

    You will need to create a separate context for each of the two
    windows since they have different visual types. You will need to
    make the appropriate window/context pair current when you switch
    modes.

    IRIS GL made it easy to switch between double buffering and single
    buffering. But essentially, IRIS GL implemented the above process
    internally.

------
Q_3_09:  On my machine, it appears that glXChooseVisual is only able
	 to match double-buffered visuals. I want to have more bits
	 of color resolution, so how do I render in single buffer mode?

A: from mjk@sgi.com (Mark Kilgard)

    On mid- to low-end machines with double buffer hardware, you'll
    probably find you get twice as much color resolution using a single
    buffer visual. But if there is no hardware double buffering
    support, the double buffered and single buffered visuals are
    generally the same depth (the back buffer is "carved" out of
    software).

    Search again for a double buffered visual. If you find one, use it
    instead. Call glDrawBuffer(GL_FRONT) though to make sure you are
    drawing to the front buffer (the default for a double buffered
    visual is to draw into the back buffer).

------
Q_3_10:  I've got a 24-bit machine, but my OpenGL windows are not using
	 the full color resolution. What's wrong? My program looks
	 fine on one machine, but the depth buffer doesn't work on
	 another. What's wrong?

A: from mjk@sgi.com (Mark Kilgard) and woo@sgi.com (Mason Woo) 

    An unfortunate (but documented) semantic of glXChooseVisual is that
    if you don't request GLX_RED_SIZE, GLX_GREEN_SIZE, or
    GLX_BLUE_SIZE, glXChooseVisual assumes zero for these parameters
    which means pick the visual with the _smallest_ amount of red,
    green, and blue that matches the other visual attributes. Make
    sure you ask for at least 1 bit of GLX_RED_SIZE, GLX_GREEN_SIZE,
    and GLX_BLUE_SIZE. If these configuration parameters are non-zero,
    it matches the visual with the _largest_ amount of red, green, and
    blue with at least 1 bit of each (probably what you want).

    Similarly, if you don't request GLX_DEPTH_SIZE, you may get a
    visual with zero bits of depth buffer. Some systems may have few
    visuals available, and those visuals all have at least 1 bit of
    depth buffer. On other systems, there may be dozens of visuals
    available, some with zero bits for the depth buffer. In short, if
    hidden surface removal appears to fail, check to see if you have
    explicitly specified any bits of depth buffer you have requested.
    Also check to see what visual you have received.

------
Q_3_11:  What information is available about OpenGL extensions?

A:
    Examples of extensions include vertex arrays (calling several
    vertexes or related data, such as normals or colors, with a single
    function call), blending of constant colors, polygon offset
    (multiple coplanar polygons can be rendered without interaction),

    Procedure names and tokens for OpenGL extensions are either
    suffixed with EXT or a vendor-specific acronym: such as SGI for
    Silicon Graphics machines, or INGR for Intergraph. Also note that
    Silicon Graphics extensions to OpenGL are suffixed to indicate
    whether they will be available on all machines (SGI), on just a
    subset of machines (SGIS), or are very experimental and may become
    unavailable or completely changed (SGIX).

    Vendors are encouraged to add extension information to their
    documentation. For Silicon Graphics, extension information is
    summarized on the glIntro man page.

------
Q_3_12:  How do I make shadows in OpenGL?

A:
    There are no individual routines to control shadows nor an OpenGL
    state for shadows. However, code can be written to render
    shadows.

    from woo@sgi.com (Mason Woo)

	To project a shadow onto a flat plane (such as in the insect
	demo), draw the stippled object, flattened using matrix
	transformations. The easiest way to flatten an object is to
	use the scale function. For example, use glScalef(1., 0., 1.)
	to create from an infinite light shining straight down the y
	axis. A transformation matrix can be used to cast a shadow
	from an infinite or local light source in an arbitrary
	direction. See the article:

	    Thant Tessman, "Casting Shadows on Flat Surfaces,"
	    IRIS Universe, Winter, 1989.

    from shreiner@sgi.com (Dave Shreiner)

	Check out the fast shadow and projective texture multi-pass
	algorithms for producing realistic shadows using texture
	mapping. See the SIGGRAPH paper:

	    Mark Segal, Carl Korobkin, et al. "Fast Shadows and
	    Lighting Effects using Texture Mapping" 1992 SIGGRAPH
	    Proceedings

------
Q_3_13:  How can I use 16 bit X fonts?

A: from James A. ("Jim") Miller

    Here is some code that will load any font into the server and use
    glXUseXFont to build the display lists for you (this does work with
    16 bit fonts, it has been tested on IBM, DEC and SGI machines at an
    OpenGL interop testing). Once your display lists are created for
    each character, you can use the same basic logic to figure out
    which characters are valid (using first, last, firstrow and lastrow
    in the sample code to get : firstchar = 256 * firstrow + first
    lastchar = 256 * lastrow + last ) and use glCallList with those
    characters to print them out.

    static int LoadFont(char *fontName)
    {
	Font id;
	int first, last, firstbitmap, i;
	GLuint base;
	Display *display=0;
	int firstrow, lastrow;
	int maxchars;

	tkGetSystem(TK_X_DISPLAY, &display);

	fontInfo = XLoadQueryFont(display, fontName);
	if (fontInfo == NULL) {
	    return 0;
	}
	id = fontInfo->fid;
	/*
	 * First and Last char in a row of chars
	 */
	first = (int)fontInfo->min_char_or_byte2;
	last = (int)fontInfo->max_char_or_byte2;
	/*
	 * First and Last row of chars, important for multibyte charset's
	 */
	firstrow = (int)fontInfo->min_byte1;
	lastrow = (int)fontInfo->max_byte1;
	/*
	 * How many chars in the charset
	 */
	maxchars = 256 * lastrow + last;
	base = glGenLists(maxchars+1);
	if (base == 0) {
	    return 0;
	}
	/*
	 * Get offset to first char in the charset
	 */
	firstbitmap = 256 * firstrow + first;
	/*
	 * for each row of chars, call glXUseXFont to build the bitmaps.
	 */
	for(i=firstrow; i<=lastrow; i++)
	{
	    glXUseXFont(id, firstbitmap, last-first+1, base+firstbitmap);
	    firstbitmap += 256;
	}
	return base;
    }

------
Q_3_14:  What's in the new GLU 1.2 tesselator?

A: from Mark Kilgard

    Our friends at Digital have the answers:
	http://www.digital.com:80/pub/doc/opengl/opengl_new_glu.html

------
Q_3_15:  Why is my glDrawPixels (or glCopyPixels or glReadPixels) slow?

A: from Allen Akin

    It's not, for the most common cases. After all, similar microcode
    and the same hardware are used for both commands. However, there
    are three issues to keep in mind.

    First, some midrange and low-end SGI graphics adaptors
    (particularly XS, XZ, Elan, and Extreme) transfer ABGR-ordered
    images much faster than they transfer RGBA-ordered images. The
    normal image format in IrisGL was ABGR, while in OpenGL it's RGBA.
    So to achieve the same performance in OpenGL that you did in IrisGL
    on those machines, you need to use ABGR-format images in OpenGL.
    The ABGR extension available in Irix 5.3 and later releases allows
    you to do this. See ``man glintro'' for background information on
    using OpenGL extensions, and ``man gldrawpixels'' for details on
    ABGR. Note that RealityEngine, IMPACT, and all future machines
    will process RGBA data at least as fast as ABGR, so RGBA is the way
    to go for new code.

    Second, some OpenGL pixel data types are faster than others. For
    most machines, unsigned byte RGBA (or ABGR) is the fastest
    full-color type. Unsigned byte and unsigned short are usually the
    fastest gray-scale types. Signed integer types are slower.

    Third, OpenGL pixel operations have a much richer set of features
    than IrisGL, and if any of those features are enabled, then image
    transfer can be significantly slower. Always disable the features
    that you don't need. The following code fragment disables features
    that are likely to make glDrawPixels slow:

	/*
	 * Disable stuff that's likely to slow down glDrawPixels.
	 * (Omit as much of this as possible, when you know in advance
	 * that the OpenGL state will already be set correctly.)
	 * Note that all of these are the default settings, except
	 * for GL_DITHER!
	 */
	glDisable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_DITHER);
	glDisable(GL_FOG);
	glDisable(GL_LIGHTING);
	glDisable(GL_LOGIC_OP);
	glDisable(GL_STENCIL_TEST);
	glDisable(GL_TEXTURE_1D);
	glDisable(GL_TEXTURE_2D);
	glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
	glPixelTransferi(GL_RED_SCALE, 1);
	glPixelTransferi(GL_RED_BIAS, 0);
	glPixelTransferi(GL_GREEN_SCALE, 1);
	glPixelTransferi(GL_GREEN_BIAS, 0);
	glPixelTransferi(GL_BLUE_SCALE, 1);
	glPixelTransferi(GL_BLUE_BIAS, 0);
	glPixelTransferi(GL_ALPHA_SCALE, 1);
	glPixelTransferi(GL_ALPHA_BIAS, 0);

	/*
	 * Disable extensions that could slow down glDrawPixels.
	 * (Actually, you should check for the presence of the proper
	 * extension before making these calls. I've omitted that
	 * code for simplicity.)
	 */

	#ifdef GL_EXT_convolution
	    glDisable(GL_CONVOLUTION_1D_EXT);
	    glDisable(GL_CONVOLUTION_2D_EXT);
	    glDisable(GL_SEPARABLE_2D_EXT);
	#endif

	#ifdef GL_EXT_histogram
	    glDisable(GL_HISTOGRAM_EXT);
	    glDisable(GL_MINMAX_EXT);
	#endif

	#ifdef GL_EXT_texture3D
	    glDisable(GL_TEXTURE_3D_EXT);
	#endif

------
Q_3_16:  How can I used OpenGL with Tcl/Tk?

A:
    TIGER 1.2: ftp://metallica.prakinf.tu-ilmenau.de/pub/PROJECTS/TIGER1.2/
    Tix: http://www.cis.upenn.edu/~ioi/tix/sgi.html

------
Q_3_17:  OpenGL pixel-exact rasterization.

A: from Kurt Akeley

    1) Why is the center for points, lines and bitmaps different than for
       polygon vertices and pixel image positions?

    Let's just consider lines and polygons. Polygons have real area,
    lines have area only because they would otherwise not be visible.
    A polygon is sampled by determining if its area intersects a
    pixel. OpenGL makes this determination by testing a single point
    within each pixel (this way only a single polygon in a 2D mesh can
    contain the sample point). It is symmetric and obvious for this
    point to be in the middle of the pixel, so that's what we chose.

    There were lots of considerations for our choice of line
    rasterization algorithms. We ended up with what we call the
    "diamond-exit rule". Each pixel center is surrounded by a square
    rotated 45 degrees, whose vertexes just touch the 1x1 pixel
    boundaries. If a line segment *exits* this diamond, the pixel is
    rasterized; otherwise it is not. This algorithm generates
    Bresenham lines, does not multiply rasterize pixels at the shared
    vertexes of line segments, and guarantees to generate pixels, no
    matter how convoluted and intricate the path of a sequence of
    segments is. (It has other properties as well, I don't remember
    all the considerations.)

    2) How does translationg 0.375 ensure predicatable rasterization for all
       primitives?

    To generate lines reliably the vertexes must be within the
    diamonds, and to fill polygons reliably, the vertexes must *not* be
    too near the pixel centers. I chose 0.375 as the best compromise,
    as seen in the diagram below ('x' is the center of the pixel, 'O'
    is the point 0.375, 0.375, and the diamond is shown with
    asterisks).

	+------*------+
	|     * *     |
	|    *   *    |
	|   *     *   |
	|  *       *  |
	| *         * |
	|*           *|
	*      x      *
	|*           *|
	| *  O      * |
	|  *       *  |
	|   *     *   |
	|    *   *    |
	|     * *     |
	+------*------+

------
Q_3_18:  Saving OpenGL screen output.

A: from Reto Koradi

    /* OpenGL image dump, written by Reto Koradi (kor@spectrospin.ch) */

    /* This file contains code for doing OpenGL off-screen rendering and
       saving the result in a TIFF file. It requires Sam Leffler's libtiff
       library which is available from ftp.sgi.com.
       The code is used by calling the function StartDump(..), drawing the
       scene, and then calling EndDump(..).
       Please note that StartDump creates a new context, so all attributes
       stored in the current context (colors, lighting parameters, etc.)
       have to be set again beforing performing the actual redraw. This
       can be rather painful, but unfortunately GLX does not allow
       sharing/copying of attributes between direct and nondirect
       rendering contexts. */

    #include <stdio.h>
    #include <stdlib.h>
    #include <X11/Xlib.h>
    #include <X11/Intrinsic.h>
    #include <GL/gl.h>
    #include <GL/glx.h>

    #include <tiffio.h>

    /* X servers often grow bigger and bigger when allocating/freeing
       many pixmaps, so it's better to keep and reuse them if possible.
       Set this to 0 if you don't want to use that. */
    #define KEEP_PIXMAP 1

    static FILE *TiffFileP;
    static int Orient;
    static int ImgW, ImgH;
    static Bool OutOfMemory;
    static Display *Dpy;
    static Pixmap XPix = 0;
    static GLXPixmap GPix = 0;
    static GLXContext OldCtx, Ctx;
    static float OldVpX, OldVpY, OldVpW, OldVpH;

    static void
    destroyPixmap(void)
    {
      glXDestroyGLXPixmap(Dpy, GPix);
      GPix = 0;
      XFreePixmap(Dpy, XPix);
      XPix = 0;
    }

    static int
    xErrorHandler(Display *dpy, XErrorEvent *evtP)
    {
      OutOfMemory = True;
      return 0;
    }

    int
    StartDump(char *fileName, int orient, int w, int h)
    /* Prepare for image dump. fileName is the name of the file the image
       will be written to. If orient is 0, the image is written in the
       normal orientation, if it is 1, it will be rotated by 90 degrees.
       w and h give the width and height (in pixels) of the desired image.
       Returns 0 on success, calls RaiseError(..) and returns 1 on error. */
    {
      Widget drawW = GetDrawW();  /* the GLwMDrawA widget used */
      XErrorHandler oldHandler;
      int attrList[10];
      XVisualInfo *visP;
      int n, i;

      TiffFileP = fopen(fileName, "w");
      if (TiffFileP == NULL) {
	RaiseError("could not open output file");
	return 1;
      }

    #if KEEP_PIXMAP
      if (GPix != 0 && (w != ImgW || h != ImgH))
	destroyPixmap();
    #endif

      Orient = orient;
      ImgW = w;
      ImgH = h;

      Dpy = XtDisplay(drawW);

      n = 0;
      attrList[n++] = GLX_RGBA;
      attrList[n++] = GLX_RED_SIZE; attrList[n++] = 8;
      attrList[n++] = GLX_GREEN_SIZE; attrList[n++] = 8;
      attrList[n++] = GLX_BLUE_SIZE; attrList[n++] = 8;
      attrList[n++] = GLX_DEPTH_SIZE; attrList[n++] = 1;
      attrList[n++] = None;
      visP = glXChooseVisual(Dpy,
	  XScreenNumberOfScreen(XtScreen(drawW)), attrList);
      if (visP == NULL) {
	RaiseError("no 24-bit true color visual available");
	return 1;
      }

      /* catch BadAlloc error */
      OutOfMemory = False;
      oldHandler = XSetErrorHandler(xErrorHandler);

      if (XPix == 0) {
	XPix = XCreatePixmap(Dpy, XtWindow(drawW), w, h, 24);
	XSync(Dpy, False);  /* error comes too late otherwise */
	if (OutOfMemory) {
	  XPix = 0;
	  XSetErrorHandler(oldHandler);
	  RaiseError("could not allocate Pixmap");
	  return 1;
	}
      }

      if (GPix == 0) {
	GPix = glXCreateGLXPixmap(Dpy, visP, XPix);
	XSync(Dpy, False);
	XSetErrorHandler(oldHandler);
	if (OutOfMemory) {
	  GPix = 0;
	  XFreePixmap(Dpy, XPix);
	  XPix = 0;
	  RaiseError("could not allocate Pixmap");
	  return 1;
	}
      }

      Ctx = glXCreateContext(Dpy, visP, NULL, False);
      if (Ctx == NULL) {
	destroyPixmap();
	RaiseError("could not create rendering context");
	return 1;
      }

      OldCtx = glXGetCurrentContext();
      (void) glXMakeCurrent(Dpy, GPix, Ctx);

      return 0;
    }

    static int
    writeTiff(void)
    {
      TIFF *tif;
      int tiffW, tiffH;
      int rowsPerStrip, bufSize, rowI;
      unsigned char *buf;
      int res;

      tif = TIFFFdOpen(fileno(TiffFileP), "output file", "w");
      if (tif == NULL) {
	RaiseError("could not create TIFF file");
	return 1;
      }

      if (Orient == 0) {
	tiffW = ImgW;
	tiffH = ImgH;
	bufSize = 4 * ((3 * tiffW + 3) / 4);
	glPixelStorei(GL_PACK_ALIGNMENT, 4);
      } else {
	tiffW = ImgH;
	tiffH = ImgW;
	bufSize = 3 * tiffW;
	glPixelStorei(GL_PACK_ALIGNMENT, 1);
      }

      rowsPerStrip = (8 * 1024) / (3 * tiffW);
      if (rowsPerStrip == 0)
        rowsPerStrip = 1;

      TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, tiffW);
      TIFFSetField(tif, TIFFTAG_IMAGELENGTH, tiffH);
      TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
      TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
      TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
      TIFFSetField(tif, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
      TIFFSetField(tif, TIFFTAG_DOCUMENTNAME, "My Name");
      TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "My Description");
      TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
      TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
      TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);

      buf = malloc(bufSize * sizeof(*buf));

      res = 0;
      for (rowI = 0; rowI < tiffH; rowI++) {
	if (Orient == 0)
	  glReadPixels(0, ImgH - 1 - rowI, ImgW, 1,
	      GL_RGB, GL_UNSIGNED_BYTE, buf);
	else
	  glReadPixels(rowI, 0, 1, ImgH,
	      GL_RGB, GL_UNSIGNED_BYTE, buf);

	if (TIFFWriteScanline(tif, buf, rowI, 0) < 0) {
	  RaiseError("error while writing TIFF file");
	  res = 1;
	  break;
	}
      }

      free(buf);

      TIFFFlushData(tif);
      TIFFClose(tif);

      return res;
    }

    int
    EndDump(void)
    /* Write current image to file. May only be called after StartDump(..).
       Returns 0 on success, calls RaiseError(..) and returns 1 on error. */
    {
      int res;

      res = writeTiff();
      (void) fclose(TiffFileP);

      (void) glXMakeCurrent(Dpy, XtWindow(GetDrawW()), OldCtx);

    #if KEEP_PIXMAP
    #else
      destroyPixmap();
    #endif

      glXDestroyContext(Dpy, Ctx);

      return res;
    }

------
Q_3_19:  No Logicop in RGB for OpenGL?

A:
    - from Kurt Akeley

	The best solution that I know of is to save and restore the
	portion of the 3D image that will be damaged by the 2D
	rendering. If the machine supports double buffering, you can
	use the back buffer for this purpose. Use glCopyPixels to do
	the transfers. Otherwise use glReadPixels to save the region
	and glDrawPixels to restore it.

	Note that an advantage to the save/restore approach is that you
	have control of the colors of your 2D rendering. Using logical
	operations provides much less control.

    - From Brian Paul (brianp@ssec.wisc.edu)

	There is an extension EXT_blend_logic_op, available in many
	implementations which allows you to do what you need. Use
	#ifdef GL_EXT_blend_logic_op / #endif in your source code to
	test for the extension at compile time and
	glGetString(GL_EXTENSIONS) to test for it at run-time. Then
	the call to glBlendEquationEXT(GL_LOGIC_OP) will tell GL that
	blending is to be done with the logic op specified by
	glLogicOp().

	If neither overlay planes nor this extension are available, you
	may have to resort to your window system's XOR drawing
	facility. If you're using X, you can set your GC's function to
	GXxor and use the Xlib drawing functions. If you do this be
	sure to synchronize the GL and X drawing with glXWaitX() and
	glXWaitGL().

------
Q_3_20:  Why does the raster position get clipped by the viewing volume?

A: from Kurt Akeley

    A vertex on the Z=0 plane in clip coordinates is projected to
    infinity if not clipped. Since OpenGL is a 3D library, such a
    vertex is always a possibility, even in a 2D application. Thus the
    raster position is always clipped.

------
Q_3_21:  Is texturing really enabled?

A:
    One thing to point on (in Section 3.8.1 "Texture Minification,
    Mipmapping" in the OpenGL 1.1 spec) is "If texturing is enabled at the
    time a primitive is rasterized and if the set of arrays 0 through p is
    incomplete, based on the dimensions of array 0, then it is as if
    texture mapping were disabled.  The set of arrays 0 through p is
    incomplete if the internal formats of all the mipmap arrays were not
    specified with the same symbolic constant, or if the border widths of
    the mipmap arrays are not the same, or if the dimensions of the mipmap
    arrays do not follow the sequence described above.  Arrays indexed
    greater than p are insignificant."



------
end of file