Quick 3d and noise() example for people beginning in 3d

I tried to document this as well as I could, just a little starter 3d project. It simply provides a block class and a quick snippet of code that generates noise onto which the blocks are mapped. It creates a nice, hilly effect :smiley:


--# block

block = class()

function block:init(x,y,z,dim,tex,p)
    -- you can accept and set parameters here
    --use parameters to define all vertices needed to make a cube
    self.verts={
    vec3(x+dim/2,y-dim/2,z+dim/2),
    vec3(x-dim/2,y-dim/2,z+dim/2),
    vec3(x+dim/2,y-dim/2,z-dim/2),
    vec3(x-dim/2,y-dim/2,z-dim/2),
    vec3(x+dim/2,y+dim/2,z+dim/2),
    vec3(x-dim/2,y+dim/2,z+dim/2),
    vec3(x+dim/2,y+dim/2,z-dim/2),
    vec3(x-dim/2,y+dim/2,z-dim/2)
    }
    --use parameter p to determine how much of image to crop out
    self.tex={
    vec2(0+p/2,0+p/2),
    vec2(0+p/2,1-p/2),
    vec2(1-p/2,0+p/2),
    vec2(1-p/2,1-p/2)
    }
    --set up our actual mesh
    self.m=mesh()
    --put the verts in order into our mesh
    self.m.vertices={
    self.verts[1],self.verts[5],self.verts[3],
    self.verts[3],self.verts[7],self.verts[5],
    
    self.verts[1],self.verts[5],self.verts[2],
    self.verts[2],self.verts[6],self.verts[5],
    
    self.verts[2],self.verts[6],self.verts[4],
    self.verts[4],self.verts[8],self.verts[6],
    
    self.verts[4],self.verts[8],self.verts[3],
    self.verts[7],self.verts[8],self.verts[3],
    
    self.verts[5],self.verts[6],self.verts[8],
    self.verts[5],self.verts[7],self.verts[8],
    
    self.verts[1],self.verts[2],self.verts[4],
    self.verts[1],self.verts[3],self.verts[4]
    }
    --put the texCoords into our mesh
    self.m.texCoords={
    self.tex[3],self.tex[4],self.tex[1],
    self.tex[1],self.tex[2],self.tex[4],
    
    self.tex[3],self.tex[4],self.tex[1],
    self.tex[1],self.tex[2],self.tex[4],
    
    self.tex[3],self.tex[4],self.tex[1],
    self.tex[1],self.tex[2],self.tex[4],
    
    self.tex[1],self.tex[2],self.tex[3],
    self.tex[4],self.tex[2],self.tex[3],
    
    self.tex[4],self.tex[2],self.tex[1],
    self.tex[4],self.tex[3],self.tex[1],
    
    self.tex[4],self.tex[2],self.tex[1],
    self.tex[4],self.tex[3],self.tex[1]
    }
    --set the user-defined texture
    self.m.texture=tex
    --make sure there is no tint
    self.m:setColors(255,255,255)
end

function block:draw()
    -- Codea does not automatically call this method
    --draw the mesh
    self.m:draw()
end

function block:touched(touch)
    -- Codea does not automatically call this method
end

--# Main

function setup()
    --set up our height map
    m={}
    --set up our control variable
    control=math.random(1,100)
    --generate heightmap
    for y=1,20 do
        m[y]={}
        for x=1,20 do
            m[y][x]=block(x*20,noise(x/10,y/10,control)*100,y*20,20,"Cargo Bot:Game Area",0.1)
        end
    end
    --initialize the y position
    pos=0
    
end

function draw()
    background(40,40,40)
    --set perspective and field of view
    perspective(45,WIDTH/HEIGHT)
    --set the camera position, look values, and orientation
    camera(0,pos,0, 200,0,200, 0,1,0)
    --iterate through table and draw each element
    for i,v in pairs(m) do
        for a,b in pairs(v) do
            b:draw()
        end
    end
    --move upwards
    pos = pos + 1
end

@TheSolderKing Nice example. I changed pos=pos+1 to .1 to slow down the scrolling. Made it more interesting.

@TheSolderKing - nice

Unless you are building Minecraft, you may prefer a smoother landscape, like this, which also has fewer vertices because it doesn’t use blocks. (The shader tiles the texture nicely). It would look nice with a good texture and some lighting.

Your next challenge is to put stuff on it and walk over it!

function setup()
    displayMode(FULLSCREEN)
    --set up our height map
    m={}
    --set up our control variable
    control=math.random(1,100)
    --generate heightmap
    local n,s=100,20
    for y=1,n+1 do
        m[y]={}
        for x=1,n+1 do
            m[y][x]=noise(x/10,y/10,control)
        end
    end
    tex=readImage("SpaceCute:Background") --:copy(4,4,62,62)
    local w,h=s*3/tex.width,s*3/tex.height
    mm=mesh()
    v,t={},{}
    for x=1,n do
        for y=1,n do
            v[#v+1]=vec3(x*s,m[x][y]*100,y*s)    t[#t+1]=vec2(w*(x-1),h*(y-1))
            v[#v+1]=vec3((x+1)*s,m[x+1][y]*100,y*s)    t[#t+1]=vec2(w*(x),h*(y-1))
            v[#v+1]=vec3((x+1)*s,m[x+1][y+1]*100,(y+1)*s)  t[#t+1]=vec2(w*(x),h*(y))
            v[#v+1]=vec3((x+1)*s,m[x+1][y+1]*100,(y+1)*s)  t[#t+1]=vec2(w*(x),h*(y))
            v[#v+1]=vec3(x*s,m[x][y+1]*100,(y+1)*s)    t[#t+1]=vec2(w*(x-1),h*(y))
            v[#v+1]=vec3(x*s,m[x][y]*100,y*s)    t[#t+1]=vec2(w*(x-1),h*(y-1))
        end
    end
    mm.vertices=v
    mm.texCoords=t
    mm.texture=tex
    mm.shader=shader(TileShader.vertexShader,TileShader.fragmentShader)
    --initialize the y position
    posx,posy=0,40
end

function draw()
    background(40,40,40)
    --set perspective and field of view
    perspective(45,WIDTH/HEIGHT)
    --set the camera position, look values, and orientation
    camera(posx,posy,0, 200,posy/2,200, 0,1,0)
    mm:draw()
    --move upwards
    posx = posx + 0.1
    posy=posy+0.25
end

TileShader = {
vertexShader = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
    vColor=color;
    vTexCoord = texCoord;
    gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
    lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0)));
    gl_FragColor = col;
}
]]}

Hi @TheSolderKing and @Ignatz,

Thanks for this input - I’m working on something similar and these examples will help speed up my own development.

Couple of issues this raised - firstly @TheSolderKing - when copying and pasting your code using Aircode I obtained an error with respect to the perspective. I had pasted your code as is into one Tab. By moving the block class below the setup() and draw() functions the error was resolve - I presume you have written your code for cutting and pasting using the new app button which will generate separate tabs - think that’s the cause one to watch out for.

Secondly using Aircode - I very often edit the comment fields at the top of the Main tab. This causes me problems with the cursor - it very often won’t allow me to select or sometimes even click into the top couple of lines - I can usually get this to work but it can take some fiddling with. Whilst editing your code I found that the cursor even ended up in the active Tab title for the Main tab - what I mean is I selected the word Main above the editing block of code ???

Something wrong here with Aircode - anyone else experience this ?

Thanks for you postings.

Bri_G

:smiley:

@Bri_G - I have a lot of cool stuff on 3D in my ebook and blog, which you’re welcome to use.

Thanks @dave1707! I tried changing it to pos=pos+0.1, and it makes it a lot cooler to look at.

Thanks @Ignatz! That code is really cool! Can you give me a quick rundown of how it works? I tried to do something like that, but I never could figure out how to turn the vertices into triangles.

@Bri_G I haven’t had this problem with aircode, at least not yet because I do not use aircode very often.

@TheSolderKing

** Creating the vertices **

I used the same table of heights as you, but added an extra row and column (think of the mesh as being a wireframe. If you want 20 squares, you need 21 rows and 21 columns).

Then I just loop through from 1 to numRows-1, creating two triangles for the current tile. So the tile whose bottom left corner is at (3,4) is a square with corners (3,4), (4,4), 4,5), (3,5). I can create two triangles with the vertices (3,4), (4,4), (4,5) and (4,5), (3,5), (3,4).

** Tiling the texture **

This explains how to tile a texture repeatedly across a large surface (flat or bumpy). The trick is a shader with just one simple change to the normal code, and special texture coordinates.

https://coolcodea.wordpress.com/2013/05/25/64-3d-tiling-images-across-an-object/

Then, later when I understood what the shader was doing, I wrote this

https://coolcodea.wordpress.com/2013/06/06/78-shaders-tiling-images/

Make sure you understand this one, it is really, really valuable for tiling large or weirdly shaped surfaces.

If you want to put stuff on a bumpy surface, or walk on it, I have code for that. It’s all in my blog.