VR: Initial Virtual Reality support - Milestone 1, Scene Inspection

NOTE: While most of the milestone 1 goals are there, a few smaller features and
improvements are still to be done.

Big picture of this milestone: Initial, OpenXR-based virtual reality support
for users and foundation for advanced use cases.
Maniphest Task: https://developer.blender.org/T71347
The tasks contains more information about this milestone.

To be clear: This is not a feature rich VR implementation, it's focused on the
initial scene inspection use case. We intentionally focused on that, further
features like controller support are part of the next milestone.

- How to use?
Instructions on how to use this are here:
https://wiki.blender.org/wiki/User:Severin/GSoC-2019/How_to_Test
These will be updated and moved to a more official place (likely the manual) soon.

Currently Windows Mixed Reality and Oculus devices are usable. Valve/HTC
headsets don't support the OpenXR standard yet and hence, do not work with this
implementation.

---------------

This is the C-side implementation of the features added for initial VR
support as per milestone 1. A "VR Scene Inspection" Add-on will be
committed separately, to expose the VR functionality in the UI. It also
adds some further features for milestone 1, namely a landmarking system
(stored view locations in the VR space)

Main additions/features:
* Support for rendering viewports to an HMD, with good performance.
* Option to sync the VR view perspective with a fully interactive,
  regular 3D View (VR-Mirror).
* Option to disable positional tracking. Keeps the current position (calculated
  based on the VR eye center pose) when enabled while a VR session is running.
* Some regular viewport settings for the VR view
* RNA/Python-API to query and set VR session state information.
* WM-XR: Layer tying Ghost-XR to the Blender specific APIs/data
* wmSurface API: drawable, non-window container (manages Ghost-OpenGL and GPU
  context)
* DNA/RNA for management of VR session settings
* `--debug-xr` and `--debug-xr-time` commandline options
* Utility batch & config file for using the Oculus runtime on Windows.
* Most VR data is runtime only. The exception is user settings which are saved
  to files (`XrSessionSettings`).
* VR support can be disabled through the `WITH_XR_OPENXR` compiler flag.

For architecture and code documentation, see
https://wiki.blender.org/wiki/Source/Interface/XR.

---------------

A few thank you's:
* A huge shoutout to Ray Molenkamp for his help during the project - it would
  have not been that successful without him!
* Sebastian Koenig and Simeon Conzendorf for testing and feedback!
* The reviewers, especially Brecht Van Lommel!
* Dalai Felinto for pushing and managing me to get this done ;)
* The OpenXR working group for providing an open standard. I think we're the
  first bigger application to adopt OpenXR. Congratulations to them and
  ourselves :)

This project started as a Google Summer of Code 2019 project - "Core Support of
Virtual Reality Headsets through OpenXR" (see
https://wiki.blender.org/wiki/User:Severin/GSoC-2019/).
Some further information, including ideas for further improvements can be found
in the final GSoC report:
https://wiki.blender.org/wiki/User:Severin/GSoC-2019/Final_Report

Differential Revisions: D6193, D7098

Reviewed by: Brecht Van Lommel, Jeroen Bakker
This commit is contained in:
2020-03-17 20:20:55 +01:00
parent 406bfd4304
commit dc2df8307f
74 changed files with 2428 additions and 224 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -756,6 +756,18 @@ extern GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthan
*/
extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle);
/**
* Get the OpenGL framebuffer handle that serves as a default framebuffer.
*/
extern unsigned int GHOST_GetContextDefaultOpenGLFramebuffer(GHOST_ContextHandle contexthandle);
/**
* Returns whether a context is rendered upside down compared to OpenGL. This only needs to be
* called if there's a non-OpenGL context, which is really the exception.
* So generally, this does not need to be called.
*/
extern int GHOST_isUpsideDownContext(GHOST_ContextHandle contexthandle);
/**
* Get the OpenGL framebuffer handle that serves as a default framebuffer.
*/

View File

@@ -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

View File

@@ -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__

View File

@@ -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;

View File

@@ -119,6 +119,14 @@ class GHOST_Context : public GHOST_IContext {
return m_stereoVisual;
}
/**
* Returns if the window is rendered upside down compared to OpenGL.
*/
inline bool isUpsideDown() const
{
return false;
}
/**
* Gets the OpenGL framebuffer associated with the OpenGL context
* \return The ID of an OpenGL framebuffer object.

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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)

View File

@@ -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. */
};

View File

@@ -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")

View File

@@ -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];

View File

@@ -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,

View File

@@ -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)

View File

@@ -7187,6 +7187,7 @@ static void direct_link_region(FileData *fd, ARegion *region, int spacetype)
rv3d->smooth_timer = NULL;
rv3d->rflag &= ~(RV3D_NAVIGATING | RV3D_PAINTING);
rv3d->runtime_viewlock = 0;
}
}
}
@@ -7277,7 +7278,10 @@ static void direct_link_area(FileData *fd, ScrArea *area)
direct_link_gpencil(fd, v3d->gpd);
}
v3d->localvd = newdataadr(fd, v3d->localvd);
/* Runtime data */
v3d->runtime.properties_storage = NULL;
v3d->runtime.flag = 0;
/* render can be quite heavy, set to solid on load */
if (v3d->shading.type == OB_RENDER) {
@@ -7657,6 +7661,23 @@ static bool direct_link_area_map(FileData *fd, ScrAreaMap *area_map)
/** \} */
/* -------------------------------------------------------------------- */
/** \name XR-data
* \{ */
static void direct_link_wm_xr_data(FileData *fd, wmXrData *xr_data)
{
direct_link_view3dshading(fd, &xr_data->session_settings.shading);
}
static void lib_link_wm_xr_data(FileData *fd, ID *parent_id, wmXrData *xr_data)
{
xr_data->session_settings.base_pose_object = newlibadr(
fd, parent_id->lib, xr_data->session_settings.base_pose_object);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Window Manager
* \{ */
@@ -7710,6 +7731,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
}
}
direct_link_wm_xr_data(fd, &wm->xr);
BLI_listbase_clear(&wm->timers);
BLI_listbase_clear(&wm->operators);
BLI_listbase_clear(&wm->paintcursors);
@@ -7724,6 +7747,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
wm->message_bus = NULL;
wm->xr.runtime = NULL;
BLI_listbase_clear(&wm->jobs);
BLI_listbase_clear(&wm->drags);
@@ -7747,6 +7772,8 @@ static void lib_link_windowmanager(FileData *fd, Main *UNUSED(bmain), wmWindowMa
for (ScrArea *area = win->global_areas.areabase.first; area; area = area->next) {
lib_link_area(fd, &wm->id, area);
}
lib_link_wm_xr_data(fd, &wm->id, &wm->xr);
}
}
@@ -7911,6 +7938,12 @@ static void lib_link_main_data_restore(struct IDNameLib_Map *id_map, Main *newma
FOREACH_MAIN_ID_END;
}
static void lib_link_wm_xr_data_restore(struct IDNameLib_Map *id_map, wmXrData *xr_data)
{
xr_data->session_settings.base_pose_object = restore_pointer_by_name(
id_map, (ID *)xr_data->session_settings.base_pose_object, USER_REAL);
}
static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, ViewLayer *view_layer)
{
bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
@@ -8241,6 +8274,8 @@ void blo_lib_link_restore(Main *oldmain,
BLI_assert(win->screen == NULL);
}
lib_link_wm_xr_data_restore(id_map, &curwm->xr);
/* Restore all ID pointers in Main database itself
* (especially IDProperties might point to some word-space of other 'weirdly unchanged' ID
* pointers, see T69146).

View File

@@ -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;
}
}
}
}

View File

@@ -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

View File

@@ -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}")

View File

@@ -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);

View File

@@ -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
/** \} */

View File

@@ -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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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,

View File

@@ -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.

View File

@@ -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,38 +115,14 @@ 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;
}
}
@@ -155,6 +132,47 @@ bool ED_view3d_context_user_region(bContext *C, View3D **r_v3d, ARegion **r_ar)
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;
}
/* Most of the time this isn't needed since you could assume the view matrix was
* set while drawing, however when functions like mesh_foreachScreenVert are
* called by selection tools, we can't be sure this object was the last.
@@ -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;
}
}

View File

@@ -317,10 +317,35 @@ static void view3d_stereo3d_setup(
}
}
#ifdef WITH_XR_OPENXR
static void view3d_xr_mirror_setup(const wmWindowManager *wm,
Depsgraph *depsgraph,
Scene *scene,
View3D *v3d,
ARegion *region,
const rcti *rect)
{
RegionView3D *rv3d = region->regiondata;
float viewmat[4][4];
const float lens_old = v3d->lens;
if (!WM_xr_session_state_viewer_pose_matrix_info_get(&wm->xr, viewmat, &v3d->lens)) {
/* Can't get info from XR session, use fallback values. */
copy_m4_m4(viewmat, rv3d->viewmat);
v3d->lens = lens_old;
}
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, NULL, rect);
/* Reset overridden View3D data */
v3d->lens = lens_old;
}
#endif /* WITH_XR_OPENXR */
/**
* Set the correct matrices
*/
void ED_view3d_draw_setup_view(wmWindow *win,
void ED_view3d_draw_setup_view(const wmWindowManager *wm,
wmWindow *win,
Depsgraph *depsgraph,
Scene *scene,
ARegion *region,
@@ -331,13 +356,23 @@ void ED_view3d_draw_setup_view(wmWindow *win,
{
RegionView3D *rv3d = region->regiondata;
#ifdef WITH_XR_OPENXR
/* Setup the view matrix. */
if (view3d_stereo3d_active(win, scene, v3d, rv3d)) {
if (ED_view3d_is_region_xr_mirror_active(wm, v3d, region)) {
view3d_xr_mirror_setup(wm, depsgraph, scene, v3d, region, rect);
}
else
#endif
if (view3d_stereo3d_active(win, scene, v3d, rv3d)) {
view3d_stereo3d_setup(depsgraph, scene, v3d, region, rect);
}
else {
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, winmat, rect);
}
#ifndef WITH_XR_OPENXR
UNUSED_VARS(wm);
#endif
}
/** \} */
@@ -803,7 +838,8 @@ void ED_view3d_draw_depth(Depsgraph *depsgraph, ARegion *region, View3D *v3d, bo
UI_Theme_Store(&theme_state);
UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
ED_view3d_draw_setup_view(NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
ED_view3d_draw_setup_view(
G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
GPU_clear(GPU_DEPTH_BIT);
@@ -1481,7 +1517,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *region)
wmWindowManager *wm = CTX_wm_manager(C);
#ifdef WITH_INPUT_NDOF
if ((U.ndof_flag & NDOF_SHOW_GUIDE) && ((rv3d->viewlock & RV3D_LOCKED) == 0) &&
if ((U.ndof_flag & NDOF_SHOW_GUIDE) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) &&
(rv3d->persp != RV3D_CAMOB)) {
/* TODO: draw something else (but not this) during fly mode */
draw_rotation_guide(rv3d);
@@ -1552,7 +1588,8 @@ void view3d_draw_region_info(const bContext *C, ARegion *region)
static void view3d_draw_view(const bContext *C, ARegion *region)
{
ED_view3d_draw_setup_view(CTX_wm_window(C),
ED_view3d_draw_setup_view(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_expect_evaluated_depsgraph(C),
CTX_data_scene(C),
region,
@@ -1641,6 +1678,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
int winy,
float viewmat[4][4],
float winmat[4][4],
bool is_image_render,
bool do_sky,
bool UNUSED(is_persp),
const char *viewname,
@@ -1690,8 +1728,15 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
}
/* main drawing call */
DRW_draw_render_loop_offscreen(
depsgraph, engine_type, region, v3d, do_sky, do_color_management, ofs, viewport);
DRW_draw_render_loop_offscreen(depsgraph,
engine_type,
region,
v3d,
is_image_render,
do_sky,
do_color_management,
ofs,
viewport);
/* restore size */
region->winx = bwinx;
@@ -1706,6 +1751,94 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
G.f &= ~G_FLAG_RENDER_VIEWPORT;
}
/**
* Creates own fake 3d views (wrapping #ED_view3d_draw_offscreen). Similar too
* #ED_view_draw_offscreen_imbuf_simple, but takes view/projection matrices as arguments.
*/
void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
Scene *scene,
View3DShading *shading_override,
int drawtype,
int winx,
int winy,
uint draw_flags,
float viewmat[4][4],
float winmat[4][4],
float clip_start,
float clip_end,
bool is_image_render,
bool do_sky,
bool is_persp,
const char *viewname,
const bool do_color_management,
GPUOffScreen *ofs,
GPUViewport *viewport)
{
View3D v3d = {NULL};
ARegion ar = {NULL};
RegionView3D rv3d = {{{0}}};
v3d.regionbase.first = v3d.regionbase.last = &ar;
ar.regiondata = &rv3d;
ar.regiontype = RGN_TYPE_WINDOW;
View3DShading *source_shading_settings = &scene->display.shading;
if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != NULL) {
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
v3d.shading.type = drawtype;
if (shading_override) {
/* Pass. */
}
else if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
}
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
}
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
v3d.grid = 1.0f;
v3d.gridlines = 16;
v3d.gridsubdiv = 10;
/* Show grid, disable other overlays (set all available _HIDE_ flags). */
v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES |
V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
v3d.flag |= V3D_HIDE_HELPLINES;
}
else {
v3d.flag2 = V3D_HIDE_OVERLAYS;
}
rv3d.persp = RV3D_PERSP;
v3d.clip_start = clip_start;
v3d.clip_end = clip_end;
/* Actually not used since we pass in the projection matrix. */
v3d.lens = 0;
ED_view3d_draw_offscreen(depsgraph,
scene,
drawtype,
&v3d,
&ar,
winx,
winy,
viewmat,
winmat,
is_image_render,
do_sky,
is_persp,
viewname,
do_color_management,
ofs,
viewport);
}
/**
* Utility func for ED_view3d_draw_offscreen
*
@@ -1815,6 +1948,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
sizey,
NULL,
winmat,
true,
draw_sky,
!is_ortho,
viewname,
@@ -1902,6 +2036,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
}
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
}
v3d.shading.background_type = V3D_SHADING_BACKGROUND_WORLD;
@@ -2212,7 +2349,7 @@ float view3d_depth_near(ViewDepths *d)
void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *region, View3D *v3d)
{
/* Setup view matrix. */
ED_view3d_draw_setup_view(NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
GPU_clear(GPU_DEPTH_BIT);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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->viewlock & RV3D_LOCKED) == 0) {
gz = navgroup->gz_array[GZ_INDEX_CAMERA];
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_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);
}
}

View File

@@ -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) {

View File

@@ -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
/** \} */

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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, true);
gpu_viewport_draw_colormanaged(viewport, &pos_rect, &uv_rect, display_colorspace);
}
/**
* 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);
}
/**

View File

@@ -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 */

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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__ */

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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(
.

View File

@@ -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},
};

View File

@@ -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 */

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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 */

View File

@@ -257,6 +257,7 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self,
(float(*)[4])py_mat_projection->matrix,
true,
true,
true,
"",
false,
self->ofs,

View File

@@ -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}")

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
/**

View File

@@ -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];

View File

@@ -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);

View File

@@ -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))

View File

@@ -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();

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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 */

View File

@@ -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__ */

View File

@@ -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__ */

View File

@@ -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 "."
)

View File

@@ -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,

View File

@@ -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__)

View File

@@ -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:])