Files
test2/source/blender/draw/engines/overlay/overlay_cursor.hh
Clément Foucault 5bd572d4fd Cleanup: Overlay: Remove _next in overlay filename
These are not needed anymore since the legacy codebase
is gone.
2025-04-16 20:51:24 +02:00

200 lines
6.4 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup overlay
*/
#pragma once
#include "BKE_paint.hh"
#include "DNA_brush_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "ED_view3d.hh"
#include "UI_view2d.hh"
#include "overlay_base.hh"
namespace blender::draw::overlay {
/**
* Draw the 2D/3D cursor.
* Controlled by (Overlay > 3D Cursor)
*/
class Cursor : Overlay {
private:
PassSimple ps_ = {"Cursor"};
bool enabled_ = false;
public:
Cursor() {}
void begin_sync(Resources &res, const State &state) final
{
if (state.is_space_v3d()) {
enabled_ = is_cursor_visible_3d(state);
}
else {
enabled_ = is_cursor_visible_2d(state);
}
if (!enabled_) {
return;
}
/* 2D coordinate of the cursor in screen space pixel. */
int2 pixel_coord;
float3x3 rotation = float3x3::identity();
/* TODO(fclem): This is against design. Sync shouldn't depend on view. */
if (state.is_space_v3d()) {
const View3DCursor *cursor = &state.scene->cursor;
rotation = float3x3(float4x4(state.rv3d->viewmat).view<3, 3>()) *
math::from_rotation<float3x3>(cursor->rotation());
eV3DProjStatus status = ED_view3d_project_int_global(
state.region, cursor->location, pixel_coord, V3D_PROJ_TEST_CLIP_NEAR);
if (status != V3D_PROJ_RET_OK) {
/* Clipped. */
return;
}
}
else {
const SpaceImage *sima = (SpaceImage *)state.space_data;
UI_view2d_view_to_region(
&state.region->v2d, sima->cursor[0], sima->cursor[1], &pixel_coord[0], &pixel_coord[1]);
}
float4x4 cursor_mat = math::from_scale<float4x4>(float2(U.widget_unit));
cursor_mat.location()[0] = pixel_coord[0] + 0.5f;
cursor_mat.location()[1] = pixel_coord[1] + 0.5f;
/* Copy of wmOrtho2_region_pixelspace but without GPU_matrix_ortho_set. */
const float ofs = -0.01f;
float4x4 proj_mat = math::projection::orthographic(
ofs, state.region->winx + ofs, ofs, state.region->winy + ofs, -100.0f, 100.0f);
float4x4 mvp = proj_mat * cursor_mat;
PassSimple &pass = ps_;
pass.init();
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_POLYLINE_FLAT_COLOR));
pass.push_constant("ModelViewProjectionMatrix", mvp);
pass.push_constant("viewportSize", float2(state.region->winx, state.region->winy));
pass.push_constant("lineWidth", U.pixelsize);
pass.push_constant("lineSmooth", true);
/* WORKAROUND: This is normally set by the GPUBatch or IMM API but we don't use them here.
* So make sure it is set otherwise it can be in undefined state (see #136911). */
pass.push_constant("gpu_attr_0_fetch_int", false);
pass.push_constant("gpu_attr_1_fetch_unorm8", false);
/* See `polyline_draw_workaround`. */
int3 vert_stride_count_line = {2, 9999 /* Doesn't matter. */, 0};
int3 vert_stride_count_circle = {1, 9999 /* Doesn't matter. */, 0};
if (state.is_space_v3d()) {
/* Render line first to avoid Z fighting. */
pass.push_constant("ModelViewProjectionMatrix", mvp * float4x4(rotation));
pass.push_constant("gpu_vert_stride_count_offset", vert_stride_count_line);
pass.draw_expand(res.shapes.cursor_lines.get(), GPU_PRIM_TRIS, 2, 1, ResourceHandle(0));
pass.push_constant("ModelViewProjectionMatrix", mvp);
pass.push_constant("gpu_vert_stride_count_offset", vert_stride_count_circle);
pass.draw_expand(res.shapes.cursor_circle.get(), GPU_PRIM_TRIS, 2, 1, ResourceHandle(0));
}
else {
pass.push_constant("ModelViewProjectionMatrix", mvp);
pass.push_constant("gpu_vert_stride_count_offset", vert_stride_count_circle);
pass.draw_expand(res.shapes.cursor_circle.get(), GPU_PRIM_TRIS, 2, 1, ResourceHandle(0));
pass.push_constant("gpu_vert_stride_count_offset", vert_stride_count_line);
pass.draw_expand(res.shapes.cursor_lines.get(), GPU_PRIM_TRIS, 2, 1, ResourceHandle(0));
}
}
void draw_output(Framebuffer &framebuffer, Manager &manager, View & /*view*/) final
{
if (!enabled_) {
return;
}
GPU_framebuffer_bind(framebuffer);
manager.submit(ps_);
}
private:
bool is_cursor_visible_3d(const State &state)
{
if (G.moving & G_TRANSFORM_CURSOR) {
return true;
}
const View3D *v3d = state.v3d;
if ((v3d->flag2 & V3D_HIDE_OVERLAYS) || (v3d->overlay.flag & V3D_OVERLAY_HIDE_CURSOR)) {
return false;
}
/* don't draw cursor in paint modes, but with a few exceptions */
if ((state.object_mode & (OB_MODE_ALL_PAINT | OB_MODE_SCULPT_CURVES)) != 0) {
/* exception: object is in weight paint and has deforming armature in pose mode */
if (state.object_mode & OB_MODE_WEIGHT_PAINT) {
if (BKE_object_pose_armature_get(const_cast<Object *>(state.object_active)) != nullptr) {
return true;
}
}
/* exception: object in texture paint mode, clone brush, use_clone_layer disabled */
else if (state.object_mode & OB_MODE_TEXTURE_PAINT) {
const Paint *paint = BKE_paint_get_active(const_cast<Scene *>(state.scene),
const_cast<ViewLayer *>(state.view_layer));
const Brush *brush = (paint) ? BKE_paint_brush_for_read(paint) : nullptr;
if (brush && brush->image_brush_type == IMAGE_PAINT_BRUSH_TYPE_CLONE) {
if ((state.scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) == 0) {
return true;
}
}
}
/* no exception met? then don't draw cursor! */
return false;
}
if (state.object_mode & OB_MODE_WEIGHT_GREASE_PENCIL) {
/* grease pencil hide always in some modes */
return false;
}
return true;
}
static bool is_cursor_visible_2d(const State &state)
{
SpaceInfo *space_data = (SpaceInfo *)state.space_data;
if (space_data == nullptr) {
return false;
}
if (space_data->spacetype != SPACE_IMAGE) {
return false;
}
SpaceImage *sima = (SpaceImage *)space_data;
switch (sima->mode) {
case SI_MODE_VIEW:
return false;
break;
case SI_MODE_PAINT:
return false;
break;
case SI_MODE_MASK:
break;
case SI_MODE_UV:
break;
}
return (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) != 0;
}
};
} // namespace blender::draw::overlay