LOD grid generation

I’m trying to animate ocean waves using the Gerstner wave function with a grid on an editablemesh where the vertices are positioned more closely together near the camera and spread further apart in the distance for performance, but I’ve been struggling to get the logic behind that LOD system. Any help to get this effect?

local WaterMeshPart = workspace.Water.MeshPart
local EditableMesh = Instance.new("EditableMesh")
EditableMesh.Parent = WaterMeshPart
local CameraPosition = workspace.Camera.CFrame.Position
local Size, Padding = 100, 10

for y = 1, Size do
	local Raw = {}
	for x = 1, Size do
		Raw[x] = EditableMesh:AddVertex(Vector3.new((Padding * (x - 1)) - (Padding * (Size - 1)) / 2, 0, (Padding * (y - 1)) - (Padding * (Size - 1)) / 2))
	end
	Vertices[y] = Raw
end

for y = 1, Size - 1 do
	for x = 1, Size - 1 do
		local Vertex1, Vertex2 = Vertices[y + 1][x], Vertices[y][x + 1]
		local Vertex3, Vertex4 = Vertices[y][x], Vertices[y + 1][x + 1]
		EditableMesh:AddTriangle(Vertex3, Vertex1, Vertex2)
		EditableMesh:AddTriangle(Vertex1, Vertex4, Vertex2)
	end
end

As.distance from camera decreases, distance between verts decrease. Multiply the grid position w distance from camera and then math.max it to its min value

This results in a serious deform of the grid’s resolution though. How would I have them scaled uniformly with no curvature or stretching?

Whats your code
Show it please

local CameraPosition = Camera.CFrame.Position
local Size, BasePadding, MinPadding = 50, 20, 0.1

for y = 1, Size do
	local Raw = {}
	for x = 1, Size do
		local Padding = math.max(MinPadding, (CameraPosition - Vector3.new((BasePadding * (x - 1)) - (BasePadding * (Size - 1)) / 2, 0, (BasePadding * (y - 1)) - (BasePadding * (Size - 1)) / 2)).Magnitude * 0.05)
		Raw[x] = EditableMesh:AddVertex(Vector3.new((Padding * (x - 1)) - (Padding * (Size - 1)) / 2, 0, (Padding * (y - 1)) - (Padding * (Size - 1)) / 2))
	end
	Vertices[y] = Raw
end

for y = 1, Size - 1 do
	for x = 1, Size - 1 do
		local Vertex1, Vertex2 = Vertices[y + 1][x], Vertices[y][x + 1]
		local Vertex3, Vertex4 = Vertices[y][x], Vertices[y + 1][x + 1]
		EditableMesh:AddTriangle(Vertex3, Vertex1, Vertex2)
		EditableMesh:AddTriangle(Vertex1, Vertex4, Vertex2)
	end
end

Do padding divided by the distance from camera
You didnt do what i said?
BasePadding * (y - 1) / distanceFromCam.Y
for example

Actually i think u can divide the whole vector

local Size, Padding = 100, 10

-- dist from charcater is the distance between where it would be w/o the optimization, and the camera position

local scaleFactorInverse = 4
local scale = distFromCharacter / scaleFactorInverse
local pad = Vector3.new(
      scale * (Padding * (x - 1)) - (Padding * (Size - 1)) / 2,
      0,
      scale * (Padding * (y - 1)) - (Padding * (Size - 1)) / 2
)

EditableMesh:AddVertex(pad)

That does something extremely weird, I’ve done the scale 0.01 because else the grid sized extremely big to show you.

local CameraPosition = Camera.CFrame.Position
local Size, Padding = 100, 10

for y = 1, Size do
	local Raw = {}
	for x = 1, Size do
		local DistFromCharacter = (CameraPosition - Vector3.new((Padding * (x - 1)) - (Padding * (Size - 1)) / 2, 0, (Padding * (y - 1)) - (Padding * (Size - 1)) / 2)).Magnitude
		local Scale = DistFromCharacter * 0.01
		local AdjustedPosition = Vector3.new(
			Scale * (Padding * (x - 1)) - (Padding * (Size - 1)) / 2, 
			0,
			Scale * (Padding * (y - 1)) - (Padding * (Size - 1)) / 2)
		Raw[x] = EditableMesh:AddVertex(AdjustedPosition)
	end
	Vertices[y] = Raw
end

for y = 1, Size - 1 do
	for x = 1, Size - 1 do
		local Vertex1, Vertex2 = Vertices[y + 1][x], Vertices[y][x + 1]
		local Vertex3, Vertex4 = Vertices[y][x], Vertices[y + 1][x + 1]
		EditableMesh:AddTriangle(Vertex3, Vertex1, Vertex2)
		EditableMesh:AddTriangle(Vertex1, Vertex4, Vertex2)
	end
end

1 Like

Yeah i solved your problem :joy: Just clamp the division.
Thats a beautiful visualization of 1/x, now math.min it to some value so it doesnt do that

1 over absolute value of x* where x is the cam distance*

Im on phone but let me try and fix it for you

local maximum = .1
local AdjustedPosition = Vector3.new(
math.min(Scale * (Padding * (x - 1)) - (Padding * (Size - 1)) / 2, maximum),
0,
math.min(Scale * (Padding * (y - 1)) - (Padding * (Size - 1)) / 2, maximum))

Experiment w/ maximum. Also i have no clue why it gets smaller on the Y, thats weird.

Edit: You have to remove the Y axis on camera.Position

okay, i’m on computer now

local cameraPosition = Camera.CFrame.Position - Vector3,new(0, Camera.CFrame.Position.Y, 0)
  • get only the X and Z of camera so we dont get a negative 1/|x| shape

now, i’ve been wanting to fix this equation for a minute

(Padding * (x - 1)) - (Padding * (Size - 1)) / 2

it can be simplified a little bit

(also start using camelcase not pascalcase. use camelcase for metatable methods)

local xPad = padding * ((x - 1) - (size - 1) / 2)
local zPad = padding * ((z - 1) - (size - 1) / 2)

i changed y to z because it makes more sense. you can also make (size-1)/2 a variable and reuse it

now as for the entire vertex calculation:

-- out loop
local cameraPosition = Camera.CFrame.Position - Vector3,new(0, Camera.CFrame.Position.Y, 0)
local minimum = .1
local halfOfNonOffsetedSize = (size - 1) / 2

-- in loop
local xPad = padding * ((x - 1) - halfOfNonOffsetedSize)
local zPad = padding * ((z - 1) - halfOfNonOffsetedSize)
local pad = Vector3.new(xPad, 0, zPad)

local adjustedDistFromCam = math.max((cameraPosition - pad).Magnitude * .01, minimum)
local adjustedPosition = pad * adjustedDistFromCam

you can math.ceil adjustedDistFromCam to get something similar to this (with potentially overlapping grid points)

but ultimately my solution gives a circular LOD

to potentially fix this (if you REALLY need a square grid), create a new distance function which disregards diagonals and converts them to what their adj or opp is, and then use math.ceil(2^magnitude) or something to determine the padding scale. i havent tested this but its a wild guess.

and i just found the name of the distance function i decribed i think. ‘chebyshev distance’

1 Like

It’s still not quite right, this is me using your circular LOD. (I know I should use camelcase but I prefer the clean capital look lol). Next to the total resolution getting stretched, it also expands in general with this logic.

task.spawn(function()
	while true do
		for _, v in EditableMesh:GetTriangles() do
			EditableMesh:RemoveTriangle(v)
		end
		for _, v in EditableMesh:GetVertices() do
			EditableMesh:RemoveVertex(v)
		end
		local CameraPosition = Camera.CFrame.Position
		local Size, Padding = 15, 60
		local HalfOfNonOffsetedSize = (Size - 1) / 2
		for y = 1, Size do
			local Raw = {}
			for x = 1, Size do
				local Padding = Vector3.new(Padding * ((x - 1) - HalfOfNonOffsetedSize), 0, Padding * ((y - 1) - HalfOfNonOffsetedSize))
				Raw[x] = EditableMesh:AddVertex(Padding * math.max((CameraPosition - Padding).Magnitude * 0.01, 0.1))
			end
			Vertices[y] = Raw
		end
		for y = 1, Size - 1 do
			for x = 1, Size - 1 do
				local Vertex1, Vertex2 = Vertices[y + 1][x], Vertices[y][x + 1]
				local Vertex3, Vertex4 = Vertices[y][x], Vertices[y + 1][x + 1]
				EditableMesh:AddTriangle(Vertex3, Vertex1, Vertex2)
				EditableMesh:AddTriangle(Vertex1, Vertex4, Vertex2)
			end
		end
		task.wait(0.1)
	end
end)

make it magnitudes smaller so i can understand the behaviour

It’s at 0.001 now.

this is the part where i start shooting strays at the code (i have no clue what im doing from this point onward)

it looks like the extruding thing is inverted in a sense.

  • try pad - cameraPosition instead of cameraPosition - pad?
  • undo the paddign formula change juist in case?

try both of these individually

local Padding = Vector3.new(Padding * ((x - 1) - HalfOfNonOffsetedSize), 0, Padding * ((y - 1) - HalfOfNonOffsetedSize))
‘Padding’ vector3 is too small for CameraPosition, so (CameraPosition - Padding).Magitude is a very negligible differentce. its working, but you need to convert the camera position to the same plane ‘scale’ as the padding

Place1.rbxl (49.8 KB)

local c = 2000^math.max((CameraPosition - LocalPadding).Magnitude * 0.01, .1) / 1000
				local pos = LocalPadding * c

doing this

yielded this, where the red is the camera.
in the local variable c i just maximized the difference and then divided it by 1000