diff --git a/.gitignore b/.gitignore index d412245..7758125 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ .DS_Store logs Thumbs.db +.vscode/ diff --git a/demo/Construct.gd b/demo/Construct.gd index f86660c..98765b5 100644 --- a/demo/Construct.gd +++ b/demo/Construct.gd @@ -16,3 +16,16 @@ func _ready(): get_tree().get_root().connect("size_changed", self, "on_window_size_change") on_window_size_change(); + +# Manually toggle pause +func _on_Pause_released(): + get_tree().paused = !get_tree().paused + +# If the dashboard is open, hide the player to avoid depth confusion +# The dashboard renders its own models for the controllers +func _on_PauseOnDashboard_dashboard_opened(): + $Viewport/Main/Player.visible = false + +# Restore our player when the dashboard closes +func _on_PauseOnDashboard_dashboard_closed(): + $Viewport/Main/Player.visible = true diff --git a/demo/Construct.tscn b/demo/Construct.tscn index 1082891..d18c2c8 100644 --- a/demo/Construct.tscn +++ b/demo/Construct.tscn @@ -1,7 +1,9 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=7 format=2] [ext_resource path="res://Main.tscn" type="PackedScene" id=1] [ext_resource path="res://Construct.gd" type="Script" id=2] +[ext_resource path="res://addons/godot-openvr/OpenVRFocus.gdns" type="Script" id=3] +[ext_resource path="res://addons/godot-openvr/OpenVRAction.gdns" type="Script" id=4] [sub_resource type="Shader" id=1] code = "shader_type canvas_item; @@ -15,7 +17,6 @@ void fragment() { COLOR = color; }" -custom_defines = "" [sub_resource type="ShaderMaterial" id=2] shader = SubResource( 1 ) @@ -45,13 +46,33 @@ shadow_atlas_size = 4096 [node name="Main" parent="Viewport" instance=ExtResource( 1 )] -[node name="Player" parent="Viewport/Main" index="1"] -viewport = NodePath("../..") +[node name="Left_Hand" parent="Viewport/Main/Player" index="1"] +pause_mode = 1 + +[node name="Right_Hand" parent="Viewport/Main/Player" index="2"] +pause_mode = 1 [node name="ovr_left_hand" parent="Viewport/Main/Player" index="6"] +pause_mode = 1 transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 1.25, 0 ) [node name="ovr_right_hand" parent="Viewport/Main/Player" index="7"] +pause_mode = 1 transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 1.25, 0 ) +[node name="Pause" type="Node" parent="Viewport/Main"] +pause_mode = 2 +script = ExtResource( 4 ) +pressed_action = "/actions/godot/in/button_ax" +on_hand = 1 + +[node name="PauseOnDashboard" type="Node" parent="Viewport/Main"] +pause_mode = 2 +script = ExtResource( 3 ) +use_auto_pause = true + +[connection signal="released" from="Viewport/Main/Pause" to="." method="_on_Pause_released"] +[connection signal="dashboard_closed" from="Viewport/Main/PauseOnDashboard" to="." method="_on_PauseOnDashboard_dashboard_closed"] +[connection signal="dashboard_opened" from="Viewport/Main/PauseOnDashboard" to="." method="_on_PauseOnDashboard_dashboard_opened"] + [editable path="Viewport/Main"] diff --git a/demo/addons/godot-openvr/OpenVRFocus.gdns b/demo/addons/godot-openvr/OpenVRFocus.gdns new file mode 100644 index 0000000..1d5ed13 --- /dev/null +++ b/demo/addons/godot-openvr/OpenVRFocus.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://addons/godot-openvr/godot_openvr.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "OpenVRFocus" +class_name = "OpenVRFocus" +library = ExtResource( 1 ) diff --git a/demo/addons/godot-openvr/bin/win64/libgodot_openvr.dll b/demo/addons/godot-openvr/bin/win64/libgodot_openvr.dll index fd9562f..1c5d55d 100644 Binary files a/demo/addons/godot-openvr/bin/win64/libgodot_openvr.dll and b/demo/addons/godot-openvr/bin/win64/libgodot_openvr.dll differ diff --git a/demo/misc/Box.tscn b/demo/misc/Box.tscn index ccb0040..7e41dff 100644 --- a/demo/misc/Box.tscn +++ b/demo/misc/Box.tscn @@ -10,6 +10,7 @@ size = Vector3( 1, 1, 1 ) extents = Vector3( 0.5, 0.5, 0.5 ) [node name="Box" type="RigidBody"] +pause_mode = 1 collision_layer = 2 collision_mask = 2 diff --git a/demo/misc/Cup.tscn b/demo/misc/Cup.tscn index 1ea37eb..349fb4a 100644 --- a/demo/misc/Cup.tscn +++ b/demo/misc/Cup.tscn @@ -9,6 +9,7 @@ radius = 0.06 height = 0.109788 [node name="Cup" instance=ExtResource( 1 )] +pause_mode = 1 collision_layer = 4 reset_transform_on_pickup = false highlight_mesh_instance = NodePath("MeshInstance") diff --git a/demo/player/Guardian.gd b/demo/player/Guardian.gd index 527790e..e4c885a 100644 --- a/demo/player/Guardian.gd +++ b/demo/player/Guardian.gd @@ -6,119 +6,119 @@ var ws : float = 0.0 var reference : Transform = Transform() func _ready(): - # todo subscribe to our play area changed signal - pass + # todo subscribe to our play area changed signal + pass func _process(delta): - # if either of these change, our play area needs to be adjusted. - var new_ws = ARVRServer.world_scale - var new_reference = ARVRServer.get_reference_frame() - - if ws!=new_ws or reference!=new_reference: - var openvr_config = get_parent().get_openvr_config() - if !openvr_config: - # can't update this yet - return - - if !openvr_config.play_area_available(): - # can't update this yet - return - - # get our play area, ws and our reference frame have already been applied - var play_area = openvr_config.get_play_area() - var h = Vector3(0.0, height, 0.0) - - var st = SurfaceTool.new() - st.begin(Mesh.PRIMITIVE_TRIANGLES) - - # floor - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[0]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[2]) - st.add_uv(Vector2(1,0)) - st.add_vertex(play_area[1]) - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[0]) - st.add_uv(Vector2(0,1)) - st.add_vertex(play_area[3]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[2]) - - # ceiling - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[0] + h) - st.add_uv(Vector2(1,0)) - st.add_vertex(play_area[1] + h) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[2] + h) - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[0] + h) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[2] + h) - st.add_uv(Vector2(0,1)) - st.add_vertex(play_area[3] + h) - - # side A - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[0]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[1] + h) - st.add_uv(Vector2(1,0)) - st.add_vertex(play_area[0] + h) - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[0]) - st.add_uv(Vector2(0,1)) - st.add_vertex(play_area[1]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[1] + h) - - # side B - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[3]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[0] + h) - st.add_uv(Vector2(1,0)) - st.add_vertex(play_area[3] + h) - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[3]) - st.add_uv(Vector2(0,1)) - st.add_vertex(play_area[0]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[0] + h) - - # side C - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[1]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[2] + h) - st.add_uv(Vector2(1,0)) - st.add_vertex(play_area[1] + h) - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[1]) - st.add_uv(Vector2(0,1)) - st.add_vertex(play_area[2]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[2] + h) - - # side D - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[2]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[3] + h) - st.add_uv(Vector2(1,0)) - st.add_vertex(play_area[2] + h) - st.add_uv(Vector2(0,0)) - st.add_vertex(play_area[2]) - st.add_uv(Vector2(0,1)) - st.add_vertex(play_area[3]) - st.add_uv(Vector2(1,1)) - st.add_vertex(play_area[3] + h) - - st.generate_normals() - st.generate_tangents() - - # we've updated it - mesh = st.commit() - ws = new_ws - reference = new_reference + # if either of these change, our play area needs to be adjusted. + var new_ws = ARVRServer.world_scale + var new_reference = ARVRServer.get_reference_frame() + + if ws!=new_ws or reference!=new_reference: + var openvr_config = get_parent().get_openvr_config() + if !openvr_config: + # can't update this yet + return + + if !openvr_config.play_area_available(): + # can't update this yet + return + + # get our play area, ws and our reference frame have already been applied + var play_area = openvr_config.get_play_area() + var h = Vector3(0.0, height, 0.0) + + var st = SurfaceTool.new() + st.begin(Mesh.PRIMITIVE_TRIANGLES) + + # floor + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[0]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[2]) + st.add_uv(Vector2(1,0)) + st.add_vertex(play_area[1]) + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[0]) + st.add_uv(Vector2(0,1)) + st.add_vertex(play_area[3]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[2]) + + # ceiling + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[0] + h) + st.add_uv(Vector2(1,0)) + st.add_vertex(play_area[1] + h) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[2] + h) + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[0] + h) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[2] + h) + st.add_uv(Vector2(0,1)) + st.add_vertex(play_area[3] + h) + + # side A + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[0]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[1] + h) + st.add_uv(Vector2(1,0)) + st.add_vertex(play_area[0] + h) + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[0]) + st.add_uv(Vector2(0,1)) + st.add_vertex(play_area[1]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[1] + h) + + # side B + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[3]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[0] + h) + st.add_uv(Vector2(1,0)) + st.add_vertex(play_area[3] + h) + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[3]) + st.add_uv(Vector2(0,1)) + st.add_vertex(play_area[0]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[0] + h) + + # side C + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[1]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[2] + h) + st.add_uv(Vector2(1,0)) + st.add_vertex(play_area[1] + h) + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[1]) + st.add_uv(Vector2(0,1)) + st.add_vertex(play_area[2]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[2] + h) + + # side D + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[2]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[3] + h) + st.add_uv(Vector2(1,0)) + st.add_vertex(play_area[2] + h) + st.add_uv(Vector2(0,0)) + st.add_vertex(play_area[2]) + st.add_uv(Vector2(0,1)) + st.add_vertex(play_area[3]) + st.add_uv(Vector2(1,1)) + st.add_vertex(play_area[3] + h) + + st.generate_normals() + st.generate_tangents() + + # we've updated it + mesh = st.commit() + ws = new_ws + reference = new_reference diff --git a/src/godot_openvr.cpp b/src/godot_openvr.cpp index fac2dcd..7da1db9 100644 --- a/src/godot_openvr.cpp +++ b/src/godot_openvr.cpp @@ -36,4 +36,5 @@ void GDN_EXPORT godot_openvr_nativescript_init(void *p_handle) { godot::register_class(); godot::register_class(); godot::register_class(); + godot::register_class(); } diff --git a/src/godot_openvr.h b/src/godot_openvr.h index ff4f2c1..13351d9 100644 --- a/src/godot_openvr.h +++ b/src/godot_openvr.h @@ -18,6 +18,7 @@ #include "OpenVRPose.h" #include "OpenVRRenderModel.h" #include "OpenVRSkeleton.h" +#include "OpenVRFocus.h" #ifdef __cplusplus extern "C" { diff --git a/src/open_vr/OpenVRFocus.cpp b/src/open_vr/OpenVRFocus.cpp new file mode 100644 index 0000000..e439451 --- /dev/null +++ b/src/open_vr/OpenVRFocus.cpp @@ -0,0 +1,60 @@ +#include "OpenVRFocus.h" + +using namespace godot; + +void OpenVRFocus::_register_methods() { + register_method("_process", &OpenVRFocus::_process); + + register_method("get_is_dashboard_active", &OpenVRFocus::get_is_dashboard_active); + + register_property("use_auto_pause", &OpenVRFocus::set_auto_pause_on_dashboard, &OpenVRFocus::get_auto_pause_on_dashboard, false); + + register_signal(String("dashboard_opened"), Dictionary()); + register_signal(String("dashboard_closed"), Dictionary()); +} + +void OpenVRFocus::_init() { + dashboard_triggers_pause = false; +} + +void OpenVRFocus::_process(float delta) { + if (ovr != NULL) { + bool is_dashboard_active_current = ovr->is_dashboard_active(); + if (is_dashboard_active != is_dashboard_active_current) { + is_dashboard_active = is_dashboard_active_current; + if (is_dashboard_active) { + emit_signal("dashboard_opened"); + if (dashboard_triggers_pause) { + get_tree()->set_pause(true); + } + } else { + emit_signal("dashboard_closed"); + // Never auto un-pause as the player will want to confirm game status themselves first + } + } + } +} + +bool OpenVRFocus::get_is_dashboard_active() { + return is_dashboard_active; +} + +bool OpenVRFocus::get_auto_pause_on_dashboard() { + return dashboard_triggers_pause; +} + +void OpenVRFocus::set_auto_pause_on_dashboard(bool p_auto) { + dashboard_triggers_pause = p_auto; +} + +OpenVRFocus::OpenVRFocus() { + ovr = openvr_data::retain_singleton(); + is_dashboard_active = false; +} + +OpenVRFocus::~OpenVRFocus() { + if (ovr != NULL) { + ovr->release(); + ovr = NULL; + } +} \ No newline at end of file diff --git a/src/open_vr/OpenVRFocus.h b/src/open_vr/OpenVRFocus.h new file mode 100644 index 0000000..3bb3819 --- /dev/null +++ b/src/open_vr/OpenVRFocus.h @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////////////////////// +// GDNative module that exposes some OpenVR focus events and status to Godot +// +// Written by James "Nidonocu" Shaw + +#ifndef OPENVR_FOCUS_H +#define OPENVR_FOCUS_H + +#include "openvr_data.h" +#include +#include + +namespace godot { + +class OpenVRFocus : public Node { + GODOT_CLASS(OpenVRFocus, Node) + +private: + openvr_data *ovr; + + bool is_dashboard_active; + bool dashboard_triggers_pause; + +protected: +public: + static void _register_methods(); + + void _init(); + void _process(float delta); + + OpenVRFocus(); + ~OpenVRFocus(); + + bool get_is_dashboard_active(); + bool get_auto_pause_on_dashboard(); + void set_auto_pause_on_dashboard(bool p_auto); +}; + +} // namespace godot + +#endif /* !OPENVR_FOCUS_H */ \ No newline at end of file diff --git a/src/open_vr/openvr_data.cpp b/src/open_vr/openvr_data.cpp index 294448e..f977619 100644 --- a/src/open_vr/openvr_data.cpp +++ b/src/open_vr/openvr_data.cpp @@ -29,6 +29,8 @@ openvr_data::openvr_data() { int default_action_set = register_action_set(String("/actions/godot")); action_sets[default_action_set].is_active = true; active_action_set_count = 1; + + dashboard_open = false; } openvr_data::~openvr_data() { @@ -313,6 +315,14 @@ void openvr_data::process() { case vr::VREvent_ChaperoneDataHasChanged: { play_area_is_dirty = true; }; break; + case vr::VREvent_DashboardActivated: { + Godot::print(String("Steam VR Dashboard Opened")); + dashboard_open = true; + }; break; + case vr::VREvent_DashboardDeactivated: { + Godot::print(String("Steam VR Dashboard Closed")); + dashboard_open = false; + }; break; default: { // ignored for now... }; break; @@ -436,6 +446,10 @@ const godot::Vector3 *openvr_data::get_play_area() const { return play_area; } +bool openvr_data::is_dashboard_active() { + return dashboard_open; +} + //////////////////////////////////////////////////////////////// // interact with openvr diff --git a/src/open_vr/openvr_data.h b/src/open_vr/openvr_data.h index bd4d0a6..f273a8c 100644 --- a/src/open_vr/openvr_data.h +++ b/src/open_vr/openvr_data.h @@ -157,6 +157,8 @@ class openvr_data { void load_texture(TextureType p_type, vr::TextureID_t p_texture_id, godot::Ref p_material); bool _load_texture(texture_material *p_texture); + bool dashboard_open; + public: vr::IVRSystem *hmd; // make this private? @@ -182,6 +184,7 @@ class openvr_data { void set_tracking_universe(OpenVRTrackingUniverse p_new_value); bool play_area_available() const; const godot::Vector3 *get_play_area() const; + bool is_dashboard_active(); // interact with openvr void get_recommended_rendertarget_size(uint32_t *p_width, uint32_t *p_height);