Here’s the code I used to accomplish this:
do
local insert, remove = table.insert, table.remove
local LODSpecs = {}
local function bfind(t, v)
local a, b = 1, #t
while a <= b do
local u = floor((a + b) * 0.5)
local w = t[u].Sort
if v > w then
a = u + 1
elseif v < w then
b = u - 1
else
return u
end
end
return a
end
local function CalculateCenter(m)
local Center = v3b
local Children = m:GetChildren()
for _, Model in next, Children, nil do
if Model.ClassName == "Model" then
Center = Center + Model.PrimaryPart.Position
else
Center = Center + Model.Position
end
end
Center = Center / #Children
return Center
end
function LODSplitEach(m)
local Groups = {}
for _, Model in next, m:GetChildren() do
local Group = Instance.new("Model")
Model.Parent = Group
table.insert(Groups, Group)
end
for _, Model in next, Groups, nil do
Model.Parent = m
end
end
function LODSplitModel(m, n)
local w = floor(n ^ 0.5)
local Size = m:GetExtentsSize()
local Center = CalculateCenter(m)
local wx = floor(Size.X / w + 0.5)
local wz = floor(Size.Z / w + 0.5)
local Groups = {}
for _, Model in next, m:GetChildren() do
local p = Model.ClassName == "Model" and Model.PrimaryPart.Position or Model.Position
local rx = floor((p.X - Center.X) / wx) * wx
local rz = floor((p.Z - Center.Z) / wz) * wz
local v = ("%d/%d"):format(rx, rz)
local Group = Groups[v]
if not Group then
Group = Instance.new("Model")
Group.Parent = workspace
Group.Name = v
Groups[v] = Group
end
Model.Parent = Group
end
for _, Group in next, Groups, nil do
Group.Parent = m
end
return 0.25 * (wx + wz) * 1.4142135623730951
end
function LODAddModel(m, d)
local Parent = m.Parent
assert(Parent)
local Spec = {
Model = m,
Parent = Parent,
Center = CalculateCenter(m),
MinDist = d,
Loaded = true,
Locked = false
}
table.insert(LODSpecs, Spec)
return Spec
end
function LODAddModels(m, d)
local Specs = {}
for _, v in next, m:GetChildren() do
local Spec = LODAddModel(v, d)
table.insert(Specs, Spec)
end
return Specs
end
function LODRemoveModel(m)
for i = 1, #LODSpecs do
local Spec = LODSpecs[i]
if Spec.Model == m then
LODForceLoad(Spec)
Spec.Locked = true
end
end
return false
end
function LODForceLoad(Spec)
Spec.Model.Parent = Spec.Parent
Spec.Loaded = true
Spec.Locked = true
end
local LoadQueue = {}
local UnloadQueue = {}
function LODUpdateInterest(p)
local GFX = UserSettings():GetService("UserGameSettings").SavedQualityLevel
local Multiplier = GFX.Value > 0 and GFX.Value/2 or 2.5
LoadQueue = {}
UnloadQueue = {}
for _, Spec in next, LODSpecs, nil do
local d = (Spec.Center - p).magnitude
Spec.Sort = d
local ShouldBeLoaded = d < (Spec.MinDist*Multiplier)
if Spec.Locked then
ShouldBeLoaded = true
end
local Loaded = Spec.Loaded
if ShouldBeLoaded and not Loaded then
local i = bfind(LoadQueue, d)
insert(LoadQueue, i, Spec)
elseif not ShouldBeLoaded and Loaded then
local i = bfind(UnloadQueue, d)
insert(UnloadQueue, i, Spec)
end
end
end
local function ProcessQueue()
if #LoadQueue > 0 then
local Spec = remove(LoadQueue, 1)
Spec.Model.Parent = Spec.Parent
Spec.Loaded = true
end
if #UnloadQueue > 0 then
local Spec = remove(UnloadQueue)
Spec.Model.Parent = nil
Spec.Loaded = false
end
end
function LODStart(i)
ThreadLoopAdd(i, ProcessQueue,"LOD Queue Processing")
end
end
In order to get this to work, you will need to put this code below it.
local ThreadLoopAdd
do
local Loops = {}
function ThreadLoopAdd(Interval, Callback, Desc)
local Thread = {
t = 0,
i = Interval,
c = Callback
}
table.insert(Loops, Thread)
end
local function Stepped(t, dt)
for i = 1, #Loops do
local Loop = Loops[i]
if t - Loop.t > Loop.i then
Loop.t = t
Loop.c(t, dt)
end
end
end
RunService.Stepped:connect(Stepped)
end
And here’s an example of how to use it:
local Trees = workspace:FindFirstChild("Trees")
if Trees then
LODSplitEach(Trees)
LODAddModels(Trees, 400)
end
LODStart(0.1)
The main reason this is so useful is because you can specify certain things to be rendered from certain distances (So you aren’t rendering small objects from miles away just because the players device can handle it)