diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d45b64..56b4eea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,4 @@ add_executable(${CMAKE_PROJECT_NAME} ${files}) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE include) 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(lightmapper_example src/lightmapper_example.c) -target_include_directories(lightmapper_example PRIVATE include) -target_link_libraries(lightmapper_example raylib lightmapper) +target_link_libraries(${CMAKE_PROJECT_NAME} quickjs raylib lightmapper) diff --git a/bindings/src/index.ts b/bindings/src/index.ts index 8c6779b..a82b6ac 100644 --- a/bindings/src/index.ts +++ b/bindings/src/index.ts @@ -68,6 +68,30 @@ function main(){ const reasingsFunctions = parser.parseFunctions(reasingsHeader); reasingsFunctions.forEach(x => api.functions.push(x)) + const rlightmapperHeader = readFileSync("src/rlightmapper.h", "utf8"); + const rlightmapperFunctions = parser.parseFunctionDefinitions(rlightmapperHeader); + const rlightmapperStructs = parser.parseStructs(rlightmapperHeader); + rlightmapperFunctions.forEach(x => api.functions.push(x)); + rlightmapperStructs.forEach(x => api.structs.push(x)); + rlightmapperStructs[0].binding = { + properties: { + w: { get: true }, + h: { get: true }, + progress: { get: true } + } + } + rlightmapperStructs[1].binding = { + properties: { + hemisphereSize: { get: true, set: true }, + zNear: { get: true, set: true }, + zFar: { get: true, set: true }, + backgroundColor: { get: true, set: true }, + interpolationPasses: { get: true, set: true }, + interpolationThreshold: { get: true, set: true }, + cameraToSurfaceDistanceModifier: { get: true, set: true }, + } + } + // Custom Rayjs functions api.functions.push({ name: "SetModelMaterial", @@ -87,6 +111,12 @@ function main(){ returnType: "Color", params: [{type: "Image *",name:"image"},{type:"int",name:"x"},{type:"int",name:"y"}] }) + api.functions.push({ + name: "GetModelMesh", + description: "Get a single mesh from a model", + returnType: "Mesh", + params: [{type: "Model *",name:"model"},{type:"int",name:"meshIndex"}] + }) // Define a new header const core = new RayLibHeader("raylib_core") @@ -97,6 +127,8 @@ function main(){ core.includes.line("#define RLIGHTS_IMPLEMENTATION") core.includes.include("rlights.h") core.includes.include("reasings.h") + core.includes.line("#define RLIGHTMAPPER_IMPLEMENTATION") + core.includes.include("rlightmapper.h") getStruct(api.structs, "Color")!.binding = { properties: { diff --git a/examples/js_lightmapper.js b/examples/js_lightmapper.js new file mode 100644 index 0000000..e51e657 --- /dev/null +++ b/examples/js_lightmapper.js @@ -0,0 +1,88 @@ +function drawScene(scene){ + drawModel(scene.raylib_model, new Vector3(0,0,0), 1, WHITE); +} + +setConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI | FLAG_VSYNC_HINT); + +initWindow(1024,768,"Test"); + +const scene = {} + +scene.raylib_model = loadModel("models/resources/models/obj/bridge.obj"); + +scene.w = 512; +scene.h = 512; +scene.raylib_texture = loadTextureFromImage(genImageColor(1,1,BLACK)); +const defMat = loadMaterialDefault(); +setMaterialTexture(defMat, MATERIAL_MAP_ALBEDO, scene.raylib_texture); +setModelMaterial(scene.raylib_model, 0, defMat); + +const position = new Vector3( 0.0, 10.0, 30.0 ); // Camera position +const target = new Vector3( 0.0, 0.35, 0.0); // Camera looking at point +const up = new Vector3(0.0, 1.0, 0.0); // Camera up vector (rotation towards target) +const fovy = 45.0; // Camera field-of-view Y +const projection = CAMERA_PERSPECTIVE; // Camera mode type +scene.camera = new Camera3D(position, target, up, fovy, projection); +const config = getDefaultLightmapperConfig(); +//config.backgroundColor = new Color(6,0,10); +//config.hemisphereSize = 512; +const mesh = getModelMesh(scene.raylib_model, 0); +const lm = loadLightmapper(scene.w, scene.h, mesh, config); +const lmMat = loadMaterialLightmapper(BLACK, 0); +const light = genMeshCube(0.2,0.2,0.2); +const lightMaterial = loadMaterialLightmapper(ORANGE, 0.005); + +while (!windowShouldClose()) +{ + if(isMouseButtonDown(MOUSE_BUTTON_LEFT)) + updateCamera(scene.camera, CAMERA_THIRD_PERSON); + + if(lm.progress < 1.0){ + let startTime = getTime(); + beginLightmap(); + while(beginLightmapFragment(lm)){ + drawMesh(mesh, lmMat, matrixIdentity()); + // drawMesh(light, lightMaterial, matrixTranslate(0.0,0.3,0.5)); + // drawMesh(light, lightMaterial, matrixTranslate(0.0,0.3,-0.5)); + // drawMesh(light, lightMaterial, matrixMultiply(matrixScale(2,1,2), matrixTranslate(0.0,1.3,0))); + // drawMesh(light, lightMaterial, matrixTranslate(0.5,0.3,0)); + // drawMesh(light, lightMaterial, matrixTranslate(-0.5,0.3,0)); + endLightmapFragment(lm); + // display progress every second (printf is expensive) + let time = getTime(); + if (getTime() - startTime > 0.03) break; + } + endLightmap(); + if(lm.progress == 1.0){ + const img = loadImageFromLightmapper(lm); + //exportImage(img, "my_result.png"); + const old = scene.raylib_texture; + scene.raylib_texture = loadTextureFromImage(img); + unloadTexture(old); + let mat = loadMaterialDefault(); + setMaterialTexture(mat, MATERIAL_MAP_DIFFUSE, scene.raylib_texture); + setModelMaterial(scene.raylib_model, 0, mat); + unloadLightmapper(lm); + } + } + + beginDrawing(); + clearBackground(BLUE); + + beginMode3D(scene.camera); + //float intensity = 1.0f; + //SetShaderValue(scene.shader, scene.u_intensity, &intensity, SHADER_UNIFORM_FLOAT); + drawScene(scene); + endMode3D(); + + // printf("%d\n",(int)(lm.progress*GetScreenWidth())); + if(lm.progress < 1.0){ + drawRectangle(0,0,getScreenWidth(),20, fade(GREEN,0.5)); + drawRectangle(0,0,getScreenWidth()*lm.progress,20, GREEN); + } + endDrawing(); +} + +unloadModel(scene.raylib_model); +unloadTexture(scene.raylib_texture); +closeWindow(); \ No newline at end of file diff --git a/examples/lib.raylib.d.ts b/examples/lib.raylib.d.ts index e6f72ec..9558d5d 100644 --- a/examples/lib.raylib.d.ts +++ b/examples/lib.raylib.d.ts @@ -361,6 +361,26 @@ interface Light { declare var Light: { prototype: Light; } +interface Lightmapper { + w: number, + h: number, + progress: number, +} +declare var Lightmapper: { + prototype: Lightmapper; +} +interface LightmapperConfig { + hemisphereSize: number, + zNear: number, + zFar: number, + backgroundColor: Color, + interpolationPasses: number, + interpolationThreshold: number, + cameraToSurfaceDistanceModifier: number, +} +declare var LightmapperConfig: { + prototype: LightmapperConfig; +} /** Initialize window and OpenGL context */ declare function initWindow(width: number, height: number, title: string | undefined | null): void; /** Check if KEY_ESCAPE pressed or Close icon pressed */ @@ -1616,12 +1636,32 @@ declare function easeBounceOut(t: number, b: number, c: number, d: number): numb declare function easeBounceInOut(t: number, b: number, c: number, d: number): number; /** Elastic Easing functions */ declare function easeElasticIn(t: number, b: number, c: number, d: number): number; +/** */ +declare function getDefaultLightmapperConfig(): LightmapperConfig; +/** */ +declare function loadLightmapper(w: number, h: number, mesh: Mesh, cfg: LightmapperConfig): Lightmapper; +/** */ +declare function loadMaterialLightmapper(emissiveColor: Color, intensity: number): Material; +/** */ +declare function unloadLightmapper(lm: Lightmapper): void; +/** */ +declare function beginLightmap(): void; +/** */ +declare function endLightmap(): void; +/** */ +declare function beginLightmapFragment(lm: Lightmapper): boolean; +/** */ +declare function endLightmapFragment(lm: Lightmapper): void; +/** */ +declare function loadImageFromLightmapper(lm: Lightmapper): Image; /** Replace material in slot materialIndex */ declare function setModelMaterial(model: Model, materialIndex: number, material: Material): void; /** Set shader constant in shader locations array */ declare function setShaderLocation(shader: Shader, shaderConstant: number, location: number): void; /** Read a single pixel from an image */ declare function imageReadPixel(image: Image, x: number, y: number): Color; +/** Get a single mesh from a model */ +declare function getModelMesh(model: Model, meshIndex: number): Mesh; /** (PI/180.0) */ declare var DEG2RAD: number; /** (180.0/PI) */ diff --git a/generate-bindings.js b/generate-bindings.js index d741079..d658524 100644 --- a/generate-bindings.js +++ b/generate-bindings.js @@ -1009,6 +1009,29 @@ function main() { const reasingsHeader = (0, fs_1.readFileSync)("include/reasings.h", "utf8"); const reasingsFunctions = parser.parseFunctions(reasingsHeader); reasingsFunctions.forEach(x => api.functions.push(x)); + const rlightmapperHeader = (0, fs_1.readFileSync)("src/rlightmapper.h", "utf8"); + const rlightmapperFunctions = parser.parseFunctionDefinitions(rlightmapperHeader); + const rlightmapperStructs = parser.parseStructs(rlightmapperHeader); + rlightmapperFunctions.forEach(x => api.functions.push(x)); + rlightmapperStructs.forEach(x => api.structs.push(x)); + rlightmapperStructs[0].binding = { + properties: { + w: { get: true }, + h: { get: true }, + progress: { get: true } + } + }; + rlightmapperStructs[1].binding = { + properties: { + hemisphereSize: { get: true, set: true }, + zNear: { get: true, set: true }, + zFar: { get: true, set: true }, + backgroundColor: { get: true, set: true }, + interpolationPasses: { get: true, set: true }, + interpolationThreshold: { get: true, set: true }, + cameraToSurfaceDistanceModifier: { get: true, set: true }, + } + }; // Custom Rayjs functions api.functions.push({ name: "SetModelMaterial", @@ -1028,6 +1051,12 @@ function main() { returnType: "Color", params: [{ type: "Image *", name: "image" }, { type: "int", name: "x" }, { type: "int", name: "y" }] }); + api.functions.push({ + name: "GetModelMesh", + description: "Get a single mesh from a model", + returnType: "Mesh", + params: [{ type: "Model *", name: "model" }, { type: "int", name: "meshIndex" }] + }); // Define a new header const core = new raylib_header_1.RayLibHeader("raylib_core"); core.includes.include("raymath.h"); @@ -1037,6 +1066,8 @@ function main() { core.includes.line("#define RLIGHTS_IMPLEMENTATION"); core.includes.include("rlights.h"); core.includes.include("reasings.h"); + core.includes.line("#define RLIGHTMAPPER_IMPLEMENTATION"); + core.includes.include("rlightmapper.h"); getStruct(api.structs, "Color").binding = { properties: { r: { get: true, set: true }, diff --git a/src/bindings/js_raylib_core.h b/src/bindings/js_raylib_core.h index 2b414ac..fa4374c 100644 --- a/src/bindings/js_raylib_core.h +++ b/src/bindings/js_raylib_core.h @@ -14,6 +14,8 @@ #define RLIGHTS_IMPLEMENTATION #include #include +#define RLIGHTMAPPER_IMPLEMENTATION +#include #ifndef countof #define countof(x) (sizeof(x) / sizeof((x)[0])) @@ -53,6 +55,8 @@ static JSClassID js_VrDeviceInfo_class_id; static JSClassID js_VrStereoConfig_class_id; static JSClassID js_FilePathList_class_id; static JSClassID js_Light_class_id; +static JSClassID js_Lightmapper_class_id; +static JSClassID js_LightmapperConfig_class_id; static void js_Vector2_finalizer(JSRuntime * rt, JSValue val) { Vector2* ptr = JS_GetOpaque(val, js_Vector2_class_id); @@ -2095,6 +2099,192 @@ static int js_declare_Light(JSContext * ctx, JSModuleDef * m) { return 0; } +static void js_Lightmapper_finalizer(JSRuntime * rt, JSValue val) { + Lightmapper* ptr = JS_GetOpaque(val, js_Lightmapper_class_id); + if(ptr) { + js_free_rt(rt, ptr); + } +} + +static JSValue js_Lightmapper_get_w(JSContext* ctx, JSValueConst this_val) { + Lightmapper* ptr = JS_GetOpaque2(ctx, this_val, js_Lightmapper_class_id); + int w = ptr->w; + JSValue ret = JS_NewInt32(ctx, w); + return ret; +} + +static JSValue js_Lightmapper_get_h(JSContext* ctx, JSValueConst this_val) { + Lightmapper* ptr = JS_GetOpaque2(ctx, this_val, js_Lightmapper_class_id); + int h = ptr->h; + JSValue ret = JS_NewInt32(ctx, h); + return ret; +} + +static JSValue js_Lightmapper_get_progress(JSContext* ctx, JSValueConst this_val) { + Lightmapper* ptr = JS_GetOpaque2(ctx, this_val, js_Lightmapper_class_id); + float progress = ptr->progress; + JSValue ret = JS_NewFloat64(ctx, progress); + return ret; +} + +static const JSCFunctionListEntry js_Lightmapper_proto_funcs[] = { + JS_CGETSET_DEF("w",js_Lightmapper_get_w,NULL), + JS_CGETSET_DEF("h",js_Lightmapper_get_h,NULL), + JS_CGETSET_DEF("progress",js_Lightmapper_get_progress,NULL), + JS_PROP_STRING_DEF("[Symbol.toStringTag]","Lightmapper", JS_PROP_CONFIGURABLE), +}; + +static int js_declare_Lightmapper(JSContext * ctx, JSModuleDef * m) { + JS_NewClassID(&js_Lightmapper_class_id); + JSClassDef js_Lightmapper_def = { .class_name = "Lightmapper", .finalizer = js_Lightmapper_finalizer }; + JS_NewClass(JS_GetRuntime(ctx), js_Lightmapper_class_id, &js_Lightmapper_def); + JSValue proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_Lightmapper_proto_funcs, countof(js_Lightmapper_proto_funcs)); + JS_SetClassProto(ctx, js_Lightmapper_class_id, proto); + return 0; +} + +static void js_LightmapperConfig_finalizer(JSRuntime * rt, JSValue val) { + LightmapperConfig* ptr = JS_GetOpaque(val, js_LightmapperConfig_class_id); + if(ptr) { + js_free_rt(rt, ptr); + } +} + +static JSValue js_LightmapperConfig_get_hemisphereSize(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + int hemisphereSize = ptr->hemisphereSize; + JSValue ret = JS_NewInt32(ctx, hemisphereSize); + return ret; +} + +static JSValue js_LightmapperConfig_set_hemisphereSize(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + int value; + JS_ToInt32(ctx, &value, v); + ptr->hemisphereSize = value; + return JS_UNDEFINED; +} + +static JSValue js_LightmapperConfig_get_zNear(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + float zNear = ptr->zNear; + JSValue ret = JS_NewFloat64(ctx, zNear); + return ret; +} + +static JSValue js_LightmapperConfig_set_zNear(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + double _double_value; + JS_ToFloat64(ctx, &_double_value, v); + float value = (float)_double_value; + ptr->zNear = value; + return JS_UNDEFINED; +} + +static JSValue js_LightmapperConfig_get_zFar(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + float zFar = ptr->zFar; + JSValue ret = JS_NewFloat64(ctx, zFar); + return ret; +} + +static JSValue js_LightmapperConfig_set_zFar(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + double _double_value; + JS_ToFloat64(ctx, &_double_value, v); + float value = (float)_double_value; + ptr->zFar = value; + return JS_UNDEFINED; +} + +static JSValue js_LightmapperConfig_get_backgroundColor(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + Color backgroundColor = ptr->backgroundColor; + Color* ret_ptr = (Color*)js_malloc(ctx, sizeof(Color)); + *ret_ptr = backgroundColor; + JSValue ret = JS_NewObjectClass(ctx, js_Color_class_id); + JS_SetOpaque(ret, ret_ptr); + return ret; +} + +static JSValue js_LightmapperConfig_set_backgroundColor(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + Color* value_ptr = (Color*)JS_GetOpaque2(ctx, v, js_Color_class_id); + if(value_ptr == NULL) return JS_EXCEPTION; + Color value = *value_ptr; + ptr->backgroundColor = value; + return JS_UNDEFINED; +} + +static JSValue js_LightmapperConfig_get_interpolationPasses(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + int interpolationPasses = ptr->interpolationPasses; + JSValue ret = JS_NewInt32(ctx, interpolationPasses); + return ret; +} + +static JSValue js_LightmapperConfig_set_interpolationPasses(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + int value; + JS_ToInt32(ctx, &value, v); + ptr->interpolationPasses = value; + return JS_UNDEFINED; +} + +static JSValue js_LightmapperConfig_get_interpolationThreshold(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + float interpolationThreshold = ptr->interpolationThreshold; + JSValue ret = JS_NewFloat64(ctx, interpolationThreshold); + return ret; +} + +static JSValue js_LightmapperConfig_set_interpolationThreshold(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + double _double_value; + JS_ToFloat64(ctx, &_double_value, v); + float value = (float)_double_value; + ptr->interpolationThreshold = value; + return JS_UNDEFINED; +} + +static JSValue js_LightmapperConfig_get_cameraToSurfaceDistanceModifier(JSContext* ctx, JSValueConst this_val) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + float cameraToSurfaceDistanceModifier = ptr->cameraToSurfaceDistanceModifier; + JSValue ret = JS_NewFloat64(ctx, cameraToSurfaceDistanceModifier); + return ret; +} + +static JSValue js_LightmapperConfig_set_cameraToSurfaceDistanceModifier(JSContext* ctx, JSValueConst this_val, JSValueConst v) { + LightmapperConfig* ptr = JS_GetOpaque2(ctx, this_val, js_LightmapperConfig_class_id); + double _double_value; + JS_ToFloat64(ctx, &_double_value, v); + float value = (float)_double_value; + ptr->cameraToSurfaceDistanceModifier = value; + return JS_UNDEFINED; +} + +static const JSCFunctionListEntry js_LightmapperConfig_proto_funcs[] = { + JS_CGETSET_DEF("hemisphereSize",js_LightmapperConfig_get_hemisphereSize,js_LightmapperConfig_set_hemisphereSize), + JS_CGETSET_DEF("zNear",js_LightmapperConfig_get_zNear,js_LightmapperConfig_set_zNear), + JS_CGETSET_DEF("zFar",js_LightmapperConfig_get_zFar,js_LightmapperConfig_set_zFar), + JS_CGETSET_DEF("backgroundColor",js_LightmapperConfig_get_backgroundColor,js_LightmapperConfig_set_backgroundColor), + JS_CGETSET_DEF("interpolationPasses",js_LightmapperConfig_get_interpolationPasses,js_LightmapperConfig_set_interpolationPasses), + JS_CGETSET_DEF("interpolationThreshold",js_LightmapperConfig_get_interpolationThreshold,js_LightmapperConfig_set_interpolationThreshold), + JS_CGETSET_DEF("cameraToSurfaceDistanceModifier",js_LightmapperConfig_get_cameraToSurfaceDistanceModifier,js_LightmapperConfig_set_cameraToSurfaceDistanceModifier), + JS_PROP_STRING_DEF("[Symbol.toStringTag]","LightmapperConfig", JS_PROP_CONFIGURABLE), +}; + +static int js_declare_LightmapperConfig(JSContext * ctx, JSModuleDef * m) { + JS_NewClassID(&js_LightmapperConfig_class_id); + JSClassDef js_LightmapperConfig_def = { .class_name = "LightmapperConfig", .finalizer = js_LightmapperConfig_finalizer }; + JS_NewClass(JS_GetRuntime(ctx), js_LightmapperConfig_class_id, &js_LightmapperConfig_def); + JSValue proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_LightmapperConfig_proto_funcs, countof(js_LightmapperConfig_proto_funcs)); + JS_SetClassProto(ctx, js_LightmapperConfig_class_id, proto); + return 0; +} + static JSValue js_Vector2_constructor(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { double _double_x; JS_ToFloat64(ctx, &_double_x, argv[0]); @@ -10057,6 +10247,94 @@ static JSValue js_easeElasticIn(JSContext * ctx, JSValueConst this_val, int argc return ret; } +static JSValue js_getDefaultLightmapperConfig(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + LightmapperConfig returnVal = GetDefaultLightmapperConfig(); + LightmapperConfig* ret_ptr = (LightmapperConfig*)js_malloc(ctx, sizeof(LightmapperConfig)); + *ret_ptr = returnVal; + JSValue ret = JS_NewObjectClass(ctx, js_LightmapperConfig_class_id); + JS_SetOpaque(ret, ret_ptr); + return ret; +} + +static JSValue js_loadLightmapper(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + int w; + JS_ToInt32(ctx, &w, argv[0]); + int h; + JS_ToInt32(ctx, &h, argv[1]); + Mesh* mesh_ptr = (Mesh*)JS_GetOpaque2(ctx, argv[2], js_Mesh_class_id); + if(mesh_ptr == NULL) return JS_EXCEPTION; + Mesh mesh = *mesh_ptr; + LightmapperConfig* cfg_ptr = (LightmapperConfig*)JS_GetOpaque2(ctx, argv[3], js_LightmapperConfig_class_id); + if(cfg_ptr == NULL) return JS_EXCEPTION; + LightmapperConfig cfg = *cfg_ptr; + Lightmapper returnVal = LoadLightmapper(w, h, mesh, cfg); + Lightmapper* ret_ptr = (Lightmapper*)js_malloc(ctx, sizeof(Lightmapper)); + *ret_ptr = returnVal; + JSValue ret = JS_NewObjectClass(ctx, js_Lightmapper_class_id); + JS_SetOpaque(ret, ret_ptr); + return ret; +} + +static JSValue js_loadMaterialLightmapper(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Color* emissiveColor_ptr = (Color*)JS_GetOpaque2(ctx, argv[0], js_Color_class_id); + if(emissiveColor_ptr == NULL) return JS_EXCEPTION; + Color emissiveColor = *emissiveColor_ptr; + double _double_intensity; + JS_ToFloat64(ctx, &_double_intensity, argv[1]); + float intensity = (float)_double_intensity; + Material returnVal = LoadMaterialLightmapper(emissiveColor, intensity); + Material* ret_ptr = (Material*)js_malloc(ctx, sizeof(Material)); + *ret_ptr = returnVal; + JSValue ret = JS_NewObjectClass(ctx, js_Material_class_id); + JS_SetOpaque(ret, ret_ptr); + return ret; +} + +static JSValue js_unloadLightmapper(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Lightmapper* lm_ptr = (Lightmapper*)JS_GetOpaque2(ctx, argv[0], js_Lightmapper_class_id); + if(lm_ptr == NULL) return JS_EXCEPTION; + Lightmapper lm = *lm_ptr; + UnloadLightmapper(lm); + return JS_UNDEFINED; +} + +static JSValue js_beginLightmap(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + BeginLightmap(); + return JS_UNDEFINED; +} + +static JSValue js_endLightmap(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + EndLightmap(); + return JS_UNDEFINED; +} + +static JSValue js_beginLightmapFragment(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Lightmapper* lm = (Lightmapper*)JS_GetOpaque2(ctx, argv[0], js_Lightmapper_class_id); + if(lm == NULL) return JS_EXCEPTION; + bool returnVal = BeginLightmapFragment(lm); + JSValue ret = JS_NewBool(ctx, returnVal); + return ret; +} + +static JSValue js_endLightmapFragment(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Lightmapper* lm = (Lightmapper*)JS_GetOpaque2(ctx, argv[0], js_Lightmapper_class_id); + if(lm == NULL) return JS_EXCEPTION; + EndLightmapFragment(lm); + return JS_UNDEFINED; +} + +static JSValue js_loadImageFromLightmapper(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Lightmapper* lm_ptr = (Lightmapper*)JS_GetOpaque2(ctx, argv[0], js_Lightmapper_class_id); + if(lm_ptr == NULL) return JS_EXCEPTION; + Lightmapper lm = *lm_ptr; + Image returnVal = LoadImageFromLightmapper(lm); + Image* ret_ptr = (Image*)js_malloc(ctx, sizeof(Image)); + *ret_ptr = returnVal; + JSValue ret = JS_NewObjectClass(ctx, js_Image_class_id); + JS_SetOpaque(ret, ret_ptr); + return ret; +} + static JSValue js_setModelMaterial(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { Model* model = (Model*)JS_GetOpaque2(ctx, argv[0], js_Model_class_id); if(model == NULL) return JS_EXCEPTION; @@ -10095,6 +10373,19 @@ static JSValue js_imageReadPixel(JSContext * ctx, JSValueConst this_val, int arg return ret; } +static JSValue js_getModelMesh(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Model* model = (Model*)JS_GetOpaque2(ctx, argv[0], js_Model_class_id); + if(model == NULL) return JS_EXCEPTION; + int meshIndex; + JS_ToInt32(ctx, &meshIndex, argv[1]); + Mesh returnVal = GetModelMesh(model, meshIndex); + Mesh* ret_ptr = (Mesh*)js_malloc(ctx, sizeof(Mesh)); + *ret_ptr = returnVal; + JSValue ret = JS_NewObjectClass(ctx, js_Mesh_class_id); + JS_SetOpaque(ret, ret_ptr); + return ret; +} + static const JSCFunctionListEntry js_raylib_core_funcs[] = { JS_CFUNC_DEF("initWindow",3,js_initWindow), JS_CFUNC_DEF("windowShouldClose",0,js_windowShouldClose), @@ -10711,9 +11002,19 @@ static const JSCFunctionListEntry js_raylib_core_funcs[] = { JS_CFUNC_DEF("easeBounceOut",4,js_easeBounceOut), JS_CFUNC_DEF("easeBounceInOut",4,js_easeBounceInOut), JS_CFUNC_DEF("easeElasticIn",4,js_easeElasticIn), + JS_CFUNC_DEF("getDefaultLightmapperConfig",0,js_getDefaultLightmapperConfig), + JS_CFUNC_DEF("loadLightmapper",4,js_loadLightmapper), + JS_CFUNC_DEF("loadMaterialLightmapper",2,js_loadMaterialLightmapper), + JS_CFUNC_DEF("unloadLightmapper",1,js_unloadLightmapper), + JS_CFUNC_DEF("beginLightmap",0,js_beginLightmap), + JS_CFUNC_DEF("endLightmap",0,js_endLightmap), + JS_CFUNC_DEF("beginLightmapFragment",1,js_beginLightmapFragment), + JS_CFUNC_DEF("endLightmapFragment",1,js_endLightmapFragment), + JS_CFUNC_DEF("loadImageFromLightmapper",1,js_loadImageFromLightmapper), JS_CFUNC_DEF("setModelMaterial",3,js_setModelMaterial), JS_CFUNC_DEF("setShaderLocation",3,js_setShaderLocation), JS_CFUNC_DEF("imageReadPixel",3,js_imageReadPixel), + JS_CFUNC_DEF("getModelMesh",2,js_getModelMesh), }; static int js_raylib_core_init(JSContext * ctx, JSModuleDef * m) { @@ -10777,6 +11078,8 @@ static int js_raylib_core_init(JSContext * ctx, JSModuleDef * m) { js_declare_VrStereoConfig(ctx, m); js_declare_FilePathList(ctx, m); js_declare_Light(ctx, m); + js_declare_Lightmapper(ctx, m); + js_declare_LightmapperConfig(ctx, m); Color LIGHTGRAY_struct = { 200, 200, 200, 255 }; Color* LIGHTGRAY_js_ptr = (Color*)js_malloc(ctx, sizeof(Color)); *LIGHTGRAY_js_ptr = LIGHTGRAY_struct; diff --git a/src/lightmapper_example.c b/src/lightmapper_example.c deleted file mode 100644 index 8c070a3..0000000 --- a/src/lightmapper_example.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define RLIGHTMAPPER_IMPLEMENTATION -#include "rlightmapper.h" - -typedef struct -{ - //Shader raylib_shader; - Texture raylib_texture; - int w, h; - Model raylib_model; - Model model2; - Camera camera; - Shader shader; - GLuint u_intensity; -} scene_t; - -static void drawScene(scene_t *scene){ - DrawModel(scene->raylib_model, (Vector3){ 0,0,0 }, 1, WHITE); - //DrawModel(scene->model2, (Vector3){ -10,10,0.0 }, 3,RAYWHITE); -} - -int main(int argc, char* argv[]) -{ - SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI | FLAG_VSYNC_HINT); - - InitWindow(1024,768,"Test"); - - scene_t scene = {0}; - - scene.shader = LoadShader("assets/shaders/glsl330/default.vs","assets/shaders/glsl330/default.fs"); - scene.u_intensity = GetShaderLocation(scene.shader, "intensity"); - // load mesh - scene.raylib_model = LoadModel("monkey.obj"); - scene.raylib_model.materials[0].shader = scene.shader; - scene.model2 = LoadModel("thirdparty/lightmapper/example/cube.obj"); - scene.model2.materials[0].shader = scene.shader; - - scene.w = 512; - scene.h = 512; - scene.raylib_texture = LoadTextureFromImage(GenImageColor(1,1,BLACK)); - scene.raylib_model.materials[0].maps[0].texture = scene.raylib_texture; - - Camera camera = { 0 }; - camera.position = (Vector3){ 0.0f, 0.5f, 1.5f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.35f, 0.0f }; // Camera looking at point - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) - camera.fovy = 45.0f; // Camera field-of-view Y - camera.projection = CAMERA_PERSPECTIVE; // Camera mode type - scene.camera = camera; - - LightmapperConfig config = GetDefaultLightmapperConfig(); - config.backgroundColor = (Color){6,0,10}; - //config.hemisphereSize = 512; - Lightmapper lm = LoadLightmapper(scene.w, scene.h, scene.raylib_model.meshes[0], config); - Material lmMat = LoadMaterialLightmapper(BLACK, 0); - Mesh light = GenMeshCube(0.3,0.3, 0.3); - Material lightMaterial = LoadMaterialLightmapper(ORANGE, 1.0f); - - while (!WindowShouldClose()) - { - if(IsMouseButtonDown(MOUSE_BUTTON_LEFT)) - UpdateCamera(&scene.camera, CAMERA_THIRD_PERSON); - - if(lm.progress < 1.0f){ - double startTime = GetTime(); - BeginLightmap(); - while(BeginLightmapFragment(&lm)){ - DrawMesh(scene.raylib_model.meshes[0], lmMat, MatrixIdentity()); - DrawMesh(light, lightMaterial, MatrixTranslate(0,1.0,0)); - EndLightmapFragment(&lm); - // display progress every second (printf is expensive) - double time = GetTime(); - if (GetTime() - startTime > 0.03) break; - } - EndLightmap(); - if(lm.progress == 1.0f){ - Image img = LoadImageFromLightmapper(lm); - //ExportImage(img, "my_result.png"); - UnloadTexture(scene.raylib_texture); - scene.raylib_texture = LoadTextureFromImage(img); - scene.raylib_model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = scene.raylib_texture; - UnloadLightmapper(lm); - } - } - - BeginDrawing(); - ClearBackground(BLUE); - - BeginMode3D(scene.camera); - float intensity = 1.0f; - SetShaderValue(scene.shader, scene.u_intensity, &intensity, SHADER_UNIFORM_FLOAT); - drawScene(&scene); - EndMode3D(); - - // printf("%d\n",(int)(lm.progress*GetScreenWidth())); - if(lm.progress < 1.0f){ - DrawRectangle(0,0,GetScreenWidth(),20, Fade(GREEN,0.5)); - DrawRectangle(0,0,GetScreenWidth()*lm.progress,20, GREEN); - } - EndDrawing(); - } - - UnloadModel(scene.raylib_model); - UnloadTexture(scene.raylib_texture); - CloseWindow(); - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/src/lightmapper_example.c.bakk b/src/lightmapper_example.c.bakk deleted file mode 100644 index 1a61a28..0000000 --- a/src/lightmapper_example.c.bakk +++ /dev/null @@ -1,192 +0,0 @@ -#include -#include "raylib.h" -#include "rlgl.h" -#define GLAD_MALLOC(sz) malloc(sz) -#define GLAD_FREE(sz) free(sz) -//#define GLAD_GL_IMPLEMENTATION -#include "../thirdparty/raylib/src/external/glad.h" - -#define LIGHTMAPPER_IMPLEMENTATION -#define LM_DEBUG_INTERPOLATION -#include "lightmapper.h" - -// 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" -}; - -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - InitWindow(screenWidth, screenHeight, "raylib [models] lightmapping"); - - - - Model model = LoadModel("thirdparty/lightmapper/example/gazebo.obj"); - Shader shader = LoadShaderFromMemory(vp, fp); - Texture texture = LoadTextureFromImage(GenImageColor(1,1,BLACK)); - int u_view = GetShaderLocation(shader, "u_view"); - int u_projection = GetShaderLocation(shader, "u_projection"); - int u_lightmap = GetShaderLocation(shader, "u_lightmap"); - - - Vector3 position = { 0.0f, 0.0f, 0.0f }; - - //-------------------------------------------------------------------------------------- - - 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) TraceLog(LOG_ERROR, "Lightmapper not initialized"); - - int w = 512; - int h = 512; - float *data = calloc(w*h*4,sizeof(float)); - - lmSetTargetLightmap(ctx, data, w, h, 4); - - Mesh m = model.meshes[0]; - - lmSetGeometry(ctx, NULL, // no transformation in this example - LM_FLOAT, m.vertices, 0, - LM_NONE , NULL, 0, - LM_FLOAT, m.texcoords, 0, - m.vertexCount, LM_NONE, 0); - - printf("%p\n", m.texcoords); - - int vp[4]; - Matrix view, projection; - double lastUpdateTime = 0.0; - - Mesh mesh = model.meshes[0]; - //SetTargetFPS(10000); - while (lmBegin(ctx, vp, (float*)&view, (float*)&projection)) - { - glViewport(vp[0], vp[1], vp[2], vp[3]); - glEnable(GL_DEPTH_TEST); - - glUseProgram(shader.id); - glUniform1i(u_lightmap, 0); - glUniformMatrix4fv(u_projection, 1, GL_FALSE, (float *)&projection); - glUniformMatrix4fv(u_view, 1, GL_FALSE, (float *)&view); - - glBindTexture(GL_TEXTURE_2D, texture.id); - - glBindVertexArray(mesh.vaoId); - glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount); - - double time = GetTime(); - if (time - lastUpdateTime > 1.0) - { - lastUpdateTime = time; - printf("\r%6.2f%%", lmProgress(ctx) * 100.0f); - fflush(stdout); - } - - lmEnd(ctx); - if(WindowShouldClose()) break; - - } - - // 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"); - - rlViewport(0,0, screenWidth, screenHeight); - - -// Define the camera to look into our 3d world - Camera camera = { 0 }; - camera.position = (Vector3){ 1.0f, 0.5f, 1.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) - camera.fovy = 45.0f; // Camera field-of-view Y - camera.projection = CAMERA_PERSPECTIVE; // Camera mode type - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - //UpdateCamera(&camera, CAMERA_FIRST_PERSON); - - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - BeginDrawing(); - - ClearBackground(RAYWHITE); - - BeginMode3D(camera); - - DrawModel(model, position, 1.0f, WHITE); - - EndMode3D(); - - EndDrawing(); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - //UnloadTexture(texture); // Unload texture - UnloadModel(model); // Unload model - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} \ No newline at end of file diff --git a/src/quickjs.c b/src/quickjs.c index 825db8e..055c474 100644 --- a/src/quickjs.c +++ b/src/quickjs.c @@ -4,6 +4,7 @@ #include #include //#include +#include #include #include @@ -212,6 +213,12 @@ void SetModelMaterial(Model *model, int materialIndex, Material material) model->materials[materialIndex] = material; } +Mesh GetModelMesh(Model *model, int meshIndex){ + Mesh m = { 0 }; + if(model->meshCount <= meshIndex) return m; + return model->meshes[meshIndex]; +} + void SetShaderLocation(Shader *shader, int constant, int location){ shader->locs[constant] = location; } diff --git a/src/rlightmapper.h b/src/rlightmapper.h index 13caad2..90e857f 100644 --- a/src/rlightmapper.h +++ b/src/rlightmapper.h @@ -1,233 +1,267 @@ -#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; -} +#ifndef RLIGHTMAPPER_H +#define RLIGHTMAPPER_H + +// ... +typedef struct { + void *lm_handle; + float *data; + int w; + int h; + float progress; +} Lightmapper; + +// ... +typedef struct { + int hemisphereSize; + float zNear; + float zFar; + Color backgroundColor; + int interpolationPasses; + float interpolationThreshold; + float cameraToSurfaceDistanceModifier; +} LightmapperConfig; + +#define RLMAPI + +// ... +RLMAPI LightmapperConfig GetDefaultLightmapperConfig(void); +// ... +RLMAPI Lightmapper LoadLightmapper(int w, int h, Mesh mesh, LightmapperConfig cfg); +// ... +RLMAPI Material LoadMaterialLightmapper(Color emissiveColor, float intensity); +// ... +RLMAPI void UnloadLightmapper(Lightmapper lm); +// ... +RLMAPI void BeginLightmap(void); +// ... +RLMAPI void EndLightmap(void); +// .. +RLMAPI bool BeginLightmapFragment(Lightmapper *lm); +// ... +RLMAPI void EndLightmapFragment(Lightmapper *lm); +// ... +RLMAPI Image LoadImageFromLightmapper(Lightmapper lm); + +#endif + +#if defined(RLIGHTMAPPER_IMPLEMENTATION) + +#include + +#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 sampler2D texture1;\n" + "uniform vec4 colDiffuse;\n" + "void main()\n" + "{\n" + " vec4 texelColor = texture(texture0, fragTexCoord);\n" + " vec4 emissionColor = texture(texture1, fragTexCoord);\n" + " texelColor = texelColor * colDiffuse * fragColor * emissionColor;\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; + float colF[4] = {emissiveColor.r * emissiveIntensity, emissiveColor.g * emissiveIntensity, emissiveColor.b * emissiveIntensity, emissiveColor.a * emissiveIntensity}; + Texture tex = {0}; + tex.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; + tex.width = 1; + tex.height = 1; + tex.mipmaps = 1; + tex.id = rlLoadTexture(colF, 1, 1, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, 1); + mat.maps[MATERIAL_MAP_SPECULAR].texture = tex; + // 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 \ No newline at end of file