Character creator: hair grown from the body's own scalp #120
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "khyretos/claude/creator-hair"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Builds on PR 116 (claude/character-creator), which builds on PR 115: merge those first; this one then shows only its own commit.
What changes
kke/HumanHair.h: the engine's strand hair on any MakeHuman head.findScalpfinds where hair grows on the shaped body (the head bone's skin above the brows and ear tops, behind the hairline; measured from the eyes, so it scales with the head) and fits a sphere.hairOnHumanlays any hairstyle on it and sets each root on the skin straight under it (ray against the scalp's faces); loose styles start back from the hairline so they don't fall over the eyes.KKE_CREATOR_HAIR,KKE_CREATOR_TURN.How I verified it
./build/bin/kke_tests: 1260 passed (3 new: scalp found for both genders and never on the face; roots on the skin for short, long, afro, cornrows on a taller head)character_creatorunder lavapipe with Vulkan validation + sync validation, turning, long hair: no errors; self-test passed three runs with random stylesPerformance
160 guide strands in a one-worker RigidWorld; drawn hairs built on the GPU (HairRenderer). Only while the creator is open.
Docs
UI change: holds for Kees's look-OK.
🤖 Generated with Claude Code
https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6
Title
Character creator: hair grown from the body's own scalp
User description
Builds on PR 116 (claude/character-creator), which builds on PR 115: merge those first; this one then shows only its own commit.
What changes
kke/HumanHair.h: the engine's strand hair on any MakeHuman head.findScalpfinds where hair grows on the shaped body (the head bone's skin above the brows and ear tops, behind the hairline; measured from the eyes, so it scales with the head) and fits a sphere.hairOnHumanlays any hairstyle on it and sets each root on the skin straight under it (ray against the scalp's faces); loose styles start back from the hairline so they don't fall over the eyes.KKE_CREATOR_HAIR,KKE_CREATOR_TURN.How I verified it
./build/bin/kke_tests: 1260 passed (3 new: scalp found for both genders and never on the face; roots on the skin for short, long, afro, cornrows on a taller head)character_creatorunder lavapipe with Vulkan validation + sync validation, turning, long hair: no errors; self-test passed three runs with random stylesPerformance
160 guide strands in a one-worker RigidWorld; drawn hairs built on the GPU (HairRenderer). Only while the creator is open.
Docs
UI change: holds for Kees's look-OK.
🤖 Generated with Claude Code
https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6
PR Type
Enhancement, Tests, Documentation
Description
Adds hair system to character creator with 14 styles
Implements scalp detection and root placement logic
Includes UI tabs, save/load, and random generation
Adds CI tests for hair placement on various heads
Diagram Walkthrough
File Walkthrough
11 files
Implements character creator with hair system, UI tabs and physicsAdds scalp finding and hair root placement for Human classParses human pack data including modifiers and targetsStandalone executable for the character creator demoHumanLook JSON/Share code for saves and multiplayerfindScalp and hairOnHuman for strand hair on MakeHuman headsCharacterCreatorModule interface with hair, save and share codeHumanPack class to read the binary MakeHuman data fileHuman class to blend morph targets into a skinned modelHumanLook struct for saving looks and generating share codesHumanScalp and hairOnHuman declarations for hair on bodies5 files
Tests scalp detection and hair roots on skin for various stylesAdds character_creator to CI self-test loopUnit tests for the human pack cooker formatCI job runs cook_human tests and creator self-testInclude test_human.cpp in the kke_tests executable5 files
Registers character_creator as a reserved game nameNight theme stylesheet for the character creator UIAdds kke_use_character_creator function and demo subdirectoryDefine game metadata and required modules for character creatorConfigure CMake build rules for the character creator game1 files
Cooks MakeHuman CC0 data into a binary .kkhpack file10 files
Documentation for humans, creator screen and hair systemDemo README explaining controls and build notesAdds Humans (MakeHuman) entry to the project roadmapLists MakeHuman assets and creator screen as dependenciesLinks character_creator demo in the demos indexAdd Character Creator and Humans to documentation navigationAdd character_creator demo to the list of available demosLink HUMANS.md in the documentation indexAdd CC0 license credits for MakeHuman base mesh and dataDocument hairOnHuman function for MakeHuman scalp placement6 files
PR Reviewer Guide 🔍
📋 Focus areas: 🔴 6 open · ✅ 0 handled. Every push re-checks them; a handled area moves to Resolved findings at the bottom.
Missing Error Handling for File Operations
The
save()function (lines 763-769) opens a file for writing without checking if the path is valid or if the file was successfully opened before attempting to write. Ifstd::ofstreamfails to open the file (e.g., due to permissions, disk full, or invalid path), the code proceeds to write JSON to a potentially invalid stream object, which will fail silently or corrupt data. The function should check if the stream is good after opening and report an error status if it fails.Potential Memory Leak on Shutdown
In the
shutdown()function (lines 784-792),m_hairDrawnis retired but not explicitly reset tonullptr. While theretirecall likely handles cleanup, failing to nullify the pointer could lead to dangling references if the module is re-initialized or if other parts of the code expect a null pointer after shutdown. It is best practice to setm_hairDrawn = nullptr;after retiring.The
load()function (lines 771-782) reads a file into a stringstream and then parses it. If the file is empty or contains invalid JSON,lookFromJsonreturns false and sets an error message instatus, but the function does not explicitly clear any previous state or handle the case where the file exists but is unreadable (e.g., permission denied). Additionally, ifpath.empty()is true (no save folder), the function returns early without setting a status message, leaving the user unaware that the load operation was skipped.Missing Error Handling for Hair Placement
In
hairOnHuman, if the hairstyle fails to place any roots on the scalp (e.g., due to a bad style definition or a malformed head shape), the function returnsfalsewithout cleaning up thedescobject. The caller receives a failure signal but may not know if the hair data was partially modified, potentially leading to inconsistent state in the character creator module.Potential Integer Overflow in Vertex Count
The script checks if vertex count exceeds 0xFFFF (line 267) and raises an error. However, it does not validate that the number of morph targets or modifiers fits within reasonable limits that could cause memory issues or integer overflows elsewhere in the pack format (e.g., in
HumanPack::parsewhere arrays are allocated based on counts). While the current check prevents immediate crash, adding bounds checks for target count and modifier count would make the parser more robust against malformed packs.Hardcoded Base64 Alphabet
The
kB64array on line 40 defines a custom base64 alphabet using-and_. While this is standard for URL-safe base64, the code does not explicitly handle cases where the input string might contain characters outside this set during decoding (unbase64url). Although the loop checksif (!p || c == '\0'), relying onstd::strchron a hardcoded array without explicit documentation of the allowed character set could lead to confusion or subtle bugs if the alphabet changes or is misused.ℹ️ Chunked review: the diff exceeded the model token budget, so it was reviewed in 3 chunks and the per-chunk results were merged.
PR Code Suggestions ✨
No code suggestions found for the PR.
🧹 Lint report · ❌ 38 problems in 6 checks · 🧹 3 formatting checks to tidy
Nothing here blocks the merge (report only).
MARKDOWN · markdownlint: first lines of the output
CPP · cppcheck: first lines of the output
YAML · yamllint: first lines of the output
BASH · shellcheck: first lines of the output
DOCKERFILE · hadolint: first lines of the output
REPOSITORY · trivy: first lines of the output
🧹 Formatting only (layout, no behaviour; the nightly lint-fix job applies these as a PR): YAML · prettier (56 files checked), BASH · shfmt (11 files checked), JSON · prettier (147 files checked)
✅ Clean: actionlint, git_diff, gitleaks, grype, jsonlint, secretlint, stylelint, trufflehog, xmllint
MegaLinter on soucouyant · full logs in the Actions run · comment
/improvefor AI fix suggestions