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

Hey thank you! Option 2 works but… Its kind of out of sync and stuff. So if I’m going to do this I should probably take the time to just try Option 1. One question: how would I find what Bones the Floating part is above in the first place? Again sorry for so many questions mesh deformation and working with bones is all new to me.

Well, that depends.

Are your bones organized in any way? I mean, if I asked you to give me the bone in row 7, column 12, could you tell me easily?

Or are they all just named “Bone” and tossed into the same parent?

If the latter—are they at least evenly spaced in a grid?

They are all evenly spaced and numbered.

How are they numbered? Are they named with their row and column? Are they at least named in spatial order (e.g. left to right, top to bottom)?

So… No they are not ordered under columns but just numbered under the Plane itself.
I just realized they are kinda random???
Although they are numbered as far as Can tell they are random… You would think bone 1 would be closest to 0 right? well not in my case. The nearest bone to 0 is 35. The bones appear to start on the x axis from right to left.

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.