Blender V4.3
editcurve_paint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "DNA_object_types.h"
10#include "DNA_scene_types.h"
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_math_matrix.h"
15#include "BLI_math_rotation.h"
16#include "BLI_mempool.h"
17
18#include "BKE_context.hh"
19#include "BKE_curve.hh"
20#include "BKE_fcurve.hh"
21#include "BKE_object_types.hh"
22#include "BKE_report.hh"
23
24#include "DEG_depsgraph.hh"
25
26#include "WM_api.hh"
27#include "WM_types.hh"
28
29#include "ED_curve.hh"
30#include "ED_screen.hh"
31#include "ED_space_api.hh"
32#include "ED_view3d.hh"
33
34#include "GPU_batch.hh"
35#include "GPU_batch_presets.hh"
36#include "GPU_immediate.hh"
37#include "GPU_immediate_util.hh"
38#include "GPU_matrix.hh"
39#include "GPU_state.hh"
40
41#include "curve_intern.hh"
42
43#include "UI_resources.hh"
44
45#include "RNA_access.hh"
46#include "RNA_define.hh"
47#include "RNA_prototypes.hh"
48
49#include "RNA_enum_types.hh"
50
51#define USE_SPLINE_FIT
52
53#ifdef USE_SPLINE_FIT
54extern "C" {
55# include "curve_fit_nd.h"
56}
57#endif
58
59/* Distance between input samples */
60#define STROKE_SAMPLE_DIST_MIN_PX 1
61#define STROKE_SAMPLE_DIST_MAX_PX 3
62
63/* Distance between start/end points to consider cyclic */
64#define STROKE_CYCLIC_DIST_PX 8
65
66/* -------------------------------------------------------------------- */
70struct StrokeElem {
71 float mval[2];
74
75 /* surface normal, may be zero'd */
76 float normal_world[3];
77 float normal_local[3];
78
79 float pressure;
80};
81
86
90
91 /* projecting 2D into 3D space */
92 struct {
93 /* use a plane or project to the surface */
95 float plane[4];
96
97 /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but that's OK. */
99
100 /* offset projection by this value */
102 float offset[3]; /* world-space */
106
107 /* cursor sampling */
108 struct {
109 /* use substeps, needed for nicely interpolating depth */
112
113 struct {
114 float min, max, range;
116
117 struct {
118 float mval[2];
119 /* Used in case we can't calculate the depth. */
121
123
126
130
131 /* StrokeElem */
133
135};
136
137static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
138{
139 const Curve *cu = static_cast<const Curve *>(cdd->vc.obedit->data);
140 return ((pressure * cdd->radius.range) + cdd->radius.min) * cu->bevel_radius;
141}
142
143static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
144{
145 return stroke_elem_radius_from_pressure(cdd, selem->pressure);
146}
147
148static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
149{
150 if ((cdd->project.surface_offset != 0.0f) && !cdd->project.use_surface_offset_absolute &&
151 !is_zero_v3(selem->normal_local))
152 {
153 const float adjust = stroke_elem_radius_from_pressure(cdd, pressure) -
155 madd_v3_v3fl(selem->location_local, selem->normal_local, adjust);
157 selem->location_world, cdd->vc.obedit->object_to_world().ptr(), selem->location_local);
158 }
159 selem->pressure = pressure;
160}
161
162static void stroke_elem_interp(StrokeElem *selem_out,
163 const StrokeElem *selem_a,
164 const StrokeElem *selem_b,
165 float t)
166{
167 interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
168 interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
169 interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
170 selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
171}
172
176static bool stroke_elem_project(const CurveDrawData *cdd,
177 const int mval_i[2],
178 const float mval_fl[2],
179 float surface_offset,
180 const float radius,
181 float r_location_world[3],
182 float r_normal_world[3])
183{
184 ARegion *region = cdd->vc.region;
185
186 bool is_location_world_set = false;
187
188 /* project to 'location_world' */
189 if (cdd->project.use_plane) {
190 /* get the view vector to 'location' */
191 if (ED_view3d_win_to_3d_on_plane(region, cdd->project.plane, mval_fl, true, r_location_world))
192 {
193 if (r_normal_world) {
194 zero_v3(r_normal_world);
195 }
196 is_location_world_set = true;
197 }
198 }
199 else {
200 const ViewDepths *depths = cdd->depths;
201 if (depths && (uint(mval_i[0]) < depths->w) && (uint(mval_i[1]) < depths->h)) {
202 float depth_fl = 1.0f;
203 ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl);
204 const double depth = double(depth_fl);
205 if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
206 if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) {
207 is_location_world_set = true;
208 if (r_normal_world) {
209 zero_v3(r_normal_world);
210 }
211
212 if (surface_offset != 0.0f) {
213 const float offset = cdd->project.use_surface_offset_absolute ? 1.0f : radius;
214 float normal[3];
215 if (ED_view3d_depth_read_cached_normal(region, depths, mval_i, normal)) {
216 madd_v3_v3fl(r_location_world, normal, offset * surface_offset);
217 if (r_normal_world) {
218 copy_v3_v3(r_normal_world, normal);
219 }
220 }
221 }
222 }
223 }
224 }
225 }
226
227 if (is_location_world_set) {
228 if (cdd->project.use_offset) {
229 add_v3_v3(r_location_world, cdd->project.offset);
230 }
231 }
232
233 return is_location_world_set;
234}
235
237 const int mval_i[2],
238 const float mval_fl[2],
239 const float surface_offset,
240 const float radius,
241 const float location_fallback_depth[3],
242 float r_location_world[3],
243 float r_location_local[3],
244 float r_normal_world[3],
245 float r_normal_local[3])
246{
247 bool is_depth_found = stroke_elem_project(
248 cdd, mval_i, mval_fl, surface_offset, radius, r_location_world, r_normal_world);
249 if (is_depth_found == false) {
251 cdd->vc.v3d, cdd->vc.region, location_fallback_depth, mval_fl, r_location_world);
252 zero_v3(r_normal_local);
253 }
254 mul_v3_m4v3(r_location_local, cdd->vc.obedit->world_to_object().ptr(), r_location_world);
255
256 if (!is_zero_v3(r_normal_world)) {
257 copy_v3_v3(r_normal_local, r_normal_world);
258 mul_transposed_mat3_m4_v3(cdd->vc.obedit->object_to_world().ptr(), r_normal_local);
259 normalize_v3(r_normal_local);
260 }
261 else {
262 zero_v3(r_normal_local);
263 }
264
265 return is_depth_found;
266}
267
272 const float location_fallback_depth[3],
273 StrokeElem *selem)
274{
275 const int mval_i[2] = {int(selem->mval[0]), int(selem->mval[1])};
276 const float radius = stroke_elem_radius(cdd, selem);
278 mval_i,
279 selem->mval,
281 radius,
282 location_fallback_depth,
283 selem->location_world,
284 selem->location_local,
285 selem->normal_world,
286 selem->normal_local);
287}
288
291/* -------------------------------------------------------------------- */
296{
297 PointerRNA itemptr;
298 RNA_collection_add(op->ptr, "stroke", &itemptr);
299
300 RNA_float_set_array(&itemptr, "mouse", selem->mval);
301 RNA_float_set_array(&itemptr, "location", selem->location_world);
302 RNA_float_set(&itemptr, "pressure", selem->pressure);
303}
304
306{
307 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
308
309 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
310
311 RNA_float_get_array(itemptr, "mouse", selem->mval);
312 RNA_float_get_array(itemptr, "location", selem->location_world);
314 selem->location_local, cdd->vc.obedit->world_to_object().ptr(), selem->location_world);
315 selem->pressure = RNA_float_get(itemptr, "pressure");
316}
317
319{
320 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
321
322 BLI_mempool_iter iter;
323 const StrokeElem *selem;
324
326 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
327 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
328 {
330 }
331}
332
334{
335 RNA_BEGIN (op->ptr, itemptr, "stroke") {
337 }
338 RNA_END;
339}
340
343/* -------------------------------------------------------------------- */
347static void curve_draw_stroke_3d(const bContext * /*C*/, ARegion * /*region*/, void *arg)
348{
349 wmOperator *op = static_cast<wmOperator *>(arg);
350 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
351
352 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
353
354 if (stroke_len == 0) {
355 return;
356 }
357
358 Object *obedit = cdd->vc.obedit;
359 Curve *cu = static_cast<Curve *>(obedit->data);
360
361 if (cu->bevel_radius > 0.0f) {
362 BLI_mempool_iter iter;
363 const StrokeElem *selem;
364
365 const float location_zero[3] = {0};
366 const float *location_prev = location_zero;
367
368 float color[3];
370
371 blender::gpu::Batch *sphere = GPU_batch_preset_sphere(0);
373 GPU_batch_uniform_3fv(sphere, "color", color);
374
375 /* scale to edit-mode space */
377 GPU_matrix_mul(obedit->object_to_world().ptr());
378
380 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
381 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
382 {
383 GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0],
384 selem->location_local[1] - location_prev[1],
385 selem->location_local[2] - location_prev[2]);
386
387 const float radius = stroke_elem_radius(cdd, selem);
388
390 GPU_matrix_scale_1f(radius);
391 GPU_batch_draw(sphere);
393
394 location_prev = selem->location_local;
395 }
396
398 }
399
400 if (stroke_len > 1) {
401 float(*coord_array)[3] = static_cast<float(*)[3]>(
402 MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__));
403
404 {
405 BLI_mempool_iter iter;
406 const StrokeElem *selem;
407 int i;
409 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i = 0; selem;
410 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
411 {
412 copy_v3_v3(coord_array[i], selem->location_world);
413 }
414 }
415
416 {
420
423 GPU_line_smooth(true);
424 GPU_line_width(3.0f);
425
426 imm_cpack(0x0);
427 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
428 for (int i = 0; i < stroke_len; i++) {
429 immVertex3fv(pos, coord_array[i]);
430 }
431 immEnd();
432
433 GPU_line_width(1.0f);
434
435 imm_cpack(0xffffffff);
436 immBegin(GPU_PRIM_LINE_STRIP, stroke_len);
437 for (int i = 0; i < stroke_len; i++) {
438 immVertex3fv(pos, coord_array[i]);
439 }
440 immEnd();
441
442 /* Reset defaults */
445 GPU_line_smooth(false);
446
448 }
449
450 MEM_freeN(coord_array);
451 }
452}
453
454static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
455{
456 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
457 Object *obedit = cdd->vc.obedit;
458
459 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
460
461 StrokeElem *selem = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
462
463 ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
464
465 /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */
466 selem->pressure = event->tablet.pressure;
467
468 bool is_depth_found = stroke_elem_project_fallback_elem(
469 cdd, cdd->prev.location_world_valid, selem);
470
471 if (is_depth_found) {
472 /* use the depth if a fallback wasn't used */
474 }
476
477 float len_sq = len_squared_v2v2(cdd->prev.mval, selem->mval);
478 copy_v2_v2(cdd->prev.mval, selem->mval);
479
480 if (cdd->sample.use_substeps && cdd->prev.selem) {
481 const StrokeElem selem_target = *selem;
482 StrokeElem *selem_new_last = selem;
483 if (len_sq >= square_f(STROKE_SAMPLE_DIST_MAX_PX)) {
484 int n = int(ceil(sqrt(double(len_sq)))) / STROKE_SAMPLE_DIST_MAX_PX;
485
486 for (int i = 1; i < n; i++) {
487 StrokeElem *selem_new = selem_new_last;
488 stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, float(i) / n);
489
490 const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
491 cdd, cdd->prev.location_world_valid, selem_new);
492 if (is_depth_found == false) {
493 if (is_depth_found_substep) {
495 }
496 }
497
498 selem_new_last = static_cast<StrokeElem *>(BLI_mempool_calloc(cdd->stroke_elem_pool));
499 }
500 }
501 selem = selem_new_last;
502 *selem_new_last = selem_target;
503 }
504
505 cdd->prev.selem = selem;
506
508}
509
510static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
511{
512 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
514
515 /* add first point */
516 curve_draw_event_add(op, event);
517
520 {
521 RegionView3D *rv3d = cdd->vc.rv3d;
522
523 cdd->project.use_depth = false;
524 cdd->project.use_plane = true;
525
526 float normal[3] = {0.0f};
527 if (ELEM(cps->surface_plane,
530 {
531 if (ED_view3d_depth_read_cached_normal(cdd->vc.region, cdd->depths, event->mval, normal)) {
533 float cross_a[3], cross_b[3];
534 cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
535 cross_v3_v3v3(cross_b, normal, cross_a);
536 copy_v3_v3(normal, cross_b);
537 }
538 }
539 }
540
541 /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
542 if (is_zero_v3(normal)) {
543 copy_v3_v3(normal, rv3d->viewinv[2]);
544 }
545
546 normalize_v3_v3(cdd->project.plane, normal);
548
549 /* Special case for when we only have offset applied on the first-hit,
550 * the remaining stroke must be offset too. */
551 if (cdd->project.surface_offset != 0.0f) {
552 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
553
554 float location_no_offset[3];
555
556 if (stroke_elem_project(cdd, event->mval, mval_fl, 0.0f, 0.0f, location_no_offset, nullptr))
557 {
558 sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
559 if (!is_zero_v3(cdd->project.offset)) {
560 cdd->project.use_offset = true;
561 }
562 }
563 }
564 /* end special case */
565 }
566
567 cdd->init_event_type = event->type;
569}
570
571static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
572{
573 BLI_assert(op->customdata == nullptr);
574
575 CurveDrawData *cdd = static_cast<CurveDrawData *>(MEM_callocN(sizeof(*cdd), __func__));
577
578 if (is_invoke) {
580 if (ELEM(nullptr, cdd->vc.region, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
581 MEM_freeN(cdd);
582 BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport");
583 return false;
584 }
585 }
586 else {
587 cdd->vc.bmain = CTX_data_main(C);
588 cdd->vc.depsgraph = depsgraph;
589 cdd->vc.scene = CTX_data_scene(C);
592
593 /* Using an empty stroke complicates logic later,
594 * it's simplest to disallow early on (see: #94085). */
595 if (RNA_collection_is_empty(op->ptr, "stroke")) {
596 MEM_freeN(cdd);
597 BKE_report(op->reports, RPT_ERROR, "The \"stroke\" cannot be empty");
598 return false;
599 }
600 }
601
602 op->customdata = cdd;
603
605
606 cdd->curve_type = cps->curve_type;
607
608 cdd->radius.min = cps->radius_min;
609 cdd->radius.max = cps->radius_max;
610 cdd->radius.range = cps->radius_max - cps->radius_min;
614
616
617 return true;
618}
619
621{
622 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
623 if (cdd) {
624 if (cdd->draw_handle_view) {
627 }
628
629 if (cdd->stroke_elem_pool) {
631 }
632
633 if (cdd->depths) {
635 }
636 MEM_freeN(cdd);
637 op->customdata = nullptr;
638 }
639}
640
645{
646 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
648 PropertyRNA *prop;
649
650 prop = RNA_struct_find_property(op->ptr, "fit_method");
651 if (!RNA_property_is_set(op->ptr, prop)) {
652 RNA_property_enum_set(op->ptr, prop, cps->fit_method);
653 }
654
655 prop = RNA_struct_find_property(op->ptr, "corner_angle");
656 if (!RNA_property_is_set(op->ptr, prop)) {
657 const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle :
658 float(M_PI);
659 RNA_property_float_set(op->ptr, prop, corner_angle);
660 }
661
662 prop = RNA_struct_find_property(op->ptr, "error_threshold");
663 if (!RNA_property_is_set(op->ptr, prop)) {
664
665 /* Error isn't set so we'll have to calculate it from the pixel values. */
666 BLI_mempool_iter iter;
667 const StrokeElem *selem, *selem_prev;
668
669 float len_3d = 0.0f, len_2d = 0.0f;
670 float scale_px; /* pixel to local space scale */
671
672 int i = 0;
674 selem_prev = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
675 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
676 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
677 {
678 len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
679 len_2d += len_v2v2(selem->mval, selem_prev->mval);
680 selem_prev = selem;
681 }
682 scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
683 float error_threshold = (cps->error_threshold * UI_SCALE_FAC) * scale_px;
684 RNA_property_float_set(op->ptr, prop, error_threshold);
685 }
686
687 prop = RNA_struct_find_property(op->ptr, "use_cyclic");
688 if (!RNA_property_is_set(op->ptr, prop)) {
689 bool use_cyclic = false;
690
691 if (BLI_mempool_len(cdd->stroke_elem_pool) > 2) {
692 BLI_mempool_iter iter;
693 const StrokeElem *selem, *selem_first, *selem_last;
694
696 selem_first = selem_last = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter));
697 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
698 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
699 {
700 selem_last = selem;
701 }
702
703 if (len_squared_v2v2(selem_first->mval, selem_last->mval) <=
705 {
706 use_cyclic = true;
707 }
708 }
709
710 RNA_property_boolean_set(op->ptr, prop, use_cyclic);
711 }
712
713 if ((cps->radius_taper_start != 0.0f) || (cps->radius_taper_end != 0.0f)) {
714 /* NOTE: we could try to de-duplicate the length calculations above. */
715 const int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
716
717 BLI_mempool_iter iter;
718 StrokeElem *selem, *selem_prev;
719
720 float *lengths = static_cast<float *>(MEM_mallocN(sizeof(float) * stroke_len, __func__));
721 StrokeElem **selem_array = static_cast<StrokeElem **>(
722 MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__));
723 lengths[0] = 0.0f;
724
725 float len_3d = 0.0f;
726
727 int i = 1;
729 selem_prev = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter));
730 selem_array[0] = selem_prev;
731 for (selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
732 selem = static_cast<StrokeElem *>(BLI_mempool_iterstep(&iter)), i++)
733 {
734 const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
735 len_3d += len_3d_segment;
736 lengths[i] = len_3d;
737 selem_array[i] = selem;
738 selem_prev = selem;
739 }
740
741 if (cps->radius_taper_start != 0.0f) {
742 const float len_taper_max = cps->radius_taper_start * len_3d;
743 for (i = 0; i < stroke_len && lengths[i] < len_taper_max; i++) {
744 const float pressure_new = selem_array[i]->pressure * (lengths[i] / len_taper_max);
745 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
746 }
747 }
748
749 if (cps->radius_taper_end != 0.0f) {
750 const float len_taper_max = cps->radius_taper_end * len_3d;
751 const float len_taper_min = len_3d - len_taper_max;
752 for (i = stroke_len - 1; i > 0 && lengths[i] > len_taper_min; i--) {
753 const float pressure_new = selem_array[i]->pressure *
754 ((len_3d - lengths[i]) / len_taper_max);
755 stroke_elem_pressure_set(cdd, selem_array[i], pressure_new);
756 }
757 }
758
759 MEM_freeN(lengths);
760 MEM_freeN(selem_array);
761 }
762}
763
765{
766 if (op->customdata == nullptr) {
767 if (!curve_draw_init(C, op, false)) {
768 return OPERATOR_CANCELLED;
769 }
770 }
771
772 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
773
775 Object *obedit = cdd->vc.obedit;
776 Curve *cu = static_cast<Curve *>(obedit->data);
777 ListBase *nurblist = object_editcurve_get(obedit);
778
779 int stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
780
781 const bool is_3d = (cu->flag & CU_3D) != 0;
782 invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
783
784 if (BLI_mempool_len(cdd->stroke_elem_pool) == 0) {
786 stroke_len = BLI_mempool_len(cdd->stroke_elem_pool);
787 }
788
789 /* Deselect all existing curves. */
791
792 const float radius_min = cps->radius_min;
793 const float radius_max = cps->radius_max;
794 const float radius_range = cps->radius_max - cps->radius_min;
795
796 Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__));
797 nu->pntsv = 0;
798 nu->resolu = cu->resolu;
799 nu->resolv = cu->resolv;
800 nu->flag |= CU_SMOOTH;
801
802 const bool use_pressure_radius = (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
803 ((cps->radius_taper_start != 0.0f) ||
804 (cps->radius_taper_end != 0.0f));
805
806 if (cdd->curve_type == CU_BEZIER) {
807 nu->type = CU_BEZIER;
808
809#ifdef USE_SPLINE_FIT
810
811 /* Allow to interpolate multiple channels */
812 int dims = 3;
813 struct {
814 int radius;
815 } coords_indices;
816 coords_indices.radius = use_pressure_radius ? dims++ : -1;
817
818 float *coords = static_cast<float *>(
819 MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__));
820
821 float *cubic_spline = nullptr;
822 uint cubic_spline_len = 0;
823
824 /* error in object local space */
825 const int fit_method = RNA_enum_get(op->ptr, "fit_method");
826 const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
827 const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
828 const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
829
830 {
831 BLI_mempool_iter iter;
832 const StrokeElem *selem;
833 float *co = coords;
834
836 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
837 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)), co += dims)
838 {
839 copy_v3_v3(co, selem->location_local);
840 if (coords_indices.radius != -1) {
841 co[coords_indices.radius] = selem->pressure;
842 }
843
844 /* remove doubles */
845 if ((co != coords) && UNLIKELY(memcmp(co, co - dims, sizeof(float) * dims) == 0)) {
846 co -= dims;
847 stroke_len--;
848 }
849 }
850 }
851
852 uint *corners = nullptr;
853 uint corners_len = 0;
854
855 if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < float(M_PI))) {
856 /* this could be configurable... */
857 const float corner_radius_min = error_threshold / 8;
858 const float corner_radius_max = error_threshold * 2;
859 const uint samples_max = 16;
860
861 curve_fit_corners_detect_fl(coords,
862 stroke_len,
863 dims,
864 corner_radius_min,
865 corner_radius_max,
866 samples_max,
867 corner_angle,
868 &corners,
869 &corners_len);
870 }
871
872 uint *corners_index = nullptr;
873 uint corners_index_len = 0;
874 uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
875
876 if ((stroke_len > 2) && use_cyclic) {
877 calc_flag |= CURVE_FIT_CALC_CYCLIC;
878 }
879
880 int result;
881 if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
882 result = curve_fit_cubic_to_points_refit_fl(coords,
883 stroke_len,
884 dims,
885 error_threshold,
886 calc_flag,
887 nullptr,
888 0,
889 corner_angle,
890 &cubic_spline,
891 &cubic_spline_len,
892 nullptr,
893 &corners_index,
894 &corners_index_len);
895 }
896 else {
897 result = curve_fit_cubic_to_points_fl(coords,
898 stroke_len,
899 dims,
900 error_threshold,
901 calc_flag,
902 corners,
903 corners_len,
904 &cubic_spline,
905 &cubic_spline_len,
906 nullptr,
907 &corners_index,
908 &corners_index_len);
909 }
910
911 MEM_freeN(coords);
912 if (corners) {
913 free(corners);
914 }
915
916 if (result == 0) {
917 nu->pntsu = cubic_spline_len;
918 nu->bezt = static_cast<BezTriple *>(MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__));
919
920 float *co = cubic_spline;
921 BezTriple *bezt = nu->bezt;
922 for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
923 const float *handle_l = co + (dims * 0);
924 const float *pt = co + (dims * 1);
925 const float *handle_r = co + (dims * 2);
926
927 copy_v3_v3(bezt->vec[0], handle_l);
928 copy_v3_v3(bezt->vec[1], pt);
929 copy_v3_v3(bezt->vec[2], handle_r);
930
931 if (coords_indices.radius != -1) {
932 bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
933 }
934 else {
935 bezt->radius = radius_max;
936 }
937
938 bezt->h1 = bezt->h2 = HD_ALIGN; /* will set to free in second pass */
939 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
940 }
941
942 if (corners_index) {
943 /* ignore the first and last */
944 uint i_start = 0, i_end = corners_index_len;
945
946 if ((corners_index_len >= 2) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
947 i_start += 1;
948 i_end -= 1;
949 }
950
951 for (uint i = i_start; i < i_end; i++) {
952 bezt = &nu->bezt[corners_index[i]];
953 bezt->h1 = bezt->h2 = HD_FREE;
954 }
955 }
956
957 if (calc_flag & CURVE_FIT_CALC_CYCLIC) {
958 nu->flagu |= CU_NURB_CYCLIC;
959 }
960 }
961
962 if (corners_index) {
963 free(corners_index);
964 }
965
966 if (cubic_spline) {
967 free(cubic_spline);
968 }
969
970#else
971 nu->pntsu = stroke_len;
972 nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
973
974 BezTriple *bezt = nu->bezt;
975
976 {
977 BLI_mempool_iter iter;
978 const struct StrokeElem *selem;
979
981 for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
982 copy_v3_v3(bezt->vec[1], selem->location_local);
983 if (!is_3d) {
984 bezt->vec[1][2] = 0.0f;
985 }
986
987 if (use_pressure_radius) {
988 bezt->radius = selem->pressure;
989 }
990 else {
991 bezt->radius = radius_max;
992 }
993
994 bezt->h1 = bezt->h2 = HD_AUTO;
995
996 bezt->f1 |= SELECT;
997 bezt->f2 |= SELECT;
998 bezt->f3 |= SELECT;
999
1000 bezt++;
1001 }
1002 }
1003#endif
1004
1006 }
1007 else { /* CU_POLY */
1008 BLI_mempool_iter iter;
1009 const StrokeElem *selem;
1010
1011 nu->pntsu = stroke_len;
1012 nu->pntsv = 1;
1013 nu->type = CU_POLY;
1014 nu->bp = static_cast<BPoint *>(MEM_callocN(nu->pntsu * sizeof(BPoint), __func__));
1015
1016 /* Misc settings. */
1017 nu->resolu = cu->resolu;
1018 nu->resolv = 1;
1019 nu->orderu = 4;
1020 nu->orderv = 1;
1021
1022 BPoint *bp = nu->bp;
1023
1025 for (selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)); selem;
1026 selem = static_cast<const StrokeElem *>(BLI_mempool_iterstep(&iter)))
1027 {
1028 copy_v3_v3(bp->vec, selem->location_local);
1029 if (!is_3d) {
1030 bp->vec[2] = 0.0f;
1031 }
1032
1033 if (use_pressure_radius) {
1034 bp->radius = (selem->pressure * radius_range) + radius_min;
1035 }
1036 else {
1037 bp->radius = cps->radius_max;
1038 }
1039 bp->f1 = SELECT;
1040 bp->vec[3] = 1.0f;
1041
1042 bp++;
1043 }
1044
1046 }
1047
1048 BLI_addtail(nurblist, nu);
1049
1051 cu->actvert = nu->pntsu - 1;
1052
1054 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
1055
1056 curve_draw_exit(op);
1057
1058 return OPERATOR_FINISHED;
1059}
1060
1061static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1062{
1063 if (RNA_struct_property_is_set(op->ptr, "stroke")) {
1064 return curve_draw_exec(C, op);
1065 }
1066
1067 if (!curve_draw_init(C, op, true)) {
1068 return OPERATOR_CANCELLED;
1069 }
1070
1071 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1072
1074
1075 const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1076
1077 /* Fallback (in case we can't find the depth on first test). */
1078 {
1079 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1080 float center[3];
1081 negate_v3_v3(center, cdd->vc.rv3d->ofs);
1082 ED_view3d_win_to_3d(cdd->vc.v3d, cdd->vc.region, center, mval_fl, cdd->prev.location_world);
1084 }
1085
1089
1090 {
1091 View3D *v3d = cdd->vc.v3d;
1092 RegionView3D *rv3d = cdd->vc.rv3d;
1093 Object *obedit = cdd->vc.obedit;
1094 Curve *cu = static_cast<Curve *>(obedit->data);
1095
1096 const float *plane_no = nullptr;
1097 const float *plane_co = nullptr;
1098
1099 if (CU_IS_2D(cu)) {
1100 /* 2D overrides other options */
1101 plane_co = obedit->object_to_world().location();
1102 plane_no = obedit->object_to_world().ptr()[2];
1103 cdd->project.use_plane = true;
1104 }
1105 else {
1106 if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && (v3d->shading.type > OB_WIRE)) {
1107 /* needed or else the draw matrix can be incorrect */
1109
1112 depth_mode = V3D_DEPTH_SELECTED_ONLY;
1113 }
1114
1116 cdd->vc.depsgraph, cdd->vc.region, cdd->vc.v3d, nullptr, depth_mode, &cdd->depths);
1117
1118 if (cdd->depths != nullptr) {
1119 cdd->project.use_depth = true;
1120 }
1121 else {
1122 BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane");
1123 cdd->project.use_depth = false;
1124 }
1125 }
1126
1127 /* use view plane (when set or as fallback when surface can't be found) */
1128 if (cdd->project.use_depth == false) {
1129 plane_co = cdd->vc.scene->cursor.location;
1130 plane_no = rv3d->viewinv[2];
1131 cdd->project.use_plane = true;
1132 }
1133
1134 if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
1135 cdd->sample.use_substeps = true;
1136 }
1137 }
1138
1139 if (cdd->project.use_plane) {
1140 normalize_v3_v3(cdd->project.plane, plane_no);
1141 cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
1142 }
1143 }
1144
1145 if (is_modal == false) {
1146 curve_draw_event_add_first(op, event);
1147 }
1148
1149 /* add temp handler */
1151
1153}
1154
1155static void curve_draw_cancel(bContext * /*C*/, wmOperator *op)
1156{
1157 curve_draw_exit(op);
1158}
1159
1160/* Modal event handling of frame changing */
1161static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
1162{
1164 CurveDrawData *cdd = static_cast<CurveDrawData *>(op->customdata);
1165
1166 UNUSED_VARS(C, op);
1167
1168 if (event->type == cdd->init_event_type) {
1169 if (event->val == KM_RELEASE) {
1171
1173
1175
1176 curve_draw_exec(C, op);
1177
1178 return OPERATOR_FINISHED;
1179 }
1180 }
1181 else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
1183 curve_draw_cancel(C, op);
1184 return OPERATOR_CANCELLED;
1185 }
1186 else if (ELEM(event->type, LEFTMOUSE)) {
1187 if (event->val == KM_PRESS) {
1188 curve_draw_event_add_first(op, event);
1189 }
1190 }
1191 else if (ISMOUSE_MOTION(event->type)) {
1192 if (cdd->state == CURVE_DRAW_PAINTING) {
1193 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1195 curve_draw_event_add(op, event);
1196 }
1197 }
1198 }
1199
1200 return ret;
1201}
1202
1204{
1205 /* identifiers */
1206 ot->name = "Draw Curve";
1207 ot->idname = "CURVE_OT_draw";
1208 ot->description = "Draw a freehand spline";
1209
1210 /* api callbacks */
1216
1217 /* flags */
1219
1220 /* properties */
1221 PropertyRNA *prop;
1222
1224 "error_threshold",
1225 0.0f,
1226 0.0f,
1227 10.0f,
1228 "Error",
1229 "Error distance threshold (in object units)",
1230 0.0001f,
1231 10.0f);
1232 RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
1233
1235 "fit_method",
1238 "Fit Method",
1239 "");
1240
1242 ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
1244
1245 prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
1247
1248 prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
1250
1251 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1253}
1254
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_nurb_handles_calc(Nurb *nu)
Definition curve.cc:3955
#define CU_IS_2D(cu)
Definition BKE_curve.hh:86
void BKE_curve_nurb_active_set(Curve *cu, const Nurb *nu)
Definition curve.cc:4983
void BKE_nurb_knot_calc_u(Nurb *nu)
Definition curve.cc:1181
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
sqrt(x)+1/max(0
void BLI_kdtree_nd_ free(KDTree *tree)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
MINLINE float square_f(float a)
MINLINE float interpf(float target, float origin, float t)
#define M_PI
void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
#define DEG2RADF(_deg)
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition math_vector.c:21
MINLINE void copy_v2_v2(float r[2], const float a[2])
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 float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL()
void * BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1)
@ BLI_MEMPOOL_ALLOW_ITER
Definition BLI_mempool.h:95
void * BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
unsigned int uint
#define UNUSED_VARS(...)
#define ARRAY_SET_ITEMS(...)
#define UNLIKELY(x)
#define ELEM(...)
typedef double(DMatrix)[4][4]
void DEG_id_tag_update(ID *id, unsigned int flags)
@ CU_SMOOTH
@ CU_BEZIER
@ CU_POLY
@ CU_3D
@ HD_FREE
@ HD_AUTO
@ HD_ALIGN
@ CU_NURB_CYCLIC
@ OB_WIRE
Object is a sort of wrapper for general info.
@ CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE
@ CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW
@ CURVE_PAINT_FIT_METHOD_REFIT
@ CURVE_PAINT_FIT_METHOD_SPLIT
@ CURVE_PAINT_PROJECT_SURFACE
@ CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS
@ CURVE_PAINT_FLAG_DEPTH_STROKE_OFFSET_ABS
@ CURVE_PAINT_FLAG_CORNERS_DETECT
@ CURVE_PAINT_FLAG_PRESSURE_RADIUS
@ CURVE_PAINT_FLAG_DEPTH_ONLY_SELECTED
#define UI_SCALE_FAC
@ OPERATOR_RUNNING_MODAL
bool ED_operator_editcurve(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, ViewDepths **r_depths)
bool ED_view3d_depth_read_cached_normal(const ARegion *region, const ViewDepths *depths, const int mval[2], float r_normal[3])
void view3d_operator_needs_opengl(const bContext *C)
void ED_view3d_depths_free(ViewDepths *depths)
eV3DDepthOverrideMode
Definition ED_view3d.hh:184
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:194
@ V3D_DEPTH_NO_OVERLAYS
Definition ED_view3d.hh:186
bool ED_view3d_win_to_3d_on_plane(const ARegion *region, const float plane[4], const float mval[2], bool do_clip, float r_out[3])
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
#define GPU_batch_uniform_3fv(batch, name, val)
Definition GPU_batch.hh:306
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, eGPUBuiltinShader shader_id)
void GPU_batch_draw(blender::gpu::Batch *batch)
blender::gpu::Batch * GPU_batch_preset_sphere(int lod) ATTR_WARN_UNUSED_RESULT
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void imm_cpack(uint x)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_scale_1f(float factor)
void GPU_matrix_translate_3f(float x, float y, float z)
void GPU_matrix_pop()
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:161
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:111
@ GPU_DEPTH_NONE
Definition GPU_state.hh:108
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_ANGLE
Definition RNA_types.hh:155
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_WIRE
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#define SELECT
const Depsgraph * depsgraph
ListBase * object_editcurve_get(Object *ob)
Definition editcurve.cc:88
#define STROKE_CYCLIC_DIST_PX
static void curve_draw_exec_precalc(wmOperator *op)
static bool stroke_elem_project(const CurveDrawData *cdd, const int mval_i[2], const float mval_fl[2], float surface_offset, const float radius, float r_location_world[3], float r_normal_world[3])
CurveDrawState
@ CURVE_DRAW_IDLE
@ CURVE_DRAW_PAINTING
static int curve_draw_exec(bContext *C, wmOperator *op)
#define STROKE_SAMPLE_DIST_MIN_PX
static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
void CURVE_OT_draw(wmOperatorType *ot)
static void curve_draw_stroke_to_operator_elem(wmOperator *op, const StrokeElem *selem)
static void curve_draw_stroke_3d(const bContext *, ARegion *, void *arg)
static void curve_draw_stroke_from_operator_elem(wmOperator *op, PointerRNA *itemptr)
static float stroke_elem_radius(const CurveDrawData *cdd, const StrokeElem *selem)
static void curve_draw_stroke_to_operator(wmOperator *op)
static bool stroke_elem_project_fallback_elem(const CurveDrawData *cdd, const float location_fallback_depth[3], StrokeElem *selem)
static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
static float stroke_elem_radius_from_pressure(const CurveDrawData *cdd, const float pressure)
static void stroke_elem_pressure_set(const CurveDrawData *cdd, StrokeElem *selem, float pressure)
static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void curve_draw_stroke_from_operator(wmOperator *op)
#define STROKE_SAMPLE_DIST_MAX_PX
static void stroke_elem_interp(StrokeElem *selem_out, const StrokeElem *selem_a, const StrokeElem *selem_b, float t)
static bool stroke_elem_project_fallback(const CurveDrawData *cdd, const int mval_i[2], const float mval_fl[2], const float surface_offset, const float radius, const float location_fallback_depth[3], float r_location_world[3], float r_location_local[3], float r_normal_world[3], float r_normal_local[3])
static void curve_draw_cancel(bContext *, wmOperator *op)
static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
static void curve_draw_exit(wmOperator *op)
bool ED_curve_deselect_all_multi(bContext *C)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float3 ceil(const float3 a)
return ret
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_collection_is_empty(PointerRNA *ptr, const char *name)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_distance(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
void RNA_def_property_ui_range(PropertyRNA *prop, double min, double max, double step, int precision)
const EnumPropertyItem rna_enum_curve_fit_method_items[]
Definition rna_scene.cc:250
struct ARegionType * type
uint8_t f1
float vec[4]
float vec[3][3]
struct CurveDrawData::@311 project
bool use_surface_offset_absolute
ViewDepths * depths
struct CurveDrawData::@313 radius
struct CurveDrawData::@312 sample
struct CurveDrawData::@314 prev
float location_world[3]
CurveDrawState state
const StrokeElem * selem
float location_world_valid[3]
BLI_mempool * stroke_elem_pool
short resolv
short resolu
float bevel_radius
Definition DNA_ID.h:413
short flagu
short orderu
short orderv
short flag
short type
BezTriple * bezt
BPoint * bp
short resolu
short resolv
ObjectRuntimeHandle * runtime
float viewinv[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
float normal_world[3]
float normal_local[3]
float location_local[3]
float location_world[3]
struct CurvePaintSettings curve_paint_settings
View3DShading shading
RegionView3D * rv3d
Definition ED_view3d.hh:76
ARegion * region
Definition ED_view3d.hh:73
Scene * scene
Definition ED_view3d.hh:69
Main * bmain
Definition ED_view3d.hh:63
wmWindow * win
Definition ED_view3d.hh:75
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
unsigned short w
Definition ED_view3d.hh:82
double depth_range[2]
Definition ED_view3d.hh:86
unsigned short h
Definition ED_view3d.hh:82
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_PAINT_BRUSH
Definition wm_cursors.hh:33
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_MOTION(event_type)
@ RIGHTMOUSE
@ LEFTMOUSE
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4125