Path: blob/master/modules/openxr/scene/openxr_render_model_manager.cpp
10278 views
/**************************************************************************/1/* openxr_render_model_manager.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "openxr_render_model_manager.h"3132#include "../extensions/openxr_render_model_extension.h"33#include "../openxr_api.h"34#include "core/config/project_settings.h"35#include "scene/3d/xr/xr_nodes.h"36#include "servers/xr_server.h"3738void OpenXRRenderModelManager::_bind_methods() {39ClassDB::bind_method(D_METHOD("get_tracker"), &OpenXRRenderModelManager::get_tracker);40ClassDB::bind_method(D_METHOD("set_tracker", "tracker"), &OpenXRRenderModelManager::set_tracker);41ADD_PROPERTY(PropertyInfo(Variant::INT, "tracker", PROPERTY_HINT_ENUM, "Any,None set,Left Hand,Right Hand"), "set_tracker", "get_tracker");4243ClassDB::bind_method(D_METHOD("get_make_local_to_pose"), &OpenXRRenderModelManager::get_make_local_to_pose);44ClassDB::bind_method(D_METHOD("set_make_local_to_pose", "make_local_to_pose"), &OpenXRRenderModelManager::set_make_local_to_pose);45ADD_PROPERTY(PropertyInfo(Variant::STRING, "make_local_to_pose", PROPERTY_HINT_ENUM_SUGGESTION, "aim,grip"), "set_make_local_to_pose", "get_make_local_to_pose");4647ADD_SIGNAL(MethodInfo("render_model_added", PropertyInfo(Variant::OBJECT, "render_model", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRRenderModel")));48ADD_SIGNAL(MethodInfo("render_model_removed", PropertyInfo(Variant::OBJECT, "render_model", PROPERTY_HINT_RESOURCE_TYPE, "OpenXRRenderModel")));4950BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_ANY);51BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_NONE_SET);52BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_LEFT_HAND);53BIND_ENUM_CONSTANT(RENDER_MODEL_TRACKER_RIGHT_HAND);54}5556bool OpenXRRenderModelManager::_has_filters() {57return tracker != 0;58}5960void OpenXRRenderModelManager::_update_models() {61OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();62ERR_FAIL_NULL(render_model_extension);6364// Make a copy of our current models.65HashMap<RID, Node3D *> org_render_models = render_models;6667// Loop through our interaction data so we add new entries.68TypedArray<RID> render_model_rids = render_model_extension->render_model_get_all();69for (const RID rid : render_model_rids) {70bool filter = false;7172if (tracker != 0) {73XrPath model_path = render_model_extension->render_model_get_top_level_path(rid);74if (model_path != xr_path) {75// ignore this.76filter = true;77}78}7980if (!filter) {81if (render_models.has(rid)) {82org_render_models.erase(rid);83} else {84// Create our container node before adding our first render model.85if (container == nullptr) {86container = memnew(Node3D);87add_child(container);88}8990OpenXRRenderModel *render_model = memnew(OpenXRRenderModel);91render_model->set_render_model(rid);92container->add_child(render_model);93render_models[rid] = render_model;9495emit_signal(SNAME("render_model_added"), render_model);96}97}98}99100// Remove models we no longer need.101for (const KeyValue<RID, Node3D *> &e : org_render_models) {102// We sent this just before removing.103emit_signal(SNAME("render_model_removed"), e.value);104105if (container) {106container->remove_child(e.value);107}108e.value->queue_free();109render_models.erase(e.key);110}111112is_dirty = false;113}114115void OpenXRRenderModelManager::_on_render_model_added(RID p_render_model) {116if (_has_filters()) {117// We'll update this in internal process.118is_dirty = true;119} else {120// No filters? Do this right away.121_update_models();122}123}124125void OpenXRRenderModelManager::_on_render_model_removed(RID p_render_model) {126if (_has_filters()) {127// We'll update this in internal process.128is_dirty = true;129} else {130// No filters? Do this right away.131_update_models();132}133}134135void OpenXRRenderModelManager::_on_render_model_top_level_path_changed(RID p_path) {136if (_has_filters()) {137// We'll update this in internal process.138is_dirty = true;139}140}141142void OpenXRRenderModelManager::_notification(int p_what) {143// Do not run in editor!144if (Engine::get_singleton()->is_editor_hint()) {145return;146}147148OpenXRRenderModelExtension *render_model_extension = OpenXRRenderModelExtension::get_singleton();149ERR_FAIL_NULL(render_model_extension);150if (!render_model_extension->is_active()) {151return;152}153154switch (p_what) {155case NOTIFICATION_ENTER_TREE: {156_update_models();157158render_model_extension->connect(SNAME("render_model_added"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_added));159render_model_extension->connect(SNAME("render_model_removed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_removed));160render_model_extension->connect(SNAME("render_model_top_level_path_changed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_top_level_path_changed));161162if (_has_filters()) {163set_process_internal(true);164}165} break;166case NOTIFICATION_EXIT_TREE: {167render_model_extension->disconnect(SNAME("render_model_added"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_added));168render_model_extension->disconnect(SNAME("render_model_removed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_removed));169render_model_extension->disconnect(SNAME("render_model_top_level_path_changed"), callable_mp(this, &OpenXRRenderModelManager::_on_render_model_top_level_path_changed));170171set_process_internal(false);172is_dirty = false;173} break;174case NOTIFICATION_INTERNAL_PROCESS: {175if (is_dirty) {176_update_models();177}178179if (positional_tracker.is_valid() && !make_local_to_pose.is_empty() && container) {180Ref<XRPose> pose = positional_tracker->get_pose(make_local_to_pose);181if (pose.is_valid()) {182container->set_transform(pose->get_adjusted_transform().affine_inverse());183} else {184container->set_transform(Transform3D());185}186}187188if (!_has_filters()) {189// No need to keep calling this.190set_process_internal(false);191}192}193}194}195196PackedStringArray OpenXRRenderModelManager::get_configuration_warnings() const {197PackedStringArray warnings;198199XROrigin3D *parent = nullptr;200if (tracker == 0 || tracker == 1) {201if (!make_local_to_pose.is_empty()) {202warnings.push_back("Must specify a tracker to make node local to pose.");203}204205parent = Object::cast_to<XROrigin3D>(get_parent());206} else {207Node *node = get_parent();208while (!parent && node) {209parent = Object::cast_to<XROrigin3D>(node);210211node = node->get_parent();212}213}214if (!parent) {215warnings.push_back("This node must be a child of an XROrigin3D node!");216}217218if (!GLOBAL_GET("xr/openxr/extensions/render_model")) {219warnings.push_back("The render model extension is not enabled in project settings!");220}221222return warnings;223}224225void OpenXRRenderModelManager::set_tracker(RenderModelTracker p_tracker) {226if (tracker != p_tracker) {227tracker = p_tracker;228is_dirty = true;229230if (tracker == RENDER_MODEL_TRACKER_ANY || tracker == RENDER_MODEL_TRACKER_NONE_SET) {231xr_path = XR_NULL_PATH;232} else if (!Engine::get_singleton()->is_editor_hint()) {233XRServer *xr_server = XRServer::get_singleton();234OpenXRAPI *openxr_api = OpenXRAPI::get_singleton();235if (openxr_api && xr_server) {236String toplevel_path;237String tracker_name;238if (tracker == RENDER_MODEL_TRACKER_LEFT_HAND) {239tracker_name = "left_hand";240toplevel_path = "/user/hand/left";241} else if (tracker == RENDER_MODEL_TRACKER_RIGHT_HAND) {242tracker_name = "right_hand";243toplevel_path = "/user/hand/right";244} else {245ERR_FAIL_MSG("Unsupported tracker value set.");246}247248positional_tracker = xr_server->get_tracker(tracker_name);249if (positional_tracker.is_null()) {250WARN_PRINT("OpenXR: Can't find tracker " + tracker_name);251}252253xr_path = openxr_api->get_xr_path(toplevel_path);254if (xr_path == XR_NULL_PATH) {255WARN_PRINT("OpenXR: Can't find path for " + toplevel_path);256}257}258}259260// Even if we now no longer have filters, we must update at least once.261set_process_internal(true);262}263}264265OpenXRRenderModelManager::RenderModelTracker OpenXRRenderModelManager::get_tracker() const {266return tracker;267}268269void OpenXRRenderModelManager::set_make_local_to_pose(const String &p_action) {270if (make_local_to_pose != p_action) {271make_local_to_pose = p_action;272273if (container) {274// Reset just in case. It'll be set to the correct transform275// in our process if required.276container->set_transform(Transform3D());277}278}279}280281String OpenXRRenderModelManager::get_make_local_to_pose() const {282return make_local_to_pose;283}284285286