From 2cf7010c929e99daf810024dddf4a40fb40b9917 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Sat, 1 Mar 2025 19:02:13 -0700 Subject: [PATCH] Continuous line drawing --- src/player/player.gd | 24 +++---- src/player/player.tscn | 10 +-- src/world/gunk_body/gunk_body.gd | 106 ++++++++----------------------- 3 files changed, 38 insertions(+), 102 deletions(-) diff --git a/src/player/player.gd b/src/player/player.gd index 02858ee..e34a854 100644 --- a/src/player/player.gd +++ b/src/player/player.gd @@ -7,7 +7,7 @@ const JUMP_FORCE := 4.5 const GROUND_FRICTION := 0.3 const AIR_FRICTION := 0.03 -const SPRAY_SCALE := 8 +const SPRAY_SCALE := 16 var gravity: Vector3 = ( ProjectSettings.get_setting("physics/3d/default_gravity") @@ -16,8 +16,7 @@ var gravity: Vector3 = ( @onready var camera_pivot: Node3D = %CameraPivot @onready var spray_muzzle: Marker3D = %SprayMuzzle -@onready var ray_cast_left: RayCast3D = %RayCastLeft -@onready var ray_cast_right: RayCast3D = %RayCastRight +@onready var raycast: RayCast3D = %RayCast3D @onready var spray_effect: MeshInstance3D = %SprayEffect @@ -41,18 +40,13 @@ func _scale_spray_point(point: Vector3) -> float: func fire_spray() -> void: - if ray_cast_left.is_colliding() and ray_cast_right.is_colliding(): - var collider := ray_cast_left.get_collider() - if collider is GunkBody and collider == ray_cast_right.get_collider(): - var point_left := ray_cast_left.get_collision_point() - var point_right := ray_cast_right.get_collision_point() - (collider as GunkBody).paint_bar( - point_left, - ray_cast_left.get_collision_normal(), - _scale_spray_point(point_left), - point_right, - ray_cast_right.get_collision_normal(), - _scale_spray_point(point_right) + if raycast.is_colliding(): + var collider := raycast.get_collider() + if collider is GunkBody: + var point := raycast.get_collision_point() + var point_scale := point.distance_to(spray_muzzle.global_position) * SPRAY_SCALE + (collider as GunkBody).paint_continuous( + point, raycast.get_collision_normal(), point_scale ) diff --git a/src/player/player.tscn b/src/player/player.tscn index 747d91e..cb19c60 100644 --- a/src/player/player.tscn +++ b/src/player/player.tscn @@ -14,7 +14,7 @@ albedo_color = Color(0, 1, 0.301961, 0.254902) [sub_resource type="PrismMesh" id="PrismMesh_ow0jh"] material = SubResource("StandardMaterial3D_ng43h") -size = Vector3(0.5, 2, 0.1) +size = Vector3(0.2, 2, 0.2) [sub_resource type="QuadMesh" id="QuadMesh_cn0yq"] size = Vector2(1, 0.05) @@ -38,13 +38,9 @@ mesh = SubResource("BoxMesh_ua7a2") unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 0.997564, -0.0697565, 0, 0.0697565, 0.997564, 0, 0, -0.15) -[node name="RayCastLeft" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] +[node name="RayCast3D" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] unique_name_in_owner = true -target_position = Vector3(-0.25, 0, -2) - -[node name="RayCastRight" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] -unique_name_in_owner = true -target_position = Vector3(0.25, 0, -2) +target_position = Vector3(0, 0, -2) [node name="SprayEffect" type="MeshInstance3D" parent="CameraPivot/SprayNozzle/SprayMuzzle"] unique_name_in_owner = true diff --git a/src/world/gunk_body/gunk_body.gd b/src/world/gunk_body/gunk_body.gd index 6827448..d243e4c 100644 --- a/src/world/gunk_body/gunk_body.gd +++ b/src/world/gunk_body/gunk_body.gd @@ -1,25 +1,19 @@ class_name GunkBody extends StaticBody3D ## StaticBody3D with an associated "gunkable" mesh. +const CONTINUITY_LIMIT := 64 +const BUFFER_LIMIT := 3 const FACE_EPSILON := 0.4 const MASK_COLOR := Color.RED -const WRAPPINGS := [ - Vector2(-1, -1), - Vector2(-1, 0), - Vector2(-1, 1), - Vector2(0, -1), - Vector2(0, 0), - Vector2(0, 1), - Vector2(1, -1), - Vector2(1, 0), - Vector2(1, 1), -] - @export var mask_dim := 1024 var meshtool := MeshDataTool.new() +var _polyline_buffer: Array[Vector2] = [] +var _continuous := false +var _painted_this_frame := false + @onready var mesh_instance: MeshInstance3D = $MeshInstance3D @onready var mesh: ArrayMesh = mesh_instance.mesh @onready var gunk_mat: ShaderMaterial = mesh_instance.get_surface_override_material(0).next_pass @@ -86,73 +80,25 @@ func _get_uv(point: Vector3, normal: Vector3) -> Vector2: return (uv1 * bc.x) + (uv2 * bc.y) + (uv3 * bc.z) -## Paint a circle on the mask at a given point & normal on the mesh. -func paint_mask(point: Vector3, normal: Vector3, radius: float) -> void: - var local_point := point * global_transform - var local_normal := normal * global_basis - var uv := _get_uv(local_point, local_normal) - if uv == Vector2.INF: - return - var px_center: Vector2 = uv * mask_control.size - mask_control.queue_draw( - func() -> void: mask_control.draw_circle(px_center, radius, MASK_COLOR, true, -1, true) - ) +func paint_continuous(point: Vector3, normal: Vector3, width: float) -> void: + var px := _get_uv(point * global_transform, normal * global_basis) * mask_control.size + if _polyline_buffer and px.distance_to(_polyline_buffer[0]) <= CONTINUITY_LIMIT: + _polyline_buffer.push_front(px) + if len(_polyline_buffer) > BUFFER_LIMIT: + _polyline_buffer.pop_back() + var polyline := PackedVector2Array(_polyline_buffer) + mask_control.queue_draw( + func() -> void: mask_control.draw_polyline(polyline, MASK_COLOR, width * 2, true) + ) + else: + _polyline_buffer = [px] + mask_control.queue_draw( + func() -> void: mask_control.draw_circle(px, width, MASK_COLOR, true, -1, true) + ) + _painted_this_frame = true -func paint_line( - point_a: Vector3, normal_a: Vector3, point_b: Vector3, normal_b: Vector3, width: float -) -> void: - var uv_a := _get_uv(point_a * global_transform, normal_a * global_basis) - var uv_b := _get_uv(point_b * global_transform, normal_b * global_basis) - if uv_a == Vector2.INF or uv_b == Vector2.INF: - return - var px_a := uv_a * mask_control.size - var px_b := uv_b * mask_control.size - mask_control.queue_draw( - func() -> void: mask_control.draw_line(px_a, px_b, MASK_COLOR, width, true) - ) - - -func _render_bar(uv_a: Vector2, scale_a: float, uv_b: Vector2, scale_b: float) -> void: - var diff := (uv_b - uv_a).normalized() - var ortho := diff.orthogonal() - - var points := PackedVector2Array( - [ - uv_a + ortho * scale_a, - uv_a - ortho * scale_a, - uv_b - ortho * scale_b, - uv_b + ortho * scale_b, - ] - ) - - mask_control.queue_draw(func() -> void: mask_control.draw_colored_polygon(points, MASK_COLOR)) - - -func paint_bar( - point_a: Vector3, - normal_a: Vector3, - scale_a: float, - point_b: Vector3, - normal_b: Vector3, - scale_b: float -) -> void: - var uv_a := _get_uv(point_a * global_transform, normal_a * global_basis) * mask_control.size - var uv_b := _get_uv(point_b * global_transform, normal_b * global_basis) * mask_control.size - if uv_a == Vector2.INF or uv_b == Vector2.INF: - return # TODO just draw square around one valid point - - var closest_b := uv_b - var wrapped_a := uv_a - var dist_sq := INF - - for wrap_vec: Vector2 in WRAPPINGS: - var wrapped_b := uv_b + wrap_vec * mask_dim - var d := wrapped_b.distance_squared_to(uv_a) - if d < dist_sq: - closest_b = wrapped_b - wrapped_a = uv_a - wrap_vec * mask_dim - dist_sq = d - - _render_bar(uv_a, scale_a, closest_b, scale_b) - _render_bar(wrapped_a, scale_a, uv_b, scale_b) +func _process(_delta: float) -> void: + if not _painted_this_frame: + _polyline_buffer = [] + _painted_this_frame = false