summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/handmade.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/examples/handmade.c b/examples/handmade.c
new file mode 100644
index 0000000..072106e
--- /dev/null
+++ b/examples/handmade.c
@@ -0,0 +1,235 @@
+/*
+ * handmade: everything in one file, on the raw libs only
+ *
+ * no program module, no assets, no build script. copy these headers next to this file:
+ * konwinlib.h konsofren.h kontime.h konmath.h konsector.h
+ * and build it (linux): cc -std=c99 -o handmade handmade.c -lX11 -lXext -lXrandr -lm
+ * (windows): cc -std=c99 -o handmade.exe handmade.c -lgdi32 -lm
+ *
+ * the map is text inside this file and the gem is a list of vertices. w and s walk, a and d strafe, left and right turn, escape quits
+ */
+
+#define KONWINLIB_IMPLEMENTATION
+#define KONSOFREN_IMPLEMENTATION
+#define KONTIME_IMPLEMENTATION
+#define KONSECTOR_IMPLEMENTATION
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "konwinlib.h"
+#include "konsofren.h"
+#include "kontime.h"
+#include "konmath.h"
+#include "konsector.h"
+
+#define GAME_WIDTH 320
+#define GAME_HEIGHT 180
+#define EYE 0.8f
+#define RADIUS 0.25f
+
+/*** the map ***/
+
+/* x goes east, z goes south, corners go counter clockwise seen from above. two rooms and a short passage, and a picture on the east wall of the far room */
+static const char crypt[] =
+ "kmap 1\n"
+ "v 0 0\n"
+ "v 0 6\n"
+ "v 6 6\n"
+ "v 6 4\n"
+ "v 6 2\n"
+ "v 6 0\n"
+ "v 10 2\n"
+ "v 10 4\n"
+ "v 16 6\n"
+ "v 16 0\n"
+ "sector 0 3 light 0.9 floor stone ceiling stone wall brick : 0 1 2 3 4 5\n"
+ "sector 0 2.2 light 0.5 floor stone ceiling stone wall brick : 4 3 7 6\n"
+ "sector 0 4 light 0.7 floor stone ceiling stone wall plaster : 6 7 8 9\n"
+ "entity spawn 2 0.8 3 0\n"
+ "quad mark 15.95 0.4 2 15.95 0.4 4 15.95 2.4 4 15.95 2.4 2 light 0.8\n";
+
+/*** the gem ***/
+
+#define V(x, y, z, nx, ny, nz) { { x, y, z }, { nx, ny, nz }, { 0, 0 } }
+
+static const kon_vertex_t gemVertices[24] = {
+ V(1, 0, 0, 0.577, 0.577, 0.577), V(0, 1, 0, 0.577, 0.577, 0.577), V(0, 0, 1, 0.577, 0.577, 0.577),
+ V(1, 0, 0, 0.577, 0.577, -0.577), V(0, 0, -1, 0.577, 0.577, -0.577), V(0, 1, 0, 0.577, 0.577, -0.577),
+ V(1, 0, 0, 0.577, -0.577, 0.577), V(0, 0, 1, 0.577, -0.577, 0.577), V(0, -1, 0, 0.577, -0.577, 0.577),
+ V(1, 0, 0, 0.577, -0.577, -0.577), V(0, -1, 0, 0.577, -0.577, -0.577), V(0, 0, -1, 0.577, -0.577, -0.577),
+ V(-1, 0, 0, -0.577, 0.577, 0.577), V(0, 0, 1, -0.577, 0.577, 0.577), V(0, 1, 0, -0.577, 0.577, 0.577),
+ V(-1, 0, 0, -0.577, 0.577, -0.577), V(0, 1, 0, -0.577, 0.577, -0.577), V(0, 0, -1, -0.577, 0.577, -0.577),
+ V(-1, 0, 0, -0.577, -0.577, 0.577), V(0, -1, 0, -0.577, -0.577, 0.577), V(0, 0, 1, -0.577, -0.577, 0.577),
+ V(-1, 0, 0, -0.577, -0.577, -0.577), V(0, 0, -1, -0.577, -0.577, -0.577), V(0, -1, 0, -0.577, -0.577, -0.577),
+};
+
+static const uint16_t gemIndices[24] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
+};
+
+static const kon_mesh_t gem = { gemVertices, gemIndices, 8 };
+
+/*** state ***/
+
+typedef struct state {
+ kon_window_t *window;
+ kon_display_t *display;
+ kon_sectorMap_t *map;
+ kon_sectorMesh_t *mesh;
+ kon_material_t materials[8];
+ kon_image *mark;
+ float x, z, yaw;
+ int sector;
+ int forward, strafe, turn;
+ kon_time last, time;
+} state_t;
+
+/*** images made in code ***/
+
+static kon_image *makeMark(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;
+}
+
+static uint32_t colorOf(const char *name)
+{
+ if (!strcmp(name, "stone")) return 0xFF6C6688;
+ if (!strcmp(name, "brick")) return 0xFF8A5560;
+ if (!strcmp(name, "plaster")) return 0xFFB5AECB;
+ return 0xFF9A93B3;
+}
+
+/*** moving ***/
+
+/* the four corners of the body all have to be inside some room */
+static int fits(const state_t *state, float x, float z)
+{
+ static const float dx[4] = { -1, 1, 1, -1 }, dz[4] = { -1, -1, 1, 1 };
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (kon_sectorAt(state->map, x + dx[i] * RADIUS, z + dz[i] * RADIUS, state->sector) < 0) return 0;
+ }
+ return 1;
+}
+
+static void walk(state_t *state, float dx, float dz)
+{
+ if (fits(state, state->x + dx, state->z + dz)) {
+ state->x += dx;
+ state->z += dz;
+ } else if (fits(state, state->x + dx, state->z)) {
+ state->x += dx;
+ } else if (fits(state, state->x, state->z + dz)) {
+ state->z += dz;
+ }
+ state->sector = kon_sectorAt(state->map, state->x, state->z, state->sector);
+}
+
+/*** the loop ***/
+
+static void frame(void *data)
+{
+ state_t *state = data;
+ kon_framebuffer_t *game = state->display->game;
+ kon_camera_t camera = kon_cameraDefault();
+ kon_light_t light = kon_lightDefault();
+ kon_event_t ev;
+ kon_time now = kon_getTime(), dt = now - state->last;
+ float fx = sinf(state->yaw), fz = -cosf(state->yaw), step = 2.0f * (float)dt;
+ kon_mat4_t vp;
+
+ state->last = now;
+ if (dt > 0.25) dt = 0.25;
+ state->time += dt;
+
+ while (kon_pollEvent(state->window, &ev)) {
+ int down = ev.type == KON_EVENT_KEY_DOWN;
+
+ if (ev.type == KON_EVENT_RESIZE) kon_resizeDisplay(state->display, ev.width, ev.height);
+ if (ev.type != KON_EVENT_KEY_DOWN && ev.type != KON_EVENT_KEY_UP) continue;
+ switch (ev.key) {
+ case KON_KEY_W: state->forward = down ? 1 : (state->forward > 0 ? 0 : state->forward); break;
+ case KON_KEY_S: state->forward = down ? -1 : (state->forward < 0 ? 0 : state->forward); break;
+ case KON_KEY_D: state->strafe = down ? 1 : (state->strafe > 0 ? 0 : state->strafe); break;
+ case KON_KEY_A: state->strafe = down ? -1 : (state->strafe < 0 ? 0 : state->strafe); break;
+ case KON_KEY_RIGHT: state->turn = down ? 1 : (state->turn > 0 ? 0 : state->turn); break;
+ case KON_KEY_LEFT: state->turn = down ? -1 : (state->turn < 0 ? 0 : state->turn); break;
+ default: break;
+ }
+ }
+
+ state->yaw += (float)state->turn * 2.0f * (float)dt;
+ walk(state, (fx * (float)state->forward - fz * (float)state->strafe) * step, (fz * (float)state->forward + fx * (float)state->strafe) * step);
+
+ camera.position = KON_VEC3(state->x, state->map->sectors[state->sector].floor + EYE, state->z);
+ camera.target = KON_VEC3(state->x + fx, camera.position.y, state->z + fz);
+ camera.fov = KON_DEG2RAD(60);
+ camera.nearZ = 0.05f;
+ camera.farZ = 40.0f;
+ vp = kon_cameraViewProjection(&camera, game->width, game->height);
+
+ kon_clearFramebuffer(game, 0xFF1B1826);
+ light.direction = KON_VEC3(-0.3f, -1.0f, -0.2f);
+ light.ambient = 0.5f;
+ kon_sectorDraw(game, &camera, state->mesh, state->materials, state->map->textureCount, &light);
+ kon_drawMesh(game, vp, kon_mat4FromTransform(KON_VEC3(13.0f, 1.2f, 3.5f), KON_VEC3(0.0f, (float)state->time, 0.0f), KON_VEC3(0.4f, 0.6f, 0.4f)), &gem,
+ &state->materials[7], &light, KON_RENDER_DEFAULT);
+
+ kon_presentDisplay(state->display);
+ kon_blitPixels(state->window, state->display->screen->data, state->display->screen->width, state->display->screen->height);
+ kon_sleep(0.004);
+}
+
+int main(void)
+{
+ static state_t state;
+ char error[160];
+ int i;
+
+ state.map = kon_sectorParse(crypt, error, sizeof(error));
+ if (!state.map) {
+ fprintf(stderr, "map: %s\n", error);
+ return 1;
+ }
+ state.mesh = kon_sectorBuild(state.map);
+ state.mark = makeMark();
+ for (i = 0; i < state.map->textureCount && i < 7; i++) {
+ state.materials[i] = strcmp(state.map->textures[i], "mark") == 0 ? kon_materialTexture(state.mark) : kon_materialColor(colorOf(state.map->textures[i]));
+ }
+ state.materials[7] = kon_materialColor(0xFF8FD3C8);
+
+ if (!state.mesh || !state.mark || !kon_init()) return 1;
+ state.window = kon_createWindow("handmade", 0, 0, GAME_WIDTH * 3, GAME_HEIGHT * 3, KON_WINDOW_CENTER | KON_WINDOW_RESIZABLE);
+ if (!state.window) return 1;
+ kon_setExitKey(state.window, KON_KEY_ESCAPE);
+ state.display = kon_createDisplay(GAME_WIDTH, GAME_HEIGHT, GAME_WIDTH * 3, GAME_HEIGHT * 3);
+ if (!state.display) return 1;
+
+ state.x = state.map->entities[0].position.x;
+ state.z = state.map->entities[0].position.z;
+ state.sector = kon_sectorAt(state.map, state.x, state.z, -1);
+ state.last = kon_getTime();
+
+ kon_runMainLoop(state.window, frame, &state);
+
+ kon_freeDisplay(state.display);
+ kon_destroyWindow(state.window);
+ kon_deinit();
+ kon_sectorMeshFree(state.mesh);
+ kon_sectorFree(state.map);
+ kon_freeFramebuffer(state.mark);
+ return 0;
+}