Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--intern/ghost/CMakeLists.txt3
-rw-r--r--intern/ghost/GHOST_C-api.h12
-rw-r--r--intern/ghost/GHOST_IContext.h9
-rw-r--r--intern/ghost/GHOST_Types.h31
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp14
-rw-r--r--intern/ghost/intern/GHOST_Context.h8
-rw-r--r--intern/ghost/intern/GHOST_IXrGraphicsBinding.h2
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp4
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h3
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp2
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp43
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h3
-rw-r--r--release/windows/batch/blender_oculus.cmd14
-rw-r--r--release/windows/batch/oculus.json9
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_global.h2
-rw-r--r--source/blender/blenkernel/CMakeLists.txt4
-rw-r--r--source/blender/blenkernel/intern/object.c6
-rw-r--r--source/blender/blenlib/BLI_math_geom.h7
-rw-r--r--source/blender/blenlib/intern/math_geom.c19
-rw-r--r--source/blender/blenloader/intern/readfile.c35
-rw-r--r--source/blender/blenloader/intern/versioning_280.c18
-rw-r--r--source/blender/blenloader/intern/writefile.c6
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/DRW_engine.h9
-rw-r--r--source/blender/draw/intern/draw_manager.c40
-rw-r--r--source/blender/editors/include/ED_view3d.h18
-rw-r--r--source/blender/editors/include/ED_view3d_offscreen.h19
-rw-r--r--source/blender/editors/screen/screen_ops.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c13
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c87
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c153
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c130
-rw-r--r--source/blender/editors/space_view3d/view3d_fly.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate.c35
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c20
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c99
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c2
-rw-r--r--source/blender/gpu/GPU_viewport.h3
-rw-r--r--source/blender/gpu/intern/gpu_viewport.c46
-rw-r--r--source/blender/makesdna/DNA_view3d_enums.h1
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h33
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h14
-rw-r--r--source/blender/makesdna/DNA_xr_types.h58
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt5
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_space.c104
-rw-r--r--source/blender/makesrna/intern/rna_wm.c26
-rw-r--r--source/blender/makesrna/intern/rna_wm_gizmo.c6
-rw-r--r--source/blender/makesrna/intern/rna_xr.c229
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c1
-rw-r--r--source/blender/windowmanager/CMakeLists.txt9
-rw-r--r--source/blender/windowmanager/WM_api.h16
-rw-r--r--source/blender/windowmanager/WM_types.h2
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h7
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c5
-rw-r--r--source/blender/windowmanager/intern/wm.c5
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c82
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c81
-rw-r--r--source/blender/windowmanager/intern/wm_surface.c118
-rw-r--r--source/blender/windowmanager/intern/wm_window.c29
-rw-r--r--source/blender/windowmanager/intern/wm_xr.c759
-rw-r--r--source/blender/windowmanager/wm.h10
-rw-r--r--source/blender/windowmanager/wm_surface.h57
-rw-r--r--source/creator/CMakeLists.txt6
-rw-r--r--source/creator/creator_args.c24
-rw-r--r--tests/python/bl_load_addons.py3
-rw-r--r--tests/python/bl_load_py_modules.py3
74 files changed, 2425 insertions, 221 deletions
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
@@ -759,6 +759,18 @@ extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthand
/**
* 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.
+ */
extern unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windwHandle);
/**
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
@@ -120,6 +120,14 @@ class GHOST_Context : public GHOST_IContext {
}
/**
+ * 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<GHOST_XrSession>(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_ContextWGL *>(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<XrView> views;
std::vector<std::unique_ptr<GHOST_XrSwapchain>> 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 <memory>
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<XrCompositionLayerProjectionView> &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) {
@@ -7658,6 +7662,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;
@@ -1707,6 +1752,94 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
}
/**
+ * 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
*
* \param ofs: Optional off-screen buffer, can be NULL.
@@ -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, &region)) {
+ 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, &region)) {
- 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, &region);
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, &region);
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, &region)) {
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, &region_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:])