Humans: MakeHuman bodies from sliders¶
kke::Human (kke/Human.h) makes a realistic person from slider values:
gender, age, muscle, weight, height, ancestry, and about 300 face and body
sliders (nose hump, eye fold, jaw, waist, hand size...). It is the base of
character creator screens: set the sliders, bake() once, and the result
is an ordinary skinned model, so a custom character costs the same per
frame as any other model and animates with the engine's GPU skinning.
The human is MakeHuman's: its base mesh, its 1,280 morph targets, its skeleton and skin weights.
Licence: what can ship¶
| Part | Licence | In a game, commercial too |
|---|---|---|
| Base mesh, morph targets, modifier lists, skeleton, skin weights | CC0 (MakeHuman's LICENSE.md, section C) | Yes, nothing asked |
| MakeHuman's program code | AGPL-3.0 | Not used: the engine reads only the data files, with its own code |
| MPFB2, MakeHuman's Blender add-on | GPL-3.0 | A tool on your PC; what you export with it is yours |
| Community asset packs (clothes, hair, skins) | CC0 or CC-BY, per pack | Yes; CC-BY packs need their authors in the game's credits |
assets/humans/credits.json lists every asset in the pack with its
authors and licence, for a game's credits screen.
The pack¶
assets/humans/makehuman.kkhpack (about 10 MB) is cooked from MakeHuman's
makehuman/data folder at the commit in tools/makehuman/makehuman.lock:
git clone https://github.com/makehumancommunity/makehuman /tmp/makehuman
git -C /tmp/makehuman checkout "$(sed -n 's/^commit //p' tools/makehuman/makehuman.lock)"
python3 tools/makehuman/cook_human.py /tmp/makehuman/makehuman/data \
assets/humans/makehuman.kkhpack --credits assets/humans/credits.json \
--commit "$(sed -n 's/^commit //p' tools/makehuman/makehuman.lock)"
The cooker keeps every target exact (the .target files are in steps of
0.1 mm and so is the pack) but leaves out the vertices a target doesn't
move and packs the rest with zlib: 127 MB of text becomes 10 MB. Its
format is described at the top of cook_human.py, and
tools/makehuman/test_cook_human.py checks it.
Using it¶
#include "kke/Human.h"
static const kke::HumanPack pack = kke::HumanPack::load("assets/humans/makehuman.kkhpack");
kke::Human h(pack);
h.set("macrodetails/Gender", 0.2f); // 0 female, 1 male
h.set("macrodetails/Age", 0.5f); // 0 = 1 year, 0.1875 = 11, 0.5 = 25, 1 = 90
h.set("macrodetails-universal/Muscle", 0.7f);
h.set("nose/nose-hump-decr|incr", 0.4f); // two-sided: -1..1
kke::Human::Bake bake = h.bake(); // bake.model is a ModelData
ModelModule::ModelId id = models.add(bake.model, "player/1"); // one model per look
// While a slider is dragged: move the same bake, then re-upload it.
h.set("chin/chin-width-decr|incr", -0.3f);
h.reshape(bake);
Slider names are MakeHuman's own, so a save made here and an .mhm file
from the MakeHuman app use the same words. HumanPack::modifiers() lists
them all with their group (the tab) and range;
HumanPack::description(name) has MakeHuman's one-line explanation.
Body type¶
The body-type sliders blend MakeHuman's macro targets. Each one is named
after the body it was sculpted for (universal-female-young-averagemuscle-maxweight,
height/male-old-minmuscle-averageweight-maxheight), and its weight is how
close the sliders are to that body: per word, 1 at that word's slider
value, falling linearly to 0 at the next value either side; the words'
weights multiply. Ancestry (African, Asian, European) is a mix: the three
always add up to 1, and setting one scales the other two.
| Slider | Values the targets were made at |
|---|---|
| Gender | female 0, male 1 |
| Age | baby 0 (1 year), child 0.1875 (11), young 0.5 (25), old 1 (90) |
| Muscle, Weight, breast size and firmness | min 0, average 0.5, max 1 |
| Height, proportions | min (or uncommon) 0, max (or ideal) 1; none at 0.5 |
The bake¶
- One mesh per group: the skin, plus MakeHuman's helper eyes, teeth and tongue, which stand in until eye and teeth assets are added.
- The skeleton is MakeHuman's default rig (163 bones). Each joint sits at the centre of its own vertices on the shaped body, so a tall, broad or small body gets a skeleton its size. A bone's roll comes from its plane of three joints.
- Up to four bone weights per vertex, the strongest, renormalised.
Bake::sourceVertexmaps each model vertex back to the pack vertex it came from (a vertex on a UV seam becomes several), whichreshapeuses.
The creator screen¶
kke::CharacterCreatorModule (kke/modules/CharacterCreatorModule.h) is
the screen, ready for any game: tabs of sliders (body type, shape, torso,
arms and legs, head and neck, eyes, nose, mouth and jaw, ears, skin,
hair),
mirrored sides, undo and redo, randomise (believable: few sliders, small
moves), camera views, save and load, and the share code. A game adds it
next to a UiModule and a ModelModule, calls open(), and hears
onDone:
auto& creator = app.addModule<kke::CharacterCreatorModule>("player1"); // saves player1.kkhuman
creator.onDone([&](const kke::HumanLook& look) {
ModelModule::ModelId id = models.add(creator.bakeSkinned(), "player/me");
// Send creator.shareCode() to the others with your game's messages;
// they call fromShareCode, applyLook and bake.
});
In CMake, kke_use_character_creator(my_game) copies the screen
(assets/ui/character_creator/) and the human pack next to the game.
games/character_creator is the demo, and its README has the controls.
While a slider moves only vertices move (Human::reshape and
ModelModule::setDeformedVertices), so dragging stays smooth on a phone
GPU too.
Hair¶
kke/HumanHair.h grows the engine's strand hair (HAIR.md) on
a MakeHuman head, whatever shape the sliders gave it:
findScalp(pack, shaped)finds where hair grows: the skin the head bone carries, above the brows and the ear tops and behind the hairline (heights measured from the eyes, as fractions of the head, so they scale with it), and the sphere that fits it best.hairOnHuman(desc, style, pack, shaped, guides)lays any hairstyle (hairstyleOnHeadnames: afro, box braids, cornrows, locs...; or a type,short,long,4c...) on that sphere, then sets every root on the skin straight under it (a ray against the scalp's faces). Roots with no scalp under them are dropped. Loose styles start back from the hairline, since a real face stands out in front of the sphere.
The creator puts the body in a RigidWorld as a mesh only cloth and hair
feel, so long hair falls over the shoulders and back without going
through them, and paints the scalp the hair's root colour (a copy of the
scalp's triangles 2 mm over the skin), so no skin shows between the
hairs. Turn spins the figure, not the camera, so the hair swings. A
slider change regrows the hair on the new head once the slider rests.
Multiplayer¶
A character travels as its look (kke/HumanLook.h), never as a mesh:
slider values and colours. shareCode(look, pack) packs it into a
URL-safe string, kh1. and base64 of the colours, one bit per slider
(off its default or not) and a byte per moved slider: about 90 letters for
a few changes, 250 for a random person. fromShareCode reads it back. The
code carries a key of the pack's slider list, so a machine with another
pack refuses it instead of building someone else.
Every machine loads the same pack and bakes the same body from the same
values. The player's own body is baked from quantised(look, pack), the
look rounded exactly as the code rounds it, so the vertices match
everywhere. A look read back and coded again gives the same code (a CI
self-test checks this on a random person every run).
Saves are JSON (lookToJson, lookFromJson: format kk-human, version
1) with MakeHuman's slider names, and lookFromMhm reads MakeHuman's own
.mhm saves.
What comes next¶
The plan for the whole creator is in the project's "Character creator
plan" document. In order: animation through retargeting (MakeHuman bone
names to the engine's canonical ones), skin and eyes, clothes fitted with MakeHuman's .mhclo files (each
clothing vertex rides three body vertices, so clothes fit every body),
then facial expressions (ENG-F25).