GPv3: Transform operators (translate, rotate, scale, opacity, radius) #111836
@ -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",
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
|
||||
)
|
||||
|
||||
@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);
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
This changed to the This changed to the `layer_index` in `main` so I guess a merge is needed.
|
||||
|
||||
} // 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
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
`greasepencil` -> `grease_pencil`
|
||||
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,
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
`curves_populate_trans_data_structs`
|
||||
blender::bke::CurvesGeometry &curves,
|
||||
std::optional<blender::MutableSpan<float>> value_attribute,
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Hans Goudey
commented
Typically it's clearer to use Typically it's clearer to use `std::optional` than a pointer for such optional arguments
|
||||
const blender::IndexMask &selected_indices,
|
||||
bool use_proportional_edit,
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
Remove Remove `const` in declaration.
|
||||
bool use_connected_only,
|
||||
int trans_data_offset);
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
`data_offset` -> `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(
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
`/* Currently no transform for attributes other than position. */`
|
||||
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
|
||||
Hans Goudey
commented
Could this be in the Could this be in the `blender::ed::transform::curves` namespace as well?
casey-bianco-davis
commented
I tried moving the code but kept getting error and I don't know c++ good enough to fix them. :| I tried moving the code but kept getting error and I don't know c++ good enough to fix them. :|
Hans Goudey
commented
Okay, that's fine! It can be done later as a more general cleanup Okay, that's fine! It can be done later as a more general cleanup
|
||||
|
||||
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;
|
||||
}
|
||||
Hans Goudey
commented
I'd have to dig into this code more to see how possible this is, but it would be nice to keep this For some background, I'm dreaming of a time where we can just process these arrays separately directly, rather than stuffing them in a I'd have to dig into this code more to see how possible this is, but it would be nice to keep this `value_attribute` in a separate loop from the location. Even moving it to a separate function would be nice if possible.
For some background, I'm dreaming of a time where we can just process these arrays separately directly, rather than stuffing them in a `TransData` struct.
|
||||
|
||||
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"
|
||||
|
||||
casey-bianco-davis marked this conversation as resolved
Hans Goudey
commented
Unused include, maybe? Unused include, maybe?
|
||||
#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;
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Hans Goudey
commented
- `= {}` is unnecessary, `optional` has a default constructor
- `blender::` is unnecessary here
|
||||
|
||||
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++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Hans Goudey
commented
`curves.points_num()`. Just nicer to user the accessor consistently.
|
||||
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) {
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Hans Goudey
commented
`blender::` is unnecessary here
|
||||
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
|
||||
|
||||
casey-bianco-davis marked this conversation as resolved
Hans Goudey
commented
I think the drawing needs the "positions changed" tag too I think the drawing needs the "positions changed" tag too
Falk David
commented
Yep, otherwise the triangle caches are not tagged as dirty. Yep, otherwise the triangle caches are not tagged as dirty.
|
||||
/** \} */
|
||||
|
||||
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) {
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
`layer_index` (same for other places as well)
|
||||
const bke::CurvesGeometry &curves = drawing.strokes();
|
||||
|
||||
const bke::crazyspace::GeometryDeformation deformation =
|
||||
bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
|
||||
*depsgraph, *ob, layer_index);
|
||||
casey-bianco-davis marked this conversation as resolved
Outdated
Falk David
commented
`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;
|
||||
}
|
||||
|
||||
|
This triggers a warning