#ifndef RLIGHTMAPPER_H #define RLIGHTMAPPER_H #include typedef struct Lightmapper { void * lm_handle; float * data; int w; int h; float progress; } Lightmapper; typedef struct LightmapperConfig { int hemisphereSize; float zNear; float zFar; Color backgroundColor; int interpolationPasses; float interpolationThreshold; float cameraToSurfaceDistanceModifier; } LightmapperConfig; LightmapperConfig GetDefaultLightmapperConfig(); Lightmapper LoadLightmapper(int w, int h, Mesh mesh, LightmapperConfig cfg); Material LoadMaterialLightmapper(Color emissiveColor, float intensity); void UnloadLightmapper(Lightmapper lm); void BeginLightmap(); void EndLightmap(); bool BeginLightmapFragment(Lightmapper * lm); void EndLightmapFragment(Lightmapper * lm); Image LoadImageFromLightmapper(Lightmapper lm); #endif #if defined(RLIGHTMAPPER_IMPLEMENTATION) #define LIGHTMAPPER_IMPLEMENTATION //#define LM_DEBUG_INTERPOLATION #include "lightmapper.h" static const char* fs = "#version 330\n" "in vec2 fragTexCoord;\n" "in vec4 fragColor;\n" "out vec4 finalColor;\n" "uniform sampler2D texture0;\n" "uniform vec4 colDiffuse;\n" "void main()\n" "{\n" " vec4 texelColor = texture(texture0, fragTexCoord);\n" " texelColor = texelColor * colDiffuse * fragColor * vec4(intensity, intensity, intensity, 1.0);\n" " finalColor = vec4(texelColor.rgb, (gl_FrontFacing ? 1.0 : 0.0));\n" "}"; const char* vs = "#version 330\n" "in vec3 vertexPosition;\n" "in vec2 vertexTexCoord;\n" "in vec4 vertexColor;\n" "out vec2 fragTexCoord;\n" "out vec4 fragColor;\n" "uniform mat4 mvp;\n" "void main()\n" "{\n" " fragTexCoord = vertexTexCoord;\n" " fragColor = vertexColor;\n" " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" "}"; static void FloatVToMatrix(float *array, struct Matrix *matrix) { matrix->m0 = array[0]; matrix->m1 = array[1]; matrix->m2 = array[2]; matrix->m3 = array[3]; matrix->m4 = array[4]; matrix->m5 = array[5]; matrix->m6 = array[6]; matrix->m7 = array[7]; matrix->m8 = array[8]; matrix->m9 = array[9]; matrix->m10 = array[10]; matrix->m11 = array[11]; matrix->m12 = array[12]; matrix->m13 = array[13]; matrix->m14 = array[14]; matrix->m15 = array[15]; } LightmapperConfig GetDefaultLightmapperConfig(){ return (LightmapperConfig){ 64, 0.001f, 100.0f, WHITE, 2, 0.01f, 0.0f }; } static Shader defaultShader; Material LoadMaterialLightmapper(Color emissiveColor, float emissiveIntensity) { if(defaultShader.id == 0) defaultShader = LoadShaderFromMemory(vs, fs); Material mat = LoadMaterialDefault(); mat.shader = defaultShader; mat.maps[MATERIAL_MAP_DIFFUSE].color = emissiveColor; // Diffuse color //mat.params[0] = emissiveIntensity; return mat; } Lightmapper LoadLightmapper(int w, int h, Mesh mesh, LightmapperConfig cfg){ Lightmapper lm = {0}; lm_context* ctx = lm.lm_handle = lmCreate(cfg.hemisphereSize, cfg.zNear, cfg.zFar, cfg.backgroundColor.r / (float)255, cfg.backgroundColor.g / (float)255, cfg.backgroundColor.b / (float)255, cfg.interpolationPasses, cfg.interpolationThreshold, cfg.cameraToSurfaceDistanceModifier); if(ctx == NULL){ TraceLog(LOG_ERROR, "Unable to create lightmapper. Init failed."); goto RETURN; } lm.w = w; lm.h = h; float *data = lm.data = calloc(w * h * 4, sizeof(float)); lmSetTargetLightmap(ctx, data, w, h, 4); const void* indices = NULL; lm_type indicesType = LM_NONE; int count = mesh.vertexCount; if(mesh.indices != NULL){ indices = mesh.indices; indicesType = LM_UNSIGNED_SHORT; count = mesh.triangleCount * 3; } lmSetGeometry(ctx, NULL, LM_FLOAT, (unsigned char*)mesh.vertices, 0, LM_FLOAT , (unsigned char*)mesh.normals, 0, LM_FLOAT, (unsigned char*)mesh.texcoords, 0, count, indicesType, indices); RETURN: return lm; } void UnloadLightmapper(Lightmapper lm){ free(lm.data); lmDestroy((lm_context *)lm.lm_handle); } static Matrix mProjection; static Matrix mModelview; void BeginLightmap() { rlEnableDepthTest(); rlDisableColorBlend(); rlDisableBackfaceCulling(); mProjection = rlGetMatrixProjection(); mModelview = rlGetMatrixModelview(); } void EndLightmap(){ //rlDisableDepthTest(); rlEnableColorBlend(); rlEnableBackfaceCulling(); int w = GetScreenWidth() * GetWindowScaleDPI().x; int h = GetScreenHeight() * GetWindowScaleDPI().y; rlViewport(0, 0, w, h); rlDisableFramebuffer(); glUseProgram(0); glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindVertexArray(0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); rlSetMatrixModelview(mModelview); rlSetMatrixProjection(mProjection); } static int vp[4]; static float view[16], projection[16]; static Matrix matView, matProj; bool BeginLightmapFragment(Lightmapper * lm){ lm_bool status = lmBegin((lm_context *)lm->lm_handle, vp, view, projection); if(status){ rlViewport(vp[0], vp[1], vp[2], vp[3]); FloatVToMatrix(view, &matView); FloatVToMatrix(projection, &matProj); rlSetMatrixModelview(matView); rlSetMatrixProjection(matProj); } else { lm->progress = 1.0f; } return (bool)status; } void EndLightmapFragment(Lightmapper * lm){ lm->progress = lmProgress((lm_context *)lm->lm_handle); lmEnd((lm_context *)lm->lm_handle); } Image LoadImageFromLightmapper(Lightmapper lm){ Image im = { 0 }; if(lm.progress < 1.0f){ TraceLog(LOG_ERROR, "Lightmapping is not finished"); return im; } // postprocess texture float *temp = calloc(lm.w * lm.h * 4, sizeof(float)); for (int i = 0; i < 16; i++) { lmImageDilate(lm.data, temp, lm.w, lm.h, 4); lmImageDilate(temp, lm.data, lm.w, lm.h, 4); } lmImageSmooth(lm.data, temp, lm.w, lm.h, 4); lmImageDilate(temp, lm.data, lm.w, lm.h, 4); lmImagePower(lm.data, lm.w, lm.h, 4, 1.0f / 2.2f, 0x7); // gamma correct color channels free(temp); unsigned char *tempub = (unsigned char*)calloc(lm.w * lm.h * 4, sizeof(unsigned char)); lmImageFtoUB(lm.data, tempub, lm.w, lm.h, 4, 1.0f); im.data = tempub; im.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; im.height = lm.w; im.width = lm.h; im.mipmaps = 1; return im; } #endif