WIP: Anime style: VRoid's CC0 avatars drawn in the gacha style #135

Draft
khyretos wants to merge 1 commit from khyretos/claude/creator-anime into claude/creator-clothes AGit
Owner

Requested by Kees · project thread

Stacked on the clothes PR #134 (base claude/creator-clothes) and on PR 108 (glTF import, merged). One commit, cherry-picked onto the rebased clothes branch without conflicts.

WIP: visual work. Kees's look-OK on the anime style is needed before merging; merge #134 first, then drop the WIP prefix.

Kees: the old anime mode (the realistic body toon-shaded) read as a cartoon, not as an anime game. The anime style is now its own body and its own renderer; the two styles only share bone names (kke::AnimRig).

What it adds

  • kke/Vrm.h, src/Vrm.cpp: loadVrm reads VRM 0.x (mesh/skeleton via the glTF import; face morph targets and expressions; MToon materials; spring bones and colliders; humanoid map; licence fields), turned to face +Z. CPU pose() with expressions.
  • kke/VrmSprings.h: VRM 0.x spring bones in world space (hair, skirts), with collider spheres.
  • kke/AnimeRenderer.h: MToon pushed to the gacha look: hard two-tone light, the face lit as one shape (head-sphere normals, no cast shadows on the face), coloured outlines along seam-averaged normals, rim light, hair angel ring, blended parts last, shadow casting. Shaders anime*.vert/frag, anime_common.glsl.
  • Character creator: Anime button shows assets/anime/HairSample_Female.vrm / HairSample_Male.vrm (VRoid Project, CC0 per VRoid's help page and the files' VRM licence fields) by the Gender slider, blinking, breathing, hair on spring bones. KKE_CREATOR_EXPRESSION for screenshots.
  • docs/ANIME.md; DEPENDENCIES rows.

Note: the two .vrm files are 18 MB each.

Tests

  • New Vrm.ReadsTheAvatarItsFaceAndItsMaterials, Vrm.FacesPlusZ, Vrm.BlinkClosesTheEyes, VrmSprings.HairSwingsWhenTheFigureTurnsAndSettles
  • Full kke_tests on the rebased branch: 1346 passed
  • check_dependencies, check_game_modules, check_std_includes
  • Creator self-test under Xvfb with KKE_CREATOR_STYLE=anime, female and male avatar: passed, VRM loaded without warnings
  • Screenshots from the original thread (Xvfb + lavapipe): https://claude.ai/artifact/NE6JtKskKpuU217EjyPy4K

UI change: holds for Kees's look-OK on the screenshots before merging.

🤖 Generated with Claude Code

https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6

<!-- claude-projects-attribution: {"github_login":"Khyretos"} --> _Requested by **Kees** · [project thread](https://claude.ai/code/project/chan_01Ds4PKvSLCNCCKr5TJxrV2A?thread=cmsg_01Ds4PKvSLCNCCKr5TJxrV2ABb2WaYyLkc7eUMNFVK1p2Z)_ Stacked on the clothes PR #134 (base `claude/creator-clothes`) and on PR 108 (glTF import, merged). One commit, cherry-picked onto the rebased clothes branch without conflicts. **WIP: visual work. Kees's look-OK on the anime style is needed before merging; merge #134 first, then drop the WIP prefix.** Kees: the old anime mode (the realistic body toon-shaded) read as a cartoon, not as an anime game. The anime style is now its own body and its own renderer; the two styles only share bone names (kke::AnimRig). ## What it adds - `kke/Vrm.h`, `src/Vrm.cpp`: `loadVrm` reads VRM 0.x (mesh/skeleton via the glTF import; face morph targets and expressions; MToon materials; spring bones and colliders; humanoid map; licence fields), turned to face +Z. CPU `pose()` with expressions. - `kke/VrmSprings.h`: VRM 0.x spring bones in world space (hair, skirts), with collider spheres. - `kke/AnimeRenderer.h`: MToon pushed to the gacha look: hard two-tone light, the face lit as one shape (head-sphere normals, no cast shadows on the face), coloured outlines along seam-averaged normals, rim light, hair angel ring, blended parts last, shadow casting. Shaders `anime*.vert/frag`, `anime_common.glsl`. - Character creator: Anime button shows `assets/anime/HairSample_Female.vrm` / `HairSample_Male.vrm` (VRoid Project, CC0 per VRoid's help page and the files' VRM licence fields) by the Gender slider, blinking, breathing, hair on spring bones. `KKE_CREATOR_EXPRESSION` for screenshots. - docs/ANIME.md; DEPENDENCIES rows. Note: the two .vrm files are 18 MB each. ## Tests - [x] New `Vrm.ReadsTheAvatarItsFaceAndItsMaterials`, `Vrm.FacesPlusZ`, `Vrm.BlinkClosesTheEyes`, `VrmSprings.HairSwingsWhenTheFigureTurnsAndSettles` - [x] Full `kke_tests` on the rebased branch: 1346 passed - [x] check_dependencies, check_game_modules, check_std_includes - [x] Creator self-test under Xvfb with `KKE_CREATOR_STYLE=anime`, female and male avatar: passed, VRM loaded without warnings - [x] Screenshots from the original thread (Xvfb + lavapipe): https://claude.ai/artifact/NE6JtKskKpuU217EjyPy4K UI change: holds for Kees's look-OK on the screenshots before merging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6
Anime style: VRoid's CC0 avatars drawn in the gacha style
All checks were successful
CI / dependencies (pull_request) Successful in 7s
CI / std-includes (pull_request) Successful in 17s
Benchmarks / benchmarks (pull_request) Successful in 6m59s
CI / shipping-build (pull_request) Successful in 12m51s
CI / release-build (pull_request) Successful in 7m42s
Docs site / site (pull_request) Successful in 12s
lint / megalinter (pull_request) Successful in 1m1s
CI / android (pull_request) Successful in 12m17s
Windows (native) / build (pull_request) Successful in 9m32s
Windows (native) / test (pull_request) Successful in 6m6s
Windows (native) / publish (pull_request) Has been skipped
CI / build-test-and-coverage (pull_request) Successful in 1h41m21s
2bdfa1bd08
The anime style was the realistic body toon-shaded; it read as a cartoon,
not as an anime game (Kees). It is now a body of its own:

- kke::loadVrm reads VRM 0.x: mesh and skeleton through the glTF import,
  face morph targets and expressions, MToon materials, spring bones and
  colliders, the humanoid map and licence fields; turned to face +Z.
- kke::VrmSprings swings the spring bones (hair, skirts) in world space.
- kke::AnimeRenderer draws MToon pushed to the gacha look: hard two-tone
  light, the face lit as one shape (normals bent to a head sphere, no
  cast shadows on it), coloured outlines along seam-averaged normals,
  rim light, an angel ring on hair, blended parts last.
- The creator's Anime button shows VRoid's HairSample_Female/Male (CC0)
  by the Gender slider, blinking and breathing, hair on spring bones.

Stacked on PR 108 (glTF import). docs/ANIME.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6
Collaborator

Title

WIP: Anime style: VRoid's CC0 avatars drawn in the gacha style


User description

Requested by Kees · project thread

Stacked on the clothes PR #134 (base claude/creator-clothes) and on PR 108 (glTF import, merged). One commit, cherry-picked onto the rebased clothes branch without conflicts.

WIP: visual work. Kees's look-OK on the anime style is needed before merging; merge #134 first, then drop the WIP prefix.

Kees: the old anime mode (the realistic body toon-shaded) read as a cartoon, not as an anime game. The anime style is now its own body and its own renderer; the two styles only share bone names (kke::AnimRig).

What it adds

  • kke/Vrm.h, src/Vrm.cpp: loadVrm reads VRM 0.x (mesh/skeleton via the glTF import; face morph targets and expressions; MToon materials; spring bones and colliders; humanoid map; licence fields), turned to face +Z. CPU pose() with expressions.
  • kke/VrmSprings.h: VRM 0.x spring bones in world space (hair, skirts), with collider spheres.
  • kke/AnimeRenderer.h: MToon pushed to the gacha look: hard two-tone light, the face lit as one shape (head-sphere normals, no cast shadows on the face), coloured outlines along seam-averaged normals, rim light, hair angel ring, blended parts last, shadow casting. Shaders anime*.vert/frag, anime_common.glsl.
  • Character creator: Anime button shows assets/anime/HairSample_Female.vrm / HairSample_Male.vrm (VRoid Project, CC0 per VRoid's help page and the files' VRM licence fields) by the Gender slider, blinking, breathing, hair on spring bones. KKE_CREATOR_EXPRESSION for screenshots.
  • docs/ANIME.md; DEPENDENCIES rows.

Note: the two .vrm files are 18 MB each.

Tests

  • New Vrm.ReadsTheAvatarItsFaceAndItsMaterials, Vrm.FacesPlusZ, Vrm.BlinkClosesTheEyes, VrmSprings.HairSwingsWhenTheFigureTurnsAndSettles
  • Full kke_tests on the rebased branch: 1346 passed
  • check_dependencies, check_game_modules, check_std_includes
  • Creator self-test under Xvfb with KKE_CREATOR_STYLE=anime, female and male avatar: passed, VRM loaded without warnings
  • Screenshots from the original thread (Xvfb + lavapipe): https://claude.ai/artifact/NE6JtKskKpuU217EjyPy4K

UI change: holds for Kees's look-OK on the screenshots before merging.

🤖 Generated with Claude Code

https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6


PR Type

Enhancement, Tests, Documentation


Description

  • Adds VRM 0.x loader for anime avatars

  • Implements MToon gacha renderer with outlines

  • Simulates spring bones for hair and skirts

  • Includes tests for loading and animation


Diagram Walkthrough

flowchart LR
  A["VRM Loader"] --> B["VrmAvatar"]
  A --> C["VrmSprings"]
  B --> D["AnimeRenderer"]
  C --> D
  D --> E["Shaders"]
  F["Character Creator"] -.->|"Select Avatar"| B
  G["Tests"] -.->|"Validate"| B
  G -.->|"Validate"| C

File Walkthrough

Relevant files
Enhancement
13 files
Vrm.cpp
VRM 0.x loader: mesh, skeleton, morphs, expressions, MToon, springs
+402/-0 
CharacterCreatorModule.cpp
Anime style integration: VRoid avatars, spring bones, renderer
+90/-76 
AnimeRenderer.cpp
AnimeRenderer: MToon gacha look, face sphere normals, outlines, rim
light
+314/-0 
VrmSprings.cpp
VrmSprings: CPU spring bone simulation for hair and skirts
+153/-0 
anime.frag
Anime fragment shader: two-tone light, face sphere, rim, angel ring
+97/-0   
anime_common.glsl
Shared push constants and uniforms for anime rendering pipeline
+29/-0   
anime_outline.frag
Outline shader: coloured line art from shade colour or fixed tone
+26/-0   
anime_outline.vert
Outline vertex shader: hull expansion along seam-averaged normals
+32/-0   
anime.vert
Anime vertex shader: transforms posed vertices to world space
+25/-0   
Vrm.h
VrmAvatar, VrmToon, VrmSpringGroup structs and loadVrm function
+123/-0 
AnimeRenderer.h
AnimeRenderer class: materials, parts, update/draw methods
+118/-0 
CharacterCreatorModule.h
CharacterCreatorModule: anime flags, VRM and spring bone members
+18/-10 
VrmSprings.h
VrmSprings class: joint simulation and step/reset methods
+56/-0   
Tests
2 files
test_vrm.cpp
Tests for VRM loading, facing +Z, blinking, and spring bones
+125/-0 
CMakeLists.txt
Added test_vrm.cpp to test suite                                                 
+1/-0     
Documentation
4 files
ANIME.md
Documentation for anime style, VRM avatars, springs and renderer
+82/-0   
DEPENDENCIES.md
Added VRoid sample avatars to dependencies list                   
+1/-0     
HUMANS.md
Replaced old anime style description with VRoid avatars info
+4/-30   
README.md
Updated character creator README to reflect new anime style
+1/-1     
Configuration changes
2 files
CMakeLists.txt
CMake: copy anime VRMs and add anime shaders to build       
+6/-1     
CMakeLists.txt
Added VRM and AnimeRenderer source files to build               
+4/-1     
Additional files
2 files
HairSample_Female.vrm [link]   
HairSample_Male.vrm [link]   

## Title WIP: Anime style: VRoid's CC0 avatars drawn in the gacha style ___ ### **User description** <!-- claude-projects-attribution: {"github_login":"Khyretos"} --> _Requested by **Kees** · [project thread](https://claude.ai/code/project/chan_01Ds4PKvSLCNCCKr5TJxrV2A?thread=cmsg_01Ds4PKvSLCNCCKr5TJxrV2ABb2WaYyLkc7eUMNFVK1p2Z)_ Stacked on the clothes PR #134 (base `claude/creator-clothes`) and on PR 108 (glTF import, merged). One commit, cherry-picked onto the rebased clothes branch without conflicts. **WIP: visual work. Kees's look-OK on the anime style is needed before merging; merge #134 first, then drop the WIP prefix.** Kees: the old anime mode (the realistic body toon-shaded) read as a cartoon, not as an anime game. The anime style is now its own body and its own renderer; the two styles only share bone names (kke::AnimRig). ## What it adds - `kke/Vrm.h`, `src/Vrm.cpp`: `loadVrm` reads VRM 0.x (mesh/skeleton via the glTF import; face morph targets and expressions; MToon materials; spring bones and colliders; humanoid map; licence fields), turned to face +Z. CPU `pose()` with expressions. - `kke/VrmSprings.h`: VRM 0.x spring bones in world space (hair, skirts), with collider spheres. - `kke/AnimeRenderer.h`: MToon pushed to the gacha look: hard two-tone light, the face lit as one shape (head-sphere normals, no cast shadows on the face), coloured outlines along seam-averaged normals, rim light, hair angel ring, blended parts last, shadow casting. Shaders `anime*.vert/frag`, `anime_common.glsl`. - Character creator: Anime button shows `assets/anime/HairSample_Female.vrm` / `HairSample_Male.vrm` (VRoid Project, CC0 per VRoid's help page and the files' VRM licence fields) by the Gender slider, blinking, breathing, hair on spring bones. `KKE_CREATOR_EXPRESSION` for screenshots. - docs/ANIME.md; DEPENDENCIES rows. Note: the two .vrm files are 18 MB each. ## Tests - [x] New `Vrm.ReadsTheAvatarItsFaceAndItsMaterials`, `Vrm.FacesPlusZ`, `Vrm.BlinkClosesTheEyes`, `VrmSprings.HairSwingsWhenTheFigureTurnsAndSettles` - [x] Full `kke_tests` on the rebased branch: 1346 passed - [x] check_dependencies, check_game_modules, check_std_includes - [x] Creator self-test under Xvfb with `KKE_CREATOR_STYLE=anime`, female and male avatar: passed, VRM loaded without warnings - [x] Screenshots from the original thread (Xvfb + lavapipe): https://claude.ai/artifact/NE6JtKskKpuU217EjyPy4K UI change: holds for Kees's look-OK on the screenshots before merging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014ehJmtmmZLukS7FKi1aYb6 ___ ### **PR Type** Enhancement, Tests, Documentation ___ ### **Description** - Adds VRM 0.x loader for anime avatars - Implements MToon gacha renderer with outlines - Simulates spring bones for hair and skirts - Includes tests for loading and animation ___ ### Diagram Walkthrough ```mermaid flowchart LR A["VRM Loader"] --> B["VrmAvatar"] A --> C["VrmSprings"] B --> D["AnimeRenderer"] C --> D D --> E["Shaders"] F["Character Creator"] -.->|"Select Avatar"| B G["Tests"] -.->|"Validate"| B G -.->|"Validate"| C ``` <details> <summary><h3> File Walkthrough</h3></summary> <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>13 files</summary><table> <tr> <td><strong>Vrm.cpp</strong><dd><code>VRM 0.x loader: mesh, skeleton, morphs, expressions, MToon, springs</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/Vrm.cpp">+402/-0</a>&nbsp; </td> </tr> <tr> <td><strong>CharacterCreatorModule.cpp</strong><dd><code>Anime style integration: VRoid avatars, spring bones, renderer</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/modules/CharacterCreatorModule.cpp">+90/-76</a>&nbsp; </td> </tr> <tr> <td><strong>AnimeRenderer.cpp</strong><dd><code>AnimeRenderer: MToon gacha look, face sphere normals, outlines, rim </code><br><code>light</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/AnimeRenderer.cpp">+314/-0</a>&nbsp; </td> </tr> <tr> <td><strong>VrmSprings.cpp</strong><dd><code>VrmSprings: CPU spring bone simulation for hair and skirts</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/VrmSprings.cpp">+153/-0</a>&nbsp; </td> </tr> <tr> <td><strong>anime.frag</strong><dd><code>Anime fragment shader: two-tone light, face sphere, rim, angel ring</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/shaders/anime.frag">+97/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>anime_common.glsl</strong><dd><code>Shared push constants and uniforms for anime rendering pipeline</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/shaders/anime_common.glsl">+29/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>anime_outline.frag</strong><dd><code>Outline shader: coloured line art from shade colour or fixed tone</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/shaders/anime_outline.frag">+26/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>anime_outline.vert</strong><dd><code>Outline vertex shader: hull expansion along seam-averaged normals</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/shaders/anime_outline.vert">+32/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>anime.vert</strong><dd><code>Anime vertex shader: transforms posed vertices to world space</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/shaders/anime.vert">+25/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>Vrm.h</strong><dd><code>VrmAvatar, VrmToon, VrmSpringGroup structs and loadVrm function</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/include/kke/Vrm.h">+123/-0</a>&nbsp; </td> </tr> <tr> <td><strong>AnimeRenderer.h</strong><dd><code>AnimeRenderer class: materials, parts, update/draw methods</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/include/kke/AnimeRenderer.h">+118/-0</a>&nbsp; </td> </tr> <tr> <td><strong>CharacterCreatorModule.h</strong><dd><code>CharacterCreatorModule: anime flags, VRM and spring bone members</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/include/kke/modules/CharacterCreatorModule.h">+18/-10</a>&nbsp; </td> </tr> <tr> <td><strong>VrmSprings.h</strong><dd><code>VrmSprings class: joint simulation and step/reset methods</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/include/kke/VrmSprings.h">+56/-0</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>test_vrm.cpp</strong><dd><code>Tests for VRM loading, facing +Z, blinking, and spring bones</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/tests/test_vrm.cpp">+125/-0</a>&nbsp; </td> </tr> <tr> <td><strong>CMakeLists.txt</strong><dd><code>Added test_vrm.cpp to test suite</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/tests/CMakeLists.txt">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>4 files</summary><table> <tr> <td><strong>ANIME.md</strong><dd><code>Documentation for anime style, VRM avatars, springs and renderer</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/docs/ANIME.md">+82/-0</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>DEPENDENCIES.md</strong><dd><code>Added VRoid sample avatars to dependencies list</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/docs/DEPENDENCIES.md">+1/-0</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>HUMANS.md</strong><dd><code>Replaced old anime style description with VRoid avatars info</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/docs/HUMANS.md">+4/-30</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>README.md</strong><dd><code>Updated character creator README to reflect new anime style</code></dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/games/character_creator/README.md">+1/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>CMakeLists.txt</strong><dd><code>CMake: copy anime VRMs and add anime shaders to build</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/CMakeLists.txt">+6/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> <tr> <td><strong>CMakeLists.txt</strong><dd><code>Added VRM and AnimeRenderer source files to build</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/CMakeLists.txt">+4/-1</a>&nbsp; &nbsp; &nbsp; </td> </tr> </table></details></td></tr><tr><td><strong>Additional files</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>HairSample_Female.vrm</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/assets/anime/HairSample_Female.vrm">[link]</a>&nbsp; &nbsp; </td> </tr> <tr> <td><strong>HairSample_Male.vrm</strong></td> <td><a href="https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/assets/anime/HairSample_Male.vrm">[link]</a>&nbsp; &nbsp; </td> </tr> </table></details></td></tr></tr></tbody></table> </details> ___
Collaborator

PR Reviewer Guide 🔍

📋 Focus areas: 🔴 7 open · ✅ 0 handled. Every push re-checks them; a handled area moves to Resolved findings at the bottom.

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Missing Error Handling for Texture Loading

In the readToon function, texture paths are resolved using a lambda that returns an empty string if the texture is not found. Later in AnimeRenderer::build, these empty strings are used to select fallback textures. However, if a critical texture (like the main albedo) is missing and the fallback logic doesn't handle it correctly, the shader might receive invalid or uninitialized data, leading to rendering artifacts or crashes. The code should explicitly validate that required textures are present before proceeding with material setup.

auto tex = [&](const char* key) {
    auto it = textures.find(key);
    return it != textures.end() && it->is_number_integer() ? imageFile(path, data, it->get<cgltf_size>()) : std::string();
};
t.mainTexture = tex("_MainTex");
t.shadeTexture = tex("_ShadeTexture");
t.normalTexture = tex("_BumpMap");
t.matcapTexture = tex("_SphereAdd");
t.emissionTexture = tex("_EmissionMap");
t.rimTexture = tex("_RimTexture");
t.outlineWidthTexture = tex("_OutlineWidthTexture");
return t;

Potential Null Pointer Dereference
In the showAnimeBody function, m_vrm->bone(bone) is called without checking if m_vrm is valid first. Although m_vrm is initialized just before this call via std::make_unique<VrmAvatar>(loadVrm(file)), there is a risk if loadVrm throws an exception and the catch block returns early without properly initializing m_vrm. The subsequent calls to m_vrm->expressions.size() and other methods could lead to undefined behavior if m_vrm is not properly set up.

Hardcoded Magic Numbers in Shader Logic

The shader anime.frag uses hardcoded magic numbers like 0.22, 0.30, 0.42, 0.52, 0.1, 0.4 for the angel ring effect. These values are not configurable and may need adjustment for different hair styles or lighting conditions. It would be better to parameterize these values through uniform buffers or push constants for easier tuning.

        flatNormal = std::make_unique<Texture>(app.device(), flat, 1, 1, Texture::LinearData{});
        const uint8_t none[4] = { 0, 0, 0, 255 };
        black = std::make_unique<Texture>(app.device(), none, 1, 1);
    }
};

Infinite Loop Risk in Spring Simulation

In the substep function, the simulation step size is clamped to 0.25f to prevent flinging. However, if dt is extremely large (e.g., due to a frame drop), the number of substeps calculated as ceil(dt * 30.0f) could still result in a very large number of iterations, potentially causing performance issues or numerical instability. Adding a maximum limit on the number of substeps would mitigate this risk.

void VrmSprings::step(float dt, std::vector<glm::mat4>& locals, const glm::mat4& modelToWorld) {
    if (!m_started) reset(locals, modelToWorld);
    if (dt <= 0.0f) return;
    dt = std::min(dt, 0.25f); // a stall: don't fling the hair
    const int steps = std::max(1, static_cast<int>(std::ceil(dt * 30.0f)));
    for (int i = 0; i < steps; ++i) substep(dt / static_cast<float>(steps), locals, modelToWorld);
}

Missing Collider Sphere Initialization
The VrmSprings class declares a member m_spheres to store collider spheres for collision resolution, but the constructor and initialization logic are not visible in the diff. If this vector is left empty or uninitialized, the spring bone solver will fail to push tails out of colliders, causing hair and skirts to intersect with the body incorrectly. This needs verification that m_spheres is populated from the VRM file's collider data during construction.

Potential Resource Leak on Destruction
The AnimeRenderer class holds std::unique_ptr<Buffer> members (indices, params) and a FrameData array containing a vertices buffer. While the destructor is declared, the diff does not show the implementation. If these buffers are not explicitly freed or if the upload method leaks memory by not cleaning up previous frame data before uploading new data, it could lead to memory leaks over time, especially since the renderer is updated every frame.

Missing Expression Update Logic
The CharacterCreatorModule stores m_expressions and calls poseAnime, but the diff does not show the implementation of poseAnime. If the expression weights are not correctly applied to the VRM avatar's morph targets during the pose update, facial animations (blinking, talking) will not work as intended. Ensure the mapping between m_expressions and VrmAvatar::weights is correct.


ℹ️ Chunked review: the diff exceeded the model token budget, so it was reviewed in 2 chunks and the per-chunk results were merged.

## PR Reviewer Guide 🔍 <!-- pr-agent:review:full --> 📋 **Focus areas:** 🔴 7 open · ✅ 0 handled. Every push re-checks them; a handled area moves to *Resolved findings* at the bottom. <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 4 🔵🔵🔵🔵⚪</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> <tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/Vrm.cpp#L112-L123'><strong>Missing Error Handling for Texture Loading</strong></a> In the `readToon` function, texture paths are resolved using a lambda that returns an empty string if the texture is not found. Later in `AnimeRenderer::build`, these empty strings are used to select fallback textures. However, if a critical texture (like the main albedo) is missing and the fallback logic doesn't handle it correctly, the shader might receive invalid or uninitialized data, leading to rendering artifacts or crashes. The code should explicitly validate that required textures are present before proceeding with material setup. </summary> ```c++ auto tex = [&](const char* key) { auto it = textures.find(key); return it != textures.end() && it->is_number_integer() ? imageFile(path, data, it->get<cgltf_size>()) : std::string(); }; t.mainTexture = tex("_MainTex"); t.shadeTexture = tex("_ShadeTexture"); t.normalTexture = tex("_BumpMap"); t.matcapTexture = tex("_SphereAdd"); t.emissionTexture = tex("_EmissionMap"); t.rimTexture = tex("_RimTexture"); t.outlineWidthTexture = tex("_OutlineWidthTexture"); return t; ``` </details> <a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/CharacterCreatorModule.cpp#L666-L697'><strong>Potential Null Pointer Dereference</strong></a><br>In the `showAnimeBody` function, `m_vrm->bone(bone)` is called without checking if `m_vrm` is valid first. Although `m_vrm` is initialized just before this call via `std::make_unique<VrmAvatar>(loadVrm(file))`, there is a risk if `loadVrm` throws an exception and the catch block returns early without properly initializing `m_vrm`. The subsequent calls to `m_vrm->expressions.size()` and other methods could lead to undefined behavior if `m_vrm` is not properly set up. <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/AnimeRenderer.cpp#L82-L87'><strong>Hardcoded Magic Numbers in Shader Logic</strong></a> The shader `anime.frag` uses hardcoded magic numbers like `0.22`, `0.30`, `0.42`, `0.52`, `0.1`, `0.4` for the angel ring effect. These values are not configurable and may need adjustment for different hair styles or lighting conditions. It would be better to parameterize these values through uniform buffers or push constants for easier tuning. </summary> ```c++ flatNormal = std::make_unique<Texture>(app.device(), flat, 1, 1, Texture::LinearData{}); const uint8_t none[4] = { 0, 0, 0, 255 }; black = std::make_unique<Texture>(app.device(), none, 1, 1); } }; ``` </details> <details><summary><a href='https://git.kreative-kompas.com/khyretos/kk-engine/src/branch/refs/pull/135/head/engine/src/VrmSprings.cpp#L101-L107'><strong>Infinite Loop Risk in Spring Simulation</strong></a> In the `substep` function, the simulation step size is clamped to `0.25f` to prevent flinging. However, if `dt` is extremely large (e.g., due to a frame drop), the number of substeps calculated as `ceil(dt * 30.0f)` could still result in a very large number of iterations, potentially causing performance issues or numerical instability. Adding a maximum limit on the number of substeps would mitigate this risk. </summary> ```c++ void VrmSprings::step(float dt, std::vector<glm::mat4>& locals, const glm::mat4& modelToWorld) { if (!m_started) reset(locals, modelToWorld); if (dt <= 0.0f) return; dt = std::min(dt, 0.25f); // a stall: don't fling the hair const int steps = std::max(1, static_cast<int>(std::ceil(dt * 30.0f))); for (int i = 0; i < steps; ++i) substep(dt / static_cast<float>(steps), locals, modelToWorld); } ``` </details> <strong>Missing Collider Sphere Initialization</strong><br>The `VrmSprings` class declares a member `m_spheres` to store collider spheres for collision resolution, but the constructor and initialization logic are not visible in the diff. If this vector is left empty or uninitialized, the spring bone solver will fail to push tails out of colliders, causing hair and skirts to intersect with the body incorrectly. This needs verification that `m_spheres` is populated from the VRM file's collider data during construction. <strong>Potential Resource Leak on Destruction</strong><br>The `AnimeRenderer` class holds `std::unique_ptr<Buffer>` members (`indices`, `params`) and a `FrameData` array containing a `vertices` buffer. While the destructor is declared, the diff does not show the implementation. If these buffers are not explicitly freed or if the `upload` method leaks memory by not cleaning up previous frame data before uploading new data, it could lead to memory leaks over time, especially since the renderer is updated every frame. <strong>Missing Expression Update Logic</strong><br>The `CharacterCreatorModule` stores `m_expressions` and calls `poseAnime`, but the diff does not show the implementation of `poseAnime`. If the expression weights are not correctly applied to the VRM avatar's morph targets during the pose update, facial animations (blinking, talking) will not work as intended. Ensure the mapping between `m_expressions` and `VrmAvatar::weights` is correct. </td></tr> </table> <hr> ℹ️ **Chunked review:** the diff exceeded the model token budget, so it was reviewed in 2 chunks and the per-chunk results were merged. <!-- pr-agent-review-state:v1 {"findings":[{"body":"**Potential Null Pointer Dereference**\n\nIn the `showAnimeBody` function, `m_vrm->bone(bone)` is called without checking if `m_vrm` is valid first. Although `m_vrm` is initialized just before this call via `std::make_unique<VrmAvatar>(loadVrm(file))`, there is a risk if `loadVrm` throws an exception and the catch block returns early without properly initializing `m_vrm`. The subsequent calls to `m_vrm->expressions.size()` and other methods could lead to undefined behavior if `m_vrm` is not properly set up.","finding_id":"2a1b5c5993a9","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","line_end":697,"line_start":666,"path":"engine/src/CharacterCreatorModule.cpp","state":"ACTIVE"},{"body":"**Missing Expression Update Logic**\n\nThe `CharacterCreatorModule` stores `m_expressions` and calls `poseAnime`, but the diff does not show the implementation of `poseAnime`. If the expression weights are not correctly applied to the VRM avatar's morph targets during the pose update, facial animations (blinking, talking) will not work as intended. Ensure the mapping between `m_expressions` and `VrmAvatar::weights` is correct.","finding_id":"2a704467d5a8","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","path":"engine/include/kke/modules/CharacterCreatorModule.h","state":"ACTIVE"},{"body":"**Missing Error Handling for Texture Loading**\n\nIn the `readToon` function, texture paths are resolved using a lambda that returns an empty string if the texture is not found. Later in `AnimeRenderer::build`, these empty strings are used to select fallback textures. However, if a critical texture (like the main albedo) is missing and the fallback logic doesn't handle it correctly, the shader might receive invalid or uninitialized data, leading to rendering artifacts or crashes. The code should explicitly validate that required textures are present before proceeding with material setup.","finding_id":"7f216d9ce859","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","line_end":123,"line_start":112,"path":"engine/src/Vrm.cpp","state":"ACTIVE"},{"body":"**Infinite Loop Risk in Spring Simulation**\n\nIn the `substep` function, the simulation step size is clamped to `0.25f` to prevent flinging. However, if `dt` is extremely large (e.g., due to a frame drop), the number of substeps calculated as `ceil(dt * 30.0f)` could still result in a very large number of iterations, potentially causing performance issues or numerical instability. Adding a maximum limit on the number of substeps would mitigate this risk.","finding_id":"8a8769605d59","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","line_end":107,"line_start":101,"path":"engine/src/VrmSprings.cpp","state":"ACTIVE"},{"body":"**Potential Resource Leak on Destruction**\n\nThe `AnimeRenderer` class holds `std::unique_ptr<Buffer>` members (`indices`, `params`) and a `FrameData` array containing a `vertices` buffer. While the destructor is declared, the diff does not show the implementation. If these buffers are not explicitly freed or if the `upload` method leaks memory by not cleaning up previous frame data before uploading new data, it could lead to memory leaks over time, especially since the renderer is updated every frame.","finding_id":"b5f96031a631","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","path":"engine/include/kke/AnimeRenderer.h","state":"ACTIVE"},{"body":"**Missing Collider Sphere Initialization**\n\nThe `VrmSprings` class declares a member `m_spheres` to store collider spheres for collision resolution, but the constructor and initialization logic are not visible in the diff. If this vector is left empty or uninitialized, the spring bone solver will fail to push tails out of colliders, causing hair and skirts to intersect with the body incorrectly. This needs verification that `m_spheres` is populated from the VRM file's collider data during construction.","finding_id":"caa0f32d6089","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","path":"engine/include/kke/VrmSprings.h","state":"ACTIVE"},{"body":"**Hardcoded Magic Numbers in Shader Logic**\n\nThe shader `anime.frag` uses hardcoded magic numbers like `0.22`, `0.30`, `0.42`, `0.52`, `0.1`, `0.4` for the angel ring effect. These values are not configurable and may need adjustment for different hair styles or lighting conditions. It would be better to parameterize these values through uniform buffers or push constants for easier tuning.","finding_id":"ee9286f9adad","first_seen":"2026-10-27T16:50:10.307281Z","last_seen":"2026-10-27T16:50:10.307281Z","last_seen_head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","line_end":87,"line_start":82,"path":"engine/src/AnimeRenderer.cpp","state":"ACTIVE"}],"last_run":{"complete":false,"excluded_files":[],"head_sha":"2bdfa1bd0818fa82ea9d787d00848e0717193efd","kind":"partial","run_id":"https://git.kreative-kompas.com/khyretos/kk-engine/commit/2bdfa1bd0818fa82ea9d787d00848e0717193efd"},"schema_version":1} -->
Collaborator

PR Code Suggestions ✨

No code suggestions found for the PR.

## PR Code Suggestions ✨ <!-- pr-agent:improve:no-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).

Check Errors Warnings Files
❌ MARKDOWN · markdownlint 24 0 255
❌ CPP · cppcheck 9 0 953
❌ YAML · yamllint 2 0 57
❌ BASH · shellcheck 1 0 11
❌ DOCKERFILE · hadolint 1 0 9
❌ REPOSITORY · trivy 1 0 –
MARKDOWN · markdownlint: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/markdown_markdownlint/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
docs/ANDROID.md:87 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:128 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:136 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:149 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:163 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ANDROID.md:186 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/ASSETS.md:155 error MD046/code-block-style Code block style [Expected: fenced; Actual: indented]
docs/HISTORY.md:21 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: " Kreative Kompas Engine (KKE)"]
docs/HISTORY.md:520 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:727 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:734 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:2031 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/HISTORY.md:2090 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/MODDING.md:13 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/NETWORKING.md:114 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/PLATFORMS.md:48 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/PROCEDURAL_ANIMATION.md:36 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
docs/PROCEDURAL_ANIMATION.md:284 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
games/racing/README.md:34 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"]
games/racing/README.md:76:91 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
games/racing/README.md:77:89 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data]
…
CPP · cppcheck: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/cpp_cppcheck/
-----------------------------------------------
✅ [SUCCESS] benchmarks/kke_bench/main.cpp
✅ [SUCCESS] benchmarks/kke_benchmark/main.cpp
✅ [SUCCESS] engine/include/engine/Buffer.h
✅ [SUCCESS] engine/include/engine/Mesh.h
✅ [SUCCESS] engine/include/engine/Pipeline.h
✅ [SUCCESS] engine/include/engine/Renderer.h
✅ [SUCCESS] engine/include/engine/SwapChain.h
✅ [SUCCESS] engine/include/engine/VulkanCheck.h
✅ [SUCCESS] engine/include/engine/VulkanDevice.h
✅ [SUCCESS] engine/include/engine/Window.h
✅ [SUCCESS] engine/include/kke/AnimEvents.h
✅ [SUCCESS] engine/include/kke/AnimRig.h
✅ [SUCCESS] engine/include/kke/Animator.h
✅ [SUCCESS] engine/include/kke/AnimeRenderer.h
✅ [SUCCESS] engine/include/kke/Application.h
✅ [SUCCESS] engine/include/kke/AssetCatalog.h
✅ [SUCCESS] engine/include/kke/AudioMixer.h
✅ [SUCCESS] engine/include/kke/AutoRig.h
✅ [SUCCESS] engine/include/kke/BenchRecorder.h
✅ [SUCCESS] engine/include/kke/BenchmarkReport.h
✅ [SUCCESS] engine/include/kke/Bloom.h
✅ [SUCCESS] engine/include/kke/BodyShape.h
✅ [SUCCESS] engine/include/kke/BreakGraph.h
…
YAML · yamllint: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/yaml_yamllint/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
games/goblin_horde/data/heroes.yml
  53:11     error    too many spaces after colon  (colons)
  63:11     error    too many spaces after colon  (colons)
BASH · shellcheck: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/bash_shellcheck/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
In tools/packaging/package.sh line 130:
	done < <(find "$stage" "$stage/benchmark" -maxdepth 1 -type f \( -name "*$exe" -o -name '*.dll' \) $([ -z "$exe" ] && echo "-perm -u+x") -print0)
                                                                                                           ^-- SC2046 (warning): Quote this to prevent word splitting.
For more information:
  https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt...
DOCKERFILE · hadolint: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/dockerfile_hadolint/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
docker/android.Dockerfile:19 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/appimage.Dockerfile:38 SC2016 info: Expressions don't expand in single quotes, use double quotes for that.
docker/appimage.Dockerfile:51 DL3003 warning: Use WORKDIR to switch to a directory
docker/appimage.Dockerfile:57 DL3003 warning: Use WORKDIR to switch to a directory
docker/appimage.Dockerfile:63 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/appimage.Dockerfile:63 DL3003 warning: Use WORKDIR to switch to a directory
docker/appimage.Dockerfile:73 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/appimage.Dockerfile:73 DL3003 warning: Use WORKDIR to switch to a directory
docker/ci.Dockerfile:42 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/ci.Dockerfile:56 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/server/Dockerfile:22 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
docker/vm.Dockerfile:22 SC2086 info: Double quote to prevent globbing and word splitting.
docker/vm.Dockerfile:29 DL3003 warning: Use WORKDIR to switch to a directory
docker/vm.Dockerfile:29 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
website/Dockerfile:9 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check
REPOSITORY · trivy: first lines of the output
See documentation on https://megalinter.io/9.4.0/descriptors/repository_trivy/
-----------------------------------------------
❌ [ERROR] for workspace /workspace/khyretos/kk-engine
Linter raw log:
2026-10-27T17:03:07Z	INFO	[vulndb] Need to update DB
2026-10-27T17:03:07Z	INFO	[vulndb] Downloading vulnerability DB...
2026-10-27T17:03:07Z	INFO	[vulndb] Downloading artifact...	repo="mirror.gcr.io/aquasec/trivy-db:2"
18.53 MiB / 130.01 MiB [-------->___________________________________________________] 14.25% ? p/s ?36.03 MiB / 130.01 MiB [---------------->___________________________________________] 27.71% ? p/s ?44.39 MiB / 130.01 MiB [-------------------->_______________________________________] 34.15% ? p/s ?52.43 MiB / 130.01 MiB [------------------>____________________________] 40.32% 56.48 MiB p/s ETA 1s60.52 MiB / 130.01 MiB [--------------------->_________________________] 46.55% 56.48 MiB p/s ETA 1s68.75 MiB / 130.01 MiB [------------------------>______________________] 52.88% 56.48 MiB p/s ETA 1s77.20 MiB / 130.01 MiB [--------------------------->___________________] 59.38% 55.50 MiB p/s ETA 0s85.24 MiB / 130.01 MiB [------------------------------>________________] 65.56% 55.50 MiB p/s ETA 0s93.62 MiB / 130.01 MiB [--------------------------------->_____________] 72.01% 55.50 MiB p/s ETA 0s101.73 MiB / 130.01 MiB [----------------------------------->__________] 78.25% 54.56 MiB p/s ETA 0s109.95 MiB / 130.01 MiB [-------------------------------------->_______] 84.57% 54.56 MiB p/s ETA 0s117.63 MiB / 130.01 MiB [----------------------------------------->____] 90.48% 54.56 MiB p/s ETA 0s127.09 MiB / 130.01 MiB [-------------------------------------------->_] 97.75% 53.76 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 53.76 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 53.76 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.60 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.60 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.60 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.34 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.34 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.34 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.28 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.28 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.28 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-----------------------------------------------] 100.00% 27.25 MiB p/s 5.0s2026-10-27T17:03:13Z	INFO	[vulndb] Artifact successfully downloaded	repo="mirror.gcr.io/aquasec/trivy-db:2"
2026-10-27T17:03:13Z	INFO	[vuln] Vulnerability scanning is enabled
2026-10-27T17:03:13Z	INFO	[misconfig] Misconfiguration scanning is enabled
2026-10-27T17:03:13Z	INFO	[checks-client] Need to update the checks bundle
2026-10-27T17:03:13Z	INFO	[checks-client] Downloading the checks bundle...
234.57 KiB / 234.57 KiB [---------------------------------------------------------] 100.00% ? p/s 0s2026-10-27T17:03:14Z	WARN	[rego] Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module	file_path="root/.cache/trivy/policy/content/policies/cloud/policies/aws/cloudfront/enable_logging.rego" minimum_trivy_version="0.72.0"
2026-10-27T17:03:14Z	ERROR	[dockerfile scanner] Failed to parse file	file_path="docker/server/Dockerfile.dockerignore" err="parse dockerfile instruction: parse instruction \".git\": unknown instruction: .git"
2026-10-27T17:03:14Z	INFO	Number of language-specific files	num=0
2026-10-27T17:03:14Z	INFO	Detected config files	num=9
Report Summary
┌──────────────────────────────┬────────────┬─────────────────┬───────────────────┐
│            Target            │    Type    │ Vulnerabilities │ Misconfigurations │
├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ benchmarks/docker/Dockerfile │ dockerfile │        -        │         2         │
├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ docker/android.Dockerfile    │ dockerfile │        -        │         2         │
├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤
│ docker/appimage.Dockerfile   │ dockerfile │        -        │         5         │
…

🧹 Formatting only (layout, no behaviour; the nightly lint-fix job applies these as a PR): YAML · prettier (57 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 /improve for AI fix suggestions

<!-- kk-lint --> ## 🧹 Lint report · ❌ 38 problems in 6 checks · 🧹 3 formatting checks to tidy Nothing here blocks the merge (report only). | | Check | Errors | Warnings | Files | |---|---|---:|---:|---:| | ❌ | MARKDOWN · markdownlint | 24 | 0 | 255 | | ❌ | CPP · cppcheck | 9 | 0 | 953 | | ❌ | YAML · yamllint | 2 | 0 | 57 | | ❌ | BASH · shellcheck | 1 | 0 | 11 | | ❌ | DOCKERFILE · hadolint | 1 | 0 | 9 | | ❌ | REPOSITORY · trivy | 1 | 0 | – | <details><summary>MARKDOWN · markdownlint: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/markdown_markdownlint/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: docs/ANDROID.md:87 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:128 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:136 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:149 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:163 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ANDROID.md:186 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/ASSETS.md:155 error MD046/code-block-style Code block style [Expected: fenced; Actual: indented] docs/HISTORY.md:21 error MD025/single-title/single-h1 Multiple top-level headings in the same document [Context: " Kreative Kompas Engine (KKE)"] docs/HISTORY.md:520 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:727 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:734 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:2031 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/HISTORY.md:2090 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/MODDING.md:13 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/NETWORKING.md:114 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/PLATFORMS.md:48 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/PROCEDURAL_ANIMATION.md:36 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] docs/PROCEDURAL_ANIMATION.md:284 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] games/racing/README.md:34 error MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] games/racing/README.md:76:91 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data] games/racing/README.md:77:89 error MD056/table-column-count Table column count [Expected: 4; Actual: 3; Too few cells, row will be missing data] … ``` </details> <details><summary>CPP · cppcheck: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/cpp_cppcheck/ ----------------------------------------------- ✅ [SUCCESS] benchmarks/kke_bench/main.cpp ✅ [SUCCESS] benchmarks/kke_benchmark/main.cpp ✅ [SUCCESS] engine/include/engine/Buffer.h ✅ [SUCCESS] engine/include/engine/Mesh.h ✅ [SUCCESS] engine/include/engine/Pipeline.h ✅ [SUCCESS] engine/include/engine/Renderer.h ✅ [SUCCESS] engine/include/engine/SwapChain.h ✅ [SUCCESS] engine/include/engine/VulkanCheck.h ✅ [SUCCESS] engine/include/engine/VulkanDevice.h ✅ [SUCCESS] engine/include/engine/Window.h ✅ [SUCCESS] engine/include/kke/AnimEvents.h ✅ [SUCCESS] engine/include/kke/AnimRig.h ✅ [SUCCESS] engine/include/kke/Animator.h ✅ [SUCCESS] engine/include/kke/AnimeRenderer.h ✅ [SUCCESS] engine/include/kke/Application.h ✅ [SUCCESS] engine/include/kke/AssetCatalog.h ✅ [SUCCESS] engine/include/kke/AudioMixer.h ✅ [SUCCESS] engine/include/kke/AutoRig.h ✅ [SUCCESS] engine/include/kke/BenchRecorder.h ✅ [SUCCESS] engine/include/kke/BenchmarkReport.h ✅ [SUCCESS] engine/include/kke/Bloom.h ✅ [SUCCESS] engine/include/kke/BodyShape.h ✅ [SUCCESS] engine/include/kke/BreakGraph.h … ``` </details> <details><summary>YAML · yamllint: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/yaml_yamllint/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: games/goblin_horde/data/heroes.yml 53:11 error too many spaces after colon (colons) 63:11 error too many spaces after colon (colons) ``` </details> <details><summary>BASH · shellcheck: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/bash_shellcheck/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: In tools/packaging/package.sh line 130: done < <(find "$stage" "$stage/benchmark" -maxdepth 1 -type f \( -name "*$exe" -o -name '*.dll' \) $([ -z "$exe" ] && echo "-perm -u+x") -print0) ^-- SC2046 (warning): Quote this to prevent word splitting. For more information: https://www.shellcheck.net/wiki/SC2046 -- Quote this to prevent word splitt... ``` </details> <details><summary>DOCKERFILE · hadolint: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/dockerfile_hadolint/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: docker/android.Dockerfile:19 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/appimage.Dockerfile:38 SC2016 info: Expressions don't expand in single quotes, use double quotes for that. docker/appimage.Dockerfile:51 DL3003 warning: Use WORKDIR to switch to a directory docker/appimage.Dockerfile:57 DL3003 warning: Use WORKDIR to switch to a directory docker/appimage.Dockerfile:63 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/appimage.Dockerfile:63 DL3003 warning: Use WORKDIR to switch to a directory docker/appimage.Dockerfile:73 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/appimage.Dockerfile:73 DL3003 warning: Use WORKDIR to switch to a directory docker/ci.Dockerfile:42 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/ci.Dockerfile:56 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/server/Dockerfile:22 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check docker/vm.Dockerfile:22 SC2086 info: Double quote to prevent globbing and word splitting. docker/vm.Dockerfile:29 DL3003 warning: Use WORKDIR to switch to a directory docker/vm.Dockerfile:29 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check website/Dockerfile:9 DL4006 warning: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check ``` </details> <details><summary>REPOSITORY · trivy: first lines of the output</summary> ```text See documentation on https://megalinter.io/9.4.0/descriptors/repository_trivy/ ----------------------------------------------- ❌ [ERROR] for workspace /workspace/khyretos/kk-engine Linter raw log: 2026-10-27T17:03:07Z INFO [vulndb] Need to update DB 2026-10-27T17:03:07Z INFO [vulndb] Downloading vulnerability DB... 2026-10-27T17:03:07Z INFO [vulndb] Downloading artifact... repo="mirror.gcr.io/aquasec/trivy-db:2" 18.53 MiB / 130.01 MiB [-------->___________________________________________________] 14.25% ? p/s ?36.03 MiB / 130.01 MiB [---------------->___________________________________________] 27.71% ? p/s ?44.39 MiB / 130.01 MiB [-------------------->_______________________________________] 34.15% ? p/s ?52.43 MiB / 130.01 MiB [------------------>____________________________] 40.32% 56.48 MiB p/s ETA 1s60.52 MiB / 130.01 MiB [--------------------->_________________________] 46.55% 56.48 MiB p/s ETA 1s68.75 MiB / 130.01 MiB [------------------------>______________________] 52.88% 56.48 MiB p/s ETA 1s77.20 MiB / 130.01 MiB [--------------------------->___________________] 59.38% 55.50 MiB p/s ETA 0s85.24 MiB / 130.01 MiB [------------------------------>________________] 65.56% 55.50 MiB p/s ETA 0s93.62 MiB / 130.01 MiB [--------------------------------->_____________] 72.01% 55.50 MiB p/s ETA 0s101.73 MiB / 130.01 MiB [----------------------------------->__________] 78.25% 54.56 MiB p/s ETA 0s109.95 MiB / 130.01 MiB [-------------------------------------->_______] 84.57% 54.56 MiB p/s ETA 0s117.63 MiB / 130.01 MiB [----------------------------------------->____] 90.48% 54.56 MiB p/s ETA 0s127.09 MiB / 130.01 MiB [-------------------------------------------->_] 97.75% 53.76 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 53.76 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 53.76 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.60 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.60 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 50.60 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.34 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.34 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 47.34 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.28 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.28 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-------------------------------------------->] 100.00% 44.28 MiB p/s ETA 0s130.01 MiB / 130.01 MiB [-----------------------------------------------] 100.00% 27.25 MiB p/s 5.0s2026-10-27T17:03:13Z INFO [vulndb] Artifact successfully downloaded repo="mirror.gcr.io/aquasec/trivy-db:2" 2026-10-27T17:03:13Z INFO [vuln] Vulnerability scanning is enabled 2026-10-27T17:03:13Z INFO [misconfig] Misconfiguration scanning is enabled 2026-10-27T17:03:13Z INFO [checks-client] Need to update the checks bundle 2026-10-27T17:03:13Z INFO [checks-client] Downloading the checks bundle... 234.57 KiB / 234.57 KiB [---------------------------------------------------------] 100.00% ? p/s 0s2026-10-27T17:03:14Z WARN [rego] Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module file_path="root/.cache/trivy/policy/content/policies/cloud/policies/aws/cloudfront/enable_logging.rego" minimum_trivy_version="0.72.0" 2026-10-27T17:03:14Z ERROR [dockerfile scanner] Failed to parse file file_path="docker/server/Dockerfile.dockerignore" err="parse dockerfile instruction: parse instruction \".git\": unknown instruction: .git" 2026-10-27T17:03:14Z INFO Number of language-specific files num=0 2026-10-27T17:03:14Z INFO Detected config files num=9 Report Summary ┌──────────────────────────────┬────────────┬─────────────────┬───────────────────┐ │ Target │ Type │ Vulnerabilities │ Misconfigurations │ ├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤ │ benchmarks/docker/Dockerfile │ dockerfile │ - │ 2 │ ├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤ │ docker/android.Dockerfile │ dockerfile │ - │ 2 │ ├──────────────────────────────┼────────────┼─────────────────┼───────────────────┤ │ docker/appimage.Dockerfile │ dockerfile │ - │ 5 │ … ``` </details> 🧹 Formatting only (layout, no behaviour; the nightly lint-fix job applies these as a PR): YAML · prettier (57 files checked), BASH · shfmt (11 files checked), JSON · prettier (147 files checked) ✅ Clean: actionlint, git_diff, gitleaks, grype, jsonlint, secretlint, stylelint, trufflehog, xmllint <sub>MegaLinter on soucouyant · full logs in the Actions run · comment <code>/improve</code> for AI fix suggestions</sub>
All checks were successful
CI / dependencies (pull_request) Successful in 7s
CI / std-includes (pull_request) Successful in 17s
Benchmarks / benchmarks (pull_request) Successful in 6m59s
CI / shipping-build (pull_request) Successful in 12m51s
CI / release-build (pull_request) Successful in 7m42s
Docs site / site (pull_request) Successful in 12s
lint / megalinter (pull_request) Successful in 1m1s
CI / android (pull_request) Successful in 12m17s
Windows (native) / build (pull_request) Successful in 9m32s
Windows (native) / test (pull_request) Successful in 6m6s
Windows (native) / publish (pull_request) Has been skipped
CI / build-test-and-coverage (pull_request) Successful in 1h41m21s
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/135/head:khyretos/claude/creator-anime
git switch khyretos/claude/creator-anime

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch claude/creator-clothes
git merge --no-ff khyretos/claude/creator-anime
git switch khyretos/claude/creator-anime
git rebase claude/creator-clothes
git switch claude/creator-clothes
git merge --ff-only khyretos/claude/creator-anime
git switch khyretos/claude/creator-anime
git rebase claude/creator-clothes
git switch claude/creator-clothes
git merge --no-ff khyretos/claude/creator-anime
git switch claude/creator-clothes
git merge --squash khyretos/claude/creator-anime
git switch claude/creator-clothes
git merge --ff-only khyretos/claude/creator-anime
git switch claude/creator-clothes
git merge khyretos/claude/creator-anime
git push origin claude/creator-clothes
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
khyretos/kk-engine!135
No description provided.