Blender V4.5
wm_xr_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
13#include <cstring>
14
15#include "DNA_userdef_types.h"
16
17#include "BLI_listbase.h"
18#include "BLI_math_geom.h"
19#include "BLI_math_matrix.h"
20#include "BLI_math_rotation.h"
21#include "BLI_math_vector.h"
22
24
25#include "GHOST_C-api.h"
26
27#include "GPU_batch_presets.hh"
28#include "GPU_immediate.hh"
29#include "GPU_matrix.hh"
30#include "GPU_state.hh"
31#include "GPU_viewport.hh"
32
33#include "WM_api.hh"
34
35#include "wm_xr_intern.hh"
36
37void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
38{
39 quat_to_mat4(r_mat, pose->orientation_quat);
40 copy_v3_v3(r_mat[3], pose->position);
41}
42
43void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4])
44{
45 wm_xr_pose_to_mat(pose, r_mat);
46
47 BLI_assert(scale > 0.0f);
48 mul_v3_fl(r_mat[0], scale);
49 mul_v3_fl(r_mat[1], scale);
50 mul_v3_fl(r_mat[2], scale);
51}
52
53void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
54{
55 float iquat[4];
56 invert_qt_qt_normalized(iquat, pose->orientation_quat);
57 quat_to_mat4(r_imat, iquat);
58 translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
59}
60
61void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4])
62{
63 float iquat[4];
64 invert_qt_qt_normalized(iquat, pose->orientation_quat);
65 quat_to_mat4(r_imat, iquat);
66
67 BLI_assert(scale > 0.0f);
68 scale = 1.0f / scale;
69 mul_v3_fl(r_imat[0], scale);
70 mul_v3_fl(r_imat[1], scale);
71 mul_v3_fl(r_imat[2], scale);
72
73 translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
74}
75
76static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
77 const GHOST_XrDrawViewInfo *draw_view,
78 const XrSessionSettings *session_settings,
79 const wmXrSessionState *session_state,
80 float r_viewmat[4][4],
81 float r_projmat[4][4])
82{
83 GHOST_XrPose eye_pose;
84 float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4];
85
86 /* Calculate inverse eye matrix. */
87 copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
88 copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
89 if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
90 sub_v3_v3(eye_pose.position, draw_view->local_pose.position);
91 }
92 if ((session_settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
93 sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
94 }
95
96 wm_xr_pose_to_imat(&eye_pose, eye_inv);
97
98 /* Apply base pose and navigation. */
99 wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv);
100 wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv);
101 mul_m4_m4m4(m, eye_inv, base_inv);
102 mul_m4_m4m4(r_viewmat, m, nav_inv);
103
104 perspective_m4_fov(r_projmat,
105 draw_view->fov.angle_left,
106 draw_view->fov.angle_right,
107 draw_view->fov.angle_up,
108 draw_view->fov.angle_down,
109 session_settings->clip_start,
110 session_settings->clip_end);
111}
112
114 const wmXrRuntimeData *runtime_data,
115 const wmXrSurfaceData *surface_data,
116 const GHOST_XrDrawViewInfo *draw_view)
117{
118 const wmXrViewportPair *vp = static_cast<const wmXrViewportPair *>(
119 BLI_findlink(&surface_data->viewports, draw_view->view_idx));
120 BLI_assert(vp && vp->viewport);
121
122 const bool is_upside_down = GHOST_XrSessionNeedsUpsideDownDrawing(runtime_data->context);
123 rcti rect{};
124 rect.xmin = 0;
125 rect.ymin = 0;
126 rect.xmax = draw_view->width - 1;
127 rect.ymax = draw_view->height - 1;
128
129 wmViewport(&rect);
130
131 /* For upside down contexts, draw with inverted y-values. */
132 if (is_upside_down) {
133 std::swap(rect.ymin, rect.ymax);
134 }
135 GPU_viewport_draw_to_screen_ex(vp->viewport, 0, &rect, draw_view->expects_srgb_buffer, true);
136}
137
138void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
139{
140 wmXrDrawData *draw_data = static_cast<wmXrDrawData *>(customdata);
141 wmXrData *xr_data = draw_data->xr_data;
142 wmXrSurfaceData *surface_data = draw_data->surface_data;
143 wmXrSessionState *session_state = &xr_data->runtime->session_state;
144 XrSessionSettings *settings = &xr_data->session_settings;
145
146 const int display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags;
147
148 float viewmat[4][4], winmat[4][4];
149
151
152 wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data);
153 wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat);
154 wm_xr_session_state_update(settings, draw_data, draw_view, session_state);
155
156 if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) {
157 return;
158 }
159
160 const wmXrViewportPair *vp = static_cast<const wmXrViewportPair *>(
161 BLI_findlink(&surface_data->viewports, draw_view->view_idx));
162 BLI_assert(vp && vp->offscreen && vp->viewport);
163
164 /* In case a framebuffer is still bound from drawing the last eye. */
166 /* Some systems have drawing glitches without this. */
167 GPU_clear_depth(1.0f);
168
169 /* Draws the view into the surface_data->viewport's frame-buffers. */
171 draw_data->scene,
172 &settings->shading,
173 (eDrawType)settings->shading.type,
176 draw_view->width,
177 draw_view->height,
178 display_flags,
179 viewmat,
180 winmat,
181 settings->clip_start,
182 settings->clip_end,
183 true,
184 false,
185 true,
186 nullptr,
187 false,
188 vp->offscreen,
189 vp->viewport);
190
191 /* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A
192 * call to GPU_viewport_draw_to_screen() is still needed to get the final result from the
193 * viewport buffers composited together and potentially color managed for display on screen.
194 * It needs a bound frame-buffer to draw into, for which we simply reuse the GPUOffscreen one.
195 *
196 * In a next step, Ghost-XR will use the currently bound frame-buffer to retrieve the image
197 * to be submitted to the OpenXR swap-chain. So do not un-bind the off-screen yet! */
198
199 GPU_offscreen_bind(vp->offscreen, false);
200
201 wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view);
202}
203
204bool wm_xr_passthrough_enabled(void *customdata)
205{
206 wmXrDrawData *draw_data = static_cast<wmXrDrawData *>(customdata);
207 wmXrData *xr_data = draw_data->xr_data;
208 XrSessionSettings *settings = &xr_data->session_settings;
209
210 return (settings->draw_flags & V3D_OFSDRAW_XR_SHOW_PASSTHROUGH) != 0;
211}
212
213void wm_xr_disable_passthrough(void *customdata)
214{
215 wmXrDrawData *draw_data = static_cast<wmXrDrawData *>(customdata);
216 wmXrData *xr_data = draw_data->xr_data;
217 XrSessionSettings *settings = &xr_data->session_settings;
218
220 WM_global_report(RPT_INFO, "Passthrough not available");
221}
222
223static blender::gpu::Batch *wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_context,
224 const char *subaction_path)
225{
226 GHOST_XrControllerModelData model_data;
227
228 if (!GHOST_XrGetControllerModelData(xr_context, subaction_path, &model_data) ||
229 model_data.count_vertices < 1)
230 {
231 return nullptr;
232 }
233
234 GPUVertFormat format = {0};
237
239 GPU_vertbuf_data_alloc(*vbo, model_data.count_vertices);
240 vbo->data<GHOST_XrControllerModelVertex>().copy_from(
241 {model_data.vertices, model_data.count_vertices});
242
243 blender::gpu::IndexBuf *ibo = nullptr;
244 if (model_data.count_indices > 0 && ((model_data.count_indices % 3) == 0)) {
245 GPUIndexBufBuilder ibo_builder;
246 const uint prim_len = model_data.count_indices / 3;
247 GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, prim_len, model_data.count_vertices);
248 for (uint i = 0; i < prim_len; ++i) {
249 const uint32_t *idx = &model_data.indices[i * 3];
250 GPU_indexbuf_add_tri_verts(&ibo_builder, idx[0], idx[1], idx[2]);
251 }
252 ibo = GPU_indexbuf_build(&ibo_builder);
253 }
254
256}
257
259 GHOST_XrContextHandle xr_context,
261{
262 GHOST_XrControllerModelData model_data;
263
264 float color[4];
265 switch (settings->controller_draw_style) {
268 color[0] = color[1] = color[2] = 0.0f;
269 color[3] = 0.4f;
270 break;
273 color[0] = 0.422f;
274 color[1] = 0.438f;
275 color[2] = 0.446f;
276 color[3] = 0.4f;
277 break;
278 }
279
282
283 LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
284 if (!controller->grip_active) {
285 continue;
286 }
287
288 blender::gpu::Batch *model = controller->model;
289 if (!model) {
290 model = controller->model = wm_xr_controller_model_batch_create(xr_context,
291 controller->subaction_path);
292 }
293
294 if (model &&
295 GHOST_XrGetControllerModelData(xr_context, controller->subaction_path, &model_data) &&
296 model_data.count_components > 0)
297 {
299 GPU_batch_uniform_4fv(model, "color", color);
300
302 GPU_matrix_mul(controller->grip_mat);
303 for (uint component_idx = 0; component_idx < model_data.count_components; ++component_idx) {
304 const GHOST_XrControllerModelComponent *component = &model_data.components[component_idx];
306 GPU_matrix_mul(component->transform);
308 model->elem ? component->index_offset : component->vertex_offset,
309 model->elem ? component->index_count : component->vertex_count);
311 }
313 }
314 else {
315 /* Fallback. */
316 const float scale = 0.05f;
317 blender::gpu::Batch *sphere = GPU_batch_preset_sphere(2);
319 GPU_batch_uniform_4fv(sphere, "color", color);
320
322 GPU_matrix_mul(controller->grip_mat);
323 GPU_matrix_scale_1f(scale);
324 GPU_batch_draw(sphere);
326 }
327 }
328}
329
331{
332 bool draw_ray;
333 switch (settings->controller_draw_style) {
336 draw_ray = false;
337 break;
340 draw_ray = true;
341 break;
342 }
343
348
349 float viewport[4];
350 GPU_viewport_size_get_f(viewport);
351 immUniform2fv("viewportSize", &viewport[2]);
352
353 immUniform1f("lineWidth", 3.0f * U.pixelsize);
354
355 if (draw_ray) {
356 const float color[4] = {0.33f, 0.33f, 1.0f, 0.5f};
357 const float scale = settings->clip_end;
358 float ray[3];
359
362
363 LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
364 if (!controller->grip_active) {
365 continue;
366 }
367
369
370 const float(*mat)[4] = controller->aim_mat;
371 madd_v3_v3v3fl(ray, mat[3], mat[2], -scale);
372
374 immVertex3fv(pos, mat[3]);
375 immAttr4fv(col, color);
376 immVertex3fv(pos, ray);
377
378 immEnd();
379 }
380 }
381 else {
382 const float r[4] = {255 / 255.0f, 51 / 255.0f, 82 / 255.0f, 255 / 255.0f};
383 const float g[4] = {139 / 255.0f, 220 / 255.0f, 0 / 255.0f, 255 / 255.0f};
384 const float b[4] = {40 / 255.0f, 144 / 255.0f, 255 / 255.0f, 255 / 255.0f};
385 const float scale = 0.01f;
386 float x_axis[3], y_axis[3], z_axis[3];
387
390
391 LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
392 if (!controller->grip_active) {
393 continue;
394 }
395
397
398 const float(*mat)[4] = controller->aim_mat;
399 madd_v3_v3v3fl(x_axis, mat[3], mat[0], scale);
400 madd_v3_v3v3fl(y_axis, mat[3], mat[1], scale);
401 madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale);
402
404 immVertex3fv(pos, mat[3]);
405 immAttr4fv(col, r);
406 immVertex3fv(pos, x_axis);
407
409 immVertex3fv(pos, mat[3]);
410 immAttr4fv(col, g);
411 immVertex3fv(pos, y_axis);
412
414 immVertex3fv(pos, mat[3]);
415 immAttr4fv(col, b);
416 immVertex3fv(pos, z_axis);
417
418 immEnd();
419 }
420 }
421
423}
424
425void wm_xr_draw_controllers(const bContext * /*C*/, ARegion * /*region*/, void *customdata)
426{
427 wmXrData *xr = static_cast<wmXrData *>(customdata);
428 const XrSessionSettings *settings = &xr->session_settings;
429 GHOST_XrContextHandle xr_context = xr->runtime->context;
431
432 wm_xr_controller_model_draw(settings, xr_context, state);
434}
#define BLI_assert(a)
Definition BLI_assert.h:46
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void perspective_m4_fov(float mat[4][4], float angle_left, float angle_right, float angle_up, float angle_down, float nearClip, float farClip)
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
void quat_to_mat4(float m[4][4], const float q[4])
void invert_qt_qt_normalized(float q1[4], const float q2[4])
void copy_qt_qt(float q[4], const float a[4])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
unsigned int uint
eDrawType
@ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS
@ V3D_OFSDRAW_XR_SHOW_PASSTHROUGH
@ XR_SESSION_USE_ABSOLUTE_TRACKING
@ XR_SESSION_USE_POSITION_TRACKING
@ XR_CONTROLLER_DRAW_LIGHT_RAY
@ XR_CONTROLLER_DRAW_LIGHT
@ XR_CONTROLLER_DRAW_DARK_RAY
@ XR_CONTROLLER_DRAW_DARK
void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, Scene *scene, View3DShading *shading_override, eDrawType drawtype, int object_type_exclude_viewport_override, int object_type_exclude_select_override, int winx, int winy, unsigned int draw_flags, const float viewmat[4][4], const float winmat[4][4], float clip_start, float clip_end, bool is_xr_surface, bool is_image_render, bool draw_background, const char *viewname, bool do_color_management, GPUOffScreen *ofs, GPUViewport *viewport)
static Controller * controller
GHOST C-API function and type declarations.
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, eGPUBatchFlag owns_flag)
Definition gpu_batch.cc:51
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, eGPUBuiltinShader shader_id)
void GPU_batch_draw(blender::gpu::Batch *batch)
#define GPU_batch_uniform_4fv(batch, name, val)
Definition GPU_batch.hh:309
void GPU_batch_draw_range(blender::gpu::Batch *batch, int vertex_first, int vertex_count)
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:50
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:41
blender::gpu::Batch * GPU_batch_preset_sphere(int lod) ATTR_WARN_UNUSED_RESULT
void GPU_offscreen_bind(GPUOffScreen *offscreen, bool save)
void GPU_framebuffer_restore()
void GPU_clear_depth(float depth)
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immAttr4fv(uint attr_id, const float data[4])
void immAttrSkip(uint attr_id)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
blender::gpu::IndexBuf * GPU_indexbuf_build(GPUIndexBufBuilder *)
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_scale_1f(float factor)
void GPU_matrix_pop()
@ GPU_PRIM_LINES
@ GPU_PRIM_TRIS
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_POLYLINE_FLAT_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
#define GPU_vertbuf_create_with_format(format)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, int view, const rcti *rect, bool display_colorspace, bool do_overlay_merge)
#define U
MutableSpan< T > data()
uint pos
uint col
format
static ulong state[N]
int object_type_exclude_viewport
struct View3DShading shading
int ymin
int ymax
int xmin
int xmax
XrSessionSettings session_settings
struct wmXrRuntimeData * runtime
wmXrData * xr_data
float eye_position_ofs[3]
struct Depsgraph * depsgraph
GHOST_XrPose base_pose
wmXrSurfaceData * surface_data
struct Scene * scene
GHOST_XrContextHandle context
wmXrSessionState session_state
GHOST_XrPose nav_pose_prev
struct GPUViewport * viewport
struct GPUOffScreen * offscreen
i
Definition text_draw.cc:230
void WM_global_report(eReportType type, const char *message)
void wmViewport(const rcti *winrct)
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
Definition wm_xr_draw.cc:37
static void wm_xr_draw_viewport_buffers_to_active_framebuffer(const wmXrRuntimeData *runtime_data, const wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view)
void wm_xr_draw_controllers(const bContext *, ARegion *, void *customdata)
static void wm_xr_controller_model_draw(const XrSessionSettings *settings, GHOST_XrContextHandle xr_context, wmXrSessionState *state)
static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state)
void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4])
Definition wm_xr_draw.cc:43
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, const XrSessionSettings *session_settings, const wmXrSessionState *session_state, float r_viewmat[4][4], float r_projmat[4][4])
Definition wm_xr_draw.cc:76
bool wm_xr_passthrough_enabled(void *customdata)
Check if XR passthrough is enabled.
static blender::gpu::Batch * wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_context, const char *subaction_path)
void wm_xr_disable_passthrough(void *customdata)
Disable XR passthrough if not supported.
void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4])
Definition wm_xr_draw.cc:61
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
Draw a viewport for a single eye.
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
Definition wm_xr_draw.cc:53
void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data)
void wm_xr_session_state_update(const XrSessionSettings *settings, const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, wmXrSessionState *state)
bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view)
bool WM_xr_session_is_ready(const wmXrData *xr)