diff --git a/bind/v4k.lua b/bind/v4k.lua index 5f7377b..fec03ff 100644 --- a/bind/v4k.lua +++ b/bind/v4k.lua @@ -3015,7 +3015,7 @@ unsigned shader; } lightmap_t; lightmap_t lightmap(int hmsize , float near, float far, vec3 color , int passes , float threshold , float distmod ); void lightmap_setup(lightmap_t *lm, int w, int h); - void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata); + void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata); void lightmap_destroy(lightmap_t *lm); typedef struct skybox_t { handle program; diff --git a/demos/99-lmap.c b/demos/99-lmap.c index 9b5c05c..ae8f534 100644 --- a/demos/99-lmap.c +++ b/demos/99-lmap.c @@ -10,9 +10,20 @@ void bakedrawmodel(lightmap_t *lm, model_t *m, float *view, float *proj, void *u model_render(litm, proj, view, litm.pivot, lm->shader); } +void progressupdate(float progress) { + static double lastUpdateTime = 0.0; + double time = time_ss(); + if (time - lastUpdateTime > 1.0) { + lastUpdateTime = time; + PRINTF("progress: %.02f%%", progress*100); + } + // window_swap(); +} + int main() { - window_create(0.5, WINDOW_VSYNC_DISABLED); + window_create(0.5, 0); + window_title(__FILE__); camera_t cam = camera(); sky = skybox(0, 0); skybox_mie_calc_sh(&sky, 2.0f); model_t mdl = model("gazebo.obj", 0); @@ -31,11 +42,12 @@ int main() texture_t emission = texture_create(1,1,4,emissive,TEXTURE_LINEAR); model_set_texture(litm, emission); - lightmap_t baker = lightmap(64, 0.001, 1000, vec3(0,0,0), 2, 0.01, 0.0); - lightmap_setup(&baker, 640, 640); + lightmap_t baker = lightmap(64, 0.01, 100, vec3(0,0,0), 2, 0.01, 0.0); + lightmap_setup(&baker, 512, 512); array_push(baker.models, &mdl); - window_title("AO Baking demo"); + bool do_bake=0; + int b=1; while (window_swap() && !input(KEY_ESC)) { bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); @@ -53,11 +65,16 @@ int main() 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)"); - int b=2; + ui_int("Bounces", &b); if( ui_button(va("Bake %d light bounce", b)) ) { - lightmap_bake(&baker, b, bakedrawmodel, 0); + do_bake=1; } ui_panel_end(); } + + if (do_bake) { + do_bake=0; + lightmap_bake(&baker, b, bakedrawmodel, progressupdate, 0); + } } } diff --git a/engine/joint/v4k.h b/engine/joint/v4k.h index d1381e6..239fff3 100644 --- a/engine/joint/v4k.h +++ b/engine/joint/v4k.h @@ -17589,7 +17589,7 @@ typedef struct lightmap_t { API lightmap_t lightmap(int hmsize /*64*/, float near, float far, vec3 color /*1,1,1 for AO*/, int passes /*2*/, float threshold /*0.01f*/, float distmod /*0.0f*/); API void lightmap_setup(lightmap_t *lm, int w, int h); -API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata); +API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata); API void lightmap_destroy(lightmap_t *lm); // ----------------------------------------------------------------------------- @@ -348237,6 +348237,7 @@ struct lm_context { GLuint texture; lm_ivec2 writePosition; + int width, height; lm_ivec2 *toLightmapLocation; } storage; } hemisphere; @@ -348497,10 +348498,72 @@ static lm_bool lm_findNextConservativeTriangleRasterizerPosition(lm_context *ctx return lm_findFirstConservativeTriangleRasterizerPosition(ctx); } -static void lm_integrateHemisphereBatch(lm_context *ctx) +static void lm_writeResultsToLightmap(lm_context* ctx) +{ + // do the GPU->CPU transfer of downsampled hemispheres + float* hemi = (float*)LM_CALLOC(ctx->hemisphere.storage.width * ctx->hemisphere.storage.height, 4 * sizeof(float)); + glBindTexture(GL_TEXTURE_2D, ctx->hemisphere.storage.texture); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, hemi); + + // write results to lightmap texture + for (int y = 0; y < ctx->hemisphere.storage.writePosition.y + (int)ctx->hemisphere.fbHemiCountY; y++) + { + for (int x = 0; x < ctx->hemisphere.storage.width; x++) + { + int index = y * ctx->hemisphere.storage.width + x; + lm_ivec2 lmUV = ctx->hemisphere.storage.toLightmapLocation[index]; + if (lmUV.x >= 0) + { + float* c = hemi + index * 4; + float validity = c[3]; + float* lm = ctx->lightmap.data + (lmUV.y * ctx->lightmap.width + lmUV.x) * ctx->lightmap.channels; + if (!lm[0] && validity > 0.9) + { + float scale = 1.0f / validity; + switch (ctx->lightmap.channels) + { + case 1: + lm[0] = lm_maxf((c[0] + c[1] + c[2]) * scale / 3.0f, FLT_MIN); + break; + case 2: + lm[0] = lm_maxf((c[0] + c[1] + c[2]) * scale / 3.0f, FLT_MIN); + lm[1] = 1.0f; // do we want to support this format? + break; + case 3: + lm[0] = lm_maxf(c[0] * scale, FLT_MIN); + lm[1] = lm_maxf(c[1] * scale, FLT_MIN); + lm[2] = lm_maxf(c[2] * scale, FLT_MIN); + break; + case 4: + lm[0] = lm_maxf(c[0] * scale, FLT_MIN); + lm[1] = lm_maxf(c[1] * scale, FLT_MIN); + lm[2] = lm_maxf(c[2] * scale, FLT_MIN); + lm[3] = 1.0f; + break; + default: + assert(LM_FALSE); + break; + } + +#ifdef LM_DEBUG_INTERPOLATION + // set sampled pixel to red in debug output + ctx->lightmap.debug[(lmUV.y * ctx->lightmap.width + lmUV.x) * 3 + 0] = 255; +#endif + } + } + ctx->hemisphere.storage.toLightmapLocation[index].x = -1; // reset + } + } + + LM_FREE(hemi); + ctx->hemisphere.storage.writePosition = lm_i2(0, 0); +} + + +static lm_bool lm_integrateHemisphereBatch(lm_context *ctx) { if (!ctx->hemisphere.fbHemiIndex) - return; // nothing to do + return LM_FALSE; // nothing to do glDisable(GL_DEPTH_TEST); glBindVertexArray(ctx->hemisphere.vao); @@ -348568,83 +348631,30 @@ static void lm_integrateHemisphereBatch(lm_context *ctx) { int sx = ctx->hemisphere.storage.writePosition.x + x; unsigned int hemiIndex = y * ctx->hemisphere.fbHemiCountX + x; - if (hemiIndex >= ctx->hemisphere.fbHemiIndex) - ctx->hemisphere.storage.toLightmapLocation[sy * ctx->lightmap.width + sx] = lm_i2(-1, -1); - else - ctx->hemisphere.storage.toLightmapLocation[sy * ctx->lightmap.width + sx] = ctx->hemisphere.fbHemiToLightmapLocation[hemiIndex]; + ctx->hemisphere.storage.toLightmapLocation[sy * ctx->hemisphere.storage.width + sx] = + (hemiIndex >= ctx->hemisphere.fbHemiIndex) ? + lm_i2(-1, -1) : + ctx->hemisphere.fbHemiToLightmapLocation[hemiIndex]; } } + lm_bool needWrite = LM_TRUE; // advance storage texture write position ctx->hemisphere.storage.writePosition.x += ctx->hemisphere.fbHemiCountX; - if (ctx->hemisphere.storage.writePosition.x + (int)ctx->hemisphere.fbHemiCountX > ctx->lightmap.width) + if (ctx->hemisphere.storage.writePosition.x + (int)ctx->hemisphere.fbHemiCountX > ctx->hemisphere.storage.width) { ctx->hemisphere.storage.writePosition.x = 0; - ctx->hemisphere.storage.writePosition.y += ctx->hemisphere.fbHemiCountY; - assert(ctx->hemisphere.storage.writePosition.y + (int)ctx->hemisphere.fbHemiCountY < ctx->lightmap.height); + // storage is full + if (ctx->hemisphere.storage.writePosition.y + (int)ctx->hemisphere.fbHemiCountY >= ctx->hemisphere.storage.height) { + lm_writeResultsToLightmap(ctx); // read storage data from gpu memory and write it to the lightmap + needWrite = LM_FALSE; + } else { + ctx->hemisphere.storage.writePosition.y += ctx->hemisphere.fbHemiCountY; + } } ctx->hemisphere.fbHemiIndex = 0; -} - -static void lm_writeResultsToLightmap(lm_context *ctx) -{ - // do the GPU->CPU transfer of downsampled hemispheres - float *hemi = (float*)LM_CALLOC(ctx->lightmap.width * ctx->lightmap.height, 4 * sizeof(float)); - glBindTexture(GL_TEXTURE_2D, ctx->hemisphere.storage.texture); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, hemi); - - // write results to lightmap texture - for (int y = 0; y < ctx->hemisphere.storage.writePosition.y + (int)ctx->hemisphere.fbHemiCountY; y++) - { - for (int x = 0; x < ctx->lightmap.width; x++) - { - lm_ivec2 lmUV = ctx->hemisphere.storage.toLightmapLocation[y * ctx->lightmap.width + x]; - if (lmUV.x >= 0) - { - float *c = hemi + (y * ctx->lightmap.width + x) * 4; - float validity = c[3]; - float *lm = ctx->lightmap.data + (lmUV.y * ctx->lightmap.width + lmUV.x) * ctx->lightmap.channels; - if (!lm[0] && validity > 0.9) - { - float scale = 1.0f / validity; - switch (ctx->lightmap.channels) - { - case 1: - lm[0] = lm_maxf((c[0] + c[1] + c[2]) * scale / 3.0f, FLT_MIN); - break; - case 2: - lm[0] = lm_maxf((c[0] + c[1] + c[2]) * scale / 3.0f, FLT_MIN); - lm[1] = 1.0f; // do we want to support this format? - break; - case 3: - lm[0] = lm_maxf(c[0] * scale, FLT_MIN); - lm[1] = lm_maxf(c[1] * scale, FLT_MIN); - lm[2] = lm_maxf(c[2] * scale, FLT_MIN); - break; - case 4: - lm[0] = lm_maxf(c[0] * scale, FLT_MIN); - lm[1] = lm_maxf(c[1] * scale, FLT_MIN); - lm[2] = lm_maxf(c[2] * scale, FLT_MIN); - lm[3] = 1.0f; - break; - default: - assert(LM_FALSE); - break; - } - -#ifdef LM_DEBUG_INTERPOLATION - // set sampled pixel to red in debug output - ctx->lightmap.debug[(lmUV.y * ctx->lightmap.width + lmUV.x) * 3 + 0] = 255; -#endif - } - } - ctx->hemisphere.storage.toLightmapLocation[y * ctx->lightmap.width + x].x = -1; // reset - } - } - - LM_FREE(hemi); - ctx->hemisphere.storage.writePosition = lm_i2(0, 0); + return needWrite; } static void lm_setView( @@ -348921,6 +348931,7 @@ static void lm_setMeshPosition(lm_context *ctx, unsigned int indicesTriangleBase // TODO: signed formats case LM_FLOAT: { n = *(const lm_vec3*)nPtr; + n = lm_normalize3(lm_transformNormal(ctx->mesh.normalMatrix, n)); } break; case LM_NONE: { n = flatNormal; @@ -348929,7 +348940,7 @@ static void lm_setMeshPosition(lm_context *ctx, unsigned int indicesTriangleBase assert(LM_FALSE); } break; } - ctx->meshPosition.triangle.n[i] = lm_normalize3(lm_transformNormal(ctx->mesh.normalMatrix, n)); + ctx->meshPosition.triangle.n[i] = n; } // calculate area of interest (on lightmap) for conservative rasterization @@ -349286,16 +349297,13 @@ void lmSetHemisphereWeights(lm_context *ctx, lm_weight_func f, void *userdata) LM_FREE(weights); } -void lmSetTargetLightmap(lm_context *ctx, float *outLightmap, int w, int h, int c) +static void lm_initStorage(lm_context *ctx, int w, int h) { - ctx->lightmap.data = outLightmap; - ctx->lightmap.width = w; - ctx->lightmap.height = h; - ctx->lightmap.channels = c; - // allocate storage texture if (!ctx->hemisphere.storage.texture) glGenTextures(1, &ctx->hemisphere.storage.texture); + ctx->hemisphere.storage.width = w; + ctx->hemisphere.storage.height = h; glBindTexture(GL_TEXTURE_2D, ctx->hemisphere.storage.texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -349310,6 +349318,18 @@ void lmSetTargetLightmap(lm_context *ctx, float *outLightmap, int w, int h, int // invalidate all positions for (int i = 0; i < w * h; i++) ctx->hemisphere.storage.toLightmapLocation[i].x = -1; +} + +void lmSetTargetLightmap(lm_context *ctx, float *outLightmap, int w, int h, int c) +{ + ctx->lightmap.data = outLightmap; + ctx->lightmap.width = w; + ctx->lightmap.height = h; + ctx->lightmap.channels = c; + + unsigned int sw = w > ctx->hemisphere.fbHemiCountX ? w : ctx->hemisphere.fbHemiCountX; + unsigned int sh = h > ctx->hemisphere.fbHemiCountY ? h : ctx->hemisphere.fbHemiCountY; + lm_initStorage(ctx, sw, sh); #ifdef LM_DEBUG_INTERPOLATION if (ctx->lightmap.debug) @@ -349363,8 +349383,8 @@ lm_bool lmBegin(lm_context *ctx, int* outViewport4, float* outView4x4, float* ou } else { // ...and there are no triangles left: finish - lm_integrateHemisphereBatch(ctx); // integrate and store last batch - lm_writeResultsToLightmap(ctx); // read storage data from gpu memory and write it to the lightmap + if (lm_integrateHemisphereBatch(ctx)) // integrate and store last batch + lm_writeResultsToLightmap(ctx); // read storage data from gpu memory and write it to the lightmap if (++ctx->meshPosition.pass == ctx->meshPosition.passCount) { @@ -349639,8 +349659,7 @@ lm_bool lmImageSaveTGAf(const char *filename, const float *image, int w, int h, return success; } -#endif // LIGHTMAPPER_IMPLEMENTATION -#line 0 +#endif // LIGHTMAPPER_IMPLEMENTATION#line 0 #endif // V4K_3RD /* game framework. @@ -370328,7 +370347,7 @@ void lightmap_setup(lightmap_t *lm, int w, int h) { lm->h = h; } -void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata) { +void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata) { ASSERT(lm->ready); // @fixme: use xatlas to UV pack all models, update their UV1 and upload them to GPU. @@ -370371,6 +370390,7 @@ void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm // render to lightmapper framebuffer glViewport(vp[0], vp[1], vp[2], vp[3]); drawscene(lm, m, view, projection, userdata); + if (progressupdate) progressupdate(lmProgress(lm->ctx)); lmEnd(lm->ctx); } } diff --git a/engine/split/v4k_render.c b/engine/split/v4k_render.c index c27d397..99278d1 100644 --- a/engine/split/v4k_render.c +++ b/engine/split/v4k_render.c @@ -3767,7 +3767,7 @@ void lightmap_setup(lightmap_t *lm, int w, int h) { lm->h = h; } -void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata) { +void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata) { ASSERT(lm->ready); // @fixme: use xatlas to UV pack all models, update their UV1 and upload them to GPU. @@ -3810,6 +3810,7 @@ void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm // render to lightmapper framebuffer glViewport(vp[0], vp[1], vp[2], vp[3]); drawscene(lm, m, view, projection, userdata); + if (progressupdate) progressupdate(lmProgress(lm->ctx)); lmEnd(lm->ctx); } } diff --git a/engine/split/v4k_render.h b/engine/split/v4k_render.h index a4b6518..b2c7462 100644 --- a/engine/split/v4k_render.h +++ b/engine/split/v4k_render.h @@ -598,7 +598,7 @@ typedef struct lightmap_t { API lightmap_t lightmap(int hmsize /*64*/, float near, float far, vec3 color /*1,1,1 for AO*/, int passes /*2*/, float threshold /*0.01f*/, float distmod /*0.0f*/); API void lightmap_setup(lightmap_t *lm, int w, int h); -API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata); +API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata); API void lightmap_destroy(lightmap_t *lm); // ----------------------------------------------------------------------------- diff --git a/engine/v4k.c b/engine/v4k.c index a34e597..3291064 100644 --- a/engine/v4k.c +++ b/engine/v4k.c @@ -20683,7 +20683,7 @@ void lightmap_setup(lightmap_t *lm, int w, int h) { lm->h = h; } -void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata) { +void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata) { ASSERT(lm->ready); // @fixme: use xatlas to UV pack all models, update their UV1 and upload them to GPU. @@ -20726,6 +20726,7 @@ void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm // render to lightmapper framebuffer glViewport(vp[0], vp[1], vp[2], vp[3]); drawscene(lm, m, view, projection, userdata); + if (progressupdate) progressupdate(lmProgress(lm->ctx)); lmEnd(lm->ctx); } } diff --git a/engine/v4k.h b/engine/v4k.h index b463ec7..7d4e3e2 100644 --- a/engine/v4k.h +++ b/engine/v4k.h @@ -3656,7 +3656,7 @@ typedef struct lightmap_t { API lightmap_t lightmap(int hmsize /*64*/, float near, float far, vec3 color /*1,1,1 for AO*/, int passes /*2*/, float threshold /*0.01f*/, float distmod /*0.0f*/); API void lightmap_setup(lightmap_t *lm, int w, int h); -API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void *userdata); +API void lightmap_bake(lightmap_t *lm, int bounces, void (*drawscene)(lightmap_t *lm, model_t *m, float *view, float *proj, void *userdata), void (*progressupdate)(float progress), void *userdata); API void lightmap_destroy(lightmap_t *lm); // -----------------------------------------------------------------------------