GPv3: Fill gradient tool

This adds a gradient tool allowing the user to specify a gradient by dragging from a start to a end point.
This behaves the same as most gradient tool in other softwares.

Pull Request: https://projects.blender.org/blender/blender/pulls/120121
This commit is contained in:
casey bianco-davis
2024-09-24 11:25:28 +02:00
committed by Falk David
parent 5eeb5cd164
commit 1b6220a4ba
3 changed files with 209 additions and 0 deletions

View File

@@ -8897,6 +8897,19 @@ def km_3d_view_tool_sculpt_gpencil_select_lasso(params):
{"items": _template_items_tool_select_actions("gpencil.select_lasso", **params.tool_tweak_event)},
)
# ------------------------------------------------------------------------------
# Grease Pencil: Texture Gradient Tool
def km_3d_view_tool_edit_grease_pencil_texture_gradient(params):
return (
"3D View Tool: Edit Grease Pencil, Gradient",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("grease_pencil.texture_gradient", params.tool_maybe_tweak_event, None),
]},
)
# ------------------------------------------------------------------------------
# Tool System (Sequencer, Generic)
@@ -9382,6 +9395,7 @@ def generate_keymaps(params=None):
*(km_sequencer_editor_tool_generic_select_box_preview(params, fallback=fallback)
for fallback in (False, True)),
km_3d_view_tool_paint_grease_pencil_trim(params),
km_3d_view_tool_edit_grease_pencil_texture_gradient(params),
km_sequencer_editor_tool_generic_cursor(params),
km_sequencer_editor_tool_blade(params),
km_sequencer_editor_tool_sample(params),

View File

@@ -2213,6 +2213,16 @@ class _defs_grease_pencil_edit:
draw_settings=draw_settings,
)
@ToolDef.from_fn
def texture_gradient():
return dict(
idname="builtin.texture_gradient",
label="Gradient",
icon="ops.paint.weight_gradient",
widget=None,
keymap=(),
)
class _defs_image_generic:
@@ -3553,6 +3563,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_grease_pencil_edit.interpolate,
None,
_defs_grease_pencil_edit.texture_gradient,
None,
*_tools_annotate,
],
'PARTICLE': [

View File

@@ -3142,6 +3142,188 @@ static void GREASE_PENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static float4x3 expand_4x2_mat(float4x2 strokemat)
{
float4x3 strokemat4x3 = float4x3(strokemat);
/*
* We need the diagonal of ones to start from the bottom right instead top left to properly
* apply the two matrices.
*
* i.e.
* # # # # # # # #
* We need # # # # Instead of # # # #
* 0 0 0 1 0 0 1 0
*
*/
strokemat4x3[2][2] = 0.0f;
strokemat4x3[3][2] = 1.0f;
return strokemat4x3;
}
static int grease_pencil_texture_gradient_exec(bContext *C, wmOperator *op)
{
const Scene *scene = CTX_data_scene(C);
Object *object = CTX_data_active_object(C);
ARegion *region = CTX_wm_region(C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
std::atomic<bool> changed = false;
const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
IndexMaskMemory memory;
const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
*object, info.drawing, info.layer_index, memory);
if (strokes.is_empty()) {
return;
}
const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
const float4x4 layer_space_to_world_space = layer.to_world_space(*object);
/* Calculate screen space points. */
const float2 screen_start(RNA_int_get(op->ptr, "xstart"), RNA_int_get(op->ptr, "ystart"));
const float2 screen_end(RNA_int_get(op->ptr, "xend"), RNA_int_get(op->ptr, "yend"));
const float2 screen_direction = screen_end - screen_start;
const float2 screen_tangent = screen_start + float2(-screen_direction[1], screen_direction[0]);
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const Span<float3> positions = curves.positions();
const Span<float3> normals = info.drawing.curve_plane_normals();
const VArray<int> materials = *curves.attributes().lookup_or_default<int>(
"material_index", bke::AttrDomain::Curve, 0);
Array<float4x2> texture_matrices(strokes.size());
strokes.foreach_index([&](const int curve_i, const int pos) {
const int material_index = materials[curve_i];
const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(object,
material_index + 1);
const bool is_radial = gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL;
const float texture_angle = gp_style->texture_angle;
const float2 texture_scale = float2(gp_style->texture_scale);
const float2 texture_offset = float2(gp_style->texture_offset);
const float2x2 texture_rotation = math::from_rotation<float2x2>(
math::AngleRadian(texture_angle));
const float3 point = math::transform_point(layer_space_to_world_space,
positions[points_by_curve[curve_i].first()]);
const float3 normal = math::transform_direction(layer_space_to_world_space,
normals[curve_i]);
const float4 plane = float4(normal, -math::dot(normal, point));
float3 start;
float3 tangent;
float3 end;
ED_view3d_win_to_3d_on_plane(region, plane, screen_start, false, start);
ED_view3d_win_to_3d_on_plane(region, plane, screen_tangent, false, tangent);
ED_view3d_win_to_3d_on_plane(region, plane, screen_end, false, end);
const float3 origin = start;
/* Invert the length by dividing by the length squared. */
const float3 u_dir = (end - origin) / math::length_squared(end - origin);
float3 v_dir = math::cross(u_dir, normal);
/* Flip the texture if need so that it is not mirrored. */
if (math::dot(tangent - start, v_dir) < 0.0f) {
v_dir = -v_dir;
}
/* Calculate the texture space before the texture offset transformation. */
const float4x2 base_texture_space = math::transpose(float2x4(
float4(u_dir, -math::dot(u_dir, origin)), float4(v_dir, -math::dot(v_dir, origin))));
float3x2 offset_matrix = float3x2::identity();
if (is_radial) {
/* Radial gradients are scaled down by a factor of 2 and have the center at 0.5 */
offset_matrix *= 0.5f;
offset_matrix[2] += float2(0.5f, 0.5f);
}
/* For some reason 0.5 is added to the offset before being rendered, so remove it here. */
offset_matrix[2] -= float2(0.5f, 0.5f);
offset_matrix = math::from_scale<float2x2>(texture_scale) * offset_matrix;
offset_matrix = texture_rotation * offset_matrix;
offset_matrix[2] -= texture_offset;
texture_matrices[pos] = (offset_matrix * expand_4x2_mat(base_texture_space)) *
layer_space_to_world_space;
});
info.drawing.set_texture_matrices(texture_matrices, strokes);
changed.store(true, std::memory_order_relaxed);
});
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
}
return OPERATOR_RUNNING_MODAL;
}
static int grease_pencil_texture_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
int ret = WM_gesture_straightline_modal(C, op, event);
/* Check for mouse release. */
if ((ret & OPERATOR_RUNNING_MODAL) != 0 && event->type == LEFTMOUSE && event->val == KM_RELEASE)
{
WM_gesture_straightline_cancel(C, op);
ret &= ~OPERATOR_RUNNING_MODAL;
ret |= OPERATOR_FINISHED;
}
return ret;
}
static int grease_pencil_texture_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
/* Invoke interactive line drawing (representing the gradient) in viewport. */
const int ret = WM_gesture_straightline_invoke(C, op, event);
if ((ret & OPERATOR_RUNNING_MODAL) != 0) {
ARegion *region = CTX_wm_region(C);
if (region->regiontype == RGN_TYPE_WINDOW && event->type == LEFTMOUSE &&
event->val == KM_PRESS)
{
wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
gesture->is_active = true;
}
}
return ret;
}
static void GREASE_PENCIL_OT_texture_gradient(wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Texture Gradient";
ot->idname = "GREASE_PENCIL_OT_texture_gradient";
ot->description = "Draw a line to set the fill material gradient for the selected strokes";
/* Api callbacks. */
ot->invoke = grease_pencil_texture_gradient_invoke;
ot->modal = grease_pencil_texture_gradient_modal;
ot->exec = grease_pencil_texture_gradient_exec;
ot->poll = editable_grease_pencil_poll;
ot->cancel = WM_gesture_straightline_cancel;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -3484,4 +3666,5 @@ void ED_operatortypes_grease_pencil_edit()
WM_operatortype_append(GREASE_PENCIL_OT_set_curve_resolution);
WM_operatortype_append(GREASE_PENCIL_OT_set_handle_type);
WM_operatortype_append(GREASE_PENCIL_OT_reset_uvs);
WM_operatortype_append(GREASE_PENCIL_OT_texture_gradient);
}