From 0fee0a155f6c041b11f3af4e7a14af973c287f1a Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Sat, 1 Mar 2025 16:12:18 -0700 Subject: [PATCH] Using subviewport texture for gunk mask --- levels/sandbox/sandbox.tscn | 24 +++++------------------- src/player/player.gd | 2 +- src/shaders/gunk.gdshader | 21 ++++++++------------- src/world/gunk_body/draw_controller.gd | 22 ++++++++++++++++++++++ src/world/{ => gunk_body}/gunk_body.gd | 24 +++++------------------- src/world/gunk_body/gunk_body.tscn | 26 ++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 52 deletions(-) create mode 100644 src/world/gunk_body/draw_controller.gd rename src/world/{ => gunk_body}/gunk_body.gd (77%) create mode 100644 src/world/gunk_body/gunk_body.tscn diff --git a/levels/sandbox/sandbox.tscn b/levels/sandbox/sandbox.tscn index d0451ed..23554f6 100644 --- a/levels/sandbox/sandbox.tscn +++ b/levels/sandbox/sandbox.tscn @@ -1,12 +1,11 @@ -[gd_scene load_steps=24 format=3 uid="uid://16ds4fvv72xk"] +[gd_scene load_steps=23 format=3 uid="uid://16ds4fvv72xk"] -[ext_resource type="Script" path="res://src/world/gunk_body.gd" id="1_csc4b"] +[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"] [ext_resource type="ArrayMesh" uid="uid://lvats6ptrcxt" path="res://assets/props/icosahedron/icosahedron.obj" id="1_kvwui"] [ext_resource type="Shader" path="res://src/shaders/gunk.gdshader" id="2_utxkh"] [ext_resource type="Material" uid="uid://byyjoruj8mwe0" path="res://levels/sandbox/debug_gunk_mat.tres" id="3_o7v4k"] [ext_resource type="ArrayMesh" uid="uid://e6syamfwiitk" path="res://assets/props/monkey/monkey.obj" id="4_o55bf"] -[ext_resource type="Script" path="res://levels/sandbox/debug_draw.gd" id="7_r3enw"] [sub_resource type="Environment" id="Environment_cc548"] background_mode = 1 @@ -114,9 +113,8 @@ mesh = SubResource("CylinderMesh_a1koa") [node name="CollisionShape3D" type="CollisionShape3D" parent="Geometry/Cylinder"] shape = SubResource("CylinderShape3D_uo57d") -[node name="Icosahedron" type="StaticBody3D" parent="Geometry"] +[node name="Icosahedron" parent="Geometry" instance=ExtResource("1_a67lu")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.35498, 1.13294, -2.81423) -script = ExtResource("1_csc4b") [node name="MeshInstance3D" type="MeshInstance3D" parent="Geometry/Icosahedron"] mesh = ExtResource("1_kvwui") @@ -126,27 +124,15 @@ surface_material_override/0 = ExtResource("3_o7v4k") [node name="CollisionShape3D" type="CollisionShape3D" parent="Geometry/Icosahedron"] shape = SubResource("ConcavePolygonShape3D_nxc47") -[node name="Monkey" type="StaticBody3D" parent="Geometry"] +[node name="Monkey" parent="Geometry" instance=ExtResource("1_a67lu")] transform = Transform3D(-0.616239, 0, 0.787559, 0, 1, 0, -0.787559, 0, -0.616239, -2.22512, 1.41158, 2.88581) -script = ExtResource("1_csc4b") [node name="MeshInstance3D" type="MeshInstance3D" parent="Geometry/Monkey"] mesh = ExtResource("4_o55bf") +skeleton = NodePath("") surface_material_override/0 = SubResource("StandardMaterial3D_7cwtv") [node name="CollisionShape3D" type="CollisionShape3D" parent="Geometry/Monkey"] shape = SubResource("ConcavePolygonShape3D_hvf6a") [node name="Player" parent="." instance=ExtResource("1_h436a")] - -[node name="DebugDraw" type="Control" parent="."] -unique_name_in_owner = true -visible = false -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -mouse_filter = 2 -script = ExtResource("7_r3enw") diff --git a/src/player/player.gd b/src/player/player.gd index b31c0cd..47b09a1 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 := 15 +const SPRAY_RADIUS := 8 var gravity: Vector3 = ( ProjectSettings.get_setting("physics/3d/default_gravity") diff --git a/src/shaders/gunk.gdshader b/src/shaders/gunk.gdshader index 350a9f7..9869a44 100644 --- a/src/shaders/gunk.gdshader +++ b/src/shaders/gunk.gdshader @@ -13,18 +13,13 @@ uniform highp sampler2D gunk_normal_map; void fragment() { - float mask = texture(gunk_mask, UV).r; - if(mask < 0.1) { - float value = texture(gunk_noise, UV).r; - vec4 color = mix(color_1, color_2, value); - float roughness_mix = value * roughness; + float value = texture(gunk_noise, UV).r; + vec4 color = mix(color_1, color_2, value); + float roughness_mix = value * roughness; - ALBEDO = color.rgb; - ROUGHNESS = roughness_mix; - SPECULAR = 0.5 * inversesqrt(specular_contribution); - NORMAL_MAP = texture(gunk_normal_map, UV).xyz; - ALPHA = 1.0; - } else { - ALPHA = 0.0; - } + ALBEDO = color.rgb; + ROUGHNESS = roughness_mix; + SPECULAR = 0.5 * inversesqrt(specular_contribution); + NORMAL_MAP = texture(gunk_normal_map, UV).xyz; + ALPHA = 1.0 - texture(gunk_mask, UV).r; } \ No newline at end of file diff --git a/src/world/gunk_body/draw_controller.gd b/src/world/gunk_body/draw_controller.gd new file mode 100644 index 0000000..f94dd41 --- /dev/null +++ b/src/world/gunk_body/draw_controller.gd @@ -0,0 +1,22 @@ +class_name DrawController extends Control + +var _draw_queue: Array[Callable] = [] + +var _dirty := true + + +func queue_draw(op: Callable) -> void: + _draw_queue.push_back(op) + _dirty = true + + +func _draw() -> void: + while _draw_queue: + var op: Callable = _draw_queue.pop_front() + op.call() + + +func _process(_delta: float) -> void: + if _dirty: + queue_redraw() + _dirty = false diff --git a/src/world/gunk_body.gd b/src/world/gunk_body/gunk_body.gd similarity index 77% rename from src/world/gunk_body.gd rename to src/world/gunk_body/gunk_body.gd index 58ee549..c98d307 100644 --- a/src/world/gunk_body.gd +++ b/src/world/gunk_body/gunk_body.gd @@ -1,34 +1,24 @@ class_name GunkBody extends StaticBody3D ## StaticBody3D with an associated "gunkable" mesh. -const MASK_DIM := 512 const FACE_EPSILON := 0.4 const MASK_COLOR := Color.RED -@export var mask_image: Image - var meshtool := MeshDataTool.new() @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 -@onready var debug_draw: DebugDraw = %DebugDraw +@onready var mask_viewport: SubViewport = %MaskViewport +@onready var mask_control: DrawController = %MaskControl func _ready() -> void: - if not mask_image: - mask_image = Image.create_empty(MASK_DIM, MASK_DIM, false, Image.FORMAT_L8) - _update_gunk_material() - + gunk_mat.set_shader_parameter("gunk_mask", mask_viewport.get_texture()) meshtool.create_from_surface(mesh, 0) -## Must be called internally whenever `mask_image` is updated -func _update_gunk_material() -> void: - gunk_mat.set_shader_parameter("gunk_mask", ImageTexture.create_from_image(mask_image)) - - ## Transform cartesian coordinates to barycentric wrt the given triangle func _barycentric(p: Vector3, a: Vector3, b: Vector3, c: Vector3) -> Vector3: var v0 := b - a @@ -83,14 +73,10 @@ func _get_uv(point: Vector3, normal: Vector3) -> Vector2: ## Paint a rectangle on the mask at a given point & normal on the mesh. func paint_mask(point: Vector3, normal: Vector3, radius: int) -> void: - # debug_draw.draw_vector(normal, point) 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: Vector2i = Vector2i(uv * Vector2(mask_image.get_size())) - var rect := Rect2i(px_center.x - radius, px_center.y - radius, 2 * radius, 2 * radius) - mask_image.fill_rect(rect, MASK_COLOR) - # TODO: can we call this once after all painting in a single frame is finished? - _update_gunk_material() + var px_center: Vector2 = uv * mask_control.size + mask_control.queue_draw(func() -> void: mask_control.draw_circle(px_center, radius, MASK_COLOR)) diff --git a/src/world/gunk_body/gunk_body.tscn b/src/world/gunk_body/gunk_body.tscn new file mode 100644 index 0000000..c4241ed --- /dev/null +++ b/src/world/gunk_body/gunk_body.tscn @@ -0,0 +1,26 @@ +[gd_scene load_steps=3 format=3 uid="uid://c2omlx4ptrc01"] + +[ext_resource type="Script" path="res://src/world/gunk_body/gunk_body.gd" id="1_qqbpr"] +[ext_resource type="Script" path="res://src/world/gunk_body/draw_controller.gd" id="2_kkcjw"] + +[node name="GunkBody" type="StaticBody3D"] +script = ExtResource("1_qqbpr") + +[node name="MaskViewport" type="SubViewport" parent="."] +unique_name_in_owner = true +handle_input_locally = false +msaa_2d = 3 +canvas_item_default_texture_filter = 0 +size = Vector2i(1024, 1024) +render_target_clear_mode = 1 +render_target_update_mode = 4 + +[node name="MaskControl" type="Control" parent="MaskViewport"] +unique_name_in_owner = true +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2_kkcjw")