diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 5b6dcbb567bd..cd69eb3e461c 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -502,6 +502,11 @@ Copyright: 2014-2021, Syoyo Fujita 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC License: BSD-3-clause +Files: ./thirdparty/tony-mc-mapface/ +Comment: Tony McMapface +Copyright: 2023, Tomasz Stachowiak +License: Expat + Files: ./thirdparty/ufbx/ Comment: ufbx Copyright: 2020, Samuli Raivio diff --git a/SConstruct b/SConstruct index 0245531b45e2..5ecb3b3e7431 100644 --- a/SConstruct +++ b/SConstruct @@ -257,6 +257,7 @@ opts.Add("vsproj_name", "Name of the Visual Studio solution", "godot") opts.Add("import_env_vars", "A comma-separated list of environment variables to copy from the outer environment.", "") opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False)) opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False)) +opts.Add(BoolVariable("disable_tony_mc_mapface", "Disable Tony McMapface tonemapping mode (decreases binary size by ~300 KB)", False)) opts.Add("build_profile", "Path to a file containing a feature build profile", "") opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True)) opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True)) @@ -1007,6 +1008,8 @@ if env["disable_advanced_gui"]: Exit(255) else: env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"]) +if env["disable_tony_mc_mapface"]: + env.Append(CPPDEFINES=["TONY_MC_MAPFACE_DISABLED"]) if env["minizip"]: env.Append(CPPDEFINES=["MINIZIP_ENABLED"]) if env["brotli"]: diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 8c265098126b..a18e3a9269a8 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -423,6 +423,9 @@ Use the Academy Color Encoding System tonemapper. ACES is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. ACES typically has a more contrasted output compared to [constant TONE_MAPPER_REINHARDT] and [constant TONE_MAPPER_FILMIC]. [b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x. + + Tony McMapface is an artist-friendly tonemapper designed to stay close to the input color in order to achieve a neutral look that does not increase contrast or saturation. It can prevent hue shift issues common with other tonemappers. It samples its own custom lookup table (LUT) to determine the output color. + Additive glow blending mode. Mostly used for particles, glows (bloom), lens flare, bright sources. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index cea9a4cec43b..bb2ea5da5fe0 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -5204,6 +5204,9 @@ Use the Academy Color Encoding System tonemapper. ACES is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. ACES typically has a more contrasted output compared to [constant ENV_TONE_MAPPER_REINHARD] and [constant ENV_TONE_MAPPER_FILMIC]. [b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x. + + Tony McMapface is an artist-friendly tonemapper designed to stay close to the input color in order to achieve a neutral look that does not increase contrast or saturation. It can prevent hue shift issues common with other tonemappers. It samples its own custom lookup table (LUT) to determine the output color. + Lowest quality of roughness filter for screen-space reflections. Rough materials will not have blurrier screen-space reflections compared to smooth (non-rough) materials. This is the fastest option. diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 8b6d3e3268e1..2a9f78aebb78 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2752,6 +2752,19 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glTexParameteri(texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } } + + if (environment_get_tone_mapper(p_render_data->environment) == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) { + RID tony_mc_mapface_lut = environment_get_tony_mc_mapface_lut(p_render_data->environment); + if (tony_mc_mapface_lut.is_valid()) { + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_3D, texture_storage->texture_get_texid(tony_mc_mapface_lut)); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + } } if (view_count == 1) { diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index f734e4b35544..34b65184930b 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -36,6 +36,7 @@ void main() { #define M_PI 3.14159265359 +#define SKY_SHADER #include "tonemap_inc.glsl" in vec2 uv_interp; diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl index fb915aeb3830..a90bd64a9b63 100644 --- a/drivers/gles3/shaders/tonemap_inc.glsl +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -10,6 +10,8 @@ layout(std140) uniform TonemapData { //ubo:0 float saturation; }; +uniform sampler3D tony_mc_mapface_lut; //texunit:3 + // This expects 0-1 range input. vec3 linear_to_srgb(vec3 color) { //color = clamp(color, vec3(0.0), vec3(1.0)); @@ -80,10 +82,21 @@ vec3 tonemap_reinhard(vec3 color, float p_white) { return (p_white * color + color) / (color * p_white + p_white); } +// https://github.com/h3r2tic/tony-mc-mapface/blob/main/shader/tony_mc_mapface.hlsl +vec3 tonemap_tony_mc_mapface(vec3 stimulus) { + vec3 encoded = stimulus / (stimulus + 1.0f); + + const float LUT_DIMS = 48.0f; + vec3 uv = encoded * ((LUT_DIMS - 1.0f) / LUT_DIMS) + 0.5f / LUT_DIMS; + + return texture(tony_mc_mapface_lut, uv).rgb; +} + #define TONEMAPPER_LINEAR 0 #define TONEMAPPER_REINHARD 1 #define TONEMAPPER_FILMIC 2 #define TONEMAPPER_ACES 3 +#define TONEMAPPER_TONY_MC_MAPFACE 4 vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color // Ensure color values passed to tonemappers are positive. @@ -94,8 +107,18 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always return tonemap_reinhard(max(vec3(0.0f), color), p_white); } else if (tonemapper == TONEMAPPER_FILMIC) { return tonemap_filmic(max(vec3(0.0f), color), p_white); - } else { // TONEMAPPER_ACES + } else if (tonemapper == TONEMAPPER_ACES) { return tonemap_aces(max(vec3(0.0f), color), p_white); + } else if (tonemapper == TONEMAPPER_TONY_MC_MAPFACE) { +#ifdef SKY_SHADER + // Sampling the Tony McMapface LUT in the sky shader leads to pitch black shadows if the "Sky" background + // mode is enabled for the environment. Avoid this by returning the color as is. + return color; +#else + return tonemap_tony_mc_mapface(max(vec3(0.0f), color)); +#endif + } else { + return color; } } diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index f8c70c3002aa..e43b9b82c5c5 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1123,7 +1123,7 @@ void Environment::_validate_property(PropertyInfo &p_property) const { } } - if (p_property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) { + if (p_property.name == "tonemap_white" && (tone_mapper == TONE_MAPPER_LINEAR || tone_mapper == TONE_MAPPER_TONY_MC_MAPFACE)) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } @@ -1277,8 +1277,14 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tonemap_white", "white"), &Environment::set_tonemap_white); ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white); +#ifdef TONY_MC_MAPFACE_DISABLED + const char *tonemap_modes = "Linear,Reinhard,Filmic,ACES"; +#else + const char *tonemap_modes = "Linear,Reinhard,Filmic,ACES,Tony McMapface"; +#endif + ADD_GROUP("Tonemap", "tonemap_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES"), "set_tonemapper", "get_tonemapper"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, tonemap_modes), "set_tonemapper", "get_tonemapper"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white"); @@ -1583,6 +1589,7 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(TONE_MAPPER_REINHARDT); BIND_ENUM_CONSTANT(TONE_MAPPER_FILMIC); BIND_ENUM_CONSTANT(TONE_MAPPER_ACES); + BIND_ENUM_CONSTANT(TONE_MAPPER_TONY_MC_MAPFACE); BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_ADDITIVE); BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_SCREEN); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 68b49f38d746..55abce491639 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -67,6 +67,7 @@ class Environment : public Resource { TONE_MAPPER_REINHARDT, TONE_MAPPER_FILMIC, TONE_MAPPER_ACES, + TONE_MAPPER_TONY_MC_MAPFACE, }; enum SDFGIYScale { diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index e943071f0e47..937ef17adc01 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -163,6 +163,12 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton u_color_correction_texture.append_id(default_sampler); u_color_correction_texture.append_id(p_settings.color_correction_texture); + RD::Uniform u_tony_mc_mapface_lut; + u_tony_mc_mapface_lut.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u_tony_mc_mapface_lut.binding = 0; + u_tony_mc_mapface_lut.append_id(default_sampler); + u_tony_mc_mapface_lut.append_id(p_settings.tony_mc_mapface_lut); + RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -172,6 +178,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 4, u_tony_mc_mapface_lut), 4); RD::get_singleton()->draw_list_set_push_constant(draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); @@ -245,6 +252,12 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col u_color_correction_texture.append_id(default_sampler); u_color_correction_texture.append_id(p_settings.color_correction_texture); + RD::Uniform u_tony_mc_mapface_lut; + u_tony_mc_mapface_lut.uniform_type = RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE; + u_tony_mc_mapface_lut.binding = 0; + u_tony_mc_mapface_lut.append_id(default_sampler); + u_tony_mc_mapface_lut.append_id(p_settings.tony_mc_mapface_lut); + RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -253,6 +266,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); // should be set to a default texture, it's ignored RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 2, u_glow_texture, u_glow_map), 2); // should be set to a default texture, it's ignored RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 3, u_color_correction_texture), 3); + RD::get_singleton()->draw_list_bind_uniform_set(p_subpass_draw_list, uniform_set_cache->get_cache(shader, 4, u_tony_mc_mapface_lut), 4); RD::get_singleton()->draw_list_set_push_constant(p_subpass_draw_list, &tonemap.push_constant, sizeof(TonemapPushConstant)); RD::get_singleton()->draw_list_draw(p_subpass_draw_list, false, 1u, 3u); diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h index a1a99f931fc3..897e2e26dc60 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.h +++ b/servers/rendering/renderer_rd/effects/tone_mapper.h @@ -127,6 +127,7 @@ class ToneMapper { RS::EnvironmentToneMapper tonemap_mode = RS::ENV_TONE_MAPPER_LINEAR; float exposure = 1.0; float white = 1.0; + RID tony_mc_mapface_lut; bool use_auto_exposure = false; float auto_exposure_scale = 0.5; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 7d6d5018d07f..08ffbc0efb4e 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -612,11 +612,19 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende tonemap.use_debanding = rb->get_use_debanding(); tonemap.texture_size = Vector2i(color_size.x, color_size.y); + tonemap.tony_mc_mapface_lut = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_BLACK); if (p_render_data->environment.is_valid()) { tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment); tonemap.white = environment_get_white(p_render_data->environment); tonemap.exposure = environment_get_exposure(p_render_data->environment); + + if (tonemap.tonemap_mode == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) { + RID tony_mc_mapface_lut = environment_get_tony_mc_mapface_lut(p_render_data->environment); + if (tony_mc_mapface_lut.is_valid()) { + tonemap.tony_mc_mapface_lut = texture_storage->texture_get_rd_texture(tony_mc_mapface_lut); + } + } } tonemap.use_color_correction = false; @@ -706,10 +714,19 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr RendererRD::ToneMapper::TonemapSettings tonemap; + tonemap.tony_mc_mapface_lut = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_BLACK); + if (p_render_data->environment.is_valid()) { tonemap.tonemap_mode = environment_get_tone_mapper(p_render_data->environment); tonemap.exposure = environment_get_exposure(p_render_data->environment); tonemap.white = environment_get_white(p_render_data->environment); + + if (tonemap.tonemap_mode == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) { + RID tony_mc_mapface_lut = environment_get_tony_mc_mapface_lut(p_render_data->environment); + if (tony_mc_mapface_lut.is_valid()) { + tonemap.tony_mc_mapface_lut = texture_storage->texture_get_rd_texture(tony_mc_mapface_lut); + } + } } // We don't support glow or auto exposure here, if they are needed, don't use subpasses! diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index 841f02f673ea..aa51bf81d1a7 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -70,6 +70,8 @@ layout(set = 3, binding = 0) uniform sampler2D source_color_correction; layout(set = 3, binding = 0) uniform sampler3D source_color_correction; #endif +layout(set = 4, binding = 0) uniform sampler3D tony_mc_mapface_lut; + #define FLAG_USE_BCS (1 << 0) #define FLAG_USE_GLOW (1 << 1) #define FLAG_USE_AUTO_EXPOSURE (1 << 2) @@ -260,6 +262,16 @@ vec3 tonemap_reinhard(vec3 color, float white) { return (white * color + color) / (color * white + white); } +// https://github.com/h3r2tic/tony-mc-mapface/blob/main/shader/tony_mc_mapface.hlsl +vec3 tonemap_tony_mc_mapface(vec3 stimulus) { + const vec3 encoded = stimulus / (stimulus + 1.0f); + + const float LUT_DIMS = 48.0f; + const vec3 uv = encoded * ((LUT_DIMS - 1.0f) / LUT_DIMS) + 0.5f / LUT_DIMS; + + return texture(tony_mc_mapface_lut, uv).rgb; +} + vec3 linear_to_srgb(vec3 color) { //if going to srgb, clamp from 0 to 1. color = clamp(color, vec3(0.0), vec3(1.0)); @@ -271,6 +283,7 @@ vec3 linear_to_srgb(vec3 color) { #define TONEMAPPER_REINHARD 1 #define TONEMAPPER_FILMIC 2 #define TONEMAPPER_ACES 3 +#define TONEMAPPER_TONY_MC_MAPFACE 4 vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always outputs clamped [0;1] color // Ensure color values passed to tonemappers are positive. @@ -281,8 +294,12 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR, always o return tonemap_reinhard(max(vec3(0.0f), color), white); } else if (params.tonemapper == TONEMAPPER_FILMIC) { return tonemap_filmic(max(vec3(0.0f), color), white); - } else { // TONEMAPPER_ACES + } else if (params.tonemapper == TONEMAPPER_ACES) { return tonemap_aces(max(vec3(0.0f), color), white); + } else if (params.tonemapper == TONEMAPPER_TONY_MC_MAPFACE) { + return tonemap_tony_mc_mapface(max(vec3(0.0f), color)); + } else { + return color; } } diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index 76c779900fba..300c7307a5c4 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -366,6 +366,10 @@ float RendererSceneRender::environment_get_white(RID p_env) const { return environment_storage.environment_get_white(p_env); } +RID RendererSceneRender::environment_get_tony_mc_mapface_lut(RID p_env) const { + return environment_storage.environment_get_tony_mc_mapface_lut(p_env); +} + // Fog void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) { diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 3446f5dd1bbe..ad136859eb5e 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -137,6 +137,7 @@ class RendererSceneRender { RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const; float environment_get_exposure(RID p_env) const; float environment_get_white(RID p_env) const; + RID environment_get_tony_mc_mapface_lut(RID p_env) const; // Fog void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode); diff --git a/servers/rendering/storage/SCsub b/servers/rendering/storage/SCsub index 86681f9c74d3..b24b9b4ef892 100644 --- a/servers/rendering/storage/SCsub +++ b/servers/rendering/storage/SCsub @@ -2,4 +2,12 @@ Import("env") +import make_tony_mc_mapface_lut + env.add_source_files(env.servers_sources, "*.cpp") + +env.CommandNoCache( + "tony_mc_mapface_lut.gen.h", + ["make_tony_mc_mapface_lut.py", "#thirdparty/tony-mc-mapface/tony_mc_mapface.dds"], + env.Run(make_tony_mc_mapface_lut.run) +) diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 1bbb5da6bb91..01e030c3ffe8 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -30,6 +30,10 @@ #include "environment_storage.h" +#ifndef TONY_MC_MAPFACE_DISABLED +#include "tony_mc_mapface_lut.gen.h" +#endif + // Storage RendererEnvironmentStorage *RendererEnvironmentStorage::singleton = nullptr; @@ -53,6 +57,13 @@ void RendererEnvironmentStorage::environment_initialize(RID p_rid) { } void RendererEnvironmentStorage::environment_free(RID p_rid) { + Environment *env = environment_owner.get_or_null(p_rid); + ERR_FAIL_NULL(env); + + if (env->tony_mc_mapface_lut.is_valid()) { + RS::get_singleton()->free(env->tony_mc_mapface_lut); + } + environment_owner.free(p_rid); } @@ -197,6 +208,39 @@ void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::Environm env->exposure = p_exposure; env->tone_mapper = p_tone_mapper; env->white = p_white; + +#ifdef TONY_MC_MAPFACE_DISABLED + if (env->tone_mapper == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE) { + env->tone_mapper = RS::EnvironmentToneMapper::ENV_TONE_MAPPER_LINEAR; + } +#else + if (env->tone_mapper == RS::EnvironmentToneMapper::ENV_TONE_MAPPER_TONY_MC_MAPFACE && env->tony_mc_mapface_lut.is_null()) { + Vector decompressed_lut; + int compressed_size = TONY_MC_MAPFACE_LUT_COMPRESSED_SIZE; + int decompressed_size = TONY_MC_MAPFACE_LUT_DECOMPRESSED_SIZE; + + // The Tony McMapface LUT is embedded in the "tony_mc_mapface_lut.gen.h" header file. + // It is DEFLATE compressed to decrease binary size, so it first needs to be decompressed. + decompressed_lut.resize(decompressed_size); + Compression::decompress(decompressed_lut.ptrw(), decompressed_size, TONY_MC_MAPFACE_LUT, compressed_size, Compression::Mode::MODE_DEFLATE); + + Vector> images; + int dimensions = TONY_MC_MAPFACE_LUT_DIMENSIONS; + int image_bytes = 4 * dimensions * dimensions; + size_t image_size = image_bytes * sizeof(uint8_t); + + // Copy the RGBE9995 pixel data into a vector of images so that a 3d texture can be created. + for (int i = 0; i < dimensions; i++) { + Vector data; + data.resize(image_size); + + memcpy(data.ptrw(), decompressed_lut.ptr() + i * image_bytes, image_size); + images.push_back(Image::create_from_data(dimensions, dimensions, false, Image::FORMAT_RGBE9995, data)); + } + + env->tony_mc_mapface_lut = RS::get_singleton()->texture_3d_create(Image::FORMAT_RGBE9995, dimensions, dimensions, dimensions, false, images); + } +#endif // TONY_MC_MAPFACE_DISABLED } RS::EnvironmentToneMapper RendererEnvironmentStorage::environment_get_tone_mapper(RID p_env) const { @@ -217,6 +261,12 @@ float RendererEnvironmentStorage::environment_get_white(RID p_env) const { return env->white; } +RID RendererEnvironmentStorage::environment_get_tony_mc_mapface_lut(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_NULL_V(env, RID()); + return env->tony_mc_mapface_lut; +} + // Fog void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) { diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h index 9f78808ff757..f1ce0f719fdd 100644 --- a/servers/rendering/storage/environment_storage.h +++ b/servers/rendering/storage/environment_storage.h @@ -62,6 +62,7 @@ class RendererEnvironmentStorage { RS::EnvironmentToneMapper tone_mapper; float exposure = 1.0; float white = 1.0; + RID tony_mc_mapface_lut; // Fog bool fog_enabled = false; @@ -205,6 +206,7 @@ class RendererEnvironmentStorage { RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const; float environment_get_exposure(RID p_env) const; float environment_get_white(RID p_env) const; + RID environment_get_tony_mc_mapface_lut(RID p_env) const; // Fog void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode); diff --git a/servers/rendering/storage/make_tony_mc_mapface_lut.py b/servers/rendering/storage/make_tony_mc_mapface_lut.py new file mode 100644 index 000000000000..3bc1b85e9ecf --- /dev/null +++ b/servers/rendering/storage/make_tony_mc_mapface_lut.py @@ -0,0 +1,29 @@ +import zlib + + +def run(target, source, env): + with open(str(target[0]), "w", encoding="utf-8", newline="\n") as file: + file.write('// This file is generated at build time by the "{}" script.\n\n'.format(source[0].name)) + + file.write("#ifndef TONY_MC_MAPFACE_LUT_H\n") + file.write("#define TONY_MC_MAPFACE_LUT_H\n\n") + + file.write("static const int TONY_MC_MAPFACE_LUT_DIMENSIONS = 48; // 48x48x48\n") + file.write("static const int TONY_MC_MAPFACE_LUT_DECOMPRESSED_SIZE = 48 * 48 * 48 * 4 * sizeof(uint8_t);\n") + file.write("static const int TONY_MC_MAPFACE_LUT_COMPRESSED_SIZE = 313165 * sizeof(uint8_t);\n\n") + + file.write("// Tony McMapface LUT by Tomasz Stachowiak (https://github.com/h3r2tic/tony-mc-mapface)\n") + file.write("// RGBE5999 (E5B9G9R9_UFLOAT_PACK32), DEFLATE compressed\n") + + file.write("static const uint8_t TONY_MC_MAPFACE_LUT[] = {") + with open(source[1].path, mode="rb") as binary: + buffer = binary.read() + buffer = buffer[148:] # skip .dds header + buffer = zlib.compress(buffer, zlib.Z_BEST_COMPRESSION) + + file.write("0x{:02x}".format(buffer[0])) + for byte in range(1, len(buffer)): + file.write(",0x{:02x}".format(buffer[byte])) + file.write("};\n\n") + + file.write("#endif // TONY_MC_MAPFACE_LUT_H\n") diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index f354e83893b2..e3f54fe1271d 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3036,6 +3036,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_REINHARD); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_FILMIC); BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_ACES); + BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_TONY_MC_MAPFACE); BIND_ENUM_CONSTANT(ENV_SSR_ROUGHNESS_QUALITY_DISABLED); BIND_ENUM_CONSTANT(ENV_SSR_ROUGHNESS_QUALITY_LOW); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 62ca6b3b6dc6..7b262f32d331 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1173,7 +1173,8 @@ class RenderingServer : public Object { ENV_TONE_MAPPER_LINEAR, ENV_TONE_MAPPER_REINHARD, ENV_TONE_MAPPER_FILMIC, - ENV_TONE_MAPPER_ACES + ENV_TONE_MAPPER_ACES, + ENV_TONE_MAPPER_TONY_MC_MAPFACE, }; virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0; diff --git a/thirdparty/tony-mc-mapface/LICENSE b/thirdparty/tony-mc-mapface/LICENSE new file mode 100644 index 000000000000..473a6ba2f57f --- /dev/null +++ b/thirdparty/tony-mc-mapface/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2023 Tomasz Stachowiak + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/tony-mc-mapface/tony_mc_mapface.dds b/thirdparty/tony-mc-mapface/tony_mc_mapface.dds new file mode 100644 index 000000000000..8d84cd998a35 Binary files /dev/null and b/thirdparty/tony-mc-mapface/tony_mc_mapface.dds differ