#include <math.h>
#include "canvas.h"
#include "scene.h"

#include "destP.h"
#include "Hemicube.h"


Hemicube::Hemicube(HemicubeType nht)
{
  // Save parameters
  ht = nht;
  actualResolution = defaultResolution;
}



Hemicube::Hemicube(HemicubeType nht,int resolution)
{
  // Save parameters
  ht = nht;
  actualResolution = resolution;
}




void Hemicube::doIdBuffer(VECTOR &pos,VECTOR &scd,VECTOR &upd,int res,unsigned long * &buf)
{
  int      resx,resy;
  long int lresx,lresy;
  RGB      rgb;
  VECTOR   sc;
  CAMERA   *cam;

  // For display of sample points
  resx = Camera.hres;
  resy = Camera.vres;

  // Create a new camera
  cam = CameraCreate();

  // Initialise the camera
  RGBSET(rgb,0.0f,0.0f,0.0f);
  VECTORADD(pos,scd,sc);
  cam = CameraSet(cam,&pos,&sc,&upd,45.0f,res,res,&rgb);
  
  // Save old camera
  CameraPush(&Camera);
  
  // Set the camera
  Camera = *cam;

  CanvasPushMode(CANVASMODE_RENDER);

  // Do id rendering 
  buf = RenderIds(&lresx,&lresy);

  CanvasPullMode();

  // Restore old camera
  CameraPop(&Camera);
  
  // For display of sample points
  Camera.hres = resx;
  Camera.vres = resy;

  // Destroy old camera
  CameraDestroy(cam);
}



void Hemicube::recomputeProbabilities(PATCH *patch)
{
  COORDSYS      csys;        // Coordinate system of the patch.
  VECTOR        pos;         // Center of the hemicube.
  VECTOR        dir;         // Look direction.
  VECTOR        up;          // Up of screen.
  unsigned long *face[5];    // ID Buffer for each face of the hemicube.
  unsigned long *b;
  unsigned long id;
  int           res,halfres; // Resolution of a face and this number divided by 2.
  int           i,j,k,l,y;
  float         fac,delta,posx,posy;
  float         max;

  // Basic initialisation.
  res = actualResolution;
  halfres = (res>>1);

  // Get the midpoint of the patch.
  VECTORCOPY(patch->midpoint,pos);

  // Get the coordinate system of the patch.
  PatchCoordSys(patch,&csys);

  // Find the bounding-cube axis-aligned with the coordinate system
  max = 0.0f;
  for (i=0;i<patch->nrvertices;i++)
    {
      VECTORSUBTRACT(*(patch->vertex[i]->point),pos,dir);
      VECTORABS(dir,dir);
      if (dir.x>max) max = dir.x;
      if (dir.y>max) max = dir.y;
    }
  
  // max is the length of an half-edge of the cube.

  // Construct top id-buffer
  VECTORSCALE(max,csys.Z,dir);
  VECTORSCALE(max,csys.X,up);
  doIdBuffer(pos,dir,up,res,face[0]);

  // Construct side buffers
  VECTORSCALE(max,csys.Z,up);

  //  Construct front id-buffer
  VECTORSCALE(max,csys.X,dir);
  doIdBuffer(pos,dir,up,res,face[1]);

  //  Construct right id-buffer
  VECTORSCALE(max,csys.Y,dir);
  doIdBuffer(pos,dir,up,res,face[2]);

  //  Construct back id-buffer
  VECTORSCALE(-max,csys.X,dir);
  doIdBuffer(pos,dir,up,res,face[3]);

  //  Construct left id-buffer
  VECTORSCALE(-max,csys.Y,dir);
  doIdBuffer(pos,dir,up,res,face[4]);


  // Clear everything before accumulating
  clearProbabilities();
  
  // Iterate through all z-buffers (l is the current z-buffer index)
  for (l=0;l<5;l++)
    {
      b = face[l];                // b is the current z-buffer.
      k = 0;                      // k is the current pixel index in the z-buffer.
      y = (l==0) ? res : halfres; // y is the number of important lines in the current z-buffer.

      fac = 4.0f / ((float)res*(float)res*M_PI);
      delta = 2.0f / (float)res;

      posy = -1.0f + (delta*0.5f);
      
      // Iterate through all lines of the z-buffer (j is the line number)
      for (j=0;j<y;j++)
	{
	  
	  // Iterate through all collums of the current line
	  for (i=0;i<res;i++)
	    {
	      
	      posx = -1.0f + (delta*0.5f);

	      // Read the id in the id-buffer
	      id = b[k] & 0xffffff;
	      if ((id>0)&&(id<numpatches))
		{
		  switch(ht)
		    {
		    case HEMICUBE_VISIBILITY:
		      // Update visibility
		      if (pb[id]==0.0f)
			{
			  pb[id] = 1.0f;
			  sum += 1.0f;
			}
		      break;

		    case HEMICUBE_PIXEL_COUNT:
		      // Update counters
		      pb[id] += 1.0f;
		      sum += 1.0f;
		      break;

		    case HEMICUBE_FORM_FACTOR:
		      // Update form factors
		      if (l==0)
			pb[id] += fac / sqrt((posx*posx)+(posy*posy)+1.0f);
		      else
			pb[id] += fac*fabs(posy) / sqrt((posx*posx)+(posy*posy)+1.0f);
		      
		      sum += pb[id];
		      break;
		    }
		}
	      k++;

	      posx +=delta;
	    }
	  posy += delta;
	}

      // We don't need the z-buffer again...
      delete b;
    }

}












