Add more code documentation
This commit is contained in:
parent
eb2db02321
commit
9b0f04df18
154
init.lua
154
init.lua
@ -1,6 +1,10 @@
|
||||
local S = minetest.get_translator("perlin_explorer")
|
||||
local F = minetest.formspec_escape
|
||||
|
||||
-----------------------------
|
||||
-- Variable initialization --
|
||||
-----------------------------
|
||||
|
||||
local mod_storage = minetest.get_mod_storage()
|
||||
|
||||
-- If true, the Perlin test nodes will support color
|
||||
@ -98,6 +102,8 @@ local formspec_states = {}
|
||||
-- * "user": Profiles created by user. Deletable
|
||||
local np_profiles = {}
|
||||
|
||||
-- The default noiseparams are used as the initial noiseparams
|
||||
-- or as fallback when stuff fails.
|
||||
local default_noiseparams = {
|
||||
offset = 0.0,
|
||||
scale = 1.0,
|
||||
@ -223,9 +229,10 @@ current_options.autogen = false
|
||||
-- Index: Hash of node position, value: true if loaded
|
||||
local loaded_areas = {}
|
||||
|
||||
------------
|
||||
----------------------
|
||||
-- Helper functions --
|
||||
----------------------
|
||||
|
||||
-- Helper functions
|
||||
-- Reduce the pos coordinates down to the closest numbers divisible by sidelen
|
||||
local sidelen_pos = function(pos, sidelen)
|
||||
local newpos = {x=pos.x, y=pos.y, z=pos.z}
|
||||
@ -263,6 +270,8 @@ local unique_formspec_spaces = function(player_name, formspec)
|
||||
return formspec .. filler
|
||||
end
|
||||
|
||||
-- Takes the 3 noise flags default/eased/absvalue (either true or false)
|
||||
-- and converts them to a string
|
||||
local build_flags_string = function(defaults, eased, absvalue)
|
||||
local flagst = {}
|
||||
if defaults then
|
||||
@ -281,6 +290,12 @@ local build_flags_string = function(defaults, eased, absvalue)
|
||||
local flags = table.concat(flagst, ",")
|
||||
return flags
|
||||
end
|
||||
-- Takes a flags string (in the noiseparams format)
|
||||
-- and returns a table of the form {
|
||||
-- defaults = true/false
|
||||
-- eased = true/false
|
||||
-- absvalue = true/false
|
||||
-- }.
|
||||
local parse_flags_string = function(flags)
|
||||
local ftable = string.split(flags, ",")
|
||||
local defaults, eased, absvalue = false, false, false
|
||||
@ -379,7 +394,14 @@ minetest.register_on_mods_loaded(function()
|
||||
end
|
||||
end)
|
||||
|
||||
-- Test nodes to generate a map based on a perlin noise
|
||||
-----------
|
||||
-- Nodes --
|
||||
-----------
|
||||
|
||||
-- Add a bunch of nodes to generate a map based on a perlin noise.
|
||||
-- Used for visualization.
|
||||
-- Each nodes comes in a "high" and "low" variant.
|
||||
-- high/low means it represents high/low noise values.
|
||||
local paramtype2, palette
|
||||
if COLORIZE_NODES then
|
||||
paramtype2 = "color"
|
||||
@ -392,9 +414,13 @@ else
|
||||
palette_low = "perlin_explorer_node_palette_low.png"
|
||||
end
|
||||
|
||||
-- Solid nodes: Visible, walkable, opaque
|
||||
minetest.register_node("perlin_explorer:node", {
|
||||
description = S("Solid Perlin Test Node (High Value)"),
|
||||
paramtype = "light",
|
||||
-- Intentionally does not cast shadow so that
|
||||
-- cave structures are always fullbright when
|
||||
-- the sun shines.
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = paramtype2,
|
||||
tiles = {"perlin_explorer_node.png"},
|
||||
@ -415,6 +441,8 @@ minetest.register_node("perlin_explorer:node_low", {
|
||||
drop = "perlin_explorer:node_low",
|
||||
})
|
||||
|
||||
-- Grid nodes: See-through, walkable. Looks like glass.
|
||||
-- Useful to see "inside" of 3D blobs.
|
||||
minetest.register_node("perlin_explorer:grid", {
|
||||
description = S("Grid Perlin Test Node (High Value)"),
|
||||
paramtype = "light",
|
||||
@ -441,6 +469,9 @@ minetest.register_node("perlin_explorer:grid_low", {
|
||||
drop = "perlin_explorer:grid_low",
|
||||
})
|
||||
|
||||
-- Minibox nodes: See-through, non-walkable, climbable.
|
||||
-- Looks like dot clouds in large numbers.
|
||||
-- Useful to see and move "inside" of 3D blobs.
|
||||
minetest.register_node("perlin_explorer:mini", {
|
||||
description = S("Minibox Perlin Test Node (High Value)"),
|
||||
paramtype = "light",
|
||||
@ -478,6 +509,14 @@ minetest.register_node("perlin_explorer:mini_low", {
|
||||
drop = "perlin_explorer:mini_low",
|
||||
})
|
||||
|
||||
-- Helper function for the getter tool. Gets a noise value at pos
|
||||
-- and print is in user's chat.
|
||||
-- * pos: Position to get
|
||||
-- * user: Player object
|
||||
-- * precision: Coordinate precision of output (for minetest.pos_to_string)
|
||||
-- * ptype: One of ...
|
||||
-- "node": Get value at node position
|
||||
-- "player": Get value at player position
|
||||
local print_value = function(pos, user, precision, ptype)
|
||||
local val
|
||||
local getpos = sidelen_pos(pos, current_options.sidelen)
|
||||
@ -503,7 +542,7 @@ local print_value = function(pos, user, precision, ptype)
|
||||
minetest.chat_send_player(user:get_player_name(), msg)
|
||||
end
|
||||
|
||||
-- Get Perlin value of player pos
|
||||
-- Get Perlin value of player pos (on_use callback)
|
||||
local use_getter = function(itemstack, user, pointed_thing)
|
||||
if not user then
|
||||
return
|
||||
@ -528,7 +567,7 @@ local use_getter = function(itemstack, user, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
-- Get Perlin value of pointed node
|
||||
-- Get Perlin value of pointed node (on_place callback)
|
||||
local place_getter = function(itemstack, user, pointed_thing)
|
||||
if not user then
|
||||
return
|
||||
@ -551,6 +590,7 @@ local place_getter = function(itemstack, user, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
-- Gets perlin noise value
|
||||
minetest.register_tool("perlin_explorer:getter", {
|
||||
description = S("Perlin Value Getter"),
|
||||
_tt_help = S("Place: Display Perlin noise value of the pointed node position").."\n"..
|
||||
@ -562,13 +602,35 @@ minetest.register_tool("perlin_explorer:getter", {
|
||||
on_place = place_getter,
|
||||
})
|
||||
|
||||
-- Calculate the Perlin nois valued and generate nodes.
|
||||
-- * pos: Bottom front left position of where Perlin noise begins
|
||||
-- * noise: Perlin noise object to use
|
||||
-- * noiseparams: noise parameters table
|
||||
-- * options: see at create_perlin function
|
||||
-- * stats_mode: if true, will only calculate values for statistics but not place any nodes
|
||||
local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
--[[ Calculate the Perlin noise value and optionally generate nodes.
|
||||
* pos: Bottom front left position of where Perlin noise begins
|
||||
* noise: Perlin noise object to use
|
||||
* noiseparams: noise parameters table
|
||||
* options: see at create_perlin function
|
||||
* stats_mode: if true, will only calculate values for statistics but not place any nodes
|
||||
Returns: a stats table of the form
|
||||
{
|
||||
min, -- minimum calculated noise value
|
||||
max, -- maximum calculated noise value
|
||||
avg, -- average calculated noise value
|
||||
value_count, -- number of values that were calculated
|
||||
histogram, -- histogram data for the stats screen. A list
|
||||
-- of "buckets", starting with the lower bounds:
|
||||
{
|
||||
[1] = <number of values in 1st bucket>,
|
||||
[2] = <number of values in 2nd bucket>,
|
||||
-- ... etc. ...
|
||||
[HISTOGRAM_BUCKETS] = <number of values in last bucket>,
|
||||
}
|
||||
histogram_points, -- List of cutoff points for each of the
|
||||
-- data buckets
|
||||
start_pos, -- lower corner of the area that the stats were calculated for
|
||||
end_pos, -- upper corner of the area that the stats were calculated for
|
||||
}
|
||||
Returns nil on error.
|
||||
]]
|
||||
local calculate_noise = function(pos, noise, noiseparams, options, stats_mode)
|
||||
-- Init
|
||||
local stats
|
||||
if not noise then
|
||||
return
|
||||
@ -586,6 +648,7 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
|
||||
local y_max = endpos.y - startpos.y
|
||||
if options.dimensions == 2 then
|
||||
-- We don't need 3rd axis in 2D
|
||||
y_max = 0
|
||||
startpos.y = pos.y
|
||||
endpos.y = pos.y
|
||||
@ -595,6 +658,7 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
local vmanip, emin, emax, vdata, vdata2, varea
|
||||
local content_test_node, content_test_node_low, node_needs_color
|
||||
if not stats_mode then
|
||||
-- Only needed when we want to place nodes
|
||||
vmanip = VoxelManip(startpos, endpos)
|
||||
emin, emax = vmanip:get_emerged_area()
|
||||
vdata = vmanip:get_data()
|
||||
@ -606,6 +670,7 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
node_needs_color = nodetypes[options.nodetype][4]
|
||||
end
|
||||
|
||||
-- Init stats
|
||||
stats = {}
|
||||
stats.avg = 0
|
||||
local sum_of_values = 0
|
||||
@ -614,6 +679,8 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
stats.histogram = {}
|
||||
local min_possible, max_possible = analyze_noiseparams(noiseparams)
|
||||
local cutoff_points = {}
|
||||
-- Calculate the cutoff points for the histogram so we know in which data bucket
|
||||
-- to put each value into.
|
||||
for d=1,HISTOGRAM_BUCKETS do
|
||||
cutoff_points[d] = min_possible + ((max_possible-min_possible) / HISTOGRAM_BUCKETS) * d
|
||||
stats.histogram[d] = 0
|
||||
@ -622,6 +689,7 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
local perlin_map
|
||||
if not stats_mode then
|
||||
-- Initialize Perlin map
|
||||
-- The noise values will come from this (unless in Stats Mode)
|
||||
local perlin_map_object = PerlinNoiseMap(noiseparams, size_v)
|
||||
if options.dimensions == 2 then
|
||||
perlin_map = perlin_map_object:get_2d_map({x=startpos.x, y=startpos.z})
|
||||
@ -640,12 +708,16 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
z_max = endpos.z - startpos.z
|
||||
end
|
||||
|
||||
-- Main loop (time-critical!)
|
||||
for x=0, x_max do
|
||||
for y=0, y_max do
|
||||
for z=0, z_max do
|
||||
-- Note: This loop has been optimized for speed, so the code
|
||||
-- might not look pretty.
|
||||
-- Be careful of the performance implications when touching this
|
||||
-- loop
|
||||
|
||||
-- Get Perlin value at current pos
|
||||
-- Note: This section has been optimized. Don’t introduce stuff like
|
||||
-- vectors.new() here.
|
||||
local abspos
|
||||
if not stats_mode then
|
||||
abspos = {
|
||||
@ -668,6 +740,7 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
z = abspos_get.z - startpos.z + 1,
|
||||
}
|
||||
|
||||
-- Finally get the noise value
|
||||
local perlin_value
|
||||
if options.dimensions == 2 then
|
||||
if stats_mode or (indexpos.x < 1 or indexpos.z < 1) then
|
||||
@ -717,9 +790,11 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
break
|
||||
end
|
||||
end
|
||||
-- More stats
|
||||
sum_of_values = sum_of_values + perlin_value
|
||||
stats.value_count = stats.value_count + 1
|
||||
|
||||
-- This section will set the node
|
||||
if not stats_mode then
|
||||
-- Calculate color (param2) for node
|
||||
local zeropoint = options.mid_color
|
||||
@ -745,7 +820,7 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
return
|
||||
end
|
||||
|
||||
-- Set node and param2
|
||||
-- Set node and param2 in vmanip
|
||||
if perlin_value >= zeropoint then
|
||||
if options.buildmode == BUILDMODE_ALL or options.buildmode == BUILDMODE_HIGH_ONLY or options.buildmode == BUILDMODE_AUTO then
|
||||
vdata[index] = content_test_node
|
||||
@ -767,13 +842,14 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Final stats
|
||||
stats.avg = sum_of_values / stats.value_count
|
||||
stats.histogram_points = cutoff_points
|
||||
stats.start_pos = vector.new(startpos.x, startpos.y, startpos.z)
|
||||
stats.end_pos = vector.add(stats.start_pos, vector.new(options.size, options.size, options.size))
|
||||
|
||||
if not stats_mode then
|
||||
-- Set vmanip, return stats
|
||||
-- Write all the changes to map
|
||||
vmanip:set_data(vdata)
|
||||
vmanip:set_param2_data(vdata2)
|
||||
vmanip:write_to_map()
|
||||
@ -783,11 +859,10 @@ local update_map = function(pos, noise, noiseparams, options, stats_mode)
|
||||
local timediff = time2 - time1
|
||||
minetest.log("verbose", "[perlin_explorer] Noisechunk calculated/generated in "..timediff.." µs")
|
||||
|
||||
|
||||
return stats
|
||||
end
|
||||
|
||||
-- Creates and demonstrates a Perlin noise.
|
||||
-- Initiates Perlin noise calculation and optionally node generation, too.
|
||||
-- * pos: Where the Perlin noise starts
|
||||
-- * noise: Perlin noise object
|
||||
-- * noiseparams: noise parameters table
|
||||
@ -804,7 +879,7 @@ local create_perlin = function(pos, noise, noiseparams, options, stats_mode)
|
||||
local cpos = table.copy(pos)
|
||||
local mpos = vector.round(cpos)
|
||||
|
||||
local stats = update_map(mpos, noise, noiseparams, local_options, stats_mode)
|
||||
local stats = calculate_noise(mpos, noise, noiseparams, local_options, stats_mode)
|
||||
|
||||
if mapgen_star and not stats_mode then
|
||||
-- Show a particle in the center of the newly generated area
|
||||
@ -834,6 +909,9 @@ local create_perlin = function(pos, noise, noiseparams, options, stats_mode)
|
||||
end
|
||||
end
|
||||
|
||||
-- Seeder tool helper function.
|
||||
-- * player: Player object
|
||||
-- * regen: If true, force map section to regenerate (if autogen is off)
|
||||
local seeder_reseed = function(player, regen)
|
||||
local msg
|
||||
if regen and (not current_options.autogen and current_options.pos) then
|
||||
@ -849,6 +927,9 @@ local seeder_reseed = function(player, regen)
|
||||
end
|
||||
end
|
||||
|
||||
-- Generates a function for the seeder tool callbacks, for the
|
||||
-- on_use, on_secondary_use, on_place callbacks of that tool.
|
||||
-- * reseed : If true, force map section to regenerate (if autogen is off)
|
||||
local function seeder_use(reseed)
|
||||
return function(itemstack, user, pointed_thing)
|
||||
if not user then
|
||||
@ -887,9 +968,10 @@ local fix_noiseparams = function(noiseparams)
|
||||
return noiseparams
|
||||
end
|
||||
|
||||
-- Tool to set new random seed of the noiseparams
|
||||
minetest.register_tool("perlin_explorer:seeder", {
|
||||
description = S("Random Perlin seed setter"),
|
||||
_tt_help = S("Punch: Set a random seed for the current Perlin noise params").."\n"..
|
||||
_tt_help = S("Punch: Set a random seed for the active Perlin noise parameters").."\n"..
|
||||
S("Place: Set a random seed and regenerate nodes (if applicable)"),
|
||||
inventory_image = "perlin_explorer_seeder.png",
|
||||
wield_image = "perlin_explorer_seeder.png",
|
||||
@ -899,6 +981,9 @@ minetest.register_tool("perlin_explorer:seeder", {
|
||||
on_place = seeder_use(true),
|
||||
})
|
||||
|
||||
-------------------
|
||||
-- Chat commands --
|
||||
-------------------
|
||||
minetest.register_chatcommand("perlin_set_options", {
|
||||
privs = { server = true },
|
||||
description = S("Set Perlin map generation options"),
|
||||
@ -1032,6 +1117,9 @@ minetest.register_chatcommand("perlin_generate", {
|
||||
end,
|
||||
})
|
||||
|
||||
-- Show an error window to player with the given message.
|
||||
-- Set analyze_button to true to add a button "Analyze now"
|
||||
-- to open the analyze window.
|
||||
local show_error_formspec = function(player, message, analyze_button)
|
||||
local buttons
|
||||
if analyze_button then
|
||||
@ -1055,6 +1143,7 @@ local show_error_formspec = function(player, message, analyze_button)
|
||||
minetest.show_formspec(player:get_player_name(), "perlin_explorer:error", form)
|
||||
end
|
||||
|
||||
-- Stats loading screen
|
||||
local show_histogram_loading_formspec = function(player)
|
||||
local form = [[
|
||||
formspec_version[4]size[11,2]
|
||||
@ -1070,6 +1159,7 @@ local show_histogram_loading_formspec = function(player)
|
||||
minetest.show_formspec(player:get_player_name(), "perlin_explorer:histogram_loading", form)
|
||||
end
|
||||
|
||||
-- Stats screen
|
||||
local show_histogram_formspec = function(player, options, stats)
|
||||
local txt = ""
|
||||
local maxh = 6.0
|
||||
@ -1215,6 +1305,11 @@ local analyze_noiseparams_and_show_formspec = function(player, noiseparams)
|
||||
minetest.show_formspec(player:get_player_name(), "perlin_explorer:analyze", form)
|
||||
end
|
||||
|
||||
-- Show the formspec of the Perlin Noise Creator tool to player.
|
||||
-- The main formspec of this mod!
|
||||
-- * player: Player object
|
||||
-- * noiseparams: Initialize formspec with these noiseparams (optional)
|
||||
-- * profile_id: Preselect this profile (by table index of np_profiles)
|
||||
local show_noise_formspec = function(player, noiseparams, profile_id)
|
||||
local player_name = player:get_player_name()
|
||||
local np
|
||||
@ -1394,6 +1489,7 @@ local show_noise_formspec = function(player, noiseparams, profile_id)
|
||||
minetest.show_formspec(player_name, "perlin_explorer:creator", form)
|
||||
end
|
||||
|
||||
-- Formspec handling
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
-- Require 'server' priv
|
||||
local privs = minetest.get_player_privs(player:get_player_name())
|
||||
@ -1660,6 +1756,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end
|
||||
end)
|
||||
|
||||
-- The main tool of this mod. Opens the Perlin noise creation window.
|
||||
minetest.register_tool("perlin_explorer:creator", {
|
||||
description = S("Perlin Noise Creator"),
|
||||
_tt_help = S("Punch to open the Perlin noise creation menu"),
|
||||
@ -1679,6 +1776,13 @@ minetest.register_tool("perlin_explorer:creator", {
|
||||
end,
|
||||
})
|
||||
|
||||
-- Automatic map generation (autogen).
|
||||
-- The autogen is kind of a mapgen, but it doesn't use Minetest's
|
||||
-- mapgen, instead it writes directly to map. The benefit is
|
||||
-- that this will instantly update when the active noiseparams
|
||||
-- are changed.
|
||||
-- This will generate so-called noisechunks, which are like
|
||||
-- Minetest mapchunks, except with respect to this mod only.
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
@ -1688,9 +1792,11 @@ minetest.register_globalstep(function(dtime)
|
||||
timer = 0
|
||||
if current_perlin.noise and current_options.autogen then
|
||||
local players = minetest.get_connected_players()
|
||||
-- Generate close to all connected players
|
||||
for p=1, #players do
|
||||
local player = players[p]
|
||||
local player_name = player:get_player_name()
|
||||
-- Helper function for building
|
||||
local build = function(pos, pos_hash, player_name)
|
||||
if not pos or not pos.x or not pos.y or not pos.z then
|
||||
minetest.log("error", "[perlin_explorer] build(): Invalid pos!")
|
||||
@ -1703,11 +1809,15 @@ minetest.register_globalstep(function(dtime)
|
||||
if not loaded_areas[pos_hash] then
|
||||
local opts = table.copy(current_options)
|
||||
opts.size = AUTOBUILD_SIZE
|
||||
-- This actually builds the nodes
|
||||
create_perlin(pos, current_perlin.noise, current_perlin.noiseparams, opts, false)
|
||||
-- Built chunks are marked so they don't get generated over and over
|
||||
-- again
|
||||
loaded_areas[pos_hash] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Build list of coordinates to generate nodes in
|
||||
local pos = vector.round(player:get_pos())
|
||||
pos = sidelen_pos(pos, AUTOBUILD_SIZE)
|
||||
local neighbors = { vector.new(0, 0, 0) }
|
||||
@ -1739,6 +1849,7 @@ minetest.register_globalstep(function(dtime)
|
||||
if #noisechunks > 0 then
|
||||
minetest.log("verbose", "[perlin_explorer] Started building "..#noisechunks.." noisechunk(s)")
|
||||
end
|
||||
-- Build the nodes!
|
||||
for c=1, #noisechunks do
|
||||
local npos = noisechunks[c][1]
|
||||
local nhash = noisechunks[c][2]
|
||||
@ -1751,6 +1862,7 @@ minetest.register_globalstep(function(dtime)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Player variable init and cleanup
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
local flags = default_noiseparams.flags
|
||||
@ -1761,6 +1873,8 @@ minetest.register_on_joinplayer(function(player)
|
||||
absvalue = flags_table.absvalue,
|
||||
sidelen = 1,
|
||||
noiseparams = table.copy(default_noiseparams),
|
||||
|
||||
-- This is used by the `unique_formspec_spaces` hack
|
||||
sequence_number = 0,
|
||||
}
|
||||
end)
|
||||
|
Loading…
x
Reference in New Issue
Block a user