/*
 ** THIS SOFTWARE IS SUBJECT TO COPYRIGHT PROTECTION AND IS OFFERED ONLY
 ** PURSUANT TO THE 3DFX GLIDE GENERAL PUBLIC LICENSE. THERE IS NO RIGHT
 ** TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX
 ** INTERACTIVE, INC. A COPY OF THIS LICENSE MAY BE OBTAINED FROM THE 
 ** DISTRIBUTOR OR BY CONTACTING 3DFX INTERACTIVE INC(info@3dfx.com). 
 ** THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
 ** EXPRESSED OR IMPLIED. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A
 ** FULL TEXT OF THE NON-WARRANTY PROVISIONS.  
 ** 
 ** USE, DUPLICATION OR DISCLOSURE BY THE GOVERNMENT IS SUBJECT TO
 ** RESTRICTIONS AS SET FORTH IN SUBDIVISION (C)(1)(II) OF THE RIGHTS IN
 ** TECHNICAL DATA AND COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013,
 ** AND/OR IN SIMILAR OR SUCCESSOR CLAUSES IN THE FAR, DOD OR NASA FAR
 ** SUPPLEMENT. UNPUBLISHED RIGHTS RESERVED UNDER THE COPYRIGHT LAWS OF
 ** THE UNITED STATES.  
 ** 
 ** COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED
 **
 ** $Header: /cvsroot/macglide/MacGLide/glide2x/sst1/glide/src/gsplash.c,v 1.6 2006/05/18 20:50:17 jens-olaf Exp $
 ** $Log: gsplash.c,v $
 ** Revision 1.6  2006/05/18 20:50:17  jens-olaf
 ** No subtexturing/gapfix in splash anim
 **
 ** Revision 1.5  2005/11/05 14:50:49  jens-olaf
 ** Fixed endian-issues in grLfbWriteRegion() (fixes the menu backgrunds in Carmageddon 2)
 **
 ** Revision 1.4  2005/08/17 20:16:57  jens-olaf
 ** Fixed color of "3dfx" texture
 **
 ** Revision 1.3  2005/07/14 18:54:34  jens-olaf
 ** Fixed clearing the screen after the last frame
 **
 ** Revision 1.2  2005/07/11 18:59:44  jens-olaf
 ** Fixed buffer swap interval, added clear buffer command at the end of the animation because the last frame was displayed during game load
 **
 ** Revision 1.1  2005/07/09 21:28:54  jens-olaf
 ** Added splash screen and shameless plug
 **
 ** Revision 1.1.1.1  1999/12/07 21:48:52  joseph
 ** Initial checkin into SourceForge.
 **
 * 
 * 14    12/19/97 8:09a Peter
 * fog table propogation
 * 
 * 13    7/07/97 3:05p Dow
 * Moved fouled clears
 * 
 * 12    3/18/97 9:07p Dow
 * Got rid of #$#%#$ // comments
 * 
 * 11    3/16/97 12:42a Jdt
 * Removed watcom warning
 * 
 * 10    3/13/97 10:53p Jdt
 * Rewrote with simple optimizations.  Changed prototype for
 * multi-resolution, and running as a better shameless plug.
 * 
 * 9     3/13/97 2:52a Jdt
 * Added arguments to splash.
 * 
 * 8     3/09/97 10:31a Dow
 * Added GR_DIENTRY for di glide functions
 * 
 * 7     3/01/97 6:24p Jdt
 * Made splash force yorigin.
 * 
 * 6     1/02/97 1:06p Dow
 * Fixed state bug
 * 
 * 3     11/17/96 11:16p Garymct
 * Updated grSplash code to set all of the necessary state itself rather
 * than expecting the caller to do so.
 */
 
/*
#include <3dfx.h>
#define FX_DLL_DEFINITION
#include <fxdll.h>
#include <glide.h>

#include "fxglide.h"
*/
#include "fxinline.h"

// OpenGLide specific
#include "../../../driversrc_defines.h"
#include "Glide.h"

#include <math.h>

/*-----------------------------
  Constants
  -----------------------------*/
#define MAX_NUM_VERTS 2556

#define FADEIN_END_PERCENT ( 0.3f )
#define FADEOUT_BEGIN_PERCENT ( 0.8f )

#define SPIN_FRAMES 25
#define SPIN_START  26

#define NO_TABLE ((GrTexTable_t)(~0))

/*-----------------------------
  Macros
  -----------------------------*/
#define SNAP_BIAS     ((float)(3<<18))
#define SNAP_COORD(X) ((X)+SNAP_BIAS)

/*-----------------------------
  Types
  -----------------------------*/
typedef struct {
  float x, y, z;                /* object space coordinates */
  float nx, ny, nz;             /* object space vertex normal for lighting */
  float s, t;                   /* pre-glide-ified texture coordinates */
} Vert;

typedef struct {
  int v[3];                     /* vertex indices into array of vertes for face */
  int mat_index;                /* material index */
  int aa_edge_flags;
} Face;

typedef float Vector[3];
typedef float Matrix[16];

// typedef FxU32 Palette[256];
typedef struct {
  FxU8  yRGB[16];
  FxI16 iRGB[4][3];
  FxI16 qRGB[4][3];
  FxU32 packed_data[12];
} NCCTable;
typedef union {
//  Palette  palette;
	FxU32 palette[256];
  NCCTable nccTable;
} TextureTable;

/*-----------------------------
  Globals
  -----------------------------*/
/* xScale, xOffset, yScale, yOffset */
static float viewPort[4] = { 
  480.0f, 320.0f, 
  480.0f, 240.0f 
};

typedef struct {
  GrTexInfo    info;
  FxU32        addr;
  GrTexTable_t tableType;
  TextureTable tableData;
} Texture;

static int do_phong = 0;
static int pass;
static int fog;
static int useTextures;
static Vector light = { -0.57735f, -0.57735f, -0.57735f };
static Vector transformed_verts[MAX_NUM_VERTS];
static Vector transformed_norms[MAX_NUM_VERTS];

#include "splshdat.inc"

static Texture textImage;
static Texture hiliteImage;
static Texture shadowImage;

static FxU32 nextFreeBase;

/*-----------------------------
  Privates
  -----------------------------*/
static void sourceTexture( Texture *texture ) {
  static Texture *lastTexture;

  if ( texture != lastTexture && useTextures ) {
    grTexSource( GR_TMU0,
                texture->addr,
                GR_MIPMAPLEVELMASK_BOTH,
                &texture->info );
    if ( texture->tableType != NO_TABLE ) {
      grTexDownloadTable( GR_TMU0,
                         texture->tableType,
                         &texture->tableData );
    }
    lastTexture = texture;
  }
}

static GrTexTable_t texTableType( GrTextureFormat_t format ) {
  GrTexTable_t rv = (GrTexTable_t)NO_TABLE;
  switch( format ) {
  case GR_TEXFMT_YIQ_422:
  case GR_TEXFMT_AYIQ_8422:
    rv = GR_TEXTABLE_NCC0;
    break;
  case GR_TEXFMT_P_8:
  case GR_TEXFMT_AP_88:
    rv = GR_TEXTABLE_PALETTE;
    break;
  }
  return rv;
}

static void downloadTexture( Texture *texture, Gu3dfInfo *info ) {
  texture->info.data        = info->data;
  texture->info.smallLod    = info->header.small_lod;
  texture->info.largeLod    = info->header.large_lod;
  texture->info.aspectRatio = info->header.aspect_ratio;
  texture->info.format      = info->header.format;

  texture->addr = nextFreeBase;
  nextFreeBase += grTexTextureMemRequired( GR_MIPMAPLEVELMASK_BOTH,
                                          &texture->info );
  grTexDownloadMipMap( GR_TMU0,
                      texture->addr,
                      GR_MIPMAPLEVELMASK_BOTH,
                      &texture->info );

  texture->tableType = texTableType( info->header.format );
  switch( texture->tableType ) {
  case GR_TEXTABLE_NCC0:
  case GR_TEXTABLE_NCC1:
  case GR_TEXTABLE_PALETTE:
    texture->tableData = *(TextureTable*)(&info->table);
    break;
  default:
    break;
  }

  return;
}

#ifdef OPENGLIDE_HOST_MAC
void ConvertGu3dfInfoLSB2MSB(Gu3dfInfo* gu3dinfo)
{
	// Swap endianess fro LSB to MSB:
	// Less optimal than supplying the correct data,
	// but on the other hand it works with the original splash.inc
	if (gu3dinfo->header.width > 0xffff)
	{
		// Convert the first time only...
		swaplong(&gu3dinfo->header.width);
		swaplong(&gu3dinfo->header.height);
		swaplong(&gu3dinfo->header.small_lod);
		swaplong(&gu3dinfo->header.large_lod);
		swaplong(&gu3dinfo->header.aspect_ratio);
		swaplong(&gu3dinfo->header.format);
		// palette tables aren't swapped
		// simply because they're not used
		/*
		for(FxU32 i = 0; i < 256; i++)
		{
			swaplong(&gu3dinfo->table.palette.data[i]);
		}
		*/
		GuNccTable* ncc = &gu3dinfo->table.nccTable; 
		for ( int i = 0; i < 4; i++ )
		{
			swapshort(&ncc->iRGB[i][0]);
			swapshort(&ncc->iRGB[i][1]);
			swapshort(&ncc->iRGB[i][2]);
		}
		for ( int i = 0; i < 4; i++ )
		{
			swapshort(&ncc->qRGB[i][0]);
			swapshort(&ncc->qRGB[i][1]);
			swapshort(&ncc->qRGB[i][2]);
		}
		swaplong(&gu3dinfo->mem_required);
	}
}
#else
void ConvertGu3dfInfoLSB2MSB(Gu3dfInfo* gu3dinfo)
{
}
#endif

static void createTextures( void ) {
  Gu3dfInfo *info;

  /*
   * Download the texture for the "3D" part of the model.
   */
  info       = ( Gu3dfInfo * )text_3dfinfo_raw;
  info->data = ( void * )text_3dfinfo_image;
  ConvertGu3dfInfoLSB2MSB(info);
  downloadTexture( &textImage, info );

  /*
   * Download the texture for the specular highlight.
   */
  info       = ( Gu3dfInfo * )hilite_3dfinfo_raw;
  info->data = ( void * )hilite_3dfinfo_image;
  ConvertGu3dfInfoLSB2MSB(info);
  downloadTexture( &hiliteImage, info );

  /*
   * Download the texture for the shadow.
   */
  info       = ( Gu3dfInfo * )shadow_3dfinfo_raw;
  info->data = ( void * )shadow_3dfinfo_image;
  ConvertGu3dfInfoLSB2MSB(info);
  downloadTexture( &shadowImage, info );
  return;
}

static void vecMatMult( float *dstVec, float *srcVec, float *matrix ) {
  dstVec[0] = 
    srcVec[0] * matrix[0] +
      srcVec[1] * matrix[4] +
        srcVec[2] * matrix[8] +
          matrix[12];
    
  dstVec[1] = 
    srcVec[0] * matrix[1] +
      srcVec[1] * matrix[5] +
        srcVec[2] * matrix[9] +
          matrix[13];
    
  dstVec[2] = 
    srcVec[0] * matrix[2] +
      srcVec[1] * matrix[6] +
        srcVec[2] * matrix[10] +
          matrix[14];
}

static void normMatMult( float *dstVec, float *srcVec, float *matrix ) {
  dstVec[0] = 
    srcVec[0] * matrix[0] +
      srcVec[1] * matrix[4] +
        srcVec[2] * matrix[8];
    
  dstVec[1] = 
    srcVec[0] * matrix[1] +
      srcVec[1] * matrix[5] +
        srcVec[2] * matrix[9];
    
  dstVec[2] = 
    srcVec[0] * matrix[2] +
      srcVec[1] * matrix[6] +
        srcVec[2] * matrix[10];
}

static void xfAndProj( int frame, int obj ) {
  int    vertex;
  float *matrix;

  matrix = &mat[frame][obj][0];

  for( vertex = 0; vertex < num_verts[obj]; vertex++ ) {
    float *srcVec;
    float *dstVec;
    float oow;

    /* transform point */
    srcVec = (float*)&(vert[obj][vertex].x);
    dstVec = (float*)transformed_verts[vertex];
    vecMatMult( dstVec, srcVec, matrix );

    /* project point */
    oow = 1.0f / dstVec[2];
    dstVec[0] = dstVec[0] * oow * viewPort[0] + viewPort[1] + SNAP_BIAS;
    dstVec[1] = dstVec[1] * oow * viewPort[2] + viewPort[3] + SNAP_BIAS;

    /* transform normal */
    srcVec = (float*)&(vert[obj][vertex].nx);
    dstVec = (float*)transformed_norms[vertex];
    normMatMult( dstVec, srcVec, matrix );
  }
}

static void setupMaterial( int material_index ) {  
  switch( material_index ) {
  case 0:                       /* 3d */
    if( pass == 1 ) {
      sourceTexture( &textImage );
      grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                     GR_COMBINE_FACTOR_LOCAL,
                     GR_COMBINE_LOCAL_ITERATED,
                     GR_COMBINE_OTHER_TEXTURE,
                     FXFALSE );
      do_phong = 1;
    } else if ( pass == 0xbeef ) {
      /* Pantone 320C - 3D Green */
      grConstantColorValue( 0x00989100 );
      grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                     GR_COMBINE_FACTOR_LOCAL,
                     GR_COMBINE_LOCAL_CONSTANT,
                     GR_COMBINE_OTHER_ITERATED,
                     FXFALSE );
    } else {
      sourceTexture( &hiliteImage );
      grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                     GR_COMBINE_FACTOR_ONE,
                     GR_COMBINE_LOCAL_NONE,
                     GR_COMBINE_OTHER_TEXTURE,
                     FXFALSE );
    } 
    break;
  case 1:                       /* fx */
    if ( pass == 0xbeef ) {
      /* Black - fx */
      grConstantColorValue( 0x00 );
      grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                     GR_COMBINE_FACTOR_LOCAL,
                     GR_COMBINE_LOCAL_CONSTANT,
                     GR_COMBINE_OTHER_ITERATED,
                     FXFALSE );
      do_phong = 1;
    } else {
      sourceTexture( &hiliteImage );
      grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                     GR_COMBINE_FACTOR_ONE,
                     GR_COMBINE_LOCAL_NONE,
                     GR_COMBINE_OTHER_TEXTURE,
                     FXFALSE );
      do_phong = 1;
    }
    break;
  case 2:
  case 3:
  case 4:
    grColorCombine( GR_COMBINE_FUNCTION_LOCAL,
                   GR_COMBINE_FACTOR_NONE,
                   GR_COMBINE_LOCAL_ITERATED,
                   GR_COMBINE_OTHER_NONE,
                   FXFALSE );
    do_phong = 0;
    break;
  }
  return;
}

static void calculateIntensity( int material_index, 
				Vector intensity_factor, 
				int frame ) {
  switch( material_index ) {
  case 0:
    /* 3d */
    intensity_factor[0] = 1.0f;
    intensity_factor[1] = 1.0f;
    intensity_factor[2] = 1.0f;
    break;
  case 1:
    /* fx */
    intensity_factor[0] = 0.125f;
    intensity_factor[1] = 0.125f;
    intensity_factor[2] = 0.125f;
    break;
  case 2:
    /* cyan */
    intensity_factor[0] = ( 10.0f / 255.0f );
    intensity_factor[1] = ( 75.0f / 255.0f );
    intensity_factor[2] = ( 120.0f / 255.0f );
    break;
  case 3:
    /* white */
    intensity_factor[0] = 1.0f;
    intensity_factor[1] = 1.0f;
    intensity_factor[2] = 1.0f;
    break;
  case 4:
    /* yellow */
    intensity_factor[0] = ( 248.0f / 255.0f );
    intensity_factor[1] = ( 204.0f / 255.0f );
    intensity_factor[2] = 0.0f;
    break;
  }
}

static void drawFaces( int frame, int objnum ) {
  int facenum;
  int material_index;
  float intensity_factor[3];
  GrVertex gvert[3];
  static int prev_mat_index = 0xffff;
  int i;
  FxBool aa_a, aa_b, aa_c;
    
  for( facenum = 0; facenum < num_faces[objnum]; facenum++ )  {
    material_index = face[objnum][facenum].mat_index;
    if( material_index != prev_mat_index )  {
      setupMaterial( material_index );
      calculateIntensity( material_index,
                         intensity_factor, frame );
      prev_mat_index = material_index;
    }
        
    if( ( material_index != 0 ) && ( pass == 2 ) )
      continue;
          
    aa_a = aa_b = aa_c = FXFALSE;
    if( face[objnum][facenum].aa_edge_flags & 4 )
      aa_a = FXTRUE;
    if( face[objnum][facenum].aa_edge_flags & 2 )
      aa_b = FXTRUE;
    if( face[objnum][facenum].aa_edge_flags & 1 )
      aa_c = FXTRUE;
        
    for( i = 0; i < 3; i++ ) {
      float *transformed_vert, *transformed_norm;
      Vert *v;
      int vertnum;
      float factor;
            
      vertnum = face[objnum][facenum].v[i];
      transformed_vert = transformed_verts[vertnum];
      transformed_norm = transformed_norms[vertnum];
      v = &vert[objnum][vertnum];
            
      gvert[i].x = transformed_vert[0];
      gvert[i].y = transformed_vert[1];
      gvert[i].oow = 1.0f / transformed_vert[2];
      gvert[i].tmuvtx[0].oow = gvert[i].oow;
      gvert[i].tmuvtx[0].sow = v->s * gvert[i].oow;
      gvert[i].tmuvtx[0].tow = v->t * gvert[i].oow;
            
      factor = 
        ( ( light[0] * transformed_norm[0] + 
           light[1] * transformed_norm[1] + 
           light[2] * transformed_norm[2] ) + 1.0f ) * 127.5f;

      gvert[i].r = factor * intensity_factor[0];
      gvert[i].g = factor * intensity_factor[1];
      gvert[i].b = factor * intensity_factor[2];
      gvert[i].a = 255.0f;
    }
        
    if( pass == 2 ) {
      for( i = 0; i < 3; i++ ) {
        float *transformed_norm;
                
        transformed_norm = 
          transformed_norms[face[objnum][facenum].v[i]];
                
        gvert[i].tmuvtx[0].sow = gvert[i].oow * 
          ( 128.0f + transformed_norm[0] * 128.0f );
        gvert[i].tmuvtx[0].tow = gvert[i].oow * 
          ( 128.0f + transformed_norm[1] * 128.0f );
                
        gvert[i].r = intensity_factor[0] * 255.0f;
        gvert[i].g = intensity_factor[1] * 255.0f;
        gvert[i].b = intensity_factor[2] * 255.0f;
      }                 
            
      grDrawTriangle( &gvert[0], &gvert[1], &gvert[2] );
            
      continue;
    }
        
    grAlphaBlendFunction( GR_BLEND_SRC_ALPHA, GR_BLEND_ONE_MINUS_SRC_ALPHA, 
                         GR_BLEND_ONE, GR_BLEND_ZERO );
    /* Do this if 3D. */
    if( material_index == 0 ) {
      /*
       * Draw the textured 3D without specular. 
       */
      sourceTexture( &textImage );
#if 0
      grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                     GR_COMBINE_FACTOR_LOCAL,
                     GR_COMBINE_LOCAL_ITERATED,
                     GR_COMBINE_OTHER_TEXTURE,
                     FXFALSE );
#endif
      grAADrawTriangle( &gvert[0], &gvert[1], &gvert[2], 
                       aa_a, aa_b, aa_c );
    } else if( material_index != 1 ) {
      /*
       * Just go ahead and draw for things that don't
       * have specular highlights.
       */
      grAADrawTriangle( &gvert[0], &gvert[1], &gvert[2],
                       aa_a, aa_b, aa_c );
    }
        
    if( do_phong && ( material_index != 0 ) ) {
      /* 
       * figure out texture coords in [0..255.0f] based on the normal
       * the specular highlight.
       */
      for( i = 0; i < 3; i++ ) {
        float *transformed_norm;
                
        transformed_norm = 
          transformed_norms[face[objnum][facenum].v[i]];
                
        gvert[i].tmuvtx[0].sow = gvert[i].oow * 
          ( 128.0f + transformed_norm[0] * 128.0f );
        gvert[i].tmuvtx[0].tow = gvert[i].oow * 
          ( 128.0f + transformed_norm[1] * 128.0f );
                
        gvert[i].r = intensity_factor[0] * 255.0f;
        gvert[i].g = intensity_factor[1] * 255.0f;
        gvert[i].b = intensity_factor[2] * 255.0f;
      }                 
      grAADrawTriangle( &gvert[0], &gvert[1], &gvert[2], 
                       aa_a, aa_b, aa_c );
    }
  }
}

static void intersectLineWithZPlane( Vector result, Vector p1, 
                                    Vector p2, float z ) {
  float t;
  
  t = ( z - p1[2] ) / ( p2[2] - p1[2] );
  result[0] = p1[0] + ( p2[0] - p1[0] ) * t;
  result[1] = p1[1] + ( p2[1] - p1[1] ) * t;
  result[2] = z;
}

static float vectorMag( float *v ) {
  return ( float )sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
}

static void drawShadow( int frame, int shadow_object_index, 
                       int receiver_object_index, Vector light_position ) {
  float *shadow_object_matrix;
  float *receiver_object_matrix;
  Vector view_verts[4];
  Vector projected_view_verts[4];
  int i;
  
  Vector local_verts[4] = {
    { -280.0f, 0.0f, -160.0f },
    { -280.0f, 0.0f,  150.0f },
    {  280.0f, 0.0f,  150.0f },
    {  280.0f, 0.0f, -160.0f } 
  };
  
  float texcoords[4][2] = {
    {  10.5f, 127.5f },
    {  10.5f,   0.5f },
    { 255.0f,   0.5f },
    { 255.0f, 127.5f }
  };
  
  GrVertex gvert[4];
  GrVertex projected_gvert[4];
  GrVertex light_gvert;
  
  /*
   * The point relative to the back part of the shield that 
   * the shadow is going to be projected onto.
   */
  Vector shadow_light;
  
  shadow_light[0] = light_position[0];
  shadow_light[1] = light_position[1];
  shadow_light[2] = light_position[2];
  
  shadow_object_matrix   = &mat[frame][shadow_object_index][0];
  receiver_object_matrix = &mat[frame][receiver_object_index][0];
  
  /*
   * Offset the light by the Z position of 
   * the backplane that we are projected
   * onto.
   */
  shadow_light[2] += receiver_object_matrix[14];
  
  for( i = 0; i < 4; i++ ) {
    vecMatMult( view_verts[i], local_verts[i], shadow_object_matrix );
        
    /*
     * project . . . 
     */
    gvert[i].oow = 1.0f / view_verts[i][2];
    gvert[i].x = 
      view_verts[i][0] * gvert[i].oow * viewPort[0] + 
        viewPort[1] + SNAP_BIAS;
    gvert[i].y = 
      view_verts[i][1] * gvert[i].oow * viewPort[2] + 
        viewPort[3] + SNAP_BIAS;
        
    /*
     * Set up texture coordinates.
     */
    gvert[i].tmuvtx[0].sow = texcoords[i][0] * gvert[i].oow;
    gvert[i].tmuvtx[0].tow = texcoords[i][1] * gvert[i].oow;
  }
  
  
  /*
   * Intersect each line formed by the light source and a
   * particular corner of the shadow object with the 
   * plane which the texture is to be projected onto.
   */
  for( i = 0; i < 4; i++ ) {
    Vector tmpvect;
    float q;
        
    intersectLineWithZPlane( projected_view_verts[i], 
                            shadow_light, 
                            view_verts[i], 
                            receiver_object_matrix[14] - 26.0f );
    projected_gvert[i].oow = 1.0f / projected_view_verts[i][2];
    projected_gvert[i].x = 
      projected_view_verts[i][0] * projected_gvert[i].oow * viewPort[0] +
        viewPort[1] + SNAP_BIAS;
    projected_gvert[i].y = 
      projected_view_verts[i][1] * projected_gvert[i].oow * viewPort[2] +
        viewPort[3] + SNAP_BIAS;
        
    tmpvect[0] = projected_view_verts[i][0] - shadow_light[0];
    tmpvect[1] = projected_view_verts[i][1] - shadow_light[1];
    tmpvect[2] = projected_view_verts[i][2] - shadow_light[2];
    q = vectorMag( tmpvect );
        
    /*
     * Set up texture coordinates.
     */
    projected_gvert[i].tmuvtx[0].oow = projected_gvert[i].oow * q;
    projected_gvert[i].tmuvtx[0].sow = 
      texcoords[i][0] * projected_gvert[i].oow;
    projected_gvert[i].tmuvtx[0].tow = 
      texcoords[i][1] * projected_gvert[i].oow;
  }
  
  light_gvert.oow = 1.0f / shadow_light[2];
  light_gvert.x = shadow_light[0] * light_gvert.oow * viewPort[0] + 
    viewPort[1] + SNAP_BIAS;
  light_gvert.y = shadow_light[1] * light_gvert.oow * viewPort[2] + 
    viewPort[3] + SNAP_BIAS;
  
  /*
   * Draw a segment between the light and the point which hits
   * the surface that the light is being projected onto.
   */
  grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,
                 GR_COMBINE_FACTOR_ONE,
                 GR_COMBINE_LOCAL_NONE,
                 GR_COMBINE_OTHER_TEXTURE,
                 FXFALSE );
  sourceTexture( &shadowImage );
  
  grAlphaBlendFunction( GR_BLEND_DST_COLOR, GR_BLEND_ZERO,
                       GR_BLEND_ONE, GR_BLEND_ZERO );
  
  
  grDrawTriangle( &projected_gvert[0], 
                 &projected_gvert[1], 
                 &projected_gvert[2] );
  grDrawTriangle( &projected_gvert[0], 
                 &projected_gvert[2], 
                 &projected_gvert[3] );
  grDrawTriangle( &projected_gvert[0], 
                 &projected_gvert[2], 
                 &projected_gvert[1] );
  grDrawTriangle( &projected_gvert[0], 
                 &projected_gvert[3], 
                 &projected_gvert[2] );
  
  grAlphaBlendFunction( GR_BLEND_ONE, GR_BLEND_ZERO,
                       GR_BLEND_ONE, GR_BLEND_ZERO );
  
}


/*-------------------------------------------------------------------
  Function: grSplash
  Date: 3/13
  Implementor(s): GaryMcT
  Library: Glide
  Description:
  Render the opening splash screen animation, or render
  a single frame of the splash screen.
  Arguments:
  x, y - upper left hand coord of window
  w, h - width and height of window to render
  _frame - frame number to render ( ~0 to render all frames )
  Return:
  none
  -------------------------------------------------------------------*/
GR_DIENTRY(grSplash,void,(float x,float y,float w,float h,FxU32 _frame))
{
	// OpenGLide addition
	#ifdef OGL_DONE
    GlideMsg( "grSplash( %-4.2f, %-4.2f, %-4.2f, %-4.2f, %lu )\n",
        x, y, w, h, _frame );
	#endif
	
  GrState oldState;
  int frame;
  Vector lights[] = {
    { 5.0f, 300.0f, -1500.0f },
    { 5.0f, 150.0f, -1000.0f },
    { -30.0f, 150.0f, -1000.0f },
    { -30.0f, 100.0f, -1000.0f },
    { 30.0f, 70.0f, -1000.0f },
  };
  GrFog_t fogTable[kInternalFogTableEntryCount];
  int fadeInFrames, fadeOutFrames;

  GR_BEGIN_NOFIFOCHECK( "grSplash", 85 );
  GDBG_INFO_MORE((gc->myLevel,"(%f,%f,%f,%f,%d)\n",
                  x, y, w, h, _frame ));

  /* Check Screen Dimensions and Check Avail of Depth Buffer */
  // The emulation offers virtually unlimited buffers, so we don't have to check
  /*
  if ( (x > gc->state.screen_width) || 
      ((x+w) > gc->state.screen_width) ||
      ( y > gc->state.screen_height ) ||
      ((y+h) > gc->state.screen_height ) ) 
    return;
  if ( gc->state.screen_height == 640 ) {
    if ( gc->fbuf_size == 1 ) {
      return;
    }
  } else if ( gc->state.screen_width == 800 ) {
    if ( ( gc->fbuf_size == 1 ) ||
        ( gc->fbuf_size == 2 ) ) {
      return;
    }
  }
  */

  // subtexturing doesn't work with the splash animation
  // so we're going to turn it off temporarily
  const unsigned long generatesubtextures = InternalConfig.GenerateSubTextures; InternalConfig.GenerateSubTextures = 0;
  // Gapfix decreases the framerate but doesn't improve the rendering quality of the animation
  const OpenGLideGapFixFlags gapfix = InternalConfig.GapFix; InternalConfig.GapFix = OpenGLideGapFixFlag_Disabled;
  
  // if ( !(gc->state.fbi_config.fbzMode & SST_YORIGIN)
  if (Glide.State.OriginInformation == GR_ORIGIN_LOWER_LEFT) // @todo: correct?
  {
    y = (( Glide.WindowHeight - 1.0f ) - (h-1.0f) ) - y;
  }
        
  viewPort[0] = w *  ( 480.0f / 640.0f );
  viewPort[1] = x + ( w / 2.0f );
  viewPort[2] = h;
  viewPort[3] = y + ( h / 2.0f );

  grGlideGetState(&oldState);

  grSstOrigin( GR_ORIGIN_LOWER_LEFT );

  if ( _frame == 0 ) {
    createTextures();
    useTextures = 1;
  } else {
    useTextures = 0;
  }

  grAlphaTestFunction( GR_CMP_ALWAYS );
  grChromakeyMode( GR_CHROMAKEY_DISABLE );
  grConstantColorValue( 0xffffffff );
  grDepthBufferMode( GR_DEPTHBUFFER_WBUFFER );
  grDepthMask( FXTRUE );
  grAlphaCombine( GR_COMBINE_FUNCTION_LOCAL,
                 GR_COMBINE_FACTOR_NONE,
                 GR_COMBINE_LOCAL_ITERATED,
                 GR_COMBINE_OTHER_NONE,
                 FXFALSE );
  grAlphaBlendFunction( GR_BLEND_ONE, GR_BLEND_ZERO,
                       GR_BLEND_ONE, GR_BLEND_ZERO );
  if ( useTextures )
    grTexCombine( GR_TMU0, 
                 GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE,
                 GR_COMBINE_FUNCTION_NONE, GR_COMBINE_FACTOR_NONE,
                 FXFALSE, FXFALSE );
  else
    grTexCombine( GR_TMU0,
                 GR_COMBINE_FUNCTION_ZERO, GR_COMBINE_FACTOR_NONE,
                 GR_COMBINE_FUNCTION_NONE, GR_COMBINE_FACTOR_NONE,
                 FXTRUE, FXFALSE );
  grTexMipMapMode( GR_TMU0,
                  GR_MIPMAP_NEAREST,
                  FXFALSE );
  grTexFilterMode( GR_TMU0,
                  GR_TEXTUREFILTER_BILINEAR,
                  GR_TEXTUREFILTER_BILINEAR );
  grDepthBufferFunction( GR_CMP_LEQUAL );
  grCullMode( GR_CULL_NEGATIVE );
  //    grClipWindow( (int)x, (int)y, (int)(x+w), (int)(y+h) );
  grFogColorValue( 0x0 );
  
  fadeInFrames  = (int)(((float)total_num_frames) * FADEIN_END_PERCENT);
  fadeOutFrames = (int)(((float)total_num_frames) * 
                        (1.0f - FADEOUT_BEGIN_PERCENT));

  if ( _frame == 0 ) {          /* Render Whole Animation */
    for( frame = 1; frame < total_num_frames; frame++ ) {
      int i;
            
      /* Set Fog Value For This Frame */
      if( frame < fadeInFrames )  {
        unsigned char fval = 
          ((unsigned char)255) - 
            ((unsigned char)( 255.0f * 
                             (float)(frame+1) / 
                             (float)fadeInFrames ));
        for( i = 0; i < kInternalFogTableEntryCount; i++ )
          fogTable[i] = fval;
        grFogMode( GR_FOG_WITH_TABLE );
        grFogTable( fogTable );
        fog = 1;
      } else if( frame > total_num_frames-fadeOutFrames ) {
        unsigned char fval = 
          ((unsigned char)255) - 
            (unsigned char)(255.0f*
                            ((float)(total_num_frames-frame))/
                            ((float)fadeOutFrames));
        for( i = 0; i < kInternalFogTableEntryCount; i++ ) 
          fogTable[i] = fval;
        grFogMode( GR_FOG_WITH_TABLE );
        grFogTable( fogTable );
        fog = 1;
      } else  {
        grFogMode( GR_FOG_DISABLE );
        fog = 0;
      }
      grBufferClear( 0x00000000, 0, GR_WDEPTHVALUE_FARTHEST );
        
      pass = 1;

      /*
       * Avoid Z-aliasing between the shield and the 3dfx by
       * not writing Z for the shield.
       */

      grDepthMask( FXFALSE );
      /* cyan part of shield */
      xfAndProj( frame, 2 );
      drawFaces( frame, 2 );
      
      /* yellow and white part of shield. */
      xfAndProj( frame, 0 );
      drawFaces( frame, 0 );

      /*
       * Reanable writes to the depth-buffer.
       */
      grDepthMask( FXTRUE );

      /*
       * Draw the shadow projected from the 3Dfx logo onto
       * the rest of the powershield.
       */
      grDepthBufferFunction( GR_CMP_ALWAYS );
      grFogMode( GR_FOG_DISABLE );
      drawShadow( frame, 1, 0, lights[0] );
      if ( fog ) grFogMode( GR_FOG_WITH_TABLE ); /* hack around mp fog */
      grDepthBufferFunction( GR_CMP_LEQUAL );

      /* 3Dfx logo */
      xfAndProj( frame, 1 );
      drawFaces( frame, 1 );
      grAlphaBlendFunction( GR_BLEND_ONE, GR_BLEND_ONE,
                           GR_BLEND_ONE, GR_BLEND_ZERO );

      pass = 2;
      drawFaces( frame, 1 );
      pass = 1;
      grAlphaBlendFunction( GR_BLEND_ONE, GR_BLEND_ZERO,
                           GR_BLEND_ONE, GR_BLEND_ZERO );
      grBufferSwap( 2 );
    }
  } else {                      /* Render One Frame */
    frame = ((_frame>>1) % SPIN_FRAMES)+SPIN_START;
        
    grColorMask( FXFALSE, FXFALSE );
    grBufferClear( 0x00000000, 0, GR_WDEPTHVALUE_FARTHEST );
    grColorMask( FXTRUE, FXFALSE );
        
    pass = 0xbeef;
        
    /* 3Dfx logo */
    xfAndProj( frame, 1 );
    drawFaces( frame, 1 );
  }
    
    
  // Added by Jenz: Clear the buffer after the last frame to avoid
  // the last image be displayed while the game is loading
  // (May be a problem of MacGLide since the 3dfx hardware 
  if (_frame == 0)
  {
	  grColorMask(FXTRUE, FXTRUE);
	  grBufferClear(0x00000000, 0, GR_WDEPTHVALUE_FARTHEST);
	  grColorMask(FXTRUE, FXFALSE);
    grBufferSwap( 2 );

	}
  /*
   * Clean up after yourself!
   */
  grGlideSetState(&oldState);

  // Reenable temporarily disabled settings
  InternalConfig.GenerateSubTextures = generatesubtextures;
  InternalConfig.GapFix = gapfix;
}


