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