/* * 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 #include #include #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 FOG_END 80.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_mipmaps_t *mips; 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); if (!image) return NULL; kon_clearFramebuffer(image, 0xFF3A3558); kon_fillRectangle(image, 0, 0, 16, 16, 0xFF332E4E); kon_fillRectangle(image, 16, 16, 16, 16, 0xFF332E4E); 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; light.fogColor = COL_SKY; light.fogStart = 25.0f; light.fogEnd = FOG_END; 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->mips = kon_createMipmaps(state->ground); state->grass.mips = state->mips; 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 = FOG_END; 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_freeMipmaps(state->mips); 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; }