Blender V4.3
MOD_grease_pencil_texture.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 "BKE_attribute.hh"
10#include "BLI_index_range.hh"
11#include "BLI_math_base.hh"
12#include "BLI_span.hh"
13
14#include "DNA_defaults.h"
15#include "DNA_modifier_types.h"
16
17#include "BKE_curves.hh"
18#include "BKE_geometry_set.hh"
19#include "BKE_grease_pencil.hh"
20#include "BKE_instances.hh"
21#include "BKE_modifier.hh"
22#include "BKE_screen.hh"
23
24#include "BLO_read_write.hh"
25
27
28#include "UI_interface.hh"
29#include "UI_resources.hh"
30
31#include "BLT_translation.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#include "RNA_access.hh"
37#include "RNA_prototypes.hh"
38
40#include "MOD_ui_common.hh"
41
42namespace blender {
43
44static void init_data(ModifierData *md)
45{
46 auto *tmd = reinterpret_cast<GreasePencilTextureModifierData *>(md);
47
49
51 modifier::greasepencil::init_influence_data(&tmd->influence, false);
52}
53
54static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
55{
56 const auto *tmd = reinterpret_cast<const GreasePencilTextureModifierData *>(md);
57 auto *tmmd = reinterpret_cast<GreasePencilTextureModifierData *>(target);
58
60
62 modifier::greasepencil::copy_influence_data(&tmd->influence, &tmmd->influence, flag);
63}
64
65static void free_data(ModifierData *md)
66{
67 auto *tmd = reinterpret_cast<GreasePencilTextureModifierData *>(md);
69}
70
71static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
72{
73 auto *tmd = reinterpret_cast<GreasePencilTextureModifierData *>(md);
74 modifier::greasepencil::foreach_influence_ID_link(&tmd->influence, ob, walk, user_data);
75}
76
78 const IndexMask &curves_mask,
79 const float offset,
80 const float rotation,
81 const float scale,
82 const bool normalize_u)
83{
84 bke::CurvesGeometry &curves = drawing.strokes_for_write();
85 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
86 const VArray<bool> cyclic = curves.cyclic();
87
88 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
89 bke::SpanAttributeWriter<float> u_translations = attributes.lookup_or_add_for_write_span<float>(
90 "u_translation", bke::AttrDomain::Curve);
91 bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
92 "rotation", bke::AttrDomain::Point);
93 bke::SpanAttributeWriter<float> u_scales = attributes.lookup_or_add_for_write_span<float>(
94 "u_scale",
96 bke::AttributeInitVArray(VArray<float>::ForSingle(1.0f, curves.curves_num())));
97
98 curves.ensure_evaluated_lengths();
99
100 curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) {
101 const IndexRange points = points_by_curve[curve_i];
102 const bool is_cyclic = cyclic[curve_i];
103 const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, is_cyclic);
104 const float norm = normalize_u ? math::safe_rcp(lengths.last()) : 1.0f;
105
106 u_translations.span[curve_i] += offset;
107 u_scales.span[curve_i] *= scale * norm;
108 for (const int point_i : points) {
109 rotations.span[point_i] += rotation;
110 }
111 });
112
113 u_translations.finish();
114 u_scales.finish();
115 rotations.finish();
116}
117
118static float2 rotate_by_angle(const float2 &p, const float angle)
119{
120 const float cos_angle = math::cos(angle);
121 const float sin_angle = math::sin(angle);
122 return float2(p.x * cos_angle - p.y * sin_angle, p.x * sin_angle + p.y * cos_angle);
123}
124
125/*
126 * This gets the legacy stroke-space to layer-space matrix.
127 */
128static void get_legacy_stroke_matrix(const Span<float3> positions,
129 float3x4 &stroke_to_layer,
130 float4x3 &layer_to_stroke)
131{
132 using namespace blender;
133 using namespace blender::math;
134
135 if (positions.size() < 2) {
136 stroke_to_layer = float3x4::identity();
137 layer_to_stroke = float4x3::identity();
138 }
139
140 const float3 &pt0 = positions[0];
141 const float3 &pt1 = positions[1];
142 const float3 &pt3 = positions[int(positions.size() * 0.75f)];
143
144 /* Local X axis (p0 -> p1) */
145 const float3 local_x = normalize(pt1 - pt0);
146
147 /* Point vector at 3/4 */
148 const float3 local_3 = (positions.size() == 2) ? (pt3 * 0.001f) - pt0 : pt3 - pt0;
149
150 /* Vector orthogonal to polygon plane. */
151 const float3 normal = cross(local_x, local_3);
152
153 /* Local Y axis (cross to normal/x axis). */
154 const float3 local_y = normalize(cross(normal, local_x));
155
156 /* Get layer space using first point as origin. */
157 stroke_to_layer = float3x4(float4(local_x, 0), float4(local_y, 0), float4(pt0, 1));
158 layer_to_stroke = math::transpose(float3x4(float4(local_x, -dot(pt0, local_x)),
159 float4(local_y, -dot(pt0, local_y)),
160 float4(0, 0, 0, 1)));
161}
162
164 const IndexMask &curves_mask,
165 const float2 &offset,
166 const float rotation,
167 const float scale)
168{
169 /* Texture matrices are a combination of an unknown 3D transform into UV space, with a known 2D
170 * transform on top.
171 *
172 * However, the modifier offset is not applied directly to the UV transform, since it emulates
173 * legacy behavior of the GPv2 modifier, which applied translation first, before rotating about
174 * (0.5, 0.5) and scaling. To achieve the same result as the legacy modifier, the actual offset
175 * is calculated such that the result matches the GPv2 behavior.
176 *
177 * The canonical transform is
178 * uv = T + R / S * xy
179 *
180 * In terms of legacy variables TL, RL, SL the same transform is described as
181 * uv = (RL * (xy / 2 + TL) + 1/2) / SL
182 *
183 * where the 1/2 scaling factor and offset are the "bounds" transform and rotation center.
184 *
185 * Rearranging into canonical loc/rot/scale terms:
186 * uv = (RL * TL + 1/2) / SL + 1/2 * RL / SL * xy
187 * <=>
188 * T = (RL * TL + 1/2) / SL
189 * R = RL
190 * S = 2*SL
191 * <=>
192 * TL = 1/2 * R^T * (T * S - 1)
193 * RL = R
194 * SL = S/2
195 */
196
197 bke::CurvesGeometry &curves = drawing.strokes_for_write();
198 const Span<float3> positions = curves.positions();
199 Array<float4x2> texture_matrices(drawing.texture_matrices());
200
201 curves_mask.foreach_index(GrainSize(512), [&](int64_t curve_i) {
202 const IndexRange points = curves.points_by_curve()[curve_i];
203 float4x2 &texture_matrix = texture_matrices[curve_i];
204 /* Factor out the stroke-to-layer transform part used by GPv2.
205 * This may not be the same as the transform used by GPv3 for concave shapes due to a
206 * simplistic normal calculation, but we want to achieve the same effect as GPv2 so have to use
207 * the same matrix. */
208 float3x4 stroke_to_layer;
209 float4x3 layer_to_stroke;
210 get_legacy_stroke_matrix(positions.slice(points), stroke_to_layer, layer_to_stroke);
211 const float3x2 uv_matrix = texture_matrix * stroke_to_layer;
212 const float2 uv_translation = uv_matrix[2];
213 float2 inv_uv_scale;
214 const float2 axis_u = math::normalize_and_get_length(uv_matrix[0], inv_uv_scale[0]);
215 const float2 axis_v = math::normalize_and_get_length(uv_matrix[1], inv_uv_scale[1]);
216 UNUSED_VARS(axis_v); /* `inv_uv_scale[1]` is used. */
217 const float uv_rotation = math::atan2(axis_u[1], axis_u[0]);
218 const float2 uv_scale = math::safe_rcp(inv_uv_scale);
219
220 const float2 legacy_uv_translation = rotate_by_angle(0.5f * uv_scale * uv_translation - 0.5f,
221 -uv_rotation);
222 const float legacy_uv_rotation = uv_rotation;
223 const float2 legacy_uv_scale = 0.5f * uv_scale;
224
225 const float2 legacy_uv_translation_new = legacy_uv_translation + offset;
226 const float legacy_uv_rotation_new = legacy_uv_rotation + rotation;
227 const float2 legacy_uv_scale_new = legacy_uv_scale * scale;
228
229 const float2 uv_translation_new =
230 (rotate_by_angle(legacy_uv_translation_new, legacy_uv_rotation_new) + 0.5f) *
231 math::safe_rcp(legacy_uv_scale_new);
232 const float uv_rotation_new = legacy_uv_rotation_new;
233 const float2 uv_scale_new = 2.0f * legacy_uv_scale_new;
234
235 const float cos_uv_rotation_new = math::cos(uv_rotation_new);
236 const float sin_uv_rotation_new = math::sin(uv_rotation_new);
237 const float2 inv_uv_scale_new = math::safe_rcp(uv_scale_new);
238 const float3x2 uv_matrix_new = float3x2(
239 inv_uv_scale_new[0] * float2(cos_uv_rotation_new, sin_uv_rotation_new),
240 inv_uv_scale_new[1] * float2(-sin_uv_rotation_new, cos_uv_rotation_new),
241 uv_translation_new);
242 texture_matrix = uv_matrix_new * layer_to_stroke;
243 });
244
245 drawing.set_texture_matrices(texture_matrices, curves_mask);
246}
247
249 const ModifierEvalContext &ctx,
251{
252 IndexMaskMemory mask_memory;
254 ctx.object, drawing.strokes(), tmd.influence, mask_memory);
255
256 const bool normalize_u = (tmd.fit_method == MOD_GREASE_PENCIL_TEXTURE_FIT_STROKE);
260 drawing, curves_mask, tmd.uv_offset, tmd.alignment_rotation, tmd.uv_scale, normalize_u);
261 break;
264 drawing, curves_mask, tmd.fill_offset, tmd.fill_rotation, tmd.fill_scale);
265 break;
268 drawing, curves_mask, tmd.uv_offset, tmd.alignment_rotation, tmd.uv_scale, normalize_u);
270 drawing, curves_mask, tmd.fill_offset, tmd.fill_rotation, tmd.fill_scale);
271 break;
272 }
273}
274
276 const ModifierEvalContext *ctx,
277 bke::GeometrySet *geometry_set)
278{
281
282 const auto &tmd = *reinterpret_cast<const GreasePencilTextureModifierData *>(md);
283
284 if (!geometry_set->has_grease_pencil()) {
285 return;
286 }
287 GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write();
288
289 IndexMaskMemory mask_memory;
291 grease_pencil, tmd.influence, mask_memory);
292 const int frame = grease_pencil.runtime->eval_frame;
294 grease_pencil, layer_mask, frame);
296 [&](Drawing *drawing) { modify_curves(tmd, *ctx, *drawing); });
297}
298
299static void panel_draw(const bContext *C, Panel *panel)
300{
301 uiLayout *layout = panel->layout;
302
303 PointerRNA ob_ptr;
305 const auto &tmd = *static_cast<GreasePencilTextureModifierData *>(ptr->data);
306 const auto mode = GreasePencilTextureModifierMode(tmd.mode);
307 uiLayout *col;
308
309 uiLayoutSetPropSep(layout, true);
310
311 uiItemR(layout, ptr, "mode", UI_ITEM_NONE, nullptr, ICON_NONE);
312
314 col = uiLayoutColumn(layout, false);
315 uiItemR(col, ptr, "fit_method", UI_ITEM_NONE, IFACE_("Stroke Fit Method"), ICON_NONE);
316 uiItemR(col, ptr, "uv_offset", UI_ITEM_NONE, nullptr, ICON_NONE);
317 uiItemR(col, ptr, "alignment_rotation", UI_ITEM_NONE, nullptr, ICON_NONE);
318 uiItemR(col, ptr, "uv_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
319 }
320
322 uiItemS(layout);
323 }
324
326 col = uiLayoutColumn(layout, false);
327 uiItemR(col, ptr, "fill_rotation", UI_ITEM_NONE, nullptr, ICON_NONE);
328 uiItemR(col, ptr, "fill_offset", UI_ITEM_NONE, IFACE_("Offset"), ICON_NONE);
329 uiItemR(col, ptr, "fill_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
330 }
331
332 if (uiLayout *influence_panel = uiLayoutPanelProp(
333 C, layout, ptr, "open_influence_panel", IFACE_("Influence")))
334 {
338 }
339
340 modifier_panel_end(layout, ptr);
341}
342
347
348static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md)
349{
350 const auto *tmd = reinterpret_cast<const GreasePencilTextureModifierData *>(md);
351
353 modifier::greasepencil::write_influence_data(writer, &tmd->influence);
354}
355
356static void blend_read(BlendDataReader *reader, ModifierData *md)
357{
358 auto *tmd = reinterpret_cast<GreasePencilTextureModifierData *>(md);
359
360 modifier::greasepencil::read_influence_data(reader, &tmd->influence);
361}
362
363} // namespace blender
364
366 /*idname*/ "GreasePencilTexture",
367 /*name*/ N_("TextureMapping"),
368 /*struct_name*/ "GreasePencilTextureModifierData",
369 /*struct_size*/ sizeof(GreasePencilTextureModifierData),
370 /*srna*/ &RNA_GreasePencilTextureModifier,
374 /*icon*/ ICON_MOD_UVPROJECT,
375
376 /*copy_data*/ blender::copy_data,
377
378 /*deform_verts*/ nullptr,
379 /*deform_matrices*/ nullptr,
380 /*deform_verts_EM*/ nullptr,
381 /*deform_matrices_EM*/ nullptr,
382 /*modify_mesh*/ nullptr,
383 /*modify_geometry_set*/ blender::modify_geometry_set,
384
385 /*init_data*/ blender::init_data,
386 /*required_data_mask*/ nullptr,
387 /*free_data*/ blender::free_data,
388 /*is_disabled*/ nullptr,
389 /*update_depsgraph*/ nullptr,
390 /*depends_on_time*/ nullptr,
391 /*depends_on_normals*/ nullptr,
392 /*foreach_ID_link*/ blender::foreach_ID_link,
393 /*foreach_tex_link*/ nullptr,
394 /*free_runtime_data*/ nullptr,
395 /*panel_register*/ blender::panel_register,
396 /*blend_write*/ blender::blend_write,
397 /*blend_read*/ blender::blend_read,
398};
Low-level operations for curves.
Low-level operations for grease pencil.
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
#define UNUSED_VARS(...)
#define ELEM(...)
#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_GreasePencilTexture
struct GreasePencilTextureModifierData GreasePencilTextureModifierData
GreasePencilTextureModifierMode
@ MOD_GREASE_PENCIL_TEXTURE_STROKE_AND_FILL
@ MOD_GREASE_PENCIL_TEXTURE_FILL
@ MOD_GREASE_PENCIL_TEXTURE_STROKE
@ MOD_GREASE_PENCIL_TEXTURE_FIT_STROKE
ModifierTypeInfo modifierType_GreasePencilTexture
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)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
#define UI_ITEM_NONE
PanelLayout uiLayoutPanelProp(const bContext *C, uiLayout *layout, PointerRNA *open_prop_owner, const char *open_prop_name)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
SIMD_FORCE_INLINE btVector3 & normalize()
Normalize this vector x^2 + y^2 + z^2 = 1.
Definition btVector3.h:303
Span< float4x2 > texture_matrices() const
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
void foreach_index(Fn &&fn) const
static bool is_cyclic(const Nurb *nu)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uint col
ccl_device_inline float cross(const float2 a, const float2 b)
T cos(const AngleRadianBase< T > &a)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
T safe_rcp(const T &a)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
T atan2(const T &y, const T &x)
T sin(const AngleRadianBase< 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)
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 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 float2 rotate_by_angle(const float2 &p, const float angle)
static void modify_curves(ModifierData &md, const ModifierEvalContext &ctx, Drawing &drawing)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void panel_draw(const bContext *C, Panel *panel)
static void write_fill_transforms(bke::greasepencil::Drawing &drawing, const IndexMask &curves_mask, const float2 &offset, const float rotation, const float scale)
MatBase< float, 3, 4 > float3x4
VecBase< float, 4 > float4
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
VecBase< float, 2 > float2
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
MatBase< float, 3, 2 > float3x2
static void write_stroke_transforms(bke::greasepencil::Drawing &drawing, const IndexMask &curves_mask, const float offset, const float rotation, const float scale, const bool normalize_u)
static void get_legacy_stroke_matrix(const Span< float3 > positions, float3x4 &stroke_to_layer, float4x3 &layer_to_stroke)
static void blend_read(BlendDataReader *reader, ModifierData *md)
__int64 int64_t
Definition stdint.h:89
GreasePencilModifierInfluenceData influence
GreasePencilRuntimeHandle * runtime
Definition DNA_ID.h:413
struct uiLayout * layout
void * data
Definition RNA_types.hh:42
GreasePencil * get_grease_pencil_for_write()
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4126
uint8_t flag
Definition wm_window.cc:138