Author SHA1 Message Date
Dotts b0b99668ab Merge pull request 'Feat/fidelity pass 2' (#23) from feat/fidelity-pass-2 into main
Reviewed-on: #23
2026-07-28 10:31:27 -07:00
253 changed files with 18865 additions and 39125 deletions
-5
View File
@@ -57,11 +57,6 @@ __pycache__/
# Asset pipeline staging (raw downloads, not game-ready) # Asset pipeline staging (raw downloads, not game-ready)
assets/characters/incoming/ assets/characters/incoming/
# Copyrighted visual references (local-only; never imported or exported)
/.reference_assets/
/assets/characters/local_reference/
/assets/characters/skins/skins.local.json
# Misc # Misc
*LF* *LF*
tags tags
Binary file not shown.
@@ -1,24 +0,0 @@
# CMU backward locomotion source
`_cmu_locomotion.glb` contains one animation-only clip:
- `RunBackward` — CMU Graphics Lab Motion Capture Database, subject 76,
trial 11: “quick large steps backwards.”
Source BVH:
https://github.com/una-dinosauria/cmu-mocap/blob/master/data/076/76_11.bvh
Original motion database and trial description:
https://mocap.cs.cmu.edu/
The BVH is Bruce Hahnes conversion of the original CMU motion-capture data.
CMUs FAQ permits copying, modifying, and redistributing the data (including
commercial use), requests acknowledgment, and prohibits selling the database
itself. This project uses a cropped, in-place gait cycle as part of the game,
not as a standalone resale of the database.
Build command:
```text
blender --background --python tools/build_cmu_locomotion_library.py -- 76_11.bvh assets/characters/animations/_cmu_locomotion.glb
```
Binary file not shown.
@@ -1,17 +0,0 @@
# Godot TPS Demo directional animations
Source: https://github.com/godotengine/tps-demo
The animation-only `_directional.glb` contains the `strafe_front-cycle`,
`strafe_back-cycle`, `strafe_left-cycle`, and `strafe_right-cycle` actions from
the Godot Third Person Shooter Demo player model. Meshes, textures, and
unrelated animations were removed. The game retargets these authored cycles to
its Quaternius-compatible character skeleton.
Original assets Copyright (c) 2018 Juan Linietsky and Fernando Miguel Calabró.
Distributed under the Creative Commons Attribution 3.0 license:
https://creativecommons.org/licenses/by/3.0/
Changes: animation-only extraction, retargeting, in-place root motion, and
runtime phase-synchronized blending with the project's authored walk/run clips.
Binary file not shown.
@@ -1,12 +0,0 @@
Universal Animation Library 2 [Standard]
Author: Quaternius
Source: https://quaternius.itch.io/universal-animation-library-2
License:
CC0 1.0 Universal (CC0 1.0)
Public Domain Dedication
https://creativecommons.org/publicdomain/zero/1.0/
The committed _library_v2.glb is the non-root-motion Unreal/Godot export from
the free Standard package. The game drives world movement; authored vertical
body motion remains in the clips.
Binary file not shown.
@@ -1,23 +0,0 @@
Mesh2Motion Human Animation Library
Source:
https://github.com/Mesh2Motion/mesh2motion-app
https://github.com/Mesh2Motion/mesh2motion-assets
License:
CC0 1.0 Universal (CC0 1.0)
Public Domain Dedication
https://creativecommons.org/publicdomain/zero/1.0/
The committed _mesh2motion.glb is an animation-only subset of
static/animations/human-addon-animations.glb. It retains these authored clips:
- Run_Anime
- Strafe_left
- Strafe_right
- Flying Forward Super
Meshes, textures, and unrelated actions were removed. Run_Anime is retargeted
offline into the four cardinal high-speed travel headings and the wall-run
loop; the animator's keyframed limb motion is preserved. Flying Forward Super
provides the grapple flight pose.
Binary file not shown.
@@ -1,14 +0,0 @@
# Authored wall-run animation source
`_wallrun.glb` contains the animation-only UE4 Mannequin clips
`WallRunLeft` and `WallRunRight`.
- Author: Uisco
- Source: https://uisco.itch.io/wall-running-animations
- Original files: `wall_run_left.FBX`, `wall_run_right.FBX`
- Published: 2021-03-24
- Listing: free / name-your-own-price game asset
The source listing does not state a named open-content license. Keep this
provenance file with the embedded game asset, credit Uisco, and do not resell
or redistribute the clips as a standalone animation pack.
Binary file not shown.
@@ -1,9 +0,0 @@
{
"name": "Anime Maid Character",
"author": "413 Games",
"author_url": "https://413games.itch.io/",
"license": "CC0 1.0 Universal / Public Domain",
"license_url": "https://creativecommons.org/publicdomain/zero/1.0/",
"source_url": "https://413games.itch.io/maid-character",
"changes": "Original rig and materials retained, retargeted to the Papaya-Shooter animation library; game-native mecha equipment added at runtime."
}
@@ -1,306 +0,0 @@
{
"roles": {
"hips": "mixamorig:Hips",
"head": "mixamorig:Head",
"neck": "mixamorig:Neck",
"spine": [
"mixamorig:Spine",
"mixamorig:Spine1",
"mixamorig:Spine2",
"mixamorig:Neck",
"mixamorig:Head"
],
"shoulder.L": "mixamorig:LeftShoulder",
"shoulder.R": "mixamorig:RightShoulder",
"toe.R": "mixamorig:RightToeBase",
"toe.L": "mixamorig:LeftToeBase",
"forearm.L": "mixamorig:LeftForeArm",
"forearm.R": "mixamorig:RightForeArm",
"thigh.R": "mixamorig:RightUpLeg",
"thigh.L": "mixamorig:LeftUpLeg",
"foot.R": "mixamorig:RightFoot",
"foot.L": "mixamorig:LeftFoot",
"hand.R": "mixamorig:RightHand",
"hand.L": "mixamorig:LeftHand",
"upper_arm.L": "mixamorig:LeftArm",
"upper_arm.R": "mixamorig:RightArm",
"shin.L": "mixamorig:LeftLeg",
"shin.R": "mixamorig:RightLeg"
},
"fingers": {
"thumb.L": [
"mixamorig:LeftHandThumb1",
"mixamorig:LeftHandThumb2",
"mixamorig:LeftHandThumb3",
"mixamorig:LeftHandThumb4",
"mixamorig:LeftHandThumb4_end"
],
"index.L": [
"mixamorig:LeftHandIndex1",
"mixamorig:LeftHandIndex2",
"mixamorig:LeftHandIndex3",
"mixamorig:LeftHandIndex4",
"mixamorig:LeftHandIndex4_end"
],
"middle.L": [
"mixamorig:LeftHandMiddle1",
"mixamorig:LeftHandMiddle2",
"mixamorig:LeftHandMiddle3",
"mixamorig:LeftHandMiddle4",
"mixamorig:LeftHandMiddle4_end"
],
"ring.L": [
"mixamorig:LeftHandRing1",
"mixamorig:LeftHandRing2",
"mixamorig:LeftHandRing3",
"mixamorig:LeftHandRing4",
"mixamorig:LeftHandRing4_end"
],
"pinky.L": [
"mixamorig:LeftHandPinky1",
"mixamorig:LeftHandPinky2",
"mixamorig:LeftHandPinky3",
"mixamorig:LeftHandPinky4",
"mixamorig:LeftHandPinky4_end"
],
"thumb.R": [
"mixamorig:RightHandThumb1",
"mixamorig:RightHandThumb2",
"mixamorig:RightHandThumb3",
"mixamorig:RightHandThumb4",
"mixamorig:RightHandThumb4_end"
],
"index.R": [
"mixamorig:RightHandIndex1",
"mixamorig:RightHandIndex2",
"mixamorig:RightHandIndex3",
"mixamorig:RightHandIndex4",
"mixamorig:RightHandIndex4_end"
],
"middle.R": [
"mixamorig:RightHandMiddle1",
"mixamorig:RightHandMiddle2",
"mixamorig:RightHandMiddle3",
"mixamorig:RightHandMiddle4",
"mixamorig:RightHandMiddle4_end"
],
"ring.R": [
"mixamorig:RightHandRing1",
"mixamorig:RightHandRing2",
"mixamorig:RightHandRing3",
"mixamorig:RightHandRing4",
"mixamorig:RightHandRing4_end"
],
"pinky.R": [
"mixamorig:RightHandPinky1",
"mixamorig:RightHandPinky2",
"mixamorig:RightHandPinky3",
"mixamorig:RightHandPinky4",
"mixamorig:RightHandPinky4_end"
]
},
"chains": [],
"twist": [],
"surfaces": [
{
"mesh": "Body",
"surface": 0,
"material": "N00_000_00_Body_00_SKIN (Instance)",
"class": "body",
"detail": "skin",
"why": "material name says 'skin'",
"verts": 5452,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 1,
"material": "N00_000_00_HairBack_00_HAIR (Instance)",
"class": "hair",
"detail": "hair",
"why": "material name says 'hair'",
"verts": 760,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 2,
"material": "N00_002_03_Tops_01_CLOTH (Instance)",
"class": "cloth",
"detail": "garment",
"why": "material name says 'garment'",
"verts": 908,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 3,
"material": "N00_001_01_Shoes_01_CLOTH (Instance)",
"class": "cloth",
"detail": "garment",
"why": "material name says 'garment'",
"verts": 390,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 4,
"material": "N00_000_00_FaceMouth_00_FACE (Instance)",
"class": "body",
"detail": "face",
"why": "material name says 'face'",
"verts": 1053,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 5,
"material": "N00_000_00_EyeIris_00_EYE (Instance)",
"class": "body",
"detail": "eyes",
"why": "material name says 'eyes'",
"verts": 214,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 6,
"material": "N00_000_00_EyeHighlight_00_EYE (Instance)",
"class": "body",
"detail": "eyes",
"why": "material name says 'eyes'",
"verts": 38,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 7,
"material": "N00_000_00_Face_00_SKIN (Instance)",
"class": "body",
"detail": "face",
"why": "material name says 'face'",
"verts": 2266,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 8,
"material": "N00_000_00_EyeWhite_00_EYE (Instance)",
"class": "body",
"detail": "eyes",
"why": "material name says 'eyes'",
"verts": 290,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 9,
"material": "N00_000_00_FaceBrow_00_FACE (Instance)",
"class": "body",
"detail": "face",
"why": "material name says 'face'",
"verts": 52,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 10,
"material": "N00_000_00_FaceEyeline_00_FACE (Instance)",
"class": "body",
"detail": "face",
"why": "material name says 'face'",
"verts": 126,
"textured": false,
"chain_share": {}
},
{
"mesh": "Body",
"surface": 11,
"material": "N00_000_Hair_00_HAIR (Instance)",
"class": "hair",
"detail": "hair",
"why": "material name says 'hair'",
"verts": 7355,
"textured": false,
"chain_share": {}
}
],
"colliders": [
{
"bone": "mixamorig:Hips",
"child": "mixamorig:Spine",
"from": 0.0,
"radius_head": 0.1341,
"radius_tail": 0.1341,
"radius": 0.1341,
"lid": true
},
{
"bone": "mixamorig:LeftUpLeg",
"child": "mixamorig:LeftLeg",
"from": 0.1,
"radius_head": 0.0985,
"radius_tail": 0.0605,
"radius": 0.0605
},
{
"bone": "mixamorig:RightUpLeg",
"child": "mixamorig:RightLeg",
"from": 0.1,
"radius_head": 0.0985,
"radius_tail": 0.0605,
"radius": 0.0605
},
{
"bone": "mixamorig:LeftLeg",
"child": "mixamorig:LeftFoot",
"from": 0.1,
"radius_head": 0.1172,
"radius_tail": 0.0507,
"radius": 0.0507
},
{
"bone": "mixamorig:RightLeg",
"child": "mixamorig:RightFoot",
"from": 0.1,
"radius_head": 0.1172,
"radius_tail": 0.0507,
"radius": 0.0507
}
],
"weights_authored": true,
"driven_bones": [
"mixamorig:Head",
"mixamorig:Hips",
"mixamorig:LeftArm",
"mixamorig:LeftFoot",
"mixamorig:LeftForeArm",
"mixamorig:LeftHand",
"mixamorig:LeftLeg",
"mixamorig:LeftShoulder",
"mixamorig:LeftToeBase",
"mixamorig:LeftUpLeg",
"mixamorig:Neck",
"mixamorig:RightArm",
"mixamorig:RightFoot",
"mixamorig:RightForeArm",
"mixamorig:RightHand",
"mixamorig:RightLeg",
"mixamorig:RightShoulder",
"mixamorig:RightToeBase",
"mixamorig:RightUpLeg",
"mixamorig:Spine",
"mixamorig:Spine1",
"mixamorig:Spine2"
]
}
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
+202 -202
View File
@@ -1,207 +1,207 @@
{ {
"roles": { "roles": {
"hips": "DEF-hips", "hips": "DEF-hips",
"head": "DEF-head", "head": "DEF-head",
"neck": "DEF-neck", "neck": "DEF-neck",
"spine": [ "spine": [
"DEF-spine.001", "DEF-spine.001",
"DEF-spine.002", "DEF-spine.002",
"DEF-spine.003", "DEF-spine.003",
"DEF-neck", "DEF-neck",
"DEF-head" "DEF-head"
],
"shoulder.L": "DEF-shoulder.L",
"shoulder.R": "DEF-shoulder.R",
"upper_arm.L": "DEF-upper_arm.L",
"upper_arm.R": "DEF-upper_arm.R",
"forearm.R": "DEF-forearm.R",
"forearm.L": "DEF-forearm.L",
"thigh.L": "DEF-thigh.L",
"thigh.R": "DEF-thigh.R",
"foot.R": "DEF-foot.R",
"foot.L": "DEF-foot.L",
"shin.L": "DEF-shin.L",
"shin.R": "DEF-shin.R",
"hand.R": "DEF-hand.R",
"hand.L": "DEF-hand.L",
"toe.L": "DEF-toe.L",
"toe.R": "DEF-toe.R"
},
"fingers": {
"thumb.L": [
"DEF-thumb.01.L",
"DEF-thumb.02.L",
"DEF-thumb.03.L"
],
"index.L": [
"DEF-f_index.01.L",
"DEF-f_index.02.L",
"DEF-f_index.03.L"
],
"middle.L": [
"DEF-f_middle.01.L",
"DEF-f_middle.02.L",
"DEF-f_middle.03.L"
],
"ring.L": [
"DEF-f_ring.01.L",
"DEF-f_ring.02.L",
"DEF-f_ring.03.L"
],
"pinky.L": [
"DEF-f_pinky.01.L",
"DEF-f_pinky.02.L",
"DEF-f_pinky.03.L"
],
"thumb.R": [
"DEF-thumb.01.R",
"DEF-thumb.02.R",
"DEF-thumb.03.R"
],
"index.R": [
"DEF-f_index.01.R",
"DEF-f_index.02.R",
"DEF-f_index.03.R"
],
"middle.R": [
"DEF-f_middle.01.R",
"DEF-f_middle.02.R",
"DEF-f_middle.03.R"
],
"ring.R": [
"DEF-f_ring.01.R",
"DEF-f_ring.02.R",
"DEF-f_ring.03.R"
],
"pinky.R": [
"DEF-f_pinky.01.R",
"DEF-f_pinky.02.R",
"DEF-f_pinky.03.R"
]
},
"chains": [],
"twist": [],
"surfaces": [
{
"mesh": "Mannequin",
"surface": 0,
"material": "M_Main",
"class": "body",
"detail": "skin",
"why": "no name or weight evidence \u2014 treated as body",
"verts": 3391,
"textured": false,
"chain_share": {}
},
{
"mesh": "Mannequin",
"surface": 1,
"material": "M_Joints",
"class": "body",
"detail": "skin",
"why": "no name or weight evidence \u2014 treated as body",
"verts": 5157,
"textured": false,
"chain_share": {}
}
], ],
"colliders": [ "shoulder.L": "DEF-shoulder.L",
{ "shoulder.R": "DEF-shoulder.R",
"bone": "DEF-hips", "upper_arm.R": "DEF-upper_arm.R",
"child": "DEF-spine.001", "upper_arm.L": "DEF-upper_arm.L",
"from": 0.0, "forearm.L": "DEF-forearm.L",
"radius_head": 0.1381, "forearm.R": "DEF-forearm.R",
"radius_tail": 0.1381, "thigh.L": "DEF-thigh.L",
"radius": 0.1381, "thigh.R": "DEF-thigh.R",
"lid": true "foot.R": "DEF-foot.R",
}, "foot.L": "DEF-foot.L",
{ "shin.R": "DEF-shin.R",
"bone": "DEF-thigh.L", "shin.L": "DEF-shin.L",
"child": "DEF-shin.L", "hand.R": "DEF-hand.R",
"from": 0.1, "hand.L": "DEF-hand.L",
"radius_head": 0.1037, "toe.R": "DEF-toe.R",
"radius_tail": 0.0733, "toe.L": "DEF-toe.L"
"radius": 0.0733 },
}, "fingers": {
{ "thumb.L": [
"bone": "DEF-thigh.R", "DEF-thumb.01.L",
"child": "DEF-shin.R", "DEF-thumb.02.L",
"from": 0.1, "DEF-thumb.03.L"
"radius_head": 0.1037,
"radius_tail": 0.0733,
"radius": 0.0733
},
{
"bone": "DEF-shin.L",
"child": "DEF-foot.L",
"from": 0.1,
"radius_head": 0.0927,
"radius_tail": 0.0516,
"radius": 0.0516
},
{
"bone": "DEF-shin.R",
"child": "DEF-foot.R",
"from": 0.1,
"radius_head": 0.0927,
"radius_tail": 0.0516,
"radius": 0.0516
}
], ],
"weights_authored": true, "index.L": [
"driven_bones": [ "DEF-f_index.01.L",
"DEF-f_index.01.L", "DEF-f_index.02.L",
"DEF-f_index.01.R", "DEF-f_index.03.L"
"DEF-f_index.02.L", ],
"DEF-f_index.02.R", "middle.L": [
"DEF-f_index.03.L", "DEF-f_middle.01.L",
"DEF-f_index.03.R", "DEF-f_middle.02.L",
"DEF-f_middle.01.L", "DEF-f_middle.03.L"
"DEF-f_middle.01.R", ],
"DEF-f_middle.02.L", "ring.L": [
"DEF-f_middle.02.R", "DEF-f_ring.01.L",
"DEF-f_middle.03.L", "DEF-f_ring.02.L",
"DEF-f_middle.03.R", "DEF-f_ring.03.L"
"DEF-f_pinky.01.L", ],
"DEF-f_pinky.01.R", "pinky.L": [
"DEF-f_pinky.02.L", "DEF-f_pinky.01.L",
"DEF-f_pinky.02.R", "DEF-f_pinky.02.L",
"DEF-f_pinky.03.L", "DEF-f_pinky.03.L"
"DEF-f_pinky.03.R", ],
"DEF-f_ring.01.L", "thumb.R": [
"DEF-f_ring.01.R", "DEF-thumb.01.R",
"DEF-f_ring.02.L", "DEF-thumb.02.R",
"DEF-f_ring.02.R", "DEF-thumb.03.R"
"DEF-f_ring.03.L", ],
"DEF-f_ring.03.R", "index.R": [
"DEF-foot.L", "DEF-f_index.01.R",
"DEF-foot.R", "DEF-f_index.02.R",
"DEF-forearm.L", "DEF-f_index.03.R"
"DEF-forearm.R", ],
"DEF-hand.L", "middle.R": [
"DEF-hand.R", "DEF-f_middle.01.R",
"DEF-head", "DEF-f_middle.02.R",
"DEF-hips", "DEF-f_middle.03.R"
"DEF-neck", ],
"DEF-shin.L", "ring.R": [
"DEF-shin.R", "DEF-f_ring.01.R",
"DEF-shoulder.L", "DEF-f_ring.02.R",
"DEF-shoulder.R", "DEF-f_ring.03.R"
"DEF-spine.001", ],
"DEF-spine.002", "pinky.R": [
"DEF-spine.003", "DEF-f_pinky.01.R",
"DEF-thigh.L", "DEF-f_pinky.02.R",
"DEF-thigh.R", "DEF-f_pinky.03.R"
"DEF-thumb.01.L",
"DEF-thumb.01.R",
"DEF-thumb.02.L",
"DEF-thumb.02.R",
"DEF-thumb.03.L",
"DEF-thumb.03.R",
"DEF-toe.L",
"DEF-toe.R",
"DEF-upper_arm.L",
"DEF-upper_arm.R",
"root"
] ]
},
"chains": [],
"twist": [],
"colliders": [
{
"bone": "DEF-hips",
"child": "DEF-spine.001",
"from": 0.0,
"radius_head": 0.1381,
"radius_tail": 0.1381,
"radius": 0.1381,
"lid": true
},
{
"bone": "DEF-thigh.L",
"child": "DEF-shin.L",
"from": 0.1,
"radius_head": 0.1037,
"radius_tail": 0.0733,
"radius": 0.0733
},
{
"bone": "DEF-thigh.R",
"child": "DEF-shin.R",
"from": 0.1,
"radius_head": 0.1037,
"radius_tail": 0.0733,
"radius": 0.0733
},
{
"bone": "DEF-shin.L",
"child": "DEF-foot.L",
"from": 0.1,
"radius_head": 0.0927,
"radius_tail": 0.0516,
"radius": 0.0516
},
{
"bone": "DEF-shin.R",
"child": "DEF-foot.R",
"from": 0.1,
"radius_head": 0.0927,
"radius_tail": 0.0516,
"radius": 0.0516
}
],
"weights_authored": true,
"driven_bones": [
"DEF-f_index.01.L",
"DEF-f_index.01.R",
"DEF-f_index.02.L",
"DEF-f_index.02.R",
"DEF-f_index.03.L",
"DEF-f_index.03.R",
"DEF-f_middle.01.L",
"DEF-f_middle.01.R",
"DEF-f_middle.02.L",
"DEF-f_middle.02.R",
"DEF-f_middle.03.L",
"DEF-f_middle.03.R",
"DEF-f_pinky.01.L",
"DEF-f_pinky.01.R",
"DEF-f_pinky.02.L",
"DEF-f_pinky.02.R",
"DEF-f_pinky.03.L",
"DEF-f_pinky.03.R",
"DEF-f_ring.01.L",
"DEF-f_ring.01.R",
"DEF-f_ring.02.L",
"DEF-f_ring.02.R",
"DEF-f_ring.03.L",
"DEF-f_ring.03.R",
"DEF-foot.L",
"DEF-foot.R",
"DEF-forearm.L",
"DEF-forearm.R",
"DEF-hand.L",
"DEF-hand.R",
"DEF-head",
"DEF-hips",
"DEF-neck",
"DEF-shin.L",
"DEF-shin.R",
"DEF-shoulder.L",
"DEF-shoulder.R",
"DEF-spine.001",
"DEF-spine.002",
"DEF-spine.003",
"DEF-thigh.L",
"DEF-thigh.R",
"DEF-thumb.01.L",
"DEF-thumb.01.R",
"DEF-thumb.02.L",
"DEF-thumb.02.R",
"DEF-thumb.03.L",
"DEF-thumb.03.R",
"DEF-toe.L",
"DEF-toe.R",
"DEF-upper_arm.L",
"DEF-upper_arm.R",
"root"
],
"surfaces": [
{
"mesh": "Mannequin",
"surface": 0,
"material": "M_Main",
"class": "body",
"detail": "skin",
"why": "no name or weight evidence \u2014 treated as body",
"verts": 3391,
"textured": false,
"chain_share": {}
},
{
"mesh": "Mannequin",
"surface": 1,
"material": "M_Joints",
"class": "body",
"detail": "skin",
"why": "no name or weight evidence \u2014 treated as body",
"verts": 5157,
"textured": false,
"chain_share": {}
}
]
} }
Binary file not shown.
+224 -224
View File
@@ -1,229 +1,229 @@
{ {
"roles": { "roles": {
"hips": "DEF-hips", "hips": "DEF-hips",
"head": "DEF-head", "head": "DEF-head",
"neck": "DEF-neck", "neck": "DEF-neck",
"spine": [ "spine": [
"DEF-spine.001", "DEF-spine.001",
"DEF-spine.002", "DEF-spine.002",
"DEF-spine.003", "DEF-spine.003",
"DEF-neck", "DEF-neck",
"DEF-head" "DEF-head"
],
"shoulder.L": "DEF-shoulder.L",
"shoulder.R": "DEF-shoulder.R",
"upper_arm.L": "DEF-upper_arm.L",
"upper_arm.R": "DEF-upper_arm.R",
"forearm.L": "DEF-forearm.L",
"forearm.R": "DEF-forearm.R",
"thigh.L": "DEF-thigh.L",
"thigh.R": "DEF-thigh.R",
"foot.R": "DEF-foot.R",
"foot.L": "DEF-foot.L",
"shin.R": "DEF-shin.R",
"shin.L": "DEF-shin.L",
"hand.L": "DEF-hand.L",
"hand.R": "DEF-hand.R",
"toe.R": "DEF-toe.R",
"toe.L": "DEF-toe.L"
},
"fingers": {
"thumb.L": [
"DEF-thumb.01.L",
"DEF-thumb.02.L",
"DEF-thumb.03.L"
],
"index.L": [
"DEF-f_index.01.L",
"DEF-f_index.02.L",
"DEF-f_index.03.L"
],
"middle.L": [
"DEF-f_middle.01.L",
"DEF-f_middle.02.L",
"DEF-f_middle.03.L"
],
"ring.L": [
"DEF-f_ring.01.L",
"DEF-f_ring.02.L",
"DEF-f_ring.03.L"
],
"pinky.L": [
"DEF-f_pinky.01.L",
"DEF-f_pinky.02.L",
"DEF-f_pinky.03.L"
],
"thumb.R": [
"DEF-thumb.01.R",
"DEF-thumb.02.R",
"DEF-thumb.03.R"
],
"index.R": [
"DEF-f_index.01.R",
"DEF-f_index.02.R",
"DEF-f_index.03.R"
],
"middle.R": [
"DEF-f_middle.01.R",
"DEF-f_middle.02.R",
"DEF-f_middle.03.R"
],
"ring.R": [
"DEF-f_ring.01.R",
"DEF-f_ring.02.R",
"DEF-f_ring.03.R"
],
"pinky.R": [
"DEF-f_pinky.01.R",
"DEF-f_pinky.02.R",
"DEF-f_pinky.03.R"
]
},
"chains": [],
"twist": [],
"surfaces": [
{
"mesh": "Object_2",
"surface": 0,
"material": "body",
"class": "body",
"detail": "skin",
"why": "material name says 'skin'",
"verts": 1500,
"textured": true,
"chain_share": {}
},
{
"mesh": "Object_2",
"surface": 1,
"material": "body_parts",
"class": "body",
"detail": "skin",
"why": "material name says 'skin'",
"verts": 330,
"textured": true,
"chain_share": {}
},
{
"mesh": "Object_2",
"surface": 2,
"material": "hair",
"class": "hair",
"detail": "hair",
"why": "material name says 'hair'",
"verts": 500,
"textured": true,
"chain_share": {}
},
{
"mesh": "Object_2",
"surface": 3,
"material": "face",
"class": "body",
"detail": "face",
"why": "material name says 'face'",
"verts": 461,
"textured": true,
"chain_share": {}
}
], ],
"colliders": [ "shoulder.L": "DEF-shoulder.L",
{ "shoulder.R": "DEF-shoulder.R",
"bone": "DEF-hips", "upper_arm.L": "DEF-upper_arm.L",
"child": "DEF-spine.001", "upper_arm.R": "DEF-upper_arm.R",
"from": 0.0, "forearm.L": "DEF-forearm.L",
"radius_head": 0.1915, "forearm.R": "DEF-forearm.R",
"radius_tail": 0.1915, "thigh.L": "DEF-thigh.L",
"radius": 0.1915, "thigh.R": "DEF-thigh.R",
"lid": true "foot.R": "DEF-foot.R",
}, "foot.L": "DEF-foot.L",
{ "shin.R": "DEF-shin.R",
"bone": "DEF-thigh.L", "shin.L": "DEF-shin.L",
"child": "DEF-shin.L", "hand.R": "DEF-hand.R",
"from": 0.1, "hand.L": "DEF-hand.L",
"radius_head": 0.1435, "toe.R": "DEF-toe.R",
"radius_tail": 0.1304, "toe.L": "DEF-toe.L"
"radius": 0.1304 },
}, "fingers": {
{ "thumb.L": [
"bone": "DEF-thigh.R", "DEF-thumb.01.L",
"child": "DEF-shin.R", "DEF-thumb.02.L",
"from": 0.1, "DEF-thumb.03.L"
"radius_head": 0.1084,
"radius_tail": 0.1084,
"radius": 0.1084
},
{
"bone": "DEF-shin.L",
"child": "DEF-foot.L",
"from": 0.1,
"radius_head": 0.0862,
"radius_tail": 0.0728,
"radius": 0.0728
},
{
"bone": "DEF-shin.R",
"child": "DEF-foot.R",
"from": 0.1,
"radius_head": 0.0862,
"radius_tail": 0.0728,
"radius": 0.0728
}
], ],
"weights_authored": false, "index.L": [
"driven_bones": [ "DEF-f_index.01.L",
"DEF-f_index.01.L", "DEF-f_index.02.L",
"DEF-f_index.01.R", "DEF-f_index.03.L"
"DEF-f_index.02.L", ],
"DEF-f_index.02.R", "middle.L": [
"DEF-f_index.03.L", "DEF-f_middle.01.L",
"DEF-f_index.03.R", "DEF-f_middle.02.L",
"DEF-f_middle.01.L", "DEF-f_middle.03.L"
"DEF-f_middle.01.R", ],
"DEF-f_middle.02.L", "ring.L": [
"DEF-f_middle.02.R", "DEF-f_ring.01.L",
"DEF-f_middle.03.L", "DEF-f_ring.02.L",
"DEF-f_middle.03.R", "DEF-f_ring.03.L"
"DEF-f_pinky.01.L", ],
"DEF-f_pinky.01.R", "pinky.L": [
"DEF-f_pinky.02.L", "DEF-f_pinky.01.L",
"DEF-f_pinky.02.R", "DEF-f_pinky.02.L",
"DEF-f_pinky.03.L", "DEF-f_pinky.03.L"
"DEF-f_pinky.03.R", ],
"DEF-f_ring.01.L", "thumb.R": [
"DEF-f_ring.01.R", "DEF-thumb.01.R",
"DEF-f_ring.02.L", "DEF-thumb.02.R",
"DEF-f_ring.02.R", "DEF-thumb.03.R"
"DEF-f_ring.03.L", ],
"DEF-f_ring.03.R", "index.R": [
"DEF-foot.L", "DEF-f_index.01.R",
"DEF-foot.R", "DEF-f_index.02.R",
"DEF-forearm.L", "DEF-f_index.03.R"
"DEF-forearm.R", ],
"DEF-hand.L", "middle.R": [
"DEF-hand.R", "DEF-f_middle.01.R",
"DEF-head", "DEF-f_middle.02.R",
"DEF-hips", "DEF-f_middle.03.R"
"DEF-neck", ],
"DEF-shin.L", "ring.R": [
"DEF-shin.R", "DEF-f_ring.01.R",
"DEF-shoulder.L", "DEF-f_ring.02.R",
"DEF-shoulder.R", "DEF-f_ring.03.R"
"DEF-spine.001", ],
"DEF-spine.002", "pinky.R": [
"DEF-spine.003", "DEF-f_pinky.01.R",
"DEF-thigh.L", "DEF-f_pinky.02.R",
"DEF-thigh.R", "DEF-f_pinky.03.R"
"DEF-thumb.01.L",
"DEF-thumb.01.R",
"DEF-thumb.02.L",
"DEF-thumb.02.R",
"DEF-thumb.03.L",
"DEF-thumb.03.R",
"DEF-toe.L",
"DEF-toe.R",
"DEF-upper_arm.L",
"DEF-upper_arm.R",
"root"
] ]
},
"chains": [],
"twist": [],
"colliders": [
{
"bone": "DEF-hips",
"child": "DEF-spine.001",
"from": 0.0,
"radius_head": 0.1913,
"radius_tail": 0.1913,
"radius": 0.1913,
"lid": true
},
{
"bone": "DEF-thigh.L",
"child": "DEF-shin.L",
"from": 0.1,
"radius_head": 0.1435,
"radius_tail": 0.1304,
"radius": 0.1304
},
{
"bone": "DEF-thigh.R",
"child": "DEF-shin.R",
"from": 0.1,
"radius_head": 0.1084,
"radius_tail": 0.1084,
"radius": 0.1084
},
{
"bone": "DEF-shin.L",
"child": "DEF-foot.L",
"from": 0.1,
"radius_head": 0.0862,
"radius_tail": 0.0728,
"radius": 0.0728
},
{
"bone": "DEF-shin.R",
"child": "DEF-foot.R",
"from": 0.1,
"radius_head": 0.0862,
"radius_tail": 0.0728,
"radius": 0.0728
}
],
"weights_authored": false,
"driven_bones": [
"DEF-f_index.01.L",
"DEF-f_index.01.R",
"DEF-f_index.02.L",
"DEF-f_index.02.R",
"DEF-f_index.03.L",
"DEF-f_index.03.R",
"DEF-f_middle.01.L",
"DEF-f_middle.01.R",
"DEF-f_middle.02.L",
"DEF-f_middle.02.R",
"DEF-f_middle.03.L",
"DEF-f_middle.03.R",
"DEF-f_pinky.01.L",
"DEF-f_pinky.01.R",
"DEF-f_pinky.02.L",
"DEF-f_pinky.02.R",
"DEF-f_pinky.03.L",
"DEF-f_pinky.03.R",
"DEF-f_ring.01.L",
"DEF-f_ring.01.R",
"DEF-f_ring.02.L",
"DEF-f_ring.02.R",
"DEF-f_ring.03.L",
"DEF-f_ring.03.R",
"DEF-foot.L",
"DEF-foot.R",
"DEF-forearm.L",
"DEF-forearm.R",
"DEF-hand.L",
"DEF-hand.R",
"DEF-head",
"DEF-hips",
"DEF-neck",
"DEF-shin.L",
"DEF-shin.R",
"DEF-shoulder.L",
"DEF-shoulder.R",
"DEF-spine.001",
"DEF-spine.002",
"DEF-spine.003",
"DEF-thigh.L",
"DEF-thigh.R",
"DEF-thumb.01.L",
"DEF-thumb.01.R",
"DEF-thumb.02.L",
"DEF-thumb.02.R",
"DEF-thumb.03.L",
"DEF-thumb.03.R",
"DEF-toe.L",
"DEF-toe.R",
"DEF-upper_arm.L",
"DEF-upper_arm.R",
"root"
],
"surfaces": [
{
"mesh": "Object_2",
"surface": 0,
"material": "body",
"class": "body",
"detail": "skin",
"why": "material name says 'skin'",
"verts": 1496,
"textured": true,
"chain_share": {}
},
{
"mesh": "Object_2",
"surface": 1,
"material": "body_parts",
"class": "body",
"detail": "skin",
"why": "material name says 'skin'",
"verts": 330,
"textured": true,
"chain_share": {}
},
{
"mesh": "Object_2",
"surface": 2,
"material": "hair",
"class": "hair",
"detail": "hair",
"why": "material name says 'hair'",
"verts": 500,
"textured": true,
"chain_share": {}
},
{
"mesh": "Object_2",
"surface": 3,
"material": "face",
"class": "body",
"detail": "face",
"why": "material name says 'face'",
"verts": 461,
"textured": true,
"chain_share": {}
}
]
} }
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
@@ -1,10 +0,0 @@
{
"name": "3D Anime Female Adventurer Character Model",
"version": "1.2",
"author": "Dawn to Dusk Games",
"author_url": "https://dawn-to-dusk-games.itch.io/",
"license": "CC0 1.0 Universal",
"license_url": "https://creativecommons.org/publicdomain/zero/1.0/",
"source_url": "https://dawn-to-dusk-games.itch.io/3d-anime-female-adventurer",
"changes": "Outfit 01 retargeted to the Papaya-Shooter animation library; game-native mecha equipment added at runtime."
}
File diff suppressed because it is too large Load Diff
-54
View File
@@ -5,15 +5,6 @@
"name": "Miku", "name": "Miku",
"description": "Hatsune Miku \u2014 Virtual Idol", "description": "Hatsune Miku \u2014 Virtual Idol",
"model": "res://assets/characters/skins/miku.glb", "model": "res://assets/characters/skins/miku.glb",
"viewmodel": {
"sleeve": "#168F98",
"accent": "#252632",
"skin": "#F5CDBB",
"glove": "#20212C",
"nail": "#20A9B1",
"hand_style": "fingerless",
"sleeve_ratio": 0.78
},
"unlocked": true "unlocked": true
}, },
{ {
@@ -21,15 +12,6 @@
"name": "Taila", "name": "Taila",
"description": "", "description": "",
"model": "res://assets/characters/skins/taila.glb", "model": "res://assets/characters/skins/taila.glb",
"viewmodel": {
"sleeve": "#171D32",
"accent": "#E8772D",
"skin": "#EFC6B7",
"glove": "#151826",
"nail": "#E8772D",
"hand_style": "glove",
"sleeve_ratio": 0.78
},
"unlocked": true "unlocked": true
}, },
{ {
@@ -37,15 +19,6 @@
"name": "Mannequin", "name": "Mannequin",
"description": "Quaternius reference mannequin (CC0)", "description": "Quaternius reference mannequin (CC0)",
"model": "res://assets/characters/skins/mannequin.glb", "model": "res://assets/characters/skins/mannequin.glb",
"viewmodel": {
"sleeve": "#D9692D",
"accent": "#6D4C9B",
"skin": "#D9692D",
"glove": "#6D4C9B",
"nail": "#F0A14B",
"hand_style": "android",
"sleeve_ratio": 0.84
},
"unlocked": true "unlocked": true
}, },
{ {
@@ -53,15 +26,6 @@
"name": "Kiyoko", "name": "Kiyoko",
"description": "Kiyoko School Girl \u2014 VRoid (CC-BY, Kasujin)", "description": "Kiyoko School Girl \u2014 VRoid (CC-BY, Kasujin)",
"model": "res://assets/characters/skins/kiyoko.glb", "model": "res://assets/characters/skins/kiyoko.glb",
"viewmodel": {
"sleeve": "#ECE7E3",
"accent": "#253151",
"skin": "#F0C6B2",
"glove": "#253151",
"nail": "#E9A9B4",
"hand_style": "bare",
"sleeve_ratio": 0.32
},
"unlocked": true "unlocked": true
}, },
{ {
@@ -69,15 +33,6 @@
"name": "Aria", "name": "Aria",
"description": "Anime Girl Rigged \u2014 VRoid (CC-BY, dequeijospizza)", "description": "Anime Girl Rigged \u2014 VRoid (CC-BY, dequeijospizza)",
"model": "res://assets/characters/skins/aria.glb", "model": "res://assets/characters/skins/aria.glb",
"viewmodel": {
"sleeve": "#E8E8E5",
"accent": "#27252D",
"skin": "#EFC7B7",
"glove": "#27252D",
"nail": "#DFA0AE",
"hand_style": "bare",
"sleeve_ratio": 0.30
},
"unlocked": true "unlocked": true
}, },
{ {
@@ -85,15 +40,6 @@
"name": "Momo", "name": "Momo",
"description": "Momo Ayase, DANDADAN (CC-BY, HiGuys920)", "description": "Momo Ayase, DANDADAN (CC-BY, HiGuys920)",
"model": "res://assets/characters/skins/momo.glb", "model": "res://assets/characters/skins/momo.glb",
"viewmodel": {
"sleeve": "#D66F98",
"accent": "#34303A",
"skin": "#F0C5B2",
"glove": "#34303A",
"nail": "#D66F98",
"hand_style": "fingerless",
"sleeve_ratio": 0.80
},
"unlocked": true "unlocked": true
} }
] ]
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

-96
View File
@@ -1,96 +0,0 @@
#ifndef ANIME_CLOUDS_INCLUDED
#define ANIME_CLOUDS_INCLUDED
// Organic 2D cloud field shared by the actual Sky shader and the fullscreen
// ink composite. Broad domain-warped masses define the cloud, a second rotated
// octave breaks up its contour into billows, and a quiet high-frequency layer
// erodes the edge. Unlike a grid of radial primitives, no stage exposes a
// circle or a square as the final silhouette.
float cloud_hash(vec2 p) {
// Sine-free hash: substantially cheaper when the field is sampled several
// times to estimate the direction the sun meets the cloud.
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float cloud_noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
// Quintic interpolation keeps both the value and its slope continuous at
// cell boundaries, so a density threshold cannot reveal the noise grid.
vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
float a = cloud_hash(i);
float b = cloud_hash(i + vec2(1.0, 0.0));
float c = cloud_hash(i + vec2(0.0, 1.0));
float d = cloud_hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float cloud_fbm(vec2 p) {
mat2 turn = mat2(vec2(0.80, 0.60), vec2(-0.60, 0.80));
float value = cloud_noise(p) * 0.52;
p = turn * p * 2.03 + vec2(9.17, 4.31);
value += cloud_noise(p) * 0.27;
p = turn * p * 2.07 + vec2(3.73, 12.61);
value += cloud_noise(p) * 0.14;
p = turn * p * 2.01 + vec2(15.19, 7.07);
value += cloud_noise(p) * 0.07;
return value;
}
float cloud_detail(vec2 p) {
mat2 turn = mat2(vec2(0.86, 0.51), vec2(-0.51, 0.86));
return cloud_noise(turn * p * 2.75 + vec2(23.7, 6.1)) * 0.68
+ cloud_noise(p * 5.20 + vec2(2.9, 31.4)) * 0.32;
}
float cloud_field(vec2 p) {
// Low-frequency vector warp makes the mass curl and fork without making
// the outline busy. The two components use unrelated offsets so this does
// not merely slide the source noise along its own contour.
vec2 domain = p * 0.34;
vec2 warp = vec2(
cloud_noise(domain + vec2(17.2, 4.8)),
cloud_noise(domain + vec2(3.1, 29.6))) - 0.5;
vec2 q = p + warp * 1.35;
float mass = cloud_fbm(q * 0.52);
float billows = cloud_fbm(
mat2(vec2(0.74, 0.67), vec2(-0.67, 0.74)) * q * 1.18
+ vec2(11.3, 8.7));
float erosion = cloud_detail(q);
float density = mass * 0.70 + billows * 0.24 + erosion * 0.06;
// A third, much broader field separates the weather into distinct banks.
// Without this macro mask a perfectly organic contour can still connect
// across half the dome and read as one enormous sheet instead of clouds.
float islands = cloud_fbm(q * 0.24 + vec2(41.7, 18.3));
return density - (1.0 - islands) * 0.16;
}
float cloud_sun_response(vec2 p, vec3 sun_direction) {
// Treat density as a painted height field. Sampling a short distance toward
// and away from the sun tells us which edge faces it. This gives the cloud
// a bright leading rim and a colored lee side, and naturally reverses when
// the DirectionalLight turns.
vec2 sun_xz = sun_direction.xz;
float horizontal = length(sun_xz);
vec2 axis = horizontal > 0.0001
? sun_xz / horizontal : vec2(0.7071, 0.7071);
float toward = cloud_field(p + axis * 0.075);
float away = cloud_field(p - axis * 0.075);
float directional_slope = (away - toward) / 0.15;
// A high sun lights more of the cloud face; a low sun makes the directional
// division stronger and leaves a broader colored underside.
float overhead = clamp(sun_direction.y, 0.0, 1.0);
return smoothstep(-0.22, 0.22, directional_slope + overhead * 0.08);
}
#endif
@@ -1 +0,0 @@
uid://dbh0unyul1h7d
-90
View File
@@ -1,90 +0,0 @@
shader_type sky;
// Cel sky: a painted gradient with rounded, flat-toned anime clouds.
//
// ProceduralSkyMaterial gives a smooth two-colour ramp, and a smooth ramp over
// 40% of the frame is exactly what tools/levels.py was reporting as dead%:
// somewhere between a quarter and a third of every shot was a single flat
// shade with nothing in it. Sky is the cheapest real estate in the picture and
// it was empty.
//
// The clouds are drawn the way an anime background artist draws them — a
// silhouette filled with two or three flat tones and a crisp edge, NOT a soft
// volumetric puff. That is both the correct style and the cheap option.
uniform vec3 top_color : source_color = vec3(0.18, 0.40, 0.85);
uniform vec3 horizon_color : source_color = vec3(0.72, 0.88, 0.98);
uniform vec3 ground_color : source_color = vec3(0.24, 0.22, 0.30);
uniform vec3 cloud_color : source_color = vec3(1.0, 0.99, 0.97);
uniform vec3 cloud_shadow : source_color = vec3(0.72, 0.75, 0.88);
// Raise to thin the clouds out. This is a threshold on the noise, so it is the
// knob for "how much sky is covered", not a density.
uniform float cloud_cover : hint_range(0.0, 1.0) = 0.52;
uniform float cloud_scale : hint_range(0.2, 8.0) = 1.6;
uniform float cloud_softness : hint_range(0.001, 0.2) = 0.035;
// Slow. This is a competitive shooter and anything the eye can track in the
// sky is a distraction; it only needs to move enough that the sky is not a
// photograph.
uniform float drift : hint_range(0.0, 0.02) = 0.0022;
uniform float horizon_falloff : hint_range(0.1, 2.0) = 0.45;
uniform float sun_halo : hint_range(0.0, 2.0) = 0.35;
#include "res://assets/shaders/anime_clouds.gdshaderinc"
void sky() {
float up = clamp(EYEDIR.y, 0.0, 1.0);
float down = clamp(-EYEDIR.y, 0.0, 1.0);
vec3 col = EYEDIR.y >= 0.0
? mix(horizon_color, top_color, pow(up, horizon_falloff))
: mix(horizon_color, ground_color, pow(down, 0.62));
// Soft halo around the sun. The procedural sky drew a disk here; a broad
// glow reads better under the filmic shoulder and does not clip.
if (LIGHT0_ENABLED) {
float d = clamp(dot(EYEDIR, LIGHT0_DIRECTION), 0.0, 1.0);
col += LIGHT0_COLOR * pow(d, 24.0) * sun_halo;
}
if (EYEDIR.y > 0.005) {
// Project the dome onto a plane overhead, so clouds compress toward the
// horizon the way real ones do instead of smearing across the zenith.
vec2 uv = EYEDIR.xz / (EYEDIR.y + 0.18) * cloud_scale + vec2(TIME * drift, 0.0);
float n = cloud_field(uv);
// The edge remains clean enough for the anime style, but it follows a
// layered organic field instead of revealing primitive circles.
float edge = max(cloud_softness, fwidth(n) * 1.35);
float shape = smoothstep(cloud_cover - edge, cloud_cover + edge, n);
vec3 sun_direction = LIGHT0_ENABLED
? normalize(LIGHT0_DIRECTION)
: normalize(vec3(0.45, 0.55, -0.70));
vec3 sun_color = LIGHT0_ENABLED
? LIGHT0_COLOR : vec3(1.0, 0.91, 0.72);
float facing = cloud_sun_response(uv, sun_direction);
float core = smoothstep(
cloud_cover + 0.045 - edge,
cloud_cover + 0.155 + edge, n);
float carving = smoothstep(
0.38, 0.64, cloud_detail(uv + vec2(2.4, 9.7)));
// Three painted tones respond to the actual DirectionalLight: a colored
// lee side, a broad lit face, and a narrow sunward silver lining.
float lit_face = clamp(
0.12 + core * 0.25 + facing * 0.58 + carving * 0.05,
0.0, 1.0);
vec3 lit_color = mix(cloud_color, sun_color, 0.18);
vec3 cloud = mix(cloud_shadow, lit_color, lit_face);
float boundary = 1.0 - smoothstep(
cloud_cover + edge, cloud_cover + 0.105 + edge, n);
float silver_lining = boundary * facing * facing
* mix(0.10, 0.24, clamp(sun_direction.y, 0.0, 1.0));
cloud += sun_color * silver_lining;
// Fade out near the horizon, where the projection stretches a cloud
// into an infinitely long streak.
col = mix(col, cloud, shape * smoothstep(0.0, 0.22, EYEDIR.y));
}
COLOR = col;
}
-1
View File
@@ -1 +0,0 @@
uid://cx2rt7638txa1
+34 -291
View File
@@ -1,314 +1,57 @@
shader_type spatial; shader_type spatial;
render_mode unshaded, fog_disabled, depth_draw_never, depth_test_disabled, cull_disabled; render_mode unshaded, fog_disabled, depth_draw_never, depth_test_disabled, cull_disabled;
// ── The 3D-to-2D pass ──────────────────────────────────────────────────────── // Screen-space ink outline: detects depth and normal discontinuities and
// // draws thin dark edges over the scene — the drawn-line pass that unifies
// One fullscreen quad doing three jobs, in this order: // every model (kit, hero, character) into the cel look. Attach to a
// // fullscreen quad (POSITION override) so it runs for whatever camera renders.
// sky repaint the background, because this pass owns those pixels
// ink screen-space line work from the depth buffer
// grade the anime split-tone
//
// ## Lines come from a SECOND DIFFERENCE of linearised depth
//
// This is the change that matters, and it is worth spelling out because the
// previous version of this file did the usual thing and it was wrong:
//
// float ddiff = abs(dl - dr) + abs(du - dd); // <- first difference
//
// A first difference measures the SLOPE of the depth buffer, and slope is large
// wherever a surface is oblique to the camera — which the ground always is. So
// the road, the pavement, every roof and every long wall seen at an angle all
// carried ink, and the threshold had to be raised until it smeared rather than
// drew. Raising it then lost the real silhouettes at distance, which is why
// `depth_threshold` had to be scaled by `dc` to stop far geometry dissolving
// into noise: the whole formula was fighting itself.
//
// A second difference measures CURVATURE, and curvature is exactly zero across
// any planar surface no matter how oblique it is to the camera. So it fires on
// real silhouettes and real creases and on nothing else — a flat road grazing
// the camera produces literally no signal, and the threshold can therefore be
// set low enough to catch a distant railing.
//
// The two signs are kept apart and drawn differently, which is also from the
// reference: positive curvature (the near side of a silhouette, a convex ridge)
// inks strongly, negative curvature (an inside corner, where two surfaces meet
// away from the viewer) inks faintly. That is what an animator does — the
// outside line of a shape is heavy, the contact line inside it is light.
uniform sampler2D screen_tex : hint_screen_texture, filter_linear, repeat_disable; uniform sampler2D screen_tex : hint_screen_texture, filter_linear;
uniform sampler2D depth_tex : hint_depth_texture, filter_nearest, repeat_disable; uniform sampler2D depth_tex : hint_depth_texture, filter_nearest;
uniform sampler2D normal_roughness_tex : hint_normal_roughness_texture, uniform sampler2D normal_tex : hint_normal_roughness_texture, filter_nearest;
filter_nearest, repeat_disable;
group_uniforms ink; uniform vec4 ink_color : source_color = vec4(0.07, 0.06, 0.09, 1.0);
uniform vec4 ink_color : source_color = vec4(0.224, 0.196, 0.310, 1.0); uniform float depth_threshold : hint_range(0.0, 4.0) = 1.1;
// How far apart the taps sit, in pixels. This IS the line weight. The reference uniform float normal_threshold : hint_range(0.0, 1.0) = 0.55;
// runs 1.35 at a 1.5-2x internal resolution; this project renders at 1x, and the uniform float edge_strength : hint_range(0.0, 1.0) = 0.8;
// brief asks for a slightly harder and thicker line than the reference, so it uniform float max_distance : hint_range(10.0, 500.0) = 220.0;
// sits at 2.0 — about a two-pixel line at 1080p.
uniform float ink_thickness : hint_range(0.5, 6.0) = 2.3;
// Curvature at which a convex edge reaches full ink. Small, because the second
// difference is already normalised by distance and produces almost nothing on
// flat ground — this can be sensitive in a way the old first-difference test
// could never afford to be.
uniform float ink_sensitivity : hint_range(0.0005, 0.05) = 0.0034;
uniform float ink_concave : hint_range(0.002, 0.2) = 0.024;
uniform float ink_concave_amount : hint_range(0.0, 1.0) = 0.45;
// Hardness: where the ramp to full ink STARTS, as a fraction of the threshold.
// The reference effectively runs 0.32 — ink begins to appear at a third of the
// threshold curvature, which is a soft, tapering line. Pushing it toward 1.0
// gives a harder, more decisive one, which is what the brief asks for.
//
// It cannot BE 1.0: that is a step(), and a step on a diagonal edge is a
// staircase that shimmers the moment anything moves.
//
// It also sets the NOISE FLOOR, and that is not a side effect to ignore. A
// grazing ground plane quantises in the depth buffer into a staircase, and a
// staircase has a large second difference at every step — at 0.32 those steps
// ink faintly and the road comes out finely hatched, which is precisely what a
// first render at a wider tap spacing showed.
uniform float ink_hardness : hint_range(0.1, 0.95) = 0.62;
// How much of the surface's own colour survives inside the line. Low but not
// zero: pure flat ink over every edge reads as pasted on, a whisper of the
// underlying hue reads as drawn.
uniform float ink_hue_bleed : hint_range(0.0, 0.6) = 0.14;
uniform float ink_strength : hint_range(0.0, 1.0) = 1.0;
// Let the far background dissolve into haze instead of turning into a mess of
// busy line work.
uniform float ink_fade_start : hint_range(5.0, 400.0) = 85.0;
uniform float ink_fade_end : hint_range(10.0, 900.0) = 220.0;
// Depth taps land on the sky. Clamping rather than special-casing them keeps
// the silhouette signal huge but bounded, so an object against the sky inks at
// full weight on BOTH sides of its edge — which is where the reference's early
// return leaves the line only half as thick as it should be.
uniform float ink_sky_depth : hint_range(50.0, 4000.0) = 900.0;
// ── The grade ──────────────────────────────────────────────────────────────── float linear_depth(vec2 uv, mat4 inv_proj) {
// float d = texture(depth_tex, uv).r;
// A split-tone: cool violet into the darks, warm paper-white into the lights,
// plus a lift that keeps shadow off the floor. Godot tonemaps and converts to
// sRGB after this pass, so unlike the reference this stays in linear and does
// NOT do its own sRGB conversion — doing both would gamma the frame twice.
group_uniforms grade;
uniform bool grade_enabled = true;
uniform vec3 grade_shadow_tint : source_color = vec3(0.678, 0.659, 0.816);
uniform vec3 grade_light_tint : source_color = vec3(1.0, 0.969, 0.910);
uniform float grade_saturation : hint_range(0.0, 2.0) = 1.12;
uniform float grade_lift : hint_range(0.0, 0.2) = 0.028;
uniform float grade_warmth : hint_range(0.0, 0.3) = 0.05;
uniform float grade_vignette : hint_range(0.0, 0.6) = 0.15;
// The fullscreen pass owns the background pixels as well as the geometry ones.
// Godot's screen copy holds only the viewport clear colour at far depth, so
// sampling screen_tex there silently erases the real Environment sky — these
// repaint it with the same palette and law as anime_sky.gdshader, and
// LevelEnvironment keeps the two materials in sync.
group_uniforms sky;
uniform vec3 sky_top_color : source_color = vec3(0.18, 0.40, 0.85);
uniform vec3 sky_horizon_color : source_color = vec3(0.72, 0.88, 0.98);
uniform vec3 sky_ground_color : source_color = vec3(0.24, 0.22, 0.30);
uniform vec3 sky_cloud_color : source_color = vec3(1.0, 0.99, 0.97);
uniform vec3 sky_cloud_shadow : source_color = vec3(0.72, 0.75, 0.88);
uniform float sky_cloud_cover : hint_range(0.0, 1.0) = 0.46;
uniform float sky_cloud_scale : hint_range(0.2, 8.0) = 1.6;
uniform float sky_cloud_softness : hint_range(0.001, 0.2) = 0.035;
uniform float sky_drift : hint_range(0.0, 0.02) = 0.0022;
uniform float sky_horizon_falloff : hint_range(0.1, 2.0) = 0.45;
uniform vec3 sky_sun_direction = vec3(0.45, 0.34, -0.82);
uniform vec3 sky_sun_color : source_color = vec3(1.0, 0.91, 0.72);
uniform float sky_sun_halo : hint_range(0.0, 2.0) = 0.35;
#include "res://assets/shaders/anime_clouds.gdshaderinc"
vec3 painted_sky(vec3 dir) {
float up = clamp(dir.y, 0.0, 1.0);
float down = clamp(-dir.y, 0.0, 1.0);
vec3 col = dir.y >= 0.0
? mix(sky_horizon_color, sky_top_color, pow(up, sky_horizon_falloff))
: mix(sky_horizon_color, sky_ground_color, pow(down, 0.62));
float sun = pow(max(dot(dir, normalize(sky_sun_direction)), 0.0), 28.0);
col += sky_sun_color * sun * sky_sun_halo;
if (dir.y > 0.005) {
vec2 uv = dir.xz / (dir.y + 0.18) * sky_cloud_scale
+ vec2(TIME * sky_drift, 0.0);
float n = cloud_field(uv);
float edge = max(sky_cloud_softness, fwidth(n) * 1.35);
float shape = smoothstep(
sky_cloud_cover - edge, sky_cloud_cover + edge, n);
vec3 sun_direction = normalize(sky_sun_direction);
float facing = cloud_sun_response(uv, sun_direction);
float core = smoothstep(
sky_cloud_cover + 0.045 - edge,
sky_cloud_cover + 0.155 + edge, n);
float carving = smoothstep(
0.38, 0.64, cloud_detail(uv + vec2(2.4, 9.7)));
float lit_face = clamp(
0.12 + core * 0.25 + facing * 0.58 + carving * 0.05,
0.0, 1.0);
vec3 lit_color = mix(sky_cloud_color, sky_sun_color, 0.18);
vec3 cloud = mix(sky_cloud_shadow, lit_color, lit_face);
float boundary = 1.0 - smoothstep(
sky_cloud_cover + edge,
sky_cloud_cover + 0.105 + edge, n);
float silver_lining = boundary * facing * facing
* mix(0.10, 0.24, clamp(sun_direction.y, 0.0, 1.0));
cloud += sky_sun_color * silver_lining;
col = mix(col, cloud, shape * smoothstep(0.0, 0.22, dir.y));
}
return col;
}
// Linear view depth, with the sky pinned to a finite value so the second
// difference across a silhouette is large but never infinite.
float linear_depth(vec2 uv, float d, mat4 inv_proj) {
// Godot 4.3+ is reverse-Z: the far plane and the sky read exactly 0.
if (d <= 0.000001) {
return ink_sky_depth;
}
vec4 ndc = vec4(uv * 2.0 - 1.0, d, 1.0); vec4 ndc = vec4(uv * 2.0 - 1.0, d, 1.0);
vec4 view = inv_proj * ndc; vec4 view = inv_proj * ndc;
return min(-view.z / view.w, ink_sky_depth); return -view.z / view.w;
} }
vec3 view_position(vec2 uv, float raw_depth, mat4 inv_proj) {
vec4 ndc = vec4(uv * 2.0 - 1.0, raw_depth, 1.0);
vec4 view = inv_proj * ndc;
return view.xyz / view.w;
}
vec3 screen_normal(vec2 uv) {
return normalize(texture(normal_roughness_tex, uv).xyz * 2.0 - 1.0);
}
vec3 apply_grade(vec3 c, vec2 uv) {
if (!grade_enabled) {
return c;
}
float l = dot(c, vec3(0.2126, 0.7152, 0.0722));
// Split-tone: cool violet in the darks, warm paper white in the lights.
float k = smoothstep(0.02, 0.55, l);
c *= mix(grade_shadow_tint, grade_light_tint, k);
// A gentle overall warmth, like late afternoon light through blossom.
c += vec3(grade_warmth, grade_warmth * 0.45, 0.0) * l * 0.35;
// Keep the shadows readable — never crushed to black.
c += grade_lift * (1.0 - k);
c = mix(vec3(l), c, grade_saturation);
float r = length(uv - 0.5) * 1.42;
c *= 1.0 - grade_vignette * pow(clamp(r, 0.0, 1.0), 2.6);
return max(c, vec3(0.0));
}
void vertex() { void vertex() {
POSITION = vec4(VERTEX.xy, 1.0, 1.0); POSITION = vec4(VERTEX.xy, 1.0, 1.0);
} }
void fragment() { void fragment() {
vec2 px = 1.0 / VIEWPORT_SIZE; vec2 px = 1.0 / VIEWPORT_SIZE;
vec2 uv = SCREEN_UV; vec2 uv = SCREEN_UV;
// Background first: where the depth buffer has no geometry the screen copy float dc = linear_depth(uv, INV_PROJECTION_MATRIX);
// holds nothing but the clear colour, so those pixels are painted rather float dl = linear_depth(uv - vec2(px.x, 0.0), INV_PROJECTION_MATRIX);
// than sampled. They still go through the ink test below — an object read float dr = linear_depth(uv + vec2(px.x, 0.0), INV_PROJECTION_MATRIX);
// against the sky should be inked on the sky side of its edge too, which is float du = linear_depth(uv - vec2(0.0, px.y), INV_PROJECTION_MATRIX);
// what makes the silhouette the heaviest line in the frame. float dd = linear_depth(uv + vec2(0.0, px.y), INV_PROJECTION_MATRIX);
float raw_depth = texture(depth_tex, uv).r;
vec3 col;
if (raw_depth <= 0.000001) {
vec4 far_view = INV_PROJECTION_MATRIX * vec4(uv * 2.0 - 1.0, 0.0, 1.0);
vec3 view_dir = normalize(far_view.xyz / far_view.w);
vec3 world_dir = normalize((INV_VIEW_MATRIX * vec4(view_dir, 0.0)).xyz);
col = painted_sky(world_dir);
} else {
col = texture(screen_tex, uv).rgb;
}
vec2 t = px * ink_thickness; // Depth edge: neighbor difference scaled by distance so far geometry
float raw_l = texture(depth_tex, uv - vec2(t.x, 0.0)).r; // doesn't dissolve into noise.
float raw_r = texture(depth_tex, uv + vec2(t.x, 0.0)).r; float ddiff = abs(dl - dr) + abs(du - dd);
float raw_u = texture(depth_tex, uv + vec2(0.0, t.y)).r; float depth_edge = step(depth_threshold * (0.5 + dc * 0.08), ddiff);
float raw_d = texture(depth_tex, uv - vec2(0.0, t.y)).r;
float dc = linear_depth(uv, raw_depth, INV_PROJECTION_MATRIX);
float dl = linear_depth(uv - vec2(t.x, 0.0), raw_l, INV_PROJECTION_MATRIX);
float dr = linear_depth(uv + vec2(t.x, 0.0), raw_r, INV_PROJECTION_MATRIX);
float du = linear_depth(uv + vec2(0.0, t.y), raw_u, INV_PROJECTION_MATRIX);
float dd = linear_depth(uv - vec2(0.0, t.y), raw_d, INV_PROJECTION_MATRIX);
// Second difference of linear depth, normalised by distance so that a given // Normal edge: crease lines on same-depth corners.
// real-world crease inks with the same weight near and far. vec3 nc = texture(normal_tex, uv).xyz * 2.0 - 1.0;
float sx = (dl + dr - 2.0 * dc) / dc; vec3 nl = texture(normal_tex, uv - vec2(px.x, 0.0)).xyz * 2.0 - 1.0;
float sy = (du + dd - 2.0 * dc) / dc; vec3 nu = texture(normal_tex, uv - vec2(0.0, px.y)).xyz * 2.0 - 1.0;
float ndiff = length(nc - nl) + length(nc - nu);
float normal_edge = step(normal_threshold, ndiff) * step(ddiff, depth_threshold);
float convex = max(0.0, sx) + max(0.0, sy); float fade = 1.0 - smoothstep(max_distance * 0.6, max_distance, dc);
float concave = max(0.0, -sx) + max(0.0, -sy); float edge = max(depth_edge, normal_edge * 0.6) * edge_strength * fade;
float edge = smoothstep( vec3 scene = texture(screen_tex, uv).rgb;
ink_sensitivity * ink_hardness, ink_sensitivity, convex); ALBEDO = mix(scene, ink_color.rgb, edge);
edge = max(edge, smoothstep(
ink_concave, ink_concave * 3.4, concave) * ink_concave_amount);
// Depth precision forms a staircase on a flat floor viewed almost edge-on.
// Its second difference looks like curvature even though every tapped point
// belongs to the same plane; beyond a camera-dependent distance the old test
// therefore inked the entire road as one dark band. Confirm that curvature
// with the normal/depth prepass before drawing it. A true edge either changes
// normal, leaves the centre tangent plane, or crosses from geometry to sky.
if (edge > 0.0001) {
float sky_edge = step(raw_depth, 0.000001)
!= step(raw_l, 0.000001) || step(raw_depth, 0.000001)
!= step(raw_r, 0.000001) || step(raw_depth, 0.000001)
!= step(raw_u, 0.000001) || step(raw_depth, 0.000001)
!= step(raw_d, 0.000001) ? 1.0 : 0.0;
float geometry_evidence = sky_edge;
if (raw_depth > 0.000001 && sky_edge < 0.5) {
vec3 nc = screen_normal(uv);
vec3 nl = screen_normal(uv - vec2(t.x, 0.0));
vec3 nr = screen_normal(uv + vec2(t.x, 0.0));
vec3 nu = screen_normal(uv + vec2(0.0, t.y));
vec3 nd = screen_normal(uv - vec2(0.0, t.y));
float normal_turn = max(max(1.0 - dot(nc, nl), 1.0 - dot(nc, nr)),
max(1.0 - dot(nc, nu), 1.0 - dot(nc, nd)));
vec3 pc = view_position(uv, raw_depth, INV_PROJECTION_MATRIX);
vec3 pl = view_position(uv - vec2(t.x, 0.0), raw_l, INV_PROJECTION_MATRIX);
vec3 pr = view_position(uv + vec2(t.x, 0.0), raw_r, INV_PROJECTION_MATRIX);
vec3 pu = view_position(uv + vec2(0.0, t.y), raw_u, INV_PROJECTION_MATRIX);
vec3 pd = view_position(uv - vec2(0.0, t.y), raw_d, INV_PROJECTION_MATRIX);
float off_plane = max(max(abs(dot(nc, normalize(pl - pc))),
abs(dot(nc, normalize(pr - pc)))),
max(abs(dot(nc, normalize(pu - pc))),
abs(dot(nc, normalize(pd - pc)))));
float normal_evidence = smoothstep(0.006, 0.045, normal_turn);
float plane_evidence = smoothstep(0.012, 0.080, off_plane);
geometry_evidence = max(normal_evidence, plane_evidence);
}
edge *= geometry_evidence;
}
// Fade on the nearest real surface involved in the edge, not on dc: a sky
// pixel beside a nearby roof would otherwise be measured at 900 m and its
// half of the silhouette would fade out while the roof's half did not.
float near_d = min(dc, min(min(dl, dr), min(du, dd)));
edge *= 1.0 - smoothstep(ink_fade_start, ink_fade_end, near_d);
edge *= ink_strength;
vec3 line = mix(ink_color.rgb, col * 0.42, ink_hue_bleed);
col = mix(col, line, clamp(edge, 0.0, 1.0));
ALBEDO = apply_grade(col, uv);
} }
-258
View File
@@ -1,258 +0,0 @@
shader_type spatial;
// ── The cel law ──────────────────────────────────────────────────────────────
//
// Ported from the Sakura Crossing renderer (three.js MeshToonMaterial + a
// patched toon BRDF). Two ideas, and the second one is the one that matters:
//
// 1. Direct light is QUANTISED against a hand-authored ramp — a tiny
// nearest-filtered texture sampled at `dot(N,L) * 0.5 + 0.5`. So a surface
// is never lit by a falloff, only ever by one of 2-5 flat values.
//
// 2. The darker bands are HUE-SHIFTED toward a cool violet rather than being
// a darker version of the base colour:
//
// irradiance = band * mix(shadow_tint, vec3(1.0), band)
//
// The band drives its own tint, so full light is untinted, the mid band is
// slightly cooled and the darkest band is almost entirely shadow-hue. That
// hue shift in shadow is most of what separates "anime cel" from
// "low-poly 3D" — a wall whose shade is just 40% of its lit colour reads
// as an unlit polygon; a wall whose shade swings violet reads as painted.
//
// Godot's light() is called per light and adds into DIFFUSE_LIGHT, and its
// Lambert term already carries the 1/PI that three.js puts in BRDF_Lambert, so
// the two pipelines agree once the ramp value is substituted for `dot(N,L)`.
//
// This is a SEPARATE shader from toon.gdshader on purpose. That one carries the
// weapon-viewmodel colour shaping and the character band calibration, both
// tuned against imported textures that are already painted with cel shading.
// This one is for FLAT-COLOURED WORLD GEOMETRY, which is what the reference is
// built out of end to end, and it would be wrong to make either compromise for
// the other.
// ── Ramps ────────────────────────────────────────────────────────────────────
//
// The reference's stop tables, kept as raw 0-255 values so they can be checked
// against the source line by line. These are LINEAR values, not sRGB: three.js
// gives a DataTexture no colour space, so the stops are used as authored, and
// the same has to be true here or every band lands too bright.
//
// 2/3/4/5 the standard ramps. 3 is the default and does most of the world.
// 6 "soft" — high key, 2 bands, for pale masses (blossom, cloud,
// 7 "soft3" — high key, 3 bands plaster) that must stay light even
// on the shadow side. Without these a
// cherry canopy goes grey the moment
// it turns away from the sun, which
// is the one thing a blossom tree may
// never do.
const float RAMP_2[5] = float[](96.0, 255.0, 255.0, 255.0, 255.0);
const float RAMP_3[5] = float[](92.0, 178.0, 255.0, 255.0, 255.0);
const float RAMP_4[5] = float[](80.0, 142.0, 202.0, 255.0, 255.0);
const float RAMP_5[5] = float[](74.0, 124.0, 172.0, 214.0, 255.0);
const float RAMP_SOFT[5] = float[](180.0, 255.0, 255.0, 255.0, 255.0);
const float RAMP_SOFT3[5] = float[](172.0, 214.0, 255.0, 255.0, 255.0);
// Which ramp, and how many of its stops are live.
// 0 = 2 band 1 = 3 band (default) 2 = 4 band 3 = 5 band
// 4 = soft 2 band (high key) 5 = soft 3 band (high key)
uniform int ramp_id : hint_range(0, 5) = 1;
uniform vec4 albedo_color : source_color = vec4(1.0);
uniform sampler2D albedo_texture : source_color, filter_linear_mipmap, repeat_enable;
uniform bool has_texture = false;
uniform bool use_vertex_color = false;
// The cool violet the shadow bands swing toward. 0x6c5f8c is the reference's
// default; individual materials shift it (warmer for timber and blossom, bluer
// for metal and water) the same way the reference's `cel({tint})` does.
uniform vec4 shadow_tint : source_color = vec4(0.424, 0.373, 0.549, 1.0);
// Faceted shading. The reference sets `flatShading: true` on essentially
// everything, and it is not a shortcut — a quantised ramp over SMOOTH normals
// puts a curved band boundary across a curved surface, which reads as an
// airbrushed gradient no matter how few steps it has. Faceting forces every
// band edge onto a polygon edge, which is where a cel painter would put it.
uniform bool flat_shading = true;
// ── Cast shadow ──────────────────────────────────────────────────────────────
//
// The reference folds shadow attenuation into the light colour, so an occluded
// pixel there goes to zero direct light and is carried entirely by ambient.
// That works when the whole world is one authored scene; in a shooter, where
// players have to be read against the geometry they are standing on, a cast
// shadow that reaches black swallows them.
//
// So occlusion drives the surface to the ramp's DARKEST STOP — fully tinted,
// scaled by cast_shadow_depth — rather than to nothing. The result is still a
// hard, dark, clearly separate shape (which is what an animator inks), but it
// keeps hue, so a character standing in the shade of a shopfront stays legible.
uniform float cast_shadow_depth : hint_range(0.0, 1.0) = 0.72;
uniform float cast_shadow_softness : hint_range(0.01, 1.0) = 0.30;
// Band-edge anti-aliasing floor, in NdotL. Nearest-filtered quantisation is
// a hard step, and a hard step on a slowly-curving surface crawls and
// stair-steps as the camera moves — this is shading aliasing, so MSAA cannot
// touch it. See light() for how the screen-space term works.
uniform float band_softness : hint_range(0.0, 0.5) = 0.012;
// Rim and specular are OFF by default and should mostly stay that way: the
// reference has neither, and on large level surfaces a toon rim reads as a
// giant soft blob smeared across a wall.
uniform float rim_strength : hint_range(0.0, 2.0) = 0.0;
uniform float rim_width : hint_range(0.0, 1.0) = 0.28;
// ── Surface law ──────────────────────────────────────────────────────────────
//
// Carried over from toon.gdshader, because the problem it solves is real here
// too: a flat cel colour is the style, but a twelve-metre wall holding exactly
// one value is not stylised, it is empty. Both default to off — the reference
// answers this with geometry (every fascia, sill and downpipe is modelled), and
// where this map does the same it does not need them.
group_uniforms detail;
uniform float seam_scale = 0.0; // metres per panel; 0 = off
uniform float seam_strength : hint_range(0.0, 1.0) = 0.24;
uniform float seam_width : hint_range(0.001, 0.2) = 0.010;
uniform float grade_height = 0.0; // metres to fade over; 0 = off
uniform float grade_strength : hint_range(0.0, 1.0) = 0.18;
uniform float grade_floor = 0.0; // world Y the grade starts from
varying vec3 world_pos;
varying vec3 world_normal;
varying float normal_slope;
float ramp_stop(int i) {
// GLSL ES 3.0 will not index a const array with a non-constant expression on
// every driver, so the ramp is selected by branch and the stop by a small
// unrolled pick. Both indices are uniform-or-loop-constant, so this costs
// nothing measurable and works everywhere.
float s[5] = RAMP_3;
if (ramp_id == 0) { s = RAMP_2; }
else if (ramp_id == 2) { s = RAMP_4; }
else if (ramp_id == 3) { s = RAMP_5; }
else if (ramp_id == 4) { s = RAMP_SOFT; }
else if (ramp_id == 5) { s = RAMP_SOFT3; }
float v = s[0];
if (i == 1) { v = s[1]; }
else if (i == 2) { v = s[2]; }
else if (i == 3) { v = s[3]; }
else if (i >= 4) { v = s[4]; }
return v / 255.0;
}
int ramp_bands() {
if (ramp_id == 0 || ramp_id == 4) { return 2; }
if (ramp_id == 2) { return 4; }
if (ramp_id == 3) { return 5; }
return 3;
}
float seam_lines(vec2 uv) {
vec2 g = abs(fract(uv) - 0.5);
vec2 fw = fwidth(uv) + 0.0001;
vec2 line = smoothstep(0.5 - seam_width - fw, 0.5 - seam_width + fw, g);
return max(line.x, line.y);
}
float panel_seam(vec3 p, vec3 n) {
vec3 w = pow(abs(n), vec3(4.0));
w /= (w.x + w.y + w.z);
float s = seam_lines(p.zy / seam_scale) * w.x
+ seam_lines(p.xz / seam_scale) * w.y
+ seam_lines(p.xy / seam_scale) * w.z;
float cell_px = length(fwidth(p)) / seam_scale;
return s * (1.0 - smoothstep(0.25, 0.5, cell_px));
}
void vertex() {
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);
}
void fragment() {
vec3 base = albedo_color.rgb;
if (has_texture) {
base *= texture(albedo_texture, UV).rgb;
}
if (use_vertex_color) {
base *= COLOR.rgb;
}
if (seam_scale > 0.0) {
base *= 1.0 - panel_seam(world_pos, world_normal) * seam_strength;
}
if (grade_height > 0.0) {
float h = clamp((world_pos.y - grade_floor) / grade_height, 0.0, 1.0);
base *= mix(1.0 - grade_strength, 1.0, h);
}
if (flat_shading) {
// VERTEX is view-space position here, so its screen derivatives span the
// triangle's own plane and their cross product is the true face normal.
// Forced to face the viewer, because the winding of that cross product
// flips with the handedness of the projection and a back-to-front normal
// lights the facet from behind.
vec3 fn = normalize(cross(dFdx(VERTEX), dFdy(VERTEX)));
NORMAL = fn * sign(dot(fn, VIEW));
}
ALBEDO = base;
ROUGHNESS = 1.0;
SPECULAR = 0.0;
// Screen-space rate of turn of the normal, measured here because
// derivatives are a fragment-stage operation — taking fwidth() inside
// light(), which runs once per light, is undefined on some drivers.
// Faceted geometry has a slope of zero across a facet, so this only ever
// widens the band edge where the surface is genuinely curving.
normal_slope = length(fwidth(NORMAL));
float rim = 1.0 - clamp(dot(normalize(VIEW), NORMAL), 0.0, 1.0);
rim = smoothstep(1.0 - rim_width, 1.0, rim);
EMISSION = base * rim * rim_strength;
}
void light() {
float ndotl = dot(NORMAL, LIGHT);
// Quantise. `t` is the reference's ramp lookup coordinate exactly — the
// texture is sampled at dot(N,L) * 0.5 + 0.5 with NearestFilter, so band
// boundaries fall at evenly spaced values of that coordinate and nowhere
// else. With the 3-stop ramp that puts them at dot(N,L) = ±1/3.
int bands = ramp_bands();
float fb = clamp(ndotl * 0.5 + 0.5, 0.0, 0.999999) * float(bands);
int idx = int(floor(fb));
float frac_b = fb - float(idx);
float hi = ramp_stop(idx);
float lo = ramp_stop(max(idx - 1, 0));
// Anti-aliasing budget. band_softness is in NdotL, which says nothing about
// how many PIXELS a transition covers; normal_slope does. Widening the step
// to at least the on-screen rate of turn keeps every band edge about a pixel
// wide, and leaves edges that are already wider exactly as authored. The
// d(fb)/d(ndotl) factor of bands*0.5 converts the NdotL width into the
// band-index units `frac_b` is measured in.
float w = max(band_softness, normal_slope * 0.9) * float(bands) * 0.5;
float band = mix(lo, hi, smoothstep(0.0, max(w * 2.0, 0.0001), frac_b));
// The hue shift. Band drives its own tint: full light is untinted, the
// darkest band is very nearly pure shadow hue.
vec3 lit = band * mix(shadow_tint.rgb, vec3(1.0), band);
// Cast shadow: down to the darkest stop, fully tinted, never to black.
float floor_band = ramp_stop(0);
vec3 occluded = floor_band * mix(shadow_tint.rgb, vec3(1.0), floor_band)
* cast_shadow_depth;
float occ = smoothstep(0.0, cast_shadow_softness, ATTENUATION);
vec3 shade = mix(occluded, lit, occ);
DIFFUSE_LIGHT += ALBEDO * LIGHT_COLOR / PI * shade;
}
-1
View File
@@ -1 +0,0 @@
uid://dk5nb354iw044
+8 -212
View File
@@ -9,7 +9,6 @@ shader_type spatial;
uniform vec4 albedo_color : source_color = vec4(1.0); uniform vec4 albedo_color : source_color = vec4(1.0);
uniform sampler2D albedo_texture : source_color, filter_linear_mipmap, repeat_enable; uniform sampler2D albedo_texture : source_color, filter_linear_mipmap, repeat_enable;
uniform bool has_texture = true; uniform bool has_texture = true;
uniform bool use_vertex_color = false;
uniform bool use_triplanar = false; uniform bool use_triplanar = false;
uniform float triplanar_tile = 2.0; // world units per texture tile uniform float triplanar_tile = 2.0; // world units per texture tile
@@ -22,84 +21,15 @@ uniform float mid_band_edge : hint_range(-1.0, 1.0) = 0.55; // second, brighter
// reads as a glossy stripe sweeping across the hair. // reads as a glossy stripe sweeping across the hair.
uniform float mid_tone : hint_range(0.0, 1.0) = 0.82; uniform float mid_tone : hint_range(0.0, 1.0) = 0.82;
uniform vec4 shadow_color : source_color = vec4(0.62, 0.65, 0.78, 1.0); // cool shadow tint uniform vec4 shadow_color : source_color = vec4(0.62, 0.65, 0.78, 1.0); // cool shadow tint
// How far a CAST shadow drops below the form-shadow tint. Cel art draws the
// two separately: the terminator (a surface turning away from the sun) is a
// tone step, a cast shadow (something standing in the way) is a darker, harder
// shape. See light() for why collapsing them made benches float.
uniform float cast_shadow_depth : hint_range(0.0, 1.0) = 0.62;
// Width of the cast-shadow edge, in ATTENUATION. Small keeps it crisp like
// inked artwork; not zero, or the shadow map's own stair-steps show.
uniform float cast_shadow_softness : hint_range(0.01, 1.0) = 0.35;
// Matte-anime defaults: zero specular (any stepped glint reads as shine // Matte-anime defaults: zero specular (any stepped glint reads as shine
// sweeping across hair/cloth when the camera moves), whisper of rim. // sweeping across hair/cloth when the camera moves), whisper of rim.
uniform float rim_strength : hint_range(0.0, 2.0) = 0.05; uniform float rim_strength : hint_range(0.0, 2.0) = 0.05;
uniform float rim_width : hint_range(0.0, 1.0) = 0.28; uniform float rim_width : hint_range(0.0, 1.0) = 0.28;
uniform float specular_strength : hint_range(0.0, 1.0) = 0.0; uniform float specular_strength : hint_range(0.0, 1.0) = 0.0;
uniform float specular_shininess : hint_range(1.0, 128.0) = 64.0; uniform float specular_shininess : hint_range(1.0, 128.0) = 64.0;
// Viewmodel/readability controls. Both default to zero so world materials are
// byte-for-byte unchanged. The first keeps near-black authored metal from
// collapsing below display range; the second is a controlled painted fill,
// useful in a transparent viewmodel viewport that has no environment ambient.
uniform float albedo_floor : hint_range(0.0, 0.5) = 0.0;
uniform float ambient_fill : hint_range(0.0, 1.0) = 0.0;
// Viewmodel color shaping. Defaults preserve every world/character material;
// weapon profiles opt in so dark receiver metal, pale trim and painted parts
// do not all converge on the same middle grey under a uniform fill.
uniform float albedo_contrast : hint_range(0.5, 2.0) = 1.0;
uniform float albedo_saturation : hint_range(0.0, 2.0) = 1.0;
uniform float albedo_gain : hint_range(0.25, 1.5) = 1.0;
uniform float viewmodel_light_strength : hint_range(0.0, 2.0) = 0.0;
uniform vec3 viewmodel_key_direction = vec3(-0.42, 0.58, 0.70);
uniform vec3 viewmodel_key_color : source_color = vec3(1.0, 0.91, 0.80);
uniform vec3 viewmodel_fill_color : source_color = vec3(0.72, 0.80, 1.0);
// Viewmodel ink controls. The old deterministic light was an additive
// key/fill wash: it made every normal visible, but also lifted receiver
// recesses and polymer undersides toward the same middle grey. These controls
// make that painted light a four-tone cel ramp and add a thin curvature ink
// pass. They default off/neutral so world and character materials are
// unchanged.
uniform float viewmodel_shadow_tone : hint_range(0.0, 1.0) = 0.18;
uniform float viewmodel_mid_tone : hint_range(0.0, 1.0) = 0.52;
uniform float viewmodel_crease_strength : hint_range(0.0, 1.0) = 0.0;
uniform float viewmodel_crease_threshold : hint_range(0.001, 1.0) = 0.12;
// ── Surface law ──────────────────────────────────────────────────────────────
// Flat cel colour is the style, but a wall that is ONE value from end to end is
// not stylised, it is empty — tools/levels.py measured 0.28 mean adjacent-pixel
// difference across the probe, i.e. essentially nothing but the silhouettes.
// These give a surface its own quiet structure without touching the flat-shaded
// look: everything here is authored into ALBEDO, so the cel bands still land on
// top of it exactly as before.
//
// All default to OFF (scale 0). A material opts in through LevelMaterials'
// surface laws, so props, vehicles and foliage keep plain flat colour and only
// architecture and ground get the treatment.
group_uniforms detail;
// Panel seams: a world-space grid of thin darker lines, triplanar so it needs
// no UVs and stays the same physical size on every surface it crosses. This is
// the single biggest change to how "built" a surface reads.
uniform float seam_scale = 0.0; // metres per panel; 0 = off
uniform float seam_strength : hint_range(0.0, 1.0) = 0.30;
uniform float seam_width : hint_range(0.001, 0.2) = 0.012; // fraction of a panel
// Vertical grade: surfaces darken toward the ground. A painter would put this
// in by hand — it is ambient occlusion as artwork rather than as a screen
// effect, it costs nothing, and it works at any distance, which SSAO does not.
uniform float grade_height = 0.0; // metres to fade over; 0 = off
uniform float grade_strength : hint_range(0.0, 1.0) = 0.16;
uniform float grade_floor = 0.0; // world Y the grade starts from
// Halftone: screen-tone dots inside the shadow band. The signature manga move
// and ZZZ does use it, but sparingly — off unless a material asks.
uniform float halftone_scale = 0.0; // screen px per dot; 0 = off
uniform float halftone_strength : hint_range(0.0, 1.0) = 0.35;
varying vec3 world_pos; varying vec3 world_pos;
varying vec3 world_normal; varying vec3 world_normal;
// How fast the normal is turning across one pixel, measured in fragment() and
// read in light(). This is the band-edge anti-aliasing budget — see light().
varying float normal_slope;
// Screen-tone dot mask, measured in fragment() where FRAGCOORD lives and spent
// in light() where the shadow band is known.
varying float halftone_dot;
void vertex() { void vertex() {
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
@@ -119,33 +49,6 @@ vec3 sample_triplanar(vec3 p, vec3 n) {
return cx * w.x + cy * w.y + cz * w.z; return cx * w.x + cy * w.y + cz * w.z;
} }
// One axis-plane's worth of panel grid. Returns 1 on a seam line, 0 on the
// panel face, anti-aliased by the on-screen size of a cell so the lines neither
// shimmer at distance nor turn into fat bars up close — a seam drawn with
// step() looks fine in a screenshot and crawls badly the moment anything moves.
float seam_lines(vec2 uv) {
vec2 g = abs(fract(uv) - 0.5);
vec2 fw = fwidth(uv) + 0.0001;
vec2 line = smoothstep(0.5 - seam_width - fw, 0.5 - seam_width + fw, g);
return max(line.x, line.y);
}
// Triplanar panel seams, weighted the same way the albedo triplanar is so the
// lines agree with the texture across a corner instead of crossfading against
// it. Far enough away that a cell is sub-pixel the whole thing fades out,
// otherwise the grid turns into moiré.
float panel_seam(vec3 p, vec3 n) {
vec3 w = pow(abs(n), vec3(4.0));
w /= (w.x + w.y + w.z);
float s = seam_lines(p.zy / seam_scale) * w.x
+ seam_lines(p.xz / seam_scale) * w.y
+ seam_lines(p.xy / seam_scale) * w.z;
float cell_px = length(fwidth(p)) / seam_scale;
return s * (1.0 - smoothstep(0.25, 0.5, cell_px));
}
void fragment() { void fragment() {
vec3 base = albedo_color.rgb; vec3 base = albedo_color.rgb;
if (has_texture) { if (has_texture) {
@@ -155,137 +58,30 @@ void fragment() {
base *= texture(albedo_texture, UV).rgb; base *= texture(albedo_texture, UV).rgb;
} }
} }
if (use_vertex_color) {
base *= COLOR.rgb;
}
base *= albedo_gain;
float source_luma = dot(base, vec3(0.2126, 0.7152, 0.0722));
base = mix(vec3(source_luma), base, albedo_saturation);
// Non-clipping power curve. Imported gun greys arrive as very small LINEAR
// values (an sRGB 0.17 receiver is roughly 0.025 here); a conventional 18%
// linear contrast pivot clips all of those finishes to exactly zero. The
// reciprocal curve expands their separation without moving black or white.
base = pow(max(base, vec3(0.0)),
vec3(1.0 / max(albedo_contrast, 0.001)));
if (albedo_floor > 0.0) {
float luma = dot(base, vec3(0.2126, 0.7152, 0.0722));
base = min(base * max(1.0, albedo_floor / max(luma, 0.001)), vec3(1.0));
}
// Surface law, into the albedo so the cel bands still light it normally.
if (seam_scale > 0.0) {
base *= 1.0 - panel_seam(world_pos, world_normal) * seam_strength;
}
if (grade_height > 0.0) {
float h = clamp((world_pos.y - grade_floor) / grade_height, 0.0, 1.0);
base *= mix(1.0 - grade_strength, 1.0, h);
}
// A one-pixel technical-ink accent where the surface normal changes
// quickly. Unlike an inverted hull this follows receiver cut-lines and
// bevels without exploding on the weapon FBXs' split hard normals.
float local_normal_slope = length(fwidth(normalize(NORMAL)));
float vm_crease = smoothstep(
viewmodel_crease_threshold,
viewmodel_crease_threshold * 2.6,
local_normal_slope);
base *= 1.0 - vm_crease * viewmodel_crease_strength;
// Screen-tone dots, rotated 45° the way a real screentone sheet is. Spent
// in light(), which is where we know whether this pixel is in shadow.
halftone_dot = 0.0;
if (halftone_scale > 0.0) {
vec2 hp = FRAGCOORD.xy / halftone_scale;
vec2 r = vec2(hp.x - hp.y, hp.x + hp.y) * 0.70710678;
halftone_dot = smoothstep(0.36, 0.30, length(fract(r) - 0.5));
}
ALBEDO = base; ALBEDO = base;
ROUGHNESS = 1.0; ROUGHNESS = 1.0;
SPECULAR = 0.0; SPECULAR = 0.0;
// Screen-space rate of change of the normal, for the band-edge width in
// light(). It has to be measured here: derivatives are a fragment-stage
// operation, and light() runs once per light — taking fwidth() inside that
// loop is undefined behaviour on some drivers.
normal_slope = length(fwidth(NORMAL));
// Rim: brighten grazing angles for that inked-silhouette pop. // Rim: brighten grazing angles for that inked-silhouette pop.
float rim = 1.0 - clamp(dot(normalize(VIEW), NORMAL), 0.0, 1.0); float rim = 1.0 - clamp(dot(normalize(VIEW), NORMAL), 0.0, 1.0);
rim = smoothstep(1.0 - rim_width, 1.0, rim); rim = smoothstep(1.0 - rim_width, 1.0, rim);
// Deterministic view-space key/fill for the separate transparent viewmodel EMISSION = base * rim * rim_strength;
// viewport. Directional lights parented to its camera are renderer- and
// world-sharing-sensitive; this painted light map follows the gun normals
// directly and therefore survives every level environment.
float vm_ndotl = dot(normalize(NORMAL), normalize(viewmodel_key_direction));
float vm_leave_shadow = smoothstep(-0.24, -0.04, vm_ndotl);
float vm_enter_light = smoothstep(0.18, 0.34, vm_ndotl);
float vm_highlight = smoothstep(0.64, 0.78, vm_ndotl);
float vm_tone = mix(viewmodel_shadow_tone, viewmodel_mid_tone,
vm_leave_shadow);
vm_tone = mix(vm_tone, 0.82, vm_enter_light);
vm_tone = mix(vm_tone, 1.0, vm_highlight);
vec3 vm_light = mix(viewmodel_fill_color, viewmodel_key_color,
vm_enter_light);
EMISSION = base * (
vec3(rim * rim_strength + ambient_fill)
+ vm_light * vm_tone * viewmodel_light_strength);
} }
void light() { void light() {
float ndotl = dot(NORMAL, LIGHT); float ndotl = dot(NORMAL, LIGHT);
// Shadowing folds into the band test so shadow edges band too.
// Band-edge width, with a screen-space floor. float lit = ndotl * ATTENUATION;
// float band = smoothstep(band_edge - band_softness, band_edge + band_softness, lit);
// band_softness is measured in NdotL, which says nothing about how many float mid = smoothstep(mid_band_edge - band_softness, mid_band_edge + band_softness, lit);
// PIXELS the transition actually covers. On a slowly-curving surface it can
// span a third of a wall; on a tight curve, or simply at distance, it
// collapses below one pixel and the terminator crawls and stair-steps as
// the camera moves. MSAA cannot touch this — it is shading aliasing, not
// geometry. normal_slope is how much the normal turns across one pixel, so
// widening the step to at least that much keeps every band edge about a
// pixel wide while leaving edges that are already wider exactly as authored.
// Flat surfaces have a slope of zero and so keep their crisp break.
float w = max(band_softness, normal_slope * 0.9);
// FORM shadow — the surface turning away from the light. This is the cel
// terminator, and its numbers are what the whole look was calibrated on, so
// it still lands exactly where it did.
float band = smoothstep(band_edge - w, band_edge + w, ndotl);
float mid = smoothstep(mid_band_edge - w, mid_band_edge + w, ndotl);
// 3 tones: shadow tint -> base band (mid_tone) -> full light. // 3 tones: shadow tint -> base band (mid_tone) -> full light.
float tone = mix(mid_tone, 1.0, mid); float tone = mix(mid_tone, 1.0, mid);
vec3 shade = mix(shadow_color.rgb, vec3(tone), band); vec3 shade = mix(shadow_color.rgb, vec3(tone), band);
// CAST shadow — something is standing between this pixel and the light.
//
// These used to be one thing: NdotL was multiplied by ATTENUATION and the
// PRODUCT was banded, so a cast shadow could never be darker than the
// terminator's own tint, about 0.65 of full light. Under the old ambient
// flood (Neon Alley used to run 1.35) that came out as a dip of roughly a
// seventh — which is why a bench standing in direct sun threw nothing onto
// the pavement, and why tools/levels.py measured a 1st percentile of 119 on
// a daylit frame: nothing in it was dark, so nothing rested on anything.
//
// Cel art draws the two separately, and so does this now: the terminator
// keeps its calibrated tone step, and occlusion drops below it to
// cast_shadow_depth as a harder, deeper shape.
float occ = smoothstep(0.0, cast_shadow_softness, ATTENUATION);
shade = mix(shadow_color.rgb * cast_shadow_depth, shade, occ);
// Screen tone, in the shadow only — that is where a manga artist puts it,
// and confining it there means it reads as shading rather than as a filter
// laid over the picture. Scaled by how deep in shadow the pixel is, so the
// dots fade in across the terminator instead of switching on at it.
float in_shadow = (1.0 - band) * (1.0 - occ * 0.4);
shade *= 1.0 - halftone_dot * in_shadow * halftone_strength;
DIFFUSE_LIGHT += ALBEDO * LIGHT_COLOR / PI * shade; DIFFUSE_LIGHT += ALBEDO * LIGHT_COLOR / PI * shade;
// Stepped specular dot for glossy toon highlights. Gated by occ so a // Stepped specular dot for glossy toon highlights.
// highlight cannot sit inside a shadow.
vec3 h = normalize(VIEW + LIGHT); vec3 h = normalize(VIEW + LIGHT);
float spec = pow(clamp(dot(NORMAL, h), 0.0, 1.0), specular_shininess); float spec = pow(clamp(dot(NORMAL, h), 0.0, 1.0), specular_shininess);
spec = smoothstep(0.5 - w, 0.5 + w, spec); spec = smoothstep(0.5 - band_softness, 0.5 + band_softness, spec);
SPECULAR_LIGHT += LIGHT_COLOR * spec * specular_strength * band * occ; SPECULAR_LIGHT += LIGHT_COLOR * spec * specular_strength * band;
} }
+4 -30
View File
@@ -13,35 +13,9 @@ from these CC0 packs, no attribution required:
- Kenney Interface Sounds — https://kenney.nl/assets/interface-sounds (CC0) - Kenney Interface Sounds — https://kenney.nl/assets/interface-sounds (CC0)
- Kenney Music Jingles — https://kenney.nl/assets/music-jingles (CC0) - Kenney Music Jingles — https://kenney.nl/assets/music-jingles (CC0)
Runtime masters are built by `tools/build_audio_assets.py`. It retains the Exceptions (still procedural from `generate_sounds.py`): `wind.wav`,
recorded transient, layers a filtered body and short reflection tail, controls `wallrun.wav`, `slide.wav` — seamless noise loops that the Kenney one-shots
peaks, and constructs seamless movement beds from pinned source recordings. can't replace.
The industrial movement/UI pass uses: Menu music: `music_menu.wav` — "Menu Music" by mrpoly, CC0,
- "Wind Loop" — field recording by Jonathan Shaw (InspectorJ), loop edit by
AntumDeluge, CC-BY 3.0,
https://opengameart.org/content/wind-loop
- "Scrapes" by AntumDeluge — recorded cinder-block friction, CC0,
https://opengameart.org/content/scrapes
- "Mechanical Sounds" by Brian MacIntosh — clunks, ratchets and impacts, CC0,
https://opengameart.org/content/mechanical-sounds
- "Steam boiler sound loop" by bart — metallic generator source, CC0,
https://opengameart.org/content/steam-boiler-sound-loop
- "Mech Stomp / Step Sound" by hc/qubodup — low mechanical body, CC0,
https://opengameart.org/content/mech-stomp-step-sound
- "Mechanical Explosion" by Spring Spring — industrial debris layer, CC0,
https://opengameart.org/content/mechanical-explosion
- "Air whoosh" by pyranostudios — movement/thrust air layer, CC0,
https://opengameart.org/content/air-whoosh
- "Rocket Engine" by theMinesAreShakin — low jump/dash thruster body, CC0,
https://opengameart.org/content/rocket-engine
Every remote input is SHA-256 pinned in the builder. No continuous runtime cue
is made from generated random noise.
Current menu music: "PYNCHON" by James Gargette/cinameng, CC0,
https://opengameart.org/content/pynchon
Legacy menu music retained as `music_menu.ogg`: "Menu Music" by mrpoly, CC0,
https://opengameart.org/content/menu-music https://opengameart.org/content/menu-music
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More