Skip to the content.

Content Pipeline Tools

On this page

Four editor tools that take work off a content pipeline - baking a material subgraph down to a texture, turning a field of repeated props into instanced components inside one culling bound, collapsing a cluster of collision bodies into one, overriding the bounds the engine culls and streams an actor by - and three utility windows for texture and navmesh chores.

The tools themselves run in the editor and change assets, so what your game loads afterwards is ordinary engine content. Two of the pieces - the culling-bounds and collision-body components - are placed on actors and therefore live in your packaged build as well, with a Blueprint API you can use there if you want to.

What each tool is for, in numbers

Works on every Unreal Engine 5.8 hotfix; the descriptor says 5.8.0 because that is the version a launcher 5.8.x engine checks plugins against. Measured on Unreal Engine 5.8.2 from the launcher, on scenes built by a script rather than placed by hand, so the conditions below are what the numbers mean. Where a tool costs something, the cost is in the table too - you should know which side of each trade your content is on before you use it.

Tool What it changes What it costs Condition
Material Bake pixel-shader instructions 583 → 268, texture samples 6 → 1 one texture asset per bake a subgraph of three Noise nodes at six octaves, baked to 512x512 in 0.9 s
Wrap & Batch actors 12 006 → 188, components 12 001 → 728, sections 12 001 → 729, draw calls at the camera roughly halved on this content the batched map measured about 2 ms slower on the GPU 12 000 placed props in 80 clusters over two kilometres
Collision merge collision-enabled components 2 001 → 2, every one of 4 000 rays still hits a ray that hits the cluster 0.0193 → 0.0682 ms; a ray that misses is unchanged 2 000 small collision boxes
Culling Bounds decides what the engine culls and streams an actor by nothing to measure: it changes correctness, not cost -

Why the middle row says what it says. UE 5.8 already instances identical static meshes through GPUScene, so batching a field of repeats does not make the frame faster - it makes the LEVEL smaller, which is what an outliner, a save, a streaming cell and a memory budget pay for. On enclosed room-and-corridor levels a shared culling bound was worse again, two to ten times more primitives drawn, and that is what the bounds mode below is for. Measure your own level; this page will not pretend for you.


Requirements

   
Engine Unreal Engine 5.8
Platform Windows 64-bit
Project type C++ or Blueprint. Installed into the engine from Fab, the plugin comes built; a copy in a project’s Plugins folder is built with the project, which a Blueprint-only project is asked to do once
Enabled for you EditorScriptingUtilities and ProceduralMeshComponent, both shipped with the engine

EditorScriptingUtilities is marked Beta by Epic in 5.8. The label is theirs; the utility widgets use its asset and filter libraries.

Installation

  1. Install from Fab into the engine, or copy the folder into <YourProject>/Plugins/.
  2. Installed into the engine, the plugin starts disabled, as every engine plugin does: turn it on in Edit → Plugins (search for Content Pipeline Tools) and restart the editor. A copy in the project’s Plugins folder is enabled already and is built when the project opens.

Everything below appears in menus you already use. There is no separate window to find.


Material Bake — a texture instead of a subgraph

A Bake To Texture node placed in a material graph marks the subgraph feeding it. The tool renders that subgraph into a texture, writes the texture into a texture parameter on the material, and walks the whole tree of material instances beneath it so each one gets the result too.

Where it lives

The node’s inputs. Write mode decides how many pins it has and what they mean: an RGBA float4 (the default), a normal packed into red and green with two spare channels (Normal + BA), two normals packed into one texture (Two Normals), and single-channel variants. The output is a texture object, or a sampled colour when As sampler is on, which is what you want when the node feeds Base Color directly.

What it writes. Parameter Name is the texture parameter it creates and fills, so a material instance can override it later. Texture default size is the resolution, unless you connect a texture to the SizeReference pin, in which case the bake matches that texture’s size.

Reuse by content, not by name. Every baked texture carries a hash of its pixels as an asset tag. A second material that bakes to the same image is pointed at the texture that already exists rather than producing a copy, and the match is confirmed by comparing the images, so a hash collision cannot hand back the wrong picture. The tags are read back through the asset registry when a bake run starts, so the reuse works across sessions and across a whole library.

Where the textures land. /Game/BakedTextures, under the same folder path the material has in Content, as <Material>_<Parameter>_<Hash>. The hash in the name is what keeps a rebake from overwriting a texture another material is still using: change the material, and the next bake writes a new asset instead of quietly changing the old one under everybody’s feet. Both the folder and the name template are settings.

What it saves. On a subgraph of three Noise nodes at six octaves the material went from 583 pixel-shader instructions and six texture samples to 268 and one - 54 % fewer pixel-shader instructions. Adding octaves grows the first number and leaves the second alone: after the bake the material samples one texture, whatever fed it. The example inside this plugin is exactly that pair of materials, so the comparison is one you can repeat. A material with an unbaked node reports the cheap number already, because the node samples its placeholder texture until the bake fills a real one in - so judge a bake by the texture it produced, not by the statistics panel.

What it does not save. Bake something cheap and you pay for it: a gradient of two constants mixed by a texture coordinate measured 268 instructions before the bake and 268 after, with one sampler and one texture added. The tool is for subgraphs that cost real pixel work and never change - noise, layered masks, expensive blends - not for tidying a graph up.

Worth knowing. The bake runs on the editor’s tick and reports progress in the corner. A material that cannot compile is refused with the compiler’s own message rather than waited on. By default the tool shows the list of assets it is about to touch before it starts; turn that dialog off in the settings for unattended runs. The bake renders each subgraph through a throwaway copy of the material, named <Material>_<Parameter>_<id>_IMM in the baked-textures folder; those copies are deleted when the run ends, unless Keep Intermediate Materials is on in the settings.

What it will not touch. Materials that belong to the engine, to a plugin installed in the engine, or to this plugin are never baked: a bake writes into the material and points it at your project, and those assets are not yours to change. Duplicate such a material into your project’s Content folder and bake the copy.

Wrap & Batch — instancing with one culling bound

An extra tab inside the engine’s own Merge Actors window. It turns a selection into a single actor carrying instanced static mesh components, wrapped in one culling bound.

Bounds mode is the setting that decides whether this helps.

Enclosed levels need the second. Measured on room-and-corridor maps, a shared bound drew two to ten times more primitives and cost up to half again as much GPU scene time, because the whole cell is drawn the moment any corner of it is visible. The same batch in per-component mode measured within run-to-run noise. Pick by the shape of your level, not by the default.

Shadows and occlusion stay as they were. Each instanced component casts shadows and occludes exactly as the meshes it replaces did; meshes that differ go into different components. Override Shadows And Occlusion on the builder sets them for the whole batch instead. The measurements on this page were taken with them switched off.

What it saves, and what it does not. A field of 12 000 placed props becomes 188 actors and 728 instanced components holding the same instances, material sections drop from 12 001 to 729, and draw calls at the camera roughly halve. The frame does not get faster on that content: measured as a game, the batched field cost about two milliseconds more on the GPU, because UE 5.8 already instances identical static meshes on its own. Use this where the count is the problem - a level that has become slow to open, save or stream - and measure before you use it for anything else.

It is extensible from Blueprint. UWrappedInstancingBuilder is a Blueprintable class with five events: ShouldHarvest (refuse a component), GetActorClassToUse, InitializeActor, InitializeComponent and FinalizeActor. Subclass it, set it as the builder in the tool’s settings, and the merge asks your rules about every component it is about to take.

ReferenceAwareMeshBuilder is that hook, already written: name the assets that refer to placed actors — quest data, a level Blueprint, a data table — and any actor they point at is left alone instead of being batched away and silently breaking the reference. The same builder can collapse the batch’s collision into a single body, described next.

From a script. Wrap And Batch Actors is a Blueprint and Python callable that performs the same merge without the window, so a pipeline can batch a level unattended. It returns every actor it made - one, or one per HLOD volume - and, like the window, refuses actors from more than one level.

Merged Collision Body — one physics body for a cluster

UMergedCollisionBodyComponent gathers the collision shapes of many components into one aggregate body, so twenty props that answered a trace separately become one body that answers the same way.

It only merges what is safe to merge: shapes are taken from components whose collision profile matches, and the odd one out keeps its own body rather than being folded into an answer it never gave. Instanced components contribute one shape per instance, at the instance transforms. With Dynamic Obstacle on, navigation sees each shape as an area of Area Class instead of as collision.

What it saves, and what it costs. 2 000 small collision boxes become 2 collision-enabled components holding the same 2 000 shapes, and every one of 4 000 test rays that hit before still hits. A ray that misses the cluster costs what it always did. A ray that hits it costs three and a half times more (0.0193 ms to 0.0682 ms), because one aggregate body is tested shape by shape where the broadphase used to reject two thousand separate bodies.

So this is for clutter you count in thousands and do not trace every frame - not for the floor the player walks on. Measured on a map carrying hundreds of such actors, the difference did not show up in a frame at all.

Culling Bounds — bounds that do not follow the geometry

UCullingBoundsComponent decides the box the engine culls and streams an actor by, instead of inheriting it from the geometry. Two ways to use it:

Cook Bounds computes the box once - when the level is cooked - and keeps it, for clusters that never move in game.

The box is what the component itself culls by. Attached components follow it only when their Use Attach Parent Bound is on - Wrap & Batch sets it on everything it creates; on a component you attach yourself, switch it on.

ACullingBoundsWrapperActor is a ready-made actor with the component as its root and a billboard so you can find it in a busy level. Wrap & Batch spawns exactly this actor for each batch.

Three utility windows

Tools → Content Pipeline Tools opens them, and also has a Wrap & Batch… entry that opens the Merge Actors window:

   
Set Texture Channels assemble a texture from single channels of others: pick the source for R, G, B and A and write the result
Draw Texture paint into a render target laid over a level through a plane, and save the result as a texture
Bake Possible NavMesh to Texture rasterise where the navigation mesh reaches into a texture, for tools that need to ask “is this point walkable” cheaply

They are Editor Utility Widgets, so you can duplicate one and change it for your own pipeline.

The two library functions the navmesh tool needs are exposed to Blueprint as well: Pixels To Image (Texture2D) builds a texture from an array of colours, and Image (Texture2D) To Bytes encodes a texture’s source art to PNG, JPEG, BMP or EXR.

Two Blueprint repair helpers, and a warning about them. Two functions that fix Blueprint graphs the current engine refuses to compile are callable from your own scripts under Content Pipeline Tools:

   
Repair Widget Variable Writes finds Set nodes writing to a designer widget variable, which UE 5 forbids and which keep an old Widget Blueprint from compiling
Retarget Deprecated Editor Scripting Calls re-points calls to deprecated editor scripting functions - three from Editor Scripting Utilities, and the VR editor’s selected-actors query - at the stand-ins this plugin provides

Both have a report-only mode - bRemove on the first, bApply on the second: with it false they only count and log what they found, which is how to use them first. With it true they edit the Blueprint and recompile it. They change somebody’s asset, so run them on a checked-in tree and read the log before believing the result.


The example

Content/Example inside the plugin:

The bake, in the engine’s own numbers, on the content you have. Open M_ExampleSubgraph, turn on Window → Stats: 583 pixel-shader instructions and 6 texture samples. Open your baked copy of M_ExampleBake: 268 and 1. Those are the figures this page quotes, measured on these two assets rather than on something you cannot open.

To try Wrap & Batch: open the map, select one cluster, open Merge Actors, choose the Wrap & Batch tab, and watch the outliner.

To put numbers on your own level rather than on this one: the outliner’s count and stat scenerendering (primitives drawn, draw calls) before and after a batch, and ProfileGPU for the frame. Do that before you batch anything you cannot undo — the middle row of the table at the top of this page is what happens when the frame was never the problem.

Limitations

Support

Questions, bug reports and requests: the support link on the product page. Documentation: https://stunder-novikov.github.io/ContentPipelineTools-docs/. Include the engine version and, for a bake problem, the log — the tools name the material and quote the compiler when they refuse to do something.

Licensing

The plugin bundles no third-party libraries. Twelve of its files are derived from Unreal Engine’s own editor sources, because the Wrap & Batch tab is built from the panels and the tool interface the engine’s own merge tools use, and the baked-texture node’s details panel from the customisation the engine applies to its texture expressions. Each of the twelve keeps Epic’s copyright line with ours beneath it, and THIRD_PARTY_LICENSES.md beside this file lists every one against the engine file it is derived from.