Realistic Oceans Using Mesh Deformation!

oh yeah, thanks ill go and try it right away.

you may want to add that to the documentation aswell.

2 Likes

How did you get the bones to be all parented to the plane?


The bones from my mesh are like this and I don’t know how to parent them all to the plane without glitching out the mesh.
image
Nvm I solved this.

Blender isn’t totally my thing, so I’m not sure if I would be of much help.

Check out this tutorial here and if you still have the problem, try asking it there.

1 Like

Question, very late

Have you gotten around to make this work completely or not? I´m looking for a solution to bone collission within oceans/seas which are animated

That will be very difficult to do, as Roblox has not added support for collision detection with skinned meshes.

Collision detection isn’t necessarily needed, it can be calculated without it

Theres multiple videos on it just look up “gerstner waves roblox”

Take the object you’d want as a sort of “boyant/boyancy block”

Great tutorial imo, thanks a ton!

2 Likes

I did some research thru APIs and the ¨Skinned MeshParts are live!¨ post and came across this API:

¨ TransformedWorldCFrame¨
Describes the combined CFrame offset of the bone and the current animation offset in world space.

This could help with collision, possibly

Another way I´ve seen before is create triangles on the gerstner waves, which follow the same wave path to detect collision way easier (but more performance impact probably)

Along with that I came across this example:

This is probably the closest we can get to collision detection for skinned meshes, for now.

That’s some good research right there. However, unless there is an official implementation, I don’t plan on updating the module with collisions. I don’t believe it will exactly be performant in real situations, but it’s always worth a shot!

1 Like

Is it possible to detect if a player/camera is inside the water?

1 Like

@iamtryingtofindname
Is there any way to condense your expansive and useful module into a single function that takes a vector 3 and returns where that vector 3 should be at that returned time?

-- call a function and pass the origin point before the translation (and perhaps the settings?)
Wave.GerstnerPoint(Vector3)
-- expected return is the vector3 post-translation 
x, y, z

This has to be possible and simple (but math and coding aren’t my areas of expertise)
^ Any arbitrary bone can be translated by the gerstner function you have, as long as it exists in the workspace, therefore any arbitrary point should have its own translation. This is obvious, I think.

I tried a couple methods and all failed, however, the method that made the most headway was:
Putting all necessary functions in a serverscript for the individual floating part
Taking the part’s position as the vector3 to be translated
Placing the translated vector3 as the bodyposition’s position

The one problem I faced was that I could not correctly isolate and carry over your perlin noise function.

Yeah sure I can add that real quick

1 Like

Lemme know if it works. Download the updated module:

Source:

-- Added per request of @Desired_Knowledge
function Wave:GerstnerPoint(point: Vector3)
	local WorldPos = point
	local Settings = self._settings
	local Direction = Settings.Direction

	if Direction == EmptyVector2 then
		-- Use Perlin Noise
		Direction = Vector2.new(math_noise(WorldPos.X/NoiseModifier,WorldPos.Z/NoiseModifier,1),math_noise(WorldPos.X/NoiseModifier,WorldPos.Z/NoiseModifier,0))
	else
		Direction = GetDirection(Settings,WorldPos)
	end
	
	local transform = newCFrame(Gerstner(WorldPos,Settings.WaveLength,Direction,Settings.Steepness,Settings.Gravity,self._time))
	
	return transform, point+transform
end

Just realized I never hit reply lol

1 Like

It works properly, but its not synced with the waves. It doesn’t seem to be the same wave at all.

Now, I did make edits to the module to have the plane anchor to the HumanoidRootPart and keep the points where they should be, had the plane never moved. I move the plane, and subtract the humanoidrootpart’s x/z position from the worldposition of the bones. In this way, the water is basically infinite. There must be some issue in the way I did that though.

I’m trying to have an infinite ocean that uses only 1 plane and also has waves that are pretty synced from client to server to clients. I asked about the point method so I could add swimming and floating physics, as well as a check for if your camera is under water.

If that’s an edit you can walk me through, or make altogether (as long as it isn’t asking too much), that would be extremely appreciated and helpful. I can commission you, otherwise.

The problem with Gerstner waves is that there is no mathematically correct way to get the waveheight at a given xz-position.

The best you can do is use approximations. What I found by researching the problem is that you can achieve a pretty good result by plugging the wave formula in twice. As in you first calculate one offset of the position, then pass that new position in the formula again to extract the height.

How would I do that using these above methods?

Something like this:

function getHeight(XZPos)
	local w = computeDisplacement(XZPos)
	local correctedXZPos = Vector2.new(XZPos.X + w.X, XZPos.Y + w.Z)
	return computeDisplacement(correctedXZPos).Y
end

computeDisplacement() is a function that takes in an XZ-coordinate and returns the offset on that point (Vector3).

I got some version of it working, but my issue now is that the points used are the object’s origin points. That means I can’t have the items move around the x/z plane outside of the gerstner’s movement. If I allow them to move, then they aren’t in sync with the wave plane anymore. That’s the issue with swimming, camera underwater check, and boat floating.

This seems like a weird issue.

The Gerstner formula should work for any point up to infinity. Are you referencing the bones somehow? Could you show the relevant code pieces?

Well, I wasn’t entirely clear in my explanation. The objects float with a gerstner wave pattern and it roughly matches the waves (this could be an issue with my client server synchrony method).

These objects are stationary and are not dynamic. Think of them like buoys that just bob around their origin like bones would.

My problem arises when I have dynamic objects (that don’t really follow the x/z aspect of the gerstner). Say, for instance:

  • a barrel that is pushed away from that origin point
  • a character that does not originate from an origin point on the water and swims freely
  • the player’s character when going under the y height of the wave at its present x/z position.

As for the code sample, I actually deleted what I had for the floating objects. It was practically the gerstner function for the bones with some changes so it would put the return in a bodyposition.


Gerstner formula function

function GerstnerWave(SamplePosition,Wavelength,Direction,Steepness,Gravity,SampleTick)
	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(SamplePosition.X,SamplePosition.Z)) - c * SampleTick
	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

point transform (takes a vector3 argument)

function gerstnertransform(v)
	local WorldPos = v
	local Wave1 = GerstnerWave(WorldPos,100,Vector2.new(.5,0),.14,3,SyncModule:Time()*timemodifier*1)
	local Wave2 = GerstnerWave(WorldPos,120,Vector2.new(.8,.2),.07,15,SyncModule:Time()*timemodifier*0.6)
	local Wave3 = GerstnerWave(WorldPos,300,Vector2.new(.7,1),.04,15,SyncModule:Time()*timemodifier*0.3)
	local TotalDisplacement = Wave1+Wave2+Wave3
	return TotalDisplacement
end