Skip to content

Commit b03c4b0

Browse files
committed
Add support for change sky light color in night and day.
1 parent 22ef4c8 commit b03c4b0

File tree

14 files changed

+278
-116
lines changed

14 files changed

+278
-116
lines changed

builtin/game/features.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ core.features = {
4545
hotbar_hud_element = true,
4646
bulk_lbms = true,
4747
abm_without_neighbors = true,
48+
sky_light = true,
4849
}
4950

5051
function core.has_feature(arg)

doc/lua_api.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5527,6 +5527,8 @@ Utilities
55275527
bulk_lbms = true,
55285528
-- ABM supports field without_neighbors (5.10.0)
55295529
abm_without_neighbors = true,
5530+
-- set_lighting support sky_light table (5.10.0)
5531+
sky_light = true,
55305532
}
55315533
```
55325534

@@ -8605,6 +8607,18 @@ child will follow movement and rotation of that bone.
86058607
* `volumetric_light`: is a table that controls volumetric light (a.k.a. "godrays")
86068608
* `strength`: sets the strength of the volumetric light effect from 0 (off, default) to 1 (strongest)
86078609
* This value has no effect on clients who have the "Volumetric Lighting" or "Bloom" shaders disabled.
8610+
* `sky_light` is a table that controls calculation of sun light color.
8611+
`sun_color = color_offset + color_ratio_coef*daynight_ratio` where `daynight_ratio` is not linear to day time.
8612+
Result color lesser or equal to 0.0 means no color in light.
8613+
Result color greater or equal to 1.0 means full color in light.
8614+
* `color_offset` is a table that controls red, green and blue color offsets.
8615+
* `r` (default: `-0.04`)
8616+
* `g` (default: `-0.04`)
8617+
* `b` (default: `0.078`)
8618+
* `color_ratio_coef` is a table that controls red, green and blue color ration coefficients.
8619+
* `r` (default: `0.001`)
8620+
* `g` (default: `0.001`)
8621+
* `b` (default: `0.00098`)
86088622

86098623
* `get_lighting()`: returns the current state of lighting for the player.
86108624
* Result is a table with the same fields as `light_definition` in `set_lighting`.
Lines changed: 135 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,15 @@
1-
local lighting_sections = {
2-
{n = "shadows", d = "Shadows",
3-
entries = {
4-
{ n = "intensity", d = "Shadow Intensity", min = 0, max = 1 }
5-
}
6-
},
7-
{
8-
n = "exposure", d = "Exposure",
9-
entries = {
10-
{n = "luminance_min", d = "Minimum Luminance", min = -10, max = 10},
11-
{n = "luminance_max", d = "Maximum Luminance", min = -10, max = 10},
12-
{n = "exposure_correction", d = "Exposure Correction", min = -10, max = 10},
13-
{n = "speed_dark_bright", d = "Bright light adaptation speed", min = -10, max = 10, type="log2"},
14-
{n = "speed_bright_dark", d = "Dark scene adaptation speed", min = -10, max = 10, type="log2"},
15-
{n = "center_weight_power", d = "Power factor for center-weighting", min = 0.1, max = 10},
16-
}
17-
}
18-
}
191

20-
local function dump_lighting(lighting)
2+
local modpath = minetest.get_modpath(minetest.get_current_modname())
3+
4+
5+
local function dumpByRecipe(data, recipe)
216
local result = "{\n"
227
local section_count = 0
23-
for _,section in ipairs(lighting_sections) do
8+
for _,section in ipairs(recipe) do
249
section_count = section_count + 1
2510

2611
local parameters = section.entries or {}
27-
local state = lighting[section.n] or {}
12+
local state = data[section.n] or {}
2813

2914
result = result.." "..section.n.." = {\n"
3015

@@ -40,7 +25,7 @@ local function dump_lighting(lighting)
4025

4126
result = result.." }"
4227

43-
if section_count < #lighting_sections then
28+
if section_count < #recipe then
4429
result = result..","
4530
end
4631
result = result.."\n"
@@ -49,74 +34,59 @@ local function dump_lighting(lighting)
4934
return result
5035
end
5136

52-
minetest.register_chatcommand("set_lighting", {
53-
params = "",
54-
description = "Tune lighting parameters",
55-
func = function(player_name, param)
56-
local player = minetest.get_player_by_name(player_name);
57-
if not player then return end
37+
local function buildGUI(player, data, recipe, gui_name)
38+
local form = {
39+
"formspec_version[2]",
40+
"size[15,30]",
41+
"position[0.99,0.15]",
42+
"anchor[1,0]",
43+
"padding[0.05,0.1]",
44+
"no_prepend[]"
45+
};
46+
47+
local line = 1
48+
for _,section in ipairs(recipe) do
49+
local parameters = section.entries or {}
50+
local state = data[section.n] or {}
5851

59-
local lighting = player:get_lighting()
60-
local exposure = lighting.exposure or {}
61-
62-
local form = {
63-
"formspec_version[2]",
64-
"size[15,30]",
65-
"position[0.99,0.15]",
66-
"anchor[1,0]",
67-
"padding[0.05,0.1]",
68-
"no_prepend[]"
69-
};
70-
71-
local line = 1
72-
for _,section in ipairs(lighting_sections) do
73-
local parameters = section.entries or {}
74-
local state = lighting[section.n] or {}
75-
76-
table.insert(form, "label[1,"..line..";"..section.d.."]")
77-
line = line + 1
78-
79-
for _,v in ipairs(parameters) do
80-
table.insert(form, "label[2,"..line..";"..v.d.."]")
81-
table.insert(form, "scrollbaroptions[min=0;max=1000;smallstep=10;largestep=100;thumbsize=10]")
82-
local value = state[v.n]
83-
if v.type == "log2" then
84-
value = math.log(value or 1) / math.log(2)
85-
end
86-
local sb_scale = math.floor(1000 * (math.max(v.min, value or 0) - v.min) / (v.max - v.min))
87-
table.insert(form, "scrollbar[2,"..(line+0.7)..";12,1;horizontal;"..section.n.."."..v.n..";"..sb_scale.."]")
88-
line = line + 2.7
89-
end
52+
table.insert(form, "label[1,"..line..";"..section.d.."]")
53+
line = line + 1
9054

91-
line = line + 1
55+
for _,v in ipairs(parameters) do
56+
table.insert(form, "label[2,"..line..";"..v.d.."]")
57+
table.insert(form, "scrollbaroptions[min=0;max=1000;smallstep=10;largestep=100;thumbsize=10]")
58+
local value = state[v.n]
59+
if v.type == "log2" then
60+
value = math.log(value or 1) / math.log(2)
61+
end
62+
local sb_scale = math.floor(1000 * (math.max(v.min, value or 0) - v.min) / (v.max - v.min))
63+
table.insert(form, "scrollbar[2,"..(line+0.7)..";12,1;horizontal;"..section.n.."."..v.n..";"..sb_scale.."]")
64+
line = line + 2.7
9265
end
9366

94-
minetest.show_formspec(player_name, "lighting", table.concat(form))
95-
local debug_value = dump_lighting(lighting)
96-
local debug_ui = player:hud_add({type="text", position={x=0.1, y=0.3}, scale={x=1,y=1}, alignment = {x=1, y=1}, text=debug_value, number=0xFFFFFF})
97-
player:get_meta():set_int("lighting_hud", debug_ui)
67+
line = line + 1
9868
end
99-
})
10069

101-
minetest.register_on_player_receive_fields(function(player, formname, fields)
102-
if formname ~= "lighting" then return end
103-
104-
if not player then return end
70+
minetest.show_formspec(player:get_player_name(), gui_name, table.concat(form))
71+
local debug_value = dumpByRecipe(data, recipe)
72+
local debug_ui = player:hud_add({type="text", position={x=0.1, y=0.3}, scale={x=1,y=1}, alignment = {x=1, y=1}, text=debug_value, number=0xFFFFFF})
73+
player:get_meta():set_int(gui_name.."_hud", debug_ui)
74+
end
10575

106-
local hud_id = player:get_meta():get_int("lighting_hud")
76+
local function receiveFields(player, fields, data, recipe, gui_name)
77+
local hud_id = player:get_meta():get_int(gui_name.."_hud")
10778

10879
if fields.quit then
10980
player:hud_remove(hud_id)
110-
player:get_meta():set_int("lighting_hud", -1)
81+
player:get_meta():set_int(gui_name.."_hud", -1)
11182
return
11283
end
11384

114-
local lighting = player:get_lighting()
115-
for _,section in ipairs(lighting_sections) do
85+
for _,section in ipairs(recipe) do
11686
local parameters = section.entries or {}
11787

118-
local state = (lighting[section.n] or {})
119-
lighting[section.n] = state
88+
local state = (data[section.n] or {})
89+
data[section.n] = state
12090

12191
for _,v in ipairs(parameters) do
12292

@@ -133,8 +103,96 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
133103
end
134104
end
135105

136-
local debug_value = dump_lighting(lighting)
106+
local debug_value = dumpByRecipe(data, recipe)
137107
player:hud_change(hud_id, "text", debug_value)
108+
end
109+
110+
local lighting_recipe = {
111+
{n = "shadows", d = "Shadows",
112+
entries = {
113+
{ n = "intensity", d = "Shadow Intensity", min = 0, max = 1 }
114+
}
115+
},
116+
{
117+
n = "exposure", d = "Exposure",
118+
entries = {
119+
{n = "luminance_min", d = "Minimum Luminance", min = -10, max = 10},
120+
{n = "luminance_max", d = "Maximum Luminance", min = -10, max = 10},
121+
{n = "exposure_correction", d = "Exposure Correction", min = -10, max = 10},
122+
{n = "speed_dark_bright", d = "Bright light adaptation speed", min = -10, max = 10, type="log2"},
123+
{n = "speed_bright_dark", d = "Dark scene adaptation speed", min = -10, max = 10, type="log2"},
124+
{n = "center_weight_power", d = "Power factor for center-weighting", min = 0.1, max = 10},
125+
}
126+
},
127+
}
128+
129+
minetest.register_chatcommand("set_lighting", {
130+
params = "",
131+
description = "Tune lighting parameters",
132+
func = function(player_name, param)
133+
local player = minetest.get_player_by_name(player_name)
134+
if not player then return end
135+
136+
local lighting = player:get_lighting()
137+
138+
buildGUI(player, lighting, lighting_recipe, "lighting")
139+
end
140+
})
141+
142+
minetest.register_on_player_receive_fields(function(player, formname, fields)
143+
if formname ~= "lighting" then return end
144+
145+
if not player then return end
146+
147+
local lighting = player:get_lighting()
148+
149+
receiveFields(player, fields, lighting, lighting_recipe, "lighting")
138150

139151
player:set_lighting(lighting)
140-
end)
152+
end)
153+
154+
local sky_light_recipe = {
155+
{n = "color_offset", d = "Color offset",
156+
entries = {
157+
{n = "r", d = "Red color offset", min = -1, max = 2},
158+
{n = "g", d = "Green color offset", min = -1, max = 2},
159+
{n = "b", d = "Blue color offset", min = -1, max = 2},
160+
}
161+
},
162+
{n = "color_ratio_coef", d = "Color day-night ratio coefficient",
163+
entries = {
164+
{n = "r", d = "Red color day-night ratio coefficient", min = -1e-3, max = 2e-3},
165+
{n = "g", d = "Green color day-night ratio coefficient", min = -1e-3, max = 2e-3},
166+
{n = "b", d = "Blue color day-night ratio coefficient", min = -1e-3, max = 2e-3},
167+
}
168+
}
169+
}
170+
171+
172+
minetest.register_chatcommand("set_sky_light", {
173+
params = "",
174+
description = "Tune lighting sky_light parameters",
175+
func = function(player_name, param)
176+
local player = minetest.get_player_by_name(player_name)
177+
if not player then return end
178+
179+
local lighting = player:get_lighting()
180+
local sky_light = lighting.sky_light
181+
182+
buildGUI(player, sky_light, sky_light_recipe, "sky_light")
183+
end
184+
})
185+
186+
minetest.register_on_player_receive_fields(function(player, formname, fields)
187+
if formname ~= "sky_light" then return end
188+
189+
if not player then return end
190+
191+
local lighting = player:get_lighting()
192+
local sky_light = lighting.sky_light
193+
194+
receiveFields(player, fields, sky_light, sky_light_recipe, "sky_light")
195+
196+
player:set_lighting(lighting)
197+
end)
198+

src/client/clientenvironment.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ void ClientEnvironment::step(float dtime)
261261

262262
u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef());
263263
lplayer->light_color = encode_light(light, 0); // this transfers light.alpha
264-
final_color_blend(&lplayer->light_color, light, day_night_ratio);
264+
const SkyLight &sky_light = m_client->getEnv().getLocalPlayer()->getLighting().sky_light;
265+
final_color_blend(&lplayer->light_color, light, day_night_ratio, sky_light);
265266
}
266267

267268
/*

src/client/clientmap.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
737737
const float animation_time = m_client->getAnimationTime();
738738
const int crack = m_client->getCrackLevel();
739739
const u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
740+
const Lighting &lighting = m_client->getEnv().getLocalPlayer()->getLighting();
740741

741742
const v3f camera_position = m_camera_position;
742743

@@ -795,7 +796,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
795796
mesh_animate_count < (m_control.range_all ? 200 : 50)) {
796797

797798
bool animated = block_mesh->animate(faraway, animation_time,
798-
crack, daynight_ratio);
799+
crack, daynight_ratio, lighting.sky_light);
799800
if (animated)
800801
mesh_animate_count++;
801802
} else {

src/client/content_cao.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,8 +909,10 @@ void GenericCAO::updateLight(u32 day_night_ratio)
909909
// based on the entity glow.
910910
if (m_enable_shaders)
911911
light = encode_light(light_at_pos, m_prop.glow);
912-
else
913-
final_color_blend(&light, light_at_pos, day_night_ratio);
912+
else {
913+
const SkyLight &sky_light = m_env->getLocalPlayer()->getLighting().sky_light;
914+
final_color_blend(&light, light_at_pos, day_night_ratio, sky_light);
915+
}
914916

915917
if (light != m_last_light) {
916918
m_last_light = light;

src/client/game.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,10 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
472472
void onSetConstants(video::IMaterialRendererServices *services) override
473473
{
474474
u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
475-
video::SColorf sunlight;
476-
get_sunlight_color(&sunlight, daynight_ratio);
477-
m_day_light.set(sunlight, services);
475+
const auto &lighting = m_client->getEnv().getLocalPlayer()->getLighting();
476+
video::SColorf skylight;
477+
get_skylight_color(&skylight, daynight_ratio, lighting.sky_light);
478+
m_day_light.set(skylight, services);
478479

479480
u32 animation_timer = m_client->getEnv().getFrameTime() % 1000000;
480481
float animation_timer_f = (float)animation_timer / 100000.f;
@@ -511,7 +512,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
511512
m_texel_size0_vertex.set(m_texel_size0, services);
512513
m_texel_size0_pixel.set(m_texel_size0, services);
513514

514-
const AutoExposure &exposure_params = m_client->getEnv().getLocalPlayer()->getLighting().exposure;
515+
const AutoExposure &exposure_params = lighting.exposure;
515516
std::array<float, 7> exposure_buffer = {
516517
std::pow(2.0f, exposure_params.luminance_min),
517518
std::pow(2.0f, exposure_params.luminance_max),
@@ -529,7 +530,6 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
529530
m_bloom_strength_pixel.set(&m_bloom_strength, services);
530531
}
531532

532-
const auto &lighting = m_client->getEnv().getLocalPlayer()->getLighting();
533533
float saturation = lighting.saturation;
534534
m_saturation_pixel.set(&saturation, services);
535535

@@ -3557,8 +3557,9 @@ PointedThing Game::updatePointedThing(
35573557
}
35583558

35593559
u32 daynight_ratio = client->getEnv().getDayNightRatio();
3560+
const Lighting &lighting = client->getEnv().getLocalPlayer()->getLighting();
35603561
video::SColor c;
3561-
final_color_blend(&c, light_level, daynight_ratio);
3562+
final_color_blend(&c, light_level, daynight_ratio, lighting.sky_light);
35623563

35633564
// Modify final color a bit with time
35643565
u32 timer = client->getEnv().getFrameTime() % 5000;

0 commit comments

Comments
 (0)