Skip to content

Instantly share code, notes, and snippets.

@juaxix
Last active December 21, 2015 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save juaxix/6240100 to your computer and use it in GitHub Desktop.
Save juaxix/6240100 to your computer and use it in GitHub Desktop.
Destructible Terrain and water fluids
-- destructable
-- Use this function to perform your initial setup
function setup()
    parameter.boolean("addWater",false)
    defaultGravity = physics.gravity()
    parameter.boolean("useGravity",false, function(v)
       if v then 
        physics.gravity(Gravity)
       else
        physics.gravity(defaultGravity)
       end
    end)
    
    parameter.watch("#balls")
    
    txture = readImage("Documents:ExampleCircle",5,5)
    ter = Terrain(vec2(WIDTH/2,HEIGHT/4),vec2(WIDTH,HEIGHT/2))
    circ = physics.body(CIRCLE,15)
    circ.position = vec2(WIDTH/2,600)
    holding = nil
    setupWater()
    bgImg = readImage("SpaceCute:Background")
end
function touched(touch)
    if addWater then
        WaterTouched(touch)
    else
        TerrainTouched(touch)
    end
end
function TerrainTouched(t)
    local tp = vec2(t.x,t.y)
    local cp = circ.position
    if t.state == BEGAN and tp:dist(cp) < 30 then
        holding = tp
    end
    if t.state == MOVING and holding ~= nil then
        holding = tp
    end
    if t.state == ENDED and holding ~= nil then
        holding = nil
    end
    if holding == nil then
        ter:touched(t)
    end  
end
-- This function gets called once every frame
function draw()
    if useGravity then 
        physics.gravity(Gravity)
    end
    --background(127, 127, 127, 255)
    background(255)
    sprite(bgImg, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
    -- This sets the line thickness
    strokeWidth(5)
    if holding ~= nil then
        circ:applyForce(holding-circ.position-circ.linearVelocity/4)
    end
    -- Do your drawing here
    ter:draw()
    waterDraw()
    sprite("Documents:ExampleCircle",circ.x,circ.y,circ.radius*2.5)
end
function setupWater()
    m = {}
    m.shader = shader(vS, fS)
    Number_of_Balls = 500
    Metaball_Resolution = 211
    Metaball_Size = 50
    
    function GENERATE_METABALL()
        local mr = Metaball_Resolution
        local ms = Metaball_Size
        blendMode(NORMAL)
        ballTex = image(166, 166)
        local a,d2,ref2
        ref2 = ms*ms/15
        for i = 1,166 do 
            for j =1,166 do
             d2 = (i-100)*(i-100) + (j-100)*(j-100)
             a = math.exp(-d2/ref2)*255
             ballTex:set(i,j,color(a,a,a,255))
             
            end
        end 
    end
    GENERATE_METABALL()
    balls = {}
    
    img = image(WIDTH, HEIGHT)
    m = mesh()
    r = m:addRect(WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT)
    m:setRectTex(r, 0, 0, 1, 1)
    m.texture = img
    m.shader = shader(vS, fS) 
    --soft = SoftBody()
end
function createDrop(x,y)
    local ball = physics.body(CIRCLE, 6)
    --ball.type = DYNAMIC
    ball.x = x
    ball.y = y
    ball.restitution = .1
    ball.linearVelocity = vec2(0,0)
    ball.friction = 0.05
    ball.mass = 0.1
    ball.angularVelocity = 0.0
    ball.bullet = false
    ball.linearDamping = 0
    return ball
end
function waterDraw()
    -- remove invisible balls... here to avoid flickering
    for k,b in ipairs(balls) do
        if b.x+Metaball_Size> WIDTH +Metaball_Size *2 or b.x<-Metaball_Size then
            table.remove(balls, k)
            b:destroy()
        end
    end
    blendMode(NORMAL)
    setContext(img)
        background(0) -- clear buffer
        --tint(139, 145, 157, 255)
        blendMode(ADDITIVE)
        for k,b in ipairs(balls) do
            sprite(ballTex, b.x, b.y)
        end
        --noTint()
   setContext()
   
   m.texture = img
   blendMode(MULTIPLY)
   m:draw()
   
end
function WaterTouched(touch)
    if touch.state == BEGAN or touch.state == MOVING then
        if balls[Number_of_Balls + 1] == nil then
            local ball = createDrop(touch.x,touch.y)
            table.insert(balls, ball)
        else
            balls[Number_of_Balls + 1].x = touch.x
            balls[Number_of_Balls + 1].y = touch.y
        end
    end
end
vS = [[
//
// 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;
}
]]
fS = [[
//
// 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, vTexCoord ) * vColor;
    //Set the output color to the texture color
    if (max(col.r, max(col.g, col.b)) > 0.75)
    {
        gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
    }
    else
    {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}
]]
Terrain = class()
function Terrain:init(pos,size)
    self.pos = pos
    self.size = size
    local verts = {}
    for x=1,20 do
        table.insert(verts,vec2(pos.x-size.x/2+(size.x/20)*x,pos.y+size.y/2))
    end
    for y=1,20 do
        table.insert(verts,vec2(pos.x+size.x/2,pos.y+size.y/2-(size.y/20)*y))
    end
    for x=1,20 do
        table.insert(verts,vec2(pos.x+size.x/2-(size.x/20)*x,pos.y-size.y/2))
    end
    for y=1,20 do
        table.insert(verts,vec2(pos.x-size.x/2,pos.y-size.y/2+(size.y/20)*y))
    end
    self.verts = verts
    self.m = mesh()
    self.m:setColors(255,255,255,255)
    self.m.vertices = triangulate(verts)
    self.floor = physics.body(POLYGON,unpack(verts))
    self.floor.type = STATIC
    self.bound = 30
    self.oe = ElapsedTime
end
function Terrain:draw()
    if ElapsedTime > self.oe+0.2 then
        self.oe = ElapsedTime
        if self.t then
        self.m.vertices = triangulate(self.verts)
        self.floor:destroy()
        self.floor = physics.body(POLYGON,unpack(self.verts))
        self.floor.type = STATIC
        end
    end
    self.m:draw() 
    local sv = self.verts
    for i=1,#sv do
        sprite(txture,sv[i].x,sv[i].y,5,5)
    end
    pushStyle()
        fill(0,255)
        text(math.floor(1/DeltaTime).."FPS",100,600)
    popStyle()
end
function Terrain:touched(t)
    self.t = true
    if t.state == ENDED then
        self.t = false
    end
    local p,s = self.pos,self.size
    local tp = vec2(t.x,t.y)
    local sp = #self.verts
    for i=1,sp do
        local sv = self.verts[i]
        local svi
        if i > 1 then
            svi = self.verts[i-1]
        else
            svi = self.verts[sp]
        end
        if not sv then return end
        local pass = true
        if sv:dist(tp) < self.bound then
            local dir = (sv-tp):normalize()*5+vec2(t.deltaX,t.deltaY)
            for ii=1,sp do
                if i ~= ii then
                    local tsv = self.verts[ii]
                    if tsv:dist(sv+dir) < self.bound/2 then
                        pass = false
                    end
                end
            end
            --pass = true
            if pass then
                self.verts[i] = self.verts[i] + dir
            end
        end
        if sv:dist(svi) > self.bound*2 and pass then
            local d = (sv+svi)/2
            if d.x > p.x-s.x/2 and d.x < p.x+s.x/2 and d.y > p.y-s.y/2 then
                table.insert(self.verts,i,d)
            else
                table.remove(self.verts,i)
                return
            end
        elseif sv:dist(svi) < self.bound/2 then
            table.remove(self.verts,i)
            return
        end
    end  
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment