diff --git a/assets/player.png b/assets/player.png new file mode 100644 index 0000000..88b57b2 Binary files /dev/null and b/assets/player.png differ diff --git a/shaders/batch_quad_frag.glsl b/shaders/batch_quad_frag.glsl new file mode 100644 index 0000000..22bbd57 --- /dev/null +++ b/shaders/batch_quad_frag.glsl @@ -0,0 +1,13 @@ +#version 330 core +out vec4 o_color; + +in vec4 v_color; +in vec2 v_uvs; + +uniform sampler2D texture_slot; + +void main() { + //o_color = vec4(1, 0, 0, 1); + o_color = texture(texture_slot, v_uvs) * v_color; +} + diff --git a/shaders/batch_quad_vert.glsl b/shaders/batch_quad_vert.glsl new file mode 100644 index 0000000..9a01ae0 --- /dev/null +++ b/shaders/batch_quad_vert.glsl @@ -0,0 +1,15 @@ +#version 330 core +layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec2 a_uvs; +layout (location = 2) in vec4 a_color; + +out vec4 v_color; +out vec2 v_uvs; + +uniform mat4 projection; + +void main() { + v_color = a_color; + v_uvs = a_uvs; + gl_Position = projection * vec4(a_pos, 0.0, 1.0); +} diff --git a/src/engine/entity/entity.c b/src/engine/entity/entity.c index 10aa21b..f290d77 100644 --- a/src/engine/entity/entity.c +++ b/src/engine/entity/entity.c @@ -8,7 +8,8 @@ void entity_init(void) { } usize entity_create(vec2 position, vec2 size, vec2 velocity, u8 collision_layer, u8 collision_mask, On_Hit on_hit, On_Hit_Static on_hit_static) { - vec2_scale(size, size, 0.5); + // Mistake! + //vec2_scale(size, size, 0.5); Entity entity = { .body_id = physics_body_create(position, size, velocity, collision_layer, collision_mask, on_hit, on_hit_static), diff --git a/src/engine/global.h b/src/engine/global.h index 6606f45..33dffce 100644 --- a/src/engine/global.h +++ b/src/engine/global.h @@ -6,7 +6,6 @@ #include "time.h" typedef struct global { - Render_State render; Config_State config; Input_State input; Time_State time; diff --git a/src/engine/physics/physics.c b/src/engine/physics/physics.c index e99cb16..61c2ddf 100644 --- a/src/engine/physics/physics.c +++ b/src/engine/physics/physics.c @@ -110,8 +110,8 @@ void physics_init(void) { state.body_list = array_list_create(sizeof(Body), 0); state.static_body_list = array_list_create(sizeof(Static_Body), 0); - state.gravity = -200; - state.terminal_velocity = -10000; + state.gravity = -150; + state.terminal_velocity = -7000; tick_rate = 1.f / iterations; } diff --git a/src/engine/render.h b/src/engine/render.h index 50fecf7..86237ed 100644 --- a/src/engine/render.h +++ b/src/engine/render.h @@ -4,18 +4,36 @@ #include #include "types.h" +#include "array_list.h" -typedef struct render_state { - SDL_Window *window; +typedef struct sprite_sheet { f32 width; f32 height; -} Render_State; + f32 cell_width; + f32 cell_height; + u32 texture_id; +} Sprite_Sheet; -void render_init(void); +typedef struct batch_vertex { + vec2 position; + vec2 uvs; + vec4 color; +} Batch_Vertex; + +enum { + MAX_BATCH_QUADS = 10000, + MAX_BATCH_VERTICES = MAX_BATCH_QUADS * 4, + MAX_BATCH_ELEMENTS = MAX_BATCH_QUADS * 6, +}; + +SDL_Window *render_init(void); void render_begin(void); -void render_end(void); +void render_end(SDL_Window *window); void render_quad(vec2 pos, vec2 size, vec4 color); void render_quad_line(vec2 pos, vec2 size, vec4 color); void render_line_segment(vec2 start, vec2 end, vec4 color); void render_aabb(f32 *aabb, vec4 color); - +void render_sprite_sheet_init(Sprite_Sheet *sprite_sheet, const char *path, f32 cell_width, f32 cell_height); +void render_sprite_sheet_frame(Sprite_Sheet *sprite_sheet, f32 row, f32 column, vec2 position); +void render_set_batch_texture(u32 texture_id); +f32 render_get_scale(); diff --git a/src/engine/render/render.c b/src/engine/render/render.c index bedfa34..065933c 100644 --- a/src/engine/render/render.c +++ b/src/engine/render/render.c @@ -3,34 +3,84 @@ #include "../global.h" #include "../render.h" #include "render_internal.h" +#include "../util.h" + +#define STB_IMAGE_IMPLEMENTATION +#include + +static f32 window_width = 1920; +static f32 window_height = 1080; +static f32 render_width = 640; +static f32 render_height = 360; +static f32 scale = 3; + +static u32 vao_quad; +static u32 vbo_quad; +static u32 ebo_quad; +static u32 vao_line; +static u32 vbo_line; +static u32 shader_default; +static u32 texture_color; +static u32 vao_batch; +static u32 vbo_batch; +static u32 ebo_batch; +static u32 shader_batch; +static u32 texture_batch; +static Array_List *list_batch; + +SDL_Window *render_init(void) { + /// RENDER WIDTH AND VIEWPORT WIDTH SHOULD BE DIFFERENT - PIXEL ART + SDL_Window *window = render_init_window(window_width, window_height); + + render_init_quad(&vao_quad, &vbo_quad, &ebo_quad); + render_init_line(&vao_line, &vbo_line); + render_init_shaders(&shader_default, &shader_batch, render_width, render_height); + render_init_color_texture(&texture_color); + render_init_batch_quads(&vao_batch, &vbo_batch, &ebo_batch); -static Render_State_Internal state = {0}; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -void render_init(void) { - global.render.width = 1920; - global.render.height = 1080; - global.render.window = render_init_window(global.render.width, global.render.height); + list_batch = array_list_create(sizeof(Batch_Vertex), 8); - render_init_quad(&state.vao_quad, &state.vbo_quad, &state.ebo_quad); - render_init_line(&state.vao_line, &state.vbo_line); - render_init_shaders(&state); - render_init_color_texture(&state.texture_color); + stbi_set_flip_vertically_on_load(1); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + return window; } void render_begin(void) { glClearColor(0.08, 0.1, 0.1, 1); glClear(GL_COLOR_BUFFER_BIT); + + list_batch->len = 0; +} + +static void render_batch(Batch_Vertex *vertices, usize count, u32 texture_id) { + glBindBuffer(GL_ARRAY_BUFFER, vbo_batch); + glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(Batch_Vertex), vertices); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_id); + + glUseProgram(shader_batch); + glBindVertexArray(vao_batch); + + // count >> 2 s the same as count / 4. + // 4 Vertices per Quad. + // 6 Indices per Quad (two triangles). + glDrawElements(GL_TRIANGLES, (count >> 2) * 6, GL_UNSIGNED_INT, NULL); } -void render_end(void) { - SDL_GL_SwapWindow(global.render.window); +void render_end(SDL_Window *window) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINES); + render_batch(list_batch->items, list_batch->len, texture_batch); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + SDL_GL_SwapWindow(window); } void render_quad(vec2 pos, vec2 size, vec4 color) { - glUseProgram(state.shader_default); + glUseProgram(shader_default); mat4x4 model; mat4x4_identity(model); @@ -38,19 +88,19 @@ void render_quad(vec2 pos, vec2 size, vec4 color) { mat4x4_translate(model, pos[0], pos[1], 0); mat4x4_scale_aniso(model, model, size[0], size[1], 1); - glUniformMatrix4fv(glGetUniformLocation(state.shader_default, "model"), 1, GL_FALSE, &model[0][0]); - glUniform4fv(glad_glGetUniformLocation(state.shader_default, "color"), 1, color); + glUniformMatrix4fv(glGetUniformLocation(shader_default, "model"), 1, GL_FALSE, &model[0][0]); + glUniform4fv(glad_glGetUniformLocation(shader_default, "color"), 1, color); - glBindVertexArray(state.vao_quad); + glBindVertexArray(vao_quad); - glBindTexture(GL_TEXTURE_2D, state.texture_color); + glBindTexture(GL_TEXTURE_2D, texture_color); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL); glBindVertexArray(0); } void render_line_segment(vec2 start, vec2 end, vec4 color) { - glUseProgram(state.shader_default); + glUseProgram(shader_default); glLineWidth(3); f32 x = end[0] - start[0]; @@ -60,13 +110,13 @@ void render_line_segment(vec2 start, vec2 end, vec4 color) { mat4x4 model; mat4x4_translate(model, start[0], start[1], 0); - glUniformMatrix4fv(glGetUniformLocation(state.shader_default, "model"), 1, GL_FALSE, &model[0][0]); - glUniform4fv(glGetUniformLocation(state.shader_default, "color"), 1, color); + glUniformMatrix4fv(glGetUniformLocation(shader_default, "model"), 1, GL_FALSE, &model[0][0]); + glUniform4fv(glGetUniformLocation(shader_default, "color"), 1, color); - glBindTexture(GL_TEXTURE_2D, state.texture_color); - glBindVertexArray(state.vao_line); + glBindTexture(GL_TEXTURE_2D, texture_color); + glBindVertexArray(vao_line); - glBindBuffer(GL_ARRAY_BUFFER, state.vbo_line); + glBindBuffer(GL_ARRAY_BUFFER, vbo_line); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(line), line); glDrawArrays(GL_LINES, 0, 2); @@ -93,3 +143,87 @@ void render_aabb(f32 *aabb, vec4 color) { render_quad_line(&aabb[0], size, color); } +static void append_quad(vec2 position, vec2 size, vec4 texture_coordinates, vec4 color) { + vec4 uvs = {0, 0, 1, 1}; + + if (texture_coordinates != NULL) { + memcpy(uvs, texture_coordinates, sizeof(vec4)); + } + + array_list_append(list_batch, &(Batch_Vertex){ + .position = { position[0], position[1] }, + .uvs = { uvs[0], uvs[1] }, + .color = { color[0], color[1], color[2], color[3] }, + }); + + array_list_append(list_batch, &(Batch_Vertex){ + .position = { position[0] + size[0], position[1] }, + .uvs = { uvs[2], uvs[1] }, + .color = { color[0], color[1], color[2], color[3] }, + }); + + array_list_append(list_batch, &(Batch_Vertex){ + .position = { position[0] + size[0], position[1] + size[1] }, + .uvs = { uvs[2], uvs[3] }, + .color = { color[0], color[1], color[2], color[3] }, + }); + + array_list_append(list_batch, &(Batch_Vertex){ + .position = { position[0], position[1] + size[1] }, + .uvs = { uvs[0], uvs[3] }, + .color = { color[0], color[1], color[2], color[3] }, + }); +} + +static void calculate_sprite_texture_coordinates(vec4 result, f32 row, f32 column, f32 texture_width, f32 texture_height, f32 cell_width, f32 cell_height) { + f32 w = 1.0 / (texture_width / cell_width); + f32 h = 1.0 / (texture_height / cell_height); + f32 x = column * w; + f32 y = row * h; + result[0] = x; + result[1] = y; + result[2] = x + w; + result[3] = y + h; +} + +void render_sprite_sheet_init(Sprite_Sheet *sprite_sheet, const char *path, f32 cell_width, f32 cell_height) { + glGenTextures(1, &sprite_sheet->texture_id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, sprite_sheet->texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + int width, height, channel_count; + u8 *image_data = stbi_load(path, &width, &height, &channel_count, 0); + if (!image_data) { + ERROR_EXIT("Failed to load image: %s\n", path); + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); + stbi_image_free(image_data); + + sprite_sheet->width = (f32)width; + sprite_sheet->height = (f32)height; + sprite_sheet->cell_width = cell_width; + sprite_sheet->cell_height = cell_height; +} + +void render_sprite_sheet_frame(Sprite_Sheet *sprite_sheet, f32 row, f32 column, vec2 position) { + vec4 uvs; + calculate_sprite_texture_coordinates(uvs, row, column, sprite_sheet->width, sprite_sheet->height, sprite_sheet->cell_width, sprite_sheet->cell_height); + + vec2 size = {sprite_sheet->cell_width, sprite_sheet->cell_height}; + vec2 bottom_left = {position[0] - size[0] * 0.5, position[1] - size[1] * 0.5}; + append_quad(bottom_left, size, uvs, (vec4){1, 1, 1, 1}); +} + +f32 render_get_scale() { + return scale; +} + +// NOTE: Temporary setter - will use texture slots later. +void render_set_batch_texture(u32 texture_id) { + texture_batch = texture_id; +} diff --git a/src/engine/render/render_init.c b/src/engine/render/render_init.c index 22b3621..6036342 100644 --- a/src/engine/render/render_init.c +++ b/src/engine/render/render_init.c @@ -20,8 +20,8 @@ SDL_Window *render_init_window(u32 width, u32 height) { "MyGame", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - global.render.width, - global.render.height, + width, + height, SDL_WINDOW_OPENGL ); @@ -42,17 +42,28 @@ SDL_Window *render_init_window(u32 width, u32 height) { return window; } -void render_init_shaders(Render_State_Internal *state) { - state->shader_default = render_shader_create("./shaders/default.vert", "./shaders/default.frag"); +void render_init_shaders(u32 *shader_default, u32 *shader_batch, f32 render_width, f32 render_height) { + *shader_default = render_shader_create("./shaders/default.vert", "./shaders/default.frag"); - mat4x4_ortho(state->projection, 0, global.render.width, 0, global.render.height, -2, 2); + mat4x4 projection; + mat4x4_ortho(projection, 0, render_width, 0, render_height, -2, 2); - glUseProgram(state->shader_default); + glUseProgram(*shader_default); glUniformMatrix4fv( - glGetUniformLocation(state->shader_default, "projection"), + glGetUniformLocation(*shader_default, "projection"), 1, GL_FALSE, - &state->projection[0][0] + &projection[0][0] + ); + + *shader_batch = render_shader_create("shaders/batch_quad_vert.glsl", "shaders/batch_quad_frag.glsl"); + + glUseProgram(*shader_batch); + glUniformMatrix4fv( + glGetUniformLocation(*shader_batch, "projection"), + 1, + GL_FALSE, + &projection[0][0] ); } @@ -103,6 +114,41 @@ void render_init_quad(u32 *vao, u32 *vbo, u32 *ebo) { glBindVertexArray(0); } +void render_init_batch_quads(u32 *vao, u32 *vbo, u32 *ebo) { + glGenVertexArrays(1, vao); + glBindVertexArray(*vao); + + u32 indices[MAX_BATCH_ELEMENTS]; + for (u32 i = 0, offset = 0; i < MAX_BATCH_ELEMENTS; i += 6, offset += 4) { + indices[i + 0] = offset + 0; + indices[i + 1] = offset + 1; + indices[i + 2] = offset + 2; + indices[i + 3] = offset + 2; + indices[i + 4] = offset + 3; + indices[i + 5] = offset + 0; + } + + glGenBuffers(1, vbo); + glBindBuffer(GL_ARRAY_BUFFER, *vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(f32) * MAX_BATCH_VERTICES * 4, NULL, GL_DYNAMIC_DRAW); + + // [x, y], [u, v], [r, g, b, a] + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Batch_Vertex), (void*)offsetof(Batch_Vertex, position)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Batch_Vertex), (void*)offsetof(Batch_Vertex, uvs)); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Batch_Vertex), (void*)offsetof(Batch_Vertex, color)); + + glGenBuffers(1, ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + void render_init_line(u32 *vao, u32 *vbo) { glGenVertexArrays(1, vao); glBindVertexArray(*vao); diff --git a/src/engine/render/render_internal.h b/src/engine/render/render_internal.h index dca89b2..e76e9a8 100644 --- a/src/engine/render/render_internal.h +++ b/src/engine/render/render_internal.h @@ -5,21 +5,11 @@ #include "../types.h" #include "../render.h" -typedef struct render_state_internal { - u32 vao_quad; - u32 vbo_quad; - u32 ebo_quad; - u32 vao_line; - u32 vbo_line; - u32 shader_default; - u32 texture_color; - mat4x4 projection; -} Render_State_Internal; - SDL_Window *render_init_window(u32 width, u32 height); void render_init_quad(u32 *vao, u32 *vbo, u32 *ebo); void render_init_color_texture(u32 *texture); -void render_init_shaders(Render_State_Internal *state); +void render_init_shaders(u32 *shader_default, u32 *shader_batch, f32 render_width, f32 render_height); void render_init_line(u32 *vao, u32 *vbo); +void render_init_batch_quads(u32 *vao, u32 *ebo, u32 *vbo); u32 render_shader_create(const char *path_vert, const char *path_frag); diff --git a/src/main.c b/src/main.c index d20ecac..77322b9 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,7 @@ #include "engine/physics.h" #include "engine/util.h" #include "engine/entity.h" +#include "engine/array_list.h" typedef enum collision_layer { COLLISION_LAYER_PLAYER = 1, @@ -32,20 +33,16 @@ static void input_handle(Body *body_player) { f32 vely = body_player->velocity[1]; if (global.input.right) { - velx += 1000; + velx += 500; } if (global.input.left) { - velx -= 1000; + velx -= 500; } if (global.input.up && player_is_grounded) { player_is_grounded = false; - vely = 4000; - } - - if (global.input.down) { - vely -= 800; + vely = 2000; } body_player->velocity[0] = velx; @@ -67,18 +64,18 @@ void player_on_hit_static(Body *self, Static_Body *other, Hit hit) { void enemy_on_hit_static(Body *self, Static_Body *other, Hit hit) { if (hit.normal[0] > 0) { - self->velocity[0] = 700; + self->velocity[0] = 300; } if (hit.normal[0] < 0) { - self->velocity[0] = -700; + self->velocity[0] = -300; } } int main(int argc, char *argv[]) { time_init(60); config_init(); - render_init(); + SDL_Window *window = render_init(); physics_init(); entity_init(); @@ -87,19 +84,25 @@ int main(int argc, char *argv[]) { u8 enemy_mask = COLLISION_LAYER_PLAYER | COLLISION_LAYER_TERRAIN; u8 player_mask = COLLISION_LAYER_ENEMY | COLLISION_LAYER_TERRAIN; - usize player_id = entity_create((vec2){100, 800}, (vec2){100, 100}, (vec2){0, 0}, COLLISION_LAYER_PLAYER, player_mask, player_on_hit, player_on_hit_static); + usize player_id = entity_create((vec2){100, 200}, (vec2){24, 24}, (vec2){0, 0}, COLLISION_LAYER_PLAYER, player_mask, player_on_hit, player_on_hit_static); - f32 width = global.render.width; - f32 height = global.render.height; + i32 width, height; + SDL_GetWindowSize(window, &width, &height); + width /= render_get_scale(); + height /= render_get_scale(); - u32 static_body_a_id = physics_static_body_create((vec2){width * 0.5 - 25, height - 25}, (vec2){width - 50, 50}, COLLISION_LAYER_TERRAIN); - u32 static_body_b_id = physics_static_body_create((vec2){width - 25, height * 0.5 + 25}, (vec2){50, height - 50}, COLLISION_LAYER_TERRAIN); - u32 static_body_c_id = physics_static_body_create((vec2){width * 0.5 + 25, 25}, (vec2){width - 50, 50}, COLLISION_LAYER_TERRAIN); - u32 static_body_d_id = physics_static_body_create((vec2){25, height * 0.5 - 25}, (vec2){50, height - 50}, COLLISION_LAYER_TERRAIN); - u32 static_body_e_id = physics_static_body_create((vec2){width * 0.5, height * 0.5}, (vec2){150, 150}, COLLISION_LAYER_TERRAIN); + u32 static_body_a_id = physics_static_body_create((vec2){width * 0.5 - 12.5, height - 12.5}, (vec2){width - 25, 25}, COLLISION_LAYER_TERRAIN); + u32 static_body_b_id = physics_static_body_create((vec2){width - 12.5, height * 0.5 + 12.5}, (vec2){25, height - 25}, COLLISION_LAYER_TERRAIN); + u32 static_body_c_id = physics_static_body_create((vec2){width * 0.5 + 12.5, 12.5}, (vec2){width - 25, 25}, COLLISION_LAYER_TERRAIN); + u32 static_body_d_id = physics_static_body_create((vec2){12.5, height * 0.5 - 12.5}, (vec2){25, height - 25}, COLLISION_LAYER_TERRAIN); + u32 static_body_e_id = physics_static_body_create((vec2){width * 0.5, height * 0.5}, (vec2){25, 25}, COLLISION_LAYER_TERRAIN); - usize entity_a_id = entity_create((vec2){600, 600}, (vec2){50, 50}, (vec2){900, 0}, COLLISION_LAYER_ENEMY, enemy_mask, NULL, enemy_on_hit_static); - usize entity_b_id = entity_create((vec2){800, 800}, (vec2){50, 50}, (vec2){900, 0}, 0, enemy_mask, NULL, enemy_on_hit_static); + usize entity_a_id = entity_create((vec2){300, 200}, (vec2){25, 25}, (vec2){900, 0}, COLLISION_LAYER_ENEMY, enemy_mask, NULL, enemy_on_hit_static); + usize entity_b_id = entity_create((vec2){400, 200}, (vec2){25, 25}, (vec2){900, 0}, 0, enemy_mask, NULL, enemy_on_hit_static); + + Sprite_Sheet sprite_sheet_player; + render_sprite_sheet_init(&sprite_sheet_player, "assets/player.png", 24, 24); + render_set_batch_texture(sprite_sheet_player.texture_id); while (!should_quit) { time_update(); @@ -140,7 +143,12 @@ int main(int argc, char *argv[]) { render_aabb((f32*)physics_body_get(entity_get(entity_a_id)->body_id), WHITE); render_aabb((f32*)physics_body_get(entity_get(entity_b_id)->body_id), WHITE); - render_end(); + render_sprite_sheet_frame(&sprite_sheet_player, 1, 1, physics_body_get(entity_get(entity_a_id)->body_id)->aabb.position); + render_sprite_sheet_frame(&sprite_sheet_player, 1, 2, (vec2){100, 100}); + render_sprite_sheet_frame(&sprite_sheet_player, 1, 4, (vec2){100, 200}); + render_sprite_sheet_frame(&sprite_sheet_player, 0, 0, body_player->aabb.position); + + render_end(window); player_color[0] = 0; player_color[2] = 1;