This is a tutorial on using the LODService Plugin and Module
Keep in mind you will have to put the .rbxmx file into your local plugins folder otherwise you will NOT be able to generate templates!
You can do this by:
- Downloading the .rbxmx file from the devforum post
- Right clicking on ServerStorage → Insert → Import Roblox Model and selecting the LODServicePlugin.rbxmx file you just downloaded
- Right clicking on the folder called ‘LODServicePlugin’ that you just imported → Save / Export → Save as Local Plugin
After this you can delete the LODServicePlugin folder if you wish to
PROCESSING TOOLBOX MODELS
Roblox blocks CreateEditableMeshAsync/CreateEditableImageAsync for assets not owned by you, even if they are free toolbox models, and assetdelivery doesn’t work (for me atleast) in edit mode, so you will need to use an application to get past this.
You can follow the instructions and download the application used for this here: GitHub - TylerAtStarboard/RobloxAssetDeliveryProxy: Used for MeshVox and other things I need AssetDeliveryFor, because Roblox requires cookie authentication now and I don't want to send my cookie to roproxy.
You will need to have this open and running while using the plugin in order for assetdelivery to work.
ALGORITHM EXPLANATION (this is important to understand the plugin settings)
In short the simplification algorithm takes a mesh and merges the vertices into little cells based on how important they are to the geometry. This is how the original, unimproved version of vertex clustering works:
Aside from improving how the vertices are clustered, this implementation fixes the issue where triangles that have 2 vertices in the same cell just get deleted, removing too much geometry. This issue is resolved by creating a rectangle between the two cells, these are called pixel lines (this is how they will be referred to in the settings).
As for the billboards, they are constructed by a given amount of planes which are placed vertically, and 2 extra planes placed horizontally, where each plane is an orthographic projection of a given side. These planes are then blended based on view angle.
Lastly the rendering constant which is called X in the original paper. This is the pixel to cell size ratio of the lod selection, meaning the higher it is, the lower the selected lod quality will be. This is the equation for the selected cell size, we can see how X is just a multiplier for how low the selected quality will be:

I recommend keeping this between 3-7 with 3 translating to higher lod quality and 7 to a lower one, but you can experiment with your own values.
THE PLUGIN
Main Section
Insert LOD Folder- This button inserts the folder with the Module into ReplicatedStorage for in-game useModel Template- This is the model that you want to turn into an LOD Template, please reselect the model if you change main settings because it will need reprocessingMesh Simplification Timeout- The timeout for the vertex clustering algorithm (this setting is pretty redundant because the algorithm is really fast, but if your computer is really old I recommend lowering this a bit)Mesh Generation Timeout- Same as the previous, but for turning the simplified vertex data to a meshBillboard Generation Timeout- Same as the previous 2, but for rendering billboards (I recommend setting this a lot lower if your computer is old, as billboard generation is an expensive process)Use Box Parts- Whether to process every part as blocksGenerate Transparent Mesh- Whether to generate a transparent mesh, turn this on if your model has parts that aren’t 1 or 0 transparency and are large enough to be seenMesh Blockiness- How much the simplified mesh’s normals should be interpolated towards the blocky normal (I recommend leaving this at 0 unless you know what you’re doing)Ignore Unprocessable Parts- When this setting is on Unions and DataModelMeshes (FileMeshes, BlockMeshes, etc.) will be ignored, when off they will be treated as block partsGenerate Dynamic Normals- Whether to generate pixel linesMaximum Pixel Lines- How many pixel lines the algorithm can generate at maximumTexture Sampling Radius- How large the sampling radius should be when sampling from a texture - setting this high will make textures appear blurry, but I do not recommend changing this from 0 unless you know what you’re doingTexture Sampling Iterations- How many pixels the texture sampling should sample, this is related to the previous setting, also don’t recommend changing thisCell Color Averaging Bias- When 0 the cells’ center vertices will keep their color, when 1 the cells’ center vertices’ color will be the average of all vertices’ color in that cell, any other values will blend between the twoUse Averaged Material Colors- Whether materials should tint a vertex’s color based on its average color, this setting is mostly for materials that largely differ from the plastic texture such as pre-2022 granitePre 2022 Materials- Whether the algorithm should use the new materials’ averaged color or the old, pre 2022, ones’ (This should match MaterialService.Pre2022Materials)Merge Vertices- Whether the algorithm should merge vertices that are really close to eachother and are similar when processing a model (I recommend leaving this off)X- This is the rendering constant, I recommend keeping this between 3 and 7, the higher it is the lower the quality of the LODsUnrender Cell Size- The cell size where the model should be unrendered, I recommend setting this to be the size of the model, or experimenting with the unrender distanceUnrender Distance- The distance at which the model would be unrender with the given unrender cell size and XModel Tri Count- The tri count of the processed model (counts unprocessable parts as boxes)Distance to Model- The distance to the selected model, you can use this to experiment with where LOD boundaries would beReset Settings- Resets all settings to defaultAdd Mesh Element- Adds a new mesh element to the template (mesh element meaning that it’s not a billboard element)Add Billboard Element- Adds a new billboard element to the templateBillboard Generation State- The progress of an ongoing billboard generation
Mesh Elements
Cell Size- The size of each vertex clustering cellMin Visible Distance- The smallest distance where the LOD level would be visible with the given cell size and X settingDelete Mesh- Deletes the meshLOD Texture- The texture for the generated mesh, I only recommend using this when creating an LOD of a singular mesh with visually important texturesLOD SurfaceAppearance- Same as the previous but a SurfaceAppearance is used instead of a textureGenerate Mesh- Generates the LOD with the given cell size and other settingsTri Count- The tri count of the generated LOD
Billboard Elements
Cell Size- Same as for the mesh elements, but has no effect on the visual outcome of the billboardMin Visible Distance- Same as the meshDelete Billboard- Same as the meshResolution- The X Resolution of each planeSides- How many vertical sides the billboard will haveY Angle- The angle at which the horizontal planes will start to get blended inGenerate Billboard- Generates a billboard with the given settingsTri Count- Same as the mesh
Finalisation
Start Preview- Starts a preview of the template with the given settings, with this you can test out how the template would look in game (keep in mind the previewed meshes don’t have shadows but they do in-game)Stop Preview- Stops the ongoing previewTemplate Name- The name of the generated templatePixel Line Precision- How many decimals the pixel lines’ positions will be rounded to, if any of the LOD levels’ line count times the decimal precision exceeds about 1500 then it will have some of its pixel lines removed (due to memory issues)Generate Template- Publishes the template, and generates it into ReplicatedStorage.LOD.Templates
MODULE USAGE
Methods:
- LODService:SetX(X : number) - this will set X to the desired value
- LODService:Run() - this starts LODService
- LODService:Pause() - this pauses LODService
- LODService:Clear() - pauses and clears the entirety of LODService, meaning all LODs are deleted
- LODService:CreateRegion(pivot : Vector3) - creates a new ‘ModelRegion’ with the given center and returns it
ModelRegion:
- ModelRegion:Destroy(timeout : number?) - deletes everything inside of the region with the given timeout (default value is 5)
- ModelRegion:CreateGroup(template : Template) - creates a ‘ModelGroup’ inside of the region and returns it
ModelGroup:
- ModelGroup:Destroy() - deletes everything inside of the group with the given timeout (default value is 5)
- ModelGroup:RefreshScaleMultiplier() - refreshes the scale multiplier of the group, I recommend calling this if the group’s models largely differ from the template scale
- ModelGroup:ParentTo(parent : ModelRegion)
- ModelGroup:LODifyModel(model0 : Model, autoPivot : boolean?, loadingPenaltyMultiplier : number?) - creates an LODModel out of model0, enabling autoPivot will automatically move the lods with model0 if its pivot is moved.
loadingPenaltyMultiplier
loadingPenaltyMultiplier is the number that’s multiplied with the default loading penalty, if the loading the model is really laggy, then set this to something higher than 1, and if the opposite is true then set it to something lower than 1 (this setting can mostly be left alone)
LODModel:
- LODModel:Destroy() - deletes the lods and removes it from its parent group, but keeps model0
- LODModel:PivotTo(pivot : CFrame) - pivots the LODModel to the given CFrame
- LODModel:PreloadAll() - preloads every lod level, this makes loading a lot faster but takes a lot more memory
- LODModel.model0 - this is the original model, which gets reparented to nil when unloaded, so use this if you need to access the model during runtime
Settings:
- MaxMemoryUsage: the number of lods needed for the script to start deleting unloaded lods to free up memory
- PenaltyTimeout: the amount of penalties collected before halting the thread, the higher this is the faster LODs will load (it will also be laggier)
- Timeout: how many seconds the halting lasts
- BillboardTimeout: same as PenaltyTimeout but for billboards/pixel lines
- BillboardUpdateDot: the cosine of the minimum delta angle - the angle between the current viewing direction of a region and the viewing direction of a region when it was last updated - needed to refresh billboards of said region
Here is an example of using the module - this groups all models that have names that match a template into regions:
local REGION_SIZE = 100
local LOD = game:GetService("ReplicatedStorage").LOD
local Templates = LOD.Templates
local LODService = require(LOD.LODService)
local regionCount = 0
local modelCount = 0
local groupCount = 0
-- you can use GetDescendants here aswell
for _, v : Instance in workspace:GetChildren() do
if not v:IsA("Model") then
continue
end
local templateModule = Templates:FindFirstChild(v.Name)
if not templateModule then
continue
end
modelCount += 1
local template = require(templateModule)
local pivot = v:GetPivot().Position
local foundRegion = false
for _, region in _G.regions do
if (region.pivot - pivot).Magnitude < REGION_SIZE then
foundRegion = true
local foundGroup = false
for _, group in region.groups do
if group.template == template then
foundGroup = true
group:LODifyModel(v)
break
end
end
if not foundGroup then
groupCount += 1
region:CreateGroup(template):LODifyModel(v)
end
break
end
end
if not foundRegion then
regionCount += 1
groupCount += 1
LODService:CreateRegion(pivot):CreateGroup(template):LODifyModel(v)
end
end
LODService:Run()
print(string.format("LODService started with %i regions, %i groups and %i models", regionCount, groupCount, modelCount))
This tutorial is pretty bad and I’m going to improve it in the future, I just wanted to make a quick guide so people don’t have to figure the module out for themselves
If you have any questions feel free to ask!
