Codea 1.5 (Beta 11)

Starting a new thread for this beta.

This beta initiates some work on the Shader Lab — my intention has always been for uniform variables in the Shader Lab to be interactive. In this build you can tap float/vec2/vec3/vec4 uniforms to adjust their values in real-time. You can try this on the Ripple example shader (on the time and freq variables in the Fragment program).

The idea is that these values will end up saved with the shader as “sample” values. The plan is also to allow sampler2D uniforms to bring up the image picker so textures (or CAMERA input) can be selected from the Shader Lab. Vec4 types should also get the colour picker as an option.

Let me know if you have any other ideas in this area.

.@Jmv38 this should fix the cursor positioning bug on iOS 5.x — please let me know

.@tnlogy this should fix the “+” character wrapping issue you reproduced in the previous thread

.@mpilgrem this fixes some of the documentation bugs you mentioned earlier (duplicate backingMode and so on).

Edit: The build is still uploading. I have a terribly slow Internet connection.

Nice :slight_smile:
Regarding the shader uniforms, so far it seems to work, but not when multiple variables are set in the same line like:
uniform lowp vec2 a, b;
None are selectable that way

Concerning shader resources: Shaderrific on the app store is great: the free version comes with 22 great shaders to copy/ paste for us. Much better than GLSL studio - and free!
@Simeon the cursor pb seems to be solved. Thanks sooooo much!

The stability of the Shader Lab Editor seems to be good in beta 1.5(11).

I wrote the following code to understand how Lua types might map/cast to GLSL ES types:

Vertex shader:


//
// Vertex shader: Uniform Explorer
//

uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec2 texCoord;

varying highp vec2 vTexCoord;

void main() {
    vTexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
    gl_Position = modelViewProjection * position;
}

Fragment shader:


//
// Fragment shader: Uniform Explorer
//

precision highp float;
uniform lowp sampler2D texture;
uniform float uFloat;
uniform int uInt;
uniform bool uBool;
uniform vec2 uVec2;
uniform vec4 uVec4;
uniform ivec2 uIvec2;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main() {
    lowp vec4 col = texture2D( texture, vTexCoord );
    col = col * uFloat * float(uInt + 1) * float(uIvec2.x + 1)
        * uVec2.x;
    if (uBool)
        col = uVec4;
    gl_FragColor = col;
}

Lua:


--
-- Uniform Explorer
--

function setup()
    local s = 200
    m = mesh()
    m.vertices = {vec2(-s, -s), vec2(s, -s), vec2(0, s)}
    -- UV origin in top left corner of texture
    m.texCoords = {vec2(0, 1), vec2(1, 1), vec2(0.5, 0)}
    m.texture = readImage("Small World:Court")
    m:setColors(255, 255, 255)
    m.shader = shader("Documents:UniformExplorer")
    parameter.number("uFloat", 0, 1, 1)
    parameter.number("uInt", -2, 3, 0)
    parameter.boolean("uBool", true)
    parameter.number("uNumber", -2, 2, 0)
    parameter.number("uVec2x", 0, 1, 1)
    parameter.color("uVec4", color(255, 0, 0))
    parameter.number("uIvec2x", -2, 3, 0)
end

function draw()
    background(0)
    translate(WIDTH / 2, HEIGHT / 2)
    m.shader.uFloat = uFloat
    m.shader.uInt = uInt -- math.modf(uInt)?
    -- m.shader.uBool = uBool does not work
    m.shader.uBool = uNumber -- math.modf(uNumber)?
    m.shader.uVec2 = vec2(uVec2x, 0)
    m.shader.uVec4 = uVec4
    -- The following does not work
    m.shader.uIvec2 = vec2(uIvec2x, 0)
    m:draw()
end

I was expecting Lua boolean types to map to ES bool, but it looks like math.modf(Lua number) == 0 maps to false and other Lua number values map to true.

It looks like math.modf(Lua number) maps to ES int.

It looks like ES ivec2 (etc) are not wired up.

BUG SETCONTEXT this is a rather special case, but for once it shows up in a short program, here it is. On my ipad the blue square is leaking to the top, but it stops after some time, differently for bottom bar and top bar. And the red squre does not leak… Maybe solving this will solve all the other weird cases?
The bug does not show if i write img in img2.



--# Main
-- test sprite
displayMode(FULLSCREEN)
-- Use this function to perform your initial setup
function setup()
    img = image(WIDTH,HEIGHT)
    img2 = image(WIDTH,HEIGHT)
    setContext(img)
        background(0, 0, 0, 0)
        fill(0, 12, 255, 0)
        strokeWidth(7)
        stroke(0, 12, 255, 255)
        rect(300,300,100,100)
        stroke(255, 0, 0, 255)
        rect(0,0,WIDTH,HEIGHT)
    setContext()
    spriteMode(CORNER)
    setContext(img2)
        background(0, 0, 0, 0)
    setContext()
end

-- This function gets called once every frame
function draw()
    background(40, 40, 50)
    strokeWidth(5)
    sprite(img,0,0)
    setContext(img)
        sprite(img,0,0)
    setContext()
end

REQUEST i want to support @tnlogy request to have shaders in ‘normal’ tabs. One of the great values of Codea is the pleasure of quickly testing other’s programs. This is possible via the single copy/paste operation as implemented today. If shaders have to be copied one by one, in parts, then this value is gone. I don’t even try present programs when they are posted as separate tabs, because it is too much effort for unsure result (they don’t always work once copied, or were not worth it). It would be quite simple for you to implements shaders as you did for Class() tabs. For instance (might not be the correct syntax):

shadow = Shader()
Shader:vertexShader()
end
Shader:fragmentShader()
end

we could still developp the shader in the shader lab,
And once we are happy with it copy it to a tab if we want to share the project.
The program when run would first check if there are shaders defined in it and if so create them dynamically (and remove them at the end of execution, restoring the original shader with this name if any).
The difficulty for you would be to have 2 tab types: one with LUA and the other with GLSL language. And you have to check syntax and colors accordingly.
But i am fairly sure most users would prefer this structure, for many reasons (sharing, saving, Managing versions, etc…)

.@mpilgrem At the moment only sampler2d, bool, int, float, vec2, vec3, vec4, mat4 are hooked up (also float, int and bool arrays).

We’ll need to add ivec and Lua boolean type support.

.@Jmv38 your setContext bug is not showing up here — I’ll keep testing. Though it looks like you are rendering an image into itself (setContext(img) → sprite(img)). I’m not sure whether that is going to generate correct results.

Regarding shaders in tabs. It’s a good idea, a big change, and I’ll need to think about whether it’s possible. Probably unlikely for 1.5.

At the moment it’s possible to put your shaders into Codea code by doing:

myShader.vertexProgram = "... source code ..."
myShader.fragmentProgram = "... source code ..."

Not a great solution, but one that works at the moment.

Thanks for your concern @Simeon. I just posted a setContext bug example without self writing into an image, see the forum. For the shaders: if it is possible to include them into the program with the two lines above, and just copy/paste the code from the shader lab, without additionnal editing, then that’s good enough for me: the target ‘easy sharing for users’ is hit. Maybe keep the precious ressource of your time for more critical tasks. Like keeping Codea compatible with former versions of ios, let’s say ios5.1 :wink:

Hello @Simeon. Why do you hook up GLSL ES bool (which, as I understand it, is a pure boolean: true or false only) to Lua number rather than to Lua boolean (which, as I understand it, is also a pure boolean)?

(Update) I wonder if Lua values of type other than Lua boolean should be first cast to Lua boolean (if they can) before being passed to a uniform that is of GLSL ES type bool?

BUG COROUTINE the following code works fine, except when i try to print something with the coroutine it kills codea. All web examples use print in coroutine, so it should work?



--# Main
-- test coroutine simple

co = coroutine.create( function()
    for i = 1, 10 do
        value = i
        -- print(value) -- uncomment this line to kill codea
        coroutine.yield()
    end
end)

function setup()
    print("Hello World!")
    value = 0
    coroutine.resume(co)
end

-- This function gets called once every frame
function draw()
    background(40, 40, 50)
    text(tostring(value),WIDTH/2,HEIGHT/2)
end
function touched(touch)
    if touch.state == BEGAN then coroutine.resume(co) end
end

I find the same as @Jmv38: print() in coroutine code causes misbehaviour. For example, this will not run twice or three times (locking up the Editor with a glowing run button):


--
-- Coroutine Tester
--

function code()
    print("Bug!")
    myGlobal = "Code called!"
end

function setup()
    myCoroutine = coroutine.create(code)
    coroutine.resume(myCoroutine)
    print(myGlobal)
end

function draw() background(0) end

.@mpilgrem

I was under the impression that Lua didn’t have a native boolean type, I’ll map it to a boolean instead with appropriate casting.

I think that blendMode() returns 0 0 0 0, irrespective of the blend mode that has been set, in error.

Thanks @mpilgrem. I’ve fixed the blendMode return issue.

bug coroutine it seems coroutine in not compatible with setContext(). Uncomment the setContext section below to kill codea.

increment5 = coroutine.wrap( function(img,out,budget)
    local r0,g0,b0,a0
    local r,g,b,a
    local c,count =0,0
    local t0 = 0
    local clock = os.clock
    while true do
    
    for x = 2, img.width-1 do
        for y = 2, img.height-1 do
            r0,g0,b0,a0 = 0,0,0,0
            for p=1,3 do for q=1,3 do
                r,g,b,a = img:get(x+p-2,y+q-2)
                r0,g0,b0,a0 = r0+r ,g0+g ,b0+b ,a0+a
            end end
            r0,g0,b0,a0 = r0/9, g0/9, b0/9, a0/9
            out:set(x,y,r0,g0,b0,a0)
        end
        if t0 < clock() then 
            coroutine.yield() 
            t0 = clock()+1/60 * budget
        end
        value3 = x
    end
    --[[
    setContext(img)
        spriteMode(CORNER)
        sprite(out)
    setContext()
    --]]
    count = count + 1
    value4 = count
    end
end)


function setup()
    print("Hello World!")
    value = 0
    img5 = image(100,76)
    setContext(img5)
        background(0, 9, 255, 255)
        fill(255, 0, 0, 255)
        rect(20,20,20,20)
    setContext()
    out5 = image(100,76)
end

-- This function gets called once every frame
function draw()
    background(40, 40, 50)
    fontSize(50)
    local x=WIDTH/2
    text("increment3 : "..tostring(value3),x,HEIGHT-200)
    text("increment4 : "..tostring(value4),x,HEIGHT-250)
    sprite(img5,x,HEIGHT-400)
    sprite(out5,x-200,HEIGHT-400)
    increment5(img5,out5,0.1) 
end

BUG COROUTINE defining an image inside a coroutine kills codea

    local out = image(img.width,img.height)

Hello @Simeon - three thoughts on the blendMode() addition to the beta 1.5 API:

(1) is it necessary to start all of the new global variables for modes with BLEND_? I do not think that there would be much damage done (and some benefit: for example, in the utility of autocomplete in the Editor) if the prefix was removed. (Especially as the values of globals ZERO and ONE would, in fact, be 0 and 1.) I do not think that there is a need to replicate the GL_ of the underlying OpenGL in order to create an effective ‘namespace’.

(2) I noticed that you omitted the four CONSTANT modes from the global variables. I was wondering whether, for a complete implementation, they could be included and, perhaps, tint() could have a role in setting/getting the glBlendColor used by the modes (behind the scenes). If not tint(), then (new) blendColor() could set/get that colour.

(3) I have not yet checked, but does pushStyle() preserve the blendMode()?

bug: cannot import images from photo library in landscape mode. the import window is only a couple of pixels high. but work fine if I turn the ipad to portrait mode.

An extended version of the ‘Blend Modes’ example project:


--
-- Blend Modes Explorer
--

supportedOrientations(LANDSCAPE_ANY)
function setup()
    print("This demonstrates blending modes. Blend modes determine "..
    "how drawing operations blend together on-screen.")
    mode = {
    BLEND_ZERO, BLEND_ONE,
    BLEND_SRC_COLOR, BLEND_ONE_MINUS_SRC_COLOR,
    BLEND_DST_COLOR, BLEND_ONE_MINUS_DST_COLOR,  
    BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA,
    BLEND_DST_ALPHA, BLEND_ONE_MINUS_DST_ALPHA,
    BLEND_SRC_ALPHA_SATURATE}    
    modeName = {
    "ZERO", "ONE",
    "SRC_COLOR", "ONE_MINUS_SRC_COLOR",
    "DST_COLOR", "ONE_MINUS_DST_COLOR",  
    "SRC_ALPHA", "ONE_MINUS_SRC_ALPHA",
    "DST_ALPHA", "ONE_MINUS_DST_ALPHA",
    "SRC_ALPHA_SATURATE"}
    local n = #mode
    parameter.integer("srcFactor", 1, n, 2)
    parameter.integer("srcAlpha", 1, n, 2)
    parameter.integer("destFactor", 1, n, 8)
    parameter.integer("destAlpha", 1, n, 8)
    for i = 1, n do
        print("BLEND_"..modeName[i].." == 0x"..
            string.format("%04x", mode[i]))
    end
    textAlign(CENTER)
    strokeWidth(5)
    stroke(206, 179, 179)
    parameter.color("col", color(207, 52, 13))
end

function drawEllipsePair(x, y, radius)
    ellipse(x - radius / 2, y, radius * 2)
    ellipse(x + radius / 2, y, radius * 2)
end

function draw()
    background(69, 80, 96)
    msg = "rgb = "..modeName[srcFactor].." * Srgb + "..
        modeName[destFactor].." * Drgb".."\
a = "..modeName[srcAlpha]..
        " * Sa + "..modeName[destAlpha].." * Da"
    fill(255)
    blendMode(NORMAL)
    local fs = math.min(28, textSize(msg) / WIDTH * fontSize())
    fontSize(fs)
    text(msg, WIDTH / 2, HEIGHT / 2 + 250)
    fontSize(32)
    text("NORMAL", WIDTH * 0.2, 40)
    text("ADDITIVE", WIDTH * 0.5, 40)
    text("MULTIPLY", WIDTH * 0.8, 40)
    blendMode(mode[srcFactor], mode[destFactor],
        mode[srcAlpha], mode[destAlpha])
    fill(col)
    drawEllipsePair(WIDTH / 2, HEIGHT / 2, 100)
    blendMode(NORMAL)
    drawEllipsePair(WIDTH * 0.2, HEIGHT / 2 - 250, 60)
    blendMode(ADDITIVE)
    drawEllipsePair(WIDTH * 0.5, HEIGHT / 2 - 250, 60)
    blendMode(MULTIPLY)
    drawEllipsePair(WIDTH * 0.8, HEIGHT / 2 - 250, 60)   
end

Hello @Simeon - some thoughts on the tween() addition to the API, noting that the in-app reference is not yet complete:

(1) the fields of tween.easing take the form (eg) inQuad but the in-app reference indicates that they take the form tween.quadIn … and omits the .easing (tween.easing.inQuad).

(2) the in-app reference indicates the existence of tween(time, subject, target, easing, callback, ...) but does not explain the significance of the final ... (update) - although, from the sample code, it can be deduced that it is to provide optional arguments to the callback function. Unlike the other arguments, callback is not elaborated on (update) (including its use of optional arguments).

(3) the table tween has the following fields referencing functions that are not documented in the in-app reference: start, path, sequence, update, hasExpired, loop.