INTEGRATIONMATERIALS AND SHADERS
Materials and shaders
Every deformed body, wheel, pane and tyre is drawn by a material. The plugin provides the functions that move the vertices, paint the wear and draw the dirt, and it provides the data each mesh needs to find its place in the atlas. This page is the reference for those pieces. For the wiring walk-through, with the ten connections of a painted body, see Quick start. For the idea behind it, see the materials part of Core concepts.
The material functions
Section titled “The material functions”All functions are in the Materials folder of the plugin content, /Crushform/Materials. Their inputs and outputs are the pins that a material or a look connects to. The pin names are exact, spaces included.
| Function | Inputs | Outputs | Builder |
|---|---|---|---|
MF_CF_Deform | Normal, Crumple Tiling, Fold Amplitude, Fold Wavelength, Fine Fold Wavelength, Fine Fold Strength | World Position Offset, Normal, Wear Depth, Scratch | UCFContentLibrary::BuildDeformFunction |
MF_CF_Paint | Base Color, Roughness, Metallic, Wear Depth, Primer Color, Metal Color, Scratch Tiling, Dust Color, Mud Color, Dirt Strength, Snow Color, Built-in Look, Scratch, Scratch Line Tiling | Base Color, Roughness, Metallic | BuildPaintFunction |
MF_CF_DirtAmount | None | Dust, Mud, Snow, Wet, Dirt Fresh, Dirt Ranks, Pattern Position, Pattern Normal | BuildDirtAmountFunction |
MF_CF_WheelDirt | Base Color, Roughness, Metallic, Dust Color, Mud Color, Dirt Strength, Snow Color, Built-in Look | Base Color, Roughness, Metallic | BuildWheelDirtFunction |
MF_CF_WheelDirtAmount | None | Dust, Mud, Snow, Wet, Dirt Ranks, Pattern Position, Pattern Normal | BuildWheelDirtAmountFunction |
MF_CF_GlassCrack | Base Color, Opacity, Roughness, Normal, Built-in Look | Base Color, Opacity, Roughness, Normal | BuildGlassCrackFunction |
MF_CF_GlassCrackData | None | Crack Mask, Crack Frost, Crack Tilt, Crack 0 to Crack 3, Crack Radii, Pane U, Pane V | BuildGlassCrackDataFunction |
MF_CF_TireDeform | AxleAxis, HalfWidth, BulgeAmount, RimRadius, RimFalloff, FlattenScale, MaxVertDisp | Result | BuildTireDeformFunction |
MF_CF_TrunkBend | Not set by the plugin’s code | World-space World Position Offset | Made in the editor, not by code |
MF_CF_Deform works in tangent space, so the normal of a wired material must be tangent space too. MF_CF_Paint and MF_CF_WheelDirt take the Built-in Look switch, a static bool that is true by default. The look settings use that switch, as the Looks section below explains.
A tyre material has a scalar parameter RimRadius, the rim’s radius in mesh units. MF_CF_TireDeform takes it as an input, and UCFTireComponent reads the same parameter to know how far it may push a wheel off an obstacle. The first material slot of the tyre mesh that sets RimRadius itself wins, so a rim material that only inherits the master’s default does not hide the tyre’s own value. If no slot sets it, the first slot that has the parameter supplies its default. UCFTireProfile::RimRadiusOverride, in cm, replaces it.
MF_CF_TrunkBend bends the trunk of a tree. Its own description reads “Parabolic trunk bow driven by CPD 0..2; outputs world-space WPO”. Its two masters add it after the wind offset.
The builders rebuild a function from scratch in the editor. They keep the pin ids by name, and they leave the function unsaved. Save the function after a rebuild.
Masters, collection and atlases
Section titled “Masters, collection and atlases”| Asset | Role |
|---|---|
/Crushform/Materials/M_CF_VehiclePaint | The paint master: Clear Coat, with Paint Color over Base Texture where Paint Mask is set |
/Crushform/Materials/M_CF_Wheel | The wheel master: Default Lit, through MF_CF_WheelDirt and under the switch Tire Deform |
/Crushform/Materials/M_CF_GlassShard | The translucent clear glass of a shard |
/Crushform/Materials/MPC_CF_Dirt | The dirt look, shared by every body and wheel. Its parameters are read by the dirt functions |
/Crushform/Atlas/RT_CF_LatticeAtlas | The lattice atlas. Every deformed mesh finds its slot in it |
/Crushform/Atlas/RT_CF_DirtAtlas and RT_CF_DirtFreshAtlas | The dirt atlases, of the lattice atlas’s size |
/Crushform/Atlas/RT_CF_ScratchAtlas | The scratch atlas: an RG8 render target of the lattice atlas’s size, with each point’s scratch line cover and direction |
The masters take their textures from the plugin’s T_CF_Default* textures, and each texture has a plain and a virtual parameter. The parameter names are fixed. An instance sets them by name, so renaming one breaks every instance that sets it.
Custom Primitive Data
Section titled “Custom Primitive Data”Each mesh carries its own data in Custom Primitive Data, in blocks that the material functions read. The offsets of the blocks are project settings in Project Settings > Plugins > Crushform > Atlas. In Unreal Engine 5.6 to 5.8 a primitive has 36 floats (FCustomPrimitiveData::NumCustomPrimitiveDataFloats).
| Block | Offset setting | Floats | Default offset | Contents |
|---|---|---|---|---|
| Deformed mesh | UCFSettings::CustomDataOffset | 18 | 0 | The atlas slot, the mesh-to-root columns, the translation, the inverse cell size, the quantum and the resolution |
| Wheel | UCFSettings::WheelCustomDataOffset | 2 | 34 | Two whole numbers: the dust and mud amounts, and the snow and wet amounts with the layer ranks |
| Glass pane | UCFSettings::GlassCustomDataOffset | 16 | 18 | Four crack records of four floats each |
| Tyre | UCFSettings::TireCustomDataOffset | 32 | 0 | Four contact records of eight floats each |
The deformed block holds, in order: the slot at index 0, the three columns of the mesh-to-root rotation and scale at 1 to 9, the mesh origin in lattice space at 10 to 12, one over the cell size at 13 to 15, the quantum at 16 and the packed resolution at 17. MF_CF_Deform reads the same indices, so the layout is fixed by the code, not by the asset.
| Atlas | ||
|---|---|---|
| Atlas | RT_CF_LatticeAtlas | |
| Slot Width | 256 | |
| Slot Height | 8 | |
| Custom Data Offset | ||
| Wheel Custom Data Offset | ||
| Tire Custom Data Offset | ||
| Glass Custom Data Offset | ||
| Scratch Atlas | None | |
With the defaults, the blocks sit at 0 to 17 for the deformed mesh, 18 to 33 for a glass pane and 34 to 35 for a wheel. A tyre has its own 0 to 31, which never shares a mesh with the body.
The plugin refuses a block that does not fit or that overlaps another. The glass block must fit the primitive and stay clear of the deformed block. The tyre block must fit and stay clear of the wheel block. A refused glass offset is logged as an error, and the panes get no cracks:
%s: Glass Custom Data Offset %d does not fit beside the deformed block at Custom Data Offset %d; the panes get no cracksChanging an offset
Section titled “Changing an offset”When a block moves, the material functions that bake its indices must be rebuilt. The editor does that when you change one of the four offsets in the project settings. It rebuilds the eight functions that bake them: MF_CF_Deform, MF_CF_Paint, MF_CF_DirtAmount, MF_CF_WheelDirt, MF_CF_WheelDirtAmount, MF_CF_TireDeform, MF_CF_GlassCrack and MF_CF_GlassCrackData. The notice in the editor says:
Crushform: the Custom Data Offsets are in the material functions. Save the Crushform material functions to keep them.The rebuild is not saved. Save the functions, or the old indices come back when the editor reloads them. While one of the eight functions is open in the Material Editor, the editor rebuilds none of them. The console command Crushform.RebakeOffsets runs the same rebuild by hand, and it exists in the editor only.
Wiring a material
Section titled “Wiring a material”Wiring connects a material to the plugin. Three steps do the work:
- The vertex offset and the normal go through
MF_CF_Deform. - The base colour, roughness and metallic go through
MF_CF_Paint. Pass no paint function for glass and interiors that share a mesh with the body. They dent with it and keep their own look. - The material’s Max World Position Offset Displacement is raised to the value you pass in.
A painted material also gets the scalar CF_PaintWear, which is 1 by default. An instance that shares the body’s master, but should not wear, sets it to 0. For a material outside /Crushform/ and /Engine/, wiring also applies the project’s looks to it.
From Python, the editor exposes the same call:
import unreal
material = unreal.load_asset(MATERIAL_PATH) # the material to wire, in your projectdeform = unreal.load_asset("/Crushform/Materials/MF_CF_Deform")paint = unreal.load_asset("/Crushform/Materials/MF_CF_Paint")bound = 60.0 + unreal.CFContentLibrary.get_default_fold_amplitude()unreal.CFContentLibrary.wire_material(material, deform, paint, bound)Replace 60.0 with the Max Displacement of your profile. The fold amplitude comes from the function, not from the profile.
The same call is a Blueprint function of the editor module, so an editor utility Blueprint can call it. The graph below wires a material with the default fold amplitude added to a Max Displacement. Its A pin holds 60 cm, the class default of UCFProfile::MaxDisplacement. Set it to the value of your profile. The material itself goes in the Material pin, which the graph leaves for you to set.
Wiring is refused, and the material is left as it was, when the material takes a world-space normal, or when it uses material attributes and nothing feeds them. The log names the material in each case.
Nanite and Max World Position Offset Displacement
Section titled “Nanite and Max World Position Offset Displacement”The Max World Position Offset Displacement of a material is a bound. The renderer uses it to know how far the vertices of the mesh may move. The bound must be at least the profile’s Max Displacement plus the material’s fold amplitude. The folds move vertices past the dents. The default fold amplitude is 3 cm (CFContent::DefaultFoldAmplitude), and get_default_fold_amplitude() returns it.
On Nanite the bound matters more. Cluster culling grows its bounds by it, so a bound that is too short lets the renderer cull a dented panel near the edge of the screen. Wiring raises the bound when it is too small. A material that already has an offset and a bound of its own gets the displacement added to its bound. Set it by hand only when you do not wire.
The tyre masters keep Always Evaluate WPO off. UCFTireComponent turns the offset on for each wheel that has a deformer, and a material that always evaluates it makes that switch do nothing. The wheel master’s bound is 16 cm.
A skeletal mesh needs its material marked Used with Skeletal Mesh under Usage, or its shaders are not compiled and it does not bend.
A look replaces the built-in dirt or crack drawing of a function with a material function of your own. Three settings choose the looks, in Project Settings > Plugins > Crushform > Look:
| Setting | Replaces | Required output |
|---|---|---|
UCFSettings::BodyDirtLook | MF_CF_Paint | Base Color |
UCFSettings::WheelDirtLook | MF_CF_WheelDirt | Base Color |
UCFSettings::GlassCrackLook | MF_CF_GlassCrack | Opacity |
| Look | ||
|---|---|---|
| Body Dirt Look | None | |
| Wheel Dirt Look | None | |
| Glass Crack Look | None | |
The look’s inputs and outputs are matched to the function’s pins by name, case not mattering, spaces mattering. A look input that no pin can feed fails the wiring, unless you tick Use Preview Value As Default on that input. A look without its required output is refused. The log names the missing output:
WireLook: %s lacks the output "%s" that the look of %s cannot do withoutThe look’s Normal input is always fed. It takes the built-in call’s normal output, or what feeds the normal pin, or a flat tangent-space normal. A look’s Normal output is used too. A built-in call with a Normal output takes it as its own normal. Otherwise it goes in front of the Normal input of MF_CF_Deform in a body, or in front of the material’s normal. The paint of a body has no Normal output, so a body look’s normal goes in front of MF_CF_Deform.
| Look | Replaces | Data function | Inputs it may have | Outputs it may replace |
|---|---|---|---|---|
| Body Dirt Look | MF_CF_Paint | MF_CF_DirtAmount | Base Color, Roughness, Metallic, Normal; Dust, Mud, Snow, Wet, Dirt Fresh, Dirt Ranks, Pattern Position, Pattern Normal | Base Color (required), Roughness, Metallic, Normal |
| Wheel Dirt Look | MF_CF_WheelDirt | MF_CF_WheelDirtAmount | Base Color, Roughness, Metallic, Normal; Dust, Mud, Snow, Wet, Dirt Ranks, Pattern Position, Pattern Normal | Base Color (required), Roughness, Metallic, Normal |
| Glass Crack Look | MF_CF_GlassCrack | MF_CF_GlassCrackData | Base Color, Opacity, Roughness, Normal; Crack Mask, Crack Frost, Crack Tilt, Crack 0 to Crack 3, Crack Radii, Pane U, Pane V | Opacity (required), Base Color, Roughness, Normal |
An input of a look is fed from an output of the built-in call with the same name, or from an output of the data function with that name. An input that neither can feed fails the look, unless it has Use Preview Value As Default ticked:
WireLook: %s has the input "%s", which nothing can feed; name it after a pin of %s or %s, or tick Use Preview Value As DefaultA material with several calls of the built-in function gives the look to the first call only. The log warns about the others.
Changing a look setting applies it to every saved material of the project that calls the function. The materials it changes are recompiled and marked dirty, but not saved, so save them to keep the new look. A material open in the Material Editor is skipped with a warning. Close it and run the console command Crushform.RewireLooks, which does the same by hand. The command exists in the editor only.
The dirt layers are baked into the dirt functions. Changing Dirt Layer Order, or a channel’s blend or Z order, rebuilds MF_CF_Paint and MF_CF_DirtAmount in the editor at once. Crushform.RebakeDirtOrder does the same by hand.
Cook rules
Section titled “Cook rules”The cooker sees a soft reference in a settings object only from the text of an ini file. A default set in a settings constructor does not reach it. So the plugin adds, by hand, every package that a Crushform settings object soft-references. That covers the atlases, the default profile, the surfaces asset, the dirt collection and the look functions. It does this for every settings class of the plugin’s runtime modules, and it reads the class defaults, where the constructors put their defaults. Packages referenced only from editor-only properties are not added.
The effect is that a look or an atlas that a setting points at reaches the game, even when no map uses it.
The content library
Section titled “The content library”UCFContentLibrary is the library behind the builders and the wiring above. It is in the CrushformEditor module, so it exists in the editor only. Its functions are static and in the Crushform|Content category: Python and editor utility Blueprints call them. The library has 24 functions that Blueprint can call. Those not covered above:
| Function | What it does |
|---|---|
WireGlassMaterial | Routes a translucent glass material’s Base Color, Opacity, Roughness and Normal through MF_CF_GlassCrack, keeping what fed them. World Position Offset is left as it is. Glass takes this call instead of WireMaterial, and a glass material gets no paint function. Refused for a material that uses material attributes, a world-space normal, or a function that lacks a pin. Outside /Crushform/ and /Engine/ it then gets the project’s glass look. |
ApplyLooks | Gives a material the three project looks, each to the built-in call of its target through the look wiring. An empty setting takes the look out again. WireMaterial and WireGlassMaterial call it at their end, and Crushform.RewireLooks calls it for each saved material. Materials in /Crushform/ and /Engine/ are left alone. |
RepointFunctionCalls | Points every call of one function in a material to another function, keeping the links by pin name. Returns the number of calls changed. Use it to move a material from its own tyre function to MF_CF_TireDeform. |
BuildVehiclePaintMaterial | Rebuilds M_CF_VehiclePaint, the clear-coat master of a body. It does not wire Crushform: call WireMaterial next. |
BuildWheelMaterial | Rebuilds M_CF_Wheel, with MF_CF_TireDeform in World Position Offset and MF_CF_WheelDirt on the colour, roughness and metallic. |
BuildGlassShardMaterial | Rebuilds M_CF_GlassShard: translucent, two-sided, clear glass with brighter edges. |
BuildGlassShards | Fills a glass definition’s pieces with one tile of shards, each drawn from a list of static meshes. The glass page covers the shards. |
BuildGlassShardMesh | Rebuilds a static mesh as one glass shard: a 1 cm slab of a convex polygon of 3 to 7 corners, about 100 cm across, with a box collision. The same seed gives the same shard. |
BuildDirtLook | Adds the dirt look parameters that MPC_CF_Dirt lacks, at their defaults. It never changes a parameter that exists, because its value is the project’s tuning. A parameter that no dirt function reads is named in a warning. |
SetSkinnedSectionDisabled | Switches one section of a skeletal mesh off or on in one LOD, with the same result as the Disable box of the mesh editor. |
GetAtlasPath, GetDirtAtlasPath, GetDirtFreshAtlasPath, GetScratchAtlasPath, GetDirtLookPath | The long package names of the lattice atlas, the dirt atlas, the dirt fresh atlas, the scratch atlas and the dirt look collection, as set in the project settings. |
GetDefaultAtlasSize | The size of an atlas of the project’s slot size, 4 by 32 slots. |
The other functions in the library are the builders and wiring functions described in the sections above.
Check that it works
Section titled “Check that it works”- Open a wired body material in the Material Editor. The
MF_CF_Deformcall feeds World Position Offset and Normal, andMF_CF_Paintfeeds Base Color, Roughness and Metallic. - Run
Crushform.RebakeOffsetsafter you change an offset. The notice tells you to save the Crushform material functions. Save them, then reopen one and check its pins. - In Play In Editor, hit the body with
Crushform.DebugImpact. The dent shows in the material, and the paint wears where it scraped.
Troubleshooting
Section titled “Troubleshooting”The dirt look is refused
Section titled “The dirt look is refused”Cause. The look has no Base Color output, or it has an input that no pin can feed. The log says which:
WireLook: %s has the input "%s", which nothing can feed; name it after a pin of %s or %s, or tick Use Preview Value As DefaultFix. Rename the input to match a pin, or tick Use Preview Value As Default on it. Then wire the material again.
The cracks on a glass pane are wrong after an offset change
Section titled “The cracks on a glass pane are wrong after an offset change”Cause. The rebuild of MF_CF_GlassCrack or MF_CF_GlassCrackData was not saved, so the materials still read the old indices. Or the glass offset now overlaps the deformed block: the rebuild is refused and logged, and that function keeps its old indices.
Fix. Save the Crushform material functions. If the log names an overlap, move GlassCustomDataOffset so that its 16 floats fit clear of the deformed block, as the Custom Primitive Data table above shows.
A look is not applied to a material
Section titled “A look is not applied to a material”Cause. The material is not saved, or it is in /Crushform/ or /Engine/, which the plugin leaves alone. The look also does not apply to a call inside a material function of your own.
Fix. Save the material, then run Crushform.RewireLooks. Put the call in the material itself, not in a function of your own.