Last active
September 8, 2021 05:53
-
-
Save alexGuntha/93539a02056deffcf2a0a3f73de798d2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _USE_MATH_DEFINES | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <math.h> | |
#include <assert.h> | |
#include "glad/glad.h" | |
#include "GLFW/glfw3.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; | |
GLint u_intensity; | |
GLuint lightmap; | |
int w, h; | |
GLuint vao, vbo, ibo; | |
vertex_t *vertices; | |
unsigned short *indices; | |
unsigned int vertexCount, indexCount; | |
GLuint vao2, vbo2, ibo2; | |
vertex_t *vertices2; | |
unsigned short *indices2; | |
unsigned int vertexCount2, indexCount2; | |
GLuint lightColorTexture; | |
} scene_t; | |
static int initScene(scene_t *scene); | |
static void drawScene(scene_t *scene, float *view, float *projection, float intensity); | |
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 | |
0.0f, 0.0f, 0.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, 40.0f); | |
// 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, 1.0f); | |
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); | |
gladLoadGLLoader((GLADloadproc)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 | |
if (!loadSimpleObjFile("gazebo.obj", &scene->vertices, &scene->vertexCount, &scene->indices, &scene->indexCount)) | |
{ | |
fprintf(stderr, "Error loading obj file\n"); | |
return 0; | |
} | |
//light source mesh | |
if (!loadSimpleObjFile("cube.obj", &scene->vertices2, &scene->vertexCount2, &scene->indices2, &scene->indexCount2)) | |
{ | |
fprintf(stderr, "Error loading obj file\n"); | |
return 0; | |
} | |
float lightPosition[] = {-0.2f, 0.4f, 0.5f}; | |
float lightscale[] = {0.03f, 0.03f, 0.03f}; | |
for(unsigned int i = 0; i < scene->vertexCount2; ++i) | |
{ | |
scene->vertices2[i].p[0] *= lightscale[0]; | |
scene->vertices2[i].p[1] *= lightscale[1]; | |
scene->vertices2[i].p[2] *= lightscale[2]; | |
scene->vertices2[i].p[0] += lightPosition[0]; | |
scene->vertices2[i].p[1] += lightPosition[1]; | |
scene->vertices2[i].p[2] += lightPosition[2]; | |
} | |
//end light source mesh | |
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); | |
//light source buffers | |
glGenVertexArrays(1, &scene->vao2); | |
glBindVertexArray(scene->vao2); | |
glGenBuffers(1, &scene->vbo2); | |
glBindBuffer(GL_ARRAY_BUFFER, scene->vbo2); | |
glBufferData(GL_ARRAY_BUFFER, scene->vertexCount2 * sizeof(vertex_t), scene->vertices2, GL_STATIC_DRAW); | |
glGenBuffers(1, &scene->ibo2); | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene->ibo2); | |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, scene->indexCount2 * sizeof(unsigned short), scene->indices2, GL_STATIC_DRAW); | |
//end light source buffers | |
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); | |
//light source texture (1x1 plain red texture) | |
{ | |
glGenTextures(1, &scene->lightColorTexture); | |
glBindTexture(GL_TEXTURE_2D, scene->lightColorTexture); | |
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[] = {255, 0, 0, 255}; | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, emissive); | |
} | |
//end light source texture | |
// 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" | |
"uniform float u_intensity;\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" | |
"o_color *= u_intensity;\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"); | |
scene->u_intensity = glGetUniformLocation(scene->program, "u_intensity"); | |
return 1; | |
} | |
static void drawScene(scene_t *scene, float *view, float *projection, float intensity) | |
{ | |
glEnable(GL_DEPTH_TEST); | |
glUseProgram(scene->program); | |
glUniform1i(scene->u_lightmap, 0); | |
glUniform1f(scene->u_intensity, 1.0f); | |
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); | |
glBindBuffer(GL_ARRAY_BUFFER, scene->vbo); | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene->ibo); | |
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)); | |
glDrawElements(GL_TRIANGLES, scene->indexCount, GL_UNSIGNED_SHORT, 0); | |
//Draw light source | |
//light sources are drawn AFTER the model to avoid noise artifacts | |
{ | |
glBindTexture(GL_TEXTURE_2D, scene->lightColorTexture); | |
glUniform1f(scene->u_intensity, intensity); | |
glBindVertexArray(scene->vao2); | |
glBindBuffer(GL_ARRAY_BUFFER, scene->vbo2); | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, scene->ibo2); | |
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)); | |
glDrawElements(GL_TRIANGLES, scene->indexCount2, 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); | |
glDeleteVertexArrays(1, &scene->vao2); | |
glDeleteBuffers(1, &scene->vbo2); | |
glDeleteBuffers(1, &scene->ibo2); | |
glDeleteTextures(1, &scene->lightmap); | |
glDeleteTextures(1, &scene->lightColorTexture); | |
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"); | |
fseek(file, 0, SEEK_END); | |
size_t fileSize = ftell(file); | |
fseek(file, 0, SEEK_SET); | |
if (!file) | |
return 0; | |
char line[1024]; | |
// first pass | |
unsigned int np = 0, nn = 0, nt = 0, nf = 0; | |
while (!feof(file)) | |
{ | |
fgets(line, fileSize < 1024 ? fileSize : 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; } | |
if (line[0] == 'o') { continue; } | |
if (line[0] == 'm') { continue; }//mtllib | |
if (line[0] == 'u') { continue; }//usemtl | |
if (line[0] == 's') { continue; } | |
if (line[0] == '\n') { continue; } | |
printf("%c\n", line[0]); | |
assert(!"unknown identifier"); | |
} | |
assert(np && (np == nn || nn == 0) && 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; | |
} | |
if(line[0] == 'o') { continue; } | |
if(line[0] == 'm') { continue; }//mtllib | |
if(line[0] == 'u') { continue; }//usemtl | |
if(line[0] == 's') { continue; } | |
if(line[0] == '\n') { 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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment