-- Grass | |
-- Use this function to perform your initial setup | |
function setup() | |
field = mesh() | |
--field.shader = shader("Documents:Grass") | |
field.shader = shader(GrassShader.vertexShader, GrassShader.fragmentShader) | |
bladeWidth = 0.15 | |
bladeHeight = 1.5 | |
midWeight = 0.4 | |
size = vec2(30,30) | |
touchScale = 10 | |
numBlades = 10000 | |
field.shader.bladeHeight = bladeHeight | |
createField(field, size,numBlades) | |
generateTouchMesh(size) | |
--pressureZones are x,y then radius and finally type (1 = round pushout) | |
field.shader.pressureZone = vec4(-1000,-1000,0,0) | |
campos = vec2(0,30) | |
imageTouchSurface = image(WIDTH/touchScale,HEIGHT/touchScale) | |
currentPressurePoint = 1 | |
pressurePoints = {} | |
pressurePoints[1] = vec4(-1000,-1000,0,0) | |
end | |
function generateTouchMesh(size) | |
--make a square the same footprint as our field to read touch against | |
touchMesh = mesh() | |
touchMesh:resize(6) | |
touchMesh.vertices = { | |
vec3(-size.x/2, 0, -size.y/2), | |
vec3(-size.x/2, 0, size.y/2), | |
vec3(size.x/2, 0, -size.y/2), | |
vec3(-size.x/2, 0, size.y/2), | |
vec3(size.x/2, 0, size.y/2), | |
vec3(size.x/2, 0, -size.y/2) | |
} | |
touchMesh.texCoords = { | |
vec2(0,0), | |
vec2(0,1), | |
vec2(1,0), | |
vec2(0,1), | |
vec2(1,1), | |
vec2(1,0) | |
} | |
touchMesh:setColors(color(255,255,255,255)) | |
--call out for out texture | |
touchMesh.texture = generateCoordTexture(vec2(100,100)) | |
end | |
function generateCoordTexture(resolution) | |
tex = image(resolution.x, resolution.y) | |
for x=1,resolution.x do | |
for y=1, resolution.y do | |
tex:set(x,y,x/resolution.x*255,y/resolution.y*255,0) | |
end | |
end | |
return tex | |
end | |
function createField(field, size, blades) | |
--data is a vec4 that encode x as weight and y as height from base z and w represent x,y on the field | |
dataBuffer = field:buffer("data") | |
for i=1,blades do | |
pos = vec3(math.random() * size.x - (size.x/2), 0, math.random() * size.y - (size.y/2)) | |
createBlade(field, pos, pos + vec3(0,bladeHeight,0)) | |
end | |
end | |
function createBlade(field, baseLocation, topLocation) | |
local firstVertex = field.size + 1 | |
field:resize(firstVertex+11) | |
local bladeRotation = math.random(360) | |
--setup our vertex locations | |
--2d vertex positions | |
v2d = vec2(bladeWidth/2,0):rotate(math.rad(bladeRotation)) | |
midLocation = (baseLocation + topLocation)/2 | |
--calculate the vertices | |
--base corners | |
v1 = vec3(v2d.x+baseLocation.x,baseLocation.y, v2d.y+baseLocation.z) | |
v2 = vec3(-v2d.x+baseLocation.x, baseLocation.y, -v2d.y+baseLocation.z) | |
--mid corners | |
v3 = vec3((v2d.x)+midLocation.x, midLocation.y, (v2d.x)+midLocation.z) | |
v4 = vec3(-(v2d.x)+midLocation.x, midLocation.y, -(v2d.x)+midLocation.z) | |
--point | |
v5 = vec3(v2d.x/2+topLocation.x,topLocation.y, v2d.y/2+topLocation.z) | |
v6 = vec3(-v2d.x/2+topLocation.x, topLocation.y, -v2d.y/2+topLocation.z) | |
basecol = color(86, 153, 96, 255) | |
midcol = color(63, 163, 78, 255) | |
topcol = color(12, 229, 8, 255) | |
field:vertex(firstVertex, v1) | |
field:vertex(firstVertex+1, v2) | |
field:vertex(firstVertex+2, v3) | |
normal = (v2-v1):normalize():cross((v3-v1):normalize()):normalize() | |
field:normal(firstVertex, normal) | |
field:normal(firstVertex+1, normal) | |
field:normal(firstVertex+2, normal) | |
field:normal(firstVertex+3, normal) | |
field:normal(firstVertex+4, normal) | |
field:normal(firstVertex+5, normal) | |
field:normal(firstVertex+6, normal) | |
field:normal(firstVertex+7, normal) | |
field:normal(firstVertex+8, normal) | |
field:normal(firstVertex+9, normal) | |
field:normal(firstVertex+10, normal) | |
field:normal(firstVertex+11, normal) | |
field:vertex(firstVertex+3, v3) | |
field:vertex(firstVertex+4, v4) | |
field:vertex(firstVertex+5, v2) | |
field:vertex(firstVertex+6, v3) | |
field:vertex(firstVertex+7, v4) | |
field:vertex(firstVertex+8, v5) | |
field:vertex(firstVertex+9, v4) | |
field:vertex(firstVertex+10, v5) | |
field:vertex(firstVertex+11, v6) | |
field:color(firstVertex, basecol) | |
field:color(firstVertex+1, basecol) | |
field:color(firstVertex+2, midcol) | |
field:color(firstVertex+3, midcol) | |
field:color(firstVertex+4, midcol) | |
field:color(firstVertex+5, basecol) | |
field:color(firstVertex+6, midcol) | |
field:color(firstVertex+7, midcol) | |
field:color(firstVertex+8, topcol) | |
field:color(firstVertex+9, midcol) | |
field:color(firstVertex+10, topcol) | |
field:color(firstVertex+11, topcol) | |
dataBuffer[firstVertex] = vec4(0, 0, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+1] = vec4(0, 0, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+2] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+3] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+4] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+5] = vec4(0, 0, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+6] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+7] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+8] = vec4(1, topLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+9] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+10] = vec4(1, topLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
dataBuffer[firstVertex+11] = vec4(1, topLocation.y - baseLocation.y, baseLocation.x, baseLocation.z) | |
end | |
function touched(touch) | |
if touch.state == BEGAN or touch.state == MOVING then | |
r,g,b,a = imageTouchSurface:get(touch.x/touchScale, touch.y/touchScale) | |
r = r / 255 * size.x - size.x/2 | |
g = g / 255 * size.y - size.y/2 | |
if math.abs(r-pressurePoints[currentPressurePoint].x) + math.abs(g-pressurePoints[currentPressurePoint].y) > 1 then | |
currentPressurePoint = (currentPressurePoint + 1)%20 | |
pressurePoints[currentPressurePoint] = vec4(r,g,3,ElapsedTime) | |
if currentPressurePoint == 1 then | |
field.shader.pressureZone1 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 2 then | |
field.shader.pressureZone2 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 3 then | |
field.shader.pressureZone3 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 4 then | |
field.shader.pressureZone4 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 5 then | |
field.shader.pressureZone5 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 6 then | |
field.shader.pressureZone6 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 7 then | |
field.shader.pressureZone7 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 8 then | |
field.shader.pressureZone8 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 9 then | |
field.shader.pressureZone9 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 10 then | |
field.shader.pressureZone10 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 11 then | |
field.shader.pressureZone11 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 12 then | |
field.shader.pressureZone12 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 13 then | |
field.shader.pressureZone13 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 14 then | |
field.shader.pressureZone14 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 15 then | |
field.shader.pressureZone15 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 16 then | |
field.shader.pressureZone16 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 17 then | |
field.shader.pressureZone17 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 18 then | |
field.shader.pressureZone18 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 19 then | |
field.shader.pressureZone19 = pressurePoints[currentPressurePoint] | |
elseif currentPressurePoint == 20 then | |
field.shader.pressureZone20 = pressurePoints[currentPressurePoint] | |
end | |
end | |
end | |
end | |
-- This function gets called once every frame | |
function draw() | |
campos = campos:rotate(math.rad(0.2)) | |
output.clear() | |
print(1/DeltaTime) | |
field.shader.time = ElapsedTime | |
setContext(imageTouchSurface) | |
background(40, 40, 50) | |
camera(campos.x,20,campos.y, 0,0,0) | |
perspective() | |
touchMesh:draw() | |
setContext() | |
background(40,40,50) | |
camera(campos.x,20,campos.y, 0,0,0) | |
perspective() | |
field.shader.vEyePosition = vec3(campos.x, 20, campos.y) | |
field.shader.vLightPosition = vec3(10,80,20) | |
field.shader.mInvModel = modelMatrix():inverse():transpose() | |
field:draw() | |
end | |
GrassShader = { | |
vertexShader = [[ | |
// | |
// A basic vertex shader | |
// | |
//This is the current model * view * projection matrix | |
// Codea sets it automatically | |
uniform mat4 modelViewProjection; | |
uniform vec4 pressureZone1; | |
uniform vec4 pressureZone2; | |
uniform vec4 pressureZone3; | |
uniform vec4 pressureZone4; | |
uniform vec4 pressureZone5; | |
uniform vec4 pressureZone6; | |
uniform vec4 pressureZone7; | |
uniform vec4 pressureZone8; | |
uniform vec4 pressureZone9; | |
uniform vec4 pressureZone10; | |
uniform vec4 pressureZone11; | |
uniform vec4 pressureZone12; | |
uniform vec4 pressureZone13; | |
uniform vec4 pressureZone14; | |
uniform vec4 pressureZone15; | |
uniform vec4 pressureZone16; | |
uniform vec4 pressureZone17; | |
uniform vec4 pressureZone18; | |
uniform vec4 pressureZone19; | |
uniform vec4 pressureZone20; | |
uniform float time; | |
uniform highp mat4 mInvModel; | |
uniform mediump vec3 vEyePosition; | |
uniform mediump vec3 vLightPosition; | |
//This is the current mesh vertex position, color and tex coord | |
// Set automatically | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec3 normal; | |
attribute vec4 data; | |
//This is an output variable that will be passed to the fragment shader | |
varying lowp vec4 vColor; | |
varying highp vec3 lightDirection; | |
varying mediump vec3 eyeDirection; | |
varying mediump vec3 vNormal; | |
/// 2D Noise by Ian McEwan, Ashima Arts. | |
vec3 mod289(vec3 x) { | |
return x - floor(x * (1.0 / 289.0)) * 289.0; | |
} | |
vec2 mod289(vec2 x) { | |
return x - floor(x * (1.0 / 289.0)) * 289.0; | |
} | |
vec3 permute(vec3 x) { | |
return mod289(((x*34.0)+1.0)*x); | |
} | |
float snoise (vec2 v) { | |
const vec4 C = vec4( | |
0.211324865405187, // (3.0-sqrt(3.0))/6.0 | |
0.366025403784439, // 0.5*(sqrt(3.0)-1.0) | |
-0.577350269189626, // -1.0 + 2.0 * C.x | |
0.024390243902439); // 1.0 / 41.0 | |
// First corner | |
vec2 i = floor(v + dot(v, C.yy) ); | |
vec2 x0 = v - i + dot(i, C.xx); | |
// Other corners | |
vec2 i1; | |
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); | |
vec4 x12 = x0.xyxy + C.xxzz; x12.xy -= i1; | |
// Permutations | |
i = mod289(i); | |
// Avoid truncation effects in permutation | |
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 )); | |
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); | |
m = m*m ; | |
m = m*m ; | |
// Gradients: 41 points uniformly over a line, mapped onto a diamond. | |
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) | |
vec3 x = 2.0 * fract(p * C.www) - 1.0; | |
vec3 h = abs(x) - 0.5; | |
vec3 ox = floor(x + 0.5); | |
vec3 a0 = x - ox; | |
// Normalise gradients implicitly by scaling m | |
// Approximation of: m *= inversesqrt( a0*a0 + h*h ); | |
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); | |
// Compute final noise value at P | |
vec3 g; | |
g.x = a0.x * x0.x + h.x * x0.y; | |
g.yz = a0.yz * x12.xz + h.yz * x12.yw; | |
return 60.0 * dot(m, g); | |
} | |
vec2 applyPressure(vec4 pressureZone) | |
{ | |
float radius = length(pressureZone.xy - data.zw); | |
if (radius < pressureZone.z) { | |
float factor; | |
if (time < pressureZone.w + 2.5) { | |
if (time < pressureZone.w + 0.5) { | |
factor = 1.0 - (pressureZone.w + 0.5 - time)/0.5; | |
} | |
else { | |
factor = 1.0 - (time - pressureZone.w - 0.5)/2.0; | |
} | |
return (normalize(data.zw - pressureZone.xy) * | |
(1.0 - radius/pressureZone.z)) * factor * 2.0; | |
} | |
} | |
return vec2(0,0); | |
} | |
void main() | |
{ | |
//convert the positions of the eye and light from world space to object space and then make a vector from position for ADS lighting | |
lightDirection = ((vec4(vLightPosition,1) * mInvModel) - position).xyz; | |
eyeDirection = ((vec4(vEyePosition,1) * mInvModel) - position).xyz; | |
//Pass the mesh color to the fragment shader | |
vColor = color; | |
//apply force by weight | |
vec2 deformation = vec2(snoise(vec2(data.z/15.0+time/2.0,data.w/15.0+time/2.0)), | |
snoise(vec2(data.z/15.0+time/2.0+5000.0,data.w/15.0+time/2.0+5000.0))); | |
//apply pressureZone | |
deformation = deformation + applyPressure(pressureZone1); | |
deformation = deformation + applyPressure(pressureZone2); | |
deformation = deformation + applyPressure(pressureZone3); | |
deformation = deformation + applyPressure(pressureZone4); | |
deformation = deformation + applyPressure(pressureZone5); | |
deformation = deformation + applyPressure(pressureZone6); | |
deformation = deformation + applyPressure(pressureZone7); | |
deformation = deformation + applyPressure(pressureZone8); | |
deformation = deformation + applyPressure(pressureZone9); | |
deformation = deformation + applyPressure(pressureZone10); | |
deformation = deformation + applyPressure(pressureZone11); | |
deformation = deformation + applyPressure(pressureZone12); | |
deformation = deformation + applyPressure(pressureZone13); | |
deformation = deformation + applyPressure(pressureZone14); | |
deformation = deformation + applyPressure(pressureZone15); | |
deformation = deformation + applyPressure(pressureZone16); | |
deformation = deformation + applyPressure(pressureZone17); | |
deformation = deformation + applyPressure(pressureZone18); | |
deformation = deformation + applyPressure(pressureZone19); | |
deformation = deformation + applyPressure(pressureZone20); | |
deformation = deformation * data.x; | |
//limit the force length max to length from root | |
if (length(deformation) > (data.y*0.99)) { | |
deformation = normalize(deformation)*(data.y*0.99); | |
} | |
//calculate the vertical adjustment to retain length with deformation | |
float heightReduction = -(data.y-sqrt(pow(data.y,2.0) - pow(length(deformation),2.0))); | |
vec4 positionModification = vec4(deformation.x, | |
heightReduction, deformation.y,0); | |
//adjust the normal by the deformation for lighting | |
float defHorLength = length(deformation); | |
vec2 defHorDirection = normalize(deformation); | |
//normal and color to fragment | |
//normals should be adjusted for pressure... | |
vNormal = normal; | |
if (data.y > 0.1) { | |
vNormal = normalize(normal + vec3(-defHorDirection.x*heightReduction/data.y, | |
defHorLength/data.y, | |
-defHorDirection.y*heightReduction/data.y)); | |
} | |
//Multiply the vertex position by our combined transform | |
gl_Position = modelViewProjection * (position + positionModification); | |
} | |
]], | |
fragmentShader = [[ | |
precision lowp float; | |
uniform lowp sampler2D texture; | |
//uniform lowp sampler2D bumpMap; | |
varying lowp vec4 vColor; | |
varying mediump vec3 lightDirection; | |
varying mediump vec3 eyeDirection; | |
varying mediump vec3 vNormal; | |
const float c_zero = 0.0; | |
const float c_one = 1.0; | |
const float c_two = 2.0; | |
void main() | |
{ | |
//if (!gl_FrontFacing) discard; | |
vec3 curNormal = normalize(vNormal); | |
lowp vec4 curCol = vColor; | |
vec3 vLightDirection = normalize(normalize(lightDirection)); | |
vec3 vCameraDirection = normalize(eyeDirection); | |
lowp vec4 vAmbientColor = curCol * 0.2; | |
// Calculate Diffuse intensity | |
float fDiffuseIntensity = max(max( c_zero, dot( curNormal, vLightDirection )), dot(-curNormal, vLightDirection)); | |
lowp vec4 vDiffuseColor = curCol * fDiffuseIntensity; | |
vAmbientColor.a = c_one; | |
vDiffuseColor.a = c_one; | |
//Set the output color to the texture color | |
gl_FragColor = vAmbientColor + vDiffuseColor; | |
//gl_FragColor = curCol; | |
} | |
]] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment