From 878d8304498adfda357df9841a2d8d0f5ca16e00 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Jun 22 2022 05:43:12 +0000 Subject: world-gen --- diff --git a/simple/world-gen/gl3d.inc.c b/simple/world-gen/gl3d.inc.c new file mode 100644 index 0000000..62b7fb6 --- /dev/null +++ b/simple/world-gen/gl3d.inc.c @@ -0,0 +1,95 @@ + + +double cameraPos[3] = {0, 0, -100}; +double cameraRot[3] = {30, -30, 0}; + + +void begin3d() { + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_COLOR_MATERIAL); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(60.0, (double)windowGetWidth()/(double)windowGetHeight(), 0.1, 10000.0); + + const double mm[16] = { + 0, 0, 1, 0, + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 }; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslated(cameraPos[0], cameraPos[1], cameraPos[2]); + glRotated(cameraRot[0], 1, 0, 0); + glRotated(cameraRot[1], 0, 1, 0); + glRotated(cameraRot[2], 0, 0, 1); + glMultMatrixd(mm); + + float dir[4] = { 1, 1, 1, 0 }; + float k = 1.f/sqrtf(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]); + dir[0] *= k; dir[1] *= k; dir[2] *= k; + + glLightfv(GL_LIGHT0, GL_POSITION, dir); +} + + +void end3d() { + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_DEPTH_TEST); +} + + +void cubeDraw(double s) { + s *= 0.5; + glBegin(GL_QUADS); + + glColor3d(1, 0, 0); + glNormal3d(0, 0, -1); + glVertex3d(-s, -s, -s); + glVertex3d(-s, s, -s); + glVertex3d( s, s, -s); + glVertex3d( s, -s, -s); + glNormal3d(0, 0, 1); + glVertex3d(-s, -s, s); + glVertex3d( s, -s, s); + glVertex3d( s, s, s); + glVertex3d(-s, s, s); + + glColor3d(0, 1, 0); + glNormal3d(0, -1, 0); + glVertex3d(-s, -s, -s); + glVertex3d( s, -s, -s); + glVertex3d( s, -s, s); + glVertex3d(-s, -s, s); + glNormal3d(0, 1, 0); + glVertex3d(-s, s, -s); + glVertex3d(-s, s, s); + glVertex3d( s, s, s); + glVertex3d( s, s, -s); + + glColor3d(0, 0, 1); + glNormal3d(-1, 0, 0); + glVertex3d(-s, -s, -s); + glVertex3d(-s, -s, s); + glVertex3d(-s, s, s); + glVertex3d(-s, s, -s); + glNormal3d(1, 0, 0); + glVertex3d( s, -s, -s); + glVertex3d( s, s, -s); + glVertex3d( s, s, s); + glVertex3d( s, -s, s); + + glEnd(); + + glColor3d(1, 1, 1); +} + diff --git a/simple/world-gen/hmap.inc.c b/simple/world-gen/hmap.inc.c new file mode 100644 index 0000000..14a040d --- /dev/null +++ b/simple/world-gen/hmap.inc.c @@ -0,0 +1,224 @@ + +#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); +} + diff --git a/simple/world-gen/main.c b/simple/world-gen/main.c new file mode 100644 index 0000000..6de31da --- /dev/null +++ b/simple/world-gen/main.c @@ -0,0 +1,101 @@ + +#include +#include +#include +#include + +#define GL_GLEXT_PROTOTYPES +#include +#include +#include + +#include + + +#include "gl3d.inc.c" +#include "hmap.inc.c" +#include "water.inc.c" + + +double mx, my; +HMap hmap, wmap; +Water water; + + +void generate() { + hmapGenerate(&hmap); + for(int i = 0; i < HMAP_SIZE; ++i) + for(int j = 0; j < HMAP_SIZE; ++j) + wmap.map[i][j] = hmap.map[i][j] + 0.1; + for(int i = 0; i < 100; ++i) + waterCycle(&water, 1, 0.8, 0.0); + hmapCalcNormals(&hmap); + hmapCalcNormals(&wmap); + hmapBuildBuf(&hmap); + hmapBuildBuf(&wmap); +} + + +void init() { + hmapInit(&hmap, 1, 1, 1, 1, 1, 1); + hmapInit(&wmap, 1, 1, 0, 0, 1, 0.25); + waterInit(&water, &hmap, &wmap); + generate(); + + background(COLOR_BLACK); +} + + +void deinit() { + hmapDeinit(&hmap); + hmapDeinit(&wmap); +} + + +void draw() { + double pmx = mx, pmy = my; + mx = mouseX(); + my = mouseY(); + double dmx = mx - pmx; + double dmy = my - pmy; + + if (keyWentDown("space")) + generate(); + + if (mouseDown("left")) { + cameraRot[1] += dmx; + cameraRot[0] += dmy; + } + + if (mouseDown("right")) { + cameraPos[2] *= pow(1.01, dmy); + if (cameraPos[2] > -2) cameraPos[2] = -2; + } + + begin3d(); + cubeDraw(1); + hmapDraw(&hmap); + + glDepthMask(GL_FALSE); + glPushMatrix(); + glTranslated(0, 0, -0.01); + for(int i = 0; i < 1; ++i) { + hmapDraw(&wmap); + glTranslated(0, 0, 0.5); + } + glPopMatrix(); + glDepthMask(GL_TRUE); + + end3d(); +} + + +int main() { + windowSetResizable(TRUE); + windowSetVariableFrameRate(); + windowSetInit(&init); + windowSetDeinit(&deinit); + windowSetDraw(&draw); + windowRun(); + return 0; +} diff --git a/simple/world-gen/water.inc.c b/simple/world-gen/water.inc.c new file mode 100644 index 0000000..8314b1d --- /dev/null +++ b/simple/world-gen/water.inc.c @@ -0,0 +1,97 @@ + + +typedef struct { + HMap *hmap; + HMap *wmap; +} Water; + + + +void waterInit(Water *w, HMap *hmap, HMap *wmap) { + w->hmap = hmap; + w->wmap = wmap; + for(int i = 0; i < HMAP_SIZE; ++i) + for(int j = 0; j < HMAP_SIZE; ++j) + w->wmap->map[i][j] = w->hmap->map[i][j]; +} + + +void waterRain(Water *w, double k) { + for(int i = 0; i < HMAP_SIZE; ++i) + for(int j = 0; j < HMAP_SIZE; ++j) + w->wmap->map[i][j] += k; +} + + +void waterFall(Water *w, double wk, double hk) { + static const int di[4] = { -1, 1, 0, 0 }; + static const int dj[4] = { 0, 0, -1, 1 }; + + hmapCopy(w->hmap); + hmapCopy(w->wmap); + for(int i = 0; i < HMAP_SIZE; ++i) { + for(int j = 0; j < HMAP_SIZE; ++j) { + double dz = w->wmap->old[i][j] - w->hmap->old[i][j]; + dz *= 0.25; + if (dz > 0) { + double sum = 0; + double v[4] = {}; + for(int k = 0; k < 4; ++k) { + int ii = ((i+di[k]) + HMAP_SIZE)%HMAP_SIZE; + int jj = ((j+dj[k]) + HMAP_SIZE)%HMAP_SIZE; + double vv = w->wmap->old[i][j] - w->wmap->old[ii][jj]; + if (vv < 0) vv = 0; + //vv = vv*vv*vv; + v[k] = vv; + sum += vv; + } + double s = wk*sum; + if (s > dz) s = dz; + if (sum > 1e-10) { + for(int k = 0; k < 4; ++k) { + int ii = ((i+di[k]) + HMAP_SIZE)%HMAP_SIZE; + int jj = ((j+dj[k]) + HMAP_SIZE)%HMAP_SIZE; + double vv = v[k]/sum*s; + w->wmap->map[ii][jj] += v[k]/sum*s; + w->wmap->map[i][j] -= vv; + double dh = w->hmap->old[i][j] - w->hmap->map[ii][jj]; + dh *= 0.25; + if (dh > 0) { + vv = vv*hk; + if (vv > dh) vv = dh; + w->hmap->map[ii][jj] += vv; + w->hmap->map[i][j] -= vv; + } + } + } + } + } + } +} + + +double waterEvaporation(Water *w, double k) { + double sum; + for(int i = 0; i < HMAP_SIZE; ++i) { + for(int j = 0; j < HMAP_SIZE; ++j) { + double dz = w->wmap->map[i][j] - w->hmap->map[i][j]; + if (dz < 0) dz = 0; + if (dz > k) dz = k; + sum += dz; + w->wmap->map[i][j] -= dz; + } + } + return sum; +} + + +void waterCycle(Water *w, double fallK, double erosionK, double evaK) { + double sum = waterEvaporation(w, evaK); + waterRain(w, sum/(HMAP_SIZE*HMAP_SIZE)); + for(int i = 0; i < 10; ++i) { + waterFall(w, fallK/10, erosionK/10); + printf("."); + fflush(stdout); + } + printf("\n"); +}