#define	LIBQDISPLAY_CORE
#include "../include/libqdisplay.h"

/*
 *   "render" scenes by traversing over the
 *   database, marking already-visited things,
 *   using the PVS information and the "marksurface"
 *   info; use one bsp routine to sort
 */

unsigned char *visibleFaces;					/*[MAX_MAP_FACES / 8 + 1]; */
unsigned char *visibleLeafs;					/*[MAX_MAP_LEAFS / 8 + 1]; */
unsigned char *visibleNodes;					/*[MAX_MAP_NODES]; */

int model = 0;

static bool bbox_inside_plane(register short int *mins, register short int *maxs, register struct extplane *pl)
{
  short int pt[3];

  /* use quick test from graphics gems */
  if (pl->positive[0])
    pt[0] = maxs[0];
  else
    pt[0] = mins[0];

  if (pl->positive[1])
    pt[1] = maxs[1];
  else
    pt[1] = mins[1];

  if (pl->positive[2])
    pt[2] = maxs[2];
  else
    pt[2] = mins[2];

  /*
   * each frustum-plane in hesse-form have a positive and a negative half-space
   * we can check if the distance between frustum-plane and point is positive or
   * negative to check where the points is, in the positive or negative
   * half-space
   *
   * if((DotProduct(frustumNormal, point) - frustumDistance) >= 0) -> if(DotProduct(frustumNormal, point) >= frustumDistance)
   *  if is visible (lies in positive half-space)
   * else
   *  it is invisible (lies in negative half-space)
   */
  return DotProduct(pl->normal, pt) >= pl->dist;
}

/* is the complete node between the frustum-planes? */
bool node_in_frustrum(register struct dnode_t *node)
{
  if (!bbox_inside_plane(node->mins, node->maxs, &planes[0]) ||
      !bbox_inside_plane(node->mins, node->maxs, &planes[1]) ||
      !bbox_inside_plane(node->mins, node->maxs, &planes[2]) ||
      !bbox_inside_plane(node->mins, node->maxs, &planes[3]))
    return FALSE;
  return TRUE;
}

/* is the complete leaf between the frustum-planes? */
bool leaf_in_frustrum(register struct dleaf_t *node)
{
  if (!bbox_inside_plane(node->mins, node->maxs, &planes[0]) ||
      !bbox_inside_plane(node->mins, node->maxs, &planes[1]) ||
      !bbox_inside_plane(node->mins, node->maxs, &planes[2]) ||
      !bbox_inside_plane(node->mins, node->maxs, &planes[3]))
    return FALSE;
  return TRUE;
}

/* mark all faces within a given leaf */
void mark_leaf_faces(__memBase, register short int leaf)
{
  int contents = bspMem->shared.quake1.dleafs[leaf].contents;
  short int i = bspMem->shared.quake1.dleafs[leaf].nummarksurfaces;
  unsigned short int *dms = &bspMem->shared.quake1.dmarksurfaces[(int)(bspMem->shared.quake1.dleafs[leaf].firstmarksurface + i)];

  for (--i; i >= 0; i--) {
    unsigned short int s = *--dms;

    mark_face(s);
  }
}

/*
 * The visibility lists are used by BSP Leaves, to
 * determine which other leaves are visible from a given BSP Leaf.
 * The Visibility list can be of size 0, in that case it will not be used.
 * The game will crawl if there is no visibility list in a level.
 *      u_char vislist[numvislist];    / RLE encoded bit array /
 * Basically, the visibility list is an array of bits. There is one such array
 * of bits for each BSP Leaf.  They are all stored in the vislist array, and
 * each leaf has an index to the first byte of it's own array
 * The bit number N, if set to 1, tells that when laying in the tree leaf, one
 * can see the leaf number N.
 * The only complication is that this bit array in run-length encoded:
 * when a set of bytes in the array are all zero, they are coded by zero
 * followed by the number of bytes is the set (always more than 1).
 * Normally, the size of the bit array associated to a leaf should be
 * (numleafs+7)/8, but in fact due to the run length encoding, it's usually
 * much less.
 * When the player is in a leaf, the visibility list is used to tag
 * all the leaves that can possibly be visible, and then only those
 * leaves are rendered.
 * Here is an example of decoding of visibility lists:
 *   / Suppose Leaf is the leaf the player is in. /
 *   v = Leaf.vislist;
 *   for (L = 1; L &lt; numleaves; v++) {
 *     if (visisz[v] == 0) {         / value 0, leaves invisible /
 *       L += 8 * visisz[v + 1]      / skip some leaves /
 *       v++;
 *     }
 *     else {                        / tag 8 leaves, if needed /
 *                                   / examine bits right to left /
 *       for (bit = 1; bit != 0; bit = bit * 2, L++) {
 *         if (visisz[v] & bit)
 *         TagLeafAsVisible(L);
 *       }
 *     }
 *   }
 * Lots of thanks to Tony Myles who fixed the  bit mask formula.
 * There is no necessity to unpack the visibility list in memory,
 * because the code to read them is fast enough.
 */
int visit_visible_leaves(__memBase)
{
  short int n;
  int i;
  unsigned char *vis;

  n = find_leaf(bspMem);

  if ((!n) || (bspMem->shared.quake1.dleafs[n].visofs < 0)) {
    __memset(visibleLeafs, 0xFF, (bspMem->shared.quake1.dmodels[model].visleafs >> 3) * sizeof(unsigned char));
    return 0;
  }
  else {
    __bzero(visibleLeafs, (bspMem->shared.quake1.dmodels[model].visleafs >> 3) * sizeof(unsigned char));

  vis = bspMem->shared.quake1.dvisdata + bspMem->shared.quake1.dleafs[n].visofs;
  for (i = 1; i < bspMem->shared.quake1.dmodels[model].visleafs;) {
    unsigned char v = *vis++;

    if (!v) {
      i += *vis++ << 3;						/* * 8 */
    }
    else {
      if (v & 1)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 2)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 4)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 8)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 16)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 32)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 64)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
      if (v & 128)
	visibleLeafs[i >> 3] |= (1 << (i & 7));
      i++;
    }
  }
  return 1;
  }
}

/*
 * during a bsp recursion, draw all of the faces
 * stored on this node which are visible (i.e. just
 * test their mark flag)
 */
void render_node_faces(__memBase, register short int node, short register int side)
{
  unsigned short int i, n, f;

  n = bspMem->shared.quake1.dnodes[node].numfaces;
  f = bspMem->shared.quake1.dnodes[node].firstface;

  for (i = 0; i < n; ++i) {
    if (bspMem->shared.quake1.dfaces[f].side == side)
      if (is_marked_face(f))
	draw_face(bspMem, f);
    unmark_face(f);
    ++f;
  }
}

void renderWorld(__memBase)
{
  compute_view_frustrum();

  /*for(model = 0; model < bspMem->shared.quake1.nummodels; model++) { */
  visit_visible_leaves(bspMem);
  bsp_find_visible_nodes(bspMem, (short int)bspMem->shared.quake1.dmodels[model].headnode[0]);
  bsp_explore_node(bspMem, (short int)bspMem->shared.quake1.dmodels[model].headnode[0]);
  bsp_render_node(bspMem, (short int)bspMem->shared.quake1.dmodels[model].headnode[0]);
  /*} */
}
