Page 449


appendix G

OpenGL for Windows

G.1 OpenGL Auxiliary

Table G.1OpenGL auxiliary function listing

Name Description
auxIdleFunc(...) Specifies the function to be called whenever no events are pending.
auxInitDisplayMode(...) Specifies the various characteristics of the display mode that OpenGL and the auxiliary library will use.
auxInitPosition(...) Initializes the windows start-up position parameters.
auxInitWindow(...) Opens a window with the characteristics specified in function calls to auxInitDisplayMode and auxInitPosition.
auxKeyFunc(...) Specifies the function that is called whenever a specific key is pressed during display of an auxiliary model.
auxMainLoop(...) Specifies the display function that needs to be called whenever the display is to be updated.
auxMouseFunc(...) Specifies that this function is called whenever a mouse event occurs during auxiliary display.
auxReshapeFunc(...) Specifies the function that will be called whenever the auxiliary windows are resized or moved.
auxSetOneColor(...) Loads the index in the color map with the given values.
auxSolidCone(...) Draws a surfaced three-dimensional cone.
auxSolidCube(...) Draws a surfaced three-dimensional cube.
auxSolidCylinder(...) Draws a surfaced three-dimensional cylinder.
auxSolidDodecahedron(...) Draws a surfaced three-dimensional dodecahedron (similar to a sphere).
auxSolidIcosahedron(...) Draws a surfaced three-dimensional icosahedron (similar to a sphere).
auxSolidOctahedron(...) Draws a surfaced three-dimensional octahedron.
auxSolidSphere(...) Draws a surfaced three-dimensional sphere.
auxSolidTeapot(...) Draws a surfaced three-dimensional teapot.
auxSolidTetrahedron(...) Draws a surfaced three-dimensional tetrahedron.
auxSolidTorus(...) Draws a surfaced three-dimensional solid torus.
auxWireCone(...) Draws a wireframe three-dimensional cone.
auxWireCube(...) Draws a wireframe three-dimensional cube.
auxWireCylinder(...) Draws a wireframe three-dimensional cylinder.
auxWireDodecahedron(...) Draws a wireframe three-dimensional dodecahedron (similar to a sphere).
auxWireIcosahedron(...) Draws a wireframe three-dimensional icosahedron (similar to a sphere).
auxWireOctahedron(...) Draws a wireframe three-dimensional octahedron.
auxWireSphere(...) Draws a wireframe three-dimensional sphere.
auxWireTeapot(...) Draws a wireframe three-dimensional teapot.
auxWireTetrahedron(...) Draws a wireframe three-dimensional tetrahedron.
auxWireTorus(...) Draws a wireframe three-dimensional solid torus.

For more specific information on OpenGL auxiliary functions, consult the OpenGL Programming Guidethe "red book." (See Neider, Jackie.)


G.2 OpenGL functions and extended functions

Table G.2 OpenGL functions

Name Description
glAccum(...) Performs operations on the accumulation buffer.
glAddSwapHintRectWIN(...) Specifies a set of rectangles that are to be used and copied by SwapBuffers.
glAlphaFunc(...) Specifies how alpha blending works by picking a testing function and setting testing parameters.
glArrayElementEXT(...) Specifies an index into an array when indexing vertex information.
glBegin(...) Begins a new primitive or primitive group when rendering a model.
glBitmap(...) Draws a specified bitmap.
glBlendFunc(...) Specifies blending function pixel arithmetic.
glCallList(...) Executes a given display list that has been previously stored.
glCallLists(...) Executes a roster of display lists that have been previously stored.
glClear(...) Clears a specified buffer within a given viewport.
glClearAccum(...) Clears values in an accumulation buffer using specified values.
glClearColor(...) Specifies the color to use when clearing buffers.
glClearDepth(...) Specifies the clear value for the depth buffer (z-buffer).
glClearlndex(...) Specifies the clear value for the color index buffer.
glClearStencil(...) Specifies the clear value for the stencil buffer.
glClipPlane(...) Specifies additional planes to be used when clipping a model's geometry.
glColor(...) Specifies a color when drawing a model.
glColorMask(...) Enables or disables the writing of frame buffer color components.
glColorMaterial(...) Causes a material color to track the current color based on various lighting parameters.
glColorPointerEXT(...) Defines an array of colors.
glCopyPixels(...) Copies a set of pixels into the frame buffer.
glCullFace(...) Specifies whether front and back facing polygon faces can be culled.
glDeleteLists(...) Deletes a contiguous group of display lists.
glDepthFunc(...) Specifies the value used for depth buffer comparison.
glDepthMask(...) Enables or disables writing into the depth buffer.
glDepthRange(...) Specifies the mapping of z-depth values from normalized coordinates into window coordinates.
glDrawArraysEXT(...) Specifies multiple primitives to render.
glDisable(...) Disables and enables various OpenGL modeling features.
glDrawBuffer(...) Specifies which color buffers are to be drawn into.
glDrawPixels(...) Writes a block of pixels into the frame buffer.
glEdgeFlag(...) Specifies the current edge flag. Edge flags indicate whether the current edge is a boundary or a nonboundary.
glEdgeFlagPointerEXT(...) Defines an array of edge flags.
glEnable(...) Enables or disables various OpenGL capabilities.
glEvalCoord(...) Evaluates enabled one and two-dimensional maps.
glEvalMesh(...) Computes one and two-dimensional grids of points or lines.
glEvalPoint(...) Generates and evaluates a single point in a mesh.
glFeedbackBuffer(...) Controls the feedback mode by specifying buffer and vertex return information.
glEnd(...) Ends a glBegin block of primitives.
glEndList(...) Finishes declaration of a display list.
glFinish(...) Blocks all operations until GL rendering is completed.
glFlush(...) Forces execution of GL commands in finite time.
glFog(...) Specifies various parameters when modeling with fog.
glFrontFace(...) Defines front and back facing polygons.
glFrustum(...) Multiplies the current matrix by a perspective transformation matrix that defines the viewing volume.
glGenLists(...) Generates a set of empty display lists.
glGet(...) Returns a parameter from the current model.
glGetBooleanv(...) Returns the value or values of a given parameter.
glGetClipPlane(...) Returns the coefficients of the specified clipping plane.
glGetDoublev(...) Returns the value or values of a given parameter.
glGetError(...) Returns current error information.
glGetFloatv(...) Returns the value or values of a given parameter.
glGetLight Returns the light source parameter values.
glGetlntegerv(...) Returns the value or values of a given parameter
glGetMap(...) Returns function evaluator parameters.
glGetMaterial(...) Returns selected material parameters.
glGetPixelMap(...) Returns the specified pixel map.
glGetPolygonStipple(...) Returns the polygon stipple pattern.
glGetString(...) Returns a string describing the current GL connection.
glGetTexEnv(...) Returns texture environment parameters.
glGetTexGen(...) Returns texture coordinate generation parameters.
glGetTexImage(...) Returns a texture image.
glGetTexLevelParameter(...) Returns texture parameter values for a specific level of detail.
glGetTexParameter(...) Returns texture parameter values.
glHint(...) Specifies a hinting level to assist GL in determining which method to use when rendering a model.
glIndex(...) Sets the current color index.
glIndexMask(...) Controls the writing of individual bits in the color index buffers.
glIndexPointerExt Defines an array of color indexes.
glInitNames(...) Initializes the name stack.
glIsEnabled(...) Tests whether a current capability is enabled.
glIsList(...) Tests for display list existence.
glLight(...) Sets up light source parameters.
glLightModel(...) Sets the lighting model parameters.
glLineStipple(...) Specifies the current line stipple pattern.
glLineWidth(...) Specifies the current width of rasterized lines.
glListBase(...) Sets the display-list base for using the glCallLists function.
glLoadIdentity(...) Loads the identity matrix into the current matrix.
glLoadMatrix(...) Replaces the current matrix with an arbitrary user-defined matrix.
glLoadName(...) Loads a name onto the name stack.
glLogicOp(...) Specifies a logical pixel operation for color index rendering.
glMapl(...) Defines a one-dimensional evaluator.
glMap2(...) Defines a two-dimensional evaluator.
glMapGrid(...) Defines a one- or two-dimensional mesh.
glMaterial(...) Specifies material properties used in the lighting model.
glMatrixMode(...) Specifies which matrix is the current matrix, either Modelview, Projection, or Texture.
glMultMatrix(...) Multiplies the current matrix by an arbitrary matrix.
glNewList(...) Creates or replaces a display list.
glNormal(...) Sets the current normal vector for lighting calculations.
glNormalPointerEXT Defines an array of normals.
glOrtho(...) Multiplies the current matrix by an orthographic matrix that defines a viewing volume.
glPassThrough(...) Places a marker in the feedback buffer.
glPixelMap(...) Sets up pixel transfer maps.
glPixelStore(...) Sets pixel storage modes.
glPixelTransfer(...) Sets pixel transfer modes.
glPixelZoom(...) Specifies the pixel zoom factors which are used during the glDrawPixels and glCopyPixels operations.
glPointSize(...) Specifies the diameter of rasterized points.
glPolygonMode(...) Selects a polygon rasterization mode.
glPolygonStipple(...) Sets the polygon stippling pattern.
glPopAttrib(...) Pops attribute sets on the attribute stack.
glPopMatrix(...) Pops a matrix off of the current matrix stack.
glPopName(...) Pops a name on the name stack.
glPushAttrib(...) Pushes attribute sets on the attribute stack.
glPushMatrix(...) Pushes a matrix off of the current matrix stack.
glPushName(...) Pushes a name on the name stack.
glRasterPos(...) Specifies the raster position for pixel operations.
glReadBuffer(...) Selects a color buffer source for pixels.
glReadPixels(...) Reads a block of pixels from the frame buffer.
glRect(...) Draws a rectangle.
glRenderMode(...) Sets the current rasterization mode.
glRotate(...) Multiplies the current matrix by a rotation matrix.
glScale(...) Multiplies the current matrix by a scaling matrix.
glScissor(...) Defines the scissor box, which is a rectangle in window coordinates. A scissor box specifies that only pixels drawn in the box may be modified by rendering commands.
glSelectBuffer(...) Establishes a buffer for selection mode values.
glShadeModel(...) Specifies either smooth or flat shading.
glStencilFunc(...) Sets the function and reference value for stencil testing.
glStencilMask(...) Controls the writing of individual bits in the stencil planes.
glStencilOp(...) Sets the stencil test actions.
glTexCoord(...) Sets the current texture coordinates.
glTexCoordPointerEXT(...) Defines an array of texture coordinates.
glTexEnv(...) Sets texture environment parameters.
glTexGen(...) Controls the generation of texture coordinates.
glTexImage(...) Specifies a texture image.
glTexParameter(...) Sets texture parameters.
glTranslate(...) Multiplies the current matrix by a translation matrix.
glVertex(...) Specifies a vertex.
glVertexPointerExt(...) Defines an array of vertex data.
glViewport(...) Sets the current viewport.

G.2.1 OpenGL utility functions

Table G.3 OpenGL utility functions

Name Description
gluBeginCurve(...) Delimits a NURBS curve definition.
gluBeginPolygon(...) Delimits a polygon description.
gluBeginSurface(...) Delimits a NURBS surface definition.
gluBeginTrim(...) Delimits a NURBS trimming loop definition.
gluBuildldMipmaps(...) Creates one and two-dimensional MIP maps.
gluCylinder(...) Draws a cylinder.
gluDeleteNurbsRenderer(...) Destroys a NURBS object.
gluDeleteQuadric(...) Destroys a quadrics object.
gluDeleteTess(...) Destroys a tessellation object.
gluDisk(...) Draws a disk.
gluErrorString(...) Produces an error string from an OpenGL or GLU error code.
gluEndCurve(...) Completes a NURBS curve definition.
gluEndPolygon(...) Completes a polygon description.
gluEndSurface(...) Completes a NURBS surface definition.
gluEndTrim(...) Completes a trimming loop definition.
gluGetNurbsProperty(...) Gets a NURBS property.
gluGetString(...) Gets a string that describes the GLU version number and supported GLU extension calls.
gluGetTessProperty(...) Gets a tessellation object property.
gluLoadSamplingMatrices(...) Loads NURBS sampling and culling matrices.
gluLookAt(...) Defines a viewing transformation.
gluNewNurbsRenderer(...) Creates a NURBS object.
gluNewQuadric(...) Creates a quadrics object.
gluNewTess(...) Creates a tessellation object.
gluNextContour(...) Marks the beginning of another contour.
gluNurbsCallback(...) Defines a callback for a NURBS object.
gluNurbsCurve(...) Defines the shape of a NURBS curve.
gluNurbsProperty(...) Sets a NURBS property.
gluNurbsSurface(...) Defines the shape of a NURBS surface.
gluOrtho2D(...) Defines a 2D orthographic projection matrix.
gluPartialDisk(...) Draws an arc of a disk.
gluPerspective(...) Sets up a perspective projection matrix.
gluPickMatrix(...) Defines a picking region.
gluProject(...) Maps object coordinates to window coordinates.
gluPwlCurve(...) Describes a piecewise linear NURBS trimming curve.
gluQuadricCallback(...) Defines a callback for a quadrics object.
gluQuadricDrawStyle(...) Specifies the draw style desired for quadrics.
gluQuadricNormals(...) Specifies what kind of normals are desired for quadrics.
gluQuadricOrientation(...) Specifies inside/outside orientation for quadrics.
gluQuadricTexture(...) Specifies whether or not texturing is desired for quadrics.
gluScaleImage(...) Scales an image to an arbitrary size.
gluSphere(...) Draws a sphere.
gluTessBeginContour(...) Delimits a contour description.
gluTessBeginPolygon(...) Delimits a polygon description.
gluTessCallback(...) Defines a callback for a tessellation object.
gluTessNormal(...) Specifies a normal for a polygon.
gluTessProperty(...) Sets the property of a tessellation object.
gluTessVertex(...) Specifies a vertex on a polygon.
gluUnProject(...) Maps window coordinates to object coordinates.



Page 456


G.2.2 Windows implementation OpenGL functions

Table G.4 Windows-specific OpenGL functions

Name Description
wglCreateContext(...) Creates a new OpenGL rendering context. The rendering context is suitable for drawing on the device referenced by hdc. The rendering context has the same pixel format as the device context.
wglDeleteContext(...) Destroys an OpenGL rendering context that was created with wglCreateContext.
wglGetCurrentContext(...) Obtains a handle to the calling thread's current OpenGL rendering context.
wglGetCurrentDC(...) Obtains a handle to the device context that is associated with the calling thread's current OpenGL rendering context.
wglGetProcAddress(...) Returns the address of an OpenGL extension function for use with the current OpenGL rendering context.
wglMakeCurrent(...) Makes a specified OpenGL rendering context the calling thread's current rendering context. All subsequent calls made by the thread are drawn on the device identified by the current device context. This function can also be used to make the calling thread's current rendering context not current.
wglShareLists(...) Enables multiple OpenGL rendering contexts to share a single display list.
wglUseFontBitmaps(...) Creates a set of bitmap display lists based on the glyphs in a device context's currently selected font for use in the current OpenGL rendering context. These bitmaps can then be used to draw characters in an OpenGL image.
wglUseFontOutlines(...) Creates a set of display lists based on the glyphs of the currently selected outline font of a device context for use with the current rendering context. The display lists are used to draw 3D characters of TrueType fonts.

G.2.3 Related Win32 functions

Table G.5 Win32 functions

Name Description
ChoosePixelFormat(...) Attempts to find the pixel format that is supported by a device context that is the best match to a given pixel format specification.
DescribePixelFormat(...) Obtains information about the pixel format identified by the device associated with the current hdc.
GetPixelFormat(...) Obtains the index of the specified device context's currently selected pixel format.
SetPixelFormat(...) Sets the specified device context's pixel format to the format specified by the index.
SwapBuffers(...) Exchanges the front and back buffers if the current pixel format for the window referenced by the specified device context includes a back buffer.



Page 457


PIXELFORMATDESCRIPTOR

typedef struct tagPIXELFORMATDESCRIPTOR { // pfd  
    WORD  nSize;
    WORD  nVersion;
    DWORD dwFlags;
    BYTE  iPixelType;
    BYTE  cColorBits;
    BYTE  cRedBits;
    BYTE  cRedShift;
    BYTE  cGreenBits;
    BYTE  cGreenShift;
    BYTE  cBlueBits;
    BYTE  cBlueShift;
    BYTE  cAlphaBits;
    BYTE  cAlphaShift;
    BYTE  cAccumBits;
    BYTE  cAccumRedBits;
    BYTE  cAccumGreenBits;
    BYTE  cAccumBlueBits;
    BYTE  cAccumAlphaBits;
    BYTE  cDepthBits;
    BYTE  cStencilBits;
    BYTE  cAuxBuffers;
    BYTE  iLayerType;
    BYTE  bReserved;
    DWORD dwLayerMask;
    DWORD dwVisibleMask;
    DWORD dwDamageMask;
} PIXELFORMATDESCRIPTOR;

Table G.6 PIXELFORMATDESCRIPTOR members

Name Description
WORD nSize Specifies the size of this data structure.
WORD nVersion Specifies the version of this data structure.
DWORD dwFlags Set of flags that specify properties of the pixel buffer.
BYTE iPixelType Specifies the type of pixel data.
BYTE cColorBits Specifies the number of color bitplanes in each color buffer.
BYTE cRedBits Specifies the number of red bitplanes in each color buffer.
BYTE cRedShift Specifies the shift count for red bitplanes in each color buffer.
BYTE cGreenBits Specifies the number of green bitplanes in each color buffer.
BYTE cGreenShift Specifies the shift count for green bitplanes in each color buffer.
BYTE cBlueBits Specifies the number of blue bitplanes in each color buffer.
BYTE cBlueShift Specifies the shift count for blue bitplanes in each color buffer.
BYTE cAlphaBits Specifies the number of alpha bitplanes in each color buffer.
BYTE cAlphaShift Specifies the shift count for alpha bitplanes in each color buffer.
BYTE cAccumBits Specifies the total number of bitplanes in the accumulation buffer.
BYTE cAccumRedBits Specifies the total number of red bitplanes in the accumulation buffer.
BYTE cAccumGreenBits Specifies the total number of green bitplanes in the accumulation buffer.
BYTE cAccumBlueBits Specifies the total number of blue bitplanes in the accumulation buffer.
BYTE cAccumAlphaBits Specifies the total number of alpha bitplanes in the accumulation buffer.
BYTE cDepthBits Specifies the color depth of the buffer.
BYTE cStencilBits Specifies the depth of the stencil buffer.
BYTE cAuxBuffers Specifies the number of auxiliary buffers.
BYTE iLayerType Specifies the type of layer.
BYTE bReserved Not used.
DWORD dwLayerMask Specifies the layer mask.
DWORD dwVisibleMask Specifies the visible mask.
DWORD dwDamageMask Specifies whether more than one pixel format shares the same frame buffer.

Following is the pixel descriptor used when developing Partica:

PIXELFORMATDESCRIPTOR pfq = {
       sizeof(PIXELFORMATDESCRIPTOR),       // size of this pfd
      1,                                                       // version number
         PFD_DRAW_TO_WINDOW |                    // support window
         PFD_SUPPORT_OPENGL |                    // support OpenGL
         PFD_DOUBLEBUFFER,                            // double buffered
       PFD_TYPE_RGBA,                                 // RGBA type
       24,                                                      // 24 bit color depth
       0, 0, 0, 0, 0, 0,                              // color bits ignored
       0,                                                       // no alpha buffer
       0,                                                       // shift bit ignored
       0,                                                       // no accumulation buffer
       0, 0, 0, 0,                                         // accum bits ignored
       32,                                                      // 32 bit z-buffer
       0,                                                       // no stencil buffer
       0,                                                       // no auxiliary buffer
       PFD_MAIN_PLANE,                                // main layer
       0,                                                       // reserved
       0, 0, 0                                             // layer masks ignored
   };

GLYPHMETRICSFLOAT

typedef struct _GLYPHMETRICSFLOAT { // gmf  
    FLOAT      gmfBlackBoxX;
    FLOAT      gmfBlackBoxY;
    POINTFLOAT gmfptGlyphOrigin;
    FLOAT      gmfCellIncX;
    FLOAT      gmfCellIncY;
} GLYPHMETRICSFLOAT;
Page 459


Table G.7 GLYPHMETRICSFLOAT members

Name Description
FLOAT gmfBlackBoxX Specifies the width of the smallest rectangle that completely encloses the glyph.
FLOAT gmfBlackBoxY Specifies the height of the smallest rectangle that completely encloses the glyph.
POINTFLOAT gmfptGlyphOrigin Specifies the x and y coordinates of the upper left corner of the smallest rectangle that completely encloses the glyph.
FLOAT gmfCellIncX Specifies the horizontal distance from the origin of the current character cell to the origin of the next character cell.
FLOAT gmfCellIncY Specifies the vertical distance from the origin of the current character cell to the origin of the next character cell.

POINTFLOAT

typedef struct _POINTFLOAT { // ptf  
    FLOAT      x;
    FLOAT      y;
} POINTFLOAT;

Table G.8 POINTFLOAT members

Name Description
FLOAT X; Specifies the horizontal or (x) coordinate of a point.
FLOAT Y; Specifies the vertical or (y) coordinate of a point.




G.3 COpenGLView header

This source code is provided so that the reader can construct his/her own OpenGL-based classes independently. When you run into problems you can quickly look up the solution that was used in Partica. This will help speed up development by having a set of working code available. The following code has been tested and used in the Windows 95/NT environment.

The first step is to create the interface for the COpenGLView class. We do that by setting up the primary object and its member variables as follows:

// COpenGLView.h : interface of the COpenGLView class
#include "objects.h"
COpenGLView header
Page 460


Our implementation has four primary modes of operation. These modes are given the enumeration type OpenGLTool.

enum OpenGLTool {
     Select, NodeSelect, Zoom, Translate
};

The currently selected tool is a global member so that all views will have the same tool selected to save confusion.

extern OpenGLTool SelectedTool;

class CObjectViewDlg;
class COpenGLDoc;

Our COpenGLView object is an MFC extension and is derived from the MFC CView object. The critical function members listed here have been discussed in chapter 11. These functions are intended to be accessed by derived objects from the following base class:

class COpenGLView : public CView
{
protected:
     afx_msg void OnContextMenu(CWnd*, CPoint point);
     unsigned char ComponentFromIndex(int i, UINT nbits, UINT shift);

     void ComputeTessellationLocation();
     void SelectField(LField *field);
     void DrawModel(RECT &l);
     void CreateRGBPalette(HDC hDC);

     BOOL bSetupPixelFormat(HDC hDC);
     PIXELFORMATDESCRIPTOR pfd;
     COpenGLView();

     DECLARE_DYNCREATE(COpenGLView)

     LPoint3 OldField,  
          Leye,Lcenter,Lup,
          Loldeye,Loldup,center,
          trackballaxis;

     CPoint LastMouseLocation,
 BeginRotatePos,
           LastClickPos,
           BeginDrag;

     RECT oldrect;

     CObjectViewDlg *objwin;
     CWnd * THISCWND;
     CDC *hdc ;          // OpenGL Rendering Contexts

Page 461
HDC hDC;
     HGLRC     hrc ;
     LACEDocument *objectdata;
     LField * WorkingField;
     HPALETTE ghpalOld, ghPalette;

     float DepthScale;
     float dtheta;
     float theta;
     float near_plane, far_plane, aspect, fov;
     float radius;
     float CubeSize;
     float trackballangle;
     float LastTessellationTime;

     int TimerValue;
     int LightModel;
     int ModelType;  // 0-point  1-plus  2-sphere

     BOOL IsFollow,IsMouseInWindow,PausePlay,
     IsFrozen,Rotating,IsFullScreen,
       KillPlay,KillRotate,SuccessFullDraw,
     Tetrahedral,Wireframe,Shading,
     ZBuffer,AntiAlias,Backface,
     AutoField,rdown,spinmode,
     down,CanPaint,IsSplines,
     IsLighting,IsTransparent,IsPlaying,
     IsWireframe;

     LineF_t BeginLine;

     GLdouble trackballMatrix[16];
     GLdouble starttrackballMatrix[16];

The following are the publicly accessible members. These members may be accessed by any object in the application. Most of these functions are in-line for use with the virtual trackball control that is implemented in COpenGLView.

public:
     void Center() { 
          LPoint3 r=objectdata->GetCenter();
          Lcenter.x=-r.x;
          Lcenter.y=-r.y;
          Lcenter.z=-r.z;
          if(!(IsPlaying|Rotating))
               Invalidate(FALSE);
           };
     void DisconnectFromCWnd();
     void ConnectToCWND(CWnd *wnd);
     void StopAnimation();
     void AdjustLighting();
     void Tessellate();
     void NeedViewClose();


Page 462
virtual BOOL PreTranslateMessage(MSG* pMsg);
     COpenGLDoc* GetDocument();

     float GetTrackballDistance() {
          return VECTORLENGTH(Leye.x,Leye.y,Leye.z);
     };

     LPoint3 GetSphereIntersection(LineF_t &_l, SphereF_t &_sp) {
          LPoint3 result;
          result.x=-999999; // Failed Case

          LineF_t EO;
          EO.vector.x=_sp.center.x-_l.point.x;
          EO.vector.y=_sp.center.y-_l.point.y;
          EO.vector.z=_sp.center.z-_l.point.z;
          EO.point=_l.point;
          float m=VECTORLENGTH(_l.vector.x,_l.vector.y,_l.vector.z);

          if(m==0) return result; 

          _l.vector.x/=m;
          _l.vector.y/=m;
          _l.vector.z/=m;

          GLfloat v=     EO.vector.x*_l.vector.x+
                    EO.vector.y*_l.vector.y+
                    EO.vector.z*_l.vector.z;

          GLfloat EOEO=     EO.vector.x*EO.vector.x+
                    EO.vector.y*EO.vector.y+
                    EO.vector.z*EO.vector.z;

          float disc=_sp.r*_sp.r-
                    (EOEO-v*v);

          if(disc<0)
               return result;

          GLfloat d=sqrt(disc);
          result.x=EO.point.x+(v-d)*_l.vector.x;
          result.y=EO.point.y+(v-d)*_l.vector.y;
          result.z=EO.point.z+(v-d)*_l.vector.z;

          return result;
     };

     void GotoObjectSpace(float x, float y, float z) {
          Lcenter.x=-x;
          Lcenter.y=-y;
          Lcenter.z=-z;
     };

     LPoint3 WindowToObject(CPoint &t, double z=0 ) {
          LPoint3 object;

Page 463
GLdouble modelMatrix[16];
          GLdouble projMatrix[16];
          GLint viewport[4];

          GLdouble winx,winy,winz,objx,objy,objz;
          winx=t.x;
          winy=t.y;
          winz=z;

          glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
          glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
          glGetIntegerv(GL_VIEWPORT,viewport);

          gluUnProject(winx,winy,winz,
               modelMatrix,projMatrix,viewport,
               &objx,&objy,&objz);

          object.x=objx;
          object.y=objy;
          object.z=objz;

          return object;
     };

     LPoint3 ObjectToWindow(LPoint3 &t) {
          LPoint3 object;

          GLdouble modelMatrix[16];
          GLdouble projMatrix[16];
          GLint viewport[4];

          GLdouble winx,winy,winz,objx,objy,objz;
          winx=t.x;
          winy=t.y;
          winz=t.z;

          glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
          glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
          glGetIntegerv(GL_VIEWPORT,viewport);

          gluProject(winx,winy,winz,
               modelMatrix,projMatrix,viewport,
               &objx,&objy,&objz);

          object.x=objx;
          object.y=objy;
          object.z=objz;

          return object;
     };

     LPoint3 IntersectLinePlane(LineF_t &l, LPoint3 &planepoint,
          LPoint3 &planenorm) {

          LPoint3 Q,P,v,w,result;

Page 464
Q.x=l.point.x; Q.y=l.point.y; Q.z=l.point.z;
          P.x=planepoint.x; P.y=planepoint.y; P.z=planepoint.z;
          v.x=planenorm.x; v.y=planenorm.y; v.z=planenorm.z;
          w.x=l.vector.x; w.y=l.vector.y; w.z=l.vector.z;

          float top,bottom;
          top=(P.x-Q.x)*v.x+(P.y-Q.y)*v.y+(P.z-Q.z)*v.z;
          bottom=w.x*v.x+w.y*v.y+w.z*v.z;
          float mul=top/bottom;

          result.x=Q.x+mul*w.x;
          result.y=Q.y+mul*w.y;
          result.z=Q.z+mul*w.z;

          return result;
     };

     LPoint3 MoveParallelToScreen(LPoint3 &OldPoint, CPoint &oldwin,
               CPoint &newwin) {

     // Get object coord
          LPoint3 OldWin=WindowToObject(oldwin);
          LPoint3 NewWin=WindowToObject(newwin); 

          OldWin.x=(float)oldwin.x/(float)oldrect.right;          
          OldWin.y=(float)oldwin.y/(float)oldrect.bottom;          
          OldWin.z=0;

          NewWin.x=(float)newwin.x/(float)oldrect.right;          
          NewWin.y=(float)newwin.y/(float)oldrect.bottom;          
          NewWin.z=0;

          LPoint3 pnorm,ppoint;

          ppoint=OldPoint;     // Plane at point
     // Ray from screen center to eye
          pnorm.x=Lcenter.x-Leye.x;
          pnorm.y=Lcenter.y-Leye.y;
          pnorm.z=Lcenter.z-Leye.z;

          float dist=VECTORLENGTH(pnorm.x,
               pnorm.y,pnorm.z);

          pnorm.x/=dist;
          pnorm.y/=dist;
          pnorm.z/=dist;

          // Plane setup now, find ray
          LineF_t ray;
          ray.point.x=NewWin.x;
          ray.point.y=NewWin.y;
          ray.point.z=NewWin.z;
          LPoint3 NewWindeltat=WindowToObject(newwin,1);

Page 465
ray.vector.x=ray.point.x;//-NewWindeltat.x;
          ray.vector.y=ray.point.y;//-NewWindeltat.y;
          ray.vector.z=ray.point.z-1;//NewWindeltat.z; 

          dist=VECTORLENGTH(ray.vector.x,
               ray.vector.y,ray.vector.z);
          ray.vector.x/=dist;
          ray.vector.y/=dist;
          ray.vector.z/=dist;

          LPoint3 result;
          result.x=OldPoint.x+(NewWin.x-OldWin.x)*3.0;
          result.y=OldPoint.y+(OldWin.y-NewWin.y)*3.0;
          result.z=OldPoint.z+(OldWin.z-NewWin.z)*3.0;

          return result; 
     };

     void GetTrackballAngle(CPoint oldpos, CPoint newpos) {
          GLfloat r2=4;
          LineF_t ray0, ray1;
          SphereF_t sp;

          GLfloat r=r2;
          LPoint3     p0,p1;
          oldpos.y=oldrect.right-oldpos.y;
          newpos.y=oldrect.right-newpos.y;

          LPoint3 t=WindowToObject(oldpos);

          float enx,eny,enz,em=VECTORLENGTH(Leye.x,Leye.y,Leye.z);
          enx=Leye.x/em;
          eny=Leye.y/em;
          enz=Leye.z/em;

          ray0.point.x=t.x; ray0.point.y=t.y; ray0.point.z=t.z;
          ray0.vector.x=-enx; ray0.vector.y=-eny; ray0.vector.z=-enz;

          t=WindowToObject(newpos);

          ray1.point.x=t.x; ray1.point.y=t.y; ray1.point.z=t.z;

          ray1.vector.x=-enx; ray1.vector.y=-eny; ray1.vector.z=-enz;

          sp.center.x=0; sp.center.y=0; sp.center.z=0;
          sp.r=r;

          p0=GetSphereIntersection(ray0,sp);
          if(p0.x==-999999) return;
          p1=GetSphereIntersection(ray1,sp);
          if(p1.x==-999999) return;
          GLfloat m2=VECTORLENGTH(p1.x,p1.y,p1.z);

          if(p0.x==-999999) {
               trackballangle=0;
               return;
          };

Page 466
if(p1.x==-999999) {
               trackballangle=0;
               return;
          };

          LPoint3 V0,V1;

          GLfloat m=VECTORLENGTH(
                         p0.x-sp.center.x,
                         p0.y-sp.center.y,
                         p0.z-sp.center.z);

          V0.x=(p0.x-sp.center.x)/m;
          V0.y=(p0.y-sp.center.y)/m;
          V0.z=(p0.z-sp.center.z)/m;

          m=VECTORLENGTH(p1.x-sp.center.x,
                         p1.y-sp.center.y,
                         p1.z-sp.center.z);

          V1.x=(p1.x-sp.center.x)/m;
          V1.y=(p1.y-sp.center.y)/m;
          V1.z=(p1.z-sp.center.z)/m;

          LPoint3 A;
          A.x=V0.y*V1.z-V0.z*V1.y;
          A.y=V0.z*V1.x-V0.x*V1.z;
          A.z=V0.x*V1.y-V0.y*V1.x;

          float alpha=asin(VECTORLENGTH(A.x,A.y,A.z));
          float test=V0.x*V1.x+V0.y*V1.y+V0.z*V1.z;
          if(test<0) {
               alpha+=3.141592654/(float)2.0;
          };  

          trackballaxis.x=A.x;
          trackballaxis.y=A.y;
          trackballaxis.z=A.z;
          trackballangle=(alpha*r*10)/(fov/180.0);

          int     i;
          glPushMatrix();
          glLoadIdentity();
          glLoadMatrixd(starttrackballMatrix);
          glRotatef(trackballangle,trackballaxis.x,
               trackballaxis.y,trackballaxis.z);
          glGetDoublev(GL_MODELVIEW_MATRIX,trackballMatrix);
          glPopMatrix(); 
          float ex=trackballMatrix[0]*Leye.x
               +trackballMatrix[1]*Leye.y
               +trackballMatrix[2]*Leye.z
               +trackballMatrix[3];
          float ey=trackballMatrix[4]*Leye.x

Page 467
+trackballMatrix[5]*Leye.y
               +trackballMatrix[6]*Leye.z
               +trackballMatrix[7];
          float ez=trackballMatrix[8]*Leye.x
               +trackballMatrix[9]*Leye.y
               +trackballMatrix[10]*Leye.z
               +trackballMatrix[11];

          Leye.x=ex; Leye.y=ey; Leye.z=ez;                    

          ex=trackballMatrix[0]*Lup.x
               +trackballMatrix[1]*Lup.y
               +trackballMatrix[2]*Lup.z
               +trackballMatrix[3];

          ey=trackballMatrix[4]*Lup.x
               +trackballMatrix[5]*Lup.y
               +trackballMatrix[6]*Lup.z
               +trackballMatrix[7];
          ez=trackballMatrix[8]*Lup.x
               +trackballMatrix[9]*Lup.y
               +trackballMatrix[10]*Lup.z
               +trackballMatrix[11];

     Lup.x=ex; Lup.y=ey; Lup.z=ez;               

     glPushMatrix();
     glLoadIdentity();
     glGetDoublev(GL_MODELVIEW_MATRIX,trackballMatrix);
     glPopMatrix();

};
void RotateAgain() {
          glPushMatrix();
          glLoadIdentity();
          glLoadMatrixd(starttrackballMatrix);
          glRotatef(trackballangle,trackballaxis.x,
               trackballaxis.y,trackballaxis.z);
          glGetDoublev(GL_MODELVIEW_MATRIX,trackballMatrix);
          glPopMatrix(); 

          float ex=trackballMatrix[0]*Leye.x
               +trackballMatrix[1]*Leye.y
               +trackballMatrix[2]*Leye.z
               +trackballMatrix[3];
          float ey=trackballMatrix[4]*Leye.x
               +trackballMatrix[5]*Leye.y
               +trackballMatrix[6]*Leye.z
               +trackballMatrix[7];
          float ez=trackballMatrix[8]*Leye.x
               +trackballMatrix[9]*Leye.y
               +trackballMatrix[10]*Leye.z
               +trackballMatrix[11];
          Leye.x=ex; Leye.y=ey; Leye.z=ez;                    

Page 468
ex=trackballMatrix[0]*Lup.x
               +trackballMatrix[1]*Lup.y
               +trackballMatrix[2]*Lup.z
               +trackballMatrix[3];
          ey=trackballMatrix[4]*Lup.x
               +trackballMatrix[5]*Lup.y
               +trackballMatrix[6]*Lup.z
               +trackballMatrix[7];
          ez=trackballMatrix[8]*Lup.x
               +trackballMatrix[9]*Lup.y
               +trackballMatrix[10]*Lup.z
               +trackballMatrix[11];

          Lup.x=ex; Lup.y=ey; Lup.z=ez;               

          glPushMatrix();
          glLoadIdentity();
          glGetDoublev(GL_MODELVIEW_MATRIX,trackballMatrix);
          glPopMatrix();
          Rotating=TRUE;
};

void Play() {
          OnAnimPlay();
};

The following functions are primarily MFC-derived event function declarations and provide overloaded behavior for the COpenGLView object:

// Operations
public:

// Overrides
     //{{AFX_VIRTUAL(COpenGLView)
     public:
     virtual void OnDraw(CDC* pDC);  // overridden to draw this view
     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
     virtual BOOL Create(LPCTSTR lpszClassName,
               LPCTSTR lpszWindowName,
                       DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,
               CCreateContext* pContext = NULL);
     virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL);
     virtual DROPEFFECT OnDragEnter(COleDataObject* pDataObject, DWORD
                    dwKeyState, CPoint point);
     virtual void OnDragLeave();
     virtual BOOL IsSelected(const CObject* pDocItem) const;
     virtual DROPEFFECT OnDragOver(COleDataObject* pDataObject, DWORD 
                    dwKeyState, CPoint point);
     virtual void OnFinalRelease();
     virtual void OnInitialUpdate();
     virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, 
                AFX_CMDHANDLERINFO* pHandlerInfo);
     virtual BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM 

Page 469
lParam, LRESULT* pLResult);
     protected:
     virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
     virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
     virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
     virtual void CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType 
          = adjustBorder);
     virtual void OnActivateView(BOOL bActivate, CView* pActivateView, 
          CView* pDeactiveView);
     virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* 
          pHint);
     virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM 
                lParam);
     virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
     virtual void DoDataExchange(CDataExchange* pDX);
     virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM 
                lParam);
     virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* 
          pResult);
     //}}AFX_VIRTUAL

The following functions are the implementation members of the COpenGLView object. Note the preprocessor definitions for debugging. These members are only included if the Visual C++ compiler is in debug mode.

// Implementation
public:
     virtual ~COpenGLView();
#ifdef _DEBUG
     virtual void AssertValid() const;
     virtual void Dump(CDumpContext& dc) const;
#endif

The next section is the message map section from the application frameworks. All of the functions in this section respond to windows or MFC-based application events.

protected:
     //{{AFX_MSG(COpenGLView)
     afx_msg void OnCancelEditSrvr();
     afx_msg void OnOptionsModelerSettings();
     afx_msg void OnZoom();
     afx_msg void OnNodeSelect();
     afx_msg void OnSelect();
     afx_msg void OnPaint();
     afx_msg BOOL OnEraseBkgnd(CDC* pDC);
     afx_msg void OnMove(int x, int y);
     afx_msg void OnSize(UINT nType, int cx, int cy);
     afx_msg void OnParticle();
     afx_msg void OnSpace();
     afx_msg void OnRotate();
     afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest,

Page 470
UINT message);
     afx_msg void OnMouseMove(UINT nFlags, CPoint point);
     afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
     afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
     afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
     afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
     afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);
     afx_msg void OnSetFocus(CWnd* pOldWnd);
     afx_msg void OnEditCut();
     afx_msg void OnUpdateEditCut(CCmdUI* pCmdUI);
     afx_msg void OnAddblob();
     afx_msg void OnAddfield();
     afx_msg void OnUpdateAddblob(CCmdUI* pCmdUI);
     afx_msg void OnUpdateAddfield(CCmdUI* pCmdUI);
     afx_msg void OnTimer(UINT nIDEvent);
     afx_msg void OnCluster();
     afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
     afx_msg void OnDestroy();
     afx_msg void OnCamerasettings();
     afx_msg void OnImportascii();
     afx_msg void OnCenter();
     afx_msg void OnAnimatorsettings();
     afx_msg void OnSimulatoroptions();
     afx_msg void OnObjectview();
     afx_msg void OnAnimEnd();
     afx_msg void OnAnimFront();
     afx_msg void OnAnimNext();
     afx_msg void OnAnimPlay();
     afx_msg void OnAnimPrevious();
     afx_msg void OnAnimStop();
     afx_msg void OnKillFocus(CWnd* pNewWnd);
     afx_msg void OnDataadjust();
     afx_msg void OnImporthin();
     afx_msg void OnTCard(UINT idAction, DWORD dwActionData);
     afx_msg void OnImportsim();
     afx_msg void OnFullscreen();
     afx_msg void OnPmodelStar();
     afx_msg void OnPmodelSphere();
     afx_msg void OnPmodelPoint();
     afx_msg void OnPmodelFastquality();
     afx_msg void OnPmodelLighting();
     afx_msg void OnPmodelTransparency();
     afx_msg void OnPmodelWireframe();
     afx_msg void OnPmodelSplines();
     afx_msg void OnPmodelSurface();
     afx_msg void OnPmodelSlow();
     afx_msg void OnPmodelAntialias();
     afx_msg void OnParallel();
     afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
     afx_msg void OnUpdatePmodelAntialias(CCmdUI* pCmdUI);
     afx_msg void OnUpdatePmodelTransparency(CCmdUI* pCmdUI);
     afx_msg void OnUpdateAnimPlay(CCmdUI* pCmdUI);

Page 471
afx_msg void OnUpdateRotate(CCmdUI* pCmdUI);
     afx_msg void OnUpdateZoom(CCmdUI* pCmdUI);
     afx_msg void OnUpdateNodeSelect(CCmdUI* pCmdUI);
     afx_msg void OnUpdatePmodelWireframe(CCmdUI* pCmdUI);
     afx_msg void OnUpdatePmodelSplines(CCmdUI* pCmdUI);
     afx_msg void OnUpdatePmodelSphere(CCmdUI* pCmdUI);
     afx_msg void OnUpdatePmodelStar(CCmdUI* pCmdUI);
     afx_msg void OnUpdatePmodelPoint(CCmdUI* pCmdUI);
     afx_msg void OnUpdateTranslate(CCmdUI* pCmdUI);
     afx_msg void OnTranslate();
     afx_msg void OnUpdateCenter(CCmdUI* pCmdUI);
     afx_msg void OnEditCopy();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

The following function definition for the GetDocument() member is the inline version for use in release builds.

#ifndef _DEBUG  // debug version in COpenGLView.cpp
inline COpenGLDoc* COpenGLView::GetDocument()
   { return (COpenGLDoc*)m_pDocument; }
#endif

G.3.1 COpenGLView Member Functions

The following functions are implementations of members that were
declared in the COpenGLView object definition. Different functions
call different dialog box members. These dialog boxes are used for
enabling and disabling parameters. In general, these dialog box
members can be set up in whatever manner needed in user applications.
#include "stdafx.h"
#include "OpenGL Space.h"
#include "OpenGL SpaceDoc.h"
#include "COpenGLView.h"
#include "ModelerSettingsDlg.h"
#include "CameraSettingsDlg.h"
#include "AnimatorSettingsDlg.h"
#include "SimulatorSettingsDlg.h"
#include "SpaceSettingsDlg.h"
#include "ParticleSettingsDlg.h"
#include "ClustersDlg.h"
#include "ASCIIImportDlg.h"
#include "ObjectViewDlg.h"
#include "DataAdjustertDlg.h"
#include "ParallelDialogue.h"

#include "objects.h"
#include <fstream.h>
#include "resource.h"
Page 472



We will use the debug version of the new C++ operator. The debug
version tracks and locates memory leaks and missed deallocations. The
THIS_FILE member specifies a string that is the name of the current
file being compiled. This string can be used in debug strings to point
to the module that caused the problem.
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

The next section declares all of the message map functions for the different OpenGL command handlers. The Visual C++ ClassWizard inserts and removes these functions. For the most part, user modification of the components is not recommended.

// COpenGLView Implementation

IMPLEMENT_DYNCREATE(COpenGLView, CView)

BEGIN_MESSAGE_MAP(COpenGLView, CView)
     ON_WM_CONTEXTMENU()
     //{{AFX_MSG_MAP(COpenGLView)
     ON_COMMAND(ID_CANCEL_EDIT_SRVR, OnCancelEditSrvr)
     ON_COMMAND(ID_OPTIONS_MODELERSETTINGS, OnOptionsModelerSettings)
     ON_COMMAND(ID_ZOOM, OnZoom)
     ON_COMMAND(ID_NODE_SELECT, OnNodeSelect)
     ON_COMMAND(ID_SELECT, OnSelect)
     ON_WM_PAINT()
     ON_WM_ERASEBKGND()
     ON_WM_MOVE()
     ON_WM_SIZE()
     ON_COMMAND(ID_PARTICLE, OnParticle)
     ON_COMMAND(ID_SPACE, OnSpace)
     ON_COMMAND(ID_ROTATE, OnRotate)
     ON_WM_SETCURSOR()
     ON_WM_MOUSEMOVE()
     ON_WM_RBUTTONDOWN()
     ON_WM_RBUTTONUP()
     ON_WM_LBUTTONDBLCLK()
     ON_WM_LBUTTONDOWN()
     ON_WM_LBUTTONUP()
     ON_WM_RBUTTONDBLCLK()
     ON_WM_SETFOCUS()
     ON_COMMAND(ID_EDIT_CUT, OnEditCut)
     ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
     ON_COMMAND(ID_ADDBLOB, OnAddblob)
     ON_COMMAND(ID_ADDFIELD, OnAddfield)
     ON_UPDATE_COMMAND_UI(ID_ADDBLOB, OnUpdateAddblob)
     ON_UPDATE_COMMAND_UI(ID_ADDFIELD, OnUpdateAddfield)
     ON_WM_TIMER()

Page 473
ON_COMMAND(ID_CLUSTER, OnCluster)
     ON_WM_CREATE()
     ON_WM_DESTROY()
     ON_COMMAND(ID_CAMERASETTINGS, OnCamerasettings)
     ON_COMMAND(ID_IMPORTASCII, OnImportascii)
     ON_COMMAND(ID_CENTER, OnCenter)
     ON_COMMAND(ID_ANIMATORSETTINGS, OnAnimatorsettings)
     ON_COMMAND(ID_SIMULATOROPTIONS, OnSimulatoroptions)
     ON_COMMAND(ID_OBJECTVIEW, OnObjectview)
     ON_COMMAND(ID_ANIM_END, OnAnimEnd)
     ON_COMMAND(ID_ANIM_FRONT, OnAnimFront)
     ON_COMMAND(ID_ANIM_NEXT, OnAnimNext)
     ON_COMMAND(ID_ANIM_PLAY, OnAnimPlay)
     ON_COMMAND(ID_ANIM_PREVIOUS, OnAnimPrevious)
     ON_COMMAND(ID_ANIM_STOP, OnAnimStop)
     ON_WM_KILLFOCUS()
     ON_COMMAND(ID_DATAADJUST, OnDataadjust)
     ON_COMMAND(ID_IMPORTHIN, OnImporthin)
     ON_WM_TCARD()
     ON_COMMAND(ID_IMPORTSIM, OnImportsim)
     ON_COMMAND(ID_FULLSCREEN, OnFullscreen)
     ON_COMMAND(ID_PMODEL_STAR, OnPmodelStar)
     ON_COMMAND(ID_PMODEL_SPHERE, OnPmodelSphere)
     ON_COMMAND(ID_PMODEL_POINT, OnPmodelPoint)
     ON_COMMAND(ID_PMODEL_FASTQUALITY, OnPmodelFastquality)
     ON_COMMAND(ID_PMODEL_LIGHTING, OnPmodelLighting)
     ON_COMMAND(ID_PMODEL_TRANSPARENCY, OnPmodelTransparency)
     ON_COMMAND(ID_PMODEL_WIREFRAME, OnPmodelWireframe)
     ON_COMMAND(ID_PMODEL_SPLINES, OnPmodelSplines)
     ON_COMMAND(ID_PMODEL_SURFACE, OnPmodelSurface)
     ON_COMMAND(ID_PMODEL_SLOW, OnPmodelSlow)
     ON_COMMAND(ID_PMODEL_ANTIALIAS, OnPmodelAntialias)
     ON_COMMAND(ID_PARALLEL, OnParallel)
     ON_WM_SHOWWINDOW()
     ON_UPDATE_COMMAND_UI(ID_PMODEL_ANTIALIAS, 
     OnUpdatePmodelAntialias)
     ON_UPDATE_COMMAND_UI(ID_PMODEL_TRANSPARENCY, 
     OnUpdatePmodelTransparency)
     ON_UPDATE_COMMAND_UI(ID_ANIM_PLAY, OnUpdateAnimPlay)
     ON_UPDATE_COMMAND_UI(ID_ROTATE, OnUpdateRotate)
     ON_UPDATE_COMMAND_UI(ID_ZOOM, OnUpdateZoom)
     ON_UPDATE_COMMAND_UI(ID_NODE_SELECT, OnUpdateNodeSelect)
     ON_UPDATE_COMMAND_UI(ID_PMODEL_WIREFRAME, 
     OnUpdatePmodelWireframe)
     ON_UPDATE_COMMAND_UI(ID_PMODEL_SPLINES, OnUpdatePmodelSplines)
     ON_UPDATE_COMMAND_UI(ID_PMODEL_SPHERE, OnUpdatePmodelSphere)
     ON_UPDATE_COMMAND_UI(ID_PMODEL_STAR, OnUpdatePmodelStar)
     ON_UPDATE_COMMAND_UI(ID_PMODEL_POINT, OnUpdatePmodelPoint)
     ON_UPDATE_COMMAND_UI(ID_TRANSLATE, OnUpdateTranslate)
     ON_COMMAND(ID_TRANSLATE, OnTranslate)
     ON_UPDATE_COMMAND_UI(ID_CENTER, OnUpdateCenter)
     ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
     //}}AFX_MSG_MAP

Page 474
// Standard printing commands
     ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
     ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
     ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()

As mentioned before, we use a global member to declare the currently selected tool. All views should have the selected tool working in the same manner. When the tool changes, it should change for all views. Using a global member value accommodates this. Also note the IsIdleLocked member. Screen redraws should be done when the system is not doing other tasks. We use the IsIdleLocked member to accomplish this in combination with the global NumViews member.

// COpenGLView construction/destruction

OpenGLTool SelectedTool;

int NumViews=0;
BOOL IsIdleLocked=FALSE;

The COpenGLView constructor initializes all of the member variables 
to default start up values.  
COpenGLView::COpenGLView()
{
     DepthScale=1;
     CanPaint=TRUE;
     SelectedTool=Zoom;
     CubeSize=.37;
     spinmode=0;
     down=0;
     rdown=0;
     Tetrahedral=FALSE;
     Wireframe=FALSE;
     Shading=TRUE;
     ZBuffer=TRUE;
     AntiAlias=FALSE;
     Backface=TRUE;
     AutoField=TRUE;
     WorkingField=NULL;
     objwin=NULL;
     Rotating=FALSE;
     IsPlaying=FALSE;
     LightModel=0;
     IsIdleLocked=FALSE;

     ghPalette = (HPALETTE) 0;

     Leye.x=0; Leye.y=0; Leye.z=4;
     Lcenter.x=0; Lcenter.y=0; Lcenter.z=0;
     Lup.x=0; Lup.y=1; Lup.z=0;
     fov=45; aspect=1; near_plane=1; far_plane=1000;

Page 475
trackballangle=0; trackballaxis.x=1;

trackballaxis.y=0; trackballaxis.z=0;

     PIXELFORMATDESCRIPTOR pfq = {
     sizeof(PIXELFORMATDESCRIPTOR),     // size of this pfd
     1,                         // version number
     PFD_DRAW_TO_WINDOW |               // support window
     PFD_SUPPORT_OPENGL |               // support OpenGL
     PFD_DOUBLEBUFFER,               // double buffered
     PFD_TYPE_RGBA,                    // RGBA type
     24,                         // 24 bit color depth
     0, 0, 0, 0, 0, 0,               // color bits ignored
     0,                         // no alpha buffer
     0,                         // shift bit ignored
     0,                         // no accumulation buffer
     0, 0, 0, 0,                     // accum bits ignored
     32,                         // 32 bit z-buffer     
     0,                         // no stencil buffer
     0,                         // no auxiliary buffer
     PFD_MAIN_PLANE,               // main layer
     0,                         // reserved
     0, 0, 0                         // layer masks ignored
};

     pfd=pfq;
     IsFullScreen=FALSE;

     IsWireframe=FALSE;
     IsSplines=FALSE;
     IsLighting=TRUE;
     IsTransparent=FALSE;
     IsFollow=TRUE;
     ModelType=0;
     NumViews++;
     TimerValue=1;
     KillPlay=FALSE;
}

The destructor object checks to see if there is a property window open. If so, it makes sure that it has been destroyed. It also decrements the number of views.

COpenGLView::~COpenGLView()
{
     if(objwin!=NULL) {
          objwin->DestroyWindow();
          delete objwin;
     };
     NumViews;
}

The AssertValid(), Dump(), and GetDocument() members are defined for debugging builds of the application. They are not included when the system is creating a release build.

Page 476



#ifdef _DEBUG
void COpenGLView::AssertValid() const
{
     CView::AssertValid();
}

void COpenGLView::Dump(CDumpContext& dc) const
{
     CView::Dump(dc);
}

COpenGLDoc* COpenGLView::GetDocument() // non-debug version is inline
{
     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(COpenGLDoc)));
     return (COpenGLDoc*)m_pDocument;
}
#endif //_DEBUG

Following are some example command handlers, such as OnOptionsModelerSettings(), for command notifications for parameter changes. These command event handlers typically just create a dialog box and swap the necessary parameters in and out of the view via the dialog box.

void COpenGLView::OnOptionsModelerSettings() 
{
     CModelerSettingsDlg     dlg;

     static int ModelerType=0,Number=20,Fast=0;
     static BOOL LightPrim=FALSE;

     dlg.m_Center=ModelerType;
     dlg.m_Spline=IsSplines;
     dlg.m_DetailAmount=CubeSize;
     dlg.m_AntiAlias=AntiAlias;
     dlg.m_FineDetail=Tetrahedral;
     dlg.m_InnerShade=IsLighting;
     dlg.m_Wireframe=Wireframe;
     dlg.m_LightPrim=LightPrim;
     dlg.m_Backface=Backface;
     dlg.m_TrackField=AutoField;
     dlg.m_Number=Number;
     dlg.m_Fast=Fast;
     dlg.m_Alpha=IsTransparent;

     if(dlg.DoModal()==IDOK) {
          AntiAlias=dlg.m_AntiAlias;
          IsLighting=dlg.m_InnerShade;
          Wireframe=dlg.m_Wireframe;
          ModelerType=dlg.m_Center;
          IsTransparent=dlg.m_Alpha;
          AutoField=dlg.m_TrackField;
          if(Wireframe)
               glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);

Page 477
else
               glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);

          Fast=dlg.m_Fast;
          Backface=dlg.m_Backface;
          IsSplines=dlg.m_Spline;

          LightPrim=dlg.m_LightPrim;

          if(Backface)
               glEnable(GL_CULL_FACE);
          else
               glDisable(GL_CULL_FACE);

          if(IsLighting)
               glEnable(GL_LIGHTING);
          else
               glDisable(GL_LIGHTING);

          if(IsTransparent) {
               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
               glEnable(GL_BLEND);
          }
          else {
               glDisable(GL_BLEND);
          };

          if(AntiAlias) {
               glEnable(GL_LINE_SMOOTH);
               glEnable(GL_POINT_SMOOTH);
               glEnable(GL_POLYGON_SMOOTH); 
          }
          else {
               glDisable(GL_LINE_SMOOTH);
               glDisable(GL_POINT_SMOOTH);
               glDisable(GL_POLYGON_SMOOTH);
          };

          if(Fast) {
               glHint(GL_FOG_HINT,GL_FASTEST);
               glHint(GL_LINE_SMOOTH_HINT,GL_FASTEST);
               glHint(GL_POINT_SMOOTH_HINT,GL_FASTEST);
               glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST); 
               glDisable(GL_LIGHT1);
               glDisable(GL_LIGHT2);
               glDisable(GL_LIGHT3);
          }
          else {
               glHint(GL_FOG_HINT,GL_NICEST);
               glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
               glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
               glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST); 
          };

Page 478
if( (Tetrahedral!=dlg.m_FineDetail) |
               (Number!=dlg.m_Number) |
               (CubeSize!=dlg.m_DetailAmount)) 
               Tessellate();

          Tetrahedral=dlg.m_FineDetail;
          Number=dlg.m_Number;
          CubeSize=dlg.m_DetailAmount;

Invalidate(FALSE);
     };
}

void COpenGLView::OnParticle() 
{
     CParticleSettingsDlg     dlg;

     static BOOL DF=FALSE, Sphe=FALSE, Star=TRUE;
     static int ModelType=0;
     dlg.m_Slices=p_slices;
     dlg.m_Stacks=p_stacks;
     dlg.m_ModelType=ModelType;
     dlg.m_Transparency=p_transparency;

     if(dlg.DoModal()==IDOK) {
          switch(dlg.m_ModelType) {
          case(0):     // Point selected
                    break;
          case(1):     // Star Selected
                    break;
          case(2):     // Sphere Selected
                    break;
          case(3):     // Density Selected
                    break;
          };
          p_stacks=dlg.m_Stacks;
          p_slices=dlg.m_Slices;
          p_transparency=dlg.m_Transparency;
          ModelType=dlg.m_ModelType;
          objectdata->ForceFullRebuild();
          Tessellate();
          Invalidate(FALSE);
     };     
}

void COpenGLView::OnSpace() 
{
     CSpaceSettingsDlg     dlg;

     dlg.m_Accuracy=0;
     dlg.m_Newton=0;
     dlg.m_Normal=0;

     if(dlg.DoModal()==IDOK) {
     };     
}

Page 479
void COpenGLView::OnCluster() 
{
     CClustersDlg     dlg;

     static int BF=0;
     static BOOL Connect=FALSE, EveryChange=FALSE, EndToEnd=FALSE;
     static int line=0;

     dlg.m_BruteForce=BF;
     dlg.m_EveryChange=EveryChange;
     dlg.m_Line=line;
     dlg.m_EndToEnd=EndToEnd;

     if(dlg.DoModal()==IDOK) {
          BF=dlg.m_BruteForce;
          line=dlg.m_Line;
          EndToEnd=dlg.m_EndToEnd;
          EveryChange=dlg.m_EveryChange;
          objectdata->ForceFullRebuild();
          Tessellate();
          Invalidate(FALSE);
     };     
}

void COpenGLView::OnCamerasettings() 
{
     CCameraSettingsDlg     dlg;

     static int BF=0;
     static BOOL Connect=FALSE, EveryChange=FALSE;

     dlg.m_Follow=IsFollow;
     dlg.m_ZoomScale=DepthScale;
     dlg.m_Aspect=aspect;
     dlg.m_CenterX=Lcenter.x;
     dlg.m_CenterY=Lcenter.y;
     dlg.m_CenterZ=Lcenter.z;
     dlg.m_EyeX=Leye.x; dlg.m_EyeY=Leye.y; dlg.m_EyeZ=Leye.z;
     dlg.m_UpX=Lup.x; dlg.m_UpY=Lup.y; dlg.m_UpZ=Lup.z;
     dlg.m_FOV=fov;
     dlg.m_ZFar=far_plane;
     dlg.m_ZNear=near_plane;

     if(dlg.DoModal()==IDOK) {
          aspect=dlg.m_Aspect;
          Lcenter.x=dlg.m_CenterX;
          Lcenter.y=dlg.m_CenterY;
          Lcenter.z=dlg.m_CenterZ;
          Leye.x=dlg.m_EyeX;
          Leye.y=dlg.m_EyeY;
          Leye.z=dlg.m_EyeZ;
          fov=dlg.m_FOV;
          Lup.x=dlg.m_UpX;
          Lup.y=dlg.m_UpY;
          Lup.z=dlg.m_UpZ;

Page 480
far_plane=dlg.m_ZFar;
          near_plane=dlg.m_ZNear;
          DepthScale=dlg.m_ZoomScale;
          IsFollow=dlg.m_Follow;
          glMatrixMode( GL_PROJECTION );
          glLoadIdentity();
          aspect=1;
          gluPerspective( fov, aspect, near_plane, far_plane );
          aspect = (GLfloat) oldrect.right / oldrect.bottom;
          glMatrixMode( GL_MODELVIEW );
          Invalidate(FALSE);
     };     
}

void COpenGLView::OnAnimatorsettings() 
{
     CAnimatorSettingsDlg     dlg;

     static float step=0.10;
     dlg.m_TrailLength=ParticleTrails;
     if(ParticleTrails==0) 
          dlg.m_EnableTrails=FALSE;
     else
          dlg.m_EnableTrails=TRUE;
     dlg.m_Type=3;
     dlg.m_Step=step;

     if(dlg.DoModal()==IDOK) {
          objectdata->SetStep(dlg.m_Step);
          step=dlg.m_Step;
          ParticleTrails=dlg.m_TrailLength;
          if(!dlg.m_EnableTrails) ParticleTrails=0;
};
}

void COpenGLView::OnSimulatoroptions() 
{
     CSimulatorSettingsDlg     dlg;

     if(dlg.DoModal()==IDOK) {
     };
}

void COpenGLView::OnDataadjust() 
{
     CDataAdjustertDlg     dlg;

     static BOOL justr=FALSE;
     dlg.m_JustR=justr;
     float dist=objectdata->GetMostDistant();
     if(dist!=0)
          dlg.m_Scale=2/dist;
     else
          dlg.m_Scale=1;

Page 481
if(dlg.DoModal()==IDOK) {
          objectdata->ScaleNumbers(dlg.m_Scale,dlg.m_JustR);
          justr=dlg.m_JustR;
          Center();
          Invalidate(FALSE);
     };
}

void COpenGLView::OnParallel() 
{
     CParallelDialogue dlg;

     static int NumberSwitch=0;
     dlg.m_NumCPUS=NumberSwitch;
     if(dlg.DoModal()==IDOK) {
          NumberSwitch=dlg.m_NumCPUS;          
          NumberCPUS=(int) pow(2,NumberSwitch);
          NumberCPUS;
     };
}

The next series of functions provides command members for selection and view updates for the various tools available in the 3D viewing model. These functions disable the mouse's "left button down" and any of the other mouse status switches that need to be toggled.

void COpenGLView::OnZoom() 
{
     down=0;
     spinmode=0;
     rdown=0;
     SelectedTool=Zoom;
}

void COpenGLView::OnUpdateZoom(CCmdUI* pCmdUI) 
{
     if(SelectedTool==Zoom)
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnNodeSelect() 
{
      down=0;
      spinmode=0;
      rdown=0;
      SelectedTool=NodeSelect;
}

Page 482
void COpenGLView::OnUpdateNodeSelect(CCmdUI* pCmdUI) 
{
     if(SelectedTool==NodeSelect)
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdateNodeSelect(CCmdUI* pCmdUI) 
{
     if(SelectedTool==NodeSelect)
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnSelect() 
{
     down=0;
     spinmode=0;
     rdown=0;
     SelectedTool=Select;
}

void COpenGLView::OnRotate() 
{
     down=0;
     spinmode=0;
     rdown=0;
     SelectedTool=Rotate;
}

void COpenGLView::OnUpdateRotate(CCmdUI* pCmdUI) 
{
     if(SelectedTool==Rotate)
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnTranslate() 
{
     SelectedTool=Translate;     
}

void COpenGLView::OnUpdateTranslate(CCmdUI* pCmdUI) 
{
     if(SelectedTool==Translate)
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}
Page 483


The next set of functions are notifications for various menu and toolbar commands that can be transmitted.

void COpenGLView::OnPmodelStar() 
{
     ModelType=1;
     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelSphere() 
{
     ModelType=2;
     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelPoint() 
{
     ModelType=0;
     objectdata->NotifyParameter(Lmsg_Points,TRUE);
     objectdata->NotifyParameter(Lmsg_Stars,FALSE);
     objectdata->NotifyParameter(Lmsg_Spheres,FALSE);
     objectdata->NotifyParameter(Lmsg_Density,FALSE);
     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelFastquality() 
{
     glHint(GL_FOG_HINT,GL_FASTEST);
     glHint(GL_LINE_SMOOTH_HINT,GL_FASTEST);
     glHint(GL_POINT_SMOOTH_HINT,GL_FASTEST);
     glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST); 

     switch(p_stacks) {
     case(3):     p_stacks=2.0; p_slices=2.0; break;
     case(4):     p_stacks=3.0; p_slices=3.0; break;
     case(6):     p_stacks=4.0; p_slices=4.0; break;
     case(8):     p_stacks=6.0; p_slices=6.0; break;
     case(10):     p_stacks=8.0; p_slices=8.0; break;
     case(15):     p_stacks=10.0; p_slices=10.0;     break;
     case(20):     p_stacks=15.0; p_slices=15.0;     break;
     case(30):     p_stacks=20.0; p_slices=20.0;     break;
     default:     p_stacks-=1.0; p_slices-=1.0;     break;
     };

     if(p_stacks<2) p_stacks=2;
     if(p_slices<2) p_slices=2;
     if(p_stacks>100) p_stacks=100;
     if(p_slices>100) p_slices=100;

     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

Page 484
void COpenGLView::OnPmodelLighting() 
{
     LightModel++;

     if(LightModel>5) LightModel=0;
     switch(LightModel) {
     case(0):     glEnable(GL_LIGHTING);
               glEnable(GL_LIGHT0);
               glDisable(GL_LIGHT1);
               glDisable(GL_LIGHT2);
               glDisable(GL_LIGHT3);
               glDisable(GL_LIGHT4);
               glDisable(GL_LIGHT5);
               glDisable(GL_LIGHT6);
               break;
     case(1):     glEnable(GL_LIGHTING);
               glDisable(GL_LIGHT0);
               glEnable(GL_LIGHT1);
               glDisable(GL_LIGHT2);
               glDisable(GL_LIGHT3);
               glDisable(GL_LIGHT4);
               glDisable(GL_LIGHT5);
               glDisable(GL_LIGHT6);
               break;
     case(2):     glEnable(GL_LIGHTING);
               glDisable(GL_LIGHT0);
               glDisable(GL_LIGHT1);
               glEnable(GL_LIGHT2);
               glDisable(GL_LIGHT3);
               glDisable(GL_LIGHT4);
               glDisable(GL_LIGHT5);
               glDisable(GL_LIGHT6);
               break;
     case(3):     glDisable(GL_LIGHT0);
               glDisable(GL_LIGHT1);
               glDisable(GL_LIGHT2);
               glEnable(GL_LIGHT3);
               glDisable(GL_LIGHT4);
               glDisable(GL_LIGHT5);
               glDisable(GL_LIGHT6);
               break;
     case(4):     glDisable(GL_LIGHT0);
               glDisable(GL_LIGHT1);
               glDisable(GL_LIGHT2);
               glDisable(GL_LIGHT3);
               glEnable(GL_LIGHT4);
               glDisable(GL_LIGHT5);
               glDisable(GL_LIGHT6);
               break;
     case(5):     glDisable(GL_LIGHTING);
               break;
     };

Page 485
IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelTransparency() 
{
     if(!IsTransparent) {
          glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
          glEnable(GL_BLEND);
          IsTransparent=TRUE;
     }
     else {
          glDisable(GL_BLEND);
          IsTransparent=FALSE;
     };     

     objectdata->SetTransparent(IsTransparent);
     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelWireframe() 
{
     if(!IsWireframe) {
          glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
          IsWireframe=TRUE;
     }
     else {
          glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
          IsWireframe=FALSE;
     };

     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelSplines() 
{
     if(IsSplines) {
          IsSplines=FALSE;
     }
     else {
          objectdata->NotifyParameter(Lmsg_DrawSplinePath,TRUE);
          IsSplines=TRUE;
     };

     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelSlow() 
{
     glHint(GL_FOG_HINT,GL_NICEST);
     glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
     glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);
     glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST); 

Page 486
switch(p_stacks) {
     case(2):     p_stacks=3.0; p_slices=3.0; break;
     case(3):     p_stacks=4.0; p_slices=4.0; break;
     case(4):     p_stacks=6.0; p_slices=6.0; break;
     case(6):     p_stacks=8.0; p_slices=8.0; break;
     case(8):     p_stacks=10.0; p_slices=10.0; break;
     case(10):     p_stacks=15.0; p_slices=15.0; break;
     case(15):     p_stacks=20.0; p_slices=20.0; break;
     case(20):     p_stacks=30.0; p_slices=30.0; break;
     default:     p_stacks+=1.0; p_slices+=1.0; break;
     };

     if(p_stacks<2) p_stacks=2;
     if(p_slices<2) p_slices=2;
     if(p_stacks>100) p_stacks=100;
     if(p_slices>100) p_slices=100;

     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnPmodelAntialias() 
{
     if(!AntiAlias) {
          if(!IsTransparent) 
               OnPmodelTransparency();
          glEnable(GL_POINT_SMOOTH);
          glEnable(GL_LINE_SMOOTH);
          AntiAlias=TRUE;
     }
     else {
          glDisable(GL_POINT_SMOOTH);
          glDisable(GL_LINE_SMOOTH);
          AntiAlias=FALSE;
     };

     IsIdleLocked=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnCenter() 
{
     if(IsFollow)
          IsFollow=FALSE;
     else
          IsFollow=TRUE;
     Center();
}

void COpenGLView::OnObjectview() 
{
     if(objwin==NULL) {
          objwin=new CObjectViewDlg(this);

Page 487
objwin->Create(IDD_OBJECTVIEW,this);
          objwin->ShowWindow(SW_SHOWNORMAL);
     }
     else {
          objwin->BringWindowToTop();
     };
}

void COpenGLView::OnAnimEnd() 
{
     IsPlaying=FALSE;
     objectdata->Frame_Last();     
     Invalidate(FALSE);
}

void COpenGLView::OnAnimFront() 
{
     IsPlaying=FALSE;
     objectdata->Frame_First();     
     Invalidate(FALSE);
}

void COpenGLView::OnAnimNext() 
{
     IsPlaying=FALSE;
     objectdata->Frame_Next();     
     Invalidate(FALSE);
}

void COpenGLView::OnAnimPlay() 
{
     IsPlaying=TRUE;
     Invalidate(FALSE);
}

void COpenGLView::OnAnimPrevious() 
{
     IsPlaying=FALSE;
     objectdata->Frame_Prev();     
     Invalidate(FALSE);
}

void COpenGLView::OnAnimStop() 
{
     IsPlaying=FALSE;
}

void COpenGLView::StopAnimation()
{
     OnAnimStop();
     IsPlaying=FALSE;
}
Page 488


The following functions are command enablers which toggle whether a button is checked, or depressed, for the various toolbars and options:

void COpenGLView::OnUpdatePmodelAntialias(CCmdUI* pCmdUI) 
{
     if(AntiAlias) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdateCenter(CCmdUI* pCmdUI) 
{
     if(IsFollow) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);

}

void COpenGLView::OnUpdatePmodelTransparency(CCmdUI* pCmdUI) 
{
     if(IsTransparent) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdateAnimPlay(CCmdUI* pCmdUI) 
{
     if(IsPlaying) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdatePmodelWireframe(CCmdUI* pCmdUI) 
{
     if(IsWireframe) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdatePmodelSplines(CCmdUI* pCmdUI) 
{
     if(IsSplines) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);

}

Page 489
void COpenGLView::OnUpdatePmodelSphere(CCmdUI* pCmdUI) 
{
     if(ModelType==2) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdatePmodelStar(CCmdUI* pCmdUI) 
{
     if(ModelType==1) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

void COpenGLView::OnUpdatePmodelPoint(CCmdUI* pCmdUI) 
{
     if(ModelType==0) 
          pCmdUI->SetCheck(1);
     else
          pCmdUI->SetCheck(0);
}

The following collection of functions implements the example drawing function setup for the renderer. The wglMakeCurrent() function is called because the application is an MDI (multiple document interface) application. By selecting the current rendering thread on redraws, this makes all of the views render well independently.

void COpenGLView::DrawModel(RECT &l)
{
     hDC=THISCWND->GetDC()->GetSafeHdc();;
     wglMakeCurrent(hDC, hrc );

     LPoint3 eyedir;
     LPoint3 Leyesave;

     Leyesave.x=Leye.x; Leyesave.y=Leye.y; Leyesave.z=Leye.z;
     LPoint3 adjcenter;
     if(IsFollow) {
          LPoint3 r=objectdata->GetCenter();
          Lcenter.x=-r.x; Lcenter.y=-r.y; Lcenter.z=-r.z;
     };

     adjcenter.x=(Lcenter.x-(Leye.x*DepthScale*DepthScale))+Leye.x;
     adjcenter.y=(Lcenter.y-(Leye.y*DepthScale*DepthScale))+Leye.y;
     adjcenter.z=(Lcenter.z-(Leye.z*DepthScale*DepthScale))+Leye.z;
     glLoadIdentity();

     glViewport(0, 0, oldrect.right, oldrect.bottom);  
     gluLookAt(Leye.x,Leye.y,Leye.z,0,0,0,Lup.x,Lup.y,Lup.z );

Page 490
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   glPushMatrix();
     glMultMatrixd(trackballMatrix);

     if(DepthScale<1)
          glScaled(.3/DepthScale,.3/DepthScale,.3/DepthScale);
     else
          glScaled(.3,.3,.3);

glTranslatef(adjcenter.x, adjcenter.y, adjcenter.z);
     objectdata->DumpFilled();
glPopMatrix();
glFinish();

     Leye.x=Leyesave.x; Leye.y=Leyesave.y; Leye.z=Leyesave.z;

SwapBuffers(hDC);
}

The OnPaint() member is called whenever the system requests a model repaint. Some repaints are delayed if the system has not entered an idle situation recently--meaning there is a lot of activity going on. Additionally, there is a KillPlay switch that is called when the system needs to stop all timed rendering.

void COpenGLView::OnPaint() 
{
     CPaintDC dc(this);
     KillTimer(6);

     if(IsIdleLocked) {
          SetTimer(6,500,NULL);
          ValidateRect(&dc.m_ps.rcPaint);
          return;
     };

     if(KillPlay) {
          KillPlay=FALSE;
          SetTimer(6,2000,NULL);
          ValidateRect(&dc.m_ps.rcPaint);
          return;
     };  

     DrawModel(dc.m_ps.rcPaint);
     ValidateRect(&dc.m_ps.rcPaint);

     if(!CanPaint)
          return;

     if(Rotating|IsPlaying) {
          if(Rotating)
               RotateAgain();

Page 491
if(IsPlaying) {
               objectdata->Frame_Play();
               pane_time=objectdata->GetCurrentTime();
          };

          if(OnToolBar) {
               OnToolBar=FALSE;
               SetTimer(6,10,NULL);
               return;
          };
          Invalidate(FALSE);
     }; 
}

OLE drag and drop support is implemented by applying calls to the OnDragEnter() and OnDragLeave() member functions. The object does not make use of OLE. We simply make sure the system is not updating or animating via mouse clicks. If OLE were to be implemented, you would implement it here.

DROPEFFECT COpenGLView::OnDragEnter(COleDataObject* pDataObject, 
DWORD dwKeyState, CPoint point) 
{
     rdown=0;
     down=0;

     return CView::OnDragEnter(pDataObject, dwKeyState, point);
}

void COpenGLView::OnDragLeave() 
{
     rdown=0;
     down=0;

     CView::OnDragLeave();
     hDC=GetDC()->GetSafeHdc();
}

The CalcWindowRect() function is used to determine the size of a window. The default behavior in this case works well so we leave it as is. The OnSize() function also provides sizing behavior by telling OpenGL what size the window should be set to, and setting flags regarding the current rendering size and state.

void COpenGLView::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType) 
{
     CView::CalcWindowRect(lpClientRect, nAdjustType);
}

void COpenGLView::OnSize(UINT nType, int cx, int cy) 
{
     CView::OnSize(nType, cx, cy);

Page 492
RECT     rect;
     IsIdleLocked=TRUE;

GetClientRect(&rect);

glViewport(0, 0, rect.right, rect.bottom);

if((oldrect.right != rect.right) ||
 (oldrect.bottom != rect.bottom))
          Invalidate(TRUE);

oldrect.right = rect.right;
   oldrect.bottom = rect.bottom;
     hDC=GetDC()->GetSafeHdc();
}

void COpenGLView::OnShowWindow(BOOL bShow, UINT nStatus) 
{
     CView::OnShowWindow(bShow, nStatus);

     IsIdleLocked=TRUE;
}

We also want to know when children are moved so we use the OnChildNotify() handler to catch WM_SIZE messages. The OnCommand() handler allows us to catch menu or toolbar events which are also sent. For both of these operations, we use our delayed repaint.

BOOL COpenGLView::OnChildNotify(UINT message, WPARAM wParam, LPARAM 
lParam, LRESULT* pLResult) 
{
     switch(message) {
     case(WM_MOVE):     IsIdleLocked=TRUE; break;
     default:          break;
     };

     return CView::OnChildNotify(message, wParam, lParam, pLResult);
}

BOOL COpenGLView::OnCommand(WPARAM wParam, LPARAM lParam) 
{
     IsIdleLocked=TRUE;
     return CView::OnCommand(wParam, lParam);
}

The NeedViewClose() command handler is called when the object view needs to be closed, so we add a handler that destroys the object view window and cleans up.

void COpenGLView::NeedViewClose() {
          objwin->DestroyWindow();
          delete objwin;
          objwin=NULL;
}
Page 493


OnActivateView() is called whenever the current view is selected or activated. This command handler enables rendering in the window and sets the necessary switches so that invalidates behave correctly and quickly when the window is activated. OnSetFocus() replicates the necessary parts of this behavior when the window is given the focus. The same is true for the function OnKillFocus() which kills the rendering of an animation in the window.

void COpenGLView::OnActivateView(BOOL bActivate, CView* pActivateView, 
CView* pDeactiveView) 
{
     if(bActivate) {
          CanPaint=TRUE;
          TimerValue=1;
          KillPlay=FALSE;
          Invalidate(FALSE);
     }
     else {
          CanPaint=FALSE;
          TimerValue=2000;
     };   

     CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
     hDC=GetDC()->GetSafeHdc();
}

void COpenGLView::OnSetFocus(CWnd* pOldWnd) 
{
     CView::OnSetFocus(pOldWnd);
     hDC=GetDC()->GetSafeHdc();
     SetTimer(6,500,NULL);
}

void COpenGLView::OnKillFocus(CWnd* pNewWnd) 
{
     KillPlay=TRUE;
     CView::OnKillFocus(pNewWnd);
}

Timer functions are used in the view to provide a delayed invalidate functionality so that the system will only render when it is free. Care should be taken to kill the timer in order to keep the event from repeating and causing unneccesary invalidates.

void COpenGLView::OnTimer(UINT nIDEvent) 
{
     CView::OnTimer(nIDEvent);

     if(nIDEvent==6) {
          KillTimer(6);
          Invalidate(FALSE);
     };
}
Page 494



The OnMove() function says that the system needs to pause before it
redraws the window. This allows the user to move the window easily and
promptly without redraw delays.
void COpenGLView::OnMove(int x, int y) 
{
     CView::OnMove(x, y);

     IsIdleLocked=TRUE;
}

The OnSetCursor() function queries to see if the window being set is the current one. If it is, then it loads the appropriate cursor for the current tool.

BOOL COpenGLView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
     if(pWnd==this){
     switch(SelectedTool) {
     case(Select):          SetCursor(AfxGetApp()
                    ->LoadCursor(IDC_SELECT));     
                    break;
     case(NodeSelect):     SetCursor(AfxGetApp()
                    ->LoadCursor(IDC_NODE));
                    break;
     case(Zoom):          SetCursor(AfxGetApp()
                    ->LoadCursor(IDC_ZOOM));
                    break;
     case(Translate):       SetCursor(AfxGetApp()
                    ->LoadCursor(IDC_TRANSLATE));
                    break;
     };
                          return 0;
     }
     else {
          return CView::OnSetCursor(pWnd, nHitTest, message);
     };
}

The following functions provide the necessary handlers for dealing with the mouse and virtual trackball. Portions of these functions are examined in detail in section 11.5.

void COpenGLView::OnMouseMove(UINT nFlags, CPoint point) 
{
     CView::OnMouseMove(nFlags, point);

     RECT     D;
     GetClientRect(&D);
     int Width=D.right;
     int Height=D.bottom;
     float temp;

Page 495
LastMouseLocation=point;

     switch(SelectedTool) {
     case(Translate):
          if(down) {
               Lup.x=0; Lup.y=1; Lup.z=0;
               Leye.x=0; Leye.y=0; Leye.z=4;
               Invalidate(FALSE);
               LPoint3 r=MoveParallelToScreen(Lcenter,
               BeginRotatePos,point);
               Lcenter.x=r.x; Lcenter.y=r.y; Lcenter.z=r.z;

               BeginRotatePos=point;
               Invalidate(FALSE);
          };
          if(rdown) {
          Lup.x=0; Lup.y=1; Lup.z=0;
               Leye.x=0; Leye.y=0; Leye.z=4;

               Lcenter.z+=-(float)((float)BeginRotatePos.y
               -point.y)/((float)Height/(float)1);
               BeginRotatePos=point;
               Invalidate(FALSE);
          };
          break;

     case(Zoom):
          if(down) {
               float changefac=(((float)LastClickPos.y
               -point.y)/(float)Height)*DepthScale;
               DepthScale+=(changefac);
               if(DepthScale<0.0001) DepthScale=0.0001;
               if(DepthScale>999999) DepthScale=999999;
               LastClickPos=point;
               Invalidate(FALSE);
          };
          if(rdown) {
               GetTrackballAngle(BeginRotatePos,point);
               RECT r;
               DrawModel(r);
               Invalidate(FALSE); 
          };
          break;

     case(Select):
          if(rdown) {
               Invalidate(FALSE);
               break;
          };
          if(down) {
          ComputeTessellationLocation();
               Invalidate(FALSE);
          };
          break;

Page 496
case(NodeSelect):
          if(rdown) {
               Invalidate(FALSE);
               break;
          };
          if(down) {
               ComputeTessellationLocation();
               };
          break;

     default:     break;
          };
}

void COpenGLView::OnRButtonDblClk(UINT nFlags, CPoint point) 
{
     CView::OnRButtonDblClk(nFlags, point);
}

void COpenGLView::OnRButtonDown(UINT nFlags, CPoint point) 
{
     rdown=1;
     RECT     D;
     GetClientRect(&D);
     int Width=D.right;
     int Height=D.bottom;
     BeginRotatePos=point;
     int i;

     Rotating=FALSE;
     trackballangle=0;
     if(rdown) {
          for(i=0;i<=15;i++) 
               starttrackballMatrix[i]=trackballMatrix[i];
          LastClickPos=point;
          };

     if((SelectedTool==NodeSelect) | (SelectedTool==Select)) {
          center.x = (GLfloat)(Width / 2);
          center.y = (GLfloat)(Height / 2);
          radius = (Width > Height) ? center.y : center.x;

          spinmode=0;
          Invalidate(FALSE);
          return;
          };

     CView::OnRButtonDown(nFlags, point);
}

void COpenGLView::OnRButtonUp(UINT nFlags, CPoint point) 
{
     switch(SelectedTool) {
     case(Zoom):

Page 497
if(rdown) {
          if(trackballangle > 1) {
                    Rotating=TRUE;
                    Invalidate(FALSE);
                    break;
               };

     float ex=trackballMatrix[0]*Leye.x
     +trackballMatrix[1]*Leye.y
     +trackballMatrix[2]*Leye.z
     +trackballMatrix[3];
     float ey=trackballMatrix[4]*Leye.x
     +trackballMatrix[5]*Leye.y
     +trackballMatrix[6]*Leye.z
     +trackballMatrix[7];
     float ez=trackballMatrix[8]*Leye.x
     +trackballMatrix[9]*Leye.y
     +trackballMatrix[10]*Leye.z
     +trackballMatrix[11];

            Leye.x=ex; Leye.y=ey; Leye.z=ez;               
     ex=trackballMatrix[0]*Lup.x
     +trackballMatrix[1]*Lup.y
     +trackballMatrix[2]*Lup.z
     +trackballMatrix[3];
     ey=trackballMatrix[4]*Lup.x
     +trackballMatrix[5]*Lup.y
     +trackballMatrix[6]*Lup.z
     +trackballMatrix[7];
     ez=trackballMatrix[8]*Lup.x
     +trackballMatrix[9]*Lup.y
     +trackballMatrix[10]*Lup.z
     +trackballMatrix[11];

                    Lup.x=ex; Lup.y=ey; Lup.z=ez;
                    glPushMatrix();
                    glLoadIdentity();
                    glGetDoublev(GL_MODELVIEW_MATRIX,
                    trackballMatrix);
                    glPopMatrix();

                    Invalidate(FALSE);
               break;
          };
     default:     break;
     };

     rdown=0;
     if(!Rotating)     
          CView::OnRButtonUp(nFlags, point);
     if(trackballangle<=1) Rotating=FALSE;
}

Page 498
void COpenGLView::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
     CView::OnLButtonDblClk(nFlags, point);
}

void COpenGLView::OnLButtonDown(UINT nFlags, CPoint point) 
{
     RECT     D;
     GetClientRect(&D);
     int Width=D.right,Height=D.bottom,i;

     LineF_t intline;
     SphereF_t sphere;
     LField *Field;
     LPoint3 C;
     down = 1;

     switch(SelectedTool) {
     case(Zoom):          LastClickPos=point;
               BeginRotatePos=point;
                    break;
     default:          break;
     };

     down=1;
     CView::OnLButtonDown(nFlags, point);
}

void COpenGLView::OnLButtonUp(UINT nFlags, CPoint point) 
{
     RECT     D;
     GetClientRect(&D);
     int Width=D.right,Height=D.bottom,i;

     down=0;     
     CView::OnLButtonUp(nFlags, point);
}

Section 11.2 details the creation scheme for getting the COpenGLView object created and enabled. The exact implementation is listed here for your reference.

int COpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
     int result = CView::OnCreate(lpCreateStruct);
     ConnectToCWND(this);
     return result;
}

void COpenGLView::OnDestroy() 
{
     CView::OnDestroy();
     DisconnectFromCWnd();
}
Page 499


The exact implementation of the ConnectToCWND() function is also shown below. The key components for OpenGL initialization are listed here. To use OpenGL in your own rendering engines, the core OpenGL initialization is contained within ConnectToCWnd() and DisconnectFromCWnd().

void COpenGLView::ConnectToCWnd(CWnd *wnd)
{
     THISCWND=wnd;

     hdc = wnd->GetDC();
     bSetupPixelFormat(hdc->GetSafeHdc());
hrc = wglCreateContext(hdc->GetSafeHdc());
wglMakeCurrent(hdc->GetSafeHdc(), hrc );

float maxObjectSize, aspect;
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glClearDepth( 1.0 );

glEnable(GL_DEPTH_TEST);
glMatrixMode( GL_PROJECTION );
aspect = (GLfloat) oldrect.right / oldrect.bottom;;
gluPerspective( fov, aspect, near_plane, far_plane );
gluLookAt(
          Leye.x,Leye.y,Leye.z,
          Lcenter.x,Lcenter.y,Lcenter.z,
          Lup.x,Lup.y,Lup.z );

GLfloat position[] = { 2, 3, 2, 0.0 };
GLfloat ambient[] = { .5, .5, .5, 1.0 };
GLfloat diffuse[] = { .35, .35, .35, 1.0 };
GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat position1[] = { -2, 3, 2, 0.0 };
GLfloat ambient1[] = { .5, .5, .5, 1.0 };
GLfloat diffuse1[] = { .35, .35, .35, 1.0 };
GLfloat specular1[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat position2[] = { 0, 3, 1, 0.0 };
GLfloat ambient2[] = { .5, .5, .5, 1.0 };
GLfloat diffuse2[] = { .35, .35, .35, 1.0 };
GLfloat specular2[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat position3[] = { 0, -3, 1, 0.0 };
GLfloat ambient3[] = { .5, .5, .5, 1.0 };
GLfloat diffuse3[] = { .35, .35, .35, 1.0 };
GLfloat specular3[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat position4[] = { 0, -3, 5, 0.0 };
GLfloat ambient4[] = { .5, .5, .5, 1.0 };

Page 500
GLfloat diffuse4[] = { .35, .35, .35, 1.0 };
GLfloat specular4[] = { 1.0, 1.0, 1.0, 1.0 };

GLfloat exponent[] = { 20.0 };
GLfloat cutoff[] = { 45.0 };
GLfloat ca[] = { 1.5 };
GLfloat la[] = { 0.5 };
GLfloat qa[] = { 0.2 };
GLfloat spotdir[] = { -2.0, -3.0, -2.0 };

glLightfv(GL_LIGHT0,GL_POSITION,position);
glLightfv(GL_LIGHT0,GL_AMBIENT,ambient);
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuse);
glLightfv(GL_LIGHT0,GL_SPECULAR,specular);

glLightfv(GL_LIGHT1,GL_POSITION,position1);
glLightfv(GL_LIGHT1,GL_AMBIENT,ambient1);
glLightfv(GL_LIGHT1,GL_DIFFUSE,diffuse1);
glLightfv(GL_LIGHT1,GL_SPECULAR,specular1);

glLightfv(GL_LIGHT2,GL_POSITION,position2);
glLightfv(GL_LIGHT2,GL_AMBIENT,ambient2);
glLightfv(GL_LIGHT2,GL_DIFFUSE,diffuse2);
glLightfv(GL_LIGHT2,GL_SPECULAR,specular2);

glLightfv(GL_LIGHT3,GL_POSITION,position3);
glLightfv(GL_LIGHT3,GL_AMBIENT,ambient3);
glLightfv(GL_LIGHT3,GL_DIFFUSE,diffuse3);
glLightfv(GL_LIGHT3,GL_SPECULAR,specular3);

glLightfv(GL_LIGHT4,GL_POSITION,position4);
glLightfv(GL_LIGHT4,GL_AMBIENT,ambient4);
glLightfv(GL_LIGHT4,GL_DIFFUSE,diffuse4);
glLightfv(GL_LIGHT4,GL_SPECULAR,specular4);

glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_FALSE);
GLfloat high_shininess[] = { 100.0 };
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, high_shininess);

glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
glGetDoublev(GL_MODELVIEW_MATRIX,trackballMatrix);
glLoadIdentity();
glGetDoublev(GL_MODELVIEW_MATRIX,starttrackballMatrix);
glPopMatrix();

GLfloat lmodel_ambient[] = { 0.1, 0.1, 0.1, 1.0 };
GLfloat local_view[] = { 0 };

glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, local_view); 
glEnable(GL_CULL_FACE);

Page 501
glCullFace(GL_BACK);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);

glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_LIGHTING);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POINT_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
glHint(GL_FOG_HINT,GL_FASTEST);
glHint(GL_LINE_SMOOTH_HINT,GL_FASTEST);
glHint(GL_POINT_SMOOTH_HINT,GL_FASTEST);
glHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST); 
hDC=wnd->GetDC()->GetSafeHdc();

glMatrixMode( GL_PROJECTION );
glLoadIdentity();
aspect=1;
gluPerspective( fov, aspect, near_plane, far_plane );
aspect = (GLfloat) oldrect.right / oldrect.bottom;
glMatrixMode( GL_MODELVIEW );

Invalidate(FALSE);
}

void COpenGLView::DisconnectFromCWnd()
{
     wglDeleteContext(hrc);
}

The implementation of bSetupPixelFormat() is listed here. This function queries the capabilities of the current device and matches them as closely as possible to the desired OpenGL rendering context. If no match is possible it notifies the user of that as well.

BOOL COpenGLView::bSetupPixelFormat(HDC hDC)
{
    int pixelformat;

    if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
    {
        ::MessageBox(NULL, "ChoosePixelFormat failed", "Error", 
                      MB_OK);
        return FALSE;
    };

    if (SetPixelFormat(hDC, pixelformat, &pfd) == FALSE)
    {
        ::MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
        return FALSE;
    };

Page 502
CreateRGBPalette(hDC);
return TRUE;
}

The bSetupPixelFormat() makes a call to the CreateRGBPalette() member. This member creates the necessary palette information for OpenGL. The code to complete this tedious task in conjunction with the other elements in the COpenGLView object is shown as follows:

unsigned char threeto8[8] = {
    0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377
};

unsigned char twoto8[4] = {
    0, 0x55, 0xaa, 0xff
};

unsigned char oneto8[2] = {
    0, 255
};

static int defaultOverride[13] = {
    0, 3, 24, 27, 64, 67, 88, 173, 181, 236, 247, 164, 91
};

static PALETTEENTRY defaultPalEntry[20] = {
    { 0,   0,   0,    0 },
    { 0x80,0,   0,    0 },
    { 0,   0x80,0,    0 },
    { 0x80,0x80,0,    0 },
    { 0,   0,   0x80, 0 },
    { 0x80,0,   0x80, 0 },
    { 0,   0x80,0x80, 0 },
    { 0xC0,0xC0,0xC0, 0 },

    { 192, 220, 192,  0 },
    { 166, 202, 240,  0 },
    { 255, 251, 240,  0 },
    { 160, 160, 164,  0 },

    { 0x80,0x80,0x80, 0 },
    { 0xFF,0,   0,    0 },
    { 0,   0xFF,0,    0 },
    { 0xFF,0xFF,0,    0 },
    { 0,   0,   0xFF, 0 },
    { 0xFF,0,   0xFF, 0 },
    { 0,   0xFF,0xFF, 0 },
    { 0xFF,0xFF,0xFF, 0 }
};

unsigned char
COpenGLView::ComponentFromIndex(int i, UINT nbits, UINT shift)
{
    unsigned char val;

Page 503
val = (unsigned char) (i >> shift);
    switch (nbits) {
    case 1:     val &= 0x1; return oneto8[val];
    case 2:     val &= 0x3; return twoto8[val];
    case 3:     val &= 0x7; return threeto8[val];
    default:   return 0;
    };
}

void
COpenGLView::CreateRGBPalette(HDC hDC)
{
    PIXELFORMATDESCRIPTOR pfd;
    LOGPALETTE *pPal;
    int n, i;

    n = GetPixelFormat(hDC);
    DescribePixelFormat(hDC, n,
          sizeof(PIXELFORMATDESCRIPTOR),      &pfd);

    if (pfd.dwFlags & PFD_NEED_PALETTE) {
        n = 1 << pfd.cColorBits;
        pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, 
          sizeof(LOGPALETTE)
               + n * sizeof(PALETTEENTRY));
        pPal->palVersion = 0x300;
        pPal->palNumEntries = n;
        for (i=0; i<n; i++) {
                 pPal->palPalEntry[i].peRed =ComponentFromIndex(i, 
          pfd.cRedBits, pfd.cRedShift);
                 pPal->palPalEntry[i].peGreen =ComponentFromIndex(i, 
          pfd.cGreenBits, pfd.cGreenShift);
          pPal->palPalEntry[i].peBlue =ComponentFromIndex(i, 
          pfd.cBlueBits, pfd.cBlueShift);
          pPal->palPalEntry[i].peFlags = 0;
        }

if ((pfd.cColorBits == 8) &&
            (pfd.cRedBits   == 3) && (pfd.cRedShift   == 0) &&
            (pfd.cGreenBits == 3) && (pfd.cGreenShift == 3) &&
            (pfd.cBlueBits  == 2) && (pfd.cBlueShift  == 6)
           ) {
            for (i = 1 ; i <= 12 ; i++)
                pPal->palPalEntry[defaultOverride[i]] = 
          defaultPalEntry[i];
        };

        ghPalette = CreatePalette(pPal);
        LocalFree(pPal);

        ghpalOld = SelectPalette(hDC, ghPalette, FALSE);
        n = RealizePalette(hDC);
    };
}