Blob Blame Raw

#define HMAP_SIZE 1024


typedef struct {
  double x, y, z;
  double nx, ny, nz;
} Vertex;

typedef struct {
  double (*map)[HMAP_SIZE];
  double (*old)[HMAP_SIZE];
  double maps[2][HMAP_SIZE][HMAP_SIZE];
  double normals[HMAP_SIZE][HMAP_SIZE][3];
  double stepXY;
  double stepZ;
  double color[4];
  GLuint buf;
  int bufCount;
} HMap;


void hmapInit(HMap *hmap, double stepXY, double stepZ, double r, double g, double b, double a) {
  hmap->map = hmap->maps[0];
  hmap->old = hmap->maps[1];
  for(int i = 0; i < HMAP_SIZE; ++i) {
    for(int j = 0; j < HMAP_SIZE; ++j) {
      hmap->map[i][j] = 0;
      hmap->old[i][j] = 0;
      hmap->normals[i][j][0] = 0;
      hmap->normals[i][j][1] = 0;
      hmap->normals[i][j][2] = 0;
    }
  }
  hmap->stepXY = stepXY;
  hmap->stepZ = stepZ;
  hmap->color[0] = r;
  hmap->color[1] = g;
  hmap->color[2] = b;
  hmap->color[3] = a;

  hmap->buf = 0;
  hmap->bufCount = 0;
  glGenBuffers(1, &hmap->buf);
}


void hmapDeinit(HMap *hmap) {
  glDeleteBuffers(1, &hmap->buf);
}


void hmapSwap(HMap *hmap) {
  double (*m)[HMAP_SIZE] = hmap->map;
  hmap->map = hmap->old;
  hmap->old = m;
}


void hmapCopy(HMap *hmap) {
  for(int i = 0; i < HMAP_SIZE; ++i)
    for(int j = 0; j < HMAP_SIZE; ++j)
      hmap->old[i][j] = hmap->map[i][j];
}


void hmapGetVertex(const HMap *hmap, int i, int j, Vertex *v) {
  int ii = (i%HMAP_SIZE + HMAP_SIZE)%HMAP_SIZE;
  int jj = (j%HMAP_SIZE + HMAP_SIZE)%HMAP_SIZE;
  v->x = hmap->stepXY*(i - 0.5*HMAP_SIZE);
  v->y = hmap->stepXY*(j - 0.5*HMAP_SIZE);
  v->z = hmap->stepZ*hmap->map[ii][jj];
  v->nx = hmap->normals[ii][jj][0];
  v->ny = hmap->normals[ii][jj][1];
  v->nz = hmap->normals[ii][jj][2];
}


void hmapBuildBuf(HMap *hmap) {
  static Vertex buf[ (HMAP_SIZE*2 + 4)*HMAP_SIZE*10 ];
  Vertex *v = buf;
  for(int j = 0; j < HMAP_SIZE; ++j) {
    hmapGetVertex(hmap, 0, j, v++);
    for(int i = 0; i <= HMAP_SIZE; ++i) {
      hmapGetVertex(hmap, i, j, v++);
      hmapGetVertex(hmap, i, j+1, v++);
    }
    hmapGetVertex(hmap, HMAP_SIZE, j+1, v++);
  }
  hmap->bufCount = v - buf;

  glBindBuffer(GL_ARRAY_BUFFER, hmap->buf);
  glBufferData(GL_ARRAY_BUFFER, hmap->bufCount*sizeof(*v), buf, GL_STATIC_DRAW);
  glBindBuffer(GL_ARRAY_BUFFER, 0);
}


void hmapDraw(const HMap *hmap) {
  glColor4dv(hmap->color);

  glBindBuffer(GL_ARRAY_BUFFER, hmap->buf);
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  glVertexPointer(3, GL_DOUBLE, sizeof(Vertex), (const void*)offsetof(Vertex, x));
  glNormalPointer(GL_DOUBLE, sizeof(Vertex), (const void*)offsetof(Vertex, nx));

  glDrawArrays(GL_TRIANGLE_STRIP, 0, hmap->bufCount);

  glDisableClientState(GL_VERTEX_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);
  glBindBuffer(GL_ARRAY_BUFFER, 0);

  glColor4d(1, 1, 1, 1);
}


void hmapDrawWrap(const HMap *hmap) {
  for(int i = -1; i <= 1; ++i) {
    for(int j = -1; j <= 1; ++j) {
      glPushMatrix();
      glTranslated(HMAP_SIZE*i*hmap->stepXY, HMAP_SIZE*j*hmap->stepXY, 0);
      hmapDraw(hmap);
      glPopMatrix();
    }
  }
}

double hmapGet(const HMap *hmap, int i, int j) {
  i = (i%HMAP_SIZE + HMAP_SIZE)%HMAP_SIZE;
  j = (j%HMAP_SIZE + HMAP_SIZE)%HMAP_SIZE;
  return hmap->map[i][j];
}


void hmapCalcNormals(HMap *hmap) {
  double dxy2 = 4 * hmap->stepXY * hmap->stepXY;
  for(int i = 0; i < HMAP_SIZE; ++i) {
    for(int j = 0; j < HMAP_SIZE; ++j) {
      double dzx = (hmapGet(hmap, i+1, j) - hmapGet(hmap, i-1, j))*hmap->stepZ;
      double k = 1/sqrt(dzx*dzx + dxy2);
      double nz = dxy2*k;
      double nx = -dzx*k;

      double dzy = (hmapGet(hmap, i, j+1) - hmapGet(hmap, i, j-1))*hmap->stepZ;
      k = 1/sqrt(dzy*dzy + dxy2);
      nz += dxy2*k;
      double ny = -dzy*k;

      k = 1/sqrt(nx*nx + ny*ny + nz*nz);
      hmap->normals[i][j][0] = nx*k;
      hmap->normals[i][j][1] = ny*k;
      hmap->normals[i][j][2] = nz*k;
    }
  }
}


double cubicInterpolation(double x0, double x1, double x2, double x3, float l) {
    double a = x3 - x2 + x1 - x0;
    double b = x0 - x1 - a;
    double c = x2 - x0;
    double d = x1;
    return ((a*l + b)*l + c)*l + d;
}


double hmapGetBicubic(HMap *hmap, double x, double y, int step) {
  x /= step;
  y /= step;
  int i0 = (int)floor(x);
  int j0 = (int)floor(y);
  double dx = x - i0;
  double dy = y - j0;

  double m[4][4];
  for(int i = 0; i < 4; ++i)
    for(int j = 0; j < 4; ++j)
      m[i][j]= hmapGet(hmap, (i0 + i - 1)*step, (j0 + j - 1)*step);

  return cubicInterpolation(
    cubicInterpolation(m[0][0], m[1][0], m[2][0], m[3][0], dx),
    cubicInterpolation(m[0][1], m[1][1], m[2][1], m[3][1], dx),
    cubicInterpolation(m[0][2], m[1][2], m[2][2], m[3][2], dx),
    cubicInterpolation(m[0][3], m[1][3], m[2][3], m[3][3], dx),
    dy );
}


void hmapGenerate(HMap *hmap) {
  const int maxStep = HMAP_SIZE/2;
  const int minStep = 1;
  const double maxFactor = 0.25*maxStep;

  for(int i = 0; i < HMAP_SIZE; i += maxStep)
    for(int j = 0; j < HMAP_SIZE; j += maxStep)
        hmap->map[i][j] = (randomFloat() - 0.5)*2*maxFactor;

  double factor = 0.5*maxFactor;
  for(int step = maxStep/2; step >= minStep; step /= 2, factor /= 2) {
    for(int i = step; i < HMAP_SIZE; i += 2*step)
      for(int j = step; j < HMAP_SIZE; j += 2*step)
        hmap->map[i][j] = ( hmapGet(hmap, i - step, j - step)
                          + hmapGet(hmap, i + step, j - step)
                          + hmapGet(hmap, i - step, j + step)
                          + hmapGet(hmap, i + step, j + step) )*0.25
                        + (randomFloat() - 0.5)*2*factor;
    for(int i = 0; i < HMAP_SIZE; i += step)
      for(int j = step*(1 - i/step%2); j < HMAP_SIZE; j += 2*step)
        hmap->map[i][j] = ( hmapGet(hmap, i - step, j)
                          + hmapGet(hmap, i + step, j)
                          + hmapGet(hmap, i, j - step)
                          + hmapGet(hmap, i, j + step) )*0.25
                        + (randomFloat() - 0.5)*2*factor;
  }

  if (minStep > 1)
    for(int i = 0; i < HMAP_SIZE; ++i)
      for(int j = 0; j < HMAP_SIZE; ++j)
        if (i % minStep || j % minStep)
          hmap->map[i][j] = hmapGetBicubic(hmap, i, j, minStep);

  hmapCalcNormals(hmap);
}