Grooming
The groom is the source. Cards are a bake.
A groom is resolution-independent and restylable. Cards are a disposable engine-facing derivative,
re-baked when the budget changes, when an LOD tier is added, or when the tool changes. Everything on
this page follows from that one sentence, including which folder each file goes in.
Make a Hairstyle walks the whole route in order. This page is the reference behind it:
the file format, the import, and the normal transfer.
Where the files live
Hair uses two of the three stages of the Raw / Source / Exported mirror. Raw is for
unprocessed captures and a groom is not one.
Source: the .sir scene, its guides, its density and clumping |
The thing you open, edit and keep. Under Meshes/Character/Hair/, beside the .blend. |
| Exported: the card mesh, the atlas maps | Generated. Never hand-edited, and re-written whole by the next bake. |
Nudge a card in Blender and it is correct until the next re-bake, which overwrites it. So the fix
is either lost, or kept by never re-baking. Then the groom is decoration and every future
budget change is manual work on a few hundred quads.
Nothing checks this. The card mesh cannot say whether the groom it came from still describes
it. A card that is wrong is fixed in Sisir and baked again.
The route
Sisir makes the groom, the cards and the atlas. Blender bakes the channel data, transfers the
normals and runs the checks. Unity gets an .fbx and a texture set, and nothing else.
Sisir
Grooming is guide-based: you style a small number of guide curves, and the strands interpolate
between them. That is what makes a groom restylable. A parting is moved by moving four guides, not
by touching ten thousand hairs.
Cards come out of the same groom. Sisir clusters the strands, fits a strip to each cluster and maps
it into an atlas strip. The groom stays; the cards are its output.
It writes OBJ for the cards, and the atlas maps as ordinary image files.
Why OBJ and not Alembic
We need exactly three things out of Sisir, and OBJ carries all three:
| Positions | v lines. |
| Atlas UVs | vt lines. Which strip a card sits in is the only thing about the texture the mesh has to remember. |
| Separable cards | A card is a connected component of the mesh, so the faces alone separate them. OBJ carries the faces, so this costs nothing. |
Alembic's advantage over OBJ is arbitrary geometry parameters and animation, and we want neither.
We do not want the parameters because we bake the channels ourselves. A per-strand random or a
width that Sisir wrote is a number whose definition lives in Sisir's release notes. Our contract is
arc length along this card, a hash of the quantised root centroid, and a stiffness somebody
decided, and Sisir computes none of those three. Carrying its params across would give us a second
set of numbers that look like ours, mean something else, and disagree silently.
→ Hair Cards
We do not want the animation because there is none to carry. The cards ship static and the movement
happens in the shader, from the channels. → Hair Motion
What is left is a format every tool writes, that opens in a text editor when an import goes wrong.
What OBJ costs, and the three answers
OBJ is unit-less and declares no up-axis. A file that is metres and Z-up is byte-identical to one
that is centimetres and Y-up. That is a real cost and it is answered three times, because one answer
would not hold.
- The import operator pins the convention. Import a groom through the add-on, not through
File ▸ Import, which remembers whatever the last person set. It reads the file as metres and
Yup (what most tools write) and lands it the studio's way,Zup. Scale and Up Axis
are on the operator, in public, for the groom that arrives from somewhere else. The forward axis
is not. It follows the up axis (-ZwithYup,YwithZup), so either lands the file
unrotated. → Bubbly Games/Workflows/3D Models/3D Models > Scale and orientation MSH022measures the groom against a person and against the body. A groom imported at a
hundred times scale looks perfectly ordinary on its own, so the whole card set is measured: its
longest side must be at least a centimetre and no longer than the character is tall. When a
power of ten would fix it, the finding says which: probably centimetres, import it again with
the scale multiplied by 0.01. It is also measured against the mesh namedBodybeside it
(SM_Character_Body_01, never a hat or a proxy with another name). A groom whose cards sit further
from the box round the body than a card is long, and more than 5 cm, is not on the character
(an up axis swapped, or never moved onto it) and is reported as such. When the Body stands up
alongZ, the box is its head (the top 15% of it), so a groom left on the chest is caught
too. A Body in a T-pose, or a flat proxy, has no top slice that is a head, and is measured by
its whole box instead.
→ Hair Cards > What the checks can and cannot seeMSH002catches the wrong fix. The natural repair for a bad import is to scale the object
until it looks right, which bakes a factor into the.fbxand hands it to everything downstream.
MSH002 requireAppliedScaleis already an error, so that export is blocked rather than
shipped. → Modular 3D Assets > What blocks an export
The third is the one worth knowing. The first two stop a bad import; the third stops the bad import
being papered over.
Which end of a card is its root is measured from the Body. A groom metres from the body would
get a verdict that means nothing, and a Flip button beside it that would turn good UVs upside
down. So when the groom is off the character, the flip check says it did not run rather than
guessing. Placement asks about the nearer end of each card, whichever end that is, so a
groom whose atlas is simply upside down is still on the character and still gets its flip
verdict.
Normal transfer from a scalp proxy
Cards lit by their own flat normals read as a pile of planes. Each card catches light as the
quad it is, the ones facing the light go bright together, and the silhouette the alpha works so hard
for is contradicted by the shading behind it.
Blend the normals toward a scalp proxy (a smooth cap over the skull) and the same cards read
as one volume. The geometry does not change. Only what it tells the light does.
This is the biggest look win on the page for the least work, and it is a Blender step, so the
import does it on the way in when you name the Scalp Proxy in its sidebar. It runs on the joined mesh,
or on every object in turn when Join is off or the join fails. There is nothing in the cards that
says which way the skull under them faces, so with no proxy named the import says it skipped the
transfer rather than guessing one.
Transfer Hair Normals does the same job later, from the Hair Cards panel. It opens a dialog: pick
the proxy from a searchable list of the scene's objects, set Blend, and tick Apply Now only
when the look is settled. A live modifier can still follow a re-shaped scalp, and applying can't be
undone. The field opens on the proxy the cards already borrow from, and empty otherwise. It
never opens on the last groom's scalp or on a guess at the Body, because a wrong guess looks right until
the head is lit from the side, and an empty field just asks.
Pressing it again updates the transfer rather than stacking a second, because two transfers from two
proxies blend into a look nobody chose. It finds the existing transfer (a Data Transfer modifier carrying custom normals) even if you renamed
it, switches it back on if it was off, and keeps anything else it was set up to transfer.
The proxy is never counted as cards, whatever it is called: naming it here is saying it is the
scalp. It is only measured from (by the flip and placement checks) if its name says Body.
→ Hair > Which mesh is the cards, and which is the Body
requireNormalTransfer is on in the rules for the same reason: it is cheap, it is invisible when it
is missing until someone lights the head, and by then the cards are already in a prefab. MSH022
checks it on the mesh that ships, so a live transfer counts on a machine that exports with
modifiers and not on one that exports without them. When the cards ship flat, the finding names the
one repair that applies, because each sends you somewhere different:
| The finding says | Do |
|---|---|
| There is no transfer | Run Transfer Hair Normals |
| The transfer names no source object | Pick the proxy in it, or run the button again |
| The transfer is switched off in the viewport | Switch it on. The export takes the viewport's result |
| This machine exports without modifiers | Apply the transfer, or export with modifiers on |
| It is on, it is exported, and still nothing | Check that it transfers Custom Normals from the proxy |
None of them has a Fix button: which proxy the normals come from is a look decision, and the whole
body is the wrong answer for hair that hangs past it.
Blender imports an OBJ's normals as custom normals, and a file's normals are the flat card
normals the transfer exists to replace. Left in, they would pass the check that asks whether the
cards ship transferred normals, while lighting as exactly the pile of planes the check is for. So
the import clears them first. A groom brought in through File ▸ Import keeps them, and passes
that check wrongly. One more reason to import through the add-on.
Export through the add-on
Always export through our add-on, never Blender's own FBX exporter. This is the existing house
rule and hair does not get an exception. → Bubbly Games/Workflows/3D Models/3D Models > Import and export
It runs the checks first, and it knows where the file belongs. MSH022 is among them, and like
every new rule it lands as a warning. It reports a flipped card, a card over two atlas strips
and a budget overrun, and it does not block. → Check Your Work > When a rule is wrong
Hair has a second reason. Only the add-on's export ships the attribute flagged for render. Unity
reads only the first colour set, and Blender's own exporter does not control which one that is. The
add-on puts the render-flagged attribute first for the export and leaves your mesh unchanged.
→ Hair Cards > The render flag decides which attribute ships
Overdraw is not measured on export. Press Measure Hair Overdraw before exporting.
→ Hair > Overdraw is an average, and it is measured on request
See also
- Hair: the three tiers, and which one a hairstyle is
- Hair Cards: the channel contract this bake writes, and the atlas it maps into
- Hair Motion: what the shader does with the channels
- Make a Hairstyle: the steps, in order
- Project Folder Structure > The one idea: the
Raw/Source/Exportedmirror - Bubbly Games/Workflows/3D Models/3D Models: the Blender-side conventions that are not hair-specific
- Modular 3D Assets > What blocks an export:
MSH002and the other five errors - Check Your Work > When a rule is wrong:
MSH022is a warning, like every new rule