2024-03-25 08:07:28 -05:00
/**************************************************************************/
/* 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"
2025-08-07 15:50:39 -05:00
# include "../extensions/openxr_composition_layer_extension.h"
2024-03-25 08:07:28 -05:00
# include "../openxr_api.h"
# include "../openxr_interface.h"
# include "scene/3d/mesh_instance_3d.h"
2025-02-24 20:37:51 -03:00
# include "scene/3d/xr/xr_nodes.h"
2024-03-25 08:07:28 -05:00
# include "scene/main/viewport.h"
2024-08-26 17:02:28 -05:00
# include "platform/android/api/java_class_wrapper.h"
2024-05-09 14:05:20 -05:00
Vector < OpenXRCompositionLayer * > OpenXRCompositionLayer : : composition_layer_nodes ;
2024-03-25 08:07:28 -05:00
2024-05-02 16:49:53 -05:00
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 "
" \t ALBEDO = vec3(0.0, 0.0, 0.0); \n "
" } \n " ;
2025-08-07 15:50:39 -05:00
OpenXRCompositionLayer : : OpenXRCompositionLayer ( ) {
2024-03-25 08:07:28 -05:00
openxr_api = OpenXRAPI : : get_singleton ( ) ;
composition_layer_extension = OpenXRCompositionLayerExtension : : get_singleton ( ) ;
2024-10-23 14:30:56 -05:00
if ( openxr_api ) {
openxr_session_running = openxr_api - > is_running ( ) ;
}
2024-03-25 08:07:28 -05:00
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 ) ) ;
}
2025-08-07 15:50:39 -05:00
XRServer : : get_singleton ( ) - > connect ( " reference_frame_changed " , callable_mp ( this , & OpenXRCompositionLayer : : update_transform ) ) ;
2024-03-25 08:07:28 -05:00
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 ) ) ;
}
2024-05-09 14:05:20 -05:00
composition_layer_nodes . erase ( this ) ;
2024-03-25 08:07:28 -05:00
2025-08-07 15:50:39 -05:00
if ( composition_layer_extension & & composition_layer . is_valid ( ) ) {
composition_layer_extension - > composition_layer_free ( composition_layer ) ;
2024-03-25 08:07:28 -05:00
}
}
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 ) ;
2024-08-26 17:02:28 -05:00
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 ) ;
2024-05-02 16:49:53 -05:00
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 ) ;
2024-03-25 08:07:28 -05:00
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 ) ;
2024-08-26 17:02:28 -05:00
ClassDB : : bind_method ( D_METHOD ( " get_android_surface " ) , & OpenXRCompositionLayer : : get_android_surface ) ;
2024-03-25 08:07:28 -05:00
ClassDB : : bind_method ( D_METHOD ( " is_natively_supported " ) , & OpenXRCompositionLayer : : is_natively_supported ) ;
2025-07-15 22:32:51 +02:00
ClassDB : : bind_method ( D_METHOD ( " is_protected_content " ) , & OpenXRCompositionLayer : : is_protected_content ) ;
ClassDB : : bind_method ( D_METHOD ( " set_protected_content " , " protected_content " ) , & OpenXRCompositionLayer : : set_protected_content ) ;
2025-01-20 13:09:20 -06:00
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 ) ;
2024-04-05 19:54:07 -05:00
ClassDB : : bind_method ( D_METHOD ( " intersects_ray " , " origin " , " direction " ) , & OpenXRCompositionLayer : : intersects_ray ) ;
2024-03-25 08:07:28 -05:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " layer_viewport " , PROPERTY_HINT_NODE_TYPE , " SubViewport " ) , " set_layer_viewport " , " get_layer_viewport " ) ;
2024-08-26 17:02:28 -05:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_android_surface " , PROPERTY_HINT_NONE , " " ) , " set_use_android_surface " , " get_use_android_surface " ) ;
2025-07-15 22:32:51 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " protected_content " , PROPERTY_HINT_NONE , " " ) , " set_protected_content " , " is_protected_content " ) ;
2024-08-26 17:02:28 -05:00
ADD_PROPERTY ( PropertyInfo ( Variant : : VECTOR2I , " android_surface_size " , PROPERTY_HINT_NONE , " " ) , " set_android_surface_size " , " get_android_surface_size " ) ;
2024-03-25 08:07:28 -05:00
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 " ) ;
2024-05-02 16:49:53 -05:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " enable_hole_punch " , PROPERTY_HINT_NONE , " " ) , " set_enable_hole_punch " , " get_enable_hole_punch " ) ;
2025-01-20 13:09:20 -06:00
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 ) ;
2024-05-02 16:49:53 -05:00
}
bool OpenXRCompositionLayer : : _should_use_fallback_node ( ) {
2025-01-10 13:40:27 -08:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) | | openxr_api = = nullptr ) {
2024-05-02 16:49:53 -05:00
return true ;
} else if ( openxr_session_running ) {
2024-08-26 17:02:28 -05:00
return enable_hole_punch | | ( ! is_natively_supported ( ) & & ! use_android_surface ) ;
2024-05-02 16:49:53 -05:00
}
return false ;
2024-03-25 08:07:28 -05:00
}
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 ;
}
2024-05-02 16:49:53 -05:00
void OpenXRCompositionLayer : : _remove_fallback_node ( ) {
ERR_FAIL_COND ( fallback ! = nullptr ) ;
remove_child ( fallback ) ;
fallback - > queue_free ( ) ;
fallback = nullptr ;
}
2025-08-07 15:50:39 -05:00
void OpenXRCompositionLayer : : _setup_composition_layer ( ) {
2024-08-26 17:02:28 -05:00
if ( use_android_surface | | layer_viewport ) {
if ( composition_layer_extension ) {
2025-08-07 15:50:39 -05:00
composition_layer_extension - > composition_layer_register ( composition_layer ) ;
2025-01-08 14:55:03 -08:00
registered = true ;
2024-08-26 17:02:28 -05:00
2025-08-07 15:50:39 -05:00
// 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).
composition_layer_extension - > composition_layer_set_viewport ( composition_layer , layer_viewport - > get_viewport_rid ( ) , layer_viewport - > get_size ( ) ) ;
}
2024-08-26 17:02:28 -05:00
}
}
}
2025-08-07 15:50:39 -05:00
void OpenXRCompositionLayer : : _clear_composition_layer ( ) {
2024-08-26 17:02:28 -05:00
if ( composition_layer_extension ) {
2025-08-07 15:50:39 -05:00
composition_layer_extension - > composition_layer_unregister ( composition_layer ) ;
2025-01-08 14:55:03 -08:00
registered = false ;
2024-08-26 17:02:28 -05:00
2025-08-07 15:50:39 -05:00
// 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.
composition_layer_extension - > composition_layer_set_viewport ( composition_layer , RID ( ) , Size2i ( ) ) ;
}
2024-08-26 17:02:28 -05:00
}
}
2024-03-25 08:07:28 -05:00
void OpenXRCompositionLayer : : _on_openxr_session_begun ( ) {
2024-05-02 16:49:53 -05:00
openxr_session_running = true ;
2025-07-22 15:03:12 -07:00
if ( _should_register ( ) ) {
2025-08-07 15:50:39 -05:00
_setup_composition_layer ( ) ;
2024-03-25 08:07:28 -05:00
}
2024-05-02 16:49:53 -05:00
if ( ! fallback & & _should_use_fallback_node ( ) ) {
_create_fallback_node ( ) ;
}
2024-03-25 08:07:28 -05:00
}
void OpenXRCompositionLayer : : _on_openxr_session_stopping ( ) {
2024-05-02 16:49:53 -05:00
openxr_session_running = false ;
if ( fallback & & ! _should_use_fallback_node ( ) ) {
_remove_fallback_node ( ) ;
2024-03-25 08:07:28 -05:00
}
2025-08-07 15:50:39 -05:00
_clear_composition_layer ( ) ;
}
void OpenXRCompositionLayer : : update_transform ( ) {
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_transform ( composition_layer , get_transform ( ) ) ;
}
2024-03-25 08:07:28 -05:00
}
void OpenXRCompositionLayer : : update_fallback_mesh ( ) {
should_update_fallback_mesh = true ;
}
2025-07-22 15:03:12 -07:00
bool OpenXRCompositionLayer : : _should_register ( ) {
return ! registered & & openxr_session_running & & is_inside_tree ( ) & & is_visible ( ) & & is_natively_supported ( ) ;
}
2024-05-09 14:05:20 -05:00
bool OpenXRCompositionLayer : : is_viewport_in_use ( SubViewport * p_viewport ) {
2024-08-26 17:02:28 -05:00
ERR_FAIL_NULL_V ( p_viewport , false ) ;
2024-05-09 14:05:20 -05:00
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 ;
}
2024-03-25 08:07:28 -05:00
void OpenXRCompositionLayer : : set_layer_viewport ( SubViewport * p_viewport ) {
if ( layer_viewport = = p_viewport ) {
return ;
}
2024-05-21 07:52:40 -05:00
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. " ) ) ;
}
2024-08-26 17:02:28 -05:00
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. " ) ) ;
}
2024-03-25 08:07:28 -05:00
layer_viewport = p_viewport ;
2025-07-22 15:03:12 -07:00
if ( _should_register ( ) ) {
2025-08-07 15:50:39 -05:00
_setup_composition_layer ( ) ;
2025-01-08 14:55:03 -08:00
}
2024-03-25 08:07:28 -05:00
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 ( ) ;
2025-08-07 15:50:39 -05:00
} else if ( openxr_session_running & & composition_layer_extension & & is_visible ( ) & & is_inside_tree ( ) ) {
2024-03-25 08:07:28 -05:00
if ( layer_viewport ) {
2025-08-07 15:50:39 -05:00
composition_layer_extension - > composition_layer_set_viewport ( composition_layer , layer_viewport - > get_viewport_rid ( ) , layer_viewport - > get_size ( ) ) ;
2024-03-25 08:07:28 -05:00
} else {
2025-08-07 15:50:39 -05:00
composition_layer_extension - > composition_layer_set_viewport ( composition_layer , RID ( ) , Size2i ( ) ) ;
2024-03-25 08:07:28 -05:00
}
}
}
2024-08-26 17:02:28 -05:00
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 ) {
2025-07-22 15:03:12 -07:00
// It's possible that the layer provider is unregistered here (if previously invisible)
2024-08-26 17:02:28 -05:00
set_layer_viewport ( nullptr ) ;
2025-08-07 15:50:39 -05:00
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_use_android_surface ( composition_layer , true , android_surface_size ) ;
}
2025-07-22 15:03:12 -07:00
// ...and it may not be set up above because of viewport = null, android surface is false, so set it up again:
if ( _should_register ( ) ) {
2025-08-07 15:50:39 -05:00
_setup_composition_layer ( ) ;
2025-07-22 15:03:12 -07:00
}
2024-08-26 17:02:28 -05:00
} else {
2025-08-07 15:50:39 -05:00
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_use_android_surface ( composition_layer , false , Size2i ( ) ) ;
}
2024-08-26 17:02:28 -05:00
}
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 ;
2025-08-07 15:50:39 -05:00
if ( use_android_surface & & composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_use_android_surface ( composition_layer , true , android_surface_size ) ;
2024-08-26 17:02:28 -05:00
}
}
Size2i OpenXRCompositionLayer : : get_android_surface_size ( ) const {
return android_surface_size ;
}
2024-03-25 08:07:28 -05:00
SubViewport * OpenXRCompositionLayer : : get_layer_viewport ( ) const {
return layer_viewport ;
}
2024-05-02 16:49:53 -05:00
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 ;
}
2024-03-25 08:07:28 -05:00
void OpenXRCompositionLayer : : set_sort_order ( int p_order ) {
2025-08-07 15:50:39 -05:00
sort_order = p_order ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_sort_order ( composition_layer , p_order ) ;
}
2024-08-26 17:02:28 -05:00
update_configuration_warnings ( ) ;
2024-03-25 08:07:28 -05:00
}
int OpenXRCompositionLayer : : get_sort_order ( ) const {
2025-08-07 15:50:39 -05:00
return sort_order ;
2024-03-25 08:07:28 -05:00
}
void OpenXRCompositionLayer : : set_alpha_blend ( bool p_alpha_blend ) {
2025-08-07 15:50:39 -05:00
alpha_blend = p_alpha_blend ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_alpha_blend ( composition_layer , p_alpha_blend ) ;
}
2024-08-26 17:02:28 -05:00
if ( fallback ) {
_reset_fallback_material ( ) ;
2024-03-25 08:07:28 -05:00
}
}
bool OpenXRCompositionLayer : : get_alpha_blend ( ) const {
2025-08-07 15:50:39 -05:00
return alpha_blend ;
2024-03-25 08:07:28 -05:00
}
bool OpenXRCompositionLayer : : is_natively_supported ( ) const {
2025-01-10 13:40:27 -08:00
if ( composition_layer_extension & & openxr_api ) {
2025-08-07 15:50:39 -05:00
return composition_layer_extension - > is_available ( _get_openxr_type ( ) ) ;
2024-03-25 08:07:28 -05:00
}
return false ;
}
2025-07-15 22:32:51 +02:00
void OpenXRCompositionLayer : : set_protected_content ( bool p_protected_content ) {
2025-08-07 15:50:39 -05:00
if ( protected_content = = p_protected_content ) {
return ;
}
protected_content = p_protected_content ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_protected_content ( composition_layer , p_protected_content ) ;
}
2025-07-15 22:32:51 +02:00
}
bool OpenXRCompositionLayer : : is_protected_content ( ) const {
2025-08-07 15:50:39 -05:00
return protected_content ;
2025-07-15 22:32:51 +02:00
}
2025-01-20 13:09:20 -06:00
void OpenXRCompositionLayer : : set_min_filter ( Filter p_mode ) {
2025-08-07 15:50:39 -05:00
if ( min_filter = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
min_filter = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_min_filter ( composition_layer , ( OpenXRCompositionLayerExtension : : Filter ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Filter OpenXRCompositionLayer : : get_min_filter ( ) const {
2025-08-07 15:50:39 -05:00
return min_filter ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_mag_filter ( Filter p_mode ) {
2025-08-07 15:50:39 -05:00
if ( mag_filter = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
mag_filter = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_mag_filter ( composition_layer , ( OpenXRCompositionLayerExtension : : Filter ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Filter OpenXRCompositionLayer : : get_mag_filter ( ) const {
2025-08-07 15:50:39 -05:00
return mag_filter ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_mipmap_mode ( MipmapMode p_mode ) {
2025-08-07 15:50:39 -05:00
if ( mipmap_mode = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
mipmap_mode = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_mipmap_mode ( composition_layer , ( OpenXRCompositionLayerExtension : : MipmapMode ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : MipmapMode OpenXRCompositionLayer : : get_mipmap_mode ( ) const {
2025-08-07 15:50:39 -05:00
return mipmap_mode ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_horizontal_wrap ( Wrap p_mode ) {
2025-08-07 15:50:39 -05:00
if ( horizontal_wrap = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
horizontal_wrap = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_horizontal_wrap ( composition_layer , ( OpenXRCompositionLayerExtension : : Wrap ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Wrap OpenXRCompositionLayer : : get_horizontal_wrap ( ) const {
2025-08-07 15:50:39 -05:00
return horizontal_wrap ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_vertical_wrap ( Wrap p_mode ) {
2025-08-07 15:50:39 -05:00
if ( vertical_wrap = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
vertical_wrap = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_vertical_wrap ( composition_layer , ( OpenXRCompositionLayerExtension : : Wrap ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Wrap OpenXRCompositionLayer : : get_vertical_wrap ( ) const {
2025-08-07 15:50:39 -05:00
return vertical_wrap ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_red_swizzle ( Swizzle p_mode ) {
2025-08-07 15:50:39 -05:00
if ( red_swizzle = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
red_swizzle = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_red_swizzle ( composition_layer , ( OpenXRCompositionLayerExtension : : Swizzle ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Swizzle OpenXRCompositionLayer : : get_red_swizzle ( ) const {
2025-08-07 15:50:39 -05:00
return red_swizzle ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_green_swizzle ( Swizzle p_mode ) {
2025-08-07 15:50:39 -05:00
if ( green_swizzle = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
green_swizzle = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_green_swizzle ( composition_layer , ( OpenXRCompositionLayerExtension : : Swizzle ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Swizzle OpenXRCompositionLayer : : get_green_swizzle ( ) const {
2025-08-07 15:50:39 -05:00
return green_swizzle ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_blue_swizzle ( Swizzle p_mode ) {
2025-08-07 15:50:39 -05:00
if ( blue_swizzle = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
blue_swizzle = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_blue_swizzle ( composition_layer , ( OpenXRCompositionLayerExtension : : Swizzle ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Swizzle OpenXRCompositionLayer : : get_blue_swizzle ( ) const {
2025-08-07 15:50:39 -05:00
return blue_swizzle ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_alpha_swizzle ( Swizzle p_mode ) {
2025-08-07 15:50:39 -05:00
if ( alpha_swizzle = = p_mode ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
alpha_swizzle = p_mode ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_alpha_swizzle ( composition_layer , ( OpenXRCompositionLayerExtension : : Swizzle ) p_mode ) ;
}
2025-01-20 13:09:20 -06:00
}
OpenXRCompositionLayer : : Swizzle OpenXRCompositionLayer : : get_alpha_swizzle ( ) const {
2025-08-07 15:50:39 -05:00
return alpha_swizzle ;
2025-01-20 13:09:20 -06:00
}
void OpenXRCompositionLayer : : set_max_anisotropy ( float p_value ) {
2025-08-07 15:50:39 -05:00
if ( max_anisotropy = = p_value ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
max_anisotropy = p_value ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_max_anisotropy ( composition_layer , p_value ) ;
}
2025-01-20 13:09:20 -06:00
}
float OpenXRCompositionLayer : : get_max_anisotropy ( ) const {
2025-08-07 15:50:39 -05:00
return max_anisotropy ;
2025-01-20 13:09:20 -06:00
}
2025-08-07 15:50:39 -05:00
void OpenXRCompositionLayer : : set_border_color ( const Color & p_color ) {
if ( border_color = = p_color ) {
2025-01-20 13:09:20 -06:00
return ;
}
2025-08-07 15:50:39 -05:00
border_color = p_color ;
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_border_color ( composition_layer , p_color ) ;
}
2025-01-20 13:09:20 -06:00
}
Color OpenXRCompositionLayer : : get_border_color ( ) const {
2025-08-07 15:50:39 -05:00
return border_color ;
2025-01-20 13:09:20 -06:00
}
2024-08-26 17:02:28 -05:00
Ref < JavaObject > OpenXRCompositionLayer : : get_android_surface ( ) {
2025-08-07 15:50:39 -05:00
if ( composition_layer_extension ) {
return composition_layer_extension - > composition_layer_get_android_surface ( composition_layer ) ;
}
return Ref < JavaObject > ( ) ;
2024-08-26 17:02:28 -05:00
}
2024-04-05 19:54:07 -05:00
Vector2 OpenXRCompositionLayer : : intersects_ray ( const Vector3 & p_origin , const Vector3 & p_direction ) const {
return Vector2 ( - 1.0 , - 1.0 ) ;
}
2024-03-25 08:07:28 -05:00
void OpenXRCompositionLayer : : _reset_fallback_material ( ) {
ERR_FAIL_NULL ( fallback ) ;
if ( fallback - > get_mesh ( ) . is_null ( ) ) {
return ;
}
2024-05-02 16:49:53 -05:00
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 ) {
2024-03-25 08:07:28 -05:00
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 ) ;
}
2024-05-02 16:49:53 -05:00
material - > set_flag ( StandardMaterial3D : : FLAG_DISABLE_DEPTH_TEST , ! enable_hole_punch ) ;
2024-03-25 08:07:28 -05:00
material - > set_transparency ( get_alpha_blend ( ) ? StandardMaterial3D : : TRANSPARENCY_ALPHA : StandardMaterial3D : : TRANSPARENCY_DISABLED ) ;
2024-12-16 08:44:49 -06:00
material - > set_texture ( StandardMaterial3D : : TEXTURE_ALBEDO , layer_viewport - > get_texture ( ) ) ;
2024-03-25 08:07:28 -05:00
} else {
fallback - > set_surface_override_material ( 0 , Ref < Material > ( ) ) ;
}
}
void OpenXRCompositionLayer : : _notification ( int p_what ) {
switch ( p_what ) {
2024-04-04 15:04:07 -05:00
case NOTIFICATION_POSTINITIALIZE : {
2024-05-09 14:05:20 -05:00
composition_layer_nodes . push_back ( this ) ;
2024-08-26 17:02:28 -05:00
for ( OpenXRExtensionWrapper * extension : OpenXRAPI : : get_registered_extension_wrappers ( ) ) {
extension_property_values . merge ( extension - > get_viewport_composition_layer_extension_property_defaults ( ) ) ;
2024-04-04 15:04:07 -05:00
}
2025-08-07 15:50:39 -05:00
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_extension_property_values ( composition_layer , extension_property_values ) ;
}
2024-04-04 15:04:07 -05:00
} break ;
2024-03-25 08:07:28 -05:00
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 : {
2024-12-23 08:01:19 -06:00
if ( is_natively_supported ( ) & & openxr_session_running & & is_inside_tree ( ) ) {
2024-08-26 17:02:28 -05:00
if ( is_visible ( ) ) {
2025-08-07 15:50:39 -05:00
_setup_composition_layer ( ) ;
2024-03-25 08:07:28 -05:00
} else {
2025-08-07 15:50:39 -05:00
_clear_composition_layer ( ) ;
2024-03-25 08:07:28 -05:00
}
}
update_configuration_warnings ( ) ;
} break ;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED : {
2025-08-07 15:50:39 -05:00
update_transform ( ) ;
2024-03-25 08:07:28 -05:00
update_configuration_warnings ( ) ;
} break ;
case NOTIFICATION_ENTER_TREE : {
2024-08-26 17:02:28 -05:00
if ( layer_viewport & & is_viewport_in_use ( layer_viewport ) ) {
2025-08-07 15:50:39 -05:00
_clear_composition_layer ( ) ;
2024-08-26 17:02:28 -05:00
} else if ( openxr_session_running & & is_visible ( ) ) {
2025-08-07 15:50:39 -05:00
_setup_composition_layer ( ) ;
2024-03-25 08:07:28 -05:00
}
} break ;
case NOTIFICATION_EXIT_TREE : {
2024-08-26 17:02:28 -05:00
// This will clean up existing resources.
2025-08-07 15:50:39 -05:00
_clear_composition_layer ( ) ;
2024-03-25 08:07:28 -05:00
} break ;
}
}
2024-04-04 15:04:07 -05:00
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 ;
2024-12-05 17:56:08 +01:00
if ( ! String ( prop_name ) . contains_char ( ' / ' ) ) {
2024-04-04 15:04:07 -05:00
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 ] ;
2025-04-18 21:01:55 +02:00
return true ;
2024-04-04 15:04:07 -05:00
}
2025-04-18 21:01:55 +02:00
return false ;
2024-04-04 15:04:07 -05:00
}
bool OpenXRCompositionLayer : : _set ( const StringName & p_property , const Variant & p_value ) {
extension_property_values [ p_property ] = p_value ;
2025-08-07 15:50:39 -05:00
if ( composition_layer_extension ) {
composition_layer_extension - > composition_layer_set_extension_property_values ( composition_layer , extension_property_values ) ;
}
2024-04-04 15:04:07 -05:00
return true ;
}
2024-08-26 17:02:28 -05:00
void OpenXRCompositionLayer : : _validate_property ( PropertyInfo & p_property ) const {
2025-06-12 12:54:19 +08:00
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
return ;
}
2024-08-26 17:02:28 -05:00
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 ;
}
}
}
2024-03-25 08:07:28 -05:00
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). " ) ) ;
}
2024-05-02 16:49:53 -05:00
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. " ) ) ;
}
2024-03-25 08:07:28 -05:00
return warnings ;
}