CGGL - OpenGL Hooks in Common Graphics

CGGL allows you to draw OpenGL graphics on any Common Graphics window
or other CG stream.  CGGL itself has a very small API that lets you
establish a CG stream as the current OpenGL drawing destination.  Then
you can insert pure OpenGL code into redisplay-window methods to do
the actual drawing.  You can also add methods on mouse-left-down and
virtual-key-down, for example, just as you would for any other CG
window, except adding OpenGL code to them for rotating the image and
so on.

The CGGL code is written on top of Microsoft's WGL facility for doing
OpenGL in Microsoft Windows, so you shouldn't need to call WGL
functions directly.  And the file opengl.cl (included with CGGL)
defines foreign functions for the entire OpenGL API, so you can call
OpenGL functions as lisp functions directly.

To load CGGL, simply load the file load.cl into ACL 6.2 or 7.0 (with
CG present).

Two examples are included (and are loaded by load.cl).  cggltest.cl
contains a very basic example that draws a multicolored triangle; this
provides a small code example to point out what the various pieces of
code are that you need to write.  cgteapot.cl is a much fancier
example that draws a three-dimensional teapot that rotates when you
press the arrow keys on the keyboard.  You can run these examples with
the forms (color-triangle) and (red-teapot).

If you would like to draw ordinary CG graphics over top of what OpenGL
draws, you would need to do that in a redisplay-window :after method
that specializes on your window class.  That may not often be useful
due to not knowing the 2-D CG coordinates to which OpenGL will map a
3-D drawing, but for example adding the following method to the teapot
example would draw a bit of text toward the bottom of the window,
safely under the teapot:

(defmethod redisplay-window :after ((window cg-teapot) &optional box)
  (declare (ignore box))
  (with-boxes (text-box)
    (nmake-box text-box 0
      (floor (* .9 (interior-height window)))
      (interior-width window)(interior-height window))
    (draw-string-in-box window " Press the Arrow Keys! "
                        nil nil text-box :center :top nil t)))

The rest of this document describes the several exported CGGL symbols.
The symbols are in the cggl package, and the cg-user package will use
the cggl package when CGGL is loaded.  If you find the CGGL API to be
insufficient, you can modify the CGGL source code yourself; it's
rather small and fairly well documented internally.  We do not supply
any documentation for OpenGL itself.

Of the CGGL symbols, you probably will need only the cggl-mixin class
and the (setf current-cggl-stream) function.

------------------------------------------------------------
cggl-mixin

[class]
  
You must mix this class into any CG stream class that you would like
to use for OpenGL.  You can use OpenGL on windows as well as on
bitmap-streams and printer streams.

Example:

(defclass my-opengl-window (cggl-mixin frame-window)())

------------------------------------------------------------
current-cggl-stream ()

Returns the CG stream that is the current OpenGL drawing destination,
or nil if no destination has been set or the current destination is
something other than a CG window.

------------------------------------------------------------
(setf current-cggl-stream)(value)

[function]
  
Call this function to specify that a particular CG stream should start
receiving all OpenGL output.  Typically you would call this at the top
of any redisplay-window method that calls OpenGL functions.  (This
function does nothing if the stream is already the current OpenGL
destination.)

Example:

(setf (current-cggl-stream)
      (make-window :window-one
        :class 'my-opengl-window))

------------------------------------------------------------
exit-cggl ()

[function]
  
Call this function if desired to clean up all OpenGL resources that
have been used in cg streams.  We expect that it's not necessary to
call this function.

------------------------------------------------------------
cggl-double-buffering (stream cg-stream)

[overridable generic function]

By default, any CG stream that is visible on the screen will use WGL's
double-buffering option.  This eliminates flashing by drawing all
content on a memory bitmap (the "second buffer") and then copying to
the visible stream at the end.  This could be overridden by defining a
cggl-double-buffering method that returns different values for your
own subclasses.  The default methods return true for windows and nil
for other CG streams, which is likely appropriate for almost all
applications.

When cggl-double-buffering returns true, a built-in redisplay-window
:after method will call wgl:SwapBuffers to copy the memory buffer to
the visible window for you.  So you don't need to call that.

Example:

(defmethod cggl-double-buffering ((stream my-opengl-window))

  ;; Return nil to disable the usual double-buffering.
  ;; (You probably DO want the double-buffering though.)
  nil)

Side note: In 7.0.final, CG has a new double-buffered option that you
can turn on for any CG window.  You should not use this with a window
that's to be used for OpenGL, and instead use WGL's double-buffering
which is enabled by default.

------------------------------------------------------------
pixel-format-descriptor-custom-values (stream)

[overridable generic function]

The Microsoft WGL functionality includes a PIXELFORMATDESCRIPTOR
structure where you can specify various options for an OpenGL stream.
CGGL fills this in with options that should work fine for most
applications.  But if you need to use different values and are
familiar with WGL, then you can write a
pixel-format-descriptor-custom-values method that overrides the
default options.

The method should return a plist of option names and their values.
The option names are keywords that correspond to Microsoft's WGL
names, and may be any of the following:

:nVersion :dwFlags :iPixelType :cColorBits
:cRedBits :cRedShift :cGreenBits :cGreenShift
:cBlueBits :cBlueShift :cAlphaBits :cAlphaShift
:cAccumBits :cAccumRedBits :cAccumGreenBits :cAccumBlueBits
:cDepthBits :cStencilBits :cAuxBuffers :iLayerType
:bReserved :dwLayerMask :dwVisibleMask :dwDamageMask

The default method returns nil, which means to default everything to
CGGL's internal defaults.  If you define a method yourself, then each
option that it specifies will override the default for that option,
and other options will still use CG's internal defaults.

Example:

Here is an example that would be redundant with the built-in defaults
for a double-buffered window.  (If cggl-double-buffering returns nil
for the stream, then the PFD_DOUBLEBUFFER flag would NOT be included
by default.  And a bitmap-stream passes PFD_DRAW_TO_BITMAP instead of
PFD_DRAW_TO_WINDOW by default, while a printer uses neither.)  So if
you want values that are different from any of these, then you could
define a similar method that includes only the differing values.

(defmethod pixel-format-descriptor-custom-values ((stream my-opengl-window))
  (list :nVersion 1
        :dwFlags (logior wgl:PFD_DRAW_TO_WINDOW
                         wgl:PFD_DOUBLEBUFFER
                         wgl:PFD_SUPPORT_OPENGL)
        :iPixelType wgl:PFD_TYPE_RGBA
        :cColorBits (bits-per-pixel (screen *system*))
        :cDepthBits (bits-per-pixel (screen *system*))
        :iLayerType wgl:PFD_MAIN_PLANE))

------------------------------------------------------------
That's it!  Refer to the examples in cggltest.cl and cgteapot.cl to
see how to integrate calls to the above functions with OpenGL code in
a CG application.

------------------------------------------------------------
