#include "../include/RenderBackend/OpenGL/opengl.hpp" #include "../include/RenderBackend/OpenGL/Shader.hpp" #include "../include/RenderBackend/OpenGL/ShaderProgram.hpp" #include "../include/RenderBackend/OpenGL/DeviceMemory.hpp" #include "../include/RenderBackend/OpenGL/Texture2D.hpp" #include "../include/Image.hpp" #include "../include/Triangle.hpp" #include "../include/ThirdPersonCamera.hpp" #include "../include/model_loader/ObjModelLoader.hpp" #include "../include/ModelLoader.hpp" #include #include // TODO: Creating buffers etc should be somehow created/drawn using the window object, since they are associated with the window // opengl context using namespace amalgine; using namespace std; static void glfwErrorHandler(int errorCode, const char *errorDescription); static void initGlfw(); static void initGlew(); static GLFWwindow *createWindow(); static std::unique_ptr load_shader_from_file(const char *filepath, Shader::Type shader_type); static std::unique_ptr build_shader_program_from_shaders(const std::vector &shaders); struct Userdata { ThirdPersonCamera *third_person_camera; double prev_cursor_x; double prev_cursor_y; }; static std::vector generate_sand() { const int rows = 50; const int columns = 50; std::vector vertices(rows * columns * 2); int i = 0; float div = 1.0f / (float)rows; for(int y = 0; y < rows; ++y) { for(int x = 0; x < columns; ++x) { vertices[i++] = { Vertex3D{x * div + 0.0f * div, y * div + 0.0f * div, 0.0f}, Vertex3D{x * div + 1.0f * div, y * div + 0.0f * div, 0.0f}, Vertex3D{x * div + 1.0f * div, y * div + 1.0f * div, 0.0f} }; vertices[i++] = { Vertex3D{x * div + 1.0f * div, y * div + 1.0f * div, 0.0f}, Vertex3D{x * div + 0.0f * div, y * div + 1.0f * div, 0.0f}, Vertex3D{x * div + 0.0f * div, y * div + 0.0f * div, 0.0f} }; } } return vertices; } struct Model { ShaderFrame frame; Texture2D texture; }; static void create_sand(DeviceMemory *triangles, DeviceMemory *texcoords, Texture2D *texture); static Model load_model(const char *filepath, ShaderProgram *shader_program); int main() { initGlfw(); glfwSetErrorCallback(glfwErrorHandler); GLFWwindow *window = createWindow(); int window_width = 1; int window_height = 1; glfwGetWindowSize(window, &window_width, &window_height); glm::mat4 proj = glm::perspective(glm::radians(75.0f), (float)window_width / (float)window_height, 0.01f, 1000.0f); std::unique_ptr sand_vertex_shader = load_shader_from_file("shaders/sand_vertex.vert", Shader::Type::VERTEX); std::unique_ptr sand_pixel_shader = load_shader_from_file("shaders/sand_fragment.frag", Shader::Type::PIXEL); std::unique_ptr sand_shader_program = build_shader_program_from_shaders({ sand_vertex_shader.get(), sand_pixel_shader.get() }); ShaderFrame sand_frame = sand_shader_program->create_frame(); DeviceMemory *sand_triangles = sand_frame.get_input_by_name("position"); DeviceMemory *sand_texcoords = sand_frame.get_input_by_name("texcoord_vert"); Texture2D sand_texture; create_sand(sand_triangles, sand_texcoords, &sand_texture); Result sand_proj_uniform = sand_frame.get_uniform_by_name("proj"); Result sand_view_uniform = sand_frame.get_uniform_by_name("view"); Result sand_model_uniform = sand_frame.get_uniform_by_name("model"); //Result sand_time = sand_frame.get_uniform_by_name("time"); Result sand_heightmap_uniform = sand_frame.get_uniform_by_name("tex"); sand_proj_uniform->set(proj); sand_heightmap_uniform->set(sand_texture); std::unique_ptr vertex_shader = load_shader_from_file("shaders/vertex.vert", Shader::Type::VERTEX); std::unique_ptr pixel_shader = load_shader_from_file("shaders/fragment.frag", Shader::Type::PIXEL); std::unique_ptr shader_program = build_shader_program_from_shaders({ vertex_shader.get(), pixel_shader.get() }); Model buddha_model = load_model("/home/dec05eba/Downloads/Zora Armor/link.a3d", shader_program.get()); buddha_model.frame.get_uniform_by_name("proj")->set(proj); Result view_uniform = buddha_model.frame.get_uniform_by_name("view"); Result model_uniform = buddha_model.frame.get_uniform_by_name("model"); std::unique_ptr vertex_no_texture_shader = load_shader_from_file("shaders/vertex_no_texture.vert", Shader::Type::VERTEX); std::unique_ptr pixel_no_texture_shader = load_shader_from_file("shaders/fragment_no_texture.frag", Shader::Type::PIXEL); std::unique_ptr no_texture_shader_program = build_shader_program_from_shaders({ vertex_no_texture_shader.get(), pixel_no_texture_shader.get() }); Model character_model = load_model("/home/dec05eba/Downloads/FinalBaseMesh.a3d", no_texture_shader_program.get()); character_model.frame.get_uniform_by_name("proj")->set(proj); Result char_view_uniform = character_model.frame.get_uniform_by_name("view"); Result char_model_uniform = character_model.frame.get_uniform_by_name("model"); glm::mat4 model = glm::rotate( glm::mat4(1.0f), 0.0f, glm::vec3(0.0f, 0.0f, 1.0f) ); model_uniform->set(model); glm::mat4 char_model = glm::rotate( glm::mat4(1.0f), glm::pi() * 0.5f, glm::vec3(1.0f, 0.0f, 0.0f) ); char_model = glm::scale(char_model, glm::vec3(0.1f, 0.1f, 0.1f)); char_model_uniform->set(char_model); glm::mat4 sand_model = glm::rotate( glm::mat4(1.0f), 0.0f, glm::vec3(0.0f, 0.0f, 1.0f) ); sand_model = glm::scale(sand_model, glm::vec3(50.0f, 50.0f, 50.0f)); sand_model_uniform->set(sand_model); Userdata userdata; ThirdPersonCamera third_person_camera; userdata.third_person_camera = &third_person_camera; glfwGetCursorPos(window, &userdata.prev_cursor_x, &userdata.prev_cursor_y); glm::vec3 character_pos(0.0f, 0.0f, 10.0f); auto t_start = std::chrono::high_resolution_clock::now(); glfwSetWindowUserPointer(window, &userdata); glfwSetScrollCallback(window, [](GLFWwindow *window, double xoffset, double yoffset){ Userdata *userdata = (Userdata*)glfwGetWindowUserPointer(window); userdata->third_person_camera->zoom(-yoffset*0.1f); }); glfwSetCursorPosCallback(window, [](GLFWwindow *window, double xpos, double ypos) { Userdata *userdata = (Userdata*)glfwGetWindowUserPointer(window); double diff_x = xpos - userdata->prev_cursor_x; double diff_y = ypos - userdata->prev_cursor_y; userdata->prev_cursor_x = xpos; userdata->prev_cursor_y = ypos; if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS) { userdata->third_person_camera->rotate_horizontal(diff_x * 0.005f); userdata->third_person_camera->rotate_vertical(-diff_y * 0.005f); } }); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); float time = 0.0f; while(!glfwWindowShouldClose(window)) { glfwPollEvents(); if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); auto t_now = std::chrono::high_resolution_clock::now(); float time_delta = std::chrono::duration_cast>(t_now - t_start).count(); t_start = t_now; time += time_delta; glm::vec3 camera_forward = third_person_camera.get_forward_vector(); glm::vec3 camera_right = third_person_camera.get_right_vector(); float move_speed = time_delta * 3.0f; glm::vec3 move_vec = glm::vec3(); if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { move_vec.x += camera_forward.x; move_vec.y += camera_forward.y; } if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { move_vec.x += -camera_forward.x; move_vec.y += -camera_forward.y; } if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { move_vec.x += -camera_right.x; move_vec.y += -camera_right.y; } if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { move_vec.x += camera_right.x; move_vec.y += camera_right.y; } if(glm::length(move_vec) > 0.0f) character_pos += (glm::normalize(move_vec) * move_speed); third_person_camera.set_target_position(character_pos); char_model[3][0] = character_pos.x; char_model[3][1] = character_pos.y; char_model[3][2] = character_pos.z - 2.0f; char_model_uniform->set(char_model); glm::mat4 camera_matrix = third_person_camera.get_matrix(); sand_view_uniform->set(camera_matrix); view_uniform->set(camera_matrix); char_view_uniform->set(camera_matrix); // Set color for clearing glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Do the actual screen clearing, using the color set using glClearColor glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); buddha_model.frame.draw(); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); character_model.frame.draw(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); sand_frame.draw(); glfwSwapBuffers(window); } //glfwTerminate(); return 0; } void create_sand(DeviceMemory *triangles, DeviceMemory *texcoords, Texture2D *texture) { std::vector cpu_sand = generate_sand(); triangles->set({cpu_sand.data(), cpu_sand.size()}, DeviceMemory::StorageType::STATIC); Result height_map_image = Image::loadFromFile("heightmap.jpg"); if(!height_map_image) { fprintf(stderr, "Error: failed to load image: heightmap.jpg\n"); exit(1); } std::vector height_map_texcoord; for(const Triangle3D &triangle : cpu_sand) { height_map_texcoord.push_back(vec2f{ triangle.p1.x, triangle.p1.y }); height_map_texcoord.push_back(vec2f{ triangle.p2.x, triangle.p2.y }); height_map_texcoord.push_back(vec2f{ triangle.p3.x, triangle.p3.y }); } Texture2D height_map_texture(height_map_image.unwrap()); delete height_map_image.unwrap(); *texture = std::move(height_map_texture); texcoords->set({height_map_texcoord.data(), height_map_texcoord.size()}, DeviceMemory::StorageType::STATIC); } Model load_model(const char *filepath, ShaderProgram *shader_program) { ShaderFrame model_frame = shader_program->create_frame(); std::vector triangles; std::vector texture_coords; Image *image; if(!ModelLoader::load_from_file(filepath, triangles, texture_coords, &image)) { fprintf(stderr, "Error: failed to load model from file: %s\n", filepath); } // TODO: This needs to be done to prevent crash in glDrawArrays, but this should not be used when shader doesn't handle any texture //if(texture_coords.size() < triangles.size()) // texture_coords.resize(triangles.size()); DeviceMemory *gpuModel = model_frame.get_input_by_name("position"); gpuModel->set({triangles.data(), triangles.size()}, DeviceMemory::StorageType::STATIC); if(image) { Texture2D model_texture(image); delete image; DeviceMemory *model_gpu_texcoords = model_frame.get_input_by_name("texcoord_vert"); model_gpu_texcoords->set({texture_coords.data(), texture_coords.size()}, DeviceMemory::StorageType::STATIC); Result tex_uniform = model_frame.get_uniform_by_name("tex"); tex_uniform->set(model_texture); return { std::move(model_frame), std::move(model_texture) }; } else { return { std::move(model_frame), Texture2D() }; } } void glfwErrorHandler(int errorCode, const char *errorDescription) { printf("GLFW error code: %d, description: %s\n", errorCode, errorDescription); } static void GLAPIENTRY gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user_param) { fprintf(stderr, "##### OPENGL ERROR #####\n"); fprintf(stderr, "source: %d, type: %d, id: %d, severity: %d, message: %s\n", source, type, id, severity, message); fprintf(stderr, "########################\n"); } void initGlfw() { if(!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); exit(-1); } glfwWindowHint(GLFW_SAMPLES, 4); // anti aliasing glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // TODO: Only enable in debugging mode glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); GLint flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags); if(flags & GL_CONTEXT_FLAG_DEBUG_BIT) { //glEnable(GL_DEBUG_OUTPUT); //glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); //glDebugMessageCallback(gl_debug_callback, nullptr); //glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); } else { fprintf(stderr, "Warning: failed to enable gl debugging\n"); } } void initGlew() { glewExperimental = true; if(glewInit() != GLEW_OK) { fprintf(stderr, "Failed to initialize GLEW\n"); glfwTerminate(); exit(-1); } glGetError(); // Reset error to none } GLFWwindow* createWindow() { GLFWwindow *window = glfwCreateWindow(1920, 1080, "Amalgine", nullptr, nullptr); if(!window) { fprintf(stderr, "Failed to open GLFW window\n"); glfwTerminate(); exit(10); } glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_FALSE); glfwMakeContextCurrent(window); glfwSwapInterval(1); // 1 = enable vsync initGlew(); return window; } static int file_read_all(const char *filepath, std::string &result) { FILE *file = fopen(filepath, "rb"); if(!file) { perror("file_read_all"); return errno; } fseek(file, 0, SEEK_END); size_t file_size = ftell(file); fseek(file, 0, SEEK_SET); result.resize(file_size); fread(&result[0], 1, result.size(), file); fclose(file); return 0; } std::unique_ptr load_shader_from_file(const char *filepath, Shader::Type shader_type) { std::string file_content; if(file_read_all(filepath, file_content) != 0) exit(13); Result> vertex_shader_result = Shader::compile(shader_type, file_content.data(), file_content.size()); if(!vertex_shader_result) { fprintf(stderr, "Failed to compile shader, error: %s\n", vertex_shader_result.getErrorMsg().c_str()); exit(12); } return std::move(vertex_shader_result.unwrap()); } std::unique_ptr build_shader_program_from_shaders(const std::vector &shaders) { Result> shader_program_result = ShaderProgram::build(shaders); if(!shader_program_result) { fprintf(stderr, "Failed to link shaders, error: %s\n", shader_program_result.getErrorMsg().c_str()); exit(13); } return std::move(shader_program_result.unwrap()); }