Initial commit
|
@ -0,0 +1 @@
|
|||
textures
|
|
@ -0,0 +1,2 @@
|
|||
globals = {"texgen"}
|
||||
read_globals = {"minetest", "modlib", "Settings"}
|
|
@ -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`
|
||||
* >= `0.1`
|
||||
* <= `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"
|
|
@ -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
|
|
@ -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.
|
After Width: | Height: | Size: 225 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 95 B |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 68 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 147 B |
After Width: | Height: | Size: 182 B |
After Width: | Height: | Size: 201 B |
After Width: | Height: | Size: 210 B |
After Width: | Height: | Size: 173 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 873 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 288 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 766 B |
After Width: | Height: | Size: 708 B |
After Width: | Height: | Size: 255 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 908 B |
After Width: | Height: | Size: 971 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 144 B |
After Width: | Height: | Size: 255 B |
After Width: | Height: | Size: 68 B |
After Width: | Height: | Size: 234 B |
After Width: | Height: | Size: 331 B |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 580 B |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 124 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 420 B |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 714 B |
After Width: | Height: | Size: 586 B |
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 178 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 449 B |
After Width: | Height: | Size: 142 B |
After Width: | Height: | Size: 140 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 714 B |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 354 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 227 B |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 916 B |
After Width: | Height: | Size: 273 B |
After Width: | Height: | Size: 713 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 385 B |
After Width: | Height: | Size: 251 B |
After Width: | Height: | Size: 244 B |
After Width: | Height: | Size: 245 B |
After Width: | Height: | Size: 213 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 202 B |
After Width: | Height: | Size: 912 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 193 B |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 1.3 KiB |
|
@ -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
|
|
@ -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
|
||||
})
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
name = texgen
|
||||
title = Texture Generator
|
||||
author = appguru(eu)
|
||||
description = Dynamically generated texture packs
|
||||
depends = modlib
|
|
@ -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
|
After Width: | Height: | Size: 325 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 325 B |
After Width: | Height: | Size: 83 B |
After Width: | Height: | Size: 197 B |
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 179 KiB |
|
@ -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
|