GPv3: Transform operators
This adds the keybindings, menu, and functionally for the edit mode transform operators to grease pencil v3. The transform operation include: * translate, rotate, scale, change opacity, change radius Pull Request: https://projects.blender.org/blender/blender/pulls/111836
This commit is contained in:
parent
b4e6ef4279
commit
e266b142f6
|
@ -4610,6 +4610,18 @@ def km_grease_pencil_edit(params):
|
|||
{"properties": [("type", "ALL_FRAMES")]}),
|
||||
# Keyframe Menu
|
||||
op_menu("VIEW3D_MT_edit_greasepencil_animation", {"type": 'I', "value": 'PRESS'}),
|
||||
|
||||
# Transform Actions.
|
||||
*_template_items_transform_actions(params, use_bend=True, use_mirror=True, use_tosphere=True, use_shear=True),
|
||||
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("mode", 'CURVE_SHRINKFATTEN')]}),
|
||||
("transform.transform", {"type": 'F', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("mode", 'GPENCIL_OPACITY')]}),
|
||||
|
||||
# Proportional editing.
|
||||
*_template_items_proportional_editing(
|
||||
params, connected=True, toggle_data_path='tool_settings.use_proportional_edit'),
|
||||
|
||||
# Cyclical set
|
||||
("grease_pencil.cyclical_set", {"type": 'F', "value": 'PRESS'}, {"properties": [("type", "CLOSE")]}),
|
||||
("grease_pencil.cyclical_set", {"type": 'C', "value": 'PRESS',
|
||||
|
@ -7265,6 +7277,16 @@ def km_3d_view_tool_shear(params):
|
|||
]},
|
||||
)
|
||||
|
||||
def km_3d_view_tool_bend(params):
|
||||
return (
|
||||
"3D View Tool: Bend",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
# No need for `tool_modifier` since this takes all input.
|
||||
("transform.bend", params.tool_maybe_tweak_event,
|
||||
{"properties": [("release_confirm", True)]}),
|
||||
]},
|
||||
)
|
||||
|
||||
def km_3d_view_tool_measure(params):
|
||||
return (
|
||||
|
@ -8567,6 +8589,7 @@ def generate_keymaps(params=None):
|
|||
km_3d_view_tool_rotate(params),
|
||||
km_3d_view_tool_scale(params),
|
||||
km_3d_view_tool_shear(params),
|
||||
km_3d_view_tool_bend(params),
|
||||
km_3d_view_tool_measure(params),
|
||||
km_3d_view_tool_interactive_add(params),
|
||||
km_3d_view_tool_pose_breakdowner(params),
|
||||
|
|
|
@ -366,6 +366,16 @@ class _defs_transform:
|
|||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def bend():
|
||||
return dict(
|
||||
idname="builtin.bend",
|
||||
label="Bend",
|
||||
icon="ops.gpencil.edit_bend",
|
||||
widget=None,
|
||||
keymap="3D View Tool: Bend",
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def transform():
|
||||
def draw_settings(context, layout, tool):
|
||||
|
@ -3012,6 +3022,18 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
],
|
||||
'EDIT_GREASE_PENCIL': [
|
||||
*_tools_select,
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
*_tools_transform,
|
||||
None,
|
||||
_defs_edit_curve.curve_radius,
|
||||
_defs_transform.bend,
|
||||
(
|
||||
_defs_transform.shear,
|
||||
_defs_edit_mesh.tosphere,
|
||||
),
|
||||
None,
|
||||
*_tools_annotate,
|
||||
],
|
||||
'PARTICLE': [
|
||||
*_tools_select,
|
||||
|
|
|
@ -1237,10 +1237,10 @@ class VIEW3D_MT_transform(VIEW3D_MT_transform_base, Menu):
|
|||
if context.mode == 'EDIT_MESH':
|
||||
layout.operator("transform.shrink_fatten", text="Shrink/Fatten").alt_navigation = alt_navigation
|
||||
layout.operator("transform.skin_resize")
|
||||
elif context.mode == 'EDIT_CURVE':
|
||||
elif context.mode in ['EDIT_CURVE', 'EDIT_GREASE_PENCIL']:
|
||||
layout.operator("transform.transform", text="Radius").mode = 'CURVE_SHRINKFATTEN'
|
||||
|
||||
if context.mode != 'EDIT_CURVES':
|
||||
if context.mode != 'EDIT_CURVES' and context.mode != 'EDIT_GREASE_PENCIL':
|
||||
layout.separator()
|
||||
props = layout.operator("transform.translate", text="Move Texture Space")
|
||||
props.texture_space = True
|
||||
|
@ -5805,6 +5805,11 @@ class VIEW3D_MT_edit_greasepencil(Menu):
|
|||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
layout.menu("VIEW3D_MT_transform")
|
||||
layout.menu("VIEW3D_MT_mirror")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_edit_greasepencil_delete")
|
||||
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
|
|||
const Object &ob_orig);
|
||||
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval,
|
||||
const Object &ob_orig,
|
||||
int drawing_index);
|
||||
int layer_index);
|
||||
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Depsgraph &depsgraph,
|
||||
const Object &ob_orig,
|
||||
int layer_index);
|
||||
|
||||
} // namespace blender::bke::crazyspace
|
||||
|
|
|
@ -740,4 +740,12 @@ GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object
|
|||
return deformation;
|
||||
}
|
||||
|
||||
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Depsgraph &depsgraph,
|
||||
const Object &ob_orig,
|
||||
const int layer_index)
|
||||
{
|
||||
const Object *ob_eval = DEG_get_evaluated_object(&depsgraph, const_cast<Object *>(&ob_orig));
|
||||
return get_evaluated_grease_pencil_drawing_deformation(ob_eval, ob_orig, layer_index);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::crazyspace
|
||||
|
|
|
@ -32,6 +32,7 @@ set(SRC
|
|||
transform_convert_curves.cc
|
||||
transform_convert_gpencil_legacy.cc
|
||||
transform_convert_graph.cc
|
||||
transform_convert_grease_pencil.cc
|
||||
transform_convert_lattice.cc
|
||||
transform_convert_mask.cc
|
||||
transform_convert_mball.cc
|
||||
|
|
|
@ -426,11 +426,16 @@ void removeAspectRatio(TransInfo *t, float vec[2])
|
|||
static void viewRedrawForce(const bContext *C, TransInfo *t)
|
||||
{
|
||||
if (t->options & CTX_GPENCIL_STROKES) {
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
if (gpd) {
|
||||
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
|
||||
if (t->obedit_type == OB_GREASE_PENCIL) {
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, nullptr);
|
||||
}
|
||||
else if (t->obedit_type == OB_GPENCIL_LEGACY) {
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
if (gpd) {
|
||||
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
|
||||
}
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
|
||||
}
|
||||
else if (t->spacetype == SPACE_VIEW3D) {
|
||||
if (t->options & CTX_PAINT_CURVE) {
|
||||
|
|
|
@ -718,23 +718,25 @@ static int countAndCleanTransDataContainer(TransInfo *t)
|
|||
static void init_proportional_edit(TransInfo *t)
|
||||
{
|
||||
/* NOTE: Proportional editing is not usable in pose mode yet #32444. */
|
||||
if (!ELEM(t->data_type,
|
||||
&TransConvertType_Action,
|
||||
&TransConvertType_Curve,
|
||||
&TransConvertType_Curves,
|
||||
&TransConvertType_Graph,
|
||||
&TransConvertType_GPencil,
|
||||
&TransConvertType_Lattice,
|
||||
&TransConvertType_Mask,
|
||||
&TransConvertType_MBall,
|
||||
&TransConvertType_Mesh,
|
||||
&TransConvertType_MeshEdge,
|
||||
&TransConvertType_MeshSkin,
|
||||
&TransConvertType_MeshUV,
|
||||
&TransConvertType_MeshVertCData,
|
||||
&TransConvertType_Node,
|
||||
&TransConvertType_Object,
|
||||
&TransConvertType_Particle))
|
||||
/* NOTE: This `ELEM` uses more than 16 elements and so has been split. */
|
||||
if (!(ELEM(t->data_type,
|
||||
&TransConvertType_Action,
|
||||
&TransConvertType_Curve,
|
||||
&TransConvertType_Curves,
|
||||
&TransConvertType_Graph,
|
||||
&TransConvertType_GPencil,
|
||||
&TransConvertType_GreasePencil,
|
||||
&TransConvertType_Lattice,
|
||||
&TransConvertType_Mask,
|
||||
&TransConvertType_MBall,
|
||||
&TransConvertType_Mesh,
|
||||
&TransConvertType_MeshEdge,
|
||||
&TransConvertType_MeshSkin,
|
||||
&TransConvertType_MeshUV,
|
||||
&TransConvertType_MeshVertCData,
|
||||
&TransConvertType_Node,
|
||||
&TransConvertType_Object) ||
|
||||
ELEM(t->data_type, &TransConvertType_Particle)))
|
||||
{
|
||||
/* Disable proportional editing */
|
||||
t->options |= CTX_NO_PET;
|
||||
|
@ -797,6 +799,7 @@ static void init_TransDataContainers(TransInfo *t,
|
|||
&TransConvertType_Curve,
|
||||
&TransConvertType_Curves,
|
||||
&TransConvertType_GPencil,
|
||||
&TransConvertType_GreasePencil,
|
||||
&TransConvertType_Lattice,
|
||||
&TransConvertType_MBall,
|
||||
&TransConvertType_Mesh,
|
||||
|
@ -813,6 +816,7 @@ static void init_TransDataContainers(TransInfo *t,
|
|||
const short object_type = obact ? obact->type : -1;
|
||||
|
||||
if ((object_mode & OB_MODE_EDIT) || (t->data_type == &TransConvertType_GPencil) ||
|
||||
(t->data_type == &TransConvertType_GreasePencil) ||
|
||||
((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE)))
|
||||
{
|
||||
if (t->data_container) {
|
||||
|
@ -860,6 +864,9 @@ static void init_TransDataContainers(TransInfo *t,
|
|||
else if (t->data_type == &TransConvertType_GPencil) {
|
||||
tc->use_local_mat = true;
|
||||
}
|
||||
else if (t->data_type == &TransConvertType_GreasePencil) {
|
||||
tc->use_local_mat = true;
|
||||
}
|
||||
|
||||
if (tc->use_local_mat) {
|
||||
BLI_assert((t->flag & T_2D_EDIT) == 0);
|
||||
|
@ -910,7 +917,13 @@ static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj
|
|||
return &TransConvertType_MeshEdge;
|
||||
}
|
||||
if (t->options & CTX_GPENCIL_STROKES) {
|
||||
return &TransConvertType_GPencil;
|
||||
if (t->obedit_type == OB_GREASE_PENCIL) {
|
||||
return &TransConvertType_GreasePencil;
|
||||
}
|
||||
else if (t->obedit_type == OB_GPENCIL_LEGACY) {
|
||||
return &TransConvertType_GPencil;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (t->spacetype == SPACE_IMAGE) {
|
||||
if (t->options & CTX_MASK) {
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
struct BMEditMesh;
|
||||
struct BMesh;
|
||||
struct BezTriple;
|
||||
|
@ -106,6 +110,19 @@ void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc);
|
|||
*/
|
||||
void animrecord_check_state(TransInfo *t, ID *id);
|
||||
|
||||
/* `transform_convert_curves.cc` */
|
||||
|
||||
/**
|
||||
* Used for both curves and grease pencil objects.
|
||||
*/
|
||||
void curve_populate_trans_data_structs(TransDataContainer &tc,
|
||||
blender::bke::CurvesGeometry &curves,
|
||||
std::optional<blender::MutableSpan<float>> value_attribute,
|
||||
const blender::IndexMask &selected_indices,
|
||||
bool use_proportional_edit,
|
||||
bool use_connected_only,
|
||||
int trans_data_offset);
|
||||
|
||||
/* `transform_convert_action.cc` */
|
||||
|
||||
extern TransConvertTypeInfo TransConvertType_Action;
|
||||
|
@ -131,7 +148,7 @@ extern TransConvertTypeInfo TransConvertType_Cursor3D;
|
|||
|
||||
extern TransConvertTypeInfo TransConvertType_Curve;
|
||||
|
||||
/* transform_convert_curves.cc */
|
||||
/* `transform_convert_curves.cc` */
|
||||
|
||||
extern TransConvertTypeInfo TransConvertType_Curves;
|
||||
|
||||
|
@ -143,6 +160,10 @@ extern TransConvertTypeInfo TransConvertType_Graph;
|
|||
|
||||
extern TransConvertTypeInfo TransConvertType_GPencil;
|
||||
|
||||
/* `transform_convert_greasepencil.cc` */
|
||||
|
||||
extern TransConvertTypeInfo TransConvertType_GreasePencil;
|
||||
|
||||
/* `transform_convert_lattice.cc` */
|
||||
|
||||
extern TransConvertTypeInfo TransConvertType_Lattice;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* \ingroup edtransform
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_inplace_priority_queue.hh"
|
||||
#include "BLI_math_matrix.h"
|
||||
|
@ -95,82 +97,14 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
|
|||
Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
|
||||
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
|
||||
|
||||
float mtx[3][3], smtx[3][3];
|
||||
copy_m3_m4(mtx, tc.obedit->object_to_world);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
if (use_proportional_edit) {
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
|
||||
".selection", ATTR_DOMAIN_POINT, true);
|
||||
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
|
||||
Vector<float> closest_distances;
|
||||
for (const int curve_i : range) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const bool has_any_selected = ed::curves::has_anything_selected(selection, points);
|
||||
if (!has_any_selected && use_connected_only) {
|
||||
for (const int point_i : points) {
|
||||
TransData &td = tc.data[point_i];
|
||||
td.flag |= TD_SKIP;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
closest_distances.reinitialize(points.size());
|
||||
closest_distances.fill(std::numeric_limits<float>::max());
|
||||
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
const int point_i = points[i];
|
||||
TransData &td = tc.data[point_i];
|
||||
float3 *elem = &positions[point_i];
|
||||
|
||||
copy_v3_v3(td.iloc, *elem);
|
||||
copy_v3_v3(td.center, td.iloc);
|
||||
td.loc = *elem;
|
||||
|
||||
td.flag = 0;
|
||||
if (selection[point_i]) {
|
||||
closest_distances[i] = 0.0f;
|
||||
td.flag = TD_SELECTED;
|
||||
}
|
||||
|
||||
td.ext = nullptr;
|
||||
|
||||
copy_m3_m3(td.smtx, smtx);
|
||||
copy_m3_m3(td.mtx, mtx);
|
||||
}
|
||||
|
||||
if (use_connected_only) {
|
||||
calculate_curve_point_distances_for_proportional_editing(
|
||||
positions.slice(points), closest_distances.as_mutable_span());
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
TransData &td = tc.data[points[i]];
|
||||
td.dist = closest_distances[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
const IndexMask selected_indices = selection_per_object[i];
|
||||
threading::parallel_for(selected_indices.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int selection_i : range) {
|
||||
TransData *td = &tc.data[selection_i];
|
||||
float3 *elem = &positions[selected_indices[selection_i]];
|
||||
|
||||
copy_v3_v3(td->iloc, *elem);
|
||||
copy_v3_v3(td->center, td->iloc);
|
||||
td->loc = *elem;
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
td->ext = nullptr;
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
}
|
||||
});
|
||||
}
|
||||
curve_populate_trans_data_structs(
|
||||
tc,
|
||||
curves,
|
||||
{} /* Currently no transform for attributes other than position. */,
|
||||
selection_per_object[i],
|
||||
use_proportional_edit,
|
||||
use_connected_only,
|
||||
0 /* No data offset for curves. */);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,6 +123,106 @@ static void recalcData_curves(TransInfo *t)
|
|||
|
||||
} // namespace blender::ed::transform::curves
|
||||
|
||||
void curve_populate_trans_data_structs(TransDataContainer &tc,
|
||||
blender::bke::CurvesGeometry &curves,
|
||||
std::optional<blender::MutableSpan<float>> value_attribute,
|
||||
const blender::IndexMask &selected_indices,
|
||||
bool use_proportional_edit,
|
||||
bool use_connected_only,
|
||||
int trans_data_offset)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
float mtx[3][3], smtx[3][3];
|
||||
copy_m3_m4(mtx, tc.obedit->object_to_world);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
MutableSpan<float3> positions = curves.positions_for_write();
|
||||
if (use_proportional_edit) {
|
||||
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
|
||||
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
|
||||
".selection", ATTR_DOMAIN_POINT, true);
|
||||
threading::parallel_for(curves.curves_range(), 512, [&](const IndexRange range) {
|
||||
Vector<float> closest_distances;
|
||||
for (const int curve_i : range) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const bool has_any_selected = ed::curves::has_anything_selected(selection, points);
|
||||
if (!has_any_selected && use_connected_only) {
|
||||
for (const int point_i : points) {
|
||||
TransData &td = tc.data[point_i + trans_data_offset];
|
||||
td.flag |= TD_SKIP;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
closest_distances.reinitialize(points.size());
|
||||
closest_distances.fill(std::numeric_limits<float>::max());
|
||||
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
const int point_i = points[i];
|
||||
TransData &td = tc.data[point_i + trans_data_offset];
|
||||
float3 *elem = &positions[point_i];
|
||||
|
||||
copy_v3_v3(td.iloc, *elem);
|
||||
copy_v3_v3(td.center, td.iloc);
|
||||
td.loc = *elem;
|
||||
|
||||
td.flag = 0;
|
||||
if (selection[point_i]) {
|
||||
closest_distances[i] = 0.0f;
|
||||
td.flag = TD_SELECTED;
|
||||
}
|
||||
|
||||
if (value_attribute) {
|
||||
float *value = &((*value_attribute)[point_i]);
|
||||
td.val = value;
|
||||
td.ival = *value;
|
||||
}
|
||||
|
||||
td.ext = nullptr;
|
||||
|
||||
copy_m3_m3(td.smtx, smtx);
|
||||
copy_m3_m3(td.mtx, mtx);
|
||||
}
|
||||
|
||||
if (use_connected_only) {
|
||||
blender::ed::transform::curves::calculate_curve_point_distances_for_proportional_editing(
|
||||
positions.slice(points), closest_distances.as_mutable_span());
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
TransData &td = tc.data[points[i] + trans_data_offset];
|
||||
td.dist = closest_distances[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(selected_indices.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int selection_i : range) {
|
||||
TransData *td = &tc.data[selection_i + trans_data_offset];
|
||||
const int point_i = selected_indices[selection_i];
|
||||
float3 *elem = &positions[point_i];
|
||||
|
||||
copy_v3_v3(td->iloc, *elem);
|
||||
copy_v3_v3(td->center, td->iloc);
|
||||
td->loc = *elem;
|
||||
|
||||
if (value_attribute) {
|
||||
float *value = &((*value_attribute)[point_i]);
|
||||
td->val = value;
|
||||
td->ival = *value;
|
||||
}
|
||||
|
||||
td->flag = TD_SELECTED;
|
||||
td->ext = nullptr;
|
||||
|
||||
copy_m3_m3(td->smtx, smtx);
|
||||
copy_m3_m3(td->mtx, mtx);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
TransConvertTypeInfo TransConvertType_Curves = {
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edtransform
|
||||
*/
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "ED_curves.hh"
|
||||
#include "ED_grease_pencil.hh"
|
||||
|
||||
#include "transform.hh"
|
||||
#include "transform_convert.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Grease Pencil Transform Creation
|
||||
* \{ */
|
||||
|
||||
namespace blender::ed::transform::greasepencil {
|
||||
|
||||
static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
MutableSpan<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
|
||||
IndexMaskMemory memory;
|
||||
Array<IndexMask> selection_per_layer_per_object(t->data_container_len);
|
||||
const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0;
|
||||
const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0;
|
||||
int layer_offset = 0;
|
||||
|
||||
/* Count selected elements per layer per object and create TransData structs. */
|
||||
for (const int i : trans_data_contrainers.index_range()) {
|
||||
TransDataContainer &tc = trans_data_contrainers[i];
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
|
||||
|
||||
grease_pencil.foreach_editable_drawing(
|
||||
scene->r.cfra, [&](int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
|
||||
const bke::CurvesGeometry &curves = drawing.strokes();
|
||||
|
||||
if (use_proportional_edit) {
|
||||
tc.data_len += curves.point_num;
|
||||
}
|
||||
else {
|
||||
selection_per_layer_per_object[i + layer_offset] =
|
||||
ed::curves::retrieve_selected_points(curves, memory);
|
||||
tc.data_len += selection_per_layer_per_object[i + layer_offset].size();
|
||||
}
|
||||
|
||||
layer_offset++;
|
||||
});
|
||||
|
||||
if (tc.data_len > 0) {
|
||||
tc.data = MEM_cnew_array<TransData>(tc.data_len, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reuse the variable `layer_offset` */
|
||||
layer_offset = 0;
|
||||
|
||||
/* Populate TransData structs. */
|
||||
for (const int i : trans_data_contrainers.index_range()) {
|
||||
TransDataContainer &tc = trans_data_contrainers[i];
|
||||
if (tc.data_len == 0) {
|
||||
continue;
|
||||
}
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
|
||||
|
||||
float mtx[3][3], smtx[3][3];
|
||||
copy_m3_m4(mtx, tc.obedit->object_to_world);
|
||||
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
||||
|
||||
int layer_points_offset = 0;
|
||||
|
||||
grease_pencil.foreach_editable_drawing(
|
||||
scene->r.cfra, [&](int /*layer_index*/, blender::bke::greasepencil::Drawing &drawing) {
|
||||
bke::CurvesGeometry &curves = drawing.strokes_for_write();
|
||||
|
||||
const IndexMask selected_indices = selection_per_layer_per_object[i + layer_offset];
|
||||
|
||||
std::optional<MutableSpan<float>> value_attribute;
|
||||
|
||||
if (t->mode == TFM_CURVE_SHRINKFATTEN) {
|
||||
MutableSpan<float> radii = drawing.radii_for_write();
|
||||
value_attribute = radii;
|
||||
}
|
||||
else if (t->mode == TFM_GPENCIL_OPACITY) {
|
||||
MutableSpan<float> opacities = drawing.opacities_for_write();
|
||||
value_attribute = opacities;
|
||||
}
|
||||
|
||||
curve_populate_trans_data_structs(tc,
|
||||
curves,
|
||||
value_attribute,
|
||||
selected_indices,
|
||||
use_proportional_edit,
|
||||
use_connected_only,
|
||||
layer_points_offset);
|
||||
|
||||
if (use_proportional_edit) {
|
||||
layer_points_offset += curves.points_num();
|
||||
}
|
||||
else {
|
||||
layer_points_offset += selected_indices.size();
|
||||
}
|
||||
layer_offset++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void recalcData_grease_pencil(TransInfo *t)
|
||||
{
|
||||
bContext *C = t->context;
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
const Span<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
|
||||
for (const TransDataContainer &tc : trans_data_contrainers) {
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
|
||||
|
||||
grease_pencil.foreach_editable_drawing(
|
||||
scene->r.cfra, [&](int /*layer_index*/, bke::greasepencil::Drawing &drawing) {
|
||||
bke::CurvesGeometry &curves = drawing.strokes_for_write();
|
||||
|
||||
curves.calculate_bezier_auto_handles();
|
||||
curves.tag_positions_changed();
|
||||
drawing.tag_positions_changed();
|
||||
});
|
||||
|
||||
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::ed::transform::greasepencil
|
||||
|
||||
/** \} */
|
||||
|
||||
TransConvertTypeInfo TransConvertType_GreasePencil = {
|
||||
/*flags*/ (T_EDIT | T_POINTS),
|
||||
/*create_trans_data*/ blender::ed::transform::greasepencil::createTransGreasePencilVerts,
|
||||
/*recalc_data*/ blender::ed::transform::greasepencil::recalcData_grease_pencil,
|
||||
/*special_aftertrans_update*/ nullptr,
|
||||
};
|
|
@ -230,6 +230,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
|
|||
t->options |= CTX_GPENCIL_STROKES;
|
||||
}
|
||||
|
||||
/* Grease Pencil editing context */
|
||||
if (t->obedit_type == OB_GREASE_PENCIL && object_mode == OB_MODE_EDIT) {
|
||||
t->options |= CTX_GPENCIL_STROKES;
|
||||
}
|
||||
|
||||
/* Assign the space type, some exceptions for running in different mode */
|
||||
if (area == nullptr) {
|
||||
/* background mode */
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Used for 3D View
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
|
@ -26,12 +27,12 @@
|
|||
#include "BKE_editmesh.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil_legacy.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_pointcache.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_message.hh"
|
||||
|
@ -41,6 +42,7 @@
|
|||
#include "ED_gizmo_library.hh"
|
||||
#include "ED_gizmo_utils.hh"
|
||||
#include "ED_gpencil_legacy.hh"
|
||||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_particle.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
@ -807,6 +809,35 @@ static int gizmo_3d_foreach_selected(const bContext *C,
|
|||
}
|
||||
FOREACH_EDIT_OBJECT_END();
|
||||
}
|
||||
else if (obedit->type == OB_GREASE_PENCIL) {
|
||||
FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) {
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_iter->data);
|
||||
|
||||
float4x4 mat_local;
|
||||
if (use_mat_local) {
|
||||
mat_local = float4x4(obedit->world_to_object) * float4x4(ob_iter->object_to_world);
|
||||
}
|
||||
|
||||
grease_pencil.foreach_editable_drawing(
|
||||
scene->r.cfra, [&](int layer_index, blender::bke::greasepencil::Drawing &drawing) {
|
||||
const bke::CurvesGeometry &curves = drawing.strokes();
|
||||
|
||||
const bke::crazyspace::GeometryDeformation deformation =
|
||||
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
|
||||
*depsgraph, *ob, layer_index);
|
||||
|
||||
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
|
||||
|
|
|
@ -566,11 +566,17 @@ void ElementRotation_ex(const TransInfo *t,
|
|||
|
||||
/* Apply gpencil falloff. */
|
||||
if (t->options & CTX_GPENCIL_STROKES) {
|
||||
bGPDstroke *gps = (bGPDstroke *)td->extra;
|
||||
if (gps->runtime.multi_frame_falloff != 1.0f) {
|
||||
float ident_mat[3][3];
|
||||
unit_m3(ident_mat);
|
||||
interp_m3_m3m3(smat, ident_mat, smat, gps->runtime.multi_frame_falloff);
|
||||
if (t->obedit_type == OB_GPENCIL_LEGACY) {
|
||||
|
||||
bGPDstroke *gps = (bGPDstroke *)td->extra;
|
||||
if (gps->runtime.multi_frame_falloff != 1.0f) {
|
||||
float ident_mat[3][3];
|
||||
unit_m3(ident_mat);
|
||||
interp_m3_m3m3(smat, ident_mat, smat, gps->runtime.multi_frame_falloff);
|
||||
}
|
||||
}
|
||||
else if (t->obedit_type == OB_GREASE_PENCIL) {
|
||||
/* pass */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1030,21 +1036,26 @@ void ElementResize(const TransInfo *t,
|
|||
* Operating on copies as a temporary solution.
|
||||
*/
|
||||
if (t->options & CTX_GPENCIL_STROKES) {
|
||||
bGPDstroke *gps = (bGPDstroke *)td->extra;
|
||||
mul_v3_fl(vec, td->factor * gps->runtime.multi_frame_falloff);
|
||||
if (t->obedit_type == OB_GPENCIL_LEGACY) {
|
||||
bGPDstroke *gps = (bGPDstroke *)td->extra;
|
||||
mul_v3_fl(vec, td->factor * gps->runtime.multi_frame_falloff);
|
||||
|
||||
/* Scale stroke thickness. */
|
||||
if (td->val) {
|
||||
NumInput num_evil = t->num;
|
||||
float values_final_evil[4];
|
||||
copy_v4_v4(values_final_evil, t->values_final);
|
||||
transform_snap_increment(t, values_final_evil);
|
||||
applyNumInput(&num_evil, values_final_evil);
|
||||
/* Scale stroke thickness. */
|
||||
if (td->val) {
|
||||
NumInput num_evil = t->num;
|
||||
float values_final_evil[4];
|
||||
copy_v4_v4(values_final_evil, t->values_final);
|
||||
transform_snap_increment(t, values_final_evil);
|
||||
applyNumInput(&num_evil, values_final_evil);
|
||||
|
||||
float ratio = values_final_evil[0];
|
||||
float transformed_value = td->ival * fabs(ratio);
|
||||
*td->val = max_ff(interpf(transformed_value, td->ival, gps->runtime.multi_frame_falloff),
|
||||
0.001f);
|
||||
float ratio = values_final_evil[0];
|
||||
float transformed_value = td->ival * fabs(ratio);
|
||||
*td->val = max_ff(interpf(transformed_value, td->ival, gps->runtime.multi_frame_falloff),
|
||||
0.001f);
|
||||
}
|
||||
}
|
||||
else if (t->obedit_type == OB_GREASE_PENCIL) {
|
||||
mul_v3_fl(vec, td->factor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -60,10 +60,16 @@ static void applyGPOpacity(TransInfo *t)
|
|||
bool recalc = false;
|
||||
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
||||
TransData *td = tc->data;
|
||||
bGPdata *gpd = static_cast<bGPdata *>(td->ob->data);
|
||||
const bool is_curve_edit = bool(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
|
||||
/* Only recalculate data when in curve edit mode. */
|
||||
if (is_curve_edit) {
|
||||
|
||||
if (t->obedit_type == OB_GPENCIL_LEGACY) {
|
||||
bGPdata *gpd = static_cast<bGPdata *>(td->ob->data);
|
||||
const bool is_curve_edit = bool(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
|
||||
/* Only recalculate data when in curve edit mode. */
|
||||
if (is_curve_edit) {
|
||||
recalc = true;
|
||||
}
|
||||
}
|
||||
else if (t->obedit_type == OB_GREASE_PENCIL) {
|
||||
recalc = true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue