feat(art): add storybook meadow and woodland villagers
This commit is contained in:
@@ -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,884–10,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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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
|
||||
Reference in New Issue
Block a user