Blender V4.3
draw_cache_impl_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_array_utils.hh"
16#include "BLI_listbase.h"
17#include "BLI_math_base.h"
18#include "BLI_math_matrix.hh"
19#include "BLI_math_vector.hh"
21#include "BLI_span.hh"
22#include "BLI_task.hh"
23#include "BLI_utildefines.h"
24
25#include "DNA_curves_types.h"
26#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28
30
31#include "BKE_crazyspace.hh"
32#include "BKE_curves.hh"
33#include "BKE_curves_utils.hh"
34#include "BKE_customdata.hh"
35#include "BKE_geometry_set.hh"
36
37#include "GPU_batch.hh"
38#include "GPU_context.hh"
39#include "GPU_material.hh"
40#include "GPU_texture.hh"
41
42#include "DRW_render.hh"
43
44#include "draw_attributes.hh"
45#include "draw_cache_impl.hh" /* own include */
46#include "draw_cache_inline.hh"
47#include "draw_curves_private.hh" /* own include */
48
49namespace blender::draw {
50
51#define EDIT_CURVES_NURBS_CONTROL_POINT (1u)
52#define EDIT_CURVES_BEZIER_HANDLE (1u << 1)
53#define EDIT_CURVES_ACTIVE_HANDLE (1u << 2)
54#define EDIT_CURVES_HANDLE_TYPES_SHIFT (4u)
55
56/* ---------------------------------------------------------------------- */
57
60
61 gpu::Batch *edit_points;
62 gpu::Batch *edit_handles;
63
64 gpu::Batch *sculpt_cage;
66
67 /* Crazy-space point positions for original points. */
69
70 /* Additional data needed for shader to choose color for each point in edit_points_pos.
71 * If first bit is set, then point is NURBS control point. EDIT_CURVES_NURBS_CONTROL_POINT is
72 * used to set and test. If second, then point is Bezier handle point. Set and tested with
73 * EDIT_CURVES_BEZIER_HANDLE.
74 * In Bezier case two handle types of HandleType are also encoded.
75 * Byte structure for Bezier knot point (handle middle point):
76 * | left handle type | right handle type | | BEZIER| NURBS|
77 * | 7 6 | 5 4 | 3 2 | 1 | 0 |
78 *
79 * If it is left or right handle point, then same handle type is repeated in both slots.
80 */
82
83 /* Selection of original points. */
85
87
88 gpu::Batch *edit_curves_lines;
91
92 /* Whether the cache is invalid. */
94
100 std::mutex render_mutex;
101};
102
104
105static GPUVertFormat single_attr_vbo_format(const char *name,
106 const GPUVertCompType comp_type,
107 const uint comp_len,
108 const GPUVertFetchMode fetch_mode,
110{
112 attr_id = GPU_vertformat_attr_add(&format, name, comp_type, comp_len, fetch_mode);
113 return format;
114}
115
116static bool batch_cache_is_dirty(const Curves &curves)
117{
118 const CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
119 return (cache && cache->is_dirty == false);
120}
121
122static void init_batch_cache(Curves &curves)
123{
124 CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
125
126 if (!cache) {
127 cache = MEM_new<CurvesBatchCache>(__func__);
128 curves.batch_cache = cache;
129 }
130 else {
131 cache->eval_cache = {};
132 }
133
134 cache->is_dirty = false;
135}
136
137static void discard_attributes(CurvesEvalCache &eval_cache)
138{
139 for (const int i : IndexRange(GPU_MAX_ATTR)) {
141 }
142
143 for (const int j : IndexRange(GPU_MAX_ATTR)) {
145 }
146
148}
149
168
169static void clear_final_data(CurvesEvalFinalCache &final_cache)
170{
173 for (const int j : IndexRange(GPU_MAX_ATTR)) {
175 }
176}
177
178static void clear_eval_data(CurvesEvalCache &eval_cache)
179{
180 /* TODO: more granular update tagging. */
185
186 clear_final_data(eval_cache.final);
187
188 discard_attributes(eval_cache);
189}
190
191static void clear_batch_cache(Curves &curves)
192{
193 CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves.batch_cache);
194 if (!cache) {
195 return;
196 }
197
199 clear_edit_data(cache);
200}
201
203{
205 return *static_cast<CurvesBatchCache *>(curves.batch_cache);
206}
207
212
213static void fill_points_position_time_vbo(const OffsetIndices<int> points_by_curve,
214 const Span<float3> positions,
216 MutableSpan<float> hairLength_data)
217{
218 threading::parallel_for(points_by_curve.index_range(), 1024, [&](const IndexRange range) {
219 for (const int i_curve : range) {
220 const IndexRange points = points_by_curve[i_curve];
221
222 Span<float3> curve_positions = positions.slice(points);
223 MutableSpan<PositionAndParameter> curve_posTime_data = posTime_data.slice(points);
224
225 float total_len = 0.0f;
226 for (const int i_point : curve_positions.index_range()) {
227 if (i_point > 0) {
228 total_len += math::distance(curve_positions[i_point - 1], curve_positions[i_point]);
229 }
230 curve_posTime_data[i_point].position = curve_positions[i_point];
231 curve_posTime_data[i_point].parameter = total_len;
232 }
233 hairLength_data[i_curve] = total_len;
234
235 /* Assign length value. */
236 if (total_len > 0.0f) {
237 const float factor = 1.0f / total_len;
238 /* Divide by total length to have a [0-1] number. */
239 for (const int i_point : curve_positions.index_range()) {
240 curve_posTime_data[i_point].parameter *= factor;
241 }
242 }
243 }
244 });
245}
246
248 CurvesEvalCache &cache)
249{
250 GPUVertFormat format = {0};
252
256
257 GPUVertFormat length_format = {0};
258 GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
259
263
264 /* TODO: Only create hairLength VBO when necessary. */
265 fill_points_position_time_vbo(curves.points_by_curve(),
266 curves.positions(),
268 cache.proc_length_buf->data<float>());
269}
270
271static uint32_t bezier_data_value(int8_t handle_type, bool is_active)
272{
274 (is_active ? EDIT_CURVES_ACTIVE_HANDLE : 0);
275}
276
278 const bke::CurvesGeometry &curves,
279 const IndexMask bezier_curves,
280 const OffsetIndices<int> bezier_dst_offsets,
281 const bke::crazyspace::GeometryDeformation deformation,
282 CurvesBatchCache &cache)
283{
284 static GPUVertFormat format_pos = single_attr_vbo_format(
285 "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
286 /* GPU_COMP_U32 is used instead of GPU_COMP_U8 because depending on running hardware stride might
287 * still be 4. Thus adding complexity to the code and still sparing no memory. */
288 static GPUVertFormat format_data = single_attr_vbo_format(
289 "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
290
291 Span<float3> deformed_positions = deformation.positions;
292 const int bezier_point_count = bezier_dst_offsets.total_size();
293 const int size = deformed_positions.size() + bezier_point_count * 2;
296
299
301 pos_dst.take_front(deformed_positions.size()).copy_from(deformed_positions);
302
304
305 MutableSpan<uint32_t> handle_data_left(data_dst.data() + deformed_positions.size(),
306 bezier_point_count);
307 MutableSpan<uint32_t> handle_data_right(
308 data_dst.data() + deformed_positions.size() + bezier_point_count, bezier_point_count);
309
310 const Span<float3> left_handle_positions = curves.handle_positions_left();
311 const Span<float3> right_handle_positions = curves.handle_positions_right();
312 const VArray<int8_t> left_handle_types = curves.handle_types_left();
313 const VArray<int8_t> right_handle_types = curves.handle_types_right();
314 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
315
316 const VArray<bool> selection_attr = *curves.attributes().lookup_or_default<bool>(
317 ".selection", bke::AttrDomain::Point, true);
318
319 auto handle_other_curves = [&](const uint32_t fill_value, const bool mark_active) {
320 return [&, fill_value, mark_active](const IndexMask &selection) {
321 selection.foreach_index(GrainSize(256), [&](const int curve_i) {
322 const IndexRange points = points_by_curve[curve_i];
323 bool is_active = false;
324 if (mark_active) {
325 is_active = array_utils::count_booleans(selection_attr, points) > 0;
326 }
327 uint32_t data_value = fill_value | (is_active ? EDIT_CURVES_ACTIVE_HANDLE : 0u);
328 data_dst.slice(points).fill(data_value);
329 });
330 };
331 };
332
333 bke::curves::foreach_curve_by_type(
334 curves.curve_types(),
335 curves.curve_type_counts(),
336 curves.curves_range(),
337 handle_other_curves(0, false),
338 handle_other_curves(0, false),
339 [&](const IndexMask &selection) {
340 const VArray<bool> selection_left = *curves.attributes().lookup_or_default<bool>(
341 ".selection_handle_left", bke::AttrDomain::Point, true);
342 const VArray<bool> selection_right = *curves.attributes().lookup_or_default<bool>(
343 ".selection_handle_right", bke::AttrDomain::Point, true);
344
345 selection.foreach_index(GrainSize(256), [&](const int src_i, const int64_t dst_i) {
346 for (const int point : points_by_curve[src_i]) {
347 const int point_in_curve = point - points_by_curve[src_i].start();
348 const int dst_index = bezier_dst_offsets[dst_i].start() + point_in_curve;
349
351 bool is_active = selection_attr[point] || selection_left[point] ||
352 selection_right[point];
353 handle_data_left[dst_index] = bezier_data_value(left_handle_types[point], is_active);
354 handle_data_right[dst_index] = bezier_data_value(right_handle_types[point], is_active);
355 }
356 });
357 },
358 handle_other_curves(EDIT_CURVES_NURBS_CONTROL_POINT, true));
359
360 if (!bezier_point_count) {
361 return;
362 }
363
364 MutableSpan<float3> left_handles(pos_dst.data() + deformed_positions.size(), bezier_point_count);
365 MutableSpan<float3> right_handles(
366 pos_dst.data() + deformed_positions.size() + bezier_point_count, bezier_point_count);
367
368 /* TODO: Use deformed left_handle_positions and left_handle_positions. */
369 array_utils::gather_group_to_group(
370 points_by_curve, bezier_dst_offsets, bezier_curves, left_handle_positions, left_handles);
371 array_utils::gather_group_to_group(
372 points_by_curve, bezier_dst_offsets, bezier_curves, right_handle_positions, right_handles);
373}
374
376 const IndexMask bezier_curves,
377 const OffsetIndices<int> bezier_dst_offsets,
378 CurvesBatchCache &cache)
379{
380 static GPUVertFormat format_data = single_attr_vbo_format(
381 "selection", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
382
383 const int bezier_point_count = bezier_dst_offsets.total_size();
384 const int vert_count = curves.points_num() + bezier_point_count * 2;
387 MutableSpan<float> data = cache.edit_points_selection->data<float>();
388
389 const VArray<float> attribute = *curves.attributes().lookup_or_default<float>(
390 ".selection", bke::AttrDomain::Point, 1.0f);
391 attribute.materialize(data.slice(0, curves.points_num()));
392
393 if (!bezier_point_count) {
394 return;
395 }
396
397 const VArray<float> attribute_left = *curves.attributes().lookup_or_default<float>(
398 ".selection_handle_left", bke::AttrDomain::Point, 1.0f);
399 const VArray<float> attribute_right = *curves.attributes().lookup_or_default<float>(
400 ".selection_handle_right", bke::AttrDomain::Point, 1.0f);
401
402 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
403
404 IndexRange dst_range = IndexRange::from_begin_size(curves.points_num(), bezier_point_count);
405 array_utils::gather_group_to_group(
406 points_by_curve, bezier_dst_offsets, bezier_curves, attribute_left, data.slice(dst_range));
407
408 dst_range = dst_range.shift(bezier_point_count);
409 array_utils::gather_group_to_group(
410 points_by_curve, bezier_dst_offsets, bezier_curves, attribute_right, data.slice(dst_range));
411}
412
413static void create_sculpt_cage_ibo(const OffsetIndices<int> points_by_curve,
414 CurvesBatchCache &cache)
415{
416 const int points_num = points_by_curve.total_size();
417 const int curves_num = points_by_curve.size();
418 const int indices_num = points_num + curves_num;
419
421 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, indices_num, points_num);
422
423 for (const int i : points_by_curve.index_range()) {
424 const IndexRange points = points_by_curve[i];
425 for (const int i_point : points) {
426 GPU_indexbuf_add_generic_vert(&elb, i_point);
427 }
429 }
431}
432
434 const IndexMask bezier_curves,
435 const OffsetIndices<int> bezier_offsets,
436 const IndexMask other_curves,
437 CurvesBatchCache &cache)
438{
439 const int bezier_point_count = bezier_offsets.total_size();
440 /* Left and right handle will be appended for each Bezier point. */
441 const int vert_len = curves.points_num() + 2 * bezier_point_count;
442 /* For each point has 2 lines from 2 points. */
443 const int index_len_for_bezier_handles = 4 * bezier_point_count;
444 const VArray<bool> cyclic = curves.cyclic();
445 /* For curves like NURBS each control point except last generates two point line.
446 * If one point curves or two point cyclic curves are present, not all builder's buffer space
447 * will be used. */
448 const int index_len_for_other_handles = (curves.points_num() - bezier_point_count -
449 other_curves.size()) *
450 2 +
451 array_utils::count_booleans(cyclic, other_curves) * 2;
452 const int index_len = index_len_for_other_handles + index_len_for_bezier_handles;
453 /* Use two index buffer builders for the same underlying memory. */
454 GPUIndexBufBuilder elb, right_elb;
455 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINES, index_len, vert_len);
456 memcpy(&right_elb, &elb, sizeof(elb));
457 right_elb.index_len = 2 * bezier_point_count;
458
459 const OffsetIndices points_by_curve = curves.points_by_curve();
460
461 bezier_curves.foreach_index([&](const int64_t src_i, const int64_t dst_i) {
462 IndexRange bezier_points = points_by_curve[src_i];
463 const int index_shift = curves.points_num() - bezier_points.first() +
464 bezier_offsets[dst_i].first();
465 for (const int point : bezier_points) {
466 const int point_left_i = index_shift + point;
467 GPU_indexbuf_add_line_verts(&elb, point_left_i, point);
468 GPU_indexbuf_add_line_verts(&right_elb, point_left_i + bezier_point_count, point);
469 }
470 });
471 other_curves.foreach_index([&](const int64_t src_i) {
472 IndexRange curve_points = points_by_curve[src_i];
473 if (curve_points.size() <= 1) {
474 return;
475 }
476 for (const int point : curve_points.drop_back(1)) {
477 GPU_indexbuf_add_line_verts(&right_elb, point, point + 1);
478 }
479 if (cyclic[src_i] && curve_points.size() > 2) {
480 GPU_indexbuf_add_line_verts(&right_elb, curve_points.first(), curve_points.last());
481 }
482 });
483 GPU_indexbuf_join(&elb, &right_elb);
485}
486
488 const GPUVertFormat &format,
489 const int index,
490 const char * /*name*/)
491{
494
495 /* Create a destination buffer for the transform feedback. Sized appropriately */
496 /* Those are points! not line segments. */
498 cache.final.resolution * cache.curves_num);
499}
500
501static void ensure_control_point_attribute(const Curves &curves,
502 CurvesEvalCache &cache,
503 const DRW_AttributeRequest &request,
504 const int index,
505 const GPUVertFormat &format)
506{
507 if (cache.proc_attributes_buf[index] != nullptr) {
508 return;
509 }
510
512
515 gpu::VertBuf &attr_vbo = *cache.proc_attributes_buf[index];
516
517 GPU_vertbuf_data_alloc(attr_vbo,
518 request.domain == bke::AttrDomain::Point ? curves.geometry.point_num :
519 curves.geometry.curve_num);
520
521 const bke::AttributeAccessor attributes = curves.geometry.wrap().attributes();
522
523 /* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done
524 * by OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following
525 * the Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a
526 * similar texture state swizzle to map the attribute correctly as for volume attributes, so we
527 * can control the conversion ourselves. */
528 bke::AttributeReader<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>(
529 request.attribute_name, request.domain, {0.0f, 0.0f, 0.0f, 1.0f});
530
532
533 attribute.varray.materialize(vbo_span);
534}
535
536static void ensure_final_attribute(const Curves &curves,
537 CurvesEvalCache &cache,
538 const DRW_AttributeRequest &request,
539 const int index)
540{
541 char sampler_name[32];
543
544 GPUVertFormat format = {0};
545 /* All attributes use vec4, see comment below. */
547
548 ensure_control_point_attribute(curves, cache, request, index, format);
549
550 /* Existing final data may have been for a different attribute (with a different name or domain),
551 * free the data. */
553
554 /* Ensure final data for points. */
555 if (request.domain == bke::AttrDomain::Point) {
556 alloc_final_attribute_vbo(cache, format, index, sampler_name);
557 }
558}
559
560static void fill_curve_offsets_vbos(const OffsetIndices<int> points_by_curve,
561 GPUVertBufRaw &data_step,
562 GPUVertBufRaw &seg_step)
563{
564 for (const int i : points_by_curve.index_range()) {
565 const IndexRange points = points_by_curve[i];
566
567 *(uint *)GPU_vertbuf_raw_step(&data_step) = points.start();
568 *(ushort *)GPU_vertbuf_raw_step(&seg_step) = points.size() - 1;
569 }
570}
571
572static void create_curve_offsets_vbos(const OffsetIndices<int> points_by_curve,
573 CurvesEvalCache &cache)
574{
575 GPUVertBufRaw data_step, seg_step;
576
577 GPUVertFormat format_data = {0};
578 uint data_id = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
579
580 GPUVertFormat format_seg = {0};
581 uint seg_id = GPU_vertformat_attr_add(&format_seg, "data", GPU_COMP_U16, 1, GPU_FETCH_INT);
582
583 /* Curve Data. */
587 GPU_vertbuf_attr_get_raw_data(cache.proc_strand_buf, data_id, &data_step);
588
592 GPU_vertbuf_attr_get_raw_data(cache.proc_strand_seg_buf, seg_id, &seg_step);
593
594 fill_curve_offsets_vbos(points_by_curve, data_step, seg_step);
595}
596
598{
599 /* Same format as proc_point_buf. */
600 GPUVertFormat format = {0};
602
605
606 /* Create a destination buffer for the transform feedback. Sized appropriately */
607
608 /* Those are points! not line segments. */
609 uint point_len = cache.final.resolution * cache.curves_num;
610 /* Avoid creating null sized VBO which can lead to crashes on certain platforms. */
611 point_len = max_ii(1, point_len);
612
613 GPU_vertbuf_data_alloc(*cache.final.proc_buf, point_len);
614}
615
616static void calc_final_indices(const bke::CurvesGeometry &curves,
617 CurvesEvalCache &cache,
618 const int thickness_res)
619{
620 BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
621 /* Determine prim type and element count.
622 * NOTE: Metal backend uses non-restart prim types for optimal HW performance. */
623 bool use_strip_prims = (GPU_backend_get_type() != GPU_BACKEND_METAL);
624 int verts_per_curve;
625 GPUPrimType prim_type;
626
627 if (use_strip_prims) {
628 /* +1 for primitive restart */
629 verts_per_curve = cache.final.resolution * thickness_res;
630 prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
631 }
632 else {
633 /* Use full primitive type. */
634 prim_type = (thickness_res == 1) ? GPU_PRIM_LINES : GPU_PRIM_TRIS;
635 int verts_per_segment = ((prim_type == GPU_PRIM_LINES) ? 2 : 6);
636 verts_per_curve = (cache.final.resolution - 1) * verts_per_segment;
637 }
638
639 static GPUVertFormat format = {0};
641
642 /* initialize vertex format */
644
646 GPU_vertbuf_data_alloc(*vbo, 1);
647
648 gpu::IndexBuf *ibo = nullptr;
650 if (curves.curves_num()) {
651 ibo = GPU_indexbuf_build_curves_on_device(prim_type, curves.curves_num(), verts_per_curve);
652 owns_flag |= GPU_BATCH_OWNS_INDEX;
653 }
654 cache.final.proc_hairs = GPU_batch_create_ex(prim_type, vbo, ibo, owns_flag);
655}
656
657static bool ensure_attributes(const Curves &curves,
658 CurvesBatchCache &cache,
659 const GPUMaterial *gpu_material)
660{
661 const CustomData &cd_curve = curves.geometry.curve_data;
662 const CustomData &cd_point = curves.geometry.point_data;
663 CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
664
665 if (gpu_material) {
666 /* The following code should be kept in sync with `mesh_cd_calc_used_gpu_layers`. */
667 DRW_Attributes attrs_needed;
668 drw_attributes_clear(&attrs_needed);
669 ListBase gpu_attrs = GPU_material_attributes(gpu_material);
670 LISTBASE_FOREACH (const GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
671 const char *name = gpu_attr->name;
672 eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type);
673 int layer = -1;
674 std::optional<bke::AttrDomain> domain;
675
676 if (gpu_attr->type == CD_AUTO_FROM_NAME) {
677 /* We need to deduce what exact layer is used.
678 *
679 * We do it based on the specified name.
680 */
681 if (name[0] != '\0') {
682 layer = CustomData_get_named_layer(&cd_curve, CD_PROP_FLOAT2, name);
683 type = CD_MTFACE;
684 domain = bke::AttrDomain::Curve;
685
686 if (layer == -1) {
687 /* Try to match a generic attribute, we use the first attribute domain with a
688 * matching name. */
689 if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
690 domain = bke::AttrDomain::Point;
691 }
692 else if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
693 domain = bke::AttrDomain::Curve;
694 }
695 else {
696 domain.reset();
697 layer = -1;
698 }
699 }
700
701 if (layer == -1) {
702 continue;
703 }
704 }
705 else {
706 /* Fall back to the UV layer, which matches old behavior. */
707 type = CD_MTFACE;
708 }
709 }
710 else {
711 if (drw_custom_data_match_attribute(cd_curve, name, &layer, &type)) {
712 domain = bke::AttrDomain::Curve;
713 }
714 else if (drw_custom_data_match_attribute(cd_point, name, &layer, &type)) {
715 domain = bke::AttrDomain::Point;
716 }
717 }
718
719 switch (type) {
720 case CD_MTFACE: {
721 if (layer == -1) {
722 layer = (name[0] != '\0') ?
725 if (layer != -1) {
726 domain = bke::AttrDomain::Curve;
727 }
728 }
729 if (layer == -1) {
730 layer = (name[0] != '\0') ?
733 if (layer != -1) {
734 domain = bke::AttrDomain::Point;
735 }
736 }
737
738 if (layer != -1 && name[0] == '\0' && domain.has_value()) {
740 domain == bke::AttrDomain::Curve ? &cd_curve : &cd_point, CD_PROP_FLOAT2, layer);
741 }
742
743 if (layer != -1 && domain.has_value()) {
744 drw_attributes_add_request(&attrs_needed, name, CD_PROP_FLOAT2, layer, *domain);
745 }
746 break;
747 }
748
749 case CD_TANGENT:
750 case CD_ORCO:
751 break;
752
754 case CD_PROP_COLOR:
756 case CD_PROP_FLOAT3:
757 case CD_PROP_BOOL:
758 case CD_PROP_INT8:
759 case CD_PROP_INT32:
760 case CD_PROP_INT32_2D:
761 case CD_PROP_FLOAT:
762 case CD_PROP_FLOAT2: {
763 if (layer != -1 && domain.has_value()) {
764 drw_attributes_add_request(&attrs_needed, name, type, layer, *domain);
765 }
766 break;
767 }
768 default:
769 break;
770 }
771 }
772
773 if (!drw_attributes_overlap(&final_cache.attr_used, &attrs_needed)) {
774 /* Some new attributes have been added, free all and start over. */
775 for (const int i : IndexRange(GPU_MAX_ATTR)) {
778 }
779 drw_attributes_merge(&final_cache.attr_used, &attrs_needed, cache.render_mutex);
780 }
781 drw_attributes_merge(&final_cache.attr_used_over_time, &attrs_needed, cache.render_mutex);
782 }
783
784 bool need_tf_update = false;
785
786 for (const int i : IndexRange(final_cache.attr_used.num_requests)) {
787 const DRW_AttributeRequest &request = final_cache.attr_used.requests[i];
788
789 if (cache.eval_cache.final.attributes_buf[i] != nullptr) {
790 continue;
791 }
792
793 if (request.domain == bke::AttrDomain::Point) {
794 need_tf_update = true;
795 }
796
797 ensure_final_attribute(curves, cache.eval_cache, request, i);
798 }
799
800 return need_tf_update;
801}
802
803static void request_attribute(Curves &curves, const char *name)
804{
805 CurvesBatchCache &cache = get_batch_cache(curves);
806 CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
807
808 DRW_Attributes attributes{};
809
810 bke::CurvesGeometry &curves_geometry = curves.geometry.wrap();
811 std::optional<bke::AttributeMetaData> meta_data = curves_geometry.attributes().lookup_meta_data(
812 name);
813 if (!meta_data) {
814 return;
815 }
816 const bke::AttrDomain domain = meta_data->domain;
817 const eCustomDataType type = meta_data->data_type;
818 const CustomData &custom_data = domain == bke::AttrDomain::Point ? curves.geometry.point_data :
819 curves.geometry.curve_data;
820
822 &attributes, name, type, CustomData_get_named_layer(&custom_data, type, name), domain);
823
824 drw_attributes_merge(&final_cache.attr_used, &attributes, cache.render_mutex);
825}
826
827void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32])
828{
829 char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
830 GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
831 /* Attributes use auto-name. */
832 BLI_snprintf(r_sampler_name, 32, "a%s", attr_safe_name);
833}
834
836 CurvesEvalCache **r_cache,
837 const GPUMaterial *gpu_material,
838 const int subdiv,
839 const int thickness_res)
840{
841 const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
842 bool need_ft_update = false;
843
844 CurvesBatchCache &cache = get_batch_cache(*curves_id);
845 CurvesEvalCache &eval_cache = cache.eval_cache;
846
847 if (eval_cache.final.hair_subdiv != subdiv || eval_cache.final.thickres != thickness_res) {
848 /* If the subdivision or indexing settings have changed, the evaluation cache is cleared. */
849 clear_final_data(eval_cache.final);
850 eval_cache.final.hair_subdiv = subdiv;
851 eval_cache.final.thickres = thickness_res;
852 }
853
854 eval_cache.curves_num = curves.curves_num();
855 eval_cache.points_num = curves.points_num();
856
857 const int steps = 3; /* TODO: don't hard-code? */
858 eval_cache.final.resolution = 1 << (steps + subdiv);
859
860 /* Refreshed on combing and simulation. */
861 if (eval_cache.proc_point_buf == nullptr || DRW_vbo_requested(eval_cache.proc_point_buf)) {
862 create_points_position_time_vbo(curves, eval_cache);
863 need_ft_update = true;
864 }
865
866 /* Refreshed if active layer or custom data changes. */
867 if (eval_cache.proc_strand_buf == nullptr) {
868 create_curve_offsets_vbos(curves.points_by_curve(), eval_cache);
869 }
870
871 /* Refreshed only on subdiv count change. */
872 if (eval_cache.final.proc_buf == nullptr) {
873 alloc_final_points_vbo(eval_cache);
874 need_ft_update = true;
875 }
876
877 if (eval_cache.final.proc_hairs == nullptr) {
878 calc_final_indices(curves, eval_cache, thickness_res);
879 }
880 eval_cache.final.thickres = thickness_res;
881
882 need_ft_update |= ensure_attributes(*curves_id, cache, gpu_material);
883
884 *r_cache = &eval_cache;
885 return need_ft_update;
886}
887
889{
890 CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
891 if (cache == nullptr) {
892 return;
893 }
894 switch (mode) {
896 cache->is_dirty = true;
897 break;
898 default:
900 }
901}
902
904{
905 if (!batch_cache_is_dirty(*curves)) {
906 clear_batch_cache(*curves);
907 init_batch_cache(*curves);
908 }
909}
910
912{
913 clear_batch_cache(*curves);
914 CurvesBatchCache *batch_cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
915 MEM_delete(batch_cache);
916 curves->batch_cache = nullptr;
917}
918
920{
921 CurvesBatchCache *cache = static_cast<CurvesBatchCache *>(curves->batch_cache);
922 if (cache == nullptr) {
923 return;
924 }
925
926 bool do_discard = false;
927
928 CurvesEvalFinalCache &final_cache = cache->eval_cache.final;
929
930 if (drw_attributes_overlap(&final_cache.attr_used_over_time, &final_cache.attr_used)) {
931 final_cache.last_attr_matching_time = ctime;
932 }
933
934 if (ctime - final_cache.last_attr_matching_time > U.vbotimeout) {
935 do_discard = true;
936 }
937
939
940 if (do_discard) {
942 }
943}
944
946{
947 return max_ii(1, curves->totcol);
948}
949
951{
952 CurvesBatchCache &cache = get_batch_cache(*curves);
953 return DRW_batch_request(&cache.edit_points);
954}
955
957{
958 CurvesBatchCache &cache = get_batch_cache(*curves);
959 return DRW_batch_request(&cache.sculpt_cage);
960}
961
963{
964 CurvesBatchCache &cache = get_batch_cache(*curves);
965 return DRW_batch_request(&cache.edit_handles);
966}
967
969{
970 CurvesBatchCache &cache = get_batch_cache(*curves);
972}
973
975 const char *name,
976 bool *r_is_point_domain)
977{
978 CurvesBatchCache &cache = get_batch_cache(*curves);
979 CurvesEvalFinalCache &final_cache = cache.eval_cache.final;
980
981 request_attribute(*curves, name);
982
983 int request_i = -1;
984 for (const int i : IndexRange(final_cache.attr_used.num_requests)) {
985 if (STREQ(final_cache.attr_used.requests[i].attribute_name, name)) {
986 request_i = i;
987 break;
988 }
989 }
990 if (request_i == -1) {
991 *r_is_point_domain = false;
992 return nullptr;
993 }
994 switch (final_cache.attr_used.requests[request_i].domain) {
995 case bke::AttrDomain::Point:
996 *r_is_point_domain = true;
997 return &final_cache.attributes_buf[request_i];
998 case bke::AttrDomain::Curve:
999 *r_is_point_domain = false;
1000 return &cache.eval_cache.proc_attributes_buf[request_i];
1001 default:
1003 return nullptr;
1004 }
1005}
1006
1008{
1009 const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
1010 const VArray<bool> cyclic = curves.cyclic();
1011
1012 int edges_len = 0;
1013 for (const int i : curves.curves_range()) {
1014 edges_len += bke::curves::segments_num(points_by_curve[i].size(), cyclic[i]);
1015 }
1016
1017 const int index_len = edges_len + curves.curves_num() * 2;
1018
1020 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, points_by_curve.total_size());
1021
1022 for (const int i : curves.curves_range()) {
1023 const IndexRange points = points_by_curve[i];
1024 if (cyclic[i] && points.size() > 1) {
1025 GPU_indexbuf_add_generic_vert(&elb, points.last());
1026 }
1027 for (const int i_point : points) {
1028 GPU_indexbuf_add_generic_vert(&elb, i_point);
1029 }
1031 }
1032
1034}
1035
1037 const bke::CurvesGeometry &curves,
1038 const bke::crazyspace::GeometryDeformation & /*deformation*/,
1039 CurvesBatchCache &cache)
1040{
1041 static uint attr_id;
1044
1045 /* TODO: Deform curves using deformations. */
1046 const Span<float3> positions = curves.evaluated_positions();
1047
1049 GPU_vertbuf_data_alloc(*cache.edit_curves_lines_pos, positions.size());
1051}
1052
1054{
1055 Curves *curves_id = static_cast<Curves *>(ob->data);
1056 Object *ob_orig = DEG_get_original_object(ob);
1057 if (ob_orig == nullptr) {
1058 return;
1059 }
1060 const Curves *curves_orig_id = static_cast<Curves *>(ob_orig->data);
1061
1062 draw::CurvesBatchCache &cache = draw::get_batch_cache(*curves_id);
1063 const bke::CurvesGeometry &curves_orig = curves_orig_id->geometry.wrap();
1064
1065 IndexMaskMemory memory;
1066 const IndexMask bezier_curves = bke::curves::indices_for_type(curves_orig.curve_types(),
1067 curves_orig.curve_type_counts(),
1069 curves_orig.curves_range(),
1070 memory);
1071 Array<int> bezier_point_offset_data(bezier_curves.size() + 1);
1072 const OffsetIndices<int> bezier_offsets = offset_indices::gather_selected_offsets(
1073 curves_orig.points_by_curve(), bezier_curves, bezier_point_offset_data);
1074
1075 const bke::crazyspace::GeometryDeformation deformation =
1076 bke::crazyspace::get_evaluated_curves_deformation(ob, *ob_orig);
1077
1081 }
1087 }
1093 }
1097 }
1100 curves_orig, bezier_curves, bezier_offsets, deformation, cache);
1101 }
1103 create_edit_points_selection(curves_orig, bezier_curves, bezier_offsets, cache);
1104 }
1106 const IndexMask other_curves = bezier_curves.complement(curves_orig.curves_range(), memory);
1107 calc_edit_handles_ibo(curves_orig, bezier_curves, bezier_offsets, other_curves, cache);
1108 }
1110 create_sculpt_cage_ibo(curves_orig.points_by_curve(), cache);
1111 }
1112
1114 create_edit_points_position_vbo(curves_orig, deformation, cache);
1115 }
1116
1118 create_edit_lines_ibo(curves_orig, cache);
1119 }
1120}
1121
1122} // namespace blender::draw
@ BKE_CURVES_BATCH_DIRTY_ALL
Definition BKE_curves.h:39
Low-level operations for curves.
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, blender::StringRef name)
const char * CustomData_get_layer_name(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_render_layer(const CustomData *data, eCustomDataType type)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
MINLINE int max_ii(int a, int b)
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned short ushort
unsigned int uint
#define STREQ(a, b)
Object * DEG_get_original_object(Object *object)
@ CURVE_TYPE_BEZIER
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_COLOR
@ CD_PROP_QUATERNION
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_AUTO_FROM_NAME
Object is a sort of wrapper for general info.
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_DISCARD_SAFE(batch)
Definition GPU_batch.hh:205
eGPUBatchFlag
Definition GPU_batch.hh:37
@ GPU_BATCH_OWNS_INDEX
Definition GPU_batch.hh:51
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:42
eGPUBackendType GPU_backend_get_type()
#define GPU_INDEXBUF_DISCARD_SAFE(elem)
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *)
void GPU_indexbuf_add_line_verts(GPUIndexBufBuilder *, uint v1, uint v2)
void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *builder_from)
blender::gpu::IndexBuf * GPU_indexbuf_build_curves_on_device(GPUPrimType prim_type, uint curves_num, uint verts_per_curve)
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v)
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, blender::gpu::IndexBuf *)
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len)
ListBase GPU_material_attributes(const GPUMaterial *material)
GPUPrimType
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_PRIM_TRI_STRIP
@ GPU_PRIM_TRIS
#define GPU_MAX_ATTR
Definition GPU_shader.hh:29
void GPU_vertbuf_attr_get_raw_data(blender::gpu::VertBuf *, uint a_idx, GPUVertBufRaw *access)
GPU_INLINE void * GPU_vertbuf_raw_step(GPUVertBufRaw *a)
#define GPU_vertbuf_create_with_format(format)
void GPU_vertbuf_attr_fill(blender::gpu::VertBuf *, uint a_idx, const void *data)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
#define GPU_vertbuf_init_with_format(verts, format)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
@ GPU_USAGE_STATIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
@ GPU_USAGE_DEVICE_ONLY
void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint max_len)
GPUVertFetchMode
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
#define GPU_MAX_SAFE_ATTR_NAME
void GPU_vertformat_clear(GPUVertFormat *)
GPUVertCompType
@ GPU_COMP_U16
@ GPU_COMP_F32
@ GPU_COMP_U32
@ GPU_COMP_U8
Read Guarded memory(de)allocation.
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
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
constexpr int64_t first() const
constexpr IndexRange shift(int64_t n) const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr T * data() const
Definition BLI_span.hh:540
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr int64_t size() const
Definition BLI_span.hh:253
void materialize(MutableSpan< T > r_span) const
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
const std::array< int, CURVE_TYPES_NUM > & curve_type_counts() const
VArray< int8_t > curve_types() const
MutableSpan< T > data()
IndexMask slice(IndexRange range) const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
Utilities for rendering attributes.
#define EDIT_CURVES_ACTIVE_HANDLE
#define EDIT_CURVES_HANDLE_TYPES_SHIFT
#define EDIT_CURVES_BEZIER_HANDLE
#define EDIT_CURVES_NURBS_CONTROL_POINT
bool DRW_batch_requested(blender::gpu::Batch *batch, GPUPrimType prim_type)
blender::gpu::Batch * DRW_batch_request(blender::gpu::Batch **batch)
void DRW_vbo_request(blender::gpu::Batch *batch, blender::gpu::VertBuf **vbo)
bool DRW_vbo_requested(blender::gpu::VertBuf *vbo)
void DRW_ibo_request(blender::gpu::Batch *batch, blender::gpu::IndexBuf **ibo)
bool DRW_ibo_requested(blender::gpu::IndexBuf *ibo)
#define MAX_THICKRES
struct @620::@623 attr_id
format
int DRW_curves_material_count_get(const Curves *curves)
static void ensure_final_attribute(const Curves &curves, CurvesEvalCache &cache, const DRW_AttributeRequest &request, const int index)
static void clear_eval_data(CurvesEvalCache &eval_cache)
void DRW_curves_batch_cache_validate(Curves *curves)
static void create_points_position_time_vbo(const bke::CurvesGeometry &curves, CurvesEvalCache &cache)
static void alloc_final_attribute_vbo(CurvesEvalCache &cache, const GPUVertFormat &format, const int index, const char *)
blender::gpu::Batch * DRW_curves_batch_cache_get_edit_points(Curves *curves)
static void create_edit_lines_ibo(const bke::CurvesGeometry &curves, CurvesBatchCache &cache)
static bool batch_cache_is_dirty(const Curves &curves)
static void fill_points_position_time_vbo(const OffsetIndices< int > points_by_curve, const Span< float3 > positions, MutableSpan< PositionAndParameter > posTime_data, MutableSpan< float > hairLength_data)
void DRW_curves_batch_cache_free_old(Curves *curves, int ctime)
void drw_curves_get_attribute_sampler_name(const char *layer_name, char r_sampler_name[32])
void drw_attributes_clear(DRW_Attributes *attributes)
static void create_edit_points_selection(const bke::CurvesGeometry &curves, const IndexMask bezier_curves, const OffsetIndices< int > bezier_dst_offsets, CurvesBatchCache &cache)
static void clear_edit_data(CurvesBatchCache *cache)
blender::gpu::Batch * DRW_curves_batch_cache_get_edit_curves_handles(Curves *curves)
bool drw_custom_data_match_attribute(const CustomData &custom_data, const char *name, int *r_layer_index, eCustomDataType *r_type)
static void alloc_final_points_vbo(CurvesEvalCache &cache)
static void init_batch_cache(Curves &curves)
static void create_sculpt_cage_ibo(const OffsetIndices< int > points_by_curve, CurvesBatchCache &cache)
static GPUVertFormat single_attr_vbo_format(const char *name, const GPUVertCompType comp_type, const uint comp_len, const GPUVertFetchMode fetch_mode, uint &attr_id=DUMMY_ID)
blender::gpu::Batch * DRW_curves_batch_cache_get_sculpt_curves_cage(Curves *curves)
static void calc_edit_handles_ibo(const bke::CurvesGeometry &curves, const IndexMask bezier_curves, const OffsetIndices< int > bezier_offsets, const IndexMask other_curves, CurvesBatchCache &cache)
static void create_edit_points_position_and_data(const bke::CurvesGeometry &curves, const IndexMask bezier_curves, const OffsetIndices< int > bezier_dst_offsets, const bke::crazyspace::GeometryDeformation deformation, CurvesBatchCache &cache)
static void create_curve_offsets_vbos(const OffsetIndices< int > points_by_curve, CurvesEvalCache &cache)
static void calc_final_indices(const bke::CurvesGeometry &curves, CurvesEvalCache &cache, const int thickness_res)
static void create_edit_points_position_vbo(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &, CurvesBatchCache &cache)
static void clear_batch_cache(Curves &curves)
static CurvesBatchCache & get_batch_cache(Curves &curves)
void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode)
static uint32_t bezier_data_value(int8_t handle_type, bool is_active)
gpu::VertBuf ** DRW_curves_texture_for_evaluated_attribute(Curves *curves, const char *name, bool *r_is_point_domain)
static bool ensure_attributes(const Curves &curves, CurvesBatchCache &cache, const GPUMaterial *gpu_material)
bool curves_ensure_procedural_data(Curves *curves_id, CurvesEvalCache **r_cache, const GPUMaterial *gpu_material, const int subdiv, const int thickness_res)
static void fill_curve_offsets_vbos(const OffsetIndices< int > points_by_curve, GPUVertBufRaw &data_step, GPUVertBufRaw &seg_step)
void drw_attributes_merge(DRW_Attributes *dst, const DRW_Attributes *src, std::mutex &render_mutex)
static void discard_attributes(CurvesEvalCache &eval_cache)
blender::gpu::Batch * DRW_curves_batch_cache_get_edit_curves_lines(Curves *curves)
static void ensure_control_point_attribute(const Curves &curves, CurvesEvalCache &cache, const DRW_AttributeRequest &request, const int index, const GPUVertFormat &format)
void drw_attributes_add_request(DRW_Attributes *attrs, const char *name, const eCustomDataType type, const int layer_index, const blender::bke::AttrDomain domain)
void DRW_curves_batch_cache_free(Curves *curves)
void DRW_curves_batch_cache_create_requested(Object *ob)
static void clear_final_data(CurvesEvalFinalCache &final_cache)
bool drw_attributes_overlap(const DRW_Attributes *a, const DRW_Attributes *b)
static void request_attribute(Curves &curves, const char *name)
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
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
static const int steps
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
CurvesGeometry geometry
gpu::VertBuf * proc_attributes_buf[GPU_MAX_ATTR]
gpu::VertBuf * attributes_buf[GPU_MAX_ATTR]
DRW_AttributeRequest requests[GPU_MAX_ATTR]