#### 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 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
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()

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

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

• 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
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)

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"
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()