v4k-git-backup/demos/99-lmap.c

408 lines
14 KiB
C

#include "v4k.h"
#define LIGHTMAPPER_IMPLEMENTATION
// #define LM_DEBUG_INTERPOLATION
#include "3rd_lightmapper.h"
#define scene_t scene_t2
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;
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));
for (int b = 0; b < 1; b++) {
memset(data, 0, w*h*4);
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);
glDisable(GL_BLEND);
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 = time_ms() / 1000.0;
if (time - lastUpdateTime > 1.0)
{
lastUpdateTime = time;
printf("\r%6.2f%%", lmProgress(ctx) * 100.0f);
fflush(stdout);
}
lmEnd(ctx);
// window_swap();
}
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 fpsCameraViewMatrix(float *view);
static void mainLoop(scene_t *scene)
{
glViewport(0, 0, window_width(), window_height());
// camera for glfw window
float view[16], projection[16];
fpsCameraViewMatrix(view);
perspective44(projection, 45.0f, window_aspect(), 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);
}
int main()
{
window_create(0.5, WINDOW_VSYNC_DISABLED);
scene_t scene = {0};
if (!initScene(&scene))
{
fprintf(stderr, "Could not initialize scene.\n");
return EXIT_FAILURE;
}
window_title("AO Baking demo");
while (window_swap())
{
mainLoop(&scene);
if( ui_panel("Lightmapper", PANEL_OPEN) ) {
ui_label2("Freecam", "Mouse + W/A/S/D/E/Q keys");
ui_label("Warning " ICON_MD_WARNING "@This will take a few seconds and bake a lightmap illuminated by: The mesh itself (initially black) + A white sky (1.0f, 1.0f, 1.0f)");
if( ui_button("Bake 1 light bounce") ) {
bake(&scene);
}
ui_panel_end();
}
}
destroyScene(&scene);
}
// helpers ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int loadSimpleObjFile(const char *filename, vertex_t **vertices, unsigned int *vertexCount, unsigned short **indices, unsigned int *indexCount);
static int initScene(scene_t *scene)
{
// load mesh
if (!loadSimpleObjFile("demos/art/meshes/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 =
"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 =
"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";
scene->program = shader(vp, fp, "a_position,a_texcoord", "o_color", NULL);
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 void fpsCameraViewMatrix(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_coord[2];
mouse_coord[0] = input(MOUSE_X);
mouse_coord[1] = input(MOUSE_Y);
if (input(MOUSE_L))
{
rotation[0] += (float)(mouse_coord[1] - lastMouse[1]) * -0.2f;
rotation[1] += (float)(mouse_coord[0] - lastMouse[0]) * -0.2f;
}
lastMouse[0] = mouse_coord[0];
lastMouse[1] = mouse_coord[1];
float rotationY[16], rotationX[16], rotationYX[16];
rotation44(rotationX, rotation[0], 1.0f, 0.0f, 0.0f);
rotation44(rotationY, rotation[1], 0.0f, 1.0f, 0.0f);
multiply44x2(rotationYX, rotationY, rotationX);
// keyboard movement (WSADEQ)
float speed = input(KEY_SHIFT) ? 0.1f : 0.01f;
vec3 movement = {0};
if (input(KEY_W)) movement.z -= speed;
if (input(KEY_S)) movement.z += speed;
if (input(KEY_A)) movement.x -= speed;
if (input(KEY_D)) movement.x += speed;
if (input(KEY_E)) movement.y -= speed;
if (input(KEY_Q)) movement.y += speed;
vec3 worldMovement = transform344(rotationYX, movement);
position[0] += worldMovement.x;
position[1] += worldMovement.y;
position[2] += worldMovement.z;
// construct view matrix
float inverseRotation[16], inverseTranslation[16];
transpose44(inverseRotation, rotationYX);
translation44(inverseTranslation, -position[0], -position[1], -position[2]);
multiply44x2(view, inverseRotation, inverseTranslation); // = inverse(translation(position) * rotationYX);
}
#if 0
#######################################################################
76702 17.93% rendered hemicubes integrated to lightmap texels.
179388 41.94% interpolated lightmap texels.
171626 40.13% wasted lightmap texels.
29.95% of used texels were rendered.
#######################################################################
Finished baking 731 triangles.
Saved result.tga
vs
#######################################################################
124 0.05% rendered hemicubes integrated to lightmap texels.
201 0.08% interpolated lightmap texels.
261947 99.88% wasted lightmap texels.
38.15% of used texels were rendered.
#######################################################################
Finished baking 731 triangles.
Saved result.tga
#endif