It uses hidden control points (circle bodies) to modify the shape of the mesh, the function triangulate use all of these centers and then with a shader draw it.
Using some specific options of joints you can setup this behaviour, check out the code.
MyNode = class() function MyNode:init(nSegments) touches = 0 parameter.watch("touches") parameter.boolean("detach",false,detachInner) parameter.boolean("elastic",false,changeJoints) self.numSegments = nSegments or 12 self.ptmRatio = math.pi local center = vec2(WIDTH/2,HEIGHT/2) local springness = 4.0 local deltaAngle = (2.0*math.pi)/self.numSegments local radius = 500 self.bodies = {} local cos = math.cos local sin = math.sin for i=0, self.numSegments-1 do local theta = deltaAngle * i local x = radius * cos(theta) local y = radius * sin(theta) self.bodies[i] = self:createCircle( vec2( x/self.ptmRatio + math.random(-16,16), y/self.ptmRatio + math.random(6) ) + center ,37 ) self.bodies[i].info= {origin = vec2(x,y)} end -- inner circle self.inner = self:createCircle(center, 23) -- create joints self.inner.info = { joints = {} } self.inner.type = STATIC self.inner.fixedRotation = true for i=0, self.numSegments-1 do local neighborIndex = math.fmod (i+1, self.numSegments) local currentBody = self.bodies[i] local neighborBody = self.bodies[neighborIndex] currentBody.info.joint = physics.joint( DISTANCE, currentBody,neighborBody, currentBody.worldCenter, neighborBody.worldCenter ) currentBody.info.color = color( math.random(10,80), math.random(20,60), math.random(166,255) ) currentBody.info.joint.frequency = springness currentBody.info.joint.dampingRatio = 0.5 -- connect center body with a joint self.inner.info.joints[i] = physics.joint( DISTANCE, currentBody, self.inner, currentBody.worldCenter, center ) self.inner.info.joints[i].frequency = springness self.inner.info.joints[i].dampingRatio = 0.1 end -- create ground self.ground = physics.body(POLYGON, vec2(0,10), vec2(0,0), vec2(WIDTH,0), vec2(WIDTH,10) ) self.ground.type = STATIC -- touches table self.touches = {} -- mesh self.m = mesh() self.m.shader = shader( [[ uniform mat4 modelViewProjection; attribute vec4 position; attribute vec4 color; varying lowp vec4 vColor; varying mediump vec4 vPos; void main() { //Pass the mesh color to the fragment shader vColor = color; vPos = position; //Multiply the vertex position by our combined transform gl_Position = modelViewProjection * position; } ]], [[ //Default precision qualifier precision highp float; //interpolated vertex color for this fragment varying lowp vec4 vColor; varying mediump vec4 vPos; //Brick variables uniform vec4 brickColor; uniform vec4 mortarColor; uniform float time; uniform vec3 brickSize; uniform vec3 brickPct; void main() { vec3 color; vec3 position, useBrick; position = vPos.xyz / brickSize.xyz; if( fract(position.y * 0.5) > 0.5 ) { position.x += 0.5; position.z += 1.0; } position = fract(position); useBrick = step(position, brickPct.xyz); color = mix(mortarColor.rgb, brickColor.rgb, useBrick.x * useBrick.y * useBrick.z); color *= vColor.rgb; //Set the output color to the texture color gl_FragColor = vec4(color, 1.0); vec2 uv = gl_FragColor.xy/vec3(1.0,1.0,0.0).xy; gl_FragColor = vec4(uv, 0.5 + 0.5 * sin(time), 1.0); } ]]) --self.m.shader = shader("Patterns:Bricks") self.m.shader.brickColor = vec4(0.1,0.3,0.8,1.0) self.m.shader.mortarColor= vec4(0.8,0.8,0.8,1.0) self.m.shader.brickSize = vec3(33,13,23) self.m.shader.brickPct = vec3(0.9,0.85,0.85) self.m.shader.time = ElapsedTime self.bg = readImage("physicsnotebook:background3") end function MyNode:createCircle(pos,r) local circle = physics.body(CIRCLE, r) circle.density = 0.1 circle.restitution = 0.05 circle.friction = 1.0 circle.type = DYNAMIC circle.x = pos.x circle.y = pos.y circle.fixedRotation = true return circle end function MyNode:draw() sprite(self.bg, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT) fill(255) -- draw ground rectMode(CENTER) rect( self.ground.points[1].x/2, self.ground.points[1].y/2, self.ground.points[1].x, self.ground.points[1].y ) strokeWidth(1) lineCapMode(ROUND) ellipseMode(RADIUS) -- draw inner joints for i=0,#self.inner.info.joints do local joint = self.inner.info.joints[i] --[[ line( joint.anchorA.x, joint.anchorA.y, joint.anchorB.x, joint.anchorB.y )]]-- end local vs = {} for i=0, self.numSegments-1 do local body = self.bodies[i] -- draw joint also if body.info.joint then --[[ line( body.info.joint.anchorA.x, body.info.joint.anchorA.y, body.info.joint.anchorB.x, body.info.joint.anchorB.y )]]-- end fill(body.info.color) ellipse(body.x, body.y, body.radius) table.insert(vs, vec2(body.x,body.y)) end fill(0) ellipse(self.inner.x, self.inner.y,self.inner.radius) --table.sort(vs, compareV2) self.m.vertices = triangulate(vs) self.m:setColors(127,127,127,155) self.m.shader.time = ElapsedTime blendMode(MULTIPLY) self.m:draw() blendMode(NORMAL) end function MyNode:isTouchingBody(touch) local tpoint = vec2(touch.x, touch.y) for i=0, self.numSegments-1 do local body = self.bodies[i] if body:testPoint(tpoint) then return body end end return nil end function MyNode:isDraggingKey(key) for i,b in ipairs(self.touches) do if key==b.key then return i end end return -1 end function MyNode:touched(touch) -- check if it is touching a body: local index = self:isDraggingKey(touch.id) if index>-1 then local tpoint = vec2(touch.x,touch.y) if self.touches[index].body then local t = self.touches[index] -- if t.body.info.origin:dist(tpoint)<t.body.radius*12 then if t.origin:dist(tpoint)v2.x and v1.y>v2.y) return v1.x<v2.x and v1.y<v2.y end -- SoftBody node = nil -- Use this function to perform your initial setup function setup() node = MyNode() end -- This function gets called once every frame function draw() -- This sets a dark background color background(40, 40, 50) -- This sets the line thickness strokeWidth(5) -- Do your drawing here node:draw() end function touched(touch) node:touched(touch) end ``` Video: http://www.youtube.com/watch?v=fKIyPqNOwwk