From 204aa83461b948b0f4f1f58a85dbe723c6162d28 Mon Sep 17 00:00:00 2001 From: Luke Hubmayer-Werner Date: Fri, 20 Dec 2019 21:39:03 +1030 Subject: [PATCH] Converted receptor ring to mesh with shader. WIP: Bezel changes --- Bezel.gd | 1 - Menu.gd | 2 + Receptors.gd | 91 +++++++++++++++++++++++++++++----------- main.tscn | 74 +++++++++++++++++++------------- shaders/receptors.shader | 66 +++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+), 54 deletions(-) create mode 100644 shaders/receptors.shader diff --git a/Bezel.gd b/Bezel.gd index f046488..3ecb2e1 100644 --- a/Bezel.gd +++ b/Bezel.gd @@ -8,7 +8,6 @@ func arc_point_list(center: Vector2, radius: float, angle_from:=0.0, angle_to:=3 var point_list = PoolVector2Array() for i in range(points): var angle = deg2rad(angle_from + i * (angle_to - angle_from) / (points-1)) -# point_list.push_back(center + Vector2(cos(angle), sin(angle)) * radius) point_list.push_back(center + polar2cartesian(radius, angle)) return point_list diff --git a/Menu.gd b/Menu.gd index fc7484e..73cfc5e 100644 --- a/Menu.gd +++ b/Menu.gd @@ -443,6 +443,8 @@ func finished_song(song_key, score_data): func _input(event): + if !visible: + return if event is InputEventScreenTouch: if event.pressed: var pos = event.position - get_global_transform_with_canvas().get_origin() diff --git a/Receptors.gd b/Receptors.gd index 7eb57c4..372e24a 100644 --- a/Receptors.gd +++ b/Receptors.gd @@ -1,9 +1,9 @@ #tool -extends Node2D +extends MeshInstance2D -var ring_px := 4 -var receptor_px := 24 -var shadow_px := 5 +var ring_px := 4 # Analogous to diameter +var receptor_px := 24 # Diameter +var shadow_px := 8 # Outer edge, analogous to radius var shadow_color := Color.black var center := Vector2(0.0, 0.0) @@ -28,8 +28,11 @@ func make_ring_mesh(inner_vertices: int, thickness: float, radius: float, skew:= # Outer polygon side-length = inner side-length / sin(inside angle/2) # inside angle for a polygon is pi-tau/n. We already precalculated tau/n for other purposes. var r2 = (radius + thickness*0.5)/sin((PI-angle_increment)/2) + var UV_r1 = r1/radius + var UV_r2 = r2/radius var vertex_list = PoolVector2Array() + var UV_list = PoolVector2Array() var inner_list = PoolVector2Array() var outer_list = PoolVector2Array() for i in inner_vertices: @@ -39,12 +42,16 @@ func make_ring_mesh(inner_vertices: int, thickness: float, radius: float, skew:= vertex_list.push_back(polar2cartesian(r2, angle_o)) inner_list.push_back(vertex_list[-2]) outer_list.push_back(vertex_list[-1]) + UV_list.push_back(polar2cartesian(UV_r1, angle_i)) + UV_list.push_back(polar2cartesian(UV_r2, angle_o)) if repeat_start: vertex_list.push_back(vertex_list[0]) vertex_list.push_back(vertex_list[1]) inner_list.push_back(vertex_list[0]) outer_list.push_back(vertex_list[1]) - return [vertex_list, inner_list, outer_list] + UV_list.push_back(UV_list[0]) + UV_list.push_back(UV_list[1]) + return [vertex_list, inner_list, outer_list, UV_list] func triangle_area(a: Vector2, b: Vector2, c: Vector2) -> float: return 0.5 * abs((a.x-c.x)*(b.y-a.y) - (a.x-b.x)*(c.y-a.y)) @@ -58,7 +65,6 @@ func arc_point_list(center: Vector2, radius: float, angle_from:=0.0, angle_to:=3 var point_list = PoolVector2Array() for i in range(points): var angle = deg2rad(angle_from + i * (angle_to - angle_from) / (points-1)) -# point_list.push_back(center + Vector2(cos(angle), sin(angle)) * radius) point_list.push_back(center + polar2cartesian(radius, angle)) return point_list @@ -67,42 +73,79 @@ func draw_old(circles:=true, shadows:=true): # Receptor ring var receptor_centers := arc_point_list(center, GameTheme.receptor_ring_radius, Rules.FIRST_COLUMN_ANGLE_DEG, Rules.FIRST_COLUMN_ANGLE_DEG+360.0-Rules.COLS_ANGLE_DEG, Rules.COLS) if shadows: - #draw_polyline(receptor_circle, shadow_color, ring_px + shadow_px, true) - draw_polyline(receptor_circle, Color.darkblue, ring_px + shadow_px, true) + draw_polyline(receptor_circle, shadow_color, ring_px + shadow_px/2, true) if circles: for i in range(len(receptor_centers)): -# draw_circle(receptor_centers[i], (receptor_px + shadow_px)/2, shadow_color) - draw_circle(receptor_centers[i], (receptor_px + shadow_px)/2, Color.darkblue) + draw_circle(receptor_centers[i], receptor_px/2 + shadow_px, shadow_color) draw_polyline(receptor_circle, GameTheme.receptor_color, ring_px, true) if circles: for i in range(len(receptor_centers)): draw_circle(receptor_centers[i], receptor_px/2, GameTheme.receptor_color) - -func _draw(): - draw_old(true, true) - var mesh_v = $VerticesSlider.value - var skew = $SkewSlider.value - var dbg_color = Color.red - var ring_thickness = receptor_px + shadow_px - var ring_vertices = make_ring_mesh(mesh_v, ring_thickness, GameTheme.receptor_ring_radius, skew) - var estimated_area = circumscribe_polygon_area(GameTheme.receptor_ring_radius+ring_thickness*0.5, mesh_v) - inscribe_polygon_area(GameTheme.receptor_ring_radius-ring_thickness*0.5, mesh_v) - var ideal_ring_area = PI * (pow(GameTheme.receptor_ring_radius+(receptor_px+shadow_px)/2, 2) - pow(GameTheme.receptor_ring_radius-(receptor_px+shadow_px)/2, 2)) - +func draw_tris(): + var dbg_color = Color(1.0, 0.0, 0.0, 1.0) draw_polyline(ring_vertices[0], dbg_color) draw_polyline(ring_vertices[1], dbg_color) draw_polyline(ring_vertices[2], dbg_color) + +var ring_vertices +func update_ring_mesh(): + var mesh_v = $VerticesSlider.value + var skew = $SkewSlider.value + var ring_thickness = receptor_px + shadow_px*2 + ring_vertices = make_ring_mesh(mesh_v, ring_thickness, GameTheme.receptor_ring_radius, skew) + var temp_mesh = ArrayMesh.new() + var mesh_arrays = [] + mesh_arrays.resize(Mesh.ARRAY_MAX) + mesh_arrays[Mesh.ARRAY_VERTEX] = ring_vertices[0] + mesh_arrays[Mesh.ARRAY_TEX_UV] = ring_vertices[3] +# mesh_arrays[Mesh.ARRAY_COLOR] = colors + temp_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, mesh_arrays) + mesh = temp_mesh + + +func _draw(): +# draw_old(true, true) +# draw_tris() + var mesh_v = $VerticesSlider.value + var skew = $SkewSlider.value + var ring_thickness = receptor_px + shadow_px*2 + var estimated_area = circumscribe_polygon_area(GameTheme.receptor_ring_radius+ring_thickness*0.5, mesh_v) - inscribe_polygon_area(GameTheme.receptor_ring_radius-ring_thickness*0.5, mesh_v) + var ideal_ring_area = PI * (pow(GameTheme.receptor_ring_radius+receptor_px/2+shadow_px, 2) - pow(GameTheme.receptor_ring_radius-receptor_px/2-shadow_px, 2)) # var l = len(ring_vertices) # for i in l: -## estimated_area += triangle_area(ring_vertices[i], ring_vertices[(i+1)%l], ring_vertices[(i+2)%l]) - var quad_area = 4*pow(GameTheme.receptor_ring_radius+(receptor_px+shadow_px)/2, 2) +# estimated_area += triangle_area(ring_vertices[i], ring_vertices[(i+1)%l], ring_vertices[(i+2)%l]) + var quad_area = 4*pow(GameTheme.receptor_ring_radius+receptor_px/2+shadow_px, 2) var fps = Performance.get_monitor(Performance.TIME_FPS) $"/root/main/InputHandler".text = "Vertices: %d*2 Skew: %.3f\nArea: %.0f\n(%.0f%% ideal ring)\n(%.0f%% quad)\nFPS: %.0f"%[mesh_v, skew, estimated_area, 100.0*estimated_area/ideal_ring_area, 100.0*estimated_area/quad_area, fps] -# ._draw() + + material.set_shader_param("dot_radius", 0.5*receptor_px/GameTheme.receptor_ring_radius) + material.set_shader_param("line_thickness", 0.5*ring_px/GameTheme.receptor_ring_radius) + material.set_shader_param("shadow_thickness", shadow_px/GameTheme.receptor_ring_radius) + material.set_shader_param("shadow_thickness_taper", -0.75) + material.set_shader_param("px", 0.5/GameTheme.receptor_ring_radius) + +func update_ring_mesh_1arg(arg1): + # Hack because signals can't discard arguments when connected to smaller slots :( + update_ring_mesh() func _ready(): + var receptor_array_image := Image.new() + receptor_array_image.create(8, 8, false, Image.FORMAT_RH) + receptor_array_image.lock() + for i in Rules.COLS: + receptor_array_image.set_pixel(i%8, i/8, Color(GameTheme.RADIAL_COL_ANGLES[i], 0.0, 0.0)) + receptor_array_image.unlock() + var receptor_data_tex = ImageTexture.new() + receptor_data_tex.create_from_image(receptor_array_image, 0) + set_texture(receptor_data_tex) + material.set_shader_param("num_receptors", Rules.COLS) + + update_ring_mesh() + $VerticesSlider.connect("value_changed", self, "update_ring_mesh_1arg") + $SkewSlider.connect("value_changed", self, "update_ring_mesh_1arg") $"/root".connect("size_changed", self, "update") func _process(delta): diff --git a/main.tscn b/main.tscn index 6986f2c..4398675 100644 --- a/main.tscn +++ b/main.tscn @@ -1,22 +1,35 @@ -[gd_scene load_steps=17 format=2] +[gd_scene load_steps=19 format=2] [ext_resource path="res://main.gd" type="Script" id=1] [ext_resource path="res://video.gd" type="Script" id=2] [ext_resource path="res://ScreenFilter.gd" type="Script" id=3] -[ext_resource path="res://Receptors.gd" type="Script" id=4] -[ext_resource path="res://NoteHandler.gd" type="Script" id=5] -[ext_resource path="res://assets/text-4k.png" type="Texture" id=6] -[ext_resource path="res://shaders/notelines.shader" type="Shader" id=7] -[ext_resource path="res://shaders/notemesh.shader" type="Shader" id=8] -[ext_resource path="res://Menu.gd" type="Script" id=9] -[ext_resource path="res://shaders/scoretext.tres" type="Material" id=10] -[ext_resource path="res://ScoreText.gd" type="Script" id=11] -[ext_resource path="res://Bezel.gd" type="Script" id=12] -[ext_resource path="res://assets/NotoSans.tres" type="DynamicFont" id=13] -[ext_resource path="res://InputHandler.gd" type="Script" id=14] +[ext_resource path="res://shaders/receptors.shader" type="Shader" id=4] +[ext_resource path="res://Receptors.gd" type="Script" id=5] +[ext_resource path="res://NoteHandler.gd" type="Script" id=6] +[ext_resource path="res://assets/text-4k.png" type="Texture" id=7] +[ext_resource path="res://shaders/notelines.shader" type="Shader" id=8] +[ext_resource path="res://shaders/notemesh.shader" type="Shader" id=9] +[ext_resource path="res://Menu.gd" type="Script" id=10] +[ext_resource path="res://shaders/scoretext.tres" type="Material" id=11] +[ext_resource path="res://ScoreText.gd" type="Script" id=12] +[ext_resource path="res://Bezel.gd" type="Script" id=13] +[ext_resource path="res://assets/NotoSans.tres" type="DynamicFont" id=14] +[ext_resource path="res://InputHandler.gd" type="Script" id=15] -[sub_resource type="ShaderMaterial" id=1] -shader = ExtResource( 7 ) +[sub_resource type="ShaderMaterial" id=4] +shader = ExtResource( 4 ) +shader_param/num_receptors = 8 +shader_param/line_color = Plane( 0, 0, 1, 1 ) +shader_param/dot_color = Plane( 0, 0, 1, 1 ) +shader_param/shadow_color = Plane( 0, 0, 0, 1 ) +shader_param/line_thickness = 0.006 +shader_param/dot_radius = 0.033 +shader_param/shadow_thickness = 0.01 +shader_param/shadow_thickness_taper = 0.33 +shader_param/px = 0.002 + +[sub_resource type="ShaderMaterial" id=2] +shader = ExtResource( 8 ) shader_param/line_color = Plane( 0.8, 0.8, 1, 0.8 ) shader_param/line_color_double = Plane( 1, 1, 0.6, 0.9 ) shader_param/dot_color = Plane( 1, 1, 1, 0.8 ) @@ -27,8 +40,8 @@ shader_param/dot_thickness = 0.033 shader_param/dot_fullbright_thickness = 0.013 shader_param/max_angle = 1.0708 -[sub_resource type="ShaderMaterial" id=2] -shader = ExtResource( 8 ) +[sub_resource type="ShaderMaterial" id=3] +shader = ExtResource( 9 ) shader_param/bps = null shader_param/star_color = null shader_param/held_color = null @@ -63,9 +76,11 @@ __meta__ = { script = ExtResource( 3 ) [node name="Receptors" type="MeshInstance2D" parent="."] -script = ExtResource( 4 ) +material = SubResource( 4 ) +script = ExtResource( 5 ) [node name="SkewSlider" type="HSlider" parent="Receptors"] +visible = false margin_left = -200.0 margin_top = -30.0 margin_right = 200.0 @@ -80,6 +95,7 @@ tick_count = 3 ticks_on_borders = true [node name="VerticesSlider" type="HSlider" parent="Receptors"] +visible = false margin_left = -200.0 margin_top = -100.0 margin_right = 200.0 @@ -88,40 +104,40 @@ min_value = 3.0 max_value = 72.0 value = 36.0 tick_count = 2 +ticks_on_borders = true [node name="NoteHandler" type="Node2D" parent="."] -script = ExtResource( 5 ) +script = ExtResource( 6 ) [node name="SlideTrailHandler" type="Node2D" parent="NoteHandler"] [node name="JudgeText" type="MeshInstance2D" parent="NoteHandler"] -texture = ExtResource( 6 ) +texture = ExtResource( 7 ) [node name="notelines" type="MeshInstance2D" parent="NoteHandler"] -material = SubResource( 1 ) - -[node name="meshinstance" type="MeshInstance2D" parent="NoteHandler"] material = SubResource( 2 ) +[node name="meshinstance" type="MeshInstance2D" parent="NoteHandler"] +material = SubResource( 3 ) + [node name="Menu" type="Node2D" parent="."] -visible = false -script = ExtResource( 9 ) +script = ExtResource( 10 ) [node name="ScoreText" type="Node2D" parent="Menu"] -material = ExtResource( 10 ) -script = ExtResource( 11 ) +material = ExtResource( 11 ) +script = ExtResource( 12 ) [node name="Bezel" type="Node2D" parent="."] -script = ExtResource( 12 ) +script = ExtResource( 13 ) [node name="InputHandler" type="Label" parent="."] margin_left = -540.0 margin_top = -540.0 margin_right = 540.0 margin_bottom = 540.0 -custom_fonts/font = ExtResource( 13 ) +custom_fonts/font = ExtResource( 14 ) text = "Fingers on the screen:" -script = ExtResource( 14 ) +script = ExtResource( 15 ) __meta__ = { "_edit_lock_": true } diff --git a/shaders/receptors.shader b/shaders/receptors.shader new file mode 100644 index 0000000..dc7c57f --- /dev/null +++ b/shaders/receptors.shader @@ -0,0 +1,66 @@ +shader_type canvas_item; +render_mode blend_premul_alpha; + +uniform int num_receptors = 8; +uniform vec4 line_color = vec4(0.0, 0.0, 1.0, 1.0); +uniform vec4 dot_color = vec4(0.0, 0.0, 1.0, 1.0); +uniform vec4 shadow_color = vec4(0.0, 0.0, 0.0, 1.0); +//uniform float bps = 1.0; +uniform float line_thickness = 0.006; +uniform float dot_radius = 0.033; +uniform float shadow_thickness = 0.01; +uniform float shadow_thickness_taper = 0.33; +uniform float px = 0.002; // Represents 1px in UV space, for AA purposes + +//void vertex() { +//} + +float angle_diff(float a, float b) { + float d = mod((a - b), 6.28318); + if (d > 3.14159) d = 6.28318 - d; + return d; +} + +void fragment() { + if (COLOR.rgba != vec4(1.0, 0.0, 0.0, 1.0)) { + COLOR.rgba = vec4(0.0); + lowp float dist = distance(UV, vec2(0.0)); + lowp float angle = atan(-UV.y, UV.x); + float line_alpha = 0.0; + float dot_alpha = 0.0; + float shadow_alpha = 0.0; + float px2 = px/2.0; + + float diff = abs(dist - 1.0); + float d2 = diff - line_thickness; + if (d2 < -px2){ + line_alpha = 1.0; + } else if (d2 < shadow_thickness){ + if (d2 < px2) + line_alpha = 1.0 - (d2 + px2)/px; + shadow_alpha = 1.0 - min((d2 - shadow_thickness*shadow_thickness_taper)/(shadow_thickness*(1.0-shadow_thickness_taper)), 1.0); + } + + // Iterate over all the receptors and check distance to them + for (int i=0; i