/*
 * mountain.c
 *
 * Peter Janssen
 *
 */

#include "geom.h"
#include "mountain.h"
#include "allocmatrix.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>


static void MountainEvolve();
static void MountainSetupLevelList();
static void MountainSetupCache();
static void MountainSetupCalcList();
int MountainBoundsIntersect();
int CheckGrid();
int IntersectTriangle();

#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 MyPower(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;
}

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
 */
Mountain *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 (Mountain *) 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  = MyPower(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 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] = MyPower(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;

    if (Dlevel = Ilevel - Elevel) {
        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(mountain, ray, mindist, maxdist)
Mountain   *mountain;
Ray        *ray;
Float       mindist, *maxdist;
{
        Vector  currpos;
        Float   offset, currmaxdist;
        int     x, y, sum;
        int     hit=FALSE;
        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 (CheckGrid(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 (CheckGrid(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 CheckGrid(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, edge1, edge2, edge3;
   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 (IntersectTriangle(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 (IntersectTriangle(m, ray, &point1, &point2, &point3, offset, maxdist)) return TRUE;
       }
   return FALSE;
}

/* 
 * Check if ray intersects triangle 
 */

int IntersectTriangle(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(m, pos, nrm, gnrm)
Mountain *m;
Vector   *pos, *nrm, *gnrm;
{
    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(m, pos, norm, uv, dpdu, dpdv)
Mountain   *m;
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(m, bounds)
Mountain *m;
Float     bounds[2][3];
{
        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();
                iMountainMethods->create = (GeomCreateFunc *)MountainCreate;
                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;
}
