GPv3: Tint tool #119323
|
@ -10,9 +10,11 @@
|
|||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_material.h"
|
||||
|
||||
#include "BLI_lasso_2d.hh"
|
||||
#include "BLI_length_parameterize.hh"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
|
@ -20,7 +22,7 @@
|
|||
#include "ED_grease_pencil.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "GEO_smooth_curves.hh"
|
||||
#include "UI_view2d.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
@ -96,7 +98,7 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
|
|||
}
|
||||
/* Attenuate factor to get a smoother tinting. */
|
||||
strength /= 5.0f;
|
||||
float fill_strength = strength / 10.0f;
|
||||
float fill_strength = strength / 1000.0f;
|
||||
|
||||
strength = math::clamp(strength, 0.0f, 1.0f);
|
||||
fill_strength = math::clamp(fill_strength, 0.0f, 1.0f);
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
|
||||
|
@ -142,32 +144,50 @@ void TintOperation::execute_tint(const bContext &C, const InputSample &extension
|
|||
for (const int curve : range) {
|
||||
bool stroke_touched = false;
|
||||
for (const int curve_point : points_by_curve[curve].index_range()) {
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
Falk David
commented
use use `.x` for `[0]` and `.y` for `[1]`
|
||||
const int point = curve_point + points_by_curve[curve].first();
|
||||
const float distance = math::distance(screen_space_positions[point], mouse_position);
|
||||
const float influence = strength * BKE_brush_curve_strength(brush, distance, radius);
|
||||
if (influence <= 0.0f) {
|
||||
continue;
|
||||
}
|
||||
stroke_touched = true;
|
||||
if (tint_strokes) {
|
||||
vertex_colors[point].premultiply_alpha();
|
||||
float4 rgba = float4(
|
||||
math::interpolate(float3(vertex_colors[point]), float3(this->color_), influence),
|
||||
vertex_colors[point][3]);
|
||||
rgba[3] = rgba[3] * (1.0f - influence) + influence;
|
||||
vertex_colors[point] = ColorGeometry4f(rgba);
|
||||
vertex_colors[point].unpremultiply_alpha();
|
||||
const int point = curve_point + points_by_curve[curve].first();
|
||||
const float distance = math::distance(screen_space_positions[point], mouse_position);
|
||||
const float influence = strength * BKE_brush_curve_strength(brush, distance, radius);
|
||||
if (influence > 0.0f) {
|
||||
stroke_touched = true;
|
||||
vertex_colors[point].premultiply_alpha();
|
||||
float4 rgba = float4(
|
||||
math::interpolate(float3(vertex_colors[point]), float3(this->color_), influence),
|
||||
vertex_colors[point][3]);
|
||||
rgba[3] = rgba[3] * (1.0f - influence) + influence;
|
||||
vertex_colors[point] = ColorGeometry4f(rgba);
|
||||
vertex_colors[point].unpremultiply_alpha();
|
||||
}
|
||||
}
|
||||
if (!fill_colors.span.is_empty() && tint_fills) {
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
Falk David
commented
Why should this check for Why should this check for `stroke_changed` ?
YimingWu
commented
Actually that variable should be assigned outside the vertex tint branch. I'll change it to Actually that variable should be assigned outside the vertex tint branch. I'll change it to `stroke_touched`, since it will only tint the fill when stroke is touched.
Falk David
commented
That doesn't seem right.. The fill should be changed when the brush intersects on of the fill triangles That doesn't seem right.. The fill should be changed when the brush intersects on of the fill triangles
YimingWu
commented
Looks like indeed. I'll try make it to work. Does GPv3 has utility functions that checks on this like GPv2 with pbuffer stuff? Looks like indeed. I'll try make it to work. Does GPv3 has utility functions that checks on this like GPv2 with pbuffer stuff?
Falk David
commented
You can use You can use `isect_point_poly_v2` and pass the 2d points of the stroke into it
YimingWu
commented
Looks like I could use Looks like I could use `BLI_lasso_boundbox` and `BLI_lasso_is_point_inside` like the old tool. Doing that now.
Falk David
commented
Please don't use the Please don't use the `lasso` API 😅 just use `bounds::min_max` and `isect_point_poly_v2` directly (maybe wrapped in some static function).
Sietse Brouwer
commented
`BKE_brush_curve_strength` is using `brush->curve` for the falloff and that is not the curve you want. So here you have to use something like `BKE_curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0.0f, distance / radius)`. But avoid division by zero, of course.
YimingWu
commented
Eh apparently the old tint tool uses Eh apparently the old tint tool uses `BKE_brush_curve_strength`... This indeed appears to be the falloff of the brush tho, I guess `curve_strength` is a different thing that maps pressure with a curve?
Sietse Brouwer
commented
I'm sorry, you're right! I bothered you with wrong comments! So the curve init in And the weird thing is: the I'm sorry, you're right! I bothered you with wrong comments!
It's confusing, because there are 3 falloff curves at play here:
`brush->gpencil_settings->curve_sensitivity`, must be applied to the radius when `BKE_brush_use_size_pressure(brush)` is true.
`brush->gpencil_settings->curve_strength`, must be applied to the strength when `BKE_brush_use_alpha_pressure(brush)` is true (like you have it now).
`brush->curve`, the falloff curve for the influence, used by `BKE_brush_curve_strength()`.
So the curve init in `on_stroke_begin`
`BKE_curvemapping_init(brush->gpencil_settings->curve_strength);`
should be
`BKE_curvemapping_init(brush->curve);`
The other curves are already set in `BKE_brush_init_gpencil_settings()`.
And the weird thing is: the `brush->curve` falloff isn't shown in the UI. So the user can't know what the falloff is, nor adjust it.
I think it would be good to expose that in the toolbar.
Falk David
commented
That seems like a bug yea.. > the `brush->curve` falloff isn't shown in the UI.
That seems like a bug yea..
YimingWu
commented
I see. Thanks for the hint.
Good point. Will fix this.
Right we probably need to get the UI working too. > brush->gpencil_settings->curve_sensitivity, must be applied to the radius when BKE_brush_use_size_pressure(brush) is true.
I see. Thanks for the hint.
> So the curve init in on_stroke_begin
> BKE_curvemapping_init(brush->gpencil_settings->curve_strength);
> should be
> BKE_curvemapping_init(brush->curve);
Good point. Will fix this.
> the brush->curve falloff isn't shown in the UI.
Right we probably need to get the UI working too.
YimingWu
commented
UI is added, falloff/smoothing/jitter also working. UI is added, falloff/smoothing/jitter also working.
|
||||
blender::Array<blender::int2> screen_space_positions_int(
|
||||
points_by_curve[curve].size());
|
||||
for (const int local_point : points_by_curve[curve].index_range()) {
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
Sietse Brouwer
commented
`.premultiply_alpha()` returns the new color, it isn't changing the existing one. So this statement is doing nothing at the moment.
|
||||
screen_space_positions_int[local_point] = int2(
|
||||
screen_space_positions[local_point + points_by_curve[curve].first()]);
|
||||
}
|
||||
rcti rect;
|
||||
BLI_lasso_boundbox(&rect, screen_space_positions_int);
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
Falk David
commented
This shouldn't use the This shouldn't use the `lasso` API. There is also no need to convert to `int2`.
You can use `bounds::min_max(screen_space_positions)` to get the `Bounds<float2>` of the stroke.
And you can write a local helper function to check if the mouse position is inside the stroke/bounding box:
Use `isect_point_poly_v2(pt, reinterpret_cast<const float(*)[2]>(positions.data()), uint(positions.size()))` to test if the point is inside the stroke.
|
||||
/* Will tint fill color when either the brush being inside the fill region or touching
|
||||
* the stroke. */
|
||||
const bool fill_effective =
|
||||
stroke_touched ||
|
||||
(!ELEM(V2D_IS_CLIPPED, mouse_position[0], mouse_position[1]) &&
|
||||
BLI_rcti_isect_pt(&rect, mouse_position[0], mouse_position[1]) &&
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
Sietse Brouwer
commented
Should be outside the Should be outside the `if` block.
Sietse Brouwer
commented
This block should be outside the This block should be outside the `for (const int curve_point...)` loop. Now it is doing the same inside-the-curve check for every point of that curve. That's a bit inefficient 😉 And it has an odd side effect: curves with many points would tint much faster than curves with only a few points...
|
||||
BLI_lasso_is_point_inside(
|
||||
screen_space_positions_int, mouse_position[0], mouse_position[1], INT_MAX));
|
||||
if (fill_effective) {
|
||||
fill_colors.span[curve].premultiply_alpha();
|
||||
float4 rgba = float4(math::interpolate(float3(fill_colors.span[curve]),
|
||||
float3(this->color_),
|
||||
fill_strength),
|
||||
fill_colors.span[curve][3]);
|
||||
rgba[3] = rgba[3] * (1.0f - fill_strength) + fill_strength;
|
||||
fill_colors.span[curve] = ColorGeometry4f(rgba);
|
||||
fill_colors.span[curve].unpremultiply_alpha();
|
||||
ChengduLittleA marked this conversation as resolved
Outdated
Falk David
commented
I think this is not correct. You should pass the layer index, not the drawing index here. Use I think this is not correct. You should pass the layer index, not the drawing index here. Use `grease_pencil.get_layer_index()`
YimingWu
commented
Interesting... In Interesting... In `grease_pencil_erase.cc` line 817 they wrote this way too, then it's probably a mistake as well?
Falk David
commented
It is! Will push a fix. It is! Will push a fix.
Falk David
commented
Fixed for the eraser Fixed for the eraser
|
||||
stroke_touched = true;
|
||||
Sietse Brouwer
commented
Perhaps we should discuss this? I would say this has to take the multi frame editing settings into account. For that, you could use In the UI the switch says Perhaps we should discuss this? I would say this has to take the multi frame editing settings into account. For that, you could use `retrieve_editable_drawings_from_layer()` and apply `execute_tint_on_drawing` to all drawings in the returned vector. Now only the current frame is affected.
In the UI the switch says `Active Layer`, no mention of current frame only. So I would argue it should honour the multi frame editing setting.
YimingWu
commented
@filedescriptor @amelief the eraser tool could use the same change I guess? @filedescriptor @amelief the eraser tool could use the same change I guess?
Falk David
commented
Right I think that was written at a time where muti-frame editing didn't exist. Will look at this separately. Right I think that was written at a time where muti-frame editing didn't exist. Will look at this separately.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!fill_colors.span.is_empty() && stroke_touched && tint_fills) {
|
||||
fill_colors.span[curve].premultiply_alpha();
|
||||
float4 rgba = float4(math::interpolate(float3(fill_colors.span[curve]),
|
||||
float3(this->color_),
|
||||
fill_strength),
|
||||
fill_colors.span[curve][3]);
|
||||
rgba[3] = rgba[3] * (1.0f - fill_strength) + fill_strength;
|
||||
fill_colors.span[curve] = ColorGeometry4f(rgba);
|
||||
fill_colors.span[curve].unpremultiply_alpha();
|
||||
}
|
||||
if (stroke_touched) {
|
||||
changed.store(true, std::memory_order_relaxed);
|
||||
|
|
No need for
(
)
aroundbrush
.A grain size of 128 on drawings isn't very useful (who has more than 128 layers or selected keyframes?). I'd say it's better to have something like you had before:
threading::parallel_for_each(drawings_.index_range(), [&](const int drawing_index) {