diff --git a/levels/sandbox/sandbox.tscn b/levels/sandbox/sandbox.tscn index 762b3ea..66be482 100644 --- a/levels/sandbox/sandbox.tscn +++ b/levels/sandbox/sandbox.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=37 format=3 uid="uid://16ds4fvv72xk"] +[gd_scene load_steps=39 format=3 uid="uid://16ds4fvv72xk"] [ext_resource type="PackedScene" uid="uid://c2omlx4ptrc01" path="res://src/world/gunk_body/gunk_body.tscn" id="1_a67lu"] [ext_resource type="PackedScene" uid="uid://bwe2jdmvinhqd" path="res://src/player/player.tscn" id="1_h436a"] @@ -9,13 +9,18 @@ [ext_resource type="ArrayMesh" uid="uid://bwgiytiic3t32" path="res://assets/props/cube/cube.obj" id="6_4kku3"] [ext_resource type="ArrayMesh" uid="uid://dchtriuljuloj" path="res://assets/props/cylinder/cylinder.obj" id="7_gn6k6"] +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_cj8bk"] + +[sub_resource type="Sky" id="Sky_8jqw1"] +sky_material = SubResource("ProceduralSkyMaterial_cj8bk") + [sub_resource type="Environment" id="Environment_cc548"] -background_mode = 1 +background_mode = 2 +sky = SubResource("Sky_8jqw1") ambient_light_source = 2 ambient_light_color = Color(1, 1, 1, 1) ambient_light_energy = 0.06 tonemap_mode = 3 -fog_enabled = true fog_light_color = Color(0, 0, 0, 1) fog_density = 0.2 diff --git a/src/player/player.gd b/src/player/player.gd index 71570b6..02858ee 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_RADIUS := 8 +const SPRAY_SCALE := 8 var gravity: Vector3 = ( ProjectSettings.get_setting("physics/3d/default_gravity") @@ -16,6 +16,8 @@ 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 spray_effect: MeshInstance3D = %SprayEffect @@ -34,15 +36,24 @@ func get_friction() -> float: return AIR_FRICTION +func _scale_spray_point(point: Vector3) -> float: + return point.distance_to(spray_muzzle.global_position) * SPRAY_SCALE + + func fire_spray() -> void: - for raycast: RayCast3D in get_tree().get_nodes_in_group("SprayCast"): - if raycast.is_colliding(): - var collider := raycast.get_collider() - if collider is GunkBody: - # Adjust radius to compensate for distance from muzzle - var point := raycast.get_collision_point() - var radius := sqrt(point.distance_to(spray_muzzle.global_position)) * SPRAY_RADIUS - (collider as GunkBody).paint_mask(point, raycast.get_collision_normal(), radius) + 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) + ) func _physics_process(delta: float) -> void: diff --git a/src/player/player.tscn b/src/player/player.tscn index f223e86..747d91e 100644 --- a/src/player/player.tscn +++ b/src/player/player.tscn @@ -38,20 +38,13 @@ 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="RayCast3D" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] -target_position = Vector3(-0.2, 0, -2) +[node name="RayCastLeft" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] +unique_name_in_owner = true +target_position = Vector3(-0.25, 0, -2) -[node name="RayCast3D2" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] -target_position = Vector3(-0.1, 0, -2) - -[node name="RayCast3D3" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] -target_position = Vector3(0, 0, -2) - -[node name="RayCast3D4" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] -target_position = Vector3(0.1, 0, -2) - -[node name="RayCast3D5" type="RayCast3D" parent="CameraPivot/SprayNozzle/SprayMuzzle" groups=["SprayCast"]] -target_position = Vector3(0.2, 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) [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 d12cdc9..6827448 100644 --- a/src/world/gunk_body/gunk_body.gd +++ b/src/world/gunk_body/gunk_body.gd @@ -4,6 +4,18 @@ class_name GunkBody extends StaticBody3D 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() @@ -75,7 +87,7 @@ func _get_uv(point: Vector3, normal: Vector3) -> Vector2: ## Paint a circle on the mask at a given point & normal on the mesh. -func paint_mask(point: Vector3, normal: Vector3, radius: int) -> void: +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) @@ -85,3 +97,62 @@ func paint_mask(point: Vector3, normal: Vector3, radius: int) -> void: mask_control.queue_draw( func() -> void: mask_control.draw_circle(px_center, radius, MASK_COLOR, true, -1, 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) diff --git a/src/world/gunk_body/gunk_body.tscn b/src/world/gunk_body/gunk_body.tscn index c4241ed..c066beb 100644 --- a/src/world/gunk_body/gunk_body.tscn +++ b/src/world/gunk_body/gunk_body.tscn @@ -11,6 +11,7 @@ unique_name_in_owner = true handle_input_locally = false msaa_2d = 3 canvas_item_default_texture_filter = 0 +canvas_item_default_texture_repeat = 1 size = Vector2i(1024, 1024) render_target_clear_mode = 1 render_target_update_mode = 4