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
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:
11
modules/openxr/scene/SCsub
Normal file
11
modules/openxr/scene/SCsub
Normal 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
|
726
modules/openxr/scene/openxr_composition_layer.cpp
Normal file
726
modules/openxr/scene/openxr_composition_layer.cpp
Normal 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;
|
||||
}
|
201
modules/openxr/scene/openxr_composition_layer.h
Normal file
201
modules/openxr/scene/openxr_composition_layer.h
Normal 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)
|
214
modules/openxr/scene/openxr_composition_layer_cylinder.cpp
Normal file
214
modules/openxr/scene/openxr_composition_layer_cylinder.cpp
Normal 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);
|
||||
}
|
84
modules/openxr/scene/openxr_composition_layer_cylinder.h
Normal file
84
modules/openxr/scene/openxr_composition_layer_cylinder.h
Normal 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();
|
||||
};
|
238
modules/openxr/scene/openxr_composition_layer_equirect.cpp
Normal file
238
modules/openxr/scene/openxr_composition_layer_equirect.cpp
Normal 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);
|
||||
}
|
89
modules/openxr/scene/openxr_composition_layer_equirect.h
Normal file
89
modules/openxr/scene/openxr_composition_layer_equirect.h
Normal 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();
|
||||
};
|
112
modules/openxr/scene/openxr_composition_layer_quad.cpp
Normal file
112
modules/openxr/scene/openxr_composition_layer_quad.cpp
Normal 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);
|
||||
}
|
70
modules/openxr/scene/openxr_composition_layer_quad.h
Normal file
70
modules/openxr/scene/openxr_composition_layer_quad.h
Normal 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();
|
||||
};
|
416
modules/openxr/scene/openxr_hand.cpp
Normal file
416
modules/openxr/scene/openxr_hand.cpp
Normal 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;
|
||||
}
|
||||
}
|
119
modules/openxr/scene/openxr_hand.h
Normal file
119
modules/openxr/scene/openxr_hand.h
Normal 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)
|
168
modules/openxr/scene/openxr_render_model.cpp
Normal file
168
modules/openxr/scene/openxr_render_model.cpp
Normal 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();
|
||||
}
|
||||
}
|
60
modules/openxr/scene/openxr_render_model.h
Normal file
60
modules/openxr/scene/openxr_render_model.h
Normal 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;
|
||||
};
|
284
modules/openxr/scene/openxr_render_model_manager.cpp
Normal file
284
modules/openxr/scene/openxr_render_model_manager.cpp
Normal 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;
|
||||
}
|
84
modules/openxr/scene/openxr_render_model_manager.h
Normal file
84
modules/openxr/scene/openxr_render_model_manager.h
Normal 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);
|
106
modules/openxr/scene/openxr_visibility_mask.cpp
Normal file
106
modules/openxr/scene/openxr_visibility_mask.cpp
Normal 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));
|
||||
}
|
||||
}
|
53
modules/openxr/scene/openxr_visibility_mask.h
Normal file
53
modules/openxr/scene/openxr_visibility_mask.h
Normal 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();
|
||||
};
|
Reference in New Issue
Block a user