Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Animated isometric SIN function

edited July 2012 in Code Sharing Posts: 455

This is my first attempt at some Codea goodness (so be gentle), based on some old basic stuff I once wrote.

It displays an animated SIN function in an isometric grid, with the centre of the function being touch. It supports multi touch so you can do multiple waves to look at interference patterns.

-- 3d sin

-- Use this function to perform your initial setup
function setup()
    parameter("speed", 0, 0.5,0.1)
    parameter("wavelength", -1.5,-0.25,-.7)
    gridSize = 30
    isoW = 20*math.cos(math.rad(30))
    isoH = 20*math.sin(math.rad(30))
    heights = {}
    coords = {}
    touches = {}
    cens = {}
    for x = 1,gridSize do
        heights[x] = {}
        coords[x] = {}
        for y = 1,gridSize do
            heights[x][y] = 0
            coords[x][y] = {x = WIDTH/2+(x-y)*isoW, y = 50+(x+y)*isoH}
        end
    end
    offset = 0
end

function touched(touch)
    if touch.state == ENDED then
    else
        cens[touch.id] = { x = ((touch.x - WIDTH/2)/isoW + (touch.y-50)/isoH)/2, y = ((touch.y-50)/isoH - (touch.x - WIDTH/2)/isoW)/2, t = os.clock() }
    end
    for k, touch in pairs(cens) do
        if os.clock() - touch.t > 1 then
            cens[k] = nil
        end    
    end
end    

-- This function gets called once every frame
function draw()
    for x = 1,gridSize do
        for y = 1,gridSize do
            heights[x][y] = 0
            for k, touch in pairs(cens) do
                heights[x][y] = heights[x][y] + math.sin((math.sqrt((touch.x-x)^2+(touch.y-y)^2)*wavelength)+offset)*20
            end
        end
    end
    offset = offset + speed
    -- This sets a dark background color 
    background(40, 40, 50)

    -- This sets the line thickness
    noSmooth()
    noStroke()

    -- Do your drawing here
    for x = 1,(gridSize-1) do
        for y = 1,(gridSize-1) do
            line(coords[x][y].x, coords[x][y].y+heights[x][y], coords[x+1][y].x, coords[x+1][y].y+heights[x+1][y])
            line(coords[x][y].x, coords[x][y].y+heights[x][y], coords[x][y+1].x, coords[x][y+1].y+heights[x][y+1])
        end
    end
end

Comments

  • Posts: 371

    Even though I have no clue how that works, it is by far the coolest thing I have ever seen in Codea. You are a god!

  • Posts: 384

    It is hypnotic, @spacemonkey! I put this up the top to see it better:

    supportedOrientations(ANY)
    

    Would it run faster in mesh? I note if you give it more cells it slows down quickly.

  • edited July 2012 Posts: 455

    I don't know anything about mesh... I'll look it up and try it ;-)

    So, presumably I'd want a mesh of vec3s and modify the wave form on the z axis... I can kinda follow that, but how do I project the mesh into isometric or some other 3d layout for rendering?

    I think the 3d Lab example is the one to follow... better do some work, this is gonna take more thought than lunch time has to offer.

  • SimeonSimeon Admin Mod
    Posts: 5,417

    That's hypnotic, @spacemonkey.

  • Posts: 176

    Wow! Way cool!
    It didn't look quite right to me, though, so I changed the wavelength to be

    parameter("wavelength", -1,-.25,-.5)
    
  • Posts: 176

    Also tried

    parameter("wavelength"' -10,10,0)
    

    and got some very interesting effects at the extremes.

    (Previous should have been -1,-.5,-.25)

  • edited July 2012 Posts: 455

    Quite right on the wavelength. I'd been bashing my head against remembering maths from 20 years ago to do the reverse calculations for touch and ended up reversing the iso projection which meant the waves go the wrong way ;-)

  • edited July 2012 Posts: 455

    So I did a mesh conversion. In a nutshell the drawing performance is AMAZING, however, on the flip side, I'm controlling the height field by modifying the y element of the vertices, and the performance of modifying the vertices in the mesh is awful. So the original at the top is better performing (about 25fps vs 15fps for the mesh version.

    Note, the rest of this is pretty scrappy now as I have played with other optimisations like precalculating distances, but because the performance is poor I didn't actually clean these up.

    Mesh below


    -- 3d sin -- Use this function to perform your initial setup function setup()     --myFPSReporter = FPSReporter(4)     parameter("speed", 0, 0.5,0.1)     parameter("wavelength", 0.25,1.5,.7)     gridSize = 30     cens = {}     vertcount=1     surfaceMesh = mesh()     verts = {}     texverts = {}     for x = 1,gridSize do         for z = 1,gridSize do             table.insert(verts, vertcount, vec3(x,0,z))             table.insert(verts, vertcount+1, vec3(x+1,0,z))             table.insert(verts, vertcount+2, vec3(x,0,z+1))             table.insert(verts, vertcount+3, vec3(x+1,0,z))             table.insert(verts, vertcount+4, vec3(x+1,0,z+1))             table.insert(verts, vertcount+5, vec3(x,0,z+1))             table.insert(texverts, vertcount, vec2(0,0))             table.insert(texverts, vertcount+1, vec2(1,0))             table.insert(texverts, vertcount+2, vec2(0,1))             table.insert(texverts, vertcount+3, vec2(1,0))             table.insert(texverts, vertcount+4, vec2(1,1))             table.insert(texverts, vertcount+5, vec2(0,1))             vertcount = vertcount + 6         end     end     t = genWireframe(30)     surfaceMesh.texture = t     surfaceMesh.vertices = verts     surfaceMesh.texCoords = texverts     vertcount = vertcount -1     --verts = nil     texverts = nil     offset = 0     isoW = 20*math.cos(math.rad(30))     isoH = 20*math.sin(math.rad(30)) end function touched(touch)     if touch.state == ENDED then     else         --cens[touch.id] = { x = touch.x, y = touch.y, t = os.clock() }                            x = ((touch.x - WIDTH/2)/isoW + (touch.y-50)/isoH)/2         y = ((touch.y-50)/isoH - (touch.x - WIDTH/2)/isoW)/2         dists = {}         for j = 1, vertcount do             vert = surfaceMesh:vertex(j)             dists[j] = (math.sqrt((x-vert.x)^2+(y-vert.z)^2)*wavelength)         end         cens[touch.id] = {d = dists, t = os.clock()}         dists = nil     end     for k, touch in pairs(cens) do         if os.clock() - touch.t > 1 then             cens[k] = nil         end         end end     -- This function gets called once every frame function draw()     --verts = surfaceMesh.vertices     for j = 1, vertcount do         newy = 0         for k, touch in pairs(cens) do             newy = newy + math.sin(cens[k].d[j]+offset)         end         surfaceMesh:vertex(j, vec3(verts[j].x, newy, verts[j].z))     end                  offset = offset + speed     -- This sets a dark background color      background(40, 40, 50)     --myFPSReporter:draw(3)          -- First arg is FOV, second is aspect     perspective(45, WIDTH/HEIGHT)       -- Position the camera up and back, look at origin     camera(-20, 20,-20, gridSize/2,0,gridSize/2, 0,1,0)          -- Do your drawing here     surfaceMesh:draw() end -- Wireframe texture function genWireframe(w)     local t = image(w, w)         for y=1, w do         for x=1, 3 do             t:set(x, y, 255, 255, 255, 255)         end     end             for x=1, w do         for y=1, 3 do                 t:set(x, y, 255, 255, 255, 255)         end     end             for x=2, w-1 do         t:set(x+1, x, 255, 255, 255, 255)         t:set(x, x, 255, 255, 255, 255)         t:set(x-1, x, 255, 255, 255, 255)     end         t:set(w, w, 255, 255, 255, 255)     t:set(w-1, w, 255, 255, 255, 255)         return t end
  • Posts: 384

    Hi @spacemonkey, based on your code I tried keeping all the vertex information in a table and recreate the mesh from scratch rather than adjusting vertices individually, but that didn't help much...

  • edited August 2012 Posts: 455

    Fred, I tried a number of similar things myself, but everything seemed to be similarly poor. I guess at draw time it reships the table (or in some other way processes changes) to the graphics chip or something and this is where the performance drop off comes from.

  • Posts: 791

    @spacemonkey Love it!

    Here's my take on using meshes - sticking an image on each grid intersection.

    -- 3d sin
    
    -- Use this function to perform your initial setup
    function setup()
        parameter("speed", 0, 1,0.4)
        parameter("wavelength", -1,-.1,-.3)
        gridSize = 80
        
        g=mesh()
        g.texture="Tyrian Remastered:Energy Orb 2"
        isoW = 8*math.cos(math.rad(30))
        isoH = 8*math.sin(math.rad(30))
        heights = {}
        coords = {}
        touches = {}
        cens = {}
        for x = 1,gridSize do
            heights[x] = {}
            coords[x] = {}
            for y = 1,gridSize do
                heights[x][y] = 0
                coords[x][y] = {x = WIDTH/2+(x-y)*isoW, y = 50+(x+y)*isoH}
            end
        end
        offset = 0
    end
    
    function touched(touch)
        if touch.state == ENDED then
        else
            cens[touch.id] = { x = ((touch.x - WIDTH/2)/isoW + (touch.y-50)/isoH)/2, y = ((touch.y-50)/isoH - (touch.x - WIDTH/2)/isoW)/2, t = os.clock() }
        end
        for k, touch in pairs(cens) do
            if os.clock() - touch.t > 1 then
                cens[k] = nil
            end    
        end
    end    
    
    -- This function gets called once every frame
    function draw()
        for x = 1,gridSize do
            for y = 1,gridSize do
                heights[x][y] = 0
                for k, touch in pairs(cens) do
                    heights[x][y] = heights[x][y] + math.sin((math.sqrt((touch.x-x)^2+(touch.y-y)^2)*wavelength)+offset)*20
                end
            end
        end
        offset = offset + speed
        -- This sets a dark background color 
        background(40, 40, 50)
    
        -- This sets the line thickness
        noSmooth()
        noStroke()
    g:clear()
        -- Do your drawing here
        for x = 1,(gridSize-1) do
            for y = 1,(gridSize-1) do
           --     line(coords[x][y].x, coords[x][y].y+heights[x][y], coords[x+1][y].x, coords[x+1][y].y+heights[x+1][y])
        --        line(coords[x][y].x, coords[x][y].y+heights[x][y], coords[x][y+1].x, coords[x][y+1].y+heights[x][y+1])
                
                  local idx = g:addRect(coords[x][y].x, coords[x][y].y+heights[x][y],12,12) 
                    g:setRectTex(idx, 0, 0, 1, 1)
            end
        end
        g:draw()
    end
    
Sign In or Register to comment.