

#include "render_worker.h"

#include <fstream.h>
#include <stdlib.h>
#include <unistd.h>

#include "jllib_include.h"

#include "universal_threads.h"
#include "render_thread.h"
#include "cyclestate.h"
#include "globalstate.h"
#include "reconmap.h"
#include "prim.h"
#include "kdtree.h"
#include "raycastnode.h"
#include "glwin.h"
#include "fincident.h"

#include "rwlog.h"

int Render_Worker::_count = 0;

RayCache   * Render_Worker::_ray_cache= NULL;
FrameID Render_Worker::_frame_id = 0; 
FrameID Render_Worker::_sub_frame_id = 0; 
int Render_Worker::_window_width = 0;
int Render_Worker::_window_height = 0;
FrameReq Render_Worker::_frame_req;
int Render_Worker::_frustum_casting = 0;

extern GLWin *debugging_window;

#define STORE_PIXEL(x,y,r,g,b,confirmed) CS._samplebuffer->SetRealSample(confirmed, Render_Worker::_frame_id, Render_Worker::_sub_frame_id, Render_Worker::_frame_req._age, x, y, r, g, b, *CS._pixelbuffer); if ( (truly_GS->_disabled_flags2 & ENABLE_THROW_BAD_AREA_RATIO_TO_GL) || (truly_GS->_disabled_flags2 & ENABLE_THROW_BAD_HIT_RATIO_TO_GL) ) CS._samplebuffer->AddDepthSample (x,y, rayinfo->objhit.hitcell ? CS._camera.NormalizeZ (rayinfo->objhit.t) : 1.0);

void Render_Worker::freeCache (void)
{
  delete[] _ray_cache;
  _ray_cache = NULL;
  _window_width = _window_height = 0;
}

void Render_Worker::newWindowSize (int newx, int newy)
{
  if ((newx == _window_width) && (newy == _window_height))
    return;
  _window_width = newx;
  _window_height = newy;

  delete[] _ray_cache;
  cerr << "RayCache struct is of size " << sizeof (RayCache) << endl;
  cerr << "window is " << newx << " by " << newy << ", thus " << newx * newy << endl;
  cerr << "making new ray cache of size " << (sizeof (RayCache) * newx * newy) << endl;
  _ray_cache = new RayCache [newx * newy];

  RayCache *current = _ray_cache;

  int x, y;
  for (x = 0; x < newx; x++)
    {
      for (y = 0; y < newy; y++)
	{
	  current->frameID = -1;
	  current++;
	}
    }
}


Render_Worker::Render_Worker() {

	cerr << "error: Render_Worker constructor called!" << endl;
	exit(-1);
}

Render_Worker::Render_Worker(Render_Thread *p) : _pool (NULL), _lp_mem (new LPMem())  {

	cerr << "Render_Worker constructor!" << endl;

	_parent = p;
	_exit = 0;

	cerr << "setting skipped to 0" << endl;
	_skipped = 0;
	_id = _count++;
}

Render_Worker::~Render_Worker() {
  delete _lp_mem;
  _local_work_queue.Clear();
	cerr << "Render_Worker destructor!" << endl;
}

Render_Worker& Render_Worker::CopyFrom(const Render_Worker &) {

	cerr << "Error: Render_Worker::CopyFrom() called" << endl;
	exit(-1);

	return *this;
}

/*
ostream& operator<<(ostream &co, const Render_Worker &R) {

	return co;
}

istream& operator>>(istream &ci, Render_Worker &R) {

	return ci;
}
*/


#if 0
int Render_Worker::SubdivideWork(WorkRec *WR, CycleState &CS) {

  if (WR->_frustum.XSize() <= 3)
    return 0;

  WorkRec *WR1 = new WorkRec(WR->_frustum.GetCamera(), 0, CS._plane_cache, &CS, WR, WR->_cur_cell);
  WorkRec *WR2 = new WorkRec(WR->_frustum.GetCamera(), 0, CS._plane_cache, &CS, WR, WR->_cur_cell);
  WorkRec *WR3 = new WorkRec(WR->_frustum.GetCamera(), 0, CS._plane_cache, &CS, WR, WR->_cur_cell);
  WorkRec *WR4 = new WorkRec(WR->_frustum.GetCamera(), 0, CS._plane_cache, &CS, WR, WR->_cur_cell);
  
  WR->Subdivide (WR1, WR2, WR3, WR4, CS);

#ifdef DEBUG_FRUSTUM_CULLING
        if (subd == OKLEVEL)
        {
          int x, y;
          if (WR1)
            {
              x = (real)OKWIDTH * (real)WR1->_frustum.X_left() / 256.0f;
              y = (real)OKWIDTH * (real)(WR1->_frustum.Y_bottom()) / 256.0f;
              okarray[y * OKWIDTH + x] = WR1->_incident->Size();
            }
          if (WR2)
            {
              x = (real)OKWIDTH * (real)WR2->_frustum.X_left() / 256.0f;
              y = (real)OKWIDTH * (real)(WR2->_frustum.Y_bottom()) / 256.0f;
              okarray[y * OKWIDTH + x] = WR2->_incident->Size();
            }
          if (WR3)
            {
              x = (real)OKWIDTH * (real)WR3->_frustum.X_left() / 256.0f;
              y = (real)OKWIDTH * (real)(WR3->_frustum.Y_bottom()) / 256.0f;
              okarray[y * OKWIDTH + x] = WR3->_incident->Size();
            }
          if (WR4)
            {
              x = (real)OKWIDTH * (real)WR4->_frustum.X_left() / 256.0f;
              y = (real)OKWIDTH * (real)(WR4->_frustum.Y_bottom()) / 256.0f;
              okarray[y * OKWIDTH + x] = WR4->_incident->Size();
            }
        }
#endif //debug_frustum

	if (WR1) {_local_work_queue.AddWorkRec(WR1); }
	if (WR2) {_local_work_queue.AddWorkRec(WR2);}
	if (WR3) {_local_work_queue.AddWorkRec(WR3);}
	if (WR4) {_local_work_queue.AddWorkRec(WR4);}

	return 1;
}

#endif

//if we're given a givenhit.obj, we assume that's conclusive
//otherwise, 
//  if kdtreestartcell is NULL, we take it as conclusive
//  otherwise we keep shooting the ray through.

void Render_Worker::GetObjHit (int rwnum, WorkRec *WR, const Ray &ray, int x, int y, CycleState &CS, _KDTreeNode *kdtreestartcell, Hit *givenhit)
{
  RayCache *rayinfo = GET_RAY_INFO_PTR (x, y);
  
  //in f-c, this fxn is only be called once per pixel per frame; after that, just shade.
  //but in non-FC, we're lazy.
  if (!(truly_GS->_disabled_flags & DISABLE_FC))
    if (_sub_frame_id != 0 || rayinfo->frameID >= _frame_id)
      {
	cerr << "getObjHit called twice, perhaps, or out of order!" << endl;
	cerr << x << ", " << y << endl;
	cerr << "sub_frame_id is " << _sub_frame_id << endl;
	cerr << "raycache id is " << rayinfo->frameID << ", cur id is " << _frame_id << endl;
      }
  
#ifdef ONE_THREAD
  _KDTreeNode *passedinstart = kdtreestartcell; //for debugging only
#endif
  _KDTreeNode *frustumstoppedcell = NULL;
  
  
  //  cerr << "init ray cache entry" << endl;
  InitRayCacheEntry (rayinfo, ray, rwnum);
  
  if (WR)
    {
      rayinfo->_rw_owned = WR->_rw_own;
      rayinfo->_frusta_num = WR->_f_num;
    }
  
  if (givenhit)
    {
      //		cerr << "taking the hit" << endl;
      TakeHit (x, y, rayinfo, givenhit, CS, givenhit->hitcell || (!kdtreestartcell));
      frustumstoppedcell = kdtreestartcell;
    } //if givenhit
  else
    {
      rayinfo->objhit.Clear();
    }
  
  rayinfo->_shaded = 0;

  rayinfo->curcolor.Set (0,0,0,1); 
  
 
  if (rayinfo->hitsomething == DUNNO)
    {
      rayinfo->_used_kdtree = 1;

#if 0
      //the general case. As if we're not going to use the kdtree.
      truly_GS->_scene.getObjs()->Intersect(rayinfo->ray, rayinfo->objhit);
#else
      static KDTree *kdtree = (KDTree *)(truly_GS->_scene.getObjs());

      frustumstoppedcell = kdtreestartcell;
      
      //      cerr << "calling kdtree, intersect" << endl;
      if (kdtreestartcell)
	{
	  int flags = 0;
	  if (truly_GS->_kddraw_flags & DRAW_OBJS_TOUCHED)
	    flags |= DRAW_OBJS_TOUCHED;
	  if (truly_GS->_kddraw_flags & DRAW_CELLS_ENTERED)
	    flags |= DRAW_CELLS_ENTERED;
	  
	  RWLOG (RW_UseKDTree_Start, new RW_UseKDTree_Start_Entry (ray, x, y,
							   kdtreestartcell, rayinfo->objhit));

	  kdtree->Intersect (rwnum, x, y, *kdtreestartcell, rayinfo->ray, rayinfo->objhit, flags, &CS);
	  RWLOG (RW_UseKDTree_End, new RW_UseKDTree_End_Entry (ray, x, y,
							   kdtreestartcell, rayinfo->objhit));

	}
      else
	{
	  //we missed the tree altogether!
#if 0
	  if (!kdtreestartcell)
	    cerr << "no start cell ";
	  else
	    cerr << "start cell started too late ";
	  cerr << " for " << x << ", " << y << endl;
#endif
	}
      //      cerr << "did kdtree intersect" << endl;
#endif //if 0

      if (rayinfo->objhit.obj)
	rayinfo->hitsomething = YES;
      else
	rayinfo->hitsomething = NO;
    }
  
  //tell this object it was visible, for statistics/visualization purposes
  if (rayinfo->objhit.hitcell)
    {
      rayinfo->objhit.obj->_obj_used_frame_id = _frame_id;
      rayinfo->objhit.obj->_num_visible_pixels++;
    }
    
#if defined (ONE_THREAD) && defined (SANITYCHECK)
  SanityCheck (x, y, rayinfo, frustumstoppedcell, kdtreestartcell, passedinstart, CS);
#endif 

  rayinfo->frameID = _frame_id; //set this at the end, so if it's
  //set, we know we have valid object intersection data
}

void Render_Worker::DoOneRayFC(int rwnum, WorkRec *WR, const Ray &ray, int x, int y, CycleState &CS, int confirm, int alltheway,
			       _KDTreeNode *next_cell)
{
  assert (WR);

  Hit hit;
  
  RWLOG (RW_DoOneRayFC_Start, new RW_DoOneRayFC_Start_Entry (WR, ray, x, y, alltheway, next_cell));
	 
  //if we have a hit, use it.
  if (WR->_got_total_hit)
    {
#ifndef NDEBUG
      //keep the counts good. We know that
      //we're going to do one intersect with this
      //object for shading purposes, and that it'll
      //succeed.
      if (WR->totalhit.obj)
	{
	  WorkRec::_frustum_straddler_intersect_calls++;
	  WorkRec::_frustum_straddler_intersect_success++;
	}
#endif

      DoOneRay (rwnum, WR, ray, x, y, CS, confirm, NULL, &WR->totalhit);
      return;
    }

  if (WR->_cur_cell) //if we're in a cell, check its contents first
    {
#ifndef NDEBUG
      //make sure the ray either starts in this cell or enters it.
      float worstt = 0.;
      int incell = WR->_cur_cell->_bounds.IncludesPoint (ray.R());

      if (!incell)
	{
	  int face, direction;
	  Vec4 nextpt;
	  int didenter = WR->_cur_cell->_bounds.whereEntered (ray, worstt, face, direction, nextpt);

	  if ( !didenter)
	    {
	      cerr << "ray didn't enter cur cell of frustum." << endl;
	      CS.AddErrorBbox (WR->_cur_cell->_bounds);
	      CS.AddErrorRay (ray, 1000);
	    }
	}
#endif
      
      //see if the ray hits any of the frustum objects
      WR->RayCast (ray, hit, x, y, 0);

#ifndef NDEBUG      
      if (hit.obj && (hit.t < worstt))
	{
	  cerr << "somehow we hit an object at " << hit.t << ", before we entered the current cell at " << worstt << endl;
	  cerr << x << ", " << y << endl;
	}
#endif

      //if the intersections aren't in the current cell, save 'em
      //for later.
      if (hit.obj)
	{
	  int face, direction;
	  float edget;
	  WR->_cur_cell->_bounds.whereLeft (ray, edget, face, direction);

	  //	  Vec4 pt = ray.R() + hit.t * ray.D();
	  //	  if (!WR->_cur_cell->_bounds.IncludesPoint (pt))

	  if (hit.t <= edget)
	    {
	      hit.hitcell = WR->_cur_cell;
	    }
	}
    } //WR->_cur_cell
  else
    {
      //no current cell
    }
  
  RWLOG (RW_DoOneRayFC_End, new RW_DoOneRayFC_End_Entry (hit, WR, ray, x, y, alltheway, next_cell));
	 
  if (alltheway || (hit.hitcell == WR->_cur_cell) || (!next_cell) || (truly_GS->_disabled_flags & DISABLE_KDTREE))
    {
      DoOneRay (rwnum, WR, ray, x, y, CS, confirm, next_cell, &hit);
    }
  else
    {
      if (confirm)
	CS._samplebuffer->Confirm (x, y, _frame_id, _sub_frame_id);
    }
}

void Render_Worker::DoOneRay(int rwnum, WorkRec *WR, const Ray &ray, 
			     int x, int y, CycleState &CS, int confirm, 
			     _KDTreeNode *kdtreestartcell, Hit *givenhit) {
  
  //  cerr << "top of dooneray" << endl;
  GetObjHit (rwnum, WR, ray, x, y, CS, kdtreestartcell, givenhit);
  //  cerr << "shading" << endl;
  Shade (x, y, CS, confirm);
  //  cerr << "bottom of dooneray" << endl;
}

void Render_Worker::Shade (int x, int y, CycleState &CS, int confirm)
{
        RayCache *rayinfo = GET_RAY_INFO_PTR (x, y);

	//make sure the data is current.
	assert (rayinfo->frameID == Render_Worker::_frame_id);
	assert (rayinfo->hitsomething != DUNNO);

	static RTLights &lights = truly_GS->_scene.GetLights();
	static int numlights = lights.NumTotalLights();
	int numactivelights = lights.NumLights();

	//if we're going to be shadowing, we need to recalc
	//the diffuse & specular component
	//XXX can't this be cleaner?
	//XXX we also need to recalc shadows 
	//when the # of active lights changes 
	//(when the headlight's turned on and off, for example).
	if ((_frame_req._shadow_depth > 0) && (!rayinfo->_shadow_done))
	  {
	    rayinfo->diffusecontrib.Set (0,0,0,-1); 
	    rayinfo->specularcontrib.Set (0,0,0,-1);
	  }

	//shading now...
	rayinfo->_shaded = 1;	

	//STYLE_KDTREE_USED and STYLE_NO_KDTREE are 
	//really filters. So I do them first, then let the
	//other guys go.
	if (truly_GS->_style_flags & STYLE_KDTREE_USED)
	  {
	    if (!rayinfo->_used_kdtree)
	      {
	      real col = 0.;
	      STORE_PIXEL (x,y, col,col,col, confirm);
	      return;
	      }
	  }

	if (truly_GS->_style_flags & STYLE_NO_KDTREE)
	  {
	    if (rayinfo->_used_kdtree)
	      {
	      real col = 0.;
	      STORE_PIXEL (x,y,col,col,col, confirm);
	      return;
	      }
	  }

	if (truly_GS->_style_flags & STYLE_SMALL_CELL_HIT)
	  {
	    if (rayinfo->objhit.hitcell && (rayinfo->objhit.hitcell->_actual.Area() >= truly_GS->_cf_cache_area_cutoff))
	      {
	      real col = 0.;
	      STORE_PIXEL (x,y,col,col,col, confirm);
	      return;
	      }
	  }

	if (truly_GS->_style_flags & STYLE_NOT_SMALL_CELL_HIT)
	  {
	    if (rayinfo->objhit.hitcell && (rayinfo->objhit.hitcell->_actual.Area() < truly_GS->_cf_cache_area_cutoff))
	      {
	      real col = 0.;
	      STORE_PIXEL (x,y,col,col,col, confirm);
	      return;
	      }
	  }
	
	if (truly_GS->_style_flags & STYLE_THREAD_OWNED)
	  {
	    float cr = .2, cg = .5, cb = .8;
	    for (int i = 0; i < rayinfo->_rw_owned; i++)
	      {	
		cr += 0.13579; if ( cr > 0.8f ) cr = 0.2f;
		cg += 0.19753; if ( cg > 0.8f ) cg = 0.2f;
		cb += 0.17391; if ( cb > 0.8f ) cb = 0.2f;
	      }
	    STORE_PIXEL (x,y,cr, cg, cb, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_THREAD_DID)
	  {
	    float cr = .2, cg = .5, cb = .8;
	    for (int i = 0; i < rayinfo->_rw_did; i++)
	      {	
		cr += 0.13579; if ( cr > 0.8f ) cr = 0.2f;
		cg += 0.19753; if ( cg > 0.8f ) cg = 0.2f;
		cb += 0.17391; if ( cb > 0.8f ) cb = 0.2f;
	      }
	    STORE_PIXEL (x,y,cr, cg, cb, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_OBJS_MISSED)
	  {
	    real col = (rayinfo->objhit.numObjIntersections() - rayinfo->objhit.num_intersect_successes) * .1;
	    STORE_PIXEL (x,y,col,col,col, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_OBJS_HIT)
	  {
	    real col = rayinfo->objhit.num_intersect_successes * .1;
	    STORE_PIXEL (x,y,col,col,col, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_BBOXES_MISSED)
	  {
	    real col = (rayinfo->objhit.num_bbox_successes) * .1;
	    STORE_PIXEL (x,y,col,col,col, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_CELLS_WALKED)
	  {
	    //I add one because we're displaying # cells *inspected* by the
	    //k-d tree, which includes the start cell, and every cell we
	    //walked into
	    real col = 0.;
	    if (rayinfo->_used_kdtree)
	      col = (1 + rayinfo->objhit.num_kdcells_walked_by_kdtree) * .1;
	    STORE_PIXEL (x,y,col,col,col, confirm);
	    return;
	  }
	if (truly_GS->_style_flags & STYLE_DEPTH_COMPLEXITY)
	  {
	    int complexity = 0;
	    if (rayinfo->hitsomething == YES)
	      {
		//we subtract because, if we hit an object, we definitely
		//had to do one intersection to get the distance for lighting
		//purposes. The question is whether we had to do any *more* 
		//than that.

		if (truly_GS->_style_flags & STYLE_DEPTH_COMPLEXITY_W_BBOX)
		  {
		    complexity = rayinfo->objhit.numIntersections() - 1;
		    if (complexity <= 2) complexity = 0;
		  }
		else
		  {
		    complexity = rayinfo->objhit.numObjIntersections() - 1;
		    assert (complexity >= 0);
		  }
	      }
	    real col = complexity * .1;
	    STORE_PIXEL (x,y,col,col,col, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_PIXELS_DONE)
	  {
	    real col = 1.;

	    STORE_PIXEL (x,y,col,col,col, confirm);
	    return;
	  }

	if (truly_GS->_style_flags & STYLE_CELL_HIT)
	  {
	    if (rayinfo->objhit.obj)
	      {
		Vec4 color = rayinfo->objhit.hitcell->_debug_color;
		STORE_PIXEL (x,y, color.r(), color.g(), color.b(), confirm);
	      }
	    else
	      {
		STORE_PIXEL (x,y, 0., 0., 0., confirm);
	      }
	      return;
	  }
	
	if (truly_GS->_style_flags & STYLE_OBJCELL)
	  {
	    if (rayinfo->objhit.obj)
	      {
		ObjInfo *oi = rayinfo->objhit.hitcell->FindObjInfo (rayinfo->objhit.obj);
		Vec4 color = oi->_debug_color;
		STORE_PIXEL (x,y, color.r(), color.g(), color.b(), confirm);
	      }
	    else
	      {
		STORE_PIXEL (x,y, 0., 0., 0., confirm);
	      }
	      return;
	  }

	int diddiffuse = 0;
	int didspecular = 0;
	if (rayinfo->hitsomething == YES)
	  {
	    assert (rayinfo->objhit.obj);
	    assert (rayinfo->objhit.t < MAXFLOAT);
	    assert (rayinfo->objhit.t >= 0.);
	    assert (rayinfo->objhit.hitcell);
	    
	    if (rayinfo->P.w() < 0.)
	      {
		//P = ray origin (R) + ray direction (D) * hit (t)
		Vec4FastAddScale(rayinfo->P, rayinfo->ray.R(), rayinfo->ray.D(), rayinfo->objhit.t);
		rayinfo->P.set_w (1.);
	      }

	    if (rayinfo->colortexlevel != _frame_req._texturing) {
		rayinfo->objhit.obj->GetColor(rayinfo->P, rayinfo->objcolor, rayinfo->objhit, _frame_req._texturing);
		rayinfo->colortexlevel = _frame_req._texturing;
	      }

	    //XXX no lighting: just take curcolor;
	    rayinfo->curcolor = rayinfo->objcolor.diffuse;
	  	    
	    if ((_frame_req._do_specular || _frame_req._do_ambient || _frame_req._do_diffuse) && (numactivelights > 0)) {
	      if (!rayinfo->objhit.normalcalced)
		{
		  //calc the normal 
		  rayinfo->objhit.obj->NormalAtPoint(rayinfo->P, rayinfo->objhit.normal);
		  rayinfo->objhit.normalcalced = 1;
		  
		  //perturb the point!
		  //		  Vec4FastAddScale (rayinfo->P, rayinfo->P, rayinfo->objhit.normal, drand48() * truly_GS->_translational_velocity);
		}
	      
	      Vec4 color_tmp (0,0,0,0);

	      if (rayinfo->R.w() < 0.0)
		{
		  // R = E - 2 N.E * N
		  rayinfo->R = rayinfo->ray.D() - ((2 * rayinfo->objhit.normal.Dot3 (rayinfo->ray.D())) * rayinfo->objhit.normal);
		  rayinfo->R.set_w(1.);
		}
	      
	      for (int i = 0; i < numlights; i++)
		{
		  if (!lights[i]._active) continue; //skip disabled lights

		  if (rayinfo->toL[i].w() < 0.)
		    {
		      if (lights[i]._type == Directional)
			{
			  rayinfo->toL[i] = lights[i]._direction;
			  rayinfo->toL[i].Negate();
			  rayinfo->distToLight [i] = MAXFLOAT;
			}
		      else if (lights[i]._type == Point)
			{
			  //get vec from obj point to light point
			  Vec4FastSub (rayinfo->toL[i], lights[i]._direction, rayinfo->P);
			  rayinfo->distToLight[i] = rayinfo->toL[i].Length(); //store length to light
			  rayinfo->toL[i] /= rayinfo->distToLight[i]; //normalize the tolight vector
			}
		      else
			{
			  assert (1 == 0); //unknown light type!
			}
		      rayinfo->toL[i].set_w(1.);
			  
		      assert (rayinfo->objhit.normalcalced);
		      rayinfo->NdotL[i] = rayinfo->objhit.normal.Dot3(rayinfo->toL[i]);
		    }
		  
		  if (_frame_req._shadow_depth > 0)
		    {
		      if (rayinfo->shadowed[i] == DUNNO) 
			{
			  assert (rayinfo->objhit.obj);
			  assert (rayinfo->P.w() > 0.);
			  assert (rayinfo->distToLight[i] > 0.);
			  assert (rayinfo->toL[i].w() > 0.);
			  
			  rayinfo->shadowed[i] = InShadow (rayinfo->P, i, rayinfo->objhit.obj, rayinfo->distToLight [i], rayinfo->toL[i], CS) ? YES: NO;
			}
		     
		      if (rayinfo->shadowed[i] == YES)
			{
			  continue;
			}
		    }
		  
		  Vec4 lcolor = lights[i]._color * lights[i]._intensity;

		  real visibility = 1.0;
		  if (lights[i]._type == Point)
		    visibility *= DistanceAtten (rayinfo->distToLight[i]);
		  
		  if ((_frame_req._do_diffuse) && (rayinfo->diffusecontrib.w() < 0.))
		    {
		      //sum up the light * V * N.L's. Later we'll multiply by
		      //obj color
		      if (rayinfo->NdotL[i] > 0.)
			{
			  rayinfo->diffusecontrib += lcolor * rayinfo->NdotL[i] * visibility;
			}
		      diddiffuse = 1;
		    }
		  
		  if ((_frame_req._do_specular) && (rayinfo->specularcontrib.w() < 0.))
		    {
		      //collect the light * visibility * N.H terms; later we'll
		      //multiply by the object color
		      if (rayinfo->toV.w() < 0.)
			{
			  rayinfo->toV = rayinfo->ray.R() - rayinfo->P;
			  rayinfo->toV.MakeUnit();
			  rayinfo->toV.set_w (1.);
			}
		      
		      assert (rayinfo->R.w() > 0.);
		      
		      Vec4 H;
		      //H = V+L, unit vector
		      Vec4FastAdd (H, rayinfo->toV, rayinfo->toL[i]);
		      H.MakeUnit();
		      float NDotH = rayinfo->objhit.normal.Dot3 (H);
		      
		      if (NDotH > 0.)
			{
			  real n = 256.0 * rayinfo->objcolor.shininess;
#ifdef JLLIB_IRIX
			  NDotH = powf (NDotH, n);
#else
			  NDotH = pow (NDotH, n);
#endif
			  assert (NDotH >= 0.);
			  
			  rayinfo->specularcontrib += lcolor * visibility * NDotH;
			}
		      //can't set specularcontrib.w here because then
		      //we wouldn't calculate specular for the rest
		      //of the lights, thinking we'd already done it.
		      //so I set this other variable, didspecular, which
		      //tells me that when I'm totally done lighting,
		      //I should set specularcontrib.w. 
		      didspecular = 1;
		    } //if specular light
		  
		} //for each light
	      
	      //remember what we did
	      if (didspecular) 
		rayinfo->specularcontrib.set_w(1.);
	      
	      if (diddiffuse) 
		rayinfo->diffusecontrib.set_w(1.);
	      
	      if (_frame_req._shadow_depth > 0)
		rayinfo->_shadow_done = 1; 

	      if (_frame_req._do_ambient)
		{
		  rayinfo->curcolor = lights._ambient * rayinfo->objcolor.ambient ;
		}
	      else
		{
		  rayinfo->curcolor.Set (0,0,0);
		}
	      
	      if (rayinfo->diffusecontrib.w() > 0.)
		{
		  //		  float disttoeye = (rayinfo->P - CS._camera.R()).Length();
		  //		  float pval = drand48() * truly_GS->_rotational_velocity;
		  float pval = 0;
		  Vec4 perturb (pval, pval, pval);
		  rayinfo->curcolor += rayinfo->diffusecontrib * (rayinfo->objcolor.diffuse - perturb);
		}
	      
	      
	      if (rayinfo->specularcontrib.w() > 0.)
		{
		  rayinfo->curcolor += rayinfo->specularcontrib * rayinfo->objcolor.specular;
		}
	      
	      //amb light * amb object + attenuation * diffuse light color * (ks * object diffuse * NdotL) + specular light color * ks * object specular * NdotH);
	    } //if lighting is on
	    
	  } //if we hit something
	

	//keep the true color around; just clamp for display.
	Vec4 clamped = rayinfo->curcolor;
	clamped.Clamp (0., 1.);
	
	ReconNode *tryme = CS._samplebuffer->Get (x,y);
	if ((tryme->_frame_id == _frame_id) && (tryme->_sub_frame_id == _sub_frame_id))
	  assert (0);

	//		cerr << "storing" << endl;
	STORE_PIXEL (x, y, clamped.r(), clamped.g(), clamped.b(), confirm);
	//		cerr << "storing done" << endl;
}


void Render_Worker::WorkNotFC (int rwnum, WorkRec *current_work)
{
	assert (current_work);

	CycleState &CS = *(_parent->_current_CS);

	int center_x = (current_work->_frustum.X_left() +
			current_work->_frustum.X_right()) >> 1;

	int center_y = (current_work->_frustum.Y_bottom() +
			current_work->_frustum.Y_top()) >> 1;


	Vec4 origin = CS._camera.R();
	Ray ray (origin, Vec4 (1,0,0));
	float t;
	static KDTree *kdtree = (KDTree *)(truly_GS->_scene.getObjs());
	
	//cache the start cell for each frame
	static int startcellframeid = -1;
	static _KDTreeNode *startcell = NULL;
	
	if (startcellframeid != _frame_id) //have we figured it out for this frame yet?
	  {
	    //do so
	    startcell = NULL;
	    if (kdtree->bbox().IncludesPoint (origin))
	      {
		startcell = kdtree->FindStartCell (ray, t);
	      }
	    startcellframeid = _frame_id;
	  }
	
#define SET_RAY_ID(x,y) (ray.SetID (y * truly_GS->_x_winsize + x))
	
	//only do the left & bottom if they're on the edge
	int left = current_work->_frustum.X_left();
	if (left == 0)
	  {
	    ray.SetD (CS._plane_cache->ComputeEyeRayDirection (left, center_y));
	    SET_RAY_ID (left, center_y);
	    
	    RWLOG (RW_Work_CastRay, new RW_Work_CastRay_Entry (current_work, ray, left, center_y, 1));

	    DoOneRay (rwnum, current_work, ray , left, center_y, CS, 1, startcell ? startcell : kdtree->FindStartCell (ray, t));
	    _rays_cast++;
	  }
	else
	  {
	    RWLOG (RW_Work_ConfirmRay, new RW_Work_ConfirmRay_Entry (current_work, left, center_y));
	    CS._samplebuffer->Confirm (left,center_y, CS._frame_id, CS._sub_frame_id);
	  }

	//y's go up from the bottom. 
	int lowy = current_work->_frustum.Y_bottom();
	if (lowy == 0)
	  {
	    ray.SetD (CS._plane_cache->ComputeEyeRayDirection (center_x, lowy));
	    SET_RAY_ID (center_x, lowy);
	    RWLOG (RW_Work_CastRay, new RW_Work_CastRay_Entry (current_work, ray, center_x, lowy, 1));
	    DoOneRay (rwnum, current_work, ray, center_x, lowy, CS, 1, startcell ? startcell : kdtree->FindStartCell (ray, t));
	    _rays_cast++;
	  }
	else
	  {
	    RWLOG (RW_Work_ConfirmRay, new RW_Work_ConfirmRay_Entry (current_work, center_x, lowy));
	    CS._samplebuffer->Confirm (center_x, lowy, CS._frame_id, CS._sub_frame_id);
	  }
	  
	//we are responsible for actually doing the right & top pix
	int totalsize = CS._samplebuffer->Size();

	int confirm = 0;
	int highy = current_work->_frustum.Y_top();
	if (highy == (totalsize-1))
	  confirm = 1;
	ray.SetD (CS._plane_cache->ComputeEyeRayDirection (center_x, highy));
	SET_RAY_ID (center_x, highy);
	RWLOG (RW_Work_CastRay, new RW_Work_CastRay_Entry (current_work, ray, center_x, highy, confirm));
	DoOneRay (rwnum, current_work, ray, center_x, highy, CS, confirm, startcell ? startcell : kdtree->FindStartCell (ray, t));
	_rays_cast++;

	confirm = 0;
	int right = current_work->_frustum.X_right();
	if (right == (totalsize - 1))
	  confirm = 1;

	ray.SetD (CS._plane_cache->ComputeEyeRayDirection (right, center_y));
	SET_RAY_ID (right, center_y);
	RWLOG (RW_Work_CastRay, new RW_Work_CastRay_Entry (current_work, ray, right, center_y, confirm));
	DoOneRay (rwnum, current_work, ray, right, center_y, CS, confirm, startcell ? startcell : kdtree->FindStartCell (ray, t));
	_rays_cast++;

	ray.SetD (CS._plane_cache->ComputeEyeRayDirection (center_x, center_y));
	SET_RAY_ID (center_x, center_y);
	RWLOG (RW_Work_CastRay, new RW_Work_CastRay_Entry (current_work, ray, center_x, center_y, 1));
	DoOneRay (rwnum, current_work, ray, center_x,center_y, CS, 1, startcell ? startcell : kdtree->FindStartCell (ray, t));
	_rays_cast++;

	if ( (truly_GS->_show_middle_ray_info) && (current_work->_subd_count == 0))
	  {
	    cerr << "-------" << endl;

	    RayCache *rayinfo = GET_RAY_INFO_PTR (center_x, center_y);
		
	    cerr << "ray going thru " << center_x << ", " << center_y << " checked " << rayinfo->objhit.num_sphere_intersections << " spheres, " <<
	      rayinfo->objhit.num_box_intersections << " boxes, and " <<
	      rayinfo->objhit.num_polygon_intersections << " polygons." << endl;
	    cerr << "it did skip over " << rayinfo->objhit.num_straddlers_skipped << " already-checked straddlers, but checked " << rayinfo->objhit.num_straddlers_checked_twice << " twice or more " << endl;
	  } //show middle ray info
	
}

int Render_Worker::Work(int rwnum, WorkRec *current_work) {

	assert (current_work);
	
	RWLOG (RW_Work_Start, new RW_Work_Start_Entry (current_work));
	
	int numskipped = 0;
	if (!_frustum_casting)
	  {
	    WorkNotFC (rwnum, current_work);
	  }
	else
	  {
	    CycleState &CS = *(_parent->_current_CS);

	    numskipped = current_work->Work(rwnum, _rays_cast, CS, &_local_work_queue);

	//	if (numskipped)
	    //	  cerr << "skipped " << numskipped << endl;
	//if it made kids, throw 'em on the queue.
	//	cerr << "adding kids to self" << endl;
	/*	if ((_sub_frame_id == 0) && (current_work->_kids[0]))
	  {
	    current_work->_kids[0]->SetOwner (_local_work_queue.Size(), _id);
	    _local_work_queue.AddWorkRec (current_work->_kids[0]);
	    current_work->_kids[1]->SetOwner (_local_work_queue.Size(), _id);
	    _local_work_queue.AddWorkRec (current_work->_kids[1]);
	    current_work->_kids[2]->SetOwner (_local_work_queue.Size(), _id);
	    _local_work_queue.AddWorkRec (current_work->_kids[2]);
	    current_work->_kids[3]->SetOwner (_local_work_queue.Size(), _id);
	    _local_work_queue.AddWorkRec (current_work->_kids[3]);
	  }
	  */
	    //	cerr << "done adding kids to self" << endl;
	  }
	
	RWLOG (RW_Work_End, new RW_Work_End_Entry (current_work));
	return numskipped;
}


void Render_Worker::WorkLoop(int threadless) {

  WorkQueue *queue;
  int startrec;
  int numrecstodo;

  if (threadless) //one-threaded version - far simpler. this render worker owns it all!
    {
      if (_exit) {delete this; return;}
      
      _rays_cast = 0;

      //      cerr << "--- worker " << _id << " starting work" << endl;
      
      while (queue = _parent->GimmeWork (this, startrec, numrecstodo))
	{
	  //	  cerr << "render thread " << _id << " got " << numrecstodo << " recs." << endl;
	  assert (numrecstodo > 0);
	  for (; numrecstodo > 0; numrecstodo--)
	    {
	      _skipped += Work (_id, (*queue)[startrec++]);
	    }
	  //	  cerr << "render thread " << _id << " asking for more." << endl;
	}
	
      return;

    }
  
  //threaded version
  while (1) {

    WaitToStart();

    _rays_cast = 0;
    
    if (_exit) //end of program!
      return;
    
    //    cerr << "--- worker " << _id << " starting work" << endl;
    
    
    //parent returns 1 when it's time for us to stop
    while (queue = _parent->GimmeWork (this, startrec, numrecstodo))
      {
	//	cerr << "render thread " << _id << " got " << numrecstodo << " recs." << endl;
	  assert (numrecstodo > 0);
	  assert (startrec >= 0);
	  assert (startrec + numrecstodo <= queue->Size());
	for (; numrecstodo > 0; numrecstodo--)
	  {
	    _skipped += Work (_id, (*queue)[startrec++]);
	  }
	//	cerr << "render thread " << _id << " asking for more." << endl;

      }

    //    cerr << "--- worker " << _id << " ending work" << endl;

    Finish();

  } //while 1
}


UNIV_THREAD_RETURN_TYPE Render_Worker::_Do_Work_Proc(void *arg) {

	cerr << "Render_Worker starting" << endl;

	Render_Worker *This = (Render_Worker*) arg;

	This->WorkLoop();

	cerr << "Render_Worker exiting" << endl;

	delete This;

	UNIV_THREAD_RETURN_COMMAND;
}



int Render_Worker::InShadow (const Vec4 &pt, int i, RayCastable *obj, RCFloat lightt, const Vec4 &tolight, CycleState &CS)
{
 
  //shoot ray at light
  //if we hit anything before the light, we're in shadow.
  //  cerr << "pt is " << pt << endl;

  //the T_FUDGE avoids hitting the object we're bouncing off of. Really,
  //it works.
  Ray ray (pt + tolight * T_FUDGE, tolight);

  RayCastable *cached_obj_ = obj->GetShadowCache(i);
  Hit hit;

  hit.t = lightt; //it'll reject anything beyond this, see.

  //try the cached object
  if (cached_obj_)
    {
      cached_obj_->Intersect (ray, hit);
      if (hit.obj) 
	{
	  assert (hit.t < lightt);
	  CS._cache_hit_shadow_tests++;
	  return 1;
	}
    }

  //try the rest
  truly_GS->_scene.getObjs()->Intersect(ray, hit);

  if (hit.obj)
    {
      assert (hit.t < lightt);
      CS._full_shadow_tests++;

      //if the cache is empty, set it.
      if (!cached_obj_)
	obj->SetShadowCache (i, hit.obj);
      return 1;
    }

  CS._gratuitous_full_shadow_tests++;

  return 0;
}


void Render_Worker::InitRayCacheEntry (RayCache *rayinfo, const Ray &ray, int rwnum)
{
  rayinfo->ray = ray;
  
  rayinfo->_rw_did = rwnum;
  
  rayinfo->_rw_owned = 0;
  rayinfo->_frusta_num = 0;
  
  rayinfo->hitsomething = DUNNO;
  rayinfo->diffusecontrib.Set (0,0,0,-1);  // SJT -- why? it is set unconditionally below
  rayinfo->specularcontrib.Set (0,0,0,-1); // JST -- why? it is set unconditionally below
  rayinfo->toV.set_w (-1.);
  rayinfo->P.set_w (-1.);
  rayinfo->colortexlevel = 0; //if this != the current tex level, we ask the object for its color. curtexlevel can never equal 0, so this makes it query the first time.
  
  rayinfo->_shadow_done = 0;
  
  rayinfo->_used_kdtree = 0;
  rayinfo->R.set_w(-1.);

  static RTLights &lights = truly_GS->_scene.GetLights();
  static int numlights = lights.NumTotalLights();

  for (int k = 0; k < numlights; k++) // SJT -- fixed
    {
      rayinfo->toL[k].set_w(-1.);
      rayinfo->distToLight[k] = 0;
      rayinfo->NdotL[k] = 0.;
      rayinfo->shadowed[k] = DUNNO;
    }
}

void Render_Worker::TakeHit (int x, int y, RayCache *rayinfo, Hit *givenhit, CycleState &CS, int conc)
{
  rayinfo->objhit = *givenhit;

  if (conc) //conclusively the hit, or just a record of work done so far?
    {
      rayinfo->_used_kdtree = 0;
      
      if (givenhit->hitcell)
	{
	  //do we know the obj, but need the t?
	  if (givenhit->t < 0.) 
	    {
	      rayinfo->objhit.t = MAXFLOAT;
	      rayinfo->objhit.obj->IntersectCertain (rayinfo->ray, rayinfo->objhit);
	      if (rayinfo->objhit.t == MAXFLOAT)
		{
		  cerr << "interior ray " << rayinfo->ray << " through pixel " << x << ", " << y << " missed object that all four corners hit, this one: ";
		  cerr << rayinfo->objhit.obj->bbox() << ", so we're recomputing via a goto. oh dear!" << endl;
		  CS.AddErrorRay (rayinfo->ray, 1000);
		  CS.AddErrorBbox (rayinfo->objhit.obj->bbox());
		  rayinfo->objhit.t = -1;
		}
	      else assert (rayinfo->objhit.t >= 0.);
	    }
	  
	  if (rayinfo->objhit.t >= 0.)
	    {
	      rayinfo->hitsomething = YES;
	    }
	}
      else
	{
	  rayinfo->hitsomething = NO;
	}
    }
}

void Render_Worker::SanityCheck (int x, int y, RayCache *rayinfo, _KDTreeNode *frustumstoppedcell, _KDTreeNode *kdtreestartcell, _KDTreeNode *passedinstart, CycleState &CS)
{
  float startt = -4.5, endt = -5.6;
  if (!frustumstoppedcell)
    startt = 0.;
  else
    {
      if (frustumstoppedcell->_bounds.IncludesPoint (rayinfo->ray.R()))
	startt = 0.;
      else
	{
	  int face, dir;
	  Vec4 ptentered;
	  int hitit = frustumstoppedcell->_bounds.whereEntered (rayinfo->ray, startt, face, dir, ptentered);
	  if (!hitit)
	    {
	      
	      cerr << "ray missed debugging cell!" << endl;
	      CS.AddErrorRay (rayinfo->ray, 1000);
	      CS.AddErrorBbox (frustumstoppedcell->_bounds);
	      //for debuggin'.
	      //		    startt = -4.5;
	      //		    hitit = frustumstoppedcell->_bounds.whereEntered (rayinfo->ray, startt, face, dir, ptentered);
	      //		    startt = 0.;
	    }
	}
      
    }
  
  if (rayinfo->objhit.hitcell)
    endt = rayinfo->objhit.t;
  else
    {
#if 1
      if (frustumstoppedcell)
	{
	  int face, dir;
	  frustumstoppedcell->_bounds.whereLeft (rayinfo->ray, endt, face, dir);
	  if (endt < 0.)
	    {
	      cerr << "didn't hit the bbox we're supposed to leave!" << endl;
	      endt = startt;
	      CS.AddErrorRay (rayinfo->ray, 1000);
	      CS.AddErrorBbox (frustumstoppedcell->_bounds);
	    }
	}
      else
#endif
	endt = startt; //cancel the ray for now
    }
  
#define EPSILON .001
  if (startt > (endt + EPSILON))
    {
      cerr << "doh! startt > endt!!!!!" << endl;
      if (rayinfo->objhit.obj) cerr << "hit obj with bbox " << rayinfo->objhit.obj->bbox() << " at " << rayinfo->objhit.t << ", or " << (rayinfo->ray.R() + rayinfo->ray.D() * rayinfo->objhit.t) << endl;
      else cerr << "missed obj, endt is " << endt;
      if (!rayinfo->_used_kdtree)
	cerr << "used kdtree" << endl;
      else
	cerr << "used frustum" << endl;
      
      if (passedinstart)
	{
	  cerr << "given startcell " << passedinstart->_bounds << ", entering ";
	  float t;
	  int face, dir;
	  Vec4 pt;
	  passedinstart->_bounds.whereEntered (rayinfo->ray, t, face, dir, pt);
	  cerr << t << " or " << pt << ", leaving ";
	  passedinstart->_bounds.whereLeft (rayinfo->ray, t, face, dir, &pt);
	  cerr << t << " or " << pt << endl;
	  if (rayinfo->objhit.obj)
	    {
	      if (rayinfo->objhit.hitcell == passedinstart)
		cerr << "thinks it hit in passedinstart" << endl;
	    }
	  CS.AddErrorBbox (passedinstart->_bounds);
	}
      else
	{
	  cerr << "not given startcell, calced it" << endl;
	  if (kdtreestartcell)
	    {
	      float t;
	      int face, dir;
	      Vec4 pt;
	      cerr << kdtreestartcell->_bounds << endl;
	      kdtreestartcell->_bounds.whereEntered (rayinfo->ray, t, face, dir, pt);
	      cerr << t << ", leaving ";
	      kdtreestartcell->_bounds.whereLeft (rayinfo->ray, t, face, dir, &pt);
	      cerr << t << endl;
	      CS.AddErrorBbox (kdtreestartcell->_bounds);
	    }
	  else
	    cerr << "calced it to be NULL." << endl;
	  if (rayinfo->objhit.obj)
	    {
	      if (rayinfo->objhit.hitcell == kdtreestartcell)
		cerr << "hit in startcell" << endl;
	    }
	}
      
      //	    cerr << "start t is " << startt << endl;
      cerr << x << ", " << y << endl;
      CS.AddErrorRay (rayinfo->ray, startt);
    }
}

void Render_Worker::ClearStats (void)
{
  _num_objs_in_frusta = 0;
  _num_objs_out_frusta = 0;
}

real Render_Worker::DistanceAtten (real dist)
{ 
  // Here, we use the PHONG distance attenuation function, which is of the form
  //
  //  fatt = min(1, 1/(c1 + c2*d + c3*(d^2)))
  //
  //   where d is the distance to the light source
  //   and   c1, c2, and c3 are constants between 0 and 1

  // Here, we arbitrarily set c1, c2, and c3 to 0, 1, and 0 respectively

  float c1 = 1.0, c2 = 0.0, c3 = 0.0;
  //need to put GL in too.
  //  float c1 = 0.0, c2 = 1.0, c3 = 0.0;

  // Don't divide by zero!
  if (dist == 0.0)
    return 1.0;
  else
    {
      float fatt = 1.0 / (c1 + c2*dist + c3*dist*dist);
      if (fatt < 1.0)
	return fatt;
      else
	return 1.0;
    }
}

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
