Improve generated river terrain
This commit is contained in:
parent
8456093f8c
commit
e6f46dcad9
21 changed files with 586 additions and 18 deletions
|
|
@ -261,9 +261,11 @@ copies of the entire customization catalog.
|
|||
|
||||
### Texture sampling
|
||||
|
||||
NETfishing artwork always uses nearest-neighbor sampling. Do not enable linear,
|
||||
bilinear, trilinear, or anisotropic texture filtering, and do not generate
|
||||
mipmaps. Shader samplers must declare `filter_nearest`. The
|
||||
NETfishing artwork uses nearest-neighbor sampling. Do not enable linear,
|
||||
bilinear, trilinear, or anisotropic texture filtering. Shader samplers must
|
||||
declare `filter_nearest`. The three canonical repeating terrain textures are
|
||||
the sole mipmap exception: their projected shader uses nearest-filtered
|
||||
mipmaps to prevent distance aliasing without smoothing nearby pixels. The
|
||||
`TextureSamplingPolicy` autoload applies nearest sampling to 2D canvas items,
|
||||
Sprite3D presentations, imported 3D materials, and dynamically constructed
|
||||
mesh materials at runtime.
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ const FACIAL_FEATURE_IMPORT_PATH: String = (
|
|||
"art/exported/characters/faces/"
|
||||
)
|
||||
const FACIAL_FEATURE_SIZE_LIMIT: int = 512
|
||||
const PROJECTED_TERRAIN_TEXTURE_IMPORTS: Array[String] = [
|
||||
"res://world/generation/chunks/assets/chunk_0000_grass_lite.png.import",
|
||||
"res://world/generation/chunks/assets/chunk_0001_sand.png.import",
|
||||
"res://world/generation/chunks/assets/chunk_0031_dirt.png.import",
|
||||
]
|
||||
|
||||
var apply_changes: bool = false
|
||||
var verbose: bool = false
|
||||
|
|
@ -153,6 +158,8 @@ func _handle_import_file(import_path: String) -> void:
|
|||
var desired: Variant = section_data[section_key]
|
||||
if section_key == "process/size_limit":
|
||||
desired = _size_limit_for(import_path)
|
||||
elif section_key == "mipmaps/generate":
|
||||
desired = _generates_mipmaps(import_path)
|
||||
var existing: Variant = null
|
||||
if cfg.has_section(section_name) and cfg.has_section_key(section_name, section_key):
|
||||
existing = cfg.get_value(section_name, section_key)
|
||||
|
|
@ -187,6 +194,11 @@ func _size_limit_for(import_path: String) -> int:
|
|||
return int(DEFAULT_PROFILE["params"]["process/size_limit"])
|
||||
|
||||
|
||||
func _generates_mipmaps(import_path: String) -> bool:
|
||||
var normalized_path := import_path.replace("\\", "/")
|
||||
return normalized_path in PROJECTED_TERRAIN_TEXTURE_IMPORTS
|
||||
|
||||
|
||||
func print_summary() -> void:
|
||||
if apply_changes:
|
||||
_print(
|
||||
|
|
|
|||
|
|
@ -247,6 +247,7 @@ func _validate_generated_region(
|
|||
assert(_placement_count(placements, "chunk_0034") == 1)
|
||||
assert(_placement_count(placements, "chunk_0035") == 1)
|
||||
assert(_placement_count(placements, "chunk_0036") == 1)
|
||||
assert(_placement_count(placements, "chunk_0038") == 2)
|
||||
assert(
|
||||
_placement_count(placements, "chunk_0014")
|
||||
== (2 if has_secondary_elevation else 0)
|
||||
|
|
@ -285,10 +286,31 @@ func _validate_generated_region(
|
|||
var river_edge_count := (
|
||||
_placement_count(placements, "chunk_0024")
|
||||
+ _placement_count(placements, "chunk_0025")
|
||||
+ _placement_count(placements, "chunk_0038")
|
||||
)
|
||||
assert(river_coordinate_count >= 12)
|
||||
assert(generator._river_feature_placements.size() == river_coordinate_count)
|
||||
assert(river_edge_count == river_coordinate_count - 8)
|
||||
var stone_bridge_specs: Array[Dictionary] = []
|
||||
for spec: Dictionary in generator._river_feature_placements:
|
||||
if spec.get("id", &"") == &"chunk_0038":
|
||||
stone_bridge_specs.append(spec)
|
||||
assert(stone_bridge_specs.size() == 2)
|
||||
var first_bridge_coordinate := stone_bridge_specs[0].get(
|
||||
"coordinate",
|
||||
Vector2i(-1, -1),
|
||||
) as Vector2i
|
||||
var second_bridge_coordinate := stone_bridge_specs[1].get(
|
||||
"coordinate",
|
||||
Vector2i(-1, -1),
|
||||
) as Vector2i
|
||||
var bridge_delta := second_bridge_coordinate - first_bridge_coordinate
|
||||
assert(absi(bridge_delta.x) + absi(bridge_delta.y) == 1)
|
||||
assert(posmod(
|
||||
int(stone_bridge_specs[1].get("turns", -1))
|
||||
- int(stone_bridge_specs[0].get("turns", -1)),
|
||||
4,
|
||||
) == 2)
|
||||
var river_source_coordinates := generator._river_source_coordinates(
|
||||
generator._river_feature_origin,
|
||||
generator._river_feature_footprint_size,
|
||||
|
|
@ -446,6 +468,20 @@ func _validate_generated_region(
|
|||
assert(fresh.surface_size.x <= 10.0 and fresh.surface_size.y <= 10.0)
|
||||
assert(not fresh.visual_surface_enabled)
|
||||
assert(not (fresh.get_node("VisualWater") as MeshInstance3D).visible)
|
||||
var generated_bed := fresh.get_node_or_null(
|
||||
"GeneratedBed",
|
||||
) as MeshInstance3D
|
||||
assert(generated_bed != null and generated_bed.mesh != null)
|
||||
assert(
|
||||
is_equal_approx(
|
||||
generated_bed.global_position.y,
|
||||
GeneratedWorldRegion.GENERATED_FRESH_WATER_BED_HEIGHT,
|
||||
)
|
||||
)
|
||||
assert(
|
||||
generated_bed.material_override != null
|
||||
and generated_bed.material_override.resource_name == "dirt"
|
||||
)
|
||||
var fresh_recovery := (
|
||||
fresh.get_node("RecoveryRegion") as PlayerWaterTrigger
|
||||
)
|
||||
|
|
@ -486,6 +522,16 @@ func _validate_generated_region(
|
|||
)
|
||||
|
||||
var decorations := region.get_node("Decorations") as Node3D
|
||||
var grass_prop_surface_triangles := region.get_spawn_surface_triangles(
|
||||
[&"grass_lite"],
|
||||
-INF,
|
||||
0.35,
|
||||
)
|
||||
var sand_prop_surface_triangles := region.get_spawn_surface_triangles(
|
||||
[&"sand"],
|
||||
-INF,
|
||||
0.35,
|
||||
)
|
||||
var anchors := region.get_node(
|
||||
"GatherableAnchors/ReachableTreeTrunks"
|
||||
) as GatherableAnchorSet3D
|
||||
|
|
@ -540,6 +586,8 @@ func _validate_generated_region(
|
|||
region,
|
||||
child as Node3D,
|
||||
definition,
|
||||
grass_prop_surface_triangles,
|
||||
sand_prop_surface_triangles,
|
||||
)
|
||||
if prop_id == &"prop_palm":
|
||||
var cluster_id := int(
|
||||
|
|
@ -1063,6 +1111,8 @@ func _validate_decoration_transform(
|
|||
region: GeneratedWorldRegion,
|
||||
prop: Node3D,
|
||||
definition: TerrainPropDefinition,
|
||||
grass_surface_triangles: Array[PackedVector3Array],
|
||||
sand_surface_triangles: Array[PackedVector3Array],
|
||||
) -> void:
|
||||
assert(prop != null)
|
||||
assert(prop.scale.is_equal_approx(Vector3.ONE))
|
||||
|
|
@ -1142,6 +1192,43 @@ func _validate_decoration_transform(
|
|||
],
|
||||
],
|
||||
)
|
||||
var required_surface_triangles: Array[PackedVector3Array] = (
|
||||
sand_surface_triangles
|
||||
if definition.procedural_group == &"sand_tree"
|
||||
else grass_surface_triangles
|
||||
)
|
||||
var required_surface_height := _triangle_surface_height_at(
|
||||
prop.global_position,
|
||||
required_surface_triangles,
|
||||
)
|
||||
assert(
|
||||
required_surface_height > -INF
|
||||
and absf(required_surface_height - prop.global_position.y) <= 0.01,
|
||||
"%s (%s) is not rooted on its required terrain material at %s."
|
||||
% [prop.name, definition.stable_id, prop.global_position],
|
||||
)
|
||||
|
||||
|
||||
func _triangle_surface_height_at(
|
||||
position: Vector3,
|
||||
triangles: Array[PackedVector3Array],
|
||||
) -> float:
|
||||
var highest := -INF
|
||||
var segment_start := position + Vector3.UP * 100.0
|
||||
var segment_end := position + Vector3.DOWN * 100.0
|
||||
for triangle: PackedVector3Array in triangles:
|
||||
if triangle.size() != 3:
|
||||
continue
|
||||
var hit: Variant = Geometry3D.segment_intersects_triangle(
|
||||
segment_start,
|
||||
segment_end,
|
||||
triangle[0],
|
||||
triangle[1],
|
||||
triangle[2],
|
||||
)
|
||||
if hit is Vector3:
|
||||
highest = maxf(highest, (hit as Vector3).y)
|
||||
return highest
|
||||
|
||||
|
||||
func _validate_tree_gatherable_anchors(
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ const EXPECTED_IDS: Array[String] = [
|
|||
"chunk_0035",
|
||||
"chunk_0036",
|
||||
"chunk_0037",
|
||||
"chunk_0038",
|
||||
"chunk_9999",
|
||||
"chunk_9998",
|
||||
"chunk_9997",
|
||||
|
|
@ -98,6 +99,7 @@ const EXPECTED_VARIANT_COUNTS: Dictionary[StringName, int] = {
|
|||
&"chunk_0035": 4,
|
||||
&"chunk_0036": 4,
|
||||
&"chunk_0037": 4,
|
||||
&"chunk_0038": 4,
|
||||
&"chunk_9999": 4,
|
||||
&"chunk_9998": 4,
|
||||
&"chunk_9997": 4,
|
||||
|
|
@ -163,6 +165,7 @@ func _validate_catalog() -> void:
|
|||
var lake_narrow_to_regular := CATALOG.definition_for_id(&"chunk_0023")
|
||||
var river_edge := CATALOG.definition_for_id(&"chunk_0024")
|
||||
var river_edge_variant := CATALOG.definition_for_id(&"chunk_0025")
|
||||
var river_stone_bridge := CATALOG.definition_for_id(&"chunk_0038")
|
||||
var river_source_right := CATALOG.definition_for_id(&"chunk_0029")
|
||||
var river_source_left := CATALOG.definition_for_id(&"chunk_0030")
|
||||
var river_outlet_right := CATALOG.definition_for_id(&"chunk_0031")
|
||||
|
|
@ -208,6 +211,7 @@ func _validate_catalog() -> void:
|
|||
and lake_narrow_to_regular != null
|
||||
and river_edge != null
|
||||
and river_edge_variant != null
|
||||
and river_stone_bridge != null
|
||||
and river_source_right != null
|
||||
and river_source_left != null
|
||||
and river_outlet_right != null
|
||||
|
|
@ -255,6 +259,7 @@ func _validate_catalog() -> void:
|
|||
and lake_narrow_to_regular != null
|
||||
and river_edge != null
|
||||
and river_edge_variant != null
|
||||
and river_stone_bridge != null
|
||||
and river_source_right != null
|
||||
and river_source_left != null
|
||||
and river_outlet_right != null
|
||||
|
|
@ -509,6 +514,8 @@ func _validate_catalog() -> void:
|
|||
(
|
||||
not river_edge.participates_in_base_solver
|
||||
and not river_edge_variant.participates_in_base_solver
|
||||
and not river_stone_bridge.participates_in_base_solver
|
||||
and river_stone_bridge.maximum_placements == 2
|
||||
and not river_source_right.participates_in_base_solver
|
||||
and not river_source_left.participates_in_base_solver
|
||||
and not river_outlet_right.participates_in_base_solver
|
||||
|
|
@ -522,6 +529,8 @@ func _validate_catalog() -> void:
|
|||
and river_edge.water_outlet_edges == 14
|
||||
and river_edge_variant.water_inlet_edges == 14
|
||||
and river_edge_variant.water_outlet_edges == 14
|
||||
and river_stone_bridge.water_inlet_edges == 14
|
||||
and river_stone_bridge.water_outlet_edges == 14
|
||||
and river_source_right.water_inlet_edges == 6
|
||||
and river_source_right.water_outlet_edges == 6
|
||||
and river_source_left.water_inlet_edges == 3
|
||||
|
|
@ -544,6 +553,7 @@ func _validate_catalog() -> void:
|
|||
and river_cap.water_outlet_edges == 6
|
||||
and river_edge.water_surface_polygon.size() == 13
|
||||
and river_edge_variant.water_surface_polygon.size() == 13
|
||||
and river_stone_bridge.water_surface_polygon.size() == 13
|
||||
and river_source_right.water_surface_polygon.size() == 13
|
||||
and river_source_left.water_surface_polygon.size() == 13
|
||||
and river_outlet_right.water_surface_polygon.size() == 19
|
||||
|
|
@ -554,6 +564,8 @@ func _validate_catalog() -> void:
|
|||
and river_cap.primary_mesh_name == &"chunk_0020"
|
||||
and "river" in river_edge.tags
|
||||
and "river" in river_edge_variant.tags
|
||||
and "river_bridge" in river_stone_bridge.tags
|
||||
and "stepping_stones" in river_stone_bridge.tags
|
||||
and "river_source_right" in river_source_right.tags
|
||||
and "river_source_left" in river_source_left.tags
|
||||
and "river_outlet_right" in river_outlet_right.tags
|
||||
|
|
@ -1461,6 +1473,10 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
var river_edge_variant_bottom := _find_variant(generator, &"chunk_0025", 2)
|
||||
var river_edge_vertical_left := _find_variant(generator, &"chunk_0024", 1)
|
||||
var river_edge_vertical_right := _find_variant(generator, &"chunk_0025", 3)
|
||||
var river_stones_top := _find_variant(generator, &"chunk_0038", 0)
|
||||
var river_stones_bottom := _find_variant(generator, &"chunk_0038", 2)
|
||||
var river_stones_left := _find_variant(generator, &"chunk_0038", 1)
|
||||
var river_stones_right := _find_variant(generator, &"chunk_0038", 3)
|
||||
var river_source_right := _find_variant(generator, &"chunk_0029", 0)
|
||||
var river_source_left := _find_variant(generator, &"chunk_0030", 0)
|
||||
var river_outlet_right := _find_variant(generator, &"chunk_0031", 0)
|
||||
|
|
@ -1500,6 +1516,10 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
and river_edge_variant_bottom != null
|
||||
and river_edge_vertical_left != null
|
||||
and river_edge_vertical_right != null
|
||||
and river_stones_top != null
|
||||
and river_stones_bottom != null
|
||||
and river_stones_left != null
|
||||
and river_stones_right != null
|
||||
and river_source_right != null
|
||||
and river_source_left != null
|
||||
and river_outlet_right != null
|
||||
|
|
@ -1525,6 +1545,10 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
and river_edge_variant_bottom != null
|
||||
and river_edge_vertical_left != null
|
||||
and river_edge_vertical_right != null
|
||||
and river_stones_top != null
|
||||
and river_stones_bottom != null
|
||||
and river_stones_left != null
|
||||
and river_stones_right != null
|
||||
and river_source_right != null
|
||||
and river_source_left != null
|
||||
and river_outlet_right != null
|
||||
|
|
@ -1535,6 +1559,30 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
and river_bend_d != null
|
||||
and beach_east != null
|
||||
):
|
||||
_check(
|
||||
generator._edges_are_compatible(
|
||||
river_edge_top,
|
||||
TerrainChunkTopology.Edge.EAST,
|
||||
river_stones_top,
|
||||
TerrainChunkTopology.Edge.WEST,
|
||||
)
|
||||
and generator._edges_are_compatible(
|
||||
river_stones_top,
|
||||
TerrainChunkTopology.Edge.SOUTH,
|
||||
river_stones_bottom,
|
||||
TerrainChunkTopology.Edge.NORTH,
|
||||
)
|
||||
and generator._edges_are_compatible(
|
||||
river_stones_left,
|
||||
TerrainChunkTopology.Edge.EAST,
|
||||
river_stones_right,
|
||||
TerrainChunkTopology.Edge.WEST,
|
||||
),
|
||||
(
|
||||
"The stepping-stone banks must replace one paired straight river "
|
||||
+ "station in either orientation."
|
||||
),
|
||||
)
|
||||
_check(
|
||||
generator._edges_are_compatible(
|
||||
river_source_right,
|
||||
|
|
@ -1750,8 +1798,8 @@ func _validate_authored_rotations(generator: TerrainChunkGenerator) -> void:
|
|||
func _validate_generation_summary(generator: TerrainChunkGenerator) -> void:
|
||||
var summary := generator._build_summary()
|
||||
_check(
|
||||
int(summary.get("variant_count", 0)) == 170,
|
||||
"The current catalog must expose all 170 authored rotations.",
|
||||
int(summary.get("variant_count", 0)) == 174,
|
||||
"The current catalog must expose all 174 authored rotations.",
|
||||
)
|
||||
_check(
|
||||
int(summary.get("solver_variant_count", 0))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
extends SceneTree
|
||||
|
||||
const POLICY := preload("res://main/texture_sampling_policy.gd")
|
||||
const PROJECTED_TERRAIN_TEXTURE_IMPORTS: Array[String] = [
|
||||
"res://world/generation/chunks/assets/chunk_0000_grass_lite.png.import",
|
||||
"res://world/generation/chunks/assets/chunk_0001_sand.png.import",
|
||||
"res://world/generation/chunks/assets/chunk_0031_dirt.png.import",
|
||||
]
|
||||
|
||||
var failures: Array[String] = []
|
||||
|
||||
|
|
@ -83,9 +88,14 @@ func _validate_repository_files(path: String) -> void:
|
|||
|
||||
func _validate_import_file(path: String) -> void:
|
||||
var source := FileAccess.get_file_as_string(path)
|
||||
var expects_mipmaps := path in PROJECTED_TERRAIN_TEXTURE_IMPORTS
|
||||
_check(
|
||||
"mipmaps/generate=true" not in source,
|
||||
"Texture import generates mipmaps: %s" % path,
|
||||
("mipmaps/generate=true" in source) == expects_mipmaps,
|
||||
(
|
||||
"Projected terrain mipmap policy mismatch: %s"
|
||||
if expects_mipmaps
|
||||
else "Texture import generates unapproved mipmaps: %s"
|
||||
) % path,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ compress/rdo_quality_loss=0.0
|
|||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/generate=true
|
||||
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=false
|
||||
mipmaps/generate=true
|
||||
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=false
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
|
|
|
|||
BIN
world/generation/chunks/assets/chunk_0038.glb
Normal file
BIN
world/generation/chunks/assets/chunk_0038.glb
Normal file
Binary file not shown.
45
world/generation/chunks/assets/chunk_0038.glb.import
Normal file
45
world/generation/chunks/assets/chunk_0038.glb.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://b3gl4nmbcgb6i"
|
||||
path="res://.godot/imported/chunk_0038.glb-aab0f64bae856fb9a86c3367af401891.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/generation/chunks/assets/chunk_0038.glb"
|
||||
dest_files=["res://.godot/imported/chunk_0038.glb-aab0f64bae856fb9a86c3367af401891.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_0038_cliff_wall.png
Normal file
BIN
world/generation/chunks/assets/chunk_0038_cliff_wall.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://wmom2kmvf0sb"
|
||||
path.s3tc="res://.godot/imported/chunk_0038_cliff_wall.png-43b1f2500c61c119fe3d6dd64d103bce.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0038_cliff_wall.png-43b1f2500c61c119fe3d6dd64d103bce.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_0038_cliff_wall.png"
|
||||
dest_files=["res://.godot/imported/chunk_0038_cliff_wall.png-43b1f2500c61c119fe3d6dd64d103bce.s3tc.ctex", "res://.godot/imported/chunk_0038_cliff_wall.png-43b1f2500c61c119fe3d6dd64d103bce.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_0038_dirt.png
Normal file
BIN
world/generation/chunks/assets/chunk_0038_dirt.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
45
world/generation/chunks/assets/chunk_0038_dirt.png.import
Normal file
45
world/generation/chunks/assets/chunk_0038_dirt.png.import
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cvffkp5rc3xnq"
|
||||
path.s3tc="res://.godot/imported/chunk_0038_dirt.png-523fbe5835e3869864352e6e698e49e3.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0038_dirt.png-523fbe5835e3869864352e6e698e49e3.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_0038_dirt.png"
|
||||
dest_files=["res://.godot/imported/chunk_0038_dirt.png-523fbe5835e3869864352e6e698e49e3.s3tc.ctex", "res://.godot/imported/chunk_0038_dirt.png-523fbe5835e3869864352e6e698e49e3.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_0038_grass_lite.png
Normal file
BIN
world/generation/chunks/assets/chunk_0038_grass_lite.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1,45 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b231ivhqjd1n5"
|
||||
path.s3tc="res://.godot/imported/chunk_0038_grass_lite.png-0784255d8b50ad328314d6b349ab3d43.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/chunk_0038_grass_lite.png-0784255d8b50ad328314d6b349ab3d43.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_0038_grass_lite.png"
|
||||
dest_files=["res://.godot/imported/chunk_0038_grass_lite.png-0784255d8b50ad328314d6b349ab3d43.s3tc.ctex", "res://.godot/imported/chunk_0038_grass_lite.png-0784255d8b50ad328314d6b349ab3d43.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
|
||||
23
world/generation/chunks/definitions/chunk_0038.tres
Normal file
23
world/generation/chunks/definitions/chunk_0038.tres
Normal 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_0038.glb" id="2_scene"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1_definition")
|
||||
stable_id = &"chunk_0038"
|
||||
label = "river stepping-stone edge"
|
||||
packed_scene = ExtResource("2_scene")
|
||||
primary_mesh_name = &"chunk_0038"
|
||||
participates_in_base_solver = false
|
||||
selection_weight = 0.1
|
||||
maximum_placements = 2
|
||||
tags = PackedStringArray("land", "walkable", "grass", "flat", "spawn_safe", "fresh_water", "river", "river_border", "river_bridge", "stepping_stones", "flowing")
|
||||
allowed_neighbor_tags = PackedStringArray("grass")
|
||||
north_allowed_neighbor_tags = PackedStringArray("grass")
|
||||
north_surface_tags = PackedStringArray("grass")
|
||||
preferred_neighbor_tags = PackedStringArray("grass", "fresh_water", "river")
|
||||
must_be_interior = true
|
||||
water_inlet_edges = 14
|
||||
water_outlet_edges = 14
|
||||
water_surface_polygon = PackedVector2Array(-5, -4.619923, -3.897078, -4.512341, -2.980631, -4.203938, -2.250611, -4.038979, -0.609729, -4.132217, 0.90103, -4.286418, 1.924084, -4.17525, 2.678182, -4.067667, 3.604412, -4.189594, 4.269883, -4.408344, 5, -4.619923, 5, 5, -5, 5)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=47 format=3]
|
||||
[gd_resource type="Resource" script_class="TerrainChunkCatalog" load_steps=48 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"]
|
||||
|
|
@ -42,6 +42,7 @@
|
|||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0035.tres" id="44_river_bend_a"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0036.tres" id="45_river_bend_d"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0037.tres" id="46_lake_edge_variant"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_0038.tres" id="47_river_stone_bridge"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_9999.tres" id="21_cliff_sea_transition_right"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_9998.tres" id="22_cliff_sea_transition_left"]
|
||||
[ext_resource type="Resource" path="res://world/generation/chunks/definitions/chunk_9997.tres" id="23_cliff_beach_transition_right"]
|
||||
|
|
@ -50,4 +51,4 @@
|
|||
[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("20_stream_variant"), ExtResource("6_pond"), ExtResource("25_lake_edge"), ExtResource("46_lake_edge_variant"), ExtResource("26_lake_corner"), ExtResource("27_lake_fill"), ExtResource("28_lake_diagonal"), ExtResource("29_lake_narrow"), ExtResource("30_lake_regular_to_narrow"), ExtResource("31_lake_narrow_to_regular"), ExtResource("32_river_edge"), ExtResource("33_river_edge_variant"), ExtResource("38_river_source_right"), ExtResource("39_river_source_left"), ExtResource("40_river_outlet_right"), ExtResource("41_river_outlet_left"), ExtResource("42_river_bend_c"), ExtResource("43_river_bend_b"), ExtResource("44_river_bend_a"), ExtResource("45_river_bend_d"), ExtResource("34_river_cap"), 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("35_cliff_third_tier_edge"), ExtResource("36_cliff_third_tier_corner"), ExtResource("37_cliff_third_tier_top"), ExtResource("18_cliff_sea_edge"), ExtResource("19_cliff_sea_corner"), ExtResource("21_cliff_sea_transition_right"), ExtResource("22_cliff_sea_transition_left"), ExtResource("23_cliff_beach_transition_right"), ExtResource("24_cliff_beach_transition_left"), 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("25_lake_edge"), ExtResource("46_lake_edge_variant"), ExtResource("26_lake_corner"), ExtResource("27_lake_fill"), ExtResource("28_lake_diagonal"), ExtResource("29_lake_narrow"), ExtResource("30_lake_regular_to_narrow"), ExtResource("31_lake_narrow_to_regular"), ExtResource("32_river_edge"), ExtResource("33_river_edge_variant"), ExtResource("47_river_stone_bridge"), ExtResource("38_river_source_right"), ExtResource("39_river_source_left"), ExtResource("40_river_outlet_right"), ExtResource("41_river_outlet_left"), ExtResource("42_river_bend_c"), ExtResource("43_river_bend_b"), ExtResource("44_river_bend_a"), ExtResource("45_river_bend_d"), ExtResource("34_river_cap"), 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("35_cliff_third_tier_edge"), ExtResource("36_cliff_third_tier_corner"), ExtResource("37_cliff_third_tier_top"), ExtResource("18_cliff_sea_edge"), ExtResource("19_cliff_sea_corner"), ExtResource("21_cliff_sea_transition_right"), ExtResource("22_cliff_sea_transition_left"), ExtResource("23_cliff_beach_transition_right"), ExtResource("24_cliff_beach_transition_left"), ExtResource("8_spawn")])
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ const SALT_WATER_MATERIAL: Material = preload(
|
|||
const FRESH_WATER_MATERIAL: Material = preload(
|
||||
"res://world/materials/stylized_water_fresh.tres"
|
||||
)
|
||||
const GENERATED_TERRAIN_DIRT_MATERIAL: Material = preload(
|
||||
"res://world/materials/generated_terrain_dirt.tres"
|
||||
)
|
||||
const GENERATED_POND_POOL: FishPool = preload(
|
||||
"res://fish/pools/generated_pond_pool.tres"
|
||||
)
|
||||
|
|
@ -32,12 +35,26 @@ const OCEAN_POOL: FishPool = preload(
|
|||
"res://fish/pools/starter_ocean_pool.tres"
|
||||
)
|
||||
const WATER_HEIGHT := -0.25
|
||||
const GENERATED_FRESH_WATER_BED_HEIGHT := -2.85
|
||||
const PROP_EDGE_MARGIN := 2.2
|
||||
const PROP_PLACEMENT_ATTEMPTS := 12
|
||||
const PROP_CLUSTER_PLACEMENT_ATTEMPTS := 10
|
||||
const PROP_MINIMUM_GROUND_CLEARANCE := 0.05
|
||||
const PROP_MAXIMUM_SUPPORT_HEIGHT_DIFFERENCE := 0.35
|
||||
const PROP_CHANCE_SCALE := 10000
|
||||
const PROP_SELECTION_WEIGHT_SCALE := 1000
|
||||
const PROP_SURFACE_SAMPLE_DIRECTIONS: Array[Vector2] = [
|
||||
Vector2(1.0, 0.0),
|
||||
Vector2(0.70710678, 0.70710678),
|
||||
Vector2(0.0, 1.0),
|
||||
Vector2(-0.70710678, 0.70710678),
|
||||
Vector2(-1.0, 0.0),
|
||||
Vector2(-0.70710678, -0.70710678),
|
||||
Vector2(0.0, -1.0),
|
||||
Vector2(0.70710678, -0.70710678),
|
||||
]
|
||||
const GRASS_PROP_SURFACE_MATERIALS: Array[StringName] = [&"grass_lite"]
|
||||
const SAND_PROP_SURFACE_MATERIALS: Array[StringName] = [&"sand"]
|
||||
const PROCEDURAL_PROP_GROUPS: Array[StringName] = [
|
||||
&"grass_tree",
|
||||
&"grass_detail",
|
||||
|
|
@ -185,7 +202,16 @@ func _on_generation_completed(summary: Dictionary) -> void:
|
|||
_placed_group_coordinates.clear()
|
||||
var random := RandomNumberGenerator.new()
|
||||
random.seed = _current_seed ^ 0x5EED71
|
||||
var terrain_triangles := _terrain_surface_triangles()
|
||||
var grass_surface_triangles_by_coordinate := (
|
||||
_terrain_surface_triangles_by_coordinate(
|
||||
GRASS_PROP_SURFACE_MATERIALS,
|
||||
)
|
||||
)
|
||||
var sand_surface_triangles_by_coordinate := (
|
||||
_terrain_surface_triangles_by_coordinate(
|
||||
SAND_PROP_SURFACE_MATERIALS,
|
||||
)
|
||||
)
|
||||
var biomes: Array[TerrainBiomeDefinition] = []
|
||||
if biome_catalog != null:
|
||||
biomes.assign(biome_catalog.definitions)
|
||||
|
|
@ -250,6 +276,14 @@ func _on_generation_completed(summary: Dictionary) -> void:
|
|||
"tags",
|
||||
PackedStringArray(),
|
||||
)
|
||||
var terrain_triangles := _surface_triangles_for_coordinate(
|
||||
(
|
||||
sand_surface_triangles_by_coordinate
|
||||
if group == &"sand_tree"
|
||||
else grass_surface_triangles_by_coordinate
|
||||
),
|
||||
coordinate,
|
||||
)
|
||||
var definitions := _spawn_distance_eligible_definitions(
|
||||
_prop_definitions_for(rule, tags),
|
||||
coordinate,
|
||||
|
|
@ -287,7 +321,8 @@ func _on_generation_completed(summary: Dictionary) -> void:
|
|||
eligible_records,
|
||||
group_counts,
|
||||
random,
|
||||
terrain_triangles,
|
||||
grass_surface_triangles_by_coordinate,
|
||||
sand_surface_triangles_by_coordinate,
|
||||
)
|
||||
_place_spawn_amenities(spawn_position)
|
||||
_configure_fresh_water(records)
|
||||
|
|
@ -406,6 +441,11 @@ func _configure_fresh_water(records: Array[Dictionary]) -> void:
|
|||
body.surface_polygon = surface_polygon
|
||||
else:
|
||||
body.surface_size = surface_size
|
||||
_add_generated_fresh_water_bed(
|
||||
body,
|
||||
surface_polygon,
|
||||
surface_size,
|
||||
)
|
||||
body.visual_surface_enabled = false
|
||||
body.water_material = _fresh_water_material()
|
||||
body.water_type = WaterType.Type.FRESH_WATER
|
||||
|
|
@ -418,6 +458,59 @@ func _configure_fresh_water(records: Array[Dictionary]) -> void:
|
|||
body.selection_priority = 10
|
||||
|
||||
|
||||
func _add_generated_fresh_water_bed(
|
||||
body: WaterBodyAuthoring,
|
||||
surface_polygon: PackedVector2Array,
|
||||
surface_size: Vector2,
|
||||
) -> void:
|
||||
var polygon := surface_polygon
|
||||
if polygon.size() < 3:
|
||||
var half_size := surface_size * 0.5
|
||||
polygon = PackedVector2Array([
|
||||
Vector2(-half_size.x, -half_size.y),
|
||||
Vector2(half_size.x, -half_size.y),
|
||||
Vector2(half_size.x, half_size.y),
|
||||
Vector2(-half_size.x, half_size.y),
|
||||
])
|
||||
var triangle_indices := Geometry2D.triangulate_polygon(polygon)
|
||||
if triangle_indices.size() < 3:
|
||||
return
|
||||
var vertices := PackedVector3Array()
|
||||
var normals := PackedVector3Array()
|
||||
for offset: int in range(0, triangle_indices.size(), 3):
|
||||
var triangle := PackedVector3Array()
|
||||
for point_offset: int in 3:
|
||||
var point := polygon[triangle_indices[offset + point_offset]]
|
||||
triangle.append(Vector3(point.x, 0.0, point.y))
|
||||
if (
|
||||
(triangle[1] - triangle[0]).cross(
|
||||
triangle[2] - triangle[0]
|
||||
).dot(Vector3.UP) < 0.0
|
||||
):
|
||||
var swap := triangle[1]
|
||||
triangle[1] = triangle[2]
|
||||
triangle[2] = swap
|
||||
vertices.append_array(triangle)
|
||||
normals.append_array(PackedVector3Array([
|
||||
Vector3.UP,
|
||||
Vector3.UP,
|
||||
Vector3.UP,
|
||||
]))
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = vertices
|
||||
arrays[Mesh.ARRAY_NORMAL] = normals
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
var bed := MeshInstance3D.new()
|
||||
bed.name = "GeneratedBed"
|
||||
bed.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
bed.position.y = GENERATED_FRESH_WATER_BED_HEIGHT - WATER_HEIGHT
|
||||
bed.material_override = GENERATED_TERRAIN_DIRT_MATERIAL
|
||||
bed.mesh = mesh
|
||||
body.add_child(bed)
|
||||
|
||||
|
||||
func _fresh_water_location_tags(tags: PackedStringArray) -> Array[StringName]:
|
||||
var result: Array[StringName] = [&"generated_fresh_water"]
|
||||
if "pond" in tags:
|
||||
|
|
@ -524,6 +617,7 @@ func _add_prop(
|
|||
)
|
||||
var primary_scale := visual_scales[0]
|
||||
var scaled_clearance := definition.clearance_radius * primary_scale
|
||||
var support_radius := clampf(scaled_clearance * 0.35, 0.25, 0.65)
|
||||
var placement := Vector3.ZERO
|
||||
var found_surface := false
|
||||
for _attempt: int in PROP_PLACEMENT_ATTEMPTS:
|
||||
|
|
@ -541,6 +635,12 @@ func _add_prop(
|
|||
surface_height > -INF
|
||||
and surface_height
|
||||
> WATER_HEIGHT + PROP_MINIMUM_GROUND_CLEARANCE
|
||||
and _surface_supports_prop_footprint(
|
||||
placement,
|
||||
surface_height,
|
||||
support_radius,
|
||||
terrain_triangles,
|
||||
)
|
||||
and _has_prop_clearance(
|
||||
placement,
|
||||
scaled_clearance,
|
||||
|
|
@ -572,6 +672,11 @@ func _add_prop(
|
|||
for member_index: int in range(1, cluster_size):
|
||||
var member_scale := visual_scales[member_index]
|
||||
var member_clearance := definition.clearance_radius * member_scale
|
||||
var member_support_radius := clampf(
|
||||
member_clearance * 0.35,
|
||||
0.25,
|
||||
0.65,
|
||||
)
|
||||
for _attempt: int in PROP_CLUSTER_PLACEMENT_ATTEMPTS:
|
||||
var sector_angle := (
|
||||
cluster_angle
|
||||
|
|
@ -598,6 +703,12 @@ func _add_prop(
|
|||
)
|
||||
if (
|
||||
surface_height <= WATER_HEIGHT + PROP_MINIMUM_GROUND_CLEARANCE
|
||||
or not _surface_supports_prop_footprint(
|
||||
candidate,
|
||||
surface_height,
|
||||
member_support_radius,
|
||||
terrain_triangles,
|
||||
)
|
||||
or not _has_prop_clearance(candidate, member_clearance)
|
||||
):
|
||||
continue
|
||||
|
|
@ -959,7 +1070,8 @@ func _ensure_minimum_props(
|
|||
eligible_records: Dictionary[StringName, Array],
|
||||
group_counts: Dictionary[StringName, int],
|
||||
random: RandomNumberGenerator,
|
||||
terrain_triangles: Array[PackedVector3Array],
|
||||
grass_surface_triangles_by_coordinate: Dictionary[Vector2i, Array],
|
||||
sand_surface_triangles_by_coordinate: Dictionary[Vector2i, Array],
|
||||
) -> void:
|
||||
if biome_catalog == null:
|
||||
return
|
||||
|
|
@ -993,6 +1105,14 @@ func _ensure_minimum_props(
|
|||
"coordinate",
|
||||
Vector2i.ZERO,
|
||||
)
|
||||
var terrain_triangles := _surface_triangles_for_coordinate(
|
||||
(
|
||||
sand_surface_triangles_by_coordinate
|
||||
if group == &"sand_tree"
|
||||
else grass_surface_triangles_by_coordinate
|
||||
),
|
||||
coordinate,
|
||||
)
|
||||
var definitions := _spawn_distance_eligible_definitions(
|
||||
_prop_definitions_for(rule, tags),
|
||||
coordinate,
|
||||
|
|
@ -1101,12 +1221,26 @@ func _apply_material_variant_to_meshes(
|
|||
)
|
||||
|
||||
|
||||
func _terrain_surface_triangles() -> Array[PackedVector3Array]:
|
||||
var triangles: Array[PackedVector3Array] = []
|
||||
func _terrain_surface_triangles_by_coordinate(
|
||||
material_names: Array[StringName],
|
||||
) -> Dictionary[Vector2i, Array]:
|
||||
var triangles_by_coordinate: Dictionary[Vector2i, Array] = {}
|
||||
if material_names.is_empty():
|
||||
return triangles_by_coordinate
|
||||
for mesh_instance: MeshInstance3D in _generator.get_primary_terrain_meshes():
|
||||
if mesh_instance.mesh == null:
|
||||
continue
|
||||
var coordinate := _terrain_coordinate_for(mesh_instance)
|
||||
if coordinate.x < 0 or coordinate.y < 0:
|
||||
continue
|
||||
var triangles: Array[PackedVector3Array] = []
|
||||
for surface_index: int in mesh_instance.mesh.get_surface_count():
|
||||
var material := mesh_instance.get_active_material(surface_index)
|
||||
if (
|
||||
material == null
|
||||
or StringName(material.resource_name) not in material_names
|
||||
):
|
||||
continue
|
||||
_append_surface_triangles(
|
||||
triangles,
|
||||
mesh_instance,
|
||||
|
|
@ -1114,6 +1248,35 @@ func _terrain_surface_triangles() -> Array[PackedVector3Array]:
|
|||
-INF,
|
||||
0.35,
|
||||
)
|
||||
if triangles.is_empty():
|
||||
continue
|
||||
var coordinate_triangles: Array = triangles_by_coordinate.get(
|
||||
coordinate,
|
||||
[],
|
||||
)
|
||||
coordinate_triangles.append_array(triangles)
|
||||
triangles_by_coordinate[coordinate] = coordinate_triangles
|
||||
return triangles_by_coordinate
|
||||
|
||||
|
||||
func _terrain_coordinate_for(mesh_instance: MeshInstance3D) -> Vector2i:
|
||||
var current: Node = mesh_instance
|
||||
while current != null and current != _generator:
|
||||
if current.has_meta(&"terrain_chunk_coordinate"):
|
||||
return current.get_meta(
|
||||
&"terrain_chunk_coordinate",
|
||||
Vector2i(-1, -1),
|
||||
) as Vector2i
|
||||
current = current.get_parent()
|
||||
return Vector2i(-1, -1)
|
||||
|
||||
|
||||
func _surface_triangles_for_coordinate(
|
||||
triangles_by_coordinate: Dictionary[Vector2i, Array],
|
||||
coordinate: Vector2i,
|
||||
) -> Array[PackedVector3Array]:
|
||||
var triangles: Array[PackedVector3Array] = []
|
||||
triangles.assign(triangles_by_coordinate.get(coordinate, []))
|
||||
return triangles
|
||||
|
||||
|
||||
|
|
@ -1139,6 +1302,28 @@ func _surface_height_at(
|
|||
return highest
|
||||
|
||||
|
||||
func _surface_supports_prop_footprint(
|
||||
position: Vector3,
|
||||
surface_height: float,
|
||||
radius: float,
|
||||
triangles: Array[PackedVector3Array],
|
||||
) -> bool:
|
||||
for direction: Vector2 in PROP_SURFACE_SAMPLE_DIRECTIONS:
|
||||
var sample_position := position + Vector3(
|
||||
direction.x * radius,
|
||||
0.0,
|
||||
direction.y * radius,
|
||||
)
|
||||
var sample_height := _surface_height_at(sample_position, triangles)
|
||||
if (
|
||||
sample_height <= -INF
|
||||
or absf(sample_height - surface_height)
|
||||
> PROP_MAXIMUM_SUPPORT_HEIGHT_DIFFERENCE
|
||||
):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _add_prop_collision(
|
||||
prop: Node3D,
|
||||
definition: TerrainPropDefinition,
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ const PROJECTED_DIRT_MATERIAL: Material = preload(
|
|||
@export var river_feature_enabled := false
|
||||
@export var river_edge_chunk_id: StringName = &"chunk_0024"
|
||||
@export var river_edge_variant_chunk_id: StringName = &"chunk_0025"
|
||||
@export var river_stone_bridge_chunk_id: StringName = &"chunk_0038"
|
||||
@export var river_source_right_chunk_id: StringName = &"chunk_0029"
|
||||
@export var river_source_left_chunk_id: StringName = &"chunk_0030"
|
||||
@export var river_outlet_right_chunk_id: StringName = &"chunk_0031"
|
||||
|
|
@ -1728,6 +1729,7 @@ func _prepare_river_feature_region(report_no_room_error := true) -> bool:
|
|||
for stable_id: StringName in [
|
||||
river_edge_chunk_id,
|
||||
river_edge_variant_chunk_id,
|
||||
river_stone_bridge_chunk_id,
|
||||
river_source_right_chunk_id,
|
||||
river_source_left_chunk_id,
|
||||
river_outlet_right_chunk_id,
|
||||
|
|
@ -2190,6 +2192,16 @@ func _build_river_feature_candidate(
|
|||
+ bend_origin.y,
|
||||
2,
|
||||
)
|
||||
var straight_station_count := bend_distance + outlet_distance - 2
|
||||
var bridge_station_index := posmod(
|
||||
generation_seed
|
||||
+ source_coordinates[0].x * 73856093
|
||||
+ source_coordinates[0].y * 19349663
|
||||
+ bend_origin.x * 83492791
|
||||
+ bend_origin.y * 2971215073
|
||||
+ bend_turns * 433494437,
|
||||
straight_station_count,
|
||||
)
|
||||
var station_index := 0
|
||||
for step: int in range(1, bend_distance):
|
||||
var pair: Array[Vector2i] = []
|
||||
|
|
@ -2200,6 +2212,7 @@ func _build_river_feature_candidate(
|
|||
pair,
|
||||
flow_direction,
|
||||
phase + station_index,
|
||||
station_index == bridge_station_index,
|
||||
)
|
||||
)
|
||||
station_index += 1
|
||||
|
|
@ -2215,6 +2228,7 @@ func _build_river_feature_candidate(
|
|||
pair,
|
||||
outlet_direction,
|
||||
phase + station_index,
|
||||
station_index == bridge_station_index,
|
||||
)
|
||||
)
|
||||
station_index += 1
|
||||
|
|
@ -2355,6 +2369,7 @@ func _river_edge_pair_specs(
|
|||
pair: Array[Vector2i],
|
||||
flow_direction: Vector2i,
|
||||
phase: int,
|
||||
use_stone_bridge := false,
|
||||
) -> Array[Dictionary]:
|
||||
var ordered := _river_sorted_pair(pair, flow_direction)
|
||||
if ordered.size() != 2:
|
||||
|
|
@ -2363,6 +2378,11 @@ func _river_edge_pair_specs(
|
|||
river_edge_chunk_id,
|
||||
river_edge_variant_chunk_id,
|
||||
]
|
||||
if use_stone_bridge:
|
||||
edge_ids.assign([
|
||||
river_stone_bridge_chunk_id,
|
||||
river_stone_bridge_chunk_id,
|
||||
])
|
||||
var first_index := posmod(phase, edge_ids.size())
|
||||
var first_turns := 0 if flow_direction.x != 0 else 1
|
||||
var second_turns := 2 if flow_direction.x != 0 else 3
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
shader_type spatial;
|
||||
render_mode cull_back;
|
||||
|
||||
uniform sampler2D albedo_texture : source_color, repeat_enable, filter_nearest;
|
||||
uniform sampler2D albedo_texture : source_color, repeat_enable, filter_nearest_mipmap;
|
||||
uniform vec4 albedo_tint : source_color = vec4(1.0);
|
||||
uniform float tile_world_size : hint_range(0.1, 20.0, 0.05) = 2.5;
|
||||
uniform float blend_sharpness : hint_range(1.0, 16.0, 0.25) = 6.0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue