GPv3: Overlay: Only show visible and editable materials

This PR makes it so that locked materials as well as hidden materials will not have their edit points and edit lines visible.

Note: Previously in grease pencil, strokes with hidden materials would still display the edit lines. This behavior is now fixed in GPv3.

Pull Request: https://projects.blender.org/blender/blender/pulls/115740
This commit is contained in:
casey bianco-davis
2023-12-08 10:38:12 +01:00
committed by Falk David
parent 9ddd5126fe
commit 58041799bc
5 changed files with 237 additions and 128 deletions

View File

@@ -222,6 +222,7 @@ void invert_booleans(MutableSpan<bool> span);
void invert_booleans(MutableSpan<bool> span, const IndexMask &mask);
int64_t count_booleans(const VArray<bool> &varray);
int64_t count_booleans(const VArray<bool> &varray, const IndexMask &mask);
enum class BooleanMix {
None,

View File

@@ -141,40 +141,59 @@ BooleanMix booleans_mix_calc(const VArray<bool> &varray, const IndexRange range_
[&](BooleanMix a, BooleanMix b) { return (a == b) ? a : BooleanMix::Mixed; });
}
int64_t count_booleans(const VArray<bool> &varray)
int64_t count_booleans(const VArray<bool> &varray, const IndexMask &mask)
{
if (varray.is_empty()) {
if (varray.is_empty() || mask.is_empty()) {
return 0;
}
const CommonVArrayInfo info = varray.common_info();
if (info.type == CommonVArrayInfo::Type::Single) {
return *static_cast<const bool *>(info.data) ? varray.size() : 0;
}
if (info.type == CommonVArrayInfo::Type::Span) {
const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
/* Check if mask is full. */
if (varray.size() == mask.size()) {
const CommonVArrayInfo info = varray.common_info();
if (info.type == CommonVArrayInfo::Type::Single) {
return *static_cast<const bool *>(info.data) ? varray.size() : 0;
}
if (info.type == CommonVArrayInfo::Type::Span) {
const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
return threading::parallel_reduce(
varray.index_range(),
4096,
0,
[&](const IndexRange range, const int64_t init) {
const Span<bool> slice = span.slice(range);
return init + std::count(slice.begin(), slice.end(), true);
},
std::plus<int64_t>());
}
return threading::parallel_reduce(
varray.index_range(),
4096,
2048,
0,
[&](const IndexRange range, const int64_t init) {
const Span<bool> slice = span.slice(range);
return init + std::count(slice.begin(), slice.end(), true);
int64_t value = init;
/* Alternatively, this could use #materialize to retrieve many values at once. */
for (const int64_t i : range) {
value += int64_t(varray[i]);
}
return value;
},
std::plus<int64_t>());
}
return threading::parallel_reduce(
varray.index_range(),
2048,
0,
[&](const IndexRange range, const int64_t init) {
int64_t value = init;
/* Alternatively, this could use #materialize to retrieve many values at once. */
for (const int64_t i : range) {
value += int64_t(varray[i]);
}
return value;
},
std::plus<int64_t>());
const CommonVArrayInfo info = varray.common_info();
if (info.type == CommonVArrayInfo::Type::Single) {
return *static_cast<const bool *>(info.data) ? mask.size() : 0;
}
int64_t value = 0;
mask.foreach_segment([&](const IndexMaskSegment segment) {
for (const int64_t i : segment) {
value += int64_t(varray[i]);
}
});
return value;
}
int64_t count_booleans(const VArray<bool> &varray)
{
return count_booleans(varray, IndexMask(varray.size()));
}
} // namespace blender::array_utils

View File

@@ -198,7 +198,9 @@ BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float har
return packed;
}
static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, const Scene &scene)
static void grease_pencil_edit_batch_ensure(Object &object,
const GreasePencil &grease_pencil,
const Scene &scene)
{
using namespace blender::bke::greasepencil;
BLI_assert(grease_pencil.runtime != nullptr);
@@ -249,6 +251,10 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c
const bke::CurvesGeometry &curves = info.drawing.strokes();
const bke::AttributeAccessor attributes = curves.attributes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
IndexMaskMemory memory;
const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_strokes(
object, info.drawing, memory);
/* Assumes that if the ".selection" attribute does not exist, all points are selected. */
const VArray<float> selection_float = *attributes.lookup_or_default<float>(
@@ -267,11 +273,15 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c
drawing_start_offset += curves.points_num();
/* Add one id for the restart after every curve. */
total_line_ids_num += curves.curves_num();
total_line_ids_num += editable_strokes.size();
Array<int> size_per_editable_stroke(editable_strokes.size());
offset_indices::gather_group_sizes(
points_by_curve, editable_strokes, size_per_editable_stroke);
/* Add one id for every non-cyclic segment. */
total_line_ids_num += curves.points_num();
total_line_ids_num += std::accumulate(
size_per_editable_stroke.begin(), size_per_editable_stroke.end(), 0);
/* Add one id for the last segment of every cyclic curve. */
total_line_ids_num += array_utils::count_booleans(curves.cyclic());
total_line_ids_num += array_utils::count_booleans(curves.cyclic(), editable_strokes);
/* Do not show points for locked layers. */
if (layer->is_locked()) {
@@ -281,12 +291,12 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c
const VArray<bool> selection = *attributes.lookup_or_default<bool>(
".selection", ATTR_DOMAIN_POINT, true);
for (const int curve_i : curves.curves_range()) {
editable_strokes.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
if (ed::curves::has_anything_selected(selection, points)) {
visible_points_num += points.size();
}
}
});
}
GPUIndexBufBuilder elb;
@@ -308,23 +318,24 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
IndexMaskMemory memory;
const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_strokes(
object, info.drawing, memory);
/* Fill line indices. */
threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
editable_strokes.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
for (const int point : points) {
GPU_indexbuf_add_generic_vert(&elb, point + drawing_start_offset);
}
if (is_cyclic) {
GPU_indexbuf_add_generic_vert(&elb, points.first() + drawing_start_offset);
}
GPU_indexbuf_add_primitive_restart(&elb);
for (const int point_i : points) {
GPU_indexbuf_add_generic_vert(&elb, point_i + drawing_start_offset);
}
if (is_cyclic) {
GPU_indexbuf_add_generic_vert(&elb, points.first() + drawing_start_offset);
}
GPU_indexbuf_add_primitive_restart(&elb);
});
/* Assumes that if the ".selection" attribute does not exist, all points are selected. */
@@ -333,16 +344,14 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c
/* Fill point indices. */
if (!layer->is_locked()) {
threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) {
for (const int curve_i : range) {
const IndexRange points = points_by_curve[curve_i];
if (!ed::curves::has_anything_selected(selection, points)) {
continue;
}
editable_strokes.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
if (!ed::curves::has_anything_selected(selection, points)) {
return;
}
for (const int point : points) {
GPU_indexbuf_add_generic_vert(&epb, point + drawing_start_offset);
}
for (const int point : points) {
GPU_indexbuf_add_generic_vert(&epb, point + drawing_start_offset);
}
});
}
@@ -365,7 +374,9 @@ static void grease_pencil_edit_batch_ensure(const GreasePencil &grease_pencil, c
cache->is_dirty = false;
}
static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, const Scene &scene)
static void grease_pencil_geom_batch_ensure(Object &object,
const GreasePencil &grease_pencil,
const Scene &scene)
{
using namespace blender::bke::greasepencil;
BLI_assert(grease_pencil.runtime != nullptr);
@@ -386,7 +397,6 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c
/* First, count how many vertices and triangles are needed for the whole object. Also record the
* offsets into the curves for the vertices and triangles. */
// int total_points_num = 0; /* UNUSED. */
int total_verts_num = 0;
int total_triangles_num = 0;
int v_offset = 0;
@@ -396,16 +406,34 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c
const bke::CurvesGeometry &curves = info.drawing.strokes();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
object, info.drawing, memory);
int verts_start_offsets_size = curves.curves_num();
int tris_start_offsets_size = curves.curves_num();
const int num_curves = visible_strokes.size();
const int verts_start_offsets_size = num_curves;
const int tris_start_offsets_size = num_curves;
Array<int> verts_start_offsets(verts_start_offsets_size);
Array<int> tris_start_offsets(tris_start_offsets_size);
/* Calculate the vertex and triangle offsets for all the curves. */
/* Calculate the triangle offsets for all the visible curves. */
int t_offset = 0;
int num_cyclic = 0;
int pos = 0;
for (const int curve_i : curves.curves_range()) {
IndexRange points = points_by_curve[curve_i];
if (visible_strokes.contains(curve_i)) {
tris_start_offsets[pos] = t_offset;
pos++;
}
if (points.size() >= 3) {
t_offset += points.size() - 2;
}
}
/* Calculate the vertex offsets for all the visible curves. */
int num_cyclic = 0;
int num_points = 0;
visible_strokes.foreach_index([&](const int curve_i, const int pos) {
IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
@@ -413,20 +441,14 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c
num_cyclic++;
}
tris_start_offsets[curve_i] = t_offset;
if (points.size() >= 3) {
t_offset += points.size() - 2;
}
verts_start_offsets[curve_i] = v_offset;
verts_start_offsets[pos] = v_offset;
v_offset += 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
}
// total_points_num += curves.points_num(); /* UNUSED. */
num_points += points.size();
});
/* One vertex is stored before and after as padding. Cyclic strokes have one extra vertex. */
total_verts_num += curves.points_num() + num_cyclic + curves.curves_num() * 2;
total_triangles_num += (curves.points_num() + num_cyclic) * 2;
total_verts_num += num_points + num_cyclic + num_curves * 2;
total_triangles_num += (num_points + num_cyclic) * 2;
total_triangles_num += info.drawing.triangles().size();
verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
@@ -477,6 +499,9 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c
const Span<uint3> triangles = info.drawing.triangles();
const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
const Span<int> tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i];
IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
object, info.drawing, memory);
auto populate_point = [&](IndexRange verts_range,
int curve_i,
@@ -510,59 +535,57 @@ static void grease_pencil_geom_batch_ensure(const GreasePencil &grease_pencil, c
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
};
threading::parallel_for(curves.curves_range(), 512, [&](IndexRange range) {
for (const int curve_i : range) {
IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
const int verts_start_offset = verts_start_offsets[curve_i];
const int tris_start_offset = tris_start_offsets[curve_i];
const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
visible_strokes.foreach_index([&](const int curve_i, const int pos) {
IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i];
const int verts_start_offset = verts_start_offsets[pos];
const int tris_start_offset = tris_start_offsets[pos];
const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
/* First vertex is not drawn. */
verts_slice.first().mat = -1;
/* First vertex is not drawn. */
verts_slice.first().mat = -1;
/* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
if (points.size() >= 3) {
const Span<uint3> tris_slice = triangles.slice(tris_start_offset, points.size() - 2);
for (const uint3 tri : tris_slice) {
GPU_indexbuf_add_tri_verts(&ibo,
(verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT,
(verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT,
(verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT);
}
/* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
if (points.size() >= 3) {
const Span<uint3> tris_slice = triangles.slice(tris_start_offset, points.size() - 2);
for (const uint3 tri : tris_slice) {
GPU_indexbuf_add_tri_verts(&ibo,
(verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT,
(verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT,
(verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT);
}
/* Write all the point attributes to the vertex buffers. Create a quad for each point. */
for (const int i : IndexRange(points.size())) {
const int idx = i + 1;
populate_point(verts_range,
curve_i,
start_caps[curve_i],
end_caps[curve_i],
points[i],
idx,
verts_slice[idx],
cols_slice[idx]);
}
if (is_cyclic) {
const int idx = points.size() + 1;
populate_point(verts_range,
curve_i,
start_caps[curve_i],
end_caps[curve_i],
points[0],
idx,
verts_slice[idx],
cols_slice[idx]);
}
/* Last vertex is not drawn. */
verts_slice.last().mat = -1;
}
/* Write all the point attributes to the vertex buffers. Create a quad for each point. */
for (const int i : IndexRange(points.size())) {
const int idx = i + 1;
populate_point(verts_range,
curve_i,
start_caps[curve_i],
end_caps[curve_i],
points[i],
idx,
verts_slice[idx],
cols_slice[idx]);
}
if (is_cyclic) {
const int idx = points.size() + 1;
populate_point(verts_range,
curve_i,
start_caps[curve_i],
end_caps[curve_i],
points[0],
idx,
verts_slice[idx],
cols_slice[idx]);
}
/* Last vertex is not drawn. */
verts_slice.last().mat = -1;
});
}
@@ -628,7 +651,7 @@ GPUBatch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, *scene);
grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
return cache->geom_batch;
}
@@ -638,7 +661,7 @@ GPUBatch *DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_edit_batch_ensure(grease_pencil, *scene);
grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
return cache->edit_points;
}
@@ -648,7 +671,7 @@ GPUBatch *DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob)
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_edit_batch_ensure(grease_pencil, *scene);
grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
return cache->edit_lines;
}
@@ -658,7 +681,7 @@ GPUVertBuf *DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Obje
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, *scene);
grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
return cache->vbo;
}
@@ -668,7 +691,7 @@ GPUVertBuf *DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object
using namespace blender::draw;
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
GreasePencilBatchCache *cache = grease_pencil_batch_cache_get(grease_pencil);
grease_pencil_geom_batch_ensure(grease_pencil, *scene);
grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
return cache->vbo_col;
}

View File

@@ -312,12 +312,27 @@ static VectorSet<int> get_editable_material_indices(Object &object)
(material->gp_style->flag & GP_MATERIAL_LOCKED) == 0 &&
(material->gp_style->flag & GP_MATERIAL_HIDE) == 0)
{
locked_material_indices.add(mat_i);
locked_material_indices.add_new(mat_i);
}
}
return locked_material_indices;
}
static VectorSet<int> get_hidden_material_indices(Object &object)
{
BLI_assert(object.type == OB_GREASE_PENCIL);
VectorSet<int> hidden_material_indices;
for (const int mat_i : IndexRange(object.totcol)) {
Material *material = BKE_object_material_get(&object, mat_i + 1);
if (material != nullptr && material->gp_style != nullptr &&
(material->gp_style->flag & GP_MATERIAL_HIDE) != 0)
{
hidden_material_indices.add_new(mat_i);
}
}
return hidden_material_indices;
}
IndexMask retrieve_editable_strokes(Object &object,
const bke::greasepencil::Drawing &drawing,
IndexMaskMemory &memory)
@@ -327,13 +342,23 @@ IndexMask retrieve_editable_strokes(Object &object,
/* Get all the editable material indices */
VectorSet<int> editable_material_indices = get_editable_material_indices(object);
if (locked_material_indices.is_empty()) {
return drawing.strokes().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", ATTR_DOMAIN_CURVE);
if (!materials) {
/* if the attribute does not exist then the default is the first material. */
if (locked_material_indices.contains(0)) {
return curves_range;
}
return IndexMask();
}
/* Get all the strokes that have their material unlocked. */
const VArray<int> materials = *attributes.lookup_or_default<int>(
"material_index", ATTR_DOMAIN_CURVE, -1);
return IndexMask::from_predicate(
curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
const int material_index = materials[curve_i];
@@ -348,13 +373,23 @@ IndexMask retrieve_editable_points(Object &object,
/* Get all the editable material indices */
VectorSet<int> editable_material_indices = get_editable_material_indices(object);
if (locked_material_indices.is_empty()) {
return drawing.strokes().points_range();
}
const bke::CurvesGeometry &curves = drawing.strokes();
const IndexRange points_range = drawing.strokes().points_range();
const bke::AttributeAccessor attributes = curves.attributes();
/* Propagate the material index to the points. */
const VArray<int> materials = *attributes.lookup_or_default<int>(
"material_index", ATTR_DOMAIN_POINT, -1);
const VArray<int> materials = *attributes.lookup<int>("material_index", ATTR_DOMAIN_POINT);
if (!materials) {
/* if the attribute does not exist then the default is the first material. */
if (locked_material_indices.contains(0)) {
return points_range;
}
return IndexMask();
}
/* 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) {
@@ -377,6 +412,33 @@ IndexMask retrieve_editable_elements(Object &object,
return {};
}
IndexMask retrieve_visible_strokes(Object &object,
const bke::greasepencil::Drawing &drawing,
IndexMaskMemory &memory)
{
using namespace blender;
/* Get all the hidden material indices. */
VectorSet<int> hidden_material_indices = get_hidden_material_indices(object);
if (hidden_material_indices.is_empty()) {
return drawing.strokes().curves_range();
}
const bke::CurvesGeometry &curves = drawing.strokes();
const IndexRange curves_range = drawing.strokes().curves_range();
const bke::AttributeAccessor attributes = curves.attributes();
/* Get all the strokes that have their material visible. */
const VArray<int> materials = *attributes.lookup_or_default<int>(
"material_index", ATTR_DOMAIN_CURVE, -1);
return IndexMask::from_predicate(
curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
const int material_index = materials[curve_i];
return !hidden_material_indices.contains(material_index);
});
}
IndexMask retrieve_editable_and_selected_strokes(Object &object,
const bke::greasepencil::Drawing &drawing,
IndexMaskMemory &memory)

View File

@@ -181,6 +181,10 @@ IndexMask retrieve_editable_elements(Object &object,
eAttrDomain selection_domain,
IndexMaskMemory &memory);
IndexMask retrieve_visible_strokes(Object &grease_pencil_object,
const bke::greasepencil::Drawing &drawing,
IndexMaskMemory &memory);
IndexMask retrieve_editable_and_selected_strokes(Object &grease_pencil_object,
const bke::greasepencil::Drawing &drawing,
IndexMaskMemory &memory);