diff --git a/.gitignore b/.gitignore index e728f92..2b0a607 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ rayjs.exe include/generated/ .DS_Store controls_test -controls_test.exe \ No newline at end of file +controls_test.exe +debug_interpolation.tga +result.tga +lightmapper_example diff --git a/.gitmodules b/.gitmodules index 8429ea1..af55ebc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,3 +9,6 @@ path = thirdparty/raygui url = https://github.com/raysan5/raygui.git branch = tags/3.6 +[submodule "thirdparty/lightmapper"] + path = thirdparty/lightmapper + url = https://github.com/ands/lightmapper.git diff --git a/.vscode/settings.json b/.vscode/settings.json index c21d077..554c054 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "vs.sc.mtl.bin.h": "c", "embedded_shader.h": "c", "common.h": "c", - "raylib.h": "c" + "raylib.h": "c", + "glad.h": "c" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 898f128..e9c92ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,9 @@ target_include_directories(quickjs ${quickjs_sources_root} ) +add_library(lightmapper INTERFACE) +target_include_directories(lightmapper INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/lightmapper) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) file(GLOB files src/*.c) #add_executable(${CMAKE_PROJECT_NAME} MACOSX_BUNDLE ${files}) @@ -40,6 +43,5 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE src) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE thirdparty/raygui/src) target_link_libraries(${CMAKE_PROJECT_NAME} quickjs raylib) -add_executable(controls_test thirdparty/raygui/examples/controls_test_suite/controls_test_suite.c) -target_include_directories(controls_test PRIVATE thirdparty/raygui/src) -target_link_libraries(controls_test raylib) \ No newline at end of file +add_executable(lightmapper_example src/lightmapper_example.c) +target_link_libraries(lightmapper_example raylib lightmapper) diff --git a/src/lightmapper_example.c b/src/lightmapper_example.c new file mode 100644 index 0000000..ebb903e --- /dev/null +++ b/src/lightmapper_example.c @@ -0,0 +1,564 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include +//#include "glad/glad.h" +#define GLAD_MALLOC(sz) malloc(sz) +#define GLAD_FREE(sz) free(sz) +//#define GLAD_GL_IMPLEMENTATION +#include "../thirdparty/raylib/src/external/glad.h" +#include "GLFW/glfw3.h" +#include "raylib.h" + +#define LIGHTMAPPER_IMPLEMENTATION +#define LM_DEBUG_INTERPOLATION +#include "lightmapper.h" + +#ifndef M_PI // even with _USE_MATH_DEFINES not always available +#define M_PI 3.14159265358979323846 +#endif + +typedef struct { + float p[3]; + float t[2]; +} vertex_t; + +typedef struct +{ + GLuint program; + GLint u_lightmap; + GLint u_projection; + GLint u_view; + + GLuint lightmap; + int w, h; + Model raylib_model; + GLuint vao, vbo, ibo; + vertex_t *vertices; + unsigned short *indices; + unsigned int vertexCount, indexCount; +} scene_t; + +static int initScene(scene_t *scene); +static void drawScene(scene_t *scene, float *view, float *projection); +static void destroyScene(scene_t *scene); + +static int bake(scene_t *scene) +{ + lm_context *ctx = lmCreate( + 64, // hemisphere resolution (power of two, max=512) + 0.001f, 100.0f, // zNear, zFar of hemisphere cameras + 1.0f, 1.0f, 1.0f, // background color (white for ambient occlusion) + 2, 0.01f, // lightmap interpolation threshold (small differences are interpolated rather than sampled) + // check debug_interpolation.tga for an overview of sampled (red) vs interpolated (green) pixels. + 0.0f); // modifier for camera-to-surface distance for hemisphere rendering. + // tweak this to trade-off between interpolated normals quality and other artifacts (see declaration). + + if (!ctx) + { + fprintf(stderr, "Error: Could not initialize lightmapper.\n"); + return 0; + } + + int w = scene->w, h = scene->h; + float *data = calloc(w * h * 4, sizeof(float)); + lmSetTargetLightmap(ctx, data, w, h, 4); + + lmSetGeometry(ctx, NULL, // no transformation in this example + LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, p), sizeof(vertex_t), + LM_NONE , NULL , 0 , // no interpolated normals in this example + LM_FLOAT, (unsigned char*)scene->vertices + offsetof(vertex_t, t), sizeof(vertex_t), + scene->indexCount, LM_UNSIGNED_SHORT, scene->indices); + + int vp[4]; + float view[16], projection[16]; + double lastUpdateTime = 0.0; + while (lmBegin(ctx, vp, view, projection)) + { + // render to lightmapper framebuffer + glViewport(vp[0], vp[1], vp[2], vp[3]); + drawScene(scene, view, projection); + + // display progress every second (printf is expensive) + double time = glfwGetTime(); + if (time - lastUpdateTime > 1.0) + { + lastUpdateTime = time; + printf("\r%6.2f%%", lmProgress(ctx) * 100.0f); + fflush(stdout); + } + + lmEnd(ctx); + } + printf("\rFinished baking %d triangles.\n", scene->indexCount / 3); + + lmDestroy(ctx); + + // postprocess texture + float *temp = calloc(w * h * 4, sizeof(float)); + for (int i = 0; i < 16; i++) + { + lmImageDilate(data, temp, w, h, 4); + lmImageDilate(temp, data, w, h, 4); + } + lmImageSmooth(data, temp, w, h, 4); + lmImageDilate(temp, data, w, h, 4); + lmImagePower(data, w, h, 4, 1.0f / 2.2f, 0x7); // gamma correct color channels + free(temp); + + // save result to a file + if (lmImageSaveTGAf("result.tga", data, w, h, 4, 1.0f)) + printf("Saved result.tga\n"); + + // upload result + glBindTexture(GL_TEXTURE_2D, scene->lightmap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_FLOAT, data); + free(data); + + return 1; +} + +static void error_callback(int error, const char *description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void fpsCameraViewMatrix(GLFWwindow *window, float *view); +static void perspectiveMatrix(float *out, float fovy, float aspect, float zNear, float zFar); + +static void mainLoop(GLFWwindow *window, scene_t *scene) +{ + glfwPollEvents(); + if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) + bake(scene); + + int w, h; + glfwGetFramebufferSize(window, &w, &h); + glViewport(0, 0, w, h); + + // camera for glfw window + float view[16], projection[16]; + fpsCameraViewMatrix(window, view); + perspectiveMatrix(projection, 45.0f, (float)w / (float)h, 0.01f, 100.0f); + + // draw to screen with a blueish sky + glClearColor(0.6f, 0.8f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + drawScene(scene, view, projection); + + glfwSwapBuffers(window); +} + +int main(int argc, char* argv[]) +{ + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + { + fprintf(stderr, "Could not initialize GLFW.\n"); + return EXIT_FAILURE; + } + + glfwWindowHint(GLFW_RED_BITS, 8); + glfwWindowHint(GLFW_GREEN_BITS, 8); + glfwWindowHint(GLFW_BLUE_BITS, 8); + glfwWindowHint(GLFW_ALPHA_BITS, 8); + glfwWindowHint(GLFW_DEPTH_BITS, 32); + glfwWindowHint(GLFW_STENCIL_BITS, GLFW_DONT_CARE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + glfwWindowHint(GLFW_SAMPLES, 4); + + GLFWwindow *window = glfwCreateWindow(1024, 768, "Lightmapping Example", NULL, NULL); + if (!window) + { + fprintf(stderr, "Could not create window.\n"); + glfwTerminate(); + return EXIT_FAILURE; + } + + glfwMakeContextCurrent(window); + gladLoadGL((GLADloadfunc)glfwGetProcAddress); + glfwSwapInterval(1); + + scene_t scene = {0}; + if (!initScene(&scene)) + { + fprintf(stderr, "Could not initialize scene.\n"); + glfwDestroyWindow(window); + glfwTerminate(); + return EXIT_FAILURE; + } + + printf("Ambient Occlusion Baking Example.\n"); + printf("Use your mouse and the W, A, S, D, E, Q keys to navigate.\n"); + printf("Press SPACE to start baking one light bounce!\n"); + printf("This will take a few seconds and bake a lightmap illuminated by:\n"); + printf("1. The mesh itself (initially black)\n"); + printf("2. A white sky (1.0f, 1.0f, 1.0f)\n"); + + while (!glfwWindowShouldClose(window)) + { + mainLoop(window, &scene); + } + + destroyScene(&scene); + glfwDestroyWindow(window); + glfwTerminate(); + return EXIT_SUCCESS; +} + +// helpers //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static int loadSimpleObjFile(const char *filename, vertex_t **vertices, unsigned int *vertexCount, unsigned short **indices, unsigned int *indexCount); +static GLuint loadProgram(const char *vp, const char *fp, const char **attributes, int attributeCount); + +static int initScene(scene_t *scene) +{ + // load mesh + scene->raylib_model = LoadModel("thirdparty/lightmapper/example/gazebo.obj"); + //scene->vertices = myModel.meshes[0]. + if (!loadSimpleObjFile("thirdparty/lightmapper/example/gazebo.obj", &scene->vertices, &scene->vertexCount, &scene->indices, &scene->indexCount)) + { + fprintf(stderr, "Error loading obj file\n"); + return 0; + } + + glGenVertexArrays(1, &scene->vao); + glBindVertexArray(scene->vao); + + glGenBuffers(1, &scene->vbo); + glBindBuffer(GL_ARRAY_BUFFER, scene->vbo); + glBufferData(GL_ARRAY_BUFFER, scene->vertexCount * sizeof(vertex_t), scene->vertices, GL_STATIC_DRAW); + + glGenBuffers(1, &scene->ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene->ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, scene->indexCount * sizeof(unsigned short), scene->indices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, p)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (void*)offsetof(vertex_t, t)); + + // create lightmap texture + scene->w = 654; + scene->h = 654; + glGenTextures(1, &scene->lightmap); + glBindTexture(GL_TEXTURE_2D, scene->lightmap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + unsigned char emissive[] = { 0, 0, 0, 255 }; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive); + + // load shader + const char *vp = + "#version 150 core\n" + "in vec3 a_position;\n" + "in vec2 a_texcoord;\n" + "uniform mat4 u_view;\n" + "uniform mat4 u_projection;\n" + "out vec2 v_texcoord;\n" + + "void main()\n" + "{\n" + "gl_Position = u_projection * (u_view * vec4(a_position, 1.0));\n" + "v_texcoord = a_texcoord;\n" + "}\n"; + + const char *fp = + "#version 150 core\n" + "in vec2 v_texcoord;\n" + "uniform sampler2D u_lightmap;\n" + "out vec4 o_color;\n" + + "void main()\n" + "{\n" + "o_color = vec4(texture(u_lightmap, v_texcoord).rgb, gl_FrontFacing ? 1.0 : 0.0);\n" + "}\n"; + + const char *attribs[] = + { + "a_position", + "a_texcoord" + }; + + scene->program = loadProgram(vp, fp, attribs, 2); + if (!scene->program) + { + fprintf(stderr, "Error loading shader\n"); + return 0; + } + scene->u_view = glGetUniformLocation(scene->program, "u_view"); + scene->u_projection = glGetUniformLocation(scene->program, "u_projection"); + scene->u_lightmap = glGetUniformLocation(scene->program, "u_lightmap"); + + return 1; +} + +static void drawScene(scene_t *scene, float *view, float *projection) +{ + glEnable(GL_DEPTH_TEST); + + glUseProgram(scene->program); + glUniform1i(scene->u_lightmap, 0); + glUniformMatrix4fv(scene->u_projection, 1, GL_FALSE, projection); + glUniformMatrix4fv(scene->u_view, 1, GL_FALSE, view); + + glBindTexture(GL_TEXTURE_2D, scene->lightmap); + + glBindVertexArray(scene->vao); + glDrawElements(GL_TRIANGLES, scene->indexCount, GL_UNSIGNED_SHORT, 0); +} + +static void destroyScene(scene_t *scene) +{ + free(scene->vertices); + free(scene->indices); + glDeleteVertexArrays(1, &scene->vao); + glDeleteBuffers(1, &scene->vbo); + glDeleteBuffers(1, &scene->ibo); + glDeleteTextures(1, &scene->lightmap); + glDeleteProgram(scene->program); +} + +static int loadSimpleObjFile(const char *filename, vertex_t **vertices, unsigned int *vertexCount, unsigned short **indices, unsigned int *indexCount) +{ + FILE *file = fopen(filename, "rt"); + if (!file) + return 0; + char line[1024]; + + // first pass + unsigned int np = 0, nn = 0, nt = 0, nf = 0; + while (!feof(file)) + { + fgets(line, 1024, file); + if (line[0] == '#') continue; + if (line[0] == 'v') + { + if (line[1] == ' ') { np++; continue; } + if (line[1] == 'n') { nn++; continue; } + if (line[1] == 't') { nt++; continue; } + assert(!"unknown vertex attribute"); + } + if (line[0] == 'f') { nf++; continue; } + assert(!"unknown identifier"); + } + assert(np && np == nn && np == nt && nf); // only supports obj files without separately indexed vertex attributes + + // allocate memory + *vertexCount = np; + *vertices = calloc(np, sizeof(vertex_t)); + *indexCount = nf * 3; + *indices = calloc(nf * 3, sizeof(unsigned short)); + + // second pass + fseek(file, 0, SEEK_SET); + unsigned int cp = 0, cn = 0, ct = 0, cf = 0; + while (!feof(file)) + { + fgets(line, 1024, file); + if (line[0] == '#') continue; + if (line[0] == 'v') + { + if (line[1] == ' ') { float *p = (*vertices)[cp++].p; char *e1, *e2; p[0] = (float)strtod(line + 2, &e1); p[1] = (float)strtod(e1, &e2); p[2] = (float)strtod(e2, 0); continue; } + if (line[1] == 'n') { /*float *n = (*vertices)[cn++].n; char *e1, *e2; n[0] = (float)strtod(line + 3, &e1); n[1] = (float)strtod(e1, &e2); n[2] = (float)strtod(e2, 0);*/ continue; } // no normals needed + if (line[1] == 't') { float *t = (*vertices)[ct++].t; char *e1; t[0] = (float)strtod(line + 3, &e1); t[1] = (float)strtod(e1, 0); continue; } + assert(!"unknown vertex attribute"); + } + if (line[0] == 'f') + { + unsigned short *tri = (*indices) + cf; + cf += 3; + char *e1, *e2, *e3 = line + 1; + for (int i = 0; i < 3; i++) + { + unsigned long pi = strtoul(e3 + 1, &e1, 10); + assert(e1[0] == '/'); + unsigned long ti = strtoul(e1 + 1, &e2, 10); + assert(e2[0] == '/'); + unsigned long ni = strtoul(e2 + 1, &e3, 10); + assert(pi == ti && pi == ni); + tri[i] = (unsigned short)(pi - 1); + } + continue; + } + assert(!"unknown identifier"); + } + + fclose(file); + return 1; +} + +static GLuint loadShader(GLenum type, const char *source) +{ + GLuint shader = glCreateShader(type); + if (shader == 0) + { + fprintf(stderr, "Could not create shader!\n"); + return 0; + } + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + GLint compiled; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) + { + fprintf(stderr, "Could not compile shader!\n"); + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen) + { + char* infoLog = (char*)malloc(infoLen); + glGetShaderInfoLog(shader, infoLen, NULL, infoLog); + fprintf(stderr, "%s\n", infoLog); + free(infoLog); + } + glDeleteShader(shader); + return 0; + } + return shader; +} +static GLuint loadProgram(const char *vp, const char *fp, const char **attributes, int attributeCount) +{ + GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vp); + if (!vertexShader) + return 0; + GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fp); + if (!fragmentShader) + { + glDeleteShader(vertexShader); + return 0; + } + + GLuint program = glCreateProgram(); + if (program == 0) + { + fprintf(stderr, "Could not create program!\n"); + return 0; + } + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + for (int i = 0; i < attributeCount; i++) + glBindAttribLocation(program, i, attributes[i]); + + glLinkProgram(program); + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + GLint linked; + glGetProgramiv(program, GL_LINK_STATUS, &linked); + if (!linked) + { + fprintf(stderr, "Could not link program!\n"); + GLint infoLen = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen) + { + char* infoLog = (char*)malloc(sizeof(char) * infoLen); + glGetProgramInfoLog(program, infoLen, NULL, infoLog); + fprintf(stderr, "%s\n", infoLog); + free(infoLog); + } + glDeleteProgram(program); + return 0; + } + return program; +} + +static void multiplyMatrices(float *out, float *a, float *b) +{ + for (int y = 0; y < 4; y++) + for (int x = 0; x < 4; x++) + out[y * 4 + x] = a[x] * b[y * 4] + a[4 + x] * b[y * 4 + 1] + a[8 + x] * b[y * 4 + 2] + a[12 + x] * b[y * 4 + 3]; +} +static void translationMatrix(float *out, float x, float y, float z) +{ + out[ 0] = 1.0f; out[ 1] = 0.0f; out[ 2] = 0.0f; out[ 3] = 0.0f; + out[ 4] = 0.0f; out[ 5] = 1.0f; out[ 6] = 0.0f; out[ 7] = 0.0f; + out[ 8] = 0.0f; out[ 9] = 0.0f; out[10] = 1.0f; out[11] = 0.0f; + out[12] = x; out[13] = y; out[14] = z; out[15] = 1.0f; +} +static void rotationMatrix(float *out, float angle, float x, float y, float z) +{ + angle *= (float)M_PI / 180.0f; + float c = cosf(angle), s = sinf(angle), c2 = 1.0f - c; + out[ 0] = x*x*c2 + c; out[ 1] = y*x*c2 + z*s; out[ 2] = x*z*c2 - y*s; out[ 3] = 0.0f; + out[ 4] = x*y*c2 - z*s; out[ 5] = y*y*c2 + c; out[ 6] = y*z*c2 + x*s; out[ 7] = 0.0f; + out[ 8] = x*z*c2 + y*s; out[ 9] = y*z*c2 - x*s; out[10] = z*z*c2 + c; out[11] = 0.0f; + out[12] = 0.0f; out[13] = 0.0f; out[14] = 0.0f; out[15] = 1.0f; +} +static void transformPosition(float *out, float *m, float *p) +{ + float d = 1.0f / (m[3] * p[0] + m[7] * p[1] + m[11] * p[2] + m[15]); + out[2] = d * (m[2] * p[0] + m[6] * p[1] + m[10] * p[2] + m[14]); + out[1] = d * (m[1] * p[0] + m[5] * p[1] + m[ 9] * p[2] + m[13]); + out[0] = d * (m[0] * p[0] + m[4] * p[1] + m[ 8] * p[2] + m[12]); +} +static void transposeMatrix(float *out, float *m) +{ + out[ 0] = m[0]; out[ 1] = m[4]; out[ 2] = m[ 8]; out[ 3] = m[12]; + out[ 4] = m[1]; out[ 5] = m[5]; out[ 6] = m[ 9]; out[ 7] = m[13]; + out[ 8] = m[2]; out[ 9] = m[6]; out[10] = m[10]; out[11] = m[14]; + out[12] = m[3]; out[13] = m[7]; out[14] = m[11]; out[15] = m[15]; +} +static void perspectiveMatrix(float *out, float fovy, float aspect, float zNear, float zFar) +{ + float f = 1.0f / tanf(fovy * (float)M_PI / 360.0f); + float izFN = 1.0f / (zNear - zFar); + out[ 0] = f / aspect; out[ 1] = 0.0f; out[ 2] = 0.0f; out[ 3] = 0.0f; + out[ 4] = 0.0f; out[ 5] = f; out[ 6] = 0.0f; out[ 7] = 0.0f; + out[ 8] = 0.0f; out[ 9] = 0.0f; out[10] = (zFar + zNear) * izFN; out[11] = -1.0f; + out[12] = 0.0f; out[13] = 0.0f; out[14] = 2.0f * zFar * zNear * izFN; out[15] = 0.0f; +} + +static void fpsCameraViewMatrix(GLFWwindow *window, float *view) +{ + // initial camera config + static float position[] = { 0.0f, 0.3f, 1.5f }; + static float rotation[] = { 0.0f, 0.0f }; + + // mouse look + static double lastMouse[] = { 0.0, 0.0 }; + double mouse[2]; + glfwGetCursorPos(window, &mouse[0], &mouse[1]); + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) + { + rotation[0] += (float)(mouse[1] - lastMouse[1]) * -0.2f; + rotation[1] += (float)(mouse[0] - lastMouse[0]) * -0.2f; + } + lastMouse[0] = mouse[0]; + lastMouse[1] = mouse[1]; + + float rotationY[16], rotationX[16], rotationYX[16]; + rotationMatrix(rotationX, rotation[0], 1.0f, 0.0f, 0.0f); + rotationMatrix(rotationY, rotation[1], 0.0f, 1.0f, 0.0f); + multiplyMatrices(rotationYX, rotationY, rotationX); + + // keyboard movement (WSADEQ) + float speed = (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) ? 0.1f : 0.01f; + float movement[3] = {0}; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) movement[2] -= speed; + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) movement[2] += speed; + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) movement[0] -= speed; + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) movement[0] += speed; + if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) movement[1] -= speed; + if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) movement[1] += speed; + + float worldMovement[3]; + transformPosition(worldMovement, rotationYX, movement); + position[0] += worldMovement[0]; + position[1] += worldMovement[1]; + position[2] += worldMovement[2]; + + // construct view matrix + float inverseRotation[16], inverseTranslation[16]; + transposeMatrix(inverseRotation, rotationYX); + translationMatrix(inverseTranslation, -position[0], -position[1], -position[2]); + multiplyMatrices(view, inverseRotation, inverseTranslation); // = inverse(translation(position) * rotationYX); +} diff --git a/thirdparty/lightmapper b/thirdparty/lightmapper new file mode 160000 index 0000000..4fd3bf4 --- /dev/null +++ b/thirdparty/lightmapper @@ -0,0 +1 @@ +Subproject commit 4fd3bf4e2c07263f85d5d875ebdef061bc512dd4