diff options
| -rw-r--r-- | kon/kon3d.c | 448 | ||||
| -rw-r--r-- | kon/kon3d.h | 53 |
2 files changed, 496 insertions, 5 deletions
diff --git a/kon/kon3d.c b/kon/kon3d.c index 8184039..6d6cee7 100644 --- a/kon/kon3d.c +++ b/kon/kon3d.c @@ -28,6 +28,7 @@ #include <math.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> /*** private ***/ @@ -71,7 +72,12 @@ void kon_fpInit(kon_fp_t *fp, const kon_tilemap_t *map, int column, int row) fp->speed = 2.5f; fp->turnSpeed = 2.0f; fp->fov = KON_DEG2RAD(60); + fp->farZ = 40.0f; fp->forward = fp->strafe = fp->turn = 0; + fp->feet = 0.0f; + fp->fall = 0.0f; + fp->maxSlope = 1.0f; + fp->gravity = 20.0f; fp->body = KON_BOX2(((float)column + 0.5f) * (float)map->tileWidth - size * 0.5f, ((float)row + 0.5f) * (float)map->tileHeight - size * 0.5f, size, size); } @@ -109,22 +115,74 @@ int kon_fpEvent(kon_fp_t *fp, kon_program_t *program, const kon_event_t *ev) return 1; } -void kon_fpUpdate(kon_fp_t *fp, kon_program_t *program, const kon_tilemap_t *map, const uint8_t *solid, int solidCount, double dt) +static float kon_groundAt_(const kon_fp_t *fp, const kon_tilemap_t *map, const kon_terrain_t *terrain) +{ + return kon_terrainHeight(terrain, (fp->body.x + fp->body.w * 0.5f) / (float)map->tileWidth, (fp->body.y + fp->body.h * 0.5f) / (float)map->tileHeight); +} + +/* going up more than maxSlope allows, from the ground height before the move to the one at the body now */ +static int kon_tooSteep_(const kon_fp_t *fp, const kon_tilemap_t *map, const kon_terrain_t *terrain, float before, float oldX, float oldY) +{ + float dx = (fp->body.x - oldX) / (float)map->tileWidth, dy = (fp->body.y - oldY) / (float)map->tileHeight; + float run = sqrtf(dx * dx + dy * dy); + + return run > 0.0f && kon_groundAt_(fp, map, terrain) - before > fp->maxSlope * run; +} + +void kon_fpPlace(kon_fp_t *fp, const kon_tilemap_t *map, const kon_terrain_t *terrain) +{ + fp->feet = terrain ? kon_groundAt_(fp, map, terrain) : 0.0f; + fp->fall = 0.0f; +} + +void kon_fpUpdate(kon_fp_t *fp, kon_program_t *program, const kon_tilemap_t *map, const uint8_t *solid, int solidCount, const kon_terrain_t *terrain, double dt) { float step = fp->speed * (float)dt * (float)map->tileWidth; float fx = sinf(fp->yaw), fz = -cosf(fp->yaw); float dx = (fx * (float)fp->forward - fz * (float)fp->strafe) * step; float dz = (fz * (float)fp->forward + fx * (float)fp->strafe) * step; + float oldX = fp->body.x, oldY = fp->body.y, ground = 0.0f, before = 0.0f; if (kon_debugConsoleOpen(program->debug) && kon_isMouseCaptured(program->window)) kon_setMouseCapture(program->window, false); fp->yaw += (float)fp->turn * fp->turnSpeed * (float)dt; + if (terrain) before = kon_groundAt_(fp, map, terrain); kon_tilemapMove(map, &fp->body, dx, dz, solid, solidCount); + + if (!terrain) return; + + /* too steep to climb, try sliding along it on one axis before giving up */ + if (kon_tooSteep_(fp, map, terrain, before, oldX, oldY)) { + float newX = fp->body.x, newY = fp->body.y; + + fp->body.x = newX; + fp->body.y = oldY; + if (kon_tooSteep_(fp, map, terrain, before, oldX, oldY)) { + fp->body.x = oldX; + fp->body.y = newY; + if (kon_tooSteep_(fp, map, terrain, before, oldX, oldY)) fp->body.y = oldY; + } + } + + ground = kon_groundAt_(fp, map, terrain); + if (fp->feet < ground) { + float t = (float)dt * 18.0f; + + fp->feet += (ground - fp->feet) * (t > 1.0f ? 1.0f : t); + fp->fall = 0.0f; + } else if (fp->feet > ground) { + fp->fall += fp->gravity * (float)dt; + fp->feet -= fp->fall * (float)dt; + if (fp->feet <= ground) { + fp->feet = ground; + fp->fall = 0.0f; + } + } } kon_vec3_t kon_fpEye(const kon_fp_t *fp, const kon_tilemap_t *map) { - return KON_VEC3((fp->body.x + fp->body.w * 0.5f) / (float)map->tileWidth, fp->eyeHeight, (fp->body.y + fp->body.h * 0.5f) / (float)map->tileHeight); + return KON_VEC3((fp->body.x + fp->body.w * 0.5f) / (float)map->tileWidth, fp->feet + fp->eyeHeight, (fp->body.y + fp->body.h * 0.5f) / (float)map->tileHeight); } kon_camera_t kon_fpCamera(const kon_fp_t *fp, const kon_tilemap_t *map) @@ -136,7 +194,7 @@ kon_camera_t kon_fpCamera(const kon_fp_t *fp, const kon_tilemap_t *map) camera.target = KON_VEC3(eye.x + sinf(fp->yaw) * cosf(fp->pitch), eye.y + sinf(fp->pitch), eye.z - cosf(fp->yaw) * cosf(fp->pitch)); camera.fov = fp->fov; camera.nearZ = 0.05f; - camera.farZ = 40.0f; + camera.farZ = fp->farZ; return camera; } @@ -206,6 +264,390 @@ void kon_drawLevel(kon_framebuffer_t *fb, const kon_camera_t *camera, const kon_ KON_VEC3(0, -1, 0), KON_VEC2(0, 0), KON_VEC2(w, d)); } +/*** terrain ***/ + +static float *kon_corner_(const kon_terrain_t *terrain, int column, int row) +{ + return &terrain->heights[(size_t)row * (size_t)(terrain->columns + 1) + (size_t)column]; +} + +static void kon_chunkBounds_(kon_terrain_t *terrain, int cx, int cz) +{ + int x0 = cx * terrain->chunk, z0 = cz * terrain->chunk; + int x1 = x0 + terrain->chunk, z1 = z0 + terrain->chunk; + float low, high; + int x, z; + + if (x1 > terrain->columns) x1 = terrain->columns; + if (z1 > terrain->rows) z1 = terrain->rows; + low = high = *kon_corner_(terrain, x0, z0); + for (z = z0; z <= z1; z++) { + for (x = x0; x <= x1; x++) { + float h = *kon_corner_(terrain, x, z); + + if (h < low) low = h; + if (h > high) high = h; + } + } + terrain->chunkMin[cz * terrain->chunkColumns + cx] = low; + terrain->chunkMax[cz * terrain->chunkColumns + cx] = high; +} + +/* every chunk that has a corner inside the cell rectangle, a corner on a border belongs to both chunks */ +static void kon_boundsAround_(kon_terrain_t *terrain, int x0, int z0, int x1, int z1) +{ + int cx0 = (x0 - 1) / terrain->chunk, cx1 = x1 / terrain->chunk; + int cz0 = (z0 - 1) / terrain->chunk, cz1 = z1 / terrain->chunk; + int cx, cz; + + if (x0 < 1) cx0 = 0; + if (z0 < 1) cz0 = 0; + if (cx1 >= terrain->chunkColumns) cx1 = terrain->chunkColumns - 1; + if (cz1 >= terrain->chunkRows) cz1 = terrain->chunkRows - 1; + for (cz = cz0; cz <= cz1; cz++) { + for (cx = cx0; cx <= cx1; cx++) kon_chunkBounds_(terrain, cx, cz); + } +} + +kon_terrain_t *kon_terrainCreate(int columns, int rows) +{ + kon_terrain_t *terrain; + size_t chunks; + + if (columns < 1 || rows < 1 || columns > 4096 || rows > 4096) return NULL; + terrain = calloc(1, sizeof(kon_terrain_t)); + if (!terrain) return NULL; + + terrain->columns = columns; + terrain->rows = rows; + terrain->chunk = 16; + terrain->chunkColumns = (columns + terrain->chunk - 1) / terrain->chunk; + terrain->chunkRows = (rows + terrain->chunk - 1) / terrain->chunk; + chunks = (size_t)terrain->chunkColumns * (size_t)terrain->chunkRows; + + terrain->heights = calloc((size_t)(columns + 1) * (size_t)(rows + 1), sizeof(float)); + terrain->chunkMin = calloc(chunks, sizeof(float)); + terrain->chunkMax = calloc(chunks, sizeof(float)); + if (!terrain->heights || !terrain->chunkMin || !terrain->chunkMax) { + kon_terrainFree(terrain); + return NULL; + } + return terrain; +} + +void kon_terrainFree(kon_terrain_t *terrain) +{ + if (!terrain) return; + free(terrain->heights); + free(terrain->chunkMin); + free(terrain->chunkMax); + free(terrain); +} + +float kon_terrainCorner(const kon_terrain_t *terrain, int column, int row) +{ + if (!terrain || column < 0 || row < 0 || column > terrain->columns || row > terrain->rows) return 0.0f; + return *kon_corner_(terrain, column, row); +} + +void kon_terrainSetCorner(kon_terrain_t *terrain, int column, int row, float height) +{ + if (!terrain || column < 0 || row < 0 || column > terrain->columns || row > terrain->rows) return; + *kon_corner_(terrain, column, row) = height; + kon_boundsAround_(terrain, column, row, column, row); +} + +float kon_terrainHeight(const kon_terrain_t *terrain, float x, float z) +{ + float fx, fz, a, b, c, d; + int column, row; + + if (!terrain) return 0.0f; + if (x < 0.0f) x = 0.0f; + if (z < 0.0f) z = 0.0f; + if (x > (float)terrain->columns) x = (float)terrain->columns; + if (z > (float)terrain->rows) z = (float)terrain->rows; + + column = (int)x; + row = (int)z; + if (column >= terrain->columns) column = terrain->columns - 1; + if (row >= terrain->rows) row = terrain->rows - 1; + fx = x - (float)column; + fz = z - (float)row; + + a = *kon_corner_(terrain, column, row); + b = *kon_corner_(terrain, column + 1, row); + c = *kon_corner_(terrain, column + 1, row + 1); + d = *kon_corner_(terrain, column, row + 1); + + /* the cell is split from its top left corner to its bottom right one */ + if (fz >= fx) return a + (d - a) * fz + (c - d) * fx; + return a + (b - a) * fx + (c - b) * fz; +} + +void kon_terrainRaise(kon_terrain_t *terrain, float x, float z, float radius, float amount) +{ + int x0, x1, z0, z1, column, row; + + if (!terrain || radius <= 0.0f) return; + x0 = (int)floorf(x - radius); + x1 = (int)ceilf(x + radius); + z0 = (int)floorf(z - radius); + z1 = (int)ceilf(z + radius); + if (x0 < 0) x0 = 0; + if (z0 < 0) z0 = 0; + if (x1 > terrain->columns) x1 = terrain->columns; + if (z1 > terrain->rows) z1 = terrain->rows; + if (x0 > x1 || z0 > z1) return; + + for (row = z0; row <= z1; row++) { + for (column = x0; column <= x1; column++) { + float dx = (float)column - x, dz = (float)row - z; + float f = 1.0f - sqrtf(dx * dx + dz * dz) / radius; + + if (f <= 0.0f) continue; + *kon_corner_(terrain, column, row) += amount * f * f * (3.0f - 2.0f * f); + } + } + kon_boundsAround_(terrain, x0, z0, x1, z1); +} + +void kon_terrainFill(kon_terrain_t *terrain, float height) +{ + int i, count; + + if (!terrain) return; + count = (terrain->columns + 1) * (terrain->rows + 1); + for (i = 0; i < count; i++) terrain->heights[i] = height; + kon_boundsAround_(terrain, 0, 0, terrain->columns, terrain->rows); +} + +int kon_terrainRay(const kon_terrain_t *terrain, kon_vec3_t origin, kon_vec3_t direction, float maxDistance, kon_vec3_t *hit) +{ + float length = sqrtf(direction.x * direction.x + direction.y * direction.y + direction.z * direction.z); + float step = 0.1f, t, from = 0.0f; + int i; + + if (!terrain || length < 0.0001f) return 0; + direction = KON_VEC3(direction.x / length, direction.y / length, direction.z / length); + + for (t = step; t <= maxDistance; t += step) { + float x = origin.x + direction.x * t, y = origin.y + direction.y * t, z = origin.z + direction.z * t; + + if (x < 0.0f || z < 0.0f || x > (float)terrain->columns || z > (float)terrain->rows) { + from = t; + continue; + } + if (y > kon_terrainHeight(terrain, x, z)) { + from = t; + continue; + } + + /* it went under between from and t, narrow it down */ + for (i = 0; i < 10; i++) { + float mid = (from + t) * 0.5f; + + if (origin.y + direction.y * mid > kon_terrainHeight(terrain, origin.x + direction.x * mid, origin.z + direction.z * mid)) from = mid; + else t = mid; + } + if (hit) { + hit->x = origin.x + direction.x * t; + hit->z = origin.z + direction.z * t; + hit->y = kon_terrainHeight(terrain, hit->x, hit->z); + } + return 1; + } + return 0; +} + +int kon_terrainSave(const kon_terrain_t *terrain, const char *path) +{ + FILE *file; + int column, row; + + if (!terrain || !path) return 1; + file = fopen(path, "w"); + if (!file) return 1; + + fprintf(file, "kter %d %d\n", terrain->columns, terrain->rows); + for (row = 0; row <= terrain->rows; row++) { + for (column = 0; column <= terrain->columns; column++) fprintf(file, column ? " %.4f" : "%.4f", (double)*kon_corner_(terrain, column, row)); + fputc('\n', file); + } + return fclose(file) != 0; +} + +kon_terrain_t *kon_terrainLoad(const char *path) +{ + kon_terrain_t *terrain; + FILE *file; + char magic[8]; + int columns, rows, column, row; + + if (!path) return NULL; + file = fopen(path, "r"); + if (!file) return NULL; + + if (fscanf(file, "%7s %d %d", magic, &columns, &rows) != 3 || strcmp(magic, "kter") != 0) { + fclose(file); + return NULL; + } + terrain = kon_terrainCreate(columns, rows); + if (!terrain) { + fclose(file); + return NULL; + } + + for (row = 0; row <= rows; row++) { + for (column = 0; column <= columns; column++) { + float h; + + if (fscanf(file, "%f", &h) != 1) { + kon_terrainFree(terrain); + fclose(file); + return NULL; + } + *kon_corner_(terrain, column, row) = h; + } + } + fclose(file); + kon_boundsAround_(terrain, 0, 0, columns, rows); + return terrain; +} + +/* the six planes of the view, pointing inwards, from the rows of the view projection matrix */ +static void kon_frustum_(kon_mat4_t vp, float planes[6][4]) +{ + const float *m = vp.m; + int i, j; + + for (j = 0; j < 4; j++) { + planes[0][j] = m[j * 4 + 3] + m[j * 4 + 0]; + planes[1][j] = m[j * 4 + 3] - m[j * 4 + 0]; + planes[2][j] = m[j * 4 + 3] + m[j * 4 + 1]; + planes[3][j] = m[j * 4 + 3] - m[j * 4 + 1]; + planes[4][j] = m[j * 4 + 3] + m[j * 4 + 2]; + planes[5][j] = m[j * 4 + 3] - m[j * 4 + 2]; + } + for (i = 0; i < 6; i++) { + float length = sqrtf(planes[i][0] * planes[i][0] + planes[i][1] * planes[i][1] + planes[i][2] * planes[i][2]); + + for (j = 0; j < 4 && length > 0.0f; j++) planes[i][j] /= length; + } +} + +static int kon_sphereVisible_(float planes[6][4], float x, float y, float z, float radius) +{ + int i; + + for (i = 0; i < 6; i++) { + if (planes[i][0] * x + planes[i][1] * y + planes[i][2] * z + planes[i][3] < -radius) return 0; + } + return 1; +} + +void kon_drawTerrain(kon_framebuffer_t *fb, const kon_camera_t *camera, const kon_terrain_t *terrain, const kon_light_t *light) +{ + kon_mat4_t vp = kon_cameraViewProjection(camera, fb->width, fb->height); + float planes[6][4]; + int cx, cz; + + if (!terrain || !terrain->material) return; + kon_frustum_(vp, planes); + + for (cz = 0; cz < terrain->chunkRows; cz++) { + for (cx = 0; cx < terrain->chunkColumns; cx++) { + int x0 = cx * terrain->chunk, z0 = cz * terrain->chunk; + int x1 = x0 + terrain->chunk, z1 = z0 + terrain->chunk; + float low = terrain->chunkMin[cz * terrain->chunkColumns + cx], high = terrain->chunkMax[cz * terrain->chunkColumns + cx]; + float halfW, halfD, halfH, radius; + int x, z; + + if (x1 > terrain->columns) x1 = terrain->columns; + if (z1 > terrain->rows) z1 = terrain->rows; + halfW = (float)(x1 - x0) * 0.5f; + halfD = (float)(z1 - z0) * 0.5f; + halfH = (high - low) * 0.5f; + radius = sqrtf(halfW * halfW + halfD * halfD + halfH * halfH); + if (!kon_sphereVisible_(planes, (float)x0 + halfW, low + halfH, (float)z0 + halfD, radius)) continue; + + for (z = z0; z < z1; z++) { + for (x = x0; x < x1; x++) { + float ha = *kon_corner_(terrain, x, z), hb = *kon_corner_(terrain, x + 1, z); + float hc = *kon_corner_(terrain, x + 1, z + 1), hd = *kon_corner_(terrain, x, z + 1); + kon_vec3_t a = KON_VEC3((float)x, ha, (float)z), b = KON_VEC3((float)x + 1, hb, (float)z); + kon_vec3_t c = KON_VEC3((float)x + 1, hc, (float)z + 1), d = KON_VEC3((float)x, hd, (float)z + 1); + kon_vec3_t upper = kon_vec3Normalize(KON_VEC3(hd - hc, 1.0f, ha - hd)), lower = kon_vec3Normalize(KON_VEC3(ha - hb, 1.0f, hb - hc)); + kon_vertex_t va = { a, upper, KON_VEC2((float)x, (float)z) }, vd = { d, upper, KON_VEC2((float)x, (float)z + 1) }, vc = { c, upper, KON_VEC2((float)x + 1, (float)z + 1) }; + kon_vertex_t wa = { a, lower, va.uv }, wc = { c, lower, vc.uv }, wb = { b, lower, KON_VEC2((float)x + 1, (float)z) }; + + kon_drawTriangle3D(fb, vp, va, vd, vc, terrain->material, light, KON_RENDER_DEFAULT); + kon_drawTriangle3D(fb, vp, wa, wc, wb, terrain->material, light, KON_RENDER_DEFAULT); + } + } + } + } +} + +#if defined(KON_DEVELOPER) && defined(KON_MODULE_DEBUG) + +static void kon_cmdTerrainSave_(kon_debug_t *debug, void *user, int argc, char **argv) +{ + if (argc < 2) { + kon_debugLog(debug, "tsave file"); + return; + } + if (kon_terrainSave(user, argv[1]) == 0) kon_debugLog(debug, "saved %s", argv[1]); + else kon_debugLog(debug, "could not save %s", argv[1]); +} + +/* loads into the same struct so whatever points at the terrain keeps working */ +static void kon_cmdTerrainLoad_(kon_debug_t *debug, void *user, int argc, char **argv) +{ + kon_terrain_t *terrain = user, *loaded; + const kon_material_t *material = terrain->material; + + if (argc < 2) { + kon_debugLog(debug, "tload file"); + return; + } + loaded = kon_terrainLoad(argv[1]); + if (!loaded) { + kon_debugLog(debug, "could not load %s", argv[1]); + return; + } + + free(terrain->heights); + free(terrain->chunkMin); + free(terrain->chunkMax); + *terrain = *loaded; + terrain->material = material; + free(loaded); + kon_debugLog(debug, "loaded %s", argv[1]); +} + +static void kon_cmdTerrainFill_(kon_debug_t *debug, void *user, int argc, char **argv) +{ + kon_terrainFill(user, argc > 1 ? (float)atof(argv[1]) : 0.0f); + kon_debugLog(debug, "filled"); +} + +void kon_terrainDebug(kon_terrain_t *terrain, kon_debug_t *debug) +{ + kon_debugCommand(debug, "tsave", "tsave file: write the terrain as text", kon_cmdTerrainSave_, terrain); + kon_debugCommand(debug, "tload", "tload file: read a terrain back", kon_cmdTerrainLoad_, terrain); + kon_debugCommand(debug, "tfill", "tfill height: flatten everything", kon_cmdTerrainFill_, terrain); +} + +#else + +void kon_terrainDebug(kon_terrain_t *terrain, kon_debug_t *debug) +{ + (void)terrain; (void)debug; +} + +#endif + float kon_distanceFade(float distance, float falloff) { return 1.0f / (1.0f + distance * falloff); diff --git a/kon/kon3d.h b/kon/kon3d.h index e4ffc6b..510fb86 100644 --- a/kon/kon3d.h +++ b/kon/kon3d.h @@ -40,9 +40,25 @@ typedef struct kon_fp { float speed; /* tiles per second, 2.5 */ float turnSpeed; /* radians per second for the arrow keys, 2 */ float fov; /* vertical, radians, 60 degrees */ + float farZ; /* how far you see, 40 */ int forward, strafe, turn; /* -1, 0 or 1, set by kon_fpEvent */ + float feet; /* height of the floor under you, the eyes are eyeHeight above this */ + float fall; /* downward speed while dropping */ + float maxSlope; /* rise over run you can walk up, 1 is 45 degrees. anything steeper is a wall */ + float gravity; /* 20 */ } kon_fp_t; +/* a grid of heights, one world unit per cell, x is the column and z the row like the tilemap. every cell is two triangles. + edit the heights any time, it is drawn in chunks that are skipped when they are off screen */ +typedef struct kon_terrain { + int columns, rows; /* cells, there is one more height than that each way */ + float *heights; + int chunk; /* cells per chunk side, 16 */ + int chunkColumns, chunkRows; + float *chunkMin, *chunkMax; + const kon_material_t *material; /* the texture repeats once per cell */ +} kon_terrain_t; + /* what to draw for a map. solid is indexed by tile id like in kon_tilemapMove, solid tiles become walls. ceiling can be NULL, that leaves the sky colour */ typedef struct kon_level3d { @@ -62,8 +78,12 @@ void kon_fpInit(kon_fp_t *fp, const kon_tilemap_t *map, int column, int row); a click grabs the mouse and tab lets go */ int kon_fpEvent(kon_fp_t *fp, kon_program_t *program, const kon_event_t *event); -/* walks and turns, slides along solid tiles. lets the mouse go while the debug console is open */ -void kon_fpUpdate(kon_fp_t *fp, kon_program_t *program, const kon_tilemap_t *map, const uint8_t *solid, int solidCount, double dt); +/* walks and turns, slides along solid tiles. lets the mouse go while the debug console is open. + terrain can be NULL for flat ground, otherwise you follow it, fall off ledges and stop at slopes steeper than maxSlope */ +void kon_fpUpdate(kon_fp_t *fp, kon_program_t *program, const kon_tilemap_t *map, const uint8_t *solid, int solidCount, const kon_terrain_t *terrain, double dt); + +/* puts your feet on the ground right away, for after kon_fpInit or a teleport */ +void kon_fpPlace(kon_fp_t *fp, const kon_tilemap_t *map, const kon_terrain_t *terrain); /* where the eyes are in world units */ kon_vec3_t kon_fpEye(const kon_fp_t *fp, const kon_tilemap_t *map); @@ -78,6 +98,35 @@ void kon_fpDebug(kon_fp_t *fp, kon_debug_t *debug); so the depth test throws out what the walls already cover */ void kon_drawLevel(kon_framebuffer_t *fb, const kon_camera_t *camera, const kon_level3d_t *level, const kon_light_t *light); +/*** terrain ***/ + +/* all heights start at 0. NULL if the size is bad or there is no memory */ +kon_terrain_t *kon_terrainCreate(int columns, int rows); +void kon_terrainFree(kon_terrain_t *terrain); + +/* the corner at column, row, from 0 to columns and 0 to rows. both do nothing outside */ +float kon_terrainCorner(const kon_terrain_t *terrain, int column, int row); +void kon_terrainSetCorner(kon_terrain_t *terrain, int column, int row, float height); + +/* the exact height of the surface at x, z, the same triangles that get drawn. clamps at the edges */ +float kon_terrainHeight(const kon_terrain_t *terrain, float x, float z); + +/* pushes the ground up (or down with a negative amount) around x, z. full strength in the middle, nothing at radius */ +void kon_terrainRaise(kon_terrain_t *terrain, float x, float z, float radius, float amount); +void kon_terrainFill(kon_terrain_t *terrain, float height); + +/* walks along the ray in small steps. returns 1 and the point if it hits the ground inside maxDistance */ +int kon_terrainRay(const kon_terrain_t *terrain, kon_vec3_t origin, kon_vec3_t direction, float maxDistance, kon_vec3_t *hit); + +/* a text file, "kter columns rows" and then the heights, so it is easy to read and diff. returns 0 on success */ +int kon_terrainSave(const kon_terrain_t *terrain, const char *path); +kon_terrain_t *kon_terrainLoad(const char *path); + +void kon_drawTerrain(kon_framebuffer_t *fb, const kon_camera_t *camera, const kon_terrain_t *terrain, const kon_light_t *light); + +/* adds the commands tsave file, tload file and tfill height. the terrain has to stay where it is, a load changes it in place */ +void kon_terrainDebug(kon_terrain_t *terrain, kon_debug_t *debug); + /* 1 up close and less the further away, for dimming sprites. falloff 0.18 is gentle */ float kon_distanceFade(float distance, float falloff); |
