/* mountain.c,v 1.1.1.1 1995/02/27 07:38:34 explorer Exp */

/*
 * Peter Janssen
 */

#include "libcommon/common.h"
#include "geom.h"
#include "mountain.h"
#include "allocmatrix.h"

int IntPower _PROTO((int, int));
static void MountainEvolve _PROTO((Mountain *));
static void MountainSetupLevelList _PROTO((Mountain *));
static void MountainSetupCache _PROTO((Mountain *));
static void MountainSetupCalcList _PROTO((Mountain *));
int MountainBoundsIntersect _PROTO((Ray *, Float [2][2], Float, Float *));
int MountainCheckGrid _PROTO((Mountain *, int, int, Ray *, Float, Float *,
			      int, Float, Float));
int MountainIntersectTriangle _PROTO((Mountain *, Ray *, Vector *, Vector *,
				      Vector *, Float, Float *));
static long rand_number _PROTO((void));
static double GAUSS _PROTO((int, int, int, int));

#define    x2grid(m, x) (((x) - m->D2Bounds[0][0]) * m->InvXSize)
#define    y2grid(m, y) (((y) - m->D2Bounds[0][1]) * m->InvYSize)

#define    grid2x(m, x) ((x) * m->XSize + m->D2Bounds[0][0])
#define    grid2y(m, y) ((y) * m->YSize + m->D2Bounds[0][1])

#define OutOf2DBounds(p, b)                                 \
               ((p).x < (b)[0][0] || (p).x > (b)[1][0] ||   \
                (p).y < (b)[0][1] || (p).y > (b)[1][1] )

static Methods *iMountainMethods = NULL; 
static char mountainName[] = "mountain";
unsigned long MountainTests, MountainHits;
static int WhichCache[3][3] = {
  {TRUE, TRUE, FALSE},
  {FALSE, FALSE, FALSE},
  {FALSE, TRUE, TRUE}
};

int
IntPower(a, b)
     int   a, b;
{
  int result, i;
  
  for (result = 1, i = 1; i <= b; i++)
    result *= a;

  return result;
}

/* Random Generator */

#define rand_m  (unsigned long)2147483647
#define rand_q  (unsigned long)127773

#define rand_a (unsigned int)16807
#define rand_r (unsigned int)2836

/*
 * F(z) = (az)%m
 *      = az-m(az/m)
 *
 * F(z)  = G(z)+mT(z)
 * G(z)  = a(z%q)- r(z/q)
 * T(z)  = (z/q) - (az/m)
 *
 * F(z)  = a(z%q)- rz/q+ m((z/q) - a(z/m))
 *       = a(z%q)- rz/q+ m(z/q) - az
*/

static long rand_seed;

static long
rand_number()
{
  long    lo, hi, test;
  
  hi   = rand_seed/rand_q;
  lo   = rand_seed%rand_q;
  
  test = rand_a*lo - rand_r*hi;
  
  if (test > 0)
    rand_seed = test;
  else
    rand_seed = test + rand_m;
  
  return rand_seed;
}

static double
GAUSS(s, x, y, level)
     int  s, x, y, level;
{
  long    x1, x2;
  double  r, v1, v2;
  static double inv_rand_m;
  inv_rand_m = 1.0 / (double) rand_m;
  
  rand_seed = s + (y<<15) + x + (level<<8);
  do {
    x1 = rand_number();
    x2 = rand_number();
    v1 = (((double)x1 * inv_rand_m) * 2) - 1;
    v2 = (((double)x2 * inv_rand_m) * 2) - 1;
    r = v1*v1 + v2*v2;
  } while (r >= 1.0);
  r = sqrt(-2.0 * log(r) / r);
  return v1*r;
}


/* 
 * Create MountainObject and return Reference to it
 */
GeomRef
MountainCreate(p1, p2, number, dimension, evolvelevel,
	       iterationlevel, scale, startgrid)
     Vec2d            p1, p2;
     int              number, evolvelevel, iterationlevel;
     Float            dimension, scale;
     StartGridStruct *startgrid;
{
  Mountain *mountain;
  Vec2d     size;
  int       gridmax;
  
  mountain = (Mountain *)share_malloc(sizeof(Mountain));
  size.u = fabs(p1.u - p2.u);
  size.v = fabs(p1.v - p2.v);
  if (equal(size.u, 0.) || equal(size.v, 0.)) {
    RLerror(RL_ADVISE, "Degenerated startgrid.\n");
    return (GeomRef)NULL;
  }
  mountain->D2Bounds[LOW][X] = min(p1.u, p2.u);
  mountain->D2Bounds[HIGH][X] = max(p1.u, p2.u);
  mountain->D2Bounds[LOW][Y] = min(p1.v, p2.v);
  mountain->D2Bounds[HIGH][Y] = max(p1.v, p2.v);
  
  if (number < 0 || number > 65565) {
    RLerror(RL_WARN, "Illegal MountainNumber : %d", number);
    number = 1000;
  }
  if (dimension < 0.5 || dimension > 3.5) {
    RLerror(RL_WARN, "Illegal FractalDimension : %f", dimension);
    dimension = 2.5;
  }
  if (iterationlevel < 0) {
    RLerror(RL_WARN, "Illegal IterationLevel : %d", iterationlevel);
    iterationlevel = 2;
  }
  if (evolvelevel < 0 || evolvelevel > iterationlevel) {
    RLerror(RL_WARN, "Illegal EvolveLevel : %d", evolvelevel);
    evolvelevel = 0;
  }
  if (scale < 0) {
    RLerror(RL_WARN, "Illegal ScaleFactor : %f", scale);
    scale = 1.0;
  }
  
  gridmax  = IntPower(2, iterationlevel) * (startgrid->Size - 1) + 1;
  mountain->XSize    = size.u / gridmax;
  mountain->YSize    = size.v / gridmax;
  mountain->InvXSize = 1.0 / mountain->XSize;
  mountain->InvYSize = 1.0 / mountain->YSize;
  
  mountain->Number           = number;
  mountain->IterationLevel   = iterationlevel;
  mountain->EvolveLevel      = evolvelevel;
  mountain->FractalDimension = dimension;
  mountain->Scale            = scale;
  mountain->StartGridSize    = startgrid->Size;
  mountain->StartGridData    = startgrid->Data;
  mountain->GridMax          = gridmax;
  
  
  MountainEvolve(mountain);
  MountainSetupLevelList(mountain);
  MountainSetupCache(mountain);
  MountainSetupCalcList(mountain);
  
  return (GeomRef)mountain;
}

/*
 * Calculating MountainData up till iterationlevel 'EvolveLevel'
 */
static void
MountainEvolve(m)
     Mountain *m;
{
  Float      Scale;
  Float    **OldMountain, **NewMountain;
  int        OldSize, NewSize;
  int        XFar, XShort, YFar, YShort;
  int        i, x, y;
  
  Scale         = m->Scale;
  OldMountain   = m->StartGridData;
  OldSize       = m->StartGridSize;
  
  for (i = 1; i <= m->EvolveLevel; i++) {
    NewSize = 2 * OldSize - 1;
    Scale = Scale * pow((OldSize / (Float) NewSize), m->FractalDimension);
    NewMountain = AllocMatrix(Float, NewSize+1, NewSize+1);
    for (x = 0; x <= NewSize; x++)
      for (y = 0; y <= NewSize; y++) {
	XShort = (x+1)>>1;
	XFar   = (x|1) - XShort;
	YShort = (y+1)>>1;
	YFar   = (y|1) - YShort;
	NewMountain[x][y] =
	  (OldMountain[XFar][YFar] +
	   3 * (OldMountain[XFar][YShort] +
		OldMountain[XShort][YFar] +
		3 * OldMountain[XShort][YShort])) * 0.0625 +
		  GAUSS(m->Number, x, y, i) * Scale;
      }
    FreeMatrix(OldMountain, OldSize+1);
    OldMountain = NewMountain; OldSize = NewSize;
  }
  m->EvolvedData = OldMountain;
  m->EvolvedSize = OldSize;
}

/*
 * Calculate how many cells on every iterationlevel
 */
static void
MountainSetupLevelList(m)
     Mountain  *m;
{
  int i, Ilevel, Elevel, Dlevel;
  
  Ilevel = m->IterationLevel;
  Elevel = m->EvolveLevel;
  
  Dlevel = Ilevel - Elevel + 1;
  m->MaxSize = (int *) share_malloc(Dlevel * sizeof(int));
  m->MaxSize[0] = IntPower(2, Elevel) * (m->StartGridSize - 1) +  1;
  for (i = 1; i < Dlevel; i++)
    m->MaxSize[i] = 2 * m->MaxSize[i - 1] - 1;
}

/*
 * Allocate memory for cache on every iterationlevel
 */
static void
MountainSetupCache(m)
     Mountain *m;
{
  int   i,j, Ilevel, Elevel, Dlevel;
  
  Ilevel = m->IterationLevel;
  Elevel = m->EvolveLevel;
  Dlevel = (Ilevel - Elevel);

  if (Dlevel) {
    m->Cache1 = (HeightCache **) share_malloc(Dlevel * sizeof(HeightCache *));
    m->Cache2 = (HeightCache **) share_malloc(Dlevel * sizeof(HeightCache *));
    for (i = 0; i < Dlevel; i++)  {
      m->Cache1[i] = (HeightCache *) share_malloc((2 * m->MaxSize[i + 1] + 1) * sizeof(HeightCache));
      m->Cache2[i] = (HeightCache *) share_malloc((2 * m->MaxSize[i + 1] + 1) * sizeof(HeightCache));
      for (j = 0; j <= 2 * m->MaxSize[i + 1]; j++) {
	m->Cache1[i][j].x = -1;
	m->Cache2[i][j].x = -1;
      }
    }
  }
}


/* 
 * Setup list to keep track of which cells are needed
 * on the different iterationlevels
 */
static void
MountainSetupCalcList(m)
     Mountain   *m;
{
  int       i, j;
  CalcList  *Chasingcl, *Cl;
  
  Chasingcl   = (CalcList *) share_malloc(sizeof(CalcList));
  for (i = 0; i <= 2; i++)
    for (j = 0; j <= 2; j++)
      Chasingcl->ToDo[i][j] = FALSE;
  m->ToCalcList       = Chasingcl;
  Chasingcl->Previous = NULL;
  for (i = m->IterationLevel; i > m->EvolveLevel + 1; i--) {
    Cl              = (CalcList *) share_malloc(sizeof(CalcList));
    Chasingcl->Next = Cl;
    Cl->Previous    = Chasingcl;
    Chasingcl       = Cl;
  }
  Chasingcl->Next = NULL;
}


/*
 * Intersect ray with mountain.
 */
int
MountainIntersect(ref, ray, hl, mindist, maxdist)
     GeomRef     ref;
     Ray        *ray;
     HitList    *hl;  /* unused here */
     Float       mindist, *maxdist;
{
  Mountain   *mountain = (Mountain *)ref;
  Vector  currpos;
  Float   offset, currmaxdist;
  int     x, y, sum;
  Float   Abs_t_X, Delta_t_X;
  int     incX, boundX;
  Float   Abs_t_Y, Delta_t_Y;
  int     incY, boundY;
  Float   Delta_w_X_z, Delta_w_Y_z;
  Float   wx_z, wy_z, currpos_z;
  int     triangleorder;
  Float   Rx, Ry, Rsum;
  
  MountainTests++;
  
  /*
   * Find out where the projected ray hits the grid.
   */
  VecAddScaled(ray->pos, mindist, ray->dir, &currpos);
  if (OutOf2DBounds(currpos, mountain->D2Bounds)) {
    offset = *maxdist;
    if (!MountainBoundsIntersect(ray, mountain->D2Bounds, mindist, &offset))
      return FALSE;
    else {
      VecAddScaled(ray->pos, offset, ray->dir, &currpos);
      if (equal(currpos.x, mountain->D2Bounds[LOW][X]) ||
	  equal(currpos.y, mountain->D2Bounds[LOW][Y]))
	triangleorder = 1;
      else triangleorder = -1;
    }
  } else {
    offset = mindist;
    Rx = x2grid(mountain, currpos.x);
    Ry = y2grid(mountain, currpos.y);
    Rsum = Rx + Ry;
    sum = (int) Rx + (int) Ry;
    if ((Rsum - sum) <= 0.5) triangleorder = 1;
    else triangleorder = -1;
  }
  
  x = x2grid(mountain, currpos.x);
  if (x == mountain->GridMax) x--;
  
  if (fabs(ray->dir.x) < EPSILON) {
    Abs_t_X   = FAR_AWAY;
    Delta_t_X = 0;
  } else if (ray->dir.x < 0.) {
    Abs_t_X     = offset + (grid2x(mountain, x) - currpos.x) / ray->dir.x;
    Delta_t_X   = mountain->XSize / (-ray->dir.x);
    incX = -1;  boundX = -1;
  } else {
    Abs_t_X     = offset + (grid2x(mountain, x+1) - currpos.x) / ray->dir.x;
    Delta_t_X   = mountain->XSize / ray->dir.x;
    incX = 1;  boundX = mountain->GridMax;
  }
  
  y = y2grid(mountain, currpos.y);
  if (y == mountain->GridMax) y--;
  
  if (fabs(ray->dir.y) < EPSILON) {
    Abs_t_Y   = FAR_AWAY;
    Delta_t_Y = 0;
  } else if (ray->dir.y < 0.) {
    Abs_t_Y     = offset + (grid2y(mountain, y) - currpos.y) / ray->dir.y;
    Delta_t_Y   = mountain->YSize / (-ray->dir.y);
    incY = -1;  boundY = -1;
  } else {
    Abs_t_Y     = offset + (grid2y(mountain, y+1) - currpos.y) / ray->dir.y;
    Delta_t_Y   = mountain->YSize / ray->dir.y;
    incY = 1;  boundY = mountain->GridMax;
  }
  
  
  Delta_w_X_z = Delta_t_X * ray->dir.z;
  Delta_w_Y_z = Delta_t_Y * ray->dir.z;
  
  wx_z = ray->pos.z + Abs_t_X * ray->dir.z;
  wy_z = ray->pos.z + Abs_t_Y * ray->dir.z;
  
  currpos_z   = currpos.z;
  
  /*
   * Traverse grid with DDA-algorithm
   */
  while(TRUE) {
    if (Abs_t_X < Abs_t_Y) {
      currmaxdist = min(Abs_t_X, *maxdist);
      if (MountainCheckGrid(mountain, x, y, ray, offset, &currmaxdist,
			    triangleorder, currpos_z, wx_z)) {
	*maxdist = currmaxdist;
	return TRUE;
      }
      x += incX;
      if (x == boundX) break;
      triangleorder = incX;
      offset = currmaxdist;
      currpos_z  = wx_z;
      Abs_t_X   += Delta_t_X;
      wx_z      += Delta_w_X_z;
    } else {
      currmaxdist = min(Abs_t_Y, *maxdist);
      if (MountainCheckGrid(mountain, x, y, ray, offset, &currmaxdist,
			    triangleorder, currpos_z, wy_z)) {
	*maxdist = currmaxdist;
	return TRUE;
      }
      y += incY;
      if (y == boundY) break;
      triangleorder = incY;
      offset = currmaxdist;
      currpos_z   = wy_z;
      Abs_t_Y   += Delta_t_Y;
      wy_z      += Delta_w_Y_z;
    }
  }
  return FALSE;
}


int
MountainBoundsIntersect(ray, D2Bounds, mindist, maxdist)
     Ray     *ray;
     Float    D2Bounds[2][2];
     Float    mindist;
     Float   *maxdist;
{
  
  Float    t, tmin, tmax;
  Float    dir, pos;
  
  
  tmax = *maxdist;
  tmin =  mindist;
  
  dir = ray->dir.x;
  pos = ray->pos.x;
  
  if (dir < 0) {
    t = (D2Bounds[LOW][X] - pos) / dir;
    if (t < tmin) return FALSE;
    if (t <= tmax) tmax = t;
    t = (D2Bounds[HIGH][X] - pos) / dir;
    if (t >= tmin) {
      if (t > tmax) return FALSE;
      tmin = t;
    }
  } else if (dir > 0) {
    t = (D2Bounds[HIGH][X] - pos) / dir;
    if (t < tmin) return FALSE;
    if (t <= tmax) tmax = t;
    t = (D2Bounds[LOW][X] - pos) / dir;
    if (t >= tmin) {
      if (t > tmax) return FALSE;
      tmin = t;
    }
  } else if (pos < D2Bounds[LOW][X] ||
	     pos > D2Bounds[HIGH][X]) return FALSE;
  
  dir = ray->dir.y;
  pos = ray->pos.y;
  
  if (dir < 0) {
    t = (D2Bounds[LOW][Y] - pos) / dir;
    if (t < tmin) return FALSE;
    if (t <= tmax) tmax = t;
    t = (D2Bounds[HIGH][Y] - pos) / dir;
    if (t >= tmin) {
      if (t > tmax) return FALSE;
      tmin = t;
    }
  } else if (dir > 0) {
    t = (D2Bounds[HIGH][Y] - pos) / dir;
    if (t < tmin) return FALSE;
    if (t <= tmax) tmax = t;
    t = (D2Bounds[LOW][Y] - pos) / dir;
    if (t >= tmin) {
      if (t > tmax) return FALSE;
      tmin = t;
    }
  } else if (pos < D2Bounds[LOW][Y] ||
	     pos > D2Bounds[HIGH][Y]) return FALSE;
  
  
  if (tmin == mindist) {
    if (tmax < *maxdist) {
      *maxdist = tmax;
      return TRUE;
    }
  } else {
    if (tmin < *maxdist) {
      *maxdist = tmin;
      return TRUE;
    }
  }
  return FALSE;
}

#define CHECKCACHE(cache1, cache2, Rx, Ry, calclist, xx, yy)    \
    for (ii = 0; ii <= 1; ii++, Rx++, Ry-=2) {                  \
        for (jj = 0; jj <= 1; jj++, Ry++) {                     \
            if ((cache1->x == Rx) && (cache1->y == Ry))         \
                calclist->Data[xx+ii][yy+jj] = cache1->z;       \
            else if ((cache2->x == Rx) && (cache2->y == Ry))    \
                calclist->Data[xx+ii][yy+jj] = cache2->z;       \
            else calclist->ToDo[xx+ii][yy+jj] = TRUE;           \
            cache1++;                                           \
            cache2--;                                           \
        }                                                       \
        cache1--;                                               \
        cache2+=3;                                              \
    }


/*
 * Calculate heightvalues at four corners of grid
 * and check if ray possibly intersects the two triangles
 * defined by the four points
 */
int
MountainCheckGrid(m, x, y, ray, offset, maxdist, triangleorder, z_near, z_far)
     Mountain    *m;
     int          x, y;
     Ray         *ray;
     Float        offset;
     Float       *maxdist;
     int          triangleorder;
     Float        z_near, z_far;
{
   HeightCache *Cache1, *Cache2;
   int          NX, NY, TX, TY, TTX, TTY, XP, YP;
   int          level, i, j, xx, yy, ii, jj;
   int          XShort, XFar, YShort, YFar;
   CalcList    *TCL, *TCLP;
   Vector       point1, point2, point3;
   Float        scale;
   static CalcList  EvolveLevelData;

   level = m->IterationLevel - m->EvolveLevel - 1;
   if (level >= 0) {
     TCL = m->ToCalcList;
     TCL->x = x;
     TCL->y = y;
     for (i = 0; i <= 1; i++)
       for (j = 0; j<= 1; j++)
	 TCL->ToDo[i][j] = FALSE;
     /* 
      * Check Cache if cell is available 
      */
     Cache1 = &m->Cache1[level][x+y];
     Cache2 = &m->Cache2[level][m->MaxSize[level + 1] + x - y];
     xx = x;  yy = y;
     CHECKCACHE(Cache1, Cache2, xx, yy, TCL, 0, 0);
     xx= x; yy=y;
     level--;
     /*  
      * Calculate the coordinates of the cells necsarry for calculating
      * the top cell, check cache if they are available 
      */
     for (TCLP = TCL, TCL = TCL->Next; TCL; TCLP = TCL, TCL = TCL->Next,
	  level--, xx = NX, yy = NY) {
       NX = TCL->x = xx>>1;
       NY = TCL->y = yy>>1;
       for (i = 0; i <= 2; i++)
	 for (j = 0; j <= 2; j++)
	   TCL->ToDo[i][j] = FALSE;
       for (i = 0; i <= 2; i++)
	 for (j = 0; j <= 2; j++)
	   if (TCLP->ToDo[i][j]) {
	     TX = (xx+i)>>1;  TY = (yy+j)>>1;
	     Cache1 = &m->Cache1[level][TX+TY];
	     Cache2 = &m->Cache2[level][m->MaxSize[level + 1]+TX-TY];
	     TTX = TX - NX;  TTY = TY - NY;
	     CHECKCACHE(Cache1, Cache2, TX, TY, TCL, TTX, TTY);
	   }
     }
     xx = xx >> 1;
     yy = yy >> 1;
     for (i = 0; i <= min(2, m->MaxSize[0] - xx); i++)
       for (j = 0; j <= min(2, m->MaxSize[0] - yy); j++)
	 EvolveLevelData.Data[i][j] = m->EvolvedData[xx + i][yy + j];
     level = 0;
     XP    = xx;
     YP    = yy;
     /*
      * Calculate heightvalues of corners of cell who were
      * not available in cache 
      */
     for (TCL = TCLP, TCLP = &EvolveLevelData; TCL; TCLP = TCL, TCL = TCL->Previous,
	  level++, XP = xx, YP = yy) {
       xx = TCL->x;  yy = TCL->y;
       Cache1 = &m->Cache1[level][xx + yy];
       Cache2 = &m->Cache2[level][m->MaxSize[level + 1] + xx - yy];
       scale = m->Scale * pow((m->StartGridSize /
			       (Float) m->MaxSize[level + 1]),
			      m->FractalDimension);
       for (i = 0; i<=2; i++) {
	 for (j = 0; j <= 2; j++) {
	   if (TCL->ToDo[i][j]) {
	     XShort = (xx + 1 + i)>>1;
	     XFar   = ((xx + i)|1) - XShort;
	     YShort = (yy + 1 + j)>>1;
	     YFar   = ((yy + j)|1) - YShort;
	     XShort -= XP;
	     XFar -= XP;
	     YShort -= YP;
	     YFar -= YP;
	     TCL->Data[i][j] = (3*(TCLP->Data[XShort][YFar]
				   + TCLP->Data[XFar][YShort]
				   + 3 * TCLP->Data[XShort][YShort])
				+ TCLP->Data[XFar][YFar]) * 0.0625 +
				  GAUSS(m->Number, xx + i, yy + j,
					level + m->EvolveLevel + 1) * scale;
	     
	     if (WhichCache[i][j]) {
	       Cache1->x = xx + i;
	       Cache1->y = yy + j;
	       Cache1->z = TCL->Data[i][j];
	     } else {
	       Cache2->x = xx + i;
	       Cache2->y = yy + j;
	       Cache2->z = TCL->Data[i][j];
	     }
	   }
	   Cache1++;
	   Cache2--;
	 }
	 Cache1 -= 2;
	 Cache2 += 4;
       }
     }
   } else {
     for (i = 0; i <= 1; i++)
       for (j = 0; j <= 1; j++)
	 m->ToCalcList->Data[i][j] = m->EvolvedData[x+i][y+j];
   }
   TCL = m->ToCalcList;
   /*
    * Check if ray crosses cell at right height 
    */
   if ((min(z_near, z_far) > max(max(max(TCL->Data[0][0], TCL->Data[0][1]),
				     TCL->Data[1][0]), TCL->Data[1][1])) ||
       (max(z_near, z_far) < min(min(min(TCL->Data[0][0], TCL->Data[0][1]),
				     TCL->Data[1][0]), TCL->Data[1][1])))
     return FALSE;
   /*
    * Test for ray-triangle intersection 
    */
   for (i = 1; i <=2 ; i++, triangleorder += 2)
     if (triangleorder == 1) {
       point1.x = (x * m->XSize) + m->D2Bounds[LOW][X];
       point1.y = (y * m->YSize) + m->D2Bounds[LOW][Y];
       point1.z = TCL->Data[0][0];
       point2.x = ((x+1) * m->XSize) + m->D2Bounds[LOW][X];
       point2.y = (y * m->YSize) + m->D2Bounds[LOW][Y];
       point2.z = TCL->Data[1][0];
       point3.x = (x * m->XSize) + m->D2Bounds[LOW][X];
       point3.y = ((y+1) * m->YSize) + m->D2Bounds[LOW][Y];
       point3.z = TCL->Data[0][1];
       if (MountainIntersectTriangle(m, ray, &point1, &point2, &point3,
				     offset, maxdist))
	 return TRUE;
     } else {
       point1.x = ((x+1) * m->XSize) + m->D2Bounds[LOW][X];
       point1.y = ((y+1) * m->YSize) + m->D2Bounds[LOW][Y];
       point1.z = TCL->Data[1][1];
       point2.x = (x * m->XSize) + m->D2Bounds[LOW][X];
       point2.y = ((y+1) * m->YSize) + m->D2Bounds[LOW][Y];
       point2.z = TCL->Data[0][1];
       point3.x = ((x+1) * m->XSize) + m->D2Bounds[LOW][X];
       point3.y = (y * m->YSize) + m->D2Bounds[LOW][Y];
       point3.z = TCL->Data[1][0];
       if (MountainIntersectTriangle(m, ray, &point1, &point2, &point3,
				     offset, maxdist))
	 return TRUE;
     }
   return FALSE;
 }

/* 
 * Check if ray intersects triangle 
 */

int
MountainIntersectTriangle(m, ray, point1, point2, point3, mindist, maxdist)
     Mountain   *m;
     Ray        *ray;
     Vector     *point1, *point2, *point3;
     Float       mindist, *maxdist;
{
  Vector  edge1, edge2, edge3;
  Float   k, s;
  Vector  normal, absnorm;
  Vector  pos, dir;
  Float   qi1, qi2, b0, b1, b2;
  
  
  VecSub(*point2, *point1, &edge1);
  VecSub(*point3, *point2, &edge2);
  VecSub(*point1, *point3, &edge3);
  
  /* 
   * Find plane normal. 
   */
  VecCross(&edge1, &edge2, &normal);
  
  pos = ray->pos;
  dir = ray->dir;
  /*
   * Plane intersection.
   */
  k = dotp(&normal, &dir);
  if (fabs(k) < EPSILON)
    return FALSE;
  s = (dotp(&normal, point1) - dotp(&normal, &pos)) / k;
  if (s < mindist || s > *maxdist)
    return FALSE;
  
  /*
   * Find "dominant" part of normal vector.
   */
  absnorm.x = fabs(normal.x);
  absnorm.y = fabs(normal.y);
  absnorm.z = fabs(normal.z);
  
  if (absnorm.x > absnorm.y && absnorm.x > absnorm.z) {
    qi1 = pos.y + s * dir.y;
    qi2 = pos.z + s * dir.z;
    b0 = (edge2.y * (qi2 - point2->z) -
	  edge2.z * (qi1 - point2->y)) / normal.x;
    if (b0 < 0. || b0 > 1.) return FALSE;
    b1 = (edge3.y * (qi2 - point3->z) -
	  edge3.z * (qi1 - point3->y)) / normal.x;
    if (b1 < 0. || b1 > 1.) return FALSE;
    b2 = (edge1.y * (qi2 - point1->z) -
	  edge1.z * (qi1 - point1->y)) / normal.x;
    if (b2 < 0. || b2 > 1.) return FALSE;
  } else if (absnorm.y > absnorm.z) {
    qi1 = pos.x + s * dir.x;
    qi2 = pos.z + s * dir.z;
    b0 = (edge2.z * (qi1 - point2->x) -
	  edge2.x * (qi2 - point2->z)) / normal.y;
    if (b0 < 0. || b0 > 1.) return FALSE;
    b1 = (edge3.z * (qi1 - point3->x) -
	  edge3.x * (qi2 - point3->z)) / normal.y;
    if (b1 < 0. || b1 > 1.) return FALSE;
    b2 = (edge1.z * (qi1 - point1->x) -
	  edge1.x * (qi2 - point1->z)) / normal.y;
    if (b2 < 0. || b2 > 1.) return FALSE;
  } else {
    qi1 = pos.x + s * dir.x;
    qi2 = pos.y + s * dir.y;
    b0 = (edge2.x * (qi2 - point2->y) -
	  edge2.y * (qi1 - point2->x)) / normal.z;
    if (b0 < 0. || b0 > 1.) return FALSE;
    b1 = (edge3.x * (qi2 - point3->y) -
	  edge3.y * (qi1 - point3->x)) / normal.z;
    if (b1 < 0. || b1 > 1.) return FALSE;
    b2 = (edge1.x * (qi2 - point1->y) -
	  edge1.y * (qi1 - point1->x)) / normal.z;
    if (b2 < 0. || b2 > 1.) return FALSE;
  }
  MountainHits++;
  *maxdist = s;
  m->Normal = normal;
  VecAddScaled(ray->pos, s, ray->dir, &m->IntersectPos);
  
  return TRUE;
}

#define VecEqual(a,b) ((equal((a).x, (b).x)) && (equal((a).y, (b).y)) && (equal((a).z, (b).z)))

/*
 * Calculate normal on triangle in pos
 */
int
MountainNormal(ref, pos, nrm, gnrm)
     GeomRef   ref;
     Vector   *pos, *nrm, *gnrm;
{
  Mountain   *m = (Mountain *)ref;

  if (!VecEqual(*pos, m->IntersectPos))
    RLerror(RL_ABORT, "Can't compute mountain normal in point other than the last intersection.\n");
  *gnrm = m->Normal;
  
  VecNormalize(gnrm);
  *nrm = *gnrm;
  return FALSE;
}


char *
MountainName()
{
  return mountainName;
}

void
MountainStats(tests, hits)
     unsigned long *tests, *hits;
{
  *tests = MountainTests;
  *hits = MountainHits;
}

void
MountainUV(ref, pos, norm, uv, dpdu, dpdv)
     GeomRef     ref;
     Vector     *pos, *norm, *dpdu, *dpdv;
     Vec2d      *uv;
{
  uv->u = pos->x;
  uv->v = pos->y;
  if (dpdu) {
    dpdu->x = 1.;
    dpdu->y = dpdu->z = 0.;
    dpdv->x = dpdv->z = 0.;
    dpdv->y = 1.;
  }
}

void
MountainBounds(ref, bounds)
     GeomRef   ref;
     Float     bounds[2][3];
{
  Mountain   *m = (Mountain *)ref;

  bounds[LOW][X] = m->D2Bounds[LOW][X];
  bounds[LOW][Y] = m->D2Bounds[LOW][Y];
  bounds[HIGH][X] = m->D2Bounds[HIGH][X];
  bounds[HIGH][Y] = m->D2Bounds[HIGH][Y];
  bounds[LOW][Z] = -FAR_AWAY;
  bounds[HIGH][Z] = FAR_AWAY;
}

void
MountainMethodRegister(meth)
     UserMethodType  meth;
{
  if (iMountainMethods)
    iMountainMethods->user = meth;
}


Methods *
MountainMethods()
{
  if (iMountainMethods == (Methods *)NULL) {
    iMountainMethods = MethodsCreate();
#if 0
    iMountainMethods->create = (GeomCreateFunc *)MountainCreate;
#endif
    iMountainMethods->methods = MountainMethods;
    iMountainMethods->name = MountainName;
    iMountainMethods->intersect = MountainIntersect;
    iMountainMethods->normal = MountainNormal;
    iMountainMethods->uv = MountainUV;
    iMountainMethods->bounds = MountainBounds;
    iMountainMethods->stats = MountainStats;
    iMountainMethods->checkbounds = FALSE;
    iMountainMethods->closed = FALSE;
  }
  return iMountainMethods;
}
