// OpenGLBase.cpp : implementation file
//

#define OPENGLBASE_VERSION 1.10

#include "stdafx.h"
#include "OpenGLBase.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// COpenGLBase

COpenGLBase::COpenGLBase()
{
  m_hRC         = NULL;
  m_hDC         = NULL;
  m_bInit       = FALSE;
  m_fTextHeight = 0.0;
}

COpenGLBase::~COpenGLBase()
{
}


BEGIN_MESSAGE_MAP(COpenGLBase, CWnd)
	//{{AFX_MSG_MAP(COpenGLBase)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COpenGLBase message handlers

static boolean (APIENTRY *wglSwapIntervalEXT)(GLint interval);

DWORD COpenGLBase::InitGL()
{
  glShadeModel(GL_SMOOTH);						                    // Enables Smooth Shading
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);					          // Black Background
  glClearDepth(1.0f);							                        // Depth Buffer Setup
  glEnable(GL_DEPTH_TEST);						                    // Enables Depth Testing
	glDepthFunc(GL_LEQUAL);							                    // The Type Of Depth Test To Do
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);			// Really Nice Perspective Calculations
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

  wglSwapIntervalEXT = (unsigned char (__stdcall *)(int))wglGetProcAddress("wglSwapIntervalEXT");
  if(wglSwapIntervalEXT)
  {
    // use 0 to disable sync, or 1 to wait for sync 
    wglSwapIntervalEXT(0);
  }

  return 1;
}

DWORD COpenGLBase::CreateGLWindow()
{
  GLint     tex_size;
  GLuint		PixelFormat;						// Holds The Results After Searching For A Match
  DWORD     pixelformats;
  
  PIXELFORMATDESCRIPTOR tpfd, pfd=	// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),	// Size Of This Pixel Format Descriptor
		1,								              // Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,						    // Must Support Double Buffering
		PFD_TYPE_RGBA,							    // Request An RGBA Format
		32,								              // Select Our 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,							        // Accumulation Bits Ignored
		16,								              // 16Bit Z-Buffer (Depth Buffer)
		0,								              // No Stencil Buffer
		0,								              // No Auxiliary Buffer
		PFD_MAIN_PLANE,	  					    // Main Drawing Layer
		0,  							              // Reserved
		0, 0, 0								          // Layer Masks Ignored
	};

  if (!(m_hDC=::GetDC(m_hWnd)))			                      // Did We Get A Device Context?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  pixelformats = DescribePixelFormat(m_hDC, 0, 0, NULL);
  for(DWORD i=0;i<pixelformats;i++)
    DescribePixelFormat(m_hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &tpfd);

  if (!(PixelFormat=ChoosePixelFormat(m_hDC,&pfd)))				// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if(!SetPixelFormat(m_hDC,PixelFormat,&pfd))				      // Are We Able To Set The Pixel Format?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if (!(m_hRC=wglCreateContext(m_hDC)))					          // Are We Able To Get A Rendering Context?
	{
		KillGLWindow();	  		                                // Reset The Display
		MessageBox("Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if(!wglMakeCurrent(m_hDC, m_hRC))						            // Try To Activate The Rendering Context
	{
		KillGLWindow();							                          // Reset The Display
		MessageBox("Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;							
	}

  // Create font.
  BuildFont();

  // Make sure we have enough texture map memory.
  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tex_size);
  if(tex_size < TWIDTH || tex_size < THEIGHT)
  {
    AfxMessageBox("Not enough OpenGL texture map memory!");
  }

  m_bInit = TRUE;
  
  return 1;
	/*
  GLuint		PixelFormat;						// Holds The Results After Searching For A Match

  PIXELFORMATDESCRIPTOR pfd2 = {0};
  
  static PIXELFORMATDESCRIPTOR pfd=	// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),	// Size Of This Pixel Format Descriptor
		1,								              // Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL  |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,						    // Must Support Double Buffering
		PFD_TYPE_RGBA,							    // Request An RGBA Format
		32,								              // Select Our 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,							        // Accumulation Bits Ignored
		8,								              // 16Bit Z-Buffer (Depth Buffer)
		0,								              // No Stencil Buffer
		0,								              // No Auxiliary Buffer
		PFD_MAIN_PLANE,							    // Main Drawing Layer
		0,								              // Reserved
		0, 0, 0								          // Layer Masks Ignored
	};

  if (!(m_hDC=::GetDC(m_hWnd)))			                      // Did We Get A Device Context?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if (!(PixelFormat=ChoosePixelFormat(m_hDC,&pfd)))				// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if(!SetPixelFormat(m_hDC,PixelFormat,&pfd))				      // Are We Able To Set The Pixel Format?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if (!(m_hRC=wglCreateContext(m_hDC)))					          // Are We Able To Get A Rendering Context?
	{
		KillGLWindow();	  		                                // Reset The Display
		MessageBox("Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if(!wglMakeCurrent(m_hDC, m_hRC))						            // Try To Activate The Rendering Context
	{
		KillGLWindow();							                          // Reset The Display
		MessageBox("Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;							
	}

  BuildFont();

  m_bInit = TRUE;

  return 1;
  */
}

DWORD COpenGLBase::KillGLWindow()
{
  if(m_hRC)								// Do We Have A Rendering Context?
	{
    wglMakeCurrent(m_hDC, m_hRC);

    if(!wglMakeCurrent(NULL,NULL))				// Are We Able To Release The DC And RC Contexts?
		{
      	MessageBox("Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        TRACE("Release Of DC And RC Failed, %u\n", GetLastError());
		}
    
    if (m_hDC && !::ReleaseDC(m_hWnd,m_hDC)) // Are We Able To Release The DC
  	{
		  MessageBox("Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		  m_hDC = NULL;				
	  }


    if(!wglDeleteContext(m_hRC))		       // Are We Able To Delete The RC?
		{
	    MessageBox("Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		m_hRC = NULL;					
	}

  KillFont();

  m_bInit = FALSE;

  return 1;
}

#define FONT_HEIGHT 10

GLvoid COpenGLBase::BuildFont(GLvoid)					// Build Our Bitmap Font
{
  HFONT	font;						          // Windows Font ID
  HFONT	oldfont;				        	// Used For Good House Keeping
  GLint viewport[4];
  GLdouble mvmatrix[16], projmatrix[16];
  GLdouble wx =0, wy1=0, wy2=0, wz=0;
  

	m_fontBase = glGenLists(224);		                  // Storage For 192 Characters

	font = CreateFont(-FONT_HEIGHT,				            // Height Of Font
	                  0,				                      // Width Of Font
	                  0,				                      // Angle Of Escapement
	                  0,				                      // Orientation Angle
	                  0,			                        // Font Weight
	                  FALSE,				                  // Italic
	                  FALSE,				                  // Underline
	                  FALSE,				                  // Strikeout
	                  ANSI_CHARSET,			              // Character Set Identifier
	                  OUT_TT_PRECIS,			            // Output Precision
	                  CLIP_DEFAULT_PRECIS,		        // Clipping Precision
	                  ANTIALIASED_QUALITY,		        // Output Quality
	                  FF_DONTCARE|DEFAULT_PITCH,	    // Family And Pitch
	                  "Arial Unicode MS");			      // Font Name

  if(!font)
  {
  	font = CreateFont(-FONT_HEIGHT,				          // Height Of Font
	                    0,				                    // Width Of Font
	                    0,				                    // Angle Of Escapement
	                    0,				                    // Orientation Angle
	                    0,			                      // Font Weight
	                    FALSE,				                // Italic
	                    FALSE,				                // Underline
	                    FALSE,				                // Strikeout
	                    ANSI_CHARSET,			            // Character Set Identifier
	                    OUT_TT_PRECIS,			          // Output Precision
	                    CLIP_DEFAULT_PRECIS,		      // Clipping Precision
	                    ANTIALIASED_QUALITY,		      // Output Quality
	                    FF_DONTCARE|DEFAULT_PITCH,	  // Family And Pitch
	                    "Arial");                     // Font Name

    if(!font)
    {
      TRACE("Could not create font for OpenGL.\n");
      return;
    }
  }

	oldfont = (HFONT)SelectObject(m_hDC, font);		    // Selects The Font We Want
	
  // Call it twice.. it must be OpenGL bug?
  wglUseFontBitmaps(m_hDC, 32, 224, m_fontBase); 	  // Builds 192 Characters Starting At Character 32
  wglUseFontBitmaps(m_hDC, 32, 224, m_fontBase); 	  // Builds 192 Characters Starting At Character 32
    
  GetCharWidth(m_hDC, 0, 255, m_iFontWidths);
  
  SelectObject(m_hDC, oldfont);				              // Selects The Font We Want
  DeleteObject(font);					                      // Delete The Font

  // Calculate font heigth
  glGetIntegerv(GL_VIEWPORT, viewport);
  glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
  glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
  
  gluUnProject(0.0, 1.0, 0.9999, mvmatrix, projmatrix, viewport, &wx, &wy1, &wz);
  gluUnProject(0.0, FONT_HEIGHT, 0.9999, mvmatrix, projmatrix, viewport, &wx, &wy2, &wz);

  m_fTextHeight = (float)(wy2 - wy1);
}

GLvoid COpenGLBase::KillFont(GLvoid)						// Delete The Font List
{
 	glDeleteLists(m_fontBase, 96);				// Delete All 96 Characters ( NEW )
}


DWORD COpenGLBase::GetTextWidth(char *pText)
{
  DWORD width = 0, length, i;

  length = strlen(pText);

  for(i=0; i<length; i++)
    width += m_iFontWidths[pText[i]];

  return width;
}

float COpenGLBase::glGetTextWidth(char *pText)
{
  DWORD width;
  GLint viewport[4];
  GLdouble mvmatrix[16], projmatrix[16];
  GLdouble wx1, wx2, wy, wz;

  width = GetTextWidth(pText);

  glGetIntegerv(GL_VIEWPORT, viewport);
  glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
  glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
  
  gluUnProject(1, 0, 0.0f, mvmatrix, projmatrix, viewport, &wx1, &wy, &wz);
  gluUnProject(width, 0, 0.0f, mvmatrix, projmatrix, viewport, &wx2, &wy, &wz);
  
  return float(wx2 - wx1);
}


GLvoid COpenGLBase::glPrint(const char *fmt, ...)				// Custom GL "Print" Routine
{
	char		text[256];				    // Holds Our String
	va_list		ap;					        // Pointer To List Of Arguments

	if (fmt == NULL)					    // If There's No Text
		return;						          // Do Nothing

	va_start(ap, fmt);					  // Parses The String For Variables
	vsprintf(text, fmt, ap);      // And Converts Symbols To Actual Numbers
	va_end(ap);						        // Results Are Stored In Text


	glPushAttrib(GL_LIST_BIT);		// Pushes The Display List Bits		( NEW )
	glListBase(m_fontBase - 32);	// Sets The Base Character to 32	( NEW )

	glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);	// Draws The Display List Text	( NEW )
	glPopAttrib();						                          // Pops The Display List Bits	( NEW )
}

DWORD COpenGLBase::ResizeGL(int x, int y)
{
  /*
m_y = y = y ? y : 1;
  m_x = x = x ? x : 1;
  
  if(!m_bInit)
    return 0;

  double currentratio = m_x/m_y;
  double myratio	  = 0;
  
  wglMakeCurrent(m_hDC, m_hRC);

  glViewport(0, 0, x, y);

  glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
  glLoadIdentity();							          // Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	glOrtho(-0.5*y/x , 0.5*x/y , 0.0, 1.0, 0.0, -1.0);

	glMatrixMode(GL_MODELVIEW);						  // Select The Modelview Matrix
  
  Render();				                        // Draw The Scene

  return 1;


  */

  m_y = y = y ? y : 1;
  m_x = x = x ? x : 1;
 // y = y ? y : 1;
  //x = x ? x : 1;
  
  if(!m_bInit)
    return 0;
  
  wglMakeCurrent(m_hDC, m_hRC);

  glViewport(0,0,x+1,y+1);

  glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();							          // Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(61.92751306f,(GLfloat)x/(GLfloat)y,1.0f,11.0f);

	glMatrixMode(GL_MODELVIEW);						  // Select The Modelview Matrix
	glLoadIdentity();	
  
  Render(TRUE);				                        // Draw The Scene
  SwapBuffers(m_hDC);			                // Swap Buffers (Double Buffering)

  return 1;
}


float COpenGLBase::glGetTextHeight()
{
  GLint viewport[4];
  GLdouble mvmatrix[16], projmatrix[16];
  GLdouble wx, wy1, wy2, wz;
  
  glGetIntegerv(GL_VIEWPORT, viewport);
  glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
  glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
  
  gluUnProject(0, 1, 0.0f, mvmatrix, projmatrix, viewport, &wx, &wy1, &wz);
  gluUnProject(0, 10, 0.0f, mvmatrix, projmatrix, viewport, &wx, &wy2, &wz);

  return (float)(wy2 - wy1);
}


DWORD COpenGLBase::CreateGLWindowSlave(HGLRC hRC)
{
  GLuint		PixelFormat;						// Holds The Results After Searching For A Match

  if(!hRC)                          // Check parameter
    return 0;

  static PIXELFORMATDESCRIPTOR pfd=	// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),	// Size Of This Pixel Format Descriptor
		1,								              // Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL  |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,						    // Must Support Double Buffering
		PFD_TYPE_RGBA,							    // Request An RGBA Format
		32,								              // Select Our 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,							        // Accumulation Bits Ignored
		8,								              // 16Bit Z-Buffer (Depth Buffer)
		0,								              // No Stencil Buffer
		0,								              // No Auxiliary Buffer
		PFD_MAIN_PLANE,							    // Main Drawing Layer
		0,								              // Reserved
		0, 0, 0								          // Layer Masks Ignored
	};

  if (!(m_hDC=::GetDC(m_hWnd)))			                      // Did We Get A Device Context?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if (!(PixelFormat=ChoosePixelFormat(m_hDC,&pfd)))				// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  if(!SetPixelFormat(m_hDC,PixelFormat,&pfd))				      // Are We Able To Set The Pixel Format?
	{
		KillGLWindow();				                                // Reset The Display
		MessageBox("Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return 0;
	}

  m_hRC   = hRC;
  
  BuildFont();

  m_bInit = TRUE;

  return 1;
}

void COpenGLBase::glCircle(float x, float y, float r)
{
  double angle;
  
  glBegin(GL_LINE_LOOP);
  for (angle=0; angle<2*PI-0.001; angle+=PI/40)
    glVertex2f(x + r * (float)cos(angle), y + r * (float)sin(angle));
  glEnd();
}

HGLRC COpenGLBase::GetRC()
{
  return m_hRC;
}

DWORD COpenGLBase::KillGLWindowSlave()
{
  if (m_hDC && !::ReleaseDC(m_hWnd, m_hDC)) // Are We Able To Release The DC
  {
		MessageBox("Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		m_hDC = NULL;				
	}

  return 1;
}

DWORD COpenGLBase::SetCurrentContext()
{
  if(!wglMakeCurrent(m_hDC, m_hRC))
  {
    TRACE("wglMakeCurrent failed, %u\n", GetLastError());
    return 0;
  }

  glViewport(0, 0, m_x, m_y);

  glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();							          // Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
  glOrtho(-0.5*m_x/m_y, 0.5*m_x/m_y, 0.0, 1.0, 0.0, -1.0);

  glMatrixMode(GL_MODELVIEW);						  // Select The Modelview Matrix

  return 1;
}

DWORD COpenGLBase::glSemiCircle(double x, double y, double r, double start, double stop)
{
  double angle;

  glBegin(GL_LINE_LOOP);
  
  for (angle=start; angle<(stop+0.001); angle+=PI/40)
    glVertex2d(x + r * cos(angle), y + r * sin(angle));

  glEnd();

  gl_setcolor(GLBLACK);
  gl_line(x-r,y, x+r, y);
  
  return 1;
}

DWORD COpenGLBase::glSemiFilledCircle(double x, double y, double r, double start, double stop)
{
  double angle;

  glBegin(GL_TRIANGLE_STRIP);

  if( stop > start)
  {
    for (angle=start; angle<(stop-0.001), angle > (stop - 2*PI); angle-=PI/100)
    {
      glVertex2d(x, y);
      glVertex2d(x + r * cos(angle), y + r * sin(angle));
    }
  }
  else
  {
    for (angle=start; angle>(stop-0.001); angle-=PI/100)
    {
      glVertex2d(x, y);
      glVertex2d(x + r * cos(angle), y + r * sin(angle));
    }
  }

  glVertex2d(x, y);
  glVertex2d(x + r * cos(stop), y + r * sin(stop));

  glEnd();
  
  return 1;
}
