Skip to content

Instantly share code, notes, and snippets.

@steveRoll-git
Created September 17, 2022 19:26
Show Gist options
  • Select an option

  • Save steveRoll-git/a1bfb635a4295de72897a57a5d92f20f to your computer and use it in GitHub Desktop.

Select an option

Save steveRoll-git/a1bfb635a4295de72897a57a5d92f20f to your computer and use it in GitHub Desktop.
löve - fluid simulation using verlet integration
local love = love
local lg = love.graphics
local random = love.math.random
local function dist(x1, y1, x2, y2)
return math.sqrt((x2 - x1)^2 + (y2 - y1)^2)
end
local function length(x, y)
return math.sqrt(x^2 + y^2)
end
local function normalize(x, y)
local len = length(x, y)
return x / len, y / len
end
local points = {}
local gravity = 0.4
local bounce = 0.0
local numIterations = 1
local draggingPoint
local dragOffsetX, dragOffsetY
local attracting = false
local attractX, attractY
local fluidImage = lg.newImage("fluid.png")
local fluidShader = lg.newShader [[
vec4 effect(vec4 color, Image texture, vec2 tc, vec2 sc) {
vec4 p = Texel(texture, tc);
p.rgb = color.rgb;
p.a = floor(p.a + 0.2);
return p * color;
}
]]
local fluidCanvas = lg.newCanvas()
local function addPoint(x, y, radius, bouyancy, color)
local p = {x = x, y = y, radius = radius, bouyancy = bouyancy, color = color, oldX = x, oldY = y}
table.insert(points, p)
return p
end
local fluidBalls = {}
for i = 1, 1500 do
local p = addPoint(random(0, lg.getWidth()), random(0, lg.getHeight()), 10, 10, {0.1, 0.5, 0.9})
p.drawRadius = 10 * 3
table.insert(fluidBalls, p)
end
local otherBalls = {}
table.insert(otherBalls, addPoint(lg.getWidth() / 2, 0, 50, 1, {1,1,1}))
function love.update(dt)
attracting = love.mouse.isDown(2)
attractX, attractY = love.mouse.getPosition()
-- update velocities
for i, p in ipairs(points) do
local vx = p.x - p.oldX
local vy = p.y - p.oldY
p.oldX = p.x
p.oldY = p.y
if not p.pin then
p.x = p.oldX + vx
p.y = p.oldY + vy + gravity
if attracting then
local d = dist(p.x, p.y, attractX, attractY)
local dx, dy = attractX - p.x, attractY - p.y
p.x = p.x + dx * 1 / d
p.y = p.y + dy * 1 / d
end
end
end
for i = 1, numIterations do
for _, p1 in ipairs(points) do
for _, p2 in ipairs(points) do
if p1 ~= p2 then
local d = dist(p1.x, p1.y, p2.x, p2.y)
if d < p1.radius + p2.radius then
local totalB = p1.bouyancy + p2.bouyancy
local space = (p1.radius + p2.radius - d) / totalB
local dirX, dirY = normalize(p2.x - p1.x, p2.y - p1.y)
local space1 = space * (p1.bouyancy / totalB)
local space2 = space * (p2.bouyancy / totalB)
p1.x = p1.x - dirX * space1
p1.y = p1.y - dirY * space1
p2.x = p2.x + dirX * space2
p2.y = p2.y + dirY * space2
end
end
end
end
if draggingPoint then
draggingPoint.x = love.mouse.getX() - dragOffsetX
draggingPoint.y = love.mouse.getY() - dragOffsetY
end
end
-- update constraints
for _, p in ipairs(points) do
local vx = p.x - p.oldX
local vy = p.y - p.oldY
if p.y + p.radius > lg.getHeight() then
p.y = lg.getHeight() - p.radius
p.oldY = p.y + vy * bounce
end
if p.x + p.radius > lg.getWidth() then
p.x = lg.getWidth() - p.radius
p.oldX = p.x + vx * bounce
elseif p.x - p.radius < 0 then
p.x = p.radius
p.oldX = p.x + vx * bounce
end
end
end
function love.mousepressed(x, y, b)
if b == 1 then
for i, p in ipairs(points) do
if dist(p.x, p.y, x, y) <= p.radius then
draggingPoint = p
dragOffsetX = x - p.x
dragOffsetY = y - p.y
break
end
end
end
end
function love.mousereleased(x, y, b)
if b == 1 then
draggingPoint = nil
end
end
function love.draw()
for _, p in ipairs(otherBalls) do
lg.setColor(p.color)
lg.circle("fill", p.x, p.y, p.drawRadius or p.radius)
end
lg.setCanvas(fluidCanvas)
lg.clear()
for i, p in ipairs(fluidBalls) do
lg.setColor(p.color)
lg.draw(fluidImage, p.x, p.y, 0, 1, 1, fluidImage:getWidth()/2, fluidImage:getHeight()/2)
--local dx, dy = p.x - p.oldX, p.y - p.oldY
--lg.draw(fluidImage, p.x, p.y, math.atan2(dy, dx), 1 + length(dx, dy) / 10, 1 - length(dx, dy) / 50, fluidImage:getWidth()/2, fluidImage:getHeight()/2)
--lg.circle("fill", p.x, p.y, p.drawRadius or p.radius)
end
lg.setCanvas()
lg.setColor(0.1, 0.7, 0.9)
lg.setShader(fluidShader)
lg.draw(fluidCanvas)
lg.setShader()
end
function love.keypressed(k)
if k == "escape" then
love.event.quit()
end
end
@steveRoll-git
Copy link
Author

And here is fluid.png:
fluid

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment