diff options
22 files changed, 76 insertions, 43 deletions
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 7b4131b3a3..75fc0c8c35 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -333,18 +333,14 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c return; } - Size2i win_size = DisplayServer::get_singleton()->screen_get_size(); + Size2i win_size = DisplayServer::get_singleton()->window_get_size(); glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, win_size.width, win_size.height); - glDisable(GL_BLEND); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); glDepthMask(GL_FALSE); - if (false) { - // if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { - glClearColor(0.0, 0.0, 0.0, 0.0); - } else { - glClearColor(p_color.r, p_color.g, p_color.b, 1.0); - } + glClearColor(p_color.r, p_color.g, p_color.b, 1.0); glClear(GL_COLOR_BUFFER_BIT); RID texture = texture_storage->texture_allocate(); @@ -370,14 +366,24 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); } + // Flip Y. + screenrect.position.y = win_size.y - screenrect.position.y; + screenrect.size.y = -screenrect.size.y; + + // Normalize texture coordinates to window size. + screenrect.position /= win_size; + screenrect.size /= win_size; + GLES3::Texture *t = texture_storage->get_texture(texture); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 1); + t->gl_set_filter(p_use_filter ? RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR : RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, t->tex_id); + copy_effects->copy_to_rect(screenrect); glBindTexture(GL_TEXTURE_2D, 0); - texture_storage->texture_free(texture); - end_frame(true); + + texture_storage->texture_free(texture); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index ae5515e249..2b44043faf 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1739,7 +1739,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ render_data.reflection_probes = ∅ } - bool reverse_cull = false; + bool reverse_cull = render_data.cam_transform.basis.determinant() < 0; /////////// // Fill Light lists here diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 5bbbc7b91b..9b9efb0ba2 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -528,7 +528,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); - if (!skeleton || skeleton->size == 0) { + if (!skeleton || skeleton->size == 0 || mesh->skeleton_aabb_version == skeleton->version) { return mesh->aabb; } @@ -622,6 +622,8 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } + mesh->aabb = aabb; + mesh->skeleton_aabb_version = skeleton->version; return aabb; } diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 0f30814928..0b6b861b5b 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -126,6 +126,7 @@ struct Mesh { AABB aabb; AABB custom_aabb; + uint64_t skeleton_aabb_version = 0; Vector<RID> material_cache; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a5397a8e6a..2df14aef6c 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2597,7 +2597,7 @@ bool EditorInspector::_is_property_disabled_by_feature_profile(const StringName return false; } -void EditorInspector::update_tree() { +void EditorInspector::_update_tree() { //to update properly if all is refreshed StringName current_selected = property_selected; int current_focusable = -1; @@ -3316,6 +3316,10 @@ void EditorInspector::update_tree() { } } +void EditorInspector::update_tree() { + update_tree_pending = true; +} + void EditorInspector::update_property(const String &p_prop) { if (!editor_property_map.has(p_prop)) { return; @@ -3910,10 +3914,9 @@ void EditorInspector::_notification(int p_what) { changing++; if (update_tree_pending) { - update_tree(); update_tree_pending = false; pending.clear(); - + _update_tree(); } else { while (pending.size()) { StringName prop = *pending.begin(); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 41651494ce..56d0a55319 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -540,6 +540,8 @@ class EditorInspector : public ScrollContainer { void _show_add_meta_dialog(); void _check_meta_name(const String &p_name); + void _update_tree(); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/main/main.cpp b/main/main.cpp index 1a005583af..7e1fd38e14 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2121,7 +2121,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { MAIN_PRINT("Main: Setup Logo"); -#if defined(WEB_ENABLED) || defined(ANDROID_ENABLED) +#if !defined(TOOLS_ENABLED) && (defined(WEB_ENABLED) || defined(ANDROID_ENABLED)) bool show_logo = false; #else bool show_logo = true; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 30ac1f29b1..a443556fd5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -996,7 +996,9 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum); if (member.enum_value.index > 0) { - member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; + const GDScriptParser::EnumNode::Value &prev_value = member.enum_value.parent_enum->values[member.enum_value.index - 1]; + resolve_class_member(p_class, prev_value.identifier->name, member.enum_value.identifier); + member.enum_value.value = prev_value.value + 1; } else { member.enum_value.value = 0; } diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd index 069e54c528..11349cc916 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd @@ -42,7 +42,8 @@ const c2 := EV2 enum { EV1 = 42, - EV2 = EV3 + 1 + UNUSED = EV3, + EV2 } enum { diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp index a2c33a91c4..844423afc0 100644 --- a/modules/openxr/editor/openxr_action_map_editor.cpp +++ b/modules/openxr/editor/openxr_action_map_editor.cpp @@ -57,7 +57,7 @@ void OpenXRActionMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < tabs->get_child_count(); i++) { - Control *tab = static_cast<Control *>(tabs->get_child(i)); + Control *tab = Object::cast_to<Control>(tabs->get_child(i)); if (tab) { tab->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree"))); } @@ -208,7 +208,7 @@ void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) { void OpenXRActionMapEditor::_on_action_removed() { for (int i = 0; i < tabs->get_tab_count(); i++) { // First tab won't be an interaction profile editor, but being thorough.. - OpenXRInteractionProfileEditorBase *interaction_profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(i)); + OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i)); if (interaction_profile_editor) { } } @@ -309,7 +309,7 @@ void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) { } void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) { - OpenXRInteractionProfileEditorBase *interaction_profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(p_tab)); + OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(p_tab)); ERR_FAIL_NULL(interaction_profile_editor); undo_redo->create_action(TTR("Remove interaction profile")); @@ -375,9 +375,9 @@ void OpenXRActionMapEditor::_clear_action_map() { child->queue_free(); } - for (int i = 0; i < tabs->get_tab_count(); i++) { + for (int i = tabs->get_tab_count() - 1; i >= 0; --i) { // First tab won't be an interaction profile editor, but being thorough.. - OpenXRInteractionProfileEditorBase *interaction_profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(i)); + OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i)); if (interaction_profile_editor) { tabs->remove_child(interaction_profile_editor); interaction_profile_editor->queue_free(); diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp index 3eca1967f5..ee73f6a5cd 100644 --- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp +++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp @@ -308,7 +308,7 @@ void OpenXRInteractionProfileEditor::_update_interaction_profile() { void OpenXRInteractionProfileEditor::_theme_changed() { for (int i = 0; i < main_hb->get_child_count(); i++) { - Control *panel = static_cast<Control *>(main_hb->get_child(i)); + Control *panel = Object::cast_to<Control>(main_hb->get_child(i)); if (panel) { panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer"))); } diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp index 5f018291d5..c4b2ef95c5 100644 --- a/modules/openxr/editor/openxr_select_action_dialog.cpp +++ b/modules/openxr/editor/openxr_select_action_dialog.cpp @@ -47,7 +47,7 @@ void OpenXRSelectActionDialog::_notification(int p_what) { void OpenXRSelectActionDialog::_on_select_action(const String p_action) { if (selected_action != "") { NodePath button_path = action_buttons[selected_action]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(true); } @@ -57,7 +57,7 @@ void OpenXRSelectActionDialog::_on_select_action(const String p_action) { if (selected_action != "") { NodePath button_path = action_buttons[selected_action]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(false); } diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp index 6ce26a62db..b871840cb2 100644 --- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp +++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp @@ -46,7 +46,7 @@ void OpenXRSelectInteractionProfileDialog::_notification(int p_what) { void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const String p_interaction_profile) { if (selected_interaction_profile != "") { NodePath button_path = ip_buttons[selected_interaction_profile]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(true); } @@ -56,7 +56,7 @@ void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const if (selected_interaction_profile != "") { NodePath button_path = ip_buttons[selected_interaction_profile]; - Button *button = static_cast<Button *>(get_node(button_path)); + Button *button = Object::cast_to<Button>(get_node(button_path)); if (button != nullptr) { button->set_flat(false); } diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml index f3ddaafd0e..80ef10b6a4 100644 --- a/platform/android/java/editor/src/main/AndroidManifest.xml +++ b/platform/android/java/editor/src/main/AndroidManifest.xml @@ -27,6 +27,7 @@ android:icon="@mipmap/icon" android:label="@string/godot_editor_name_string" tools:ignore="GoogleAppIndexingWarning" + android:theme="@style/GodotEditorTheme" android:requestLegacyExternalStorage="true"> <activity @@ -35,7 +36,6 @@ android:launchMode="singleTask" android:screenOrientation="userLandscape" android:exported="true" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" android:process=":GodotProjectManager"> <layout android:defaultHeight="@dimen/editor_default_window_height" @@ -53,8 +53,7 @@ android:process=":GodotEditor" android:launchMode="singleTask" android:screenOrientation="userLandscape" - android:exported="false" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> + android:exported="false"> <layout android:defaultHeight="@dimen/editor_default_window_height" android:defaultWidth="@dimen/editor_default_window_width" /> </activity> @@ -66,8 +65,7 @@ android:process=":GodotGame" android:launchMode="singleTask" android:exported="false" - android:screenOrientation="userLandscape" - android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> + android:screenOrientation="userLandscape"> <layout android:defaultHeight="@dimen/editor_default_window_height" android:defaultWidth="@dimen/editor_default_window_width" /> </activity> diff --git a/platform/android/java/editor/src/main/res/values/themes.xml b/platform/android/java/editor/src/main/res/values/themes.xml new file mode 100644 index 0000000000..fda04d6dc7 --- /dev/null +++ b/platform/android/java/editor/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <style name="GodotEditorTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen"> + </style> +</resources> diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 550fe27e4c..61b7dd4504 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -3496,6 +3496,10 @@ void GI::init(SkyRD *p_sky) { { //calculate tables String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { + defines += "\n#define USE_VRS\n"; + } + Vector<String> gi_modes; gi_modes.push_back("\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_VOXEL_GI @@ -4011,7 +4015,7 @@ void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_nor u.append_id(rbgi->scene_data_ubo); uniforms.push_back(u); } - { + if (RendererSceneRenderRD::get_singleton()->is_vrs_supported()) { RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 19; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 3d1e04fe99..bd27eeb981 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1622,7 +1622,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool using_ssr = false; bool using_sdfgi = false; bool using_voxelgi = false; - bool reverse_cull = false; + bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_ssil = p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); if (rb.is_valid()) { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index a55cccdc6b..765f71aa6f 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -673,7 +673,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Size2i screen_size; RID framebuffer; - bool reverse_cull = false; + bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_subpass_transparent = true; bool using_subpass_post_process = true; diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl index ab927df678..459c4dcb1d 100644 --- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl @@ -108,7 +108,9 @@ layout(set = 0, binding = 18, std140) uniform SceneData { } scene_data; +#ifdef USE_VRS layout(r8ui, set = 0, binding = 19) uniform restrict readonly uimage2D vrs_buffer; +#endif layout(push_constant, std430) uniform Params { uint max_voxel_gi_instances; @@ -661,6 +663,7 @@ void main() { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); uint vrs_x, vrs_y; +#ifdef USE_VRS if (sc_use_vrs) { ivec2 vrs_pos; @@ -684,6 +687,7 @@ void main() { return; } } +#endif if (sc_half_res) { pos <<= 1; @@ -708,6 +712,7 @@ void main() { imageStore(ambient_buffer, pos, ambient_light); imageStore(reflection_buffer, pos, reflection_light); +#ifdef USE_VRS if (sc_use_vrs) { if (vrs_x > 1) { imageStore(ambient_buffer, pos + ivec2(1, 0), ambient_light); @@ -766,4 +771,5 @@ void main() { imageStore(reflection_buffer, pos + ivec2(3, 3), reflection_light); } } +#endif } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 503a25184e..1487336d8d 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -715,6 +715,8 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { } } + mesh->aabb = aabb; + mesh->skeleton_aabb_version = skeleton->version; return aabb; } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 543eb0cb72..cc9f92de9b 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -419,11 +419,12 @@ TextureStorage::TextureStorage() { tformat.format = RD::DATA_FORMAT_R8_UINT; tformat.width = 4; tformat.height = 4; - tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; - if (RD::get_singleton()->has_feature(RD::SUPPORTS_ATTACHMENT_VRS)) { - tformat.usage_bits |= RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT; - } + tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_VRS_ATTACHMENT_BIT; tformat.texture_type = RD::TEXTURE_TYPE_2D; + if (!RD::get_singleton()->has_feature(RD::SUPPORTS_ATTACHMENT_VRS)) { + tformat.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + tformat.format = RD::DATA_FORMAT_R8_UNORM; + } Vector<uint8_t> pv; pv.resize(4 * 4); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 2030af204d..51f800381b 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -476,6 +476,7 @@ public: Instance *instance = (Instance *)tracker->userdata; switch (p_notification) { case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: + case Dependency::DEPENDENCY_CHANGED_SKELETON_BONES: case Dependency::DEPENDENCY_CHANGED_AABB: { singleton->_instance_queue_update(instance, true, false); @@ -491,8 +492,7 @@ public: case Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE: { singleton->_instance_queue_update(instance, true, true); } break; - case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: - case Dependency::DEPENDENCY_CHANGED_SKELETON_BONES: { + case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { //ignored } break; case Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR: { |