Improving code

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?

@deactive How many objects do you want on the screen at one time.

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 :slight_smile: 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! :smiley:

:slight_smile: 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.

@Ignatz

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

@LoopSpace - I averaged it out to get my figures above

@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.