Blender V5.0
dynamicpaint.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
8
9#include "MEM_guardedalloc.h"
10
11#include <algorithm>
12#include <cmath>
13#include <cstdio>
14
15#include "BLI_fileops.h"
16#include "BLI_kdtree.h"
17#include "BLI_listbase.h"
18#include "BLI_math_color.h"
19#include "BLI_math_geom.h"
20#include "BLI_math_matrix.h"
21#include "BLI_math_vector.h"
22#include "BLI_mutex.hh"
23#include "BLI_path_utils.hh"
24#include "BLI_string.h"
25#include "BLI_string_utf8.h"
26#include "BLI_string_utils.hh"
27#include "BLI_task.h"
28#include "BLI_threads.h"
29#include "BLI_utildefines.h"
30
31#include "BLT_translation.hh"
32
34#include "DNA_mesh_types.h"
35#include "DNA_meshdata_types.h"
36#include "DNA_modifier_types.h"
38#include "DNA_object_types.h"
39#include "DNA_scene_types.h"
40#include "DNA_texture_types.h"
41
42#include "BKE_armature.hh"
43#include "BKE_bvhutils.hh" /* bvh tree */
44#include "BKE_collision.h"
45#include "BKE_colorband.hh"
46#include "BKE_constraint.h"
47#include "BKE_customdata.hh"
48#include "BKE_deform.hh"
49#include "BKE_dynamicpaint.h"
50#include "BKE_effect.h"
51#include "BKE_image.hh"
52#include "BKE_image_format.hh"
53#include "BKE_lib_id.hh"
54#include "BKE_main.hh"
55#include "BKE_material.hh"
56#include "BKE_mesh.hh"
57#include "BKE_mesh_mapping.hh"
58#include "BKE_mesh_runtime.hh"
59#include "BKE_modifier.hh"
60#include "BKE_object.hh"
61#include "BKE_particle.h"
62#include "BKE_pointcache.h"
63#include "BKE_scene.hh"
64
65#include "DEG_depsgraph.hh"
67
68/* for image output */
69#include "IMB_imbuf.hh"
70#include "IMB_imbuf_types.hh"
71
72#include "RE_texture.h"
73
74#include "atomic_ops.h"
75
76#include "CLG_log.h"
77
78using blender::int3;
79
80/* could enable at some point but for now there are far too many conversions */
81#ifdef __GNUC__
82// # pragma GCC diagnostic ignored "-Wdouble-promotion"
83#endif
84
85static CLG_LogRef LOG = {"object.dynamicpaint"};
86
87/* precalculated gaussian factors for 5x super sampling */
88static const float gaussianFactors[5] = {
89 0.996849f,
90 0.596145f,
91 0.596145f,
92 0.596145f,
93 0.524141f,
94};
95static const float gaussianTotal = 3.309425f;
96
97/* UV Image neighboring pixel table x and y list */
98static int neighX[8] = {1, 1, 0, -1, -1, -1, 0, 1};
99static int neighY[8] = {0, 1, 1, 1, 0, -1, -1, -1};
100
101/* Neighbor x/y list that prioritizes grid directions over diagonals */
102static int neighStraightX[8] = {1, 0, -1, 0, 1, -1, -1, 1};
103static int neighStraightY[8] = {0, 1, 0, -1, 1, 1, -1, -1};
104
105/* subframe_updateObject() flags */
106#define SUBFRAME_RECURSION OBJECT_MODIFIER_UPDATE_SUBFRAME_RECURSION_DEFAULT
107/* #surface_getBrushFlags() return values. */
108#define BRUSH_USES_VELOCITY (1 << 0)
109/* Brush mesh ray-cast status. */
110#define HIT_VOLUME 1
111#define HIT_PROXIMITY 2
112/* dynamicPaint_findNeighborPixel() return codes */
113#define NOT_FOUND -1
114#define ON_MESH_EDGE -2
115#define OUT_OF_TEXTURE -3
116/* paint effect default movement per frame in global units */
117#define EFF_MOVEMENT_PER_FRAME 0.05f
118/* initial wave time factor */
119#define WAVE_TIME_FAC (1.0f / 24.0f)
120#define CANVAS_REL_SIZE 5.0f
121/* drying limits */
122#define MIN_WETNESS 0.001f
123#define MAX_WETNESS 5.0f
124
135
136/* dissolve inline function */
137BLI_INLINE void value_dissolve(float *r_value,
138 const float time,
139 const float scale,
140 const bool is_log)
141{
142 *r_value = (is_log) ? (*r_value) * powf(MIN_WETNESS, 1.0f / (1.2f * time / scale)) :
143 (*r_value) - 1.0f / time * scale;
144}
145
146/***************************** Internal Structs ***************************/
147
148struct Bounds2D {
149 float min[2], max[2];
150};
151
152struct Bounds3D {
153 float min[3], max[3];
154 bool valid;
155};
156
173
174struct Vec3f {
175 float v[3];
176};
177
180 float dir[3];
182 float dist;
183};
184
188 float invNorm[3];
191};
192
195 /* point space data */
198 int *s_pos;
200 int *s_num;
204 float dim[3];
205
206 /* adjacency info */
210 /* space partitioning */
213
214 /* velocity and movement */
226 float prev_obmat[4][4];
228 int clear;
229};
230
233 /* Pixel / mesh data */
237 /* vertex indexes */
239
242};
243
246 Vec3f *barycentricWeights; /* b-weights for all pixel samples */
247};
248
249/* adjacency data flags */
250#define ADJ_ON_MESH_EDGE (1 << 0)
251#define ADJ_BORDER_PIXEL (1 << 1)
252
269
270/************************* Runtime evaluation store ***************************/
271
273{
274 if (runtime_data == nullptr) {
275 return;
276 }
277 if (runtime_data->canvas_mesh) {
278 BKE_id_free(nullptr, runtime_data->canvas_mesh);
279 }
280 {
281 std::lock_guard lock(runtime_data->brush_mutex);
282 if (runtime_data->brush_mesh) {
283 BKE_id_free(nullptr, runtime_data->brush_mesh);
284 }
285 }
286 MEM_delete(runtime_data);
287}
288
290{
291 if (pmd->modifier.runtime == nullptr) {
292 pmd->modifier.runtime = MEM_new<DynamicPaintRuntime>("dynamic paint runtime");
293 }
294 return (DynamicPaintRuntime *)pmd->modifier.runtime;
295}
296
298{
299 if (canvas->pmd->modifier.runtime == nullptr) {
300 return nullptr;
301 }
302 DynamicPaintRuntime *runtime_data = (DynamicPaintRuntime *)canvas->pmd->modifier.runtime;
303 return runtime_data->canvas_mesh;
304}
305
306/***************************** General Utils ******************************/
307
308/* Set canvas error string to display at the bake report */
309static bool setError(DynamicPaintCanvasSettings *canvas, const char *string)
310{
311 /* Add error to canvas ui info label */
312 STRNCPY(canvas->error, string);
313 CLOG_STR_ERROR(&LOG, string);
314 return false;
315}
316
317/* Get number of surface points for cached types */
319{
320 if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) {
321 return 0; /* Not supported at the moment. */
322 }
323 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
324 const Mesh *canvas_mesh = dynamicPaint_canvas_mesh_get(surface->canvas);
325 return (canvas_mesh) ? canvas_mesh->verts_num : 0;
326 }
327
328 return 0;
329}
330
335
337{
338 using namespace blender;
339 using namespace blender::bke;
340 const char *name;
341
342 if (output == 0) {
343 name = surface->output_name;
344 }
345 else if (output == 1) {
346 name = surface->output_name2;
347 }
348 else {
349 return false;
350 }
351
352 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
353 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
354 Mesh *mesh = static_cast<Mesh *>(ob->data);
355 const AttributeAccessor attributes = mesh->attributes();
356 const std::optional<AttributeMetaData> meta_data = attributes.lookup_meta_data(name);
358 }
359 if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
360 return (BKE_object_defgroup_name_index(ob, name) != -1);
361 }
362 }
363
364 return false;
365}
366
369{
370 DynamicPaintSurface *surface = static_cast<DynamicPaintSurface *>(
371 t_surface->canvas->surfaces.first);
372
373 for (; surface; surface = surface->next) {
374 if (surface != t_surface && surface->type == t_surface->type &&
375 surface->format == t_surface->format)
376 {
377 if ((surface->output_name[0] != '\0' && !BLI_path_cmp(name.c_str(), surface->output_name)) ||
378 (surface->output_name2[0] != '\0' && !BLI_path_cmp(name.c_str(), surface->output_name2)))
379 {
380 return true;
381 }
382 }
383 }
384 return false;
385}
386
387static void surface_setUniqueOutputName(DynamicPaintSurface *surface, char *basename, int output)
388{
389 auto is_unique_fn = [&](const blender::StringRefNull check_name) {
390 return surface_duplicateOutputExists(surface, check_name);
391 };
392
393 char name[64];
394 STRNCPY(name, basename); /* in case basename is surface->name use a copy */
395 if (output == 0) {
396 BLI_uniquename_cb(is_unique_fn, name, '.', surface->output_name, sizeof(surface->output_name));
397 }
398 else if (output == 1) {
400 is_unique_fn, name, '.', surface->output_name2, sizeof(surface->output_name2));
401 }
402}
403
406{
407 DynamicPaintSurface *surface = static_cast<DynamicPaintSurface *>(
408 t_surface->canvas->surfaces.first);
409
410 for (; surface; surface = surface->next) {
411 if (surface != t_surface && STREQ(name.c_str(), surface->name)) {
412 return true;
413 }
414 }
415 return false;
416}
417
418void dynamicPaintSurface_setUniqueName(DynamicPaintSurface *surface, const char *basename)
419{
420 char name[64];
421 STRNCPY_UTF8(name, basename); /* in case basename is surface->name use a copy */
423 [&](const blender::StringRefNull check_name) {
424 return surface_duplicateNameExists(surface, check_name);
425 },
426 name,
427 '.',
428 surface->name,
429 sizeof(surface->name));
430}
431
433{
434 const char *name_prefix = "";
435 const char *name_suffix_1 = "";
436 const char *name_suffix_2 = "";
437
438 if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
439 surface->output_name[0] = '\0';
440 surface->output_name2[0] = '\0';
441 surface->flags |= MOD_DPAINT_ANTIALIAS;
442 surface->depth_clamp = 1.0f;
443 }
444 else {
445 name_prefix = "dp_";
446 surface->flags &= ~MOD_DPAINT_ANTIALIAS;
447 surface->depth_clamp = 0.0f;
448 }
449
450 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
451 name_suffix_1 = "paintmap";
452 name_suffix_2 = "wetmap";
453 }
454 else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) {
455 name_suffix_1 = name_suffix_2 = "displace";
456 }
457 else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
458 name_suffix_1 = name_suffix_2 = "weight";
459 }
460 else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
461 name_suffix_1 = name_suffix_2 = "wave";
462 }
463
464 SNPRINTF(surface->output_name, "%s%s", name_prefix, name_suffix_1);
465 SNPRINTF(surface->output_name2, "%s%s", name_prefix, name_suffix_2);
466 const bool output_name_equal = STREQ(surface->output_name, surface->output_name2);
467
468 surface_setUniqueOutputName(surface, surface->output_name, 0);
469 if (!output_name_equal) {
470 surface_setUniqueOutputName(surface, surface->output_name2, 1);
471 }
472}
473
475{
476 if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ && surface->flags & MOD_DPAINT_ANTIALIAS) {
477 return (surface->data->total_points * 5);
478 }
479 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX && surface->flags & MOD_DPAINT_ANTIALIAS &&
480 surface->data->adj_data)
481 {
482 return (surface->data->total_points + surface->data->adj_data->total_targets);
483 }
484
485 return surface->data->total_points;
486}
487
488static void blendColors(const float t_color[3],
489 const float t_alpha,
490 const float s_color[3],
491 const float s_alpha,
492 float result[4])
493{
494 /* Same thing as BLI's blend_color_mix_float(), but for non-premultiplied alpha. */
495 float i_alpha = 1.0f - s_alpha;
496 float f_alpha = t_alpha * i_alpha + s_alpha;
497
498 /* blend colors */
499 if (f_alpha) {
500 for (int i = 0; i < 3; i++) {
501 result[i] = (t_color[i] * t_alpha * i_alpha + s_color[i] * s_alpha) / f_alpha;
502 }
503 }
504 else {
505 copy_v3_v3(result, t_color);
506 }
507 /* return final alpha */
508 result[3] = f_alpha;
509}
510
511/* Mix two alpha weighed colors by a defined ratio. output is saved at a_color */
512static float mixColors(
513 float a_color[3], float a_weight, const float b_color[3], float b_weight, float ratio)
514{
515 float weight_ratio, factor;
516 if (b_weight) {
517 /* if first value has no weight just use b_color */
518 if (!a_weight) {
519 copy_v3_v3(a_color, b_color);
520 return b_weight * ratio;
521 }
522 weight_ratio = b_weight / (a_weight + b_weight);
523 }
524 else {
525 return a_weight * (1.0f - ratio);
526 }
527
528 /* calculate final interpolation factor */
529 if (ratio <= 0.5f) {
530 factor = weight_ratio * (ratio * 2.0f);
531 }
532 else {
533 ratio = (ratio * 2.0f - 1.0f);
534 factor = weight_ratio * (1.0f - ratio) + ratio;
535 }
536 /* mix final color */
537 interp_v3_v3v3(a_color, a_color, b_color, factor);
538 return (1.0f - factor) * a_weight + factor * b_weight;
539}
540
541static void scene_setSubframe(Scene *scene, float subframe)
542{
543 /* Dynamic paint sub-frames must be done on previous frame. */
544 scene->r.cfra -= 1;
545 scene->r.subframe = subframe;
546}
547
549{
550 uint numobjects;
552 depsgraph, nullptr, surface->brush_group, &numobjects, eModifierType_DynamicPaint);
553
554 int flags = 0;
555
556 for (int i = 0; i < numobjects; i++) {
557 Object *brushObj = objects[i];
558
562
563 if (pmd2->brush) {
565
566 if (brush->flags & MOD_DPAINT_USES_VELOCITY) {
567 flags |= BRUSH_USES_VELOCITY;
568 }
569 }
570 }
571 }
572
574
575 return flags;
576}
577
578/* check whether two bounds intersect */
579static bool boundsIntersect(Bounds3D *b1, Bounds3D *b2)
580{
581 if (!b1->valid || !b2->valid) {
582 return false;
583 }
584 for (int i = 2; i--;) {
585 if (!(b1->min[i] <= b2->max[i] && b1->max[i] >= b2->min[i])) {
586 return false;
587 }
588 }
589 return true;
590}
591
592/* check whether two bounds intersect inside defined proximity */
593static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist)
594{
595 if (!b1->valid || !b2->valid) {
596 return false;
597 }
598 for (int i = 2; i--;) {
599 if (!(b1->min[i] <= (b2->max[i] + dist) && b1->max[i] >= (b2->min[i] - dist))) {
600 return false;
601 }
602 }
603 return true;
604}
605
606/* check whether bounds intersects a point with given radius */
607static bool boundIntersectPoint(Bounds3D *b, const float point[3], const float radius)
608{
609 if (!b->valid) {
610 return false;
611 }
612 for (int i = 2; i--;) {
613 if (!(b->min[i] <= (point[i] + radius) && b->max[i] >= (point[i] - radius))) {
614 return false;
615 }
616 }
617 return true;
618}
619
620/* expand bounds by a new point */
621static void boundInsert(Bounds3D *b, const float point[3])
622{
623 if (!b->valid) {
624 copy_v3_v3(b->min, point);
625 copy_v3_v3(b->max, point);
626 b->valid = true;
627 return;
628 }
629
630 minmax_v3v3_v3(b->min, b->max, point);
631}
632
634{
635 Bounds3D *mb = &sData->bData->mesh_bounds;
636 return max_fff((mb->max[0] - mb->min[0]), (mb->max[1] - mb->min[1]), (mb->max[2] - mb->min[2]));
637}
638
640{
641 PaintBakeData *bData = data->bData;
642 DynamicPaintVolumeGrid *grid = bData->grid;
643
644 if (grid->bounds) {
645 MEM_freeN(grid->bounds);
646 }
647 if (grid->s_pos) {
648 MEM_freeN(grid->s_pos);
649 }
650 if (grid->s_num) {
651 MEM_freeN(grid->s_num);
652 }
653 if (grid->t_index) {
654 MEM_freeN(grid->t_index);
655 }
656
657 MEM_freeN(bData->grid);
658 bData->grid = nullptr;
659}
660
661static void grid_bound_insert_cb_ex(void *__restrict userdata,
662 const int i,
663 const TaskParallelTLS *__restrict tls)
664{
665 PaintBakeData *bData = static_cast<PaintBakeData *>(userdata);
666
667 Bounds3D *grid_bound = static_cast<Bounds3D *>(tls->userdata_chunk);
668
669 boundInsert(grid_bound, bData->realCoord[bData->s_pos[i]].v);
670}
671
672static void grid_bound_insert_reduce(const void *__restrict /*userdata*/,
673 void *__restrict chunk_join,
674 void *__restrict chunk)
675{
676 Bounds3D *join = static_cast<Bounds3D *>(chunk_join);
677 Bounds3D *grid_bound = static_cast<Bounds3D *>(chunk);
678
679 boundInsert(join, grid_bound->min);
680 boundInsert(join, grid_bound->max);
681}
682
683static void grid_cell_points_cb_ex(void *__restrict userdata,
684 const int i,
685 const TaskParallelTLS *__restrict tls)
686{
687 PaintBakeData *bData = static_cast<PaintBakeData *>(userdata);
688 DynamicPaintVolumeGrid *grid = bData->grid;
689 int *temp_t_index = grid->temp_t_index;
690 int *s_num = static_cast<int *>(tls->userdata_chunk);
691
692 int co[3];
693
694 for (int j = 3; j--;) {
695 co[j] = int(floorf((bData->realCoord[bData->s_pos[i]].v[j] - grid->grid_bounds.min[j]) /
696 bData->dim[j] * grid->dim[j]));
697 CLAMP(co[j], 0, grid->dim[j] - 1);
698 }
699
700 temp_t_index[i] = co[0] + co[1] * grid->dim[0] + co[2] * grid->dim[0] * grid->dim[1];
701 s_num[temp_t_index[i]]++;
702}
703
704static void grid_cell_points_reduce(const void *__restrict userdata,
705 void *__restrict chunk_join,
706 void *__restrict chunk)
707{
708 const PaintBakeData *bData = static_cast<const PaintBakeData *>(userdata);
709 const DynamicPaintVolumeGrid *grid = bData->grid;
710 const int grid_cells = grid->dim[0] * grid->dim[1] * grid->dim[2];
711
712 int *join_s_num = static_cast<int *>(chunk_join);
713 int *s_num = static_cast<int *>(chunk);
714
715 /* calculate grid indexes */
716 for (int i = 0; i < grid_cells; i++) {
717 join_s_num[i] += s_num[i];
718 }
719}
720
721static void grid_cell_bounds_cb(void *__restrict userdata,
722 const int x,
723 const TaskParallelTLS *__restrict /*tls*/)
724{
725 PaintBakeData *bData = static_cast<PaintBakeData *>(userdata);
726 DynamicPaintVolumeGrid *grid = bData->grid;
727 float *dim = bData->dim;
728 int *grid_dim = grid->dim;
729
730 for (int y = 0; y < grid_dim[1]; y++) {
731 for (int z = 0; z < grid_dim[2]; z++) {
732 const int b_index = x + y * grid_dim[0] + z * grid_dim[0] * grid_dim[1];
733 /* set bounds */
734 for (int j = 3; j--;) {
735 const int s = (j == 0) ? x : ((j == 1) ? y : z);
736 grid->bounds[b_index].min[j] = grid->grid_bounds.min[j] + dim[j] / grid_dim[j] * s;
737 grid->bounds[b_index].max[j] = grid->grid_bounds.min[j] + dim[j] / grid_dim[j] * (s + 1);
738 }
739 grid->bounds[b_index].valid = true;
740 }
741 }
742}
743
745{
746 PaintSurfaceData *sData = surface->data;
747 PaintBakeData *bData = sData->bData;
749 int grid_cells, axis = 3;
750 int *temp_t_index = nullptr;
751 int *temp_s_num = nullptr;
752
753 if (bData->grid) {
754 freeGrid(sData);
755 }
756
757 bData->grid = MEM_callocN<DynamicPaintVolumeGrid>(__func__);
758 grid = bData->grid;
759
760 {
761 int i, error = 0;
762 float dim_factor, volume, dim[3];
763 float td[3];
764 float min_dim;
765
766 /* calculate canvas dimensions */
767 /* Important to init correctly our ref grid_bound... */
768 boundInsert(&grid->grid_bounds, bData->realCoord[bData->s_pos[0]].v);
769 {
770 TaskParallelSettings settings;
772 settings.use_threading = (sData->total_points > 1000);
773 settings.userdata_chunk = &grid->grid_bounds;
774 settings.userdata_chunk_size = sizeof(grid->grid_bounds);
777 }
778 /* get dimensions */
779 sub_v3_v3v3(dim, grid->grid_bounds.max, grid->grid_bounds.min);
780 copy_v3_v3(td, dim);
781 copy_v3_v3(bData->dim, dim);
782 min_dim = max_fff(td[0], td[1], td[2]) / 1000.0f;
783
784 /* deactivate zero axes */
785 for (i = 0; i < 3; i++) {
786 if (td[i] < min_dim) {
787 td[i] = 1.0f;
788 axis--;
789 }
790 }
791
792 if (axis == 0 || max_fff(td[0], td[1], td[2]) < 0.0001f) {
793 MEM_freeN(bData->grid);
794 bData->grid = nullptr;
795 return;
796 }
797
798 /* now calculate grid volume/area/width depending on num of active axis */
799 volume = td[0] * td[1] * td[2];
800
801 /* determine final grid size by trying to fit average 10.000 points per grid cell */
802 dim_factor = float(
803 pow(double(volume) / (double(sData->total_points) / 10000.0), 1.0 / double(axis)));
804
805 /* define final grid size using dim_factor, use min 3 for active axes */
806 for (i = 0; i < 3; i++) {
807 grid->dim[i] = int(floor(td[i] / dim_factor));
808 CLAMP(grid->dim[i], (dim[i] >= min_dim) ? 3 : 1, 100);
809 }
810 grid_cells = grid->dim[0] * grid->dim[1] * grid->dim[2];
811
812 /* allocate memory for grids */
813 grid->bounds = MEM_calloc_arrayN<Bounds3D>(grid_cells, "Surface Grid Bounds");
814 grid->s_pos = MEM_calloc_arrayN<int>(grid_cells, "Surface Grid Position");
815
816 grid->s_num = MEM_calloc_arrayN<int>(grid_cells, "Surface Grid Points");
817 temp_s_num = MEM_calloc_arrayN<int>(grid_cells, "Temp Surface Grid Points");
818 grid->t_index = MEM_calloc_arrayN<int>(sData->total_points, "Surface Grid Target Ids");
819 grid->temp_t_index = temp_t_index = MEM_calloc_arrayN<int>(sData->total_points,
820 "Temp Surface Grid Target Ids");
821
822 /* in case of an allocation failure abort here */
823 if (!grid->bounds || !grid->s_pos || !grid->s_num || !grid->t_index || !temp_s_num ||
824 !temp_t_index)
825 {
826 error = 1;
827 }
828
829 if (!error) {
830 /* calculate number of points within each cell */
831 {
832 TaskParallelSettings settings;
834 settings.use_threading = (sData->total_points > 1000);
835 settings.userdata_chunk = grid->s_num;
836 settings.userdata_chunk_size = sizeof(*grid->s_num) * grid_cells;
838 BLI_task_parallel_range(0, sData->total_points, bData, grid_cell_points_cb_ex, &settings);
839 }
840
841 /* calculate grid indexes (not needed for first cell, which is zero). */
842 for (i = 1; i < grid_cells; i++) {
843 grid->s_pos[i] = grid->s_pos[i - 1] + grid->s_num[i - 1];
844 }
845
846 /* save point indexes to final array */
847 for (i = 0; i < sData->total_points; i++) {
848 int pos = grid->s_pos[temp_t_index[i]] + temp_s_num[temp_t_index[i]];
849 grid->t_index[pos] = i;
850
851 temp_s_num[temp_t_index[i]]++;
852 }
853
854 /* calculate cell bounds */
855 {
856 TaskParallelSettings settings;
858 settings.use_threading = (grid_cells > 1000);
859 BLI_task_parallel_range(0, grid->dim[0], bData, grid_cell_bounds_cb, &settings);
860 }
861 }
862
863 if (temp_s_num) {
864 MEM_freeN(temp_s_num);
865 }
866 MEM_SAFE_FREE(temp_t_index);
867
868 if (error || !grid->s_num) {
869 setError(surface->canvas, N_("Not enough free memory"));
870 freeGrid(sData);
871 }
872 }
873}
874
875/***************************** Freeing data ******************************/
876
878{
879 if (pmd->brush) {
880 if (pmd->brush->paint_ramp) {
882 }
883 if (pmd->brush->vel_ramp) {
884 MEM_freeN(pmd->brush->vel_ramp);
885 }
886
887 MEM_freeN(pmd->brush);
888 pmd->brush = nullptr;
889 }
890}
891
893{
894 if (data->adj_data) {
895 if (data->adj_data->n_index) {
896 MEM_freeN(data->adj_data->n_index);
897 }
898 if (data->adj_data->n_num) {
899 MEM_freeN(data->adj_data->n_num);
900 }
901 if (data->adj_data->n_target) {
902 MEM_freeN(data->adj_data->n_target);
903 }
904 if (data->adj_data->flags) {
905 MEM_freeN(data->adj_data->flags);
906 }
907 if (data->adj_data->border) {
908 MEM_freeN(data->adj_data->border);
909 }
910 MEM_freeN(data->adj_data);
911 data->adj_data = nullptr;
912 }
913}
914
916{
917 PaintBakeData *bData = data->bData;
918 if (bData) {
919 if (bData->bNormal) {
920 MEM_freeN(bData->bNormal);
921 }
922 if (bData->s_pos) {
923 MEM_freeN(bData->s_pos);
924 }
925 if (bData->s_num) {
926 MEM_freeN(bData->s_num);
927 }
928 if (bData->realCoord) {
929 MEM_freeN(bData->realCoord);
930 }
931 if (bData->bNeighs) {
932 MEM_freeN(bData->bNeighs);
933 }
934 if (bData->grid) {
935 freeGrid(data);
936 }
937 if (bData->prev_positions) {
939 }
940 if (bData->velocity) {
941 MEM_freeN(bData->velocity);
942 }
943 if (bData->prev_velocity) {
944 MEM_freeN(bData->prev_velocity);
945 }
946
947 MEM_freeN(data->bData);
948 data->bData = nullptr;
949 }
950}
951
952/* free surface data if it's not used anymore */
954{
955 if (!surface->data) {
956 return;
957 }
958
959 /* Free bake-data if not active or surface is baked. */
960 if (!(surface->flags & MOD_DPAINT_ACTIVE) ||
961 (surface->pointcache && surface->pointcache->flag & PTCACHE_BAKED))
962 {
963 free_bakeData(surface->data);
964 }
965}
966
968{
969 PaintSurfaceData *data = surface->data;
970 if (!data) {
971 return;
972 }
973
974 if (data->format_data) {
975 /* format specific free */
976 if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
977 ImgSeqFormatData *format_data = (ImgSeqFormatData *)data->format_data;
978 if (format_data->uv_p) {
979 MEM_freeN(format_data->uv_p);
980 }
981 if (format_data->barycentricWeights) {
982 MEM_freeN(format_data->barycentricWeights);
983 }
984 }
985 MEM_freeN(data->format_data);
986 }
987 /* type data */
988 if (data->type_data) {
989 MEM_freeN(data->type_data);
990 }
992 /* bake data */
994
995 MEM_freeN(surface->data);
996 surface->data = nullptr;
997}
998
1000{
1001 /* point cache */
1002 if ((pmd->modifier.flag & eModifierFlag_SharedCaches) == 0) {
1003 BKE_ptcache_free_list(&(surface->ptcaches));
1004 }
1005 surface->pointcache = nullptr;
1006
1008
1009 BLI_remlink(&(surface->canvas->surfaces), surface);
1011 MEM_freeN(surface);
1012}
1013
1015{
1016 if (pmd->canvas) {
1017 /* Free surface data */
1018 DynamicPaintSurface *surface = static_cast<DynamicPaintSurface *>(pmd->canvas->surfaces.first);
1019 DynamicPaintSurface *next_surface = nullptr;
1020
1021 while (surface) {
1022 next_surface = surface->next;
1023 dynamicPaint_freeSurface(pmd, surface);
1024 surface = next_surface;
1025 }
1026
1027 MEM_freeN(pmd->canvas);
1028 pmd->canvas = nullptr;
1029 }
1030}
1031
1033{
1034 if (pmd == nullptr) {
1035 return;
1036 }
1040}
1041
1042/***************************** Initialize and reset ******************************/
1043
1045 Scene *scene)
1046{
1048 if (!surface) {
1049 return nullptr;
1050 }
1051
1052 surface->canvas = canvas;
1055
1056 /* cache */
1057 surface->pointcache = BKE_ptcache_add(&(surface->ptcaches));
1058 surface->pointcache->flag |= PTCACHE_DISK_CACHE;
1059 surface->pointcache->step = 1;
1060
1061 /* Set initial values */
1065 surface->effect = 0;
1066 surface->effect_ui = 1;
1067
1068 surface->diss_speed = 250;
1069 surface->dry_speed = 500;
1070 surface->color_dry_threshold = 1.0f;
1071 surface->depth_clamp = 0.0f;
1072 surface->disp_factor = 1.0f;
1075
1076 surface->influence_scale = 1.0f;
1077 surface->radius_scale = 1.0f;
1078
1079 surface->init_color[0] = 1.0f;
1080 surface->init_color[1] = 1.0f;
1081 surface->init_color[2] = 1.0f;
1082 surface->init_color[3] = 1.0f;
1083
1084 surface->image_resolution = 256;
1085 surface->substeps = 0;
1086
1087 if (scene) {
1088 surface->start_frame = scene->r.sfra;
1089 surface->end_frame = scene->r.efra;
1090 }
1091 else {
1092 surface->start_frame = 1;
1093 surface->end_frame = 250;
1094 }
1095
1096 surface->spread_speed = 1.0f;
1097 surface->color_spread_speed = 1.0f;
1098 surface->shrink_speed = 1.0f;
1099
1100 surface->wave_damping = 0.04f;
1101 surface->wave_speed = 1.0f;
1102 surface->wave_timescale = 1.0f;
1103 surface->wave_spring = 0.20f;
1104 surface->wave_smoothness = 1.0f;
1105
1107 surface->image_output_path, sizeof(surface->image_output_path), "cache_dynamicpaint");
1108
1109 /* Using ID_BRUSH i18n context, as we have no physics/dynamic-paint one for now. */
1111
1112 surface->effector_weights = BKE_effector_add_weights(nullptr);
1113
1115
1116 BLI_addtail(&canvas->surfaces, surface);
1117
1118 return surface;
1119}
1120
1122{
1123 if (pmd) {
1124 if (type == MOD_DYNAMICPAINT_TYPE_CANVAS) {
1126 if (pmd->canvas) {
1128 }
1129
1130 canvas = pmd->canvas = MEM_callocN<DynamicPaintCanvasSettings>(__func__);
1131 if (!canvas) {
1132 return false;
1133 }
1134 canvas->pmd = pmd;
1135
1136 /* Create one surface */
1137 if (!dynamicPaint_createNewSurface(canvas, scene)) {
1138 return false;
1139 }
1140 }
1141 else if (type == MOD_DYNAMICPAINT_TYPE_BRUSH) {
1143 if (pmd->brush) {
1145 }
1146
1148 if (!brush) {
1149 return false;
1150 }
1151 brush->pmd = pmd;
1152
1153 brush->psys = nullptr;
1154
1156 brush->collision = MOD_DPAINT_COL_VOLUME;
1157
1158 brush->r = 0.15f;
1159 brush->g = 0.4f;
1160 brush->b = 0.8f;
1161 brush->alpha = 1.0f;
1162 brush->wetness = 1.0f;
1163
1164 brush->paint_distance = 1.0f;
1165 brush->proximity_falloff = MOD_DPAINT_PRFALL_SMOOTH;
1166
1167 brush->particle_radius = 0.2f;
1168 brush->particle_smooth = 0.05f;
1169
1170 brush->wave_type = MOD_DPAINT_WAVEB_CHANGE;
1171 brush->wave_factor = 1.0f;
1172 brush->wave_clamp = 0.0f;
1173 brush->smudge_strength = 0.3f;
1174 brush->max_velocity = 1.0f;
1175
1176 /* Paint proximity falloff color-ramp. */
1177 {
1178 CBData *ramp;
1179
1180 brush->paint_ramp = BKE_colorband_add(false);
1181 if (!brush->paint_ramp) {
1182 return false;
1183 }
1184 ramp = brush->paint_ramp->data;
1185 /* Add default smooth-falloff ramp. */
1186 ramp[0].r = ramp[0].g = ramp[0].b = ramp[0].a = 1.0f;
1187 ramp[0].pos = 0.0f;
1188 ramp[1].r = ramp[1].g = ramp[1].b = ramp[1].pos = 1.0f;
1189 ramp[1].a = 0.0f;
1190 pmd->brush->paint_ramp->tot = 2;
1191 }
1192
1193 /* Brush velocity ramp. */
1194 {
1195 CBData *ramp;
1196
1197 brush->vel_ramp = BKE_colorband_add(false);
1198 if (!brush->vel_ramp) {
1199 return false;
1200 }
1201 ramp = brush->vel_ramp->data;
1202 ramp[0].r = ramp[0].g = ramp[0].b = ramp[0].a = ramp[0].pos = 0.0f;
1203 ramp[1].r = ramp[1].g = ramp[1].b = ramp[1].a = ramp[1].pos = 1.0f;
1204 brush->paint_ramp->tot = 2;
1205 }
1206 }
1207 }
1208 else {
1209 return false;
1210 }
1211
1212 return true;
1213}
1214
1217 int flag)
1218{
1219 /* Init modifier */
1220 tpmd->type = pmd->type;
1221 if (pmd->canvas) {
1223 }
1224 if (pmd->brush) {
1226 }
1227
1228 /* Copy data */
1229 if (tpmd->canvas) {
1230 DynamicPaintSurface *surface;
1231 tpmd->canvas->pmd = tpmd;
1232 /* free default surface */
1233 if (tpmd->canvas->surfaces.first) {
1235 static_cast<DynamicPaintSurface *>(tpmd->canvas->surfaces.first));
1236 }
1237
1238 tpmd->canvas->active_sur = pmd->canvas->active_sur;
1239
1240 /* copy existing surfaces */
1241 for (surface = static_cast<DynamicPaintSurface *>(pmd->canvas->surfaces.first); surface;
1242 surface = surface->next)
1243 {
1244 DynamicPaintSurface *t_surface = dynamicPaint_createNewSurface(tpmd->canvas, nullptr);
1246 /* TODO(sergey): Consider passing some tips to the surface
1247 * creation to avoid this allocate-and-free cache behavior. */
1248 BKE_ptcache_free_list(&t_surface->ptcaches);
1250 t_surface->ptcaches = surface->ptcaches;
1251 t_surface->pointcache = surface->pointcache;
1252 }
1253
1254 /* surface settings */
1255 t_surface->brush_group = surface->brush_group;
1256 MEM_freeN(t_surface->effector_weights);
1257 t_surface->effector_weights = static_cast<EffectorWeights *>(
1259
1260 STRNCPY(t_surface->name, surface->name);
1261 t_surface->format = surface->format;
1262 t_surface->type = surface->type;
1263 t_surface->disp_type = surface->disp_type;
1264 t_surface->image_fileformat = surface->image_fileformat;
1265 t_surface->effect_ui = surface->effect_ui;
1266 t_surface->init_color_type = surface->init_color_type;
1267 t_surface->flags = surface->flags;
1268 t_surface->effect = surface->effect;
1269
1270 t_surface->image_resolution = surface->image_resolution;
1271 t_surface->substeps = surface->substeps;
1272 t_surface->start_frame = surface->start_frame;
1273 t_surface->end_frame = surface->end_frame;
1274
1275 copy_v4_v4(t_surface->init_color, surface->init_color);
1276 t_surface->init_texture = surface->init_texture;
1277 STRNCPY(t_surface->init_layername, surface->init_layername);
1278
1279 t_surface->dry_speed = surface->dry_speed;
1280 t_surface->diss_speed = surface->diss_speed;
1281 t_surface->color_dry_threshold = surface->color_dry_threshold;
1282 t_surface->depth_clamp = surface->depth_clamp;
1283 t_surface->disp_factor = surface->disp_factor;
1284
1285 t_surface->spread_speed = surface->spread_speed;
1286 t_surface->color_spread_speed = surface->color_spread_speed;
1287 t_surface->shrink_speed = surface->shrink_speed;
1288 t_surface->drip_vel = surface->drip_vel;
1289 t_surface->drip_acc = surface->drip_acc;
1290
1291 t_surface->influence_scale = surface->influence_scale;
1292 t_surface->radius_scale = surface->radius_scale;
1293
1294 t_surface->wave_damping = surface->wave_damping;
1295 t_surface->wave_speed = surface->wave_speed;
1296 t_surface->wave_timescale = surface->wave_timescale;
1297 t_surface->wave_spring = surface->wave_spring;
1298 t_surface->wave_smoothness = surface->wave_smoothness;
1299
1300 STRNCPY(t_surface->uvlayer_name, surface->uvlayer_name);
1301 STRNCPY(t_surface->image_output_path, surface->image_output_path);
1302 STRNCPY(t_surface->output_name, surface->output_name);
1303 STRNCPY(t_surface->output_name2, surface->output_name2);
1304 }
1305 }
1306 if (tpmd->brush) {
1307 DynamicPaintBrushSettings *brush = pmd->brush, *t_brush = tpmd->brush;
1308 t_brush->pmd = tpmd;
1309
1310 t_brush->flags = brush->flags;
1311 t_brush->collision = brush->collision;
1312
1313 t_brush->r = brush->r;
1314 t_brush->g = brush->g;
1315 t_brush->b = brush->b;
1316 t_brush->alpha = brush->alpha;
1317 t_brush->wetness = brush->wetness;
1318
1319 t_brush->particle_radius = brush->particle_radius;
1320 t_brush->particle_smooth = brush->particle_smooth;
1321 t_brush->paint_distance = brush->paint_distance;
1322
1323 /* NOTE: This is dangerous, as it will generate invalid data in case we are copying between
1324 * different objects. Extra external code has to be called to ensure proper remapping of
1325 * that pointer. See e.g. `BKE_object_copy_particlesystems` or `BKE_object_copy_modifier`. */
1326 t_brush->psys = brush->psys;
1327
1328 if (brush->paint_ramp) {
1329 memcpy(t_brush->paint_ramp, brush->paint_ramp, sizeof(ColorBand));
1330 }
1331 if (brush->vel_ramp) {
1332 memcpy(t_brush->vel_ramp, brush->vel_ramp, sizeof(ColorBand));
1333 }
1334
1335 t_brush->proximity_falloff = brush->proximity_falloff;
1336 t_brush->wave_type = brush->wave_type;
1337 t_brush->ray_dir = brush->ray_dir;
1338
1339 t_brush->wave_factor = brush->wave_factor;
1340 t_brush->wave_clamp = brush->wave_clamp;
1341 t_brush->max_velocity = brush->max_velocity;
1342 t_brush->smudge_strength = brush->smudge_strength;
1343 }
1344}
1345
1346/* allocates surface data depending on surface type */
1348{
1349 PaintSurfaceData *sData = surface->data;
1350
1351 switch (surface->type) {
1354 "DynamicPaintSurface Data");
1355 break;
1358 "DynamicPaintSurface DepthData");
1359 break;
1362 "DynamicPaintSurface WeightData");
1363 break;
1366 "DynamicPaintSurface WaveData");
1367 break;
1368 }
1369
1370 if (sData->type_data == nullptr) {
1371 setError(surface->canvas, N_("Not enough free memory"));
1372 }
1373}
1374
1376{
1377 return ((surface->type == MOD_DPAINT_SURFACE_T_PAINT && surface->effect) ||
1378 (surface->type == MOD_DPAINT_SURFACE_T_WAVE));
1379}
1380
1382{
1383 return (surface_usesAdjDistance(surface) || (surface->format == MOD_DPAINT_SURFACE_F_VERTEX &&
1384 surface->flags & MOD_DPAINT_ANTIALIAS));
1385}
1386
1387/* initialize surface adjacency data */
1388static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const bool force_init)
1389{
1390 PaintSurfaceData *sData = surface->data;
1392 PaintAdjData *ad;
1393 int *temp_data;
1394 int neigh_points = 0;
1395
1396 if (!force_init && !surface_usesAdjData(surface)) {
1397 return;
1398 }
1399
1400 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
1401 /* For vertex format, neighbors are connected by edges */
1402 neigh_points = 2 * mesh->edges_num;
1403 }
1404 else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
1405 neigh_points = sData->total_points * 8;
1406 }
1407
1408 if (!neigh_points) {
1409 return;
1410 }
1411
1412 /* allocate memory */
1413 ad = sData->adj_data = MEM_callocN<PaintAdjData>(__func__);
1414 if (!ad) {
1415 return;
1416 }
1417 ad->n_index = MEM_calloc_arrayN<int>(sData->total_points, "Surface Adj Index");
1418 ad->n_num = MEM_calloc_arrayN<int>(sData->total_points, "Surface Adj Counts");
1419 temp_data = MEM_calloc_arrayN<int>(sData->total_points, "Temp Adj Data");
1420 ad->n_target = MEM_calloc_arrayN<int>(neigh_points, "Surface Adj Targets");
1421 ad->flags = MEM_calloc_arrayN<int>(sData->total_points, "Surface Adj Flags");
1422 ad->total_targets = neigh_points;
1423 ad->border = nullptr;
1424 ad->total_border = 0;
1425
1426 /* in case of allocation error, free memory */
1427 if (!ad->n_index || !ad->n_num || !ad->n_target || !temp_data) {
1429 if (temp_data) {
1430 MEM_freeN(temp_data);
1431 }
1432 setError(surface->canvas, N_("Not enough free memory"));
1433 return;
1434 }
1435
1436 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
1437 /* For vertex format, count every vertex that is connected by an edge */
1438 int numOfEdges = mesh->edges_num;
1439 int numOfPolys = mesh->faces_num;
1440 const blender::Span<blender::int2> edges = mesh->edges();
1441 const blender::OffsetIndices faces = mesh->faces();
1442 const blender::Span<int> corner_verts = mesh->corner_verts();
1443
1444 /* count number of edges per vertex */
1445 for (int i = 0; i < numOfEdges; i++) {
1446 ad->n_num[edges[i][0]]++;
1447 ad->n_num[edges[i][1]]++;
1448
1449 temp_data[edges[i][0]]++;
1450 temp_data[edges[i][1]]++;
1451 }
1452
1453 /* also add number of vertices to temp_data
1454 * to locate points on "mesh edge" */
1455 for (int i = 0; i < numOfPolys; i++) {
1456 for (const int vert : corner_verts.slice(faces[i])) {
1457 temp_data[vert]++;
1458 }
1459 }
1460
1461 /* now check if total number of edges+faces for
1462 * each vertex is even, if not -> vertex is on mesh edge */
1463 for (int i = 0; i < sData->total_points; i++) {
1464 if ((temp_data[i] % 2) || (temp_data[i] < 4)) {
1465 ad->flags[i] |= ADJ_ON_MESH_EDGE;
1466 }
1467
1468 /* reset temp data */
1469 temp_data[i] = 0;
1470 }
1471
1472 /* order n_index array */
1473 int n_pos = 0;
1474 for (int i = 0; i < sData->total_points; i++) {
1475 ad->n_index[i] = n_pos;
1476 n_pos += ad->n_num[i];
1477 }
1478
1479 /* and now add neighbor data using that info */
1480 for (int i = 0; i < numOfEdges; i++) {
1481 /* first vertex */
1482 int index = edges[i][0];
1483 n_pos = ad->n_index[index] + temp_data[index];
1484 ad->n_target[n_pos] = edges[i][1];
1485 temp_data[index]++;
1486
1487 /* second vertex */
1488 index = edges[i][1];
1489 n_pos = ad->n_index[index] + temp_data[index];
1490 ad->n_target[n_pos] = edges[i][0];
1491 temp_data[index]++;
1492 }
1493 }
1494 else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
1495 /* for image sequences, only allocate memory.
1496 * bake initialization takes care of rest */
1497 }
1498
1499 MEM_freeN(temp_data);
1500}
1501
1511
1512static void dynamic_paint_set_init_color_tex_to_vcol_cb(void *__restrict userdata,
1513 const int i,
1514 const TaskParallelTLS *__restrict /*tls*/)
1515{
1516 const DynamicPaintSetInitColorData *data = static_cast<DynamicPaintSetInitColorData *>(userdata);
1517
1518 const PaintSurfaceData *sData = data->surface->data;
1519 PaintPoint *pPoint = (PaintPoint *)sData->type_data;
1520
1521 const blender::Span<int> corner_verts = data->corner_verts;
1522 const blender::Span<int3> corner_tris = data->corner_tris;
1523 const blender::Span<blender::float2> uv_map = data->uv_map;
1524 ImagePool *pool = data->pool;
1525 Tex *tex = data->surface->init_texture;
1526
1527 float uv[3] = {0.0f};
1528
1529 for (int j = 3; j--;) {
1530 TexResult texres = {0};
1531 const int vert = corner_verts[corner_tris[i][j]];
1532
1533 /* remap to [-1.0, 1.0] */
1534 uv[0] = uv_map[corner_tris[i][j]][0] * 2.0f - 1.0f;
1535 uv[1] = uv_map[corner_tris[i][j]][1] * 2.0f - 1.0f;
1536
1537 multitex_ext_safe(tex, uv, &texres, pool, true, false);
1538
1539 if (texres.tin > pPoint[vert].color[3]) {
1540 copy_v3_v3(pPoint[vert].color, texres.trgba);
1541 pPoint[vert].color[3] = texres.tin;
1542 }
1543 }
1544}
1545
1546static void dynamic_paint_set_init_color_tex_to_imseq_cb(void *__restrict userdata,
1547 const int i,
1548 const TaskParallelTLS *__restrict /*tls*/)
1549{
1550 const DynamicPaintSetInitColorData *data = static_cast<DynamicPaintSetInitColorData *>(userdata);
1551
1552 const PaintSurfaceData *sData = data->surface->data;
1553 PaintPoint *pPoint = (PaintPoint *)sData->type_data;
1554
1555 const blender::Span<int3> corner_tris = data->corner_tris;
1556 const blender::Span<blender::float2> uv_map = data->uv_map;
1557 Tex *tex = data->surface->init_texture;
1558 ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data;
1559 const int samples = (data->surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1;
1560
1561 float uv[9] = {0.0f};
1562 float uv_final[3] = {0.0f};
1563
1564 TexResult texres = {0};
1565
1566 /* collect all uvs */
1567 for (int j = 3; j--;) {
1568 copy_v2_v2(&uv[j * 3], uv_map[corner_tris[f_data->uv_p[i].tri_index][j]]);
1569 }
1570
1571 /* interpolate final uv pos */
1572 interp_v3_v3v3v3(uv_final, &uv[0], &uv[3], &uv[6], f_data->barycentricWeights[i * samples].v);
1573 /* remap to [-1.0, 1.0] */
1574 uv_final[0] = uv_final[0] * 2.0f - 1.0f;
1575 uv_final[1] = uv_final[1] * 2.0f - 1.0f;
1576
1577 multitex_ext_safe(tex, uv_final, &texres, nullptr, true, false);
1578
1579 /* apply color */
1580 copy_v3_v3(pPoint[i].color, texres.trgba);
1581 pPoint[i].color[3] = texres.tin;
1582}
1583
1585 void *__restrict userdata, const int i, const TaskParallelTLS *__restrict /*tls*/)
1586{
1587 const DynamicPaintSetInitColorData *data = static_cast<DynamicPaintSetInitColorData *>(userdata);
1588
1589 const PaintSurfaceData *sData = data->surface->data;
1590 PaintPoint *pPoint = (PaintPoint *)sData->type_data;
1591
1592 const blender::Span<int3> corner_tris = data->corner_tris;
1593 const blender::Span<blender::ColorGeometry4b> mloopcol = data->mloopcol;
1594 ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data;
1595 const int samples = (data->surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1;
1596
1597 const int tri_idx = f_data->uv_p[i].tri_index;
1598 float colors[3][4];
1599 float final_color[4];
1600
1601 /* collect color values */
1602 for (int j = 3; j--;) {
1603 rgba_uchar_to_float(colors[j], (const uchar *)&mloopcol[corner_tris[tri_idx][j]].r);
1604 }
1605
1606 /* interpolate final color */
1607 interp_v4_v4v4v4(final_color, UNPACK3(colors), f_data->barycentricWeights[i * samples].v);
1608
1609 copy_v4_v4(pPoint[i].color, final_color);
1610}
1611
1612static void dynamicPaint_setInitialColor(const Scene * /*scene*/, DynamicPaintSurface *surface)
1613{
1614 using namespace blender;
1615 PaintSurfaceData *sData = surface->data;
1616 PaintPoint *pPoint = (PaintPoint *)sData->type_data;
1618 const bke::AttributeAccessor attributes = mesh->attributes();
1619
1620 if (surface->type != MOD_DPAINT_SURFACE_T_PAINT) {
1621 return;
1622 }
1623
1624 if (surface->init_color_type == MOD_DPAINT_INITIAL_NONE) {
1625 return;
1626 }
1627
1628 /* Single color */
1629 if (surface->init_color_type == MOD_DPAINT_INITIAL_COLOR) {
1630 /* apply color to every surface point */
1631 for (int i = 0; i < sData->total_points; i++) {
1632 copy_v4_v4(pPoint[i].color, surface->init_color);
1633 }
1634 }
1635 /* UV mapped texture */
1636 else if (surface->init_color_type == MOD_DPAINT_INITIAL_TEXTURE) {
1637 Tex *tex = surface->init_texture;
1638
1639 const blender::Span<int> corner_verts = mesh->corner_verts();
1640 const blender::Span<int3> corner_tris = mesh->corner_tris();
1641
1642 char uvname[MAX_CUSTOMDATA_LAYER_NAME];
1643
1644 if (!tex) {
1645 return;
1646 }
1647
1648 /* get uv map */
1650 &mesh->corner_data, CD_PROP_FLOAT2, surface->init_layername, uvname);
1651 const VArraySpan uv_map = *attributes.lookup<float2>(uvname, bke::AttrDomain::Corner);
1652
1653 if (uv_map.is_empty()) {
1654 return;
1655 }
1656
1657 /* For vertex surface loop through `corner_tris` and find UV color
1658 * that provides highest alpha. */
1659 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
1660 ImagePool *pool = BKE_image_pool_new();
1661
1663 data.surface = surface;
1664 data.corner_verts = corner_verts;
1665 data.corner_tris = corner_tris;
1666 data.uv_map = uv_map;
1667 data.pool = pool;
1668
1669 TaskParallelSettings settings;
1671 settings.use_threading = (corner_tris.size() > 1000);
1673 0, corner_tris.size(), &data, dynamic_paint_set_init_color_tex_to_vcol_cb, &settings);
1674 BKE_image_pool_free(pool);
1675 }
1676 else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
1678 data.surface = surface;
1679 data.corner_tris = corner_tris;
1680 data.uv_map = uv_map;
1681
1682 TaskParallelSettings settings;
1684 settings.use_threading = (sData->total_points > 1000);
1687 }
1688 }
1689 /* vertex color layer */
1690 else if (surface->init_color_type == MOD_DPAINT_INITIAL_VERTEXCOLOR) {
1691
1692 /* For vertex surface, just copy colors from #MLoopCol. */
1693 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
1694 const blender::Span<int> corner_verts = mesh->corner_verts();
1695 const VArraySpan col = *attributes.lookup<ColorGeometry4b>(surface->init_layername,
1697 if (col.is_empty()) {
1698 return;
1699 }
1700
1701 for (const int i : corner_verts.index_range()) {
1702 rgba_uchar_to_float(pPoint[corner_verts[i]].color, (const uchar *)&col[i].r);
1703 }
1704 }
1705 else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
1706 const blender::Span<int3> corner_tris = mesh->corner_tris();
1707 const VArraySpan col = *attributes.lookup<ColorGeometry4b>(surface->init_layername,
1709 if (col.is_empty()) {
1710 return;
1711 }
1712
1714 data.surface = surface;
1715 data.corner_tris = corner_tris;
1716 data.mloopcol = col;
1717
1718 TaskParallelSettings settings;
1720 settings.use_threading = (sData->total_points > 1000);
1723 }
1724 }
1725}
1726
1728{
1729 PaintSurfaceData *sData = surface->data;
1730 if (sData && sData->type_data) {
1731 uint data_size;
1732
1733 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
1734 data_size = sizeof(PaintPoint);
1735 }
1736 else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
1737 data_size = sizeof(PaintWavePoint);
1738 }
1739 else {
1740 data_size = sizeof(float);
1741 }
1742
1743 memset(sData->type_data, 0, data_size * sData->total_points);
1744
1745 /* set initial color */
1746 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
1747 dynamicPaint_setInitialColor(scene, surface);
1748 }
1749
1750 if (sData->bData) {
1751 sData->bData->clear = 1;
1752 }
1753 }
1754}
1755
1757{
1758 int numOfPoints = dynamicPaint_surfaceNumOfPoints(surface);
1759 /* free existing data */
1760 if (surface->data) {
1762 }
1763
1764 /* don't reallocate for image sequence types. they get handled only on bake */
1765 if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
1766 return true;
1767 }
1768 if (numOfPoints < 1) {
1769 return false;
1770 }
1771
1772 /* allocate memory */
1773 surface->data = MEM_callocN<PaintSurfaceData>(__func__);
1774 if (!surface->data) {
1775 return false;
1776 }
1777
1778 /* allocate data depending on surface type and format */
1779 surface->data->total_points = numOfPoints;
1781 dynamicPaint_initAdjacencyData(surface, false);
1782
1783 /* set initial color */
1784 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
1785 dynamicPaint_setInitialColor(scene, surface);
1786 }
1787
1788 return true;
1789}
1790
1791/* make sure allocated surface size matches current requirements */
1793{
1794 if (!surface->data || (dynamicPaint_surfaceNumOfPoints(surface) != surface->data->total_points))
1795 {
1796 return dynamicPaint_resetSurface(scene, surface);
1797 }
1798 return true;
1799}
1800
1801/***************************** Modifier processing ******************************/
1802
1816
1817static void dynamic_paint_apply_surface_displace_cb(void *__restrict userdata,
1818 const int i,
1819 const TaskParallelTLS *__restrict /*tls*/)
1820{
1822 userdata);
1823
1824 const DynamicPaintSurface *surface = data->surface;
1825
1826 const float *value = (float *)surface->data->type_data;
1827 const float val = value[i] * surface->disp_factor;
1828
1829 madd_v3_v3fl(data->vert_positions[i], data->vert_normals[i], -val);
1830}
1831
1834{
1835 PaintSurfaceData *sData = surface->data;
1836
1837 if (!sData || surface->format != MOD_DPAINT_SURFACE_F_VERTEX) {
1838 return;
1839 }
1840
1841 /* displace paint */
1842 if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) {
1844 data.surface = surface;
1845 data.vert_positions = result->vert_positions_for_write();
1846 data.vert_normals = result->vert_normals();
1847
1848 TaskParallelSettings settings;
1850 settings.use_threading = (sData->total_points > 10000);
1853 }
1854}
1855
1856static void dynamic_paint_apply_surface_vpaint_blend_cb(void *__restrict userdata,
1857 const int i,
1858 const TaskParallelTLS *__restrict /*tls*/)
1859{
1861 userdata);
1862
1863 PaintPoint *pPoint = (PaintPoint *)data->surface->data->type_data;
1864 float (*fcolor)[4] = data->fcolor;
1865
1866 /* blend dry and wet layer */
1868 pPoint[i].color, pPoint[i].color[3], pPoint[i].e_color, pPoint[i].e_color[3], fcolor[i]);
1869}
1870
1871static void dynamic_paint_apply_surface_vpaint_cb(void *__restrict userdata,
1872 const int p_index,
1873 const TaskParallelTLS *__restrict /*tls*/)
1874{
1876 userdata);
1877
1878 const blender::Span<int> corner_verts = data->corner_verts;
1879
1880 const DynamicPaintSurface *surface = data->surface;
1881 PaintPoint *pPoint = (PaintPoint *)surface->data->type_data;
1882 float (*fcolor)[4] = data->fcolor;
1883
1885 blender::MutableSpan<blender::ColorGeometry4b> mloopcol_wet = data->mloopcol_wet;
1886
1887 for (const int l_index : data->faces[p_index]) {
1888 const int v_index = corner_verts[l_index];
1889
1890 /* save layer data to output layer */
1891 /* apply color */
1892 if (!mloopcol.is_empty()) {
1893 rgba_float_to_uchar(mloopcol[l_index], fcolor[v_index]);
1894 }
1895 /* apply wetness */
1896 if (!mloopcol_wet.is_empty()) {
1897 const char c = unit_float_to_uchar_clamp(pPoint[v_index].wetness);
1898 mloopcol_wet[l_index].r = c;
1899 mloopcol_wet[l_index].g = c;
1900 mloopcol_wet[l_index].b = c;
1901 mloopcol_wet[l_index].a = 255;
1902 }
1903 }
1904}
1905
1906static void dynamic_paint_apply_surface_wave_cb(void *__restrict userdata,
1907 const int i,
1908 const TaskParallelTLS *__restrict /*tls*/)
1909{
1911 userdata);
1912
1913 PaintWavePoint *wPoint = (PaintWavePoint *)data->surface->data->type_data;
1914
1915 madd_v3_v3fl(data->vert_positions[i], data->vert_normals[i], wPoint[i].height);
1916}
1917
1922{
1923 using namespace blender;
1924 using namespace blender::bke;
1926
1927 if (pmd->canvas && !(pmd->canvas->flags & MOD_DPAINT_BAKING) &&
1929 {
1930
1931 DynamicPaintSurface *surface;
1932
1933 /* loop through surfaces */
1934 for (surface = static_cast<DynamicPaintSurface *>(pmd->canvas->surfaces.first); surface;
1935 surface = surface->next)
1936 {
1937 PaintSurfaceData *sData = surface->data;
1938
1939 if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ && sData) {
1940 if (!(surface->flags & MOD_DPAINT_ACTIVE)) {
1941 continue;
1942 }
1943
1944 /* process vertex surface previews */
1945 if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
1946
1947 /* vertex color paint */
1948 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
1949 const blender::OffsetIndices faces = result->faces();
1950 const blender::Span<int> corner_verts = result->corner_verts();
1951
1952 /* paint is stored on dry and wet layers, so mix final color first */
1953 float (*fcolor)[4] = MEM_calloc_arrayN<float[4]>(sData->total_points,
1954 "Temp paint color");
1955
1957 data.surface = surface;
1958 data.fcolor = fcolor;
1959
1960 {
1961 TaskParallelSettings settings;
1963 settings.use_threading = (sData->total_points > 1000);
1965 sData->total_points,
1966 &data,
1968 &settings);
1969 }
1970
1971 MutableAttributeAccessor attributes = result->attributes_for_write();
1972
1973 /* paint layer */
1975 if (attributes.lookup_meta_data(surface->output_name) ==
1976 AttributeMetaData{AttrDomain::Corner, AttrType::ColorByte})
1977 {
1978 mloopcol = attributes.lookup_for_write_span<ColorGeometry4b>(surface->output_name);
1979 }
1980 /* if output layer is lost from a constructive modifier, re-add it */
1981 if (!mloopcol && dynamicPaint_outputLayerExists(surface, ob, 0)) {
1982 mloopcol = attributes.lookup_or_add_for_write_span<ColorGeometry4b>(
1984 }
1985
1987 if (attributes.lookup_meta_data(surface->output_name2) ==
1988 AttributeMetaData{AttrDomain::Corner, AttrType::ColorByte})
1989 {
1990 mloopcol_wet = attributes.lookup_for_write_span<ColorGeometry4b>(
1991 surface->output_name2);
1992 }
1993 /* if output layer is lost from a constructive modifier, re-add it */
1994 if (!mloopcol_wet && dynamicPaint_outputLayerExists(surface, ob, 1)) {
1995 mloopcol_wet = attributes.lookup_or_add_for_write_span<ColorGeometry4b>(
1997 }
1998
1999 data.ob = ob;
2000 data.corner_verts = corner_verts;
2001 data.faces = faces;
2002 data.mloopcol = mloopcol.span;
2003 data.mloopcol_wet = mloopcol_wet.span;
2004
2005 {
2006 TaskParallelSettings settings;
2008 settings.use_threading = (faces.size() > 1000);
2010 0, faces.size(), &data, dynamic_paint_apply_surface_vpaint_cb, &settings);
2011 }
2012
2013 mloopcol.finish();
2014 mloopcol_wet.finish();
2015
2016 MEM_freeN(fcolor);
2017 }
2018 /* vertex group paint */
2019 else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
2020 int defgrp_index = BKE_object_defgroup_name_index(ob, surface->output_name);
2021 float *weight = (float *)sData->type_data;
2022
2023 /* apply weights into a vertex group, if doesn't exists add a new layer */
2024 blender::MutableSpan<MDeformVert> dverts = result->deform_verts_for_write();
2025 if (defgrp_index != -1) {
2026 for (int i = 0; i < sData->total_points; i++) {
2027 MDeformVert *dv = &dverts[i];
2028 MDeformWeight *def_weight = BKE_defvert_find_index(dv, defgrp_index);
2029
2030 /* skip if weight value is 0 and no existing weight is found */
2031 if ((def_weight != nullptr) || (weight[i] != 0.0f)) {
2032 /* if not found, add a weight for it */
2033 if (def_weight == nullptr) {
2034 def_weight = BKE_defvert_ensure_index(dv, defgrp_index);
2035 }
2036
2037 /* set weight value */
2038 def_weight->weight = weight[i];
2039 }
2040 }
2041 }
2042 }
2043 /* wave simulation */
2044 else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
2046 data.surface = surface;
2047 data.vert_positions = result->vert_positions_for_write();
2048 data.vert_normals = result->vert_normals();
2049
2050 TaskParallelSettings settings;
2052 settings.use_threading = (sData->total_points > 1000);
2054 0, sData->total_points, &data, dynamic_paint_apply_surface_wave_cb, &settings);
2055 result->tag_positions_changed();
2056 }
2057
2058 /* displace */
2059 if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) {
2061 result->tag_positions_changed();
2062 }
2063 }
2064 }
2065 }
2066 }
2067 /* make a copy of mesh to use as brush data */
2068 else if (pmd->brush && pmd->type == MOD_DYNAMICPAINT_TYPE_BRUSH) {
2070 BLI_assert(runtime_data != nullptr);
2071 std::lock_guard lock(runtime_data->brush_mutex);
2072 if (runtime_data->brush_mesh != nullptr) {
2073 BKE_id_free(nullptr, runtime_data->brush_mesh);
2074 }
2075 runtime_data->brush_mesh = BKE_mesh_copy_for_eval(*result);
2076 }
2077
2078 return result;
2079}
2080
2082{
2083 if (surface->pointcache) {
2084 surface->pointcache->startframe = surface->start_frame;
2085 surface->pointcache->endframe = surface->end_frame;
2086 }
2087}
2088
2090{
2092 if (runtime->canvas_mesh != nullptr) {
2093 BKE_id_free(nullptr, runtime->canvas_mesh);
2094 }
2095
2097}
2098
2099/*
2100 * Updates evaluated-mesh copy and processes dynamic paint step / caches.
2101 */
2103 DynamicPaintModifierData *pmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
2104{
2105 if (pmd->canvas) {
2106 DynamicPaintCanvasSettings *canvas = pmd->canvas;
2107 DynamicPaintSurface *surface = static_cast<DynamicPaintSurface *>(canvas->surfaces.first);
2108
2109 /* update evaluated-mesh copy */
2110 canvas_copyMesh(canvas, mesh);
2111
2112 /* in case image sequence baking, stop here */
2113 if (canvas->flags & MOD_DPAINT_BAKING) {
2114 return;
2115 }
2116
2117 /* loop through surfaces */
2118 for (; surface; surface = surface->next) {
2119 int current_frame = scene->r.cfra;
2120 bool no_surface_data;
2121
2122 /* free bake data if not required anymore */
2123 surface_freeUnusedData(surface);
2124
2125 /* image sequences are handled by bake operator */
2126 if ((surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) ||
2127 !(surface->flags & MOD_DPAINT_ACTIVE))
2128 {
2129 continue;
2130 }
2131
2132 /* make sure surface is valid */
2133 no_surface_data = surface->data == nullptr;
2134 if (!dynamicPaint_checkSurfaceData(scene, surface)) {
2135 continue;
2136 }
2137
2138 /* limit frame range */
2139 CLAMP(current_frame, surface->start_frame, surface->end_frame);
2140
2141 if (no_surface_data || current_frame != surface->current_frame ||
2142 scene->r.cfra == surface->start_frame)
2143 {
2144 PointCache *cache = surface->pointcache;
2145 PTCacheID pid;
2146 surface->current_frame = current_frame;
2147
2148 /* read point cache */
2149 BKE_ptcache_id_from_dynamicpaint(&pid, ob, surface);
2150 pid.cache->startframe = surface->start_frame;
2151 pid.cache->endframe = surface->end_frame;
2152 BKE_ptcache_id_time(&pid, scene, float(scene->r.cfra), nullptr, nullptr, nullptr);
2153
2154 /* reset non-baked cache at first frame */
2155 if (scene->r.cfra == surface->start_frame && !(cache->flag & PTCACHE_BAKED)) {
2156 cache->flag |= PTCACHE_REDO_NEEDED;
2158 cache->flag &= ~PTCACHE_REDO_NEEDED;
2159 }
2160
2161 /* try to read from cache */
2162 bool can_simulate = (scene->r.cfra == current_frame) && !(cache->flag & PTCACHE_BAKED);
2163
2164 if (BKE_ptcache_read(&pid, float(scene->r.cfra), can_simulate)) {
2165 BKE_ptcache_validate(cache, scene->r.cfra);
2166 }
2167 /* if read failed and we're on surface range do recalculate */
2168 else if (can_simulate) {
2169 /* calculate surface frame */
2170 canvas->flags |= MOD_DPAINT_BAKING;
2171 dynamicPaint_calculateFrame(surface, depsgraph, scene, ob, current_frame);
2172 canvas->flags &= ~MOD_DPAINT_BAKING;
2173
2174 /* restore canvas mesh if required */
2175 if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE &&
2176 surface->flags & MOD_DPAINT_DISP_INCREMENTAL && surface->next)
2177 {
2178 canvas_copyMesh(canvas, mesh);
2179 }
2180
2181 BKE_ptcache_validate(cache, surface->current_frame);
2182 BKE_ptcache_write(&pid, surface->current_frame);
2183 }
2184 }
2185 }
2186 }
2187}
2188
2190 DynamicPaintModifierData *pmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
2191{
2192 /* Update canvas data for a new frame */
2193 dynamicPaint_frameUpdate(pmd, depsgraph, scene, ob, mesh);
2194
2195 /* Return output mesh */
2196 return dynamicPaint_Modifier_apply(pmd, ob, mesh);
2197}
2198
2199/* -------------------------------------------------------------------- */
2202
2203/* Create a surface for uv image sequence format. */
2204#define JITTER_SAMPLES \
2205 { \
2206 0.0f, \
2207 0.0f, \
2208 -0.2f, \
2209 -0.4f, \
2210 0.2f, \
2211 0.4f, \
2212 0.4f, \
2213 -0.2f, \
2214 -0.4f, \
2215 0.3f, \
2216 }
2217
2231
2232static void dynamic_paint_create_uv_surface_direct_cb(void *__restrict userdata,
2233 const int ty,
2234 const TaskParallelTLS *__restrict /*tls*/)
2235{
2237 static_cast<const DynamicPaintCreateUVSurfaceData *>(userdata);
2238
2239 const DynamicPaintSurface *surface = data->surface;
2240 PaintUVPoint *tempPoints = data->tempPoints;
2241 Vec3f *tempWeights = data->tempWeights;
2242
2243 const blender::Span<int3> corner_tris = data->corner_tris;
2244 const blender::Span<blender::float2> uv_map = data->uv_map;
2245 const blender::Span<int> corner_verts = data->corner_verts;
2246
2247 const Bounds2D *faceBB = data->faceBB;
2248
2249 const float jitter5sample[10] = JITTER_SAMPLES;
2250 const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1;
2251 const int w = surface->image_resolution;
2252 const int h = w;
2253
2254 for (int tx = 0; tx < w; tx++) {
2255 const int index = tx + w * ty;
2256 PaintUVPoint *tPoint = &tempPoints[index];
2257 float point[5][2];
2258
2259 /* Init per pixel settings */
2260 tPoint->tri_index = -1;
2261 tPoint->neighbor_pixel = -1;
2262 tPoint->pixel_index = index;
2263
2264 /* Actual pixel center, used when collision is found */
2265 point[0][0] = (float(tx) + 0.5f) / w;
2266 point[0][1] = (float(ty) + 0.5f) / h;
2267
2268 /*
2269 * A pixel middle sample isn't enough to find very narrow polygons
2270 * So using 4 samples of each corner too
2271 */
2272 point[1][0] = float(tx) / w;
2273 point[1][1] = float(ty) / h;
2274
2275 point[2][0] = (float(tx) + 1) / w;
2276 point[2][1] = float(ty) / h;
2277
2278 point[3][0] = float(tx) / w;
2279 point[3][1] = (float(ty) + 1) / h;
2280
2281 point[4][0] = (float(tx) + 1) / w;
2282 point[4][1] = (float(ty) + 1) / h;
2283
2284 /* Loop through samples, starting from middle point */
2285 for (int sample = 0; sample < 5; sample++) {
2286 /* Loop through every face in the mesh */
2287 /* XXX TODO: This is *horrible* with big meshes, should use a 2D BVHTree over UV tris here!
2288 */
2289 for (const int i : corner_tris.index_range()) {
2290 /* Check uv bb */
2291 if ((faceBB[i].min[0] > point[sample][0]) || (faceBB[i].min[1] > point[sample][1]) ||
2292 (faceBB[i].max[0] < point[sample][0]) || (faceBB[i].max[1] < point[sample][1]))
2293 {
2294 continue;
2295 }
2296
2297 const float *uv1 = uv_map[corner_tris[i][0]];
2298 const float *uv2 = uv_map[corner_tris[i][1]];
2299 const float *uv3 = uv_map[corner_tris[i][2]];
2300
2301 /* If point is inside the face */
2302 if (isect_point_tri_v2(point[sample], uv1, uv2, uv3) != 0) {
2303 float uv[2];
2304
2305 /* Add b-weights per anti-aliasing sample */
2306 for (int j = 0; j < aa_samples; j++) {
2307 uv[0] = point[0][0] + jitter5sample[j * 2] / w;
2308 uv[1] = point[0][1] + jitter5sample[j * 2 + 1] / h;
2309
2310 barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v);
2311 }
2312
2313 /* Set surface point face values */
2314 tPoint->tri_index = i;
2315
2316 /* save vertex indexes */
2317 tPoint->v1 = corner_verts[corner_tris[i][0]];
2318 tPoint->v2 = corner_verts[corner_tris[i][1]];
2319 tPoint->v3 = corner_verts[corner_tris[i][2]];
2320
2321 sample = 5; /* make sure we exit sample loop as well */
2322 break;
2323 }
2324 }
2325 }
2326 }
2327}
2328
2329static void dynamic_paint_create_uv_surface_neighbor_cb(void *__restrict userdata,
2330 const int ty,
2331 const TaskParallelTLS *__restrict /*tls*/)
2332{
2334 static_cast<const DynamicPaintCreateUVSurfaceData *>(userdata);
2335
2336 const DynamicPaintSurface *surface = data->surface;
2337 PaintUVPoint *tempPoints = data->tempPoints;
2338 Vec3f *tempWeights = data->tempWeights;
2339
2340 const blender::Span<int3> corner_tris = data->corner_tris;
2341 const blender::Span<blender::float2> uv_map = data->uv_map;
2342 const blender::Span<int> corner_verts = data->corner_verts;
2343
2344 uint32_t *active_points = data->active_points;
2345
2346 const float jitter5sample[10] = JITTER_SAMPLES;
2347 const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1;
2348 const int w = surface->image_resolution;
2349 const int h = w;
2350
2351 for (int tx = 0; tx < w; tx++) {
2352 const int index = tx + w * ty;
2353 PaintUVPoint *tPoint = &tempPoints[index];
2354
2355 /* If point isn't on canvas mesh */
2356 if (tPoint->tri_index == -1) {
2357 float point[2];
2358
2359 /* get loop area */
2360 const int u_min = (tx > 0) ? -1 : 0;
2361 const int u_max = (tx < (w - 1)) ? 1 : 0;
2362 const int v_min = (ty > 0) ? -1 : 0;
2363 const int v_max = (ty < (h - 1)) ? 1 : 0;
2364
2365 point[0] = (float(tx) + 0.5f) / w;
2366 point[1] = (float(ty) + 0.5f) / h;
2367
2368 /* search through defined area for neighbor, checking grid directions first */
2369 for (int ni = 0; ni < 8; ni++) {
2370 int u = neighStraightX[ni];
2371 int v = neighStraightY[ni];
2372
2373 if (u >= u_min && u <= u_max && v >= v_min && v <= v_max) {
2374 /* if not this pixel itself */
2375 if (u != 0 || v != 0) {
2376 const int ind = (tx + u) + w * (ty + v);
2377
2378 /* if neighbor has index */
2379 if (tempPoints[ind].neighbor_pixel == -1 && tempPoints[ind].tri_index != -1) {
2380 float uv[2];
2381 const int i = tempPoints[ind].tri_index;
2382 const float *uv1 = uv_map[corner_tris[i][0]];
2383 const float *uv2 = uv_map[corner_tris[i][1]];
2384 const float *uv3 = uv_map[corner_tris[i][2]];
2385
2386 /* tri index */
2387 /* There is a low possibility of actually having a neighbor point which tri is
2388 * already set from another neighbor in a separate thread here.
2389 * Checking for both tri_index and neighbor_pixel above reduces that probability
2390 * but it remains possible.
2391 * That atomic op (and its memory fence) ensures tPoint->neighbor_pixel is set
2392 * to non--1 *before* its tri_index is set (i.e. that it cannot be used a neighbor).
2393 */
2394 tPoint->neighbor_pixel = ind - 1;
2396 tPoint->tri_index = i;
2397
2398 /* Now calculate pixel data for this pixel as it was on face surface */
2399 /* Add b-weights per anti-aliasing sample */
2400 for (int j = 0; j < aa_samples; j++) {
2401 uv[0] = point[0] + jitter5sample[j * 2] / w;
2402 uv[1] = point[1] + jitter5sample[j * 2 + 1] / h;
2403 barycentric_weights_v2(uv1, uv2, uv3, uv, tempWeights[index * aa_samples + j].v);
2404 }
2405
2406 /* save vertex indexes */
2407 tPoint->v1 = corner_verts[corner_tris[i][0]];
2408 tPoint->v2 = corner_verts[corner_tris[i][1]];
2409 tPoint->v3 = corner_verts[corner_tris[i][2]];
2410
2411 break;
2412 }
2413 }
2414 }
2415 }
2416 }
2417
2418 /* Increase the final number of active surface points if relevant. */
2419 if (tPoint->tri_index != -1) {
2420 atomic_add_and_fetch_uint32(active_points, 1);
2421 }
2422 }
2423}
2424
2425#undef JITTER_SAMPLES
2426
2428 const blender::Span<blender::float2> uv_map,
2429 int tri_index,
2430 const float point[2])
2431{
2432 BLI_assert(tri_index >= 0);
2433
2434 float min_distance = FLT_MAX;
2435
2436 for (int i = 0; i < 3; i++) {
2437 const float dist_squared = dist_squared_to_line_segment_v2(
2438 point,
2439 uv_map[corner_tris[tri_index][(i + 0)]],
2440 uv_map[corner_tris[tri_index][(i + 1) % 3]]);
2441
2442 min_distance = std::min(dist_squared, min_distance);
2443 }
2444
2445 return min_distance;
2446}
2447
2455
2458 int tri_index,
2459 const float pixel[2],
2460 int in_edge,
2461 int depth);
2462
2463/* Tries to find the neighboring pixel in given (uv space) direction.
2464 * Result is used by effect system to move paint on the surface.
2465 *
2466 * px, py : origin pixel x and y
2467 * n_index : lookup direction index (use neighX, neighY to get final index)
2468 */
2470 const MeshElemMap *vert_to_tri_map,
2471 const int w,
2472 const int h,
2473 const int px,
2474 const int py,
2475 const int n_index)
2476{
2477 /* NOTE: Current method only uses face edges to detect neighboring pixels.
2478 * -> It doesn't always lead to the optimum pixel but is accurate enough
2479 * and faster/simpler than including possible face tip point links)
2480 */
2481
2482 /* shift position by given n_index */
2483 const int x = px + neighX[n_index];
2484 const int y = py + neighY[n_index];
2485
2486 if (x < 0 || x >= w || y < 0 || y >= h) {
2487 return OUT_OF_TEXTURE;
2488 }
2489
2490 const PaintUVPoint *tempPoints = data->tempPoints;
2491 const PaintUVPoint *tPoint = &tempPoints[x + w * y]; /* UV neighbor */
2492 const PaintUVPoint *cPoint = &tempPoints[px + w * py]; /* Origin point */
2493
2494 /* Check if shifted point is on same face -> it's a correct neighbor
2495 * (and if it isn't marked as an "edge pixel") */
2496 if ((tPoint->tri_index == cPoint->tri_index) && (tPoint->neighbor_pixel == -1)) {
2497 return (x + w * y);
2498 }
2499
2500 /* Even if shifted point is on another face
2501 * -> use this point.
2502 *
2503 * !! Replace with "is uv faces linked" check !!
2504 * This should work fine as long as uv island margin is > 1 pixel.
2505 */
2506 if ((tPoint->tri_index != -1) && (tPoint->neighbor_pixel == -1)) {
2507 return (x + w * y);
2508 }
2509
2510 /* If we get here, the actual neighboring pixel is located on a non-linked uv face,
2511 * and we have to find its "real" position.
2512 *
2513 * Simple neighboring face finding algorithm:
2514 * - find closest uv edge to shifted pixel and get the another face that shares that edge
2515 * - find corresponding position of that new face edge in uv space
2516 *
2517 * TODO: Implement something more accurate / optimized?
2518 */
2519 {
2521 bdata.vert_to_tri_map = vert_to_tri_map;
2522 bdata.w = w;
2523 bdata.h = h;
2524 bdata.px = px;
2525 bdata.py = py;
2526 bdata.best_index = NOT_FOUND;
2527 bdata.best_weight = 1.0f;
2528
2529 float pixel[2];
2530
2531 pixel[0] = (float(px + neighX[n_index]) + 0.5f) / float(w);
2532 pixel[1] = (float(py + neighY[n_index]) + 0.5f) / float(h);
2533
2534 /* Do a small recursive search for the best island edge. */
2535 dynamic_paint_find_island_border(data, &bdata, cPoint->tri_index, pixel, -1, 5);
2536
2537 return bdata.best_index;
2538 }
2539}
2540
2543 int tri_index,
2544 const float pixel[2],
2545 int in_edge,
2546 int depth)
2547{
2548 const blender::Span<int> corner_verts = data->corner_verts;
2549 const blender::Span<int3> corner_tris = data->corner_tris;
2550 const blender::Span<blender::float2> uv_map = data->uv_map;
2551
2552 const int3 loop_idx = corner_tris[tri_index];
2553
2554 /* Enumerate all edges of the triangle, rotating the vertex list accordingly. */
2555 for (int edge_idx = 0; edge_idx < 3; edge_idx++) {
2556 /* but not the edge we have just recursed through */
2557 if (edge_idx == in_edge) {
2558 continue;
2559 }
2560
2561 float uv0[2], uv1[2], uv2[2];
2562
2563 copy_v2_v2(uv0, uv_map[loop_idx[(edge_idx + 0)]]);
2564 copy_v2_v2(uv1, uv_map[loop_idx[(edge_idx + 1) % 3]]);
2565 copy_v2_v2(uv2, uv_map[loop_idx[(edge_idx + 2) % 3]]);
2566
2567 /* Verify the target point is on the opposite side of the edge from the third triangle
2568 * vertex, to ensure that we always move closer to the goal point. */
2569 const float sidep = line_point_side_v2(uv0, uv1, pixel);
2570 const float side2 = line_point_side_v2(uv0, uv1, uv2);
2571
2572 if (side2 == 0.0f) {
2573 continue;
2574 }
2575
2576 /* Hack: allow all edges of the original triangle */
2577 const bool correct_side = (in_edge == -1) || (sidep < 0 && side2 > 0) ||
2578 (sidep > 0 && side2 < 0);
2579
2580 /* Allow exactly on edge for the non-recursive case */
2581 if (!correct_side && sidep != 0.0f) {
2582 continue;
2583 }
2584
2585 /* Now find another face that is linked to that edge. */
2586 const int vert0 = corner_verts[loop_idx[(edge_idx + 0)]];
2587 const int vert1 = corner_verts[loop_idx[(edge_idx + 1) % 3]];
2588
2589 /* Use a pre-computed vert-to-corner_tri mapping,
2590 * speeds up things a lot compared to looping over all corner_tris. */
2591 const MeshElemMap *map = &bdata->vert_to_tri_map[vert0];
2592
2593 bool found_other = false;
2594 int target_tri = -1;
2595 int target_edge = -1;
2596
2597 float ouv0[2], ouv1[2];
2598
2599 for (int i = 0; i < map->count && !found_other; i++) {
2600 const int tri_other_index = map->indices[i];
2601
2602 if (tri_other_index == tri_index) {
2603 continue;
2604 }
2605
2606 const int3 other_tri = corner_tris[tri_other_index];
2607
2608 /* Check edges for match, looping in the same order as the outer loop. */
2609 for (int j = 0; j < 3; j++) {
2610 const int overt0 = corner_verts[other_tri[(j + 0)]];
2611 const int overt1 = corner_verts[other_tri[(j + 1) % 3]];
2612
2613 /* Allow for swapped vertex order */
2614 if (overt0 == vert0 && overt1 == vert1) {
2615 found_other = true;
2616 copy_v2_v2(ouv0, uv_map[other_tri[(j + 0)]]);
2617 copy_v2_v2(ouv1, uv_map[other_tri[(j + 1) % 3]]);
2618 }
2619 else if (overt0 == vert1 && overt1 == vert0) {
2620 found_other = true;
2621 copy_v2_v2(ouv1, uv_map[other_tri[(j + 0)]]);
2622 copy_v2_v2(ouv0, uv_map[other_tri[(j + 1) % 3]]);
2623 }
2624
2625 if (found_other) {
2626 target_tri = tri_other_index;
2627 target_edge = j;
2628 break;
2629 }
2630 }
2631 }
2632
2633 if (!found_other) {
2634 if (bdata->best_index < 0) {
2635 bdata->best_index = ON_MESH_EDGE;
2636 }
2637
2638 continue;
2639 }
2640
2641 /* If this edge is connected in UV space too, recurse */
2642 if (equals_v2v2(uv0, ouv0) && equals_v2v2(uv1, ouv1)) {
2643 if (depth > 0 && correct_side) {
2644 dynamic_paint_find_island_border(data, bdata, target_tri, pixel, target_edge, depth - 1);
2645 }
2646
2647 continue;
2648 }
2649
2650 /* Otherwise try to map to the other side of the edge.
2651 * First check if there already is a better solution. */
2652 const float dist_squared = dist_squared_to_line_segment_v2(pixel, uv0, uv1);
2653
2654 if (bdata->best_index >= 0 && dist_squared >= bdata->best_weight) {
2655 continue;
2656 }
2657
2658 /*
2659 * Find a point that is relatively at same edge position
2660 * on this other face UV
2661 */
2662 float closest_point[2], dir_vec[2], tgt_pixel[2];
2663
2664 float lambda = closest_to_line_v2(closest_point, pixel, uv0, uv1);
2665 CLAMP(lambda, 0.0f, 1.0f);
2666
2667 sub_v2_v2v2(dir_vec, ouv1, ouv0);
2668 madd_v2_v2v2fl(tgt_pixel, ouv0, dir_vec, lambda);
2669
2670 int w = bdata->w, h = bdata->h, px = bdata->px, py = bdata->py;
2671
2672 const int final_pixel[2] = {int(floorf(tgt_pixel[0] * w)), int(floorf(tgt_pixel[1] * h))};
2673
2674 /* If current pixel uv is outside of texture */
2675 if (final_pixel[0] < 0 || final_pixel[0] >= w || final_pixel[1] < 0 || final_pixel[1] >= h) {
2676 if (bdata->best_index == NOT_FOUND) {
2677 bdata->best_index = OUT_OF_TEXTURE;
2678 }
2679
2680 continue;
2681 }
2682
2683 const PaintUVPoint *tempPoints = data->tempPoints;
2684 int final_index = final_pixel[0] + w * final_pixel[1];
2685
2686 /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */
2687 if (final_index == (px + w * py)) {
2688 continue;
2689 }
2690
2691 /* If final point is an "edge pixel", use its "real" neighbor instead */
2692 if (tempPoints[final_index].neighbor_pixel != -1) {
2693 final_index = tempPoints[final_index].neighbor_pixel;
2694
2695 /* If we ended up to our origin point */
2696 if (final_index == (px + w * py)) {
2697 continue;
2698 }
2699 }
2700
2701 const int final_tri_index = tempPoints[final_index].tri_index;
2702 /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */
2703 if (!ELEM(final_tri_index, target_tri, -1)) {
2704 /* Check if it's close enough to likely touch the intended triangle. Any triangle
2705 * becomes thinner than a pixel at its vertices, so robustness requires some margin. */
2706 const float final_pt[2] = {((final_index % w) + 0.5f) / w, ((final_index / w) + 0.5f) / h};
2707 const float threshold = square_f(0.7f) / (w * h);
2708
2709 if (dist_squared_to_corner_tris_uv_edges(corner_tris, uv_map, final_tri_index, final_pt) >
2710 threshold)
2711 {
2712 continue;
2713 }
2714 }
2715
2716 bdata->best_index = final_index;
2717 bdata->best_weight = dist_squared;
2718 }
2719}
2720
2721static bool dynamicPaint_pointHasNeighbor(PaintAdjData *ed, int index, int neighbor)
2722{
2723 const int idx = ed->n_index[index];
2724
2725 for (int i = 0; i < ed->n_num[index]; i++) {
2726 if (ed->n_target[idx + i] == neighbor) {
2727 return true;
2728 }
2729 }
2730
2731 return false;
2732}
2733
2734/* Makes the adjacency data symmetric, except for border pixels.
2735 * I.e. if A is neighbor of B, B is neighbor of A. */
2736static bool dynamicPaint_symmetrizeAdjData(PaintAdjData *ed, int active_points)
2737{
2738 int *new_n_index = MEM_calloc_arrayN<int>(active_points, "Surface Adj Index");
2739 int *new_n_num = MEM_calloc_arrayN<int>(active_points, "Surface Adj Counts");
2740
2741 if (new_n_num && new_n_index) {
2742 /* Count symmetrized neighbors */
2743 int total_targets = 0;
2744
2745 for (int index = 0; index < active_points; index++) {
2746 total_targets += ed->n_num[index];
2747 new_n_num[index] = ed->n_num[index];
2748 }
2749
2750 for (int index = 0; index < active_points; index++) {
2751 if (ed->flags[index] & ADJ_BORDER_PIXEL) {
2752 continue;
2753 }
2754
2755 for (int i = 0, idx = ed->n_index[index]; i < ed->n_num[index]; i++) {
2756 const int target = ed->n_target[idx + i];
2757
2758 BLI_assert(!(ed->flags[target] & ADJ_BORDER_PIXEL));
2759
2760 if (!dynamicPaint_pointHasNeighbor(ed, target, index)) {
2761 new_n_num[target]++;
2762 total_targets++;
2763 }
2764 }
2765 }
2766
2767 /* Allocate a new target map */
2768 int *new_n_target = MEM_calloc_arrayN<int>(total_targets, "Surface Adj Targets");
2769
2770 if (new_n_target) {
2771 /* Copy existing neighbors to the new map */
2772 int n_pos = 0;
2773
2774 for (int index = 0; index < active_points; index++) {
2775 new_n_index[index] = n_pos;
2776 memcpy(&new_n_target[n_pos],
2777 &ed->n_target[ed->n_index[index]],
2778 sizeof(int) * ed->n_num[index]);
2779
2780 /* Reset count to old, but advance position by new, leaving a gap to fill below. */
2781 n_pos += new_n_num[index];
2782 new_n_num[index] = ed->n_num[index];
2783 }
2784
2785 BLI_assert(n_pos == total_targets);
2786
2787 /* Add symmetrized - this loop behavior must exactly match the count pass above */
2788 for (int index = 0; index < active_points; index++) {
2789 if (ed->flags[index] & ADJ_BORDER_PIXEL) {
2790 continue;
2791 }
2792
2793 for (int i = 0, idx = ed->n_index[index]; i < ed->n_num[index]; i++) {
2794 const int target = ed->n_target[idx + i];
2795
2796 if (!dynamicPaint_pointHasNeighbor(ed, target, index)) {
2797 const int num = new_n_num[target]++;
2798 new_n_target[new_n_index[target] + num] = index;
2799 }
2800 }
2801 }
2802
2803 /* Swap maps */
2804 MEM_freeN(ed->n_target);
2805 ed->n_target = new_n_target;
2806
2807 MEM_freeN(ed->n_index);
2808 ed->n_index = new_n_index;
2809
2810 MEM_freeN(ed->n_num);
2811 ed->n_num = new_n_num;
2812
2813 ed->total_targets = total_targets;
2814 return true;
2815 }
2816 }
2817
2818 if (new_n_index) {
2819 MEM_freeN(new_n_index);
2820 }
2821 if (new_n_num) {
2822 MEM_freeN(new_n_num);
2823 }
2824
2825 return false;
2826}
2827
2829 DynamicPaintSurface *surface,
2830 float *progress,
2831 bool *do_update)
2832{
2833 using namespace blender;
2834 /* Anti-alias jitter point relative coords. */
2835 const int aa_samples = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1;
2836 char uvname[MAX_CUSTOMDATA_LAYER_NAME];
2837 uint32_t active_points = 0;
2838 bool error = false;
2839
2840 PaintSurfaceData *sData;
2841 DynamicPaintCanvasSettings *canvas = surface->canvas;
2843
2844 PaintUVPoint *tempPoints = nullptr;
2845 Vec3f *tempWeights = nullptr;
2846 VArraySpan<float2> uv_map;
2847
2848 Bounds2D *faceBB = nullptr;
2849 int *final_index;
2850
2851 *progress = 0.0f;
2852 *do_update = true;
2853
2854 if (!mesh) {
2855 return setError(canvas, N_("Canvas mesh not updated"));
2856 }
2857 if (surface->format != MOD_DPAINT_SURFACE_F_IMAGESEQ) {
2858 return setError(canvas, N_("Cannot bake non-'image sequence' formats"));
2859 }
2860
2861 const blender::Span<int> corner_verts = mesh->corner_verts();
2862 const blender::Span<int3> corner_tris = mesh->corner_tris();
2863
2864 /* get uv map */
2865 if (CustomData_has_layer(&mesh->corner_data, CD_PROP_FLOAT2)) {
2867 &mesh->corner_data, CD_PROP_FLOAT2, surface->uvlayer_name, uvname);
2868 const bke::AttributeAccessor attributes = mesh->attributes();
2869 uv_map = *attributes.lookup<float2>(uvname, bke::AttrDomain::Corner);
2870 }
2871
2872 /* Check for validity */
2873 if (uv_map.is_empty()) {
2874 return setError(canvas, N_("No UV data on canvas"));
2875 }
2876 if (surface->image_resolution < 16 || surface->image_resolution > 8192) {
2877 return setError(canvas, N_("Invalid resolution"));
2878 }
2879
2880 const int w = surface->image_resolution;
2881 const int h = w;
2882
2883 /*
2884 * Start generating the surface
2885 */
2886 CLOG_DEBUG(
2887 &LOG, "Preparing UV surface of %ix%i pixels and %i tris.", w, h, int(corner_tris.size()));
2888
2889 /* Init data struct */
2890 if (surface->data) {
2892 }
2893 sData = surface->data = MEM_callocN<PaintSurfaceData>(__func__);
2894 if (!surface->data) {
2895 return setError(canvas, N_("Not enough free memory"));
2896 }
2897
2898 tempPoints = MEM_calloc_arrayN<PaintUVPoint>(size_t(w) * size_t(h), "Temp PaintUVPoint");
2899 if (!tempPoints) {
2900 error = true;
2901 }
2902
2903 final_index = MEM_calloc_arrayN<int>(size_t(w) * size_t(h), "Temp UV Final Indexes");
2904 if (!final_index) {
2905 error = true;
2906 }
2907
2908 tempWeights = MEM_malloc_arrayN<Vec3f>(size_t(w) * size_t(h) * size_t(aa_samples),
2909 "Temp bWeights");
2910 if (!tempWeights) {
2911 error = true;
2912 }
2913
2914 /*
2915 * Generate a temporary bounding box array for UV faces to optimize
2916 * the pixel-inside-a-face search.
2917 */
2918 if (!error) {
2919 faceBB = MEM_malloc_arrayN<Bounds2D>(size_t(corner_tris.size()), "MPCanvasFaceBB");
2920 if (!faceBB) {
2921 error = true;
2922 }
2923 }
2924
2925 *progress = 0.01f;
2926 *do_update = true;
2927
2928 if (!error) {
2929 for (const int i : corner_tris.index_range()) {
2930 copy_v2_v2(faceBB[i].min, uv_map[corner_tris[i][0]]);
2931 copy_v2_v2(faceBB[i].max, uv_map[corner_tris[i][0]]);
2932
2933 for (int j = 1; j < 3; j++) {
2934 minmax_v2v2_v2(faceBB[i].min, faceBB[i].max, uv_map[corner_tris[i][j]]);
2935 }
2936 }
2937
2938 *progress = 0.02f;
2939 *do_update = true;
2940
2941 /* Loop through every pixel and check if pixel is uv-mapped on a canvas face. */
2943 data.surface = surface;
2944 data.tempPoints = tempPoints;
2945 data.tempWeights = tempWeights;
2946 data.corner_tris = corner_tris;
2947 data.uv_map = uv_map;
2948 data.corner_verts = corner_verts;
2949 data.faceBB = faceBB;
2950
2951 {
2952 TaskParallelSettings settings;
2954 settings.use_threading = (h > 64 || corner_tris.size() > 1000);
2956 }
2957
2958 *progress = 0.04f;
2959 *do_update = true;
2960
2961 /*
2962 * Now loop through every pixel that was left without index
2963 * and find if they have neighboring pixels that have an index.
2964 * If so use that face as pixel surface.
2965 * (To avoid seams on uv island edges)
2966 */
2967 data.active_points = &active_points;
2968 {
2969 TaskParallelSettings settings;
2971 settings.use_threading = (h > 64);
2973 }
2974
2975 *progress = 0.06f;
2976 *do_update = true;
2977
2978 /* Generate surface adjacency data. */
2979 {
2980 int cursor = 0;
2981
2982 /* Create a temporary array of final indexes (before unassigned
2983 * pixels have been dropped) */
2984 for (int i = 0; i < w * h; i++) {
2985 if (tempPoints[i].tri_index != -1) {
2986 final_index[i] = cursor;
2987 cursor++;
2988 }
2989 }
2990 /* allocate memory */
2991 sData->total_points = w * h;
2992 dynamicPaint_initAdjacencyData(surface, true);
2993
2994 if (sData->adj_data) {
2995 PaintAdjData *ed = sData->adj_data;
2996 int n_pos = 0;
2997
2998 MeshElemMap *vert_to_tri_map;
2999 int *vert_to_tri_map_mem;
3000
3001 BKE_mesh_vert_corner_tri_map_create(&vert_to_tri_map,
3002 &vert_to_tri_map_mem,
3003 mesh->verts_num,
3004 corner_tris.data(),
3005 corner_tris.size(),
3006 corner_verts.data(),
3007 mesh->corners_num);
3008
3009 int total_border = 0;
3010
3011 for (int ty = 0; ty < h; ty++) {
3012 for (int tx = 0; tx < w; tx++) {
3013 const int index = tx + w * ty;
3014
3015 if (tempPoints[index].tri_index != -1) {
3016 ed->n_index[final_index[index]] = n_pos;
3017 ed->n_num[final_index[index]] = 0;
3018
3019 if (tempPoints[index].neighbor_pixel != -1) {
3020 ed->flags[final_index[index]] |= ADJ_BORDER_PIXEL;
3021 total_border++;
3022 }
3023
3024 for (int i = 0; i < 8; i++) {
3025 /* Try to find a neighboring pixel in defined direction.
3026 * If not found, -1 is returned */
3027 const int n_target = dynamic_paint_find_neighbor_pixel(
3028 &data, vert_to_tri_map, w, h, tx, ty, i);
3029
3030 if (n_target >= 0 && n_target != index) {
3032 ed, final_index[index], final_index[n_target]))
3033 {
3034 ed->n_target[n_pos] = final_index[n_target];
3035 ed->n_num[final_index[index]]++;
3036 n_pos++;
3037 }
3038 }
3039 else if (ELEM(n_target, ON_MESH_EDGE, OUT_OF_TEXTURE)) {
3040 ed->flags[final_index[index]] |= ADJ_ON_MESH_EDGE;
3041 }
3042 }
3043 }
3044 }
3045 }
3046
3047 MEM_freeN(vert_to_tri_map);
3048 MEM_freeN(vert_to_tri_map_mem);
3049
3050 /* Make neighbors symmetric */
3051 if (!dynamicPaint_symmetrizeAdjData(ed, active_points)) {
3052 error = true;
3053 }
3054
3055 /* Create a list of border pixels */
3056 ed->border = MEM_calloc_arrayN<int>(total_border, "Border Pixel Index");
3057
3058 if (ed->border) {
3059 ed->total_border = total_border;
3060
3061 for (int i = 0, next = 0; i < active_points; i++) {
3062 if (ed->flags[i] & ADJ_BORDER_PIXEL) {
3063 ed->border[next++] = i;
3064 }
3065 }
3066 }
3067
3068#if 0
3069 /* -----------------------------------------------------------------
3070 * For debug, write a dump of adjacency data to a file.
3071 * ----------------------------------------------------------------- */
3072 FILE *dump_file = fopen("dynpaint-adj-data.txt", "w");
3073 int *tmp = MEM_calloc_arrayN<int>(active_points, "tmp");
3074 for (int ty = 0; ty < h; ty++) {
3075 for (int tx = 0; tx < w; tx++) {
3076 const int index = tx + w * ty;
3077 if (tempPoints[index].tri_index != -1) {
3078 tmp[final_index[index]] = index;
3079 }
3080 }
3081 }
3082 for (int ty = 0; ty < h; ty++) {
3083 for (int tx = 0; tx < w; tx++) {
3084 const int index = tx + w * ty;
3085 const int fidx = final_index[index];
3086
3087 if (tempPoints[index].tri_index != -1) {
3088 int nidx = tempPoints[index].neighbor_pixel;
3089 fprintf(dump_file,
3090 "%d\t%d,%d\t%u\t%d,%d\t%d\t",
3091 fidx,
3092 tx,
3093 h - 1 - ty,
3094 tempPoints[index].tri_index,
3095 nidx < 0 ? -1 : (nidx % w),
3096 nidx < 0 ? -1 : h - 1 - (nidx / w),
3097 ed->flags[fidx]);
3098 for (int i = 0; i < ed->n_num[fidx]; i++) {
3099 int tgt = tmp[ed->n_target[ed->n_index[fidx] + i]];
3100 fprintf(dump_file, "%s%d,%d", i ? " " : "", tgt % w, h - 1 - tgt / w);
3101 }
3102 fprintf(dump_file, "\n");
3103 }
3104 }
3105 }
3106 MEM_freeN(tmp);
3107 fclose(dump_file);
3108#endif
3109 }
3110 }
3111
3112 *progress = 0.08f;
3113 *do_update = true;
3114
3115 /* Create final surface data without inactive points */
3117 if (f_data) {
3118 f_data->uv_p = MEM_calloc_arrayN<PaintUVPoint>(active_points, "PaintUVPoint");
3119 f_data->barycentricWeights = MEM_calloc_arrayN<Vec3f>(active_points * size_t(aa_samples),
3120 "PaintUVPoint");
3121
3122 if (!f_data->uv_p || !f_data->barycentricWeights) {
3123 error = true;
3124 }
3125 }
3126 else {
3127 error = true;
3128 }
3129
3130 /* in case of allocation error, free everything */
3131 if (error) {
3132 if (f_data) {
3133 if (f_data->uv_p) {
3134 MEM_freeN(f_data->uv_p);
3135 }
3136 if (f_data->barycentricWeights) {
3138 }
3139 MEM_freeN(f_data);
3140 }
3141 sData->total_points = 0;
3142 }
3143 else {
3144 sData->total_points = int(active_points);
3145 sData->format_data = f_data;
3146
3147 for (int index = 0, cursor = 0; index < (w * h); index++) {
3148 if (tempPoints[index].tri_index != -1) {
3149 memcpy(&f_data->uv_p[cursor], &tempPoints[index], sizeof(PaintUVPoint));
3150 memcpy(&f_data->barycentricWeights[cursor * aa_samples],
3151 &tempWeights[index * aa_samples],
3152 sizeof(*tempWeights) * aa_samples);
3153 cursor++;
3154 }
3155 }
3156 }
3157 }
3158 if (error == 1) {
3159 setError(canvas, N_("Not enough free memory"));
3160 }
3161
3162 if (faceBB) {
3163 MEM_freeN(faceBB);
3164 }
3165 if (tempPoints) {
3166 MEM_freeN(tempPoints);
3167 }
3168 if (tempWeights) {
3169 MEM_freeN(tempWeights);
3170 }
3171 if (final_index) {
3172 MEM_freeN(final_index);
3173 }
3174
3175 /* Init surface type data */
3176 if (!error) {
3178
3179#if 0
3180 /* -----------------------------------------------------------------
3181 * For debug, output pixel statuses to the color map
3182 * ----------------------------------------------------------------- */
3183 for (index = 0; index < sData->total_points; index++) {
3184 ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data;
3185 PaintUVPoint *uvPoint = &((PaintUVPoint *)f_data->uv_p)[index];
3186 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
3187 pPoint->alpha = 1.0f;
3188
3189 /* Every pixel that is assigned as "edge pixel" gets blue color */
3190 if (uvPoint->neighbor_pixel != -1) {
3191 pPoint->color[2] = 1.0f;
3192 }
3193 /* and every pixel that finally got an face gets red color */
3194 /* green color shows pixel face index hash */
3195 if (uvPoint->tri_index != -1) {
3196 pPoint->color[0] = 1.0f;
3197 pPoint->color[1] = float(uvPoint->tri_index % 255) / 256.0f;
3198 }
3199 }
3200#endif
3201
3202 dynamicPaint_setInitialColor(scene, surface);
3203 }
3204
3205 *progress = 0.09f;
3206 *do_update = true;
3207
3208 return (error == 0);
3209}
3210
3211/*
3212 * Outputs an image file from uv surface data.
3213 */
3218
3219static void dynamic_paint_output_surface_image_paint_cb(void *__restrict userdata,
3220 const int index,
3221 const TaskParallelTLS *__restrict /*tls*/)
3222{
3224 static_cast<const DynamicPaintOutputSurfaceImageData *>(userdata);
3225
3226 const DynamicPaintSurface *surface = data->surface;
3227 const PaintPoint *point = &((PaintPoint *)surface->data->type_data)[index];
3228
3229 ImBuf *ibuf = data->ibuf;
3230 /* image buffer position */
3231 const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4;
3232
3233 /* blend wet and dry layers */
3234 blendColors(point->color,
3235 point->color[3],
3236 point->e_color,
3237 point->e_color[3],
3238 &ibuf->float_buffer.data[pos]);
3239
3240 /* Multiply color by alpha if enabled */
3241 if (surface->flags & MOD_DPAINT_MULALPHA) {
3242 mul_v3_fl(&ibuf->float_buffer.data[pos], ibuf->float_buffer.data[pos + 3]);
3243 }
3244}
3245
3247 void *__restrict userdata, const int index, const TaskParallelTLS *__restrict /*tls*/)
3248{
3250 static_cast<const DynamicPaintOutputSurfaceImageData *>(userdata);
3251
3252 const DynamicPaintSurface *surface = data->surface;
3253 float depth = ((float *)surface->data->type_data)[index];
3254
3255 ImBuf *ibuf = data->ibuf;
3256 /* image buffer position */
3257 const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4;
3258
3259 if (surface->depth_clamp) {
3260 depth /= surface->depth_clamp;
3261 }
3262
3263 if (surface->disp_type == MOD_DPAINT_DISP_DISPLACE) {
3264 depth = (0.5f - depth / 2.0f);
3265 }
3266
3267 CLAMP(depth, 0.0f, 1.0f);
3268
3269 copy_v3_fl(&ibuf->float_buffer.data[pos], depth);
3270 ibuf->float_buffer.data[pos + 3] = 1.0f;
3271}
3272
3273static void dynamic_paint_output_surface_image_wave_cb(void *__restrict userdata,
3274 const int index,
3275 const TaskParallelTLS *__restrict /*tls*/)
3276{
3278 static_cast<const DynamicPaintOutputSurfaceImageData *>(userdata);
3279
3280 const DynamicPaintSurface *surface = data->surface;
3281 const PaintWavePoint *wPoint = &((PaintWavePoint *)surface->data->type_data)[index];
3282 float depth = wPoint->height;
3283
3284 ImBuf *ibuf = data->ibuf;
3285 /* image buffer position */
3286 const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4;
3287
3288 if (surface->depth_clamp) {
3289 depth /= surface->depth_clamp;
3290 }
3291
3292 depth = (0.5f + depth / 2.0f);
3293 CLAMP(depth, 0.0f, 1.0f);
3294
3295 copy_v3_fl(&ibuf->float_buffer.data[pos], depth);
3296 ibuf->float_buffer.data[pos + 3] = 1.0f;
3297}
3298
3299static void dynamic_paint_output_surface_image_wetmap_cb(void *__restrict userdata,
3300 const int index,
3301 const TaskParallelTLS *__restrict /*tls*/)
3302{
3304 static_cast<const DynamicPaintOutputSurfaceImageData *>(userdata);
3305
3306 const DynamicPaintSurface *surface = data->surface;
3307 const PaintPoint *point = &((PaintPoint *)surface->data->type_data)[index];
3308
3309 ImBuf *ibuf = data->ibuf;
3310 /* image buffer position */
3311 const int pos = ((ImgSeqFormatData *)(surface->data->format_data))->uv_p[index].pixel_index * 4;
3312
3313 copy_v3_fl(&ibuf->float_buffer.data[pos], (point->wetness > 1.0f) ? 1.0f : point->wetness);
3314 ibuf->float_buffer.data[pos + 3] = 1.0f;
3315}
3316
3318 const char *filepath,
3319 short output_layer)
3320{
3321 ImBuf *ibuf = nullptr;
3322 PaintSurfaceData *sData = surface->data;
3323 /* OpenEXR or PNG */
3326 char output_file[FILE_MAX];
3327
3328 if (!sData->type_data) {
3329 setError(surface->canvas, N_("Image save failed: invalid surface"));
3330 return;
3331 }
3332 /* if selected format is openexr, but current build doesn't support one */
3333#ifndef WITH_IMAGE_OPENEXR
3336 }
3337#endif
3338 STRNCPY(output_file, filepath);
3339 BKE_image_path_ext_from_imtype_ensure(output_file, sizeof(output_file), format);
3340
3341 /* Validate output file path */
3344
3345 /* Init image buffer */
3346 ibuf = IMB_allocImBuf(surface->image_resolution, surface->image_resolution, 32, IB_float_data);
3347 if (ibuf == nullptr) {
3348 setError(surface->canvas, N_("Image save failed: not enough free memory"));
3349 return;
3350 }
3351
3353 data.surface = surface;
3354 data.ibuf = ibuf;
3355
3356 switch (surface->type) {
3358 switch (output_layer) {
3359 case 0: {
3360 TaskParallelSettings settings;
3362 settings.use_threading = (sData->total_points > 10000);
3364 sData->total_points,
3365 &data,
3367 &settings);
3368 break;
3369 }
3370 case 1: {
3371 TaskParallelSettings settings;
3373 settings.use_threading = (sData->total_points > 10000);
3375 sData->total_points,
3376 &data,
3378 &settings);
3379 break;
3380 }
3381 default:
3382 BLI_assert(0);
3383 break;
3384 }
3385 break;
3387 switch (output_layer) {
3388 case 0: {
3389 TaskParallelSettings settings;
3391 settings.use_threading = (sData->total_points > 10000);
3393 sData->total_points,
3394 &data,
3396 &settings);
3397 break;
3398 }
3399 case 1:
3400 break;
3401 default:
3402 BLI_assert(0);
3403 break;
3404 }
3405 break;
3407 switch (output_layer) {
3408 case 0: {
3409 TaskParallelSettings settings;
3411 settings.use_threading = (sData->total_points > 10000);
3413 sData->total_points,
3414 &data,
3416 &settings);
3417 break;
3418 }
3419 case 1:
3420 break;
3421 default:
3422 BLI_assert(0);
3423 break;
3424 }
3425 break;
3426 default:
3427 BLI_assert(0);
3428 break;
3429 }
3430
3431 /* Set output format, PNG in case EXR isn't supported. */
3432#ifdef WITH_IMAGE_OPENEXR
3433 if (format == R_IMF_IMTYPE_OPENEXR) { /* OpenEXR 32-bit float */
3434 ibuf->ftype = IMB_FTYPE_OPENEXR;
3436 }
3437 else
3438#endif
3439 {
3440 ibuf->ftype = IMB_FTYPE_PNG;
3441 ibuf->foptions.quality = 15;
3442 }
3443
3444 /* Save image */
3445 IMB_save_image(ibuf, output_file, IB_float_data);
3446 IMB_freeImBuf(ibuf);
3447}
3448
3450
3451/***************************** Ray / Nearest Point Utils ******************************/
3452
3453/* A modified callback to bvh tree ray-cast.
3454 * The tree must have been built using bvhtree_from_mesh_corner_tri.
3455 * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree.
3456 *
3457 * To optimize brush detection speed this doesn't calculate hit coordinates or normal.
3458 */
3459static void mesh_tris_spherecast_dp(void *userdata,
3460 int index,
3461 const BVHTreeRay *ray,
3462 BVHTreeRayHit *hit)
3463{
3465 const blender::Span<blender::float3> positions = data->vert_positions;
3466 const int3 *corner_tris = data->corner_tris.data();
3467 const int *corner_verts = data->corner_verts.data();
3468
3469 const float *t0, *t1, *t2;
3470 float dist;
3471
3472 t0 = positions[corner_verts[corner_tris[index][0]]];
3473 t1 = positions[corner_verts[corner_tris[index][1]]];
3474 t2 = positions[corner_verts[corner_tris[index][2]]];
3475
3476 dist = blender::bke::bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2);
3477
3478 if (dist >= 0 && dist < hit->dist) {
3479 hit->index = index;
3480 hit->dist = dist;
3481 hit->no[0] = 0.0f;
3482 }
3483}
3484
3485/* A modified callback to bvh tree nearest point.
3486 * The tree must have been built using bvhtree_from_mesh_corner_tri.
3487 * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree.
3488 *
3489 * To optimize brush detection speed this doesn't calculate hit normal.
3490 */
3491static void mesh_tris_nearest_point_dp(void *userdata,
3492 int index,
3493 const float co[3],
3494 BVHTreeNearest *nearest)
3495{
3497 const blender::Span<blender::float3> positions = data->vert_positions;
3498 const int3 *corner_tris = data->corner_tris.data();
3499 const int *corner_verts = data->corner_verts.data();
3500 float nearest_tmp[3], dist_sq;
3501
3502 const float *t0, *t1, *t2;
3503 t0 = positions[corner_verts[corner_tris[index][0]]];
3504 t1 = positions[corner_verts[corner_tris[index][1]]];
3505 t2 = positions[corner_verts[corner_tris[index][2]]];
3506
3507 closest_on_tri_to_point_v3(nearest_tmp, co, t0, t1, t2);
3508 dist_sq = len_squared_v3v3(co, nearest_tmp);
3509
3510 if (dist_sq < nearest->dist_sq) {
3511 nearest->index = index;
3512 nearest->dist_sq = dist_sq;
3513 copy_v3_v3(nearest->co, nearest_tmp);
3514 nearest->no[0] = 0.0f;
3515 }
3516}
3517
3518/***************************** Brush Painting Calls ******************************/
3519
3531 const int index,
3532 const int paintFlags,
3533 const float paintColor[3],
3534 const float paintAlpha,
3535 const float paintWetness,
3536 const float timescale)
3537{
3538 PaintPoint *pPoint = &((PaintPoint *)surface->data->type_data)[index];
3539
3540 /* Add paint */
3541 if (!(paintFlags & MOD_DPAINT_ERASE)) {
3542 float mix[4];
3543 float temp_alpha = paintAlpha * ((paintFlags & MOD_DPAINT_ABS_ALPHA) ? 1.0f : timescale);
3544
3545 /* mix brush color with wet layer color */
3546 blendColors(pPoint->e_color, pPoint->e_color[3], paintColor, temp_alpha, mix);
3547 copy_v3_v3(pPoint->e_color, mix);
3548
3549 /* mix wetness and alpha depending on selected alpha mode */
3550 if (paintFlags & MOD_DPAINT_ABS_ALPHA) {
3551 /* update values to the brush level unless they're higher already */
3552 CLAMP_MIN(pPoint->e_color[3], paintAlpha);
3553 CLAMP_MIN(pPoint->wetness, paintWetness);
3554 }
3555 else {
3556 float wetness = paintWetness;
3557 CLAMP(wetness, 0.0f, 1.0f);
3558 pPoint->e_color[3] = mix[3];
3559 pPoint->wetness = pPoint->wetness * (1.0f - wetness) + wetness;
3560 }
3561
3562 CLAMP_MIN(pPoint->wetness, MIN_WETNESS);
3563
3564 pPoint->state = DPAINT_PAINT_NEW;
3565 }
3566 /* Erase paint */
3567 else {
3568 float a_ratio, a_highest;
3569 float wetness;
3570 float invFact = 1.0f - paintAlpha;
3571
3572 /*
3573 * Make highest alpha to match erased value
3574 * but maintain alpha ratio
3575 */
3576 if (paintFlags & MOD_DPAINT_ABS_ALPHA) {
3577 a_highest = max_ff(pPoint->color[3], pPoint->e_color[3]);
3578 if (a_highest > invFact) {
3579 a_ratio = invFact / a_highest;
3580
3581 pPoint->e_color[3] *= a_ratio;
3582 pPoint->color[3] *= a_ratio;
3583 }
3584 }
3585 else {
3586 pPoint->e_color[3] -= paintAlpha * timescale;
3587 CLAMP_MIN(pPoint->e_color[3], 0.0f);
3588 pPoint->color[3] -= paintAlpha * timescale;
3589 CLAMP_MIN(pPoint->color[3], 0.0f);
3590 }
3591
3592 wetness = (1.0f - paintWetness) * pPoint->e_color[3];
3593 CLAMP_MAX(pPoint->wetness, wetness);
3594 }
3595}
3596
3597/* applies given brush intersection value for wave surface */
3600 float isect_height)
3601{
3602 const float isect_change = isect_height - wPoint->brush_isect;
3603 const float wave_factor = brush->wave_factor;
3604 bool hit = false;
3605
3606 /* intersection marked regardless of brush type or hit */
3607 wPoint->brush_isect = isect_height;
3609
3610 isect_height *= wave_factor;
3611
3612 /* determine hit depending on wave_factor */
3613 if (wave_factor > 0.0f && wPoint->height > isect_height) {
3614 hit = true;
3615 }
3616 else if (wave_factor < 0.0f && wPoint->height < isect_height) {
3617 hit = true;
3618 }
3619
3620 if (hit) {
3621 switch (brush->wave_type) {
3623 wPoint->height = isect_height;
3624 wPoint->state = DPAINT_WAVE_OBSTACLE;
3625 wPoint->velocity = 0.0f;
3626 break;
3628 wPoint->velocity = isect_height;
3629 break;
3632 break;
3634 if (isect_change < 0.0f) {
3635 wPoint->height += isect_change * wave_factor;
3636 }
3637 break;
3638 default:
3639 BLI_assert(0);
3640 break;
3641 }
3642 }
3643}
3644
3645/*
3646 * add brush results to the surface data depending on surface type
3647 */
3649 const int index,
3651 float paint[3],
3652 float influence,
3653 float depth,
3654 float vel_factor,
3655 const float timescale)
3656{
3657 PaintSurfaceData *sData = surface->data;
3658 float strength;
3659
3660 /* apply influence scale */
3661 influence *= surface->influence_scale;
3662 depth *= surface->influence_scale;
3663
3664 strength = influence * brush->alpha;
3665 CLAMP(strength, 0.0f, 1.0f);
3666
3667 /* Sample velocity colorband if required */
3668 if (brush->flags &
3670 {
3671 float coba_res[4];
3672 vel_factor /= brush->max_velocity;
3673 CLAMP(vel_factor, 0.0f, 1.0f);
3674
3675 if (BKE_colorband_evaluate(brush->vel_ramp, vel_factor, coba_res)) {
3676 if (brush->flags & MOD_DPAINT_VELOCITY_COLOR) {
3677 copy_v3_v3(paint, coba_res);
3678 }
3679 if (brush->flags & MOD_DPAINT_VELOCITY_ALPHA) {
3680 strength *= coba_res[3];
3681 }
3682 if (brush->flags & MOD_DPAINT_VELOCITY_DEPTH) {
3683 depth *= coba_res[3];
3684 }
3685 }
3686 }
3687
3688 /* mix paint surface */
3689 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
3690 float paintWetness = brush->wetness * strength;
3691 float paintAlpha = strength;
3692
3694 surface, index, brush->flags, paint, paintAlpha, paintWetness, timescale);
3695 }
3696 /* displace surface */
3697 else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) {
3698 float *value = (float *)sData->type_data;
3699
3700 if (surface->flags & MOD_DPAINT_DISP_INCREMENTAL) {
3701 depth = value[index] + depth;
3702 }
3703
3704 if (surface->depth_clamp) {
3705 CLAMP(depth, 0.0f - surface->depth_clamp, surface->depth_clamp);
3706 }
3707
3708 if (brush->flags & MOD_DPAINT_ERASE) {
3709 value[index] *= (1.0f - strength);
3710 CLAMP_MIN(value[index], 0.0f);
3711 }
3712 else {
3713 CLAMP_MIN(value[index], depth);
3714 }
3715 }
3716 /* vertex weight group surface */
3717 else if (surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) {
3718 float *value = (float *)sData->type_data;
3719
3720 if (brush->flags & MOD_DPAINT_ERASE) {
3721 value[index] *= (1.0f - strength);
3722 CLAMP_MIN(value[index], 0.0f);
3723 }
3724 else {
3725 CLAMP_MIN(value[index], strength);
3726 }
3727 }
3728 /* wave surface */
3729 else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
3730 if (brush->wave_clamp) {
3731 CLAMP(depth, 0.0f - brush->wave_clamp, brush->wave_clamp);
3732 }
3733
3734 dynamicPaint_mixWaveHeight(&((PaintWavePoint *)sData->type_data)[index], brush, 0.0f - depth);
3735 }
3736
3737 /* doing velocity based painting */
3738 if (sData->bData->brush_velocity) {
3739 sData->bData->brush_velocity[index * 4 + 3] *= influence;
3740 }
3741}
3742
3743/* checks whether surface and brush bounds intersect depending on brush type */
3745 Bounds3D *b2,
3747 float brush_radius)
3748{
3749 if (brush->collision == MOD_DPAINT_COL_VOLUME) {
3750 return boundsIntersect(b1, b2);
3751 }
3753 return boundsIntersectDist(b1, b2, brush_radius);
3754 }
3755 return true;
3756}
3757
3758/* calculate velocity for mesh vertices */
3761
3762 const float (*positions_p)[3];
3763 const float (*positions_c)[3];
3764
3765 const float (*obmat)[4];
3767
3769};
3770
3771static void dynamic_paint_brush_velocity_compute_cb(void *__restrict userdata,
3772 const int i,
3773 const TaskParallelTLS *__restrict /*tls*/)
3774{
3776 userdata);
3777
3778 Vec3f *brush_vel = data->brush_vel;
3779
3780 const float (*positions_p)[3] = data->positions_p;
3781 const float (*positions_c)[3] = data->positions_c;
3782
3783 const float (*obmat)[4] = data->obmat;
3784 float (*prev_obmat)[4] = data->prev_obmat;
3785
3786 const float timescale = data->timescale;
3787
3788 float p1[3], p2[3];
3789
3790 copy_v3_v3(p1, positions_p[i]);
3791 mul_m4_v3(prev_obmat, p1);
3792
3793 copy_v3_v3(p2, positions_c[i]);
3794 mul_m4_v3(obmat, p2);
3795
3796 sub_v3_v3v3(brush_vel[i].v, p2, p1);
3797 mul_v3_fl(brush_vel[i].v, 1.0f / timescale);
3798}
3799
3801 Scene *scene,
3802 Object *ob,
3804 Vec3f **brushVel,
3805 float timescale)
3806{
3807 float prev_obmat[4][4];
3808 Mesh *mesh_p, *mesh_c;
3809 int numOfVerts_p, numOfVerts_c;
3810
3811 float cur_sfra = scene->r.subframe;
3812 int cur_fra = scene->r.cfra;
3813 float prev_sfra = cur_sfra - timescale;
3814 int prev_fra = cur_fra;
3815
3816 if (prev_sfra < 0.0f) {
3817 prev_sfra += 1.0f;
3818 prev_fra = cur_fra - 1;
3819 }
3820
3821 /* previous frame mesh */
3822 scene->r.cfra = prev_fra;
3823 scene->r.subframe = prev_sfra;
3824
3826 scene,
3827 ob,
3828 true,
3830 BKE_scene_ctime_get(scene),
3832
3833 {
3834 auto *runtime_data = static_cast<DynamicPaintRuntime *>(brush->pmd->modifier.runtime);
3835 if (!runtime_data) {
3836 return;
3837 }
3838 std::lock_guard lock(runtime_data->brush_mutex);
3839 mesh_p = BKE_mesh_copy_for_eval(*runtime_data->brush_mesh);
3840 }
3841 numOfVerts_p = mesh_p->verts_num;
3842
3843 float (*positions_p)[3] = reinterpret_cast<float (*)[3]>(
3844 mesh_p->vert_positions_for_write().data());
3845 copy_m4_m4(prev_obmat, ob->object_to_world().ptr());
3846
3847 /* current frame mesh */
3848 scene->r.cfra = cur_fra;
3849 scene->r.subframe = cur_sfra;
3850
3852 scene,
3853 ob,
3854 true,
3856 BKE_scene_ctime_get(scene),
3858 auto *runtime_data = static_cast<DynamicPaintRuntime *>(brush->pmd->modifier.runtime);
3859 if (!runtime_data) {
3860 return;
3861 }
3862 std::lock_guard lock(runtime_data->brush_mutex);
3863 mesh_c = runtime_data->brush_mesh;
3864
3865 numOfVerts_c = mesh_c->verts_num;
3866 float (*positions_c)[3] = reinterpret_cast<float (*)[3]>(
3867 mesh_c->vert_positions_for_write().data());
3868
3869 (*brushVel) = MEM_malloc_arrayN<Vec3f>(size_t(numOfVerts_c), "Dynamic Paint brush velocity");
3870 if (!(*brushVel)) {
3871 return;
3872 }
3873
3874 /* If mesh is constructive -> num of verts has changed,
3875 * only use current frame evaluated-mesh. */
3876 if (numOfVerts_p != numOfVerts_c) {
3877 positions_p = positions_c;
3878 }
3879
3880 /* calculate speed */
3882 data.brush_vel = *brushVel;
3883 data.positions_p = positions_p;
3884 data.positions_c = positions_c;
3885 data.obmat = ob->object_to_world().ptr();
3886 data.prev_obmat = prev_obmat;
3887 data.timescale = timescale;
3888
3889 TaskParallelSettings settings;
3891 settings.use_threading = (numOfVerts_c > 10000);
3893 0, numOfVerts_c, &data, dynamic_paint_brush_velocity_compute_cb, &settings);
3894
3895 BKE_id_free(nullptr, mesh_p);
3896}
3897
3898/* calculate velocity for object center point */
3900 Depsgraph *depsgraph, Scene *scene, Object *ob, Vec3f *brushVel, float timescale)
3901{
3902 float prev_obmat[4][4];
3903 float cur_loc[3] = {0.0f}, prev_loc[3] = {0.0f};
3904
3905 float cur_sfra = scene->r.subframe;
3906 int cur_fra = scene->r.cfra;
3907 float prev_sfra = cur_sfra - timescale;
3908 int prev_fra = cur_fra;
3909
3910 if (prev_sfra < 0.0f) {
3911 prev_sfra += 1.0f;
3912 prev_fra = cur_fra - 1;
3913 }
3914
3915 /* previous frame mesh */
3916 scene->r.cfra = prev_fra;
3917 scene->r.subframe = prev_sfra;
3919 scene,
3920 ob,
3921 false,
3923 BKE_scene_ctime_get(scene),
3925 copy_m4_m4(prev_obmat, ob->object_to_world().ptr());
3926
3927 /* current frame mesh */
3928 scene->r.cfra = cur_fra;
3929 scene->r.subframe = cur_sfra;
3931 scene,
3932 ob,
3933 false,
3935 BKE_scene_ctime_get(scene),
3937
3938 /* calculate speed */
3939 mul_m4_v3(prev_obmat, prev_loc);
3940 mul_m4_v3(ob->object_to_world().ptr(), cur_loc);
3941
3942 sub_v3_v3v3(brushVel->v, cur_loc, prev_loc);
3943 mul_v3_fl(brushVel->v, 1.0f / timescale);
3944}
3945
3969
3970/*
3971 * Paint a brush object mesh to the surface
3972 */
3973static void dynamic_paint_paint_mesh_cell_point_cb_ex(void *__restrict userdata,
3974 const int id,
3975 const TaskParallelTLS *__restrict /*tls*/)
3976{
3977 const DynamicPaintPaintData *data = static_cast<const DynamicPaintPaintData *>(userdata);
3978
3979 const DynamicPaintSurface *surface = data->surface;
3980 const PaintSurfaceData *sData = surface->data;
3981 const PaintBakeData *bData = sData->bData;
3982 DynamicPaintVolumeGrid *grid = bData->grid;
3983
3984 const DynamicPaintBrushSettings *brush = data->brush;
3985
3986 const float timescale = data->timescale;
3987 const int c_index = data->c_index;
3988
3989 const blender::Span<blender::float3> positions = data->positions;
3990 const blender::Span<int> corner_verts = data->corner_verts;
3991 const blender::Span<int3> corner_tris = data->corner_tris;
3992 const float brush_radius = data->brush_radius;
3993 const float *avg_brushNor = data->avg_brushNor;
3994 const Vec3f *brushVelocity = data->brushVelocity;
3995
3997 data->treeData);
3998
3999 const int index = grid->t_index[grid->s_pos[c_index] + id];
4000 const int samples = bData->s_num[index];
4001 int ss;
4002 float total_sample = float(samples);
4003 float brushStrength = 0.0f; /* brush influence factor */
4004 float depth = 0.0f; /* brush intersection depth */
4005 float velocity_val = 0.0f;
4006
4007 float paintColor[3] = {0.0f};
4008 int numOfHits = 0;
4009
4010 /* for image sequence anti-aliasing, use gaussian factors */
4011 if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
4012 total_sample = gaussianTotal;
4013 }
4014
4015 /* Super-sampling */
4016 for (ss = 0; ss < samples; ss++) {
4017 float ray_start[3], ray_dir[3];
4018 float sample_factor = 0.0f;
4019 float sampleStrength = 0.0f;
4020 BVHTreeRayHit hit;
4021 BVHTreeNearest nearest;
4022 short hit_found = 0;
4023
4024 /* volume sample */
4025 float volume_factor = 0.0f;
4026 /* proximity sample */
4027 float proximity_factor = 0.0f;
4028 float prox_colorband[4] = {0.0f};
4029 const bool inner_proximity = (brush->flags & MOD_DPAINT_INVERSE_PROX &&
4030 brush->collision == MOD_DPAINT_COL_VOLDIST);
4031
4032 /* hit data */
4033 float hitCoord[3];
4034 int hitTri = -1;
4035
4036 /* Super-sampling factor. */
4037 if (samples > 1 && surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
4038 sample_factor = gaussianFactors[ss];
4039 }
4040 else {
4041 sample_factor = 1.0f;
4042 }
4043
4044 /* Get current sample position in world coordinates */
4045 copy_v3_v3(ray_start, bData->realCoord[bData->s_pos[index] + ss].v);
4046 copy_v3_v3(ray_dir, bData->bNormal[index].invNorm);
4047
4048 /* a simple hack to minimize chance of ray leaks at identical ray <-> edge locations */
4049 add_v3_fl(ray_start, 0.001f);
4050
4051 hit.index = -1;
4053 nearest.index = -1;
4054 nearest.dist_sq = brush_radius * brush_radius; /* find_nearest uses squared distance */
4055
4056 /* Check volume collision */
4059 treeData->tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, treeData);
4060 if (hit.index != -1) {
4061 /* We hit a triangle, now check if collision point normal is facing the point */
4062
4063 /* For optimization sake, hit point normal isn't calculated in ray cast loop */
4064 const int vtri[3] = {
4065 corner_verts[corner_tris[hit.index][0]],
4066 corner_verts[corner_tris[hit.index][1]],
4067 corner_verts[corner_tris[hit.index][2]],
4068 };
4069 float dot;
4070
4071 normal_tri_v3(hit.no, positions[vtri[0]], positions[vtri[1]], positions[vtri[2]]);
4072 dot = dot_v3v3(ray_dir, hit.no);
4073
4074 /* If ray and hit face normal are facing same direction
4075 * hit point is inside a closed mesh. */
4076 if (dot >= 0.0f) {
4077 const float dist = hit.dist;
4078 const int f_index = hit.index;
4079
4080 /* Also cast a ray in opposite direction to make sure
4081 * point is at least surrounded by two brush faces */
4082 negate_v3(ray_dir);
4083 hit.index = -1;
4085
4087 treeData->tree, ray_start, ray_dir, 0.0f, &hit, mesh_tris_spherecast_dp, treeData);
4088
4089 if (hit.index != -1) {
4090 /* Add factor on super-sample filter. */
4091 volume_factor = 1.0f;
4092 hit_found = HIT_VOLUME;
4093
4094 /* Mark hit info */
4095
4096 /* Calculate final hit coordinates */
4097 madd_v3_v3v3fl(hitCoord, ray_start, ray_dir, hit.dist);
4098
4099 depth += dist * sample_factor;
4100 hitTri = f_index;
4101 }
4102 }
4103 }
4104 }
4105
4106 /* Check proximity collision */
4108 (!hit_found || (brush->flags & MOD_DPAINT_INVERSE_PROX)))
4109 {
4110 float proxDist = -1.0f;
4111 float hitCo[3] = {0.0f, 0.0f, 0.0f};
4112 int tri = 0;
4113
4114 /* if inverse prox and no hit found, skip this sample */
4115 if (inner_proximity && !hit_found) {
4116 continue;
4117 }
4118
4119 /* If pure distance proximity, find the nearest point on the mesh */
4120 if (!(brush->flags & MOD_DPAINT_PROX_PROJECT)) {
4122 treeData->tree, ray_start, &nearest, mesh_tris_nearest_point_dp, treeData);
4123 if (nearest.index != -1) {
4124 proxDist = sqrtf(nearest.dist_sq);
4125 copy_v3_v3(hitCo, nearest.co);
4126 tri = nearest.index;
4127 }
4128 }
4129 else { /* else cast a ray in defined projection direction */
4130 float proj_ray[3] = {0.0f};
4131
4132 if (brush->ray_dir == MOD_DPAINT_RAY_CANVAS) {
4133 copy_v3_v3(proj_ray, bData->bNormal[index].invNorm);
4134 negate_v3(proj_ray);
4135 }
4136 else if (brush->ray_dir == MOD_DPAINT_RAY_BRUSH_AVG) {
4137 copy_v3_v3(proj_ray, avg_brushNor);
4138 }
4139 else { /* MOD_DPAINT_RAY_ZPLUS */
4140 proj_ray[2] = 1.0f;
4141 }
4142 hit.index = -1;
4143 hit.dist = brush_radius;
4144
4145 /* Do a face normal directional ray-cast, and use that distance. */
4147 treeData->tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, treeData);
4148 if (hit.index != -1) {
4149 proxDist = hit.dist;
4150
4151 /* Calculate final hit coordinates */
4152 madd_v3_v3v3fl(hitCo, ray_start, proj_ray, hit.dist);
4153
4154 tri = hit.index;
4155 }
4156 }
4157
4158 /* If a hit was found, calculate required values */
4159 if (proxDist >= 0.0f && proxDist <= brush_radius) {
4160 proximity_factor = proxDist / brush_radius;
4161 CLAMP(proximity_factor, 0.0f, 1.0f);
4162 if (!inner_proximity) {
4163 proximity_factor = 1.0f - proximity_factor;
4164 }
4165
4166 hit_found = HIT_PROXIMITY;
4167
4168 /* if no volume hit, use prox point face info */
4169 if (hitTri == -1) {
4170 copy_v3_v3(hitCoord, hitCo);
4171 hitTri = tri;
4172 }
4173 }
4174 }
4175
4176 /* mix final sample strength depending on brush settings */
4177 if (hit_found) {
4178 /* If "negate volume" enabled, negate all factors within volume. */
4179 if (brush->collision == MOD_DPAINT_COL_VOLDIST && brush->flags & MOD_DPAINT_NEGATE_VOLUME) {
4180 volume_factor = 1.0f - volume_factor;
4181 if (inner_proximity) {
4182 proximity_factor = 1.0f - proximity_factor;
4183 }
4184 }
4185
4186 /* apply final sample depending on final hit type */
4187 if (hit_found == HIT_VOLUME) {
4188 sampleStrength = volume_factor;
4189 }
4190 else if (hit_found == HIT_PROXIMITY) {
4191 /* apply falloff curve to the proximity_factor */
4192 if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP &&
4193 BKE_colorband_evaluate(brush->paint_ramp, (1.0f - proximity_factor), prox_colorband))
4194 {
4195 proximity_factor = prox_colorband[3];
4196 }
4197 else if (brush->proximity_falloff == MOD_DPAINT_PRFALL_CONSTANT) {
4198 proximity_factor = (!inner_proximity || brush->flags & MOD_DPAINT_NEGATE_VOLUME) ? 1.0f :
4199 0.0f;
4200 }
4201 /* apply sample */
4202 sampleStrength = proximity_factor;
4203 }
4204
4205 sampleStrength *= sample_factor;
4206 }
4207 else {
4208 continue;
4209 }
4210
4211 /* velocity brush, only do on main sample */
4212 if (brush->flags & MOD_DPAINT_USES_VELOCITY && ss == 0 && brushVelocity) {
4213 float weights[3];
4214 float brushPointVelocity[3];
4215 float velocity[3];
4216
4217 const int v1 = corner_verts[corner_tris[hitTri][0]];
4218 const int v2 = corner_verts[corner_tris[hitTri][1]];
4219 const int v3 = corner_verts[corner_tris[hitTri][2]];
4220
4221 /* calculate barycentric weights for hit point */
4222 interp_weights_tri_v3(weights, positions[v1], positions[v2], positions[v3], hitCoord);
4223
4224 /* Simple check based on brush surface velocity,
4225 * TODO: perhaps implement something that handles volume movement as well. */
4226
4227 /* interpolate vertex speed vectors to get hit point velocity */
4228 interp_v3_v3v3v3(brushPointVelocity,
4229 brushVelocity[v1].v,
4230 brushVelocity[v2].v,
4231 brushVelocity[v3].v,
4232 weights);
4233
4234 /* subtract canvas point velocity */
4235 if (bData->velocity) {
4236 sub_v3_v3v3(velocity, brushPointVelocity, bData->velocity[index].v);
4237 }
4238 else {
4239 copy_v3_v3(velocity, brushPointVelocity);
4240 }
4241 velocity_val = normalize_v3(velocity);
4242
4243 /* if brush has smudge enabled store brush velocity */
4244 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && brush->flags & MOD_DPAINT_DO_SMUDGE &&
4245 bData->brush_velocity)
4246 {
4247 copy_v3_v3(&bData->brush_velocity[index * 4], velocity);
4248 bData->brush_velocity[index * 4 + 3] = velocity_val;
4249 }
4250 }
4251
4252 /*
4253 * Process hit color and alpha
4254 */
4255 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
4256 float sampleColor[3];
4257 float alpha_factor = 1.0f;
4258
4259 sampleColor[0] = brush->r;
4260 sampleColor[1] = brush->g;
4261 sampleColor[2] = brush->b;
4262
4263 /* Sample proximity colorband if required */
4264 if ((hit_found == HIT_PROXIMITY) && (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP)) {
4265 if (!(brush->flags & MOD_DPAINT_RAMP_ALPHA)) {
4266 sampleColor[0] = prox_colorband[0];
4267 sampleColor[1] = prox_colorband[1];
4268 sampleColor[2] = prox_colorband[2];
4269 }
4270 }
4271
4272 /* Add AA sample */
4273 paintColor[0] += sampleColor[0];
4274 paintColor[1] += sampleColor[1];
4275 paintColor[2] += sampleColor[2];
4276 sampleStrength *= alpha_factor;
4277 numOfHits++;
4278 }
4279
4280 /* Apply sample strength. */
4281 brushStrength += sampleStrength;
4282 } /* End super-sampling. */
4283
4284 /* If any sample was inside paint range. */
4285 if (brushStrength > 0.0f || depth > 0.0f) {
4286 /* Apply super-sampling results. */
4287 if (samples > 1) {
4288 brushStrength /= total_sample;
4289 }
4290 CLAMP(brushStrength, 0.0f, 1.0f);
4291
4292 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
4293 /* Get final pixel color and alpha. */
4294 paintColor[0] /= numOfHits;
4295 paintColor[1] /= numOfHits;
4296 paintColor[2] /= numOfHits;
4297 }
4298 /* Get final object space depth. */
4300 depth /= bData->bNormal[index].normal_scale * total_sample;
4301 }
4302
4304 surface, index, brush, paintColor, brushStrength, depth, velocity_val, timescale);
4305 }
4306}
4307
4308static bool dynamicPaint_paintMesh(Depsgraph *depsgraph,
4309 DynamicPaintSurface *surface,
4311 Object *brushOb,
4312 Scene *scene,
4313 float timescale)
4314{
4315 PaintSurfaceData *sData = surface->data;
4316 PaintBakeData *bData = sData->bData;
4317 Mesh *mesh = nullptr;
4318 Vec3f *brushVelocity = nullptr;
4319
4320 if (brush->flags & MOD_DPAINT_USES_VELOCITY) {
4322 depsgraph, scene, brushOb, brush, &brushVelocity, timescale);
4323 }
4324
4325 auto *runtime_data = static_cast<DynamicPaintRuntime *>(brush->pmd->modifier.runtime);
4326 if (!runtime_data) {
4327 return false;
4328 }
4329 std::lock_guard lock(runtime_data->brush_mutex);
4330 const Mesh *brush_mesh = runtime_data->brush_mesh;
4331 if (brush_mesh == nullptr) {
4332 return false;
4333 }
4334
4335 {
4336 float avg_brushNor[3] = {0.0f};
4337 const float brush_radius = brush->paint_distance * surface->radius_scale;
4338 int numOfVerts;
4339 int ii;
4340 Bounds3D mesh_bb = {{0}};
4341 DynamicPaintVolumeGrid *grid = bData->grid;
4342
4343 mesh = BKE_mesh_copy_for_eval(*brush_mesh);
4344 blender::MutableSpan<blender::float3> positions = mesh->vert_positions_for_write();
4345 const blender::Span<blender::float3> vert_normals = mesh->vert_normals();
4346 const blender::Span<int> corner_verts = mesh->corner_verts();
4347 const blender::Span<int3> corner_tris = mesh->corner_tris();
4348 numOfVerts = mesh->verts_num;
4349
4350 /* Transform collider vertices to global space
4351 * (Faster than transforming per surface point
4352 * coordinates and normals to object space) */
4353 for (ii = 0; ii < numOfVerts; ii++) {
4354 mul_m4_v3(brushOb->object_to_world().ptr(), positions[ii]);
4355 boundInsert(&mesh_bb, positions[ii]);
4356
4357 /* for proximity project calculate average normal */
4358 if (brush->flags & MOD_DPAINT_PROX_PROJECT && brush->collision != MOD_DPAINT_COL_VOLUME) {
4359 float nor[3];
4360 copy_v3_v3(nor, vert_normals[ii]);
4361 mul_mat3_m4_v3(brushOb->object_to_world().ptr(), nor);
4363
4364 add_v3_v3(avg_brushNor, nor);
4365 }
4366 }
4367
4368 mesh->tag_positions_changed();
4369
4370 if (brush->flags & MOD_DPAINT_PROX_PROJECT && brush->collision != MOD_DPAINT_COL_VOLUME) {
4371 mul_v3_fl(avg_brushNor, 1.0f / float(numOfVerts));
4372 /* instead of null vector use positive z */
4373 if (UNLIKELY(normalize_v3(avg_brushNor) == 0.0f)) {
4374 avg_brushNor[2] = 1.0f;
4375 }
4376 }
4377
4378 /* check bounding box collision */
4379 if (grid && meshBrush_boundsIntersect(&grid->grid_bounds, &mesh_bb, brush, brush_radius)) {
4380 /* Build a bvh tree from transformed vertices */
4381 blender::bke::BVHTreeFromMesh treeData = mesh->bvh_corner_tris();
4382 if (treeData.tree != nullptr) {
4383 int c_index;
4384 int total_cells = grid->dim[0] * grid->dim[1] * grid->dim[2];
4385
4386 /* loop through space partitioning grid */
4387 for (c_index = 0; c_index < total_cells; c_index++) {
4388 /* check grid cell bounding box */
4389 if (!grid->s_num[c_index] ||
4390 !meshBrush_boundsIntersect(&grid->bounds[c_index], &mesh_bb, brush, brush_radius))
4391 {
4392 continue;
4393 }
4394
4395 /* loop through cell points and process brush */
4397 data.surface = surface;
4398 data.brush = brush;
4399 data.brushOb = brushOb;
4400 data.scene = scene;
4401 data.timescale = timescale;
4402 data.c_index = c_index;
4403 data.mesh = mesh;
4404 data.positions = positions;
4405 data.corner_verts = corner_verts;
4406 data.corner_tris = corner_tris;
4407 data.brush_radius = brush_radius;
4408 data.avg_brushNor = avg_brushNor;
4409 data.brushVelocity = brushVelocity;
4410 data.treeData = &treeData;
4411
4412 TaskParallelSettings settings;
4414 settings.use_threading = (grid->s_num[c_index] > 250);
4416 grid->s_num[c_index],
4417 &data,
4419 &settings);
4420 }
4421 }
4422 }
4423 BKE_id_free(nullptr, mesh);
4424 }
4425
4426 /* free brush velocity data */
4427 if (brushVelocity) {
4428 MEM_freeN(brushVelocity);
4429 }
4430
4431 return true;
4432}
4433
4434/*
4435 * Paint a particle system to the surface
4436 */
4438 void *__restrict userdata, const int id, const TaskParallelTLS *__restrict /*tls*/)
4439{
4440 const DynamicPaintPaintData *data = static_cast<const DynamicPaintPaintData *>(userdata);
4441
4442 const DynamicPaintSurface *surface = data->surface;
4443 const PaintSurfaceData *sData = surface->data;
4444 const PaintBakeData *bData = sData->bData;
4445 DynamicPaintVolumeGrid *grid = bData->grid;
4446
4447 const DynamicPaintBrushSettings *brush = data->brush;
4448
4449 const ParticleSystem *psys = data->psys;
4450
4451 const float timescale = data->timescale;
4452 const int c_index = data->c_index;
4453
4454 KDTree_3d *tree = static_cast<KDTree_3d *>(data->treeData);
4455
4456 const float solidradius = data->solidradius;
4457 const float smooth = brush->particle_smooth * surface->radius_scale;
4458 const float range = solidradius + smooth;
4459 const float particle_timestep = 0.04f * psys->part->timetweak;
4460
4461 const int index = grid->t_index[grid->s_pos[c_index] + id];
4462 float disp_intersect = 0.0f;
4463 float radius = 0.0f;
4464 float strength = 0.0f;
4465 int part_index = -1;
4466
4467 /*
4468 * With predefined radius, there is no variation between particles.
4469 * It's enough to just find the nearest one.
4470 */
4471 {
4472 KDTreeNearest_3d nearest;
4473 float smooth_range, part_solidradius;
4474
4475 /* Find nearest particle and get distance to it */
4476 BLI_kdtree_3d_find_nearest(tree, bData->realCoord[bData->s_pos[index]].v, &nearest);
4477 /* if outside maximum range, no other particle can influence either */
4478 if (nearest.dist > range) {
4479 return;
4480 }
4481
4482 if (brush->flags & MOD_DPAINT_PART_RAD) {
4483 /* use particles individual size */
4484 ParticleData *pa = psys->particles + nearest.index;
4485 part_solidradius = pa->size;
4486 }
4487 else {
4488 part_solidradius = solidradius;
4489 }
4490 radius = part_solidradius + smooth;
4491 if (nearest.dist < radius) {
4492 /* distances inside solid radius has maximum influence -> dist = 0 */
4493 smooth_range = max_ff(0.0f, (nearest.dist - part_solidradius));
4494 /* do smoothness if enabled */
4495 if (smooth) {
4496 smooth_range /= smooth;
4497 }
4498
4499 strength = 1.0f - smooth_range;
4500 disp_intersect = radius - nearest.dist;
4501 part_index = nearest.index;
4502 }
4503 }
4504 /* If using random per particle radius and closest particle didn't give max influence */
4505 if (brush->flags & MOD_DPAINT_PART_RAD && strength < 1.0f && psys->part->randsize > 0.0f) {
4506 /*
4507 * If we use per particle radius, we have to sample all particles
4508 * within max radius range
4509 */
4510 KDTreeNearest_3d *nearest;
4511
4512 float smooth_range = smooth * (1.0f - strength), dist;
4513 /* calculate max range that can have particles with higher influence than the nearest one */
4514 const float max_range = smooth - strength * smooth + solidradius;
4515 /* Make gcc happy! */
4516 dist = max_range;
4517
4518 const int particles = BLI_kdtree_3d_range_search(
4519 tree, bData->realCoord[bData->s_pos[index]].v, &nearest, max_range);
4520
4521 /* Find particle that produces highest influence */
4522 for (int n = 0; n < particles; n++) {
4523 ParticleData *pa = &psys->particles[nearest[n].index];
4524
4525 /* skip if out of range */
4526 if (nearest[n].dist > (pa->size + smooth)) {
4527 continue;
4528 }
4529
4530 /* update hit data */
4531 const float s_range = nearest[n].dist - pa->size;
4532 /* skip if higher influence is already found */
4533 if (smooth_range < s_range) {
4534 continue;
4535 }
4536
4537 /* update hit data */
4538 smooth_range = s_range;
4539 dist = nearest[n].dist;
4540 part_index = nearest[n].index;
4541
4542 /* If inside solid range and no disp depth required, no need to seek further */
4543 if ((s_range < 0.0f) &&
4545 {
4546 break;
4547 }
4548 }
4549
4550 if (nearest) {
4551 MEM_freeN(nearest);
4552 }
4553
4554 /* now calculate influence for this particle */
4555 const float rad = radius + smooth;
4556 if ((rad - dist) > disp_intersect) {
4557 disp_intersect = radius - dist;
4558 radius = rad;
4559 }
4560
4561 /* do smoothness if enabled */
4562 CLAMP_MIN(smooth_range, 0.0f);
4563 if (smooth) {
4564 smooth_range /= smooth;
4565 }
4566
4567 const float str = 1.0f - smooth_range;
4568 /* if influence is greater, use this one */
4569 strength = std::max(str, strength);
4570 }
4571
4572 if (strength > 0.001f) {
4573 float paintColor[4] = {0.0f};
4574 float depth = 0.0f;
4575 float velocity_val = 0.0f;
4576
4577 /* apply velocity */
4578 if ((brush->flags & MOD_DPAINT_USES_VELOCITY) && (part_index != -1)) {
4579 float velocity[3];
4580 ParticleData *pa = psys->particles + part_index;
4581 mul_v3_v3fl(velocity, pa->state.vel, particle_timestep);
4582
4583 /* subtract canvas point velocity */
4584 if (bData->velocity) {
4585 sub_v3_v3(velocity, bData->velocity[index].v);
4586 }
4587 velocity_val = normalize_v3(velocity);
4588
4589 /* store brush velocity for smudge */
4590 if ((surface->type == MOD_DPAINT_SURFACE_T_PAINT) &&
4591 (brush->flags & MOD_DPAINT_DO_SMUDGE && bData->brush_velocity))
4592 {
4593 copy_v3_v3(&bData->brush_velocity[index * 4], velocity);
4594 bData->brush_velocity[index * 4 + 3] = velocity_val;
4595 }
4596 }
4597
4598 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
4599 copy_v3_v3(paintColor, &brush->r);
4600 }
4602 /* get displace depth */
4603 disp_intersect = (1.0f - sqrtf(disp_intersect / radius)) * radius;
4604 depth = max_ff(0.0f, (radius - disp_intersect) / bData->bNormal[index].normal_scale);
4605 }
4606
4608 surface, index, brush, paintColor, strength, depth, velocity_val, timescale);
4609 }
4610}
4611
4613 ParticleSystem *psys,
4615 float timescale)
4616{
4617 ParticleSettings *part = psys->part;
4618 PaintSurfaceData *sData = surface->data;
4619 PaintBakeData *bData = sData->bData;
4620 DynamicPaintVolumeGrid *grid = bData->grid;
4621
4622 KDTree_3d *tree;
4623 int particlesAdded = 0;
4624 int invalidParticles = 0;
4625 int p = 0;
4626
4627 const float solidradius = surface->radius_scale * ((brush->flags & MOD_DPAINT_PART_RAD) ?
4628 part->size :
4629 brush->particle_radius);
4630 const float smooth = brush->particle_smooth * surface->radius_scale;
4631
4632 const float range = solidradius + smooth;
4633
4634 Bounds3D part_bb = {{0}};
4635
4636 if (psys->totpart < 1) {
4637 return true;
4638 }
4639
4640 /*
4641 * Build a KD-tree to optimize distance search
4642 */
4643 tree = BLI_kdtree_3d_new(psys->totpart);
4644
4645 /* loop through particles and insert valid ones to the tree */
4646 p = 0;
4647 for (ParticleData *pa = psys->particles; p < psys->totpart; p++, pa++) {
4648 /* Proceed only if particle is active */
4649 if ((pa->alive == PARS_UNBORN && (part->flag & PART_UNBORN) == 0) ||
4650 (pa->alive == PARS_DEAD && (part->flag & PART_DIED) == 0) || (pa->flag & PARS_UNEXIST))
4651 {
4652 continue;
4653 }
4654
4655 /* for debug purposes check if any NAN particle proceeds
4656 * For some reason they get past activity check, this should rule most of them out */
4657 if (isnan(pa->state.co[0]) || isnan(pa->state.co[1]) || isnan(pa->state.co[2])) {
4658 invalidParticles++;
4659 continue;
4660 }
4661
4662 /* make sure particle is close enough to canvas */
4663 if (!boundIntersectPoint(&grid->grid_bounds, pa->state.co, range)) {
4664 continue;
4665 }
4666
4667 BLI_kdtree_3d_insert(tree, p, pa->state.co);
4668
4669 /* calc particle system bounds */
4670 boundInsert(&part_bb, pa->state.co);
4671
4672 particlesAdded++;
4673 }
4674 if (invalidParticles) {
4675 CLOG_WARN(&LOG, "Invalid particle(s) found!");
4676 }
4677
4678 /* If no suitable particles were found, exit */
4679 if (particlesAdded < 1) {
4680 BLI_kdtree_3d_free(tree);
4681 return true;
4682 }
4683
4684 /* only continue if particle bb is close enough to canvas bb */
4685 if (boundsIntersectDist(&grid->grid_bounds, &part_bb, range)) {
4686 int c_index;
4687 int total_cells = grid->dim[0] * grid->dim[1] * grid->dim[2];
4688
4689 /* balance tree */
4690 BLI_kdtree_3d_balance(tree);
4691
4692 /* loop through space partitioning grid */
4693 for (c_index = 0; c_index < total_cells; c_index++) {
4694 /* check cell bounding box */
4695 if (!grid->s_num[c_index] || !boundsIntersectDist(&grid->bounds[c_index], &part_bb, range)) {
4696 continue;
4697 }
4698
4699 /* loop through cell points */
4701 data.surface = surface;
4702 data.brush = brush;
4703 data.psys = psys;
4704 data.solidradius = solidradius;
4705 data.timescale = timescale;
4706 data.c_index = c_index;
4707 data.treeData = tree;
4708
4709 TaskParallelSettings settings;
4711 settings.use_threading = (grid->s_num[c_index] > 250);
4713 grid->s_num[c_index],
4714 &data,
4716 &settings);
4717 }
4718 }
4719 BLI_kdtree_3d_free(tree);
4720
4721 return true;
4722}
4723
4724/* paint a single point of defined proximity radius to the surface */
4725static void dynamic_paint_paint_single_point_cb_ex(void *__restrict userdata,
4726 const int index,
4727 const TaskParallelTLS *__restrict /*tls*/)
4728{
4729 const DynamicPaintPaintData *data = static_cast<const DynamicPaintPaintData *>(userdata);
4730
4731 const DynamicPaintSurface *surface = data->surface;
4732 const PaintSurfaceData *sData = surface->data;
4733 const PaintBakeData *bData = sData->bData;
4734
4735 const DynamicPaintBrushSettings *brush = data->brush;
4736
4737 const float timescale = data->timescale;
4738
4739 const float brush_radius = data->brush_radius;
4740 const Vec3f *brushVelocity = data->brushVelocity;
4741
4742 float *pointCoord = data->pointCoord;
4743
4744 const float distance = len_v3v3(pointCoord, bData->realCoord[bData->s_pos[index]].v);
4745 float colorband[4] = {0.0f};
4746 float strength;
4747
4748 if (distance > brush_radius) {
4749 return;
4750 }
4751
4752 /* Smooth range or color ramp */
4753 if (ELEM(brush->proximity_falloff, MOD_DPAINT_PRFALL_SMOOTH, MOD_DPAINT_PRFALL_RAMP)) {
4754 strength = 1.0f - distance / brush_radius;
4755 CLAMP(strength, 0.0f, 1.0f);
4756 }
4757 else {
4758 strength = 1.0f;
4759 }
4760
4761 if (strength >= 0.001f) {
4762 float paintColor[3] = {0.0f};
4763 float depth = 0.0f;
4764 float velocity_val = 0.0f;
4765
4766 /* color ramp */
4767 if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP &&
4768 BKE_colorband_evaluate(brush->paint_ramp, (1.0f - strength), colorband))
4769 {
4770 strength = colorband[3];
4771 }
4772
4773 if (brush->flags & MOD_DPAINT_USES_VELOCITY) {
4774 float velocity[3];
4775
4776 /* subtract canvas point velocity */
4777 if (bData->velocity) {
4778 sub_v3_v3v3(velocity, brushVelocity->v, bData->velocity[index].v);
4779 }
4780 else {
4781 copy_v3_v3(velocity, brushVelocity->v);
4782 }
4783 velocity_val = len_v3(velocity);
4784
4785 /* store brush velocity for smudge */
4786 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && brush->flags & MOD_DPAINT_DO_SMUDGE &&
4787 bData->brush_velocity)
4788 {
4789 mul_v3_v3fl(&bData->brush_velocity[index * 4], velocity, 1.0f / velocity_val);
4790 bData->brush_velocity[index * 4 + 3] = velocity_val;
4791 }
4792 }
4793
4794 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
4795 if (brush->proximity_falloff == MOD_DPAINT_PRFALL_RAMP &&
4796 !(brush->flags & MOD_DPAINT_RAMP_ALPHA))
4797 {
4798 paintColor[0] = colorband[0];
4799 paintColor[1] = colorband[1];
4800 paintColor[2] = colorband[2];
4801 }
4802 else {
4803 paintColor[0] = brush->r;
4804 paintColor[1] = brush->g;
4805 paintColor[2] = brush->b;
4806 }
4807 }
4809 /* get displace depth */
4810 const float disp_intersect = (1.0f - sqrtf((brush_radius - distance) / brush_radius)) *
4811 brush_radius;
4812 depth = max_ff(0.0f, (brush_radius - disp_intersect) / bData->bNormal[index].normal_scale);
4813 }
4815 surface, index, brush, paintColor, strength, depth, velocity_val, timescale);
4816 }
4817}
4818
4820 Depsgraph *depsgraph,
4821 DynamicPaintSurface *surface,
4822 /* Cannot be const, because it is assigned to non-const variable.
4823 * NOLINTNEXTLINE: readability-non-const-parameter. */
4824 float *pointCoord,
4826 Object *brushOb,
4827 Scene *scene,
4828 float timescale)
4829{
4830 PaintSurfaceData *sData = surface->data;
4831 float brush_radius = brush->paint_distance * surface->radius_scale;
4832 Vec3f brushVel;
4833
4834 if (brush->flags & MOD_DPAINT_USES_VELOCITY) {
4835 dynamicPaint_brushObjectCalculateVelocity(depsgraph, scene, brushOb, &brushVel, timescale);
4836 }
4837
4838 auto *runtime_data = static_cast<DynamicPaintRuntime *>(brush->pmd->modifier.runtime);
4839 std::lock_guard lock(runtime_data->brush_mutex);
4840 const Mesh *brush_mesh = runtime_data->brush_mesh;
4841
4842 /*
4843 * Loop through every surface point
4844 */
4846 data.surface = surface;
4847 data.brush = brush;
4848 data.brushOb = brushOb;
4849 data.scene = scene;
4850 data.timescale = timescale;
4851 data.positions = brush_mesh->vert_positions();
4852 data.brush_radius = brush_radius;
4853 data.brushVelocity = &brushVel;
4854 data.pointCoord = pointCoord;
4855
4856 TaskParallelSettings settings;
4858 settings.use_threading = (sData->total_points > 1000);
4861
4862 return true;
4863}
4864
4865/***************************** Dynamic Paint Step / Baking ******************************/
4866
4867/*
4868 * Calculate current frame distances and directions for adjacency data
4869 */
4870
4871static void dynamic_paint_prepare_adjacency_cb(void *__restrict userdata,
4872 const int index,
4873 const TaskParallelTLS *__restrict /*tls*/)
4874{
4875 PaintSurfaceData *sData = static_cast<PaintSurfaceData *>(userdata);
4876 PaintBakeData *bData = sData->bData;
4877 BakeAdjPoint *bNeighs = bData->bNeighs;
4878 PaintAdjData *adj_data = sData->adj_data;
4879 Vec3f *realCoord = bData->realCoord;
4880
4881 const int num_neighs = adj_data->n_num[index];
4882
4883 for (int i = 0; i < num_neighs; i++) {
4884 const int n_index = adj_data->n_index[index] + i;
4885 const int t_index = adj_data->n_target[n_index];
4886
4887 /* dir vec */
4888 sub_v3_v3v3(bNeighs[n_index].dir,
4889 realCoord[bData->s_pos[t_index]].v,
4890 realCoord[bData->s_pos[index]].v);
4891 /* dist */
4892 bNeighs[n_index].dist = normalize_v3(bNeighs[n_index].dir);
4893 }
4894}
4895
4896static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, const bool force_init)
4897{
4898 PaintSurfaceData *sData = surface->data;
4899 PaintBakeData *bData = sData->bData;
4900 BakeAdjPoint *bNeighs;
4901 PaintAdjData *adj_data = sData->adj_data;
4902
4903 int index;
4904
4905 if ((!surface_usesAdjDistance(surface) && !force_init) || !sData->adj_data) {
4906 return;
4907 }
4908
4909 if (bData->bNeighs) {
4910 MEM_freeN(bData->bNeighs);
4911 }
4912 bNeighs = bData->bNeighs = MEM_malloc_arrayN<BakeAdjPoint>(
4913 size_t(sData->adj_data->total_targets), "PaintEffectBake");
4914 if (!bNeighs) {
4915 return;
4916 }
4917
4918 TaskParallelSettings settings;
4920 settings.use_threading = (sData->total_points > 1000);
4922 0, sData->total_points, sData, dynamic_paint_prepare_adjacency_cb, &settings);
4923
4924 /* calculate average values (single thread).
4925 * NOTE: tried to put this in threaded callback (using _reduce feature),
4926 * but gave ~30% slower result! */
4927 bData->average_dist = 0.0;
4928 for (index = 0; index < sData->total_points; index++) {
4929 int numOfNeighs = adj_data->n_num[index];
4930
4931 for (int i = 0; i < numOfNeighs; i++) {
4932 bData->average_dist += double(bNeighs[adj_data->n_index[index] + i].dist);
4933 }
4934 }
4935 bData->average_dist /= adj_data->total_targets;
4936}
4937
4938/* Find two adjacency points (closest_id) and influence (closest_d)
4939 * to move paint towards when affected by a force. */
4941 const int index,
4942 const float force[3],
4943 float closest_d[2],
4944 int closest_id[2])
4945{
4946 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
4947 const int numOfNeighs = sData->adj_data->n_num[index];
4948
4949 closest_id[0] = closest_id[1] = -1;
4950 closest_d[0] = closest_d[1] = -1.0f;
4951
4952 /* find closest neigh */
4953 for (int i = 0; i < numOfNeighs; i++) {
4954 const int n_index = sData->adj_data->n_index[index] + i;
4955 const float dir_dot = dot_v3v3(bNeighs[n_index].dir, force);
4956
4957 if (dir_dot > closest_d[0] && dir_dot > 0.0f) {
4958 closest_d[0] = dir_dot;
4959 closest_id[0] = n_index;
4960 }
4961 }
4962
4963 if (closest_d[0] < 0.0f) {
4964 return;
4965 }
4966
4967 /* find second closest neigh */
4968 for (int i = 0; i < numOfNeighs; i++) {
4969 const int n_index = sData->adj_data->n_index[index] + i;
4970
4971 if (n_index == closest_id[0]) {
4972 continue;
4973 }
4974
4975 const float dir_dot = dot_v3v3(bNeighs[n_index].dir, force);
4976 const float closest_dot = dot_v3v3(bNeighs[n_index].dir, bNeighs[closest_id[0]].dir);
4977
4978 /* only accept neighbor at "other side" of the first one in relation to force dir
4979 * so make sure angle between this and closest neigh is greater than first angle. */
4980 if (dir_dot > closest_d[1] && closest_dot < closest_d[0] && dir_dot > 0.0f) {
4981 closest_d[1] = dir_dot;
4982 closest_id[1] = n_index;
4983 }
4984 }
4985
4986 /* if two valid neighs found, calculate how force effect is divided evenly between them
4987 * (so that d[0] + d[1] = 1.0) */
4988 if (closest_id[1] != -1) {
4989 float force_proj[3];
4990 float tangent[3];
4991 const float neigh_diff = acosf(
4992 dot_v3v3(bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir));
4993 float force_intersect;
4994 float temp;
4995
4996 /* project force vector on the plane determined by these two neighbor points
4997 * and calculate relative force angle from it. */
4998 cross_v3_v3v3(tangent, bNeighs[closest_id[0]].dir, bNeighs[closest_id[1]].dir);
4999 normalize_v3(tangent);
5000 force_intersect = dot_v3v3(force, tangent);
5001 madd_v3_v3v3fl(force_proj, force, tangent, (-1.0f) * force_intersect);
5002 normalize_v3(force_proj);
5003
5004 /* get drip factor based on force dir in relation to angle between those neighbors */
5005 temp = dot_v3v3(bNeighs[closest_id[0]].dir, force_proj);
5006 CLAMP(temp, -1.0f, 1.0f); /* float precision might cause values > 1.0f that return infinite */
5007 closest_d[1] = acosf(temp) / neigh_diff;
5008 closest_d[0] = 1.0f - closest_d[1];
5009
5010 /* and multiply depending on how deeply force intersects surface */
5011 temp = fabsf(force_intersect);
5012 CLAMP(temp, 0.0f, 1.0f);
5013 mul_v2_fl(closest_d, acosf(temp) / float(M_PI_2));
5014 }
5015 else {
5016 /* if only single neighbor, still linearize force intersection effect */
5017 closest_d[0] = 1.0f - acosf(closest_d[0]) / float(M_PI_2);
5018 }
5019}
5020
5023 float timescale)
5024{
5025 PaintSurfaceData *sData = surface->data;
5026 PaintBakeData *bData = sData->bData;
5027 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
5028 float max_velocity = 0.0f;
5029
5030 if (!sData->adj_data) {
5031 return;
5032 }
5033
5034 /* find max velocity */
5035 for (int index = 0; index < sData->total_points; index++) {
5036 float vel = bData->brush_velocity[index * 4 + 3];
5037 CLAMP_MIN(max_velocity, vel);
5038 }
5039
5040 int steps = int(ceil(double(max_velocity) / bData->average_dist * double(timescale)));
5041 CLAMP(steps, 0, 12);
5042 float eff_scale = brush->smudge_strength / float(steps) * timescale;
5043
5044 for (int step = 0; step < steps; step++) {
5045 for (int index = 0; index < sData->total_points; index++) {
5046
5047 if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) {
5048 continue;
5049 }
5050
5051 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
5052 float smudge_str = bData->brush_velocity[index * 4 + 3];
5053
5054 /* force targets */
5055 int closest_id[2];
5056 float closest_d[2];
5057
5058 if (!smudge_str) {
5059 continue;
5060 }
5061
5062 /* get force affect points */
5064 sData, index, &bData->brush_velocity[index * 4], closest_d, closest_id);
5065
5066 /* Apply movement towards those two points */
5067 for (int i = 0; i < 2; i++) {
5068 int n_index = closest_id[i];
5069 if (n_index != -1 && closest_d[i] > 0.0f) {
5070 float dir_dot = closest_d[i], dir_factor;
5071 float speed_scale = eff_scale * smudge_str / bNeighs[n_index].dist;
5072 PaintPoint *ePoint = &(
5073 (PaintPoint *)sData->type_data)[sData->adj_data->n_target[n_index]];
5074
5075 /* just skip if angle is too extreme */
5076 if (dir_dot <= 0.0f) {
5077 continue;
5078 }
5079
5080 dir_factor = dir_dot * speed_scale;
5081 CLAMP_MAX(dir_factor, brush->smudge_strength);
5082
5083 /* mix new color and alpha */
5084 mixColors(ePoint->color, ePoint->color[3], pPoint->color, pPoint->color[3], dir_factor);
5085 ePoint->color[3] = ePoint->color[3] * (1.0f - dir_factor) +
5086 pPoint->color[3] * dir_factor;
5087
5088 /* smudge "wet layer" */
5089 mixColors(ePoint->e_color,
5090 ePoint->e_color[3],
5091 pPoint->e_color,
5092 pPoint->e_color[3],
5093 dir_factor);
5094 ePoint->e_color[3] = ePoint->e_color[3] * (1.0f - dir_factor) +
5095 pPoint->e_color[3] * dir_factor;
5096 pPoint->wetness *= (1.0f - dir_factor);
5097 }
5098 }
5099 }
5100 }
5101}
5102
5123
5124/*
5125 * Prepare data required by effects for current frame.
5126 * Returns number of steps required
5127 */
5128static void dynamic_paint_prepare_effect_cb(void *__restrict userdata,
5129 const int index,
5130 const TaskParallelTLS *__restrict /*tls*/)
5131{
5132 const DynamicPaintEffectData *data = static_cast<const DynamicPaintEffectData *>(userdata);
5133
5134 const DynamicPaintSurface *surface = data->surface;
5135 const PaintSurfaceData *sData = surface->data;
5136 const PaintBakeData *bData = sData->bData;
5137 Vec3f *realCoord = bData->realCoord;
5138
5139 Scene *scene = data->scene;
5140
5141 float *force = data->force;
5142 ListBase *effectors = data->effectors;
5143
5144 float forc[3] = {0};
5145 float vel[3] = {0};
5146
5147 /* apply force fields */
5148 if (effectors) {
5149 EffectedPoint epoint;
5150 pd_point_from_loc(scene, realCoord[bData->s_pos[index]].v, vel, index, &epoint);
5151 epoint.vel_to_sec = 1.0f;
5153 effectors, nullptr, surface->effector_weights, &epoint, forc, nullptr, nullptr);
5154 }
5155
5156 /* if global gravity is enabled, add it too */
5158 /* also divide by 10 to about match default gravity
5159 * with default force strength (1.0). */
5160 madd_v3_v3fl(forc,
5162 surface->effector_weights->global_gravity * surface->effector_weights->weight[0] /
5163 10.0f);
5164 }
5165
5166 /* add surface point velocity and acceleration if enabled */
5167 if (bData->velocity) {
5168 if (surface->drip_vel) {
5169 madd_v3_v3fl(forc, bData->velocity[index].v, surface->drip_vel * (-1.0f));
5170 }
5171
5172 /* acceleration */
5173 if (bData->prev_velocity && surface->drip_acc) {
5174 float acc[3];
5175 copy_v3_v3(acc, bData->velocity[index].v);
5176 sub_v3_v3(acc, bData->prev_velocity[index].v);
5177 madd_v3_v3fl(forc, acc, surface->drip_acc * (-1.0f));
5178 }
5179 }
5180
5181 /* force strength, and normalize force vec */
5182 force[index * 4 + 3] = normalize_v3_v3(&force[index * 4], forc);
5183}
5184
5186 DynamicPaintSurface *surface,
5187 Scene *scene,
5188 Object *ob,
5189 float **force,
5190 float timescale)
5191{
5192 double average_force = 0.0f;
5193 float shrink_speed = 0.0f, spread_speed = 0.0f;
5194 float fastest_effect, avg_dist;
5195 int steps;
5196 PaintSurfaceData *sData = surface->data;
5197 PaintBakeData *bData = sData->bData;
5198
5199 /* Init force data if required */
5200 if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
5201 ListBase *effectors = BKE_effectors_create(
5202 depsgraph, ob, nullptr, surface->effector_weights, false);
5203
5204 /* allocate memory for force data (dir vector + strength) */
5205 *force = MEM_malloc_arrayN<float>(4 * size_t(sData->total_points), "PaintEffectForces");
5206
5207 if (*force) {
5209 data.surface = surface;
5210 data.scene = scene;
5211 data.force = *force;
5212 data.effectors = effectors;
5213
5214 TaskParallelSettings settings;
5216 settings.use_threading = (sData->total_points > 1000);
5218 0, sData->total_points, &data, dynamic_paint_prepare_effect_cb, &settings);
5219
5220 /* calculate average values (single thread) */
5221 for (int index = 0; index < sData->total_points; index++) {
5222 average_force += double((*force)[index * 4 + 3]);
5223 }
5224 average_force /= sData->total_points;
5225 }
5226 BKE_effectors_free(effectors);
5227 }
5228
5229 /* Get number of required steps using average point distance
5230 * so that just a few ultra close pixels won't increase sub-steps to max. */
5231
5232 /* Adjust number of required sub-step by fastest active effect. */
5233 if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) {
5234 spread_speed = surface->spread_speed;
5235 }
5236 if (surface->effect & MOD_DPAINT_EFFECT_DO_SHRINK) {
5237 shrink_speed = surface->shrink_speed;
5238 }
5239
5240 fastest_effect = max_fff(spread_speed, shrink_speed, average_force);
5241 avg_dist = bData->average_dist * double(CANVAS_REL_SIZE) / double(getSurfaceDimension(sData));
5242
5243 steps = int(ceilf(1.5f * EFF_MOVEMENT_PER_FRAME * fastest_effect / avg_dist * timescale));
5244 CLAMP(steps, 1, 20);
5245
5246 return steps;
5247}
5248
5252static void dynamic_paint_effect_spread_cb(void *__restrict userdata,
5253 const int index,
5254 const TaskParallelTLS *__restrict /*tls*/)
5255{
5256 const DynamicPaintEffectData *data = static_cast<const DynamicPaintEffectData *>(userdata);
5257
5258 const DynamicPaintSurface *surface = data->surface;
5259 const PaintSurfaceData *sData = surface->data;
5260
5261 if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) {
5262 return;
5263 }
5264
5265 const int numOfNeighs = sData->adj_data->n_num[index];
5266 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
5267 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
5268 const PaintPoint *prevPoint = static_cast<const PaintPoint *>(data->prevPoint);
5269 const float eff_scale = data->eff_scale;
5270
5271 const int *n_index = sData->adj_data->n_index;
5272 const int *n_target = sData->adj_data->n_target;
5273
5274 /* Loop through neighboring points */
5275 for (int i = 0; i < numOfNeighs; i++) {
5276 const int n_idx = n_index[index] + i;
5277 float w_factor;
5278 const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]];
5279 const float speed_scale = (bNeighs[n_idx].dist < eff_scale) ? 1.0f :
5280 eff_scale / bNeighs[n_idx].dist;
5281 const float color_mix = min_fff(pPoint_prev->wetness, pPoint->wetness, 1.0f) * 0.25f *
5282 surface->color_spread_speed;
5283
5284 /* do color mixing */
5285 if (color_mix) {
5286 mixColors(pPoint->e_color,
5287 pPoint->e_color[3],
5288 pPoint_prev->e_color,
5289 pPoint_prev->e_color[3],
5290 color_mix);
5291 }
5292
5293 /* Only continue if surrounding point has higher wetness */
5294 if (pPoint_prev->wetness < pPoint->wetness || pPoint_prev->wetness < MIN_WETNESS) {
5295 continue;
5296 }
5297
5298 w_factor = 1.0f / numOfNeighs * min_ff(pPoint_prev->wetness, 1.0f) * speed_scale;
5299 CLAMP(w_factor, 0.0f, 1.0f);
5300
5301 /* mix new wetness and color */
5302 pPoint->wetness = pPoint->wetness + w_factor * (pPoint_prev->wetness - pPoint->wetness);
5303 pPoint->e_color[3] = mixColors(pPoint->e_color,
5304 pPoint->e_color[3],
5305 pPoint_prev->e_color,
5306 pPoint_prev->e_color[3],
5307 w_factor);
5308 }
5309}
5310
5311static void dynamic_paint_effect_shrink_cb(void *__restrict userdata,
5312 const int index,
5313 const TaskParallelTLS *__restrict /*tls*/)
5314{
5315 const DynamicPaintEffectData *data = static_cast<const DynamicPaintEffectData *>(userdata);
5316
5317 const DynamicPaintSurface *surface = data->surface;
5318 const PaintSurfaceData *sData = surface->data;
5319
5320 if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) {
5321 return;
5322 }
5323
5324 const int numOfNeighs = sData->adj_data->n_num[index];
5325 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
5326 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
5327 const PaintPoint *prevPoint = static_cast<const PaintPoint *>(data->prevPoint);
5328 const float eff_scale = data->eff_scale;
5329
5330 const int *n_index = sData->adj_data->n_index;
5331 const int *n_target = sData->adj_data->n_target;
5332
5333 /* Loop through neighboring points */
5334 for (int i = 0; i < numOfNeighs; i++) {
5335 const int n_idx = n_index[index] + i;
5336 const float speed_scale = (bNeighs[n_idx].dist < eff_scale) ? 1.0f :
5337 eff_scale / bNeighs[n_idx].dist;
5338 const PaintPoint *pPoint_prev = &prevPoint[n_target[n_idx]];
5339 float a_factor, ea_factor, w_factor;
5340
5341 /* Check if neighboring point has lower alpha,
5342 * if so, decrease this point's alpha as well. */
5343 if (pPoint->color[3] <= 0.0f && pPoint->e_color[3] <= 0.0f && pPoint->wetness <= 0.0f) {
5344 continue;
5345 }
5346
5347 /* decrease factor for dry paint alpha */
5348 a_factor = max_ff((1.0f - pPoint_prev->color[3]) / numOfNeighs *
5349 (pPoint->color[3] - pPoint_prev->color[3]) * speed_scale,
5350 0.0f);
5351 /* decrease factor for wet paint alpha */
5352 ea_factor = max_ff((1.0f - pPoint_prev->e_color[3]) / 8 *
5353 (pPoint->e_color[3] - pPoint_prev->e_color[3]) * speed_scale,
5354 0.0f);
5355 /* decrease factor for paint wetness */
5356 w_factor = max_ff((1.0f - pPoint_prev->wetness) / 8 *
5357 (pPoint->wetness - pPoint_prev->wetness) * speed_scale,
5358 0.0f);
5359
5360 pPoint->color[3] -= a_factor;
5361 CLAMP_MIN(pPoint->color[3], 0.0f);
5362 pPoint->e_color[3] -= ea_factor;
5363 CLAMP_MIN(pPoint->e_color[3], 0.0f);
5364 pPoint->wetness -= w_factor;
5365 CLAMP_MIN(pPoint->wetness, 0.0f);
5366 }
5367}
5368
5369static void dynamic_paint_effect_drip_cb(void *__restrict userdata,
5370 const int index,
5371 const TaskParallelTLS *__restrict /*tls*/)
5372{
5373 const DynamicPaintEffectData *data = static_cast<const DynamicPaintEffectData *>(userdata);
5374
5375 const DynamicPaintSurface *surface = data->surface;
5376 const PaintSurfaceData *sData = surface->data;
5377
5378 if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) {
5379 return;
5380 }
5381
5382 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
5383 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
5384 const PaintPoint *prevPoint = static_cast<const PaintPoint *>(data->prevPoint);
5385 const PaintPoint *pPoint_prev = &prevPoint[index];
5386 const float *force = data->force;
5387 const float eff_scale = data->eff_scale;
5388
5389 const int *n_target = sData->adj_data->n_target;
5390
5391 uint8_t *point_locks = data->point_locks;
5392
5393 int closest_id[2];
5394 float closest_d[2];
5395
5396 /* adjust drip speed depending on wetness */
5397 float w_factor = pPoint_prev->wetness - 0.025f;
5398 if (w_factor <= 0) {
5399 return;
5400 }
5401 CLAMP(w_factor, 0.0f, 1.0f);
5402
5403 float ppoint_wetness_diff = 0.0f;
5404
5405 /* get force affect points */
5406 surface_determineForceTargetPoints(sData, index, &force[index * 4], closest_d, closest_id);
5407
5408 /* Apply movement towards those two points */
5409 for (int i = 0; i < 2; i++) {
5410 const int n_idx = closest_id[i];
5411 if (n_idx != -1 && closest_d[i] > 0.0f) {
5412 const float dir_dot = closest_d[i];
5413
5414 /* just skip if angle is too extreme */
5415 if (dir_dot <= 0.0f) {
5416 continue;
5417 }
5418
5419 float dir_factor, a_factor;
5420 const float speed_scale = eff_scale * force[index * 4 + 3] / bNeighs[n_idx].dist;
5421
5422 const uint n_trgt = uint(n_target[n_idx]);
5423
5424 /* Sort of spin-lock, but only for given ePoint.
5425 * Since the odds a same ePoint is modified at the same time by several threads is very low,
5426 * this is much more efficient than a global spin lock. */
5427 const uint epointlock_idx = n_trgt / 8;
5428 const uint8_t epointlock_bitmask = 1 << (n_trgt & 7); /* 7 == 0b111 */
5429 while (atomic_fetch_and_or_uint8(&point_locks[epointlock_idx], epointlock_bitmask) &
5430 epointlock_bitmask)
5431 {
5432 /* pass */
5433 }
5434
5435 PaintPoint *ePoint = &((PaintPoint *)sData->type_data)[n_trgt];
5436 const float e_wet = ePoint->wetness;
5437
5438 dir_factor = min_ff(0.5f, dir_dot * min_ff(speed_scale, 1.0f) * w_factor);
5439
5440 /* mix new wetness */
5441 ePoint->wetness += dir_factor;
5442 CLAMP(ePoint->wetness, 0.0f, MAX_WETNESS);
5443
5444 /* mix new color */
5445 a_factor = dir_factor / pPoint_prev->wetness;
5446 CLAMP(a_factor, 0.0f, 1.0f);
5447 mixColors(ePoint->e_color,
5448 ePoint->e_color[3],
5449 pPoint_prev->e_color,
5450 pPoint_prev->e_color[3],
5451 a_factor);
5452 /* dripping is supposed to preserve alpha level */
5453 if (pPoint_prev->e_color[3] > ePoint->e_color[3]) {
5454 ePoint->e_color[3] += a_factor * pPoint_prev->e_color[3];
5455 CLAMP_MAX(ePoint->e_color[3], pPoint_prev->e_color[3]);
5456 }
5457
5458 /* Decrease paint wetness on current point (just store diff here,
5459 * that way we can only lock current point once at the end to apply it). */
5460 ppoint_wetness_diff += (ePoint->wetness - e_wet);
5461
5462#ifndef NDEBUG
5463 {
5464 uint8_t ret = atomic_fetch_and_and_uint8(&point_locks[epointlock_idx],
5465 ~epointlock_bitmask);
5466 BLI_assert(ret & epointlock_bitmask);
5467 }
5468#else
5469 atomic_fetch_and_and_uint8(&point_locks[epointlock_idx], ~epointlock_bitmask);
5470#endif
5471 }
5472 }
5473
5474 {
5475 const uint ppointlock_idx = index / 8;
5476 const uint8_t ppointlock_bitmask = 1 << (index & 7); /* 7 == 0b111 */
5477 while (atomic_fetch_and_or_uint8(&point_locks[ppointlock_idx], ppointlock_bitmask) &
5478 ppointlock_bitmask)
5479 {
5480 /* pass */
5481 }
5482
5483 pPoint->wetness -= ppoint_wetness_diff;
5484 CLAMP(pPoint->wetness, 0.0f, MAX_WETNESS);
5485
5486#ifndef NDEBUG
5487 {
5488 uint8_t ret = atomic_fetch_and_and_uint8(&point_locks[ppointlock_idx], ~ppointlock_bitmask);
5489 BLI_assert(ret & ppointlock_bitmask);
5490 }
5491#else
5492 atomic_fetch_and_and_uint8(&point_locks[ppointlock_idx], ~ppointlock_bitmask);
5493#endif
5494 }
5495}
5496
5498 DynamicPaintSurface *surface,
5499 /* Cannot be const, because it is assigned to non-const variable.
5500 * NOLINTNEXTLINE: readability-non-const-parameter. */
5501 float *force,
5502 PaintPoint *prevPoint,
5503 float timescale,
5504 float steps)
5505{
5506 PaintSurfaceData *sData = surface->data;
5507
5508 const float distance_scale = getSurfaceDimension(sData) / CANVAS_REL_SIZE;
5509 timescale /= steps;
5510
5511 if (!sData->adj_data) {
5512 return;
5513 }
5514
5515 /*
5516 * Spread Effect
5517 */
5518 if (surface->effect & MOD_DPAINT_EFFECT_DO_SPREAD) {
5519 const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->spread_speed *
5520 timescale;
5521
5522 /* Copy current surface to the previous points array to read unmodified values */
5523 memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintPoint));
5524
5526 data.surface = surface;
5527 data.prevPoint = prevPoint;
5528 data.eff_scale = eff_scale;
5529
5530 TaskParallelSettings settings;
5532 settings.use_threading = (sData->total_points > 1000);
5534 0, sData->total_points, &data, dynamic_paint_effect_spread_cb, &settings);
5535 }
5536
5537 /*
5538 * Shrink Effect
5539 */
5540 if (surface->effect & MOD_DPAINT_EFFECT_DO_SHRINK) {
5541 const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * surface->shrink_speed *
5542 timescale;
5543
5544 /* Copy current surface to the previous points array to read unmodified values */
5545 memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintPoint));
5546
5548 data.surface = surface;
5549 data.prevPoint = prevPoint;
5550 data.eff_scale = eff_scale;
5551
5552 TaskParallelSettings settings;
5554 settings.use_threading = (sData->total_points > 1000);
5556 0, sData->total_points, &data, dynamic_paint_effect_shrink_cb, &settings);
5557 }
5558
5559 /*
5560 * Drip Effect
5561 */
5562 if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP && force) {
5563 const float eff_scale = distance_scale * EFF_MOVEMENT_PER_FRAME * timescale / 2.0f;
5564
5565 /* Same as #BLI_bitmask, but handled atomically as 'ePoint' locks. */
5566 const size_t point_locks_size = (sData->total_points / 8) + 1;
5567 uint8_t *point_locks = MEM_calloc_arrayN<uint8_t>(point_locks_size, __func__);
5568
5569 /* Copy current surface to the previous points array to read unmodified values */
5570 memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintPoint));
5571
5573 data.surface = surface;
5574 data.prevPoint = prevPoint;
5575 data.eff_scale = eff_scale;
5576 data.force = force;
5577 data.point_locks = point_locks;
5578
5579 TaskParallelSettings settings;
5581 settings.use_threading = (sData->total_points > 1000);
5583 0, sData->total_points, &data, dynamic_paint_effect_drip_cb, &settings);
5584
5585 MEM_freeN(point_locks);
5586 }
5587}
5588
5589static void dynamic_paint_border_cb(void *__restrict userdata,
5590 const int b_index,
5591 const TaskParallelTLS *__restrict /*tls*/)
5592{
5593 const DynamicPaintEffectData *data = static_cast<const DynamicPaintEffectData *>(userdata);
5594
5595 const DynamicPaintSurface *surface = data->surface;
5596 const PaintSurfaceData *sData = surface->data;
5597
5598 const int index = sData->adj_data->border[b_index];
5599
5600 const int numOfNeighs = sData->adj_data->n_num[index];
5601 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
5602
5603 const int *n_index = sData->adj_data->n_index;
5604 const int *n_target = sData->adj_data->n_target;
5605
5606 /* Average neighboring points. Intermediaries use premultiplied alpha. */
5607 float mix_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
5608 float mix_e_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
5609 float mix_wetness = 0.0f;
5610
5611 for (int i = 0; i < numOfNeighs; i++) {
5612 const int n_idx = n_index[index] + i;
5613 const int target = n_target[n_idx];
5614
5615 PaintPoint *pPoint2 = &((PaintPoint *)sData->type_data)[target];
5616
5617 BLI_assert(!(sData->adj_data->flags[target] & ADJ_BORDER_PIXEL));
5618
5619 madd_v3_v3fl(mix_color, pPoint2->color, pPoint2->color[3]);
5620 mix_color[3] += pPoint2->color[3];
5621
5622 madd_v3_v3fl(mix_e_color, pPoint2->e_color, pPoint2->e_color[3]);
5623 mix_e_color[3] += pPoint2->e_color[3];
5624
5625 mix_wetness += pPoint2->wetness;
5626 }
5627
5628 const float divisor = 1.0f / numOfNeighs;
5629
5630 if (mix_color[3]) {
5631 pPoint->color[3] = mix_color[3] * divisor;
5632 mul_v3_v3fl(pPoint->color, mix_color, divisor / pPoint->color[3]);
5633 }
5634 else {
5635 pPoint->color[3] = 0.0f;
5636 }
5637
5638 if (mix_e_color[3]) {
5639 pPoint->e_color[3] = mix_e_color[3] * divisor;
5640 mul_v3_v3fl(pPoint->e_color, mix_e_color, divisor / pPoint->e_color[3]);
5641 }
5642 else {
5643 pPoint->e_color[3] = 0.0f;
5644 }
5645
5646 pPoint->wetness = mix_wetness / numOfNeighs;
5647}
5648
5650{
5651 PaintSurfaceData *sData = surface->data;
5652
5653 if (!sData->adj_data || !sData->adj_data->border) {
5654 return;
5655 }
5656
5657 /* Don't use prevPoint, relying on the condition that neighbors are never border pixels. */
5659 data.surface = surface;
5660
5661 TaskParallelSettings settings;
5663 settings.use_threading = (sData->adj_data->total_border > 1000);
5665 0, sData->adj_data->total_border, &data, dynamic_paint_border_cb, &settings);
5666}
5667
5668static void dynamic_paint_wave_step_cb(void *__restrict userdata,
5669 const int index,
5670 const TaskParallelTLS *__restrict /*tls*/)
5671{
5672 const DynamicPaintEffectData *data = static_cast<const DynamicPaintEffectData *>(userdata);
5673
5674 const DynamicPaintSurface *surface = data->surface;
5675 const PaintSurfaceData *sData = surface->data;
5676 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
5677 const PaintWavePoint *prevPoint = static_cast<const PaintWavePoint *>(data->prevPoint);
5678
5679 const float wave_speed = data->wave_speed;
5680 const float wave_scale = data->wave_scale;
5681 const float wave_max_slope = data->wave_max_slope;
5682
5683 const float dt = data->dt;
5684 const float min_dist = data->min_dist;
5685 const float damp_factor = data->damp_factor;
5686
5687 PaintWavePoint *wPoint = &((PaintWavePoint *)sData->type_data)[index];
5688 const int numOfNeighs = sData->adj_data->n_num[index];
5689 float force = 0.0f, avg_dist = 0.0f, avg_height = 0.0f, avg_n_height = 0.0f;
5690 int numOfN = 0, numOfRN = 0;
5691
5692 if (wPoint->state > 0) {
5693 return;
5694 }
5695
5696 const int *n_index = sData->adj_data->n_index;
5697 const int *n_target = sData->adj_data->n_target;
5698 const int *adj_flags = sData->adj_data->flags;
5699
5700 /* calculate force from surrounding points */
5701 for (int i = 0; i < numOfNeighs; i++) {
5702 const int n_idx = n_index[index] + i;
5703 float dist = bNeighs[n_idx].dist * wave_scale;
5704 const PaintWavePoint *tPoint = &prevPoint[n_target[n_idx]];
5705
5706 if (!dist || tPoint->state > 0) {
5707 continue;
5708 }
5709
5710 CLAMP_MIN(dist, min_dist);
5711 avg_dist += dist;
5712 numOfN++;
5713
5714 /* count average height for edge points for open borders */
5715 if (!(adj_flags[n_target[n_idx]] & ADJ_ON_MESH_EDGE)) {
5716 avg_n_height += tPoint->height;
5717 numOfRN++;
5718 }
5719
5720 force += (tPoint->height - wPoint->height) / (dist * dist);
5721 avg_height += tPoint->height;
5722 }
5723 avg_dist = (numOfN) ? avg_dist / numOfN : 0.0f;
5724
5725 if (surface->flags & MOD_DPAINT_WAVE_OPEN_BORDERS && adj_flags[index] & ADJ_ON_MESH_EDGE) {
5726 /* if open borders, apply a fake height to keep waves going on */
5727 avg_n_height = (numOfRN) ? avg_n_height / numOfRN : 0.0f;
5728 wPoint->height = (dt * wave_speed * avg_n_height + wPoint->height * avg_dist) /
5729 (avg_dist + dt * wave_speed);
5730 }
5731 /* Else do wave equation. */
5732 else {
5733 /* add force towards zero height based on average dist */
5734 if (avg_dist) {
5735 force += (0.0f - wPoint->height) * surface->wave_spring / (avg_dist * avg_dist) / 2.0f;
5736 }
5737
5738 /* change point velocity */
5739 wPoint->velocity += force * dt * wave_speed * wave_speed;
5740 /* damping */
5741 wPoint->velocity *= damp_factor;
5742 /* and new height */
5743 wPoint->height += wPoint->velocity * dt;
5744
5745 /* limit wave slope steepness */
5746 if (wave_max_slope && avg_dist) {
5747 const float max_offset = wave_max_slope * avg_dist;
5748 const float offset = (numOfN) ? (avg_height / numOfN - wPoint->height) : 0.0f;
5749 if (offset > max_offset) {
5750 wPoint->height += offset - max_offset;
5751 }
5752 else if (offset < -max_offset) {
5753 wPoint->height += offset + max_offset;
5754 }
5755 }
5756 }
5757
5758 if (data->reset_wave) {
5759 /* if there wasn't any brush intersection, clear isect height */
5760 if (wPoint->state == DPAINT_WAVE_NONE) {
5761 wPoint->brush_isect = 0.0f;
5762 }
5763 wPoint->state = DPAINT_WAVE_NONE;
5764 }
5765}
5766
5767static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescale)
5768{
5769 PaintSurfaceData *sData = surface->data;
5770 BakeAdjPoint *bNeighs = sData->bData->bNeighs;
5771 int index;
5772 int steps, ss;
5773 float dt, min_dist, damp_factor;
5774 const float wave_speed = surface->wave_speed;
5775 const float wave_max_slope = (surface->wave_smoothness >= 0.01f) ?
5776 (0.5f / surface->wave_smoothness) :
5777 0.0f;
5778 double average_dist = 0.0f;
5779 const float canvas_size = getSurfaceDimension(sData);
5780 const float wave_scale = CANVAS_REL_SIZE / canvas_size;
5781
5782 /* allocate memory */
5784 __func__);
5785 if (!prevPoint) {
5786 return;
5787 }
5788
5789 /* calculate average neigh distance (single thread) */
5790 for (index = 0; index < sData->total_points; index++) {
5791 int numOfNeighs = sData->adj_data->n_num[index];
5792
5793 for (int i = 0; i < numOfNeighs; i++) {
5794 average_dist += double(bNeighs[sData->adj_data->n_index[index] + i].dist);
5795 }
5796 }
5797 average_dist *= double(wave_scale) / sData->adj_data->total_targets;
5798
5799 /* determine number of required steps */
5800 steps = int(ceil(double(WAVE_TIME_FAC * timescale * surface->wave_timescale) /
5801 (average_dist / double(wave_speed) / 3)));
5802 CLAMP(steps, 1, 20);
5803 timescale /= steps;
5804
5805 /* apply simulation values for final timescale */
5806 dt = WAVE_TIME_FAC * timescale * surface->wave_timescale;
5807 min_dist = wave_speed * dt * 1.5f;
5808 damp_factor = pow((1.0f - surface->wave_damping), timescale * surface->wave_timescale);
5809
5810 for (ss = 0; ss < steps; ss++) {
5811 /* copy previous frame data */
5812 memcpy(prevPoint, sData->type_data, sData->total_points * sizeof(PaintWavePoint));
5813
5815 data.surface = surface;
5816 data.prevPoint = prevPoint;
5817 data.wave_speed = wave_speed;
5818 data.wave_scale = wave_scale;
5819 data.wave_max_slope = wave_max_slope;
5820 data.dt = dt;
5821 data.min_dist = min_dist;
5822 data.damp_factor = damp_factor;
5823 data.reset_wave = (ss == steps - 1);
5824
5825 TaskParallelSettings settings;
5827 settings.use_threading = (sData->total_points > 1000);
5829 }
5830
5831 MEM_freeN(prevPoint);
5832}
5833
5834/* Do dissolve and fading effects */
5842
5847
5848static void dynamic_paint_surface_pre_step_cb(void *__restrict userdata,
5849 const int index,
5850 const TaskParallelTLS *__restrict /*tls*/)
5851{
5852 const DynamicPaintDissolveDryData *data = static_cast<const DynamicPaintDissolveDryData *>(
5853 userdata);
5854
5855 const DynamicPaintSurface *surface = data->surface;
5856 const PaintSurfaceData *sData = surface->data;
5857 const float timescale = data->timescale;
5858
5859 /* Do drying dissolve effects */
5860 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
5861 PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index];
5862 /* drying */
5863 if (surface->flags & MOD_DPAINT_USE_DRYING) {
5864 if (pPoint->wetness >= MIN_WETNESS) {
5865 float f_color[4];
5866 float p_wetness = pPoint->wetness;
5867
5868 value_dissolve(&pPoint->wetness,
5869 surface->dry_speed,
5870 timescale,
5871 (surface->flags & MOD_DPAINT_DRY_LOG) != 0);
5872 CLAMP_MIN(pPoint->wetness, 0.0f);
5873
5874 if (pPoint->wetness < surface->color_dry_threshold) {
5875 float dry_ratio = pPoint->wetness / p_wetness;
5876
5877 /*
5878 * Slowly "shift" paint from wet layer to dry layer as it drys:
5879 */
5880 /* make sure alpha values are within proper range */
5881 CLAMP(pPoint->color[3], 0.0f, 1.0f);
5882 CLAMP(pPoint->e_color[3], 0.0f, 1.0f);
5883
5884 /* get current final blended color of these layers */
5886 pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color);
5887 /* reduce wet layer alpha by dry factor */
5888 pPoint->e_color[3] *= dry_ratio;
5889
5890 /* Now calculate new alpha for dry layer that keeps final blended color unchanged. */
5891 pPoint->color[3] = (f_color[3] - pPoint->e_color[3]) / (1.0f - pPoint->e_color[3]);
5892 /* For each rgb component, calculate a new dry layer color that keeps the final blend
5893 * color with these new alpha values. (wet layer color doesn't change). */
5894 if (pPoint->color[3]) {
5895 for (int i = 0; i < 3; i++) {
5896 pPoint->color[i] = (f_color[i] * f_color[3] -
5897 pPoint->e_color[i] * pPoint->e_color[3]) /
5898 (pPoint->color[3] * (1.0f - pPoint->e_color[3]));
5899 }
5900 }
5901 }
5902
5903 pPoint->state = DPAINT_PAINT_WET;
5904 }
5905 /* In case of just dried paint, just mix it to the dry layer and mark it empty. */
5906 else if (pPoint->state > 0) {
5907 float f_color[4];
5908 blendColors(pPoint->color, pPoint->color[3], pPoint->e_color, pPoint->e_color[3], f_color);
5909 copy_v4_v4(pPoint->color, f_color);
5910 /* clear wet layer */
5911 pPoint->wetness = 0.0f;
5912 pPoint->e_color[3] = 0.0f;
5913 pPoint->state = DPAINT_PAINT_DRY;
5914 }
5915 }
5916
5917 if (surface->flags & MOD_DPAINT_DISSOLVE) {
5918 value_dissolve(&pPoint->color[3],
5919 surface->diss_speed,
5920 timescale,
5921 (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0);
5922 CLAMP_MIN(pPoint->color[3], 0.0f);
5923
5924 value_dissolve(&pPoint->e_color[3],
5925 surface->diss_speed,
5926 timescale,
5927 (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0);
5928 CLAMP_MIN(pPoint->e_color[3], 0.0f);
5929 }
5930 }
5931 /* dissolve for float types */
5932 else if (surface->flags & MOD_DPAINT_DISSOLVE &&
5934 {
5935 float *point = &((float *)sData->type_data)[index];
5936 /* log or linear */
5938 point, surface->diss_speed, timescale, (surface->flags & MOD_DPAINT_DISSOLVE_LOG) != 0);
5939 CLAMP_MIN(*point, 0.0f);
5940 }
5941}
5942
5944{
5945 PaintSurfaceData *sData = surface->data;
5946 PaintBakeData *bData = sData->bData;
5948 const blender::Span<blender::float3> positions = mesh->vert_positions();
5949
5950 int numOfVerts = mesh->verts_num;
5951
5952 if (!bData->prev_positions) {
5953 return true;
5954 }
5955
5956 /* matrix comparison */
5957 if (!equals_m4m4(bData->prev_obmat, ob->object_to_world().ptr())) {
5958 return true;
5959 }
5960
5961 /* vertices */
5962 for (int i = 0; i < numOfVerts; i++) {
5963 if (!equals_v3v3(bData->prev_positions[i], positions[i])) {
5964 return true;
5965 }
5966 }
5967
5968 return false;
5969}
5970
5971/* Prepare for surface step by creating PaintBakeNormal data */
5983
5984static void dynamic_paint_generate_bake_data_cb(void *__restrict userdata,
5985 const int index,
5986 const TaskParallelTLS *__restrict /*tls*/)
5987{
5989 userdata);
5990
5991 const DynamicPaintSurface *surface = data->surface;
5992 const PaintSurfaceData *sData = surface->data;
5993 const PaintAdjData *adj_data = sData->adj_data;
5994 const PaintBakeData *bData = sData->bData;
5995
5996 Object *ob = data->ob;
5997
5998 const Vec3f *canvas_verts = data->canvas_verts;
5999
6000 const bool do_velocity_data = data->do_velocity_data;
6001 const bool new_bdata = data->new_bdata;
6002
6003 float prev_point[3] = {0.0f, 0.0f, 0.0f};
6004 float temp_nor[3];
6005
6006 if (do_velocity_data && !new_bdata) {
6007 copy_v3_v3(prev_point, bData->realCoord[bData->s_pos[index]].v);
6008 }
6009
6010 /*
6011 * Calculate current 3D-position and normal of each surface point
6012 */
6013 if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
6014 float n1[3], n2[3], n3[3];
6015 const ImgSeqFormatData *f_data = (ImgSeqFormatData *)sData->format_data;
6016 const PaintUVPoint *tPoint = &((PaintUVPoint *)f_data->uv_p)[index];
6017
6018 bData->s_num[index] = (surface->flags & MOD_DPAINT_ANTIALIAS) ? 5 : 1;
6019 bData->s_pos[index] = index * bData->s_num[index];
6020
6021 /* per sample coordinates */
6022 for (int ss = 0; ss < bData->s_num[index]; ss++) {
6023 interp_v3_v3v3v3(bData->realCoord[bData->s_pos[index] + ss].v,
6024 canvas_verts[tPoint->v1].v,
6025 canvas_verts[tPoint->v2].v,
6026 canvas_verts[tPoint->v3].v,
6027 f_data->barycentricWeights[index * bData->s_num[index] + ss].v);
6028 }
6029
6030 /* Calculate current pixel surface normal */
6031 copy_v3_v3(n1, data->vert_normals[tPoint->v1]);
6032 copy_v3_v3(n2, data->vert_normals[tPoint->v2]);
6033 copy_v3_v3(n3, data->vert_normals[tPoint->v3]);
6034
6036 temp_nor, n1, n2, n3, f_data->barycentricWeights[index * bData->s_num[index]].v);
6037 normalize_v3(temp_nor);
6039 /* Prepare surface normal directional scale to easily convert
6040 * brush intersection amount between global and local space */
6041 float scaled_nor[3];
6042 mul_v3_v3v3(scaled_nor, temp_nor, ob->scale);
6043 bData->bNormal[index].normal_scale = len_v3(scaled_nor);
6044 }
6045 mul_mat3_m4_v3(ob->object_to_world().ptr(), temp_nor);
6046 normalize_v3(temp_nor);
6047 negate_v3_v3(bData->bNormal[index].invNorm, temp_nor);
6048 }
6049 else if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
6050 int ss;
6051 if (surface->flags & MOD_DPAINT_ANTIALIAS && adj_data) {
6052 bData->s_num[index] = adj_data->n_num[index] + 1;
6053 bData->s_pos[index] = adj_data->n_index[index] + index;
6054 }
6055 else {
6056 bData->s_num[index] = 1;
6057 bData->s_pos[index] = index;
6058 }
6059
6060 /* calculate position for each sample */
6061 for (ss = 0; ss < bData->s_num[index]; ss++) {
6062 /* first sample is always point center */
6063 copy_v3_v3(bData->realCoord[bData->s_pos[index] + ss].v, canvas_verts[index].v);
6064 if (ss > 0) {
6065 int t_index = adj_data->n_index[index] + (ss - 1);
6066 /* get vertex position at 1/3 of each neigh edge */
6067 mul_v3_fl(bData->realCoord[bData->s_pos[index] + ss].v, 2.0f / 3.0f);
6068 madd_v3_v3fl(bData->realCoord[bData->s_pos[index] + ss].v,
6069 canvas_verts[adj_data->n_target[t_index]].v,
6070 1.0f / 3.0f);
6071 }
6072 }
6073
6074 /* normal */
6075 copy_v3_v3(temp_nor, data->vert_normals[index]);
6077 /* Prepare surface normal directional scale to easily convert
6078 * brush intersection amount between global and local space */
6079 float scaled_nor[3];
6080 mul_v3_v3v3(scaled_nor, temp_nor, ob->scale);
6081 bData->bNormal[index].normal_scale = len_v3(scaled_nor);
6082 }
6083 mul_mat3_m4_v3(ob->object_to_world().ptr(), temp_nor);
6084 normalize_v3(temp_nor);
6085 negate_v3_v3(bData->bNormal[index].invNorm, temp_nor);
6086 }
6087
6088 /* calculate speed vector */
6089 if (do_velocity_data && !new_bdata && !bData->clear) {
6090 sub_v3_v3v3(bData->velocity[index].v, bData->realCoord[bData->s_pos[index]].v, prev_point);
6091 }
6092}
6093
6095 Depsgraph *depsgraph,
6096 Object *ob)
6097{
6098 PaintSurfaceData *sData = surface->data;
6099 PaintBakeData *bData = sData->bData;
6101 int index;
6102 bool new_bdata = false;
6103 const bool do_velocity_data = ((surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) ||
6104 (surface_getBrushFlags(surface, depsgraph) &
6106 const bool do_accel_data = (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) != 0;
6107
6108 int canvasNumOfVerts = mesh->verts_num;
6109 const blender::Span<blender::float3> positions = mesh->vert_positions();
6110 Vec3f *canvas_verts;
6111
6112 if (bData) {
6113 const bool surface_moved = dynamicPaint_surfaceHasMoved(surface, ob);
6114
6115 /* Get previous speed for acceleration. */
6116 if (do_accel_data && bData->prev_velocity && bData->velocity) {
6117 memcpy(bData->prev_velocity, bData->velocity, sData->total_points * sizeof(Vec3f));
6118 }
6119
6120 /* reset speed vectors */
6121 if (do_velocity_data && bData->velocity && (bData->clear || !surface_moved)) {
6122 memset(bData->velocity, 0, sData->total_points * sizeof(Vec3f));
6123 }
6124
6125 /* if previous data exists and mesh hasn't moved, no need to recalc */
6126 if (!surface_moved) {
6127 return true;
6128 }
6129 }
6130
6131 canvas_verts = MEM_malloc_arrayN<Vec3f>(size_t(canvasNumOfVerts),
6132 "Dynamic Paint transformed canvas verts");
6133 if (!canvas_verts) {
6134 return false;
6135 }
6136
6137 /* allocate memory if required */
6138 if (!bData) {
6139 sData->bData = bData = MEM_callocN<PaintBakeData>("Dynamic Paint bake data");
6140 if (!bData) {
6141 if (canvas_verts) {
6142 MEM_freeN(canvas_verts);
6143 }
6144 return false;
6145 }
6146
6147 /* Init bdata */
6149 "Dynamic Paint step data");
6150 bData->s_pos = MEM_malloc_arrayN<int>(size_t(sData->total_points),
6151 "Dynamic Paint bData s_pos");
6152 bData->s_num = MEM_malloc_arrayN<int>(size_t(sData->total_points),
6153 "Dynamic Paint bData s_num");
6155 "Dynamic Paint point coords");
6156 bData->prev_positions = MEM_malloc_arrayN<float[3]>(size_t(canvasNumOfVerts),
6157 "Dynamic Paint bData prev_positions");
6158
6159 /* if any allocation failed, free everything */
6160 if (!bData->bNormal || !bData->s_pos || !bData->s_num || !bData->realCoord || !canvas_verts) {
6161 if (bData->bNormal) {
6162 MEM_freeN(bData->bNormal);
6163 }
6164 if (bData->s_pos) {
6165 MEM_freeN(bData->s_pos);
6166 }
6167 if (bData->s_num) {
6168 MEM_freeN(bData->s_num);
6169 }
6170 if (bData->realCoord) {
6171 MEM_freeN(bData->realCoord);
6172 }
6173 if (canvas_verts) {
6174 MEM_freeN(canvas_verts);
6175 }
6176
6177 return setError(surface->canvas, N_("Not enough free memory"));
6178 }
6179
6180 new_bdata = true;
6181 }
6182
6183 if (do_velocity_data && !bData->velocity) {
6184 bData->velocity = MEM_calloc_arrayN<Vec3f>(sData->total_points, "Dynamic Paint velocity");
6185 }
6186 if (do_accel_data && !bData->prev_velocity) {
6187 bData->prev_velocity = MEM_malloc_arrayN<Vec3f>(size_t(sData->total_points),
6188 "Dynamic Paint prev velocity");
6189 /* copy previous vel */
6190 if (bData->prev_velocity && bData->velocity) {
6191 memcpy(bData->prev_velocity, bData->velocity, sData->total_points * sizeof(Vec3f));
6192 }
6193 }
6194
6195 /*
6196 * Make a transformed copy of canvas evaluated-mesh vertices to avoid recalculation.
6197 */
6198 bData->mesh_bounds.valid = false;
6199 for (index = 0; index < canvasNumOfVerts; index++) {
6200 copy_v3_v3(canvas_verts[index].v, positions[index]);
6201 mul_m4_v3(ob->object_to_world().ptr(), canvas_verts[index].v);
6202 boundInsert(&bData->mesh_bounds, canvas_verts[index].v);
6203 }
6204
6205 /*
6206 * Prepare each surface point for a new step
6207 */
6209 data.surface = surface;
6210 data.ob = ob;
6211 data.positions = positions;
6212 data.vert_normals = mesh->vert_normals();
6213 data.canvas_verts = canvas_verts;
6214 data.do_velocity_data = do_velocity_data;
6215 data.new_bdata = new_bdata;
6216
6217 TaskParallelSettings settings;
6219 settings.use_threading = (sData->total_points > 1000);
6221 0, sData->total_points, &data, dynamic_paint_generate_bake_data_cb, &settings);
6222
6223 MEM_freeN(canvas_verts);
6224
6225 /* generate surface space partitioning grid */
6226 surfaceGenerateGrid(surface);
6227 /* Calculate current frame adjacency point distances and global directions. */
6228 dynamicPaint_prepareAdjacencyData(surface, false);
6229
6230 /* Copy current frame vertices to check against in next frame */
6231 copy_m4_m4(bData->prev_obmat, ob->object_to_world().ptr());
6232 memcpy(bData->prev_positions, positions.data(), canvasNumOfVerts * sizeof(float[3]));
6233
6234 bData->clear = 0;
6235
6236 return true;
6237}
6238
6239/*
6240 * Do Dynamic Paint step. Paints scene brush objects of current state/frame to the surface.
6241 */
6242static int dynamicPaint_doStep(Depsgraph *depsgraph,
6243 Scene *scene,
6244 Object *ob,
6245 DynamicPaintSurface *surface,
6246 float timescale,
6247 float subframe)
6248{
6249 PaintSurfaceData *sData = surface->data;
6250 PaintBakeData *bData = sData->bData;
6251 DynamicPaintCanvasSettings *canvas = surface->canvas;
6252 const bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
6253 int ret = 1;
6254
6255 if (sData->total_points < 1) {
6256 return 0;
6257 }
6258
6261 data.surface = surface;
6262 data.timescale = timescale;
6263
6264 TaskParallelSettings settings;
6266 settings.use_threading = (sData->total_points > 1000);
6268 0, sData->total_points, &data, dynamic_paint_surface_pre_step_cb, &settings);
6269 }
6270
6271 /*
6272 * Loop through surface's target paint objects and do painting
6273 */
6274 {
6275 uint numobjects;
6277 depsgraph, nullptr, surface->brush_group, &numobjects, eModifierType_DynamicPaint);
6278
6279 /* backup current scene frame */
6280 int scene_frame = scene->r.cfra;
6281 float scene_subframe = scene->r.subframe;
6282
6283 for (int i = 0; i < numobjects; i++) {
6284 Object *brushObj = objects[i];
6285
6286 /* check if target has an active dp modifier */
6288 if (md && md->mode & (eModifierMode_Realtime | eModifierMode_Render)) {
6290 /* make sure we're dealing with a brush */
6291 if (pmd2->brush && pmd2->type == MOD_DYNAMICPAINT_TYPE_BRUSH) {
6293
6294 /* calculate brush speed vectors if required */
6295 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && brush->flags & MOD_DPAINT_DO_SMUDGE) {
6296 bData->brush_velocity = MEM_calloc_arrayN<float>(4 * size_t(sData->total_points),
6297 "Dynamic Paint brush velocity");
6298 /* init adjacency data if not already */
6299 if (!sData->adj_data) {
6300 dynamicPaint_initAdjacencyData(surface, true);
6301 }
6302 if (!bData->bNeighs) {
6303 dynamicPaint_prepareAdjacencyData(surface, true);
6304 }
6305 }
6306
6307 /* update object data on this subframe */
6308 if (subframe) {
6309 scene_setSubframe(scene, subframe);
6311 scene,
6312 brushObj,
6313 true,
6315 BKE_scene_ctime_get(scene),
6317 }
6318
6319 /* Apply brush on the surface depending on its collision type */
6320 if (brush->psys && brush->psys->part &&
6321 ELEM(brush->psys->part->type,
6323 PART_FLUID,
6333 psys_check_enabled(brushObj, brush->psys, for_render))
6334 {
6335 /* Paint a particle system */
6336 dynamicPaint_paintParticles(surface, brush->psys, brush, timescale);
6337 }
6338 /* Object center distance: */
6339 if (brush->collision == MOD_DPAINT_COL_POINT && brushObj != ob) {
6341 depsgraph, surface, brushObj->loc, brush, brushObj, scene, timescale);
6342 }
6343 /* Mesh volume/proximity: */
6344 else if (brushObj != ob) {
6345 dynamicPaint_paintMesh(depsgraph, surface, brush, brushObj, scene, timescale);
6346 }
6347
6348 /* reset object to its original state */
6349 if (subframe) {
6350 scene->r.cfra = scene_frame;
6351 scene->r.subframe = scene_subframe;
6353 scene,
6354 brushObj,
6355 true,
6357 BKE_scene_ctime_get(scene),
6359 }
6360
6361 /* process special brush effects, like smudge */
6362 if (bData->brush_velocity) {
6363 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT && brush->flags & MOD_DPAINT_DO_SMUDGE)
6364 {
6365 dynamicPaint_doSmudge(surface, brush, timescale);
6366 }
6367 MEM_freeN(bData->brush_velocity);
6368 bData->brush_velocity = nullptr;
6369 }
6370 }
6371 }
6372 }
6373
6375 }
6376
6377 /* surfaces operations that use adjacency data */
6378 if (sData->adj_data && bData->bNeighs) {
6379 /* wave type surface simulation step */
6380 if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
6381 dynamicPaint_doWaveStep(surface, timescale);
6382 }
6383
6384 /* paint surface effects */
6385 if (surface->effect && surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
6386 int steps = 1, s;
6387 PaintPoint *prevPoint;
6388 float *force = nullptr;
6389
6390 /* Allocate memory for surface previous points to read unchanged values from */
6391 prevPoint = MEM_malloc_arrayN<PaintPoint>(size_t(sData->total_points),
6392 "PaintSurfaceDataCopy");
6393 if (!prevPoint) {
6394 return setError(canvas, N_("Not enough free memory"));
6395 }
6396
6397 /* Prepare effects and get number of required steps */
6398 steps = dynamicPaint_prepareEffectStep(depsgraph, surface, scene, ob, &force, timescale);
6399 for (s = 0; s < steps; s++) {
6400 dynamicPaint_doEffectStep(surface, force, prevPoint, timescale, float(steps));
6401 }
6402
6403 /* Free temporary effect data */
6404 if (prevPoint) {
6405 MEM_freeN(prevPoint);
6406 }
6407 if (force) {
6408 MEM_freeN(force);
6409 }
6410 }
6411
6412 /* paint island border pixels */
6413 if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
6415 }
6416 }
6417
6418 return ret;
6419}
6420
6422 DynamicPaintSurface *surface, Depsgraph *depsgraph, Scene *scene, Object *cObject, int frame)
6423{
6424 float timescale = 1.0f;
6425
6426 /* Apply previous displace on evaluated-mesh if incremental surface. */
6427 if (surface->flags & MOD_DPAINT_DISP_INCREMENTAL) {
6429 }
6430
6431 /* update bake data */
6432 dynamicPaint_generateBakeData(surface, depsgraph, cObject);
6433
6434 /* don't do substeps for first frame */
6435 if (surface->substeps && (frame != surface->start_frame)) {
6436 int st;
6437 timescale = 1.0f / (surface->substeps + 1);
6438
6439 for (st = 1; st <= surface->substeps; st++) {
6440 float subframe = float(st) / (surface->substeps + 1);
6441 if (!dynamicPaint_doStep(depsgraph, scene, cObject, surface, timescale, subframe)) {
6442 return 0;
6443 }
6444 }
6445 }
6446
6447 return dynamicPaint_doStep(depsgraph, scene, cObject, surface, timescale, 0.0f);
6448}
void BKE_collision_objects_free(struct Object **objects)
struct Object ** BKE_collision_objects_create(struct Depsgraph *depsgraph, struct Object *self, struct Collection *collection, unsigned int *numcollobj, unsigned int modifier_type)
bool BKE_colorband_evaluate(const ColorBand *coba, float in, float out[4])
Definition colorband.cc:396
ColorBand * BKE_colorband_add(bool rangetype)
Definition colorband.cc:298
CustomData interface, see also DNA_customdata_types.h.
void CustomData_validate_layer_name(const CustomData *data, eCustomDataType type, blender::StringRef name, char *outname)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:825
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:806
int BKE_object_defgroup_name_index(const Object *ob, blender::StringRef name)
Definition deform.cc:591
#define DPAINT_PAINT_NEW
#define DPAINT_PAINT_DRY
#define DPAINT_WAVE_ISECT_CHANGED
#define DPAINT_WAVE_REFLECT_ONLY
#define DPAINT_PAINT_WET
#define DPAINT_WAVE_NONE
#define DPAINT_WAVE_OBSTACLE
struct EffectorWeights * BKE_effector_add_weights(struct Collection *collection)
Definition effect.cc:58
void BKE_effectors_free(struct ListBase *lb)
Definition effect.cc:361
void BKE_effectors_apply(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *wind_force, float *impulse)
Definition effect.cc:1106
struct ListBase * BKE_effectors_create(struct Depsgraph *depsgraph, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool use_rotation)
Definition effect.cc:306
void pd_point_from_loc(struct Scene *scene, float *loc, float *vel, int index, struct EffectedPoint *point)
Definition effect.cc:414
void BKE_image_pool_free(ImagePool *pool)
ImagePool * BKE_image_pool_new()
int BKE_image_path_ext_from_imtype_ensure(char *filepath, size_t filepath_maxncpy, char imtype)
void BKE_id_free(Main *bmain, void *idv)
@ LIB_ID_COPY_SET_COPIED_ON_WRITE
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
General operations, lookup, etc. for materials.
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
void BKE_mesh_vert_corner_tri_map_create(MeshElemMap **r_map, int **r_mem, int totvert, const blender::int3 *corner_tris, int tris_num, const int *corner_verts, int corners_num)
void BKE_modifier_path_init(char *path, int path_maxncpy, const char *name)
ModifierData * BKE_modifiers_findby_type(const Object *ob, ModifierType type)
General operations, lookup, etc. for blender objects.
void BKE_object_modifier_update_subframe(Depsgraph *depsgraph, Scene *scene, Object *ob, bool update_mesh, int parent_recursion_limit, float frame, int modifier_type)
bool psys_check_enabled(struct Object *ob, struct ParticleSystem *psys, bool use_render_params)
Definition particle.cc:710
void BKE_ptcache_id_time(PTCacheID *pid, struct Scene *scene, float cfra, int *startframe, int *endframe, float *timescale)
void BKE_ptcache_validate(struct PointCache *cache, int framenr)
void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, struct Object *ob, struct DynamicPaintSurface *surface)
struct PointCache * BKE_ptcache_add(struct ListBase *ptcaches)
int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old)
int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra)
void BKE_ptcache_free_list(struct ListBase *ptcaches)
#define PTCACHE_RESET_OUTDATED
int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *pid, int mode)
float BKE_scene_ctime_get(const Scene *scene)
Definition scene.cc:2370
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
File and directory operations.
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:452
#define BVH_RAYCAST_DIST_MAX
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
MINLINE float max_fff(float a, float b, float c)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float square_f(float a)
MINLINE float min_fff(float a, float b, float c)
MINLINE void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
MINLINE void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
#define M_PI_2
void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3])
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
int isect_point_tri_v2(const float pt[2], const float v1[2], const float v2[2], const float v3[2])
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:291
void mul_m4_v3(const float M[4][4], float r[3])
bool equals_m4m4(const float mat1[4][4], const float mat2[4][4])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_mat3_m4_v3(const float mat[4][4], float r[3])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_fl(float r[3], float f)
MINLINE void madd_v2_v2v2fl(float r[2], const float a[2], const float b[2], float f)
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float w[3])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
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)
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void interp_v4_v4v4v4(float p[4], const float v1[4], const float v2[4], const float v3[4], const float w[3])
MINLINE void negate_v3(float r[3])
MINLINE void mul_v3_v3v3(float r[3], const float v1[3], const float v2[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float line_point_side_v2(const float l1[2], const float l2[2], const float pt[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
ATTR_WARN_UNUSED_RESULT const size_t num
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
#define BLI_path_cmp
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define STRNCPY_UTF8(dst, src)
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
unsigned char uchar
unsigned int uint
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
#define CLAMP(a, b, c)
#define CLAMP_MAX(a, c)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
#define BLT_I18NCONTEXT_ID_BRUSH
#define CTX_DATA_(context, msgid)
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
#define CLOG_STR_ERROR(clg_ref, str)
Definition CLG_log.h:196
@ DAG_EVAL_RENDER
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
#define MAX_CUSTOMDATA_LAYER_NAME
@ CD_PROP_FLOAT2
@ MOD_DPAINT_INITIAL_NONE
@ MOD_DPAINT_INITIAL_VERTEXCOLOR
@ MOD_DPAINT_INITIAL_COLOR
@ MOD_DPAINT_INITIAL_TEXTURE
@ MOD_DPAINT_PROX_PROJECT
@ MOD_DPAINT_ABS_ALPHA
@ MOD_DPAINT_RAMP_ALPHA
@ MOD_DPAINT_INVERSE_PROX
@ MOD_DPAINT_USES_VELOCITY
@ MOD_DPAINT_VELOCITY_ALPHA
@ MOD_DPAINT_VELOCITY_DEPTH
@ MOD_DPAINT_VELOCITY_COLOR
@ MOD_DPAINT_DO_SMUDGE
@ MOD_DPAINT_PART_RAD
@ MOD_DPAINT_NEGATE_VOLUME
@ MOD_DPAINT_COL_DIST
@ MOD_DPAINT_COL_VOLDIST
@ MOD_DPAINT_COL_POINT
@ MOD_DPAINT_COL_VOLUME
@ MOD_DPAINT_ACTIVE
@ MOD_DPAINT_USE_DRYING
@ MOD_DPAINT_ANTIALIAS
@ MOD_DPAINT_WAVE_OPEN_BORDERS
@ MOD_DPAINT_DISP_INCREMENTAL
@ MOD_DPAINT_OUT1
@ MOD_DPAINT_DISSOLVE
@ MOD_DPAINT_MULALPHA
@ MOD_DPAINT_DISSOLVE_LOG
@ MOD_DPAINT_DRY_LOG
@ MOD_DPAINT_DISP_DISPLACE
@ MOD_DPAINT_WAVEB_REFLECT
@ MOD_DPAINT_WAVEB_DEPTH
@ MOD_DPAINT_WAVEB_CHANGE
@ MOD_DPAINT_WAVEB_FORCE
@ MOD_DPAINT_RAY_BRUSH_AVG
@ MOD_DPAINT_RAY_CANVAS
@ MOD_DPAINT_PRFALL_CONSTANT
@ MOD_DPAINT_PRFALL_SMOOTH
@ MOD_DPAINT_PRFALL_RAMP
@ MOD_DPAINT_EFFECT_DO_DRIP
@ MOD_DPAINT_EFFECT_DO_SPREAD
@ MOD_DPAINT_EFFECT_DO_SHRINK
@ MOD_DPAINT_SURFACE_T_WEIGHT
@ MOD_DPAINT_SURFACE_T_PAINT
@ MOD_DPAINT_SURFACE_T_DISPLACE
@ MOD_DPAINT_SURFACE_T_WAVE
@ MOD_DPAINT_BAKING
@ MOD_DPAINT_IMGFORMAT_OPENEXR
@ MOD_DPAINT_IMGFORMAT_PNG
@ MOD_DPAINT_SURFACE_F_PTEX
@ MOD_DPAINT_SURFACE_F_VERTEX
@ MOD_DPAINT_SURFACE_F_IMAGESEQ
@ eModifierFlag_SharedCaches
@ eModifierMode_Render
@ eModifierMode_Realtime
@ MOD_DYNAMICPAINT_TYPE_BRUSH
@ MOD_DYNAMICPAINT_TYPE_CANVAS
@ eModifierType_DynamicPaint
Object is a sort of wrapper for general info.
@ PART_UNBORN
@ PART_DIED
@ PARS_DEAD
@ PARS_UNBORN
@ PART_FLUID_FLIP
@ PART_EMITTER
@ PART_FLUID_BUBBLE
@ PART_FLUID_SPRAYBUBBLE
@ PART_FLUID_TRACER
@ PART_FLUID
@ PART_FLUID_FOAM
@ PART_FLUID_SPRAYFOAMBUBBLE
@ PART_FLUID_SPRAYFOAM
@ PART_FLUID_SPRAY
@ PART_FLUID_FOAMBUBBLE
@ PARS_UNEXIST
@ PTCACHE_BAKED
@ PTCACHE_DISK_CACHE
@ PTCACHE_REDO_NEEDED
@ R_IMF_EXR_CODEC_ZIP
@ PHYS_GLOBAL_GRAVITY
@ R_IMF_IMTYPE_OPENEXR
@ R_IMF_IMTYPE_PNG
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
bool IMB_save_image(ImBuf *ibuf, const char *filepath, const int flags)
Definition writeimage.cc:23
@ IMB_FTYPE_OPENEXR
@ IMB_FTYPE_PNG
@ IB_float_data
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE uint8_t atomic_fetch_and_and_uint8(uint8_t *p, uint8_t b)
ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b)
ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x)
volatile int lock
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert * v
BPy_StructRNA * depsgraph
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
constexpr bool is_empty() const
Definition BLI_span.hh:509
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup(const StringRef attribute_id) const
std::optional< AttributeMetaData > lookup_meta_data(StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
nullptr float
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
#define powf(x, y)
#define acosf(x)
static bool dynamicPaint_checkSurfaceData(const Scene *scene, DynamicPaintSurface *surface)
static bool dynamicPaint_paintMesh(Depsgraph *depsgraph, DynamicPaintSurface *surface, DynamicPaintBrushSettings *brush, Object *brushOb, Scene *scene, float timescale)
static void dynamic_paint_apply_surface_vpaint_cb(void *__restrict userdata, const int p_index, const TaskParallelTLS *__restrict)
void dynamicPaint_clearSurface(const Scene *scene, DynamicPaintSurface *surface)
static void dynamic_paint_set_init_color_tex_to_vcol_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static void dynamic_paint_paint_single_point_cb_ex(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void dynamic_paint_wave_step_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void freeGrid(PaintSurfaceData *data)
static void dynamicPaint_mixPaintColors(const DynamicPaintSurface *surface, const int index, const int paintFlags, const float paintColor[3], const float paintAlpha, const float paintWetness, const float timescale)
static bool surface_usesAdjDistance(DynamicPaintSurface *surface)
DynamicPaintSurface * dynamicPaint_createNewSurface(DynamicPaintCanvasSettings *canvas, Scene *scene)
static Mesh * dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *ob, Mesh *mesh)
static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrushSettings *brush, float timescale)
static void free_bakeData(PaintSurfaceData *data)
static void grid_cell_points_reduce(const void *__restrict userdata, void *__restrict chunk_join, void *__restrict chunk)
static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, float *force, PaintPoint *prevPoint, float timescale, float steps)
static void scene_setSubframe(Scene *scene, float subframe)
static const float gaussianTotal
static void dynamic_paint_border_cb(void *__restrict userdata, const int b_index, const TaskParallelTLS *__restrict)
static void dynamicPaint_mixWaveHeight(PaintWavePoint *wPoint, const DynamicPaintBrushSettings *brush, float isect_height)
#define NOT_FOUND
static bool boundsIntersect(Bounds3D *b1, Bounds3D *b2)
static bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *ob)
static int neighX[8]
static void dynamic_paint_effect_drip_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
void dynamicPaint_freeSurface(const DynamicPaintModifierData *pmd, DynamicPaintSurface *surface)
static void grid_bound_insert_cb_ex(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
static const float gaussianFactors[5]
static void dynamic_paint_output_surface_image_wave_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void dynamic_paint_output_surface_image_displace_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
bool dynamicPaint_createType(DynamicPaintModifierData *pmd, int type, Scene *scene)
static bool boundIntersectPoint(Bounds3D *b, const float point[3], const float radius)
static int dynamicPaint_prepareEffectStep(Depsgraph *depsgraph, DynamicPaintSurface *surface, Scene *scene, Object *ob, float **force, float timescale)
static void dynamic_paint_find_island_border(const DynamicPaintCreateUVSurfaceData *data, DynamicPaintFindIslandBorderData *bdata, int tri_index, const float pixel[2], int in_edge, int depth)
static void dynamic_paint_brush_velocity_compute_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static void surface_freeUnusedData(DynamicPaintSurface *surface)
static void surface_setUniqueOutputName(DynamicPaintSurface *surface, char *basename, int output)
static float dist_squared_to_corner_tris_uv_edges(const blender::Span< int3 > corner_tris, const blender::Span< blender::float2 > uv_map, int tri_index, const float point[2])
void dynamicPaintSurface_setUniqueName(DynamicPaintSurface *surface, const char *basename)
void dynamicPaint_cacheUpdateFrames(DynamicPaintSurface *surface)
static void dynamic_paint_paint_mesh_cell_point_cb_ex(void *__restrict userdata, const int id, const TaskParallelTLS *__restrict)
#define HIT_VOLUME
static int dynamic_paint_find_neighbor_pixel(const DynamicPaintCreateUVSurfaceData *data, const MeshElemMap *vert_to_tri_map, const int w, const int h, const int px, const int py, const int n_index)
static bool dynamicPaint_symmetrizeAdjData(PaintAdjData *ed, int active_points)
static void dynamic_paint_paint_particle_cell_point_cb_ex(void *__restrict userdata, const int id, const TaskParallelTLS *__restrict)
static int neighStraightX[8]
static void dynamicPaint_allocateSurfaceType(DynamicPaintSurface *surface)
#define EFF_MOVEMENT_PER_FRAME
static void dynamicPaint_doWaveStep(DynamicPaintSurface *surface, float timescale)
static Mesh * dynamicPaint_canvas_mesh_get(DynamicPaintCanvasSettings *canvas)
static void dynamicPaint_doBorderStep(DynamicPaintSurface *surface)
#define WAVE_TIME_FAC
static void dynamic_paint_output_surface_image_paint_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, const char *filepath, short output_layer)
#define CANVAS_REL_SIZE
static void dynamic_paint_effect_spread_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void mesh_tris_nearest_point_dp(void *userdata, int index, const float co[3], BVHTreeNearest *nearest)
static void dynamicPaint_prepareAdjacencyData(DynamicPaintSurface *surface, const bool force_init)
static bool surface_duplicateNameExists(DynamicPaintSurface *t_surface, const blender::StringRefNull name)
static void dynamic_paint_output_surface_image_wetmap_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static bool setError(DynamicPaintCanvasSettings *canvas, const char *string)
static DynamicPaintRuntime * dynamicPaint_Modifier_runtime_ensure(DynamicPaintModifierData *pmd)
static bool dynamic_paint_surface_needs_dry_dissolve(DynamicPaintSurface *surface)
static void dynamicPaint_brushMeshCalculateVelocity(Depsgraph *depsgraph, Scene *scene, Object *ob, DynamicPaintBrushSettings *brush, Vec3f **brushVel, float timescale)
#define MAX_WETNESS
#define HIT_PROXIMITY
#define JITTER_SAMPLES
static void dynamic_paint_surface_pre_step_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void dynamic_paint_prepare_effect_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void surface_determineForceTargetPoints(const PaintSurfaceData *sData, const int index, const float force[3], float closest_d[2], int closest_id[2])
bool dynamicPaint_resetSurface(const Scene *scene, DynamicPaintSurface *surface)
BLI_INLINE void value_dissolve(float *r_value, const float time, const float scale, const bool is_log)
int dynamicPaint_calculateFrame(DynamicPaintSurface *surface, Depsgraph *depsgraph, Scene *scene, Object *cObject, int frame)
static float getSurfaceDimension(PaintSurfaceData *sData)
static bool surface_duplicateOutputExists(DynamicPaintSurface *t_surface, const blender::StringRefNull name)
static void dynamic_paint_set_init_color_tex_to_imseq_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static int surface_totalSamples(DynamicPaintSurface *surface)
#define ADJ_ON_MESH_EDGE
static void dynamic_paint_generate_bake_data_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static bool dynamicPaint_generateBakeData(DynamicPaintSurface *surface, Depsgraph *depsgraph, Object *ob)
#define SUBFRAME_RECURSION
void dynamicPaintSurface_updateType(DynamicPaintSurface *surface)
static void dynamic_paint_apply_surface_wave_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
#define MIN_WETNESS
static void dynamicPaint_brushObjectCalculateVelocity(Depsgraph *depsgraph, Scene *scene, Object *ob, Vec3f *brushVel, float timescale)
void dynamicPaint_Modifier_copy(const DynamicPaintModifierData *pmd, DynamicPaintModifierData *tpmd, int flag)
static int neighY[8]
static void dynamic_paint_effect_shrink_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, Mesh *result)
DynamicPaintSurface * get_activeSurface(DynamicPaintCanvasSettings *canvas)
static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist)
static void grid_cell_points_cb_ex(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
static void mesh_tris_spherecast_dp(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
static void canvas_copyMesh(DynamicPaintCanvasSettings *canvas, Mesh *mesh)
int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface, float *progress, bool *do_update)
static void grid_bound_insert_reduce(const void *__restrict, void *__restrict chunk_join, void *__restrict chunk)
static void boundInsert(Bounds3D *b, const float point[3])
#define ADJ_BORDER_PIXEL
static void dynamic_paint_prepare_adjacency_cb(void *__restrict userdata, const int index, const TaskParallelTLS *__restrict)
static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const bool force_init)
void dynamicPaint_freeSurfaceData(DynamicPaintSurface *surface)
static int surface_getBrushFlags(DynamicPaintSurface *surface, Depsgraph *depsgraph)
void dynamicPaint_freeBrush(DynamicPaintModifierData *pmd)
#define BRUSH_USES_VELOCITY
static bool dynamicPaint_paintParticles(DynamicPaintSurface *surface, ParticleSystem *psys, DynamicPaintBrushSettings *brush, float timescale)
static void dynamic_paint_create_uv_surface_direct_cb(void *__restrict userdata, const int ty, const TaskParallelTLS *__restrict)
static bool meshBrush_boundsIntersect(Bounds3D *b1, Bounds3D *b2, DynamicPaintBrushSettings *brush, float brush_radius)
static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
void dynamicPaint_Modifier_free(DynamicPaintModifierData *pmd)
static void blendColors(const float t_color[3], const float t_alpha, const float s_color[3], const float s_alpha, float result[4])
static void surfaceGenerateGrid(DynamicPaintSurface *surface)
static int dynamicPaint_doStep(Depsgraph *depsgraph, Scene *scene, Object *ob, DynamicPaintSurface *surface, float timescale, float subframe)
static bool dynamicPaint_paintSinglePoint(Depsgraph *depsgraph, DynamicPaintSurface *surface, float *pointCoord, DynamicPaintBrushSettings *brush, Object *brushOb, Scene *scene, float timescale)
static int neighStraightY[8]
Mesh * dynamicPaint_Modifier_do(DynamicPaintModifierData *pmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *mesh)
void dynamicPaint_freeCanvas(DynamicPaintModifierData *pmd)
#define ON_MESH_EDGE
bool dynamicPaint_outputLayerExists(DynamicPaintSurface *surface, Object *ob, int output)
static void dynamic_paint_set_init_color_vcol_to_imseq_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static bool dynamicPaint_pointHasNeighbor(PaintAdjData *ed, int index, int neighbor)
static float mixColors(float a_color[3], float a_weight, const float b_color[3], float b_weight, float ratio)
void dynamicPaint_Modifier_free_runtime(DynamicPaintRuntime *runtime_data)
static bool surface_usesAdjData(DynamicPaintSurface *surface)
#define OUT_OF_TEXTURE
static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface)
static void dynamic_paint_create_uv_surface_neighbor_cb(void *__restrict userdata, const int ty, const TaskParallelTLS *__restrict)
static void dynamicPaint_setInitialColor(const Scene *, DynamicPaintSurface *surface)
static void dynamicPaint_updatePointData(const DynamicPaintSurface *surface, const int index, const DynamicPaintBrushSettings *brush, float paint[3], float influence, float depth, float vel_factor, const float timescale)
static void dynamicPaint_freeAdjData(PaintSurfaceData *data)
static void dynamic_paint_apply_surface_vpaint_blend_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static void grid_cell_bounds_cb(void *__restrict userdata, const int x, const TaskParallelTLS *__restrict)
static void dynamic_paint_apply_surface_displace_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
KDTree_3d * tree
#define str(s)
uint pos
uint nor
uint col
#define output
#define isnan
#define pow
#define floor
#define ceil
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
format
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
static ulong * next
static char faces[256]
static void error(const char *str)
float bvhtree_ray_tri_intersection(const BVHTreeRay *ray, float m_dist, const float v0[3], const float v1[3], const float v2[3])
Definition bvhutils.cc:28
VecBase< int32_t, 3 > int3
std::mutex Mutex
Definition BLI_mutex.hh:47
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
const char * name
return ret
#define mix
#define floorf
#define fabsf
#define sqrtf
#define ceilf
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
float min[2]
float max[2]
float min[3]
float max[3]
struct DynamicPaintModifierData * pmd
struct DynamicPaintModifierData * pmd
blender::Span< blender::float2 > uv_map
blender::Span< int3 > corner_tris
blender::Span< int > corner_verts
const DynamicPaintSurface * surface
const DynamicPaintSurface * surface
const DynamicPaintSurface * surface
const MeshElemMap * vert_to_tri_map
blender::Span< blender::float3 > positions
const DynamicPaintSurface * surface
blender::Span< blender::float3 > vert_normals
blender::Span< int > corner_verts
blender::MutableSpan< blender::float3 > vert_positions
const DynamicPaintSurface * surface
blender::Span< blender::float3 > vert_normals
blender::MutableSpan< blender::ColorGeometry4b > mloopcol_wet
blender::MutableSpan< blender::ColorGeometry4b > mloopcol
blender::OffsetIndices< int > faces
struct DynamicPaintCanvasSettings * canvas
struct DynamicPaintBrushSettings * brush
const DynamicPaintSurface * surface
blender::Span< blender::float3 > positions
blender::Span< int > corner_verts
const DynamicPaintBrushSettings * brush
const float * avg_brushNor
const ParticleSystem * psys
const DynamicPaintSurface * surface
const Vec3f * brushVelocity
blender::Span< int3 > corner_tris
blender::Mutex brush_mutex
blender::Span< blender::ColorGeometry4b > mloopcol
blender::Span< blender::float2 > uv_map
const DynamicPaintSurface * surface
blender::Span< int > corner_verts
blender::Span< int3 > corner_tris
struct DynamicPaintCanvasSettings * canvas
struct PaintSurfaceData * data
struct Collection * brush_group
struct DynamicPaintSurface * next
struct EffectorWeights * effector_weights
struct PointCache * pointcache
float vel_to_sec
Definition BKE_effect.h:30
ImBufFloatBuffer float_buffer
ImbFormatOptions foptions
enum eImbFileType ftype
PaintUVPoint * uv_p
Vec3f * barycentricWeights
void * first
int verts_num
float loc[3]
float scale[3]
struct PointCache * cache
BakeAdjPoint * bNeighs
Vec3f * prev_velocity
float prev_obmat[4][4]
float * brush_velocity
DynamicPaintVolumeGrid * grid
float(* prev_positions)[3]
Bounds3D mesh_bounds
PaintBakeNormal * bNormal
float e_color[4]
struct PaintBakeData * bData
struct PaintAdjData * adj_data
ParticleKey state
ParticleData * particles
ParticleSettings * part
struct PhysicsSettings physics_settings
struct RenderData r
TaskParallelReduceFunc func_reduce
Definition BLI_task.h:176
size_t userdata_chunk_size
Definition BLI_task.h:164
float tin
Definition RE_texture.h:59
float trgba[4]
Definition RE_texture.h:60
float v[3]
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
int multitex_ext_safe(Tex *tex, const float texvec[3], TexResult *texres, ImagePool *pool, bool scene_color_manage, const bool skip_load_image)
#define N_(msgid)
uint8_t flag
Definition wm_window.cc:145