Körperbewegung
This commit is contained in:
parent
b16b24e4f7
commit
95945c0306
78 changed files with 12503 additions and 0 deletions
429
mods/modlib/minetest/texmod/read.lua
Normal file
429
mods/modlib/minetest/texmod/read.lua
Normal file
|
@ -0,0 +1,429 @@
|
|||
local texmod = ...
|
||||
local colorspec = modlib.minetest.colorspec
|
||||
|
||||
-- Generator readers
|
||||
|
||||
local gr = {}
|
||||
|
||||
function gr.png(r)
|
||||
r:expect":"
|
||||
local base64 = r:match_str"[a-zA-Z0-9+/=]"
|
||||
return assert(minetest.decode_base64(base64), "invalid base64")
|
||||
end
|
||||
|
||||
function gr.inventorycube(r)
|
||||
local top = r:invcubeside()
|
||||
local left = r:invcubeside()
|
||||
local right = r:invcubeside()
|
||||
return top, left, right
|
||||
end
|
||||
|
||||
function gr.combine(r)
|
||||
r:expect":"
|
||||
local w = r:int()
|
||||
r:expect"x"
|
||||
local h = r:int()
|
||||
local blits = {}
|
||||
while r:match":" do
|
||||
if r.eof then break end -- we can just end with `:`, right?
|
||||
local x = r:int()
|
||||
r:expect","
|
||||
local y = r:int()
|
||||
r:expect"="
|
||||
table.insert(blits, {x = x, y = y, texture = r:subtexp()})
|
||||
end
|
||||
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
|
||||
|
||||
function pr.resize(r)
|
||||
r:expect":"
|
||||
local w = r:int()
|
||||
r:expect"x"
|
||||
local h = r:int()
|
||||
return w, h
|
||||
end
|
||||
|
||||
function pr.makealpha(r)
|
||||
r:expect":"
|
||||
local red = r:int()
|
||||
r:expect","
|
||||
local green = r:int()
|
||||
r:expect","
|
||||
local blue = r:int()
|
||||
return red, green, blue
|
||||
end
|
||||
|
||||
function pr.opacity(r)
|
||||
r:expect":"
|
||||
local ratio = r:int()
|
||||
return ratio
|
||||
end
|
||||
|
||||
function pr.invert(r)
|
||||
r:expect":"
|
||||
local channels = {}
|
||||
while true do
|
||||
local c = r:match_charset"[rgba]"
|
||||
if not c then break end
|
||||
channels[c] = true
|
||||
end
|
||||
return channels
|
||||
end
|
||||
|
||||
do
|
||||
function pr.transform(r)
|
||||
if r:match_charset"[iI]" then
|
||||
return pr.transform(r)
|
||||
end
|
||||
local idx = r:match_charset"[0-7]"
|
||||
if idx then
|
||||
return tonumber(idx), pr.transform(r)
|
||||
end
|
||||
if r:match_charset"[fF]" then
|
||||
local flip_axis = assert(r:match_charset"[xXyY]", "axis expected")
|
||||
return "f" .. flip_axis, pr.transform(r)
|
||||
end
|
||||
if r:match_charset"[rR]" then
|
||||
local deg = r:match_str"%d"
|
||||
-- Be strict here: Minetest won't recognize other ways to write these numbers (or other numbers)
|
||||
assert(deg == "90" or deg == "180" or deg == "270")
|
||||
return ("r%d"):format(deg), pr.transform(r)
|
||||
end
|
||||
-- return nothing, we're done
|
||||
end
|
||||
end
|
||||
|
||||
function pr.verticalframe(r)
|
||||
r:expect":"
|
||||
local framecount = r:int()
|
||||
r:expect":"
|
||||
local frame = r:int()
|
||||
return framecount, frame
|
||||
end
|
||||
|
||||
function pr.crack(r)
|
||||
r:expect":"
|
||||
local framecount = r:int()
|
||||
r:expect":"
|
||||
local frame = r:int()
|
||||
if r:match":" then
|
||||
return framecount, frame, r:int()
|
||||
end
|
||||
return framecount, frame
|
||||
end
|
||||
pr.cracko = pr.crack
|
||||
|
||||
function pr.sheet(r)
|
||||
r:expect":"
|
||||
local w = r:int()
|
||||
r:expect"x"
|
||||
local h = r:int()
|
||||
r:expect":"
|
||||
local x = r:int()
|
||||
r:expect","
|
||||
local y = r:int()
|
||||
return w, h, x, y
|
||||
end
|
||||
|
||||
function pr.multiply(r)
|
||||
r:expect":"
|
||||
return r:colorspec()
|
||||
end
|
||||
pr.screen = pr.multiply
|
||||
|
||||
function pr.colorize(r)
|
||||
r:expect":"
|
||||
local color = r:colorspec()
|
||||
if not r:match":" then
|
||||
return color
|
||||
end
|
||||
if not r:match"a" then
|
||||
return color, r:int()
|
||||
end
|
||||
for c in ("lpha"):gmatch"." do
|
||||
r:expect(c)
|
||||
end
|
||||
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()
|
||||
end
|
||||
|
||||
function pr.mask(r)
|
||||
r:expect":"
|
||||
return r:subtexp()
|
||||
end
|
||||
|
||||
function pr.lowpart(r)
|
||||
r:expect":"
|
||||
local percent = r:int()
|
||||
assert(percent)
|
||||
r:expect":"
|
||||
return percent, r:subtexp()
|
||||
end
|
||||
|
||||
-- Build a prefix tree of parameter readers to greedily match the longest texture modifier prefix;
|
||||
-- just matching `%a+` and looking it up in a table
|
||||
-- doesn't work since `[transform` may be followed by a lowercase transform name
|
||||
-- TODO (?...) consolidate with `modlib.trie`
|
||||
local texmod_reader_trie = {}
|
||||
for _, readers in pairs{pr, gr} do
|
||||
for type in pairs(readers) do
|
||||
local subtrie = texmod_reader_trie
|
||||
for char in type:gmatch"." do
|
||||
subtrie[char] = subtrie[char] or {}
|
||||
subtrie = subtrie[char]
|
||||
end
|
||||
subtrie.type = type
|
||||
end
|
||||
end
|
||||
|
||||
-- Reader methods. We use `r` instead of the `self` "sugar" for consistency (and to save us some typing).
|
||||
local rm = {}
|
||||
|
||||
function rm.peek(r, parenthesized)
|
||||
if r.eof then return end
|
||||
local expected_escapes = 0
|
||||
if r.level > 0 then
|
||||
-- Premature optimization my beloved (this is `2^(level-1)`)
|
||||
expected_escapes = math.ldexp(0.5, r.level)
|
||||
end
|
||||
if r.character:match"[&^:]" then -- "special" characters - these need to be escaped
|
||||
if r.escapes == expected_escapes then
|
||||
return r.character
|
||||
elseif parenthesized and r.character == "^" and r.escapes < expected_escapes then
|
||||
-- Special handling for `^` inside `(...)`: This is undocumented behavior but works in Minetest
|
||||
r.warn"parenthesized caret (`^`) with too few escapes"
|
||||
return r.character
|
||||
end
|
||||
elseif r.escapes <= expected_escapes then
|
||||
return r.character
|
||||
end if r.escapes >= 2*expected_escapes then
|
||||
return "\\"
|
||||
end
|
||||
end
|
||||
function rm.popchar(r)
|
||||
assert(not r.eof, "unexpected eof")
|
||||
r.escapes = 0
|
||||
while true do
|
||||
r.character = r:read_char()
|
||||
if r.character ~= "\\" then break end
|
||||
r.escapes = r.escapes + 1
|
||||
end
|
||||
if r.character == nil then
|
||||
assert(r.escapes == 0, "end of texmod expected")
|
||||
r.eof = true
|
||||
end
|
||||
end
|
||||
function rm.pop(r)
|
||||
local expected_escapes = 0
|
||||
if r.level > 0 then
|
||||
-- Premature optimization my beloved (this is `2^(level-1)`)
|
||||
expected_escapes = math.ldexp(0.5, r.level)
|
||||
end
|
||||
if r.escapes > 0 and r.escapes >= 2*expected_escapes then
|
||||
r.escapes = r.escapes - 2*expected_escapes
|
||||
return
|
||||
end
|
||||
return r:popchar()
|
||||
end
|
||||
function rm.match(r, char)
|
||||
if r:peek() == char then
|
||||
r:pop()
|
||||
return true
|
||||
end
|
||||
end
|
||||
function rm.expect(r, char)
|
||||
if not r:match(char) then
|
||||
error(("%q expected"):format(char))
|
||||
end
|
||||
end
|
||||
function rm.hat(r, parenthesized)
|
||||
if r:peek(parenthesized) == (r.invcube and "&" or "^") then
|
||||
r:pop()
|
||||
return true
|
||||
end
|
||||
end
|
||||
function rm.match_charset(r, set)
|
||||
local char = r:peek()
|
||||
if char and char:match(set) then
|
||||
r:pop()
|
||||
return char
|
||||
end
|
||||
end
|
||||
function rm.match_str(r, set)
|
||||
local c = r:match_charset(set)
|
||||
if not c then
|
||||
error(("character in %s expected"):format(set))
|
||||
end
|
||||
local t = {c}
|
||||
while true do
|
||||
c = r:match_charset(set)
|
||||
if not c then break end
|
||||
table.insert(t, c)
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
function rm.int(r)
|
||||
local sign = 1
|
||||
if r:match"-" then sign = -1 end
|
||||
return sign * tonumber(r:match_str"%d")
|
||||
end
|
||||
function rm.fname(r)
|
||||
-- This is overly permissive, as is Minetest;
|
||||
-- we just allow arbitrary characters up until a character which may terminate the name.
|
||||
-- Inside an inventorycube, `&` also terminates names.
|
||||
-- Note that the constructor will however - unlike Minetest - perform validation.
|
||||
-- We could leverage the knowledge of the allowed charset here already,
|
||||
-- but that might lead to more confusing error messages.
|
||||
return r:match_str(r.invcube and "[^:^&){]" or "[^:^){]")
|
||||
end
|
||||
function rm.subtexp(r)
|
||||
r.level = r.level + 1
|
||||
local res = r:texp()
|
||||
r.level = r.level - 1
|
||||
return res
|
||||
end
|
||||
function rm.invcubeside(r)
|
||||
assert(not r.invcube, "can't nest inventorycube")
|
||||
r.invcube = true
|
||||
assert(r:match"{", "'{' expected")
|
||||
local res = r:texp()
|
||||
r.invcube = false
|
||||
return res
|
||||
end
|
||||
function rm.basexp(r)
|
||||
if r:match"(" then
|
||||
local res = r:texp(true)
|
||||
r:expect")"
|
||||
return res
|
||||
end
|
||||
if r:match"[" then
|
||||
local type = r:match_str"%a"
|
||||
local gen_reader = gr[type]
|
||||
if not gen_reader then
|
||||
error("invalid texture modifier: " .. type)
|
||||
end
|
||||
return texmod[type](gen_reader(r))
|
||||
end
|
||||
return texmod.file(r:fname())
|
||||
end
|
||||
function rm.colorspec(r)
|
||||
-- Leave exact validation up to colorspec, only do a rough greedy charset matching
|
||||
return assert(colorspec.from_string(r:match_str"[#%x%a]"))
|
||||
end
|
||||
function rm.texp(r, parenthesized)
|
||||
local base = r:basexp() -- TODO (?) make optional - warn about omitting the base
|
||||
while r:hat(parenthesized) do
|
||||
if r:match"[" then
|
||||
local reader_subtrie = texmod_reader_trie
|
||||
while true do
|
||||
local next_subtrie = reader_subtrie[r:peek()]
|
||||
if next_subtrie then
|
||||
reader_subtrie = next_subtrie
|
||||
r:pop()
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
local type = assert(reader_subtrie.type, "invalid texture modifier")
|
||||
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:blit(texmod[type](gen_reader(r)))
|
||||
end
|
||||
-- TODO (?...) we could consume leftover parameters here to be as lax as Minetest
|
||||
else
|
||||
base = base:blit(r:basexp())
|
||||
end
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
local mt = {__index = rm}
|
||||
return function(read_char, warn --[[function(str)]])
|
||||
local r = setmetatable({
|
||||
level = 0,
|
||||
invcube = false,
|
||||
parenthesized = false,
|
||||
eof = false,
|
||||
read_char = read_char,
|
||||
warn = warn or error,
|
||||
}, mt)
|
||||
r:popchar()
|
||||
local res = r:texp(false)
|
||||
assert(r.eof, "eof expected")
|
||||
return res
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue