Minetest-WorldEditAdditions/worldeditadditions/lib/sculpt/apply_heightmap.lua
2023-08-03 02:42:46 +01:00

60 lines
2.7 KiB
Lua

local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3
---
-- @module worldeditadditions.sculpt
--- Applies the given brush at the given x/z position to the given heightmap.
-- Important: Where a Vector3 is mentioned in the parameter list, it reall MUST
-- be a Vector3 instance.
-- Also important: Remember that the position there is RELATIVE TO THE HEIGHTMAP'S origin (0, 0) and is on the X and Z axes!
-- @param brush table The ZERO-indexed brush to apply. Values should be normalised to be between 0 and 1.
-- @param brush_size Vector3 The size of the brush on the x/y axes.
-- @pram height number The multiplier to apply to each brush pixel value just before applying it. Negative values are allowed - this will cause a subtraction operation instead of an addition.
-- @param position Vector3 The position RELATIVE TO THE HEIGHTMAP on the x/z coordinates to centre the brush application on.
-- @param heightmap table The heightmap to apply the brush to. See worldeditadditions.make_heightmap for how to obtain one of these.
-- @param heightmap_size Vector3 The size of the aforementioned heightmap. See worldeditadditions.make_heightmap for more information.
-- @returns true,number,number|false,string If the operation was not successful, then false followed by an error message as a string is returned. If it was successful however, 3 values are returned: true, then the number of nodes added, then the number of nodes removed.
local function apply_heightmap(brush, brush_size, height, position, heightmap, heightmap_size)
-- Convert brush_size to match the scheme used in the heightmap
brush_size = brush_size:clone()
brush_size.z = brush_size.y
brush_size.y = 0
local brush_radius = (brush_size/2):ceil() - 1
local pos_start = (position - brush_radius)
:clamp(Vector3.new(0, 0, 0), heightmap_size)
local pos_end = (pos_start + brush_size)
:clamp(Vector3.new(0, 0, 0), heightmap_size)
local added = 0
local removed = 0
-- TODO: Make the strength of the brush depend on the height of the nodes in the heightmap?
-- Iterate over the heightmap and apply the brush
-- Note that we do not iterate over the brush, because we don't know if the
-- brush actually fits inside the region.... O.o
for z = pos_end.z - 1, pos_start.z, -1 do
for x = pos_end.x - 1, pos_start.x, -1 do
local hi = z*heightmap_size.x + x
local pos_brush = Vector3.new(x, 0, z) - pos_start
local bi = pos_brush.z*brush_size.x + pos_brush.x
local adjustment = math.floor(brush[bi]*height)
if adjustment > 0 then
added = added + adjustment
elseif adjustment < 0 then
removed = removed + math.abs(adjustment)
end
heightmap[hi] = heightmap[hi] + adjustment
end
end
return true, added, removed
end
return apply_heightmap