EinsDreiDreiSieben/mods/cozylights/light_brush.lua

341 lines
12 KiB
Lua

local mf = math.floor
local function on_secondary_use(user)
local lb = cozylights.cozyplayers[user:get_player_name()].lbrush
local settings_formspec = {
"formspec_version[4]",
--"size[6,6.4]",
"size[5.2,5]",
"label[1.45,0.5;Light Brush Settings]",
"label[0.95,1.35;Radius]",
"field[3.6,1.1;0.7,0.5;radius;;"..lb.radius.."]",
"tooltip[0.95,1.1;3.4,0.5;If radius is 0 then only one node will be affected by the brush.\n"..
"If not zero then it's a sphere of affected nodes with specified radius.\n"..
"As of now max radius is only 120.\n"..
"With radiuses over 30 mouse hold as of now does not work, only point and click]",
"label[0.95,2.05;Brightness]",
"field[3.6,1.8;0.7,0.5;brightness;;"..lb.brightness.."]",
"tooltip[0.95,1.8;3.4,0.5;Brightness - for most brush modes values are from 1 to 14, corresponding to engine light levels.\n"..
"If brush mode is 'darken' or 'override' then 0 will replace lowest light levels with air.]",
"label[0.95,2.75;Strength]",
"field[3.6,2.5;0.7,0.5;strength;;"..lb.strength.."]",
"tooltip[0.95,2.5;3.4,0.5;Strength, can be from 0 to 1, decimal values of any precision are valid.\n"..
"Determines how bright(relative to brightness setting) light nodes in affected area will be.]",
"label[0.95,3.45;Brush Mode]",
"dropdown[2.8,3.2;1.5,0.5;mode;default,erase,override,lighten,darken,blend;"..lb.mode.."]",
"tooltip[0.95,3.2;3.4,0.5;\nDefault - replace only dimmer light nodes or air with brush.\n\n"..
"Erase - inverse of default, replaces only lighter nodes with darker nodes or air if brightness is 0.\n\n"..
"Override - set light nodes as brush settings dictate regardless of difference in brigthness.\n\n"..
"Lighten - milder than default mode.\n\n"..
"Darken - milder erase, does not darken below light 1(does not replace with air).\n\n"..
"Blend - blend affected nodes' brigthness with brush brigthness.\n"..
"Even though behaves correctly, as of now looks weird and unintuitive if radius is not 0.]",
--"checkbox[1.7,4.6;cover_only_surfaces;cover only surfaces;"..(lb.cover_only_surfaces == 1 and "true" or "false").."]",
--"tooltip[1.7,4.4;2.6,0.4;if enabled brush will not fill up the air with light above the ground;"..bgcolor..";#FFFFFF]",
--"button_exit[1,5.1;4,0.8;confirm;Confirm]",
"button_exit[1.1,4;3,0.8;confirm;Confirm]",
}
minetest.show_formspec(user:get_player_name(), "cozylights:brush_settings",table.concat(settings_formspec, ""))
end
minetest.register_tool("cozylights:light_brush", {
description = "Light Brush",
inventory_image = "light_brush.png",
wield_image = "light_brush.png^[transformR90",
tool_capabilities = {
full_punch_interval = 0.3,
max_drop_level = 1,
},
range = 100.0,
on_use = function(itemstack, user, pointed_thing)
if pointed_thing.under then
local nodenameunder = minetest.get_node(pointed_thing.under).name
local nodedefunder = minetest.registered_nodes[nodenameunder]
local lb = cozylights.cozyplayers[user:get_player_name()].lbrush
local above = pointed_thing.above
if nodenameunder ~= "air" and nodedefunder.buildable_to == true then
above.y = above.y - 1
end
local above_hash = above.x + (above.y)*100 + above.z*10000
lb.pos_hash = above_hash
cozylights:draw_brush_light(pointed_thing.above, lb)
end
end,
on_place = function(_, placer)
on_secondary_use(placer)
end,
on_secondary_use = function(_, user)
on_secondary_use(user)
end,
sound = {breaks = "default_tool_breaks"}
})
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= ("cozylights:brush_settings") then return end
if player == nil then return end
local lb = cozylights.cozyplayers[player:get_player_name()].lbrush
if fields.brightness then
local brightness = tonumber(fields.brightness) > 14 and 14 or tonumber(fields.brightness)
lb.brightness = brightness < 0 and 0 or mf(brightness or 0)
end
if fields.radius then
local radius = tonumber(fields.radius) > 200 and 200 or tonumber(fields.radius)
lb.radius = radius < 0 and 0 or mf(radius or 0)
end
if fields.strength then
local strength = tonumber(fields.strength) > 1 and 1 or tonumber(fields.strength)
lb.strength = strength < 0 and 0 or strength
end
if fields.mode then
local mode = fields.mode
local idx = 6
if mode == "default" then
idx = 1
elseif mode == "erase" then
idx = 2
elseif mode == "override" then
idx = 3
elseif mode == "lighten" then
idx = 4
elseif mode == "darken" then
idx = 5
end
lb.mode = idx
end
if fields.cover_only_surfaces then
lb.cover_only_surfaces = fields.cover_only_surfaces == "true" and 1 or 0
end
end)
local function calc_dims_for_brush(brightness, radius, strength, even)
local dim_levels = {}
--- this gradient attempts to get more colors, but that looks like a super weird monochrome rainbow and immersion braking
--strength = (strength+0.05)*2
--
--local current_brightness = brightness
--local step = math.sqrt(radius/brightness)
--local initial_step = step
--for i = 1, radius do
-- dim_levels[i] = current_brightness
-- if i>step then
-- step = step*strength + math.sqrt(i)
-- current_brightness = current_brightness - 1
-- end
--end
--- this gradient drops brightness fast but spreads dimmer lights over farther
if strength == 1 then
even = true
end
strength = strength*5
dim_levels[1] = brightness
if even ~= true then
for i = 2, radius do
local dim = math.sqrt(math.sqrt(i)) * (6-strength)
local light_i = mf(brightness - dim)
if light_i > 0 then
if light_i < 15 then
dim_levels[i] = light_i
else
dim_levels[i] = 14
end
else
dim_levels[i] = 1
end
end
else
for i = 2, radius do
dim_levels[i] = brightness
end
end
return dim_levels
end
local c_air = minetest.get_content_id("air")
local c_light1 = minetest.get_content_id("cozylights:light1")
local c_lights = { c_light1, c_light1 + 1, c_light1 + 2, c_light1 + 3, c_light1 + 4, c_light1 + 5, c_light1 + 6,
c_light1 + 7, c_light1 + 8, c_light1 + 9, c_light1 + 10, c_light1 + 11, c_light1 + 12, c_light1 + 13 }
local gent_total = 0
local gent_count = 0
local function draw_one_node(pos,lb)
local node = minetest.get_node(pos)
local brightness = lb.brightness
local new_node_name = "cozylights:light"..brightness
if brightness == 0 then
new_node_name = "air"
end
if node.name == "air" and new_node_name ~= node.name then
minetest.set_node(
pos,
{
name=new_node_name,
param2=brightness
}
)
return
end
if string.find(node.name,"cozylights:") then
if lb.mode == 1 and brightness <= node.param2 then return end
if lb.mode == 2 and brightness >= node.param2 then return end
if lb.mode == 4 then
if brightness <= node.param2 then return end
brightness = mf((brightness+node.param2)/2+0.5)
if brightness < 1 then return end
new_node_name = "cozylights:light"..brightness
elseif lb.mode == 5 then
if brightness >= node.param2 then return end
brightness = mf((brightness+node.param2)/2)
new_node_name = "cozylights:light"..brightness
if brightness < 1 then
brightness = 0
new_node_name = "air"
end
elseif lb.mode == 6 then
brightness = mf((brightness+node.param2)/2+0.5)
new_node_name = "cozylights:light"..brightness
if brightness < 0 then
brightness = 0
new_node_name = "air"
end
end
minetest.set_node(
pos,
{
name=new_node_name,
param2=brightness
}
)
end
end
--this function pulls numbers out of its ass instead of seriously computing everything, so its faster
--some nodes are being missed for big spheres
function cozylights:draw_brush_light(pos, lb)
local t = os.clock()
local radius = lb.radius
if radius == 0 then
draw_one_node(pos,lb)
return
end
local mode = lb.mode
local brightness = lb.brightness
local dim_levels = calc_dims_for_brush(brightness,radius,lb.strength, mode==2 and true or false)
print("dim_levels:"..cozylights:dump(dim_levels))
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(vector.subtract(pos, radius+1), vector.add(pos, radius+1))
local data = vm:get_data()
local param2data = vm:get_param2_data()
local a = VoxelArea:new{
MinEdge = emin,
MaxEdge = emax
}
local sphere_surface = cozylights:get_sphere_surface(radius)
local ylvl = 1
local cid = data[a:index(pos.x,pos.y-1,pos.z)]
local cida = data[a:index(pos.x,pos.y+1,pos.z)]
if cid and cida then
if (cid == c_air or (cid >= c_lights[1] and cid <= c_lights[14]))
and cida ~= c_air and (cida < c_lights[1] or cida > c_lights[14])
then
ylvl = -1
end
else
return
end
pos.y = pos.y + ylvl
if mode == 1 then
if cozylights.always_fix_edges == true then
local visited_pos = {}
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_fix_edges(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels,visited_pos)
end
else
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels)
end
end
elseif mode == 2 then
if cozylights.always_fix_edges == true then
local visited_pos = {}
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_erase_fix_edges(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels,visited_pos)
end
else
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_erase(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels)
end
end
elseif mode == 3 then
if cozylights.always_fix_edges == true then
local visited_pos = {}
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_override_fix_edges(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels,visited_pos)
end
else
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_override(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels)
end
end
elseif mode == 4 then
if cozylights.always_fix_edges == true then
local visited_pos = {}
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_lighten_fix_edges(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels,visited_pos)
end
else
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_lighten(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels)
end
end
elseif mode == 5 then
if cozylights.always_fix_edges == true then
local visited_pos = {}
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_darken_fix_edges(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels,visited_pos)
end
else
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_darken(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels)
end
end
else
if cozylights.always_fix_edges == true then
local visited_pos = {}
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_blend_fix_edges(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels,visited_pos)
end
else
for i,pos2 in ipairs(sphere_surface) do
local end_pos = {x=pos.x+pos2.x,y=pos.y+pos2.y,z=pos.z+pos2.z}
cozylights:lightcast_blend(pos, vector.direction(pos, end_pos),radius,data,param2data,a,dim_levels)
end
end
end
vm:set_data(data)
vm:set_param2_data(param2data)
vm:update_liquids()
vm:write_to_map()
gent_total = gent_total + mf((os.clock() - t) * 1000)
gent_count = gent_count + 1
print("Av draw time " .. mf(gent_total/gent_count) .. " ms. Sample of: "..gent_count)
end