forked from ShadowNinja/minetest-mapper-cpp
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Rewrite colors.txt generation script for more functions and better us…
…ability
- Loading branch information
Showing
4 changed files
with
246 additions
and
132 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
local function get_tile(tiles, n) | ||
local tile = tiles[n] | ||
if type(tile) == 'table' then | ||
return tile.name | ||
end | ||
return tile | ||
end | ||
|
||
local function pairs_s(dict) | ||
local keys = {} | ||
for k in pairs(dict) do | ||
keys[#keys+1] = k | ||
end | ||
table.sort(keys) | ||
return ipairs(keys) | ||
end | ||
|
||
minetest.register_chatcommand("dumpnodes", { | ||
description = "Dump node and texture list for use with minetestmapper", | ||
func = function() | ||
local ntbl = {} | ||
for _, nn in pairs_s(minetest.registered_nodes) do | ||
local prefix, name = nn:match('(.*):(.*)') | ||
if prefix == nil or name == nil then | ||
print("ignored(1): " .. nn) | ||
else | ||
if ntbl[prefix] == nil then | ||
ntbl[prefix] = {} | ||
end | ||
ntbl[prefix][name] = true | ||
end | ||
end | ||
local out, err = io.open(minetest.get_worldpath() .. "/nodes.txt", 'wb') | ||
if not out then | ||
return true, err | ||
end | ||
local n = 0 | ||
for _, prefix in pairs_s(ntbl) do | ||
out:write('# ' .. prefix .. '\n') | ||
for _, name in pairs_s(ntbl[prefix]) do | ||
local nn = prefix .. ":" .. name | ||
local nd = minetest.registered_nodes[nn] | ||
local tiles = nd.tiles or nd.tile_images | ||
if tiles == nil or nd.drawtype == 'airlike' then | ||
print("ignored(2): " .. nn) | ||
else | ||
local tex = get_tile(tiles, 1) | ||
tex = (tex .. '^'):match('%(*(.-)%)*^') -- strip modifiers | ||
if tex:find("[combine", 1, true) then | ||
tex = tex:match('.-=([^:]-)') -- extract first texture | ||
end | ||
out:write(nn .. ' ' .. tex .. '\n') | ||
n = n + 1 | ||
end | ||
end | ||
out:write('\n') | ||
end | ||
out:close() | ||
return true, n .. " nodes dumped." | ||
end, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
name = dumpnodes | ||
description = minetestmapper development mod (node dumper) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
#!/usr/bin/env python3 | ||
import sys | ||
import os.path | ||
import getopt | ||
import re | ||
from math import sqrt | ||
try: | ||
from PIL import Image | ||
except: | ||
print("Could not load image routines, install PIL ('pillow' on pypi)!", file=sys.stderr) | ||
exit(1) | ||
|
||
############ | ||
############ | ||
# Instructions for generating a colors.txt file for custom games and/or mods: | ||
# 1) Add the dumpnodes mod to a Minetest world with the chosen game and mods enabled. | ||
# 2) Join ingame and run the /dumpnodes chat command. | ||
# 3) Run this script and poin it to the installation path of the game using -g, | ||
# the path(s) where mods are stored using -m and the nodes.txt in your world folder. | ||
# Example command line: | ||
# ./util/generate_colorstxt.py --game /usr/share/minetest/games/minetest_game \ | ||
# -m ~/.minetest/mods ~/.minetest/worlds/my_world/nodes.txt | ||
# 4) Copy the resulting colors.txt file to your world folder or to any other places | ||
# and use it with minetestmapper's --colors option. | ||
########### | ||
########### | ||
|
||
# minimal sed syntax, s|match|replace| and /match/d supported | ||
REPLACEMENTS = [ | ||
# Delete some nodes that are usually hidden | ||
r'/^fireflies:firefly /d', | ||
r'/^butterflies:butterfly_/d', | ||
# Nicer colors for water and lava | ||
r's/^(default:(river_)?water_(flowing|source)) [0-9 ]+$/\1 39 66 106 128 224/', | ||
r's/^(default:lava_(flowing|source)) [0-9 ]+$/\1 255 100 0/', | ||
# Transparency for glass nodes and panes | ||
r's/^(default:.*glass) ([0-9 ]+)$/\1 \2 64 16/', | ||
r's/^(doors:.*glass[^ ]*) ([0-9 ]+)$/\1 \2 64 16/', | ||
r's/^(xpanes:.*(pane|bar)[^ ]*) ([0-9 ]+)$/\1 \3 64 16/', | ||
] | ||
|
||
def usage(): | ||
print("Usage: generate_colorstxt.py [options] [input file] [output file]") | ||
print("If not specified the input file defaults to ./nodes.txt and the output file to ./colors.txt") | ||
print(" -g / --game <folder>\t\tSet path to the game (for textures), required") | ||
print(" -m / --mods <folder>\t\tAdd search path for mod textures") | ||
print(" --replace <file>\t\tLoad replacements from file (ADVANCED)") | ||
|
||
def collect_files(path): | ||
dirs = [] | ||
with os.scandir(path) as it: | ||
for entry in it: | ||
if entry.name[0] == '.': continue | ||
if entry.is_dir(): | ||
dirs.append(entry.path) | ||
continue | ||
if entry.is_file() and '.' in entry.name: | ||
if entry.name not in textures.keys(): | ||
textures[entry.name] = entry.path | ||
for path2 in dirs: | ||
collect_files(path2) | ||
|
||
def average_color(filename): | ||
inp = Image.open(filename).convert('RGBA') | ||
data = inp.load() | ||
|
||
c0, c1, c2 = [], [], [] | ||
for x in range(inp.size[0]): | ||
for y in range(inp.size[1]): | ||
px = data[x, y] | ||
if px[3] < 128: continue # alpha | ||
c0.append(px[0]**2) | ||
c1.append(px[1]**2) | ||
c2.append(px[2]**2) | ||
|
||
if len(c0) == 0: | ||
print(f"didn't find color for '{os.path.basename(filename)}'", file=sys.stderr) | ||
return "0 0 0" | ||
c0 = sqrt(sum(c0) / len(c0)) | ||
c1 = sqrt(sum(c1) / len(c1)) | ||
c2 = sqrt(sum(c2) / len(c2)) | ||
return "%d %d %d" % (c0, c1, c2) | ||
|
||
def apply_sed(line, exprs): | ||
for expr in exprs: | ||
if expr[0] == '/': | ||
if not expr.endswith("/d"): raise ValueError() | ||
if re.search(expr[1:-2], line): | ||
return '' | ||
elif expr[0] == 's': | ||
expr = expr.split(expr[1]) | ||
if len(expr) != 4 or expr[3] != '': raise ValueError() | ||
line = re.sub(expr[1], expr[2], line) | ||
else: | ||
raise ValueError() | ||
return line | ||
# | ||
|
||
try: | ||
opts, args = getopt.getopt(sys.argv[1:], "hg:m:", ["help", "game=", "mods=", "replace="]) | ||
except getopt.GetoptError as e: | ||
print(str(e)) | ||
exit(1) | ||
if ('-h', '') in opts or ('--help', '') in opts: | ||
usage() | ||
exit(0) | ||
|
||
input_file = "./nodes.txt" | ||
output_file = "./colors.txt" | ||
texturepaths = [] | ||
|
||
try: | ||
gamepath = next(o[1] for o in opts if o[0] in ('-g', '--game')) | ||
if not os.path.isdir(os.path.join(gamepath, "mods")): | ||
print(f"'{gamepath}' doesn't exist or does not contain a game.", file=sys.stderr) | ||
exit(1) | ||
texturepaths.append(os.path.join(gamepath, "mods")) | ||
except StopIteration: | ||
print("No game path set but one is required. (see --help)", file=sys.stderr) | ||
exit(1) | ||
|
||
try: | ||
tmp = next(o[1] for o in opts if o[0] == "--replace") | ||
REPLACEMENTS.clear() | ||
with open(tmp, 'r') as f: | ||
for line in f: | ||
if not line or line[0] == '#': continue | ||
REPLACEMENTS.append(line.strip()) | ||
except StopIteration: | ||
pass | ||
|
||
for o in opts: | ||
if o[0] not in ('-m', '--mods'): continue | ||
if not os.path.isdir(o[1]): | ||
print(f"Given path '{o[1]}' does not exist.'", file=sys.stderr) | ||
exit(1) | ||
texturepaths.append(o[1]) | ||
|
||
if len(args) > 2: | ||
print("Too many arguments.", file=sys.stderr) | ||
exit(1) | ||
if len(args) > 1: | ||
output_file = args[1] | ||
if len(args) > 0: | ||
input_file = args[0] | ||
|
||
if not os.path.exists(input_file) or os.path.isdir(input_file): | ||
print(f"Input file '{input_file}' does not exist.", file=sys.stderr) | ||
exit(1) | ||
|
||
# | ||
|
||
print(f"Collecting textures from {len(texturepaths)} path(s)... ", end="", flush=True) | ||
textures = {} | ||
for path in texturepaths: | ||
collect_files(path) | ||
print("done") | ||
|
||
print("Processing nodes...") | ||
fin = open(input_file, 'r') | ||
fout = open(output_file, 'w') | ||
n = 0 | ||
for line in fin: | ||
line = line.rstrip('\r\n') | ||
if not line or line[0] == '#': | ||
fout.write(line + '\n') | ||
continue | ||
node, tex = line.split(" ") | ||
if not tex or tex == "blank.png": | ||
continue | ||
elif tex not in textures.keys(): | ||
print(f"skip {node} texture not found") | ||
continue | ||
color = average_color(textures[tex]) | ||
line = f"{node} {color}" | ||
#print(f"ok {node}") | ||
line = apply_sed(line, REPLACEMENTS) | ||
if line: | ||
fout.write(line + '\n') | ||
n += 1 | ||
fin.close() | ||
fout.close() | ||
print(f"Done, {n} entries written.") |