thanks @dave1707, but I meant to see @Ignatz’s approach with translate and no table involved in the process of adding continuosly a rect on an interval basis. With setRect I know there’s a limit I can’t go beyond and I think you both pretty answered me already at this regard
@deactive OK, but I don’t think it can be done using translate and no table. Basicly your just moving a picture of objects around the screen. They won’t be individual objects.
@deactive - if they are moving at random speeds, then you’ll need a table of objects because you have to move each one separately.
There is an in between solution where you choose one of (say) 5 random speeds, and put the object into the mesh that has that speed, so you end up with 5 meshes, each of which can be translated and drawn as I showed above. This would still look very random, but would run very fast.
@Jmv38 @Ignatz Just butting in without reading the whole discussion … you can send different parts of a mesh in different directions. My explosion shader uses a single mesh and sends stuff all over the screen.
@LoopSpace - exploding a single object by distorting vertices is one thing. Here we are moving a whole bunch of objects down the screen.
@Ignatz but to a mesh, there’s no difference between a single object composed of many rectangles, and a “whole bunch of objects”.
Yes, but if you want thousands of objects to move down the screen at different speeds, and be able to shoot them, why (and how) would you use a shader?
Hi @dave1707, at the inception of this thread i was asking how to draw more than 500 rects as my code was already doing it with table and setRect (with unstable fps thou). I was wondering if there was a more efficient way to draw with meshes and break that 500 rects limit. As i said you both were clear on that but if there’s any alternative as @ignatz pointed out, im happy to try it and perhaps use for different purposes in the future.
I’m not sure I want to play a game where I have to shoot more than 500 enemies!
look at doujin shooters then, there will be far more than 500. Anyway, 500 is the gross amount of items, ships and bullets i’d like to set in the advanced gameplay.
Why? Because it is incredibly efficient.
How? See below.
Tap on a highlighted square and your score goes up, tap on a dull one and it goes down. Currently generates 1000 squares, but could do more. At 10,000 I was still getting a decent framerate.
-- RectangleShoot
function setup()
setupRects()
for h = 1,10 do
for k=1,100 do
addRect(vec2(math.random(0,WIDTH),HEIGHT+h*10),vec2(0,-math.random(25,50)),0,math.random())
end
end
score = 0
parameter.watch("math.ceil(1/DeltaTime)")
parameter.watch("score")
end
function draw()
background(105, 68, 68, 255)
activateRects()
drawRects()
for k,v in ipairs(lostRects()) do
resetRect(v,vec2(math.random(0,WIDTH),HEIGHT),vec2(0,-math.random(25,50)),0,math.random())
end
end
function touched(t)
if t.state == ENDED then
local r = checkRects(vec2(t.x,t.y))
if r then
if isActive(r) then
score = score + 3
deactivate(r)
else
score = score - 1
end
end
end
end
local rectmesh = mesh()
local size = vec2(10,10)
local rects = {}
local nRects = 0
local origins, velocities, angles, angvels, itimes
function setupRects()
rectmesh.shader = descending()
rectmesh.texture = "Cargo Bot:Codea Icon"
origins = rectmesh:buffer("origin")
velocities = rectmesh:buffer("velocity")
angles = rectmesh:buffer("angle")
angvels = rectmesh:buffer("angvel")
itimes = rectmesh:buffer("itime")
end
function drawRects()
rectmesh.shader.time = ElapsedTime
rectmesh:draw()
end
function addRect(p,v,a,w)
local i = rectmesh:addRect(p.x,p.y,size.x,size.y,a)
rectmesh:resize(rectmesh.size)
table.insert(rects,{i,p,v,a,w,ElapsedTime})
rectmesh:setRectColor(i,127,127,0)
for j=1,6 do
origins[6*(i-1)+j] = p
velocities[6*(i-1)+j] = v
angles[6*(i-1)+j] = a
angvels[6*(i-1)+j] = w
itimes[6*(i-1)+j] = ElapsedTime
end
nRects = nRects + 1
end
function resetRect(i,p,v,a,w)
rectmesh:setRect(i,p.x,p.y,size.x,size.y,a)
rects[i] = {i,p,v,a,w,ElapsedTime}
rectmesh:setRectColor(i,127,127,0)
for j=1,6 do
origins[6*(i-1)+j] = p
velocities[6*(i-1)+j] = v
angles[6*(i-1)+j] = a
angvels[6*(i-1)+j] = w
itimes[6*(i-1)+j] = ElapsedTime
end
end
function checkRects(p)
local v,t
t = ElapsedTime
for k=1,nRects do
v = (p - rects[k][2] - (t-rects[k][6])*rects[k][3]):rotate(-rects[k][4] - (t-rects[k][6])*rects[k][5])
if math.abs(v.x) < size.x and math.abs(v.y) < size.y then
return k
end
end
return false
end
function lostRects()
local t,r = ElapsedTime,{}
for k=1,nRects do
if (rects[k][2] + (t-rects[k][6])*rects[k][3]).y < 0 then
table.insert(r,k)
end
end
return r
end
function activateRects()
for k=1,nRects do
if math.random() > .999 then
if rectmesh:color(6*k-1).r > 200 then
rectmesh:setRectColor(k,127,127,0)
else
rectmesh:setRectColor(k,255,255,255)
end
end
end
end
function isActive(k)
if rectmesh:color(6*k-1).r > 200 then
return true
else
return false
end
end
function deactivate(k)
rectmesh:setRectColor(k,127,127,0)
end
function descending()
return shader([[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform float time;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec2 velocity;
attribute vec2 origin;
attribute float itime;
attribute float angvel;
attribute float angle;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
highp float a = angle + (time - itime)*angvel;
highp vec2 v = position.xy - origin;
highp vec4 p = position;
p.xy = origin + (time-itime)*velocity;
p.x += cos(a)*v.x + sin(a)*v.y;
p.y += -sin(a)*v.x + cos(a)*v.y;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * p;
}
]],[[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
//Set the output color to the texture color
gl_FragColor = col;
}
]])
end
@LoopSpace - I fear your apparent speed is purely because you moved the goalposts by making your objects so small, 10x10.
Make them 100x50 like the other code above, and on my iPad3, your shader version runs 1,000 objects at about 6 FPS, 10x slower than the other code above.
If I make the objects 10x10 in that other code, it can run 10,000 objects at close to 60 FPS. Even if they are small, that’s quite amazing speed (I guess I’m easily impressed because I remember how slow computers used to be).
@Simeon why does the framerate in the following code vary so much? It’s doing the same thing every frame so should be static, but it jumps around like crzy.
-- RectangleShoot
function setup()
setupRects()
for h = 1,10 do
for k=1,100 do
-- addRect(vec2(math.random(0,WIDTH),HEIGHT+h*10),vec2(0,-math.random(25,50)),math.pi*math.random(),math.random())
addRect(vec2(math.random(0,WIDTH),math.random(0,HEIGHT)),vec2(0,-math.random(25,50)),math.pi*math.random(),math.random())
end
end
score = 0
parameter.watch("math.ceil(1/DeltaTime)")
parameter.watch("score")
end
function draw()
background(105, 68, 68, 255)
-- activateRects()
drawRects()
--[[
for k,v in ipairs(lostRects()) do
resetRect(v,vec2(math.random(0,WIDTH),HEIGHT),vec2(0,-math.random(25,50)),math.pi*math.random(),math.random())
end
--]]
end
function touched(t)
if t.state == ENDED then
local r = checkRects(vec2(t.x,t.y))
if r then
if isActive(r) then
score = score + 3
deactivate(r)
else
score = score - 1
end
end
end
end
local rectmesh = mesh()
local size = vec2(50,50)
local rects = {}
local nRects = 0
local origins, velocities, angles, angvels, itimes
function setupRects()
rectmesh.shader = descending()
rectmesh.texture = "Cargo Bot:Codea Icon"
-- origins = rectmesh:buffer("origin")
-- velocities = rectmesh:buffer("velocity")
-- angles = rectmesh:buffer("angle")
-- angvels = rectmesh:buffer("angvel")
-- itimes = rectmesh:buffer("itime")
end
function drawRects()
-- rectmesh.shader.time = ElapsedTime
rectmesh:draw()
end
function addRect(p,v,a,w)
local i = rectmesh:addRect(p.x,p.y,size.x,size.y,a)
rectmesh:resize(rectmesh.size)
table.insert(rects,{i,p,v,a,w,ElapsedTime})
rectmesh:setRectColor(i,127,127,0)
for j=1,6 do
-- origins[6*(i-1)+j] = p
-- velocities[6*(i-1)+j] = v
-- angles[6*(i-1)+j] = a
-- angvels[6*(i-1)+j] = w
-- itimes[6*(i-1)+j] = ElapsedTime
end
nRects = nRects + 1
end
function resetRect(i,p,v,a,w)
rectmesh:setRect(i,p.x,p.y,size.x,size.y,a)
rects[i] = {i,p,v,a,w,ElapsedTime}
rectmesh:setRectColor(i,127,127,0)
for j=1,6 do
-- origins[6*(i-1)+j] = p
-- velocities[6*(i-1)+j] = v
-- angles[6*(i-1)+j] = a
-- angvels[6*(i-1)+j] = w
-- itimes[6*(i-1)+j] = ElapsedTime
end
end
function checkRects(p)
local v,t
t = ElapsedTime
for k=1,nRects do
v = (p - rects[k][2] - (t-rects[k][6])*rects[k][3]):rotate(-rects[k][4] - (t-rects[k][6])*rects[k][5])
if math.abs(v.x) < size.x and math.abs(v.y) < size.y then
return k
end
end
return false
end
function lostRects()
local t,r = ElapsedTime,{}
for k=1,nRects do
if (rects[k][2] + (t-rects[k][6])*rects[k][3]).y < 0 then
table.insert(r,k)
end
end
return r
end
function activateRects()
for k=1,nRects do
if math.random() > .999 then
if rectmesh:color(6*k-1).r > 200 then
rectmesh:setRectColor(k,127,127,0)
else
rectmesh:setRectColor(k,255,255,255)
end
end
end
end
function isActive(k)
if rectmesh:color(6*k-1).r > 200 then
return true
else
return false
end
end
function deactivate(k)
rectmesh:setRectColor(k,127,127,0)
end
function descending()
return shader([[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform float time;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec2 velocity;
attribute vec2 origin;
attribute float itime;
attribute float angvel;
attribute float angle;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
//highp float a = angle + (time - itime)*angvel;
//highp vec2 v = position.xy - origin;
//highp vec4 p = position;
//p.xy += (time-itime)*velocity;
//p.xy = v+origin + (time-itime)*velocity;
//p.x += cos(a)*v.x + sin(a)*v.y;
//p.y += -sin(a)*v.x + cos(a)*v.y;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}
]],[[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
//Set the output color to the texture color
gl_FragColor = col;
}
]])
end
@Ignatz Try this:
-- RectangleShoot
function setup()
setupRects()
for h = 1,10 do
for k=1,100 do
addRect(vec2(math.random(0,WIDTH),HEIGHT+h*10),vec2(0,-math.random(25,50)),math.pi*math.random(),math.random())
end
end
score = 0
parameter.watch("math.ceil(1/DeltaTime)")
parameter.watch("score")
end
function draw()
background(105, 68, 68, 255)
activateRects()
drawRects()
for k,v in ipairs(lostRects()) do
resetRect(v,vec2(math.random(0,WIDTH),HEIGHT),vec2(0,-math.random(25,50)),math.pi*math.random(),math.random())
end
end
function touched(t)
if t.state == ENDED then
local r = checkRects(vec2(t.x,t.y))
if r then
if isActive(r) then
score = score + 3
deactivate(r)
else
score = score - 1
end
end
end
end
local rectmesh = mesh()
local size = vec2(100,50)
local rects = {}
local nRects = 0
local origins, velocities, angles, angvels, itimes, zs
local oz,dz = 1,-0.001
function setupRects()
rectmesh.shader = descending()
rectmesh.texture = "Cargo Bot:Codea Icon"
origins = rectmesh:buffer("origin")
velocities = rectmesh:buffer("velocity")
angles = rectmesh:buffer("angle")
angvels = rectmesh:buffer("angvel")
itimes = rectmesh:buffer("itime")
zs = rectmesh:buffer("z")
end
function drawRects()
rectmesh.shader.time = ElapsedTime
rectmesh:draw()
end
function addRect(p,v,a,w)
local i = rectmesh:addRect(p.x,p.y,size.x,size.y,a)
rectmesh:resize(rectmesh.size)
table.insert(rects,{i,p,v,a,w,ElapsedTime})
rectmesh:setRectColor(i,127,127,0)
for j=1,6 do
origins[6*(i-1)+j] = p
velocities[6*(i-1)+j] = v
angles[6*(i-1)+j] = a
angvels[6*(i-1)+j] = w
itimes[6*(i-1)+j] = ElapsedTime
zs[6*(i-1)+j] = oz
end
nRects = nRects + 1
oz = oz + dz
end
function resetRect(i,p,v,a,w)
rectmesh:setRect(i,p.x,p.y,size.x,size.y,a)
rects[i] = {i,p,v,a,w,ElapsedTime}
rectmesh:setRectColor(i,127,127,0)
for j=1,6 do
origins[6*(i-1)+j] = p
velocities[6*(i-1)+j] = v
angles[6*(i-1)+j] = a
angvels[6*(i-1)+j] = w
itimes[6*(i-1)+j] = ElapsedTime
end
end
function checkRects(p)
local v,t
t = ElapsedTime
for k=1,nRects do
v = (p - rects[k][2] - (t-rects[k][6])*rects[k][3]):rotate(-rects[k][4] - (t-rects[k][6])*rects[k][5])
if math.abs(v.x) < size.x and math.abs(v.y) < size.y then
return k
end
end
return false
end
function lostRects()
local t,r = ElapsedTime,{}
for k=1,nRects do
if (rects[k][2] + (t-rects[k][6])*rects[k][3]).y < 0 then
table.insert(r,k)
end
end
return r
end
function activateRects()
for k=1,nRects do
if math.random() > .999 then
if rectmesh:color(6*k-1).r > 200 then
rectmesh:setRectColor(k,127,127,0)
else
rectmesh:setRectColor(k,255,255,255)
end
end
end
end
function isActive(k)
if rectmesh:color(6*k-1).r > 200 then
return true
else
return false
end
end
function deactivate(k)
rectmesh:setRectColor(k,127,127,0)
end
function descending()
return shader([[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform float time;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec2 velocity;
attribute vec2 origin;
attribute float itime;
attribute float angvel;
attribute float angle;
attribute float z;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
highp float a = angle + (time - itime)*angvel;
highp vec2 v = position.xy - origin;
highp vec4 p = position;
p.z = z;
//p.xy += (time-itime)*velocity;
p.xy = origin + (time-itime)*velocity;
p.x += cos(a)*v.x + sin(a)*v.y;
p.y += -sin(a)*v.x + cos(a)*v.y;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * p;
}
]],[[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
//Set the output color to the texture color
gl_FragColor = col;
}
]])
end
NB Which code are you comparing it with? A fair comparison should be against code that rotates and translates each rectangle individually. Which of the above codes does that?
@LoopSpace - I’m looking at the code, but in the meantime, I notice there is a problem with the transparent pixels surrounding each image being drawn, in that they are not being drawn transparently, but are blocking images behind…
Discarding transparent pixels fixes it. This fragment shader works.
lowp vec4 col = texture2D( texture, vTexCoord );
if (col.a==0.0) discard;
else gl_FragColor = col* vColor;
@Ignatz Incidentally, the reason why I chose to draw my rectangles originally size 10x10 was to make it sensible to draw 1000 objects. If they’re of size 100x50 then there’s only really room for a couple of hundred objects on the screen. Drawing 1000 is a bit of a waste of time.