feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

View file

@ -0,0 +1,10 @@
#!/usr/bin/env python
Import("env")
Import("env_openxr")
module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj

View file

@ -0,0 +1,430 @@
/**************************************************************************/
/* openxr_composition_layer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_composition_layer.h"
#include "../extensions/openxr_composition_layer_extension.h"
#include "../openxr_api.h"
#include "../openxr_interface.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/xr_nodes.h"
#include "scene/main/viewport.h"
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() {
openxr_api = OpenXRAPI::get_singleton();
composition_layer_extension = OpenXRCompositionLayerExtension::get_singleton();
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->connect("session_begun", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_begun));
openxr_interface->connect("session_stopping", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_stopping));
}
set_process_internal(true);
set_notify_local_transform(true);
if (Engine::get_singleton()->is_editor_hint()) {
// In the editor, create the fallback right away.
_create_fallback_node();
}
}
OpenXRCompositionLayer::~OpenXRCompositionLayer() {
Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
if (openxr_interface.is_valid()) {
openxr_interface->disconnect("session_begun", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_begun));
openxr_interface->disconnect("session_stopping", callable_mp(this, &OpenXRCompositionLayer::_on_openxr_session_stopping));
}
composition_layer_nodes.erase(this);
if (openxr_layer_provider != nullptr) {
memdelete(openxr_layer_provider);
openxr_layer_provider = nullptr;
}
}
void OpenXRCompositionLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer_viewport", "viewport"), &OpenXRCompositionLayer::set_layer_viewport);
ClassDB::bind_method(D_METHOD("get_layer_viewport"), &OpenXRCompositionLayer::get_layer_viewport);
ClassDB::bind_method(D_METHOD("set_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("is_natively_supported"), &OpenXRCompositionLayer::is_natively_supported);
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::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");
}
bool OpenXRCompositionLayer::_should_use_fallback_node() {
if (Engine::get_singleton()->is_editor_hint()) {
return true;
} else if (openxr_session_running) {
return enable_hole_punch || !is_natively_supported();
}
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::_on_openxr_session_begun() {
openxr_session_running = true;
if (layer_viewport && is_natively_supported() && is_visible() && is_inside_tree()) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
}
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();
} else {
openxr_layer_provider->set_viewport(RID(), Size2i());
}
}
void OpenXRCompositionLayer::update_fallback_mesh() {
should_update_fallback_mesh = true;
}
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) {
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."));
}
layer_viewport = p_viewport;
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());
}
}
}
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) {
if (openxr_layer_provider) {
openxr_layer_provider->set_sort_order(p_order);
update_configuration_warnings();
}
}
int OpenXRCompositionLayer::get_sort_order() const {
if (openxr_layer_provider) {
return openxr_layer_provider->get_sort_order();
}
return 1;
}
void OpenXRCompositionLayer::set_alpha_blend(bool p_alpha_blend) {
if (openxr_layer_provider) {
openxr_layer_provider->set_alpha_blend(p_alpha_blend);
if (fallback) {
_reset_fallback_material();
}
}
}
bool OpenXRCompositionLayer::get_alpha_blend() const {
if (openxr_layer_provider) {
return openxr_layer_provider->get_alpha_blend();
}
return false;
}
bool OpenXRCompositionLayer::is_natively_supported() const {
if (composition_layer_extension) {
return composition_layer_extension->is_available(openxr_layer_provider->get_openxr_type());
}
return false;
}
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);
Ref<ViewportTexture> texture = material->get_texture(StandardMaterial3D::TEXTURE_ALBEDO);
if (texture.is_null()) {
texture.instantiate();
// ViewportTexture can't be configured without a local scene, so use this hack to set it.
HashMap<Ref<Resource>, Ref<Resource>> remap_cache;
texture->configure_for_local_scene(this, remap_cache);
}
Node *loc_scene = texture->get_local_scene();
NodePath viewport_path = loc_scene->get_path_to(layer_viewport);
texture->set_viewport_path_in_scene(viewport_path);
material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, texture);
} else {
fallback->set_surface_override_material(0, Ref<Material>());
}
}
void OpenXRCompositionLayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
composition_layer_nodes.push_back(this);
if (openxr_layer_provider) {
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 (!fallback && openxr_session_running && is_inside_tree()) {
if (layer_viewport && is_visible()) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
} else {
openxr_layer_provider->set_viewport(RID(), Size2i());
}
}
update_configuration_warnings();
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
update_configuration_warnings();
} break;
case NOTIFICATION_ENTER_TREE: {
if (composition_layer_extension) {
composition_layer_extension->register_viewport_composition_layer_provider(openxr_layer_provider);
}
if (is_viewport_in_use(layer_viewport)) {
set_layer_viewport(nullptr);
} else if (!fallback && layer_viewport && openxr_session_running && is_visible()) {
openxr_layer_provider->set_viewport(layer_viewport->get_viewport_rid(), layer_viewport->get_size());
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (composition_layer_extension) {
composition_layer_extension->unregister_viewport_composition_layer_provider(openxr_layer_provider);
}
if (!fallback) {
// This will clean up existing resources.
openxr_layer_provider->set_viewport(RID(), Size2i());
}
} 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("/")) {
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;
}
bool OpenXRCompositionLayer::_set(const StringName &p_property, const Variant &p_value) {
extension_property_values[p_property] = p_value;
if (openxr_layer_provider) {
openxr_layer_provider->set_extension_property_values(extension_property_values);
}
return true;
}
PackedStringArray OpenXRCompositionLayer::get_configuration_warnings() const {
PackedStringArray warnings = Node3D::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
warnings.push_back(RTR("OpenXR composition layers must have an XROrigin3D node as their parent."));
}
}
if (!get_transform().basis.is_orthonormal()) {
warnings.push_back(RTR("OpenXR composition layers must have orthonormalized transforms (ie. no scale or shearing)."));
}
if (enable_hole_punch && get_sort_order() >= 0) {
warnings.push_back(RTR("Hole punching won't work as expected unless the sort order is less than zero."));
}
return warnings;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,416 @@
/**************************************************************************/
/* openxr_hand.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "openxr_hand.h"
#include "../extensions/openxr_hand_tracking_extension.h"
#include "../openxr_api.h"
#include "scene/3d/skeleton_3d.h"
#include "servers/xr_server.h"
void OpenXRHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
ClassDB::bind_method(D_METHOD("set_skeleton_rig", "skeleton_rig"), &OpenXRHand::set_skeleton_rig);
ClassDB::bind_method(D_METHOD("get_skeleton_rig"), &OpenXRHand::get_skeleton_rig);
ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &OpenXRHand::set_bone_update);
ClassDB::bind_method(D_METHOD("get_bone_update"), &OpenXRHand::get_bone_update);
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
BIND_ENUM_CONSTANT(HAND_LEFT);
BIND_ENUM_CONSTANT(HAND_RIGHT);
BIND_ENUM_CONSTANT(HAND_MAX);
BIND_ENUM_CONSTANT(MOTION_RANGE_UNOBSTRUCTED);
BIND_ENUM_CONSTANT(MOTION_RANGE_CONFORM_TO_CONTROLLER);
BIND_ENUM_CONSTANT(MOTION_RANGE_MAX);
BIND_ENUM_CONSTANT(SKELETON_RIG_OPENXR);
BIND_ENUM_CONSTANT(SKELETON_RIG_HUMANOID);
BIND_ENUM_CONSTANT(SKELETON_RIG_MAX);
BIND_ENUM_CONSTANT(BONE_UPDATE_FULL);
BIND_ENUM_CONSTANT(BONE_UPDATE_ROTATION_ONLY);
BIND_ENUM_CONSTANT(BONE_UPDATE_MAX);
}
OpenXRHand::OpenXRHand() {
openxr_api = OpenXRAPI::get_singleton();
hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
}
void OpenXRHand::set_hand(Hands p_hand) {
ERR_FAIL_INDEX(p_hand, HAND_MAX);
hand = p_hand;
}
OpenXRHand::Hands OpenXRHand::get_hand() const {
return hand;
}
void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
hand_skeleton = p_hand_skeleton;
// TODO if inside tree call _get_bones()
}
void OpenXRHand::set_motion_range(MotionRange p_motion_range) {
ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
motion_range = p_motion_range;
_set_motion_range();
}
OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
return motion_range;
}
NodePath OpenXRHand::get_hand_skeleton() const {
return hand_skeleton;
}
void OpenXRHand::_set_motion_range() {
if (!hand_tracking_ext) {
return;
}
XrHandJointsMotionRangeEXT xr_motion_range;
switch (motion_range) {
case MOTION_RANGE_UNOBSTRUCTED:
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
break;
case MOTION_RANGE_CONFORM_TO_CONTROLLER:
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
break;
default:
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
break;
}
hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(hand), xr_motion_range);
}
void OpenXRHand::set_skeleton_rig(SkeletonRig p_skeleton_rig) {
ERR_FAIL_INDEX(p_skeleton_rig, SKELETON_RIG_MAX);
skeleton_rig = p_skeleton_rig;
}
OpenXRHand::SkeletonRig OpenXRHand::get_skeleton_rig() const {
return skeleton_rig;
}
void OpenXRHand::set_bone_update(BoneUpdate p_bone_update) {
ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX);
bone_update = p_bone_update;
}
OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
return bone_update;
}
Skeleton3D *OpenXRHand::get_skeleton() {
if (!has_node(hand_skeleton)) {
return nullptr;
}
Node *node = get_node(hand_skeleton);
if (!node) {
return nullptr;
}
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
return skeleton;
}
void OpenXRHand::_get_joint_data() {
// Table of bone names for different rig types.
static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = {
// SKELETON_RIG_OPENXR bone names.
{
"Palm",
"Wrist",
"Thumb_Metacarpal",
"Thumb_Proximal",
"Thumb_Distal",
"Thumb_Tip",
"Index_Metacarpal",
"Index_Proximal",
"Index_Intermediate",
"Index_Distal",
"Index_Tip",
"Middle_Metacarpal",
"Middle_Proximal",
"Middle_Intermediate",
"Middle_Distal",
"Middle_Tip",
"Ring_Metacarpal",
"Ring_Proximal",
"Ring_Intermediate",
"Ring_Distal",
"Ring_Tip",
"Little_Metacarpal",
"Little_Proximal",
"Little_Intermediate",
"Little_Distal",
"Little_Tip" },
// SKELETON_RIG_HUMANOID bone names.
{
"Palm",
"Hand",
"ThumbMetacarpal",
"ThumbProximal",
"ThumbDistal",
"ThumbTip",
"IndexMetacarpal",
"IndexProximal",
"IndexIntermediate",
"IndexDistal",
"IndexTip",
"MiddleMetacarpal",
"MiddleProximal",
"MiddleIntermediate",
"MiddleDistal",
"MiddleTip",
"RingMetacarpal",
"RingProximal",
"RingIntermediate",
"RingDistal",
"RingTip",
"LittleMetacarpal",
"LittleProximal",
"LittleIntermediate",
"LittleDistal",
"LittleTip" }
};
// Table of bone name formats for different rig types and left/right hands.
static const String bone_name_formats[SKELETON_RIG_MAX][2] = {
// SKELETON_RIG_OPENXR bone name format.
{ "<bone>_L", "<bone>_R" },
// SKELETON_RIG_HUMANOID bone name format.
{ "Left<bone>", "Right<bone>" }
};
// reset JIC
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
// Find the skeleton-bones associated with each OpenXR joint.
int bones[XR_HAND_JOINT_COUNT_EXT];
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
// Construct the expected bone name.
String bone_name = bone_name_formats[skeleton_rig][hand].replace("<bone>", bone_names[skeleton_rig][i]);
// Find the skeleton bone.
bones[i] = skeleton->find_bone(bone_name);
if (bones[i] == -1) {
print_line("Couldn't obtain bone for", bone_name);
}
}
// Assemble the OpenXR joint relationship to the available skeleton bones.
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
// Get the skeleton bone (skip if not found).
const int bone = bones[i];
if (bone == -1) {
continue;
}
// Find the parent skeleton-bone.
const int parent_bone = skeleton->get_bone_parent(bone);
if (parent_bone == -1) {
// If no parent skeleton-bone exists then drive this relative to palm joint.
joints[i].bone = bone;
joints[i].parent_joint = XR_HAND_JOINT_PALM_EXT;
continue;
}
// Find the OpenXR joint associated with the parent skeleton-bone.
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; ++j) {
if (bones[j] == parent_bone) {
// If a parent joint is found then drive this bone relative to it.
joints[i].bone = bone;
joints[i].parent_joint = j;
break;
}
}
}
}
void OpenXRHand::_update_skeleton() {
if (openxr_api == nullptr || !openxr_api->is_initialized()) {
return;
} else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
return;
}
Skeleton3D *skeleton = get_skeleton();
if (!skeleton) {
return;
}
// Table of bone adjustments for different rig types
static const Quaternion bone_adjustments[SKELETON_RIG_MAX] = {
// SKELETON_RIG_OPENXR bone adjustment. This is an identity quaternion
// because the incoming quaternions are already in OpenXR format.
Quaternion(),
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
Quaternion(0.0, -Math_SQRT12, Math_SQRT12, 0.0),
};
// we cache our transforms so we can quickly calculate local transforms
XRPose::TrackingConfidence confidences[XR_HAND_JOINT_COUNT_EXT];
Quaternion quaternions[XR_HAND_JOINT_COUNT_EXT];
Quaternion inv_quaternions[XR_HAND_JOINT_COUNT_EXT];
Vector3 positions[XR_HAND_JOINT_COUNT_EXT];
const Quaternion &rig_adjustment = bone_adjustments[skeleton_rig];
const OpenXRHandTrackingExtension::HandTracker *hand_tracker = hand_tracking_ext->get_hand_tracker(OpenXRHandTrackingExtension::HandTrackedHands(hand));
const float ws = XRServer::get_singleton()->get_world_scale();
if (hand_tracker->is_initialized && hand_tracker->locations.isActive) {
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_NONE;
quaternions[i] = Quaternion();
positions[i] = Vector3();
const XrHandJointLocationEXT &location = hand_tracker->joint_locations[i];
const XrPosef &pose = location.pose;
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
quaternions[i] = Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * rig_adjustment;
inv_quaternions[i] = quaternions[i].inverse();
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
positions[i] = Vector3(pose.position.x * ws, pose.position.y * ws, pose.position.z * ws);
// TODO get inverse of position, we'll do this later. For now we're ignoring bone positions which generally works better anyway
} else {
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_LOW;
}
}
}
}
if (confidences[XR_HAND_JOINT_PALM_EXT] != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
// Iterate over all the OpenXR joints.
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
// Get the skeleton bone (skip if none).
const int bone = joints[joint].bone;
if (bone == -1) {
continue;
}
// Calculate the relative relationship to the parent bone joint.
const int parent_joint = joints[joint].parent_joint;
const Quaternion q = inv_quaternions[parent_joint] * quaternions[joint];
const Vector3 p = inv_quaternions[parent_joint].xform(positions[joint] - positions[parent_joint]);
// Update the bone position if enabled by update mode.
if (bone_update == BONE_UPDATE_FULL) {
skeleton->set_bone_pose_position(joints[joint].bone, p);
}
// Always update the bone rotation.
skeleton->set_bone_pose_rotation(joints[joint].bone, q);
}
// Transform the OpenXRHand to the skeleton pose.
Transform3D t;
t.basis = Basis(quaternions[XR_HAND_JOINT_PALM_EXT]);
t.origin = positions[XR_HAND_JOINT_PALM_EXT];
set_transform(t);
// show it
set_visible(true);
} else {
// hide it
set_visible(false);
}
} else {
// hide it
set_visible(false);
}
}
void OpenXRHand::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_get_joint_data();
set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
set_process_internal(false);
// reset
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
_update_skeleton();
} break;
default: {
} break;
}
}

View file

@ -0,0 +1,122 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef OPENXR_HAND_H
#define OPENXR_HAND_H
#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)
#endif // OPENXR_HAND_H