diff --git a/viewport/dynamic_split_screen/README.md b/viewport/dynamic_split_screen/README.md index 42686d76..97ae4bb7 100644 --- a/viewport/dynamic_split_screen/README.md +++ b/viewport/dynamic_split_screen/README.md @@ -3,12 +3,9 @@ This sample project showcases an implementation of dynamic split screen, also called Voronoi split screen. -Language: [GDSL](https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/shading_language.html) and GDScript +Language: [Godot shader language](https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/shading_language.html) and GDScript -Renderer: GLES 2 - -Note: An HTML5 export is testable -[here](https://benjaminnavarro.github.io/godot_dynamic_split_screen/index.html). +Renderer: Forward Mobile Check out this demo on the asset library: https://godotengine.org/asset-library/asset/541 @@ -38,14 +35,14 @@ distance otherwise. ## How to use it -Open and launch the project inside the Godot engine and then -you can use WASD keys to move the first player and IJKL keys -to move the second one. +Open and launch the project inside the Godot engine, then +use WASD to move the first player (in red) and IJKL (or arrow keys) +to move the second player (in blue). -The `Cameras` node has parameters to tune the distance at +The `camera_controller.gd` script sets parameters to tune the distance at which the screen splits and also the width and color of the splitting line. ## Screenshots -![Screenshots](screenshots/splitscreen.png) +![Screenshots](screenshots/dynamic_split_screen.webp) diff --git a/viewport/dynamic_split_screen/camera_controller.gd b/viewport/dynamic_split_screen/camera_controller.gd index da5c2a4a..ee0575bc 100644 --- a/viewport/dynamic_split_screen/camera_controller.gd +++ b/viewport/dynamic_split_screen/camera_controller.gd @@ -32,6 +32,7 @@ extends Node3D @onready var camera1 = viewport1.get_node(^"Camera1") @onready var camera2 = viewport2.get_node(^"Camera2") +var viewport_base_height = ProjectSettings.get_setting("display/window/size/viewport_height") func _ready(): _on_size_changed() diff --git a/viewport/dynamic_split_screen/player.gd b/viewport/dynamic_split_screen/player.gd index d2b091d6..a199d839 100644 --- a/viewport/dynamic_split_screen/player.gd +++ b/viewport/dynamic_split_screen/player.gd @@ -3,7 +3,7 @@ extends CharacterBody3D # Moves the player @export_range(1, 2) var player_id: int = 1 -@export var walk_speed: float = 2.5 +@export var walk_speed: float = 2 func _physics_process(_delta): diff --git a/viewport/dynamic_split_screen/project.godot b/viewport/dynamic_split_screen/project.godot index f95aa660..0995b189 100644 --- a/viewport/dynamic_split_screen/project.godot +++ b/viewport/dynamic_split_screen/project.godot @@ -82,6 +82,7 @@ common/physics_ticks_per_second=120 [rendering] -anti_aliasing/quality/msaa_3d=2 +renderer/rendering_method="mobile" environment/defaults/default_clear_color=Color(1, 1, 1, 1) +anti_aliasing/quality/msaa_3d=2 environment/default_environment="res://default_env.tres" diff --git a/viewport/dynamic_split_screen/screenshots/dynamic_split_screen.webp b/viewport/dynamic_split_screen/screenshots/dynamic_split_screen.webp new file mode 100644 index 00000000..091d5850 Binary files /dev/null and b/viewport/dynamic_split_screen/screenshots/dynamic_split_screen.webp differ diff --git a/viewport/dynamic_split_screen/screenshots/splitscreen.png b/viewport/dynamic_split_screen/screenshots/splitscreen.png deleted file mode 100644 index e4967dea..00000000 Binary files a/viewport/dynamic_split_screen/screenshots/splitscreen.png and /dev/null differ diff --git a/viewport/dynamic_split_screen/split_screen.gdshader b/viewport/dynamic_split_screen/split_screen.gdshader index f0c69c0b..bc3d6ac1 100644 --- a/viewport/dynamic_split_screen/split_screen.gdshader +++ b/viewport/dynamic_split_screen/split_screen.gdshader @@ -1,18 +1,17 @@ shader_type canvas_item; render_mode unshaded; -uniform vec2 viewport_size; // size in pixels of the viewport. Cannot be access from the shader in GLES2 +uniform vec2 viewport_size; // size in pixels of the viewport uniform sampler2D viewport1 : source_color; uniform sampler2D viewport2 : source_color; -uniform bool split_active; // true: split screen, false: use view1 -uniform vec2 player1_position; // position of player 1 un UV coordinates -uniform vec2 player2_position; // position of player 2 un UV coordinates -uniform float split_line_thickness; // width of the split boder -uniform vec4 split_line_color; // color of the split border - +uniform bool split_active; // true: split screen, false: use view1 +uniform vec2 player1_position; // position of player 1 un UV coordinates +uniform vec2 player2_position; // position of player 2 un UV coordinates +uniform float split_line_thickness : hint_range(0, 10, 0.1); // width of the split boder +uniform vec3 split_line_color : source_color; // color of the split border // from https://stackoverflow.com/questions/15276454/is-it-possible-to-draw-line-thickness-in-a-fragment-shader -float distanceToLine(vec2 p1, vec2 p2, vec2 point) { +float distance_to_line(vec2 p1, vec2 p2, vec2 point) { float a = p1.y - p2.y; float b = p2.x - p1.x; return abs(a * point.x + b * point.y + p1.x * p2.y - p2.x * p1.y) / sqrt(a * a + b * b); @@ -27,8 +26,8 @@ void fragment() { if (split_active) { vec2 dx = player2_position - player1_position; - float split_slope; + float split_slope; if (dx.y != 0.0) { split_slope = dx.x / dx.y; } else { @@ -38,29 +37,29 @@ void fragment() { vec2 split_origin = vec2(0.5, 0.5); vec2 split_line_start = vec2(0.0, height * ((split_origin.x - 0.0) * split_slope + split_origin.y)); vec2 split_line_end = vec2(width, height * ((split_origin.x - 1.0) * split_slope + split_origin.y)); - float distance_to_split_line = distanceToLine(split_line_start, split_line_end, vec2(UV.x * width, UV.y * height)); - // Draw split border if close enough - if (distance_to_split_line < split_line_thickness) { - COLOR = split_line_color; - } else { - float split_current_y = (split_origin.x - UV.x) * split_slope + split_origin.y; - float split_player1_position_y = (split_origin.x - player1_position.x) * split_slope + split_origin.y; + float split_current_y = (split_origin.x - UV.x) * split_slope + split_origin.y; + float split_player1_position_y = (split_origin.x - player1_position.x) * split_slope + split_origin.y; - // Check on which side of the split UV is and select the proper view - if (UV.y > split_current_y) { - if (player1_position.y > split_player1_position_y) { - COLOR = vec4(view1, 1.0); - } else { - COLOR = vec4(view2, 1.0); - } + // Check on which side of the split UV is and select the proper view. + if (UV.y > split_current_y) { + if (player1_position.y > split_player1_position_y) { + COLOR = vec4(view1, 1.0); } else { - if (player1_position.y < split_player1_position_y) { - COLOR = vec4(view1, 1.0); - } else { - COLOR = vec4(view2, 1.0); - } + COLOR = vec4(view2, 1.0); } + } else { + if (player1_position.y < split_player1_position_y) { + COLOR = vec4(view1, 1.0); + } else { + COLOR = vec4(view2, 1.0); + } + } + + float distance_to_split_line = distance_to_line(split_line_start, split_line_end, vec2(UV.x * width, UV.y * height)); + if (distance_to_split_line < split_line_thickness) { + // Draw antialiased split line. + COLOR.rgb = mix(split_line_color, COLOR.rgb, distance_to_split_line / split_line_thickness); } } else { COLOR = vec4(view1, 1.0); diff --git a/viewport/dynamic_split_screen/split_screen.tscn b/viewport/dynamic_split_screen/split_screen.tscn index 9f7c6f92..a5ebbbf2 100644 --- a/viewport/dynamic_split_screen/split_screen.tscn +++ b/viewport/dynamic_split_screen/split_screen.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=60 format=3 uid="uid://dksa68cph6y4b"] +[gd_scene load_steps=63 format=3 uid="uid://dksa68cph6y4b"] [ext_resource type="Script" path="res://camera_controller.gd" id="2"] [ext_resource type="Shader" path="res://split_screen.gdshader" id="3"] @@ -15,21 +15,26 @@ sky_material = SubResource("ProceduralSkyMaterial_16la2") [sub_resource type="Environment" id="Environment_vdrvu"] background_mode = 2 sky = SubResource("Sky_i64ko") +ambient_light_source = 2 +ambient_light_color = Color(0.79, 0.8775, 1, 1) +ambient_light_sky_contribution = 0.0 +ambient_light_energy = 0.33 tonemap_mode = 2 glow_enabled = true [sub_resource type="ShaderMaterial" id="1"] shader = ExtResource("3") +shader_parameter/viewport_size = null +shader_parameter/split_active = false shader_parameter/player1_position = null shader_parameter/player2_position = null -shader_parameter/split_active = null -shader_parameter/split_line_color = null -shader_parameter/split_line_thickness = null -shader_parameter/viewport_size = null +shader_parameter/split_line_thickness = 10.0 +shader_parameter/split_line_color = Vector3(0, 1, 0) [sub_resource type="CapsuleMesh" id="2"] radius = 0.375 height = 1.75 +rings = 4 [sub_resource type="StandardMaterial3D" id="3"] albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1) @@ -38,9 +43,24 @@ albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1) radius = 0.375 height = 1.75 +[sub_resource type="TorusMesh" id="TorusMesh_abtrc"] +inner_radius = 0.4 +outer_radius = 0.6 +ring_segments = 6 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_63nwq"] +albedo_color = Color(0.5, 0.5, 0.5, 1) +emission_enabled = true +emission = Color(1, 0, 0, 1) + [sub_resource type="StandardMaterial3D" id="5"] albedo_color = Color(0.0784314, 0.411765, 0.933333, 1) +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wi7e2"] +albedo_color = Color(0.5, 0.5, 0.5, 1) +emission_enabled = true +emission = Color(0.12549, 0.501961, 1, 1) + [sub_resource type="StandardMaterial3D" id="6"] [sub_resource type="PlaneMesh" id="7"] @@ -183,12 +203,15 @@ albedo_color = Color(0.791675, 0.946163, 0.317723, 1) [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +light_energy = 0.75 shadow_enabled = true +shadow_bias = 0.03 +shadow_blur = 2.0 directional_shadow_mode = 0 directional_shadow_split_3 = 0.25 directional_shadow_blend_splits = true directional_shadow_fade_start = 1.0 -directional_shadow_max_distance = 25.0 +directional_shadow_max_distance = 12.0 [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_vdrvu") @@ -235,6 +258,14 @@ surface_material_override/0 = SubResource("3") [node name="CollisionShape3D" type="CollisionShape3D" parent="Player1"] shape = SubResource("4") +[node name="MeshInstance3D" type="MeshInstance3D" parent="Player1"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.6, 0) +mesh = SubResource("TorusMesh_abtrc") +surface_material_override/0 = SubResource("StandardMaterial3D_63nwq") + +[node name="OmniLight3D" type="OmniLight3D" parent="Player1"] +light_color = Color(1, 0, 0, 1) + [node name="Player2" type="CharacterBody3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.184, 0.875, 3.019) script = ExtResource("5") @@ -247,6 +278,15 @@ surface_material_override/0 = SubResource("5") [node name="CollisionShape3D" type="CollisionShape3D" parent="Player2"] shape = SubResource("4") +[node name="MeshInstance3D2" type="MeshInstance3D" parent="Player2"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.7, 0) +mesh = SubResource("TorusMesh_abtrc") +skeleton = NodePath("../../Player1") +surface_material_override/0 = SubResource("StandardMaterial3D_wi7e2") + +[node name="OmniLight3D" type="OmniLight3D" parent="Player2"] +light_color = Color(0.12549, 0.501961, 1, 1) + [node name="Ground" type="StaticBody3D" parent="."] [node name="Mesh" type="MeshInstance3D" parent="Ground"]