Created
June 6, 2021 05:48
-
-
Save atonamy/0f0b9bac89e152f6dcbe70f3f411a42d to your computer and use it in GitHub Desktop.
2D Metaballs implementation in Godot
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
#based on this article http://jamie-wong.com/2014/08/19/metaballs-and-marching-squares/ | |
extends Node2D | |
class Blob: | |
var pos_x | |
var pos_y | |
var radius | |
var velocity | |
func _init(x, y, r, v): | |
pos_x = x | |
pos_y = y | |
radius = r | |
velocity = v | |
const cell_size = 16 | |
const blobs_count = 10 | |
const blob_size = [20, 40] | |
var screen_size | |
var blobs | |
var allowUpdate = true | |
#var font = DynamicFont.new() | |
const drawMap = { | |
0: null, | |
1: [-0.5, 0, 0, -0.5], | |
2: [-0.5, -1, 0, -0.5], | |
3: [-0.5, 0, -0.5, -1], | |
4: [-1, -0.5, -0.5, -1], | |
5: [-1, -0.5, -0.5, 0, -0.5, -1, 0, -0.5], | |
6: [-1, -0.5, 0, -0.5], | |
7: [-1, -0.5, -0.5, 0], | |
8: [-1, -0.5, -0.5, 0], | |
9: [-1, -0.5, 0, -0.5], | |
10: [-1, -0.5, -0.5, -1, -0.5, 0, 0, -0.5], | |
11: [-1, -0.5, -0.5, -1], | |
12: [-0.5, -1, -0.5, 0], | |
13: [-0.5, -1, 0, -0.5], | |
14: [-0.5, 0, 0, -0.5], | |
15: null | |
} | |
func calcIsoSurface(x1, x2, y1, y2, r): | |
var dx = abs(x1 - x2) | |
var dy = abs(y1 - y2) | |
var sd = dx*dx + dy*dy | |
var res = float(r*r) / float(sd) | |
return res | |
# Called when the node enters the scene tree for the first time. | |
func _ready(): | |
#font.font_data = load("res://font.ttf") | |
#font.size = 7 | |
screen_size = get_viewport().size | |
blobs = Array() | |
var rng = RandomNumberGenerator.new() | |
var r = rng.randi_range(blob_size[0], blob_size[1]) | |
var x = rng.randi_range(r, screen_size.x - r) | |
var y = rng.randi_range(r, screen_size.y - r) | |
for n in range(blobs_count): | |
blobs.push_back( | |
Blob.new( | |
x, | |
y, | |
r, | |
Vector2(rng.randf_range(-3, 3), rng.randf_range(-3, 3)) | |
) | |
) | |
print(screen_size) | |
func formDrawIndex(x, y, sum, vertexes): | |
var drawIndex = 0 | |
var corners = [] | |
if x > 0 && y > 0: | |
var vertex = vertexes.pop_front() | |
if sum >= 1: | |
drawIndex |= 1 | |
if vertexes.back() >= 1: | |
drawIndex |= 2 | |
if vertex >= 1: | |
drawIndex |= 4 | |
if vertexes.front() >= 1: | |
drawIndex |= 8 | |
corners.push_back(sum) | |
corners.push_back(vertexes.back()) | |
corners.push_back(vertex) | |
corners.push_back(vertexes.front()) | |
return {"draw_index": drawIndex, "corners": corners} | |
func exLerp(oneSum, zeroSum): | |
if oneSum == zeroSum: | |
return null | |
return -(1-((1 - oneSum) / (zeroSum - oneSum))) | |
func interpolateLines(lines, corners): | |
if lines == null: | |
return lines | |
for i in range(0, lines.size(), 2): | |
var x = lines[i] | |
var y = lines[i+1] | |
if (x == 0 || x == -1) && (y == 0 || y == -1): | |
continue | |
if (x == 0 || x == -1): | |
lines[i+1] = exLerp(corners[1], corners[0]) if x == 0 else exLerp(corners[2], corners[3]) | |
if (y == 0 || y == -1): | |
lines[i] = exLerp(corners[3], corners[0]) if y == 0 else exLerp(corners[2], corners[1]) | |
return lines | |
func drawLines(x, y, lines): | |
if lines != null && lines.size() >= 4: | |
draw_line( | |
Vector2(x + (cell_size*lines[0]), y + (cell_size*lines[1])), | |
Vector2(x + (cell_size*lines[2]), y + (cell_size*lines[3])), | |
Color.green | |
) | |
if lines != null && lines.size() == 8: | |
draw_line( | |
Vector2(x + (cell_size*lines[4]), y + (cell_size*lines[5])), | |
Vector2(x + (cell_size*lines[6]), y + (cell_size*lines[7])), | |
Color.green | |
) | |
# Called after update() in the _process() | |
func _draw(): | |
var vertexes = [] | |
for x in range(0, screen_size.x, cell_size): | |
for y in range(0, screen_size.y, cell_size): | |
var sum = 0 | |
for blob in blobs: | |
sum += calcIsoSurface(x, blob.pos_x, y, blob.pos_y, blob.radius) | |
#var c = Color.blue | |
#c.a = 0.0001 | |
#draw_circle(Vector2(blob.pos_x,blob.pos_y), blob.radius, c) | |
#if sum >= 1: | |
# draw_rect(Rect2(x, y, 1, 1), Color.red) | |
#else: | |
# draw_rect(Rect2(x, y, 1, 1), Color.black) | |
#draw_string(font, Vector2(x, y), "%s" % sum, Color.black) | |
var indexies = formDrawIndex(x, y, sum, vertexes) | |
var lines = drawMap[indexies["draw_index"]] | |
var corners = indexies["corners"] | |
lines = interpolateLines(lines, corners) | |
drawLines(x, y, lines) | |
vertexes.push_back(sum) | |
if x > 0: | |
vertexes.pop_front() | |
func _input(event): | |
if event is InputEventMouseButton && event.is_pressed(): | |
allowUpdate = !allowUpdate | |
print(allowUpdate) | |
# Called every frame. 'delta' is the elapsed time since the previous frame. | |
func _process(delta): | |
if !allowUpdate: | |
return | |
update() | |
for blob in blobs: | |
blob.pos_x += blob.velocity.x | |
blob.pos_y += blob.velocity.y | |
if blob.pos_x > screen_size.x || blob.pos_x < 0: | |
blob.velocity.x *= -1 | |
if blob.pos_y > screen_size.y || blob.pos_y < 0: | |
blob.velocity.y *= -1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment