diff --git a/worldeditadditions/lib/sculpt/import_static.lua b/worldeditadditions/lib/sculpt/import_static.lua new file mode 100644 index 0000000..92f628a --- /dev/null +++ b/worldeditadditions/lib/sculpt/import_static.lua @@ -0,0 +1,23 @@ +local wea = worldeditadditions +local Vector3 = wea.Vector3 + +local parse_static = dofile(wea.modpath.."/lib/sculpt/parse_static.lua") + +--- Reads and parses the brush stored in the specified file. +-- @param filepath string The path to file that contains the static brush to read in. +-- @returns true,table,Vector3|false,string A success boolean, followed either by an error message as a string or the brush (as a table) and it's size (as an X/Y Vector3) +return function(filepath) + local handle = io.open(filepath) + if not handle then + handle:close() + return false, "Error: Failed to open the static brush file at '"..filepath.."'." + end + + local data = handle:read("*all") + handle:close() + + local success, brush, brush_size = parse_static(data) + if not success then return success, brush end + + return true, brush, brush_size +end diff --git a/worldeditadditions/lib/sculpt/init.lua b/worldeditadditions/lib/sculpt/init.lua index 98cde48..89f928a 100644 --- a/worldeditadditions/lib/sculpt/init.lua +++ b/worldeditadditions/lib/sculpt/init.lua @@ -14,9 +14,14 @@ local sculpt = { preview_brush = dofile(wea.modpath.."/lib/sculpt/preview_brush.lua"), read_brush_static = dofile(wea.modpath.."/lib/sculpt/read_brush_static.lua"), apply_heightmap = dofile(wea.modpath.."/lib/sculpt/apply_heightmap.lua"), - apply = dofile(wea.modpath.."/lib/sculpt/apply.lua") + apply = dofile(wea.modpath.."/lib/sculpt/apply.lua"), + scan_static = dofile(wea.modpath.."/lib/sculpt/scan_static.lua"), + import_static = dofile(wea.modpath.."/lib/sculpt/import_static.lua"), + parse_static = dofile(wea.modpath.."/lib/sculpt/parse_static.lua") } +sculpt.scan_static(wea.modpath.."/lib/sculpt/brushes") + return sculpt -- TODO: Automatically find & register all text file based brushes in the brushes directory diff --git a/worldeditadditions/lib/sculpt/parse_static.lua b/worldeditadditions/lib/sculpt/parse_static.lua new file mode 100644 index 0000000..4570ea1 --- /dev/null +++ b/worldeditadditions/lib/sculpt/parse_static.lua @@ -0,0 +1,48 @@ +local wea = worldeditadditions +local Vector3 = wea.Vector3 + +--- Parses a static brush definition. +-- @param source string The source string that contains the static brush, formatted as TSV. +-- @returns true,table,Vector3|false,string A success boolean, followed either by an error message as a string or the brush (as a table) and it's size (as an X/Y Vector3) +return function(source) + local width = -1 + local height + local maxvalue, minvalue, range + + -- Parse out the TSV into a table of tables, while also parsing values as numbers + -- Also keeps track of the maximum/minimum values found for rescaling later. + local values = wea.table.map( + wea.split(source, "\n", false), + function(line) + local row = wea.split(line, "%s+", false) + width = math.max(width, #row) + return wea.table.map( + row, + function(pixel) + local value = tonumber(pixel) + if not value then value = 0 end + if maxvalue == nil or value > maxvalue then + maxvalue = value + end + if minvalue == nil or value < minvalue then + minvalue = value + end + return value + end + ) + end + ) + + height = #values + range = maxvalue - minvalue + + local brush = {} + for y,row in ipairs(values) do + for x,value in ipairs(row) do + local i = (y-1)*width + (x-1) + brush[i] = (value - minvalue) / range + end + end + + return true, brush, Vector3.new(width, height, 0) +end diff --git a/worldeditadditions/lib/sculpt/scan_static.lua b/worldeditadditions/lib/sculpt/scan_static.lua new file mode 100644 index 0000000..caacf37 --- /dev/null +++ b/worldeditadditions/lib/sculpt/scan_static.lua @@ -0,0 +1,48 @@ +local wea = worldeditadditions +local Vector3 = wea.Vector3 + +local import_static = dofile(wea.modpath.."/lib/sculpt/import_static.lua") + +local function import_filepath(filepath, name, overwrite_existing) + if overwrite_existing and wea.sculpt.brushes[name] ~= nil then + return false, "Error: A brush with the name '"..name.."' already exists." + end + + local success, brush, brush_size = import_static(filepath) + if not success then return success, "Error while reading from '"..filepath.."': "..brush end + + wea.sculpt.brushes[name] = { + brush = brush, + size = brush_size + } + + return true +end + +--- Scans the given directory and imports all static brushes found. +-- Static brushes have the file extension ".brush.tsv" (without quotes). +-- @param dirpath string The path to directory that contains the static brushs to import. +-- @returns bool,loaded,errors A success boolean, followed by the number of brushes loaded, followed by the number of errors encountered while loading brushes (errors are logged as warnings with Minetest) +return function(dirpath, overwrite_existing) + if overwrite_existing == nil then overwrite_existing = false end + local files = wea.io.scandir_files(dirpath) + + local brushes_loaded = 0 + local errors = 0 + + + for filename in pairs(files) do + if wea.str_ends(filename, ".brush.tsv") then + local filepath = dirpath.."/"..filename + local name = filepath:gsub(".brush.tsv", "") + + local success, msg = import_filepath(filepath, name, overwrite_existing) + if not success then + minetest.log("warning", "[WorldEditAdditions:sculpt] Encountered error when loading brush from '"..filepath.."':"..msg) + end + brushes_loaded = brushes_loaded + 1 + end + end + + return true, brushes_loaded, errors +end