diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-11 00:14:28 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-11 00:14:28 +0800 |
| commit | 1f7b24336653c01098d4428dc8da1829a6000750 (patch) | |
| tree | 21e198f774e52a69a7aa46932106b370a528284b /examples | |
| parent | a0775c7a437516b52c7978194583d7fee99fb5e9 (diff) | |
add hills example, corridor uses the new update call
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/corridor.c | 2 | ||||
| -rw-r--r-- | examples/hills.c | 221 |
2 files changed, 222 insertions, 1 deletions
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; +} |
