Blender V5.0
MOD_grease_pencil_envelope.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_defaults.h"
10#include "DNA_modifier_types.h"
11
12#include "BLI_array_utils.hh"
13#include "BLI_math_geom.h"
14
15#include "BKE_curves.hh"
16#include "BKE_geometry_set.hh"
17#include "BKE_grease_pencil.hh"
18#include "BKE_instances.hh"
19#include "BKE_material.hh"
20#include "BKE_modifier.hh"
21#include "BKE_screen.hh"
22
23#include "BLO_read_write.hh"
24
26#include "UI_resources.hh"
27
28#include "BLT_translation.hh"
29
30#include "WM_types.hh"
31
32#include "RNA_access.hh"
33#include "RNA_prototypes.hh"
34
36#include "MOD_modifiertypes.hh"
37#include "MOD_ui_common.hh"
38
39namespace blender {
40
50
51static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
52{
53 const auto *emd = reinterpret_cast<const GreasePencilEnvelopeModifierData *>(md);
54 auto *temd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(target);
55
57
59 modifier::greasepencil::copy_influence_data(&emd->influence, &temd->influence, flag);
60}
61
62static void free_data(ModifierData *md)
63{
64 auto *emd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(md);
66}
67
68static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
69{
70 auto *emd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(md);
71 modifier::greasepencil::foreach_influence_ID_link(&emd->influence, ob, walk, user_data);
72}
73
74static inline float3 calculate_plane(const float3 &center, const float3 &prev, const float3 &next)
75{
76 const float3 v1 = math::normalize(prev - center);
77 const float3 v2 = math::normalize(next - center);
78 return math::normalize(v1 - v2);
79}
80
81static inline std::optional<float3> find_plane_intersection(const float3 &plane_point,
82 const float3 &plane_normal,
83 const float3 &from,
84 const float3 &to)
85{
86 const float lambda = line_plane_factor_v3(plane_point, plane_normal, from, to);
87 if (lambda <= 0.0f || lambda >= 1.0f) {
88 return std::nullopt;
89 }
90 return math::interpolate(from, to, lambda);
91}
92
93/* "Infinite" radius in case no limit is applied. */
94static const float unlimited_radius = FLT_MAX;
95
111static float calc_min_radius_v3v3(const float3 &p1, const float3 &p2, const float3 &dir)
112{
113 const float p1_dir = math::dot(p1, dir);
114 const float p2_dir = math::dot(p2, dir);
115 const float p2_sqr = math::length_squared(p2);
116 const float diff_dir = p1_dir - p2_dir;
117
118 const float u = [=]() {
119 if (diff_dir == 0.0f) {
120 const float p1_sqr = math::length_squared(p1);
121 return p1_sqr < p2_sqr ? 1.0f : 0.0f;
122 }
123
124 const float p = p2_dir / diff_dir;
125 const float3 diff = p1 - p2;
126 const float diff_sqr = math::length_squared(diff);
127 const float diff_p2 = math::dot(diff, p2);
128 const float q = (2 * diff_p2 * p2_dir - p2_sqr * diff_dir) / (diff_dir * diff_sqr);
129 if (p * p - q < 0) {
130 return 0.5f - std::copysign(0.5f, p);
131 }
132
133 return math::clamp(-p - math::sqrt(p * p - q) * std::copysign(1.0f, p), 0.0f, 1.0f);
134 }();
135
136 /* v is the determined minimal radius. In case p1 and p2 are the same, there is a
137 * simple proof for the following formula using the geometric mean theorem and Thales theorem. */
138 const float v = math::length_squared(math::interpolate(p2, p1, u)) /
139 (2 * math::interpolate(p2_dir, p1_dir, u));
140 if (v < 0 || !isfinite(v)) {
141 /* No limit to the radius from this segment. */
142 return unlimited_radius;
143 }
144 return v;
145}
146
147static float calc_radius_limit(const Span<float3> positions,
148 const bool is_cyclic,
149 const int spread,
150 const int point,
151 const float3 &direction)
152{
153 if (math::is_zero(direction)) {
154 return unlimited_radius;
155 }
156
157 const int point_num = positions.size();
158 const float3 &center = positions[point];
159
160 float result = unlimited_radius;
161 if (is_cyclic) {
162 /* Spread should be limited to half the points in the cyclic case. */
163 BLI_assert(spread <= point_num / 2);
164 /* Left side. */
165 for (const int line_i : IndexRange(spread)) {
166 const int from_i = (point - line_i - 2 + point_num) % point_num;
167 const int to_i = (point - line_i - 1 + point_num) % point_num;
168 const float limit = calc_min_radius_v3v3(
169 positions[from_i] - center, positions[to_i] - center, direction);
170 result = std::min(result, limit);
171 }
172 /* Right side. */
173 for (const int line_i : IndexRange(spread)) {
174 const int from_i = (point + line_i + 1 + point_num) % point_num;
175 const int to_i = (point + line_i + 2 + point_num) % point_num;
176 const float limit = calc_min_radius_v3v3(
177 positions[from_i] - center, positions[to_i] - center, direction);
178 result = std::min(result, limit);
179 }
180 }
181 else {
182 if (point == 0 || point >= point_num - 1) {
183 return unlimited_radius;
184 }
185 /* Left side. */
186 const int spread_left = std::min(spread, std::max(point - 2, 0));
187 for (const int line_i : IndexRange(spread_left)) {
188 const int from_i = std::max(point - line_i - 2, 0);
189 const int to_i = std::max(point - line_i - 1, 0);
190 const float limit = calc_min_radius_v3v3(
191 positions[from_i] - center, positions[to_i] - center, direction);
192 result = std::min(result, limit);
193 }
194 /* Right side. */
195 const int spread_right = std::min(spread, std::max(point_num - point - 2, 0));
196 for (const int line_i : IndexRange(spread_right)) {
197 const int from_i = std::min(point + line_i + 1, point_num - 1);
198 const int to_i = std::min(point + line_i + 2, point_num - 1);
199 const float limit = calc_min_radius_v3v3(
200 positions[from_i] - center, positions[to_i] - center, direction);
201 result = std::min(result, limit);
202 }
203 }
204 return result;
205}
206
210static bool find_envelope(const Span<float3> positions,
211 const bool is_cyclic,
212 const int spread,
213 const int point,
214 float3 &r_center,
215 float &r_radius)
216{
217 /* Compute a plane normal for intersections. */
218 const IndexRange points = positions.index_range();
219 const float3 &pos = positions[point];
220 const float3 &prev_pos =
221 positions[points.contains(point - 1) ? point - 1 : (is_cyclic ? points.last() : point)];
222 const float3 &next_pos =
223 positions[points.contains(point + 1) ? point + 1 : (is_cyclic ? points.first() : point)];
224 const float3 plane_normal = calculate_plane(pos, prev_pos, next_pos);
225 if (math::is_zero(plane_normal)) {
226 return false;
227 }
228
229 /* Find two intersections with maximal radii. */
230 float max_distance1 = 0.0f;
231 float max_distance2 = 0.0f;
232 float3 intersect1 = pos;
233 float3 intersect2 = pos;
234 for (const int line_i : IndexRange(spread + 2)) {
235 /* Raw indices, can be out of range. */
236 const int from_spread_i = point - spread - 1 + line_i;
237 const int to_spread_i = point + line_i;
238 /* Clamp or wrap to valid indices. */
239 const int from_i = is_cyclic ? (from_spread_i + points.size()) % points.size() :
240 std::max(from_spread_i, int(points.first()));
241 const int to_i = is_cyclic ? (to_spread_i + points.size()) % points.size() :
242 std::min(to_spread_i, int(points.last()));
243 const float3 &from_pos = positions[from_i];
244 const float3 &to_pos = positions[to_i];
245 const float3 line_delta = to_pos - from_pos;
246
247 const std::optional<float3> line_intersect = find_plane_intersection(
248 pos, plane_normal, from_pos, to_pos);
249 if (!line_intersect) {
250 continue;
251 }
252 const float3 line_direction = line_intersect.value() - pos;
253 const float line_distance = math::length(line_direction);
254
255 /* Diameter of a sphere centered in the plane, touching both #pos and the intersection line. */
256 const float cos_angle = math::abs(math::dot(plane_normal, line_delta)) /
257 math::length(line_delta);
258 const float diameter = line_distance * 2.0f * cos_angle / (1 + cos_angle);
259
260 if (line_i == 0) {
261 max_distance1 = diameter;
262 intersect1 = line_intersect.value();
263 continue;
264 }
265 /* Use as vector 1 or 2 based on primary direction. */
266 if (math::dot(intersect1 - pos, line_direction) >= 0.0f) {
267 if (diameter > max_distance1) {
268 intersect1 = line_intersect.value();
269 max_distance1 = diameter;
270 }
271 }
272 else {
273 if (diameter > max_distance2) {
274 intersect2 = line_intersect.value();
275 max_distance2 = diameter;
276 }
277 }
278 }
279
280 r_radius = 0.5f * (max_distance1 + max_distance2);
281 if (r_radius < FLT_EPSILON) {
282 return false;
283 }
284
285 const float3 new_center = 0.5f * (intersect1 + intersect2);
286 /* Apply radius limiting to not cross existing lines. */
287 const float3 dir = math::normalize(new_center - pos);
288 r_radius = std::min(r_radius, calc_radius_limit(positions, is_cyclic, spread, point, dir));
289
290 r_center = math::interpolate(
291 pos, new_center, 2.0f * r_radius / math::distance(intersect1, intersect2));
292
293 return true;
294}
295
298 const IndexMask &curves_mask)
299{
300 bke::CurvesGeometry &curves = drawing.strokes_for_write();
301 const bke::AttributeAccessor attributes = curves.attributes();
302 const MutableSpan<float3> positions = curves.positions_for_write();
303 const MutableSpan<float> radii = drawing.radii_for_write();
304 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
306 curves, emd.influence);
307 const VArray<bool> cyclic_flags = *attributes.lookup_or_default(
308 "cyclic", bke::AttrDomain::Curve, false);
309
310 /* Cache to avoid affecting neighboring point results when updating positions. */
311 const Array<float3> old_positions(positions.as_span());
312
313 curves_mask.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
314 const IndexRange points = points_by_curve[curve_i];
315 const bool cyclic = cyclic_flags[curve_i];
316 const int point_num = points.size();
317 const int spread = cyclic ?
318 math::abs(((emd.spread + point_num / 2) % point_num) - point_num / 2) :
319 std::min(emd.spread, point_num - 1);
320
321 for (const int64_t i : points.index_range()) {
322 const int64_t point_i = points[i];
323 const float weight = vgroup_weights[point_i];
324
325 float3 envelope_center;
326 float envelope_radius;
327 if (!find_envelope(old_positions.as_span().slice(points),
328 cyclic,
329 spread,
330 i,
331 envelope_center,
332 envelope_radius))
333 {
334 continue;
335 }
336
337 const float target_radius = radii[point_i] * emd.thickness + envelope_radius;
338 radii[point_i] = math::interpolate(radii[point_i], target_radius, weight);
339 positions[point_i] = math::interpolate(old_positions[point_i], envelope_center, weight);
340 }
341 });
342
343 drawing.tag_positions_changed();
344 curves.tag_radii_changed();
345}
346
348 /* Offset left and right from the source point. */
350 /* Number of points to skip. */
351 int skip;
352 /* Number of points in each envelope stroke. */
354 /* Material index assigned to new strokes. */
357 float strength;
358};
359
361 const ModifierEvalContext &ctx)
362{
363 EnvelopeInfo info;
364 info.spread = emd.spread;
365 info.skip = emd.skip;
368 info.points_per_curve = 0;
369 break;
371 info.points_per_curve = 2;
372 break;
374 info.points_per_curve = 2 * (2 + emd.skip);
375 break;
376 }
377 info.material_index = std::min(emd.mat_nr, ctx.object->totcol - 1);
378 info.thickness = emd.thickness;
379 info.strength = emd.strength;
380 return info;
381}
382
383static int curve_spread(const EnvelopeInfo &info, const int point_num, const bool is_cyclic_curve)
384{
385 /* Clamp spread in the cyclic case to half the curve size. */
386 return is_cyclic_curve ? std::min(info.spread, point_num / 2) : info.spread;
387}
388
390 const int point_num,
391 const bool is_cyclic_curve)
392{
393 const int spread = curve_spread(info, point_num, is_cyclic_curve);
394 /* Number of envelope strokes making up the envelope. */
395 const int num_strokes = point_num + spread - 1;
396 /* Skip strokes (only every n-th point generates strokes). */
397 const int num_strokes_simplified = (num_strokes + info.skip) / (1 + info.skip);
398 return num_strokes_simplified;
399}
400
426static void create_envelope_stroke_for_point(const IndexRange src_curve_points,
427 const bool src_curve_cyclic,
428 const int point,
429 const int spread,
430 const int base_length,
431 const MutableSpan<int> point_src_indices)
432{
433 const int point_num = src_curve_points.size();
434 BLI_assert(point_src_indices.size() == base_length * 2);
435
436 /* Clamp or wrap to ensure a valid index. */
437 auto get_index = [=](const int index) -> int {
438 return src_curve_cyclic ? (index + point_num) % point_num :
439 math::clamp(index, 0, point_num - 1);
440 };
441
442 for (const int i : IndexRange(base_length)) {
443 const int reverse_i = base_length - 1 - i;
444 const int point_left = get_index(point - spread + reverse_i);
445 const int point_right = get_index(point + reverse_i);
446 point_src_indices[i] = src_curve_points[point_left];
447 point_src_indices[base_length + i] = src_curve_points[point_right];
448 }
449}
450
452 const int src_curve_index,
453 const IndexRange src_curve_points,
454 const bool src_curve_cyclic,
455 const VArray<int> &src_material_indices,
456 const IndexRange dst_points,
457 const MutableSpan<int> curve_offsets,
458 const MutableSpan<int> material_indices,
459 const MutableSpan<int> curve_src_indices,
460 const MutableSpan<int> point_src_indices)
461{
462 const int src_point_num = src_curve_points.size();
463 const int spread = curve_spread(info, src_point_num, src_curve_cyclic);
464 const int num_strokes = curve_envelope_strokes_num(info, src_point_num, src_curve_cyclic);
465 const bool use_fills = info.points_per_curve > 2;
466 /* Length of continuous point ranges that get connected. */
467 const int base_length = use_fills ? 2 + info.skip : 1;
468
469 BLI_assert(curve_offsets.size() == num_strokes);
470 BLI_assert(material_indices.size() == num_strokes);
471 BLI_assert(curve_src_indices.size() == num_strokes);
472 BLI_assert(point_src_indices.size() == num_strokes * info.points_per_curve);
473
474 curve_src_indices.fill(src_curve_index);
475
476 /*
477 * Index range here goes beyond the point range:
478 * This adds points [i - spread, i + 1] as a curve.
479 * The total range covers [-spread - 1, spread + 1].
480 * Each span only gets added once since it repeats for neighboring points.
481 */
482
483 for (const int dst_i : IndexRange(num_strokes)) {
484 const int src_i = dst_i * (1 + info.skip);
485 const IndexRange dst_envelope_points = {dst_i * info.points_per_curve, info.points_per_curve};
486
487 curve_offsets[dst_i] = dst_points[dst_envelope_points.start()];
488 material_indices[dst_i] = info.material_index >= 0 ? info.material_index :
489 src_material_indices[src_curve_index];
490
491 create_envelope_stroke_for_point(src_curve_points,
492 src_curve_cyclic,
493 src_i,
494 spread,
495 base_length,
496 point_src_indices.slice(dst_envelope_points));
497 }
498}
499
500static void create_envelope_strokes(const EnvelopeInfo &info,
502 const IndexMask &curves_mask,
503 const bool keep_original)
504{
505 const bke::CurvesGeometry &src_curves = drawing.strokes();
506 const bke::AttributeAccessor src_attributes = src_curves.attributes();
507 const VArray<bool> src_cyclic = *src_attributes.lookup_or_default(
508 "cyclic", bke::AttrDomain::Curve, false);
509 const VArray<int> src_material_indices = *src_attributes.lookup_or_default(
510 "material_index", bke::AttrDomain::Curve, 0);
511 const int src_curves_num = src_curves.curves_num();
512 const int src_points_num = src_curves.points_num();
513
514 /* Count envelopes. */
515 Array<int> envelope_curves_by_curve(src_curves_num + 1, 0);
516 Array<int> envelope_points_by_curve(src_curves_num + 1, 0);
517 curves_mask.foreach_index([&](const int64_t src_curve_i) {
518 const IndexRange points = src_curves.points_by_curve()[src_curve_i];
519 const int curve_num = curve_envelope_strokes_num(info, points.size(), src_cyclic[src_curve_i]);
520 envelope_curves_by_curve[src_curve_i] = curve_num;
521 envelope_points_by_curve[src_curve_i] = info.points_per_curve * curve_num;
522 });
523 /* Ranges by source curve for envelope curves and points. */
524 const int dst_curve_start_offset = keep_original ? src_curves_num : 0;
525 const int dst_points_start_offset = keep_original ? src_points_num : 0;
526 const OffsetIndices envelope_curve_offsets = offset_indices::accumulate_counts_to_offsets(
527 envelope_curves_by_curve, dst_curve_start_offset);
528 const OffsetIndices envelope_point_offsets = offset_indices::accumulate_counts_to_offsets(
529 envelope_points_by_curve, dst_points_start_offset);
530 const int dst_curve_num = envelope_curve_offsets.total_size() + dst_curve_start_offset;
531 const int dst_point_num = envelope_point_offsets.total_size() + dst_points_start_offset;
532 if (dst_curve_num == 0 || dst_point_num == 0) {
533 return;
534 }
535
536 bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
537 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
538 bke::SpanAttributeWriter<int> dst_material_indices =
539 dst_attributes.lookup_or_add_for_write_span<int>("material_index", bke::AttrDomain::Curve);
540 bke::SpanAttributeWriter<bool> dst_cyclic = dst_attributes.lookup_or_add_for_write_span<bool>(
541 "cyclic", bke::AttrDomain::Curve);
542 /* Map each destination curve and point to its source. */
543 Array<int> src_curve_indices(dst_curve_num);
544 Array<int> src_point_indices(dst_point_num);
545
546 if (keep_original) {
547 /* Add indices to original data. */
548 dst_curves.offsets_for_write()
549 .slice(src_curves.curves_range())
550 .copy_from(src_curves.offsets().drop_back(1));
551
553 src_curve_indices.as_mutable_span().slice(src_curves.curves_range()));
555 src_point_indices.as_mutable_span().slice(src_curves.points_range()));
556
557 array_utils::copy(src_material_indices,
558 dst_material_indices.span.slice(src_curves.curves_range()));
559 }
560
561 curves_mask.foreach_index([&](const int64_t i) {
562 const bool src_curve_cyclic = src_cyclic[i];
563 const IndexRange src_curve_points = src_curves.points_by_curve()[i];
564 const IndexRange envelope_curves = envelope_curve_offsets[i];
565 const IndexRange envelope_points = envelope_point_offsets[i];
566
568 i,
569 src_curve_points,
570 src_curve_cyclic,
571 src_material_indices,
572 envelope_points,
573 dst_curves.offsets_for_write().slice(envelope_curves),
574 dst_material_indices.span.slice(envelope_curves),
575 src_curve_indices.as_mutable_span().slice(envelope_curves),
576 src_point_indices.as_mutable_span().slice(envelope_points));
577 });
578 dst_curves.offsets_for_write().last() = dst_point_num;
579
580 bke::gather_attributes(src_attributes,
583 {},
584 src_point_indices,
585 dst_attributes);
586 bke::gather_attributes(src_attributes,
589 bke::attribute_filter_from_skip_ref({"cyclic", "material_index"}),
590 src_curve_indices,
591 dst_attributes);
592
593 /* Apply thickness and strength factors. */
594 {
595 bke::SpanAttributeWriter<float> radius_writer =
596 dst_attributes.lookup_or_add_for_write_span<float>(
597 "radius",
600 const IndexRange all_new_points = keep_original ?
601 IndexRange(src_curves.point_num,
602 dst_point_num - src_curves.point_num) :
603 IndexRange(dst_point_num);
604 for (const int point_i : all_new_points) {
605 radius_writer.span[point_i] *= info.thickness;
606 }
607 radius_writer.finish();
608 if (bke::SpanAttributeWriter<float> opacity_writer =
609 dst_attributes.lookup_or_add_for_write_span<float>(
610 "opacity",
613 {
614 for (const int point_i : all_new_points) {
615 opacity_writer.span[point_i] *= info.strength;
616 }
617 opacity_writer.finish();
618 }
619 }
620
621 dst_cyclic.finish();
622 dst_material_indices.finish();
623 dst_curves.update_curve_types();
624
625 drawing.strokes_for_write() = std::move(dst_curves);
626 drawing.tag_topology_changed();
627}
628
630 const ModifierEvalContext &ctx,
632{
633 const EnvelopeInfo info = get_envelope_info(emd, ctx);
634
636
637 IndexMaskMemory mask_memory;
639 ctx.object, drawing.strokes(), emd.influence, mask_memory);
640
641 const auto mode = GreasePencilEnvelopeModifierMode(emd.mode);
642 switch (mode) {
644 deform_drawing_as_envelope(emd, drawing, curves_mask);
645 break;
647 create_envelope_strokes(info, drawing, curves_mask, true);
648 break;
650 create_envelope_strokes(info, drawing, curves_mask, false);
651 break;
652 }
653}
654
656 const ModifierEvalContext *ctx,
657 bke::GeometrySet *geometry_set)
658{
660
661 auto *emd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(md);
662
663 if (!geometry_set->has_grease_pencil()) {
664 return;
665 }
666 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
667 const int frame = grease_pencil.runtime->eval_frame;
668
669 IndexMaskMemory mask_memory;
671 grease_pencil, emd->influence, mask_memory);
672
674 grease_pencil, layer_mask, frame);
676 [&](Drawing *drawing) { modify_drawing(*emd, *ctx, *drawing); });
677}
678
679static void panel_draw(const bContext *C, Panel *panel)
680{
681 uiLayout *layout = panel->layout;
682
683 PointerRNA ob_ptr;
686 RNA_enum_get(ptr, "mode"));
687
688 layout->use_property_split_set(true);
689
690 layout->prop(ptr, "mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
691
692 layout->prop(ptr, "spread", UI_ITEM_NONE, std::nullopt, ICON_NONE);
693 layout->prop(ptr, "thickness", UI_ITEM_NONE, std::nullopt, ICON_NONE);
694
695 switch (mode) {
697 break;
700 layout->prop(ptr, "strength", UI_ITEM_NONE, std::nullopt, ICON_NONE);
701 layout->prop(ptr, "mat_nr", UI_ITEM_NONE, std::nullopt, ICON_NONE);
702 layout->prop(ptr, "skip", UI_ITEM_NONE, std::nullopt, ICON_NONE);
703 break;
704 }
705
706 if (uiLayout *influence_panel = layout->panel_prop(
707 C, ptr, "open_influence_panel", IFACE_("Influence")))
708 {
712 }
713
715}
716
721
722static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
723{
724 const auto *emd = reinterpret_cast<const GreasePencilEnvelopeModifierData *>(md);
725
727 modifier::greasepencil::write_influence_data(writer, &emd->influence);
728}
729
730static void blend_read(BlendDataReader *reader, ModifierData *md)
731{
732 auto *emd = reinterpret_cast<GreasePencilEnvelopeModifierData *>(md);
733
734 modifier::greasepencil::read_influence_data(reader, &emd->influence);
735}
736
737} // namespace blender
738
740 /*idname*/ "GreasePencilEnvelope",
741 /*name*/ N_("Envelope"),
742 /*struct_name*/ "GreasePencilEnvelopeModifierData",
743 /*struct_size*/ sizeof(GreasePencilEnvelopeModifierData),
744 /*srna*/ &RNA_GreasePencilEnvelopeModifier,
748 /*icon*/ ICON_MOD_ENVELOPE,
749
750 /*copy_data*/ blender::copy_data,
751
752 /*deform_verts*/ nullptr,
753 /*deform_matrices*/ nullptr,
754 /*deform_verts_EM*/ nullptr,
755 /*deform_matrices_EM*/ nullptr,
756 /*modify_mesh*/ nullptr,
757 /*modify_geometry_set*/ blender::modify_geometry_set,
758
759 /*init_data*/ blender::init_data,
760 /*required_data_mask*/ nullptr,
761 /*free_data*/ blender::free_data,
762 /*is_disabled*/ nullptr,
763 /*update_depsgraph*/ nullptr,
764 /*depends_on_time*/ nullptr,
765 /*depends_on_normals*/ nullptr,
766 /*foreach_ID_link*/ blender::foreach_ID_link,
767 /*foreach_tex_link*/ nullptr,
768 /*free_runtime_data*/ nullptr,
769 /*panel_register*/ blender::panel_register,
770 /*blend_write*/ blender::blend_write,
771 /*blend_read*/ blender::blend_read,
772};
Low-level operations for curves.
Low-level operations for grease pencil.
General operations, lookup, etc. for materials.
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_SupportsMapping
@ eModifierTypeFlag_AcceptsGreasePencil
@ eModifierTypeFlag_EnableInEditmode
@ eModifierTypeFlag_SupportsEditmode
#define BLI_assert(a)
Definition BLI_assert.h:46
float line_plane_factor_v3(const float plane_co[3], const float plane_no[3], const float l1[3], const float l2[3])
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define IFACE_(msgid)
#define DNA_struct_default_get(struct_name)
@ eModifierType_GreasePencilEnvelope
GreasePencilEnvelopeModifierMode
@ MOD_GREASE_PENCIL_ENVELOPE_SEGMENTS
@ MOD_GREASE_PENCIL_ENVELOPE_FILLS
@ MOD_GREASE_PENCIL_ENVELOPE_DEFORM
ModifierTypeInfo modifierType_GreasePencilEnvelope
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
constexpr Span drop_back(int64_t n) const
Definition BLI_span.hh:182
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool contains(int64_t value) const
constexpr IndexRange index_range() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr Span< T > as_span() const
Definition BLI_span.hh:661
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static VArray from_single(T value, const int64_t size)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
IndexRange points_range() const
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void foreach_index(Fn &&fn) const
static bool is_cyclic(const Nurb *nu)
IMETHOD Vector diff(const Vector &a, const Vector &b, double dt)
Definition frames.inl:1166
uint pos
static ulong * next
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void fill_index_range(MutableSpan< T > span, const T start=0)
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
T length_squared(const VecBase< T, Size > &a)
T clamp(const T &a, const T &min, const T &max)
T sqrt(const T &a)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
bool is_zero(const T &a)
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T abs(const T &a)
void read_influence_data(BlendDataReader *reader, GreasePencilModifierInfluenceData *influence_data)
void init_influence_data(GreasePencilModifierInfluenceData *influence_data, const bool has_custom_curve)
static IndexMask get_filtered_stroke_mask(const Object *ob, const bke::CurvesGeometry &curves, const Material *material_filter, const std::optional< int > material_pass_filter, const bool material_filter_invert, const bool material_pass_filter_invert, IndexMaskMemory &memory)
void write_influence_data(BlendWriter *writer, const GreasePencilModifierInfluenceData *influence_data)
void draw_vertex_group_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
VArray< float > get_influence_vertex_weights(const bke::CurvesGeometry &curves, const GreasePencilModifierInfluenceData &influence_data)
static IndexMask get_filtered_layer_mask(const GreasePencil &grease_pencil, const std::optional< StringRef > tree_node_name_filter, const std::optional< int > layer_pass_filter, const bool layer_filter_invert, const bool layer_pass_filter_invert, IndexMaskMemory &memory)
Vector< bke::greasepencil::Drawing * > get_drawings_for_write(GreasePencil &grease_pencil, const IndexMask &layer_mask, const int frame)
void draw_material_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void draw_layer_filter_settings(const bContext *, uiLayout *layout, PointerRNA *ptr)
void free_influence_data(GreasePencilModifierInfluenceData *influence_data)
void foreach_influence_ID_link(GreasePencilModifierInfluenceData *influence_data, Object *ob, IDWalkFunc walk, void *user_data)
void copy_influence_data(const GreasePencilModifierInfluenceData *influence_data_src, GreasePencilModifierInfluenceData *influence_data_dst, const int)
void ensure_no_bezier_curves(Drawing &drawing)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static void blend_write(BlendWriter *writer, const ID *, const ModifierData *md)
static void create_envelope_strokes_for_curve(const EnvelopeInfo &info, const int src_curve_index, const IndexRange src_curve_points, const bool src_curve_cyclic, const VArray< int > &src_material_indices, const IndexRange dst_points, const MutableSpan< int > curve_offsets, const MutableSpan< int > material_indices, const MutableSpan< int > curve_src_indices, const MutableSpan< int > point_src_indices)
static const float unlimited_radius
static void init_data(ModifierData *md)
static void create_envelope_stroke_for_point(const IndexRange src_curve_points, const bool src_curve_cyclic, const int point, const int spread, const int base_length, const MutableSpan< int > point_src_indices)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void panel_draw(const bContext *C, Panel *panel)
static float3 calculate_plane(const float3 &center, const float3 &prev, const float3 &next)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static float calc_radius_limit(const Span< float3 > positions, const bool is_cyclic, const int spread, const int point, const float3 &direction)
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void create_envelope_strokes(const EnvelopeInfo &info, bke::greasepencil::Drawing &drawing, const IndexMask &curves_mask, const bool keep_original)
static bool find_envelope(const Span< float3 > positions, const bool is_cyclic, const int spread, const int point, float3 &r_center, float &r_radius)
static void modify_drawing(const GreasePencilArrayModifierData &mmd, const ModifierEvalContext &ctx, bke::greasepencil::Drawing &drawing)
VecBase< float, 3 > float3
static void blend_read(BlendDataReader *reader, ModifierData *md)
static float calc_min_radius_v3v3(const float3 &p1, const float3 &p2, const float3 &dir)
static int curve_spread(const EnvelopeInfo &info, const int point_num, const bool is_cyclic_curve)
static void deform_drawing_as_envelope(const GreasePencilEnvelopeModifierData &emd, bke::greasepencil::Drawing &drawing, const IndexMask &curves_mask)
static EnvelopeInfo get_envelope_info(const GreasePencilEnvelopeModifierData &emd, const ModifierEvalContext &ctx)
static int curve_envelope_strokes_num(const EnvelopeInfo &info, const int point_num, const bool is_cyclic_curve)
static std::optional< float3 > find_plane_intersection(const float3 &plane_point, const float3 &plane_normal, const float3 &from, const float3 &to)
int RNA_enum_get(PointerRNA *ptr, const char *name)
#define FLT_MAX
Definition stdcycles.h:14
GreasePencilModifierInfluenceData influence
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:414
struct uiLayout * layout
GreasePencil * get_grease_pencil_for_write()
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145