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
+12 -10
View File
@@ -1,16 +1,18 @@
[gd_scene load_steps=9 format=3]
[gd_scene load_steps=10 format=3]
[sub_resource type="StandardMaterial3D" id="Material_deck"]
albedo_color = Color(0.61, 0.53, 0.42, 1)
roughness = 0.94
[ext_resource type="Shader" path="res://world/presentation/materials/storybook_surface.gdshader" id="story_surface"]
[sub_resource type="ShaderMaterial" id="Material_deck"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.61, 0.53, 0.42, 1)
[sub_resource type="BoxMesh" id="Mesh_deck"]
material = SubResource("Material_deck")
size = Vector3(15.2, 0.32, 3.15)
[sub_resource type="StandardMaterial3D" id="Material_stone"]
albedo_color = Color(0.48, 0.47, 0.41, 1)
roughness = 0.98
[sub_resource type="ShaderMaterial" id="Material_stone"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.48, 0.47, 0.41, 1)
[sub_resource type="BoxMesh" id="Mesh_abutment"]
material = SubResource("Material_stone")
@@ -20,9 +22,9 @@ size = Vector3(2.25, 1.45, 4.2)
material = SubResource("Material_stone")
size = Vector3(1.05, 1.65, 3.7)
[sub_resource type="StandardMaterial3D" id="Material_rail"]
albedo_color = Color(0.2, 0.115, 0.055, 1)
roughness = 0.92
[sub_resource type="ShaderMaterial" id="Material_rail"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.2, 0.115, 0.055, 1)
[sub_resource type="BoxMesh" id="Mesh_rail"]
material = SubResource("Material_rail")
+21 -19
View File
@@ -1,8 +1,10 @@
[gd_scene load_steps=19 format=3]
[gd_scene load_steps=21 format=3]
[sub_resource type="StandardMaterial3D" id="Material_rock"]
albedo_color = Color(0.25, 0.27, 0.24, 1)
roughness = 1.0
[ext_resource type="Shader" path="res://world/presentation/materials/storybook_surface.gdshader" id="story_surface"]
[sub_resource type="ShaderMaterial" id="Material_rock"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.25, 0.27, 0.24, 1)
[sub_resource type="CylinderMesh" id="Mesh_rock_plinth"]
material = SubResource("Material_rock")
@@ -11,13 +13,13 @@ bottom_radius = 10.2
height = 3.2
radial_segments = 9
[sub_resource type="StandardMaterial3D" id="Material_stone"]
albedo_color = Color(0.47, 0.47, 0.43, 1)
roughness = 0.96
[sub_resource type="ShaderMaterial" id="Material_stone"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.47, 0.47, 0.43, 1)
[sub_resource type="StandardMaterial3D" id="Material_stone_light"]
albedo_color = Color(0.57, 0.56, 0.51, 1)
roughness = 0.95
[sub_resource type="ShaderMaterial" id="Material_stone_light"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.57, 0.56, 0.51, 1)
[sub_resource type="BoxMesh" id="Mesh_keep"]
material = SubResource("Material_stone_light")
@@ -57,9 +59,9 @@ size = Vector3(1.25, 3.7, 1.7)
material = SubResource("Material_stone_light")
size = Vector3(4.6, 1.05, 1.7)
[sub_resource type="StandardMaterial3D" id="Material_gate"]
albedo_color = Color(0.18, 0.11, 0.065, 1)
roughness = 0.9
[sub_resource type="ShaderMaterial" id="Material_gate"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.18, 0.11, 0.065, 1)
[sub_resource type="BoxMesh" id="Mesh_gate"]
material = SubResource("Material_gate")
@@ -72,17 +74,17 @@ bottom_radius = 0.09
height = 4.2
radial_segments = 8
[sub_resource type="StandardMaterial3D" id="Material_banner"]
albedo_color = Color(0.58, 0.1, 0.08, 1)
roughness = 0.76
[sub_resource type="ShaderMaterial" id="Material_banner"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.58, 0.1, 0.08, 1)
[sub_resource type="BoxMesh" id="Mesh_banner"]
material = SubResource("Material_banner")
size = Vector3(0.12, 2.2, 1.35)
[sub_resource type="StandardMaterial3D" id="Material_window_slit"]
albedo_color = Color(0.12, 0.14, 0.13, 1)
roughness = 0.98
[sub_resource type="ShaderMaterial" id="Material_window_slit"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.12, 0.14, 0.13, 1)
[sub_resource type="BoxMesh" id="Mesh_window_slit"]
material = SubResource("Material_window_slit")
+27
View File
@@ -1,18 +1,45 @@
extends Node
const SHADER_INTERACTOR_LIMIT := 8
const CLEARING_LIMIT := 4
@export var grass_material: ShaderMaterial
@export_range(1, SHADER_INTERACTOR_LIMIT, 1) var max_interactors := 8
@export_range(0.02, 0.5, 0.01) var update_interval := 0.08
@export var path_clearings: Array[NodePath] = []
var _elapsed := 0.0
func _ready() -> void:
# JajceWorld first isolates its mutable materials in the parent's _ready().
call_deferred("_configure_path_clearings")
_refresh_interactors()
func _configure_path_clearings() -> void:
if grass_material == null:
return
var segments := PackedVector4Array()
segments.resize(CLEARING_LIMIT)
var widths := Vector4.ZERO
var count := 0
for path in path_clearings.slice(0, CLEARING_LIMIT):
var strip := get_node_or_null(path) as MeshInstance3D
if strip == null or not strip.mesh is BoxMesh:
continue
var box := strip.mesh as BoxMesh
var half_length := strip.global_basis.z * box.size.z * 0.5
var start := strip.global_position - half_length
var end := strip.global_position + half_length
segments[count] = Vector4(start.x, start.z, end.x, end.z)
widths[count] = strip.global_basis.x.length() * box.size.x * 0.5 + 0.12
count += 1
grass_material.set_shader_parameter("clearing_count", count)
grass_material.set_shader_parameter("clearing_segments", segments)
grass_material.set_shader_parameter("clearing_half_widths", widths)
func _process(delta: float) -> void:
_elapsed += delta
if _elapsed < update_interval:
+17 -16
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=62 format=3]
[gd_scene load_steps=68 format=3]
[ext_resource type="Terrain3DAssets" path="res://terrain/jajce/assets.tres" id="1_assets"]
[ext_resource type="PackedScene" path="res://world/resource_nodes/ResourceNode.tscn" id="2_resource"]
@@ -31,6 +31,9 @@
[ext_resource type="PackedScene" path="res://world/animals/goat/dunja_habitat.tscn" id="29_habitat"]
[ext_resource type="PackedScene" path="res://world/animals/goat/zora_shelter.tscn" id="30_zora_shelter"]
[ext_resource type="ArrayMesh" path="res://assets/storybook/meadow_grass.res" id="31_meadow"]
[ext_resource type="Shader" path="res://world/jajce/materials/storybook_sky.gdshader" id="32_sky"]
[sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_jajce"]
_shader_parameters = {
"auto_base_texture": 0,
@@ -38,8 +41,8 @@ _shader_parameters = {
"auto_slope": 0.62,
"blend_sharpness": 0.88,
"enable_macro_variation": true,
"macro_variation1": Color(0.78, 0.9, 0.42, 1),
"macro_variation2": Color(0.55, 0.75, 0.38, 1),
"macro_variation1": Color(0.84, 0.95, 0.73, 1),
"macro_variation2": Color(0.60, 0.79, 0.61, 1),
"macro_variation_slope": 0.48
}
world_background = 0
@@ -172,13 +175,9 @@ size = Vector3(0.42, 0.08, 0.3)
material = SubResource("Material_site_canvas")
size = Vector3(2.9, 0.08, 1.4)
[sub_resource type="ProceduralSkyMaterial" id="SkyMaterial_jajce"]
[sub_resource type="ShaderMaterial" id="SkyMaterial_jajce"]
resource_local_to_scene = true
sky_top_color = Color(0.28, 0.52, 0.72, 1)
sky_horizon_color = Color(0.88, 0.74, 0.52, 1)
ground_bottom_color = Color(0.18, 0.24, 0.14, 1)
ground_horizon_color = Color(0.68, 0.6, 0.44, 1)
sun_angle_max = 18.0
shader = ExtResource("32_sky")
[sub_resource type="Sky" id="Sky_jajce"]
resource_local_to_scene = true
@@ -196,17 +195,17 @@ reflected_light_source = 2
tonemap_mode = 2
glow_enabled = true
glow_normalized = true
glow_intensity = 0.5
glow_intensity = 0.18
glow_strength = 0.9
glow_bloom = 0.12
glow_bloom = 0.03
fog_enabled = true
fog_light_color = Color(0.88, 0.84, 0.7, 1)
fog_light_energy = 0.78
fog_density = 0.002
fog_height = -2.0
fog_height_density = 0.08
fog_aerial_perspective = 0.35
fog_sky_affect = 0.28
fog_aerial_perspective = 0.5
fog_sky_affect = 0.12
volumetric_fog_enabled = false
volumetric_fog_density = 0.0035
volumetric_fog_albedo = Color(0.82, 0.9, 0.82, 1)
@@ -215,9 +214,9 @@ volumetric_fog_emission_energy = 0.12
volumetric_fog_length = 96.0
volumetric_fog_detail_spread = 1.6
adjustment_enabled = true
adjustment_brightness = 1.08
adjustment_contrast = 1.04
adjustment_saturation = 1.08
adjustment_brightness = 1.0
adjustment_contrast = 1.03
adjustment_saturation = 1.04
[sub_resource type="StandardMaterial3D" id="Material_guard"]
albedo_color = Color(0.55, 0.45, 0.35, 1)
@@ -269,6 +268,7 @@ rows = 34
amount = 1156
process_fixed_fps = 24
process_material = ExtResource("25_grass_process")
mesh = ExtResource("31_meadow")
shadow_mode = 0
mesh_material_override = ExtResource("20_grass_material")
min_draw_distance = 36.0
@@ -277,6 +277,7 @@ particle_count = 10404
[node name="GrassInteractionController" type="Node" parent="TerrainRoot"]
script = ExtResource("21_grass_controller")
grass_material = ExtResource("20_grass_material")
path_clearings = Array[NodePath]([NodePath("../../VillageRoot/Path_Village_Spine"), NodePath("../../VillageRoot/Path_Pantry_Worksites"), NodePath("../../VillageRoot/Path_River_Bridge"), NodePath("../../VillageRoot/Path_Forest_Edge")])
[node name="FortressBlockout" type="Node3D" parent="."]
position = Vector3(-25, 7, 9)
+15 -13
View File
@@ -1,33 +1,35 @@
[gd_scene load_steps=15 format=3]
[gd_scene load_steps=17 format=3]
[sub_resource type="StandardMaterial3D" id="Material_foundation"]
albedo_color = Color(0.43, 0.42, 0.36, 1)
roughness = 0.98
[ext_resource type="Shader" path="res://world/presentation/materials/storybook_surface.gdshader" id="story_surface"]
[sub_resource type="ShaderMaterial" id="Material_foundation"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.43, 0.42, 0.36, 1)
[sub_resource type="BoxMesh" id="Mesh_foundation"]
material = SubResource("Material_foundation")
size = Vector3(4.35, 0.65, 5.35)
[sub_resource type="StandardMaterial3D" id="Material_walls"]
albedo_color = Color(0.78, 0.7, 0.56, 1)
roughness = 0.94
[sub_resource type="ShaderMaterial" id="Material_walls"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.78, 0.7, 0.56, 1)
[sub_resource type="BoxMesh" id="Mesh_walls"]
material = SubResource("Material_walls")
size = Vector3(4, 2.75, 5)
[sub_resource type="StandardMaterial3D" id="Material_roof"]
albedo_color = Color(0.48, 0.2, 0.12, 1)
roughness = 0.9
[sub_resource type="ShaderMaterial" id="Material_roof"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.48, 0.2, 0.12, 1)
[sub_resource type="PrismMesh" id="Mesh_roof"]
material = SubResource("Material_roof")
size = Vector3(4.65, 1.7, 5.65)
left_to_right = 0.5
[sub_resource type="StandardMaterial3D" id="Material_timber"]
albedo_color = Color(0.2, 0.105, 0.05, 1)
roughness = 0.92
[sub_resource type="ShaderMaterial" id="Material_timber"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.2, 0.105, 0.05, 1)
[sub_resource type="BoxMesh" id="Mesh_beam_vertical"]
material = SubResource("Material_timber")
+12 -10
View File
@@ -1,25 +1,27 @@
[gd_scene load_steps=7 format=3]
[gd_scene load_steps=8 format=3]
[sub_resource type="StandardMaterial3D" id="Material_body"]
albedo_color = Color(0.55, 0.38, 0.22, 1)
roughness = 0.8
[ext_resource type="Shader" path="res://world/presentation/materials/storybook_surface.gdshader" id="story_surface"]
[sub_resource type="ShaderMaterial" id="Material_body"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.55, 0.38, 0.22, 1)
[sub_resource type="BoxMesh" id="Mesh_body"]
material = SubResource("Material_body")
size = Vector3(3, 3, 4)
[sub_resource type="StandardMaterial3D" id="Material_roof"]
albedo_color = Color(0.55, 0.30, 0.18, 1)
roughness = 0.75
[sub_resource type="ShaderMaterial" id="Material_roof"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.55, 0.30, 0.18, 1)
[sub_resource type="PrismMesh" id="Mesh_roof"]
material = SubResource("Material_roof")
size = Vector3(3.4, 1.2, 4.4)
left_to_right = 0.5
[sub_resource type="StandardMaterial3D" id="Material_wheel"]
albedo_color = Color(0.20, 0.12, 0.06, 1)
roughness = 0.7
[sub_resource type="ShaderMaterial" id="Material_wheel"]
shader = ExtResource("story_surface")
shader_parameter/albedo = Color(0.20, 0.12, 0.06, 1)
[sub_resource type="CylinderMesh" id="Mesh_wheel"]
material = SubResource("Material_wheel")
+4 -4
View File
@@ -7,9 +7,9 @@ enum CanopyVariant {
}
const COLORS := {
CanopyVariant.GREEN: Color(0.18, 0.39, 0.16),
CanopyVariant.DARK_GREEN: Color(0.10, 0.28, 0.12),
CanopyVariant.YELLOW_GREEN: Color(0.34, 0.47, 0.16),
CanopyVariant.GREEN: Color(0.32, 0.51, 0.29),
CanopyVariant.DARK_GREEN: Color(0.22, 0.41, 0.30),
CanopyVariant.YELLOW_GREEN: Color(0.49, 0.60, 0.28),
}
const WIND_SHADER := preload("res://world/jajce/materials/wind.gdshader")
@@ -25,7 +25,7 @@ func _ready() -> void:
var rng := RandomNumberGenerator.new()
rng.seed = hash_seed
var canopy_variant := rng.randi() % COLORS.size() as CanopyVariant
var canopy_radius := 1.0 + rng.randf() * 0.55
var canopy_radius := 1.25 + rng.randf() * 0.6
var wind_phase := rng.randf_range(0.0, TAU)
var trunk_mesh := CylinderMesh.new()
+23 -10
View File
@@ -7,17 +7,17 @@ extends Node
const TO_RAD := PI / 180.0
const DAY_SKY_TOP := Color(0.23, 0.43, 0.62, 1)
const DAY_SKY_HORIZON := Color(0.82, 0.68, 0.5, 1)
const DAY_SKY_TOP := Color(0.27, 0.57, 0.74, 1)
const DAY_SKY_HORIZON := Color(0.79, 0.88, 0.82, 1)
const DAY_GROUND_BOTTOM := Color(0.12, 0.17, 0.12, 1)
const DAY_GROUND_HORIZON := Color(0.62, 0.55, 0.42, 1)
const DAY_AMBIENT := Color(0.91, 0.85, 0.72, 1)
const DAY_AMBIENT_ENERGY := 0.7
const DAY_FOG_LIGHT := Color(0.82, 0.78, 0.66, 1)
const DAY_FOG_LIGHT_ENERGY := 0.72
const DAY_FOG_DENSITY := 0.002
const DAY_LIGHT_COLOR := Color(1, 0.87, 0.72, 1)
const DAY_LIGHT_ENERGY := 1.45
const DAY_GROUND_HORIZON := Color(0.56, 0.69, 0.57, 1)
const DAY_AMBIENT := Color(0.72, 0.83, 0.88, 1)
const DAY_AMBIENT_ENERGY := 0.55
const DAY_FOG_LIGHT := Color(0.70, 0.83, 0.81, 1)
const DAY_FOG_LIGHT_ENERGY := 0.8
const DAY_FOG_DENSITY := 0.0015
const DAY_LIGHT_COLOR := Color(1, 0.94, 0.79, 1)
const DAY_LIGHT_ENERGY := 1.2
const NIGHT_SKY_TOP := Color(0.04, 0.04, 0.12, 1)
const NIGHT_SKY_HORIZON := Color(0.08, 0.06, 0.14, 1)
@@ -123,6 +123,19 @@ func _apply_environment(cycle: float) -> void:
mat.sky_horizon_color = _lerp_color(NIGHT_SKY_HORIZON, DAY_SKY_HORIZON, day_factor)
mat.ground_bottom_color = _lerp_color(NIGHT_GROUND_BOTTOM, DAY_GROUND_BOTTOM, day_factor)
mat.ground_horizon_color = _lerp_color(NIGHT_GROUND_HORIZON, DAY_GROUND_HORIZON, day_factor)
elif sky != null and sky.sky_material is ShaderMaterial:
var mat := sky.sky_material as ShaderMaterial
mat.set_shader_parameter("sky_top_color", NIGHT_SKY_TOP.lerp(DAY_SKY_TOP, day_factor))
mat.set_shader_parameter(
"sky_horizon_color", NIGHT_SKY_HORIZON.lerp(DAY_SKY_HORIZON, day_factor)
)
mat.set_shader_parameter(
"ground_bottom_color", NIGHT_GROUND_BOTTOM.lerp(DAY_GROUND_BOTTOM, day_factor)
)
mat.set_shader_parameter(
"ground_horizon_color", NIGHT_GROUND_HORIZON.lerp(DAY_GROUND_HORIZON, day_factor)
)
mat.set_shader_parameter("daylight", day_factor)
func _find_simulation_node() -> void:
+43 -47
View File
@@ -1,70 +1,66 @@
// Adapted for The Steward from the bundled Terrain3D particle-grass example.
// Original Blender tuft. Opaque blades avoid alpha overdraw and sorting.
shader_type spatial;
render_mode skip_vertex_transform, cull_disabled, blend_mix;
render_mode skip_vertex_transform, cull_disabled, specular_disabled;
const int MAX_INTERACTORS = 8;
uniform vec2 wind_direction = vec2(1.0, 0.7);
uniform vec3 base_color : source_color = vec3(0.16, 0.31, 0.08);
uniform vec3 tip_color : source_color = vec3(0.54, 0.68, 0.24);
uniform float wind_amount : hint_range(0.0, 1.0) = 0.34;
uniform vec3 base_color : source_color = vec3(0.23, 0.40, 0.22);
uniform vec3 tip_color : source_color = vec3(0.53, 0.67, 0.35);
uniform float wind_amount : hint_range(0.0, 1.0) = 0.24;
uniform int interactor_count : hint_range(0, 8) = 0;
uniform vec3 interactor_positions[MAX_INTERACTORS];
uniform float interaction_radius : hint_range(0.25, 4.0) = 1.8;
uniform float interaction_strength : hint_range(0.0, 2.0) = 0.75;
varying vec4 grass_data;
uniform float interaction_strength : hint_range(0.0, 2.0) = 0.55;
uniform int clearing_count = 0;
uniform vec4 clearing_segments[4];
uniform vec4 clearing_half_widths = vec4(0.0);
varying float blade_height;
varying float shade_variation;
varying float pressed_weight;
void vertex() {
grass_data[2] = VERTEX.y + 0.55;
grass_data[2] *= grass_data[2];
VERTEX.xz *= 1.0 - grass_data[2];
COLOR = mix(COLOR, vec4(1.0), smoothstep(0.9, 1.0, grass_data[2]));
COLOR *= INSTANCE_CUSTOM[3] < 0.35 ? 1.0 : mix(1.0, 0.75, smoothstep(0.35, 0.0, grass_data[2]));
grass_data.rg = INSTANCE_CUSTOM.rg;
blade_height = UV.y;
float tip_weight = blade_height * blade_height;
vec3 anchor = MODEL_MATRIX[3].xyz;
float clearing_scale = 1.0;
for (int index = 0; index < 4; index++) {
if (index >= clearing_count) { break; }
vec2 start = clearing_segments[index].xy;
vec2 line = clearing_segments[index].zw - start;
float along = clamp(dot(anchor.xz - start, line) / max(dot(line, line), 0.001), 0.0, 1.0);
float distance_to_path = length(anchor.xz - start - line * along);
clearing_scale *= smoothstep(clearing_half_widths[index], clearing_half_widths[index] + 0.20, distance_to_path);
}
VERTEX *= clearing_scale;
tip_weight *= clearing_scale;
vec3 world_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
float scale = pow(INSTANCE_CUSTOM[3] * INSTANCE_CUSTOM[3], 0.707);
float force = INSTANCE_CUSTOM[2] * grass_data[2] * scale * wind_amount;
force -= fract(force * 256.0) * force * 0.05;
force = pow(max(force, 0.0), 0.707);
float lateral_wobble = sin(TIME * 2.0 * (1.0 + grass_data.r + grass_data.g)) * 0.2 * (1.0 - INSTANCE_CUSTOM[2]);
vec2 direction = normalize(wind_direction);
world_vertex.xz -= (vec2(-direction.y, direction.x) * lateral_wobble + direction) * force;
world_vertex.y -= INSTANCE_CUSTOM[2] * force * grass_data[2];
grass_data[3] = force;
float phase = dot(anchor.xz, vec2(0.38, 0.29));
float gust = sin(TIME * 1.15 + phase) * 0.65 + sin(TIME * 0.58 + phase * 0.42) * 0.35;
world_vertex.xz += normalize(wind_direction) * gust * wind_amount * tip_weight;
shade_variation = sin(anchor.x * 0.30 + anchor.z * 0.21) * 0.5 + 0.5;
pressed_weight = 0.0;
for (int index = 0; index < MAX_INTERACTORS; index++) {
if (index >= interactor_count) {
break;
}
vec2 offset = world_vertex.xz - interactor_positions[index].xz;
if (index >= interactor_count) { break; }
vec2 offset = anchor.xz - interactor_positions[index].xz;
float distance_to_actor = length(offset);
float influence = 1.0 - smoothstep(0.25, interaction_radius, distance_to_actor);
vec2 away = offset / max(distance_to_actor, 0.08);
world_vertex.xz += away * influence * interaction_strength * grass_data[2];
world_vertex.y -= influence * 0.28 * grass_data[2];
world_vertex.xz += away * influence * interaction_strength * tip_weight;
world_vertex.y -= influence * 0.18 * tip_weight;
pressed_weight = max(pressed_weight, influence);
}
VERTEX = (VIEW_MATRIX * vec4(world_vertex, 1.0)).xyz;
NORMAL = MODELVIEW_NORMAL_MATRIX * NORMAL;
BINORMAL = MODELVIEW_NORMAL_MATRIX * BINORMAL;
TANGENT = MODELVIEW_NORMAL_MATRIX * TANGENT;
NORMAL = normalize((VIEW_MATRIX * vec4(0.0, 1.0, 0.0, 0.0)).xyz);
}
void fragment() {
vec3 color = mix(base_color, tip_color, grass_data[2] * 0.62);
color.rg *= grass_data.rg * 0.18 + 0.91;
color *= pow(COLOR.rgb, vec3(2.2));
ALBEDO = mix(color, base_color * 0.72, pressed_weight * 0.42);
float specular_roughness = clamp(max(grass_data[2], grass_data[3]), 0.0, 1.0);
ROUGHNESS = 1.0 - specular_roughness * 0.22;
SPECULAR = clamp(specular_roughness * 0.2, 0.0, 0.12);
BACKLIGHT = tip_color * 0.24;
vec3 color = mix(base_color, tip_color, smoothstep(0.05, 1.0, blade_height) * 0.78);
color *= mix(0.90, 1.09, shade_variation);
ALBEDO = mix(color, base_color, pressed_weight * 0.15);
ROUGHNESS = 1.0;
}
void light() {
float band = smoothstep(0.05, 0.16, dot(NORMAL, LIGHT));
DIFFUSE_LIGHT += mix(vec3(0.46, 0.60, 0.52), vec3(1.0, 0.98, 0.85), band) * LIGHT_COLOR * ATTENUATION / PI;
}
@@ -7,10 +7,10 @@ resource_local_to_scene = true
render_priority = 0
shader = ExtResource("1_grass")
shader_parameter/wind_direction = Vector2(1, 0.7)
shader_parameter/base_color = Color(0.16, 0.31, 0.08, 1)
shader_parameter/tip_color = Color(0.54, 0.68, 0.24, 1)
shader_parameter/wind_amount = 0.34
shader_parameter/base_color = Color(0.23, 0.40, 0.22, 1)
shader_parameter/tip_color = Color(0.53, 0.67, 0.35, 1)
shader_parameter/wind_amount = 0.24
shader_parameter/interactor_count = 0
shader_parameter/interactor_positions = PackedVector3Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
shader_parameter/interaction_radius = 1.8
shader_parameter/interaction_strength = 0.75
shader_parameter/interaction_strength = 0.55
@@ -16,13 +16,13 @@ resource_local_to_scene = true
shader = ExtResource("1_process_shader")
shader_parameter/main_noise = SubResource("NoiseTexture_grass")
shader_parameter/main_noise_scale = 0.012
shader_parameter/position_offset = Vector3(0, 0.22, 0)
shader_parameter/position_offset = Vector3(0, 0.015, 0)
shader_parameter/align_to_normal = true
shader_parameter/normal_strength = 0.22
shader_parameter/random_rotation = true
shader_parameter/random_spacing = 0.72
shader_parameter/min_scale = Vector3(0.09, 0.22, 0.09)
shader_parameter/max_scale = Vector3(0.13, 0.48, 0.13)
shader_parameter/min_scale = Vector3(0.72, 0.28, 0.72)
shader_parameter/max_scale = Vector3(1.15, 0.48, 1.15)
shader_parameter/noise_scale = 0.0041
shader_parameter/wind_speed = 0.022
shader_parameter/wind_strength = 0.6
@@ -0,0 +1,27 @@
shader_type sky;
uniform vec3 sky_top_color : source_color = vec3(0.27, 0.57, 0.74);
uniform vec3 sky_horizon_color : source_color = vec3(0.79, 0.88, 0.82);
uniform vec3 ground_bottom_color : source_color = vec3(0.12, 0.17, 0.12);
uniform vec3 ground_horizon_color : source_color = vec3(0.56, 0.69, 0.57);
uniform float daylight : hint_range(0.0, 1.0) = 1.0;
float hash21(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
return mix(mix(hash21(i), hash21(i + vec2(1.0, 0.0)), f.x), mix(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(1.0)), f.x), f.y);
}
void sky() {
float height = max(EYEDIR.y, 0.0);
vec3 sky_color = mix(sky_horizon_color, sky_top_color, pow(height, 0.48));
vec2 p = EYEDIR.xz / max(0.17, height) * 1.15;
float cloud = noise(p) * 0.65 + noise(p * 2.1 + 7.3) * 0.25 + noise(p * 4.3) * 0.10;
float mask = smoothstep(0.55, 0.64, cloud) * smoothstep(0.02, 0.20, height);
vec3 cloud_shadow = mix(vec3(0.08, 0.10, 0.19), vec3(0.70, 0.80, 0.80), daylight);
vec3 cloud_light = mix(vec3(0.15, 0.18, 0.29), vec3(1.0, 0.98, 0.87), daylight);
vec3 cloud_color = mix(cloud_shadow, cloud_light, smoothstep(0.61, 0.77, cloud));
sky_color = mix(sky_color, cloud_color, mask);
COLOR = EYEDIR.y >= 0.0 ? sky_color : mix(ground_horizon_color, ground_bottom_color, pow(-EYEDIR.y, 0.4));
}
@@ -0,0 +1 @@
uid://c10spbmnnwwto
+14 -1
View File
@@ -10,8 +10,10 @@ uniform float flutter_strength : hint_range(0.0, 0.05) = 0.012;
uniform float wind_phase : hint_range(0.0, 6.2832) = 0.0;
uniform vec3 albedo : source_color = vec3(0.19, 0.38, 0.17);
uniform float roughness : hint_range(0.0, 1.0) = 0.9;
varying vec3 paint_position;
void vertex() {
paint_position = VERTEX;
float half_height = 1.2;
float height_factor = clamp((VERTEX.y + half_height) / (half_height * 2.0), 0.0, 1.0);
height_factor = height_factor * height_factor;
@@ -29,6 +31,17 @@ void vertex() {
}
void fragment() {
ALBEDO = albedo;
float patches = sin(paint_position.x * 6.0 + sin(paint_position.z * 7.0)) * sin(paint_position.y * 5.0);
float top = smoothstep(-0.65, 0.8, paint_position.y);
ALBEDO = albedo * mix(0.85, 1.17, top) * (1.0 + smoothstep(0.2, 0.4, patches) * 0.10);
ROUGHNESS = roughness;
SPECULAR = 0.0;
}
void light() {
float band = smoothstep(0.08, 0.18, dot(NORMAL, LIGHT));
float highlight = smoothstep(0.62, 0.74, dot(NORMAL, LIGHT));
vec3 tone = mix(vec3(0.32, 0.48, 0.39), vec3(0.87, 0.92, 0.72), band);
tone = mix(tone, vec3(1.12, 1.08, 0.82), highlight);
DIFFUSE_LIGHT += tone * ATTENUATION * LIGHT_COLOR / PI;
}
+38
View File
@@ -0,0 +1,38 @@
class_name AnimalAppearance
extends RefCounted
## Disposable woodland identities, rebuilt from stable NPC IDs. No simulation RNG.
const CEL_SHADER := preload("res://world/presentation/materials/storybook_cel.gdshader")
const SPECIES: Array[StringName] = [&"fox", &"rabbit", &"badger", &"otter"]
const MODELS: Array[PackedScene] = [
preload("res://assets/storybook/fox.glb"),
preload("res://assets/storybook/rabbit.glb"),
preload("res://assets/storybook/badger.glb"),
preload("res://assets/storybook/otter.glb"),
]
const PARTS: Array[StringName] = [
&"Body", &"Head", &"Hair", &"ArmLeft", &"ArmRight", &"LegLeft", &"LegRight", &"Tail"
]
static func apply_to(root: Node3D, identity: int, npc_body := false) -> void:
var index := posmod(identity, MODELS.size())
var source := MODELS[index].instantiate()
var material := ShaderMaterial.new()
material.shader = CEL_SHADER
root.set_meta("animal_species", SPECIES[index])
for part in PARTS:
var source_mesh := source.find_child(String(part), true, false) as MeshInstance3D
var target_name := "MeshInstance3D" if npc_body and part == &"Body" else String(part)
var target := root.get_node(target_name) as MeshInstance3D
assert(source_mesh != null, "Missing Blender animation part: " + String(part))
target.mesh = source_mesh.mesh
target.material_override = material
source.free()
static func set_profession_color(root: Node3D, color: Color) -> void:
var body := root.get_node("MeshInstance3D") as MeshInstance3D
var material := body.material_override as ShaderMaterial
material.set_shader_parameter("coat_color", color)
material.set_shader_parameter("profession_tint", 0.28)
@@ -0,0 +1 @@
uid://dfb2a5yut45av
@@ -0,0 +1,23 @@
shader_type spatial;
render_mode specular_disabled;
// Opaque vertex-painted models: alpha is a clothing mask, never transparency.
uniform vec4 coat_color : source_color = vec4(1.0);
uniform float profession_tint : hint_range(0.0, 1.0) = 0.0;
uniform vec3 shadow_color : source_color = vec3(0.55, 0.64, 0.70);
uniform float band_softness : hint_range(0.005, 0.2) = 0.045;
void fragment() {
ALBEDO = mix(COLOR.rgb, COLOR.rgb * coat_color.rgb * 1.6, COLOR.a * profession_tint);
ROUGHNESS = 1.0;
SPECULAR = 0.0;
}
void light() {
float diffuse = dot(NORMAL, LIGHT);
float mid = smoothstep(-0.15 - band_softness, -0.15 + band_softness, diffuse);
float sun = smoothstep(0.50 - band_softness, 0.50 + band_softness, diffuse);
vec3 tone = mix(shadow_color * 0.36, vec3(0.70), mid);
tone = mix(tone, vec3(1.0, 0.97, 0.87), sun);
DIFFUSE_LIGHT += tone * ATTENUATION * LIGHT_COLOR / PI;
}
@@ -0,0 +1 @@
uid://bs1ip7dyu7jy8
@@ -0,0 +1,24 @@
shader_type spatial;
render_mode specular_disabled;
uniform vec3 albedo : source_color = vec3(0.8, 0.72, 0.52);
uniform float paint_strength : hint_range(0.0, 0.2) = 0.04;
varying vec3 paint_position;
void vertex() {
paint_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
void fragment() {
float brush = sin(paint_position.x * 2.1 + sin(paint_position.y * 3.0)) * sin(paint_position.z * 2.8 + paint_position.y);
ALBEDO = albedo * (1.0 + brush * paint_strength);
ROUGHNESS = 1.0;
}
void light() {
float ndl = dot(NORMAL, LIGHT);
float middle = smoothstep(-0.10, 0.03, ndl);
float sunny = smoothstep(0.48, 0.58, ndl);
vec3 shade = mix(vec3(0.26, 0.34, 0.40), vec3(0.74), middle);
shade = mix(shade, vec3(1.0, 0.97, 0.88), sunny);
DIFFUSE_LIGHT += shade * ATTENUATION * LIGHT_COLOR / PI;
}
@@ -0,0 +1 @@
uid://c8biomtfwtf4w