This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/windowmanager/xr/intern/wm_xr_draw.c

407 lines
14 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*
* \name Window-Manager XR Drawing
*
* Implements Blender specific drawing functionality for use with the Ghost-XR API.
*/
#include <string.h>
#include "BKE_context.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "ED_view3d_offscreen.h"
#include "GHOST_C-api.h"
#include "GPU_batch_presets.h"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "GPU_viewport.h"
#include "WM_api.h"
#include "wm_surface.h"
#include "wm_xr_intern.h"
void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
{
quat_to_mat4(r_mat, pose->orientation_quat);
copy_v3_v3(r_mat[3], pose->position);
}
void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4])
{
wm_xr_pose_to_mat(pose, r_mat);
BLI_assert(scale > 0.0f);
mul_v3_fl(r_mat[0], scale);
mul_v3_fl(r_mat[1], scale);
mul_v3_fl(r_mat[2], scale);
}
void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
{
float iquat[4];
invert_qt_qt_normalized(iquat, pose->orientation_quat);
quat_to_mat4(r_imat, iquat);
translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4])
{
float iquat[4];
invert_qt_qt_normalized(iquat, pose->orientation_quat);
quat_to_mat4(r_imat, iquat);
BLI_assert(scale > 0.0f);
scale = 1.0f / scale;
mul_v3_fl(r_imat[0], scale);
mul_v3_fl(r_imat[1], scale);
mul_v3_fl(r_imat[2], scale);
translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
const wmXrSessionState *session_state,
float r_viewmat[4][4],
float r_projmat[4][4])
{
GHOST_XrPose eye_pose;
float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4];
/* Calculate inverse eye matrix. */
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
sub_v3_v3(eye_pose.position, draw_view->local_pose.position);
}
if ((session_settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) {
sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
}
wm_xr_pose_to_imat(&eye_pose, eye_inv);
/* 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);
mul_m4_m4m4(r_viewmat, m, nav_inv);
perspective_m4_fov(r_projmat,
draw_view->fov.angle_left,
draw_view->fov.angle_right,
draw_view->fov.angle_up,
draw_view->fov.angle_down,
session_settings->clip_start,
session_settings->clip_end);
}
static void wm_xr_draw_viewport_buffers_to_active_framebuffer(
const wmXrRuntimeData *runtime_data,
const wmXrSurfaceData *surface_data,
const GHOST_XrDrawViewInfo *draw_view)
{
const wmXrViewportPair *vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx);
BLI_assert(vp && vp->viewport);
const bool is_upside_down = GHOST_XrSessionNeedsUpsideDownDrawing(runtime_data->context);
rcti rect = {.xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1};
wmViewport(&rect);
/* For upside down contexts, draw with inverted y-values. */
if (is_upside_down) {
SWAP(int, rect.ymin, rect.ymax);
}
GPU_viewport_draw_to_screen_ex(vp->viewport, 0, &rect, draw_view->expects_srgb_buffer, true);
}
/**
* \brief Draw a viewport for a single eye.
*
* This is the main viewport drawing function for VR sessions. It's assigned to Ghost-XR as a
* callback (see GHOST_XrDrawViewFunc()) and executed for each view (read: eye).
*/
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata)
{
wmXrDrawData *draw_data = customdata;
wmXrData *xr_data = draw_data->xr_data;
wmXrSurfaceData *surface_data = draw_data->surface_data;
wmXrSessionState *session_state = &xr_data->runtime->session_state;
XrSessionSettings *settings = &xr_data->session_settings;
const int display_flags = V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS | settings->draw_flags;
float viewmat[4][4], winmat[4][4];
BLI_assert(WM_xr_session_is_ready(xr_data));
wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data);
wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat);
wm_xr_session_state_update(settings, draw_data, draw_view, session_state);
if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) {
return;
}
const wmXrViewportPair *vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx);
BLI_assert(vp && vp->offscreen && vp->viewport);
/* In case a framebuffer is still bound from drawing the last eye. */
GPU_framebuffer_restore();
/* Some systems have drawing glitches without this. */
GPU_clear_depth(1.0f);
/* Draws the view into the surface_data->viewport's frame-buffers. */
ED_view3d_draw_offscreen_simple(draw_data->depsgraph,
draw_data->scene,
&settings->shading,
(eDrawType)settings->shading.type,
draw_view->width,
draw_view->height,
display_flags,
viewmat,
winmat,
settings->clip_start,
settings->clip_end,
true,
false,
true,
NULL,
false,
vp->offscreen,
vp->viewport);
/* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A
* call to GPU_viewport_draw_to_screen() is still needed to get the final result from the
* viewport buffers composited together and potentially color managed for display on screen.
* It needs a bound frame-buffer to draw into, for which we simply reuse the GPUOffscreen one.
*
* In a next step, Ghost-XR will use the currently bound frame-buffer to retrieve the image
* to be submitted to the OpenXR swap-chain. So do not un-bind the off-screen yet! */
GPU_offscreen_bind(vp->offscreen, false);
wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view);
}
static GPUBatch *wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_context,
const char *subaction_path)
{
GHOST_XrControllerModelData model_data;
if (!GHOST_XrGetControllerModelData(xr_context, subaction_path, &model_data) ||
model_data.count_vertices < 1) {
return NULL;
}
GPUVertFormat format = {0};
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "nor", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, model_data.count_vertices);
void *vbo_data = GPU_vertbuf_get_data(vbo);
memcpy(
vbo_data, model_data.vertices, model_data.count_vertices * sizeof(model_data.vertices[0]));
GPUIndexBuf *ibo = NULL;
if (model_data.count_indices > 0 && ((model_data.count_indices % 3) == 0)) {
GPUIndexBufBuilder ibo_builder;
const unsigned int prim_len = model_data.count_indices / 3;
GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, prim_len, model_data.count_vertices);
for (unsigned int i = 0; i < prim_len; ++i) {
const uint32_t *idx = &model_data.indices[i * 3];
GPU_indexbuf_add_tri_verts(&ibo_builder, idx[0], idx[1], idx[2]);
}
ibo = GPU_indexbuf_build(&ibo_builder);
}
return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
}
static void wm_xr_controller_model_draw(const XrSessionSettings *settings,
GHOST_XrContextHandle xr_context,
wmXrSessionState *state)
{
GHOST_XrControllerModelData model_data;
float color[4];
switch (settings->controller_draw_style) {
case XR_CONTROLLER_DRAW_DARK:
case XR_CONTROLLER_DRAW_DARK_RAY:
color[0] = color[1] = color[2] = 0.0f, color[3] = 0.4f;
break;
case XR_CONTROLLER_DRAW_LIGHT:
case XR_CONTROLLER_DRAW_LIGHT_RAY:
color[0] = 0.422f, color[1] = 0.438f, color[2] = 0.446f, color[3] = 0.4f;
break;
}
GPU_depth_test(GPU_DEPTH_NONE);
GPU_blend(GPU_BLEND_ALPHA);
LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
GPUBatch *model = controller->model;
if (!model) {
model = controller->model = wm_xr_controller_model_batch_create(xr_context,
controller->subaction_path);
}
if (model &&
GHOST_XrGetControllerModelData(xr_context, controller->subaction_path, &model_data) &&
model_data.count_components > 0) {
GPU_batch_program_set_builtin(model, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4fv(model, "color", color);
GPU_matrix_push();
GPU_matrix_mul(controller->grip_mat);
for (unsigned int component_idx = 0; component_idx < model_data.count_components;
++component_idx) {
const GHOST_XrControllerModelComponent *component = &model_data.components[component_idx];
GPU_matrix_push();
GPU_matrix_mul(component->transform);
GPU_batch_draw_range(model,
model->elem ? component->index_offset : component->vertex_offset,
model->elem ? component->index_count : component->vertex_count);
GPU_matrix_pop();
}
GPU_matrix_pop();
}
else {
/* Fallback. */
const float scale = 0.05f;
GPUBatch *sphere = GPU_batch_preset_sphere(2);
GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4fv(sphere, "color", color);
GPU_matrix_push();
GPU_matrix_mul(controller->grip_mat);
GPU_matrix_scale_1f(scale);
GPU_batch_draw(sphere);
GPU_matrix_pop();
}
}
}
static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state)
{
bool draw_ray;
switch (settings->controller_draw_style) {
case XR_CONTROLLER_DRAW_DARK:
case XR_CONTROLLER_DRAW_LIGHT:
draw_ray = false;
break;
case XR_CONTROLLER_DRAW_DARK_RAY:
case XR_CONTROLLER_DRAW_LIGHT_RAY:
draw_ray = true;
break;
}
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
float viewport[4];
GPU_viewport_size_get_f(viewport);
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", 3.0f * U.pixelsize);
if (draw_ray) {
const uchar color[4] = {89, 89, 255, 127};
const float scale = settings->clip_end;
float ray[3];
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
GPU_blend(GPU_BLEND_ALPHA);
immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 2);
LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
const float(*mat)[4] = controller->aim_mat;
madd_v3_v3v3fl(ray, mat[3], mat[2], -scale);
immAttrSkip(col);
immVertex3fv(pos, mat[3]);
immAttr4ubv(col, color);
immVertex3fv(pos, ray);
}
immEnd();
}
else {
const uchar r[4] = {255, 51, 82, 255};
const uchar g[4] = {139, 220, 0, 255};
const uchar b[4] = {40, 144, 255, 255};
const float scale = 0.01f;
float x_axis[3], y_axis[3], z_axis[3];
GPU_depth_test(GPU_DEPTH_NONE);
GPU_blend(GPU_BLEND_NONE);
immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 6);
LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) {
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);
immAttrSkip(col);
immVertex3fv(pos, mat[3]);
immAttr4ubv(col, r);
immVertex3fv(pos, x_axis);
immAttrSkip(col);
immVertex3fv(pos, mat[3]);
immAttr4ubv(col, g);
immVertex3fv(pos, y_axis);
immAttrSkip(col);
immVertex3fv(pos, mat[3]);
immAttr4ubv(col, b);
immVertex3fv(pos, z_axis);
}
immEnd();
}
immUnbindProgram();
}
void wm_xr_draw_controllers(const bContext *UNUSED(C), ARegion *UNUSED(region), void *customdata)
{
wmXrData *xr = customdata;
const XrSessionSettings *settings = &xr->session_settings;
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);
}