feat: carve Jajce river into terrain
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,49 @@
|
||||
class_name JajceWatercourse
|
||||
extends RefCounted
|
||||
|
||||
const WATERFALL_CENTER := Vector2(25.0, -24.0)
|
||||
const SHELF_TOP_HEIGHT := 12.4
|
||||
const UPPER_STREAM_START_Z := -58.0
|
||||
const UPPER_STREAM_END_Z := -25.0
|
||||
const PLUNGE_POOL_CENTER := Vector2(25.0, -19.5)
|
||||
const PLUNGE_POOL_BED_HEIGHT := -0.65
|
||||
const DOWNSTREAM_START_Z := -20.0
|
||||
const DOWNSTREAM_END_Z := 42.0
|
||||
const WATER_SURFACE_OFFSET := 0.32
|
||||
|
||||
|
||||
static func downstream_progress(world_z: float) -> float:
|
||||
return clampf(inverse_lerp(DOWNSTREAM_START_Z, DOWNSTREAM_END_Z, world_z), 0.0, 1.0)
|
||||
|
||||
|
||||
static func downstream_center_x(world_z: float) -> float:
|
||||
var progress := downstream_progress(world_z)
|
||||
var downstream_curve := smoothstep(0.58, 1.0, progress) * 12.0
|
||||
return WATERFALL_CENTER.x + sin(progress * TAU) * 1.4 + downstream_curve
|
||||
|
||||
|
||||
static func downstream_half_width(world_z: float) -> float:
|
||||
var progress := downstream_progress(world_z)
|
||||
var base_width := lerpf(4.8, 3.8, smoothstep(0.0, 0.62, progress))
|
||||
return base_width + smoothstep(0.78, 1.0, progress) * 0.8
|
||||
|
||||
|
||||
static func downstream_bed_height(world_z: float) -> float:
|
||||
return lerpf(-0.55, -1.3, downstream_progress(world_z))
|
||||
|
||||
|
||||
static func upper_stream_progress(world_z: float) -> float:
|
||||
return clampf(inverse_lerp(UPPER_STREAM_START_Z, UPPER_STREAM_END_Z, world_z), 0.0, 1.0)
|
||||
|
||||
|
||||
static func upper_stream_center_x(world_z: float) -> float:
|
||||
var progress := upper_stream_progress(world_z)
|
||||
return WATERFALL_CENTER.x + sin(progress * PI * 1.6) * 0.9
|
||||
|
||||
|
||||
static func upper_stream_half_width(world_z: float) -> float:
|
||||
return lerpf(2.8, 4.4, smoothstep(0.55, 1.0, upper_stream_progress(world_z)))
|
||||
|
||||
|
||||
static func upper_stream_bed_height(world_z: float) -> float:
|
||||
return lerpf(11.55, 11.15, upper_stream_progress(world_z))
|
||||
@@ -0,0 +1 @@
|
||||
uid://dl3523bi00bto
|
||||
@@ -1,15 +1,16 @@
|
||||
extends Node3D
|
||||
|
||||
const Watercourse := preload("res://world/jajce/JajceWatercourse.gd")
|
||||
|
||||
@export var terrain_path := NodePath("../../TerrainRoot/Terrain3D")
|
||||
@export var river_material: Material
|
||||
@export_range(6.0, 20.0, 0.5) var river_width := 8.0
|
||||
@export_range(2, 12, 1) var width_segments := 6
|
||||
@export_range(8, 96, 1) var length_segments := 36
|
||||
@export var local_start_z := -25.0
|
||||
@export var local_end_z := 40.0
|
||||
@export_range(0.05, 0.5, 0.01) var water_offset := 0.18
|
||||
@export_range(8, 64, 1) var upper_length_segments := 18
|
||||
@export_range(0.05, 0.5, 0.01) var water_offset := Watercourse.WATER_SURFACE_OFFSET
|
||||
|
||||
@onready var river_ribbon: MeshInstance3D = $RiverRibbon
|
||||
@onready var upper_river_ribbon: MeshInstance3D = $UpperRiverRibbon
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -22,22 +23,48 @@ func _build_terrain_ribbon() -> void:
|
||||
push_warning("TerrainRiverRibbon: terrain or material is unavailable")
|
||||
return
|
||||
|
||||
river_ribbon.mesh = _build_ribbon(
|
||||
terrain,
|
||||
Watercourse.DOWNSTREAM_START_Z,
|
||||
Watercourse.DOWNSTREAM_END_Z,
|
||||
length_segments,
|
||||
false
|
||||
)
|
||||
upper_river_ribbon.mesh = _build_ribbon(
|
||||
terrain,
|
||||
Watercourse.UPPER_STREAM_START_Z,
|
||||
Watercourse.UPPER_STREAM_END_Z,
|
||||
upper_length_segments,
|
||||
true
|
||||
)
|
||||
_align_pool_to_terrain(terrain)
|
||||
|
||||
|
||||
func _build_ribbon(
|
||||
terrain: Terrain3D, start_z: float, end_z: float, segment_count: int, is_upper: bool
|
||||
) -> ArrayMesh:
|
||||
var vertices := PackedVector3Array()
|
||||
var normals := PackedVector3Array()
|
||||
var uvs := PackedVector2Array()
|
||||
var indices := PackedInt32Array()
|
||||
var row_size := width_segments + 1
|
||||
for z_index in length_segments + 1:
|
||||
var progress := float(z_index) / float(length_segments)
|
||||
var local_z := lerpf(local_start_z, local_end_z, progress)
|
||||
var downstream_curve := smoothstep(0.58, 1.0, progress) * 12.0
|
||||
var center_offset := sin(progress * TAU) * 1.4 + downstream_curve
|
||||
var downstream_taper := lerpf(1.0, 0.45, smoothstep(0.65, 1.0, progress))
|
||||
var half_width := river_width * lerpf(0.52, 0.45, progress) * downstream_taper
|
||||
for z_index in segment_count + 1:
|
||||
var progress := float(z_index) / float(segment_count)
|
||||
var world_z := lerpf(start_z, end_z, progress)
|
||||
var center_x := (
|
||||
Watercourse.upper_stream_center_x(world_z)
|
||||
if is_upper
|
||||
else Watercourse.downstream_center_x(world_z)
|
||||
)
|
||||
var half_width := (
|
||||
Watercourse.upper_stream_half_width(world_z)
|
||||
if is_upper
|
||||
else Watercourse.downstream_half_width(world_z)
|
||||
)
|
||||
for x_index in width_segments + 1:
|
||||
var across := float(x_index) / float(width_segments)
|
||||
var local_x := center_offset + lerpf(-half_width, half_width, across)
|
||||
var world_sample := to_global(Vector3(local_x, 0.0, local_z))
|
||||
var world_x := center_x + lerpf(-half_width, half_width, across)
|
||||
var world_sample := Vector3(world_x, 0.0, world_z)
|
||||
var terrain_height := terrain.data.get_height(world_sample)
|
||||
if is_nan(terrain_height):
|
||||
terrain_height = global_position.y
|
||||
@@ -47,7 +74,7 @@ func _build_terrain_ribbon() -> void:
|
||||
normals.append(Vector3.UP)
|
||||
uvs.append(Vector2(across, progress))
|
||||
|
||||
for z_index in length_segments:
|
||||
for z_index in segment_count:
|
||||
for x_index in width_segments:
|
||||
var top_left := z_index * row_size + x_index
|
||||
var top_right := top_left + 1
|
||||
@@ -66,17 +93,20 @@ func _build_terrain_ribbon() -> void:
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
mesh.surface_set_material(0, river_material)
|
||||
river_ribbon.mesh = mesh
|
||||
_align_pool_to_terrain(terrain)
|
||||
return mesh
|
||||
|
||||
|
||||
func _align_pool_to_terrain(terrain: Terrain3D) -> void:
|
||||
var pool := $PlungePool as MeshInstance3D
|
||||
var pool_sample := to_global(Vector3(pool.position.x, 0.0, pool.position.z))
|
||||
var pool_sample := Vector3(
|
||||
Watercourse.PLUNGE_POOL_CENTER.x, 0.0, Watercourse.PLUNGE_POOL_CENTER.y
|
||||
)
|
||||
var pool_height := terrain.data.get_height(pool_sample)
|
||||
if is_nan(pool_height):
|
||||
return
|
||||
var local_surface := to_local(Vector3(pool_sample.x, pool_height + water_offset, pool_sample.z))
|
||||
for node_name in [&"PlungePool", &"FoamRing", &"FoamAtFall"]:
|
||||
var surface := get_node(NodePath(node_name)) as MeshInstance3D
|
||||
surface.position.y = local_surface.y
|
||||
pool.position = local_surface
|
||||
var foam_ring := $FoamRing as MeshInstance3D
|
||||
foam_ring.position = local_surface + Vector3(0.0, 0.1, 0.0)
|
||||
var foam_at_fall := $FoamAtFall as MeshInstance3D
|
||||
foam_at_fall.position = local_surface + Vector3(0.0, 0.13, -2.7)
|
||||
|
||||
@@ -17,22 +17,22 @@ rings = 2
|
||||
[sub_resource type="StandardMaterial3D" id="Material_foam"]
|
||||
transparency = 1
|
||||
shading_mode = 0
|
||||
albedo_color = Color(0.8, 0.93, 0.86, 0.46)
|
||||
albedo_color = Color(0.8, 0.93, 0.86, 0.22)
|
||||
emission_enabled = true
|
||||
emission = Color(0.34, 0.56, 0.48, 1)
|
||||
emission_energy_multiplier = 0.18
|
||||
emission_energy_multiplier = 0.06
|
||||
roughness = 0.74
|
||||
|
||||
[sub_resource type="TorusMesh" id="Mesh_foam_ring"]
|
||||
material = SubResource("Material_foam")
|
||||
inner_radius = 4.8
|
||||
outer_radius = 5.65
|
||||
inner_radius = 5.15
|
||||
outer_radius = 5.5
|
||||
rings = 28
|
||||
ring_segments = 10
|
||||
|
||||
[sub_resource type="PlaneMesh" id="Mesh_foam_patch"]
|
||||
material = SubResource("Material_foam")
|
||||
size = Vector2(10.5, 2.2)
|
||||
size = Vector2(6, 1.5)
|
||||
subdivide_width = 8
|
||||
subdivide_depth = 2
|
||||
|
||||
@@ -44,18 +44,18 @@ river_material = SubResource("ShaderMaterial_water")
|
||||
[node name="RiverRibbon" type="MeshInstance3D" parent="."]
|
||||
cast_shadow = 0
|
||||
|
||||
[node name="UpperRiverRibbon" type="MeshInstance3D" parent="."]
|
||||
cast_shadow = 0
|
||||
|
||||
[node name="PlungePool" type="MeshInstance3D" parent="."]
|
||||
position = Vector3(0, -0.02, -25)
|
||||
scale = Vector3(1.25, 1, 0.82)
|
||||
mesh = SubResource("Mesh_plunge_pool")
|
||||
|
||||
[node name="FoamRing" type="MeshInstance3D" parent="."]
|
||||
position = Vector3(0, 0.09, -25)
|
||||
scale = Vector3(1.25, 0.12, 0.82)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("Mesh_foam_ring")
|
||||
|
||||
[node name="FoamAtFall" type="MeshInstance3D" parent="."]
|
||||
position = Vector3(0, 0.12, -25.8)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("Mesh_foam_patch")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=10 format=3]
|
||||
[gd_scene load_steps=12 format=3]
|
||||
|
||||
[ext_resource type="Shader" path="res://world/jajce/materials/waterfall.gdshader" id="1_waterfall_shader"]
|
||||
|
||||
@@ -7,7 +7,7 @@ shader = ExtResource("1_waterfall_shader")
|
||||
|
||||
[sub_resource type="QuadMesh" id="Mesh_waterfall_body"]
|
||||
material = SubResource("ShaderMaterial_waterfall")
|
||||
size = Vector2(10.8, 14)
|
||||
size = Vector2(9.6, 11.4)
|
||||
subdivide_width = 18
|
||||
subdivide_depth = 28
|
||||
|
||||
@@ -32,50 +32,74 @@ size = Vector3(4.8, 5.5, 3.4)
|
||||
|
||||
[sub_resource type="BoxMesh" id="Mesh_lip"]
|
||||
material = SubResource("Material_rock_moss")
|
||||
size = Vector3(11.6, 0.7, 3.2)
|
||||
size = Vector3(10.4, 0.65, 3.0)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material_lip_foam"]
|
||||
transparency = 1
|
||||
shading_mode = 0
|
||||
albedo_color = Color(0.82, 0.95, 0.87, 0.72)
|
||||
emission_enabled = true
|
||||
emission = Color(0.46, 0.72, 0.62, 1)
|
||||
emission_energy_multiplier = 0.22
|
||||
roughness = 0.62
|
||||
|
||||
[sub_resource type="PlaneMesh" id="Mesh_lip_foam"]
|
||||
material = SubResource("Material_lip_foam")
|
||||
size = Vector2(9.2, 1.45)
|
||||
subdivide_width = 8
|
||||
subdivide_depth = 2
|
||||
|
||||
[node name="WaterfallBody" type="Node3D"]
|
||||
position = Vector3(25, 8.9, -24)
|
||||
position = Vector3(25, 5.8, -24)
|
||||
|
||||
[node name="RockFrame" type="Node3D" parent="."]
|
||||
|
||||
[node name="WestCliff" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(-7.2, -0.45, -1)
|
||||
position = Vector3(-6.1, -0.15, -0.9)
|
||||
rotation_degrees = Vector3(0, 8, -3)
|
||||
scale = Vector3(0.72, 0.86, 0.8)
|
||||
mesh = SubResource("Mesh_cliff_side")
|
||||
|
||||
[node name="EastCliff" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(7.2, -0.25, -1.2)
|
||||
position = Vector3(6.2, 0.05, -1.1)
|
||||
rotation_degrees = Vector3(0, -7, 4)
|
||||
scale = Vector3(0.68, 0.88, 0.76)
|
||||
mesh = SubResource("Mesh_cliff_side")
|
||||
|
||||
[node name="WestCrownRock" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(-5.0, 7.45, -1.25)
|
||||
position = Vector3(-4.9, 5.8, -1.2)
|
||||
rotation_degrees = Vector3(8, -14, -11)
|
||||
scale = Vector3(0.85, 0.62, 0.9)
|
||||
scale = Vector3(0.82, 0.58, 0.88)
|
||||
mesh = SubResource("Mesh_rock_lobe")
|
||||
|
||||
[node name="EastCrownRock" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(5.1, 7.55, -1.4)
|
||||
position = Vector3(4.8, 5.9, -1.35)
|
||||
rotation_degrees = Vector3(-6, 12, 9)
|
||||
scale = Vector3(0.82, 0.6, 0.86)
|
||||
scale = Vector3(0.78, 0.58, 0.84)
|
||||
mesh = SubResource("Mesh_rock_lobe")
|
||||
|
||||
[node name="WestLowerRock" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(-5.9, -4.8, 0.1)
|
||||
position = Vector3(-5.35, -5.0, 0.15)
|
||||
rotation_degrees = Vector3(7, 14, 12)
|
||||
scale = Vector3(0.88, 0.72, 0.9)
|
||||
mesh = SubResource("Mesh_rock_lobe")
|
||||
|
||||
[node name="EastLowerRock" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(6.0, -4.4, 0)
|
||||
position = Vector3(5.45, -4.75, 0)
|
||||
rotation_degrees = Vector3(-4, -12, -9)
|
||||
scale = Vector3(0.9, 0.74, 0.88)
|
||||
mesh = SubResource("Mesh_rock_lobe")
|
||||
|
||||
[node name="RiverLip" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(0, 7.05, 0.2)
|
||||
position = Vector3(0, 5.42, -0.2)
|
||||
mesh = SubResource("Mesh_lip")
|
||||
|
||||
[node name="LipFoam" type="MeshInstance3D" parent="RockFrame"]
|
||||
position = Vector3(0, 5.74, -0.85)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("Mesh_lip_foam")
|
||||
|
||||
[node name="FallingWater" type="MeshInstance3D" parent="."]
|
||||
position = Vector3(0, 0, 1.05)
|
||||
position = Vector3(0, 0, 1.1)
|
||||
cast_shadow = 0
|
||||
mesh = SubResource("Mesh_waterfall_body")
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
[gd_scene load_steps=4 format=3]
|
||||
[gd_scene load_steps=5 format=3]
|
||||
|
||||
[ext_resource type="Texture2D" path="res://world/jajce/materials/mist_soft.svg" id="1_mist_texture"]
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_mist"]
|
||||
lifetime_randomness = 0.35
|
||||
@@ -7,26 +9,27 @@ gravity = Vector3(0, -0.35, 0)
|
||||
initial_velocity_min = 0.25
|
||||
initial_velocity_max = 1.2
|
||||
scale_min = 0.08
|
||||
scale_max = 2.0
|
||||
color = Color(0.82, 0.94, 0.96, 0.35)
|
||||
scale_max = 1.35
|
||||
color = Color(0.82, 0.94, 0.96, 0.22)
|
||||
emission_shape = 1
|
||||
emission_box_extents = Vector3(5.8, 1.5, 1.35)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="Material_mist"]
|
||||
albedo_color = Color(0.82, 0.94, 0.96, 0.28)
|
||||
albedo_color = Color(0.82, 0.94, 0.96, 0.3)
|
||||
transparency = 1
|
||||
shading_mode = 0
|
||||
albedo_texture = ExtResource("1_mist_texture")
|
||||
billboard_mode = 1
|
||||
cull_mode = 0
|
||||
|
||||
[sub_resource type="QuadMesh" id="Mesh_mist"]
|
||||
material = SubResource("Material_mist")
|
||||
size = Vector2(1.8, 1.8)
|
||||
size = Vector2(1.45, 1.45)
|
||||
|
||||
[node name="WaterfallMist" type="GPUParticles3D"]
|
||||
position = Vector3(25, 2.05, -24)
|
||||
position = Vector3(25, 0.65, -21.5)
|
||||
emitting = true
|
||||
amount = 96
|
||||
amount = 56
|
||||
lifetime = 3.0
|
||||
one_shot = false
|
||||
preprocess = 1.5
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||
<defs>
|
||||
<radialGradient id="mist" cx="50%" cy="48%" r="50%">
|
||||
<stop offset="0" stop-color="#dff4ef" stop-opacity="0.82"/>
|
||||
<stop offset="0.42" stop-color="#c8e7e3" stop-opacity="0.48"/>
|
||||
<stop offset="0.76" stop-color="#b8d9d7" stop-opacity="0.14"/>
|
||||
<stop offset="1" stop-color="#b8d9d7" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<circle cx="32" cy="32" r="31" fill="url(#mist)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c0ie27xnkmfd8"
|
||||
path="res://.godot/imported/mist_soft.svg-8d0454048e96ea4cac3f1157714c34e4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://world/jajce/materials/mist_soft.svg"
|
||||
dest_files=["res://.godot/imported/mist_soft.svg-8d0454048e96ea4cac3f1157714c34e4.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
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
Reference in New Issue
Block a user