Initial commit

master
Lars Mueller 2022-05-13 14:57:15 +02:00
commit 3bc0bb9e89
100 changed files with 714 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
textures

2
.luacheckrc Normal file
View File

@ -0,0 +1,2 @@
globals = {"texgen"}
read_globals = {"minetest", "modlib", "Settings"}

111
Readme.md Normal file
View File

@ -0,0 +1,111 @@
# Texture Generator (`texgen`)
Dynamically generates a texture pack for your current game in the `textures` directory.
## Features
* Compatible with virtually all mods and games
* Easy texture pack generation, no complex installation
* Server-side (mod-based) texture pack; textures can be accessed by other mods
## Instructions
Just fire up the mod and configure it to your liking. It might throw an error message as it updates its `mod.conf` to depend on all enabled mods. Simply retry after that.
### Downloading palettes
You can use the `/download_palette <url>` chatcommand to download a PNG palette file, for example `/download_palette https://lospec.com/palette-list/zughy-32-1x.png`.
See `/help download_palette` for details on its usage.
Requires the `server` priv. Only available if `texgen` is added to `secure.http_mods`.
**WARNING: Enabling this feature poses a minor security risk.**
## Configuration
<!--modlib:conf:2-->
### `average`
Replace each texture with a single pixel of its weighted average RGB color
* Type: boolean
* Default: `false`
### `invert`
Invert the RGB colors
* Type: boolean
* Default: `false`
### `monochrome`
Convert RGB to monochrome (greyscale)
* Type: boolean
* Default: `false`
### `palette`
#### `dithering`
Dithering method to use
* Type: string
* Default: `floyd_steinberg`
* Possible values:
* sierra
* stucke
* sierra_lite
* jarvis_judice_ninke
* floyd_steinberg
* none
* two_row_sierra
* burkes
* atkinson
#### `name`
Name of the palette to use (without extension)
* Type: string
* Default: `apollo`
* Possible values:
* soggy-newspapers
* apollo
* zughy-32
* aap-64
* pico-8
* resurrect-64
### `saturate`
Increase or decrease saturation by a factor
* Type: number
* Default: `1`
* &gt;= `0.1`
* &lt;= `10`
<!--modlib:conf-->
## Links
* [GitHub](https://github.com/appgurueu/texgen) - sources, issue tracking, contributing
* [Discord](https://discord.gg/ysP74by) - discussion, chatting
* [Minetest Forum](https://forum.minetest.net/viewtopic.php?f=9&t=28115) - (more organized) discussion
* [ContentDB](https://content.minetest.net/packages/LMD/texgen) - releases (downloading from GitHub is recommended)
## License
Code written by Lars Müller (appgurueu) and licensed under the MIT license; builtin Minetest media licensed under various free Creative Commons licenses as well as the Apache 2 license (see `LICENSE.txt` in the `builtin` folder for details and attribution).
The palettes are (for copyright reasons shuffled) versions of palettes available on [Lospec](https://lospec.com/):
* Adigun A. Polack's "AAP-64"
* AdamCYounis' "Apollo"
* Lexaloffle Games' "PICO-8"
* Kerrie Lake's "Resurrect 64"
* Walking's "Soggy Newspapers"
* Zughy's "Zughy 32"

18
average_color.lua Normal file
View File

@ -0,0 +1,18 @@
-- Average color, weighted by alpha and calculated in linear RGB colorspace
return function(png)
-- TODO make colorspecs extend vectors
local avg_color = modlib.vector.new{r = 0, g = 0, b = 0}
local total_alpha = 0
for _, color in ipairs(png.data) do
color = modlib.minetest.colorspec.from_number(color)
-- Squared average
avg_color = modlib.vector.add(avg_color, modlib.vector.multiply_scalar(
modlib.vector.pow_scalar({r = color.r, g = color.g, b = color.b}, 2), color.a))
total_alpha = total_alpha + color.a
end
if total_alpha == 0 then total_alpha = 1 end -- Avoid division by zero
-- Round & convert back to colorspec
avg_color = modlib.minetest.colorspec.new(avg_color:divide_scalar(total_alpha)
:apply(math.sqrt):add_scalar(0.5):floor())
return avg_color
end

197
builtin/LICENSE.txt Normal file
View File

@ -0,0 +1,197 @@
License of Minetest textures and sounds
---------------------------------------
This applies to textures and sounds contained in the main Minetest
distribution.
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/
textures/base/pack/refresh.png is under the Apache 2 license
https://www.apache.org/licenses/LICENSE-2.0.html
Textures by Zughy are under CC BY-SA 4.0
https://creativecommons.org/licenses/by-sa/4.0/
textures/base/pack/server_public.png is under CC-BY 4.0, taken from Twitter's Twemoji set
https://creativecommons.org/licenses/by/4.0/
Authors of media files
-----------------------
Everything not listed in here:
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
ShadowNinja:
textures/base/pack/smoke_puff.png
paramat:
textures/base/pack/menu_header.png
textures/base/pack/next_icon.png
textures/base/pack/prev_icon.png
textures/base/pack/clear.png
textures/base/pack/search.png
rubenwardy, paramat:
textures/base/pack/start_icon.png
textures/base/pack/end_icon.png
erlehmann:
misc/minetest-icon-24x24.png
misc/minetest-icon.ico
misc/minetest.svg
textures/base/pack/logo.png
JRottm:
textures/base/pack/player_marker.png
srifqi:
textures/base/pack/chat_hide_btn.png
textures/base/pack/chat_show_btn.png
textures/base/pack/joystick_bg.png
textures/base/pack/joystick_center.png
textures/base/pack/joystick_off.png
textures/base/pack/minimap_btn.png
Zughy:
textures/base/pack/cdb_add.png
textures/base/pack/cdb_clear.png
textures/base/pack/cdb_downloading.png
textures/base/pack/cdb_queued.png
textures/base/pack/cdb_update.png
textures/base/pack/cdb_viewonline.png
appgurueu:
textures/base/pack/server_incompatible.png
License of Minetest source code
-------------------------------
Minetest
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Irrlicht
---------------
This program uses IrrlichtMt, Minetest's fork of
the Irrlicht Engine. http://irrlicht.sourceforge.net/
The Irrlicht Engine License
Copyright © 2002-2005 Nikolaus Gebhardt
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
JThread
---------------
This program uses the JThread library. License for JThread follows:
Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
Lua
---------------
Lua is licensed under the terms of the MIT license reproduced below.
This means that Lua is free software and can be used for both academic
and commercial purposes at absolutely no cost.
For details and rationale, see https://www.lua.org/license.html .
Copyright (C) 1994-2008 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Fonts
---------------
Bitstream Vera Fonts Copyright:
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
a trademark of Bitstream, Inc.
Arimo - Apache License, version 2.0
Digitized data copyright (c) 2010-2012 Google Corporation.
Cousine - Apache License, version 2.0
Digitized data copyright (c) 2010-2012 Google Corporation.
DroidSansFallBackFull:
Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

BIN
builtin/air.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

BIN
builtin/aux1_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
builtin/blank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

BIN
builtin/bubble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

BIN
builtin/bubble_gone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

BIN
builtin/camera_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
builtin/cdb_add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

BIN
builtin/cdb_clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

BIN
builtin/cdb_downloading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

BIN
builtin/cdb_queued.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
builtin/cdb_update.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

BIN
builtin/cdb_viewonline.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

BIN
builtin/chat_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

BIN
builtin/chat_hide_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
builtin/chat_show_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
builtin/checkbox_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

BIN
builtin/checkbox_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

BIN
builtin/checkbox_64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
builtin/clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

BIN
builtin/crack_anylength.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

BIN
builtin/debug_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
builtin/down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
builtin/drop_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
builtin/end_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

BIN
builtin/fast_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
builtin/fly_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
builtin/gear_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
builtin/halo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

BIN
builtin/heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

BIN
builtin/heart_gone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

BIN
builtin/ignore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

BIN
builtin/inventory_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

BIN
builtin/joystick_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
builtin/joystick_center.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
builtin/joystick_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
builtin/jump_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
builtin/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
builtin/menu_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

BIN
builtin/menu_header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
builtin/minimap_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
builtin/next_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

BIN
builtin/no_screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

BIN
builtin/no_texture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

BIN
builtin/noclip_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

BIN
builtin/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

BIN
builtin/player_back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

BIN
builtin/player_marker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
builtin/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

BIN
builtin/prev_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

BIN
builtin/progress_bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

BIN
builtin/progress_bar_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

BIN
builtin/rangeview_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
builtin/rare_controls.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

BIN
builtin/refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
builtin/search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
builtin/server_favorite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

BIN
builtin/server_ping_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

BIN
builtin/server_ping_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

BIN
builtin/server_ping_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

BIN
builtin/server_ping_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

BIN
builtin/server_public.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

BIN
builtin/smoke_puff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

BIN
builtin/start_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

BIN
builtin/sunrisebg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
builtin/unknown_item.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

BIN
builtin/unknown_node.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

BIN
builtin/unknown_object.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

BIN
builtin/wieldhand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

BIN
builtin/zoom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

102
dithering.lua Normal file
View File

@ -0,0 +1,102 @@
-- Simple error diffusion dithering
-- TODO use HSV/HSL for dithering closer to human perception
local matrices = {
none = {
fraction = 0
},
floyd_steinberg = {
fraction = 1/16,
{x_off = 1, 7};
{x_off = -1, 3, 5, 1};
},
jarvis_judice_ninke = {
fraction = 1/48,
{x_off = 1, 7, 5};
{x_off = -2, 3, 5, 7, 5, 3};
{x_off = -2, 1, 3, 5, 3, 1};
},
stucke = {
fraction = 1/42,
{x_off = 1, 8, 4};
{x_off = -2, 2, 4, 8, 4, 2};
{x_off = -2, 1, 2, 4, 2, 1};
},
atkinson = {
fraction = 1/8,
{x_off = 1, 1, 1};
{x_off = -1, 1, 1, 1 };
{x_off = 0, 1 };
},
burkes = {
fraction = 1/42,
{x_off = 1, 8, 4};
{x_off = -2, 2, 4, 8, 4, 2};
},
sierra = {
fraction = 1/16,
{x_off = 1, 4, 3};
{x_off = -2, 1, 2, 3, 2, 1};
},
sierra_lite = {
fraction = 1/4,
{x_off = 1, 2},
{x_off = -1, 1, 1 },
},
two_row_sierra = {
fraction = 1/32,
{x_off = 1, 5, 3},
{x_off = -2, 2, 4, 5, 4, 2},
{x_off = -1, 2, 3, 2 },
},
}
texgen.dithering_methods = matrices
local clamp = modlib.math.clamp
return function(method)
local matrix = assert(matrices[method])
return function(png, closest_color)
local width = png.width
local diffused_errors = {}
local zero_err = {r = 0, g = 0, b = 0}
for index, color in ipairs(png.data) do
local x, y = (index - 1) % width, math.floor((index - 1) / width)
color = modlib.minetest.colorspec.from_number(color)
local diff_err = diffused_errors[index] or zero_err
diffused_errors[index] = nil
local col_err = {
r = clamp(color.r + diff_err.r, 0, 255),
g = clamp(color.g + diff_err.g, 0, 255),
b = clamp(color.b + diff_err.b, 0, 255)
}
local new_color = closest_color(col_err)
png.data[index] = modlib.minetest.colorspec.new{
r = new_color.r,
g = new_color.g,
b = new_color.b,
a = color.a -- keep alpha
}:to_number()
-- Diffuse error
local weight = matrix.fraction * color.a / 255
if weight > 0 then
local err = {
r = weight * (new_color.r - col_err.r),
g = weight * (new_color.g - col_err.g),
b = weight * (new_color.b - col_err.b)
}
for y_off, row in ipairs(matrix) do
local ey = y + y_off - 1
for x_off, factor in ipairs(row) do
local ex = x + row.x_off + x_off - 1
local idx = ey * width + ex
local derr = diffused_errors[idx] or {r = 0, g = 0, b = 0}
derr.r = derr.r + factor * err.r
derr.g = derr.g + factor * err.g
derr.b = derr.b + factor * err.b
diffused_errors[idx] = derr
end
end
end
end
end
end

39
download_palette.lua Normal file
View File

@ -0,0 +1,39 @@
local http = ...
local palettes = modlib.mod.get_resource"palettes"
minetest.register_chatcommand("download_palette", {
params = "<url>",
description = "Download a palette to use for generating textures",
privs = {server = true},
func = function(name, url)
local palette_name = url:match"/([A-Za-z0-9-_]+)%.png$"
if not (url:match"^https://" and palette_name) then
return false, "URL must be a valid HTTPS URL with a filename consisting of ASCII letters, digits, hyphens and underscores and ending in a .png extension." -- luacheck: ignore
end
http.fetch({
url = url,
method = "GET"
}, function(res)
local failure = (res.timeout and "Timeout") or (not res.succeeded and ("HTTP Status Code %d"):format(res.code))
if failure then
minetest.chat_send_player(name, ("Downloading from URL %s failed: %s"):format(url, failure))
return
end
local stream = modlib.text.inputstream(res.data)
local status, res_or_err = pcall(modlib.minetest.decode_png, stream)
if stream:read(1) then
status, res_or_err = false, "EOF expected"
end
if status then
-- Rewrite & store PNG
local png = res_or_err
modlib.minetest.convert_png_to_argb8(png)
modlib.table.shuffle(png.data) -- for copyright reasons
modlib.file.write(modlib.file.concat_path{palettes, palette_name .. ".png"},
modlib.minetest.encode_png(png.width, png.height, png.data))
minetest.chat_send_player(name, ("Palette %s added to palettes!"):format(palette_name))
else
minetest.chat_send_player(name, ("PNG image from URL %s is invalid: %s"):format(url, res_or_err))
end
end)
end
})

130
init.lua Normal file
View File

@ -0,0 +1,130 @@
local modname = "texgen"
local modpath = minetest.get_modpath(modname)
-- Rewrite mod.conf on startup
do
local conf = Settings(modpath .. "/mod.conf")
conf:set("name", modname)
conf:set("description", "Dynamically generated texture packs")
conf:set("depends", "modlib")
local to_depend = {}
local to_dep_list = {}
for _, modname in pairs(minetest.get_modnames()) do to_depend[modname] = true end -- luacheck: ignore
local opt_dep_str = conf:get"optional_depends" or ""
for opt_depend in opt_dep_str:gmatch"[^%s,]+" do
table.insert(to_dep_list, opt_depend)
to_depend[opt_depend] = nil
end
to_depend.texgen = nil -- no circular dependency
to_depend.modlib = nil -- modlib is already a hard dependency
if next(to_depend) or not modlib then
for dep in pairs(to_depend) do table.insert(to_dep_list, dep) end
conf:set("optional_depends", table.concat(to_dep_list, ", "))
conf:write()
error"mod.conf updated to optionally depend on all enabled mods, please restart the game"
end
end
assert(modlib.version >= 94, "update modlib to version rolling-94 or newer")
-- Register palette downloading command if HTTP API is available
local http = minetest.request_http_api()
if http then
assert(loadfile(modlib.mod.get_resource"download_palette.lua"))(http)
end
local texture_path = modpath .. "/textures/"
if minetest.rmdir then
-- Clear texture directory on startup
minetest.rmdir(texture_path, true)
end
minetest.mkdir(texture_path)
local function get_path(filename)
local path
local mod = modlib.minetest.media.mods[filename]
if mod == modname then -- media overridden by this mod
local overridden_paths = modlib.minetest.media.overridden_paths[filename]
if not overridden_paths then return end
path = overridden_paths[#overridden_paths]
else
path = modlib.minetest.media.paths[filename]
end
return path
end
texgen = {} -- HACK only use the mod namespace to expose the dithering methods to the schema...
local dithering = modlib.mod.include"dithering.lua" -- sets texgen.dithering_methods
local conf = modlib.mod.configuration()
texgen = nil -- ...delete it afterwards
local palette, dither
if conf.palette.name then
palette = modlib.mod.include"palette.lua"(conf.palette.name)
dither = dithering(conf.palette.dithering)
end
local saturate, monochrome, invert = conf.saturate, conf.monochrome, conf.invert
local average
if conf.average then
average = modlib.mod.include"average_color.lua"
end
local function read_convert_png(path)
local file = assert(io.open(path, "rb"))
local png = modlib.minetest.decode_png(file)
assert(not file:read(1), "EOF expected")
file:close()
modlib.minetest.convert_png_to_argb8(png)
return png
end
local clamp = modlib.math.clamp
local function transform_png(filename, path)
local png = read_convert_png(path)
if palette then
dither(png, palette)
end
modlib.table.map(png.data, function(color_num)
local color = modlib.minetest.colorspec.from_number(color_num)
if saturate ~= 1 then
-- See https://alienryderflex.com/saturation.html
local P = math.sqrt(0.299 * color.r^2 + 0.587 * color.g^2 + 0.114 * color.b^2)
local res = modlib.vector.apply({
r = clamp((color.r - P) * saturate + P + 0.5, 0, 255),
g = clamp((color.g - P) * saturate + P + 0.5, 0, 255),
b = clamp((color.b - P) * saturate + P + 0.5, 0, 255),
}, math.floor)
color.r, color.g, color.b = res.r, res.g, res.b
end
if invert then
color.r, color.g, color.b = 255 - color.r, 255 - color.g, 255 - color.b
end
if monochrome then
local brightness = math.floor(0.299 * color.r + 0.587 * color.g + 0.114 * color.b + 0.5)
color.r, color.g, color.b = brightness, brightness, brightness
end
return color:to_number()
end)
local width, height, data = png.width, png.height, png.data
if average then
width, height, data = 1, 1, {average(png):to_number()}
end
modlib.file.write(modlib.file.concat_path{modpath, "textures", filename},
modlib.minetest.encode_png(width, height, data))
end
for filename in pairs(modlib.minetest.media.paths) do
local _, ext = modlib.file.get_extension(filename)
if ext == "png" then
local path = get_path(filename)
-- May be (only) overridden media from this mod, which does not have a path (as it was deleted)
if path then transform_png(filename, path) end
end
end
-- Builtin textures aren't provided by mods and are thus unknown to modlib; provide them through this mod
for _, filename in ipairs(minetest.get_dir_list(modlib.file.concat_path{modpath, "builtin"}, false)) do
-- Don't override builtin overrides by other mods
if filename:match"%.png$" and not (modlib.minetest.media[filename] and modlib.minetest.overridden_paths[filename]) then
transform_png(filename, modlib.file.concat_path{modpath, "builtin", filename})
end
end

5
mod.conf Normal file
View File

@ -0,0 +1,5 @@
name = texgen
title = Texture Generator
author = appguru(eu)
description = Dynamically generated texture packs
depends = modlib

42
palette.lua Normal file
View File

@ -0,0 +1,42 @@
local function relative_color_distance(color, other_color)
-- See https://www.compuphase.com/cmetric.htm
local redmean = (color.r + other_color.r) / 2
-- Omits the square root as this only has to be relative
return (2 + redmean/256) * (color.r-other_color.r)^2
+ 4 * (color.g-other_color.g)^2
+ (2 + (255 - redmean)/256) * (color.b-other_color.b)^2
end
local palettes = modlib.mod.get_resource"palettes"
return function(name)
local path = modlib.file.concat_path{palettes, name .. ".png"}
local file = assert(io.open(path, "rb"))
local png = modlib.minetest.decode_png(file)
assert(not file:read(1), "EOF expected")
file:close()
modlib.minetest.convert_png_to_argb8(png)
local colors = {}
for _, color in pairs(png.data) do
-- TODO ignore colors with alpha 0?
local rgb = color % 0x1000000
colors[rgb] = true
end
local palette_colors = {}
for colornum in pairs(colors) do
table.insert(palette_colors, modlib.minetest.colorspec.from_number(colornum))
end
return function(color)
-- Find closest color using a linear search; a k-d-tree can't be employed here because the metric isn't euclidean
local closest_color = palette_colors[1]
local closest_distance = relative_color_distance(color, closest_color)
for i = 2, #palette_colors do
local palette_color = palette_colors[i]
local distance = relative_color_distance(color, palette_color)
-- TODO deal with same distances through random choice?
if distance < closest_distance then
closest_color = palette_color
closest_distance = distance
end
end
return closest_color
end
end

BIN
palettes/aap-64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

BIN
palettes/apollo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

BIN
palettes/pico-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

BIN
palettes/resurrect-64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

BIN
palettes/zughy-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

48
schema.lua Normal file
View File

@ -0,0 +1,48 @@
local palettes = {}
for _, filename in ipairs(minetest.get_dir_list(modlib.mod.get_resource"palettes", false)) do
local palette = filename:match"(.+)%.png$"
if palette then palettes[palette] = true end
end
return {
type = "table",
entries = {
palette = {
type = "table",
entries = {
name = {
type = "string",
description = "Name of the palette to use (without extension)",
values = palettes,
default = "apollo"
},
dithering = {
type = "string",
description = "Dithering method to use",
values = texgen.dithering_methods,
default = "floyd_steinberg"
}
}
},
saturate = {
type = "number",
description = "Increase or decrease saturation by a factor",
range = {min = 0.1, max = 10},
default = 1
},
monochrome = {
type = "boolean",
description = "Convert RGB to monochrome (greyscale)",
default = false
},
invert = {
type = "boolean",
description = "Invert the RGB colors",
default = false
},
average = {
type = "boolean",
description = "Replace each texture with a single pixel of its weighted average RGB color",
default = false,
}
}
}

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

19
settingtypes.txt Normal file
View File

@ -0,0 +1,19 @@
# Replace each texture with a single pixel of its weighted average RGB color
texgen.average (Texgen Average) bool false
# Invert the RGB colors
texgen.invert (Texgen Invert) bool false
# Convert RGB to monochrome (greyscale)
texgen.monochrome (Texgen Monochrome) bool false
# Increase or decrease saturation by a factor
texgen.saturate (Texgen Saturate) float 1 0.100000 10.000000
[texgen.palette]
# Dithering method to use
texgen.palette.dithering (Texgen Palette Dithering) enum floyd_steinberg sierra,stucke,sierra_lite,jarvis_judice_ninke,floyd_steinberg,none,two_row_sierra,burkes,atkinson
# Name of the palette to use (without extension)
texgen.palette.name (Texgen Palette Name) enum apollo soggy-newspapers,apollo,zughy-32,aap-64,pico-8,resurrect-64