Blender V4.3
draw_cache_impl_grease_pencil.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "BKE_attribute.hh"
12#include "BKE_curves.hh"
13#include "BKE_deform.hh"
14#include "BKE_grease_pencil.h"
15#include "BKE_grease_pencil.hh"
16
17#include "BLI_offset_indices.hh"
18#include "BLI_task.hh"
19
21
22#include "DRW_engine.hh"
23#include "DRW_render.hh"
24
25#include "ED_curves.hh"
26#include "ED_grease_pencil.hh"
27
28#include "GPU_batch.hh"
29
30#include "draw_cache_impl.hh"
31
34
35namespace blender::draw {
36
44 gpu::Batch *geom_batch;
45 gpu::Batch *lines_batch;
46 gpu::Batch *edit_points;
47 gpu::Batch *edit_lines;
48
49 /* Crazy-space point positions for original points. */
51 /* Selection of original points. */
53 /* vflag of original points. */
55 /* Indices of visible points. */
57
58 /* Crazy-space point positions for all line points. */
60 /* Selection of line points. */
62 /* Indices for lines segments. */
64
69};
70
71/* -------------------------------------------------------------------- */
75/* MUST match the format below. */
84
95
96/* MUST match the format below. */
98 float vcol[4]; /* Vertex color */
99 float fcol[4]; /* Fill color */
100};
101
103{
104 static GPUVertFormat format = {0};
105 if (format.attr_len == 0) {
108 }
109 return &format;
110}
111
114/* -------------------------------------------------------------------- */
118static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
119{
120 BLI_assert(grease_pencil.runtime != nullptr);
121 const GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
122 grease_pencil.runtime->batch_cache);
123 return (cache && cache->is_dirty == false &&
124 cache->cache_frame == grease_pencil.runtime->eval_frame);
125}
126
128{
129 BLI_assert(grease_pencil.runtime != nullptr);
130 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
131 grease_pencil.runtime->batch_cache);
132 if (cache == nullptr) {
133 cache = MEM_new<GreasePencilBatchCache>(__func__);
134 grease_pencil.runtime->batch_cache = cache;
135 }
136 else {
137 *cache = {};
138 }
139
140 cache->is_dirty = false;
141 cache->cache_frame = grease_pencil.runtime->eval_frame;
142
143 return cache;
144}
145
147{
148 BLI_assert(grease_pencil.runtime != nullptr);
149 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
150 grease_pencil.runtime->batch_cache);
151 if (cache == nullptr) {
152 return;
153 }
154
159
163
168
172
173 cache->is_dirty = true;
174}
175
177{
178 BLI_assert(grease_pencil.runtime != nullptr);
179 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
180 grease_pencil.runtime->batch_cache);
181 if (!grease_pencil_batch_cache_valid(grease_pencil)) {
182 grease_pencil_batch_cache_clear(grease_pencil);
183 return grease_pencil_batch_cache_init(grease_pencil);
184 }
185
186 return cache;
187}
188
191/* -------------------------------------------------------------------- */
195BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float softness)
196{
197 int32_t packed = 0;
198 /* Aspect uses 9 bits */
199 float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
200 packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
201 /* Store if inverted in the 9th bit. */
202 if (asp > 1.0f) {
203 packed |= 1 << 8;
204 }
205 /* Rotation uses 9 bits */
206 /* Rotation are in [-90..90] degree range, so we can encode the sign of the angle + the cosine
207 * because the cosine will always be positive. */
208 packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
209 /* Store sine sign in 9th bit. */
210 if (rot < 0.0f) {
211 packed |= 1 << 17;
212 }
213 /* Hardness uses 8 bits */
214 packed |= int32_t(unit_float_to_uchar_clamp(1.0f - softness)) << 18;
215 return packed;
216}
217
218static void copy_transformed_positions(const Span<float3> src_positions,
219 const IndexRange range,
220 const float4x4 &transform,
221 MutableSpan<float3> dst_positions)
222{
223 for (const int point_i : range) {
224 dst_positions[point_i] = math::transform_point(transform, src_positions[point_i]);
225 }
226}
227
230{
231 return cache->edit_points_pos == nullptr && cache->edit_line_indices == nullptr &&
232 cache->edit_points_indices == nullptr && cache->edit_points == nullptr &&
233 cache->edit_lines == nullptr;
234}
235
237 const GreasePencil &grease_pencil,
238 const Scene &scene)
239{
240 using namespace blender::bke::greasepencil;
241
242 constexpr float no_active_weight = 666.0f;
243
244 BLI_assert(grease_pencil.runtime != nullptr);
245 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
246 grease_pencil.runtime->batch_cache);
247
248 if (cache->edit_points_pos != nullptr) {
249 return;
250 }
251
252 /* Should be discarded together. */
254
255 /* Get active vertex group. */
256 const bDeformGroup *active_defgroup = static_cast<bDeformGroup *>(BLI_findlink(
257 &grease_pencil.vertex_group_names, grease_pencil.vertex_group_active_index - 1));
258 const char *active_defgroup_name = (active_defgroup == nullptr) ? "" : active_defgroup->name;
259
260 /* Get the visible drawings. */
262 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
263
264 const Span<const Layer *> layers = grease_pencil.layers();
265
266 static GPUVertFormat format_points_pos = {0};
267 if (format_points_pos.attr_len == 0) {
268 GPU_vertformat_attr_add(&format_points_pos, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
269 }
270
271 static GPUVertFormat format_points_weight = {0};
272 if (format_points_weight.attr_len == 0) {
273 GPU_vertformat_attr_add(&format_points_weight, "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
274 }
275
277 cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(format_points_pos, vbo_flag);
278 cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(format_points_weight, vbo_flag);
279
280 int visible_points_num = 0;
281 int total_line_ids_num = 0;
282 int total_points_num = 0;
283 for (const ed::greasepencil::DrawingInfo &info : drawings) {
284 const bke::CurvesGeometry &curves = info.drawing.strokes();
285 total_points_num += curves.points_num();
286 }
287
288 if (total_points_num == 0) {
289 return;
290 }
291
292 GPU_vertbuf_data_alloc(*cache->edit_points_pos, total_points_num);
293 GPU_vertbuf_data_alloc(*cache->edit_points_selection, total_points_num);
294
295 MutableSpan<float3> points_pos = cache->edit_points_pos->data<float3>();
296 MutableSpan<float> points_weight = cache->edit_points_selection->data<float>();
297
298 int drawing_start_offset = 0;
299 for (const ed::greasepencil::DrawingInfo &info : drawings) {
300 const Layer &layer = *layers[info.layer_index];
301 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
302 const bke::CurvesGeometry &curves = info.drawing.strokes();
303 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
304 const VArray<bool> cyclic = curves.cyclic();
305 IndexMaskMemory memory;
307 object, info.drawing, memory);
308
309 const IndexRange points(drawing_start_offset, curves.points_num());
310 const Span<float3> positions = curves.positions();
311 MutableSpan<float3> positions_slice = points_pos.slice(points);
312 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange range) {
313 copy_transformed_positions(positions, range, layer_space_to_object_space, positions_slice);
314 });
315
316 /* Get vertex weights of the active vertex group in this drawing. */
317 const VArray<float> weights = *curves.attributes().lookup_or_default<float>(
318 active_defgroup_name, bke::AttrDomain::Point, no_active_weight);
319 MutableSpan<float> weights_slice = points_weight.slice(points);
320 weights.materialize(weights_slice);
321
322 drawing_start_offset += curves.points_num();
323
324 /* Add one id for the restart after every curve. */
325 total_line_ids_num += visible_strokes.size();
326 Array<int> size_per_editable_stroke(visible_strokes.size());
327 offset_indices::gather_group_sizes(points_by_curve, visible_strokes, size_per_editable_stroke);
328 /* Add one id for every non-cyclic segment. */
329 total_line_ids_num += std::accumulate(
330 size_per_editable_stroke.begin(), size_per_editable_stroke.end(), 0);
331 /* Add one id for the last segment of every cyclic curve. */
332 total_line_ids_num += array_utils::count_booleans(curves.cyclic(), visible_strokes);
333
334 /* Do not show weights for locked layers. */
335 if (layer.is_locked()) {
336 continue;
337 }
338
339 visible_strokes.foreach_index([&](const int curve_i) {
340 const IndexRange points = points_by_curve[curve_i];
341 visible_points_num += points.size();
342 });
343 }
344
348 total_line_ids_num,
350
354 visible_points_num,
356
357 /* Fill point index buffer with data. */
358 drawing_start_offset = 0;
359 for (const ed::greasepencil::DrawingInfo &info : drawings) {
360 const Layer *layer = layers[info.layer_index];
361 const bke::CurvesGeometry &curves = info.drawing.strokes();
362 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
363 const VArray<bool> cyclic = curves.cyclic();
364 IndexMaskMemory memory;
366 object, info.drawing, memory);
367
368 /* Fill line indices. */
369 visible_strokes.foreach_index([&](const int curve_i) {
370 const IndexRange points = points_by_curve[curve_i];
371 const bool is_cyclic = cyclic[curve_i];
372
373 for (const int point_i : points) {
374 GPU_indexbuf_add_generic_vert(&elb, point_i + drawing_start_offset);
375 }
376
377 if (is_cyclic) {
378 GPU_indexbuf_add_generic_vert(&elb, points.first() + drawing_start_offset);
379 }
380
382 });
383
384 /* Fill point indices. */
385 if (!layer->is_locked()) {
386 visible_strokes.foreach_index([&](const int curve_i) {
387 const IndexRange points = points_by_curve[curve_i];
388 for (const int point : points) {
389 GPU_indexbuf_add_generic_vert(&epb, point + drawing_start_offset);
390 }
391 });
392 }
393
394 drawing_start_offset += curves.points_num();
395 }
396
399
400 /* Create the batches. */
404
408
409 /* Allow creation of buffer texture. */
412
413 cache->is_dirty = false;
414}
415
417 const bke::greasepencil::Drawing &drawing,
418 int layer_index,
419 IndexMaskMemory &memory)
420{
421 const bke::CurvesGeometry &curves = drawing.strokes();
422
423 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
424 return IndexMask(0);
425 }
426
427 const Array<int> point_to_curve_map = curves.point_to_curve_map();
428 const VArray<int8_t> types = curves.curve_types();
429
430 const IndexMask editable_and_selected_curves =
432 object, drawing, layer_index, memory);
433
434 const IndexMask nurbs_points = IndexMask::from_predicate(
435 curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
436 const int curve_i = point_to_curve_map[point_i];
437 const bool is_selected = editable_and_selected_curves.contains(curve_i);
438 const bool is_nurbs = types[curve_i] == CURVE_TYPE_NURBS;
439 return is_selected && is_nurbs;
440 });
441
442 return nurbs_points;
443}
444
446 const bke::greasepencil::Drawing &drawing,
447 int layer_index,
448 IndexMaskMemory &memory)
449{
450 const bke::CurvesGeometry &curves = drawing.strokes();
451
452 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
453 return IndexMask(0);
454 }
455
456 const VArray<int8_t> types = curves.curve_types();
457
458 const IndexMask selected_editable_strokes =
460 object, drawing, layer_index, memory);
461
462 const IndexMask nurbs_curves = IndexMask::from_predicate(
463 curves.curves_range(), GrainSize(4096), memory, [&](const int64_t curve_i) {
464 return types[curve_i] == CURVE_TYPE_NURBS;
465 });
466
467 return IndexMask::from_intersection(selected_editable_strokes, nurbs_curves, memory);
468}
469
471 Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
472{
473 const bke::CurvesGeometry &curves = drawing.strokes();
475 object, drawing, memory);
476
477 const VArray<int8_t> types = curves.curve_types();
478 const IndexMask non_nurbs_curves = IndexMask::from_predicate(
479 curves.curves_range(), GrainSize(4096), memory, [&](const int64_t curve_i) {
480 return types[curve_i] != CURVE_TYPE_NURBS;
481 });
482
483 return IndexMask::from_intersection(visible_strokes, non_nurbs_curves, memory);
484}
485
487 const bke::greasepencil::Drawing &drawing,
488 const int layer_index,
489 IndexMaskMemory &memory,
490 const VArray<float> &selected_point,
491 const float4x4 &layer_space_to_object_space,
492 MutableSpan<float3> edit_line_points,
493 MutableSpan<float> edit_line_selection,
494 int *r_drawing_line_start_offset,
495 int *r_total_line_ids_num)
496{
498 object, drawing, layer_index, memory);
499 if (nurbs_curves.is_empty()) {
500 return;
501 }
502
503 const bke::CurvesGeometry &curves = drawing.strokes();
504 const Span<float3> positions = curves.positions();
505
507 object, drawing, layer_index, memory);
508 const IndexRange eval_slice = IndexRange(*r_drawing_line_start_offset, nurbs_points.size());
509
510 MutableSpan<float3> positions_eval_slice = edit_line_points.slice(eval_slice);
511
512 /* This will copy over the position but without the layer transform. */
513 array_utils::gather(positions, nurbs_points, positions_eval_slice);
514
515 /* Go through the position and apply the layer transform. */
516 threading::parallel_for(nurbs_points.index_range(), 1024, [&](const IndexRange range) {
517 copy_transformed_positions(
518 positions_eval_slice, range, layer_space_to_object_space, positions_eval_slice);
519 });
520
521 MutableSpan<float> selection_eval_slice = edit_line_selection.slice(eval_slice);
522
523 array_utils::gather(selected_point, nurbs_points, selection_eval_slice);
524
525 /* Add one point for each NURBS point. */
526 *r_drawing_line_start_offset += nurbs_points.size();
527 *r_total_line_ids_num += nurbs_points.size();
528
529 /* Add one id for the restart after every NURBS. */
530 *r_total_line_ids_num += nurbs_curves.size();
531}
532
534 const bke::greasepencil::Drawing &drawing,
535 int /*layer_index*/,
537 IndexMaskMemory &memory,
538 int *r_drawing_line_start_offset)
539{
540 const bke::CurvesGeometry &curves = drawing.strokes();
541 const VArray<bool> cyclic = curves.cyclic();
542 const OffsetIndices<int> points_by_curve_eval = curves.evaluated_points_by_curve();
543
544 const IndexMask visible_strokes_for_lines = grease_pencil_get_visible_non_nurbs_curves(
545 object, drawing, memory);
546
547 /* Fill line indices. */
548 visible_strokes_for_lines.foreach_index([&](const int curve_i) {
549 const IndexRange points = points_by_curve_eval[curve_i];
550 const bool is_cyclic = cyclic[curve_i];
551
552 for (const int point_i : points) {
553 GPU_indexbuf_add_generic_vert(elb, point_i + (*r_drawing_line_start_offset));
554 }
555
556 if (is_cyclic) {
557 GPU_indexbuf_add_generic_vert(elb, points.first() + (*r_drawing_line_start_offset));
558 }
559
561 });
562
563 *r_drawing_line_start_offset += curves.evaluated_points_num();
564}
565
567 const bke::greasepencil::Drawing &drawing,
568 int layer_index,
570 IndexMaskMemory &memory,
571 int *r_drawing_line_start_offset)
572{
573 const bke::CurvesGeometry &curves = drawing.strokes();
574 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
576 object, drawing, layer_index, memory);
577 if (nurbs_curves.is_empty()) {
578 return;
579 }
580
581 /* Add all NURBS points. */
582 nurbs_curves.foreach_index([&](const int curve_i) {
583 const IndexRange points = points_by_curve[curve_i];
584
585 for (const int point_i : points.index_range()) {
586 GPU_indexbuf_add_generic_vert(elb, point_i + (*r_drawing_line_start_offset));
587 }
588
590
591 *r_drawing_line_start_offset += points.size();
592 });
593}
594
596 const bke::greasepencil::Drawing &drawing,
597 int layer_index,
599 IndexMaskMemory &memory,
600 int *r_drawing_line_start_offset)
601{
603 object, drawing, layer_index, memory);
604 if (bezier_points.is_empty()) {
605 return;
606 }
607
608 /* Add all bezier points. */
609 for (const int point : bezier_points.index_range()) {
611 elb, point + bezier_points.size() * 0 + (*r_drawing_line_start_offset));
613 elb, point + bezier_points.size() * 1 + (*r_drawing_line_start_offset));
615 elb, point + bezier_points.size() * 2 + (*r_drawing_line_start_offset));
616
618 }
619
620 *r_drawing_line_start_offset += bezier_points.size() * 3;
621}
622
623static void index_buf_add_points(Object &object,
624 const bke::greasepencil::Drawing &drawing,
625 int layer_index,
627 IndexMaskMemory &memory,
628 int *r_drawing_start_offset)
629{
630 const bke::CurvesGeometry &curves = drawing.strokes();
631 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
632
633 /* Fill point indices. */
634 const IndexMask selected_editable_strokes =
636 object, drawing, layer_index, memory);
637
638 selected_editable_strokes.foreach_index([&](const int curve_i) {
639 const IndexRange points = points_by_curve[curve_i];
640 for (const int point : points) {
641 GPU_indexbuf_add_generic_vert(epb, point + (*r_drawing_start_offset));
642 }
643 });
644
645 *r_drawing_start_offset += curves.points_num();
646}
647
649 const bke::greasepencil::Drawing &drawing,
650 int layer_index,
652 IndexMaskMemory &memory,
653 int *r_drawing_start_offset)
654{
656 object, drawing, layer_index, memory);
657
658 if (bezier_points.is_empty()) {
659 return;
660 }
661
662 /* Add all bezier points. */
663 for (const int point : IndexRange(bezier_points.size() * 2)) {
664 GPU_indexbuf_add_generic_vert(epb, point + (*r_drawing_start_offset));
665 }
666
667 *r_drawing_start_offset += bezier_points.size() * 2;
668}
669
670/* Still use legacy vflag for GPv3 for now due to common shader defines. */
671#define GREASE_PENCIL_EDIT_POINT_SELECTED (1 << 0)
672#define GREASE_PENCIL_EDIT_STROKE_SELECTED (1 << 1)
673#define GREASE_PENCIL_EDIT_MULTIFRAME (1 << 2)
674#define GREASE_PENCIL_EDIT_STROKE_START (1 << 3)
675#define GREASE_PENCIL_EDIT_STROKE_END (1 << 4)
676#define GREASE_PENCIL_EDIT_POINT_DIMMED (1 << 5)
677
679 const GreasePencil &grease_pencil,
680 const Scene &scene)
681{
682 using namespace blender::bke::greasepencil;
683 BLI_assert(grease_pencil.runtime != nullptr);
684 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
685 grease_pencil.runtime->batch_cache);
686
687 if (cache->edit_points_pos != nullptr) {
688 return;
689 }
690
691 /* Should be discarded together. */
693
694 /* Get the visible drawings. */
696 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
697
698 const Span<const Layer *> layers = grease_pencil.layers();
699
700 static GPUVertFormat format_edit_points_pos = {0};
701 if (format_edit_points_pos.attr_len == 0) {
702 GPU_vertformat_attr_add(&format_edit_points_pos, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
703 }
704
705 static GPUVertFormat format_edit_line_pos = {0};
706 if (format_edit_line_pos.attr_len == 0) {
707 GPU_vertformat_attr_add(&format_edit_line_pos, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
708 }
709
710 static GPUVertFormat format_edit_points_selection = {0};
711 if (format_edit_points_selection.attr_len == 0) {
713 &format_edit_points_selection, "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
714 }
715
716 static GPUVertFormat format_edit_points_vflag = {0};
717 if (format_edit_points_vflag.attr_len == 0) {
718 GPU_vertformat_attr_add(&format_edit_points_vflag, "vflag", GPU_COMP_U32, 1, GPU_FETCH_INT);
719 }
720
721 static GPUVertFormat format_edit_line_selection = {0};
722 if (format_edit_line_selection.attr_len == 0) {
724 &format_edit_line_selection, "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
725 }
726
728 cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(format_edit_points_pos, vbo_flag);
729 cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(format_edit_points_selection,
730 vbo_flag);
731 cache->edit_points_vflag = GPU_vertbuf_create_with_format_ex(format_edit_points_vflag, vbo_flag);
732 cache->edit_line_pos = GPU_vertbuf_create_with_format_ex(format_edit_line_pos, vbo_flag);
733 cache->edit_line_selection = GPU_vertbuf_create_with_format_ex(format_edit_line_selection,
734 vbo_flag);
735
736 int total_points_num = 0;
737 for (const ed::greasepencil::DrawingInfo &info : drawings) {
738 const Layer &layer = *layers[info.layer_index];
739 /* Do not show points for locked layers. */
740 if (layer.is_locked()) {
741 continue;
742 }
743
744 const bke::CurvesGeometry &curves = info.drawing.strokes();
745 total_points_num += curves.points_num();
746 }
747
748 int total_line_points_num = 0;
749 for (const ed::greasepencil::DrawingInfo &info : drawings) {
750 const bke::CurvesGeometry &curves = info.drawing.strokes();
751 total_line_points_num += curves.evaluated_points_num();
752 }
753
754 int total_bezier_point_num = 0;
755 for (const ed::greasepencil::DrawingInfo &info : drawings) {
756 IndexMaskMemory memory;
758 object, info.drawing, info.layer_index, memory);
759
760 total_bezier_point_num += bezier_points.size();
761 }
762
763 for (const ed::greasepencil::DrawingInfo &info : drawings) {
764 IndexMaskMemory memory;
766 object, info.drawing, info.layer_index, memory);
767
768 /* Add one point for each NURBS point. */
769 total_line_points_num += nurbs_points.size();
770 }
771
772 /* Add two for each bezier point, (one left, one right). */
773 total_points_num += total_bezier_point_num * 2;
774 /* Add three for each bezier point, (one left, one right and one for the center point). */
775 total_line_points_num += total_bezier_point_num * 3;
776
777 if (total_points_num == 0) {
778 return;
779 }
780
781 GPU_vertbuf_data_alloc(*cache->edit_points_pos, total_points_num);
782 GPU_vertbuf_data_alloc(*cache->edit_points_selection, total_points_num);
783 GPU_vertbuf_data_alloc(*cache->edit_points_vflag, total_points_num);
784 GPU_vertbuf_data_alloc(*cache->edit_line_pos, total_line_points_num);
785 GPU_vertbuf_data_alloc(*cache->edit_line_selection, total_line_points_num);
786
787 MutableSpan<float3> edit_points = cache->edit_points_pos->data<float3>();
788 MutableSpan<float> edit_points_selection = cache->edit_points_selection->data<float>();
789 MutableSpan<uint32_t> edit_points_vflag = cache->edit_points_vflag->data<uint32_t>();
790 MutableSpan<float3> edit_line_points = cache->edit_line_pos->data<float3>();
791 MutableSpan<float> edit_line_selection = cache->edit_line_selection->data<float>();
792 edit_points_selection.fill(0.0f);
793 edit_points_vflag.fill(0);
794 edit_line_selection.fill(0.0f);
795
796 int visible_points_num = 0;
797 int total_line_ids_num = 0;
798 int drawing_start_offset = 0;
799 int drawing_line_start_offset = 0;
800 for (const ed::greasepencil::DrawingInfo &info : drawings) {
801 const Layer &layer = *layers[info.layer_index];
802 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
803 const bke::CurvesGeometry &curves = info.drawing.strokes();
804 const OffsetIndices<int> points_by_curve_eval = curves.evaluated_points_by_curve();
805 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
806
807 IndexMaskMemory memory;
808 const IndexMask visible_strokes_for_lines = grease_pencil_get_visible_non_nurbs_curves(
809 object, info.drawing, memory);
810
811 const IndexRange points(drawing_start_offset, curves.points_num());
812 const IndexRange points_eval(drawing_line_start_offset, curves.evaluated_points_num());
813
814 const Span<float3> positions = curves.positions();
815 if (!layer.is_locked()) {
816 MutableSpan<float3> positions_slice = edit_points.slice(points);
817 threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange range) {
818 copy_transformed_positions(positions, range, layer_space_to_object_space, positions_slice);
819 });
820 }
821
822 const Span<float3> positions_eval = curves.evaluated_positions();
823
824 MutableSpan<float3> positions_eval_slice = edit_line_points.slice(points_eval);
826 IndexRange(curves.evaluated_points_num()), 1024, [&](const IndexRange range) {
827 copy_transformed_positions(
828 positions_eval, range, layer_space_to_object_space, positions_eval_slice);
829 });
830
831 /* Do not show selection for locked layers. */
832 if (!layer.is_locked()) {
833
834 /* Flag the start and end points. */
835 for (const int curve_i : curves.curves_range()) {
836 const IndexRange points = points_by_curve[curve_i].shift(drawing_start_offset);
837 edit_points_vflag[points.first()] |= GREASE_PENCIL_EDIT_STROKE_START;
838 edit_points_vflag[points.last()] |= GREASE_PENCIL_EDIT_STROKE_END;
839 }
840
841 const IndexMask selected_editable_points =
843 object, info.drawing, info.layer_index, memory);
844
845 MutableSpan<float> selection_slice = edit_points_selection.slice(points);
846 index_mask::masked_fill(selection_slice, 1.0f, selected_editable_points);
847
848 MutableSpan<float> line_selection_slice = edit_line_selection.slice(points_eval);
849
850 /* Poly curves evaluated points match the curve points, no need to interpolate. */
851 if (curves.is_single_type(CURVE_TYPE_POLY)) {
852 array_utils::copy(selection_slice.as_span(), line_selection_slice);
853 }
854 else {
855 curves.ensure_can_interpolate_to_evaluated();
856 curves.interpolate_to_evaluated(selection_slice.as_span(), line_selection_slice);
857 }
858 }
859
860 drawing_line_start_offset += curves.evaluated_points_num();
861
862 /* Add one id for the restart after every curve. */
863 total_line_ids_num += visible_strokes_for_lines.size();
864 Array<int> size_per_editable_stroke(visible_strokes_for_lines.size());
866 points_by_curve_eval, visible_strokes_for_lines, size_per_editable_stroke);
867 /* Add one id for every non-cyclic segment. */
868 total_line_ids_num += std::accumulate(
869 size_per_editable_stroke.begin(), size_per_editable_stroke.end(), 0);
870 /* Add one id for the last segment of every cyclic curve. */
871 total_line_ids_num += array_utils::count_booleans(curves.cyclic(), visible_strokes_for_lines);
872
873 /* Do not show points for locked layers. */
874 if (layer.is_locked()) {
875 continue;
876 }
877
878 drawing_start_offset += curves.points_num();
879 const IndexMask selected_editable_strokes =
881 object, info.drawing, info.layer_index, memory);
882
883 Array<int> size_per_selected_editable_stroke(selected_editable_strokes.size());
885 points_by_curve, selected_editable_strokes, size_per_selected_editable_stroke);
886
887 /* Add one id for every point in a selected curve. */
888 visible_points_num += std::accumulate(
889 size_per_selected_editable_stroke.begin(), size_per_selected_editable_stroke.end(), 0);
890
891 const VArray<float> selected_point = *curves.attributes().lookup_or_default<float>(
892 ".selection", bke::AttrDomain::Point, true);
893
895 info.drawing,
896 info.layer_index,
897 memory,
898 selected_point,
899 layer_space_to_object_space,
900 edit_line_points,
901 edit_line_selection,
902 &drawing_line_start_offset,
903 &total_line_ids_num);
904
906 object, info.drawing, info.layer_index, memory);
907 if (bezier_points.is_empty()) {
908 continue;
909 }
910
911 const IndexRange left_slice = IndexRange(drawing_start_offset, bezier_points.size());
912 const IndexRange right_slice = IndexRange(drawing_start_offset + bezier_points.size(),
913 bezier_points.size());
914
915 MutableSpan<float3> positions_slice_left = edit_points.slice(left_slice);
916 MutableSpan<float3> positions_slice_right = edit_points.slice(right_slice);
917
918 const Span<float3> handles_left = curves.handle_positions_left();
919 const Span<float3> handles_right = curves.handle_positions_right();
920
921 /* This will copy over the position but without the layer transform. */
922 array_utils::gather(handles_left, bezier_points, positions_slice_left);
923 array_utils::gather(handles_right, bezier_points, positions_slice_right);
924
925 /* Go through the position and apply the layer transform. */
926 threading::parallel_for(bezier_points.index_range(), 1024, [&](const IndexRange range) {
927 copy_transformed_positions(
928 positions_slice_left, range, layer_space_to_object_space, positions_slice_left);
929 copy_transformed_positions(
930 positions_slice_right, range, layer_space_to_object_space, positions_slice_right);
931 });
932
933 const VArray<float> selected_left = *curves.attributes().lookup_or_default<float>(
934 ".selection_handle_left", bke::AttrDomain::Point, true);
935 const VArray<float> selected_right = *curves.attributes().lookup_or_default<float>(
936 ".selection_handle_right", bke::AttrDomain::Point, true);
937
938 MutableSpan<float> selection_slice_left = edit_points_selection.slice(left_slice);
939 MutableSpan<float> selection_slice_right = edit_points_selection.slice(right_slice);
940 array_utils::gather(selected_left, bezier_points, selection_slice_left);
941 array_utils::gather(selected_right, bezier_points, selection_slice_right);
942
943 const IndexRange eval_left_slice = IndexRange(drawing_line_start_offset, bezier_points.size());
944 const IndexRange eval_center_slice = IndexRange(
945 drawing_line_start_offset + bezier_points.size(), bezier_points.size());
946 const IndexRange eval_right_slice = IndexRange(
947 drawing_line_start_offset + bezier_points.size() * 2, bezier_points.size());
948
949 MutableSpan<float3> positions_eval_left_slice = edit_line_points.slice(eval_left_slice);
950 MutableSpan<float3> positions_eval_center_slice = edit_line_points.slice(eval_center_slice);
951 MutableSpan<float3> positions_eval_right_slice = edit_line_points.slice(eval_right_slice);
952
953 array_utils::copy(positions_slice_left.as_span(), positions_eval_left_slice);
954 array_utils::copy(positions_slice_right.as_span(), positions_eval_right_slice);
955
956 /* This will copy over the position but without the layer transform. */
957 array_utils::gather(positions, bezier_points, positions_eval_center_slice);
958
959 /* Go through the position and apply the layer transform. */
960 threading::parallel_for(bezier_points.index_range(), 1024, [&](const IndexRange range) {
961 copy_transformed_positions(positions_eval_center_slice,
962 range,
963 layer_space_to_object_space,
964 positions_eval_center_slice);
965 });
966
967 MutableSpan<float> selection_eval_slice_left = edit_line_selection.slice(eval_left_slice);
968 MutableSpan<float> selection_eval_slice_center = edit_line_selection.slice(eval_center_slice);
969 MutableSpan<float> selection_eval_slice_right = edit_line_selection.slice(eval_right_slice);
970 array_utils::copy(selection_slice_left.as_span(), selection_eval_slice_left);
971 array_utils::copy(selection_slice_right.as_span(), selection_eval_slice_right);
972
973 array_utils::gather(selected_point, bezier_points, selection_eval_slice_center);
974
975 /* Add two for each bezier point, (one left, one right). */
976 visible_points_num += bezier_points.size() * 2;
977 drawing_start_offset += bezier_points.size() * 2;
978
979 /* Add three for each bezier point, (one left, one right and one for the center point). */
980 drawing_line_start_offset += bezier_points.size() * 3;
981 total_line_ids_num += bezier_points.size() * 3;
982
983 /* Add one id for the restart after every bezier. */
984 total_line_ids_num += bezier_points.size();
985 }
986
990 total_line_ids_num,
992
996 visible_points_num,
998
999 /* Fill line index and point index buffers with data. */
1000 drawing_start_offset = 0;
1001 drawing_line_start_offset = 0;
1002 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1003 const Layer *layer = layers[info.layer_index];
1004 IndexMaskMemory memory;
1005
1007 object, info.drawing, info.layer_index, &elb, memory, &drawing_line_start_offset);
1008
1009 if (!layer->is_locked()) {
1011 object, info.drawing, info.layer_index, &elb, memory, &drawing_line_start_offset);
1013 object, info.drawing, info.layer_index, &elb, memory, &drawing_line_start_offset);
1015 object, info.drawing, info.layer_index, &epb, memory, &drawing_start_offset);
1017 object, info.drawing, info.layer_index, &epb, memory, &drawing_start_offset);
1018 }
1019 }
1020
1023
1024 /* Create the batches */
1029
1033
1034 /* Allow creation of buffer texture. */
1040
1041 cache->is_dirty = false;
1042}
1043
1044template<typename T>
1046{
1047 if (curves.is_single_type(CURVE_TYPE_POLY)) {
1048 return input;
1049 }
1050
1051 Array<T> out(curves.evaluated_points_num());
1052 curves.interpolate_to_evaluated(VArraySpan(input), out.as_mutable_span());
1053 return VArray<T>::ForContainer(std::move(out));
1054};
1055
1057 const GreasePencil &grease_pencil,
1058 const Scene &scene)
1059{
1060 using namespace blender::bke::greasepencil;
1061 BLI_assert(grease_pencil.runtime != nullptr);
1062 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1063 grease_pencil.runtime->batch_cache);
1064
1065 if (cache->vbo != nullptr) {
1066 return;
1067 }
1068
1069 /* Should be discarded together. */
1070 BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
1071 BLI_assert(cache->geom_batch == nullptr);
1072
1073 /* Get the visible drawings. */
1075 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
1076
1077 /* First, count how many vertices and triangles are needed for the whole object. Also record the
1078 * offsets into the curves for the vertices and triangles. */
1079 int total_verts_num = 0;
1080 int total_triangles_num = 0;
1081 int v_offset = 0;
1082 Vector<Array<int>> verts_start_offsets_per_visible_drawing;
1083 Vector<Array<int>> tris_start_offsets_per_visible_drawing;
1084 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1085 const bke::CurvesGeometry &curves = info.drawing.strokes();
1086 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1087 const VArray<bool> cyclic = curves.cyclic();
1088 IndexMaskMemory memory;
1090 object, info.drawing, memory);
1091
1092 const int num_curves = visible_strokes.size();
1093 const int verts_start_offsets_size = num_curves;
1094 const int tris_start_offsets_size = num_curves;
1095 Array<int> verts_start_offsets(verts_start_offsets_size);
1096 Array<int> tris_start_offsets(tris_start_offsets_size);
1097
1098 /* Calculate the triangle offsets for all the visible curves. */
1099 int t_offset = 0;
1100 int pos = 0;
1101 for (const int curve_i : curves.curves_range()) {
1102 IndexRange points = points_by_curve[curve_i];
1103 if (visible_strokes.contains(curve_i)) {
1104 tris_start_offsets[pos] = t_offset;
1105 pos++;
1106 }
1107 if (points.size() >= 3) {
1108 t_offset += points.size() - 2;
1109 }
1110 }
1111
1112 /* Calculate the vertex offsets for all the visible curves. */
1113 int num_cyclic = 0;
1114 int num_points = 0;
1115 visible_strokes.foreach_index([&](const int curve_i, const int pos) {
1116 IndexRange points = points_by_curve[curve_i];
1117 const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
1118
1119 if (is_cyclic) {
1120 num_cyclic++;
1121 }
1122
1123 verts_start_offsets[pos] = v_offset;
1124 v_offset += 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
1125 num_points += points.size();
1126 });
1127
1128 /* One vertex is stored before and after as padding. Cyclic strokes have one extra vertex. */
1129 total_verts_num += num_points + num_cyclic + num_curves * 2;
1130 total_triangles_num += (num_points + num_cyclic) * 2;
1131 total_triangles_num += info.drawing.triangles().size();
1132
1133 verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
1134 tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets));
1135 }
1136
1138 /* Create VBOs. */
1141 cache->vbo = GPU_vertbuf_create_with_format_ex(*format, vbo_flag);
1142 cache->vbo_col = GPU_vertbuf_create_with_format_ex(*format_col, vbo_flag);
1143 /* Add extra space at the end of the buffer because of quad load. */
1144 GPU_vertbuf_data_alloc(*cache->vbo, total_verts_num + 2);
1145 GPU_vertbuf_data_alloc(*cache->vbo_col, total_verts_num + 2);
1146
1150 /* Create IBO. */
1151 GPU_indexbuf_init(&ibo, GPU_PRIM_TRIS, total_triangles_num, 0xFFFFFFFFu);
1152
1153 /* Fill buffers with data. */
1154 for (const int drawing_i : drawings.index_range()) {
1155 const ed::greasepencil::DrawingInfo &info = drawings[drawing_i];
1156 const Layer &layer = grease_pencil.layer(info.layer_index);
1157 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
1158 const float4x4 object_space_to_layer_space = math::invert(layer_space_to_object_space);
1159 const bke::CurvesGeometry &curves = info.drawing.strokes();
1160 if (curves.evaluated_points_num() == 0) {
1161 continue;
1162 }
1163
1164 const bke::AttributeAccessor attributes = curves.attributes();
1165 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1166 const Span<float3> positions = curves.evaluated_positions();
1167 const VArray<bool> cyclic = curves.cyclic();
1168
1169 curves.ensure_can_interpolate_to_evaluated();
1170
1171 const VArray<float> radii = attribute_interpolate<float>(info.drawing.radii(), curves);
1172 const VArray<float> opacities = attribute_interpolate<float>(info.drawing.opacities(), curves);
1174 *attributes.lookup_or_default<float>("rotation", bke::AttrDomain::Point, 0.0f), curves);
1176 *attributes.lookup_or_default<ColorGeometry4f>(
1177 "vertex_color", bke::AttrDomain::Point, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)),
1178 curves);
1179
1180 /* Assumes that if the ".selection" attribute does not exist, all points are selected. */
1181 const VArray<float> selection_float = *attributes.lookup_or_default<float>(
1182 ".selection", bke::AttrDomain::Point, true);
1183 const VArray<int8_t> start_caps = *attributes.lookup_or_default<int8_t>(
1185 const VArray<int8_t> end_caps = *attributes.lookup_or_default<int8_t>(
1186 "end_cap", bke::AttrDomain::Curve, 0);
1187 const VArray<float> stroke_softness = *attributes.lookup_or_default<float>(
1188 "softness", bke::AttrDomain::Curve, 0.0f);
1189 const VArray<float> stroke_point_aspect_ratios = *attributes.lookup_or_default<float>(
1190 "aspect_ratio", bke::AttrDomain::Curve, 1.0f);
1191 const VArray<ColorGeometry4f> stroke_fill_colors = info.drawing.fill_colors();
1192 const VArray<int> materials = *attributes.lookup_or_default<int>(
1193 "material_index", bke::AttrDomain::Curve, 0);
1194 const VArray<float> u_translations = *attributes.lookup_or_default<float>(
1195 "u_translation", bke::AttrDomain::Curve, 0.0f);
1196 const VArray<float> u_scales = *attributes.lookup_or_default<float>(
1197 "u_scale", bke::AttrDomain::Curve, 1.0f);
1198 const VArray<float> fill_opacities = *attributes.lookup_or_default<float>(
1199 "fill_opacity", bke::AttrDomain::Curve, 1.0f);
1200
1201 const Span<uint3> triangles = info.drawing.triangles();
1202 const Span<float4x2> texture_matrices = info.drawing.texture_matrices();
1203 const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
1204 const Span<int> tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i];
1205 IndexMaskMemory memory;
1207 object, info.drawing, memory);
1208
1209 curves.ensure_evaluated_lengths();
1210
1211 auto populate_point = [&](IndexRange verts_range,
1212 int curve_i,
1213 int8_t start_cap,
1214 int8_t end_cap,
1215 int point_i,
1216 int idx,
1217 float u_stroke,
1218 const float4x2 &texture_matrix,
1219 GreasePencilStrokeVert &s_vert,
1220 GreasePencilColorVert &c_vert) {
1221 const float3 pos = math::transform_point(layer_space_to_object_space, positions[point_i]);
1222 copy_v3_v3(s_vert.pos, pos);
1223 /* GP data itself does not constrain radii to be positive, but drawing code expects it, and
1224 * use negative values as a special 'flag' to get rounded caps. */
1225 s_vert.radius = math::max(radii[point_i], 0.0f) *
1226 ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
1227 /* Convert to legacy "pixel" space. We divide here, because the shader expects the values to
1228 * be in the `px` space rather than world space. Otherwise the values will get clamped. */
1230 s_vert.opacity = opacities[point_i] *
1231 ((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
1232 s_vert.point_id = verts_range[idx];
1233 s_vert.stroke_id = verts_range.first();
1234 s_vert.mat = materials[curve_i] % GPENCIL_MATERIAL_BUFFER_LEN;
1235
1236 s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness(
1237 rotations[point_i], stroke_point_aspect_ratios[curve_i], stroke_softness[curve_i]);
1238 s_vert.u_stroke = u_stroke;
1239 copy_v2_v2(s_vert.uv_fill, texture_matrix * float4(pos, 1.0f));
1240
1241 copy_v4_v4(c_vert.vcol, vertex_colors[point_i]);
1242 copy_v4_v4(c_vert.fcol, stroke_fill_colors[curve_i]);
1243 c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + fill_opacities[curve_i];
1244
1245 int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
1246 GPU_indexbuf_add_tri_verts(&ibo, v_mat + 0, v_mat + 1, v_mat + 2);
1247 GPU_indexbuf_add_tri_verts(&ibo, v_mat + 2, v_mat + 1, v_mat + 3);
1248 };
1249
1250 visible_strokes.foreach_index([&](const int curve_i, const int pos) {
1251 const IndexRange points = points_by_curve[curve_i];
1252 const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
1253 const int verts_start_offset = verts_start_offsets[pos];
1254 const int tris_start_offset = tris_start_offsets[pos];
1255 const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
1256 const IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
1257 MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
1258 MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
1259 const float4x2 texture_matrix = texture_matrices[curve_i] * object_space_to_layer_space;
1260
1261 const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]);
1262
1263 /* First vertex is not drawn. */
1264 verts_slice.first().mat = -1;
1265
1266 /* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
1267 if (points.size() >= 3) {
1268 const Span<uint3> tris_slice = triangles.slice(tris_start_offset, points.size() - 2);
1269 for (const uint3 tri : tris_slice) {
1271 (verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT,
1272 (verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT,
1273 (verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT);
1274 }
1275 }
1276
1277 /* Write all the point attributes to the vertex buffers. Create a quad for each point. */
1278 const float u_scale = u_scales[curve_i];
1279 const float u_translation = u_translations[curve_i];
1280 for (const int i : IndexRange(points.size())) {
1281 const int idx = i + 1;
1282 const float u_stroke = u_scale * (i > 0 ? lengths[i - 1] : 0.0f) + u_translation;
1283 populate_point(verts_range,
1284 curve_i,
1285 start_caps[curve_i],
1286 end_caps[curve_i],
1287 points[i],
1288 idx,
1289 u_stroke,
1290 texture_matrix,
1291 verts_slice[idx],
1292 cols_slice[idx]);
1293 }
1294
1295 if (is_cyclic) {
1296 const int idx = points.size() + 1;
1297 const float u = points.size() > 1 ? lengths[points.size() - 1] : 0.0f;
1298 const float u_stroke = u_scale * u + u_translation;
1299 populate_point(verts_range,
1300 curve_i,
1301 start_caps[curve_i],
1302 end_caps[curve_i],
1303 points[0],
1304 idx,
1305 u_stroke,
1306 texture_matrix,
1307 verts_slice[idx],
1308 cols_slice[idx]);
1309 }
1310
1311 /* Last vertex is not drawn. */
1312 verts_slice.last().mat = -1;
1313 });
1314 }
1315
1316 /* Mark last 2 verts as invalid. */
1317 verts[total_verts_num + 0].mat = -1;
1318 verts[total_verts_num + 1].mat = -1;
1319 /* Also mark first vert as invalid. */
1320 verts[0].mat = -1;
1321
1322 /* Finish the IBO. */
1323 cache->ibo = GPU_indexbuf_build(&ibo);
1324 /* Create the batches */
1325 cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
1326 /* Allow creation of buffer texture. */
1327 GPU_vertbuf_use(cache->vbo);
1328 GPU_vertbuf_use(cache->vbo_col);
1329
1330 cache->is_dirty = false;
1331}
1332
1334 const GreasePencil &grease_pencil,
1335 const Scene &scene)
1336{
1337 using namespace blender::bke::greasepencil;
1338
1339 BLI_assert(grease_pencil.runtime != nullptr);
1340 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1341 grease_pencil.runtime->batch_cache);
1342
1343 if (cache->lines_batch != nullptr) {
1344 return;
1345 }
1346
1347 grease_pencil_geom_batch_ensure(object, grease_pencil, scene);
1348 uint32_t max_index = GPU_vertbuf_get_vertex_len(cache->vbo);
1349
1350 /* Get the visible drawings. */
1352 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
1353
1354 Vector<int> index_start_per_curve;
1355 Vector<bool> cyclic_per_curve;
1356 Vector<bool> is_onion_per_curve;
1357
1358 int index_len = 0;
1359 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1360 const bke::CurvesGeometry &curves = info.drawing.strokes();
1361 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1362 const VArray<bool> cyclic = curves.cyclic();
1363 IndexMaskMemory memory;
1365 object, info.drawing, memory);
1366
1367 visible_strokes.foreach_index([&](const int curve_i) {
1368 const IndexRange points = points_by_curve[curve_i];
1369 const int point_len = points.size();
1370 const int point_start = index_len;
1371 const bool is_cyclic = cyclic[curve_i] && (point_len > 2);
1372 /* Count the primitive restart. */
1373 index_len += point_len + (is_cyclic ? 1 : 0) + 1;
1374 /* Don't draw the onion frames in wireframe mode. */
1375 index_start_per_curve.append(point_start);
1376 cyclic_per_curve.append(is_cyclic);
1377 is_onion_per_curve.append(info.onion_id != 0);
1378 });
1379 }
1380 index_start_per_curve.append(index_len);
1381 const OffsetIndices<int> range_per_curve(index_start_per_curve, offset_indices::NoSortCheck{});
1382
1384 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, max_index);
1385
1387
1388 threading::parallel_for(cyclic_per_curve.index_range(), 1024, [&](const IndexRange range) {
1389 for (const int curve : range) {
1390 /* Drop the trailing restart index. */
1391 const IndexRange offset_range = range_per_curve[curve].drop_back(1);
1392 /* Shift the range by `curve` to account for the second padding vertices.
1393 * The first one is already accounted for during counting (as primitive restart). */
1394 const IndexRange index_range = offset_range.shift(curve + 1);
1395 if (is_onion_per_curve[curve]) {
1396 for (const int i : offset_range.index_range()) {
1397 indices[offset_range[i]] = gpu::RESTART_INDEX;
1398 }
1399 if (cyclic_per_curve[curve]) {
1400 indices[offset_range.last()] = gpu::RESTART_INDEX;
1401 }
1402 }
1403 else {
1404 for (const int i : offset_range.index_range()) {
1405 indices[offset_range[i]] = index_range[i];
1406 }
1407 if (cyclic_per_curve[curve]) {
1408 indices[offset_range.last()] = index_range.first();
1409 }
1410 }
1411 indices[offset_range.one_after_last()] = gpu::RESTART_INDEX;
1412 }
1413 });
1414
1416 GPU_indexbuf_build_in_place_ex(&elb, 0, max_index, true, ibo);
1417
1418 cache->lines_batch = GPU_batch_create_ex(
1419 GPU_PRIM_LINE_STRIP, cache->vbo, ibo, GPU_BATCH_OWNS_INDEX);
1420
1421 cache->is_dirty = false;
1422}
1423
1427{
1428 BLI_assert(grease_pencil->runtime != nullptr);
1429 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1430 grease_pencil->runtime->batch_cache);
1431 if (cache == nullptr) {
1432 return;
1433 }
1434 switch (mode) {
1436 cache->is_dirty = true;
1437 break;
1438 default:
1440 }
1441}
1442
1444{
1445 BLI_assert(grease_pencil->runtime != nullptr);
1446 if (!grease_pencil_batch_cache_valid(*grease_pencil)) {
1447 grease_pencil_batch_cache_clear(*grease_pencil);
1448 grease_pencil_batch_cache_init(*grease_pencil);
1449 }
1450}
1451
1453{
1454 grease_pencil_batch_cache_clear(*grease_pencil);
1455 MEM_delete(static_cast<GreasePencilBatchCache *>(grease_pencil->runtime->batch_cache));
1456 grease_pencil->runtime->batch_cache = nullptr;
1457}
1458
1459gpu::Batch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
1460{
1461 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1463 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1464
1465 return cache->geom_batch;
1466}
1467
1469{
1470 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1472 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1473
1474 return cache->edit_points;
1475}
1476
1478{
1479 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1481 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1482
1483 return cache->edit_lines;
1484}
1485
1487{
1488 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1490 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1491
1492 return cache->vbo;
1493}
1494
1496{
1497 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1499 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1500
1501 return cache->vbo_col;
1502}
1503
1505{
1506 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1508 grease_pencil_weight_batch_ensure(*ob, grease_pencil, *scene);
1509
1510 return cache->edit_points;
1511}
1512
1514{
1515 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1517 grease_pencil_weight_batch_ensure(*ob, grease_pencil, *scene);
1518
1519 return cache->edit_lines;
1520}
1521
1523{
1524 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
1526 grease_pencil_wire_batch_ensure(*ob, grease_pencil, *scene);
1527
1528 return cache->lines_batch;
1529}
1530
1531} // namespace blender::draw
Low-level operations for curves.
support for deformation groups and hooks.
Low-level operations for grease pencil that cannot be defined in the C++ header yet.
@ BKE_GREASEPENCIL_BATCH_DIRTY_ALL
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ GP_STROKE_CAP_TYPE_ROUND
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, eGPUBatchFlag owns_flag)
Definition gpu_batch.cc:56
#define GPU_batch_create(primitive_type, vertex_buf, index_buf)
Definition GPU_batch.hh:149
int GPU_batch_vertbuf_add(blender::gpu::Batch *batch, blender::gpu::VertBuf *vertex_buf, bool own_vbo)
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:205
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:51
void GPU_indexbuf_build_in_place_ex(GPUIndexBufBuilder *builder, uint index_min, uint index_max, bool uses_restart_indices, blender::gpu::IndexBuf *elem)
blender::MutableSpan< uint32_t > GPU_indexbuf_get_data(GPUIndexBufBuilder *)
#define GPU_INDEXBUF_DISCARD_SAFE(elem)
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len)
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *)
blender::gpu::IndexBuf * GPU_indexbuf_calloc()
blender::gpu::IndexBuf * GPU_indexbuf_build(GPUIndexBufBuilder *)
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v)
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len)
void GPU_indexbuf_add_tri_verts(GPUIndexBufBuilder *, uint v1, uint v2, uint v3)
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_PRIM_TRIS
void GPU_vertbuf_use(blender::gpu::VertBuf *)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
uint GPU_vertbuf_get_vertex_len(const blender::gpu::VertBuf *verts)
@ GPU_USAGE_STATIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ GPU_COMP_I32
@ GPU_COMP_U32
int64_t size() const
Definition BLI_array.hh:245
const T * end() const
Definition BLI_array.hh:314
const T * begin() const
Definition BLI_array.hh:310
constexpr int64_t first() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr Span< T > as_span() const
Definition BLI_span.hh:662
constexpr T & first() const
Definition BLI_span.hh:680
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
void materialize(MutableSpan< T > r_span) const
static VArray ForContainer(ContainerT container)
void append(const T &value)
IndexRange index_range() const
Span< float4x2 > texture_matrices() const
const bke::CurvesGeometry & strokes() const
VArray< ColorGeometry4f > fill_colors() const
VArray< float > opacities() const
MutableSpan< T > data()
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_intersection(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
bool contains(int64_t query_index) const
void foreach_index(Fn &&fn) const
#define cosf(x)
#define GREASE_PENCIL_EDIT_STROKE_START
#define GREASE_PENCIL_EDIT_STROKE_END
static bool is_cyclic(const Nurb *nu)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define rot(x, k)
static float verts[][3]
#define GPENCIL_MATERIAL_BUFFER_LEN
#define GP_IS_STROKE_VERTEX_BIT
#define GP_VERTEX_ID_SHIFT
IndexRange range
format
#define unit_float_to_uchar_clamp(val)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
int64_t count_booleans(const VArray< bool > &varray)
constexpr float LEGACY_RADIUS_CONVERSION_FACTOR
static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
static void grease_pencil_cache_add_nurbs(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory, const VArray< float > &selected_point, const float4x4 &layer_space_to_object_space, MutableSpan< float3 > edit_line_points, MutableSpan< float > edit_line_selection, int *r_drawing_line_start_offset, int *r_total_line_ids_num)
static void index_buf_add_nurbs_lines(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, GPUIndexBufBuilder *elb, IndexMaskMemory &memory, int *r_drawing_line_start_offset)
blender::gpu::Batch * DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
void DRW_grease_pencil_batch_cache_validate(GreasePencil *grase_pencil)
static void grease_pencil_batch_cache_clear(GreasePencil &grease_pencil)
static IndexMask grease_pencil_get_visible_nurbs_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static void index_buf_add_bezier_line_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, GPUIndexBufBuilder *epb, IndexMaskMemory &memory, int *r_drawing_start_offset)
blender::gpu::Batch * DRW_cache_grease_pencil_face_wireframe_get(const Scene *scene, Object *ob)
static void index_buf_add_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, GPUIndexBufBuilder *epb, IndexMaskMemory &memory, int *r_drawing_start_offset)
static GreasePencilBatchCache * grease_pencil_batch_cache_init(GreasePencil &grease_pencil)
static void grease_pencil_edit_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
static void grease_pencil_weight_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
void DRW_grease_pencil_batch_cache_free(GreasePencil *grase_pencil)
static VArray< T > attribute_interpolate(const VArray< T > &input, const bke::CurvesGeometry &curves)
static GreasePencilBatchCache * grease_pencil_batch_cache_get(GreasePencil &grease_pencil)
gpu::VertBuf * DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Object *ob)
blender::gpu::Batch * DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob)
static IndexMask grease_pencil_get_visible_non_nurbs_curves(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
static IndexMask grease_pencil_get_visible_nurbs_curves(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
blender::gpu::Batch * DRW_cache_grease_pencil_weight_lines_get(const Scene *scene, Object *ob)
static bool grease_pencil_batch_cache_is_edit_discarded(GreasePencilBatchCache *cache)
blender::gpu::Batch * DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob)
static void index_buf_add_line_points(Object &object, const bke::greasepencil::Drawing &drawing, int, GPUIndexBufBuilder *elb, IndexMaskMemory &memory, int *r_drawing_line_start_offset)
static GPUVertFormat * grease_pencil_stroke_format()
static GPUVertFormat * grease_pencil_color_format()
void DRW_grease_pencil_batch_cache_dirty_tag(GreasePencil *grase_pencil, int mode)
blender::gpu::Batch * DRW_cache_grease_pencil_weight_points_get(const Scene *scene, Object *ob)
static void grease_pencil_wire_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
static void copy_transformed_positions(const Span< float3 > src_positions, const IndexRange range, const float4x4 &transform, MutableSpan< float3 > dst_positions)
static void grease_pencil_geom_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float softness)
gpu::VertBuf * DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object *ob)
static void index_buf_add_bezier_lines(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, GPUIndexBufBuilder *elb, IndexMaskMemory &memory, int *r_drawing_line_start_offset)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_handle_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
IndexMask retrieve_visible_strokes(Object &object, const bke::greasepencil::Drawing &drawing, IndexMaskMemory &memory)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
CartesianBasis invert(const CartesianBasis &basis)
T max(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void gather_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
signed int int32_t
Definition stdint.h:77
signed char int8_t
Definition stdint.h:75
GreasePencilRuntimeHandle * runtime
const bke::greasepencil::Drawing & drawing