initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj

View File

@@ -0,0 +1,726 @@
/**************************************************************************/
/* 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 "../openxr_api.h"
#include "../openxr_interface.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/xr/xr_nodes.h"
#include "scene/main/viewport.h"
#include "platform/android/api/java_class_wrapper.h"
Vector<OpenXRCompositionLayer *> OpenXRCompositionLayer::composition_layer_nodes;
static const char *HOLE_PUNCH_SHADER_CODE =
"shader_type spatial;\n"
"render_mode blend_mix, depth_draw_opaque, cull_back, shadow_to_opacity, shadows_disabled;\n"
"void fragment() {\n"
"\tALBEDO = vec3(0.0, 0.0, 0.0);\n"
"}\n";
OpenXRCompositionLayer::OpenXRCompositionLayer(XrCompositionLayerBaseHeader *p_composition_layer) {
composition_layer_base_header = p_composition_layer;
openxr_layer_provider = memnew(OpenXRViewportCompositionLayerProvider(composition_layer_base_header));
swapchain_state = openxr_layer_provider->get_swapchain_state();
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
if (openxr_api) {
openxr_session_running = openxr_api->is_running();
}
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));
}
composition_layer_nodes.erase(this);
if (openxr_layer_provider != nullptr) {
_clear_composition_layer_provider();
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_use_android_surface", "enable"), &OpenXRCompositionLayer::set_use_android_surface);
ClassDB::bind_method(D_METHOD("get_use_android_surface"), &OpenXRCompositionLayer::get_use_android_surface);
ClassDB::bind_method(D_METHOD("set_android_surface_size", "size"), &OpenXRCompositionLayer::set_android_surface_size);
ClassDB::bind_method(D_METHOD("get_android_surface_size"), &OpenXRCompositionLayer::get_android_surface_size);
ClassDB::bind_method(D_METHOD("set_enable_hole_punch", "enable"), &OpenXRCompositionLayer::set_enable_hole_punch);
ClassDB::bind_method(D_METHOD("get_enable_hole_punch"), &OpenXRCompositionLayer::get_enable_hole_punch);
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("get_android_surface"), &OpenXRCompositionLayer::get_android_surface);
ClassDB::bind_method(D_METHOD("is_natively_supported"), &OpenXRCompositionLayer::is_natively_supported);
ClassDB::bind_method(D_METHOD("set_min_filter", "mode"), &OpenXRCompositionLayer::set_min_filter);
ClassDB::bind_method(D_METHOD("get_min_filter"), &OpenXRCompositionLayer::get_min_filter);
ClassDB::bind_method(D_METHOD("set_mag_filter", "mode"), &OpenXRCompositionLayer::set_mag_filter);
ClassDB::bind_method(D_METHOD("get_mag_filter"), &OpenXRCompositionLayer::get_mag_filter);
ClassDB::bind_method(D_METHOD("set_mipmap_mode", "mode"), &OpenXRCompositionLayer::set_mipmap_mode);
ClassDB::bind_method(D_METHOD("get_mipmap_mode"), &OpenXRCompositionLayer::get_mipmap_mode);
ClassDB::bind_method(D_METHOD("set_horizontal_wrap", "mode"), &OpenXRCompositionLayer::set_horizontal_wrap);
ClassDB::bind_method(D_METHOD("get_horizontal_wrap"), &OpenXRCompositionLayer::get_horizontal_wrap);
ClassDB::bind_method(D_METHOD("set_vertical_wrap", "mode"), &OpenXRCompositionLayer::set_vertical_wrap);
ClassDB::bind_method(D_METHOD("get_vertical_wrap"), &OpenXRCompositionLayer::get_vertical_wrap);
ClassDB::bind_method(D_METHOD("set_red_swizzle", "mode"), &OpenXRCompositionLayer::set_red_swizzle);
ClassDB::bind_method(D_METHOD("get_red_swizzle"), &OpenXRCompositionLayer::get_red_swizzle);
ClassDB::bind_method(D_METHOD("set_green_swizzle", "mode"), &OpenXRCompositionLayer::set_green_swizzle);
ClassDB::bind_method(D_METHOD("get_green_swizzle"), &OpenXRCompositionLayer::get_green_swizzle);
ClassDB::bind_method(D_METHOD("set_blue_swizzle", "mode"), &OpenXRCompositionLayer::set_blue_swizzle);
ClassDB::bind_method(D_METHOD("get_blue_swizzle"), &OpenXRCompositionLayer::get_blue_swizzle);
ClassDB::bind_method(D_METHOD("set_alpha_swizzle", "mode"), &OpenXRCompositionLayer::set_alpha_swizzle);
ClassDB::bind_method(D_METHOD("get_alpha_swizzle"), &OpenXRCompositionLayer::get_alpha_swizzle);
ClassDB::bind_method(D_METHOD("set_max_anisotropy", "value"), &OpenXRCompositionLayer::set_max_anisotropy);
ClassDB::bind_method(D_METHOD("get_max_anisotropy"), &OpenXRCompositionLayer::get_max_anisotropy);
ClassDB::bind_method(D_METHOD("set_border_color", "color"), &OpenXRCompositionLayer::set_border_color);
ClassDB::bind_method(D_METHOD("get_border_color"), &OpenXRCompositionLayer::get_border_color);
ClassDB::bind_method(D_METHOD("intersects_ray", "origin", "direction"), &OpenXRCompositionLayer::intersects_ray);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "layer_viewport", PROPERTY_HINT_NODE_TYPE, "SubViewport"), "set_layer_viewport", "get_layer_viewport");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_android_surface", PROPERTY_HINT_NONE, ""), "set_use_android_surface", "get_use_android_surface");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "android_surface_size", PROPERTY_HINT_NONE, ""), "set_android_surface_size", "get_android_surface_size");
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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_hole_punch", PROPERTY_HINT_NONE, ""), "set_enable_hole_punch", "get_enable_hole_punch");
ADD_GROUP("Swapchain State", "swapchain_state_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_min_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Cubic"), "set_min_filter", "get_min_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_mag_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Cubic"), "set_mag_filter", "get_mag_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_mipmap_mode", PROPERTY_HINT_ENUM, "Disabled,Nearest,Linear"), "set_mipmap_mode", "get_mipmap_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_horizontal_wrap", PROPERTY_HINT_ENUM, "Clamp to Border,Clamp to Edge,Repeat,Mirrored Repeat,Mirror Clamp to Edge"), "set_horizontal_wrap", "get_horizontal_wrap");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_vertical_wrap", PROPERTY_HINT_ENUM, "Clamp to Border,Clamp to Edge,Repeat,Mirrored Repeat,Mirror Clamp to Edge"), "set_vertical_wrap", "get_vertical_wrap");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_red_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_red_swizzle", "get_red_swizzle");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_green_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_green_swizzle", "get_green_swizzle");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_blue_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_blue_swizzle", "get_blue_swizzle");
ADD_PROPERTY(PropertyInfo(Variant::INT, "swapchain_state_alpha_swizzle", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Zero,One"), "set_alpha_swizzle", "get_alpha_swizzle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "swapchain_state_max_anisotropy", PROPERTY_HINT_RANGE, "1.0,16.0,0.001"), "set_max_anisotropy", "get_max_anisotropy");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "swapchain_state_border_color"), "set_border_color", "get_border_color");
ADD_GROUP("", "");
BIND_ENUM_CONSTANT(FILTER_NEAREST);
BIND_ENUM_CONSTANT(FILTER_LINEAR);
BIND_ENUM_CONSTANT(FILTER_CUBIC);
BIND_ENUM_CONSTANT(MIPMAP_MODE_DISABLED);
BIND_ENUM_CONSTANT(MIPMAP_MODE_NEAREST);
BIND_ENUM_CONSTANT(MIPMAP_MODE_LINEAR);
BIND_ENUM_CONSTANT(WRAP_CLAMP_TO_BORDER);
BIND_ENUM_CONSTANT(WRAP_CLAMP_TO_EDGE);
BIND_ENUM_CONSTANT(WRAP_REPEAT);
BIND_ENUM_CONSTANT(WRAP_MIRRORED_REPEAT);
BIND_ENUM_CONSTANT(WRAP_MIRROR_CLAMP_TO_EDGE);
BIND_ENUM_CONSTANT(SWIZZLE_RED);
BIND_ENUM_CONSTANT(SWIZZLE_GREEN);
BIND_ENUM_CONSTANT(SWIZZLE_BLUE);
BIND_ENUM_CONSTANT(SWIZZLE_ALPHA);
BIND_ENUM_CONSTANT(SWIZZLE_ZERO);
BIND_ENUM_CONSTANT(SWIZZLE_ONE);
}
bool OpenXRCompositionLayer::_should_use_fallback_node() {
if (Engine::get_singleton()->is_editor_hint() || openxr_api == nullptr) {
return true;
} else if (openxr_session_running) {
return enable_hole_punch || (!is_natively_supported() && !use_android_surface);
}
return false;
}
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::_remove_fallback_node() {
ERR_FAIL_COND(fallback != nullptr);
remove_child(fallback);
fallback->queue_free();
fallback = nullptr;
}
void OpenXRCompositionLayer::_setup_composition_layer_provider() {
if (use_android_surface || layer_viewport) {
if (composition_layer_extension) {
composition_layer_extension->register_viewport_composition_layer_provider(openxr_layer_provider);
registered = true;
}
// NOTE: We don't setup/clear when using Android surfaces, so we don't destroy the surface unexpectedly.
if (layer_viewport) {
// Set our properties on the layer provider, which will create all the necessary resources (ex swap chains).
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
}
}
}
void OpenXRCompositionLayer::_clear_composition_layer_provider() {
if (composition_layer_extension) {
composition_layer_extension->unregister_viewport_composition_layer_provider(openxr_layer_provider);
registered = false;
}
// NOTE: We don't setup/clear when using Android surfaces, so we don't destroy the surface unexpectedly.
if (!use_android_surface) {
// This will reset the viewport and free all the resources (ex swap chains) used by the layer.
openxr_layer_provider->set_viewport(RID(), Size2i());
}
}
void OpenXRCompositionLayer::_on_openxr_session_begun() {
openxr_session_running = true;
if (_should_register()) {
_setup_composition_layer_provider();
}
if (!fallback && _should_use_fallback_node()) {
_create_fallback_node();
}
}
void OpenXRCompositionLayer::_on_openxr_session_stopping() {
openxr_session_running = false;
if (fallback && !_should_use_fallback_node()) {
_remove_fallback_node();
}
_clear_composition_layer_provider();
}
void OpenXRCompositionLayer::update_fallback_mesh() {
should_update_fallback_mesh = true;
}
bool OpenXRCompositionLayer::_should_register() {
return !registered && openxr_session_running && is_inside_tree() && is_visible() && is_natively_supported();
}
XrPosef OpenXRCompositionLayer::get_openxr_pose() {
Transform3D reference_frame = XRServer::get_singleton()->get_reference_frame();
Transform3D transform = reference_frame.inverse() * get_transform();
Quaternion quat(transform.basis.orthonormalized());
return {
{ (float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w },
{ (float)transform.origin.x, (float)transform.origin.y, (float)transform.origin.z }
};
}
bool OpenXRCompositionLayer::is_viewport_in_use(SubViewport *p_viewport) {
ERR_FAIL_NULL_V(p_viewport, false);
for (const OpenXRCompositionLayer *other_composition_layer : composition_layer_nodes) {
if (other_composition_layer != this && other_composition_layer->is_inside_tree() && other_composition_layer->get_layer_viewport() == p_viewport) {
return true;
}
}
return false;
}
void OpenXRCompositionLayer::set_layer_viewport(SubViewport *p_viewport) {
if (layer_viewport == p_viewport) {
return;
}
if (p_viewport != nullptr) {
ERR_FAIL_COND_EDMSG(is_viewport_in_use(p_viewport), RTR("Cannot use the same SubViewport with multiple OpenXR composition layers. Clear it from its current layer first."));
}
if (use_android_surface) {
ERR_FAIL_COND_MSG(p_viewport != nullptr, RTR("Cannot set SubViewport on an OpenXR composition layer when using an Android surface."));
}
layer_viewport = p_viewport;
if (_should_register()) {
_setup_composition_layer_provider();
}
if (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_session_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());
}
}
}
void OpenXRCompositionLayer::set_use_android_surface(bool p_use_android_surface) {
if (use_android_surface == p_use_android_surface) {
return;
}
use_android_surface = p_use_android_surface;
if (use_android_surface) {
// It's possible that the layer provider is unregistered here (if previously invisible)
set_layer_viewport(nullptr);
openxr_layer_provider->set_use_android_surface(true, android_surface_size);
// ...and it may not be set up above because of viewport = null, android surface is false, so set it up again:
if (_should_register()) {
_setup_composition_layer_provider();
}
} else {
openxr_layer_provider->set_use_android_surface(false, Size2i());
}
notify_property_list_changed();
}
bool OpenXRCompositionLayer::get_use_android_surface() const {
return use_android_surface;
}
void OpenXRCompositionLayer::set_android_surface_size(Size2i p_size) {
if (android_surface_size == p_size) {
return;
}
android_surface_size = p_size;
if (use_android_surface) {
openxr_layer_provider->set_use_android_surface(true, android_surface_size);
}
}
Size2i OpenXRCompositionLayer::get_android_surface_size() const {
return android_surface_size;
}
SubViewport *OpenXRCompositionLayer::get_layer_viewport() const {
return layer_viewport;
}
void OpenXRCompositionLayer::set_enable_hole_punch(bool p_enable) {
if (enable_hole_punch == p_enable) {
return;
}
enable_hole_punch = p_enable;
if (_should_use_fallback_node()) {
if (fallback) {
_reset_fallback_material();
} else {
_create_fallback_node();
}
} else if (fallback) {
_remove_fallback_node();
}
update_configuration_warnings();
}
bool OpenXRCompositionLayer::get_enable_hole_punch() const {
return enable_hole_punch;
}
void OpenXRCompositionLayer::set_sort_order(int p_order) {
openxr_layer_provider->set_sort_order(p_order);
update_configuration_warnings();
}
int OpenXRCompositionLayer::get_sort_order() const {
return openxr_layer_provider->get_sort_order();
}
void OpenXRCompositionLayer::set_alpha_blend(bool p_alpha_blend) {
openxr_layer_provider->set_alpha_blend(p_alpha_blend);
if (fallback) {
_reset_fallback_material();
}
}
bool OpenXRCompositionLayer::get_alpha_blend() const {
return openxr_layer_provider->get_alpha_blend();
}
bool OpenXRCompositionLayer::is_natively_supported() const {
if (composition_layer_extension && openxr_api) {
return composition_layer_extension->is_available(openxr_layer_provider->get_openxr_type());
}
return false;
}
void OpenXRCompositionLayer::set_min_filter(Filter p_mode) {
if (swapchain_state->min_filter == (OpenXRViewportCompositionLayerProvider::Filter)p_mode) {
return;
}
swapchain_state->min_filter = (OpenXRViewportCompositionLayerProvider::Filter)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Filter OpenXRCompositionLayer::get_min_filter() const {
return (OpenXRCompositionLayer::Filter)swapchain_state->min_filter;
}
void OpenXRCompositionLayer::set_mag_filter(Filter p_mode) {
if (swapchain_state->mag_filter == (OpenXRViewportCompositionLayerProvider::Filter)p_mode) {
return;
}
swapchain_state->mag_filter = (OpenXRViewportCompositionLayerProvider::Filter)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Filter OpenXRCompositionLayer::get_mag_filter() const {
return (OpenXRCompositionLayer::Filter)swapchain_state->mag_filter;
}
void OpenXRCompositionLayer::set_mipmap_mode(MipmapMode p_mode) {
if (swapchain_state->mipmap_mode == (OpenXRViewportCompositionLayerProvider::MipmapMode)p_mode) {
return;
}
swapchain_state->mipmap_mode = (OpenXRViewportCompositionLayerProvider::MipmapMode)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::MipmapMode OpenXRCompositionLayer::get_mipmap_mode() const {
return (OpenXRCompositionLayer::MipmapMode)swapchain_state->mipmap_mode;
}
void OpenXRCompositionLayer::set_horizontal_wrap(Wrap p_mode) {
if (swapchain_state->horizontal_wrap == (OpenXRViewportCompositionLayerProvider::Wrap)p_mode) {
return;
}
swapchain_state->horizontal_wrap = (OpenXRViewportCompositionLayerProvider::Wrap)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Wrap OpenXRCompositionLayer::get_horizontal_wrap() const {
return (OpenXRCompositionLayer::Wrap)swapchain_state->horizontal_wrap;
}
void OpenXRCompositionLayer::set_vertical_wrap(Wrap p_mode) {
if (swapchain_state->vertical_wrap == (OpenXRViewportCompositionLayerProvider::Wrap)p_mode) {
return;
}
swapchain_state->vertical_wrap = (OpenXRViewportCompositionLayerProvider::Wrap)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Wrap OpenXRCompositionLayer::get_vertical_wrap() const {
return (OpenXRCompositionLayer::Wrap)swapchain_state->vertical_wrap;
}
void OpenXRCompositionLayer::set_red_swizzle(Swizzle p_mode) {
if (swapchain_state->red_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) {
return;
}
swapchain_state->red_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_red_swizzle() const {
return (OpenXRCompositionLayer::Swizzle)swapchain_state->red_swizzle;
}
void OpenXRCompositionLayer::set_green_swizzle(Swizzle p_mode) {
if (swapchain_state->green_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) {
return;
}
swapchain_state->green_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_green_swizzle() const {
return (OpenXRCompositionLayer::Swizzle)swapchain_state->green_swizzle;
}
void OpenXRCompositionLayer::set_blue_swizzle(Swizzle p_mode) {
if (swapchain_state->blue_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) {
return;
}
swapchain_state->blue_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_blue_swizzle() const {
return (OpenXRCompositionLayer::Swizzle)swapchain_state->blue_swizzle;
}
void OpenXRCompositionLayer::set_alpha_swizzle(Swizzle p_mode) {
if (swapchain_state->alpha_swizzle == (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode) {
return;
}
swapchain_state->alpha_swizzle = (OpenXRViewportCompositionLayerProvider::Swizzle)p_mode;
swapchain_state->dirty = true;
}
OpenXRCompositionLayer::Swizzle OpenXRCompositionLayer::get_alpha_swizzle() const {
return (OpenXRCompositionLayer::Swizzle)swapchain_state->alpha_swizzle;
}
void OpenXRCompositionLayer::set_max_anisotropy(float p_value) {
if (swapchain_state->max_anisotropy == p_value) {
return;
}
swapchain_state->max_anisotropy = p_value;
swapchain_state->dirty = true;
}
float OpenXRCompositionLayer::get_max_anisotropy() const {
return swapchain_state->max_anisotropy;
}
void OpenXRCompositionLayer::set_border_color(Color p_color) {
if (swapchain_state->border_color == p_color) {
return;
}
swapchain_state->border_color = p_color;
swapchain_state->dirty = true;
}
Color OpenXRCompositionLayer::get_border_color() const {
return swapchain_state->border_color;
}
Ref<JavaObject> OpenXRCompositionLayer::get_android_surface() {
return openxr_layer_provider->get_android_surface();
}
Vector2 OpenXRCompositionLayer::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const {
return Vector2(-1.0, -1.0);
}
void OpenXRCompositionLayer::_reset_fallback_material() {
ERR_FAIL_NULL(fallback);
if (fallback->get_mesh().is_null()) {
return;
}
if (enable_hole_punch && !Engine::get_singleton()->is_editor_hint() && is_natively_supported()) {
Ref<ShaderMaterial> material = fallback->get_surface_override_material(0);
if (material.is_null()) {
Ref<Shader> shader;
shader.instantiate();
shader->set_code(HOLE_PUNCH_SHADER_CODE);
material.instantiate();
material->set_shader(shader);
fallback->set_surface_override_material(0, material);
}
} else 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_local_to_scene(true);
fallback->set_surface_override_material(0, material);
}
material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, !enable_hole_punch);
material->set_transparency(get_alpha_blend() ? StandardMaterial3D::TRANSPARENCY_ALPHA : StandardMaterial3D::TRANSPARENCY_DISABLED);
material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, layer_viewport->get_texture());
} else {
fallback->set_surface_override_material(0, Ref<Material>());
}
}
void OpenXRCompositionLayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
composition_layer_nodes.push_back(this);
for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
extension_property_values.merge(extension->get_viewport_composition_layer_extension_property_defaults());
}
openxr_layer_provider->set_extension_property_values(extension_property_values);
} break;
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 (is_natively_supported() && openxr_session_running && is_inside_tree()) {
if (is_visible()) {
_setup_composition_layer_provider();
} else {
_clear_composition_layer_provider();
}
}
update_configuration_warnings();
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
update_configuration_warnings();
} break;
case NOTIFICATION_ENTER_TREE: {
if (layer_viewport && is_viewport_in_use(layer_viewport)) {
_clear_composition_layer_provider();
} else if (openxr_session_running && is_visible()) {
_setup_composition_layer_provider();
}
} break;
case NOTIFICATION_EXIT_TREE: {
// This will clean up existing resources.
_clear_composition_layer_provider();
} break;
}
}
void OpenXRCompositionLayer::_get_property_list(List<PropertyInfo> *p_property_list) const {
List<PropertyInfo> extension_properties;
for (OpenXRExtensionWrapper *extension : OpenXRAPI::get_registered_extension_wrappers()) {
extension->get_viewport_composition_layer_extension_properties(&extension_properties);
}
for (const PropertyInfo &pinfo : extension_properties) {
StringName prop_name = pinfo.name;
if (!String(prop_name).contains_char('/')) {
WARN_PRINT_ONCE(vformat("Discarding OpenXRCompositionLayer property name '%s' from extension because it doesn't contain a '/'."));
continue;
}
p_property_list->push_back(pinfo);
}
}
bool OpenXRCompositionLayer::_get(const StringName &p_property, Variant &r_value) const {
if (extension_property_values.has(p_property)) {
r_value = extension_property_values[p_property];
return true;
}
return false;
}
bool OpenXRCompositionLayer::_set(const StringName &p_property, const Variant &p_value) {
extension_property_values[p_property] = p_value;
openxr_layer_provider->set_extension_property_values(extension_property_values);
return true;
}
void OpenXRCompositionLayer::_validate_property(PropertyInfo &p_property) const {
if (!Engine::get_singleton()->is_editor_hint()) {
return;
}
if (p_property.name == "layer_viewport") {
if (use_android_surface) {
p_property.usage &= ~PROPERTY_USAGE_EDITOR;
} else {
p_property.usage |= PROPERTY_USAGE_EDITOR;
}
} else if (p_property.name == "android_surface_size") {
if (use_android_surface) {
p_property.usage |= PROPERTY_USAGE_EDITOR;
} else {
p_property.usage &= ~PROPERTY_USAGE_EDITOR;
}
}
}
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)."));
}
if (enable_hole_punch && get_sort_order() >= 0) {
warnings.push_back(RTR("Hole punching won't work as expected unless the sort order is less than zero."));
}
return warnings;
}

View File

@@ -0,0 +1,201 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include <openxr/openxr.h>
#include "../extensions/openxr_composition_layer_extension.h"
#include "scene/3d/node_3d.h"
class JavaObject;
class MeshInstance3D;
class Mesh;
class OpenXRAPI;
class OpenXRCompositionLayerExtension;
class OpenXRViewportCompositionLayerProvider;
class SubViewport;
class OpenXRCompositionLayer : public Node3D {
GDCLASS(OpenXRCompositionLayer, Node3D);
public:
// Must be identical to Filter enum definition in OpenXRViewportCompositionLayerProvider.
enum Filter {
FILTER_NEAREST,
FILTER_LINEAR,
FILTER_CUBIC,
};
// Must be identical to MipmapMode enum definition in OpenXRViewportCompositionLayerProvider.
enum MipmapMode {
MIPMAP_MODE_DISABLED,
MIPMAP_MODE_NEAREST,
MIPMAP_MODE_LINEAR,
};
// Must be identical to Wrap enum definition in OpenXRViewportCompositionLayerProvider.
enum Wrap {
WRAP_CLAMP_TO_BORDER,
WRAP_CLAMP_TO_EDGE,
WRAP_REPEAT,
WRAP_MIRRORED_REPEAT,
WRAP_MIRROR_CLAMP_TO_EDGE,
};
// Must be identical to Swizzle enum definition in OpenXRViewportCompositionLayerProvider.
enum Swizzle {
SWIZZLE_RED,
SWIZZLE_GREEN,
SWIZZLE_BLUE,
SWIZZLE_ALPHA,
SWIZZLE_ZERO,
SWIZZLE_ONE,
};
private:
XrCompositionLayerBaseHeader *composition_layer_base_header = nullptr;
OpenXRViewportCompositionLayerProvider *openxr_layer_provider = nullptr;
SubViewport *layer_viewport = nullptr;
bool use_android_surface = false;
Size2i android_surface_size = Size2i(1024, 1024);
bool enable_hole_punch = false;
MeshInstance3D *fallback = nullptr;
bool should_update_fallback_mesh = false;
bool openxr_session_running = false;
bool registered = false;
OpenXRViewportCompositionLayerProvider::SwapchainState *swapchain_state = nullptr;
Dictionary extension_property_values;
bool _should_use_fallback_node();
void _create_fallback_node();
void _reset_fallback_material();
void _remove_fallback_node();
void _setup_composition_layer_provider();
void _clear_composition_layer_provider();
protected:
OpenXRAPI *openxr_api = nullptr;
OpenXRCompositionLayerExtension *composition_layer_extension = nullptr;
static void _bind_methods();
void _notification(int p_what);
void _get_property_list(List<PropertyInfo> *p_property_list) const;
bool _get(const StringName &p_property, Variant &r_value) const;
bool _set(const StringName &p_property, const Variant &p_value);
void _validate_property(PropertyInfo &p_property) const;
virtual void _on_openxr_session_begun();
virtual void _on_openxr_session_stopping();
bool _should_register();
virtual Ref<Mesh> _create_fallback_mesh() = 0;
void update_fallback_mesh();
XrPosef get_openxr_pose();
static Vector<OpenXRCompositionLayer *> composition_layer_nodes;
bool is_viewport_in_use(SubViewport *p_viewport);
OpenXRCompositionLayer(XrCompositionLayerBaseHeader *p_composition_layer);
public:
void set_layer_viewport(SubViewport *p_viewport);
SubViewport *get_layer_viewport() const;
void set_use_android_surface(bool p_use_android_surface);
bool get_use_android_surface() const;
void set_android_surface_size(Size2i p_size);
Size2i get_android_surface_size() const;
void set_enable_hole_punch(bool p_enable);
bool get_enable_hole_punch() 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;
Ref<JavaObject> get_android_surface();
bool is_natively_supported() const;
void set_min_filter(Filter p_mode);
Filter get_min_filter() const;
void set_mag_filter(Filter p_mode);
Filter get_mag_filter() const;
void set_mipmap_mode(MipmapMode p_mode);
MipmapMode get_mipmap_mode() const;
void set_horizontal_wrap(Wrap p_mode);
Wrap get_horizontal_wrap() const;
void set_vertical_wrap(Wrap p_mode);
Wrap get_vertical_wrap() const;
void set_red_swizzle(Swizzle p_mode);
Swizzle get_red_swizzle() const;
void set_green_swizzle(Swizzle p_mode);
Swizzle get_green_swizzle() const;
void set_blue_swizzle(Swizzle p_mode);
Swizzle get_blue_swizzle() const;
void set_alpha_swizzle(Swizzle p_mode);
Swizzle get_alpha_swizzle() const;
void set_max_anisotropy(float p_value);
float get_max_anisotropy() const;
void set_border_color(Color p_color);
Color get_border_color() const;
virtual PackedStringArray get_configuration_warnings() const override;
virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const;
~OpenXRCompositionLayer();
};
VARIANT_ENUM_CAST(OpenXRCompositionLayer::Filter)
VARIANT_ENUM_CAST(OpenXRCompositionLayer::MipmapMode)
VARIANT_ENUM_CAST(OpenXRCompositionLayer::Wrap)
VARIANT_ENUM_CAST(OpenXRCompositionLayer::Swizzle)

View File

@@ -0,0 +1,214 @@
/**************************************************************************/
/* 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 "../openxr_interface.h"
#include "scene/resources/mesh.h"
OpenXRCompositionLayerCylinder::OpenXRCompositionLayerCylinder() :
OpenXRCompositionLayer((XrCompositionLayerBaseHeader *)&composition_layer) {
XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerCylinder::update_transform));
}
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");
}
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: {
update_transform();
} break;
}
}
void OpenXRCompositionLayerCylinder::update_transform() {
composition_layer.pose = get_openxr_pose();
}
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;
}
Vector2 OpenXRCompositionLayerCylinder::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const {
Transform3D cylinder_transform = get_global_transform();
Vector3 cylinder_axis = cylinder_transform.basis.get_column(1);
Vector3 offset = p_origin - cylinder_transform.origin;
float a = p_direction.dot(p_direction - cylinder_axis * p_direction.dot(cylinder_axis));
float b = 2.0 * (p_direction.dot(offset - cylinder_axis * offset.dot(cylinder_axis)));
float c = offset.dot(offset - cylinder_axis * offset.dot(cylinder_axis)) - (radius * radius);
float discriminant = b * b - 4.0 * a * c;
if (discriminant < 0.0) {
return Vector2(-1.0, -1.0);
}
float t0 = (-b - Math::sqrt(discriminant)) / (2.0 * a);
float t1 = (-b + Math::sqrt(discriminant)) / (2.0 * a);
float t = MAX(t0, t1);
if (t < 0.0) {
return Vector2(-1.0, -1.0);
}
Vector3 intersection = p_origin + p_direction * t;
Basis correction = cylinder_transform.basis.inverse();
correction.rotate(Vector3(0.0, 1.0, 0.0), -Math::PI / 2.0);
Vector3 relative_point = correction.xform(intersection - cylinder_transform.origin);
Vector2 projected_point = Vector2(relative_point.x, relative_point.z);
float intersection_angle = Math::atan2(projected_point.y, projected_point.x);
if (Math::abs(intersection_angle) > central_angle / 2.0) {
return Vector2(-1.0, -1.0);
}
float arc_length = radius * central_angle;
float height = aspect_ratio * arc_length;
if (Math::abs(relative_point.y) > height / 2.0) {
return Vector2(-1.0, -1.0);
}
float u = 0.5 + (intersection_angle / central_angle);
float v = 1.0 - (0.5 + (relative_point.y / height));
return Vector2(u, v);
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include <openxr/openxr.h>
#include "openxr_composition_layer.h"
class OpenXRCompositionLayerCylinder : public OpenXRCompositionLayer {
GDCLASS(OpenXRCompositionLayerCylinder, OpenXRCompositionLayer);
XrCompositionLayerCylinderKHR 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
1.0, // radius
Math::PI / 2.0, // centralAngle
1.0, // aspectRatio
};
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);
void update_transform();
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;
virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const override;
OpenXRCompositionLayerCylinder();
~OpenXRCompositionLayerCylinder();
};

View File

@@ -0,0 +1,238 @@
/**************************************************************************/
/* 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 "../openxr_interface.h"
#include "scene/resources/mesh.h"
OpenXRCompositionLayerEquirect::OpenXRCompositionLayerEquirect() :
OpenXRCompositionLayer((XrCompositionLayerBaseHeader *)&composition_layer) {
XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerEquirect::update_transform));
}
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");
}
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: {
update_transform();
} break;
}
}
void OpenXRCompositionLayerEquirect::update_transform() {
composition_layer.pose = get_openxr_pose();
}
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;
}
Vector2 OpenXRCompositionLayerEquirect::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const {
Transform3D equirect_transform = get_global_transform();
Vector3 offset = p_origin - equirect_transform.origin;
float a = p_direction.dot(p_direction);
float b = 2.0 * offset.dot(p_direction);
float c = offset.dot(offset) - (radius * radius);
float discriminant = b * b - 4.0 * a * c;
if (discriminant < 0.0) {
return Vector2(-1.0, -1.0);
}
float t0 = (-b - Math::sqrt(discriminant)) / (2.0 * a);
float t1 = (-b + Math::sqrt(discriminant)) / (2.0 * a);
float t = MAX(t0, t1);
if (t < 0.0) {
return Vector2(-1.0, -1.0);
}
Vector3 intersection = p_origin + p_direction * t;
Basis correction = equirect_transform.basis.inverse();
correction.rotate(Vector3(0.0, 1.0, 0.0), -Math::PI / 2.0);
Vector3 relative_point = correction.xform(intersection - equirect_transform.origin);
float horizontal_intersection_angle = Math::atan2(relative_point.z, relative_point.x);
if (Math::abs(horizontal_intersection_angle) > central_horizontal_angle / 2.0) {
return Vector2(-1.0, -1.0);
}
float vertical_intersection_angle = Math::acos(relative_point.y / radius) - (Math::PI / 2.0);
if (vertical_intersection_angle < 0) {
if (Math::abs(vertical_intersection_angle) > upper_vertical_angle) {
return Vector2(-1.0, -1.0);
}
} else if (vertical_intersection_angle > lower_vertical_angle) {
return Vector2(-1.0, -1.0);
}
// Re-center the intersection angle if the vertical angle is uneven between upper and lower.
if (upper_vertical_angle != lower_vertical_angle) {
vertical_intersection_angle -= (-upper_vertical_angle + lower_vertical_angle) / 2.0;
}
float u = 0.5 + (horizontal_intersection_angle / central_horizontal_angle);
float v = 0.5 + (vertical_intersection_angle / (upper_vertical_angle + lower_vertical_angle));
return Vector2(u, v);
}

View File

@@ -0,0 +1,89 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include <openxr/openxr.h>
#include "openxr_composition_layer.h"
class OpenXRCompositionLayerEquirect : public OpenXRCompositionLayer {
GDCLASS(OpenXRCompositionLayerEquirect, OpenXRCompositionLayer);
XrCompositionLayerEquirect2KHR 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
1.0, // radius
Math::PI / 2.0, // centralHorizontalAngle
Math::PI / 4.0, // upperVerticalAngle
-Math::PI / 4.0, // lowerVerticalAngle
};
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);
void update_transform();
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;
virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const override;
OpenXRCompositionLayerEquirect();
~OpenXRCompositionLayerEquirect();
};

View File

@@ -0,0 +1,112 @@
/**************************************************************************/
/* 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 "../openxr_interface.h"
#include "scene/resources/3d/primitive_meshes.h"
OpenXRCompositionLayerQuad::OpenXRCompositionLayerQuad() :
OpenXRCompositionLayer((XrCompositionLayerBaseHeader *)&composition_layer) {
XRServer::get_singleton()->connect("reference_frame_changed", callable_mp(this, &OpenXRCompositionLayerQuad::update_transform));
}
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");
}
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: {
update_transform();
} break;
}
}
void OpenXRCompositionLayerQuad::update_transform() {
composition_layer.pose = get_openxr_pose();
}
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;
}
Vector2 OpenXRCompositionLayerQuad::intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const {
Transform3D quad_transform = get_global_transform();
Vector3 quad_normal = quad_transform.basis.get_column(2);
float denom = quad_normal.dot(p_direction);
if (Math::abs(denom) > 0.0001) {
Vector3 vector = quad_transform.origin - p_origin;
float t = vector.dot(quad_normal) / denom;
if (t < 0.0) {
return Vector2(-1.0, -1.0);
}
Vector3 intersection = p_origin + p_direction * t;
Vector3 relative_point = intersection - quad_transform.origin;
Vector2 projected_point = Vector2(
relative_point.dot(quad_transform.basis.get_column(0)),
relative_point.dot(quad_transform.basis.get_column(1)));
if (Math::abs(projected_point.x) > quad_size.x / 2.0) {
return Vector2(-1.0, -1.0);
}
if (Math::abs(projected_point.y) > quad_size.y / 2.0) {
return Vector2(-1.0, -1.0);
}
float u = 0.5 + (projected_point.x / quad_size.x);
float v = 1.0 - (0.5 + (projected_point.y / quad_size.y));
return Vector2(u, v);
}
return Vector2(-1.0, -1.0);
}

View File

@@ -0,0 +1,70 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#pragma once
#include <openxr/openxr.h>
#include "openxr_composition_layer.h"
class OpenXRCompositionLayerQuad : public OpenXRCompositionLayer {
GDCLASS(OpenXRCompositionLayerQuad, OpenXRCompositionLayer);
XrCompositionLayerQuad 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
{ 1.0, 1.0 }, // size
};
Size2 quad_size = Size2(1.0, 1.0);
protected:
static void _bind_methods();
void _notification(int p_what);
void update_transform();
virtual Ref<Mesh> _create_fallback_mesh() override;
public:
void set_quad_size(const Size2 &p_size);
Size2 get_quad_size() const;
virtual Vector2 intersects_ray(const Vector3 &p_origin, const Vector3 &p_direction) const override;
OpenXRCompositionLayerQuad();
~OpenXRCompositionLayerQuad();
};

View File

@@ -0,0 +1,416 @@
/**************************************************************************/
/* openxr_hand.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_hand.h"
#include "../extensions/openxr_hand_tracking_extension.h"
#include "../openxr_api.h"
#include "scene/3d/skeleton_3d.h"
#include "servers/xr_server.h"
void OpenXRHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
ClassDB::bind_method(D_METHOD("set_skeleton_rig", "skeleton_rig"), &OpenXRHand::set_skeleton_rig);
ClassDB::bind_method(D_METHOD("get_skeleton_rig"), &OpenXRHand::get_skeleton_rig);
ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &OpenXRHand::set_bone_update);
ClassDB::bind_method(D_METHOD("get_bone_update"), &OpenXRHand::get_bone_update);
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
BIND_ENUM_CONSTANT(HAND_LEFT);
BIND_ENUM_CONSTANT(HAND_RIGHT);
BIND_ENUM_CONSTANT(HAND_MAX);
BIND_ENUM_CONSTANT(MOTION_RANGE_UNOBSTRUCTED);
BIND_ENUM_CONSTANT(MOTION_RANGE_CONFORM_TO_CONTROLLER);
BIND_ENUM_CONSTANT(MOTION_RANGE_MAX);
BIND_ENUM_CONSTANT(SKELETON_RIG_OPENXR);
BIND_ENUM_CONSTANT(SKELETON_RIG_HUMANOID);
BIND_ENUM_CONSTANT(SKELETON_RIG_MAX);
BIND_ENUM_CONSTANT(BONE_UPDATE_FULL);
BIND_ENUM_CONSTANT(BONE_UPDATE_ROTATION_ONLY);
BIND_ENUM_CONSTANT(BONE_UPDATE_MAX);
}
OpenXRHand::OpenXRHand() {
openxr_api = OpenXRAPI::get_singleton();
hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
}
void OpenXRHand::set_hand(Hands p_hand) {
ERR_FAIL_INDEX(p_hand, HAND_MAX);
hand = p_hand;
}
OpenXRHand::Hands OpenXRHand::get_hand() const {
return hand;
}
void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
hand_skeleton = p_hand_skeleton;
// TODO if inside tree call _get_bones()
}
void OpenXRHand::set_motion_range(MotionRange p_motion_range) {
ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
motion_range = p_motion_range;
_set_motion_range();
}
OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
return motion_range;
}
NodePath OpenXRHand::get_hand_skeleton() const {
return hand_skeleton;
}
void OpenXRHand::_set_motion_range() {
if (!hand_tracking_ext) {
return;
}
XrHandJointsMotionRangeEXT xr_motion_range;
switch (motion_range) {
case MOTION_RANGE_UNOBSTRUCTED:
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
break;
case MOTION_RANGE_CONFORM_TO_CONTROLLER:
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
break;
default:
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
break;
}
hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(hand), xr_motion_range);
}
void OpenXRHand::set_skeleton_rig(SkeletonRig p_skeleton_rig) {
ERR_FAIL_INDEX(p_skeleton_rig, SKELETON_RIG_MAX);
skeleton_rig = p_skeleton_rig;
}
OpenXRHand::SkeletonRig OpenXRHand::get_skeleton_rig() const {
return skeleton_rig;
}
void OpenXRHand::set_bone_update(BoneUpdate p_bone_update) {
ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX);
bone_update = p_bone_update;
}
OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
return bone_update;
}
Skeleton3D *OpenXRHand::get_skeleton() {
if (!has_node(hand_skeleton)) {
return nullptr;
}
Node *node = get_node(hand_skeleton);
if (!node) {
return nullptr;
}
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
return skeleton;
}
void OpenXRHand::_get_joint_data() {
// Table of bone names for different rig types.
static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = {
// SKELETON_RIG_OPENXR bone names.
{
"Palm",
"Wrist",
"Thumb_Metacarpal",
"Thumb_Proximal",
"Thumb_Distal",
"Thumb_Tip",
"Index_Metacarpal",
"Index_Proximal",
"Index_Intermediate",
"Index_Distal",
"Index_Tip",
"Middle_Metacarpal",
"Middle_Proximal",
"Middle_Intermediate",
"Middle_Distal",
"Middle_Tip",
"Ring_Metacarpal",
"Ring_Proximal",
"Ring_Intermediate",
"Ring_Distal",
"Ring_Tip",
"Little_Metacarpal",
"Little_Proximal",
"Little_Intermediate",
"Little_Distal",
"Little_Tip" },
// SKELETON_RIG_HUMANOID bone names.
{
"Palm",
"Hand",
"ThumbMetacarpal",
"ThumbProximal",
"ThumbDistal",
"ThumbTip",
"IndexMetacarpal",
"IndexProximal",
"IndexIntermediate",
"IndexDistal",
"IndexTip",
"MiddleMetacarpal",
"MiddleProximal",
"MiddleIntermediate",
"MiddleDistal",
"MiddleTip",
"RingMetacarpal",
"RingProximal",
"RingIntermediate",
"RingDistal",
"RingTip",
"LittleMetacarpal",
"LittleProximal",
"LittleIntermediate",
"LittleDistal",
"LittleTip" }
};
// Table of bone name formats for different rig types and left/right hands.
static const String bone_name_formats[SKELETON_RIG_MAX][2] = {
// SKELETON_RIG_OPENXR bone name format.
{ "<bone>_L", "<bone>_R" },
// SKELETON_RIG_HUMANOID bone name format.
{ "Left<bone>", "Right<bone>" }
};
// reset JIC
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
// Find the skeleton-bones associated with each OpenXR joint.
int bones[XR_HAND_JOINT_COUNT_EXT];
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
// Construct the expected bone name.
String bone_name = bone_name_formats[skeleton_rig][hand].replace("<bone>", bone_names[skeleton_rig][i]);
// Find the skeleton bone.
bones[i] = skeleton->find_bone(bone_name);
if (bones[i] == -1) {
print_line("Couldn't obtain bone for", bone_name);
}
}
// Assemble the OpenXR joint relationship to the available skeleton bones.
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
// Get the skeleton bone (skip if not found).
const int bone = bones[i];
if (bone == -1) {
continue;
}
// Find the parent skeleton-bone.
const int parent_bone = skeleton->get_bone_parent(bone);
if (parent_bone == -1) {
// If no parent skeleton-bone exists then drive this relative to palm joint.
joints[i].bone = bone;
joints[i].parent_joint = XR_HAND_JOINT_PALM_EXT;
continue;
}
// Find the OpenXR joint associated with the parent skeleton-bone.
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; ++j) {
if (bones[j] == parent_bone) {
// If a parent joint is found then drive this bone relative to it.
joints[i].bone = bone;
joints[i].parent_joint = j;
break;
}
}
}
}
void OpenXRHand::_update_skeleton() {
if (openxr_api == nullptr || !openxr_api->is_initialized()) {
return;
} else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
return;
}
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
// Table of bone adjustments for different rig types
static const Quaternion bone_adjustments[SKELETON_RIG_MAX] = {
// SKELETON_RIG_OPENXR bone adjustment. This is an identity quaternion
// because the incoming quaternions are already in OpenXR format.
Quaternion(),
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
Quaternion(0.0, -Math::SQRT12, Math::SQRT12, 0.0),
};
// we cache our transforms so we can quickly calculate local transforms
XRPose::TrackingConfidence confidences[XR_HAND_JOINT_COUNT_EXT];
Quaternion quaternions[XR_HAND_JOINT_COUNT_EXT];
Quaternion inv_quaternions[XR_HAND_JOINT_COUNT_EXT];
Vector3 positions[XR_HAND_JOINT_COUNT_EXT];
const Quaternion &rig_adjustment = bone_adjustments[skeleton_rig];
const OpenXRHandTrackingExtension::HandTracker *hand_tracker = hand_tracking_ext->get_hand_tracker(OpenXRHandTrackingExtension::HandTrackedHands(hand));
const float ws = XRServer::get_singleton()->get_world_scale();
if (hand_tracker->is_initialized && hand_tracker->locations.isActive) {
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_NONE;
quaternions[i] = Quaternion();
positions[i] = Vector3();
const XrHandJointLocationEXT &location = hand_tracker->joint_locations[i];
const XrPosef &pose = location.pose;
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
quaternions[i] = Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * rig_adjustment;
inv_quaternions[i] = quaternions[i].inverse();
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
positions[i] = Vector3(pose.position.x * ws, pose.position.y * ws, pose.position.z * ws);
// TODO get inverse of position, we'll do this later. For now we're ignoring bone positions which generally works better anyway
} else {
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_LOW;
}
}
}
}
if (confidences[XR_HAND_JOINT_PALM_EXT] != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
// Iterate over all the OpenXR joints.
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
// Get the skeleton bone (skip if none).
const int bone = joints[joint].bone;
if (bone == -1) {
continue;
}
// Calculate the relative relationship to the parent bone joint.
const int parent_joint = joints[joint].parent_joint;
const Quaternion q = inv_quaternions[parent_joint] * quaternions[joint];
const Vector3 p = inv_quaternions[parent_joint].xform(positions[joint] - positions[parent_joint]);
// Update the bone position if enabled by update mode.
if (bone_update == BONE_UPDATE_FULL) {
skeleton->set_bone_pose_position(joints[joint].bone, p);
}
// Always update the bone rotation.
skeleton->set_bone_pose_rotation(joints[joint].bone, q);
}
// Transform the OpenXRHand to the skeleton pose.
Transform3D t;
t.basis = Basis(quaternions[XR_HAND_JOINT_PALM_EXT]);
t.origin = positions[XR_HAND_JOINT_PALM_EXT];
set_transform(t);
// show it
set_visible(true);
} else {
// hide it
set_visible(false);
}
} else {
// hide it
set_visible(false);
}
}
void OpenXRHand::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_get_joint_data();
set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
set_process_internal(false);
// reset
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
_update_skeleton();
} break;
default: {
} break;
}
}

View File

@@ -0,0 +1,119 @@
/**************************************************************************/
/* openxr_hand.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. */
/**************************************************************************/
#pragma once
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRHandTrackingExtension;
class OpenXRHand : public Node3D {
GDCLASS(OpenXRHand, Node3D);
public:
enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands.
HAND_LEFT,
HAND_RIGHT,
HAND_MAX
};
enum MotionRange { // Deprecated, need to change this to OpenXRInterface::HandMotionRange.
MOTION_RANGE_UNOBSTRUCTED,
MOTION_RANGE_CONFORM_TO_CONTROLLER,
MOTION_RANGE_MAX
};
enum SkeletonRig {
SKELETON_RIG_OPENXR,
SKELETON_RIG_HUMANOID,
SKELETON_RIG_MAX
};
enum BoneUpdate {
BONE_UPDATE_FULL,
BONE_UPDATE_ROTATION_ONLY,
BONE_UPDATE_MAX
};
private:
struct JointData {
int bone = -1;
int parent_joint = -1;
};
OpenXRAPI *openxr_api = nullptr;
OpenXRHandTrackingExtension *hand_tracking_ext = nullptr;
Hands hand = HAND_LEFT;
MotionRange motion_range = MOTION_RANGE_UNOBSTRUCTED;
NodePath hand_skeleton;
SkeletonRig skeleton_rig = SKELETON_RIG_OPENXR;
BoneUpdate bone_update = BONE_UPDATE_FULL;
JointData joints[XR_HAND_JOINT_COUNT_EXT];
void _set_motion_range();
Skeleton3D *get_skeleton();
void _get_joint_data();
void _update_skeleton();
protected:
static void _bind_methods();
public:
OpenXRHand();
void set_hand(Hands p_hand);
Hands get_hand() const;
void set_motion_range(MotionRange p_motion_range);
MotionRange get_motion_range() const;
void set_hand_skeleton(const NodePath &p_hand_skeleton);
NodePath get_hand_skeleton() const;
void set_skeleton_rig(SkeletonRig p_skeleton_rig);
SkeletonRig get_skeleton_rig() const;
void set_bone_update(BoneUpdate p_bone_update);
BoneUpdate get_bone_update() const;
void _notification(int p_what);
};
VARIANT_ENUM_CAST(OpenXRHand::Hands)
VARIANT_ENUM_CAST(OpenXRHand::MotionRange)
VARIANT_ENUM_CAST(OpenXRHand::SkeletonRig)
VARIANT_ENUM_CAST(OpenXRHand::BoneUpdate)

View File

@@ -0,0 +1,168 @@
/**************************************************************************/
/* openxr_render_model.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_render_model.h"
#include "../extensions/openxr_render_model_extension.h"
#include "core/config/project_settings.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/xr/xr_nodes.h"
#include "scene/resources/3d/primitive_meshes.h"
void OpenXRRenderModel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_top_level_path"), &OpenXRRenderModel::get_top_level_path);
ClassDB::bind_method(D_METHOD("get_render_model"), &OpenXRRenderModel::get_render_model);
ClassDB::bind_method(D_METHOD("set_render_model", "render_model"), &OpenXRRenderModel::set_render_model);
ADD_PROPERTY(PropertyInfo(Variant::RID, "render_model"), "set_render_model", "get_render_model");
ADD_SIGNAL(MethodInfo("render_model_top_level_path_changed"));
}
void OpenXRRenderModel::_load_render_model_scene() {
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
ERR_FAIL_NULL(render_model_extension);
ERR_FAIL_COND(render_model.is_null());
scene = render_model_extension->render_model_new_scene_instance(render_model);
if (scene) {
// Get and cache our animatable nodes.
animatable_nodes.clear();
uint32_t count = render_model_extension->render_model_get_animatable_node_count(render_model);
for (uint32_t i = 0; i < count; i++) {
String node_name = render_model_extension->render_model_get_animatable_node_name(render_model, i);
if (!node_name.is_empty()) {
Node3D *child = Object::cast_to<Node3D>(scene->find_child(node_name));
if (child) {
animatable_nodes[node_name] = child;
}
}
}
// Now add to scene.
add_child(scene);
}
}
void OpenXRRenderModel::_on_render_model_top_level_path_changed(RID p_render_model) {
if (render_model == p_render_model) {
emit_signal("render_model_top_level_path_changed");
}
}
void OpenXRRenderModel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
ERR_FAIL_NULL(render_model_extension);
if (render_model.is_valid()) {
_load_render_model_scene();
}
set_process_internal(true);
render_model_extension->connect("render_model_top_level_path_changed", callable_mp(this, &OpenXRRenderModel::_on_render_model_top_level_path_changed));
} break;
case NOTIFICATION_EXIT_TREE: {
set_process_internal(false);
if (scene) {
animatable_nodes.clear();
remove_child(scene);
scene->queue_free();
scene = nullptr;
}
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
if (render_model_extension) {
render_model_extension->disconnect("render_model_top_level_path_changed", callable_mp(this, &OpenXRRenderModel::_on_render_model_top_level_path_changed));
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (render_model.is_valid()) {
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
ERR_FAIL_NULL(render_model_extension);
if (render_model_extension->render_model_get_confidence(render_model) != XRPose::TrackingConfidence::XR_TRACKING_CONFIDENCE_NONE) {
set_transform(render_model_extension->render_model_get_root_transform(render_model));
if (scene) {
uint32_t count = render_model_extension->render_model_get_animatable_node_count(render_model);
for (uint32_t i = 0; i < count; i++) {
String node_name = render_model_extension->render_model_get_animatable_node_name(render_model, i);
if (!node_name.is_empty() && animatable_nodes.has(node_name)) {
Node3D *child = animatable_nodes[node_name];
child->set_visible(render_model_extension->render_model_is_animatable_node_visible(render_model, i));
child->set_transform(render_model_extension->render_model_get_animatable_node_transform(render_model, i));
}
}
}
}
}
} break;
}
}
String OpenXRRenderModel::get_top_level_path() const {
String ret;
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
if (render_model.is_valid() && render_model_extension) {
ret = render_model_extension->render_model_get_top_level_path_as_string(render_model);
}
return ret;
}
PackedStringArray OpenXRRenderModel::get_configuration_warnings() const {
PackedStringArray warnings;
Node *parent = get_parent();
if (!parent->is_class("XROrigin3D") && !parent->is_class("OpenXRRenderModelManager")) {
warnings.push_back("This node must be a child of either a XROrigin3D or OpenXRRenderModelManager node!");
}
if (!GLOBAL_GET("xr/openxr/extensions/render_model")) {
warnings.push_back("The render model extension is not enabled in project settings!");
}
return warnings;
}
RID OpenXRRenderModel::get_render_model() const {
return render_model;
}
void OpenXRRenderModel::set_render_model(RID p_render_model) {
render_model = p_render_model;
if (is_inside_tree() && render_model.is_valid()) {
_load_render_model_scene();
}
}

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* openxr_render_model.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. */
/**************************************************************************/
#pragma once
#include "scene/3d/node_3d.h"
#include <openxr/openxr.h>
class OpenXRRenderModel : public Node3D {
GDCLASS(OpenXRRenderModel, Node3D);
private:
RID render_model;
Node3D *scene = nullptr;
HashMap<String, Node3D *> animatable_nodes;
void _load_render_model_scene();
void _on_render_model_top_level_path_changed(RID p_render_model);
protected:
static void _bind_methods();
void _notification(int p_what);
public:
virtual PackedStringArray get_configuration_warnings() const override;
RID get_render_model() const;
void set_render_model(RID p_render_model);
String get_top_level_path() const;
};

View File

@@ -0,0 +1,284 @@
/**************************************************************************/
/* openxr_render_model_manager.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_render_model_manager.h"
#include "../extensions/openxr_render_model_extension.h"
#include "../openxr_api.h"
#include "core/config/project_settings.h"
#include "scene/3d/xr/xr_nodes.h"
#include "servers/xr_server.h"
void OpenXRRenderModelManager::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tracker"), &OpenXRRenderModelManager::get_tracker);
ClassDB::bind_method(D_METHOD("set_tracker", "tracker"), &OpenXRRenderModelManager::set_tracker);
ADD_PROPERTY(PropertyInfo(Variant::INT, "tracker", PROPERTY_HINT_ENUM, "Any,None set,Left Hand,Right Hand"), "set_tracker", "get_tracker");
ClassDB::bind_method(D_METHOD("get_make_local_to_pose"), &OpenXRRenderModelManager::get_make_local_to_pose);
ClassDB::bind_method(D_METHOD("set_make_local_to_pose", "make_local_to_pose"), &OpenXRRenderModelManager::set_make_local_to_pose);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "make_local_to_pose", PROPERTY_HINT_ENUM_SUGGESTION, "aim,grip"), "set_make_local_to_pose", "get_make_local_to_pose");
ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::OBJECT, "render_model", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRRenderModel")));
ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::OBJECT, "render_model", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRRenderModel")));
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_ANY);
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_NONE_SET);
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_LEFT_HAND);
BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_RIGHT_HAND);
}
bool OpenXRRenderModelManager::_has_filters() {
return tracker != 0;
}
void OpenXRRenderModelManager::_update_models() {
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
ERR_FAIL_NULL(render_model_extension);
// Make a copy of our current models.
HashMap<RID, Node3D *> org_render_models = render_models;
// Loop through our interaction data so we add new entries.
TypedArray<RID> render_model_rids = render_model_extension->render_model_get_all();
for (const RID rid : render_model_rids) {
bool filter = false;
if (tracker != 0) {
XrPath model_path = render_model_extension->render_model_get_top_level_path(rid);
if (model_path != xr_path) {
// ignore this.
filter = true;
}
}
if (!filter) {
if (render_models.has(rid)) {
org_render_models.erase(rid);
} else {
// Create our container node before adding our first render model.
if (container == nullptr) {
container = memnew(Node3D);
add_child(container);
}
OpenXRRenderModel *render_model = memnew(OpenXRRenderModel);
render_model->set_render_model(rid);
container->add_child(render_model);
render_models[rid] = render_model;
emit_signal(SNAME("render_model_added"), render_model);
}
}
}
// Remove models we no longer need.
for (const KeyValue<RID, Node3D *> &e : org_render_models) {
// We sent this just before removing.
emit_signal(SNAME("render_model_removed"), e.value);
if (container) {
container->remove_child(e.value);
}
e.value->queue_free();
render_models.erase(e.key);
}
is_dirty = false;
}
void OpenXRRenderModelManager::_on_render_model_added(RID p_render_model) {
if (_has_filters()) {
// We'll update this in internal process.
is_dirty = true;
} else {
// No filters? Do this right away.
_update_models();
}
}
void OpenXRRenderModelManager::_on_render_model_removed(RID p_render_model) {
if (_has_filters()) {
// We'll update this in internal process.
is_dirty = true;
} else {
// No filters? Do this right away.
_update_models();
}
}
void OpenXRRenderModelManager::_on_render_model_top_level_path_changed(RID p_path) {
if (_has_filters()) {
// We'll update this in internal process.
is_dirty = true;
}
}
void OpenXRRenderModelManager::_notification(int p_what) {
// Do not run in editor!
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();
ERR_FAIL_NULL(render_model_extension);
if (!render_model_extension->is_active()) {
return;
}
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_models();
render_model_extension->connect(SNAME("render_model_added"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_added));
render_model_extension->connect(SNAME("render_model_removed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_removed));
render_model_extension->connect(SNAME("render_model_top_level_path_changed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_top_level_path_changed));
if (_has_filters()) {
set_process_internal(true);
}
} break;
case NOTIFICATION_EXIT_TREE: {
render_model_extension->disconnect(SNAME("render_model_added"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_added));
render_model_extension->disconnect(SNAME("render_model_removed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_removed));
render_model_extension->disconnect(SNAME("render_model_top_level_path_changed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_top_level_path_changed));
set_process_internal(false);
is_dirty = false;
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (is_dirty) {
_update_models();
}
if (positional_tracker.is_valid() && !make_local_to_pose.is_empty() && container) {
Ref<XRPose> pose = positional_tracker->get_pose(make_local_to_pose);
if (pose.is_valid()) {
container->set_transform(pose->get_adjusted_transform().affine_inverse());
} else {
container->set_transform(Transform3D());
}
}
if (!_has_filters()) {
// No need to keep calling this.
set_process_internal(false);
}
}
}
}
PackedStringArray OpenXRRenderModelManager::get_configuration_warnings() const {
PackedStringArray warnings;
XROrigin3D *parent = nullptr;
if (tracker == 0 || tracker == 1) {
if (!make_local_to_pose.is_empty()) {
warnings.push_back("Must specify a tracker to make node local to pose.");
}
parent = Object::cast_to<XROrigin3D>(get_parent());
} else {
Node *node = get_parent();
while (!parent && node) {
parent = Object::cast_to<XROrigin3D>(node);
node = node->get_parent();
}
}
if (!parent) {
warnings.push_back("This node must be a child of an XROrigin3D node!");
}
if (!GLOBAL_GET("xr/openxr/extensions/render_model")) {
warnings.push_back("The render model extension is not enabled in project settings!");
}
return warnings;
}
void OpenXRRenderModelManager::set_tracker(RenderModelTracker p_tracker) {
if (tracker != p_tracker) {
tracker = p_tracker;
is_dirty = true;
if (tracker == RENDER_MODEL_TRACKER_ANY || tracker == RENDER_MODEL_TRACKER_NONE_SET) {
xr_path = XR_NULL_PATH;
} else if (!Engine::get_singleton()->is_editor_hint()) {
XRServer *xr_server = XRServer::get_singleton();
OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();
if (openxr_api && xr_server) {
String toplevel_path;
String tracker_name;
if (tracker == RENDER_MODEL_TRACKER_LEFT_HAND) {
tracker_name = "left_hand";
toplevel_path = "/user/hand/left";
} else if (tracker == RENDER_MODEL_TRACKER_RIGHT_HAND) {
tracker_name = "right_hand";
toplevel_path = "/user/hand/right";
} else {
ERR_FAIL_MSG("Unsupported tracker value set.");
}
positional_tracker = xr_server->get_tracker(tracker_name);
if (positional_tracker.is_null()) {
WARN_PRINT("OpenXR: Can't find tracker " + tracker_name);
}
xr_path = openxr_api->get_xr_path(toplevel_path);
if (xr_path == XR_NULL_PATH) {
WARN_PRINT("OpenXR: Can't find path for " + toplevel_path);
}
}
}
// Even if we now no longer have filters, we must update at least once.
set_process_internal(true);
}
}
OpenXRRenderModelManager::RenderModelTracker OpenXRRenderModelManager::get_tracker() const {
return tracker;
}
void OpenXRRenderModelManager::set_make_local_to_pose(const String &p_action) {
if (make_local_to_pose != p_action) {
make_local_to_pose = p_action;
if (container) {
// Reset just in case. It'll be set to the correct transform
// in our process if required.
container->set_transform(Transform3D());
}
}
}
String OpenXRRenderModelManager::get_make_local_to_pose() const {
return make_local_to_pose;
}

View File

@@ -0,0 +1,84 @@
/**************************************************************************/
/* openxr_render_model_manager.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. */
/**************************************************************************/
#pragma once
#include "openxr_render_model.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/packed_scene.h"
#include "servers/xr/xr_positional_tracker.h"
#include <openxr/openxr.h>
class OpenXRRenderModelManager : public Node3D {
GDCLASS(OpenXRRenderModelManager, Node3D);
public:
enum RenderModelTracker {
RENDER_MODEL_TRACKER_ANY,
RENDER_MODEL_TRACKER_NONE_SET,
RENDER_MODEL_TRACKER_LEFT_HAND,
RENDER_MODEL_TRACKER_RIGHT_HAND,
};
virtual PackedStringArray get_configuration_warnings() const override;
void set_tracker(RenderModelTracker p_tracker);
RenderModelTracker get_tracker() const;
void set_make_local_to_pose(const String &p_action);
String get_make_local_to_pose() const;
private:
HashMap<RID, Node3D *> render_models;
Node3D *container = nullptr;
bool is_dirty = false;
RenderModelTracker tracker = RENDER_MODEL_TRACKER_ANY;
String make_local_to_pose;
// cached values
Ref<XRPositionalTracker> positional_tracker;
XrPath xr_path = XR_NULL_PATH;
bool _has_filters();
void _on_render_model_added(RID p_render_model);
void _on_render_model_removed(RID p_render_model);
void _on_render_model_top_level_path_changed(RID p_path);
void _update_models();
protected:
static void _bind_methods();
void _notification(int p_what);
};
VARIANT_ENUM_CAST(OpenXRRenderModelManager::RenderModelTracker);

View File

@@ -0,0 +1,106 @@
/**************************************************************************/
/* openxr_visibility_mask.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_visibility_mask.h"
#include "../extensions/openxr_visibility_mask_extension.h"
#include "../openxr_interface.h"
#include "scene/3d/xr/xr_nodes.h"
void OpenXRVisibilityMask::_bind_methods() {
}
void OpenXRVisibilityMask::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
OpenXRVisibilityMaskExtension *vis_mask_ext = OpenXRVisibilityMaskExtension::get_singleton();
if (vis_mask_ext && vis_mask_ext->is_available()) {
set_base(vis_mask_ext->get_mesh());
}
} break;
case NOTIFICATION_EXIT_TREE: {
set_base(RID());
} break;
}
}
void OpenXRVisibilityMask::_on_openxr_session_begun() {
if (is_inside_tree()) {
OpenXRVisibilityMaskExtension *vis_mask_ext = OpenXRVisibilityMaskExtension::get_singleton();
if (vis_mask_ext && vis_mask_ext->is_available()) {
set_base(vis_mask_ext->get_mesh());
}
}
}
void OpenXRVisibilityMask::_on_openxr_session_stopping() {
set_base(RID());
}
PackedStringArray OpenXRVisibilityMask::get_configuration_warnings() const {
PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
XRCamera3D *camera = Object::cast_to<XRCamera3D>(get_parent());
if (camera == nullptr) {
warnings.push_back(RTR("OpenXR visibility mask must have an XRCamera3D node as their parent."));
}
}
return warnings;
}
AABB OpenXRVisibilityMask::get_aabb() const {
AABB ret;
// Make sure it's always visible, this is positioned through its shader.
ret.position = Vector3(-1000.0, -1000.0, -1000.0);
ret.size = Vector3(2000.0, 2000.0, 2000.0);
return ret;
}
OpenXRVisibilityMask::OpenXRVisibilityMask() {
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->connect("session_begun", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_begun));
openxr_interface->connect("session_stopping", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_stopping));
}
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(get_instance(), RS::SHADOW_CASTING_SETTING_OFF);
}
OpenXRVisibilityMask::~OpenXRVisibilityMask() {
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->disconnect("session_begun", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_begun));
openxr_interface->disconnect("session_stopping", callable_mp(this, &OpenXRVisibilityMask::_on_openxr_session_stopping));
}
}

View File

@@ -0,0 +1,53 @@
/**************************************************************************/
/* openxr_visibility_mask.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. */
/**************************************************************************/
#pragma once
#include "scene/3d/visual_instance_3d.h"
class OpenXRVisibilityMask : public VisualInstance3D {
GDCLASS(OpenXRVisibilityMask, VisualInstance3D);
protected:
static void _bind_methods();
void _notification(int p_what);
void _on_openxr_session_begun();
void _on_openxr_session_stopping();
public:
virtual PackedStringArray get_configuration_warnings() const override;
virtual AABB get_aabb() const override;
OpenXRVisibilityMask();
~OpenXRVisibilityMask();
};