GPv3: Add Compound Shapes Rendering (i.e. Hole rendering) #12

Closed
casey-bianco-davis wants to merge 8 commits from GPv3-Compound-Shapes-2 into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 388 additions and 164 deletions
Showing only changes of commit 2f5dbf76f5 - Show all commits

View File

@ -86,6 +86,9 @@ class Drawing : public ::GreasePencilDrawing {
const bke::CurvesGeometry &strokes() const; const bke::CurvesGeometry &strokes() const;
bke::CurvesGeometry &strokes_for_write(); bke::CurvesGeometry &strokes_for_write();
Array<IndexMask> get_shapes_index_masks(IndexMaskMemory &memory) const;
/** /**
* The triangles for all the fills in the geometry. * The triangles for all the fills in the geometry.
*/ */

View File

@ -29,6 +29,7 @@
#include "BKE_object_types.hh" #include "BKE_object_types.hh"
#include "BLI_bounds.hh" #include "BLI_bounds.hh"
#include "BLI_delaunay_2d.hh"
#include "BLI_enumerable_thread_specific.hh" #include "BLI_enumerable_thread_specific.hh"
#include "BLI_map.hh" #include "BLI_map.hh"
#include "BLI_math_euler_types.hh" #include "BLI_math_euler_types.hh"
@ -366,61 +367,234 @@ Drawing::~Drawing()
this->runtime = nullptr; this->runtime = nullptr;
} }
Span<Vector<uint3>> Drawing::triangles() const Array<IndexMask> Drawing::get_shapes_index_masks(IndexMaskMemory &memory) const
{ {
struct LocalMemArena { const CurvesGeometry &curves = this->strokes();
MemArena *pf_arena = nullptr; const bke::AttributeAccessor attributes = curves.attributes();
LocalMemArena() : pf_arena(BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "Drawing::triangles")) {}
~LocalMemArena() const int num_curves = curves.curves_num();
const VArray<int> shape_ids = *attributes.lookup<int>("shape_id", bke::AttrDomain::Curve);
if (!shape_ids) {
/* If the attribute does not exist then the default is each shape containing one curve. */
Array<IndexMask> data(num_curves);
IndexMask::from_groups<int>(
IndexMask(num_curves), memory, [&](const int i) { return i; }, data);
return data;
}
const int max_shape_id = *std::max_element(shape_ids.get_internal_span().begin(),
shape_ids.get_internal_span().end());
Array<IndexMask> data(max_shape_id + 1);
IndexMask::from_groups<int>(
IndexRange(num_curves), memory, [&](const int i) { return shape_ids[i]; }, data);
return data;
}
static bool check_self_intersections(Span<float2> projverts)
{ {
if (pf_arena != nullptr) { std::atomic<bool> intersect = false;
BLI_memarena_free(pf_arena); threading::parallel_for(projverts.index_range(), 512, [&](const IndexRange range) {
for (const int e2_id : range) {
if (intersect) {
return;
}
for (const int e1_id : projverts.index_range().drop_front(e2_id)) {
const int p1 = e1_id;
const int p2 = (e1_id + 1) % projverts.size();
const int p3 = e2_id;
const int p4 = (e2_id + 1) % projverts.size();
if (p1 == p4) {
continue;
}
if (p2 == p3) {
continue;
}
if (isect_seg_seg_v2_simple(projverts[p1], projverts[p2], projverts[p3], projverts[p4])) {
intersect.store(true, std::memory_order_relaxed);
return;
} }
} }
}; };
});
return intersect;
}
static bool check_other_intersections(Span<float2> projverts1, Span<float2> projverts2)
{
std::atomic<bool> intersect = false;
threading::parallel_for(projverts1.index_range(), 512, [&](const IndexRange range) {
for (const int e1_id : range) {
for (const int e2_id : projverts2.index_range()) {
const int p11 = e1_id;
const int p12 = (e1_id + 1) % projverts1.size();
const int p21 = e2_id;
const int p22 = (e2_id + 1) % projverts2.size();
if (isect_seg_seg_v2_simple(
projverts1[p11], projverts1[p12], projverts2[p21], projverts2[p22]))
{
intersect.store(true, std::memory_order_relaxed);
return;
}
}
}
});
return intersect;
}
static bool check_valid_curves(Span<float2> projverts, const OffsetIndices<int> points_by_group)
{
std::atomic<bool> intersect = false;
/* Check for self intersections. */
threading::parallel_for(points_by_group.index_range(), 512, [&](const IndexRange range) {
for (const int pos : range) {
if (intersect) {
return;
}
const IndexRange point_group = points_by_group[pos];
if (check_self_intersections(projverts.slice(point_group))) {
intersect.store(true, std::memory_order_relaxed);
return;
}
}
});
if (intersect) {
return false;
}
/* Check if other intersect. */
threading::parallel_for(points_by_group.index_range(), 512, [&](const IndexRange range1) {
for (const int pos1 : range1) {
if (intersect) {
return;
}
const IndexRange point_group1 = points_by_group[pos1];
threading::parallel_for(points_by_group.index_range(), 512, [&](const IndexRange range2) {
for (const int pos2 : range2) {
if (intersect) {
return;
}
const IndexRange point_group2 = points_by_group[pos2];
if (pos2 >= pos1) {
return;
}
if (check_other_intersections(projverts.slice(point_group1),
projverts.slice(point_group2)))
{
intersect.store(true, std::memory_order_relaxed);
return;
}
}
});
}
});
if (intersect) {
return false;
}
return true;
}
Span<Vector<uint3>> Drawing::triangles() const
{
this->runtime->triangles_cache.ensure([&](Vector<Vector<uint3>> &r_data) { this->runtime->triangles_cache.ensure([&](Vector<Vector<uint3>> &r_data) {
const CurvesGeometry &curves = this->strokes(); const CurvesGeometry &curves = this->strokes();
const Span<float3> positions = curves.evaluated_positions(); const Span<float3> positions = curves.evaluated_positions();
const Span<float3> normals = this->curve_plane_normals(); const Span<float3> normals = this->curve_plane_normals();
const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve(); const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
const Array<int> point_to_curve_map = curves.point_to_curve_map();
r_data.resize(curves.curves_num()); IndexMaskMemory memory;
const Array<IndexMask> groups = this->get_shapes_index_masks(memory);
r_data.resize(groups.size() + 1);
// r_data.resize(curves.curves_num());
MutableSpan<Vector<uint3>> strokes_triangles = r_data.as_mutable_span(); MutableSpan<Vector<uint3>> strokes_triangles = r_data.as_mutable_span();
threading::EnumerableThreadSpecific<LocalMemArena> all_local_mem_arenas;
threading::parallel_for(curves.curves_range(), 32, [&](const IndexRange range) { threading::parallel_for(groups.index_range(), 32, [&](const IndexRange group_range) {
MemArena *pf_arena = all_local_mem_arenas.local().pf_arena; for (const int group_id : group_range) {
for (const int curve_i : range) { const IndexMask &group = groups[group_id];
float3x3 axis_mat;
axis_dominant_v3_to_m3(axis_mat.ptr(), normals[group.first()]);
Array<int> offsets_data(group.size() + 1);
offset_indices::gather_group_sizes(
points_by_curve, group, offsets_data.as_mutable_span().drop_back(1));
offset_indices::accumulate_counts_to_offsets(offsets_data);
const OffsetIndices<int> points_by_group = OffsetIndices<int>(offsets_data);
const int num_points = points_by_group.total_size();
Array<float2> projverts2(num_points);
group.foreach_index(GrainSize(256), [&](const int64_t curve_i, const int64_t pos) {
const IndexRange point_group = points_by_group[pos];
const IndexRange points = points_by_curve[curve_i]; const IndexRange points = points_by_curve[curve_i];
if (points.size() < 3) { threading::parallel_for(points.index_range(), 512, [&](const IndexRange range) {
for (const int p_id : range) {
mul_v2_m3v3(projverts2[point_group[p_id]], axis_mat.ptr(), positions[points[p_id]]);
}
});
});
if (!check_valid_curves(projverts2, points_by_group)) {
continue; continue;
} }
const int num_triangles = points.size() - 2; Array<double2> verts(num_points);
strokes_triangles[curve_i].resize(num_triangles); Array<std::pair<int, int>> edges(num_points);
MutableSpan<uint3> r_tris = strokes_triangles[curve_i]; Array<Vector<int>> faces(group.size());
float(*projverts)[2] = static_cast<float(*)[2]>( Array<int> vert_to_point_map(num_points);
BLI_memarena_alloc(pf_arena, sizeof(*projverts) * size_t(points.size())));
float3x3 axis_mat; group.foreach_index(GrainSize(256), [&](const int64_t curve_i, const int64_t pos) {
axis_dominant_v3_to_m3(axis_mat.ptr(), normals[curve_i]); const IndexRange point_group = points_by_group[pos];
const IndexRange points = points_by_curve[curve_i];
for (const int i : points.index_range()) { faces[pos].resize(points.size());
mul_v2_m3v3(projverts[i], axis_mat.ptr(), positions[points[i]]); threading::parallel_for(points.index_range(), 512, [&](const IndexRange range) {
for (const int p_id : range) {
vert_to_point_map[point_group[p_id]] = points[p_id];
verts[point_group[p_id]] = double2(projverts2[point_group[p_id]]);
edges[point_group[p_id]] = std::pair<int, int>(
point_group[p_id], point_group[(p_id + 1) % points.size()]);
faces[pos][p_id] = point_group[p_id];
} }
});
});
BLI_polyfill_calc_arena(projverts, meshintersect::CDT_input<double> input;
points.size(), input.vert = verts;
0, input.edge = edges;
reinterpret_cast<uint32_t(*)[3]>(r_tris.data()), input.face = faces;
pf_arena); input.need_ids = false;
for (const int i : r_tris.index_range()) { meshintersect::CDT_result<double> result = delaunay_2d_calc(input, CDT_INSIDE_WITH_HOLES);
r_tris[i] += uint3(points.first());
strokes_triangles[group_id].resize(result.face.size());
MutableSpan<uint3> r_tris = strokes_triangles[group_id];
threading::parallel_for(result.face.index_range(), 512, [&](const IndexRange range) {
for (const int i : range) {
BLI_assert(result.face[i].size() == 3);
r_tris[i] = uint3(vert_to_point_map[result.face[i][0]],
vert_to_point_map[result.face[i][1]],
vert_to_point_map[result.face[i][2]]);
} }
});
BLI_memarena_clear(pf_arena);
} }
}); });
}); });

View File

@ -725,24 +725,32 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
IndexMaskMemory memory; IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes( const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
*ob, info.drawing, memory); *ob, info.drawing, memory);
const Array<IndexMask> groups = info.drawing.get_shapes_index_masks(memory);
/* Precompute all the triangle and vertex counts. /* Precompute all the triangle and vertex counts.
* In case the drawing should not be rendered, we need to compute the offset where the next * In case the drawing should not be rendered, we need to compute the offset where the next
* drawing begins. */ * drawing begins. */
Array<int> num_triangles_per_stroke(visible_strokes.size()); Array<int> num_triangles_per_stroke(groups.size());
Array<int> num_vertices_per_stroke(visible_strokes.size()); Array<int> num_vertices_per_stroke(visible_strokes.size());
int total_num_triangles = 0; int total_num_triangles = 0;
int total_num_vertices = 0; int total_num_vertices = 0;
visible_strokes.foreach_index([&](const int stroke_i, const int pos) { int current_curve = 0;
const IndexRange points = points_by_curve[stroke_i]; for (const int group_id : groups.index_range()) {
const int num_stroke_triangles = triangles[stroke_i].size(); const IndexMask &group = groups[group_id];
const int num_stroke_vertices = (points.size() +
int(cyclic[stroke_i] && (points.size() >= 3))); const int num_stroke_triangles = triangles[group_id].size();
num_triangles_per_stroke[pos] = num_stroke_triangles; num_triangles_per_stroke[group_id] = num_stroke_triangles;
num_vertices_per_stroke[pos] = num_stroke_vertices;
total_num_triangles += num_stroke_triangles; total_num_triangles += num_stroke_triangles;
group.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
const int num_stroke_vertices = (points.size() +
int(cyclic[curve_i] && (points.size() >= 3)));
num_vertices_per_stroke[current_curve] = num_stroke_vertices;
total_num_vertices += num_stroke_vertices; total_num_vertices += num_stroke_vertices;
current_curve++;
}); });
}
bool is_layer_used_as_mask = false; bool is_layer_used_as_mask = false;
const bool show_drawing_in_render = use_layer_in_render( const bool show_drawing_in_render = use_layer_in_render(
@ -789,17 +797,21 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
info.frame_number != pd->cfra && pd->use_multiedit_lines_only; info.frame_number != pd->cfra && pd->use_multiedit_lines_only;
const bool is_onion = info.onion_id != 0; const bool is_onion = info.onion_id != 0;
visible_strokes.foreach_index([&](const int stroke_i, const int pos) { current_curve = 0;
const IndexRange points = points_by_curve[stroke_i]; for (const int group_id : groups.index_range()) {
const IndexMask &group = groups[group_id];
const int curve_i = group.first();
/* The material index is allowed to be negative as it's stored as a generic attribute. We /* The material index is allowed to be negative as it's stored as a generic attribute. We
* clamp it here to avoid crashing in the rendering code. Any stroke with a material < 0 will * clamp it here to avoid crashing in the rendering code. Any stroke with a material < 0
* use the first material in the first material slot.*/ * will use the first material in the first material slot.*/
const int material_index = std::max(stroke_materials[stroke_i], 0); const int material_index = std::max(stroke_materials[curve_i], 0);
const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1); const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
const bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0); const bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
const bool show_fill = (points.size() >= 3) && const bool show_fill = (!triangles[group_id].is_empty()) &&
((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) &&
(!pd->simplify_fill); (!pd->simplify_fill);
const bool hide_onion = is_onion && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0 || const bool hide_onion = is_onion && ((gp_style->flag & GP_MATERIAL_HIDE_ONIONSKIN) != 0 ||
@ -808,9 +820,12 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
(only_lines && !is_onion) || hide_onion; (only_lines && !is_onion) || hide_onion;
if (skip_stroke) { if (skip_stroke) {
t_offset += num_triangles_per_stroke[pos]; t_offset += num_triangles_per_stroke[group_id];
t_offset += num_vertices_per_stroke[pos] * 2; for (const int i : group.index_range()) {
return; t_offset += num_vertices_per_stroke[current_curve + i] * 2;
};
current_curve += group.size();
continue;
} }
GPUUniformBuf *new_ubo_mat; GPUUniformBuf *new_ubo_mat;
@ -855,21 +870,26 @@ static GPENCIL_tObject *grease_pencil_object_cache_populate(GPENCIL_PrivateData
if (show_fill) { if (show_fill) {
const int v_first = t_offset * 3; const int v_first = t_offset * 3;
const int v_count = num_triangles_per_stroke[pos] * 3; const int v_count = num_triangles_per_stroke[group_id] * 3;
drawcall_add(geom, v_first, v_count); drawcall_add(geom, v_first, v_count);
} }
t_offset += num_triangles_per_stroke[pos]; t_offset += num_triangles_per_stroke[group_id];
group.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
if (show_stroke) { if (show_stroke) {
const int v_first = t_offset * 3; const int v_first = t_offset * 3;
const int v_count = num_vertices_per_stroke[pos] * 2 * 3; const int v_count = num_vertices_per_stroke[current_curve] * 2 * 3;
drawcall_add(geom, v_first, v_count); drawcall_add(geom, v_first, v_count);
} }
t_offset += num_vertices_per_stroke[pos] * 2; t_offset += num_vertices_per_stroke[current_curve] * 2;
current_curve++;
}); });
} }
}
drawcall_flush(); drawcall_flush();

View File

@ -89,33 +89,32 @@ class GreasePencil {
IndexMaskMemory memory; IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes( const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
*ob, info.drawing, memory); *ob, info.drawing, memory);
const Array<IndexMask> groups = info.drawing.get_shapes_index_masks(memory);
const Span<Vector<uint3>> triangles = info.drawing.triangles(); const Span<Vector<uint3>> triangles = info.drawing.triangles();
visible_strokes.foreach_index([&](const int stroke_i) { const bool hide_onion = info.onion_id != 0;
const IndexRange points = points_by_curve[stroke_i];
const int material_index = stroke_materials[stroke_i]; for (const int group_id : groups.index_range()) {
const IndexMask &group = groups[group_id];
const int material_index = stroke_materials[group.first()];
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1); MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
const bool hide_onion = info.onion_id != 0;
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
const int num_stroke_triangles = triangles[stroke_i].size(); const int num_stroke_triangles = triangles[group_id].size();
const int num_stroke_vertices = (points.size() +
int(cyclic[stroke_i] && (points.size() >= 3)));
if (hide_material || hide_onion) { if (hide_material || hide_onion) {
t_offset += num_stroke_triangles; t_offset += num_stroke_triangles;
t_offset += num_stroke_vertices * 2;
return;
} }
blender::gpu::Batch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
const bool show_fill = (points.size() >= 3) && const bool show_fill = (num_stroke_triangles != 0) &&
(gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0; (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
blender::gpu::Batch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
if (show_fill) { if (show_fill) {
int v_first = t_offset * 3; int v_first = t_offset * 3;
int v_count = num_stroke_triangles * 3; int v_count = num_stroke_triangles * 3;
@ -124,6 +123,17 @@ class GreasePencil {
t_offset += num_stroke_triangles; t_offset += num_stroke_triangles;
group.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
const int num_stroke_vertices = (points.size() +
int(cyclic[curve_i] && (points.size() >= 3)));
if (hide_material || hide_onion) {
t_offset += num_stroke_vertices * 2;
return;
}
if (show_stroke) { if (show_stroke) {
int v_first = t_offset * 3; int v_first = t_offset * 3;
int v_count = num_stroke_vertices * 2 * 3; int v_count = num_stroke_vertices * 2 * 3;
@ -133,6 +143,7 @@ class GreasePencil {
}); });
} }
} }
}
private: private:
/* Returns the normal plane in NDC space. */ /* Returns the normal plane in NDC space. */

View File

@ -323,31 +323,26 @@ static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene,
IndexMaskMemory memory; IndexMaskMemory memory;
const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes( const IndexMask visible_strokes = ed::greasepencil::retrieve_visible_strokes(
*ob, info.drawing, memory); *ob, info.drawing, memory);
const Array<IndexMask> groups = info.drawing.get_shapes_index_masks(memory);
const Span<Vector<uint3>> triangles = info.drawing.triangles(); const Span<Vector<uint3>> triangles = info.drawing.triangles();
visible_strokes.foreach_index([&](const int stroke_i) { for (const int group_id : groups.index_range()) {
const IndexRange points = points_by_curve[stroke_i]; const IndexMask &group = groups[group_id];
const int material_index = stroke_materials[stroke_i];
const int material_index = stroke_materials[group.first()];
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1); MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, material_index + 1);
const bool hide_onion = info.onion_id != 0; const bool hide_onion = info.onion_id != 0;
const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; const bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
const int num_stroke_triangles = triangles[stroke_i].size(); const int num_stroke_triangles = triangles[group_id].size();
const int num_stroke_vertices = (points.size() +
int(cyclic[stroke_i] && (points.size() >= 3)));
if (hide_material || hide_onion) {
t_offset += num_stroke_triangles;
t_offset += num_stroke_vertices * 2;
return;
}
blender::gpu::Batch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0; const bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
const bool show_fill = (points.size() >= 3) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0; const bool show_fill = (num_stroke_triangles != 0) &&
(gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
blender::gpu::Batch *geom = draw::DRW_cache_grease_pencil_get(scene, ob);
if (show_fill) { if (show_fill) {
int v_first = t_offset * 3; int v_first = t_offset * 3;
@ -357,6 +352,16 @@ static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene,
t_offset += num_stroke_triangles; t_offset += num_stroke_triangles;
group.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i];
const int num_stroke_vertices = (points.size() +
int(cyclic[curve_i] && (points.size() >= 3)));
if (hide_material || hide_onion) {
t_offset += num_stroke_vertices * 2;
return;
}
if (show_stroke) { if (show_stroke) {
int v_first = t_offset * 3; int v_first = t_offset * 3;
int v_count = num_stroke_vertices * 2 * 3; int v_count = num_stroke_vertices * 2 * 3;
@ -366,6 +371,7 @@ static void OVERLAY_outline_grease_pencil(OVERLAY_PrivateData *pd, Scene *scene,
}); });
} }
} }
}
static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob) static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob)
{ {

View File

@ -1075,8 +1075,8 @@ static void grease_pencil_geom_batch_ensure(Object &object,
total_verts_num += num_points + num_cyclic + num_curves * 2; total_verts_num += num_points + num_cyclic + num_curves * 2;
total_triangles_num += (num_points + num_cyclic) * 2; total_triangles_num += (num_points + num_cyclic) * 2;
for (const int curve_i : curves.curves_range()) { for (const int group_id : info.drawing.triangles().index_range()) {
total_triangles_num += info.drawing.triangles()[curve_i].size(); total_triangles_num += info.drawing.triangles()[group_id].size();
} }
verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets)); verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
@ -1194,10 +1194,31 @@ static void grease_pencil_geom_batch_ensure(Object &object,
GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3); GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
}; };
visible_strokes.foreach_index([&](const int curve_i, const int pos) { const Array<IndexMask> groups = info.drawing.get_shapes_index_masks(memory);
const Array<int> point_to_curve_map = curves.point_to_curve_map();
auto point_to_id = [&](uint32_t p) {
const int curve_ = point_to_curve_map[p];
const IndexRange points_ = points_by_curve[curve_];
return (1 + (p - points_.first()) + verts_start_offsets[curve_]) << GP_VERTEX_ID_SHIFT;
};
for (const int group_id : groups.index_range()) {
const IndexMask &group = groups[group_id];
const Span<uint3> tris_slice = triangles[group_id];
/* Add the triangle indices to the index buffer. */
// if (tris_slice.size() != 0) {
for (const uint3 tri : tris_slice) {
const uint3 tri_verts = uint3(point_to_id(tri.x), point_to_id(tri.y), point_to_id(tri.z));
GPU_indexbuf_add_tri_verts(&ibo, tri_verts.x, tri_verts.y, tri_verts.z);
}
// }
group.foreach_index([&](const int curve_i) {
const IndexRange points = points_by_curve[curve_i]; const IndexRange points = points_by_curve[curve_i];
const bool is_cyclic = cyclic[curve_i] && (points.size() > 2); const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
const int verts_start_offset = verts_start_offsets[pos]; const int verts_start_offset = verts_start_offsets[curve_i];
const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1; const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
const IndexRange verts_range = IndexRange(verts_start_offset, num_verts); const IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range); MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
@ -1209,18 +1230,6 @@ static void grease_pencil_geom_batch_ensure(Object &object,
/* First vertex is not drawn. */ /* First vertex is not drawn. */
verts_slice.first().mat = -1; 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[curve_i];
for (const uint3 tri : tris_slice) {
GPU_indexbuf_add_tri_verts(
&ibo,
(verts_range.first() + tri.x - points.first() + 1) << GP_VERTEX_ID_SHIFT,
(verts_range.first() + tri.y - points.first() + 1) << GP_VERTEX_ID_SHIFT,
(verts_range.first() + tri.z - points.first() + 1) << GP_VERTEX_ID_SHIFT);
}
}
/* Write all the point attributes to the vertex buffers. Create a quad for each point. */ /* Write all the point attributes to the vertex buffers. Create a quad for each point. */
const float u_scale = u_scales[curve_i]; const float u_scale = u_scales[curve_i];
const float u_translation = u_translations[curve_i]; const float u_translation = u_translations[curve_i];
@ -1259,6 +1268,7 @@ static void grease_pencil_geom_batch_ensure(Object &object,
verts_slice.last().mat = -1; verts_slice.last().mat = -1;
}); });
} }
}
/* Mark last 2 verts as invalid. */ /* Mark last 2 verts as invalid. */
verts[total_verts_num + 0].mat = -1; verts[total_verts_num + 0].mat = -1;