CullingService V2 - Custom Client Sided Culling/Streaming Module

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

Thanks for the bug report! I found the issue - it was an attempted optimization that affected that way that anchor points were initially sorted. The issue should be fixed now (fixes applied to your repro file attached). Fixes are also live on GitHub, test place, and Roblox Library.

Thanks for the suggestion about pivot points, it’s not something I was familiar with before, but it looks to be a handy feature. I’ll look towards migrating to that soon, appreciate the heads up!

CullingService Testing Fixed.rbxl (224.2 KB)

I swear this doesnt work for me. It just doesn’t seem to spawn in when I test the game

Hi, if you send me a repro file, DM, or add me on Discord, I’d be happy to check it out and see if there’s an issue with set-up or a bug.

I got it to work on a new baseplate; I’m guessing something in the old Roblox place is bugging it out. Thank you for replying tho

CullingService V2

Download

GitHub Repository
Roblox Library Model
Roblox Library Plugin
Example Place

Key New Features

  • Added animation framework for developers to control streaming aesthetics
  • Added five animation presets: Rise, CartoonRise, SpaceShuttle, Transparency, and Blink
  • Fully modular (previously involved some hard coding related to the default ‘Short,’ ‘Medium,’ and ‘Long’ distance folders)
  • Significantly more reliable (now employs back-up checking)

Animations?!?!?

Yup, developers can now add animations to CullingService as a way of controlling the aesthetics within their project. Perhaps you want to make culled objects subtly load into the background by fading their transparency in? Maybe, you want a very obvious cartoony load-in: rather than hiding the streaming process you’d like to embrace it! Who knows! CullingService includes a very easy to use animation system that essentially lets you freely tailor the callback animation directly. This handler contains a Template module and 5 presets to serve as examples.

Previews of 3 Presets
Transparency

Rise (for that subtle feel)

CartoonRise (for that cartoony simulator-y feel)

To activate an animation package (or your own), go to Settings → Animation Package and change that value to the package name. If it is nil, no animation package will be provided.

Ex:

local module = {
    ["Animation Package"] = "CartoonRise", --// Defaults to nil for backwards compatability

    --// The rest of the settings....

Create your own animation package, just modify the clone the Template in CullingService → AnimationPackages and modify from there. It should be pretty self-explanatory and the 5 presets are intended to be used as references.

Note: these have not been benchmarked, but all presets ran in Studio at a comfortable 240-270 FPS (which is my average FPS anyways). FPS may be affected based on the complexity of your animations and the numbers of instances being animated at any one moment

Full Modularization

CullingService previously had a few points in the code that were hard-coded to the module’s base (range folders of ‘Short’, ‘Medium’, and ‘Long’). This has been removed, so that developers can add as many range folders as they want. This allows developers to better specify the ranges for certain instances (ex: giving terrain elements, landmarks, or particular instance types their own ranges). Hopefully, this lets developers take even more control of their experience.

Bug Fixes and Other Random Stuff

Check the GitHub commit if you’d like more details (as well as the side-by-side comparison of code changes)

How to Upgrade to V2

Just replace the old CullingService modules with the new one. There are a few small changes to the Settings, so I would recommend loading in the new Settings module and then just manually setting them again (if you aren’t using the out-of-the-box configuration). Because there are a couple new settings, CullingService V2 is not completely backwards compatible with V1.

Don’t fret though, the conversion is minimal. Your models are also fine. The plugin was not updated and does not need a redownload or update.

Legacy Downloads

CullingService V1 Legacy Model.rbxm (25.2 KB)
CullingService V1 Legacy Testing Place.rbxl (113.3 KB)

Because CullingService V2 is not backwards compatible, the latest V1 variant has been included for your convenience. This should function perfectly fine, however, I highly recommend using CullingService V2 for more control.

Enjoy!

7 Likes

Small V2.1 update

  • Added ‘Reset Anchor Points’ to the plugin, which allows you to select anchor points, cull them back in, and then delete the anchor points in one fell swoop. Perfect for that “whoops, didn’t want that to be culled out… crap how do I reset this??” moment. Realistically, this new button + auto add selection are probably the only two buttons that you need.
  • String formatting fixes to the plugin
1 Like

V2.2 update

  • Added Setting ‘Ignore Y Dimension’. This performs magnitude calculations ignoring the Y dimension (because anchor points tend to be higher than the actual player position when applied to large models). For any experience utilizing large structures (ex: buildings, tall trees, etc.) this will make things calculate more logically.

This change is backwards compatible, you only need to update the CullingService script. You can update your settings module by manually adding the entry [“Ignore Y Dimension”] = true (or false) to your module.

V2.3 update

  • Added ManualCulling with CullingSerivce:ManualCull(Position: Vector3). This allows you to manually cull around a specific point on the map and will halt culling around the player. This will be ended when calling CullingService:Resume(). Useful if you want to manipulate the camera in a custom manner and then cull around the camera vice the player (ex: intro screens)
  • Added manual refreshing, essentially calling the functions that cull around the player with CullingService:Refresh()
  • Added AutoStart Setting (backwards compatible with previous Settings). Setting this to ‘false’ will mean that CullingService does not start once initialized. You will have to then call CullingService:Resume() for CullingService to begin cull objects in/out. For backwards compatibility, if the setting does not exist, it will default to enabling AutoStart (previously normal behavior)
  • Removed Settings[“Paused”] (for consistency - use the methods CullingService:Pause() and CullingService:Resume() for future work)
  • Bug fixes and improvements
4 Likes

Hi, I can’t seem to be able to set up this, I made a model with the 3 folders in it, and i placed a part in the easy one. However, On step 9, i select the model and then click generate anchor points and the primary part is created, but there are no anchor points on the workspace AnchorPoints folder. I think this is the main problem, but then when i click the Add Selection to Model Storage button, it gives an error on the output:

cloud_7064741679.PluginCode.CullingPlugin.Culling:136: attempt to index nil with ‘FindFirstChild’

Can anyone help me please?

The issue with custom culling systems is that they use replicated storage which still costs memory, this issue is not present with Streaming Enabled. Correct me if I’m wrong.

I know this is kinda late but uh do you still recommend using this service? Or is this outdated

It depends on your use case.

If you want precise control over which assets are streamed/culled, at what ranges, how that looks, etc. then yes, I would recommend this. To further expand, the biggest overall advantage of CullingService is complete control while still getting performance benefits from streaming/culling. This can be essential for very ambitious projects where StreamingEnabled’s more-or-less one size all implementation either blocks you from being able to fully realize what you are trying to create or may possibly cause an impediment with future updates that are rolled out indiscriminately. With CullingService, you control everything.

If that does not apply to you and you want a fire and forget method, then I’d recommend utilizing StreamingEnabled. Roblox is investing lots of time into this and its default engine behavior which allows for better under the hood optimizations and performance (at the expense of less control by the creator).

Hello it doesnt seem to be working for me I tried to compare my file to the demo file and im not sure what I did wrong

My discord is zirdic if you want to communicate there

test.rbxl (144.2 KB)

This doesn’t really “replace” StreamingEnabled directly. A quick look at the source code reveals that this module only removes a copy of the model from the workspace when culling it, and still stores a copy in the ReplicatedStorage.

This module actually adds overhead as models need to be constantly cloned and destroyed, and also doesn’t reduce the memory usage (rather increases it as two copies of the whole map are kept on the client - one in the workspace and one in ReplicatedStorage .)

StreamingEnabled actually streams the model’s info from the server to the client so that the client doesn’t need to store it when its far away, reducing memory usage.

Yes, it does provide developers with more flexibility.
Yes, it does reduce rendering load. But it doesn’t really help with memory usage reduction, which is one of the biggest use cases for StreamingEnabled

I wonder what lead you to create this, would love to hear from you :slight_smile:

1 Like
Debug Steps

Name all of your models something unique (unless they are duplicates, in which case they can have the same name)
https://i.vgy.me/eB4hSh.png

Use the plugin (link in the original post) to add Anchor Points (you don’t put the actual models in there).
Before
https://i.vgy.me/Nc9LfK.png
After
https://i.vgy.me/2vuoyE.png

Create folders in all of your models that correlate to the Range Folders in Culling Service’s settings
https://i.vgy.me/evPb4a.png

Either set ‘AutoStart’ to true or call CullingService:Resume manually in your code
https://i.vgy.me/f3nRmX.png

Fixed place file: Debugged test.rbxl (143.8 KB) [I moved the spawn to make it easier to test, so don’t be alarmed that the house spawns in very close - it is where you originally had it placed]

The biggest issue is that AutoStart (in Settings) was set to false and the example code does not explicitly call CullingService:Resume(). The code in the Roblox Library model and GitHub has been updated to default set AutoStart to true, which is also consistent with backwards compatibility checks that set AutoStart to true if not detected in the user’s settings

Correct, philosophically CullingService and StreamingEnabled approaches are completely different in how they aim to optimize a place.

I believe I’ve made this pretty clear here: CullingService V2 - Custom Client Sided Culling/Streaming Module
and as recently as earlier today (there may be other posts also on the thread): CullingService V2 - Custom Client Sided Culling/Streaming Module - #43 by https_KingPie

Incorrect, although I can see why you came to this conclusion if you just peaked at code alone. Only one reference model is kept in ReplicatedStorage (ModelStorage). This gives you the advantage of allowing a large forest of say, 10 tree variants each with 100 copies (total 1,000 trees), to be stored as only 10 models and then selective cull in the necessary number of trees based on the player’s location (ex: only 35 are loaded at any given time). While I haven’t personally done performance benchmarking of what this would look like (either standalone or compared to StreamingEnabled), conceptually I don’t think anyone would assert that you wouldn’t see a significant performance increase.

Yes, CullingService is entirely client sided. The server does not need to be concerned about tracking what every player ought to see and ought not to see - clients can determine this for themselves (any worry of exploiting is somewhat small, given that map deletions/additions (ex: btools) can be difficult to detect in the first place without relying on client sided detections and any serious concerns or mechanics that depend on this should already be verified by the server). Of course, there are some exceptions to this (ex: when the server needs to determine whether two players are in actual line of sight of each other — this could be tricky if buildings and obstructions only appear on the clients themselves. That’s an example of a use case where StreamingEnabled might be preferable, unless the developer wants to get creative)

I designed CullingService to support a large RPG project that necessarily required streaming optimizations, but at the same time I wanted more control than Roblox inherently gave me (ex: specifying which assets get streamed and which don’t, controlling what ranges certain things appear [and giving player’s the ability to adjust these themselves to tailor their own load], aesthetically make streaming elegant by implementing custom visuals when assets streaming in or stream out, etc.) Pretty much every feature within CullingService was designed for my own project, but I decided to open source it because the initial effort was superbly annoying and I wanted to save other people the hassle if they wanted to create something similar.

StreamingEnabled is a great tool, but it’s inherently inconsistent. That’s not from the standpoint of gameplay reliability — Roblox is doing a good job of dedicating more time and effort into StreamingEnabled which is awesome — but more so from the standpoint that if an update is pushed tomorrow that suddenly breaks StreamingEnabled or causes it to perform weirdly, you more or less have to take that and then do damage control from there. CullingService is one of many examples of developer created alternatives to maintain exact control of what is going on in the game, mostly independent of engine behavior. It’s not going to be perfect for all games or all games that use StreamingEnabled, as mentioned before it is not at all a 1:1 replacement and the philosophy is entirely different.

1 Like

Thank you

so besides me not setting the autostart to true did I do anything else wrong?

Is it possible to create your own custom regions, and dictate what models are culled based on the player being within them, as opposed to using the built-in regions?

It’s not possible to change the regions (culling service creates those automatically), but you retain full control over which models will be culled in and which are not.