/****************************************************************************\
*                                                                            *
*  ENGINE3.CPP - Source Code Module                                          *
*                                                                            *
*  (c)Copyright 2001 Indotek, LLC. All Rights Reserved.                      *
*                                                                            *
\****************************************************************************/

#include "internal.h"

int _r3d_LensFlare[5];
BOOL R3D_STDCALL
r3d_LensFlareCreate(int hImage1, int hImage2, int hImage3, int hImage4, int hImage5)
{
   // Determine if we have an appropriate 3D alphapixel format
   _r3d_LensFlare[0] = hImage1;
   _r3d_LensFlare[1] = hImage2;
   _r3d_LensFlare[2] = hImage3;
   _r3d_LensFlare[3] = hImage4;
   _r3d_LensFlare[4] = hImage5;
   return TRUE;
}

BOOL R3D_STDCALL
r3d_LensFlareBlit(float LightX, float LightY, float LightZ)
{
   float MatrixView[12];
   MatrixView[0] = _r3d_View.m._11;
   MatrixView[1] = _r3d_View.m._12;
   MatrixView[2] = _r3d_View.m._13;
   MatrixView[3] = _r3d_View.m._21;
   MatrixView[4] = _r3d_View.m._22;
   MatrixView[5] = _r3d_View.m._23;
   MatrixView[6] = _r3d_View.m._31;
   MatrixView[7] = _r3d_View.m._32;
   MatrixView[8] = _r3d_View.m._33;
   MatrixView[9] = _r3d_View.x;
   MatrixView[10] = _r3d_View.y;
   MatrixView[11] = _r3d_View.z;

   D3DVECTOR vLightCenter = D3DVECTOR(MatrixView[2], MatrixView[5], MatrixView[8]);

   D3DVECTOR vLightPt = D3DVECTOR(LightX, LightY, LightZ);

   FLOAT fDotProduct = DotProduct(vLightCenter, vLightPt);
   D3DVECTOR vArbitrary;

   float fSize = 4.0;
   vLightCenter = vLightCenter * fSize;
   vLightPt = vLightPt * fSize;
   BOOL bOffScreen;
   int sx, sy, handle;
   int Width, Height, HalfWidth, HalfHeight;

   // Only render flares if we are within the "flare cone of view"
   if (fDotProduct > 0.707106781186547524400844362104849F)
   {
      float fMatrix[12];
      memcpy(fMatrix, MatrixView, 9 * 4);
      fMatrix[9] = fMatrix[10] = fMatrix[11] = 0.0F;
      r3d_SetViewMatrix(fMatrix);
      r3d_2DBegin(NULL, NULL, NULL);

      // Calculate the opposite sun point
      D3DVECTOR vLightAxisOppPt = vLightCenter - ((vLightPt / 2) / fDotProduct);
      vLightAxisOppPt = Normalize(vLightAxisOppPt);
      vLightAxisOppPt = vLightAxisOppPt * fSize;

      vLightAxisOppPt = vLightAxisOppPt * 0.85F;
      fMatrix[9] = vLightAxisOppPt.x;
      fMatrix[10] = vLightAxisOppPt.y;
      fMatrix[11] = vLightAxisOppPt.z;
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[0];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }
      //r3d_ZBlit3DModel1(hModelFlare1,fMatrix);

      vArbitrary = vLightCenter - (((vLightPt) / 3) / fDotProduct);
      vArbitrary = Normalize(vArbitrary);
      vArbitrary = vArbitrary * fSize * 0.90F;
      fMatrix[9] = vArbitrary.x;
      fMatrix[10] = vArbitrary.y;
      fMatrix[11] = vArbitrary.z;
      //r3d_ZBlit3DModel1(hModelFlare1,fMatrix);
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[1];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }

      vArbitrary = vLightCenter - (((vLightPt) / 4) / fDotProduct);
      vArbitrary = Normalize(vArbitrary);
      vArbitrary = vArbitrary * fSize * 0.95F;
      fMatrix[9] = vArbitrary.x;
      fMatrix[10] = vArbitrary.y;
      fMatrix[11] = vArbitrary.z;
      //r3d_ZBlit3DModel1(hModelFlare2,fMatrix);
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[2];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }
      vArbitrary = vLightCenter - (((vLightPt) / 5) / fDotProduct);
      vArbitrary = Normalize(vArbitrary);
      vArbitrary = vArbitrary * fSize * 1.0F;
      fMatrix[9] = vArbitrary.x;
      fMatrix[10] = vArbitrary.y;
      fMatrix[11] = vArbitrary.z;
      //r3d_ZBlit3DModel1(hModelFlare1,fMatrix);
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[3];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }

      vLightCenter = (vLightAxisOppPt + vLightPt) * 0.5F;
      vLightCenter = Normalize(vLightCenter);
      vLightCenter = vLightCenter * fSize * 1.05F;
      fMatrix[9] = vLightCenter.x;
      fMatrix[10] = vLightCenter.y;
      fMatrix[11] = vLightCenter.z;
      //r3d_ZBlit3DModel1(hModelFlare1,fMatrix);
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[4];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }

      vArbitrary = vLightCenter + (((vLightPt) / 5) / fDotProduct);
      vArbitrary = Normalize(vArbitrary);
      vArbitrary = vArbitrary * fSize * 1.10F;
      fMatrix[9] = vArbitrary.x;
      fMatrix[10] = vArbitrary.y;
      fMatrix[11] = vArbitrary.z;
      //r3d_ZBlit3DModel1(hModelFlare4,fMatrix);
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[3];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }

      vArbitrary = vLightCenter + (((vLightPt)) / fDotProduct);
      vArbitrary = Normalize(vArbitrary);
      vArbitrary = vArbitrary * fSize * 1.15F;
      fMatrix[9] = vArbitrary.x;
      fMatrix[10] = vArbitrary.y;
      fMatrix[11] = vArbitrary.z;
      //r3d_ZBlit3DModel1(hModelFlare0,fMatrix);
      r3d_3DVertexToScreenVertex(0, 0, 0, fMatrix, &sx, &sy, &bOffScreen);
      if (bOffScreen == FALSE)
      {
         handle = _r3d_LensFlare[2];
         Width = (int)_r3d_Surface[handle]->dwWidth;
         Height = (int)_r3d_Surface[handle]->dwHeight;
         HalfWidth = Width / 2;
         HalfHeight = Height / 2;
         r3d_2DBlit(handle, 0, Width, 0, Height, sx - HalfWidth, sx + HalfWidth, sy - HalfHeight, sy + HalfHeight, TRUE);
      }

      // Reset Original View Matrix
      r3d_SetViewMatrix(MatrixView);
      r3d_2DEnd();
   }

   return TRUE;
}

BOOL R3D_STDCALL
_r3d_MakeMaterial(_R3D_MATERIAL *pMaterial, float r, float g, float b, float a, float pow, float sr, float sg, float sb, float er, float eg, float eb, BOOL bLoadTexture, char *TextureFilenameBMP, BOOL bLoadAlpha, char *AlphaFilenameBMP, BOOL bMipMap)
{
   // First we load the texture
   BOOL bFoundDuplicate = FALSE;
   int LoopVar;
   DDSURFACEDESC2 ddsd;
   int x, y;
   int Index = 0, AlphaIndex = 0;
   unsigned char Alpha, Red, Green, Blue;
   long ucr[5], ucg[5], ucb[5], uca[5];
   int hTempBitmap;
   unsigned long *pTextureRGBA;
   unsigned long dwWidthHeight;
   unsigned char UCcolorA, UCcolorR, UCcolorG, UCcolorB;
   BOOL bColorKeyedTexture;
   DDCOLORKEY ddColorKey;
   unsigned char *SurfacePointer;
   int SurfacePitch, TextureFormat;

   // We set this here...
   pMaterial->TextureBMP[0] = '\0';
   pMaterial->bTexture = FALSE;
   pMaterial->AlphaBMP[0] = '\0';
   pMaterial->bAlpha = FALSE;
   pMaterial->bMipMap = FALSE;
   pMaterial->lpSystemTextureSurface = NULL;
   pMaterial->lpSystemTexture = NULL;
   pMaterial->bDuplicate = FALSE;
   pMaterial->DuplicateIndex = 0;
   pMaterial->lpMaterial = NULL;
   pMaterial->bColorKeyedTexture = FALSE;

   // DirectX 6 Method
   BOOL bFalseAlpha = FALSE;
   float aFalse;
   pMaterial->bStoreAlpha = FALSE;
   if (bLoadTexture == TRUE)
   {
      if (bLoadAlpha == TRUE)
      {
         bFalseAlpha = FALSE;
         pMaterial->bStoreAlpha = TRUE;
         a = 1.0F;
      }
      else if (a < 1.0F)
      {
         bFalseAlpha = TRUE;
         pMaterial->bStoreAlpha = TRUE;
         aFalse = a;
         a = 1.0F;
      }
   }
   else if (a < 1.0F)
   {
      bFalseAlpha = FALSE;
      pMaterial->bStoreAlpha = TRUE;
   }

   ZeroMemory(&pMaterial->Material, sizeof(pMaterial->Material));
   pMaterial->Material.dwSize = sizeof(pMaterial->Material);
   pMaterial->Material.diffuse.r = D3DVAL(r);
   pMaterial->Material.diffuse.g = D3DVAL(g);
   pMaterial->Material.diffuse.b = D3DVAL(b);
   pMaterial->Material.diffuse.a = D3DVAL(a);
   pMaterial->Material.ambient.r = D3DVAL((r));
   pMaterial->Material.ambient.g = D3DVAL((g));
   pMaterial->Material.ambient.b = D3DVAL((b));
   pMaterial->Material.ambient.a = D3DVAL((a));
   pMaterial->Material.specular.r = D3DVAL(sr);
   pMaterial->Material.specular.g = D3DVAL(sg);
   pMaterial->Material.specular.b = D3DVAL(sb);
   pMaterial->Material.specular.a = D3DVAL(1.0);
   pMaterial->Material.emissive.r = D3DVAL(er);
   pMaterial->Material.emissive.g = D3DVAL(eg);
   pMaterial->Material.emissive.b = D3DVAL(eb);
   pMaterial->Material.emissive.a = D3DVAL(1.0);
   pMaterial->Material.power = D3DVAL(pow);
   pMaterial->Material.dwRampSize = 0; // Microsoft sets this to 0...

   if (bLoadTexture == TRUE)
   {
      // Always save texture filename... Even if there's a duplicate...
      // We do this in case this material needs to be re-made because of a lost surface
      pMaterial->bTexture = TRUE;

      _r3d_strcpy(pMaterial->TextureBMP, TextureFilenameBMP);
      if (bLoadAlpha == TRUE)
      {
         pMaterial->bAlpha = TRUE;
         _r3d_strcpy(pMaterial->AlphaBMP, AlphaFilenameBMP);
      }

      // Run through all materials and see if any texture filenames match
      bFoundDuplicate = FALSE;
      for (LoopVar = 0;LoopVar < _r3d_MaterialIndex;LoopVar++)
      {
         if (bLoadAlpha == TRUE)
         {
            if (!strncmp(_r3d_Material[LoopVar].TextureBMP, TextureFilenameBMP, strlen(TextureFilenameBMP)) &&
               !strncmp(_r3d_Material[LoopVar].AlphaBMP, AlphaFilenameBMP, strlen(AlphaFilenameBMP)))
            {
               // Found a match!
               pMaterial->bDuplicate = TRUE;
               pMaterial->DuplicateIndex = LoopVar;
               bFoundDuplicate = TRUE;
               break;
            }
         }
         else
         {
            if (!strncmp(_r3d_Material[LoopVar].TextureBMP, TextureFilenameBMP, strlen(TextureFilenameBMP)))
            {
               if (_r3d_Material[LoopVar].Material.diffuse.a == a) // Avoid False Alpha texture versus non False Alpha Texture
               {
                  // Found a match!
                  pMaterial->bDuplicate = TRUE;
                  pMaterial->DuplicateIndex = LoopVar;
                  bFoundDuplicate = TRUE;
                  break;
               }
            }
         }
      }

      if (bFoundDuplicate == FALSE)
      {
         // NO MATTER WHAT, In RenderIt,
         //
         // 1) The texture must be square
         // 2) The texture size must be a power of 2
         // 3) The texture must be an 8 or 24-bit uncompressed *.bmp file
         //
         // 4) No more PPM file support.
         //
         if (bLoadAlpha == TRUE)
         {
            if (!_r3d_SurfaceCreateScaledTexture(&hTempBitmap, pMaterial->TextureBMP, TRUE, pMaterial->AlphaBMP, "_r3d_MakeMaterial", &dwWidthHeight))
            {
               // Error string already defined
               return FALSE;
            }
         }
         else
         {
            if (!_r3d_SurfaceCreateScaledTexture(&hTempBitmap, pMaterial->TextureBMP, FALSE, NULL, "_r3d_MakeMaterial", &dwWidthHeight))
            {
               // Error string already defined
               return FALSE;
            }
         }
         pTextureRGBA = r3d_SurfaceGet(hTempBitmap);

         // Now we could run through the entire surface, look at
         // the Alpha components, and if any of these components
         // is 0 or 255 and nothing else, we can use a color
         // keyed texture instead for speed and higher texture
         // quality. Since they would not be alpha textures,
         // we would also not have to sort them!
         //
         // Since we want the color key to be black, we also check
         // to make sure that when
         //
         // Setting a color key can be tricky... For reliable results
         // we will use a color key value of BLACK and if any pixels
         // are black, we bump them up to be 9,9,9 RGB.
         bColorKeyedTexture = FALSE; // Assume FALSE
         pMaterial->bColorKeyedTexture = FALSE; // Assume FALSE
         if (bLoadAlpha == TRUE && bMipMap == FALSE) // We don't color key with mip-maps
         {
            bColorKeyedTexture = _r3d_bColorKeyedTexturingSupport; // Assume TRUE if device supports color keyed textures
            pMaterial->bColorKeyedTexture = bColorKeyedTexture;
            for (y = 0;y < (int)dwWidthHeight;y++)
            {
               for (x = 0;x < (int)dwWidthHeight;x++)
               {
                  UCcolorA = r3d_SurfaceColorGetA(pTextureRGBA[(y * dwWidthHeight) + x]);
                  if (UCcolorA != 0 && UCcolorA != 255)
                     // Texture can't use a color key
                     bColorKeyedTexture = FALSE;
                  if (UCcolorA == 0)
                     pTextureRGBA[(y * dwWidthHeight) + x] = (DWORD)0;
               }
            }
            // Modify texture to use color key of an unused color
            // and set the color key to the unused color
            if (bColorKeyedTexture == TRUE)
            {
               //if (_r3d_SolidTextureFormat==
               for (y = 0;y < (int)dwWidthHeight;y++)
               {
                  for (x = 0;x < (int)dwWidthHeight;x++)
                  {
                     UCcolorA = r3d_SurfaceColorGetA(pTextureRGBA[(y * dwWidthHeight) + x]);
                     if (UCcolorA == 255)
                     {
                        // Boost the color to allow our black
                        // color key to work properly. We make
                        // sure we boost by 9 so that our 16 BPP
                        // modes handle the color key properly.
                        // The smallest color value is 9 when
                        // a range of 0-31 is used for 555.
                        //
                        // 255/8=31.875
                        //
                        UCcolorR = r3d_SurfaceColorGetR(pTextureRGBA[(y * dwWidthHeight) + x]);
                        UCcolorG = r3d_SurfaceColorGetG(pTextureRGBA[(y * dwWidthHeight) + x]);
                        UCcolorB = r3d_SurfaceColorGetB(pTextureRGBA[(y * dwWidthHeight) + x]);
                        if (UCcolorR < 9 && UCcolorG < 9 && UCcolorB < 9)
                        {
                           pTextureRGBA[(y * dwWidthHeight) + x] = r3d_SurfaceColorMakeRGBA(9, 9, 9, 255);
                        }
                     }
                  }
               }
               // No alpha left in this texture...
               bLoadAlpha = FALSE;
               bFalseAlpha = FALSE;
               pMaterial->bStoreAlpha = FALSE;
            }
         }

         // OK, let's throw the texture data onto an
         // appropriate DirectDraw texture surface
         pMaterial->bMipMap = bMipMap;
         if (bMipMap == FALSE)
         {
            // Only one texture map for 0 or 1 mip-map levels

            // Create a texture surface in any memory... DX6 IM TEXTURE MEMORY MANAGMENT
            _r3d_ProperlyInitDDSD(&ddsd);
            ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
            if (bColorKeyedTexture == TRUE)
            {
               ddsd.dwFlags |= DDSD_CKSRCBLT;
               ddColorKey.dwColorSpaceLowValue = ddColorKey.dwColorSpaceHighValue = (DWORD)0;
               ddsd.ddckCKSrcBlt = ddColorKey;
            }

            ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
            ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE; //|DDSCAPS2_OPAQUE;
            ddsd.dwWidth = ddsd.dwHeight = dwWidthHeight;
            if (bLoadAlpha == TRUE || bFalseAlpha == TRUE)
               TextureFormat = _r3d_TranslucentTextureFormat;
            else
               TextureFormat = _r3d_SolidTextureFormat;
            memcpy(&ddsd.ddpfPixelFormat, &_r3d_TextureFormatList[TextureFormat].ddpfPixelFormat, sizeof(ddsd.ddpfPixelFormat));
            if ((_r3d_hResult = _r3d_DirectDraw4->CreateSurface(&ddsd, &pMaterial->lpSystemTextureSurface, NULL)) != DD_OK)
            {
               r3d_SurfaceDestroy(hTempBitmap);
               _r3d_FormulateErrorString("_r3d_MakeMaterial", "_r3d_DirectDraw4->CreateSurface(&ddsd, &pMaterial->lptmpSystemTextureSurface, NULL)", _r3d_hResult);
               return FALSE;
            }
            // Set color key if required
            if (bColorKeyedTexture == TRUE)
            {
               ddColorKey.dwColorSpaceLowValue = ddColorKey.dwColorSpaceHighValue = (DWORD)0;
               pMaterial->lpSystemTextureSurface->SetColorKey(DDCKEY_SRCBLT, &ddColorKey);
            }
            // Required for other functions
            _r3d_ProperlyInitDDSD(&pMaterial->ddsd);
            //ZeroMemory(&pMaterial->ddsd, sizeof(pMaterial->ddsd));
            //pMaterial->ddsd.dwSize = sizeof(pMaterial->ddsd);

            pMaterial->ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
            if ((_r3d_hResult = pMaterial->lpSystemTextureSurface->GetSurfaceDesc(&pMaterial->ddsd)) != DD_OK)
            {
               r3d_SurfaceDestroy(hTempBitmap);
               _r3d_FormulateErrorString("_r3d_MakeMaterial", "pMaterial->lpTextureSurface->GetSurfaceDesc(&pMaterial->ddsd)", _r3d_hResult);
               return FALSE;
            }
            // We need to lock the surface
            // We need to work with lpitch as well as pixel format
            memset(&ddsd, 0, sizeof(ddsd));
            ddsd.dwSize = sizeof(ddsd);
            ddsd.dwFlags = DDSD_PITCH | DDSD_PIXELFORMAT;
            pMaterial->lpSystemTextureSurface->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
            //mem_ptr = (unsigned char *)ddsd.lpSurface;
            SurfacePointer = (unsigned char *)ddsd.lpSurface;
            SurfacePitch = (int)ddsd.lPitch;
            for (y = 0;y < (int)dwWidthHeight;y++)
            {
               for (x = 0;x < (int)dwWidthHeight;x++)
               {
                  Alpha = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x] >> 24) & 0x000000FF);
                  Blue = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x] >> 16) & 0x000000FF);
                  Green = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x] >> 8) & 0x000000FF);
                  Red = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x]) & 0x000000FF);
                  //fRed=(float)Red/255.0F;
                  //fGreen=(float)Green/255.0F;
                  //fBlue=(float)Blue/255.0F;
                  //fAlpha=(float)Alpha/255.0F;
                  switch (TextureFormat)
                  {
                  case 0:
                     r3d_TextureAnimateSurfaceTexel0(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                     break;
                  case 1:
                     r3d_TextureAnimateSurfaceTexel1(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                     break;
                  case 2:
                     r3d_TextureAnimateSurfaceTexel2(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                     break;
                  case 3:
                     r3d_TextureAnimateSurfaceTexel3(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                     break;
                  case 4:
                     r3d_TextureAnimateSurfaceTexel4(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                     break;
                  case 5:
                     r3d_TextureAnimateSurfaceTexel5(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                     break;
                  }
               }
            }
            pMaterial->lpSystemTextureSurface->Unlock(NULL);
         }
         else
         {
            // It's a mip-map
            _r3d_ProperlyInitDDSD(&ddsd);
            //ZeroMemory(&ddsd, sizeof(ddsd));
            //ddsd.dwSize = sizeof(ddsd);
            //ddsd.dwFlags = DDSD_CAPS | DDSD_MIPMAPCOUNT;
            //ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_MIPMAPCOUNT;
            ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
            ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP | DDSCAPS_COMPLEX;
            ddsd.ddsCaps.dwCaps2 = DDSCAPS2_TEXTUREMANAGE; //|DDSCAPS2_OPAQUE;
            //ddsd.dwMipMapCount = NumMipMapLevels;
            //ddsd.dwMipMapCount = 0;
            ddsd.dwWidth = ddsd.dwHeight = dwWidthHeight;
            //ddsd.dwTextureStage = 0;
            if (bLoadAlpha == TRUE || bFalseAlpha == TRUE)
               TextureFormat = _r3d_TranslucentTextureFormat;
            else
               TextureFormat = _r3d_SolidTextureFormat;
            memcpy(&ddsd.ddpfPixelFormat, &_r3d_TextureFormatList[TextureFormat].ddpfPixelFormat, sizeof(ddsd.ddpfPixelFormat));
            if ((_r3d_hResult = _r3d_DirectDraw4->CreateSurface(&ddsd, &pMaterial->lpSystemTextureSurface, NULL)) != DD_OK)
            {
               r3d_SurfaceDestroy(hTempBitmap);
               _r3d_FormulateErrorString("_r3d_MakeMaterial", "_r3d_DirectDraw4->CreateSurface(&ddsd, &pMaterial->lptmpSystemTextureSurface, NULL)", _r3d_hResult);
               return FALSE;
            }

            // Traverse the mip-map chain and smooth scale textures and apply them to each mip-map
            LPDIRECTDRAWSURFACE4 lpDDLevel, lpDDNextLevel;
            DDSCAPS2 ddsCaps2;
            lpDDLevel = pMaterial->lpSystemTextureSurface;
            lpDDLevel->AddRef();
            ddsCaps2.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
            _r3d_hResult = DD_OK;
            while (_r3d_hResult == DD_OK)
            {
               // Process this mip map level...
               // (Copy to the mip-map level after smooth scaling)...


               // We need to lock the surface
               // We need to work with lpitch as well as pixel format
               // We need to determine whether or not we need to scale the image
               memset(&ddsd, 0, sizeof(ddsd));
               ddsd.dwSize = sizeof(ddsd);
               ddsd.dwFlags = DDSD_PITCH | DDSD_PIXELFORMAT;
               lpDDLevel->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
               //mem_ptr = (unsigned char *)ddsd.lpSurface;
               SurfacePointer = (unsigned char *)ddsd.lpSurface;
               SurfacePitch = (int)ddsd.lPitch;
               for (y = 0;y < (int)dwWidthHeight;y++)
               {
                  for (x = 0;x < (int)dwWidthHeight;x++)
                  {
                     Alpha = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x] >> 24) & 0x000000FF);
                     Blue = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x] >> 16) & 0x000000FF);
                     Green = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x] >> 8) & 0x000000FF);
                     Red = (unsigned char)((pTextureRGBA[(y * dwWidthHeight) + x]) & 0x000000FF);
                     switch (TextureFormat)
                     {
                     case 0:
                        r3d_TextureAnimateSurfaceTexel0(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                        break;
                     case 1:
                        r3d_TextureAnimateSurfaceTexel1(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                        break;
                     case 2:
                        r3d_TextureAnimateSurfaceTexel2(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                        break;
                     case 3:
                        r3d_TextureAnimateSurfaceTexel3(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                        break;
                     case 4:
                        r3d_TextureAnimateSurfaceTexel4(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                        break;
                     case 5:
                        r3d_TextureAnimateSurfaceTexel5(SurfacePointer, SurfacePitch, x, y, Alpha, Red, Green, Blue);
                        break;
                     }
                  }
               }
               lpDDLevel->Unlock(NULL);


               // Get ready for next level
               _r3d_hResult = lpDDLevel->GetAttachedSurface(&ddsCaps2, &lpDDNextLevel);
               lpDDLevel->Release();
               lpDDLevel = lpDDNextLevel;


               // Now scale the mip-map for the next level
               // We will smooth it first by averaging.
               for (y = 0;y < (int)dwWidthHeight;y += 2)
               {
                  for (x = 0;x < (int)dwWidthHeight;x += 2)
                  {
                     uca[0] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x)] >> 24) & 0x000000FF;
                     ucb[0] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x)] >> 16) & 0x000000FF;
                     ucg[0] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x)] >> 8) & 0x000000FF;
                     ucr[0] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x)]) & 0x000000FF;
                     uca[1] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x+1)] >> 24) & 0x000000FF;
                     ucb[1] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x+1)] >> 16) & 0x000000FF;
                     ucg[1] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x+1)] >> 8) & 0x000000FF;
                     ucr[1] = (long)(pTextureRGBA[((y) * dwWidthHeight) + (x+1)]) & 0x000000FF;
                     uca[2] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x)] >> 24) & 0x000000FF;
                     ucb[2] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x)] >> 16) & 0x000000FF;
                     ucg[2] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x)] >> 8) & 0x000000FF;
                     ucr[2] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x)]) & 0x000000FF;
                     uca[3] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x+1)] >> 24) & 0x000000FF;
                     ucb[3] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x+1)] >> 16) & 0x000000FF;
                     ucg[3] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x+1)] >> 8) & 0x000000FF;
                     ucr[3] = (long)(pTextureRGBA[((y+1) * dwWidthHeight) + (x+1)]) & 0x000000FF;

                     uca[4] = (((long)uca[0] + (long)uca[1] + (long)uca[2] + (long)uca[3]) / 4L);
                     ucb[4] = (((long)ucb[0] + (long)ucb[1] + (long)ucb[2] + (long)ucb[3]) / 4L);
                     ucg[4] = (((long)ucg[0] + (long)ucg[1] + (long)ucg[2] + (long)ucg[3]) / 4L);
                     ucr[4] = (((long)ucr[0] + (long)ucr[1] + (long)ucr[2] + (long)ucr[3]) / 4L);

                     pTextureRGBA[((y/2) * (dwWidthHeight/2)) + (x/2)] = ((((long)(uca[4])) << 24) | (((long)(ucb[4])) << 16) | (((long)(ucg[4])) << 8) | (long)(ucr[4]));
                  }
               }
               dwWidthHeight /= 2;
            }
            if ((_r3d_hResult != DD_OK) && (_r3d_hResult != DDERR_NOTFOUND))
            {
               // Code to handle the error goes here
               r3d_SurfaceDestroy(hTempBitmap);
               _r3d_FormulateErrorString("_r3d_MakeMaterial", "lpDDLevel->GetAttachedSurface( &ddsCaps, &lpDDNextLevel)", _r3d_hResult);
               return FALSE;
            }
         }
         r3d_SurfaceDestroy(hTempBitmap);

         // Get the texture interface
         if ((_r3d_hResult = pMaterial->lpSystemTextureSurface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&pMaterial->lpSystemTexture)) != S_OK)
         {
            _r3d_FormulateErrorString("_r3d_MakeMaterial", "pMaterial->lpSystemTextureSurface->QueryInterface(IID_IDirect3DTexture2, (LPVOID*)&pMaterial->lpSystemTexture)", _r3d_hResult);
            //r3d_MessageBox(r3d_GetLastErrorString(),"_r3d_MakeMaterial",_r3d_hResult);
            return FALSE;
         }

      }

   }

   // Create Material
   if ((_r3d_hResult = _r3d_Direct3D->CreateMaterial(&pMaterial->lpMaterial, NULL)) != DD_OK)
   {
      _r3d_FormulateErrorString("_r3d_MakeMaterial", "_r3d_Direct3D->CreateMaterial(&pMaterial->lpMaterial,NULL)", _r3d_hResult);
      return FALSE;
   }
   // Set Material
   if ((_r3d_hResult = pMaterial->lpMaterial->SetMaterial((D3DMATERIAL *)&pMaterial->Material)) != DD_OK)
   {
      _r3d_FormulateErrorString("_r3d_MakeMaterial", "pMaterial->lpMaterial->SetMaterial((D3DMATERIAL *)&pMaterial->Material)", _r3d_hResult);
      return FALSE;
   }
   // Get Material Handle
   if ((_r3d_hResult = pMaterial->lpMaterial->GetHandle(_r3d_Device, &pMaterial->hMaterial)) != DD_OK)
   {
      _r3d_FormulateErrorString("_r3d_MakeMaterial", "pMaterial->lpMaterial->GetHandle(_r3d_Device,&pMaterial->hMaterial)", _r3d_hResult);
      return FALSE;
   }

   return TRUE;
}

BOOL R3D_STDCALL
_r3d_SetMaterial(_R3D_MATERIAL *pMat)
{
   int hMat;
   _R3D_MATERIAL *pMat0;

   pMat0 = &_r3d_Material[0];
   hMat = (int)(pMat - pMat0);

   return (r3d_SetMaterial(hMat));
}

BOOL R3D_STDCALL
r3d_SetMaterial(int handle)
{
   // This function needs to be optimized!
   static LPDIRECT3DTEXTURE2 lpPreviousTexture = NULL;
   static BOOL bPreviousColorKeyEnable = FALSE; // Default
   static BOOL bPreviousAlphaBlend = FALSE; // Default
   int TexFormat = 0;

   // First, we do a little optimization
   if (handle == _r3d_SetMaterialLast)
      return TRUE; // It's already been set previously
   else
   {
      // Check for errors
      if (handle < 0 || handle >= _r3d_MaterialIndex)
      {
         _r3d_FormulateErrorString("r3d_SetMaterial", "Invalid Material Handle", DD_OK);
         return FALSE;
      }
      // Safe to set this
      _r3d_SetMaterialLast = handle;
   }

   // Handle Alpha Textures:
   if (_r3d_Material[handle].bStoreAlpha == TRUE)
   {
      // Multipass using alpha blending
      // HEY! Order of operations matters here.... It don't say
      // this in Microsofts crappy docs... I stole this
      // from one of the stinkin demos and found that this order
      // of operations actually works...
      if (bPreviousAlphaBlend == FALSE)
      {
         _r3d_Device->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
         _r3d_Device->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
         _r3d_Device->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
         bPreviousAlphaBlend = TRUE;
      }
   }
   else
   {
      if (bPreviousAlphaBlend == TRUE)
      {
         _r3d_Device->SetRenderState(D3DRENDERSTATE_SRCBLEND, (DWORD)D3DBLEND_ONE);
         _r3d_Device->SetRenderState(D3DRENDERSTATE_DESTBLEND, (DWORD)D3DBLEND_ZERO);
         _r3d_Device->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, (DWORD)FALSE);
         bPreviousAlphaBlend = FALSE;
      }
   }

   // Set textures to duplicates only after the above code sets the
   // material properties.
   if (_r3d_Material[handle].bDuplicate == TRUE)
   {
      // To color key or not to color, that is the question
      if (_r3d_Material[_r3d_Material[handle].DuplicateIndex].bColorKeyedTexture == TRUE)
      {
         if (bPreviousColorKeyEnable == FALSE)
         {
            _r3d_Device->SetRenderState(D3DRENDERSTATE_COLORKEYENABLE, (DWORD)TRUE);
            bPreviousColorKeyEnable = TRUE;
         }
      }
      else
      {
         if (bPreviousColorKeyEnable == TRUE)
         {
            _r3d_Device->SetRenderState(D3DRENDERSTATE_COLORKEYENABLE, (DWORD)FALSE);
            bPreviousColorKeyEnable = FALSE;
         }
      }

      if (lpPreviousTexture != _r3d_Material[_r3d_Material[handle].DuplicateIndex].lpSystemTexture)
      {
         _r3d_Device->SetTexture(0, _r3d_Material[_r3d_Material[handle].DuplicateIndex].lpSystemTexture);
         lpPreviousTexture = _r3d_Material[_r3d_Material[handle].DuplicateIndex].lpSystemTexture;
      }
   }
   else
   {
      // To color key or not to color, that is the question
      if (_r3d_Material[handle].bColorKeyedTexture == TRUE)
      {
         if (bPreviousColorKeyEnable == FALSE)
         {
            _r3d_Device->SetRenderState(D3DRENDERSTATE_COLORKEYENABLE, (DWORD)TRUE);
            bPreviousColorKeyEnable = TRUE;
         }
      }
      else
      {
         if (bPreviousColorKeyEnable == TRUE)
         {
            _r3d_Device->SetRenderState(D3DRENDERSTATE_COLORKEYENABLE, (DWORD)FALSE);
            bPreviousColorKeyEnable = FALSE;
         }
      }

      if (lpPreviousTexture != _r3d_Material[handle].lpSystemTexture)
      {
         _r3d_Device->SetTexture(0, _r3d_Material[handle].lpSystemTexture);
         lpPreviousTexture = _r3d_Material[handle].lpSystemTexture;
      }
   }

   _r3d_Device->SetLightState(D3DLIGHTSTATE_MATERIAL, _r3d_Material[handle].hMaterial);

   return TRUE;
}

BOOL R3D_STDCALL
_r3d_SurfaceCreateScaledTexture(int *hBitmapRet, char *pTextureFilename, BOOL bLoadAlpha, char *pAlphaFilename, char *FunctionNameString, unsigned long *dwWidthHeightRet)
{
   unsigned long dwWidth, dwHeight;
   unsigned long dwWidthHeight, dwWidthAlf, dwHeightAlf;
   BOOL bPalette256, bPalette256Alf;
   int hBitmap, hBitmapScaled;
   char buffer[256];

   // First, we get the BMP width and height
   if (_r3d_BMPGetInfo(pTextureFilename, FunctionNameString, &dwWidth, &dwHeight, &bPalette256) == FALSE)
   {
      // Error string already defined
      return FALSE;
   }

   // Now, we'll error check the alpha BMP
   if (bLoadAlpha)
   {
      if (_r3d_BMPGetInfo(pAlphaFilename, FunctionNameString, &dwWidthAlf, &dwHeightAlf, &bPalette256Alf) == FALSE)
      {
         // Error string already defined
         return FALSE;
      }

      // Now, we'll make sure that the alpha BMP and texture BMP are the
      // same size
      if (dwWidthAlf != dwWidth || dwHeightAlf != dwHeight)
      {
         sprintf(buffer, "%s is not the same size as %s", pTextureFilename, pAlphaFilename);
         _r3d_FormulateErrorString(FunctionNameString, "", DD_OK);
         return FALSE;
      }
   }


   // Let's force texture to be square, the largest of width or height
   if (dwWidth > dwHeight)
      dwWidthHeight = dwWidth;
   else if (dwWidth < dwHeight)
      dwWidthHeight = dwHeight;
   else
      dwWidthHeight = dwWidth;

   // Let's determine if this texture size is a power of 2 (within our 1024 limit)
   if (dwWidthHeight != _R3D_MAXTEXTURESIZE &&
      dwWidthHeight != 768 &&
      dwWidthHeight != 512 &&
      dwWidthHeight != 256 &&
      dwWidthHeight != 128 &&
      dwWidthHeight != 64 &&
      dwWidthHeight != 32 &&
      dwWidthHeight != 16 &&
      dwWidthHeight != 8 &&
      dwWidthHeight != 4 &&
      dwWidthHeight != 2)
   {
      // Force texture to be a power of 2 in size and scale upwards if
      // possible
      if (dwWidthHeight > 768)
         dwWidthHeight = 1024;
      if (dwWidthHeight > 512)
         dwWidthHeight = 768;
      if (dwWidthHeight > 256)
         dwWidthHeight = 512;
      if (dwWidthHeight > 128)
         dwWidthHeight = 256;
      if (dwWidthHeight > 64)
         dwWidthHeight = 128;
      if (dwWidthHeight > 32)
         dwWidthHeight = 64;
      if (dwWidthHeight > 16)
         dwWidthHeight = 32;
      if (dwWidthHeight > 8)
         dwWidthHeight = 16;
      if (dwWidthHeight > 4)
         dwWidthHeight = 8;
      if (dwWidthHeight > 2)
         dwWidthHeight = 4;
      if (dwWidthHeight > 1)
         dwWidthHeight = 2;
   }

   // Force texture to be within the min,max width and height constraints
   if (dwWidthHeight < _r3d_dwMinTextureWidthHeight)
      dwWidthHeight = _r3d_dwMinTextureWidthHeight;
   else if (dwWidthHeight > _r3d_dwMaxTextureWidthHeight)
      dwWidthHeight = _r3d_dwMaxTextureWidthHeight;

   // Load the bitmap
   if (!r3d_SurfaceCreateFromBMP(&hBitmap, pTextureFilename))
   {
      // Error already defined but it won't say "_r3d_TextureLoadScale"
      return FALSE;
   }
   if (bLoadAlpha == TRUE)
   {
      if (!r3d_SurfaceSetAlphaFromBMP(hBitmap, pAlphaFilename))
      {
         // Error already defined but it won't say "_r3d_TextureLoadScale"
         return FALSE;
      }
   }

   // Determine if scaling is even required and abort if not
   if (dwWidth == dwHeight && dwWidth == dwWidthHeight)
   {
      *hBitmapRet = hBitmap;
      *dwWidthHeightRet = dwWidthHeight;
      return TRUE;
   }

   // Now we are ready to scale the texture
   if (!r3d_SurfaceCreate(&hBitmapScaled, dwWidthHeight, dwWidthHeight))
   {
      // Error already defined but it won't say "_r3d_TextureLoadScale"
      return FALSE;
   }
   if (!r3d_SurfaceStretchSmooth(hBitmap, hBitmapScaled))
   {
      // Error already defined but it won't say "_r3d_TextureLoadScale"
      return FALSE;
   }
   r3d_SurfaceDestroy(hBitmap);

   *hBitmapRet = hBitmapScaled;
   *dwWidthHeightRet = dwWidthHeight;
   return TRUE;
}