diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 4532ab759fdb..c46a7b5073fe 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -524,6 +524,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 8d2d0b4eb8f3..217a0965ae37 100644 --- a/SConstruct +++ b/SConstruct @@ -238,6 +238,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)) @@ -1004,6 +1005,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 47fc48305b5f..fc657ab54e09 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -425,6 +425,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 ea0db2dda454..b2daf37e2130 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -5307,6 +5307,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 8c05dff79a14..ae8158d79884 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2771,6 +2771,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 043023aee0d7..528026c57899 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -34,6 +34,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 6738bdf748cb..148e8e066320 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)); @@ -84,10 +86,21 @@ vec3 tonemap_reinhard(vec3 color, float p_white) { return (white_squared_color + color * color) / (white_squared_color + white_squared); } +// 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 // Ensure color values passed to tonemappers are positive. @@ -98,8 +111,18 @@ vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR 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 b5e23e983216..81c505e3fd56 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1120,7 +1120,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; } @@ -1274,8 +1274,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"); @@ -1580,6 +1586,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 83cf2ed7195d..6b34d38d4d94 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 23685473fea0..e8f4f3325e80 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -617,11 +617,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; @@ -711,10 +719,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 fa3b45a96274..37cb104f9d02 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) @@ -264,6 +266,16 @@ vec3 tonemap_reinhard(vec3 color, float white) { return (white_squared_color + color * color) / (white_squared_color + white_squared); } +// 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)); @@ -275,6 +287,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 // Ensure color values passed to tonemappers are positive. @@ -285,8 +298,12 @@ vec3 apply_tonemapping(vec3 color, float white) { // inputs are LINEAR 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 07259f73d201..0e9b934d4cd5 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -377,6 +377,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 99418e0411a9..1489ba54571f 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -140,6 +140,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 98f918b2458c..03147ee032b0 100644 --- a/servers/rendering/storage/SCsub +++ b/servers/rendering/storage/SCsub @@ -3,4 +3,12 @@ from misc.utility.scons_hints import * 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 bfb2852d5f0d..fb1b8019723e 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); } @@ -209,6 +220,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 { @@ -229,6 +273,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 6fdc047ba288..0bb1d10ae006 100644 --- a/servers/rendering/storage/environment_storage.h +++ b/servers/rendering/storage/environment_storage.h @@ -63,6 +63,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; @@ -204,6 +205,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 93e278c3c592..1eeec36cdf76 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3065,6 +3065,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 099c332de23b..b9933c002672 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1222,7 +1222,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