Add layered inland cliff generation
|
|
@ -6,10 +6,11 @@
|
|||
[sub_resource type="Resource" id="Rule_grass_tree"]
|
||||
script = ExtResource("2_rule")
|
||||
procedural_group = &"grass_tree"
|
||||
placement_chance = 9000
|
||||
adjacency_bonus = 1000
|
||||
maximum_density = 10000
|
||||
placement_chance = 8200
|
||||
adjacency_bonus = 1200
|
||||
maximum_density = 8500
|
||||
minimum_placements = 2
|
||||
placement_attempts_per_chunk = 3
|
||||
allowed_prop_ids = PackedStringArray("prop_tree_1", "prop_tree_2", "prop_tree_3")
|
||||
|
||||
[sub_resource type="Resource" id="Rule_grass_detail"]
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@
|
|||
[sub_resource type="Resource" id="Rule_grass_tree"]
|
||||
script = ExtResource("2_rule")
|
||||
procedural_group = &"grass_tree"
|
||||
placement_chance = 9000
|
||||
adjacency_bonus = 1000
|
||||
maximum_density = 10000
|
||||
placement_chance = 8200
|
||||
adjacency_bonus = 1200
|
||||
maximum_density = 8500
|
||||
minimum_placements = 2
|
||||
placement_attempts_per_chunk = 3
|
||||
allowed_prop_ids = PackedStringArray("prop_pine")
|
||||
|
||||
[sub_resource type="Resource" id="Rule_grass_detail"]
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
BIN
world/generation/chunks/assets/chunk_0008.glb
Normal file
45
world/generation/chunks/assets/chunk_0008.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bm1teitx5ijxl"
|
||||
path="res://.godot/imported/chunk_0008.glb-0b0a6712e5384c3f72c65e6ef1fc12c4.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0008.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0008.glb-0b0a6712e5384c3f72c65e6ef1fc12c4.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
|
||||
BIN
world/generation/chunks/assets/chunk_0008_sand.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
45
world/generation/chunks/assets/chunk_0008_sand.png.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dj24j2d1u8712"
|
||||
path.s3tc="res://.godot/imported/chunk_0008_sand.png-55d7cf4f540d9cfb7d4ba59e9f9f38da.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0008_sand.png-55d7cf4f540d9cfb7d4ba59e9f9f38da.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_0008_sand.png"
|
||||
dest_files=["res://.godot/imported/chunk_0008_sand.png-55d7cf4f540d9cfb7d4ba59e9f9f38da.s3tc.ctex", "res://.godot/imported/chunk_0008_sand.png-55d7cf4f540d9cfb7d4ba59e9f9f38da.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
|
||||
BIN
world/generation/chunks/assets/chunk_0009.glb
Normal file
45
world/generation/chunks/assets/chunk_0009.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://hasempwso5gi"
|
||||
path="res://.godot/imported/chunk_0009.glb-397f642bee3da14146c56fddf8e21369.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0009.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0009.glb-397f642bee3da14146c56fddf8e21369.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
|
||||
BIN
world/generation/chunks/assets/chunk_0009_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://djjyaoih5vcnd"
|
||||
path="res://.godot/imported/chunk_0009_grass_lite.png-5d49eb465b8a8f8739796ce13c646f7d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0009_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0009_grass_lite.png-5d49eb465b8a8f8739796ce13c646f7d.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
|
||||
BIN
world/generation/chunks/assets/chunk_0010.glb
Normal file
45
world/generation/chunks/assets/chunk_0010.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://b5tf5e1g5auu7"
|
||||
path="res://.godot/imported/chunk_0010.glb-6ffffa0064f5930acc433afe5e4334e4.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0010.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0010.glb-6ffffa0064f5930acc433afe5e4334e4.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
|
||||
BIN
world/generation/chunks/assets/chunk_0010_cliff_wall.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cwwraho3pj5aa"
|
||||
path="res://.godot/imported/chunk_0010_cliff_wall.png-78830d69b22e3c32cc295e574d596c3a.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "fc97184f537cc23f83a731327e10c836"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0010_cliff_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0010_cliff_wall.png-78830d69b22e3c32cc295e574d596c3a.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
|
||||
BIN
world/generation/chunks/assets/chunk_0010_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://q7k2r4mb3bsr"
|
||||
path="res://.godot/imported/chunk_0010_grass_lite.png-694b30e6e36cc16c1cf98b2f3823a0c2.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0010_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0010_grass_lite.png-694b30e6e36cc16c1cf98b2f3823a0c2.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
|
||||
BIN
world/generation/chunks/assets/chunk_0011.glb
Normal file
45
world/generation/chunks/assets/chunk_0011.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cym6ilrdikjcj"
|
||||
path="res://.godot/imported/chunk_0011.glb-cda3ab701cf5e469b53437c043314d05.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0011.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0011.glb-cda3ab701cf5e469b53437c043314d05.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
|
||||
BIN
world/generation/chunks/assets/chunk_0011_cliff_wall.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dggc1jbgenl11"
|
||||
path="res://.godot/imported/chunk_0011_cliff_wall.png-9a4d64a03d75f23c2ef3614386484a4e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "fc97184f537cc23f83a731327e10c836"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0011_cliff_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0011_cliff_wall.png-9a4d64a03d75f23c2ef3614386484a4e.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
|
||||
BIN
world/generation/chunks/assets/chunk_0011_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bhuhcaowvcyrk"
|
||||
path="res://.godot/imported/chunk_0011_grass_lite.png-3a0a1a7ad2d55bd853b238730af91a7c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0011_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0011_grass_lite.png-3a0a1a7ad2d55bd853b238730af91a7c.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
|
||||
BIN
world/generation/chunks/assets/chunk_0012.glb
Normal file
45
world/generation/chunks/assets/chunk_0012.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c4i32u0bdtix5"
|
||||
path="res://.godot/imported/chunk_0012.glb-1c818ae539bef19d5ca4fbe3ace96887.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0012.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0012.glb-1c818ae539bef19d5ca4fbe3ace96887.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
|
||||
BIN
world/generation/chunks/assets/chunk_0012_cliff_wall.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cf27tcolkmfog"
|
||||
path="res://.godot/imported/chunk_0012_cliff_wall.png-3d27d4041790b1c3dbc838d6a0c94d40.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "fc97184f537cc23f83a731327e10c836"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0012_cliff_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0012_cliff_wall.png-3d27d4041790b1c3dbc838d6a0c94d40.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
|
||||
BIN
world/generation/chunks/assets/chunk_0012_dirt.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
43
world/generation/chunks/assets/chunk_0012_dirt.png.import
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c3f1w276lmg7i"
|
||||
path="res://.godot/imported/chunk_0012_dirt.png-5c3a2a10642f9d078ad1d1c6333d8264.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "07d90b3c46a4c3d0c172bed143dc9436"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0012_dirt.png"
|
||||
dest_files=["res://.godot/imported/chunk_0012_dirt.png-5c3a2a10642f9d078ad1d1c6333d8264.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
|
||||
BIN
world/generation/chunks/assets/chunk_0012_grass_lite.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,43 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://lv8xn2moas2s"
|
||||
path="res://.godot/imported/chunk_0012_grass_lite.png-ccfdc53acb9a967eb5d00e669258fc3c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "4dc1ac400d85d9b3e4f43a5af0a38ed4"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0012_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0012_grass_lite.png-ccfdc53acb9a967eb5d00e669258fc3c.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
|
||||
|
|
@ -11,7 +11,7 @@ packed_scene = ExtResource("2_scene")
|
|||
primary_mesh_name = &"chunk_0002"
|
||||
selection_weight = 0.75
|
||||
maximum_placements = 20
|
||||
tags = PackedStringArray("land", "walkable", "sand", "coast", "slope")
|
||||
tags = PackedStringArray("land", "walkable", "sand", "coast", "slope", "beach_ocean_edge")
|
||||
allowed_neighbor_tags = PackedStringArray("sand", "grass")
|
||||
required_neighbor_tags = PackedStringArray("sand")
|
||||
minimum_required_neighbors = 1
|
||||
|
|
|
|||
22
world/generation/chunks/definitions/chunk_0008.tres
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
[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_0008.glb" id="2_scene"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0008"
|
||||
label = "beach corner"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0008"
|
||||
selection_weight = 0.2
|
||||
maximum_placements = 4
|
||||
tags = PackedStringArray("land", "walkable", "sand", "coast", "slope", "corner")
|
||||
allowed_neighbor_tags = PackedStringArray("sand", "coast")
|
||||
north_allowed_neighbor_tags = PackedStringArray("beach_ocean_edge")
|
||||
west_allowed_neighbor_tags = PackedStringArray("beach_ocean_edge")
|
||||
required_neighbor_tags = PackedStringArray("beach_ocean_edge")
|
||||
minimum_required_neighbors = 2
|
||||
preferred_neighbor_tags = PackedStringArray("coast")
|
||||
prefers_map_boundary = true
|
||||
ocean_facing_edges = 6
|
||||
23
world/generation/chunks/definitions/chunk_0009.tres
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=4 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_0009.glb" id="2_scene"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0000.glb" id="3_base_grass"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0009"
|
||||
label = "second-tier grass top"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0009"
|
||||
base_layer_scene = ExtResource("3_base_grass")
|
||||
base_layer_mesh_name = &"chunk_0000"
|
||||
overlay_only = true
|
||||
selection_weight = 0.22
|
||||
maximum_placements = 8
|
||||
tags = PackedStringArray("land", "walkable", "grass", "elevation_2", "elevation_2_top")
|
||||
allowed_neighbor_tags = PackedStringArray("elevation_2")
|
||||
required_neighbor_tags = PackedStringArray("elevation_2")
|
||||
minimum_required_neighbors = 4
|
||||
preferred_neighbor_tags = PackedStringArray("elevation_2_top")
|
||||
must_be_interior = true
|
||||
27
world/generation/chunks/definitions/chunk_0010.tres
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=4 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_0010.glb" id="2_scene"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0000.glb" id="3_base_grass"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0010"
|
||||
label = "second-tier grass corner"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0010"
|
||||
base_layer_scene = ExtResource("3_base_grass")
|
||||
base_layer_mesh_name = &"chunk_0000"
|
||||
overlay_only = true
|
||||
selection_weight = 0.12
|
||||
maximum_placements = 16
|
||||
tags = PackedStringArray("land", "walkable", "grass", "cliff", "corner", "elevation_2", "elevation_2_border")
|
||||
allowed_neighbor_tags = PackedStringArray("grass", "elevation_2")
|
||||
north_allowed_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
east_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
south_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
west_allowed_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
required_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
minimum_required_neighbors = 2
|
||||
preferred_neighbor_tags = PackedStringArray("elevation_2")
|
||||
must_be_interior = true
|
||||
27
world/generation/chunks/definitions/chunk_0011.tres
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=4 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_0011.glb" id="2_scene"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0000.glb" id="3_base_grass"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0011"
|
||||
label = "second-tier grass edge"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0011"
|
||||
base_layer_scene = ExtResource("3_base_grass")
|
||||
base_layer_mesh_name = &"chunk_0000"
|
||||
overlay_only = true
|
||||
selection_weight = 0.28
|
||||
maximum_placements = 32
|
||||
tags = PackedStringArray("land", "walkable", "grass", "cliff", "edge", "elevation_2", "elevation_2_border")
|
||||
allowed_neighbor_tags = PackedStringArray("grass", "elevation_2")
|
||||
north_allowed_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
east_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
south_allowed_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
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")
|
||||
must_be_interior = true
|
||||
27
world/generation/chunks/definitions/chunk_0012.tres
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkDefinition" load_steps=4 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_0012.glb" id="2_scene"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/chunks/assets/chunk_0000.glb" id="3_base_grass"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0012"
|
||||
label = "second-tier grass ramp"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0012"
|
||||
base_layer_scene = ExtResource("3_base_grass")
|
||||
base_layer_mesh_name = &"chunk_0000"
|
||||
overlay_only = true
|
||||
selection_weight = 0.12
|
||||
maximum_placements = 8
|
||||
tags = PackedStringArray("land", "walkable", "grass", "cliff", "edge", "ramp", "elevation_2", "elevation_2_border")
|
||||
allowed_neighbor_tags = PackedStringArray("grass", "elevation_2")
|
||||
north_allowed_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
east_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
south_allowed_neighbor_tags = PackedStringArray("elevation_2_border")
|
||||
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")
|
||||
must_be_interior = true
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=12 format=3]
|
||||
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=17 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"]
|
||||
|
|
@ -11,8 +11,13 @@
|
|||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0005.tres" id="9_grass_ocean_edge"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0006.tres" id="10_grass_beach_transition"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0007.tres" id="11_grass_ocean_corner"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0008.tres" id="12_beach_ocean_corner"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0009.tres" id="13_cliff_top"]
|
||||
[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"]
|
||||
|
||||
[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("8_spawn")])
|
||||
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")])
|
||||
|
|
|
|||
|
|
@ -78,6 +78,11 @@ func _ready() -> void:
|
|||
func generate_world(seed: int) -> bool:
|
||||
if seed <= 0 or seed > PlayerSaveManager.MAX_WORLD_SEED:
|
||||
return false
|
||||
if (
|
||||
seed == _current_seed
|
||||
and _generator.get_generated_chunks_root() != null
|
||||
):
|
||||
return true
|
||||
_current_seed = seed
|
||||
_generator.generation_seed = seed
|
||||
return _generator.generate()
|
||||
|
|
@ -237,29 +242,34 @@ func _on_generation_completed(summary: Dictionary) -> void:
|
|||
_prop_definitions_for(rule, tags),
|
||||
coordinate,
|
||||
)
|
||||
if (
|
||||
definitions.is_empty()
|
||||
or not _prop_group_roll_succeeds(
|
||||
rule,
|
||||
group_key,
|
||||
for _placement_attempt: int in (
|
||||
rule.placement_attempts_per_chunk
|
||||
):
|
||||
if group_counts.get(group_key, 0) >= maximum:
|
||||
break
|
||||
if (
|
||||
definitions.is_empty()
|
||||
or not _prop_group_roll_succeeds(
|
||||
rule,
|
||||
group_key,
|
||||
coordinate,
|
||||
random,
|
||||
)
|
||||
):
|
||||
continue
|
||||
if _maybe_add_prop(
|
||||
definitions,
|
||||
record.get("position", Vector3.ZERO),
|
||||
coordinate,
|
||||
biome.stable_id,
|
||||
random,
|
||||
)
|
||||
):
|
||||
continue
|
||||
if _maybe_add_prop(
|
||||
definitions,
|
||||
record.get("position", Vector3.ZERO),
|
||||
coordinate,
|
||||
biome.stable_id,
|
||||
random,
|
||||
terrain_triangles,
|
||||
):
|
||||
group_counts[group_key] += 1
|
||||
_record_prop_group_coordinate(
|
||||
group_key,
|
||||
coordinate,
|
||||
)
|
||||
terrain_triangles,
|
||||
):
|
||||
group_counts[group_key] += 1
|
||||
_record_prop_group_coordinate(
|
||||
group_key,
|
||||
coordinate,
|
||||
)
|
||||
_ensure_minimum_props(
|
||||
eligible_records,
|
||||
group_counts,
|
||||
|
|
@ -322,7 +332,7 @@ func _configure_static_water() -> void:
|
|||
_ocean.fish_pool = OCEAN_POOL
|
||||
_ocean.location_tags = [&"coast", &"ocean", &"generated_ocean"]
|
||||
_ocean.surface_size = Vector2(10000.0, 10000.0)
|
||||
_ocean.position.y = WATER_HEIGHT - 0.025
|
||||
_ocean.position.y = WATER_HEIGHT
|
||||
_apply_water_materials()
|
||||
|
||||
|
||||
|
|
@ -451,6 +461,11 @@ func _add_prop(
|
|||
) -> bool:
|
||||
if definition == null or definition.packed_scene == null:
|
||||
return false
|
||||
var visual_scale := random.randf_range(
|
||||
definition.minimum_visual_scale,
|
||||
definition.maximum_visual_scale,
|
||||
)
|
||||
var scaled_clearance := definition.clearance_radius * visual_scale
|
||||
var placement := Vector3.ZERO
|
||||
var found_surface := false
|
||||
for attempt: int in PROP_PLACEMENT_ATTEMPTS:
|
||||
|
|
@ -470,7 +485,7 @@ func _add_prop(
|
|||
> WATER_HEIGHT + PROP_MINIMUM_GROUND_CLEARANCE
|
||||
and _has_prop_clearance(
|
||||
placement,
|
||||
definition.clearance_radius,
|
||||
scaled_clearance,
|
||||
)
|
||||
):
|
||||
placement.y = surface_height
|
||||
|
|
@ -492,22 +507,26 @@ func _add_prop(
|
|||
prop.set_meta(&"terrain_prop_group", definition.procedural_group)
|
||||
prop.set_meta(&"terrain_chunk_coordinate", chunk_coordinate)
|
||||
prop.set_meta(&"terrain_biome_id", biome_id)
|
||||
prop.set_meta(&"terrain_prop_visual_scale", visual_scale)
|
||||
prop.position = placement
|
||||
prop.rotation.y = random.randf_range(-PI, PI)
|
||||
_decorations.add_child(prop)
|
||||
visual_root.name = "Visual"
|
||||
prop.add_child(visual_root)
|
||||
visual_root.position = definition.visual_offset
|
||||
visual_root.scale = Vector3.ONE * visual_scale
|
||||
_configure_prop_visuals(visual_root)
|
||||
_add_prop_collision(prop, definition)
|
||||
_apply_prop_material_variant(visual_root, definition, random)
|
||||
_add_prop_collision(prop, definition, visual_scale)
|
||||
_placed_prop_positions.append(placement)
|
||||
_placed_prop_clearance_radii.append(definition.clearance_radius)
|
||||
_placed_prop_clearance_radii.append(scaled_clearance)
|
||||
_placed_prop_groups.append(definition.procedural_group)
|
||||
if definition.gatherable_anchor_height > 0.0:
|
||||
var anchor := Marker3D.new()
|
||||
anchor.name = "TreeAnchor_%d" % _tree_anchors.get_child_count()
|
||||
anchor.position = prop.position + Vector3(
|
||||
0.0,
|
||||
definition.gatherable_anchor_height,
|
||||
definition.gatherable_anchor_height * visual_scale,
|
||||
0.0,
|
||||
)
|
||||
_tree_anchors.add_child(anchor)
|
||||
|
|
@ -624,7 +643,11 @@ func _prop_group_maximum(
|
|||
if eligible_count <= 0:
|
||||
return 0
|
||||
var maximum := ceili(
|
||||
float(eligible_count * rule.maximum_density)
|
||||
float(
|
||||
eligible_count
|
||||
* rule.placement_attempts_per_chunk
|
||||
* rule.maximum_density
|
||||
)
|
||||
/ float(PROP_CHANCE_SCALE)
|
||||
)
|
||||
return maxi(maximum, rule.minimum_placements)
|
||||
|
|
@ -777,6 +800,51 @@ func _configure_prop_visuals(root_node: Node) -> void:
|
|||
_configure_prop_visuals(child)
|
||||
|
||||
|
||||
func _apply_prop_material_variant(
|
||||
root_node: Node,
|
||||
definition: TerrainPropDefinition,
|
||||
random: RandomNumberGenerator,
|
||||
) -> void:
|
||||
if (
|
||||
definition.material_variants.is_empty()
|
||||
or definition.variant_material_slot_names.is_empty()
|
||||
):
|
||||
return
|
||||
var variant := definition.material_variants[
|
||||
random.randi_range(0, definition.material_variants.size() - 1)
|
||||
]
|
||||
_apply_material_variant_to_meshes(
|
||||
root_node,
|
||||
definition.variant_material_slot_names,
|
||||
variant,
|
||||
)
|
||||
|
||||
|
||||
func _apply_material_variant_to_meshes(
|
||||
root_node: Node,
|
||||
target_material_names: PackedStringArray,
|
||||
variant: Material,
|
||||
) -> void:
|
||||
var mesh_instance := root_node as MeshInstance3D
|
||||
if mesh_instance != null and mesh_instance.mesh != null:
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var active_material := mesh_instance.get_active_material(surface_index)
|
||||
if (
|
||||
active_material != null
|
||||
and active_material.resource_name in target_material_names
|
||||
):
|
||||
mesh_instance.set_surface_override_material(
|
||||
surface_index,
|
||||
variant,
|
||||
)
|
||||
for child: Node in root_node.get_children():
|
||||
_apply_material_variant_to_meshes(
|
||||
child,
|
||||
target_material_names,
|
||||
variant,
|
||||
)
|
||||
|
||||
|
||||
func _terrain_surface_triangles() -> Array[PackedVector3Array]:
|
||||
var triangles: Array[PackedVector3Array] = []
|
||||
for mesh_instance: MeshInstance3D in _generator.get_primary_terrain_meshes():
|
||||
|
|
@ -818,6 +886,7 @@ func _surface_height_at(
|
|||
func _add_prop_collision(
|
||||
prop: Node3D,
|
||||
definition: TerrainPropDefinition,
|
||||
visual_scale: float,
|
||||
) -> void:
|
||||
if not definition.has_collision():
|
||||
return
|
||||
|
|
@ -830,16 +899,16 @@ func _add_prop_collision(
|
|||
collision.name = "CollisionShape"
|
||||
if definition.has_box_collision():
|
||||
var box := BoxShape3D.new()
|
||||
box.size = definition.collision_box_size
|
||||
box.size = definition.collision_box_size * visual_scale
|
||||
collision.shape = box
|
||||
collision.position = definition.collision_offset
|
||||
collision.position = definition.collision_offset * visual_scale
|
||||
else:
|
||||
var cylinder := CylinderShape3D.new()
|
||||
cylinder.radius = definition.collision_radius
|
||||
cylinder.height = definition.collision_height
|
||||
cylinder.radius = definition.collision_radius * visual_scale
|
||||
cylinder.height = definition.collision_height * visual_scale
|
||||
collision.shape = cylinder
|
||||
collision.position = (
|
||||
definition.collision_offset
|
||||
definition.collision_offset * visual_scale
|
||||
+ Vector3.UP * cylinder.height * 0.5
|
||||
)
|
||||
body.add_child(collision)
|
||||
|
|
|
|||
|
|
@ -31,12 +31,13 @@ gatherable_anchor_root = NodePath("GatherableAnchors")
|
|||
unique_name_in_owner = true
|
||||
script = ExtResource("2_generator")
|
||||
catalog = ExtResource("3_catalog")
|
||||
grid_size = Vector2i(7, 7)
|
||||
grid_size = Vector2i(15, 13)
|
||||
generation_seed = 13001
|
||||
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")
|
||||
required_chunk_ids = PackedStringArray("chunk_spawn", "chunk_0001", "chunk_0002", "chunk_0003", "chunk_0004", "chunk_0005", "chunk_0006", "chunk_0007", "chunk_0008")
|
||||
elevated_cliff_feature_enabled = true
|
||||
required_chunk_weight_multiplier = 64.0
|
||||
maximum_backtracks = 100000
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ script = ExtResource("8_diggable")
|
|||
area_id = &"starter_beach"
|
||||
terrain_source = NodePath("../../Terrain/TerrainChunkGenerator")
|
||||
surface_materials = Array[StringName]([&"sand"])
|
||||
generation_bounds = Rect2(-35, -35, 70, 70)
|
||||
generation_bounds = Rect2(-55, -45, 110, 90)
|
||||
minimum_global_y = -0.24
|
||||
maximum_global_y = 0.5
|
||||
minimum_up_dot = 0.72
|
||||
|
|
|
|||
|
|
@ -16,3 +16,7 @@ preferred_nearby_prop_groups = PackedStringArray("grass_tree")
|
|||
preferred_nearby_radius = 12.0
|
||||
nearby_preference_weight_multiplier = 3.0
|
||||
clearance_radius = 0.8
|
||||
visual_offset = Vector3(0, -0.08, 0)
|
||||
collision_radius = 0.65
|
||||
collision_height = 1.0
|
||||
collision_offset = Vector3(0, -0.08, 0)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ required_chunk_tags = PackedStringArray("grass")
|
|||
selection_weight = 0.7
|
||||
minimum_spawn_chunk_distance = 2
|
||||
clearance_radius = 1.55
|
||||
minimum_visual_scale = 0.75
|
||||
maximum_visual_scale = 1.22
|
||||
collision_radius = 0.5
|
||||
collision_height = 4.0
|
||||
gatherable_anchor_height = 2.15
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
[gd_resource type="Resource" script_class="TerrainPropDefinition" load_steps=3 format=3]
|
||||
[gd_resource type="Resource" script_class="TerrainPropDefinition" load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_prop_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/props/assets/prop_tree_1.glb" id="2_scene"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_light.tres" id="3_leaf_light"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_mid.tres" id="4_leaf_mid"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_dark.tres" id="5_leaf_dark"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
|
|
@ -12,6 +15,10 @@ procedural_group = &"grass_tree"
|
|||
required_chunk_tags = PackedStringArray("grass")
|
||||
minimum_spawn_chunk_distance = 2
|
||||
clearance_radius = 1.3
|
||||
minimum_visual_scale = 0.75
|
||||
maximum_visual_scale = 1.15
|
||||
variant_material_slot_names = PackedStringArray("leaf_light", "leaf", "leaf_dark")
|
||||
material_variants = Array[Material]([ExtResource("3_leaf_light"), ExtResource("4_leaf_mid"), ExtResource("5_leaf_dark")])
|
||||
collision_radius = 0.4
|
||||
collision_height = 3.2
|
||||
gatherable_anchor_height = 2.15
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
[gd_resource type="Resource" script_class="TerrainPropDefinition" load_steps=3 format=3]
|
||||
[gd_resource type="Resource" script_class="TerrainPropDefinition" load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_prop_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/props/assets/prop_tree_2.glb" id="2_scene"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_light.tres" id="3_leaf_light"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_mid.tres" id="4_leaf_mid"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_dark.tres" id="5_leaf_dark"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
|
|
@ -12,6 +15,10 @@ procedural_group = &"grass_tree"
|
|||
required_chunk_tags = PackedStringArray("grass")
|
||||
minimum_spawn_chunk_distance = 2
|
||||
clearance_radius = 1.5
|
||||
minimum_visual_scale = 0.8
|
||||
maximum_visual_scale = 1.18
|
||||
variant_material_slot_names = PackedStringArray("leaf_light", "leaf", "leaf_dark")
|
||||
material_variants = Array[Material]([ExtResource("3_leaf_light"), ExtResource("4_leaf_mid"), ExtResource("5_leaf_dark")])
|
||||
collision_radius = 0.5
|
||||
collision_height = 3.8
|
||||
gatherable_anchor_height = 2.15
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
[gd_resource type="Resource" script_class="TerrainPropDefinition" load_steps=3 format=3]
|
||||
[gd_resource type="Resource" script_class="TerrainPropDefinition" load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://world/generation/terrain_prop_definition.gd" id="1_definition"]
|
||||
[ext_resource type="PackedScene" path="res://world/generation/props/assets/prop_tree_3.glb" id="2_scene"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_light.tres" id="3_leaf_light"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_mid.tres" id="4_leaf_mid"]
|
||||
[ext_resource type="Material" path="res://world/generation/props/materials/leaf_dark.tres" id="5_leaf_dark"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
|
|
@ -12,6 +15,10 @@ procedural_group = &"grass_tree"
|
|||
required_chunk_tags = PackedStringArray("grass")
|
||||
minimum_spawn_chunk_distance = 2
|
||||
clearance_radius = 1.8
|
||||
minimum_visual_scale = 0.85
|
||||
maximum_visual_scale = 1.15
|
||||
variant_material_slot_names = PackedStringArray("leaf_light", "leaf", "leaf_dark")
|
||||
material_variants = Array[Material]([ExtResource("3_leaf_light"), ExtResource("4_leaf_mid"), ExtResource("5_leaf_dark")])
|
||||
collision_radius = 0.55
|
||||
collision_height = 4.2
|
||||
gatherable_anchor_height = 2.15
|
||||
|
|
|
|||
9
world/generation/props/materials/leaf_dark.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[gd_resource type="StandardMaterial3D" format=3]
|
||||
|
||||
[resource]
|
||||
resource_name = "leaf_variant_dark"
|
||||
albedo_color = Color(0.1712, 0.252, 0, 1)
|
||||
metallic = 0.0
|
||||
roughness = 1.0
|
||||
shading_mode = 1
|
||||
texture_filter = 0
|
||||
9
world/generation/props/materials/leaf_light.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[gd_resource type="StandardMaterial3D" format=3]
|
||||
|
||||
[resource]
|
||||
resource_name = "leaf_variant_light"
|
||||
albedo_color = Color(0.3388, 0.7187, 0, 1)
|
||||
metallic = 0.0
|
||||
roughness = 1.0
|
||||
shading_mode = 1
|
||||
texture_filter = 0
|
||||
9
world/generation/props/materials/leaf_mid.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[gd_resource type="StandardMaterial3D" format=3]
|
||||
|
||||
[resource]
|
||||
resource_name = "leaf_variant_mid"
|
||||
albedo_color = Color(0.3147, 0.4631, 0, 1)
|
||||
metallic = 0.0
|
||||
roughness = 1.0
|
||||
shading_mode = 1
|
||||
texture_filter = 0
|
||||
|
|
@ -6,6 +6,9 @@ extends Resource
|
|||
@export_range(0, 10000, 1) var adjacency_bonus := 0
|
||||
@export_range(0, 10000, 1) var maximum_density := 10000
|
||||
@export_range(0, 64, 1) var minimum_placements := 0
|
||||
## More than one attempt permits intentionally dense biomes without changing
|
||||
## the density of every biome that uses the same procedural prop group.
|
||||
@export_range(1, 8, 1) var placement_attempts_per_chunk := 1
|
||||
## Empty permits every compatible prop in the procedural group.
|
||||
@export var allowed_prop_ids := PackedStringArray()
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,29 @@ static func create_variants(
|
|||
chunk_size,
|
||||
edge_epsilon,
|
||||
)
|
||||
if definition.base_layer_scene != null:
|
||||
var base_layer_root := definition.base_layer_scene.instantiate()
|
||||
var base_layer_mesh := find_primary_mesh(
|
||||
base_layer_root,
|
||||
definition.base_layer_mesh_name,
|
||||
)
|
||||
if base_layer_mesh == null or base_layer_mesh.mesh == null:
|
||||
push_error(
|
||||
"Terrain chunk %s has no base-layer MeshInstance3D named %s."
|
||||
% [definition.stable_id, definition.base_layer_mesh_name]
|
||||
)
|
||||
base_layer_root.free()
|
||||
chunk_root.free()
|
||||
return variants
|
||||
boundary_points.append_array(
|
||||
_collect_boundary_points(
|
||||
base_layer_mesh.mesh,
|
||||
_transform_relative_to(base_layer_mesh, base_layer_root),
|
||||
chunk_size,
|
||||
edge_epsilon,
|
||||
)
|
||||
)
|
||||
base_layer_root.free()
|
||||
for quarter_turns: int in 4:
|
||||
if not definition.allows_quarter_turn(quarter_turns):
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -30,6 +30,19 @@ func validation_errors() -> PackedStringArray:
|
|||
errors.append("%s has no PackedScene." % definition.stable_id)
|
||||
if definition.primary_mesh_name == &"":
|
||||
errors.append("%s has no primary mesh name." % definition.stable_id)
|
||||
if (
|
||||
(definition.base_layer_scene == null)
|
||||
!= (definition.base_layer_mesh_name == &"")
|
||||
):
|
||||
errors.append(
|
||||
"%s must define both base-layer scene and mesh name."
|
||||
% definition.stable_id
|
||||
)
|
||||
if definition.overlay_only and definition.base_layer_scene == null:
|
||||
errors.append(
|
||||
"%s is overlay-only but has no base-layer scene."
|
||||
% definition.stable_id
|
||||
)
|
||||
if definition.allowed_rotation_mask == 0:
|
||||
errors.append("%s allows no rotations." % definition.stable_id)
|
||||
var has_coast := "coast" in definition.tags
|
||||
|
|
@ -44,6 +57,11 @@ func validation_errors() -> PackedStringArray:
|
|||
"%s has an ocean-facing edge without the coast tag."
|
||||
% definition.stable_id
|
||||
)
|
||||
if definition.must_be_interior and definition.prefers_map_boundary:
|
||||
errors.append(
|
||||
"%s cannot require the interior and prefer the boundary."
|
||||
% definition.stable_id
|
||||
)
|
||||
var has_fresh_water := "fresh_water" in definition.tags
|
||||
var has_water_footprint := (
|
||||
definition.water_surface_size.x > 0.0
|
||||
|
|
|
|||
|
|
@ -5,6 +5,17 @@ extends Resource
|
|||
@export var label := ""
|
||||
@export var packed_scene: PackedScene
|
||||
@export var primary_mesh_name: StringName
|
||||
## Optional existing level-one chunk placed beneath an authored elevated
|
||||
## overlay. This lets cliff edges leave their downhill side open while the
|
||||
## ordinary base terrain remains present in the same generated cell.
|
||||
@export_group("Layered Terrain")
|
||||
@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.
|
||||
@export var overlay_only := false
|
||||
@export_group("")
|
||||
@export_flags("0 degrees", "90 degrees", "180 degrees", "270 degrees")
|
||||
var allowed_rotation_mask := 15
|
||||
@export_range(0.01, 100.0, 0.01) var selection_weight := 1.0
|
||||
|
|
@ -36,6 +47,9 @@ var allowed_rotation_mask := 15
|
|||
## weight. This shapes regions without turning a visual preference into a hard
|
||||
## generation constraint.
|
||||
@export var preferred_neighbor_tags := PackedStringArray()
|
||||
## Interior-only pieces may not occupy any outer grid cell. Coastal elevated
|
||||
## pieces use separate authored definitions rather than weakening this rule.
|
||||
@export var must_be_interior := false
|
||||
## Coastal transition pieces should normally migrate toward the generated
|
||||
## region's perimeter while remaining legal in the interior.
|
||||
@export var prefers_map_boundary := false
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ signal generation_completed(summary: Dictionary)
|
|||
|
||||
const CONSTRAINT_PROFILE_QUANTIZATION := 0.001
|
||||
const CANDIDATE_WEIGHT_SCALE := 10000.0
|
||||
const LARGE_GRID_LIGHTWEIGHT_THRESHOLD := 128
|
||||
const LARGE_GRID_PROPAGATION_INTERVAL := 8
|
||||
|
||||
@export var catalog: TerrainChunkCatalog
|
||||
@export var grid_size := Vector2i(7, 7)
|
||||
|
|
@ -14,6 +16,15 @@ const CANDIDATE_WEIGHT_SCALE := 10000.0
|
|||
@export var show_chunk_labels := false
|
||||
@export var force_center_chunk_id: StringName = &"chunk_0000"
|
||||
@export var required_chunk_ids := PackedStringArray()
|
||||
@export_group("Elevated Inland Feature")
|
||||
@export var elevated_cliff_feature_enabled := false
|
||||
@export var elevated_cliff_base_chunk_id: StringName = &"chunk_0000"
|
||||
@export var elevated_cliff_top_chunk_id: StringName = &"chunk_0009"
|
||||
@export var elevated_cliff_corner_chunk_id: StringName = &"chunk_0010"
|
||||
@export var elevated_cliff_edge_chunk_id: StringName = &"chunk_0011"
|
||||
@export var elevated_cliff_ramp_chunk_id: StringName = &"chunk_0012"
|
||||
@export_range(0.0, 1.0, 0.05) var elevated_cliff_ramp_chance := 0.75
|
||||
@export_group("")
|
||||
@export_range(1.0, 100.0, 1.0) var required_chunk_weight_multiplier := 64.0
|
||||
@export_range(1.0, 4.0, 0.05) var preferred_neighbor_weight_multiplier := 1.6
|
||||
@export_range(0.01, 1.0, 0.01) var long_repeat_weight_multiplier := 0.3
|
||||
|
|
@ -33,6 +44,8 @@ var _placements: Array[TerrainChunkVariant] = []
|
|||
var _random := RandomNumberGenerator.new()
|
||||
var _generated_chunks: Node3D
|
||||
var _backtrack_count := 0
|
||||
var _elevated_feature_center := Vector2i(-1, -1)
|
||||
var _elevated_feature_reserved_grass_indices: Dictionary[int, bool] = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -49,6 +62,9 @@ func generate() -> bool:
|
|||
_backtrack_count = 0
|
||||
_placements.clear()
|
||||
_placements.resize(grid_size.x * grid_size.y)
|
||||
if not _prepare_elevated_feature_region():
|
||||
_placements.assign(previous_placements)
|
||||
return false
|
||||
if not _solve_cell(0):
|
||||
_placements.assign(previous_placements)
|
||||
push_error(
|
||||
|
|
@ -57,6 +73,9 @@ func generate() -> bool:
|
|||
)
|
||||
return false
|
||||
_resolve_equivalent_rotations()
|
||||
if not _apply_elevated_feature():
|
||||
_placements.assign(previous_placements)
|
||||
return false
|
||||
var solution_root := _build_solution_root()
|
||||
if solution_root == null:
|
||||
_placements.assign(previous_placements)
|
||||
|
|
@ -195,6 +214,8 @@ func _prepare_catalog() -> bool:
|
|||
return false
|
||||
for variant: TerrainChunkVariant in analyzed:
|
||||
_variants.append(variant)
|
||||
if definition.overlay_only:
|
||||
continue
|
||||
var constraint_key := _constraint_key(variant)
|
||||
var rotations: PackedInt32Array = _equivalent_rotations.get(
|
||||
constraint_key,
|
||||
|
|
@ -207,6 +228,9 @@ func _prepare_catalog() -> bool:
|
|||
if _variants.is_empty():
|
||||
push_error("Terrain chunk catalog produced no usable variants.")
|
||||
return false
|
||||
if _solver_variants.is_empty():
|
||||
push_error("Terrain chunk catalog produced no base-terrain solver variants.")
|
||||
return false
|
||||
for index: int in _solver_variants.size():
|
||||
_solver_variant_indices[_solver_variants[index]] = index
|
||||
_build_edge_compatibility_cache()
|
||||
|
|
@ -303,7 +327,7 @@ func _solve_cell(placed_count: int) -> bool:
|
|||
return false
|
||||
if not _requirements_can_still_be_satisfied():
|
||||
return false
|
||||
var next_cell := _select_next_cell()
|
||||
var next_cell := _select_next_cell(placed_count)
|
||||
var index := int(next_cell.get("index", -1))
|
||||
if index < 0:
|
||||
return false
|
||||
|
|
@ -325,7 +349,12 @@ func _solve_cell(placed_count: int) -> bool:
|
|||
return false
|
||||
|
||||
|
||||
func _select_next_cell() -> Dictionary:
|
||||
func _select_next_cell(placed_count: int) -> Dictionary:
|
||||
if (
|
||||
_placements.size() >= LARGE_GRID_LIGHTWEIGHT_THRESHOLD
|
||||
and placed_count % LARGE_GRID_PROPAGATION_INTERVAL != 0
|
||||
):
|
||||
return _select_next_large_grid_cell()
|
||||
# Rebuild and propagate the small domain table on every branch. This catches
|
||||
# unsupported connector chains before they turn into a deep recursive dead
|
||||
# end, while keeping placement state simple and deterministic.
|
||||
|
|
@ -356,6 +385,54 @@ func _select_next_cell() -> Dictionary:
|
|||
return {"index": best_index, "candidates": best_candidates}
|
||||
|
||||
|
||||
func _select_next_large_grid_cell() -> Dictionary:
|
||||
# Full all-cell arc propagation scales cubically as the map grows. Large
|
||||
# worlds instead use the same compatibility checks with a frontier-aware MRV
|
||||
# pass. Exact placement validation and backtracking remain unchanged.
|
||||
var best_index := -1
|
||||
var best_candidates: Array[TerrainChunkVariant] = []
|
||||
var best_placed_neighbors := -1
|
||||
for index: int in _placements.size():
|
||||
if _placements[index] != null:
|
||||
continue
|
||||
var candidates := _compatible_candidates(index)
|
||||
if candidates.is_empty():
|
||||
return {"index": index, "candidates": candidates}
|
||||
var placed_neighbors := _placed_neighbor_count(index)
|
||||
if (
|
||||
best_index < 0
|
||||
or candidates.size() < best_candidates.size()
|
||||
or (
|
||||
candidates.size() == best_candidates.size()
|
||||
and placed_neighbors > best_placed_neighbors
|
||||
)
|
||||
):
|
||||
best_index = index
|
||||
best_candidates = candidates
|
||||
best_placed_neighbors = placed_neighbors
|
||||
return {"index": best_index, "candidates": best_candidates}
|
||||
|
||||
|
||||
func _placed_neighbor_count(index: int) -> int:
|
||||
var coordinate := Vector2i(index % grid_size.x, index / grid_size.x)
|
||||
var count := 0
|
||||
for edge_value: int in TerrainChunkTopology.Edge.values():
|
||||
var neighbor_coordinate := (
|
||||
coordinate
|
||||
+ TerrainChunkTopology.grid_offset(
|
||||
edge_value as TerrainChunkTopology.Edge
|
||||
)
|
||||
)
|
||||
if not _coordinate_is_inside_grid(neighbor_coordinate):
|
||||
continue
|
||||
var neighbor_index := (
|
||||
neighbor_coordinate.y * grid_size.x + neighbor_coordinate.x
|
||||
)
|
||||
if _placements[neighbor_index] != null:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
func _propagate_domains(domains: Dictionary) -> bool:
|
||||
var changed := true
|
||||
while changed:
|
||||
|
|
@ -568,6 +645,11 @@ func _candidate_can_occupy_cell(
|
|||
if not _definition_has_capacity(candidate.definition):
|
||||
return false
|
||||
var coordinate := Vector2i(index % grid_size.x, index / grid_size.x)
|
||||
if (
|
||||
_elevated_feature_reserved_grass_indices.has(index)
|
||||
and candidate.definition.stable_id != elevated_cliff_base_chunk_id
|
||||
):
|
||||
return false
|
||||
var center := Vector2i(grid_size.x / 2, grid_size.y / 2)
|
||||
var center_index := center.y * grid_size.x + center.x
|
||||
if (
|
||||
|
|
@ -599,10 +681,123 @@ func _candidate_can_occupy_cell(
|
|||
)
|
||||
|
||||
|
||||
func _prepare_elevated_feature_region() -> bool:
|
||||
_elevated_feature_center = Vector2i(-1, -1)
|
||||
_elevated_feature_reserved_grass_indices.clear()
|
||||
if not elevated_cliff_feature_enabled:
|
||||
return true
|
||||
for stable_id: StringName in [
|
||||
elevated_cliff_base_chunk_id,
|
||||
elevated_cliff_top_chunk_id,
|
||||
elevated_cliff_corner_chunk_id,
|
||||
elevated_cliff_edge_chunk_id,
|
||||
elevated_cliff_ramp_chunk_id,
|
||||
]:
|
||||
if catalog.definition_for_id(stable_id) == null:
|
||||
push_error("Elevated cliff feature references missing chunk %s." % stable_id)
|
||||
return false
|
||||
if grid_size.x < 11 or grid_size.y < 9:
|
||||
push_error(
|
||||
"Elevated cliff feature requires at least an 11x9 terrain grid."
|
||||
)
|
||||
return false
|
||||
var forced_center := Vector2i(grid_size.x / 2, grid_size.y / 2)
|
||||
var candidates: Array[Vector2i] = []
|
||||
for row: int in range(2, grid_size.y - 2):
|
||||
for column: int in range(2, grid_size.x - 2):
|
||||
var candidate := Vector2i(column, row)
|
||||
if (
|
||||
absi(candidate.x - forced_center.x) <= 2
|
||||
and absi(candidate.y - forced_center.y) <= 2
|
||||
):
|
||||
continue
|
||||
candidates.append(candidate)
|
||||
if candidates.is_empty():
|
||||
push_error("Terrain grid has no inland 3x3 cliff feature location.")
|
||||
return false
|
||||
var feature_random := RandomNumberGenerator.new()
|
||||
feature_random.seed = generation_seed ^ 0x2E1E7A7ED
|
||||
_elevated_feature_center = candidates[
|
||||
feature_random.randi_range(0, candidates.size() - 1)
|
||||
]
|
||||
# Solve the authored 3x3 cliff assembly into a one-cell ring of ordinary
|
||||
# level-one flat grass. Every raised piece still occupies one of the inner
|
||||
# nine grass cells; the outer ring prevents a stream, pond, or coast profile
|
||||
# from being exposed directly against its downhill edge.
|
||||
for row_offset: int in range(-2, 3):
|
||||
for column_offset: int in range(-2, 3):
|
||||
var coordinate := (
|
||||
_elevated_feature_center
|
||||
+ Vector2i(column_offset, row_offset)
|
||||
)
|
||||
_elevated_feature_reserved_grass_indices[
|
||||
coordinate.y * grid_size.x + coordinate.x
|
||||
] = true
|
||||
return true
|
||||
|
||||
|
||||
func _apply_elevated_feature() -> bool:
|
||||
if not elevated_cliff_feature_enabled:
|
||||
return true
|
||||
if _elevated_feature_center.x < 0 or _elevated_feature_center.y < 0:
|
||||
push_error("Elevated cliff feature has no reserved base region.")
|
||||
return false
|
||||
var edge_specs: Array[Dictionary] = [
|
||||
{"offset": Vector2i(0, -1), "turns": 1},
|
||||
{"offset": Vector2i(1, 0), "turns": 0},
|
||||
{"offset": Vector2i(0, 1), "turns": 3},
|
||||
{"offset": Vector2i(-1, 0), "turns": 2},
|
||||
]
|
||||
var feature_random := RandomNumberGenerator.new()
|
||||
feature_random.seed = generation_seed ^ 0x51A7C11FF
|
||||
var ramp_edge := -1
|
||||
if feature_random.randf() < elevated_cliff_ramp_chance:
|
||||
ramp_edge = feature_random.randi_range(0, edge_specs.size() - 1)
|
||||
var placements: Array[Dictionary] = [
|
||||
{"offset": Vector2i(-1, -1), "id": elevated_cliff_corner_chunk_id, "turns": 2},
|
||||
{"offset": Vector2i(1, -1), "id": elevated_cliff_corner_chunk_id, "turns": 1},
|
||||
{"offset": Vector2i(0, 0), "id": elevated_cliff_top_chunk_id, "turns": 0},
|
||||
{"offset": Vector2i(-1, 1), "id": elevated_cliff_corner_chunk_id, "turns": 3},
|
||||
{"offset": Vector2i(1, 1), "id": elevated_cliff_corner_chunk_id, "turns": 0},
|
||||
]
|
||||
for edge_index: int in edge_specs.size():
|
||||
var edge_spec := edge_specs[edge_index]
|
||||
placements.append({
|
||||
"offset": edge_spec["offset"],
|
||||
"id": (
|
||||
elevated_cliff_ramp_chunk_id
|
||||
if edge_index == ramp_edge
|
||||
else elevated_cliff_edge_chunk_id
|
||||
),
|
||||
"turns": edge_spec["turns"],
|
||||
})
|
||||
for spec: Dictionary in placements:
|
||||
var definition := catalog.definition_for_id(spec["id"] as StringName)
|
||||
var variant := _authored_variant(definition, int(spec["turns"]))
|
||||
if variant == null:
|
||||
push_error(
|
||||
"Elevated cliff feature cannot resolve %s rotation %d."
|
||||
% [spec["id"], spec["turns"]]
|
||||
)
|
||||
return false
|
||||
var coordinate := _elevated_feature_center + (spec["offset"] as Vector2i)
|
||||
_placements[coordinate.y * grid_size.x + coordinate.x] = variant
|
||||
var validation_error := _resolved_layout_validation_error(_placements)
|
||||
if not validation_error.is_empty():
|
||||
push_error("Elevated cliff feature is invalid: " + validation_error)
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _variant_respects_ocean_boundary(
|
||||
variant: TerrainChunkVariant,
|
||||
coordinate: Vector2i,
|
||||
) -> bool:
|
||||
if (
|
||||
variant.definition.must_be_interior
|
||||
and _coordinate_is_on_boundary(coordinate)
|
||||
):
|
||||
return false
|
||||
var ocean_edges := variant.rotated_edge_mask(
|
||||
variant.definition.ocean_facing_edges
|
||||
)
|
||||
|
|
@ -1231,7 +1426,7 @@ func _build_solution_root() -> Node3D:
|
|||
if variant == null:
|
||||
continue
|
||||
var coordinate := Vector2i(index % grid_size.x, index / grid_size.x)
|
||||
var chunk_root := variant.definition.packed_scene.instantiate() as Node3D
|
||||
var chunk_root := _instantiate_chunk_root(variant.definition)
|
||||
if chunk_root == null:
|
||||
push_error("%s does not instantiate as Node3D." % variant.stable_key())
|
||||
solution_root.free()
|
||||
|
|
@ -1260,6 +1455,27 @@ func _build_solution_root() -> Node3D:
|
|||
return solution_root
|
||||
|
||||
|
||||
func _instantiate_chunk_root(
|
||||
definition: TerrainChunkDefinition,
|
||||
) -> Node3D:
|
||||
var terrain_visual := definition.packed_scene.instantiate() as Node3D
|
||||
if terrain_visual == null:
|
||||
return null
|
||||
if definition.base_layer_scene == null:
|
||||
return terrain_visual
|
||||
var base_layer_visual := definition.base_layer_scene.instantiate() as Node3D
|
||||
if base_layer_visual == null:
|
||||
terrain_visual.free()
|
||||
return null
|
||||
var layered_root := Node3D.new()
|
||||
layered_root.name = "LayeredTerrainChunk"
|
||||
base_layer_visual.name = "TerrainBaseLayer"
|
||||
terrain_visual.name = "TerrainOverlay"
|
||||
layered_root.add_child(base_layer_visual)
|
||||
layered_root.add_child(terrain_visual)
|
||||
return layered_root
|
||||
|
||||
|
||||
func _replace_generated_chunks(solution_root: Node3D) -> void:
|
||||
_clear_generated_chunks()
|
||||
_generated_chunks = solution_root
|
||||
|
|
@ -1274,11 +1490,34 @@ func _add_collision(
|
|||
chunk_root,
|
||||
definition.primary_mesh_name,
|
||||
)
|
||||
_add_collision_to_mesh(
|
||||
primary_mesh,
|
||||
definition.stable_id,
|
||||
"TerrainCollision",
|
||||
)
|
||||
if definition.base_layer_scene == null:
|
||||
return
|
||||
var base_layer_mesh := TerrainChunkAnalyzer.find_primary_mesh(
|
||||
chunk_root,
|
||||
definition.base_layer_mesh_name,
|
||||
)
|
||||
_add_collision_to_mesh(
|
||||
base_layer_mesh,
|
||||
definition.stable_id,
|
||||
"TerrainBaseLayerCollision",
|
||||
)
|
||||
|
||||
|
||||
func _add_collision_to_mesh(
|
||||
primary_mesh: MeshInstance3D,
|
||||
stable_id: StringName,
|
||||
collision_name: String,
|
||||
) -> void:
|
||||
if primary_mesh == null or primary_mesh.mesh == null:
|
||||
return
|
||||
var terrain_shape := primary_mesh.mesh.create_trimesh_shape()
|
||||
if terrain_shape == null or terrain_shape.get_faces().is_empty():
|
||||
push_warning("%s produced no terrain collision." % definition.stable_id)
|
||||
push_warning("%s produced no terrain collision." % stable_id)
|
||||
return
|
||||
var concave_shape := terrain_shape as ConcavePolygonShape3D
|
||||
if concave_shape != null:
|
||||
|
|
@ -1287,7 +1526,7 @@ func _add_collision(
|
|||
# can never become an invisible collision gap.
|
||||
concave_shape.backface_collision = true
|
||||
var collision_body := StaticBody3D.new()
|
||||
collision_body.name = "TerrainCollision"
|
||||
collision_body.name = collision_name
|
||||
collision_body.collision_layer = 1
|
||||
collision_body.collision_mask = 0
|
||||
primary_mesh.add_child(collision_body)
|
||||
|
|
|
|||
|
|
@ -82,4 +82,23 @@ func validation_errors() -> PackedStringArray:
|
|||
"%s has a negative box collision dimension."
|
||||
% definition.stable_id
|
||||
)
|
||||
if definition.minimum_visual_scale > definition.maximum_visual_scale:
|
||||
errors.append(
|
||||
"%s has an inverted visual scale range."
|
||||
% definition.stable_id
|
||||
)
|
||||
if (
|
||||
not definition.material_variants.is_empty()
|
||||
and definition.variant_material_slot_names.is_empty()
|
||||
):
|
||||
errors.append(
|
||||
"%s has material variants but no target material slots."
|
||||
% definition.stable_id
|
||||
)
|
||||
for material: Material in definition.material_variants:
|
||||
if material == null:
|
||||
errors.append(
|
||||
"%s contains an empty material variant."
|
||||
% definition.stable_id
|
||||
)
|
||||
return errors
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@ extends Resource
|
|||
@export_range(0.0, 100.0, 0.5) var preferred_nearby_radius := 0.0
|
||||
@export_range(1.0, 10.0, 0.1) var nearby_preference_weight_multiplier := 1.0
|
||||
@export_range(0.0, 10.0, 0.05) var clearance_radius := 0.5
|
||||
## Procedural instances use a deterministic uniform scale in this range.
|
||||
@export_range(0.1, 4.0, 0.05) var minimum_visual_scale := 1.0
|
||||
@export_range(0.1, 4.0, 0.05) var maximum_visual_scale := 1.0
|
||||
## Presentation-only offset. The prop root remains exactly terrain-aligned.
|
||||
@export var visual_offset := Vector3.ZERO
|
||||
## A single material is chosen per prop and applied only to matching imported
|
||||
## material slots. This keeps trunks untouched while varying foliage.
|
||||
@export var variant_material_slot_names := PackedStringArray()
|
||||
@export var material_variants: Array[Material] = []
|
||||
@export_range(0.0, 5.0, 0.05) var collision_radius := 0.0
|
||||
@export_range(0.0, 20.0, 0.05) var collision_height := 0.0
|
||||
@export var collision_box_size := Vector3.ZERO
|
||||
|
|
|
|||