From e19e88328b3facc17027e7a85cfb2274407a2311 Mon Sep 17 00:00:00 2001 From: hiina Date: Wed, 12 Feb 2025 20:03:47 -0700 Subject: [PATCH] add working manipulators in title scene --- override.cfg | 2 +- project.godot | 3 + scenes/manipulator.gd | 38 +++++++++ scenes/manipulator.tscn | 62 ++++++++++++++ scenes/manipulatorsphere.tres | 80 ++++++++++++++++++ scenes/particle_test.gdshader | 2 +- scenes/proxy_collision_detector.gd | 8 +- ..._scene.GPUParticlesCollisionSDF3D_data.exr | Bin 67269 -> 67749 bytes scenes/title_scene.tscn | 58 ++++++++----- scenes/xr_origin_3d.tscn | 4 + 10 files changed, 233 insertions(+), 24 deletions(-) create mode 100644 scenes/manipulator.gd create mode 100644 scenes/manipulator.tscn create mode 100644 scenes/manipulatorsphere.tres diff --git a/override.cfg b/override.cfg index 0d1998e..4cd32b3 100644 --- a/override.cfg +++ b/override.cfg @@ -1,3 +1,3 @@ [xr] -openxr/enabled=false +openxr/enabled=true diff --git a/project.godot b/project.godot index f3f5a01..e78137e 100644 --- a/project.godot +++ b/project.godot @@ -63,6 +63,9 @@ trigger_left={ [layer_names] +3d_render/layer_1="static geometry" +3d_render/layer_2="manipulators" +3d_render/layer_3="targets" 3d_render/layer_20="particle_collision_proxy" 3d_physics/layer_1="Static World" 3d_physics/layer_2="Dynamic World" diff --git a/scenes/manipulator.gd b/scenes/manipulator.gd new file mode 100644 index 0000000..7fe3144 --- /dev/null +++ b/scenes/manipulator.gd @@ -0,0 +1,38 @@ +# keeps the visual mesh in sync with the attractor radius, and allows the +# attractor radius to be scaled by joystick input of the controller holding the +# object. +extends Node3D + +@onready var mesh: MeshInstance3D = $Bounds +@onready var attractor: GPUParticlesAttractorSphere3D = $GPUParticlesAttractorSphere3D + +var held_controller: XRController3D = null +const MIN_SCALE = 0.5 +const MAX_SCALE = 10 +const SCALE_SPEED = 1.0 + +var initial_rotation: Vector3 = Vector3.ZERO + +func _ready() -> void: + initial_rotation = attractor.global_rotation + +func _process(delta: float) -> void: + # Keep attractor radius in sync with mesh scale + mesh.scale = Vector3(attractor.radius, attractor.radius, attractor.radius) + + # Keep attractor's global rotation fixed + attractor.global_rotation = initial_rotation + + # Adjust scale based on controller input if being held + if held_controller: + var joystick_y = held_controller.get_vector2("primary").y + if abs(joystick_y) > 0.1: + var new_radius = attractor.radius + (joystick_y * SCALE_SPEED * delta) + attractor.radius = clamp(new_radius, MIN_SCALE, MAX_SCALE) + + +func _on_pickable_object_picked_up(pickable: XRToolsPickable) -> void: + held_controller = pickable.get_picked_up_by_controller() + +func _on_pickable_object_dropped(_pickable: XRToolsPickable) -> void: + held_controller = null diff --git a/scenes/manipulator.tscn b/scenes/manipulator.tscn new file mode 100644 index 0000000..44600af --- /dev/null +++ b/scenes/manipulator.tscn @@ -0,0 +1,62 @@ +[gd_scene load_steps=11 format=3 uid="uid://bixan352mi1j6"] + +[ext_resource type="PackedScene" uid="uid://c8l60rnugru40" path="res://addons/godot-xr-tools/objects/pickable.tscn" id="1_dshke"] +[ext_resource type="PackedScene" uid="uid://c25yxb0vt53vc" path="res://addons/godot-xr-tools/objects/grab_points/grab_point_hand_left.tscn" id="2_m3e5t"] +[ext_resource type="PackedScene" uid="uid://ctw7nbntd5pcj" path="res://addons/godot-xr-tools/objects/grab_points/grab_point_hand_right.tscn" id="3_1s35s"] +[ext_resource type="Script" path="res://scenes/manipulator.gd" id="4_25j3l"] +[ext_resource type="Shader" uid="uid://dilr5r6k3ik7t" path="res://scenes/manipulatorsphere.tres" id="4_ewjtf"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_f261g"] +radius = 0.1 + +[sub_resource type="SphereMesh" id="SphereMesh_mou0a"] +radius = 0.1 +height = 0.2 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ljyno"] +albedo_color = Color(0, 0.470085, 0.308113, 1) +emission_enabled = true +emission = Color(1.44392e-06, 0.591371, 0.38197, 1) +emission_energy_multiplier = 0.22 + +[sub_resource type="SphereMesh" id="SphereMesh_56y52"] +radius = 1.0 +height = 2.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_1u5an"] +render_priority = 0 +shader = ExtResource("4_ewjtf") +shader_parameter/Albedo = Color(1, 1, 1, 1) + +[node name="Manipulator" instance=ExtResource("1_dshke")] +lock_rotation = true +freeze = true +release_mode = 1 +ranged_grab_method = 0 +second_hand_grab = 1 + +[node name="CollisionShape3D" parent="." index="0"] +shape = SubResource("SphereShape3D_f261g") + +[node name="Visual" type="MeshInstance3D" parent="." index="1"] +layers = 2 +mesh = SubResource("SphereMesh_mou0a") +surface_material_override/0 = SubResource("StandardMaterial3D_ljyno") + +[node name="GrabPointHandLeft" parent="." index="2" instance=ExtResource("2_m3e5t")] + +[node name="GrabPointHandRight" parent="." index="3" instance=ExtResource("3_1s35s")] + +[node name="ManipulatorSync" type="Node3D" parent="." index="4"] +script = ExtResource("4_25j3l") + +[node name="Bounds" type="MeshInstance3D" parent="ManipulatorSync" index="0"] +layers = 2 +mesh = SubResource("SphereMesh_56y52") +skeleton = NodePath("../..") +surface_material_override/0 = SubResource("ShaderMaterial_1u5an") + +[node name="GPUParticlesAttractorSphere3D" type="GPUParticlesAttractorSphere3D" parent="ManipulatorSync" index="1"] + +[connection signal="dropped" from="." to="ManipulatorSync" method="_on_pickable_object_dropped"] +[connection signal="picked_up" from="." to="ManipulatorSync" method="_on_pickable_object_picked_up"] diff --git a/scenes/manipulatorsphere.tres b/scenes/manipulatorsphere.tres new file mode 100644 index 0000000..da6c4f5 --- /dev/null +++ b/scenes/manipulatorsphere.tres @@ -0,0 +1,80 @@ +[gd_resource type="VisualShader" load_steps=7 format=3 uid="uid://dilr5r6k3ik7t"] + +[sub_resource type="VisualShaderNodeInput" id="VisualShaderNodeInput_5b52k"] +input_name = "normal" + +[sub_resource type="VisualShaderNodeDotProduct" id="VisualShaderNodeDotProduct_ktfjh"] + +[sub_resource type="VisualShaderNodeInput" id="VisualShaderNodeInput_s5g41"] +input_name = "view" + +[sub_resource type="VisualShaderNodeFloatOp" id="VisualShaderNodeFloatOp_1sq4e"] +default_input_values = [0, 1.0, 1, 0.0] +operator = 1 + +[sub_resource type="VisualShaderNodeFloatOp" id="VisualShaderNodeFloatOp_sui6q"] +default_input_values = [0, 0.0, 1, 5.0] +operator = 5 + +[sub_resource type="VisualShaderNodeColorParameter" id="VisualShaderNodeColorParameter_mm7j8"] +parameter_name = "Albedo" +default_value_enabled = true + +[resource] +code = "shader_type spatial; +render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_lambert, specular_schlick_ggx; + +uniform vec4 Albedo : source_color = vec4(1.000000, 1.000000, 1.000000, 1.000000); + + + +void fragment() { +// ColorParameter:8 + vec4 n_out8p0 = Albedo; + + +// Input:2 + vec3 n_out2p0 = NORMAL; + + +// Input:4 + vec3 n_out4p0 = VIEW; + + +// DotProduct:3 + float n_out3p0 = dot(n_out2p0, n_out4p0); + + +// FloatOp:5 + float n_in5p0 = 1.00000; + float n_out5p0 = n_in5p0 - n_out3p0; + + +// FloatOp:6 + float n_in6p1 = 5.00000; + float n_out6p0 = pow(n_out5p0, n_in6p1); + + +// Output:0 + ALBEDO = vec3(n_out8p0.xyz); + ALPHA = n_out6p0; + EMISSION = vec3(n_out6p0); + + +} +" +graph_offset = Vector2(-471.144, 48.9653) +nodes/fragment/0/position = Vector2(620, 180) +nodes/fragment/2/node = SubResource("VisualShaderNodeInput_5b52k") +nodes/fragment/2/position = Vector2(-940, 280) +nodes/fragment/3/node = SubResource("VisualShaderNodeDotProduct_ktfjh") +nodes/fragment/3/position = Vector2(-420, 280) +nodes/fragment/4/node = SubResource("VisualShaderNodeInput_s5g41") +nodes/fragment/4/position = Vector2(-940, 500) +nodes/fragment/5/node = SubResource("VisualShaderNodeFloatOp_1sq4e") +nodes/fragment/5/position = Vector2(-80, 180) +nodes/fragment/6/node = SubResource("VisualShaderNodeFloatOp_sui6q") +nodes/fragment/6/position = Vector2(280, 180) +nodes/fragment/8/node = SubResource("VisualShaderNodeColorParameter_mm7j8") +nodes/fragment/8/position = Vector2(-940, 720) +nodes/fragment/connections = PackedInt32Array(2, 0, 3, 0, 4, 0, 3, 1, 3, 0, 5, 1, 6, 0, 0, 1, 5, 0, 6, 0, 8, 0, 0, 0, 6, 0, 0, 5) diff --git a/scenes/particle_test.gdshader b/scenes/particle_test.gdshader index 0d7f114..4194e34 100644 --- a/scenes/particle_test.gdshader +++ b/scenes/particle_test.gdshader @@ -62,5 +62,5 @@ void vertex() { void fragment() { ALBEDO = vec3(1.0, 0.0, 0.0); - ALPHA = 0.05f; + ALPHA = 0.01f; } diff --git a/scenes/proxy_collision_detector.gd b/scenes/proxy_collision_detector.gd index 4ca54e7..ddf1155 100644 --- a/scenes/proxy_collision_detector.gd +++ b/scenes/proxy_collision_detector.gd @@ -62,9 +62,11 @@ func _ready(): # Clone the source particles and set the shader material. clone_particles = source_particles.duplicate() - # duplicate the draw pass so we don't affect the original. - clone_particles.draw_pass_1 = clone_particles.draw_pass_1.duplicate() - # replace with the special material + # diable trails since they add extra meshes. + clone_particles.trail_enabled = false + # replace the draw pass with a simple quad mesh. + clone_particles.draw_pass_1 = QuadMesh.new() + # and set the special material clone_particles.draw_pass_1.surface_set_material(0, shader_material) # set visual layer to 20 so it doesn't draw on main camera clone_particles.layers = 0 diff --git a/scenes/title_scene.GPUParticlesCollisionSDF3D_data.exr b/scenes/title_scene.GPUParticlesCollisionSDF3D_data.exr index daa057773886d1083e525cf20fe63e1d660e90d5..878c13ee29c0e47710a0ed072f19f781cf5f831a 100644 GIT binary patch delta 10218 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