main.c (8987B)
1 #define GS_IMPL 2 #include <gs/gs.h> 3 4 // Vertex data 5 float v_data[] = { 6 -1.0f, -1.0f, 0.0f, 0.0f, // Top Left 7 1.0f, -1.0f, 1.0f, 0.0f, // Top Right 8 -1.0f, 1.0f, 0.0f, 1.0f, // Bottom Left 9 1.0f, 1.0f, 1.0f, 1.0f // Bottom Right 10 }; 11 // Index data for quad 12 uint32_t i_data[] = { 13 0, 3, 2, // First Triangle 14 0, 1, 3 // Second Triangle 15 }; 16 17 gs_command_buffer_t cb = {0}; 18 gs_handle(gs_graphics_vertex_buffer_t) vbo = {0}; 19 gs_handle(gs_graphics_index_buffer_t) ibo = {0}; 20 gs_handle(gs_graphics_pipeline_t) pip = {0}; 21 gs_handle(gs_graphics_shader_t) shader = {0}; 22 gs_handle(gs_graphics_uniform_t) u_resolution = {0}; 23 gs_handle(gs_graphics_uniform_t) u_food = {0}; 24 gs_handle(gs_graphics_uniform_t) u_map = {0}; 25 26 char map_names[32][32] = {0}; 27 uint32_t map_buffer[32] = {0}; 28 typedef union { 29 int32_t xy[2]; 30 struct { 31 int32_t x, y; 32 }; 33 } ivec; 34 35 ivec snake[32 * 32] = {0}; 36 uint32_t head = 3; 37 38 gs_vec2 food = {16.0f, 16.0f}; 39 40 ivec prev_move = {0}; 41 ivec move = {0}; 42 uint32_t prev_move_time = 0; 43 44 void new_game() 45 { 46 // reinitialise buffers 47 for (uint32_t i = 0; i < 32; i++) 48 map_buffer[i] = 0; 49 for (uint32_t i = 0; i < 32 * 32; i++) 50 snake[i] = (ivec){0}; 51 prev_move_time = gs_platform_elapsed_time() * 0.01f; 52 move = (ivec){1, 0}; 53 prev_move = move; 54 head = 3; 55 56 // place snake in map_buffer 57 for (uint32_t i = 0; i <= 3; i++) { 58 snake[i] = (ivec){16+i,16}; 59 map_buffer[snake[i].y] |= (1 << snake[i].x); 60 } 61 62 // place food at random position 63 do { 64 food = (gs_vec2){rand() % 32, rand() % 32}; 65 } while (map_buffer[(int)food.y] & (1 << (int)food.x)); 66 } 67 68 void move_snake() 69 { 70 prev_move = move; 71 ivec new_pos = {snake[head].x + move.x, snake[head].y + move.y}; 72 73 // wrap snake 74 if (new_pos.x > 31) new_pos.x = 0; 75 else if (new_pos.x < 0) new_pos.x = 31; 76 else if (new_pos.y > 31) new_pos.y = 0; 77 else if (new_pos.y < 0) new_pos.y = 31; 78 79 // if new pos is food 80 if (new_pos.x == (int)food.x && new_pos.y == (int)food.y) { 81 // add head to buffer 82 head++; 83 snake[head] = new_pos; 84 map_buffer[new_pos.y] |= (1 << new_pos.x); 85 86 // place food in a random place 87 do { 88 food = (gs_vec2){rand() % 32, rand() % 32}; 89 } while (map_buffer[(int)food.y] & (1 << (int)food.x)); 90 91 } else { 92 // remove tail from buffer 93 map_buffer[snake[0].y] &= ~(1 << snake[0].x); 94 95 // move each snake piece to the next ones position 96 for (uint32_t i = 0; i < head; i++) 97 snake[i] = snake[i+1]; 98 99 // check for collision 100 if (map_buffer[new_pos.y] & (1 << new_pos.x)) { 101 new_game(); 102 return; 103 } 104 // add head to buffer 105 snake[head] = new_pos; 106 map_buffer[snake[head].y] |= (1 << snake[head].x); 107 } 108 } 109 110 void init() 111 { 112 // Construct new command buffer 113 cb = gs_command_buffer_new(); 114 115 // Construct vertex buffer 116 vbo = gs_graphics_vertex_buffer_create(&(gs_graphics_vertex_buffer_desc_t) { 117 .data = v_data, 118 .size = sizeof(v_data) 119 }); 120 121 // Construct index buffer 122 ibo = gs_graphics_index_buffer_create(&(gs_graphics_index_buffer_desc_t) { 123 .data = i_data, 124 .size = sizeof(i_data) 125 }); 126 127 // shader sources 128 char* f_src = gs_platform_read_file_contents("./source/frag.glsl", "r", NULL); 129 char* v_src = gs_platform_read_file_contents("./source/vertex.glsl", "r", NULL); 130 131 // Create shader 132 shader = gs_graphics_shader_create (&(gs_graphics_shader_desc_t) { 133 .sources = (gs_graphics_shader_source_desc_t[]){ 134 {.type = GS_GRAPHICS_SHADER_STAGE_VERTEX, .source = v_src}, 135 {.type = GS_GRAPHICS_SHADER_STAGE_FRAGMENT, .source = f_src} 136 }, 137 .size = 2 * sizeof(gs_graphics_shader_source_desc_t), 138 .name = "quad" 139 }); 140 141 // Construct uniforms 142 143 // Construct layout fields for sub elements of array 144 gs_graphics_uniform_layout_desc_t layout[32] = {0}; 145 for (uint32_t i = 0; i < 32; ++i) { 146 gs_snprintf(map_names[i], sizeof(map_names[i]), "[%d]", i); 147 layout[i].type = GS_GRAPHICS_UNIFORM_INT; 148 layout[i].fname = map_names[i]; 149 } 150 u_map = gs_graphics_uniform_create(&(gs_graphics_uniform_desc_t) { 151 .name = "u_map", 152 .layout = layout, 153 .layout_size = 32 * sizeof(gs_graphics_uniform_layout_desc_t) 154 }); 155 u_resolution = gs_graphics_uniform_create(&(gs_graphics_uniform_desc_t) { 156 .name = "u_resolution", 157 .layout = &(gs_graphics_uniform_layout_desc_t) { 158 .type = GS_GRAPHICS_UNIFORM_VEC2 159 } 160 }); 161 u_food = gs_graphics_uniform_create(&(gs_graphics_uniform_desc_t) { 162 .name = "u_food", 163 .layout = &(gs_graphics_uniform_layout_desc_t) { 164 .type = GS_GRAPHICS_UNIFORM_VEC2 165 } 166 }); 167 168 // init pipeline 169 pip = gs_graphics_pipeline_create (&(gs_graphics_pipeline_desc_t) { 170 .raster = { 171 .shader = shader, 172 .index_buffer_element_size = sizeof(uint32_t) 173 }, 174 .layout = { 175 .attrs = (gs_graphics_vertex_attribute_desc_t[]){ 176 {.format = GS_GRAPHICS_VERTEX_ATTRIBUTE_FLOAT2, .name = "a_pos"}, 177 {.format = GS_GRAPHICS_VERTEX_ATTRIBUTE_FLOAT2, .name = "a_uv"} 178 }, 179 .size = 2 * sizeof(gs_graphics_vertex_attribute_desc_t) 180 } 181 }); 182 183 // set random seed 184 srand((uint32_t)time(NULL)); 185 186 // start game 187 new_game(); 188 189 // Bindings for all buffers: vertex, index, uniforms 190 gs_graphics_bind_desc_t binds = { 191 .vertex_buffers = {.desc = &(gs_graphics_bind_vertex_buffer_desc_t){.buffer = vbo}}, 192 .index_buffers = {.desc = &(gs_graphics_bind_index_buffer_desc_t){.buffer = ibo}} 193 }; 194 195 gs_graphics_begin_render_pass(&cb, GS_GRAPHICS_RENDER_PASS_DEFAULT); 196 gs_graphics_bind_pipeline(&cb, pip); 197 gs_graphics_apply_bindings(&cb, &binds); 198 } 199 200 void update() 201 { 202 // inputs 203 if (gs_platform_key_pressed(GS_KEYCODE_ESC)) gs_engine_quit(); 204 else if (gs_platform_key_pressed(GS_KEYCODE_UP) && prev_move.x != 0) move = (ivec){0,1}; 205 else if (gs_platform_key_pressed(GS_KEYCODE_DOWN) && prev_move.x != 0) move = (ivec){0,-1}; 206 else if (gs_platform_key_pressed(GS_KEYCODE_RIGHT) && prev_move.y != 0) move = (ivec){1,0}; 207 else if (gs_platform_key_pressed(GS_KEYCODE_LEFT) && prev_move.y != 0) move = (ivec){-1,0}; 208 209 // move snake every 0.2 seconds 210 const uint32_t time_now = gs_platform_elapsed_time() * 0.01f; 211 const bool has_moved = time_now - prev_move_time >= 2; 212 if (has_moved) { 213 move_snake(); 214 prev_move_time = time_now; 215 } 216 217 // set uniforms 218 gs_vec2 fbs = gs_platform_framebuffer_sizev(gs_platform_main_window()); 219 gs_graphics_bind_uniform_desc_t uniforms[] = { 220 (gs_graphics_bind_uniform_desc_t) {.uniform = u_map, .data = &map_buffer}, 221 (gs_graphics_bind_uniform_desc_t) {.uniform = u_resolution, .data = &fbs}, 222 (gs_graphics_bind_uniform_desc_t) {.uniform = u_food, .data = &food}, 223 }; 224 225 // Bindings for all buffers: vertex, index, uniforms 226 gs_graphics_bind_desc_t binds = { 227 .uniforms = {.desc = uniforms, .size = sizeof(uniforms)} 228 }; 229 230 /* Render */ 231 gs_graphics_set_viewport(&cb, 0, 0, (int32_t)fbs.x, (int32_t)fbs.y); 232 if (has_moved) 233 gs_graphics_apply_bindings(&cb, &binds); 234 gs_graphics_draw(&cb, &(gs_graphics_draw_desc_t){.start = 0, .count = 6}); 235 236 // Submit command buffer (syncs to GPU, MUST be done on main thread where you have your GPU context created) 237 gs_graphics_submit_command_buffer(&cb); 238 } 239 240 gs_app_desc_t gs_main(int32_t argc, char** argv) 241 { 242 return (gs_app_desc_t){ 243 .window_title = "Gunslinger Snake", 244 .init = init, 245 .update = update, 246 }; 247 }