Help with water physics

I’ve got a main loop running 10 times a second like so:

while task.wait(.1) do
	local deltaTime = os.clock() - l_os; l_os = os.clock()
	task.desynchronize() calculate_vectors(1)
	task.synchronize() perform_velocity(1)
end

Functions perform_velocity and calculate_vectors each take a number as a modifier, usually it would be the deltaTime, which would be around 0.1, but for experimental purposes, it is set to one to speed up the proccess.

calculate_vectors calculate the height difference between a pixel on the map and its surrounding pixels and adds a value to the pixel’s velocity
perform_velocity adds the velocity to the water pixel, calculating how much water is lost in the pixel.

Although for whatever reasons, once I place a water pixel it doesn’t spread out like I intend.

Full source code:
local rootFloor = workspace.RootGround

local entities = workspace.MapInstances
local fol_land = entities.segments.land
local fol_water = entities.segments.water

type water_object = 
	{
		['c']: number,
		['m']: number,
		['i']: number
	}

type land_object = 
	{
		['m']: number,
		['d']: number,
		['i']: number,
	}

--[[
ca: cached
ld: land data
wd: water data
w_obj: water objects
l_obj: land objects
v2: Vector2
bw_preset: base water preset
bl_preset: base land preset
--]]

local ca_wd : {[number]:water_object} = {}
local ca_ld : {[number]:land_object} = {}
local cached_vectors : {[number]: Vector2}= {}

local w_obj : {[number]: Part} = {}
local l_obj : {[number]: Part} = {}

local assembly : {{w:water_object, n:number, wo: water_object}} = {}

local clamp = math.clamp
local v2new = Vector2.new
local v2unpack = function(v:Vector2) return v.X, v.Y end
local v2clamp = function(v:Vector2, min, max) return v2new(clamp(v.X, min, max), clamp(v.X, min, max)) end


local def_x, def_y = 100,100

local function to_cord(index:number): (number, number)
	local x = index % def_x
	local y = bit32.bor(index / def_x)
	return x, y
end
local function to_int(x, y)
	return y * def_x + x
end

local landcol, watercol = Color3.fromRGB(51, 65, 63), Color3.fromRGB(19, 39, 127)


local corres = function(x, y)
	return 
		{
			[Vector2.xAxis]=y * def_x + x + 1,
			[-Vector2.xAxis]= y * def_x + math.min(x-1,0),
			[Vector2.yAxis]=(y+1) * def_x + x,
			[-Vector2.yAxis]=(math.min(y-1,0)) * def_x + x
		}
end

local function construct_cache()
	for x=0, def_x do
		task.wait()
		for y=0, def_y do
			local i = y * def_x + x
			cached_vectors[i] = Vector2.zero
			ca_ld[i] = 
				{
					['m'] = 1,
					['d'] = 1,
					['i'] = i
				}
			ca_wd[i] = 
				{
					['m'] = 0,
					['c'] = 0,
					['i'] = i
				}
		end end end
local function construct_land(f:(number, number)-> number)
	for x=0, def_x do
		task.wait()
		for y=0, def_y do
			local h = f(x,y)
			local i = y * def_x + x
			local earth_pos = Vector3.new(
				(x - def_x/2),
				h/2,
				(y - def_y/2))
			
			local earthPart = Instance.new('Part')
			earthPart.Anchored = true
			earthPart.Parent = fol_land
			earthPart.Position = earth_pos
			earthPart.Size = Vector3.one + Vector3.yAxis * h
			earthPart.CastShadow = false
			earthPart.Color = Color3.new(.4,.4,.4)
			l_obj[i] = earthPart
			ca_ld[i].m = h
		end
	end
end


local function place_water(index:number, amount: number)
	local waterPart = Instance.new('Part')
	local sel_ld = ca_ld[index]
	local x, y = to_cord(index)
	waterPart.Anchored = true
	waterPart.CastShadow = false
	waterPart.Parent = fol_water
	waterPart.Size = Vector3.new(1, amount, 1)
	waterPart.Position = 
		Vector3.new(
			x - def_x / 2,
			sel_ld.m + amount / 2,
			y - def_y / 2
		)
	waterPart.Color = Color3.new(.1,.1,1)
	
	w_obj[index] = waterPart
	ca_wd[index] = {m = amount, c = 0, i = index}
	return waterPart
end
local function modify_w(i:number, n:number, l:boolean?)
	local w_data = ca_wd[i] if not w_data then return end
	local part = w_obj[i] if not part then place_water(i, n) return end
	if l then
		w_data.m += n
		part.Position += Vector3.yAxis * n / 2
		part.Size += Vector3.yAxis * n
	else
		local dif = n - w_data.m
		part.Position += Vector3.yAxis * n / 2
		part.Size += Vector3.yAxis * n
	end
end

local function modify_l(i:number, n:number, l:boolean?)
	local l_data = ca_wd[i] if not l_data then return end
	local dif = n - l_data.m if l then dif = n end
	local part = l_obj[i] 
	l_data.m += n
	part.Position += Vector3.yAxis * dif/2
	part.Size += Vector3.yAxis * dif
	if w_obj[i] then modify_w(i, dif, true) end
end
local function calculate_vectors(mod)
	for i, wd in ca_wd do
		if not w_obj[i] then return end
		local ld = ca_ld[i]
		for dif, co in pairs(corres(to_cord(i))) do
			if clamp(co, 0, def_x*def_y) ~= co then return end
			local co_d = ca_wd[co]
			local hdif = (wd.m + ld.m) - (co_d.m + ca_ld[co].m)
			if hdif <= 0 then return end
			cached_vectors[i] += dif * (hdif ^ .77) * mod
			print("cached vector: "..wd.i.." to "..co)
			table.insert(assembly, {wo=wd, w = co_d, n = hdif})
	end
	end
	end

local function perform_velocity(mod)
	for i, t in pairs(assembly) do
		modify_w(t.wo.i, -t.n*mod, true)
		modify_w(t.w, t.n*mod, true)
		table.remove(assembly,1)
		print(t.wo.i.." transferred "..t.n*mod.." to "..t.w.i)
	end
end

local l_os = os.clock()

construct_cache()
construct_land(function(x,y)
	return 90 - (clamp(math.noise(x/10+.5,y/10+.5), -1, 1) + 1) / 2 * 45 * (math.sin(x/12.5)+1)
end)
place_water(math.random(0,def_x*def_y), 55)

while task.wait(.1) do
	local deltaTime = os.clock() - l_os; l_os = os.clock()
	task.desynchronize() calculate_vectors(1)
	task.synchronize() perform_velocity(1)
end

image

image

1 Like

bumping post, still haven’t found a solution

1 Like

bumping yet again, still havent figured it out

1 Like

bumping again :sob: please someone help :pray:

This code is very hard to read, no offense. As I understand, the velocity is stored in a table? If you could send a video of the script running I might be able to help.

The script just loads the terrain and places a water particle, but it does not move anything else post that. The image is in the original thread

I am aware, but I’m looking at the functions calculatevectors() and performvelocity() and the variable names are pretty confusing. Is it possible you could rewrite/define these in a more understandable way so I can check the logic?