Realistic Oceans Using Mesh Deformation!

Make sure that you use the same exact wave settings

HeHello, could you please tell me how to make the character swim on the waves, I have it moving up and down, but this more or less works when the character is next to a bone.


local Players =  game:GetService("Players")
local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer.PlayerGui
local Backpack = LocalPlayer.Backpack
local Character = LocalPlayer.Character
local HRP = Character:WaitForChild("HumanoidRootPart")
local Hum = Character:WaitForChild("Humanoid")

local Wave3 = game:GetService("Workspace").Wave3

local origPosTable = {}

for i, bone in pairs(Wave3.Plane:GetChildren()) do
    if bone:IsA("Bone") then
        origPosTable[bone] = bone.Position
    end
end

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

game:GetService("RunService").Heartbeat:Connect(function()
    for i, bone in pairs(Wave3.Plane:GetChildren()) do
        if bone:IsA("Bone") then
            local bonePos = GerstnerWave(origPosTable[bone], 120, Vector2.new(1, 0.5), 0.6, 1.5, tick()/8)
            bone.Position = origPosTable[bone] + bonePos

            if (HRP.Position - bone.WorldPosition).Magnitude <= 100 then
                local v = Vector3.new(HRP.Position.X, bone.WorldPosition.Y, HRP.Position.Z)
                Character:MoveTo(v)
            end

        end
    end
end)
1 Like

The texture of the ocean mesh looks too massive/stretched and not realistic

What I am offering is the code for the wave, the texture I used is from another tutorial, and is purely for educational purposes. If you don’t like the texture, change it, I am just open-sourcing the module and an example.

2 Likes

His texture worked just great for me switching it from a surfaceappearence to MaterialVarient.

I switched to materialvarient but then the textures looked repetitive and weird

I don’t seem to have any issues with them? Did you be sure to switch the MaterialPattern to Organic? On regular you will see massive tiling.

3 Likes

Woah it looks cooler now, but i’m not sure if materialservice is live yet

MaterialService went live in-game few days ago.

1 Like

Oh, Great! Thank you for the answer, Although one last thing, If there were multiple planes (10+) will the game lag? even tho there is maximum render distance customization in the script? since the creator of this module said 4 planes may lag? Nvm, i just realized these final questions don’t seem to be answered.

Hello, is there any way of adding object floating and swimming to this “ocean”?
If yes then could somebody help me with it? If no then is there a way of doing this any other way?

does anyone have the function to get the height of the wave since im making a boat that will need this

Is it possible to make this ocean infinite? Or is there any way to make it cover more area?

Im not sure, I could code it if I get the time but I’m pretty busy, however I will tell you how to get cracking on making your own.

The skinned mesh I provided contains a ton of squares that each corner connects to a bone. You can get the position on the wave of the height you want to get, get the four bones that the point is inside of, and calculate the height based off of the four positions of the bones.

2 Likes

What you could do is create like 4 or more of these oceans, and as the player moves in a direction, move the fastest wave planes in front of where the player is moving, giving it a chunk loading effect but you really are just moving waves. You do not want to update wave frames every frame to the character of camera position, as that will not move the texture with it (unless there is a way to do skinned mesh texture offset, I can’t remember as I am using the demo wave from another tutorial I linked in the OP).

1 Like

Is there a way to sync the waves with the server? Not render in on the server, but If i were to name a position with a specific wave instance or something then it will give the height of the wave at that point(trying to implement buoyancy)

1 Like

Hi
The way I did it was by making the scripts that calculate waveheight a shared module.
Keep in mind that the time component in the Gerstner formula should be synced as precisely as possible between the server and the clients (with of course the server as authority).

This way you could update bones on each client (which would be an intensive calculation for the server), and make server-owned objects float using simpler calculations on the server.

An important parmeter here is server-owned! Use this piece of code on the object you want to float: partToFloat:SetNetworkOwner(nil)

I don’t think you understand, what I want to do is be able to input a vector2 of x and z and get a Y value out of that(the top of the wave at that specific point)

1 Like

For anyone wondering how to get the exact height of a gerstner wave at a specific position, you have to use an iterative function that backtracks to get the height at the desired position.

13:21 of this video provides a perfect visual

It is likely the most performant method, and doesn’t take an octtree or other shenanigans.

The only downside (maybe) is that it doesn’t get the visual height, but the actual height of the function at that point. It would essentially be the height if your mesh had infinite bones.

2 Likes

Sorry for the late reply, but I believe I understood your question correctly. I explained how you would make a server part float (in broad terms). You should be able to implement this on your own using some of the tips I gave.
Sorry for the unclear explanation, but I can’t give away the scripts. I hope you understand.

As @WreckDough pointed out you can only get an approximation of the height from an xz-pos. I found that running the position twice through the bone offset function already produced a good enough result for my usecase.

1 Like