slime wrote: ↑Mon Mar 27, 2023 9:35 pm
I think I'd either structure the input data such that it doesn't need to branch depending on the shader's declaration (e.g. by having the function implementation always use unpack no matter what), or make multiple versions of the function where the caller can specify what they intend via the function name.
With the branch approach you want, how would you support arrays of vectors?
This shouldn't be a problem for a vector array (unless I missed a case) however another major problem lies elsewhere, thanks to your advice I could see the problem differently.
For the example here is a test shader:
Code: Select all
local shader = lg.newShader[[
extern vec4 col1;
extern vec4 col2;
extern vec4 col3;
extern vec4 cols[3];
vec4 effect(vec4 c, Image t, vec2 tc, vec2 sc) {
int v = int(mod(sc.x, 6.0));
if (v==0) return col1;
if (v==1) return col2;
if (v==2) return col3;
return cols[v-3];
}
]]
With this shader, to always use unpack would require having a table like this and it's not really desirable:
Code: Select all
local values = {
col1 = { {0,1,1,1} };
col2 = { {1,0,1,1} };
col3 = { {1,1,0,1} };
cols = {
{0,0,1,1},
{0,1,0,1},
{1,0,0,1}
}
}
So I opted for the option to get all the values that follow a character string in order to send them to the shader (since I'm not using a table I also did a recursive `select` function to get the parameters from N1 to N2):
Code: Select all
local function get_elements(n1, n2, ...)
if n1 > n2 then
return
else
return select(n1, ...), get_elements(n1 + 1, n2, ...)
end
end
local function send(shader, ...)
local n = select("#", ...)
local key;
local i = 1
while i <= n do
local v = select(i, ...)
if type(v) == "string" then
key = v
else
local from, to = i, nil
while i <= n and type(v) ~= "string" do
to, i = i, i + 1
v = select(i, ...)
end
shader:send(key, get_elements(from, to, ...))
goto continue
end
i = i + 1
::continue::
end
end
The function can be called like this:
Code: Select all
send(shader,
"col1", {0,1,1,1},
"col2", {1,0,1,1},
"col3", {1,1,0,1},
"cols", {0,0,1,1},
{0,1,0,1},
{1,0,0,1}
)
The exercise is actually much less obvious if we send it a table in this form:
Code: Select all
values = {
col1 = {0,1,1,1};
col2 = {1,0,1,1};
col3 = {1,1,0,1};
cols = {
{0,0,1,1},
{0,1,0,1},
{1,0,0,1}
}
}
Because it is impossible to guess intuitively if the `col1` corresponds to an array or to a vector without adding other values which will make the table heavier, which in any case will end up being discarded almost immediately.
But it would have been much easier to do if the Shader:getExternVariable method was still available, hence my despair
So good, I was finally able to do what I wanted but if you have the answer to why this method is no longer available it will relieve my disappointment
Thank you anyway for your opinion, I might not have done this tonight otherwise!
Here is my complete code if you want to try it on your side:
Code: Select all
local lg = love.graphics
local mask = lg.newCanvas()
local shader = lg.newShader[[
extern vec4 col1;
extern vec4 col2;
extern vec4 col3;
extern vec4 cols[3];
vec4 effect(vec4 c, Image t, vec2 tc, vec2 sc) {
int v = int(mod(sc.x, 48.0))/8;
if (v==0) return col1;
if (v==1) return col2;
if (v==2) return col3;
return cols[v-3];
}
]]
local function get_elements(n1, n2, ...)
if n1 > n2 then
return
else
return select(n1, ...), get_elements(n1 + 1, n2, ...)
end
end
local function send(shader, ...)
local n = select("#", ...)
local key;
local i = 1
while i <= n do
local v = select(i, ...)
if type(v) == "string" then
key = v
else
local from, to = i, nil
while i <= n and type(v) ~= "string" do
to, i = i, i + 1
v = select(i, ...)
end
shader:send(key, get_elements(from, to, ...))
goto continue
end
i = i + 1
::continue::
end
end
send(shader,
"col1", {0,1,1,1},
"col2", {1,0,1,1},
"col3", {1,1,0,1},
"cols", {0,0,1,1},
{0,1,0,1},
{1,0,0,1}
)
function love.draw()
lg.setShader(shader)
lg.draw(mask)
lg.setShader()
end