Gizmo: use simple unclipped win_to_ray function
[blender.git] / source / blender / editors / curve / editcurve_paint.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/curve/editcurve_paint.c
22  *  \ingroup edcurve
23  */
24
25 #include "DNA_object_types.h"
26 #include "DNA_scene_types.h"
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_blenlib.h"
31 #include "BLI_math.h"
32 #include "BLI_mempool.h"
33
34 #include "BKE_context.h"
35 #include "BKE_curve.h"
36 #include "BKE_fcurve.h"
37 #include "BKE_main.h"
38 #include "BKE_report.h"
39
40 #include "DEG_depsgraph.h"
41
42 #include "WM_api.h"
43 #include "WM_types.h"
44
45 #include "ED_space_api.h"
46 #include "ED_screen.h"
47 #include "ED_view3d.h"
48 #include "ED_curve.h"
49
50 #include "BIF_gl.h"
51
52 #include "GPU_batch.h"
53 #include "GPU_batch_presets.h"
54 #include "GPU_immediate.h"
55 #include "GPU_immediate_util.h"
56 #include "GPU_matrix.h"
57 #include "GPU_state.h"
58
59 #include "curve_intern.h"
60
61 #include "UI_resources.h"
62
63 #include "RNA_access.h"
64 #include "RNA_define.h"
65
66 #include "RNA_enum_types.h"
67
68 #define USE_SPLINE_FIT
69
70 #ifdef USE_SPLINE_FIT
71 #include "curve_fit_nd.h"
72 #endif
73
74 /* Distance between input samples */
75 #define STROKE_SAMPLE_DIST_MIN_PX 1
76 #define STROKE_SAMPLE_DIST_MAX_PX 3
77
78 /* Distance between start/end points to consider cyclic */
79 #define STROKE_CYCLIC_DIST_PX     8
80
81 /* -------------------------------------------------------------------- */
82
83 /** \name StrokeElem / #RNA_OperatorStrokeElement Conversion Functions
84  * \{ */
85
86 struct StrokeElem {
87         float mval[2];
88         float location_world[3];
89         float location_local[3];
90
91         /* surface normal, may be zero'd */
92         float normal_world[3];
93         float normal_local[3];
94
95         float pressure;
96 };
97
98 struct CurveDrawData {
99         Depsgraph *depsgraph;
100
101         short init_event_type;
102         short curve_type;
103
104         /* projecting 2D into 3D space */
105         struct {
106                 /* use a plane or project to the surface */
107                 bool use_plane;
108                 float    plane[4];
109
110                 /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */
111                 bool use_depth;
112
113                 /* offset projection by this value */
114                 bool use_offset;
115                 float    offset[3];  /* worldspace */
116                 float    surface_offset;
117                 bool     use_surface_offset_absolute;
118         } project;
119
120         /* cursor sampling */
121         struct {
122                 /* use substeps, needed for nicely interpolating depth */
123                 bool use_substeps;
124         } sample;
125
126         struct {
127                 float min, max, range;
128         } radius;
129
130         struct {
131                 float mouse[2];
132                 /* used incase we can't calculate the depth */
133                 float location_world[3];
134
135                 float location_world_valid[3];
136
137                 const struct StrokeElem *selem;
138         } prev;
139
140         ViewContext vc;
141         enum {
142                 CURVE_DRAW_IDLE = 0,
143                 CURVE_DRAW_PAINTING = 1,
144         } state;
145
146         /* StrokeElem */
147         BLI_mempool *stroke_elem_pool;
148
149         void *draw_handle_view;
150 };
151
152 static float stroke_elem_radius_from_pressure(const struct CurveDrawData *cdd, const float pressure)
153 {
154         const Curve *cu = cdd->vc.obedit->data;
155         return ((pressure * cdd->radius.range) + cdd->radius.min) * cu->ext2;
156 }
157
158 static float stroke_elem_radius(const struct CurveDrawData *cdd, const struct StrokeElem *selem)
159 {
160         return stroke_elem_radius_from_pressure(cdd, selem->pressure);
161 }
162
163 static void stroke_elem_pressure_set(const struct CurveDrawData *cdd, struct StrokeElem *selem, float pressure)
164 {
165         if ((cdd->project.surface_offset != 0.0f) &&
166             !cdd->project.use_surface_offset_absolute &&
167             !is_zero_v3(selem->normal_local))
168         {
169                 const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) -
170                                      stroke_elem_radius_from_pressure(cdd, selem->pressure);
171                 madd_v3_v3fl(selem->location_local, selem->normal_local, adjust);
172                 mul_v3_m4v3(selem->location_world, cdd->vc.obedit->obmat, selem->location_local);
173         }
174         selem->pressure = pressure;
175 }
176
177 static void stroke_elem_interp(
178         struct StrokeElem *selem_out,
179         const struct StrokeElem *selem_a,  const struct StrokeElem *selem_b, float t)
180 {
181         interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
182         interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
183         interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
184         selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
185 }
186
187
188 /**
189  * Sets the depth from #StrokeElem.mval
190  */
191 static bool stroke_elem_project(
192         const struct CurveDrawData *cdd,
193         const int mval_i[2], const float mval_fl[2],
194         float surface_offset, const float radius,
195         float r_location_world[3], float r_normal_world[3])
196 {
197         ARegion *ar = cdd->vc.ar;
198         RegionView3D *rv3d = cdd->vc.rv3d;
199
200         bool is_location_world_set = false;
201
202         /* project to 'location_world' */
203         if (cdd->project.use_plane) {
204                 /* get the view vector to 'location' */
205                 float ray_origin[3], ray_direction[3];
206                 ED_view3d_win_to_ray(ar, mval_fl, ray_origin, ray_direction);
207
208                 float lambda;
209                 if (isect_ray_plane_v3(ray_origin, ray_direction, cdd->project.plane, &lambda, true)) {
210                         madd_v3_v3v3fl(r_location_world, ray_origin, ray_direction, lambda);
211                         if (r_normal_world) {
212                                 zero_v3(r_normal_world);
213                         }
214                         is_location_world_set = true;
215                 }
216         }
217         else {
218                 const ViewDepths *depths = rv3d->depths;
219                 if (depths &&
220                     ((unsigned int)mval_i[0] < depths->w) &&
221                     ((unsigned int)mval_i[1] < depths->h))
222                 {
223                         const double depth = (double)ED_view3d_depth_read_cached(&cdd->vc, mval_i);
224                         if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
225                                 if (ED_view3d_depth_unproject(ar, mval_i, depth, r_location_world)) {
226                                         is_location_world_set = true;
227                                         if (r_normal_world) {
228                                                 zero_v3(r_normal_world);
229                                         }
230
231                                         if (surface_offset != 0.0f) {
232                                                 const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius;
233                                                 float normal[3];
234                                                 if (ED_view3d_depth_read_cached_normal(&cdd->vc, mval_i, normal)) {
235                                                         madd_v3_v3fl(r_location_world, normal, offset * surface_offset);
236                                                         if (r_normal_world) {
237                                                                 copy_v3_v3(r_normal_world, normal);
238                                                         }
239                                                 }
240                                         }
241                                 }
242                         }
243                 }
244         }
245
246         if (is_location_world_set) {
247                 if (cdd->project.use_offset) {
248                         add_v3_v3(r_location_world, cdd->project.offset);
249                 }
250         }
251
252         return is_location_world_set;
253 }
254
255 static bool stroke_elem_project_fallback(
256         const struct CurveDrawData *cdd,
257         const int mval_i[2], const float mval_fl[2],
258         const float surface_offset, const float radius,
259         const float location_fallback_depth[3],
260         float r_location_world[3], float r_location_local[3],
261         float r_normal_world[3], float r_normal_local[3])
262 {
263         bool is_depth_found = stroke_elem_project(
264                 cdd, mval_i, mval_fl,
265                 surface_offset, radius,
266                 r_location_world, r_normal_world);
267         if (is_depth_found == false) {
268                 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.ar, location_fallback_depth, mval_fl, r_location_world);
269                 zero_v3(r_normal_local);
270         }
271         mul_v3_m4v3(r_location_local, cdd->vc.obedit->imat, r_location_world);
272
273         if (!is_zero_v3(r_normal_world)) {
274                 copy_v3_v3(r_normal_local, r_normal_world);
275                 mul_transposed_mat3_m4_v3(cdd->vc.obedit->obmat, r_normal_local);
276                 normalize_v3(r_normal_local);
277         }
278         else {
279                 zero_v3(r_normal_local);
280         }
281
282         return is_depth_found;
283 }
284
285 /**
286  * \note #StrokeElem.mval & #StrokeElem.pressure must be set first.
287  */
288 static bool stroke_elem_project_fallback_elem(
289         const struct CurveDrawData *cdd,
290         const float location_fallback_depth[3],
291         struct StrokeElem *selem)
292 {
293         const int mval_i[2] = {UNPACK2(selem->mval)};
294         const float radius = stroke_elem_radius(cdd, selem);
295         return stroke_elem_project_fallback(
296                 cdd, mval_i, selem->mval,
297                 cdd->project.surface_offset, radius,
298                 location_fallback_depth,
299                 selem->location_world, selem->location_local,
300                 selem->normal_world, selem->normal_local);
301 }
302
303 /** \} */
304
305
306 /* -------------------------------------------------------------------- */
307
308 /** \name Operator/Stroke Conversion
309  * \{ */
310
311 static void curve_draw_stroke_to_operator_elem(
312         wmOperator *op, const struct StrokeElem *selem)
313 {
314         PointerRNA itemptr;
315         RNA_collection_add(op->ptr, "stroke", &itemptr);
316
317         RNA_float_set_array(&itemptr, "mouse", selem->mval);
318         RNA_float_set_array(&itemptr, "location", selem->location_world);
319         RNA_float_set(&itemptr, "pressure", selem->pressure);
320 }
321
322 static void curve_draw_stroke_from_operator_elem(
323         wmOperator *op, PointerRNA *itemptr)
324 {
325         struct CurveDrawData *cdd = op->customdata;
326
327         struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
328
329         RNA_float_get_array(itemptr, "mouse", selem->mval);
330         RNA_float_get_array(itemptr, "location", selem->location_world);
331         mul_v3_m4v3(selem->location_local, cdd->vc.obedit->imat, selem->location_world);
332         selem->pressure = RNA_float_get(itemptr, "pressure");
333 }
334
335 static void curve_draw_stroke_to_operator(wmOperator *op)
336 {
337         struct CurveDrawData *cdd = op->customdata;
338
339         BLI_mempool_iter iter;
340         const struct StrokeElem *selem;
341
342         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
343         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
344                 curve_draw_stroke_to_operator_elem(op, selem);
345         }
346 }
347
348 static void curve_draw_stroke_from_operator(wmOperator *op)
349 {
350         RNA_BEGIN (op->ptr, itemptr, "stroke")
351         {
352                 curve_draw_stroke_from_operator_elem(op, &itemptr);
353         }
354         RNA_END;
355 }
356
357 /** \} */
358
359
360 /* -------------------------------------------------------------------- */
361
362 /** \name Operator Callbacks & Helpers
363  * \{ */
364
365 static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
366 {
367         wmOperator *op = arg;
368         struct CurveDrawData *cdd = op->customdata;
369
370         const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
371
372         if (stroke_len == 0) {
373                 return;
374         }
375
376         Object *obedit = cdd->vc.obedit;
377         Curve *cu = obedit->data;
378
379         if (cu->ext2 > 0.0f) {
380                 BLI_mempool_iter iter;
381                 const struct StrokeElem *selem;
382
383                 const float  location_zero[3] = {0};
384                 const float *location_prev = location_zero;
385
386                 float color[3];
387                 UI_GetThemeColor3fv(TH_WIRE, color);
388
389                 GPUBatch *sphere = GPU_batch_preset_sphere(0);
390                 GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR);
391                 GPU_batch_uniform_3fv(sphere, "color", color);
392
393                 /* scale to edit-mode space */
394                 GPU_matrix_push();
395                 GPU_matrix_mul(obedit->obmat);
396
397                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
398                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
399                         GPU_matrix_translate_3f(
400                                 selem->location_local[0] - location_prev[0],
401                                 selem->location_local[1] - location_prev[1],
402                                 selem->location_local[2] - location_prev[2]);
403                         location_prev = selem->location_local;
404
405                         const float radius = stroke_elem_radius(cdd, selem);
406
407                         GPU_matrix_push();
408                         GPU_matrix_scale_1f(radius);
409                         GPU_batch_draw(sphere);
410                         GPU_matrix_pop();
411
412                         location_prev = selem->location_local;
413                 }
414
415                 GPU_matrix_pop();
416         }
417
418         if (stroke_len > 1) {
419                 float (*coord_array)[3] = MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__);
420
421                 {
422                         BLI_mempool_iter iter;
423                         const struct StrokeElem *selem;
424                         int i;
425                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
426                         for (selem = BLI_mempool_iterstep(&iter), i = 0; selem; selem = BLI_mempool_iterstep(&iter), i++) {
427                                 copy_v3_v3(coord_array[i], selem->location_world);
428                         }
429                 }
430
431                 {
432                         GPUVertFormat *format = immVertexFormat();
433                         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
434                         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
435
436                         GPU_depth_test(false);
437                         GPU_blend(true);
438                         GPU_line_smooth(true);
439                         GPU_line_width(3.0f);
440
441                         imm_cpack(0x0);
442                         immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
443                         for (int i = 0; i < stroke_len; i++) {
444                                 immVertex3fv(pos, coord_array[i]);
445                         }
446                         immEnd();
447
448                         GPU_line_width(1.0f);
449
450                         imm_cpack(0xffffffff);
451                         immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
452                         for (int i = 0; i < stroke_len; i++) {
453                                 immVertex3fv(pos, coord_array[i]);
454                         }
455                         immEnd();
456
457                         /* Reset defaults */
458                         GPU_depth_test(true);
459                         GPU_blend(false);
460                         GPU_line_smooth(false);
461
462                         immUnbindProgram();
463                 }
464
465                 MEM_freeN(coord_array);
466         }
467 }
468
469 static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
470 {
471         struct CurveDrawData *cdd = op->customdata;
472         Object *obedit = cdd->vc.obedit;
473
474         invert_m4_m4(obedit->imat, obedit->obmat);
475
476         struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
477
478         ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
479
480         /* handle pressure sensitivity (which is supplied by tablets) */
481         if (event->tablet_data) {
482                 const wmTabletData *wmtab = event->tablet_data;
483                 selem->pressure = wmtab->Pressure;
484         }
485         else {
486                 selem->pressure = 1.0f;
487         }
488
489         bool is_depth_found = stroke_elem_project_fallback_elem(
490                 cdd, cdd->prev.location_world_valid, selem);
491
492         if (is_depth_found) {
493                 /* use the depth if a fallback wasn't used */
494                 copy_v3_v3(cdd->prev.location_world_valid, selem->location_world);
495         }
496         copy_v3_v3(cdd->prev.location_world, selem->location_world);
497
498         float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval);
499         copy_v2_v2(cdd->prev.mouse, selem->mval);
500
501         if (cdd->sample.use_substeps && cdd->prev.selem) {
502                 const struct StrokeElem selem_target = *selem;
503                 struct StrokeElem *selem_new_last = selem;
504                 if (len_sq >= SQUARE(STROKE_SAMPLE_DIST_MAX_PX)) {
505                         int n = (int)ceil(sqrt((double)len_sq)) / STROKE_SAMPLE_DIST_MAX_PX ;
506
507                         for (int i = 1; i < n; i++) {
508                                 struct StrokeElem *selem_new = selem_new_last;
509                                 stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, (float)i / n);
510
511                                 const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
512                                         cdd, cdd->prev.location_world_valid, selem_new);
513                                 if (is_depth_found == false) {
514                                         if (is_depth_found_substep) {
515                                                 copy_v3_v3(cdd->prev.location_world_valid, selem_new->location_world);
516                                         }
517                                 }
518
519                                 selem_new_last = BLI_mempool_calloc(cdd->stroke_elem_pool);
520                         }
521                 }
522                 selem = selem_new_last;
523                 *selem_new_last = selem_target;
524         }
525
526         cdd->prev.selem = selem;
527
528         ED_region_tag_redraw(cdd->vc.ar);
529 }
530
531 static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
532 {
533         struct CurveDrawData *cdd = op->customdata;
534         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
535
536         /* add first point */
537         curve_draw_event_add(op, event);
538
539         if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && cdd->project.use_depth &&
540             (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS))
541         {
542                 RegionView3D *rv3d = cdd->vc.rv3d;
543
544                 cdd->project.use_depth = false;
545                 cdd->project.use_plane = true;
546
547                 float normal[3] = {0.0f};
548                 if (ELEM(cps->surface_plane,
549                          CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW,
550                          CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE))
551                 {
552                         if (ED_view3d_depth_read_cached_normal(&cdd->vc, event->mval, normal)) {
553                                 if (cps->surface_plane == CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW) {
554                                         float cross_a[3], cross_b[3];
555                                         cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
556                                         cross_v3_v3v3(cross_b, normal, cross_a);
557                                         copy_v3_v3(normal, cross_b);
558                                 }
559                         }
560                 }
561
562                 /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
563                 if (is_zero_v3(normal)) {
564                         copy_v3_v3(normal, rv3d->viewinv[2]);
565                 }
566
567                 normalize_v3_v3(cdd->project.plane, normal);
568                 cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid);
569
570                 /* Special case for when we only have offset applied on the first-hit,
571                  * the remaining stroke must be offset too. */
572                 if (cdd->project.surface_offset != 0.0f) {
573                         const float mval_fl[2] = {UNPACK2(event->mval)};
574
575                         float location_no_offset[3];
576
577                         if (stroke_elem_project(
578                                 cdd, event->mval, mval_fl, 0.0f, 0.0f,
579                                 location_no_offset, NULL))
580                         {
581                                 sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
582                                 if (!is_zero_v3(cdd->project.offset)) {
583                                         cdd->project.use_offset = true;
584                                 }
585                         }
586                 }
587                 /* end special case */
588
589         }
590
591         cdd->init_event_type = event->type;
592         cdd->state = CURVE_DRAW_PAINTING;
593 }
594
595 static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
596 {
597         BLI_assert(op->customdata == NULL);
598
599         struct CurveDrawData *cdd = MEM_callocN(sizeof(*cdd), __func__);
600
601         cdd->depsgraph = CTX_data_depsgraph(C);
602
603         if (is_invoke) {
604                 ED_view3d_viewcontext_init(C, &cdd->vc);
605                 if (ELEM(NULL, cdd->vc.ar, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
606                         MEM_freeN(cdd);
607                         BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport");
608                         return false;
609                 }
610         }
611         else {
612                 cdd->vc.bmain = CTX_data_main(C);
613                 cdd->vc.depsgraph = CTX_data_depsgraph(C);
614                 cdd->vc.scene = CTX_data_scene(C);
615                 cdd->vc.view_layer = CTX_data_view_layer(C);
616                 cdd->vc.obedit = CTX_data_edit_object(C);
617         }
618
619         op->customdata = cdd;
620
621         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
622
623         cdd->curve_type = cps->curve_type;
624
625         cdd->radius.min = cps->radius_min;
626         cdd->radius.max = cps->radius_max;
627         cdd->radius.range = cps->radius_max - cps->radius_min;
628         cdd->project.surface_offset = cps->surface_offset;
629         cdd->project.use_surface_offset_absolute = (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS) != 0;
630
631         cdd->stroke_elem_pool = BLI_mempool_create(
632                 sizeof(struct StrokeElem), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
633
634         return true;
635 }
636
637
638 static void curve_draw_exit(wmOperator *op)
639 {
640         struct CurveDrawData *cdd = op->customdata;
641         if (cdd) {
642                 if (cdd->draw_handle_view) {
643                         ED_region_draw_cb_exit(cdd->vc.ar->type, cdd->draw_handle_view);
644                         WM_cursor_modal_restore(cdd->vc.win);
645                 }
646
647                 if (cdd->stroke_elem_pool) {
648                         BLI_mempool_destroy(cdd->stroke_elem_pool);
649                 }
650
651                 MEM_freeN(cdd);
652                 op->customdata = NULL;
653         }
654 }
655
656 /**
657  * Initialize values before calling 'exec' (when running interactively).
658  */
659 static void curve_draw_exec_precalc(wmOperator *op)
660 {
661         struct CurveDrawData *cdd = op->customdata;
662         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
663         PropertyRNA *prop;
664
665         prop = RNA_struct_find_property(op->ptr, "fit_method");
666         if (!RNA_property_is_set(op->ptr, prop)) {
667                 RNA_property_enum_set(op->ptr, prop, cps->fit_method);
668         }
669
670         prop = RNA_struct_find_property(op->ptr, "corner_angle");
671         if (!RNA_property_is_set(op->ptr, prop)) {
672                 const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : (float)M_PI;
673                 RNA_property_float_set(op->ptr, prop, corner_angle);
674         }
675
676         prop = RNA_struct_find_property(op->ptr, "error_threshold");
677         if (!RNA_property_is_set(op->ptr, prop)) {
678
679                 /* error isnt set so we'll have to calculate it from the pixel values */
680                 BLI_mempool_iter iter;
681                 const struct StrokeElem *selem, *selem_prev;
682
683                 float len_3d = 0.0f, len_2d = 0.0f;
684                 float scale_px;  /* pixel to local space scale */
685
686                 int i = 0;
687                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
688                 selem_prev = BLI_mempool_iterstep(&iter);
689                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
690                         len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
691                         len_2d += len_v2v2(selem->mval, selem_prev->mval);
692                         selem_prev = selem;
693                 }
694                 scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ?  (len_3d / len_2d) : 0.0f;
695                 float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px;
696                 RNA_property_float_set(op->ptr, prop, error_threshold);
697         }
698
699         prop = RNA_struct_find_property(op->ptr, "use_cyclic");
700         if (!RNA_property_is_set(op->ptr, prop)) {
701                 bool use_cyclic = false;
702
703                 if (BLI_mempool_len(cdd->stroke_elem_pool) > 2) {
704                         BLI_mempool_iter iter;
705                         const struct StrokeElem *selem, *selem_first, *selem_last;
706
707                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
708                         selem_first = selem_last = BLI_mempool_iterstep(&iter);
709                         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
710                                 selem_last = selem;
711                         }
712
713                         if (len_squared_v2v2(
714                                 selem_first->mval,
715                                 selem_last->mval) <= SQUARE(STROKE_CYCLIC_DIST_PX * U.pixelsize))
716                         {
717                                 use_cyclic = true;
718                         }
719                 }
720
721                 RNA_property_boolean_set(op->ptr, prop, use_cyclic);
722         }
723
724
725         if ((cps->radius_taper_start != 0.0f) ||
726             (cps->radius_taper_end   != 0.0f))
727         {
728                 /* note, we could try to de-duplicate the length calculations above */
729                 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
730
731                 BLI_mempool_iter iter;
732                 struct StrokeElem *selem, *selem_prev;
733
734                 float *lengths = MEM_mallocN(sizeof(float) * stroke_len, __func__);
735                 struct StrokeElem **selem_array = MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__);
736                 lengths[0] = 0.0f;
737
738                 float len_3d = 0.0f;
739
740                 int i = 1;
741                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
742                 selem_prev = BLI_mempool_iterstep(&iter);
743                 selem_array[0] = selem_prev;
744                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
745                         const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
746                         len_3d += len_3d_segment;
747                         lengths[i] = len_3d;
748                         selem_array[i] = selem;
749                         selem_prev = selem;
750                 }
751
752                 if (cps->radius_taper_start != 0.0f) {
753                         const float len_taper_max = cps->radius_taper_start * len_3d;
754                         for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) {
755                                 const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max);
756                                 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
757                         }
758                 }
759
760                 if (cps->radius_taper_end != 0.0f) {
761                         const float len_taper_max = cps->radius_taper_end * len_3d;
762                         const float len_taper_min = len_3d - len_taper_max;
763                         for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) {
764                                 const float pressure_new = selem_array[i]->pressure * ((len_3d - lengths[i]) / len_taper_max);
765                                 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
766                         }
767                 }
768
769                 MEM_freeN(lengths);
770                 MEM_freeN(selem_array);
771         }
772 }
773
774 static int curve_draw_exec(bContext *C, wmOperator *op)
775 {
776         if (op->customdata == NULL) {
777                 if (!curve_draw_init(C, op, false)) {
778                         return OPERATOR_CANCELLED;
779                 }
780         }
781
782         struct CurveDrawData *cdd = op->customdata;
783
784         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
785         Object *obedit = cdd->vc.obedit;
786         Curve *cu = obedit->data;
787         ListBase *nurblist = object_editcurve_get(obedit);
788
789         int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
790
791         const bool is_3d = (cu->flag & CU_3D) != 0;
792         invert_m4_m4(obedit->imat, obedit->obmat);
793
794         if (BLI_mempool_len(cdd->stroke_elem_pool) == 0) {
795                 curve_draw_stroke_from_operator(op);
796                 stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
797         }
798
799         ED_curve_deselect_all(cu->editnurb);
800
801         const float radius_min = cps->radius_min;
802         const float radius_max = cps->radius_max;
803         const float radius_range = cps->radius_max - cps->radius_min;
804
805         Nurb *nu = MEM_callocN(sizeof(Nurb), __func__);
806         nu->pntsv = 0;
807         nu->resolu = cu->resolu;
808         nu->resolv = cu->resolv;
809         nu->flag |= CU_SMOOTH;
810
811         const bool use_pressure_radius =
812                 (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
813                 ((cps->radius_taper_start != 0.0f) ||
814                  (cps->radius_taper_end   != 0.0f));
815
816         if (cdd->curve_type == CU_BEZIER) {
817                 nu->type = CU_BEZIER;
818
819 #ifdef USE_SPLINE_FIT
820
821                 /* Allow to interpolate multiple channels */
822                 int dims = 3;
823                 struct {
824                         int radius;
825                 } coords_indices;
826                 coords_indices.radius = use_pressure_radius ? dims++ : -1;
827
828                 float *coords = MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__);
829
830                 float       *cubic_spline = NULL;
831                 unsigned int cubic_spline_len = 0;
832
833                 /* error in object local space */
834                 const int fit_method = RNA_enum_get(op->ptr, "fit_method");
835                 const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
836                 const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
837                 const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
838
839                 {
840                         BLI_mempool_iter iter;
841                         const struct StrokeElem *selem;
842                         float *co = coords;
843
844                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
845                         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), co += dims) {
846                                 copy_v3_v3(co, selem->location_local);
847                                 if (coords_indices.radius != -1) {
848                                         co[coords_indices.radius] = selem->pressure;
849                                 }
850
851                                 /* remove doubles */
852                                 if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) {
853                                         co -= dims;
854                                         stroke_len--;
855                                 }
856                         }
857                 }
858
859                 unsigned int *corners = NULL;
860                 unsigned int  corners_len = 0;
861
862                 if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < (float)M_PI)) {
863                         /* this could be configurable... */
864                         const float corner_radius_min = error_threshold / 8;
865                         const float corner_radius_max = error_threshold * 2;
866                         const unsigned int samples_max = 16;
867
868                         curve_fit_corners_detect_fl(
869                                 coords, stroke_len, dims,
870                                 corner_radius_min, corner_radius_max,
871                                 samples_max, corner_angle,
872                                 &corners, &corners_len);
873                 }
874
875                 unsigned int *corners_index = NULL;
876                 unsigned int  corners_index_len = 0;
877                 unsigned int  calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
878
879                 if ((stroke_len > 2) && use_cyclic) {
880                         calc_flag |= CURVE_FIT_CALC_CYCLIC;
881                 }
882
883                 int result;
884                 if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
885                         result = curve_fit_cubic_to_points_refit_fl(
886                                 coords, stroke_len, dims, error_threshold, calc_flag,
887                                 NULL, 0, corner_angle,
888                                 &cubic_spline, &cubic_spline_len,
889                                 NULL,
890                                 &corners_index, &corners_index_len);
891                 }
892                 else {
893                         result = curve_fit_cubic_to_points_fl(
894                                 coords, stroke_len, dims, error_threshold, calc_flag,
895                                 corners, corners_len,
896                                 &cubic_spline, &cubic_spline_len,
897                                 NULL,
898                                 &corners_index, &corners_index_len);
899                 }
900
901                 MEM_freeN(coords);
902                 if (corners) {
903                         free(corners);
904                 }
905
906                 if (result == 0) {
907                         nu->pntsu = cubic_spline_len;
908                         nu->bezt = MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__);
909
910                         float *co = cubic_spline;
911                         BezTriple *bezt = nu->bezt;
912                         for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
913                                 const float *handle_l = co + (dims * 0);
914                                 const float *pt       = co + (dims * 1);
915                                 const float *handle_r = co + (dims * 2);
916
917                                 copy_v3_v3(bezt->vec[0], handle_l);
918                                 copy_v3_v3(bezt->vec[1], pt);
919                                 copy_v3_v3(bezt->vec[2], handle_r);
920
921                                 if (coords_indices.radius != -1) {
922                                         bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
923                                 }
924                                 else {
925                                         bezt->radius = radius_max;
926                                 }
927
928                                 bezt->h1 = bezt->h2 = HD_ALIGN;  /* will set to free in second pass */
929                                 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
930                         }
931
932                         if (corners_index) {
933                                 /* ignore the first and last */
934                                 unsigned int i_start = 0, i_end = corners_index_len;
935
936                                 if ((corners_index_len >= 2) &&
937                                     (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0)
938                                 {
939                                         i_start += 1;
940                                         i_end   -= 1;
941                                 }
942
943                                 for (unsigned int i = i_start; i < i_end; i++) {
944                                         bezt = &nu->bezt[corners_index[i]];
945                                         bezt->h1 = bezt->h2 = HD_FREE;
946                                 }
947                         }
948
949                         if (calc_flag & CURVE_FIT_CALC_CYCLIC) {
950                                 nu->flagu |= CU_NURB_CYCLIC;
951                         }
952                 }
953
954                 if (corners_index) {
955                         free(corners_index);
956                 }
957
958                 if (cubic_spline) {
959                         free(cubic_spline);
960                 }
961
962 #else
963                 nu->pntsu = stroke_len;
964                 nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
965
966                 BezTriple *bezt = nu->bezt;
967
968                 {
969                         BLI_mempool_iter iter;
970                         const struct StrokeElem *selem;
971
972                         BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
973                         for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
974                                 copy_v3_v3(bezt->vec[1], selem->location_local);
975                                 if (!is_3d) {
976                                         bezt->vec[1][2] = 0.0f;
977                                 }
978
979                                 if (use_pressure_radius) {
980                                         bezt->radius = selem->pressure;
981                                 }
982                                 else {
983                                         bezt->radius = radius_max;
984                                 }
985
986                                 bezt->h1 = bezt->h2 = HD_AUTO;
987
988                                 bezt->f1 |= SELECT;
989                                 bezt->f2 |= SELECT;
990                                 bezt->f3 |= SELECT;
991
992                                 bezt++;
993                         }
994                 }
995 #endif
996
997                 BKE_nurb_handles_calc(nu);
998         }
999         else {  /* CU_POLY */
1000                 BLI_mempool_iter iter;
1001                 const struct StrokeElem *selem;
1002
1003                 nu->pntsu = stroke_len;
1004                 nu->pntsv = 1;
1005                 nu->type = CU_POLY;
1006                 nu->bp = MEM_callocN(nu->pntsu * sizeof(BPoint), __func__);
1007
1008                 /* Misc settings. */
1009                 nu->resolu = cu->resolu;
1010                 nu->resolv = 1;
1011                 nu->orderu = 4;
1012                 nu->orderv = 1;
1013
1014                 BPoint *bp = nu->bp;
1015
1016                 BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
1017                 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
1018                         copy_v3_v3(bp->vec, selem->location_local);
1019                         if (!is_3d) {
1020                                 bp->vec[2] = 0.0f;
1021                         }
1022
1023                         if (use_pressure_radius) {
1024                                 bp->radius = (selem->pressure * radius_range) + radius_min;
1025                         }
1026                         else {
1027                                 bp->radius = cps->radius_max;
1028                         }
1029                         bp->f1 = SELECT;
1030                         bp->vec[3] = 1.0f;
1031
1032                         bp++;
1033                 }
1034
1035                 BKE_nurb_knot_calc_u(nu);
1036         }
1037
1038         BLI_addtail(nurblist, nu);
1039
1040         BKE_curve_nurb_active_set(cu, nu);
1041         cu->actvert = nu->pntsu - 1;
1042
1043         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1044         DEG_id_tag_update(obedit->data, 0);
1045
1046         curve_draw_exit(op);
1047
1048         return OPERATOR_FINISHED;
1049 }
1050
1051 static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1052 {
1053         if (RNA_struct_property_is_set(op->ptr, "stroke")) {
1054                 return curve_draw_exec(C, op);
1055         }
1056
1057         if (!curve_draw_init(C, op, true)) {
1058                 return OPERATOR_CANCELLED;
1059         }
1060
1061         struct CurveDrawData *cdd = op->customdata;
1062
1063         const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
1064
1065         const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1066
1067         /* fallback (incase we can't find the depth on first test) */
1068         {
1069                 const float mval_fl[2] = {UNPACK2(event->mval)};
1070                 float center[3];
1071                 negate_v3_v3(center, cdd->vc.rv3d->ofs);
1072                 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.ar, center, mval_fl, cdd->prev.location_world);
1073                 copy_v3_v3(cdd->prev.location_world_valid, cdd->prev.location_world);
1074         }
1075
1076         cdd->draw_handle_view = ED_region_draw_cb_activate(
1077                 cdd->vc.ar->type, curve_draw_stroke_3d, op, REGION_DRAW_POST_VIEW);
1078         WM_cursor_modal_set(cdd->vc.win, BC_PAINTBRUSHCURSOR);
1079
1080         {
1081                 View3D *v3d = cdd->vc.v3d;
1082                 RegionView3D *rv3d = cdd->vc.rv3d;
1083                 Object *obedit = cdd->vc.obedit;
1084                 Curve *cu = obedit->data;
1085
1086                 const float *plane_no = NULL;
1087                 const float *plane_co = NULL;
1088
1089                 if ((cu->flag & CU_3D) == 0) {
1090                         /* 2D overrides other options */
1091                         plane_co = obedit->obmat[3];
1092                         plane_no = obedit->obmat[2];
1093                         cdd->project.use_plane = true;
1094                 }
1095                 else {
1096                         if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) &&
1097                             (v3d->shading.type > OB_WIRE))
1098                         {
1099                                 /* needed or else the draw matrix can be incorrect */
1100                                 view3d_operator_needs_opengl(C);
1101
1102                                 ED_view3d_autodist_init(cdd->vc.depsgraph, cdd->vc.ar, cdd->vc.v3d, 0);
1103
1104                                 if (cdd->vc.rv3d->depths) {
1105                                         cdd->vc.rv3d->depths->damaged = true;
1106                                 }
1107
1108                                 ED_view3d_depth_update(cdd->vc.ar);
1109
1110                                 if (cdd->vc.rv3d->depths != NULL) {
1111                                         cdd->project.use_depth = true;
1112                                 }
1113                                 else {
1114                                         BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
1115                                         cdd->project.use_depth = false;
1116                                 }
1117                         }
1118
1119                         /* use view plane (when set or as fallback when surface can't be found) */
1120                         if (cdd->project.use_depth == false) {
1121                                 plane_co = ED_view3d_cursor3d_get(cdd->vc.scene, v3d)->location;
1122                                 plane_no = rv3d->viewinv[2];
1123                                 cdd->project.use_plane = true;
1124                         }
1125
1126                         if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
1127                                 cdd->sample.use_substeps = true;
1128                         }
1129                 }
1130
1131                 if (cdd->project.use_plane) {
1132                         normalize_v3_v3(cdd->project.plane, plane_no);
1133                         cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
1134                 }
1135         }
1136
1137         if (is_modal == false) {
1138                 curve_draw_event_add_first(op, event);
1139         }
1140
1141         /* add temp handler */
1142         WM_event_add_modal_handler(C, op);
1143
1144         return OPERATOR_RUNNING_MODAL;
1145 }
1146
1147 static void curve_draw_cancel(bContext *UNUSED(C), wmOperator *op)
1148 {
1149         curve_draw_exit(op);
1150 }
1151
1152
1153 /* Modal event handling of frame changing */
1154 static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
1155 {
1156         int ret = OPERATOR_RUNNING_MODAL;
1157         struct CurveDrawData *cdd = op->customdata;
1158
1159         UNUSED_VARS(C, op);
1160
1161         if (event->type == cdd->init_event_type) {
1162                 if (event->val == KM_RELEASE) {
1163                         ED_region_tag_redraw(cdd->vc.ar);
1164
1165                         curve_draw_exec_precalc(op);
1166
1167                         curve_draw_stroke_to_operator(op);
1168
1169                         curve_draw_exec(C, op);
1170
1171                         return OPERATOR_FINISHED;
1172                 }
1173         }
1174         else if (ELEM(event->type, ESCKEY, RIGHTMOUSE)) {
1175                 ED_region_tag_redraw(cdd->vc.ar);
1176                 curve_draw_cancel(C, op);
1177                 return OPERATOR_CANCELLED;
1178         }
1179         else if (ELEM(event->type, LEFTMOUSE)) {
1180                 if (event->val == KM_PRESS) {
1181                         curve_draw_event_add_first(op, event);
1182                 }
1183         }
1184         else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
1185                 if (cdd->state == CURVE_DRAW_PAINTING) {
1186                         const float mval_fl[2] = {UNPACK2(event->mval)};
1187                         if (len_squared_v2v2(mval_fl, cdd->prev.mouse) > SQUARE(STROKE_SAMPLE_DIST_MIN_PX)) {
1188                                 curve_draw_event_add(op, event);
1189                         }
1190                 }
1191         }
1192
1193         return ret;
1194 }
1195
1196 void CURVE_OT_draw(wmOperatorType *ot)
1197 {
1198         /* identifiers */
1199         ot->name = "Draw Curve";
1200         ot->idname = "CURVE_OT_draw";
1201         ot->description = "Draw a freehand spline";
1202
1203         /* api callbacks */
1204         ot->exec = curve_draw_exec;
1205         ot->invoke = curve_draw_invoke;
1206         ot->cancel = curve_draw_cancel;
1207         ot->modal = curve_draw_modal;
1208         ot->poll = ED_operator_editcurve;
1209
1210         /* flags */
1211         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1212
1213         /* properties */
1214         PropertyRNA *prop;
1215
1216         prop = RNA_def_float_distance(
1217                 ot->srna, "error_threshold", 0.0f, 0.0f, 10.0f, "Error",
1218                 "Error distance threshold (in object units)",
1219                 0.0001f, 10.0f);
1220         RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
1221
1222         RNA_def_enum(ot->srna, "fit_method", rna_enum_curve_fit_method_items, CURVE_PAINT_FIT_METHOD_REFIT,
1223                      "Fit Method", "");
1224
1225         prop = RNA_def_float_distance(
1226                 ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
1227         RNA_def_property_subtype(prop, PROP_ANGLE);
1228
1229         prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
1230         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1231
1232         prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1233         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
1234
1235         prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1236         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
1237 }
1238
1239 /** \} */