realterrain/init.lua

269 lines
8.9 KiB
Lua

local realterrain = {}
local ie = minetest.request_insecure_environment()
ie.require "luarocks.loader"
local imlib2 = ie.require "imlib2"
-- Parameters
local DEM = 'mandelbrot16bit.tif'
local COVER = 'cover.tif' --cover should only be an 8-bit file of the same dimensions as the DEM
realterrain.settings = {} --form persistence
--defaults
realterrain.settings.yscale = 1
realterrain.settings.xscale = 1
realterrain.settings.zscale = 1
realterrain.settings.waterlevel = 0
local demfilename = minetest.get_modpath("realterrain").."/dem/"..DEM
local dem = imlib2.image.load(demfilename)
local width = dem:get_width()
local length = dem:get_height()
print("width: "..width..", height: "..length)
local coverfilename = minetest.get_modpath("realterrain").."/dem/"..COVER
local cover = imlib2.image.load(coverfilename)
-- Set mapgen parameters
minetest.register_on_mapgen_init(function(mgparams)
minetest.set_mapgen_params({mgname="singlenode", flags="nolight"})
end)
-- On generated function
minetest.register_on_generated(function(minp, maxp, seed)
local t0 = os.clock()
local x1 = maxp.x
local y1 = maxp.y
local z1 = maxp.z
local x0 = minp.x
local y0 = minp.y
local z0 = minp.z
--local blelev = get_pixel(x0, z0)
--print("block corner elev: "..blelev)
local sidelen = x1 - x0 + 1
local ystridevm = sidelen + 32
local cx0 = math.floor((x0 + 32) / 80) -- mapchunk co-ordinates to select
local cz0 = math.floor((z0 + 32) / 80) -- the flat array of DEM values
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
local data = vm:get_data()
local c_grass = minetest.get_content_id("default:dirt_with_grass")
local c_alpine = minetest.get_content_id("default:gravel")
local c_stone = minetest.get_content_id("default:stone")
local c_sand = minetest.get_content_id("default:sand")
local c_water = minetest.get_content_id("default:water_source")
local demi = 1 -- index of 80x80 flat array of DEM values
--local blockel = get_pixel(x0, z0)
for z = z0, z1 do
for x = x0, x1 do
local elev, cover = get_pixel(x, z) -- elevation in meters from DEM and water true/false
-- use demi to get elevation value from flat array
local node_elev = math.floor(tonumber(realterrain.settings.waterlevel) + elev / tonumber(realterrain.settings.yscale))
local vi = area:index(x, y0, z) -- voxelmanip index
for y = y0, y1 do
if y < node_elev then
data[vi] = c_stone
-- decide on ores, caverns
elseif y == node_elev then
--if the river map says this is water then that's all we set
if cover > 225 then
data[vi] = c_water
elseif cover > 128 then
data[vi] = c_stone
else
if y <= tonumber(realterrain.settings.waterlevel) then
data[vi] = c_sand
else
if y > 100 then
data[vi] = c_alpine -- shrubs?
else
data[vi] = c_grass -- decide on trees?
end
end
end
elseif y <= tonumber(realterrain.settings.waterlevel) then
data[vi] = c_water
end
vi = vi + ystridevm
end
demi = demi + 1
end
end
vm:set_data(data)
vm:calc_lighting()
vm:write_to_map(data)
vm:update_liquids()
local chugent = math.ceil((os.clock() - t0) * 1000)
--print ("[DEM] "..chugent.." ms mapchunk ("..cx0..", "..math.floor((y0 + 32) / 80)..", "..cz0..")")
end)
--for now we are going to assume 32 bit signed elevation pixels
--and a header offset of
function get_pixel(x,z)
--local row = math.floor(length / 2) + (z / tonumber(realterrain.settings.zscale))
--local col = math.floor(width / 2) + (x / tonumber(realterrain.settings.xscale))
local row,col = 0+x, 0-z
local elev = dem:get_pixel(math.floor(col / tonumber(realterrain.settings.xscale)), math.floor(row / tonumber(realterrain.settings.zscale)))
local cover = cover:get_pixel(math.floor(col / tonumber(realterrain.settings.xscale)), math.floor(row / tonumber(realterrain.settings.zscale)))
return elev.red, cover.red
end
-- the controller for changing map settings
minetest.register_tool("realterrain:remote" , {
description = "Realterrain Settings",
inventory_image = "remote.png",
--left-clicking the tool
on_use = function (itemstack, user, pointed_thing)
local pname = user:get_player_name()
realterrain.show_rc_form(pname)
end,
})
-- Processing the form from the RC
minetest.register_on_player_receive_fields(function(player, formname, fields)
if string.sub(formname, 1, 12) == "realterrain:" then
local wait = os.clock()
while os.clock() - wait < 0.05 do end --popups don't work without this see issue #30
print("fields submitted: "..dump(fields))
local pname = player:get_player_name()
-- always save any form fields
for k,v in next, fields do
realterrain.settings[k] = v --we will preserve field entries exactly as entered
end
realterrain.save_settings()
if formname == "realterrain:popup" then
if fields.exit == "Back" then
realterrain.show_rc_form(pname)
return true
end
end
--the main form
if formname == "realterrain:rc_form" then
--actual form submissions
if fields.exit == "Delete" then
minetest.chat_send_player(pname, "You changed mapgen settings and are deleting the current map, restart the world!")
--kick all other players
--delete the map.sqlite file
--kick this player? @todo what if this is a dedicated server?
return true
elseif fields.exit == "Apply" then
minetest.chat_send_player(pname, "You changed the mapgen settings!")
return true
end
return true
end
return true
end
end)
--the formspecs and related settings and functions / selected field variables
--called at each form submission
function realterrain.save_settings()
local file = io.open(minetest.get_worldpath().."/realterrain_settings", "w")
if file then
for k,v in next, realterrain.settings do
local line = {key=k, values=v}
file:write(minetest.serialize(line).."\n")
end
file:close()
end
end
-- load settings run at EOF at mod start
function realterrain.load_settings()
local file = io.open(minetest.get_worldpath().."/realterrain_settings", "r")
if file then
for line in file:lines() do
if line ~= "" then
local tline = minetest.deserialize(line)
realterrain.settings[tline.key] = tline.values
end
end
file:close()
end
end
--retrieve individual form field
function realterrain.get_setting(setting)
if realterrain.settings ~= {} then
if realterrain.settings[setting] then
if realterrain.settings[setting] ~= "" then
return realterrain.settings[setting]
else
return false
end
else
return false
end
else
return false
end
end
-- show the main remote control form
function realterrain.show_rc_form(pname)
local player = minetest.get_player_by_name(pname)
local ppos = player:getpos()
local degree = player:get_look_yaw()*180/math.pi - 90
if degree < 0 then degree = degree + 360 end
local dir
if degree <= 45 or degree > 315 then dir = "North"
elseif degree <= 135 then dir = "West"
elseif degree <= 225 then dir = "South"
else dir = "South" end
local yscale = realterrain.get_setting("yscale")
local xscale = realterrain.get_setting("xscale")
local zscale = realterrain.get_setting("zscale")
local waterlevel = realterrain.get_setting("waterlevel")
--form header
local f_header = "size[12,10]" ..
--"tabheader[0,0;tab;1D, 2D, 3D, Import, Manage;"..tab.."]"..
"label[0,0;You are at x= "..math.floor(ppos.x)..
" y= "..math.floor(ppos.y).." z= "..math.floor(ppos.z).." and mostly facing "..dir.."]"
--Scale settings
local f_scale_settings = "field[1,4;4,1;yscale;Vertical Scale;"..minetest.formspec_escape(yscale).."]" ..
"field[1,5;4,1;xscale;East-West Scale;"..minetest.formspec_escape(xscale).."]" ..
"field[1,6;4,1;zscale;North-South Scale;"..minetest.formspec_escape(zscale).."]" ..
"field[1,7;4,1;waterlevel;Water Level;"..minetest.formspec_escape(waterlevel).."]"
--Action buttons
local f_footer = "label[3,8.5;Delete the map, reset]"..
"button_exit[3,9;2,1;exit;Delete]"..
"label[7,8.5;Reset the map only]"..
"button_exit[7,9;2,1;exit;Apply]"
minetest.show_formspec(pname, "realterrain:rc_form",
f_header ..
f_scale_settings ..
f_footer
)
return true
end
-- this is the form-error popup
function realterrain.show_popup(pname, message)
minetest.chat_send_player(pname, "Form error: ".. message)
minetest.show_formspec(pname, "realterrain:popup",
"size[10,8]" ..
"button_exit[1,1;2,1;exit;Back]"..
"label[1,3;"..minetest.formspec_escape(message).."]"
)
return true
end
--read from file, various persisted settings
realterrain.load_settings()