Merge pull request #89880 from dsnopek/openxr-composition-layers-node3d-drs

Add support for OpenXR composition layers
This commit is contained in:
Rémi Verschelde 2024-04-04 17:09:07 +02:00
commit 0c71ba7801
No known key found for this signature in database
GPG key ID: C3336907360768E1
25 changed files with 1721 additions and 49 deletions

View file

@ -3684,6 +3684,14 @@
Returns the viewport's last rendered frame.
</description>
</method>
<method name="viewport_get_update_mode" qualifiers="const">
<return type="int" enum="RenderingServer.ViewportUpdateMode" />
<param index="0" name="viewport" type="RID" />
<description>
Returns the viewport's update mode. See [enum ViewportUpdateMode] constants for options.
[b]Warning:[/b] Calling this from any thread other than the rendering thread will be detrimental to performance.
</description>
</method>
<method name="viewport_remove_canvas">
<return type="void" />
<param index="0" name="viewport" type="RID" />

View file

@ -22,6 +22,10 @@ def get_doc_classes():
"OpenXRInteractionProfileMetadata",
"OpenXRIPBinding",
"OpenXRHand",
"OpenXRCompositionLayer",
"OpenXRCompositionLayerQuad",
"OpenXRCompositionLayerCylinder",
"OpenXRCompositionLayerEquirect",
]

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayer" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
The parent class of all OpenXR composition layer nodes.
</brief_description>
<description>
Composition layers allow 2D viewports to be displayed inside of the headset by the XR compositor through special projections that retain their quality. This allows for rendering clear text while keeping the layer at a native resolution.
[b]Note:[/b] If the OpenXR runtime doesn't support the given composition layer type, a fallback mesh can be generated with a [ViewportTexture], in order to emulate the composition layer.
</description>
<tutorials>
</tutorials>
<methods>
<method name="is_natively_supported" qualifiers="const">
<return type="bool" />
<description>
Returns true if the OpenXR runtime natively supports this composition layer type.
[b]Note:[/b] This will only return an accurate result after the OpenXR session has started.
</description>
</method>
</methods>
<members>
<member name="alpha_blend" type="bool" setter="set_alpha_blend" getter="get_alpha_blend" default="false">
Enables the blending the layer using its alpha channel.
Can be combined with [member Viewport.transparent_bg] to give the layer a transparent background.
</member>
<member name="layer_viewport" type="SubViewport" setter="set_layer_viewport" getter="get_layer_viewport">
The [SubViewport] to render on the composition layer.
</member>
<member name="sort_order" type="int" setter="set_sort_order" getter="get_sort_order" default="1">
The sort order for this composition layer. Higher numbers will be shown in front of lower numbers.
[b]Note:[/b] This will have no effect if a fallback mesh is being used.
</member>
</members>
</class>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayerCylinder" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as an internal slice of a cylinder.
</brief_description>
<description>
An OpenXR composition layer that allows rendering a [SubViewport] on an internal slice of a cylinder.
</description>
<tutorials>
</tutorials>
<members>
<member name="aspect_ratio" type="float" setter="set_aspect_ratio" getter="get_aspect_ratio" default="1.0">
The aspect ratio of the slice. Used to set the height relative to the width.
</member>
<member name="central_angle" type="float" setter="set_central_angle" getter="get_central_angle" default="1.5708">
The central angle of the cylinder. Used to set the width.
</member>
<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
The number of segments to use in the fallback mesh.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the cylinder.
</member>
</members>
</class>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayerEquirect" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as an internal slice of a sphere.
</brief_description>
<description>
An OpenXR composition layer that allows rendering a [SubViewport] on an internal slice of a sphere.
</description>
<tutorials>
</tutorials>
<members>
<member name="central_horizontal_angle" type="float" setter="set_central_horizontal_angle" getter="get_central_horizontal_angle" default="1.5708">
The central horizontal angle of the sphere. Used to set the width.
</member>
<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
The number of segments to use in the fallback mesh.
</member>
<member name="lower_vertical_angle" type="float" setter="set_lower_vertical_angle" getter="get_lower_vertical_angle" default="0.785398">
The lower vertical angle of the sphere. Used (together with [member upper_vertical_angle]) to set the height.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the sphere.
</member>
<member name="upper_vertical_angle" type="float" setter="set_upper_vertical_angle" getter="get_upper_vertical_angle" default="0.785398">
The upper vertical angle of the sphere. Used (together with [member lower_vertical_angle]) to set the height.
</member>
</members>
</class>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="OpenXRCompositionLayerQuad" inherits="OpenXRCompositionLayer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
An OpenXR composition layer that is rendered as a quad.
</brief_description>
<description>
An OpenXR composition layer that allows rendering a [SubViewport] on a quad.
</description>
<tutorials>
</tutorials>
<members>
<member name="quad_size" type="Vector2" setter="set_quad_size" getter="get_quad_size" default="Vector2(1, 1)">
The dimensions of the quad.
</member>
</members>
</class>

View file

@ -0,0 +1,296 @@
/**************************************************************************/
/* openxr_composition_layer_extension.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer_extension.h"
#include "servers/rendering/rendering_server_globals.h"
////////////////////////////////////////////////////////////////////////////
// OpenXRCompositionLayerExtension
OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::singleton = nullptr;
OpenXRCompositionLayerExtension *OpenXRCompositionLayerExtension::get_singleton() {
return singleton;
}
OpenXRCompositionLayerExtension::OpenXRCompositionLayerExtension() {
singleton = this;
}
OpenXRCompositionLayerExtension::~OpenXRCompositionLayerExtension() {
singleton = nullptr;
}
HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions;
request_extensions[XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME] = &cylinder_ext_available;
request_extensions[XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME] = &equirect_ext_available;
return request_extensions;
}
void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_instance) {
OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
}
void OpenXRCompositionLayerExtension::on_session_destroyed() {
OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);
}
void OpenXRCompositionLayerExtension::on_pre_render() {
for (OpenXRViewportCompositionLayerProvider *composition_layer : composition_layers) {
composition_layer->on_pre_render();
}
}
int OpenXRCompositionLayerExtension::get_composition_layer_count() {
return composition_layers.size();
}
XrCompositionLayerBaseHeader *OpenXRCompositionLayerExtension::get_composition_layer(int p_index) {
ERR_FAIL_INDEX_V(p_index, composition_layers.size(), nullptr);
return composition_layers[p_index]->get_composition_layer();
}
int OpenXRCompositionLayerExtension::get_composition_layer_order(int p_index) {
ERR_FAIL_INDEX_V(p_index, composition_layers.size(), 1);
return composition_layers[p_index]->get_sort_order();
}
void OpenXRCompositionLayerExtension::register_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.push_back(p_composition_layer);
}
void OpenXRCompositionLayerExtension::unregister_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer) {
composition_layers.erase(p_composition_layer);
}
bool OpenXRCompositionLayerExtension::is_available(XrStructureType p_which) {
switch (p_which) {
case XR_TYPE_COMPOSITION_LAYER_QUAD: {
// Doesn't require an extension.
return true;
} break;
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {
return cylinder_ext_available;
} break;
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {
return equirect_ext_available;
} break;
default: {
ERR_PRINT(vformat("Unsupported composition layer type: %s", p_which));
return false;
}
}
}
////////////////////////////////////////////////////////////////////////////
// OpenXRViewportCompositionLayerProvider
OpenXRViewportCompositionLayerProvider::OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer) {
composition_layer = p_composition_layer;
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
}
OpenXRViewportCompositionLayerProvider::~OpenXRViewportCompositionLayerProvider() {
// This will reset the viewport and free the swapchain too.
set_viewport(RID(), Size2i());
}
void OpenXRViewportCompositionLayerProvider::set_alpha_blend(bool p_alpha_blend) {
if (alpha_blend != p_alpha_blend) {
alpha_blend = p_alpha_blend;
if (alpha_blend) {
composition_layer->layerFlags |= XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
} else {
composition_layer->layerFlags &= ~XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT;
}
}
}
void OpenXRViewportCompositionLayerProvider::set_viewport(RID p_viewport, Size2i p_size) {
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
if (viewport != p_viewport) {
if (viewport.is_valid()) {
RID rt = rs->viewport_get_render_target(viewport);
RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID());
}
viewport = p_viewport;
if (viewport.is_valid()) {
viewport_size = p_size;
} else {
free_swapchain();
viewport_size = Size2i();
}
}
}
void OpenXRViewportCompositionLayerProvider::on_pre_render() {
RenderingServer *rs = RenderingServer::get_singleton();
ERR_FAIL_NULL(rs);
if (viewport.is_valid() && openxr_api && openxr_api->is_running()) {
RS::ViewportUpdateMode update_mode = rs->viewport_get_update_mode(viewport);
if (update_mode == RS::VIEWPORT_UPDATE_ONCE || update_mode == RS::VIEWPORT_UPDATE_ALWAYS) {
// Update our XR swapchain
if (update_and_acquire_swapchain(update_mode == RS::VIEWPORT_UPDATE_ONCE)) {
// Render to our XR swapchain image.
RID rt = rs->viewport_get_render_target(viewport);
RSG::texture_storage->render_target_set_override(rt, get_current_swapchain_texture(), RID(), RID());
}
}
}
}
XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_composition_layer() {
if (openxr_api == nullptr || composition_layer_extension == nullptr) {
// OpenXR not initialised or we're in the editor?
return nullptr;
}
if (!composition_layer_extension->is_available(composition_layer->type)) {
// Selected type is not supported, ignore our layer.
return nullptr;
}
if (swapchain_info.swapchain == XR_NULL_HANDLE) {
// Don't have a swapchain to display? Ignore our layer.
return nullptr;
}
if (swapchain_info.image_acquired) {
openxr_api->release_image(swapchain_info);
}
// Update the layer struct for the swapchain.
switch (composition_layer->type) {
case XR_TYPE_COMPOSITION_LAYER_QUAD: {
XrCompositionLayerQuad *quad_layer = (XrCompositionLayerQuad *)composition_layer;
quad_layer->subImage.swapchain = swapchain_info.swapchain;
quad_layer->subImage.imageArrayIndex = 0;
quad_layer->subImage.imageRect.offset.x = 0;
quad_layer->subImage.imageRect.offset.y = 0;
quad_layer->subImage.imageRect.extent.width = swapchain_size.width;
quad_layer->subImage.imageRect.extent.height = swapchain_size.height;
} break;
case XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR: {
XrCompositionLayerCylinderKHR *cylinder_layer = (XrCompositionLayerCylinderKHR *)composition_layer;
cylinder_layer->subImage.swapchain = swapchain_info.swapchain;
cylinder_layer->subImage.imageArrayIndex = 0;
cylinder_layer->subImage.imageRect.offset.x = 0;
cylinder_layer->subImage.imageRect.offset.y = 0;
cylinder_layer->subImage.imageRect.extent.width = swapchain_size.width;
cylinder_layer->subImage.imageRect.extent.height = swapchain_size.height;
} break;
case XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR: {
XrCompositionLayerEquirect2KHR *equirect_layer = (XrCompositionLayerEquirect2KHR *)composition_layer;
equirect_layer->subImage.swapchain = swapchain_info.swapchain;
equirect_layer->subImage.imageArrayIndex = 0;
equirect_layer->subImage.imageRect.offset.x = 0;
equirect_layer->subImage.imageRect.offset.y = 0;
equirect_layer->subImage.imageRect.extent.width = swapchain_size.width;
equirect_layer->subImage.imageRect.extent.height = swapchain_size.height;
} break;
default: {
return nullptr;
} break;
}
return composition_layer;
}
bool OpenXRViewportCompositionLayerProvider::update_and_acquire_swapchain(bool p_static_image) {
if (openxr_api == nullptr || composition_layer_extension == nullptr) {
// OpenXR not initialised or we're in the editor?
return false;
}
if (!composition_layer_extension->is_available(composition_layer->type)) {
// Selected type is not supported?
return false;
}
// See if our current swapchain is outdated.
if (swapchain_info.swapchain != XR_NULL_HANDLE) {
// If this swap chain, or the previous one, were static, then we can't reuse it.
if (swapchain_size == viewport_size && !p_static_image && !static_image) {
// We're all good! Just acquire it.
return openxr_api->acquire_image(swapchain_info);
}
openxr_api->free_swapchain(swapchain_info);
}
// Create our new swap chain
int64_t swapchain_format = openxr_api->get_color_swapchain_format();
const uint32_t sample_count = 3;
const uint32_t array_size = 1;
XrSwapchainCreateFlags create_flags = 0;
if (p_static_image) {
create_flags |= XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT;
}
if (!openxr_api->create_swapchain(create_flags, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format, viewport_size.width, viewport_size.height, sample_count, array_size, swapchain_info.swapchain, &swapchain_info.swapchain_graphics_data)) {
swapchain_size = Size2i();
return false;
}
// Acquire our image so we can start rendering into it
bool ret = openxr_api->acquire_image(swapchain_info);
swapchain_size = viewport_size;
static_image = p_static_image;
return ret;
}
void OpenXRViewportCompositionLayerProvider::free_swapchain() {
if (swapchain_info.swapchain != XR_NULL_HANDLE) {
openxr_api->free_swapchain(swapchain_info);
}
swapchain_size = Size2i();
static_image = false;
}
RID OpenXRViewportCompositionLayerProvider::get_current_swapchain_texture() {
if (openxr_api == nullptr) {
return RID();
}
return openxr_api->get_image(swapchain_info);
}

View file

@ -0,0 +1,112 @@
/**************************************************************************/
/* openxr_composition_layer_extension.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OPENXR_COMPOSITION_LAYER_EXTENSION_H
#define OPENXR_COMPOSITION_LAYER_EXTENSION_H
#include "openxr_composition_layer_provider.h"
#include "openxr_extension_wrapper.h"
#include "../openxr_api.h"
class OpenXRViewportCompositionLayerProvider;
// This extension provides access to composition layers for displaying 2D content through the XR compositor.
// OpenXRCompositionLayerExtension enables the extensions related to this functionality
class OpenXRCompositionLayerExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
public:
static OpenXRCompositionLayerExtension *get_singleton();
OpenXRCompositionLayerExtension();
virtual ~OpenXRCompositionLayerExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
virtual void on_session_created(const XrSession p_instance) override;
virtual void on_session_destroyed() override;
virtual void on_pre_render() override;
virtual int get_composition_layer_count() override;
virtual XrCompositionLayerBaseHeader *get_composition_layer(int p_index) override;
virtual int get_composition_layer_order(int p_index) override;
void register_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
void unregister_composition_layer_provider(OpenXRViewportCompositionLayerProvider *p_composition_layer);
bool is_available(XrStructureType p_which);
private:
static OpenXRCompositionLayerExtension *singleton;
Vector<OpenXRViewportCompositionLayerProvider *> composition_layers;
bool cylinder_ext_available = false;
bool equirect_ext_available = false;
};
class OpenXRViewportCompositionLayerProvider {
XrCompositionLayerBaseHeader *composition_layer = nullptr;
int sort_order = 1;
bool alpha_blend = false;
RID viewport;
Size2i viewport_size;
OpenXRAPI::OpenXRSwapChainInfo swapchain_info;
Size2i swapchain_size;
bool static_image = false;
OpenXRAPI *openxr_api = nullptr;
OpenXRCompositionLayerExtension *composition_layer_extension = nullptr;
bool update_and_acquire_swapchain(bool p_static_image);
void free_swapchain();
RID get_current_swapchain_texture();
public:
XrStructureType get_openxr_type() { return composition_layer->type; }
void set_sort_order(int p_sort_order) { sort_order = p_sort_order; }
int get_sort_order() const { return sort_order; }
void set_alpha_blend(bool p_alpha_blend);
bool get_alpha_blend() const { return alpha_blend; }
void set_viewport(RID p_viewport, Size2i p_size);
RID get_viewport() const { return viewport; }
void on_pre_render();
XrCompositionLayerBaseHeader *get_composition_layer();
OpenXRViewportCompositionLayerProvider(XrCompositionLayerBaseHeader *p_composition_layer);
~OpenXRViewportCompositionLayerProvider();
};
#endif // OPENXR_COMPOSITION_LAYER_EXTENSION_H

View file

@ -906,25 +906,25 @@ bool OpenXRAPI::create_swapchains() {
{
// Build a vector with swapchain formats we want to use, from best fit to worst
Vector<int64_t> usable_swapchain_formats;
int64_t swapchain_format_to_use = 0;
color_swapchain_format = 0;
graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats);
// now find out which one is supported
for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
for (int i = 0; i < usable_swapchain_formats.size() && color_swapchain_format == 0; i++) {
if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
swapchain_format_to_use = usable_swapchain_formats[i];
color_swapchain_format = usable_swapchain_formats[i];
}
}
if (swapchain_format_to_use == 0) {
swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best...
print_line("Couldn't find usable color swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead.");
if (color_swapchain_format == 0) {
color_swapchain_format = usable_swapchain_formats[0]; // just use the first one and hope for the best...
print_line("Couldn't find usable color swap chain format, using", get_swapchain_format_name(color_swapchain_format), "instead.");
} else {
print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(swapchain_format_to_use));
print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(color_swapchain_format));
}
if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, sample_count, view_count, swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain, &swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data)) {
if (!create_swapchain(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, recommended_size.width, recommended_size.height, sample_count, view_count, swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain, &swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data)) {
return false;
}
}
@ -942,25 +942,25 @@ bool OpenXRAPI::create_swapchains() {
if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
// Build a vector with swapchain formats we want to use, from best fit to worst
Vector<int64_t> usable_swapchain_formats;
int64_t swapchain_format_to_use = 0;
depth_swapchain_format = 0;
graphics_extension->get_usable_depth_formats(usable_swapchain_formats);
// now find out which one is supported
for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
for (int i = 0; i < usable_swapchain_formats.size() && depth_swapchain_format == 0; i++) {
if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
swapchain_format_to_use = usable_swapchain_formats[i];
depth_swapchain_format = usable_swapchain_formats[i];
}
}
if (swapchain_format_to_use == 0) {
if (depth_swapchain_format == 0) {
print_line("Couldn't find usable depth swap chain format, depth buffer will not be submitted.");
} else {
print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(swapchain_format_to_use));
print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(depth_swapchain_format));
// Note, if VK_FORMAT_D32_SFLOAT is used here but we're using the forward+ renderer, we should probably output a warning.
if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, sample_count, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) {
if (!create_swapchain(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, recommended_size.width, recommended_size.height, sample_count, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) {
return false;
}
@ -1014,10 +1014,6 @@ void OpenXRAPI::destroy_session() {
xrEndSession(session);
}
if (graphics_extension) {
graphics_extension->cleanup_swapchain_graphics_data(&swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data);
}
if (views != nullptr) {
memfree(views);
views = nullptr;
@ -1034,10 +1030,7 @@ void OpenXRAPI::destroy_session() {
}
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
if (swapchains[i].swapchain != XR_NULL_HANDLE) {
xrDestroySwapchain(swapchains[i].swapchain);
swapchains[i].swapchain = XR_NULL_HANDLE;
}
free_swapchain(swapchains[i]);
}
if (supported_swapchain_formats != nullptr) {
@ -1071,7 +1064,7 @@ void OpenXRAPI::destroy_session() {
}
}
bool OpenXRAPI::create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
bool OpenXRAPI::create_swapchain(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
ERR_FAIL_NULL_V(graphics_extension, false);
@ -1088,7 +1081,7 @@ bool OpenXRAPI::create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_
XrSwapchainCreateInfo swapchain_create_info = {
XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
next_pointer, // next
0, // createFlags
p_create_flags, // createFlags
p_usage_flags, // usageFlags
p_swapchain_format, // format
p_sample_count, // sampleCount
@ -1805,6 +1798,21 @@ bool OpenXRAPI::process() {
return true;
}
void OpenXRAPI::free_swapchain(OpenXRSwapChainInfo &p_swapchain) {
if (p_swapchain.image_acquired) {
release_image(p_swapchain);
}
if (graphics_extension && p_swapchain.swapchain_graphics_data != nullptr) {
graphics_extension->cleanup_swapchain_graphics_data(&p_swapchain.swapchain_graphics_data);
}
if (p_swapchain.swapchain != XR_NULL_HANDLE) {
xrDestroySwapchain(p_swapchain.swapchain);
p_swapchain.swapchain = XR_NULL_HANDLE;
}
}
bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) {
ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // This was not released when it should be, error out and reuse...
@ -1856,10 +1864,26 @@ bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) {
p_swapchain.skip_acquire_swapchain = false;
}
p_swapchain.image_acquired = true;
return true;
}
RID OpenXRAPI::get_image(OpenXRSwapChainInfo &p_swapchain) {
if (p_swapchain.image_acquired) {
return graphics_extension->get_texture(p_swapchain.swapchain_graphics_data, p_swapchain.image_index);
} else {
return RID();
}
}
bool OpenXRAPI::release_image(OpenXRSwapChainInfo &p_swapchain) {
if (!p_swapchain.image_acquired) {
// Already released or never acquired.
return true;
}
p_swapchain.image_acquired = false; // Regardless if we succeed or not, consider this released.
XrSwapchainImageReleaseInfo swapchain_image_release_info = {
XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type
nullptr // next
@ -1987,7 +2011,6 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
if (!acquire_image(swapchains[i])) {
return false;
}
swapchains[i].image_acquired = true;
}
}
@ -2003,17 +2026,13 @@ XrSwapchain OpenXRAPI::get_color_swapchain() {
}
RID OpenXRAPI::get_color_texture() {
if (swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_COLOR].image_index);
} else {
return RID();
}
return get_image(swapchains[OPENXR_SWAPCHAIN_COLOR]);
}
RID OpenXRAPI::get_depth_texture() {
// Note, image will not be acquired if we didn't have a suitable swap chain format.
if (submit_depth_buffer && swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) {
return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_DEPTH].image_index);
if (submit_depth_buffer) {
return get_image(swapchains[OPENXR_SWAPCHAIN_DEPTH]);
} else {
return RID();
}
@ -2069,8 +2088,6 @@ void OpenXRAPI::end_frame() {
// release our swapchain image if we acquired it
for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
if (swapchains[i].image_acquired) {
swapchains[i].image_acquired = false; // whether we succeed or not, consider this released.
release_image(swapchains[i]);
}
}

View file

@ -57,6 +57,15 @@
class OpenXRInterface;
class OpenXRAPI {
public:
struct OpenXRSwapChainInfo {
XrSwapchain swapchain = XR_NULL_HANDLE;
void *swapchain_graphics_data = nullptr;
uint32_t image_index = 0;
bool image_acquired = false;
bool skip_acquire_swapchain = false;
};
private:
// our singleton
static OpenXRAPI *singleton;
@ -137,14 +146,8 @@ private:
OPENXR_SWAPCHAIN_MAX
};
struct OpenXRSwapChainInfo {
XrSwapchain swapchain = XR_NULL_HANDLE;
void *swapchain_graphics_data = nullptr;
uint32_t image_index = 0;
bool image_acquired = false;
bool skip_acquire_swapchain = false;
};
int64_t color_swapchain_format = 0;
int64_t depth_swapchain_format = 0;
OpenXRSwapChainInfo swapchains[OPENXR_SWAPCHAIN_MAX];
XrSpace play_space = XR_NULL_HANDLE;
@ -241,11 +244,6 @@ private:
bool create_swapchains();
void destroy_session();
// swapchains
bool create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
bool acquire_image(OpenXRSwapChainInfo &p_swapchain);
bool release_image(OpenXRSwapChainInfo &p_swapchain);
// action map
struct Tracker { // Trackers represent tracked physical objects such as controllers, pucks, etc.
String name; // Name for this tracker (i.e. "/user/hand/left")
@ -405,6 +403,14 @@ public:
// Play space.
Size2 get_play_space_bounds() const;
// swapchains
int64_t get_color_swapchain_format() const { return color_swapchain_format; }
bool create_swapchain(XrSwapchainCreateFlags p_create_flags, XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
void free_swapchain(OpenXRSwapChainInfo &p_swapchain);
bool acquire_image(OpenXRSwapChainInfo &p_swapchain);
RID get_image(OpenXRSwapChainInfo &p_swapchain);
bool release_image(OpenXRSwapChainInfo &p_swapchain);
// action map
String get_default_action_map_resource_name();

View file

@ -39,9 +39,14 @@
#include "extensions/openxr_extension_wrapper_extension.h"
#include "scene/openxr_composition_layer.h"
#include "scene/openxr_composition_layer_cylinder.h"
#include "scene/openxr_composition_layer_equirect.h"
#include "scene/openxr_composition_layer_quad.h"
#include "scene/openxr_hand.h"
#include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_composition_layer_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_hand_tracking_extension.h"
@ -110,6 +115,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRLocalFloorExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRPicoControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHuaweiControllerExtension));
@ -164,6 +170,11 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(OpenXRIPBinding);
GDREGISTER_CLASS(OpenXRInteractionProfile);
GDREGISTER_ABSTRACT_CLASS(OpenXRCompositionLayer);
GDREGISTER_CLASS(OpenXRCompositionLayerEquirect);
GDREGISTER_CLASS(OpenXRCompositionLayerCylinder);
GDREGISTER_CLASS(OpenXRCompositionLayerQuad);
GDREGISTER_CLASS(OpenXRHand);
XRServer *xr_server = XRServer::get_singleton();

View file

@ -0,0 +1,303 @@
/**************************************************************************/
/* openxr_composition_layer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer.h"
#include "../extensions/openxr_composition_layer_extension.h"
#include "../openxr_api.h"
#include "../openxr_interface.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/xr_nodes.h"
#include "scene/main/viewport.h"
HashSet<SubViewport *> OpenXRCompositionLayer::viewports_in_use;
OpenXRCompositionLayer::OpenXRCompositionLayer() {
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->connect("session_begun", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_begun));
openxr_interface->connect("session_stopping", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_stopping));
}
set_process_internal(true);
set_notify_local_transform(true);
if (Engine::get_singleton()->is_editor_hint()) {
// In the editor, create the fallback right away.
_create_fallback_node();
}
}
OpenXRCompositionLayer::~OpenXRCompositionLayer() {
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->disconnect("session_begun", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_begun));
openxr_interface->disconnect("session_stopping", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_stopping));
}
if (layer_viewport) {
viewports_in_use.erase(layer_viewport);
}
if (openxr_layer_provider != nullptr) {
memdelete(openxr_layer_provider);
openxr_layer_provider = nullptr;
}
}
void OpenXRCompositionLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer_viewport", "viewport"), &OpenXRCompositionLayer::set_layer_viewport);
ClassDB::bind_method(D_METHOD("get_layer_viewport"), &OpenXRCompositionLayer::get_layer_viewport);
ClassDB::bind_method(D_METHOD("set_sort_order", "order"), &OpenXRCompositionLayer::set_sort_order);
ClassDB::bind_method(D_METHOD("get_sort_order"), &OpenXRCompositionLayer::get_sort_order);
ClassDB::bind_method(D_METHOD("set_alpha_blend", "enabled"), &OpenXRCompositionLayer::set_alpha_blend);
ClassDB::bind_method(D_METHOD("get_alpha_blend"), &OpenXRCompositionLayer::get_alpha_blend);
ClassDB::bind_method(D_METHOD("is_natively_supported"), &OpenXRCompositionLayer::is_natively_supported);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "layer_viewport", PROPERTY_HINT_NODE_TYPE, "SubViewport"), "set_layer_viewport", "get_layer_viewport");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sort_order", PROPERTY_HINT_NONE, ""), "set_sort_order", "get_sort_order");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "alpha_blend", PROPERTY_HINT_NONE, ""), "set_alpha_blend", "get_alpha_blend");
}
void OpenXRCompositionLayer::_create_fallback_node() {
ERR_FAIL_COND(fallback);
fallback = memnew(MeshInstance3D);
fallback->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
add_child(fallback, false, INTERNAL_MODE_FRONT);
should_update_fallback_mesh = true;
}
void OpenXRCompositionLayer::_on_openxr_session_begun() {
if (!is_natively_supported()) {
if (!fallback) {
_create_fallback_node();
}
} else if (layer_viewport && is_visible() && is_inside_tree()) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
}
}
void OpenXRCompositionLayer::_on_openxr_session_stopping() {
if (fallback && !Engine::get_singleton()->is_editor_hint()) {
fallback->queue_free();
remove_child(fallback);
fallback = nullptr;
} else {
openxr_layer_provider->set_viewport(RID(), Size2i());
}
}
void OpenXRCompositionLayer::update_fallback_mesh() {
should_update_fallback_mesh = true;
}
void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) {
if (layer_viewport == p_viewport) {
return;
}
ERR_FAIL_COND_EDMSG(viewports_in_use.has(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first."));
if (layer_viewport) {
viewports_in_use.erase(layer_viewport);
}
layer_viewport = p_viewport;
if (layer_viewport) {
viewports_in_use.insert(layer_viewport);
SubViewport::UpdateMode update_mode = layer_viewport->get_update_mode();
if (update_mode == SubViewport::UPDATE_WHEN_VISIBLE || update_mode == SubViewport::UPDATE_WHEN_PARENT_VISIBLE) {
WARN_PRINT_ONCE("OpenXR composition layers cannot use SubViewports with UPDATE_WHEN_VISIBLE or UPDATE_WHEN_PARENT_VISIBLE. Switching to UPDATE_ALWAYS.");
layer_viewport->set_update_mode(SubViewport::UPDATE_ALWAYS);
}
}
if (fallback) {
_reset_fallback_material();
} else if (openxr_api && openxr_api->is_running() && is_visible() && is_inside_tree()) {
if (layer_viewport) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
} else {
openxr_layer_provider->set_viewport(RID(), Size2i());
}
}
}
SubViewport *OpenXRCompositionLayer::get_layer_viewport() const {
return layer_viewport;
}
void OpenXRCompositionLayer::set_sort_order(int p_order) {
if (openxr_layer_provider) {
openxr_layer_provider->set_sort_order(p_order);
}
}
int OpenXRCompositionLayer::get_sort_order() const {
if (openxr_layer_provider) {
return openxr_layer_provider->get_sort_order();
}
return 1;
}
void OpenXRCompositionLayer::set_alpha_blend(bool p_alpha_blend) {
if (openxr_layer_provider) {
openxr_layer_provider->set_alpha_blend(p_alpha_blend);
if (fallback) {
_reset_fallback_material();
}
}
}
bool OpenXRCompositionLayer::get_alpha_blend() const {
if (openxr_layer_provider) {
return openxr_layer_provider->get_alpha_blend();
}
return false;
}
bool OpenXRCompositionLayer::is_natively_supported() const {
if (composition_layer_extension) {
return composition_layer_extension->is_available(openxr_layer_provider->get_openxr_type());
}
return false;
}
void OpenXRCompositionLayer::_reset_fallback_material() {
ERR_FAIL_NULL(fallback);
if (fallback->get_mesh().is_null()) {
return;
}
if (layer_viewport) {
Ref<StandardMaterial3D> material = fallback->get_surface_override_material(0);
if (material.is_null()) {
material.instantiate();
material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
material->set_local_to_scene(true);
fallback->set_surface_override_material(0, material);
}
material->set_transparency(get_alpha_blend() ? StandardMaterial3D::TRANSPARENCY_ALPHA : StandardMaterial3D::TRANSPARENCY_DISABLED);
Ref<ViewportTexture> texture = material->get_texture(StandardMaterial3D::TEXTURE_ALBEDO);
if (texture.is_null()) {
texture.instantiate();
// ViewportTexture can't be configured without a local scene, so use this hack to set it.
HashMap<Ref<Resource>, Ref<Resource>> remap_cache;
texture->configure_for_local_scene(this, remap_cache);
}
Node *loc_scene = texture->get_local_scene();
NodePath viewport_path = loc_scene->get_path_to(layer_viewport);
texture->set_viewport_path_in_scene(viewport_path);
material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, texture);
} else {
fallback->set_surface_override_material(0, Ref<Material>());
}
}
void OpenXRCompositionLayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_INTERNAL_PROCESS: {
if (fallback) {
if (should_update_fallback_mesh) {
fallback->set_mesh(_create_fallback_mesh());
_reset_fallback_material();
should_update_fallback_mesh = false;
}
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!fallback && openxr_api && openxr_api->is_running() && is_inside_tree()) {
if (layer_viewport && is_visible()) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
} else {
openxr_layer_provider->set_viewport(RID(), Size2i());
}
}
update_configuration_warnings();
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
update_configuration_warnings();
} break;
case NOTIFICATION_ENTER_TREE: {
if (composition_layer_extension) {
composition_layer_extension->register_composition_layer_provider(openxr_layer_provider);
}
if (!fallback && layer_viewport && openxr_api && openxr_api->is_running() && is_visible()) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (composition_layer_extension) {
composition_layer_extension->unregister_composition_layer_provider(openxr_layer_provider);
}
// When a node is removed in the editor, we need to clear the layer viewport, because otherwise
// there will be issues with the tracking in viewports_in_use, since nodes deleted in the editor
// aren't really deleted in order to support undo.
if (Engine::get_singleton()->is_editor_hint() && layer_viewport) {
set_layer_viewport(nullptr);
} else if (!fallback) {
// This will clean up existing resources.
openxr_layer_provider->set_viewport(RID(), Size2i());
}
} break;
}
}
PackedStringArray OpenXRCompositionLayer::get_configuration_warnings() const {
PackedStringArray warnings = Node3D::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
warnings.push_back(RTR("OpenXR composition layers must have an XROrigin3D node as their parent."));
}
}
if (!get_transform().basis.is_orthonormal()) {
warnings.push_back(RTR("OpenXR composition layers must have orthonormalized transforms (ie. no scale or shearing)."));
}
return warnings;
}

View file

@ -0,0 +1,91 @@
/**************************************************************************/
/* openxr_composition_layer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OPENXR_COMPOSITION_LAYER_H
#define OPENXR_COMPOSITION_LAYER_H
#include <openxr/openxr.h>
#include "scene/3d/node_3d.h"
class MeshInstance3D;
class Mesh;
class OpenXRAPI;
class OpenXRCompositionLayerExtension;
class OpenXRViewportCompositionLayerProvider;
class SubViewport;
class OpenXRCompositionLayer : public Node3D {
GDCLASS(OpenXRCompositionLayer, Node3D);
SubViewport *layer_viewport = nullptr;
MeshInstance3D *fallback = nullptr;
bool should_update_fallback_mesh = false;
void _create_fallback_node();
void _reset_fallback_material();
protected:
OpenXRAPI *openxr_api = nullptr;
OpenXRCompositionLayerExtension *composition_layer_extension = nullptr;
OpenXRViewportCompositionLayerProvider *openxr_layer_provider = nullptr;
static void _bind_methods();
void _notification(int p_what);
virtual void _on_openxr_session_begun();
virtual void _on_openxr_session_stopping();
virtual Ref<Mesh> _create_fallback_mesh() = 0;
void update_fallback_mesh();
static HashSet<SubViewport *> viewports_in_use;
public:
void set_layer_viewport(SubViewport *p_viewport);
SubViewport *get_layer_viewport() const;
void set_sort_order(int p_order);
int get_sort_order() const;
void set_alpha_blend(bool p_alpha_blend);
bool get_alpha_blend() const;
bool is_natively_supported() const;
virtual PackedStringArray get_configuration_warnings() const override;
OpenXRCompositionLayer();
~OpenXRCompositionLayer();
};
#endif // OPENXR_COMPOSITION_LAYER_H

View file

@ -0,0 +1,190 @@
/**************************************************************************/
/* openxr_composition_layer_cylinder.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer_cylinder.h"
#include "../extensions/openxr_composition_layer_extension.h"
#include "../openxr_api.h"
#include "../openxr_interface.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/main/viewport.h"
#include "scene/resources/mesh.h"
OpenXRCompositionLayerCylinder::OpenXRCompositionLayerCylinder() {
composition_layer = {
XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR, // type
nullptr, // next
0, // layerFlags
XR_NULL_HANDLE, // space
XR_EYE_VISIBILITY_BOTH, // eyeVisibility
{}, // subImage
{ { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose
radius, // radius
central_angle, // centralAngle
aspect_ratio, // aspectRatio
};
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer));
}
OpenXRCompositionLayerCylinder::~OpenXRCompositionLayerCylinder() {
}
void OpenXRCompositionLayerCylinder::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &OpenXRCompositionLayerCylinder::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &OpenXRCompositionLayerCylinder::get_radius);
ClassDB::bind_method(D_METHOD("set_aspect_ratio", "aspect_ratio"), &OpenXRCompositionLayerCylinder::set_aspect_ratio);
ClassDB::bind_method(D_METHOD("get_aspect_ratio"), &OpenXRCompositionLayerCylinder::get_aspect_ratio);
ClassDB::bind_method(D_METHOD("set_central_angle", "angle"), &OpenXRCompositionLayerCylinder::set_central_angle);
ClassDB::bind_method(D_METHOD("get_central_angle"), &OpenXRCompositionLayerCylinder::get_central_angle);
ClassDB::bind_method(D_METHOD("set_fallback_segments", "segments"), &OpenXRCompositionLayerCylinder::set_fallback_segments);
ClassDB::bind_method(D_METHOD("get_fallback_segments"), &OpenXRCompositionLayerCylinder::get_fallback_segments);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_NONE, ""), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "aspect_ratio", PROPERTY_HINT_RANGE, "0,100"), "set_aspect_ratio", "get_aspect_ratio");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "central_angle", PROPERTY_HINT_RANGE, "0,360,0.1,or_less,or_greater,radians_as_degrees"), "set_central_angle", "get_central_angle");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fallback_segments", PROPERTY_HINT_NONE, ""), "set_fallback_segments", "get_fallback_segments");
}
void OpenXRCompositionLayerCylinder::_on_openxr_session_begun() {
OpenXRCompositionLayer::_on_openxr_session_begun();
if (openxr_api) {
composition_layer.space = openxr_api->get_play_space();
}
}
Ref<Mesh> OpenXRCompositionLayerCylinder::_create_fallback_mesh() {
Ref<ArrayMesh> mesh;
mesh.instantiate();
float arc_length = radius * central_angle;
float half_height = ((1.0 / aspect_ratio) * arc_length) / 2.0;
Array arrays;
arrays.resize(ArrayMesh::ARRAY_MAX);
Vector<Vector3> vertices;
Vector<Vector3> normals;
Vector<Vector2> uvs;
Vector<int> indices;
float delta_angle = central_angle / fallback_segments;
float start_angle = (-Math_PI / 2.0) - (central_angle / 2.0);
for (uint32_t i = 0; i < fallback_segments + 1; i++) {
float current_angle = start_angle + (delta_angle * i);
float x = radius * Math::cos(current_angle);
float z = radius * Math::sin(current_angle);
Vector3 normal(Math::cos(current_angle), 0, Math::sin(current_angle));
vertices.push_back(Vector3(x, -half_height, z));
normals.push_back(normal);
uvs.push_back(Vector2((float)i / fallback_segments, 1));
vertices.push_back(Vector3(x, half_height, z));
normals.push_back(normal);
uvs.push_back(Vector2((float)i / fallback_segments, 0));
}
for (uint32_t i = 0; i < fallback_segments; i++) {
uint32_t index = i * 2;
indices.push_back(index);
indices.push_back(index + 1);
indices.push_back(index + 3);
indices.push_back(index);
indices.push_back(index + 3);
indices.push_back(index + 2);
}
arrays[ArrayMesh::ARRAY_VERTEX] = vertices;
arrays[ArrayMesh::ARRAY_NORMAL] = normals;
arrays[ArrayMesh::ARRAY_TEX_UV] = uvs;
arrays[ArrayMesh::ARRAY_INDEX] = indices;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
return mesh;
}
void OpenXRCompositionLayerCylinder::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
Transform3D transform = get_transform();
Quaternion quat(transform.basis.orthonormalized());
composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w };
composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z };
} break;
}
}
void OpenXRCompositionLayerCylinder::set_radius(float p_radius) {
ERR_FAIL_COND(p_radius <= 0);
radius = p_radius;
composition_layer.radius = radius;
update_fallback_mesh();
}
float OpenXRCompositionLayerCylinder::get_radius() const {
return radius;
}
void OpenXRCompositionLayerCylinder::set_aspect_ratio(float p_aspect_ratio) {
ERR_FAIL_COND(p_aspect_ratio <= 0);
aspect_ratio = p_aspect_ratio;
composition_layer.aspectRatio = aspect_ratio;
update_fallback_mesh();
}
float OpenXRCompositionLayerCylinder::get_aspect_ratio() const {
return aspect_ratio;
}
void OpenXRCompositionLayerCylinder::set_central_angle(float p_central_angle) {
ERR_FAIL_COND(p_central_angle <= 0);
central_angle = p_central_angle;
composition_layer.centralAngle = central_angle;
update_fallback_mesh();
}
float OpenXRCompositionLayerCylinder::get_central_angle() const {
return central_angle;
}
void OpenXRCompositionLayerCylinder::set_fallback_segments(uint32_t p_fallback_segments) {
ERR_FAIL_COND(p_fallback_segments == 0);
fallback_segments = p_fallback_segments;
update_fallback_mesh();
}
uint32_t OpenXRCompositionLayerCylinder::get_fallback_segments() const {
return fallback_segments;
}

View file

@ -0,0 +1,73 @@
/**************************************************************************/
/* openxr_composition_layer_cylinder.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OPENXR_COMPOSITION_LAYER_CYLINDER_H
#define OPENXR_COMPOSITION_LAYER_CYLINDER_H
#include <openxr/openxr.h>
#include "openxr_composition_layer.h"
class OpenXRCompositionLayerCylinder : public OpenXRCompositionLayer {
GDCLASS(OpenXRCompositionLayerCylinder, OpenXRCompositionLayer);
XrCompositionLayerCylinderKHR composition_layer;
float radius = 1.0;
float aspect_ratio = 1.0;
float central_angle = Math_PI / 2.0;
uint32_t fallback_segments = 10;
protected:
static void _bind_methods();
void _notification(int p_what);
virtual void _on_openxr_session_begun() override;
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
void set_radius(float p_radius);
float get_radius() const;
void set_aspect_ratio(float p_aspect_ratio);
float get_aspect_ratio() const;
void set_central_angle(float p_angle);
float get_central_angle() const;
void set_fallback_segments(uint32_t p_fallback_segments);
uint32_t get_fallback_segments() const;
OpenXRCompositionLayerCylinder();
~OpenXRCompositionLayerCylinder();
};
#endif // OPENXR_COMPOSITION_LAYER_CYLINDER_H

View file

@ -0,0 +1,209 @@
/**************************************************************************/
/* openxr_composition_layer_equirect.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer_equirect.h"
#include "../extensions/openxr_composition_layer_extension.h"
#include "../openxr_api.h"
#include "../openxr_interface.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/main/viewport.h"
#include "scene/resources/mesh.h"
OpenXRCompositionLayerEquirect::OpenXRCompositionLayerEquirect() {
composition_layer = {
XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR, // type
nullptr, // next
0, // layerFlags
XR_NULL_HANDLE, // space
XR_EYE_VISIBILITY_BOTH, // eyeVisibility
{}, // subImage
{ { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose
radius, // radius
central_horizontal_angle, // centralHorizontalAngle
upper_vertical_angle, // upperVerticalAngle
-lower_vertical_angle, // lowerVerticalAngle
};
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer));
}
OpenXRCompositionLayerEquirect::~OpenXRCompositionLayerEquirect() {
}
void OpenXRCompositionLayerEquirect::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &OpenXRCompositionLayerEquirect::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &OpenXRCompositionLayerEquirect::get_radius);
ClassDB::bind_method(D_METHOD("set_central_horizontal_angle", "angle"), &OpenXRCompositionLayerEquirect::set_central_horizontal_angle);
ClassDB::bind_method(D_METHOD("get_central_horizontal_angle"), &OpenXRCompositionLayerEquirect::get_central_horizontal_angle);
ClassDB::bind_method(D_METHOD("set_upper_vertical_angle", "angle"), &OpenXRCompositionLayerEquirect::set_upper_vertical_angle);
ClassDB::bind_method(D_METHOD("get_upper_vertical_angle"), &OpenXRCompositionLayerEquirect::get_upper_vertical_angle);
ClassDB::bind_method(D_METHOD("set_lower_vertical_angle", "angle"), &OpenXRCompositionLayerEquirect::set_lower_vertical_angle);
ClassDB::bind_method(D_METHOD("get_lower_vertical_angle"), &OpenXRCompositionLayerEquirect::get_lower_vertical_angle);
ClassDB::bind_method(D_METHOD("set_fallback_segments", "segments"), &OpenXRCompositionLayerEquirect::set_fallback_segments);
ClassDB::bind_method(D_METHOD("get_fallback_segments"), &OpenXRCompositionLayerEquirect::get_fallback_segments);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_NONE, ""), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "central_horizontal_angle", PROPERTY_HINT_RANGE, "0,360,0.1,or_less,or_greater,radians_as_degrees"), "set_central_horizontal_angle", "get_central_horizontal_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_vertical_angle", PROPERTY_HINT_RANGE, "0,90,0.1,or_less,or_greater,radians_as_degrees"), "set_upper_vertical_angle", "get_upper_vertical_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_vertical_angle", PROPERTY_HINT_RANGE, "0,90,0.1,or_less,or_greater,radians_as_degrees"), "set_lower_vertical_angle", "get_lower_vertical_angle");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fallback_segments", PROPERTY_HINT_NONE, ""), "set_fallback_segments", "get_fallback_segments");
}
void OpenXRCompositionLayerEquirect::_on_openxr_session_begun() {
OpenXRCompositionLayer::_on_openxr_session_begun();
if (openxr_api) {
composition_layer.space = openxr_api->get_play_space();
}
}
Ref<Mesh> OpenXRCompositionLayerEquirect::_create_fallback_mesh() {
Ref<ArrayMesh> mesh;
mesh.instantiate();
Array arrays;
arrays.resize(ArrayMesh::ARRAY_MAX);
Vector<Vector3> vertices;
Vector<Vector3> normals;
Vector<Vector2> uvs;
Vector<int> indices;
float step_horizontal = central_horizontal_angle / fallback_segments;
float step_vertical = (upper_vertical_angle + lower_vertical_angle) / fallback_segments;
float start_horizontal_angle = Math_PI - (central_horizontal_angle / 2.0);
for (uint32_t i = 0; i < fallback_segments + 1; i++) {
for (uint32_t j = 0; j < fallback_segments + 1; j++) {
float horizontal_angle = start_horizontal_angle + (step_horizontal * i);
float vertical_angle = -lower_vertical_angle + (step_vertical * j);
Vector3 vertex(
radius * Math::cos(vertical_angle) * Math::sin(horizontal_angle),
radius * Math::sin(vertical_angle),
radius * Math::cos(vertical_angle) * Math::cos(horizontal_angle));
vertices.push_back(vertex);
normals.push_back(vertex.normalized());
uvs.push_back(Vector2(1.0 - ((float)i / fallback_segments), 1.0 - (float(j) / fallback_segments)));
}
}
for (uint32_t i = 0; i < fallback_segments; i++) {
for (uint32_t j = 0; j < fallback_segments; j++) {
uint32_t index = i * (fallback_segments + 1) + j;
indices.push_back(index);
indices.push_back(index + fallback_segments + 1);
indices.push_back(index + fallback_segments + 2);
indices.push_back(index);
indices.push_back(index + fallback_segments + 2);
indices.push_back(index + 1);
}
}
arrays[ArrayMesh::ARRAY_VERTEX] = vertices;
arrays[ArrayMesh::ARRAY_NORMAL] = normals;
arrays[ArrayMesh::ARRAY_TEX_UV] = uvs;
arrays[ArrayMesh::ARRAY_INDEX] = indices;
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
return mesh;
}
void OpenXRCompositionLayerEquirect::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
Transform3D transform = get_transform();
Quaternion quat(transform.basis.orthonormalized());
composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w };
composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z };
} break;
}
}
void OpenXRCompositionLayerEquirect::set_radius(float p_radius) {
ERR_FAIL_COND(p_radius <= 0);
radius = p_radius;
composition_layer.radius = radius;
update_fallback_mesh();
}
float OpenXRCompositionLayerEquirect::get_radius() const {
return radius;
}
void OpenXRCompositionLayerEquirect::set_central_horizontal_angle(float p_angle) {
ERR_FAIL_COND(p_angle <= 0);
central_horizontal_angle = p_angle;
composition_layer.centralHorizontalAngle = central_horizontal_angle;
update_fallback_mesh();
}
float OpenXRCompositionLayerEquirect::get_central_horizontal_angle() const {
return central_horizontal_angle;
}
void OpenXRCompositionLayerEquirect::set_upper_vertical_angle(float p_angle) {
ERR_FAIL_COND(p_angle <= 0 || p_angle > (Math_PI / 2.0));
upper_vertical_angle = p_angle;
composition_layer.upperVerticalAngle = p_angle;
update_fallback_mesh();
}
float OpenXRCompositionLayerEquirect::get_upper_vertical_angle() const {
return upper_vertical_angle;
}
void OpenXRCompositionLayerEquirect::set_lower_vertical_angle(float p_angle) {
ERR_FAIL_COND(p_angle <= 0 || p_angle > (Math_PI / 2.0));
lower_vertical_angle = p_angle;
composition_layer.lowerVerticalAngle = -p_angle;
update_fallback_mesh();
}
float OpenXRCompositionLayerEquirect::get_lower_vertical_angle() const {
return lower_vertical_angle;
}
void OpenXRCompositionLayerEquirect::set_fallback_segments(uint32_t p_fallback_segments) {
ERR_FAIL_COND(p_fallback_segments == 0);
fallback_segments = p_fallback_segments;
update_fallback_mesh();
}
uint32_t OpenXRCompositionLayerEquirect::get_fallback_segments() const {
return fallback_segments;
}

View file

@ -0,0 +1,77 @@
/**************************************************************************/
/* openxr_composition_layer_equirect.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OPENXR_COMPOSITION_LAYER_EQUIRECT_H
#define OPENXR_COMPOSITION_LAYER_EQUIRECT_H
#include <openxr/openxr.h>
#include "openxr_composition_layer.h"
class OpenXRCompositionLayerEquirect : public OpenXRCompositionLayer {
GDCLASS(OpenXRCompositionLayerEquirect, OpenXRCompositionLayer);
XrCompositionLayerEquirect2KHR composition_layer;
float radius = 1.0;
float central_horizontal_angle = Math_PI / 2.0;
float upper_vertical_angle = Math_PI / 4.0;
float lower_vertical_angle = Math_PI / 4.0;
uint32_t fallback_segments = 10;
protected:
static void _bind_methods();
void _notification(int p_what);
virtual void _on_openxr_session_begun() override;
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
void set_radius(float p_radius);
float get_radius() const;
void set_central_horizontal_angle(float p_angle);
float get_central_horizontal_angle() const;
void set_upper_vertical_angle(float p_angle);
float get_upper_vertical_angle() const;
void set_lower_vertical_angle(float p_angle);
float get_lower_vertical_angle() const;
void set_fallback_segments(uint32_t p_fallback_segments);
uint32_t get_fallback_segments() const;
OpenXRCompositionLayerEquirect();
~OpenXRCompositionLayerEquirect();
};
#endif // OPENXR_COMPOSITION_LAYER_EQUIRECT_H

View file

@ -0,0 +1,98 @@
/**************************************************************************/
/* openxr_composition_layer_quad.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer_quad.h"
#include "../extensions/openxr_composition_layer_extension.h"
#include "../openxr_api.h"
#include "../openxr_interface.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/main/viewport.h"
#include "scene/resources/3d/primitive_meshes.h"
OpenXRCompositionLayerQuad::OpenXRCompositionLayerQuad() {
composition_layer = {
XR_TYPE_COMPOSITION_LAYER_QUAD, // type
nullptr, // next
0, // layerFlags
XR_NULL_HANDLE, // space
XR_EYE_VISIBILITY_BOTH, // eyeVisibility
{}, // subImage
{ { 0, 0, 0, 0 }, { 0, 0, 0 } }, // pose
{ (float)quad_size.x, (float)quad_size.y }, // size
};
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider((XrCompositionLayerBaseHeader *)&composition_layer));
}
OpenXRCompositionLayerQuad::~OpenXRCompositionLayerQuad() {
}
void OpenXRCompositionLayerQuad::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quad_size", "size"), &OpenXRCompositionLayerQuad::set_quad_size);
ClassDB::bind_method(D_METHOD("get_quad_size"), &OpenXRCompositionLayerQuad::get_quad_size);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "quad_size", PROPERTY_HINT_NONE, ""), "set_quad_size", "get_quad_size");
}
void OpenXRCompositionLayerQuad::_on_openxr_session_begun() {
OpenXRCompositionLayer::_on_openxr_session_begun();
if (openxr_api) {
composition_layer.space = openxr_api->get_play_space();
}
}
Ref<Mesh> OpenXRCompositionLayerQuad::_create_fallback_mesh() {
Ref<QuadMesh> mesh;
mesh.instantiate();
mesh->set_size(quad_size);
return mesh;
}
void OpenXRCompositionLayerQuad::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
Transform3D transform = get_transform();
Quaternion quat(transform.basis.orthonormalized());
composition_layer.pose.orientation = { (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w };
composition_layer.pose.position = { (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z };
} break;
}
}
void OpenXRCompositionLayerQuad::set_quad_size(const Size2 &p_size) {
quad_size = p_size;
composition_layer.size = { (float)quad_size.x, (float)quad_size.y };
update_fallback_mesh();
}
Size2 OpenXRCompositionLayerQuad::get_quad_size() const {
return quad_size;
}

View file

@ -0,0 +1,61 @@
/**************************************************************************/
/* openxr_composition_layer_quad.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OPENXR_COMPOSITION_LAYER_QUAD_H
#define OPENXR_COMPOSITION_LAYER_QUAD_H
#include <openxr/openxr.h>
#include "openxr_composition_layer.h"
class OpenXRCompositionLayerQuad : public OpenXRCompositionLayer {
GDCLASS(OpenXRCompositionLayerQuad, OpenXRCompositionLayer);
XrCompositionLayerQuad composition_layer;
Size2 quad_size = Size2(1.0, 1.0);
protected:
static void _bind_methods();
void _notification(int p_what);
virtual void _on_openxr_session_begun() override;
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
void set_quad_size(const Size2 &p_size);
Size2 get_quad_size() const;
OpenXRCompositionLayerQuad();
~OpenXRCompositionLayerQuad();
};
#endif // OPENXR_COMPOSITION_LAYER_QUAD_H

View file

@ -4487,6 +4487,10 @@ void Viewport::set_use_xr(bool p_use_xr) {
} else {
RS::get_singleton()->viewport_set_size(viewport, 0, 0);
}
// Reset render target override textures.
RID rt = RS::get_singleton()->viewport_get_render_target(viewport);
RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID());
}
}
}

View file

@ -802,8 +802,6 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
} else
#endif // _3D_DISABLED
{
RSG::texture_storage->render_target_set_override(vp->render_target, RID(), RID(), RID());
RSG::scene->set_debug_draw_mode(vp->debug_draw);
// render standard mono camera
@ -1062,6 +1060,13 @@ void RendererViewport::viewport_set_update_mode(RID p_viewport, RS::ViewportUpda
viewport->update_mode = p_mode;
}
RS::ViewportUpdateMode RendererViewport::viewport_get_update_mode(RID p_viewport) const {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL_V(viewport, RS::VIEWPORT_UPDATE_DISABLED);
return viewport->update_mode;
}
RID RendererViewport::viewport_get_render_target(RID p_viewport) const {
const Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL_V(viewport, RID());

View file

@ -236,6 +236,7 @@ public:
void viewport_set_texture_mipmap_bias(RID p_viewport, float p_mipmap_bias);
void viewport_set_update_mode(RID p_viewport, RS::ViewportUpdateMode p_mode);
RS::ViewportUpdateMode viewport_get_update_mode(RID p_viewport) const;
void viewport_set_vflip(RID p_viewport, bool p_enable);
void viewport_set_clear_mode(RID p_viewport, RS::ViewportClearMode p_clear_mode);

View file

@ -625,6 +625,7 @@ public:
FUNC2(viewport_set_texture_mipmap_bias, RID, float)
FUNC2(viewport_set_update_mode, RID, ViewportUpdateMode)
FUNC1RC(ViewportUpdateMode, viewport_get_update_mode, RID)
FUNC1RC(RID, viewport_get_render_target, RID)
FUNC1RC(RID, viewport_get_texture, RID)

View file

@ -2783,6 +2783,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("viewport_set_fsr_sharpness", "viewport", "sharpness"), &RenderingServer::viewport_set_fsr_sharpness);
ClassDB::bind_method(D_METHOD("viewport_set_texture_mipmap_bias", "viewport", "mipmap_bias"), &RenderingServer::viewport_set_texture_mipmap_bias);
ClassDB::bind_method(D_METHOD("viewport_set_update_mode", "viewport", "update_mode"), &RenderingServer::viewport_set_update_mode);
ClassDB::bind_method(D_METHOD("viewport_get_update_mode", "viewport"), &RenderingServer::viewport_get_update_mode);
ClassDB::bind_method(D_METHOD("viewport_set_clear_mode", "viewport", "clear_mode"), &RenderingServer::viewport_set_clear_mode);
ClassDB::bind_method(D_METHOD("viewport_get_render_target", "viewport"), &RenderingServer::viewport_get_render_target);
ClassDB::bind_method(D_METHOD("viewport_get_texture", "viewport"), &RenderingServer::viewport_get_texture);

View file

@ -873,6 +873,7 @@ public:
};
virtual void viewport_set_update_mode(RID p_viewport, ViewportUpdateMode p_mode) = 0;
virtual ViewportUpdateMode viewport_get_update_mode(RID p_viewport) const = 0;
enum ViewportClearMode {
VIEWPORT_CLEAR_ALWAYS,