# Animated isometric SIN function

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
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
``````

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!

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.

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.

That’s hypnotic, @spacemonkey.

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

``````parameter("wavelength", -1,-.25,-.5)
``````

Also tried

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

and got some very interesting effects at the extremes.

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

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

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
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
``````

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…

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.

@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"
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
``````