/*
    GL-117
    Copyright 2001, 2002 Thomas A. Drexl aka heptargon

    This file is part of GL-117.

    GL-117 is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    GL-117 is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GL-117; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* This file includes a random landscape generator. */

#ifndef IS_GLLAND_H

#include "glland.h"
#include "main.h"
#include "mathtab.h"
#include "gl.h"
#include "common.h"

#include <stdlib.h>

const float zoomz = 1.0/(100.0*MAXX);
const float hh = (float) 1 / (float) MAXX;
const float zoomz2 = 32768.0 * zoomz;
const float hh2 = 2.0*hh;
CTexture *texgrass, *texrocks, *texwater, *textree, *textree2, *textree3, *texcactus1, *texredstone;
CTexture *textreeu, *textreeu2, *textreeu3, *textreeu4, *textreeu5, *texcactusu1;
CTexture *textree4, *textree5, *texearth, *texsand, *texredsand, *texgravel1;
CTexture *texglitter1;

VertexArray *va;
VertexArray vertexarrayquadstrip;
VertexArray vertexarrayquad [30];
VertexArray vertexarraytriangle [30];
VertexArray vertexarrayglitter [2];

class IndexCounter
{
  public:
  int index [100];
  int n;

  IndexCounter ()
  {
    init ();
  }

  ~IndexCounter ()
  {
  }

  void init ()
  {
    n = 0;
    memset (index, 0, 100 * sizeof (int));
  }

  int get (int index1)
  {
    int i;
    for (i = 0; i < n; i ++)
    {
      if (index [i] == index1)
        return i;
    }
    index [n] = index1;
    n ++;
    return n - 1;
  }
};

IndexCounter ic;

void GLLandscape::norm (float *c)
{
  float n;
  n = 1.0/sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]);
  c[0] *= n;
  c[1] *= n;
  c[2] *= n;
}

void GLLandscape::normalcrossproduct( float* a, float* b, float*c )
{
  c[0] = a[1]*b[2] - a[2]*b[1];
  c[1] = a[2]*b[0] - a[0]*b[2];
  c[2] = a[0]*b[1] - a[1]*b[0];
  norm (c);
}

int GLLandscape::selectColor (int x, int y)
{
  return f [x] [y];
/*  if (f [x] [y] == GRASS) return 0;
  else if (f [x] [y] >= CONIFEROUSWOODS1 && f [x] [y] <= MIXEDWOODS3) return 1;
  else if (f [x] [y] == ROCKS) return 2;
  else if (f [x] [y] == GLACIER) return 3;
  else if (f [x] [y] >= DWARFPINES1 && f [x] [y] <= BUSHES3) return 1;
  else if (f [x] [y] == WATER) return 5;
  else if (f [x] [y] == SHALLOWWATER) return 4;
  else if (f [x] [y] == DEEPWATER) return 6;
  else if (f [x] [y] == ROCKS2) return 7;
  else if (f [x] [y] == XSHALLOWWATER) return 8;
  else if (f [x] [y] == XDEEPWATER) return 9;
  else if (f [x] [y] == MOONSAND) return 10;
  else if (f [x] [y] == REDSTONE) return 11;
  else if (f [x] [y] == REDSAND || f [x] [y] == REDTREE0) return 12;
  else if (f [x] [y] == DESERTSAND || f [x] [y] == CACTUS0) return 13;
  else if (f [x] [y] == GREYSAND) return 14;
  else if (f [x] [y] == GRAVEL) return 15;
  else if (f [x] [y] == TOWN) return 16;
  else return 0;*/
}

void GLLandscape::precalculate ()
{
  int i, i2, i3, i4, x, z;
  float no[3];
  float nw[3];
  float so[3];
  float sw[3];
  float a[3];
  float c[3];

  // initialize dynamic light mask
  memset (dl, 0, (MAXX + 1) * (MAXX + 1));

  norm (lv); // normalize light vector

  lightfac = (0.002 * contrast + 1.0) * 0.001 / 256.0;

  // generate greyish material values for night if necessary
  float matgrey [MAXMATERIAL] [4];
  if (!day)
  {
    float greyfactor = 0.75; // 0 = grey, 1 = no modification
    for (i = 0; i < MAXMATERIAL; i ++)
    {
      float mid = (mat [i] [0] + mat [i] [1] + mat [i] [2]) / 3.0F;
      matgrey [i] [0] = mid + (mat [i] [0] - mid) * greyfactor;
      matgrey [i] [1] = mid + (mat [i] [1] - mid) * greyfactor;
      matgrey [i] [2] = mid + (mat [i] [2] - mid) * greyfactor;
    }
  }

  // create coarse height map
  for (x = 0; x <= MAXX - 4; x += 4)
    for (z = 0; z <= MAXX - 4; z += 4)
    {
      unsigned short max = 0, min = 65535;
      for (i = 0; i <= 3; i ++)
        for (i2 = 0; i2 <= 3; i2 ++)
        {
          if (hw [x + i] [z + i2] > max) max = hw [x + i] [z + i2];
          if (hw [x + i] [z + i2] < min) min = hw [x + i] [z + i2];
        }
      hcmax [x / 4] [z / 4] = max;
      hcmin [x / 4] [z / 4] = min;
    }

  // set the colors of the landscape
  hastowns = false;
  float mzoom = zoomz;
  for (x=0; x<=MAXX; x++)
    for (z=0; z<=MAXX; z++)
    {
      int a;
      a = f [x] [z];
      if (type == 2 && a == 4)
      {
        a = 11;
      }
      else if (type == 4 && a == 4)
      {
        a = 2;
      }
      else if (a == 16)
      {
        hastowns = true;
      }
      if (day)
      {
        r [x] [z] = (unsigned char) (mat [a] [0] * 255.9);
        g [x] [z] = (unsigned char) (mat [a] [1] * 255.9);
        b [x] [z] = (unsigned char) (mat [a] [2] * 255.9);
      }
      else
      {
        r [x] [z] = (unsigned char) (matgrey [a] [0] * 255.9);
        g [x] [z] = (unsigned char) (matgrey [a] [1] * 255.9);
        b [x] [z] = (unsigned char) (matgrey [a] [2] * 255.9);
      }
    }

  long sum;
  // smooth the colors (obsolete)
/*  long g3[3][3]={{0,1,0},
        {1,4,1},
        {0,1,0}};
  for (i = 1; i < MAXX; i ++)
    for (i2 = 1; i2 < MAXX; i2 ++)
    {
      sum = 0;
      for (i3 = 0; i3 < 3; i3 ++)
        for (i4 = 0; i4 < 3; i4 ++)
        {
          sum += g3[i3][i4] * r[i+i3-1][i2+i4-1];
        }
      sum /= 8;
      lg[i][i2]=sum;
    }
  for (i = 1; i <= MAXX - 1; i ++)
    for (i2 = 1; i2 <= MAXX - 1; i2 ++)
    {
      r [i] [i2] = (unsigned char) lg [i] [i2];
    }
  for (i = 1; i < MAXX; i ++)
    for (i2 = 1; i2 < MAXX; i2 ++)
    {
      sum = 0;
      for (i3 = 0; i3 < 3; i3 ++)
        for (i4 = 0; i4 < 3; i4 ++)
        {
          sum += g3[i3][i4] * g[i+i3-1][i2+i4-1];
        }
      sum /= 8;
      lg[i][i2]=sum;
    }
  for (i = 1; i <= MAXX - 1; i ++)
    for (i2 = 1; i2 <= MAXX - 1; i2 ++)
    {
      g [i] [i2] = (unsigned char) lg [i] [i2];
    }
  for (i = 1; i < MAXX; i ++)
    for (i2 = 1; i2 < MAXX; i2 ++)
    {
      float sum = 0;
      for (i3 = 0; i3 < 3; i3 ++)
        for (i4 = 0; i4 < 3; i4 ++)
        {
          sum += g3[i3][i4] * b[i+i3-1][i2+i4-1];
        }
      sum /= 8;
      lg[i][i2]=sum;
    }
  for (i = 1; i <= MAXX - 1; i ++)
    for (i2 = 1; i2 <= MAXX - 1; i2 ++)
    {
      b [i] [i2] = (unsigned char) lg [i] [i2];
    }*/

  // Set the height mask for the lowest sunrays touching the landscape's surface
  // This is just an approximation presuming the sun is a vertical line
  float m1 = mzoom / hh;
  float ih = tan ((sungamma + 5) * PI / 180) / m1; // 0 degree vertical sun radius
  for (x = 0; x <= MAXX; x ++)
  {
    float rayheight = hw [x] [MAXX];
    for (z = MAXX; z >= 0; z --)
    {
      unsigned short maxheight = (int) rayheight;
      if (rayheight < hw [x] [z])
      {
        rayheight = hw [x] [z];
        maxheight = hw [x] [z];
      }
      rayheight -= ih;
      hray [x] [z] = maxheight;
    }
  }

  // precalculate water light, always the same angle
  int nlwater = 1200 - (int) (1000.0 * 2.0 * fabs ((90.0 - sungamma) * PI / 180.0) / PI);

  // precalculate a height average
  int midheight = (highestpoint + lowestpoint) / 2;

  // set minimum ambient light
  int minambient = (int) (100.0 + sungamma * 4);
  if (!day) minambient = 100;
  if (minambient > 350) minambient = 350;

  // Set the luminance of the landscape
  for (x = 0; x <= MAXX; x ++)
    for (z = 0; z <= MAXX; z ++)
    {
      int xm1 = GETCOORD(x - 1);
      int xp1 = GETCOORD(x + 1);
      int zm1 = GETCOORD(z - 1);
      int zp1 = GETCOORD(z + 1);

      // Calculate the normal vectors
      a[0] = 0;
      a[1] = (float) (hw[x][zm1] - hw[x][z]) * mzoom;
      a[2] = -hh;
      c[0] = -hh;
      c[1] = (float) (hw[xm1][z] - hw[x][z]) * mzoom;
      c[2] =  0;
      normalcrossproduct( a, c, nw );
      a[0] = hh;
      a[1] = (float) (hw[xp1][z] - hw[x][z]) * mzoom;
      a[2] = 0;
      c[0] = 0;
      c[1] = (float) (hw[x][zm1] - hw[x][z]) * mzoom;
      c[2] = -hh;
      normalcrossproduct( a, c, no );
      a[0] = 0;
      a[1] = (float) (hw[x][zp1] - hw[x][z]) * mzoom;
      a[2] = hh;
      c[0] = hh;
      c[1] = (float) (hw[xp1][z] - hw[x][z]) * mzoom;
      c[2] = 0;
      normalcrossproduct( a, c, so );
      a[0] = -hh;
      a[1] = (float) (hw[xm1][z] - hw[x][z]) * mzoom;
      a[2] =  0;
      c[0] = 0;
      c[1] = (float) (hw[x][zp1] - hw[x][z]) * mzoom;
      c[2] = hh;
      normalcrossproduct( a, c, sw );
      float normx = (no[0] + nw[0] + so[0] + sw[0]) / 4.0;
      float normy = (no[1] + nw[1] + so[1] + sw[1]) / 4.0;
      float normz = (no[2] + nw[2] + so[2] + sw[2]) / 4.0;

      // Calculate the light hitting the surface
      float gamma = (float) acos (normx * lv [0] + normy * lv [1] + normz * lv [2]); // angle
      if (!isWater (f [x] [z]))
      {
        nl [x] [z] = 1200 - (int) (900.0 * 2.0 * fabs (gamma) / PI); // calculate light
        if (type == LAND_CANYON) // in canyons more ambient light in higher regions
          nl [x] [z] += (h [x] [z] - midheight) / 40; // typical max height diff is 10000
      }
      else
      {
        nl [x] [z] = nlwater; // precalculated light (above)
      }
      if (nl [x] [z] < minambient) // minimum ambient light
        nl [x] [z] = minambient;
      // Check whether this point is in the shadow of some mountain
      if (hw [x] [z] < hray [x] [z])
      {
        nl [x] [z] /= 2;
        if (nl [x] [z] < minambient)
          nl [x] [z] = minambient; // minimum ambient light
      }
    }

  // Smooth the luminance (very important)
  long g3_1[3][3]={{1,2,1},
                   {2,4,2},
                   {1,2,1}};
  for (i = 1; i <= MAXX - 1; i ++)
  {
    for (i2 = 1; i2 <= MAXX - 1; i2 ++)
    {
      sum = 0;
      for (i3 = 0; i3 < 3; i3 ++)
        for (i4 = 0; i4 < 3; i4 ++)
        {
          sum += g3_1[i3][i4] * nl[i+i3-1][i2+i4-1];
        }
      sum /= 16;
      lg[i][i2] = (unsigned short) sum;
    }
  }
  for (i = 1; i <= MAXX - 1; i ++)
    for (i2 = 1; i2 <= MAXX - 1; i2 ++)
    {
      nl [i] [i2] = lg [i] [i2];
    }

  // Assign textures: tex1 = quad texture
  // if tex2 defined: tex1 = upper triangle texture, tex2 = lower triangle texture
  for (i = 0; i < MAXX; i ++)
    for (i2 = 0; i2 < MAXX; i2 ++)
    {
      drawrule [i] [i2] = 0;
      
      int f1 = f [i] [i2], f2 = f [i + 1] [i2], f3 = f [i] [i2 + 1], f4 = f [i + 1] [i2 + 1];
      if (isType (f1, GRASS))
      {
        tex1 [i] [i2] = texgrass->textureID;
      }
      else if (isWoods (f1) || isType (f1, MOONSAND) || isType (f1, REDSAND) || isType (f1, REDTREE0) || isType (f1, CACTUS0) || isType (f1, GREYSAND))
      {
        tex1 [i] [i2] = texredsand->textureID;
      }
      else if (isType (f1, GRAVEL))
      {
        tex1 [i] [i2] = texgravel1->textureID;
      }
      else if (isWater (f1))
      {
        if (type == 0 || type == 2)
          tex1 [i] [i2] = texgrass->textureID;
        else
          tex1 [i] [i2] = 0xFF;
      }
      else if (isType (f1, ROCKS) || isType (f1, ROCKS2))
      {
        tex1 [i] [i2] = texrocks->textureID;
      }
      else if (isType (f1, REDSTONE))
      {
        tex1 [i] [i2] = texredstone->textureID;
      }
      else if (isType (f1, DESERTSAND))
      {
        tex1 [i] [i2] = texsand->textureID;
      }
      else
      {
        tex1 [i] [i2] = 0xFF;
      }

      if (!isGlacier (f1) && isGlacier (f2) && isGlacier (f3) && isGlacier (f4))
      { drawrule [i] [i2] = 1; tex1 [i] [i2] = texrocks->textureID; tex2 [i] [i2] = 0xFF; }
      if (isGlacier (f1) && !isGlacier (f2) && !isGlacier (f3) && !isGlacier (f4))
      { drawrule [i] [i2] = 1; tex1 [i] [i2] = texrocks->textureID; tex2 [i] [i2] = texrocks->textureID; }
      if (!isGlacier (f1) && !isGlacier (f2) && !isGlacier (f3) && isGlacier (f4))
      { drawrule [i] [i2] = 1; tex1 [i] [i2] = texrocks->textureID; tex2 [i] [i2] = texrocks->textureID; }
      if (isGlacier (f1) && isGlacier (f2) && isGlacier (f3) && !isGlacier (f4))
      { drawrule [i] [i2] = 1; tex1 [i] [i2] = 0xFF; tex2 [i] [i2] = texrocks->textureID; }
      
      if (isGlacier (f1) && !isGlacier (f2) && isGlacier (f3) && isGlacier (f4))
      { drawrule [i] [i2] = 2; tex1 [i] [i2] = texrocks->textureID; tex2 [i] [i2] = 0xFF; }
      if (!isGlacier (f1) && isGlacier (f2) && !isGlacier (f3) && !isGlacier (f4))
      { drawrule [i] [i2] = 2; tex1 [i] [i2] = texrocks->textureID; tex2 [i] [i2] = texrocks->textureID; }
      if (!isGlacier (f1) && !isGlacier (f2) && isGlacier (f3) && !isGlacier (f4))
      { drawrule [i] [i2] = 2; tex1 [i] [i2] = texrocks->textureID; tex2 [i] [i2] = texrocks->textureID; }
      if (isGlacier (f1) && isGlacier (f2) && !isGlacier (f3) && isGlacier (f4))
      { drawrule [i] [i2] = 2; tex1 [i] [i2] = 0xFF; tex2 [i] [i2] = texrocks->textureID; }
    }

}

// Get height over landscape/water, no interpolation (fast)
float GLLandscape::getMinHeight (float x, float z)
{
  int mx = GETCOORD((int)floor (x));
  int mz = GETCOORD((int)floor (z));
  return (ZOOM * ((float)hcmin[mx/4][mz/4]*zoomz - zoomz2));
}

// Get height over landscape/water, no interpolation (fast)
float GLLandscape::getMaxHeight (float x, float z)
{
  int mx = GETCOORD((int)floor (x));
  int mz = GETCOORD((int)floor (z));
  return (ZOOM * ((float)hcmax[mx/4][mz/4]*zoomz - zoomz2));
}

// Get height over landscape/water, no interpolation (fast)
float GLLandscape::getHeight (float x, float z)
{
  int mx = GETCOORD((int)floor (x));
  int mz = GETCOORD((int)floor (z));
  return (ZOOM * ((float)hw[mx][mz]*zoomz - zoomz2));
}

// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactHeight2 (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  mx1 -= mx1 & 1;
  mz1 -= mz1 & 1;
  int mx2 = mx1 + 2;
  int mz2 = mz1 + 2;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (ZOOM * (h2/4*zoomz - zoomz2));
}

// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactHeight3 (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  mx1 -= mx1 % 3;
  mz1 -= mz1 % 3;
  int mx2 = mx1 + 3;
  int mz2 = mz1 + 3;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (ZOOM * (h2/9*zoomz - zoomz2));
}

// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactHeight4 (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  mx1 -= mx1 & 3;
  mz1 -= mz1 & 3;
  int mx2 = mx1 + 4;
  int mz2 = mz1 + 4;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (ZOOM * (h2/16*zoomz - zoomz2));
}

// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactHeight (float x, float z)
{
  if (gridstep == 2) return getExactHeight2 (x, z);
  else if (gridstep == 3) return getExactHeight3 (x, z);
  else if (gridstep == 4) return getExactHeight4 (x, z);
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  int mx2 = mx1 + 1;
  int mz2 = mz1 + 1;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (ZOOM * (h2*zoomz - zoomz2));
}

// Get height over landscape/water without ZOOM scaling, linear interpolation (slow)
// Only used to draw trees
// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactLSHeight2 (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  mx1 -= mx1 & 1;
  mz1 -= mz1 & 1;
  int mx2 = mx1 + 2;
  int mz2 = mz1 + 2;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (h2/4*zoomz - zoomz2);
}

// Get height over landscape/water without ZOOM scaling, linear interpolation (slow)
// Only used to draw trees
// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactLSHeight3 (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  mx1 -= mx1 % 3;
  mz1 -= mz1 % 3;
  int mx2 = mx1 + 3;
  int mz2 = mz1 + 3;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (h2/9*zoomz - zoomz2);
}

// Get height over landscape/water without ZOOM scaling, linear interpolation (slow)
// Only used to draw trees
// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactLSHeight4 (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  mx1 -= mx1 & 3;
  mz1 -= mz1 & 3;
  int mx2 = mx1 + 4;
  int mz2 = mz1 + 4;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (h2/16*zoomz - zoomz2);
}

// Get height over landscape/water without ZOOM scaling, linear interpolation (slow)
// Only used to draw trees
// Get height over landscape/water, linear interpolation (slow)
float GLLandscape::getExactLSHeight (float x, float z)
{
  if (gridstep == 2) return getExactLSHeight2 (x, z);
  else if (gridstep == 3) return getExactLSHeight3 (x, z);
  else if (gridstep == 4) return getExactLSHeight4 (x, z);
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mz1 = (int) floor (mz);
  int mx2 = mx1 + 1;
  int mz2 = mz1 + 1;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hw[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hw[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hw[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hw[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (h2*zoomz - zoomz2);
}

// Get height of lowest sunray, no interpolation
float GLLandscape::getRayHeight (float x, float z)
{
  int mx = GETCOORD((int)floor (x));
  int mz = GETCOORD((int)floor (z));
  return (ZOOM * ((float)hray[mx][mz]*zoomz - zoomz2));
}

// Get height of lowest sunray, linear interpolation
float GLLandscape::getExactRayHeight (float x, float z)
{
  float mx = x;
  float mz = z;
  int mx1 = (int) floor (mx);
  int mx2 = mx1 + 1;
  int mz1 = (int) floor (mz);
  int mz2 = mz1 + 1;
  int ax1 = GETCOORD(mx1);
  int ax2 = GETCOORD(mx2);
  int az1 = GETCOORD(mz1);
  int az2 = GETCOORD(mz2);
  float h2 = (float)hray[ax1][az1]*((float)mx2-mx)*((float)mz2-mz) + (float)hray[ax2][az1]*(mx-mx1)*((float)mz2-mz) +
             (float)hray[ax1][az2]*((float)mx2-mx)*(mz-mz1) + (float)hray[ax2][az2]*(mx-mx1)*(mz-mz1);
  return (ZOOM * (h2*zoomz - zoomz2));
}

// Draw tree using two static quads (high quality, slow)
void GLLandscape::drawTree (float x, float y, float htree, float wtree, int phi)
{
  float ht = getExactLSHeight (x, y);
  phi = 359 - phi;
  if (phi < 0 || phi > 359)
  {
    printf ("Test: maybe a problem in drawTree!");
  }
  // Draw tree using a single rotated quad (low quality, fast)
  float ex1 = cosi [phi] * wtree, ey1 = sine [phi] * wtree;
  float ex2 = -ex1, ey2 = -ey1;
  int myticker;
  float zy = 0;
  if (weather == 1) // stormy weather
  {
    myticker = (int) (0.05 / htree * lsticker / timestep + 1000 * wtree + (x + y) * 50);
    if (myticker != 0)
      myticker %= 360;
    zy = 0.2 * (2.0 + sine [myticker]);
  }
  if (texturetree1 >= 0)
  {
//    glBindTexture (GL_TEXTURE_2D, texturetree1);
    va = &vertexarrayquad [texturetree1 + 1];
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (0, 1);
    va->glVertex3f (hh2*(ex1+x), ht + htree, hh2*((ey1+y+zy)));
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (1, 1);
    va->glVertex3f (hh2*(ex2+x), ht + htree, hh2*((ey2+y+zy)));
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (1, 0);
    va->glVertex3f (hh2*(ex2+x), ht, hh2*((ey2+y)));
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (0, 0);
    va->glVertex3f (hh2*(ex1+x), ht, hh2*((ey1+y)));
  }
  if (quality >= 2 && texturetree2 >= 0)
  {
    wtree *= 1.4F;
    ht += htree * 0.4F;
    zy *= 0.4F;
    phi += 45;
    if (phi >= 360) phi -= 360;
    ex1 = cosi [phi] * wtree; ey1 = sine [phi] * wtree;
    ex2 = -ex1; ey2 = -ey1;
    phi += 90;
    if (phi >= 360) phi -= 360;
    float ex3 = cosi [phi] * wtree, ey3 = sine [phi] * wtree;
    float ex4 = -ex3, ey4 = -ey3;
//    glBindTexture (GL_TEXTURE_2D, texturetree2);
    va = &vertexarrayquad [texturetree2 + 1];
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (0, 1);
    va->glVertex3f (hh2*(ex1+x), ht, hh2*((ey1+y+zy)));
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (1, 1);
    va->glVertex3f (hh2*(ex3+x), ht, hh2*((ey3+y+zy)));
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (1, 0);
    va->glVertex3f (hh2*(ex2+x), ht, hh2*((ey2+y+zy)));
    va->glColor3ub (treecolor.c [0], treecolor.c [1], treecolor.c [2]);
    va->glTexCoord2d (0, 0);
    va->glVertex3f (hh2*(ex4+x), ht, hh2*((ey4+y+zy)));
  }
}

float xtree [256];
float ytree [256];

// Draw tree using two static quads (high quality, slow)
void GLLandscape::drawTreeQuad (int x, int y, int phi, bool hq)
{
  int i;
  int rotval = (x + 2 * y) & 127;
  int xs = getCoord (x);
  int ys = getCoord (y);
  if (f [xs] [ys] >= CONIFEROUSWOODS0 && f [xs] [ys] <= CONIFEROUSWOODS3)
  {
    texturetree1 = textree2->textureID;
    if (hq) texturetree2 = textreeu2->textureID;
    else texturetree2 = -1;
    int trees = CONIFEROUSWOODS3 - f [xs] [ys] + 1;
    if (hq && quality >= 2) trees += (trees - 1);
    for (i = 0; i < trees; i ++)
    {
      float htree = 0.0035 + 0.0002 * ((3 * y + 2 * x) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 70, phi);
    }
  }
  else if (f [xs] [ys] >= DECIDUOUSWOODS0 && f [xs] [ys] <= DECIDUOUSWOODS3)
  {
    texturetree1 = textree->textureID;
    if (hq) texturetree2 = textreeu->textureID;
    else texturetree2 = -1;
    int trees = DECIDUOUSWOODS3 - f [xs] [ys] + 1;
    if (hq && quality >= 2) trees += (trees - 1);
    for (i = 0; i < trees; i ++)
    {
      float htree = 0.0035 + 0.0003 * ((3 * y + 2 * x + 4 * i) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 60, phi);
    }
  }
  else if (f [xs] [ys] >= MIXEDWOODS0 && f [xs] [ys] <= MIXEDWOODS3)
  {
    texturetree1 = textree2->textureID;
    if (hq) texturetree2 = textreeu2->textureID;
    else texturetree2 = -1;
    int trees = MIXEDWOODS3 - f [xs] [ys] + 1;
    if (hq && quality >= 2) trees += (trees - 1);
    for (i = 0; i < 2; i ++)
    {
      float htree = 0.0035 + 0.0002 * ((3 * y + 2 * x + 4 * i) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 70, phi);
    }
    texturetree1 = textree5->textureID;
    if (hq) texturetree2 = textreeu5->textureID;
    else texturetree2 = -1;
    for (i = 2; i < 3; i ++)
    {
      float htree = 0.0025 + 0.00015 * ((3 * y + 2 * x + 4 * i) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 60, phi);
    }
    texturetree1 = textree->textureID;
    if (hq) texturetree2 = textreeu->textureID;
    else texturetree2 = -1;
    for (i = 3; i < trees; i ++)
    {
      float htree = 0.0035 + 0.0003 * ((3 * y + 2 * x + 4 * i) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 60, phi);
    }
  }
  else if (f [xs] [ys] >= DWARFPINES0 && f [xs] [ys] <= DWARFPINES3)
  {
    texturetree1 = textree3->textureID;
    if (hq) texturetree2 = textreeu3->textureID;
    else texturetree2 = -1;
    int trees = DWARFPINES3 - f [xs] [ys] + 1;
    if (hq && quality >= 2) trees += (trees - 1);
    for (i = 0; i < trees; i ++)
    {
      float htree = 0.0015 + 0.00015 * ((3 * y + 2 * x + 4 * i) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 150, phi);
    }
  }
  else if (f [xs] [ys] >= BUSHES0 && f [xs] [ys] <= BUSHES3)
  {
    texturetree1 = textree5->textureID;
    if (hq) texturetree2 = textreeu5->textureID;
    else texturetree2 = -1;
    int trees = BUSHES3 - f [xs] [ys] + 1;
    if (hq && quality >= 2) trees += (trees - 1);
    for (i = 0; i < trees; i ++)
    {
      float htree = 0.0025 + 0.00015 * ((3 * y + 2 * x + 4 * i) & 7);
      drawTree (xtree [i + rotval] + x, ytree [i + rotval] + y, htree, htree * 80, phi);
    }
  }
  else if (f [xs] [ys] == REDTREE0)
  {
    texturetree1 = textree4->textureID;
    if (hq) texturetree2 = textreeu4->textureID;
    else texturetree2 = -1;
    drawTree (x, y, 0.004, 0.35, phi);
  }
  else if (f [xs] [ys] == CACTUS0)
  {
    texturetree1 = texcactus1->textureID;
    if (hq) texturetree2 = texcactusu1->textureID;
    else texturetree2 = -1;
    drawTree (x, y, 0.004, 0.3, phi);
  }
}

void GLLandscape::drawTown (int x, int y)
{
  int xs = getCoord (x);
  int ys = getCoord (y);
  if (f [xs] [ys] == TOWN)
  {
    CVector3 tl;
    CRotation rot;
    rot.a = 270;
    rot.c = 90 * ((xs + ys / 3) & 3);
    tl.set (x + 0.5, getExactHeight ((float) xs + 0.5, (float) ys + 0.5) + 0.2, y + 0.5);
    glPushMatrix ();
    model_house1.draw (&tl, NULL, &rot, 0.3, 1, 0);
    glPopMatrix ();
    return;
  }
}

int visibility = 0;

// Fast landscape rendering without textures
void GLLandscape::drawQuadStrip (int x1, int y1, int x2, int y2)
{
  int x, y, xs, ys;
  float cr, cg, cb;
  bool water = false;
  bool last = false;
  int step = fargridstep;
  float texred, texgreen, texblue;

  va = &vertexarrayquadstrip;

  glDisable (GL_TEXTURE_2D);

  CTexture *tex;

  x1 -= x1 % step;
  y1 -= y1 % step;

  for (xs = x1; xs < x2;)
  {
    x = GETCOORD(xs);
    for (ys = y1; ys < y2;)
    {
      y = GETCOORD(ys);
      int xstep = GETCOORD(x + step);
      int y2 = GETCOORD(y + step);
      if (gl->isSphereInFrustum ((float) xs + 0.5F * step, (float)(hw[x][y]+hw[xstep][y])*0.5, (float) ys, step*8))
      {
        int a = f [x] [y];
        if (a >= 40 && a <= 49)
          water = true;
		    int x2 = xstep;
        int y0 = GETCOORD(y - step);
		    if (!(h [x] [y] < hw [x] [y] && h [x2] [y] < hw [x2] [y] &&
              h [x] [y2] < hw [x] [y2] && h [x2] [y2] < hw [x2] [y2] &&
              h [x] [y0] < hw [x] [y0] && h [x2] [y0] < hw [x2] [y0]))
        {
          if (!last)
          {
            last = true;
            va->glBegin (GL_QUAD_STRIP);
          }
          tex = texmap [a];
          if (tex == NULL)
          {
            texred = 1.0F;
            texgreen = 1.0F;
            texblue = 1.0F;
          }
          else
          {
            texred = tex->texred;
            texgreen = tex->texgreen;
            texblue = tex->texblue;
          }
          float fac = lightfac * (nl [x] [y] + (short) dl [x] [y] * 16) * sunlight;
          cr = (float) r [x] [y] * fac * texred;
          cg = (float) g [x] [y] * fac * texgreen;
          cb = (float) b [x] [y] * fac * texblue;
          if (cr >= texred) cr = texred;
          if (cg >= texgreen) cg = texgreen;
          if (cb >= texblue) cb = texblue;
          va->glColor3f (cr, cg, cb);
          va->glVertex3f (xs, (float)h[x][y], (ys));
          fac = lightfac * (nl [xstep] [y] + (short) dl [xstep] [y] * 16) * sunlight;
          cr = (float) r [x + step] [y] * fac * texred;
          cg = (float) g [x + step] [y] * fac * texgreen;
          cb = (float) b [x + step] [y] * fac * texblue;
          if (cr >= texred) cr = texred;
          if (cg >= texgreen) cg = texgreen;
          if (cb >= texblue) cb = texblue;
          va->glColor3f (cr, cg, cb);
          va->glVertex3f ((xs+step), (float)h[xstep][y], (ys));
        }
        else
        {
          if (last)
            va->glEnd ();
          last = false;
        }
      }
      ys += step;
    }
    if (last)
    {
      va->glEnd ();
      last = false;
    }
    xs += step;
  }

  last = false;
  if (water)
  {
    float texlight;
    float watergreen = 0.00025;
    if (day) watergreen = 0.0004;
    for (xs = x1; xs < x2;)
    {
      x = GETCOORD(xs);
      for (ys = y1; ys < y2;)
      {
        y = GETCOORD(ys);
        int xstep = GETCOORD(x + step);
        int ystep = GETCOORD(y + step);
        int ymstep = GETCOORD(y - step);
        if (isWater (f [x] [y]) || isWater (f [xstep] [y]) || isWater (f [xstep] [ystep]) || isWater (f [x] [ystep]) ||
            isWater (f [x] [ymstep]) || isWater (f [xstep] [ymstep]))
        {
          float h1 = hw [x] [y];
          if (hw [xstep] [ystep] < h1) h1 = hw [xstep] [ystep];
          if (hw [xstep] [y] < h1) h1 = hw [xstep] [y];
          if (hw [x] [ystep] < h1) h1 = hw [x] [ystep];
          if (hw [x] [ymstep] < h1) h1 = hw [x] [ymstep];
          if (hw [xstep] [ymstep] < h1) h1 = hw [xstep] [ymstep];
          if (gl->isSphereInFrustum ((xs), (float)h1, (ys), step*2))
          {
            if (!last)
            {
              last = true;
              va->glBegin (GL_QUAD_STRIP);
            }

            texlight = texwater->texlight;
            float d = watergreen * (h1 - h [x] [y]);
            if (d > 0.75) d = 0.75;
            if (type == LAND_ALPINE)
            {
              cr = 0.1 * 256;
              cg = (0.85 - d) * 256;
              cb = (0.6 + d) * 256;
            }
            else if (type == LAND_CANYON)
            {
              cr = (0.55 - d/2) * 256;
              cg = (0.55 - d/2) * 256;
              cb = (0.6 + d) * 256;
            }
            else if (type == LAND_ARCTIC)
            {
              cr = (0.7 - d/2) * 256;
              cg = (0.7 - d/2) * 256;
              cb = (0.7 + d / 2) * 256;
            }
            else
            {
              cr = 0; cg = 0; cb = 0;
            }
            float fac = lightfac * (nl [x] [y] + (short) dl [x] [y] * 16) * sunlight * texlight;
            cr = (float) cr * fac;
            cg = (float) cg * fac;
            cb = (float) cb * fac;
            if (cr > texlight) cr = texlight;
            if (cg < 0.1 * texlight) cg = 0.1 * texlight;
            if (cg > texlight) cg = texlight;
            if (cb > texlight) cb = texlight;
            va->glColor3f (cr, cg, cb);
            va->glVertex3f (xs, h1, (ys));

            d = watergreen * (h1 - h [xstep] [y]);
            if (d > 0.75) d = 0.75;
            if (type == LAND_ALPINE)
            {
              cr = 0.1 * 256;
              cg = (0.85 - d) * 256;
              cb = (0.6 + d) * 256;
            }
            else if (type == LAND_CANYON)
            {
              cr = (0.55 - d/2) * 256;
              cg = (0.55 - d/2) * 256;
              cb = (0.6 + d) * 256;
            }
            else if (type == LAND_ARCTIC)
            {
              cr = (0.7 - d/2) * 256;
              cg = (0.7 - d/2) * 256;
              cb = (0.7 + d / 2) * 256;
            }
            fac = lightfac * (nl [xstep] [y] + (short) dl [xstep] [y] * 16) * sunlight * texlight;
            cr = (float) cr * fac;
            cg = (float) cg * fac;
            cb = (float) cb * fac;
            if (cr > texlight) cr = texlight;
            if (cg < 0.1 * texlight) cg = 0.1 * texlight;
            if (cg > texlight) cg = texlight;
            if (cb > texlight) cb = texlight;
            va->glColor3f (cr, cg, cb);
            va->glVertex3f ((xs+step), h1, (ys));
          }
          else
          {
            if (last)
              va->glEnd ();
            last = false;
          }
        }
        ys += step;
      }
      if (last)
      {
        va->glEnd ();
        last = false;
      }
      xs += step;
    }
  }
}

// Draw a single untextured quad
void GLLandscape::drawQuad (int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
{
  int j;
  int step = fargridstep;
  float texred, texgreen, texblue;
  CTexture *tex = NULL;
  float col [4] [3];
  float pos [4] [3];
  float fac;
  int px [4], py [4];
  int xs = x1, ys = y1;
  int x = GETCOORD(xs);
  int y = GETCOORD(ys);
  px [0] = x1; py [0] = y1;
  px [1] = x2; py [1] = y2;
  px [2] = x3; py [2] = y3;
  px [3] = x4; py [3] = y4;
  float minh = h[x][y];
  float maxh = minh;
  for (j = 1; j < 4; j ++)
  {
    int h1 = h [GETCOORD(px [j])] [GETCOORD(py [j])];
    if (h1 > maxh) maxh = h1;
    else if (h1 < minh) minh = h1;
  }
  float midh = (minh + maxh) / 2;
  float size = (maxh - minh) * step; // exakt w�e mal 0.5
  if (size < 1.0 / 2 * step)
    size = 1.0 / 2 * step;
  if (!gl->isSphereInFrustum ((0.5+xs), midh, (0.5+ys), size * 2))
    return;

  va = &vertexarrayquad [0];

  int a = f [x] [y];
  tex = texmap [a];
  if (tex == NULL)
  {
    texred = 1.0F;
    texgreen = 1.0F;
    texblue = 1.0F;
  }
  else
  {
    texred = tex->texred;
    texgreen = tex->texgreen;
    texblue = tex->texblue;
  }
//  glDisable (GL_TEXTURE_2D);
  float fac2 = lightfac * sunlight;
  for (j = 0; j < 4; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my] * 16);
    col [j] [0] = r [mx] [my] * fac * texred;
    col [j] [1] = g [mx] [my] * fac * texgreen;
    col [j] [2] = b [mx] [my] * fac * texblue;
    if (col [j] [0] >= texred) col [j] [0] = texred;
    if (col [j] [1] >= texgreen) col [j] [1] = texgreen;
    if (col [j] [2] >= texblue) col [j] [2] = texblue;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }
  for (j = 0; j < 4; j ++)
  {
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
}

// Draw a single untextured triangle
void GLLandscape::drawTriangle (int x1, int y1, int x2, int y2, int x3, int y3)
{
  int j;
  int step = fargridstep;
  float texred, texgreen, texblue;
  CTexture *tex = NULL;
  float col [3] [3];
  float pos [3] [3];
  float fac;
  int px [3], py [3];
  int xs = x1, ys = y1;
  int x = GETCOORD(xs);
  int y = GETCOORD(ys);
  px [0] = x1; py [0] = y1;
  px [1] = x2; py [1] = y2;
  px [2] = x3; py [2] = y3;
  float minh = h[x][y];
  float maxh = minh;
  for (j = 1; j < 3; j ++)
  {
    int h1 = h [GETCOORD(px [j])] [GETCOORD(py [j])];
    if (h1 > maxh) maxh = h1;
    else if (h1 < minh) minh = h1;
  }
  float midh = (minh + maxh) / 2;
  float size = (maxh - minh) * step; // exakt w�e mal 0.5
  if (size < 1.0 / 2 * step)
    size = 1.0 / 2 * step;
  if (!gl->isSphereInFrustum ((0.5+xs), midh, (0.5+ys), size * 2))
    return;

  va = &vertexarraytriangle [0];

  int a = f [x] [y];
  tex = texmap [a];
  if (tex == NULL)
  {
    texred = 1.0F;
    texgreen = 1.0F;
    texblue = 1.0F;
  }
  else
  {
    texred = tex->texred;
    texgreen = tex->texgreen;
    texblue = tex->texblue;
  }
//  glDisable (GL_TEXTURE_2D);
  float fac2 = lightfac * sunlight;
  for (j = 0; j < 3; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my] * 16);
    col [j] [0] = r [mx] [my] * fac * texred;
    col [j] [1] = g [mx] [my] * fac * texgreen;
    col [j] [2] = b [mx] [my] * fac * texblue;
    if (col [j] [0] >= texred) col [j] [0] = texred;
    if (col [j] [1] >= texgreen) col [j] [1] = texgreen;
    if (col [j] [2] >= texblue) col [j] [2] = texblue;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }
//  glBegin (GL_TRIANGLES);
  for (j = 0; j < 3; j ++)
  {
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
//  glEnd();
}

// Draw a single textured quad
void GLLandscape::drawTexturedQuad (int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
{
  int j;
  int step = gridstep;
  bool texture = false;
  float col [4] [3];
  float pos [4] [3];
  float tf [4] [2];
  float fac;
  float texzoom;
  int px [4], py [4];
  int x = GETCOORD(x2);
  int y = GETCOORD(y2);
  px [0] = x1; py [0] = y1;
  px [1] = x2; py [1] = y2;
  px [2] = x3; py [2] = y3;
  px [3] = x4; py [3] = y4;
  float minh = h[x][y];
  float maxh = minh;
  for (j = 1; j < 4; j ++)
  {
    int h1 = h [GETCOORD(px [j])] [GETCOORD(py [j])];
    if (h1 > maxh) maxh = h1;
    else if (h1 < minh) minh = h1;
  }
  float midh = (minh + maxh) / 2;
  float size = (maxh - minh) * step; // exakt w�e mal 0.5
  if (size < 1.0 / 2 * step)
    size = 1.0 / 2 * step;
  if (!gl->isSphereInFrustum ((0.5+x2), midh, (0.5+y2), size * 2))
    return;
  if (tex1 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarrayquad [0];
//    glDisable (GL_TEXTURE_2D);
  }
  else
  {
    texture = true;
    va = &vertexarrayquad [tex1 [x] [y] + 1];
//    gl->enableTextures (tex1 [x] [y]);
  }
  int texcoord = 0;
  if (tex1 [x] [y] == texredstone->textureID)
  {
    texzoom = 0.5;
    texcoord = 1;
  }
  else if (tex1 [x] [y] != texgrass->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }
  float fac2 = lightfac * sunlight;
  for (j = 0; j < 4; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my] * 16);
    col [j] [0] = r [mx] [my] * fac;
    col [j] [1] = g [mx] [my] * fac;
    col [j] [2] = b [mx] [my] * fac;
    if (col [j] [0] >= 1.0) col [j] [0] = 1.0;
    if (col [j] [1] >= 1.0) col [j] [1] = 1.0;
    if (col [j] [2] >= 1.0) col [j] [2] = 1.0;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }
  for (j = 0; j < 4; j ++)
  {
    if (texcoord == 0)
    {
      tf [j] [0] = (float) px [j] * texzoom;
      tf [j] [1] = (float) py [j] * texzoom;
    }
    else
    {
      tf [j] [0] = (float) px [j] * texzoom;
      tf [j] [1] = (float) h [GETCOORD(px [j])] [GETCOORD(py [j])] * texzoom / 400.0;
    }
  }
  for (j = 0; j < 4; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
}

// Draw a single textured triangle
void GLLandscape::drawTexturedTriangle (int x1, int y1, int x2, int y2, int x3, int y3)
{
  int j;
  int step = gridstep;
  bool texture = false;
  float col [3] [3];
  float pos [3] [3];
  float tf [3] [2];
  float fac;
  float texzoom;
  int px [3], py [3];
  int x = GETCOORD(x2);
  int y = GETCOORD(y2);
  px [0] = x1; py [0] = y1;
  px [1] = x2; py [1] = y2;
  px [2] = x3; py [2] = y3;
  float minh = h[x][y];
  float maxh = minh;
  for (j = 1; j < 3; j ++)
  {
    int h1 = h [GETCOORD(px [j])] [GETCOORD(py [j])];
    if (h1 > maxh) maxh = h1;
    else if (h1 < minh) minh = h1;
  }
  float midh = (minh + maxh) / 2;
  float size = (maxh - minh) * step; // exakt w�e mal 0.5
  if (size < 1.0 / 2 * step)
    size = 1.0 / 2 * step;
  if (!gl->isSphereInFrustum ((0.5+x2), midh, (0.5+y2), size * 2))
    return;
  if (tex1 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarraytriangle [0];
//    glDisable (GL_TEXTURE_2D);
  }
  else
  {
    texture = true;
    va = &vertexarraytriangle [tex1 [x] [y] + 1];
//    gl->enableTextures (tex1 [x] [y]);
  }
  int texcoord = 0;
  if (tex1 [x] [y] == texredstone->textureID)
  {
    texzoom = 0.5;
    texcoord = 1;
  }
  else if (tex1 [x] [y] != texgrass->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }
  float fac2 = lightfac * sunlight;
  for (j = 0; j < 3; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my] * 16);
    col [j] [0] = r [mx] [my] * fac;
    col [j] [1] = g [mx] [my] * fac;
    col [j] [2] = b [mx] [my] * fac;
    if (col [j] [0] >= 1.0) col [j] [0] = 1.0;
    if (col [j] [1] >= 1.0) col [j] [1] = 1.0;
    if (col [j] [2] >= 1.0) col [j] [2] = 1.0;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }
  for (j = 0; j < 3; j ++)
  {
    if (texcoord == 0)
    {
      tf [j] [0] = (float) px [j] * texzoom;
      tf [j] [1] = (float) py [j] * texzoom;
    }
    else
    {
      tf [j] [0] = (float) px [j] * texzoom;
      tf [j] [1] = (float) h [GETCOORD(px [j])] [GETCOORD(py [j])] * texzoom / 400.0;
    }
  }
  for (j = 0; j < 3; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
}

// Draw a single textured quad
void GLLandscape::drawTexturedQuad (int xs, int ys)
{
  int j;
  int step = gridstep;
  bool texture = false;
  float col [4] [3];
  float pos [4] [3];
  float tf [4] [2];
  float fac;
  float texzoom;
  int px [4], py [4];
  int pcx [4], pcy [4];
  px [0] = xs; py [0] = ys;
  px [1] = xs + step; py [1] = ys;
  px [2] = xs + step; py [2] = ys + step;
  px [3] = xs; py [3] = ys + step;
  for (j = 0; j < 4; j ++)
  {
    pcx [j] = GETCOORD(px [j]);
    pcy [j] = GETCOORD(py [j]);
  }
  int x = GETCOORD(pcx [0]);
  int y = GETCOORD(pcy [0]);
  float minh = h [x] [y];
  float maxh = minh;
  for (j = 1; j < 4; j ++)
  {
    int h1 = h [pcx [j]] [pcy [j]];
    if (h1 > maxh) maxh = h1;
    else if (h1 < minh) minh = h1;
  }
  float midh = (minh + maxh) / 2;
  float size = (maxh - minh) * step; // exakt w�e mal 0.5
  if (size < 1.0 / 2 * step)
    size = 1.0 / 2 * step;
  if (!gl->isSphereInFrustum ((0.5+xs), midh, (0.5+ys), size * 2))
    return;
  if (tex1 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarrayquad [0];
//    glDisable (GL_TEXTURE_2D);
  }
  else
  {
    texture = true;
    va = &vertexarrayquad [tex1 [x] [y] + 1];
//    gl->enableTextures (tex1 [x] [y]);
  }
  int texcoord = 0;
  if (tex1 [x] [y] == texredstone->textureID)
  {
    texzoom = 0.5;
    texcoord = 1;
  }
  else if (tex1 [x] [y] != texgrass->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }
  float fac2 = lightfac * sunlight;
  for (j = 0; j < 4; j ++)
  {
    int mx = pcx [j], my = pcy [j];
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my] * 16);
    col [j] [0] = (float) r [mx] [my] * fac;
    col [j] [1] = (float) g [mx] [my] * fac;
    col [j] [2] = (float) b [mx] [my] * fac;
    if (col [j] [0] >= 1.0) col [j] [0] = 1.0;
    if (col [j] [1] >= 1.0) col [j] [1] = 1.0;
    if (col [j] [2] >= 1.0) col [j] [2] = 1.0;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }
  for (j = 0; j < 4; j ++)
  {
    if (texcoord == 0)
    {
      tf [j] [0] = (float) px [j] * texzoom;
      tf [j] [1] = (float) py [j] * texzoom;
    }
    else
    {
      tf [j] [0] = (float) px [j] * texzoom;
      tf [j] [1] = (float) h [pcx [j]] [pcy [j]] * texzoom / 400.0;
    }
  }
  for (j = 0; j < 4; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
}

// Draw a single textured water quad
void GLLandscape::drawWaterTexturedQuad (int xs, int ys)
{
  int i, j;
  int step = gridstep;
  bool texture = false;
  float col [4] [4];
  float pos [4] [3];
  float tf [4] [2];
  float li [4];
  float fac;
  float texzoom;
  int px [4], py [4];
  int x = GETCOORD(xs);
  int y = GETCOORD(ys);
  int xstep = GETCOORD(xs + step);
  int ystep = GETCOORD(ys + step);
  px [0] = xs; py [0] = ys; li [0] = (nl [x] [y] + (short) dl [x] [y] * 16);
  px [1] = xs + step; py [1] = ys; li [1] = (nl [xstep] [y] + (short) dl [xstep] [y] * 16);
  px [2] = xs + step; py [2] = ys + step; li [2] = (nl [xstep] [ystep] + (short) dl [xstep] [ystep] * 16);
  px [3] = xs; py [3] = ys + step; li [3] = (nl [x] [ystep] + (short) dl [x] [ystep] * 16);

  float h1 = hw [x] [y];
  for (i = 1; i <= 3; i ++)
  {
    int mx = GETCOORD(px [i]);
    int my = GETCOORD(py [i]);
    if (hw [mx] [my] < h1)
    {
      h1 = hw [mx] [my];
    }
  }

  if (!gl->isSphereInFrustum ((0.5+xs), (float) h1, (0.5+ys), step))
    return;

  float quadglittering = 0;
  float glitter [4] = { 1, 1, 1, 1 };
  if (specialeffects)
  if (weather == WEATHER_SUNNY || weather == WEATHER_CLOUDY)
  {
    float dz1 = fabs ((float) camx - xs);
    float dz2 = fabs ((float) camx - xs - step);
    float dy = fabs (camy - (h1*zoomz - zoomz2) * ZOOM);
    float dtheta1 = fabs (atan (dy / dz1) * 180.0 / PI - 90);
    float dtheta2 = fabs (atan (dy / dz2) * 180.0 / PI - 90);
    dtheta1 /= 4; dtheta2 /= 4;
//    if (lz1 <= 5 || lz2 <= 5)
    {
      float divdy = 1.0F / dy * 200;
      float dx1 = ((float) -camz + ys);
      float dx2 = ((float) -camz + ys + step);
//      float dy = fabs (camy - (h1*zoomz - zoomz2) * ZOOM);
      float dgamma1 = fabs (atan (dy / dx1) * 180.0 / PI - sungamma);
      float dgamma2 = fabs (atan (dy / dx2) * 180.0 / PI - sungamma);
      dgamma1 /= 4; dgamma2 /= 4;
      float sc = 1.0;
      float test;
      if (dx1 < 0) dgamma1 += 90;
      if (dx2 < 0) dgamma2 += 90;
      if (h1 >= hray [x] [y])
      {
        test = sc * exp ((-dgamma1 * dgamma1 - dtheta1 * dtheta1) / divdy) + 0.98;
        if (test > 1.0)
        {
          glitter [0] = test;
          if (test > glittering) glittering = test;
          if (test > quadglittering) quadglittering = test;
        }
      }
      if (h1 >= hray [xstep] [y])
      {
        test = sc * exp ((-dgamma1 * dgamma1 - dtheta2 * dtheta2) / divdy) + 0.98;
        if (test > 1.0)
        {
          glitter [1] = test;
          if (test > glittering) glittering = test;
          if (test > quadglittering) quadglittering = test;
        }
      }
      if (h1 >= hray [x] [ystep])
      {
        test = sc * exp ((-dgamma2 * dgamma2 - dtheta1 * dtheta1) / divdy) + 0.98;
        if (test > 1.0)
        {
          glitter [3] = test;
          if (test > glittering) glittering = test;
          if (test > quadglittering) quadglittering = test;
        }
      }
      if (h1 >= hray [xstep] [ystep])
      {
        test = sc * exp ((-dgamma2 * dgamma2 - dtheta2 * dtheta2) / divdy) + 0.98;
        if (test > 1.0)
        {
          glitter [2] = test;
          if (test > glittering) glittering = test;
          if (test > quadglittering) quadglittering = test;
        }
      }
    }
  }

  texture = true;
  va = &vertexarrayquad [texwater->textureID + 1];
  gl->enableTextures (texwater->textureID);
  texzoom = 0.5;
  float watergreen = 0.00025;
  if (day) watergreen = 0.0004;
  float fac2 = lightfac * sunlight * 256.0;
  for (j = 0; j < 4; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    float d = watergreen * (h1 - h [mx] [my]);
    if (d > 0.75) d = 0.75;
    fac = fac2 * li [j];
    if (type == LAND_ALPINE)
    {
      col [j] [0] = 0.1 * fac;
      col [j] [1] = (0.85 - d) * fac;
      col [j] [2] = (0.6 + d) * fac;
    }
    else if (type == LAND_CANYON)
    {
      col [j] [0] = (0.55 - d/2) * fac;
      col [j] [1] = (0.55 - d/2) * fac;
      col [j] [2] = (0.6 + d) * fac;
    }
    else if (type == LAND_ARCTIC)
    {
      col [j] [0] = (0.7 - d/2) * fac;
      col [j] [1] = (0.7 - d/2) * fac;
      col [j] [2] = (0.7 + d / 2) * fac;
    }
    if (col [j] [0] >= 1.0) col [j] [0] = 1.0;
    if (col [j] [1] <= 0.1) col [j] [1] = 0.1;
    if (col [j] [1] >= 1.0) col [j] [1] = 1.0;
    if (col [j] [2] >= 1.0) col [j] [2] = 1.0;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h1;
    pos [j] [2] = py[j];
  }
  for (j = 0; j < 4; j ++)
  {
    float waterspeed = 0.008;
    if (weather == 1) waterspeed = 0.016;
    tf [j] [0] = (float) px [j] * texzoom + waterspeed * lsticker / timestep;
    tf [j] [1] = (float) py [j] * texzoom;
  }
  
  for (j = 0; j < 4; j ++)
  {
    if (texture)
    {
      va->glTexCoord2fv (tf [j]);
    }
    va->glColor4fv (col [j]);
    va->glVertex3fv (pos [j]);
  }

  va = &vertexarrayglitter [0];
  if (specialeffects && quadglittering > 1.2)
  {
    glEnable (GL_BLEND);
    glDepthFunc (GL_LEQUAL);
    glBlendFunc (GL_ONE, GL_SRC_ALPHA);
    glEnable (GL_ALPHA_TEST);
    glAlphaFunc (GL_GEQUAL, 0.2);
    gl->enableTextures (texglitter1->textureID);
    gl->enableLinearTexture (texglitter1->textureID);
    for (j = 0; j < 4; j ++)
    {
      if (texture)
      {
        tf [j] [0] = (px [j] * texzoom) + (float) ((lsticker / timestep / 2) & 7) * 0.6;
        tf [j] [1] = (py [j] * texzoom) + (float) ((lsticker / timestep / 2) & 7) * 0.6;
        va->glTexCoord2fv (tf [j]);
      }
      col [j] [3] = glitter [j] - 1.0;
      col [j] [0] = 1.0;
      col [j] [1] = 1.0;
      col [j] [2] = 1.0;
      va->glColor4fv (col [j]);
      va->glVertex3fv (pos [j]);
    }
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable (GL_ALPHA_TEST);
    glDisable (GL_BLEND);
  }

  va = &vertexarrayglitter [1];
  if (specialeffects && quadglittering > 1.02)
  {
    glEnable (GL_BLEND);
    glDepthFunc (GL_LEQUAL);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable (GL_ALPHA_TEST);
    glAlphaFunc (GL_GEQUAL, 0.02);
    glDisable (GL_TEXTURE_2D);
    for (j = 0; j < 4; j ++)
    {
      col [j] [3] = glitter [j] - 1.0;
      col [j] [0] = 1.0;
      col [j] [1] = 1.0;
      col [j] [2] = 1.0;
      va->glColor4fv (col [j]);
      va->glVertex3fv (pos [j]);
    }
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDisable (GL_ALPHA_TEST);
    glDisable (GL_BLEND);
  }

}

// Draw two textured triangles (quad)
void GLLandscape::drawTexturedTriangle1 (int xs, int ys)
{
  int j;
  int step = gridstep;
  bool texture = false;
  float col [4] [3];
  float pos [4] [3];
  float tf [4] [2];
  float fac;
  float texzoom;
  int px [4], py [4];
  int x = GETCOORD(xs);
  int y = GETCOORD(ys);
  px [0] = xs; py [0] = ys;
  px [1] = xs + step; py [1] = ys;
  px [2] = xs + step; py [2] = ys + step;
  px [3] = xs; py [3] = ys + step;

  if (!gl->isSphereInFrustum ((0.5+xs), (float)h[x][y], (0.5+ys), 2*step))
    return;

  if (tex1 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarraytriangle [0];
//    glDisable (GL_TEXTURE_2D);
  }
  else
  {
    texture = true;
    va = &vertexarraytriangle [tex1 [x] [y] + 1];
//    gl->enableTextures (tex1 [x] [y]);
  }
  if (tex1 [x] [y] != texgrass->textureID && tex1 [x] [y] != texgrass->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }

  float fac2 = lightfac * sunlight;

  for (j = 0; j < 4; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my] * 16);
    col [j] [0] = r [mx] [my] * fac;
    col [j] [1] = g [mx] [my] * fac;
    col [j] [2] = b [mx] [my] * fac;
    if (col [j] [0] >= 1.0) col [j] [0] = 1.0;
    if (col [j] [1] >= 1.0) col [j] [1] = 1.0;
    if (col [j] [2] >= 1.0) col [j] [2] = 1.0;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }

  for (j = 0; j < 4; j ++)
  {
    tf [j] [0] = (float) px [j] * texzoom;
    tf [j] [1] = (float) py [j] * texzoom;
  }

  for (j = 0; j < 3; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }

  if (tex2 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarraytriangle [0];
  }
  else
  {
    texture = true;
    va = &vertexarraytriangle [tex2 [x] [y] + 1];
  }

  if (tex2 [x] [y] != texgrass->textureID && tex2 [x] [y] != texredstone->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }

  for (j = 0; j < 4; j ++)
  {
    tf [j] [0] = (float) px [j] * texzoom;
    tf [j] [1] = (float) py [j] * texzoom;
  }

  if (texture)
    va->glTexCoord2fv (tf [0]);
  va->glColor3fv (col [0]);
  va->glVertex3fv (pos [0]);
  for (j = 2; j < 4; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
}

// Draw two textured triangles (quad)
void GLLandscape::drawTexturedTriangle2 (int xs, int ys)
{
  int j;
  int step = gridstep;
  bool texture = false;
  float col [4] [3];
  float pos [4] [3];
  float tf [4] [2];
  float fac;
  float texzoom;
  int px [4], py [4];
  int x = GETCOORD(xs);
  int y = GETCOORD(ys);
  px [0] = xs; py [0] = ys;
  px [1] = xs + step; py [1] = ys;
  px [2] = xs + step; py [2] = ys + step;
  px [3] = xs; py [3] = ys + step;

  if (!gl->isSphereInFrustum ((0.5+xs), (float)h[x][y], (0.5+ys), 2*step))
    return;

  if (tex1 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarraytriangle [0];
//    glDisable (GL_TEXTURE_2D);
  }
  else
  {
    texture = true;
    va = &vertexarraytriangle [tex1 [x] [y] + 1];
//    gl->enableTextures (tex1 [x] [y]);
  }

  if (tex1 [x] [y] != texgrass->textureID && tex1 [x] [y] != texredstone->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }

  float fac2 = lightfac * sunlight;
  for (j = 0; j < 4; j ++)
  {
    int mx = GETCOORD(px [j]), my = GETCOORD(py [j]);
    fac = fac2 * (nl [mx] [my] + (short) dl [mx] [my]);
    col [j] [0] = r [mx] [my] * fac;
    col [j] [1] = g [mx] [my] * fac;
    col [j] [2] = b [mx] [my] * fac;
    if (col [j] [0] >= 1.0) col [j] [0] = 1.0;
    if (col [j] [1] >= 1.0) col [j] [1] = 1.0;
    if (col [j] [2] >= 1.0) col [j] [2] = 1.0;
    pos [j] [0] = px[j];
    pos [j] [1] = (float)h[mx][my];
    pos [j] [2] = py[j];
  }

  for (j = 0; j < 4; j ++)
  {
    tf [j] [0] = (float) px [j] * texzoom;
    tf [j] [1] = (float) py [j] * texzoom;
  }

//  glBegin (GL_TRIANGLES);
  for (j = 0; j < 2; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
  if (texture)
    va->glTexCoord2fv (tf [3]);
  va->glColor3fv (col [3]);
  va->glVertex3fv (pos [3]);
//  glEnd();

  if (tex2 [x] [y] == 0xFF)
  {
    texture = false;
    va = &vertexarraytriangle [0];
//    glDisable (GL_TEXTURE_2D);
  }
  else
  {
    texture = true;
    va = &vertexarraytriangle [tex2 [x] [y] + 1];
//    gl->enableTextures (tex2 [x] [y]);
  }

  if (tex2 [x] [y] != texgrass->textureID && tex2 [x] [y] != texredstone->textureID)
  {
    texzoom = 0.5;
  }
  else
  {
    texzoom = 0.25;
  }

  for (j = 0; j < 4; j ++)
  {
    tf [j] [0] = (float) px [j] * texzoom;
    tf [j] [1] = (float) py [j] * texzoom;
  }

//  glBegin (GL_TRIANGLES);
  for (j = 1; j < 4; j ++)
  {
    if (texture)
      va->glTexCoord2fv (tf [j]);
    va->glColor3fv (col [j]);
    va->glVertex3fv (pos [j]);
  }
//  glEnd();
}

void GLLandscape::draw (int phi, int gamma)
{
  char buf [STDSIZE];
  int i, i2, i3, x, y;
  int xs, ys;

  float fac;

  int fardetail = quality;
  int middetail = quality;
  int lineardetail = -1;

// until v1.2 (no vertex arrays)
/*  if (quality == 0)
  { neargridstep = 3; fargridstep = 3; middetail = -1; fardetail = -1; lineardetail = -1; }
  else if (quality == 1)
  { neargridstep = 2; fargridstep = 4; middetail = 2; fardetail = 2; lineardetail = -1; }
  else if (quality == 2)
  { neargridstep = 2; fargridstep = 4; middetail = 5; fardetail = 5; lineardetail = -1; }
  else if (quality == 3)
  { neargridstep = 2; fargridstep = 4; middetail = 7; fardetail = 7; lineardetail = 0; }
  else if (quality == 4)
  { neargridstep = 2; fargridstep = 2; middetail = -1; fardetail = 7; lineardetail = 0; }
  else
  { neargridstep = 1; fargridstep = 2; middetail = 3; fardetail = 7; lineardetail = 0; }*/

  if (quality == 0)
  { neargridstep = 3; fargridstep = 3; middetail = -1; fardetail = -1; lineardetail = -1; }
  else if (quality == 1)
  { neargridstep = 2; fargridstep = 4; middetail = 3; fardetail = 3; lineardetail = -1; }
  else if (quality == 2)
  { neargridstep = 2; fargridstep = 4; middetail = 6; fardetail = 6; lineardetail = -1; }
  else if (quality == 3)
  { neargridstep = 2; fargridstep = 2; middetail = -1; fardetail = 7; lineardetail = 0; }
  else if (quality == 4)
  { neargridstep = 1; fargridstep = 2; middetail = 3; fardetail = 7; lineardetail = 0; }
  else
  { neargridstep = 1; fargridstep = 2; middetail = 5; fardetail = 7; lineardetail = 0; }

  if (phi < 0) phi += 360;
  else if (phi >= 360) phi -= 360;

  if (gamma < 0) gamma += 360;
  else if (gamma >= 360) gamma -= 360;

  if (phi < 0 || phi >= 360)
  {
    sprintf (buf, "Phi exceeds in file %s, line %d, val %d", __FILE__, __LINE__, phi);
    display (buf, LOG_ERROR);
  }

  if (gamma < 0 || gamma >= 360)
  {
    sprintf (buf, "Gamma exceeds in file %s, line %d, val %d", __FILE__, __LINE__, gamma);
    display (buf, LOG_ERROR);
  }

  glPushMatrix ();

  glScalef (MAXX2, ZOOM, MAXX2);
  glPushMatrix ();
  glTranslatef (0, -zoomz2, 0);
  glScalef (hh2, zoomz, hh2);

  gl->extractFrustum ();
    
  float pseudoview = getView ();
  float radius = pseudoview / cosi [45];

  int minx = (int) (camx - radius);
  int miny = (int) (camz - radius);
  int maxx = (int) (minx + radius * 2);
  int maxy = (int) (miny + radius * 2);

  space->z1->x = minx - MAXX2;
  space->z1->y = -MAXX2;
  space->z1->z = maxy - MAXX2;
  space->z2->x = maxx - MAXX2;
  space->z2->y = MAXX2;
  space->z2->z = miny - MAXX2;

  if (camera == 50)
  {
    minx = 0; maxx = MAXX;
    miny = 0; maxy = MAXX;
  }

  int parts = (int) (view / 13);
  parts *= 2;
  parts ++;
  if (parts >= PARTS)
  {
    display ("view exceeds ray casting blocks - not implemented", LOG_FATAL);
    exit (6);
  }

  int mp = parts / 2;
  for (i = 0; i < parts; i ++)
    for (i2 = 0; i2 < parts; i2 ++)
    {
      float d = dist (mp - i, mp - i2);
      detail [i] [i2] = (int) (d * 200.0F / view); // do not use pseudoview
    }

  float dx = (float) (maxx - minx + 1) / parts;
  float dy = (float) (maxy - miny + 1) / parts;

  // Efficient occlusion culling (kind of ray casting technique):
  // Run from inner grid point (viewer) to outer grid parts and check if grid points are hidden
  // This is currently not completely correct (needs two comparisons of inner fields), but
  // it already works very well, so I negligate the second comparison, as it would double the code

  for (i = 0; i < parts; i ++)
    for (i2 = 0; i2 < parts; i2 ++)
    {
      int ax = minx + (int) (dx * (float) i2);
      int ay = miny + (int) (dy * (float) i);
      int zx = minx + (int) (dx * (float) (i2 + 1));
      int zy = miny + (int) (dy * (float) (i + 1));
      vmin [i] [i2] = 65535;
      vmax [i] [i2] = 0;
      for (int i3 = 0; i3 < zy - ay + 1; i3 += 4)
        for (int i4 = 0; i4 < zx - ax + 1; i4 += 4)
        {
          int by = getCoord (ay + i3) / 4;
          int bx = getCoord (ax + i4) / 4;
          if (hcmin [bx] [by] < vmin [i] [i2]) vmin [i] [i2] = hcmin [bx] [by];
          if (hcmax [bx] [by] > vmax [i] [i2]) vmax [i] [i2] = hcmax [bx] [by];
        }
    }

  // ray casting
  bool dosecondtest = false;
  int count = 0;
  bool set = true;
  memset (vis, 0, PARTS * PARTS * sizeof (bool));
  int cx = parts / 2, cy = parts / 2;
  float ch = (unsigned int) ((camy / ZOOM + zoomz2) / zoomz); // - (h1*zoomz - zoomz2) * ZOOM;
  vh [cy] [cx] = ch;
  vis [cy] [cx] = set;
  for (i = cy + 1; i < parts; i ++)
    for (i2 = parts - i - 1; i2 < i + 1; i2 ++)
    {
      int lasty = 1;
      int lastx = 0;
      if (i2 < cx) lastx = -1;
      if (i2 > cx) lastx = 1;
//      if (i2 == cx - 1 && i2 > parts - i - 1) lastx = 0;
//      if (i2 == cx + 1 && i2 < i - 1) lastx = 0;
      int vminref = (int) vh [i - lasty] [i2 - lastx];
      int deltax = cx - i2 + lastx;
      int deltay = cy - i + lasty;
      float dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
      float dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
      int dh1 = vminref - (int) ch;
      int dhp;
      if (dist1 > 1E-4)
        dhp = (int) (dist2 * dh1 / dist1);
      else
        dhp = -30000;
      int h1 = vminref + dhp;
      if (h1 < vmin [i] [i2]) h1 = vmin [i] [i2];

      // also test non-diagonal element if available
      if (dosecondtest)
      {
        bool secondtest = false;
        if (i2 < cx && i2 > parts - i - 1)
        {
          lastx = 0;
          secondtest = true;
        }
        if (i2 > cx && i2 < i)
        {
          lastx = 0;
          secondtest = true;
        }
        if (secondtest)
        {
          vminref = (int) vh [i - lasty] [i2 - lastx];
          deltax = cx - i2 + lastx;
          deltay = cy - i + lasty;
          dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
          dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
          dh1 = vminref - (int) ch;
          if (dist1 > 1E-4)
            dhp = (int) (dist2 * dh1 / dist1);
          else
            dhp = -30000;
          int h11 = vminref + dhp;
          if (h11 < vmin [i] [i2]) h11 = vmin [i] [i2];
          if (h11 < h1) h1 = h11;
        }
      }

      vh [i] [i2] = h1;
      if (vmax [i] [i2] >= h1) vis [i] [i2] = set;
      else
      { vis [i] [i2] = !set; count ++; }
    }
  for (i = cy - 1; i >= 0; i --)
    for (i2 = i; i2 < parts - i; i2 ++)
    {
      int lasty = -1;
      int lastx = 0;
      if (i2 < cx) lastx = -1;
      if (i2 > cx) lastx = 1;
      if (i2 == cx - 1 && i2 > i) lastx = 0;
      if (i2 == cx + 1 && i2 < parts - 1) lastx = 0;
      int vminref = (int) vh [i - lasty] [i2 - lastx];
      int deltax = cx - i2 + lastx;
      int deltay = cy - i + lasty;
      float dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
      float dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
      int dh1 = vminref - (int) ch;
      int dhp;
      if (dist1 > 1E-4)
        dhp = (int) (dist2 * dh1 / dist1);
      else
        dhp = -30000;
      int h1 = vminref + dhp;
      if (h1 < vmin [i] [i2]) h1 = vmin [i] [i2];

      // also test non-diagonal element if available
      if (dosecondtest)
      {
        bool secondtest = false;
        if (i2 < cx && i2 > i)
        {
          lastx = 0;
          secondtest = true;
        }
        if (i2 > cx && i2 < parts - i - 1)
        {
          lastx = 0;
          secondtest = true;
        }
        if (secondtest)
        {
          vminref = (int) vh [i - lasty] [i2 - lastx];
          deltax = cx - i2 + lastx;
          deltay = cy - i + lasty;
          dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
          dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
          dh1 = vminref - (int) ch;
          if (dist1 > 1E-4)
            dhp = (int) (dist2 * dh1 / dist1);
          else
            dhp = -30000;
          int h11 = vminref + dhp;
          if (h11 < vmin [i] [i2]) h11 = vmin [i] [i2];
          if (h11 < h1) h1 = h11;
        }
      }

      vh [i] [i2] = h1;
      if (vmax [i] [i2] >= h1) vis [i] [i2] = set;
      else
      { vis [i] [i2] = !set; count ++; }
    }
  for (i2 = cx + 1; i2 < parts; i2 ++)
    for (i = parts - i2; i < i2; i ++)
    {
      int lasty = 0;
      int lastx = 1;
      if (i < cy) lasty = -1;
      if (i > cy) lasty = 1;
      if (i == cy - 1 && i > parts - i2) lasty = 0;
      if (i == cy + 1 && i < i2) lasty = 0;
      int vminref = (int) vh [i - lasty] [i2 - lastx];
      int deltax = cx - i2 + lastx;
      int deltay = cy - i + lasty;
      float dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
      float dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
      int dh1 = vminref - (int) ch;
      int dhp;
      if (dist1 > 1E-4)
        dhp = (int) (dist2 * dh1 / dist1);
      else
        dhp = -30000;
      int h1 = vminref + dhp;
      if (h1 < vmin [i] [i2]) h1 = vmin [i] [i2];

      // also test non-diagonal element if available
      if (dosecondtest)
      {
        bool secondtest = false;
        if (i < cy && i > parts - i2)
        {
          lasty = 0;
          secondtest = true;
        }
        if (i > cy && i < i2 - 1)
        {
          lasty = 0;
          secondtest = true;
        }
        if (secondtest)
        {
          vminref = (int) vh [i - lasty] [i2 - lastx];
          deltax = cx - i2 + lastx;
          deltay = cy - i + lasty;
          dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
          dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
          dh1 = vminref - (int) ch;
          if (dist1 > 1E-4)
            dhp = (int) (dist2 * dh1 / dist1);
          else
            dhp = -30000;
          int h11 = vminref + dhp;
          if (h11 < vmin [i] [i2]) h11 = vmin [i] [i2];
          if (h11 < h1) h1 = h11;
        }
      }

      vh [i] [i2] = h1;
      if (vmax [i] [i2] >= h1) vis [i] [i2] = set;
      else
      { vis [i] [i2] = !set; count ++; }
    }
  for (i2 = cx - 1; i2 >= 0; i2 --)
    for (i = i2 + 1; i < parts - i2 - 1; i ++)
    {
      int lasty = 0;
      int lastx = -1;
      if (i < cy) lasty = -1;
      if (i > cy) lasty = 1;
      if (i == cy - 1 && i > i2 - 1) lasty = 0;
      if (i == cy + 1 && i < parts - i2 - 1) lasty = 0;
      int vminref = (int) vh [i - lasty] [i2 - lastx];
      int deltax = cx - i2 + lastx;
      int deltay = cy - i + lasty;
      float dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
      float dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
      int dh1 = vminref - (int) ch;
      int dhp;
      if (dist1 > 1E-4)
        dhp = (int) (dist2 * dh1 / dist1);
      else
        dhp = -30000;
      int h1 = vminref + dhp;
      if (h1 < vmin [i] [i2]) h1 = vmin [i] [i2];

      // also test non-diagonal element if available
      if (dosecondtest)
      {
        bool secondtest = false;
        if (i < cy && i > i2 + 1)
        {
          lasty = 0;
          secondtest = true;
        }
        if (i > cy && i < parts - i2 - 2)
        {
          lasty = 0;
          secondtest = true;
        }
        if (secondtest)
        {
          vminref = (int) vh [i - lasty] [i2 - lastx];
          deltax = cx - i2 + lastx;
          deltay = cy - i + lasty;
          dist1 = sqrt ((float) deltax * deltax + deltay * deltay);
          dist2 = sqrt ((float) lastx * lastx + lasty * lasty);
          dh1 = vminref - (int) ch;
          if (dist1 > 1E-4)
            dhp = (int) (dist2 * dh1 / dist1);
          else
            dhp = -30000;
          int h11 = vminref + dhp;
          if (h11 < vmin [i] [i2]) h11 = vmin [i] [i2];
          if (h11 < h1) h1 = h11;
        }
      }

      vh [i] [i2] = h1;
      if (vmax [i] [i2] >= h1) vis [i] [i2] = set;
      else
      { vis [i] [i2] = !set; count ++; }
    }
//  printf ("c=%d ", count);

//  memset (vis, 0xFF, PARTS * PARTS * sizeof (bool));


  for (i = 0; i < 20; i ++)
    vertexarrayquad [i].glBegin (GL_QUADS);
  for (i = 0; i < 20; i ++)
    vertexarraytriangle [i].glBegin (GL_TRIANGLES);

  vertexarrayglitter [0].glBegin (GL_QUADS);
  vertexarrayglitter [1].glBegin (GL_QUADS);



  int zz1 = 0, zz = 0;

  if (quality <= 0 || camera == 50)
  {

    drawQuadStrip (minx, miny, maxx, maxy);

  }
  else
  {

    if (mode == 0)
    {

      for (i = 0; i < parts; i ++)
        for (i2 = 0; i2 < parts; i2 ++)
        if (vis [i] [i2])
        {
          int ax = (minx + (int) (dx * (float) i2));
          int ay = (miny + (int) (dy * (float) i));
          int zx = (minx + (int) (dx * (float) (i2 + 1)));
          int zy = (miny + (int) (dy * (float) (i + 1))/* + gridstep*/);

          if (fargridstep == 2)
          {
            ax -= ax & 1; ay -= ay & 1;
            zx -= zx & 1; zy -= zy & 1;
          }
          else if (fargridstep == 3)
          {
            ax -= ax % 3; ay -= ay % 3;
            zx -= zx % 3; zy -= zy % 3;
          }
          else if (fargridstep == 4)
          {
            ax -= ax & 3; ay -= ay & 3;
            zx -= zx & 3; zy -= zy & 3;
          }

          if (detail [i] [i2] > fardetail)
          {
            zy += fargridstep;
//            if (gl->isSphereInFrustum ((ax+zx)/2, (float)hw[getCoord((ax+zx)/2)][getCoord((ay+zy)/2)], ((float)(ay+zy)/2), (zx-ax)*2.0))
            if (gl->isSphereInFrustum (ax, (float)hw[getCoord(ax)][getCoord(ay)], (float)(ay), 0.00001) ||
                gl->isSphereInFrustum (ax, (float)hw[getCoord(ax)][getCoord(zy)], (float)(zy), 0.00001) ||
                gl->isSphereInFrustum (zx, (float)hw[getCoord(zx)][getCoord(ay)], (float)(ay), 0.00001) ||
                gl->isSphereInFrustum (zx, (float)hw[getCoord(zx)][getCoord(zy)], (float)(zy), 0.00001))
            {
              drawQuadStrip (ax, ay, zx, zy);
            }
            else
            {
              float sl = sunlight;
              sunlight = 10.0;
//              drawQuadStrip (ax, ay, zx, zy);
              sunlight = sl;
            }
          }
          else
          {
            if (detail [i] [i2] <= lineardetail)
            {
              gl->enableLinearTexture (texgrass->textureID);
              gl->enableLinearTexture (texgravel1->textureID);
              gl->enableLinearTexture (texredsand->textureID);
              gl->enableLinearTexture (texrocks->textureID);
              gl->enableLinearTexture (texwater->textureID);
              gl->enableLinearTexture (texredstone->textureID);
            }
            else
            {
              gl->disableLinearTexture (texgrass->textureID);
              gl->disableLinearTexture (texgravel1->textureID);
              gl->disableLinearTexture (texredsand->textureID);
              gl->disableLinearTexture (texrocks->textureID);
              gl->disableLinearTexture (texwater->textureID);
              gl->disableLinearTexture (texredstone->textureID);
            }
            if (detail [i] [i2] <= middetail)
            {
            if (i > 0) // south
            {
              if (detail [i - 1] [i2] > middetail)
              {
                if (fargridstep == 3 * neargridstep)
                {
                  ys = ay;
                  for (xs = ax; xs < zx;)
                  {
                    drawTexturedQuad (xs, ys, xs + 3 * neargridstep, ys, xs + 2 * neargridstep, ys, xs + 1 * neargridstep, ys);
                    xs += fargridstep;
                  }
                }
                else if (fargridstep == 2 * neargridstep)
                {
                  ys = ay;
                  for (xs = ax; xs < zx;)
                  {
                    drawTexturedTriangle (xs, ys, xs + 2 * neargridstep, ys, xs + 1 * neargridstep, ys);
                    xs += fargridstep;
                  }
                }
              }
            }
            if (i < parts - 1) // north
            {
              if (detail [i + 1] [i2] > middetail)
              {
                if (fargridstep == 3 * neargridstep)
                {
                  ys = zy;
                  for (xs = ax; xs < zx;)
                  {
                    drawTexturedQuad (xs, ys, xs + 1 * neargridstep, ys, xs + 2 * neargridstep, ys, xs + 3 * neargridstep, ys);
                    xs += fargridstep;
                  }
                }
                else if (fargridstep == 2 * neargridstep)
                {
                  ys = zy;
                  for (xs = ax; xs < zx;)
                  {
                    drawTexturedTriangle (xs, ys, xs + 1 * neargridstep, ys, xs + 2 * neargridstep, ys);
                    xs += fargridstep;
                  }
                }
              }
            }
            if (i2 > 0) // east
            {
              if (detail [i] [i2 - 1] > middetail)
              {
                if (fargridstep == 3 * neargridstep)
                {
                  xs = ax;
                  for (ys = ay; ys < zy;)
                  {
                    drawTexturedQuad (xs, ys, xs, ys + 1 * neargridstep, xs, ys + 2 * neargridstep, xs, ys + 3 * neargridstep);
                    ys += fargridstep;
                  }
                }
                else if (fargridstep == 2 * neargridstep)
                {
                  xs = ax;
                  for (ys = ay; ys < zy;)
                  {
                    drawTexturedTriangle (xs, ys, xs, ys + 1 * neargridstep, xs, ys + 2 * neargridstep);
                    ys += fargridstep;
                  }
                }
              }
            }
            if (i2 < parts - 1) // west
            {
              if (detail [i] [i2 + 1] > middetail)
              {
                if (fargridstep == 3 * neargridstep)
                {
                  xs = zx;
                  for (ys = ay; ys < zy;)
                  {
                    drawTexturedQuad (xs, ys, xs, ys + 3 * neargridstep, xs, ys + 2 * neargridstep, xs, ys + 1 * neargridstep);
                    ys += fargridstep;
                  }
                }
                else if (fargridstep == 2 * neargridstep)
                {
                  xs = zx;
                  for (ys = ay; ys < zy;)
                  {
                    drawTexturedTriangle (xs, ys, xs, ys + 2 * neargridstep, xs, ys + 1 * neargridstep);
                    ys += fargridstep;
                  }
                }
              }
            }
              gridstep = neargridstep;
            }
            else
            {
              gridstep = fargridstep;
            }
            for (xs = ax; xs < zx;)
            {
              x = GETCOORD(xs);
              for (ys = ay; ys < zy;)
              {
                y = GETCOORD(ys);
                zz1 ++;
				        int x2 = GETCOORD(xs+gridstep);
				        int y2 = GETCOORD(ys+gridstep);
				        if (h [x] [y] < hw [x] [y] && h [x2] [y] < hw [x2] [y] && h [x] [y2] < hw [x] [y2] && h [x2] [y2] < hw [x2] [y2])
			              ; // water
                else
                {
                  if (drawrule [x] [y] == 0)
                    drawTexturedQuad (xs, ys);
                  else if (drawrule [x] [y] == 2)
                    drawTexturedTriangle1 (xs, ys);
                  else
                    drawTexturedTriangle2 (xs, ys);
                }

                ys += gridstep;
                zz ++;
              }
              xs += gridstep;
            }
            for (xs = ax; xs < zx;)
            {
              x = GETCOORD(xs);
              for (ys = ay; ys < zy;)
              {
                y = GETCOORD(ys);
                zz1 ++;
                int xstep = GETCOORD(xs + gridstep);
                int ystep = GETCOORD(ys + gridstep);
                if (isWater (f [x] [y]) || isWater (f [xstep] [y]) || isWater (f [xstep] [ystep]) || isWater (f [x] [ystep]))
                {
                  drawWaterTexturedQuad (xs, ys);
                }
                ys += gridstep;
                zz ++;
              }
              xs += gridstep;
            }
          }

        } // for i2

    } // if mode

  }



  glDisable (GL_TEXTURE_2D);
  vertexarrayquad [0].glEnd ();
  vertexarraytriangle [0].glEnd ();
  for (i = 1; i < 20; i ++)
  {
    gl->enableTextures (i - 1);
    vertexarrayquad [i].glEnd ();
    vertexarraytriangle [i].glEnd ();
  }


  glEnable (GL_BLEND);
  glDepthFunc (GL_LEQUAL);
  glBlendFunc (GL_ONE, GL_SRC_ALPHA);
  glEnable (GL_ALPHA_TEST);
  glAlphaFunc (GL_GEQUAL, 0.2);
  gl->enableTextures (texglitter1->textureID);
  gl->enableLinearTexture (texglitter1->textureID);
  vertexarrayglitter [0].glEnd ();
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisable (GL_ALPHA_TEST);
  glDisable (GL_BLEND);

  glEnable (GL_BLEND);
  glDepthFunc (GL_LEQUAL);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable (GL_ALPHA_TEST);
  glAlphaFunc (GL_GEQUAL, 0.02);
  glDisable (GL_TEXTURE_2D);
  vertexarrayglitter [1].glEnd ();
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisable (GL_ALPHA_TEST);
  glDisable (GL_BLEND);



  glPopMatrix ();
  gl->extractFrustum ();



  int treestep = 2;
  if (quality >= 2) treestep = 1;

// Draw trees, bushes
  if (quality >= 1)
  {
    glPushMatrix ();
    glDisable (GL_CULL_FACE);
    if (quality >= 6)
    {
      gl->enableAlphaBlending ();
      glEnable (GL_ALPHA_TEST);
      glAlphaFunc (GL_GEQUAL, 0.1);
    }
    else
    {
      glEnable (GL_ALPHA_TEST);
      glAlphaFunc (GL_GEQUAL, 0.5);
    }
    gl->enableTextures (textree->textureID);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    gl->enableTextures (textree2->textureID);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    gl->enableTextures (textree3->textureID);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    gl->enableTextures (textree4->textureID);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    gl->enableTextures (textree5->textureID);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    gl->enableTextures (texcactus1->textureID);
    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    float mydep = 1000;
    if (quality == 2) mydep = 1800;
    else if (quality == 3) mydep = 2500;
    else if (quality == 4) mydep = 3200;
    else if (quality == 5) mydep = 3800;
    if (mydep > view * view) mydep = view * view;
    int cutdep = 800;

    int lineartree = -1;
    if (antialiasing) lineartree = 0;

    float treelightfac = lightfac * 1000.0 * 256.0 * 0.00085;

    for (i = 0; i < parts; i ++)
      for (i2 = 0; i2 < parts; i2 ++)
      {
        if (detail [i] [i2] > middetail)
          gridstep = fargridstep;
        else
          gridstep = neargridstep;
        if (detail [i] [i2] <= lineartree)
        {
          gl->enableLinearTexture (textree->textureID);
          gl->enableLinearTexture (textree2->textureID);
          gl->enableLinearTexture (textree3->textureID);
          gl->enableLinearTexture (textree4->textureID);
          gl->enableLinearTexture (textree5->textureID);
          gl->enableLinearTexture (texcactus1->textureID);
          gl->enableLinearTexture (textreeu->textureID);
          gl->enableLinearTexture (textreeu2->textureID);
          gl->enableLinearTexture (textreeu3->textureID);
          gl->enableLinearTexture (textreeu4->textureID);
          gl->enableLinearTexture (textreeu5->textureID);
          gl->enableLinearTexture (texcactusu1->textureID);
        }
        else
        {
          gl->disableLinearTexture (textree->textureID);
          gl->disableLinearTexture (textree2->textureID);
          gl->disableLinearTexture (textree3->textureID);
          gl->disableLinearTexture (textree4->textureID);
          gl->disableLinearTexture (textree5->textureID);
          gl->disableLinearTexture (texcactus1->textureID);
          gl->disableLinearTexture (textreeu->textureID);
          gl->disableLinearTexture (textreeu2->textureID);
          gl->disableLinearTexture (textreeu3->textureID);
          gl->disableLinearTexture (textreeu4->textureID);
          gl->disableLinearTexture (textreeu5->textureID);
          gl->disableLinearTexture (texcactusu1->textureID);
        }
        int ax = minx + (int) (dx * (float) i2);
        int ay = miny + (int) (dy * (float) i);
        int ex = minx + (int) (dx * (float) (i2 + 1));
        int ey = miny + (int) (dy * (float) (i + 1)) + treestep;
        float dep;
        if (treestep == 2)
        {
          ax -= ax & 1; ay -= ay & 1;
        }

        for (i3 = 1; i3 < 20; i3 ++)
          vertexarrayquad [i3].glBegin (GL_QUADS);
        for (i3 = 1; i3 < 20; i3 ++)
          vertexarraytriangle [i3].glBegin (GL_TRIANGLES);

        for (xs = ax; xs < ex;)
        {
          x = GETCOORD(xs);
          zz = 0;
          for (ys = ay; ys <= ey;)
          {
            y = GETCOORD(ys);
            float tdx = camx - xs;
            float tdy = camz - ys;
            dep = tdx * tdx + tdy * tdy;
            if (dep < mydep)
              if (isWoods (f [x] [y]) || isType (f [x] [y], REDTREE0) || isType (f [x] [y], CACTUS0))
                if (gl->isSphereInFrustum (hh2*(xs), (float)h[x][y]*zoomz - zoomz2, hh2*((ys)), hh2*2))
                {
                  float cg = g [x] [y];
                  fac = treelightfac * (nl [x] [y] + (short) dl [x] [y] * 16) * sunlight;
                  cg = (float) cg * fac;
                  if (cg >= 256.0) cg = 255.0;
                  treecolor.c [0] = treecolor.c [1] = treecolor.c [2] = (int) cg;
                  drawTreeQuad (xs, ys, phi, dep < cutdep);
                }
            ys += treestep;
          } // ys for
          xs += treestep;
        } // xs for

  for (i3 = 1; i3 < 20; i3 ++)
  {
    glBindTexture (GL_TEXTURE_2D, i3 - 1);
    vertexarrayquad [i3].glEnd ();
    vertexarraytriangle [i3].glEnd ();
  }

      }

    glDisable (GL_ALPHA_TEST);
    glPopMatrix ();
    gl->disableAlphaBlending ();

  }



  glDisable (GL_TEXTURE_2D);

  glPopMatrix ();

  if (quality >= 1 && hastowns)
  {
    float mydep = 1000;
    if (quality == 2) mydep = 1800;
    else if (quality == 3) mydep = 2600;
    else if (quality == 4) mydep = 3300;
    else if (quality == 5) mydep = 4000;
    if (mydep > view * view) mydep = view * view;

    for (i = 0; i < parts; i ++)
      for (i2 = 0; i2 < parts; i2 ++)
      {
        int ax = minx + (int) (dx * (float) i2);
        int ay = miny + (int) (dy * (float) i);
        int ex = minx + (int) (dx * (float) (i2 + 1));
        int ey = miny + (int) (dy * (float) (i + 1));
        float dep;
        for (xs = ax; xs < ex;)
        {
          x = GETCOORD(xs);
          zz = 0;
          for (ys = ay; ys <= ey;)
          {
            y = GETCOORD(ys);
            if (f [x] [y] == TOWN)
            {
              float tdx = camx - xs;
              float tdy = camz - ys;
              dep = tdx * tdx + tdy * tdy;
              if (dep < mydep)
                if (gl->isSphereInFrustum (hh2*(xs), (float)h[x][y]*zoomz - zoomz2, hh2*((ys)), hh2*2))
                {
                  drawTown (xs, ys);
                }
            }
            ys += 1;
          } // ys for
          xs += 1;
        } // xs for
      }
  }

  gridstep = neargridstep; // set to finer grid for ground collision detection
}

void GLLandscape::calcDynamicLight (CExplosion **explo, DynamicObj **cannon, DynamicObj **missile, DynamicObj **flare)
{
  int i, x, y;
  memset (dl, 0, (MAXX + 1) * (MAXX + 1));
  if (!dynamiclighting) return;
  for (i = 0; i < maxexplosion; i ++)
  {
    if (explo [i]->ttl > 0)
    {
      int mx = (int) explo [i]->tl->x;
      int mz = (int) explo [i]->tl->z;
      float h = explo [i]->tl->y - getHeight (explo [i]->tl->x, explo [i]->tl->z);
      if (h < 0) h = 0;
      float radius = h / 2 + 3;
      if (h < 50)
      {
        float intens = 100.0 - 2 * h;
        for (x = mx - (int) radius; x <= mx + (int) radius; x ++)
          for (y = mz - (int) radius; y <= mz + (int) radius; y ++)
          {
            int xn = GETCOORD(x);
            int yn = GETCOORD(y);
            int dx = x - mx, dy = y - mz;
            float dist = sqrt (dx*dx + dy*dy);
            if (dist < radius)
            {
              int light = (int) ((radius - dist) * intens / radius * explo [i]->zoom) + dl [xn] [yn];
              if (light > 255) light = 255;
              dl [xn] [yn] = light;
            }
          }
      }
    }
  }
  if (!day)
    for (i = 0; i < maxlaser; i ++)
    {
      if (cannon [i]->draw && cannon [i]->active)
      {
        int mx = (int) cannon [i]->tl->x;
        int mz = (int) cannon [i]->tl->z;
        float h = cannon [i]->tl->y - getHeight (cannon [i]->tl->x, cannon [i]->tl->z);
        if (h < 0) h = 0;
        float radius = h / 2 + 3;
        if (h < 15)
        {
          float intens = 75.0 - 5 * h;
          for (x = mx - (int) radius; x <= mx + (int) radius; x ++)
            for (y = mz - (int) radius; y <= mz + (int) radius; y ++)
            {
              int xn = GETCOORD(x);
              int yn = GETCOORD(y);
              int dx = x - mx, dy = y - mz;
              float dist = sqrt (dx*dx + dy*dy);
              if (dist < radius)
              {
                int light = (int) ((radius - dist) * intens / radius * cannon [i]->zoom) + dl [xn] [yn];
                if (light > 255) light = 255;
                dl [xn] [yn] = light;
              }
            }
        }
      }
    }
  for (i = 0; i < maxmissile; i ++)
  {
    if (missile [i]->draw && missile [i]->active)
    {
      int mx = (int) missile [i]->tl->x;
      int mz = (int) missile [i]->tl->z;
      float h = missile [i]->tl->y - getHeight (missile [i]->tl->x, missile [i]->tl->z);
      if (h < 0) h = 0;
      float radius = h / 2 + 3;
      if (h < 15)
      {
        float intens = 150.0 - 10 * h;
        for (x = mx - (int) radius; x <= mx + (int) radius; x ++)
          for (y = mz - (int) radius; y <= mz + (int) radius; y ++)
          {
            int xn = GETCOORD(x);
            int yn = GETCOORD(y);
            int dx = x - mx, dy = y - mz;
            float dist = sqrt (dx*dx + dy*dy);
            if (dist < radius)
            {
              int light = (int) ((radius - dist) * intens / radius * missile [i]->zoom) + dl [xn] [yn];
              if (light > 255) light = 255;
              dl [xn] [yn] = light;
            }
          }
      }
    }
  }
  float flarezoom = 0.2F;
  for (i = 0; i < maxflare; i ++)
  {
    if (flare [i]->draw && flare [i]->active)
    {
      int mx = (int) flare [i]->tl->x;
      int mz = (int) flare [i]->tl->z;
      float h = flare [i]->tl->y - getHeight (flare [i]->tl->x, flare [i]->tl->z);
      if (h < 0) h = 0;
      float radius = h / 2 + 3;
      if (h < 50)
      {
        float intens = 200.0 - 2 * h;
        for (x = mx - (int) radius; x <= mx + (int) radius; x ++)
          for (y = mz - (int) radius; y <= mz + (int) radius; y ++)
          {
            int xn = GETCOORD(x);
            int yn = GETCOORD(y);
            int dx = x - mx, dy = y - mz;
            float dist = sqrt (dx*dx + dy*dy);
            if (dist < radius)
            {
              int light = (int) ((radius - dist) * intens / radius * flarezoom) + dl [xn] [yn];
              if (light > 255) light = 255;
              dl [xn] [yn] = light;
            }
          }
      }
    }
  }
}

void GLLandscape::setMaterial (int n, float r, float g, float b, CTexture *tex)
{
  mat [n] [0] = r;
  mat [n] [1] = g;
  mat [n] [2] = b;
  mat [n] [3] = 1.0;
  texmap [n] = tex;
}

GLLandscape::GLLandscape (Space *space2, int type, int *heightmask)
{
  int i, i2;
  lsticker = 0;
  space = space2;
  randptr = 0;
//  if (!multiplayer || isserver)
  {
    if (type == LANDSCAPE_ALPINE || type == LANDSCAPE_ALPINE_NOLAKE || type == LANDSCAPE_LOW_ALPINE)
    {
      if (type == 0 || type == 1)
      {
        genSurface (60, heightmask);
        genRocks (30, 40);
      }
      else
      {
        genSurface (40, heightmask);
        genRocks (30, 10);
      }
      if (type == 0 || type == 2)
      {
        int lakes = myrandom (20) + 20;
        genLake (lakes);
        genLake (lakes / 3);
        genLake (lakes / 4);
        genLake (lakes / 4);
        genLake (2);
        genLake (2);
        genLake (2);
        genLake (2);
        genLake (2);
      }
      calcWoods (150);
    }
    else if (type == LANDSCAPE_ALPINE_EROSION)
    {
      genErosionSurface (50, heightmask);
      genRocks (30, 25);
      calcWoods (150);
    }
    else if (type == LANDSCAPE_ALPINE_SEA)
    {
      genSurface (60, heightmask);
      genRocks (30, 70);
      int diff = lowestpoint + (highestpoint - lowestpoint) * 3 / 4;
      for (i = 0; i <= MAXX; i ++)
        for (i2 = 0; i2 <= MAXX; i2 ++)
        {
          if (h [i] [i2] < diff)
          {
            hw [i] [i2] = diff;
            if (diff - h [i] [i2] < 1000)
              f [i] [i2] = SHALLOWWATER;
            else
              f [i] [i2] = DEEPWATER;
          }
        }
    }
    else if (type == LANDSCAPE_ALPINE_ROCKY)
    {
      genSurface (60, heightmask);
      genRocks (1, 99);
      genLake (10);
      genLake (10);
      genLake (10);
      genLake (10);
      genLake (10);
      genLake (10);
      genLake (10);
      genLake (10);
      calcWoods (200);
    }
    else if (type == LANDSCAPE_SEA)
    {
      for (i = 0; i <= MAXX; i ++)
        for (i2 = 0; i2 <= MAXX; i2 ++)
        {
          f [i] [i2] = DEEPWATER;
          h [i] [i2] = 10000;
          hw [i] [i2] = 30000;
        }
    }
    else if (type == LANDSCAPE_MOON)
    {
      genMoonSurface (60);
      for (i = 0; i <= MAXX; i ++)
        for (i2 = 0; i2 <= MAXX; i2 ++)
        {
          f [i] [i2] = MOONSAND;
        }
    }
    else if (type == LANDSCAPE_FLAT_MOON)
    {
      genMoonSurface (30);
      for (i = 0; i <= MAXX; i ++)
        for (i2 = 0; i2 <= MAXX; i2 ++)
        {
          f [i] [i2] = MOONSAND;
        }
    }
    else if (type == LANDSCAPE_CANYON)
    {
      genCanyonSurface (120);
    }
    else if (type == LANDSCAPE_ARCTIC)
    {
      genArcticSurface (60, NULL);
    }
    else if (type == LANDSCAPE_CANYON_TRENCH)
    {
      genCanyonSurface (10);
      genTrench (22, 3800);
    }
    else if (type == LANDSCAPE_DESERT)
    {
      genDesertSurface (20);
    }
  }
/*#ifdef HAVE_SDL_NET
  if (isserver)
  {
// Send map data to all clients
    char buf [10];
    for (i = 0; i <= MAXX; i ++)
    {
    printf (" %d ", i);
      server->sendMessage (1, (char *) h [i], (MAXX + 1) * 2);
      server->sendMessage (1, (char *) hw [i], (MAXX + 1) * 2);
      server->sendMessage (1, (char *) f [i], (MAXX + 1) * 1);
      while (!server->getMessage (1, buf)) ;
    }
  }
  if (multiplayer && !isserver)
  {
//    for (;;)
//    {
    for (i = 0; i <= MAXX; i ++)
    {
//    printf (" %d ", i);
      while (!client->getMessage ((char *) h [i])) ;
      while (!client->getMessage ((char *) hw [i])) ;
      while (!client->getMessage ((char *) f [i])) ;
      client->sendMessage (".", 1);
    }
  
  }
#endif*/

  lv [0] = 0.0; lv [1] = 1.0; lv [2] = 1.0;
  for (i = 0; i < MAXMATERIAL; i ++)
  {
    if (i == GRASS) { setMaterial (i, 0.4, 0.8, 0.3, texgrass); }
    else if (i >= CONIFEROUSWOODS1 && i <= MIXEDWOODS3) { setMaterial (i, 0.3, 0.55, 0.2, texgrass); }
    else if (i == ROCKS) { setMaterial (i, 0.7, 0.7, 0.7, texrocks); }
    else if (i == GLACIER) { setMaterial (i, 1.0, 1.0, 1.0, NULL); }
    else if (i >= DWARFPINES1 && i <= BUSHES3) { setMaterial (i, 0.3, 0.55, 0.2, texgrass); }
    else if (i == WATER) { setMaterial (i, 0.2, 1.0, 0.2, texwater); }
    else if (i == SHALLOWWATER) { setMaterial (i, 0.25, 1.0, 0.25, texwater); }
    else if (i == DEEPWATER) { setMaterial (i, 0.1, 0.25, 1.0, texwater); }
    else if (i == ROCKS2) { setMaterial (i, 0.5, 0.5, 0.5, texrocks); }
    else if (i == XSHALLOWWATER) { setMaterial (i, 0.3, 1.0, 0.3, texwater); }
    else if (i == XDEEPWATER) { setMaterial (i, 0.1, 0.15, 1.0, texwater); }
    else if (i == MOONSAND) { setMaterial (i, 0.8, 0.8, 0.8, texgrass); }
    else if (i == REDSTONE) { setMaterial (i, 0.95, 0.6, 0.4, texredstone); }
    else if (i == REDSAND || i == REDTREE0) { setMaterial (i, 0.9, 0.75, 0.55, texgrass); }
    else if (i == DESERTSAND || i == CACTUS0) { setMaterial (i, 1.0, 0.76, 0.35, texgrass); }
    else if (i == GREYSAND) { setMaterial (i, 0.7, 0.7, 0.65, texgrass); }
    else if (i == GRAVEL) { setMaterial (i, 0.75, 0.78, 0.68, texgravel1); }
    else if (i == TOWN) { setMaterial (i, 0.7, 0.7, 0.7, texgrass); }
    else { setMaterial (i, 0.4, 0.8, 0.3, texgrass); }
  }

/*  texmap [0] = texmap [1] = texmap [10] = texmap [12] = texmap [13] = texgrass;
  texmap [2] = texmap [7] = texrocks;
  texmap [4] = texmap [5] = texmap [6] = texmap [8] = texmap [9] = texwater;
  texmap [11] = texredstone;
  texmap [13] = texsand;
  texmap [3] = texmap [14] = texmap [15] = texmap [16] = NULL;

  if (f [x] [y] == GRASS) return 0;
  else if (f [x] [y] >= CONIFEROUSWOODS1 && f [x] [y] <= MIXEDWOODS3) return 1;
  else if (f [x] [y] == ROCKS) return 2;
  else if (f [x] [y] == GLACIER) return 3;
  else if (f [x] [y] >= DWARFPINES1 && f [x] [y] <= BUSHES3) return 1;
  else if (f [x] [y] == WATER) return 5;
  else if (f [x] [y] == SHALLOWWATER) return 4;
  else if (f [x] [y] == DEEPWATER) return 6;
  else if (f [x] [y] == ROCKS2) return 7;
  else if (f [x] [y] == XSHALLOWWATER) return 8;
  else if (f [x] [y] == XDEEPWATER) return 9;
  else if (f [x] [y] == MOONSAND) return 10;
  else if (f [x] [y] == REDSTONE) return 11;
  else if (f [x] [y] == REDSAND || f [x] [y] == REDTREE0) return 12;
  else if (f [x] [y] == DESERTSAND || f [x] [y] == CACTUS0) return 13;
  else if (f [x] [y] == GREYSAND) return 14;
  else if (f [x] [y] == GRAVEL) return 15;
  else if (f [x] [y] == TOWN) return 16;
  else return 0;

  mat [0] [0] = 0.4; mat [0] [1] = 0.8; mat [0] [2] = 0.3; mat [0] [3] = 1.0;
  mat [1] [0] = 0.3; mat [1] [1] = 0.55; mat [1] [2] = 0.2; mat [1] [3] = 1.0;
  mat [2] [0] = 0.7; mat [2] [1] = 0.7; mat [2] [2] = 0.7; mat [2] [3] = 1.0;
  mat [3] [0] = 1.0; mat [3] [1] = 1.0; mat [3] [2] = 1.0; mat [3] [3] = 1.0;
  mat [4] [0] = 0.25; mat [4] [1] = 1.0; mat [4] [2] = 0.25; mat [4] [3] = 1.0;
  mat [5] [0] = 0.2; mat [5] [1] = 1.0; mat [5] [2] = 0.2; mat [5] [3] = 1.0;
  mat [6] [0] = 0.1; mat [6] [1] = 0.25; mat [6] [2] = 1.0; mat [6] [3] = 1.0;
  mat [7] [0] = 0.5; mat [7] [1] = 0.5; mat [7] [2] = 0.5; mat [7] [3] = 1.0;
  mat [8] [0] = 0.3; mat [8] [1] = 1.0; mat [8] [2] = 0.3; mat [8] [3] = 1.0;
  mat [9] [0] = 0.1; mat [9] [1] = 0.15; mat [9] [2] = 1.0; mat [9] [3] = 1.0;
  mat [10] [0] = 0.8; mat [10] [1] = 0.8; mat [10] [2] = 0.8; mat [10] [3] = 1.0;
  mat [11] [0] = 0.95; mat [11] [1] = 0.6; mat [11] [2] = 0.4; mat [11] [3] = 1.0;
  mat [12] [0] = 0.9; mat [12] [1] = 0.75; mat [12] [2] = 0.55; mat [12] [3] = 1.0;
  mat [13] [0] = 1.0; mat [13] [1] = 0.76; mat [13] [2] = 0.35; mat [13] [3] = 1.0;
  mat [14] [0] = 0.7; mat [14] [1] = 0.7; mat [14] [2] = 0.65; mat [14] [3] = 1.0;
  mat [15] [0] = 0.75; mat [15] [1] = 0.78; mat [15] [2] = 0.68; mat [15] [3] = 1.0;
  mat [16] [0] = 0.7; mat [16] [1] = 0.7; mat [16] [2] = 0.7; mat [16] [3] = 1.0;*/

  for (i = 0; i <= MAXX; i ++)
    for (i2 = 0; i2 <= MAXX; i2 ++)
      if (hw [i] [i2] == 0)
      {
        hw [i] [i2] = h [i] [i2];
      }

  i = 0;
  while (i < 256)
  {
    bool again = false;
    xtree [i] = -0.48 + 0.001 * myrandom (960);
    ytree [i] = -0.48 + 0.001 * myrandom (960);
    for (i2 = i - 1; i2 >= 0 && i2 >= i - 6; i2 --)
    {
      if (fabs (xtree [i] - xtree [i2]) + fabs (ytree [i] - ytree [i2]) < 0.08)
      {
        again = true;
        break;
      }
    }
    if (!again) i ++;
  }

  if (type >= 0) precalculate (); // do not precalculate anything for custom height maps
}

#endif
