// glad needs to be initialized before GLFW #include #include #include #include #include #include "logger/log.h" #include "util.h" #include "logger/gl_log.h" #include "renderer/matrix.h" #include "renderer/mesh.h" #include "renderer/shader.h" #include "renderer/vector.h" #include "wmo/wmo.h" #include "wmo/wmo_structs.h" #include "renderer/texture.h" // reported window size maybe good to know for a few things int g_win_width = 640; int g_win_height = 480; // keep track of framebuffer size for things like the viewport and the mouse cursor int g_fb_width = 640; int g_fb_height = 480; // time tracking double previous_seconds; int frame_count; // function to update the window title with a frame rate void _update_fps_counter(GLFWwindow* window) { double current_seconds; double elapsed_seconds; current_seconds = glfwGetTime(); elapsed_seconds = current_seconds - previous_seconds; /* limit text updates to 4 per second */ if (elapsed_seconds > 0.25) { previous_seconds = current_seconds; char tmp[128]; double fps = (double)frame_count / elapsed_seconds; // 1000 / fps = expected ms per frame // elapsed_seconds / frame_count * 1000 = time per frame in ms sprintf(tmp, "opengl @ fps: %.2f time per frame in milliseconds: %.4f", fps, ((elapsed_seconds / frame_count) * 1000)); glfwSetWindowTitle(window, tmp); frame_count = 0; } frame_count++; } // clears stdin buffer, trailing '\n' causes issues when using scanf_s() and fgets() for file name parsing void clear_input_buffer() { int c; while ((c = getchar()) != '\n' && c != EOF); } void glfw_error_callback(int error, const char *description) { gl_log_err("GLFW ERROR: code %i msg: %s\n", error, description); log_error("GLFW ERROR: code %i msg: %s", error, description); } void glfw_window_size_callback(GLFWwindow* window, int width, int height) { g_win_width = width; g_win_height = height; gl_log("Window Resolution changed: width: %d, height: %d\n"); } void glfw_framebuffer_resize_callback(GLFWwindow* window, int width, int height) { g_fb_width = width; g_fb_height = height; gl_log("Framebuffer resized: width: %d, height: %d\n"); // TODO: update any perspective matrices used here //mat4_make_perspective(67 * ONE_DEG_IN_RAD, g_fb_width/g_fb_height, 1.0f, 100.0f); } const char *GL_type_to_string(GLenum type) { switch (type) { case GL_BOOL: return "bool"; case GL_INT: return "int"; case GL_FLOAT: return "float"; case GL_FLOAT_VEC2: return "vec2"; case GL_FLOAT_VEC3: return "vec3"; case GL_FLOAT_VEC4: return "vec4"; case GL_FLOAT_MAT2: return "mat2"; case GL_FLOAT_MAT3: return "mat3"; case GL_FLOAT_MAT4: return "mat4"; case GL_SAMPLER_2D: return "sampler2D"; case GL_SAMPLER_3D: return "sampler3D"; case GL_SAMPLER_CUBE: return "samplerCube"; case GL_SAMPLER_2D_SHADOW: return "sampler2DShadow"; default: break; } return "other"; } void _print_shader_info_log(GLuint shader_index) { int max_length = 2048; int actual_length = 0; char log[2048]; glGetShaderInfoLog(shader_index, max_length, &actual_length, log); printf("shader info log for GL index %u:\n%s\n", shader_index, log); } void _print_programme_info_log(GLuint programme) { int max_length = 2048; int actual_length = 0; char log[2048]; glGetProgramInfoLog(programme, max_length, &actual_length, log); log_info("program info log for GL index %u:\n%s", programme, log); } void print_all(GLuint programme) { log_info("--------------------\nshader programme %i info:", programme); int params = -1; glGetProgramiv(programme, GL_LINK_STATUS, ¶ms); log_info("GL_LINK_STATUS = %i", params); glGetProgramiv(programme, GL_ATTACHED_SHADERS, ¶ms); log_info("GL_ATTACHED_SHADERS = %i", params); for (GLuint i = 0; i < (GLuint)params; i++) { char name[64]; int max_length = 64; int actual_length = 0; int size = 0; GLenum type; glGetActiveAttrib(programme, i, max_length, &actual_length, &size, &type, name); if (size > 1) { for (int j = 0; j < size; j++) { char long_name[64]; sprintf(long_name, "%s[%i]", name, j); int location = glGetAttribLocation(programme, long_name); log_info(" %i) type:%s name:%s location:%i", i, GL_type_to_string(type), long_name, location); } } else { int location = glGetAttribLocation(programme, name); log_info(" %i) type:%s name:%s location:%i", i, GL_type_to_string(type), name, location); } } glGetProgramiv(programme, GL_ACTIVE_UNIFORMS, ¶ms); log_info("GL_ACTIVE_UNIFORMS = %i", params); for (GLuint i = 0; i < (GLuint)params; i++) { char name[64]; int max_length = 64; int actual_length = 0; int size = 0; GLenum type; glGetActiveUniform(programme, i, max_length, &actual_length, &size, &type, name); if (size > 1) { for (int j = 0; j < size; j++) { char long_name[64]; sprintf(long_name, "%s[%i]", name, j); int location = glGetUniformLocation(programme, long_name); log_info(" %i) type:%s name:%s location:%i", i, GL_type_to_string (type), long_name, location); } } else { int location = glGetUniformLocation(programme, name); log_info(" %i) type:%s name:%s location:%i", i, GL_type_to_string (type), name, location); } } _print_programme_info_log(programme); } // should only be used during development, quite computationally expensive bool is_valid(GLuint programme) { glValidateProgram(programme); int params = -1; glGetProgramiv(programme, GL_VALIDATE_STATUS, ¶ms); log_info("program %i GL_VALIDATE_STATUS = %i", programme, params); if (GL_TRUE != params) { _print_programme_info_log(programme); return false; } return true; } void init() { printf("What would you like to do?\n"); printf("\t1. Open MPQ\n\t2. Create MPQ\n\t3. Extract BLPs from WMO\n\t4. Renderer\n"); int input = 0; scanf_s("%d", &input); clear_input_buffer(); switch (input) { case 1: { HANDLE hMPQ = NULL; //DWORD dwError = 0; HANDLE hMPQ_COMMON = NULL; ArchiveManager manager; //char mpqFilePath[MAX_PATH]; // get_mpq_file_path(mpqFilePath, MAX_PATH); PathList list_container; char wowDataDir[MAX_PATH]; get_data_dir(wowDataDir, MAX_PATH); init_archive_manager(&manager, wowDataDir); init_path_list(&list_container); recursively_scan_directory(wowDataDir, &list_container); for (int i = 0; i < list_container.count; i++) { log_info("Found path: '%s'", list_container.entries[i].path); } // free_path_list(&list_container); qsort(list_container.entries, list_container.count, sizeof(ArchiveEntry), archive_comparator); for (int i = 0; i < list_container.count; i++) { HANDLE hArchive; const char *mpqFilePath = list_container.entries[i].path; if (manager.count >= manager.capacity) { expand_archive_manager(&manager); } if (!SFileOpenArchive( mpqFilePath, 0, MPQ_OPEN_READ_ONLY, &hArchive)) { // Log error and continue to the next archive! log_error("Error opening MPQ '%s'. Error code: %lu\n", mpqFilePath, GetLastError()); continue; } // Success: Store the handle in the ArchiveManager manager.archives[manager.count++] = hArchive; // TODO - honestly I should mount these as needed... check newest patch first -> oldest // for files needed log_info("Successfully mounted archive #%zu: %s", manager.count, mpqFilePath); } /* if (!SFileOpenArchive( mpqFilePath, 0, MPQ_OPEN_READ_ONLY, &hMPQ)) { log_error("Error opening MPQ '%s'. Error code: %lu\n", mpqFilePath, GetLastError()); return; } if (!SFileOpenArchive("./common.MPQ", 0, MPQ_OPEN_READ_ONLY, &hMPQ_COMMON)) { DWORD dwError = GetLastError(); log_error("Error opening `common.MPQ` at `%s`. Error code: %lu\n", "./common.MPQ", dwError); return; } log_info("Successfully opened MPQ file: '%s'\n", mpqFilePath); getchar(); log_info("Getting (listfile) for test run...\n"); get_file_size_in_mpq(get_file_in_mpq(hMPQ, "(listfile)"), "(listfile)"); log_info("Getting (CTFNightelf_A.wmo) for WMO data test"); //WMOData test_WMOdata = load_wmo_data(hMPQ, "World\\wmo\\PvP\\Buildings\\CTF\\CTFOrc_A.wmo");*/ // "World\\wmo\\Dungeon\\MD_Caveden\\MD_VrykulDen.wmo" //char *wmoFileName = "World\\wmo\\PvP\\Buildings\\CTF\\CTFNightelf_A.wmo"; //char *wmoFileName = "World\\wmo\\Dungeon\\MD_Caveden\\MD_VrykulDen.wmo"; //char *wmoFileName = "World\\wmo\\Test\\test_petes_wmo_rotation_test.wmo"; char *wmoFileName = "World\\wmo\\Dungeon\\Ulduar\\Ulduar_Raid.wmo"; WMOData test_WMOdata = load_wmo_data(&manager, wmoFileName); const char *log_file_name = "wmo_geometry_export.obj.log"; char log_file_path[MAX_PATH]; snprintf(log_file_path, MAX_PATH, "%s.obj.log", log_file_name); FILE *log_file = fopen(log_file_name, "w"); if (log_file == NULL) { log_error("Failed to open log file: '%s'. Check permissions or dir", log_file_name); } WMORootData wmo_root_data = {0}; // TODO - SOURCE OF ERROR FOR BYTE MISMATCH? parse_wmo_chunks(&manager, test_WMOdata.data, test_WMOdata.size, &wmo_root_data, wmoFileName); // extract_wmo_geometry(&wmo_root_data, log_file); if (log_file != NULL) { fclose(log_file); } if (wmo_root_data.motx_data_ptr && wmo_root_data.momt_data_ptr) { log_info("Successfully located chunks."); // log_info("Located MVER chunk at offset %td", out_wmo_data.mver_data_ptr - wmo_buffer); // pass by reference parse_momt_and_extract_textures(&manager, &wmo_root_data); } else { log_error("Failed to find required MOMT and MOTX chunks."); } log_info("Testing struct data storage... MVER at %td, MOGN at %td", wmo_root_data.mver_data_ptr - test_WMOdata.data, wmo_root_data.mogn_data_ptr - test_WMOdata.data); if (!restart_gl_log()) { break; } gl_log("starting GLFW\n%s\n", glfwGetVersionString()); glfwSetErrorCallback(glfw_error_callback); // Initialize GLFW helper library to start GL context and O/S window if (!glfwInit()) { log_error("Could not initialize glfw"); return; } glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint (GLFW_SAMPLES, 4); // Set to "16" before taking screen shots? GLFWmonitor *mon = glfwGetPrimaryMonitor(); const GLFWvidmode *vmode = glfwGetVideoMode(mon); // Create a windowed mode window and its OpenGL context GLFWwindow* window = glfwCreateWindow(vmode->width, vmode->height, "GLFW Window", mon, NULL); if (!window) { log_error("Could not create glfw window, Error: %d", GetLastError()); glfwTerminate(); return; } // Make window's context current glfwMakeContextCurrent(window); // Load all OpenGL function pointers with GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { log_error("Failed to initialize GLAD"); glfwTerminate(); return; } glEnable(GL_MULTISAMPLE); // log GL params log_gl_params(); // get version info const GLubyte* renderer = glGetString(GL_RENDERER); const GLubyte* version = glGetString(GL_VERSION); log_info("Renderer: %s", renderer); log_info("OpenGL version supported %s", version); glfwSetWindowSizeCallback(window, glfw_window_size_callback); glfwSetFramebufferSizeCallback(window, glfw_framebuffer_resize_callback); // tell GL to only draw onto a pixel if the shape is closer to the viewer glEnable(GL_DEPTH_TEST); // enable depth testing glDepthFunc(GL_LESS); // depth-testing interprets a smaller value as "closer" wmo_load_textures_to_gpu(&manager, &wmo_root_data); GroupMesh *meshes = (GroupMesh*)calloc(wmo_root_data.group_count, sizeof(GroupMesh)); if (wmo_root_data.groups == NULL || wmo_root_data.group_count == 0) { log_error("No WMO groups loaded. Skipping mesh creation"); } else { for (size_t i = 0; i < wmo_root_data.group_count; i++) { if (wmo_root_data.groups[i].movt_data_ptr != NULL) { meshes[i] = create_mesh_from_group(&wmo_root_data.groups[i], &wmo_root_data); } else { log_warn("Skipping empty group %zu", i); } } } /* GLfloat points [] = { 0.0f, 0.5f, -1.0f, 0.4f, -0.5f, -1.0f, -0.4f, -0.5f, -1.0f, //-0.4f, -0.5f, 0.0f, //-0.4f, 0.5f, 0.0f, //0.4f, -0.5f, 0.0f, }; GLfloat colours[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; float matrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f, // first column 0.0f, 1.0f, 0.0f, 0.0f, // second column 0.0f, 0.0f, 1.0f, 0.0f, // third column 0.5f, 0.0f, 0.0f, 1.0f // fourth column }; GLuint points_vbo = 0; glGenBuffers(1, &points_vbo); glBindBuffer(GL_ARRAY_BUFFER, points_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW); GLuint colours_vbo = 0; glGenBuffers(1, &colours_vbo); glBindBuffer(GL_ARRAY_BUFFER, colours_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(colours), colours, GL_STATIC_DRAW); // bind vbos to ONE vertex array object // need to keep track of the VAO index for each type of mesh that is created GLuint vao = 0; glGenVertexArrays(1, &vao); glBindVertexArray(vao); //glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, points_vbo); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); glBindBuffer(GL_ARRAY_BUFFER, colours_vbo); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL); // this only affects the currently bound vertex array object. // only affects the attributes above basically // need to bind every new vertex array and repeat this procedure for those too glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); GLfloat rotation_x = 0.01; GLfloat rotation_y = 0.01; GLfloat rotation_z = 0.01; mat4_t scale_matrix = mat4_make_scale(1.0f, 1.0f, 1.0f); mat4_t translate_matrix = mat4_make_translation(1.0f, 1.0f, 5.0f); mat4_t rotation_matrix_x = mat4_make_rotation_x(rotation_x); mat4_t rotation_matrix_y = mat4_make_rotation_y(rotation_y); mat4_t rotation_matrix_z = mat4_make_rotation_z(rotation_z); */ char *vertex_shader = ("../../shaders/wmo.vert"); char *fragment_shader = ("../../shaders/wmo.frag"); GLuint shader_programme = create_shader_program_from_files(vertex_shader, fragment_shader); //print_all(shader_programme); // get unique location of the variable "inputColour" //GLuint colour_location = glGetUniformLocation(shader_programme, "inputColour"); int matrix_location = glGetUniformLocation(shader_programme, "matrix"); glUseProgram(shader_programme); float matrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f, // first column 0.0f, 1.0f, 0.0f, 0.0f, // second column 0.0f, 0.0f, 1.0f, 0.0f, // third column 0.5f, 0.0f, 0.0f, 1.0f // fourth column }; //glUniformMatrix4fv(matrix_location, 1, GL_FALSE, matrix); // assigns an initial colour to the fragment shader // can change uniform variables with glUniform() but only affects shader // glUniform4f(colour_location, 1.0f, 0.0f, 0.0f, 1.0f); /* pass pointer to matrix array's first element*/ // Optional: Set up the viewport and clear color once int width, height; glfwGetFramebufferSize(window, &width, &height); glViewport(0, 0, width, height); glClearColor(0.8f, 0.8f, 0.8f, 1.0f); //glEnable(GL_CULL_FACE); //glCullFace(GL_BACK); //glFrontFace(GL_CW); // GL_CCW for counter clock-wise glDisable(GL_CULL_FACE); float speed = 10.0f; // 1 unit per second float last_position = 0.0f; float cam_speed = 10.0f; // 1 unit per sec float cam_yaw_speed = 3.5f; // 3.5 degrees per second vec3_t cam_pos = { 0.0f, 20.0f, 150.0f }; // don't start at zero, or we will be too close float cam_yaw = 0.0f; // y-rotation in degrees float cam_pitch = 0.0f; mat4_t T = mat4_make_translation(-cam_pos.x, -cam_pos.y, -cam_pos.z); mat4_t R = mat4_make_rotation_y(-cam_yaw); mat4_t view_mat = mat4_mul_mat4(R, T); /* // projection matrix variables // input variables float znear = 0.1f; // clipping plane float zfar = 1000.0f; // clipping plane float fov = 67.0f * ONE_DEG_IN_RAD; // convert 67 degrees to radians float aspect = (float)width / (float)height; // aspect ratio // matrix components float range = tan(fov * 0.5f) * znear; //float Sx = (2.0f * znear) / (range * aspect + range * aspect); float Sy = znear / range; float Sx = Sy / aspect; float Sz = -(zfar + znear) / (zfar - znear); float Pz = -(2.0f * zfar * znear) / (zfar - znear); float proj_mat[] = { Sx, 0.0f, 0.0f, 0.0f, 0.0f, Sy, 0.0f, 0.0f, 0.0f, 0.0f, Sz, -1.0f, 0.0f, 0.0f, Pz, 0.0f };*/ float fov_radians = 67.0f * ONE_DEG_IN_RAD; mat4_t projection = mat4_make_perspective(fov_radians, (float)width / (float)height, 0.1f, 1000.0f); //mat4_t rotation_correction = mat4_make_rotation_x(TO_RAD(-90.0f)); mat4_t model_mat = mat4_identity(); int view_mat_location = glGetUniformLocation (shader_programme, "view"); glUseProgram (shader_programme); //glUniformMatrix4fv (view_mat_location, 1, GL_FALSE, view_mat.m); int proj_mat_location = glGetUniformLocation (shader_programme, "proj"); glUseProgram (shader_programme); //glUniformMatrix4fv (proj_mat_location, 1, GL_FALSE, proj_mat); glUniformMatrix4fv(matrix_location, 1, GL_FALSE, model_mat.m); glUniformMatrix4fv(view_mat_location, 1, GL_FALSE, view_mat.m); glUniformMatrix4fv(proj_mat_location, 1, GL_FALSE, projection.m); print_all(shader_programme); // call `glPolygonMode` before rendering if you want to use wire-frame rendering mode // Loop until the user closes the window while(!glfwWindowShouldClose(window)) { _update_fps_counter(window); // add a timer for doing animation static double previous_seconds = 0; double current_seconds = glfwGetTime(); double elapsed_seconds = current_seconds - previous_seconds; previous_seconds = current_seconds; if (fabs(last_position) > 1.0f) { speed = -speed; } //rotation_z += 0.1; // update the matrix //matrix[12] = elapsed_seconds * speed + last_position; //last_position = matrix[12]; //glUseProgram(shader_programme); //glUniformMatrix4fv(matrix_location, 1, GL_FALSE, rotation_matrix_z.m); double current_time = glfwGetTime(); /*if (current_time > 5000) { mat4_t new_scale_matrix = mat4_make_scale(0.5f, 2.0f, 1.0f); glUseProgram(shader_programme); glUniformMatrix4fv(matrix_location, 1, GL_FALSE, new_scale_matrix.m); }*/ /* if (current_time > 5000) { // Scale the vectors, rotate, then translate mat4_t S = mat4_make_scale(1.5f, 1.5f, 1.5f); mat4_t Rz = mat4_make_rotation_z(M_PI / 4.0f); mat4_t T = mat4_make_translation(1.0f, 0.0f, 0.0f); mat4_t temp_matrix = mat4_mul_mat4(Rz, S); mat4_t final_matrix = mat4_mul_mat4(T, temp_matrix); glUseProgram(shader_programme); glUniformMatrix4fv(matrix_location, 1, GL_FALSE, final_matrix.m); }*/ float yaw_rad = TO_RAD(cam_yaw); float pitch_rad = TO_RAD(cam_pitch); vec3_t cam_front; cam_front.x = cosf(yaw_rad) * cosf(pitch_rad); cam_front.y = sinf(pitch_rad); cam_front.z = sinf(yaw_rad) * cosf(pitch_rad); vec3_normalize(&cam_front); vec3_t world_up = { 0.0f, 1.0f, 0.0f }; vec3_t cam_right = vec3_cross(cam_front, world_up); vec3_normalize(&cam_right); vec3_t cam_up = vec3_cross(cam_right, cam_front); vec3_normalize(&cam_up); float move_dist = cam_speed * elapsed_seconds; view_mat = mat4_look_at( cam_pos.x, cam_pos.y, cam_pos.z, cam_pos.x + cam_front.x, cam_pos.y + cam_front.y, cam_pos.z + cam_front.z, cam_up.x, cam_up.y, cam_up.z ); // Render here // wipe the drawing surface clear glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Update viewport size for scaling glViewport(0, 0, g_fb_width, g_fb_height); for (size_t i = 0; i < wmo_root_data.group_count; i++) { if (meshes[i].VAO != 0) { //glActiveTexture(GL_TEXTURE0); //glBindTexture(GL_TEXTURE_2D, meshes[i].textureID); draw_group_mesh(meshes[i]); } } // update other events like input handling glfwPollEvents(); // Swap front and back buffers glfwSwapBuffers(window); // Check if we should exit if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE)) { glfwSetWindowShouldClose(window, 1); } else if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_9)) { glfwSetWindowMonitor(window, NULL, 480, 270, vmode->width / 2, vmode->height / 2, GLFW_DONT_CARE); } else if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_8)) { glfwSetWindowMonitor(window, mon, 0, 0, vmode->width, vmode->height, vmode->refreshRate); } else if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_7)) { shader_reload(&shader_programme, vertex_shader, fragment_shader); int new_matrix_loc = glGetUniformLocation(shader_programme, "matrix"); int new_view_loc = glGetUniformLocation(shader_programme, "view"); int new_proj_loc = glGetUniformLocation(shader_programme, "proj"); glUseProgram(shader_programme); glUniformMatrix4fv(new_matrix_loc, 1, GL_FALSE, model_mat.m); glUniformMatrix4fv(new_view_loc, 1, GL_FALSE, view_mat.m); glUniformMatrix4fv(new_proj_loc, 1, GL_FALSE, projection.m); } // control keys bool cam_moved = false; if (glfwGetKey (window, GLFW_KEY_A)) { //cam_pos[0] -= cam_speed * elapsed_seconds; vec3_t move_vec = vec3_mul(cam_right, move_dist); cam_pos = vec3_sub(cam_pos, move_vec); cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_D)) { //cam_pos[0] += cam_speed * elapsed_seconds; vec3_t move_vec = vec3_mul(cam_right, move_dist); cam_pos = vec3_add(cam_pos, move_vec); cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_PAGE_UP)) { //cam_pos[1] += cam_speed * elapsed_seconds; cam_pos.y += move_dist; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_PAGE_DOWN)) { //cam_pos[1] -= cam_speed * elapsed_seconds; cam_pos.y -= move_dist; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_W)) { //cam_pos[2] -= cam_speed * elapsed_seconds; vec3_t move_vec = vec3_mul(cam_front, move_dist); cam_pos = vec3_add(cam_pos, move_vec); cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_S)) { //cam_pos[2] += cam_speed * elapsed_seconds; vec3_t move_vec = vec3_mul(cam_front, move_dist); cam_pos = vec3_sub(cam_pos, move_vec); cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_LEFT)) { cam_yaw += cam_yaw_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_RIGHT)) { cam_yaw -= cam_yaw_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_UP)) { cam_pitch += cam_yaw_speed *elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_DOWN)) { cam_pitch -= cam_yaw_speed * elapsed_seconds; } if (cam_pitch > 89.0f) { cam_pitch = 89.0f; } if (cam_pitch < -89.0f) { cam_pitch = -89.0f; } // update view matrix if (cam_moved) { //T = mat4_make_translation(-cam_pos.x, -cam_pos.y, -cam_pos.z); //R = mat4_make_rotation_y(-cam_yaw); //view_mat = mat4_mul_mat4(R, T); view_mat = mat4_look_at( cam_pos.x, cam_pos.y, cam_pos.z, cam_pos.x + cam_front.x, cam_pos.y + cam_front.y, cam_pos.z + cam_front.z, cam_up.x, cam_up.y, cam_up.z ); glUniformMatrix4fv (view_mat_location, 1, GL_FALSE, view_mat.m); } } free_group_mesh(meshes); glfwTerminate(); break; } case 2: printf("TODO!!"); break; case 3: // printf("Enter a WMO filename or path\n"); FILE *file_handle = NULL; char wmo_file_path[MAX_PATH]; get_wmo_file_path(wmo_file_path, MAX_PATH); if (load_wmo_file(wmo_file_path, &file_handle) == 0) { log_info("Successfully loaded WMO file '%s'!", wmo_file_path); } else { log_error("Could not open WMO file '%s': %d", wmo_file_path, GetLastError()); } WMOData test_WMOdata = load_wmo_data_from_file(&file_handle); WMORootData wmo_root_data = {0}; HANDLE hMPQ = NULL; HANDLE hMPQ_COMMON = NULL; // parse_wmo_chunks(hMPQ, test_WMOdata.data, test_WMOdata.size, hMPQ_COMMON, &wmo_root_data, wmo_file_path); log_info("Sizeof SMOGroupInfo: %d", sizeof(SMOGroupInfo)); if (wmo_root_data.motx_data_ptr && wmo_root_data.momt_data_ptr) { log_info("Successfully located chunks."); // log_info("Located MVER chunk at offset %td", out_wmo_data.mver_data_ptr - wmo_buffer); // pass by reference // TODO: this currently uses stormlib for extracting out of the MPQ's when loaded // when we do it manually loading, there will be 0 BLP's unless we load from data dir // which is kinda redundant then, we could just export a list of BLP's through text...? // but the only way to do it I imagine with actual BLP files is loading the MPQ's themselves... // unless the MPQ is already fully extracted then we could just read from that 'cache'... otherwise we would just // read from MPQ memory!! only problem is the startup time is long while reading with Stormlib... // probably should just read header data of MPQ's and confirm if the file exists with the listfile instead of loading the whole // mpq into memory as we won't need EVERYTHING in there at the current moment (reduce debugging time) // parse_momt_and_extract_textures(hMPQ, &wmo_root_data, hMPQ_COMMON); } else { log_error("Failed to find required MOMT and MOTX chunks."); } break; case 4: if (!restart_gl_log()) { break; } gl_log("starting GLFW\n%s\n", glfwGetVersionString()); glfwSetErrorCallback(glfw_error_callback); // Initialize GLFW helper library to start GL context and O/S window if (!glfwInit()) { log_error("Could not initialize glfw"); return; } glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint (GLFW_SAMPLES, 4); // Set to "16" before taking screen shots? GLFWmonitor *mon = glfwGetPrimaryMonitor(); const GLFWvidmode *vmode = glfwGetVideoMode(mon); // Create a windowed mode window and its OpenGL context GLFWwindow* window = glfwCreateWindow(vmode->width, vmode->height, "GLFW Window", mon, NULL); if (!window) { log_error("Could not create glfw window, Error: %d", GetLastError()); glfwTerminate(); return; } // Make window's context current glfwMakeContextCurrent(window); // Load all OpenGL function pointers with GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { log_error("Failed to initialize GLAD"); glfwTerminate(); return; } glEnable(GL_MULTISAMPLE); // log GL params log_gl_params(); // get version info const GLubyte* renderer = glGetString(GL_RENDERER); const GLubyte* version = glGetString(GL_VERSION); log_info("Renderer: %s", renderer); log_info("OpenGL version supported %s", version); glfwSetWindowSizeCallback(window, glfw_window_size_callback); glfwSetFramebufferSizeCallback(window, glfw_framebuffer_resize_callback); // tell GL to only draw onto a pixel if the shape is closer to the viewer glEnable(GL_DEPTH_TEST); // enable depth testing glDepthFunc(GL_LESS); // depth-testing interprets a smaller value as "closer" GLfloat points [] = { 0.0f, 0.5f, -1.0f, 0.4f, -0.5f, -1.0f, -0.4f, -0.5f, -1.0f, //-0.4f, -0.5f, 0.0f, //-0.4f, 0.5f, 0.0f, //0.4f, -0.5f, 0.0f, }; GLfloat colours[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; float matrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f, // first column 0.0f, 1.0f, 0.0f, 0.0f, // second column 0.0f, 0.0f, 1.0f, 0.0f, // third column 0.5f, 0.0f, 0.0f, 1.0f // fourth column }; GLuint points_vbo = 0; glGenBuffers(1, &points_vbo); glBindBuffer(GL_ARRAY_BUFFER, points_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW); GLuint colours_vbo = 0; glGenBuffers(1, &colours_vbo); glBindBuffer(GL_ARRAY_BUFFER, colours_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(colours), colours, GL_STATIC_DRAW); // bind vbos to ONE vertex array object // need to keep track of the VAO index for each type of mesh that is created GLuint vao = 0; glGenVertexArrays(1, &vao); glBindVertexArray(vao); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, points_vbo); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); glBindBuffer(GL_ARRAY_BUFFER, colours_vbo); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL); // this only affects the currently bound vertex array object. // only affects the attributes above basically // need to bind every new vertex array and repeat this procedure for those too glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); GLfloat rotation_x = 0.01; GLfloat rotation_y = 0.01; GLfloat rotation_z = 0.01; mat4_t scale_matrix = mat4_make_scale(1.0f, 1.0f, 1.0f); mat4_t translate_matrix = mat4_make_translation(1.0f, 1.0f, 5.0f); mat4_t rotation_matrix_x = mat4_make_rotation_x(rotation_x); mat4_t rotation_matrix_y = mat4_make_rotation_y(rotation_y); mat4_t rotation_matrix_z = mat4_make_rotation_z(rotation_z); char *vertex_shader = ("../../shaders/wmo.vert"); char *fragment_shader = ("../../shaders/wmo.frag"); GLuint shader_programme = create_shader_program_from_files(vertex_shader, fragment_shader); // print_all(shader_programme); // get unique location of the variable "inputColour" GLuint colour_location = glGetUniformLocation(shader_programme, "inputColour"); int matrix_location = glGetUniformLocation(shader_programme, "matrix"); glUseProgram(shader_programme); glUniformMatrix4fv(matrix_location, 1, GL_FALSE, matrix); // assigns an initial colour to the fragment shader // can change uniform variables with glUniform() but only affects shader glUniform4f(colour_location, 1.0f, 0.0f, 0.0f, 1.0f); /* pass pointer to matrix array's first element*/ // Optional: Set up the viewport and clear color once int width, height; glfwGetFramebufferSize(window, &width, &height); glViewport(0, 0, width, height); glClearColor(0.8f, 0.8f, 0.8f, 1.0f); //glEnable(GL_CULL_FACE); //glCullFace(GL_BACK); //glFrontFace(GL_CW); // GL_CCW for counter clock-wise glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); float speed = 1.0f; // 1 unit per second float last_position = 0.0f; float cam_speed = 1.0f; // 1 unit per sec float cam_yaw_speed = 1.0f; // 10 degrees per second float cam_pos[] = { 0.0f, 0.0f, 2.0f }; // don't start at zero, or we will be too close float cam_yaw = 0.0f; // y-rotation in degrees mat4_t T = mat4_make_translation(-cam_pos[0], -cam_pos[1], -cam_pos[2]); mat4_t R = mat4_make_rotation_y(-cam_yaw); mat4_t view_mat = mat4_mul_mat4(R, T); // input variables float znear = 0.1f; // clipping plane float zfar = 100.0f; // clipping plane float fov = 67.0f * ONE_DEG_IN_RAD; // convert 67 degrees to radians float aspect = (float)width / (float)height; // aspect ratio // matrix components float range = tan(fov * 0.5f) * znear; //float Sx = (2.0f * znear) / (range * aspect + range * aspect); float Sy = znear / range; float Sx = Sy / aspect; float Sz = -(zfar + znear) / (zfar - znear); float Pz = -(2.0f * zfar * znear) / (zfar - znear); float proj_mat[] = { Sx, 0.0f, 0.0f, 0.0f, 0.0f, Sy, 0.0f, 0.0f, 0.0f, 0.0f, Sz, -1.0f, 0.0f, 0.0f, Pz, 0.0f }; int view_mat_location = glGetUniformLocation (shader_programme, "view"); glUseProgram (shader_programme); glUniformMatrix4fv (view_mat_location, 1, GL_FALSE, view_mat.m); int proj_mat_location = glGetUniformLocation (shader_programme, "proj"); glUseProgram (shader_programme); glUniformMatrix4fv (proj_mat_location, 1, GL_FALSE, proj_mat); // call `glPolygonMode` before rendering if you want to use wire-frame rendering mode // Loop until the user closes the window while(!glfwWindowShouldClose(window)) { _update_fps_counter(window); // add a timer for doing animation static double previous_seconds = 0; double current_seconds = glfwGetTime(); double elapsed_seconds = current_seconds - previous_seconds; previous_seconds = current_seconds; if (fabs(last_position) > 1.0f) { speed = -speed; } double current_time = glfwGetTime(); // Render here // wipe the drawing surface clear glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Update viewport size for scaling glViewport(0, 0, g_fb_width, g_fb_height); // draw points from the currently bound VAO with current in-use shader // GL_POINTS looks ideal for debugging vertex locations if unsure glUseProgram(shader_programme); //glBindVertexArray(vao); glDrawArrays(GL_TRIANGLES, 0, 3); // update other events like input handling glfwPollEvents(); // Swap front and back buffers glfwSwapBuffers(window); // Check if we should exit if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_ESCAPE)) { glfwSetWindowShouldClose(window, 1); } else if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_9)) { glfwSetWindowMonitor(window, NULL, 480, 270, vmode->width / 2, vmode->height / 2, GLFW_DONT_CARE); } else if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_8)) { glfwSetWindowMonitor(window, mon, 0, 0, vmode->width, vmode->height, vmode->refreshRate); } else if (GLFW_PRESS == glfwGetKey(window, GLFW_KEY_7)) { shader_reload(&shader_programme, vertex_shader, fragment_shader); int new_matrix_loc = glGetUniformLocation(shader_programme, "matrix"); int new_view_loc = glGetUniformLocation(shader_programme, "view"); int new_proj_loc = glGetUniformLocation(shader_programme, "proj"); glUseProgram(shader_programme); glUniformMatrix4fv(new_matrix_loc, 1, GL_FALSE, matrix); glUniformMatrix4fv(new_view_loc, 1, GL_FALSE, view_mat.m); glUniformMatrix4fv(new_proj_loc, 1, GL_FALSE, proj_mat); } // control keys bool cam_moved = false; if (glfwGetKey (window, GLFW_KEY_A)) { cam_pos[0] -= cam_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_D)) { cam_pos[0] += cam_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_PAGE_UP)) { cam_pos[1] += cam_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_PAGE_DOWN)) { cam_pos[1] -= cam_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_W)) { cam_pos[2] -= cam_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_S)) { cam_pos[2] += cam_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_LEFT)) { cam_yaw += cam_yaw_speed * elapsed_seconds; cam_moved = true; } if (glfwGetKey (window, GLFW_KEY_RIGHT)) { cam_yaw -= cam_yaw_speed * elapsed_seconds; cam_moved = true; } // update view matrix if (cam_moved) { T = mat4_make_translation(-cam_pos[0], -cam_pos[1], -cam_pos[2]); R = mat4_make_rotation_y(-cam_yaw); view_mat = mat4_mul_mat4(R, T); glUniformMatrix4fv (view_mat_location, 1, GL_FALSE, view_mat.m); } } glfwTerminate(); break; default: printf("DEFAULT TODO!"); } } int main() { log_set_level(1); log_info("Starting program and initializing..."); init(); // hard coded path or './' source path works currently // const char *mpqFileName = "F:\\C Projects\\BLPExtractor\\patch-3.MPQ"; return 0; }