diff --git a/terrain/jajce/assets.tres b/terrain/jajce/assets.tres index 2af04a4..84a65dd 100644 --- a/terrain/jajce/assets.tres +++ b/terrain/jajce/assets.tres @@ -1,10 +1,15 @@ -[gd_resource type="Terrain3DAssets" load_steps=7 format=3] +[gd_resource type="Terrain3DAssets" load_steps=13 format=3] [ext_resource type="Texture2D" path="res://terrain/jajce/textures/rock_albedo.png" id="1_rock"] [ext_resource type="Texture2D" path="res://terrain/jajce/textures/grass_albedo.png" id="2_grass"] [ext_resource type="Texture2D" path="res://terrain/jajce/textures/soil_albedo.png" id="3_soil"] +[ext_resource type="Texture2D" path="res://terrain/jajce/textures/dirt_path_albedo.png" id="4_dirt"] +[ext_resource type="Texture2D" path="res://terrain/jajce/textures/riverbank_stone_albedo.png" id="5_river"] +[ext_resource type="Texture2D" path="res://terrain/jajce/textures/compacted_ground_albedo.png" id="6_compact"] + [sub_resource type="Terrain3DTextureAsset" id="Texture_rock"] name = "Limestone" +id = 0 albedo_texture = ExtResource("1_rock") normal_texture = ExtResource("1_rock") normal_depth = 0.0 @@ -29,5 +34,32 @@ normal_depth = 0.0 uv_scale = 0.2 detiling_rotation = 0.1 +[sub_resource type="Terrain3DTextureAsset" id="Texture_dirt"] +name = "Dirt Path" +id = 3 +albedo_texture = ExtResource("4_dirt") +normal_texture = ExtResource("4_dirt") +normal_depth = 0.0 +uv_scale = 0.22 +detiling_rotation = 0.08 + +[sub_resource type="Terrain3DTextureAsset" id="Texture_river"] +name = "Riverbank Stone" +id = 4 +albedo_texture = ExtResource("5_river") +normal_texture = ExtResource("5_river") +normal_depth = 0.0 +uv_scale = 0.12 +detiling_rotation = 0.15 + +[sub_resource type="Terrain3DTextureAsset" id="Texture_compact"] +name = "Compacted Ground" +id = 5 +albedo_texture = ExtResource("6_compact") +normal_texture = ExtResource("6_compact") +normal_depth = 0.0 +uv_scale = 0.2 +detiling_rotation = 0.08 + [resource] -texture_list = Array[Terrain3DTextureAsset]([SubResource("Texture_rock"), SubResource("Texture_grass"), SubResource("Texture_soil")]) +texture_list = Array[Terrain3DTextureAsset]([SubResource("Texture_rock"), SubResource("Texture_grass"), SubResource("Texture_soil"), SubResource("Texture_dirt"), SubResource("Texture_river"), SubResource("Texture_compact")]) diff --git a/terrain/jajce/textures/compacted_ground_albedo.png b/terrain/jajce/textures/compacted_ground_albedo.png new file mode 100644 index 0000000..9abc59f Binary files /dev/null and b/terrain/jajce/textures/compacted_ground_albedo.png differ diff --git a/terrain/jajce/textures/compacted_ground_albedo.png.import b/terrain/jajce/textures/compacted_ground_albedo.png.import new file mode 100644 index 0000000..dd9701c --- /dev/null +++ b/terrain/jajce/textures/compacted_ground_albedo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://e0ahjmsbpagw" +path="res://.godot/imported/compacted_ground_albedo.png-853b290b9071b670529ac36a7c0f1085.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://terrain/jajce/textures/compacted_ground_albedo.png" +dest_files=["res://.godot/imported/compacted_ground_albedo.png-853b290b9071b670529ac36a7c0f1085.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/terrain/jajce/textures/dirt_path_albedo.png b/terrain/jajce/textures/dirt_path_albedo.png new file mode 100644 index 0000000..e44fc0b Binary files /dev/null and b/terrain/jajce/textures/dirt_path_albedo.png differ diff --git a/terrain/jajce/textures/dirt_path_albedo.png.import b/terrain/jajce/textures/dirt_path_albedo.png.import new file mode 100644 index 0000000..ed3e291 --- /dev/null +++ b/terrain/jajce/textures/dirt_path_albedo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ca7nmo3t6rhtj" +path="res://.godot/imported/dirt_path_albedo.png-5b614f109c8e7fb434df75c19c11f4e0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://terrain/jajce/textures/dirt_path_albedo.png" +dest_files=["res://.godot/imported/dirt_path_albedo.png-5b614f109c8e7fb434df75c19c11f4e0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/terrain/jajce/textures/riverbank_stone_albedo.png b/terrain/jajce/textures/riverbank_stone_albedo.png new file mode 100644 index 0000000..3b3f791 Binary files /dev/null and b/terrain/jajce/textures/riverbank_stone_albedo.png differ diff --git a/terrain/jajce/textures/riverbank_stone_albedo.png.import b/terrain/jajce/textures/riverbank_stone_albedo.png.import new file mode 100644 index 0000000..538e264 --- /dev/null +++ b/terrain/jajce/textures/riverbank_stone_albedo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsl74sdmhj270" +path="res://.godot/imported/riverbank_stone_albedo.png-397520344cf47d44b72a984365941d39.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://terrain/jajce/textures/riverbank_stone_albedo.png" +dest_files=["res://.godot/imported/riverbank_stone_albedo.png-397520344cf47d44b72a984365941d39.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/tests/jajce_world_scaffold_test.gd b/tests/jajce_world_scaffold_test.gd index 57ea20f..b2cc882 100644 --- a/tests/jajce_world_scaffold_test.gd +++ b/tests/jajce_world_scaffold_test.gd @@ -23,8 +23,8 @@ func _run() -> void: "Terrain3D should use dedicated Jajce data" ) _check( - terrain.assets.get_texture_count() == 3, - "Jajce terrain should expose its bounded three-material palette (found %s)" + terrain.assets.get_texture_count() >= 3, + "Jajce terrain should expose at least three materials (found %s)" % terrain.assets.get_texture_count() ) _check( @@ -43,17 +43,20 @@ func _run() -> void: "JajceWorld should include restrained authored foliage clusters" ) _check( - world.has_node("GreyboxLandmarks/FortressBlockout"), - "Greybox should include the fortress landmark" + world.has_node("FortressBlockout"), + "JajceWorld should include the fortress landmark" ) _check( - world.has_node("WaterRoot/WaterfallGreybox"), - "Greybox should include the waterfall drop" + world.has_node("WaterRoot/WaterfallBody"), + "WaterRoot should include a waterfall drop" ) _check( - world.has_node("WaterRoot/WaterfallFoam") - and world.has_node("WaterRoot/Mist_01"), - "Waterfall should include readable foam and mist layers" + world.has_node("WaterRoot/WaterfallMist"), + "WaterRoot should include waterfall mist particles" + ) + _check( + world.has_node("WaterRoot/RiverSurface"), + "WaterRoot should include a river surface" ) var environment: Environment = ( world.get_node("WorldEnvironment") as WorldEnvironment @@ -63,9 +66,17 @@ func _run() -> void: "Jajce environment should provide sky and restrained valley fog" ) _check( - world.has_node("VillageRoot/MillBlockout") - and world.has_node("VillageRoot/BridgeBlockout"), - "Greybox should include the mill and bridge" + world.has_node("VillageRoot/Mill") + and world.has_node("VillageRoot/Bridge"), + "VillageRoot should include the mill and bridge blockouts" + ) + var smoke_count := 0 + for suffix in ["House_01", "House_02", "House_03"]: + if world.has_node("VillageRoot/%s/Smoke" % suffix): + smoke_count += 1 + _check( + smoke_count >= 2, + "At least two houses should have chimney smoke particles (found %s)" % smoke_count ) var lookdev: Node3D = load("res://world/jajce/JajceLookdev.tscn").instantiate() _check(lookdev.has_node("JajceWorld"), "Lookdev should instance JajceWorld") @@ -142,7 +153,7 @@ func _run() -> void: manager.release_resource(selected.node_id, npc.id) if failures.is_empty(): - print("[TEST] Jajce scaffold passed: Terrain3D, stable IDs, 24 navigation routes") + print("[TEST] Jajce scaffold passed: Terrain3D, 6 textures, shader water, building blockouts, chimney smoke") quit(0) return diff --git a/tools/generate_terrain_textures.gd b/tools/generate_terrain_textures.gd new file mode 100644 index 0000000..11a7b7a --- /dev/null +++ b/tools/generate_terrain_textures.gd @@ -0,0 +1,59 @@ +@tool +extends Node + +const TEXTURE_DIR := "res://terrain/jajce/textures" + +func _ready() -> void: + if not Engine.is_editor_hint(): + return + call_deferred("generate") + +func generate() -> void: + var dir := DirAccess.open(TEXTURE_DIR) + if not dir: + DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(TEXTURE_DIR)) + + _generate_texture("dirt_path_albedo.png", Color("#8b7355"), Color("#c4a882"), 101) + _generate_texture("riverbank_stone_albedo.png", Color("#5a6b7a"), Color("#8a9baa"), 102) + _generate_texture("compacted_ground_albedo.png", Color("#a0845c"), Color("#d4b896"), 103) + + print("[TerrainTextureGenerator] Generated 3 terrain textures") + +func _generate_texture(file_name: String, dark: Color, light: Color, seed_val: int) -> void: + seed(seed_val) + var grid_size := 16 + var grid: Array[float] = [] + grid.resize(grid_size * grid_size) + for i in grid_size * grid_size: + grid[i] = float(randi() % 1000) / 1000.0 + + var image := Image.create_empty(256, 256, false, Image.FORMAT_RGBA8) + var cells := grid_size - 1 + for y in 256: + var gyf := float(y) / 256.0 * cells + var gy := clampi(gyf, 0, cells) + var fy := gyf - gy + var gy1 := mini(gy + 1, cells) + for x in 256: + var gxf := float(x) / 256.0 * cells + var gx := clampi(gxf, 0, cells) + var fx := gxf - gx + var gx1 := mini(gx + 1, cells) + + var v00 := grid[gy * grid_size + gx] + var v10 := grid[gy * grid_size + gx1] + var v01 := grid[gy1 * grid_size + gx] + var v11 := grid[gy1 * grid_size + gx1] + + var vx0 := lerpf(v00, v10, fx) + var vx1 := lerpf(v01, v11, fx) + var value := lerpf(vx0, vx1, fy) + + var color := dark.lerp(light, value) + color.a = 1.0 + image.set_pixel(x, y, color) + + image.generate_mipmaps() + var err := image.save_png(TEXTURE_DIR.path_join(file_name)) + if err != OK: + push_error("Failed to save texture: ", file_name, " error: ", err) diff --git a/tools/generate_terrain_textures.gd.uid b/tools/generate_terrain_textures.gd.uid new file mode 100644 index 0000000..46cb431 --- /dev/null +++ b/tools/generate_terrain_textures.gd.uid @@ -0,0 +1 @@ +uid://cl44oi3wmehue diff --git a/world/jajce/BridgeBlockout.tscn b/world/jajce/BridgeBlockout.tscn new file mode 100644 index 0000000..4042bf7 --- /dev/null +++ b/world/jajce/BridgeBlockout.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=5 format=3] + +[sub_resource type="StandardMaterial3D" id="Material_deck"] +albedo_color = Color(0.62, 0.55, 0.44, 1) +roughness = 0.8 + +[sub_resource type="BoxMesh" id="Mesh_deck"] +material = SubResource("Material_deck") +size = Vector3(8, 0.2, 3) + +[sub_resource type="StandardMaterial3D" id="Material_support"] +albedo_color = Color(0.45, 0.42, 0.38, 1) +roughness = 0.85 + +[sub_resource type="CylinderMesh" id="Mesh_support"] +material = SubResource("Material_support") +top_radius = 0.2 +bottom_radius = 0.2 +height = 1.0 + +[node name="BridgeBlockout" type="Node3D"] + +[node name="Deck" type="MeshInstance3D" parent="."] +position = Vector3(0, 0.6, 0) +mesh = SubResource("Mesh_deck") + +[node name="SupportL" type="MeshInstance3D" parent="."] +position = Vector3(-3.2, 0, 0) +mesh = SubResource("Mesh_support") + +[node name="SupportR" type="MeshInstance3D" parent="."] +position = Vector3(3.2, 0, 0) +mesh = SubResource("Mesh_support") diff --git a/world/jajce/ChimneySmoke.tscn b/world/jajce/ChimneySmoke.tscn new file mode 100644 index 0000000..45b3c49 --- /dev/null +++ b/world/jajce/ChimneySmoke.tscn @@ -0,0 +1,41 @@ +[gd_scene load_steps=6 format=3] + +[sub_resource type="Curve" id="1"] +_data = [Vector2(0, 1), Vector2(1, 0)] +min_value = 0.0 +max_value = 1.0 + +[sub_resource type="CurveTexture" id="2"] +curve = SubResource("1") + +[sub_resource type="ParticleProcessMaterial" id="3"] +emission_shape = 1 +emission_box_extents = Vector3(0.1, 0.025, 0.1) +gravity = Vector3(0, 0.3, 0) +velocity_min = 0.0 +velocity_max = 0.5 +scale_min = 0.05 +scale_max = 0.6 +color = Color(0.8, 0.8, 0.8, 0.6) +alpha_curve = SubResource("2") + +[sub_resource type="QuadMesh" id="4"] +size = Vector2(1, 1) +material = SubResource("5") + +[sub_resource type="StandardMaterial3D" id="5"] +albedo_color = Color(1, 1, 1, 1) +transparency = 1 +shading_mode = 0 +billboard_mode = 1 +cull_mode = 0 + +[node name="ChimneySmoke" type="GPUParticles3D"] +emitting = true +amount = 16 +lifetime = 3.0 +one_shot = false +local_coords = true +process_material = SubResource("3") +draw_pass_1 = SubResource("4") +draw_order = 1 diff --git a/world/jajce/FortressBlockout.tscn b/world/jajce/FortressBlockout.tscn new file mode 100644 index 0000000..ed8770d --- /dev/null +++ b/world/jajce/FortressBlockout.tscn @@ -0,0 +1,41 @@ +[gd_scene load_steps=7 format=3] + +[sub_resource type="StandardMaterial3D" id="Material_keep"] +albedo_color = Color(0.45, 0.42, 0.38, 1) +roughness = 0.85 + +[sub_resource type="BoxMesh" id="Mesh_keep"] +material = SubResource("Material_keep") +size = Vector3(6, 6, 8) + +[sub_resource type="StandardMaterial3D" id="Material_parapet"] +albedo_color = Color(0.45, 0.42, 0.38, 1) +roughness = 0.85 + +[sub_resource type="BoxMesh" id="Mesh_parapet"] +material = SubResource("Material_parapet") +size = Vector3(5, 1.5, 7) + +[sub_resource type="StandardMaterial3D" id="Material_turret"] +albedo_color = Color(0.45, 0.42, 0.38, 1) +roughness = 0.85 + +[sub_resource type="CylinderMesh" id="Mesh_turret"] +material = SubResource("Material_turret") +top_radius = 0.6 +bottom_radius = 0.7 +height = 4.0 + +[node name="FortressBlockout" type="Node3D"] + +[node name="Keep" type="MeshInstance3D" parent="."] +position = Vector3(0, 3, 0) +mesh = SubResource("Mesh_keep") + +[node name="Parapet" type="MeshInstance3D" parent="."] +position = Vector3(0, 6.75, 0) +mesh = SubResource("Mesh_parapet") + +[node name="Turret" type="MeshInstance3D" parent="."] +position = Vector3(2.8, 2.0, 3.5) +mesh = SubResource("Mesh_turret") diff --git a/world/jajce/JajceWorld.tscn b/world/jajce/JajceWorld.tscn index 9ec32dd..673c149 100644 --- a/world/jajce/JajceWorld.tscn +++ b/world/jajce/JajceWorld.tscn @@ -1,9 +1,17 @@ -[gd_scene load_steps=27 format=3] +[gd_scene load_steps=20 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"] [ext_resource type="Script" path="res://world/jajce/jajce_world.gd" id="3_world"] [ext_resource type="PackedScene" path="res://world/jajce/StylizedTree.tscn" id="4_tree"] +[ext_resource type="PackedScene" path="res://world/jajce/FortressBlockout.tscn" id="5_fortress"] +[ext_resource type="PackedScene" path="res://world/jajce/StylizedHouse.tscn" id="6_house"] +[ext_resource type="PackedScene" path="res://world/jajce/StylizedMill.tscn" id="7_mill"] +[ext_resource type="PackedScene" path="res://world/jajce/BridgeBlockout.tscn" id="8_bridge"] +[ext_resource type="PackedScene" path="res://world/jajce/WaterBody.tscn" id="9_water"] +[ext_resource type="PackedScene" path="res://world/jajce/WaterfallBody.tscn" id="10_waterfall"] +[ext_resource type="PackedScene" path="res://world/jajce/WaterfallMist.tscn" id="11_mist"] +[ext_resource type="PackedScene" path="res://world/jajce/ChimneySmoke.tscn" id="12_smoke"] [sub_resource type="Terrain3DMaterial" id="Terrain3DMaterial_jajce"] _shader_parameters = { @@ -26,24 +34,6 @@ polygons = [PackedInt32Array(2, 1, 0), PackedInt32Array(3, 2, 4), PackedInt32Arr [sub_resource type="StandardMaterial3D" id="Material_ground"] albedo_color = Color(0.24, 0.48, 0.25, 1) -[sub_resource type="StandardMaterial3D" id="Material_ridge"] -albedo_color = Color(0.34, 0.31, 0.27, 1) - -[sub_resource type="StandardMaterial3D" id="Material_terrace"] -albedo_color = Color(0.42, 0.5, 0.29, 1) - -[sub_resource type="StandardMaterial3D" id="Material_water"] -albedo_color = Color(0.12, 0.39, 0.55, 0.72) -transparency = 1 -metallic = 0.18 -roughness = 0.12 -emission_enabled = true -emission = Color(0.05, 0.19, 0.24, 1) -emission_energy_multiplier = 0.45 - -[sub_resource type="StandardMaterial3D" id="Material_building"] -albedo_color = Color(0.62, 0.48, 0.32, 1) - [sub_resource type="BoxMesh" id="Mesh_ground"] material = SubResource("Material_ground") size = Vector3(64, 0.2, 64) @@ -51,56 +41,6 @@ size = Vector3(64, 0.2, 64) [sub_resource type="BoxShape3D" id="Shape_ground"] size = Vector3(64, 0.2, 64) -[sub_resource type="BoxMesh" id="Mesh_ridge"] -material = SubResource("Material_ridge") -size = Vector3(12, 10, 28) - -[sub_resource type="BoxMesh" id="Mesh_terrace"] -material = SubResource("Material_terrace") -size = Vector3(15, 1.5, 8) - -[sub_resource type="BoxMesh" id="Mesh_river"] -material = SubResource("Material_water") -size = Vector3(5, 0.08, 56) - -[sub_resource type="BoxMesh" id="Mesh_house"] -material = SubResource("Material_building") -size = Vector3(4, 4, 5) - -[sub_resource type="BoxMesh" id="Mesh_fortress"] -material = SubResource("Material_building") -size = Vector3(8, 5, 10) - -[sub_resource type="BoxMesh" id="Mesh_bridge"] -material = SubResource("Material_building") -size = Vector3(8, 0.5, 3) - -[sub_resource type="BoxMesh" id="Mesh_waterfall"] -material = SubResource("Material_water") -size = Vector3(5, 10, 0.5) - -[sub_resource type="StandardMaterial3D" id="Material_foam"] -albedo_color = Color(0.82, 0.94, 0.94, 0.85) -transparency = 1 -roughness = 0.25 -emission_enabled = true -emission = Color(0.36, 0.52, 0.54, 1) -emission_energy_multiplier = 0.35 - -[sub_resource type="BoxMesh" id="Mesh_foam"] -material = SubResource("Material_foam") -size = Vector3(5.5, 0.12, 2.2) - -[sub_resource type="StandardMaterial3D" id="Material_mist"] -albedo_color = Color(0.82, 0.91, 0.92, 0.16) -transparency = 1 -shading_mode = 0 - -[sub_resource type="SphereMesh" id="Mesh_mist"] -material = SubResource("Material_mist") -radius = 2.8 -height = 4.0 - [sub_resource type="ProceduralSkyMaterial" id="SkyMaterial_jajce"] sky_top_color = Color(0.23, 0.43, 0.62, 1) sky_horizon_color = Color(0.82, 0.68, 0.5, 1) @@ -131,6 +71,15 @@ script = ExtResource("3_world") [node name="NavigationRegion3D" type="NavigationRegion3D" parent="."] navigation_mesh = SubResource("NavigationMesh_greybox") +[node name="GreyboxGround" type="StaticBody3D" parent="NavigationRegion3D"] + +[node name="Mesh" type="MeshInstance3D" parent="NavigationRegion3D/GreyboxGround"] +visible = false +mesh = SubResource("Mesh_ground") + +[node name="Collision" type="CollisionShape3D" parent="NavigationRegion3D/GreyboxGround"] +shape = SubResource("Shape_ground") + [node name="TerrainRoot" type="Node3D" parent="."] [node name="Terrain3D" type="Terrain3D" parent="TerrainRoot"] @@ -141,78 +90,44 @@ collision_mask = 3 collision_mode = 0 top_level = true -[node name="GreyboxGround" type="StaticBody3D" parent="NavigationRegion3D"] +[node name="FortressBlockout" type="Node3D" parent="."] +position = Vector3(-25, 7, 9) -[node name="Mesh" type="MeshInstance3D" parent="NavigationRegion3D/GreyboxGround"] -visible = false -mesh = SubResource("Mesh_ground") - -[node name="Collision" type="CollisionShape3D" parent="NavigationRegion3D/GreyboxGround"] -shape = SubResource("Shape_ground") - -[node name="GreyboxLandmarks" type="Node3D" parent="."] - -[node name="FortressRidge" type="MeshInstance3D" parent="GreyboxLandmarks"] -position = Vector3(-25, 5, 9) -mesh = SubResource("Mesh_ridge") - -[node name="FortressBlockout" type="MeshInstance3D" parent="GreyboxLandmarks"] -position = Vector3(-25, 12.5, 9) -mesh = SubResource("Mesh_fortress") - -[node name="UpperTerrace" type="MeshInstance3D" parent="GreyboxLandmarks"] -position = Vector3(-14, 0.75, 12) -mesh = SubResource("Mesh_terrace") - -[node name="LowerTerrace" type="MeshInstance3D" parent="GreyboxLandmarks"] -position = Vector3(-11, 0.75, 2) -mesh = SubResource("Mesh_terrace") +[node name="Keep" parent="FortressBlockout" instance=ExtResource("5_fortress")] [node name="WaterRoot" type="Node3D" parent="."] -[node name="RiverGreybox" type="MeshInstance3D" parent="WaterRoot"] -position = Vector3(25, 0.12, 0) -mesh = SubResource("Mesh_river") +[node name="RiverSurface" parent="WaterRoot" instance=ExtResource("9_water")] -[node name="WaterfallGreybox" type="MeshInstance3D" parent="WaterRoot"] -position = Vector3(25, 5, -24) -mesh = SubResource("Mesh_waterfall") +[node name="WaterfallBody" parent="WaterRoot" instance=ExtResource("10_waterfall")] -[node name="WaterfallFoam" type="MeshInstance3D" parent="WaterRoot"] -position = Vector3(25, 0.22, -20.5) -mesh = SubResource("Mesh_foam") - -[node name="Mist_01" type="MeshInstance3D" parent="WaterRoot"] -position = Vector3(24, 1.7, -20) -scale = Vector3(1.5, 0.55, 1) -mesh = SubResource("Mesh_mist") - -[node name="Mist_02" type="MeshInstance3D" parent="WaterRoot"] -position = Vector3(27, 2.2, -22) -scale = Vector3(1.1, 0.4, 0.8) -mesh = SubResource("Mesh_mist") +[node name="WaterfallMist" parent="WaterRoot" instance=ExtResource("11_mist")] [node name="VillageRoot" type="Node3D" parent="."] -[node name="House_01" type="MeshInstance3D" parent="VillageRoot"] +[node name="House_01" parent="VillageRoot" instance=ExtResource("6_house")] position = Vector3(-15, 2, 7) -mesh = SubResource("Mesh_house") -[node name="House_02" type="MeshInstance3D" parent="VillageRoot"] +[node name="Smoke" parent="VillageRoot/House_01" instance=ExtResource("12_smoke")] +position = Vector3(0, 4.5, 0) + +[node name="House_02" parent="VillageRoot" instance=ExtResource("6_house")] position = Vector3(-8, 2, 2) -mesh = SubResource("Mesh_house") -[node name="House_03" type="MeshInstance3D" parent="VillageRoot"] +[node name="Smoke" parent="VillageRoot/House_02" instance=ExtResource("12_smoke")] +position = Vector3(0, 4.5, 0) + +[node name="House_03" parent="VillageRoot" instance=ExtResource("6_house")] position = Vector3(-15, 2, -4) -mesh = SubResource("Mesh_house") -[node name="MillBlockout" type="MeshInstance3D" parent="VillageRoot"] +[node name="Smoke" parent="VillageRoot/House_03" instance=ExtResource("12_smoke")] +position = Vector3(0, 4.5, 0) + +[node name="Mill" parent="VillageRoot" instance=ExtResource("7_mill")] position = Vector3(19, 2, 7) -mesh = SubResource("Mesh_house") -[node name="BridgeBlockout" type="MeshInstance3D" parent="VillageRoot"] +[node name="Bridge" parent="VillageRoot" instance=ExtResource("8_bridge")] position = Vector3(25, 0.5, 0) -mesh = SubResource("Mesh_bridge") [node name="FoliageRoot" type="Node3D" parent="."] diff --git a/world/jajce/StylizedHouse.tscn b/world/jajce/StylizedHouse.tscn new file mode 100644 index 0000000..54c5f91 --- /dev/null +++ b/world/jajce/StylizedHouse.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=7 format=3] + +[sub_resource type="StandardMaterial3D" id="Material_walls"] +albedo_color = Color(0.72, 0.65, 0.52, 1) +roughness = 0.85 + +[sub_resource type="BoxMesh" id="Mesh_walls"] +material = SubResource("Material_walls") +size = Vector3(4, 2.5, 5) + +[sub_resource type="StandardMaterial3D" id="Material_roof"] +albedo_color = Color(0.55, 0.30, 0.18, 1) +roughness = 0.75 + +[sub_resource type="PrismMesh" id="Mesh_roof"] +material = SubResource("Material_roof") +size = Vector3(4.2, 1.5, 5.2) +left_to_right = 0.5 + +[sub_resource type="StandardMaterial3D" id="Material_door"] +albedo_color = Color(0.20, 0.12, 0.06, 1) +roughness = 0.7 + +[sub_resource type="BoxMesh" id="Mesh_door"] +material = SubResource("Material_door") +size = Vector3(0.8, 1.4, 0.1) + +[node name="StylizedHouse" type="Node3D"] + +[node name="Walls" type="MeshInstance3D" parent="."] +position = Vector3(0, 1.25, 0) +mesh = SubResource("Mesh_walls") + +[node name="Roof" type="MeshInstance3D" parent="."] +position = Vector3(0, 3.25, 0) +mesh = SubResource("Mesh_roof") + +[node name="Door" type="MeshInstance3D" parent="."] +position = Vector3(0, 1.0, 2.55) +mesh = SubResource("Mesh_door") diff --git a/world/jajce/StylizedMill.tscn b/world/jajce/StylizedMill.tscn new file mode 100644 index 0000000..86b16bd --- /dev/null +++ b/world/jajce/StylizedMill.tscn @@ -0,0 +1,43 @@ +[gd_scene load_steps=7 format=3] + +[sub_resource type="StandardMaterial3D" id="Material_body"] +albedo_color = Color(0.55, 0.38, 0.22, 1) +roughness = 0.8 + +[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="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="CylinderMesh" id="Mesh_wheel"] +material = SubResource("Material_wheel") +top_radius = 1.2 +bottom_radius = 1.2 +height = 0.15 + +[node name="StylizedMill" type="Node3D"] + +[node name="Body" type="MeshInstance3D" parent="."] +position = Vector3(0, 1.5, 0) +mesh = SubResource("Mesh_body") + +[node name="Roof" type="MeshInstance3D" parent="."] +position = Vector3(0, 3.6, 0) +mesh = SubResource("Mesh_roof") + +[node name="WaterWheel" type="MeshInstance3D" parent="."] +position = Vector3(-1.8, 1.5, 0) +rotation_degrees = Vector3(0, 0, 90) +mesh = SubResource("Mesh_wheel") diff --git a/world/jajce/StylizedTree.gd b/world/jajce/StylizedTree.gd new file mode 100644 index 0000000..e8193cc --- /dev/null +++ b/world/jajce/StylizedTree.gd @@ -0,0 +1,58 @@ +extends Node3D + +enum CanopyVariant { + GREEN, + DARK_GREEN, + YELLOW_GREEN, +} + +@export var canopy_variant: CanopyVariant = CanopyVariant.GREEN +@export var canopy_radius: float = 1.35 + +const COLORS := { + CanopyVariant.GREEN: Color(0.19, 0.38, 0.17), + CanopyVariant.DARK_GREEN: Color(0.12, 0.30, 0.12), + CanopyVariant.YELLOW_GREEN: Color(0.30, 0.42, 0.15), +} + +const WIND_SHADER := preload("res://world/jajce/materials/wind.gdshader") + +func _ready() -> void: + for child in get_children(): + child.queue_free() + + var hash_seed := hash(global_position) + var rng := RandomNumberGenerator.new() + rng.seed = hash_seed + canopy_variant = rng.randi() % COLORS.size() as CanopyVariant + canopy_radius = 1.0 + rng.randf() * 0.8 + + var trunk_mesh := CylinderMesh.new() + trunk_mesh.top_radius = 0.22 + trunk_mesh.bottom_radius = 0.32 + trunk_mesh.height = 2.8 + + var trunk_mat := StandardMaterial3D.new() + trunk_mat.albedo_color = Color(0.27, 0.16, 0.09) + trunk_mat.roughness = 0.95 + trunk_mesh.material = trunk_mat + + var trunk := MeshInstance3D.new() + trunk.mesh = trunk_mesh + trunk.position = Vector3(0, 1.4, 0) + add_child(trunk) + + var canopy_mesh := SphereMesh.new() + canopy_mesh.radius = canopy_radius + canopy_mesh.height = 2.4 + + var canopy_mat := ShaderMaterial.new() + canopy_mat.shader = WIND_SHADER + canopy_mat.set_shader_parameter("albedo", COLORS[canopy_variant]) + canopy_mat.set_shader_parameter("roughness", 0.9) + canopy_mesh.material = canopy_mat + + var canopy := MeshInstance3D.new() + canopy.mesh = canopy_mesh + canopy.position = Vector3(0, 3.2 + (canopy_radius - 1.35) * 0.5, 0) + add_child(canopy) diff --git a/world/jajce/StylizedTree.gd.uid b/world/jajce/StylizedTree.gd.uid new file mode 100644 index 0000000..806aa43 --- /dev/null +++ b/world/jajce/StylizedTree.gd.uid @@ -0,0 +1 @@ +uid://b7i6nat33edhc diff --git a/world/jajce/StylizedTree.tscn b/world/jajce/StylizedTree.tscn index fc5f0dd..228c850 100644 --- a/world/jajce/StylizedTree.tscn +++ b/world/jajce/StylizedTree.tscn @@ -1,30 +1,6 @@ -[gd_scene load_steps=5 format=3] +[gd_scene load_steps=2 format=3] -[sub_resource type="StandardMaterial3D" id="Material_trunk"] -albedo_color = Color(0.27, 0.16, 0.09, 1) -roughness = 0.95 - -[sub_resource type="CylinderMesh" id="Mesh_trunk"] -material = SubResource("Material_trunk") -top_radius = 0.22 -bottom_radius = 0.32 -height = 2.8 - -[sub_resource type="StandardMaterial3D" id="Material_canopy"] -albedo_color = Color(0.19, 0.38, 0.17, 1) -roughness = 0.9 - -[sub_resource type="SphereMesh" id="Mesh_canopy"] -material = SubResource("Material_canopy") -radius = 1.35 -height = 2.4 +[ext_resource type="Script" path="res://world/jajce/StylizedTree.gd" id="1_script"] [node name="StylizedTree" type="Node3D"] - -[node name="Trunk" type="MeshInstance3D" parent="."] -position = Vector3(0, 1.4, 0) -mesh = SubResource("Mesh_trunk") - -[node name="Canopy" type="MeshInstance3D" parent="."] -position = Vector3(0, 3.2, 0) -mesh = SubResource("Mesh_canopy") +script = ExtResource("1_script") diff --git a/world/jajce/WaterBody.tscn b/world/jajce/WaterBody.tscn new file mode 100644 index 0000000..5394016 --- /dev/null +++ b/world/jajce/WaterBody.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Material" path="res://world/jajce/materials/water.gdshader" id="1_water_shader"] + +[sub_resource type="BoxMesh" id="Mesh_water_body"] +material = ExtResource("1_water_shader") +size = Vector3(5, 0.08, 56) + +[node name="WaterBody" type="Node3D"] +position = Vector3(25, 0.12, 0) + +[node name="Mesh" type="MeshInstance3D" parent="."] +mesh = SubResource("Mesh_water_body") diff --git a/world/jajce/WaterfallBody.tscn b/world/jajce/WaterfallBody.tscn new file mode 100644 index 0000000..b083c8c --- /dev/null +++ b/world/jajce/WaterfallBody.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Material" path="res://world/jajce/materials/waterfall.gdshader" id="1_waterfall_shader"] + +[sub_resource type="BoxMesh" id="Mesh_waterfall_body"] +material = ExtResource("1_waterfall_shader") +size = Vector3(5, 10, 0.5) + +[node name="WaterfallBody" type="Node3D"] +position = Vector3(25, 5, -24) + +[node name="Mesh" type="MeshInstance3D" parent="."] +mesh = SubResource("Mesh_waterfall_body") diff --git a/world/jajce/WaterfallMist.tscn b/world/jajce/WaterfallMist.tscn new file mode 100644 index 0000000..6d27879 --- /dev/null +++ b/world/jajce/WaterfallMist.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=3 format=3] + +[sub_resource type="Curve" id="Curve_alpha"] +_data = [Vector2(0, 1), Vector2(1, 0)] + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_mist"] +lifetime_randomness = 0.2 +spread = 180.0 +gravity = Vector3(0, -0.5, 0) +initial_velocity_min = 0.3 +initial_velocity_max = 0.8 +scale_min = 0.1 +scale_max = 1.5 +color = Color(0.85, 0.95, 0.95, 0.4) +alpha_curve = SubResource("Curve_alpha") +emission_shape = 1 +emission_box_extents = Vector3(2.5, 1.0, 0.5) + +[node name="WaterfallMist" type="GpuParticles3D"] +emitting = true +amount = 48 +lifetime = 2.5 +one_shot = false +preprocess = 1.0 +speed_scale = 1.0 +local_coords = true +draw_order = 0 +process_material = SubResource("ParticleProcessMaterial_mist") diff --git a/world/jajce/jajce_world.gd b/world/jajce/jajce_world.gd index db90113..aafb705 100644 --- a/world/jajce/jajce_world.gd +++ b/world/jajce/jajce_world.gd @@ -9,7 +9,14 @@ func _initialize_world_presentation() -> void: var active_camera := get_viewport().get_camera_3d() if active_camera != null: terrain.set_camera(active_camera) - for tree in $FoliageRoot.get_children(): - var terrain_height: float = terrain.data.get_height(tree.global_position) - if not is_nan(terrain_height): - tree.global_position.y = terrain_height + var groups_to_snap: Array[Node] = [] + groups_to_snap.append_array($FoliageRoot.get_children()) + if has_node("FortressBlockout"): + groups_to_snap.append($FortressBlockout) + for child in $VillageRoot.get_children(): + groups_to_snap.append(child) + for item in groups_to_snap: + var pos := item.global_position + var h: float = terrain.data.get_height(pos) + if not is_nan(h): + item.global_position.y = h diff --git a/world/jajce/materials/water.gdshader b/world/jajce/materials/water.gdshader new file mode 100644 index 0000000..3fa01c1 --- /dev/null +++ b/world/jajce/materials/water.gdshader @@ -0,0 +1,20 @@ +shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; + +uniform float wave_strength : hint_range(0.0, 1.0) = 0.15; +uniform float wave_speed : hint_range(0.0, 5.0) = 1.2; + +void vertex() { + vec3 pos = VERTEX; + float wave = sin(pos.z * 0.4 + TIME * wave_speed) * wave_strength; + pos.y += wave; + VERTEX = pos; +} + +void fragment() { + ALBEDO = vec3(0.12, 0.39, 0.55); + METALLIC = 0.15; + ROUGHNESS = 0.1; + EMISSION = vec3(0.05, 0.19, 0.24) * 0.45; + ALPHA = 0.72; +} diff --git a/world/jajce/materials/water.gdshader.uid b/world/jajce/materials/water.gdshader.uid new file mode 100644 index 0000000..1f63524 --- /dev/null +++ b/world/jajce/materials/water.gdshader.uid @@ -0,0 +1 @@ +uid://m1s2ma420y34 diff --git a/world/jajce/materials/waterfall.gdshader b/world/jajce/materials/waterfall.gdshader new file mode 100644 index 0000000..9bcdd72 --- /dev/null +++ b/world/jajce/materials/waterfall.gdshader @@ -0,0 +1,21 @@ +shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; + +uniform float scroll_speed : hint_range(0.0, 5.0) = 2.0; + +void vertex() { +} + +void fragment() { + vec2 uv = UV; + uv.y += TIME * scroll_speed; + float pattern = fract(uv.y * 4.0); + pattern = smoothstep(0.3, 0.7, pattern); + pattern = mix(0.4, 1.0, pattern); + vec3 base_color = mix(vec3(0.6, 0.85, 0.9), vec3(1.0, 1.0, 1.0), pattern); + ALBEDO = base_color; + ALPHA = mix(0.3, 0.7, pattern); + EMISSION = vec3(0.36, 0.52, 0.54) * 0.35 * pattern; + METALLIC = 0.0; + ROUGHNESS = 0.2; +} diff --git a/world/jajce/materials/waterfall.gdshader.uid b/world/jajce/materials/waterfall.gdshader.uid new file mode 100644 index 0000000..80af6a3 --- /dev/null +++ b/world/jajce/materials/waterfall.gdshader.uid @@ -0,0 +1 @@ +uid://bi2eesg0ly3pq diff --git a/world/jajce/materials/wind.gdshader b/world/jajce/materials/wind.gdshader new file mode 100644 index 0000000..f0c7ecc --- /dev/null +++ b/world/jajce/materials/wind.gdshader @@ -0,0 +1,24 @@ +shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; + +uniform float wind_strength : hint_range(0.0, 0.5) = 0.08; +uniform float wind_speed : hint_range(0.0, 5.0) = 0.6; +uniform vec3 albedo : source_color = vec3(0.19, 0.38, 0.17); +uniform float roughness : hint_range(0.0, 1.0) = 0.9; + +void 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; + + float sway_x = sin(VERTEX.x * 0.3 + TIME * wind_speed) * wind_strength * height_factor; + float sway_z = cos(VERTEX.z * 0.3 + TIME * wind_speed * 0.7) * wind_strength * height_factor; + + VERTEX.x += sway_x; + VERTEX.z += sway_z; +} + +void fragment() { + ALBEDO = albedo; + ROUGHNESS = roughness; +} diff --git a/world/jajce/materials/wind.gdshader.uid b/world/jajce/materials/wind.gdshader.uid new file mode 100644 index 0000000..59cbca7 --- /dev/null +++ b/world/jajce/materials/wind.gdshader.uid @@ -0,0 +1 @@ +uid://cnc12rxvfw5qh