summaryrefslogtreecommitdiff
path: root/examples/corridor.c
blob: bd639afbd7797d4a8e6d4607203aa73eb58323c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
 * corridor: a small first person level, walls from a tilemap, sprites that face you, and collision on the same map
 *
 * build:  ./build.sh -d -m 3d,font obj/corridor
 *         ./build.sh -d -m 3d,font,assets,image obj/corridor   (also loads sprites/marker.png from the assets if there is one)
 * run:    obj/corridor
 *
 * click to grab the mouse and look around with it, tab lets go. with the debug module the variable sens sets the mouse speed. w and s walk, a and d strafe, the arrows turn too
 * the walls are quads made from the tilemap every frame, the markers are billboards drawn with depth so walls hide them
 * the markers are plain placeholders, put your own sprite in assets/sprites/marker.png to see it in their place
 */

#include <stdio.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/corridor"
#endif

#define COLUMNS 16
#define ROWS 16
#define TILE 32
#define WALL_HEIGHT 1.2f
#define MARKERS 5

#define COL_SKY 0xFF1B1826u
#define COL_DIM 0xFF9A93B3u

static const char *level[ROWS] = {
	"################",
	"#..............#",
	"#.####....####.#",
	"#.#..........#.#",
	"#.#..........#.#",
	"#.#...####...#.#",
	"#.....#..#.....#",
	"#.....#..#.....#",
	"#.#...#..#...#.#",
	"#.#...####...#.#",
	"#.#..........#.#",
	"#.#..........#.#",
	"#.####....####.#",
	"#..............#",
	"#..............#",
	"################",
};

typedef struct state {
	kon_tilemap_t map;
	uint16_t tiles[COLUMNS * ROWS];
	uint8_t solid[2];

	kon_image *brick, *floor, *marker;
	int ownsMarker;
	kon_fp_t fp;
	double time;
	kon_vec2_t markers[MARKERS]; /* world units */
} state_t;

/*** images made in code ***/

static kon_image *makeBrick(void)
{
	kon_image *image = kon_createFramebuffer(32, 32);
	int row, i;

	if (!image) return NULL;
	kon_clearFramebuffer(image, 0xFF6B5F92);
	for (row = 0; row < 4; row++) {
		kon_fillRectangle(image, 0, row * 8, 32, 1, 0xFF3A3558);
		for (i = 0; i < 2; i++) kon_fillRectangle(image, (row % 2) * 8 + i * 16, row * 8, 1, 8, 0xFF3A3558);
	}
	return image;
}

static kon_image *makeFloor(void)
{
	kon_image *image = kon_createFramebuffer(32, 32);

	if (!image) return NULL;
	kon_clearFramebuffer(image, 0xFF2A2640);
	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, 0xFF6B5F92);
	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, 0xFF6B5F92);
		kon_fillRectangle(image, 2 + i, 37 - i * 36 / 28, 1, 1, 0xFF6B5F92);
	}
	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_material_t brick = kon_materialTexture(state->brick), floor = kon_materialTexture(state->floor);
	kon_level3d_t level = { &state->map, state->solid, 2, WALL_HEIGHT, &brick, &floor, &floor };
	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.3f, -1.0f, -0.5f);
	light.ambient = 0.55f;
	kon_drawLevel(game, &camera, &level, &light);

	for (i = 0; i < MARKERS; i++) {
		float dx = state->markers[i].x - camera.position.x, dz = state->markers[i].y - camera.position.z;
		float bob = 0.08f * sinf((float)state->time * 2.0f + (float)i);

		kon_drawBillboard(game, vp, KON_VEC3(state->markers[i].x, 0.1f + bob, state->markers[i].y), 0.6f, 0.75f, state->marker, KON_RECT(0, 0, 0, 0),
			kon_distanceFade(sqrtf(dx * dx + dz * dz), 0.18f), KON_RENDER_DEPTH);
	}

	if (program->font) kon_drawText(game, program->font, 8, 16, "click to look with the mouse, tab lets go", COL_DIM);
}

/*** program ***/

static int event(kon_program_t *program, const kon_event_t *ev, void *user)
{
	state_t *state = user;

	return kon_fpEvent(&state->fp, program, ev);
}

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, NULL, dt);
}

static int init(kon_program_t *program, void *user)
{
	state_t *state = user;
	static const kon_vec2_t spots[MARKERS] = { { 3.5f, 6.5f }, { 12.5f, 3.5f }, { 8.0f, 13.0f }, { 5.5f, 11.5f }, { 11.5f, 7.5f } };
	int row, column, i;

	state->map.columns = COLUMNS;
	state->map.rows = ROWS;
	state->map.tileWidth = TILE;
	state->map.tileHeight = TILE;
	state->map.tiles = state->tiles;
	state->solid[1] = 1;
	for (row = 0; row < ROWS; row++) {
		for (column = 0; column < COLUMNS; column++) state->tiles[row * COLUMNS + column] = level[row][column] == '#';
	}

	state->brick = makeBrick();
	state->floor = makeFloor();
#if defined(KON_MODULE_ASSETS) && defined(KON_MODULE_IMAGE)
	if (program->assets) state->marker = kon_assetImage(program->assets, "sprites/marker.png");
#endif
	if (!state->marker) {
		state->marker = makeMarker();
		state->ownsMarker = 1;
	}
	if (!state->brick || !state->floor || !state->marker) return 0;

	kon_fpInit(&state->fp, &state->map, 1, 13);
	kon_fpDebug(&state->fp, program->debug);
	for (i = 0; i < MARKERS; i++) state->markers[i] = spots[i];
	return 1;
}

static void shutdown(kon_program_t *program, void *user)
{
	state_t *state = user;

	(void)program;
	kon_freeFramebuffer(state->brick);
	kon_freeFramebuffer(state->floor);
	if (state->ownsMarker) kon_freeFramebuffer(state->marker);
}

int main(void)
{
	state_t state = {0};
	kon_programConfig_t config = {0};

	config.title = "corridor";
	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;
	return kon_programRun(&config);
}