Add support for returning the play area from XRInterface

This commit is contained in:
Bastiaan Olij 2021-06-27 21:51:30 +10:00
parent 4387f9645b
commit 96b707215d
10 changed files with 229 additions and 28 deletions

View File

@ -29,6 +29,12 @@
Returns the name of this interface (OpenXR, OpenVR, OpenHMD, ARKit, etc).
</description>
</method>
<method name="get_play_area" qualifiers="const">
<return type="PackedVector3Array" />
<description>
Returns an array of vectors that denotes the physical play area mapped to the virtual space around the [XROrigin3D] point. The points form a convex polygon that can be used to react to or visualise the play area. This returns an empty array if this feature is not supported or if the information is not yet available.
</description>
</method>
<method name="get_render_target_size">
<return type="Vector2" />
<description>
@ -63,6 +69,20 @@
Is [code]true[/code] if this interface has been initialised.
</description>
</method>
<method name="set_play_area_mode">
<return type="bool" />
<argument index="0" name="mode" type="int" enum="XRInterface.PlayAreaMode" />
<description>
Sets the active play area mode, will return [code]false[/code] if the mode can't be used with this interface.
</description>
</method>
<method name="supports_play_area_mode">
<return type="bool" />
<argument index="0" name="mode" type="int" enum="XRInterface.PlayAreaMode" />
<description>
Call this to find out if a given play area mode is supported by this interface.
</description>
</method>
<method name="trigger_haptic_pulse">
<return type="void" />
<argument index="0" name="action_name" type="String" />
@ -91,7 +111,18 @@
<member name="interface_is_primary" type="bool" setter="set_primary" getter="is_primary" default="false">
[code]true[/code] if this is the primary interface.
</member>
<member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" enum="XRInterface.PlayAreaMode" default="0">
The play area mode for this interface.
</member>
</members>
<signals>
<signal name="play_area_changed">
<argument index="0" name="mode" type="int" />
<description>
Emitted when the play area is changed. This can be a result of the player resetting the boundary or entering a new play area, the player changing the play area mode, the world scale changing or the player resetting their headset orientation.
</description>
</signal>
</signals>
<constants>
<constant name="XR_NONE" value="0" enum="Capabilities">
No XR capabilities.
@ -129,5 +160,20 @@
<constant name="XR_NOT_TRACKING" value="4" enum="TrackingStatus">
Tracking is not functional (camera not plugged in or obscured, lighthouses turned off, etc.).
</constant>
<constant name="XR_PLAY_AREA_UNKNOWN" value="0" enum="PlayAreaMode">
Play area mode not set or not available.
</constant>
<constant name="XR_PLAY_AREA_3DOF" value="1" enum="PlayAreaMode">
Play area only supports orientation tracking, no positional tracking, area will center around player.
</constant>
<constant name="XR_PLAY_AREA_SITTING" value="2" enum="PlayAreaMode">
Player is in seated position, limited positional tracking, fixed guardian around player.
</constant>
<constant name="XR_PLAY_AREA_ROOMSCALE" value="3" enum="PlayAreaMode">
Player is free to move around, full positional tracking.
</constant>
<constant name="XR_PLAY_AREA_STAGE" value="4" enum="PlayAreaMode">
Same as roomscale but origin point is fixed to the center of the physical space, XRServer.center_on_hmd disabled.
</constant>
</constants>
</class>

View File

@ -41,6 +41,16 @@
<description>
</description>
</method>
<method name="_get_play_area" qualifiers="virtual const">
<return type="PackedVector3Array" />
<description>
</description>
</method>
<method name="_get_play_area_mode" qualifiers="virtual const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_projection_for_view" qualifiers="virtual">
<return type="PackedFloat64Array" />
<argument index="0" name="view" type="int" />
@ -71,6 +81,11 @@
<description>
</description>
</method>
<method name="_get_tracking_status" qualifiers="virtual const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_transform_for_view" qualifiers="virtual">
<return type="Transform3D" />
<argument index="0" name="view" type="int" />
@ -110,6 +125,18 @@
<description>
</description>
</method>
<method name="_set_play_area_mode" qualifiers="virtual const">
<return type="bool" />
<argument index="0" name="mode" type="int" />
<description>
</description>
</method>
<method name="_supports_play_area_mode" qualifiers="virtual const">
<return type="bool" />
<argument index="0" name="mode" type="int" enum="XRInterface.PlayAreaMode" />
<description>
</description>
</method>
<method name="_trigger_haptic_pulse" qualifiers="virtual">
<return type="void" />
<argument index="0" name="action_name" type="String" />

View File

@ -37,5 +37,6 @@
<member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5">
The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance.
</member>
<member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" override="true" enum="XRInterface.PlayAreaMode" default="1" />
</members>
</class>

View File

@ -370,6 +370,19 @@ void MobileVRInterface::uninitialize() {
};
};
bool MobileVRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
// This interface has no positional tracking so fix this to 3DOF
return p_mode == XR_PLAY_AREA_3DOF;
}
XRInterface::PlayAreaMode MobileVRInterface::get_play_area_mode() const {
return XR_PLAY_AREA_3DOF;
}
bool MobileVRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return p_mode == XR_PLAY_AREA_3DOF;
}
Size2 MobileVRInterface::get_render_target_size() {
_THREAD_SAFE_METHOD_

View File

@ -143,6 +143,10 @@ public:
virtual bool initialize() override;
virtual void uninitialize() override;
virtual bool supports_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
virtual XRInterface::PlayAreaMode get_play_area_mode() const override;
virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
virtual Size2 get_render_target_size() override;
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;

View File

@ -32,6 +32,8 @@
// #include "servers/rendering/renderer_compositor.h"
void XRInterface::_bind_methods() {
ADD_SIGNAL(MethodInfo("play_area_changed", PropertyInfo(Variant::INT, "mode")));
ClassDB::bind_method(D_METHOD("get_name"), &XRInterface::get_name);
ClassDB::bind_method(D_METHOD("get_capabilities"), &XRInterface::get_capabilities);
@ -52,9 +54,16 @@ void XRInterface::_bind_methods() {
ADD_GROUP("Interface", "interface_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_primary", "is_primary");
// we don't have any properties specific to VR yet....
// methods and properties specific to VR...
ClassDB::bind_method(D_METHOD("supports_play_area_mode", "mode"), &XRInterface::supports_play_area_mode);
ClassDB::bind_method(D_METHOD("get_play_area_mode"), &XRInterface::get_play_area_mode);
ClassDB::bind_method(D_METHOD("set_play_area_mode", "mode"), &XRInterface::set_play_area_mode);
ClassDB::bind_method(D_METHOD("get_play_area"), &XRInterface::get_play_area);
// but we do have properties specific to AR....
ADD_GROUP("XR", "xr_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "xr_play_area_mode", PROPERTY_HINT_ENUM, "Unknown,3DOF,Sitting,Roomscale,Stage"), "set_play_area_mode", "get_play_area_mode");
// methods and properties specific to AR....
ClassDB::bind_method(D_METHOD("get_anchor_detection_is_enabled"), &XRInterface::get_anchor_detection_is_enabled);
ClassDB::bind_method(D_METHOD("set_anchor_detection_is_enabled", "enable"), &XRInterface::set_anchor_detection_is_enabled);
ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &XRInterface::get_camera_feed_id);
@ -75,7 +84,13 @@ void XRInterface::_bind_methods() {
BIND_ENUM_CONSTANT(XR_INSUFFICIENT_FEATURES);
BIND_ENUM_CONSTANT(XR_UNKNOWN_TRACKING);
BIND_ENUM_CONSTANT(XR_NOT_TRACKING);
}
BIND_ENUM_CONSTANT(XR_PLAY_AREA_UNKNOWN);
BIND_ENUM_CONSTANT(XR_PLAY_AREA_3DOF);
BIND_ENUM_CONSTANT(XR_PLAY_AREA_SITTING);
BIND_ENUM_CONSTANT(XR_PLAY_AREA_ROOMSCALE);
BIND_ENUM_CONSTANT(XR_PLAY_AREA_STAGE);
};
bool XRInterface::is_primary() {
XRServer *xr_server = XRServer::get_singleton();
@ -101,6 +116,29 @@ XRInterface::XRInterface() {}
XRInterface::~XRInterface() {}
// query if this interface supports this play area mode
bool XRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return p_mode == XR_PLAY_AREA_UNKNOWN;
}
// get the current play area mode
XRInterface::PlayAreaMode XRInterface::get_play_area_mode() const {
return XR_PLAY_AREA_UNKNOWN;
}
// change the play area mode, note that this should return false if the mode is not available
bool XRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return p_mode == XR_PLAY_AREA_UNKNOWN;
}
// if available, returns an array of vectors denoting the play area the player can move around in
PackedVector3Array XRInterface::get_play_area() const {
// Return an empty array by default.
// Note implementation is responsible for applying our reference frame and world scale to the raw data.
// `play_area_changed` should be emitted if play area data is available and either the reference frame or world scale changes.
return PackedVector3Array();
};
/** these will only be implemented on AR interfaces, so we want dummies for VR **/
bool XRInterface::get_anchor_detection_is_enabled() const {
return false;

View File

@ -72,7 +72,14 @@ public:
XR_NOT_TRACKING
};
private:
enum PlayAreaMode { /* defines the mode used by the XR interface for tracking */
XR_PLAY_AREA_UNKNOWN, /* Area mode not set or not available */
XR_PLAY_AREA_3DOF, /* Only support orientation tracking, no positional tracking, area will center around player */
XR_PLAY_AREA_SITTING, /* Player is in seated position, limited positional tracking, fixed guardian around player */
XR_PLAY_AREA_ROOMSCALE, /* Player is free to move around, full positional tracking */
XR_PLAY_AREA_STAGE, /* Same as roomscale but origin point is fixed to the center of the physical space, XRServer.center_on_hmd disabled */
};
protected:
_THREAD_SAFE_CLASS_
@ -98,7 +105,10 @@ public:
virtual void trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec = 0); /* trigger a haptic pulse */
/** specific to VR **/
// nothing yet
virtual bool supports_play_area_mode(XRInterface::PlayAreaMode p_mode); /* query if this interface supports this play area mode */
virtual XRInterface::PlayAreaMode get_play_area_mode() const; /* get the current play area mode */
virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode); /* change the play area mode, note that this should return false if the mode is not available */
virtual PackedVector3Array get_play_area() const; /* if available, returns an array of vectors denoting the play area the player can move around in */
/** specific to AR **/
virtual bool get_anchor_detection_is_enabled() const;
@ -126,5 +136,6 @@ public:
VARIANT_ENUM_CAST(XRInterface::Capabilities);
VARIANT_ENUM_CAST(XRInterface::TrackingStatus);
VARIANT_ENUM_CAST(XRInterface::PlayAreaMode);
#endif // !XR_INTERFACE_H

View File

@ -41,6 +41,13 @@ void XRInterfaceExtension::_bind_methods() {
GDVIRTUAL_BIND(_initialize);
GDVIRTUAL_BIND(_uninitialize);
GDVIRTUAL_BIND(_get_tracking_status);
GDVIRTUAL_BIND(_supports_play_area_mode, "mode");
GDVIRTUAL_BIND(_get_play_area_mode);
GDVIRTUAL_BIND(_set_play_area_mode, "mode");
GDVIRTUAL_BIND(_get_play_area);
GDVIRTUAL_BIND(_get_render_target_size);
GDVIRTUAL_BIND(_get_view_count);
GDVIRTUAL_BIND(_get_camera_transform);
@ -146,6 +153,44 @@ void XRInterfaceExtension::trigger_haptic_pulse(const String &p_action_name, con
GDVIRTUAL_CALL(_trigger_haptic_pulse, p_action_name, p_tracker_name, p_frequency, p_amplitude, p_duration_sec, p_delay_sec);
}
bool XRInterfaceExtension::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
bool is_supported;
if (GDVIRTUAL_CALL(_supports_play_area_mode, p_mode, is_supported)) {
return is_supported;
}
return false;
}
XRInterface::PlayAreaMode XRInterfaceExtension::get_play_area_mode() const {
uint32_t mode;
if (GDVIRTUAL_CALL(_get_play_area_mode, mode)) {
return XRInterface::PlayAreaMode(mode);
}
return XRInterface::XR_PLAY_AREA_UNKNOWN;
}
bool XRInterfaceExtension::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
bool success;
if (GDVIRTUAL_CALL(_set_play_area_mode, p_mode, success)) {
return success;
}
return false;
}
PackedVector3Array XRInterfaceExtension::get_play_area() const {
PackedVector3Array arr;
GDVIRTUAL_CALL(_get_play_area, arr);
return arr;
}
/** these will only be implemented on AR interfaces, so we want dummies for VR **/
bool XRInterfaceExtension::get_anchor_detection_is_enabled() const {
bool enabled;

View File

@ -75,7 +75,15 @@ public:
GDVIRTUAL6(_trigger_haptic_pulse, const String &, const StringName &, double, double, double, double);
/** specific to VR **/
// nothing yet
virtual bool supports_play_area_mode(XRInterface::PlayAreaMode p_mode) override; /* query if this interface supports this play area mode */
virtual XRInterface::PlayAreaMode get_play_area_mode() const override; /* get the current play area mode */
virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override; /* change the play area mode, note that this should return false if the mode is not available */
virtual PackedVector3Array get_play_area() const override; /* if available, returns an array of vectors denoting the play area the player can move around in */
GDVIRTUAL1RC(bool, _supports_play_area_mode, XRInterface::PlayAreaMode);
GDVIRTUAL0RC(uint32_t, _get_play_area_mode);
GDVIRTUAL1RC(bool, _set_play_area_mode, uint32_t);
GDVIRTUAL0RC(PackedVector3Array, _get_play_area);
/** specific to AR **/
virtual bool get_anchor_detection_is_enabled() const override;

View File

@ -116,35 +116,43 @@ Transform3D XRServer::get_reference_frame() const {
};
void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
if (primary_interface != nullptr) {
// clear our current reference frame or we'll end up double adjusting it
if (primary_interface == nullptr) {
return;
}
if (primary_interface->get_play_area_mode() == XRInterface::XR_PLAY_AREA_STAGE) {
// center_on_hmd is not available in this mode
reference_frame = Transform3D();
return;
}
// requesting our EYE_MONO transform should return our current HMD position
Transform3D new_reference_frame = primary_interface->get_camera_transform();
// clear our current reference frame or we'll end up double adjusting it
reference_frame = Transform3D();
// remove our tilt
if (p_rotation_mode == 1) {
// take the Y out of our Z
new_reference_frame.basis.set_axis(2, Vector3(new_reference_frame.basis.elements[0][2], 0.0, new_reference_frame.basis.elements[2][2]).normalized());
// requesting our EYE_MONO transform should return our current HMD position
Transform3D new_reference_frame = primary_interface->get_camera_transform();
// Y is straight up
new_reference_frame.basis.set_axis(1, Vector3(0.0, 1.0, 0.0));
// remove our tilt
if (p_rotation_mode == 1) {
// take the Y out of our Z
new_reference_frame.basis.set_axis(2, Vector3(new_reference_frame.basis.elements[0][2], 0.0, new_reference_frame.basis.elements[2][2]).normalized());
// and X is our cross reference
new_reference_frame.basis.set_axis(0, new_reference_frame.basis.get_axis(1).cross(new_reference_frame.basis.get_axis(2)).normalized());
} else if (p_rotation_mode == 2) {
// remove our rotation, we're only interesting in centering on position
new_reference_frame.basis = Basis();
};
// Y is straight up
new_reference_frame.basis.set_axis(1, Vector3(0.0, 1.0, 0.0));
// don't negate our height
if (p_keep_height) {
new_reference_frame.origin.y = 0.0;
};
reference_frame = new_reference_frame.inverse();
// and X is our cross reference
new_reference_frame.basis.set_axis(0, new_reference_frame.basis.get_axis(1).cross(new_reference_frame.basis.get_axis(2)).normalized());
} else if (p_rotation_mode == 2) {
// remove our rotation, we're only interesting in centering on position
new_reference_frame.basis = Basis();
};
// don't negate our height
if (p_keep_height) {
new_reference_frame.origin.y = 0.0;
};
reference_frame = new_reference_frame.inverse();
};
Transform3D XRServer::get_hmd_transform() {