From dc2df8307f41888bab722f75fa9e73adecf86b72 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Tue, 17 Mar 2020 20:20:55 +0100 Subject: VR: Initial Virtual Reality support - Milestone 1, Scene Inspection NOTE: While most of the milestone 1 goals are there, a few smaller features and improvements are still to be done. Big picture of this milestone: Initial, OpenXR-based virtual reality support for users and foundation for advanced use cases. Maniphest Task: https://developer.blender.org/T71347 The tasks contains more information about this milestone. To be clear: This is not a feature rich VR implementation, it's focused on the initial scene inspection use case. We intentionally focused on that, further features like controller support are part of the next milestone. - How to use? Instructions on how to use this are here: https://wiki.blender.org/wiki/User:Severin/GSoC-2019/How_to_Test These will be updated and moved to a more official place (likely the manual) soon. Currently Windows Mixed Reality and Oculus devices are usable. Valve/HTC headsets don't support the OpenXR standard yet and hence, do not work with this implementation. --------------- This is the C-side implementation of the features added for initial VR support as per milestone 1. A "VR Scene Inspection" Add-on will be committed separately, to expose the VR functionality in the UI. It also adds some further features for milestone 1, namely a landmarking system (stored view locations in the VR space) Main additions/features: * Support for rendering viewports to an HMD, with good performance. * Option to sync the VR view perspective with a fully interactive, regular 3D View (VR-Mirror). * Option to disable positional tracking. Keeps the current position (calculated based on the VR eye center pose) when enabled while a VR session is running. * Some regular viewport settings for the VR view * RNA/Python-API to query and set VR session state information. * WM-XR: Layer tying Ghost-XR to the Blender specific APIs/data * wmSurface API: drawable, non-window container (manages Ghost-OpenGL and GPU context) * DNA/RNA for management of VR session settings * `--debug-xr` and `--debug-xr-time` commandline options * Utility batch & config file for using the Oculus runtime on Windows. * Most VR data is runtime only. The exception is user settings which are saved to files (`XrSessionSettings`). * VR support can be disabled through the `WITH_XR_OPENXR` compiler flag. For architecture and code documentation, see https://wiki.blender.org/wiki/Source/Interface/XR. --------------- A few thank you's: * A huge shoutout to Ray Molenkamp for his help during the project - it would have not been that successful without him! * Sebastian Koenig and Simeon Conzendorf for testing and feedback! * The reviewers, especially Brecht Van Lommel! * Dalai Felinto for pushing and managing me to get this done ;) * The OpenXR working group for providing an open standard. I think we're the first bigger application to adopt OpenXR. Congratulations to them and ourselves :) This project started as a Google Summer of Code 2019 project - "Core Support of Virtual Reality Headsets through OpenXR" (see https://wiki.blender.org/wiki/User:Severin/GSoC-2019/). Some further information, including ideas for further improvements can be found in the final GSoC report: https://wiki.blender.org/wiki/User:Severin/GSoC-2019/Final_Report Differential Revisions: D6193, D7098 Reviewed by: Brecht Van Lommel, Jeroen Bakker --- CMakeLists.txt | 3 +- intern/ghost/CMakeLists.txt | 3 + intern/ghost/GHOST_C-api.h | 12 + intern/ghost/GHOST_IContext.h | 9 + intern/ghost/GHOST_Types.h | 31 +- intern/ghost/intern/GHOST_C-api.cpp | 14 + intern/ghost/intern/GHOST_Context.h | 8 + intern/ghost/intern/GHOST_IXrGraphicsBinding.h | 2 + intern/ghost/intern/GHOST_XrContext.cpp | 4 +- intern/ghost/intern/GHOST_XrContext.h | 3 + intern/ghost/intern/GHOST_XrGraphicsBinding.cpp | 2 + intern/ghost/intern/GHOST_XrSession.cpp | 43 +- intern/ghost/intern/GHOST_XrSession.h | 3 +- release/windows/batch/blender_oculus.cmd | 14 + release/windows/batch/oculus.json | 9 + source/blender/CMakeLists.txt | 1 + source/blender/blenkernel/BKE_global.h | 2 + source/blender/blenkernel/CMakeLists.txt | 4 + source/blender/blenkernel/intern/object.c | 6 +- source/blender/blenlib/BLI_math_geom.h | 7 + source/blender/blenlib/intern/math_geom.c | 19 + source/blender/blenloader/intern/readfile.c | 35 + source/blender/blenloader/intern/versioning_280.c | 18 + source/blender/blenloader/intern/writefile.c | 6 + source/blender/draw/CMakeLists.txt | 4 + source/blender/draw/DRW_engine.h | 9 + source/blender/draw/intern/draw_manager.c | 40 +- source/blender/editors/include/ED_view3d.h | 18 +- .../blender/editors/include/ED_view3d_offscreen.h | 19 + source/blender/editors/screen/screen_ops.c | 3 +- source/blender/editors/sculpt_paint/paint_cursor.c | 13 +- source/blender/editors/space_view3d/CMakeLists.txt | 4 + source/blender/editors/space_view3d/space_view3d.c | 87 ++- source/blender/editors/space_view3d/view3d_draw.c | 153 ++++- source/blender/editors/space_view3d/view3d_edit.c | 130 ++-- source/blender/editors/space_view3d/view3d_fly.c | 2 +- .../editors/space_view3d/view3d_gizmo_navigate.c | 35 +- source/blender/editors/space_view3d/view3d_utils.c | 20 +- source/blender/editors/space_view3d/view3d_view.c | 99 ++- source/blender/editors/space_view3d/view3d_walk.c | 2 +- source/blender/gpu/GPU_viewport.h | 3 + source/blender/gpu/intern/gpu_viewport.c | 46 +- source/blender/makesdna/DNA_view3d_enums.h | 1 + source/blender/makesdna/DNA_view3d_types.h | 33 +- source/blender/makesdna/DNA_windowmanager_types.h | 14 + source/blender/makesdna/DNA_xr_types.h | 58 ++ source/blender/makesdna/intern/makesdna.c | 2 + source/blender/makesrna/RNA_access.h | 2 + source/blender/makesrna/intern/CMakeLists.txt | 5 + source/blender/makesrna/intern/makesrna.c | 1 + source/blender/makesrna/intern/rna_internal.h | 1 + source/blender/makesrna/intern/rna_space.c | 104 ++- source/blender/makesrna/intern/rna_wm.c | 26 + source/blender/makesrna/intern/rna_wm_gizmo.c | 6 + source/blender/makesrna/intern/rna_xr.c | 229 +++++++ source/blender/python/gpu/gpu_py_offscreen.c | 1 + source/blender/windowmanager/CMakeLists.txt | 9 + source/blender/windowmanager/WM_api.h | 16 + source/blender/windowmanager/WM_types.h | 2 + .../blender/windowmanager/gizmo/WM_gizmo_types.h | 7 + .../windowmanager/gizmo/intern/wm_gizmo_map.c | 5 +- source/blender/windowmanager/intern/wm.c | 5 + source/blender/windowmanager/intern/wm_draw.c | 82 ++- source/blender/windowmanager/intern/wm_init_exit.c | 2 + source/blender/windowmanager/intern/wm_operators.c | 81 +++ source/blender/windowmanager/intern/wm_surface.c | 118 ++++ source/blender/windowmanager/intern/wm_window.c | 29 + source/blender/windowmanager/intern/wm_xr.c | 759 +++++++++++++++++++++ source/blender/windowmanager/wm.h | 10 + source/blender/windowmanager/wm_surface.h | 57 ++ source/creator/CMakeLists.txt | 6 + source/creator/creator_args.c | 24 + tests/python/bl_load_addons.py | 3 + tests/python/bl_load_py_modules.py | 3 + 74 files changed, 2425 insertions(+), 221 deletions(-) create mode 100644 release/windows/batch/blender_oculus.cmd create mode 100644 release/windows/batch/oculus.json create mode 100644 source/blender/makesdna/DNA_xr_types.h create mode 100644 source/blender/makesrna/intern/rna_xr.c create mode 100644 source/blender/windowmanager/intern/wm_surface.c create mode 100644 source/blender/windowmanager/intern/wm_xr.c create mode 100644 source/blender/windowmanager/wm_surface.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b78b0e7d647..9f4d0c20886 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,8 +186,7 @@ if(APPLE) option(WITH_XR_OPENXR "Enable VR features through the OpenXR specification" OFF) mark_as_advanced(WITH_XR_OPENXR) else() - # Disabled until there's more than just the build system stuff. Should be enabled soon. - option(WITH_XR_OPENXR "Enable VR features through the OpenXR specification" OFF) + option(WITH_XR_OPENXR "Enable VR features through the OpenXR specification" ON) endif() # Compositor diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 07d98475c00..611da8d6a44 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -377,6 +377,9 @@ if(WITH_XR_OPENXR) list(APPEND INC_SYS ${XR_OPENXR_SDK_INCLUDE_DIR} ) + list(APPEND LIB + ${XR_OPENXR_SDK_LIBRARIES} + ) set(XR_PLATFORM_DEFINES -DXR_USE_GRAPHICS_API_OPENGL) diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index aafe374a93b..aba5b5f733b 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -756,6 +756,18 @@ extern GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthan */ extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle); +/** + * Get the OpenGL framebuffer handle that serves as a default framebuffer. + */ +extern unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle); + +/** + * Returns whether a context is rendered upside down compared to OpenGL. This only needs to be + * called if there's a non-OpenGL context, which is really the exception. + * So generally, this does not need to be called. + */ +extern int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle); + /** * Get the OpenGL framebuffer handle that serves as a default framebuffer. */ diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h index a341e18ca0a..33422b8e351 100644 --- a/intern/ghost/GHOST_IContext.h +++ b/intern/ghost/GHOST_IContext.h @@ -56,6 +56,15 @@ class GHOST_IContext { */ virtual GHOST_TSuccess releaseDrawingContext() = 0; + virtual unsigned int getDefaultFramebuffer() = 0; + + virtual GHOST_TSuccess swapBuffers() = 0; + + /** + * Returns if the window is rendered upside down compared to OpenGL. + */ + virtual bool isUpsideDown() const = 0; + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IContext") #endif diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index adda782b96d..70c4d3ef00c 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -598,6 +598,8 @@ typedef void (*GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, GHOST_T #ifdef WITH_XR_OPENXR +struct GHOST_XrError; +struct GHOST_XrDrawViewInfo; /** * The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. An * offscreen texture of the viewport will then be drawn into using OpenGL, but the final texture @@ -605,7 +607,7 @@ typedef void (*GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, GHOST_T * * This enum defines the possible graphics bindings to attempt to enable. */ -typedef enum { +typedef enum GHOST_TXrGraphicsBinding { GHOST_kXrGraphicsUnknown = 0, GHOST_kXrGraphicsOpenGL, # ifdef WIN32 @@ -614,6 +616,16 @@ typedef enum { /* For later */ // GHOST_kXrGraphicsVulkan, } GHOST_TXrGraphicsBinding; + +typedef void (*GHOST_XrErrorHandlerFn)(const struct GHOST_XrError *); + +typedef void (*GHOST_XrSessionExitFn)(void *customdata); + +typedef void *(*GHOST_XrGraphicsContextBindFn)(enum GHOST_TXrGraphicsBinding graphics_lib); +typedef void (*GHOST_XrGraphicsContextUnbindFn)(enum GHOST_TXrGraphicsBinding graphics_lib, + GHOST_ContextHandle graphics_context); +typedef void (*GHOST_XrDrawViewFn)(const struct GHOST_XrDrawViewInfo *draw_view, void *customdata); + /* An array of GHOST_TXrGraphicsBinding items defining the candidate bindings to use. The first * available candidate will be chosen, so order defines priority. */ typedef const GHOST_TXrGraphicsBinding *GHOST_XrGraphicsBindingCandidates; @@ -638,13 +650,17 @@ typedef struct { typedef struct { GHOST_XrPose base_pose; + + GHOST_XrSessionExitFn exit_fn; + void *exit_customdata; } GHOST_XrSessionBeginInfo; -typedef struct { +typedef struct GHOST_XrDrawViewInfo { int ofsx, ofsy; int width, height; - GHOST_XrPose pose; + GHOST_XrPose eye_pose; + GHOST_XrPose local_pose; struct { float angle_left, angle_right; @@ -655,19 +671,12 @@ typedef struct { char expects_srgb_buffer; } GHOST_XrDrawViewInfo; -typedef struct { +typedef struct GHOST_XrError { const char *user_message; void *customdata; } GHOST_XrError; -typedef void (*GHOST_XrErrorHandlerFn)(const GHOST_XrError *); - -typedef void *(*GHOST_XrGraphicsContextBindFn)(GHOST_TXrGraphicsBinding graphics_lib); -typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_TXrGraphicsBinding graphics_lib, - void *graphics_context); -typedef void (*GHOST_XrDrawViewFn)(const GHOST_XrDrawViewInfo *draw_view, void *customdata); - #endif #endif // __GHOST_TYPES_H__ diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 60d20474a5a..d43a2637ad3 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -709,6 +709,20 @@ GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle) return context->releaseDrawingContext(); } +unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle) +{ + GHOST_IContext *context = (GHOST_IContext *)contexthandle; + + return context->getDefaultFramebuffer(); +} + +int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle) +{ + GHOST_IContext *context = (GHOST_IContext *)contexthandle; + + return context->isUpsideDown(); +} + unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; diff --git a/intern/ghost/intern/GHOST_Context.h b/intern/ghost/intern/GHOST_Context.h index bbf6d6a510d..0bd6f63d07e 100644 --- a/intern/ghost/intern/GHOST_Context.h +++ b/intern/ghost/intern/GHOST_Context.h @@ -119,6 +119,14 @@ class GHOST_Context : public GHOST_IContext { return m_stereoVisual; } + /** + * Returns if the window is rendered upside down compared to OpenGL. + */ + inline bool isUpsideDown() const + { + return false; + } + /** * Gets the OpenGL framebuffer associated with the OpenGL context * \return The ID of an OpenGL framebuffer object. diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h index 19fe00cdad5..25281d3d0ba 100644 --- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -41,6 +41,8 @@ class GHOST_IXrGraphicsBinding { #endif } oxr_binding; + virtual ~GHOST_IXrGraphicsBinding() = default; + /** * Does __not__ require this object is initialized (can be called prior to * #initFromGhostContext). It's actually meant to be called first. diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 410837e9805..d7b83114c85 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -454,10 +454,12 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToEnable( void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info) { + m_custom_funcs.session_exit_fn = begin_info->exit_fn; + m_custom_funcs.session_exit_customdata = begin_info->exit_customdata; + if (m_session == nullptr) { m_session = std::unique_ptr(new GHOST_XrSession(this)); } - m_session->start(begin_info); } diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h index b361fb5caf8..5140ad7ea27 100644 --- a/intern/ghost/intern/GHOST_XrContext.h +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -33,6 +33,9 @@ struct GHOST_XrCustomFuncs { /** Function to release (possibly free) a graphics context. */ GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn = nullptr; + GHOST_XrSessionExitFn session_exit_fn = nullptr; + void *session_exit_customdata = nullptr; + /** Custom per-view draw function for Blender side drawing. */ GHOST_XrDrawViewFn draw_view_fn = nullptr; }; diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index ddc757b8f8a..f094b0744a2 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -116,6 +116,8 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { oxr_binding.glx.glxDrawable = ctx_glx->m_window; oxr_binding.glx.glxContext = ctx_glx->m_context; oxr_binding.glx.visualid = visual_info->visualid; + + XFree(visual_info); #elif defined(WIN32) GHOST_ContextWGL *ctx_wgl = static_cast(ghost_ctx); diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 1e2b8c0bc9d..a85bde3cab6 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -43,6 +43,7 @@ struct OpenXRSessionData { /* Only stereo rendering supported now. */ const XrViewConfigurationType view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; XrSpace reference_space; + XrSpace view_space; std::vector views; std::vector> swapchains; }; @@ -81,6 +82,8 @@ GHOST_XrSession::~GHOST_XrSession() m_oxr->session = XR_NULL_HANDLE; m_oxr->session_state = XR_SESSION_STATE_UNKNOWN; + + m_context->getCustomFuncs().session_exit_fn(m_context->getCustomFuncs().session_exit_customdata); } /** @@ -107,7 +110,7 @@ void GHOST_XrSession::initSystem() * * \{ */ -static void create_reference_space(OpenXRSessionData *oxr, const GHOST_XrPose *base_pose) +static void create_reference_spaces(OpenXRSessionData *oxr, const GHOST_XrPose *base_pose) { XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; create_info.poseInReferenceSpace.orientation.w = 1.0f; @@ -138,6 +141,10 @@ static void create_reference_space(OpenXRSessionData *oxr, const GHOST_XrPose *b CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->reference_space), "Failed to create reference space."); + + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + CHECK_XR(xrCreateReferenceSpace(oxr->session, &create_info, &oxr->view_space), + "Failed to create view reference space."); } void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) @@ -184,7 +191,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) "detailed error information to the command line."); prepareDrawing(); - create_reference_space(m_oxr.get(), &begin_info->base_pose); + create_reference_spaces(m_oxr.get(), &begin_info->base_pose); } void GHOST_XrSession::requestEnd() @@ -343,16 +350,22 @@ void GHOST_XrSession::draw(void *draw_customdata) endFrameDrawing(&layers); } +static void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose) +{ + /* Set and convert to Blender coodinate space. */ + r_ghost_pose.position[0] = oxr_pose.position.x; + r_ghost_pose.position[1] = oxr_pose.position.y; + r_ghost_pose.position[2] = oxr_pose.position.z; + r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w; + r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x; + r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y; + r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z; +} + static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info) { /* Set and convert to Blender coodinate space. */ - r_info.pose.position[0] = view.pose.position.x; - r_info.pose.position[1] = view.pose.position.y; - r_info.pose.position[2] = view.pose.position.z; - r_info.pose.orientation_quat[0] = view.pose.orientation.w; - r_info.pose.orientation_quat[1] = view.pose.orientation.x; - r_info.pose.orientation_quat[2] = view.pose.orientation.y; - r_info.pose.orientation_quat[3] = view.pose.orientation.z; + copy_openxr_pose_to_ghost_pose(view.pose, r_info.eye_pose); r_info.fov.angle_left = view.fov.angleLeft; r_info.fov.angle_right = view.fov.angleRight; @@ -370,6 +383,7 @@ static bool ghost_xr_draw_view_expects_srgb_buffer(const GHOST_XrContext *contex void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain, XrCompositionLayerProjectionView &r_proj_layer_view, + XrSpaceLocation &view_location, XrView &view, void *draw_customdata) { @@ -386,6 +400,7 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain, draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y; draw_view_info.width = r_proj_layer_view.subImage.imageRect.extent.width; draw_view_info.height = r_proj_layer_view.subImage.imageRect.extent.height; + copy_openxr_pose_to_ghost_pose(view_location.pose, draw_view_info.local_pose); ghost_xr_draw_view_info_from_view(view, draw_view_info); /* Draw! */ @@ -401,6 +416,7 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( XrViewLocateInfo viewloc_info = {XR_TYPE_VIEW_LOCATE_INFO}; XrViewState view_state = {XR_TYPE_VIEW_STATE}; XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + XrSpaceLocation view_location{XR_TYPE_SPACE_LOCATION}; uint32_t view_count; viewloc_info.viewConfigurationType = m_oxr->view_type; @@ -416,11 +432,17 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( "Failed to query frame view and projection state."); assert(m_oxr->swapchains.size() == view_count); + CHECK_XR( + xrLocateSpace( + m_oxr->view_space, m_oxr->reference_space, viewloc_info.displayTime, &view_location), + "Failed to query frame view space"); + r_proj_layer_views.resize(view_count); for (uint32_t view_idx = 0; view_idx < view_count; view_idx++) { drawView(*m_oxr->swapchains[view_idx], r_proj_layer_views[view_idx], + view_location, m_oxr->views[view_idx], draw_customdata); } @@ -479,7 +501,8 @@ void GHOST_XrSession::unbindGraphicsContext() { const GHOST_XrCustomFuncs &custom_funcs = m_context->getCustomFuncs(); if (custom_funcs.gpu_ctx_unbind_fn) { - custom_funcs.gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), m_gpu_ctx); + custom_funcs.gpu_ctx_unbind_fn(m_context->getGraphicsBindingType(), + (GHOST_ContextHandle)m_gpu_ctx); } m_gpu_ctx = nullptr; } diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index 3340385c1b6..ef2da61df3d 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -25,9 +25,9 @@ #include class GHOST_XrContext; +class GHOST_XrSwapchain; struct OpenXRSessionData; struct GHOST_XrDrawInfo; -struct GHOST_XrSwapchain; class GHOST_XrSession { public: @@ -74,6 +74,7 @@ class GHOST_XrSession { std::vector &r_proj_layer_views, void *draw_customdata); void drawView(GHOST_XrSwapchain &swapchain, XrCompositionLayerProjectionView &r_proj_layer_view, + XrSpaceLocation &view_location, XrView &view, void *draw_customdata); void beginFrameDrawing(); diff --git a/release/windows/batch/blender_oculus.cmd b/release/windows/batch/blender_oculus.cmd new file mode 100644 index 00000000000..ffb725eb32f --- /dev/null +++ b/release/windows/batch/blender_oculus.cmd @@ -0,0 +1,14 @@ +@echo off + +REM Helper setting hints to get the OpenXR preview support enabled for Oculus. +REM Of course this is not meant as a permanent solution. Oculus will likely provide a better setup at some point. + +echo Starting Blender with Oculus OpenXR support. This assumes the Oculus runtime +echo is installed in the default location. If this is not the case, please adjust +echo the path inside oculus.json. +echo. +echo Note that OpenXR support in Oculus is considered a preview. Use with care! +echo. +pause +set XR_RUNTIME_JSON=%~dp0oculus.json +blender diff --git a/release/windows/batch/oculus.json b/release/windows/batch/oculus.json new file mode 100644 index 00000000000..ba8767f2c58 --- /dev/null +++ b/release/windows/batch/oculus.json @@ -0,0 +1,9 @@ +{ + "file_format_version": "1.0.0", + "runtime": + { + "api_version": "1.0", + "name": "Oculus OpenXR", + "library_path": "c:\\Program Files\\Oculus\\Support\\oculus-runtime\\LibOVRRT64_1.dll" + } +} diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index b44b6db8804..203b6da272f 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -90,6 +90,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_workspace_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_xr_types.h ) add_subdirectory(datatoc) diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index fe050075bae..f6cae6d8a9c 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -153,6 +153,8 @@ enum { G_DEBUG_IO = (1 << 17), /* IO Debugging (for Collada, ...)*/ G_DEBUG_GPU_SHADERS = (1 << 18), /* GLSL shaders */ G_DEBUG_GPU_FORCE_WORKAROUNDS = (1 << 19), /* force gpu workarounds bypassing detections. */ + G_DEBUG_XR = (1 << 20), /* XR/OpenXR messages */ + G_DEBUG_XR_TIME = (1 << 21), /* XR/OpenXR timing messages */ G_DEBUG_GHOST = (1 << 20), /* Debug GHOST module. */ }; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 923108240dd..047901b4c81 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -667,6 +667,10 @@ if(WITH_TBB) ) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + # # Warnings as errors, this is too strict! # if(MSVC) # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index d7247518f9e..fa3284c18d6 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2680,7 +2680,8 @@ void BKE_object_workob_calc_parent(Depsgraph *depsgraph, Scene *scene, Object *o } /** - * Applies the global transformation \a mat to the \a ob using a relative parent space if supplied. + * Applies the global transformation \a mat to the \a ob using a relative parent space if + * supplied. * * \param mat: the global transformation mat that the object should be set object to. * \param parent: the parent space in which this object will be set relative to @@ -3183,7 +3184,8 @@ typedef struct ObTfmBack { float obmat[4][4]; /** inverse result of parent, so that object doesn't 'stick' to parent. */ float parentinv[4][4]; - /** inverse result of constraints. doesn't include effect of parent or object local transform. */ + /** inverse result of constraints. doesn't include effect of parent or object local transform. + */ float constinv[4][4]; /** inverse matrix of 'obmat' for during render, temporally: ipokeys of transform. */ float imat[4][4]; diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 534c25f6e01..2049f368578 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -636,6 +636,13 @@ void perspective_m4(float mat[4][4], const float top, const float nearClip, const float farClip); +void perspective_m4_fov(float mat[4][4], + const float angle_left, + const float angle_right, + const float angle_up, + const float angle_down, + const float nearClip, + const float farClip); void orthographic_m4(float mat[4][4], const float left, const float right, diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index d54ec0f1ac1..fa50bf202a8 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -4709,6 +4709,25 @@ void perspective_m4(float mat[4][4], mat[3][3] = 0.0f; } +void perspective_m4_fov(float mat[4][4], + const float angle_left, + const float angle_right, + const float angle_up, + const float angle_down, + const float nearClip, + const float farClip) +{ + const float tan_angle_left = tanf(angle_left); + const float tan_angle_right = tanf(angle_right); + const float tan_angle_bottom = tanf(angle_up); + const float tan_angle_top = tanf(angle_down); + + perspective_m4( + mat, tan_angle_left, tan_angle_right, tan_angle_top, tan_angle_bottom, nearClip, farClip); + mat[0][0] /= nearClip; + mat[1][1] /= nearClip; +} + /* translate a matrix created by orthographic_m4 or perspective_m4 in XY coords * (used to jitter the view) */ void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c3fba697bd3..f1f274f97d5 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7187,6 +7187,7 @@ static void direct_link_region(FileData *fd, ARegion *region, int spacetype) rv3d->smooth_timer = NULL; rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING); + rv3d->runtime_viewlock = 0; } } } @@ -7277,7 +7278,10 @@ static void direct_link_area(FileData *fd, ScrArea *area) direct_link_gpencil(fd, v3d->gpd); } v3d->localvd = newdataadr(fd, v3d->localvd); + + /* Runtime data */ v3d->runtime.properties_storage = NULL; + v3d->runtime.flag = 0; /* render can be quite heavy, set to solid on load */ if (v3d->shading.type == OB_RENDER) { @@ -7657,6 +7661,23 @@ static bool direct_link_area_map(FileData *fd, ScrAreaMap *area_map) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name XR-data + * \{ */ + +static void direct_link_wm_xr_data(FileData *fd, wmXrData *xr_data) +{ + direct_link_view3dshading(fd, &xr_data->session_settings.shading); +} + +static void lib_link_wm_xr_data(FileData *fd, ID *parent_id, wmXrData *xr_data) +{ + xr_data->session_settings.base_pose_object = newlibadr( + fd, parent_id->lib, xr_data->session_settings.base_pose_object); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Read ID: Window Manager * \{ */ @@ -7710,6 +7731,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) } } + direct_link_wm_xr_data(fd, &wm->xr); + BLI_listbase_clear(&wm->timers); BLI_listbase_clear(&wm->operators); BLI_listbase_clear(&wm->paintcursors); @@ -7724,6 +7747,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) wm->message_bus = NULL; + wm->xr.runtime = NULL; + BLI_listbase_clear(&wm->jobs); BLI_listbase_clear(&wm->drags); @@ -7747,6 +7772,8 @@ static void lib_link_windowmanager(FileData *fd, Main *UNUSED(bmain), wmWindowMa for (ScrArea *area = win->global_areas.areabase.first; area; area = area->next) { lib_link_area(fd, &wm->id, area); } + + lib_link_wm_xr_data(fd, &wm->id, &wm->xr); } } @@ -7911,6 +7938,12 @@ static void lib_link_main_data_restore(struct IDNameLib_Map *id_map, Main *newma FOREACH_MAIN_ID_END; } +static void lib_link_wm_xr_data_restore(struct IDNameLib_Map *id_map, wmXrData *xr_data) +{ + xr_data->session_settings.base_pose_object = restore_pointer_by_name( + id_map, (ID *)xr_data->session_settings.base_pose_object, USER_REAL); +} + static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, ViewLayer *view_layer) { bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); @@ -8241,6 +8274,8 @@ void blo_lib_link_restore(Main *oldmain, BLI_assert(win->screen == NULL); } + lib_link_wm_xr_data_restore(id_map, &curwm->xr); + /* Restore all ID pointers in Main database itself * (especially IDProperties might point to some word-space of other 'weirdly unchanged' ID * pointers, see T69146). diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 87442a10d12..3535897aa66 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -30,6 +30,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "DNA_defaults.h" + #include "DNA_anim_types.h" #include "DNA_object_types.h" #include "DNA_camera_types.h" @@ -4845,5 +4847,21 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!DNA_struct_find(fd->filesdna, "XrSessionSettings")) { + for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { + const View3D *v3d_default = DNA_struct_default_get(View3D); + + wm->xr.session_settings.shading = v3d_default->shading; + /* Don't rotate light with the viewer by default, make it fixed. */ + wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION; + wm->xr.session_settings.draw_flags = (V3D_OFSDRAW_SHOW_GRIDFLOOR | + V3D_OFSDRAW_SHOW_ANNOTATION); + wm->xr.session_settings.clip_start = v3d_default->clip_start; + wm->xr.session_settings.clip_end = v3d_default->clip_end; + + wm->xr.session_settings.flag = XR_SESSION_USE_POSITION_TRACKING; + } + } } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 29366e3bae5..837134c0156 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2820,6 +2820,11 @@ static void write_gpencil(WriteData *wd, bGPdata *gpd) } } +static void write_wm_xr_data(WriteData *wd, wmXrData *xr_data) +{ + write_view3dshading(wd, &xr_data->session_settings.shading); +} + static void write_region(WriteData *wd, ARegion *region, int spacetype) { writestruct(wd, DATA, ARegion, 1, region); @@ -3066,6 +3071,7 @@ static void write_windowmanager(WriteData *wd, wmWindowManager *wm) { writestruct(wd, ID_WM, wmWindowManager, 1, wm); write_iddata(wd, &wm->id); + write_wm_xr_data(wd, &wm->xr); for (wmWindow *win = wm->windows.first; win; win = win->next) { #ifndef WITH_GLOBAL_AREA_WRITING diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index a1213be4be0..81f2214b402 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -390,4 +390,8 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index b4c23d5e57c..7ecf9df275d 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -93,6 +93,7 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, struct RenderEngineType *engine_type, struct ARegion *region, struct View3D *v3d, + const bool is_image_render, const bool draw_background, const bool do_color_management, struct GPUOffScreen *ofs, @@ -139,6 +140,14 @@ void DRW_opengl_context_destroy(void); void DRW_opengl_context_enable(void); void DRW_opengl_context_disable(void); +#ifdef WITH_XR_OPENXR +/* XXX see comment on DRW_xr_opengl_context_get() */ +void *DRW_xr_opengl_context_get(void); +void *DRW_xr_gpu_context_get(void); +void DRW_xr_drawing_begin(void); +void DRW_xr_drawing_end(void); +#endif + /* For garbage collection */ void DRW_cache_free_old_batches(struct Main *bmain); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 71e2d8d9b57..618922d8544 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -562,7 +562,7 @@ static void drw_viewport_var_init(void) DRW_view_camtexco_set(DST.view_default, rv3d->viewcamtexcofac); if (DST.draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) { - int plane_len = (rv3d->viewlock & RV3D_BOXCLIP) ? 4 : 6; + int plane_len = (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXCLIP) ? 4 : 6; DRW_view_clip_planes_set(DST.view_default, rv3d->clip, plane_len); } @@ -1554,6 +1554,7 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, RenderEngineType *engine_type, ARegion *region, View3D *v3d, + const bool is_image_render, const bool draw_background, const bool do_color_management, GPUOffScreen *ofs, @@ -1569,7 +1570,7 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); - DST.options.is_image_render = true; + DST.options.is_image_render = is_image_render; DST.options.do_color_management = do_color_management; DST.options.draw_background = draw_background; DRW_draw_render_loop_ex(depsgraph, engine_type, region, v3d, render_viewport, NULL); @@ -2829,4 +2830,39 @@ void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context)) GPU_context_active_set(NULL); } +#ifdef WITH_XR_OPENXR + +/* XXX + * There should really be no such getter, but for VR we currently can't easily avoid it. OpenXR + * needs some low level info for the OpenGL context that will be used for submitting the + * final framebuffer. VR could in theory create its own context, but that would mean we have to + * switch to it just to submit the final frame, which has notable performance impact. + * + * We could "inject" a context through DRW_opengl_render_context_enable(), but that would have to + * work from the main thread, which is tricky to get working too. The preferable solution would be + * using a separate thread for VR drawing where a single context can stay active. */ +void *DRW_xr_opengl_context_get(void) +{ + return DST.gl_context; +} + +/* XXX See comment on DRW_xr_opengl_context_get(). */ +void *DRW_xr_gpu_context_get(void) +{ + return DST.gpu_context; +} + +/* XXX See comment on DRW_xr_opengl_context_get(). */ +void DRW_xr_drawing_begin(void) +{ + BLI_ticket_mutex_lock(DST.gl_context_mutex); +} + +/* XXX See comment on DRW_xr_opengl_context_get(). */ +void DRW_xr_drawing_end(void) +{ + BLI_ticket_mutex_unlock(DST.gl_context_mutex); +} + +#endif /** \} */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index f882d6be9a2..06de4705ac6 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -561,6 +561,9 @@ struct RegionView3D *ED_view3d_context_rv3d(struct bContext *C); bool ED_view3d_context_user_region(struct bContext *C, struct View3D **r_v3d, struct ARegion **r_ar); +bool ED_view3d_area_user_region(const struct ScrArea *sa, + const struct View3D *v3d, + struct ARegion **r_ar); bool ED_operator_rv3d_user_region_poll(struct bContext *C); void ED_view3d_init_mats_rv3d(struct Object *ob, struct RegionView3D *rv3d); @@ -584,7 +587,8 @@ void ED_draw_object_facemap(struct Depsgraph *depsgraph, struct RenderEngineType *ED_view3d_engine_type(const struct Scene *scene, int drawtype); bool ED_view3d_context_activate(struct bContext *C); -void ED_view3d_draw_setup_view(struct wmWindow *win, +void ED_view3d_draw_setup_view(const struct wmWindowManager *wm, + struct wmWindow *win, struct Depsgraph *depsgraph, struct Scene *scene, struct ARegion *region, @@ -730,6 +734,18 @@ void ED_view3d_buttons_region_layout_ex(const struct bContext *C, bool ED_view3d_local_collections_set(struct Main *bmain, struct View3D *v3d); void ED_view3d_local_collections_reset(struct bContext *C, const bool reset_all); +#ifdef WITH_XR_OPENXR +void ED_view3d_xr_mirror_update(const struct ScrArea *area, + const struct View3D *v3d, + const bool enable); +void ED_view3d_xr_shading_update(struct wmWindowManager *wm, + const View3D *v3d, + const struct Scene *scene); +bool ED_view3d_is_region_xr_mirror_active(const struct wmWindowManager *wm, + const struct View3D *v3d, + const struct ARegion *region); +#endif + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_view3d_offscreen.h b/source/blender/editors/include/ED_view3d_offscreen.h index 3cbb13fa8b5..e1ed7472973 100644 --- a/source/blender/editors/include/ED_view3d_offscreen.h +++ b/source/blender/editors/include/ED_view3d_offscreen.h @@ -51,12 +51,31 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph, int winy, float viewmat[4][4], float winmat[4][4], + bool is_image_render, bool do_sky, bool is_persp, const char *viewname, const bool do_color_management, struct GPUOffScreen *ofs, struct GPUViewport *viewport); +void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph, + struct Scene *scene, + struct View3DShading *shading_override, + int drawtype, + int winx, + int winy, + unsigned int draw_flags, + float viewmat[4][4], + float winmat[4][4], + float clip_start, + float clip_end, + bool is_image_render, + bool do_sky, + bool is_persp, + const char *viewname, + const bool do_color_management, + struct GPUOffScreen *ofs, + struct GPUViewport *viewport); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index a9f44f54b3a..a72b18f63f8 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3818,6 +3818,7 @@ static void region_quadview_init_rv3d( } rv3d->viewlock = viewlock; + rv3d->runtime_viewlock = 0; rv3d->view = view; rv3d->view_axis_roll = RV3D_VIEW_AXIS_ROLL_0; rv3d->persp = persp; @@ -3915,7 +3916,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) RegionView3D *rv3d = region->regiondata; const char viewlock = (rv3d->viewlock_quad & RV3D_VIEWLOCK_INIT) ? (rv3d->viewlock_quad & ~RV3D_VIEWLOCK_INIT) : - RV3D_LOCKED; + RV3D_LOCK_ROTATION; region_quadview_init_rv3d( sa, region, viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index dd922834d9e..0ddce872545 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1239,6 +1239,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) return; } + const wmWindowManager *wm = CTX_wm_manager(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -1443,7 +1444,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* Draw 3D brush cursor. */ GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_window(C), + ED_view3d_draw_setup_view(wm, + CTX_wm_window(C), CTX_data_depsgraph_pointer(C), CTX_data_scene(C), region, @@ -1537,7 +1539,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) !is_multires) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->deform_modifiers_active) { GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_window(C), + ED_view3d_draw_setup_view(wm, + CTX_wm_window(C), CTX_data_depsgraph_pointer(C), CTX_data_scene(C), region, @@ -1556,7 +1559,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) if (brush->sculpt_tool == SCULPT_TOOL_MULTIPLANE_SCRAPE && brush->flag2 & BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW && !ss->cache->first_time) { GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_window(C), + ED_view3d_draw_setup_view(wm, + CTX_wm_window(C), CTX_data_depsgraph_pointer(C), CTX_data_scene(C), region, @@ -1573,7 +1577,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && !ss->cache->first_time) { GPU_matrix_push_projection(); - ED_view3d_draw_setup_view(CTX_wm_window(C), + ED_view3d_draw_setup_view(CTX_wm_manager(C), + CTX_wm_window(C), CTX_data_depsgraph_pointer(C), CTX_data_scene(C), region, diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 91694cfc1ef..c7fe82e0cbb 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -94,6 +94,10 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + blender_add_lib(bf_editor_space_view3d "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") # Needed so we can use dna_type_offsets.h for defaults initialization. diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index b2fc6c6e4cc..f16e19c598e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -39,6 +39,7 @@ #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_global.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_lattice.h" @@ -114,44 +115,61 @@ bool ED_view3d_context_user_region(bContext *C, View3D **r_v3d, ARegion **r_ar) if (region) { RegionView3D *rv3d; if ((region->regiontype == RGN_TYPE_WINDOW) && (rv3d = region->regiondata) && - (rv3d->viewlock & RV3D_LOCKED) == 0) { + (rv3d->viewlock & RV3D_LOCK_ROTATION) == 0) { *r_v3d = v3d; *r_ar = region; return true; } else { - ARegion *ar_unlock_user = NULL; - ARegion *ar_unlock = NULL; - for (region = sa->regionbase.first; region; region = region->next) { - /* find the first unlocked rv3d */ - if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) { - rv3d = region->regiondata; - if ((rv3d->viewlock & RV3D_LOCKED) == 0) { - ar_unlock = region; - if (rv3d->persp == RV3D_PERSP || rv3d->persp == RV3D_CAMOB) { - ar_unlock_user = region; - break; - } - } - } - } - - /* camera/perspective view get priority when the active region is locked */ - if (ar_unlock_user) { + if (ED_view3d_area_user_region(sa, v3d, r_ar)) { *r_v3d = v3d; - *r_ar = ar_unlock_user; return true; } + } + } + } - if (ar_unlock) { - *r_v3d = v3d; - *r_ar = ar_unlock; - return true; + return false; +} + +/** + * Similar to #ED_view3d_context_user_region() but does not use context. Always performs a lookup. + * Also works if \a v3d is not the active space. + */ +bool ED_view3d_area_user_region(const ScrArea *sa, const View3D *v3d, ARegion **r_ar) +{ + RegionView3D *rv3d = NULL; + ARegion *ar_unlock_user = NULL; + ARegion *ar_unlock = NULL; + const ListBase *region_list = (v3d == sa->spacedata.first) ? &sa->regionbase : &v3d->regionbase; + + BLI_assert(v3d->spacetype == SPACE_VIEW3D); + + for (ARegion *region = region_list->first; region; region = region->next) { + /* find the first unlocked rv3d */ + if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) { + rv3d = region->regiondata; + if ((rv3d->viewlock & RV3D_LOCK_ROTATION) == 0) { + ar_unlock = region; + if (rv3d->persp == RV3D_PERSP || rv3d->persp == RV3D_CAMOB) { + ar_unlock_user = region; + break; } } } } + /* camera/perspective view get priority when the active region is locked */ + if (ar_unlock_user) { + *r_ar = ar_unlock_user; + return true; + } + + if (ar_unlock) { + *r_ar = ar_unlock; + return true; + } + return false; } @@ -333,9 +351,11 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) v3dn->localvd = NULL; v3dn->runtime.properties_storage = NULL; } + /* Only one View3D is allowed to have this flag! */ + v3dn->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; v3dn->local_collections_uuid = 0; - v3dn->flag &= ~V3D_LOCAL_COLLECTIONS; + v3dn->flag &= ~(V3D_LOCAL_COLLECTIONS | V3D_XR_SESSION_MIRROR); if (v3dn->shading.type == OB_RENDER) { v3dn->shading.type = OB_SOLID; @@ -715,6 +735,13 @@ static void view3d_main_region_listener( if (ELEM(wmn->data, ND_UNDO)) { WM_gizmomap_tag_refresh(gzmap); } + else if (ELEM(wmn->data, ND_XR_DATA_CHANGED)) { + /* Only cause a redraw if this a VR session mirror. Should more features be added that + * require redraws, we could pass something to wmn->reference, e.g. the flag value. */ + if (v3d->flag & V3D_XR_SESSION_MIRROR) { + ED_region_tag_redraw(region); + } + } break; case NC_ANIMATION: switch (wmn->data) { @@ -912,6 +939,11 @@ static void view3d_main_region_listener( if (wmn->subtype == NS_VIEW3D_GPU) { rv3d->rflag |= RV3D_GPULIGHT_UPDATE; } +#ifdef WITH_XR_OPENXR + else if (wmn->subtype == NS_VIEW3D_SHADING) { + ED_view3d_xr_shading_update(G_MAIN->wm.first, v3d, scene); + } +#endif ED_region_tag_redraw(region); WM_gizmomap_tag_refresh(gzmap); } @@ -1374,6 +1406,11 @@ static void view3d_buttons_region_listener(wmWindow *UNUSED(win), ED_region_tag_redraw(region); } break; + case NC_WM: + if (wmn->data == ND_XR_DATA_CHANGED) { + ED_region_tag_redraw(region); + } + break; } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 4cf5ecfc4f0..78d053c36a7 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -317,10 +317,35 @@ static void view3d_stereo3d_setup( } } +#ifdef WITH_XR_OPENXR +static void view3d_xr_mirror_setup(const wmWindowManager *wm, + Depsgraph *depsgraph, + Scene *scene, + View3D *v3d, + ARegion *region, + const rcti *rect) +{ + RegionView3D *rv3d = region->regiondata; + float viewmat[4][4]; + const float lens_old = v3d->lens; + + if (!WM_xr_session_state_viewer_pose_matrix_info_get(&wm->xr, viewmat, &v3d->lens)) { + /* Can't get info from XR session, use fallback values. */ + copy_m4_m4(viewmat, rv3d->viewmat); + v3d->lens = lens_old; + } + view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, NULL, rect); + + /* Reset overridden View3D data */ + v3d->lens = lens_old; +} +#endif /* WITH_XR_OPENXR */ + /** * Set the correct matrices */ -void ED_view3d_draw_setup_view(wmWindow *win, +void ED_view3d_draw_setup_view(const wmWindowManager *wm, + wmWindow *win, Depsgraph *depsgraph, Scene *scene, ARegion *region, @@ -331,13 +356,23 @@ void ED_view3d_draw_setup_view(wmWindow *win, { RegionView3D *rv3d = region->regiondata; +#ifdef WITH_XR_OPENXR /* Setup the view matrix. */ - if (view3d_stereo3d_active(win, scene, v3d, rv3d)) { + if (ED_view3d_is_region_xr_mirror_active(wm, v3d, region)) { + view3d_xr_mirror_setup(wm, depsgraph, scene, v3d, region, rect); + } + else +#endif + if (view3d_stereo3d_active(win, scene, v3d, rv3d)) { view3d_stereo3d_setup(depsgraph, scene, v3d, region, rect); } else { view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, winmat, rect); } + +#ifndef WITH_XR_OPENXR + UNUSED_VARS(wm); +#endif } /** \} */ @@ -803,7 +838,8 @@ void ED_view3d_draw_depth(Depsgraph *depsgraph, ARegion *region, View3D *v3d, bo UI_Theme_Store(&theme_state); UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW); - ED_view3d_draw_setup_view(NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL); + ED_view3d_draw_setup_view( + G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL); GPU_clear(GPU_DEPTH_BIT); @@ -1481,7 +1517,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *region) wmWindowManager *wm = CTX_wm_manager(C); #ifdef WITH_INPUT_NDOF - if ((U.ndof_flag & NDOF_SHOW_GUIDE) && ((rv3d->viewlock & RV3D_LOCKED) == 0) && + if ((U.ndof_flag & NDOF_SHOW_GUIDE) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) && (rv3d->persp != RV3D_CAMOB)) { /* TODO: draw something else (but not this) during fly mode */ draw_rotation_guide(rv3d); @@ -1552,7 +1588,8 @@ void view3d_draw_region_info(const bContext *C, ARegion *region) static void view3d_draw_view(const bContext *C, ARegion *region) { - ED_view3d_draw_setup_view(CTX_wm_window(C), + ED_view3d_draw_setup_view(CTX_wm_manager(C), + CTX_wm_window(C), CTX_data_expect_evaluated_depsgraph(C), CTX_data_scene(C), region, @@ -1641,6 +1678,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, int winy, float viewmat[4][4], float winmat[4][4], + bool is_image_render, bool do_sky, bool UNUSED(is_persp), const char *viewname, @@ -1690,8 +1728,15 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, } /* main drawing call */ - DRW_draw_render_loop_offscreen( - depsgraph, engine_type, region, v3d, do_sky, do_color_management, ofs, viewport); + DRW_draw_render_loop_offscreen(depsgraph, + engine_type, + region, + v3d, + is_image_render, + do_sky, + do_color_management, + ofs, + viewport); /* restore size */ region->winx = bwinx; @@ -1706,6 +1751,94 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, G.f &= ~G_FLAG_RENDER_VIEWPORT; } +/** + * Creates own fake 3d views (wrapping #ED_view3d_draw_offscreen). Similar too + * #ED_view_draw_offscreen_imbuf_simple, but takes view/projection matrices as arguments. + */ +void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, + Scene *scene, + View3DShading *shading_override, + int drawtype, + int winx, + int winy, + uint draw_flags, + float viewmat[4][4], + float winmat[4][4], + float clip_start, + float clip_end, + bool is_image_render, + bool do_sky, + bool is_persp, + const char *viewname, + const bool do_color_management, + GPUOffScreen *ofs, + GPUViewport *viewport) +{ + View3D v3d = {NULL}; + ARegion ar = {NULL}; + RegionView3D rv3d = {{{0}}}; + + v3d.regionbase.first = v3d.regionbase.last = &ar; + ar.regiondata = &rv3d; + ar.regiontype = RGN_TYPE_WINDOW; + + View3DShading *source_shading_settings = &scene->display.shading; + if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != NULL) { + source_shading_settings = shading_override; + } + memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading)); + v3d.shading.type = drawtype; + + if (shading_override) { + /* Pass. */ + } + else if (drawtype == OB_MATERIAL) { + v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS; + } + + if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { + v3d.flag2 |= V3D_SHOW_ANNOTATION; + } + if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) { + v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y; + v3d.grid = 1.0f; + v3d.gridlines = 16; + v3d.gridsubdiv = 10; + + /* Show grid, disable other overlays (set all available _HIDE_ flags). */ + v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT | + V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES | + V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS; + v3d.flag |= V3D_HIDE_HELPLINES; + } + else { + v3d.flag2 = V3D_HIDE_OVERLAYS; + } + + rv3d.persp = RV3D_PERSP; + v3d.clip_start = clip_start; + v3d.clip_end = clip_end; + /* Actually not used since we pass in the projection matrix. */ + v3d.lens = 0; + + ED_view3d_draw_offscreen(depsgraph, + scene, + drawtype, + &v3d, + &ar, + winx, + winy, + viewmat, + winmat, + is_image_render, + do_sky, + is_persp, + viewname, + do_color_management, + ofs, + viewport); +} + /** * Utility func for ED_view3d_draw_offscreen * @@ -1815,6 +1948,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, sizey, NULL, winmat, + true, draw_sky, !is_ortho, viewname, @@ -1902,6 +2036,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { v3d.flag2 |= V3D_SHOW_ANNOTATION; } + if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) { + v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y; + } v3d.shading.background_type = V3D_SHADING_BACKGROUND_WORLD; @@ -2212,7 +2349,7 @@ float view3d_depth_near(ViewDepths *d) void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *region, View3D *v3d) { /* Setup view matrix. */ - ED_view3d_draw_setup_view(NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL); + ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL); GPU_clear(GPU_DEPTH_BIT); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index a7136cc580c..68d5765e43d 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -85,6 +85,52 @@ enum { HAS_ROTATE = (1 << 0), }; +/* test for unlocked camera view in quad view */ +static bool view3d_camera_user_poll(bContext *C) +{ + View3D *v3d; + ARegion *region; + + if (ED_view3d_context_user_region(C, &v3d, ®ion)) { + RegionView3D *rv3d = region->regiondata; + if ((rv3d->persp == RV3D_CAMOB) && !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM)) { + return 1; + } + } + + return 0; +} + +static bool view3d_lock_poll(bContext *C) +{ + View3D *v3d = CTX_wm_view3d(C); + if (v3d) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d) { + return ED_view3d_offset_lock_check(v3d, rv3d); + } + } + return false; +} + +static bool view3d_pan_poll(bContext *C) +{ + if (ED_operator_region_view3d_active(C)) { + const RegionView3D *rv3d = CTX_wm_region_view3d(C); + return !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_LOCATION); + } + return false; +} + +static bool view3d_zoom_or_dolly_poll(bContext *C) +{ + if (ED_operator_region_view3d_active(C)) { + const RegionView3D *rv3d = CTX_wm_region_view3d(C); + return !(RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ZOOM_AND_DOLLY); + } + return false; +} + /* -------------------------------------------------------------------- */ /** \name Generic View Operator Properties * \{ */ @@ -935,7 +981,7 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) vod = op->customdata; /* poll should check but in some cases fails, see poll func for details */ - if (vod->rv3d->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_ROTATION) { viewops_data_free(C, op); return OPERATOR_PASS_THROUGH; } @@ -983,34 +1029,6 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) } } -/* test for unlocked camera view in quad view */ -static bool view3d_camera_user_poll(bContext *C) -{ - View3D *v3d; - ARegion *region; - - if (ED_view3d_context_user_region(C, &v3d, ®ion)) { - RegionView3D *rv3d = region->regiondata; - if (rv3d->persp == RV3D_CAMOB) { - return 1; - } - } - - return 0; -} - -static bool view3d_lock_poll(bContext *C) -{ - View3D *v3d = CTX_wm_view3d(C); - if (v3d) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (rv3d) { - return ED_view3d_offset_lock_check(v3d, rv3d); - } - } - return false; -} - static void viewrotate_cancel(bContext *C, wmOperator *op) { viewops_data_free(C, op); @@ -1051,7 +1069,7 @@ static bool ndof_has_translate(const wmNDOFMotionData *ndof, static bool ndof_has_rotate(const wmNDOFMotionData *ndof, const RegionView3D *rv3d) { - return !is_zero_v3(ndof->rvec) && ((rv3d->viewlock & RV3D_LOCKED) == 0); + return !is_zero_v3(ndof->rvec) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0); } /** @@ -1159,7 +1177,7 @@ static void view3d_ndof_pan_zoom(const struct wmNDOFMotionData *ndof, /* move center of view opposite of hand motion (this is camera mode, not object mode) */ sub_v3_v3(rv3d->ofs, pan_vec); - if (rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(sa, region); } } @@ -1176,7 +1194,7 @@ static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, float view_inv[4]; - BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0); + BLI_assert((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0); ED_view3d_persp_ensure(vod->depsgraph, v3d, region); @@ -1400,7 +1418,7 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool has_rotation = ndof_has_rotate(ndof, rv3d); /* if we can't rotate, fallback to translate (locked axis views) */ const bool has_translate = ndof_has_translate(ndof, v3d, rv3d) && - (rv3d->viewlock & RV3D_LOCKED); + (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION); const bool has_zoom = (ndof->tvec[2] != 0.0f) && !rv3d->is_persp; if (has_translate || has_zoom) { @@ -1732,7 +1750,7 @@ static void viewmove_apply(ViewOpsData *vod, int x, int y) add_v3_v3(vod->rv3d->ofs, dvec); - if (vod->rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(vod->sa, vod->region); } } @@ -1807,12 +1825,17 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* makes op->customdata */ viewops_data_alloc(C, op); + vod = op->customdata; + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_LOCATION) { + viewops_data_free(C, op); + return OPERATOR_PASS_THROUGH; + } + viewops_data_create(C, op, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) | (use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0)); - vod = op->customdata; ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); @@ -2165,7 +2188,7 @@ static void viewzoom_apply_3d(ViewOpsData *vod, /* these limits were in old code too */ CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]); - if (vod->rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(vod->sa, vod->region); } @@ -2318,7 +2341,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) } } - if (rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(sa, region); } @@ -2416,7 +2439,7 @@ void VIEW3D_OT_zoom(wmOperatorType *ot) ot->invoke = viewzoom_invoke; ot->exec = viewzoom_exec; ot->modal = viewzoom_modal; - ot->poll = ED_operator_region_view3d_active; + ot->poll = view3d_zoom_or_dolly_poll; ot->cancel = viewzoom_cancel; /* flags */ @@ -2514,7 +2537,7 @@ static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_ view_dolly_to_vector_3d(vod->region, vod->init.ofs, vod->init.mousevec, zfac); } - if (vod->rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(vod->sa, vod->region); } @@ -2612,7 +2635,7 @@ static int viewdolly_exec(bContext *C, wmOperator *op) view_dolly_to_vector_3d(region, rv3d->ofs, mousevec, delta < 0 ? 0.2f : 1.8f); - if (rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(sa, region); } @@ -2641,7 +2664,7 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) vod = op->customdata; /* poll should check but in some cases fails, see poll func for details */ - if (vod->rv3d->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_LOCK_ROTATION) { viewops_data_free(C, op); return OPERATOR_PASS_THROUGH; } @@ -3121,7 +3144,7 @@ void VIEW3D_OT_view_selected(wmOperatorType *ot) /* api callbacks */ ot->exec = viewselected_exec; - ot->poll = ED_operator_region_view3d_active; + ot->poll = view3d_zoom_or_dolly_poll; /* flags */ ot->flag = 0; @@ -3265,7 +3288,7 @@ void VIEW3D_OT_view_center_cursor(wmOperatorType *ot) /* api callbacks */ ot->exec = viewcenter_cursor_exec; - ot->poll = ED_operator_view3d_active; + ot->poll = view3d_pan_poll; /* flags */ ot->flag = 0; @@ -3317,7 +3340,7 @@ void VIEW3D_OT_view_center_pick(wmOperatorType *ot) /* api callbacks */ ot->invoke = viewcenter_pick_invoke; - ot->poll = ED_operator_view3d_active; + ot->poll = view3d_pan_poll; /* flags */ ot->flag = 0; @@ -3701,7 +3724,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) .dist = &new_dist, }); - if (rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(CTX_wm_area(C), region); } @@ -3721,7 +3744,7 @@ void VIEW3D_OT_zoom_border(wmOperatorType *ot) ot->modal = WM_gesture_box_modal; ot->cancel = WM_gesture_box_cancel; - ot->poll = ED_operator_region_view3d_active; + ot->poll = view3d_zoom_or_dolly_poll; /* flags */ ot->flag = 0; @@ -3834,7 +3857,7 @@ static void axis_set_view(bContext *C, rv3d->view_axis_roll = view_axis_roll; } - if (rv3d->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) { ED_region_tag_redraw(region); return; } @@ -4058,7 +4081,7 @@ static int view_camera_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_force_finish(C, v3d, region); - if ((rv3d->viewlock & RV3D_LOCKED) == 0) { + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) { /* lastview - */ ViewLayer *view_layer = CTX_data_view_layer(C); @@ -4207,7 +4230,7 @@ static int vieworbit_exec(bContext *C, wmOperator *op) RV3D_VIEW_USER; orbitdir = RNA_enum_get(op->ptr, "type"); - if ((rv3d->viewlock & RV3D_LOCKED) && (view_opposite == RV3D_VIEW_USER)) { + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) && (view_opposite == RV3D_VIEW_USER)) { /* no NULL check is needed, poll checks */ ED_view3d_context_user_region(C, &v3d, ®ion); rv3d = region->regiondata; @@ -4215,7 +4238,7 @@ static int vieworbit_exec(bContext *C, wmOperator *op) ED_view3d_smooth_view_force_finish(C, v3d, region); - if ((rv3d->viewlock & RV3D_LOCKED) == 0 || (view_opposite != RV3D_VIEW_USER)) { + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) { if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { int smooth_viewtx = WM_operator_smooth_viewtx_get(op); float quat_mul[4]; @@ -4352,7 +4375,7 @@ static void viewroll_apply(ViewOpsData *vod, int x, int UNUSED(y)) vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs); } - if (vod->rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(vod->sa, vod->region); } @@ -4621,7 +4644,7 @@ void VIEW3D_OT_view_pan(wmOperatorType *ot) /* api callbacks */ ot->invoke = viewpan_invoke; - ot->poll = ED_operator_region_view3d_active; + ot->poll = view3d_pan_poll; /* flags */ ot->flag = 0; @@ -4647,7 +4670,8 @@ static int viewpersportho_exec(bContext *C, wmOperator *UNUSED(op)) ED_view3d_context_user_region(C, &v3d_dummy, ®ion); rv3d = region->regiondata; - if ((rv3d->viewlock & RV3D_LOCKED) == 0) { + /* Could add a separate lock flag for locking persp. */ + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) { if (rv3d->persp != RV3D_ORTHO) { rv3d->persp = RV3D_ORTHO; } diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index eb7b1412fde..b32bcbde3e9 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -1039,7 +1039,7 @@ static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event) RegionView3D *rv3d = CTX_wm_region_view3d(C); FlyInfo *fly; - if (rv3d->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c index 422cba7c52e..91a2ee297ad 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c @@ -252,14 +252,14 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g (navgroup->state.rect_visible.ymax == rect_visible->ymax) && (navgroup->state.rv3d.is_persp == rv3d->is_persp) && (navgroup->state.rv3d.is_camera == (rv3d->persp == RV3D_CAMOB)) && - (navgroup->state.rv3d.viewlock == rv3d->viewlock)) { + (navgroup->state.rv3d.viewlock == RV3D_LOCK_FLAGS(rv3d))) { return; } navgroup->state.rect_visible = *rect_visible; navgroup->state.rv3d.is_persp = rv3d->is_persp; navgroup->state.rv3d.is_camera = (rv3d->persp == RV3D_CAMOB); - navgroup->state.rv3d.viewlock = rv3d->viewlock; + navgroup->state.rv3d.viewlock = RV3D_LOCK_FLAGS(rv3d); const bool show_navigate = (U.uiflag & USER_SHOW_GIZMO_NAVIGATE) != 0; const bool show_rotate_gizmo = (U.mini_axis_type == USER_MINI_AXIS_TYPE_GIZMO); @@ -296,7 +296,6 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true); } - /* RV3D_LOCKED or Camera: only show supported buttons. */ if (show_rotate_gizmo) { gz = navgroup->gz_array[GZ_INDEX_ROTATE]; gz->matrix_basis[3][0] = co_rotate[0]; @@ -306,26 +305,30 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g if (show_navigate) { int icon_mini_slot = 0; - gz = navgroup->gz_array[GZ_INDEX_ZOOM]; - gz->matrix_basis[3][0] = roundf(co[0]); - gz->matrix_basis[3][1] = roundf(co[1] - (icon_offset_mini * icon_mini_slot++)); - WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false); - - gz = navgroup->gz_array[GZ_INDEX_MOVE]; - gz->matrix_basis[3][0] = roundf(co[0]); - gz->matrix_basis[3][1] = roundf(co[1] - (icon_offset_mini * icon_mini_slot++)); - WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false); + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ZOOM_AND_DOLLY) == 0) { + gz = navgroup->gz_array[GZ_INDEX_ZOOM]; + gz->matrix_basis[3][0] = roundf(co[0]); + gz->matrix_basis[3][1] = roundf(co[1] - (icon_offset_mini * icon_mini_slot++)); + WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false); + } - if ((rv3d->viewlock & RV3D_LOCKED) == 0) { - gz = navgroup->gz_array[GZ_INDEX_CAMERA]; + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_LOCATION) == 0) { + gz = navgroup->gz_array[GZ_INDEX_MOVE]; gz->matrix_basis[3][0] = roundf(co[0]); gz->matrix_basis[3][1] = roundf(co[1] - (icon_offset_mini * icon_mini_slot++)); WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false); + } + + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + gz = navgroup->gz_array[GZ_INDEX_CAMERA]; + gz->matrix_basis[3][0] = co[0]; + gz->matrix_basis[3][1] = co[1] - (icon_offset_mini * icon_mini_slot++); + WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false); if (navgroup->state.rv3d.is_camera == false) { gz = navgroup->gz_array[rv3d->is_persp ? GZ_INDEX_PERSP : GZ_INDEX_ORTHO]; - gz->matrix_basis[3][0] = roundf(co[0]); - gz->matrix_basis[3][1] = roundf(co[1] - (icon_offset_mini * icon_mini_slot++)); + gz->matrix_basis[3][0] = co[0]; + gz->matrix_basis[3][1] = co[1] - (icon_offset_mini * icon_mini_slot++); WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false); } } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index bad283740c8..55778db7353 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -459,7 +459,7 @@ bool ED_view3d_persp_ensure(const Depsgraph *depsgraph, View3D *v3d, ARegion *re RegionView3D *rv3d = region->regiondata; const bool autopersp = (U.uiflag & USER_AUTOPERSP) != 0; - BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0); + BLI_assert((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0); if (ED_view3d_camera_lock_check(v3d, rv3d)) { return false; @@ -679,7 +679,7 @@ static void view3d_boxview_clip(ScrArea *sa) if (region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; - if (rv3d->viewlock & RV3D_BOXCLIP) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXCLIP) { if (ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) { if (region->winx > region->winy) { x1 = rv3d->dist; @@ -751,7 +751,7 @@ static void view3d_boxview_clip(ScrArea *sa) if (region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; - if (rv3d->viewlock & RV3D_BOXCLIP) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXCLIP) { rv3d->rflag |= RV3D_CLIPPING; memcpy(rv3d->clip, clip, sizeof(clip)); if (rv3d->clipbb) { @@ -822,10 +822,10 @@ void view3d_boxview_sync(ScrArea *sa, ARegion *region) if (artest != region && artest->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3dtest = artest->regiondata; - if (rv3dtest->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(rv3dtest) & RV3D_LOCK_ROTATION) { rv3dtest->dist = rv3d->dist; view3d_boxview_sync_axis(rv3dtest, rv3d); - clip |= rv3dtest->viewlock & RV3D_BOXCLIP; + clip |= RV3D_LOCK_FLAGS(rv3dtest) & RV3D_BOXCLIP; ED_region_tag_redraw(artest); } @@ -848,12 +848,12 @@ void view3d_boxview_copy(ScrArea *sa, ARegion *region) if (artest != region && artest->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3dtest = artest->regiondata; - if (rv3dtest->viewlock) { + if (RV3D_LOCK_FLAGS(rv3dtest)) { rv3dtest->dist = rv3d->dist; copy_v3_v3(rv3dtest->ofs, rv3d->ofs); ED_region_tag_redraw(artest); - clip |= ((rv3dtest->viewlock & RV3D_BOXCLIP) != 0); + clip |= ((RV3D_LOCK_FLAGS(rv3dtest) & RV3D_BOXCLIP) != 0); } } } @@ -874,7 +874,7 @@ void ED_view3d_quadview_update(ScrArea *sa, ARegion *region, bool do_clip) * properties are always being edited, weak */ viewlock = rv3d->viewlock; - if ((viewlock & RV3D_LOCKED) == 0) { + if ((viewlock & RV3D_LOCK_ROTATION) == 0) { do_clip = (viewlock & RV3D_BOXCLIP) != 0; viewlock = 0; } @@ -899,12 +899,12 @@ void ED_view3d_quadview_update(ScrArea *sa, ARegion *region, bool do_clip) } } - if (rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(sa, ar_sync ? ar_sync : sa->regionbase.last); } /* ensure locked regions have an axis, locked user views don't make much sense */ - if (viewlock & RV3D_LOCKED) { + if (viewlock & RV3D_LOCK_ROTATION) { int index_qsplit = 0; for (region = sa->regionbase.first; region; region = region->next) { if (region->alignment == RGN_ALIGN_QSPLIT) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index b27bdf93389..159ea249337 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -36,6 +36,7 @@ #include "BKE_action.h" #include "BKE_camera.h" #include "BKE_context.h" +#include "BKE_idprop.h" #include "BKE_object.h" #include "BKE_global.h" #include "BKE_layer.h" @@ -228,7 +229,7 @@ void ED_view3d_smooth_view_ex( ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); } /* grid draw as floor */ - if ((rv3d->viewlock & RV3D_LOCKED) == 0) { + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { /* use existing if exists, means multiple calls to smooth view * wont loose the original 'view' setting */ rv3d->view = RV3D_VIEW_USER; @@ -291,7 +292,7 @@ void ED_view3d_smooth_view_ex( ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); } - if (rv3d->viewlock & RV3D_BOXVIEW) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { view3d_boxview_copy(sa, region); } @@ -344,7 +345,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } - if ((rv3d->viewlock & RV3D_LOCKED) == 0) { + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { rv3d->view = sms->org_view; } @@ -384,7 +385,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b WM_event_add_mousemove(CTX_wm_window(C)); } - if (sync_boxview && (rv3d->viewlock & RV3D_BOXVIEW)) { + if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { view3d_boxview_copy(CTX_wm_area(C), region); } @@ -494,7 +495,7 @@ static bool view3d_camera_to_view_poll(bContext *C) if (ED_view3d_context_user_region(C, &v3d, ®ion)) { RegionView3D *rv3d = region->regiondata; if (v3d && v3d->camera && !ID_IS_LINKED(v3d->camera)) { - if (rv3d && (rv3d->viewlock & RV3D_LOCKED) == 0) { + if (rv3d && (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) == 0) { if (rv3d->persp != RV3D_CAMOB) { return 1; } @@ -826,7 +827,7 @@ void view3d_viewmatrix_set(Depsgraph *depsgraph, bool use_lock_ofs = false; /* should be moved to better initialize later on XXX */ - if (rv3d->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) { ED_view3d_lock(rv3d); } @@ -991,6 +992,7 @@ int view3d_opengl_select(ViewContext *vc, eV3DSelectObjectFilter select_filter) { struct bThemeState theme_state; + const wmWindowManager *wm = CTX_wm_manager(vc->C); Depsgraph *depsgraph = vc->depsgraph; Scene *scene = vc->scene; View3D *v3d = vc->v3d; @@ -1097,7 +1099,7 @@ int view3d_opengl_select(ViewContext *vc, /* Important we use the 'viewmat' and don't re-calculate since * the object & bone view locking takes 'rect' into account, see: T51629. */ ED_view3d_draw_setup_view( - vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, &rect); + wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, &rect); if (!XRAY_ACTIVE(v3d)) { GPU_depth_test(true); @@ -1165,7 +1167,8 @@ int view3d_opengl_select(ViewContext *vc, } G.f &= ~G_FLAG_PICKSEL; - ED_view3d_draw_setup_view(vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, NULL); + ED_view3d_draw_setup_view( + wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, NULL); if (!XRAY_ACTIVE(v3d)) { GPU_depth_test(false); @@ -1684,3 +1687,83 @@ void ED_view3d_local_collections_reset(struct bContext *C, const bool reset_all) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Functionality + * \{ */ + +#ifdef WITH_XR_OPENXR + +static void view3d_xr_mirror_begin(RegionView3D *rv3d) +{ + /* If there is no session yet, changes below should not be applied! */ + BLI_assert(WM_xr_session_exists(&((wmWindowManager *)G_MAIN->wm.first)->xr)); + + rv3d->runtime_viewlock |= RV3D_LOCK_ANY_TRANSFORM; + /* Force perspective view. This isn't reset but that's not really an issue. */ + rv3d->persp = RV3D_PERSP; +} + +static void view3d_xr_mirror_end(RegionView3D *rv3d) +{ + rv3d->runtime_viewlock &= ~RV3D_LOCK_ANY_TRANSFORM; +} + +void ED_view3d_xr_mirror_update(const ScrArea *area, const View3D *v3d, const bool enable) +{ + ARegion *region_rv3d; + + BLI_assert(v3d->spacetype == SPACE_VIEW3D); + + if (ED_view3d_area_user_region(area, v3d, ®ion_rv3d)) { + if (enable) { + view3d_xr_mirror_begin(region_rv3d->regiondata); + } + else { + view3d_xr_mirror_end(region_rv3d->regiondata); + } + } +} + +void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const Scene *scene) +{ + if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) { + View3DShading *xr_shading = &wm->xr.session_settings.shading; + + BLI_assert(WM_xr_session_exists(&wm->xr)); + + if (v3d->shading.type == OB_RENDER) { + if (!(BKE_scene_uses_blender_workbench(scene) || BKE_scene_uses_blender_eevee(scene))) { + /* Keep old shading while using Cycles or another engine, they are typically not usable in + * VR. */ + return; + } + } + + if (xr_shading->prop) { + IDP_FreeProperty(xr_shading->prop); + xr_shading->prop = NULL; + } + + /* Copy shading from View3D to VR view. */ + *xr_shading = v3d->shading; + if (v3d->shading.prop) { + xr_shading->prop = IDP_CopyProperty(xr_shading->prop); + } + } +} + +bool ED_view3d_is_region_xr_mirror_active(const wmWindowManager *wm, + const View3D *v3d, + const ARegion *region) +{ + return (v3d->flag & V3D_XR_SESSION_MIRROR) && + /* The free region (e.g. the camera region in quad-view) is always the last in the list + base. We don't want any other to be affected. */ + !region->next && // + WM_xr_session_is_ready(&wm->xr); +} + +#endif + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 9f488a1ef78..e25f8b4b8ad 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -1347,7 +1347,7 @@ static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event) RegionView3D *rv3d = CTX_wm_region_view3d(C); WalkInfo *walk; - if (rv3d->viewlock & RV3D_LOCKED) { + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ANY_TRANSFORM) { return OPERATOR_CANCELLED; } diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 986d9720ab9..848da778d1c 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -104,6 +104,9 @@ GPUViewport *GPU_viewport_create(void); void GPU_viewport_bind(GPUViewport *viewport, const rcti *rect); void GPU_viewport_unbind(GPUViewport *viewport); void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect); +void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, + const rcti *rect, + bool display_colorspace); void GPU_viewport_free(GPUViewport *viewport); void GPU_viewport_colorspace_set(GPUViewport *viewport, diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 8ccfa3d9898..e4fd5d3f122 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -532,7 +532,13 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, } } -void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect) +/** + * Version of #GPU_viewport_draw_to_screen() that lets caller decide if display colorspace + * transform should be performed. + */ +void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, + const rcti *rect, + bool display_colorspace) { DefaultFramebufferList *dfbl = viewport->fbl; DefaultTextureList *dtxl = viewport->txl; @@ -545,18 +551,22 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect) const float w = (float)GPU_texture_width(color); const float h = (float)GPU_texture_height(color); - BLI_assert(w == BLI_rcti_size_x(rect) + 1); - BLI_assert(h == BLI_rcti_size_y(rect) + 1); + /* We allow rects with min/max swapped, but we also need coorectly assigned coordinates. */ + rcti sanitized_rect = *rect; + BLI_rcti_sanitize(&sanitized_rect); + + BLI_assert(w == BLI_rcti_size_x(&sanitized_rect) + 1); + BLI_assert(h == BLI_rcti_size_y(&sanitized_rect) + 1); /* wmOrtho for the screen has this same offset */ const float halfx = GLA_PIXEL_OFS / w; const float halfy = GLA_PIXEL_OFS / h; rctf pos_rect = { - .xmin = rect->xmin, - .ymin = rect->ymin, - .xmax = rect->xmin + w, - .ymax = rect->ymin + h, + .xmin = sanitized_rect.xmin, + .ymin = sanitized_rect.ymin, + .xmax = sanitized_rect.xmin + w, + .ymax = sanitized_rect.ymin + h, }; rctf uv_rect = { @@ -565,8 +575,28 @@ void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect) .xmax = halfx + 1.0f, .ymax = halfy + 1.0f, }; + /* Mirror the UV rect in case axis-swapped drawing is requested (by passing a rect with min and + * max values swapped). */ + if (BLI_rcti_size_x(rect) < 0) { + SWAP(float, uv_rect.xmin, uv_rect.xmax); + } + if (BLI_rcti_size_y(rect) < 0) { + SWAP(float, uv_rect.ymin, uv_rect.ymax); + } + + gpu_viewport_draw_colormanaged(viewport, &pos_rect, &uv_rect, display_colorspace); +} - gpu_viewport_draw_colormanaged(viewport, &pos_rect, &uv_rect, true); +/** + * Merge and draw the buffers of \a viewport into the currently active framebuffer, performing + * color transform to display space. + * + * \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done with + * inversed axis coordinates (upside down or sideways). + */ +void GPU_viewport_draw_to_screen(GPUViewport *viewport, const rcti *rect) +{ + GPU_viewport_draw_to_screen_ex(viewport, rect, true); } /** diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h index 85522ea88a2..f8c772422bb 100644 --- a/source/blender/makesdna/DNA_view3d_enums.h +++ b/source/blender/makesdna/DNA_view3d_enums.h @@ -26,6 +26,7 @@ typedef enum eV3DOffscreenDrawFlag { V3D_OFSDRAW_NONE = (0), V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0), V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1), + V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2), } eV3DOffscreenDrawFlag; /** #View3DShading.light */ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index a25d66cbc9b..3dc29d40bc1 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -106,10 +106,12 @@ typedef struct RegionView3D { char persp; char view; char view_axis_roll; - char viewlock; + char viewlock; /* Should usually be accessed with RV3D_LOCK_FLAGS()! */ + /** Options for runtime only locking (cleared on file read) */ + char runtime_viewlock; /* Should usually be accessed with RV3D_LOCK_FLAGS()! */ /** Options for quadview (store while out of quad view). */ char viewlock_quad; - char _pad[2]; + char _pad[1]; /** Normalized offset for locked view: (-1, -1) bottom left, (1, 1) upper right. */ float ofs_lock[2]; @@ -232,6 +234,10 @@ typedef struct View3DOverlay { typedef struct View3D_Runtime { /** Nkey panel stores stuff here. */ void *properties_storage; + /** Runtime only flags. */ + int flag; + + char _pad1[4]; } View3D_Runtime; /** 3D ViewPort Struct. */ @@ -342,6 +348,7 @@ typedef struct View3D { #define V3D_FLAG_UNUSED_1 (1 << 1) /* cleared */ #define V3D_HIDE_HELPLINES (1 << 2) #define V3D_INVALID_BACKBUF (1 << 3) +#define V3D_XR_SESSION_MIRROR (1 << 4) #define V3D_FLAG_UNUSED_10 (1 << 10) /* cleared */ #define V3D_SELECT_OUTLINE (1 << 11) @@ -349,6 +356,12 @@ typedef struct View3D { #define V3D_GLOBAL_STATS (1 << 13) #define V3D_DRAW_CENTERS (1 << 15) +/** #View3D_Runtime.flag */ +enum { + /** The 3D view which the XR session was created in is flagged with this. */ + V3D_RUNTIME_XR_SESSION_ROOT = (1 << 0), +}; + /** #RegionView3D.persp */ #define RV3D_ORTHO 0 #define RV3D_PERSP 1 @@ -367,9 +380,19 @@ typedef struct View3D { #define RV3D_ZOFFSET_DISABLED 64 /** #RegionView3D.viewlock */ -#define RV3D_LOCKED (1 << 0) -#define RV3D_BOXVIEW (1 << 1) -#define RV3D_BOXCLIP (1 << 2) +enum { + RV3D_LOCK_ROTATION = (1 << 0), + RV3D_BOXVIEW = (1 << 1), + RV3D_BOXCLIP = (1 << 2), + RV3D_LOCK_LOCATION = (1 << 3), + RV3D_LOCK_ZOOM_AND_DOLLY = (1 << 4), + + RV3D_LOCK_ANY_TRANSFORM = (RV3D_LOCK_LOCATION | RV3D_LOCK_ROTATION | RV3D_LOCK_ZOOM_AND_DOLLY), +}; + +/* Bitwise OR of the regular lock-flags with runtime only lock-flags. */ +#define RV3D_LOCK_FLAGS(rv3d) ((rv3d)->viewlock | ((rv3d)->runtime_viewlock)) + /** #RegionView3D.viewlock_quad */ #define RV3D_VIEWLOCK_INIT (1 << 7) diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 1eaf3777d0a..321e3925155 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -28,6 +28,7 @@ #include "DNA_screen_types.h" #include "DNA_vec_types.h" #include "DNA_userdef_types.h" +#include "DNA_xr_types.h" #include "DNA_ID.h" @@ -119,6 +120,16 @@ typedef struct ReportTimerInfo { float widthfac; } ReportTimerInfo; +//#ifdef WITH_XR_OPENXR +typedef struct wmXrData { + /** Runtime information for managing Blender specific behaviors. */ + struct wmXrRuntimeData *runtime; + /** Permanent session settings (draw mode, feature toggles, etc). Stored in files and accessible + * even before the session runs. */ + XrSessionSettings session_settings; +} wmXrData; +//#endif + /* reports need to be before wmWindowManager */ /* windowmanager is saved, tag WMAN */ @@ -180,6 +191,9 @@ typedef struct wmWindowManager { struct wmMsgBus *message_bus; + //#ifdef WITH_XR_OPENXR + wmXrData xr; + //#endif } wmWindowManager; /* wmWindowManager.initialized */ diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h new file mode 100644 index 00000000000..a026f7554cb --- /dev/null +++ b/source/blender/makesdna/DNA_xr_types.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup DNA + */ + +#ifndef __DNA_XR_TYPES_H__ +#define __DNA_XR_TYPES_H__ + +#include "DNA_view3d_types.h" + +typedef struct XrSessionSettings { + /** Shading settings, struct shared with 3D-View so settings are the same. */ + struct View3DShading shading; + + char _pad[7]; + + char base_pose_type; /* eXRSessionBasePoseType */ + /** Object to take the location and rotation as base position from. */ + Object *base_pose_object; + float base_pose_location[3]; + float base_pose_angle; + + /** View3D draw flags (V3D_OFSDRAW_NONE, V3D_OFSDRAW_SHOW_ANNOTATION, ...). */ + char draw_flags; + char _pad2[3]; + + /** Clipping distance. */ + float clip_start, clip_end; + + int flag; +} XrSessionSettings; + +typedef enum eXrSessionFlag { + XR_SESSION_USE_POSITION_TRACKING = (1 << 0), +} eXrSessionFlag; + +typedef enum eXRSessionBasePoseType { + XR_BASE_POSE_SCENE_CAMERA = 0, + XR_BASE_POSE_OBJECT = 1, + XR_BASE_POSE_CUSTOM = 2, +} eXRSessionBasePoseType; + +#endif /* __DNA_XR_TYPES_H__ */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 6b4b4854515..a33f13be09a 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -132,6 +132,7 @@ static const char *includefiles[] = { "DNA_workspace_types.h", "DNA_lightprobe_types.h", "DNA_curveprofile_types.h", + "DNA_xr_types.h", /* see comment above before editing! */ @@ -1598,6 +1599,7 @@ int main(int argc, char **argv) #include "DNA_workspace_types.h" #include "DNA_lightprobe_types.h" #include "DNA_curveprofile_types.h" +#include "DNA_xr_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index fa8cf6903ad..1e07da23429 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -694,6 +694,8 @@ extern StructRNA RNA_WorkSpace; extern StructRNA RNA_World; extern StructRNA RNA_WorldLighting; extern StructRNA RNA_WorldMistSettings; +extern StructRNA RNA_XrSessionSettings; +extern StructRNA RNA_XrSessionState; extern StructRNA RNA_uiPopover; extern StructRNA RNA_wmOwnerIDs; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index a813fe12c02..30989a62603 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -92,6 +92,7 @@ set(DEFSRC rna_wm_gizmo.c rna_workspace.c rna_world.c + rna_xr.c ) set(APISRC @@ -325,6 +326,10 @@ if(WITH_INPUT_NDOF) add_definitions(-DWITH_INPUT_NDOF) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + # Build makesrna executable blender_include_dirs( . diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 0f69adf6298..7ec8f6167d0 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4312,6 +4312,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_movieclip.c", NULL, RNA_def_movieclip}, {"rna_tracking.c", NULL, RNA_def_tracking}, {"rna_mask.c", NULL, RNA_def_mask}, + {"rna_xr.c", NULL, RNA_def_xr}, {NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 6bda8ebdb4c..3e18e882e2b 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -205,6 +205,7 @@ void RNA_def_world(struct BlenderRNA *brna); void RNA_def_movieclip(struct BlenderRNA *brna); void RNA_def_tracking(struct BlenderRNA *brna); void RNA_def_mask(struct BlenderRNA *brna); +void RNA_def_xr(struct BlenderRNA *brna); /* Common Define functions */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 66aeab725ea..b4bb141ba7a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -997,7 +997,7 @@ static IDProperty *rna_View3DShading_idprops(PointerRNA *ptr, bool create) static void rna_3DViewShading_type_update(Main *bmain, Scene *scene, PointerRNA *ptr) { ID *id = ptr->owner_id; - if (GS(id->name) == ID_SCE) { + if (GS(id->name) != ID_SCR) { return; } @@ -1333,6 +1333,25 @@ static const EnumPropertyItem *rna_SpaceView3D_stereo3d_camera_itemf(bContext *C } } +static void rna_SpaceView3D_mirror_xr_session_update(Main *main, + Scene *UNUSED(scene), + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = main->wm.first; + + /* Handle mirror toggling while there is a session already. */ + if (WM_xr_session_exists(&wm->xr)) { + const View3D *v3d = ptr->data; + const ScrArea *area = rna_area_from_space(ptr); + ED_view3d_xr_mirror_update(area, v3d, v3d->flag & V3D_XR_SESSION_MIRROR); + } + +# else + UNUSED_VARS(main, ptr); +# endif +} + static int rna_SpaceView3D_icon_from_show_object_viewport_get(PointerRNA *ptr) { const View3D *v3d = (View3D *)ptr->data; @@ -3166,19 +3185,20 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) "rna_3DViewShading_type_itemf"); RNA_def_property_ui_text( prop, "Viewport Shading", "Method to display/shade objects in the 3D View"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_3DViewShading_type_update"); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, "rna_3DViewShading_type_update"); prop = RNA_def_property(srna, "light", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "light"); RNA_def_property_enum_items(prop, rna_enum_viewport_lighting_items); RNA_def_property_ui_text(prop, "Lighting", "Lighting Method for Solid/Texture Viewport Shading"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_object_outline", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_OBJECT_OUTLINE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Outline", "Show Object Outline"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "studio_light", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_studio_light_items); @@ -3188,45 +3208,45 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) "rna_View3DShading_studio_light_set", "rna_View3DShading_studio_light_itemf"); RNA_def_property_ui_text(prop, "Studiolight", "Studio lighting setup"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "use_world_space_lighting", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_WORLD_ORIENTATION); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text( prop, "World Space Lighting", "Make the lighting fixed and not follow the camera"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_backface_culling", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_BACKFACE_CULLING); RNA_def_property_ui_text( prop, "Backface Culling", "Use back face culling to hide the back side of faces"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_cavity", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_CAVITY); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Cavity", "Show Cavity"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "cavity_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, cavity_type_items); RNA_def_property_ui_text(prop, "Cavity Type", "Way to draw the cavity shading"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "curvature_ridge_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "curvature_ridge_factor"); RNA_def_property_ui_text(prop, "Curvature Ridge", "Factor for the curvature ridges"); RNA_def_property_range(prop, 0.0f, 2.0f); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "curvature_valley_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "curvature_valley_factor"); RNA_def_property_ui_text(prop, "Curvature Valley", "Factor for the curvature valleys"); RNA_def_property_range(prop, 0.0f, 2.0f); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "cavity_ridge_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "cavity_ridge_factor"); @@ -3234,7 +3254,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 250.0f); RNA_def_property_ui_range(prop, 0.00f, 2.5f, 1, 3); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "cavity_valley_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "cavity_valley_factor"); @@ -3242,7 +3262,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 250.0f); RNA_def_property_ui_range(prop, 0.00f, 2.5f, 1, 3); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "selected_studio_light", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "StudioLight"); @@ -3259,7 +3279,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) prop, "Studiolight Rotation", "Rotation of the studiolight around the Z-Axis"); RNA_def_property_range(prop, -M_PI, M_PI); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "studiolight_intensity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "studiolight_intensity"); @@ -3267,7 +3287,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Strength", "Strength of the studiolight"); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_range(prop, 0.0f, 2.0f, 1, 3); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "studiolight_background_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "studiolight_background"); @@ -3275,7 +3295,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "studiolight_background_blur", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "studiolight_blur"); @@ -3284,7 +3304,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 2); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "color_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "color_type"); @@ -3292,64 +3312,65 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_View3DShading_color_type_itemf"); RNA_def_property_ui_text(prop, "Color", "Color Type"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update"); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, "rna_GPencil_update"); prop = RNA_def_property(srna, "wireframe_color_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "wire_color_type"); RNA_def_property_enum_items(prop, rna_enum_shading_color_type_items); RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_View3DShading_color_type_itemf"); RNA_def_property_ui_text(prop, "Color", "Color Type"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "single_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "single_color"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Color", "Color for single color mode"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "background_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, background_type_items); RNA_def_property_ui_text(prop, "Background", "Way to draw the background"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "background_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Background Color", "Color for custom background color"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_shadows", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_SHADOW); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Shadow", "Show Shadow"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_xray", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_XRAY); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Show X-Ray", "Show whole scene transparent"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_xray_wireframe", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_XRAY_WIREFRAME); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Show X-Ray", "Show whole scene transparent"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "xray_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "xray_alpha"); RNA_def_property_ui_text(prop, "X-Ray Alpha", "Amount of alpha to use"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "xray_alpha_wireframe", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "xray_alpha_wire"); RNA_def_property_ui_text(prop, "X-Ray Alpha", "Amount of alpha to use"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "use_dof", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_DEPTH_OF_FIELD); @@ -3358,46 +3379,46 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) prop, "Depth Of Field", "Use depth of field on viewport using the values from the active camera"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "use_scene_lights", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_SCENE_LIGHTS); RNA_def_property_boolean_default(prop, false); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Scene Lights", "Render lights and light probes of the scene"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "use_scene_world", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_SCENE_WORLD); RNA_def_property_boolean_default(prop, false); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Scene World", "Use scene world for lighting"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "use_scene_lights_render", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_SCENE_LIGHTS_RENDER); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Scene Lights", "Render lights and light probes of the scene"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "use_scene_world_render", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_SCENE_WORLD_RENDER); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Scene World", "Use scene world for lighting"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "show_specular_highlight", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_SPECULAR_HIGHLIGHT); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Specular Highlights", "Render specular highlights"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "object_outline_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_float_sdna(prop, NULL, "object_outline_color"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Outline Color", "Color for object outline"); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "shadow_intensity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "shadow_intensity"); @@ -3405,7 +3426,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.00f, 1.0f, 1, 3); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); prop = RNA_def_property(srna, "render_pass", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "render_pass"); @@ -3413,7 +3434,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Render Pass", "Render Pass to show in the viewport"); RNA_def_property_enum_funcs( prop, "rna_3DViewShading_render_pass_get", NULL, "rna_3DViewShading_render_pass_itemf"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); } static void rna_def_space_view3d_overlay(BlenderRNA *brna) @@ -4189,6 +4210,15 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Volume Alpha", "Opacity (alpha) of the cameras' frustum volume"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "mirror_xr_session", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_XR_SESSION_MIRROR); + RNA_def_property_ui_text( + prop, + "Mirror VR Session", + "Synchronize the viewer perspective of virtual reality sessions with this 3D viewport"); + RNA_def_property_update( + prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_SpaceView3D_mirror_xr_session_update"); + { struct { const char *name; @@ -4268,7 +4298,7 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "3D View Region", "3D View region data"); prop = RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_LOCKED); + RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_LOCK_ROTATION); RNA_def_property_ui_text(prop, "Lock", "Lock view rotation in side views"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_update"); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 929540f10ac..261f1fb9d3b 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1266,6 +1266,20 @@ static void rna_wmClipboard_set(PointerRNA *UNUSED(ptr), const char *value) WM_clipboard_text_set((void *)value, false); } +static PointerRNA rna_WindowManager_xr_session_state_get(PointerRNA *ptr) +{ + wmWindowManager *wm = ptr->data; + struct wmXrSessionState *state = +# ifdef WITH_XR_OPENXR + WM_xr_session_state_handle_get(&wm->xr); +# else + NULL; + UNUSED_VAR(wm); +# endif + + return rna_pointer_inherit_refine(ptr, &RNA_XrSessionState, state); +} + # ifdef WITH_PYTHON static bool rna_operator_poll_cb(bContext *C, wmOperatorType *ot) @@ -2484,6 +2498,18 @@ static void rna_def_windowmanager(BlenderRNA *brna) prop, "rna_wmClipboard_get", "rna_wmClipboard_length", "rna_wmClipboard_set"); RNA_def_property_ui_text(prop, "Text Clipboard", ""); + prop = RNA_def_property(srna, "xr_session_settings", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "xr.session_settings"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "XR Session Settings", ""); + + prop = RNA_def_property(srna, "xr_session_state", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "XrSessionState"); + RNA_def_property_pointer_funcs(prop, "rna_WindowManager_xr_session_state_get", NULL, NULL, NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "XR Session State", "Runtime state information about the VR session"); + RNA_api_wm(srna); } diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index da12c782052..47d793382a7 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -1376,6 +1376,12 @@ static void rna_def_gizmogroup(BlenderRNA *brna) 0, "Tool Init", "Postpone running until tool operator run (when used with a tool)"}, + {WM_GIZMOGROUPTYPE_VR_REDRAWS, + "VR_REDRAWS", + 0, + "VR Redraws", + "The gizmos are made for use with virtual reality sessions and require special redraw " + "management"}, {0, NULL, 0, NULL, NULL}, }; prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c new file mode 100644 index 00000000000..76cbc99b397 --- /dev/null +++ b/source/blender/makesrna/intern/rna_xr.c @@ -0,0 +1,229 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup RNA + */ + +#include "DNA_view3d_types.h" +#include "DNA_xr_types.h" + +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_types.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +# include "BLI_math.h" + +# include "WM_api.h" + +static bool rna_XrSessionState_is_running(bContext *C) +{ +# ifdef WITH_XR_OPENXR + const wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_session_is_ready(&wm->xr); +# else + UNUSED_VARS(C); + return false; +# endif +} + +# ifdef WITH_XR_OPENXR +static wmXrData *rna_XrSessionState_wm_xr_data_get(PointerRNA *ptr) +{ + /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just + * consistently pass wmXrData pointers to the WM_xr_xxx() API. */ + + BLI_assert(ptr->type == &RNA_XrSessionState); + + wmWindowManager *wm = (wmWindowManager *)ptr->owner_id; + BLI_assert(wm && (GS(wm->id.name) == ID_WM)); + + return &wm->xr; +} +# endif + +static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSessionState_wm_xr_data_get(ptr); + WM_xr_session_state_viewer_pose_location_get(xr, r_values); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSessionState_wm_xr_data_get(ptr); + WM_xr_session_state_viewer_pose_rotation_get(xr, r_values); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +#else /* RNA_RUNTIME */ + +static void rna_def_xr_session_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem base_pose_types[] = { + {XR_BASE_POSE_SCENE_CAMERA, + "SCENE_CAMERA", + 0, + "Scene Camera", + "Follow the active scene camera to define the VR view's base pose"}, + {XR_BASE_POSE_OBJECT, + "OBJECT", + 0, + "Object", + "Follow the transformation of an object to define the VR view's base pose"}, + {XR_BASE_POSE_CUSTOM, + "CUSTOM", + 0, + "Custom", + "Follow a custom transformation to define the VR view's base pose"}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "XrSessionSettings", NULL); + RNA_def_struct_ui_text(srna, "XR Session Settings", ""); + + prop = RNA_def_property(srna, "shading", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "Shading Settings", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "base_pose_type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_items(prop, base_pose_types); + RNA_def_property_ui_text( + prop, + "Base Pose Type", + "Define where the location and rotation for the VR view come from, to which " + "translation and rotation deltas from the VR headset will be applied to"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "base_pose_object", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Base Pose Object", + "Object to take the location and rotation to which translation and " + "rotation deltas from the VR headset will be applied to"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "base_pose_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_ui_text(prop, + "Base Pose Location", + "Coordinates to apply translation deltas from the VR headset to"); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "base_pose_angle", PROP_FLOAT, PROP_AXISANGLE); + RNA_def_property_ui_text( + prop, + "Base Pose Angle", + "Rotation angle around the Z-Axis to apply the rotation deltas from the VR headset to"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR); + RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_ANNOTATION); + RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "clip_start", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 1e-6f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); + RNA_def_property_ui_text(prop, "Clip Start", "VR viewport near clipping distance"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "clip_end", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 1e-6f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); + RNA_def_property_ui_text(prop, "Clip End", "VR viewport far clipping distance"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "use_positional_tracking", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_USE_POSITION_TRACKING); + RNA_def_property_ui_text( + prop, + "Positional Tracking", + "Allow VR headsets to affect the location in virtual space, in addition to the rotation"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); +} + +static void rna_def_xr_session_state(BlenderRNA *brna) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm, *prop; + + srna = RNA_def_struct(brna, "XrSessionState", NULL); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Session State", "Runtime state information about the VR session"); + + func = RNA_def_function(srna, "is_running", "rna_XrSessionState_is_running"); + RNA_def_function_ui_description(func, "Query if the VR session is currently running"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_boolean(func, "result", 0, "Result", ""); + RNA_def_function_return(func, parm); + + prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, + "Viewer Pose Location", + "Last known location of the viewer pose (center between the eyes) in world space"); + + prop = RNA_def_property(srna, "viewer_pose_rotation", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_rotation_get", NULL, NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, + "Viewer Pose Rotation", + "Last known rotation of the viewer pose (center between the eyes) in world space"); +} + +void RNA_def_xr(BlenderRNA *brna) +{ + RNA_define_animate_sdna(false); + + rna_def_xr_session_settings(brna); + rna_def_xr_session_state(brna); + + RNA_define_animate_sdna(true); +} + +#endif /* RNA_RUNTIME */ diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index f09440a1ceb..f128c46ec05 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -257,6 +257,7 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, (float(*)[4])py_mat_projection->matrix, true, true, + true, "", false, self->ofs, diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index e7a4ca9a005..a1b67216f1a 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -75,6 +75,7 @@ set(SRC intern/wm_splash_screen.c intern/wm_stereo.c intern/wm_subwindow.c + intern/wm_surface.c intern/wm_toolsystem.c intern/wm_tooltip.c intern/wm_uilist_type.c @@ -101,6 +102,7 @@ set(SRC wm_event_system.h wm_event_types.h wm_files.h + wm_surface.h wm_window.h intern/wm_platform_support.h intern/wm_window_private.h @@ -187,4 +189,11 @@ if(WITH_COMPOSITOR) add_definitions(-DWITH_COMPOSITOR) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) + list(APPEND SRC + intern/wm_xr.c + ) +endif() + blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6781f055fc2..1be7a8679c4 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -156,6 +156,10 @@ void *WM_opengl_context_create(void); void WM_opengl_context_dispose(void *context); void WM_opengl_context_activate(void *context); void WM_opengl_context_release(void *context); +#ifdef WIN32 +void *WM_directx_context_create(void); +void WM_directx_context_dispose(void *context); +#endif struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect); struct wmWindow *WM_window_open_temp(struct bContext *C, @@ -867,6 +871,18 @@ void WM_generic_callback_free(struct wmGenericCallback *callback); void WM_generic_user_data_free(struct wmGenericUserData *user_data); +#ifdef WITH_XR_OPENXR +/* wm_xr.c */ +bool WM_xr_session_exists(const wmXrData *xr); +bool WM_xr_session_is_ready(const wmXrData *xr); +struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr); +bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]); +bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_rotation[4]); +bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, + float r_viewmat[4][4], + float *r_focal_len); +#endif + #ifdef __cplusplus } #endif diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index b9dd6b3923d..1edfe252cf4 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -310,6 +310,7 @@ typedef struct wmNotifier { #define ND_HISTORY (4 << 16) #define ND_JOB (5 << 16) #define ND_UNDO (6 << 16) +#define ND_XR_DATA_CHANGED (7 << 17) /* NC_SCREEN */ #define ND_LAYOUTBROWSE (1 << 16) @@ -437,6 +438,7 @@ typedef struct wmNotifier { /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16 << 8) +#define NS_VIEW3D_SHADING (16 << 9) /* action classification */ #define NOTE_ACTION (0x000000FF) diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index c667dd564f7..04311b9c070 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -139,6 +139,13 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * with click drag events by popping up under the cursor and catching the tweak event. */ WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 8), + + /** + * Cause continuous redraws, i.e. set the region redraw flag on every main loop itertion. This + * should really be avoided by using proper region redraw tagging, notifiers and the message-bus, + * however for VR it's sometimes needed. + */ + WM_GIZMOGROUPTYPE_VR_REDRAWS = (1 << 9), } eWM_GizmoFlagGroupTypeFlag; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 28690fec2d8..d463f1df2e9 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -576,6 +576,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, const int co[2], const int hotspot) { + const wmWindowManager *wm = CTX_wm_manager(C); ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); View3D *v3d = sa->spacedata.first; @@ -588,7 +589,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, BLI_rcti_init_pt_radius(&rect, co, hotspot); ED_view3d_draw_setup_view( - CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect); bool use_select_bias = false; @@ -608,7 +609,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, } ED_view3d_draw_setup_view( - CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); + wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL); if (use_select_bias && (hits > 1)) { float co_direction[3]; diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 2edef54c778..2a4ba7fdb8e 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -379,6 +379,11 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) wm_autosave_timer_ended(wm); } +#ifdef WITH_XR_OPENXR + /* May send notifier, so do before freeing notifier queue. */ + wm_xr_exit(wm); +#endif + while ((win = BLI_pophead(&wm->windows))) { /* prevent draw clear to use screen */ BKE_workspace_active_set(win->workspace_hook, NULL); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 9ccff2a3e3d..a0e8374a46a 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -67,6 +67,7 @@ #include "wm_draw.h" #include "wm_window.h" #include "wm_event_system.h" +#include "wm_surface.h" #ifdef WITH_OPENSUBDIV # include "BKE_subsurf.h" @@ -186,7 +187,10 @@ static void wm_area_mark_invalid_backbuf(ScrArea *sa) } } -static void wm_region_test_gizmo_do_draw(ARegion *region, bool tag_redraw) +static void wm_region_test_gizmo_do_draw(bContext *C, + ScrArea *sa, + ARegion *region, + bool tag_redraw) { if (region->gizmo_map == NULL) { return; @@ -195,10 +199,26 @@ static void wm_region_test_gizmo_do_draw(ARegion *region, bool tag_redraw) wmGizmoMap *gzmap = region->gizmo_map; for (wmGizmoGroup *gzgroup = WM_gizmomap_group_list(gzmap)->first; gzgroup; gzgroup = gzgroup->next) { + if (tag_redraw && (gzgroup->type->flag & WM_GIZMOGROUPTYPE_VR_REDRAWS)) { + ScrArea *ctx_sa = CTX_wm_area(C); + ARegion *ctx_ar = CTX_wm_region(C); + + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, region); + + if (WM_gizmo_group_type_poll(C, gzgroup->type)) { + ED_region_tag_redraw_editor_overlays(region); + } + + /* Reset. */ + CTX_wm_area_set(C, ctx_sa); + CTX_wm_region_set(C, ctx_ar); + } + for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) { if (gz->do_draw) { if (tag_redraw) { - ED_region_tag_redraw_no_rebuild(region); + ED_region_tag_redraw_editor_overlays(region); } gz->do_draw = false; } @@ -237,6 +257,19 @@ static void wm_region_test_render_do_draw(const Scene *scene, } } +#ifdef WITH_XR_OPENXR +static void wm_region_test_xr_do_draw(const wmWindowManager *wm, + const ScrArea *area, + ARegion *region) +{ + if ((area->spacetype == SPACE_VIEW3D) && (region->regiontype == RGN_TYPE_WINDOW)) { + if (ED_view3d_is_region_xr_mirror_active(wm, area->spacedata.first, region)) { + ED_region_tag_redraw_no_rebuild(region); + } + } +} +#endif + static bool wm_region_use_viewport_by_type(short space_type, short region_type) { return (ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE) && region_type == RGN_TYPE_WINDOW); @@ -836,11 +869,26 @@ static void wm_draw_window(bContext *C, wmWindow *win) screen->do_draw = false; } +/** + * Draw offscreen contexts not bound to a specific window. + */ +static void wm_draw_surface(bContext *C, wmSurface *surface) +{ + wm_window_clear_drawable(CTX_wm_manager(C)); + wm_surface_make_drawable(surface); + + surface->draw(C); + + /* Avoid interference with window drawable */ + wm_surface_clear_drawable(); +} + /****************** main update call **********************/ /* quick test to prevent changing window drawable */ -static bool wm_draw_update_test_window(Main *bmain, wmWindow *win) +static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win) { + const wmWindowManager *wm = CTX_wm_manager(C); Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); @@ -861,8 +909,11 @@ static bool wm_draw_update_test_window(Main *bmain, wmWindow *win) ED_screen_areas_iter(win, screen, sa) { for (region = sa->regionbase.first; region; region = region->next) { - wm_region_test_gizmo_do_draw(region, true); + wm_region_test_gizmo_do_draw(C, sa, region, true); wm_region_test_render_do_draw(scene, depsgraph, sa, region); +#ifdef WITH_XR_OPENXR + wm_region_test_xr_do_draw(wm, sa, region); +#endif if (region->visible && region->do_draw) { do_draw = true; @@ -890,19 +941,23 @@ static bool wm_draw_update_test_window(Main *bmain, wmWindow *win) return true; } +#ifndef WITH_XR_OPENXR + UNUSED_VARS(wm); +#endif + return false; } /* Clear drawing flags, after drawing is complete so any draw flags set during * drawing don't cause any additional redraws. */ -static void wm_draw_update_clear_window(wmWindow *win) +static void wm_draw_update_clear_window(bContext *C, wmWindow *win) { bScreen *screen = WM_window_get_active_screen(win); ED_screen_areas_iter(win, screen, sa) { for (ARegion *region = sa->regionbase.first; region; region = region->next) { - wm_region_test_gizmo_do_draw(region, false); + wm_region_test_gizmo_do_draw(C, sa, region, false); } } @@ -944,10 +999,10 @@ void wm_draw_update(bContext *C) } #endif - if (wm_draw_update_test_window(bmain, win)) { - bScreen *screen = WM_window_get_active_screen(win); + CTX_wm_window_set(C, win); - CTX_wm_window_set(C, win); + if (wm_draw_update_test_window(bmain, C, win)) { + bScreen *screen = WM_window_get_active_screen(win); /* sets context window+screen */ wm_window_make_drawable(wm, win); @@ -956,13 +1011,16 @@ void wm_draw_update(bContext *C) ED_screen_ensure_updated(wm, win, screen); wm_draw_window(C, win); - wm_draw_update_clear_window(win); + wm_draw_update_clear_window(C, win); wm_window_swap_buffers(win); - - CTX_wm_window_set(C, NULL); } } + + CTX_wm_window_set(C, NULL); + + /* Draw non-windows (surfaces) */ + wm_surfaces_iter(C, wm_draw_surface); } void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(region)) diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index a87f0a3e42c..7b04b40e626 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -98,6 +98,7 @@ #include "wm_event_system.h" #include "wm.h" #include "wm_files.h" +#include "wm_surface.h" #include "wm_window.h" #include "wm_platform_support.h" @@ -534,6 +535,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_materials_exit(); wm_operatortype_free(); + wm_surfaces_free(); wm_dropbox_free(); WM_menutype_free(); WM_uilisttype_free(); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d649b21569e..1d6ae10c396 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3645,6 +3645,84 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) /** \} */ +#ifdef WITH_XR_OPENXR + +static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) +{ + const bool session_exists = WM_xr_session_exists(xr_data); + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *area = screen->areabase.first; area; area = area->next) { + for (SpaceLink *slink = area->spacedata.first; slink; slink = slink->next) { + if (slink->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)slink; + + if (v3d->flag & V3D_XR_SESSION_MIRROR) { + ED_view3d_xr_mirror_update(area, v3d, session_exists); + } + + if (session_exists) { + wmWindowManager *wm = bmain->wm.first; + const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); + + ED_view3d_xr_shading_update(wm, v3d, scene); + } + /* Ensure no 3D View is tagged as session root. */ + else { + v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; + } + } + } + } + } +} + +static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) +{ + /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ + wm_xr_session_update_screen(G_MAIN, xr_data); +} + +static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + View3D *v3d = CTX_wm_view3d(C); + + /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ + if (wm_xr_init(wm) == false) { + return OPERATOR_CANCELLED; + } + + v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; + wm_xr_session_toggle(wm, wm_xr_session_update_screen_on_exit_cb); + wm_xr_session_update_screen(bmain, &wm->xr); + + WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_session_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle VR Session"; + ot->idname = "WM_OT_xr_session_toggle"; + ot->description = + "Open a view for use with virtual reality headsets, or close it if already " + "opened"; + + /* callbacks */ + ot->exec = wm_xr_session_toggle_exec; + ot->poll = ED_operator_view3d_active; + + /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the + * UI instead. Not meant as a permanent solution. */ + ot->flag = OPTYPE_INTERNAL; +} + +#endif /* WITH_XR_OPENXR */ + /* -------------------------------------------------------------------- */ /** \name Operator Registration & Keymaps * \{ */ @@ -3686,6 +3764,9 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_call_panel); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); +#ifdef WITH_XR_OPENXR + WM_operatortype_append(WM_OT_xr_session_toggle); +#endif #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif diff --git a/source/blender/windowmanager/intern/wm_surface.c b/source/blender/windowmanager/intern/wm_surface.c new file mode 100644 index 00000000000..cb575cc8f7d --- /dev/null +++ b/source/blender/windowmanager/intern/wm_surface.c @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLF_api.h" + +#include "BLI_listbase.h" +#include "BLI_threads.h" + +#include "GHOST_C-api.h" + +#include "GPU_batch_presets.h" +#include "GPU_framebuffer.h" +#include "GPU_immediate.h" +#include "GPU_context.h" + +#include "MEM_guardedalloc.h" + +#include "WM_types.h" +#include "WM_api.h" +#include "wm.h" + +#include "wm_surface.h" + +static ListBase global_surface_list = {NULL, NULL}; +static wmSurface *g_drawable = NULL; + +void wm_surfaces_iter(bContext *C, void (*cb)(bContext *C, wmSurface *)) +{ + for (wmSurface *surf = global_surface_list.first; surf; surf = surf->next) { + cb(C, surf); + } +} + +void wm_surface_clear_drawable(void) +{ + if (g_drawable) { + BLF_batch_reset(); + gpu_batch_presets_reset(); + immDeactivate(); + + g_drawable = NULL; + } +} + +void wm_surface_set_drawable(wmSurface *surface, bool activate) +{ + BLI_assert(ELEM(g_drawable, NULL, surface)); + + g_drawable = surface; + if (activate) { + GHOST_ActivateOpenGLContext(surface->ghost_ctx); + } + + GPU_context_active_set(surface->gpu_ctx); + immActivate(); +} + +void wm_surface_make_drawable(wmSurface *surface) +{ + BLI_assert(GPU_framebuffer_active_get() == NULL); + + if (surface != g_drawable) { + wm_surface_clear_drawable(); + wm_surface_set_drawable(surface, true); + } +} + +void wm_surface_reset_drawable(void) +{ + BLI_assert(BLI_thread_is_main()); + BLI_assert(GPU_framebuffer_active_get() == NULL); + + if (g_drawable) { + wm_surface_clear_drawable(); + wm_surface_set_drawable(g_drawable, true); + } +} + +void wm_surface_add(wmSurface *surface) +{ + BLI_addtail(&global_surface_list, surface); +} + +void wm_surface_remove(wmSurface *surface) +{ + BLI_remlink(&global_surface_list, surface); + surface->free_data(surface); + MEM_freeN(surface); +} + +void wm_surfaces_free(void) +{ + for (wmSurface *surf = global_surface_list.first, *surf_next; surf; surf = surf_next) { + surf_next = surf->next; + wm_surface_remove(surf); + } + + BLI_assert(BLI_listbase_is_empty(&global_surface_list)); +} diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index fe6272686bc..eaa0fa7b7a5 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1631,6 +1631,11 @@ void wm_window_process_events(const bContext *C) GHOST_DispatchEvents(g_system); } hasevent |= wm_window_timer(C); +#ifdef WITH_XR_OPENXR + /* XR events don't use the regular window queues. So here we don't only trigger + * processing/dispatching but also handling. */ + hasevent |= wm_xr_events_handle(CTX_wm_manager(C)); +#endif /* no event, we sleep 5 milliseconds */ if (hasevent == 0) { @@ -1957,6 +1962,9 @@ void wm_window_raise(wmWindow *win) /** \name Window Buffers * \{ */ +/** + * \brief Push rendered buffer to the screen. + */ void wm_window_swap_buffers(wmWindow *win) { GHOST_SwapWindowBuffers(win->ghostwin); @@ -2446,3 +2454,24 @@ void WM_ghost_show_message_box(const char *title, GHOST_ShowMessageBox(g_system, title, message, help_label, continue_label, link, dialog_options); } /** \} */ + +#ifdef WIN32 +/* -------------------------------------------------------------------- */ +/** \name Direct DirectX Context Management + * \{ */ + +void *WM_directx_context_create(void) +{ + BLI_assert(GPU_framebuffer_active_get() == NULL); + return GHOST_CreateDirectXContext(g_system); +} + +void WM_directx_context_dispose(void *context) +{ + BLI_assert(GPU_framebuffer_active_get() == NULL); + GHOST_DisposeDirectXContext(g_system, context); +} + +/** \} */ + +#endif diff --git a/source/blender/windowmanager/intern/wm_xr.c b/source/blender/windowmanager/intern/wm_xr.c new file mode 100644 index 00000000000..bd9fd7cdc3e --- /dev/null +++ b/source/blender/windowmanager/intern/wm_xr.c @@ -0,0 +1,759 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + * + * \name Window-Manager XR API + * + * Implements Blender specific functionality for the GHOST_Xr API. + */ + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "BLI_ghash.h" +#include "BLI_math_geom.h" +#include "BLI_math_matrix.h" + +#include "CLG_log.h" + +#include "DNA_camera_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" +#include "DNA_xr_types.h" + +#include "DRW_engine.h" + +#include "ED_view3d.h" +#include "ED_view3d_offscreen.h" + +#include "GHOST_C-api.h" + +#include "GPU_context.h" +#include "GPU_draw.h" +#include "GPU_matrix.h" +#include "GPU_viewport.h" + +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" + +#include "WM_types.h" +#include "WM_api.h" + +#include "wm.h" +#include "wm_surface.h" +#include "wm_window.h" + +struct wmXrRuntimeData *wm_xr_runtime_data_create(void); +void wm_xr_runtime_data_free(struct wmXrRuntimeData **runtime); +void wm_xr_draw_view(const GHOST_XrDrawViewInfo *, void *); +void *wm_xr_session_gpu_binding_context_create(GHOST_TXrGraphicsBinding); +void wm_xr_session_gpu_binding_context_destroy(GHOST_TXrGraphicsBinding, GHOST_ContextHandle); +wmSurface *wm_xr_session_surface_create(wmWindowManager *, unsigned int); +void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]); + +/* -------------------------------------------------------------------- */ + +typedef struct wmXrSessionState { + bool is_started; + + /** Last known viewer pose (centroid of eyes, in world space) stored for queries. */ + GHOST_XrPose viewer_pose; + /** The last known view matrix, calculated from above's viewer pose. */ + float viewer_viewmat[4][4]; + float focal_len; + + /** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */ + int prev_settings_flag; + /** Copy of wmXrDrawData.eye_position_ofs. */ + float prev_eye_position_ofs[3]; + + bool is_view_data_set; +} wmXrSessionState; + +typedef struct wmXrRuntimeData { + GHOST_XrContextHandle context; + + /* Although this struct is internal, RNA gets a handle to this for state information queries. */ + wmXrSessionState session_state; + wmXrSessionExitFn exit_fn; +} wmXrRuntimeData; + +typedef struct wmXrDrawData { + /** The pose (location + rotation) to which eye deltas will be applied to when drawing (world + * space). With positional tracking enabled, it should be the same as the base pose, when + * disabled it also contains a location delta from the moment the option was toggled. */ + GHOST_XrPose base_pose; + float eye_position_ofs[3]; /* Local/view space. */ +} wmXrDrawData; + +typedef struct { + GHOST_TXrGraphicsBinding gpu_binding_type; + GPUOffScreen *offscreen; + GPUViewport *viewport; + + GHOST_ContextHandle secondary_ghost_ctx; +} wmXrSurfaceData; + +typedef struct { + wmWindowManager *wm; +} wmXrErrorHandlerData; + +/* -------------------------------------------------------------------- */ + +static wmSurface *g_xr_surface = NULL; +static CLG_LogRef LOG = {"wm.xr"}; + +/* -------------------------------------------------------------------- */ +/** \name XR-Context + * + * All XR functionality is accessed through a #GHOST_XrContext handle. + * The lifetime of this context also determines the lifetime of the OpenXR instance, which is the + * representation of the OpenXR runtime connection within the application. + * + * \{ */ + +static void wm_xr_error_handler(const GHOST_XrError *error) +{ + wmXrErrorHandlerData *handler_data = error->customdata; + wmWindowManager *wm = handler_data->wm; + + BKE_reports_clear(&wm->reports); + WM_report(RPT_ERROR, error->user_message); + WM_report_banner_show(); + + if (wm->xr.runtime->context) { + /* Just play safe and destroy the entire runtime data, including context. */ + wm_xr_runtime_data_free(&wm->xr.runtime); + } +} + +bool wm_xr_init(wmWindowManager *wm) +{ + if (wm->xr.runtime && wm->xr.runtime->context) { + return true; + } + static wmXrErrorHandlerData error_customdata; + + /* Set up error handling */ + error_customdata.wm = wm; + GHOST_XrErrorHandler(wm_xr_error_handler, &error_customdata); + + { + const GHOST_TXrGraphicsBinding gpu_bindings_candidates[] = { + GHOST_kXrGraphicsOpenGL, +#ifdef WIN32 + GHOST_kXrGraphicsD3D11, +#endif + }; + GHOST_XrContextCreateInfo create_info = { + .gpu_binding_candidates = gpu_bindings_candidates, + .gpu_binding_candidates_count = ARRAY_SIZE(gpu_bindings_candidates), + }; + GHOST_XrContextHandle context; + + if (G.debug & G_DEBUG_XR) { + create_info.context_flag |= GHOST_kXrContextDebug; + } + if (G.debug & G_DEBUG_XR_TIME) { + create_info.context_flag |= GHOST_kXrContextDebugTime; + } + + if (!(context = GHOST_XrContextCreate(&create_info))) { + return false; + } + + /* Set up context callbacks */ + GHOST_XrGraphicsContextBindFuncs(context, + wm_xr_session_gpu_binding_context_create, + wm_xr_session_gpu_binding_context_destroy); + GHOST_XrDrawViewFunc(context, wm_xr_draw_view); + + if (!wm->xr.runtime) { + wm->xr.runtime = wm_xr_runtime_data_create(); + wm->xr.runtime->context = context; + } + } + BLI_assert(wm->xr.runtime && wm->xr.runtime->context); + + return true; +} + +void wm_xr_exit(wmWindowManager *wm) +{ + if (wm->xr.runtime != NULL) { + wm_xr_runtime_data_free(&wm->xr.runtime); + } + if (wm->xr.session_settings.shading.prop) { + IDP_FreeProperty(wm->xr.session_settings.shading.prop); + wm->xr.session_settings.shading.prop = NULL; + } +} + +bool wm_xr_events_handle(wmWindowManager *wm) +{ + if (wm->xr.runtime && wm->xr.runtime->context) { + return GHOST_XrEventsHandle(wm->xr.runtime->context); + } + return false; +} + +/** \} */ /* XR-Context */ + +/* -------------------------------------------------------------------- */ +/** \name XR Runtime Data + * + * \{ */ + +wmXrRuntimeData *wm_xr_runtime_data_create(void) +{ + wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__); + return runtime; +} + +void wm_xr_runtime_data_free(wmXrRuntimeData **runtime) +{ + /* Note that this function may be called twice, because of an indirect recursion: If a session is + * running while WM-XR calls this function, calling GHOST_XrContextDestroy() will call this + * again, because it's also set as the session exit callback. So NULL-check and NULL everything + * that is freed here. */ + + /* We free all runtime XR data here, so if the context is still alive, destroy it. */ + if ((*runtime)->context != NULL) { + GHOST_XrContextHandle context = (*runtime)->context; + /* Prevent recursive GHOST_XrContextDestroy() call by NULL'ing the context pointer before the + * first call, see comment above. */ + (*runtime)->context = NULL; + GHOST_XrContextDestroy(context); + } + MEM_SAFE_FREE(*runtime); +} + +static void wm_xr_base_pose_calc(const Scene *scene, + const XrSessionSettings *settings, + GHOST_XrPose *r_base_pose) +{ + const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) && + settings->base_pose_object) ? + settings->base_pose_object : + scene->camera; + + if (settings->base_pose_type == XR_BASE_POSE_CUSTOM) { + float tmp_quatx[4], tmp_quatz[4]; + + copy_v3_v3(r_base_pose->position, settings->base_pose_location); + axis_angle_to_quat_single(tmp_quatx, 'X', M_PI_2); + axis_angle_to_quat_single(tmp_quatz, 'Z', settings->base_pose_angle); + mul_qt_qtqt(r_base_pose->orientation_quat, tmp_quatz, tmp_quatx); + } + else if (base_pose_object) { + float tmp_quat[4]; + float tmp_eul[3]; + + mat4_to_loc_quat(r_base_pose->position, tmp_quat, base_pose_object->obmat); + + /* Only use rotation around Z-axis to align view with floor. */ + quat_to_eul(tmp_eul, tmp_quat); + tmp_eul[0] = M_PI_2; + tmp_eul[1] = 0; + eul_to_quat(r_base_pose->orientation_quat, tmp_eul); + } + else { + copy_v3_fl(r_base_pose->position, 0.0f); + axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2); + } +} + +static void wm_xr_draw_data_populate(const wmXrSessionState *state, + const GHOST_XrDrawViewInfo *draw_view, + const XrSessionSettings *settings, + const Scene *scene, + wmXrDrawData *r_draw_data) +{ + const bool position_tracking_toggled = ((state->prev_settings_flag & + XR_SESSION_USE_POSITION_TRACKING) != + (settings->flag & XR_SESSION_USE_POSITION_TRACKING)); + const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING; + + memset(r_draw_data, 0, sizeof(*r_draw_data)); + + wm_xr_base_pose_calc(scene, settings, &r_draw_data->base_pose); + + if (position_tracking_toggled || !state->is_view_data_set) { + if (use_position_tracking) { + copy_v3_fl(r_draw_data->eye_position_ofs, 0.0f); + } + else { + /* Store the current local offset (local pose) so that we can apply that to the eyes. This + * way the eyes stay exactly where they are when disabling positional tracking. */ + copy_v3_v3(r_draw_data->eye_position_ofs, draw_view->local_pose.position); + } + } + else if (!use_position_tracking) { + /* Keep previous offset when positional tracking is disabled. */ + copy_v3_v3(r_draw_data->eye_position_ofs, state->prev_eye_position_ofs); + } +} + +/** + * Update information that is only stored for external state queries. E.g. for Python API to + * request the current (as in, last known) viewer pose. + */ +static void wm_xr_session_state_update(wmXrSessionState *state, + const GHOST_XrDrawViewInfo *draw_view, + const XrSessionSettings *settings, + const wmXrDrawData *draw_data) +{ + GHOST_XrPose viewer_pose; + const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING; + + mul_qt_qtqt(viewer_pose.orientation_quat, + draw_data->base_pose.orientation_quat, + draw_view->local_pose.orientation_quat); + copy_v3_v3(viewer_pose.position, draw_data->base_pose.position); + /* The local pose and the eye pose (which is copied from an earlier local pose) both are view + * space, so Y-up. In this case we need them in regular Z-up. */ + viewer_pose.position[0] += draw_data->eye_position_ofs[0]; + viewer_pose.position[1] -= draw_data->eye_position_ofs[2]; + viewer_pose.position[2] += draw_data->eye_position_ofs[1]; + if (use_position_tracking) { + viewer_pose.position[0] += draw_view->local_pose.position[0]; + viewer_pose.position[1] -= draw_view->local_pose.position[2]; + viewer_pose.position[2] += draw_view->local_pose.position[1]; + } + + copy_v3_v3(state->viewer_pose.position, viewer_pose.position); + copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat); + wm_xr_pose_to_viewmat(&viewer_pose, state->viewer_viewmat); + /* No idea why, but multiplying by two seems to make it match the VR view more. */ + state->focal_len = 2.0f * + fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left, + DEFAULT_SENSOR_WIDTH); + + copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); + state->prev_settings_flag = settings->flag; + state->is_view_data_set = true; +} + +wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr) +{ + return xr->runtime ? &xr->runtime->session_state : NULL; +} + +bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + zero_v3(r_location); + return false; + } + + copy_v3_v3(r_location, xr->runtime->session_state.viewer_pose.position); + return true; +} + +bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + unit_qt(r_rotation); + return false; + } + + copy_v4_v4(r_rotation, xr->runtime->session_state.viewer_pose.orientation_quat); + return true; +} + +bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, + float r_viewmat[4][4], + float *r_focal_len) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + unit_m4(r_viewmat); + *r_focal_len = 0.0f; + return false; + } + + copy_m4_m4(r_viewmat, xr->runtime->session_state.viewer_viewmat); + *r_focal_len = xr->runtime->session_state.focal_len; + + return true; +} + +/** \} */ /* XR Runtime Data */ + +/* -------------------------------------------------------------------- */ +/** \name XR-Session + * + * \{ */ + +void *wm_xr_session_gpu_binding_context_create(GHOST_TXrGraphicsBinding graphics_binding) +{ + wmSurface *surface = wm_xr_session_surface_create(G_MAIN->wm.first, graphics_binding); + wmXrSurfaceData *data = surface->customdata; + + wm_surface_add(surface); + + /* Some regions may need to redraw with updated session state after the session is entirely up + * and running. */ + WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); + + return data->secondary_ghost_ctx ? data->secondary_ghost_ctx : surface->ghost_ctx; +} + +void wm_xr_session_gpu_binding_context_destroy(GHOST_TXrGraphicsBinding UNUSED(graphics_lib), + GHOST_ContextHandle UNUSED(context)) +{ + if (g_xr_surface) { /* Might have been freed already */ + wm_surface_remove(g_xr_surface); + } + + wm_window_reset_drawable(); + + /* Some regions may need to redraw with updated session state after the session is entirely + * stopped. */ + WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); +} + +static void wm_xr_session_exit_cb(void *customdata) +{ + wmXrData *xr_data = customdata; + + xr_data->runtime->session_state.is_started = false; + if (xr_data->runtime->exit_fn) { + xr_data->runtime->exit_fn(xr_data); + } + + /* Free the entire runtime data (including session state and context), to play safe. */ + wm_xr_runtime_data_free(&xr_data->runtime); +} + +static void wm_xr_session_begin_info_create(wmXrData *xr_data, + GHOST_XrSessionBeginInfo *r_begin_info) +{ + /* WM-XR exit function, does some own stuff and calls callback passed to wm_xr_session_toggle(), + * to allow external code to execute its own session-exit logic. */ + r_begin_info->exit_fn = wm_xr_session_exit_cb; + r_begin_info->exit_customdata = xr_data; +} + +void wm_xr_session_toggle(wmWindowManager *wm, wmXrSessionExitFn session_exit_fn) +{ + wmXrData *xr_data = &wm->xr; + + if (WM_xr_session_exists(xr_data)) { + GHOST_XrSessionEnd(xr_data->runtime->context); + } + else { + GHOST_XrSessionBeginInfo begin_info; + + xr_data->runtime->session_state.is_started = true; + xr_data->runtime->exit_fn = session_exit_fn; + + wm_xr_session_begin_info_create(xr_data, &begin_info); + GHOST_XrSessionStart(xr_data->runtime->context, &begin_info); + } +} + +/** + * Check if the XR-Session was triggered. + * If an error happened while trying to start a session, this returns false too. + */ +bool WM_xr_session_exists(const wmXrData *xr) +{ + return xr->runtime && xr->runtime->context && xr->runtime->session_state.is_started; +} + +/** + * Check if the session is running, according to the OpenXR definition. + */ +bool WM_xr_session_is_ready(const wmXrData *xr) +{ + return WM_xr_session_exists(xr) && GHOST_XrSessionIsRunning(xr->runtime->context); +} + +/** \} */ /* XR-Session */ + +/* -------------------------------------------------------------------- */ +/** \name XR-Session Surface + * + * A wmSurface is used to manage drawing of the VR viewport. It's created and destroyed with the + * session. + * + * \{ */ + +/** + * \brief Call Ghost-XR to draw a frame + * + * Draw callback for the XR-session surface. It's expected to be called on each main loop iteration + * and tells Ghost-XR to submit a new frame by drawing its views. Note that for drawing each view, + * #wm_xr_draw_view() will be called through Ghost-XR (see GHOST_XrDrawViewFunc()). + */ +static void wm_xr_session_surface_draw(bContext *C) +{ + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + + if (!GHOST_XrSessionIsRunning(wm->xr.runtime->context)) { + return; + } + DRW_xr_drawing_begin(); + GHOST_XrSessionDrawViews(wm->xr.runtime->context, C); + GPU_offscreen_unbind(surface_data->offscreen, false); + DRW_xr_drawing_end(); +} + +static void wm_xr_session_free_data(wmSurface *surface) +{ + wmXrSurfaceData *data = surface->customdata; + + if (data->secondary_ghost_ctx) { +#ifdef WIN32 + if (data->gpu_binding_type == GHOST_kXrGraphicsD3D11) { + WM_directx_context_dispose(data->secondary_ghost_ctx); + } +#endif + } + if (data->viewport) { + GPU_viewport_free(data->viewport); + } + if (data->offscreen) { + GPU_offscreen_free(data->offscreen); + } + + MEM_freeN(surface->customdata); + + g_xr_surface = NULL; +} + +static bool wm_xr_session_surface_offscreen_ensure(const GHOST_XrDrawViewInfo *draw_view) +{ + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + const bool size_changed = surface_data->offscreen && + (GPU_offscreen_width(surface_data->offscreen) != draw_view->width) && + (GPU_offscreen_height(surface_data->offscreen) != draw_view->height); + char err_out[256] = "unknown"; + bool failure = false; + + if (surface_data->offscreen) { + BLI_assert(surface_data->viewport); + + if (!size_changed) { + return true; + } + GPU_viewport_free(surface_data->viewport); + GPU_offscreen_free(surface_data->offscreen); + } + + if (!(surface_data->offscreen = GPU_offscreen_create( + draw_view->width, draw_view->height, 0, true, false, err_out))) { + failure = true; + } + + if (failure) { + /* Pass. */ + } + else if (!(surface_data->viewport = GPU_viewport_create())) { + GPU_offscreen_free(surface_data->offscreen); + failure = true; + } + + if (failure) { + CLOG_ERROR(&LOG, "Failed to get buffer, %s\n", err_out); + return false; + } + + return true; +} + +wmSurface *wm_xr_session_surface_create(wmWindowManager *UNUSED(wm), unsigned int gpu_binding_type) +{ + if (g_xr_surface) { + BLI_assert(false); + return g_xr_surface; + } + + wmSurface *surface = MEM_callocN(sizeof(*surface), __func__); + wmXrSurfaceData *data = MEM_callocN(sizeof(*data), "XrSurfaceData"); + +#ifndef WIN32 + BLI_assert(gpu_binding_type == GHOST_kXrGraphicsOpenGL); +#endif + + surface->draw = wm_xr_session_surface_draw; + surface->free_data = wm_xr_session_free_data; + + data->gpu_binding_type = gpu_binding_type; + surface->customdata = data; + + surface->ghost_ctx = DRW_xr_opengl_context_get(); + + switch (gpu_binding_type) { + case GHOST_kXrGraphicsOpenGL: + break; +#ifdef WIN32 + case GHOST_kXrGraphicsD3D11: + data->secondary_ghost_ctx = WM_directx_context_create(); + break; +#endif + } + + surface->gpu_ctx = DRW_xr_gpu_context_get(); + + g_xr_surface = surface; + + return surface; +} + +/** \} */ /* XR-Session Surface */ + +/* -------------------------------------------------------------------- */ +/** \name XR Drawing + * + * \{ */ + +void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]) +{ + float iquat[4]; + invert_qt_qt_normalized(iquat, pose->orientation_quat); + quat_to_mat4(r_viewmat, iquat); + translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]); +} + +static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, + const GHOST_XrDrawViewInfo *draw_view, + const XrSessionSettings *session_settings, + float r_view_mat[4][4], + float r_proj_mat[4][4]) +{ + GHOST_XrPose eye_pose; + + copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); + copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); + add_v3_v3(eye_pose.position, draw_data->eye_position_ofs); + if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { + sub_v3_v3(eye_pose.position, draw_view->local_pose.position); + } + + perspective_m4_fov(r_proj_mat, + draw_view->fov.angle_left, + draw_view->fov.angle_right, + draw_view->fov.angle_up, + draw_view->fov.angle_down, + session_settings->clip_start, + session_settings->clip_end); + + float eye_mat[4][4]; + float base_mat[4][4]; + + wm_xr_pose_to_viewmat(&eye_pose, eye_mat); + /* Calculate the base pose matrix (in world space!). */ + wm_xr_pose_to_viewmat(&draw_data->base_pose, base_mat); + + mul_m4_m4m4(r_view_mat, eye_mat, base_mat); +} + +static void wm_xr_draw_viewport_buffers_to_active_framebuffer( + const wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view) +{ + const bool is_upside_down = surface_data->secondary_ghost_ctx && + GHOST_isUpsideDownContext(surface_data->secondary_ghost_ctx); + rcti rect = {.xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1}; + + wmViewport(&rect); + + /* For upside down contexts, draw with inverted y-values. */ + if (is_upside_down) { + SWAP(int, rect.ymin, rect.ymax); + } + GPU_viewport_draw_to_screen_ex(surface_data->viewport, &rect, draw_view->expects_srgb_buffer); +} + +/** + * \brief Draw a viewport for a single eye. + * + * This is the main viewport drawing function for VR sessions. It's assigned to Ghost-XR as a + * callback (see GHOST_XrDrawViewFunc()) and executed for each view (read: eye). + */ +void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) +{ + bContext *C = customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + wmXrSessionState *session_state = &wm->xr.runtime->session_state; + XrSessionSettings *settings = &wm->xr.session_settings; + wmXrDrawData draw_data; + Scene *scene = CTX_data_scene(C); + + const int display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags; + + float viewmat[4][4], winmat[4][4]; + + BLI_assert(WM_xr_session_is_ready(&wm->xr)); + + wm_xr_draw_data_populate(session_state, draw_view, settings, scene, &draw_data); + wm_xr_draw_matrices_create(&draw_data, draw_view, settings, viewmat, winmat); + wm_xr_session_state_update(session_state, draw_view, settings, &draw_data); + + if (!wm_xr_session_surface_offscreen_ensure(draw_view)) { + return; + } + + /* In case a framebuffer is still bound from drawing the last eye. */ + GPU_framebuffer_restore(); + /* Some systems have drawing glitches without this. */ + GPU_clear(GPU_DEPTH_BIT); + + /* Draws the view into the surface_data->viewport's framebuffers */ + ED_view3d_draw_offscreen_simple(CTX_data_ensure_evaluated_depsgraph(C), + scene, + &wm->xr.session_settings.shading, + wm->xr.session_settings.shading.type, + draw_view->width, + draw_view->height, + display_flags, + viewmat, + winmat, + settings->clip_start, + settings->clip_end, + false, + true, + true, + NULL, + false, + surface_data->offscreen, + surface_data->viewport); + + /* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A + * call to GPU_viewport_draw_to_screen() is still needed to get the final result from the + * viewport buffers composited together and potentially color managed for display on screen. + * It needs a bound framebuffer to draw into, for which we simply reuse the GPUOffscreen one. + * + * In a next step, Ghost-XR will use the the currently bound framebuffer to retrieve the image to + * be submitted to the OpenXR swapchain. So do not un-bind the offscreen yet! */ + + GPU_offscreen_bind(surface_data->offscreen, false); + + wm_xr_draw_viewport_buffers_to_active_framebuffer(surface_data, draw_view); +} + +/** \} */ /* XR Drawing */ diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 5bb9de87e82..22c01df5d3b 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -98,4 +98,14 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op); void wm_open_init_load_ui(wmOperator *op, bool use_prefs); void wm_open_init_use_scripts(wmOperator *op, bool use_prefs); +#ifdef WITH_XR_OPENXR +typedef void (*wmXrSessionExitFn)(const wmXrData *xr_data); + +/* wm_xr.c */ +bool wm_xr_init(wmWindowManager *wm); +void wm_xr_exit(wmWindowManager *wm); +void wm_xr_session_toggle(wmWindowManager *wm, wmXrSessionExitFn session_exit_fn); +bool wm_xr_events_handle(wmWindowManager *wm); +#endif + #endif /* __WM_H__ */ diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h new file mode 100644 index 00000000000..98d67c55619 --- /dev/null +++ b/source/blender/windowmanager/wm_surface.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + * + * \name WM-Surface + * + * Container to manage painting in an offscreen context. + */ + +#ifndef __WM_SURFACE_H__ +#define __WM_SURFACE_H__ + +struct bContext; + +typedef struct wmSurface { + struct wmSurface *next, *prev; + + GHOST_ContextHandle ghost_ctx; + struct GPUContext *gpu_ctx; + + void *customdata; + + void (*draw)(struct bContext *); + /** Free customdata, not the surface itself (done by wm_surface API) */ + void (*free_data)(struct wmSurface *); +} wmSurface; + +/* Create/Free */ +void wm_surface_add(wmSurface *surface); +void wm_surface_remove(wmSurface *surface); +void wm_surfaces_free(void); + +/* Utils */ +void wm_surfaces_iter(struct bContext *C, void (*cb)(bContext *, wmSurface *)); + +/* Drawing */ +void wm_surface_make_drawable(wmSurface *surface); +void wm_surface_clear_drawable(void); +void wm_surface_set_drawable(wmSurface *surface, bool activate); +void wm_surface_reset_drawable(void); + +#endif /* __WM_SURFACE_H__ */ diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index c485caf2779..bbef3a4d52a 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -111,6 +111,10 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c @@ -856,6 +860,8 @@ elseif(WIN32) ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json DESTINATION "." ) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index f265112570f..565cf60d2e1 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -603,6 +603,10 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo BLI_argsPrintArgDoc(ba, "--debug-gpu-shaders"); BLI_argsPrintArgDoc(ba, "--debug-gpu-force-workarounds"); BLI_argsPrintArgDoc(ba, "--debug-wm"); +# ifdef WITH_XR_OPENXR + BLI_argsPrintArgDoc(ba, "--debug-xr"); + BLI_argsPrintArgDoc(ba, "--debug-xr-time"); +# endif BLI_argsPrintArgDoc(ba, "--debug-all"); BLI_argsPrintArgDoc(ba, "--debug-io"); @@ -940,6 +944,16 @@ static const char arg_handle_debug_mode_generic_set_doc_wm[] = "\n\t" "Enable debug messages for the window manager, shows all operators in search, shows " "keymap errors."; +# ifdef WITH_XR_OPENXR +static const char arg_handle_debug_mode_generic_set_doc_xr[] = + "\n\t" + "Enable debug messages for virtual reality contexts.\n" + "\tEnables the OpenXR API validation layer, (OpenXR) debug messages and general information " + "prints."; +static const char arg_handle_debug_mode_generic_set_doc_xr_time[] = + "\n\t" + "Enable debug messages for virtual reality frame rendering times."; +# endif static const char arg_handle_debug_mode_generic_set_doc_jobs[] = "\n\t" "Enable time profiling for background jobs."; @@ -2091,6 +2105,16 @@ void main_args_setup(bContext *C, bArgs *ba) (void *)G_DEBUG_HANDLERS); BLI_argsAdd( ba, 1, NULL, "--debug-wm", CB_EX(arg_handle_debug_mode_generic_set, wm), (void *)G_DEBUG_WM); +# ifdef WITH_XR_OPENXR + BLI_argsAdd( + ba, 1, NULL, "--debug-xr", CB_EX(arg_handle_debug_mode_generic_set, xr), (void *)G_DEBUG_XR); + BLI_argsAdd(ba, + 1, + NULL, + "--debug-xr-time", + CB_EX(arg_handle_debug_mode_generic_set, xr_time), + (void *)G_DEBUG_XR_TIME); +# endif BLI_argsAdd(ba, 1, NULL, diff --git a/tests/python/bl_load_addons.py b/tests/python/bl_load_addons.py index bb730bc362c..f0c2f3f7fdf 100644 --- a/tests/python/bl_load_addons.py +++ b/tests/python/bl_load_addons.py @@ -50,6 +50,9 @@ def _init_addon_blacklist(): # netrender has known problems re-registering BLACKLIST_ADDONS.add("netrender") + if not bpy.app.build_options.xr_openxr: + BLACKLIST_ADDONS.add("viewport_vr_preview") + for mod in addon_utils.modules(): if addon_utils.module_bl_info(mod)['blender'] < (2, 80, 0): BLACKLIST_ADDONS.add(mod.__name__) diff --git a/tests/python/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py index 2252af7a02f..5d1a5dd8ee0 100644 --- a/tests/python/bl_load_py_modules.py +++ b/tests/python/bl_load_py_modules.py @@ -56,6 +56,9 @@ MODULE_SYS_PATHS = { if not bpy.app.build_options.freestyle: BLACKLIST.add("render_freestyle_svg") +if not bpy.app.build_options.xr_openxr: + BLACKLIST.add("viewport_vr_preview") + BLACKLIST_DIRS = ( os.path.join(bpy.utils.resource_path('USER'), "scripts"), ) + tuple(addon_utils.paths()[1:]) -- cgit v1.2.3