#include "GL/glew.h" #include "GLFW/glfw3.h" #include "pwyazh.h" #include "pwyazh_GL.h" #include "common.h" #include static S32 global_width = 1024, global_height = 768; static Input global_input; void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { switch (action) { case GLFW_PRESS: { switch (key) { case GLFW_KEY_D: { global_input.move_right.state = KeyState_PRESS; } break; case GLFW_KEY_W: { global_input.move_forward.state = KeyState_PRESS; } break; case GLFW_KEY_A: { global_input.move_left.state = KeyState_PRESS; } break; case GLFW_KEY_S: { global_input.move_backward.state = KeyState_PRESS; } break; case GLFW_KEY_E: { global_input.move_up.state = KeyState_PRESS; } break; case GLFW_KEY_Q: { global_input.move_down.state = KeyState_PRESS; } break; case GLFW_KEY_SPACE: { global_input.jump.state = KeyState_PRESS; } break; case GLFW_KEY_RIGHT: { global_input.action_right.state = KeyState_PRESS; } break; case GLFW_KEY_LEFT: { global_input.action_left.state = KeyState_PRESS; } break; case GLFW_KEY_ESCAPE: { global_input.exit.state = KeyState_PRESS; } break; } } break; case GLFW_RELEASE: { switch (key) { case GLFW_KEY_D: { global_input.move_right.state = KeyState_RELEASE; } break; case GLFW_KEY_W: { global_input.move_forward.state = KeyState_RELEASE; } break; case GLFW_KEY_A: { global_input.move_left.state = KeyState_RELEASE; } break; case GLFW_KEY_S: { global_input.move_backward.state = KeyState_RELEASE; } break; case GLFW_KEY_E: { global_input.move_up.state = KeyState_RELEASE; } break; case GLFW_KEY_Q: { global_input.move_down.state = KeyState_RELEASE; } break; case GLFW_KEY_SPACE: { global_input.jump.state = KeyState_RELEASE; } break; case GLFW_KEY_RIGHT: { global_input.action_right.state = KeyState_RELEASE; } break; case GLFW_KEY_LEFT: { global_input.action_left.state = KeyState_RELEASE; } break; case GLFW_KEY_ESCAPE: { global_input.exit.state = KeyState_RELEASE; } break; } } break; } } void window_resize_callback(GLFWwindow* window, int width, int height) { global_width = width; global_height = height; glViewport(0, 0, global_width, global_height); } int main(void) { GLFWwindow *window; Arena *arena = 0; if (glfwInit() == GLFW_FALSE) { fprintf(stderr, "[ERROR] Failed to initialize glfw.\n"); return(1); } /* glfwWindowHint(GLFW_RESIZABLE, 0); */ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); window = glfwCreateWindow(global_width, global_height, "Instancing", 0, 0); if (!window) { fprintf(stderr, "[ERROR] Failed to create window.\n"); glfwTerminate(); return(1); } glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); glfwSetWindowSizeCallback(window, window_resize_callback); if (glewInit() != GLEW_OK) { fprintf(stderr, "[ERROR] Failed to initialize glew.\n"); glfwTerminate(); return(1); } glEnable(GL_DEPTH_TEST); U32 instancing_uniform_array_shader = create_shader_program("shaders/instancing_uniform_array.vs", "shaders/instancing_uniform_array.fs"); U32 instanced_arrays_shader = create_shader_program("shaders/instanced_arrays.vs", "shaders/instanced_arrays.fs"); F32 quad_vertices[] = { -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, 0.05f, -0.05f, 0.0f, 1.0f, 0.0f, -0.05f, -0.05f, 0.0f, 0.0f, 1.0f, -0.05f, 0.05f, 1.0f, 0.0f, 0.0f, 0.05f, -0.05f, 0.0f, 1.0f, 0.0f, 0.05f, 0.05f, 0.0f, 1.0f, 1.0f, }; GLuint quad_vao, vbo; glGenVertexArrays(1, &quad_vao); glBindVertexArray(quad_vao); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5*sizeof(F32), (void *)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5*sizeof(F32), (void *)(2*sizeof(F32))); glBindVertexArray(0); V2F translations[100]; S32 index = 0; F32 offset = 0.1f; for (S32 y = -10; y < 10; y += 2) { for (S32 x = -10; x < 10; x += 2) { V2F translation = v2f((F32)x, (F32)y); translation = v2f_scalef(translation, 1.0f/10.0f); translation = v2f_add(translation, v2f(offset, offset)); translations[index++] = translation; } } U32 instance_vbo; glBindVertexArray(quad_vao); glGenBuffers(1, &instance_vbo); glBindBuffer(GL_ARRAY_BUFFER, instance_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(translations), translations, GL_STATIC_DRAW); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(V2F), (void *)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexAttribDivisor(2, 1); glBindVertexArray(0); arena = arena_alloc(Megabytes(64)); U32 default_shader = create_shader_program("shaders/default.vs", "shaders/default.fs"); U32 instanced_shader = create_shader_program("shaders/instanced_mat4.vs", "shaders/instanced_mat4.fs"); Mesh *planet_mesh = mesh_load_obj(arena, "../../data/models/planet/planet.obj"); U32 planet_texture = load_texture("../../data/models/planet/mars.png"); Mesh *rock_mesh = mesh_load_obj(arena, "../../data/models/rock/rock.obj"); U32 rock_texture = load_texture("../../data/models/rock/rock.png"); S32 amount = 80000; F32 radius = 150.0f; offset = 25.0f; MAT4 trns[amount]; for (S32 i = 0; i < amount; ++i) { F32 angle, displacement, x, y, z, scale; MAT4 model = mat4_identity(); angle = rand()%360; model = mat4_rotate_angles(model, v3f(angle, angle, angle)); scale = (rand()%20)/100.0f+0.05f; model = mat4_scale(model, v3f(scale, scale, scale)); angle = ((F32)i/(F32)amount)*360.0f; displacement = (rand()%(S32)(2.0f*offset))-offset; x = f32_sin(DEG2RAD*angle)*radius+displacement; displacement = (rand()%(S32)(2.0f*offset))-offset; y = 0.4f*displacement; displacement = (rand()%(S32)(2.0f*offset))-offset; z = f32_cos(DEG2RAD*angle)*radius; model = mat4_translate(model, v3f(x, y, z)); trns[i] = model; } U32 buffer; glGenBuffers(1, &buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, amount*sizeof(MAT4), &trns[0], GL_STATIC_DRAW); glBindVertexArray(rock_mesh->vao); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4*sizeof(V4F), (void *)0); glEnableVertexAttribArray(4); glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4*sizeof(V4F), (void *)(1*sizeof(V4F))); glEnableVertexAttribArray(5); glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4*sizeof(V4F), (void *)(2*sizeof(V4F))); glEnableVertexAttribArray(6); glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4*sizeof(V4F), (void *)(3*sizeof(V4F))); glVertexAttribDivisor(3, 1); glVertexAttribDivisor(4, 1); glVertexAttribDivisor(5, 1); glVertexAttribDivisor(6, 1); glBindVertexArray(0); F32 target_fps = 60.0f; F32 target_spf = 1.0f/target_fps; F32 last_time = glfwGetTime(); F32 time = last_time; F32 dt; S32 scene = 0; S32 number_scene = 4; MAT4 projection, view, model; V3F camera_pos = v3f(0.0f, 50.0f, 220.0f); F32 camera_speed = 10.0f; F32 fovx = 90.0f; F32 near = 0.1f; F32 far = 1000.0f; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); /* NOTE(pryazha): Update */ if (key_is_pressed(global_input.exit)) { glfwSetWindowShouldClose(window, GLFW_TRUE); } if (key_first_press(global_input.jump) || key_first_press(global_input.action_right)) { scene++; } else if (key_first_press(global_input.action_left)) { scene--; if (scene < 0) scene = number_scene-1; } scene %= number_scene; /* NOTE(pryazha): Render */ glClearColor(0.15f, 0.15f, 0.15f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); switch (scene) { case 0: { glUseProgram(instancing_uniform_array_shader); for (S32 translation_index = 0; translation_index < 100; ++translation_index) { char temp[256]; snprintf(temp, 256, "offsets[%d]", translation_index); shader_set_2fv(instancing_uniform_array_shader, temp, translations[translation_index]); } glBindVertexArray(quad_vao); glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); glBindVertexArray(0); } break; case 1: { glUseProgram(instanced_arrays_shader); glBindVertexArray(quad_vao); glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); glBindVertexArray(0); } break; case 2: { V3F camera_dv = get_dv_camera_orbital(&global_input, camera_pos, v3f_zero(), dt, camera_speed); camera_pos = v3f_add(camera_pos, camera_dv); projection = perspective(fovx, (F32)global_width/(F32)global_height, near, far); V3F world_up = v3f(0.0f, 1.0f, 0.0f); view = look_at(camera_pos, v3f_zero(), world_up); glUseProgram(default_shader); shader_set_mat4fv(default_shader, "projection", projection); shader_set_mat4fv(default_shader, "view", view); F32 scale = 5.0f; model = mat4_make_scale(v3f(scale, scale, scale)); shader_set_mat4fv(default_shader, "model", model); glBindTexture(GL_TEXTURE_2D, planet_texture); mesh_draw(planet_mesh); glBindTexture(GL_TEXTURE_2D, rock_texture); for (S32 i = 0; i < amount; ++i) { model = trns[i]; shader_set_mat4fv(default_shader, "model", model); mesh_draw(rock_mesh); } } break; case 3: { V3F camera_dv = get_dv_camera_orbital(&global_input, camera_pos, v3f_zero(), dt, camera_speed); camera_pos = v3f_add(camera_pos, camera_dv); projection = perspective(fovx, (F32)global_width/(F32)global_height, near, far); V3F world_up = v3f(0.0f, 1.0f, 0.0f); view = look_at(camera_pos, v3f_zero(), world_up); glUseProgram(default_shader); shader_set_mat4fv(default_shader, "projection", projection); shader_set_mat4fv(default_shader, "view", view); F32 scale = 5.0f; model = mat4_make_scale(v3f(scale, scale, scale)); shader_set_mat4fv(default_shader, "model", model); glBindTexture(GL_TEXTURE_2D, planet_texture); mesh_draw(planet_mesh); glBindTexture(GL_TEXTURE_2D, 0); glUseProgram(instanced_shader); shader_set_mat4fv(default_shader, "projection", projection); shader_set_mat4fv(default_shader, "view", view); glBindTexture(GL_TEXTURE_2D, rock_texture); glBindVertexArray(rock_mesh->vao); glDrawElementsInstanced(GL_TRIANGLES, rock_mesh->index_count, GL_UNSIGNED_INT, 0, amount); glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } break; } input_update_last_state(&global_input); glfwSwapBuffers(window); F32 elapsed = glfwGetTime()-last_time; if (elapsed < target_spf) { U32 sleep_time = (U32)(target_spf-elapsed); if (sleep_time > 0) sleep(sleep_time); } F32 current_time = glfwGetTime(); dt = current_time-last_time; time += dt; last_time = current_time; } glfwTerminate(); return(0); }