CullingService V2 - Custom Client Sided Culling/Streaming Module

Just a quick suggestion, if you dont have the folder(s) created before enabling/installing the plugin it will give out this error


And looking through the plugin code I noticed as soon as the plugin is enabled it searches for the folders, so just small a suggestion to make it look for the folders either on pressing initialize button or whenever you click one of the required buttons

Its not really a big deal all you have to do is make the missing folders and re-open the file game might be confusing to new users that don’t know

1 Like

POV: Anchor points have collisions enabled
Good module though, definitely will be put to use in my project Feudal Japan.

1 Like

If so, I think it should be possible to choose between the two instead of having the original system completely replaced.

I have a question, the module is really cool tho there isn’t collision right on the server ? So an IA will just go throught object ? I have the idea to create for each part being culled a replica of them with a part with transparency 1 even tho idk, if it’s a good choice

so uh, This is a cool module. But why cant it identify models that have the same name? its kinda… bizzare…

Correct, there is not a collision on the server, models only appear for the player. However, you have the option of controlling which models are affected by CS. If a model needs to have universal collisions or appear on the server, just don’t set up an anchor point for it. This affords you the control to specify exactly which instances are affected by CS and which aren’t, while still retaining the performance benefits of a culling system.

The way CS works, at its most basic level, is that when you get within a certain distance to an anchor point, the player loads in a model with the same name as the anchor point.

Because CS is set up to require everything to be culled out initially, using name associations is the easiest and most performant way to ensure that everything goes in the right place. This means that minimal models need to be stored in ModelStorage (since duplicate models of common items like trees, lightposts, crates, etc. need only be cloned and have their CFrames adjusted, versus storing every single model in the game), instances like ObjectValues or even attributes can be avoided (which, in bulk, will still be less performant than simply referencing the name property) and it reduces the initial strain on players compared to culling everything out upon join, which could lead to crashing or significant performance issues on initial join.

Plus, it’s just a good habit to not have all of your models named “Model”!

I guess when it comes to dummies or normal models that arent that important. I guess the best way to deal with this is to add a random number to their name? I plan on using this module in combination with well ContentStreaming…

Because to my knowledge, isnt occlusion culling what most big games used? they unrender things that cant be seen by the player

CS can be used for important models. All models don’t need unique names, only models which aren’t duplicates. For example, if you have three different variations of a tree design, you can name them “Tree1”, “Tree2”, and “Tree3” and leave it at that. Then when you create anchor points appropriately named “Tree1”, “Tree2”, and “Tree3” (all this can be done via the plugin), and only three actual models will be saved into ModelStorage, even if you end up having hundreds of anchor points.

If you turn on Workspace.StreamingEnabled (what I think you mean when you say using ContentStreaming) you are doing something very similar. It’s not actually occlusion culling, it takes into account where the player is in relation to other instances. If you look, there’s properties for Workspace.StreamingMinRadius and Workspace.StreamingTargetRadius. CS operates with a similar philosophy, but you’re getting more control over what specifically is streamed in and out. The increased control is especially useful for programmers, since you have a specific and exact idea of exactly what is streaming in and out, as well as when. You also have manual control over the distances that specific instances included in Short, Medium, and Long folders will be culled in, allowing for manual control of level of detail and opening the door to player’s customizing it themselves (ex: allowing settings for low, medium, high, or ultra streaming settings).

One of the downsides of manual control (CS) versus automatic control (StreamingEnabled) is that it’s simply going to take a bit longer to set up. I created a plugin which is designed to expedite the process significantly, making it so that your longest and most arduous step is simply give your models unique names if they are distinct models (again, to clarify all your models do not need unique names if they are the same model, but placed in a different position or orientation, like the tree example I gave earlier). However, one of the big upsides of manual control is that you have precise control over exactly what is happening in your game, what models are being affected (and what models aren’t), levels of detail, configuration settings, etc.

If that’s still not making sense, I’d recommend checking out the open-sourced example place and messing around with the configurations there.

Oh ok, thanks now i understand.

I actually plan on making an Open Sourced Module/Script that allows devs to implement Occlusion Culling. Its really simple if you think about it, the module/script also takes into account what instances that shouldnt be affected. This doesnt reparent (As you should know that isnt the most performant way of doing things), instead it manipulates the cframes/vector3 of the models/parts and sends them extremely far away (Similar to partCache) to the extent of it not being rendered any more. Forgot to say, the script constantly checks wether the position of affected models/parts is within view, if it is, then obvi it will bring them back.

Is this still usable for modern work and does it lag?

Does this module also use the new parallel library features? that may be given a boost in this module of yours

Uh, nevermind, this module seems to be very tedious anyway.

Lag: no.
Usable for Modern Experiences: yes

The only real lag you might notice is the initialization, which is where most of the ‘work’ is done. Although indirectly alluded to in the thread, the module works by essentially chopping up 3D space and using the segmentation to efficiently sort relevant instances. This optimizes things significantly.

If you’re interested in a benchmark on efficiency, CullingService is able to fully initialize my experience with the following stats:

Game Volume: ~ 150 billion studs (true number is 149,698,256,400) [it is a very big map]
Region Length: 250
Approximate number of initialization calculations: ~10-10.5k (wrote this down a while ago, but didn’t save the exact amount)
Initialization Time: ~16ms seconds (true number is 15.976020006928593)

This will obviously be affected by your own configuration, instances, etc. but it blazes pretty fast.


Does it use the new parallel library?: no.

The source code is viewable here (can also be found in the OP). This was developed before the parallel library and from my understanding, the parallel library is really intended to let your experience run in parallel, not just individual modules. If you want to run this in parallel, it shouldn’t be difficult. If that’s incorrect, let me know - I’m not super read up on it.


Tedious: aight

I would highly recommend re-reading the OP and checking the source code to see if this fits your project’s needs, because I think a lot of your questions are already answered there.

To quickly sum it up, this is designed as an alternative to StreamingEnabled primarily geared towards programmers (allows control over which assets are specifically streamed) and high-intensity builders (i.e., those whose builds require sufficient optimization, but want more precise control of what is streamed and at what range it is streamed). I’m sure people have found other creative uses for it, but that’s the primary idea behind it.

Its very boring to go through a lot of steps to apply this service in games with alot of assets and if you ever think to update it then hopefully use the new parallel feature since it may give another advantage to this module but more importantly hope you can make it more clear and easier to setup your module more quickly As I see more ways to improve this module’s setup procedure.

(TL;DR, becasue the response got long: configurable code unavoidably requires configuration, if this is a serious barrier to you + this is relevant to your experience - I’ve added some console code to help you out)

I think there’s some kind of miscommunication. CullingService is not designed or intended to be some kind of automatically initialized/self-determining system. That’s very much in the vein of StreamingEnabled, which indiscriminately streams everything in and out (more or less).

CullingService is designed to be configurable. This lets you reap the benefits of streaming objects in and out while still maintaining very precise (at least relative to the default engine options) control over your experience.

However, the clear downside of a configurable system is that it requires configuration! Like most things (ex: redoing a UI framework, shifting data/replication handling, etc.), converting an existing experience over is going to be more time-consuming than starting at the onset. It can be necessary, but it’s definitely not flashy development - we can agree on this, I’m sure.

With that said (and assuming you have good naming conventions in your experience, i.e., not “Model” everywhere). You can expedite the process with some simple console work.

Run this in your console
for _, Model in ipairs (workspace:GetChildren()) do
	if not Model:IsA("Model") then
		continue
	end
	
	local MediumFolder = Instance.new("Folder")
	MediumFolder.Name = "Medium"
	
	for _, Child in pairs (Model:GetChildren()) do
		Child.Parent = MediumFolder
	end
	
	MediumFolder.Parent = Model
end

Wrote without testing - should work, but it’s possible there is a typo

Add the 5-6 necessary folders (per instructions)
Open the plugin, initialize it, click the auto button.

And, your place should be set up.


With that said still, if you’re averse to steps, then it’s very possible this is simply not necessary for your experience. If your experience from a programming perspective isn’t detrimentally affected by StreamingEnabled, that might be better for you - it’s definitely easier (because it’s a toggle setting). If you’re not overly concerned about performance controls, then this simply might not be relevant to you.

I was just saying, good luck tho.

Hey! This is a awesome module so far, it is really something I wish Roblox could support natively already for larger more open and detailed worlds.

I was just curious if this supports models with varied rotation, scaling, and colors?

For example, can I use this module to cull in and out a field of flowers that are randomly larger or smaller than one another, or rotated to grow with the direction of a hill?

I’m not sure, but I have tested around a bit, and found that it doesn’t seem to work, unless I have done something wrong.

Anyways, thank you for the amazing free module so far!

1 Like

Heyo!

Right now CS doesn’t support that, but that’s a really interesting suggestion. This is definitely something that’s possible to implement, the only drawback(?) would be that each time you would generate an experience it would be somewhat different. Maybe that’s a positive in some ways, though. Definitely possible to add, and I’m looking into it.

Could you explain a little more what you mean by this? Is it that the model isn’t properly rotating when you rotate the anchor point, that generating the anchor point doesn’t respect rotated models, or is this in the line of randomization?

Update

  • Fixed a bug that prevented large regions from being effectively created
  • Added GoodSignal compatibility and API calls (this should now make it easier to easily create client-sided events utilizing motion)

New API Functions

module:CreateSignalForModelCullIn(ModelName: string)
Returns a Signal which is fired every time a model with the name provided is culled in. Signal provides the model as a first argument

module:CreateSignalForModelCullOut(ModelName: string)
Returns a Signal which is fired every time a model with the name provided is culled out. Signal provides the model (in this case, nil) as a first argument

module:CreateSignalForModelCullInAtRange(ModelName: string, RangeName: string)
Returns a Signal which is fired every time a model with the name provided is culled in. When the Signal is fired, it provides the model (in this case nil) as the first parameter

module:CreateSignalForModelCullOutAtRange(ModelName: string, RangeName: string)
Returns a Signal which is fired every time a model with the name provided is culled out. When the Signal is fired, it provides the model as the first parameter

Example API usage (direct from my project, which makes a boat sway with the waves when culled in)

local function ListenForShips()
    for _, ShipName in pairs (AllShipNames) do
        local ShipAdded = CullingService:CreateSignalForModelCullIn(ShipName)

        ShipAdded:Connect(function(Ship: Model)
            HandleShip(Ship)
        end)
    end
end
2 Likes

Two things,

One I think you should add a setting to use an objects Pivot Points keeps it up to date with modern day roblox technology plus I’m pretty sure the current positioning function your using is deprecated.

Two, It seems like not all the objects in a region load and its random at that. I’d suggest looking into it as it doesn’t seem like its a guaranteed your objects will get loaded

CullingService Testing.rbxl (224.6 KB)

1 Like