forked from Mirrorlandia_minetest/digistuff
a5ca8f19b7
This allows part of a buffer to be sent instead of the whole thing, meaning that driving digiscreen arrays can now be done with fewer commands and no temporary buffers. This also greatly improves the speed of doing so, and I suppose also allows hardware-accelerated scrolling if your display is smaller than 64x64...
459 lines
16 KiB
Lua
459 lines
16 KiB
Lua
local font = dofile(minetest.get_modpath("digistuff")..DIR_DELIM.."gpu-font.lua")
|
|
|
|
local function explodebits(input)
|
|
local output = {}
|
|
for i=0,7,1 do
|
|
output[i] = input%(2^(i+1)) >= 2^i
|
|
end
|
|
return output
|
|
end
|
|
|
|
local function implodebits(input)
|
|
local output = 0
|
|
for i=0,7,1 do
|
|
output = output + (input[i] and 2^i or 0)
|
|
end
|
|
return output
|
|
end
|
|
|
|
local function rgbtohsv(r,g,b)
|
|
r = r/255
|
|
g = g/255
|
|
b = b/255
|
|
max = math.max(r,g,b)
|
|
min = math.min(r,g,b)
|
|
delta = max-min
|
|
local hue = 0
|
|
if delta > 0 then
|
|
if max == r then
|
|
hue = (g-b)/delta
|
|
hue = (hue%6)*60
|
|
elseif max == g then
|
|
hue = (b-r)/delta
|
|
hue = 60*(hue+2)
|
|
elseif max == b then
|
|
hue = (r-g)/delta
|
|
hue = 60*(hue+4)
|
|
end
|
|
hue = hue/360
|
|
end
|
|
local sat = 0
|
|
if max > 0 then
|
|
sat = delta/max
|
|
end
|
|
return math.floor(hue*255),math.floor(sat*255),math.floor(max*255)
|
|
end
|
|
|
|
local function hsvtorgb(h,s,v)
|
|
h = h/255*360
|
|
s = s/255
|
|
v = v/255
|
|
local c = s*v
|
|
local x = (h/60)%2
|
|
x = 1-math.abs(x-1)
|
|
x = x*c
|
|
local m = v-c
|
|
local r = 0
|
|
local g = 0
|
|
local b = 0
|
|
if h < 60 then
|
|
r = c
|
|
g = x
|
|
elseif h < 120 then
|
|
r = x
|
|
g = c
|
|
elseif h < 180 then
|
|
g = c
|
|
b = x
|
|
elseif h < 240 then
|
|
g = x
|
|
b = c
|
|
elseif h < 300 then
|
|
r = x
|
|
b = c
|
|
else
|
|
r = c
|
|
b = x
|
|
end
|
|
r = r+m
|
|
g = g+m
|
|
b = b+m
|
|
return math.floor(r*255),math.floor(g*255),math.floor(b*255)
|
|
end
|
|
|
|
local function bitwiseblend(srcr,dstr,srcg,dstg,srcb,dstb,mode)
|
|
local srbits = explodebits(srcr)
|
|
local sgbits = explodebits(srcg)
|
|
local sbbits = explodebits(srcb)
|
|
local drbits = explodebits(dstr)
|
|
local dgbits = explodebits(dstg)
|
|
local dbbits = explodebits(dstb)
|
|
for i=0,7,1 do
|
|
if mode == "and" then
|
|
drbits[i] = srbits[i] and drbits[i]
|
|
dgbits[i] = sgbits[i] and dgbits[i]
|
|
dbbits[i] = sbbits[i] and dbbits[i]
|
|
elseif mode == "or" then
|
|
drbits[i] = srbits[i] or drbits[i]
|
|
dgbits[i] = sgbits[i] or dgbits[i]
|
|
dbbits[i] = sbbits[i] or dbbits[i]
|
|
elseif mode == "xor" then
|
|
drbits[i] = srbits[i] ~= drbits[i]
|
|
dgbits[i] = sgbits[i] ~= dgbits[i]
|
|
dbbits[i] = sbbits[i] ~= dbbits[i]
|
|
elseif mode == "xnor" then
|
|
drbits[i] = srbits[i] == drbits[i]
|
|
dgbits[i] = sgbits[i] == dgbits[i]
|
|
dbbits[i] = sbbits[i] == dbbits[i]
|
|
elseif mode == "not" then
|
|
drbits[i] = not srbits[i]
|
|
dgbits[i] = not sgbits[i]
|
|
dbbits[i] = not sbbits[i]
|
|
elseif mode == "nand" then
|
|
drbits[i] = not (srbits[i] and drbits[i])
|
|
dgbits[i] = not (sgbits[i] and dgbits[i])
|
|
dbbits[i] = not (sbbits[i] and dbbits[i])
|
|
elseif mode == "nor" then
|
|
drbits[i] = not (srbits[i] or drbits[i])
|
|
dgbits[i] = not (sgbits[i] or dgbits[i])
|
|
dbbits[i] = not (sbbits[i] or dbbits[i])
|
|
end
|
|
end
|
|
return string.format("%02X%02X%02X",implodebits(drbits),implodebits(dgbits),implodebits(dbbits))
|
|
end
|
|
|
|
local function blend(src,dst,mode,transparent)
|
|
local srcr = tonumber(string.sub(src,1,2),16)
|
|
local srcg = tonumber(string.sub(src,3,4),16)
|
|
local srcb = tonumber(string.sub(src,5,6),16)
|
|
local dstr = tonumber(string.sub(dst,1,2),16)
|
|
local dstg = tonumber(string.sub(dst,3,4),16)
|
|
local dstb = tonumber(string.sub(dst,5,6),16)
|
|
local op = "normal"
|
|
if type(mode) == "string" then op = string.lower(mode) end
|
|
if op == "normal" then
|
|
return src
|
|
elseif op == "nop" then
|
|
return dst
|
|
elseif op == "overlay" then
|
|
return (string.upper(src) == string.upper(transparent)) and dst or src
|
|
elseif op == "add" then
|
|
local r = math.min(255,srcr+dstr)
|
|
local g = math.min(255,srcg+dstg)
|
|
local b = math.min(255,srcb+dstb)
|
|
return string.format("%02X%02X%02X",r,g,b)
|
|
elseif op == "sub" then
|
|
local r = math.max(0,dstr-srcr)
|
|
local g = math.max(0,dstg-srcg)
|
|
local b = math.max(0,dstb-srcb)
|
|
return string.format("%02X%02X%02X",r,g,b)
|
|
elseif op == "isub" then
|
|
local r = math.max(0,srcr-dstr)
|
|
local g = math.max(0,srcg-dstg)
|
|
local b = math.max(0,srcb-dstb)
|
|
return string.format("%02X%02X%02X",r,g,b)
|
|
elseif op == "average" then
|
|
local r = math.min(255,(srcr+dstr)/2)
|
|
local g = math.min(255,(srcg+dstg)/2)
|
|
local b = math.min(255,(srcb+dstb)/2)
|
|
return string.format("%02X%02X%02X",r,g,b)
|
|
elseif op == "and" or op == "or" or op == "xor" or op == "xnor" or op == "not" or op == "nand" or op == "nor" then
|
|
return bitwiseblend(srcr,dstr,srcg,dstg,srcb,dstb,op)
|
|
elseif op == "tohsv" or op == "rgbtohsv" then
|
|
return string.format("%02X%02X%02X",rgbtohsv(srcr,srcg,srcb))
|
|
elseif op == "torgb" or op == "hsvtorgb" then
|
|
return string.format("%02X%02X%02X",hsvtorgb(srcr,srcg,srcb))
|
|
else
|
|
return src
|
|
end
|
|
end
|
|
|
|
local function runcommand(pos,meta,command)
|
|
if type(command) ~= "table" then return end
|
|
if command.command == "createbuffer" then
|
|
if type(command.buffer) ~= "number" or type(command.xsize) ~= "number" or type(command.ysize) ~= "number" then return end
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local xsize = math.min(64,math.floor(command.xsize))
|
|
local ysize = math.min(64,math.floor(command.ysize))
|
|
if xsize < 1 or ysize < 1 then return end
|
|
local fillcolor = command.fill
|
|
if type(fillcolor) ~= "string" or string.len(fillcolor) > 7 or string.len(fillcolor) < 6 then fillcolor = "000000" end
|
|
if string.sub(fillcolor,1,1) == "#" then fillcolor = string.sub(fillcolor,2,7) end
|
|
if not tonumber(fillcolor,16) then fillcolor = "000000" end
|
|
local buffer = {}
|
|
buffer.xsize = xsize
|
|
buffer.ysize = ysize
|
|
for y=1,ysize,1 do
|
|
buffer[y] = {}
|
|
for x=1,xsize,1 do
|
|
buffer[y][x] = fillcolor
|
|
end
|
|
end
|
|
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
|
|
elseif command.command == "send" then
|
|
if type(command.buffer) ~= "number" or type(command.channel) ~= "string" then return end
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local buffer = meta:get_string("buffer"..bufnum)
|
|
if string.len(buffer) == 0 then return end
|
|
buffer = minetest.deserialize(buffer)
|
|
if type(buffer) == "table" then
|
|
digiline:receptor_send(pos,digiline.rules.default,command.channel,buffer)
|
|
end
|
|
elseif command.command == "sendregion" then
|
|
if type(command.buffer) ~= "number" or type(command.channel) ~= "string" then return end
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local buffer = meta:get_string("buffer"..bufnum)
|
|
if string.len(buffer) == 0 then return end
|
|
buffer = minetest.deserialize(buffer)
|
|
if type(buffer) ~= "table" then return end
|
|
if type(command.x1) ~= "number" or type(command.x2) ~= "number" or type(command.y1) ~= "number" or type(command.x2) ~= "number" then return end
|
|
local x1 = math.min(64,math.floor(command.x1))
|
|
local y1 = math.min(64,math.floor(command.y1))
|
|
local x2 = math.min(64,math.floor(command.x2))
|
|
local y2 = math.min(64,math.floor(command.y2))
|
|
if x1 < 1 or y1 < 1 or x2 < 1 or y2 < 1 then return end
|
|
x2 = math.min(x2,buffer.xsize)
|
|
y2 = math.min(y2,buffer.ysize)
|
|
if x1 > x2 or y1 > y2 then return end
|
|
local tempbuf = {}
|
|
for y=y1,y2,1 do
|
|
local dsty = y-y1+1
|
|
tempbuf[dsty] = {}
|
|
for x=x1,x2,1 do
|
|
local dstx = x-x1+1
|
|
tempbuf[dsty][dstx] = buffer[y][x]
|
|
end
|
|
end
|
|
digiline:receptor_send(pos,digiline.rules.default,command.channel,tempbuf)
|
|
elseif command.command == "drawrect" then
|
|
if type(command.buffer) ~= "number" or type(command.x1) ~= "number" or type(command.y1) ~= "number" or type(command.x2) ~= "number" or type(command.y2) ~= "number" then return end
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local x1 = math.min(64,math.floor(command.x1))
|
|
local y1 = math.min(64,math.floor(command.y1))
|
|
local x2 = math.min(64,math.floor(command.x2))
|
|
local y2 = math.min(64,math.floor(command.y2))
|
|
if x1 < 1 or y1 < 1 or x2 < 1 or y2 < 1 then return end
|
|
local buffer = meta:get_string("buffer"..bufnum)
|
|
if string.len(buffer) == 0 then return end
|
|
buffer = minetest.deserialize(buffer)
|
|
if type(buffer) ~= "table" then return end
|
|
x2 = math.min(x2,buffer.xsize)
|
|
y2 = math.min(y2,buffer.ysize)
|
|
if x1 > x2 or y1 > y2 then return end
|
|
local fillcolor = command.fill
|
|
if type(fillcolor) ~= "string" or string.len(fillcolor) > 7 or string.len(fillcolor) < 6 then fillcolor = "000000" end
|
|
if string.sub(fillcolor,1,1) == "#" then fillcolor = string.sub(fillcolor,2,7) end
|
|
if not tonumber(fillcolor,16) then fillcolor = "000000" end
|
|
local edgecolor = command.edge
|
|
if type(edgecolor) ~= "string" or string.len(edgecolor) > 7 or string.len(edgecolor) < 6 then edgecolor = fillcolor end
|
|
if string.sub(edgecolor,1,1) == "#" then edgecolor = string.sub(edgecolor,2,7) end
|
|
if not tonumber(edgecolor,16) then edgecolor = fillcolor end
|
|
for y=y1,y2,1 do
|
|
for x=x1,x2,1 do
|
|
buffer[y][x] = fillcolor
|
|
end
|
|
end
|
|
if fillcolor ~= edgecolor then
|
|
for x=x1,x2,1 do
|
|
buffer[y1][x] = edgecolor
|
|
buffer[y2][x] = edgecolor
|
|
end
|
|
for y=y1,y2,1 do
|
|
buffer[y][x1] = edgecolor
|
|
buffer[y][x2] = edgecolor
|
|
end
|
|
end
|
|
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
|
|
elseif command.command == "drawpoint" then
|
|
if type(command.buffer) ~= "number" or type(command.x) ~= "number" or type(command.y) ~= "number" then return end
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local x = math.floor(command.x)
|
|
local y = math.floor(command.y)
|
|
if x < 1 or y < 1 then return end
|
|
local buffer = meta:get_string("buffer"..bufnum)
|
|
if string.len(buffer) == 0 then return end
|
|
buffer = minetest.deserialize(buffer)
|
|
if type(buffer) ~= "table" then return end
|
|
if x > buffer.xsize or y > buffer.ysize then return end
|
|
local color = command.color
|
|
if type(color) ~= "string" or string.len(color) > 7 or string.len(color) < 6 then color = "000000" end
|
|
if string.sub(color,1,1) == "#" then color = string.sub(color,2,7) end
|
|
if not tonumber(color,16) then color = "000000" end
|
|
buffer[y][x] = color
|
|
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
|
|
elseif command.command == "copy" then
|
|
if type(command.src) ~= "number" or type(command.dst) ~= "number" or type(command.srcx) ~= "number" or type(command.srcy) ~= "number" or type(command.dstx) ~= "number" or type(command.dsty) ~= "number" or type(command.xsize) ~= "number" or type(command.ysize) ~= "number" then return end
|
|
local src = math.floor(command.src)
|
|
if src < 0 or src > 7 then return end
|
|
local dst = math.floor(command.dst)
|
|
if dst < 0 or dst > 7 then return end
|
|
local srcx = math.floor(command.srcx)
|
|
local srcy = math.floor(command.srcy)
|
|
local dstx = math.floor(command.dstx)
|
|
local dsty = math.floor(command.dsty)
|
|
local xsize = math.floor(command.xsize)
|
|
local ysize = math.floor(command.ysize)
|
|
if srcx < 1 or srcy < 1 or dstx < 1 or dsty < 1 or xsize < 1 or ysize < 1 then return end
|
|
local sourcebuffer = meta:get_string("buffer"..src)
|
|
local destbuffer = meta:get_string("buffer"..dst)
|
|
if string.len(sourcebuffer) == 0 then return end
|
|
sourcebuffer = minetest.deserialize(sourcebuffer)
|
|
if type(sourcebuffer) ~= "table" then return end
|
|
if string.len(destbuffer) == 0 then return end
|
|
destbuffer = minetest.deserialize(destbuffer)
|
|
if type(destbuffer) ~= "table" then return end
|
|
if srcx + xsize-1 > sourcebuffer.xsize or srcy + ysize-1 > sourcebuffer.ysize then return end
|
|
if dstx + xsize-1 > destbuffer.xsize or dsty + ysize-1 > destbuffer.ysize then return end
|
|
local transparent = command.transparent
|
|
if type(transparent) ~= "string" or string.len(transparent) > 7 or string.len(transparent) < 6 then transparent = "000000" end
|
|
if string.sub(transparent,1,1) == "#" then transparent = string.sub(transparent,2,7) end
|
|
if not tonumber(transparent,16) then transparent = "000000" end
|
|
for y=0,ysize-1,1 do
|
|
for x=0,xsize-1,1 do
|
|
local srcpx = sourcebuffer[srcy+y][srcx+x]
|
|
local destpx = destbuffer[dsty+y][dstx+x]
|
|
destbuffer[dsty+y][dstx+x] = blend(srcpx,destpx,command.mode,transparent)
|
|
end
|
|
end
|
|
meta:set_string("buffer"..dst,minetest.serialize(destbuffer))
|
|
elseif command.command == "load" then
|
|
if type(command.buffer) ~= "number" or type(command.x) ~= "number" or type(command.y) ~= "number" or type(command.data) ~= "table" then return end
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local xstart = math.floor(command.x)
|
|
local ystart = math.floor(command.y)
|
|
if xstart < 1 or ystart < 1 then return end
|
|
local buffer = meta:get_string("buffer"..bufnum)
|
|
if string.len(buffer) == 0 then return end
|
|
buffer = minetest.deserialize(buffer)
|
|
if type(buffer) ~= "table" then return end
|
|
if type(command.data[1]) ~= "table" then return end
|
|
if #command.data[1] < 1 then return end
|
|
local ysize = #command.data
|
|
local xsize = #command.data[1]
|
|
if xstart+xsize-1 > buffer.xsize or ystart+ysize-1 > buffer.ysize then return end
|
|
for y=1,ysize,1 do
|
|
if type(command.data[y]) == "table" then
|
|
for x=1,xsize,1 do
|
|
local color = command.data[y][x]
|
|
if type(color) == "string" then
|
|
if string.len(color) == 7 then color = string.sub(color,2,7) end
|
|
if tonumber(color,16) then
|
|
buffer[ystart+y-1][xstart+x-1] = color
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
|
|
elseif command.command == "text" then
|
|
if type(command.buffer) ~= "number" or type(command.x) ~= "number" or type(command.y) ~= "number" or type(command.text) ~= "string" or string.len(command.text) < 1 then return end
|
|
command.text = string.sub(command.text,1,16)
|
|
local bufnum = math.floor(command.buffer)
|
|
if bufnum < 0 or bufnum > 7 then return end
|
|
local x = math.floor(command.x)
|
|
local y = math.floor(command.y)
|
|
if x < 1 or y < 1 then return end
|
|
local buffer = meta:get_string("buffer"..bufnum)
|
|
if string.len(buffer) == 0 then return end
|
|
buffer = minetest.deserialize(buffer)
|
|
if type(buffer) ~= "table" then return end
|
|
if x > buffer.xsize or y > buffer.ysize then return end
|
|
local color = command.color
|
|
if type(color) ~= "string" or string.len(color) > 7 or string.len(color) < 6 then color = "ff6600" end
|
|
if string.sub(color,1,1) == "#" then color = string.sub(color,2,7) end
|
|
if not tonumber(color,16) then color = "ff6600" end
|
|
for i=1,string.len(command.text),1 do
|
|
local char = font[string.byte(string.sub(command.text,i,i))]
|
|
for chary=1,12,1 do
|
|
for charx=1,5,1 do
|
|
local startx = x + (i*6-6)
|
|
if char[chary][charx] and y+chary-1 <= buffer.ysize and startx+charx-1 <= buffer.xsize then
|
|
local dstpx = buffer[y+chary-1][startx+charx-1]
|
|
buffer[y+chary-1][startx+charx-1] = blend(color,dstpx,command.mode,"")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
meta:set_string("buffer"..bufnum,minetest.serialize(buffer))
|
|
end
|
|
end
|
|
|
|
minetest.register_node("digistuff:gpu", {
|
|
description = "Digilines 2D Graphics Processor",
|
|
groups = {cracky=3},
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("formspec","field[channel;Channel;${channel}")
|
|
end,
|
|
tiles = {
|
|
"digistuff_gpu_top.png",
|
|
"jeija_microcontroller_bottom.png",
|
|
"jeija_microcontroller_sides.png",
|
|
"jeija_microcontroller_sides.png",
|
|
"jeija_microcontroller_sides.png",
|
|
"jeija_microcontroller_sides.png"
|
|
},
|
|
inventory_image = "digistuff_gpu_top.png",
|
|
drawtype = "nodebox",
|
|
selection_box = {
|
|
--From luacontroller
|
|
type = "fixed",
|
|
fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
|
|
},
|
|
_digistuff_channelcopier_fieldname = "channel",
|
|
node_box = {
|
|
--From Luacontroller
|
|
type = "fixed",
|
|
fixed = {
|
|
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, -- Bottom slab
|
|
{-5/16, -7/16, -5/16, 5/16, -6/16, 5/16}, -- Circuit board
|
|
{-3/16, -6/16, -3/16, 3/16, -5/16, 3/16}, -- IC
|
|
}
|
|
},
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
on_receive_fields = function(pos, formname, fields, sender)
|
|
local name = sender:get_player_name()
|
|
if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then
|
|
minetest.record_protection_violation(pos,name)
|
|
return
|
|
end
|
|
local meta = minetest.get_meta(pos)
|
|
if fields.channel then meta:set_string("channel",fields.channel) end
|
|
end,
|
|
digiline =
|
|
{
|
|
receptor = {},
|
|
effector = {
|
|
action = function(pos,node,channel,msg)
|
|
local meta = minetest.get_meta(pos)
|
|
if meta:get_string("channel") ~= channel or type(msg) ~= "table" then return end
|
|
if type(msg[1]) == "table" then
|
|
for i=1,32,1 do
|
|
if type(msg[i]) == "table" then
|
|
runcommand(pos,meta,msg[i])
|
|
end
|
|
end
|
|
else
|
|
runcommand(pos,meta,msg)
|
|
end
|
|
end
|
|
},
|
|
},
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "digistuff:gpu",
|
|
recipe = {
|
|
{"","default:steel_ingot",""},
|
|
{"digilines:wire_std_00000000","mesecons_luacontroller:luacontroller0000","digilines:wire_std_00000000"},
|
|
{"dye:red","dye:green","dye:blue"},
|
|
}
|
|
})
|