Skip to content

Instantly share code, notes, and snippets.

@dermotbalson
Last active December 29, 2015 01:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dermotbalson/7590746 to your computer and use it in GitHub Desktop.
Save dermotbalson/7590746 to your computer and use it in GitHub Desktop.
Codea users world map
--# Main
--The name of the project must match your Codea project name if dependencies are used.
--Project: Place Project Name Here
--Version: Alpha 1.0
--Comments:
--by ignatz
--November 2013
--This map shows light/dark in real time
--It knows your time, but it needs your longitude, so BEFORE RUNNING THE CODE
--look up your longitude and put it in below
MyLongitude=116 --NOTE - make it NEGATIVE if it is WEST
--You can also add yourself or others in the Users tab. See instructions at the top.
--The first time you run, there will be a little delay while the images download
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
function setup()
LoadImages() --download image maps if you don't have them
end
function setup2()
url1=readImage("Dropbox:EarthDay")
url2=readImage("Dropbox:EarthNight")
AddUsers() --set up user list
CreateEarth()
end
function CreateEarth()
local color1 = color(255, 255, 255, 255)
--this sphere code comes from Jmv38, see bottom
planet1 = Sphere({
nx = 40, ny = 20 , -- mesh definition
meshOptimize = true, -- optimize mesh for sphere
c1 = color1 , c2 = color1 , -- mesh colors
cx=50, cy=0, cz=0 , -- sphere center
r = 100 , -- radius of the sphere
rotTime1 = 140 , -- rotation time in s
hflip = true, -- to flip image horozontally
})
cam = vec3(0, 50, 300)
--set up shader with the two images
planet1.ms.shader=shader(specularShader2.vertexShader,specularShader2.fragmentShader)
planet1.ms.texture=url1
planet1.ms.shader.texture2=url2
planet1.ax,planet1.ay,planet1.az=0,-MyLongitude,0 --set rotation to current longitude
local t=os.date("*t") --get time of day
--send through sun position by calculating where it is currently midday
planet1.ms.shader.sunPos=math.fmod(0.5+MyLongitude/360+(12-t.hour-t.min/60)/24,1)
end
--this function just calculates some extra info we need for users
--and draws locations on the maps
function AddUsers()
table.sort(Users,function(a,b) return (a[4]>b[4]) end)
UserShow={} --list of users being shown on screen
for i=1,#Users do
Users[i][5]=0.5+Users[i][3]/180 --turn Latitude into a texture mapping
Users[i][6]=0.5+Users[i][4]/360 --ditto for Longitude
end
--now draw a dot on both maps for each user
pushStyle()
fill(255,0,0)
strokeWidth(0)
local s=2
setContext(url1)
for i=1,#Users do
ellipse(Users[i][6]*url1.width,(1-Users[i][5])*url1.height,s)
end
setContext()
setContext(url2)
for i=1,#Users do
ellipse(Users[i][6]*url2.width,(1-Users[i][5])*url2.height,s)
end
setContext()
popStyle()
--find starting point for user tracking
--find first person who is to the left of the initial viewing position
--they will be the first one shown, when the sun reaches them
tracker=1
for i=1,#Users do
if Users[i][4]<MyLongitude then tracker=i break end
end
end
--run every draw, checks if we have passed any more users
function UpdateTracker()
local a=math.fmod(-planet1.ay,360) --calc longitude of current view
--restart just deals with the international dateline
if restart and a<0 then return else restart=nil end
--add users to the list being shown if they are now to the right of the viewing position
while a<Users[tracker][4] do
ShowUser(tracker)
tracker=tracker+1
if tracker==#Users then tracker=1 restart=true end
break
end
end
--add user #u to the list being shown on screen
function ShowUser(u)
local pos={t=0}
--store time because we fade them over time
table.insert(UserShow,{id=u,t=ElapsedTime})
print(Users[u][1],Users[u][2])
end
function draw()
background(14, 14, 14, 255)
--don't start drawing until we have the images
if imageStatus~="Ready" then return end
perspective(50, WIDTH/HEIGHT)
camera(cam.x,cam.y,cam.z,0,0,0)
planet1.ms.shader.mModel=modelMatrix()
--send through rotation position so we can draw a line to show current position
planet1.ms.shader.rotation=(-0.5-planet1.ay/360)
planet1:draw()
UpdateTracker() --check if more users have been reached
--now draw the current list of users being shown
pushMatrix()
ortho()
viewMatrix(matrix())
pushStyle()
local c=color(255,255,0,255)
font("HelveticaNeue-Light")
fontSize(24)
textMode(CORNER)
for i=#UserShow,1,-1 do
local f=(ElapsedTime-UserShow[i].t)/10 --fade them out over 10 secs
if f>1 then --delete when faded completely
table.remove(UserShow,i)
else
c.a=255*(1-f) --fade colour too
fill(c)
local x,y=50,HEIGHT-90-(#UserShow-i)*30 --show list
text(Users[UserShow[i].id][1].." ("..Users[UserShow[i].id][2]..")",x,y)
end
end
popStyle()
popMatrix()
end
--the code below comes from Jmv38, who explains it here
--http://jmv38.comze.com/CODEAbis/server.php (look for 3D tutorial)
Sphere = class()
function Sphere:init(input)
-- spatial position of sphere
self.cx = input.cx or 0
self.cy = input.cy or 0
self.cz = input.cz or 0
-- angular position of sphere, defined by angles around x,y,z axis
self.ax = 0
self.ay = 0
self.az = 0
-- sphere radius and rotation
self.radius = input.r
self.tRot = input.rotTime1
-- sphere rotation 2
self.tRot2 = input.rotTime2
self.cx2 = input.cx2 or 0 -- center of rotation 2
self.cy2 = input.cy2 or 0
self.cz2 = input.cz2 or 0
self.ax2 = input.ax2 or 0 -- axis of rotation 2
self.ay2 = input.ay2 or 1
self.az2 = input.az2 or 0
-- mesh definition
self.nx = input.nx -- number of triangles in x
self.ny = input.ny -- and in y
self.c1 = input.c1 -- 2 color() objects, to see the triangles
self.c2 = input.c2
self.optimized = input.meshOptimize -- boolean
-- sphere decoration
self.url = input.url -- texture as a url (text)
self.hflip = input.hflip -- to flip image horizontally
if input.lightDir then
self.lightDir = input.lightDir:normalize() -- a vec3 pointing to the sun
end
self.shadowRatio = input.shadowRatio or 1.05 -- close to 1.05
-- create mesh and colors
local vertices,colors,tc = {},{},{}
if self.optimized then
vertices,colors,tc = self:optimMesh({ nx=self.nx, ny=self.ny, c1=self.c1, c2=self.c2 })
else
vertices,colors,tc = self:simpleMesh({ nx=self.nx, ny=self.ny, c1=self.c1, c2=self.c2 })
end
-- if a radius is given, warp to a sphere
if self.radius then
vertices = self:warpVertices({
verts=vertices,
xangle=180,
yangle=180
}) end
-- create the mesh itself
self.ms = mesh()
self.ms.vertices = vertices
self.ms.colors = colors
-- add the texture from internet
--if self.url then
-- self:load( self.url ) -- this will not be instantaneous!
--end
self.ms.texture = self.url
self.ms.texCoords = tc
-- add some shadows
if self.lightDir then self:shadows() end
end
function Sphere:shadows()
self.ms2 = mesh()
local dir = self.lightDir
local vertices2,colors2 = {},{}
local d = 0
for i,v in ipairs(self.ms.vertices) do
vertices2[i] = v
d = v:dot(dir)
d = 128 - 4*(d-0.1)*128
if d<0 then d=0 end
if d>255 then d=255 end
colors2[i] = color(0,0,0,d)
end
self.ms2.vertices = vertices2
self.ms2.colors = colors2
end
function Sphere:simpleMesh(input)
-- create the mesh tables
local vertices = {}
local colors = {}
local texCoords = {}
--local w,h = img.width/10, img.height/10
local k = 0
local s = 1
-- create a rectangular set of triangles
local x,y
local nx,ny = input.nx,input.ny
local opt = input.opt
local sx, sy = 1/ny, 1/ny
local color1 = input.c1
local color2 = input.c2
local center = vec3(1,0.5,0)
for y=0,ny-1 do
for x=0,nx-1 do
vertices[k+1] = vec3( sx*x , sy*y , 1) - center
vertices[k+2] = vec3( sx*(x+1), sy*y , 1) - center
vertices[k+3] = vec3( sx*(x+1), sy*(y+1), 1) - center
vertices[k+4] = vec3( sx*x , sy*y , 1) - center
vertices[k+5] = vec3( sx*x , sy*(y+1), 1) - center
vertices[k+6] = vec3( sx*(x+1), sy*(y+1), 1) - center
colors[k+1] = color1
colors[k+2] = color1
colors[k+3] = color1
colors[k+4] = color2
colors[k+5] = color2
colors[k+6] = color2
k = k + 6
end
end
return vertices,colors
end
function Sphere:optimMesh(input)
-- create the mesh tables
local vertices = {}
local colors = {}
local texCoords = {}
--local w,h = img.width/10, img.height/10
local k = 0
local s = 1
-- create a set of triangles with approx constant surface on a sphere
local x,y
local x1,x2 = {},{}
local i1,i2 = 0,0
local nx,ny = input.nx,input.ny
local sx, sy = nx/ny, 1/ny
local color1 = input.c1
local color2 = input.c2
local center = vec3(1,0.5,0)
local m1,m2,c
local flip = 1
if self.hflip then flip=-1 end
for y=0,ny-1 do -- for each horizontal band
-- number of points on each side of the band
local nx1 = math.floor( nx * math.abs(math.cos( ( y*sy-0.5)*2 * math.pi/2)) )
if nx1<6 then nx1=6 end
local nx2 = math.floor( nx * math.abs(math.cos( ((y+1)*sy-0.5)*2 * math.pi/2)) )
if nx2<6 then nx2=6 end
-- points on each side of the band
x1,x2 = {},{}
for i1 = 1,nx1 do x1[i1] = (i1-1)/(nx1-1)*sx end
for i2 = 1,nx2 do x2[i2] = (i2-1)/(nx2-1)*sx end
x1[nx1+1] = x1[nx1] -- just a trick to manage last triangle without thinking
x2[nx2+1] = x2[nx2]
-- start on the left
local i1,i2 = 1,1
c = 1 -- starting color
local continue = true
local n,nMax = 0,0
nMax = nx*2+1
while continue do
-- center of the 2 current segments
m1 = (x1[i1]+x1[i1+1])/2
m2 = (x2[i2]+x2[i2+1])/2
if m1<=m2 then -- the less advanced base makes the triangle
vertices[k+1] = vec3( x1[i1], sy*y , 1) - center
vertices[k+2] = vec3( x1[i1+1], sy*y , 1) - center
vertices[k+3] = vec3( x2[i2], sy*(y+1), 1) - center
texCoords[k+1] = vec2( x1[i1]/2*flip, sy*y )
texCoords[k+2] = vec2( x1[i1+1]/2*flip, sy*y )
texCoords[k+3] = vec2( x2[i2]/2*flip, sy*(y+1))
if i1<nx1 then i1 = i1 +1 end
else
vertices[k+1] = vec3( x1[i1], sy*y , 1) - center
vertices[k+2] = vec3( x2[i2], sy*(y+1), 1) - center
vertices[k+3] = vec3( x2[i2+1], sy*(y+1), 1) - center
texCoords[k+1] = vec2( x1[i1]/2*flip, sy*y )
texCoords[k+2] = vec2( x2[i2]/2*flip, sy*(y+1))
texCoords[k+3] = vec2( x2[i2+1]/2*flip, sy*(y+1))
if i2<nx2 then i2 = i2 +1 end
end
-- set the triangle color
if c==1 then col=color1 else col=color2 end
colors[k+1] = col
colors[k+2] = col
colors[k+3] = col
if c==1 then c=2 else c=1 end
if i1==nx1 and i2==nx2 then continue=false end
-- increment index for next triangle
k = k + 3
n = n + 1
if n>nMax then continue=false end -- just in case of infinite loop
end
end
return vertices,colors,texCoords
end
function Sphere:warpVertices(input)
-- move each vector to its position on sphere
local verts = input.verts
local xangle = input.xangle
local yangle = input.yangle
local s = self.radius
local m = matrix(0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0) -- empty matrix
local vx,vy,vz,vm
for i,v in ipairs(verts) do
vx,vy = v[1], v[2]
vm = m:rotate(xangle*vy,1,0,0):rotate(yangle*vx,0,1,0)
vx,vy,vz = vm[1],vm[5],vm[9]
verts[i] = vec3(vx,vy,vz)
end
return verts
end
function Sphere:move()
local ay
ay = self.ay + 360 * DeltaTime / self.tRot
if ay > 180 then ay = ay - 360 end
if ay < -180 then ay = ay + 360 end
self.ay = ay
end
function Sphere:draw()
local s
pushMatrix()
pushStyle()
self:move()
translate(self.cx,self.cy,self.cz)
rotate(self.az,0,0,1)
rotate(self.ax,1,0,0)
rotate(self.ay,0,1,0)
if self.radius then s = self.radius else s = 100 end
scale(s,s,s)
self.ms:draw()
popStyle()
popMatrix()
end
--# Shader
specularShader2 = {
vertexShader = [[
uniform mat4 modelViewProjection;
uniform mat4 mModel;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying lowp vec4 vPosition;
void main() //--vertex shader is standard
{
vColor = color;
vTexCoord = texCoord;
vPosition = mModel * position;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
uniform lowp sampler2D texture; //--day texture
uniform lowp sampler2D texture2; //--night texture
uniform float sunPos; //--sun position as texture fraction 0-1
uniform float rotation; //--current rotation position
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying lowp vec4 vPosition;
void main()
{
lowp vec4 pixel1 = texture2D( texture, vTexCoord ); //--read in the two pixels
lowp vec4 pixel2 = texture2D( texture2, vTexCoord );
lowp float d=abs(sunPos-fract(vTexCoord.x)); //--calc distance to sun from current position
if (d>0.5) d = 1.0 - d; //--if > 0.5, measure it the other wya round the earth
lowp float intensity=1.0; //--intensity=1 if day, 0 if night
if (d>0.30) intensity=0.0;
else if (d>0.20) intensity=(0.30-d)/0.10; //--fade into night between 0.2 and 0.3
else //--intensity=1, daylight
{
intensity=1.0;
//--if very close to sun'e position, draw reflection on earth
lowp float h = 0.5-vTexCoord.y;
lowp float a = d*d*4.0 + h*h;
if (a<0.001) pixel1+=(1.0-a/0.001)*.5;
}
vec4 totalColor = mix( pixel2, pixel1, intensity); //--mix the images
//--draw a vertical line round earth for current position
if (abs(rotation-vTexCoord.x) < 0.0003) totalColor=vec4(0.3,0.3,0.3,0.3);
gl_FragColor=totalColor;
}
]]
}
--********************
--# Users
--To add more names
--Add a line for each, as shown below
--Fields are: name, location, latitude, longitude
--Latitude is negative if in Northern hemisphere
--Longitude is negative if West
--Users are sorted in Longitude order (the last number on each line)
--So insert any extra lines in the right place!
Users={
{'Dalorbi','Sydney',33.9,151.2},
{'Reefwing','Sydney',33.9,151.2},
{'Simeon','Adelaide',35,138.6},
{'John','Adelaide',35,138.6},
{'colincapurso','Adelaide',35,138.6},
{'silvesun','Shanghai',-31.2,121.5},
{'jasperlamis','Pangasinan',-16,120.3},
{'Ignatz','Perth',32,115.9},
{'luger','Bangkok',-13.8,100.5},
{'sanit','Bangkok',-13.8,100.5},
{'Saurabh','Mumbai',-19,72.8},
{'Abnormalia','Tbilisi',-41.7,44.8},
{'gigazavr','Moscow',-55.8,37.6},
{'akiva','Jerusalem',-31.8,35.2},
{'inancyuce','Istanbul',-41,29},
{'Jordan','Johannesburg',26.2,28},
{'Cabernet','Banska Bystrica',-48.7,19.2},
{'akaJag','Stockholm',-59.3,18.1},
{'tnlogy','Norrkoping',-58.6,16.2},
{'chunnoo','Trondheim',-63,10.4},
{'Elturro','Germany',-51.5,9.9},
{'deactive','Milan',-45.4,9.2},
{'gunnar_z','Frankfurt',-50.1,8.7},
{'CodeChaos','Zurich',-47.4,8.5},
{'Andrew_Stacey','Norway',-61,8},
{'warox','Norway',-61,8},
{'DaDo','Cologne',-51,7},
{'derhannes','Cologne',-51,7},
{'Spielkind','Dusseldorf',-51.2,6.8},
{'hpsoft','Frejus',-43.4,6.7},
{'matox','Aachen',-50.8,6},
{'Jessevenderheide','Dokkum',-53.3,6},
{'Jmv38','Grenoble',-45.2,5.7},
{'yelnats','Marche-en-Famenne',-50.2,5.3},
{'Dreamdancer','Zoetemeer',-52,4.5},
{'hartland','Rhoon',-51.9,4.4},
{'Gareth','Brussels',-50.9,4.4},
{'juaxix','Granada',-37.2,3.6},
{'toffer','Paris',-48.9,2.4},
{'Bendiben','Boulogne',-50.7,1.6},
{'Trasco','Toulouse',-43.6,1.4},
{'spacemonkey','London',-51.5,0},
{'andymac3d','London',-51.5,0},
{'TechDojo','Stafford',-52.7,-2},
{'Luatee','England',-53,-2.4},
{'west','Glasgow',-55.9,-4.3},
{'MurielleG','Bermuda',-32.3,-64.8},
{'Godzila','Montreal',-45.5,-73.5},
{'Ceres','Philadelphia',-40,-75.2},
{'Ric_Esrey','Virginia',-37.5,-79},
{'matthew','Jacksonville, FL',-30.3,-81.7},
{'dave1707','Oberlin',-41.3,-82.2},
{'corneliuhoffman','Ohio',-40.5,-82.5},
{'pewbert','Ontario',-50,-85},
{'edat44','South Bend',-41.7,-86.2},
{'Keebo','Alabama',-32.7,-86.7},
{'EpicPotato','Alabama',-32.7,-86.7},
{'blmacbeth','Birmingham, Alabama',-33.5,-86.8},
{'Mark','St Louis, Missouri',-38.6,-90.2},
{'toadkick','Austin',-30.2,-97.8},
{'starblue','Austin',-30.2,-97.8},
{'quezadav','Guadalajara',-20.7,-103.3},
{'CodeaNoob','Alberta',-55,-115},
{'Briarfox','Orange County',-33.7,-117.8},
{'Levenp','Foster City',-37.8,-122.3},
{'CodingOnNapkins','Seattle',-47.6,-122.3},
{'HHNET','San Francisco',-37.8,-122.4},
{'jlslate','Eugene',-44,-123},
{'Zoyt','Prescott, AZ',-34.6,-112.5},
{'piinthesky','Marseille',-43.3,5.4},
{'JustinDrake','B.C.',-54,-125},
{'Prynok','Kansas',-38.5,-98},
{'Luismi','Miranda',-10.3,-66.3},
{'stevon8ter','Brussels',-50.9,4.4},
{'SkyTheCoder','WA',-47.5,-120.5},
{'baris','Izmir',-38.4,27.1},
{'Kjell','Groningen',-53.2,6.5}
}
--# LoadPics
--These functions download the images required
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
img1=LoadImage('Dropbox:EarthDay',
'http://i1303.photobucket.com/albums/ag142/ignatz_mouse/Daylight2x_zps16070fda.png')
img=LoadImage('Dropbox:EarthNight',
'http://i1303.photobucket.com/albums/ag142/ignatz_mouse/Earthlights2x_zps6a89100c.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
--# ccConfig
--[[
###########################################
##Codea Community Project Config Settings##
###########################################
##You can use # to comment out a line
##Use 1 for true and 0 for false
###########################################
# Add project info below #
#==========================================
ProjectName: Codea users world map
Version: 1.01
Comments: real time day and night world map
Author: ignatz
##License Info: http://choosealicense.com
##Supported Licneses: MIT, GPL, Apache, NoLicense
License: MIT
#==========================================
###########################################
# Settings #
[Settings]=================================
##Codea Community Configuration settings
##Format: Setting state
Button 1
NotifyCCUpdate 1
ResetUserOption 0
AddHeaderInfo 1
[/Settings]================================
###########################################
# Screenshots #
[Screenshots]==============================
##Screenshots from your project.
##Format: url
##Example: http://www.dropbox.com/screenshot.jpg
[/Screenshots]=============================
###########################################
# Video #
[Video]====================================
##Link to a YouTube.com video.
##Format: url
##Example: http://www.youtube.com/videolink
[/Video]===================================
###########################################
# Dependencies #
[Dependencies]=============================
##Include the names of any dependencies here
##Format: Dependency
##Example: Codea Community
[/Dependencies]============================
############################################
# Tabs #
[Tabs]======================================
##If you would like to upload specific tabs. If this is blank then all tabs will be uploaded.
##Format: tab
##Example:Main
[/Tabs]=====================================
#############################################
# Assets #
[Assets]=====================================
##Directory, path and url info for any assets besides the standard Codea assets.
##Format: Folder:sprite URL
##Example: Documents:sprite1 http://www.somewebsite.com/img.jpg
[/Assets]====================================
--]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment