I’ve recently started working on implementing buoyancy to Gerstner waves. My plan is to essentially generate a set of points on whatever object is floating, then look up the height of the wave by plugging the X and Z position to the equation and resolving for Y. From there I will apply force to the object so it stays afloat.
My wave is a mesh part with roughly ~1.4k bones, these bones make up a grid with about 40 studs between each bone. I have figured out how to sample height of any of these bone positions on their own, the issue of I am facing is finding height when the point I wish to lookup is between these points. Each bone has randomly generated X and Z noise that is plugged into the Gerstner equation as direction in order to simulate random water. When calculating height for one of these bones it’s extremely simple as I can just reference the noise table and plug the values in, however I am unsure how I would lookup a point not directly tied to noise.
My plan is to find the 4 closest bones to the point I want to sample as those should always be the positions that make up the square of water the point is contained in, then I can do a weighted average of these 4 bone positions to solve for Y. The problem with this is I can’t figure out how to efficiently find the closest bones. Given I have ~1.4k bones doing something like a for loop to sort through them would be wildly inefficient because it would have to run every frame. I need some sort of spatial sorting algorithm that can find the closest points on a grid.
here is the relevant code:
Height lookup function (I think I honestly don’t need this as if I am able to find the 4 closest bones those will have Y positions):
function Wave:GetHeight(x, z)
local Settings = self._settings
local Direction = Settings.Direction
local worldPos = Vector3.new(x, 0, z)
if Direction == EmptyVector2 then
local noiseValue = self._noise[self._bones["bone that I need to reference noise value for"]] -- Directly use self._noise
if noiseValue then
Direction = Vector2.new(noiseValue.X, noiseValue.Z)
else
warn("Noise value not found. Ensure Update() is called first.")
return 0
end
else
Direction = GetDirection(Settings, worldPos) or Settings.Direction
end
local Y = Gerstner(worldPos, Settings.WaveLength,Direction,Settings.Steepness,Settings.Gravity,self._time).Y
return Y
end
Gerstner wave function:
local function Gerstner(Position: Vector3,Wavelength: number,Direction: Vector2,Steepness: number,Gravity: number,Time: number)
local k = (2 * math.pi) / Wavelength
local a = Steepness/k
local d = Direction.Unit
local c = math.sqrt(Gravity / k)
local f = k * d:Dot(Vector2.new(Position.X,Position.Z)) - c * Time
local cosF = math.cos(f)
--Displacement Vectors
local dX = (d.X * (a * cosF))
local dY = a * math.sin(f)
local dZ = ( d.Y * (a * cosF))
return Vector3.new(dX,dY,dZ)
end
I don’t think any of this code is really needed to solve my problem, I think all I really need is an efficient spatial look up system for grids.