mirror of
https://github.com/appgurueu/modlib.git
synced 2024-11-25 00:33:44 +01:00
Add support for new texture modifiers
https://github.com/minetest/minetest/pull/10100 has introduced a few inconsistencies with the formats of previous texture modifiers: `[fill` is the first texture modifier to have a modifying and a generating variant. Thus `texmod` "constructors" and `texmod` "modifiers" / methods had to be separated; `texmod.fill` is not the same as `tm.fill` where `tm` is `texmod` instance. It is rather dirty that the generating variant would ignore extraneous parameters of the modifying variant, so this is not replicated by the parser. `[hsl`, `[colorizehsl`, `[contrast` and `[screen` are pretty standard texture modifiers as far as the DSL is concerned. `[hsl` and `[colorizehsl` are very similar. `[hardlight` is the first texture modifier to exist just for swapping the base and parameter. `a^[overlay:b` and `b^[hardlight:a` are both normalized to `b:[hardlight:a` by the DSL. `[overlay` (called "overlay blend" in the docs) creates a naming conflict with literal overlaying (`^`). This is resolved by renaming `:overlay` to `:blit`.
This commit is contained in:
parent
a9b5d80ca8
commit
2b1c9cff7c
@ -6,9 +6,10 @@ local function component(component_name, ...)
|
||||
end
|
||||
|
||||
local texmod, metatable = component"dsl"
|
||||
texmod.write = component"write"
|
||||
local methods = metatable.__index
|
||||
methods.write = component"write"
|
||||
texmod.read = component("read", texmod)
|
||||
texmod.calc_dims = component("calc_dims", texmod)
|
||||
methods.calc_dims = component("calc_dims", texmod)
|
||||
|
||||
function metatable:__tostring()
|
||||
local rope = {}
|
||||
|
@ -19,6 +19,10 @@ do
|
||||
cd.mask = base_dim
|
||||
cd.multiply = base_dim
|
||||
cd.colorize = base_dim
|
||||
cd.colorizehsl = base_dim
|
||||
cd.hsl = base_dim
|
||||
cd.screen = base_dim
|
||||
cd.contrast = base_dim
|
||||
end
|
||||
|
||||
do
|
||||
@ -27,10 +31,23 @@ do
|
||||
cd.combine = wh
|
||||
end
|
||||
|
||||
function cd:overlay(get_dims)
|
||||
local base_w, base_h = calc_dims(self.base, get_dims)
|
||||
local over_w, over_h = calc_dims(self.over, get_dims)
|
||||
return math.max(base_w, over_w), math.max(base_h, over_h)
|
||||
function cd:fill(get_dims)
|
||||
if self.base then return calc_dims(self.base, get_dims) end
|
||||
return self.w, self.h
|
||||
end
|
||||
|
||||
do
|
||||
local function upscale_to_higher_res(self, get_dims)
|
||||
local base_w, base_h = calc_dims(self.base, get_dims)
|
||||
local over_w, over_h = calc_dims(self.over, get_dims)
|
||||
if base_w * base_h > over_w * over_h then
|
||||
return base_w, base_h
|
||||
end
|
||||
return over_w, over_h
|
||||
end
|
||||
cd.blit = upscale_to_higher_res
|
||||
cd.overlay = upscale_to_higher_res
|
||||
cd.hardlight = upscale_to_higher_res
|
||||
end
|
||||
|
||||
function cd:transform(get_dims)
|
||||
|
@ -1,5 +1,8 @@
|
||||
local colorspec = modlib.minetest.colorspec
|
||||
|
||||
local texmod = {}
|
||||
local metatable = {__index = texmod}
|
||||
local mod = {}
|
||||
local metatable = {__index = mod}
|
||||
|
||||
local function new(self)
|
||||
return setmetatable(self, metatable)
|
||||
@ -52,31 +55,65 @@ function texmod.inventorycube(top, left, right)
|
||||
}
|
||||
end
|
||||
|
||||
-- As a base generator, `fill` ignores `x` and `y`. Leave them as `nil`.
|
||||
function texmod.fill(w, h, color)
|
||||
assert(w % 1 == 0 and w > 0)
|
||||
assert(h % 1 == 0 and h > 0)
|
||||
return new{
|
||||
type = "fill",
|
||||
w = w,
|
||||
h = h,
|
||||
color = colorspec.from_any(color)
|
||||
}
|
||||
end
|
||||
|
||||
-- Methods / "modifiers"
|
||||
|
||||
function texmod:overlay(overlay)
|
||||
local function assert_int_range(num, min, max)
|
||||
assert(num % 1 == 0 and num >= min and num <= max)
|
||||
end
|
||||
|
||||
-- As a modifier, `fill` takes `x` and `y`
|
||||
function mod:fill(w, h, x, y, color)
|
||||
assert(w % 1 == 0 and w > 0)
|
||||
assert(h % 1 == 0 and h > 0)
|
||||
assert(x % 1 == 0 and x >= 0)
|
||||
assert(y % 1 == 0 and y >= 0)
|
||||
return new{
|
||||
type = "overlay",
|
||||
type = "fill",
|
||||
base = self,
|
||||
w = w,
|
||||
h = h,
|
||||
x = x,
|
||||
y = y,
|
||||
color = colorspec.from_any(color)
|
||||
}
|
||||
end
|
||||
|
||||
-- This is the real "overlay", associated with `^`.
|
||||
function mod:blit(overlay)
|
||||
return new{
|
||||
type = "blit",
|
||||
base = self,
|
||||
over = overlay
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:brighten()
|
||||
function mod:brighten()
|
||||
return new{
|
||||
type = "brighten",
|
||||
base = self,
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:noalpha()
|
||||
function mod:noalpha()
|
||||
return new{
|
||||
type = "noalpha",
|
||||
base = self
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:resize(w, h)
|
||||
function mod:resize(w, h)
|
||||
assert(w % 1 == 0 and w > 0)
|
||||
assert(h % 1 == 0 and h > 0)
|
||||
return new{
|
||||
@ -88,10 +125,10 @@ function texmod:resize(w, h)
|
||||
end
|
||||
|
||||
local function assert_uint8(num)
|
||||
assert(num % 1 == 0 and num >= 0 and num <= 0xFF)
|
||||
assert_int_range(num, 0, 0xFF)
|
||||
end
|
||||
|
||||
function texmod:makealpha(r, g, b)
|
||||
function mod:makealpha(r, g, b)
|
||||
assert_uint8(r); assert_uint8(g); assert_uint8(b)
|
||||
return new{
|
||||
type = "makealpha",
|
||||
@ -100,7 +137,7 @@ function texmod:makealpha(r, g, b)
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:opacity(ratio)
|
||||
function mod:opacity(ratio)
|
||||
assert_uint8(ratio)
|
||||
return new{
|
||||
type = "opacity",
|
||||
@ -113,7 +150,7 @@ local function tobool(val)
|
||||
return not not val
|
||||
end
|
||||
|
||||
function texmod:invert(channels --[[set with keys "r", "g", "b", "a"]])
|
||||
function mod:invert(channels --[[set with keys "r", "g", "b", "a"]])
|
||||
return new{
|
||||
type = "invert",
|
||||
base = self,
|
||||
@ -124,14 +161,14 @@ function texmod:invert(channels --[[set with keys "r", "g", "b", "a"]])
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:flip(flip_axis --[["x" or "y"]])
|
||||
function mod:flip(flip_axis --[["x" or "y"]])
|
||||
return self:transform(assert(
|
||||
(flip_axis == "x" and "fx")
|
||||
or (flip_axis == "y" and "fy")
|
||||
or (not flip_axis and "i")))
|
||||
end
|
||||
|
||||
function texmod:rotate(deg)
|
||||
function mod:rotate(deg)
|
||||
assert(deg % 90 == 0)
|
||||
deg = deg % 360
|
||||
return self:transform(("r%d"):format(deg))
|
||||
@ -201,7 +238,7 @@ do
|
||||
transform_idx(mat_2x2_compose(transform_mats[i], transform_mats[j]))])
|
||||
end
|
||||
end
|
||||
function texmod:transform(...)
|
||||
function mod:transform(...)
|
||||
if select("#", ...) == 0 then return self end
|
||||
local idx = ...
|
||||
if type(idx) == "string" then
|
||||
@ -227,7 +264,7 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
function texmod:verticalframe(framecount, frame)
|
||||
function mod:verticalframe(framecount, frame)
|
||||
assert(framecount >= 1)
|
||||
assert(frame >= 0)
|
||||
return new{
|
||||
@ -258,16 +295,16 @@ local function crack(self, name, ...)
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:crack(...)
|
||||
function mod:crack(...)
|
||||
return crack(self, "crack", ...)
|
||||
end
|
||||
|
||||
function texmod:cracko(...)
|
||||
function mod:cracko(...)
|
||||
return crack(self, "cracko", ...)
|
||||
end
|
||||
texmod.crack_with_opacity = texmod.cracko
|
||||
mod.crack_with_opacity = mod.cracko
|
||||
|
||||
function texmod:sheet(w, h, x, y)
|
||||
function mod:sheet(w, h, x, y)
|
||||
assert(w % 1 == 0 and w >= 1)
|
||||
assert(h % 1 == 0 and h >= 1)
|
||||
assert(x % 1 == 0 and x >= 0)
|
||||
@ -282,18 +319,24 @@ function texmod:sheet(w, h, x, y)
|
||||
}
|
||||
end
|
||||
|
||||
local colorspec = modlib.minetest.colorspec
|
||||
|
||||
function texmod:multiply(color)
|
||||
function mod:screen(color)
|
||||
return new{
|
||||
type = "multiply",
|
||||
type = "screen",
|
||||
base = self,
|
||||
color = colorspec.from_any(color) -- copies a given colorspec
|
||||
color = colorspec.from_any(color),
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:colorize(color, ratio)
|
||||
color = colorspec.from_any(color) -- copies a given colorspec
|
||||
function mod:multiply(color)
|
||||
return new{
|
||||
type = "multiply",
|
||||
base = self,
|
||||
color = colorspec.from_any(color)
|
||||
}
|
||||
end
|
||||
|
||||
function mod:colorize(color, ratio)
|
||||
color = colorspec.from_any(color)
|
||||
if ratio == "alpha" then
|
||||
assert(color.alpha or 0xFF == 0xFF)
|
||||
else
|
||||
@ -311,15 +354,62 @@ function texmod:colorize(color, ratio)
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:mask(_mask)
|
||||
local function hsl(type, s_def, s_max, l_def)
|
||||
return function(self, h, s, l)
|
||||
s, l = s or s_def, l or l_def
|
||||
assert_int_range(h, -180, 180)
|
||||
assert_int_range(s, 0, s_max)
|
||||
assert_int_range(l, -100, 100)
|
||||
return new{
|
||||
type = type,
|
||||
base = self,
|
||||
hue = h,
|
||||
saturation = s,
|
||||
lightness = l,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
mod.colorizehsl = hsl("colorizehsl", 50, 100, 0)
|
||||
mod.hsl = hsl("hsl", 0, math.huge, 0)
|
||||
|
||||
function mod:contrast(contrast, brightness)
|
||||
brightness = brightness or 0
|
||||
assert_int_range(contrast, -127, 127)
|
||||
assert_int_range(brightness, -127, 127)
|
||||
return new{
|
||||
type = "mask",
|
||||
type = "contrast",
|
||||
base = self,
|
||||
_mask = _mask
|
||||
contrast = contrast,
|
||||
brightness = brightness,
|
||||
}
|
||||
end
|
||||
|
||||
function texmod:lowpart(percent, overlay)
|
||||
function mod:mask(mask_texmod)
|
||||
return new{
|
||||
type = "mask",
|
||||
base = self,
|
||||
_mask = mask_texmod
|
||||
}
|
||||
end
|
||||
|
||||
function mod:hardlight(overlay)
|
||||
return new{
|
||||
type = "hardlight",
|
||||
base = self,
|
||||
over = overlay
|
||||
}
|
||||
end
|
||||
|
||||
-- Overlay *blend*.
|
||||
-- This was unfortunately named `[overlay` in Minetest,
|
||||
-- and so is named `:overlay` for consistency.
|
||||
--! Do not confuse this with the simple `^` used for blitting
|
||||
function mod:overlay(overlay)
|
||||
return overlay:hardlight(self)
|
||||
end
|
||||
|
||||
function mod:lowpart(percent, overlay)
|
||||
assert(percent % 1 == 0 and percent >= 0 and percent <= 100)
|
||||
return new{
|
||||
type = "lowpart",
|
||||
|
@ -35,10 +35,39 @@ function gr.combine(r)
|
||||
return w, h, blits
|
||||
end
|
||||
|
||||
function gr.fill(r)
|
||||
r:expect":"
|
||||
local w = r:int()
|
||||
r:expect"x"
|
||||
local h = r:int()
|
||||
r:expect":"
|
||||
-- Be strict(er than Minetest): Do not accept x, y for a base
|
||||
local color = r:colorspec()
|
||||
return w, h, color
|
||||
end
|
||||
|
||||
-- Parameter readers
|
||||
|
||||
local pr = {}
|
||||
|
||||
function pr.fill(r)
|
||||
r:expect":"
|
||||
local w = r:int()
|
||||
r:expect"x"
|
||||
local h = r:int()
|
||||
r:expect":"
|
||||
if assert(r:peek(), "unexpected eof"):find"%d" then
|
||||
local x = r:int()
|
||||
r:expect","
|
||||
local y = r:int()
|
||||
r:expect":"
|
||||
local color = r:colorspec()
|
||||
return w, h, x, y, color
|
||||
end
|
||||
local color = r:colorspec()
|
||||
return w, h, color
|
||||
end
|
||||
|
||||
function pr.brighten() end
|
||||
|
||||
function pr.noalpha() end
|
||||
@ -137,6 +166,7 @@ function pr.multiply(r)
|
||||
r:expect":"
|
||||
return r:colorspec()
|
||||
end
|
||||
pr.screen = pr.multiply
|
||||
|
||||
function pr.colorize(r)
|
||||
r:expect":"
|
||||
@ -153,6 +183,41 @@ function pr.colorize(r)
|
||||
return color, "alpha"
|
||||
end
|
||||
|
||||
function pr.colorizehsl(r)
|
||||
r:expect":"
|
||||
local hue = r:int()
|
||||
if not r:match":" then
|
||||
return hue
|
||||
end
|
||||
local saturation = r:int()
|
||||
if not r:match":" then
|
||||
return hue, saturation
|
||||
end
|
||||
local lightness = r:int()
|
||||
return hue, saturation, lightness
|
||||
end
|
||||
pr.hsl = pr.colorizehsl
|
||||
|
||||
function pr.contrast(r)
|
||||
r:expect":"
|
||||
local contrast = r:int()
|
||||
if not r:match":" then
|
||||
return contrast
|
||||
end
|
||||
local brightness = r:int()
|
||||
return contrast, brightness
|
||||
end
|
||||
|
||||
function pr.overlay(r)
|
||||
r:expect":"
|
||||
return r:subtexp()
|
||||
end
|
||||
|
||||
function pr.hardlight(r)
|
||||
r:expect":"
|
||||
return r:subtexp(r)
|
||||
end
|
||||
|
||||
function pr.mask(r)
|
||||
r:expect":"
|
||||
return r:subtexp(r)
|
||||
@ -325,12 +390,14 @@ function rm.texp(r)
|
||||
local param_reader, gen_reader = pr[type], gr[type]
|
||||
assert(param_reader or gen_reader)
|
||||
if param_reader then
|
||||
-- Note: It is important that this takes precedence to properly handle `[fill`
|
||||
base = base[type](base, param_reader(r))
|
||||
elseif gen_reader then
|
||||
base = base:overlay(texmod[type](gen_reader(r)))
|
||||
base = base:blit(texmod[type](gen_reader(r)))
|
||||
end
|
||||
-- TODO (?...) we could consume leftover parameters here to be as lax as Minetest
|
||||
else
|
||||
base = base:overlay(r:basexp())
|
||||
base = base:blit(r:basexp())
|
||||
end
|
||||
end
|
||||
return base
|
||||
|
@ -26,6 +26,15 @@ function pw:inventorycube(w)
|
||||
write_side"right"
|
||||
end
|
||||
|
||||
-- Handles both the generator & the modifier
|
||||
function pw:fill(w)
|
||||
w.colon(); w.int(self.w); w.str"x"; w.int(self.h)
|
||||
if self.base then
|
||||
w.colon(); w.int(self.x); w.str","; w.int(self.y)
|
||||
end
|
||||
w.colon(); w.str(self.color:to_string())
|
||||
end
|
||||
|
||||
-- No parameters to write
|
||||
pw.brighten = modlib.func.no_op
|
||||
pw.noalpha = modlib.func.no_op
|
||||
@ -68,6 +77,11 @@ function pw:sheet(w)
|
||||
w.colon(); w.int(self.w); w.str"x"; w.int(self.h); w.colon(); w.int(self.x); w.str","; w.int(self.y)
|
||||
end
|
||||
|
||||
function pw:screen(w)
|
||||
w.colon()
|
||||
w.str(self.color:to_string())
|
||||
end
|
||||
|
||||
function pw:multiply(w)
|
||||
w.colon()
|
||||
w.str(self.color:to_string())
|
||||
@ -86,6 +100,20 @@ function pw:colorize(w)
|
||||
end
|
||||
end
|
||||
|
||||
function pw:colorizehsl(w)
|
||||
w.colon(); w.int(self.hue); w.colon(); w.int(self.saturation); w.colon(); w.int(self.lightness)
|
||||
end
|
||||
pw.hsl = pw.colorizehsl
|
||||
|
||||
function pw:contrast(w)
|
||||
w.colon(); w.int(self.contrast); w.colon(); w.int(self.brightness)
|
||||
end
|
||||
|
||||
-- We don't have to handle `[overlay`; the DSL normalizes everything to `[hardlight`
|
||||
function pw:hardlight(w)
|
||||
w.colon(); w.esctex(self.over)
|
||||
end
|
||||
|
||||
function pw:mask(w)
|
||||
w.colon(); w.esctex(self._mask)
|
||||
end
|
||||
@ -130,7 +158,7 @@ return function(self, write_str)
|
||||
w.tex(tex.base)
|
||||
w.hat()
|
||||
end
|
||||
if tex.type == "overlay" then
|
||||
if tex.type == "blit" then -- simply `^`
|
||||
if non_modifiers[tex.over.type] then
|
||||
w.tex(tex.over)
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user