diff --git a/LICENSE.txt b/LICENSE.txt index 75d2400..1253de2 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,14 +1,11 @@ - Copyright (C) 2004 Sam Hocevar + Copyright (C) 2004 Sam Hocevar (original mapgen code, modified by MisterE) - Copyright (C) 2022 MisterE + Copyright (C) 2022 MisterE (api code) - Copyright (C) 2022 regulus - - Copyright (C) 2022 LMD - - Copyright (C) 2010 David Ng (voronoi.lua) + Copyright (C) 2022 regulus (some mapgen code) + Copyright (C) 2022 LMD (some mapgen code) MIT LICENSE diff --git a/README.md b/README.md new file mode 100644 index 0000000..68788a2 --- /dev/null +++ b/README.md @@ -0,0 +1,272 @@ +## Intro and usage +Luamap is a library for modders to focus on mapgen logic rather than figuruing +out how mapgen works. using it is simple: override the luamap.logic function. +The goal of the luamap logic function is to return a content id at a point. + +Calling the old logic function before running your own logic allows you to add +to other mods that use luamap. If you do not save and call the old luamap +function, then your mod will completely override any other mapgens made by other +mods using luamap. + +```lua +local c_stone = minetest.get_content_id("default:stone") +local c_water = minetest.get_content_id("default:water_source") +local water_level = 0 +local old_logic = luamap.logic +function luamap.logic(noise_vals,x,y,z,seed) + -- get any terrain defined in another mod + local content = old_logic(noise_vals,x,y,z,seed) + -- use our own logic to add to that logic + -- make any nodes below sea level water and below stone level stone + if y < water_level then + content = c_water + end + if y < -3 then + content = c_stone + end + return content +end +``` + +This would make a flat mapgen covered in a layer of water. + +luamap gives you an easy way to make and use 2d and 3d noises in your mapgen: + +```lua +-- 2d noise mapgen +luamap.register_noise("terrainmap",{ + type = "2d", + np_vals = { + offset = 0, + scale = 1, + spread = {x=384, y=256, z=384}, + seed = 5900033, + octaves = 5, + persist = 0.63, + lacunarity = 2.0, + flags = "" + }, + ymin = -31000, + ymax = 31000, +}) + +local c_stone = minetest.get_content_id("default:stone") +local c_water = minetest.get_content_id("default:water_source") +local water_level = 0 +local old_logic = luamap.logic +function luamap.logic(noise_vals,x,y,z,seed) + -- get any terrain defined in another mod + local content = old_logic(noise_vals,x,y,z,seed) + + if y < water_level then + content = c_water + end + if y < noise_vals.terrainmap * 50 then + content = c_stone + end + + return content +end +``` + +you can also register a 3d noise like so: +```lua +luamap.register_noise("terrainmap",{ + type = "3d", + np_vals = { + offset = 0, + scale = 1, + spread = {x=384, y=384, z=384}, + seed = 5900033, + octaves = 5, + persist = 0.63, + lacunarity = 2.0, + flags = "" + }, + ymin = -31000, + ymax = 31000, +}) +``` + +## API + +### luamap.logic +```lua +luamap.logic(noise_vals,x,y,z,seed) +``` + +DESCRIPTION: Override this function to define mapgen logic. Best practice is to +save the original function and call it to get a preset content id from any +previously-defined mapgens. + +PARAMETERS: + +`noise_vals` + +A table indexed by noise names defined with `luamap.register_noise()`. So, if I +have registered 2 noises: `terrain_2d` and `caves_3d` then noisevals might be: + +```lua +{ + ["terrain_2d"] = .7, + ["caves_3d"] = -.9, +} +``` + +`x,y,z` + +The coordinates of the position to set the content for + +`seed` + +The seed passed to minetest.register_on_generated() + +RETURNS: + +should return a content id such as that gotten from +`minetest.get_content_id("nodename")` + +NOTES: +remember that this function is called for every single node ever generated. +Keep your calculations fast or the mapgen will slow down. +2d noise is much faster than 3d noise. + +### luamap.register_noise + +```lua +luamap.register_noise(name,data) +``` + +DESCRIPTION: creates a mapgen noise that will be calculated and returned in +`luamap.logic` + +PARAMETERS: + +`name` + +String, the name of the noise + +`data` + +table, the noise data: + +```lua +{ + type = "2d", -- or "3d" + np_vals = { -- noise params, see minetest lua api + offset = 0, + scale = 1, + spread = {x=384, y=256, z=384}, + seed = 5900033, + octaves = 5, + persist = 0.63, + lacunarity = 2.0, + flags = "" + }, + ymin = -31000, + ymax = 31000, +} +``` +NOTES: +`ymin` and `ymax` define a range in which to calculate the noise. This is useful +for making realms: a noise might not be needed in a certian y-range. Note that +if the position passed to `luamap.logic` is outside of the y-range, then +`noise_vals` will not contain that noise, which saves on calculation times. Be +sure to set the y-range to completely encompass the range that you expect to +find your noises. + +### luamap.precalc + +```lua +luamap.precalc(data, area, vm, minp, maxp, seed) +``` + +DESCRIPTION: An overridable function called just before setting the map lighting +and data. It offers compatability with biomegen, which needs these parameters to +function. Best practice is to save the old function and call it before adding +one's own logic, just like with `luamap.logic` + +### luamap.set_singlenode + +```lua +luamap.set_singlenode() +``` + +DESCRIPTION: Sets the mapgen to singlenode. Do not call this if you intend to +make a realm, only call if you intend to completely replace engine mapgens + +No params, returns nothing. + +## HELPER FUNCTIONS + +### luamap.remap + +```lua +luamap.remap(val, min_val, max_val, min_map, max_map) +``` + +DESCRIPTION: remaps a value between a range of values to a new range of values + +PARAMETERS: + +`val` +value to remap +`min_val` +old range minimum +`max_val` +old range maximum +`min_map` +new range min +`max_map` +new range max + +NOTES: + +Minetest's perlin noises range from -2 to 2. If you want a noise that ranges +from 0 to 1, then you can use remap: + +```lua +mynoise = noisevals.mynoise +mynoise = luamap.remap(mynoise, -2, 2, 0, 1) +``` +### luamap.lerp + +```lua +luamap.lerp(var_a, var_b, ratio, power) +``` +DESCRIPTION: a linear interpolation function +Interpolates between 2 values + +PARAMETERS: +`var_a` +first value to interpolate between + +`var_b` +second value to interpolate between + +`ratio` +number between 0 and 1 interpolation ratio: 0 is 100% var_a, 1 is 100% +var_b, values between 0 and 1 return a mix of var_a and var_b + +`power` +(optional, default 1) controls the exponent of the power. By default, it +is 1, which gives linear interpolation. 3 would give cubic interpolation. +Controls the shape of the interpolation curve. + +NOTES: + +useful for mixing two noises. If you use a third noise as the ratio, you can +have areas that are mostly controlled by one noise, and other areas controlled +by another noise. + +### luamap.coserp + +```lua +luamap.coserp(var_a,var_b,ratio) +``` + +DESCRIPTION: +Same as `luamap.lerp` but uses cosine interpolation for a smoother curve. + + + diff --git a/example.lua b/example.lua index 127ee1f..46333f0 100644 --- a/example.lua +++ b/example.lua @@ -1,4 +1,4 @@ --- forces the map into singlenode mode +-- forces the map into singlenode mode, don't do this if this is just a "realm". luamap.set_singlenode() -- creates a terrain noise @@ -14,6 +14,7 @@ luamap.register_noise("terrain",{ lacunarity = 2.0, flags = "" }, + }) diff --git a/init.lua b/init.lua index b84a7d7..9227934 100644 --- a/init.lua +++ b/init.lua @@ -7,20 +7,34 @@ function luamap.remap(val, min_val, max_val, min_map, max_map) return (val-min_val)/(max_val-min_val) * (max_map-min_map) + min_map end +-- linear interpolation, optional power modifier function luamap.lerp(var_a, var_b, ratio, power) + if ratio > 1 then ratio = 1 end + if ratio < 0 then ratio = 0 end power = power or 1 return (1-ratio)*(var_a^power) + (ratio*(var_b^power)) end +function luamap.coserp(var_a,var_b,ratio) + if ratio > 1 then ratio = 1 end + if ratio < 0 then ratio = 0 end + local rat2 = (1-math.cos(ratio*3.14159))/2 + return (var_a*(1-rat2)+var_b*rat2) +end + function luamap.register_noise(name,data) if data.type == "2d" then luamap.noises_2d[name] = {} luamap.noises_2d[name].np_vals = data.np_vals luamap.noises_2d[name].nobj = nil + luamap.noises_2d[name].ymin = data.ymin or -31000 + luamap.noises_2d[name].ymax = data.ymax or 31000 else -- 3d luamap.noises_3d[name] = {} luamap.noises_3d[name].np_vals = data.np_vals luamap.noises_3d[name].nobj = nil + luamap.noises_3d[name].ymin = data.ymin or -31000 + luamap.noises_3d[name].ymax = data.ymax or 31000 end end @@ -65,13 +79,23 @@ minetest.register_on_generated(function(minp, maxp, seed) local minpos2d = {x=minp.x, y=minp.z} for name,elements in pairs(noises_2d) do - noises_2d[name].nobj = noises_2d[name].nobj or minetest.get_perlin_map(noises_2d[name].np_vals, chulens2d) - noises_2d[name].nvals = noises_2d[name].nobj:get_2d_map_flat(minpos2d) + if emin.y >= elements.ymin and emax.y <= elements.ymax then + noises_2d[name].nobj = noises_2d[name].nobj or minetest.get_perlin_map(noises_2d[name].np_vals, chulens2d) + noises_2d[name].nvals = noises_2d[name].nobj:get_2d_map_flat(minpos2d) + noises_2d[name].use = true + else + noises_2d[name].use = false + end end for name,elements in pairs(noises_3d) do - noises_3d[name].nobj = noises_3d[name].nobj or minetest.get_perlin_map(noises_3d[name].np_vals, chulens3d) - noises_3d[name].nvals = noises_3d[name].nobj:get_3d_map_flat(minpos3d) + if emin.y >= elements.ymin and emax.y <= elements.ymax then + noises_3d[name].nobj = noises_3d[name].nobj or minetest.get_perlin_map(noises_3d[name].np_vals, chulens3d) + noises_3d[name].nvals = noises_3d[name].nobj:get_3d_map_flat(minpos3d) + noises_3d[name].use = true + else + noises_3d[name].use = false + end end local xstride, ystride, zstride = 1,sidelen,sidelen*sidelen @@ -86,14 +110,18 @@ minetest.register_on_generated(function(minp, maxp, seed) for x = minp.x, maxp.x do for name,elements in pairs(noises_2d) do - noise_vals[name] = elements.nvals[i2d] + if elements.use then + noise_vals[name] = elements.nvals[i2d] + end end local i3dy=i3dx for y = minp.y, maxp.y do local vi = area:index(x, y, z) for name,elements in pairs(noises_3d) do - noise_vals[name] = elements.nvals[i3dy] + if elements.use then + noise_vals[name] = elements.nvals[i3dy] + end end data[vi] = logic(noise_vals,x,y,z,seed) i3dy = i3dy + ystride