Guest User

Vector Field Particles

a guest
May 17th, 2012
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.37 KB | None | 0 0
  1. --# Flowfield
  2. Flowfield = class()
  3.  
  4. function Flowfield:init(sampleRate)
  5.   self.rate = sampleRate or 50
  6.  
  7.   self.noiseXPos = vec2( math.random(1,100), math.random(1,100) )
  8.   self.noiseYPos = vec2( math.random(1,100), math.random(1,100) )
  9.  
  10.   self.noiseXDir = vec2(0,1):rotate( math.rad( math.random(0,360) ) )
  11.   self.noiseYDir = vec2(0,1):rotate( math.rad( math.random(0,360) ) )
  12.  
  13.   self.speed = 0.01
  14.   self.scale = 1
  15.   self.noiseScale = 1/5
  16.   self.vels = {}
  17.  
  18.   self.sampX = WIDTH/self.rate
  19.   self.sampY = HEIGHT/self.rate
  20.  
  21.   for x = 1,self.sampX do
  22.       self.vels[x] = {}
  23.       for y = 1,self.sampY do
  24.           self.vels[x][y] = vec2(0,0)
  25.       end
  26.   end
  27. end
  28.  
  29. -- Sample the field at x,y using bilinear interpolation
  30. function Flowfield:sample(x,y)
  31.   -- f(x,y) = f(0,0)(1-x)(1-y) + f(1,0)x(1-y) +
  32.   --          f(0,1)(1-x)y + f(1,1)xy
  33.   x = math.max( self.rate, math.min( x, WIDTH - self.rate ) )
  34.   y = math.max( self.rate, math.min( y, HEIGHT - self.rate ) )
  35.  
  36.   local vels = self.vels
  37.  
  38.   local x0 = math.floor( x/self.rate )
  39.   local x1 = x0 + 1
  40.   local y0 = math.floor( y/self.rate )
  41.   local y1 = y0 + 1
  42.  
  43.   local xa = (x/self.rate - x0)
  44.   local ya = (y/self.rate - y0)
  45.   local invXa = 1 - xa
  46.   local invYa = 1 - ya
  47.  
  48.   local f00 = vels[x0][y0]
  49.   local f10 = vels[x1][y0]
  50.   local f01 = vels[x0][y1]
  51.   local f11 = vels[x1][y1]
  52.  
  53.   return (f00 * invXa * invYa) + (f10 * xa * invYa) +
  54.          (f01 * invXa * ya) + (f11 * xa * ya)
  55. end
  56.  
  57. function Flowfield:update()
  58.   -- Move the noise
  59.   self.noiseXPos = self.noiseXPos + (self.noiseXDir * self.speed)
  60.   self.noiseYPos = self.noiseYPos + (self.noiseYDir * self.speed)
  61.  
  62.   -- Rotate the "wind"
  63.   self.noiseXDir = self.noiseXDir:rotate( 0.001 )
  64.   self.noiseYDir = self.noiseYDir:rotate( 0.001 )
  65.  
  66.   -- Compute values at all gridpoints
  67.   local sX = self.sampX
  68.   local sY = self.sampY
  69.  
  70.   local vX, vY
  71.   local vels = self.vels
  72.   local s = self.scale
  73.  
  74.   local noiseScale = self.noiseScale
  75.  
  76.   local nX = self.noiseXPos
  77.   local nY = self.noiseYPos
  78.  
  79.   for x = 1,sX do
  80.       for y = 1,sY do
  81.           vX = noise( nX.x + x * noiseScale,
  82.                       nX.y + y * noiseScale )
  83.           vY = noise( nY.x + y * noiseScale,
  84.                       nY.y + x * noiseScale )
  85.  
  86.           vels[x][y] = vec2(vX, vY)
  87.       end
  88.   end
  89.  
  90.   -- Should probably track one of the velocities
  91.   -- If it exceeds a length of 2 then re-normalize the
  92.   --  entire field
  93. end
  94.  
  95. function Flowfield:draw()
  96.   -- For debugging
  97.   pushStyle()
  98.   noSmooth()
  99.  
  100.   stroke(0,0,0,100)
  101.   strokeWidth(2)
  102.  
  103.   fill(128)
  104.   rectMode(CENTER)
  105.  
  106.    local rate = self.rate
  107.    local sX = self.sampX
  108.    local sY = self.sampY
  109.    local vels = self.vels
  110.    local s = 70
  111.  
  112.   for x = 1,sX do
  113.       local pX = x * rate
  114.       for y = 1,sY do
  115.           local pY = y * rate
  116.           local v = vels[x][y]
  117.           -- local v = self:sample(pX+0.5,pY+0.5)
  118.  
  119.           line( pX, pY, pX + v.x * s, pY + v.y * s )
  120.  
  121.           --rect( pX, pY, 5, 5 )            
  122.       end
  123.   end
  124.  
  125.   popStyle()
  126. end
  127.  
  128. --# Main
  129. supportedOrientations(LANDSCAPE_ANY)            
  130.  
  131. -- Use this function to perform your initial setup
  132. function setup()
  133.    displayMode(FULLSCREEN)
  134.  
  135.    iparameter("FlowVisible", 0, 1, 1)
  136.  
  137.    field = Flowfield(44)
  138.  
  139.    particles = {}
  140.  
  141.    for i = 1,153 do
  142.        table.insert( particles,
  143.                      Particle( vec2( math.random( 30, WIDTH - 30 ),
  144.                                math.random( 30, HEIGHT - 30 ) ) ) )
  145.    end
  146. end
  147.  
  148. function resetParticle(p)
  149.    p.opacity = 0
  150.    p.pos = vec2( math.random( 30, WIDTH - 30 ),
  151.                  math.random( 30, HEIGHT - 30 ) )
  152. end
  153.  
  154. function updateParticle(p)
  155.    p.vel = p.vel * 0.8 + field:sample(p.pos.x,p.pos.y) * 0.8
  156.    p:update()
  157.  
  158.    local pos = p.pos + p.offset
  159.  
  160.    if pos.x < 0 or pos.x > WIDTH or
  161.       pos.y < 0 or pos.y > HEIGHT then
  162.        resetParticle(p)
  163.    end
  164. end
  165.  
  166. function draw()
  167.    -- This sets a dark background color
  168.    background(129, 120, 162, 255)
  169.  
  170.    -- Do your drawing here
  171.    field:update()
  172.  
  173.    if FlowVisible == 1 then
  174.        field:draw()
  175.    end
  176.  
  177.    noSmooth()
  178.    fill(255)
  179.  
  180.    rectMode(CENTER)    
  181.    noStroke()
  182.  
  183.    local angle
  184.    for _,v in pairs(particles) do        
  185.        updateParticle(v)
  186.        v:draw()
  187.    end
  188.  
  189.    --FPS
  190.    textMode(CORNER)
  191.    font("Futura-CondensedMedium")
  192.    fill(0, 40, 55, 100)
  193.    fontSize(34)
  194.    local fps = math.floor(1/DeltaTime)
  195.  
  196.    text("FPS "..fps, 20,35)
  197.  
  198. end
  199.  
  200.  
  201. --# Particle
  202. Particle = class()
  203.  
  204. function Particle:init(pos)
  205.   -- you can accept and set parameters here
  206.   self.offset = vec2( 0,0 )
  207.   self.orientation = math.random(0,360)
  208.   self.twistSpeed = 0
  209.   self.size = math.random(8, 14)
  210.   self.pos = pos or vec2(0,0)
  211.   self.vel = vec2(0,0)
  212.   self.opacity = 0
  213.   self.color = color(250,242,242)
  214. end
  215.  
  216. function Particle:update()
  217.   self.pos = self.pos + self.vel --+ self.fate
  218.   self.orientation = self.orientation + self.twistSpeed
  219.  
  220.   if self.opacity < 1 then
  221.       self.opacity = self.opacity + 0.07
  222.   end
  223. end
  224.  
  225. function Particle:draw()
  226.   pushMatrix()
  227.  
  228.   local os = self.offset
  229.   translate(self.pos.x + os.x, self.pos.y + os.y)
  230.  
  231.   local angle = vec2(0,1):angleBetween( self.vel:normalize() )
  232.   rotate( math.deg(angle) )
  233.  
  234.   local c = self.color
  235.  
  236.   fill(c.r,c.g,c.b,
  237.        math.max( math.min( c.a * self.opacity, 255 ), 0 ) )
  238.  
  239.   rect( 0, 0, self.size, self.size )
  240.  
  241.   popMatrix()
  242. end
Advertisement
Add Comment
Please, Sign In to add comment