main.c (12276B)
1 #include <epoxy/gl.h> 2 #include <GLES3/gl3.h> 3 #include <GLFW/glfw3.h> 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <math.h> 9 #include <fcntl.h> 10 #include <unistd.h> 11 #include <sys/wait.h> 12 13 #include <arpa/inet.h> 14 15 #include "util.h" 16 #include "config.h" 17 18 /* Private definitions */ 19 typedef struct { 20 float x; 21 float y; 22 float scale; 23 } scout_instance_card_t; 24 25 typedef struct { 26 float x; 27 float y; 28 } scout_camera_t; 29 30 /* Misc. globals */ 31 const char *title = "SCOUT"; 32 scout_cfg_t cfg = DEFAULT_CFG; 33 34 scout_camera_t camera = { 35 .x = 0.0f, 36 .y = 0.0f 37 }; 38 39 /* Card data */ 40 unsigned int num_cards = 0; 41 unsigned int max_cards = 0; 42 unsigned int current_card = 0; 43 scout_card_t *cards; 44 unsigned int *card_textures; 45 float *card_ratios; 46 float *card_offsets; 47 48 /* Actions */ 49 int change_card = 0; 50 int launch_card = 0; 51 52 /* OpenGL variables */ 53 unsigned int vertex_shader; 54 unsigned int fragment_shader; 55 int scale_uniform; 56 int trans_uniform; 57 int camera_uniform; 58 int texture_uniform; 59 60 /* Load all shaders from file */ 61 void load_shaders() 62 { 63 const char *path; 64 if (!(path = getenv("SCOUT_SHADER_PATH"))) 65 path = SHADER_PATH; 66 67 int dir_fd = open(path, O_RDONLY | O_DIRECTORY); 68 if (dir_fd < 0) { 69 fprintf(stderr, "Unable to open shader path \"%s\"\n", path); 70 return; 71 } 72 73 int vert_fd = openat(dir_fd, "vert.glsl", O_RDONLY); 74 if (vert_fd < 0) { 75 fprintf(stderr, "Unable to open vertex shader\n"); 76 return; 77 } 78 79 int frag_fd = openat(dir_fd, "frag.glsl", O_RDONLY); 80 if (frag_fd < 0) { 81 fprintf(stderr, "Unable to open fragment shader\n"); 82 return; 83 } 84 85 int success; 86 87 const char *vert_source = fdreadfile(vert_fd); 88 89 vertex_shader = glCreateShader(GL_VERTEX_SHADER); 90 glShaderSource(vertex_shader, 1, &vert_source, NULL); 91 glCompileShader(vertex_shader); 92 93 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); 94 if (!success) { 95 fprintf(stderr, "Failed to compile vertex shader!\n"); 96 return; 97 } 98 99 const char *frag_source = fdreadfile(frag_fd); 100 101 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); 102 glShaderSource(fragment_shader, 1, &frag_source, NULL); 103 glCompileShader(fragment_shader); 104 105 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); 106 if (!success) { 107 fprintf(stderr, "Failed to compile fragment shader!\n"); 108 return; 109 } 110 111 fprintf(stderr, "Compiled shaders\n"); 112 } 113 114 /* GLFW Callbacks */ 115 void error_callback(int error, const char *description) 116 { 117 fprintf(stderr, "GLFW Error: %s\n", description); 118 } 119 120 void framebuffer_size_callback(GLFWwindow *window, int width, int height) 121 { 122 glViewport(0, 0, width, height); 123 /* Apply aspect ratio */ 124 float ratio = (float) width / (float) height; 125 fprintf(stderr, "Resized: %i x %i, max %f\n", width, height, ratio); 126 glUniform2f(scale_uniform, ratio, 1.0f); 127 } 128 129 /* TODO: Proper input management */ 130 void key_callback(GLFWwindow *window, int key, int scancode, int action, 131 int mods) 132 { 133 if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS) { 134 change_card = 1; 135 return; 136 } 137 if (key == GLFW_KEY_LEFT && action == GLFW_PRESS) { 138 change_card = -1; 139 return; 140 } 141 if (key == GLFW_KEY_ENTER && action == GLFW_PRESS) { 142 launch_card = 1; 143 return; 144 } 145 } 146 147 void joystick_callback(int jid, int event) 148 { 149 if (!glfwJoystickIsGamepad(jid)) 150 return; 151 152 if (event == GLFW_CONNECTED) { 153 fprintf(stderr, "Connected %d\n", jid); 154 } else if (event == GLFW_DISCONNECTED) { 155 fprintf(stderr, "Disconnected %d\n", jid); 156 } 157 } 158 159 int main() 160 { 161 /* Initialize GLFW */ 162 glfwSetErrorCallback(error_callback); 163 164 if (!glfwInit()) { 165 printf("Failed to initialize GLFW!\n"); 166 return -1; 167 } 168 169 glfwSetJoystickCallback(joystick_callback); 170 171 /* Load configuration */ 172 load_config(&cfg, &num_cards, &cards); 173 fprintf(stderr, "Loaded %d cards\n", num_cards); 174 175 176 /* Window creation */ 177 GLFWwindow *window = glfwCreateWindow(640, 480, title, NULL, NULL); 178 if (!window) { 179 printf("Failed to create GLFW window!\n"); 180 glfwTerminate(); 181 return -1; 182 } 183 glfwMakeContextCurrent(window); 184 185 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); 186 glfwSetKeyCallback(window, key_callback); 187 188 /* Controller setup */ 189 for (int i = 0; i < GLFW_JOYSTICK_LAST; i++) { 190 if (glfwJoystickPresent(i) && glfwJoystickIsGamepad(i)) { 191 fprintf(stderr, "Controller %d available\n", i); 192 } 193 } 194 195 /* Set up shaders */ 196 load_shaders(); 197 198 unsigned int shader_program = glCreateProgram(); 199 200 glAttachShader(shader_program, vertex_shader); 201 glAttachShader(shader_program, fragment_shader); 202 glLinkProgram(shader_program); 203 204 glDeleteShader(vertex_shader); 205 glDeleteShader(fragment_shader); 206 207 /* Uniforms */ 208 scale_uniform = glGetUniformLocation(shader_program, "scale"); 209 trans_uniform = glGetUniformLocation(shader_program, "trans"); 210 camera_uniform = glGetUniformLocation(shader_program, "camera"); 211 212 texture_uniform = glGetUniformLocation(shader_program, "texture1"); 213 214 /* Set up vertex data and configure vertex attributes */ 215 float vertices[] = { 216 0.5f, 0.5f, 1.0f, 0.0f, 217 0.5f, -0.5f, 1.0f, 1.0f, 218 -0.5f, -0.5f, 0.0f, 1.0f, 219 -0.5f, 0.5f, 0.0f, 0.0f 220 }; 221 unsigned int indices[] = { 222 0, 1, 2, 223 2, 3, 0 224 }; 225 226 /* Create vertex arrays, buffers, etc. */ 227 unsigned int VBO, EBO, VAO; 228 glGenVertexArrays(1, &VAO); 229 glGenBuffers(1, &VBO); 230 glGenBuffers(1, &EBO); 231 232 glBindVertexArray(VAO); 233 234 glBindBuffer(GL_ARRAY_BUFFER, VBO); 235 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, 236 GL_STATIC_DRAW); 237 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 238 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, 239 GL_STATIC_DRAW); 240 241 /* Vertex coordinates */ 242 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 243 (void *) 0); 244 glEnableVertexAttribArray(0); 245 /* Texture coordinates */ 246 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 247 (void *) (2 * sizeof(float))); 248 glEnableVertexAttribArray(1); 249 250 /* Create cards */ 251 card_ratios = malloc(num_cards * sizeof(float)); 252 card_offsets = malloc(num_cards * sizeof(float)); 253 card_textures = malloc(num_cards * sizeof(unsigned int)); 254 glGenTextures(num_cards, card_textures); 255 256 for (unsigned int i = 0; i < num_cards; i++) { 257 printf(" %s\n", cards[i].title); 258 259 if (*cards[i].image_path != 0) { 260 /* Decode image */ 261 int img_fd = open(cards[i].image_path, O_RDONLY); 262 if (img_fd < 0) { 263 continue; 264 } 265 266 unsigned int hdr[4]; 267 read(img_fd, hdr, 16); 268 269 if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1) != 0) 270 continue; 271 272 unsigned int width = ntohl(hdr[2]); 273 unsigned int height = ntohl(hdr[3]); 274 size_t size = width * height * 8; 275 unsigned char *data = malloc(size); 276 read(img_fd, data, size); 277 278 close(img_fd); 279 280 /* Create OpenGL texture */ 281 glBindTexture(GL_TEXTURE_2D, card_textures[i]); 282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 283 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 284 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 285 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 286 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT, data); 287 288 /* The texture is on GPU so we can release the decoded data. */ 289 free(data); 290 291 /* Calculate the vertical scale of the image */ 292 card_ratios[i] = (float) height / (float) width; 293 } else { 294 /* This is really ugly but that's also kinda the point... */ 295 const unsigned char blah[16] = { 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff }; 296 297 /* Create OpenGL texture */ 298 glBindTexture(GL_TEXTURE_2D, card_textures[i]); 299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 303 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, blah); 304 305 /* TODO: Use default image */ 306 card_ratios[i] = 1.0f; 307 } 308 } 309 310 /* Main loop */ 311 float counter = 0.0f; 312 float time = 0.0f; 313 float lastCheck = 0.0f; 314 float fps = 0.0f; 315 316 float dt = 0.0f; 317 318 GLFWgamepadstate state; 319 float move_cooldown_left = 0.0f; 320 float move_cooldown_right = 0.0f; 321 int down = 0; 322 323 /* Set background color */ 324 glClearColor(cfg.bg.r, cfg.bg.g, cfg.bg.b, 1.0f); 325 326 while (!glfwWindowShouldClose(window)) { 327 /* Game Logic */ 328 lastCheck = time; 329 time = glfwGetTime(); 330 dt = time - lastCheck; 331 332 /* Controller setup */ 333 for (int i = 0; i < GLFW_JOYSTICK_LAST; i++) { 334 if (!glfwJoystickIsGamepad(i)) 335 continue; 336 337 glfwGetGamepadState(i, &state); 338 339 /* Avoid repeating inputs */ 340 if (state.buttons[GLFW_GAMEPAD_BUTTON_A]) { 341 if (down != 1) { 342 launch_card = 1; 343 down = 1; 344 } 345 } else { 346 down = 0; 347 } 348 349 if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] 350 || state.axes[GLFW_GAMEPAD_AXIS_LEFT_X] > 0.01f) { 351 if (move_cooldown_left <= 0.0f) { 352 change_card = 1; 353 move_cooldown_left = 0.2f; 354 } 355 } else { 356 move_cooldown_left = 0.0f; 357 } 358 359 if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] 360 || state.axes[GLFW_GAMEPAD_AXIS_LEFT_X] < -0.01f) { 361 if (move_cooldown_right <= 0.0f) { 362 change_card = -1; 363 move_cooldown_right = 0.2f; 364 } 365 } else { 366 move_cooldown_right = 0.0f; 367 } 368 } 369 370 move_cooldown_left -= dt; 371 move_cooldown_right -= dt; 372 373 /* Handle card change */ 374 if (change_card != 0) { 375 if (change_card < 0 && current_card > 0) { 376 current_card--; 377 } else if (change_card > 0 && current_card < num_cards - 1) { 378 current_card++; 379 } 380 381 change_card = 0; 382 } 383 384 /* Animate cards */ 385 for (int i = 0; i < num_cards; i++) { 386 if (i == current_card) 387 card_offsets[i] = exp_decay(card_offsets[i], 1.0f, 16.0f, dt); 388 else 389 card_offsets[i] = exp_decay(card_offsets[i], 0.0f, 16.0f, dt); 390 } 391 392 /* Smooth camera */ 393 camera.x = exp_decay(camera.x, (float) current_card, 8.0f, dt); 394 395 /* Handle launching card */ 396 if (launch_card) { 397 pid_t pid = fork(); 398 399 if (pid == 0) { 400 char *const argv[] = { 401 "sh", 402 "-c", 403 cards[current_card].command, 404 NULL 405 }; 406 407 execv("/bin/sh", argv); 408 409 printf("Error..."); 410 _exit(127); 411 } else if (pid > 0) { 412 printf("Launched %s\n", cards[current_card].title); 413 } else { 414 printf("Error launching\n"); 415 } 416 417 launch_card = 0; 418 419 /* TODO: Monitor child process */ 420 } 421 422 /* Calculate FPS */ 423 counter += 1.0f; 424 425 if (time - lastCheck >= 1.0f) { 426 fps = counter / (time - lastCheck); 427 //fprintf(stderr, "%.2f FPS\n", fps); 428 counter = 0.0f; 429 lastCheck = time; 430 } 431 432 /* Clear screen */ 433 glClear(GL_COLOR_BUFFER_BIT); 434 435 436 /* Render */ 437 glUseProgram(shader_program); 438 439 /* Update camera */ 440 glUniform2f(camera_uniform, camera.x, camera.y); 441 442 /* Bind mesh */ 443 glBindVertexArray(VAO); 444 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 445 446 for (int i = (current_card <= 2 ? 0 : current_card - 2); i < MIN(current_card + 3, num_cards); i++) { 447 glActiveTexture(GL_TEXTURE0); 448 glBindTexture(GL_TEXTURE_2D, card_textures[i]); 449 450 glUniform4f(trans_uniform, (float) i * 1.0f, card_ratios[i] * -0.5f * (1.0f - card_offsets[i]), card_ratios[i], 0.8f + card_offsets[i] * 0.2f); 451 452 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 453 } 454 455 glfwSwapBuffers(window); 456 glfwPollEvents(); 457 } 458 459 fprintf(stderr, "Closing\n"); 460 461 /* De-allocate GL resources when no longer needed */ 462 glDeleteVertexArrays(1, &VAO); 463 glDeleteBuffers(1, &VBO); 464 glDeleteBuffers(1, &EBO); 465 glDeleteProgram(shader_program); 466 467 /* TODO: Dealloc configuration */ 468 free(card_ratios); 469 free(card_offsets); 470 free(card_textures); 471 472 glfwTerminate(); 473 }