Files
wmo-editor/src/main.c
2026-01-05 10:03:07 -06:00

1051 lines
41 KiB
C

// glad needs to be initialized before GLFW
#include <math.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <StormLib.h>
#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, &params);
log_info("GL_LINK_STATUS = %i", params);
glGetProgramiv(programme, GL_ATTACHED_SHADERS, &params);
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, &params);
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, &params);
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;
}