#include "stdafx.h"

#include <vector>

#include "Tile.h"
#include "Triangle.h"
#include "Vec.h"
#include "TextureMapper.h"

// MFC - Assists in finding memory leaks:
#ifdef MFC_DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#pragma warning( disable : 4244 )

TextureMapper* TextureMapper::_instance = 0;

/****************************************************************************/
/** \brief Liefert die Singleton-Instanz
    \return Pointer auf die Instanz
*****************************************************************************/
TextureMapper* TextureMapper::getInstance()
{
 if (_instance == 0) 
   _instance = new TextureMapper();
 return _instance;
}

/****************************************************************************/
/** \brief Konstruktor
*****************************************************************************/
TextureMapper::TextureMapper()
{
   debugFlag = false;
   
   m_ZBuffer = 0;

   m_alpha = -1;

   m_tex = 0;
   m_p0.clear();// = new Vec();
   m_p1.clear(); // = new Vec();
   m_p2.clear(); // = new Vec();
   
   m_planePixels = 0;

   for (int c=0; c < 256; c++)
      for (int i=0; i < 256; i++)
         { 
         m_rgbIntensity[c][i] = (BYTE)((c * i) >> 8);
         }                  
}

/****************************************************************************/
/** \brief Destruktor
*****************************************************************************/
TextureMapper::~TextureMapper()
{
_instance = 0; // Singleton !!!
}

/*******************************************************************************
* // d - Distanz vom Ursprung zur Plane
*******************************************************************************/
void TextureMapper::render(float d, Triangle& t, BYTE* pane, float* ZBuffer, int width, int height)
  {
  m_alpha = t.m_alpha;
 
  m_xmax = t.m_xmax ; // SkyBox
  m_ymax = t.m_ymax ; // SkyBox

  m_height   = height;
  m_height_1 = height-1;
  m_width    = width;

  msg_count = 0;
  m_d = d;
  m_p0.set(t.m_p0);
  m_p1.set(t.m_p1);
  m_p2.set(t.m_p2);

  m_surface = SurfaceCache::getInstance()->getSurface(t.m_sid);
  if (m_surface == 0)
     return;
     
  m_tex = t.m_tex;

  m_tex_width  = m_tex->getWidth() ;
  m_tex_height = m_tex->getHeight();

  m_planePixels = pane;
  m_ZBuffer = ZBuffer;

//  test();

//  dumpVertices();

  project(d, t.m_p0, t.m_p1, t.m_p2);
  x0 += (m_width/2);  // Der Mapper funktioniert nur mit pos. Koordinaten
  y0 += (m_height/2); // (sonst Rundungsprobleme)

  x1 += (m_width/2);  
  y1 += (m_height/2); 

  x2 += (m_width/2);  
  y2 += (m_height/2); 



  uc0 = c0 * t.m_u0;
  vc0 = c0 * t.m_v0;

  uc1 = c1 * t.m_u1;
  vc1 = c1 * t.m_v1;

  uc2 = c2 * t.m_u2;
  vc2 = c2 * t.m_v2;

//  dumpProjection();
  addOffset();

 //dumpProjection();
  sortVertices(); // Sortiere die 2D-Vertices
//  dumpProjection();

  gradients2();    // berechne die Gradienten
//  dumpGradients();

  doit();
  }

/*******************************************************************************
* Projiziere die Raumkoordinaten auf die ViewPlane (2D)
*
* @param d  Distanz zur (Near-)Pane
* @param p0 Punkt 0 von Dreieck
* @param p1 Punkt 1 von Dreieck
* @param p2 Punkt 2 von Dreieck
*******************************************************************************/
   void TextureMapper::project(float d, Vec p0, Vec p1, Vec p2)
   {
   float q=0;

   // 1. Punkt
    q = d / p0.m_z;
   x0 = (q * p0.m_x);
   y0 = (q * p0.m_y);
   z0 = p0.m_z; // Wird eigentlich nicht mehr benoetigt!
   c0 = 1/z0;

   // 2. Punkt
   q = d / p1.m_z;
   x1 = (q * p1.m_x);
   y1 = (q * p1.m_y);
   z1 = p1.m_z;  // Wird eigentlich nicht mehr benoetigt!
   c1 = 1/z1;

   // 3. Punkt
    q = d / p2.m_z;
   x2 = (q * p2.m_x);
   y2 = (q * p2.m_y);
   z2 = p2.m_z; // Wird eigentlich nicht mehr benoetigt!
   c2 = 1/z2;
   }

/*******************************************************************************
*
*******************************************************************************/
 void  TextureMapper::addOffset()
{
  x0 += (float)0.5;
  y0 += (float)0.5;

  x1 += (float)0.5;
  y1 += (float)0.5;

  x2 += (float)0.5;
  y2 += (float)0.5;
}

/*******************************************************************************
* Sortiere die Vertices (y0 <= y1 <= y2)
* Sortierung bzgl. y-Koordinate (aufsteigend)
*******************************************************************************/
 void TextureMapper::sortVertices()
{
float t=0;

if (y0 > y1)
   {    // p0 <--> p1
   t=x0; x0=x1; x1=t;
   t=y0; y0=y1; y1=t;
   t=z0; z0=z1; z1=t;
   t=c0; c0=c1; c1=t;

   t=uc0; uc0=uc1; uc1=t;
   t=vc0; vc0=vc1; vc1=t;
   }

if (y0 > y2)
   {    // p0 <--> p2
   t=x0; x0=x2; x2=t;
   t=y0; y0=y2; y2=t;
   t=z0; z0=z2; z2=t;
   t=c0; c0=c2; c2=t;

   t=uc0; uc0=uc2; uc2=t;
   t=vc0; vc0=vc2; vc2=t;
   }

if (y1 > y2)
   {   // p1 <--> p2
   t=x1; x1=x2; x2=t;
   t=y1; y1=y2; y2=t;
   t=z1; z1=z2; z2=t;
   t=c1; c1=c2; c2=t;

   t=uc1; uc1=uc2; uc2=t;
   t=vc1; vc1=vc2; vc2=t;
   }
}

/*******************************************************************************
* Berechne die Gradienten
* Berechnung wegen 'Nachvollziehbarkeit' der Formeln nicht optimiert.
* Ist aber nicht tragisch, weil diese Funktion nur einmal aufgerufen wird
*******************************************************************************/
   void TextureMapper::gradients()
   {
   float x_4, y_4, c_4, uc_4, vc_4; // Hilfspunkt "4"
   float x_3, y_3, c_3, uc_3, vc_3; // Hilfspunkt "3"

   // --------------------------------------------------------------------------
   // Gradienten des Polygons (Tile) ermitteln
   // --------------------------------------------------------------------------
   // Hilfspunkt "4" konstruieren:
   x_4  = ((x1-x2)/(y1-y2)) * (y0-y2) + x2;
   y_4  = y0;
   c_4  = ((c1-c2)  /(y1-y2)) * (y0-y2) + c2;
   uc_4 = ((uc1-uc2)/(y1-y2)) * (y0-y2) + uc2;
   vc_4 = ((vc1-vc2)/(y1-y2)) * (y0-y2) + vc2;

   // Mit Hilfpunkt "4" dc/dx, duc/dx und dvc/dx berechnen (dy = const!!!)
   dc_dx  = (c_4  - c0)  / (x_4 - x0);
   duc_dx = (uc_4 - uc0) / (x_4 - x0);
   dvc_dx = (vc_4 - vc0) / (x_4 - x0);

   // Hilfspunkt "3" konstruieren:
   x_3 = x0;
   y_3 = ((y1-y2)/(x1-x2)) * (x0-x2) + y2;
   c_3 = ((c1-c2)/(x1-x2)) * (x0-x2) + c2;
   uc_3 = ((uc1-uc2)/(x1-x2)) * (x0-x2) + uc2;
   vc_3 = ((vc1-vc2)/(x1-x2)) * (x0-x2) + vc2;

   // Mit Hilfpunkt "3" dc/dy, duc/dy und dvc/dy berechnen (dx = const!!!)
   dc_dy = (c_3 - c0) / (y_3 - y0);
   duc_dy = (uc_3 - uc0) / (y_3 - y0);
   dvc_dy = (vc_3 - vc0) / (y_3 - y0);
   }

/*******************************************************************************
* Berechne die Gradienten
*******************************************************************************/
 void TextureMapper::gradients2()
{
  float  denom = ((x2 - x0) * (y1 - y0) - (x1 - x0) * (y2 - y0));

  denom = 1 / denom;

  dc_dx  = ((c2  - c0)  * (y1 - y0) - (c1 - c0)   * (y2 - y0)) * denom;
  duc_dx = ((uc2 - uc0) * (y1 - y0) - (uc1 - uc0) * (y2 - y0)) * denom;
  dvc_dx = ((vc2 - vc0) * (y1 - y0) - (vc1 - vc0) * (y2 - y0)) * denom;

  dc_dy  = ((c1  - c0)  * (x2 - x0) - (c2 - c0)   * (x1 - x0)) * denom;
  duc_dy = ((uc1 - uc0) * (x2 - x0) - (uc2 - uc0) * (x1 - x0)) * denom;
  dvc_dy = ((vc1 - vc0) * (x2 - x0) - (vc2 - vc0) * (x1 - x0)) * denom;
}


/*******************************************************************************
* Berechne die Gradienten (auch nicht schneller :-( )
*******************************************************************************/
 void TextureMapper::gradients3()
{
  float x2_minus_x0 = x2-x0;
  float x1_minus_x0 = x1-x0;

  float y1_minus_y0 = y1-y0;
  float y2_minus_y0 = y2-y0;


//float  denom = ((x2 - x0) * (y1 - y0) - (x1 - x0) * (y2 - y0));
  float  denom = (x2_minus_x0 * y1_minus_y0 - x1_minus_x0 * y2_minus_y0);

  denom = 1 / denom;

  dc_dx  = ((c2  - c0)  * y1_minus_y0 - (c1  - c0)  * y2_minus_y0) * denom;
  duc_dx = ((uc2 - uc0) * y1_minus_y0 - (uc1 - uc0) * y2_minus_y0) * denom;
  dvc_dx = ((vc2 - vc0) * y1_minus_y0 - (vc1 - vc0) * y2_minus_y0) * denom;

  dc_dy  = ((c1  - c0)  * x2_minus_x0 - (c2  - c0)  * x1_minus_x0) * denom;
  duc_dy = ((uc1 - uc0) * x2_minus_x0 - (uc2 - uc0) * x1_minus_x0) * denom;
  dvc_dy = ((vc1 - vc0) * x2_minus_x0 - (vc2 - vc0) * x1_minus_x0) * denom;
}


/*******************************************************************************
*
  y = mx + b        oder

       dy                        dc                        dc
  x = ---- x + x0    oder   c = ----  y + c0    oder  c = ---- x + c0
       dx                        dy                        dx

*******************************************************************************/
 void TextureMapper::doit()
{
int y0i = (int)y0;
int y1i = (int)y1;
int y2i = (int)y2;

int side = 0; // 1 ist links, 0 ist rechts;

// zuerst die Steigungen dx/dy der Kanten berechnen
// Bem.: die Vertices sind sortiert (y aufsteigend)
float dxdy1 = 0, dxdy2 = 0, dxdy3 = 0;

if (y1 > y0)
        dxdy1 = (x1 - x0) / (y1 - y0); // Steigung der Kante
if (y2 > y0)
        dxdy2 = (x2 - x0) / (y2 - y0); // Steigung der Kante
if (y2 > y1)
        dxdy3 = (x2 - x1) / (y2 - y1); // Steigung der Kante

if (dxdy1 > dxdy2)
  side = 1;

if (y0 == y1)
    if(x0 < x1)
       side = 1;
    else
       side = 0;

if (y1 == y2)
    if (x2 < x1)
        side = 1;
    else
        side = 0;

if (side == 1) // die linke Kante ist die laengste
  {
  // Steigungen entlang der linken Kante ermitteln
  dx_dy_a  = dxdy2;
  dc_dy_a  = (c0  - c2)  / (y0-y2);      // dxdy1 * dc_dx + dc_dy;
  duc_dy_a = (uc0 - uc2) / (y0-y2);
  dvc_dy_a = (vc0 - vc2) / (y0-y2);

  float dy = 1.0f - (y0 - y0i);  // Offset (Preset)
  xa  =  x0  + dy * dx_dy_a;   // Start-X auf der linken Seite
  ca  =  c0  + dy * dc_dy_a;   // Startwert von c: (Geradengleichung)
  uca =  uc0 + dy * duc_dy_a;  // Startwert von UDurchZ
  vca =  vc0 + dy * dvc_dy_a;  // Startwert von VDurchZ

  if (y0i < y1i) // 'oberes' Segment zeichnen wenn sichtbar
     {
     // Steigungen dx/dy entlang der rechten Kante ermitteln
     // dc/dy ist fuer linke und rechte Kante gleich !!
     dx_dy_b = dxdy1;        // Steigung dx/dy der rechten Seite
     xb = dx_dy_b * dy + x0 ;   // End-X auf der rechten Seite (Geradengleichung)
     renderSegment(y0i, y1i);
     }

  if (y1i < y2i) // 'unteres' Segment zeichnen wenn sichtbar
     {
     // Steigungen dx/dy entlang der rechten Kante ermitteln
     // dc/dy ist fuer linke und rechte Kante gleich !!
     dx_dy_b = dxdy3;        // Steigung dx/dy der rechten Seite
     xb = x1 + (1.0f - (y1 - y1i)) * dx_dy_b;
     renderSegment(y1i, y2i);
     }
  }
else
  {
  dx_dy_b = dxdy2;
  float dy = 1 - (y0 - y0i);
  xb = x0 + dy * dx_dy_b;

  if (y0i < y1i)
    {
    dx_dy_a  = dxdy1;
    dc_dy_a  = dx_dy_a * dc_dx  + dc_dy;
    duc_dy_a = dx_dy_a * duc_dx + duc_dy;
    dvc_dy_a = dx_dy_a * dvc_dx + dvc_dy;
    xa  = x0  + dy * dx_dy_a;
    ca  = c0  + dy * dc_dy_a;
    uca = uc0 + dy * duc_dy_a;
    vca = vc0 + dy * dvc_dy_a;

    renderSegment(y0i, y1i);
    }

  if (y1i < y2i)
    {
    dx_dy_a  = dxdy3;
    dc_dy_a  = dx_dy_a * dc_dx  + dc_dy;
    duc_dy_a = dx_dy_a * duc_dx + duc_dy;
    dvc_dy_a = dx_dy_a * dvc_dx + dvc_dy;
    dy  = 1.0f - (y1 - y1i);
    xa  = x1 + dy * dx_dy_a;
    ca  = c1 + dy * dc_dy_a;
    uca = uc1 + dy * duc_dy_a;
    vca = vc1 + dy * dvc_dy_a;

    renderSegment(y1i, y2i);
    }
  }
}

/*******************************************************************************
* Zeichnet ein Segment eines Dreicks
* @param y1 'oberer'  y Wert
* @param y2 'unterer' y Wert
*******************************************************************************/
 void TextureMapper::renderSegment(int y1, int y2)
{
int y, x1, x2;
float /*uu, vv,*/ dx, c, uc, vc;

//uc = uca;
//vc = vca;

for (y=y1; y<y2; y++) // Gehe ueber alle Scanlines
   {
   x1 = (int) xa;
   x2 = (int) xb;

   dx = 1.0f - (xa - x1);   // Offset (Preset)
   c  = ca  + dx * dc_dx;  // Startwert von c setzen, bzw. mit dx korrigieren
   uc = uca + dx * duc_dx;
   vc = vca + dx * dvc_dx;

//   uu = uc / c; //uc * (1/c);
//   vv = vc / c; //vc * (1/c);

   renderLine(y, x1, x2, c, uc, vc);

   xa  += dx_dy_a;  // fuer die naechste Zeile: x-Start berechnen (also dx/dy der A-bzw. linken  Linie addieren)
   xb  += dx_dy_b;  // fuer die naechste Zeile: x-End berechnen   (also dx/dy der B-bzw. rechten Linie addieren)
   ca  += dc_dy_a;  // fuer die naechste Zeile: c berechnen (also Delta)
   uca += duc_dy_a;
   vca += dvc_dy_a;
   }
}

/*******************************************************************************
*
*******************************************************************************/
inline void TextureMapper::renderLine(int y, int x1, int x2, float cs, float ucs, float vcs)
{
int x, tx, ty, k, kc;
float z, zBuf, u, v;
kc = (((m_height_1) - y)*m_width);

for (x=x1; x < x2; ++x)
   {
   int kcx = kc + x;
   z = 1.0f/cs;  // Z zurueckgewinnen

   zBuf = m_ZBuffer[kcx];
   if (z <= zBuf)
      {
      m_ZBuffer[kcx] = z;

      u = ucs * z;
      v = vcs * z;

      tx = u;
      ty = v;
  
      k = kcx * 3; // BYTES_PER_PIXEL
      if ((tx < m_tex_width) && (ty < m_tex_height))  // Teuer !!!!!
         {
         if (m_alpha == -1)
            {   
            int pixIndex = (ty << m_surface->m_shift) + tx;
            pixIndex *= 3; //(ty << m_tex->m_shift) + tx]
            m_planePixels[k]   = m_surface->m_indexArray[pixIndex+2];
            m_planePixels[k+1] = m_surface->m_indexArray[pixIndex+1];
            m_planePixels[k+2] = m_surface->m_indexArray[pixIndex+0];
            }
         else
            {   
            int pixIndex = (ty << m_surface->m_shift) + tx;
            pixIndex *= 4; //(ty << m_tex->m_shift) + tx]
            if (m_surface->m_indexArray[pixIndex+3] == 1)
               {    
               m_planePixels[k]   = m_surface->m_indexArray[pixIndex+2];
               m_planePixels[k+1] = m_surface->m_indexArray[pixIndex+1];
               m_planePixels[k+2] = m_surface->m_indexArray[pixIndex+0];
               }
            }
         }
      }

   cs  += dc_dx;   // c um dc/dx erhoehen
   ucs += duc_dx;
   vcs += dvc_dx;
   }
}

/*******************************************************************************
*
*******************************************************************************/
   void TextureMapper::dumpVertices()
  {
  printf("--- Vertices ---");
  printf("p0=%s", m_p0.dump());
  printf("p1=%s", m_p1.dump());
  printf("p2=%s", m_p2.dump());
  }

/*******************************************************************************
*
*******************************************************************************/
   void TextureMapper::dumpProjection()
  {
  printf("--- Projection ---");
  printf("d=%ld", m_d);
  printf("x0=%lf y0=%lf z0=%lf c0=%lf", x0, y0, z0, c0);
  printf("x1=%lf y1=%lf z1=%lf c1=%lf", x1, y1, z1, c1);
  printf("x2=%lf y2=%lf z2=%lf c2=%lf", x2, y2, z2, c2);
  }

/*******************************************************************************
*
*******************************************************************************/
void TextureMapper::dumpGradients()
  {
  printf("--- Gradients ---");
  printf("dc_dx=%lf",  dc_dx);
  printf("dc_dy=%lf",  dc_dy);
  printf("duc_dx=%lf", duc_dx);
  printf("duc_dy=%lf", duc_dy);
  printf("dvc_dx=%lf", dvc_dx);
  printf("dvc_dy=%lf", dvc_dy);
  }


