const AROUND : Array[Vector2i] = [ Vector2i(-1, 0), Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1) ] func compute_water_from(source : Vector2i, heights : PackedFloat64Array, size : Vector2i, required_output : float = 1.) -> PackedFloat64Array: assert(source.x < size.x and source.y < size.y) var cell_count : int = size.x * size.y var water : PackedFloat64Array = PackedFloat64Array() water.resize(cell_count); water.fill(NAN) var sort_func : Callable = func (a : Vector2i, b : Vector2i) -> bool: var ai : int = a.y * size.x + a.x; var bi : int = b.y * size.x + b.x return heights[ai] < heights[bi] var compute : PackedInt32Array = PackedInt32Array() compute.resize(cell_count); compute.fill(-1) var queue : Array[Vector2i] = [ source ] var stack : Array[Vector2i] = [ source ] var id : int = 0 var lowest_output : float = NAN var current_output : float = NAN while not (queue.is_empty() or (is_finite(current_output) and (current_output - lowest_output) >= required_output)): id += 1 var current : Vector2i = queue.pop_front() var idx : int = current.y * size.x + current.x if compute[idx] > 0: continue compute[idx] = id stack.append(current) for a : Vector2i in AROUND: var v : Vector2i = current + a if v.x < 0 or v.y < 0 or v.x == size.x or v.y == size.y: if not is_finite(lowest_output): lowest_output = heights[idx] current_output = lowest_output elif heights[idx] < lowest_output: lowest_output = heights[idx] else: current_output = heights[idx] break var vidx : int = v.y * size.x + v.x if compute[vidx] > 0: continue # Add to queue var i : int = max(0, queue.bsearch_custom(v, sort_func)) queue.insert(i, v) # Resolve water level while not stack.is_empty(): var current : Vector2i = stack.pop_back() var idx : int = current.y * size.y + current.x if is_finite(water[idx]): continue var max_id : int = compute[idx] var water_level : float = heights[idx] queue.clear();queue.append(current) while not queue.is_empty(): var fill : Vector2i = queue.pop_back() var fidx : int = fill.y * size.x + fill.x if is_finite(water[fidx]): continue water[fidx] = water_level for a : Vector2i in AROUND: var t : Vector2i = fill + a if t.x < 0 or t.y < 0 or t.x == size.x or t.y == size.y: continue var tidx : int = t.y * size.x + t.x if compute[tidx] > -1 and compute[tidx] < max_id and heights[tidx] <= water_level: queue.append(t) return water
or share this direct link: