2470 lines
78 KiB
C++
2470 lines
78 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup edtransform
|
|
*
|
|
* \name 3D Transform Gizmo
|
|
*
|
|
* Used for 3D View
|
|
*/
|
|
|
|
#include "BLI_function_ref.hh"
|
|
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_gpencil_legacy_types.h"
|
|
#include "DNA_lattice_types.h"
|
|
#include "DNA_meta_types.h"
|
|
|
|
#include "BKE_armature.h"
|
|
#include "BKE_context.h"
|
|
#include "BKE_crazyspace.hh"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_editmesh.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_gpencil_legacy.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_paint.h"
|
|
#include "BKE_pointcache.h"
|
|
#include "BKE_scene.h"
|
|
#include "BLI_array_utils.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_message.h"
|
|
|
|
#include "ED_armature.h"
|
|
#include "ED_curves.h"
|
|
#include "ED_gizmo_library.h"
|
|
#include "ED_gizmo_utils.h"
|
|
#include "ED_gpencil_legacy.h"
|
|
#include "ED_object.h"
|
|
#include "ED_particle.h"
|
|
#include "ED_screen.h"
|
|
|
|
#include "UI_resources.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
|
|
/* local module include */
|
|
#include "transform.h"
|
|
#include "transform_convert.h"
|
|
#include "transform_gizmo.h"
|
|
#include "transform_snap.h"
|
|
|
|
static wmGizmoGroupType *g_GGT_xform_gizmo = nullptr;
|
|
static wmGizmoGroupType *g_GGT_xform_gizmo_context = nullptr;
|
|
|
|
static void gizmogroup_refresh_from_matrix(wmGizmoGroup *gzgroup,
|
|
const float twmat[4][4],
|
|
const float scale[3],
|
|
const bool ignore_hidden);
|
|
|
|
/* return codes for select, and drawing flags */
|
|
|
|
#define MAN_TRANS_X (1 << 0)
|
|
#define MAN_TRANS_Y (1 << 1)
|
|
#define MAN_TRANS_Z (1 << 2)
|
|
#define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
|
|
|
|
#define MAN_ROT_X (1 << 3)
|
|
#define MAN_ROT_Y (1 << 4)
|
|
#define MAN_ROT_Z (1 << 5)
|
|
#define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
|
|
|
|
#define MAN_SCALE_X (1 << 8)
|
|
#define MAN_SCALE_Y (1 << 9)
|
|
#define MAN_SCALE_Z (1 << 10)
|
|
#define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
|
|
|
|
/* threshold for testing view aligned gizmo axis */
|
|
static struct {
|
|
float min, max;
|
|
} g_tw_axis_range[2] = {
|
|
/* Regular range */
|
|
{0.02f, 0.1f},
|
|
/* Use a different range because we flip the dot product,
|
|
* also the view aligned planes are harder to see so hiding early is preferred. */
|
|
{0.175f, 0.25f},
|
|
};
|
|
|
|
/* axes as index */
|
|
enum {
|
|
MAN_AXIS_TRANS_X = 0,
|
|
MAN_AXIS_TRANS_Y,
|
|
MAN_AXIS_TRANS_Z,
|
|
MAN_AXIS_TRANS_C,
|
|
|
|
MAN_AXIS_TRANS_XY,
|
|
MAN_AXIS_TRANS_YZ,
|
|
MAN_AXIS_TRANS_ZX,
|
|
#define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X
|
|
#define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1)
|
|
|
|
MAN_AXIS_ROT_X,
|
|
MAN_AXIS_ROT_Y,
|
|
MAN_AXIS_ROT_Z,
|
|
MAN_AXIS_ROT_C,
|
|
MAN_AXIS_ROT_T, /* trackball rotation */
|
|
#define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X
|
|
#define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1)
|
|
|
|
MAN_AXIS_SCALE_X,
|
|
MAN_AXIS_SCALE_Y,
|
|
MAN_AXIS_SCALE_Z,
|
|
MAN_AXIS_SCALE_C,
|
|
MAN_AXIS_SCALE_XY,
|
|
MAN_AXIS_SCALE_YZ,
|
|
MAN_AXIS_SCALE_ZX,
|
|
#define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X
|
|
#define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1)
|
|
|
|
MAN_AXIS_LAST = MAN_AXIS_SCALE_ZX + 1,
|
|
};
|
|
|
|
/* axis types */
|
|
enum {
|
|
MAN_AXES_ALL = 0,
|
|
MAN_AXES_TRANSLATE,
|
|
MAN_AXES_ROTATE,
|
|
MAN_AXES_SCALE,
|
|
};
|
|
|
|
struct GizmoGroup {
|
|
bool all_hidden;
|
|
int twtype;
|
|
|
|
/* Users may change the twtype, detect changes to re-setup gizmo options. */
|
|
int twtype_init;
|
|
int twtype_prev;
|
|
int use_twtype_refresh;
|
|
|
|
/* Only for view orientation. */
|
|
struct {
|
|
float viewinv_m3[3][3];
|
|
} prev;
|
|
|
|
/* Only for Rotate operator. */
|
|
float rotation;
|
|
|
|
wmGizmo *gizmos[MAN_AXIS_LAST];
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Utilities
|
|
* \{ */
|
|
|
|
/* loop over axes */
|
|
#define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
|
|
{ \
|
|
wmGizmo *axis; \
|
|
int axis_idx; \
|
|
for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
|
|
axis = gizmo_get_axis_from_index(ggd, axis_idx);
|
|
|
|
#define MAN_ITER_AXES_END \
|
|
} \
|
|
} \
|
|
((void)0)
|
|
|
|
static wmGizmo *gizmo_get_axis_from_index(const GizmoGroup *ggd, const short axis_idx)
|
|
{
|
|
BLI_assert(IN_RANGE_INCL(axis_idx, float(MAN_AXIS_TRANS_X), float(MAN_AXIS_LAST)));
|
|
return ggd->gizmos[axis_idx];
|
|
}
|
|
|
|
static short gizmo_get_axis_type(const int axis_idx)
|
|
{
|
|
if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
|
|
return MAN_AXES_TRANSLATE;
|
|
}
|
|
if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
|
|
return MAN_AXES_ROTATE;
|
|
}
|
|
if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) {
|
|
return MAN_AXES_SCALE;
|
|
}
|
|
BLI_assert(0);
|
|
return -1;
|
|
}
|
|
|
|
static uint gizmo_orientation_axis(const int axis_idx, bool *r_is_plane)
|
|
{
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
if (r_is_plane) {
|
|
*r_is_plane = true;
|
|
}
|
|
ATTR_FALLTHROUGH;
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_ROT_X:
|
|
case MAN_AXIS_SCALE_X:
|
|
return 0;
|
|
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
if (r_is_plane) {
|
|
*r_is_plane = true;
|
|
}
|
|
ATTR_FALLTHROUGH;
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_ROT_Y:
|
|
case MAN_AXIS_SCALE_Y:
|
|
return 1;
|
|
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_SCALE_XY:
|
|
if (r_is_plane) {
|
|
*r_is_plane = true;
|
|
}
|
|
ATTR_FALLTHROUGH;
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_ROT_Z:
|
|
case MAN_AXIS_SCALE_Z:
|
|
return 2;
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
static bool gizmo_is_axis_visible(const RegionView3D *rv3d,
|
|
const int twtype,
|
|
const float idot[3],
|
|
const int axis_type,
|
|
const int axis_idx)
|
|
{
|
|
if ((axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) == 0) {
|
|
bool is_plane = false;
|
|
const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
|
|
/* don't draw axis perpendicular to the view */
|
|
if (aidx_norm < 3) {
|
|
float idot_axis = idot[aidx_norm];
|
|
if (is_plane) {
|
|
idot_axis = 1.0f - idot_axis;
|
|
}
|
|
if (idot_axis < g_tw_axis_range[is_plane].min) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE)) ||
|
|
(axis_type == MAN_AXES_ROTATE && !(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE)) ||
|
|
(axis_type == MAN_AXES_SCALE && !(twtype & V3D_GIZMO_SHOW_OBJECT_SCALE)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
return (rv3d->twdrawflag & MAN_TRANS_X);
|
|
case MAN_AXIS_TRANS_Y:
|
|
return (rv3d->twdrawflag & MAN_TRANS_Y);
|
|
case MAN_AXIS_TRANS_Z:
|
|
return (rv3d->twdrawflag & MAN_TRANS_Z);
|
|
case MAN_AXIS_TRANS_C:
|
|
return (rv3d->twdrawflag & MAN_TRANS_C);
|
|
case MAN_AXIS_ROT_X:
|
|
return (rv3d->twdrawflag & MAN_ROT_X);
|
|
case MAN_AXIS_ROT_Y:
|
|
return (rv3d->twdrawflag & MAN_ROT_Y);
|
|
case MAN_AXIS_ROT_Z:
|
|
return (rv3d->twdrawflag & MAN_ROT_Z);
|
|
case MAN_AXIS_ROT_C:
|
|
case MAN_AXIS_ROT_T:
|
|
return (rv3d->twdrawflag & MAN_ROT_C);
|
|
case MAN_AXIS_SCALE_X:
|
|
return (rv3d->twdrawflag & MAN_SCALE_X);
|
|
case MAN_AXIS_SCALE_Y:
|
|
return (rv3d->twdrawflag & MAN_SCALE_Y);
|
|
case MAN_AXIS_SCALE_Z:
|
|
return (rv3d->twdrawflag & MAN_SCALE_Z);
|
|
case MAN_AXIS_SCALE_C:
|
|
return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0);
|
|
case MAN_AXIS_TRANS_XY:
|
|
return (rv3d->twdrawflag & MAN_TRANS_X && rv3d->twdrawflag & MAN_TRANS_Y &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
|
|
case MAN_AXIS_TRANS_YZ:
|
|
return (rv3d->twdrawflag & MAN_TRANS_Y && rv3d->twdrawflag & MAN_TRANS_Z &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
|
|
case MAN_AXIS_TRANS_ZX:
|
|
return (rv3d->twdrawflag & MAN_TRANS_Z && rv3d->twdrawflag & MAN_TRANS_X &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
|
|
case MAN_AXIS_SCALE_XY:
|
|
return (rv3d->twdrawflag & MAN_SCALE_X && rv3d->twdrawflag & MAN_SCALE_Y &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
|
|
case MAN_AXIS_SCALE_YZ:
|
|
return (rv3d->twdrawflag & MAN_SCALE_Y && rv3d->twdrawflag & MAN_SCALE_Z &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
|
|
case MAN_AXIS_SCALE_ZX:
|
|
return (rv3d->twdrawflag & MAN_SCALE_Z && rv3d->twdrawflag & MAN_SCALE_X &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_TRANSLATE) == 0 &&
|
|
(twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) == 0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void gizmo_get_axis_color(const int axis_idx,
|
|
const float idot[3],
|
|
float r_col[4],
|
|
float r_col_hi[4])
|
|
{
|
|
/* alpha values for normal/highlighted states */
|
|
const float alpha = 0.6f;
|
|
const float alpha_hi = 1.0f;
|
|
float alpha_fac;
|
|
|
|
if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
|
|
/* Never fade rotation rings. */
|
|
/* trackball rotation axis is a special case, we only draw a slight overlay */
|
|
alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.05f : 1.0f;
|
|
}
|
|
else {
|
|
bool is_plane = false;
|
|
const int axis_idx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
|
|
/* Get alpha fac based on axis angle,
|
|
* to fade axis out when hiding it because it points towards view. */
|
|
if (axis_idx_norm < 3) {
|
|
const float idot_min = g_tw_axis_range[is_plane].min;
|
|
const float idot_max = g_tw_axis_range[is_plane].max;
|
|
float idot_axis = idot[axis_idx_norm];
|
|
if (is_plane) {
|
|
idot_axis = 1.0f - idot_axis;
|
|
}
|
|
alpha_fac = ((idot_axis > idot_max) ? 1.0f :
|
|
(idot_axis < idot_min) ? 0.0f :
|
|
((idot_axis - idot_min) / (idot_max - idot_min)));
|
|
}
|
|
else {
|
|
alpha_fac = 1.0f;
|
|
}
|
|
}
|
|
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_ROT_X:
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
UI_GetThemeColor4fv(TH_AXIS_X, r_col);
|
|
break;
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_ROT_Y:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
|
|
break;
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_ROT_Z:
|
|
case MAN_AXIS_SCALE_Z:
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_SCALE_XY:
|
|
UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
|
|
break;
|
|
case MAN_AXIS_TRANS_C:
|
|
case MAN_AXIS_ROT_C:
|
|
case MAN_AXIS_SCALE_C:
|
|
case MAN_AXIS_ROT_T:
|
|
UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, r_col);
|
|
break;
|
|
}
|
|
|
|
copy_v4_v4(r_col_hi, r_col);
|
|
|
|
r_col[3] = alpha * alpha_fac;
|
|
r_col_hi[3] = alpha_hi * alpha_fac;
|
|
}
|
|
|
|
static void gizmo_get_axis_constraint(const int axis_idx, bool r_axis[3])
|
|
{
|
|
ARRAY_SET_ITEMS(r_axis, 0, 0, 0);
|
|
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_ROT_X:
|
|
case MAN_AXIS_SCALE_X:
|
|
r_axis[0] = 1;
|
|
break;
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_ROT_Y:
|
|
case MAN_AXIS_SCALE_Y:
|
|
r_axis[1] = 1;
|
|
break;
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_ROT_Z:
|
|
case MAN_AXIS_SCALE_Z:
|
|
r_axis[2] = 1;
|
|
break;
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_SCALE_XY:
|
|
r_axis[0] = r_axis[1] = 1;
|
|
break;
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
r_axis[1] = r_axis[2] = 1;
|
|
break;
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
r_axis[2] = r_axis[0] = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* **************** Preparation Stuff **************** */
|
|
|
|
static void reset_tw_center(TransformBounds *tbounds)
|
|
{
|
|
INIT_MINMAX(tbounds->min, tbounds->max);
|
|
zero_v3(tbounds->center);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
tbounds->axis_min[i] = +FLT_MAX;
|
|
tbounds->axis_max[i] = -FLT_MAX;
|
|
}
|
|
}
|
|
|
|
/* transform widget center calc helper for below */
|
|
static void calc_tw_center(TransformBounds *tbounds, const float co[3])
|
|
{
|
|
minmax_v3v3_v3(tbounds->min, tbounds->max, co);
|
|
add_v3_v3(tbounds->center, co);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
const float d = dot_v3v3(tbounds->axis[i], co);
|
|
tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]);
|
|
tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]);
|
|
}
|
|
}
|
|
|
|
static void protectflag_to_drawflags(short protectflag, short *drawflags)
|
|
{
|
|
if (protectflag & OB_LOCK_LOCX) {
|
|
*drawflags &= ~MAN_TRANS_X;
|
|
}
|
|
if (protectflag & OB_LOCK_LOCY) {
|
|
*drawflags &= ~MAN_TRANS_Y;
|
|
}
|
|
if (protectflag & OB_LOCK_LOCZ) {
|
|
*drawflags &= ~MAN_TRANS_Z;
|
|
}
|
|
|
|
if (protectflag & OB_LOCK_ROTX) {
|
|
*drawflags &= ~MAN_ROT_X;
|
|
}
|
|
if (protectflag & OB_LOCK_ROTY) {
|
|
*drawflags &= ~MAN_ROT_Y;
|
|
}
|
|
if (protectflag & OB_LOCK_ROTZ) {
|
|
*drawflags &= ~MAN_ROT_Z;
|
|
}
|
|
|
|
if (protectflag & OB_LOCK_SCALEX) {
|
|
*drawflags &= ~MAN_SCALE_X;
|
|
}
|
|
if (protectflag & OB_LOCK_SCALEY) {
|
|
*drawflags &= ~MAN_SCALE_Y;
|
|
}
|
|
if (protectflag & OB_LOCK_SCALEZ) {
|
|
*drawflags &= ~MAN_SCALE_Z;
|
|
}
|
|
}
|
|
|
|
/* Similar to #transform_object_deform_pose_armature_get but does not check visibility. */
|
|
static Object *gizmo_3d_transform_space_object_get(Scene *scene, ViewLayer *view_layer)
|
|
{
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
|
Object *ob = BKE_view_layer_active_object_get(view_layer);
|
|
if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) {
|
|
/* It is assumed that when the object is in Weight Paint mode, it is not in Edit mode. So we
|
|
* don't need to check the #OB_MODE_EDIT flag. */
|
|
BLI_assert(!(ob->mode & OB_MODE_EDIT));
|
|
Object *obpose = BKE_object_pose_armature_get(ob);
|
|
if (obpose != nullptr) {
|
|
ob = obpose;
|
|
}
|
|
}
|
|
return ob;
|
|
}
|
|
|
|
/**
|
|
* Run \a user_fn for each coordinate of elements selected in View3D (vertices, particles...).
|
|
* \note Each coordinate has the space matrix of the active object.
|
|
*
|
|
* \param orient_index: A #TransformOrientationSlot.type. Here used for calculating \a r_drawflags.
|
|
* \param use_curve_handles: If true, the handles of curves are traversed.
|
|
* \param use_only_center: For objects in object mode, defines whether the corners of the bounds or
|
|
* just the center are traversed.
|
|
* \param user_fn: Callback that runs on each coordinate.
|
|
* \param r_mat: Returns the space matrix of the coordinates.
|
|
* \param r_drawflags: Drawing flags for gizmos. Usually stored in #RegionView3D::drawflags.
|
|
*/
|
|
static int gizmo_3d_foreach_selected(const bContext *C,
|
|
const short orient_index,
|
|
const bool use_curve_handles,
|
|
const bool use_only_center,
|
|
blender::FunctionRef<void(const blender::float3 &)> user_fn,
|
|
const float (**r_mat)[4],
|
|
short *r_drawflags)
|
|
{
|
|
using namespace blender;
|
|
|
|
const auto run_coord_with_matrix =
|
|
[&](const float co[3], const bool use_matrix, const float matrix[4][4]) {
|
|
float co_world[3];
|
|
if (use_matrix) {
|
|
mul_v3_m4v3(co_world, matrix, co);
|
|
co = co_world;
|
|
}
|
|
user_fn(co);
|
|
};
|
|
|
|
ScrArea *area = CTX_wm_area(C);
|
|
Scene *scene = CTX_data_scene(C);
|
|
/* TODO(sergey): This function is used from operator's modal() and from gizmo's refresh().
|
|
* Is it fine to possibly evaluate dependency graph here? */
|
|
Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
|
|
Base *base;
|
|
bGPdata *gpd = CTX_data_gpencil_data(C);
|
|
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
|
|
const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
|
|
int a, totsel = 0;
|
|
|
|
Object *ob = gizmo_3d_transform_space_object_get(scene, view_layer);
|
|
|
|
if (is_gp_edit) {
|
|
float diff_mat[4][4];
|
|
const bool use_mat_local = true;
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
/* only editable and visible layers are considered */
|
|
|
|
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != nullptr)) {
|
|
|
|
/* calculate difference matrix */
|
|
BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat);
|
|
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpl->actframe->strokes) {
|
|
/* skip strokes that are invalid for current view */
|
|
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
|
continue;
|
|
}
|
|
|
|
if (is_curve_edit) {
|
|
if (gps->editcurve == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
bGPDcurve *gpc = gps->editcurve;
|
|
if (gpc->flag & GP_CURVE_SELECT) {
|
|
for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
|
|
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
|
|
BezTriple *bezt = &gpc_pt->bezt;
|
|
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
|
|
for (uint32_t j = 0; j < 3; j++) {
|
|
if (BEZT_ISSEL_IDX(bezt, j)) {
|
|
run_coord_with_matrix(bezt->vec[j], use_mat_local, diff_mat);
|
|
totsel++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* we're only interested in selected points here... */
|
|
if (gps->flag & GP_STROKE_SELECT) {
|
|
bGPDspoint *pt;
|
|
int i;
|
|
|
|
/* Change selection status of all points, then make the stroke match */
|
|
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
|
if (pt->flag & GP_SPOINT_SELECT) {
|
|
run_coord_with_matrix(&pt->x, use_mat_local, diff_mat);
|
|
totsel++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (Object *obedit = OBEDIT_FROM_OBACT(ob)) {
|
|
|
|
#define FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) \
|
|
{ \
|
|
invert_m4_m4(obedit->world_to_object, obedit->object_to_world); \
|
|
uint objects_len = 0; \
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode( \
|
|
scene, view_layer, CTX_wm_view3d(C), &objects_len); \
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) { \
|
|
Object *ob_iter = objects[ob_index]; \
|
|
const bool use_mat_local = (ob_iter != obedit);
|
|
|
|
#define FOREACH_EDIT_OBJECT_END() \
|
|
} \
|
|
MEM_freeN(objects); \
|
|
} \
|
|
((void)0)
|
|
|
|
ob = obedit;
|
|
if (obedit->type == OB_MESH) {
|
|
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
|
BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
|
|
BMesh *bm = em_iter->bm;
|
|
|
|
if (bm->totvertsel == 0) {
|
|
continue;
|
|
}
|
|
|
|
BMVert *eve;
|
|
BMIter iter;
|
|
|
|
float mat_local[4][4];
|
|
if (use_mat_local) {
|
|
mul_m4_m4m4(mat_local, obedit->world_to_object, ob_iter->object_to_world);
|
|
}
|
|
|
|
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
|
|
if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
|
|
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
|
|
run_coord_with_matrix(eve->co, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FOREACH_EDIT_OBJECT_END();
|
|
} /* end editmesh */
|
|
else if (obedit->type == OB_ARMATURE) {
|
|
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
|
bArmature *arm = static_cast<bArmature *>(ob_iter->data);
|
|
|
|
float mat_local[4][4];
|
|
if (use_mat_local) {
|
|
mul_m4_m4m4(mat_local, obedit->world_to_object, ob_iter->object_to_world);
|
|
}
|
|
LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
|
|
if (EBONE_VISIBLE(arm, ebo)) {
|
|
if (ebo->flag & BONE_TIPSEL) {
|
|
run_coord_with_matrix(ebo->tail, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
if ((ebo->flag & BONE_ROOTSEL) &&
|
|
/* don't include same point multiple times */
|
|
((ebo->flag & BONE_CONNECTED) && (ebo->parent != nullptr) &&
|
|
(ebo->parent->flag & BONE_TIPSEL) && EBONE_VISIBLE(arm, ebo->parent)) == 0)
|
|
{
|
|
run_coord_with_matrix(ebo->head, use_mat_local, mat_local);
|
|
totsel++;
|
|
|
|
if (r_drawflags) {
|
|
if (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) {
|
|
if (ebo->flag & BONE_EDITMODE_LOCKED) {
|
|
protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE,
|
|
r_drawflags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
FOREACH_EDIT_OBJECT_END();
|
|
}
|
|
else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
|
|
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
|
Curve *cu = static_cast<Curve *>(ob_iter->data);
|
|
BezTriple *bezt;
|
|
BPoint *bp;
|
|
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
|
|
|
|
float mat_local[4][4];
|
|
if (use_mat_local) {
|
|
mul_m4_m4m4(mat_local, obedit->world_to_object, ob_iter->object_to_world);
|
|
}
|
|
|
|
Nurb *nu = static_cast<Nurb *>(nurbs->first);
|
|
while (nu) {
|
|
if (nu->type == CU_BEZIER) {
|
|
bezt = nu->bezt;
|
|
a = nu->pntsu;
|
|
while (a--) {
|
|
/* exceptions
|
|
* if handles are hidden then only check the center points.
|
|
* If the center knot is selected then only use this as the center point.
|
|
*/
|
|
if (v3d->overlay.handle_display == CURVE_HANDLE_NONE) {
|
|
if (bezt->f2 & SELECT) {
|
|
run_coord_with_matrix(bezt->vec[1], use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
}
|
|
else if (bezt->f2 & SELECT) {
|
|
run_coord_with_matrix(bezt->vec[1], use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
else {
|
|
if (bezt->f1 & SELECT) {
|
|
const float *co = bezt->vec[!use_curve_handles ? 1 : 0];
|
|
run_coord_with_matrix(co, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
if (bezt->f3 & SELECT) {
|
|
const float *co = bezt->vec[!use_curve_handles ? 1 : 2];
|
|
run_coord_with_matrix(co, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
}
|
|
bezt++;
|
|
}
|
|
}
|
|
else {
|
|
bp = nu->bp;
|
|
a = nu->pntsu * nu->pntsv;
|
|
while (a--) {
|
|
if (bp->f1 & SELECT) {
|
|
run_coord_with_matrix(bp->vec, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
bp++;
|
|
}
|
|
}
|
|
nu = nu->next;
|
|
}
|
|
}
|
|
FOREACH_EDIT_OBJECT_END();
|
|
}
|
|
else if (obedit->type == OB_MBALL) {
|
|
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
|
MetaBall *mb = (MetaBall *)ob_iter->data;
|
|
|
|
float mat_local[4][4];
|
|
if (use_mat_local) {
|
|
mul_m4_m4m4(mat_local, obedit->world_to_object, ob_iter->object_to_world);
|
|
}
|
|
|
|
LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) {
|
|
if (ml->flag & SELECT) {
|
|
run_coord_with_matrix(&ml->x, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
}
|
|
}
|
|
FOREACH_EDIT_OBJECT_END();
|
|
}
|
|
else if (obedit->type == OB_LATTICE) {
|
|
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
|
Lattice *lt = ((Lattice *)ob_iter->data)->editlatt->latt;
|
|
BPoint *bp = lt->def;
|
|
a = lt->pntsu * lt->pntsv * lt->pntsw;
|
|
|
|
float mat_local[4][4];
|
|
if (use_mat_local) {
|
|
mul_m4_m4m4(mat_local, obedit->world_to_object, ob_iter->object_to_world);
|
|
}
|
|
|
|
while (a--) {
|
|
if (bp->f1 & SELECT) {
|
|
run_coord_with_matrix(bp->vec, use_mat_local, mat_local);
|
|
totsel++;
|
|
}
|
|
bp++;
|
|
}
|
|
}
|
|
FOREACH_EDIT_OBJECT_END();
|
|
}
|
|
else if (obedit->type == OB_CURVES) {
|
|
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
|
const Curves &curves_id = *static_cast<Curves *>(ob_iter->data);
|
|
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
|
const bke::crazyspace::GeometryDeformation deformation =
|
|
bke::crazyspace::get_evaluated_curves_deformation(*depsgraph, *ob);
|
|
|
|
float4x4 mat_local;
|
|
if (use_mat_local) {
|
|
mat_local = float4x4(obedit->world_to_object) * float4x4(ob_iter->object_to_world);
|
|
}
|
|
|
|
IndexMaskMemory memory;
|
|
const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
|
|
const Span<float3> positions = deformation.positions;
|
|
totsel += selected_points.size();
|
|
selected_points.foreach_index([&](const int point_i) {
|
|
run_coord_with_matrix(positions[point_i], use_mat_local, mat_local.ptr());
|
|
});
|
|
}
|
|
FOREACH_EDIT_OBJECT_END();
|
|
}
|
|
|
|
#undef FOREACH_EDIT_OBJECT_BEGIN
|
|
#undef FOREACH_EDIT_OBJECT_END
|
|
}
|
|
else if (ob && (ob->mode & OB_MODE_POSE)) {
|
|
invert_m4_m4(ob->world_to_object, ob->object_to_world);
|
|
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_object_pose_array_get(scene, view_layer, v3d, &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *ob_iter = objects[ob_index];
|
|
const bool use_mat_local = (ob_iter != ob);
|
|
/* mislead counting bones... bah. We don't know the gizmo mode, could be mixed */
|
|
const int mode = TFM_ROTATION;
|
|
|
|
transform_convert_pose_transflags_update(ob_iter, mode, V3D_AROUND_CENTER_BOUNDS);
|
|
|
|
float mat_local[4][4];
|
|
if (use_mat_local) {
|
|
mul_m4_m4m4(mat_local, ob->world_to_object, ob_iter->object_to_world);
|
|
}
|
|
|
|
/* Use channels to get stats. */
|
|
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
|
|
if (!(pchan->bone->flag & BONE_TRANSFORM)) {
|
|
continue;
|
|
}
|
|
run_coord_with_matrix(pchan->pose_head, use_mat_local, mat_local);
|
|
totsel++;
|
|
|
|
if (r_drawflags) {
|
|
/* Protect-flags apply to local space in pose mode, so only let them influence axis
|
|
* visibility if we show the global orientation, otherwise it's confusing. */
|
|
if (ELEM(orient_index, V3D_ORIENT_LOCAL, V3D_ORIENT_GIMBAL)) {
|
|
protectflag_to_drawflags(pchan->protectflag, r_drawflags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MEM_freeN(objects);
|
|
}
|
|
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
|
|
if (ob->mode & OB_MODE_SCULPT) {
|
|
totsel = 1;
|
|
run_coord_with_matrix(ob->sculpt->pivot_pos, false, ob->object_to_world);
|
|
}
|
|
}
|
|
else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
|
|
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
|
|
PTCacheEditPoint *point;
|
|
PTCacheEditKey *ek;
|
|
int k;
|
|
|
|
if (edit) {
|
|
point = edit->points;
|
|
for (a = 0; a < edit->totpoint; a++, point++) {
|
|
if (point->flag & PEP_HIDE) {
|
|
continue;
|
|
}
|
|
|
|
for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
|
|
if (ek->flag & PEK_SELECT) {
|
|
user_fn((ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
|
|
totsel++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
/* we need the one selected object, if its not active */
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
|
base = BKE_view_layer_active_base_get(view_layer);
|
|
ob = base ? base->object : nullptr;
|
|
if (base && ((base->flag & BASE_SELECTED) == 0)) {
|
|
ob = nullptr;
|
|
}
|
|
|
|
for (base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first); base;
|
|
base = base->next)
|
|
{
|
|
if (!BASE_SELECTED_EDITABLE(v3d, base)) {
|
|
continue;
|
|
}
|
|
if (ob == nullptr) {
|
|
ob = base->object;
|
|
}
|
|
|
|
/* Get the boundbox out of the evaluated object. */
|
|
const BoundBox *bb = nullptr;
|
|
if (use_only_center == false) {
|
|
bb = BKE_object_boundbox_get(base->object);
|
|
}
|
|
|
|
if (use_only_center || (bb == nullptr)) {
|
|
user_fn(base->object->object_to_world[3]);
|
|
}
|
|
else {
|
|
for (uint j = 0; j < 8; j++) {
|
|
float co[3];
|
|
mul_v3_m4v3(co, base->object->object_to_world, bb->vec[j]);
|
|
user_fn(co);
|
|
}
|
|
}
|
|
totsel++;
|
|
if (r_drawflags) {
|
|
if (orient_index == V3D_ORIENT_GLOBAL) {
|
|
/* Protect-flags apply to world space in object mode,
|
|
* so only let them influence axis visibility if we show the global orientation,
|
|
* otherwise it's confusing. */
|
|
protectflag_to_drawflags(base->object->protectflag & OB_LOCK_LOC, r_drawflags);
|
|
}
|
|
else if (ELEM(orient_index, V3D_ORIENT_LOCAL, V3D_ORIENT_GIMBAL)) {
|
|
protectflag_to_drawflags(base->object->protectflag, r_drawflags);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r_mat && ob) {
|
|
*r_mat = ob->object_to_world;
|
|
}
|
|
|
|
return totsel;
|
|
}
|
|
|
|
int ED_transform_calc_gizmo_stats(const bContext *C,
|
|
const TransformCalcParams *params,
|
|
TransformBounds *tbounds,
|
|
RegionView3D *rv3d)
|
|
{
|
|
ScrArea *area = CTX_wm_area(C);
|
|
Scene *scene = CTX_data_scene(C);
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
|
|
int totsel = 0;
|
|
|
|
const int pivot_point = scene->toolsettings->transform_pivot_point;
|
|
const short orient_index = params->orientation_index ?
|
|
(params->orientation_index - 1) :
|
|
BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
|
|
|
|
Object *ob = gizmo_3d_transform_space_object_get(scene, view_layer);
|
|
Object *obedit = OBEDIT_FROM_OBACT(ob);
|
|
|
|
tbounds->use_matrix_space = false;
|
|
unit_m3(tbounds->axis);
|
|
|
|
/* global, local or normal orientation?
|
|
* if we could check 'totsel' now, this should be skipped with no selection. */
|
|
if (ob) {
|
|
float mat[3][3];
|
|
ED_transform_calc_orientation_from_type_ex(
|
|
scene, view_layer, v3d, rv3d, ob, obedit, orient_index, pivot_point, mat);
|
|
copy_m3_m3(tbounds->axis, mat);
|
|
}
|
|
|
|
reset_tw_center(tbounds);
|
|
|
|
if (rv3d) {
|
|
/* transform widget centroid/center */
|
|
copy_m4_m3(rv3d->twmat, tbounds->axis);
|
|
rv3d->twdrawflag = short(0xFFFF);
|
|
}
|
|
|
|
if (params->use_local_axis && (ob && ob->mode & (OB_MODE_EDIT | OB_MODE_POSE))) {
|
|
float diff_mat[3][3];
|
|
copy_m3_m4(diff_mat, ob->object_to_world);
|
|
normalize_m3(diff_mat);
|
|
invert_m3(diff_mat);
|
|
mul_m3_m3_pre(tbounds->axis, diff_mat);
|
|
normalize_m3(tbounds->axis);
|
|
|
|
tbounds->use_matrix_space = true;
|
|
copy_m4_m4(tbounds->matrix_space, ob->object_to_world);
|
|
}
|
|
|
|
const auto gizmo_3d_tbounds_calc_fn = [&](const blender::float3 &co) {
|
|
calc_tw_center(tbounds, co);
|
|
};
|
|
|
|
totsel = gizmo_3d_foreach_selected(C,
|
|
orient_index,
|
|
(pivot_point != V3D_AROUND_LOCAL_ORIGINS),
|
|
params->use_only_center,
|
|
gizmo_3d_tbounds_calc_fn,
|
|
nullptr,
|
|
rv3d ? &rv3d->twdrawflag : nullptr);
|
|
|
|
if (totsel) {
|
|
mul_v3_fl(tbounds->center, 1.0f / float(totsel)); /* centroid! */
|
|
|
|
bGPdata *gpd = CTX_data_gpencil_data(C);
|
|
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
|
|
if (!is_gp_edit && (obedit || (ob && (ob->mode & (OB_MODE_POSE | OB_MODE_SCULPT))))) {
|
|
if (ob->mode & OB_MODE_POSE) {
|
|
invert_m4_m4(ob->world_to_object, ob->object_to_world);
|
|
}
|
|
mul_m4_v3(ob->object_to_world, tbounds->center);
|
|
mul_m4_v3(ob->object_to_world, tbounds->min);
|
|
mul_m4_v3(ob->object_to_world, tbounds->max);
|
|
}
|
|
}
|
|
|
|
if (rv3d) {
|
|
if (totsel == 0) {
|
|
unit_m4(rv3d->twmat);
|
|
unit_m3(rv3d->tw_axis_matrix);
|
|
zero_v3(rv3d->tw_axis_min);
|
|
zero_v3(rv3d->tw_axis_max);
|
|
}
|
|
else {
|
|
copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
|
|
copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
|
|
copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
|
|
}
|
|
}
|
|
|
|
return totsel;
|
|
}
|
|
|
|
static void gizmo_get_idot(const RegionView3D *rv3d, float r_idot[3])
|
|
{
|
|
float view_vec[3], axis_vec[3];
|
|
ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
|
|
for (int i = 0; i < 3; i++) {
|
|
normalize_v3_v3(axis_vec, rv3d->twmat[i]);
|
|
r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
|
|
}
|
|
}
|
|
|
|
static bool gizmo_3d_calc_pos(const bContext *C,
|
|
const Scene *scene,
|
|
const TransformBounds *tbounds,
|
|
const short pivot_type,
|
|
float r_pivot_pos[3])
|
|
{
|
|
zero_v3(r_pivot_pos);
|
|
|
|
switch (pivot_type) {
|
|
case V3D_AROUND_CURSOR:
|
|
copy_v3_v3(r_pivot_pos, scene->cursor.location);
|
|
return true;
|
|
case V3D_AROUND_ACTIVE: {
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
|
Object *ob = BKE_view_layer_active_object_get(view_layer);
|
|
if (ob != nullptr) {
|
|
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
|
|
SculptSession *ss = ob->sculpt;
|
|
copy_v3_v3(r_pivot_pos, ss->pivot_pos);
|
|
return true;
|
|
}
|
|
else if (ED_object_calc_active_center(ob, false, r_pivot_pos)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
case V3D_AROUND_CENTER_BOUNDS: {
|
|
TransformBounds tbounds_stack;
|
|
if (tbounds == nullptr) {
|
|
TransformCalcParams calc_params{};
|
|
calc_params.use_only_center = true;
|
|
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds_stack, nullptr)) {
|
|
tbounds = &tbounds_stack;
|
|
}
|
|
}
|
|
if (tbounds) {
|
|
mid_v3_v3v3(r_pivot_pos, tbounds->min, tbounds->max);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case V3D_AROUND_LOCAL_ORIGINS:
|
|
case V3D_AROUND_CENTER_MEDIAN: {
|
|
if (tbounds) {
|
|
copy_v3_v3(r_pivot_pos, tbounds->center);
|
|
return true;
|
|
}
|
|
|
|
const auto gizmo_3d_calc_center_fn = [&](const blender::float3 &co) {
|
|
add_v3_v3(r_pivot_pos, co);
|
|
};
|
|
const float(*r_mat)[4] = nullptr;
|
|
int totsel;
|
|
totsel = gizmo_3d_foreach_selected(C,
|
|
0,
|
|
(pivot_type != V3D_AROUND_LOCAL_ORIGINS),
|
|
true,
|
|
gizmo_3d_calc_center_fn,
|
|
&r_mat,
|
|
nullptr);
|
|
if (totsel) {
|
|
mul_v3_fl(r_pivot_pos, 1.0f / float(totsel));
|
|
if (r_mat) {
|
|
mul_m4_v3(r_mat, r_pivot_pos);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void gizmo_prepare_mat(const struct bContext *C,
|
|
struct RegionView3D *rv3d,
|
|
const struct TransformBounds *tbounds)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
gizmo_3d_calc_pos(C, scene, tbounds, scene->toolsettings->transform_pivot_point, rv3d->twmat[3]);
|
|
}
|
|
|
|
/**
|
|
* Sets up \a r_start and \a r_len to define arrow line range.
|
|
* Needed to adjust line drawing for combined gizmo axis types.
|
|
*/
|
|
static void gizmo_line_range(const int twtype, const short axis_type, float *r_start, float *r_end)
|
|
{
|
|
float start = 0.2f;
|
|
float end = 1.0f;
|
|
|
|
switch (axis_type) {
|
|
case MAN_AXES_TRANSLATE:
|
|
if (twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) {
|
|
start = end - 0.125f;
|
|
}
|
|
if (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
|
|
/* Avoid rotate and translate gizmos overlap. */
|
|
const float rotate_offset = 0.215f;
|
|
start += rotate_offset;
|
|
end += rotate_offset + 0.2f;
|
|
}
|
|
break;
|
|
case MAN_AXES_SCALE:
|
|
if (twtype & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE)) {
|
|
end -= 0.225f;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (r_start) {
|
|
*r_start = start;
|
|
}
|
|
if (r_end) {
|
|
*r_end = end;
|
|
}
|
|
}
|
|
|
|
void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup,
|
|
wmMsgBus *mbus,
|
|
Scene *scene,
|
|
bScreen *screen,
|
|
ScrArea *area,
|
|
ARegion *region,
|
|
void (*type_fn)(wmGizmoGroupType *))
|
|
{
|
|
/* Subscribe to view properties */
|
|
wmMsgSubscribeValue msg_sub_value_gz_tag_refresh{};
|
|
msg_sub_value_gz_tag_refresh.owner = region;
|
|
msg_sub_value_gz_tag_refresh.user_data = gzgroup->parent_gzmap;
|
|
msg_sub_value_gz_tag_refresh.notify = WM_gizmo_do_msg_notify_tag_refresh;
|
|
|
|
int orient_flag = 0;
|
|
if (type_fn == VIEW3D_GGT_xform_gizmo) {
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
orient_flag = ggd->twtype_init;
|
|
}
|
|
else if (type_fn == VIEW3D_GGT_xform_cage) {
|
|
orient_flag = V3D_GIZMO_SHOW_OBJECT_SCALE;
|
|
/* pass */
|
|
}
|
|
else if (type_fn == VIEW3D_GGT_xform_shear) {
|
|
orient_flag = V3D_GIZMO_SHOW_OBJECT_ROTATE;
|
|
}
|
|
TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene,
|
|
orient_flag);
|
|
PointerRNA orient_ref_ptr;
|
|
RNA_pointer_create(&scene->id, &RNA_TransformOrientationSlot, orient_slot, &orient_ref_ptr);
|
|
const ToolSettings *ts = scene->toolsettings;
|
|
|
|
PointerRNA scene_ptr;
|
|
RNA_id_pointer_create(&scene->id, &scene_ptr);
|
|
{
|
|
const PropertyRNA *props[] = {
|
|
&rna_Scene_transform_orientation_slots,
|
|
};
|
|
for (int i = 0; i < ARRAY_SIZE(props); i++) {
|
|
WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
|
|
}
|
|
}
|
|
|
|
if ((ts->transform_pivot_point == V3D_AROUND_CURSOR) || (orient_slot->type == V3D_ORIENT_CURSOR))
|
|
{
|
|
/* We could be more specific here, for now subscribe to any cursor change. */
|
|
PointerRNA cursor_ptr;
|
|
RNA_pointer_create(&scene->id, &RNA_View3DCursor, &scene->cursor, &cursor_ptr);
|
|
WM_msg_subscribe_rna(mbus, &cursor_ptr, nullptr, &msg_sub_value_gz_tag_refresh, __func__);
|
|
}
|
|
|
|
{
|
|
const PropertyRNA *props[] = {
|
|
&rna_TransformOrientationSlot_type,
|
|
&rna_TransformOrientationSlot_use,
|
|
};
|
|
for (int i = 0; i < ARRAY_SIZE(props); i++) {
|
|
if (props[i]) {
|
|
WM_msg_subscribe_rna(
|
|
mbus, &orient_ref_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
|
|
}
|
|
}
|
|
}
|
|
|
|
PointerRNA toolsettings_ptr;
|
|
RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
|
|
|
|
if (ELEM(type_fn, VIEW3D_GGT_xform_gizmo, VIEW3D_GGT_xform_shear)) {
|
|
const PropertyRNA *props[] = {
|
|
&rna_ToolSettings_transform_pivot_point,
|
|
};
|
|
for (int i = 0; i < ARRAY_SIZE(props); i++) {
|
|
WM_msg_subscribe_rna(
|
|
mbus, &toolsettings_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
|
|
}
|
|
}
|
|
|
|
{
|
|
const PropertyRNA *props[] = {
|
|
&rna_ToolSettings_workspace_tool_type,
|
|
};
|
|
for (int i = 0; i < ARRAY_SIZE(props); i++) {
|
|
WM_msg_subscribe_rna(
|
|
mbus, &toolsettings_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
|
|
}
|
|
}
|
|
|
|
PointerRNA view3d_ptr;
|
|
RNA_pointer_create(&screen->id, &RNA_SpaceView3D, area->spacedata.first, &view3d_ptr);
|
|
|
|
if (type_fn == VIEW3D_GGT_xform_gizmo) {
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
if (ggd->use_twtype_refresh) {
|
|
const PropertyRNA *props[] = {
|
|
&rna_SpaceView3D_show_gizmo_object_translate,
|
|
&rna_SpaceView3D_show_gizmo_object_rotate,
|
|
&rna_SpaceView3D_show_gizmo_object_scale,
|
|
};
|
|
for (int i = 0; i < ARRAY_SIZE(props); i++) {
|
|
WM_msg_subscribe_rna(mbus, &view3d_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
|
|
}
|
|
}
|
|
}
|
|
else if (type_fn == VIEW3D_GGT_xform_cage) {
|
|
/* pass */
|
|
}
|
|
else if (type_fn == VIEW3D_GGT_xform_shear) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
BLI_assert(0);
|
|
}
|
|
|
|
WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_gz_tag_refresh);
|
|
WM_msg_subscribe_rna_anon_prop(mbus, EditBone, lock, &msg_sub_value_gz_tag_refresh);
|
|
}
|
|
|
|
static void gizmo_3d_dial_matrixbasis_calc(const ARegion *region,
|
|
const float axis[3],
|
|
const float center_global[3],
|
|
const float mval_init[2],
|
|
float r_mat_basis[4][4])
|
|
{
|
|
plane_from_point_normal_v3(r_mat_basis[2], center_global, axis);
|
|
copy_v3_v3(r_mat_basis[3], center_global);
|
|
|
|
if (ED_view3d_win_to_3d_on_plane(region, r_mat_basis[2], mval_init, false, r_mat_basis[1])) {
|
|
sub_v3_v3(r_mat_basis[1], center_global);
|
|
normalize_v3(r_mat_basis[1]);
|
|
cross_v3_v3v3(r_mat_basis[0], r_mat_basis[1], r_mat_basis[2]);
|
|
}
|
|
else {
|
|
/* The plane and the mouse direction are parallel.
|
|
* Calculate a matrix orthogonal to the axis. */
|
|
ortho_basis_v3v3_v3(r_mat_basis[0], r_mat_basis[1], r_mat_basis[2]);
|
|
}
|
|
|
|
r_mat_basis[0][3] = 0.0f;
|
|
r_mat_basis[1][3] = 0.0f;
|
|
r_mat_basis[2][3] = 0.0f;
|
|
r_mat_basis[3][3] = 1.0f;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Transform Gizmo
|
|
* \{ */
|
|
|
|
/** Scale of the two-axis planes. */
|
|
#define MAN_AXIS_SCALE_PLANE_SCALE 0.7f
|
|
|
|
static void rotation_get_fn(const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, void *value)
|
|
{
|
|
const GizmoGroup *ggd = (const GizmoGroup *)gz_prop->custom_func.user_data;
|
|
*(float *)value = ggd->rotation;
|
|
}
|
|
|
|
static void rotation_set_fn(const wmGizmo * /*gz*/, wmGizmoProperty *gz_prop, const void *value)
|
|
{
|
|
GizmoGroup *ggd = (GizmoGroup *)gz_prop->custom_func.user_data;
|
|
ggd->rotation = *(const float *)value;
|
|
}
|
|
|
|
static void gizmo_3d_setup_default_matrix(wmGizmo *axis, const int axis_idx)
|
|
{
|
|
float matrix[3][3];
|
|
|
|
switch (axis_idx) {
|
|
/* Arrow. */
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_ROT_X:
|
|
copy_v3_fl3(matrix[0], 0.0f, -1.0f, 0.0f);
|
|
copy_v3_fl3(matrix[1], 0.0f, 0.0f, -1.0f);
|
|
copy_v3_fl3(matrix[2], 1.0f, 0.0f, 0.0f);
|
|
break;
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_ROT_Y:
|
|
copy_v3_fl3(matrix[0], 1.0f, 0.0f, 0.0f);
|
|
copy_v3_fl3(matrix[1], 0.0f, 0.0f, -1.0f);
|
|
copy_v3_fl3(matrix[2], 0.0f, 1.0f, 0.0f);
|
|
break;
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_SCALE_Z:
|
|
case MAN_AXIS_ROT_Z:
|
|
copy_v3_fl3(matrix[0], 1.0f, 0.0f, 0.0f);
|
|
copy_v3_fl3(matrix[1], 0.0f, 1.0f, 0.0f);
|
|
copy_v3_fl3(matrix[2], 0.0f, 0.0f, 1.0f);
|
|
break;
|
|
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_SCALE_XY:
|
|
copy_v3_fl3(matrix[0], -M_SQRT1_2, M_SQRT1_2, 0.0f);
|
|
copy_v3_fl3(matrix[1], 0.0f, 0.0f, 1.0f);
|
|
copy_v3_fl3(matrix[2], M_SQRT1_2, M_SQRT1_2, 0.0f);
|
|
break;
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
copy_v3_fl3(matrix[0], 0.0f, -M_SQRT1_2, M_SQRT1_2);
|
|
copy_v3_fl3(matrix[1], 1.0f, 0.0f, 0.0f);
|
|
copy_v3_fl3(matrix[2], 0, M_SQRT1_2, M_SQRT1_2);
|
|
break;
|
|
case MAN_AXIS_SCALE_ZX:
|
|
case MAN_AXIS_TRANS_ZX:
|
|
copy_v3_fl3(matrix[0], M_SQRT1_2, 0.0f, -M_SQRT1_2);
|
|
copy_v3_fl3(matrix[1], 0.0f, 1.0f, 0.0f);
|
|
copy_v3_fl3(matrix[2], M_SQRT1_2, 0.0f, M_SQRT1_2);
|
|
break;
|
|
|
|
case MAN_AXIS_TRANS_C:
|
|
case MAN_AXIS_SCALE_C:
|
|
case MAN_AXIS_ROT_C:
|
|
case MAN_AXIS_ROT_T:
|
|
default:
|
|
return;
|
|
}
|
|
|
|
copy_m4_m3(axis->matrix_offset, matrix);
|
|
}
|
|
|
|
static void gizmo_3d_setup_draw_default(wmGizmo *axis, const int axis_idx)
|
|
{
|
|
gizmo_3d_setup_default_matrix(axis, axis_idx);
|
|
|
|
switch (axis_idx) {
|
|
/* Arrow. */
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_NORMAL);
|
|
break;
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_SCALE_Z:
|
|
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX);
|
|
RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_ARROW_DRAW_FLAG_STEM);
|
|
break;
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_XY:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_PLANE);
|
|
RNA_enum_set(axis->ptr, "draw_options", 0);
|
|
RNA_float_set(axis->ptr, "length", MAN_AXIS_SCALE_PLANE_SCALE);
|
|
break;
|
|
|
|
/* Primitive. */
|
|
case MAN_AXIS_TRANS_C:
|
|
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_PRIMITIVE_STYLE_CIRCLE);
|
|
RNA_boolean_set(axis->ptr, "draw_inner", false);
|
|
WM_gizmo_set_scale(axis, 0.2f);
|
|
|
|
/* Prevent axis gizmos overlapping the center point, see: #63744. */
|
|
axis->select_bias = 2.0f;
|
|
break;
|
|
case MAN_AXIS_SCALE_C:
|
|
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_PRIMITIVE_STYLE_ANNULUS);
|
|
RNA_boolean_set(axis->ptr, "draw_inner", false);
|
|
|
|
/* Use 6 since this is '1.2' if the main scale is 0.2. */
|
|
RNA_float_set(axis->ptr, "arc_inner_factor", 6.0f);
|
|
WM_gizmo_set_scale(axis, 0.2f);
|
|
|
|
/* Prevent axis gizmos overlapping the center point, see: #63744. */
|
|
axis->select_bias = -2.0f;
|
|
break;
|
|
|
|
/* Dial. */
|
|
case MAN_AXIS_ROT_X:
|
|
case MAN_AXIS_ROT_Y:
|
|
case MAN_AXIS_ROT_Z:
|
|
RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
|
|
break;
|
|
case MAN_AXIS_ROT_C:
|
|
RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_NOP);
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
|
|
WM_gizmo_set_scale(axis, 1.2f);
|
|
break;
|
|
case MAN_AXIS_ROT_T:
|
|
RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_FILL);
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_SELECT_BACKGROUND, true);
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_HOVER, true);
|
|
break;
|
|
}
|
|
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_SCALE_Y:
|
|
WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
|
|
break;
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_XY:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
WM_gizmo_set_line_width(axis, 1.0f);
|
|
break;
|
|
case MAN_AXIS_ROT_X:
|
|
case MAN_AXIS_ROT_Y:
|
|
case MAN_AXIS_ROT_Z:
|
|
/* Increased line width for better display. */
|
|
WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH + 1.0f);
|
|
break;
|
|
default:
|
|
WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
|
|
break;
|
|
}
|
|
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
switch (axis_type) {
|
|
case MAN_AXES_ROTATE: {
|
|
RNA_float_set(axis->ptr, "incremental_angle", 0.0f);
|
|
axis->select_bias = 0.0f;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void gizmo_3d_setup_draw_from_twtype(wmGizmo *axis, const int axis_idx, const int twtype)
|
|
{
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
RNA_enum_set(axis->ptr,
|
|
"draw_options",
|
|
(twtype & (V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)) ?
|
|
0 :
|
|
ED_GIZMO_ARROW_DRAW_FLAG_STEM);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_SCALE_Z: {
|
|
float start;
|
|
float end;
|
|
gizmo_line_range(twtype, axis_type, &start, &end);
|
|
mul_v3_v3fl(axis->matrix_offset[3], axis->matrix_offset[2], start);
|
|
|
|
RNA_float_set(axis->ptr, "length", end - start);
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (axis_type) {
|
|
case MAN_AXES_ROTATE: {
|
|
if ((twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) && twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) {
|
|
axis->select_bias = -2.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void gizmo_3d_setup_draw_modal(wmGizmo *axis, const int axis_idx, const int twtype)
|
|
{
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_SCALE_Z: {
|
|
|
|
float end, start_co[3] = {0.0f, 0.0f, 0.0f};
|
|
gizmo_line_range(twtype, axis_type, nullptr, &end);
|
|
RNA_float_set(axis->ptr, "length", end);
|
|
RNA_enum_set(axis->ptr,
|
|
"draw_options",
|
|
ED_GIZMO_ARROW_DRAW_FLAG_STEM | ED_GIZMO_ARROW_DRAW_FLAG_ORIGIN);
|
|
WM_gizmo_set_matrix_offset_location(axis, start_co);
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, false);
|
|
break;
|
|
}
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_XY:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_ARROW_DRAW_FLAG_ORIGIN);
|
|
break;
|
|
case MAN_AXIS_SCALE_C:
|
|
RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_PRIMITIVE_STYLE_CIRCLE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (axis_type) {
|
|
case MAN_AXES_ROTATE: {
|
|
PropertyRNA *prop = RNA_struct_find_property(axis->ptr, "draw_options");
|
|
const int dial_flag = RNA_property_enum_get(axis->ptr, prop);
|
|
RNA_property_enum_set(axis->ptr, prop, dial_flag | ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
|
|
{
|
|
GizmoGroup *ggd = MEM_cnew<GizmoGroup>(__func__);
|
|
|
|
const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
|
|
const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
|
|
const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true);
|
|
|
|
wmGizmoPropertyFnParams params{};
|
|
params.value_get_fn = rotation_get_fn;
|
|
params.value_set_fn = rotation_set_fn;
|
|
params.user_data = ggd;
|
|
|
|
#define GIZMO_NEW_ARROW(v) \
|
|
{ \
|
|
ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, nullptr); \
|
|
} \
|
|
((void)0)
|
|
#define GIZMO_NEW_DIAL(v) \
|
|
{ \
|
|
ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, nullptr); \
|
|
WM_gizmo_target_property_def_func(ggd->gizmos[v], "offset", ¶ms); \
|
|
} \
|
|
((void)0)
|
|
#define GIZMO_NEW_PRIM(v) \
|
|
{ \
|
|
ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_prim, gzgroup, nullptr); \
|
|
} \
|
|
((void)0)
|
|
|
|
/* add/init widgets - order matters! */
|
|
GIZMO_NEW_DIAL(MAN_AXIS_ROT_T);
|
|
|
|
GIZMO_NEW_PRIM(MAN_AXIS_SCALE_C);
|
|
|
|
GIZMO_NEW_ARROW(MAN_AXIS_SCALE_X);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Y);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Z);
|
|
|
|
GIZMO_NEW_ARROW(MAN_AXIS_SCALE_XY);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_SCALE_YZ);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_SCALE_ZX);
|
|
|
|
GIZMO_NEW_DIAL(MAN_AXIS_ROT_X);
|
|
GIZMO_NEW_DIAL(MAN_AXIS_ROT_Y);
|
|
GIZMO_NEW_DIAL(MAN_AXIS_ROT_Z);
|
|
|
|
/* init screen aligned widget last here, looks better, behaves better */
|
|
GIZMO_NEW_DIAL(MAN_AXIS_ROT_C);
|
|
|
|
GIZMO_NEW_PRIM(MAN_AXIS_TRANS_C);
|
|
|
|
GIZMO_NEW_ARROW(MAN_AXIS_TRANS_X);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Y);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Z);
|
|
|
|
GIZMO_NEW_ARROW(MAN_AXIS_TRANS_XY);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_TRANS_YZ);
|
|
GIZMO_NEW_ARROW(MAN_AXIS_TRANS_ZX);
|
|
|
|
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
|
|
gizmo_3d_setup_draw_default(axis, axis_idx);
|
|
}
|
|
MAN_ITER_AXES_END;
|
|
|
|
return ggd;
|
|
}
|
|
|
|
/**
|
|
* Custom handler for gizmo widgets
|
|
*/
|
|
static int gizmo_modal(bContext *C,
|
|
wmGizmo *widget,
|
|
const wmEvent *event,
|
|
eWM_GizmoFlagTweak /*tweak_flag*/)
|
|
{
|
|
/* Avoid unnecessary updates, partially address: #55458. */
|
|
if (ELEM(event->type, TIMER, INBETWEEN_MOUSEMOVE)) {
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
ARegion *region = CTX_wm_region(C);
|
|
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
|
wmGizmoGroup *gzgroup = widget->parent_gzgroup;
|
|
|
|
/* Recalculating the orientation has two problems.
|
|
* - The matrix calculated based on the transformed selection may not match the matrix
|
|
* that was set when transform started.
|
|
* - Inspecting the selection for every update is expensive (for *every* redraw).
|
|
*
|
|
* Instead, use #transform_apply_matrix to transform `rv3d->twmat` or the final scale value
|
|
* when scaling.
|
|
*/
|
|
if (false) {
|
|
TransformBounds tbounds;
|
|
|
|
TransformCalcParams calc_params{};
|
|
calc_params.use_only_center = true;
|
|
if (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds, rv3d)) {
|
|
gizmo_prepare_mat(C, rv3d, &tbounds);
|
|
for (wmGizmo *gz = static_cast<wmGizmo *>(gzgroup->gizmos.first); gz; gz = gz->next) {
|
|
WM_gizmo_set_matrix_location(gz, rv3d->twmat[3]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
wmWindow *win = CTX_wm_window(C);
|
|
wmOperator *op = nullptr;
|
|
for (int i = 0; i < widget->op_data_len; i++) {
|
|
wmGizmoOpElem *gzop = WM_gizmo_operator_get(widget, i);
|
|
op = WM_operator_find_modal_by_type(win, gzop->type);
|
|
if (op != nullptr) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (op != nullptr) {
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &widget);
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
|
|
float twmat[4][4];
|
|
float scale_buf[3];
|
|
float *scale = nullptr;
|
|
bool update = false;
|
|
copy_m4_m4(twmat, rv3d->twmat);
|
|
|
|
if (axis_type == MAN_AXES_SCALE) {
|
|
scale = scale_buf;
|
|
transform_final_value_get(static_cast<const TransInfo *>(op->customdata), scale, 3);
|
|
update = true;
|
|
}
|
|
else if (axis_type == MAN_AXES_ROTATE) {
|
|
transform_final_value_get(
|
|
static_cast<const TransInfo *>(op->customdata), &ggd->rotation, 1);
|
|
if (widget != ggd->gizmos[MAN_AXIS_ROT_C]) {
|
|
ggd->rotation *= -1;
|
|
}
|
|
RNA_float_set(
|
|
widget->ptr,
|
|
"incremental_angle",
|
|
transform_snap_increment_get(static_cast<const TransInfo *>(op->customdata)));
|
|
}
|
|
else if (transform_apply_matrix(static_cast<TransInfo *>(op->customdata), twmat)) {
|
|
update = true;
|
|
}
|
|
|
|
if (update) {
|
|
gizmogroup_refresh_from_matrix(gzgroup, twmat, scale, true);
|
|
ED_region_tag_redraw_editor_overlays(region);
|
|
}
|
|
}
|
|
}
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
|
|
static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup)
|
|
{
|
|
struct {
|
|
wmOperatorType *translate, *rotate, *trackball, *resize;
|
|
} ot_store = {nullptr};
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
|
|
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
bool constraint_axis[3] = {1, 0, 0};
|
|
PointerRNA *ptr = nullptr;
|
|
|
|
gizmo_get_axis_constraint(axis_idx, constraint_axis);
|
|
|
|
/* custom handler! */
|
|
WM_gizmo_set_fn_custom_modal(axis, gizmo_modal);
|
|
|
|
gizmo_3d_setup_draw_from_twtype(axis, axis_idx, ggd->twtype);
|
|
|
|
switch (axis_type) {
|
|
case MAN_AXES_TRANSLATE:
|
|
if (ot_store.translate == nullptr) {
|
|
ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
|
|
}
|
|
ptr = WM_gizmo_operator_set(axis, 0, ot_store.translate, nullptr);
|
|
break;
|
|
case MAN_AXES_ROTATE: {
|
|
wmOperatorType *ot_rotate;
|
|
if (axis_idx == MAN_AXIS_ROT_T) {
|
|
if (ot_store.trackball == nullptr) {
|
|
ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
|
|
}
|
|
ot_rotate = ot_store.trackball;
|
|
}
|
|
else {
|
|
if (ot_store.rotate == nullptr) {
|
|
ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
|
|
}
|
|
ot_rotate = ot_store.rotate;
|
|
}
|
|
ptr = WM_gizmo_operator_set(axis, 0, ot_rotate, nullptr);
|
|
break;
|
|
}
|
|
case MAN_AXES_SCALE: {
|
|
if (ot_store.resize == nullptr) {
|
|
ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
|
|
}
|
|
ptr = WM_gizmo_operator_set(axis, 0, ot_store.resize, nullptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ptr) {
|
|
PropertyRNA *prop;
|
|
if (ELEM(true, UNPACK3(constraint_axis))) {
|
|
if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
|
|
RNA_property_boolean_set_array(ptr, prop, constraint_axis);
|
|
}
|
|
}
|
|
|
|
RNA_boolean_set(ptr, "release_confirm", 1);
|
|
}
|
|
}
|
|
MAN_ITER_AXES_END;
|
|
}
|
|
|
|
static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup)
|
|
{
|
|
GizmoGroup *ggd = gizmogroup_init(gzgroup);
|
|
|
|
gzgroup->customdata = ggd;
|
|
|
|
{
|
|
ScrArea *area = CTX_wm_area(C);
|
|
const bToolRef *tref = area->runtime.tool;
|
|
|
|
ggd->twtype = 0;
|
|
if (tref && STREQ(tref->idname, "builtin.move")) {
|
|
ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_TRANSLATE;
|
|
}
|
|
else if (tref && STREQ(tref->idname, "builtin.rotate")) {
|
|
ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_ROTATE;
|
|
}
|
|
else if (tref && STREQ(tref->idname, "builtin.scale")) {
|
|
ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_SCALE;
|
|
}
|
|
else if (tref && STREQ(tref->idname, "builtin.transform")) {
|
|
ggd->twtype = V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE |
|
|
V3D_GIZMO_SHOW_OBJECT_SCALE;
|
|
}
|
|
else {
|
|
/* This is also correct logic for 'builtin.transform', no special check needed. */
|
|
/* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */
|
|
ggd->twtype = V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE |
|
|
V3D_GIZMO_SHOW_OBJECT_SCALE;
|
|
ggd->use_twtype_refresh = true;
|
|
}
|
|
BLI_assert(ggd->twtype != 0);
|
|
ggd->twtype_init = ggd->twtype;
|
|
}
|
|
|
|
/* *** set properties for axes *** */
|
|
gizmogroup_init_properties_from_twtype(gzgroup);
|
|
}
|
|
|
|
/**
|
|
* Set properties for axes.
|
|
*
|
|
* \param twmat: The transform matrix (typically #RegionView3D.twmat).
|
|
* \param scale: Optional scale, to show scale while modally dragging the scale handles.
|
|
*/
|
|
static void gizmo_refresh_from_matrix(wmGizmo *axis,
|
|
const int axis_idx,
|
|
const int twtype,
|
|
const float twmat[4][4],
|
|
const float scale[3])
|
|
{
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
const int aidx_norm = gizmo_orientation_axis(axis_idx, nullptr);
|
|
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_XY:
|
|
case MAN_AXIS_TRANS_YZ:
|
|
case MAN_AXIS_TRANS_ZX:
|
|
case MAN_AXIS_SCALE_XY:
|
|
case MAN_AXIS_SCALE_YZ:
|
|
case MAN_AXIS_SCALE_ZX:
|
|
copy_m4_m4(axis->matrix_basis, twmat);
|
|
if (scale) {
|
|
RNA_float_set(axis->ptr,
|
|
"length",
|
|
MAN_AXIS_SCALE_PLANE_SCALE * scale[aidx_norm == 2 ? 0 : aidx_norm + 1]);
|
|
}
|
|
break;
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_SCALE_Z:
|
|
copy_m4_m4(axis->matrix_basis, twmat);
|
|
if (scale) {
|
|
float end;
|
|
gizmo_line_range(twtype, axis_type, nullptr, &end);
|
|
RNA_float_set(axis->ptr, "length", end * scale[aidx_norm]);
|
|
}
|
|
|
|
break;
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
copy_m4_m4(axis->matrix_basis, twmat);
|
|
break;
|
|
case MAN_AXIS_SCALE_C:
|
|
WM_gizmo_set_matrix_location(axis, twmat[3]);
|
|
if (scale) {
|
|
WM_gizmo_set_scale(axis, 0.2f * scale[0]);
|
|
}
|
|
break;
|
|
case MAN_AXIS_ROT_X:
|
|
case MAN_AXIS_ROT_Y:
|
|
case MAN_AXIS_ROT_Z:
|
|
copy_m4_m4(axis->matrix_basis, twmat);
|
|
orthogonalize_m4(axis->matrix_basis, aidx_norm);
|
|
break;
|
|
case MAN_AXIS_ROT_C:
|
|
case MAN_AXIS_ROT_T:
|
|
default:
|
|
WM_gizmo_set_matrix_location(axis, twmat[3]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void gizmogroup_refresh_from_matrix(wmGizmoGroup *gzgroup,
|
|
const float twmat[4][4],
|
|
const float scale[3],
|
|
const bool ignore_hidden)
|
|
{
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
|
|
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
|
|
if (ignore_hidden && axis->flag & WM_GIZMO_HIDDEN) {
|
|
continue;
|
|
}
|
|
gizmo_refresh_from_matrix(axis, axis_idx, ggd->twtype, twmat, scale);
|
|
}
|
|
MAN_ITER_AXES_END;
|
|
}
|
|
|
|
static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
|
|
{
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
{
|
|
wmGizmo *gz = WM_gizmomap_get_modal(region->gizmo_map);
|
|
if (gz && gz->parent_gzgroup == gzgroup) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
Scene *scene = CTX_data_scene(C);
|
|
ScrArea *area = CTX_wm_area(C);
|
|
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
|
|
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
|
TransformBounds tbounds;
|
|
|
|
if (ggd->use_twtype_refresh) {
|
|
ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init;
|
|
if (ggd->twtype != ggd->twtype_prev) {
|
|
ggd->twtype_prev = ggd->twtype;
|
|
gizmogroup_init_properties_from_twtype(gzgroup);
|
|
}
|
|
}
|
|
|
|
const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init);
|
|
|
|
/* skip, we don't draw anything anyway */
|
|
TransformCalcParams calc_params{};
|
|
calc_params.use_only_center = true;
|
|
calc_params.orientation_index = orient_index + 1;
|
|
if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C, &calc_params, &tbounds, rv3d) == 0))) {
|
|
return;
|
|
}
|
|
|
|
gizmo_3d_calc_pos(
|
|
C, scene, &tbounds, scene->toolsettings->transform_pivot_point, rv3d->twmat[3]);
|
|
|
|
gizmogroup_refresh_from_matrix(gzgroup, rv3d->twmat, nullptr, false);
|
|
}
|
|
|
|
static void WIDGETGROUP_gizmo_message_subscribe(const bContext *C,
|
|
wmGizmoGroup *gzgroup,
|
|
wmMsgBus *mbus)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
bScreen *screen = CTX_wm_screen(C);
|
|
ScrArea *area = CTX_wm_area(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
gizmo_xform_message_subscribe(
|
|
gzgroup, mbus, scene, screen, area, region, VIEW3D_GGT_xform_gizmo);
|
|
}
|
|
|
|
static void gizmogroup_hide_all(GizmoGroup *ggd)
|
|
{
|
|
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
|
|
}
|
|
MAN_ITER_AXES_END;
|
|
}
|
|
|
|
static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
|
|
{
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
// ScrArea *area = CTX_wm_area(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
// View3D *v3d =static_cast< View3D *> (area->spacedata.first);
|
|
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
|
float viewinv_m3[3][3];
|
|
copy_m3_m4(viewinv_m3, rv3d->viewinv);
|
|
float idot[3];
|
|
|
|
/* Re-calculate hidden unless modal. */
|
|
bool is_modal = false;
|
|
{
|
|
wmGizmo *gz = WM_gizmomap_get_modal(region->gizmo_map);
|
|
if (gz && gz->parent_gzgroup == gzgroup) {
|
|
is_modal = true;
|
|
}
|
|
}
|
|
|
|
/* when looking through a selected camera, the gizmo can be at the
|
|
* exact same position as the view, skip so we don't break selection */
|
|
if (ggd->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 5e-7f) {
|
|
if (!is_modal) {
|
|
gizmogroup_hide_all(ggd);
|
|
}
|
|
return;
|
|
}
|
|
gizmo_get_idot(rv3d, idot);
|
|
|
|
/* *** set properties for axes *** */
|
|
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
|
|
if (is_modal) {
|
|
if (axis->flag & WM_GIZMO_HIDDEN) {
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) {
|
|
/* XXX maybe unset _HIDDEN flag on redraw? */
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
|
|
}
|
|
else {
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
|
|
continue;
|
|
}
|
|
|
|
/* Align to view. */
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_C:
|
|
case MAN_AXIS_ROT_C:
|
|
case MAN_AXIS_SCALE_C:
|
|
case MAN_AXIS_ROT_T:
|
|
WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
float color[4], color_hi[4];
|
|
gizmo_get_axis_color(axis_idx, idot, color, color_hi);
|
|
WM_gizmo_set_color(axis, color);
|
|
WM_gizmo_set_color_highlight(axis, color_hi);
|
|
}
|
|
MAN_ITER_AXES_END;
|
|
|
|
/* Refresh handled above when using view orientation. */
|
|
if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
|
|
scene, ggd->twtype_init);
|
|
switch (orient_slot->type) {
|
|
case V3D_ORIENT_VIEW: {
|
|
WIDGETGROUP_gizmo_refresh(C, gzgroup);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
|
|
}
|
|
}
|
|
|
|
static void gizmo_3d_draw_invoke(wmGizmoGroup *gzgroup,
|
|
const ARegion *region,
|
|
const int axis_idx_active,
|
|
const float mval[2])
|
|
{
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
|
|
|
|
wmGizmo *axis_active = ggd->gizmos[axis_idx_active];
|
|
|
|
const short axis_active_type = gizmo_get_axis_type(axis_idx_active);
|
|
|
|
/* Display only the active gizmo. */
|
|
gizmogroup_hide_all(ggd);
|
|
WM_gizmo_set_flag(axis_active, WM_GIZMO_HIDDEN, false);
|
|
gizmo_refresh_from_matrix(axis_active, axis_idx_active, ggd->twtype, rv3d->twmat, nullptr);
|
|
|
|
if (ELEM(axis_idx_active, MAN_AXIS_TRANS_C, MAN_AXIS_SCALE_C, MAN_AXIS_ROT_C, MAN_AXIS_ROT_T)) {
|
|
WM_gizmo_set_matrix_rotation_from_z_axis(axis_active, rv3d->viewinv[2]);
|
|
}
|
|
|
|
gizmo_3d_setup_draw_modal(axis_active, axis_idx_active, ggd->twtype);
|
|
|
|
if (axis_active_type == MAN_AXES_TRANSLATE) {
|
|
/* Arrows are used for visual reference, so keep all visible. */
|
|
for (int axis_idx = MAN_AXIS_TRANS_X; axis_idx <= MAN_AXIS_TRANS_Z; axis_idx++) {
|
|
if (axis_idx == axis_idx_active) {
|
|
continue;
|
|
}
|
|
wmGizmo *axis = ggd->gizmos[axis_idx];
|
|
WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
|
|
gizmo_refresh_from_matrix(axis, axis_idx, ggd->twtype, rv3d->twmat, nullptr);
|
|
gizmo_3d_setup_draw_default(axis, axis_idx);
|
|
gizmo_3d_setup_draw_from_twtype(axis, axis_idx, ggd->twtype);
|
|
RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_ARROW_DRAW_FLAG_STEM);
|
|
}
|
|
}
|
|
else if (axis_active_type == MAN_AXES_ROTATE && axis_idx_active != MAN_AXIS_ROT_T) {
|
|
float mat[3][3];
|
|
mul_m3_m4m4(mat, axis_active->matrix_basis, axis_active->matrix_offset);
|
|
gizmo_3d_dial_matrixbasis_calc(
|
|
region, mat[2], axis_active->matrix_basis[3], mval, axis_active->matrix_offset);
|
|
|
|
copy_m3_m4(mat, axis_active->matrix_basis);
|
|
invert_m3(mat);
|
|
mul_m4_m3m4(axis_active->matrix_offset, mat, axis_active->matrix_offset);
|
|
zero_v3(axis_active->matrix_offset[3]);
|
|
}
|
|
}
|
|
|
|
static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C,
|
|
wmGizmoGroup *gzgroup,
|
|
wmGizmo *gz,
|
|
const wmEvent *event)
|
|
{
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup->customdata);
|
|
const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &gz);
|
|
|
|
const float mval[2] = {float(event->mval[0]), float(event->mval[1])};
|
|
gizmo_3d_draw_invoke(gzgroup, CTX_wm_region(C), axis_idx, mval);
|
|
|
|
/* Support gizmo specific orientation. */
|
|
if (gz != ggd->gizmos[MAN_AXIS_ROT_T]) {
|
|
Scene *scene = CTX_data_scene(C);
|
|
wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
|
|
PointerRNA *ptr = &gzop->ptr;
|
|
PropertyRNA *prop_orient_type = RNA_struct_find_property(ptr, "orient_type");
|
|
const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
|
|
scene, ggd->twtype_init);
|
|
if ((gz == ggd->gizmos[MAN_AXIS_ROT_C]) ||
|
|
(orient_slot == &scene->orientation_slots[SCE_ORIENT_DEFAULT]))
|
|
{
|
|
/* #MAN_AXIS_ROT_C always uses the #V3D_ORIENT_VIEW orientation,
|
|
* optionally we could set this orientation instead of unset the property. */
|
|
RNA_property_unset(ptr, prop_orient_type);
|
|
}
|
|
else {
|
|
/* TODO: APIfunction */
|
|
int index = BKE_scene_orientation_slot_get_index(orient_slot);
|
|
RNA_property_enum_set(ptr, prop_orient_type, index);
|
|
}
|
|
}
|
|
|
|
/* Support shift click to constrain axis. */
|
|
int axis = -1;
|
|
switch (axis_idx) {
|
|
case MAN_AXIS_TRANS_X:
|
|
case MAN_AXIS_TRANS_Y:
|
|
case MAN_AXIS_TRANS_Z:
|
|
axis = axis_idx - MAN_AXIS_TRANS_X;
|
|
break;
|
|
case MAN_AXIS_SCALE_X:
|
|
case MAN_AXIS_SCALE_Y:
|
|
case MAN_AXIS_SCALE_Z:
|
|
axis = axis_idx - MAN_AXIS_SCALE_X;
|
|
break;
|
|
}
|
|
|
|
if (axis != -1) {
|
|
/* Swap single axis for two-axis constraint. */
|
|
const bool flip = (event->modifier & KM_SHIFT) != 0;
|
|
BLI_assert(axis_idx != -1);
|
|
const short axis_type = gizmo_get_axis_type(axis_idx);
|
|
if (axis_type != MAN_AXES_ROTATE) {
|
|
wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
|
|
PointerRNA *ptr = &gzop->ptr;
|
|
PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
|
|
if (prop_constraint_axis) {
|
|
bool constraint[3] = {false};
|
|
constraint[axis] = true;
|
|
if (flip) {
|
|
for (int i = 0; i < ARRAY_SIZE(constraint); i++) {
|
|
constraint[i] = !constraint[i];
|
|
}
|
|
}
|
|
RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d)
|
|
{
|
|
if (v3d->gizmo_flag & V3D_GIZMO_HIDE) {
|
|
return false;
|
|
}
|
|
if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool WIDGETGROUP_gizmo_poll_context(const bContext *C, wmGizmoGroupType * /*gzgt*/)
|
|
{
|
|
ScrArea *area = CTX_wm_area(C);
|
|
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
|
|
if (!WIDGETGROUP_gizmo_poll_generic(v3d)) {
|
|
return false;
|
|
}
|
|
|
|
const bToolRef *tref = area->runtime.tool;
|
|
if (v3d->gizmo_flag & V3D_GIZMO_HIDE_CONTEXT) {
|
|
return false;
|
|
}
|
|
if ((v3d->gizmo_show_object & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE |
|
|
V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Don't show if the tool has a gizmo. */
|
|
if (tref && tref->runtime && tref->runtime->gizmo_group[0]) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool WIDGETGROUP_gizmo_poll_tool(const bContext *C, wmGizmoGroupType *gzgt)
|
|
{
|
|
if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
|
|
return false;
|
|
}
|
|
|
|
ScrArea *area = CTX_wm_area(C);
|
|
View3D *v3d = static_cast<View3D *>(area->spacedata.first);
|
|
if (!WIDGETGROUP_gizmo_poll_generic(v3d)) {
|
|
return false;
|
|
}
|
|
|
|
if (v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Expose as multiple gizmos so tools use one, persistent context another.
|
|
* Needed because they use different options which isn't so simple to dynamically update. */
|
|
|
|
void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt)
|
|
{
|
|
gzgt->name = "3D View: Transform Gizmo";
|
|
gzgt->idname = "VIEW3D_GGT_xform_gizmo";
|
|
|
|
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
|
|
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
|
|
|
|
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
|
|
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
|
|
|
|
gzgt->poll = WIDGETGROUP_gizmo_poll_tool;
|
|
gzgt->setup = WIDGETGROUP_gizmo_setup;
|
|
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
|
|
gzgt->refresh = WIDGETGROUP_gizmo_refresh;
|
|
gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
|
|
gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
|
|
gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
|
|
|
|
static const EnumPropertyItem rna_enum_gizmo_items[] = {
|
|
{V3D_GIZMO_SHOW_OBJECT_TRANSLATE, "TRANSLATE", 0, "Move", ""},
|
|
{V3D_GIZMO_SHOW_OBJECT_ROTATE, "ROTATE", 0, "Rotate", ""},
|
|
{V3D_GIZMO_SHOW_OBJECT_SCALE, "SCALE", 0, "Scale", ""},
|
|
{0, "NONE", 0, "None", ""},
|
|
{0, nullptr, 0, nullptr, nullptr},
|
|
};
|
|
RNA_def_enum(gzgt->srna,
|
|
"drag_action",
|
|
rna_enum_gizmo_items,
|
|
V3D_GIZMO_SHOW_OBJECT_TRANSLATE,
|
|
"Drag Action",
|
|
"");
|
|
|
|
g_GGT_xform_gizmo = gzgt;
|
|
}
|
|
|
|
void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt)
|
|
{
|
|
gzgt->name = "3D View: Transform Gizmo Context";
|
|
gzgt->idname = "VIEW3D_GGT_xform_gizmo_context";
|
|
|
|
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_PERSISTENT |
|
|
WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
|
|
|
|
gzgt->poll = WIDGETGROUP_gizmo_poll_context;
|
|
gzgt->setup = WIDGETGROUP_gizmo_setup;
|
|
gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
|
|
gzgt->refresh = WIDGETGROUP_gizmo_refresh;
|
|
gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
|
|
gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
|
|
gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
|
|
|
|
g_GGT_xform_gizmo_context = gzgt;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
static wmGizmoGroup *gizmogroup_xform_find(TransInfo *t)
|
|
{
|
|
struct wmGizmoMap *gizmo_map = t->region->gizmo_map;
|
|
if (gizmo_map == nullptr) {
|
|
BLI_assert_msg(false, "#T_NO_GIZMO should already be set to return early before.");
|
|
return nullptr;
|
|
}
|
|
|
|
wmGizmo *gizmo_modal_current = WM_gizmomap_get_modal(gizmo_map);
|
|
if (gizmo_modal_current) {
|
|
wmGizmoGroup *gzgroup = gizmo_modal_current->parent_gzgroup;
|
|
/* Check #wmGizmoGroup::customdata to make sure the GizmoGroup has been initialized. */
|
|
if (gzgroup->customdata && ELEM(gzgroup->type, g_GGT_xform_gizmo, g_GGT_xform_gizmo_context)) {
|
|
return gzgroup;
|
|
}
|
|
}
|
|
else {
|
|
/* See #WM_gizmomap_group_find_ptr. */
|
|
LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, WM_gizmomap_group_list(gizmo_map)) {
|
|
if (ELEM(gzgroup->type, g_GGT_xform_gizmo, g_GGT_xform_gizmo_context)) {
|
|
/* Choose the one that has been initialized. */
|
|
if (gzgroup->customdata) {
|
|
return gzgroup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void transform_gizmo_3d_model_from_constraint_and_mode_init(TransInfo *t)
|
|
{
|
|
wmGizmo *gizmo_modal_current = t->region && t->region->gizmo_map ?
|
|
WM_gizmomap_get_modal(t->region->gizmo_map) :
|
|
nullptr;
|
|
if (!gizmo_modal_current || !ELEM(gizmo_modal_current->parent_gzgroup->type,
|
|
g_GGT_xform_gizmo,
|
|
g_GGT_xform_gizmo_context))
|
|
{
|
|
t->flag |= T_NO_GIZMO;
|
|
}
|
|
}
|
|
|
|
void transform_gizmo_3d_model_from_constraint_and_mode_set(TransInfo *t)
|
|
{
|
|
if (t->flag & T_NO_GIZMO) {
|
|
return;
|
|
}
|
|
|
|
wmGizmoGroup *gzgroup_xform = gizmogroup_xform_find(t);
|
|
if (gzgroup_xform == nullptr) {
|
|
return;
|
|
}
|
|
|
|
int axis_idx = -1;
|
|
if (t->mode == TFM_TRACKBALL) {
|
|
/* Pass. Do not display gizmo. */
|
|
}
|
|
else if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
|
|
const int axis_map[3][7] = {
|
|
{MAN_AXIS_TRANS_X,
|
|
MAN_AXIS_TRANS_Y,
|
|
MAN_AXIS_TRANS_XY,
|
|
MAN_AXIS_TRANS_Z,
|
|
MAN_AXIS_TRANS_ZX,
|
|
MAN_AXIS_TRANS_YZ,
|
|
MAN_AXIS_TRANS_C},
|
|
{MAN_AXIS_ROT_X,
|
|
MAN_AXIS_ROT_Y,
|
|
MAN_AXIS_ROT_Z,
|
|
MAN_AXIS_ROT_Z,
|
|
MAN_AXIS_ROT_Y,
|
|
MAN_AXIS_ROT_X,
|
|
MAN_AXIS_ROT_C},
|
|
{MAN_AXIS_SCALE_X,
|
|
MAN_AXIS_SCALE_Y,
|
|
MAN_AXIS_SCALE_XY,
|
|
MAN_AXIS_SCALE_Z,
|
|
MAN_AXIS_SCALE_ZX,
|
|
MAN_AXIS_SCALE_YZ,
|
|
MAN_AXIS_SCALE_C},
|
|
};
|
|
|
|
BLI_STATIC_ASSERT(
|
|
/* Assert mode values. */
|
|
((TFM_ROTATION == TFM_TRANSLATION + 1) && (TFM_RESIZE == TFM_TRANSLATION + 2) &&
|
|
/* Assert constrain values. */
|
|
(CON_AXIS0 == (1 << 1)) && (CON_AXIS1 == (1 << 2)) && (CON_AXIS2 == (1 << 3))),
|
|
"");
|
|
|
|
const int trans_mode = t->mode - TFM_TRANSLATION;
|
|
int con_mode = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) >> 1) - 1;
|
|
if (t->con.mode & CON_APPLY) {
|
|
con_mode = ((t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) >> 1) - 1;
|
|
}
|
|
|
|
axis_idx = axis_map[trans_mode][con_mode];
|
|
}
|
|
|
|
wmGizmo *gizmo_modal_current = WM_gizmomap_get_modal(t->region->gizmo_map);
|
|
if (axis_idx != -1) {
|
|
RegionView3D *rv3d = static_cast<RegionView3D *>(t->region->regiondata);
|
|
float(*mat_cmp)[3] = t->orient[t->orient_curr != O_DEFAULT ? t->orient_curr : O_SCENE].matrix;
|
|
|
|
bool update_orientation = !(equals_v3v3(rv3d->twmat[0], mat_cmp[0]) &&
|
|
equals_v3v3(rv3d->twmat[1], mat_cmp[1]) &&
|
|
equals_v3v3(rv3d->twmat[2], mat_cmp[2]));
|
|
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup_xform->customdata);
|
|
wmGizmo *gizmo_expected = ggd->gizmos[axis_idx];
|
|
if (update_orientation || gizmo_modal_current != gizmo_expected) {
|
|
if (update_orientation) {
|
|
copy_m4_m3(rv3d->twmat, mat_cmp);
|
|
copy_v3_v3(rv3d->twmat[3], t->center_global);
|
|
}
|
|
|
|
wmEvent event = {nullptr};
|
|
|
|
/* Set the initial mouse value. Used for rotation gizmos. */
|
|
copy_v2_v2_int(event.mval, t->mouse.imval);
|
|
|
|
/* We need to update the position of the gizmo before invoking otherwise
|
|
* #wmGizmo::scale_final could be calculated wrong. */
|
|
gizmo_refresh_from_matrix(gizmo_expected, axis_idx, ggd->twtype, rv3d->twmat, nullptr);
|
|
|
|
BLI_assert_msg(!gizmo_modal_current || gizmo_modal_current->highlight_part == 0,
|
|
"Avoid changing the highlight part");
|
|
gizmo_expected->highlight_part = 0;
|
|
WM_gizmo_modal_set_while_modal(t->region->gizmo_map, t->context, gizmo_expected, &event);
|
|
WM_gizmo_highlight_set(t->region->gizmo_map, gizmo_expected);
|
|
}
|
|
}
|
|
else if (gizmo_modal_current) {
|
|
WM_gizmo_modal_set_while_modal(t->region->gizmo_map, t->context, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
void transform_gizmo_3d_model_from_constraint_and_mode_restore(TransInfo *t)
|
|
{
|
|
if (t->flag & T_NO_GIZMO) {
|
|
return;
|
|
}
|
|
|
|
wmGizmoGroup *gzgroup_xform = gizmogroup_xform_find(t);
|
|
if (gzgroup_xform == nullptr) {
|
|
return;
|
|
}
|
|
|
|
GizmoGroup *ggd = static_cast<GizmoGroup *>(gzgroup_xform->customdata);
|
|
|
|
/* #wmGizmoGroup::draw_prepare will handle the rest. */
|
|
MAN_ITER_AXES_BEGIN (axis, axis_idx) {
|
|
gizmo_3d_setup_draw_default(axis, axis_idx);
|
|
gizmo_3d_setup_draw_from_twtype(axis, axis_idx, ggd->twtype);
|
|
}
|
|
MAN_ITER_AXES_END;
|
|
}
|
|
|
|
bool ED_transform_calc_pivot_pos(const struct bContext *C,
|
|
const short pivot_type,
|
|
float r_pivot_pos[3])
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
return gizmo_3d_calc_pos(C, scene, nullptr, pivot_type, r_pivot_pos);
|
|
}
|