/*
   CMSC 427 - Term Project Candidate
   ---------------------------------

   Beth Weinstein - bweinste@wam.umd.edu
   Ben Stewart - stewartb@wam.umd.edu
   Nawfel Tricha - Topaz@endorproductions.com

   Minimal OpenGL project using Microsoft Visual C++.
   This project is a premilinary candidate for the term 
   project required by CMSC 427.

   ABSTRACT:
      In this project, we will develop several fast 
      rendering and collision routines which would 
      require C/C++ speed and accuracy and then 
      implement a VisualJ++ interface.

   MODULE:
      Main rendering module.
*/

#include "Global.h"
#include "RenderGL.h"
#include <mmsystem.h>
#include <math.h>




/*
   NT:
   Initialize the engine.
*/
static void InitEngine()
{
   // Randomize:
   srand(timeGetTime());
   memset(&g_skins, 0, sizeof(g_skins));
   memset(&g_edicts, 0, sizeof(g_edicts));
   memset(&g_tedicts, 0, sizeof(g_tedicts));
   memset(&g_parts, 0, sizeof(g_parts));
   memset(&g_models, 0, sizeof(g_models));   
   memset(&g_CMD2s, 0, sizeof(g_CMD2s));   
   memset(&cam, 0, sizeof(cam));
   memset(&eng, 0, sizeof(eng));
   memset(&dsp, 0, sizeof(dsp));
   //memset(last_skin_name, 0, 255);
   AngleVectors(cam.angle, cam.forward, cam.right, cam.up);

   r_mode = RENDER_BETH_ENGINE | RENDER_BEN_ENGINE 
            | RENDER_NAWFEL_ENGINE
            | RENDER_FLUSH_ENGINE
            | RENDER_HUD | RENDER_CLEAR_ACC;
   r_effect = EFFECT_SPECTRUM;
   // | RENDER_CLEAR_ACC [Disabled since the console overwrites the background];
   // r_mode = RENDER_WIREFRAME | RENDER_FRONT_AND_BACK | RENDER_SMOOTH;

   // NT: Load font texture into memory:
   LoadFont("c:/DiscoStu/gfx/conchars.tga");
   eng.time = timeGetTime();
   
   // NT: Initialize all 3 engines:
   BethInit();
   BenInit();
   NawfelInit();
   FlushInit();

   // NT: Set OpenGL states:
   InitGL();
}

/*
   NT:
   Setup the viewing frustum:
*/
inline void SetupFrustum(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
{
   GLdouble xmin, xmax, ymin, ymax;

   ymax = zNear * tan( fovy * M_PI / 360.0 );
   ymin = -ymax;

   xmin = ymin * aspect;
   xmax = ymax * aspect;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
   glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
}

/*
   NT:
   Setup OpenGL and link it to the main application window hWnd.
*/
VOID EnableOpenGL(HWND hWnd, HDC *hDC, HGLRC *hRC)
{
   PIXELFORMATDESCRIPTOR pfd;
   int iFormat;
  
   // NT: get the device context (DC)
   *hDC = GetDC( hWnd );

   // NT: set the pixel format for the DC
   ZeroMemory( &pfd, sizeof( pfd ) );
   pfd.nSize = sizeof( pfd );
   pfd.nVersion = 1;
   pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
   pfd.iPixelType = PFD_TYPE_RGBA;
   pfd.cRedBits = 8;
   pfd.cGreenBits = 8;
   pfd.cBlueBits = 8;
   pfd.cColorBits = 16;
   pfd.cDepthBits = 16;
   pfd.iLayerType = PFD_MAIN_PLANE;
   iFormat = ChoosePixelFormat( *hDC, &pfd );
   SetPixelFormat( *hDC, iFormat, &pfd );
  
   // NT: Create and enable the render context (RC)
   *hRC = wglCreateContext( *hDC );
   wglMakeCurrent( *hDC, *hRC );

   InitEngine();
   SetupFrustum(90, 1.3f, 4, 4096);

   cam.origin[1] = 40;
}

/*
   NT:
   Disable OpenGL and unlink from application window:
*/
VOID DisableOpenGL( HWND hWnd, HDC hDC, HGLRC hRC )
{
   wglMakeCurrent( NULL, NULL );
   wglDeleteContext( hRC );
   ReleaseDC( hWnd, hDC );
}


/*
   NT: 
   Initialize OpenGL parameters
*/
void InitGL()
{
   // NT: Use local variable instead of r_mode since it's faster than globals:
   int cr_mode = r_mode;

	glFlush();
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

   // Black color background:
   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   
   // NT: WireFrame:
   glEnable(GL_DEPTH_TEST);
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);
   // NT: Textured:   
   glEnable(GL_TEXTURE_2D);
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   glPolygonMode (GL_FRONT, GL_FILL);
   // NT: Smooth shading:
   glShadeModel(GL_SMOOTH);
	glEnable(GL_POLYGON_SMOOTH);
   // NT: Blending:
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glDisable(GL_ALPHA_TEST);

   // NT: Clear the screen once at least:
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

}


/*
   NT:
   Main rendering routine.
*/
void RenderFrame(struct winampVisModule *this_mod)
{   
   static float fadein = 1.0f;
   vec3_t origin = {0, 0, 0};
   float scale_scene;
   int cr_mode = r_mode;

   // Get DSP function:
   scale_scene = DSPGetBass(this_mod, DSP_SPECTRUM_KICK);
   
   // NT: Clear the screen:
   if (cr_mode & RENDER_CLEAR_ACC)
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   else
      glClear(GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);

   // Increase stanza of engine narative:
   // Idealy, only one of the engines will set eng.next_stanza
   // Each engine will check if its their time to narate the
   // next scene. Engines not concerned should return from their stanza
   // functions quietly:
   // Example:
   // Beth could take stanza 1 and 3,
   // Ben would narate stanza 4 and 5
   // Nawfel would render stanza 2 and 6
   // Flush engine can overlap the stanzas
   if (eng.next_stanza)
      {
      if (eng.next_stanza < eng.time)
         {
         eng.stanza++;
         BethStanza(eng.stanza);
         BenStanza(eng.stanza);
         NawfelStanza(eng.stanza);
         FlushStanza(eng.stanza);
         }
      }

   // NT: Display the console (background)
   if (cr_mode & RENDER_CONSOLE)
      DisplayConsole(scale_scene);
   else
      glLoadIdentity();

   if (cam.target)
      cam.target->camera(cam.target);
   else
      {
	   glLoadIdentity();
      glRotatef(-cam.angle[PITCH], 1, 0, 0);
      glRotatef(-cam.angle[YAW], 0, 1, 0);
      glRotatef(-cam.angle[ROLL], 0, 0, 1);
      glTranslatef(-cam.origin[0], -cam.origin[1], -cam.origin[2]);
      }
  
   // Render all three engines:
   if (cr_mode & RENDER_BETH_ENGINE)
      BethMain(this_mod);
   if (cr_mode & RENDER_BEN_ENGINE)
      BenMain(this_mod);
   if (cr_mode & RENDER_NAWFEL_ENGINE)
      NawfelMain(this_mod);

   // NT: Draw all entities:
   DrawEdicts();

   // Render Flush:
   if (cr_mode & RENDER_FLUSH_ENGINE)
      {
      glPushMatrix();
      FlushMain(this_mod);
      glPopMatrix();
      }

   // NT: Draw all entities:
   DrawtEdicts();

   // NT: Displaye the HUD (overlay)
   if (cr_mode & RENDER_HUD)
      DisplayHUD(1);
   else
      glLoadIdentity();

   if (cr_mode & RENDER_DSP_LIGHT)
      {
      glDisable(GL_TEXTURE_2D);
      glColor4f(0, 0, 0, 1 - dsp.kick);
      glBegin(GL_TRIANGLE_STRIP);
         glVertex3f(-5.2, -4, -4);
         glVertex3f(5.2, -4, -4);
         glVertex3f(-5.2, 4, -4);
         glVertex3f(5.2, 4, -4);
      glEnd();
      glEnable(GL_TEXTURE_2D);
      }
   else if (fadein)
      {
      fadein -= .004f;
      glDisable(GL_TEXTURE_2D);
      glColor4f(0, 0, 0, fadein);
      glBegin(GL_TRIANGLE_STRIP);
         glVertex3f(-5.2, -4, -4);
         glVertex3f(5.2, -4, -4);
         glVertex3f(-5.2, 4, -4);
         glVertex3f(5.2, 4, -4);
      glEnd();
      glEnable(GL_TEXTURE_2D);
      }

   if (cr_mode & RENDER_HELP)
      DisplayHELP();

   // NT: Execute any rendering left
   glFlush();

   // NT: Swap the buffers
	SwapBuffers(m_hDC); 
}

