About
A few days ago I saw a post on the devforum about an endless PBR Skinned Mesh ocean so I decided to try and research the topic further since it seemed like a fun challenge and after a few sleepless nights I am finally ready to release it.
Features
-
High performance (~0.05 ms with 9 planes (530 bones each))
-
Synchronized between clients using “GetServerTimeNow”
-
A fast and accurate height lookup function for any XZ coordinate
-
Built-in floatables support for stuff like boats, ships etc.
-
Automatic bone scaling at a distance so it smoothly blends into any bones outside of render distance
-
Customizable performance settings (render distance, floatables render distance, floatables resolution drop off distance*)
“floatables resolution drop off distance” is the distance at which the system will automatically switch to only using the center point of a floatable object for height lookup
So what does it look like ?
Glad you asked! Here are some videos + performance benchmarks:
6 boats with 2 lookup points each (~1.1 ms total simulation time)
12 boats (~1.7 ms total simulation time)
Tutorial on creating floatables
Creating a floatable object is pretty simple you just need a platform with two attachment (front and back which will be used to determine the height) and a body.
The platform is anchored and doesn’t move it’s basically the HumanoidRootPart of a character (also the PrimaryPart of the main model), it is used to position the body in the world and it is required that you keep it at the same height as your ocean.
The body is what contains your boat/ship whatever you want to use, it must have a part named Primary and it’s PrimaryPart must be set to it.
And that’s pretty much how you set one up, you can also remove the attachments if you want the height lookup to be performed at the center of the Platform.
API
WaveModule.Enable(IsEnabled: boolean) (Client Only)
Toggles the simulation
WaveModule.SetSetting(Setting: string, Value: number | boolean)
List of available settings:
- Debug (boolean) - Debug prints (grid creation, simulation time etc.)
- RenderDistance (number) - The maximum render distance
- FloatablesRenderDistance (number) - The maximum distance for floatables simulation
- FloatablesDropOffDistance (number) - The distance at which the module changes to only using the center of the floatable for height lookup
WaveModule.GetHeightAtXZ(Position: Vector2 | Vector3, CameraOrigin: Vector3) -> Height: number
Get the height of the ocean at XZ coordinates
Disclaimer: If you pass in position as a vector3 value it will be read as
X = Position.X
Y = Position.Z
Note: CameraOrigin is used on the server to simulate the scaling of bones at a distance which is present on the client, if you don’t provide it the calculation will be done with all bones at 0 distance from the ‘camera’
Download
You can do whatever you want with this resource, I do ask that you credit me but I don’t mind if you choose not to.
Skinned mesh ocean system.rbxl (184.6 KB)
Technical stuff
This part is for those of you interested in the inner workings of the module and how some of the features are achieved.
Starting off with the one thing I saw as the biggest problem people were having when it comes to Gerstner wave based oceans, the height lookup. I achieved what I call “pretty much perfect accuracy” by creating a grid of boxes that each contain 2 triangles.
Whenever a height lookup is performed the module gets the nearest “node” using an octree nearest neighbor search then it determines which triangle the XZ point lies in and finds the height of it inside that triangle, and that allows for “pretty much perfect accuracy” height lookups.
Another thing I did when trying to optimize the module further was to use Vector3 instead of Vector2 due to it being a native datatype which allows for better performance when indexing it’s axes and calling it’s functions. I also decided to ditch metatables for the grid systems and instead define the nodes as functions that I can call to get the height of a XZ coordinate in them.
Finish
This is my first community resource post so any feedback is appreciated! Also if any of you have any tips or tricks on how I can speed up the module even more I would love to hear them!
Credits
@Sir_Falstaff - For adapting the Gerstner Wave formula to roblox
Roblox engineers - For the atomic binding module used to track the floatables with streaming enabled