I want to create an infinite tiling ocean. So far this is my code:
-- Get the required services
local AssetService = game:GetService("AssetService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
-- Constants
local g = 35 -- Gravitational constant (in m/s^2)
local offset = 0.5 -- Spacing between vertices
local amplitude = 0.08 -- Amplitude A of the wave
-- Animation loop time (Section 4.2 implementation)
local T = 10 -- Loop time in seconds
local omega0 = (2 * math.pi) / T
-- Function to generate a random wave (optional)
local function generateRandomWave()
local direction = Vector3.new(math.random() - 0.5, math.random() - 0.5, 0).Unit
local wavelength = math.random(20, 60) -- Wavelength (lambda)
local k = (2 * math.pi) / wavelength
local omega_unadjusted = math.sqrt(g * k) * 2
local omega = math.floor((omega_unadjusted / omega0) + 0.5) * omega0 -- Quantized omega
return {
direction = direction,
wavelength = wavelength,
k = k,
omega = omega
}
end
-- Define waves and precompute k and adjusted omega
local waves = {
{ direction = Vector3.new(1, 0, 0), wavelength = 60 }, -- Wave A
{ direction = Vector3.new(0.7, 0.4, 0), wavelength = 45 }, -- Wave B
{ direction = Vector3.new(0.3, 0.9, 0), wavelength = 30 }, -- Wave C
{ direction = Vector3.new(0.9, 0.1, 0), wavelength = 20 }, -- Wave D
}
for i = 1, 10 do
table.insert(waves, generateRandomWave())
end
-- Precompute k and adjusted omega for each wave
for _, wave in ipairs(waves) do
wave.wavelength = wave.wavelength / 4
wave.k = (2 * math.pi) / wave.wavelength
local omega_unadjusted = math.sqrt(g * wave.k) * 2
wave.omega = math.floor((omega_unadjusted / omega0) + 0.5) * omega0 -- Quantized omega
end
-- Gerstner wave function (with adjusted omega)
local function GerstnerWave(wave, vertPos, currentTime)
local direction = wave.direction.Unit
local k = wave.k
local omega = wave.omega
-- Calculate the phase: k ⋅ x - ωt
local phase = k * (direction.X * vertPos.X + direction.Y * vertPos.Z) - (omega * currentTime * 0.3)
-- Calculate displacements (textbook parametric equations)
local horizontalDisplacement = Vector3.new(
-amplitude * math.sin(phase) * direction.X,
0,
-amplitude * math.sin(phase) * direction.Y
)
local verticalDisplacement = amplitude * math.cos(phase)
return Vector3.new(
horizontalDisplacement.X,
verticalDisplacement,
horizontalDisplacement.Z
)
end
-- Mesh setup
local mesh = game.Workspace:WaitForChild("Ocean")
local editableMesh = Instance.new("EditableMesh", mesh)
local width, height = 100, 100
local vertices = {}
-- Create a grid of vertices
for y = 1, height do
local row = {}
for x = 1, width do
local vertPos = Vector3.new(x - 1, 0, y - 1) * offset
local vertID = editableMesh:AddVertex(vertPos)
row[x] = { vertID = vertID, vertPos = vertPos }
end
vertices[y] = row
end
-- Create triangles between vertices to form the mesh surface
for y = 1, height - 1 do
for x = 1, width - 1 do
local v1 = vertices[y][x].vertID
local v2 = vertices[y + 1][x].vertID
local v3 = vertices[y][x + 1].vertID
local v4 = vertices[y + 1][x + 1].vertID
editableMesh:AddTriangle(v1, v2, v3)
editableMesh:AddTriangle(v2, v4, v3)
end
end
-- Get the player's camera
local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
-- Update the mesh on each frame
RunService.Heartbeat:Connect(function()
local currentTime = os.clock()
for y = 1, height do
for x = 1, width do
local vertData = vertices[y][x]
local vertID = vertData.vertID
local vertPos = vertData.vertPos
-- Convert vertex position to world space
local worldVertPos = mesh.CFrame:PointToWorldSpace(vertPos)
-- Check if the vertex is within the camera's view
local screenPoint, onScreen = camera:WorldToViewportPoint(worldVertPos)
if onScreen then
-- Sum displacements from all waves
local totalDisplacement = Vector3.new(0, 0, 0)
for _, wave in ipairs(waves) do
totalDisplacement += GerstnerWave(wave, vertPos, currentTime)
end
-- Calculate new vertex position
local newPos = vertPos + totalDisplacement
-- Update vertex position in EditableMesh
editableMesh:SetPosition(vertID, newPos)
else
-- Optionally reset to original position
editableMesh:SetPosition(vertID, vertPos)
end
end
end
end)
The code above produces the following patch of ocean
How would I go about tiling this? It’s quite performance heavy already, so any optimisations to improve this would be nice.
Thanks!