diff options
author | Malcolm Nixon <Malcolm.nixon@gmail.com> | 2024-04-13 17:26:46 -0400 |
---|---|---|
committer | Malcolm Nixon <Malcolm.nixon@gmail.com> | 2024-04-18 20:04:01 -0400 |
commit | 823ae7b3fa25832d1cd9d7613c650bfc712d1f49 (patch) | |
tree | 36a0c75dd36faf7c15cf81a78267f71c62b3009e /scene | |
parent | 3b1806182a3564736ad64793b203c2c13c251f56 (diff) | |
download | redot-engine-823ae7b3fa25832d1cd9d7613c650bfc712d1f49.tar.gz |
Rework XR Trackers to have a common ancestor. Allow creation of XRNode3D to drive node positions and visibility.
Diffstat (limited to 'scene')
-rw-r--r-- | scene/3d/xr_body_modifier_3d.cpp | 45 | ||||
-rw-r--r-- | scene/3d/xr_body_modifier_3d.h | 6 | ||||
-rw-r--r-- | scene/3d/xr_face_modifier_3d.cpp | 8 | ||||
-rw-r--r-- | scene/3d/xr_face_modifier_3d.h | 2 | ||||
-rw-r--r-- | scene/3d/xr_hand_modifier_3d.cpp | 109 | ||||
-rw-r--r-- | scene/3d/xr_hand_modifier_3d.h | 4 | ||||
-rw-r--r-- | scene/3d/xr_nodes.cpp | 37 | ||||
-rw-r--r-- | scene/3d/xr_nodes.h | 4 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 2 |
9 files changed, 103 insertions, 114 deletions
diff --git a/scene/3d/xr_body_modifier_3d.cpp b/scene/3d/xr_body_modifier_3d.cpp index 0099784a05..8aec3e856e 100644 --- a/scene/3d/xr_body_modifier_3d.cpp +++ b/scene/3d/xr_body_modifier_3d.cpp @@ -44,13 +44,9 @@ void XRBodyModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &XRBodyModifier3D::set_bone_update); ClassDB::bind_method(D_METHOD("get_bone_update"), &XRBodyModifier3D::get_bone_update); - ClassDB::bind_method(D_METHOD("set_show_when_tracked", "show"), &XRBodyModifier3D::set_show_when_tracked); - ClassDB::bind_method(D_METHOD("get_show_when_tracked"), &XRBodyModifier3D::get_show_when_tracked); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "body_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/body"), "set_body_tracker", "get_body_tracker"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "body_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/body_tracker"), "set_body_tracker", "get_body_tracker"); ADD_PROPERTY(PropertyInfo(Variant::INT, "body_update", PROPERTY_HINT_FLAGS, "Upper Body,Lower Body,Hands"), "set_body_update", "get_body_update"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_when_tracked"), "set_show_when_tracked", "get_show_when_tracked"); BIND_BITFIELD_FLAG(BODY_UPDATE_UPPER_BODY); BIND_BITFIELD_FLAG(BODY_UPDATE_LOWER_BODY); @@ -86,14 +82,6 @@ XRBodyModifier3D::BoneUpdate XRBodyModifier3D::get_bone_update() const { return bone_update; } -void XRBodyModifier3D::set_show_when_tracked(bool p_show_when_tracked) { - show_when_tracked = p_show_when_tracked; -} - -bool XRBodyModifier3D::get_show_when_tracked() const { - return show_when_tracked; -} - void XRBodyModifier3D::_get_joint_data() { // Table of Godot Humanoid bone names. static const String bone_names[XRBodyTracker::JOINT_MAX] = { @@ -189,7 +177,7 @@ void XRBodyModifier3D::_get_joint_data() { joints[i].parent_joint = -1; } - Skeleton3D *skeleton = get_skeleton(); + const Skeleton3D *skeleton = get_skeleton(); if (!skeleton) { return; } @@ -257,27 +245,22 @@ void XRBodyModifier3D::_process_modification() { return; } - XRServer *xr_server = XRServer::get_singleton(); + const XRServer *xr_server = XRServer::get_singleton(); if (!xr_server) { return; } - Ref<XRBodyTracker> tracker = xr_server->get_body_tracker(tracker_name); - if (tracker.is_null()) { + const Ref<XRBodyTracker> tracker = xr_server->get_tracker(tracker_name); + if (!tracker.is_valid()) { return; } - // Handle no tracking data. + // Skip if no tracking data. if (!tracker->get_has_tracking_data()) { - // If tracking-state determines visibility then hide the node. - if (show_when_tracked) { - set_visible(false); - } return; } // Get the world and skeleton scale. - const float ws = xr_server->get_world_scale(); const float ss = skeleton->get_motion_scale(); // Read the relevant tracking data. This applies the skeleton motion scale to @@ -296,12 +279,8 @@ void XRBodyModifier3D::_process_modification() { } } - // Handle root joint not tracked. + // Skip if root joint not tracked. if (!has_valid_data[XRBodyTracker::JOINT_ROOT]) { - // If tracking-state determines visibility then hide the node. - if (show_when_tracked) { - set_visible(false); - } return; } @@ -331,16 +310,6 @@ void XRBodyModifier3D::_process_modification() { // Always update the bone rotation. skeleton->set_bone_pose_rotation(joints[joint].bone, Quaternion(relative_transform.basis)); } - - // Transform to the tracking data root pose. This also applies the XR world-scale to allow - // scaling the avatars mesh and skeleton appropriately (if they are child nodes). - set_transform( - transforms[XRBodyTracker::JOINT_ROOT] * ws); - - // If tracking-state determines visibility then show the node. - if (show_when_tracked) { - set_visible(true); - } } void XRBodyModifier3D::_tracker_changed(const StringName &p_tracker_name, const Ref<XRBodyTracker> &p_tracker) { diff --git a/scene/3d/xr_body_modifier_3d.h b/scene/3d/xr_body_modifier_3d.h index 03b1c07d53..9ff0cd7207 100644 --- a/scene/3d/xr_body_modifier_3d.h +++ b/scene/3d/xr_body_modifier_3d.h @@ -66,9 +66,6 @@ public: void set_bone_update(BoneUpdate p_bone_update); BoneUpdate get_bone_update() const; - void set_show_when_tracked(bool p_show_when_tracked); - bool get_show_when_tracked() const; - void _notification(int p_what); protected: @@ -83,10 +80,9 @@ private: int parent_joint = -1; }; - StringName tracker_name = "/user/body"; + StringName tracker_name = "/user/body_tracker"; BitField<BodyUpdate> body_update = BODY_UPDATE_UPPER_BODY | BODY_UPDATE_LOWER_BODY | BODY_UPDATE_HANDS; BoneUpdate bone_update = BONE_UPDATE_FULL; - bool show_when_tracked = true; JointData joints[XRBodyTracker::JOINT_MAX]; void _get_joint_data(); diff --git a/scene/3d/xr_face_modifier_3d.cpp b/scene/3d/xr_face_modifier_3d.cpp index be92a587b0..43cef95fb9 100644 --- a/scene/3d/xr_face_modifier_3d.cpp +++ b/scene/3d/xr_face_modifier_3d.cpp @@ -495,7 +495,7 @@ static void remove_driven_unified_blend_shapes(RBMap<int, int> &p_blend_mapping) void XRFaceModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_face_tracker", "tracker_name"), &XRFaceModifier3D::set_face_tracker); ClassDB::bind_method(D_METHOD("get_face_tracker"), &XRFaceModifier3D::get_face_tracker); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "face_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/head"), "set_face_tracker", "get_face_tracker"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "face_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/face_tracker"), "set_face_tracker", "get_face_tracker"); ClassDB::bind_method(D_METHOD("set_target", "target"), &XRFaceModifier3D::set_target); ClassDB::bind_method(D_METHOD("get_target"), &XRFaceModifier3D::get_target); @@ -576,8 +576,8 @@ void XRFaceModifier3D::_update_face_blends() const { } // Get the face tracker. - const Ref<XRFaceTracker> p = xr_server->get_face_tracker(tracker_name); - if (!p.is_valid()) { + const Ref<XRFaceTracker> tracker = xr_server->get_tracker(tracker_name); + if (!tracker.is_valid()) { return; } @@ -588,7 +588,7 @@ void XRFaceModifier3D::_update_face_blends() const { } // Get the blend weights. - const PackedFloat32Array weights = p->get_blend_shapes(); + const PackedFloat32Array weights = tracker->get_blend_shapes(); // Apply all the face blend weights to the mesh. for (const KeyValue<int, int> &it : blend_mapping) { diff --git a/scene/3d/xr_face_modifier_3d.h b/scene/3d/xr_face_modifier_3d.h index 147c374e95..e5e59afe1d 100644 --- a/scene/3d/xr_face_modifier_3d.h +++ b/scene/3d/xr_face_modifier_3d.h @@ -47,7 +47,7 @@ class XRFaceModifier3D : public Node3D { GDCLASS(XRFaceModifier3D, Node3D); private: - StringName tracker_name = "/user/head"; + StringName tracker_name = "/user/face_tracker"; NodePath target; // Map from XRFaceTracker blend shape index to mesh blend shape index. diff --git a/scene/3d/xr_hand_modifier_3d.cpp b/scene/3d/xr_hand_modifier_3d.cpp index 7fecb53008..1e78a4630f 100644 --- a/scene/3d/xr_hand_modifier_3d.cpp +++ b/scene/3d/xr_hand_modifier_3d.cpp @@ -40,7 +40,7 @@ void XRHandModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &XRHandModifier3D::set_bone_update); ClassDB::bind_method(D_METHOD("get_bone_update"), &XRHandModifier3D::get_bone_update); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "hand_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/left,/user/right"), "set_hand_tracker", "get_hand_tracker"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "hand_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/hand_tracker/left,/user/hand_tracker/right"), "set_hand_tracker", "get_hand_tracker"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update"); BIND_ENUM_CONSTANT(BONE_UPDATE_FULL); @@ -111,22 +111,30 @@ void XRHandModifier3D::_get_joint_data() { joints[i].parent_joint = -1; } - Skeleton3D *skeleton = get_skeleton(); + const Skeleton3D *skeleton = get_skeleton(); if (!skeleton) { return; } - XRServer *xr_server = XRServer::get_singleton(); + const XRServer *xr_server = XRServer::get_singleton(); if (!xr_server) { return; } - Ref<XRHandTracker> tracker = xr_server->get_hand_tracker(tracker_name); + const Ref<XRHandTracker> tracker = xr_server->get_tracker(tracker_name); if (tracker.is_null()) { return; } - XRHandTracker::Hand hand = tracker->get_hand(); + // Verify we have a left or right hand tracker. + const XRPositionalTracker::TrackerHand tracker_hand = tracker->get_tracker_hand(); + if (tracker_hand != XRPositionalTracker::TRACKER_HAND_LEFT && + tracker_hand != XRPositionalTracker::TRACKER_HAND_RIGHT) { + return; + } + + // Get the hand index (0 = left, 1 = right). + const int hand = tracker_hand == XRPositionalTracker::TRACKER_HAND_LEFT ? 0 : 1; // Find the skeleton-bones associated with each joint. int bones[XRHandTracker::HAND_JOINT_MAX]; @@ -176,18 +184,22 @@ void XRHandModifier3D::_process_modification() { return; } - XRServer *xr_server = XRServer::get_singleton(); + const XRServer *xr_server = XRServer::get_singleton(); if (!xr_server) { return; } - Ref<XRHandTracker> tracker = xr_server->get_hand_tracker(tracker_name); + const Ref<XRHandTracker> tracker = xr_server->get_tracker(tracker_name); if (tracker.is_null()) { return; } + // Skip if no tracking data + if (!tracker->get_has_tracking_data()) { + return; + } + // Get the world and skeleton scale. - const float ws = xr_server->get_world_scale(); const float ss = skeleton->get_motion_scale(); // We cache our transforms so we can quickly calculate local transforms. @@ -195,55 +207,44 @@ void XRHandModifier3D::_process_modification() { Transform3D transforms[XRHandTracker::HAND_JOINT_MAX]; Transform3D inv_transforms[XRHandTracker::HAND_JOINT_MAX]; - if (tracker->get_has_tracking_data()) { - for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) { - BitField<XRHandTracker::HandJointFlags> flags = tracker->get_hand_joint_flags((XRHandTracker::HandJoint)joint); - has_valid_data[joint] = flags.has_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID); + for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) { + BitField<XRHandTracker::HandJointFlags> flags = tracker->get_hand_joint_flags((XRHandTracker::HandJoint)joint); + has_valid_data[joint] = flags.has_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID); - if (has_valid_data[joint]) { - transforms[joint] = tracker->get_hand_joint_transform((XRHandTracker::HandJoint)joint); - transforms[joint].origin *= ss; - inv_transforms[joint] = transforms[joint].inverse(); - } + if (has_valid_data[joint]) { + transforms[joint] = tracker->get_hand_joint_transform((XRHandTracker::HandJoint)joint); + transforms[joint].origin *= ss; + inv_transforms[joint] = transforms[joint].inverse(); } + } - if (has_valid_data[XRHandTracker::HAND_JOINT_PALM]) { - for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) { - // Get the skeleton bone (skip if none). - const int bone = joints[joint].bone; - if (bone == -1) { - continue; - } - - // Calculate the relative relationship to the parent bone joint. - const int parent_joint = joints[joint].parent_joint; - const Transform3D relative_transform = inv_transforms[parent_joint] * transforms[joint]; - - // Update the bone position if enabled by update mode. - if (bone_update == BONE_UPDATE_FULL) { - skeleton->set_bone_pose_position(joints[joint].bone, relative_transform.origin); - } - - // Always update the bone rotation. - skeleton->set_bone_pose_rotation(joints[joint].bone, Quaternion(relative_transform.basis)); - } + // Skip if palm has no tracking data + if (!has_valid_data[XRHandTracker::HAND_JOINT_PALM]) { + return; + } + + for (int joint = 0; joint < XRHandTracker::HAND_JOINT_MAX; joint++) { + // Get the skeleton bone (skip if none). + const int bone = joints[joint].bone; + if (bone == -1) { + continue; + } - // Transform to the skeleton pose. This uses the HAND_JOINT_PALM position without skeleton-scaling, as it - // must be positioned to match the physical hand location. It is scaled with the world space to match - // the scaling done to the camera and eyes. - set_transform( - tracker->get_hand_joint_transform(XRHandTracker::HAND_JOINT_PALM) * ws); + // Calculate the relative relationship to the parent bone joint. + const int parent_joint = joints[joint].parent_joint; + const Transform3D relative_transform = inv_transforms[parent_joint] * transforms[joint]; - set_visible(true); - } else { - set_visible(false); + // Update the bone position if enabled by update mode. + if (bone_update == BONE_UPDATE_FULL) { + skeleton->set_bone_pose_position(joints[joint].bone, relative_transform.origin); } - } else { - set_visible(false); + + // Always update the bone rotation. + skeleton->set_bone_pose_rotation(joints[joint].bone, Quaternion(relative_transform.basis)); } } -void XRHandModifier3D::_tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker) { +void XRHandModifier3D::_tracker_changed(StringName p_tracker_name, XRServer::TrackerType p_tracker_type) { if (tracker_name == p_tracker_name) { _get_joint_data(); } @@ -258,9 +259,9 @@ void XRHandModifier3D::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { - xr_server->connect("hand_tracker_added", callable_mp(this, &XRHandModifier3D::_tracker_changed)); - xr_server->connect("hand_tracker_updated", callable_mp(this, &XRHandModifier3D::_tracker_changed)); - xr_server->connect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>())); + xr_server->connect("tracker_added", callable_mp(this, &XRHandModifier3D::_tracker_changed)); + xr_server->connect("tracker_updated", callable_mp(this, &XRHandModifier3D::_tracker_changed)); + xr_server->connect("tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed)); } _get_joint_data(); @@ -268,9 +269,9 @@ void XRHandModifier3D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { XRServer *xr_server = XRServer::get_singleton(); if (xr_server) { - xr_server->disconnect("hand_tracker_added", callable_mp(this, &XRHandModifier3D::_tracker_changed)); - xr_server->disconnect("hand_tracker_updated", callable_mp(this, &XRHandModifier3D::_tracker_changed)); - xr_server->disconnect("hand_tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed).bind(Ref<XRHandTracker>())); + xr_server->disconnect("tracker_added", callable_mp(this, &XRHandModifier3D::_tracker_changed)); + xr_server->disconnect("tracker_updated", callable_mp(this, &XRHandModifier3D::_tracker_changed)); + xr_server->disconnect("tracker_removed", callable_mp(this, &XRHandModifier3D::_tracker_changed)); } for (int i = 0; i < XRHandTracker::HAND_JOINT_MAX; i++) { diff --git a/scene/3d/xr_hand_modifier_3d.h b/scene/3d/xr_hand_modifier_3d.h index 9f7ce45c9d..67d1694d41 100644 --- a/scene/3d/xr_hand_modifier_3d.h +++ b/scene/3d/xr_hand_modifier_3d.h @@ -69,12 +69,12 @@ private: int parent_joint = -1; }; - StringName tracker_name = "/user/left"; + StringName tracker_name = "/user/hand_tracker/left"; BoneUpdate bone_update = BONE_UPDATE_FULL; JointData joints[XRHandTracker::HAND_JOINT_MAX]; void _get_joint_data(); - void _tracker_changed(StringName p_tracker_name, const Ref<XRHandTracker> &p_tracker); + void _tracker_changed(StringName p_tracker_name, XRServer::TrackerType p_tracker_type); }; VARIANT_ENUM_CAST(XRHandModifier3D::BoneUpdate) diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 12a9f50ed7..3f4b962641 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -80,10 +80,11 @@ PackedStringArray XRCamera3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { - // must be child node of XROrigin3D! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - warnings.push_back(RTR("XRCamera3D must have an XROrigin3D node as its parent.")); + // Warn if the node has a parent which isn't an XROrigin3D! + Node *parent = get_parent(); + XROrigin3D *origin = Object::cast_to<XROrigin3D>(parent); + if (parent && origin == nullptr) { + warnings.push_back(RTR("XRCamera3D may not function as expected without an XROrigin3D node as its parent.")); }; } @@ -229,6 +230,10 @@ void XRNode3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_pose_name"), &XRNode3D::get_pose_name); ADD_PROPERTY(PropertyInfo(Variant::STRING, "pose", PROPERTY_HINT_ENUM_SUGGESTION), "set_pose_name", "get_pose_name"); + ClassDB::bind_method(D_METHOD("set_show_when_tracked", "show"), &XRNode3D::set_show_when_tracked); + ClassDB::bind_method(D_METHOD("get_show_when_tracked"), &XRNode3D::get_show_when_tracked); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_when_tracked"), "set_show_when_tracked", "get_show_when_tracked"); + ClassDB::bind_method(D_METHOD("get_is_active"), &XRNode3D::get_is_active); ClassDB::bind_method(D_METHOD("get_has_tracking_data"), &XRNode3D::get_has_tracking_data); ClassDB::bind_method(D_METHOD("get_pose"), &XRNode3D::get_pose); @@ -296,6 +301,14 @@ StringName XRNode3D::get_pose_name() const { return pose_name; } +void XRNode3D::set_show_when_tracked(bool p_show) { + show_when_tracked = p_show; +} + +bool XRNode3D::get_show_when_tracked() const { + return show_when_tracked; +} + bool XRNode3D::get_is_active() const { if (tracker.is_null()) { return false; @@ -402,6 +415,11 @@ void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) { // Handle change of has_tracking_data. has_tracking_data = p_has_tracking_data; emit_signal(SNAME("tracking_changed"), has_tracking_data); + + // If configured, show or hide the node based on tracking data. + if (show_when_tracked) { + set_visible(has_tracking_data); + } } XRNode3D::XRNode3D() { @@ -428,11 +446,12 @@ PackedStringArray XRNode3D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (is_visible() && is_inside_tree()) { - // must be child node of XROrigin! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - warnings.push_back(RTR("XRController3D must have an XROrigin3D node as its parent.")); - } + // Warn if the node has a parent which isn't an XROrigin3D! + Node *parent = get_parent(); + XROrigin3D *origin = Object::cast_to<XROrigin3D>(parent); + if (parent && origin == nullptr) { + warnings.push_back(RTR("XRNode3D may not function as expected without an XROrigin3D node as its parent.")); + }; if (tracker_name == "") { warnings.push_back(RTR("No tracker name is set.")); diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index bdcccd51ea..a42f6d470f 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -79,6 +79,7 @@ private: StringName tracker_name; StringName pose_name = "default"; bool has_tracking_data = false; + bool show_when_tracked = false; protected: Ref<XRPositionalTracker> tracker; @@ -105,6 +106,9 @@ public: bool get_is_active() const; bool get_has_tracking_data() const; + void set_show_when_tracked(bool p_show); + bool get_show_when_tracked() const; + void trigger_haptic_pulse(const String &p_action_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec = 0); Ref<XRPose> get_pose(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 1c8833494d..21682cca94 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -545,7 +545,7 @@ void register_scene_types() { GDREGISTER_CLASS(Camera3D); GDREGISTER_CLASS(AudioListener3D); GDREGISTER_CLASS(XRCamera3D); - GDREGISTER_ABSTRACT_CLASS(XRNode3D); + GDREGISTER_CLASS(XRNode3D); GDREGISTER_CLASS(XRController3D); GDREGISTER_CLASS(XRAnchor3D); GDREGISTER_CLASS(XROrigin3D); |