Hi all
I have been mucking around a bit and could not find a ready made sollution so i came up with the following:
It's a small mod that logs the hight as each chunck gets initiated. I called it hfind
you can get the groundlevel or treetop level by adding or removing tree-stuff from:
local not_surface_node = {["ignore"]=true, ["air"]=true, ["default:water_source"]=true, ["default:tree"]=true, ["default:leaves"]=true, ["default:jungletree"]=true, ["default:jungleleaves"]=true }
sorry i dont have git setup so you will gave to create the files by hand (but there are only 3 of them and an optional python script for making an image)
Readme.txt, depends.txt and init.lua go in .../mods/hfind/
the python script (h_map.py) is easiest copied into the world dir as it will then find the h_file.txt, otherwise pass the path to the file as a commandline argument like this:
Code: Select all
python h_map.py path_to_h_file/h_file.txt
here is the code for README.txt:
Code: Select all
hfind by Smitje
function:
make the ground height available for mod using mapgen v6, eighter use this mod as a base or
read the h_file from your own.
(v7 has a handy function: lastheightmap = minetest.get_mapgen_object("heightmap"), but not v6)
The h values are stored in h_file.txt in the world dir
The accompanying python script h_map.py can create an image of the data.
Stability not checked, works for me on minetest 0.4.10
Depends default
Licenses: code WTFPL
verry loosely based on:
noise23 0.1.1 by paramat
depends.txt:
and init.lua:
Code: Select all
-- hfind by Smitje
-- converted from:
-- 23noise 0.1.1 by paramat
-- stability not verrified, works on 0.4.10
-- Depends default
-- License: code WTFPL
local YMIN = -1000
local YMAX = 2000
local not_surface_node = {["ignore"]=true, ["air"]=true, ["default:water_source"]=true, ["default:tree"]=true, ["default:leaves"]=true, ["default:jungletree"]=true, ["default:jungleleaves"]=true }
local world_path = minetest.get_worldpath()
local h_file = io.open(world_path.."/h_file.txt", "r")
print ("world_path "..world_path)
local known_chuncks = {}
local chuncks_heights = {}
-- read contents of h_file if it exists
if h_file then
print("processing h_file")
local chunck_name_line = h_file:read("*line") --should be the first chunck name
while chunck_name_line do
local split_line = {}
for i in string.gmatch(chunck_name_line, "%S+") do table.insert(split_line, i)end
local chunck_name = split_line[1]
known_chuncks[chunck_name] = {["status"] = split_line[2]}
local chunck_table = {}
local data_line = h_file:read("*line") --should be the first line of height data
while data_line and #data_line > 79 do
local data = {}
for h_val in string.gmatch(data_line, "%S+") do
if h_val then
if h_val == "A" or h_val == "U" then
table.insert(data, h_val)
else
table.insert(data, tonumber(h_val))
end
end
end
table.insert(chunck_table, data)
data_line = h_file:read("*line")
end
chuncks_heights[chunck_name] = chunck_table
chunck_name_line = data_line
end
end
print("imported chuncks:")
for ty, ui in pairs(known_chuncks) do print(ty, ui["status"])end
if h_file then h_file:close() end
minetest.register_on_generated(function(minp, maxp, seed)
if minp.y < YMIN or maxp.y > YMAX then
return -- The chunk is outside the set height band (YMIN YMAX)
end
local t1 = 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 chunck_name = tostring(x0)..","..tostring(z0)
print ("[hfind] name "..chunck_name.." chunk minp ("..x0.." "..y0.." "..z0..") maxp ("..x1.." "..y1.." "..z1..")")
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
local data = vm:get_data()
if known_chuncks[chunck_name] then
if known_chuncks[chunck_name]["status"] == "complete" then
print(chunck_name.." known and complete; not generating")
else
print(chunck_name.." incomplete; updating")
local chunck_status = "complete"
--find ground level
for z = z0, z1 do -- for each xy plane progressing northwards
--local ground_y = " "
local data = {}
for x = x0, x1 do
local known_height = chuncks_heights[chunck_name][z-z0+1][x-x0+1]
if known_height == "A" or known_height < y0 then
for y = y1, y0, -1 do
nodename = minetest.get_node({x=x,y=y,z=z}).name
if not_surface_node[nodename] then
if y == y0 and known_height == "A" then
chunck_status = "incomplete"
end
elseif y == y1 and known_height ~= "A" then
chuncks_heights[chunck_name][z-z0+1][x-x0+1] = y
chunck_status = "incomplete"
break
else
chuncks_heights[chunck_name][z-z0+1][x-x0+1] = y
break
end
end
end
end
end
known_chuncks[chunck_name] = {["status"] = chunck_status}
-- for ty, ui in pairs(known_chuncks) do print(ty, ui["status"])end
end
else -- build the data from scratch
print("generating")
chunck_table = {}
local chunck_status = "complete"
--find ground level
for z = z0, z1 do -- for each xy plane progressing northwards
--local ground_y = " "
local data = {}
for x = x0, x1 do
for y = y1, y0, -1 do
nodename = minetest.get_node({x=x,y=y,z=z}).name
if not_surface_node[nodename] then
if y == y0 then
table.insert(data, "A")
chunck_status = "incomplete"
end
elseif y == y1 then
table.insert(data, y)
chunck_status = "incomplete"
--ground_y = ground_y.." ".."UUU"
break
else
table.insert(data, y)
--ground_y = ground_y.." "..y
break
end
end
end
table.insert(chunck_table, data)
end
chuncks_heights[chunck_name] = chunck_table
known_chuncks[chunck_name] = {["status"] = chunck_status}
-- for ty, ui in pairs(known_chuncks) do print(ty, ui["status"])end
end
vm:set_data(data)
vm:set_lighting({day=0, night=0})
vm:calc_lighting()
vm:write_to_map(data)
local chugent = math.ceil((os.clock() - t1) * 1000)
print ("[hfind] "..chugent.." ms\n")
end)
--to save file:
minetest.register_on_shutdown(function()
print("im leaving now\n\n")
local h_file = assert(io.open(world_path.."/h_file.txt", "w"), "Failed to open 'h_file.txt'")
for ty, ui in pairs(known_chuncks) do print(ty, ui["status"])end
for ch, data in pairs(chuncks_heights) do
h_file:write(ch.." "..known_chuncks[ch]["status"], "\n")
for i, ti in pairs(data) do
for j, tj in pairs(ti) do
h_file:write(tj, " ")
end
h_file:write("\n")
end
end
h_file:flush()
h_file:close()
end)
the python script h_map.py:
Code: Select all
#! python
# produce a height map immage from a mintest h_file
import os, sys, Image
minh = 100000
maxh = -100000
minx = 100000
maxx = -100000
miny = 100000
maxy = -100000
def interp_color(color0, color1, factor):
"""Interpolate between color0 and color1.
factor = 0 returns color0,
factor = 1 returns color1.
"""
r0 = int((color1[0] - color0[0])* factor) + color0[0]
r1 = int((color1[1] - color0[1])* factor) + color0[1]
r2 = int((color1[2] - color0[2])* factor) + color0[2]
if len(color0) == 4:
r3 = ((color1[3] - color0[3])* factor) + color0[3]
return (r0, r1, r2, r3)
else:
return (r0, r1, r2)
def read_chuncks(filename):
"""read the h_file and return a dict of chuncks"""
global minh, maxh, minx, maxx, miny, maxy
chuncks = {}
h_file = open(filename, "r")
line = h_file.readline()
while line:
chunck_name = line.split()[0]
chuncks[chunck_name] = []
chz = int(chunck_name.split(" ")[0].split(",")[0])
chx = int(chunck_name.split(" ")[0].split(",")[1])
if chz < minx : minx = chz
if chz > maxx : maxx = chz + 80
if chx < miny : miny = chx
if chx > maxy : maxy = chx + 80
for i in range(80):
line = h_file.readline()
linlist = line.split()
for j in range(len(linlist)):
if not (linlist[j] == "A" or linlist[j] == "U"):
linlist[j] = int(linlist[j])
else:
linlist[j] = 0
mi = min(linlist)
if mi < minh:
minh = mi
ma = max(linlist)
if ma > maxh and ma != "A":
maxh = ma
chuncks[chunck_name].append(linlist)
line = h_file.readline()
print"chuncks", len(chuncks), "\nmin h", minh, "max h", maxh
print"min x", minx, "max x", maxx
print"min y", miny, "max y", maxy
h_file.close()
return chuncks
def h_to_color(h):
""" converts a h value to a color.
values below 0 in blue, deeper is darker
0 in beige
values above 0 in green, higher is lighter
"""
global minh, maxh
lightblue = (47, 174, 255)
darkblue = (0, 0, 109)
lightgreen = (30, 203, 43)
darkgreen = (34, 109, 39)
beige = (214, 159, 42)
white = (255, 255, 255)
hwater = 1
if h == "A" or h == "U":
return white
elif h < hwater:
return interp_color(lightblue, darkblue, (float(h)/(minh-hwater)))
elif h > hwater:
return interp_color(darkgreen, lightgreen, (float(h)/(maxh-hwater)))
else:
return beige
def create_image(chuncks):
""" convert the chunck data to an image """
i_file = Image.new("RGB", (80+maxx-minx, 80+maxy-miny), (0, 0, 0))
i_file.load()
for i in chuncks:
chx = int(i.split(" ")[0].split(",")[0]) - minx
chy = maxy - miny - (int(i.split(" ")[0].split(",")[1]) - miny)
for x in range(80):
for y in range(80):
#print i, chx + x, 79 + chy - y
i_file.putpixel((chx + x, chy + 79 - y ), h_to_color(chuncks[i][y][x]))
i_file.save("h_map.png")
if __name__ == "__main__":
if len(sys.argv) == 2:
chuncks = read_chuncks(sys.argv[1])
else:
chuncks = read_chuncks("h_file.txt")
create_image(chuncks)