diff options
Diffstat (limited to 'source')
36 files changed, 1125 insertions, 6 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index b44b6db8804..203b6da272f 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -90,6 +90,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_workspace_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_xr_types.h ) add_subdirectory(datatoc) diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index fe050075bae..f6cae6d8a9c 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -153,6 +153,8 @@ enum { G_DEBUG_IO = (1 << 17), /* IO Debugging (for Collada, ...)*/ G_DEBUG_GPU_SHADERS = (1 << 18), /* GLSL shaders */ G_DEBUG_GPU_FORCE_WORKAROUNDS = (1 << 19), /* force gpu workarounds bypassing detections. */ + G_DEBUG_XR = (1 << 20), /* XR/OpenXR messages */ + G_DEBUG_XR_TIME = (1 << 21), /* XR/OpenXR timing messages */ G_DEBUG_GHOST = (1 << 20), /* Debug GHOST module. */ }; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 112933e40be..135168a6de4 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -661,6 +661,10 @@ if(WITH_TBB) ) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + # # Warnings as errors, this is too strict! # if(MSVC) # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX") diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 534c25f6e01..2049f368578 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -636,6 +636,13 @@ void perspective_m4(float mat[4][4], const float top, const float nearClip, const float farClip); +void perspective_m4_fov(float mat[4][4], + const float angle_left, + const float angle_right, + const float angle_up, + const float angle_down, + const float nearClip, + const float farClip); void orthographic_m4(float mat[4][4], const float left, const float right, diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 7da080dbe9a..bf4b90db14f 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -4709,6 +4709,25 @@ void perspective_m4(float mat[4][4], mat[3][3] = 0.0f; } +void perspective_m4_fov(float mat[4][4], + const float angle_left, + const float angle_right, + const float angle_up, + const float angle_down, + const float nearClip, + const float farClip) +{ + const float tan_angle_left = tanf(angle_left); + const float tan_angle_right = tanf(angle_right); + const float tan_angle_bottom = tanf(angle_up); + const float tan_angle_top = tanf(angle_down); + + perspective_m4( + mat, tan_angle_left, tan_angle_right, tan_angle_top, tan_angle_bottom, nearClip, farClip); + mat[0][0] /= nearClip; + mat[1][1] /= nearClip; +} + /* translate a matrix created by orthographic_m4 or perspective_m4 in XY coords * (used to jitter the view) */ void window_translate_m4(float winmat[4][4], float perspmat[4][4], const float x, const float y) diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 450d3fc2371..3963ddc179a 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -92,6 +92,10 @@ if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") # needed so writefile.c can use dna_type_offsets.h diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 49b51c34562..3059caf83f1 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -7643,6 +7643,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) wm->undo_stack = NULL; wm->message_bus = NULL; + wm->xr.context = NULL; BLI_listbase_clear(&wm->jobs); BLI_listbase_clear(&wm->drags); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index d0659bfd34c..0ce32d234a8 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -30,6 +30,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "DNA_defaults.h" + #include "DNA_anim_types.h" #include "DNA_object_types.h" #include "DNA_camera_types.h" @@ -4860,5 +4862,17 @@ 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_type = OB_SOLID; + 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; + } + } } } diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index b4c23d5e57c..07d6219b85e 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -139,6 +139,11 @@ void DRW_opengl_context_destroy(void); void DRW_opengl_context_enable(void); void DRW_opengl_context_disable(void); +/* Not nice to expose these. Code to render offscreen viewports can save expensive context switches + * by using this directly however. */ +void *DRW_opengl_context_get(void); +void *DRW_gpu_context_get(void); + /* For garbage collection */ void DRW_cache_free_old_batches(struct Main *bmain); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 9553117c179..6d81d82a6cf 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2794,6 +2794,16 @@ void DRW_opengl_context_disable(void) DRW_opengl_context_disable_ex(true); } +void *DRW_opengl_context_get(void) +{ + return DST.gl_context; +} + +void *DRW_gpu_context_get(void) +{ + return DST.gpu_context; +} + void DRW_opengl_render_context_enable(void *re_gl_context) { /* If thread is main you should use DRW_opengl_context_enable(). */ diff --git a/source/blender/editors/include/ED_view3d_offscreen.h b/source/blender/editors/include/ED_view3d_offscreen.h index 359c55d89a5..f69d8367dca 100644 --- a/source/blender/editors/include/ED_view3d_offscreen.h +++ b/source/blender/editors/include/ED_view3d_offscreen.h @@ -57,6 +57,23 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph, 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 do_sky, + bool is_persp, + const char *viewname, + const bool do_color_management, + struct GPUOffScreen *ofs, + struct GPUViewport *viewport); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 4e2aada1e26..087fce9e4fd 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1704,6 +1704,89 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, } /** + * Creates own fake 3d views (wrapping #ED_view3d_draw_offscreen). Similar too + * #ED_view_draw_offscreen_imbuf_simple, but takes view/projection matrices as arguments. + */ +void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, + Scene *scene, + View3DShading *shading_override, + int drawtype, + int winx, + int winy, + uint draw_flags, + float viewmat[4][4], + float winmat[4][4], + float clip_start, + float clip_end, + bool 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 (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, + do_sky, + is_persp, + viewname, + do_color_management, + ofs, + viewport); +} + +/** * Utility func for ED_view3d_draw_offscreen * * \param ofs: Optional off-screen buffer, can be NULL. @@ -1899,6 +1982,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; diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index a3ee2d11e56..351c85aa2d8 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -532,6 +532,13 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, } } +/** + * 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) { DefaultFramebufferList *dfbl = viewport->fbl; @@ -545,18 +552,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,6 +576,14 @@ 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); } diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h index 85522ea88a2..f8c772422bb 100644 --- a/source/blender/makesdna/DNA_view3d_enums.h +++ b/source/blender/makesdna/DNA_view3d_enums.h @@ -26,6 +26,7 @@ typedef enum eV3DOffscreenDrawFlag { V3D_OFSDRAW_NONE = (0), V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0), V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1), + V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2), } eV3DOffscreenDrawFlag; /** #View3DShading.light */ diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 1eaf3777d0a..9226ff20ae6 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -28,6 +28,7 @@ #include "DNA_screen_types.h" #include "DNA_vec_types.h" #include "DNA_userdef_types.h" +#include "DNA_xr_types.h" #include "DNA_ID.h" @@ -42,6 +43,7 @@ struct wmKeyMap; struct wmMsgBus; struct wmOperator; struct wmOperatorType; +struct GHOST_XrContext; /* forwards */ struct PointerRNA; @@ -119,6 +121,14 @@ typedef struct ReportTimerInfo { float widthfac; } ReportTimerInfo; +//#ifdef WITH_XR_OPENXR +typedef struct wmXrData { + void *context; /* GHOST_XrContextHandle */ + + XrSessionSettings session_settings; +} wmXrData; +//#endif + /* reports need to be before wmWindowManager */ /* windowmanager is saved, tag WMAN */ @@ -180,6 +190,9 @@ typedef struct wmWindowManager { struct wmMsgBus *message_bus; + //#ifdef WITH_XR_OPENXR + wmXrData xr; + //#endif } wmWindowManager; /* wmWindowManager.initialized */ diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h new file mode 100644 index 00000000000..70927399a32 --- /dev/null +++ b/source/blender/makesdna/DNA_xr_types.h @@ -0,0 +1,37 @@ +/* + * 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__ + +typedef struct XrSessionSettings { + /** Shading type (OB_SOLID, ...). */ + char shading_type; + /** View3D draw flags (V3D_OFSDRAW_NONE, V3D_OFSDRAW_SHOW_ANNOTATION, ...). */ + char draw_flags; + char _pad[2]; + + /** Clipping distance. */ + float clip_start, clip_end; + + char _pad2[4]; +} XrSessionSettings; + +#endif /* __DNA_XR_TYPES_H__ */ diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 6b4b4854515..a33f13be09a 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -132,6 +132,7 @@ static const char *includefiles[] = { "DNA_workspace_types.h", "DNA_lightprobe_types.h", "DNA_curveprofile_types.h", + "DNA_xr_types.h", /* see comment above before editing! */ @@ -1598,6 +1599,7 @@ int main(int argc, char **argv) #include "DNA_workspace_types.h" #include "DNA_lightprobe_types.h" #include "DNA_curveprofile_types.h" +#include "DNA_xr_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index fa8cf6903ad..4f05c1be7f4 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -694,6 +694,7 @@ extern StructRNA RNA_WorkSpace; extern StructRNA RNA_World; extern StructRNA RNA_WorldLighting; extern StructRNA RNA_WorldMistSettings; +extern StructRNA RNA_XrSessionSettings; extern StructRNA RNA_uiPopover; extern StructRNA RNA_wmOwnerIDs; diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index a813fe12c02..f810d81e124 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -92,6 +92,7 @@ set(DEFSRC rna_wm_gizmo.c rna_workspace.c rna_world.c + rna_xr.c ) set(APISRC diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index af3069cf95e..b2d02600124 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4306,6 +4306,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_movieclip.c", NULL, RNA_def_movieclip}, {"rna_tracking.c", NULL, RNA_def_tracking}, {"rna_mask.c", NULL, RNA_def_mask}, + {"rna_xr.c", NULL, RNA_def_xr}, {NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 6bda8ebdb4c..3e18e882e2b 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -205,6 +205,7 @@ void RNA_def_world(struct BlenderRNA *brna); void RNA_def_movieclip(struct BlenderRNA *brna); void RNA_def_tracking(struct BlenderRNA *brna); void RNA_def_mask(struct BlenderRNA *brna); +void RNA_def_xr(struct BlenderRNA *brna); /* Common Define functions */ diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 929540f10ac..a903833448c 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2484,6 +2484,11 @@ 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", ""); + RNA_api_wm(srna); } diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c new file mode 100644 index 00000000000..7fdb77c3d0e --- /dev/null +++ b/source/blender/makesrna/intern/rna_xr.c @@ -0,0 +1,72 @@ +/* + * 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 "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +#else /* RNA_RUNTIME */ + +static void rna_def_xr_session_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "XrSessionSettings", NULL); + RNA_def_struct_ui_text(srna, "XR-Session Settings", ""); + + prop = RNA_def_property(srna, "shading_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_shading_type_items); + RNA_def_property_ui_text(prop, "Shading Type", "Method to display/shade objects in the VR View"); + + 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"); + + 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"); + + 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 View near clipping distance"); + + 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 View far clipping distance"); +} + +void RNA_def_xr(BlenderRNA *brna) +{ + RNA_define_animate_sdna(false); + + rna_def_xr_session_settings(brna); + + RNA_define_animate_sdna(true); +} + +#endif /* RNA_RUNTIME */ diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index e7a4ca9a005..a1b67216f1a 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -75,6 +75,7 @@ set(SRC intern/wm_splash_screen.c intern/wm_stereo.c intern/wm_subwindow.c + intern/wm_surface.c intern/wm_toolsystem.c intern/wm_tooltip.c intern/wm_uilist_type.c @@ -101,6 +102,7 @@ set(SRC wm_event_system.h wm_event_types.h wm_files.h + wm_surface.h wm_window.h intern/wm_platform_support.h intern/wm_window_private.h @@ -187,4 +189,11 @@ if(WITH_COMPOSITOR) add_definitions(-DWITH_COMPOSITOR) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) + list(APPEND SRC + intern/wm_xr.c + ) +endif() + blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6781f055fc2..bb8b67da14e 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -156,6 +156,10 @@ void *WM_opengl_context_create(void); void WM_opengl_context_dispose(void *context); void WM_opengl_context_activate(void *context); void WM_opengl_context_release(void *context); +#ifdef WIN32 +void *WM_directx_context_create(void); +void WM_directx_context_dispose(void *context); +#endif struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect); struct wmWindow *WM_window_open_temp(struct bContext *C, diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 2edef54c778..b2600ee0bf4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -399,6 +399,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) WM_msgbus_destroy(wm->message_bus); } +#ifdef WITH_XR_OPENXR + wm_xr_context_destroy(wm); +#endif + BLI_freelistN(&wm->paintcursors); WM_drag_free_list(&wm->drags); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 9ccff2a3e3d..1610bec59e7 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -67,6 +67,7 @@ #include "wm_draw.h" #include "wm_window.h" #include "wm_event_system.h" +#include "wm_surface.h" #ifdef WITH_OPENSUBDIV # include "BKE_subsurf.h" @@ -836,6 +837,20 @@ 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 */ @@ -963,6 +978,9 @@ void wm_draw_update(bContext *C) CTX_wm_window_set(C, NULL); } } + + /* Draw non-windows (surfaces) */ + wm_surfaces_iter(C, wm_draw_surface); } void wm_draw_region_clear(wmWindow *win, ARegion *UNUSED(region)) diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index a87f0a3e42c..7b04b40e626 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -98,6 +98,7 @@ #include "wm_event_system.h" #include "wm.h" #include "wm_files.h" +#include "wm_surface.h" #include "wm_window.h" #include "wm_platform_support.h" @@ -534,6 +535,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_materials_exit(); wm_operatortype_free(); + wm_surfaces_free(); wm_dropbox_free(); WM_menutype_free(); WM_uilisttype_free(); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 93ba5016ed7..aab2acaac76 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3645,6 +3645,39 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) /** \} */ +#ifdef WITH_XR_OPENXR +static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + + /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ + if (wm_xr_context_ensure(wm) == false) { + return OPERATOR_CANCELLED; + } + + wm_xr_session_toggle(C, wm->xr.context); + + 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; + + /* 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 +3719,9 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_call_panel); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); +#ifdef WITH_XR_OPENXR + WM_operatortype_append(WM_OT_xr_session_toggle); +#endif #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif diff --git a/source/blender/windowmanager/intern/wm_surface.c b/source/blender/windowmanager/intern/wm_surface.c new file mode 100644 index 00000000000..cb575cc8f7d --- /dev/null +++ b/source/blender/windowmanager/intern/wm_surface.c @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + */ + +#include "BKE_context.h" + +#include "BLF_api.h" + +#include "BLI_listbase.h" +#include "BLI_threads.h" + +#include "GHOST_C-api.h" + +#include "GPU_batch_presets.h" +#include "GPU_framebuffer.h" +#include "GPU_immediate.h" +#include "GPU_context.h" + +#include "MEM_guardedalloc.h" + +#include "WM_types.h" +#include "WM_api.h" +#include "wm.h" + +#include "wm_surface.h" + +static ListBase global_surface_list = {NULL, NULL}; +static wmSurface *g_drawable = NULL; + +void wm_surfaces_iter(bContext *C, void (*cb)(bContext *C, wmSurface *)) +{ + for (wmSurface *surf = global_surface_list.first; surf; surf = surf->next) { + cb(C, surf); + } +} + +void wm_surface_clear_drawable(void) +{ + if (g_drawable) { + BLF_batch_reset(); + gpu_batch_presets_reset(); + immDeactivate(); + + g_drawable = NULL; + } +} + +void wm_surface_set_drawable(wmSurface *surface, bool activate) +{ + BLI_assert(ELEM(g_drawable, NULL, surface)); + + g_drawable = surface; + if (activate) { + GHOST_ActivateOpenGLContext(surface->ghost_ctx); + } + + GPU_context_active_set(surface->gpu_ctx); + immActivate(); +} + +void wm_surface_make_drawable(wmSurface *surface) +{ + BLI_assert(GPU_framebuffer_active_get() == NULL); + + if (surface != g_drawable) { + wm_surface_clear_drawable(); + wm_surface_set_drawable(surface, true); + } +} + +void wm_surface_reset_drawable(void) +{ + BLI_assert(BLI_thread_is_main()); + BLI_assert(GPU_framebuffer_active_get() == NULL); + + if (g_drawable) { + wm_surface_clear_drawable(); + wm_surface_set_drawable(g_drawable, true); + } +} + +void wm_surface_add(wmSurface *surface) +{ + BLI_addtail(&global_surface_list, surface); +} + +void wm_surface_remove(wmSurface *surface) +{ + BLI_remlink(&global_surface_list, surface); + surface->free_data(surface); + MEM_freeN(surface); +} + +void wm_surfaces_free(void) +{ + for (wmSurface *surf = global_surface_list.first, *surf_next; surf; surf = surf_next) { + surf_next = surf->next; + wm_surface_remove(surf); + } + + BLI_assert(BLI_listbase_is_empty(&global_surface_list)); +} diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index fe6272686bc..a6893300900 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1631,6 +1631,9 @@ void wm_window_process_events(const bContext *C) GHOST_DispatchEvents(g_system); } hasevent |= wm_window_timer(C); +#ifdef WITH_XR_OPENXR + hasevent |= GHOST_XrEventsHandle(CTX_wm_manager(C)->xr.context); +#endif /* no event, we sleep 5 milliseconds */ if (hasevent == 0) { @@ -1957,6 +1960,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 +2452,24 @@ void WM_ghost_show_message_box(const char *title, GHOST_ShowMessageBox(g_system, title, message, help_label, continue_label, link, dialog_options); } /** \} */ + +#ifdef WIN32 +/* -------------------------------------------------------------------- */ +/** \name Direct DirectX Context Management + * \{ */ + +void *WM_directx_context_create(void) +{ + BLI_assert(GPU_framebuffer_active_get() == NULL); + return GHOST_CreateDirectXContext(g_system); +} + +void WM_directx_context_dispose(void *context) +{ + BLI_assert(GPU_framebuffer_active_get() == NULL); + GHOST_DisposeDirectXContext(g_system, context); +} + +/** \} */ + +#endif diff --git a/source/blender/windowmanager/intern/wm_xr.c b/source/blender/windowmanager/intern/wm_xr.c new file mode 100644 index 00000000000..be6af48f5de --- /dev/null +++ b/source/blender/windowmanager/intern/wm_xr.c @@ -0,0 +1,484 @@ +/* + * 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_main.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "BLI_math_geom.h" +#include "BLI_math_matrix.h" + +#include "CLG_log.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" + +static wmSurface *g_xr_surface = NULL; +static CLG_LogRef LOG = {"wm.xr"}; + +typedef struct { + GHOST_TXrGraphicsBinding gpu_binding_type; + GPUOffScreen *offscreen; + GPUViewport *viewport; + + GHOST_ContextHandle secondary_ghost_ctx; +} wmXrSurfaceData; + +typedef struct { + wmWindowManager *wm; +} wmXrErrorHandlerData; + +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, void *); +wmSurface *wm_xr_session_surface_create(wmWindowManager *, unsigned int); + +/* -------------------------------------------------------------------- */ +/** \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.context) { + /* Just play safe and destroy the entire context. */ + GHOST_XrContextDestroy(wm->xr.context); + wm->xr.context = NULL; + } +} + +bool wm_xr_context_ensure(wmWindowManager *wm) +{ + if (wm->xr.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)}; + + 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 (!(wm->xr.context = GHOST_XrContextCreate(&create_info))) { + return false; + } + + /* Set up context callbacks */ + GHOST_XrGraphicsContextBindFuncs(wm->xr.context, + wm_xr_session_gpu_binding_context_create, + wm_xr_session_gpu_binding_context_destroy); + GHOST_XrDrawViewFunc(wm->xr.context, wm_xr_draw_view); + } + BLI_assert(wm->xr.context != NULL); + + return true; +} + +void wm_xr_context_destroy(wmWindowManager *wm) +{ + if (wm->xr.context != NULL) { + GHOST_XrContextDestroy(wm->xr.context); + } +} + +/** \} */ /* XR-Context */ + +/* -------------------------------------------------------------------- */ +/** \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); + + 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), + void *UNUSED(context)) +{ + if (g_xr_surface) { /* Might have been freed already */ + wm_surface_remove(g_xr_surface); + } + + wm_window_reset_drawable(); +} + +static void wm_xr_session_begin_info_create(const Scene *scene, + GHOST_XrSessionBeginInfo *begin_info) +{ + if (scene->camera) { + copy_v3_v3(begin_info->base_pose.position, scene->camera->loc); + if (ELEM(scene->camera->rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) { + axis_angle_to_quat( + begin_info->base_pose.orientation_quat, scene->camera->rotAxis, scene->camera->rotAngle); + } + else if (scene->camera->rotmode == ROT_MODE_QUAT) { + copy_v4_v4(begin_info->base_pose.orientation_quat, scene->camera->quat); + } + else { + eul_to_quat(begin_info->base_pose.orientation_quat, scene->camera->rot); + } + } + else { + copy_v3_fl(begin_info->base_pose.position, 0.0f); + unit_qt(begin_info->base_pose.orientation_quat); + } +} + +void wm_xr_session_toggle(bContext *C, void *xr_context_ptr) +{ + GHOST_XrContextHandle xr_context = xr_context_ptr; + + if (xr_context && GHOST_XrSessionIsRunning(xr_context)) { + GHOST_XrSessionEnd(xr_context); + } + else { + GHOST_XrSessionBeginInfo begin_info; + + wm_xr_session_begin_info_create(CTX_data_scene(C), &begin_info); + + GHOST_XrSessionStart(xr_context, &begin_info); + } +} + +/** \} */ /* 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.context)) { + return; + } + GHOST_XrSessionDrawViews(wm->xr.context, C); + + GPU_offscreen_unbind(surface_data->offscreen, false); +} + +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_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_gpu_context_get(); + + g_xr_surface = surface; + + return surface; +} + +/** \} */ /* XR-Session Surface */ + +/* -------------------------------------------------------------------- */ +/** \name XR Drawing + * + * \{ */ + +/** + * Proper reference space set up is not supported yet. We simply hand OpenXR the global space as + * reference space and apply its pose onto the active camera matrix to get a basic viewing + * experience going. If there's no active camera with stick to the world origin. + */ +static void wm_xr_draw_matrices_create(const Scene *scene, + const GHOST_XrDrawViewInfo *draw_view, + const float clip_start, + const float clip_end, + float r_view_mat[4][4], + float r_proj_mat[4][4]) +{ + float scalemat[4][4], quat[4]; + float temp[4][4]; + + 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, + clip_start, + clip_end); + + scale_m4_fl(scalemat, 1.0f); + invert_qt_qt_normalized(quat, draw_view->pose.orientation_quat); + quat_to_mat4(temp, quat); + translate_m4(temp, + -draw_view->pose.position[0], + -draw_view->pose.position[1], + -draw_view->pose.position[2]); + + if (scene->camera) { + invert_m4_m4(scene->camera->imat, scene->camera->obmat); + mul_m4_m4m4(r_view_mat, temp, scene->camera->imat); + } + else { + copy_m4_m4(r_view_mat, temp); + } +} + +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(surface_data->viewport, &rect); +} + +/** + * \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; + XrSessionSettings *settings = &wm->xr.session_settings; + const float display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags; + + View3DShading shading; + float viewmat[4][4], winmat[4][4]; + + wm_xr_draw_matrices_create( + CTX_data_scene(C), draw_view, settings->clip_start, settings->clip_end, viewmat, winmat); + + 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(); + + BKE_screen_view3d_shading_init(&shading); + shading.flag |= V3D_SHADING_WORLD_ORIENTATION; + shading.flag &= ~V3D_SHADING_SPECULAR_HIGHLIGHT; + shading.background_type = V3D_SHADING_BACKGROUND_WORLD; + + /* Draws the view into the surface_data->viewport's framebuffers */ + ED_view3d_draw_offscreen_simple(CTX_data_ensure_evaluated_depsgraph(C), + CTX_data_scene(C), + &shading, + wm->xr.session_settings.shading_type, + draw_view->width, + draw_view->height, + display_flags, + viewmat, + winmat, + settings->clip_start, + settings->clip_end, + true, + true, + NULL, + false, + surface_data->offscreen, + surface_data->viewport); + + /* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A + * call to GPU_viewport_draw_to_screen() is still needed to get the final result from the + * viewport buffers composited together and potentially color managed for display on screen. + * It needs a bound framebuffer to draw into, for which we simply reuse the GPUOffscreen one. + * + * In a next step, Ghost-XR will use the the currently bound framebuffer to retrieve the image to + * be submitted to the OpenXR swapchain. So do not un-bind the offscreen yet! */ + + GPU_offscreen_bind(surface_data->offscreen, false); + + wm_xr_draw_viewport_buffers_to_active_framebuffer(surface_data, draw_view); +} + +/** \} */ /* XR Drawing */ diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 5bb9de87e82..c8dc883d731 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -98,4 +98,11 @@ 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 +/* wm_xr.c */ +bool wm_xr_context_ensure(wmWindowManager *wm); +void wm_xr_context_destroy(wmWindowManager *wm); +void wm_xr_session_toggle(bContext *C, void *xr_context); +#endif + #endif /* __WM_H__ */ diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h new file mode 100644 index 00000000000..98d67c55619 --- /dev/null +++ b/source/blender/windowmanager/wm_surface.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup wm + * + * \name WM-Surface + * + * Container to manage painting in an offscreen context. + */ + +#ifndef __WM_SURFACE_H__ +#define __WM_SURFACE_H__ + +struct bContext; + +typedef struct wmSurface { + struct wmSurface *next, *prev; + + GHOST_ContextHandle ghost_ctx; + struct GPUContext *gpu_ctx; + + void *customdata; + + void (*draw)(struct bContext *); + /** Free customdata, not the surface itself (done by wm_surface API) */ + void (*free_data)(struct wmSurface *); +} wmSurface; + +/* Create/Free */ +void wm_surface_add(wmSurface *surface); +void wm_surface_remove(wmSurface *surface); +void wm_surfaces_free(void); + +/* Utils */ +void wm_surfaces_iter(struct bContext *C, void (*cb)(bContext *, wmSurface *)); + +/* Drawing */ +void wm_surface_make_drawable(wmSurface *surface); +void wm_surface_clear_drawable(void); +void wm_surface_set_drawable(wmSurface *surface, bool activate); +void wm_surface_reset_drawable(void); + +#endif /* __WM_SURFACE_H__ */ diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index c485caf2779..bbef3a4d52a 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -111,6 +111,10 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_XR_OPENXR) + add_definitions(-DWITH_XR_OPENXR) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c @@ -856,6 +860,8 @@ elseif(WIN32) ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd + ${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json DESTINATION "." ) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index f265112570f..565cf60d2e1 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -603,6 +603,10 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo BLI_argsPrintArgDoc(ba, "--debug-gpu-shaders"); BLI_argsPrintArgDoc(ba, "--debug-gpu-force-workarounds"); BLI_argsPrintArgDoc(ba, "--debug-wm"); +# ifdef WITH_XR_OPENXR + BLI_argsPrintArgDoc(ba, "--debug-xr"); + BLI_argsPrintArgDoc(ba, "--debug-xr-time"); +# endif BLI_argsPrintArgDoc(ba, "--debug-all"); BLI_argsPrintArgDoc(ba, "--debug-io"); @@ -940,6 +944,16 @@ static const char arg_handle_debug_mode_generic_set_doc_wm[] = "\n\t" "Enable debug messages for the window manager, shows all operators in search, shows " "keymap errors."; +# ifdef WITH_XR_OPENXR +static const char arg_handle_debug_mode_generic_set_doc_xr[] = + "\n\t" + "Enable debug messages for virtual reality contexts.\n" + "\tEnables the OpenXR API validation layer, (OpenXR) debug messages and general information " + "prints."; +static const char arg_handle_debug_mode_generic_set_doc_xr_time[] = + "\n\t" + "Enable debug messages for virtual reality frame rendering times."; +# endif static const char arg_handle_debug_mode_generic_set_doc_jobs[] = "\n\t" "Enable time profiling for background jobs."; @@ -2091,6 +2105,16 @@ void main_args_setup(bContext *C, bArgs *ba) (void *)G_DEBUG_HANDLERS); BLI_argsAdd( ba, 1, NULL, "--debug-wm", CB_EX(arg_handle_debug_mode_generic_set, wm), (void *)G_DEBUG_WM); +# ifdef WITH_XR_OPENXR + BLI_argsAdd( + ba, 1, NULL, "--debug-xr", CB_EX(arg_handle_debug_mode_generic_set, xr), (void *)G_DEBUG_XR); + BLI_argsAdd(ba, + 1, + NULL, + "--debug-xr-time", + CB_EX(arg_handle_debug_mode_generic_set, xr_time), + (void *)G_DEBUG_XR_TIME); +# endif BLI_argsAdd(ba, 1, NULL, |