Built-in LOD & Foliage model support

unions
meshparts
parts
openworld
lod

#1

As a roblox developer, it is currently too hard to make large, free-roam / open-world maps. This is not a problem with the roblox engine itself; any engine would struggle rendering the amount of polygons that roblox renders. The problem is that there are no ways to make up for the large amount of polygons. Developers have no way of making LOD models like every other engine uses. This causes great strains on the roblox render engine, and it’ll kill some graphics cards in no time. I think roblox should add built-in LOD support that works in studio and in the client. This way players with mediocre computers can both develop and play large, optimized games.

Here’s how I think roblox could go about doing this:
Because bricks are, by nature, incapable of being optimized any better without a human brain deciding what needs to be taken out, it would be a smart move to allow players to create their own LOD models. This could be done by adding a new instance called an LOD Modifier. the LOD modifier would contain more than one object value, and an integer value. The object values would point to the default model & LOD models in ReplecatedFirst, or somewhere that they’re not being rendered. (Perhaps the object itself could prevent the object values from being rendered, despite whether or not they’re in workspace.) The integer value would define the distance threshold that had to be met any time the object would jump another LOD level.

I think this would help developers a LOT at optimizing their games, especially for groups that have a large roleplay city for their group members. It would also help developers make their games more interactive because less processing power would be spent on graphics, so more could be spent on the game itself.

Optimization is what makes the videogame world spin. I think it’s very necessary that roblox devs get more options when it comes to determining how the game runs, and what kind of aspects of the game will require more / less processing power. Thank you,
-bigcrazycarboy

EDIT: I forgot to add in a vegetation section lol
I think vegetation should have its own LOD settings that can view the category as a whole. Making interactive, realistic worlds is hard to do when there is little to no volumetric foliage. I think vegetation should be handled by directing an LOD object at a folder, and checking a “For all children” bool. This would iterate through all the children of the folder (the, for instance, trees) and apply the LOD settings to all of them. This way, roblox devs can make dense forests, and beautiful scenery in large levels.

Example of dense forests that could use some LOD. It wouldn’t be a good idea to put that scene in a game right now…


#2

I have used scripts to simulate LOD and was able to have maps with extreme amounts of trees, building interiors, etc. fairly well. However, it takes a bit of setup and has its share of limitations (including speed) compared to a built in alternative.


#3

What was your strategy for implementing your LoD system?


#4

I didn’t want to bump the thread but I’m pretty curious too :stuck_out_tongue:


#5

Basically: Chunks

My system works best for repeated models but can be used on anything. I have a 2 folders in replicatedstorage. The first folder holds a single copy of the full model, the second holds a low quality version of the model. These models must have a PrimaryPart.

I also have a folder in replicatedstorage that holds a list of CFrameValues, which have the name of the model, and the CFrame of the PrimaryPart. These CFrameValues are sorted into grid chunks.

As the player moves, I do a bit of simple math to see which chunks surround the player that aren’t currently rendering. Then, I grab the CFrameValues for that chunk (using a dictionary) and place the models. When a player is too far from a chunk, I set it as inactive, remove the main models, place the low poly models (if one exists; if there is no low poly model, it leaves empty space). No calculations are done on inactive chunks, only active chunks that have just become inactive.

Most of the performance goes into loading and unloading the models, however you can gradually place the models or spread out the chunk swapping over time fairly easily. However, distant models can be made not cancollide, and unions/meshes can be given a hull or box hitbox, which counters this a bit and overall speeds up the game.

The important part to keeping it fast is that all it does is round the players position to the nearest grid position, and then turn that position into a key to get the list of objects in that chunk. No further calculations or work is needed whatsoever. It could probably be optimized further fairly easily but I don’t know enough to do that.

As a bonus, this system is extremely easy to add multiple levels of detail too or multiple layers of chunks (so you can have small precise chunks for clutter and small details, and big optimized chunks for large objects that won’t be swapped as often, such as trees and buildings.


#6

Thanks for the reply. That sounds like a ton of work, setting up low quality versions of all your models. What I am trying to do is similar to yours, except it just removes models that are too far away. How did you set up your chunk system? Currently I just used a 2d array with a grid size of 200 and it goes through all the models and adds them to the right chunk based on position (using mod to get the right chunk) and goes through all the models/parts in that chunk to check their distance from the character and whether to render them. I’m not sure if this is a good way to do it. How’d you set up the smaller chunks if you don’t mind me asking?

Edit: Actually this was way easier than I thought. Just set up a couple more chunks of different sizes with the same system and have it load the whole thing, no need to go through every part.


#7

So, you don’t actually have to set up low quality versions of all your models. As I mentioned, it’ll place a low poly model only if one exists. If one does not exist, it just removes the high quality one and leaves it. And yeah, your edit hits the nail on the head. Load the whole chunk instead of checking all models in the chunk, and just add a new layer of chunks that have a different size if you want more size variety.


#8

so basically

Player.Moves:connect(function()
if (player location is not contained by the chunk region) then
    loadanewchunk(based on new player position)
end)

?


#9

I personally do it on a while loop and check every second or every time the player moves x studs. But the big thing is I don’t JUST load the chunk the player was in. I load the 8 adjacent chunks as well, and unload any chunks outside of that range. If you just load the chunk the player is in, then if you’re at the edge of a chunk you’ll be right next to an unloaded chunk and it won’t look very good.


#10

I feel like using a while loop might not have as optimal performance as an event because an event will only check when the player moves, and if the player stays in the same chunk there will be no calculations performed except for comparing the position to the chunk regions. a while loop would load the chunks every second if I’m not mistaken


#11

Using .Moved or While wait(1) isn’t really going to affect the rest of the code, and neither has much of a performance impact. Checking every 1 second vs however often the move event fires when walking doesn’t mean you’d be loading the chunks every second. You only load the chunks when a player enters a new chunk with either version.


#12

oh ur right I was thinking u loaded all chunks every second but I have no idea why I was thinking that. I guess that’s good except for saving some memory when the player is standing still - I suppose a while loop would be more consistent though anyways.