Skip to content

Instantly share code, notes, and snippets.

@sp4cemonkey
Last active December 14, 2015 06:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sp4cemonkey/5046513 to your computer and use it in GitHub Desktop.
Save sp4cemonkey/5046513 to your computer and use it in GitHub Desktop.
--# Main
--# Main
-- Use this function to perform your initial setup
displayMode(FULLSCREEN)
function setup()
stick = Stick()
pylon = Wall("Cargo Bot:Crate Red 2")
wall = Wall("Cargo Bot:Crate Yellow 2")
floor = Floor("Cargo Bot:Crate Green 2")
world = World()
hero = Hero(3,3)
TO_DEG = 180/math.pi
end
-- This function gets called once every frame
function draw()
--output.clear()
--print(1/DeltaTime)
--print(ElapsedTime)
background(0)
local TO_DEG = TO_DEG
local hero = hero
perspective(60)
camera(hero.x, 3, 1 + hero.z, hero.x, 0, hero.z, 0, 1, 0)
eye = vec3(hero.x, 3, 1 + hero.z)
--fixed light
--light = vec3(7, 3, 7)
--light just above the hero's head
light = vec3(hero.x, 0.5, hero.z)
-- Draw world
pushMatrix()
world:draw(eye, light)
popMatrix()
-- Draw hero
translate(hero.x, hero.y, hero.z)
rotate(stick.direction*TO_DEG, 0, 1, 0)
-- roll animation
if stick.active then
rotate(-ElapsedTime*10*TO_DEG, 0, 0, 1)
end
scale(.25, .25, .25)
hero:draw(eye, light)
-- Restore orthographic projection
ortho()
viewMatrix(matrix())
resetMatrix()
-- fade out overlay
--sprite("Cargo Bot:Background Fade", WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
if stick.active then
local ceil = math.ceil
stick:draw()
-- move hero based on stick direction
local mvtx = math.cos(stick.direction)/50*stick.dist
local mvtz = -math.sin(stick.direction)/50*stick.dist
hero.x = hero.x + mvtx
hero.z = hero.z + mvtz
-- convert to table coordinates
hero.px = ceil(hero.x - .5)
hero.py = ceil(hero.z - .5)
-- lazy collision check
if world.data[hero.py][hero.px] ~= 0 then
hero.x = hero.x - mvtx
hero.z = hero.z - mvtz
hero.px = ceil(hero.x - .5)
hero.py = ceil(hero.z - .5)
end
end
end
function touched(touch)
stick:touched(touch)
end
--# World
World = class()
function World:init()
-- define the world
self.data =
{
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 2, 0, 0, 0, 0, 2, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 1, 2, 0, 0, 1},
{1, 0, 0, 2, 1, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1},
{1, 2, 0, 0, 0, 0, 2, 1},
{1, 1, 1, 1, 1, 1, 1, 1}
}
end
function World:draw(eye, light)
local floor, wall, pylon = floor, wall, pylon
local offSet = 3
local px, py = hero.px, hero.py
-- look around the hero to draw whatever is around him
translate(px - offSet, 0, py - offSet)
for y = py - offSet, py + offSet do
for x = px - offSet, px + offSet do
if self.data[y] then
local val = self.data[y][x]
if val == 0 then
floor:draw(eye, light)
elseif val == 1 then
wall:draw(eye, light)
elseif val == 2 then
pylon:draw(eye, light)
end
end
translate(1,0,0)
end
translate(-(1 + 2 * offSet), 0, 1)
end
end
--# Hero
Hero = class()
function Hero:init(x, z)
self.x, self.y, self.z = x,0,z
self.px, self.py = math.ceil(.5+x), math.ceil(.5+z)
self.mdl = Wall("Cargo Bot:Crate Blue 2")
end
function Hero:draw(eye, light)
self.mdl:draw(eye, light)
end
--# Wall
Wall = class()
function Wall:init(tex)
-- all the unique vertices that make up a cube
local vertices =
{
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
local verts =
{
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1]
}
-- all the unique texture positions needed
local texvertices =
{
vec2(0.02,0.02),
vec2(0.98,0.02),
vec2(0.02,0.98),
vec2(0.98,0.98)
}
-- apply the texture coordinates to each triangle
local texCoords =
{
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3]
}
self.model = LitMesh()
self.model.litMesh.vertices = verts
theTexture = readImage(tex)
self.model.litMesh.texCoords = texCoords
self.model:deriveVertexNTB()
self.model:setTexture(theTexture)
local tempText = readImage(tex)
theBumpMap = self.model:generateTextureBumpMap(tempText,0.007)
self.model:setBumpMap(theBumpMap)
end
function Wall:draw(eye, light)
self.model:setLight(light, 0.2, 1.0, 1.0, color(255))
self.model:setEye(eye)
self.model:draw()
end
--# Floor
Floor = class()
function Floor:init(tex)
-- all the unique vertices that make up a cube
local vertices =
{
vec3( 0.5, -0.5, 0.5), -- Right top front
vec3(-0.5, -0.5, 0.5), -- Left top front
vec3( 0.5, -0.5, -0.5), -- Right top back
vec3(-0.5, -0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
local verts =
{
-- Bottom
vertices[3], vertices[4], vertices[2],
vertices[3], vertices[2], vertices[1],
}
-- all the unique texture positions needed
local texvertices =
{
vec2(0.02,0.02),
vec2(0.98,0.02),
vec2(0.02,0.98),
vec2(0.98,0.98)
}
-- apply the texture coordinates to each triangle
local texCoords =
{
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
self.model = LitMesh()
self.model.litMesh.vertices = verts
theTexture = readImage(tex)
self.model.litMesh.texCoords = texCoords
self.model:deriveVertexNTB()
self.model:setTexture(theTexture)
local tempText = readImage(tex)
theBumpMap = self.model:generateTextureBumpMap(tempText,0.007)
self.model:setBumpMap(theBumpMap)
end
function Floor:draw(eye, light)
self.model:setLight(light, 0.2, 1.0, 0.6, color(255))
self.model:setEye(eye)
self.model:draw()
end
--# Stick
Stick = class()
function Stick:init()
self.direction = 0
self.dist = 0
self.active = false
self.origin = vec2(150, 150)
self.center = self.origin
self.pos = self.origin
self.stick_bg = readImage("Small World:Dialog Icon")
self.stick = readImage("Small World:Bush")
-- self.stick_bg = readImage("Space Art:Eclipse")
-- self.stick = readImage("Space Art:UFO")
end
function Stick:draw()
sprite(self.stick_bg, self.center.x, self.center.y)
sprite(self.stick, self.pos.x, self.pos.y)
end
function Stick:touched(touch)
if touch.state == BEGAN then
self.center = vec2(touch.x, touch.y)
self.active = true
end
self.pos = vec2(touch.x, touch.y)
self.direction = math.atan2(self.pos.y - self.center.y, self.pos.x - self.center.x)
self.dist = math.min(2, self.pos:dist(self.center)/32)
if touch.state == ENDED then
self.center = self.origin
self.pos = self.center
self.active = false
end
end
--# LitMesh
LitMesh = class()
--[[
LitMesh provides a bumpmappable ADS (ambient/diffuse/specular) lighting enhanced class
for meshes.
usage:
setup()
myObject = LitMesh()
myObject.litMesh.vertices = some table of vertices
-- this must be a multiple of 3 representing the triangles
-- this is the normal mesh vertices array and can be manipulated as per docs
-- eg myObject.litMesh:vertex(i, vec3(x,y,z))
-- myObject.litMesh:setRect(i,x,y,w,h)
myObject.litMesh.texCoords = some table of texture coordinates for the vertices
-- this is the normal mesh texCoords array and can be
manipulated as per the docs
-- eg myObject.litMesh:texCoord(i, x, y)
-- myObject.litMesh:setRectTex(i,s,t,w,h)
--Once all vertexes and texture coordinates have been set call:
myObject:deriveVertexNTB()
-- you will need to make this call again if you modify any vertexes or texture coordinates
--setup lighting
--lightPosition is the location of the light in world space
--eyePosition is the location of the camera in world space this is the eye vector in your camera() call
myObject:setLight(vec3(lightPosition), ambient, diffuse, specular, lightColor)
--set the eye position which is the eye vector from your camera statement
myObject:setEye(vec3(eyePosition))
--then either color or texture your mesh with one of
myObject:setColor(color)
myObject:setTexture(texture)
--finally if you wish to use bumpmapping set a bumpmap
myObject:setBumpMap(bumpMap)
--a bumpMap is just an image with normal vectors encoded into the colors, these can be generated in tools like blender or photoshop
--Alternately the LitMesh class contains (a poor performing) helper method for generating bumpMaps from your texture: - strength is how aggressive the edge mapping is 0.01 is a good start
myObject:generateTextureBumpMap(texture, strength)
--this could be used as a shortcut by:
bumpTexture = cube:generateTextureBumpMap(cubeTexture, 0.01)
myObject:setBumpMap(bumpTexture)
draw()
--do all your normal drawing activity
background(40, 40, 50)
camera(eyex, eyey, eyez, lookatx, lookaty, lookatz)
--if the camera eye is moving remember to do
myObject:setEye(vec3(eyex, eyey, eyez))
perspective()
--object drawing
pushMatrix()
translate(x,y,z) --move centre of your mesh wherever
rotate(angle,x,y,z) -- rotate your object about it's centre
myObject:draw()
popMatrix()
]]
function LitMesh:init()
self.litMesh = mesh()
self.litMesh.shader = shader(LitMesh.ADSLighting.vertexShader, LitMesh.ADSLighting.fragmentShader)
self.litMesh.shader.useTexture = false
self.litMesh.shader.useBumpMap = false
end
function LitMesh:draw()
self.litMesh.shader.mInvModel = modelMatrix():inverse():transpose()
self.litMesh:draw()
end
function LitMesh:setLight(lightPosition, ambient, diffuse, specular, lightColor)
self.litMesh.shader.vLightPosition = lightPosition
self.litMesh.shader.vAmbientMaterial = ambient
self.litMesh.shader.vDiffuseMaterial = diffuse
self.litMesh.shader.vSpecularMaterial = specular
self.litMesh.shader.lightColor = lightColor
end
function LitMesh:setEye(eyePosition)
self.litMesh.shader.vEyePosition = eyePosition
end
function LitMesh:setTexture(texture)
self.litMesh.shader.useTexture = true
self.litMesh.texture = texture
end
function LitMesh:disableBumpMap()
self.litMesh.shader.useBumpMap = false
end
function LitMesh:setBumpMap(bumpMap)
self.litMesh.shader.useBumpMap = true
self.litMesh.shader.bumpMap = bumpMap
end
function LitMesh:setColor(surfaceColor)
--assumes the reason you are setting the color is because you don't want to use a texture
self.litMesh.texture = nil
self.litMesh.shader.useTexture = false
self.litMesh:setColors(surfaceColor)
end
function LitMesh:deriveVertexNTB()
--this will calculate the tangent, binormal and from those the normal for each vertex, these will all be stored in buffers
--assumes that the surfaces have their texture coordinates set (even if being left untextured)
--tangent is the X axis on the plane of the surface relative to textures
--binormal is the Y axis on the plane of the surface relative to textures
--normal is the surface normal
local texCoordBuffer = self.litMesh:buffer("texCoord")
if texCoordBuffer.length == self.litMesh.size then
useTexCoords = true
else
useTexCoords = false
end
local normalBuffer = self.litMesh:buffer("normal")
normalBuffer:resize(self.litMesh.size)
local tangentBuffer = self.litMesh:buffer("tangent")
tangentBuffer:resize(self.litMesh.size)
--local binormalBuffer = self.litMesh:buffer("binormal")
--binormalBuffer:resize(self.litMesh.size)
local tangent,binormal, normal
for i=1, self.litMesh.size/3 do
--calculate the surface vectors
local v1 = self.litMesh:vertex(i*3-1) - self.litMesh:vertex(i*3-2)
local v2 = self.litMesh:vertex(i*3) - self.litMesh:vertex(i*3-2)
--calculate the texture space vectors
if useTexCoords then
local tuV = vec2(self.litMesh:texCoord(i*3-1).x - self.litMesh:texCoord(i*3-2).x, self.litMesh:texCoord(i*3).x - self.litMesh:texCoord(i*3-2).x)
local tvV = vec2(self.litMesh:texCoord(i*3-1).y - self.litMesh:texCoord(i*3-2).y, self.litMesh:texCoord(i*3).y - self.litMesh:texCoord(i*3-2).y)
--calculate denominator
local den=1/(tuV.x*tvV.y - tuV.y*tvV.x)
--tangent
tangent = vec3((tvV.y*v1.x - tvV.x*v2.x)*den, (tvV.y*v1.y - tvV.x*v2.y)*den, (tvV.y*v1.z - tvV.x*v2.z)*den):normalize()
binormal = vec3((tuV.x*v2.x - tuV.y*v1.x)*den, (tuV.x*v2.y - tuV.y*v1.y)*den, (tuV.x*v2.z - tuV.y*v1.z)*den):normalize()
normal = tangent:cross(binormal):normalize()
else
tangent = v1:normalize()
normal = v1:normalize():cross(v2:normalize()):normalize()
binormal = normal:cross(tangent):normalize()
end
for j=i*3-2,i*3 do
normalBuffer[j] = normal
--binormalBuffer[j] = binormal
tangentBuffer[j] = tangent
end
end
end
function LitMesh:generateTextureBumpMap(source, strength)
local t = image(source.width, source.height)
for y=2,source.height-1 do
for x=2,source.width-1 do
r,g,b,a = source:get(x-1,y)
xLeft = (0.3*r+0.59*g+0.11*b)*strength
r,g,b,a = source:get(x+1,y)
xRight = (0.3*r+0.59*g+0.11*b)*strength
r,g,b,a = source:get(x,y-1)
yUp = (0.3*r+0.59*g+0.11*b)*strength
r,g,b,a = source:get(x,y+1)
yDown = (0.3*r+0.59*g+0.11*b)*strength
xDelta = ((xLeft-xRight)+1)*127.5
yDelta = ((yUp - yDown)+1)*127.5
t:set(x,y,color(xDelta,yDelta,255,255))
end
end
for y=1,source.height do
t:set(1,y,color(0,0,255,255))
t:set(source.width,y,color(0,0,255,255))
end
for x=1,source.width do
t:set(x,1,color(0,0,255,255))
t:set(x,source.height,color(0,0,255,255))
end
return t
end
LitMesh.ADSLighting = {
--shader()
vertexShader = [[
uniform lowp mat4 modelViewProjection;
uniform lowp mat4 mInvModel;
uniform lowp vec3 vEyePosition;
uniform lowp vec3 vLightPosition;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute vec3 normal;
attribute vec3 tangent;
varying lowp vec2 vTexCoord;
varying lowp vec3 lightDirection;
varying lowp vec3 eyeDirection;
void main()
{
lowp mat3 tangentMatrix = mat3(tangent, cross(normal, tangent), normal);
//convert the positions to object space, then get direction, then convert to tangent space
lightDirection = (((vec4(vLightPosition,1) * mInvModel) - position).xyz
* tangentMatrix).xyz;
eyeDirection = (((vec4(vEyePosition,1) * mInvModel) - position).xyz
* tangentMatrix).xyz;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision lowp float;
uniform lowp sampler2D texture;
uniform lowp sampler2D bumpMap;
varying lowp vec2 vTexCoord;
varying lowp vec3 lightDirection;
varying lowp vec3 eyeDirection;
uniform float vAmbientMaterial;
uniform float vDiffuseMaterial;
uniform float vSpecularMaterial;
uniform lowp vec4 lightColor;
void main()
{
vec3 curNormal = normalize(texture2D( bumpMap, vTexCoord ).xyz*2.0-vec3(1.0,1.0,1.0));
lowp vec4 curCol = texture2D( texture, vTexCoord);
vec3 vLightDirection = normalize(normalize(lightDirection));
vec3 vCameraDirection = normalize(eyeDirection);
lowp vec4 vAmbientColor = curCol * lightColor * vAmbientMaterial;
// Calculate Diffuse intensity
float fDiffuseIntensity = max( 0.0, dot( curNormal, vLightDirection ));
lowp vec4 vDiffuseColor = curCol * lightColor * fDiffuseIntensity * vDiffuseMaterial;
// Calculate the reflection vector between the incoming light and the
// normal (incoming angle = outgoing angle)
vec3 vReflection = reflect( -vLightDirection, curNormal );
// Calculate specular component
// Based on the dot product between the reflection vector and the camera
// direction.
float spec = pow( max( 0.0, dot( vCameraDirection, vReflection )), 32.0 );
lowp vec4 vSpecularColor = lightColor * spec * vSpecularMaterial;
vAmbientColor.a = 1.0;
vDiffuseColor.a = 1.0;
vSpecularColor.a = 1.0;
//Set the output color to the texture color
gl_FragColor = vAmbientColor + vDiffuseColor + vSpecularColor;
}
]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment