Where do I even start with a Gerstner wave floating system?

That’s fine! The first step is to organize them.

You don’t need to do this manually, we can automate this.

For example, we could:

  1. Put the bones into a single array. local bones = someModel:GetChildren()
  2. Sort them on one axis: table.sort(bones, function(a, b) return a.Position.Z < b.Position.Z end)
  3. Split the resulting table into chunks based on how many per row there are (you hardcode that number)
  4. Sort the subtables.

If we put all that together:

local function Gridify(objects, cols: number)
	-- first sort top to bottom
	table.sort(objects, function(a, b) return a.Position.Z < b.Position.Z end)
	
	local grid = {}	
	
	-- then, split up into groups
	for row = 1, #objects / cols do
		-- the first object in the row
		local lowerIndex = 1 + (row - 1) * cols
		
		-- the last object in the row
		local upperIndex = row * cols
		
		-- copy this row into its own table..
		local thisRow = {}
		table.move(objects, lowerIndex, upperIndex, 1, thisRow)
		
		-- ... so we can sort it based on position
		table.sort(thisRow, function(a, b) return a.Position.X < b.Position.X end)
		grid[row] = thisRow
	end
	
	return grid
end

With this function, say we did local grid = Gridify(bonesParent:GetChildren()).

Then grid[7][12] would give us the Bone instance in row 7, column 12.

For instance, I tried this on a grid of parts. I’m showing the (row, column) positions that this function calculated in this image:

image

So we’ve succesfully organized the bones into a grid. The next question is: how are the triangles connected in this grid? Does it look like this?

+---+---+---+---+---+
|  /|  /|  /|  /|  /|
| / | / | / | / | / |
|/  |/  |/  |/  |/  |
+---+---+---+---+---+
|  /|  /|  /|  /|  /|
| / | / | / | / | / |
|/  |/  |/  |/  |/  |
+---+---+---+---+---+

Or this?

+---+---+---+---+---+
|\  |\  |\  |\  |\  |
| \ | \ | \ | \ | \ |
|  \|  \|  \|  \|  \|
+---+---+---+---+---+
|\  |\  |\  |\  |\  |
| \ | \ | \ | \ | \ |
|  \|  \|  \|  \|  \|
+---+---+---+---+---+

… or maybe this?

+---+---+---+---+---+
|  /|  /|  /|  /|  /|
| / | / | / | / | / |
|/  |/  |/  |/  |/  |
+---+---+---+---+---+
|\  |\  |\  |\  |\  |
| \ | \ | \ | \ | \ |
|  \|  \|  \|  \|  \|
+---+---+---+---+---+
1 Like

Before I go further I just want to reiterate what the problem is.

We have a grid of triangles. Those triangles stretch between points on a regular grid.

We have a position.

We want to figure out what triangle we’re on, so that we can figure out exactly what height we’re at at this specific position.

  1. We could just raycast up or down and see where we hit the ocean, but since this is a mesh part, we’d be relying on Roblox’s mesh decomposition and physics for that. Seems buggy.
  2. We could loop through every Bone and find the three closest to us, but then we’d be looping through every vertex every frame. We can do this in constant time if we’re smarter about it.
  3. Instead, we can calculate which grid position we’re at to figure out what box we’re in, and then we can figure out which of the two triangles in that box that we’re in.
1 Like

Wow, That’s very detailed reply thanks! I’m having some issues with sorting…

I am executing this in the command bar and nothing is happening. Is there something obvious i am missing? Do i need to specify the starting bone or something? Or say that i have 32 in a column? Do i need to do something after local thisRow = {}?

local function Gridify(bones, cols: number)
	
	local bones = game.Workspace.Ocean.Plane:GetChildren()
	
	local grid = Gridify(game.Workspace.Ocean.Plane:GetChildren())
	table.sort(bones, function(a, b) return a.Position.Z < b.Position.Z end)

	
	


	for row = 1, #bones / cols do

		local lowerIndex = 1 + (row - 1) * cols


		local upperIndex = row * cols


		local thisRow = {}
		table.move(bones, lowerIndex, upperIndex, 1, thisRow)
		
		
		table.sort(thisRow, function(a, b) return a.Position.X < b.Position.X end)
		grid[row] = thisRow
	end

	return grid
end

Its a function, so you have to call it with some arguments.

You give it a list of bones (probably with MeshPart:GetChildren()), and the number of columns of bones you have (I’m assuming a “column” runs parallel to the Z axis here).

It returns an array of rows, where a “row” is itself an array of Bones in order from left (-X) to right (+X).

All that’s to say you could print out the result of the function in the command line to get an idea of what it’s doing:

local grid = Gridify(workspace.MeshPart:GetChildren(), 32)
print(grid)
1 Like

The sorting works! So my plane looks like its made of just a grid of squares… Not triangles.

what it looks like:

+----+----+----+----+----+----+      
|    |    |    |    |    |    |
+----+----+----+----+----+----+
|    |    |    |    |    |    |
+----+----+----+----+----+----+
|    |    |    |    |    |    |
+----+----+----+----+----+----+
|    |    |    |    |    |    |
+----+----+----+----+----+----+
|    |    |    |    |    |    |
+----+----+----+----+----+----+
|    |    |    |    |    |    |
+----+----+----+----+----+----+

So, they really are made of triangles, even if your mesh isn’t.

All meshes are rendered as a bunch of triangles, because triangles can form any shape.

I don’t know who is converting it to triangles—could be the importer tool, could be roblox mesh upload servers, could be the GPU.

Anyways, you want to know for sure what your triangles look like, so I would triangulate your plane in blender before importing. I think you can just highlight the faces in edit mode and press Ctrl+T.

Edit: could you actually upload the imported plane model you’re using? I can’t get my fbx bones to import correctly.

1 Like

Alright I made the mesh triangles. Thanks for the tip :+1:

Here is the FBX file: roblox-ocean-FBX.zip (336.0 KB) (Its inside the zip…) (edit: You need to import like this: Plugins/AvatarImporter/Custom… Choose the fbx file)

Also the mesh looks like this:

+---+---+---+---+---+
|\  |\  |\  |\  |\  |
| \ | \ | \ | \ | \ |
|  \|  \|  \|  \|  \|
+---+---+---+---+---+
|\  |\  |\  |\  |\  |
| \ | \ | \ | \ | \ |
|  \|  \|  \|  \|  \|
+---+---+---+---+---+
1 Like

Out of the three options you suggested I think this will suit me best:

I fill like this will be the best performant. Can you explain how to do this (now that we have the grid System, Triangles and such) Again so sorry for so many questions im still learning :joy:

The problem with this was the wavelength was too short for my mesh
I Haven’t come back to this in a while but, if my theory is correct, make sure that the wavelength is half the distance between two bones.
because the wavelength was too short, there’d be waves that werent showing up, creating a fractal like effect. (You can see this in the gifs in my post you linked)
you could probably get this code working if you tuned the wavelength properly.
also the code has to be adjusted to be in a loop to go through all the bones, don’t put a script under each bone, but I think you know that.
(also make sure the bones are alligned within a grid)

Hey, thank you so much! Ill definitely give this try. However I might go with another method like @nicemike40 posted just because it might look and perform better. Quick question: In the method that you created is there away to easily add* some rotation? I mean like the boat/(floatingPart) going up and down the wave and rocking/tilting up/down.

I was trying to figure out floatation in my last post and found out the flaw with my setup (the one I mentioned above)
if you had, say a boat, and you wanted it to rotate, then you could sample four points around the boat, and generate the rotation with that. You’d have to come up with a way to determine the rotation of the boat from those points, which is what I haven’t figured out yet.

Ok i might mess around with it a little later. Thanks! :+1: (Probably wont be able to respond to any more reply’s until 3:00 CDT)

goodluck
I’ll be getting to water soon in my own project too, so if I get a working version I’ll condense it down into a module and post it publicly.

1 Like

How would i do this:

  1. Instead, we can calculate which grid position we’re at to figure out what box we’re in, and then we can figure out which of the two triangles in that box that we’re in.

Sorry for not being very responsive, it’s the weekend! :slight_smile:

I can go into more detail later but:

  • my first attempt at solving this was slightly wrong. We still need to figure out the grid (so that we can easily lookup which vertices are next to which others), but the part you quoted won’t work
  • this is because gerstner waves can move vertices side to side as well as up/down. So a simple “what grid square am I in” won’t really work
  • I’ll have to think about how to solve it. Maybe some octree based solution like @tyridge77 did
  • if you just want a quick and easy solution I would do what @TheCursedSpud suggested and make a few vertical raycasts to sample the slope easily. Raycasts aren’t too expensive

Sadly raycasts don’t work on deformable meshes in roblox at the moment.
You’d have to generate the info on whether or whether not your point is below the water, and how far below it is, etc.

1 Like

Good that means all this work is at least necessary :slight_smile:

What would be the best option then?

so I started work on the module, Wave(alphav1).rbxm (18.1 KB)
is my current version of it, you use it by adding a localscript somewhere, requiring it, then calling the ClientInit function. You also have to set the objectvalue “WaterFolder” to a folder in workspace. (the value is parented to the module)

Wave:ClientInit({RenderDistance = 1, WaterHeight = 0, Gravity = 0.5, Steepness = 0.3})

these are the settings I am using for it atm, they work just fine.
-note- don’t make RenderDistance higher than 1 unless you want to experience pain.
current problems with the module:
→ poorly optimized
→ seams appear between waterplanes
→ my loading system for the waterplanes (which is supposed to make a square of them around you) sometimes doesn’t put you at the middle of the square
→ currently don’t know how to make the wavedirection change dynamically with my current setup.
I’ll be pushing forward with this, but I have no idea why there are seams and the loading system messing up.

1 Like