Last active
December 17, 2015 15:39
-
-
Save dermotbalson/5632997 to your computer and use it in GitHub Desktop.
terrain!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Main | |
function setup() | |
LoadImages() | |
end | |
function setup2() | |
parameter.integer("Viewpoint",-2000,2000,20) | |
speed,angle=0,0 | |
ds,da=0,0 | |
posX,posY,posZ=-20,0,10 --starting position | |
rad=math.pi/180 | |
meshTable={} | |
meshTable[1],meshTable[2]={},{} | |
AddLevel(-500,0,0,1000,1000,gravel,.1) | |
HM={} --holds height maps, one for each terrain area we build -- NEW | |
--add a terrain area --NEW | |
AddTerrain({x=-400,y=0.5,z=100,width=800,depth=800,tileSize=50,minY=10,maxY=100,random=false, | |
noiseLevel=.2,noiseStep=.5,img=grass,scaleFactor=.1,heightMaps=HM}) | |
--add 200 trees | |
for i=1,200 do | |
local x=math.random(-375,375) | |
local z=math.random(150,750) | |
local w=math.random(20,60) | |
AddItem(x,0,z,w,tree) --rotating to face us | |
end | |
count=0 | |
FPS=60 | |
end | |
function draw() | |
if imageStatus~="Ready" then return end | |
count=count+1 | |
background(220) | |
perspective(45,WIDTH/HEIGHT) | |
if Viewpoint<10 then Viewpoint=10 end | |
angle=angle+da*DeltaTime | |
posX,posZ=posX+ds*DeltaTime*math.sin(angle*rad),posZ+ds*DeltaTime*math.cos(angle*rad) | |
local h0=posY --NEW | |
posY=math.max(Viewpoint,HeightAtPos(posX,posZ)+10) --NEW | |
local h1=posY --NEW | |
--try to look up if climbing or down if descending --NEW | |
if posY==Viewpoint then lookY=20 else lookY=(h1-h0)*100/ds+10 end | |
--look in the same direction as we are facing | |
lookX,lookY,lookZ=posX+1000*math.sin(angle*rad),20,posZ+1000*math.cos(angle*rad) | |
camera(posX,posY,-posZ,lookX,lookY,-lookZ, 0,1,0) | |
if count%20==1 then | |
table.sort(meshTable[2], | |
function(a,b) return | |
vec2(posX,-posZ):dist(vec2(a.pos.x,-a.pos.z))>vec2(posX,-posZ):dist(vec2(b.pos.x,-b.pos.z)) end) | |
end | |
for k=1,2 do | |
for i,m in pairs(meshTable[k]) do | |
--rotate free standing objects if required, so they face us | |
if m.rotate==true then | |
pushMatrix() | |
translate(m.pos.x,m.pos.y,-m.pos.z) | |
local dx,dz=m.pos.x-posX,-m.pos.z+posZ | |
local ang=math.atan(dx/-dz)/rad | |
rotate(-ang,0,1,0) | |
m:draw() | |
popMatrix() | |
elseif m.ang==nil then m:draw() | |
else | |
pushMatrix() | |
translate(m.pos.x,m.pos.y,-m.pos.z) | |
rotate(-m.ang,0,1,0) | |
m:draw() | |
popMatrix() | |
end | |
end | |
end | |
ortho() --this is needed to get text written on the screen | |
viewMatrix(matrix()) --and this | |
FPS=FPS*.9+.1/DeltaTime | |
pushStyle() | |
fill(0) | |
fontSize(18) | |
strokeWidth(3) | |
textMode(CORNER) | |
text("FPS: "..string.format("%d",FPS),50,HEIGHT-50) | |
text("x="..string.format("% d",posX),50,HEIGHT-70) | |
text("y="..string.format("% d",posY),50,HEIGHT-90) | |
text("z="..string.format("% d",posZ),50,HEIGHT-110) | |
popStyle() | |
end | |
-- x,z = bottom left corner | |
-- y = height around the edges | |
-- w,d = width and depth of area to be terrained (pixels) | |
-- p = pixels per tile | |
-- f = minimum height (pixels), can be lower than y | |
-- h = max height (pixels) | |
-- r = whether to randomise terrain each run (true/false) | |
-- n = noise level (bigger numbers result in bumpy terrain, smaller gives smoother results) | |
-- tbl = table of heights, if provided | |
-- img = image or string name of image (do not include library name) | |
-- sc = image scaling factor (see above) 0-1 | |
-- hm = height map storing heights of each point in | |
-- ns = the fraction by which noise level increases for each tile as we go deeper into the terrain | |
-- eg a value of 0.3 means that the outside ring of tiles has nil noise (as always, so these tiles | |
-- exactly meet the surface around), the next ring of tiles has noise of 0.3 of the full level, | |
-- the third ring of tiles has noise of 0.6, and so on, up to a maximum of 1.0. The bigger ns, the | |
-- the more you will get cliffs at the edges of the terrain | |
function AddTerrain(...) | |
local x,y,z,w,d,p,f,h,r,n,tbl,img,sc,hm,ns=Params( | |
{"x","y","z","width","depth","tileSize","minY","maxY","random","noiseLevel", | |
"heightTable","img","scaleFactor","heightMaps","noiseStep"},arg) | |
local nw,nd=math.floor(w/p),math.floor(d/p) --number of tiles in each direction | |
--if no table of heights provided, generate one with noise | |
if tbl==nil then | |
n=n or 0.2 --default noise level | |
tbl1,tbl2={},{} | |
--noise starts at 0 on the outside, increases by the step as we move inward, max of 1 | |
--noise is nil at edge, increases by 30% per tile inwards, to 100% | |
local min,max=9999,0 | |
--if user wants a random result each time. add a random number to the noise results | |
if r==true then rnd=math.random(1,10000) else rnd=0 end | |
for i=1,nw+1 do | |
tbl1[i],tbl2[i]={},{} | |
for j=1,nd+1 do | |
--noise fraction for this tile, based on how close to the edge it is | |
--this formula counts how many rows in from the edge this tile is | |
tbl1[i][j]=math.min(1,math.min(i-1,j-1,nw+1-i,nd+1-j)*ns) | |
--the noise itself | |
tbl2[i][j]=noise(rnd+i*n, rnd+j*n) | |
--keep track of min and max values of noise | |
if tbl2[i][j]<min then min=tbl2[i][j] end | |
if tbl2[i][j]>max then max=tbl2[i][j] end | |
end | |
end | |
--now go back through the whole array and scale it | |
--we know the user wants values between f and h | |
--we know our noise varies between min and max | |
--so now we pro rate all our values so they vary between f and h, based on the noise values | |
for i=1,nw+1 do | |
for j=1,nd+1 do | |
local f1=y | |
--pro rate | |
local f2=f+(h-f)*(tbl2[i][j]-min)/(max-min) | |
--now apply noise fraction, to reduce noise around the edges | |
tbl2[i][j]=f1*(1-tbl1[i][j])+f2*tbl1[i][j] | |
end | |
end | |
end | |
--store details for later use in determining height | |
table.insert(hm,{x1=x,z1=z,x2=x+w,z2=z+d,p=p,t=tbl2}) | |
--create the vectors and mesh | |
local wx,wz=img.width*sc,img.height*sc | |
v,t={},{} | |
for i=1,nw do | |
for j=1,nd do | |
local x1,x2=x+(i-1)*p,x+i*p local x3,x4=x2,x1 | |
local y1,y2,y3,y4=tbl2[i][j],tbl2[i+1][j],tbl2[i+1][j+1],tbl2[i][j+1] | |
local z1,z3=z+(j-1)*p,z+j*p local z2,z4=z1,z3 | |
v[#v+1]=vec3(x1,y1,-z1) t[#t+1]=vec2(x1/wx,z1/wz) --bottom left | |
v[#v+1]=vec3(x2,y2,-z2) t[#t+1]=vec2(x2/wx,z2/wz) --bottom right | |
v[#v+1]=vec3(x3,y3,-z3) t[#t+1]=vec2(x3/wx,z3/wz) --top right | |
v[#v+1]=vec3(x3,y3,-z3) t[#t+1]=vec2(x3/wx,z3/wz) --top right | |
v[#v+1]=vec3(x4,y4,-z4) t[#t+1]=vec2(x4/wx,z4/wz) --top left | |
v[#v+1]=vec3(x1,y1,-z1) t[#t+1]=vec2(x1/wx,z1/wz) --bottom left | |
end | |
end | |
local m=mesh() | |
m.texture=img | |
m.vertices=v | |
m:setColors(color(255)) | |
m.texCoords=t | |
m.pos=vec3(x,y,z) | |
m.shader = shader(autoTilerShader.vertexShader, autoTilerShader.fragmentShader) | |
table.insert(meshTable[1],m) | |
end | |
--calculates height at x,z | |
function HeightAtPos(x,z) | |
--identify the location and calculate height | |
local h=0 --set default as 0 | |
for i,v in pairs(HM) do --look in each terrain area | |
if x>=v.x1 and x<=v.x2 and z>=v.z1 and z<=v.z2 then --if in this area... | |
--calc which square we are in | |
local mx,mz=1+math.floor((x-v.x1)/v.p),1+math.floor((z-v.z1)/v.p) | |
--use bilinear interpolation (most common method) for interpolating 4 corner values | |
local px,pz=1+(x-v.x1)/v.p-mx,1+(z-v.z1)/v.p-mz | |
h=v.t[mx][mz]*(1-px)*(1-pz)+ | |
v.t[mx+1][mz]*px*(1-pz)+ | |
v.t[mx+1][mz+1]*px*pz+ | |
v.t[mx][mz+1]*(1-px)*pz | |
break | |
end | |
end | |
return h | |
end | |
function AddItem(x,y,z,w,i,r) --centred on x | |
y=HeightAtPos(x,z) | |
local h=i.height*w/i.width | |
local m=mesh() | |
m.texture=i | |
local v,t={},{} | |
v,t=AddImage(-w/2,0,0,w,h,0,v,t) | |
m.pos=vec3(x,y,z) | |
if r~=nil then m.ang=r else m.rotate=true end | |
m.vertices=v | |
m:setColors(color(255)) | |
m.texCoords=t | |
table.insert(meshTable[2],m) | |
end | |
function AddImage(x,y,z,w,h,d,v,t) | |
v[#v+1]=vec3(x,y,z) t[#t+1]=vec2(0,0) | |
v[#v+1]=vec3(x+w,y,z-d) t[#t+1]=vec2(1,0) | |
v[#v+1]=vec3(x+w,y+h,z-d) t[#t+1]=vec2(1,1) | |
v[#v+1]=vec3(x+w,y+h,z-d) t[#t+1]=vec2(1,1) | |
v[#v+1]=vec3(x,y+h,z) t[#t+1]=vec2(0,1) | |
v[#v+1]=vec3(x,y,z) t[#t+1]=vec2(0,0) | |
return v,t | |
end | |
function AddLevel(x,y,z,w,d,i,s) | |
local m=mesh() | |
m.texture=i | |
local v,t={},{} | |
local nx,nz=w/i.width/s,d/i.height/s | |
v[#v+1]=vec3(x,y,-z) t[#t+1]=vec2(0,0) | |
v[#v+1]=vec3(x+w,y,-z) t[#t+1]=vec2(nx,0) | |
v[#v+1]=vec3(x+w,y,-z-d) t[#t+1]=vec2(nx,nz) | |
v[#v+1]=vec3(x+w,y,-z-d) t[#t+1]=vec2(nx,nz) | |
v[#v+1]=vec3(x,y,-z-d) t[#t+1]=vec2(0,nz) | |
v[#v+1]=vec3(x,y,-z) t[#t+1]=vec2(0,0) | |
m.vertices=v | |
m:setColors(color(255)) | |
m.texCoords=t | |
m.pos=vec3(x,y,z) | |
m.shader = shader(autoTilerShader.vertexShader, autoTilerShader.fragmentShader) | |
table.insert(meshTable[1],m) | |
end | |
function touched(touch) | |
local center=true | |
if touch.x<WIDTH/4 then | |
da=da-2 | |
center=false | |
elseif touch.x>WIDTH*3/4 then | |
da=da+2 | |
center=false | |
end | |
if touch.y<HEIGHT/4 then | |
ds=ds-3 | |
center=false | |
elseif touch.y>HEIGHT*3/4 then | |
ds=ds+3 | |
center=false | |
end | |
if center then ds=0 da=0 end | |
end | |
function Params(list,tbl) --tbl is params | |
if type(tbl[1])=="table" then --was named table | |
local t={} --match the named params with the list provided | |
for n,v in pairs(tbl[1]) do | |
for i=1,#list do | |
if n==list[i] then t[i]=v break end | |
end | |
end | |
return unpack(t) | |
else --was an unnamed list, return it as is | |
return unpack(tbl) | |
end | |
end | |
function LoadImages() | |
imageStatus="Ready" --tells draw it's ok to draw the scene (will be turned off if we have to download images) | |
output.clear() | |
--pass through Codea name of image and internet url | |
--not in Codea, will be downloaded and saved | |
grass=LoadImage("Dropbox:3D-grass2", | |
"http://i1303.photobucket.com/albums/ag142/ignatz_mouse/map-grass10_zps7573c68c.png") | |
gravel=LoadImage("Dropbox:3D-gravel", | |
"http://i1303.photobucket.com/albums/ag142/ignatz_mouse/map-gravel11_zpsc3a6ac7d.png") | |
tree=LoadImage("Dropbox:3D-tree2", | |
"http://i1303.photobucket.com/albums/ag142/ignatz_mouse/map-tree21c_zps655e421c.png") | |
if imageStatus=="Ready" then setup2() end | |
end | |
--downloads images one by one | |
function LoadImage(fileName,url) | |
local i=readImage(fileName) | |
if i~=nil then return i end | |
--not found, we need to download, add to queue (ie table) | |
if imageTable==nil then imageTable={} end | |
imageTable[#imageTable+1]={name=fileName,url=url} | |
print('Queueing',fileName) | |
imageStatus='Loading' | |
--if the first one, go ahead and download | |
if #imageTable==1 then | |
http.request(imageTable[1].url,ImageDownloaded) | |
print('loading',imageTable[1].name) | |
end | |
end | |
--saves downloaded images | |
function ImageDownloaded(img) | |
print(imageTable[1].name,'loaded') | |
saveImage(imageTable[1].name,img) --save | |
table.remove(imageTable,1) | |
--load next one if we have any more to do | |
if #imageTable>0 then | |
http.request(imageTable[1].url,ImageDownloaded) | |
print('loading',imageTable[1].name) | |
else | |
LoadImages() | |
end | |
end | |
--# Tiler | |
autoTilerShader = { | |
vertexShader = [[ | |
// | |
// A basic vertex shader | |
// | |
//This is the current model * view * projection matrix | |
// Codea sets it automatically | |
uniform mat4 modelViewProjection; | |
//This is the current mesh vertex position, color and tex coord | |
// Set automatically | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec2 texCoord; | |
//This is an output variable that will be passed to the fragment shader | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
//Pass the mesh color to the fragment shader | |
vColor = color; | |
vTexCoord = texCoord; | |
//Multiply the vertex position by our combined transform | |
gl_Position = modelViewProjection * position; | |
} | |
]], | |
fragmentShader = [[ | |
// | |
// A basic fragment shader | |
// | |
//Default precision qualifier | |
precision highp float; | |
//This represents the current texture on the mesh | |
uniform lowp sampler2D texture; | |
//The interpolated vertex color for this fragment | |
varying lowp vec4 vColor; | |
//The interpolated texture coordinate for this fragment | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
//Sample the texture at the interpolated coordinate | |
lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0))) * vColor; | |
//Set the output color to the texture color | |
gl_FragColor = col; | |
} | |
]]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment