Fix: Grease Pencil: Strokes are locked when material index is out-of-range

When the material index of a stroke is out-of-range of the material slots, the
strokes are never editable. This makes it hard to even re-assign the material or
otherwise remove such strokes. A negative index can happen when curves are added
while a Grease Pencil object has no material slots (see #123887).

Since `material_index` is a generic attribute and should be allowed any value,
strokes with out-of-range indices should be considered editable by default.
This patch slightly changes the `get_editable_material_indices` function for
tools, such that only __valid__ materials with the "Locked" flag set are
considered non-editable.

Pull Request: https://projects.blender.org/blender/blender/pulls/132806
This commit is contained in:
Lukas Tönne
2025-01-09 13:34:31 +01:00
parent b4d5e256cb
commit fd83be0fa5

View File

@@ -870,21 +870,21 @@ Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
return visible_drawings;
}
static VectorSet<int> get_editable_material_indices(Object &object)
static VectorSet<int> get_locked_material_indices(Object &object)
{
BLI_assert(object.type == OB_GREASE_PENCIL);
VectorSet<int> editable_material_indices;
VectorSet<int> locked_material_indices;
for (const int mat_i : IndexRange(object.totcol)) {
Material *material = BKE_object_material_get(&object, mat_i + 1);
/* The editable materials are unlocked and not hidden. */
if (material != nullptr && material->gp_style != nullptr &&
(material->gp_style->flag & GP_MATERIAL_LOCKED) == 0 &&
(material->gp_style->flag & GP_MATERIAL_HIDE) == 0)
((material->gp_style->flag & GP_MATERIAL_LOCKED) != 0 ||
(material->gp_style->flag & GP_MATERIAL_HIDE) != 0))
{
editable_material_indices.add_new(mat_i);
locked_material_indices.add_new(mat_i);
}
}
return editable_material_indices;
return locked_material_indices;
}
static VectorSet<int> get_hidden_material_indices(Object &object)
@@ -939,24 +939,24 @@ IndexMask retrieve_editable_strokes(Object &object,
}
/* Get all the editable material indices */
VectorSet<int> editable_material_indices = get_editable_material_indices(object);
if (editable_material_indices.is_empty()) {
return {};
VectorSet<int> locked_material_indices = get_locked_material_indices(object);
if (locked_material_indices.is_empty()) {
return curves_range;
}
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
if (!materials) {
/* If the attribute does not exist then the default is the first material. */
if (editable_material_indices.contains(0)) {
return curves_range;
if (locked_material_indices.contains(0)) {
return {};
}
return {};
return curves_range;
}
/* Get all the strokes that have their material unlocked. */
return IndexMask::from_predicate(
curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
return editable_material_indices.contains(materials[curve_i]);
return !locked_material_indices.contains(materials[curve_i]);
});
}
@@ -1000,30 +1000,31 @@ IndexMask retrieve_editable_strokes_by_material(Object &object,
{
using namespace blender;
const bke::CurvesGeometry &curves = drawing.strokes();
const IndexRange curves_range = curves.curves_range();
/* Get all the editable material indices */
VectorSet<int> editable_material_indices = get_editable_material_indices(object);
if (editable_material_indices.is_empty()) {
return {};
VectorSet<int> locked_material_indices = get_locked_material_indices(object);
if (locked_material_indices.is_empty()) {
return curves_range;
}
const bke::CurvesGeometry &curves = drawing.strokes();
const IndexRange curves_range = drawing.strokes().curves_range();
const bke::AttributeAccessor attributes = curves.attributes();
const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
if (!materials) {
/* If the attribute does not exist then the default is the first material. */
if (editable_material_indices.contains(0)) {
return curves_range;
if (locked_material_indices.contains(0)) {
return {};
}
return {};
return curves_range;
}
/* Get all the strokes that share the same material and have it unlocked. */
return IndexMask::from_predicate(
curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
const int material_index = materials[curve_i];
if (material_index == mat_i) {
return editable_material_indices.contains(material_index);
return !locked_material_indices.contains(material_index);
}
return false;
});
@@ -1050,9 +1051,9 @@ IndexMask retrieve_editable_points(Object &object,
}
/* Get all the editable material indices */
VectorSet<int> editable_material_indices = get_editable_material_indices(object);
if (editable_material_indices.is_empty()) {
return {};
VectorSet<int> locked_material_indices = get_locked_material_indices(object);
if (locked_material_indices.is_empty()) {
return points_range;
}
/* Propagate the material index to the points. */
@@ -1060,15 +1061,15 @@ IndexMask retrieve_editable_points(Object &object,
const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Point);
if (!materials) {
/* If the attribute does not exist then the default is the first material. */
if (editable_material_indices.contains(0)) {
return points_range;
if (locked_material_indices.contains(0)) {
return {};
}
return {};
return points_range;
}
/* Get all the points that are part of a stroke with an unlocked material. */
return IndexMask::from_predicate(
points_range, GrainSize(4096), memory, [&](const int64_t point_i) {
return editable_material_indices.contains(materials[point_i]);
return !locked_material_indices.contains(materials[point_i]);
});
}