main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. #include <assert.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <stdarg.h>
  5. #include <stdbool.h>
  6. #include <string.h>
  7. #include <errno.h>
  8. #include <math.h>
  9. #define GLFW_INCLUDE_GLEXT
  10. #include <GLFW/glfw3.h>
  11. #define LA_IMPLEMENTATION
  12. #include "la.h"
  13. #define SV_IMPLEMENTATION
  14. #include "sv.h"
  15. #define DEFAULT_SCREEN_WIDTH 1600
  16. #define DEFAULT_SCREEN_HEIGHT 900
  17. #define MANUAL_TIME_STEP 0.1
  18. #include "glextloader.c"
  19. #define STB_IMAGE_IMPLEMENTATION
  20. #include "stb_image.h"
  21. #define STB_IMAGE_WRITE_IMPLEMENTATION
  22. #include "stb_image_write.h"
  23. char *slurp_file_into_malloced_cstr(const char *file_path)
  24. {
  25. FILE *f = NULL;
  26. char *buffer = NULL;
  27. f = fopen(file_path, "r");
  28. if (f == NULL) goto fail;
  29. if (fseek(f, 0, SEEK_END) < 0) goto fail;
  30. long size = ftell(f);
  31. if (size < 0) goto fail;
  32. buffer = malloc(size + 1);
  33. if (buffer == NULL) goto fail;
  34. if (fseek(f, 0, SEEK_SET) < 0) goto fail;
  35. fread(buffer, 1, size, f);
  36. if (ferror(f)) goto fail;
  37. buffer[size] = '\0';
  38. if (f) {
  39. fclose(f);
  40. errno = 0;
  41. }
  42. return buffer;
  43. fail:
  44. if (f) {
  45. int saved_errno = errno;
  46. fclose(f);
  47. errno = saved_errno;
  48. }
  49. if (buffer) {
  50. free(buffer);
  51. }
  52. return NULL;
  53. }
  54. const char *shader_type_as_cstr(GLuint shader)
  55. {
  56. switch (shader) {
  57. case GL_VERTEX_SHADER:
  58. return "GL_VERTEX_SHADER";
  59. case GL_FRAGMENT_SHADER:
  60. return "GL_FRAGMENT_SHADER";
  61. default:
  62. return "(Unknown)";
  63. }
  64. }
  65. bool compile_shader_source(const GLchar *source, GLenum shader_type, GLuint *shader)
  66. {
  67. *shader = glCreateShader(shader_type);
  68. glShaderSource(*shader, 1, &source, NULL);
  69. glCompileShader(*shader);
  70. GLint compiled = 0;
  71. glGetShaderiv(*shader, GL_COMPILE_STATUS, &compiled);
  72. if (!compiled) {
  73. GLchar message[1024];
  74. GLsizei message_size = 0;
  75. glGetShaderInfoLog(*shader, sizeof(message), &message_size, message);
  76. fprintf(stderr, "ERROR: could not compile %s\n", shader_type_as_cstr(shader_type));
  77. fprintf(stderr, "%.*s\n", message_size, message);
  78. return false;
  79. }
  80. return true;
  81. }
  82. bool compile_shader_file(const char *file_path, GLenum shader_type, GLuint *shader)
  83. {
  84. char *source = slurp_file_into_malloced_cstr(file_path);
  85. if (source == NULL) {
  86. fprintf(stderr, "ERROR: failed to read file `%s`: %s\n", file_path, strerror(errno));
  87. errno = 0;
  88. return false;
  89. }
  90. bool ok = compile_shader_source(source, shader_type, shader);
  91. if (!ok) {
  92. fprintf(stderr, "ERROR: failed to compile `%s` shader file\n", file_path);
  93. }
  94. free(source);
  95. return ok;
  96. }
  97. bool link_program(GLuint vert_shader, GLuint frag_shader, GLuint *program)
  98. {
  99. *program = glCreateProgram();
  100. glAttachShader(*program, vert_shader);
  101. glAttachShader(*program, frag_shader);
  102. glLinkProgram(*program);
  103. GLint linked = 0;
  104. glGetProgramiv(*program, GL_LINK_STATUS, &linked);
  105. if (!linked) {
  106. GLsizei message_size = 0;
  107. GLchar message[1024];
  108. glGetProgramInfoLog(*program, sizeof(message), &message_size, message);
  109. fprintf(stderr, "Program Linking: %.*s\n", message_size, message);
  110. }
  111. glDeleteShader(vert_shader);
  112. glDeleteShader(frag_shader);
  113. return program;
  114. }
  115. typedef enum {
  116. RESOLUTION_UNIFORM = 0,
  117. TIME_UNIFORM,
  118. MOUSE_UNIFORM,
  119. COUNT_UNIFORMS
  120. } Uniform;
  121. static_assert(COUNT_UNIFORMS == 3, "Update list of uniform names");
  122. static const char *uniform_names[COUNT_UNIFORMS] = {
  123. [RESOLUTION_UNIFORM] = "resolution",
  124. [TIME_UNIFORM] = "time",
  125. [MOUSE_UNIFORM] = "mouse",
  126. };
  127. typedef enum {
  128. VA_POS = 0,
  129. VA_UV,
  130. VA_COLOR,
  131. COUNT_VAS,
  132. } Vertex_Attrib;
  133. typedef struct {
  134. V2f pos;
  135. V2f uv;
  136. V4f color;
  137. } Vertex;
  138. #define VERTEX_BUF_CAP (8 * 1024)
  139. typedef struct {
  140. bool reload_failed;
  141. GLuint vao;
  142. GLuint vbo;
  143. GLuint program;
  144. GLint uniforms[COUNT_UNIFORMS];
  145. Vertex vertex_buf[VERTEX_BUF_CAP];
  146. size_t vertex_buf_sz;
  147. GLuint texture;
  148. } Renderer;
  149. // Global variables (fragile people with CS degree look away)
  150. static double time = 0.0;
  151. static bool pause = false;
  152. static Renderer global_renderer = {0};
  153. void r_vertex(Renderer *r, V2f pos, V2f uv, V4f color)
  154. {
  155. assert(r->vertex_buf_sz < VERTEX_BUF_CAP);
  156. r->vertex_buf[r->vertex_buf_sz].pos = pos;
  157. r->vertex_buf[r->vertex_buf_sz].uv = uv;
  158. r->vertex_buf[r->vertex_buf_sz].color = color;
  159. r->vertex_buf_sz += 1;
  160. }
  161. void r_quad_pp(Renderer *r, V2f p1, V2f p2, V4f color)
  162. {
  163. V2f a = p1;
  164. V2f b = v2f(p2.x, p1.y);
  165. V2f c = v2f(p1.x, p2.y);
  166. V2f d = p2;
  167. r_vertex(r, a, v2f(0.0f, 0.0f), color);
  168. r_vertex(r, b, v2f(1.0f, 0.0f), color);
  169. r_vertex(r, c, v2f(0.0f, 1.0f), color);
  170. r_vertex(r, b, v2f(1.0f, 0.0f), color);
  171. r_vertex(r, c, v2f(0.0f, 1.0f), color);
  172. r_vertex(r, d, v2f(1.0f, 1.0f), color);
  173. }
  174. void r_quad_cr(Renderer *r, V2f center, V2f radius, V4f color)
  175. {
  176. r_quad_pp(r, v2f_sub(center, radius), v2f_sum(center, radius), color);
  177. }
  178. void r_sync_buffers(Renderer *r)
  179. {
  180. glBufferSubData(GL_ARRAY_BUFFER,
  181. 0,
  182. sizeof(Vertex) * r->vertex_buf_sz,
  183. r->vertex_buf);
  184. }
  185. void r_sync_uniforms(Renderer *r,
  186. GLfloat resolution_width, GLfloat resolution_height,
  187. GLfloat time,
  188. GLfloat mouse_x, GLfloat mouse_y)
  189. {
  190. static_assert(COUNT_UNIFORMS == 3, "Exhaustive uniform handling in ");
  191. glUniform2f(r->uniforms[RESOLUTION_UNIFORM], resolution_width, resolution_height);
  192. glUniform1f(r->uniforms[TIME_UNIFORM], time);
  193. glUniform2f(r->uniforms[MOUSE_UNIFORM], mouse_x, mouse_y);
  194. }
  195. bool load_shader_program(const char *vertex_file_path,
  196. const char *fragment_file_path,
  197. GLuint *program)
  198. {
  199. GLuint vert = 0;
  200. if (!compile_shader_file(vertex_file_path, GL_VERTEX_SHADER, &vert)) {
  201. return false;
  202. }
  203. GLuint frag = 0;
  204. if (!compile_shader_file(fragment_file_path, GL_FRAGMENT_SHADER, &frag)) {
  205. return false;
  206. }
  207. if (!link_program(vert, frag, program)) {
  208. return false;
  209. }
  210. return true;
  211. }
  212. static char *render_conf = NULL;
  213. const char *vert_path = NULL;
  214. const char *frag_path = NULL;
  215. const char *texture_path = NULL;
  216. void reload_render_conf(const char *render_conf_path)
  217. {
  218. if (render_conf) free(render_conf);
  219. render_conf = slurp_file_into_malloced_cstr(render_conf_path);
  220. if (render_conf == NULL) {
  221. fprintf(stderr, "ERROR: could not load %s: %s\n", render_conf_path, strerror(errno));
  222. exit(1);
  223. }
  224. String_View content = sv_from_cstr(render_conf);
  225. vert_path = NULL;
  226. frag_path = NULL;
  227. texture_path = NULL;
  228. for (int row = 0; content.count > 0; row++) {
  229. String_View line = sv_chop_by_delim(&content, '\n');
  230. const char *line_start = line.data;
  231. line = sv_trim_left(line);
  232. if (line.count > 0 && line.data[0] != '#') {
  233. String_View key = sv_trim(sv_chop_by_delim(&line, '='));
  234. String_View value = sv_trim_left(line);
  235. ((char*)value.data)[value.count] = '\0';
  236. // ^^^SAFETY NOTES: this is needed so we can use `value` as a NULL-terminated C-string.
  237. // This should not cause any problems because the original string `render_conf`
  238. // that we are processing the `value` from is mutable, NULL-terminated and we are splitting
  239. // it by newlines which garantees that there is always a character after
  240. // the end of `value`.
  241. //
  242. // Let's consider an example where `render_conf` is equal to this:
  243. //
  244. // ```
  245. // key = value\n
  246. // key = value\n
  247. // key = value\0
  248. // ```
  249. //
  250. // There is always something after `value`. It's either `\n` or `\0`. With all of these
  251. // invariats in place writing to `value.data[value.count]` should be safe.
  252. if (sv_eq(key, SV("vert"))) {
  253. vert_path = value.data;
  254. printf("Vertex Path: %s\n", vert_path);
  255. } else if (sv_eq(key, SV("frag"))) {
  256. frag_path = value.data;
  257. printf("Fragment Path: %s\n", frag_path);
  258. } else if (sv_eq(key, SV("texture"))) {
  259. texture_path = value.data;
  260. printf("Texture Path: %s\n", texture_path);
  261. } else {
  262. printf("%s:%d:%ld: ERROR: unsupported key `"SV_Fmt"`\n",
  263. render_conf_path, row, key.data - line_start,
  264. SV_Arg(key));
  265. }
  266. }
  267. }
  268. }
  269. bool r_reload_textures(Renderer *r)
  270. {
  271. int texture_width, texture_height;
  272. unsigned char *texture_pixels = stbi_load(texture_path, &texture_width, &texture_height, NULL, 4);
  273. if (texture_pixels == NULL) {
  274. fprintf(stderr, "ERROR: could not load image %s: %s\n",
  275. texture_path, strerror(errno));
  276. return false;
  277. }
  278. glDeleteTextures(1, &r->texture);
  279. glGenTextures(1, &r->texture);
  280. glBindTexture(GL_TEXTURE_2D, r->texture);
  281. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  282. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  283. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
  284. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
  285. glTexImage2D(GL_TEXTURE_2D,
  286. 0,
  287. GL_RGBA,
  288. texture_width,
  289. texture_height,
  290. 0,
  291. GL_RGBA,
  292. GL_UNSIGNED_BYTE,
  293. texture_pixels);
  294. stbi_image_free(texture_pixels);
  295. printf("Successfully reloaded textures\n");
  296. return true;
  297. }
  298. bool r_reload_shaders(Renderer *r)
  299. {
  300. glDeleteProgram(r->program);
  301. if (!load_shader_program(vert_path, frag_path, &r->program)) return false;
  302. glUseProgram(r->program);
  303. for (Uniform index = 0; index < COUNT_UNIFORMS; ++index) {
  304. r->uniforms[index] = glGetUniformLocation(r->program, uniform_names[index]);
  305. }
  306. printf("Successfully reloaded the Shaders\n");
  307. return true;
  308. }
  309. void r_reload(Renderer *r)
  310. {
  311. r->reload_failed = true;
  312. glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
  313. if (!r_reload_shaders(r)) return;
  314. if (!r_reload_textures(r)) return;
  315. r->reload_failed = false;
  316. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  317. }
  318. void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  319. {
  320. (void) scancode;
  321. (void) action;
  322. (void) mods;
  323. if (action == GLFW_PRESS) {
  324. if (key == GLFW_KEY_F5) {
  325. reload_render_conf("render.conf");
  326. r_reload(&global_renderer);
  327. } else if (key == GLFW_KEY_F6) {
  328. #define SCREENSHOT_PNG_PATH "screenshot.png"
  329. printf("Saving the screenshot at %s\n", SCREENSHOT_PNG_PATH);
  330. int width, height;
  331. glfwGetWindowSize(window, &width, &height);
  332. void *pixels = malloc(4 * width * height);
  333. if (pixels == NULL) {
  334. fprintf(stderr, "ERROR: could not allocate memory for pixels to make a screenshot: %s\n",
  335. strerror(errno));
  336. return;
  337. }
  338. glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  339. if (!stbi_write_png(SCREENSHOT_PNG_PATH, width, height, 4, pixels, width * 4)) {
  340. fprintf(stderr, "ERROR: could not save %s: %s\n", SCREENSHOT_PNG_PATH, strerror(errno));
  341. }
  342. free(pixels);
  343. } else if (key == GLFW_KEY_SPACE) {
  344. pause = !pause;
  345. } else if (key == GLFW_KEY_Q) {
  346. exit(1);
  347. }
  348. if (pause) {
  349. if (key == GLFW_KEY_LEFT) {
  350. time -= MANUAL_TIME_STEP;
  351. } else if (key == GLFW_KEY_RIGHT) {
  352. time += MANUAL_TIME_STEP;
  353. }
  354. }
  355. }
  356. }
  357. void window_size_callback(GLFWwindow* window, int width, int height)
  358. {
  359. (void) window;
  360. glViewport(0, 0, width, height);
  361. }
  362. void MessageCallback(GLenum source,
  363. GLenum type,
  364. GLuint id,
  365. GLenum severity,
  366. GLsizei length,
  367. const GLchar* message,
  368. const void* userParam)
  369. {
  370. (void) source;
  371. (void) id;
  372. (void) length;
  373. (void) userParam;
  374. fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
  375. (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
  376. type, severity, message);
  377. }
  378. void r_init(Renderer *r)
  379. {
  380. glGenVertexArrays(1, &r->vao);
  381. glBindVertexArray(r->vao);
  382. glGenBuffers(1, &r->vbo);
  383. glBindBuffer(GL_ARRAY_BUFFER, r->vbo);
  384. glBufferData(GL_ARRAY_BUFFER, sizeof(r->vertex_buf), r->vertex_buf, GL_DYNAMIC_DRAW);
  385. glEnableVertexAttribArray(VA_POS);
  386. glVertexAttribPointer(VA_POS,
  387. 2,
  388. GL_FLOAT,
  389. GL_FALSE,
  390. sizeof(Vertex),
  391. (void*) offsetof(Vertex, pos));
  392. glEnableVertexAttribArray(VA_UV);
  393. glVertexAttribPointer(VA_UV,
  394. 2,
  395. GL_FLOAT,
  396. GL_FALSE,
  397. sizeof(Vertex),
  398. (void*) offsetof(Vertex, uv));
  399. glEnableVertexAttribArray(VA_COLOR);
  400. glVertexAttribPointer(VA_COLOR,
  401. 4,
  402. GL_FLOAT,
  403. GL_FALSE,
  404. sizeof(Vertex),
  405. (void*) offsetof(Vertex, color));
  406. }
  407. void r_clear(Renderer *r)
  408. {
  409. r->vertex_buf_sz = 0;
  410. }
  411. #define COLOR_BLACK_V4F ((V4f){0.0f, 0.0f, 0.0f, 1.0f})
  412. int main(void)
  413. {
  414. reload_render_conf("render.conf");
  415. if (!glfwInit()) {
  416. fprintf(stderr, "ERROR: could not initialize GLFW\n");
  417. exit(1);
  418. }
  419. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  420. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  421. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  422. GLFWwindow * const window = glfwCreateWindow(
  423. DEFAULT_SCREEN_WIDTH,
  424. DEFAULT_SCREEN_HEIGHT,
  425. "OpenGL Template",
  426. NULL,
  427. NULL);
  428. if (window == NULL) {
  429. fprintf(stderr, "ERROR: could not create a window.\n");
  430. glfwTerminate();
  431. exit(1);
  432. }
  433. int gl_ver_major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
  434. int gl_ver_minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
  435. printf("OpenGL %d.%d\n", gl_ver_major, gl_ver_minor);
  436. glfwMakeContextCurrent(window);
  437. load_gl_extensions();
  438. if (glDrawArraysInstanced == NULL) {
  439. fprintf(stderr, "Support for EXT_draw_instanced is required!\n");
  440. exit(1);
  441. }
  442. if (glDebugMessageCallback != NULL) {
  443. glEnable(GL_DEBUG_OUTPUT);
  444. glDebugMessageCallback(MessageCallback, 0);
  445. }
  446. glEnable(GL_BLEND);
  447. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  448. Renderer *r = &global_renderer;
  449. r_init(r);
  450. r_reload(r);
  451. glfwSetKeyCallback(window, key_callback);
  452. glfwSetFramebufferSizeCallback(window, window_size_callback);
  453. time = glfwGetTime();
  454. double prev_time = 0.0;
  455. double delta_time = 0.0f;
  456. while (!glfwWindowShouldClose(window)) {
  457. glClear(GL_COLOR_BUFFER_BIT);
  458. int width, height;
  459. glfwGetWindowSize(window, &width, &height);
  460. double xpos, ypos;
  461. glfwGetCursorPos(window, &xpos, &ypos);
  462. xpos = xpos - width * 0.5f;
  463. ypos = (height - ypos) - height * 0.5f;
  464. if (!global_renderer.reload_failed) {
  465. r_clear(r);
  466. r_sync_uniforms(r, width, height, time, xpos, ypos);
  467. r_quad_cr(
  468. r,
  469. v2ff(0.0f),
  470. v2f_mul(v2f(width, height), v2ff(0.5f)),
  471. COLOR_BLACK_V4F);
  472. r_sync_buffers(r);
  473. glDrawArraysInstanced(GL_TRIANGLES, 0, (GLsizei) global_renderer.vertex_buf_sz, 1);
  474. }
  475. glfwSwapBuffers(window);
  476. glfwPollEvents();
  477. double cur_time = glfwGetTime();
  478. delta_time = cur_time - prev_time;
  479. if (!pause) {
  480. time += delta_time;
  481. }
  482. prev_time = cur_time;
  483. }
  484. return 0;
  485. }