Created
September 17, 2022 19:26
-
-
Save steveRoll-git/a1bfb635a4295de72897a57a5d92f20f to your computer and use it in GitHub Desktop.
löve - fluid simulation using verlet integration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And here is

fluid.png: