Sculpt: Transform tool

The sculpt mode transform tool applies the sculpt pivot transformation to all vertices, taking XYZ symmetry into account.
This commit also includes an operator to set the pivot point initial position.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D5717
This commit is contained in:
Pablo Dobarro 2019-09-10 19:55:15 +02:00
parent ef18b672f5
commit 309cd047ef
Notes: blender-bot 2023-02-14 02:45:41 +01:00
Referenced by commit 4782e941c8, Fix T71494: brush curve transform crash in sculpt mode
Referenced by commit 67310ed976, Fix T70006: crash when transforming paint curve edit points in sculpt mode
Referenced by issue #70006, Crash when using Curve as Stroke Method for Crease or Pinch in sculpt mode
14 changed files with 548 additions and 8 deletions

View File

@ -1986,6 +1986,11 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_sculpt.mesh_filter,
None,
_defs_transform.translate,
_defs_transform.rotate,
_defs_transform.scale,
_defs_transform.transform,
None,
*_tools_annotate,
],
'PAINT_TEXTURE': [

View File

@ -267,12 +267,19 @@ typedef struct SculptSession {
float cursor_view_normal[3];
struct RegionView3D *rv3d;
float pivot_pos[3];
/* Dynamic mesh preview */
int *preview_vert_index_list;
int preview_vert_index_count;
/* Transform operator */
float pivot_pos[3];
float pivot_rot[4];
float pivot_scale[3];
float init_pivot_pos[3];
float init_pivot_rot[4];
float init_pivot_scale[3];
union {
struct {
struct SculptVertexPaintGeomMap gmap;

View File

@ -39,6 +39,11 @@ bool ED_sculpt_mask_box_select(struct bContext *C,
const struct rcti *rect,
bool select);
/* transform */
void ED_sculpt_update_modal_transform(struct bContext *C);
void ED_sculpt_init_transform(struct bContext *C);
void ED_sculpt_end_transform(struct bContext *C);
/* sculpt_undo.c */
void ED_sculpt_undosys_type(struct UndoType *ut);

View File

@ -94,6 +94,7 @@ enum TfmMode {
#define CTX_OBMODE_XFORM_OBDATA (1 << 11)
/** Transform object parents without moving their children. */
#define CTX_OBMODE_XFORM_SKIP_CHILDREN (1 << 12)
#define CTX_SCULPT (1 << 13)
/* Standalone call to get the transformation center corresponding to the current situation
* returns 1 if successful, 0 otherwise (usually means there's no selection)

View File

@ -8737,7 +8737,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
for (int i = 0; i < vertex_count; i++) {
if (sculpt_vertex_mask_get(ss, i) < (0.5f + threshold) &&
sculpt_vertex_mask_get(ss, i) > (0.5f - threshold) &&
check_vertex_pivot_symmetry(sculpt_vertex_co_get(ss, i), ss->pivot_pos, symm)) {
check_vertex_pivot_symmetry(
sculpt_vertex_co_get(ss, i), ss->filter_cache->mask_expand_initial_co, symm)) {
total++;
add_v3_v3(avg, sculpt_vertex_co_get(ss, i));
}
@ -8859,6 +8860,9 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
ss->filter_cache->mask_update_current_it = 1;
ss->filter_cache->mask_update_it[(int)sculpt_active_vertex_get(ss)] = 1;
copy_v3_v3(ss->filter_cache->mask_expand_initial_co,
sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)));
char *visited_vertices = MEM_callocN(vertex_count * sizeof(char), "visited vertices");
sculpt_vertex_normal_get(ss, sculpt_active_vertex_get(ss), original_normal);
@ -9074,6 +9078,364 @@ void sculpt_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
ss->preview_vert_index_count = totpoints;
}
void ED_sculpt_init_transform(struct bContext *C)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
copy_v3_v3(ss->init_pivot_pos, ss->pivot_pos);
copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot);
ss->init_pivot_scale[0] = 1.0f;
ss->init_pivot_scale[1] = 1.0f;
ss->init_pivot_scale[2] = 1.0f;
ss->pivot_scale[0] = 1.0f;
ss->pivot_scale[1] = 1.0f;
ss->pivot_scale[2] = 1.0f;
sculpt_undo_push_begin("Transform");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
ss->pivot_rot[3] = 1.0f;
sculpt_vertex_random_access_init(ss);
sculpt_filter_cache_init(ob, sd);
}
typedef enum PaintSymmetryAreas {
AREA_SYMM_X = (1 << 0),
AREA_SYMM_Y = (1 << 1),
AREA_SYMM_Z = (1 << 2),
} PaintSymmetryAreas;
static char sculpt_get_vertex_symm_area(float co[3])
{
float vco[3];
char symm_area = 0;
copy_v3_v3(vco, co);
if (vco[0] < 0) {
symm_area |= AREA_SYMM_X;
}
if (vco[1] < 0) {
symm_area |= AREA_SYMM_Y;
}
if (vco[2] < 0) {
symm_area |= AREA_SYMM_Z;
}
return symm_area;
}
static void flip_qt(float qt[4], char symm)
{
float euler[3];
if (symm & PAINT_SYMM_X) {
quat_to_eul(euler, qt);
euler[1] = -euler[1];
euler[2] = -euler[2];
eul_to_quat(qt, euler);
}
if (symm & PAINT_SYMM_Y) {
quat_to_eul(euler, qt);
euler[0] = -euler[0];
euler[2] = -euler[2];
eul_to_quat(qt, euler);
}
if (symm & PAINT_SYMM_Z) {
quat_to_eul(euler, qt);
euler[0] = -euler[0];
euler[1] = -euler[1];
eul_to_quat(qt, euler);
}
}
static void sculpt_flip_transform_by_symm_area(
float disp[3], float rot[4], char symm, char symmarea, float pivot[3])
{
for (char i = 0; i < 3; i++) {
char symm_it = 1 << i;
if (symm & symm_it) {
if (symmarea & symm_it) {
if (disp) {
flip_v3(disp, symm_it);
}
if (rot) {
flip_qt(rot, symm_it);
}
}
if (pivot[0] < 0) {
if (disp) {
flip_v3(disp, symm_it);
}
if (rot) {
flip_qt(rot, symm_it);
}
}
}
}
}
static void sculpt_transform_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
PBVHNode *node = data->nodes[i];
SculptOrigVertData orig_data;
sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
PBVHVertexIter vd;
sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL)
{
sculpt_orig_vert_data_update(&orig_data, &vd);
float transformed_co[3], orig_co[3], disp[3];
float fade = vd.mask ? *vd.mask : 0.0f;
copy_v3_v3(orig_co, orig_data.co);
char symm_area = sculpt_get_vertex_symm_area(orig_co);
copy_v3_v3(transformed_co, orig_co);
mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co);
sub_v3_v3v3(disp, transformed_co, orig_co);
mul_v3_fl(disp, 1.0f - fade);
add_v3_v3v3(vd.co, orig_co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_redraw(node);
BKE_pbvh_node_mark_normals_update(node);
}
void ED_sculpt_update_modal_transform(struct bContext *C)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
sculpt_vertex_random_access_init(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false);
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.nodes = ss->filter_cache->nodes,
};
float final_pivot_pos[3], d_t[3], d_r[4];
float t_mat[4][4], r_mat[4][4], s_mat[4][4], pivot_mat[4][4], pivot_imat[4][4],
transform_mat[4][4];
copy_v3_v3(final_pivot_pos, ss->pivot_pos);
for (int i = 0; i < 8; i++) {
copy_v3_v3(final_pivot_pos, ss->pivot_pos);
unit_m4(pivot_mat);
unit_m4(t_mat);
unit_m4(r_mat);
unit_m4(s_mat);
/* Translation matrix */
sub_v3_v3v3(d_t, ss->pivot_pos, ss->init_pivot_pos);
sculpt_flip_transform_by_symm_area(d_t, NULL, symm, (char)i, ss->init_pivot_pos);
translate_m4(t_mat, d_t[0], d_t[1], d_t[2]);
/* Rotation matrix */
sub_qt_qtqt(d_r, ss->pivot_rot, ss->init_pivot_rot);
normalize_qt(d_r);
sculpt_flip_transform_by_symm_area(NULL, d_r, symm, (char)i, ss->init_pivot_pos);
quat_to_mat4(r_mat, d_r);
/* Scale matrix */
size_to_mat4(s_mat, ss->pivot_scale);
/* Pivot matrix */
sculpt_flip_transform_by_symm_area(final_pivot_pos, NULL, symm, (char)i, ss->init_pivot_pos);
translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]);
invert_m4_m4(pivot_imat, pivot_mat);
/* Final transform matrix */
mul_m4_m4m4(transform_mat, r_mat, t_mat);
mul_m4_m4m4(transform_mat, transform_mat, s_mat);
mul_m4_m4m4(data.transform_mats[i], transform_mat, pivot_imat);
mul_m4_m4m4(data.transform_mats[i], pivot_mat, data.transform_mats[i]);
}
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) &&
ss->filter_cache->totnode > SCULPT_THREADED_LIMIT);
BLI_task_parallel_range(
0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings);
if (ss->modifiers_active || ss->kb) {
sculpt_flush_stroke_deform(sd, ob, true);
}
sculpt_flush_update_step(C);
}
void ED_sculpt_end_transform(struct bContext *C)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
if (ss->filter_cache) {
sculpt_filter_cache_free(ss);
}
sculpt_undo_push_end();
sculpt_flush_update_done(C, ob);
}
typedef enum eSculptPivotPositionModes {
SCULPT_PIVOT_POSITION_ORIGIN = 0,
SCULPT_PIVOT_POSITION_UNMASKED = 1,
SCULPT_PIVOT_POSITION_MASK_BORDER = 2,
SCULPT_PIVOT_POSITION_ACTIVE_VERTEX = 3,
SCULPT_PIVOT_POSITION_CURSOR_SURFACE = 4,
} eSculptPivotPositionModes;
EnumPropertyItem prop_sculpt_pivot_position_types[] = {
{SCULPT_PIVOT_POSITION_ORIGIN,
"ORIGIN",
0,
"Origin",
"Sets the pivot to the origin of the sculpt"},
{SCULPT_PIVOT_POSITION_UNMASKED,
"UNMASKED",
0,
"Unmasked",
"Sets the pivot position to the average position of the unmasked vertices"},
{SCULPT_PIVOT_POSITION_MASK_BORDER,
"BORDER",
0,
"Mask border",
"Sets the pivot position to the center of the border of the mask"},
{SCULPT_PIVOT_POSITION_ACTIVE_VERTEX,
"ACTIVE",
0,
"Active vertex",
"Sets the pivot position to the active vertex position"},
{SCULPT_PIVOT_POSITION_CURSOR_SURFACE,
"SURFACE",
0,
"Surface",
"Sets the pivot position to the surface under the cursor"},
{0, NULL, 0, NULL, NULL},
};
static int sculpt_set_pivot_position_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
ARegion *ar = CTX_wm_region(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
int mode = RNA_enum_get(op->ptr, "mode");
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true);
int vert_count = sculpt_vertex_count_get(ss);
/* Pivot to center */
if (mode == SCULPT_PIVOT_POSITION_ORIGIN) {
zero_v3(ss->pivot_pos);
}
/* Pivot to unmasked */
if (mode == SCULPT_PIVOT_POSITION_UNMASKED) {
float avg[3];
int total = 0;
zero_v3(avg);
for (int i = 0; i < vert_count; i++) {
if (sculpt_vertex_mask_get(ss, i) < 1.0f &&
check_vertex_pivot_symmetry(sculpt_vertex_co_get(ss, i), ss->pivot_pos, symm)) {
total++;
add_v3_v3(avg, sculpt_vertex_co_get(ss, i));
}
}
if (total > 0) {
mul_v3_fl(avg, 1.0f / total);
copy_v3_v3(ss->pivot_pos, avg);
}
}
/* Pivot to mask border */
if (mode == SCULPT_PIVOT_POSITION_MASK_BORDER) {
float avg[3];
int total = 0;
float threshold = 0.2f;
zero_v3(avg);
for (int i = 0; i < vert_count; i++) {
if (sculpt_vertex_mask_get(ss, i) < (0.5f + threshold) &&
sculpt_vertex_mask_get(ss, i) > (0.5f - threshold) &&
check_vertex_pivot_symmetry(sculpt_vertex_co_get(ss, i), ss->pivot_pos, symm)) {
total++;
add_v3_v3(avg, sculpt_vertex_co_get(ss, i));
}
}
if (total > 0) {
mul_v3_fl(avg, 1.0f / total);
copy_v3_v3(ss->pivot_pos, avg);
}
}
/* Pivot to active vertex */
if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) {
copy_v3_v3(ss->pivot_pos, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)));
}
/* Pivot to raycast surface */
if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) {
float stroke_location[3];
float mouse[2];
mouse[0] = event->mval[0];
mouse[1] = event->mval[1];
if (sculpt_stroke_get_location(C, stroke_location, mouse)) {
copy_v3_v3(ss->pivot_pos, stroke_location);
}
}
ED_region_tag_redraw(ar);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
return OPERATOR_FINISHED;
}
static void SCULPT_OT_set_pivot_position(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Pivot Position";
ot->idname = "SCULPT_OT_set_pivot_position";
ot->description = "Sets the sculpt transform pivot position";
/* api callbacks */
ot->invoke = sculpt_set_pivot_position_invoke;
ot->poll = sculpt_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(ot->srna,
"mode",
prop_sculpt_pivot_position_types,
SCULPT_PIVOT_POSITION_UNMASKED,
"Mode",
"");
}
void ED_operatortypes_sculpt(void)
{
@ -9090,4 +9452,5 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_mask_filter);
WM_operatortype_append(SCULPT_OT_dirty_mask);
WM_operatortype_append(SCULPT_OT_mask_expand);
WM_operatortype_append(SCULPT_OT_set_pivot_position);
}

View File

@ -122,6 +122,10 @@ typedef struct SculptUndoNode {
int geom_totloop;
int geom_totpoly;
/* pivot */
float pivot_pos[3];
float pivot_rot[4];
size_t undo_size;
} SculptUndoNode;
@ -173,16 +177,16 @@ typedef struct SculptThreadedTaskData {
float (*mat)[4];
float (*vertCos)[3];
int filter_type;
float filter_strength;
int *node_mask;
/* 0=towards view, 1=flipped */
float (*area_cos)[3];
float (*area_nos)[3];
int *count;
bool any_vertex_sampled;
int filter_type;
float filter_strength;
int *node_mask;
float *prev_mask;
float *pose_origin;
@ -198,6 +202,8 @@ typedef struct SculptThreadedTaskData {
bool mask_expand_use_normals;
bool mask_expand_keep_prev_mask;
float transform_mats[8][4][4];
ThreadMutex mutex;
} SculptThreadedTaskData;
@ -387,6 +393,7 @@ typedef struct FilterCache {
int *mask_update_it;
float *normal_factor;
float *prev_mask;
float mask_expand_initial_co[3];
} FilterCache;
void sculpt_cache_calc_brushdata_symm(StrokeCache *cache,

View File

@ -494,6 +494,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
bool partial_update = true;
for (unode = lb->first; unode; unode = unode->next) {
/* restore pivot */
copy_v3_v3(ss->pivot_pos, unode->pivot_pos);
copy_v3_v3(ss->pivot_rot, unode->pivot_rot);
if (STREQ(unode->idname, ob->id.name)) {
if (unode->type == SCULPT_UNDO_MASK) {
/* is possible that we can't do the mask undo (below)
@ -1055,6 +1058,10 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
break;
}
/* store sculpt pivot */
copy_v3_v3(unode->pivot_pos, ss->pivot_pos);
copy_v3_v3(unode->pivot_rot, ss->pivot_rot);
/* store active shape key */
if (ss->kb) {
BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name));

View File

@ -56,6 +56,7 @@ set(SRC
transform_convert_object.c
transform_convert_paintcurve.c
transform_convert_particle.c
transform_convert_sculpt.c
transform_convert_sequencer.c
transform_convert_tracking.c
transform_generics.c

View File

@ -79,6 +79,7 @@
#include "ED_clip.h"
#include "ED_node.h"
#include "ED_gpencil.h"
#include "ED_sculpt.h"
#include "WM_types.h"
#include "WM_api.h"
@ -2299,6 +2300,10 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
}
}
if (t->options & CTX_SCULPT) {
ED_sculpt_end_transform(C);
}
if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
RNA_property_boolean_set(
op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
@ -2342,6 +2347,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
}
Object *ob = CTX_data_active_object(C);
if (ob && ob->mode == OB_MODE_SCULPT && ob->sculpt) {
options |= CTX_SCULPT;
}
t->options = options;
t->mode = mode;
@ -2404,6 +2414,10 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
createTransData(C, t); // make TransData structs from selection
if (t->options & CTX_SCULPT) {
ED_sculpt_init_transform(C);
}
if (t->data_len_all == 0) {
postTrans(C, t);
return 0;

View File

@ -2264,6 +2264,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
else if (t->options & CTX_PAINT_CURVE) {
/* pass */
}
else if (t->options & CTX_SCULPT) {
/* pass */
}
else if ((t->view_layer->basact) && (ob = t->view_layer->basact->object) &&
(ob->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, ob)) {
/* do nothing */
@ -2414,6 +2417,10 @@ void createTransData(bContext *C, TransInfo *t)
}
countAndCleanTransDataContainer(t);
}
else if (t->options & CTX_SCULPT) {
createTransSculpt(t);
countAndCleanTransDataContainer(t);
}
else if (t->options & CTX_TEXTURE) {
t->flag |= T_TEXTURE;
t->obedit_type = -1;

View File

@ -129,6 +129,8 @@ void createTransTexspace(TransInfo *t);
void createTransPaintCurveVerts(bContext *C, TransInfo *t);
/* transform_convert_particle.c */
void createTransParticleVerts(bContext *C, TransInfo *t);
/* transform_convert_sculpt.c */
void createTransSculpt(TransInfo *t);
/* transform_convert_sequence.c */
void createTransSeqData(bContext *C, TransInfo *t);
/* transform_convert_tracking.c */

View File

@ -0,0 +1,101 @@
/*
* 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.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup edtransform
*/
#include "DNA_space_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_paint.h"
#include "transform.h"
#include "transform_convert.h"
/* -------------------------------------------------------------------- */
/** \name Sculpt Transform Creation
*
* \{ */
void createTransSculpt(TransInfo *t)
{
TransData *td;
Scene *scene = t->scene;
if (ID_IS_LINKED(scene)) {
BKE_report(t->reports, RPT_ERROR, "Linked data can't text-space transform");
return;
}
Object *ob = CTX_data_active_object(t->context);
SculptSession *ss = ob->sculpt;
{
BLI_assert(t->data_container_len == 1);
TransDataContainer *tc = t->data_container;
tc->data_len = 1;
tc->is_active = 1;
td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace");
td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace");
}
td->flag = TD_SELECTED;
copy_v3_v3(td->center, ss->pivot_pos);
td->ob = NULL;
float tquat[4];
normalize_qt_qt(tquat, ss->pivot_rot);
quat_to_mat3(td->axismtx, tquat);
td->loc = ss->pivot_pos;
copy_v3_v3(td->iloc, ss->pivot_pos);
if (is_zero_v4(ss->pivot_rot)) {
ss->pivot_rot[3] = 1.0f;
}
td->ext->rot = NULL;
td->ext->rotAxis = NULL;
td->ext->rotAngle = NULL;
td->ext->quat = ss->pivot_rot;
copy_qt_qt(td->ext->iquat, ss->pivot_rot);
td->ext->rotOrder = ROT_MODE_QUAT;
ss->pivot_scale[0] = 1.0f;
ss->pivot_scale[1] = 1.0f;
ss->pivot_scale[2] = 1.0f;
td->ext->size = ss->pivot_scale;
copy_v3_v3(td->ext->isize, ss->pivot_scale);
float obmat_inv[3][3];
copy_m3_m4(obmat_inv, ob->obmat);
invert_m3(obmat_inv);
copy_m3_m3(td->smtx, obmat_inv);
copy_m3_m4(td->mtx, ob->obmat);
}
/** \} */

View File

@ -99,6 +99,7 @@
#include "ED_clip.h"
#include "ED_screen.h"
#include "ED_gpencil.h"
#include "ED_sculpt.h"
#include "WM_types.h"
#include "WM_api.h"
@ -1206,6 +1207,11 @@ static void recalcData_gpencil_strokes(TransInfo *t)
}
}
static void recalcData_sculpt(TransInfo *t)
{
ED_sculpt_update_modal_transform(t->context);
}
/* called for updating while transform acts, once per redraw */
void recalcData(TransInfo *t)
{
@ -1226,6 +1232,9 @@ void recalcData(TransInfo *t)
/* set recalc triangle cache flag */
recalcData_gpencil_strokes(t);
}
if (t->options & CTX_SCULPT) {
recalcData_sculpt(t);
}
else if (t->spacetype == SPACE_IMAGE) {
recalcData_image(t);
}

View File

@ -55,6 +55,7 @@
#include "BKE_scene.h"
#include "BKE_workspace.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "DEG_depsgraph.h"
@ -1056,7 +1057,13 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
}
}
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
/* pass */
if (ob->mode & OB_MODE_SCULPT) {
totsel = 1;
calc_tw_center_with_matrix(tbounds, ob->sculpt->pivot_pos, false, ob->obmat);
mul_m4_v3(ob->obmat, tbounds->center);
mul_m4_v3(ob->obmat, tbounds->min);
mul_m4_v3(ob->obmat, tbounds->max);
}
}
else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
PTCacheEdit *edit = PE_get_current(scene, ob);
@ -1168,6 +1175,10 @@ static void gizmo_prepare_mat(const bContext *C,
if (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
/* pass */
}
else if (ob->sculpt) {
SculptSession *ss = ob->sculpt;
copy_v3_v3(rv3d->twmat[3], ss->pivot_pos);
}
else if (ob != NULL) {
ED_object_calc_active_center(ob, false, rv3d->twmat[3]);
}