A monster cat for Unreal Engine 5.7 with fur solved strand by strand and an idle loop that keeps it twitching when nothing is asking it to.
Full shot, 264 frames at 20 fps, drawn by the software renderer in this repo with nothing driving the character. Two weight shifts, four breaths, three blinks and two ear flicks land in that window, each off its own timer, and the coat re-hangs through all of it.
Same renderer, one frame, no motion blur to hide behind. 117k render strands over an 11148-triangle body. The rim light doing the work on the tips is most of why a coat reads as a coat and not as a fuzzy outline.
The body is 11148 triangles over a 38-bone rig, both generated in code rather than sculpted:
FFRig.h is the bone table, FFCatMesh.h sweeps tubes along the chains and skins them.
Digitigrade, hunched, head carried below the withers and well forward. The eyes are real
geometry and the lids are closed plates concentric with the eyeball, so a blink slides shut on
a fixed radius and cannot punch through the ball however far it is driven.
Over that go two grooms, a short body coat and a longer neck ruff. On top of that, the idle.
Everything except the Unreal glue is portable C++17 in Source/Furfiend/Core/: header only,
namespace ff, no engine types anywhere. That is the point of it. The same headers compile
into the UE module and into the local preview, so the fur and the idle in that gif are the code
the engine will run rather than a stand-in for it.
Same two-tier shape as a real groom. Roughly 15.4k guide strands are simulated; 117k render strands are interpolated off them at draw time and none of their state survives a frame.
A guide is rooted by triangle index plus barycentric coordinates, not by a world position. Every frame it asks the skinned mesh where that point is now and what frame it has, which is what makes the coat ride the surface instead of hanging off the bind pose. The solve is Follow-The-Leader, unconditionally stable no matter what the timestep does, with capsule collision against a 29-capsule proxy fitted to the body and a velocity correction so the chain cannot gain energy. Gravity, wind with a per-strand gust phase, and the body's own motion all arrive through the same integrator.
Render strands borrow the guides' offsets rather than copying their curves, so each one starts at its own root. Clumping pulls their tips toward the dominant guide, which is what gathers the coat into points instead of a nap, and a couple of octaves of per-strand kink keep it from combing flat.
Content/Fur/Furfiend_Coat.abc and Furfiend_Ruff.abc are that same fur baked out of that
same code at the rest pose, 18.3 MB and 6.6 MB under git-lfs. They carry groom_guide,
groom_group_id, groom_root_uv, groom_width and groom_color, which is the set the
engine's Alembic groom importer reads. The guide flag matters most: without it UE decimates
its own guides out of the render strands, and a groom that imports with zero guides renders
perfectly and sits perfectly still. tools/GROOM.md has the attribute table and the import
conversion settings.
Three layers, plus two smaller ones, none synchronised to any other and all of them decided in
C++. ff::IdleDirector keeps a separate RNG stream per layer, so retuning the blink cannot
move where the weight shifts land.
Breathing runs continuously on a 3.2 s cycle that drifts ±12% per breath, with the amplitude drifting ±15% on top. The curve is not a sine, which reads as a pump: a third of the period filling, a 9% beat at the top, and the rest leaking out over what is left. Both ends are smootherstep, so the segments meet flat and the wrap has nothing to catch on.
Blinks fire on an interval redrawn uniformly from 3 to 7 s after every blink. The two lids are offset by up to 18 ms, which eye leads flips each time, and 22% of blinks come out as a double, the two pulses 0.32 s apart.
Weight shifts come every 9 to 16 s and take about two seconds, starting from wherever the last one got to rather than from centre. Fifteen percent of the time the cat settles back the way it came. After every layer has run, the four limb roots are put back where they were, so the hips can lean without walking the paws off the floor.
Ear flicks are per ear on independent 4.5 to 13 s timers, and the flick curve carries past centre on the way back rather than easing to zero. The tail sways on a 6.5 s period that drifts ±18%, tip lagging the base by 0.7 rad.
The Animation Blueprint decides none of this. It reads alphas off UFurfiendAnimInstance and
blends. Content/Character/AnimBlueprint.md lists which variable feeds which node, which
additive poses have to be authored, and what goes wrong if you put a second set of timers in
the graph.
make test builds and runs the suite: 1128 checks
make core every core header into one empty TU at -Werror
make preview the renderer
make render a short headless sequence into out/
make groom re-bakes the two .abc archives
make gif regenerates idle.gif and blink.gif from their pinned seeds
make test and make core want nothing but a C++17 compiler. The preview wants SDL2 and
zlib: it is one binary for both modes, so the headless path links the window code even though
it never calls it. Running headless needs no display, which is just as well, because the
container all of this was made in does not have one. make gif additionally wants ImageMagick,
and make groom wants Alembic, which nothing else in the tree links and which you can skip
entirely since both archives are checked in.
One binary, two modes:
./build/ffpreview
./build/ffpreview --headless --frames 240 --fps 20 --shot flank --wind 45 --out out
--shot takes full, face or flank. --seed replays an idle exactly. --no-fur and
--no-idle isolate one from the other, which is how the coat's motion was measured separately
from the body's. --strands 0.25 when you want frames rather than pixels. At the default
960x540 with 2x supersampling, a full or flank frame with fur and idle costs about 0.6 s; the
face shot is 1.1 s, because at that framing it is nearly all coat.
make gif takes about eleven minutes and reproduces both checked-in gifs byte for byte.
Source/Furfiend/ is a real UE 5.7 module. A character with two UGroomComponents, an idle
component that ticks the director and publishes a frame of it, an anim instance the graph reads
through, binding and tuning helpers, a game mode. HairStrandsCore is a public dependency, and
Config/DefaultEngine.ini turns the skin cache on, which a groom bound to a skeletal mesh
needs in order to find its roots.
None of it has been compiled. There is no Unreal Engine in this container and no way to install
one, so UnrealBuildTool has never run, Furfiend.uproject has never been opened, and
Play-In-Editor has never happened. Everything below the module boundary is shared verbatim with
the preview and is genuinely verified here. Everything at or above it is careful code that
nobody has built.
On a machine that has 5.7: Content/ holds no .uasset files at all yet, so the first job is
a skeletal mesh whose bone names match FFRig.h character for character, since the idle looks
bones up by name and a miss is silent. Then import the two grooms as strands, create a binding
against that mesh for each, wire the four slots on the character, and press Play without
touching anything. docs/QA.md closes with the checklist in order, each step with what its
failure actually looks like; the two worth reading before you start are the guide count coming
back zero and the per-LOD Simulation override, because neither of those says a word when it
goes wrong. Content/Fur/README.md covers the asset wiring and the exact material parameter
names.
The muzzle is blobby under the coat. It is a swept tube with a few lumps on it, and at this fur length the strands hide the shape rather than the shape carrying the strands. Side on it reads as a face. Front on it is soft.
The body is not one mesh. It is a pile of closed shells that interpenetrate at the joints,
which is what the fur roots and the rasteriser want and why there is a burial test in
FFStrands.h at all, but anybody who wants to sculpt on this has a boolean pass to do first.
The paws are stubs. Four legs standing on the floor, no toes on any of them.
The forehead fur stipples up close. face.png shows it plainly: on the tight face camera the
coat over the skull thins into individual dots instead of resolving into hair, which is the
density mask running thin where the muzzle taper starts.
And the loop closes, but not to zero. idle.gif runs 13.2 seconds, which is three whole periods
of the wind gust, and the coat is in the same state at both ends because the gust was made to
repeat instead of wander, so the cut measures quieter than the average frame step inside the loop.
The breathing is the part that does not close and never will: its period is redrawn every cycle
on purpose, so four breaths do not add up to exactly 13.2 s. This seed gets that down to four
milliseconds. docs/QA.md has the numbers, before and after, and what is still not zero.



