squashed XR, Support Mirror Blender UI to XR

This commit is contained in:
Wayde Moss 2023-09-28 22:48:08 -04:00
parent 1628cfee8d
commit b67c7eab58
22 changed files with 1447 additions and 72 deletions

View file

@ -84,6 +84,8 @@ _modules = [
# XXX, keep last so panels show after all other tool options.
"properties_workspace",
# XXX This depends on spaces
"properties_viewport_vr_mirror",
]
import bpy

View file

@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Panel
class VIEW3D_PT_vr_session_blender_ui_mirror(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'VR'
bl_label = 'Blender UI Mirror'
def draw(self, context):
layout = self.layout
session_settings = context.window_manager.xr_session_settings
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
col = layout.column(align=True)
col.prop(session_settings, "show_blender_ui_mirror")
col.prop(session_settings, "show_xr_controllers")
col.prop(session_settings, "use_mirror_ui_xray")
col.prop(session_settings, "hide_mirror_ui_on_mouse_over_v3d")
col.prop(session_settings, "viewer_offset")
col.prop(session_settings, "viewer_angle_offset")
col.prop(session_settings, "mirrored_ui_rads_span")
col.prop(session_settings, "mirrored_ui_offset")
col.prop(session_settings, "mirrored_ui_scale_y")
col.prop(session_settings, "mirrored_ui_distance_factor")
col.prop(session_settings, "cursor_raycast_distance0")
col.prop(session_settings, "cursor_raycast_distance1")
col.prop(session_settings, "cursor_raycast_distance2")
classes = [
VIEW3D_PT_vr_session_blender_ui_mirror
]
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View file

@ -277,6 +277,13 @@ void CTX_data_type_set(struct bContextDataResult *result, short type);
short CTX_data_type_get(struct bContextDataResult *result);
bool CTX_data_equals(const char *member, const char *str);
// GG: If is_empty_string(member), then callers should CTX_data_dir_set(result, dir) where dir
// is an array of strings w/ a null sentinel as the last element. These strings are the names of
// members/vars that the caller knows about and can set. Why this was never commented/documented
// within the code I dont know. (see: https://wiki.blender.org/wiki/Source/Architecture/Context
// under callbacks).. And why we've chosen to design a singular function (image_context(..))
// that returns two different results for different uses (do you have this member? / fill this
// member) is beyond me.
bool CTX_data_dir(const char *member);
#define CTX_DATA_BEGIN(C, Type, instance, member) \
@ -285,7 +292,8 @@ bool CTX_data_dir(const char *member);
CollectionPointerLink *ctx_link; \
CTX_data_##member(C, &ctx_data_list); \
for (ctx_link = (CollectionPointerLink *)ctx_data_list.first; ctx_link; \
ctx_link = ctx_link->next) { \
ctx_link = ctx_link->next) \
{ \
Type instance = (Type)ctx_link->ptr.data;
#define CTX_DATA_END \

View file

@ -3812,6 +3812,23 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "XrSessionSettings", "float", "mirrored_ui_scale_y")) {
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
wm->xr.session_settings.flag |= XR_SESSION_USE_UI_MIRROR_XRAY | XR_SESSION_SHOW_CONTROLLERS |
XR_SESSION_SHOW_BLENDER_UI_MIRROR;
// Commented code left as reminder that the default hiding is off.
// wm->xr.session_settings.flag &= ~XR_SESSION_HIDE_UI_MIRROR_ON_MOUSE_OVER_V3D;
zero_v3(wm->xr.session_settings.viewer_offset);
wm->xr.session_settings.viewer_angle_offset = 0;
wm->xr.session_settings.mirrored_ui_rads_span = 1.57f;
zero_v2(wm->xr.session_settings.mirrored_ui_offset);
wm->xr.session_settings.mirrored_ui_scale_y = 1;
wm->xr.session_settings.mirrored_ui_distance_factor = 10.0f;
wm->xr.session_settings.cursor_raycast_distance0 = -1;
wm->xr.session_settings.cursor_raycast_distance1 = 1;
wm->xr.session_settings.cursor_raycast_distance2 = 6;
}
}
if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "short", "ikflag_location")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->type != OB_ARMATURE || ob->pose == nullptr) {

View file

@ -100,7 +100,8 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph,
bool draw_background,
bool do_color_management,
struct GPUOffScreen *ofs,
struct GPUViewport *viewport);
struct GPUViewport *viewport,
const struct bContext *evil_C);
void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph,
struct ARegion *region,
struct GPUViewport *viewport,

View file

@ -1465,7 +1465,7 @@ void DRW_draw_callbacks_post_scene(void)
const bool do_annotations = drw_draw_show_annotation();
if (DST.draw_ctx.evil_C) {
if (DST.draw_ctx.evil_C && (v3d->flag & V3D_XR_SESSION_SURFACE) == 0) {
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DRW_state_reset();
@ -1490,33 +1490,37 @@ void DRW_draw_callbacks_post_scene(void)
/* Apply state for callbacks. */
GPU_apply_state();
// GG: TODO: ... sadly this very call (operator helper guides) is what causes the visual
// distortion while in XR. I suppose the proper way
// is for the operators to add their callbacks to the v3D region surface? Then the problematic
// operators just plain won't draw? Or just choose to be more aware of when they're causing an
// issue and make better choices?
ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW);
#ifdef WITH_XR_OPENXR
/* XR callbacks (controllers, custom draw functions) for session mirror. */
if ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0) {
if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) {
ARegionType *art = WM_xr_surface_controller_region_type_get();
// if ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0) {
if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) {
ARegionType *art = WM_xr_surface_controller_region_type_get();
if (art) {
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
}
}
if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
if (st) {
ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
if (art) {
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
}
}
if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) {
SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D);
if (st) {
ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR);
if (art) {
ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW);
}
}
}
// }
}
#endif
/* Callback can be nasty and do whatever they want with the state.
* Don't trust them! */
DRW_state_reset();
/* Needed so gizmo isn't occluded. */
if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) {
GPU_depth_test(GPU_DEPTH_NONE);
@ -1561,6 +1565,9 @@ void DRW_draw_callbacks_post_scene(void)
}
#ifdef WITH_XR_OPENXR
// GG: This is where Blender explicitly draws XR controllers, or more relveant atm,
// where blender allows custom drawing during an XR session. this is where I can (properly)
// insert the BLender UI quad draw.
if ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0) {
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
@ -1597,6 +1604,13 @@ void DRW_draw_callbacks_post_scene(void)
DRW_state_reset();
}
// disabled since its causing visual distortions?
// GPU_depth_test(GPU_DEPTH_NONE);
// // 3D cursor drawing,
// // ideally, we do this before the controllers are drawn but for some reason doing so leads
// // to the controllers not being draw at all
// DRW_draw_region_info();
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
#endif
@ -1773,7 +1787,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
DRW_stats_reset();
DRW_draw_callbacks_post_scene();
DRW_draw_callbacks_post_scene(); // maybe gizmos and all dont render in XR because the
// callbacks aren't attacheds to it?
if (WM_draw_region_get_bound_viewport(region)) {
/* Don't unbind the frame-buffer yet in this case and let
@ -1812,7 +1827,8 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph,
const bool draw_background,
const bool do_color_management,
GPUOffScreen *ofs,
GPUViewport *viewport)
GPUViewport *viewport,
const bContext *evil_C)
{
const bool is_xr_surface = ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0);
@ -1834,7 +1850,12 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph,
drw_state_prepare_clean_for_draw(&DST);
DST.options.is_image_render = is_image_render;
DST.options.draw_background = draw_background;
DRW_draw_render_loop_ex(depsgraph, engine_type, region, v3d, render_viewport, NULL);
// GG: I thikn the callbacks dont get called because NULL is sent for the context?
// ..well the above one alsos sends NULL, so thats probably not it. Though the CB
// beign called do dep on evil_c being non null. Actually i think that's exactly it.
// This call stack just doesn't do callbacks for w/e reason? yet its the only way to
// draw to an offscreen buffer....
DRW_draw_render_loop_ex(depsgraph, engine_type, region, v3d, render_viewport, evil_C);
if (draw_background) {
/* HACK(@fclem): In this case we need to make sure the final alpha is 1.

View file

@ -40,7 +40,8 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph,
bool do_color_management,
bool restore_rv3d_mats,
struct GPUOffScreen *ofs,
struct GPUViewport *viewport);
struct GPUViewport *viewport,
const struct bContext *evil_C);
/**
* 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.
@ -64,7 +65,8 @@ void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph,
const char *viewname,
bool do_color_management,
struct GPUOffScreen *ofs,
struct GPUViewport *viewport);
struct GPUViewport *viewport,
struct bContext *evil_C);
/**
* Utility func for ED_view3d_draw_offscreen

View file

@ -29,6 +29,7 @@
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_studiolight.h"
#include "BKE_unit.h"
@ -78,6 +79,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_draw.h"
#include "RNA_access.h"
@ -1656,7 +1658,8 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
const bool do_color_management,
const bool restore_rv3d_mats,
GPUOffScreen *ofs,
GPUViewport *viewport)
GPUViewport *viewport,
const bContext *evil_C)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
RenderEngineType *engine_type = ED_view3d_engine_type(scene, drawtype);
@ -1732,7 +1735,8 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
draw_background,
do_color_management,
ofs,
viewport);
viewport,
evil_C);
DRW_cache_free_old_subdiv();
GPU_matrix_pop_projection();
GPU_matrix_pop();
@ -1775,7 +1779,8 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
const char *viewname,
const bool do_color_management,
GPUOffScreen *ofs,
GPUViewport *viewport)
GPUViewport *viewport,
bContext *evil_C)
{
View3D v3d = blender::dna::shallow_zero_initialize();
ARegion ar = {nullptr};
@ -1784,7 +1789,74 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
v3d.regionbase.first = v3d.regionbase.last = &ar;
ar.regiondata = &rv3d;
ar.regiontype = RGN_TYPE_WINDOW;
// GG: callbacks are attached to ARegion.type so this needs to be assigned properly.
// GG: TODO: a nonNull evil_C works and the deired callbacks will occur (pre/post view for ex:
// mesh loopcut) however, I've disbled it for now since it creates distortion in the xr session
// drawing and I dont know why, nor do I have the energy to look into why. A: it seems the
// postview callbacks themselves cause the distortion, so
// .. the only way for this to work and be practical (no distrtions) then it wo9uld have to be a
// GPU state that's being changed by a callback, but i doubt it since we reset() things I think?
// Thus the solution is for callbacks themselves to not casuse the distortion which isn't
// practical w/o them explicitly knowing whether they're drawing to an XR surface. And thats too
// much work atm so i'm leaving the callback drawing disabled.
evil_C = nullptr;
if (evil_C) {
ar.type = BKE_regiontype_from_id(BKE_spacetype_from_id(SPACE_VIEW3D), RGN_TYPE_WINDOW);
wmWindowManager *wm = CTX_wm_manager(evil_C);
wmWindow *win = static_cast<wmWindow *>(wm->windows.first);
ARegion *v3d_region_under_mouse = nullptr;
bScreen *screen = WM_window_get_active_screen(win);
ScrArea *v3d_area = nullptr;
ED_screen_areas_iter (win, screen, area) {
if (area->spacetype != SPACE_VIEW3D) {
continue;
}
// v3d = area->spacedata.first;
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (!region->visible) {
continue;
}
if (region->overlap) {
continue;
}
if (region->draw_buffer->viewport == NULL) {
continue;
}
if (region->regiontype != RGN_TYPE_WINDOW) {
continue;
}
v3d_region_under_mouse = region;
v3d_area = area;
}
}
// after this, its just tedious stuff that also needs to be set otherwise an error is thrown
// but otherwise isn't specifically needed for callbacks to be called.
// needed for view3d_draw_border().. GG: TODO: still erroring though..
// v3d.spacetype = SPACE_VIEW3D;
// send in any old existing v3d area so callbacks occur. Our offscreen buffer is active so it
// will draw to our buffer anyways. Who cares what dta its reading from.
// IT works!
CTX_wm_area_set(evil_C, v3d_area); // static_cast<ScrArea *>(&v3d));
// View3D *v3d_ret = CTX_wm_view3d(evil_C); // 48dcbfcc70
// BLI_assert(v3d_ret != nullptr);
}
/*
* note: seach for CTX_wm_area_set() for proper useage: One file sets it like this:
*
CTX_wm_manager_set(C, static_cast<wmWindowManager *>(bmain->wm.first));
CTX_wm_screen_set(C, bfd->curscreen);
CTX_data_scene_set(C, bfd->curscene);
CTX_wm_area_set(C, nullptr);
CTX_wm_region_set(C, nullptr);
CTX_wm_menu_set(C, nullptr);
SO maybe I do too?
*/
View3DShading *source_shading_settings = &scene->display.shading;
if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != nullptr) {
source_shading_settings = shading_override;
@ -1822,15 +1894,15 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
v3d.flag2 |= V3D_XR_SHOW_CUSTOM_OVERLAYS;
}
/* 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_OBJECT_ORIGINS;
// v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
// V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
if ((draw_flags & V3D_OFSDRAW_SHOW_OBJECT_EXTRAS) == 0) {
v3d.overlay.flag |= V3D_OVERLAY_HIDE_OBJECT_XTRAS;
}
if ((object_type_exclude_viewport_override & (1 << OB_ARMATURE)) != 0) {
v3d.overlay.flag |= V3D_OVERLAY_HIDE_BONES;
}
v3d.flag |= V3D_HIDE_HELPLINES;
// v3d.flag |= V3D_HIDE_HELPLINES;
}
if (is_xr_surface) {
@ -1861,7 +1933,8 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
do_color_management,
true,
ofs,
viewport);
viewport,
evil_C);
}
ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
@ -1989,6 +2062,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
do_color_management,
restore_rv3d_mats,
ofs,
nullptr,
nullptr);
if (ibuf->rect_float) {

View file

@ -522,6 +522,9 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport,
void GPU_viewport_draw_to_screen(GPUViewport *viewport, int view, const rcti *rect)
{
// GG: TODO: LOOK: the windows are drawn w/ colorspace stuff going on!, I should be able to just
// disable it? Unless the pre-draw itself is also done w/ colorspacing stuff then its a bit more
// invovled..
GPU_viewport_draw_to_screen_ex(viewport, view, rect, true, true);
}

View file

@ -1383,6 +1383,14 @@ static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, const Stereo3dFormat
(s3d->flag & S3D_INTERLACE_SWAP) != 0);
break;
case S3D_DISPLAY_SIDEBYSIDE:
// GG: This func shows that both L/R img buffers already have the scene drawn.
// Since these funcs are just properly interlacing/placing the pixel data within the
// buffers.
//... which means that the Blender UI probnably isn't part of these buffers and they're
// just the view3D w/o the overlays. meaning that We must copy the UI elsewhere. This
// also means that the BlenderUI part will not have the view3D drawn so hopefully, itll
// be trivial to set the view3D non-overlay elements as alpha cutout. Probbaly as simple
// as a GL_CLEARBuffEr_COlor(trnasparent) bfore rendering overlays.
imb_stereo3d_read_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0);
break;
case S3D_DISPLAY_TOPBOTTOM:

View file

@ -111,15 +111,20 @@ typedef struct ReportTimerInfo {
float widthfac;
} ReportTimerInfo;
//#ifdef WITH_XR_OPENXR
// #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;
ListBase full_blender_ui_screens; /* #LinkData of GPUOffScreen, not saved to file. */
short window_positions_xy[64];
int mouse_positions_per_window_xy[64];
int mouse_position_global_xy[2];
struct bContext *evil_C;
} wmXrData;
//#endif
// #endif
/* reports need to be before wmWindowManager */
@ -199,9 +204,9 @@ typedef struct wmWindowManager {
struct wmMsgBus *message_bus;
//#ifdef WITH_XR_OPENXR
// #ifdef WITH_XR_OPENXR
wmXrData xr;
//#endif
// #endif
} wmWindowManager;
/** #wmWindowManager.initialized */

View file

@ -37,6 +37,18 @@ typedef struct XrSessionSettings {
int flag;
float mirrored_ui_offset[2];
// float mirrored_ui_scale;
float mirrored_ui_scale_y;
float mirrored_ui_distance_factor;
float cursor_raycast_distance0;
float cursor_raycast_distance1;
float cursor_raycast_distance2;
float viewer_offset[3];
float viewer_angle_offset;
float mirrored_ui_rads_span;
// float _pad3;
/** Object type settings to apply to VR view (unlike shading, not shared with window 3D-View). */
int object_type_exclude_viewport;
int object_type_exclude_select;
@ -45,6 +57,10 @@ typedef struct XrSessionSettings {
typedef enum eXrSessionFlag {
XR_SESSION_USE_POSITION_TRACKING = (1 << 0),
XR_SESSION_USE_ABSOLUTE_TRACKING = (1 << 1),
XR_SESSION_USE_UI_MIRROR_XRAY = (1 << 2),
XR_SESSION_HIDE_UI_MIRROR_ON_MOUSE_OVER_V3D = (1 << 3),
XR_SESSION_SHOW_CONTROLLERS = (1 << 4),
XR_SESSION_SHOW_BLENDER_UI_MIRROR = (1 << 5),
} eXrSessionFlag;
typedef enum eXRSessionBasePoseType {

View file

@ -699,7 +699,7 @@ static int rna_XrSessionSettings_icon_from_show_object_viewport_get(PointerRNA *
const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
return rna_object_type_visibility_icon_get_common(
xr->session_settings.object_type_exclude_viewport,
# if 0
# if 1
/* For the future when selection in VR is reliably supported. */
&xr->session_settings.object_type_exclude_select
# else
@ -2035,6 +2035,106 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
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, "viewer_offset", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_default(prop, 0);
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_range(prop, -10, 10, 0.01f, 2);
RNA_def_property_ui_text(prop, "Viewer Offset", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "viewer_angle_offset", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_default(prop, 0);
RNA_def_property_range(prop, -6.28f, 6.28f);
RNA_def_property_ui_range(prop, -6.28f, 6.28f, 1, 0);
RNA_def_property_ui_text(prop, "Angle Offset", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "mirrored_ui_rads_span", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_default(prop, 1.57f);
RNA_def_property_range(prop, 0, 6.28f);
RNA_def_property_ui_range(prop, 0, 6.28f, 1, 0);
RNA_def_property_ui_text(prop, "Angle Span", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "mirrored_ui_offset", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_float_default(prop, 0);
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 10, 0);
RNA_def_property_ui_text(prop, "UI Offset", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
// prop = RNA_def_property(srna, "mirrored_ui_scale", PROP_FLOAT, PROP_DISTANCE);
// RNA_def_property_float_default(prop, 1);
// RNA_def_property_range(prop, 1e-6f, FLT_MAX);
// RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 0.1f, 3);
// RNA_def_property_ui_text(prop, "UI Scale", "VR viewport far clipping distance");
// RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "mirrored_ui_scale_y", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_default(prop, 1);
RNA_def_property_range(prop, 1e-6f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 0.1f, 3);
RNA_def_property_ui_text(prop, "UI YScale", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "mirrored_ui_distance_factor", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_default(prop, 1);
RNA_def_property_range(prop, 1e-6f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 0.1f, 3);
RNA_def_property_ui_text(prop, "UI Distance Factor", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "cursor_raycast_distance0", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_default(prop, -1.0f);
RNA_def_property_range(prop, -1.0f, FLT_MAX);
RNA_def_property_ui_range(prop, -1.0f, 20.0f, 0.001f, 3);
RNA_def_property_ui_text(
prop, "Cursor RayCast p0 Distance", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "cursor_raycast_distance1", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_default(prop, 1);
RNA_def_property_range(prop, -1.0f, FLT_MAX);
RNA_def_property_ui_range(prop, -1.0f, 20.0f, 0.001f, 3);
RNA_def_property_ui_text(
prop, "Cursor RayCast p1 Distance", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "cursor_raycast_distance2", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_default(prop, 6);
RNA_def_property_range(prop, -1.0f, FLT_MAX);
RNA_def_property_ui_range(prop, -1.0f, 20.0f, 0.001f, 3);
RNA_def_property_ui_text(
prop, "Cursor RayCast p2 Distance", "VR viewport far clipping distance");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "use_mirror_ui_xray", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_USE_UI_MIRROR_XRAY);
RNA_def_property_ui_text(prop, "Mirrored UI: Do XRay", "");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "hide_mirror_ui_on_mouse_over_v3d", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_HIDE_UI_MIRROR_ON_MOUSE_OVER_V3D);
RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(prop,
"Mouse Over V3D: Hide Mirror UI",
"Best used w/ prefs set to disabled region overlapping");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "show_xr_controllers", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_SHOW_CONTROLLERS);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop, "Show Controllers", "");
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
prop = RNA_def_property(srna, "show_blender_ui_mirror", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_SHOW_BLENDER_UI_MIRROR);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop, "Show Blender UI Mirror", "");
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_funcs(prop,
"rna_XrSessionSettings_use_positional_tracking_get",
@ -2180,15 +2280,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
parm = RNA_def_string(
func, "user_path", NULL, XR_MAX_USER_PATH_LENGTH, "User Path", "OpenXR user path");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_float_array(
func,
parm = RNA_def_float_array(func,
"state",
2,
NULL,
-FLT_MAX,
FLT_MAX,
"Action State",
"Current state of the VR action. Second float value is only set for 2D vector type actions",
"Current state of the VR action. Second float value is only set "
"for 2D vector type actions",
-FLT_MAX,
FLT_MAX);
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT);

View file

@ -411,7 +411,8 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
do_color_management,
true,
self->ofs,
self->viewport);
self->viewport,
NULL);
GPU_offscreen_unbind(self->ofs, true);

View file

@ -211,6 +211,11 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
wm->message_bus = NULL;
wm->xr.runtime = NULL;
// GG: what if it has data in it? Or is this more of a nulling out refs that dont exist anymore?
// or maybe Blender checks which data has been orphaned and will not save orphaned data? it
// exists on first load of saved data, but not on reload after savingagain?
wm->xr.full_blender_ui_screens.first = NULL;
wm->xr.full_blender_ui_screens.last = NULL;
BLI_listbase_clear(&wm->jobs);
BLI_listbase_clear(&wm->drags);

View file

@ -750,6 +750,7 @@ static void wm_draw_region_unbind(ARegion *region)
static void wm_draw_region_blit(ARegion *region, int view)
{
// int original_view = view;
if (!region->draw_buffer) {
return;
}
@ -765,10 +766,27 @@ static void wm_draw_region_blit(ARegion *region, int view)
}
}
// GG: so they blit w/ awareness of whether its stereo or not.
// GG: This is sthe call that draws the viewports scenes (not the overlays).
// So.. i just need to make this completely transparent instead.
// Since this is a blit, that means all the UI was already rendered earlier. This is just
// properly blitting to the L/R buffers, I think. And, that means all other code draws the
// rest of the UI, which is done in GPU_offscreen_draw_to_screen() and caller for overlays.
if (region->draw_buffer->viewport) {
// if (original_view == 1) {
// original_view += 1;
// }
GPU_viewport_draw_to_screen(region->draw_buffer->viewport, view, &region->winrct);
}
else {
// GG: not sure why this draws for view==0, but not for view==1? Maybe the 1st time its drawn
// repeated? and the 2nd is just redundant?
// but.. then when the 2nd view only draws, then everything is bal
/*if (original_view == 1)
return;*/
// GG:*USEFUL* That means that the thing of interest is region->draw_buffer->offscreen which
// has the UI of interest.
GPU_offscreen_draw_to_screen(
region->draw_buffer->offscreen, region->winrct.xmin, region->winrct.ymin);
}
@ -1013,7 +1031,10 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
}
}
static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
static void wm_draw_window_onscreen(bContext *C,
wmWindow *win,
int view,
const bool do_view3D_cutout)
{
wmWindowManager *wm = CTX_wm_manager(C);
bScreen *screen = WM_window_get_active_screen(win);
@ -1021,7 +1042,8 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
GPU_debug_group_begin("Window Redraw");
/* Draw into the window frame-buffer, in full window coordinates. */
wmWindowViewport(win);
wmWindowViewport(
win); // GG: for upscaling, this prob should use upscale res instead of window res.
/* We draw on all pixels of the windows so we don't need to clear them before.
* Actually this is only a problem when resizing the window.
@ -1030,17 +1052,28 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
GPU_clear_color(0, 0, 0, 0);
#endif
if (do_view3D_cutout)
GPU_clear_color(0, 0, 0, 0);
/* Blit non-overlapping area regions. */
// GG: This is nonoverlays, so its the File menu, view3D, timeline ,etc.
// Overlays are the T/N panels, timeline playback cursor, etc. view3D nav is drawn here too.
ED_screen_areas_iter (win, screen, area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (!region->visible) {
continue;
}
if (region->overlap == false) {
/* Blit from off-screen buffer. */
wm_draw_region_blit(region, view);
if (region->overlap) {
continue;
}
if (do_view3D_cutout && region->draw_buffer->viewport) {
continue;
}
/* Blit from off-screen buffer. */
wm_draw_region_blit(region, view);
}
}
@ -1123,7 +1156,7 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
GPU_debug_group_end();
}
static void wm_draw_window(bContext *C, wmWindow *win)
static void wm_draw_window(bContext *C, wmWindow *win, GPUOffScreen *mirrored_screen)
{
GPU_context_begin_frame(win->gpuctx);
@ -1135,25 +1168,48 @@ static void wm_draw_window(bContext *C, wmWindow *win)
/* Draw area regions into their own frame-buffer. This way we can redraw
* the areas that need it, and blit the rest from existing frame-buffers. */
// GG: I think this is where we drawn Blender UI? then the L/R stero imagaes are just blitted
// ontop of the view3D?
// GG: I think that this draws all elements, probably except view3D. These elements are then
// blitted
// to each L/R side. So the UI is drawn once, cached, blitted per L/R.
// So I think this is the
wm_draw_window_offscreen(C, win, stereo);
// GG: TODO: if mirrored_screen is not NULL, then draw to that along with drawing to the normal
// backbuffer.
// So basically, draw to the mirroeed scren then copy mirror to buffer.
/* Now we draw into the window frame-buffer, in full window coordinates. */
if (!stereo) {
/* Regular mono drawing. */
wm_draw_window_onscreen(C, win, -1);
// GG: *USEFUL* This is the call of interest. It blits all the buffers for the entire UI. To
// prevent the view3D from drawing, we just
// avoid calling wm_draw_region_blit():GPU_viewport_draw_to_screen within this function.
// FOr VR sessions, we just set the backbuffer to a render texture (or blit to one) then draw
// the texture in scene space for the session.
wm_draw_window_onscreen(C, win, -1, false);
// TODO: Clear view3D to trnasparent and dont draw the sceen at all.
if (mirrored_screen != NULL) {
GPU_offscreen_bind(mirrored_screen, false);
wm_draw_window_onscreen(C, win, -1, true);
GPU_offscreen_unbind(mirrored_screen, false);
}
}
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
/* For page-flip we simply draw to both back buffers. */
GPU_backbuffer_bind(GPU_BACKBUFFER_RIGHT);
wm_draw_window_onscreen(C, win, 1);
wm_draw_window_onscreen(C, win, 1, false);
GPU_backbuffer_bind(GPU_BACKBUFFER_LEFT);
wm_draw_window_onscreen(C, win, 0);
wm_draw_window_onscreen(C, win, 0, false);
}
else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) {
/* For anaglyph and interlace, we draw individual regions with
* stereo frame-buffers using different shaders. */
wm_draw_window_onscreen(C, win, -1);
wm_draw_window_onscreen(C, win, -1, false);
}
else {
/* For side-by-side and top-bottom, we need to render each view to an
@ -1171,7 +1227,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
for (int view = 0; view < 2; view++) {
/* Draw view into offscreen buffer. */
GPU_offscreen_bind(offscreen, false);
wm_draw_window_onscreen(C, win, view);
wm_draw_window_onscreen(C, win, view, false);
GPU_offscreen_unbind(offscreen, false);
/* Draw offscreen buffer to screen. */
@ -1179,6 +1235,9 @@ static void wm_draw_window(bContext *C, wmWindow *win)
wmWindowViewport(win);
if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
// GG: Per iter, this draws half of the entire blender UI, which I think is
// a single buffer i think by this point?- as in all the UI is already in the
// texture, we're just properly placing it on screen...
wm_stereo3d_draw_sidebyside(win, view);
}
else {
@ -1192,7 +1251,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
}
else {
/* Still draw something in case of allocation failure. */
wm_draw_window_onscreen(C, win, 0);
wm_draw_window_onscreen(C, win, 0, false);
}
}
@ -1320,7 +1379,7 @@ uint *WM_window_pixels_read_from_offscreen(bContext *C, wmWindow *win, int r_siz
const uint rect_len = r_size[0] * r_size[1];
uint *rect = MEM_mallocN(sizeof(*rect) * rect_len, __func__);
GPU_offscreen_bind(offscreen, false);
wm_draw_window_onscreen(C, win, -1);
wm_draw_window_onscreen(C, win, -1, false);
GPU_offscreen_unbind(offscreen, false);
GPU_offscreen_read_color(offscreen, GPU_DATA_UBYTE, rect);
GPU_offscreen_free(offscreen);
@ -1350,7 +1409,7 @@ bool WM_window_pixels_read_sample_from_offscreen(bContext *C,
float rect_pixel[4];
GPU_offscreen_bind(offscreen, false);
wm_draw_window_onscreen(C, win, -1);
wm_draw_window_onscreen(C, win, -1, false);
GPU_offscreen_unbind(offscreen, false);
GPU_offscreen_read_color_region(offscreen, GPU_DATA_FLOAT, pos[0], pos[1], 1, 1, rect_pixel);
GPU_offscreen_free(offscreen);
@ -1495,7 +1554,12 @@ void wm_draw_update(bContext *C)
BKE_image_free_unused_gpu_textures();
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const bool xr_session_exists = WM_xr_session_exists(&wm->xr);
// if(xr_session_exists){
// BLI_assert(BLI_listbase_count(wm->xr->runtime->))
// }
int wn_index;
LISTBASE_FOREACH_INDEX (wmWindow *, win, &wm->windows, wn_index) {
#ifdef WIN32
GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);
@ -1519,7 +1583,16 @@ void wm_draw_update(bContext *C)
/* notifiers for screen redraw */
ED_screen_ensure_updated(C, wm, win, screen);
wm_draw_window(C, win);
GPUOffScreen *xr_mirror_BUI_to_hmd_screen = NULL;
const bool draw_to_blender_ui_mirror = (wm->xr.session_settings.flag &
XR_SESSION_SHOW_BLENDER_UI_MIRROR) != 0;
if (xr_session_exists && draw_to_blender_ui_mirror) {
// BLI_assert(wn_index < BLI_)
LinkData *ld = BLI_findlink(&wm->xr.full_blender_ui_screens, wn_index);
if (ld != NULL)
xr_mirror_BUI_to_hmd_screen = ld->data;
}
wm_draw_window(C, win, xr_mirror_BUI_to_hmd_screen);
wm_draw_update_clear_window(C, win);
wm_window_swap_buffers(win);

View file

@ -73,10 +73,10 @@ void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
immBegin(GPU_PRIM_TRI_FAN, 4);
immAttr2f(texcoord, halfx, halfy);
immVertex2f(pos, soffx, 0.0f);
immVertex2f(pos, soffx, 0);
immAttr2f(texcoord, 1.0f + halfx, halfy);
immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f);
immVertex2f(pos, soffx + (sizex * 0.5f), 0);
immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
immVertex2f(pos, soffx + (sizex * 0.5f), sizey);

View file

@ -22,7 +22,9 @@
#include "GHOST_C-api.h"
#include "GPU_framebuffer.h"
#include "GPU_platform.h"
#include "GPU_texture.h"
#include "MEM_guardedalloc.h"
@ -101,6 +103,29 @@ bool wm_xr_init(wmWindowManager *wm)
if (!wm->xr.runtime) {
wm->xr.runtime = wm_xr_runtime_data_create();
wm->xr.runtime->context = context;
// GG: TODO: .. what happens if user deletes a window while in XR session?
// so maybe just have a max buffer of offscreens? Otherwise have to sync w/ window
// creation/deletion.
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const int width = WM_window_pixels_x(win);
const int height = WM_window_pixels_y(win);
// GG: TODO: I need to use the XR per-eye resolution, not window resolution. Maybe thats
// why the UI is blurry? But then, would blender UI drawing implementation.. just work?
// There's probably hardcoded areas that query/use the window pixel size...
// Though.. maybe im wrong.. because an image quad scnee object w/ blender UI on it appears
// fine and not low res/blurry at all..-it is just as blurry when viewed at ~30degrees away
// from forward?- so curving the UI is a big help.
GPUOffScreen *offscreen = GPU_offscreen_create(
width, height, false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL);
// Dont skip so that indices align with wm->windows, so wm_draw_window() can draw to proper
// window offscreen buffer if (offscreen == NULL)
// continue;
BLI_addtail(&wm->xr.full_blender_ui_screens, BLI_genericNodeN(offscreen));
}
}
}
BLI_assert(wm->xr.runtime && wm->xr.runtime->context);

View file

@ -18,6 +18,7 @@
#include "ED_view3d_offscreen.h"
#include "GHOST_C-api.h"
#include "GHOST_Types.h"
#include "GPU_batch_presets.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
@ -28,6 +29,7 @@
#include "wm_surface.h"
#include "wm_xr_intern.h"
typedef struct GHOST_XrPose;
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
{
@ -93,7 +95,14 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
/* Apply base pose and navigation. */
wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv);
wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv);
mul_m4_m4m4(m, eye_inv, base_inv);
float viewer_offset[4][4];
unit_m4(viewer_offset);
rotate_m4(viewer_offset, 'Y', -session_settings->viewer_angle_offset);
copy_v3_v3(viewer_offset[3], session_settings->viewer_offset);
mul_m4_m4m4(m, eye_inv, viewer_offset);
mul_m4_m4_post(m, base_inv);
mul_m4_m4m4(r_viewmat, m, nav_inv);
perspective_m4_fov(r_projmat,
@ -133,6 +142,9 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
wmXrSessionState *session_state = &xr_data->runtime->session_state;
XrSessionSettings *settings = &xr_data->session_settings;
// GG: using 0: shows everything: helper lines (knife cut op), wireframes bones, xform gizmos,
// etc
// const int display_flags = ~0;
const int display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags;
float viewmat[4][4], winmat[4][4];
@ -155,7 +167,14 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
/* Some systems have drawing glitches without this. */
GPU_clear_depth(1.0f);
/* Draws the view into the surface_data->viewport's frame-buffers. */
// GG: I feel like maybe drawing the BBlender UI can just happen around here and be optional?
/* Draws the view into the surface_data->viewport's frame-buffers. Though, perhaps we dont have
* access to window data?*/
// GG: maybe we need to set CTX_wm_region_set() to use our fake v3d? maybe thats why tools aren't
// being drawn in XR? (see editmesh_loopcut.c/search for REGION_DRAW_POST_VIEW They just attach a
// cb to the active region. Since we don't see it, then maybe the region isn't being set? or,
// maybe its as simple as this offscreen not calling callbacks)
ED_view3d_draw_offscreen_simple(draw_data->depsgraph,
draw_data->scene,
&settings->shading,
@ -175,7 +194,8 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
NULL,
false,
vp->offscreen,
vp->viewport);
vp->viewport,
xr_data->evil_C);
/* 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
@ -230,6 +250,7 @@ static void wm_xr_controller_model_draw(const XrSessionSettings *settings,
GHOST_XrContextHandle xr_context,
wmXrSessionState *state)
{
GHOST_XrControllerModelData model_data;
float color[4];
@ -334,9 +355,15 @@ static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSes
madd_v3_v3v3fl(ray, mat[3], mat[2], -scale);
immAttrSkip(col);
immVertex3fv(pos, mat[3]);
// immVertex3fv(pos, mat[3]);
// immVertex3f(pos, mat[3][0] + 1, mat[3][1], mat[3][2]);
immAttr4ubv(col, color);
immVertex3fv(pos, ray);
// immAttrSkip(col);
// immVertex3f(pos, mat[3][0] + 1, mat[3][1], mat[3][2]);
// immAttr4ubv(col, color);
// immVertex3f(pos, ray[0] + 1, ray[1], ray[2]);
}
immEnd();
@ -357,7 +384,7 @@ static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSes
const float(*mat)[4] = controller->aim_mat;
madd_v3_v3v3fl(x_axis, mat[3], mat[0], scale);
madd_v3_v3v3fl(y_axis, mat[3], mat[1], scale);
madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale);
madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale * 5);
immAttrSkip(col);
immVertex3fv(pos, mat[3]);
@ -385,9 +412,639 @@ void wm_xr_draw_controllers(const bContext *UNUSED(C), ARegion *UNUSED(region),
{
wmXrData *xr = customdata;
const XrSessionSettings *settings = &xr->session_settings;
if ((settings->flag & XR_SESSION_SHOW_CONTROLLERS) == 0) {
return;
}
GHOST_XrContextHandle xr_context = xr->runtime->context;
wmXrSessionState *state = &xr->runtime->session_state;
wm_xr_controller_model_draw(settings, xr_context, state);
wm_xr_controller_aim_draw(settings, state);
}
// GG: draw blender UI, need to have access to all regions
void draw_mirror_blender_ui_to_xr(const bContext *UNUSED(C),
ARegion *UNUSED(region),
void *customdata)
{
wmXrData *xr = customdata;
const XrSessionSettings *settings = &xr->session_settings;
if ((settings->flag & XR_SESSION_SHOW_BLENDER_UI_MIRROR) == 0) {
return;
}
// GG: TIP: Setting UI Resolution in prefs to 1.5+ makes the mirrored UI extrmely readable, along
// w/ setting text rendering to full and using AA. You can also plain make the window itself
// larger and span across multiple monitors before starting the XR session (not as effective
// though).
// GG: TIP: Placing raycast origin aligned with nose or lower isn't jarring, probably least
// jarring
// of all possible placements (probably because we've learned to ignore our nose). Next least
// jarring is to palce the raycast on our shoulder but then rotating objects is slightly off,
// enough to notice but not enough to make it unusable. Best place is under the nose.
// GG: TIP: TO get more wokring space, just use a single window thats wide and extends mupltiple
// monitors. Its better than using multiple window since cursor capture is slow when moving
// between
// windows.
// GG: TODO: use camera origin + forward projected onto quad sphere as the ui origin (suhc that
// its
// where the center of the screenspace v3D is in viewspace.
// (base_pose.location + base_pose.forawrd * radius)
// This also makes it the scale origin so that changing scale in the ui props doesn't affect
// the v3D center point in vr space, which is what the user calibrates their UI pos/scale/rads
// to.
// GG: TODO: default ui offset should be centered on raycast origin. Currently its higher up than
// expected by 0.5 * ui_height
GPUVertFormat *format;
uint texcoord;
uint pos;
uint col;
float ui_width = 1920;
float ui_height = 1080;
if (!BLI_listbase_is_empty(&xr->full_blender_ui_screens)) {
GPUOffScreen *any_screen = ((LinkData *)xr->full_blender_ui_screens.first)->data;
if (any_screen) {
ui_width = GPU_offscreen_width(any_screen);
// / 4.0f; // xr->ui_res_scaling_factor;
ui_height = GPU_offscreen_height(any_screen);
// / 4.0f; // xr->ui_res_scaling_factor;
}
}
// TODO: really should alpha clip v3D pixels instead of just doing alpha blend. (maybe try
// colormask?)
// since non-clip still writes to depth buffer.
GPU_blend(GPU_BLEND_ALPHA);
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
/**
* OK, so I think that the entire blenderUI should
* just be fully rendered on XR again instead of just
* being blitted. This allows python users to draw
* overlays specifically for the XR device for non
* view3D's, or do things specifically when seen through XR.
* ...Q:... if XR is already only drawn for XR view3D,
* then shouldnt some caller high up be able to draw
* to the same space and even have access to the entire
* blender UI?
*/
float base_pose_mat[4][4];
GHOST_XrPose *base_pose = &xr->runtime->session_state.prev_base_pose;
wm_xr_pose_to_mat(base_pose, base_pose_mat);
float resx = 2.0f;
const float resy_per_x = (ui_height / ui_width);
float resy = resy_per_x * resx;
float halfresx = resx * 0.5f;
float halfresy = resy * 0.5f;
// GG: window iter from L1534
// GG: TODO: really, only need one window, the main one that user is using.
// float fmouse_xyz[3] = {xr->runtime->mouse_xy[0], xr->runtime->mouse_xy[1], ui_distance};
// // Normalize mouse coords to UI resolution.
// fmouse_xyz[0] /= ui_width;
// fmouse_xyz[1] /= ui_height;
// fmouse_xyz[0] *= resx;
// fmouse_xyz[1] *= resy;
// fmouse_xyz[0] -= halfresx;
// fmouse_xyz[1] -= halfresy;
// float fmouse_xyz_screenspace[3];
// copy_v3_v3(fmouse_xyz_screenspace, fmouse_xyz);
// mul_m4_v3(base_pose_mat, fmouse_xyz);
// float fmouse_mat[4][4];
// unit_m4(fmouse_mat);
// copy_v3_v3(fmouse_mat[3], fmouse_xyz);
// copy_m3_m3(fmouse_mat, base_pose_mat);
// mul_m4_m4m4(fmouse_mat, base_pose_mat, fmouse_mat);
// unit_m4(fmouse_mat);
// float color[4] = {0, 0, 1, 1};
// GPUBatch *sphere = GPU_batch_preset_sphere(0);
// GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
// GPU_batch_uniform_4fv(sphere, "color", color);
// GPU_matrix_push();
// GPU_matrix_set(base_pose_mat);
////GPU_matrix_set(fmouse_mat);
// GPU_matrix_scale_1f(0.1f);
// GPU_batch_draw(sphere);
// GPU_matrix_pop();
bool is_mouse_over_v3d = (xr->runtime->flags & XR_RUNTIME_IS_MOUSE_OVER_V3D) != 0;
if (is_mouse_over_v3d) {
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR);
float viewport[4];
GPU_viewport_size_get_f(viewport);
immUniform2fv("viewportSize", &viewport[2]);
// immUniform1f("lineWidth", 1); // 3.0f * U.pixelsize);//thinner is easier on eyes
immUniform1f("lineWidth", 10.0f * U.pixelsize); // thinner is easier on eyes
// GG:TODO: replace with drawing blender UI to a quad (then eventually sphere quad)
// currently just ensuring XR arbitrary drawing.
immBeginAtMost(GPU_PRIM_LINE_STRIP, 16);
// consider starting ray at mouse? or atleast screen? (since mouse may not match w/ ray)
immAttr3f(col, 0, 0, 1);
float raycast_startpoint[3];
copy_v3_v3(raycast_startpoint, xr->runtime->v3d_ray_dir);
// starting depth of drawn ray, dont want it to be too closer to user's eyes
mul_v3_fl(raycast_startpoint, xr->session_settings.cursor_raycast_distance0);
add_v3_v3(raycast_startpoint, xr->runtime->v3d_origin);
immVertex3fv(pos, raycast_startpoint);
immAttr3f(col, 0, 1, 0);
float raycast_endpoint[3];
copy_v3_v3(raycast_endpoint, xr->runtime->v3d_ray_dir);
mul_v3_fl(raycast_endpoint, xr->session_settings.cursor_raycast_distance1);
add_v3_v3(raycast_endpoint, xr->runtime->v3d_origin);
immVertex3fv(pos, raycast_endpoint);
immAttr3f(col, 1, 0, 0);
copy_v3_v3(raycast_endpoint, xr->runtime->v3d_ray_dir);
mul_v3_fl(raycast_endpoint, xr->session_settings.cursor_raycast_distance2);
add_v3_v3(raycast_endpoint, xr->runtime->v3d_origin);
immVertex3fv(pos, raycast_endpoint);
immEnd();
immUnbindProgram();
}
float viewspace_from_worldspace[4][4];
float worldspace_from_viewspace[4][4];
copy_m4_m4(worldspace_from_viewspace, base_pose_mat);
invert_m4_m4(viewspace_from_worldspace, worldspace_from_viewspace);
float rads_per_viewspace_unit = 1;
float total_rads_per_ui_width = xr->session_settings.mirrored_ui_rads_span;
const float rads_per_pixel = total_rads_per_ui_width / ui_width;
float viewspace_ui_height = 0;
float window_origin_viewspace[3] = {0, 0, 0};
{
mul_m4_v3(xr->runtime->worldspace_from_windowspace, window_origin_viewspace);
mul_m4_v3(viewspace_from_worldspace, window_origin_viewspace);
}
{
float viewspace_unit_per_rad_vec[3] = {1.0 / rads_per_pixel, 0, 0};
mul_m4_v3(xr->runtime->worldspace_from_windowspace, viewspace_unit_per_rad_vec);
mul_m4_v3(viewspace_from_worldspace, viewspace_unit_per_rad_vec);
sub_v3_v3(viewspace_unit_per_rad_vec, window_origin_viewspace);
rads_per_viewspace_unit = 1.0 / len_v3(viewspace_unit_per_rad_vec);
float window_top_left_viewspace[3] = {0, ui_height, 0};
mul_m4_v3(xr->runtime->worldspace_from_windowspace, window_top_left_viewspace);
mul_m4_v3(viewspace_from_worldspace, window_top_left_viewspace);
sub_v3_v3(window_top_left_viewspace, window_origin_viewspace);
viewspace_ui_height = len_v3(window_top_left_viewspace);
}
float center_viewspace[3] = {ui_width / 2.0f, ui_height / 2.0f, 0};
mul_m4_v3(xr->runtime->worldspace_from_windowspace, center_viewspace);
// curve the UI so its easier to read (for now its at an arbitrary distance)
mul_m4_v3(viewspace_from_worldspace, center_viewspace);
float radius = len_v3(center_viewspace) * 2.0f *
xr->session_settings.mirrored_ui_distance_factor;
float ui_width_arclength = total_rads_per_ui_width * radius;
float pre_ui_width = ui_width;
float pre_ui_height = ui_height;
int windex;
bool hide_ui_on_mouse_over_v3d = (xr->session_settings.flag &
XR_SESSION_HIDE_UI_MIRROR_ON_MOUSE_OVER_V3D) != 0;
float mirror_ui_camera_center_viewspace[3] = {0, 0, radius};
if (!hide_ui_on_mouse_over_v3d || !is_mouse_over_v3d) {
LISTBASE_FOREACH_INDEX (LinkData *, ld, &xr->full_blender_ui_screens, windex) {
GPUOffScreen *offscreen = ld->data;
if (offscreen == NULL)
continue;
ui_width = GPU_offscreen_width(offscreen);
ui_height = GPU_offscreen_height(offscreen);
short window_pos_x = 0;
short window_pos_y = 0;
if (windex < 32) {
window_pos_x = xr->window_positions_xy[windex * 2 + 0];
window_pos_y = xr->window_positions_xy[windex * 2 + 1];
}
/* Setup offscreen color texture for drawing. */
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
GPU_texture_bind(texture, 0);
/* No mipmaps or filtering. */
// GPU_texture_mipmap_mode(texture, false, false);
GPU_texture_mipmap_mode(texture, true, true);
// wmWindowViewport(win); // not necessary?
// float vp_mat[4][4];
// GPU_matrix_model_view_projection_get();
// immUniformMatrix4fv("ModelViewProjectionMatrix",);
// GPU_matrix_push()
// float vert_positions[3 * 4] = {
// -halfresx,
// -halfresy,
// ui_distance,
// //
// halfresx,
// -halfresy,
// ui_distance,
// //
// halfresx,
// halfresy,
// ui_distance,
// //
// -halfresx,
// halfresy,
// ui_distance,
// };
// window space vert positoins (basically pixel coords)
int total_verts = 4;
// float vert_positions[3 * 4] = {
// 0,
// 0,
// 0,
// //
// ui_width,
// 0,
// 0,
// //
// ui_width,
// ui_height,
// 0,
// //
// 0,
// ui_height,
// 0,
// };
// float vert_texcoords[2 * 4] = {0,
// 0,
// //
// 1,
// 0,
// //
// 1,
// 1,
// //
// 0,
// 1};
#define vertices_per_width 32
total_verts = (vertices_per_width + 0) * (vertices_per_width + 0);
float vert_positions[3 * (vertices_per_width + 0) * (vertices_per_width + 0)];
float vert_texcoords[2 * (vertices_per_width + 0) * (vertices_per_width + 0)];
for (int y = 0; y < vertices_per_width; y++) {
for (int x = 0; x < vertices_per_width; x++) {
int vert_index = (x + y * vertices_per_width);
float mu_x = ((float)x / (vertices_per_width - 1));
float mu_y = ((float)y / (vertices_per_width - 1));
vert_positions[vert_index * 3 + 0] = mu_x * ui_width;
vert_positions[vert_index * 3 + 1] = mu_y * ui_height;
vert_positions[vert_index * 3 + 2] = 0;
// float z = fabsf((mu_x - 0.5f) * 2.0f * ui_width);
// vert_positions[vert_index * 3 + 2] = .2 * z * z;
/*
z = fabs((mu_x - 0.5f) * 2.0f * ui_width);
vert_positions[vert_index * 3 + 2] = .001f * z * z;*/
/* z = mu_x;
vert_positions[vert_index * 3 + 2] = 5 * ui_width * -sin(z * 3.14);
vert_positions[vert_index * 3 + 0] = -.4 * ui_width * sin((mu_x + 0.5) * 3.14) +
.5 * ui_width;*/
// vert_positions[vert_index * 3 + 2] = mu_x * 10;
// float base_length = 500;
// float center_xz[2] = {(0.5f * ui_width), 10*base_length};
// float vec[2] = {vert_positions[vert_index * 3 + 0], vert_positions[vert_index * 3 +
// 2]}; sub_v2_v2(vec, center_xz); normalize_v2_length(vec, base_length); add_v2_v2(vec,
// center_xz); vert_positions[vert_index * 3 + 0] = vec[0]; vert_positions[vert_index * 3
// + 2] = vec[1];
/*
float center[3] = {0.5f * ui_width, 0.5f * ui_height, -1000};
sub_v3_v3(&vert_positions[vert_index * 3], center);
normalize_v3_length(&vert_positions[vert_index * 3], 1000);
add_v3_v3(&vert_positions[vert_index * 3], center);*/
vert_texcoords[vert_index * 2 + 0] = mu_x;
vert_texcoords[vert_index * 2 + 1] = mu_y;
}
}
#define tri_buffer_len (2 * 3 * (vertices_per_width + -1) * (vertices_per_width + -1))
int tri_index_buffer[tri_buffer_len];
for (int y = 0; y < vertices_per_width - 1; y++) {
for (int x = 0; x < vertices_per_width - 1; x++) {
int vert_index = (x + y * vertices_per_width);
int tri_index = (x + y * (vertices_per_width - 1));
tri_index_buffer[tri_index * 6 + 0] = vert_index;
tri_index_buffer[tri_index * 6 + 1] = ((x + 0) + (y + 1) * vertices_per_width);
tri_index_buffer[tri_index * 6 + 2] = ((x + 1) + (y + 1) * vertices_per_width);
tri_index_buffer[tri_index * 6 + 3] = vert_index;
tri_index_buffer[tri_index * 6 + 4] = ((x + 1) + (y + 1) * vertices_per_width);
tri_index_buffer[tri_index * 6 + 5] = ((x + 1) + (y + 0) * vertices_per_width);
}
}
// int soffx = 0;
// const int sizex = WM_window_pixels_x(win);
// const int sizey = WM_window_pixels_y(win);
// /* wmOrtho for the screen has this same offset */
// const float halfx = GLA_PIXEL_OFS / sizex;
// const float halfy = GLA_PIXEL_OFS / sizex;
/* Texture is already bound to GL_TEXTURE0 unit. */
// DONE: so the rads origin (vert pos X) and the screen space to viewpsace origin need to
// match
// w/ camera forward as zero.
float vert_pos_viewspace[3];
for (int i = 0; i < total_verts; i++) {
float *vert_position = &vert_positions[i * 3];
// vert_position[1] = (vert_position[1] / ui_height);
// GG: TODO: scale from center of view3D instead of bottom left corner since the user
// sets up their mirror UI offsets based on the raycast range/area. Currently the scaling
// moves the entire UI/v3d area so it no longer matches with the raycast effective
// range/area thus the user will have to re-offset everything again.
// mul_v3_fl(vert_position, xr->session_settings.mirrored_ui_scale);
// angle span and ui_scale counter eachother, so need yscale for really wide windows to see
// better
vert_position[1] *= xr->session_settings.mirrored_ui_scale_y;
add_v2_v2(vert_position, xr->session_settings.mirrored_ui_offset);
// better Y default. than zero
vert_position[1] -= ui_height * 0.5 * xr->session_settings.mirrored_ui_scale_y;
vert_position[0] += window_pos_x;
vert_position[1] += window_pos_y;
mul_m4_v3(xr->runtime->worldspace_from_windowspace, vert_position);
mul_m4_v3(viewspace_from_worldspace, vert_position);
float offset_from_origin_viewspace[3];
copy_v3_v3(offset_from_origin_viewspace, vert_position);
sub_v3_v3(offset_from_origin_viewspace, window_origin_viewspace);
// I'm assuming that the UI is planar, which it is, and that viewspace up is aligned with
// UI up, which I think is true.
vert_position[1] = (offset_from_origin_viewspace[1] / viewspace_ui_height) *
(ui_width_arclength * resy_per_x);
sub_v3_v3(vert_position, mirror_ui_camera_center_viewspace);
float radsxz = -vert_position[0] * rads_per_viewspace_unit;
radsxz += 3.14;
float xz_rotation_forward[3] = {sinf(radsxz), 0, cosf(radsxz)};
vert_position[0] = xz_rotation_forward[0] * radius;
vert_position[2] = xz_rotation_forward[2] * radius;
// float viewspace_up[3] = {0, 1, 0};
// float secondary_rotation_axis[3];
// cross_v3_v3v3(secondary_rotation_axis, viewspace_up, xz_rotation_forward);
//
// float radsyz = -vert_position[1] * rads_per_viewspace_unit;
////radsyz += 3.14;
// rotate_v3_v3v3fl(vert_position, xz_rotation_forward, secondary_rotation_axis, radsyz);
// normalize_v3_length(vert_position, radius);
mul_m4_v3(worldspace_from_viewspace, vert_position);
}
//// curve the UI so its easier to read (for now its at an arbitrary distance)
// mul_m4_v3(viewspace_from_worldspace, &vert_positions[i * 3]);
//// float v2[2] = {vert_positions[i * 3 + 0], vert_positions[i * 3 + 2]};
// normalize_v3_length(&vert_positions[i * 3], radius);
// mul_m4_v3(worldspace_from_viewspace, &vert_positions[i * 3]);
format = immVertexFormat();
texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
// draw UI Xrayed through objects. (a bit annoying for animating, but nice in general..?)
if ((xr->session_settings.flag & XR_SESSION_USE_UI_MIRROR_XRAY) != 0) {
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
float gray = 0.75f;
immUniformColor4f(0, gray, 0, 0.5f);
GPU_depth_test(GPU_DEPTH_NONE);
GPU_depth_mask(false);
immBegin(GPU_PRIM_TRIS, tri_buffer_len);
// for (int i = 0; i < total_verts; i++) {
// immAttr2fv(texcoord, &vert_texcoords[i * 2]);
// immVertex3fv(pos, &vert_positions[i * 3]);
// }
for (int i = 0; i < tri_buffer_len; i++) {
int vertex_index = tri_index_buffer[i];
immAttr2fv(texcoord, &vert_texcoords[vertex_index * 2]);
immVertex3fv(pos, &vert_positions[vertex_index * 3]);
}
immEnd();
immUnbindProgram();
}
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
GPU_depth_mask(true);
immBegin(GPU_PRIM_TRIS, tri_buffer_len);
// immBegin(GPU_PRIM_TRI_FAN, 4);
// for (int i = 0; i < 4; i++) {
// immAttr2fv(texcoord, &vert_texcoords[i * 2]);
// immVertex3fv(pos, &vert_positions[i * 3]);
// }
for (int i = 0; i < tri_buffer_len; i++) {
int vertex_index = tri_index_buffer[i];
immAttr2fv(texcoord, &vert_texcoords[vertex_index * 2]);
immVertex3fv(pos, &vert_positions[vertex_index * 3]);
}
immEnd();
immUnbindProgram();
GPU_texture_unbind(texture);
#undef vertices_per_width
#undef tri_buffer_len
}
}
ui_width = pre_ui_width;
ui_height = pre_ui_height;
GPU_depth_test(GPU_DEPTH_NONE);
GPU_depth_mask(false);
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
// GG:TODO: replace with drawing blender UI to a quad (then eventually sphere quad)
// currently just ensuring XR arbitrary drawing.
immBeginAtMost(GPU_PRIM_TRI_FAN, 4);
// float size = 0.005f;
float size = 6;
float mverts[3 * 4] = {
-size,
-size,
0, // size * .5f,
//
size,
-size,
0, // size * .5f,
//
size,
size,
0, // size * .5f,
//
-size,
size,
0, // size * .5f,
};
float windowspace_from_mouse[4][4];
unit_m4(windowspace_from_mouse);
// windowspace_from_mouse[3][0] = xr->runtime->mouse_xy[0];
// windowspace_from_mouse[3][1] = xr->runtime->mouse_xy[1];
windowspace_from_mouse[3][0] = xr->mouse_position_global_xy[0];
windowspace_from_mouse[3][1] = xr->mouse_position_global_xy[1];
for (int i = 0; i < 4; i++) {
// mul_m4_v3(fmouse_mat, &mverts[i * 3]);
float *vert_position = &mverts[i * 3];
mul_m4_v3(windowspace_from_mouse, &mverts[i * 3]);
// GG: TODO: scale from center of view3D instead of bottom left corner since the user
// sets up their mirror UI offsets based on the raycast range/area. Currently the scaling
// moves the entire UI/v3d area so it no longer matches with the raycast effective range/area
// thus the user will have to re-offset everything again.
// mul_v3_fl(vert_position, xr->session_settings.mirrored_ui_scale);
// angle span and ui_scale counter eachother, so need yscale for really wide windows to see
// better
vert_position[1] *= xr->session_settings.mirrored_ui_scale_y;
add_v2_v2(vert_position, xr->session_settings.mirrored_ui_offset);
// better Y default. than zero
vert_position[1] -= ui_height * 0.5 * xr->session_settings.mirrored_ui_scale_y;
mul_m4_v3(xr->runtime->worldspace_from_windowspace, &mverts[i * 3]);
mul_m4_v3(viewspace_from_worldspace, vert_position);
float offset_from_origin_viewspace[3];
copy_v3_v3(offset_from_origin_viewspace, vert_position);
sub_v3_v3(offset_from_origin_viewspace, window_origin_viewspace);
// I'm assuming that the UI is planar, which it is, and that viewspace up is aligned with UI
// up, which I think is true.
vert_position[1] = (offset_from_origin_viewspace[1] / viewspace_ui_height) *
(ui_width_arclength * resy_per_x);
sub_v3_v3(vert_position, mirror_ui_camera_center_viewspace);
float radsxz = -vert_position[0] * rads_per_viewspace_unit;
float width_arclength = radsxz * radius;
radsxz += 3.14;
float xz_rotation_forward[3] = {sinf(radsxz), 0, cosf(radsxz)};
vert_position[0] = xz_rotation_forward[0] * radius * 0.999f;
vert_position[2] = xz_rotation_forward[2] * radius * 0.999f; // NOTE: only use 0.99f on final
// vert_position[1] = (vert_position[1] / ui_width) * (width_arclength * resy_per_x);
//
//
// float viewspace_up[3] = {0, 1, 0};
// float secondary_rotation_axis[3];
// cross_v3_v3v3(secondary_rotation_axis, viewspace_up, xz_rotation_forward);
// float radsyz = -vert_position[1] * rads_per_viewspace_unit;
//// radsyz += 3.14;
// rotate_v3_v3v3fl(vert_position, xz_rotation_forward, secondary_rotation_axis, radsyz);
// normalize_v3_length(vert_position, radius * 0.99f);
mul_m4_v3(worldspace_from_viewspace, vert_position);
}
// top left
immAttr4f(col, 1, 1, 1, 0.5f);
immVertex3fv(pos, &mverts[0]);
// top right
immAttr4f(col, 1, 1, 1, 0.5f);
immVertex3fv(pos, &mverts[3]);
// bottom left
immAttr4f(col, 1, 1, 1, 0.5f);
immVertex3fv(pos, &mverts[6]);
// bottom right
immAttr4f(col, 1, 1, 1, 0.5f);
immVertex3fv(pos, &mverts[9]);
immEnd();
const int floats_per_vert = 3;
const int verts_per_line = 2;
const int total_lines = 3;
float line_lengths = 0.1;
const int total_verts = 6;
float scene_cursor_verts[3 * 2 * 3] = {
// X-axis
-1,
0,
0,
//
1,
0,
0,
// Y-axis
0,
-1,
0,
//
0,
1,
0,
// Z-axis
0,
0,
-1,
//
0,
0,
1,
};
float *scene_cursor_location = xr->runtime->cursor_location_worldspace;
for (int i = 0; i < total_verts; i++) {
float *vert_position = &scene_cursor_verts[i * 3];
mul_v3_fl(vert_position, line_lengths);
add_v3_v3(vert_position, scene_cursor_location);
}
immBegin(GPU_PRIM_LINES, total_verts);
for (int i = 0; i < total_verts; i++) {
float *vert_position = &scene_cursor_verts[i * 3];
immAttr4f(col, 1, 1, 0, 0.5f);
immVertex3fv(pos, vert_position);
}
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
GPU_depth_test(GPU_DEPTH_NONE);
}

View file

@ -78,8 +78,29 @@ typedef struct wmXrRuntimeData {
ListBase actionmaps; /* #XrActionMap */
short actactionmap;
short selactionmap;
int flags;
int _pad;
int mouse_xy[2];
float v3d_origin[3];
float v3d_ray_dir[3];
float worldspace_from_windowspace[4][4];
float viewspace_from_worldspace[4][4];
float worldspace_from_viewspace[4][4];
float cursor_location_worldspace[3];
float _pad1;
// float ui_res_scaling_factor;
// ideal palce for this, but this etnire struct is intern..
// // GG: Might need to store some kind of handle/ref to window so that
// // wm_draw_window() can draw to the proper offscreen buffer?
// ListBase full_blender_ui_screens; /* #LinkData of GPUOffScreen */
} wmXrRuntimeData;
enum wmXrRuntimeDataFlags {
XR_RUNTIME_IS_MOUSE_OVER_V3D = (1 << 0),
};
typedef struct wmXrViewportPair {
struct wmXrViewportPair *next, *prev;
struct GPUOffScreen *offscreen;
@ -94,6 +115,7 @@ typedef struct {
struct ARegionType *controller_art;
/** Controller draw callback handle. */
void *controller_draw_handle;
void *draw_mirror_blender_ui_to_xr;
} wmXrSurfaceData;
typedef struct wmXrDrawData {
@ -234,3 +256,10 @@ void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_ima
*/
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata);
void draw_mirror_blender_ui_to_xr(const struct bContext *C,
struct ARegion *region,
void *customdata);
#ifdef __cplusplus
}
#endif

View file

@ -128,7 +128,6 @@ static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op))
v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT;
wm_xr_session_toggle(wm, win, 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;

View file

@ -19,12 +19,14 @@
#include "DEG_depsgraph_query.h"
#include "DNA_camera_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DRW_engine.h"
#include "ED_screen.h"
#include "ED_space_api.h"
#include "ED_view3d.h"
#include "GHOST_C-api.h"
@ -38,6 +40,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_draw.h"
#include "wm_event_system.h"
#include "wm_surface.h"
#include "wm_window.h"
@ -102,6 +105,13 @@ static void wm_xr_session_exit_cb(void *customdata)
/* Free the entire runtime data (including session state and context), to play safe. */
wm_xr_runtime_data_free(&xr_data->runtime);
LISTBASE_FOREACH (LinkData *, ld, &xr_data->full_blender_ui_screens) {
if (ld->data != NULL)
GPU_offscreen_free(ld->data);
ld->data = NULL;
}
BLI_freelistN(&xr_data->full_blender_ui_screens);
}
static void wm_xr_session_begin_info_create(wmXrData *xr_data,
@ -157,14 +167,15 @@ bool WM_xr_session_is_ready(const wmXrData *xr)
}
static void wm_xr_session_base_pose_calc(const Scene *scene,
const XrSessionSettings *settings,
const wmXrData *xr,
GHOST_XrPose *r_base_pose,
float *r_base_scale)
{
XrSessionSettings *settings = &xr->session_settings;
const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) &&
settings->base_pose_object) ?
settings->base_pose_object :
scene->camera;
NULL;
if (settings->base_pose_type == XR_BASE_POSE_CUSTOM) {
float tmp_quatx[4], tmp_quatz[4];
@ -178,17 +189,22 @@ static void wm_xr_session_base_pose_calc(const Scene *scene,
float tmp_quat[4];
float tmp_eul[3];
mat4_to_loc_quat(r_base_pose->position, tmp_quat, base_pose_object->object_to_world);
mat4_to_loc_quat(
r_base_pose->position, r_base_pose->orientation_quat, base_pose_object->object_to_world);
// mat4_to_loc_quat(r_base_pose->position, tmp_quat, base_pose_object->object_to_world);
/* 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);
// 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);
mat4_to_loc_quat(r_base_pose->position,
r_base_pose->orientation_quat,
xr->runtime->worldspace_from_viewspace);
// copy_v3_fl(r_base_pose->position, 0.0f);
// axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2);
}
*r_base_scale = settings->base_scale;
@ -208,7 +224,7 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data,
r_draw_data->surface_data = g_xr_surface->customdata;
wm_xr_session_base_pose_calc(
r_draw_data->scene, settings, &r_draw_data->base_pose, &r_draw_data->base_scale);
r_draw_data->scene, xr_data, &r_draw_data->base_pose, &r_draw_data->base_scale);
}
wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm,
@ -1179,6 +1195,7 @@ static void wm_xr_session_events_dispatch(wmXrData *xr,
MEM_freeN(actions);
}
// GG: XR update loop func
void wm_xr_session_actions_update(wmWindowManager *wm)
{
wmXrData *xr = &wm->xr;
@ -1235,7 +1252,6 @@ void wm_xr_session_actions_update(wmWindowManager *wm)
if (!xr->runtime->area) {
xr->runtime->area = ED_area_offscreen_create(win, SPACE_VIEW3D);
}
/* Set XR area object type flags for operators. */
View3D *v3d = xr->runtime->area->spacedata.first;
v3d->object_type_exclude_viewport = settings->object_type_exclude_viewport;
@ -1278,6 +1294,18 @@ void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
surface_data->controller_art, wm_xr_draw_controllers, xr, REGION_DRAW_POST_VIEW);
}
}
// GG: TODO: Since the runtime may end before user explicitly deactivates it, then we need to
// actually
// clean this up elsewhere (look for general XR runtime free func ).
// GG: TODO: this is tmp placement of code. I just need to get the core implemnetation working
// before worry about proper implementation/blender-code-consistency
if (surface_data) {
if (surface_data->controller_art) {
surface_data->draw_mirror_blender_ui_to_xr = ED_region_draw_cb_activate(
surface_data->controller_art, draw_mirror_blender_ui_to_xr, xr, REGION_DRAW_POST_VIEW);
}
}
}
}
@ -1295,6 +1323,18 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state)
surface_data->controller_draw_handle = NULL;
}
}
// GG: Move to proper location. I Think this is for literal hand control data clearing.
if (g_xr_surface) {
wmXrSurfaceData *surface_data = g_xr_surface->customdata;
if (surface_data && surface_data->controller_draw_handle) {
if (surface_data->controller_art) {
ED_region_draw_cb_exit(surface_data->controller_art,
surface_data->draw_mirror_blender_ui_to_xr);
}
surface_data->draw_mirror_blender_ui_to_xr = NULL;
}
}
}
/** \} */ /* XR-Session Actions */
@ -1326,6 +1366,255 @@ static void wm_xr_session_surface_draw(bContext *C)
Scene *scene;
Depsgraph *depsgraph;
wm_xr_session_scene_and_depsgraph_get(wm, &scene, &depsgraph);
{
// placed in draw() instead of update to reduce jitter problems
wmWindow *win;
int wn_index;
LISTBASE_FOREACH_INDEX (wmWindow *, win, &wm->windows, wn_index) {
// BLI_assert(wn_index < BLI_)
LinkData *ld = BLI_findlink(&wm->xr.full_blender_ui_screens, wn_index);
if (ld != NULL && wn_index < 32) {
wm->xr.window_positions_xy[wn_index * 2 + 0] = win->posx;
wm->xr.window_positions_xy[wn_index * 2 + 1] = win->posy;
wm->xr.mouse_positions_per_window_xy[wn_index * 2 + 0] = win->eventstate->xy[0];
wm->xr.mouse_positions_per_window_xy[wn_index * 2 + 1] = win->eventstate->xy[1];
// this check doesnt work in general since mouse coords .. arent releative to the window??
// I thin kthey're relative to the active window?
/*if (win->eventstate->xy[0] >= 0 &&
win->eventstate->xy[0] <= win->sizex &&
win->eventstate->xy[1] >= 0 &&
win->eventstate->xy[1] <= win->sizey)*/
if (win->active != 0) {
wm->xr.mouse_position_global_xy[0] = win->posx + win->eventstate->xy[0];
wm->xr.mouse_position_global_xy[1] = win->posy + win->eventstate->xy[1];
}
}
}
wmXrData *xr = &wm->xr;
win = wm_xr_session_root_window_or_fallback_get(wm, wm->xr.runtime);
copy_v2_v2_int(wm->xr.runtime->mouse_xy, win->eventstate->xy);
xr->evil_C = C;
copy_v3_v3(xr->runtime->cursor_location_worldspace, scene->cursor.location);
ARegion *v3d_region_under_mouse = NULL;
bScreen *screen = WM_window_get_active_screen(win);
// View3D *v3d = NULL;
xr->runtime->flags &= ~XR_RUNTIME_IS_MOUSE_OVER_V3D;
ED_screen_areas_iter (win, screen, area) {
if (area->spacetype != SPACE_VIEW3D) {
continue;
}
// v3d = area->spacedata.first;
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (!region->visible) {
continue;
}
if (region->overlap) {
continue;
}
if (region->draw_buffer->viewport == NULL) {
continue;
}
if (region->regiontype != RGN_TYPE_WINDOW) {
continue;
}
int mouse_over_result = 0;
SET_FLAG_FROM_TEST(mouse_over_result,
ED_region_contains_xy(region, win->eventstate->xy),
XR_RUNTIME_IS_MOUSE_OVER_V3D);
xr->runtime->flags |= mouse_over_result;
v3d_region_under_mouse = region;
}
}
zero_v3(wm->xr.runtime->v3d_origin);
zero_v3(wm->xr.runtime->v3d_ray_dir);
if (v3d_region_under_mouse != NULL) {
float v3d_origin[3];
float v3d_ray_dir[3];
float mval[2] = {
win->eventstate->xy[0],
win->eventstate->xy[1],
};
mval[0] = mval[0] - v3d_region_under_mouse->winrct.xmin;
mval[1] = mval[1] - v3d_region_under_mouse->winrct.ymin;
ED_view3d_win_to_origin(v3d_region_under_mouse, mval, v3d_origin);
ED_view3d_win_to_vector(v3d_region_under_mouse, mval, v3d_ray_dir);
copy_v3_v3(wm->xr.runtime->v3d_origin, v3d_origin);
copy_v3_v3(wm->xr.runtime->v3d_ray_dir, v3d_ray_dir);
// wm->xr.runtime->v3d_clip_start = v3d->clip_start; // distance to draw UI from cam origin
// mval[0] = 0;
// mval[1] = 0;
// ED_view3d_win_to_origin(&wm->xr.runtime->world_viewplane_bottom_left_origin, mval,
// v3d_origin); ED_view3d_win_to_vector(&wm->xr.runtime->world_viewplane_bottom_left_raydir,
// mval, v3d_ray_dir);
// mval[0] = v3d_region_under_mouse->winx;
// mval[1] = 0;
// ED_view3d_win_to_origin(&wm->xr.runtime->world_viewplane_bottom_right_origin, mval,
// v3d_origin); ED_view3d_win_to_vector(&wm->xr.runtime->world_viewplane_bottom_right_raydir,
// mval, v3d_ray_dir);
// mval[0] = v3d_region_under_mouse->winx;
// mval[1] = v3d_region_under_mouse->winy;
// ED_view3d_win_to_origin(&wm->xr.runtime->world_viewplane_top_right_origin, mval,
// v3d_origin); ED_view3d_win_to_vector(&wm->xr.runtime->world_viewplane_top_right_raydir,
// mval, v3d_ray_dir);
// mval[0] = 0;
// mval[1] = v3d_region_under_mouse->winy;
// ED_view3d_win_to_origin(&wm->xr.runtime->world_viewplane_top_left_origin, mval,
// v3d_origin); ED_view3d_win_to_vector(&wm->xr.runtime->world_viewplane_top_left_raydir,
// mval, v3d_ray_dir); float v3d_projection_matrix[4][4];
// {
// RegionView3D *r3d = v3d_region_under_mouse->regiondata;
// Scene *scene;
// Depsgraph *depsgraph;
// wm_xr_session_scene_and_depsgraph_get(wm, &scene, &depsgraph);
// rctf viewplane = {0};
// float clip_start = 0;
// float clip_end = 0;
// bool is_ortho = ED_view3d_viewplane_get(depsgraph,
// v3d,
// r3d,
// v3d_region_under_mouse->winx,
// v3d_region_under_mouse->winy,
// &viewplane,
// &clip_start,
// &clip_end,
// NULL);
// if (is_ortho) {
// orthographic_m4(v3d_projection_matrix,
// viewplane.xmin,
// viewplane.xmax,
// viewplane.ymin,
// viewplane.ymax,
// clip_start,
// clip_end);
// }
// else {
// perspective_m4(v3d_projection_matrix,
// viewplane.xmin,
// viewplane.xmax,
// viewplane.ymin,
// viewplane.ymax,
// clip_start,
// clip_end);
// }
// }
// takes region [0,1] to worldspace [world left/bottom, world right/top]
RegionView3D *r3d = v3d_region_under_mouse->regiondata;
copy_m4_m4(xr->runtime->worldspace_from_viewspace, r3d->viewinv);
copy_m4_m4(xr->runtime->viewspace_from_worldspace, r3d->viewmat);
float worldspace_from_normalized_region[4][4];
unit_m4(worldspace_from_normalized_region);
{
// Scene *scene;
// Depsgraph *depsgraph;
// wm_xr_session_scene_and_depsgraph_get(wm, &scene, &depsgraph);
// Camera *cam_obj = scene->camera->data;
float worldspace_bottom_left[3];
float worldspace_bottom_right[3];
float worldspace_top_left[3];
float viewspace_from_ndc[4][4]; // same as projection input space
copy_m4_m4(viewspace_from_ndc, r3d->persinv);
float worldspace_from_viewspace[4][4];
copy_m4_m4(worldspace_from_viewspace, r3d->viewinv);
// invert_m4(worldspace_from_viewspace);
float worldspace_from_ndc[4][4];
copy_m4_m4(worldspace_from_ndc, worldspace_from_viewspace);
mul_m4_m4_post(worldspace_from_ndc, viewspace_from_ndc);
// GG: USE: besides changing this between [-1,1],
// you can also move and scale the UI by unlocking the camera, zooming/inout and
// panning then relocking the camera.
// GG: TIP: GOTCHA: Wireframe bones dont show in XR... Solid do.
float ndc_depth = .75f;
float ndc[3];
ndc[0] = -1;
ndc[1] = -1;
ndc[2] = ndc_depth;
// persinv is worldspace_from_ndc?, not viewspace_from_ndc?
mul_v3_project_m4_v3(worldspace_bottom_left, viewspace_from_ndc, ndc);
// mul_m4_v3(worldspace_from_viewspace, worldspace_bottom_left);
// mul_m4_v3(worldspace_from_viewspace, worldspace_bottom_left);
// ndc[0] = 0;
// ndc[1] = 0;
// ndc[2] = -5;
// mul_v3_project_m4_v3(worldspace_bottom_left, viewspace_from_ndc, ndc);
// mul_m4_v3(worldspace_from_viewspace, worldspace_bottom_left);
ndc[0] = 1;
ndc[1] = -1;
ndc[2] = ndc_depth;
mul_v3_project_m4_v3(worldspace_bottom_right, viewspace_from_ndc, ndc);
// mul_m4_v3(worldspace_from_viewspace, worldspace_bottom_right);
// mul_v3_project_m4_v3(worldspace_bottom_right, worldspace_from_ndc, ndc);
// ndc[0] = 1;
// ndc[1] = 1;
// ndc[2] = 0;
// mul_v3_project_m4_v3(worldspace_top_right, worldspace_from_ndc, ndc);
ndc[0] = -1;
ndc[1] = 1;
ndc[2] = ndc_depth;
mul_v3_project_m4_v3(worldspace_top_left, viewspace_from_ndc, ndc);
// mul_m4_v3(worldspace_from_viewspace, worldspace_top_left);
// mul_v3_project_m4_v3(worldspace_top_left, worldspace_from_ndc, ndc);
sub_v3_v3v3(
worldspace_from_normalized_region[0], worldspace_bottom_right, worldspace_bottom_left);
sub_v3_v3v3(
worldspace_from_normalized_region[1], worldspace_top_left, worldspace_bottom_left);
cross_v3_v3v3(worldspace_from_normalized_region[2],
worldspace_from_normalized_region[0],
worldspace_from_normalized_region[1]);
copy_v3_v3(worldspace_from_normalized_region[3], worldspace_bottom_left);
}
float windowspace_from_normalized_region[4][4];
unit_m4(windowspace_from_normalized_region);
{
float windowspace_bottom_left[3] = {
v3d_region_under_mouse->winrct.xmin, v3d_region_under_mouse->winrct.ymin, 0};
float windowspace_bottom_right[3] = {
v3d_region_under_mouse->winrct.xmax, v3d_region_under_mouse->winrct.ymin, 0};
float windowspace_top_left[3] = {
v3d_region_under_mouse->winrct.xmin, v3d_region_under_mouse->winrct.ymax, 0};
sub_v3_v3v3(windowspace_from_normalized_region[0],
windowspace_bottom_right,
windowspace_bottom_left);
sub_v3_v3v3(
windowspace_from_normalized_region[1], windowspace_top_left, windowspace_bottom_left);
cross_v3_v3v3(windowspace_from_normalized_region[2],
windowspace_from_normalized_region[0],
windowspace_from_normalized_region[1]);
copy_v3_v3(windowspace_from_normalized_region[3], windowspace_bottom_left);
}
float normalized_region_from_windowspace[4][4];
invert_m4_m4(normalized_region_from_windowspace, windowspace_from_normalized_region);
mul_m4_m4m4(wm->xr.runtime->worldspace_from_windowspace,
worldspace_from_normalized_region,
normalized_region_from_windowspace);
}
}
/* Might fail when force-redrawing windows with #WM_redraw_windows(), which is done on file
* writing for example. */
// BLI_assert(DEG_is_fully_evaluated(depsgraph));