Cloth and hair¶
Six scenes of things that are soft and thin:
- six fabrics falling on tables and flying as banners
- a bed made of three layers of cloth
- a hammock and a tennis net catching balls
- a runner in a cape going through a curtain
- a hair catalog: every hair type from 1A to 4C, and an afro, a puff, a high-top fade, a twist-out, bantu knots, box braids, cornrows, locs and two-strand twists, on heads turning in the wind
- a stress test that shows what cloth costs
Cloth never passes through cloth, not even through itself, and hair never goes into the head. That is the engine's default, and a panel switches it off so you can see and time the difference.
The demo teaches:
- cloth on Jolt soft bodies and the engine's clipping protection (docs/CLOTH.md)
- fabric presets, and a fabric shader with weave, sheen and thread gloss
- guide-strand hair drawn on the GPU (docs/HAIR.md)
- skinned cloth with back-stops, colliders only cloth sees, and wind
- a settings panel that works with a controller
Start here for capes, flags, curtains, bedding, sails, nets, trampolines, hair and fur, or any game where something soft has to meet something solid without clipping. Everything is built from simple shapes, so no Synty assets are needed.
Run it¶
The executable is cloth_demo (kke_add_game(cloth_demo ...) in
CMakeLists.txt). The root CMakeLists.txt only adds it
when KKE_ENABLE_JOLT is on, which is the default.
cmake --build build --target cloth_demo
cd build/bin
./cloth_demo
KKE_SKIP_INTRO=1 KKE_CLOTH_SCENE=hair ./cloth_demo # straight to the hair
KKE_BENCHMARK=1 ./cloth_demo # tours every scene and records frame times
| Variable | Effect |
|---|---|
KKE_CLOTH_SCENE=fabrics\|bed\|nets\|cape\|stress\|hair |
Start in that scene |
KKE_CLOTH_PROTECTION=full\|basic\|off |
Start with that protection |
KKE_CLOTH_WIND=<m/s> |
Wind speed (default 4, and 3 in the hair scene) |
KKE_CLOTH_COUNT, KKE_CLOTH_RES |
Stress scene: sheets, vertices per side (default 16, 24) |
KKE_HAIR_GUIDES, KKE_HAIR_PER_GUIDE |
Hair scene: guide strands per head, hairs drawn per guide (default 160, and 0 = as the style draws) |
KKE_HAIR_MOTION=0..1 |
Hair scene: hair physics, 1 natural (default), 0 solid (no simulation) |
KKE_HAIR_DETAIL=0.05..1 |
Hair scene: the share of hairs drawn (default 1, 0.5 on phones), for phones and slow GPUs |
KKE_HAIR_SHOW=types34\|styles\|braids\|types12\|classic |
Hair scene: the catalog's page (default types 3 and 4) |
KKE_HAIR_STYLES=4c,afro |
Hair scene: exactly these heads, from hairStyleNames() and hairstyleNames() |
KKE_CLOTH_GPU=0\|shared |
Search for cloth pairs on the CPU only, or on the graphics queue (docs/CLOTH.md, "On the GPU") |
KKE_CLOTH_GPU_CHECK=1 |
Search on the GPU and the CPU and log any difference |
KKE_CLOTH_TOUR=1 |
Visit every scene for 14 s each (on by itself under KKE_BENCHMARK) |
Rebindings are saved to cloth_demo_input.json (the name passed to
InputModule in main.cpp).
Controls¶
Every binding is a rebindable action in the "Cloth" group, made in
ClothDemoModule::defineInput (ClothDemoModule.cpp).
| Action | Keyboard / mouse | Controller |
|---|---|---|
Next scene (cloth.scene) |
Tab | RB |
Previous scene (cloth.scene_back) |
` | LB |
A scene directly (cloth.scene1..6) |
1 to 6 | |
Drop everything again (cloth.redrop) |
R | A |
Clipping protection: Full, Basic, Off (cloth.protection) |
P | X |
Gusts on or off (cloth.gusts) |
G | Y |
| Orbit, pan, zoom | left drag, right drag, wheel | right stick, d-pad |
| Settings panel | F3, or click it | View |
The settings panel (RmlUi, on the right) has the scene, the protection, the wind, each scene's own settings, and a Cost section. The Cost section shows the physics step, the protection pass and the mesh rebuild, the contacts and undone crossings, and for hair the guide and hair counts and the upload. The hair scene also has "Hair physics" (1 natural, 0 solid: no simulation, the hair turns with the head as styled) and "Hair detail" (the share of hairs drawn), both live, for slower devices. F1 shows the engine's developer panels.
The scenes¶
| # | Scene | What to look at |
|---|---|---|
| 1 | Fabrics | Satin, leather, wool, denim, cotton and silk (left to right). Each is dropped on a little table, and each flies as a banner in gusty wind. Silk floats down last and drapes softest. Denim and leather fold stiffly. The weave (twill, satin, knit) shows up close. |
| 2 | Bed | A wool blanket, then a silk sheet (2.5 s), then a denim throw (5 s): layers on layers, folding on themselves. This is the hardest case for the protection (CLOTH.md, "Known limit"). |
| 3 | Nets | A hammock catching balls, and a tennis net stopping shots at 15 to 21 m/s. |
| 4 | Cape | A runner with a satin cape running through a linen curtain. The cape is skinned to the body, and back-stops keep it off the back. |
| 5 | Stress | Sheets of cotton over balls, dropped again every 5 s. Set the count and resolution in the panel. |
| 6 | Hair | A hairdresser's catalog on heads that turn, nod and now and then shake, in gusting wind. Show picks the page: types 3A to 4C (ringlets to tight coils that shrink and spring), hairstyles (afro, puff, high-top fade, twist-out, bantu knots), braids and locs (box braids, cornrows, locs, two-strand twists), types 1A to 2C, or the classic long, wavy, curly and short. Set the guides per head and the hairs drawn per guide in the panel. |
How it works¶
Startup and the frame¶
main.cpp sets the studio mood (an even light, which fabric
reads best in), then adds the modules: input, RmlUi, an orbit camera with
controller controls, ClothDemoModule and the DemoPanelModule.
ClothDemoModule::init reads the variables, builds the first scene, then
defines the input actions and the panel.
Each scene is a new kke::RigidWorld (clear()), so nothing of the last
scene lingers in Jolt. fixedUpdate runs at 60 Hz. It sets the wind
(steady or gusting: two slow waves and a flutter), runs each scene's
events (the bed's later layers, the nets' balls and shots, the runner, the
heads), then steps the world. update rebuilds the cloth meshes from
clothPositions and clothNormals, and sends each head's guide strands
to its HairRenderer. render and renderShadow draw it all.
Cloth¶
addCloth passes a ClothDesc to RigidWorld::addCloth with the protection
chosen in the panel. Changing the protection rebuilds the scene, because
tethers and back-stops are built with the cloth. Sheets are drawn with
DynamicMeshRenderer::drawCloth (the fabric shader, both sides). Nets
are threads with no triangles, so updateMeshes builds a ribbon per
thread, facing the camera and at least a pixel wide.
- Fabrics:
clothFabric(name)presets. The drapes getClothDesc::wind = 0.1(sheltered, to show how each fabric falls). The banners are pinned along their top edge and get the full wind. - Bed: boxes for the frame, mattress, headboard and pillows. The blanket is 46 x 46 vertices, the sheet 32 x 32 and the throw 22 x 22, added as the scene runs.
- Nets:
clothNetgrids pinned along their frame.contactMassmakes the hammock (8 kg) and the tennis net (5 kg) feel as heavy as their frame holds them, so a ball stops instead of punching through. - Cape: the runner's torso, legs and arms are kinematic capsules with
clothOnlyset, moved every step (stepRunner). The cape is skinned to the torso withmaxDistance2 m and a 1 cm back-stop, and its top edge is pinned.setClothJointspasses the torso matrix each step.
Hair¶
buildHair makes a head per style on the catalog's page (the panel's
Show: types 3 and 4, hairstyles, types 1 and 2, or the classic four), with
skin tones and hair colours varied along the row. Each head is three
things:
- a kinematic sphere with
clothOnlyset: only cloth and hair feel it - a static neck capsule, and solid shoulders
- a
HairDescfromhairstyleOnHead(160 guides, off the face): the type, and for a hairstyle its cut (lengths) and ties
stepHeads turns each head about its neck (yaw, nod, tilt, and a quick
shake now and then). It then moves the sphere with moveKinematic and
calls setHairJoint with the head's matrix. The world does the rest: the
strands' roots follow the head, the strands swing and blow, and the
sphere and shoulders keep them out.
Drawing: a HairRenderer per head is built once from the HairDesc. Every
frame update passes it hairPositions and the head matrix, and the GPU
builds the hairs around the head's 160 guides (32 or 40 each), and for
types 3 and 4 the coils round every hair. The head mesh paints the scalp
the hair's root colour near every root, so no skin shows between hairs
(but the parts between bantu knots do), and paints the high-top's fade.
Under braids, cornrows and locs it paints only where they lie on the
scalp (the rest pose's pieces within a braid's width of it), so the parts
between them and the skin between cornrows show; those heads get a finer
mesh for it.
The panel and the input¶
buildPanel makes one section per concern. Rows that belong to one scene
hide with sectionIf. The scene and protection choices are Refs that
read the live value, so a key press shows in the panel too. readInput
turns the actions into scene changes, drops and toggles.
Design decisions¶
- One solver for cloth and hair: Jolt's soft bodies. They already
collide with every rigid body and character in a
RigidWorld, and they run on the job system. FEMFX (volumes of tetrahedra) suits neither thin sheets nor strands. - No clipping by default. Kees's rule: clipping is off unless a
developer turns it on to save time. So
ClothProtection::Fullis the default, and the demo can show Basic and Off side by side on the same scene. - Hair is distance constraints, not rods. Cosserat rods were tried: a quick head turn could flip a strand's rest curve upward, because nothing turns the first rod's twist with the head. Distances have no twist to lose, and cost 2.5 times less (HAIR.md).
- Simulate a few, draw many. 160 guides a head are simulated. The thousands of hairs are built on the GPU, so their only CPU cost is the 0.1 ms upload.
- Everything visible is anti-aliased without tricks. Threads and hairs are at least a pixel wide, and the weave fades to its average where a thread gets smaller than a pixel. There is no dithering and no temporal accumulation.
- A new world per scene. It is simpler than removing everything, and nothing from one scene can wake or slow another.
Tuning¶
| What | Where | Effect |
|---|---|---|
clothFabric(...) presets |
engine/src/Cloth.cpp | density, stretch, shear, bend, drag, thickness, look of each fabric |
hairStyle(...) presets |
engine/src/Hair.cpp | length, segments, curl, coils, shrinkage, bend, hold, drawn hairs of each type |
hairstyleOnHead(...) |
engine/src/Hair.cpp | each hairstyle's type, cut and ties |
drape.wind = 0.1 |
buildFabrics |
how sheltered the table drapes are |
| grid sizes (28 x 28 drapes, 46 x 46 blanket...) | each build* |
detail against cost (cost grows with vertices) |
contactMass 8 and 5 kg |
buildNets |
how hard the hammock and net stop balls |
maxDistance 2, backStop 0.01 |
buildCape |
how far the cape may swing, how close to the back it may come |
m_hairGuides 160, m_hairsPerGuide 0 (the style's) |
panel, KKE_HAIR_* |
simulation cost against how full the hair looks |
m_hairMotion 1, m_hairDetail 1 |
panel, KKE_HAIR_MOTION, KKE_HAIR_DETAIL |
CPU and GPU cost against how the hair moves and how full it is |
| shake and turn amplitudes | stepHeads |
how hard the heads move (the panel's Head motion scales them) |
| gust waves | fixedUpdate |
how the wind varies |
Engine features it uses¶
| Feature | Header / module | Doc |
|---|---|---|
| Cloth, fabrics, clipping protection | kke/Cloth.h, RigidWorld::addCloth |
CLOTH.md |
| Hair: guide strands, styles, scalp | kke/Hair.h, RigidWorld::addHair |
HAIR.md |
| Hair drawn on the GPU | kke/HairRenderer.h (shaders/hair.*) |
HAIR.md |
| Rigid bodies, kinematic and cloth-only colliders | kke/RigidWorld.h |
MOVEMENT.md |
| Meshes drawn from code, fabric shader, sphere impostors | kke/SphereImpostors.h (DynamicMeshRenderer, SphereImpostorRenderer) |
CLOTH.md |
| Settings panel with controller support | DemoPanelModule |
DEMO_PANEL.md |
| Rebindable actions, button prompts | InputModule, kke/InputMap.h |
INPUT.md |
| Mood: sky, light | Application::setMood |
MOODS.md |
| Benchmark recording | KKE_BENCHMARK, BenchRecorder |
BENCHMARKS.md |
Assets¶
None: shapes and the studio mood only.
Make a game like this¶
- Copy the folder.
cp -r games/cloth_demo games/my_game, rename the target and namespace, and addadd_subdirectory(games/my_game)inside anif(KKE_ENABLE_JOLT)block in the rootCMakeLists.txt. - Give your character a cape or hair. A cape: copy
buildCape(a grid pinned at the top, skinned to the torso,setClothJointsevery step). Hair: copy one head frombuildHairandstepHeads, and use your character's head bone matrix as the head matrix. - Give cloth something to lean on. Put
clothOnlycapsules on the limbs that cloth should feel. Characters fromaddCharacteralready carry one, so walking into a curtain pushes it aside. - Pick fabrics and styles. Start from a preset, change the colour, and change one behaviour number at a time.
- Budget it. Keep a cape around 16 x 20 and a blanket around 32 x
- Use 100 to 200 hair guides per head. Use Basic protection for
background cloth that never folds onto itself. Check the Cost section,
or
kke_bench --filter cloth_/hair_. - Read next: CLOTH.md, HAIR.md, DEMO_PANEL.md.
Pitfalls the code shows:
- Move kinematic colliders with
moveKinematic(notsetTransform) once the scene runs. A teleport gives the cloth no velocity to react to, and it can end up inside. - Call
setClothJointsorsetHairJointevery step, beforestep, with the same matrix the collider got. Otherwise cloth or hair lags one step behind its body. - Changing the protection means rebuilding the cloth: tethers and back-stops are part of it.
- Paint the scalp the hair's colour. At any density there are gaps between drawn hairs.
Files¶
| File | What is in it |
|---|---|
| main.cpp | The application, the mood and the module list |
| ClothDemoModule.h | The module, the scene list, pieces, solids, balls, the runner, heads |
| ClothDemoModule.cpp | The scenes, the wind, the runner and the heads, mesh rebuilds, drawing, input and the panel |
| game.json | Marketplace manifest (id, title, tags, modules) |
| CMakeLists.txt | The executable, its shaders and the UI files copied next to it |