/*
   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:
      Alias model module.
*/


#include "global.h"

/*
   NT:
   Spawn next model in list, initialize, and return index.
*/
int CMD2Spawn(char *model_name, CMD2 *model)
{
   int x = 0;
   while (g_CMD2s[x] && x < G_MAX_CMD2S)
      x++;
   if (x == G_MAX_CMD2S)
      {
      EngineError("SpawnCMD2: CMD2 library is full. Quitting.\n");
      return NOT_FOUND;
      }
   g_CMD2s[x] = new CMD2_list_t;
   memset (g_CMD2s[x], 0, sizeof(*g_CMD2s[x]));
   if (x >= eng.num_CMD2s)
      eng.num_CMD2s++;
   g_CMD2s[x]->model_name = new char[strlen(model_name) + 1];
   strcpy (g_CMD2s[x]->model_name, model_name);
   g_CMD2s[x]->model = model;
   return x;
}

/*
   NT:
   Return index of model in list, or NOT_FOUND.
*/
int CMD2Exist(char *model_name)
{
   int x = 0;
   if (!model_name)
      return NOT_FOUND;
   for (; x < eng.num_CMD2s; x++)
      if (g_CMD2s[x])
         if (!strcmpi(g_CMD2s[x]->model_name, model_name))
            return x;
   return NOT_FOUND;
}

/*
   NT:
   Spawn next model in list, initialize, and return index.
*/
int ModelSpawn(char *model_name, mdl_t *model)
{
   int x = 0;
   while (g_models[x] && x < G_MAX_MODELS)
      x++;
   if (x == G_MAX_MODELS)
      {
      EngineError("SpawnModel: Model library is full. Quitting.\n");
      return -1;
      }
   g_models[x] = new model_list_t;
   memset (g_models[x], 0, sizeof(*g_models[x]));
   if (x >= eng.num_models)
      eng.num_models++;
   g_models[x]->model_name = new char[strlen(model_name) + 1];
   strcpy (g_models[x]->model_name, model_name);
   g_models[x]->model = model;
   return x;
}

/*
   NT:
   Return index of model in list, or NOT_FOUND.
*/
int ModelExist(char *model_name)
{
   int x = 0;
   if (!model_name)
      return NOT_FOUND;
   for (; x < eng.num_models; x++)
      if (g_models[x])
         if (!strcmpi(g_models[x]->model_name, model_name))
            return x;
   return NOT_FOUND;
}

/*
   Update texture coordinates for model:
*/
void UpdateTextureCoordinates(CMD2 *mdl, skin_t *skin)
{
   int i;
   float width = skin->width, height = skin->height;
   if (width == 0 || height == 0)
      return;
	for (i = 0; i < mdl->getnumberTriangles(); i++) 
		{
		mdl->triangle[i].a_s /= width;
      mdl->triangle[i].a_t /= height;
		mdl->triangle[i].b_s /= width;
      mdl->triangle[i].b_t /= height;
		mdl->triangle[i].c_s /= width;
      mdl->triangle[i].c_t /= height;
		}
}   

/*
   NT:
   Render a hierarchical model part:
   Call render with root part (ent->body) to render the whole model.
*/
void RenderPart(edict_t *ent, part_t *p)
{
   static z_transform = 3;
   mdl_t *mdl;
   skin_t *tga;
   float x, y, z;
   int i = 0, tilted = 0;
   if (!p)
      return;

   glPushMatrix();
   // Translate rotation axis of part:
   glTranslatef (p->origin[0], p->origin[1], p->origin[2]);
   
   // Load part model proprieties:
   if (p->model_index != NOT_FOUND)
      mdl = g_models[p->model_index]->model;
   else
      return;

   if (p->skin_index != NOT_FOUND)
      tga = g_skins[p->skin_index]->skin;

   // Set part angle to brother's angle:
   if (p->brother)
      VectorCopy(p->brother->angle, p->angle);

   switch (z_transform)
      {
      case 1:
         x = -.5f + random() * 1;
         y = -.5f + random() * 1;
         z = -.5f + random() * 1;
         p->angle[0] += x;
         p->angle[1] += y; 
         p->angle[2] += z; 
         if (p->angle[0] < p->min_angle[0] || p->angle[0] > p->max_angle[0])
            p->angle[0] -= x;   
         if (p->angle[1] < p->min_angle[1] || p->angle[1] > p->max_angle[1])
            p->angle[1] -= y;   
         if (p->angle[2] < p->min_angle[2] || p->angle[2] > p->max_angle[2])
            p->angle[2] -= z;   
         break;
      case 3:
         if (random() < .01) 
            p->delta_angle[0] = -p->delta_angle[0];
         if (random() < .01) 
            p->delta_angle[1] = -p->delta_angle[1];
         if (random() < .01) 
            p->delta_angle[2] = -p->delta_angle[2];
         p->angle[0] += p->delta_angle[0];
         p->angle[1] += p->delta_angle[1]; 
         p->angle[2] += p->delta_angle[2]; 
         if (p->angle[0] < p->min_angle[0] || p->angle[0] > p->max_angle[0])
            {
            p->angle[0] -= p->delta_angle[0];
            p->delta_angle[0] = -p->delta_angle[0];
            }
         if (p->angle[1] < p->min_angle[1] || p->angle[1] > p->max_angle[1])
            {
            p->angle[1] -= p->delta_angle[1];
            p->delta_angle[1] = -p->delta_angle[1];
            }
         if (p->angle[2] < p->min_angle[2] || p->angle[2] > p->max_angle[2])
            {
            p->angle[2] -= p->delta_angle[2];
            p->delta_angle[2] = -p->delta_angle[2];
            }
         break;
      /*
      NT:
      FRAME INTERPOLATION
      NOT NEEDED IN OUR PROJECT
      case 0:
         if (p->current_pose)
            {
            if (interpolation > INTERPOLATION)
               {
               if (p->current_pose->next)
                  {
                  p->current_pose = p->current_pose->next;
                  FrameInterpolate(p);
                  }
               else
                  {
                  p->current_pose->frame++;
                  if (p->action[p->current_pose->frame])
                     p->current_pose = p->action[p->current_pose->frame];
                  else
                     {
                     p->current_pose->frame = 0;
                     p->current_pose = p->action[p->current_pose->frame];
                     }            
                  FrameInterpolate(p);
                  }
               }
            else
               {
               VectorAdd(p->current_pose->rotation, p->angle , p->angle);
               VectorAdd(p->current_pose->delta_angle, p->angle , p->angle);
               }
            }
         break;
      */
      case 4:
      case 5:
         break;
      }
   // Rotate part model:
   glRotatef (p->angle[YAW], 0.0, 0.0f, 1.0f);
   glRotatef (p->angle[ROLL], 0.0, 1.0f, 0.0);
   glRotatef (p->angle[PITCH], 1.0, 0.0f, 0.0);

   if (p->tilt[0] || p->tilt[1] || p->tilt[2])
      {
      glPushMatrix();
      glRotatef (p->tilt[YAW], 0.0, 0.0f, 1.0f);
      glRotatef (p->tilt[PITCH], 1.0, 0.0f, 0.0);
      glRotatef (p->tilt[ROLL], 0.0, 1.0f, 0.0);
      tilted = 1;
      }
   // Translate the part model:
   if (p->current_pose)
      {
      glTranslatef (p->current_pose->translation[0], 
                    p->current_pose->translation[1], 
                    p->current_pose->translation[2]);     
      }
   // Or else translate the part model relative to brother if any:
   else if (p->brother && p->brother->current_pose)
      {
      glTranslatef (p->brother->current_pose->translation[0], 
                    p->brother->current_pose->translation[1], 
                    p->brother->current_pose->translation[2]);
      }
   if (1 /* Hard Part*/)
      {
      if (tga) 
	      {
         glBindTexture (GL_TEXTURE_2D, tga->index);
         glBegin(GL_TRIANGLES);
	      for (i = 0; i < mdl->num_triangles; i++) 
   	      {
            glTexCoord2f((mdl->tri[i].a_s)/(float)tga->width, (mdl->tri[i].a_t)/(float)tga->height);
   	      glVertex3fv (mdl->tri[i].a);
            glTexCoord2f((mdl->tri[i].b_s)/(float)tga->width, (mdl->tri[i].b_t)/(float)tga->height);
		      glVertex3fv (mdl->tri[i].b);
            glTexCoord2f((mdl->tri[i].c_s)/(float)tga->width, (mdl->tri[i].c_t)/(float)tga->height);
		      glVertex3fv (mdl->tri[i].c);
		      }
         glEnd();
         }
      else
         {
         glBegin(GL_TRIANGLES);
	      for (i = 0; i < mdl->num_triangles; i++) 
   	      {
   	      glVertex3fv (mdl->tri[i].a);
		      glVertex3fv (mdl->tri[i].b);
		      glVertex3fv (mdl->tri[i].c);
		      }
         glEnd();
         }
      }
   /* NT: 
   NOT NEEDED IN OUR PROJECT 
   // Copy transform matrix result into part matrix:
   glGetFloatv(GL_MODELVIEW_MATRIX, p->mat);
   */
   if (tilted)
      glPopMatrix();
   /* NT:
   NOT NEEDED IN OUR PROJECT
   p->mat[12] += ent->origin[0];
   p->mat[13] += ent->origin[1];
   p->mat[14] += ent->origin[2];
   // Copy part skin into model display buffer skin index:
   model_tris[softpart_index]->skin_index = p->skin_index;
   softpart_index++;
   */

   i = 0;
   while (p->son[i])
      RenderPart(ent, p->son[i++]);
   glPopMatrix();
}

/*
   NT: 
   Render interpolated/animated alias model:
*/
void RenderAliasModel(edict_t *self)
{
   float interpolation = eng.interpolation;
   vec3_t frame_x, frame_y, frame_z;
   CMD2 *mdl = self->model;
   skin_t *tga = NULL;
   make_vertex_list *cframe, *cnextframe;
   make_triangle *cindex;
   int next_frame, frame;
   int i;
   float ainterpolation;

   if (self->skin_num != NOT_FOUND)
      tga = g_skins[self->skin_num]->skin;
   else 
      return;
      
   if (!mdl)
      return;
	if (interpolation >= 1)
		{
		interpolation = 0;
		self->frame = self->next_frame;
		self->next_frame++;
		if (self->next_frame > self->end_frame)
			{
			if (self->loop_frame >= 0)
				self->next_frame = self->loop_frame;
			else
				self->next_frame = self->end_frame;
			}
		if (self->frame > self->end_frame)
			{
			if (self->loop_frame >= 0)
				{
				self->frame = self->loop_frame;
				self->next_frame = self->loop_frame + 1;
				}
			else
				{
				self->frame = self->end_frame;
				self->next_frame = self->end_frame;
				}	
			}
		}
   frame = self->frame;
   next_frame = self->next_frame;
   cframe = mdl->frame[frame].vertex;
   cnextframe = mdl->frame[next_frame].vertex;
   ainterpolation = 1 - interpolation;

   glBegin(GL_TRIANGLES);
	for (i = 0; i < mdl->getnumberTriangles(); i++) 
		{
		cindex = &mdl->triangle[i];
      // Interpolate the frames:
		frame_x[0] = cnextframe[cindex->a].x * interpolation +  cframe[cindex->a].x * ainterpolation;
		frame_y[0] = cnextframe[cindex->a].y * interpolation +  cframe[cindex->a].y * ainterpolation;
		frame_z[0] = cnextframe[cindex->a].z * interpolation +  cframe[cindex->a].z * ainterpolation;
		frame_x[1] = cnextframe[cindex->b].x * interpolation +  cframe[cindex->b].x * ainterpolation;
		frame_y[1] = cnextframe[cindex->b].y * interpolation +  cframe[cindex->b].y * ainterpolation;
		frame_z[1] = cnextframe[cindex->b].z * interpolation +  cframe[cindex->b].z * ainterpolation;
		frame_x[2] = cnextframe[cindex->c].x * interpolation +  cframe[cindex->c].x * ainterpolation;
		frame_y[2] = cnextframe[cindex->c].y * interpolation +  cframe[cindex->c].y * ainterpolation;
		frame_z[2] = cnextframe[cindex->c].z * interpolation +  cframe[cindex->c].z * ainterpolation;
		   glTexCoord2f(cindex->a_s, cindex->a_t);
		   glVertex3f (frame_x[0], frame_y[0], frame_z[0]);
		   glTexCoord2f(cindex->b_s, cindex->b_t);
		   glVertex3f (frame_x[1], frame_y[1], frame_z[1]);
		   glTexCoord2f(cindex->c_s, cindex->c_t);
		   glVertex3f (frame_x[2], frame_y[2], frame_z[2]);	   
		}
   glEnd();
}

/*
   NT:
   Project Alias model into 2D plane and render it:
*/
void RenderAliasShadow(edict_t *self)
{
   CMD2 *mdl = self->model;
   skin_t *tga = NULL;
   make_vertex_list *cframe;
   make_triangle *cindex;
   int i;

   if (self->skin_num != NOT_FOUND)
      tga = g_skins[self->skin_num]->skin;
   else 
      return;
      
   if (!mdl)
      return;
   cframe = mdl->frame[self->frame].vertex;
   glDisable(GL_TEXTURE_2D);
   glColor4f(0, 0, 0, .3f);
   glBegin(GL_TRIANGLES);
	for (i = 0; i < mdl->getnumberTriangles(); i++) 
		{
		cindex = &mdl->triangle[i];
		glVertex3f (cframe[cindex->a].x, -23, cframe[cindex->a].y + 24 + cframe[cindex->a].z);
		glVertex3f (cframe[cindex->b].x, -23, cframe[cindex->b].y + 24 + cframe[cindex->b].z);
		glVertex3f (cframe[cindex->c].x, -23, cframe[cindex->c].y + 24 + cframe[cindex->c].z);
		}
	glEnd();
   glEnable(GL_TEXTURE_2D);
}