feat(art): add storybook meadow and woodland villagers

This commit is contained in:
Rijad Zuzo
2026-09-05 18:56:44 +02:00
parent 6aae41d36f
commit da3bed67a0
60 changed files with 1222 additions and 373 deletions
+83
View File
@@ -0,0 +1,83 @@
# Woodland storybook art
Original Blender models for The Steward's warm, painted animation direction.
The runtime cast is a fox traveler, rabbit gardener, bespectacled badger and
otter in oversized coats, scarves, boots and satchels. These are original
characters, not models of existing film characters.
`art/storybook/` contains the editable Blender 5.1 sources. Its `.gdignore`
keeps Blender out of Godot's runtime import dependency chain. This directory
contains portable GLBs and the extracted grass mesh consumed by Terrain3D.
## Rebuild
From the project root, with Blender and Godot 4.7 installed:
```sh
"$BLENDER_BIN" --background --python tools/art/build_storybook_assets.py
"$GODOT_BIN" --headless --editor --path "$PWD" --import
"$GODOT_BIN" --headless --path "$PWD" --script tools/import_storybook_grass.gd
```
On macOS the binaries used for this pass were
`/Applications/Blender.app/Contents/MacOS/Blender` and
`/Applications/Godot.app/Contents/MacOS/Godot`. Headless CI should use the
isolated profile configured by `tools/quality.sh`.
## Mesh and material contract
- The seven-blade meadow tuft has **35 triangles**, one surface, rooted Y=0,
and UV.y=0 at each root / 1 at each tip. Wind and actor bending run on the
GPU. The material is opaque and double-sided, with no alpha cards, texture
lookups, per-blade nodes, shadow casting, or outline pass.
- Terrain3D owns the existing nine camera-local emitter cells: 10,404 tuft
slots on High, 5,184 on Balanced, hidden and stopped on Low. Terrain masks
and distance fading further reduce visible grass. Four scene-derived path
clearings collapse blades around the walking strips. Changing density does
not alter the terrain, navigation, resource state or collision.
- Each animal has eight mesh surfaces, **7,88410,050 triangles** total, and
vertex colors. `mesh_budget.json` is emitted by the Blender generator.
Vertex alpha is a clothing tint mask; the cel shader never writes ALPHA.
- `AnimalAppearance` derives NPC species from stable ID modulo four; the
player is a fox. It shares imported meshes, creates an isolated material
per actor and tints clothing from the existing profession definition.
`Body`, `Head`, `Hair`, four limbs and `Tail` preserve the visual controllers'
animation slots. Shoulder/hip pivots support the existing velocity-driven
gait. Ears, face details, boots and scarves are joined into those surfaces.
- Inventory sacks, carried logs, profession tools, task glyphs, trust reactions,
death and player sword cues remain controlled by their existing state paths.
Animal species here is appearance only: no animal simulation classification,
RNG calls, saved fields, migration or gameplay mechanics are introduced.
## Review captures
```sh
"$GODOT_BIN" --path "$PWD" --script tools/capture_storybook.gd -- --cast
"$GODOT_BIN" --path "$PWD" --script tools/capture_storybook.gd
```
These require a real renderer. The first writes `storybook_cast.png`; the
second captures the running main scene at a controlled daytime presentation,
then writes gameplay/valley PNGs and a small profile sample to `docs/baselines/`.
It pauses simulation only in the capture process and does not save anything.
The gameplay camera defaults remain unchanged. The matched environment camera
can also be captured with `tools/capture_jajce_lookdev.gd -- --hide-debug-labels
--output=res://docs/baselines/storybook_after.png`.
Profile samples are local wall-frame latency at a static camera with six NPC
visuals, including vsync. They are not isolated GPU timings or weak-PC proof.
The full macOS quality gate passed on Godot 4.7, including all scenarios,
Compatibility profile checks and 180 GUT tests / 2,258 assertions. Native
Metal captures were visually reviewed. The character shader also rendered on
Compatibility, whose color/tonemapping differs substantially from Metal; the
approved color reference is `storybook_cast.png` (Metal).
Native Metal shutdown reports one `ParticlesShaderRD` / material Shader RID
retained at exit. The identical diagnostic was reproduced by importing and
running untouched commit `6aae41d` in a separate temporary directory. The
existing headless gate allowlist was not changed. Import/startup diagnostics
and the baseline comparison are in ignored `logs/quality/storybook-*.log`.
Shader interfaces follow the official [Godot 4.7 spatial shader contract](https://docs.godotengine.org/en/4.7/tutorials/shaders/shader_reference/spatial_shader.html).
The exporter uses Blender's [glTF export API](https://docs.blender.org/api/main/bpy.ops.export_scene.html).
Binary file not shown.
+45
View File
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://buehm0sth6etc"
path="res://.godot/imported/badger.glb-4a20c61fae6d6e8d68c151cba8d056d5.scn"
[deps]
source_file="res://assets/storybook/badger.glb"
dest_files=["res://.godot/imported/badger.glb-4a20c61fae6d6e8d68c151cba8d056d5.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
Binary file not shown.
+45
View File
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cbne2ol0h0uku"
path="res://.godot/imported/fox.glb-972d78ae35ada569a710c17f00c10a3e.scn"
[deps]
source_file="res://assets/storybook/fox.glb"
dest_files=["res://.godot/imported/fox.glb-972d78ae35ada569a710c17f00c10a3e.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
Binary file not shown.
+45
View File
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bd6mrof50g4wn"
path="res://.godot/imported/meadow_grass.glb-0321cd60906212db637f52f1f1493ea1.scn"
[deps]
source_file="res://assets/storybook/meadow_grass.glb"
dest_files=["res://.godot/imported/meadow_grass.glb-0321cd60906212db637f52f1f1493ea1.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
Binary file not shown.
+27
View File
@@ -0,0 +1,27 @@
{
"fox": {
"triangles": 7884,
"surfaces": 8,
"bytes": 271636
},
"rabbit": {
"triangles": 9060,
"surfaces": 8,
"bytes": 314636
},
"badger": {
"triangles": 10050,
"surfaces": 8,
"bytes": 345484
},
"otter": {
"triangles": 9088,
"surfaces": 8,
"bytes": 314108
},
"meadow_grass": {
"triangles": 35,
"surfaces": 1,
"bytes": 3648
}
}
Binary file not shown.
+45
View File
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://6f01q2ru2sq4"
path="res://.godot/imported/otter.glb-92d54495ed52927d77a0a5213ac92330.scn"
[deps]
source_file="res://assets/storybook/otter.glb"
dest_files=["res://.godot/imported/otter.glb-92d54495ed52927d77a0a5213ac92330.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1
Binary file not shown.
+45
View File
@@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bywpllx00b6aq"
path="res://.godot/imported/rabbit.glb-ed7bf640394b68863b9dc6437e0f1808.scn"
[deps]
source_file="res://assets/storybook/rabbit.glb"
dest_files=["res://.godot/imported/rabbit.glb-ed7bf640394b68863b9dc6437e0f1808.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1