/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "software/r1_software.hh"
#include "software/r1_software_globals.hh"
#include "software/span_buffer.hh"
#include "software/inline_fpu.hh"
#include "software/mappers.hh"

//all calls to qftoi() here assume it will truncate, so be sure that start_trunc() gets
//called before these functions execute

//straight rasterization routines 1st
//span rasterization at the bottom of the file

void tri_draw_perspective_lit(tri_edge &top_to_middle,tri_edge &top_to_bottom, tri_edge &mid_to_bottom,
                               sw32 start_y, i4_bool edge_comp)
{
  sw32 cur_y = start_y;
  
  w16 *screen_line;
  
  screen_line = r1_software_render_buffer_ptr + cur_y*r1_software_render_buffer_wpl;
  
  tri_edge *left;
  tri_edge *right;
  tri_edge *last_left=0;
  
  perspective_span cps; //(cur_perspective_span)
  
  //rasterize top to middle / top to bottom
  float cur_l;
  float oozystep,sozystep,tozystep,lystep;

  if (top_to_middle.dy)
  {    
    if (edge_comp)
    {      
      left  = &top_to_bottom;
      right = &top_to_middle;      
    }
    else
    {      
      left  = &top_to_middle;
      right = &top_to_bottom;
    }

    last_left = left;
    
    sw32 lx,rx;

    lx = (left->px  + 0xFFFF) >> 16;
    rx = (right->px + 0xFFFF) >> 16;
        
    cps.ooz = cur_grads.oozat00 + ((float)cur_y*cur_grads.doozdy) + ((float)lx * cur_grads.doozdx);
    cps.soz = cur_grads.sozat00 + ((float)cur_y*cur_grads.dsozdy) + ((float)lx * cur_grads.dsozdx);
    cps.toz = cur_grads.tozat00 + ((float)cur_y*cur_grads.dtozdy) + ((float)lx * cur_grads.dtozdx);
    cur_l   = cur_grads.lat00   + ((float)cur_y*cur_grads.dldy)   + ((float)lx * cur_grads.dldx);

    oozystep = cur_grads.doozdy + cur_grads.doozdx * left->dxdy;
    sozystep = cur_grads.dsozdy + cur_grads.dsozdx * left->dxdy;
    tozystep = cur_grads.dtozdy + cur_grads.dtozdx * left->dxdy;
    lystep   = cur_grads.dldy   + cur_grads.dldx   * left->dxdy;
        
    while (top_to_middle.dy)
    {      
      lx = (left->px  + 0xFFFF) >> 16;
      rx = (right->px + 0xFFFF) >> 16;
    
      sw32 width = rx - lx;
      if (width>0)
      {
        cps.l = qftoi(cur_l);
        cur_scanline_texture_func(screen_line,(lx<<1),&cps,width);        
      }
     
      //advance y down
      cur_y++;

      cps.ooz += oozystep;
      cps.soz += sozystep;
      cps.toz += tozystep;
      cur_l   += lystep;

      top_to_middle.px  += top_to_middle.dxdy_fixed;
      top_to_bottom.px  += top_to_bottom.dxdy_fixed;
      
      top_to_middle.dy--;
      top_to_bottom.dy--;
            
      screen_line += r1_software_render_buffer_wpl;      
    }    
  }
  
  if (mid_to_bottom.dy)
  {
    if (edge_comp)
    {
      left  = &top_to_bottom;
      right = &mid_to_bottom;
    }
    else
    {
      left  = &mid_to_bottom;
      right = &top_to_bottom;
    }

    sw32 lx,rx;

    lx = (left->px  + 0xFFFF) >> 16;
    rx = (right->px + 0xFFFF) >> 16;

    if (left != last_left)
    {
      //switched edges, recalc these values
      cps.ooz = cur_grads.oozat00 + ((float)cur_y*cur_grads.doozdy) + ((float)lx * cur_grads.doozdx);
      cps.soz = cur_grads.sozat00 + ((float)cur_y*cur_grads.dsozdy) + ((float)lx * cur_grads.dsozdx);
      cps.toz = cur_grads.tozat00 + ((float)cur_y*cur_grads.dtozdy) + ((float)lx * cur_grads.dtozdx);
      cur_l   = cur_grads.lat00   + ((float)cur_y*cur_grads.dldy)   + ((float)lx * cur_grads.dldx);

      oozystep = cur_grads.doozdy + cur_grads.doozdx * left->dxdy;
      sozystep = cur_grads.dsozdy + cur_grads.dsozdx * left->dxdy;
      tozystep = cur_grads.dtozdy + cur_grads.dtozdx * left->dxdy;
      lystep   = cur_grads.dldy   + cur_grads.dldx   * left->dxdy;
    }    
    
    while (mid_to_bottom.dy)
    {      
      lx = (left->px  + 0xFFFF) >> 16;
      rx = (right->px + 0xFFFF) >> 16;
    
      sw32 width = rx - lx;
      if (width>0)
      {
        cps.l = qftoi(cur_l);
        cur_scanline_texture_func(screen_line,(lx<<1),&cps,width);        
      }
     
      //advance y down
      cur_y++;

      cps.ooz += oozystep;
      cps.soz += sozystep;
      cps.toz += tozystep;
      cur_l   += lystep;

      top_to_bottom.px  += top_to_bottom.dxdy_fixed;
      mid_to_bottom.px  += mid_to_bottom.dxdy_fixed;
      
      top_to_bottom.dy--;
      mid_to_bottom.dy--;
                  
      screen_line += r1_software_render_buffer_wpl;
    }
  }
}

void tri_draw_perspective_unlit(tri_edge &top_to_middle,tri_edge &top_to_bottom, tri_edge &mid_to_bottom,
                                 sw32 start_y, i4_bool edge_comp)
{
  sw32 cur_y = start_y;
  
  w16 *screen_line;
  
  screen_line = r1_software_render_buffer_ptr + cur_y*r1_software_render_buffer_wpl;
  
  tri_edge *left;
  tri_edge *right;
  tri_edge *last_left=0;
  
  perspective_span cps; //(cur_perspective_span)
  
  //rasterize top to middle / top to bottom  
  float oozystep,sozystep,tozystep,lystep;

  if (top_to_middle.dy)
  {    
    if (edge_comp)
    {      
      left  = &top_to_bottom;
      right = &top_to_middle;      
    }
    else
    {      
      left  = &top_to_middle;
      right = &top_to_bottom;
    }

    last_left = left;
    
    sw32 lx,rx;

    lx = (left->px  + 0xFFFF) >> 16;
    rx = (right->px + 0xFFFF) >> 16;
        
    cps.ooz = cur_grads.oozat00 + ((float)cur_y*cur_grads.doozdy) + ((float)lx * cur_grads.doozdx);
    cps.soz = cur_grads.sozat00 + ((float)cur_y*cur_grads.dsozdy) + ((float)lx * cur_grads.dsozdx);
    cps.toz = cur_grads.tozat00 + ((float)cur_y*cur_grads.dtozdy) + ((float)lx * cur_grads.dtozdx);    

    oozystep = cur_grads.doozdy + cur_grads.doozdx * left->dxdy;
    sozystep = cur_grads.dsozdy + cur_grads.dsozdx * left->dxdy;
    tozystep = cur_grads.dtozdy + cur_grads.dtozdx * left->dxdy;
        
    while (top_to_middle.dy)
    {      
      lx = (left->px  + 0xFFFF) >> 16;
      rx = (right->px + 0xFFFF) >> 16;
    
      sw32 width = rx - lx;
      if (width>0)
      {        
        cur_scanline_texture_func(screen_line,(lx<<1),&cps,width);        
      }
     
      //advance y down
      cur_y++;

      cps.ooz += oozystep;
      cps.soz += sozystep;
      cps.toz += tozystep;      

      top_to_middle.px  += top_to_middle.dxdy_fixed;
      top_to_bottom.px  += top_to_bottom.dxdy_fixed;
      
      top_to_middle.dy--;
      top_to_bottom.dy--;
            
      screen_line += r1_software_render_buffer_wpl;
    }    
  }
  
  if (mid_to_bottom.dy)
  {
    if (edge_comp)
    {
      left  = &top_to_bottom;
      right = &mid_to_bottom;
    }
    else
    {
      left  = &mid_to_bottom;
      right = &top_to_bottom;
    }

    sw32 lx,rx;

    lx = (left->px  + 0xFFFF) >> 16;
    rx = (right->px + 0xFFFF) >> 16;

    if (left != last_left)
    {
      //switched edges, recalc these values
      cps.ooz = cur_grads.oozat00 + ((float)cur_y*cur_grads.doozdy) + ((float)lx * cur_grads.doozdx);
      cps.soz = cur_grads.sozat00 + ((float)cur_y*cur_grads.dsozdy) + ((float)lx * cur_grads.dsozdx);
      cps.toz = cur_grads.tozat00 + ((float)cur_y*cur_grads.dtozdy) + ((float)lx * cur_grads.dtozdx);      

      oozystep = cur_grads.doozdy + cur_grads.doozdx * left->dxdy;
      sozystep = cur_grads.dsozdy + cur_grads.dsozdx * left->dxdy;
      tozystep = cur_grads.dtozdy + cur_grads.dtozdx * left->dxdy;
    }    
    
    while (mid_to_bottom.dy)
    {      
      lx = (left->px  + 0xFFFF) >> 16;
      rx = (right->px + 0xFFFF) >> 16;
    
      sw32 width = rx - lx;
      if (width>0)
      {        
        cur_scanline_texture_func(screen_line,(lx<<1),&cps,width);        
      }
     
      //advance y down
      cur_y++;

      cps.ooz += oozystep;
      cps.soz += sozystep;
      cps.toz += tozystep;      

      top_to_bottom.px  += top_to_bottom.dxdy_fixed;
      mid_to_bottom.px  += mid_to_bottom.dxdy_fixed;
      
      top_to_bottom.dy--;
      mid_to_bottom.dy--;
                  
      screen_line += r1_software_render_buffer_wpl;
    }
  }
}

void span_draw_perspective_lit(span_tri_info *tri)
{      
  r1_software_class_instance.set_color_tint(tri->color_tint);

  //setup some of the global variables
  r1_software_texture_ptr    = tri->texture;
  r1_software_twidth_log2    = tri->twidth_log2;
  r1_software_texture_width  = tri->texture_width;
  r1_software_texture_height = tri->texture_height;
  cur_grads                  = tri->grads;
    
  s_mask = ((r1_software_texture_width -1)<<16) | 0xFFFF;
  t_mask = ((r1_software_texture_height-1)<<16) | 0xFFFF;
  
  //dont forget the light
  dldx_fixed = qftoi(tri->grads.dldx);

  span_entry *s = &global_span_list[tri->span_list_head];

  perspective_span left;  
  while (s!=global_span_list)
  {    
    left.ooz = s->s.ooz;

#ifndef USE_ASM
    float fx = s->x;
    float fy = s->y;
    
    left.toz = cur_grads.tozat00 + (fx * cur_grads.dtozdx) + (fy * cur_grads.dtozdy);
    left.soz = cur_grads.sozat00 + (fx * cur_grads.dsozdx) + (fy * cur_grads.dsozdy);
    left.l   = qftoi(cur_grads.lat00 + (fx * cur_grads.dldx) + (fy * cur_grads.dldy));
#else
    _asm
    {
      mov edi,dword ptr [s]
      
      fild dword ptr [edi]span_entry.s.x
      fild dword ptr [edi]span_entry.s.y

      fld dword ptr [cur_grads]tri_gradients.dtdx
      fld dword ptr [cur_grads]tri_gradients.dtdy

      //dtdy  dtdx  y   x
      fmul st(0),st(2)
      fxch st(1)

      //dtdx  dtdy*y  y  x

      fmul st(0),st(3)
      fxch st(1)

      //dtdy*y  dtdx*x  y  x

      fld dword ptr [cur_grads]tri_gradients.dsdx
      fld dword ptr [cur_grads]tri_gradients.dsdy

      //dsdy  dsdx  dtdy*y  dtdx*x  y  x

      fmul st(0),st(4)
      fxch st(1)

      //dsdx  dsdy*y  dtdy*y  dtdx*x  y  x

      fmul st(0),st(5)
      fxch st(1)

      //dsdy*y  dsdx*x  dtdy*y  dtdx*x  y  x
      fld dword ptr [cur_grads]tri_gradients.dldx
      fld dword ptr [cur_grads]tri_gradients.dldy

      //dldy  dldx  dsdy*y  dsdx*x  dtdy*y  dtdx*x  y  x

      fmul st(0),st(6)
      fxch st(1)

      //dldx  dldy*y  dsdy*y  dsdx*x  dtdy*y  dtdx*x  y  x

      fmul st(0),st(7)
      fxch st(4)
      
      //dtdy*y  dldx*x  dsdy*y  dsdx*x  dldy*y  dtdx*x  y  x
      
      fadd dword ptr [cur_grads]tri_gradients.tat00
      fxch st(2)

      //dsdy*y  dldx*x  dtdy*y+tat00  dsdx*x  dldy*y  dtdx*x  y  x

      fadd dword ptr [cur_grads]tri_gradients.sat00
      fxch st(4)

      //dldy*y  dldx*x  dtdy*y+tat00  dsdx*x  dsdy*y+sat00  dtdx*x  y  x

      fadd dword ptr [cur_grads]tri_gradients.lat00
      fxch st(2)

      //dtdy*y+tat00  dldx*x  dldy*y+lat00  dsdx*x  dsdy*y+sat00  dtdx*x  y  x
      faddp st(5),st(0)

      //dldx*x  dldy*y+lat00  dsdx*x  dsdy*y+sat00  dtdx*x+dtdy*y+tat00  y  x
      fxch st(3)

      //dsdy*y+sat00  dldy*y+lat00  dsdx*x  dldx*x  dtdx*x+dtdy*y+tat00  y  x
      faddp st(2),st(0)

      //dldy*y+lat00  dsdx*x+dsdy*y+sat00  dldx*x  dtdx*x+dtdy*y+tat00  y  x
      faddp st(2),st(0)

      //dsdx*x+dsdy*y+sat00  dldx*x+dldy*y+lat00  dtdx*x+dtdy*y+tat00  y  x
      fxch st(3)
      
      //y  dldx*x+dldy*y+lat00  dtdx*x+dtdy*y+tat00  dsdx*x+dsdy*y+sat00  x
      fstp st(0)
      fxch st(3)
      
      //x  dtdx*x+dtdy*y+tat00  dsdx*x+dsdy*y+sat00  dldx*x+dldy*y+lat00
      fstp st(0)

      fstp dword ptr [left]perspective_span.toz
      fstp dword ptr [left]perspective_span.soz
      fistp dword ptr [left]perspective_span.l
    }
#endif

    cur_scanline_texture_func(s->s.scanline_ptr,(s->s.x<<1),&left,s->s.width);    
    s = &global_span_list[s->s.next_tri_span];
  }
}

void span_draw_perspective_unlit(span_tri_info *tri)
{      
  //setup some of the global variables
  r1_software_texture_ptr    = tri->texture;
  r1_software_twidth_log2    = tri->twidth_log2;
  r1_software_texture_width  = tri->texture_width;
  r1_software_texture_height = tri->texture_height;
  cur_grads                  = tri->grads;

  span_entry *s = &global_span_list[tri->span_list_head];

  s_mask = ((r1_software_texture_width -1)<<16) | 0xFFFF;
  t_mask = ((r1_software_texture_height-1)<<16) | 0xFFFF;

  perspective_span left;  
  while (s!=global_span_list)
  {    
    float fx = s->s.x;
    float fy = s->s.y;
    
    left.ooz = s->s.ooz;
    left.toz = tri->grads.tozat00 + (fx * tri->grads.dtozdx) + (fy * tri->grads.dtozdy);
    left.soz = tri->grads.sozat00 + (fx * tri->grads.dsozdx) + (fy * tri->grads.dsozdy);
                                     
    cur_scanline_texture_func(s->s.scanline_ptr,(s->s.x<<1),&left,s->s.width);    
    s = &global_span_list[s->s.next_tri_span];
  }
}