summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README.md2
-rw-r--r--examples/corridor.c2
-rw-r--r--examples/hills.c221
4 files changed, 224 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 1678292..3d3e888 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,7 @@ PACKFLAGS ?=
BIN = $(OBJDIR)/$(PROGRAM)$(EXE)
PACKER = $(OBJDIR)/konpack$(EXE)
MODELER = $(OBJDIR)/konmodel$(EXE)
-EXAMPLES = modelview sound ui platform corridor
+EXAMPLES = modelview sound ui platform corridor hills
EXAMPLEBINS = $(EXAMPLES:%=$(OBJDIR)/%$(EXE))
PACKFILE = $(OBJDIR)/data.kpk
CORE = $(OBJDIR)/engine.o $(OBJDIR)/konwinlib.o $(OBJDIR)/konsofren.o \
diff --git a/README.md b/README.md
index 9ef4eaa..2c4082d 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ The engine modules are a `.h` and a `.c` pair, they depend on each other a bit,
| font | konfont |
| toml | kontoml |
| tile | tilemaps and sprite sheets |
-| 3d | first person kit: mouse look body on a tilemap, walls and floor from the map (adds tile) |
+| 3d | first person kit: mouse look body, walls from a tilemap, editable hill terrain (adds tile) |
| pack | encrypted asset packs |
| assets | named asset cache with reference counts, loose files or a pack |
| image | stb_image so assets can load pngs |
diff --git a/examples/corridor.c b/examples/corridor.c
index bf829a7..bd639af 100644
--- a/examples/corridor.c
+++ b/examples/corridor.c
@@ -144,7 +144,7 @@ static void update(kon_program_t *program, double dt, void *user)
state_t *state = user;
state->time += dt;
- kon_fpUpdate(&state->fp, program, &state->map, state->solid, 2, dt);
+ kon_fpUpdate(&state->fp, program, &state->map, state->solid, 2, NULL, dt);
}
static int init(kon_program_t *program, void *user)
diff --git a/examples/hills.c b/examples/hills.c
new file mode 100644
index 0000000..c3b4a55
--- /dev/null
+++ b/examples/hills.c
@@ -0,0 +1,221 @@
+/*
+ * hills: an open field with hills you can walk on and reshape while you stand in it
+ *
+ * build: ./build.sh -d -m 3d,font obj/hills
+ * run: obj/hills
+ *
+ * click to grab the mouse, tab lets go. w and s walk, a and d strafe. hold e to raise the ground where the cross is, q to lower it
+ * the console (`) has tsave file, tload file and tfill height to keep what you made. the markers sit on the ground wherever it is
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "konprogram.h"
+#include "kon3d.h"
+
+#if !defined(KON_MODULE_3D) || !defined(KON_MODULE_FONT)
+#error "build with: ./build.sh -d -m 3d,font obj/hills"
+#endif
+
+#define SIZE 128
+#define MARKERS 24
+#define BRUSH 3.0f
+
+#define COL_SKY 0xFF1B1826u
+#define COL_DIM 0xFF9A93B3u
+#define COL_CROSS 0xFFF3DDA0u
+
+typedef struct state {
+ kon_tilemap_t map; /* only the collision, the ground comes from the terrain */
+ uint16_t tiles[SIZE * SIZE];
+ uint8_t solid[2];
+
+ kon_terrain_t *terrain;
+ kon_material_t grass;
+ kon_image *ground, *marker;
+ kon_fp_t fp;
+ int raise, lower;
+ double time;
+ kon_vec2_t markers[MARKERS];
+} state_t;
+
+/*** images made in code ***/
+
+static kon_image *makeGround(void)
+{
+ kon_image *image = kon_createFramebuffer(32, 32);
+ uint32_t seed = 7;
+ int x, y;
+
+ if (!image) return NULL;
+ for (y = 0; y < 32; y++) {
+ for (x = 0; x < 32; x++) {
+ int shade;
+
+ seed = seed * 1664525u + 1013904223u;
+ shade = (int)(seed >> 28) - 8;
+ image->data[y * 32 + x] = 0xFF000000u | (uint32_t)(0x2F + shade / 2) << 16 | (uint32_t)(0x4A + shade) << 8 | (uint32_t)(0x52 + shade);
+ }
+ }
+ return image;
+}
+
+static kon_image *makeMarker(void)
+{
+ kon_image *image = kon_createFramebuffer(32, 40);
+ int i;
+
+ if (!image) return NULL;
+ kon_clearFramebuffer(image, 0xFFF2A7C3);
+ kon_fillRectangle(image, 2, 2, 28, 36, 0xFF332E4E);
+ for (i = 0; i < 28; i++) {
+ kon_fillRectangle(image, 2 + i, 2 + i * 36 / 28, 1, 1, 0xFFF2A7C3);
+ kon_fillRectangle(image, 2 + i, 37 - i * 36 / 28, 1, 1, 0xFFF2A7C3);
+ }
+ return image;
+}
+
+/*** drawing ***/
+
+static void draw(kon_program_t *program, kon_framebuffer_t *game, void *user)
+{
+ state_t *state = user;
+ kon_light_t light = kon_lightDefault();
+ kon_camera_t camera = kon_fpCamera(&state->fp, &state->map);
+ kon_mat4_t vp = kon_cameraViewProjection(&camera, game->width, game->height);
+ int i;
+
+ kon_clearFramebuffer(game, COL_SKY);
+ light.direction = KON_VEC3(-0.4f, -1.0f, -0.3f);
+ light.ambient = 0.45f;
+ kon_drawTerrain(game, &camera, state->terrain, &light);
+
+ for (i = 0; i < MARKERS; i++) {
+ float x = state->markers[i].x, z = state->markers[i].y;
+ float dx = x - camera.position.x, dz = z - camera.position.z;
+ float bob = 0.08f * sinf((float)state->time * 2.0f + (float)i);
+
+ kon_drawBillboard(game, vp, KON_VEC3(x, kon_terrainHeight(state->terrain, x, z) + 0.1f + bob, z), 0.6f, 0.75f, state->marker, KON_RECT(0, 0, 0, 0),
+ kon_distanceFade(sqrtf(dx * dx + dz * dz), 0.05f), KON_RENDER_DEPTH);
+ }
+
+ kon_fillRectangle(game, game->width / 2 - 4, game->height / 2, 9, 1, COL_CROSS);
+ kon_fillRectangle(game, game->width / 2, game->height / 2 - 4, 1, 9, COL_CROSS);
+ if (program->font) kon_drawText(game, program->font, 8, 16, "click to look, e raises the ground, q lowers it", COL_DIM);
+}
+
+/*** program ***/
+
+static int event(kon_program_t *program, const kon_event_t *ev, void *user)
+{
+ state_t *state = user;
+ int down;
+
+ if (kon_fpEvent(&state->fp, program, ev)) return 1;
+ if (ev->type != KON_EVENT_KEY_DOWN && ev->type != KON_EVENT_KEY_UP) return 0;
+ down = ev->type == KON_EVENT_KEY_DOWN;
+
+ if (ev->key == KON_KEY_E) state->raise = down;
+ else if (ev->key == KON_KEY_Q) state->lower = down;
+ else return 0;
+ return 1;
+}
+
+static void update(kon_program_t *program, double dt, void *user)
+{
+ state_t *state = user;
+ kon_camera_t camera = kon_fpCamera(&state->fp, &state->map);
+ kon_vec3_t hit;
+
+ state->time += dt;
+ kon_fpUpdate(&state->fp, program, &state->map, state->solid, 2, state->terrain, dt);
+
+ if ((state->raise || state->lower) && kon_isMouseCaptured(program->window)) {
+ kon_vec3_t forward = KON_VEC3(camera.target.x - camera.position.x, camera.target.y - camera.position.y, camera.target.z - camera.position.z);
+
+ if (kon_terrainRay(state->terrain, camera.position, forward, 30.0f, &hit)) {
+ kon_terrainRaise(state->terrain, hit.x, hit.z, BRUSH, (state->raise ? 4.0f : -4.0f) * (float)dt);
+ }
+ }
+}
+
+static int init(kon_program_t *program, void *user)
+{
+ state_t *state = user;
+ int x, z, i;
+
+ state->terrain = kon_terrainCreate(SIZE, SIZE);
+ state->ground = makeGround();
+ state->marker = makeMarker();
+ if (!state->terrain || !state->ground || !state->marker) return 0;
+
+ for (z = 0; z <= SIZE; z++) {
+ for (x = 0; x <= SIZE; x++) {
+ float h = 3.0f * sinf((float)x * 0.11f) * cosf((float)z * 0.09f) + 1.2f * sinf((float)x * 0.35f + (float)z * 0.27f);
+
+ kon_terrainSetCorner(state->terrain, x, z, h);
+ }
+ }
+ state->grass = kon_materialTexture(state->ground);
+ state->terrain->material = &state->grass;
+
+ state->map.columns = SIZE;
+ state->map.rows = SIZE;
+ state->map.tileWidth = 32;
+ state->map.tileHeight = 32;
+ state->map.tiles = state->tiles;
+ state->solid[1] = 1;
+ for (i = 0; i < SIZE; i++) {
+ state->tiles[i] = 1;
+ state->tiles[(SIZE - 1) * SIZE + i] = 1;
+ state->tiles[i * SIZE] = 1;
+ state->tiles[i * SIZE + SIZE - 1] = 1;
+ }
+
+ for (i = 0; i < MARKERS; i++) {
+ state->markers[i].x = 8.0f + (float)((i * 37) % 112);
+ state->markers[i].y = 8.0f + (float)((i * 53) % 112);
+ }
+
+ kon_fpInit(&state->fp, &state->map, SIZE / 2, SIZE / 2);
+ state->fp.farZ = 90.0f;
+ kon_fpPlace(&state->fp, &state->map, state->terrain);
+ kon_fpDebug(&state->fp, program->debug);
+ kon_terrainDebug(state->terrain, program->debug);
+ return 1;
+}
+
+static void shutdown(kon_program_t *program, void *user)
+{
+ state_t *state = user;
+
+ (void)program;
+ kon_terrainFree(state->terrain);
+ kon_freeFramebuffer(state->ground);
+ kon_freeFramebuffer(state->marker);
+}
+
+int main(void)
+{
+ state_t *state = calloc(1, sizeof(state_t));
+ kon_programConfig_t config = {0};
+ int result;
+
+ if (!state) return 1;
+ config.title = "hills";
+ config.gameWidth = 480;
+ config.gameHeight = 270;
+ config.windowWidth = 960;
+ config.windowHeight = 540;
+ config.user = state;
+ config.init = init;
+ config.event = event;
+ config.update = update;
+ config.draw = draw;
+ config.shutdown = shutdown;
+ result = kon_programRun(&config);
+ free(state);
+ return result;
+}