mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-22 23:42:59 +00:00
Implement more of the noise command backend
It's still not hooked up yet though, and there's no command parsing logic either
This commit is contained in:
parent
cae75ec5a2
commit
ee2e5716eb
8 changed files with 113 additions and 32 deletions
38
worldeditadditions/lib/noise/apply_2d.lua
Normal file
38
worldeditadditions/lib/noise/apply_2d.lua
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
--- Applies the given noise field to the given heightmap.
|
||||||
|
-- Mutates the given heightmap.
|
||||||
|
-- @param heightmap number[] A table of ZERO indexed numbers representing the heghtmap - see worldeditadditions.make_heightmap().
|
||||||
|
-- @param noise number[] An table identical in structure to the heightmap containing the noise values to apply.
|
||||||
|
-- @param heightmap_size {x:number,z:number} A 2d vector representing the size of the heightmap.
|
||||||
|
-- @param region_height number The height of the defined region.
|
||||||
|
-- @param apply_mode string The apply mode to use to apply the noise to the heightmap.
|
||||||
|
-- @returns bool[,string] A boolean value representing whether the application was successful or not. If false, then an error message as a string is also returned describing the error that occurred.
|
||||||
|
function worldeditadditions.noise.apply_2d(heightmap, noise, heightmap_size, region_height, apply_mode)
|
||||||
|
if type(apply_mode) ~= "string" then
|
||||||
|
return false, "Error: Expected string value for apply_mode, but received value of type "..type(apply_mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
local percent = tonumber(apply_mode:match("^(%d+)%%$"))
|
||||||
|
|
||||||
|
for z = heightmap_size.z - 1, 0, -1 do
|
||||||
|
for x = heightmap_size.x - 1, 0, -1 do
|
||||||
|
local i = (z * heightmap_size.x) + x
|
||||||
|
|
||||||
|
if apply_mode == "add" then
|
||||||
|
heightmap[i] = heightmap[i] + noise[i]
|
||||||
|
elseif apply_mode == "multiply" then
|
||||||
|
heightmap[i] = heightmap[i] * noise[i]
|
||||||
|
elseif percent then
|
||||||
|
-- Rescale from 0 - 1 to -1 - +1
|
||||||
|
local rescaled = (noise[i] * 2) - 1
|
||||||
|
-- Rescale to match the percentage specified
|
||||||
|
rescaled = rescaled * region_height * percent
|
||||||
|
heightmap[i] = rescaled
|
||||||
|
else
|
||||||
|
return false, "Error: Unknown apply_mode '"..apply_mode.."'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
|
@ -1,8 +1,5 @@
|
||||||
local wea = worldeditadditions
|
local wea = worldeditadditions
|
||||||
|
|
||||||
-- original code by Ken Perlin: http://mrl.nyu.edu/~perlin/noise/
|
|
||||||
-- Port from this StackOverflow answer: https://stackoverflow.com/a/33425812/1460422
|
|
||||||
|
|
||||||
|
|
||||||
local function BitAND(a, b) --Bitwise and
|
local function BitAND(a, b) --Bitwise and
|
||||||
local p, c = 1, 0
|
local p, c = 1, 0
|
||||||
|
@ -29,10 +26,15 @@ local function grad(hash, x, y, z)
|
||||||
return ((h and 1) == 0 and u or - u) + ((h and 2) == 0 and v or - v)
|
return ((h and 1) == 0 and u or - u) + ((h and 2) == 0 and v or - v)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Perlin noise generation engine.
|
||||||
|
-- Original code by Ken Perlin: http://mrl.nyu.edu/~perlin/noise/
|
||||||
|
-- Port from this StackOverflow answer: https://stackoverflow.com/a/33425812/1460422
|
||||||
|
-- @class
|
||||||
|
local Perlin = {}
|
||||||
|
Perlin.__index = Perlin
|
||||||
|
|
||||||
wea.noise.perlin = {}
|
Perlin.p = {}
|
||||||
wea.noise.perlin.p = {}
|
Perlin.permutation = {
|
||||||
wea.noise.perlin.permutation = {
|
|
||||||
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140,
|
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140,
|
||||||
36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120,
|
36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120,
|
||||||
234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
|
234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
|
||||||
|
@ -50,28 +52,36 @@ wea.noise.perlin.permutation = {
|
||||||
115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29,
|
115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29,
|
||||||
24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
|
24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
|
||||||
}
|
}
|
||||||
wea.noise.perlin.size = 256
|
Perlin.size = 256
|
||||||
wea.noise.perlin.gx = {}
|
Perlin.gx = {}
|
||||||
wea.noise.perlin.gy = {}
|
Perlin.gy = {}
|
||||||
wea.noise.perlin.randMax = 256
|
Perlin.randMax = 256
|
||||||
|
|
||||||
--- Creates a new perlin instance.
|
--- Creates a new Perlin instance.
|
||||||
-- @return perlin A new perlin instance.
|
-- @return Perlin A new perlin instance.
|
||||||
function wea.noise.perlin.new()
|
function Perlin.new()
|
||||||
local instance = {}
|
local instance = {}
|
||||||
setmetatable(instance, wea.noise.perlin)
|
setmetatable(instance, Perlin)
|
||||||
instance:load()
|
instance:load()
|
||||||
return instance
|
return instance
|
||||||
end
|
end
|
||||||
|
|
||||||
function wea.noise.perlin:load()
|
--- Initialises this Perlin instance.
|
||||||
|
-- Called automatically by the constructor - you do NOT need to call this
|
||||||
|
-- yourself.
|
||||||
|
function Perlin:load()
|
||||||
for i = 1, self.size do
|
for i = 1, self.size do
|
||||||
self.p[i] = self.permutation[i]
|
self.p[i] = self.permutation[i]
|
||||||
self.p[255 + i] = self.p[i]
|
self.p[255 + i] = self.p[i]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function wea.noise.perlin:noise( x, y, z )
|
--- Returns a noise value for a given point in 3D space.
|
||||||
|
-- @param x number The x co-ordinate.
|
||||||
|
-- @param y number The y co-ordinate.
|
||||||
|
-- @param z number The z co-ordinate.
|
||||||
|
-- @returns number The calculated noise value.
|
||||||
|
function Perlin:noise( x, y, z )
|
||||||
local X = BitAND(math.floor(x), 255) + 1
|
local X = BitAND(math.floor(x), 255) + 1
|
||||||
local Y = BitAND(math.floor(y), 255) + 1
|
local Y = BitAND(math.floor(y), 255) + 1
|
||||||
local Z = BitAND(math.floor(z), 255) + 1
|
local Z = BitAND(math.floor(z), 255) + 1
|
||||||
|
@ -98,3 +108,5 @@ function wea.noise.perlin:noise( x, y, z )
|
||||||
lerp(u, grad(self.p[AB + 1], x, y - 1, z - 1),
|
lerp(u, grad(self.p[AB + 1], x, y - 1, z - 1),
|
||||||
grad(self.p[BB + 1], x - 1, y - 1, z - 1))))
|
grad(self.p[BB + 1], x - 1, y - 1, z - 1))))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return Perlin
|
||||||
|
|
12
worldeditadditions/lib/noise/init.lua
Normal file
12
worldeditadditions/lib/noise/init.lua
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
worldeditadditions.noise = {}
|
||||||
|
|
||||||
|
-- The command itself
|
||||||
|
dofile(worldeditadditions.modpath.."/lib/noise/noise2d.lua")
|
||||||
|
|
||||||
|
-- Dependencies
|
||||||
|
dofile(worldeditadditions.modpath.."/lib/noise/apply_2d.lua")
|
||||||
|
dofile(worldeditadditions.modpath.."/lib/noise/make_2d.lua")
|
||||||
|
dofile(worldeditadditions.modpath.."/lib/noise/params_apply_default.lua")
|
||||||
|
|
||||||
|
-- Noise generation engines
|
||||||
|
dofile(worldeditadditions.modpath.."/lib/noise/engines/perlin.lua")
|
|
@ -11,15 +11,13 @@
|
||||||
-- @param size Vector An x/y vector representing the size of the noise area to generate.
|
-- @param size Vector An x/y vector representing the size of the noise area to generate.
|
||||||
-- @param params table|table<table> A table of noise params to use to generate the noise. Values that aren't specified are filled in automatically. If a table of tables is specified, it is interpreted as multiple octaves of noise to apply in sequence.
|
-- @param params table|table<table> A table of noise params to use to generate the noise. Values that aren't specified are filled in automatically. If a table of tables is specified, it is interpreted as multiple octaves of noise to apply in sequence.
|
||||||
function worldeditadditions.noise.make_2d(size, params)
|
function worldeditadditions.noise.make_2d(size, params)
|
||||||
params = worldeditadditions.noise.params_apply_default(params)
|
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
local generator;
|
local generator;
|
||||||
if params.algorithm == "perlin" then
|
if params.algorithm == "perlin" then
|
||||||
generator = worldeditadditions.noise.perlin.new()
|
generator = worldeditadditions.noise.perlin.new()
|
||||||
else -- We don't have any other generators just yet
|
else -- We don't have any other generators just yet
|
||||||
return false, "Error: Unknown noise algorithm '"..params.."'."
|
return false, "Error: Unknown noise algorithm '"..params.."' (available algorithms: perlin)."
|
||||||
end
|
end
|
||||||
|
|
||||||
for x=params.offset.x, params.offset.x+size.x do
|
for x=params.offset.x, params.offset.x+size.x do
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
worldeditadditions.noise = {}
|
|
||||||
|
|
||||||
dofile(worldeditadditions.modpath.."/lib/noise/noise_params.lua")
|
|
||||||
dofile(worldeditadditions.modpath.."/lib/noise/make_2d.lua")
|
|
||||||
dofile(worldeditadditions.modpath.."/lib/noise/engines/perlin.lua")
|
|
|
@ -1,6 +1,8 @@
|
||||||
--- Applies a layer of 2D noise over the terrain in the defined region.
|
--- Applies a layer of 2D noise over the terrain in the defined region.
|
||||||
-- @module worldeditadditions.noise2d
|
-- @module worldeditadditions.noise2d
|
||||||
|
|
||||||
|
local wea = worldeditadditions
|
||||||
|
|
||||||
-- ███ ██ ██████ ██ ███████ ███████ ██████ ██████
|
-- ███ ██ ██████ ██ ███████ ███████ ██████ ██████
|
||||||
-- ████ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
-- ████ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||||
-- ██ ██ ██ ██ ██ ██ ███████ █████ █████ ██ ██
|
-- ██ ██ ██ ██ ██ ██ ███████ █████ █████ ██ ██
|
||||||
|
@ -10,10 +12,14 @@
|
||||||
-- @param pos1 Vector pos1 of the defined region
|
-- @param pos1 Vector pos1 of the defined region
|
||||||
-- @param pos2 Vector pos2 of the defined region
|
-- @param pos2 Vector pos2 of the defined region
|
||||||
-- @param noise_params table A noise parameters table. Will be passed unmodified to PerlinNoise() from the Minetest API.
|
-- @param noise_params table A noise parameters table. Will be passed unmodified to PerlinNoise() from the Minetest API.
|
||||||
function worldeditadditions.noise2d(pos1, pos2, noise_params)
|
function worldeditadditions.noise.noise2d(pos1, pos2, noise_params)
|
||||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||||
|
local region_height = pos1.y - pos2.y
|
||||||
-- pos2 will always have the highest co-ordinates now
|
-- pos2 will always have the highest co-ordinates now
|
||||||
|
|
||||||
|
-- Fill in the default params
|
||||||
|
noise_params = worldeditadditions.noise.params_apply_default(noise_params)
|
||||||
|
|
||||||
-- Fetch the nodes in the specified area
|
-- Fetch the nodes in the specified area
|
||||||
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
||||||
local data = manip:get_data()
|
local data = manip:get_data()
|
||||||
|
@ -25,14 +31,25 @@ function worldeditadditions.noise2d(pos1, pos2, noise_params)
|
||||||
)
|
)
|
||||||
local heightmap_new = worldeditadditions.table.shallowcopy(heightmap_old)
|
local heightmap_new = worldeditadditions.table.shallowcopy(heightmap_old)
|
||||||
|
|
||||||
local perlin_map = PerlinNoiseMap(noise_params, heightmap_size)
|
local noisemap = wea.noise.make_2d(noise_params, heightmap_size)
|
||||||
|
|
||||||
-- TODO: apply the perlin noise map here
|
wea.noise.apply_2d(
|
||||||
|
heightmap_new,
|
||||||
|
noisemap,
|
||||||
|
heightmap_size,
|
||||||
|
region_height,
|
||||||
|
noise_params.apply
|
||||||
|
)
|
||||||
|
|
||||||
local stats = { added = 0, removed = 0 }
|
local success, stats = wea.apply_heightmap_changes(
|
||||||
|
pos1, pos2,
|
||||||
|
area, data,
|
||||||
|
heightmap_old, heightmap_new,
|
||||||
|
heightmap_size
|
||||||
|
)
|
||||||
|
if not success then return success, stats end
|
||||||
|
|
||||||
-- Save the modified nodes back to disk & return
|
-- Save the modified nodes back to disk & return
|
||||||
-- No need to save - this function doesn't actually change anything
|
|
||||||
worldedit.manip_helpers.finish(manip, data)
|
worldedit.manip_helpers.finish(manip, data)
|
||||||
|
|
||||||
return true, stats
|
return true, stats
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
|
local wea = worldeditadditions
|
||||||
|
|
||||||
--- Makes sure that the default settings are all present in the given params table.
|
--- Makes sure that the default settings are all present in the given params table.
|
||||||
-- This way not all params have to be specified by the user.
|
-- This way not all params have to be specified by the user.
|
||||||
-- @param params table The params table generated from the user's input.
|
-- @param params table The params table generated from the user's input.
|
||||||
-- @return table A NEW table with all the missing properties filled in with the default values.
|
-- @return table A NEW table with all the missing properties filled in with the default values.
|
||||||
function worldeditadditions.noise.params_apply_default(params)
|
function worldeditadditions.noise.params_apply_default(params)
|
||||||
local params_default = {
|
local params_default = {
|
||||||
|
-- How to apply the noise to the heightmap. Different values result in
|
||||||
|
-- different effects:
|
||||||
|
-- - The exact string "add": Noise values are added to each heightmap pixel.
|
||||||
|
-- - The exact string "multiply": Each heightmap pixel is multiplied by the corresponding noise value.
|
||||||
|
-- - A string in the form of digits followed by a percent sign (e.g. "40%"), then the noise will is remapped from the range 0 - 1 to the range -1 - +1, and then for each pixel in the heightmap will be altered at most the given percentage of the total height of the defined region.
|
||||||
|
apply = "40%",
|
||||||
|
-- The backend noise algorithm to use
|
||||||
algorithm = "perlin",
|
algorithm = "perlin",
|
||||||
-- Zooms in and out
|
-- Zooms in and out
|
||||||
scale = vector.new(1, 1, 1),
|
scale = wea.Vector3.new(1, 1, 1),
|
||||||
-- Offset the generated noise by this vector.
|
-- Offset the generated noise by this vector.
|
||||||
offset = vector.new(0, 0, 0),
|
offset = wea.Vector3.new(0, 0, 0),
|
||||||
-- Apply this exponent to the resulting noise value
|
-- Apply this exponent to the resulting noise value
|
||||||
exponent = 1,
|
exponent = 1,
|
||||||
-- Multiply the resulting noise value by this number. Changes the "strength" of the noise
|
-- Multiply the resulting noise value by this number. Changes the "strength" of the noise
|
|
@ -92,7 +92,7 @@ end
|
||||||
-- @param heightmap_old number[] The original heightmap from worldeditadditions.make_heightmap.
|
-- @param heightmap_old number[] The original heightmap from worldeditadditions.make_heightmap.
|
||||||
-- @param heightmap_new number[] The new heightmap containing the altered updated values. It is expected that worldeditadditions.table.shallowcopy be used to make a COPY of the data worldeditadditions.make_heightmap for this purpose. Both heightmap_old AND heightmap_new are REQUIRED in order for this function to work.
|
-- @param heightmap_new number[] The new heightmap containing the altered updated values. It is expected that worldeditadditions.table.shallowcopy be used to make a COPY of the data worldeditadditions.make_heightmap for this purpose. Both heightmap_old AND heightmap_new are REQUIRED in order for this function to work.
|
||||||
-- @param heightmap_size vector The x / z size of the heightmap. Any y value set in the vector is ignored.
|
-- @param heightmap_size vector The x / z size of the heightmap. Any y value set in the vector is ignored.
|
||||||
--
|
-- @returns bool, string|{ added: number, removed: number } A bool indicating whether the operation was successful or not, followed by either an error message as a string (if it was not successful) or a table of statistics (if it was successful).
|
||||||
function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heightmap_old, heightmap_new, heightmap_size)
|
function worldeditadditions.apply_heightmap_changes(pos1, pos2, area, data, heightmap_old, heightmap_new, heightmap_size)
|
||||||
local stats = { added = 0, removed = 0 }
|
local stats = { added = 0, removed = 0 }
|
||||||
local node_id_air = minetest.get_content_id("air")
|
local node_id_air = minetest.get_content_id("air")
|
||||||
|
|
Loading…
Reference in a new issue