2022-04-09 18:23:01 +02:00

160 lines
4.9 KiB
Lua

local S = minetest.get_translator("perlin_test")
-- Holds the currently used Perlin noise
local current_perlin = {}
-- holds the current PerlinNoise object
current_perlin.noise = nil
-- dimensions of current Perlin noise (2 or 3)
current_perlin.dimensions = nil
-- distance from player center in each cardinal direction in which the Perlin noise is calculated and shown (squarius = "square radius")
current_perlin.squarius = nil
-- Theoretical min and max values for Perlin noise (for colorization)
current_perlin.max = 1
current_perlin.min = -1
-- Test nodes to generate a map based on a perlin noise
minetest.register_node("perlin_test:node", {
description = S("Perlin Test Node"),
paramtype2 = "color",
tiles = {"perlin_test_node.png"},
palette = "perlin_test_node_palette.png",
groups = { dig_immediate = 3 },
-- Force-drop without metadata to avoid spamming the inventory
drop = "perlin_test:node",
})
minetest.register_node("perlin_test:node_negative", {
description = S("Negative Perlin Test Node"),
paramtype2 = "color",
tiles = {"perlin_test_node_neg.png"},
palette = "perlin_test_node_palette_neg.png",
groups = { dig_immediate = 3 },
-- Force-drop without metadata to avoid spamming the inventory
drop = "perlin_test:node_negative",
})
local CONTENT_TEST_NODE = minetest.get_content_id("perlin_test:node")
local CONTENT_TEST_NODE_NEGATIVE = minetest.get_content_id("perlin_test:node_negative")
local update_map = function(pos)
local stats
if not current_perlin.noise then
return
end
local squarius_v = vector.new(current_perlin.squarius, current_perlin.squarius, current_perlin.squarius)
local startpos = pos
local endpos = vector.add(startpos, current_perlin.squarius*2+1)
local y_max = endpos.y - startpos.y
if current_perlin.dimensions == 2 then
y_max = 0
startpos.y = pos.y
endpos.y = pos.y
end
local vmanip = VoxelManip(startpos, endpos)
local emin, emax = vmanip:get_emerged_area()
local vdata = vmanip:get_data()
local vdata2 = vmanip:get_param2_data()
local varea = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
stats = {}
stats.avg = 0
local sum_of_values = 0
local value_count = 0
for x=0, endpos.x - startpos.x do
for y=0, y_max do
for z=0, endpos.z - startpos.z do
-- Get Perlin value at current pos
local relpos = vector.new(x,y,z)
local abspos = vector.add(startpos, relpos)
local perlin_value
if current_perlin.dimensions == 2 then
perlin_value = current_perlin.noise:get_2d({x=abspos.x, y=abspos.z})
elseif current_perlin.dimensions == 3 then
perlin_value = current_perlin.noise:get_3d(abspos)
else
error("[perlin_test] Unknown/invalid number of Perlin noise dimensions!")
return
end
-- Statistics
if not stats.min then
stats.min = perlin_value
elseif perlin_value < stats.min then
stats.min = perlin_value
end
if not stats.max then
stats.max = perlin_value
elseif perlin_value > stats.max then
stats.max = perlin_value
end
sum_of_values = sum_of_values + perlin_value
value_count = value_count + 1
-- Calculate color (param2) for node
local node_param2 = math.floor(math.abs(perlin_value) * 255)
node_param2 = math.max(0, math.min(255, node_param2))
-- Get vmanip index
local index = varea:indexp(abspos)
if not index then
return
end
-- Set node and param2
if perlin_value >= 0 then
vdata[index] = CONTENT_TEST_NODE
vdata2[index] = node_param2
else
if current_perlin.show_negative == true then
vdata[index] = CONTENT_TEST_NODE_NEGATIVE
vdata2[index] = node_param2
else
vdata[index] = minetest.CONTENT_AIR
vdata2[index] = 0
end
end
end
end
end
stats.avg = sum_of_values / value_count
-- Set vmanip, return stats
vmanip:set_data(vdata)
vmanip:set_param2_data(vdata2)
vmanip:calc_lighting()
vmanip:write_to_map()
return stats
end
-- Creates and demonstrates a Perlin noise.
-- * pos: Where the Perlin noise starts
-- * options: table with:
-- * dimensions: number of Perlin noise dimensions (2 or 3)
-- * squarius: distance from center to the edge of the square/cube of the whole Perlin noise area
-- * show_negative: if true, places nodes for negative Perlin values (default: true)
-- * noiseparams: NoiseParams table (see Minetest's Lua API documentation)
create_perlin = function(pos, options, noiseparams)
current_perlin.dimensions = options.dimensions
current_perlin.squarius = options.squarius
current_perlin.show_negative = options.show_negative
if current_perlin.show_negative == nil then
options.show_negative = true
end
current_perlin.noise = PerlinNoise(noiseparams)
local mpos = vector.round(pos)
local stats = update_map(mpos)
if stats then
minetest.chat_send_all(string.format("Perlin noise stats: min. value=%.3f, max. value=%.3f, avg. value=%.3f", stats.min, stats.max, stats.avg))
end
end
-- Clears the currently active Perlin noise
clear_perlin = function()
if current_perlin.noise then
current_perlin.noise = nil
end
end