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
10
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/* -------------------------------------------------------------------- */
74
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
113
114/* -------------------------------------------------------------------- */
117
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
190
191/* -------------------------------------------------------------------- */
194
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
1174 *attributes.lookup_or_default<float>("rotation", bke::AttrDomain::Point, 0.0f), curves);
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
1425
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
const T * end() const
Definition BLI_array.hh:314
const T * begin() const
Definition BLI_array.hh:310
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)
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange index_range() 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
void materialize(MutableSpan< T > r_span) const
static VArray ForContainer(ContainerT container)
void append(const T &value)
IndexRange index_range() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
Span< float4x2 > texture_matrices() const
const bke::CurvesGeometry & strokes() const
VArray< ColorGeometry4f > fill_colors() const
VArray< float > opacities() const
float4x4 to_object_space(const Object &object) const
MutableSpan< T > data()
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 ushort indices[]
static float verts[][3]
#define GPENCIL_MATERIAL_BUFFER_LEN
#define GP_IS_STROKE_VERTEX_BIT
#define GP_VERTEX_ID_SHIFT
format
static char ** types
Definition makesdna.cc:71
#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
VecBase< uint32_t, 3 > uint3
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
VecBase< float, 3 > float3
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