main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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 renderer_push_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 renderer_push_quad(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. renderer_push_vertex(r, a, v2f(0.0f, 0.0f), color);
  168. renderer_push_vertex(r, b, v2f(1.0f, 0.0f), color);
  169. renderer_push_vertex(r, c, v2f(0.0f, 1.0f), color);
  170. renderer_push_vertex(r, b, v2f(1.0f, 0.0f), color);
  171. renderer_push_vertex(r, c, v2f(0.0f, 1.0f), color);
  172. renderer_push_vertex(r, d, v2f(1.0f, 1.0f), color);
  173. }
  174. void renderer_push_checker_board(Renderer *r, int grid_size)
  175. {
  176. float cell_width = 2.0f/grid_size;
  177. float cell_height = 2.0f/grid_size;
  178. for (int y = 0; y < grid_size; ++y) {
  179. for (int x = 0; x < grid_size; ++x) {
  180. renderer_push_quad(
  181. r,
  182. v2f(-1.0f + x*cell_width, -1.0f + y*cell_height),
  183. v2f(-1.0f + (x + 1)*cell_width, -1.0f + (y + 1)*cell_height),
  184. (x + y)%2 == 0 ? v4f(1.0f, 0.0f, 0.0f, 1.0f) : v4f(0.0f, 0.0f, 0.0f, 1.0f));
  185. }
  186. }
  187. }
  188. void renderer_sync(Renderer *r)
  189. {
  190. glBufferSubData(GL_ARRAY_BUFFER,
  191. 0,
  192. sizeof(Vertex) * r->vertex_buf_sz,
  193. r->vertex_buf);
  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. void renderer_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;
  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. }
  296. void renderer_reload_shaders(Renderer *r)
  297. {
  298. glDeleteProgram(r->program);
  299. r->reload_failed = true;
  300. glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
  301. {
  302. if (!load_shader_program(vert_path, frag_path, &r->program)) {
  303. return;
  304. }
  305. glUseProgram(r->program);
  306. for (Uniform index = 0; index < COUNT_UNIFORMS; ++index) {
  307. r->uniforms[index] = glGetUniformLocation(r->program, uniform_names[index]);
  308. }
  309. }
  310. r->reload_failed = false;
  311. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  312. printf("Successfully Reload the Shaders\n");
  313. }
  314. void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
  315. {
  316. (void) scancode;
  317. (void) action;
  318. (void) mods;
  319. if (action == GLFW_PRESS) {
  320. if (key == GLFW_KEY_F5) {
  321. reload_render_conf("render.conf");
  322. renderer_reload_textures(&global_renderer);
  323. renderer_reload_shaders(&global_renderer);
  324. } else if (key == GLFW_KEY_F6) {
  325. #define SCREENSHOT_PNG_PATH "screenshot.png"
  326. printf("Saving the screenshot at %s\n", SCREENSHOT_PNG_PATH);
  327. int width, height;
  328. glfwGetWindowSize(window, &width, &height);
  329. void *pixels = malloc(4 * width * height);
  330. if (pixels == NULL) {
  331. fprintf(stderr, "ERROR: could not allocate memory for pixels to make a screenshot: %s\n",
  332. strerror(errno));
  333. return;
  334. }
  335. glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
  336. if (!stbi_write_png(SCREENSHOT_PNG_PATH, width, height, 4, pixels, width * 4)) {
  337. fprintf(stderr, "ERROR: could not save %s: %s\n", SCREENSHOT_PNG_PATH, strerror(errno));
  338. }
  339. free(pixels);
  340. } else if (key == GLFW_KEY_SPACE) {
  341. pause = !pause;
  342. } else if (key == GLFW_KEY_Q) {
  343. exit(1);
  344. }
  345. if (pause) {
  346. if (key == GLFW_KEY_LEFT) {
  347. time -= MANUAL_TIME_STEP;
  348. } else if (key == GLFW_KEY_RIGHT) {
  349. time += MANUAL_TIME_STEP;
  350. }
  351. }
  352. }
  353. }
  354. void window_size_callback(GLFWwindow* window, int width, int height)
  355. {
  356. (void) window;
  357. glViewport(0, 0, width, height);
  358. }
  359. void MessageCallback(GLenum source,
  360. GLenum type,
  361. GLuint id,
  362. GLenum severity,
  363. GLsizei length,
  364. const GLchar* message,
  365. const void* userParam)
  366. {
  367. (void) source;
  368. (void) id;
  369. (void) length;
  370. (void) userParam;
  371. fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
  372. (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
  373. type, severity, message);
  374. }
  375. void renderer_init(Renderer *r)
  376. {
  377. glGenVertexArrays(1, &r->vao);
  378. glBindVertexArray(r->vao);
  379. glGenBuffers(1, &r->vbo);
  380. glBindBuffer(GL_ARRAY_BUFFER, r->vbo);
  381. glBufferData(GL_ARRAY_BUFFER, sizeof(r->vertex_buf), r->vertex_buf, GL_DYNAMIC_DRAW);
  382. glEnableVertexAttribArray(VA_POS);
  383. glVertexAttribPointer(VA_POS,
  384. 2,
  385. GL_FLOAT,
  386. GL_FALSE,
  387. sizeof(Vertex),
  388. (void*) offsetof(Vertex, pos));
  389. glEnableVertexAttribArray(VA_UV);
  390. glVertexAttribPointer(VA_UV,
  391. 2,
  392. GL_FLOAT,
  393. GL_FALSE,
  394. sizeof(Vertex),
  395. (void*) offsetof(Vertex, uv));
  396. glEnableVertexAttribArray(VA_COLOR);
  397. glVertexAttribPointer(VA_COLOR,
  398. 4,
  399. GL_FLOAT,
  400. GL_FALSE,
  401. sizeof(Vertex),
  402. (void*) offsetof(Vertex, color));
  403. }
  404. void renderer_clear(Renderer *r)
  405. {
  406. r->vertex_buf_sz = 0;
  407. }
  408. #define COLOR_BLACK_V4F ((V4f){0.0f, 0.0f, 0.0f, 1.0f})
  409. int main(void)
  410. {
  411. reload_render_conf("render.conf");
  412. if (!glfwInit()) {
  413. fprintf(stderr, "ERROR: could not initialize GLFW\n");
  414. exit(1);
  415. }
  416. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  417. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  418. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  419. GLFWwindow * const window = glfwCreateWindow(
  420. DEFAULT_SCREEN_WIDTH,
  421. DEFAULT_SCREEN_HEIGHT,
  422. "OpenGL Template",
  423. NULL,
  424. NULL);
  425. if (window == NULL) {
  426. fprintf(stderr, "ERROR: could not create a window.\n");
  427. glfwTerminate();
  428. exit(1);
  429. }
  430. int gl_ver_major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
  431. int gl_ver_minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
  432. printf("OpenGL %d.%d\n", gl_ver_major, gl_ver_minor);
  433. glfwMakeContextCurrent(window);
  434. load_gl_extensions();
  435. if (glDrawArraysInstanced == NULL) {
  436. fprintf(stderr, "Support for EXT_draw_instanced is required!\n");
  437. exit(1);
  438. }
  439. if (glDebugMessageCallback != NULL) {
  440. glEnable(GL_DEBUG_OUTPUT);
  441. glDebugMessageCallback(MessageCallback, 0);
  442. }
  443. glEnable(GL_BLEND);
  444. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  445. renderer_init(&global_renderer);
  446. renderer_reload_textures(&global_renderer);
  447. renderer_reload_shaders(&global_renderer);
  448. glfwSetKeyCallback(window, key_callback);
  449. glfwSetFramebufferSizeCallback(window, window_size_callback);
  450. time = glfwGetTime();
  451. double prev_time = 0.0;
  452. while (!glfwWindowShouldClose(window)) {
  453. glClear(GL_COLOR_BUFFER_BIT);
  454. int width, height;
  455. glfwGetWindowSize(window, &width, &height);
  456. double xpos, ypos;
  457. glfwGetCursorPos(window, &xpos, &ypos);
  458. if (!global_renderer.reload_failed) {
  459. static_assert(COUNT_UNIFORMS == 3, "Update the uniform sync");
  460. glUniform2f(global_renderer.uniforms[RESOLUTION_UNIFORM], (GLfloat) width, (GLfloat) height);
  461. glUniform1f(global_renderer.uniforms[TIME_UNIFORM], (GLfloat) time);
  462. glUniform2f(global_renderer.uniforms[MOUSE_UNIFORM], (GLfloat) xpos, (GLfloat) (height - ypos));
  463. renderer_clear(&global_renderer);
  464. renderer_push_quad(
  465. &global_renderer,
  466. v2f(width * -0.5f, height * -0.5f),
  467. v2f(width * 0.5f, height * 0.5f),
  468. COLOR_BLACK_V4F);
  469. renderer_sync(&global_renderer);
  470. glDrawArraysInstanced(GL_TRIANGLES, 0, (GLsizei) global_renderer.vertex_buf_sz, 1);
  471. }
  472. glfwSwapBuffers(window);
  473. glfwPollEvents();
  474. double cur_time = glfwGetTime();
  475. if (!pause) {
  476. time += cur_time - prev_time;
  477. }
  478. prev_time = cur_time;
  479. }
  480. return 0;
  481. }