Expand procedural terrain generation

This commit is contained in:
Alexander Sellite 2026-08-20 21:42:50 -04:00
parent b82f6fe15c
commit 5d55afcc26
51 changed files with 1924 additions and 223 deletions

View file

@ -17,4 +17,5 @@ script = ExtResource("1_definition")
stable_id = &"biome_coast"
label = "coast"
required_chunk_tags = PackedStringArray("sand")
forbidden_chunk_tags = PackedStringArray("mixed_surface")
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_sand_tree")])

View file

@ -27,6 +27,6 @@ script = ExtResource("1_definition")
stable_id = &"biome_forest"
label = "forest"
required_chunk_tags = PackedStringArray("grass")
forbidden_chunk_tags = PackedStringArray("spawn")
forbidden_chunk_tags = PackedStringArray("spawn", "mixed_surface")
region_weight = 1.4
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")])

View file

@ -27,6 +27,6 @@ script = ExtResource("1_definition")
stable_id = &"biome_pine_forest"
label = "pine forest"
required_chunk_tags = PackedStringArray("grass")
forbidden_chunk_tags = PackedStringArray("spawn")
forbidden_chunk_tags = PackedStringArray("spawn", "mixed_surface")
region_weight = 1.2
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")])

View file

@ -24,5 +24,6 @@ script = ExtResource("1_definition")
stable_id = &"biome_plains"
label = "plains"
required_chunk_tags = PackedStringArray("grass")
forbidden_chunk_tags = PackedStringArray("mixed_surface")
region_weight = 1.0
prop_rules = Array[ExtResource("2_rule")]([SubResource("Rule_grass_tree"), SubResource("Rule_grass_detail")])

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dom2fvsn0s1wa"
path="res://.godot/imported/chunk_0013.glb-64cf2d2bb9c9020fa3d06004b1963f09.scn"
[deps]
source_file="res://world/generation/chunks/assets/chunk_0013.glb"
dest_files=["res://.godot/imported/chunk_0013.glb-64cf2d2bb9c9020fa3d06004b1963f09.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.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dct65l02vr54q"
path.s3tc="res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0013_grass_lite.png"
dest_files=["res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.s3tc.ctex", "res://.godot/imported/chunk_0013_grass_lite.png-0cf332d69bdd6f5da588e0ddda3bd437.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://btjhnb6owbjj2"
path.s3tc="res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "adfa66b8fe9da7f3450d3ddfd432b86a"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0013_sand.png"
dest_files=["res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.s3tc.ctex", "res://.godot/imported/chunk_0013_sand.png-5fd756543742405c3864f1fdfb36632c.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://50aolv5pg8d2"
path="res://.godot/imported/chunk_0014.glb-636ccc776ff9575e58a9db68c3f273af.scn"
[deps]
source_file="res://world/generation/chunks/assets/chunk_0014.glb"
dest_files=["res://.godot/imported/chunk_0014.glb-636ccc776ff9575e58a9db68c3f273af.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.

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://6sd1uj06ti8f"
path.s3tc="res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "fc97184f537cc23f83a731327e10c836"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0014_cliff_wall.png"
dest_files=["res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.s3tc.ctex", "res://.godot/imported/chunk_0014_cliff_wall.png-9ea694f83659f89e6c17ae1a39cf97cb.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6fftn8x2o1dd"
path.s3tc="res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0014_grass_lite.png"
dest_files=["res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.s3tc.ctex", "res://.godot/imported/chunk_0014_grass_lite.png-90b3bea1cb09cf8dbae98afc39c1f5cd.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://d26fd6sjsh50x"
path="res://.godot/imported/chunk_0015.glb-0a28f27956d19c7965f7bc9c838f1b29.scn"
[deps]
source_file="res://world/generation/chunks/assets/chunk_0015.glb"
dest_files=["res://.godot/imported/chunk_0015.glb-0a28f27956d19c7965f7bc9c838f1b29.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.

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cpce75ti5cwnw"
path.s3tc="res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "fc97184f537cc23f83a731327e10c836"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0015_cliff_wall.png"
dest_files=["res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.s3tc.ctex", "res://.godot/imported/chunk_0015_cliff_wall.png-08e30f267a5816ed94aa39dc21ccf05c.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://pmaf2oetdqln"
path.s3tc="res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0015_grass_lite.png"
dest_files=["res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.s3tc.ctex", "res://.godot/imported/chunk_0015_grass_lite.png-0ae072e9d3b6d68d7fdb493a6acf3bad.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://te1yrm2uhin1"
path="res://.godot/imported/chunk_0016.glb-6904d8b3583dd301bda1835c501225f3.scn"
[deps]
source_file="res://world/generation/chunks/assets/chunk_0016.glb"
dest_files=["res://.godot/imported/chunk_0016.glb-6904d8b3583dd301bda1835c501225f3.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.

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dxy8u3vyyu2on"
path.s3tc="res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "07d90b3c46a4c3d0c172bed143dc9436"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0016_dirt.png"
dest_files=["res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.s3tc.ctex", "res://.godot/imported/chunk_0016_dirt.png-5f2ccbadabdb1e395518abce5c09d66f.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cn8ljtpbsg253"
path.s3tc="res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "b5430dba0b52db87d30bae85ae640b09"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0016_dirt_wall.png"
dest_files=["res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.s3tc.ctex", "res://.godot/imported/chunk_0016_dirt_wall.png-536115f36dddb3063c7b5162468a4876.etc2.ctex"]
[params]
compress/mode=2
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=0

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -0,0 +1,45 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dn84haedw08rx"
path.s3tc="res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.s3tc.ctex"
path.etc2="res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
}
[deps]
source_file="res://world/generation/chunks/assets/chunk_0016_grass_lite.png"
dest_files=["res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.s3tc.ctex", "res://.godot/imported/chunk_0016_grass_lite.png-df65a0c79eb0a4e70a36096ead0f76f3.etc2.ctex"]
[params]
compress/mode=2
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=0

View file

@ -9,7 +9,7 @@ stable_id = &"chunk_0003"
label = "stream"
packed_scene = ExtResource("2_scene")
primary_mesh_name = &"chunk_0003"
selection_weight = 0.6
selection_weight = 0.3
tags = PackedStringArray("land", "walkable", "fresh_water", "river", "flowing")
allowed_neighbor_tags = PackedStringArray("grass")
preferred_neighbor_tags = PackedStringArray("grass")

View file

@ -0,0 +1,28 @@
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=3 format=3]
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0013.glb" id="2_scene"]
[resource]
script = ExtResource("1_definition")
stable_id = &"chunk_0013"
label = "grass-sand diagonal"
packed_scene = ExtResource("2_scene")
primary_mesh_name = &"chunk_0013"
participates_in_base_solver = false
allowed_rotation_mask = 15
selection_weight = 0.8
maximum_placements = 16
tags = PackedStringArray("land", "walkable", "grass", "sand", "flat", "transition", "mixed_surface")
allowed_neighbor_tags = PackedStringArray("grass", "sand")
forbidden_neighbor_tags = PackedStringArray("fresh_water", "spawn")
north_allowed_neighbor_tags = PackedStringArray("sand")
east_allowed_neighbor_tags = PackedStringArray("grass")
south_allowed_neighbor_tags = PackedStringArray("grass")
west_allowed_neighbor_tags = PackedStringArray("sand")
north_surface_tags = PackedStringArray("sand")
east_surface_tags = PackedStringArray("grass")
south_surface_tags = PackedStringArray("grass")
west_surface_tags = PackedStringArray("sand")
preferred_neighbor_tags = PackedStringArray("grass", "sand", "transition")
must_be_interior = true

View file

@ -0,0 +1,24 @@
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=3 format=3]
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0014.glb" id="2_scene"]
[resource]
script = ExtResource("1_definition")
stable_id = &"chunk_0014"
label = "second-tier grass sea edge"
packed_scene = ExtResource("2_scene")
primary_mesh_name = &"chunk_0014"
overlay_only = true
selection_weight = 0.28
maximum_placements = 32
tags = PackedStringArray("land", "walkable", "grass", "coast", "cliff", "edge", "elevation_2", "elevation_2_border", "elevation_2_sea_edge")
allowed_neighbor_tags = PackedStringArray("elevation_2")
north_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
south_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
west_allowed_neighbor_tags = PackedStringArray("elevation_2_top")
required_neighbor_tags = PackedStringArray("elevation_2_top")
minimum_required_neighbors = 1
preferred_neighbor_tags = PackedStringArray("elevation_2", "coast")
prefers_map_boundary = true
ocean_facing_edges = 2

View file

@ -0,0 +1,23 @@
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=3 format=3]
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0015.glb" id="2_scene"]
[resource]
script = ExtResource("1_definition")
stable_id = &"chunk_0015"
label = "second-tier grass sea corner"
packed_scene = ExtResource("2_scene")
primary_mesh_name = &"chunk_0015"
overlay_only = true
selection_weight = 0.12
maximum_placements = 16
tags = PackedStringArray("land", "walkable", "grass", "coast", "cliff", "corner", "elevation_2", "elevation_2_border", "elevation_2_sea_edge")
allowed_neighbor_tags = PackedStringArray("elevation_2")
north_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
west_allowed_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
required_neighbor_tags = PackedStringArray("elevation_2_sea_edge")
minimum_required_neighbors = 2
preferred_neighbor_tags = PackedStringArray("elevation_2", "coast")
prefers_map_boundary = true
ocean_facing_edges = 6

View file

@ -0,0 +1,19 @@
[gd_resource type="Resource" script_class="TerrainChunkDefinition" format=3]
[ext_resource type="Script" path="res://world/generation/terrain_chunk_definition.gd" id="1_definition"]
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0016.glb" id="2_scene"]
[resource]
script = ExtResource("1_definition")
stable_id = &"chunk_0016"
label = "stream variant"
packed_scene = ExtResource("2_scene")
primary_mesh_name = &"chunk_0016"
selection_weight = 0.3
tags = PackedStringArray("land", "walkable", "fresh_water", "river", "flowing")
allowed_neighbor_tags = PackedStringArray("grass")
preferred_neighbor_tags = PackedStringArray("grass")
water_inlet_edges = 1
water_outlet_edges = 4
water_surface_size = Vector2(2.1, 10)
water_surface_offset = Vector2(-0.315, 0)

View file

@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=17 format=3]
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=21 format=3]
[ext_resource type="Script" path="res://world/generation/terrain_chunk_catalog.gd" id="1_catalog"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0000.tres" id="2_grass"]
@ -16,8 +16,12 @@
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0010.tres" id="14_cliff_corner"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0011.tres" id="15_cliff_edge"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0012.tres" id="16_cliff_ramp"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0013.tres" id="17_grass_sand_diagonal"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0014.tres" id="18_cliff_sea_edge"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0015.tres" id="19_cliff_sea_corner"]
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0016.tres" id="20_stream_variant"]
[resource]
script = ExtResource("1_catalog")
chunk_size = 10.0
definitions = Array[ExtResource("7_definition_script")]([ExtResource("2_grass"), ExtResource("3_sand"), ExtResource("4_beach"), ExtResource("5_stream"), ExtResource("6_pond"), ExtResource("9_grass_ocean_edge"), ExtResource("10_grass_beach_transition"), ExtResource("11_grass_ocean_corner"), ExtResource("12_beach_ocean_corner"), ExtResource("13_cliff_top"), ExtResource("14_cliff_corner"), ExtResource("15_cliff_edge"), ExtResource("16_cliff_ramp"), ExtResource("8_spawn")])
definitions = Array[ExtResource("7_definition_script")]([ExtResource("2_grass"), ExtResource("3_sand"), ExtResource("4_beach"), ExtResource("5_stream"), ExtResource("20_stream_variant"), ExtResource("6_pond"), ExtResource("9_grass_ocean_edge"), ExtResource("10_grass_beach_transition"), ExtResource("11_grass_ocean_corner"), ExtResource("12_beach_ocean_corner"), ExtResource("17_grass_sand_diagonal"), ExtResource("13_cliff_top"), ExtResource("14_cliff_corner"), ExtResource("15_cliff_edge"), ExtResource("16_cliff_ramp"), ExtResource("18_cliff_sea_edge"), ExtResource("19_cliff_sea_corner"), ExtResource("8_spawn")])

View file

@ -37,6 +37,7 @@ generate_on_ready = false
build_collision = true
force_center_chunk_id = &"chunk_spawn"
required_chunk_ids = PackedStringArray("chunk_spawn", "chunk_0001", "chunk_0002", "chunk_0003", "chunk_0004", "chunk_0005", "chunk_0006", "chunk_0007", "chunk_0008")
grass_sand_smoothing_enabled = true
elevated_cliff_feature_enabled = true
required_chunk_weight_multiplier = 64.0
maximum_backtracks = 100000

View file

@ -38,10 +38,16 @@ func validation_errors() -> PackedStringArray:
"%s must define both base-layer scene and mesh name."
% definition.stable_id
)
if definition.overlay_only and definition.base_layer_scene == null:
if (
definition.overlay_only
and definition.base_layer_scene == null
and definition.ocean_facing_edges == 0
):
errors.append(
"%s is overlay-only but has no base-layer scene."
% definition.stable_id
(
"%s is an inland overlay but has no base-layer scene."
% definition.stable_id
)
)
if definition.allowed_rotation_mask == 0:
errors.append("%s allows no rotations." % definition.stable_id)

View file

@ -12,9 +12,14 @@ extends Resource
@export var base_layer_scene: PackedScene
@export var base_layer_mesh_name: StringName
## Overlay-only chunks replace a reserved base-terrain placement after the
## ordinary terrain solve. They remain in placement manifests and runtime
## output, but do not inflate every cell's global solver domain.
## ordinary terrain solve. Inland overlays normally provide base_layer_scene;
## complete coastal overlays can instead carry their own descent to sea level.
## They remain in placement manifests and runtime output, but do not inflate
## every cell's global solver domain.
@export var overlay_only := false
## Post-process pieces remain available as authored variants and in placement
## manifests, but do not participate in the ordinary base-terrain solve.
@export var participates_in_base_solver := true
@export_group("")
@export_flags("0 degrees", "90 degrees", "180 degrees", "270 degrees")
var allowed_rotation_mask := 15
@ -38,6 +43,16 @@ var allowed_rotation_mask := 15
@export var south_allowed_neighbor_tags := PackedStringArray()
@export var west_allowed_neighbor_tags := PackedStringArray()
@export_group("")
## Optional canonical descriptions of the visible terrain surface at each
## edge. Empty edges inherit the definition's ordinary tags. Mixed-surface
## chunks use these to prevent a visually grass edge from joining sand (or the
## reverse) even when the chunk itself carries both biome tags.
@export_group("Directional Surface Rules")
@export var north_surface_tags := PackedStringArray()
@export var east_surface_tags := PackedStringArray()
@export var south_surface_tags := PackedStringArray()
@export var west_surface_tags := PackedStringArray()
@export_group("")
## When non-empty, at least this many in-grid cardinal neighbors must carry one
## of the listed tags. This supports statements such as "three sides around the
## spawn must be safe grass" without hard-coding a particular chunk ID.
@ -106,6 +121,28 @@ func directional_allowed_neighbor_tags(
return PackedStringArray()
func surface_tags_for_edge(
edge: TerrainChunkTopology.Edge,
) -> PackedStringArray:
var edge_tags := directional_surface_tags(edge)
return tags if edge_tags.is_empty() else edge_tags
func directional_surface_tags(
edge: TerrainChunkTopology.Edge,
) -> PackedStringArray:
match edge:
TerrainChunkTopology.Edge.NORTH:
return north_surface_tags
TerrainChunkTopology.Edge.EAST:
return east_surface_tags
TerrainChunkTopology.Edge.SOUTH:
return south_surface_tags
TerrainChunkTopology.Edge.WEST:
return west_surface_tags
return PackedStringArray()
func _allows_neighbor_with_tags(
neighbor: TerrainChunkDefinition,
allowed_tags: PackedStringArray,

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,10 @@ func allows_non_water_neighbor_on_edge(
)
func surface_tags(edge: TerrainChunkTopology.Edge) -> PackedStringArray:
return definition.surface_tags_for_edge(source_edge(edge))
func topology_signature(quantization: float) -> String:
var tokens := PackedStringArray()
for edge_value: int in TerrainChunkTopology.Edge.values():
@ -44,9 +48,10 @@ func topology_signature(quantization: float) -> String:
func constraint_signature(quantization: float) -> String:
return "%s|neighbors:%s|ocean:%d|in:%d|out:%d" % [
return "%s|neighbors:%s|surfaces:%s|ocean:%d|in:%d|out:%d" % [
topology_signature(quantization),
_neighbor_rule_signature(),
_surface_rule_signature(),
rotated_edge_mask(definition.ocean_facing_edges),
rotated_edge_mask(definition.water_inlet_edges),
rotated_edge_mask(definition.water_outlet_edges),
@ -65,6 +70,16 @@ func _neighbor_rule_signature() -> String:
return "/".join(edge_tokens)
func _surface_rule_signature() -> String:
var edge_tokens := PackedStringArray()
for edge_value: int in TerrainChunkTopology.Edge.values():
var edge := edge_value as TerrainChunkTopology.Edge
var edge_tags: PackedStringArray = surface_tags(edge).duplicate()
edge_tags.sort()
edge_tokens.append(",".join(edge_tags))
return "/".join(edge_tokens)
func rotated_edge_mask(source_mask: int) -> int:
var result := 0
for edge_value: int in TerrainChunkTopology.Edge.values():