Blender V5.0
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_grease_pencil.h"
14#include "BKE_grease_pencil.hh"
15
16#include "BLI_array_utils.hh"
17#include "BLI_listbase.h"
18#include "BLI_offset_indices.hh"
19#include "BLI_task.hh"
20
22
23#include "DRW_engine.hh"
24#include "DRW_render.hh"
25
26#include "ED_curves.hh"
27#include "ED_grease_pencil.hh"
28
29#include "GPU_batch.hh"
30
31#include "draw_cache.hh"
32#include "draw_cache_impl.hh"
33
36
37namespace blender::draw {
38
39#define EDIT_CURVES_NURBS_CONTROL_POINT (1u)
40#define EDIT_CURVES_BEZIER_HANDLE (1u << 1)
41#define EDIT_CURVES_ACTIVE_HANDLE (1u << 2)
42/* Bezier curve control point lying on the curve.
43 * The one between left and right handles. */
44#define EDIT_CURVES_BEZIER_KNOT (1u << 3)
45#define EDIT_CURVES_HANDLE_TYPES_SHIFT (4u)
46
54 gpu::Batch *geom_batch;
55 gpu::Batch *lines_batch;
56 gpu::Batch *edit_points;
57 gpu::Batch *edit_lines;
58 gpu::Batch *edit_handles;
59
60 /* Crazy-space point positions for original points. */
62 /* Selection of original points. */
64 /* vflag of original points. */
66 /* Indices of visible points. */
68
69 /* Crazy-space point positions for all line points. */
71 /* Selection of line points. */
73 /* Indices for lines segments. */
75
76 /* Additional data needed for shader to choose color for each point in edit_points_pos.
77 * If first bit is set, then point is NURBS control point. EDIT_CURVES_NURBS_CONTROL_POINT is
78 * used to set and test. If second, then point is Bezier handle point. Set and tested with
79 * EDIT_CURVES_BEZIER_HANDLE.
80 * In Bezier case two handle types of HandleType are also encoded.
81 * Byte structure for Bezier knot point (handle middle point):
82 * | left handle type | right handle type | | BEZIER| NURBS|
83 * | 7 6 | 5 4 | 3 2 | 1 | 0 |
84 *
85 * If it is left or right handle point, then same handle type is repeated in both slots.
86 */
88
90
95};
96
97/* -------------------------------------------------------------------- */
100
101/* MUST match the format below. */
110
112{
113 static const GPUVertFormat format = []() {
115 GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32_32);
116 GPU_vertformat_attr_add(&format, "ma", gpu::VertAttrType::SINT_32_32_32_32);
117 GPU_vertformat_attr_add(&format, "uv", gpu::VertAttrType::SFLOAT_32_32_32_32);
118 return format;
119 }();
120 return &format;
121}
122
123/* MUST match the format below. */
125 float vcol[4]; /* Vertex color */
126 float fcol[4]; /* Fill color */
127};
128
130{
131 static const GPUVertFormat format = []() {
133 GPU_vertformat_attr_add(&format, "col", gpu::VertAttrType::SFLOAT_32_32_32_32);
134 GPU_vertformat_attr_add(&format, "fcol", gpu::VertAttrType::SFLOAT_32_32_32_32);
135 return format;
136 }();
137 return &format;
138}
139
141
142/* -------------------------------------------------------------------- */
145
146static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
147{
148 BLI_assert(grease_pencil.runtime != nullptr);
149 const GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
150 grease_pencil.runtime->batch_cache);
151 return (cache && cache->is_dirty == false &&
152 cache->cache_frame == grease_pencil.runtime->eval_frame);
153}
154
156{
157 BLI_assert(grease_pencil.runtime != nullptr);
158 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
159 grease_pencil.runtime->batch_cache);
160 if (cache == nullptr) {
161 cache = MEM_new<GreasePencilBatchCache>(__func__);
162 grease_pencil.runtime->batch_cache = cache;
163 }
164 else {
165 *cache = {};
166 }
167
168 cache->is_dirty = false;
169 cache->cache_frame = grease_pencil.runtime->eval_frame;
170
171 return cache;
172}
173
206
208{
209 BLI_assert(grease_pencil.runtime != nullptr);
210 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
211 grease_pencil.runtime->batch_cache);
212 if (!grease_pencil_batch_cache_valid(grease_pencil)) {
213 grease_pencil_batch_cache_clear(grease_pencil);
214 return grease_pencil_batch_cache_init(grease_pencil);
215 }
216
217 return cache;
218}
219
221
222/* -------------------------------------------------------------------- */
225
227 const float asp,
228 const float softness,
229 const float miter_angle)
230{
231 int32_t packed = 0;
232 /* Aspect uses 9 bits */
233 float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
234 /* Use the default aspect ratio of 1 when the value is outside of the valid range. */
235 if (asp_normalized <= 0.0f) {
236 asp_normalized = 1.0f;
237 }
238 packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
239 /* Store if inverted in the 9th bit. */
240 if (asp > 1.0f) {
241 packed |= 1 << 8;
242 }
243 /* Rotation uses 9 bits */
244 /* Rotation are in [-90..90] degree range, so we can encode the sign of the angle + the cosine
245 * because the cosine will always be positive. */
247 /* Store sine sign in 9th bit. */
248 if (rot < 0.0f) {
249 packed |= 1 << 17;
250 }
251 /* Hardness uses 8 bits */
252 packed |= int32_t(unit_float_to_uchar_clamp(1.0f - softness)) << 18;
253
254 /* Miter Angle uses the last 6 bits */
255 if (miter_angle <= GP_STROKE_MITER_ANGLE_ROUND) {
257 }
258 else if (miter_angle >= GP_STROKE_MITER_ANGLE_BEVEL) {
260 }
261 else {
262 const float miter_norm = (miter_angle / M_PI);
265 << 26;
266 }
267
268 return packed;
269}
270
273{
274 return cache->edit_points_pos == nullptr && cache->edit_line_indices == nullptr &&
275 cache->edit_points_indices == nullptr && cache->edit_points == nullptr &&
276 cache->edit_lines == nullptr;
277}
278
280 const GreasePencil &grease_pencil,
281 const Scene &scene)
282{
283 using namespace blender::bke::greasepencil;
284
285 constexpr float no_active_weight = 666.0f;
286
287 BLI_assert(grease_pencil.runtime != nullptr);
288 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
289 grease_pencil.runtime->batch_cache);
290
291 if (cache->edit_points_pos != nullptr) {
292 return;
293 }
294
295 /* Should be discarded together. */
297
298 /* Get active vertex group. */
299 const bDeformGroup *active_defgroup = static_cast<bDeformGroup *>(BLI_findlink(
300 &grease_pencil.vertex_group_names, grease_pencil.vertex_group_active_index - 1));
301 const char *active_defgroup_name = (active_defgroup == nullptr) ? "" : active_defgroup->name;
302
303 /* Get the visible drawings. */
305 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
306
307 const Span<const Layer *> layers = grease_pencil.layers();
308
309 static const GPUVertFormat format_points_pos = GPU_vertformat_from_attribute(
310 "pos", gpu::VertAttrType::SFLOAT_32_32_32);
311
312 static const GPUVertFormat format_points_weight = GPU_vertformat_from_attribute(
313 "selection", gpu::VertAttrType::SFLOAT_32);
314
316 cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(format_points_pos, vbo_flag);
317 cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(format_points_weight, vbo_flag);
318
319 int visible_points_num = 0;
320 int total_line_ids_num = 0;
321 int total_points_num = 0;
322 for (const ed::greasepencil::DrawingInfo &info : drawings) {
323 const bke::CurvesGeometry &curves = info.drawing.strokes();
324 total_points_num += curves.points_num();
325 }
326
327 if (total_points_num == 0) {
328 return;
329 }
330
331 GPU_vertbuf_data_alloc(*cache->edit_points_pos, total_points_num);
332 GPU_vertbuf_data_alloc(*cache->edit_points_selection, total_points_num);
333
334 MutableSpan<float3> points_pos = cache->edit_points_pos->data<float3>();
335 MutableSpan<float> points_weight = cache->edit_points_selection->data<float>();
336
337 int drawing_start_offset = 0;
338 for (const ed::greasepencil::DrawingInfo &info : drawings) {
339 const Layer &layer = *layers[info.layer_index];
340 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
341 const bke::CurvesGeometry &curves = info.drawing.strokes();
342 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
343 const VArray<bool> cyclic = curves.cyclic();
344 IndexMaskMemory memory;
346 object, info.drawing, memory);
347
348 const IndexRange points(drawing_start_offset, curves.points_num());
350 curves.positions(), layer_space_to_object_space, points_pos.slice(points));
351
352 /* Get vertex weights of the active vertex group in this drawing. */
353 const VArray<float> weights = *curves.attributes().lookup_or_default<float>(
354 active_defgroup_name, bke::AttrDomain::Point, no_active_weight);
355 MutableSpan<float> weights_slice = points_weight.slice(points);
356 weights.materialize(weights_slice);
357
358 drawing_start_offset += curves.points_num();
359
360 const int drawing_visible_points_num = offset_indices::sum_group_sizes(points_by_curve,
361 visible_strokes);
362
363 /* Add one id for the restart after every curve. */
364 total_line_ids_num += visible_strokes.size();
365 /* Add one id for every non-cyclic segment. */
366 total_line_ids_num += drawing_visible_points_num;
367 /* Add one id for the last segment of every cyclic curve. */
368 total_line_ids_num += array_utils::count_booleans(curves.cyclic(), visible_strokes);
369
370 /* Do not show weights for locked layers. */
371 if (layer.is_locked()) {
372 continue;
373 }
374
375 visible_points_num += drawing_visible_points_num;
376 }
377
378 GPUIndexBufBuilder lines_builder;
379 GPU_indexbuf_init_ex(&lines_builder, GPU_PRIM_LINE_STRIP, total_line_ids_num, total_points_num);
380 MutableSpan<uint> lines_data = GPU_indexbuf_get_data(&lines_builder);
381 int lines_ibo_index = 0;
382
383 GPUIndexBufBuilder points_builder;
384 GPU_indexbuf_init(&points_builder, GPU_PRIM_POINTS, visible_points_num, total_points_num);
385 MutableSpan<uint> points_data = GPU_indexbuf_get_data(&points_builder);
386 int points_ibo_index = 0;
387
388 /* Fill point index buffer with data. */
389 drawing_start_offset = 0;
390 for (const ed::greasepencil::DrawingInfo &info : drawings) {
391 const Layer *layer = layers[info.layer_index];
392 const bke::CurvesGeometry &curves = info.drawing.strokes();
393 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
394 const VArray<bool> cyclic = curves.cyclic();
395 IndexMaskMemory memory;
397 object, info.drawing, memory);
398
399 /* Fill line indices. */
400 visible_strokes.foreach_index([&](const int curve_i) {
401 const IndexRange points = points_by_curve[curve_i];
402 const bool is_cyclic = cyclic[curve_i];
403
404 for (const int point : points) {
405 lines_data[lines_ibo_index++] = point + drawing_start_offset;
406 }
407
408 if (is_cyclic) {
409 lines_data[lines_ibo_index++] = points.first() + drawing_start_offset;
410 }
411
412 lines_data[lines_ibo_index++] = gpu::RESTART_INDEX;
413 });
414
415 /* Fill point indices. */
416 if (!layer->is_locked()) {
417 visible_strokes.foreach_index([&](const int curve_i) {
418 const IndexRange points = points_by_curve[curve_i];
419 for (const int point : points) {
420 points_data[points_ibo_index++] = point + drawing_start_offset;
421 }
422 });
423 }
424
425 drawing_start_offset += curves.points_num();
426 }
427
428 cache->edit_line_indices = GPU_indexbuf_build_ex(&lines_builder, 0, total_points_num, true);
429 cache->edit_points_indices = GPU_indexbuf_build_ex(&points_builder, 0, total_points_num, false);
430
431 /* Create the batches. */
435
439
440 /* Allow creation of buffer texture. */
443
444 cache->is_dirty = false;
445}
446
448 const bke::greasepencil::Drawing &drawing,
449 int layer_index,
450 IndexMaskMemory &memory)
451{
452 const bke::CurvesGeometry &curves = drawing.strokes();
453
454 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
455 return IndexMask(0);
456 }
457
458 const Array<int> point_to_curve_map = curves.point_to_curve_map();
459 const VArray<int8_t> types = curves.curve_types();
460
461 const IndexMask editable_and_selected_curves =
463 object, drawing, layer_index, memory);
464
465 const IndexMask nurbs_points = IndexMask::from_predicate(
466 curves.points_range(), GrainSize(4096), memory, [&](const int64_t point_i) {
467 const int curve_i = point_to_curve_map[point_i];
468 const bool is_selected = editable_and_selected_curves.contains(curve_i);
469 const bool is_nurbs = types[curve_i] == CURVE_TYPE_NURBS;
470 return is_selected && is_nurbs;
471 });
472
473 return nurbs_points;
474}
475
477 const bke::greasepencil::Drawing &drawing,
478 int layer_index,
479 IndexMaskMemory &memory)
480{
481 const bke::CurvesGeometry &curves = drawing.strokes();
482
483 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
484 return IndexMask(0);
485 }
486
487 const IndexMask selected_editable_strokes =
489 object, drawing, layer_index, memory);
490
491 const VArray<int8_t> types = curves.curve_types();
493 selected_editable_strokes, GrainSize(4096), memory, [&](const int64_t curve_i) {
494 return types[curve_i] == CURVE_TYPE_NURBS;
495 });
496}
497
499 Object &object,
500 const bke::greasepencil::Drawing &drawing,
501 const int layer_index,
502 IndexMaskMemory &memory)
503{
504 const bke::CurvesGeometry &curves = drawing.strokes();
506 object, drawing, layer_index, memory);
507 if (!curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
508 return visible_strokes;
509 }
510
511 const VArray<int8_t> types = curves.curve_types();
513 visible_strokes, GrainSize(4096), memory, [&](const int64_t curve) {
514 return types[curve] != CURVE_TYPE_NURBS;
515 });
516}
517
519 const bke::greasepencil::Drawing &drawing,
520 const int layer_index,
521 IndexMaskMemory &memory,
522 const VArray<float> &selected_point,
523 const float4x4 &layer_space_to_object_space,
524 MutableSpan<float3> edit_line_points,
525 MutableSpan<float> edit_line_selection,
526 int *r_drawing_line_start_offset,
527 int *r_total_line_ids_num)
528{
530 object, drawing, layer_index, memory);
531 if (nurbs_curves.is_empty()) {
532 return;
533 }
534
535 const bke::CurvesGeometry &curves = drawing.strokes();
536 const Span<float3> positions = curves.positions();
537
539 object, drawing, layer_index, memory);
540 const IndexRange eval_slice = IndexRange(*r_drawing_line_start_offset, nurbs_points.size());
541
542 MutableSpan<float3> positions_eval_slice = edit_line_points.slice(eval_slice);
543
544 array_utils::gather(positions, nurbs_points, positions_eval_slice);
545 math::transform_points(layer_space_to_object_space, positions_eval_slice);
546
547 MutableSpan<float> selection_eval_slice = edit_line_selection.slice(eval_slice);
548
549 array_utils::gather(selected_point, nurbs_points, selection_eval_slice);
550
551 /* Add one point for each NURBS point. */
552 *r_drawing_line_start_offset += nurbs_points.size();
553 *r_total_line_ids_num += nurbs_points.size();
554
555 /* Add one id for the restart after every NURBS. */
556 *r_total_line_ids_num += nurbs_curves.size();
557}
558
560 const bke::greasepencil::Drawing &drawing,
561 const int layer_index,
562 IndexMaskMemory &memory,
563 MutableSpan<uint> lines_data,
564 int *r_drawing_line_index,
565 int *r_drawing_line_start_offset)
566{
567 const bke::CurvesGeometry &curves = drawing.strokes();
568 const VArray<bool> cyclic = curves.cyclic();
569 const OffsetIndices<int> points_by_curve_eval = curves.evaluated_points_by_curve();
570
571 const IndexMask visible_strokes_for_lines = grease_pencil_get_visible_non_nurbs_curves(
572 object, drawing, layer_index, memory);
573
574 const int offset = *r_drawing_line_start_offset;
575 int line_index = *r_drawing_line_index;
576
577 /* Fill line indices. */
578 visible_strokes_for_lines.foreach_index([&](const int curve_i) {
579 const IndexRange points = points_by_curve_eval[curve_i];
580 const bool is_cyclic = cyclic[curve_i];
581
582 for (const int point : points) {
583 lines_data[line_index++] = point + offset;
584 }
585
586 if (is_cyclic) {
587 lines_data[line_index++] = points.first() + offset;
588 }
589
590 lines_data[line_index++] = gpu::RESTART_INDEX;
591 });
592
593 *r_drawing_line_index = line_index;
594 *r_drawing_line_start_offset += curves.evaluated_points_num();
595}
596
598 const bke::greasepencil::Drawing &drawing,
599 int layer_index,
600 IndexMaskMemory &memory,
601 MutableSpan<uint> lines_data,
602 int *r_drawing_line_index,
603 int *r_drawing_line_start_offset)
604{
605 const bke::CurvesGeometry &curves = drawing.strokes();
606 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
608 object, drawing, layer_index, memory);
609 if (nurbs_curves.is_empty()) {
610 return;
611 }
612
613 int line_index = *r_drawing_line_index;
614
615 /* Add all NURBS points. */
616 nurbs_curves.foreach_index([&](const int curve_i) {
617 const IndexRange points = points_by_curve[curve_i];
618
619 for (const int point : points.index_range()) {
620 lines_data[line_index++] = point + *r_drawing_line_start_offset;
621 }
622
623 lines_data[line_index++] = gpu::RESTART_INDEX;
624
625 *r_drawing_line_start_offset += points.size();
626 });
627
628 *r_drawing_line_index = line_index;
629}
630
631static void index_buf_add_bezier_handle_lines(const IndexMask bezier_points,
632 const int all_points,
633 MutableSpan<uint2> handle_lines,
634 int *r_drawing_line_index,
635 int *r_drawing_line_start_offset)
636{
637 if (bezier_points.is_empty()) {
638 return;
639 }
640
641 const int offset = *r_drawing_line_start_offset;
642 int line_index = *r_drawing_line_index;
643
644 /* Add all bezier handle lines. */
645 bezier_points.foreach_index([&](const int point_i, const int pos) {
646 handle_lines[line_index++] = uint2(offset + all_points + pos + bezier_points.size() * 0,
647 offset + point_i);
648 handle_lines[line_index++] = uint2(offset + all_points + pos + bezier_points.size() * 1,
649 offset + point_i);
650 });
651
652 *r_drawing_line_index = line_index;
653}
654
655static void index_buf_add_points(Object &object,
656 const bke::greasepencil::Drawing &drawing,
657 int layer_index,
658 IndexMaskMemory &memory,
659 MutableSpan<uint> points_data,
660 int *r_drawing_point_index,
661 int *r_drawing_start_offset)
662{
663 const bke::CurvesGeometry &curves = drawing.strokes();
664 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
665
666 /* Fill point indices. */
667 const IndexMask selected_editable_strokes =
669 object, drawing, layer_index, memory);
670
671 const int offset = *r_drawing_start_offset;
672 int ibo_index = *r_drawing_point_index;
673
674 selected_editable_strokes.foreach_index([&](const int curve_i) {
675 const IndexRange points = points_by_curve[curve_i];
676 for (const int point : points) {
677 points_data[ibo_index++] = point + offset;
678 }
679 });
680
681 *r_drawing_point_index = ibo_index;
682 *r_drawing_start_offset += curves.points_num();
683}
684
685static uint32_t bezier_data_value(int8_t handle_type, bool is_active)
686{
688 (is_active ? EDIT_CURVES_ACTIVE_HANDLE : 0);
689}
690
691static void index_buf_add_bezier_line_points(const IndexMask bezier_points,
692 MutableSpan<uint> points_data,
693 int *r_drawing_point_index,
694 int *r_drawing_start_offset)
695{
696 if (bezier_points.is_empty()) {
697 return;
698 }
699
700 const int offset = *r_drawing_start_offset;
701 int ibo_index = *r_drawing_point_index;
702
703 /* Add all bezier points. */
704 for (const int point : IndexRange(bezier_points.size() * 2)) {
705 points_data[ibo_index++] = point + offset;
706 }
707
708 *r_drawing_point_index = ibo_index;
709 *r_drawing_start_offset += bezier_points.size() * 2;
710}
711
712/* Still use legacy vflag for GPv3 for now due to common shader defines. */
713#define GREASE_PENCIL_EDIT_POINT_SELECTED (1 << 0)
714#define GREASE_PENCIL_EDIT_STROKE_SELECTED (1 << 1)
715#define GREASE_PENCIL_EDIT_MULTIFRAME (1 << 2)
716#define GREASE_PENCIL_EDIT_STROKE_START (1 << 3)
717#define GREASE_PENCIL_EDIT_STROKE_END (1 << 4)
718#define GREASE_PENCIL_EDIT_POINT_DIMMED (1 << 5)
719
721 const GreasePencil &grease_pencil,
722 const Scene &scene)
723{
724 using namespace blender::bke::greasepencil;
725 BLI_assert(grease_pencil.runtime != nullptr);
726 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
727 grease_pencil.runtime->batch_cache);
728
729 if (cache->edit_points_pos != nullptr) {
730 return;
731 }
732
733 /* Should be discarded together. */
735
736 /* Get the visible drawings. */
738 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, false);
739
740 const Span<const Layer *> layers = grease_pencil.layers();
741
742 static const GPUVertFormat format_edit_points_pos = GPU_vertformat_from_attribute(
743 "pos", gpu::VertAttrType::SFLOAT_32_32_32);
744
745 static const GPUVertFormat format_edit_line_pos = GPU_vertformat_from_attribute(
746 "pos", gpu::VertAttrType::SFLOAT_32_32_32);
747
748 static const GPUVertFormat format_edit_points_selection = GPU_vertformat_from_attribute(
749 "selection", gpu::VertAttrType::SFLOAT_32);
750
751 static const GPUVertFormat format_edit_points_vflag = GPU_vertformat_from_attribute(
752 "vflag", gpu::VertAttrType::UINT_32);
753
754 static const GPUVertFormat format_edit_line_selection = GPU_vertformat_from_attribute(
755 "selection", gpu::VertAttrType::SFLOAT_32);
756
757 static const GPUVertFormat format_edit_points_info = GPU_vertformat_from_attribute(
758 "data", gpu::VertAttrType::UINT_32);
759
761 cache->edit_points_pos = GPU_vertbuf_create_with_format_ex(format_edit_points_pos, vbo_flag);
762 cache->edit_points_selection = GPU_vertbuf_create_with_format_ex(format_edit_points_selection,
763 vbo_flag);
764 cache->edit_points_vflag = GPU_vertbuf_create_with_format_ex(format_edit_points_vflag, vbo_flag);
765 cache->edit_line_pos = GPU_vertbuf_create_with_format_ex(format_edit_line_pos, vbo_flag);
766 cache->edit_line_selection = GPU_vertbuf_create_with_format_ex(format_edit_line_selection,
767 vbo_flag);
768 cache->edit_points_info = GPU_vertbuf_create_with_format_ex(format_edit_points_info, vbo_flag);
769
770 int total_points_num = 0;
771 for (const ed::greasepencil::DrawingInfo &info : drawings) {
772 const Layer &layer = *layers[info.layer_index];
773 /* Do not show points for locked layers. */
774 if (layer.is_locked()) {
775 continue;
776 }
777
778 const bke::CurvesGeometry &curves = info.drawing.strokes();
779 total_points_num += curves.points_num();
780 }
781
782 int total_line_points_num = 0;
783 for (const ed::greasepencil::DrawingInfo &info : drawings) {
784 const bke::CurvesGeometry &curves = info.drawing.strokes();
785 total_line_points_num += curves.evaluated_points_num();
786 }
787
788 int total_bezier_point_num = 0;
789 for (const ed::greasepencil::DrawingInfo &info : drawings) {
790 IndexMaskMemory memory;
792 object, info.drawing, info.layer_index, CURVE_HANDLE_ALL, memory);
793
794 total_bezier_point_num += bezier_points.size();
795 }
796
797 for (const ed::greasepencil::DrawingInfo &info : drawings) {
798 IndexMaskMemory memory;
800 object, info.drawing, info.layer_index, memory);
801
802 /* Add one point for each NURBS point. */
803 total_line_points_num += nurbs_points.size();
804 }
805
806 /* Add two for each bezier point, (one left, one right). */
807 total_points_num += total_bezier_point_num * 2;
808
809 if (total_points_num == 0) {
810 return;
811 }
812
813 GPU_vertbuf_data_alloc(*cache->edit_points_pos, total_points_num);
814 GPU_vertbuf_data_alloc(*cache->edit_points_selection, total_points_num);
815 GPU_vertbuf_data_alloc(*cache->edit_points_vflag, total_points_num);
816 GPU_vertbuf_data_alloc(*cache->edit_line_pos, total_line_points_num);
817 GPU_vertbuf_data_alloc(*cache->edit_line_selection, total_line_points_num);
818 GPU_vertbuf_data_alloc(*cache->edit_points_info, total_points_num);
819
820 MutableSpan<float3> edit_points = cache->edit_points_pos->data<float3>();
821 MutableSpan<float> edit_points_selection = cache->edit_points_selection->data<float>();
822 MutableSpan<uint32_t> edit_points_vflag = cache->edit_points_vflag->data<uint32_t>();
823 MutableSpan<float3> edit_line_points = cache->edit_line_pos->data<float3>();
824 MutableSpan<float> edit_line_selection = cache->edit_line_selection->data<float>();
825 MutableSpan<uint32_t> edit_points_info = cache->edit_points_info->data<uint32_t>();
826 edit_points_selection.fill(0.0f);
827 edit_points_vflag.fill(0);
828 edit_points_info.fill(0);
829 edit_line_selection.fill(0.0f);
830
831 int visible_points_num = 0;
832 int total_line_ids_num = 0;
833 int total_bezier_num = 0;
834 int drawing_start_offset = 0;
835 int drawing_line_start_offset = 0;
836 for (const ed::greasepencil::DrawingInfo &info : drawings) {
837 const Layer &layer = *layers[info.layer_index];
838 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
839 const bke::CurvesGeometry &curves = info.drawing.strokes();
840 const OffsetIndices<int> points_by_curve_eval = curves.evaluated_points_by_curve();
841 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
842
843 IndexMaskMemory memory;
844 const IndexMask visible_strokes_for_lines = grease_pencil_get_visible_non_nurbs_curves(
845 object, info.drawing, info.layer_index, memory);
846
847 const IndexRange points(drawing_start_offset, curves.points_num());
848 const IndexRange points_eval(drawing_line_start_offset, curves.evaluated_points_num());
849
850 if (!layer.is_locked()) {
852 curves.positions(), layer_space_to_object_space, edit_points.slice(points));
853 }
854
855 math::transform_points(curves.evaluated_positions(),
856 layer_space_to_object_space,
857 edit_line_points.slice(points_eval));
858
859 /* Do not show selection for locked layers. */
860 if (!layer.is_locked()) {
861
862 /* Flag the start and end points. */
863 for (const int curve_i : curves.curves_range()) {
864 const IndexRange sub_points = points_by_curve[curve_i].shift(drawing_start_offset);
865 edit_points_vflag[sub_points.first()] |= GREASE_PENCIL_EDIT_STROKE_START;
866 edit_points_vflag[sub_points.last()] |= GREASE_PENCIL_EDIT_STROKE_END;
867 }
868
869 const IndexMask selected_editable_points =
871 object, info.drawing, info.layer_index, memory);
872
873 MutableSpan<float> selection_slice = edit_points_selection.slice(points);
874 index_mask::masked_fill(selection_slice, 1.0f, selected_editable_points);
875
876 MutableSpan<float> line_selection_slice = edit_line_selection.slice(points_eval);
877
878 /* Poly curves evaluated points match the curve points, no need to interpolate. */
879 if (curves.is_single_type(CURVE_TYPE_POLY)) {
880 array_utils::copy(selection_slice.as_span(), line_selection_slice);
881 }
882 else {
883 curves.ensure_can_interpolate_to_evaluated();
884 curves.interpolate_to_evaluated(selection_slice.as_span(), line_selection_slice);
885 }
886 }
887
888 drawing_line_start_offset += curves.evaluated_points_num();
889
890 /* Add one id for the restart after every curve. */
891 total_line_ids_num += visible_strokes_for_lines.size();
892 /* Add one id for every non-cyclic segment. */
893 total_line_ids_num += offset_indices::sum_group_sizes(points_by_curve_eval,
894 visible_strokes_for_lines);
895 /* Add one id for the last segment of every cyclic curve. */
896 total_line_ids_num += array_utils::count_booleans(curves.cyclic(), visible_strokes_for_lines);
897
898 /* Do not show points for locked layers. */
899 if (layer.is_locked()) {
900 continue;
901 }
902
903 drawing_start_offset += curves.points_num();
904 const IndexMask selected_editable_strokes =
906 object, info.drawing, info.layer_index, memory);
907
908 /* Add one id for every point in a selected curve. */
909 visible_points_num += offset_indices::sum_group_sizes(points_by_curve,
910 selected_editable_strokes);
911
912 const VArray<float> selected_point = *curves.attributes().lookup_or_default<float>(
913 ".selection", bke::AttrDomain::Point, true);
914
916 info.drawing,
917 info.layer_index,
918 memory,
919 selected_point,
920 layer_space_to_object_space,
921 edit_line_points,
922 edit_line_selection,
923 &drawing_line_start_offset,
924 &total_line_ids_num);
925
927 object, info.drawing, info.layer_index, CURVE_HANDLE_ALL, memory);
928 if (bezier_points.is_empty()) {
929 continue;
930 }
931
932 const IndexRange left_slice = IndexRange(drawing_start_offset, bezier_points.size());
933 const IndexRange right_slice = IndexRange(drawing_start_offset + bezier_points.size(),
934 bezier_points.size());
935
936 MutableSpan<float3> positions_slice_left = edit_points.slice(left_slice);
937 MutableSpan<float3> positions_slice_right = edit_points.slice(right_slice);
938
939 const Span<float3> handles_left = *curves.handle_positions_left();
940 const Span<float3> handles_right = *curves.handle_positions_right();
941
942 array_utils::gather(handles_left, bezier_points, positions_slice_left);
943 array_utils::gather(handles_right, bezier_points, positions_slice_right);
944
945 math::transform_points(layer_space_to_object_space, positions_slice_left);
946 math::transform_points(layer_space_to_object_space, positions_slice_right);
947
948 const VArray<float> selected_left = *curves.attributes().lookup_or_default<float>(
949 ".selection_handle_left", bke::AttrDomain::Point, true);
950 const VArray<float> selected_right = *curves.attributes().lookup_or_default<float>(
951 ".selection_handle_right", bke::AttrDomain::Point, true);
952
953 MutableSpan<float> selection_slice_left = edit_points_selection.slice(left_slice);
954 MutableSpan<float> selection_slice_right = edit_points_selection.slice(right_slice);
955 array_utils::gather(selected_left, bezier_points, selection_slice_left);
956 array_utils::gather(selected_right, bezier_points, selection_slice_right);
957
958 const VArray<int8_t> types_left = curves.handle_types_left();
959 const VArray<int8_t> types_right = curves.handle_types_right();
960
961 bezier_points.foreach_index([&](const int point_i, const int pos) {
962 const bool selected = selected_point[point_i] || selected_left[point_i] ||
963 selected_right[point_i];
964 edit_points_info.slice(left_slice)[pos] = bezier_data_value(types_left[point_i], selected);
965 edit_points_info.slice(right_slice)[pos] = bezier_data_value(types_right[point_i], selected);
966
967 edit_points_info.slice(points)[point_i] = EDIT_CURVES_BEZIER_KNOT;
968 });
969
970 /* Add two for each bezier point, (one left, one right). */
971 visible_points_num += bezier_points.size() * 2;
972 drawing_start_offset += bezier_points.size() * 2;
973
974 total_bezier_num += bezier_points.size();
975 }
976
977 GPUIndexBufBuilder lines_builder;
979 &lines_builder, GPU_PRIM_LINE_STRIP, total_line_ids_num, total_line_points_num);
980 MutableSpan<uint> lines_data = GPU_indexbuf_get_data(&lines_builder);
981 int lines_ibo_index = 0;
982
983 GPUIndexBufBuilder points_builder;
984 GPU_indexbuf_init(&points_builder, GPU_PRIM_POINTS, visible_points_num, total_points_num);
985 MutableSpan<uint> points_data = GPU_indexbuf_get_data(&points_builder);
986 int points_ibo_index = 0;
987
988 GPUIndexBufBuilder handles_builder;
989 GPU_indexbuf_init(&handles_builder, GPU_PRIM_LINES, total_bezier_num * 2, total_points_num);
990 MutableSpan<uint2> handle_lines = GPU_indexbuf_get_data(&handles_builder).cast<uint2>();
991
992 int handle_lines_id = 0;
993 /* Fill line index and point index buffers with data. */
994 drawing_start_offset = 0;
995 drawing_line_start_offset = 0;
996 for (const ed::greasepencil::DrawingInfo &info : drawings) {
997 const Layer *layer = layers[info.layer_index];
998 IndexMaskMemory memory;
999
1001 info.drawing,
1002 info.layer_index,
1003 memory,
1004 lines_data,
1005 &lines_ibo_index,
1006 &drawing_line_start_offset);
1007
1008 if (!layer->is_locked()) {
1010 object, info.drawing, info.layer_index, CURVE_HANDLE_ALL, memory);
1011
1013 info.drawing,
1014 info.layer_index,
1015 memory,
1016 lines_data,
1017 &lines_ibo_index,
1018 &drawing_line_start_offset);
1020 info.drawing.strokes().points_num(),
1021 handle_lines,
1022 &handle_lines_id,
1023 &drawing_start_offset);
1024 index_buf_add_points(object,
1025 info.drawing,
1026 info.layer_index,
1027 memory,
1028 points_data,
1029 &points_ibo_index,
1030 &drawing_start_offset);
1032 bezier_points, points_data, &points_ibo_index, &drawing_start_offset);
1033 }
1034 }
1035
1036 cache->edit_line_indices = GPU_indexbuf_build_ex(&lines_builder, 0, INT_MAX, true);
1037 cache->edit_points_indices = GPU_indexbuf_build_ex(&points_builder, 0, INT_MAX, false);
1038 cache->edit_handles_ibo = GPU_indexbuf_build_ex(&handles_builder, 0, INT_MAX, false);
1039
1040 /* Create the batches */
1045 GPU_batch_vertbuf_add(cache->edit_points, cache->edit_points_info, false);
1046
1050
1055
1056 /* Allow creation of buffer texture. */
1063
1064 cache->is_dirty = false;
1065}
1066
1067template<typename T>
1069{
1070 if (curves.is_single_type(CURVE_TYPE_POLY)) {
1071 return input;
1072 }
1073
1074 Array<T> out(curves.evaluated_points_num());
1075 curves.interpolate_to_evaluated(VArraySpan(input), out.as_mutable_span());
1076 return VArray<T>::from_container(std::move(out));
1077};
1078
1080{
1081 const VArray<float> miter_angles = *curves.attributes().lookup_or_default<float>(
1083
1084 if (curves.is_single_type(CURVE_TYPE_POLY)) {
1085 return miter_angles;
1086 }
1087
1088 if (miter_angles.is_single() &&
1090 {
1091 return VArray<float>::from_single(GP_STROKE_MITER_ANGLE_ROUND, curves.evaluated_points_num());
1092 }
1093
1094 /* Default all the evaluated points to be round.
1095 * This is done so that the added points look as smooth as possible. */
1096 Array<float> eval_corners(curves.evaluated_points_num(), GP_STROKE_MITER_ANGLE_ROUND);
1097
1098 const VArray<int8_t> types = curves.curve_types();
1099 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1100 const OffsetIndices<int> evaluated_points_by_curve = curves.evaluated_points_by_curve();
1101
1102 threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) {
1103 for (const int curve_i : range) {
1104 const IndexRange eval_points = evaluated_points_by_curve[curve_i];
1105 const IndexRange points = points_by_curve[curve_i];
1106 MutableSpan<float> eval_corners_range = eval_corners.as_mutable_span().slice(eval_points);
1107
1108 switch (types[curve_i]) {
1109 case CURVE_TYPE_POLY:
1110 for (const int i : points.index_range()) {
1111 eval_corners_range[i] = miter_angles[points[i]];
1112 }
1113 break;
1114 case CURVE_TYPE_BEZIER: {
1115 const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(curve_i);
1116 for (const int i : points.index_range()) {
1117 eval_corners_range[offsets[i]] = miter_angles[points[i]];
1118 }
1119 break;
1120 }
1121 case CURVE_TYPE_NURBS:
1122 case CURVE_TYPE_CATMULL_ROM: {
1123 /* NUBRS and Catmull-Rom are continuous and don't have corners. */
1124 break;
1125 }
1126 }
1127 }
1128 });
1129 return VArray<float>::from_container(std::move(eval_corners));
1130}
1131
1133 const GreasePencil &grease_pencil,
1134 const Scene &scene)
1135{
1136 using namespace blender::bke::greasepencil;
1137 BLI_assert(grease_pencil.runtime != nullptr);
1138 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1139 grease_pencil.runtime->batch_cache);
1140
1141 if (cache->vbo != nullptr) {
1142 return;
1143 }
1144
1145 /* Should be discarded together. */
1146 BLI_assert(cache->vbo == nullptr && cache->ibo == nullptr);
1147 BLI_assert(cache->geom_batch == nullptr);
1148
1149 /* Get the visible drawings. */
1151 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
1152
1153 /* First, count how many vertices and triangles are needed for the whole object. Also record the
1154 * offsets into the curves for the vertices and triangles. */
1155 int total_verts_num = 0;
1156 int total_triangles_num = 0;
1157 int v_offset = 0;
1158 Vector<Array<int>> verts_start_offsets_per_visible_drawing;
1159 Vector<Array<int>> tris_start_offsets_per_visible_drawing;
1160 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1161 const bke::CurvesGeometry &curves = info.drawing.strokes();
1162 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1163 const VArray<bool> cyclic = curves.cyclic();
1164 IndexMaskMemory memory;
1166 object, info.drawing, memory);
1167
1168 const int num_curves = visible_strokes.size();
1169 const int verts_start_offsets_size = num_curves;
1170 const int tris_start_offsets_size = num_curves;
1171 Array<int> verts_start_offsets(verts_start_offsets_size);
1172 Array<int> tris_start_offsets(tris_start_offsets_size);
1173
1174 /* Calculate the triangle offsets for all the visible curves. */
1175 int t_offset = 0;
1176 int pos = 0;
1177 for (const int curve_i : curves.curves_range()) {
1178 IndexRange points = points_by_curve[curve_i];
1179 if (visible_strokes.contains(curve_i)) {
1180 tris_start_offsets[pos] = t_offset;
1181 pos++;
1182 }
1183 if (points.size() >= 3) {
1184 t_offset += points.size() - 2;
1185 }
1186 }
1187
1188 /* Calculate the vertex offsets for all the visible curves. */
1189 int num_cyclic = 0;
1190 int num_points = 0;
1191 visible_strokes.foreach_index([&](const int curve_i, const int pos) {
1192 IndexRange points = points_by_curve[curve_i];
1193 const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
1194
1195 if (is_cyclic) {
1196 num_cyclic++;
1197 }
1198
1199 verts_start_offsets[pos] = v_offset;
1200 v_offset += 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
1201 num_points += points.size();
1202 });
1203
1204 /* One vertex is stored before and after as padding. Cyclic strokes have one extra vertex. */
1205 total_verts_num += num_points + num_cyclic + num_curves * 2;
1206 total_triangles_num += (num_points + num_cyclic) * 2;
1207 total_triangles_num += info.drawing.triangles().size();
1208
1209 verts_start_offsets_per_visible_drawing.append(std::move(verts_start_offsets));
1210 tris_start_offsets_per_visible_drawing.append(std::move(tris_start_offsets));
1211 }
1212
1214 /* Create VBOs. */
1216 const GPUVertFormat *format_col = grease_pencil_color_format();
1217 cache->vbo = GPU_vertbuf_create_with_format_ex(*format, vbo_flag);
1218 cache->vbo_col = GPU_vertbuf_create_with_format_ex(*format_col, vbo_flag);
1219 /* Add extra space at the end of the buffer because of quad load. */
1220 GPU_vertbuf_data_alloc(*cache->vbo, total_verts_num + 2);
1221 GPU_vertbuf_data_alloc(*cache->vbo_col, total_verts_num + 2);
1222
1225 /* Create IBO. */
1227 GPU_indexbuf_init(&ibo, GPU_PRIM_TRIS, total_triangles_num, INT_MAX);
1228 MutableSpan<uint3> triangle_ibo_data = GPU_indexbuf_get_data(&ibo).cast<uint3>();
1229 int triangle_ibo_index = 0;
1230
1231 /* Fill buffers with data. */
1232 for (const int drawing_i : drawings.index_range()) {
1233 const ed::greasepencil::DrawingInfo &info = drawings[drawing_i];
1234 const Layer &layer = grease_pencil.layer(info.layer_index);
1235 const float4x4 layer_space_to_object_space = layer.to_object_space(object);
1236 const float4x4 object_space_to_layer_space = math::invert(layer_space_to_object_space);
1237 const bke::CurvesGeometry &curves = info.drawing.strokes();
1238 if (curves.evaluated_points_num() == 0) {
1239 continue;
1240 }
1241
1242 const bke::AttributeAccessor attributes = curves.attributes();
1243 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1244 const Span<float3> positions = curves.evaluated_positions();
1245 const VArray<bool> cyclic = curves.cyclic();
1246
1247 curves.ensure_can_interpolate_to_evaluated();
1248
1252 *attributes.lookup_or_default<float>("rotation", bke::AttrDomain::Point, 0.0f), curves);
1255 "vertex_color", bke::AttrDomain::Point, ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)),
1256 curves);
1257 const VArray<float> miter_angles = interpolate_corners(curves);
1258
1259 /* Assumes that if the ".selection" attribute does not exist, all points are selected. */
1260 const VArray<float> selection_float = *attributes.lookup_or_default<float>(
1261 ".selection", bke::AttrDomain::Point, true);
1262 const VArray<int8_t> start_caps = *attributes.lookup_or_default<int8_t>(
1264 const VArray<int8_t> end_caps = *attributes.lookup_or_default<int8_t>(
1265 "end_cap", bke::AttrDomain::Curve, 0);
1266 const VArray<float> stroke_softness = *attributes.lookup_or_default<float>(
1267 "softness", bke::AttrDomain::Curve, 0.0f);
1268 const VArray<float> stroke_point_aspect_ratios = *attributes.lookup_or_default<float>(
1269 "aspect_ratio", bke::AttrDomain::Curve, 1.0f);
1270 const VArray<ColorGeometry4f> stroke_fill_colors = info.drawing.fill_colors();
1271 const VArray<int> materials = *attributes.lookup_or_default<int>(
1272 "material_index", bke::AttrDomain::Curve, 0);
1273 const VArray<float> u_translations = *attributes.lookup_or_default<float>(
1274 "u_translation", bke::AttrDomain::Curve, 0.0f);
1275 const VArray<float> u_scales = *attributes.lookup_or_default<float>(
1276 "u_scale", bke::AttrDomain::Curve, 1.0f);
1277 const VArray<float> fill_opacities = *attributes.lookup_or_default<float>(
1278 "fill_opacity", bke::AttrDomain::Curve, 1.0f);
1279
1280 const Span<int3> triangles = info.drawing.triangles();
1281 const Span<float4x2> texture_matrices = info.drawing.texture_matrices();
1282 const Span<int> verts_start_offsets = verts_start_offsets_per_visible_drawing[drawing_i];
1283 const Span<int> tris_start_offsets = tris_start_offsets_per_visible_drawing[drawing_i];
1284 IndexMaskMemory memory;
1286 object, info.drawing, memory);
1287
1288 curves.ensure_evaluated_lengths();
1289
1290 auto populate_point = [&](IndexRange verts_range,
1291 int curve_i,
1292 int8_t start_cap,
1293 int8_t end_cap,
1294 int point_i,
1295 int idx,
1296 float u_stroke,
1297 bool cyclic,
1298 const float4x2 &texture_matrix,
1299 GreasePencilStrokeVert &s_vert,
1300 GreasePencilColorVert &c_vert) {
1301 const float3 pos = math::transform_point(layer_space_to_object_space, positions[point_i]);
1302 copy_v3_v3(s_vert.pos, pos);
1303 /* GP data itself does not constrain radii to be positive, but drawing code expects it, and
1304 * use negative values as a special 'flag' to get rounded caps. */
1305 s_vert.radius = math::max(radii[point_i], 0.0f) *
1306 ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
1307 s_vert.opacity = opacities[point_i] *
1308 ((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
1309
1310 /* Store if the curve is cyclic in the sign of the point index. */
1311 s_vert.point_id = cyclic ? -verts_range[idx] : verts_range[idx];
1312 s_vert.stroke_id = verts_range.first();
1313
1314 /* The material index is allowed to be negative as it's stored as a generic attribute. To
1315 * ensure the material used by the shader is valid this needs to be clamped to zero. */
1316 s_vert.mat = std::max(materials[curve_i], 0) % GPENCIL_MATERIAL_BUFFER_LEN;
1317
1318 s_vert.packed_asp_hard_rot = pack_rotation_aspect_hardness_miter(
1319 rotations[point_i],
1320 stroke_point_aspect_ratios[curve_i],
1321 stroke_softness[curve_i],
1322 miter_angles[point_i]);
1323 s_vert.u_stroke = u_stroke;
1324 copy_v2_v2(s_vert.uv_fill, texture_matrix * float4(pos, 1.0f));
1325
1326 copy_v4_v4(c_vert.vcol, vertex_colors[point_i]);
1327 copy_v4_v4(c_vert.fcol, stroke_fill_colors[curve_i]);
1328 c_vert.fcol[3] = (int(c_vert.fcol[3] * 10000.0f) * 10.0f) + fill_opacities[curve_i];
1329
1330 int v_mat = (verts_range[idx] << GP_VERTEX_ID_SHIFT) | GP_IS_STROKE_VERTEX_BIT;
1331 triangle_ibo_data[triangle_ibo_index] = uint3(v_mat + 0, v_mat + 1, v_mat + 2);
1332 triangle_ibo_index++;
1333 triangle_ibo_data[triangle_ibo_index] = uint3(v_mat + 2, v_mat + 1, v_mat + 3);
1334 triangle_ibo_index++;
1335 };
1336
1337 visible_strokes.foreach_index([&](const int curve_i, const int pos) {
1338 const IndexRange points = points_by_curve[curve_i];
1339 const bool is_cyclic = cyclic[curve_i] && (points.size() > 2);
1340 const int verts_start_offset = verts_start_offsets[pos];
1341 const int tris_start_offset = tris_start_offsets[pos];
1342 const int num_verts = 1 + points.size() + (is_cyclic ? 1 : 0) + 1;
1343 const IndexRange verts_range = IndexRange(verts_start_offset, num_verts);
1344 MutableSpan<GreasePencilStrokeVert> verts_slice = verts.slice(verts_range);
1345 MutableSpan<GreasePencilColorVert> cols_slice = cols.slice(verts_range);
1346 const float4x2 texture_matrix = texture_matrices[curve_i] * object_space_to_layer_space;
1347
1348 const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]);
1349
1350 /* First vertex is not drawn. */
1351 verts_slice.first().mat = -1;
1352 /* The first vertex will have the index of the last vertex. */
1353 verts_slice.first().stroke_id = verts_range.last();
1354
1355 /* If the stroke has more than 2 points, add the triangle indices to the index buffer. */
1356 if (points.size() >= 3) {
1357 const Span<int3> tris_slice = triangles.slice(tris_start_offset, points.size() - 2);
1358 for (const int3 tri : tris_slice) {
1359 triangle_ibo_data[triangle_ibo_index] = uint3(
1360 (verts_range[1] + tri.x) << GP_VERTEX_ID_SHIFT,
1361 (verts_range[1] + tri.y) << GP_VERTEX_ID_SHIFT,
1362 (verts_range[1] + tri.z) << GP_VERTEX_ID_SHIFT);
1363 triangle_ibo_index++;
1364 }
1365 }
1366
1367 /* Write all the point attributes to the vertex buffers. Create a quad for each point. */
1368 const float u_scale = u_scales[curve_i];
1369 const float u_translation = u_translations[curve_i];
1370 for (const int i : IndexRange(points.size())) {
1371 const int idx = i + 1;
1372 const float u_stroke = u_scale * (i > 0 ? lengths[i - 1] : 0.0f) + u_translation;
1373 populate_point(verts_range,
1374 curve_i,
1375 start_caps[curve_i],
1376 end_caps[curve_i],
1377 points[i],
1378 idx,
1379 u_stroke,
1380 is_cyclic,
1381 texture_matrix,
1382 verts_slice[idx],
1383 cols_slice[idx]);
1384 }
1385
1386 if (is_cyclic) {
1387 const int idx = points.size() + 1;
1388 const float u = points.size() > 1 ? lengths[points.size() - 1] : 0.0f;
1389 const float u_stroke = u_scale * u + u_translation;
1390 populate_point(verts_range,
1391 curve_i,
1392 start_caps[curve_i],
1393 end_caps[curve_i],
1394 points[0],
1395 idx,
1396 u_stroke,
1397 is_cyclic,
1398 texture_matrix,
1399 verts_slice[idx],
1400 cols_slice[idx]);
1401 }
1402
1403 /* Last vertex is not drawn. */
1404 verts_slice.last().mat = -1;
1405 });
1406 }
1407
1408 /* Mark last 2 verts as invalid. */
1409 verts[total_verts_num + 0].mat = -1;
1410 verts[total_verts_num + 1].mat = -1;
1411 /* Also mark first vert as invalid. */
1412 verts[0].mat = -1;
1413
1414 /* Finish the IBO. */
1415 cache->ibo = GPU_indexbuf_build_ex(&ibo, 0, INT_MAX, false);
1416 /* Create the batches */
1417 cache->geom_batch = GPU_batch_create(GPU_PRIM_TRIS, cache->vbo, cache->ibo);
1418 /* Allow creation of buffer texture. */
1419 GPU_vertbuf_use(cache->vbo);
1420 GPU_vertbuf_use(cache->vbo_col);
1421
1422 cache->is_dirty = false;
1423}
1424
1426 const GreasePencil &grease_pencil,
1427 const Scene &scene)
1428{
1429 using namespace blender::bke::greasepencil;
1430
1431 BLI_assert(grease_pencil.runtime != nullptr);
1432 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1433 grease_pencil.runtime->batch_cache);
1434
1435 if (cache->lines_batch != nullptr) {
1436 return;
1437 }
1438
1439 grease_pencil_geom_batch_ensure(object, grease_pencil, scene);
1440 uint32_t max_index = GPU_vertbuf_get_vertex_len(cache->vbo);
1441
1442 /* Get the visible drawings. */
1444 ed::greasepencil::retrieve_visible_drawings(scene, grease_pencil, true);
1445
1446 Vector<int> index_start_per_curve;
1447 Vector<bool> cyclic_per_curve;
1448 Vector<bool> is_onion_per_curve;
1449
1450 int index_len = 0;
1451 for (const ed::greasepencil::DrawingInfo &info : drawings) {
1452 const bke::CurvesGeometry &curves = info.drawing.strokes();
1453 const OffsetIndices<int> points_by_curve = curves.evaluated_points_by_curve();
1454 const VArray<bool> cyclic = curves.cyclic();
1455 IndexMaskMemory memory;
1457 object, info.drawing, memory);
1458
1459 visible_strokes.foreach_index([&](const int curve_i) {
1460 const IndexRange points = points_by_curve[curve_i];
1461 const int point_len = points.size();
1462 const int point_start = index_len;
1463 const bool is_cyclic = cyclic[curve_i] && (point_len > 2);
1464 /* Count the primitive restart. */
1465 index_len += point_len + (is_cyclic ? 1 : 0) + 1;
1466 /* Don't draw the onion frames in wireframe mode. */
1467 index_start_per_curve.append(point_start);
1468 cyclic_per_curve.append(is_cyclic);
1469 is_onion_per_curve.append(info.onion_id != 0);
1470 });
1471 }
1472 index_start_per_curve.append(index_len);
1473 const OffsetIndices<int> range_per_curve(index_start_per_curve, offset_indices::NoSortCheck{});
1474
1476 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, max_index);
1477
1479
1480 threading::parallel_for(cyclic_per_curve.index_range(), 1024, [&](const IndexRange range) {
1481 for (const int curve : range) {
1482 /* Drop the trailing restart index. */
1483 const IndexRange offset_range = range_per_curve[curve].drop_back(1);
1484 /* Shift the range by `curve` to account for the second padding vertices.
1485 * The first one is already accounted for during counting (as primitive restart). */
1486 const IndexRange index_range = offset_range.shift(curve + 1);
1487 if (is_onion_per_curve[curve]) {
1488 for (const int i : offset_range.index_range()) {
1489 indices[offset_range[i]] = gpu::RESTART_INDEX;
1490 }
1491 if (cyclic_per_curve[curve]) {
1492 indices[offset_range.last()] = gpu::RESTART_INDEX;
1493 }
1494 }
1495 else {
1496 for (const int i : offset_range.index_range()) {
1497 indices[offset_range[i]] = index_range[i];
1498 }
1499 if (cyclic_per_curve[curve]) {
1500 indices[offset_range.last()] = index_range.first();
1501 }
1502 }
1503 indices[offset_range.one_after_last()] = gpu::RESTART_INDEX;
1504 }
1505 });
1506
1507 gpu::IndexBuf *ibo = GPU_indexbuf_build_ex(&elb, 0, max_index, true);
1508
1509 cache->lines_batch = GPU_batch_create_ex(
1510 GPU_PRIM_LINE_STRIP, cache->vbo, ibo, GPU_BATCH_OWNS_INDEX);
1511
1512 cache->is_dirty = false;
1513}
1514
1516
1518{
1519 BLI_assert(grease_pencil->runtime != nullptr);
1520 GreasePencilBatchCache *cache = static_cast<GreasePencilBatchCache *>(
1521 grease_pencil->runtime->batch_cache);
1522 if (cache == nullptr) {
1523 return;
1524 }
1525 switch (mode) {
1527 cache->is_dirty = true;
1528 break;
1529 default:
1531 }
1532}
1533
1535{
1536 BLI_assert(grease_pencil->runtime != nullptr);
1537 if (!grease_pencil_batch_cache_valid(*grease_pencil)) {
1538 grease_pencil_batch_cache_clear(*grease_pencil);
1539 grease_pencil_batch_cache_init(*grease_pencil);
1540 }
1541}
1542
1544{
1545 grease_pencil_batch_cache_clear(*grease_pencil);
1546 MEM_delete(static_cast<GreasePencilBatchCache *>(grease_pencil->runtime->batch_cache));
1547 grease_pencil->runtime->batch_cache = nullptr;
1548}
1549
1550gpu::Batch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
1551{
1554 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1555
1556 return cache->geom_batch;
1557}
1558
1560{
1563 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1564
1565 /* Can be `nullptr` when there's no Grease Pencil drawing visible. */
1566 return cache->edit_points;
1567}
1568
1570{
1573 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1574
1575 /* Can be `nullptr` when there's no Grease Pencil drawing visible. */
1576 return cache->edit_lines;
1577}
1578
1580{
1583 grease_pencil_edit_batch_ensure(*ob, grease_pencil, *scene);
1584
1585 /* Can be `nullptr` when there's no Grease Pencil drawing visible. */
1586 return cache->edit_handles;
1587}
1588
1590{
1593 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1594
1595 return cache->vbo;
1596}
1597
1599{
1602 grease_pencil_geom_batch_ensure(*ob, grease_pencil, *scene);
1603
1604 return cache->vbo_col;
1605}
1606
1608{
1611 grease_pencil_weight_batch_ensure(*ob, grease_pencil, *scene);
1612
1613 /* Can be `nullptr` when there's no Grease Pencil drawing visible. */
1614 return cache->edit_points;
1615}
1616
1618{
1621 grease_pencil_weight_batch_ensure(*ob, grease_pencil, *scene);
1622
1623 /* Can be `nullptr` when there's no Grease Pencil drawing visible. */
1624 return cache->edit_lines;
1625}
1626
1628{
1631 grease_pencil_wire_batch_ensure(*ob, grease_pencil, *scene);
1632
1633 return cache->lines_batch;
1634}
1635
1636} // namespace blender::draw
Low-level operations for curves.
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:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
MINLINE int clamp_i(int value, int min, int max)
#define M_PI
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])
@ GP_STROKE_CAP_TYPE_ROUND
#define GP_STROKE_MITER_ANGLE_BEVEL
#define GP_STROKE_MITER_ANGLE_ROUND
@ CURVE_HANDLE_ALL
T & DRW_object_get_data_for_drawing(const Object &object)
#define GPU_batch_create(primitive_type, vertex_buf, index_buf)
Definition GPU_batch.hh:141
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:197
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, GPUBatchFlag owns_flag)
Definition gpu_batch.cc:51
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:46
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)
blender::gpu::IndexBuf * GPU_indexbuf_build_ex(GPUIndexBufBuilder *builder, uint index_min, uint index_max, bool uses_restart_indices)
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len)
@ GPU_PRIM_LINES
@ 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
GPUVertFormat GPU_vertformat_from_attribute(blender::StringRef name, blender::gpu::VertAttrType type)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
long long int int64_t
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
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:573
constexpr MutableSpan< NewT > cast() const
Definition BLI_span.hh:749
constexpr Span< T > as_span() const
Definition BLI_span.hh:661
constexpr T & first() const
Definition BLI_span.hh:679
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
void materialize(MutableSpan< T > r_span) const
static VArray from_single(T value, const int64_t size)
static VArray from_container(ContainerT container)
void append(const T &value)
IndexRange index_range() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType 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 EDIT_CURVES_ACTIVE_HANDLE
#define EDIT_CURVES_HANDLE_TYPES_SHIFT
#define EDIT_CURVES_BEZIER_KNOT
#define EDIT_CURVES_BEZIER_HANDLE
#define GREASE_PENCIL_EDIT_STROKE_START
#define GREASE_PENCIL_EDIT_STROKE_END
static bool is_cyclic(const Nurb *nu)
#define rot(x, k)
static ushort indices[]
static float verts[][3]
#define GPENCIL_MATERIAL_BUFFER_LEN
#define GP_IS_STROKE_VERTEX_BIT
#define GP_CORNER_TYPE_BEVEL_BITS
#define GP_CORNER_TYPE_ROUND_BITS
#define GP_VERTEX_ID_SHIFT
#define GP_CORNER_TYPE_MITER_NUMBER
uint pos
#define packed
#define input
#define out
format
static char ** types
Definition makesdna.cc:71
MINLINE unsigned char unit_float_to_uchar_clamp(float 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)
static bool grease_pencil_batch_cache_valid(const GreasePencil &grease_pencil)
static void index_buf_add_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory, MutableSpan< uint > points_data, int *r_drawing_point_index, int *r_drawing_start_offset)
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)
gpu::Batch * DRW_cache_grease_pencil_get(const Scene *scene, Object *ob)
void DRW_grease_pencil_batch_cache_validate(GreasePencil *grease_pencil)
static const GPUVertFormat * grease_pencil_stroke_format()
static void grease_pencil_batch_cache_clear(GreasePencil &grease_pencil)
static void index_buf_add_nurbs_lines(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory, MutableSpan< uint > lines_data, int *r_drawing_line_index, int *r_drawing_line_start_offset)
static IndexMask grease_pencil_get_visible_nurbs_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static VArray< float > interpolate_corners(const bke::CurvesGeometry &curves)
static IndexMask grease_pencil_get_visible_non_nurbs_curves(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory)
void DRW_grease_pencil_batch_cache_free(GreasePencil *grease_pencil)
gpu::Batch * DRW_cache_grease_pencil_face_wireframe_get(const Scene *scene, Object *ob)
static void index_buf_add_bezier_line_points(const IndexMask bezier_points, MutableSpan< uint > points_data, int *r_drawing_point_index, int *r_drawing_start_offset)
gpu::Batch * DRW_cache_grease_pencil_edit_handles_get(const Scene *scene, Object *ob)
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)
void DRW_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode)
static void grease_pencil_weight_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
static VArray< T > attribute_interpolate(const VArray< T > &input, const bke::CurvesGeometry &curves)
static GreasePencilBatchCache * grease_pencil_batch_cache_get(GreasePencil &grease_pencil)
static void index_buf_add_bezier_handle_lines(const IndexMask bezier_points, const int all_points, MutableSpan< uint2 > handle_lines, int *r_drawing_line_index, int *r_drawing_line_start_offset)
gpu::VertBuf * DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Object *ob)
gpu::Batch * DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob)
static IndexMask grease_pencil_get_visible_nurbs_curves(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static const GPUVertFormat * grease_pencil_color_format()
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)
gpu::Batch * DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob)
static uint32_t bezier_data_value(int8_t handle_type, bool is_active)
BLI_INLINE int32_t pack_rotation_aspect_hardness_miter(const float rot, const float asp, const float softness, const float miter_angle)
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 index_buf_add_line_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, IndexMaskMemory &memory, MutableSpan< uint > lines_data, int *r_drawing_line_index, int *r_drawing_line_start_offset)
static void grease_pencil_geom_batch_ensure(Object &object, const GreasePencil &grease_pencil, const Scene &scene)
gpu::VertBuf * DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object *ob)
IndexMask retrieve_visible_bezier_handle_points(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, const int handle_display, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, 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)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
constexpr uint32_t RESTART_INDEX
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)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
int sum_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask)
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:93
VecBase< uint32_t, 2 > uint2
VecBase< uint32_t, 3 > uint3
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 3 > int3
MatBase< float, 4, 2 > float4x2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
#define cosf
GreasePencilRuntimeHandle * runtime
const bke::greasepencil::Drawing & drawing
i
Definition text_draw.cc:230