/* software ID rendering: because hardware ID rendering is tricky due to frame buffer
 * formats, .... */

#include <string.h>

#include "softids.h"
#include "scene.h"
#include "camera.h"
#include "render.h"
#include "error.h"

SGL_CONTEXT *SetupSoftFrameBuffer(void)
{
  SGL_CONTEXT *sgl;

  sgl = sglOpen(Camera.hres, Camera.vres);
  sglDepthTesting(TRUE);
  sglClipping(TRUE);
  sglClear((SGL_PIXEL)0, SGL_ZMAX);

  sglLoadMatrix(Perspective(Camera.fov*2.*M_PI/180., (float)Camera.hres/(float)Camera.vres, Camera.near, Camera.far)); 
  sglMultMatrix(LookAt(Camera.eyep, Camera.lookp, Camera.updir)); 

  return sgl;
}

static SGL_PIXEL (*PatchPixel)(PATCH *) = NULL;

static void SoftRenderPatch(PATCH *P)
{
  VECTOR verts[4];

  if (renderopts.backface_culling &&
      VECTORDOTPRODUCT(P->normal, Camera.eyep) + P->plane_constant < EPSILON)
    return;

  verts[0] = *P->vertex[0]->point;
  verts[1] = *P->vertex[1]->point;
  verts[2] = *P->vertex[2]->point;
  if (P->nrvertices > 3)
    verts[3] = *P->vertex[3]->point;

  sglSetColor(PatchPixel(P));
  sglPolygon(P->nrvertices, verts);
}

void SoftRenderPatches(SGL_PIXEL (*patch_pixel)(PATCH *))
{
  PatchPixel = patch_pixel;

  if (renderopts.frustum_culling) {
    int use_display_lists = renderopts.use_display_lists;
    renderopts.use_display_lists = FALSE;  /* temporarily switch it off */
    RenderWorldOctree(SoftRenderPatch);
    renderopts.use_display_lists = use_display_lists;    
  } else {
    ForAllPatches(P, Patches) {
      SoftRenderPatch(P);
    } EndForAll;
  }
}

static SGL_PIXEL PatchID(PATCH *P)
{
  return (SGL_PIXEL)P->id;
}

static void SoftRenderPatchIds(void)
{
  SoftRenderPatches(PatchID);
}

unsigned long *SoftRenderIds(long *x, long *y)
{
  SGL_CONTEXT *sgl, *oldsgl;
  unsigned long *ids;

  if (sizeof(SGL_PIXEL)!=sizeof(long)) {
    Fatal(-1, "SoftRenderIds", "sizeof(SGL_PIXEL)!=sizeof(long). Disable SOFT_ID_RENDERING");
  }

  oldsgl = sglGetCurrent();
  sgl = SetupSoftFrameBuffer();
  SoftRenderPatchIds();

  *x = sgl->width; *y = sgl->height;
  ids = (unsigned long *)Alloc((int)(*x) * (int)(*y) * sizeof(unsigned long));
  memcpy(ids, sgl->fbuf, sgl->width*sgl->height*sizeof(long));

  sglClose(sgl);
  sglMakeCurrent(oldsgl);

  return ids;
}
