generated from krampus/template-godot4
	Compare commits
	
		
			3 Commits
		
	
	
		
			96c73c5587
			...
			0931abce56
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0931abce56 | |||
| 40a1b3548a | |||
| 7c7458be65 | 
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								assets/ui/lifebar_fill_damage.png
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/ui/lifebar_fill_damage.png
									 (Stored with Git LFS)
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										34
									
								
								assets/ui/lifebar_fill_damage.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								assets/ui/lifebar_fill_damage.png.import
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | [remap] | ||||||
|  | 
 | ||||||
|  | importer="texture" | ||||||
|  | type="CompressedTexture2D" | ||||||
|  | uid="uid://dv8757eh7bgmm" | ||||||
|  | path="res://.godot/imported/lifebar_fill_damage.png-844591e686129d7c89c44d44c5c8df44.ctex" | ||||||
|  | metadata={ | ||||||
|  | "vram_texture": false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [deps] | ||||||
|  | 
 | ||||||
|  | source_file="res://assets/ui/lifebar_fill_damage.png" | ||||||
|  | dest_files=["res://.godot/imported/lifebar_fill_damage.png-844591e686129d7c89c44d44c5c8df44.ctex"] | ||||||
|  | 
 | ||||||
|  | [params] | ||||||
|  | 
 | ||||||
|  | compress/mode=0 | ||||||
|  | compress/high_quality=false | ||||||
|  | compress/lossy_quality=0.7 | ||||||
|  | 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/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 | ||||||
| @ -22,6 +22,9 @@ const IRON_DAMPING := 9999.0 | |||||||
| 			physics_material_override = normal_physics | 			physics_material_override = normal_physics | ||||||
| 		iron_ball = value | 		iron_ball = value | ||||||
| 
 | 
 | ||||||
|  | ## Base damage inflicted on impact with a player | ||||||
|  | @export var base_damage := 15.0 | ||||||
|  | 
 | ||||||
| var _zones: Array[BallZone] = [] | var _zones: Array[BallZone] = [] | ||||||
| 
 | 
 | ||||||
| @onready var normal_physics: PhysicsMaterial = preload( | @onready var normal_physics: PhysicsMaterial = preload( | ||||||
|  | |||||||
| @ -35,6 +35,7 @@ class ScenePromise: | |||||||
| 	extends Promise | 	extends Promise | ||||||
| 
 | 
 | ||||||
| 	func resolve(res: Variant) -> void: | 	func resolve(res: Variant) -> void: | ||||||
|  | 		@warning_ignore("unsafe_cast") | ||||||
| 		var instance: Node = (res as PackedScene).instantiate() | 		var instance: Node = (res as PackedScene).instantiate() | ||||||
| 		super.resolve(instance) | 		super.resolve(instance) | ||||||
| 
 | 
 | ||||||
| @ -79,16 +80,6 @@ func _finish_scene_load(instance: Node) -> void: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| func _process(_delta: float) -> void: | func _process(_delta: float) -> void: | ||||||
| 	# REMOVEME |  | ||||||
| 	if Input.is_action_just_pressed("debug_1"): |  | ||||||
| 		viewport.hit_lag(1) |  | ||||||
| 	if Input.is_action_just_pressed("debug_2"): |  | ||||||
| 		viewport.hit_lag_small() |  | ||||||
| 	if Input.is_action_just_pressed("debug_3"): |  | ||||||
| 		viewport.hit_lag_big() |  | ||||||
| 	if Input.is_action_just_pressed("debug_4"): |  | ||||||
| 		viewport.hit_lag_huge() |  | ||||||
| 
 |  | ||||||
| 	if _loading_resources and not loading_screen.visible: | 	if _loading_resources and not loading_screen.visible: | ||||||
| 		loader_transition.play("fade_in") | 		loader_transition.play("fade_in") | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -184,7 +184,6 @@ script = ExtResource("3_rmm5i") | |||||||
| 
 | 
 | ||||||
| [node name="Viewport" type="SubViewport" parent="RootControl/Rumbler/ViewportContainer"] | [node name="Viewport" type="SubViewport" parent="RootControl/Rumbler/ViewportContainer"] | ||||||
| handle_input_locally = false | handle_input_locally = false | ||||||
| msaa_2d = 3 |  | ||||||
| msaa_3d = 3 | msaa_3d = 3 | ||||||
| screen_space_aa = 1 | screen_space_aa = 1 | ||||||
| use_taa = true | use_taa = true | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								src/player/shot_setup/hitbox.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/player/shot_setup/hitbox.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | extends Area3D | ||||||
|  | ## World player hitbox | ||||||
|  | 
 | ||||||
|  | signal ball_collision(ball: GameBall) | ||||||
|  | 
 | ||||||
|  | @export var ignored_balls: Array[GameBall] = [] | ||||||
|  | 
 | ||||||
|  | @onready var shot_setup: ShotSetup = $".." | ||||||
|  | @onready var physics_ball: GameBall = %PhysicsBall | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_ball_entered(ball: GameBall) -> void: | ||||||
|  | 	if not ball in ignored_balls: | ||||||
|  | 		ball_collision.emit(ball) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_body_entered(body: Node3D) -> void: | ||||||
|  | 	if body is GameBall: | ||||||
|  | 		_on_ball_entered(body as GameBall) | ||||||
| @ -31,6 +31,9 @@ const WASTED_BALL_RETURN_DELAY := 3.5 | |||||||
| ## Shots above this threshold trigger a "big power" effect | ## Shots above this threshold trigger a "big power" effect | ||||||
| const BIG_POWER_THRESHOLD := 0.7 | const BIG_POWER_THRESHOLD := 0.7 | ||||||
| 
 | 
 | ||||||
|  | ## Amount of life lost when landing in water | ||||||
|  | const WATER_DAMAGE := 10.0 | ||||||
|  | 
 | ||||||
| ## In Driving Range mode, the ball can be retrieved in the shot phase. | ## In Driving Range mode, the ball can be retrieved in the shot phase. | ||||||
| @export var driving_range := false | @export var driving_range := false | ||||||
| 
 | 
 | ||||||
| @ -158,7 +161,7 @@ func _init_deferred() -> void: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| func _ready() -> void: | func _ready() -> void: | ||||||
| 	call_deferred("_init_deferred") | 	_init_deferred.call_deferred() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| func _set_camera_distance(value: float) -> void: | func _set_camera_distance(value: float) -> void: | ||||||
| @ -500,6 +503,7 @@ func _on_ball_entered_water() -> void: | |||||||
| 	if phase == Phase.SHOT: | 	if phase == Phase.SHOT: | ||||||
| 		physics_ball.freeze = true | 		physics_ball.freeze = true | ||||||
| 		hud.play_wasted_animation() | 		hud.play_wasted_animation() | ||||||
|  | 		player.life -= WATER_DAMAGE | ||||||
| 		ball_return_timer.start(WASTED_BALL_RETURN_DELAY) | 		ball_return_timer.start(WASTED_BALL_RETURN_DELAY) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -520,6 +524,11 @@ func _on_ball_return_timer_timeout() -> void: | |||||||
| 	return_ball() | 	return_ball() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | func _on_hitbox_ball_collision(ball: GameBall) -> void: | ||||||
|  | 	# TODO play animation | ||||||
|  | 	player.life -= ball.base_damage | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ## Create a new instance for the given player. | ## Create a new instance for the given player. | ||||||
| static func create(_player: WorldPlayer) -> ShotSetup: | static func create(_player: WorldPlayer) -> ShotSetup: | ||||||
| 	var instance: ShotSetup = ShotSetup.scene.instantiate() | 	var instance: ShotSetup = ShotSetup.scene.instantiate() | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| [gd_scene load_steps=17 format=3 uid="uid://cy7t2tc4y3b4"] | [gd_scene load_steps=19 format=3 uid="uid://cy7t2tc4y3b4"] | ||||||
| 
 | 
 | ||||||
| [ext_resource type="Script" path="res://src/player/shot_setup/shot_setup.gd" id="1_r6ei4"] | [ext_resource type="Script" path="res://src/player/shot_setup/shot_setup.gd" id="1_r6ei4"] | ||||||
| [ext_resource type="PackedScene" uid="uid://dfttci386ohip" path="res://src/equipment/balls/physics_ball/physics_ball.tscn" id="2_1i5j5"] | [ext_resource type="PackedScene" uid="uid://dfttci386ohip" path="res://src/equipment/balls/physics_ball/physics_ball.tscn" id="2_1i5j5"] | ||||||
| @ -6,6 +6,7 @@ | |||||||
| [ext_resource type="PackedScene" uid="uid://1s3gywmoi20e" path="res://src/characters/player_characters/gfolf_girl/gfolf_girl.tscn" id="3_e4aur"] | [ext_resource type="PackedScene" uid="uid://1s3gywmoi20e" path="res://src/characters/player_characters/gfolf_girl/gfolf_girl.tscn" id="3_e4aur"] | ||||||
| [ext_resource type="PackedScene" uid="uid://fht6j87o8ecr" path="res://src/ui/3d/projectile_arc/projectile_arc.tscn" id="4_ry2ho"] | [ext_resource type="PackedScene" uid="uid://fht6j87o8ecr" path="res://src/ui/3d/projectile_arc/projectile_arc.tscn" id="4_ry2ho"] | ||||||
| [ext_resource type="PackedScene" uid="uid://dbdul15c4oblg" path="res://src/ui/3d/projected_target.tscn" id="6_mynqj"] | [ext_resource type="PackedScene" uid="uid://dbdul15c4oblg" path="res://src/ui/3d/projected_target.tscn" id="6_mynqj"] | ||||||
|  | [ext_resource type="Script" path="res://src/player/shot_setup/hitbox.gd" id="7_uh8kn"] | ||||||
| 
 | 
 | ||||||
| [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lnol1"] | [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lnol1"] | ||||||
| albedo_color = Color(0, 0.537255, 1, 1) | albedo_color = Color(0, 0.537255, 1, 1) | ||||||
| @ -203,6 +204,9 @@ _data = { | |||||||
| "swing_delay": SubResource("Animation_u8k07") | "swing_delay": SubResource("Animation_u8k07") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [sub_resource type="SphereShape3D" id="SphereShape3D_xvvdi"] | ||||||
|  | radius = 1.5 | ||||||
|  | 
 | ||||||
| [node name="ShotSetup" type="Node3D"] | [node name="ShotSetup" type="Node3D"] | ||||||
| script = ExtResource("1_r6ei4") | script = ExtResource("1_r6ei4") | ||||||
| 
 | 
 | ||||||
| @ -360,7 +364,16 @@ libraries = { | |||||||
| unique_name_in_owner = true | unique_name_in_owner = true | ||||||
| one_shot = true | one_shot = true | ||||||
| 
 | 
 | ||||||
|  | [node name="Hitbox" type="Area3D" parent="." node_paths=PackedStringArray("ignored_balls")] | ||||||
|  | script = ExtResource("7_uh8kn") | ||||||
|  | ignored_balls = [NodePath("../BallPoint/PhysicsBall")] | ||||||
|  | 
 | ||||||
|  | [node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"] | ||||||
|  | shape = SubResource("SphereShape3D_xvvdi") | ||||||
|  | 
 | ||||||
| [connection signal="body_entered" from="BallPoint/PhysicsBall" to="." method="_on_physics_ball_body_entered"] | [connection signal="body_entered" from="BallPoint/PhysicsBall" to="." method="_on_physics_ball_body_entered"] | ||||||
| [connection signal="entered_water" from="BallPoint/PhysicsBall" to="." method="_on_ball_entered_water"] | [connection signal="entered_water" from="BallPoint/PhysicsBall" to="." method="_on_ball_entered_water"] | ||||||
| [connection signal="sleeping_state_changed" from="BallPoint/PhysicsBall" to="." method="_on_physics_ball_sleeping_state_changed"] | [connection signal="sleeping_state_changed" from="BallPoint/PhysicsBall" to="." method="_on_physics_ball_sleeping_state_changed"] | ||||||
| [connection signal="timeout" from="BallReturnTimer" to="." method="_on_ball_return_timer_timeout"] | [connection signal="timeout" from="BallReturnTimer" to="." method="_on_ball_return_timer_timeout"] | ||||||
|  | [connection signal="ball_collision" from="Hitbox" to="." method="_on_hitbox_ball_collision"] | ||||||
|  | [connection signal="body_entered" from="Hitbox" to="Hitbox" method="_on_body_entered"] | ||||||
|  | |||||||
							
								
								
									
										547
									
								
								src/shaders/terrain.tres
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										547
									
								
								src/shaders/terrain.tres
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,547 @@ | |||||||
|  | [gd_resource type="Shader" format=3 uid="uid://dcbswuorsomae"] | ||||||
|  | 
 | ||||||
|  | [resource] | ||||||
|  | code = "shader_type spatial; | ||||||
|  | render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform; | ||||||
|  | 
 | ||||||
|  | /* This shader is generated based upon the debug views you have selected. | ||||||
|  |  * The terrain function depends on this shader. So don't change: | ||||||
|  |  * - vertex positioning in vertex() | ||||||
|  |  * - terrain normal calculation in fragment() | ||||||
|  |  * - the last function being fragment() as the editor injects code before the closing } | ||||||
|  |  * | ||||||
|  |  * Most will only want to customize the material calculation and PBR application in fragment() | ||||||
|  |  * | ||||||
|  |  * Uniforms that begin with _ are private and will not display in the inspector. However, | ||||||
|  |  * you can set them via code. You are welcome to create more of your own hidden uniforms. | ||||||
|  |  * | ||||||
|  |  * This system only supports albedo, height, normal, roughness. Most textures don't need the other | ||||||
|  |  * PBR channels. Height can be used as an approximation for AO. For the rare textures do need | ||||||
|  |  * additional channels, you can add maps for that one texture. e.g. an emissive map for lava. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // Private uniforms | ||||||
|  | 
 | ||||||
|  | uniform float _region_size = 1024.0; | ||||||
|  | uniform float _region_texel_size = 0.0009765625; // = 1/1024 | ||||||
|  | uniform float _vertex_spacing = 1.0; | ||||||
|  | uniform float _vertex_density = 1.0; // = 1/_vertex_spacing | ||||||
|  | uniform int _region_map_size = 32; | ||||||
|  | uniform int _region_map[1024]; | ||||||
|  | uniform vec2 _region_locations[1024]; | ||||||
|  | uniform highp sampler2DArray _height_maps : repeat_disable; | ||||||
|  | uniform highp usampler2DArray _control_maps : repeat_disable; | ||||||
|  | uniform highp sampler2DArray _color_maps : source_color, filter_nearest_mipmap_anisotropic, repeat_disable; | ||||||
|  | uniform highp sampler2DArray _texture_array_albedo : source_color, filter_nearest_mipmap_anisotropic, repeat_enable; | ||||||
|  | uniform highp sampler2DArray _texture_array_normal : hint_normal, filter_nearest_mipmap_anisotropic, repeat_enable; | ||||||
|  | uniform highp sampler2D noise_texture : source_color, filter_nearest_mipmap_anisotropic, repeat_enable; | ||||||
|  | 
 | ||||||
|  | uniform float _texture_uv_scale_array[32]; | ||||||
|  | uniform float _texture_detile_array[32]; | ||||||
|  | uniform vec4 _texture_color_array[32]; | ||||||
|  | uniform uint _background_mode = 1u;  // NONE = 0, FLAT = 1, NOISE = 2 | ||||||
|  | uniform uint _mouse_layer = 0x80000000u; // Layer 32 | ||||||
|  | 
 | ||||||
|  | // Public uniforms | ||||||
|  | uniform float vertex_normals_distance : hint_range(0, 1024) = 128.0; | ||||||
|  | uniform bool height_blending = true; | ||||||
|  | uniform float blend_sharpness : hint_range(0, 1) = 0.87; | ||||||
|  | uniform float auto_slope : hint_range(0, 10) = 1.0; | ||||||
|  | uniform float auto_height_reduction : hint_range(0, 1) = 0.1; | ||||||
|  | uniform int auto_base_texture : hint_range(0, 31) = 0; | ||||||
|  | uniform int auto_overlay_texture : hint_range(0, 31) = 1; | ||||||
|  | 
 | ||||||
|  | uniform vec3 macro_variation1 : source_color = vec3(1.); | ||||||
|  | uniform vec3 macro_variation2 : source_color = vec3(1.); | ||||||
|  | 
 | ||||||
|  | // Generic noise at 3 scales, which can be used for anything | ||||||
|  | uniform float noise1_scale : hint_range(0.001, 1.) = 0.04;	// Used for macro variation 1. Scaled up 10x | ||||||
|  | uniform float noise1_angle : hint_range(0, 6.283) = 0.; | ||||||
|  | uniform vec2 noise1_offset = vec2(0.5); | ||||||
|  | uniform float noise2_scale : hint_range(0.001, 1.) = 0.076;	// Used for macro variation 2. Scaled up 10x | ||||||
|  | uniform float noise3_scale : hint_range(0.001, 1.) = 0.225;  // Used for texture blending edge. | ||||||
|  | 
 | ||||||
|  | // Varyings & Types | ||||||
|  | 
 | ||||||
|  | struct Material { | ||||||
|  | 	vec4 alb_ht; | ||||||
|  | 	vec4 nrm_rg; | ||||||
|  | 	int base; | ||||||
|  | 	int over; | ||||||
|  | 	float blend; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | varying flat vec3 v_vertex;	// World coordinate vertex location | ||||||
|  | varying flat vec3 v_camera_pos; | ||||||
|  | varying float v_vertex_xz_dist; | ||||||
|  | varying flat ivec3 v_region; | ||||||
|  | varying flat vec2 v_uv_offset; | ||||||
|  | varying flat vec2 v_uv2_offset; | ||||||
|  | varying vec3 v_normal; | ||||||
|  | varying float v_region_border_mask; | ||||||
|  | 
 | ||||||
|  | //////////////////////// | ||||||
|  | // Vertex | ||||||
|  | //////////////////////// | ||||||
|  | 
 | ||||||
|  | // Takes in UV world space coordinates, returns ivec3 with: | ||||||
|  | // XY: (0 to _region_size) coordinates within a region | ||||||
|  | // Z: layer index used for texturearrays, -1 if not in a region | ||||||
|  | ivec3 get_region_uv(const vec2 uv) { | ||||||
|  | 	ivec2 pos = ivec2(floor(uv * _region_texel_size)) + (_region_map_size / 2); | ||||||
|  | 	int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size)); | ||||||
|  | 	int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1; | ||||||
|  | 	return ivec3(ivec2(mod(uv,_region_size)), layer_index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Takes in UV2 region space coordinates, returns vec3 with: | ||||||
|  | // XY: (0 to 1) coordinates within a region | ||||||
|  | // Z: layer index used for texturearrays, -1 if not in a region | ||||||
|  | vec3 get_region_uv2(const vec2 uv2) { | ||||||
|  | 	// Remove Texel Offset to ensure correct region index. | ||||||
|  | 	ivec2 pos = ivec2(floor(uv2 - vec2(_region_texel_size * 0.5))) + (_region_map_size / 2); | ||||||
|  | 	int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size)); | ||||||
|  | 	int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1; | ||||||
|  | 	return vec3(uv2 - _region_locations[layer_index], float(layer_index)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // World Noise | ||||||
|  | uniform float world_noise_region_blend : hint_range(0.05, 0.95, 0.01) = 0.33; | ||||||
|  | uniform int world_noise_max_octaves : hint_range(0, 15) = 4; | ||||||
|  | uniform int world_noise_min_octaves : hint_range(0, 15) = 2; | ||||||
|  | uniform float world_noise_lod_distance : hint_range(0, 40000, 1) = 7500.; | ||||||
|  | uniform float world_noise_scale : hint_range(0.25, 20, 0.01) = 5.0; | ||||||
|  | uniform float world_noise_height : hint_range(0, 1000, 0.1) = 64.0; | ||||||
|  | uniform vec3 world_noise_offset = vec3(0.0); | ||||||
|  | 
 | ||||||
|  | // Takes in UV2 region space coordinates, returns 1.0 or 0.0 if a region is present or not. | ||||||
|  | float check_region(const vec2 uv2) { | ||||||
|  | 	ivec2 pos = ivec2(floor(uv2)) + (_region_map_size / 2); | ||||||
|  | 	int layer_index = 0; | ||||||
|  | 	if (uint(pos.x | pos.y) < uint(_region_map_size)) { | ||||||
|  | 		layer_index = clamp(_region_map[ pos.y * _region_map_size + pos.x ] - 1, -1, 0) + 1; | ||||||
|  | 	} | ||||||
|  | 	return float(layer_index); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Takes in UV2 region space coordinates, returns a blend value (0 - 1 range) between empty, and valid regions | ||||||
|  | float region_blend(vec2 uv2) { | ||||||
|  | 	uv2 -= 0.5; | ||||||
|  | 	const vec2 offset = vec2(0.0,1.0); | ||||||
|  | 	float a = check_region(uv2 + offset.xy); | ||||||
|  | 	float b = check_region(uv2 + offset.yy); | ||||||
|  | 	float c = check_region(uv2 + offset.yx); | ||||||
|  | 	float d = check_region(uv2 + offset.xx); | ||||||
|  | 	vec2 w = smoothstep(vec2(0.0), vec2(1.0), fract(uv2)); | ||||||
|  | 	float blend = mix(mix(d, c, w.x), mix(a, b, w.x), w.y); | ||||||
|  |     return 1.0 - blend; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float hashf(float f) { | ||||||
|  | 	return fract(sin(f) * 1e4); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float hashv2(vec2 v) { | ||||||
|  | 	return fract(1e4 * sin(fma(17.0, v.x, v.y * 0.1)) * (0.1 + abs(sin(fma(v.y, 13.0, v.x))))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // https://iquilezles.org/articles/morenoise/ | ||||||
|  | vec3 noise2D(vec2 x) { | ||||||
|  |     vec2 f = fract(x); | ||||||
|  |     // Quintic Hermine Curve.  Similar to SmoothStep() | ||||||
|  |     vec2 u = f*f*f*(f*(f*6.0-15.0)+10.0); | ||||||
|  |     vec2 du = 30.0*f*f*(f*(f-2.0)+1.0); | ||||||
|  | 
 | ||||||
|  |     vec2 p = floor(x); | ||||||
|  | 
 | ||||||
|  | 	// Four corners in 2D of a tile | ||||||
|  | 	float a = hashv2( p+vec2(0,0) ); | ||||||
|  |     float b = hashv2( p+vec2(1,0) ); | ||||||
|  |     float c = hashv2( p+vec2(0,1) ); | ||||||
|  |     float d = hashv2( p+vec2(1,1) ); | ||||||
|  | 
 | ||||||
|  |     // Mix 4 corner percentages | ||||||
|  |     float k0 =   a; | ||||||
|  |     float k1 =   b - a; | ||||||
|  |     float k2 =   c - a; | ||||||
|  |     float k3 =   a - b - c + d; | ||||||
|  |     return vec3( k0 + k1 * u.x + k2 * u.y + k3 * u.x * u.y, | ||||||
|  |                 du * ( vec2(k1, k2) + k3 * u.yx) ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float world_noise(vec2 p) { | ||||||
|  |     float a = 0.0; | ||||||
|  |     float b = 1.0; | ||||||
|  |     vec2  d = vec2(0.0); | ||||||
|  | 
 | ||||||
|  |     int octaves = int( clamp( | ||||||
|  | 	float(world_noise_max_octaves) - floor(v_vertex_xz_dist/(world_noise_lod_distance)), | ||||||
|  |     float(world_noise_min_octaves), float(world_noise_max_octaves)) ); | ||||||
|  | 
 | ||||||
|  |     for( int i=0; i < octaves; i++ ) { | ||||||
|  |         vec3 n = noise2D(p); | ||||||
|  |         d += n.yz; | ||||||
|  |         a += b * n.x / (1.0 + dot(d,d)); | ||||||
|  |         b *= 0.5; | ||||||
|  |         p = mat2( vec2(0.8, -0.6), vec2(0.6, 0.8) ) * p * 2.0; | ||||||
|  |     } | ||||||
|  |     return a; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float get_noise_height(const vec2 uv) { | ||||||
|  | 	float weight = region_blend(uv); | ||||||
|  | 	// only calculate world noise when it could be visibile. | ||||||
|  | 	if (weight <= 1.0 - world_noise_region_blend) { | ||||||
|  | 		return 0.0; | ||||||
|  | 	} | ||||||
|  | 	//TODO: Offset/scale UVs are semi-dependent upon region size 1024. Base on v_vertex.xz instead | ||||||
|  | 	float noise = world_noise((uv + world_noise_offset.xz * 1024. / _region_size) * world_noise_scale * _region_size / 1024. * .1) * | ||||||
|  |             world_noise_height * 10. + world_noise_offset.y * 100.; | ||||||
|  | 	weight = smoothstep(1.0 - world_noise_region_blend, 1.0, weight); | ||||||
|  | 	return mix(0.0, noise, weight); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // World Noise end | ||||||
|  | 
 | ||||||
|  | // 1 lookup | ||||||
|  | float get_height(vec2 uv) { | ||||||
|  | 	highp float height = 0.0; | ||||||
|  | 	vec3 region = get_region_uv2(uv); | ||||||
|  | 	if (region.z >= 0.) { | ||||||
|  | 		height = texture(_height_maps, region).r; | ||||||
|  | 	} | ||||||
|  | 	// World Noise | ||||||
|  |    	if (_background_mode == 2u) { | ||||||
|  | 	    height += get_noise_height(uv); | ||||||
|  |     } | ||||||
|  |  	return height; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void vertex() { | ||||||
|  | 	// Get camera pos in world vertex coords | ||||||
|  | 	v_camera_pos = INV_VIEW_MATRIX[3].xyz; | ||||||
|  | 
 | ||||||
|  | 	// Get vertex of flat plane in world coordinates and set world UV | ||||||
|  | 	v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; | ||||||
|  | 
 | ||||||
|  | 	// Camera distance to vertex on flat plane | ||||||
|  | 	v_vertex_xz_dist = length(v_vertex.xz - v_camera_pos.xz); | ||||||
|  | 
 | ||||||
|  | 	// UV coordinates in world space. Values are 0 to _region_size within regions | ||||||
|  | 	UV = round(v_vertex.xz * _vertex_density); | ||||||
|  | 
 | ||||||
|  | 	// UV coordinates in region space + texel offset. Values are 0 to 1 within regions | ||||||
|  | 	UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size)); | ||||||
|  | 
 | ||||||
|  | 	// Discard vertices for Holes. 1 lookup | ||||||
|  | 	v_region = get_region_uv(UV); | ||||||
|  | 	uint control = texelFetch(_control_maps, v_region, 0).r; | ||||||
|  | 	bool hole = bool(control >>2u & 0x1u); | ||||||
|  | 
 | ||||||
|  | 	// Show holes to all cameras except mouse camera (on exactly 1 layer) | ||||||
|  | 	if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) && | ||||||
|  | 			(hole || (_background_mode == 0u && (get_region_uv(UV - _region_texel_size) & v_region).z < 0))) { | ||||||
|  | 		VERTEX.x = 0. / 0.; | ||||||
|  | 	} else { | ||||||
|  | 		// Set final vertex height & calculate vertex normals. 3 lookups. | ||||||
|  | 		VERTEX.y = get_height(UV2); | ||||||
|  | 		v_vertex.y = VERTEX.y; | ||||||
|  | 		v_normal = vec3( | ||||||
|  | 			v_vertex.y - get_height(UV2 + vec2(_region_texel_size, 0)), | ||||||
|  | 			_vertex_spacing, | ||||||
|  | 			v_vertex.y - get_height(UV2 + vec2(0, _region_texel_size)) | ||||||
|  | 		); | ||||||
|  | 		// Due to a bug caused by the GPUs linear interpolation across edges of region maps, | ||||||
|  | 		// mask region edges and use vertex normals only across region boundaries. | ||||||
|  | 		v_region_border_mask = mod(UV.x + 2.5, _region_size) - fract(UV.x) < 5.0 || mod(UV.y + 2.5, _region_size) - fract(UV.y) < 5.0 ? 1. : 0.; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Transform UVs to local to avoid poor precision during varying interpolation. | ||||||
|  | 	v_uv_offset = MODEL_MATRIX[3].xz * _vertex_density; | ||||||
|  | 	UV -= v_uv_offset; | ||||||
|  | 	v_uv2_offset = v_uv_offset * _region_texel_size; | ||||||
|  | 	UV2 -= v_uv2_offset; | ||||||
|  | 
 | ||||||
|  | 	// Convert model space to view space w/ skip_vertex_transform render mode | ||||||
|  | 	VERTEX = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; | ||||||
|  | 	VERTEX = (VIEW_MATRIX * vec4(VERTEX, 1.0)).xyz; | ||||||
|  | 	NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz); | ||||||
|  | 	BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz); | ||||||
|  | 	TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //////////////////////// | ||||||
|  | // Fragment | ||||||
|  | //////////////////////// | ||||||
|  | 
 | ||||||
|  | // 0 - 3 lookups | ||||||
|  | vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) { | ||||||
|  | 	float u, v, height; | ||||||
|  | 	vec3 normal; | ||||||
|  | 	// Use vertex normals within radius of vertex_normals_distance, and along region borders. | ||||||
|  | 	if ((v_region_border_mask > 0.5 || v_vertex_xz_dist < vertex_normals_distance) && v_region.z >= 0) { | ||||||
|  | 		normal = normalize(v_normal); | ||||||
|  | 	} else { | ||||||
|  | 		height = get_height(uv); | ||||||
|  | 		u = height - get_height(uv + vec2(_region_texel_size, 0)); | ||||||
|  | 		v = height - get_height(uv + vec2(0, _region_texel_size)); | ||||||
|  | 		normal = normalize(vec3(u, _vertex_spacing, v)); | ||||||
|  | 	} | ||||||
|  | 	tangent = normalize(cross(normal, vec3(0, 0, 1))); | ||||||
|  | 	binormal = normalize(cross(normal, tangent)); | ||||||
|  | 	return normal; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec3 unpack_normal(vec4 rgba) { | ||||||
|  | 	vec3 n = fma(rgba.xzy, vec3(2.0, 2.0, -2.0), vec3(-1.0, -1.0, 1.0)); | ||||||
|  | 	return n; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec4 pack_normal(vec3 n, float a) { | ||||||
|  | 	return vec4(fma(n.xzy, vec3(0.5, -0.5, 0.5), vec3(0.5)), a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float random(in vec2 xy) { | ||||||
|  | 	return fract(sin(dot(xy, vec2(12.9898, 78.233))) * 43758.5453); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec2 rotate(vec2 v, float cosa, float sina) { | ||||||
|  | 	return vec2(fma(cosa, v.x, - sina * v.y), fma(sina, v.x, cosa * v.y)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Moves a point around a pivot point. | ||||||
|  | vec2 rotate_around(vec2 point, vec2 pivot, float angle){ | ||||||
|  | 	float x = pivot.x + (point.x - pivot.x) * cos(angle) - (point.y - pivot.y) * sin(angle); | ||||||
|  | 	float y = pivot.y + (point.x - pivot.x) * sin(angle) + (point.y - pivot.y) * cos(angle); | ||||||
|  | 	return vec2(x, y); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec4 height_blend(vec4 a_value, float a_height, vec4 b_value, float b_height, float blend) { | ||||||
|  | 	if(height_blending) { | ||||||
|  | 		float ma = max(a_height + (1.0 - blend), b_height + blend) - (1.001 - blend_sharpness); | ||||||
|  | 	    float b1 = max(a_height + (1.0 - blend) - ma, 0.0); | ||||||
|  | 	    float b2 = max(b_height + blend - ma, 0.0); | ||||||
|  | 	    return (a_value * b1 + b_value * b2) / (b1 + b2); | ||||||
|  | 	} else { | ||||||
|  | 		float contrast = 1.0 - blend_sharpness; | ||||||
|  | 		float factor = (blend - contrast) / contrast; | ||||||
|  | 		return mix(a_value, b_value, clamp(factor, 0.0, 1.0)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec2 detiling(vec2 uv, vec2 uv_center, int mat_id, inout float normal_rotation){ | ||||||
|  | 	if (_texture_detile_array[mat_id] >= 0.001){ | ||||||
|  | 		uv_center = floor(uv_center) + 0.5; | ||||||
|  | 		float detile = fma(random(uv_center), 2.0, -1.0) * TAU * _texture_detile_array[mat_id]; // -180deg to 180deg | ||||||
|  | 		uv = rotate_around(uv, uv_center, detile); | ||||||
|  | 		// Accumulate total rotation for normal rotation | ||||||
|  | 		normal_rotation += detile; | ||||||
|  | 	} | ||||||
|  | 	return uv; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec2 rotate_normal(vec2 normal, float angle) { | ||||||
|  | 	angle = fma(PI, 0.5, angle); | ||||||
|  | 	float new_y = dot(vec2(cos(angle), sin(angle)), normal); | ||||||
|  | 	angle = fma(PI, -0.5, angle); | ||||||
|  | 	float new_x = dot(vec2(cos(angle) ,sin(angle)) ,normal); | ||||||
|  | 	return vec2(new_x, new_y); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 2-4 lookups | ||||||
|  | void get_material(vec2 base_uv, uint control, ivec3 iuv_center, vec3 normal, out Material out_mat) { | ||||||
|  | 	out_mat = Material(vec4(0.), vec4(0.), 0, 0, 0.0); | ||||||
|  | 	vec2 uv_center = vec2(iuv_center.xy); | ||||||
|  | 	int region = iuv_center.z; | ||||||
|  | 
 | ||||||
|  | 	// Enable Autoshader if outside regions or painted in regions, otherwise manual painted | ||||||
|  | 	bool auto_shader = region < 0 || bool(control & 0x1u); | ||||||
|  | 	out_mat.base = int(auto_shader) * auto_base_texture + int(!auto_shader) * int(control >>27u & 0x1Fu); | ||||||
|  | 	out_mat.over = int(auto_shader) * auto_overlay_texture + int(!auto_shader) * int(control >> 22u & 0x1Fu); | ||||||
|  | 	out_mat.blend = float(auto_shader) * clamp( | ||||||
|  | 			dot(vec3(0., 1., 0.), normal * auto_slope * 2. - (auto_slope * 2. - 1.)) | ||||||
|  | 			- auto_height_reduction * .01 * v_vertex.y // Reduce as vertices get higher | ||||||
|  | 			, 0., 1.) + | ||||||
|  | 			 float(!auto_shader) * float(control >>14u & 0xFFu) * 0.003921568627450; // 1./255.0 | ||||||
|  | 
 | ||||||
|  | 	// Control map scale & rotation, apply to both base and | ||||||
|  | 	// uv_center. Translate uv center to the current region. | ||||||
|  | 	uv_center += _region_locations[region] * _region_size; | ||||||
|  | 	// Define base scale from control map value as array index. 0.5 as baseline. | ||||||
|  | 	float[8] scale_array = { 0.5, 0.4, 0.3, 0.2, 0.1, 0.8, 0.7, 0.6}; | ||||||
|  | 	float control_scale = scale_array[(control >>7u & 0x7u)]; | ||||||
|  | 	base_uv *= control_scale; | ||||||
|  | 	uv_center *=  control_scale; | ||||||
|  | 	// calculate baseline derivatives | ||||||
|  | 	vec2 ddx = dFdxCoarse(base_uv); | ||||||
|  | 	vec2 ddy = dFdyCoarse(base_uv); | ||||||
|  | 	// Apply global uv rotation from control map. | ||||||
|  | 	float uv_rotation = float(control >>10u & 0xFu) / 16. * TAU; | ||||||
|  | 	base_uv = rotate_around(base_uv, vec2(0), uv_rotation); | ||||||
|  | 	uv_center = rotate_around(uv_center, vec2(0), uv_rotation); | ||||||
|  | 
 | ||||||
|  | 	vec2 matUV = base_uv; | ||||||
|  | 	vec4 albedo_ht = vec4(0.); | ||||||
|  | 	vec4 normal_rg = vec4(0.5f, 0.5f, 1.0f, 1.0f); | ||||||
|  | 	vec4 albedo_far = vec4(0.); | ||||||
|  | 	vec4 normal_far = vec4(0.5f, 0.5f, 1.0f, 1.0f); | ||||||
|  | 	float mat_scale = _texture_uv_scale_array[out_mat.base]; | ||||||
|  | 	float normal_angle = uv_rotation; | ||||||
|  | 	vec2 ddx1 = ddx; | ||||||
|  | 	vec2 ddy1 = ddy; | ||||||
|  | 
 | ||||||
|  | 	matUV = detiling(base_uv * mat_scale, uv_center * mat_scale, out_mat.base, normal_angle); | ||||||
|  | 	ddx1 *= mat_scale; | ||||||
|  | 	ddy1 *= mat_scale; | ||||||
|  | 	albedo_ht = textureGrad(_texture_array_albedo, vec3(matUV, float(out_mat.base)), ddx1, ddy1); | ||||||
|  | 	normal_rg = textureGrad(_texture_array_normal, vec3(matUV, float(out_mat.base)), ddx1, ddy1); | ||||||
|  | 
 | ||||||
|  | 	// Unpack & rotate base normal for blending | ||||||
|  | 	normal_rg.xz = unpack_normal(normal_rg).xz; | ||||||
|  | 	normal_rg.xz = rotate_normal(normal_rg.xz, normal_angle); | ||||||
|  | 
 | ||||||
|  | 	// Apply color to base | ||||||
|  | 	albedo_ht.rgb *= _texture_color_array[out_mat.base].rgb; | ||||||
|  | 
 | ||||||
|  | 	// Setup overlay texture to blend | ||||||
|  | 	float mat_scale2 = _texture_uv_scale_array[out_mat.over]; | ||||||
|  | 	float normal_angle2 = uv_rotation; | ||||||
|  | 	vec2 matUV2 = detiling(base_uv * mat_scale2, uv_center * mat_scale2, out_mat.over, normal_angle2); | ||||||
|  | 	vec2 ddx2 = ddx * mat_scale2; | ||||||
|  | 	vec2 ddy2 = ddy * mat_scale2; | ||||||
|  | 	vec4 albedo_ht2 = textureGrad(_texture_array_albedo, vec3(matUV2, float(out_mat.over)), ddx2, ddy2); | ||||||
|  | 	vec4 normal_rg2 = textureGrad(_texture_array_normal, vec3(matUV2, float(out_mat.over)), ddx2, ddy2); | ||||||
|  | 
 | ||||||
|  | 	// Though it would seem having the above lookups in this block, or removing the branch would | ||||||
|  | 	// be more optimal, the first introduces artifacts #276, and the second is noticably slower. | ||||||
|  | 	// It seems the branching off dual scaling and the color array lookup is more optimal. | ||||||
|  | 	if (out_mat.blend > 0.f) { | ||||||
|  | 		// Unpack & rotate overlay normal for blending | ||||||
|  | 		normal_rg2.xz = unpack_normal(normal_rg2).xz; | ||||||
|  | 		normal_rg2.xz = rotate_normal(normal_rg2.xz, normal_angle2); | ||||||
|  | 
 | ||||||
|  | 		// Apply color to overlay | ||||||
|  | 		albedo_ht2.rgb *= _texture_color_array[out_mat.over].rgb; | ||||||
|  | 
 | ||||||
|  | 		// Blend overlay and base | ||||||
|  | 		albedo_ht = height_blend(albedo_ht, albedo_ht.a, albedo_ht2, albedo_ht2.a, out_mat.blend); | ||||||
|  | 		normal_rg = height_blend(normal_rg, albedo_ht.a, normal_rg2, albedo_ht2.a, out_mat.blend); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Repack normals and return material | ||||||
|  | 	normal_rg = pack_normal(normal_rg.xyz, normal_rg.a); | ||||||
|  | 	out_mat.alb_ht = albedo_ht; | ||||||
|  | 	out_mat.nrm_rg = normal_rg; | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float blend_weights(float weight, float detail) { | ||||||
|  | 	weight = smoothstep(0.0, 1.0, weight); | ||||||
|  | 	weight = sqrt(weight * 0.5); | ||||||
|  | 	float result = max(0.1 * weight, fma(10.0, (weight + detail), 1.0f - (detail + 10.0))); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void fragment() { | ||||||
|  | 	// Recover UVs | ||||||
|  | 	vec2 uv = UV + v_uv_offset; | ||||||
|  | 	vec2 uv2 = UV2 + v_uv2_offset; | ||||||
|  | 
 | ||||||
|  | 	// Calculate Terrain Normals. 4 lookups | ||||||
|  | 	vec3 w_tangent, w_binormal; | ||||||
|  | 	vec3 w_normal = get_normal(uv2, w_tangent, w_binormal); | ||||||
|  | 	NORMAL = mat3(VIEW_MATRIX) * w_normal; | ||||||
|  | 	TANGENT = mat3(VIEW_MATRIX) * w_tangent; | ||||||
|  | 	BINORMAL = mat3(VIEW_MATRIX) * w_binormal; | ||||||
|  | 
 | ||||||
|  | 	// Idenfity 4 vertices surrounding this pixel | ||||||
|  | 	vec2 texel_pos = uv; | ||||||
|  | 	highp vec2 texel_pos_floor = floor(uv); | ||||||
|  | 
 | ||||||
|  | 	// Create a cross hatch grid of alternating 0/1 horizontal and vertical stripes 1 unit wide in XY | ||||||
|  | 	vec4 mirror = vec4(fract(texel_pos_floor * 0.5) * 2.0, 1.0, 1.0); | ||||||
|  | 	// And the opposite grid in ZW | ||||||
|  | 	mirror.zw = vec2(1.0) - mirror.xy; | ||||||
|  | 
 | ||||||
|  | 	// Get the region and control map ID for the vertices | ||||||
|  | 	ivec3 indexUV[4] = { | ||||||
|  | 		get_region_uv(texel_pos_floor + mirror.xy), | ||||||
|  | 		get_region_uv(texel_pos_floor + mirror.xw), | ||||||
|  | 		get_region_uv(texel_pos_floor + mirror.zy), | ||||||
|  | 		get_region_uv(texel_pos_floor + mirror.zw) | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	// Lookup adjacent vertices. 4 lookups | ||||||
|  | 	uint control[4] = { | ||||||
|  | 		texelFetch(_control_maps, indexUV[0], 0).r, | ||||||
|  | 		texelFetch(_control_maps, indexUV[1], 0).r, | ||||||
|  | 		texelFetch(_control_maps, indexUV[2], 0).r, | ||||||
|  | 		texelFetch(_control_maps, indexUV[3], 0).r | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	// Get the textures for each vertex. 8-16 lookups (2-4 ea) | ||||||
|  | 	Material mat[4]; | ||||||
|  | 	get_material(uv, control[0], indexUV[0], w_normal, mat[0]); | ||||||
|  | 	get_material(uv, control[1], indexUV[1], w_normal, mat[1]); | ||||||
|  | 	get_material(uv, control[2], indexUV[2], w_normal, mat[2]); | ||||||
|  | 	get_material(uv, control[3], indexUV[3], w_normal, mat[3]); | ||||||
|  | 
 | ||||||
|  | 	// Calculate weight for the pixel position between the vertices | ||||||
|  | 	// Bilinear interpolation of difference of uv and floor(uv) | ||||||
|  | 	vec2 weights1 = clamp(texel_pos - texel_pos_floor, 0, 1); | ||||||
|  | 	weights1 = mix(weights1, vec2(1.0) - weights1, mirror.xy); | ||||||
|  | 	vec2 weights0 = vec2(1.0) - weights1; | ||||||
|  | 	// Adjust final weights by texture's height/depth + noise. 1 lookup | ||||||
|  | 	float noise3 = texture(noise_texture, uv*noise3_scale).r; | ||||||
|  | 	vec4 weights; | ||||||
|  | 	weights.x = blend_weights(weights0.x * weights0.y, clamp(mat[0].alb_ht.a + noise3, 0., 1.)); | ||||||
|  | 	weights.y = blend_weights(weights0.x * weights1.y, clamp(mat[1].alb_ht.a + noise3, 0., 1.)); | ||||||
|  | 	weights.z = blend_weights(weights1.x * weights0.y, clamp(mat[2].alb_ht.a + noise3, 0., 1.)); | ||||||
|  | 	weights.w = blend_weights(weights1.x * weights1.y, clamp(mat[3].alb_ht.a + noise3, 0., 1.)); | ||||||
|  | 	float weight_sum = weights.x + weights.y + weights.z + weights.w; | ||||||
|  | 	float weight_inv = 1.0 / weight_sum; | ||||||
|  | 
 | ||||||
|  | 	// Weighted average of albedo & height | ||||||
|  | 	vec4 albedo_height = weight_inv * ( | ||||||
|  | 		mat[0].alb_ht * weights.x + | ||||||
|  | 		mat[1].alb_ht * weights.y + | ||||||
|  | 		mat[2].alb_ht * weights.z + | ||||||
|  | 		mat[3].alb_ht * weights.w ); | ||||||
|  | 
 | ||||||
|  | 	// Weighted average of normal & rough | ||||||
|  | 	vec4 normal_rough = weight_inv * ( | ||||||
|  | 		mat[0].nrm_rg * weights.x + | ||||||
|  | 		mat[1].nrm_rg * weights.y + | ||||||
|  | 		mat[2].nrm_rg * weights.z + | ||||||
|  | 		mat[3].nrm_rg * weights.w ); | ||||||
|  | 
 | ||||||
|  | 	// Determine if we're in a region or not (region_uv.z>0) | ||||||
|  | 	vec3 region_uv = get_region_uv2(uv2); | ||||||
|  | 
 | ||||||
|  | 	// Colormap. 1 lookup | ||||||
|  | 	vec4 color_map = vec4(1., 1., 1., .5); | ||||||
|  | 	if (region_uv.z >= 0.) { | ||||||
|  | 		float lod = textureQueryLod(_color_maps, uv2.xy).y; | ||||||
|  | 		color_map = textureLod(_color_maps, region_uv, lod); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Macro variation. 2 Lookups | ||||||
|  | 	float noise1 = texture(noise_texture, rotate(uv*noise1_scale * .1, cos(noise1_angle), sin(noise1_angle)) + noise1_offset).r; | ||||||
|  | 	float noise2 = texture(noise_texture, uv*noise2_scale * .1).r; | ||||||
|  | 	vec3 macrov = mix(macro_variation1, vec3(1.), clamp(noise1 + v_vertex_xz_dist * .0002, 0., 1.)); | ||||||
|  | 	macrov *= mix(macro_variation2, vec3(1.), clamp(noise2 + v_vertex_xz_dist * .0002, 0., 1.)); | ||||||
|  | 
 | ||||||
|  | 	// Wetness/roughness modifier, converting 0-1 range to -1 to 1 range | ||||||
|  | 	float roughness = fma(color_map.a - 0.5, 2.0, normal_rough.a); | ||||||
|  | 
 | ||||||
|  | 	// Apply PBR | ||||||
|  | 	ALBEDO = albedo_height.rgb * color_map.rgb * macrov; | ||||||
|  | 	ROUGHNESS = roughness; | ||||||
|  | 	SPECULAR = 1. - normal_rough.a; | ||||||
|  | 	NORMAL_MAP = normal_rough.rgb; | ||||||
|  | 	NORMAL_MAP_DEPTH = 1.0; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | " | ||||||
| @ -34,6 +34,8 @@ func set_state_for_player(player: WorldPlayer) -> void: | |||||||
| 	wedge_label.set_enabled(player.wedge != null) | 	wedge_label.set_enabled(player.wedge != null) | ||||||
| 	special_label.set_enabled(player.special != null) | 	special_label.set_enabled(player.special != null) | ||||||
| 	putter_label.set_enabled(player.putter != null) | 	putter_label.set_enabled(player.putter != null) | ||||||
|  | 	if player.shot_setup: | ||||||
|  | 		value = player.shot_setup.club | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| func _get_club_label(club: Club.Type) -> Label: | func _get_club_label(club: Club.Type) -> Label: | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								src/ui/shot_hud/life_bar/life_bar.gd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/ui/shot_hud/life_bar/life_bar.gd
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | @tool | ||||||
|  | extends TextureProgressBar | ||||||
|  | 
 | ||||||
|  | @export var damage_delay := 1.0 | ||||||
|  | @export var damage_tween_time := 0.4 | ||||||
|  | 
 | ||||||
|  | @onready var damage_bar: TextureProgressBar = %DamageBar | ||||||
|  | @onready var damage_update_timer: Timer = $DamageUpdateTimer | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_value_changed(_value: float) -> void: | ||||||
|  | 	damage_update_timer.start(damage_delay) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func _on_damage_update_timer_timeout() -> void: | ||||||
|  | 	var tween := get_tree().create_tween() | ||||||
|  | 	( | ||||||
|  | 		tween | ||||||
|  | 		. tween_property(damage_bar, "value", value, damage_tween_time) | ||||||
|  | 		. set_trans(Tween.TRANS_CUBIC) | ||||||
|  | 		. set_ease(Tween.EASE_OUT) | ||||||
|  | 	) | ||||||
							
								
								
									
										46
									
								
								src/ui/shot_hud/life_bar/life_bar.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/ui/shot_hud/life_bar/life_bar.tscn
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | [gd_scene load_steps=5 format=3 uid="uid://dmciuk3pbjsae"] | ||||||
|  | 
 | ||||||
|  | [ext_resource type="Texture2D" uid="uid://dtdqninlnu10o" path="res://assets/ui/lifebar_fill_grey.png" id="1_nhls1"] | ||||||
|  | [ext_resource type="Script" path="res://src/ui/shot_hud/life_bar/life_bar.gd" id="2_6jpmf"] | ||||||
|  | [ext_resource type="Texture2D" uid="uid://bvwh0yunmvirp" path="res://assets/ui/lifebar_patch.png" id="3_8s5ot"] | ||||||
|  | [ext_resource type="Texture2D" uid="uid://dv8757eh7bgmm" path="res://assets/ui/lifebar_fill_damage.png" id="4_ohduq"] | ||||||
|  | 
 | ||||||
|  | [node name="LifeBar" type="TextureProgressBar"] | ||||||
|  | anchors_preset = 15 | ||||||
|  | anchor_right = 1.0 | ||||||
|  | anchor_bottom = 1.0 | ||||||
|  | grow_horizontal = 2 | ||||||
|  | grow_vertical = 2 | ||||||
|  | step = 0.01 | ||||||
|  | nine_patch_stretch = true | ||||||
|  | stretch_margin_left = 32 | ||||||
|  | stretch_margin_top = 16 | ||||||
|  | stretch_margin_right = 32 | ||||||
|  | stretch_margin_bottom = 16 | ||||||
|  | texture_progress = ExtResource("1_nhls1") | ||||||
|  | script = ExtResource("2_6jpmf") | ||||||
|  | damage_tween_time = 0.6 | ||||||
|  | 
 | ||||||
|  | [node name="DamageBar" type="TextureProgressBar" parent="."] | ||||||
|  | unique_name_in_owner = true | ||||||
|  | show_behind_parent = true | ||||||
|  | layout_mode = 1 | ||||||
|  | anchors_preset = 15 | ||||||
|  | anchor_right = 1.0 | ||||||
|  | anchor_bottom = 1.0 | ||||||
|  | grow_horizontal = 2 | ||||||
|  | grow_vertical = 2 | ||||||
|  | step = 0.01 | ||||||
|  | nine_patch_stretch = true | ||||||
|  | stretch_margin_left = 32 | ||||||
|  | stretch_margin_top = 16 | ||||||
|  | stretch_margin_right = 32 | ||||||
|  | stretch_margin_bottom = 16 | ||||||
|  | texture_under = ExtResource("3_8s5ot") | ||||||
|  | texture_progress = ExtResource("4_ohduq") | ||||||
|  | tint_progress = Color(0.9, 0, 0, 1) | ||||||
|  | 
 | ||||||
|  | [node name="DamageUpdateTimer" type="Timer" parent="."] | ||||||
|  | 
 | ||||||
|  | [connection signal="value_changed" from="." to="." method="_on_value_changed"] | ||||||
|  | [connection signal="timeout" from="DamageUpdateTimer" to="." method="_on_damage_update_timer_timeout"] | ||||||
| @ -1,6 +1,13 @@ | |||||||
| class_name ShotHUD extends Control | class_name ShotHUD extends Control | ||||||
| ## HUD for main gameplay loop | ## HUD for main gameplay loop | ||||||
| 
 | 
 | ||||||
|  | ## Scale factor for the life bar rumble intensity on taking damage | ||||||
|  | const LIFE_BAR_DAMAGE_RUMBLE_SCALE := 0.2 | ||||||
|  | ## Time it takes to dampen the life bar rumble on taking damage, in seconds | ||||||
|  | const LIFE_BAR_DAMAGE_RUMBLE_TIME := 0.2 | ||||||
|  | 
 | ||||||
|  | var _life_signal: Signal | ||||||
|  | 
 | ||||||
| @onready var power_bar: TextureProgressBar = %PowerBar | @onready var power_bar: TextureProgressBar = %PowerBar | ||||||
| @onready var curve_bar: ProgressBar = %CurveBar | @onready var curve_bar: ProgressBar = %CurveBar | ||||||
| @onready var life_bar: TextureProgressBar = %LifeBar | @onready var life_bar: TextureProgressBar = %LifeBar | ||||||
| @ -17,6 +24,8 @@ class_name ShotHUD extends Control | |||||||
| 
 | 
 | ||||||
| @onready var _player_name: Label = %PlayerName | @onready var _player_name: Label = %PlayerName | ||||||
| 
 | 
 | ||||||
|  | @onready var _life_bar_rumbler: Rumbler = %LifeBarRumbler | ||||||
|  | 
 | ||||||
| @onready var _state: AnimationNodeStateMachinePlayback = hud_state_machine["parameters/playback"] | @onready var _state: AnimationNodeStateMachinePlayback = hud_state_machine["parameters/playback"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -28,6 +37,14 @@ func set_state_for_player(player: WorldPlayer) -> void: | |||||||
| 	# TODO animate on life loss? | 	# TODO animate on life loss? | ||||||
| 	life_bar.value = player.life | 	life_bar.value = player.life | ||||||
| 	life_bar.tint_progress = player.color | 	life_bar.tint_progress = player.color | ||||||
|  | 
 | ||||||
|  | 	# TODO this is soooooo wack... | ||||||
|  | 	# TODO we should just revert to having distinct ShotHUDs for each player | ||||||
|  | 	if _life_signal and _life_signal.is_connected(set_life_value): | ||||||
|  | 		_life_signal.disconnect(set_life_value) | ||||||
|  | 	_life_signal = player.on_life_changed | ||||||
|  | 	_life_signal.connect(set_life_value) | ||||||
|  | 
 | ||||||
| 	# TODO special equipment | 	# TODO special equipment | ||||||
| 	# TODO abilities | 	# TODO abilities | ||||||
| 
 | 
 | ||||||
| @ -70,3 +87,24 @@ func play_nice_animation() -> void: | |||||||
| 
 | 
 | ||||||
| func play_wasted_animation() -> void: | func play_wasted_animation() -> void: | ||||||
| 	_wasted_animation.play("display") | 	_wasted_animation.play("display") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Set the value of the life bar, potentially playing some kind of effect in response. | ||||||
|  | ## | ||||||
|  | ## To set the life bar without triggering an effect, set it directly with `life_bar.value` | ||||||
|  | func set_life_value(new_value: float) -> void: | ||||||
|  | 	var difference := new_value - life_bar.value | ||||||
|  | 	if difference < 0: | ||||||
|  | 		# Taking damage | ||||||
|  | 		_life_bar_rumbler.intensity = LIFE_BAR_DAMAGE_RUMBLE_SCALE * abs(difference) | ||||||
|  | 		var tween := get_tree().create_tween() | ||||||
|  | 		( | ||||||
|  | 			tween | ||||||
|  | 			. tween_property(_life_bar_rumbler, "intensity", 0, LIFE_BAR_DAMAGE_RUMBLE_TIME) | ||||||
|  | 			. set_trans(Tween.TRANS_CUBIC) | ||||||
|  | 		) | ||||||
|  | 	elif difference > 0: | ||||||
|  | 		# Restoring health | ||||||
|  | 		# TODO: something for this? | ||||||
|  | 		pass | ||||||
|  | 	life_bar.value = new_value | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| [gd_scene load_steps=34 format=3 uid="uid://c4ifdiohng830"] | [gd_scene load_steps=33 format=3 uid="uid://c4ifdiohng830"] | ||||||
| 
 | 
 | ||||||
| [ext_resource type="Script" path="res://src/ui/shot_hud/shot_hud.gd" id="1_x5b4c"] | [ext_resource type="Script" path="res://src/ui/shot_hud/shot_hud.gd" id="1_x5b4c"] | ||||||
| [ext_resource type="Shader" path="res://src/shaders/canvas_retro.gdshader" id="1_ybxxp"] | [ext_resource type="Shader" path="res://src/shaders/canvas_retro.gdshader" id="1_ybxxp"] | ||||||
| @ -8,8 +8,7 @@ | |||||||
| [ext_resource type="Texture2D" uid="uid://76fjx2ukavqe" path="res://assets/ui/power_gauge_fill.png" id="5_3i1yq"] | [ext_resource type="Texture2D" uid="uid://76fjx2ukavqe" path="res://assets/ui/power_gauge_fill.png" id="5_3i1yq"] | ||||||
| [ext_resource type="Texture2D" uid="uid://4a8tvjgwegv3" path="res://assets/ui/power_gauge_tab.png" id="6_sw48q"] | [ext_resource type="Texture2D" uid="uid://4a8tvjgwegv3" path="res://assets/ui/power_gauge_tab.png" id="6_sw48q"] | ||||||
| [ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="8_bejx4"] | [ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="8_bejx4"] | ||||||
| [ext_resource type="Texture2D" uid="uid://bvwh0yunmvirp" path="res://assets/ui/lifebar_patch.png" id="9_4f1d7"] | [ext_resource type="PackedScene" uid="uid://dmciuk3pbjsae" path="res://src/ui/shot_hud/life_bar/life_bar.tscn" id="9_w1fiw"] | ||||||
| [ext_resource type="Texture2D" uid="uid://dtdqninlnu10o" path="res://assets/ui/lifebar_fill_grey.png" id="10_130v7"] |  | ||||||
| 
 | 
 | ||||||
| [sub_resource type="Animation" id="Animation_3xds6"] | [sub_resource type="Animation" id="Animation_3xds6"] | ||||||
| resource_name = "RESET" | resource_name = "RESET" | ||||||
| @ -276,7 +275,7 @@ _data = { | |||||||
| [sub_resource type="ShaderMaterial" id="ShaderMaterial_afsun"] | [sub_resource type="ShaderMaterial" id="ShaderMaterial_afsun"] | ||||||
| shader = ExtResource("1_ybxxp") | shader = ExtResource("1_ybxxp") | ||||||
| shader_parameter/change_color_depth = true | shader_parameter/change_color_depth = true | ||||||
| shader_parameter/target_color_depth = 3 | shader_parameter/target_color_depth = 4 | ||||||
| shader_parameter/dithering = true | shader_parameter/dithering = true | ||||||
| shader_parameter/scale_resolution = true | shader_parameter/scale_resolution = true | ||||||
| shader_parameter/target_resolution_scale = 3 | shader_parameter/target_resolution_scale = 3 | ||||||
| @ -698,16 +697,15 @@ theme_override_fonts/font = ExtResource("8_bejx4") | |||||||
| theme_override_font_sizes/font_size = 32 | theme_override_font_sizes/font_size = 32 | ||||||
| text = "PLAYER NAME" | text = "PLAYER NAME" | ||||||
| 
 | 
 | ||||||
| [node name="LifeBar" type="TextureProgressBar" parent="SouthWest/VBoxContainer"] | [node name="MarginContainer" type="MarginContainer" parent="SouthWest/VBoxContainer"] | ||||||
|  | layout_mode = 2 | ||||||
|  | 
 | ||||||
|  | [node name="LifeBarRumbler" type="Control" parent="SouthWest/VBoxContainer/MarginContainer"] | ||||||
| unique_name_in_owner = true | unique_name_in_owner = true | ||||||
| custom_minimum_size = Vector2(0, 48) | custom_minimum_size = Vector2(0, 48) | ||||||
| layout_mode = 2 | layout_mode = 2 | ||||||
| min_value = -4.0 | script = ExtResource("3_6groq") | ||||||
| step = 0.01 | 
 | ||||||
| nine_patch_stretch = true | [node name="LifeBar" parent="SouthWest/VBoxContainer/MarginContainer/LifeBarRumbler" instance=ExtResource("9_w1fiw")] | ||||||
| stretch_margin_left = 32 | unique_name_in_owner = true | ||||||
| stretch_margin_top = 16 | layout_mode = 1 | ||||||
| stretch_margin_right = 32 |  | ||||||
| stretch_margin_bottom = 16 |  | ||||||
| texture_under = ExtResource("9_4f1d7") |  | ||||||
| texture_progress = ExtResource("10_130v7") |  | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user