Blender  V3.3
gpencil_sculpt_paint.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2015 Blender Foundation. */
3 
9 #include <math.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include "MEM_guardedalloc.h"
16 
17 #include "BLI_blenlib.h"
18 #include "BLI_ghash.h"
19 #include "BLI_math.h"
20 #include "BLI_rand.h"
21 #include "BLI_utildefines.h"
22 
23 #include "PIL_time.h"
24 
25 #include "BLT_translation.h"
26 
27 #include "DNA_gpencil_types.h"
28 #include "DNA_material_types.h"
29 #include "DNA_meshdata_types.h"
30 #include "DNA_object_types.h"
31 #include "DNA_scene_types.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_space_types.h"
34 #include "DNA_view3d_types.h"
35 
36 #include "BKE_brush.h"
37 #include "BKE_colortools.h"
38 #include "BKE_context.h"
39 #include "BKE_deform.h"
40 #include "BKE_gpencil.h"
41 #include "BKE_gpencil_geom.h"
42 #include "BKE_gpencil_modifier.h"
44 #include "BKE_main.h"
45 #include "BKE_material.h"
46 #include "BKE_object_deform.h"
47 #include "BKE_report.h"
48 
49 #include "UI_interface.h"
50 
51 #include "WM_api.h"
52 #include "WM_types.h"
53 
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56 #include "RNA_enum_types.h"
57 #include "RNA_prototypes.h"
58 
59 #include "UI_view2d.h"
60 
61 #include "ED_gpencil.h"
62 #include "ED_keyframing.h"
63 #include "ED_screen.h"
64 #include "ED_view3d.h"
65 
66 #include "DEG_depsgraph.h"
67 #include "DEG_depsgraph_query.h"
68 
69 #include "gpencil_intern.h"
70 
71 /* ************************************************ */
72 /* General Brush Editing Context */
73 
74 /* Context for brush operators */
75 typedef struct tGP_BrushEditData {
76  /* Current editor/region/etc. */
81 
84 
85  /* Current GPencil datablock */
87 
88  /* Brush Settings */
92 
95 
96  /* Space Conversion Data */
98 
99  /* Is the brush currently painting? */
102 
103  /* Start of new sculpt stroke */
104  bool first;
105 
106  /* Is multiframe editing enabled, and are we using falloff for that? */
109 
110  /* Current frame */
111  int cfra;
112 
113  /* Brush Runtime Data: */
114  /* - position and pressure
115  * - the *_prev variants are the previous values
116  */
117  float mval[2], mval_prev[2];
119 
120  /* - effect vector (e.g. 2D/3D translation for grab brush) */
121  float dvec[3];
122 
123  /* rotation for evaluated data */
124  float rot_eval;
125 
126  /* - multiframe falloff factor */
127  float mf_falloff;
128 
129  /* active vertex group */
130  int vrgroup;
131 
132  /* brush geometry (bounding box) */
134 
135  /* Custom data for certain brushes */
136  /* - map from bGPDstroke's to structs containing custom data about those strokes */
138  /* - general customdata */
139  void *customdata;
140 
141  /* Timer for in-place accumulation of brush effect */
143  bool timerTick; /* is this event from a timer */
144 
145  /* Object invert matrix */
146  float inv_mat[4][4];
147 
149  /* Auto-masking strokes. */
152 
154 
155 /* Callback for performing some brush operation on a single point */
157  bGPDstroke *gps,
158  float rotation,
159  int pt_index,
160  const int radius,
161  const int co[2]);
162 
163 /* ************************************************ */
164 /* Utility Functions */
165 
166 /* apply lock axis reset */
168  bGPDspoint *pt,
169  const float save_pt[3])
170 {
171  const ToolSettings *ts = gso->scene->toolsettings;
172  const View3DCursor *cursor = &gso->scene->cursor;
173  const int axis = ts->gp_sculpt.lock_axis;
174 
175  /* lock axis control */
176  switch (axis) {
177  case GP_LOCKAXIS_X: {
178  pt->x = save_pt[0];
179  break;
180  }
181  case GP_LOCKAXIS_Y: {
182  pt->y = save_pt[1];
183  break;
184  }
185  case GP_LOCKAXIS_Z: {
186  pt->z = save_pt[2];
187  break;
188  }
189  case GP_LOCKAXIS_CURSOR: {
190  /* Compute a plane with cursor normal and position of the point before do the sculpt. */
191  const float scale[3] = {1.0f, 1.0f, 1.0f};
192  float plane_normal[3] = {0.0f, 0.0f, 1.0f};
193  float plane[4];
194  float mat[4][4];
195  float r_close[3];
196 
197  loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, scale);
198 
199  mul_mat3_m4_v3(mat, plane_normal);
200  plane_from_point_normal_v3(plane, save_pt, plane_normal);
201 
202  /* find closest point to the plane with the new position */
203  closest_to_plane_v3(r_close, plane, &pt->x);
204  copy_v3_v3(&pt->x, r_close);
205  break;
206  }
207  default: {
208  break;
209  }
210  }
211 }
212 
213 /* Context ---------------------------------------- */
214 
215 /* Get the sculpting settings */
217 {
218  return &scene->toolsettings->gp_sculpt;
219 }
220 
221 /* Brush Operations ------------------------------- */
222 
223 /* Invert behavior of brush? */
225 {
226  /* The basic setting is the brush's setting (from the panel) */
229  /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */
230  if (gso->flag & GP_SCULPT_FLAG_INVERT) {
231  invert ^= true;
232  }
233 
234  /* set temporary status */
235  if (invert) {
237  }
238  else {
240  }
241 
242  return invert;
243 }
244 
245 /* Compute strength of effect */
247  const int radius,
248  const int co[2])
249 {
250  Brush *brush = gso->brush;
251 
252  /* basic strength factor from brush settings */
253  float influence = brush->alpha;
254 
255  /* use pressure? */
257  influence *= gso->pressure;
258  }
259 
260  /* distance fading */
261  int mval_i[2];
262  round_v2i_v2fl(mval_i, gso->mval);
263  float distance = (float)len_v2v2_int(mval_i, co);
264 
265  /* Apply Brush curve. */
266  float brush_falloff = BKE_brush_curve_strength(brush, distance, (float)radius);
267  influence *= brush_falloff;
268 
269  /* apply multiframe falloff */
270  influence *= gso->mf_falloff;
271 
272  /* return influence */
273  return influence;
274 }
275 
276 /* Tag stroke to be recalculated. */
278 {
279  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
280  gps_active->flag |= GP_STROKE_TAG;
281 }
282 
283 /* Recalc any stroke tagged. */
285 {
286  if (gpd == NULL) {
287  return;
288  }
289 
290  bool changed = false;
291 
292  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
293  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
294  if ((gpl->actframe != gpf) && ((gpf->flag & GP_FRAME_SELECT) == 0)) {
295  continue;
296  }
297 
298  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
299  if (gps->flag & GP_STROKE_TAG) {
301  BKE_gpencil_tag_full_update(gpd, gpl, gpf, gps);
302  gps->flag &= ~GP_STROKE_TAG;
303  changed = true;
304  }
305  }
306  }
307  }
308  if (changed) {
311  }
312 }
313 
314 /* ************************************************ */
315 /* Brush Callbacks */
316 /* This section defines the callbacks used by each brush to perform their magic.
317  * These are called on each point within the brush's radius.
318  */
319 
320 /* ----------------------------------------------- */
321 /* Smooth Brush */
322 
323 /* A simple (but slower + inaccurate)
324  * smooth-brush implementation to test the algorithm for stroke smoothing. */
326  bGPDstroke *gps,
327  float UNUSED(rot_eval),
328  int pt_index,
329  const int radius,
330  const int co[2])
331 {
332  float inf = gpencil_brush_influence_calc(gso, radius, co);
333 
334  /* perform smoothing */
336  BKE_gpencil_stroke_smooth_point(gps, pt_index, inf, 2, false, false, gps);
337  }
339  BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf, 2, gps);
340  }
342  BKE_gpencil_stroke_smooth_thickness(gps, pt_index, inf, 2, gps);
343  }
345  BKE_gpencil_stroke_smooth_uv(gps, pt_index, inf, 2, gps);
346  }
347 
348  return true;
349 }
350 
351 /* ----------------------------------------------- */
352 /* Line Thickness Brush */
353 
354 /* Make lines thicker or thinner by the specified amounts */
356  bGPDstroke *gps,
357  float UNUSED(rot_eval),
358  int pt_index,
359  const int radius,
360  const int co[2])
361 {
362  bGPDspoint *pt = gps->points + pt_index;
363  float inf;
364 
365  /* Compute strength of effect
366  * - We divide the strength by 10, so that users can set "sane" values.
367  * Otherwise, good default values are in the range of 0.093
368  */
369  inf = gpencil_brush_influence_calc(gso, radius, co) / 10.0f;
370 
371  /* apply */
372  /* XXX: this is much too strong,
373  * and it should probably do some smoothing with the surrounding stuff. */
374  if (gpencil_brush_invert_check(gso)) {
375  /* make line thinner - reduce stroke pressure */
376  pt->pressure -= inf;
377  }
378  else {
379  /* make line thicker - increase stroke pressure */
380  pt->pressure += inf;
381  }
382 
383  /* Pressure should stay within [0.0, 1.0]
384  * However, it is nice for volumetric strokes to be able to exceed
385  * the upper end of this range. Therefore, we don't actually clamp
386  * down on the upper end.
387  */
388  if (pt->pressure < 0.0f) {
389  pt->pressure = 0.0f;
390  }
391 
392  return true;
393 }
394 
395 /* ----------------------------------------------- */
396 /* Color Strength Brush */
397 
398 /* Make color more or less transparent by the specified amounts */
400  bGPDstroke *gps,
401  float UNUSED(rot_eval),
402  int pt_index,
403  const int radius,
404  const int co[2])
405 {
406  bGPDspoint *pt = gps->points + pt_index;
407  float inf;
408 
409  /* Compute strength of effect */
410  inf = gpencil_brush_influence_calc(gso, radius, co) * 0.125f;
411 
412  /* Invert effect. */
413  if (gpencil_brush_invert_check(gso)) {
414  inf *= -1.0f;
415  }
416 
417  pt->strength = clamp_f(pt->strength + inf, 0.0f, 1.0f);
418 
419  return true;
420 }
421 
422 /* ----------------------------------------------- */
423 /* Grab Brush */
424 
425 /* Custom data per stroke for the Grab Brush
426  *
427  * This basically defines the strength of the effect for each
428  * affected stroke point that was within the initial range of
429  * the brush region.
430  */
431 typedef struct tGPSB_Grab_StrokeData {
432  /* array of indices to corresponding points in the stroke */
433  int *points;
434  /* array of influence weights for each of the included points */
435  float *weights;
436  /* angles to calc transformation */
437  float *rot_eval;
438 
439  /* capacity of the arrays */
440  int capacity;
441  /* actual number of items currently stored */
442  int size;
444 
449 {
451 
452  BLI_assert(gps->totpoints > 0);
453 
454  /* Check if there are buffers already (from a prior run) */
455  if (BLI_ghash_haskey(gso->stroke_customdata, gps)) {
456  /* Ensure that the caches are empty
457  * - Since we reuse these between different strokes, we don't
458  * want the previous invocation's data polluting the arrays
459  */
461  BLI_assert(data != NULL);
462 
463  data->size = 0; /* minimum requirement - so that we can repopulate again */
464 
465  memset(data->points, 0, sizeof(int) * data->capacity);
466  memset(data->weights, 0, sizeof(float) * data->capacity);
467  memset(data->rot_eval, 0, sizeof(float) * data->capacity);
468  }
469  else {
470  /* Create new instance */
471  data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data");
472 
473  data->capacity = gps->totpoints;
474  data->size = 0;
475 
476  data->points = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices");
477  data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights");
478  data->rot_eval = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Rotations");
479 
480  /* hook up to the cache */
482  }
483 }
484 
485 /* store references to stroke points in the initial stage */
487  bGPDstroke *gps,
488  float rot_eval,
489  int pt_index,
490  const int radius,
491  const int co[2])
492 {
494  float inf = gpencil_brush_influence_calc(gso, radius, co);
495 
496  BLI_assert(data != NULL);
497  BLI_assert(data->size < data->capacity);
498 
499  /* insert this point into the set of affected points */
500  data->points[data->size] = pt_index;
501  data->weights[data->size] = inf;
502  data->rot_eval[data->size] = rot_eval;
503  data->size++;
504 
505  /* done */
506  return true;
507 }
508 
509 /* Compute effect vector for grab brush */
511 {
512  /* Convert mouse-movements to movement vector */
513  RegionView3D *rv3d = gso->region->regiondata;
514  float *rvec = gso->object->loc;
515  const float zfac = ED_view3d_calc_zfac(rv3d, rvec);
516 
517  float mval_f[2];
518 
519  /* convert from 2D screenspace to 3D... */
520  mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
521  mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
522 
523  /* apply evaluated data transformation */
524  if (gso->rot_eval != 0.0f) {
525  const float cval = cos(gso->rot_eval);
526  const float sval = sin(gso->rot_eval);
527  float r[2];
528  r[0] = (mval_f[0] * cval) - (mval_f[1] * sval);
529  r[1] = (mval_f[0] * sval) + (mval_f[1] * cval);
530  copy_v2_v2(mval_f, r);
531  }
532 
533  ED_view3d_win_to_delta(gso->region, mval_f, zfac, gso->dvec);
534 }
535 
536 /* Apply grab transform to all relevant points of the affected strokes */
538  bGPDstroke *gps,
539  const float diff_mat[4][4])
540 {
542  /* If a new frame is created, could be impossible find the stroke. */
543  if (data == NULL) {
544  return;
545  }
546 
547  float matrix[4][4], inverse_diff_mat[4][4];
548  copy_m4_m4(matrix, diff_mat);
549  zero_axis_bias_m4(matrix);
550  invert_m4_m4(inverse_diff_mat, matrix);
551 
552  /* Apply dvec to all of the stored points */
553  for (int i = 0; i < data->size; i++) {
554  bGPDspoint *pt = &gps->points[data->points[i]];
555  float delta[3] = {0.0f};
556 
557  /* get evaluated transformation */
558  gso->rot_eval = data->rot_eval[i];
560 
561  /* adjust the amount of displacement to apply */
562  mul_v3_v3fl(delta, gso->dvec, data->weights[i]);
563 
564  float fpt[3];
565  float save_pt[3];
566  copy_v3_v3(save_pt, &pt->x);
567  /* apply transformation */
568  mul_v3_m4v3(fpt, diff_mat, &pt->x);
569  /* apply */
570  add_v3_v3v3(&pt->x, fpt, delta);
571  /* undo transformation to the init parent position */
572  mul_m4_v3(inverse_diff_mat, &pt->x);
573 
574  /* compute lock axis */
575  gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
576  }
577 }
578 
579 /* free customdata used for handling this stroke */
581 {
583 
584  /* free arrays */
585  MEM_SAFE_FREE(data->points);
586  MEM_SAFE_FREE(data->weights);
587  MEM_SAFE_FREE(data->rot_eval);
588 
589  /* ... and this item itself, since it was also allocated */
590  MEM_freeN(data);
591 }
592 
593 /* ----------------------------------------------- */
594 /* Push Brush */
595 /* NOTE: Depends on gpencil_brush_grab_calc_dvec() */
597  bGPDstroke *gps,
598  float UNUSED(rot_eval),
599  int pt_index,
600  const int radius,
601  const int co[2])
602 {
603  bGPDspoint *pt = gps->points + pt_index;
604  float save_pt[3];
605  copy_v3_v3(save_pt, &pt->x);
606 
607  float inf = gpencil_brush_influence_calc(gso, radius, co);
608  float delta[3] = {0.0f};
609 
610  /* adjust the amount of displacement to apply */
611  mul_v3_v3fl(delta, gso->dvec, inf);
612 
613  /* apply */
614  mul_mat3_m4_v3(gso->inv_mat, delta); /* only rotation component */
615  add_v3_v3(&pt->x, delta);
616 
617  /* compute lock axis */
618  gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
619 
620  /* done */
621  return true;
622 }
623 
624 /* ----------------------------------------------- */
625 /* Pinch Brush */
626 /* Compute reference midpoint for the brush - this is what we'll be moving towards */
628 {
629  /* Convert mouse position to 3D space
630  * See: gpencil_paint.c :: gpencil_stroke_convertcoords()
631  */
632  RegionView3D *rv3d = gso->region->regiondata;
633  const float *rvec = gso->object->loc;
634  const float zfac = ED_view3d_calc_zfac(rv3d, rvec);
635 
636  float mval_prj[2];
637 
638  if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
639  V3D_PROJ_RET_OK) {
640  float dvec[3];
641  float xy_delta[2];
642  sub_v2_v2v2(xy_delta, mval_prj, gso->mval);
643  ED_view3d_win_to_delta(gso->region, xy_delta, zfac, dvec);
644  sub_v3_v3v3(gso->dvec, rvec, dvec);
645  }
646  else {
647  zero_v3(gso->dvec);
648  }
649 }
650 
651 /* Shrink distance between midpoint and this point... */
653  bGPDstroke *gps,
654  float UNUSED(rot_eval),
655  int pt_index,
656  const int radius,
657  const int co[2])
658 {
659  bGPDspoint *pt = gps->points + pt_index;
660  float fac, inf;
661  float vec[3];
662  float save_pt[3];
663  copy_v3_v3(save_pt, &pt->x);
664 
665  /* Scale down standard influence value to get it more manageable...
666  * - No damping = Unmanageable at > 0.5 strength
667  * - Div 10 = Not enough effect
668  * - Div 5 = Happy medium... (by trial and error)
669  */
670  inf = gpencil_brush_influence_calc(gso, radius, co) / 5.0f;
671 
672  /* 1) Make this point relative to the cursor/midpoint (dvec) */
673  float fpt[3];
674  mul_v3_m4v3(fpt, gso->object->obmat, &pt->x);
675  sub_v3_v3v3(vec, fpt, gso->dvec);
676 
677  /* 2) Shrink the distance by pulling the point towards the midpoint
678  * (0.0 = at midpoint, 1 = at edge of brush region)
679  * OR
680  * Increase the distance (if inverting the brush action!)
681  */
682  if (gpencil_brush_invert_check(gso)) {
683  /* Inflate (inverse) */
684  fac = 1.0f + (inf * inf); /* squared to temper the effect... */
685  }
686  else {
687  /* Shrink (default) */
688  fac = 1.0f - (inf * inf); /* squared to temper the effect... */
689  }
690  mul_v3_fl(vec, fac);
691 
692  /* 3) Translate back to original space, with the shrinkage applied */
693  add_v3_v3v3(fpt, gso->dvec, vec);
694  mul_v3_m4v3(&pt->x, gso->object->imat, fpt);
695 
696  /* compute lock axis */
697  gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
698 
699  /* done */
700  return true;
701 }
702 
703 /* ----------------------------------------------- */
704 /* Twist Brush - Rotate Around midpoint */
705 /* Take the screenspace coordinates of the point, rotate this around the brush midpoint,
706  * convert the rotated point and convert it into "data" space
707  */
708 
710  bGPDstroke *gps,
711  float UNUSED(rot_eval),
712  int pt_index,
713  const int radius,
714  const int co[2])
715 {
716  bGPDspoint *pt = gps->points + pt_index;
717  float angle, inf;
718  float save_pt[3];
719  copy_v3_v3(save_pt, &pt->x);
720 
721  /* Angle to rotate by */
722  inf = gpencil_brush_influence_calc(gso, radius, co);
723  angle = DEG2RADF(1.0f) * inf;
724 
725  if (gpencil_brush_invert_check(gso)) {
726  /* invert angle that we rotate by */
727  angle *= -1;
728  }
729 
730  /* Rotate in 2D or 3D space? */
731  if (gps->flag & GP_STROKE_3DSPACE) {
732  /* Perform rotation in 3D space... */
733  RegionView3D *rv3d = gso->region->regiondata;
734  float rmat[3][3];
735  float axis[3];
736  float vec[3];
737 
738  /* Compute rotation matrix - rotate around view vector by angle */
739  negate_v3_v3(axis, rv3d->persinv[2]);
740  normalize_v3(axis);
741 
743 
744  /* Rotate point */
745  float fpt[3];
746  mul_v3_m4v3(fpt, gso->object->obmat, &pt->x);
747  sub_v3_v3v3(vec, fpt, gso->dvec); /* make relative to center
748  * (center is stored in dvec) */
749  mul_m3_v3(rmat, vec);
750  add_v3_v3v3(fpt, vec, gso->dvec); /* restore */
751  mul_v3_m4v3(&pt->x, gso->object->imat, fpt);
752 
753  /* compute lock axis */
754  gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
755  }
756  else {
757  const float axis[3] = {0.0f, 0.0f, 1.0f};
758  float vec[3] = {0.0f};
759  float rmat[3][3];
760 
761  /* Express position of point relative to cursor, ready to rotate */
762  /* XXX: There is still some offset here, but it's close to working as expected. */
763  vec[0] = (float)(co[0] - gso->mval[0]);
764  vec[1] = (float)(co[1] - gso->mval[1]);
765 
766  /* rotate point */
768  mul_m3_v3(rmat, vec);
769 
770  /* Convert back to screen-coordinates */
771  vec[0] += (float)gso->mval[0];
772  vec[1] += (float)gso->mval[1];
773 
774  /* Map from screen-coordinates to final coordinate space */
775  if (gps->flag & GP_STROKE_2DSPACE) {
776  View2D *v2d = gso->gsc.v2d;
777  UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y);
778  }
779  else {
780  /* XXX */
781  copy_v2_v2(&pt->x, vec);
782  }
783  }
784 
785  /* done */
786  return true;
787 }
788 
789 /* ----------------------------------------------- */
790 /* Randomize Brush */
791 /* Apply some random jitter to the point */
793  bGPDstroke *gps,
794  float UNUSED(rot_eval),
795  int pt_index,
796  const int radius,
797  const int co[2])
798 {
799  bGPDspoint *pt = gps->points + pt_index;
800  float save_pt[3];
801  copy_v3_v3(save_pt, &pt->x);
802 
803  /* Amount of jitter to apply depends on the distance of the point to the cursor,
804  * as well as the strength of the brush
805  */
806  const float inf = gpencil_brush_influence_calc(gso, radius, co) / 2.0f;
807  const float fac = BLI_rng_get_float(gso->rng) * inf;
808 
809  /* apply random to position */
811  /* Jitter is applied perpendicular to the mouse movement vector
812  * - We compute all effects in screenspace (since it's easier)
813  * and then project these to get the points/distances in
814  * view-space as needed.
815  */
816  float mvec[2], svec[2];
817 
818  /* mouse movement in ints -> floats */
819  mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
820  mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
821 
822  /* rotate mvec by 90 degrees... */
823  svec[0] = -mvec[1];
824  svec[1] = mvec[0];
825 
826  /* scale the displacement by the random displacement, and apply */
827  if (BLI_rng_get_float(gso->rng) > 0.5f) {
828  mul_v2_fl(svec, -fac);
829  }
830  else {
831  mul_v2_fl(svec, fac);
832  }
833 
834  /* Convert to data-space. */
835  if (gps->flag & GP_STROKE_3DSPACE) {
836  /* 3D: Project to 3D space */
837  bool flip;
838  RegionView3D *rv3d = gso->region->regiondata;
839  const float zfac = ED_view3d_calc_zfac_ex(rv3d, &pt->x, &flip);
840  if (flip == false) {
841  float dvec[3];
842  ED_view3d_win_to_delta(gso->gsc.region, svec, zfac, dvec);
843  add_v3_v3(&pt->x, dvec);
844  /* compute lock axis */
845  gpencil_sculpt_compute_lock_axis(gso, pt, save_pt);
846  }
847  }
848  }
849  /* apply random to strength */
851  if (BLI_rng_get_float(gso->rng) > 0.5f) {
852  pt->strength += fac;
853  }
854  else {
855  pt->strength -= fac;
856  }
857  CLAMP_MIN(pt->strength, 0.0f);
858  CLAMP_MAX(pt->strength, 1.0f);
859  }
860  /* apply random to thickness (use pressure) */
862  if (BLI_rng_get_float(gso->rng) > 0.5f) {
863  pt->pressure += fac;
864  }
865  else {
866  pt->pressure -= fac;
867  }
868  /* only limit lower value */
869  CLAMP_MIN(pt->pressure, 0.0f);
870  }
871  /* apply random to UV (use pressure) */
873  if (BLI_rng_get_float(gso->rng) > 0.5f) {
874  pt->uv_rot += fac;
875  }
876  else {
877  pt->uv_rot -= fac;
878  }
879  CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
880  }
881 
882  /* done */
883  return true;
884 }
885 
886 /* ************************************************ */
887 /* Non Callback-Based Brushes */
888 /* Clone Brush ------------------------------------- */
889 /* How this brush currently works:
890  * - If this is start of the brush stroke, paste immediately under the cursor
891  * by placing the midpoint of the buffer strokes under the cursor now
892  *
893  * - Otherwise, in:
894  * "Stamp Mode" - Move the newly pasted strokes so that their center follows the cursor
895  * "Continuous" - Repeatedly just paste new copies for where the brush is now
896  */
897 
898 /* Custom state data for clone brush */
899 typedef struct tGPSB_CloneBrushData {
900  /* midpoint of the strokes on the clipboard */
901  float buffer_midpoint[3];
902 
903  /* number of strokes in the paste buffer (and/or to be created each time) */
904  size_t totitems;
905 
906  /* for "stamp" mode, the currently pasted brushes */
908 
912 
913 /* Initialize "clone" brush data. */
915 {
917  bGPDstroke *gps;
918 
919  /* Initialize custom-data. */
920  gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData");
921 
922  /* compute midpoint of strokes on clipboard */
923  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
924  if (ED_gpencil_stroke_can_use(C, gps)) {
925  const float dfac = 1.0f / ((float)gps->totpoints);
926  float mid[3] = {0.0f};
927 
928  bGPDspoint *pt;
929  int i;
930 
931  /* compute midpoint of this stroke */
932  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
933  float co[3];
934 
935  mul_v3_v3fl(co, &pt->x, dfac);
936  add_v3_v3(mid, co);
937  }
938 
939  /* combine this stroke's data with the main data */
940  add_v3_v3(data->buffer_midpoint, mid);
941  data->totitems++;
942  }
943  }
944 
945  /* Divide the midpoint by the number of strokes, to finish averaging it */
946  if (data->totitems > 1) {
947  mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems);
948  }
949 
950  /* Create a buffer for storing the current strokes */
951  if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) {
952  data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems,
953  "cloned strokes ptr array");
954  }
955 
956  /* Init colormap for mapping between the pasted stroke's source color (names)
957  * and the final colors that will be used here instead.
958  */
960 }
961 
962 /* Free custom data used for "clone" brush */
964 {
966 
967  /* free strokes array */
968  MEM_SAFE_FREE(data->new_strokes);
969 
970  /* free copybuf colormap */
971  if (data->new_colors) {
972  BLI_ghash_free(data->new_colors, NULL, NULL);
973  data->new_colors = NULL;
974  }
975 
976  /* free the customdata itself */
977  MEM_freeN(data);
978  gso->customdata = NULL;
979 }
980 
981 /* Create new copies of the strokes on the clipboard */
983 {
985 
986  Object *ob = gso->object;
987  bGPdata *gpd = (bGPdata *)ob->data;
988  Scene *scene = gso->scene;
989  bGPDstroke *gps;
990 
991  float delta[3];
992  size_t strokes_added = 0;
993 
994  /* Compute amount to offset the points by */
995  /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */
996 
997  gpencil_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */
998  sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint);
999 
1000  /* Copy each stroke into the layer */
1001  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
1002  if (ED_gpencil_stroke_can_use(C, gps)) {
1003  bGPDstroke *new_stroke;
1004  bGPDspoint *pt;
1005  int i;
1006 
1007  bGPDlayer *gpl = NULL;
1008  /* Try to use original layer. */
1009  if (gps->runtime.tmp_layerinfo[0] != '\0') {
1011  }
1012 
1013  /* if not available, use active layer. */
1014  if (gpl == NULL) {
1016  }
1019  if (gpf == NULL) {
1020  continue;
1021  }
1022 
1023  /* Make a new stroke */
1024  new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
1025 
1026  new_stroke->next = new_stroke->prev = NULL;
1027  BLI_addtail(&gpf->strokes, new_stroke);
1028 
1029  /* Fix color references */
1030  Material *ma = BLI_ghash_lookup(data->new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
1031  new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
1032  if (!ma || new_stroke->mat_nr < 0) {
1033  new_stroke->mat_nr = 0;
1034  }
1035  /* Adjust all the stroke's points, so that the strokes
1036  * get pasted relative to where the cursor is now
1037  */
1038  for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) {
1039  /* Rotate around center new position */
1040  mul_mat3_m4_v3(gso->object->obmat, &pt->x); /* only rotation component */
1041 
1042  /* assume that the delta can just be applied, and then everything works */
1043  add_v3_v3(&pt->x, delta);
1044  mul_m4_v3(gso->object->imat, &pt->x);
1045  }
1046 
1047  /* Store ref for later */
1048  if ((data->new_strokes) && (strokes_added < data->totitems)) {
1049  data->new_strokes[strokes_added] = new_stroke;
1050  strokes_added++;
1051  }
1052  }
1053  }
1054 }
1055 
1056 /* Move newly-added strokes around - "Stamp" mode of the Clone brush */
1058 {
1060  size_t snum;
1061 
1062  /* Compute the amount of movement to apply (overwrites dvec) */
1063  gso->rot_eval = 0.0f;
1065 
1066  /* For each of the stored strokes, apply the offset to each point */
1067  /* NOTE: Again this assumes that in the 3D view,
1068  * we only have 3d space and not screenspace strokes... */
1069  for (snum = 0; snum < data->totitems; snum++) {
1070  bGPDstroke *gps = data->new_strokes[snum];
1071  bGPDspoint *pt;
1072  int i;
1073 
1074  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1075  /* "Smudge" Effect falloff */
1076  float delta[3] = {0.0f};
1077  int sco[2] = {0};
1078  float influence;
1079 
1080  /* compute influence on point */
1081  gpencil_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]);
1082  influence = gpencil_brush_influence_calc(gso, gso->brush->size, sco);
1083 
1084  /* adjust the amount of displacement to apply */
1085  mul_v3_v3fl(delta, gso->dvec, influence);
1086 
1087  /* apply */
1088  add_v3_v3(&pt->x, delta);
1089  }
1090  }
1091 }
1092 
1093 /* Entry-point for applying "clone" brush. */
1095 {
1096  /* Which "mode" are we operating in? */
1097  if (gso->first) {
1098  /* Create initial clones */
1099  gpencil_brush_clone_add(C, gso);
1100  }
1101  else {
1102  /* Stamp or Continuous Mode */
1103  if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) {
1104  /* Stamp - Proceed to translate the newly added strokes */
1106  }
1107  else {
1108  /* Continuous - Just keep pasting every time we move. */
1109  /* TODO: The spacing of repeat should be controlled using a
1110  * "stepsize" or similar property? */
1111  gpencil_brush_clone_add(C, gso);
1112  }
1113  }
1114 
1115  return true;
1116 }
1117 
1118 /* ************************************************ */
1119 /* Header Info for GPencil Sculpt */
1120 
1122 {
1123  Brush *brush = gso->brush;
1124  char str[UI_MAX_DRAW_STR] = "";
1125 
1126  BLI_snprintf(str,
1127  sizeof(str),
1128  TIP_("GPencil Sculpt: %s Stroke | LMB to paint | RMB/Escape to Exit"
1129  " | Ctrl to Invert Action | Wheel Up/Down for Size "
1130  " | Shift-Wheel Up/Down for Strength"),
1131  brush->id.name + 2);
1132 
1134 }
1135 
1136 /* ************************************************ */
1137 /* Grease Pencil Sculpting Operator */
1138 
1139 /* Init/Exit ----------------------------------------------- */
1140 
1142 {
1146 
1147  /* set the brush using the tool */
1148  tGP_BrushEditData *gso;
1149 
1150  /* setup operator data */
1151  gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData");
1152  op->customdata = gso;
1153 
1155  gso->bmain = CTX_data_main(C);
1156  /* store state */
1158 
1159  /* Random generator, only init once. */
1160  uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
1161  rng_seed ^= POINTER_AS_UINT(gso);
1162  gso->rng = BLI_rng_new(rng_seed);
1163 
1164  gso->is_painting = false;
1165  gso->first = true;
1166  gso->mval_prev[0] = -1.0f;
1167 
1169  gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */
1170 
1171  gso->scene = scene;
1172  gso->object = ob;
1173  if (ob) {
1174  float matrix[4][4];
1175  copy_m4_m4(matrix, ob->obmat);
1176  zero_axis_bias_m4(matrix);
1177  invert_m4_m4(gso->inv_mat, matrix);
1178  gso->vrgroup = gso->gpd->vertex_group_active_index - 1;
1179  if (!BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup)) {
1180  gso->vrgroup = -1;
1181  }
1182  /* Check if some modifier can transform the stroke. */
1184  }
1185  else {
1186  unit_m4(gso->inv_mat);
1187  gso->vrgroup = -1;
1188  gso->is_transformed = false;
1189  }
1190 
1191  gso->area = CTX_wm_area(C);
1192  gso->region = CTX_wm_region(C);
1193 
1194  Paint *paint = &ts->gp_sculptpaint->paint;
1195  Brush *brush = paint->brush;
1196  gso->brush = brush;
1198 
1199  if (brush->gpencil_settings->sculpt_mode_flag &
1202  gso->automasking_strokes = BLI_ghash_ptr_new(__func__);
1203  }
1204  else {
1205  if (gso->automasking_strokes != NULL) {
1207  }
1208  gso->automasking_strokes = NULL;
1209  }
1210  /* save mask */
1211  gso->mask = ts->gpencil_selectmode_sculpt;
1212 
1213  /* multiframe settings */
1216 
1217  /* Init multi-edit falloff curve data before doing anything,
1218  * so we won't have to do it again later. */
1219  if (gso->is_multiframe) {
1221  }
1222 
1223  /* Initialize custom data for brushes. */
1224  char tool = gso->brush->gpencil_sculpt_tool;
1225  switch (tool) {
1226  case GPSCULPT_TOOL_CLONE: {
1227  bGPDstroke *gps;
1228  bool found = false;
1229 
1230  /* check that there are some usable strokes in the buffer */
1231  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
1232  if (ED_gpencil_stroke_can_use(C, gps)) {
1233  found = true;
1234  break;
1235  }
1236  }
1237 
1238  if (found == false) {
1239  /* STOP HERE! Nothing to paste! */
1240  BKE_report(op->reports,
1241  RPT_ERROR,
1242  "Copy some strokes to the clipboard before using the Clone brush to paste "
1243  "copies of them");
1244 
1245  MEM_freeN(gso);
1246  op->customdata = NULL;
1247  return false;
1248  }
1249  /* Initialize custom-data. */
1251  break;
1252  }
1253 
1254  case GPSCULPT_TOOL_GRAB: {
1255  /* Initialize the cache needed for this brush. */
1256  gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash");
1257  break;
1258  }
1259 
1260  /* Others - No customdata needed */
1261  default:
1262  break;
1263  }
1264 
1265  /* setup space conversions */
1267 
1268  /* update header */
1270 
1271  return true;
1272 }
1273 
1275 {
1276  tGP_BrushEditData *gso = op->customdata;
1277  wmWindow *win = CTX_wm_window(C);
1278  char tool = gso->brush->gpencil_sculpt_tool;
1279 
1280  /* free brush-specific data */
1281  switch (tool) {
1282  case GPSCULPT_TOOL_GRAB: {
1283  /* Free per-stroke customdata
1284  * - Keys don't need to be freed, as those are the strokes
1285  * - Values assigned to those keys do, as they are custom structs
1286  */
1288  break;
1289  }
1290 
1291  case GPSCULPT_TOOL_CLONE: {
1292  /* Free customdata */
1294  break;
1295  }
1296 
1297  default:
1298  if (gso->stroke_customdata != NULL) {
1300  gso->stroke_customdata = NULL;
1301  }
1302  break;
1303  }
1304 
1305  /* unregister timer (only used for realtime) */
1306  if (gso->timer) {
1308  }
1309 
1310  if (gso->rng != NULL) {
1311  BLI_rng_free(gso->rng);
1312  }
1313 
1314  if (gso->automasking_strokes != NULL) {
1316  }
1317 
1318  /* Disable headerprints. */
1320 
1321  /* disable temp invert flag */
1323 
1324  /* Update geometry data for tagged strokes. */
1326 
1327  /* free operator data */
1328  MEM_freeN(gso);
1329  op->customdata = NULL;
1330 }
1331 
1332 /* poll callback for stroke sculpting operator(s) */
1334 {
1335  ScrArea *area = CTX_wm_area(C);
1336  if (area && area->spacetype != SPACE_VIEW3D) {
1337  return false;
1338  }
1339 
1340  /* NOTE: this is a bit slower, but is the most accurate... */
1341  return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
1342 }
1343 
1344 /* Init Sculpt Stroke ---------------------------------- */
1345 
1347 {
1348  bGPdata *gpd = gso->gpd;
1349 
1350  Scene *scene = gso->scene;
1351  int cfra = scene->r.cfra;
1352 
1353  /* only try to add a new frame if this is the first stroke, or the frame has changed */
1354  if ((gpd == NULL) || (cfra == gso->cfra)) {
1355  return;
1356  }
1357 
1358  /* go through each layer, and ensure that we've got a valid frame to use */
1359  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
1360  if (!IS_AUTOKEY_ON(scene) && (gpl->actframe == NULL)) {
1361  continue;
1362  }
1363 
1364  /* only editable and visible layers are considered */
1365  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1366  bGPDframe *gpf = gpl->actframe;
1367 
1368  /* Make a new frame to work on if the layer's frame
1369  * and the current scene frame don't match up:
1370  * - This is useful when animating as it saves that "uh-oh" moment when you realize you've
1371  * spent too much time editing the wrong frame.
1372  */
1373  if (IS_AUTOKEY_ON(scene) && (gpf->framenum != cfra)) {
1374  BKE_gpencil_frame_addcopy(gpl, cfra);
1376  /* Need tag to recalculate evaluated data to avoid crashes. */
1379  }
1380  }
1381  }
1382 
1383  /* save off new current frame, so that next update works fine */
1384  gso->cfra = cfra;
1385 }
1386 
1387 /* Apply ----------------------------------------------- */
1388 
1389 /* Get angle of the segment relative to the original segment before any transformation
1390  * For strokes with one point only this is impossible to calculate because there isn't a
1391  * valid reference point.
1392  */
1394  bGPDstroke *gps_eval,
1395  bGPDspoint *pt_eval,
1396  int idx_eval)
1397 {
1398  /* If multiframe or no modifiers, return 0. */
1399  if (GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd) || (!gso->is_transformed)) {
1400  return 0.0f;
1401  }
1402 
1403  const GP_SpaceConversion *gsc = &gso->gsc;
1404  bGPDstroke *gps_orig = (gps_eval->runtime.gps_orig) ? gps_eval->runtime.gps_orig : gps_eval;
1405  bGPDspoint *pt_orig = &gps_orig->points[pt_eval->runtime.idx_orig];
1406  bGPDspoint *pt_prev_eval = NULL;
1407  bGPDspoint *pt_orig_prev = NULL;
1408  if (idx_eval != 0) {
1409  pt_prev_eval = &gps_eval->points[idx_eval - 1];
1410  }
1411  else {
1412  if (gps_eval->totpoints > 1) {
1413  pt_prev_eval = &gps_eval->points[idx_eval + 1];
1414  }
1415  else {
1416  return 0.0f;
1417  }
1418  }
1419 
1420  if (pt_eval->runtime.idx_orig != 0) {
1421  pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig - 1];
1422  }
1423  else {
1424  if (gps_orig->totpoints > 1) {
1425  pt_orig_prev = &gps_orig->points[pt_eval->runtime.idx_orig + 1];
1426  }
1427  else {
1428  return 0.0f;
1429  }
1430  }
1431 
1432  /* create 2D vectors of the stroke segments */
1433  float v_orig_a[2], v_orig_b[2], v_eval_a[2], v_eval_b[2];
1434 
1435  gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig->x, v_orig_a);
1436  gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_orig_prev->x, v_orig_b);
1437  sub_v2_v2(v_orig_a, v_orig_b);
1438 
1439  gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_eval->x, v_eval_a);
1440  gpencil_point_3d_to_xy(gsc, GP_STROKE_3DSPACE, &pt_prev_eval->x, v_eval_b);
1441  sub_v2_v2(v_eval_a, v_eval_b);
1442 
1443  return angle_v2v2(v_orig_a, v_eval_a);
1444 }
1445 
1446 /* Apply brush operation to points in this stroke */
1448  bGPDstroke *gps,
1449  const float diff_mat[4][4],
1450  GP_BrushApplyCb apply)
1451 {
1452  GP_SpaceConversion *gsc = &gso->gsc;
1453  rcti *rect = &gso->brush_rect;
1454  Brush *brush = gso->brush;
1455  char tool = gso->brush->gpencil_sculpt_tool;
1456  const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
1457  gso->brush->size;
1458  const bool is_masking = GPENCIL_ANY_SCULPT_MASK(gso->mask);
1459 
1460  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1461  bGPDspoint *pt_active = NULL;
1462 
1463  bGPDspoint *pt1, *pt2;
1464  bGPDspoint *pt = NULL;
1465  int pc1[2] = {0};
1466  int pc2[2] = {0};
1467  int i;
1468  int index;
1469  bool include_last = false;
1470  bool changed = false;
1471  float rot_eval = 0.0f;
1472 
1473  if (gps->totpoints == 1) {
1474  bGPDspoint pt_temp;
1475  pt = &gps->points[0];
1476  if ((is_masking && (pt->flag & GP_SPOINT_SELECT) != 0) || (!is_masking)) {
1477  gpencil_point_to_parent_space(gps->points, diff_mat, &pt_temp);
1478  gpencil_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
1479 
1480  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1481  /* Do bound-box check first. */
1482  if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
1483  /* only check if point is inside */
1484  int mval_i[2];
1485  round_v2i_v2fl(mval_i, gso->mval);
1486  if (len_v2v2_int(mval_i, pc1) <= radius) {
1487  /* apply operation to this point */
1488  if (pt_active != NULL) {
1489  rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, 0);
1490  changed = apply(gso, gps_active, rot_eval, 0, radius, pc1);
1491  }
1492  }
1493  }
1494  }
1495  }
1496  else {
1497  /* Loop over the points in the stroke, checking for intersections
1498  * - an intersection means that we touched the stroke
1499  */
1500  for (i = 0; (i + 1) < gps->totpoints; i++) {
1501  /* Get points to work with */
1502  pt1 = gps->points + i;
1503  pt2 = gps->points + i + 1;
1504 
1505  /* Skip if neither one is selected
1506  * (and we are only allowed to edit/consider selected points) */
1507  if (GPENCIL_ANY_SCULPT_MASK(gso->mask)) {
1508  if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) {
1509  include_last = false;
1510  continue;
1511  }
1512  }
1513  bGPDspoint npt;
1514  gpencil_point_to_parent_space(pt1, diff_mat, &npt);
1515  gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
1516 
1517  gpencil_point_to_parent_space(pt2, diff_mat, &npt);
1518  gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
1519 
1520  /* Check that point segment of the bound-box of the selection stroke. */
1521  if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
1522  ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) {
1523  /* Check if point segment of stroke had anything to do with
1524  * brush region (either within stroke painted, or on its lines)
1525  * - this assumes that line-width is irrelevant.
1526  */
1527  if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
1528  /* Apply operation to these points */
1529  bool ok = false;
1530 
1531  /* To each point individually... */
1532  pt = &gps->points[i];
1533  if ((i != gps->totpoints - 2) && (pt->runtime.pt_orig == NULL) &&
1534  (tool != GPSCULPT_TOOL_GRAB)) {
1535  continue;
1536  }
1537  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1538  /* If masked and the point is not selected, skip it. */
1539  if (GPENCIL_ANY_SCULPT_MASK(gso->mask) && ((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
1540  continue;
1541  }
1542  index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
1543  if ((pt_active != NULL) && (index < gps_active->totpoints)) {
1544  rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i);
1545  ok = apply(gso, gps_active, rot_eval, index, radius, pc1);
1546  }
1547 
1548  /* Only do the second point if this is the last segment,
1549  * and it is unlikely that the point will get handled
1550  * otherwise.
1551  *
1552  * NOTE: There is a small risk here that the second point wasn't really
1553  * actually in-range. In that case, it only got in because
1554  * the line linking the points was!
1555  */
1556  if (i + 1 == gps->totpoints - 1) {
1557  pt = &gps->points[i + 1];
1558  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1559  index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i + 1;
1560  if ((pt_active != NULL) && (index < gps_active->totpoints)) {
1561  rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i + 1);
1562  ok |= apply(gso, gps_active, rot_eval, index, radius, pc2);
1563  include_last = false;
1564  }
1565  }
1566  else {
1567  include_last = true;
1568  }
1569 
1570  changed |= ok;
1571  }
1572  else if (include_last) {
1573  /* This case is for cases where for whatever reason the second vert (1st here)
1574  * doesn't get included because the whole edge isn't in bounds,
1575  * but it would've qualified since it did with the previous step
1576  * (but wasn't added then, to avoid double-ups).
1577  */
1578  pt = &gps->points[i];
1579  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1580  index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
1581  if ((pt_active != NULL) && (index < gps_active->totpoints)) {
1582  rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i);
1583  changed |= apply(gso, gps_active, rot_eval, index, radius, pc1);
1584  include_last = false;
1585  }
1586  }
1587  }
1588  }
1589  }
1590 
1591  return changed;
1592 }
1593 
1594 /* Apply sculpt brushes to strokes in the given frame */
1596  tGP_BrushEditData *gso,
1597  bGPDlayer *gpl,
1598  bGPDframe *gpf,
1599  const float diff_mat[4][4])
1600 {
1601  bool changed = false;
1602  bool redo_geom = false;
1603  Object *ob = gso->object;
1604  bGPdata *gpd = ob->data;
1605  const char tool = gso->brush->gpencil_sculpt_tool;
1606  GP_SpaceConversion *gsc = &gso->gsc;
1607  Brush *brush = gso->brush;
1608  const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
1609  gso->brush->size;
1610  const bool is_automasking = (brush->gpencil_settings->sculpt_mode_flag &
1614  /* Calc bound box matrix. */
1615  float bound_mat[4][4];
1616  BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat);
1617 
1618  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
1619  /* skip strokes that are invalid for current view */
1620  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1621  continue;
1622  }
1623  /* check if the color is editable */
1624  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
1625  continue;
1626  }
1627 
1628  {
1629  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1630  if ((is_automasking) && (!BLI_ghash_haskey(gso->automasking_strokes, gps_active))) {
1631  continue;
1632  }
1633  }
1634 
1635  /* Check if the stroke collide with brush. */
1636  if ((gps->totpoints > 1) &&
1637  (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat))) {
1638  continue;
1639  }
1640 
1641  switch (tool) {
1642  case GPSCULPT_TOOL_SMOOTH: /* Smooth strokes */
1643  {
1644  changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_smooth_apply);
1645  redo_geom |= changed;
1646  break;
1647  }
1648 
1649  case GPSCULPT_TOOL_THICKNESS: /* Adjust stroke thickness */
1650  {
1651  changed |= gpencil_sculpt_brush_do_stroke(
1652  gso, gps, diff_mat, gpencil_brush_thickness_apply);
1653  break;
1654  }
1655 
1656  case GPSCULPT_TOOL_STRENGTH: /* Adjust stroke color strength */
1657  {
1658  changed |= gpencil_sculpt_brush_do_stroke(
1659  gso, gps, diff_mat, gpencil_brush_strength_apply);
1660  break;
1661  }
1662 
1663  case GPSCULPT_TOOL_GRAB: /* Grab points */
1664  {
1665  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1666  if (gps_active != NULL) {
1667  if (gso->first) {
1668  /* First time this brush stroke is being applied:
1669  * 1) Prepare data buffers (init/clear) for this stroke
1670  * 2) Use the points now under the cursor
1671  */
1672  gpencil_brush_grab_stroke_init(gso, gps_active);
1673  changed |= gpencil_sculpt_brush_do_stroke(
1674  gso, gps_active, bound_mat, gpencil_brush_grab_store_points);
1675  }
1676  else {
1677  /* Apply effect to the stored points */
1678  gpencil_brush_grab_apply_cached(gso, gps_active, diff_mat);
1679  changed |= true;
1680  }
1681  }
1682  redo_geom |= changed;
1683  break;
1684  }
1685 
1686  case GPSCULPT_TOOL_PUSH: /* Push points */
1687  {
1688  changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_push_apply);
1689  redo_geom |= changed;
1690  break;
1691  }
1692 
1693  case GPSCULPT_TOOL_PINCH: /* Pinch points */
1694  {
1695  changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_pinch_apply);
1696  redo_geom |= changed;
1697  break;
1698  }
1699 
1700  case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */
1701  {
1702  changed |= gpencil_sculpt_brush_do_stroke(gso, gps, diff_mat, gpencil_brush_twist_apply);
1703  redo_geom |= changed;
1704  break;
1705  }
1706 
1707  case GPSCULPT_TOOL_RANDOMIZE: /* Apply jitter */
1708  {
1709  changed |= gpencil_sculpt_brush_do_stroke(
1710  gso, gps, diff_mat, gpencil_brush_randomize_apply);
1711  redo_geom |= changed;
1712  break;
1713  }
1714 
1715  default:
1716  printf("ERROR: Unknown type of GPencil Sculpt brush \n");
1717  break;
1718  }
1719 
1720  /* Triangulation must be calculated. */
1721  if (redo_geom) {
1722  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1723  if (gpl->actframe == gpf) {
1724  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
1725  /* Update active frame now, only if material has fill. */
1726  if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
1727  BKE_gpencil_stroke_geometry_update(gpd, gps_active);
1728  }
1729  else {
1730  gpencil_recalc_geometry_tag(gps_active);
1731  }
1732  }
1733  else {
1734  /* Delay a full recalculation for other frames. */
1735  gpencil_recalc_geometry_tag(gps_active);
1736  }
1737  bGPDlayer *gpl_active = (gpl->runtime.gpl_orig) ? gpl->runtime.gpl_orig : gpl;
1738  bGPDframe *gpf_active = (gpf->runtime.gpf_orig) ? gpf->runtime.gpf_orig : gpf;
1739  BKE_gpencil_tag_full_update(gpd, gpl_active, gpf_active, gps_active);
1740  }
1741  }
1742 
1743  return changed;
1744 }
1745 
1746 /* Get list of Auto-Masking strokes. */
1748 {
1749  bGPdata *gpd = gso->gpd;
1750  GP_SpaceConversion *gsc = &gso->gsc;
1751  Brush *brush = gso->brush;
1752  Object *ob = gso->object;
1753  Material *mat_active = BKE_gpencil_material(ob, ob->actcol);
1754  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1755  const bool is_masking_stroke = (brush->gpencil_settings->sculpt_mode_flag &
1757  const bool is_masking_layer = (brush->gpencil_settings->sculpt_mode_flag &
1759  const bool is_masking_material = (brush->gpencil_settings->sculpt_mode_flag &
1761  int mval_i[2];
1762  round_v2i_v2fl(mval_i, gso->mval);
1763 
1764  /* Define a fix number of pixel as cursor radius. */
1765  const int radius = 10;
1766  bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd);
1767 
1768  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
1769  /* Only editable and visible layers are considered. */
1770  if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
1771  continue;
1772  }
1773  /* Calculate bound box matrix. */
1774  float bound_mat[4][4];
1775  BKE_gpencil_layer_transform_matrix_get(gso->depsgraph, gso->object, gpl, bound_mat);
1776 
1777  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
1778  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1779  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
1780  if (gps->totpoints == 0) {
1781  continue;
1782  }
1783  /* Check if the color is editable. */
1784  if (ED_gpencil_stroke_material_editable(gso->object, gpl, gps) == false) {
1785  continue;
1786  }
1787 
1788  /* Layer Auto-Masking. */
1789  if ((is_masking_layer) && (gpl == gpl_active)) {
1790  BLI_ghash_insert(gso->automasking_strokes, gps, gps);
1791  continue;
1792  }
1793  /* Material Auto-Masking. */
1794  if (is_masking_material) {
1795  Material *mat = BKE_object_material_get(ob, gps->mat_nr + 1);
1796  if (mat == mat_active) {
1797  BLI_ghash_insert(gso->automasking_strokes, gps, gps);
1798  continue;
1799  }
1800  }
1801 
1802  /* If Stroke Auto-Masking is not enabled, nothing else to do. */
1803  if (!is_masking_stroke) {
1804  continue;
1805  }
1806 
1807  /* Check if the stroke collide with brush. */
1808  if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
1809  continue;
1810  }
1811 
1812  bGPDspoint *pt1, *pt2;
1813  int pc1[2] = {0};
1814  int pc2[2] = {0};
1815  bGPDspoint npt;
1816 
1817  if (gps->totpoints == 1) {
1818  gpencil_point_to_parent_space(gps->points, bound_mat, &npt);
1819  gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
1820 
1821  /* Only check if point is inside. */
1822  if (len_v2v2_int(mval_i, pc1) <= radius) {
1823  BLI_ghash_insert(gso->automasking_strokes, gps, gps);
1824  }
1825  }
1826  else {
1827  /* Loop over the points in the stroke, checking for intersections
1828  * - an intersection means that we touched the stroke.
1829  */
1830  for (int i = 0; (i + 1) < gps->totpoints; i++) {
1831  /* Get points to work with. */
1832  pt1 = gps->points + i;
1833  pt2 = gps->points + i + 1;
1834 
1835  /* Check first point. */
1836  gpencil_point_to_parent_space(pt1, bound_mat, &npt);
1837  gpencil_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
1838  if (len_v2v2_int(mval_i, pc1) <= radius) {
1839  BLI_ghash_insert(gso->automasking_strokes, gps, gps);
1840  i = gps->totpoints;
1841  continue;
1842  }
1843 
1844  /* Check second point. */
1845  gpencil_point_to_parent_space(pt2, bound_mat, &npt);
1846  gpencil_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
1847  if (len_v2v2_int(mval_i, pc2) <= radius) {
1848  BLI_ghash_insert(gso->automasking_strokes, gps, gps);
1849  i = gps->totpoints;
1850  continue;
1851  }
1852 
1853  /* Check segment. */
1854  if (gpencil_stroke_inside_circle(gso->mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
1855  BLI_ghash_insert(gso->automasking_strokes, gps, gps);
1856  i = gps->totpoints;
1857  continue;
1858  }
1859  }
1860  }
1861  }
1862  /* If not multi-edit, exit loop. */
1863  if (!is_multiedit) {
1864  break;
1865  }
1866  }
1867  }
1868 
1869  return true;
1870 }
1871 
1872 /* Perform two-pass brushes which modify the existing strokes */
1874 {
1875  ToolSettings *ts = gso->scene->toolsettings;
1876  Depsgraph *depsgraph = gso->depsgraph;
1877  Object *obact = gso->object;
1878  bool changed = false;
1879 
1880  Object *ob_eval = (Object *)DEG_get_evaluated_id(depsgraph, &obact->id);
1881  bGPdata *gpd = (bGPdata *)ob_eval->data;
1882 
1883  /* Calculate brush-specific data which applies equally to all points */
1884  char tool = gso->brush->gpencil_sculpt_tool;
1885  switch (tool) {
1886  case GPSCULPT_TOOL_GRAB: /* Grab points */
1887  case GPSCULPT_TOOL_PUSH: /* Push points */
1888  {
1889  /* calculate amount of displacement to apply */
1890  gso->rot_eval = 0.0f;
1892  break;
1893  }
1894 
1895  case GPSCULPT_TOOL_PINCH: /* Pinch points */
1896  case GPSCULPT_TOOL_TWIST: /* Twist points around midpoint */
1897  {
1898  /* calculate midpoint of the brush (in data space) */
1900  break;
1901  }
1902 
1903  case GPSCULPT_TOOL_RANDOMIZE: /* Random jitter */
1904  {
1905  /* compute the displacement vector for the cursor (in data space) */
1906  gso->rot_eval = 0.0f;
1908  break;
1909  }
1910 
1911  default:
1912  break;
1913  }
1914 
1915  /* Find visible strokes, and perform operations on those if hit */
1916  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
1917  /* If no active frame, don't do anything... */
1918  if ((!BKE_gpencil_layer_is_editable(gpl)) || (gpl->actframe == NULL)) {
1919  continue;
1920  }
1921 
1922  /* calculate difference matrix */
1923  float diff_mat[4][4];
1924  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
1925  mul_m4_m4m4(diff_mat, diff_mat, gpl->layer_invmat);
1926 
1927  /* Active Frame or MultiFrame? */
1928  if (gso->is_multiframe) {
1929  /* init multiframe falloff options */
1930  int f_init = 0;
1931  int f_end = 0;
1932 
1933  if (gso->use_multiframe_falloff) {
1934  BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
1935  }
1936 
1937  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
1938  /* Always do active frame; Otherwise, only include selected frames */
1939  if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
1940  /* compute multiframe falloff factor */
1941  if (gso->use_multiframe_falloff) {
1942  /* Falloff depends on distance to active frame
1943  * (relative to the overall frame range). */
1945  gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
1946  }
1947  else {
1948  /* No falloff */
1949  gso->mf_falloff = 1.0f;
1950  }
1951 
1952  /* affect strokes in this frame */
1953  changed |= gpencil_sculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat);
1954  }
1955  }
1956  }
1957  else {
1958  if (gpl->actframe != NULL) {
1959  /* Apply to active frame's strokes */
1960  gso->mf_falloff = 1.0f;
1961  changed |= gpencil_sculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
1962  }
1963  }
1964  }
1965 
1966  return changed;
1967 }
1968 
1969 /* Calculate settings for applying brush */
1971 {
1972  tGP_BrushEditData *gso = op->customdata;
1973  Brush *brush = gso->brush;
1974  const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
1975  gso->brush->size;
1976  float mousef[2];
1977  int mouse[2];
1978  bool changed = false;
1979 
1980  /* Get latest mouse coordinates */
1981  RNA_float_get_array(itemptr, "mouse", mousef);
1982  gso->mval[0] = mouse[0] = (int)(mousef[0]);
1983  gso->mval[1] = mouse[1] = (int)(mousef[1]);
1984 
1985  /* If the mouse/pen has not moved, no reason to continue. This also avoid a small
1986  * drift due precision accumulation errors. */
1987  if ((gso->mval[0] == gso->mval_prev[0]) && (gso->mval[1] == gso->mval_prev[1])) {
1988  return;
1989  }
1990 
1991  gso->pressure = RNA_float_get(itemptr, "pressure");
1992 
1993  if (RNA_boolean_get(itemptr, "pen_flip")) {
1994  gso->flag |= GP_SCULPT_FLAG_INVERT;
1995  }
1996  else {
1997  gso->flag &= ~GP_SCULPT_FLAG_INVERT;
1998  }
1999 
2000  /* Store coordinates as reference, if operator just started running */
2001  if (gso->mval_prev[0] == -1.0f) {
2002  gso->mval_prev[0] = gso->mval[0];
2003  gso->mval_prev[1] = gso->mval[1];
2004  gso->pressure_prev = gso->pressure;
2005  }
2006 
2007  /* Update brush_rect, so that it represents the bounding rectangle of brush */
2008  gso->brush_rect.xmin = mouse[0] - radius;
2009  gso->brush_rect.ymin = mouse[1] - radius;
2010  gso->brush_rect.xmax = mouse[0] + radius;
2011  gso->brush_rect.ymax = mouse[1] + radius;
2012 
2013  /* Get list of Auto-Masking strokes. */
2014  if ((!gso->automasking_ready) &&
2019  }
2020 
2021  /* Apply brush */
2022  char tool = gso->brush->gpencil_sculpt_tool;
2023  if (tool == GPSCULPT_TOOL_CLONE) {
2024  changed = gpencil_sculpt_brush_apply_clone(C, gso);
2025  }
2026  else {
2027  changed = gpencil_sculpt_brush_apply_standard(C, gso);
2028  }
2029 
2030  /* Updates */
2031  if (changed) {
2034  }
2035 
2036  /* Store values for next step */
2037  gso->mval_prev[0] = gso->mval[0];
2038  gso->mval_prev[1] = gso->mval[1];
2039  gso->pressure_prev = gso->pressure;
2040  gso->first = false;
2041 }
2042 
2043 /* Running --------------------------------------------- */
2045 {
2046  Main *bmain = gso->bmain;
2047  Brush *brush = BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2);
2048 
2049  return brush;
2050 }
2051 
2052 /* helper - a record stroke, and apply paint event */
2054 {
2055  tGP_BrushEditData *gso = op->customdata;
2056  PointerRNA itemptr;
2057  float mouse[2];
2058 
2059  mouse[0] = event->mval[0] + 1;
2060  mouse[1] = event->mval[1] + 1;
2061 
2062  /* fill in stroke */
2063  RNA_collection_add(op->ptr, "stroke", &itemptr);
2064 
2065  RNA_float_set_array(&itemptr, "mouse", mouse);
2066  RNA_boolean_set(&itemptr, "pen_flip", (event->modifier & KM_CTRL) != 0);
2067  RNA_boolean_set(&itemptr, "is_start", gso->first);
2068 
2069  /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */
2070  float pressure = event->tablet.pressure;
2071  /* special exception here for too high pressure values on first touch in
2072  * windows for some tablets: clamp the values to be sane */
2073  if (pressure >= 0.99f) {
2074  pressure = 1.0f;
2075  }
2076  RNA_float_set(&itemptr, "pressure", pressure);
2077 
2078  if (event->modifier & KM_SHIFT) {
2079  gso->brush_prev = gso->brush;
2080 
2082  if (gso->brush == NULL) {
2083  gso->brush = gso->brush_prev;
2084  }
2085  Brush *brush = gso->brush;
2086  if (brush->gpencil_settings->sculpt_mode_flag &
2089  if (gso->automasking_strokes == NULL) {
2090  gso->automasking_strokes = BLI_ghash_ptr_new(__func__);
2091  }
2092  }
2093  else {
2094  if (gso->automasking_strokes != NULL) {
2096  }
2097  gso->automasking_strokes = NULL;
2098  }
2099  }
2100  else {
2101  if (gso->brush_prev != NULL) {
2102  gso->brush = gso->brush_prev;
2103  }
2104  }
2105 
2106  /* apply */
2107  gpencil_sculpt_brush_apply(C, op, &itemptr);
2108 }
2109 
2110 /* reapply */
2112 {
2113  if (!gpencil_sculpt_brush_init(C, op)) {
2114  return OPERATOR_CANCELLED;
2115  }
2116 
2117  RNA_BEGIN (op->ptr, itemptr, "stroke") {
2118  gpencil_sculpt_brush_apply(C, op, &itemptr);
2119  }
2120  RNA_END;
2121 
2123 
2124  return OPERATOR_FINISHED;
2125 }
2126 
2127 /* start modal painting */
2129 {
2130  tGP_BrushEditData *gso = NULL;
2131  const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
2132  const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL;
2133  bool needs_timer = false;
2134  float brush_rate = 0.0f;
2135 
2136  /* the operator cannot work while play animation */
2137  if (is_playing) {
2138  BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while animation is playing");
2139 
2140  return OPERATOR_CANCELLED;
2141  }
2142 
2143  /* init painting data */
2144  if (!gpencil_sculpt_brush_init(C, op)) {
2145  return OPERATOR_CANCELLED;
2146  }
2147 
2148  gso = op->customdata;
2149 
2150  /* Initialize type-specific data (used for the entire session). */
2151  char tool = gso->brush->gpencil_sculpt_tool;
2152  switch (tool) {
2153  /* Brushes requiring timer... */
2155  brush_rate = 0.01f;
2156  needs_timer = true;
2157  break;
2158 
2160  brush_rate = 0.01f;
2161  needs_timer = true;
2162  break;
2163 
2164  case GPSCULPT_TOOL_PINCH:
2165  brush_rate = 0.001f;
2166  needs_timer = true;
2167  break;
2168 
2169  case GPSCULPT_TOOL_TWIST:
2170  brush_rate = 0.01f;
2171  needs_timer = true;
2172  break;
2173 
2174  default:
2175  break;
2176  }
2177 
2178  /* register timer for increasing influence by hovering over an area */
2179  if (needs_timer) {
2180  gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate);
2181  }
2182 
2183  /* register modal handler */
2185 
2186  /* start drawing immediately? */
2187  if (is_modal == false) {
2188  ARegion *region = CTX_wm_region(C);
2189 
2190  /* ensure that we'll have a new frame to draw on */
2192 
2193  /* apply first dab... */
2194  gso->is_painting = true;
2195  gpencil_sculpt_brush_apply_event(C, op, event);
2196 
2197  /* redraw view with feedback */
2198  ED_region_tag_redraw(region);
2199  }
2200 
2201  return OPERATOR_RUNNING_MODAL;
2202 }
2203 
2204 /* painting - handle events */
2205 static int gpencil_sculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
2206 {
2207  tGP_BrushEditData *gso = op->customdata;
2208  const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
2209  bool redraw_region = false;
2210  bool redraw_toolsettings = false;
2211 
2212  /* The operator can be in 2 states: Painting and Idling */
2213  if (gso->is_painting) {
2214  /* Painting. */
2215  switch (event->type) {
2216  /* Mouse Move = Apply somewhere else */
2217  case MOUSEMOVE:
2218  case INBETWEEN_MOUSEMOVE:
2219  /* apply brush effect at new position */
2220  gpencil_sculpt_brush_apply_event(C, op, event);
2221 
2222  /* force redraw, so that the cursor will at least be valid */
2223  redraw_region = true;
2224  break;
2225 
2226  /* Timer Tick - Only if this was our own timer */
2227  case TIMER:
2228  if (event->customdata == gso->timer) {
2229  gso->timerTick = true;
2230  gpencil_sculpt_brush_apply_event(C, op, event);
2231  gso->timerTick = false;
2232  }
2233  break;
2234 
2235  /* Painting mbut release = Stop painting (back to idle) */
2236  case LEFTMOUSE:
2237  // BLI_assert(event->val == KM_RELEASE);
2238  if (is_modal) {
2239  /* go back to idling... */
2240  gso->is_painting = false;
2241  }
2242  else {
2243  /* end sculpt session, since we're not modal */
2244  gso->is_painting = false;
2245 
2247  return OPERATOR_FINISHED;
2248  }
2249  break;
2250 
2251  /* Abort painting if any of the usual things are tried */
2252  case MIDDLEMOUSE:
2253  case RIGHTMOUSE:
2254  case EVT_ESCKEY:
2256  return OPERATOR_FINISHED;
2257  }
2258  }
2259  else {
2260  /* Idling */
2261  BLI_assert(is_modal == true);
2262 
2263  switch (event->type) {
2264  /* Painting mbut press = Start painting (switch to painting state) */
2265  case LEFTMOUSE:
2266  /* do initial "click" apply */
2267  gso->is_painting = true;
2268  gso->first = true;
2269 
2271  gpencil_sculpt_brush_apply_event(C, op, event);
2272  break;
2273 
2274  /* Exit modal operator, based on the "standard" ops */
2275  case RIGHTMOUSE:
2276  case EVT_ESCKEY:
2278  return OPERATOR_FINISHED;
2279 
2280  /* MMB is often used for view manipulations */
2281  case MIDDLEMOUSE:
2282  return OPERATOR_PASS_THROUGH;
2283 
2284  /* Mouse movements should update the brush cursor - Just redraw the active region */
2285  case MOUSEMOVE:
2286  case INBETWEEN_MOUSEMOVE:
2287  redraw_region = true;
2288  break;
2289 
2290  /* Change Frame - Allowed */
2291  case EVT_LEFTARROWKEY:
2292  case EVT_RIGHTARROWKEY:
2293  case EVT_UPARROWKEY:
2294  case EVT_DOWNARROWKEY:
2295  return OPERATOR_PASS_THROUGH;
2296 
2297  /* Camera/View Gizmo's - Allowed */
2298  /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */
2299  case EVT_PAD0:
2300  case EVT_PAD1:
2301  case EVT_PAD2:
2302  case EVT_PAD3:
2303  case EVT_PAD4:
2304  case EVT_PAD5:
2305  case EVT_PAD6:
2306  case EVT_PAD7:
2307  case EVT_PAD8:
2308  case EVT_PAD9:
2309  return OPERATOR_PASS_THROUGH;
2310 
2311  /* Unhandled event */
2312  default:
2313  break;
2314  }
2315  }
2316 
2317  /* Redraw region? */
2318  if (redraw_region) {
2319  ARegion *region = CTX_wm_region(C);
2320  ED_region_tag_redraw(region);
2321  }
2322 
2323  /* Redraw toolsettings (brush settings)? */
2324  if (redraw_toolsettings) {
2327  }
2328 
2329  return OPERATOR_RUNNING_MODAL;
2330 }
2331 
2333 {
2334  /* identifiers */
2335  ot->name = "Stroke Sculpt";
2336  ot->idname = "GPENCIL_OT_sculpt_paint";
2337  ot->description = "Apply tweaks to strokes by painting over the strokes"; /* XXX */
2338 
2339  /* api callbacks */
2345 
2346  /* flags */
2348 
2349  /* properties */
2350  PropertyRNA *prop;
2351  prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
2353 
2354  prop = RNA_def_boolean(
2355  ot->srna,
2356  "wait_for_input",
2357  true,
2358  "Wait for Input",
2359  "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke");
2361 }
typedef float(TangentPoint)[2]
float BKE_brush_curve_strength(const struct Brush *br, float p, float len)
void BKE_curvemapping_init(struct CurveMapping *cumap)
Definition: colortools.c:1235
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct bGPDlayer * CTX_data_active_gpencil_layer(const bContext *C)
Definition: context.c:1450
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1528
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
#define CTX_DATA_COUNT(C, member)
Definition: BKE_context.h:290
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
support for deformation groups and hooks.
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
Definition: gpencil.c:855
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
Definition: gpencil.c:1558
struct bGPDframe * BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:567
float BKE_gpencil_multiframe_falloff_calc(struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff)
Definition: gpencil.c:1917
bool BKE_gpencil_layer_is_editable(const struct bGPDlayer *gpl)
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma)
Definition: gpencil.c:2199
void BKE_gpencil_frame_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe)
Definition: gpencil.c:1900
struct bGPDlayer * BKE_gpencil_layer_named_get(struct bGPdata *gpd, const char *name)
Definition: gpencil.c:1419
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
void BKE_gpencil_layer_transform_matrix_get(const struct Depsgraph *depsgraph, struct Object *obact, struct bGPDlayer *gpl, float diff_mat[4][4])
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:341
@ GP_GETFRAME_USE_PREV
Definition: BKE_gpencil.h:338
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int point_index, float influence, int iterations, bool smooth_caps, bool keep_shape, struct bGPDstroke *r_gps)
bool BKE_gpencil_has_transform_modifiers(struct Object *ob)
void BKE_gpencil_tag_full_update(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps)
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:805
struct Material * BKE_object_material_get(struct Object *ob, short act)
Definition: material.c:687
struct Material * BKE_gpencil_material(struct Object *ob, short act)
Definition: material.c:795
Functions for dealing with objects and deform verts, used by painting and tools.
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
#define BLI_assert(a)
Definition: BLI_assert.h:46
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:822
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:734
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float clamp_f(float value, float min, float max)
#define M_PI_2
Definition: BLI_math_base.h:23
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition: math_geom.c:209
void closest_to_plane_v3(float r_close[3], const float plane[4], const float pt[3])
Definition: math_geom.c:401
void mul_m3_v3(const float M[3][3], float r[3])
Definition: math_matrix.c:926
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
Definition: math_matrix.c:259
void unit_m4(float m[4][4])
Definition: rct.c:1090
void mul_mat3_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:790
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void copy_m4_m4(float m1[4][4], const float m2[4][4])
Definition: math_matrix.c:77
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
void zero_axis_bias_m4(float mat[4][4])
Definition: math_matrix.c:3125
void loc_eul_size_to_mat4(float R[4][4], const float loc[3], const float eul[3], const float size[3])
Definition: math_matrix.c:2547
void axis_angle_normalized_to_mat3(float R[3][3], const float axis[3], float angle)
#define DEG2RADF(_deg)
MINLINE void round_v2i_v2fl(int r[2], const float a[2])
float angle_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:423
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE float normalize_v3(float r[3])
MINLINE float len_v2v2_int(const int v1[2], const int v2[2])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
Random number functions.
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition: rand.cc:58
struct RNG * BLI_rng_new(unsigned int seed)
Definition: rand.cc:39
float BLI_rng_get_float(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: rand.cc:93
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned int uint
Definition: BLI_sys_types.h:67
#define CLAMP_MAX(a, c)
#define POINTER_FROM_INT(i)
#define POINTER_AS_UINT(i)
#define UNUSED(x)
#define ELEM(...)
#define CLAMP_MIN(a, b)
#define TIP_(msgid)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
void DEG_id_tag_update(struct ID *id, int flag)
struct ID * DEG_get_evaluated_id(const struct Depsgraph *depsgraph, struct ID *id)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
eGP_Sculpt_Flag
@ GP_SCULPT_FLAG_TMP_INVERT
@ GP_SCULPT_FLAG_INVERT
@ GP_SCULPT_FLAGMODE_APPLY_UV
@ GP_SCULPT_FLAGMODE_APPLY_POSITION
@ GP_SCULPT_FLAGMODE_AUTOMASK_LAYER
@ GP_SCULPT_FLAGMODE_AUTOMASK_MATERIAL
@ GP_SCULPT_FLAGMODE_APPLY_THICKNESS
@ GP_SCULPT_FLAGMODE_AUTOMASK_STROKE
@ GP_SCULPT_FLAGMODE_APPLY_STRENGTH
@ GP_BRUSH_USE_PRESSURE
@ BRUSH_DIR_IN
@ GPSCULPT_TOOL_GRAB
@ GPSCULPT_TOOL_TWIST
@ GPSCULPT_TOOL_PUSH
@ GPSCULPT_TOOL_STRENGTH
@ GPSCULPT_TOOL_PINCH
@ GPSCULPT_TOOL_SMOOTH
@ GPSCULPT_TOOL_CLONE
@ GPSCULPT_TOOL_THICKNESS
@ GPSCULPT_TOOL_RANDOMIZE
@ GP_STROKE_TAG
@ GP_STROKE_2DSPACE
@ GP_STROKE_3DSPACE
#define GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)
#define GPENCIL_ANY_SCULPT_MASK(flag)
@ GP_FRAME_SELECT
@ GP_SPOINT_SELECT
@ GP_MATERIAL_FILL_SHOW
Object is a sort of wrapper for general info.
@ GP_SCULPT_SETT_FLAG_FRAME_FALLOFF
@ GP_LOCKAXIS_X
@ GP_LOCKAXIS_Y
@ GP_LOCKAXIS_Z
@ GP_LOCKAXIS_CURSOR
eGP_Sculpt_SelectMaskFlag
@ SPACE_VIEW3D
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
#define IS_AUTOKEY_ON(scene)
bScreen * ED_screen_animation_playing(const struct wmWindowManager *wm)
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
void ED_workspace_status_text(struct bContext *C, const char *str)
Definition: area.c:816
@ V3D_PROJ_TEST_NOP
Definition: ED_view3d.h:234
void ED_view3d_win_to_delta(const struct ARegion *region, const float xy_delta[2], float zfac, float r_out[3])
eV3DProjStatus ED_view3d_project_float_global(const struct ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
@ V3D_PROJ_RET_OK
Definition: ED_view3d.h:217
float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3])
float ED_view3d_calc_zfac_ex(const struct RegionView3D *rv3d, const float co[3], bool *r_flip)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
Platform independent time functions.
#define RNA_BEGIN(sptr, itemptr, propname)
Definition: RNA_access.h:543
#define RNA_END
Definition: RNA_access.h:550
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:91
void UI_view2d_region_to_view(const struct View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
#define V2D_IS_CLIPPED
Definition: UI_view2d.h:25
@ OPTYPE_BLOCKING
Definition: WM_types.h:150
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define ND_DATA
Definition: WM_types.h:456
#define NC_SCENE
Definition: WM_types.h:328
#define ND_TOOLSETTINGS
Definition: WM_types.h:397
#define NA_EDITED
Definition: WM_types.h:523
#define NC_GPENCIL
Definition: WM_types.h:349
@ KM_CTRL
Definition: WM_types.h:239
@ KM_SHIFT
Definition: WM_types.h:238
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
Scene scene
const Depsgraph * depsgraph
#define str(s)
ListBase gpencil_strokes_copypastebuf
GHash * gpencil_copybuf_validate_colormap(bContext *C)
void gpencil_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc)
void gpencil_point_3d_to_xy(const GP_SpaceConversion *gsc, short flag, const float pt[3], float xy[2])
void gpencil_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt)
bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
void gpencil_point_to_xy(const GP_SpaceConversion *gsc, const struct bGPDstroke *gps, const struct bGPDspoint *pt, int *r_x, int *r_y)
static int gpencil_sculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
static bool gpencil_sculpt_brush_poll(bContext *C)
static GP_Sculpt_Settings * gpencil_sculpt_get_settings(Scene *scene)
struct tGPSB_Grab_StrokeData tGPSB_Grab_StrokeData
static bool get_automasking_strokes_list(tGP_BrushEditData *gso)
static void gpencil_sculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
static bool gpencil_brush_invert_check(tGP_BrushEditData *gso)
static void gpencil_sculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3])
static Brush * gpencil_sculpt_get_smooth_brush(tGP_BrushEditData *gso)
static bool gpencil_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static bool gpencil_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op)
static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
static void gpencil_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
static bool gpencil_brush_strength_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static void gpencil_brush_clone_adjust(tGP_BrushEditData *gso)
static int gpencil_sculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool gpencil_sculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso)
static bool gpencil_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, float rot_eval, int pt_index, const int radius, const int co[2])
static void gpencil_update_geometry(bGPdata *gpd)
static void gpencil_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps, const float diff_mat[4][4])
static float gpencil_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2])
static bool gpencil_sculpt_brush_do_frame(bContext *C, tGP_BrushEditData *gso, bGPDlayer *gpl, bGPDframe *gpf, const float diff_mat[4][4])
static bool gpencil_sculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
static void gpencil_brush_clone_free(tGP_BrushEditData *gso)
static void gpencil_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
static void gpencil_brush_calc_midpoint(tGP_BrushEditData *gso)
bool(* GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, float rotation, int pt_index, const int radius, const int co[2])
static int gpencil_sculpt_brush_exec(bContext *C, wmOperator *op)
static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static bool gpencil_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static bool gpencil_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static bool gpencil_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, float UNUSED(rot_eval), int pt_index, const int radius, const int co[2])
static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, const float diff_mat[4][4], GP_BrushApplyCb apply)
struct tGPSB_CloneBrushData tGPSB_CloneBrushData
static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso)
struct tGP_BrushEditData tGP_BrushEditData
static void gpencil_brush_grab_stroke_free(void *ptr)
static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op)
static void gpencil_recalc_geometry_tag(bGPDstroke *gps)
static float gpencil_sculpt_rotation_eval_get(tGP_BrushEditData *gso, bGPDstroke *gps_eval, bGPDspoint *pt_eval, int idx_eval)
void GPENCIL_OT_sculpt_paint(wmOperatorType *ot)
bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps)
bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, bGPDstroke *gps, const float mval[2], const int radius, const float diff_mat[4][4])
bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
bGPdata * ED_gpencil_data_get_active(const bContext *C)
#define UINT_MAX
Definition: hash_md5.c:43
CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor)
Definition: invert.h:8
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
static void area(int d1, int d2, int e1, int e2, float weights[2])
T distance(const T &a, const T &b)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:4874
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
Definition: rna_access.c:4980
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
Definition: rna_access.c:5215
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
Definition: rna_access.c:4968
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
Definition: rna_access.c:4992
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
Definition: rna_define.c:4221
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
void * regiondata
float alpha
struct CurveMapping * curve
struct BrushGpencilSettings * gpencil_settings
char gpencil_sculpt_tool
struct CurveMapping * cur_falloff
struct ARegion * region
struct View2D * v2d
Definition: DNA_ID.h:368
char name[66]
Definition: DNA_ID.h:378
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
ListBase brushes
Definition: BKE_main.h:193
float loc[3]
float imat[4][4]
float obmat[4][4]
void * data
struct Brush * brush
Definition: rand.cc:33
float persinv[4][4]
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
GpSculptPaint * gp_sculptpaint
char gpencil_selectmode_sculpt
struct GP_Sculpt_Settings gp_sculpt
float rotation_euler[3]
struct bGPDframe * gpf_orig
struct bGPDframe * next
bGPDframe_Runtime runtime
ListBase strokes
struct bGPDlayer * gpl_orig
bGPDframe * actframe
bGPDlayer_Runtime runtime
struct bGPDspoint * pt_orig
bGPDspoint_Runtime runtime
struct bGPDstroke * gps_orig
struct bGPDstroke * prev
bGPDspoint * points
bGPDstroke_Runtime runtime
struct bGPDstroke * next
ListBase vertex_group_names
ListBase layers
int vertex_group_active_index
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
struct GHash * automasking_strokes
GP_Sculpt_Settings * settings
eGP_Sculpt_SelectMaskFlag mask
GP_SpaceConversion gsc
eGP_Sculpt_Flag flag
uint8_t modifier
Definition: WM_types.h:693
short type
Definition: WM_types.h:678
void * customdata
Definition: WM_types.h:715
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:927
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
struct ReportList * reports
struct PointerRNA * ptr
long int PIL_check_seconds_timer_i(void)
Definition: time.c:74
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_main_add_notifier(unsigned int type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_PAD8
@ EVT_PAD2
@ RIGHTMOUSE
@ TIMER
@ EVT_PAD4
@ EVT_PAD0
@ EVT_PAD9
@ EVT_DOWNARROWKEY
@ EVT_PAD3
@ EVT_RIGHTARROWKEY
@ EVT_PAD6
@ EVT_PAD5
@ MOUSEMOVE
@ EVT_UPARROWKEY
@ LEFTMOUSE
@ EVT_LEFTARROWKEY
@ MIDDLEMOUSE
@ EVT_ESCKEY
@ INBETWEEN_MOUSEMOVE
@ EVT_PAD1
@ EVT_PAD7
PointerRNA * ptr
Definition: wm_files.c:3480
wmOperatorType * ot
Definition: wm_files.c:3479
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1682
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1630