diff --git a/modules/you_done_it/client_node.cpp b/modules/you_done_it/client_node.cpp index 64516447..d131f88b 100644 --- a/modules/you_done_it/client_node.cpp +++ b/modules/you_done_it/client_node.cpp @@ -13,6 +13,8 @@ void ClientNode::enter_tree() { if (singleton_instance) { print_error("Attempt to create duplicate ClientNode, aborting"); abort(); + } else { + singleton_instance = this; } } diff --git a/modules/you_done_it/clue_finder.cpp b/modules/you_done_it/clue_finder.cpp new file mode 100644 index 00000000..50afb267 --- /dev/null +++ b/modules/you_done_it/clue_finder.cpp @@ -0,0 +1,54 @@ +#include "clue_finder.h" +#include "scene/main/node.h" +#include + +ClueFinder *ClueFinder::singleton_instance{ nullptr }; + +void ClueFinder::_bind_methods() {} + +void ClueFinder::enter_tree() { + if (singleton_instance == nullptr) { + singleton_instance = this; + } else { + queue_free(); + } +} + +void ClueFinder::exit_tree() { + if (singleton_instance == this) { + singleton_instance = nullptr; + } +} + +void ClueFinder::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + case NOTIFICATION_ENTER_TREE: + enter_tree(); + return; + case NOTIFICATION_EXIT_TREE: + exit_tree(); + return; + default: + return; + } +} + +ClueFinder *ClueFinder::get_singleton() { + return singleton_instance; +} + +ClueMarker *ClueFinder::find_current_clue() { + for (ClueMarker *marker : this->clue_markers) { + if (marker->is_visible()) { + return marker; + } + } + return nullptr; +} + +void ClueFinder::register_clue_marker(ClueMarker *marker) { + this->clue_markers.insert(marker); +} diff --git a/modules/you_done_it/clue_finder.h b/modules/you_done_it/clue_finder.h new file mode 100644 index 00000000..b33dc2b2 --- /dev/null +++ b/modules/you_done_it/clue_finder.h @@ -0,0 +1,24 @@ +#pragma once + +#include "clue_marker.h" +#include +#include + +class ClueFinder : public Node3D { + GDCLASS(ClueFinder, Node3D); + static void _bind_methods(); + static ClueFinder *singleton_instance; + void enter_tree(); + void exit_tree(); + +protected: + void _notification(int what); + +public: + static ClueFinder *get_singleton(); + ClueMarker *find_current_clue(); + void register_clue_marker(ClueMarker *marker); + +private: + HashSet clue_markers{}; +}; diff --git a/modules/you_done_it/clue_marker.cpp b/modules/you_done_it/clue_marker.cpp new file mode 100644 index 00000000..c8201423 --- /dev/null +++ b/modules/you_done_it/clue_marker.cpp @@ -0,0 +1,34 @@ +#include "clue_marker.h" +#include "macros.h" +#include "you_done_it/clue_finder.h" +#include "you_done_it/ydi_networking.h" + +void ClueMarker::_bind_methods() { + BIND_HPROPERTY(Variant::INT, clue_id, PROPERTY_HINT_ENUM, NetworkData::ClueID_hint()); +} + +bool ClueMarker::is_visible() const { + Transform3D const viewpoint{ ClueFinder::get_singleton()->get_global_transform() }; + Basis const basis{ viewpoint.get_basis() }; + Vector3 const pos_relative{ get_global_position() - viewpoint.get_origin() }; + Vector3 const pos_transformed{ basis.get_column(0).dot(pos_relative), basis.get_column(1).dot(pos_relative), basis.get_column(2).dot(pos_relative) }; + if (pos_transformed.z <= 0.5f) { + return false; + } + Vector3 const transformed_dir{ pos_transformed.normalized() }; + if (Math::abs(transformed_dir.signed_angle_to({ 0, 0, 1 }, { 0, 1, 0 })) > Math::PI / 4.0) { + return false; + } + if (Math::abs(transformed_dir.signed_angle_to({ 0, 0, 1 }, { 1, 0, 0 })) > Math::PI / 4.0) { + return false; + } + return true; +} + +void ClueMarker::set_clue_id(NetworkData::ClueID id) { + this->id = id; +} + +NetworkData::ClueID ClueMarker::get_clue_id() const { + return this->id; +} diff --git a/modules/you_done_it/clue_marker.h b/modules/you_done_it/clue_marker.h new file mode 100644 index 00000000..1494397a --- /dev/null +++ b/modules/you_done_it/clue_marker.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ydi_networking.h" +#include + +class ClueMarker : public Node3D { + GDCLASS(ClueMarker, Node3D); + static void _bind_methods(); + +public: + bool is_visible() const; + void set_clue_id(NetworkData::ClueID id); + NetworkData::ClueID get_clue_id() const; + +private: + NetworkData::ClueID id{ NetworkData::CLUE_MAX }; +}; diff --git a/modules/you_done_it/macros.h b/modules/you_done_it/macros.h index 53be3185..1a882c6b 100644 --- a/modules/you_done_it/macros.h +++ b/modules/you_done_it/macros.h @@ -17,4 +17,12 @@ ADD_PROPERTY(PropertyInfo(m_type, #m_property), "set_" #m_property, \ "get_" #m_property) +#define __VA_ARGS__STRING(...) String(#__VA_ARGS__) + +#define GDENUM(M_Name, ...) \ + enum M_Name { __VA_ARGS__ }; \ + static String M_Name##_hint() { \ + return __VA_ARGS__STRING(__VA_ARGS__); \ + } + #endif // !GODOT_EXTRA_MACROS_H diff --git a/modules/you_done_it/register_types.cpp b/modules/you_done_it/register_types.cpp index 74139d10..fe1ed098 100644 --- a/modules/you_done_it/register_types.cpp +++ b/modules/you_done_it/register_types.cpp @@ -1,8 +1,11 @@ #include "register_types.h" #include "client_node.h" +#include "clue_marker.h" #include "server_node.h" #include "ydi_networking.h" +#include "ydi_vr_origin.h" +#include "you_done_it/clue_finder.h" #include void initialize_you_done_it_module(ModuleInitializationLevel p_level) { @@ -12,6 +15,9 @@ void initialize_you_done_it_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_abstract_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); } void uninitialize_you_done_it_module(ModuleInitializationLevel p_level) { diff --git a/modules/you_done_it/server_node.cpp b/modules/you_done_it/server_node.cpp index f56fbf65..02a16de7 100644 --- a/modules/you_done_it/server_node.cpp +++ b/modules/you_done_it/server_node.cpp @@ -17,9 +17,10 @@ void ServerNode::enter_tree() { if (singleton_instance) { print_error("Attempt to create duplicate ServerNode, aborting"); abort(); + } else { + singleton_instance = this; + ydi::server::open(); } - singleton_instance = this; - ydi::server::open(); } void ServerNode::process(double delta) { diff --git a/modules/you_done_it/ydi_client.cpp b/modules/you_done_it/ydi_client.cpp index 96b9a474..be03ffc2 100644 --- a/modules/you_done_it/ydi_client.cpp +++ b/modules/you_done_it/ydi_client.cpp @@ -132,7 +132,7 @@ void disconnect() { } NetworkData::ConnectionStatus status() { - return connection->status; + return connection ? NetworkData::ConnectionStatus(connection->status) : NetworkData::CONNECTION_DISCONNECTED; } namespace send { diff --git a/modules/you_done_it/ydi_networking.cpp b/modules/you_done_it/ydi_networking.cpp index 465bce8d..901326b0 100644 --- a/modules/you_done_it/ydi_networking.cpp +++ b/modules/you_done_it/ydi_networking.cpp @@ -3,9 +3,6 @@ #include #include -MAKE_TYPE_INFO(NetworkData::ClueID, Variant::INT); -MAKE_TYPE_INFO(NetworkData::ConnectionStatus, Variant::INT); - void NetworkData::_bind_methods() { BIND_ENUM_CONSTANT(CLUE_FIRST); BIND_ENUM_CONSTANT(CLUE_SECOND); diff --git a/modules/you_done_it/ydi_networking.h b/modules/you_done_it/ydi_networking.h index f922f0bf..b3d9ab44 100644 --- a/modules/you_done_it/ydi_networking.h +++ b/modules/you_done_it/ydi_networking.h @@ -1,5 +1,6 @@ #pragma once +#include "macros.h" #include #include #include @@ -13,31 +14,28 @@ class NetworkData : Object { static void _bind_methods(); public: - enum MessageType { - MSG_NONE = 0u, - // connection management messages - MSG_CONNECT = 1u, - MSG_OK, - MSG_NOK, - MSG_HEART, - MSG_BEAT, - // gameplay messages - MSG_REVEAL, - // end of messages - MSG_INVALID - }; + GDENUM(MessageType, + MSG_NONE, + // connection management messages + MSG_CONNECT, + MSG_OK, + MSG_NOK, + MSG_HEART, + MSG_BEAT, + // gameplay messages + MSG_REVEAL, + // end of messages + MSG_INVALID); - enum ClueID { - CLUE_FIRST, - CLUE_SECOND, - CLUE_MAX - }; + GDENUM(ClueID, + CLUE_FIRST, + CLUE_SECOND, + CLUE_MAX); - enum ConnectionStatus { - CONNECTION_DISCONNECTED, - CONNECTION_CONNECTED, - CONNECTION_AUTHENTICATED - }; + GDENUM(ConnectionStatus, + CONNECTION_DISCONNECTED, + CONNECTION_CONNECTED, + CONNECTION_AUTHENTICATED); enum NOKReason { NOK_UNAUTHENTICATED, //!< message sender is not known by recipient. @@ -48,6 +46,9 @@ public: }; }; +MAKE_TYPE_INFO(NetworkData::ClueID, Variant::INT); +MAKE_TYPE_INFO(NetworkData::ConnectionStatus, Variant::INT); + namespace ydi { int to_int(zmq::message_t const &msg, int failure = 0); NetworkData::MessageType to_message_type(zmq::message_t const &msg); @@ -64,19 +65,20 @@ void extend_multipart(zmq::multipart_t &mpart, std::string_view const &strv); void extend_multipart(zmq::multipart_t &mpart, char const *cstr); void extend_multipart(zmq::multipart_t &mpart, int const &arg); void extend_multipart(zmq::multipart_t &mpart, zmq::multipart_t const &right); -void extend_multipart(zmq::multipart_t &mpart); void extend_multipart(zmq::multipart_t &mpart, std::pair range); void extend_multipart(zmq::multipart_t &mpart, std::pair range); +void extend_multipart(zmq::multipart_t &mpart); + template -void extend_multipart_r(zmq::multipart_t &mpart, TArg const &arg) { +void extend_multipart_recurse(zmq::multipart_t &mpart, TArg const &arg) { extend_multipart(mpart, arg); } template -void extend_multipart_r(zmq::multipart_t &mpart, TArg const &arg, TArgs const &...args) { +void extend_multipart_recurse(zmq::multipart_t &mpart, TArg const &arg, TArgs const &...args) { extend_multipart(mpart, arg); - extend_multipart_r(mpart, args...); + extend_multipart_recurse(mpart, args...); } template @@ -89,7 +91,7 @@ zmq::multipart_t multipart(TArg const &arg) { template zmq::multipart_t multipart(TArg const &arg, TArgs const &...args) { zmq::multipart_t mpart{ multipart(arg) }; - extend_multipart_r(mpart, args...); + extend_multipart_recurse(mpart, args...); return mpart; } } //namespace ydi diff --git a/modules/you_done_it/ydi_vr_origin.cpp b/modules/you_done_it/ydi_vr_origin.cpp new file mode 100644 index 00000000..86d31561 --- /dev/null +++ b/modules/you_done_it/ydi_vr_origin.cpp @@ -0,0 +1,43 @@ +#include "ydi_vr_origin.h" +#include +#include +#include +#include + +void VROrigin::_bind_methods() {} + +void VROrigin::setup_vr_interface(Ref with_interface) { + if (with_interface.is_valid()) { + DisplayServer::get_singleton()->window_set_vsync_mode(DisplayServer::VSYNC_DISABLED); + get_viewport()->set_use_xr(true); + } +} + +void VROrigin::on_xr_interface_added(StringName interface) { + if (interface == "OpenXR") { + setup_vr_interface(XRServer::get_singleton()->find_interface(interface)); + XRServer::get_singleton()->disconnect("interface_added", callable_mp(this, &self_type::on_xr_interface_added)); + } +} + +void VROrigin::enter_tree() { + Ref xrint{ XRServer::get_singleton()->find_interface("OpenXR") }; + if (xrint.is_valid()) { + setup_vr_interface(xrint); + } else { + XRServer::get_singleton()->connect("interface_added", callable_mp(this, &self_type::on_xr_interface_added)); + } +} + +void VROrigin::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + case NOTIFICATION_ENTER_TREE: + enter_tree(); + return; + default: + return; + } +} diff --git a/modules/you_done_it/ydi_vr_origin.h b/modules/you_done_it/ydi_vr_origin.h new file mode 100644 index 00000000..75879f26 --- /dev/null +++ b/modules/you_done_it/ydi_vr_origin.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +class VROrigin : public XROrigin3D { + GDCLASS(VROrigin, XROrigin3D); + static void _bind_methods(); + +private: + void setup_vr_interface(Ref with_interface); + void on_xr_interface_added(StringName interface); + void enter_tree(); + +protected: + void _notification(int what); +}; diff --git a/vr-project/project.godot b/vr-project/project.godot index 019bfa99..c658dcf8 100644 --- a/vr-project/project.godot +++ b/vr-project/project.godot @@ -11,6 +11,7 @@ config_version=5 [application] config/name="you_done_it" +run/main_scene="uid://b5m5h30gog3pu" config/features=PackedStringArray("4.5", "Forward Plus") config/icon="res://icon.svg" diff --git a/vr-project/scenes/game_scene.tscn b/vr-project/scenes/game_scene.tscn index 834e2643..538131c5 100644 --- a/vr-project/scenes/game_scene.tscn +++ b/vr-project/scenes/game_scene.tscn @@ -28,6 +28,9 @@ glow_enabled = true [node name="Root" type="Node3D"] +[node name="VROrigin" parent="." instance=ExtResource("2_onqr8")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0004169941, 0, -0.95417106) + [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_bw6k5") @@ -41,15 +44,13 @@ light_angular_distance = 1.0 light_specular = 5.667 light_bake_mode = 1 shadow_enabled = true -shadow_bias = 0.0 -shadow_normal_bias = 0.0 +shadow_normal_bias = 0.1 shadow_blur = 0.75 -directional_shadow_mode = 0 directional_shadow_fade_start = 0.965 directional_shadow_max_distance = 20.0 -[node name="XROrigin3D" parent="." instance=ExtResource("2_onqr8")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0004169941, 0, -0.95417106) - [node name="Block Out VR" parent="." instance=ExtResource("1_w5aja")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.27989578, 0, 1.4924412) + +[node name="ClueMarker" type="ClueMarker" parent="."] +clue_id = 0 diff --git a/vr-project/vr_base.tscn b/vr-project/vr_base.tscn index 82a64dc7..f317419c 100644 --- a/vr-project/vr_base.tscn +++ b/vr-project/vr_base.tscn @@ -1,13 +1,25 @@ -[gd_scene format=3 uid="uid://ctf3dsro4aqon"] +[gd_scene load_steps=2 format=3 uid="uid://ctf3dsro4aqon"] -[node name="XROrigin3D" type="XROrigin3D"] +[sub_resource type="CylinderMesh" id="CylinderMesh_y71p3"] +top_radius = 0.05 +bottom_radius = 0.05 +height = 0.05 + +[node name="VROrigin" type="VROrigin"] [node name="XRCamera3D" type="XRCamera3D" parent="."] +current = true [node name="LeftController" type="XRController3D" parent="."] tracker = &"left_hand" +[node name="MeshInstance3D" type="MeshInstance3D" parent="LeftController"] +mesh = SubResource("CylinderMesh_y71p3") + [node name="RightController" type="XRController3D" parent="."] tracker = &"right_hand" +[node name="MeshInstance3D" type="MeshInstance3D" parent="RightController"] +mesh = SubResource("CylinderMesh_y71p3") + [node name="ClientNode" type="ClientNode" parent="."]