Blender V4.5
paint_image_proj.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include <algorithm>
11#include <cfloat>
12#include <climits>
13#include <cmath>
14#include <cstring>
15
16#include "MEM_guardedalloc.h"
17
18#ifdef WIN32
19# include "BLI_winstuff.h"
20#endif
21
22#include "BLI_linklist.h"
23#include "BLI_listbase.h"
24#include "BLI_math_base_safe.h"
25#include "BLI_math_bits.h"
27#include "BLI_math_geom.h"
28#include "BLI_math_vector.hh"
29#include "BLI_memarena.h"
30#include "BLI_rect.h"
31#include "BLI_string.h"
32#include "BLI_string_utf8.h"
33#include "BLI_task.h"
34#include "BLI_threads.h"
35#include "BLI_utildefines.h"
36
37#include "atomic_ops.h"
38
39#include "BLT_translation.hh"
40
41#include "IMB_imbuf.hh"
42#include "IMB_interp.hh"
43
44#include "DNA_brush_types.h"
46#include "DNA_defs.h"
47#include "DNA_material_types.h"
48#include "DNA_mesh_types.h"
49#include "DNA_node_types.h"
50#include "DNA_object_enums.h"
51#include "DNA_object_types.h"
52#include "DNA_scene_types.h"
53
54#include "BKE_attribute.hh"
55#include "BKE_brush.hh"
56#include "BKE_camera.h"
57#include "BKE_colorband.hh"
58#include "BKE_colortools.hh"
59#include "BKE_context.hh"
60#include "BKE_customdata.hh"
61#include "BKE_global.hh"
62#include "BKE_idprop.hh"
63#include "BKE_image.hh"
64#include "BKE_layer.hh"
65#include "BKE_lib_id.hh"
66#include "BKE_library.hh"
67#include "BKE_main.hh"
69#include "BKE_material.hh"
70#include "BKE_mesh.hh"
71#include "BKE_mesh_mapping.hh"
72#include "BKE_mesh_runtime.hh"
73#include "BKE_node.hh"
75#include "BKE_node_runtime.hh"
76#include "BKE_object.hh"
77#include "BKE_paint.hh"
78#include "BKE_report.hh"
79#include "BKE_scene.hh"
80#include "BKE_screen.hh"
81#include "DNA_screen_types.h"
82#include "DNA_space_types.h"
83
84#include "DEG_depsgraph.hh"
86
87#include "ED_image.hh"
88#include "ED_node.hh"
89#include "ED_object.hh"
90#include "ED_paint.hh"
91#include "ED_screen.hh"
92#include "ED_sculpt.hh"
93#include "ED_uvedit.hh"
94#include "ED_view3d.hh"
96
97#include "GPU_capabilities.hh"
98#include "GPU_init_exit.hh"
99
100#include "NOD_shader.h"
101
102#include "UI_interface.hh"
103#include "UI_resources.hh"
104
105#include "WM_api.hh"
106#include "WM_types.hh"
107
108#include "RNA_access.hh"
109#include "RNA_define.hh"
110#include "RNA_enum_types.hh"
111#include "RNA_types.hh"
112
113#include "IMB_colormanagement.hh"
114
115// #include "bmesh_tools.hh"
116
117#include "paint_intern.hh"
118
119using blender::int3;
120
122
123/* Defines and Structs */
124/* unit_float_to_uchar_clamp as inline function */
125BLI_INLINE uchar f_to_char(const float val)
126{
127 return unit_float_to_uchar_clamp(val);
128}
129
130/* ProjectionPaint defines */
131
132/* approx the number of buckets to have under the brush,
133 * used with the brush size to set the ps->buckets_x and ps->buckets_y value.
134 *
135 * When 3 - a brush should have ~9 buckets under it at once
136 * ...this helps for threading while painting as well as
137 * avoiding initializing pixels that won't touch the brush */
138#define PROJ_BUCKET_BRUSH_DIV 4
139
140#define PROJ_BUCKET_RECT_MIN 4
141#define PROJ_BUCKET_RECT_MAX 256
142
143#define PROJ_BOUNDBOX_DIV 8
144#define PROJ_BOUNDBOX_SQUARED (PROJ_BOUNDBOX_DIV * PROJ_BOUNDBOX_DIV)
145
146// #define PROJ_DEBUG_PAINT 1
147// #define PROJ_DEBUG_NOSEAMBLEED 1
148// #define PROJ_DEBUG_PRINT_CLIP 1
149#define PROJ_DEBUG_WINCLIP 1
150
151#ifndef PROJ_DEBUG_NOSEAMBLEED
152/* projectFaceSeamFlags options */
153// #define PROJ_FACE_IGNORE (1<<0) /* When the face is hidden, back-facing or occluded. */
154// #define PROJ_FACE_INIT (1<<1) /* When we have initialized the faces data */
155
156/* If this face has a seam on any of its edges. */
157# define PROJ_FACE_SEAM0 (1 << 0)
158# define PROJ_FACE_SEAM1 (1 << 1)
159# define PROJ_FACE_SEAM2 (1 << 2)
160
161# define PROJ_FACE_NOSEAM0 (1 << 4)
162# define PROJ_FACE_NOSEAM1 (1 << 5)
163# define PROJ_FACE_NOSEAM2 (1 << 6)
164
165/* If the seam is completely initialized, including adjacent seams. */
166# define PROJ_FACE_SEAM_INIT0 (1 << 8)
167# define PROJ_FACE_SEAM_INIT1 (1 << 9)
168# define PROJ_FACE_SEAM_INIT2 (1 << 10)
169
170# define PROJ_FACE_DEGENERATE (1 << 12)
171
172/* face winding */
173# define PROJ_FACE_WINDING_INIT 1
174# define PROJ_FACE_WINDING_CW 2
175
176/* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams
177 * as this number approaches 1.0f the likelihood increases of float precision errors where
178 * it is occluded by an adjacent face */
179# define PROJ_FACE_SCALE_SEAM 0.99f
180#endif /* PROJ_DEBUG_NOSEAMBLEED */
181
182#define PROJ_SRC_VIEW 1
183#define PROJ_SRC_IMAGE_CAM 2
184#define PROJ_SRC_IMAGE_VIEW 3
185#define PROJ_SRC_VIEW_FILL 4
186
187#define PROJ_VIEW_DATA_ID "view_data"
188/* viewmat + winmat + clip_start + clip_end + is_ortho */
189#define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3)
190
191#define PROJ_BUCKET_NULL 0
192#define PROJ_BUCKET_INIT (1 << 0)
193// #define PROJ_BUCKET_CLONE_INIT (1<<1)
194
195/* used for testing doubles, if a point is on a line etc */
196#define PROJ_GEOM_TOLERANCE 0.00075f
197#define PROJ_PIXEL_TOLERANCE 0.01f
198
199/* vert flags */
200#define PROJ_VERT_CULL 1
201
202/* to avoid locking in tile initialization */
203#define TILE_PENDING POINTER_FROM_INT(-1)
204
205struct ProjPaintState;
206
226
231 /* Support for painting from multiple views at once,
232 * currently used to implement symmetry painting,
233 * we can assume at least the first is set while painting. */
235
236 /* Store initial starting points for perlin noise on the beginning of each stroke when using
237 * color jitter. */
238 std::optional<blender::float3> initial_hsv_jitter;
239
242
244
246
247 /* trick to bypass regular paint and allow clone picking */
249
250 /* In ProjPaintState, only here for convenience */
253};
254
256 float seam_uvs[2][2];
257 float seam_puvs[2][2];
259};
260
261/* Main projection painting struct passed to all projection painting functions */
266 Depsgraph *depsgraph;
268 /* PROJ_SRC_**** */
270
271 /* the paint color. It can change depending of inverted mode or not */
272 float paint_color[3];
274 float dither;
275
276 const Paint *paint;
278
284 short blend;
286
289 /* for symmetry, we need to store modified object matrix */
290 float obmat[4][4];
291 float obmat_imat[4][4];
292 /* end similarities with ImagePaintState */
293
298
299 /* projection painting only */
308
315
318
321
323 float (*screenCoords)[4];
325 float screenMin[2];
326 float screenMax[2];
331 int winx, winy;
332
333 /* options for projection painting */
339
357
369#ifndef PROJ_DEBUG_NOSEAMBLEED
372#endif
373 /* clone vars */
374 float cloneOffset[2];
375
377 float projectMat[4][4];
379 float projectMatInv[4][4];
381 float viewDir[3];
383 float viewPos[3];
385
386 /* reproject vars */
391
392 /* threads */
394 int bucketMin[2];
395 int bucketMax[2];
398
401
402 /* -------------------------------------------------------------------- */
403 /* Vars shared between multiple views (keep last) */
408
409#define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src) \
410 MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user)
411
412#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) MEMSET_STRUCT_AFTER(ps, 0, is_shared_user)
413
415
418 float *cavities;
419
420#ifndef PROJ_DEBUG_NOSEAMBLEED
432#endif
433
435
440
446 const bool *select_poly_eval;
447 const bool *hide_poly_eval;
449 const bool *sharp_faces_eval;
452
453 const float (*mloopuv_stencil_eval)[2];
454
460 const float (**poly_to_loop_uv)[2];
462 const float (**poly_to_loop_uv_clone)[2];
463
464 /* Actual material for each index, either from object or Mesh datablock... */
466};
467
475
479 float f[4];
480};
481
482struct ProjPixel {
484 float projCoSS[2];
485 float worldCoSS[3];
486
487 short x_px, y_px;
488
492
493 /* for various reasons we may want to mask out painting onto this pixel */
495
496 /* Only used when the airbrush is disabled.
497 * Store the max mask value to avoid painting over an area with a lower opacity
498 * with an advantage that we can avoid touching the pixel at all, if the
499 * new mask value is lower than mask_accum */
501
502 /* horrible hack, store tile valid flag pointer here to re-validate tiles
503 * used for anchored and drag-dot strokes */
504 bool *valid;
505
509};
510
515
516/* undo tile pushing */
524
525struct VertSeam {
527 int tri;
529 float angle;
531 float uv[2];
532};
533
534/* -------------------------------------------------------------------- */
537
538#define PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri) \
539 ps->corner_verts_eval[tri[0]], ps->corner_verts_eval[tri[1]], ps->corner_verts_eval[tri[2]],
540
541#define PS_CORNER_TRI_AS_UV_3(uvlayer, face_i, tri) \
542 uvlayer[face_i][tri[0]], uvlayer[face_i][tri[1]], uvlayer[face_i][tri[2]],
543
544#define PS_CORNER_TRI_ASSIGN_UV_3(uv_tri, uvlayer, face_i, tri) \
545 { \
546 (uv_tri)[0] = uvlayer[face_i][tri[0]]; \
547 (uv_tri)[1] = uvlayer[face_i][tri[1]]; \
548 (uv_tri)[2] = uvlayer[face_i][tri[2]]; \
549 } \
550 ((void)0)
551
553
554/* Finish projection painting structs */
555
556static int project_paint_face_paint_tile(Image *ima, const float *uv)
557{
558 if (ima == nullptr || ima->source != IMA_SRC_TILED) {
559 return 0;
560 }
561
562 /* Currently, faces are assumed to belong to one tile, so checking the first loop is enough. */
563 int tx = int(uv[0]);
564 int ty = int(uv[1]);
565 return 1001 + 10 * ty + tx;
566}
567
568static Material *tex_get_material(const ProjPaintState *ps, int face_i)
569{
570 int mat_nr = ps->material_indices == nullptr ? 0 : ps->material_indices[face_i];
571 if (mat_nr >= 0 && mat_nr <= ps->ob->totcol) {
572 return ps->mat_array[mat_nr];
573 }
574
575 return nullptr;
576}
577
579{
580 const int face_i = ps->corner_tri_faces_eval[tri_index];
581 Material *ma = tex_get_material(ps, face_i);
582 return ma ? ma->texpaintslot + ma->paint_active_slot : nullptr;
583}
584
585static Image *project_paint_face_paint_image(const ProjPaintState *ps, int tri_index)
586{
587 if (ps->do_stencil_brush) {
588 return ps->stencil_ima;
589 }
590
591 const int face_i = ps->corner_tri_faces_eval[tri_index];
592 Material *ma = tex_get_material(ps, face_i);
593 TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : nullptr;
594 return slot ? slot->ima : ps->canvas_ima;
595}
596
598{
599 const int face_i = ps->corner_tri_faces_eval[tri_index];
600 Material *ma = tex_get_material(ps, face_i);
601 return ma ? ma->texpaintslot + ma->paint_clone_slot : nullptr;
602}
603
604static Image *project_paint_face_clone_image(const ProjPaintState *ps, int tri_index)
605{
606 const int face_i = ps->corner_tri_faces_eval[tri_index];
607 Material *ma = tex_get_material(ps, face_i);
608 TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : nullptr;
609 return slot ? slot->ima : ps->clone_ima;
610}
611
615static int project_bucket_offset(const ProjPaintState *ps, const float projCoSS[2])
616{
617 /* If we were not dealing with screen-space 2D coords we could simple do...
618 * ps->bucketRect[x + (y*ps->buckets_y)] */
619
620 /* please explain?
621 * projCoSS[0] - ps->screenMin[0] : zero origin
622 * ... / ps->screen_width : range from 0.0 to 1.0
623 * ... * ps->buckets_x : use as a bucket index
624 *
625 * Second multiplication does similar but for vertical offset
626 */
627 return int(((projCoSS[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) +
628 (int(((projCoSS[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) *
629 ps->buckets_x);
630}
631
632static int project_bucket_offset_safe(const ProjPaintState *ps, const float projCoSS[2])
633{
634 int bucket_index = project_bucket_offset(ps, projCoSS);
635
636 if (bucket_index < 0 || bucket_index >= ps->buckets_x * ps->buckets_y) {
637 return -1;
638 }
639 return bucket_index;
640}
641
642static float VecZDepthOrtho(
643 const float pt[2], const float v1[3], const float v2[3], const float v3[3], float w[3])
644{
645 barycentric_weights_v2(v1, v2, v3, pt, w);
646 return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]);
647}
648
649static float VecZDepthPersp(
650 const float pt[2], const float v1[4], const float v2[4], const float v3[4], float w[3])
651{
652 float wtot_inv, wtot;
653 float w_tmp[3];
654
655 barycentric_weights_v2_persp(v1, v2, v3, pt, w);
656 /* for the depth we need the weights to match what
657 * barycentric_weights_v2 would return, in this case its easiest just to
658 * undo the 4th axis division and make it unit-sum
659 *
660 * don't call barycentric_weights_v2() because our callers expect 'w'
661 * to be weighted from the perspective */
662 w_tmp[0] = w[0] * v1[3];
663 w_tmp[1] = w[1] * v2[3];
664 w_tmp[2] = w[2] * v3[3];
665
666 wtot = w_tmp[0] + w_tmp[1] + w_tmp[2];
667
668 if (wtot != 0.0f) {
669 wtot_inv = 1.0f / wtot;
670
671 w_tmp[0] = w_tmp[0] * wtot_inv;
672 w_tmp[1] = w_tmp[1] * wtot_inv;
673 w_tmp[2] = w_tmp[2] * wtot_inv;
674 }
675 else { /* dummy values for zero area face */
676 w_tmp[0] = w_tmp[1] = w_tmp[2] = 1.0f / 3.0f;
677 }
678 /* done mimicking barycentric_weights_v2() */
679
680 return (v1[2] * w_tmp[0]) + (v2[2] * w_tmp[1]) + (v3[2] * w_tmp[2]);
681}
682
683/* Return the top-most face index that the screen space coord 'pt' touches (or -1) */
684static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], float w[3])
685{
686 LinkNode *node;
687 float w_tmp[3];
688 int bucket_index;
689 int best_tri_index = -1;
690 float z_depth_best = FLT_MAX, z_depth;
691
692 bucket_index = project_bucket_offset_safe(ps, pt);
693 if (bucket_index == -1) {
694 return -1;
695 }
696
697 /* we could return 0 for 1 face buckets, as long as this function assumes
698 * that the point its testing is only every originated from an existing face */
699
700 for (node = ps->bucketFaces[bucket_index]; node; node = node->next) {
701 const int tri_index = POINTER_AS_INT(node->link);
702 const int3 &tri = ps->corner_tris_eval[tri_index];
703 const float *vtri_ss[3] = {
704 ps->screenCoords[ps->corner_verts_eval[tri[0]]],
705 ps->screenCoords[ps->corner_verts_eval[tri[1]]],
706 ps->screenCoords[ps->corner_verts_eval[tri[2]]],
707 };
708
709 if (isect_point_tri_v2(pt, UNPACK3(vtri_ss))) {
710 if (ps->is_ortho) {
711 z_depth = VecZDepthOrtho(pt, UNPACK3(vtri_ss), w_tmp);
712 }
713 else {
714 z_depth = VecZDepthPersp(pt, UNPACK3(vtri_ss), w_tmp);
715 }
716
717 if (z_depth < z_depth_best) {
718 best_tri_index = tri_index;
719 z_depth_best = z_depth;
720 copy_v3_v3(w, w_tmp);
721 }
722 }
723 }
724
726 return best_tri_index;
727}
728
729/* Set the top-most face color that the screen space coord 'pt' touches
730 * (or return 0 if none touch) */
732 const ProjPaintState *ps, const float pt[2], float *rgba_fp, uchar *rgba, const bool interp)
733{
734 using namespace blender;
735 const float *tri_uv[3];
736 float w[3], uv[2];
737 int tri_index;
738 Image *ima;
739 ImBuf *ibuf;
740
741 tri_index = project_paint_PickFace(ps, pt, w);
742
743 if (tri_index == -1) {
744 return false;
745 }
746
747 const int3 &tri = ps->corner_tris_eval[tri_index];
749 tri_uv, ps->poly_to_loop_uv, ps->corner_tri_faces_eval[tri_index], tri);
750
751 interp_v2_v2v2v2(uv, UNPACK3(tri_uv), w);
752
753 ima = project_paint_face_paint_image(ps, tri_index);
755 int tile_number = project_paint_face_paint_tile(ima, tri_uv[0]);
756 /* XXX get appropriate ImageUser instead */
757 ImageUser iuser;
758 BKE_imageuser_default(&iuser);
759 iuser.tile = tile_number;
760 iuser.framenr = ima->lastframe;
761 ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
762 if (ibuf == nullptr) {
763 return false;
764 }
765
766 float x = uv[0] * ibuf->x;
767 float y = uv[1] * ibuf->y;
768 if (interp) {
769 x -= 0.5f;
770 y -= 0.5f;
771 }
772
773 if (ibuf->float_buffer.data) {
776 col = math::clamp(col, 0.0f, 1.0f);
777 if (rgba_fp) {
778 memcpy(rgba_fp, &col, sizeof(col));
779 }
780 else {
782 }
783 }
784 else {
787 if (rgba) {
788 memcpy(rgba, &col, sizeof(col));
789 }
790 else {
792 }
793 }
794 BKE_image_release_ibuf(ima, ibuf, nullptr);
795 return true;
796}
797
806static int project_paint_occlude_ptv(const float pt[3],
807 const float v1[4],
808 const float v2[4],
809 const float v3[4],
810 float w[3],
811 const bool is_ortho)
812{
813 /* if all are behind us, return false */
814 if (v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2]) {
815 return 0;
816 }
817
818 /* do a 2D point in try intersection */
819 if (!isect_point_tri_v2(pt, v1, v2, v3)) {
820 return 0;
821 }
822
823 /* From here on we know there IS an intersection */
824 /* if ALL of the verts are in front of us then we know it intersects ? */
825 if (v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) {
826 return 1;
827 }
828
829 /* we intersect? - find the exact depth at the point of intersection */
830 /* Is this point is occluded by another face? */
831 if (is_ortho) {
832 if (VecZDepthOrtho(pt, v1, v2, v3, w) < pt[2]) {
833 return 2;
834 }
835 }
836 else {
837 if (VecZDepthPersp(pt, v1, v2, v3, w) < pt[2]) {
838 return 2;
839 }
840 }
841 return -1;
842}
843
844static int project_paint_occlude_ptv_clip(const float pt[3],
845 const float v1[4],
846 const float v2[4],
847 const float v3[4],
848 const float v1_3d[3],
849 const float v2_3d[3],
850 const float v3_3d[3],
851 float w[3],
852 const bool is_ortho,
853 RegionView3D *rv3d)
854{
855 float wco[3];
856 int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, is_ortho);
857
858 if (ret <= 0) {
859 return ret;
860 }
861
862 if (ret == 1) { /* weights not calculated */
863 if (is_ortho) {
864 barycentric_weights_v2(v1, v2, v3, pt, w);
865 }
866 else {
867 barycentric_weights_v2_persp(v1, v2, v3, pt, w);
868 }
869 }
870
871 /* Test if we're in the clipped area, */
872 interp_v3_v3v3v3(wco, v1_3d, v2_3d, v3_3d, w);
873
874 if (!ED_view3d_clipping_test(rv3d, wco, true)) {
875 return 1;
876 }
877
878 return -1;
879}
880
881/* Check if a screen-space location is occluded by any other faces
882 * check, pixelScreenCo must be in screen-space, its Z-Depth only needs to be used for comparison
883 * and doesn't need to be correct in relation to X and Y coords
884 * (this is the case in perspective view) */
886 LinkNode *bucketFace,
887 const int orig_face,
888 const float pixelScreenCo[4])
889{
890 int isect_ret;
891 const bool do_clip = RV3D_CLIPPING_ENABLED(ps->v3d, ps->rv3d);
892
893 /* we could return false for 1 face buckets, as long as this function assumes
894 * that the point its testing is only every originated from an existing face */
895
896 for (; bucketFace; bucketFace = bucketFace->next) {
897 const int tri_index = POINTER_AS_INT(bucketFace->link);
898
899 if (orig_face != tri_index) {
900 const int3 &tri = ps->corner_tris_eval[tri_index];
901 const float *vtri_ss[3] = {
902 ps->screenCoords[ps->corner_verts_eval[tri[0]]],
903 ps->screenCoords[ps->corner_verts_eval[tri[1]]],
904 ps->screenCoords[ps->corner_verts_eval[tri[2]]],
905 };
906 float w[3];
907
908 if (do_clip) {
909 const float *vtri_co[3] = {
910 ps->vert_positions_eval[ps->corner_verts_eval[tri[0]]],
911 ps->vert_positions_eval[ps->corner_verts_eval[tri[1]]],
912 ps->vert_positions_eval[ps->corner_verts_eval[tri[2]]],
913 };
915 pixelScreenCo, UNPACK3(vtri_ss), UNPACK3(vtri_co), w, ps->is_ortho, ps->rv3d);
916 }
917 else {
918 isect_ret = project_paint_occlude_ptv(pixelScreenCo, UNPACK3(vtri_ss), w, ps->is_ortho);
919 }
920
921 if (isect_ret >= 1) {
922 /* TODO: we may want to cache the first hit,
923 * it is not possible to swap the face order in the list anymore */
924 return true;
925 }
926 }
927 }
928 return false;
929}
930
931/* Basic line intersection, could move to math_geom.c, 2 points with a horizontal line
932 * 1 for an intersection, 2 if the first point is aligned, 3 if the second point is aligned. */
933#define ISECT_TRUE 1
934#define ISECT_TRUE_P1 2
935#define ISECT_TRUE_P2 3
936static int line_isect_y(const float p1[2], const float p2[2], const float y_level, float *x_isect)
937{
938 float y_diff;
939
940 /* are we touching the first point? - no interpolation needed */
941 if (y_level == p1[1]) {
942 *x_isect = p1[0];
943 return ISECT_TRUE_P1;
944 }
945 /* are we touching the second point? - no interpolation needed */
946 if (y_level == p2[1]) {
947 *x_isect = p2[0];
948 return ISECT_TRUE_P2;
949 }
950
952 y_diff = fabsf(p1[1] - p2[1]);
953
954 if (y_diff < 0.000001f) {
955 *x_isect = (p1[0] + p2[0]) * 0.5f;
956 return ISECT_TRUE;
957 }
958
959 if (p1[1] > y_level && p2[1] < y_level) {
960 /* `p1[1] - p2[1]`. */
961 *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff;
962 return ISECT_TRUE;
963 }
964 if (p1[1] < y_level && p2[1] > y_level) {
965 /* `p2[1] - p1[1]`. */
966 *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff;
967 return ISECT_TRUE;
968 }
969 return 0;
970}
971
972static int line_isect_x(const float p1[2], const float p2[2], const float x_level, float *y_isect)
973{
974 float x_diff;
975
976 if (x_level == p1[0]) { /* are we touching the first point? - no interpolation needed */
977 *y_isect = p1[1];
978 return ISECT_TRUE_P1;
979 }
980 if (x_level == p2[0]) { /* are we touching the second point? - no interpolation needed */
981 *y_isect = p2[1];
982 return ISECT_TRUE_P2;
983 }
984
985 /* yuck, horizontal line, we can't do much here */
986 x_diff = fabsf(p1[0] - p2[0]);
987
988 /* yuck, vertical line, we can't do much here */
989 if (x_diff < 0.000001f) {
990 *y_isect = (p1[0] + p2[0]) * 0.5f;
991 return ISECT_TRUE;
992 }
993
994 if (p1[0] > x_level && p2[0] < x_level) {
995 /* `p1[0] - p2[0]`. */
996 *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff;
997 return ISECT_TRUE;
998 }
999 if (p1[0] < x_level && p2[0] > x_level) {
1000 /* `p2[0] - p1[0]`. */
1001 *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff;
1002 return ISECT_TRUE;
1003 }
1004 return 0;
1005}
1006
1007/* simple func use for comparing UV locations to check if there are seams.
1008 * Its possible this gives incorrect results, when the UVs for 1 face go into the next
1009 * tile, but do not do this for the adjacent face, it could return a false positive.
1010 * This is so unlikely that Id not worry about it. */
1011#ifndef PROJ_DEBUG_NOSEAMBLEED
1012static bool cmp_uv(const float vec2a[2], const float vec2b[2])
1013{
1014 /* if the UVs are not between 0.0 and 1.0 */
1015 float xa = fmodf(vec2a[0], 1.0f);
1016 float ya = fmodf(vec2a[1], 1.0f);
1017
1018 float xb = fmodf(vec2b[0], 1.0f);
1019 float yb = fmodf(vec2b[1], 1.0f);
1020
1021 if (xa < 0.0f) {
1022 xa += 1.0f;
1023 }
1024 if (ya < 0.0f) {
1025 ya += 1.0f;
1026 }
1027
1028 if (xb < 0.0f) {
1029 xb += 1.0f;
1030 }
1031 if (yb < 0.0f) {
1032 yb += 1.0f;
1033 }
1034
1035 return ((fabsf(xa - xb) < PROJ_GEOM_TOLERANCE) && (fabsf(ya - yb) < PROJ_GEOM_TOLERANCE)) ?
1036 true :
1037 false;
1038}
1039#endif
1040
1041/* set min_px and max_px to the image space bounds of the UV coords
1042 * return zero if there is no area in the returned rectangle */
1043#ifndef PROJ_DEBUG_NOSEAMBLEED
1044static bool pixel_bounds_uv(const float uv_quad[4][2],
1045 const int ibuf_x,
1046 const int ibuf_y,
1047 rcti *r_bounds_px)
1048{
1049 /* UV bounds */
1050 float min_uv[2], max_uv[2];
1051
1052 INIT_MINMAX2(min_uv, max_uv);
1053
1054 minmax_v2v2_v2(min_uv, max_uv, uv_quad[0]);
1055 minmax_v2v2_v2(min_uv, max_uv, uv_quad[1]);
1056 minmax_v2v2_v2(min_uv, max_uv, uv_quad[2]);
1057 minmax_v2v2_v2(min_uv, max_uv, uv_quad[3]);
1058
1059 r_bounds_px->xmin = int(ibuf_x * min_uv[0]);
1060 r_bounds_px->ymin = int(ibuf_y * min_uv[1]);
1061
1062 r_bounds_px->xmax = int(ibuf_x * max_uv[0]) + 1;
1063 r_bounds_px->ymax = int(ibuf_y * max_uv[1]) + 1;
1064
1065 // printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);
1066
1067 /* face uses no UV area when quantized to pixels? */
1068 return (r_bounds_px->xmin == r_bounds_px->xmax || r_bounds_px->ymin == r_bounds_px->ymax) ?
1069 false :
1070 true;
1071}
1072#endif
1073
1075 float (*uv)[2], const int ibuf_x, const int ibuf_y, int tot, rcti *r_bounds_px)
1076{
1077 /* UV bounds */
1078 float min_uv[2], max_uv[2];
1079
1080 if (tot == 0) {
1081 return false;
1082 }
1083
1084 INIT_MINMAX2(min_uv, max_uv);
1085
1086 while (tot--) {
1087 minmax_v2v2_v2(min_uv, max_uv, (*uv));
1088 uv++;
1089 }
1090
1091 r_bounds_px->xmin = int(ibuf_x * min_uv[0]);
1092 r_bounds_px->ymin = int(ibuf_y * min_uv[1]);
1093
1094 r_bounds_px->xmax = int(ibuf_x * max_uv[0]) + 1;
1095 r_bounds_px->ymax = int(ibuf_y * max_uv[1]) + 1;
1096
1097 // printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);
1098
1099 /* face uses no UV area when quantized to pixels? */
1100 return (r_bounds_px->xmin == r_bounds_px->xmax || r_bounds_px->ymin == r_bounds_px->ymax) ?
1101 false :
1102 true;
1103}
1104
1105#ifndef PROJ_DEBUG_NOSEAMBLEED
1106
1107static void project_face_winding_init(const ProjPaintState *ps, const int tri_index)
1108{
1109 /* detect the winding of faces in uv space */
1110 const int3 &tri = ps->corner_tris_eval[tri_index];
1111 const int face_i = ps->corner_tri_faces_eval[tri_index];
1112 const float *tri_uv[3] = {PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, face_i, tri)};
1113 float winding = cross_tri_v2(tri_uv[0], tri_uv[1], tri_uv[2]);
1114
1115 if (winding > 0) {
1116 ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_CW;
1117 }
1118
1119 ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_INIT;
1120}
1121
1122/* This function returns 1 if this face has a seam along the 2 face-vert indices
1123 * 'orig_i1_fidx' and 'orig_i2_fidx' */
1124static bool check_seam(const ProjPaintState *ps,
1125 const int orig_face,
1126 const int orig_i1_fidx,
1127 const int orig_i2_fidx,
1128 int *other_face,
1129 int *orig_fidx)
1130{
1131 const int3 &orig_tri = ps->corner_tris_eval[orig_face];
1132 const int orig_poly_i = ps->corner_tri_faces_eval[orig_face];
1133 const float *orig_tri_uv[3] = {
1134 PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, orig_poly_i, orig_tri)};
1135 /* vert indices from face vert order indices */
1136 const uint i1 = ps->corner_verts_eval[orig_tri[orig_i1_fidx]];
1137 const uint i2 = ps->corner_verts_eval[orig_tri[orig_i2_fidx]];
1138 LinkNode *node;
1139 /* index in face */
1140 int i1_fidx = -1, i2_fidx = -1;
1141
1142 for (node = ps->vertFaces[i1]; node; node = node->next) {
1143 const int tri_index = POINTER_AS_INT(node->link);
1144
1145 if (tri_index != orig_face) {
1146 const int3 &tri = ps->corner_tris_eval[tri_index];
1147 const int face_i = ps->corner_tri_faces_eval[tri_index];
1148 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)};
1149 /* could check if the 2 faces images match here,
1150 * but then there wouldn't be a way to return the opposite face's info */
1151
1152 /* We need to know the order of the verts in the adjacent face
1153 * set the i1_fidx and i2_fidx to (0,1,2,3) */
1154 i1_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(vert_tri, i1);
1155 i2_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(vert_tri, i2);
1156
1157 /* Only need to check if 'i2_fidx' is valid because
1158 * we know i1_fidx is the same vert on both faces. */
1159 if (i2_fidx != -1) {
1160 const float *tri_uv[3] = {PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, face_i, tri)};
1161 Image *tpage = project_paint_face_paint_image(ps, tri_index);
1162 Image *orig_tpage = project_paint_face_paint_image(ps, orig_face);
1163 int tile = project_paint_face_paint_tile(tpage, tri_uv[0]);
1164 int orig_tile = project_paint_face_paint_tile(orig_tpage, orig_tri_uv[0]);
1165
1166 BLI_assert(i1_fidx != -1);
1167
1168 /* This IS an adjacent face!, now lets check if the UVs are ok */
1169
1170 /* set up the other face */
1171 *other_face = tri_index;
1172
1173 /* we check if difference is 1 here, else we might have a case of edge 2-0 for a tri */
1174 *orig_fidx = (i1_fidx < i2_fidx && (i2_fidx - i1_fidx == 1)) ? i1_fidx : i2_fidx;
1175
1176 /* initialize face winding if needed */
1177 if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) {
1178 project_face_winding_init(ps, tri_index);
1179 }
1180
1181 /* first test if they have the same image */
1182 if ((orig_tpage == tpage) && (orig_tile == tile) &&
1183 cmp_uv(orig_tri_uv[orig_i1_fidx], tri_uv[i1_fidx]) &&
1184 cmp_uv(orig_tri_uv[orig_i2_fidx], tri_uv[i2_fidx]))
1185 {
1186 /* if faces don't have the same winding in uv space,
1187 * they are on the same side so edge is boundary */
1188 if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW) !=
1189 (ps->faceWindingFlags[orig_face] & PROJ_FACE_WINDING_CW))
1190 {
1191 return true;
1192 }
1193
1194 // printf("SEAM (NONE)\n");
1195 return false;
1196 }
1197 // printf("SEAM (UV GAP)\n");
1198 return true;
1199 }
1200 }
1201 }
1202 // printf("SEAM (NO FACE)\n");
1203 *other_face = -1;
1204 return true;
1205}
1206
1208 uint loop_index,
1209 uint vert_index,
1210 VertSeam **r_seam)
1211{
1212 ListBase *vert_seams = &ps->vertSeams[vert_index];
1213 VertSeam *seam = static_cast<VertSeam *>(vert_seams->first);
1214 VertSeam *adjacent = nullptr;
1215
1216 while (seam->loop != loop_index) {
1217 seam = seam->next;
1218 }
1219
1220 if (r_seam) {
1221 *r_seam = seam;
1222 }
1223
1224 /* Circulate through the (sorted) vert seam array, in the direction of the seam normal,
1225 * until we find the first opposing seam, matching in UV space. */
1226 if (seam->normal_cw) {
1227 LISTBASE_CIRCULAR_BACKWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) {
1228 if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) {
1229 break;
1230 }
1231 }
1232 LISTBASE_CIRCULAR_BACKWARD_END(VertSeam *, vert_seams, adjacent, seam);
1233 }
1234 else {
1235 LISTBASE_CIRCULAR_FORWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) {
1236 if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) {
1237 break;
1238 }
1239 }
1240 LISTBASE_CIRCULAR_FORWARD_END(VertSeam *, vert_seams, adjacent, seam);
1241 }
1242
1243 BLI_assert(adjacent);
1244
1245 return adjacent;
1246}
1247
1248/* Computes the normal of two seams at their intersection,
1249 * and returns the angle between the seam and its normal. */
1250static float compute_seam_normal(VertSeam *seam, VertSeam *adj, float r_no[2])
1251{
1252 const float PI_2 = M_PI * 2.0f;
1253 float angle[2];
1254 float angle_rel, angle_no;
1255
1256 if (seam->normal_cw) {
1257 angle[0] = adj->angle;
1258 angle[1] = seam->angle;
1259 }
1260 else {
1261 angle[0] = seam->angle;
1262 angle[1] = adj->angle;
1263 }
1264
1265 angle_rel = angle[1] - angle[0];
1266
1267 if (angle_rel < 0.0f) {
1268 angle_rel += PI_2;
1269 }
1270
1271 angle_rel *= 0.5f;
1272
1273 angle_no = angle_rel + angle[0];
1274
1275 if (angle_no > M_PI) {
1276 angle_no -= PI_2;
1277 }
1278
1279 r_no[0] = cosf(angle_no);
1280 r_no[1] = sinf(angle_no);
1281
1282 return angle_rel;
1283}
1284
1285/* Calculate outset UVs, this is not the same as simply scaling the UVs,
1286 * since the outset coords are a margin that keep an even distance from the original UVs,
1287 * note that the image aspect is taken into account */
1288static void uv_image_outset(const ProjPaintState *ps,
1289 float (*orig_uv)[2],
1290 float (*puv)[2],
1291 uint tri_index,
1292 const int ibuf_x,
1293 const int ibuf_y)
1294{
1295 int fidx[2];
1296 uint loop_index;
1297 uint vert[2];
1298 const int3 &tri = ps->corner_tris_eval[tri_index];
1299
1300 float ibuf_inv[2];
1301
1302 ibuf_inv[0] = 1.0f / float(ibuf_x);
1303 ibuf_inv[1] = 1.0f / float(ibuf_y);
1304
1305 for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) {
1306 LoopSeamData *seam_data;
1307 float(*seam_uvs)[2];
1308 float ang[2];
1309
1310 if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0])) == 0) {
1311 continue;
1312 }
1313
1314 loop_index = tri[fidx[0]];
1315
1316 seam_data = &ps->loopSeamData[loop_index];
1317 seam_uvs = seam_data->seam_uvs;
1318
1319 if (seam_uvs[0][0] != FLT_MAX) {
1320 continue;
1321 }
1322
1323 fidx[1] = (fidx[0] == 2) ? 0 : fidx[0] + 1;
1324
1325 vert[0] = ps->corner_verts_eval[loop_index];
1326 vert[1] = ps->corner_verts_eval[tri[fidx[1]]];
1327
1328 for (uint i = 0; i < 2; i++) {
1329 VertSeam *seam;
1330 VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam);
1331 float no[2];
1332 float len_fact;
1333 float tri_ang;
1334
1335 ang[i] = compute_seam_normal(seam, adj, no);
1336 tri_ang = ang[i] - M_PI_2;
1337
1338 if (tri_ang > 0.0f) {
1339 const float dist = ps->seam_bleed_px * tanf(tri_ang);
1340 seam_data->corner_dist_sq[i] = square_f(dist);
1341 }
1342 else {
1343 seam_data->corner_dist_sq[i] = 0.0f;
1344 }
1345
1346 len_fact = cosf(tri_ang);
1347 len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact);
1348
1349 /* Clamp the length factor, see: #62236. */
1350 len_fact = std::min(len_fact, 10.0f);
1351
1352 mul_v2_fl(no, ps->seam_bleed_px * len_fact);
1353
1354 add_v2_v2v2(seam_data->seam_puvs[i], puv[fidx[i]], no);
1355
1356 mul_v2_v2v2(seam_uvs[i], seam_data->seam_puvs[i], ibuf_inv);
1357 }
1358
1359 /* Handle convergent normals (can self-intersect). */
1360 if ((ang[0] + ang[1]) < M_PI) {
1361 if (isect_seg_seg_v2_simple(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1])) {
1362 float isect_co[2];
1363
1365 orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1], isect_co);
1366
1367 copy_v2_v2(seam_uvs[0], isect_co);
1368 copy_v2_v2(seam_uvs[1], isect_co);
1369 }
1370 }
1371 }
1372}
1373
1375 MemArena *arena,
1376 const int tri_index,
1377 const int fidx1,
1378 const int ibuf_x,
1379 const int ibuf_y)
1380{
1381 const int3 &tri = ps->corner_tris_eval[tri_index];
1382 const int face_i = ps->corner_tri_faces_eval[tri_index];
1383 const float *tri_uv[3] = {PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, face_i, tri)};
1384 const int fidx[2] = {fidx1, ((fidx1 + 1) % 3)};
1385 float vec[2];
1386
1387 VertSeam *vseam = static_cast<VertSeam *>(BLI_memarena_alloc(arena, sizeof(VertSeam[2])));
1388
1389 vseam->prev = nullptr;
1390 vseam->next = nullptr;
1391
1392 vseam->tri = tri_index;
1393 vseam->loop = tri[fidx[0]];
1394
1395 sub_v2_v2v2(vec, tri_uv[fidx[1]], tri_uv[fidx[0]]);
1396 vec[0] *= ibuf_x;
1397 vec[1] *= ibuf_y;
1398 vseam->angle = atan2f(vec[1], vec[0]);
1399
1400 /* If the face winding data is not initialized, something must be wrong. */
1401 BLI_assert((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) != 0);
1402 vseam->normal_cw = (ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW);
1403
1404 copy_v2_v2(vseam->uv, tri_uv[fidx[0]]);
1405
1406 vseam[1] = vseam[0];
1407 vseam[1].angle += vseam[1].angle > 0.0f ? -M_PI : M_PI;
1408 vseam[1].normal_cw = !vseam[1].normal_cw;
1409 copy_v2_v2(vseam[1].uv, tri_uv[fidx[1]]);
1410
1411 for (uint i = 0; i < 2; i++) {
1412 const int vert = ps->corner_verts_eval[tri[fidx[i]]];
1413 ListBase *list = &ps->vertSeams[vert];
1414 VertSeam *item = static_cast<VertSeam *>(list->first);
1415
1416 while (item && item->angle < vseam[i].angle) {
1417 item = item->next;
1418 }
1419
1420 BLI_insertlinkbefore(list, item, &vseam[i]);
1421 }
1422}
1423
1431 MemArena *arena,
1432 const int tri_index,
1433 const uint vert_index,
1434 bool init_all,
1435 const int ibuf_x,
1436 const int ibuf_y)
1437{
1438 /* vars for the other face, we also set its flag */
1439 int other_face, other_fidx;
1440 /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */
1441 int fidx[2] = {2, 0};
1442 const int3 &tri = ps->corner_tris_eval[tri_index];
1443 LinkNode *node;
1444
1445 /* initialize face winding if needed */
1446 if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) {
1447 project_face_winding_init(ps, tri_index);
1448 }
1449
1450 do {
1451 if (init_all || (ps->corner_verts_eval[tri[fidx[0]]] == vert_index) ||
1452 (ps->corner_verts_eval[tri[fidx[1]]] == vert_index))
1453 {
1454 if ((ps->faceSeamFlags[tri_index] &
1455 (PROJ_FACE_SEAM0 << fidx[0] | PROJ_FACE_NOSEAM0 << fidx[0])) == 0)
1456 {
1457 if (check_seam(ps, tri_index, fidx[0], fidx[1], &other_face, &other_fidx)) {
1458 ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM0 << fidx[0];
1459 insert_seam_vert_array(ps, arena, tri_index, fidx[0], ibuf_x, ibuf_y);
1460
1461 if (other_face != -1) {
1462 /* Check if the other seam is already set.
1463 * We don't want to insert it in the list twice. */
1464 if ((ps->faceSeamFlags[other_face] & (PROJ_FACE_SEAM0 << other_fidx)) == 0) {
1465 ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM0 << other_fidx;
1466 insert_seam_vert_array(ps, arena, other_face, other_fidx, ibuf_x, ibuf_y);
1467 }
1468 }
1469 }
1470 else {
1471 ps->faceSeamFlags[tri_index] |= PROJ_FACE_NOSEAM0 << fidx[0];
1472 ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0];
1473
1474 if (other_face != -1) {
1475 /* second 4 bits for disabled */
1476 ps->faceSeamFlags[other_face] |= PROJ_FACE_NOSEAM0 << other_fidx;
1477 ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM_INIT0 << other_fidx;
1478 }
1479 }
1480 }
1481 }
1482
1483 fidx[1] = fidx[0];
1484 } while (fidx[0]--);
1485
1486 if (init_all) {
1487 char checked_verts = 0;
1488
1489 fidx[0] = 2;
1490 fidx[1] = 0;
1491
1492 do {
1493 if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM_INIT0 << fidx[0])) == 0) {
1494 for (uint i = 0; i < 2; i++) {
1495 uint vert;
1496
1497 if ((checked_verts & (1 << fidx[i])) != 0) {
1498 continue;
1499 }
1500
1501 vert = ps->corner_verts_eval[tri[fidx[i]]];
1502
1503 for (node = ps->vertFaces[vert]; node; node = node->next) {
1504 const int tri = POINTER_AS_INT(node->link);
1505
1506 project_face_seams_init(ps, arena, tri, vert, false, ibuf_x, ibuf_y);
1507 }
1508
1509 checked_verts |= 1 << fidx[i];
1510 }
1511
1512 ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0];
1513 }
1514
1515 fidx[1] = fidx[0];
1516 } while (fidx[0]--);
1517 }
1518}
1519#endif // PROJ_DEBUG_NOSEAMBLEED
1520
1521/* Converts a UV location to a 3D screen-space location
1522 * Takes a 'uv' and 3 UV coords, and sets the values of pixelScreenCo
1523 *
1524 * This is used for finding a pixels location in screen-space for painting */
1525static void screen_px_from_ortho(const float uv[2],
1526 const float v1co[3],
1527 const float v2co[3],
1528 const float v3co[3], /* Screen-space coords */
1529 const float uv1co[2],
1530 const float uv2co[2],
1531 const float uv3co[2],
1532 float pixelScreenCo[4],
1533 float w[3])
1534{
1535 barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w);
1536 interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w);
1537}
1538
1539/* same as screen_px_from_ortho except we
1540 * do perspective correction on the pixel coordinate */
1541static void screen_px_from_persp(const float uv[2],
1542 const float v1co[4],
1543 const float v2co[4],
1544 const float v3co[4], /* screen-space coords */
1545 const float uv1co[2],
1546 const float uv2co[2],
1547 const float uv3co[2],
1548 float pixelScreenCo[4],
1549 float w[3])
1550{
1551 float w_int[3];
1552 float wtot_inv, wtot;
1553 barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w);
1554
1555 /* re-weight from the 4th coord of each screen vert */
1556 w_int[0] = w[0] * v1co[3];
1557 w_int[1] = w[1] * v2co[3];
1558 w_int[2] = w[2] * v3co[3];
1559
1560 wtot = w_int[0] + w_int[1] + w_int[2];
1561
1562 if (wtot > 0.0f) {
1563 wtot_inv = 1.0f / wtot;
1564 w_int[0] *= wtot_inv;
1565 w_int[1] *= wtot_inv;
1566 w_int[2] *= wtot_inv;
1567 }
1568 else {
1569 /* Dummy values for zero area face. */
1570 w[0] = w[1] = w[2] = w_int[0] = w_int[1] = w_int[2] = 1.0f / 3.0f;
1571 }
1572 /* done re-weighting */
1573
1574 /* do interpolation based on projected weight */
1575 interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w_int);
1576}
1577
1586static void screen_px_to_vector_persp(int winx,
1587 int winy,
1588 const float projmat_inv[4][4],
1589 const float view_pos[3],
1590 const float co_px[2],
1591 float r_dir[3])
1592{
1593 r_dir[0] = 2.0f * (co_px[0] / winx) - 1.0f;
1594 r_dir[1] = 2.0f * (co_px[1] / winy) - 1.0f;
1595 r_dir[2] = -0.5f;
1596 mul_project_m4_v3((float(*)[4])projmat_inv, r_dir);
1597 sub_v3_v3(r_dir, view_pos);
1598}
1599
1609 const float p[2],
1610 const float v1[3],
1611 const float v2[3])
1612{
1613 const float zero[3] = {0};
1614 float v1_proj[3], v2_proj[3];
1615 float dir[3];
1616
1617 screen_px_to_vector_persp(ps->winx, ps->winy, ps->projectMatInv, ps->viewPos, p, dir);
1618
1619 sub_v3_v3v3(v1_proj, v1, ps->viewPos);
1620 sub_v3_v3v3(v2_proj, v2, ps->viewPos);
1621
1622 project_plane_v3_v3v3(v1_proj, v1_proj, dir);
1623 project_plane_v3_v3v3(v2_proj, v2_proj, dir);
1624
1625 return line_point_factor_v2(zero, v1_proj, v2_proj);
1626}
1627
1629 const float *tri_uv[3], ImBuf *ibuf_other, const float w[3], uchar rgba_ub[4], float rgba_f[4])
1630{
1631 using namespace blender;
1632 float uv_other[2];
1633
1634 interp_v2_v2v2v2(uv_other, UNPACK3(tri_uv), w);
1635
1636 float x = uv_other[0] * ibuf_other->x - 0.5f;
1637 float y = uv_other[1] * ibuf_other->y - 0.5f;
1638
1639 if (ibuf_other->float_buffer.data) {
1641 col = math::clamp(col, 0.0f, 1.0f);
1642 memcpy(rgba_f, &col, sizeof(col));
1643 }
1644 else {
1646 memcpy(rgba_ub, &col, sizeof(col));
1647 }
1648}
1649
1650/* run this outside project_paint_uvpixel_init since pixels with mask 0 don't need init */
1652 const int tri_index,
1653 const float w[3])
1654{
1655 float mask;
1656
1657 /* Image Mask */
1658 if (ps->do_layer_stencil) {
1659 /* another UV maps image is masking this one's */
1660 ImBuf *ibuf_other;
1661 Image *other_tpage = ps->stencil_ima;
1662
1663 if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, nullptr, nullptr))) {
1664 const int3 &tri_other = ps->corner_tris_eval[tri_index];
1665 const float *other_tri_uv[3] = {ps->mloopuv_stencil_eval[tri_other[0]],
1666 ps->mloopuv_stencil_eval[tri_other[1]],
1667 ps->mloopuv_stencil_eval[tri_other[2]]};
1668
1669 /* #BKE_image_acquire_ibuf - TODO: this may be slow. */
1670 uchar rgba_ub[4];
1671 float rgba_f[4];
1672
1673 project_face_pixel(other_tri_uv, ibuf_other, w, rgba_ub, rgba_f);
1674
1675 if (ibuf_other->float_buffer.data) { /* from float to float */
1676 mask = ((rgba_f[0] + rgba_f[1] + rgba_f[2]) * (1.0f / 3.0f)) * rgba_f[3];
1677 }
1678 else { /* from char to float */
1679 mask = ((rgba_ub[0] + rgba_ub[1] + rgba_ub[2]) * (1.0f / (255.0f * 3.0f))) *
1680 (rgba_ub[3] * (1.0f / 255.0f));
1681 }
1682
1683 BKE_image_release_ibuf(other_tpage, ibuf_other, nullptr);
1684
1685 if (!ps->do_layer_stencil_inv) {
1686 /* matching the gimps layer mask black/white rules, white==full opacity */
1687 mask = (1.0f - mask);
1688 }
1689
1690 if (mask == 0.0f) {
1691 return 0.0f;
1692 }
1693 }
1694 else {
1695 return 0.0f;
1696 }
1697 }
1698 else {
1699 mask = 1.0f;
1700 }
1701
1702 if (ps->do_mask_cavity) {
1703 const int3 &tri = ps->corner_tris_eval[tri_index];
1704 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)};
1705 float ca1, ca2, ca3, ca_mask;
1706 ca1 = ps->cavities[vert_tri[0]];
1707 ca2 = ps->cavities[vert_tri[1]];
1708 ca3 = ps->cavities[vert_tri[2]];
1709
1710 ca_mask = w[0] * ca1 + w[1] * ca2 + w[2] * ca3;
1711 ca_mask = BKE_curvemapping_evaluateF(ps->cavity_curve, 0, ca_mask);
1712 CLAMP(ca_mask, 0.0f, 1.0f);
1713 mask *= ca_mask;
1714 }
1715
1716 /* calculate mask */
1717 if (ps->do_mask_normal) {
1718 const int3 &tri = ps->corner_tris_eval[tri_index];
1719 const int face_i = ps->corner_tri_faces_eval[tri_index];
1720 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)};
1721 float no[3], angle_cos;
1722
1723 if (!(ps->sharp_faces_eval && ps->sharp_faces_eval[face_i])) {
1724 const float *no1, *no2, *no3;
1725 no1 = ps->vert_normals[vert_tri[0]];
1726 no2 = ps->vert_normals[vert_tri[1]];
1727 no3 = ps->vert_normals[vert_tri[2]];
1728
1729 no[0] = w[0] * no1[0] + w[1] * no2[0] + w[2] * no3[0];
1730 no[1] = w[0] * no1[1] + w[1] * no2[1] + w[2] * no3[1];
1731 no[2] = w[0] * no1[2] + w[1] * no2[2] + w[2] * no3[2];
1732 normalize_v3(no);
1733 }
1734 else {
1735 /* In case the normalizing per pixel isn't optimal,
1736 * we could cache or access from evaluated mesh. */
1737 normal_tri_v3(no,
1738 ps->vert_positions_eval[vert_tri[0]],
1739 ps->vert_positions_eval[vert_tri[1]],
1740 ps->vert_positions_eval[vert_tri[2]]);
1741 }
1742
1743 if (UNLIKELY(ps->is_flip_object)) {
1744 negate_v3(no);
1745 }
1746
1747 /* now we can use the normal as a mask */
1748 if (ps->is_ortho) {
1749 angle_cos = dot_v3v3(ps->viewDir, no);
1750 }
1751 else {
1752 /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */
1753 float viewDirPersp[3];
1754 const float *co1, *co2, *co3;
1755 co1 = ps->vert_positions_eval[vert_tri[0]];
1756 co2 = ps->vert_positions_eval[vert_tri[1]];
1757 co3 = ps->vert_positions_eval[vert_tri[2]];
1758
1759 /* Get the direction from the viewPoint to the pixel and normalize */
1760 viewDirPersp[0] = (ps->viewPos[0] - (w[0] * co1[0] + w[1] * co2[0] + w[2] * co3[0]));
1761 viewDirPersp[1] = (ps->viewPos[1] - (w[0] * co1[1] + w[1] * co2[1] + w[2] * co3[1]));
1762 viewDirPersp[2] = (ps->viewPos[2] - (w[0] * co1[2] + w[1] * co2[2] + w[2] * co3[2]));
1763 normalize_v3(viewDirPersp);
1764 if (UNLIKELY(ps->is_flip_object)) {
1765 negate_v3(viewDirPersp);
1766 }
1767
1768 angle_cos = dot_v3v3(viewDirPersp, no);
1769 }
1770
1771 /* If back-face culling is disabled, allow painting on back faces. */
1772 if (!ps->do_backfacecull) {
1773 angle_cos = fabsf(angle_cos);
1774 }
1775
1776 if (angle_cos <= ps->normal_angle__cos) {
1777 /* Outsize the normal limit. */
1778 return 0.0f;
1779 }
1780 if (angle_cos < ps->normal_angle_inner__cos) {
1781 mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range;
1782 } /* otherwise no mask normal is needed, we're within the limit */
1783 }
1784
1785 /* This only works when the opacity doesn't change while painting, stylus pressure messes with
1786 * this so don't use it. */
1787 // if (ps->is_airbrush == 0) mask *= BKE_brush_alpha_get(ps->brush);
1788
1789 return mask;
1790}
1791
1792static int project_paint_pixel_sizeof(const short brush_type)
1793{
1795 return sizeof(ProjPixelClone);
1796 }
1797 return sizeof(ProjPixel);
1798}
1799
1800static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
1801{
1802 ProjPaintImage *pjIma = tinf->pjima;
1803 int tile_index = tx + ty * tinf->tile_width;
1804 bool generate_tile = false;
1805
1806 /* double check lock to avoid locking */
1807 if (UNLIKELY(!pjIma->undoRect[tile_index])) {
1808 if (tinf->lock) {
1809 BLI_spin_lock(tinf->lock);
1810 }
1811 if (LIKELY(!pjIma->undoRect[tile_index])) {
1813 generate_tile = true;
1814 }
1815 if (tinf->lock) {
1816 BLI_spin_unlock(tinf->lock);
1817 }
1818 }
1819
1820 if (generate_tile) {
1822 volatile void *undorect;
1823 if (tinf->masked) {
1824 undorect = ED_image_paint_tile_push(undo_tiles,
1825 pjIma->ima,
1826 pjIma->ibuf,
1827 tinf->tmpibuf,
1828 &pjIma->iuser,
1829 tx,
1830 ty,
1831 &pjIma->maskRect[tile_index],
1832 &pjIma->valid[tile_index],
1833 true,
1834 false);
1835 }
1836 else {
1837 undorect = ED_image_paint_tile_push(undo_tiles,
1838 pjIma->ima,
1839 pjIma->ibuf,
1840 tinf->tmpibuf,
1841 &pjIma->iuser,
1842 tx,
1843 ty,
1844 nullptr,
1845 &pjIma->valid[tile_index],
1846 true,
1847 false);
1848 }
1849
1850 BKE_image_mark_dirty(pjIma->ima, pjIma->ibuf);
1851 /* tile ready, publish */
1852 if (tinf->lock) {
1853 BLI_spin_lock(tinf->lock);
1854 }
1855 pjIma->undoRect[tile_index] = undorect;
1856 if (tinf->lock) {
1857 BLI_spin_unlock(tinf->lock);
1858 }
1859 }
1860
1861 return tile_index;
1862}
1863
1864/* run this function when we know a bucket's, face's pixel can be initialized,
1865 * return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */
1867 MemArena *arena,
1868 const TileInfo *tinf,
1869 int x_px,
1870 int y_px,
1871 const float mask,
1872 const int tri_index,
1873 const float pixelScreenCo[4],
1874 const float world_spaceCo[3],
1875 const float w[3])
1876{
1877 ProjPixel *projPixel;
1878 int x_tile, y_tile;
1879 int x_round, y_round;
1880 int tile_offset;
1881 /* Volatile is important here to ensure pending check is not optimized away by compiler. */
1882 volatile int tile_index;
1883
1884 ProjPaintImage *projima = tinf->pjima;
1885 ImBuf *ibuf = projima->ibuf;
1886 /* wrap pixel location */
1887
1888 x_px = mod_i(x_px, ibuf->x);
1889 y_px = mod_i(y_px, ibuf->y);
1890
1892 projPixel = static_cast<ProjPixel *>(BLI_memarena_alloc(arena, ps->pixel_sizeof));
1893
1894 /* calculate the undo tile offset of the pixel, used to store the original
1895 * pixel color and accumulated mask if any */
1896 x_tile = x_px >> ED_IMAGE_UNDO_TILE_BITS;
1897 y_tile = y_px >> ED_IMAGE_UNDO_TILE_BITS;
1898
1899 x_round = x_tile * ED_IMAGE_UNDO_TILE_SIZE;
1900 y_round = y_tile * ED_IMAGE_UNDO_TILE_SIZE;
1901 // memset(projPixel, 0, size);
1902
1903 tile_offset = (x_px - x_round) + (y_px - y_round) * ED_IMAGE_UNDO_TILE_SIZE;
1904 tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile);
1905
1906 /* other thread may be initializing the tile so wait here */
1907 while (projima->undoRect[tile_index] == TILE_PENDING) {
1908 /* pass */
1909 }
1910
1914
1915 projPixel->valid = projima->valid[tile_index];
1916
1917 if (ibuf->float_buffer.data) {
1918 projPixel->pixel.f_pt = ibuf->float_buffer.data + ((x_px + y_px * ibuf->x) * 4);
1919 projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset;
1920 zero_v4(projPixel->newColor.f);
1921 }
1922 else {
1923 projPixel->pixel.ch_pt = ibuf->byte_buffer.data + (x_px + y_px * ibuf->x) * 4;
1924 projPixel->origColor.uint_pt = (uint *)projima->undoRect[tile_index] + tile_offset;
1925 projPixel->newColor.uint_ = 0;
1926 }
1927
1928 /* Screen-space unclamped, we could keep its z and w values but don't need them at the moment. */
1930 copy_v3_v3(projPixel->worldCoSS, world_spaceCo);
1931 }
1932
1933 copy_v2_v2(projPixel->projCoSS, pixelScreenCo);
1934
1935 projPixel->x_px = x_px;
1936 projPixel->y_px = y_px;
1937
1938 projPixel->mask = ushort(mask * 65535);
1939 if (ps->do_masking) {
1940 projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset;
1941 }
1942 else {
1943 projPixel->mask_accum = nullptr;
1944 }
1945
1946 /* which bounding box cell are we in?, needed for undo */
1947 projPixel->bb_cell_index = int((float(x_px) / float(ibuf->x)) * PROJ_BOUNDBOX_DIV) +
1948 int((float(y_px) / float(ibuf->y)) * PROJ_BOUNDBOX_DIV) *
1950
1951 /* done with view3d_project_float inline */
1953 if (ps->poly_to_loop_uv_clone) {
1954 ImBuf *ibuf_other;
1955 Image *other_tpage = project_paint_face_clone_image(ps, tri_index);
1956
1957 if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, nullptr, nullptr))) {
1958 const int3 &tri_other = ps->corner_tris_eval[tri_index];
1959 const int poly_other_i = ps->corner_tri_faces_eval[tri_index];
1960 const float *other_tri_uv[3] = {
1961 PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv_clone, poly_other_i, tri_other)};
1962
1963 /* #BKE_image_acquire_ibuf - TODO: this may be slow. */
1964
1965 if (ibuf->float_buffer.data) {
1966 if (ibuf_other->float_buffer.data) { /* from float to float */
1968 other_tri_uv, ibuf_other, w, nullptr, ((ProjPixelClone *)projPixel)->clonepx.f);
1969 }
1970 else { /* from char to float */
1971 uchar rgba_ub[4];
1972 float rgba[4];
1973 project_face_pixel(other_tri_uv, ibuf_other, w, rgba_ub, nullptr);
1974 srgb_to_linearrgb_uchar4(rgba, rgba_ub);
1975 straight_to_premul_v4_v4(((ProjPixelClone *)projPixel)->clonepx.f, rgba);
1976 }
1977 }
1978 else {
1979 if (ibuf_other->float_buffer.data) { /* float to char */
1980 float rgba[4];
1981 project_face_pixel(other_tri_uv, ibuf_other, w, nullptr, rgba);
1983 linearrgb_to_srgb_uchar3(((ProjPixelClone *)projPixel)->clonepx.ch, rgba);
1984 ((ProjPixelClone *)projPixel)->clonepx.ch[3] = rgba[3] * 255;
1985 }
1986 else { /* char to char */
1988 other_tri_uv, ibuf_other, w, ((ProjPixelClone *)projPixel)->clonepx.ch, nullptr);
1989 }
1990 }
1991
1992 BKE_image_release_ibuf(other_tpage, ibuf_other, nullptr);
1993 }
1994 else {
1995 if (ibuf->float_buffer.data) {
1996 ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0;
1997 }
1998 else {
1999 ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
2000 }
2001 }
2002 }
2003 else {
2004 float co[2];
2005 sub_v2_v2v2(co, projPixel->projCoSS, ps->cloneOffset);
2006
2007 /* no need to initialize the bucket, we're only checking buckets faces and for this
2008 * the faces are already initialized in project_paint_delayed_face_init(...) */
2009 if (ibuf->float_buffer.data) {
2011 ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, nullptr, true))
2012 {
2013 /* zero alpha - ignore */
2014 ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0;
2015 }
2016 }
2017 else {
2019 ps, co, nullptr, ((ProjPixelClone *)projPixel)->clonepx.ch, true))
2020 {
2021 /* zero alpha - ignore */
2022 ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
2023 }
2024 }
2025 }
2026 }
2027
2028#ifdef PROJ_DEBUG_PAINT
2029 if (ibuf->float_buffer.data) {
2030 projPixel->pixel.f_pt[0] = 0;
2031 }
2032 else {
2033 projPixel->pixel.ch_pt[0] = 0;
2034 }
2035#endif
2036 /* pointer arithmetic */
2037 projPixel->image_index = projima - ps->projImages;
2038
2039 return projPixel;
2040}
2041
2042static bool line_clip_rect2f(const rctf *cliprect,
2043 const rctf *rect,
2044 const float l1[2],
2045 const float l2[2],
2046 float l1_clip[2],
2047 float l2_clip[2])
2048{
2049 /* first account for horizontal, then vertical lines */
2050 /* Horizontal. */
2051 if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) {
2052 /* is the line out of range on its Y axis? */
2053 if (l1[1] < rect->ymin || l1[1] > rect->ymax) {
2054 return false;
2055 }
2056 /* line is out of range on its X axis */
2057 if ((l1[0] < rect->xmin && l2[0] < rect->xmin) || (l1[0] > rect->xmax && l2[0] > rect->xmax)) {
2058 return false;
2059 }
2060
2061 /* This is a single point (or close to). */
2062 if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) {
2063 if (BLI_rctf_isect_pt_v(rect, l1)) {
2064 copy_v2_v2(l1_clip, l1);
2065 copy_v2_v2(l2_clip, l2);
2066 return true;
2067 }
2068 return false;
2069 }
2070
2071 copy_v2_v2(l1_clip, l1);
2072 copy_v2_v2(l2_clip, l2);
2073 CLAMP(l1_clip[0], rect->xmin, rect->xmax);
2074 CLAMP(l2_clip[0], rect->xmin, rect->xmax);
2075 return true;
2076 }
2077 if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) {
2078 /* is the line out of range on its X axis? */
2079 if (l1[0] < rect->xmin || l1[0] > rect->xmax) {
2080 return false;
2081 }
2082
2083 /* line is out of range on its Y axis */
2084 if ((l1[1] < rect->ymin && l2[1] < rect->ymin) || (l1[1] > rect->ymax && l2[1] > rect->ymax)) {
2085 return false;
2086 }
2087
2088 /* This is a single point (or close to). */
2089 if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) {
2090 if (BLI_rctf_isect_pt_v(rect, l1)) {
2091 copy_v2_v2(l1_clip, l1);
2092 copy_v2_v2(l2_clip, l2);
2093 return true;
2094 }
2095 return false;
2096 }
2097
2098 copy_v2_v2(l1_clip, l1);
2099 copy_v2_v2(l2_clip, l2);
2100 CLAMP(l1_clip[1], rect->ymin, rect->ymax);
2101 CLAMP(l2_clip[1], rect->ymin, rect->ymax);
2102 return true;
2103 }
2104
2105 float isect;
2106 short ok1 = 0;
2107 short ok2 = 0;
2108
2109 /* Done with vertical lines */
2110
2111 /* are either of the points inside the rectangle ? */
2112 if (BLI_rctf_isect_pt_v(rect, l1)) {
2113 copy_v2_v2(l1_clip, l1);
2114 ok1 = 1;
2115 }
2116
2117 if (BLI_rctf_isect_pt_v(rect, l2)) {
2118 copy_v2_v2(l2_clip, l2);
2119 ok2 = 1;
2120 }
2121
2122 /* line inside rect */
2123 if (ok1 && ok2) {
2124 return true;
2125 }
2126
2127 /* top/bottom */
2128 if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= cliprect->xmin) &&
2129 (isect <= cliprect->xmax))
2130 {
2131 if (l1[1] < l2[1]) { /* line 1 is outside */
2132 l1_clip[0] = isect;
2133 l1_clip[1] = rect->ymin;
2134 ok1 = 1;
2135 }
2136 else {
2137 l2_clip[0] = isect;
2138 l2_clip[1] = rect->ymin;
2139 ok2 = 2;
2140 }
2141 }
2142
2143 if (ok1 && ok2) {
2144 return true;
2145 }
2146
2147 if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= cliprect->xmin) &&
2148 (isect <= cliprect->xmax))
2149 {
2150 if (l1[1] > l2[1]) { /* line 1 is outside */
2151 l1_clip[0] = isect;
2152 l1_clip[1] = rect->ymax;
2153 ok1 = 1;
2154 }
2155 else {
2156 l2_clip[0] = isect;
2157 l2_clip[1] = rect->ymax;
2158 ok2 = 2;
2159 }
2160 }
2161
2162 if (ok1 && ok2) {
2163 return true;
2164 }
2165
2166 /* left/right */
2167 if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= cliprect->ymin) &&
2168 (isect <= cliprect->ymax))
2169 {
2170 if (l1[0] < l2[0]) { /* line 1 is outside */
2171 l1_clip[0] = rect->xmin;
2172 l1_clip[1] = isect;
2173 ok1 = 1;
2174 }
2175 else {
2176 l2_clip[0] = rect->xmin;
2177 l2_clip[1] = isect;
2178 ok2 = 2;
2179 }
2180 }
2181
2182 if (ok1 && ok2) {
2183 return true;
2184 }
2185
2186 if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= cliprect->ymin) &&
2187 (isect <= cliprect->ymax))
2188 {
2189 if (l1[0] > l2[0]) { /* line 1 is outside */
2190 l1_clip[0] = rect->xmax;
2191 l1_clip[1] = isect;
2192 ok1 = 1;
2193 }
2194 else {
2195 l2_clip[0] = rect->xmax;
2196 l2_clip[1] = isect;
2197 ok2 = 2;
2198 }
2199 }
2200
2201 if (ok1 && ok2) {
2202 return true;
2203 }
2204 return false;
2205}
2206
2212#ifndef PROJ_DEBUG_NOSEAMBLEED
2213
2214static void scale_tri(float insetCos[3][3], const float *origCos[3], const float inset)
2215{
2216 float cent[3];
2217 cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0]) * (1.0f / 3.0f);
2218 cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1]) * (1.0f / 3.0f);
2219 cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2]) * (1.0f / 3.0f);
2220
2221 sub_v3_v3v3(insetCos[0], origCos[0], cent);
2222 sub_v3_v3v3(insetCos[1], origCos[1], cent);
2223 sub_v3_v3v3(insetCos[2], origCos[2], cent);
2224
2225 mul_v3_fl(insetCos[0], inset);
2226 mul_v3_fl(insetCos[1], inset);
2227 mul_v3_fl(insetCos[2], inset);
2228
2229 add_v3_v3(insetCos[0], cent);
2230 add_v3_v3(insetCos[1], cent);
2231 add_v3_v3(insetCos[2], cent);
2232}
2233#endif // PROJ_DEBUG_NOSEAMBLEED
2234
2235static float len_squared_v2v2_alt(const float v1[2], const float v2_1, const float v2_2)
2236{
2237 float x, y;
2238
2239 x = v1[0] - v2_1;
2240 y = v1[1] - v2_2;
2241 return x * x + y * y;
2242}
2243
2250static bool project_bucket_isect_circle(const float cent[2],
2251 const float radius_squared,
2252 const rctf *bucket_bounds)
2253{
2254
2255 /* Would normally to a simple intersection test,
2256 * however we know the bounds of these 2 already intersect so we only need to test
2257 * if the center is inside the vertical or horizontal bounds on either axis,
2258 * this is even less work than an intersection test.
2259 */
2260#if 0
2261 if (BLI_rctf_isect_pt_v(bucket_bounds, cent)) {
2262 return true;
2263 }
2264#endif
2265
2266 if ((bucket_bounds->xmin <= cent[0] && bucket_bounds->xmax >= cent[0]) ||
2267 (bucket_bounds->ymin <= cent[1] && bucket_bounds->ymax >= cent[1]))
2268 {
2269 return true;
2270 }
2271
2272 /* out of bounds left */
2273 if (cent[0] < bucket_bounds->xmin) {
2274 /* lower left out of radius test */
2275 if (cent[1] < bucket_bounds->ymin) {
2276 return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymin) <
2277 radius_squared) ?
2278 true :
2279 false;
2280 }
2281 /* top left test */
2282 if (cent[1] > bucket_bounds->ymax) {
2283 return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymax) <
2284 radius_squared) ?
2285 true :
2286 false;
2287 }
2288 }
2289 else if (cent[0] > bucket_bounds->xmax) {
2290 /* lower right out of radius test */
2291 if (cent[1] < bucket_bounds->ymin) {
2292 return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymin) <
2293 radius_squared) ?
2294 true :
2295 false;
2296 }
2297 /* top right test */
2298 if (cent[1] > bucket_bounds->ymax) {
2299 return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymax) <
2300 radius_squared) ?
2301 true :
2302 false;
2303 }
2304 }
2305
2306 return false;
2307}
2308
2309/* Note for #rect_to_uvspace_ortho() and #rect_to_uvspace_persp()
2310 * in ortho view this function gives good results when bucket_bounds are outside the triangle
2311 * however in some cases, perspective view will mess up with faces
2312 * that have minimal screen-space area (viewed from the side).
2313 *
2314 * for this reason its not reliable in this case so we'll use the Simple Barycentric'
2315 * functions that only account for points inside the triangle.
2316 * however switching back to this for ortho is always an option. */
2317
2318static void rect_to_uvspace_ortho(const rctf *bucket_bounds,
2319 const float *v1coSS,
2320 const float *v2coSS,
2321 const float *v3coSS,
2322 const float *uv1co,
2323 const float *uv2co,
2324 const float *uv3co,
2325 float bucket_bounds_uv[4][2],
2326 const int flip)
2327{
2328 float uv[2];
2329 float w[3];
2330
2331 /* get the UV space bounding box */
2332 uv[0] = bucket_bounds->xmax;
2333 uv[1] = bucket_bounds->ymin;
2334 barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w);
2335 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w);
2336
2337 // uv[0] = bucket_bounds->xmax; // set above
2338 uv[1] = bucket_bounds->ymax;
2339 barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w);
2340 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w);
2341
2342 uv[0] = bucket_bounds->xmin;
2343 // uv[1] = bucket_bounds->ymax; // set above
2344 barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w);
2345 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w);
2346
2347 // uv[0] = bucket_bounds->xmin; // set above
2348 uv[1] = bucket_bounds->ymin;
2349 barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w);
2350 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w);
2351}
2352
2356static void rect_to_uvspace_persp(const rctf *bucket_bounds,
2357 const float *v1coSS,
2358 const float *v2coSS,
2359 const float *v3coSS,
2360 const float *uv1co,
2361 const float *uv2co,
2362 const float *uv3co,
2363 float bucket_bounds_uv[4][2],
2364 const int flip)
2365{
2366 float uv[2];
2367 float w[3];
2368
2369 /* get the UV space bounding box */
2370 uv[0] = bucket_bounds->xmax;
2371 uv[1] = bucket_bounds->ymin;
2372 barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w);
2373 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w);
2374
2375 // uv[0] = bucket_bounds->xmax; // set above
2376 uv[1] = bucket_bounds->ymax;
2377 barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w);
2378 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w);
2379
2380 uv[0] = bucket_bounds->xmin;
2381 // uv[1] = bucket_bounds->ymax; // set above
2382 barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w);
2383 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w);
2384
2385 // uv[0] = bucket_bounds->xmin; // set above
2386 uv[1] = bucket_bounds->ymin;
2387 barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w);
2388 interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w);
2389}
2390
2391/* This works as we need it to but we can save a few steps and not use it */
2392
2393#if 0
2394static float angle_2d_clockwise(const float p1[2], const float p2[2], const float p3[2])
2395{
2396 float v1[2], v2[2];
2397
2398 v1[0] = p1[0] - p2[0];
2399 v1[1] = p1[1] - p2[1];
2400 v2[0] = p3[0] - p2[0];
2401 v2[1] = p3[1] - p2[1];
2402
2403 return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]);
2404}
2405#endif
2406
2407#define ISECT_1 (1)
2408#define ISECT_2 (1 << 1)
2409#define ISECT_3 (1 << 2)
2410#define ISECT_4 (1 << 3)
2411#define ISECT_ALL3 ((1 << 3) - 1)
2412#define ISECT_ALL4 ((1 << 4) - 1)
2413
2414/* limit must be a fraction over 1.0f */
2416 const float pt[2], const float v1[2], const float v2[2], const float v3[2], const float limit)
2417{
2418 return ((area_tri_v2(pt, v1, v2) + area_tri_v2(pt, v2, v3) + area_tri_v2(pt, v3, v1)) /
2419 area_tri_v2(v1, v2, v3)) < limit;
2420}
2421
2426static int float_z_sort_flip(const void *p1, const void *p2)
2427{
2428 return (((float *)p1)[2] < ((float *)p2)[2] ? 1 : -1);
2429}
2430
2431static int float_z_sort(const void *p1, const void *p2)
2432{
2433 return (((float *)p1)[2] < ((float *)p2)[2] ? -1 : 1);
2434}
2435
2436/* assumes one point is within the rectangle */
2437static bool line_rect_clip(const rctf *rect,
2438 const float l1[4],
2439 const float l2[4],
2440 const float uv1[2],
2441 const float uv2[2],
2442 float uv[2],
2443 bool is_ortho)
2444{
2445 float min = FLT_MAX, tmp;
2446 float xlen = l2[0] - l1[0];
2447 float ylen = l2[1] - l1[1];
2448
2449 /* 0.1 might seem too much, but remember, this is pixels! */
2450 if (xlen > 0.1f) {
2451 if ((l1[0] - rect->xmin) * (l2[0] - rect->xmin) <= 0) {
2452 tmp = rect->xmin;
2453 min = min_ff((tmp - l1[0]) / xlen, min);
2454 }
2455 else if ((l1[0] - rect->xmax) * (l2[0] - rect->xmax) < 0) {
2456 tmp = rect->xmax;
2457 min = min_ff((tmp - l1[0]) / xlen, min);
2458 }
2459 }
2460
2461 if (ylen > 0.1f) {
2462 if ((l1[1] - rect->ymin) * (l2[1] - rect->ymin) <= 0) {
2463 tmp = rect->ymin;
2464 min = min_ff((tmp - l1[1]) / ylen, min);
2465 }
2466 else if ((l1[1] - rect->ymax) * (l2[1] - rect->ymax) < 0) {
2467 tmp = rect->ymax;
2468 min = min_ff((tmp - l1[1]) / ylen, min);
2469 }
2470 }
2471
2472 if (min == FLT_MAX) {
2473 return false;
2474 }
2475
2476 tmp = (is_ortho) ? 1.0f : (l1[3] + min * (l2[3] - l1[3]));
2477
2478 uv[0] = (uv1[0] + min / tmp * (uv2[0] - uv1[0]));
2479 uv[1] = (uv1[1] + min / tmp * (uv2[1] - uv1[1]));
2480
2481 return true;
2482}
2483
2484static void project_bucket_clip_face(const bool is_ortho,
2485 const bool is_flip_object,
2486 const rctf *cliprect,
2487 const rctf *bucket_bounds,
2488 const float *v1coSS,
2489 const float *v2coSS,
2490 const float *v3coSS,
2491 const float *uv1co,
2492 const float *uv2co,
2493 const float *uv3co,
2494 float bucket_bounds_uv[8][2],
2495 int *tot,
2496 bool cull)
2497{
2498 int inside_bucket_flag = 0;
2499 int inside_face_flag = 0;
2500 int flip;
2501 bool collinear = false;
2502
2503 float bucket_bounds_ss[4][2];
2504
2505 /* detect pathological case where face the three vertices are almost collinear in screen space.
2506 * mostly those will be culled but when flood filling or with
2507 * smooth shading it's a possibility */
2508 if (min_fff(dist_squared_to_line_v2(v1coSS, v2coSS, v3coSS),
2509 dist_squared_to_line_v2(v2coSS, v3coSS, v1coSS),
2510 dist_squared_to_line_v2(v3coSS, v1coSS, v2coSS)) < PROJ_PIXEL_TOLERANCE)
2511 {
2512 collinear = true;
2513 }
2514
2515 /* get the UV space bounding box */
2516 inside_bucket_flag |= int(BLI_rctf_isect_pt_v(bucket_bounds, v1coSS));
2517 inside_bucket_flag |= int(BLI_rctf_isect_pt_v(bucket_bounds, v2coSS)) << 1;
2518 inside_bucket_flag |= int(BLI_rctf_isect_pt_v(bucket_bounds, v3coSS)) << 2;
2519
2520 if (inside_bucket_flag == ISECT_ALL3) {
2521 /* is_flip_object is used here because we use the face winding */
2522 flip = (((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != is_flip_object) !=
2523 (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f));
2524
2525 /* All screen-space points are inside the bucket bounding box,
2526 * this means we don't need to clip and can simply return the UVs. */
2527 if (flip) { /* facing the back? */
2528 copy_v2_v2(bucket_bounds_uv[0], uv3co);
2529 copy_v2_v2(bucket_bounds_uv[1], uv2co);
2530 copy_v2_v2(bucket_bounds_uv[2], uv1co);
2531 }
2532 else {
2533 copy_v2_v2(bucket_bounds_uv[0], uv1co);
2534 copy_v2_v2(bucket_bounds_uv[1], uv2co);
2535 copy_v2_v2(bucket_bounds_uv[2], uv3co);
2536 }
2537
2538 *tot = 3;
2539 return;
2540 }
2541 /* Handle pathological case here,
2542 * no need for further intersections below since triangle area is almost zero. */
2543 if (collinear) {
2544 int flag;
2545
2546 (*tot) = 0;
2547
2548 if (cull) {
2549 return;
2550 }
2551
2552 if (inside_bucket_flag & ISECT_1) {
2553 copy_v2_v2(bucket_bounds_uv[*tot], uv1co);
2554 (*tot)++;
2555 }
2556
2557 flag = inside_bucket_flag & (ISECT_1 | ISECT_2);
2558 if (flag && flag != (ISECT_1 | ISECT_2)) {
2559 if (line_rect_clip(
2560 bucket_bounds, v1coSS, v2coSS, uv1co, uv2co, bucket_bounds_uv[*tot], is_ortho))
2561 {
2562 (*tot)++;
2563 }
2564 }
2565
2566 if (inside_bucket_flag & ISECT_2) {
2567 copy_v2_v2(bucket_bounds_uv[*tot], uv2co);
2568 (*tot)++;
2569 }
2570
2571 flag = inside_bucket_flag & (ISECT_2 | ISECT_3);
2572 if (flag && flag != (ISECT_2 | ISECT_3)) {
2573 if (line_rect_clip(
2574 bucket_bounds, v2coSS, v3coSS, uv2co, uv3co, bucket_bounds_uv[*tot], is_ortho))
2575 {
2576 (*tot)++;
2577 }
2578 }
2579
2580 if (inside_bucket_flag & ISECT_3) {
2581 copy_v2_v2(bucket_bounds_uv[*tot], uv3co);
2582 (*tot)++;
2583 }
2584
2585 flag = inside_bucket_flag & (ISECT_3 | ISECT_1);
2586 if (flag && flag != (ISECT_3 | ISECT_1)) {
2587 if (line_rect_clip(
2588 bucket_bounds, v3coSS, v1coSS, uv3co, uv1co, bucket_bounds_uv[*tot], is_ortho))
2589 {
2590 (*tot)++;
2591 }
2592 }
2593
2594 if ((*tot) < 3) {
2595 /* no intersections to speak of, but more probable is that all face is just outside the
2596 * rectangle and culled due to float precision issues. Since above tests have failed,
2597 * just dump triangle as is for painting */
2598 *tot = 0;
2599 copy_v2_v2(bucket_bounds_uv[*tot], uv1co);
2600 (*tot)++;
2601 copy_v2_v2(bucket_bounds_uv[*tot], uv2co);
2602 (*tot)++;
2603 copy_v2_v2(bucket_bounds_uv[*tot], uv3co);
2604 (*tot)++;
2605 return;
2606 }
2607
2608 return;
2609 }
2610
2611 /* Get the UV space bounding box. */
2612 /* Use #IsectPT2Df_limit here so we catch points are touching the triangles edge
2613 * (or a small fraction over) */
2614 bucket_bounds_ss[0][0] = bucket_bounds->xmax;
2615 bucket_bounds_ss[0][1] = bucket_bounds->ymin;
2616 inside_face_flag |= (IsectPT2Df_limit(
2617 bucket_bounds_ss[0], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ?
2618 ISECT_1 :
2619 0);
2620
2621 bucket_bounds_ss[1][0] = bucket_bounds->xmax;
2622 bucket_bounds_ss[1][1] = bucket_bounds->ymax;
2623 inside_face_flag |= (IsectPT2Df_limit(
2624 bucket_bounds_ss[1], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ?
2625 ISECT_2 :
2626 0);
2627
2628 bucket_bounds_ss[2][0] = bucket_bounds->xmin;
2629 bucket_bounds_ss[2][1] = bucket_bounds->ymax;
2630 inside_face_flag |= (IsectPT2Df_limit(
2631 bucket_bounds_ss[2], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ?
2632 ISECT_3 :
2633 0);
2634
2635 bucket_bounds_ss[3][0] = bucket_bounds->xmin;
2636 bucket_bounds_ss[3][1] = bucket_bounds->ymin;
2637 inside_face_flag |= (IsectPT2Df_limit(
2638 bucket_bounds_ss[3], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ?
2639 ISECT_4 :
2640 0);
2641
2642 flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) !=
2643 (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f));
2644
2645 if (inside_face_flag == ISECT_ALL4) {
2646 /* Bucket is totally inside the screen-space face, we can safely use weights. */
2647
2648 if (is_ortho) {
2650 bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip);
2651 }
2652 else {
2654 bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip);
2655 }
2656
2657 *tot = 4;
2658 return;
2659 }
2660
2661 {
2662 /* The Complicated Case!
2663 *
2664 * The 2 cases above are where the face is inside the bucket
2665 * or the bucket is inside the face.
2666 *
2667 * we need to make a convex poly-line from the intersection between the screen-space face
2668 * and the bucket bounds.
2669 *
2670 * There are a number of ways this could be done, currently it just collects all
2671 * intersecting verts, and line intersections, then sorts them clockwise, this is
2672 * a lot easier than evaluating the geometry to do a correct clipping on both shapes.
2673 */
2674
2675 /* Add a bunch of points, we know must make up the convex hull
2676 * which is the clipped rect and triangle */
2677
2678 /* Maximum possible 6 intersections when using a rectangle and triangle */
2679
2680 /* The 3rd float is used to store angle for qsort(), NOT as a Z location */
2681 float isectVCosSS[8][3];
2682 float v1_clipSS[2], v2_clipSS[2];
2683 float w[3];
2684
2685 /* calc center */
2686 float cent[2] = {0.0f, 0.0f};
2687 // float up[2] = {0.0f, 1.0f};
2688 bool doubles;
2689
2690 (*tot) = 0;
2691
2692 if (inside_face_flag & ISECT_1) {
2693 copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[0]);
2694 (*tot)++;
2695 }
2696 if (inside_face_flag & ISECT_2) {
2697 copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[1]);
2698 (*tot)++;
2699 }
2700 if (inside_face_flag & ISECT_3) {
2701 copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[2]);
2702 (*tot)++;
2703 }
2704 if (inside_face_flag & ISECT_4) {
2705 copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[3]);
2706 (*tot)++;
2707 }
2708
2709 if (inside_bucket_flag & ISECT_1) {
2710 copy_v2_v2(isectVCosSS[*tot], v1coSS);
2711 (*tot)++;
2712 }
2713 if (inside_bucket_flag & ISECT_2) {
2714 copy_v2_v2(isectVCosSS[*tot], v2coSS);
2715 (*tot)++;
2716 }
2717 if (inside_bucket_flag & ISECT_3) {
2718 copy_v2_v2(isectVCosSS[*tot], v3coSS);
2719 (*tot)++;
2720 }
2721
2722 if ((inside_bucket_flag & (ISECT_1 | ISECT_2)) != (ISECT_1 | ISECT_2)) {
2723 if (line_clip_rect2f(cliprect, bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) {
2724 if ((inside_bucket_flag & ISECT_1) == 0) {
2725 copy_v2_v2(isectVCosSS[*tot], v1_clipSS);
2726 (*tot)++;
2727 }
2728 if ((inside_bucket_flag & ISECT_2) == 0) {
2729 copy_v2_v2(isectVCosSS[*tot], v2_clipSS);
2730 (*tot)++;
2731 }
2732 }
2733 }
2734
2735 if ((inside_bucket_flag & (ISECT_2 | ISECT_3)) != (ISECT_2 | ISECT_3)) {
2736 if (line_clip_rect2f(cliprect, bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) {
2737 if ((inside_bucket_flag & ISECT_2) == 0) {
2738 copy_v2_v2(isectVCosSS[*tot], v1_clipSS);
2739 (*tot)++;
2740 }
2741 if ((inside_bucket_flag & ISECT_3) == 0) {
2742 copy_v2_v2(isectVCosSS[*tot], v2_clipSS);
2743 (*tot)++;
2744 }
2745 }
2746 }
2747
2748 if ((inside_bucket_flag & (ISECT_3 | ISECT_1)) != (ISECT_3 | ISECT_1)) {
2749 if (line_clip_rect2f(cliprect, bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) {
2750 if ((inside_bucket_flag & ISECT_3) == 0) {
2751 copy_v2_v2(isectVCosSS[*tot], v1_clipSS);
2752 (*tot)++;
2753 }
2754 if ((inside_bucket_flag & ISECT_1) == 0) {
2755 copy_v2_v2(isectVCosSS[*tot], v2_clipSS);
2756 (*tot)++;
2757 }
2758 }
2759 }
2760
2761 if ((*tot) < 3) { /* no intersections to speak of */
2762 *tot = 0;
2763 return;
2764 }
2765
2766 /* now we have all points we need, collect their angles and sort them clockwise */
2767
2768 for (int i = 0; i < (*tot); i++) {
2769 cent[0] += isectVCosSS[i][0];
2770 cent[1] += isectVCosSS[i][1];
2771 }
2772 cent[0] = cent[0] / float(*tot);
2773 cent[1] = cent[1] / float(*tot);
2774
2775 /* Collect angles for every point around the center point */
2776
2777#if 0 /* uses a few more cycles than the above loop */
2778 for (int i = 0; i < (*tot); i++) {
2779 isectVCosSS[i][2] = angle_2d_clockwise(up, cent, isectVCosSS[i]);
2780 }
2781#endif
2782
2783 /* Abuse this var for the loop below */
2784 v1_clipSS[0] = cent[0];
2785 v1_clipSS[1] = cent[1] + 1.0f;
2786
2787 for (int i = 0; i < (*tot); i++) {
2788 v2_clipSS[0] = isectVCosSS[i][0] - cent[0];
2789 v2_clipSS[1] = isectVCosSS[i][1] - cent[1];
2790 isectVCosSS[i][2] = atan2f(v1_clipSS[0] * v2_clipSS[1] - v1_clipSS[1] * v2_clipSS[0],
2791 v1_clipSS[0] * v2_clipSS[0] + v1_clipSS[1] * v2_clipSS[1]);
2792 }
2793
2794 if (flip) {
2795 qsort(isectVCosSS, *tot, sizeof(float[3]), float_z_sort_flip);
2796 }
2797 else {
2798 qsort(isectVCosSS, *tot, sizeof(float[3]), float_z_sort);
2799 }
2800
2801 doubles = true;
2802 while (doubles == true) {
2803 doubles = false;
2804
2805 for (int i = 0; i < (*tot); i++) {
2806 if (fabsf(isectVCosSS[(i + 1) % *tot][0] - isectVCosSS[i][0]) < PROJ_PIXEL_TOLERANCE &&
2807 fabsf(isectVCosSS[(i + 1) % *tot][1] - isectVCosSS[i][1]) < PROJ_PIXEL_TOLERANCE)
2808 {
2809 for (int j = i; j < (*tot) - 1; j++) {
2810 isectVCosSS[j][0] = isectVCosSS[j + 1][0];
2811 isectVCosSS[j][1] = isectVCosSS[j + 1][1];
2812 }
2813 /* keep looking for more doubles */
2814 doubles = true;
2815 (*tot)--;
2816 }
2817 }
2818
2819 /* its possible there is only a few left after remove doubles */
2820 if ((*tot) < 3) {
2821 // printf("removed too many doubles B\n");
2822 *tot = 0;
2823 return;
2824 }
2825 }
2826
2827 if (is_ortho) {
2828 for (int i = 0; i < (*tot); i++) {
2829 barycentric_weights_v2(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w);
2830 interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w);
2831 }
2832 }
2833 else {
2834 for (int i = 0; i < (*tot); i++) {
2835 barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w);
2836 interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w);
2837 }
2838 }
2839 }
2840
2841#ifdef PROJ_DEBUG_PRINT_CLIP
2842 /* include this at the bottom of the above function to debug the output */
2843
2844 {
2845 /* If there are ever any problems, */
2846 float test_uv[4][2];
2847 int i;
2848 if (is_ortho) {
2850 bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip);
2851 }
2852 else {
2854 bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip);
2855 }
2856 printf("( [(%f,%f), (%f,%f), (%f,%f), (%f,%f)], ",
2857 test_uv[0][0],
2858 test_uv[0][1],
2859 test_uv[1][0],
2860 test_uv[1][1],
2861 test_uv[2][0],
2862 test_uv[2][1],
2863 test_uv[3][0],
2864 test_uv[3][1]);
2865
2866 printf(" [(%f,%f), (%f,%f), (%f,%f)], ",
2867 uv1co[0],
2868 uv1co[1],
2869 uv2co[0],
2870 uv2co[1],
2871 uv3co[0],
2872 uv3co[1]);
2873
2874 printf("[");
2875 for (int i = 0; i < (*tot); i++) {
2876 printf("(%f, %f),", bucket_bounds_uv[i][0], bucket_bounds_uv[i][1]);
2877 }
2878 printf("]),\\\n");
2879 }
2880#endif
2881}
2882
2930
2931#undef ISECT_1
2932#undef ISECT_2
2933#undef ISECT_3
2934#undef ISECT_4
2935#undef ISECT_ALL3
2936#undef ISECT_ALL4
2937
2938/* checks if pt is inside a convex 2D polyline, the polyline must be ordered rotating clockwise
2939 * otherwise it would have to test for mixed (line_point_side_v2 > 0.0f) cases */
2940static bool IsectPoly2Df(const float pt[2], const float uv[][2], const int tot)
2941{
2942 int i;
2943 if (line_point_side_v2(uv[tot - 1], uv[0], pt) < 0.0f) {
2944 return false;
2945 }
2946
2947 for (i = 1; i < tot; i++) {
2948 if (line_point_side_v2(uv[i - 1], uv[i], pt) < 0.0f) {
2949 return false;
2950 }
2951 }
2952
2953 return true;
2954}
2955static bool IsectPoly2Df_twoside(const float pt[2], const float uv[][2], const int tot)
2956{
2957 const bool side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f);
2958
2959 for (int i = 1; i < tot; i++) {
2960 if ((line_point_side_v2(uv[i - 1], uv[i], pt) > 0.0f) != side) {
2961 return false;
2962 }
2963 }
2964
2965 return true;
2966}
2967
2968/* One of the most important function for projection painting,
2969 * since it selects the pixels to be added into each bucket.
2970 *
2971 * initialize pixels from this face where it intersects with the bucket_index,
2972 * optionally initialize pixels for removing seams */
2974 const int thread_index,
2975 const int bucket_index,
2976 const int tri_index,
2977 const int image_index,
2978 const rctf *clip_rect,
2979 const rctf *bucket_bounds,
2980 ImBuf *ibuf,
2981 ImBuf **tmpibuf)
2982{
2983 /* Projection vars, to get the 3D locations into screen space. */
2984 MemArena *arena = ps->arena_mt[thread_index];
2985 LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index;
2986 LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index];
2987 bool threaded = (ps->thread_tot > 1);
2988
2989 TileInfo tinf = {
2990 ps->tile_lock,
2991 ps->do_masking,
2993 tmpibuf,
2994 ps->projImages + image_index,
2995 };
2996
2997 const int3 &tri = ps->corner_tris_eval[tri_index];
2998 const int face_i = ps->corner_tri_faces_eval[tri_index];
2999 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)};
3000 const float *tri_uv[3] = {PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, face_i, tri)};
3001
3002 /* UV/pixel seeking data */
3003 /* Image X/Y-Pixel */
3004 int x, y;
3005 float mask;
3006 /* Image floating point UV - same as x, y but from 0.0-1.0 */
3007 float uv[2];
3008
3009 /* vert co screen-space, these will be assigned to vert_tri[0-2] */
3010 const float *v1coSS, *v2coSS, *v3coSS;
3011
3012 /* Vertex screen-space coords. */
3013 const float *vCo[3];
3014
3015 float w[3], wco[3];
3016
3017 /* for convenience only, these will be assigned to tri_uv[0],1,2 or tri_uv[0],2,3 */
3018 float *uv1co, *uv2co, *uv3co;
3019 float pixelScreenCo[4];
3020 bool do_3d_mapping = ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D;
3021
3022 /* Image-space bounds. */
3023 rcti bounds_px;
3024 /* Variables for getting UV-space bounds. */
3025
3026 /* Bucket bounds in UV space so we can init pixels only for this face. */
3027 float tri_uv_pxoffset[3][2];
3028 float xhalfpx, yhalfpx;
3029 const float ibuf_xf = float(ibuf->x), ibuf_yf = float(ibuf->y);
3030
3031 /* for early loop exit */
3032 int has_x_isect = 0, has_isect = 0;
3033
3034 float uv_clip[8][2];
3035 int uv_clip_tot;
3036 const bool is_ortho = ps->is_ortho;
3037 const bool is_flip_object = ps->is_flip_object;
3038 const bool do_backfacecull = ps->do_backfacecull;
3039 const bool do_clip = RV3D_CLIPPING_ENABLED(ps->v3d, ps->rv3d);
3040
3041 vCo[0] = ps->vert_positions_eval[vert_tri[0]];
3042 vCo[1] = ps->vert_positions_eval[vert_tri[1]];
3043 vCo[2] = ps->vert_positions_eval[vert_tri[2]];
3044
3045 /* Use tri_uv_pxoffset instead of tri_uv so we can offset the UV half a pixel
3046 * this is done so we can avoid offsetting all the pixels by 0.5 which causes
3047 * problems when wrapping negative coords */
3048 xhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf;
3049 yhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf;
3050
3051 /* Note about (PROJ_GEOM_TOLERANCE/x) above...
3052 * Needed to add this offset since UV coords are often quads aligned to pixels.
3053 * In this case pixels can be exactly between 2 triangles causing nasty
3054 * artifacts.
3055 *
3056 * This workaround can be removed and painting will still work on most cases
3057 * but since the first thing most people try is painting onto a quad- better make it work.
3058 */
3059
3060 tri_uv_pxoffset[0][0] = tri_uv[0][0] - xhalfpx;
3061 tri_uv_pxoffset[0][1] = tri_uv[0][1] - yhalfpx;
3062
3063 tri_uv_pxoffset[1][0] = tri_uv[1][0] - xhalfpx;
3064 tri_uv_pxoffset[1][1] = tri_uv[1][1] - yhalfpx;
3065
3066 tri_uv_pxoffset[2][0] = tri_uv[2][0] - xhalfpx;
3067 tri_uv_pxoffset[2][1] = tri_uv[2][1] - yhalfpx;
3068
3069 {
3070 uv1co = tri_uv_pxoffset[0]; /* Was `tri_uv[i1];`. */
3071 uv2co = tri_uv_pxoffset[1]; /* Was `tri_uv[i2];`. */
3072 uv3co = tri_uv_pxoffset[2]; /* Was `tri_uv[i3];`. */
3073
3074 v1coSS = ps->screenCoords[vert_tri[0]];
3075 v2coSS = ps->screenCoords[vert_tri[1]];
3076 v3coSS = ps->screenCoords[vert_tri[2]];
3077
3078 /* This function gives is a concave polyline in UV space from the clipped tri. */
3079 project_bucket_clip_face(is_ortho,
3080 is_flip_object,
3081 clip_rect,
3082 bucket_bounds,
3083 v1coSS,
3084 v2coSS,
3085 v3coSS,
3086 uv1co,
3087 uv2co,
3088 uv3co,
3089 uv_clip,
3090 &uv_clip_tot,
3091 do_backfacecull || ps->do_occlude);
3092
3093 /* Sometimes this happens, better just allow for 8 intersections
3094 * even though there should be max 6 */
3095#if 0
3096 if (uv_clip_tot > 6) {
3097 printf("this should never happen! %d\n", uv_clip_tot);
3098 }
3099#endif
3100
3101 if (pixel_bounds_array(uv_clip, ibuf->x, ibuf->y, uv_clip_tot, &bounds_px)) {
3102#if 0
3103 project_paint_undo_tiles_init(
3104 &bounds_px, ps->projImages + image_index, tmpibuf, tile_width, threaded, ps->do_masking);
3105#endif
3106 /* clip face and */
3107
3108 has_isect = 0;
3109 for (y = bounds_px.ymin; y < bounds_px.ymax; y++) {
3110 // uv[1] = (float(y) + 0.5f) / float(ibuf->y);
3111 /* use pixel offset UV coords instead */
3112 uv[1] = float(y) / ibuf_yf;
3113
3114 has_x_isect = 0;
3115 for (x = bounds_px.xmin; x < bounds_px.xmax; x++) {
3116 // uv[0] = (float(x) + 0.5f) / float(ibuf->x);
3117 /* use pixel offset UV coords instead */
3118 uv[0] = float(x) / ibuf_xf;
3119
3120 /* Note about IsectPoly2Df_twoside, checking the face or uv flipping doesn't work,
3121 * could check the poly direction but better to do this */
3122 if ((do_backfacecull == true && IsectPoly2Df(uv, uv_clip, uv_clip_tot)) ||
3123 (do_backfacecull == false && IsectPoly2Df_twoside(uv, uv_clip, uv_clip_tot)))
3124 {
3125
3126 has_x_isect = has_isect = 1;
3127
3128 if (is_ortho) {
3130 uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
3131 }
3132 else {
3134 uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
3135 }
3136
3137 /* A pity we need to get the world-space pixel location here
3138 * because it is a relatively expensive operation. */
3139 if (do_clip || do_3d_mapping) {
3140 interp_v3_v3v3v3(wco,
3141 ps->vert_positions_eval[vert_tri[0]],
3142 ps->vert_positions_eval[vert_tri[1]],
3143 ps->vert_positions_eval[vert_tri[2]],
3144 w);
3145 if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) {
3146 /* Watch out that no code below this needs to run */
3147 continue;
3148 }
3149 }
3150
3151 /* Is this UV visible from the view? - ray-trace */
3152 /* project_paint_PickFace is less complex, use for testing */
3153 // if (project_paint_PickFace(ps, pixelScreenCo, w, &side) == tri_index) {
3154 if ((ps->do_occlude == false) ||
3155 !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixelScreenCo))
3156 {
3157 mask = project_paint_uvpixel_mask(ps, tri_index, w);
3158
3159 if (mask > 0.0f) {
3161 bucketPixelNodes,
3163 ps, arena, &tinf, x, y, mask, tri_index, pixelScreenCo, wco, w),
3164 arena);
3165 }
3166 }
3167 }
3168 // #if 0
3169 else if (has_x_isect) {
3170 /* assuming the face is not a bow-tie - we know we can't intersect again on the X */
3171 break;
3172 }
3173 // #endif
3174 }
3175
3176#if 0 /* TODO: investigate why this doesn't work sometimes! it should! */
3177 /* no intersection for this entire row,
3178 * after some intersection above means we can quit now */
3179 if (has_x_isect == 0 && has_isect) {
3180 break;
3181 }
3182#endif
3183 }
3184 }
3185 }
3186
3187#ifndef PROJ_DEBUG_NOSEAMBLEED
3188 if (ps->seam_bleed_px > 0.0f && !(ps->faceSeamFlags[tri_index] & PROJ_FACE_DEGENERATE)) {
3189 int face_seam_flag;
3190
3191 if (threaded) {
3192 /* Other threads could be modifying these vars. */
3194 }
3195
3196 face_seam_flag = ps->faceSeamFlags[tri_index];
3197
3198 /* are any of our edges un-initialized? */
3199 if ((face_seam_flag & PROJ_FACE_SEAM_INIT0) == 0 ||
3200 (face_seam_flag & PROJ_FACE_SEAM_INIT1) == 0 ||
3201 (face_seam_flag & PROJ_FACE_SEAM_INIT2) == 0)
3202 {
3203 project_face_seams_init(ps, arena, tri_index, 0, true, ibuf->x, ibuf->y);
3204 face_seam_flag = ps->faceSeamFlags[tri_index];
3205# if 0
3206 printf("seams - %d %d %d %d\n",
3210# endif
3211 }
3212
3213 if ((face_seam_flag & (PROJ_FACE_SEAM0 | PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2)) == 0) {
3214
3215 if (threaded) {
3216 /* Other threads could be modifying these vars. */
3218 }
3219 }
3220 else {
3221 /* we have a seam - deal with it! */
3222
3223 /* Inset face coords.
3224 * - screen-space in orthographic view.
3225 * - world-space in perspective view.
3226 */
3227 float insetCos[3][3];
3228
3229 /* Vertex screen-space coords. */
3230 const float *vCoSS[3];
3231
3232 /* Store the screen-space coords of the face,
3233 * clipped by the bucket's screen aligned rectangle. */
3234 float bucket_clip_edges[2][2];
3235 float edge_verts_inset_clip[2][3];
3236 /* face edge pairs - loop through these:
3237 * ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */
3238 int fidx1, fidx2;
3239
3240 float seam_subsection[4][2];
3241 float fac1, fac2;
3242
3243 /* Pixel-space UVs. */
3244 float tri_puv[3][2];
3245
3246 tri_puv[0][0] = tri_uv_pxoffset[0][0] * ibuf->x;
3247 tri_puv[0][1] = tri_uv_pxoffset[0][1] * ibuf->y;
3248
3249 tri_puv[1][0] = tri_uv_pxoffset[1][0] * ibuf->x;
3250 tri_puv[1][1] = tri_uv_pxoffset[1][1] * ibuf->y;
3251
3252 tri_puv[2][0] = tri_uv_pxoffset[2][0] * ibuf->x;
3253 tri_puv[2][1] = tri_uv_pxoffset[2][1] * ibuf->y;
3254
3255 if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) ||
3256 (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) ||
3257 (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2))
3258 {
3259 uv_image_outset(ps, tri_uv_pxoffset, tri_puv, tri_index, ibuf->x, ibuf->y);
3260 }
3261
3262 /* ps->loopSeamUVs can't be modified when threading, now this is done we can unlock. */
3263 if (threaded) {
3264 /* Other threads could be modifying these vars */
3266 }
3267
3268 vCoSS[0] = ps->screenCoords[vert_tri[0]];
3269 vCoSS[1] = ps->screenCoords[vert_tri[1]];
3270 vCoSS[2] = ps->screenCoords[vert_tri[2]];
3271
3272 /* PROJ_FACE_SCALE_SEAM must be slightly less than 1.0f */
3273 if (is_ortho) {
3274 scale_tri(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM);
3275 }
3276 else {
3277 scale_tri(insetCos, vCo, PROJ_FACE_SCALE_SEAM);
3278 }
3279
3280 for (fidx1 = 0; fidx1 < 3; fidx1++) {
3281 /* next fidx in the face (0,1,2) -> (1,2,0) */
3282 fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1;
3283
3284 if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */
3285 line_clip_rect2f(clip_rect,
3286 bucket_bounds,
3287 vCoSS[fidx1],
3288 vCoSS[fidx2],
3289 bucket_clip_edges[0],
3290 bucket_clip_edges[1]))
3291 {
3292 /* Avoid div by zero. */
3293 if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) {
3294 uint loop_idx = ps->corner_tris_eval[tri_index][fidx1];
3295 LoopSeamData *seam_data = &ps->loopSeamData[loop_idx];
3296 float(*seam_uvs)[2] = seam_data->seam_uvs;
3297
3298 if (is_ortho) {
3299 fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]);
3300 fac2 = line_point_factor_v2(bucket_clip_edges[1], vCoSS[fidx1], vCoSS[fidx2]);
3301 }
3302 else {
3304 ps, bucket_clip_edges[0], vCo[fidx1], vCo[fidx2]);
3306 ps, bucket_clip_edges[1], vCo[fidx1], vCo[fidx2]);
3307 }
3308
3310 seam_subsection[0], tri_uv_pxoffset[fidx1], tri_uv_pxoffset[fidx2], fac1);
3312 seam_subsection[1], tri_uv_pxoffset[fidx1], tri_uv_pxoffset[fidx2], fac2);
3313
3314 interp_v2_v2v2(seam_subsection[2], seam_uvs[0], seam_uvs[1], fac2);
3315 interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], fac1);
3316
3317 /* if the bucket_clip_edges values Z values was kept we could avoid this
3318 * Inset needs to be added so occlusion tests won't hit adjacent faces */
3319 interp_v3_v3v3(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1);
3320 interp_v3_v3v3(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2);
3321
3322 if (pixel_bounds_uv(seam_subsection, ibuf->x, ibuf->y, &bounds_px)) {
3323 /* bounds between the seam rect and the uvspace bucket pixels */
3324
3325 has_isect = 0;
3326 for (y = bounds_px.ymin; y < bounds_px.ymax; y++) {
3327 // uv[1] = (float(y) + 0.5f) / float(ibuf->y);
3328 /* use offset uvs instead */
3329 uv[1] = float(y) / ibuf_yf;
3330
3331 has_x_isect = 0;
3332 for (x = bounds_px.xmin; x < bounds_px.xmax; x++) {
3333 const float puv[2] = {float(x), float(y)};
3334 bool in_bounds;
3335 // uv[0] = (float(x) + 0.5f) / float(ibuf->x);
3336 /* use offset uvs instead */
3337 uv[0] = float(x) / ibuf_xf;
3338
3339 /* test we're inside uvspace bucket and triangle bounds */
3340 if (equals_v2v2(seam_uvs[0], seam_uvs[1])) {
3341 in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection));
3342 }
3343 else {
3344 in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection));
3345 }
3346
3347 if (in_bounds) {
3348 if ((seam_data->corner_dist_sq[0] > 0.0f) &&
3349 (len_squared_v2v2(puv, seam_data->seam_puvs[0]) <
3350 seam_data->corner_dist_sq[0]) &&
3351 (len_squared_v2v2(puv, tri_puv[fidx1]) > ps->seam_bleed_px_sq))
3352 {
3353 in_bounds = false;
3354 }
3355 else if ((seam_data->corner_dist_sq[1] > 0.0f) &&
3356 (len_squared_v2v2(puv, seam_data->seam_puvs[1]) <
3357 seam_data->corner_dist_sq[1]) &&
3358 (len_squared_v2v2(puv, tri_puv[fidx2]) > ps->seam_bleed_px_sq))
3359 {
3360 in_bounds = false;
3361 }
3362 }
3363
3364 if (in_bounds) {
3365 float pixel_on_edge[4];
3366 float fac;
3367
3368 if (is_ortho) {
3370 uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
3371 }
3372 else {
3374 uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w);
3375 }
3376
3377 /* We need the coord of the pixel on the edge, for the occlusion query. */
3378 fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection));
3380 pixel_on_edge, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac);
3381
3382 if (!is_ortho) {
3383 pixel_on_edge[3] = 1.0f;
3384 /* cast because of const */
3385 mul_m4_v4((float(*)[4])ps->projectMat, pixel_on_edge);
3386 pixel_on_edge[0] = float(ps->winx * 0.5f) +
3387 (ps->winx * 0.5f) * pixel_on_edge[0] / pixel_on_edge[3];
3388 pixel_on_edge[1] = float(ps->winy * 0.5f) +
3389 (ps->winy * 0.5f) * pixel_on_edge[1] / pixel_on_edge[3];
3390 /* Use the depth for bucket point occlusion */
3391 pixel_on_edge[2] = pixel_on_edge[2] / pixel_on_edge[3];
3392 }
3393
3394 if ((ps->do_occlude == false) ||
3396 ps, bucketFaceNodes, tri_index, pixel_on_edge))
3397 {
3398 /* A pity we need to get the world-space pixel location here
3399 * because it is a relatively expensive operation. */
3400 if (do_clip || do_3d_mapping) {
3401 interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w);
3402
3403 if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) {
3404 /* Watch out that no code below
3405 * this needs to run */
3406 continue;
3407 }
3408 }
3409
3410 mask = project_paint_uvpixel_mask(ps, tri_index, w);
3411
3412 if (mask > 0.0f) {
3414 bucketPixelNodes,
3416 ps, arena, &tinf, x, y, mask, tri_index, pixelScreenCo, wco, w),
3417 arena);
3418 }
3419 }
3420 }
3421 else if (has_x_isect) {
3422 /* assuming the face is not a bow-tie - we know
3423 * we can't intersect again on the X */
3424 break;
3425 }
3426 }
3427
3428# if 0 /* TODO: investigate why this doesn't work sometimes! it should! */
3429 /* no intersection for this entire row,
3430 * after some intersection above means we can quit now */
3431 if (has_x_isect == 0 && has_isect) {
3432 break;
3433 }
3434# endif
3435 }
3436 }
3437 }
3438 }
3439 }
3440 }
3441 }
3442#else
3443 UNUSED_VARS(vCo, threaded);
3444#endif /* PROJ_DEBUG_NOSEAMBLEED */
3445}
3446
3452 const float min[2],
3453 const float max[2],
3454 int bucketMin[2],
3455 int bucketMax[2])
3456{
3457 /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
3458
3459 /* XXX(jwilkins ): the offset of 0.5 is always truncated to zero and the offset of 1.5f
3460 * is always truncated to 1, is this really correct? */
3461
3462 /* these offsets of 0.5 and 1.5 seem odd but they are correct */
3463 bucketMin[0] = int(int((float(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) +
3464 0.5f);
3465 bucketMin[1] = int(int((float(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) +
3466 0.5f);
3467
3468 bucketMax[0] = int(int((float(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) +
3469 1.5f);
3470 bucketMax[1] = int(int((float(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) +
3471 1.5f);
3472
3473 /* in case the rect is outside the mesh 2d bounds */
3474 CLAMP(bucketMin[0], 0, ps->buckets_x);
3475 CLAMP(bucketMin[1], 0, ps->buckets_y);
3476
3477 CLAMP(bucketMax[0], 0, ps->buckets_x);
3478 CLAMP(bucketMax[1], 0, ps->buckets_y);
3479}
3480
3481/* set bucket_bounds to a screen space-aligned floating point bound-box */
3483 const int bucket_x,
3484 const int bucket_y,
3485 rctf *r_bucket_bounds)
3486{
3487 /* left */
3488 r_bucket_bounds->xmin = (ps->screenMin[0] + ((bucket_x) * (ps->screen_width / ps->buckets_x)));
3489 /* right */
3490 r_bucket_bounds->xmax = (ps->screenMin[0] +
3491 ((bucket_x + 1) * (ps->screen_width / ps->buckets_x)));
3492
3493 /* bottom */
3494 r_bucket_bounds->ymin = (ps->screenMin[1] + ((bucket_y) * (ps->screen_height / ps->buckets_y)));
3495 /* top */
3496 r_bucket_bounds->ymax = (ps->screenMin[1] +
3497 ((bucket_y + 1) * (ps->screen_height / ps->buckets_y)));
3498}
3499
3500/* Fill this bucket with pixels from the faces that intersect it.
3501 *
3502 * have bucket_bounds as an argument so we don't need to give bucket_x/y the rect function needs */
3504 const int thread_index,
3505 const int bucket_index,
3506 const rctf *clip_rect,
3507 const rctf *bucket_bounds)
3508{
3509 LinkNode *node;
3510 int tri_index, image_index = 0;
3511 ImBuf *ibuf = nullptr;
3512 Image *tpage_last = nullptr, *tpage;
3513 ImBuf *tmpibuf = nullptr;
3514 int tile_last = 0;
3515
3516 if (ps->image_tot == 1) {
3517 /* Simple loop, no context switching */
3518 ibuf = ps->projImages[0].ibuf;
3519
3520 for (node = ps->bucketFaces[bucket_index]; node; node = node->next) {
3522 thread_index,
3523 bucket_index,
3524 POINTER_AS_INT(node->link),
3525 0,
3526 clip_rect,
3527 bucket_bounds,
3528 ibuf,
3529 &tmpibuf);
3530 }
3531 }
3532 else {
3533
3534 /* More complicated loop, switch between images */
3535 for (node = ps->bucketFaces[bucket_index]; node; node = node->next) {
3536 tri_index = POINTER_AS_INT(node->link);
3537
3538 const int3 &tri = ps->corner_tris_eval[tri_index];
3539 const int face_i = ps->corner_tri_faces_eval[tri_index];
3540 const float *tri_uv[3] = {PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, face_i, tri)};
3541
3542 /* Image context switching */
3543 tpage = project_paint_face_paint_image(ps, tri_index);
3544 int tile = project_paint_face_paint_tile(tpage, tri_uv[0]);
3545 if (tpage_last != tpage || tile_last != tile) {
3546 tpage_last = tpage;
3547 tile_last = tile;
3548
3549 ibuf = nullptr;
3550 for (image_index = 0; image_index < ps->image_tot; image_index++) {
3551 ProjPaintImage *projIma = &ps->projImages[image_index];
3552 if ((projIma->ima == tpage) && (projIma->iuser.tile == tile)) {
3553 ibuf = projIma->ibuf;
3554 break;
3555 }
3556 }
3557 BLI_assert(ibuf != nullptr);
3558 }
3559 /* context switching done */
3560
3562 thread_index,
3563 bucket_index,
3564 tri_index,
3565 image_index,
3566 clip_rect,
3567 bucket_bounds,
3568 ibuf,
3569 &tmpibuf);
3570 }
3571 }
3572
3573 if (tmpibuf) {
3574 IMB_freeImBuf(tmpibuf);
3575 }
3576
3577 ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT;
3578}
3579
3580/* We want to know if a bucket and a face overlap in screen-space.
3581 *
3582 * NOTE: if this ever returns false positives its not that bad, since a face in the bounding area
3583 * will have its pixels calculated when it might not be needed later, (at the moment at least)
3584 * obviously it shouldn't have bugs though. */
3585
3587 int bucket_x,
3588 int bucket_y,
3589 const int3 &tri)
3590{
3591 /* TODO: replace this with a trickier method that uses side-of-line for all
3592 * #ProjPaintState.screenCoords edges against the closest bucket corner. */
3593 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)};
3594 rctf bucket_bounds;
3595 float p1[2], p2[2], p3[2], p4[2];
3596 const float *v, *v1, *v2, *v3;
3597 int fidx;
3598
3599 project_bucket_bounds(ps, bucket_x, bucket_y, &bucket_bounds);
3600
3601 /* Is one of the faces verts in the bucket bounds? */
3602
3603 fidx = 2;
3604 do {
3605 v = ps->screenCoords[vert_tri[fidx]];
3606 if (BLI_rctf_isect_pt_v(&bucket_bounds, v)) {
3607 return true;
3608 }
3609 } while (fidx--);
3610
3611 v1 = ps->screenCoords[vert_tri[0]];
3612 v2 = ps->screenCoords[vert_tri[1]];
3613 v3 = ps->screenCoords[vert_tri[2]];
3614
3615 p1[0] = bucket_bounds.xmin;
3616 p1[1] = bucket_bounds.ymin;
3617 p2[0] = bucket_bounds.xmin;
3618 p2[1] = bucket_bounds.ymax;
3619 p3[0] = bucket_bounds.xmax;
3620 p3[1] = bucket_bounds.ymax;
3621 p4[0] = bucket_bounds.xmax;
3622 p4[1] = bucket_bounds.ymin;
3623
3624 if (isect_point_tri_v2(p1, v1, v2, v3) || isect_point_tri_v2(p2, v1, v2, v3) ||
3625 isect_point_tri_v2(p3, v1, v2, v3) || isect_point_tri_v2(p4, v1, v2, v3) ||
3626 /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */
3627 (isect_seg_seg_v2(p1, p2, v1, v2) || isect_seg_seg_v2(p1, p2, v2, v3)) ||
3628 (isect_seg_seg_v2(p2, p3, v1, v2) || isect_seg_seg_v2(p2, p3, v2, v3)) ||
3629 (isect_seg_seg_v2(p3, p4, v1, v2) || isect_seg_seg_v2(p3, p4, v2, v3)) ||
3630 (isect_seg_seg_v2(p4, p1, v1, v2) || isect_seg_seg_v2(p4, p1, v2, v3)))
3631 {
3632 return true;
3633 }
3634
3635 return false;
3636}
3637
3638/* Add faces to the bucket but don't initialize its pixels
3639 * TODO: when painting occluded, sort the faces on their min-Z
3640 * and only add faces that faces that are not occluded */
3642 const int3 &corner_tri,
3643 const int tri_index)
3644{
3645 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, corner_tri)};
3646 float min[2], max[2], *vCoSS;
3647 /* for ps->bucketRect indexing */
3648 int bucketMin[2], bucketMax[2];
3649 int fidx, bucket_x, bucket_y;
3650 /* for early loop exit */
3651 int has_x_isect = -1, has_isect = 0;
3652 /* just use the first thread arena since threading has not started yet */
3653 MemArena *arena = ps->arena_mt[0];
3654
3656
3657 fidx = 2;
3658 do {
3659 vCoSS = ps->screenCoords[vert_tri[fidx]];
3660 minmax_v2v2_v2(min, max, vCoSS);
3661 } while (fidx--);
3662
3663 project_paint_bucket_bounds(ps, min, max, bucketMin, bucketMax);
3664
3665 for (bucket_y = bucketMin[1]; bucket_y < bucketMax[1]; bucket_y++) {
3666 has_x_isect = 0;
3667 for (bucket_x = bucketMin[0]; bucket_x < bucketMax[0]; bucket_x++) {
3668 if (project_bucket_face_isect(ps, bucket_x, bucket_y, corner_tri)) {
3669 int bucket_index = bucket_x + (bucket_y * ps->buckets_x);
3670 BLI_linklist_prepend_arena(&ps->bucketFaces[bucket_index],
3671 /* cast to a pointer to shut up the compiler */
3672 POINTER_FROM_INT(tri_index),
3673 arena);
3674
3675 has_x_isect = has_isect = 1;
3676 }
3677 else if (has_x_isect) {
3678 /* assuming the face is not a bow-tie - we know we can't intersect again on the X */
3679 break;
3680 }
3681 }
3682
3683 /* no intersection for this entire row,
3684 * after some intersection above means we can quit now */
3685 if (has_x_isect == 0 && has_isect) {
3686 break;
3687 }
3688 }
3689
3690#ifndef PROJ_DEBUG_NOSEAMBLEED
3691 if (ps->seam_bleed_px > 0.0f) {
3692 /* set as uninitialized */
3693 ps->loopSeamData[corner_tri[0]].seam_uvs[0][0] = FLT_MAX;
3694 ps->loopSeamData[corner_tri[1]].seam_uvs[0][0] = FLT_MAX;
3695 ps->loopSeamData[corner_tri[2]].seam_uvs[0][0] = FLT_MAX;
3696 }
3697#endif
3698}
3699
3700static void proj_paint_state_viewport_init(ProjPaintState *ps, const char symmetry_flag)
3701{
3702 float mat[3][3];
3703 float viewmat[4][4];
3704 float viewinv[4][4];
3705
3706 ps->viewDir[0] = 0.0f;
3707 ps->viewDir[1] = 0.0f;
3708 ps->viewDir[2] = 1.0f;
3709
3710 copy_m4_m4(ps->obmat, ps->ob->object_to_world().ptr());
3711
3712 if (symmetry_flag) {
3713 int i;
3714 for (i = 0; i < 3; i++) {
3715 if ((symmetry_flag >> i) & 1) {
3716 negate_v3(ps->obmat[i]);
3717 ps->is_flip_object = !ps->is_flip_object;
3718 }
3719 }
3720 }
3721
3722 invert_m4_m4(ps->obmat_imat, ps->obmat);
3723
3725 /* normal drawing */
3726 ps->winx = ps->region->winx;
3727 ps->winy = ps->region->winy;
3728
3729 copy_m4_m4(viewmat, ps->rv3d->viewmat);
3730 copy_m4_m4(viewinv, ps->rv3d->viewinv);
3731
3733 ps->rv3d, blender::float4x4(ps->obmat));
3734 copy_m4_m4(ps->projectMat, projection.ptr());
3735
3737 ps->depsgraph, ps->v3d, ps->rv3d, true, &ps->clip_start, &ps->clip_end);
3738 }
3739 else {
3740 /* re-projection */
3741 float winmat[4][4];
3742 float vmat[4][4];
3743
3744 ps->winx = ps->reproject_ibuf->x;
3745 ps->winy = ps->reproject_ibuf->y;
3746
3747 if (ps->source == PROJ_SRC_IMAGE_VIEW) {
3748 /* image stores camera data, tricky */
3751
3752 const float *array = (float *)IDP_Array(view_data);
3753
3754 /* use image array, written when creating image */
3755 memcpy(winmat, array, sizeof(winmat));
3756 array += sizeof(winmat) / sizeof(float);
3757 memcpy(viewmat, array, sizeof(viewmat));
3758 array += sizeof(viewmat) / sizeof(float);
3759 ps->clip_start = array[0];
3760 ps->clip_end = array[1];
3761 ps->is_ortho = bool(array[2]);
3762
3763 invert_m4_m4(viewinv, viewmat);
3764 }
3765 else if (ps->source == PROJ_SRC_IMAGE_CAM) {
3766 Object *cam_ob_eval = DEG_get_evaluated(ps->depsgraph, ps->scene->camera);
3768
3769 /* viewmat & viewinv */
3770 copy_m4_m4(viewinv, cam_ob_eval->object_to_world().ptr());
3771 normalize_m4(viewinv);
3772 invert_m4_m4(viewmat, viewinv);
3773
3774 /* window matrix, clipping and ortho */
3777 BKE_camera_params_compute_viewplane(&params, ps->winx, ps->winy, 1.0f, 1.0f);
3779
3780 copy_m4_m4(winmat, params.winmat);
3781 ps->clip_start = params.clip_start;
3782 ps->clip_end = params.clip_end;
3783 ps->is_ortho = params.is_ortho;
3784 }
3785 else {
3786 BLI_assert(0);
3787 }
3788
3789 /* same as #ED_view3d_ob_project_mat_get */
3790 mul_m4_m4m4(vmat, viewmat, ps->obmat);
3791 mul_m4_m4m4(ps->projectMat, winmat, vmat);
3792 }
3793
3795
3796 /* viewDir - object relative */
3797 copy_m3_m4(mat, viewinv);
3798 mul_m3_v3(mat, ps->viewDir);
3799 copy_m3_m4(mat, ps->obmat_imat);
3800 mul_m3_v3(mat, ps->viewDir);
3801 normalize_v3(ps->viewDir);
3802
3803 if (UNLIKELY(ps->is_flip_object)) {
3804 negate_v3(ps->viewDir);
3805 }
3806
3807 /* viewPos - object relative */
3808 copy_v3_v3(ps->viewPos, viewinv[3]);
3809 copy_m3_m4(mat, ps->obmat_imat);
3810 mul_m3_v3(mat, ps->viewPos);
3811 add_v3_v3(ps->viewPos, ps->obmat_imat[3]);
3812}
3813
3814static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter)
3815{
3816 float *projScreenCo;
3817 float projMargin;
3818 int a;
3819
3821
3822 ps->screenCoords = static_cast<float(*)[4]>(
3823 MEM_mallocN(sizeof(float) * ps->totvert_eval * 4, "ProjectPaint ScreenVerts"));
3824 projScreenCo = *ps->screenCoords;
3825
3826 if (ps->is_ortho) {
3827 for (a = 0; a < ps->totvert_eval; a++, projScreenCo += 4) {
3828 mul_v3_m4v3(projScreenCo, ps->projectMat, ps->vert_positions_eval[a]);
3829
3830 /* screen space, not clamped */
3831 projScreenCo[0] = float(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0];
3832 projScreenCo[1] = float(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1];
3833 minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo);
3834 }
3835 }
3836 else {
3837 for (a = 0; a < ps->totvert_eval; a++, projScreenCo += 4) {
3838 copy_v3_v3(projScreenCo, ps->vert_positions_eval[a]);
3839 projScreenCo[3] = 1.0f;
3840
3841 mul_m4_v4(ps->projectMat, projScreenCo);
3842
3843 if (projScreenCo[3] > ps->clip_start) {
3844 /* screen space, not clamped */
3845 projScreenCo[0] = float(ps->winx * 0.5f) +
3846 (ps->winx * 0.5f) * projScreenCo[0] / projScreenCo[3];
3847 projScreenCo[1] = float(ps->winy * 0.5f) +
3848 (ps->winy * 0.5f) * projScreenCo[1] / projScreenCo[3];
3849 /* Use the depth for bucket point occlusion */
3850 projScreenCo[2] = projScreenCo[2] / projScreenCo[3];
3851 minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo);
3852 }
3853 else {
3854 /* TODO: deal with cases where 1 side of a face goes behind the view ?
3855 *
3856 * After some research this is actually very tricky, only option is to
3857 * clip the derived mesh before painting, which is a Pain */
3858 projScreenCo[0] = FLT_MAX;
3859 }
3860 }
3861 }
3862
3863 /* If this border is not added we get artifacts for faces that
3864 * have a parallel edge and at the bounds of the 2D projected verts eg
3865 * - a single screen aligned quad */
3866 projMargin = (ps->screenMax[0] - ps->screenMin[0]) * 0.000001f;
3867 ps->screenMax[0] += projMargin;
3868 ps->screenMin[0] -= projMargin;
3869 projMargin = (ps->screenMax[1] - ps->screenMin[1]) * 0.000001f;
3870 ps->screenMax[1] += projMargin;
3871 ps->screenMin[1] -= projMargin;
3872
3873 if (ps->source == PROJ_SRC_VIEW) {
3874#ifdef PROJ_DEBUG_WINCLIP
3875 CLAMP(ps->screenMin[0], float(-diameter), float(ps->winx + diameter));
3876 CLAMP(ps->screenMax[0], float(-diameter), float(ps->winx + diameter));
3877
3878 CLAMP(ps->screenMin[1], float(-diameter), float(ps->winy + diameter));
3879 CLAMP(ps->screenMax[1], float(-diameter), float(ps->winy + diameter));
3880#else
3881 UNUSED_VARS(diameter);
3882#endif
3883 }
3884 else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */
3885 ps->screenMin[0] = 0;
3886 ps->screenMax[0] = float(ps->winx);
3887
3888 ps->screenMin[1] = 0;
3889 ps->screenMax[1] = float(ps->winy);
3890 }
3891}
3892
3894{
3895 float *cavities;
3896 int a;
3897
3898 if (ps->do_mask_cavity) {
3899 int *counter = MEM_calloc_arrayN<int>(ps->totvert_eval, "counter");
3900 float(*edges)[3] = static_cast<float(*)[3]>(
3901 MEM_callocN(sizeof(float[3]) * ps->totvert_eval, "edges"));
3902 ps->cavities = MEM_malloc_arrayN<float>(ps->totvert_eval, "ProjectPaint Cavities");
3903 cavities = ps->cavities;
3904
3905 for (const int64_t i : ps->edges_eval.index_range()) {
3906 const blender::int2 &edge = ps->edges_eval[i];
3907 float e[3];
3908 sub_v3_v3v3(e, ps->vert_positions_eval[edge[0]], ps->vert_positions_eval[edge[1]]);
3909 normalize_v3(e);
3910 add_v3_v3(edges[edge[1]], e);
3911 counter[edge[1]]++;
3912 sub_v3_v3(edges[edge[0]], e);
3913 counter[edge[0]]++;
3914 }
3915 for (a = 0; a < ps->totvert_eval; a++) {
3916 if (counter[a] > 0) {
3917 mul_v3_fl(edges[a], 1.0f / counter[a]);
3918 /* Augment the difference. */
3919 cavities[a] = safe_acosf(10.0f * dot_v3v3(ps->vert_normals[a], edges[a])) * float(M_1_PI);
3920 }
3921 else {
3922 cavities[a] = 0.0;
3923 }
3924 }
3925
3926 MEM_freeN(counter);
3927 MEM_freeN(edges);
3928 }
3929}
3930
3931#ifndef PROJ_DEBUG_NOSEAMBLEED
3933{
3934 if (ps->seam_bleed_px > 0.0f) {
3935 ps->vertFaces = MEM_calloc_arrayN<LinkNode *>(ps->totvert_eval, "paint-vertFaces");
3936 ps->faceSeamFlags = MEM_calloc_arrayN<ushort>(ps->corner_tris_eval.size(), __func__);
3937 ps->faceWindingFlags = MEM_calloc_arrayN<char>(ps->corner_tris_eval.size(), __func__);
3938 ps->loopSeamData = MEM_malloc_arrayN<LoopSeamData>(ps->totloop_eval, "paint-loopSeamUVs");
3939 ps->vertSeams = MEM_calloc_arrayN<ListBase>(ps->totvert_eval, "paint-vertSeams");
3940 }
3941}
3942#endif
3943
3944static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_threads)
3945{
3946 /* Thread stuff
3947 *
3948 * very small brushes run a lot slower multi-threaded since the advantage with
3949 * threads is being able to fill in multiple buckets at once.
3950 * Only use threads for bigger brushes. */
3951
3953
3954 /* workaround for #35057, disable threading if diameter is less than is possible for
3955 * optimum bucket number generation */
3956 if (reset_threads) {
3957 ps->thread_tot = 1;
3958 }
3959
3960 if (ps->is_shared_user == false) {
3961 if (ps->thread_tot > 1) {
3962 ps->tile_lock = MEM_mallocN<SpinLock>("projpaint_tile_lock");
3964 }
3965
3967 }
3968
3969 for (int a = 0; a < ps->thread_tot; a++) {
3970 ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena");
3971 }
3972}
3973
3975{
3976 if (ps->do_backfacecull && ps->do_mask_normal) {
3977 float viewDirPersp[3];
3978 float no[3];
3979 int a;
3980
3981 ps->vertFlags = MEM_calloc_arrayN<char>(ps->totvert_eval, "paint-vertFlags");
3982
3983 for (a = 0; a < ps->totvert_eval; a++) {
3984 copy_v3_v3(no, ps->vert_normals[a]);
3985 if (UNLIKELY(ps->is_flip_object)) {
3986 negate_v3(no);
3987 }
3988
3989 if (ps->is_ortho) {
3990 if (dot_v3v3(ps->viewDir, no) <= ps->normal_angle__cos) {
3991 /* 1 vert of this face is towards us */
3992 ps->vertFlags[a] |= PROJ_VERT_CULL;
3993 }
3994 }
3995 else {
3996 sub_v3_v3v3(viewDirPersp, ps->viewPos, ps->vert_positions_eval[a]);
3997 normalize_v3(viewDirPersp);
3998 if (UNLIKELY(ps->is_flip_object)) {
3999 negate_v3(viewDirPersp);
4000 }
4001 if (dot_v3v3(viewDirPersp, no) <= ps->normal_angle__cos) {
4002 /* 1 vert of this face is towards us */
4003 ps->vertFlags[a] |= PROJ_VERT_CULL;
4004 }
4005 }
4006 }
4007 }
4008 else {
4009 ps->vertFlags = nullptr;
4010 }
4011}
4012
4013#ifndef PROJ_DEBUG_NOSEAMBLEED
4015 MemArena *arena,
4016 const int3 &corner_tri,
4017 const int tri_index)
4018{
4019 /* add face user if we have bleed enabled, set the UV seam flags later */
4020 /* annoying but we need to add all faces even ones we never use elsewhere */
4021 if (ps->seam_bleed_px > 0.0f) {
4022 const int face_i = ps->corner_tri_faces_eval[tri_index];
4023 const float *tri_uv[3] = {PS_CORNER_TRI_AS_UV_3(ps->poly_to_loop_uv, face_i, corner_tri)};
4024
4025 /* Check for degenerate triangles. Degenerate faces cause trouble with bleed computations.
4026 * Ideally this would be checked later, not to add to the cost of computing non-degenerate
4027 * triangles, but that would allow other triangles to still find adjacent seams on degenerate
4028 * triangles, potentially causing incorrect results. */
4029 if (area_tri_v2(UNPACK3(tri_uv)) > 0.0f) {
4030 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, corner_tri)};
4031 void *tri_index_p = POINTER_FROM_INT(tri_index);
4032
4033 BLI_linklist_prepend_arena(&ps->vertFaces[vert_tri[0]], tri_index_p, arena);
4034 BLI_linklist_prepend_arena(&ps->vertFaces[vert_tri[1]], tri_index_p, arena);
4035 BLI_linklist_prepend_arena(&ps->vertFaces[vert_tri[2]], tri_index_p, arena);
4036 }
4037 else {
4038 ps->faceSeamFlags[tri_index] |= PROJ_FACE_DEGENERATE;
4039 }
4040 }
4041}
4042#endif
4043
4044/* Return true if evaluated mesh can be painted on, false otherwise */
4046{
4048 Object *ob = ps->ob;
4049
4050 const Object *ob_eval = DEG_get_evaluated(depsgraph, ob);
4052 if (!ps->mesh_eval) {
4053 return false;
4054 }
4055
4057 ps->mesh_eval = nullptr;
4058 return false;
4059 }
4060
4061 /* Build final material array, we use this a lot here. */
4062 /* materials start from 1, default material is 0 */
4063 const int totmat = ob->totcol + 1;
4064 ps->mat_array = static_cast<Material **>(
4065 MEM_malloc_arrayN(totmat, sizeof(*ps->mat_array), __func__));
4066 /* We leave last material as empty - rationale here is being able to index
4067 * the materials by using the mf->mat_nr directly and leaving the last
4068 * material as nullptr in case no materials exist on mesh, so indexing will not fail. */
4069 for (int i = 0; i < totmat - 1; i++) {
4070 ps->mat_array[i] = BKE_object_material_get(ob, i + 1);
4071 }
4072 ps->mat_array[totmat - 1] = nullptr;
4073
4074 ps->vert_positions_eval = ps->mesh_eval->vert_positions();
4075 ps->vert_normals = ps->mesh_eval->vert_normals();
4076 ps->edges_eval = ps->mesh_eval->edges();
4077 ps->faces_eval = ps->mesh_eval->faces();
4078 ps->corner_verts_eval = ps->mesh_eval->corner_verts();
4079 ps->select_poly_eval = (const bool *)CustomData_get_layer_named(
4080 &ps->mesh_eval->face_data, CD_PROP_BOOL, ".select_poly");
4081 ps->hide_poly_eval = (const bool *)CustomData_get_layer_named(
4082 &ps->mesh_eval->face_data, CD_PROP_BOOL, ".hide_poly");
4084 &ps->mesh_eval->face_data, CD_PROP_INT32, "material_index");
4085 ps->sharp_faces_eval = static_cast<const bool *>(
4087
4088 ps->totvert_eval = ps->mesh_eval->verts_num;
4091
4092 ps->corner_tris_eval = ps->mesh_eval->corner_tris();
4093 ps->corner_tri_faces_eval = ps->mesh_eval->corner_tri_faces();
4094
4095 ps->poly_to_loop_uv = static_cast<const float(**)[2]>(
4096 MEM_mallocN(ps->faces_num_eval * sizeof(float(*)[2]), "proj_paint_mtfaces"));
4097
4098 return true;
4099}
4100
4106
4108{
4109 const float(*mloopuv_clone_base)[2] = nullptr;
4110
4111 /* use clone mtface? */
4112 if (ps->do_layer_clone) {
4113 const int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->corner_data,
4115
4116 ps->poly_to_loop_uv_clone = static_cast<const float(**)[2]>(
4117 MEM_mallocN(ps->faces_num_eval * sizeof(float(*)[2]), "proj_paint_mtfaces"));
4118
4119 if (layer_num != -1) {
4120 mloopuv_clone_base = static_cast<const float(*)[2]>(
4122 }
4123
4124 if (mloopuv_clone_base == nullptr) {
4125 /* get active instead */
4126 mloopuv_clone_base = static_cast<const float(*)[2]>(
4128 }
4129 }
4130
4131 memset(layer_clone, 0, sizeof(*layer_clone));
4132 layer_clone->mloopuv_clone_base = mloopuv_clone_base;
4133}
4134
4135/* Return true if face should be skipped, false otherwise */
4138 const TexPaintSlot *slot,
4139 const int tri_index)
4140{
4141 if (ps->do_layer_clone) {
4142 if (ps->do_material_slots) {
4143 lc->slot_clone = project_paint_face_clone_slot(ps, tri_index);
4144 /* all faces should have a valid slot, reassert here */
4145 if (ELEM(lc->slot_clone, nullptr, slot)) {
4146 return true;
4147 }
4148 }
4149 else if (ps->clone_ima == ps->canvas_ima) {
4150 return true;
4151 }
4152
4153 if (ps->do_material_slots) {
4154 if (lc->slot_clone != lc->slot_last_clone) {
4155 if (!lc->slot_clone->uvname ||
4156 !(lc->mloopuv_clone_base = static_cast<const float(*)[2]>(CustomData_get_layer_named(
4158 {
4159 lc->mloopuv_clone_base = static_cast<const float(*)[2]>(
4161 }
4162 lc->slot_last_clone = lc->slot_clone;
4163 }
4164 }
4165
4166 /* will set multiple times for 4+ sided poly */
4168 }
4169 return false;
4170}
4171
4173 const bool *select_poly_orig;
4174 const bool *hide_poly_orig;
4176};
4177
4179{
4180 memset(face_lookup, 0, sizeof(*face_lookup));
4181 Mesh *orig_mesh = (Mesh *)ps->ob->data;
4182 face_lookup->index_mp_to_orig = static_cast<const int *>(
4184 if (ps->do_face_sel) {
4185 face_lookup->select_poly_orig = static_cast<const bool *>(
4186 CustomData_get_layer_named(&orig_mesh->face_data, CD_PROP_BOOL, ".select_poly"));
4187 }
4188 face_lookup->hide_poly_orig = static_cast<const bool *>(
4189 CustomData_get_layer_named(&orig_mesh->face_data, CD_PROP_BOOL, ".hide_poly"));
4190}
4191
4192/* Return true if face should be considered paintable, false otherwise */
4194 const ProjPaintFaceLookup *face_lookup,
4195 const int tri_i)
4196{
4197 if (ps->do_face_sel) {
4198 int orig_index;
4199 const int face_i = ps->corner_tri_faces_eval[tri_i];
4200 if ((face_lookup->index_mp_to_orig != nullptr) &&
4201 ((orig_index = (face_lookup->index_mp_to_orig[face_i])) != ORIGINDEX_NONE))
4202 {
4203 return face_lookup->select_poly_orig && face_lookup->select_poly_orig[orig_index];
4204 }
4205 return ps->select_poly_eval && ps->select_poly_eval[face_i];
4206 }
4207 int orig_index;
4208 const int face_i = ps->corner_tri_faces_eval[tri_i];
4209 if ((face_lookup->index_mp_to_orig != nullptr) &&
4210 ((orig_index = (face_lookup->index_mp_to_orig[face_i])) != ORIGINDEX_NONE))
4211 {
4212 return !(face_lookup->hide_poly_orig && face_lookup->hide_poly_orig[orig_index]);
4213 }
4214 return !(ps->hide_poly_eval && ps->hide_poly_eval[face_i]);
4215}
4216
4218 const float *v1;
4219 const float *v2;
4220 const float *v3;
4221};
4222
4224 const int3 &corner_tri,
4225 ProjPaintFaceCoSS *coSS)
4226{
4227 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, corner_tri)};
4228 coSS->v1 = ps->screenCoords[vert_tri[0]];
4229 coSS->v2 = ps->screenCoords[vert_tri[1]];
4230 coSS->v3 = ps->screenCoords[vert_tri[2]];
4231}
4232
4233/* Return true if face should be culled, false otherwise */
4235{
4236 if (!ps->is_ortho) {
4237 if (coSS->v1[0] == FLT_MAX || coSS->v2[0] == FLT_MAX || coSS->v3[0] == FLT_MAX) {
4238 return true;
4239 }
4240 }
4241 return false;
4242}
4243
4244#ifdef PROJ_DEBUG_WINCLIP
4245/* Return true if face should be culled, false otherwise */
4246static bool project_paint_winclip(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS)
4247{
4248 /* ignore faces outside the view */
4249 return ((ps->source != PROJ_SRC_VIEW_FILL) &&
4250 ((coSS->v1[0] < ps->screenMin[0] && coSS->v2[0] < ps->screenMin[0] &&
4251 coSS->v3[0] < ps->screenMin[0]) ||
4252
4253 (coSS->v1[0] > ps->screenMax[0] && coSS->v2[0] > ps->screenMax[0] &&
4254 coSS->v3[0] > ps->screenMax[0]) ||
4255
4256 (coSS->v1[1] < ps->screenMin[1] && coSS->v2[1] < ps->screenMin[1] &&
4257 coSS->v3[1] < ps->screenMin[1]) ||
4258
4259 (coSS->v1[1] > ps->screenMax[1] && coSS->v2[1] > ps->screenMax[1] &&
4260 coSS->v3[1] > ps->screenMax[1])));
4261}
4262#endif /* PROJ_DEBUG_WINCLIP */
4263
4269
4271 MemArena *arena,
4272 ListBase *used_images)
4273{
4274 ProjPaintImage *projIma;
4275 PrepareImageEntry *entry;
4276 int i;
4277
4278 /* build an array of images we use */
4279 projIma = ps->projImages = static_cast<ProjPaintImage *>(
4280 BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot));
4281
4282 for (entry = static_cast<PrepareImageEntry *>(used_images->first), i = 0; entry;
4283 entry = entry->next, i++, projIma++)
4284 {
4285 projIma->iuser = entry->iuser;
4286 int size;
4287 projIma->ima = entry->ima;
4288 projIma->touch = false;
4289 projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, nullptr);
4290 if (projIma->ibuf == nullptr) {
4291 projIma->iuser.tile = 0;
4292 projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, nullptr);
4293 BLI_assert(projIma->ibuf != nullptr);
4294 }
4295 size = sizeof(void **) * ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->x) *
4296 ED_IMAGE_UNDO_TILE_NUMBER(projIma->ibuf->y);
4297 projIma->partRedrawRect = static_cast<ImagePaintPartialRedraw *>(
4300 projIma->undoRect = (volatile void **)BLI_memarena_alloc(arena, size);
4301 memset((void *)projIma->undoRect, 0, size);
4302 projIma->maskRect = static_cast<ushort **>(BLI_memarena_alloc(arena, size));
4303 memset(projIma->maskRect, 0, size);
4304 projIma->valid = static_cast<bool **>(BLI_memarena_alloc(arena, size));
4305 memset(projIma->valid, 0, size);
4306 }
4307}
4308
4310 MemArena *arena,
4311 const ProjPaintFaceLookup *face_lookup,
4312 ProjPaintLayerClone *layer_clone,
4313 const float (*mloopuv_base)[2],
4314 const bool is_multi_view)
4315{
4316 /* Image Vars - keep track of images we have used */
4317 ListBase used_images = {nullptr};
4318
4319 Image *tpage_last = nullptr, *tpage;
4320 TexPaintSlot *slot_last = nullptr;
4321 TexPaintSlot *slot = nullptr;
4322 int tile_last = -1, tile;
4323 int image_index = -1, tri_index;
4324 int prev_poly = -1;
4325 const blender::Span<int3> corner_tris = ps->corner_tris_eval;
4326 const blender::Span<int> tri_faces = ps->corner_tri_faces_eval;
4327
4328 BLI_assert(ps->image_tot == 0);
4329
4330 for (tri_index = 0; tri_index < ps->corner_tris_eval.size(); tri_index++) {
4331 bool is_face_paintable;
4332 bool skip_tri = false;
4333
4334 is_face_paintable = project_paint_check_face_paintable(ps, face_lookup, tri_index);
4335
4336 if (!ps->do_stencil_brush) {
4337 slot = project_paint_face_paint_slot(ps, tri_index);
4338 /* all faces should have a valid slot, reassert here */
4339 if (slot == nullptr) {
4340 mloopuv_base = static_cast<const float(*)[2]>(
4342 tpage = ps->canvas_ima;
4343 }
4344 else {
4345 if (slot != slot_last) {
4346 if (!slot->uvname ||
4347 !(mloopuv_base = static_cast<const float(*)[2]>(CustomData_get_layer_named(
4348 &ps->mesh_eval->corner_data, CD_PROP_FLOAT2, slot->uvname))))
4349 {
4350 mloopuv_base = static_cast<const float(*)[2]>(
4352 }
4353 slot_last = slot;
4354 }
4355
4356 /* Don't allow painting on linked images. */
4357 if (slot->ima != nullptr &&
4358 (!ID_IS_EDITABLE(slot->ima) || ID_IS_OVERRIDE_LIBRARY(slot->ima)))
4359 {
4360 skip_tri = true;
4361 tpage = nullptr;
4362 }
4363
4364 /* Don't allow using the same image for painting and stenciling. */
4365 if (slot->ima == ps->stencil_ima) {
4366 /* Delay continuing the loop until after loop_uvs and bleed faces are initialized.
4367 * While this shouldn't be used, face-winding reads all faces.
4368 * It's less trouble to set all faces to valid UVs,
4369 * avoiding nullptr checks all over. */
4370 skip_tri = true;
4371 tpage = nullptr;
4372 }
4373 else {
4374 tpage = slot->ima;
4375 }
4376 }
4377 }
4378 else {
4379 tpage = ps->stencil_ima;
4380 }
4381
4382 ps->poly_to_loop_uv[tri_faces[tri_index]] = mloopuv_base;
4383
4384 tile = project_paint_face_paint_tile(tpage, mloopuv_base[corner_tris[tri_index][0]]);
4385
4386#ifndef PROJ_DEBUG_NOSEAMBLEED
4387 project_paint_bleed_add_face_user(ps, arena, corner_tris[tri_index], tri_index);
4388#endif
4389
4390 if (skip_tri || project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) {
4391 continue;
4392 }
4393
4394 BLI_assert(mloopuv_base != nullptr);
4395
4396 if (is_face_paintable && tpage) {
4397 ProjPaintFaceCoSS coSS;
4398 proj_paint_face_coSS_init(ps, corner_tris[tri_index], &coSS);
4399
4400 if (is_multi_view == false) {
4401 if (project_paint_flt_max_cull(ps, &coSS)) {
4402 continue;
4403 }
4404
4405#ifdef PROJ_DEBUG_WINCLIP
4406 if (project_paint_winclip(ps, &coSS)) {
4407 continue;
4408 }
4409
4410#endif // PROJ_DEBUG_WINCLIP
4411
4412 /* Back-face culls individual triangles but mask normal will use face. */
4413 if (ps->do_backfacecull) {
4414 if (ps->do_mask_normal) {
4415 if (prev_poly != tri_faces[tri_index]) {
4416 bool culled = true;
4417 const blender::IndexRange poly = ps->faces_eval[tri_faces[tri_index]];
4418 prev_poly = tri_faces[tri_index];
4419 for (const int corner : poly) {
4420 if (!(ps->vertFlags[ps->corner_verts_eval[corner]] & PROJ_VERT_CULL)) {
4421 culled = false;
4422 break;
4423 }
4424 }
4425
4426 if (culled) {
4427 /* poly loops - 2 is number of triangles for poly,
4428 * but counter gets incremented when continuing, so decrease by 3 */
4429 int poly_tri = poly.size() - 3;
4430 tri_index += poly_tri;
4431 continue;
4432 }
4433 }
4434 }
4435 else {
4436 if ((line_point_side_v2(coSS.v1, coSS.v2, coSS.v3) < 0.0f) != ps->is_flip_object) {
4437 continue;
4438 }
4439 }
4440 }
4441 }
4442
4443 if (tpage_last != tpage || tile_last != tile) {
4444 image_index = 0;
4445 for (PrepareImageEntry *e = static_cast<PrepareImageEntry *>(used_images.first); e;
4446 e = e->next, image_index++)
4447 {
4448 if (e->ima == tpage && e->iuser.tile == tile) {
4449 break;
4450 }
4451 }
4452
4453 if (image_index == ps->image_tot) {
4454 /* XXX get appropriate ImageUser instead */
4455 ImageUser iuser;
4456 BKE_imageuser_default(&iuser);
4457 iuser.tile = tile;
4458 iuser.framenr = tpage->lastframe;
4459 if (BKE_image_has_ibuf(tpage, &iuser)) {
4460 PrepareImageEntry *e = MEM_callocN<PrepareImageEntry>("PrepareImageEntry");
4461 e->ima = tpage;
4462 e->iuser = iuser;
4463 BLI_addtail(&used_images, e);
4464 ps->image_tot++;
4465 }
4466 else {
4467 image_index = -1;
4468 }
4469 }
4470
4471 tpage_last = tpage;
4472 tile_last = tile;
4473 }
4474
4475 if (image_index != -1) {
4476 /* Initialize the faces screen pixels */
4477 /* Add this to a list to initialize later */
4478 project_paint_delayed_face_init(ps, corner_tris[tri_index], tri_index);
4479 }
4480 }
4481 }
4482
4483 /* Build an array of images we use. */
4484 if (ps->is_shared_user == false) {
4485 project_paint_build_proj_ima(ps, arena, &used_images);
4486 }
4487
4488 /* we have built the array, discard the linked list */
4489 BLI_freelistN(&used_images);
4490}
4491
4492/* run once per stroke before projection painting */
4493static void project_paint_begin(const bContext *C,
4494 ProjPaintState *ps,
4495 const bool is_multi_view,
4496 const char symmetry_flag)
4497{
4498 ProjPaintLayerClone layer_clone;
4499 ProjPaintFaceLookup face_lookup;
4500 const float(*mloopuv_base)[2] = nullptr;
4501
4502 /* At the moment this is just ps->arena_mt[0], but use this to show were not multi-threading. */
4503 MemArena *arena;
4504
4505 const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush);
4506
4507 bool reset_threads = false;
4508
4509 /* ---- end defines ---- */
4510
4511 if (ps->source == PROJ_SRC_VIEW) {
4512 /* faster clipping lookups */
4513 ED_view3d_clipping_local(ps->rv3d, ps->ob->object_to_world().ptr());
4514 }
4515
4516 ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0);
4517 ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0;
4518
4519 /* paint onto the derived mesh */
4520 if (ps->is_shared_user == false) {
4522 return;
4523 }
4524 }
4525
4526 proj_paint_face_lookup_init(ps, &face_lookup);
4527 proj_paint_layer_clone_init(ps, &layer_clone);
4528
4529 if (ps->do_layer_stencil || ps->do_stencil_brush) {
4530 // int layer_num = CustomData_get_stencil_layer(&ps->mesh_eval->ldata, CD_PROP_FLOAT2);
4531 int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->corner_data,
4533 if (layer_num != -1) {
4534 ps->mloopuv_stencil_eval = static_cast<const float(*)[2]>(
4536 }
4537
4538 if (ps->mloopuv_stencil_eval == nullptr) {
4539 /* get active instead */
4540 ps->mloopuv_stencil_eval = static_cast<const float(*)[2]>(
4542 }
4543
4544 if (ps->do_stencil_brush) {
4545 mloopuv_base = ps->mloopuv_stencil_eval;
4546 }
4547 }
4548
4549 /* when using sub-surface or multi-resolution,
4550 * mesh-data arrays are thrown away, we need to keep a copy. */
4551 if (ps->is_shared_user == false) {
4553 }
4554
4555 proj_paint_state_viewport_init(ps, symmetry_flag);
4556
4557 /* calculate vert screen coords
4558 * run this early so we can calculate the x/y resolution of our bucket rect */
4560
4561 /* only for convenience */
4562 ps->screen_width = ps->screenMax[0] - ps->screenMin[0];
4563 ps->screen_height = ps->screenMax[1] - ps->screenMin[1];
4564
4565 ps->buckets_x = int(ps->screen_width / (float(diameter) / PROJ_BUCKET_BRUSH_DIV));
4566 ps->buckets_y = int(ps->screen_height / (float(diameter) / PROJ_BUCKET_BRUSH_DIV));
4567
4568 // printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y);
4569
4571 reset_threads = true;
4572 }
4573
4574 /* Really high values could cause problems since it has to allocate a few
4575 * `(ps->buckets_x * ps->buckets_y)` sized arrays. */
4578
4580 "paint-bucketRect");
4582 "paint-bucketFaces");
4583
4584 ps->bucketFlags = MEM_calloc_arrayN<uchar>(ps->buckets_x * ps->buckets_y, "paint-bucketFaces");
4585#ifndef PROJ_DEBUG_NOSEAMBLEED
4586 if (ps->is_shared_user == false) {
4588 }
4589#endif
4590
4591 proj_paint_state_thread_init(ps, reset_threads);
4592 arena = ps->arena_mt[0];
4593
4595
4597 ps, arena, &face_lookup, &layer_clone, mloopuv_base, is_multi_view);
4598}
4599
4600static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2])
4601{
4602 /* setup clone offset */
4604 float projCo[4];
4605 copy_v3_v3(projCo, ps->scene->cursor.location);
4606 mul_m4_v3(ps->obmat_imat, projCo);
4607
4608 projCo[3] = 1.0f;
4609 mul_m4_v4(ps->projectMat, projCo);
4610 ps->cloneOffset[0] = mouse[0] -
4611 (float(ps->winx * 0.5f) + (ps->winx * 0.5f) * projCo[0] / projCo[3]);
4612 ps->cloneOffset[1] = mouse[1] -
4613 (float(ps->winy * 0.5f) + (ps->winy * 0.5f) * projCo[1] / projCo[3]);
4614 }
4615}
4616
4618{
4619 int a;
4620
4621 /* dereference used image buffers */
4622 if (ps->is_shared_user == false) {
4623 ProjPaintImage *projIma;
4624 for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) {
4625 BKE_image_release_ibuf(projIma->ima, projIma->ibuf, nullptr);
4626 DEG_id_tag_update(&projIma->ima->id, 0);
4627 }
4628 }
4629
4630 if (ps->reproject_ibuf_free_float) {
4632 }
4633 if (ps->reproject_ibuf_free_uchar) {
4635 }
4637
4639 MEM_freeN(ps->bucketRect);
4642
4643 if (ps->is_shared_user == false) {
4644 if (ps->mat_array != nullptr) {
4645 MEM_freeN(ps->mat_array);
4646 }
4647
4648 /* must be set for non-shared */
4650 if (ps->poly_to_loop_uv) {
4652 }
4653
4654 if (ps->do_layer_clone) {
4656 }
4657 if (ps->thread_tot > 1) {
4659 /* The void cast is needed when building without TBB. */
4660 MEM_freeN((void *)ps->tile_lock);
4661 }
4662
4664
4665#ifndef PROJ_DEBUG_NOSEAMBLEED
4666 if (ps->seam_bleed_px > 0.0f) {
4667 MEM_freeN(ps->vertFaces);
4671 MEM_freeN(ps->vertSeams);
4672 }
4673#endif
4674
4675 if (ps->do_mask_cavity) {
4676 MEM_freeN(ps->cavities);
4677 }
4678
4679 ps->mesh_eval = nullptr;
4680 }
4681
4682 if (ps->blurkernel) {
4684 MEM_delete(ps->blurkernel);
4685 }
4686
4687 if (ps->vertFlags) {
4688 MEM_freeN(ps->vertFlags);
4689 }
4690
4691 for (a = 0; a < ps->thread_tot; a++) {
4693 }
4694}
4695
4696/* 1 = an undo, -1 is a redo. */
4701
4703{
4704 int tot = PROJ_BOUNDBOX_SQUARED;
4705 while (tot--) {
4707 pr++;
4708 }
4709}
4710
4712 ImagePaintPartialRedraw *pr_other,
4713 int tot)
4714{
4715 bool touch = false;
4716 while (tot--) {
4718 if (!BLI_rcti_is_empty(&pr->dirty_region)) {
4719 touch = true;
4720 }
4721
4722 pr++;
4723 pr_other++;
4724 }
4725
4726 return touch;
4727}
4728
4729/* Loop over all images on this mesh and update any we have touched */
4731{
4733 ProjPaintImage *projIma;
4734 int a, i;
4735 bool redraw = false;
4736
4737 for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) {
4738 if (projIma->touch) {
4739 /* look over each bound cell */
4740 for (i = 0; i < PROJ_BOUNDBOX_SQUARED; i++) {
4741 pr = &(projIma->partRedrawRect[i]);
4742 if (BLI_rcti_is_valid(&pr->dirty_region)) {
4744 imapaint_image_update(nullptr, projIma->ima, projIma->ibuf, &projIma->iuser, true);
4745 redraw = true;
4746 }
4747
4749 }
4750
4751 /* clear for reuse */
4752 projIma->touch = false;
4753 }
4754 }
4755
4756 return redraw;
4757}
4758
4759/* run this per painting onto each mouse location */
4760static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2])
4761{
4762 if (ps->source == PROJ_SRC_VIEW) {
4763 float min_brush[2], max_brush[2];
4764 const float radius = ps->brush_size;
4765
4766 /* so we don't have a bucket bounds that is way too small to paint into */
4767#if 0
4768 /* This doesn't work yet. */
4769 if (radius < 1.0f) {
4770 radius = 1.0f;
4771 }
4772#endif
4773
4774 min_brush[0] = mval_f[0] - radius;
4775 min_brush[1] = mval_f[1] - radius;
4776
4777 max_brush[0] = mval_f[0] + radius;
4778 max_brush[1] = mval_f[1] + radius;
4779
4780 /* offset to make this a valid bucket index */
4781 project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax);
4782
4783 /* mouse outside the model areas? */
4784 if (ps->bucketMin[0] == ps->bucketMax[0] || ps->bucketMin[1] == ps->bucketMax[1]) {
4785 return false;
4786 }
4787 }
4788 else { /* reproject: PROJ_SRC_* */
4789 ps->bucketMin[0] = 0;
4790 ps->bucketMin[1] = 0;
4791
4792 ps->bucketMax[0] = ps->buckets_x;
4793 ps->bucketMax[1] = ps->buckets_y;
4794 }
4795
4796 ps->context_bucket_index = ps->bucketMin[0] + ps->bucketMin[1] * ps->buckets_x;
4797 return true;
4798}
4799
4801 int *bucket_index,
4802 rctf *bucket_bounds,
4803 const float mval[2])
4804{
4805 const int diameter = 2 * ps->brush_size;
4806
4807 const int max_bucket_idx = ps->bucketMax[0] + (ps->bucketMax[1] - 1) * ps->buckets_x;
4808
4809 for (int bidx = atomic_fetch_and_add_int32(&ps->context_bucket_index, 1); bidx < max_bucket_idx;
4811 {
4812 const int bucket_y = bidx / ps->buckets_x;
4813 const int bucket_x = bidx - (bucket_y * ps->buckets_x);
4814
4815 BLI_assert(bucket_y >= ps->bucketMin[1] && bucket_y < ps->bucketMax[1]);
4816 if (bucket_x >= ps->bucketMin[0] && bucket_x < ps->bucketMax[0]) {
4817 /* Use bucket_bounds for #project_bucket_isect_circle and #project_bucket_init. */
4818 project_bucket_bounds(ps, bucket_x, bucket_y, bucket_bounds);
4819
4820 if ((ps->source != PROJ_SRC_VIEW) ||
4821 project_bucket_isect_circle(mval, float(diameter * diameter), bucket_bounds))
4822 {
4823 *bucket_index = bidx;
4824
4825 return true;
4826 }
4827 }
4828 }
4829
4830 return false;
4831}
4832
4833/* Each thread gets one of these, also used as an argument to pass to project_paint_op */
4835 /* args */
4837 float prevmval[2];
4838 float mval[2];
4839
4840 /* Annoying but we need to have image bounds per thread,
4841 * then merge into ps->projectPartialRedraws. */
4842
4843 /* array of partial redraws */
4845
4846 /* thread settings */
4848
4850};
4851
4852static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float mask)
4853{
4854 const uchar *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch;
4855
4856 if (clone_pt[3]) {
4857 uchar clone_rgba[4];
4858
4859 clone_rgba[0] = clone_pt[0];
4860 clone_rgba[1] = clone_pt[1];
4861 clone_rgba[2] = clone_pt[2];
4862 clone_rgba[3] = uchar(clone_pt[3] * mask);
4863
4864 if (ps->do_masking) {
4865 IMB_blend_color_byte(projPixel->pixel.ch_pt,
4866 projPixel->origColor.ch_pt,
4867 clone_rgba,
4868 IMB_BlendMode(ps->blend));
4869 }
4870 else {
4872 projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, IMB_BlendMode(ps->blend));
4873 }
4874 }
4875}
4876
4877static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float mask)
4878{
4879 const float *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.f;
4880
4881 if (clone_pt[3]) {
4882 float clone_rgba[4];
4883
4884 mul_v4_v4fl(clone_rgba, clone_pt, mask);
4885
4886 if (ps->do_masking) {
4888 projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, IMB_BlendMode(ps->blend));
4889 }
4890 else {
4892 projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, IMB_BlendMode(ps->blend));
4893 }
4894 }
4895}
4896
4903 ProjPixel *projPixel,
4904 float mask,
4905 MemArena *smearArena,
4906 LinkNode **smearPixels,
4907 const float co[2])
4908{
4909 uchar rgba_ub[4];
4910
4911 if (project_paint_PickColor(ps, co, nullptr, rgba_ub, true) == 0) {
4912 return;
4913 }
4914
4916 ((ProjPixelClone *)projPixel)->clonepx.ch, projPixel->pixel.ch_pt, rgba_ub, mask);
4917 BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena);
4918}
4919
4921 ProjPixel *projPixel,
4922 float mask,
4923 MemArena *smearArena,
4924 LinkNode **smearPixels_f,
4925 const float co[2])
4926{
4927 float rgba[4];
4928
4929 if (project_paint_PickColor(ps, co, rgba, nullptr, true) == 0) {
4930 return;
4931 }
4932
4934 ((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, mask);
4935 BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena);
4936}
4937
4939 ProjPixel *projPixel,
4940 float mask,
4941 MemArena *softenArena,
4942 LinkNode **softenPixels)
4943{
4944 float accum_tot = 0.0f;
4945 int xk, yk;
4946 BlurKernel *kernel = ps->blurkernel;
4947 float *rgba = projPixel->newColor.f;
4948
4949 /* rather than painting, accumulate surrounding colors */
4950 zero_v4(rgba);
4951
4952 for (yk = 0; yk < kernel->side; yk++) {
4953 for (xk = 0; xk < kernel->side; xk++) {
4954 float rgba_tmp[4];
4955 float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f};
4956
4957 add_v2_v2(co_ofs, projPixel->projCoSS);
4958
4959 if (project_paint_PickColor(ps, co_ofs, rgba_tmp, nullptr, true)) {
4960 float weight = kernel->wdata[xk + yk * kernel->side];
4961 mul_v4_fl(rgba_tmp, weight);
4962 add_v4_v4(rgba, rgba_tmp);
4963 accum_tot += weight;
4964 }
4965 }
4966 }
4967
4968 if (LIKELY(accum_tot != 0)) {
4969 mul_v4_fl(rgba, 1.0f / accum_tot);
4970
4971 if (ps->mode == BRUSH_STROKE_INVERT) {
4972 /* subtract blurred image from normal image gives high pass filter */
4973 sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba);
4974
4975 /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid
4976 * colored speckles appearing in final image, and also to check for threshold */
4977 rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba);
4978 if (fabsf(rgba[0]) > ps->brush->sharp_threshold) {
4979 float alpha = projPixel->pixel.f_pt[3];
4980 projPixel->pixel.f_pt[3] = rgba[3] = mask;
4981
4982 /* add to enhance edges */
4983 blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba);
4984 rgba[3] = alpha;
4985 }
4986 else {
4987 return;
4988 }
4989 }
4990 else {
4991 blend_color_interpolate_float(rgba, projPixel->pixel.f_pt, rgba, mask);
4992 }
4993
4994 BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena);
4995 }
4996}
4997
4999 ProjPixel *projPixel,
5000 float mask,
5001 MemArena *softenArena,
5002 LinkNode **softenPixels)
5003{
5004 float accum_tot = 0;
5005 int xk, yk;
5006 BlurKernel *kernel = ps->blurkernel;
5007 /* convert to byte after */
5008 float rgba[4];
5009
5010 /* rather than painting, accumulate surrounding colors */
5011 zero_v4(rgba);
5012
5013 for (yk = 0; yk < kernel->side; yk++) {
5014 for (xk = 0; xk < kernel->side; xk++) {
5015 float rgba_tmp[4];
5016 float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f};
5017
5018 add_v2_v2(co_ofs, projPixel->projCoSS);
5019
5020 if (project_paint_PickColor(ps, co_ofs, rgba_tmp, nullptr, true)) {
5021 float weight = kernel->wdata[xk + yk * kernel->side];
5022 mul_v4_fl(rgba_tmp, weight);
5023 add_v4_v4(rgba, rgba_tmp);
5024 accum_tot += weight;
5025 }
5026 }
5027 }
5028
5029 if (LIKELY(accum_tot != 0)) {
5030 uchar *rgba_ub = projPixel->newColor.ch;
5031
5032 mul_v4_fl(rgba, 1.0f / accum_tot);
5033
5034 if (ps->mode == BRUSH_STROKE_INVERT) {
5035 float rgba_pixel[4];
5036
5037 straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt);
5038
5039 /* subtract blurred image from normal image gives high pass filter */
5040 sub_v3_v3v3(rgba, rgba_pixel, rgba);
5041 /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid
5042 * colored speckles appearing in final image, and also to check for threshold */
5043 rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba);
5044 if (fabsf(rgba[0]) > ps->brush->sharp_threshold) {
5045 float alpha = rgba_pixel[3];
5046 rgba[3] = rgba_pixel[3] = mask;
5047
5048 /* add to enhance edges */
5049 blend_color_add_float(rgba, rgba_pixel, rgba);
5050
5051 rgba[3] = alpha;
5052 premul_float_to_straight_uchar(rgba_ub, rgba);
5053 }
5054 else {
5055 return;
5056 }
5057 }
5058 else {
5059 premul_float_to_straight_uchar(rgba_ub, rgba);
5060 blend_color_interpolate_byte(rgba_ub, projPixel->pixel.ch_pt, rgba_ub, mask);
5061 }
5062 BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena);
5063 }
5064}
5065
5067 ProjPixel *projPixel,
5068 const float texrgb[3],
5069 float mask,
5070 float dither,
5071 int u,
5072 int v)
5073{
5074 float rgb[3];
5075 uchar rgba_ub[4];
5076
5077 if (ps->is_texbrush) {
5078 mul_v3_v3v3(rgb, texrgb, ps->paint_color_linear);
5079 /* TODO(sergey): Support texture paint color space. */
5080 linearrgb_to_srgb_v3_v3(rgb, rgb);
5081 }
5082 else {
5083 copy_v3_v3(rgb, ps->paint_color);
5084 }
5085
5086 if (dither > 0.0f) {
5087 float_to_byte_dither_v3(rgba_ub, rgb, dither, u, v);
5088 }
5089 else {
5090 unit_float_to_uchar_clamp_v3(rgba_ub, rgb);
5091 }
5092 rgba_ub[3] = f_to_char(mask);
5093
5094 if (ps->do_masking) {
5096 projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, IMB_BlendMode(ps->blend));
5097 }
5098 else {
5100 projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, IMB_BlendMode(ps->blend));
5101 }
5102}
5103
5105 ProjPixel *projPixel,
5106 const float texrgb[3],
5107 float mask)
5108{
5109 float rgba[4];
5110
5111 copy_v3_v3(rgba, ps->paint_color_linear);
5112
5113 if (ps->is_texbrush) {
5114 mul_v3_v3(rgba, texrgb);
5115 }
5116
5117 mul_v3_fl(rgba, mask);
5118 rgba[3] = mask;
5119
5120 if (ps->do_masking) {
5122 projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, IMB_BlendMode(ps->blend));
5123 }
5124 else {
5126 projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, IMB_BlendMode(ps->blend));
5127 }
5128}
5129
5130static void do_projectpaint_mask(ProjPaintState *ps, ProjPixel *projPixel, float mask)
5131{
5132 uchar rgba_ub[4];
5133 rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f;
5134 rgba_ub[3] = f_to_char(mask);
5135
5136 if (ps->do_masking) {
5138 projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, IMB_BlendMode(ps->blend));
5139 }
5140 else {
5142 projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, IMB_BlendMode(ps->blend));
5143 }
5144}
5145
5146static void do_projectpaint_mask_f(ProjPaintState *ps, ProjPixel *projPixel, float mask)
5147{
5148 float rgba[4];
5149 rgba[0] = rgba[1] = rgba[2] = ps->stencil_value;
5150 rgba[3] = mask;
5151
5152 if (ps->do_masking) {
5154 projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, IMB_BlendMode(ps->blend));
5155 }
5156 else {
5158 projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, IMB_BlendMode(ps->blend));
5159 }
5160}
5161
5163 const ProjPixel *projPixel)
5164{
5165 rcti rect_to_add;
5167 &rect_to_add, projPixel->x_px, projPixel->x_px + 1, projPixel->y_px, projPixel->y_px + 1);
5168 BLI_rcti_do_minmax_rcti(&cell->dirty_region, &rect_to_add);
5169}
5170
5171static void copy_original_alpha_channel(ProjPixel *pixel, bool is_floatbuf)
5172{
5173 /* Use the original alpha channel data instead of the modified one */
5174 if (is_floatbuf) {
5175 /* slightly more involved case since floats are in premultiplied space we need
5176 * to make sure alpha is consistent, see #44627 */
5177 float rgb_straight[4];
5178 premul_to_straight_v4_v4(rgb_straight, pixel->pixel.f_pt);
5179 rgb_straight[3] = pixel->origColor.f_pt[3];
5180 straight_to_premul_v4_v4(pixel->pixel.f_pt, rgb_straight);
5181 }
5182 else {
5183 pixel->pixel.ch_pt[3] = pixel->origColor.ch_pt[3];
5184 }
5185}
5186
5187/* Run this for single and multi-threaded painting. */
5188static void do_projectpaint_thread(TaskPool *__restrict /*pool*/, void *ph_v)
5189{
5190 /* First unpack args from the struct */
5191 ProjPaintState *ps = ((ProjectHandle *)ph_v)->ps;
5192 ProjPaintImage *projImages = ((ProjectHandle *)ph_v)->projImages;
5193 const float *lastpos = ((ProjectHandle *)ph_v)->prevmval;
5194 const float *pos = ((ProjectHandle *)ph_v)->mval;
5195 const int thread_index = ((ProjectHandle *)ph_v)->thread_index;
5196 ImagePool *pool = ((ProjectHandle *)ph_v)->pool;
5197 /* Done with args from ProjectHandle */
5198
5199 LinkNode *node;
5200 ProjPixel *projPixel;
5201 Brush *brush = ps->brush;
5202
5203 int last_index = -1;
5204 ProjPaintImage *last_projIma = nullptr;
5205 ImagePaintPartialRedraw *last_partial_redraw_cell;
5206
5207 float dist_sq, dist;
5208
5209 float falloff;
5210 int bucket_index;
5211 bool is_floatbuf = false;
5212 const short brush_type = ps->brush_type;
5213 rctf bucket_bounds;
5214
5215 /* for smear only */
5216 float pos_ofs[2] = {0};
5217 float co[2];
5218 ushort mask_short;
5219 const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush);
5220 const float brush_radius = ps->brush_size;
5221 /* avoid a square root with every dist comparison */
5222 const float brush_radius_sq = brush_radius * brush_radius;
5223
5224 const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ?
5225 false :
5226 (brush->flag & BRUSH_LOCK_ALPHA) != 0;
5227
5228 LinkNode *smearPixels = nullptr;
5229 LinkNode *smearPixels_f = nullptr;
5230 /* mem arena for this brush projection only */
5231 MemArena *smearArena = nullptr;
5232
5233 LinkNode *softenPixels = nullptr;
5234 LinkNode *softenPixels_f = nullptr;
5235 /* mem arena for this brush projection only */
5236 MemArena *softenArena = nullptr;
5237
5238 if (brush_type == IMAGE_PAINT_BRUSH_TYPE_SMEAR) {
5239 pos_ofs[0] = pos[0] - lastpos[0];
5240 pos_ofs[1] = pos[1] - lastpos[1];
5241
5242 smearArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint smear arena");
5243 }
5244 else if (brush_type == IMAGE_PAINT_BRUSH_TYPE_SOFTEN) {
5245 softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena");
5246 }
5247
5248 // printf("brush bounds %d %d %d %d\n",
5249 // bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]);
5250
5251 while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) {
5252
5253 /* Check this bucket and its faces are initialized */
5254 if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) {
5255 rctf clip_rect = bucket_bounds;
5256 clip_rect.xmin -= PROJ_PIXEL_TOLERANCE;
5257 clip_rect.xmax += PROJ_PIXEL_TOLERANCE;
5258 clip_rect.ymin -= PROJ_PIXEL_TOLERANCE;
5259 clip_rect.ymax += PROJ_PIXEL_TOLERANCE;
5260 /* No pixels initialized */
5261 project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds);
5262 }
5263
5264 if (ps->source != PROJ_SRC_VIEW) {
5265
5266 /* Re-Projection, simple, no brushes! */
5267
5268 for (node = ps->bucketRect[bucket_index]; node; node = node->next) {
5269 projPixel = (ProjPixel *)node->link;
5270
5271 /* copy of code below */
5272 if (last_index != projPixel->image_index) {
5273 last_index = projPixel->image_index;
5274 last_projIma = projImages + last_index;
5275
5276 last_projIma->touch = true;
5277 is_floatbuf = (last_projIma->ibuf->float_buffer.data != nullptr);
5278 }
5279 /* end copy */
5280
5281 /* fill brushes */
5282 if (ps->source == PROJ_SRC_VIEW_FILL) {
5283 if (brush->flag & BRUSH_USE_GRADIENT) {
5284 /* these could probably be cached instead of being done per pixel */
5285 float tangent[2];
5286 float line_len_sq_inv, line_len;
5287 float f;
5288 float color_f[4];
5289 const float p[2] = {
5290 projPixel->projCoSS[0] - lastpos[0],
5291 projPixel->projCoSS[1] - lastpos[1],
5292 };
5293
5294 sub_v2_v2v2(tangent, pos, lastpos);
5295 line_len = len_squared_v2(tangent);
5296 line_len_sq_inv = 1.0f / line_len;
5297 line_len = sqrtf(line_len);
5298
5299 switch (brush->gradient_fill_mode) {
5300 case BRUSH_GRADIENT_LINEAR: {
5301 f = dot_v2v2(p, tangent) * line_len_sq_inv;
5302 break;
5303 }
5305 default: {
5306 f = len_v2(p) / line_len;
5307 break;
5308 }
5309 }
5310 BKE_colorband_evaluate(brush->gradient, f, color_f);
5311 color_f[3] *= float(projPixel->mask) * (1.0f / 65535.0f) * brush_alpha;
5312
5313 if (is_floatbuf) {
5314 /* Convert to premutliplied. */
5315 mul_v3_fl(color_f, color_f[3]);
5316 IMB_blend_color_float(projPixel->pixel.f_pt,
5317 projPixel->origColor.f_pt,
5318 color_f,
5319 IMB_BlendMode(ps->blend));
5320 }
5321 else {
5322 linearrgb_to_srgb_v3_v3(color_f, color_f);
5323
5324 if (ps->dither > 0.0f) {
5326 projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px);
5327 }
5328 else {
5329 unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f);
5330 }
5331 projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]);
5332 IMB_blend_color_byte(projPixel->pixel.ch_pt,
5333 projPixel->origColor.ch_pt,
5334 projPixel->newColor.ch,
5335 IMB_BlendMode(ps->blend));
5336 }
5337 }
5338 else {
5339 if (is_floatbuf) {
5340 float newColor_f[4];
5341 newColor_f[3] = float(projPixel->mask) * (1.0f / 65535.0f) * brush_alpha;
5342 copy_v3_v3(newColor_f, ps->paint_color_linear);
5343
5344 IMB_blend_color_float(projPixel->pixel.f_pt,
5345 projPixel->origColor.f_pt,
5346 newColor_f,
5347 IMB_BlendMode(ps->blend));
5348 }
5349 else {
5350 float mask = float(projPixel->mask) * (1.0f / 65535.0f);
5351 projPixel->newColor.ch[3] = mask * 255 * brush_alpha;
5352
5353 rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color);
5354 IMB_blend_color_byte(projPixel->pixel.ch_pt,
5355 projPixel->origColor.ch_pt,
5356 projPixel->newColor.ch,
5357 IMB_BlendMode(ps->blend));
5358 }
5359 }
5360
5361 if (lock_alpha) {
5362 copy_original_alpha_channel(projPixel, is_floatbuf);
5363 }
5364
5365 last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index;
5366 image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel);
5367 }
5368 else {
5369 if (is_floatbuf) {
5370 BLI_assert(ps->reproject_ibuf->float_buffer.data != nullptr);
5371
5373 projPixel->newColor.f,
5374 projPixel->projCoSS[0],
5375 projPixel->projCoSS[1]);
5376 if (projPixel->newColor.f[3]) {
5377 float mask = float(projPixel->mask) * (1.0f / 65535.0f);
5378
5379 mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask);
5380
5382 projPixel->pixel.f_pt, projPixel->origColor.f_pt, projPixel->newColor.f);
5383 }
5384 }
5385 else {
5386 BLI_assert(ps->reproject_ibuf->byte_buffer.data != nullptr);
5388 projPixel->newColor.ch,
5389 projPixel->projCoSS[0],
5390 projPixel->projCoSS[1]);
5391 if (projPixel->newColor.ch[3]) {
5392 float mask = float(projPixel->mask) * (1.0f / 65535.0f);
5393 projPixel->newColor.ch[3] *= mask;
5394
5396 projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, projPixel->newColor.ch);
5397 }
5398 }
5399 }
5400 }
5401 }
5402 else {
5403 /* Normal brush painting */
5404
5405 for (node = ps->bucketRect[bucket_index]; node; node = node->next) {
5406
5407 projPixel = (ProjPixel *)node->link;
5408
5409 dist_sq = len_squared_v2v2(projPixel->projCoSS, pos);
5410
5411 /* Faster alternative to `dist < radius` without a #sqrtf. */
5412 if (dist_sq <= brush_radius_sq) {
5413 dist = sqrtf(dist_sq);
5414
5415 falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius);
5416
5417 if (falloff > 0.0f) {
5418 float texrgb[3];
5419 float mask;
5420
5421 /* Extra mask for normal, layer stencil, etc. */
5422 float custom_mask = float(projPixel->mask) * (1.0f / 65535.0f);
5423
5424 /* Mask texture. */
5425 if (ps->is_maskbrush) {
5426 float texmask = BKE_brush_sample_masktex(
5427 ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool);
5428 CLAMP(texmask, 0.0f, 1.0f);
5429 custom_mask *= texmask;
5430 }
5431
5432 /* Color texture (alpha used as mask). */
5433 if (ps->is_texbrush) {
5435 float samplecos[3];
5436 float texrgba[4];
5437
5438 /* taking 3d copy to account for 3D mapping too.
5439 * It gets concatenated during sampling */
5440 if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
5441 copy_v3_v3(samplecos, projPixel->worldCoSS);
5442 }
5443 else {
5444 copy_v2_v2(samplecos, projPixel->projCoSS);
5445 samplecos[2] = 0.0f;
5446 }
5447
5448 /* NOTE: for clone and smear,
5449 * we only use the alpha, could be a special function */
5451 ps->scene, brush, mtex, samplecos, texrgba, thread_index, pool);
5452
5453 copy_v3_v3(texrgb, texrgba);
5454 custom_mask *= texrgba[3];
5455 }
5456 else {
5457 zero_v3(texrgb);
5458 }
5459
5460 if (ps->do_masking) {
5461 /* masking to keep brush contribution to a pixel limited. note we do not do
5462 * a simple max(mask, mask_accum), as this is very sensitive to spacing and
5463 * gives poor results for strokes crossing themselves.
5464 *
5465 * Instead we use a formula that adds up but approaches brush_alpha slowly
5466 * and never exceeds it, which gives nice smooth results. */
5467 float mask_accum = *projPixel->mask_accum;
5468 float max_mask = brush_alpha * custom_mask * falloff * 65535.0f;
5469
5470 if (brush->flag & BRUSH_ACCUMULATE) {
5471 mask = mask_accum + max_mask;
5472 }
5473 else {
5474 mask = mask_accum + (max_mask - mask_accum * falloff);
5475 }
5476
5477 mask = min_ff(mask, 65535.0f);
5478 mask_short = ushort(mask);
5479
5480 if (mask_short > *projPixel->mask_accum) {
5481 *projPixel->mask_accum = mask_short;
5482 mask = mask_short * (1.0f / 65535.0f);
5483 }
5484 else {
5485 /* Go onto the next pixel */
5486 continue;
5487 }
5488 }
5489 else {
5490 mask = brush_alpha * custom_mask * falloff;
5491 }
5492
5493 if (mask > 0.0f) {
5494
5495 /* copy of code above */
5496 if (last_index != projPixel->image_index) {
5497 last_index = projPixel->image_index;
5498 last_projIma = projImages + last_index;
5499
5500 last_projIma->touch = true;
5501 is_floatbuf = (last_projIma->ibuf->float_buffer.data != nullptr);
5502 }
5503 /* end copy */
5504
5505 /* Validate undo tile, since we will modify it. */
5506 *projPixel->valid = true;
5507
5508 last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index;
5509 image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel);
5510
5511 /* texrgb is not used for clone, smear or soften */
5512 switch (brush_type) {
5514 if (is_floatbuf) {
5515 do_projectpaint_clone_f(ps, projPixel, mask);
5516 }
5517 else {
5518 do_projectpaint_clone(ps, projPixel, mask);
5519 }
5520 break;
5522 sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs);
5523
5524 if (is_floatbuf) {
5525 do_projectpaint_smear_f(ps, projPixel, mask, smearArena, &smearPixels_f, co);
5526 }
5527 else {
5528 do_projectpaint_smear(ps, projPixel, mask, smearArena, &smearPixels, co);
5529 }
5530 break;
5532 if (is_floatbuf) {
5533 do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f);
5534 }
5535 else {
5536 do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels);
5537 }
5538 break;
5540 if (is_floatbuf) {
5541 do_projectpaint_mask_f(ps, projPixel, mask);
5542 }
5543 else {
5544 do_projectpaint_mask(ps, projPixel, mask);
5545 }
5546 break;
5547 default:
5548 if (is_floatbuf) {
5549 do_projectpaint_draw_f(ps, projPixel, texrgb, mask);
5550 }
5551 else {
5553 ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px);
5554 }
5555 break;
5556 }
5557
5558 if (lock_alpha) {
5559 copy_original_alpha_channel(projPixel, is_floatbuf);
5560 }
5561 }
5562
5563 /* done painting */
5564 }
5565 }
5566 }
5567 }
5568 }
5569
5570 if (brush_type == IMAGE_PAINT_BRUSH_TYPE_SMEAR) {
5571
5572 for (node = smearPixels; node; node = node->next) { /* this won't run for a float image */
5573 projPixel = static_cast<ProjPixel *>(node->link);
5574 *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint_;
5575 if (lock_alpha) {
5576 copy_original_alpha_channel(projPixel, false);
5577 }
5578 }
5579
5580 for (node = smearPixels_f; node; node = node->next) {
5581 projPixel = static_cast<ProjPixel *>(node->link);
5582 copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f);
5583 if (lock_alpha) {
5584 copy_original_alpha_channel(projPixel, true);
5585 }
5586 }
5587
5588 BLI_memarena_free(smearArena);
5589 }
5590 else if (brush_type == IMAGE_PAINT_BRUSH_TYPE_SOFTEN) {
5591
5592 for (node = softenPixels; node; node = node->next) { /* this won't run for a float image */
5593 projPixel = static_cast<ProjPixel *>(node->link);
5594 *projPixel->pixel.uint_pt = projPixel->newColor.uint_;
5595 if (lock_alpha) {
5596 copy_original_alpha_channel(projPixel, false);
5597 }
5598 }
5599
5600 for (node = softenPixels_f; node; node = node->next) {
5601 projPixel = static_cast<ProjPixel *>(node->link);
5602 copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f);
5603 if (lock_alpha) {
5604 copy_original_alpha_channel(projPixel, true);
5605 }
5606 }
5607
5608 BLI_memarena_free(softenArena);
5609 }
5610}
5611
5612static bool project_paint_op(void *state, const float lastpos[2], const float pos[2])
5613{
5614 /* First unpack args from the struct */
5616 bool touch_any = false;
5617
5619 TaskPool *task_pool = nullptr;
5620 int a, i;
5621
5622 ImagePool *image_pool;
5623
5624 if (!project_bucket_iter_init(ps, pos)) {
5625 return touch_any;
5626 }
5627
5628 if (ps->thread_tot > 1) {
5630 }
5631
5632 image_pool = BKE_image_pool_new();
5633
5635 /* This means we are reprojecting an image, make sure the image has the needed data available.
5636 */
5637 bool float_dest = false;
5638 bool uchar_dest = false;
5639 /* Check if the destination images are float or uchar. */
5640 for (i = 0; i < ps->image_tot; i++) {
5641 if (ps->projImages[i].ibuf->byte_buffer.data != nullptr) {
5642 uchar_dest = true;
5643 }
5644 if (ps->projImages[i].ibuf->float_buffer.data != nullptr) {
5645 float_dest = true;
5646 }
5647 }
5648
5649 /* Generate missing data if needed. */
5650 if (float_dest && ps->reproject_ibuf->float_buffer.data == nullptr) {
5652 ps->reproject_ibuf_free_float = true;
5653 }
5654 if (uchar_dest && ps->reproject_ibuf->byte_buffer.data == nullptr) {
5656 ps->reproject_ibuf_free_uchar = true;
5657 }
5658 }
5659
5660 /* get the threads running */
5661 for (a = 0; a < ps->thread_tot; a++) {
5662
5663 /* set defaults in handles */
5664 // memset(&handles[a], 0, sizeof(BakeShade));
5665
5666 handles[a].ps = ps;
5667 copy_v2_v2(handles[a].mval, pos);
5668 copy_v2_v2(handles[a].prevmval, lastpos);
5669
5670 /* thread specific */
5671 handles[a].thread_index = a;
5672
5673 handles[a].projImages = static_cast<ProjPaintImage *>(
5674 BLI_memarena_alloc(ps->arena_mt[a], ps->image_tot * sizeof(ProjPaintImage)));
5675
5676 memcpy(handles[a].projImages, ps->projImages, ps->image_tot * sizeof(ProjPaintImage));
5677
5678 /* image bounds */
5679 for (i = 0; i < ps->image_tot; i++) {
5680 handles[a].projImages[i].partRedrawRect = static_cast<ImagePaintPartialRedraw *>(
5683 memcpy(handles[a].projImages[i].partRedrawRect,
5686 }
5687
5688 handles[a].pool = image_pool;
5689
5690 if (task_pool != nullptr) {
5692 }
5693 }
5694
5695 if (task_pool != nullptr) { /* wait for everything to be done */
5698 }
5699 else {
5700 do_projectpaint_thread(nullptr, &handles[0]);
5701 }
5702
5703 BKE_image_pool_free(image_pool);
5704
5705 /* move threaded bounds back into ps->projectPartialRedraws */
5706 for (i = 0; i < ps->image_tot; i++) {
5707 int touch = false;
5708 for (a = 0; a < ps->thread_tot; a++) {
5710 handles[a].projImages[i].partRedrawRect,
5712 }
5713
5714 if (touch) {
5715 ps->projImages[i].touch = true;
5716 touch_any = true;
5717 }
5718 }
5719
5720 /* Calculate pivot for rotation around selection if needed. */
5721 if (U.uiflag & USER_ORBIT_SELECTION) {
5722 float w[3];
5723 int tri_index;
5724
5725 tri_index = project_paint_PickFace(ps, pos, w);
5726
5727 if (tri_index != -1) {
5728 const int3 &tri = ps->corner_tris_eval[tri_index];
5729 const int vert_tri[3] = {PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)};
5730 float world[3];
5732
5733 interp_v3_v3v3v3(world,
5734 ps->vert_positions_eval[vert_tri[0]],
5735 ps->vert_positions_eval[vert_tri[1]],
5736 ps->vert_positions_eval[vert_tri[2]],
5737 w);
5738
5740 mul_m4_v3(ps->obmat, world);
5741 add_v3_v3(ups->average_stroke_accum, world);
5742 ups->last_stroke_valid = true;
5743 }
5744 }
5745
5746 return touch_any;
5747}
5748
5749static void paint_proj_stroke_ps(const bContext * /*C*/,
5750 void *ps_handle_p,
5751 const float prev_pos[2],
5752 const float pos[2],
5753 const bool eraser,
5754 float pressure,
5755 float distance,
5756 float size,
5757 /* extra view */
5758 ProjPaintState *ps)
5759{
5760 ProjStrokeHandle *ps_handle = static_cast<ProjStrokeHandle *>(ps_handle_p);
5761 const Paint *paint = ps->paint;
5762 Brush *brush = ps->brush;
5763 Scene *scene = ps->scene;
5764
5765 ps->brush_size = size;
5766 ps->blend = brush->blend;
5767 if (eraser) {
5769 }
5770
5771 /* handle gradient and inverted stroke color here */
5774 paint,
5775 brush,
5776 ps_handle->initial_hsv_jitter,
5777 false,
5779 distance,
5780 pressure,
5781 nullptr,
5782 ps->paint_color);
5784 }
5785 else if (ps->brush_type == IMAGE_PAINT_BRUSH_TYPE_MASK) {
5786 ps->stencil_value = brush->weight;
5787
5788 if ((ps->mode == BRUSH_STROKE_INVERT) ^
5790 {
5791 ps->stencil_value = 1.0f - ps->stencil_value;
5792 }
5793 }
5794
5795 if (project_paint_op(ps, prev_pos, pos)) {
5796 ps_handle->need_redraw = true;
5798 }
5799}
5800
5802 void *ps_handle_p,
5803 const float prev_pos[2],
5804 const float pos[2],
5805 const bool eraser,
5806 float pressure,
5807 float distance,
5808 float size)
5809{
5810 int i;
5811 ProjStrokeHandle *ps_handle = static_cast<ProjStrokeHandle *>(ps_handle_p);
5812
5813 /* clone gets special treatment here to avoid going through image initialization */
5814 if (ps_handle->is_clone_cursor_pick) {
5815 Scene *scene = ps_handle->scene;
5817 View3D *v3d = CTX_wm_view3d(C);
5818 ARegion *region = CTX_wm_region(C);
5819 float *cursor = scene->cursor.location;
5820 const int mval_i[2] = {int(pos[0]), int(pos[1])};
5821
5823
5824 /* Ensure the depth buffer is updated for #ED_view3d_autodist. */
5826 depsgraph, region, v3d, nullptr, V3D_DEPTH_NO_GPENCIL, false, nullptr);
5827
5828 if (!ED_view3d_autodist(region, v3d, mval_i, cursor, nullptr)) {
5829 return;
5830 }
5831
5833 ED_region_tag_redraw(region);
5834
5835 return;
5836 }
5837
5838 for (i = 0; i < ps_handle->ps_views_tot; i++) {
5839 ProjPaintState *ps = ps_handle->ps_views[i];
5840 paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps);
5841 }
5842}
5843
5844/* initialize project paint settings from context */
5845static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int mode)
5846{
5847 Scene *scene = CTX_data_scene(C);
5848 ToolSettings *settings = scene->toolsettings;
5849
5850 /* brush */
5851 ps->mode = BrushStrokeMode(mode);
5853 ps->brush = BKE_paint_brush(&settings->imapaint.paint);
5854 if (ps->brush) {
5855 Brush *brush = ps->brush;
5856 ps->brush_type = brush->image_brush_type;
5857 ps->blend = brush->blend;
5858 if (mode == BRUSH_STROKE_SMOOTH) {
5860 }
5861 /* only check for inversion for the soften brush, elsewhere,
5862 * a resident brush inversion flag can cause issues */
5864 ps->mode = (((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0)) ?
5867
5868 ps->blurkernel = paint_new_blur_kernel(brush, true);
5869 }
5870
5871 /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */
5872 ps->do_masking = paint_use_opacity_masking(scene, ps->paint, brush);
5873 ps->is_texbrush = (brush->mtex.tex && ps->brush_type == IMAGE_PAINT_BRUSH_TYPE_DRAW) ? true :
5874 false;
5875 ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false;
5876 }
5877 else {
5878 /* Brush may be nullptr. */
5879 ps->do_masking = false;
5880 ps->is_texbrush = false;
5881 ps->is_maskbrush = false;
5882 }
5883
5884 /* sizeof(ProjPixel), since we alloc this a _lot_ */
5886 BLI_assert(ps->pixel_sizeof >= sizeof(ProjPixel));
5887
5888 /* these can be nullptr */
5889 ps->v3d = CTX_wm_view3d(C);
5891 ps->region = CTX_wm_region(C);
5892
5894 ps->scene = scene;
5895 /* allow override of active object */
5896 ps->ob = ob;
5897
5899 ps->stencil_ima = settings->imapaint.stencil;
5900 ps->canvas_ima = (!ps->do_material_slots) ? settings->imapaint.canvas : nullptr;
5901 ps->clone_ima = (!ps->do_material_slots) ? settings->imapaint.clone : nullptr;
5902
5904 ps->cavity_curve = settings->imapaint.paint.cavity_curve;
5905
5906 /* setup projection painting data */
5909 ps->do_occlude = !(settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY);
5911 }
5912 else {
5913 ps->do_backfacecull = ps->do_occlude = ps->do_mask_normal = false;
5914 }
5915
5918 }
5919
5921 /* deactivate stenciling for the stencil brush :) */
5923 !(ps->do_stencil_brush) && ps->stencil_ima);
5925 0);
5926
5927#ifndef PROJ_DEBUG_NOSEAMBLEED
5928 /* pixel num to bleed */
5929 ps->seam_bleed_px = settings->imapaint.seam_bleed;
5931#endif
5932
5933 if (ps->do_mask_normal) {
5935 ps->normal_angle = (ps->normal_angle_inner + 90.0f) * 0.5f;
5936 }
5937 else {
5939 }
5940
5941 ps->normal_angle_inner *= float(M_PI_2 / 90);
5942 ps->normal_angle *= float(M_PI_2 / 90);
5944
5945 if (ps->normal_angle_range <= 0.0f) {
5946 /* no need to do blending */
5947 ps->do_mask_normal = false;
5948 }
5949
5952
5953 ps->dither = settings->imapaint.dither;
5954}
5955
5956void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode)
5957{
5958 ProjStrokeHandle *ps_handle;
5959 Scene *scene = CTX_data_scene(C);
5960 ToolSettings *settings = scene->toolsettings;
5961 char symmetry_flag_views[BOUNDED_ARRAY_TYPE_SIZE<decltype(ps_handle->ps_views)>()] = {0};
5962
5963 ps_handle = MEM_new<ProjStrokeHandle>("ProjStrokeHandle");
5964 ps_handle->scene = scene;
5965 ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint);
5966
5967 if (BKE_brush_color_jitter_get_settings(scene, &settings->imapaint.paint, ps_handle->brush)) {
5968 ps_handle->initial_hsv_jitter = seed_hsv_jitter();
5969 }
5970
5971 if (mode == BRUSH_STROKE_INVERT) {
5972 /* Bypass regular stroke logic. */
5975 ps_handle->is_clone_cursor_pick = true;
5976 return ps_handle;
5977 }
5978 }
5979
5980 ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush);
5981
5982 Mesh *mesh = BKE_mesh_from_object(ob);
5983 ps_handle->symmetry_flags = mesh->symmetry;
5984 ps_handle->ps_views_tot = 1 + (pow_i(2, count_bits_i(ps_handle->symmetry_flags)) - 1);
5985 bool is_multi_view = (ps_handle->ps_views_tot != 1);
5986
5987 for (int i = 0; i < ps_handle->ps_views_tot; i++) {
5988 ProjPaintState *ps = MEM_new<ProjPaintState>("ProjectionPaintState");
5989 ps_handle->ps_views[i] = ps;
5990 }
5991
5992 if (ps_handle->symmetry_flags) {
5993 int index = 0;
5994
5995 int x = 0;
5996 do {
5997 int y = 0;
5998 do {
5999 int z = 0;
6000 do {
6001 symmetry_flag_views[index++] = ((x ? PAINT_SYMM_X : 0) | (y ? PAINT_SYMM_Y : 0) |
6002 (z ? PAINT_SYMM_Z : 0));
6003 BLI_assert(index <= ps_handle->ps_views_tot);
6004 } while ((z++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Z));
6005 } while ((y++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Y));
6006 } while ((x++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_X));
6007 BLI_assert(index == ps_handle->ps_views_tot);
6008 }
6009
6010 for (int i = 0; i < ps_handle->ps_views_tot; i++) {
6011 ProjPaintState *ps = ps_handle->ps_views[i];
6012
6013 project_state_init(C, ob, ps, mode);
6014
6015 if (ps->ob == nullptr) {
6016 ps_handle->ps_views_tot = i + 1;
6017 goto fail;
6018 }
6019 }
6020
6021 /* Don't allow brush size below 2 */
6022 if (BKE_brush_size_get(scene, ps_handle->brush) < 2) {
6023 BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize);
6024 }
6025
6026 /* allocate and initialize spatial data structures */
6027
6028 for (int i = 0; i < ps_handle->ps_views_tot; i++) {
6029 ProjPaintState *ps = ps_handle->ps_views[i];
6030
6034
6035 /* re-use! */
6036 if (i != 0) {
6037 ps->is_shared_user = true;
6038 PROJ_PAINT_STATE_SHARED_MEMCPY(ps, ps_handle->ps_views[0]);
6039 }
6040
6041 project_paint_begin(C, ps, is_multi_view, symmetry_flag_views[i]);
6042 if (ps->mesh_eval == nullptr) {
6043 goto fail;
6044 }
6045
6046 paint_proj_begin_clone(ps, mouse);
6047 }
6048
6049 paint_brush_init_tex(ps_handle->brush);
6050
6051 return ps_handle;
6052
6053fail:
6054 for (int i = 0; i < ps_handle->ps_views_tot; i++) {
6055 MEM_delete(ps_handle->ps_views[i]);
6056 }
6057 MEM_delete(ps_handle);
6058 return nullptr;
6059}
6060
6061void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final)
6062{
6063 ProjStrokeHandle *ps_handle = static_cast<ProjStrokeHandle *>(ps_handle_p);
6064
6065 if (ps_handle->need_redraw) {
6066 ps_handle->need_redraw = false;
6067 }
6068 else if (!final) {
6069 return;
6070 }
6071
6072 if (final) {
6073 /* compositor listener deals with updating */
6075 }
6076 else {
6078 }
6079}
6080
6081void paint_proj_stroke_done(void *ps_handle_p)
6082{
6083 ProjStrokeHandle *ps_handle = static_cast<ProjStrokeHandle *>(ps_handle_p);
6084 Scene *scene = ps_handle->scene;
6085
6086 if (ps_handle->is_clone_cursor_pick) {
6087 MEM_delete(ps_handle);
6088 return;
6089 }
6090
6091 for (int i = 1; i < ps_handle->ps_views_tot; i++) {
6093 }
6094
6095 BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size);
6096
6097 paint_brush_exit_tex(ps_handle->brush);
6098
6099 for (int i = 0; i < ps_handle->ps_views_tot; i++) {
6100 ProjPaintState *ps;
6101 ps = ps_handle->ps_views[i];
6103 MEM_delete(ps);
6104 }
6105
6106 MEM_delete(ps_handle);
6107}
6108/* use project paint to re-apply an image */
6110{
6111 Main *bmain = CTX_data_main(C);
6112 Image *image = static_cast<Image *>(
6113 BLI_findlink(&bmain->images, RNA_enum_get(op->ptr, "image")));
6114 Scene &scene = *CTX_data_scene(C);
6115 ViewLayer &view_layer = *CTX_data_view_layer(C);
6116 ProjPaintState ps = {nullptr};
6117 int orig_brush_size;
6118 IDProperty *idgroup;
6119 IDProperty *view_data = nullptr;
6120 BKE_view_layer_synced_ensure(&scene, &view_layer);
6121 Object *ob = BKE_view_layer_active_object_get(&view_layer);
6122 bool uvs, mat, tex;
6123
6124 if (ob == nullptr || ob->type != OB_MESH) {
6125 BKE_report(op->reports, RPT_ERROR, "No active mesh object");
6126 return OPERATOR_CANCELLED;
6127 }
6128
6129 if (!ED_paint_proj_mesh_data_check(scene, *ob, &uvs, &mat, &tex, nullptr)) {
6130 ED_paint_data_warning(op->reports, uvs, mat, tex, true);
6132 return OPERATOR_CANCELLED;
6133 }
6134
6136
6137 if (image == nullptr) {
6138 BKE_report(op->reports, RPT_ERROR, "Image could not be found");
6139 return OPERATOR_CANCELLED;
6140 }
6141
6142 ps.reproject_image = image;
6143 ps.reproject_ibuf = BKE_image_acquire_ibuf(image, nullptr, nullptr);
6144
6145 if ((ps.reproject_ibuf == nullptr) ||
6147 {
6148 BKE_report(op->reports, RPT_ERROR, "Image data could not be found");
6149 return OPERATOR_CANCELLED;
6150 }
6151
6152 idgroup = IDP_GetProperties(&image->id);
6153
6154 if (idgroup) {
6156
6157 /* type check to make sure its ok */
6158 if (view_data != nullptr &&
6159 (view_data->len != PROJ_VIEW_DATA_SIZE || view_data->subtype != IDP_FLOAT))
6160 {
6161 BKE_report(op->reports, RPT_ERROR, "Image project data invalid");
6162 return OPERATOR_CANCELLED;
6163 }
6164 }
6165
6166 if (view_data) {
6167 /* image has stored view projection info */
6169 }
6170 else {
6172
6173 if (scene.camera == nullptr) {
6174 BKE_report(op->reports, RPT_ERROR, "No active camera set");
6175 return OPERATOR_CANCELLED;
6176 }
6177 }
6178
6179 /* override */
6180 ps.is_texbrush = false;
6181 ps.is_maskbrush = false;
6182 ps.do_masking = false;
6183 orig_brush_size = BKE_brush_size_get(&scene, ps.brush);
6184 /* cover the whole image */
6185 BKE_brush_size_set(&scene, ps.brush, 32 * U.pixelsize);
6186
6187 /* so pixels are initialized with minimal info */
6189
6191
6192 /* allocate and initialize spatial data structures */
6193 project_paint_begin(C, &ps, false, 0);
6194
6195 if (ps.mesh_eval == nullptr) {
6196 BKE_brush_size_set(&scene, ps.brush, orig_brush_size);
6197 BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh");
6198 return OPERATOR_CANCELLED;
6199 }
6200
6202
6203 const float pos[2] = {0.0, 0.0};
6204 const float lastpos[2] = {0.0, 0.0};
6205 int a;
6206
6207 project_paint_op(&ps, lastpos, pos);
6208
6210
6211 for (a = 0; a < ps.image_tot; a++) {
6214 }
6215
6216 project_paint_end(&ps);
6217
6219
6221 BKE_brush_size_set(&scene, ps.brush, orig_brush_size);
6222
6223 return OPERATOR_FINISHED;
6224}
6225
6227{
6228 PropertyRNA *prop;
6229
6230 /* identifiers */
6231 ot->name = "Project Image";
6232 ot->idname = "PAINT_OT_project_image";
6233 ot->description = "Project an edited render from the active camera back onto the object";
6234
6235 /* API callbacks. */
6236 ot->invoke = WM_enum_search_invoke;
6238
6239 /* flags */
6240 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6241
6242 prop = RNA_def_enum(ot->srna, "image", rna_enum_dummy_NULL_items, 0, "Image", "");
6245 ot->prop = prop;
6246}
6247
6249{
6250 bScreen *screen = CTX_wm_screen(C);
6251 if (!(screen && BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0))) {
6252 CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from");
6253 return false;
6254 }
6255 if (G.background || !GPU_is_init()) {
6256 return false;
6257 }
6258 return true;
6259}
6260
6262{
6263 using namespace blender;
6264 Image *image;
6265 ImBuf *ibuf;
6266 char filepath[FILE_MAX];
6267
6268 Main *bmain = CTX_data_main(C);
6270 Scene *scene = CTX_data_scene(C);
6271 ToolSettings *settings = scene->toolsettings;
6272 int w = settings->imapaint.screen_grab_size[0];
6273 int h = settings->imapaint.screen_grab_size[1];
6274 int maxsize;
6275 char err_out[256] = "unknown";
6276
6278 if (!area) {
6279 BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from");
6280 return OPERATOR_CANCELLED;
6281 }
6282
6284 if (!region) {
6285 BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from");
6286 return OPERATOR_CANCELLED;
6287 }
6288 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
6289
6290 RNA_string_get(op->ptr, "filepath", filepath);
6291
6292 maxsize = GPU_max_texture_size();
6293
6294 w = std::min(w, maxsize);
6295 h = std::min(h, maxsize);
6296
6297 /* Create a copy of the overlays where they are all turned off, except the
6298 * texture paint overlay opacity */
6299 View3D *v3d = static_cast<View3D *>(area->spacedata.first);
6300 View3D v3d_copy = blender::dna::shallow_copy(*v3d);
6301 v3d_copy.gridflag = 0;
6302 v3d_copy.flag2 = 0;
6303 v3d_copy.flag = V3D_HIDE_HELPLINES;
6304 v3d_copy.gizmo_flag = V3D_GIZMO_HIDE;
6305
6306 memset(&v3d_copy.overlay, 0, sizeof(View3DOverlay));
6311
6313 scene,
6314 eDrawType(v3d_copy.shading.type),
6315 &v3d_copy,
6316 region,
6317 w,
6318 h,
6321 nullptr,
6322 false,
6323 nullptr,
6324 nullptr,
6325 true,
6326 err_out);
6327
6328 if (!ibuf) {
6329 /* NOTE(@sergey): Mostly happens when OpenGL off-screen buffer was failed to create, */
6330 /* but could be other reasons. Should be handled in the future. */
6331 BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer: %s", err_out);
6332 return OPERATOR_CANCELLED;
6333 }
6334
6335 STRNCPY(ibuf->filepath, filepath);
6336
6337 image = BKE_image_add_from_imbuf(bmain, ibuf, "image_view");
6338
6339 /* Drop reference to ibuf so that the image owns it */
6340 IMB_freeImBuf(ibuf);
6341
6342 if (image) {
6343 /* now for the trickiness. store the view projection here!
6344 * re-projection will reuse this */
6345 IDProperty *idgroup = IDP_EnsureProperties(&image->id);
6346
6348 array.extend(Span(reinterpret_cast<float *>(rv3d->winmat), 16));
6349 array.extend(Span(reinterpret_cast<float *>(rv3d->viewmat), 16));
6350 float clip_start;
6351 float clip_end;
6352 const bool is_ortho = ED_view3d_clip_range_get(
6353 depsgraph, v3d, rv3d, true, &clip_start, &clip_end);
6354 array.append(clip_start);
6355 array.append(clip_end);
6356 /* using float for a bool is dodgy but since its an extra member in the array...
6357 * easier than adding a single bool prop */
6358 array.append(is_ortho ? 1.0f : 0.0f);
6359 IDP_AddToGroup(idgroup, bke::idprop::create(PROJ_VIEW_DATA_ID, array.as_span()).release());
6360 }
6361
6362 return OPERATOR_FINISHED;
6363}
6364
6366{
6367 /* identifiers */
6368 ot->name = "Image from View";
6369 ot->idname = "PAINT_OT_image_from_view";
6370 ot->description = "Make an image from biggest 3D view for reprojection";
6371
6372 /* API callbacks. */
6375
6376 /* flags */
6377 ot->flag = OPTYPE_REGISTER;
6378
6380 ot->srna, "filepath", nullptr, FILE_MAX, "File Path", "Name of the file");
6381}
6382
6383/*********************************************
6384 * Data generation for projective texturing *
6385 * *******************************************/
6386
6388 ReportList *reports, bool has_uvs, bool has_mat, bool has_tex, bool has_stencil)
6389{
6392 "Missing%s%s%s%s detected!",
6393 !has_uvs ? RPT_(" UVs,") : "",
6394 !has_mat ? RPT_(" Materials,") : "",
6395 !has_tex ? RPT_(" Textures (or linked),") : "",
6396 !has_stencil ? RPT_(" Stencil,") : "");
6397}
6398
6400 Object &ob,
6401 bool *r_has_uvs,
6402 bool *r_has_mat,
6403 bool *r_has_tex,
6404 bool *r_has_stencil)
6405{
6406 ImagePaintSettings &imapaint = scene.toolsettings->imapaint;
6407 const Brush *br = BKE_paint_brush(&imapaint.paint);
6408 bool has_mat = true;
6409 bool has_tex = true;
6410 bool has_stencil = true;
6411 bool has_uvs = true;
6412
6413 imapaint.missing_data = 0;
6414
6415 BLI_assert(ob.type == OB_MESH);
6416
6417 if (imapaint.mode == IMAGEPAINT_MODE_MATERIAL) {
6418 /* no material, add one */
6419 if (ob.totcol == 0) {
6420 has_mat = false;
6421 has_tex = false;
6422 }
6423 else {
6424 /* there may be material slots but they may be empty, check */
6425 has_mat = false;
6426 has_tex = false;
6427
6428 for (int i = 1; i < ob.totcol + 1; i++) {
6430
6431 if (ma && ID_IS_EDITABLE(ma) && !ID_IS_OVERRIDE_LIBRARY(ma)) {
6432 has_mat = true;
6433 if (ma->texpaintslot == nullptr) {
6434 /* refresh here just in case */
6435 BKE_texpaint_slot_refresh_cache(&scene, ma, &ob);
6436 }
6437 if (ma->texpaintslot != nullptr &&
6438 ma->texpaintslot[ma->paint_active_slot].ima != nullptr &&
6441 {
6442 has_tex = true;
6443 break;
6444 }
6445 }
6446 }
6447 }
6448 }
6449 else if (imapaint.mode == IMAGEPAINT_MODE_IMAGE) {
6450 if (imapaint.canvas == nullptr || !ID_IS_EDITABLE(imapaint.canvas)) {
6451 has_tex = false;
6452 }
6453 }
6454
6455 Mesh *mesh = BKE_mesh_from_object(&ob);
6457
6458 if (layernum == 0) {
6459 has_uvs = false;
6460 }
6461
6462 /* Make sure we have a stencil to paint on! */
6465
6466 if (imapaint.stencil == nullptr) {
6467 has_stencil = false;
6468 }
6469 }
6470
6471 if (!has_uvs) {
6473 }
6474 if (!has_mat) {
6476 }
6477 if (!has_tex) {
6479 }
6480 if (!has_stencil) {
6482 }
6483
6484 if (r_has_uvs) {
6485 *r_has_uvs = has_uvs;
6486 }
6487 if (r_has_mat) {
6488 *r_has_mat = has_mat;
6489 }
6490 if (r_has_tex) {
6491 *r_has_tex = has_tex;
6492 }
6493 if (r_has_stencil) {
6494 *r_has_stencil = has_stencil;
6495 }
6496
6497 return has_uvs && has_mat && has_tex && has_stencil;
6498}
6499
6500/* Add layer operator */
6501enum {
6509};
6510
6512 {LAYER_BASE_COLOR, "BASE_COLOR", 0, "Base Color", ""},
6513 {LAYER_SPECULAR, "SPECULAR", 0, "Specular IOR Level", ""},
6514 {LAYER_ROUGHNESS, "ROUGHNESS", 0, "Roughness", ""},
6515 {LAYER_METALLIC, "METALLIC", 0, "Metallic", ""},
6516 {LAYER_NORMAL, "NORMAL", 0, "Normal", ""},
6517 {LAYER_BUMP, "BUMP", 0, "Bump", ""},
6518 {LAYER_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", ""},
6519 {0, nullptr, 0, nullptr, nullptr},
6520};
6521
6523{
6525 if (!ma) {
6526 Main *bmain = CTX_data_main(C);
6527 ma = BKE_material_add(bmain, "Material");
6529 }
6530 return ma;
6531}
6532
6533static Image *proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data)
6534{
6535 Image *ima;
6536 float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
6537 char imagename[MAX_ID_NAME - 2] = "Material Diffuse Color";
6538 int width = 1024;
6539 int height = 1024;
6540 bool use_float = false;
6541 short gen_type = IMA_GENTYPE_BLANK;
6542 bool alpha = false;
6543
6544 if (op) {
6545 width = RNA_int_get(op->ptr, "width");
6546 height = RNA_int_get(op->ptr, "height");
6547 use_float = RNA_boolean_get(op->ptr, "float");
6548 gen_type = RNA_enum_get(op->ptr, "generated_type");
6549 RNA_float_get_array(op->ptr, "color", color);
6550 alpha = RNA_boolean_get(op->ptr, "alpha");
6551 RNA_string_get(op->ptr, "name", imagename);
6552 }
6553
6554 if (!alpha) {
6555 color[3] = 1.0f;
6556 }
6557
6558 /* TODO(lukas): Add option for tiled image. */
6559 ima = BKE_image_add_generated(bmain,
6560 width,
6561 height,
6562 imagename,
6563 alpha ? 32 : 24,
6564 use_float,
6565 gen_type,
6566 color,
6567 false,
6568 is_data,
6569 false);
6570
6571 return ima;
6572}
6573
6578{
6579 using namespace blender;
6580 char name[MAX_NAME] = "";
6581 float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
6584
6585 if (op) {
6586 RNA_string_get(op->ptr, "name", name);
6587 RNA_float_get_array(op->ptr, "color", color);
6588 domain = bke::AttrDomain(RNA_enum_get(op->ptr, "domain"));
6589 type = eCustomDataType(RNA_enum_get(op->ptr, "data_type"));
6590 }
6591
6592 Mesh *mesh = static_cast<Mesh *>(ob.data);
6594 const CustomDataLayer *layer = BKE_attribute_new(owner, name, type, domain, op->reports);
6595 if (!layer) {
6596 return nullptr;
6597 }
6598
6600 if (!mesh->default_color_attribute) {
6602 }
6603
6605
6606 return layer->name;
6607}
6608
6616static void default_paint_slot_color_get(int layer_type, Material *ma, float color[4])
6617{
6618 switch (layer_type) {
6619 case LAYER_BASE_COLOR:
6620 case LAYER_SPECULAR:
6621 case LAYER_ROUGHNESS:
6622 case LAYER_METALLIC: {
6623 bNodeTree *ntree = nullptr;
6624 bNode *in_node = nullptr;
6625 if (ma && ma->nodetree) {
6626 ma->nodetree->ensure_topology_cache();
6627 const blender::Span<bNode *> nodes = ma->nodetree->nodes_by_type(
6628 "ShaderNodeBsdfPrincipled");
6629 in_node = nodes.is_empty() ? nullptr : nodes.first();
6630 }
6631 if (!in_node) {
6632 /* An existing material or Principled BSDF node could not be found.
6633 * Copy default color values from a default Principled BSDF instead. */
6635 nullptr, "Temporary Shader Nodetree", ntreeType_Shader->idname);
6637 }
6639 *in_node, SOCK_IN, layer_type_items[layer_type].name);
6640 switch (in_sock->type) {
6641 case SOCK_FLOAT: {
6642 bNodeSocketValueFloat *socket_data = static_cast<bNodeSocketValueFloat *>(
6643 in_sock->default_value);
6644 copy_v3_fl(color, socket_data->value);
6645 color[3] = 1.0f;
6646 break;
6647 }
6648 case SOCK_VECTOR:
6649 case SOCK_RGBA: {
6650 bNodeSocketValueRGBA *socket_data = static_cast<bNodeSocketValueRGBA *>(
6651 in_sock->default_value);
6652 copy_v3_v3(color, socket_data->value);
6653 color[3] = 1.0f;
6654 break;
6655 }
6656 default:
6658 rgba_float_args_set(color, 0.0f, 0.0f, 0.0f, 1.0f);
6659 break;
6660 }
6661 /* Cleanup */
6662 if (ntree) {
6664 MEM_freeN(ntree);
6665 }
6666 return;
6667 }
6668 case LAYER_NORMAL:
6669 /* Neutral tangent space normal map. */
6670 rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f);
6671 break;
6672 case LAYER_BUMP:
6673 case LAYER_DISPLACEMENT:
6674 /* Neutral displacement and bump map. */
6675 rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f);
6676 break;
6677 }
6678}
6679
6681{
6683 Scene *scene = CTX_data_scene(C);
6684 Material *ma;
6685 Image *ima = nullptr;
6686 CustomDataLayer *layer = nullptr;
6687
6688 if (!ob) {
6689 return false;
6690 }
6691
6693
6694 if (ma) {
6695 Main *bmain = CTX_data_main(C);
6696 int type = RNA_enum_get(op->ptr, "type");
6697 bool is_data = (type > LAYER_BASE_COLOR);
6698
6699 bNode *new_node;
6700 bNodeTree *ntree = ma->nodetree;
6701
6702 if (!ntree) {
6704 ntree = ma->nodetree;
6705 }
6706
6707 ma->use_nodes = true;
6708
6709 const ePaintCanvasSource slot_type = ob->mode == OB_MODE_SCULPT ?
6711 "slot_type") :
6713
6714 /* Create a new node. */
6715 switch (slot_type) {
6718 ima = proj_paint_image_create(op, bmain, is_data);
6719 new_node->id = &ima->id;
6720 break;
6721 }
6724 if (const char *name = proj_paint_color_attribute_create(op, *ob)) {
6725 STRNCPY_UTF8(((NodeShaderAttribute *)new_node->storage)->name, name);
6726 }
6727 break;
6728 }
6731 return false;
6732 }
6733 blender::bke::node_set_active(*ntree, *new_node);
6734
6735 /* Connect to first available principled BSDF node. */
6736 ntree->ensure_topology_cache();
6737 const blender::Span<bNode *> bsdf_nodes = ntree->nodes_by_type("ShaderNodeBsdfPrincipled");
6738 bNode *in_node = bsdf_nodes.is_empty() ? nullptr : bsdf_nodes.first();
6739 bNode *out_node = new_node;
6740
6741 if (in_node != nullptr) {
6742 bNodeSocket *out_sock = blender::bke::node_find_socket(*out_node, SOCK_OUT, "Color");
6743 bNodeSocket *in_sock = nullptr;
6744
6745 if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) {
6746 in_sock = blender::bke::node_find_socket(*in_node, SOCK_IN, layer_type_items[type].name);
6747 }
6748 else if (type == LAYER_NORMAL) {
6749 bNode *nor_node;
6751
6752 in_sock = blender::bke::node_find_socket(*nor_node, SOCK_IN, "Color");
6753 blender::bke::node_add_link(*ntree, *out_node, *out_sock, *nor_node, *in_sock);
6754
6755 in_sock = blender::bke::node_find_socket(*in_node, SOCK_IN, "Normal");
6756 out_sock = blender::bke::node_find_socket(*nor_node, SOCK_OUT, "Normal");
6757
6758 out_node = nor_node;
6759 }
6760 else if (type == LAYER_BUMP) {
6761 bNode *bump_node;
6763
6764 in_sock = blender::bke::node_find_socket(*bump_node, SOCK_IN, "Height");
6765 blender::bke::node_add_link(*ntree, *out_node, *out_sock, *bump_node, *in_sock);
6766
6767 in_sock = blender::bke::node_find_socket(*in_node, SOCK_IN, "Normal");
6768 out_sock = blender::bke::node_find_socket(*bump_node, SOCK_OUT, "Normal");
6769
6770 out_node = bump_node;
6771 }
6772 else if (type == LAYER_DISPLACEMENT) {
6773 /* Connect to the displacement output socket */
6774 const blender::Span<bNode *> output_nodes = ntree->nodes_by_type(
6775 "ShaderNodeOutputMaterial");
6776 in_node = output_nodes.is_empty() ? nullptr : output_nodes.first();
6777
6778 if (in_node != nullptr) {
6779 in_sock = blender::bke::node_find_socket(*in_node, SOCK_IN, layer_type_items[type].name);
6780 }
6781 else {
6782 in_sock = nullptr;
6783 }
6784 }
6785
6786 /* Check if the socket in already connected to something */
6787 bNodeLink *link = in_sock ? in_sock->link : nullptr;
6788 if (in_sock != nullptr && link == nullptr) {
6789 blender::bke::node_add_link(*ntree, *out_node, *out_sock, *in_node, *in_sock);
6790
6791 blender::bke::node_position_relative(*out_node, *in_node, out_sock, *in_sock);
6792 }
6793 }
6794
6796 /* In case we added more than one node, position them too. */
6798
6799 if (ima) {
6800 BKE_texpaint_slot_refresh_cache(scene, ma, ob);
6801 BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_USER_NEW_IMAGE);
6803 ED_space_image_sync(bmain, ima, false);
6804 }
6805 if (layer) {
6806 BKE_texpaint_slot_refresh_cache(scene, ma, ob);
6807 DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_GEOMETRY);
6809 }
6810
6811 DEG_id_tag_update(&ntree->id, 0);
6815
6816 ED_paint_proj_mesh_data_check(*scene, *ob, nullptr, nullptr, nullptr, nullptr);
6817
6818 return true;
6819 }
6820
6821 return false;
6822}
6823
6824static int get_texture_layer_type(wmOperator *op, const char *prop_name)
6825{
6826 int type_value = RNA_enum_get(op->ptr, prop_name);
6827 int type = RNA_enum_from_value(layer_type_items, type_value);
6828 BLI_assert(type != -1);
6829 return type;
6830}
6831
6839
6841 int texture_type,
6842 char *dst,
6843 int dst_maxncpy)
6844{
6846 const char *base_name = ma ? &ma->id.name[2] : &ob->id.name[2];
6847 BLI_snprintf(dst, dst_maxncpy, "%s %s", base_name, DATA_(layer_type_items[texture_type].name));
6848}
6849
6851 wmOperator *op,
6852 const wmEvent * /*event*/)
6853{
6856
6857 int type = get_texture_layer_type(op, "type");
6858
6859 /* Set default name. */
6860 char imagename[MAX_ID_NAME - 2];
6861 get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename));
6862 RNA_string_set(op->ptr, "name", imagename);
6863
6864 /* Set default color. Copy the color from nodes, so it matches the existing material.
6865 * Material could be null so we should have a default color. */
6866 float color[4];
6868 RNA_float_set_array(op->ptr, "color", color);
6869
6871 C, op, 300, IFACE_("Add Paint Slot"), CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add"));
6872}
6873
6875{
6876 uiLayout *layout = op->layout;
6877 uiLayoutSetPropSep(layout, true);
6878 uiLayoutSetPropDecorate(layout, false);
6881
6882 if (ob->mode == OB_MODE_SCULPT) {
6883 slot_type = (ePaintCanvasSource)RNA_enum_get(op->ptr, "slot_type");
6884 layout->prop(op->ptr, "slot_type", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
6885 }
6886
6887 layout->prop(op->ptr, "name", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6888
6889 switch (slot_type) {
6891 uiLayout *col = &layout->column(true);
6892 col->prop(op->ptr, "width", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6893 col->prop(op->ptr, "height", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6894
6895 layout->prop(op->ptr, "alpha", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6896 layout->prop(op->ptr, "generated_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6897 layout->prop(op->ptr, "float", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6898 break;
6899 }
6901 layout->prop(op->ptr, "domain", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
6902 layout->prop(op->ptr, "data_type", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
6903 break;
6906 break;
6907 }
6908
6909 layout->prop(op->ptr, "color", UI_ITEM_NONE, std::nullopt, ICON_NONE);
6910}
6911
6912#define IMA_DEF_NAME N_("Untitled")
6913
6915{
6916 using namespace blender;
6917 PropertyRNA *prop;
6918 static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
6919
6920 static const EnumPropertyItem slot_type_items[3] = {
6921 {PAINT_CANVAS_SOURCE_IMAGE, "IMAGE", 0, "Image", ""},
6922 {PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE, "COLOR_ATTRIBUTE", 0, "Color Attribute", ""},
6923 {0, nullptr, 0, nullptr, nullptr},
6924 };
6925
6926 /* identifiers */
6927 ot->name = "Add Paint Slot";
6928 ot->description = "Add a paint slot";
6929 ot->idname = "PAINT_OT_add_texture_paint_slot";
6930
6931 /* API callbacks. */
6936
6937 /* flags */
6938 ot->flag = OPTYPE_UNDO;
6939
6940 /* Shared Properties */
6941 prop = RNA_def_enum(ot->srna,
6942 "type",
6944 0,
6945 "Material Layer Type",
6946 "Material layer type of new paint slot");
6948
6949 prop = RNA_def_enum(
6950 ot->srna, "slot_type", slot_type_items, 0, "Slot Type", "Type of new paint slot");
6951
6952 prop = RNA_def_string(
6953 ot->srna, "name", IMA_DEF_NAME, MAX_NAME, "Name", "Name for new paint slot source");
6955
6956 prop = RNA_def_float_color(
6957 ot->srna, "color", 4, nullptr, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
6959 RNA_def_property_float_array_default(prop, default_color);
6960
6961 /* Image Properties */
6962 prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
6964
6965 prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
6967
6968 RNA_def_boolean(ot->srna, "alpha", true, "Alpha", "Create an image with an alpha channel");
6969
6970 RNA_def_enum(ot->srna,
6971 "generated_type",
6974 "Generated Type",
6975 "Fill the image with a grid for UV map testing");
6976
6977 RNA_def_boolean(ot->srna,
6978 "float",
6979 false,
6980 "32-bit Float",
6981 "Create image with 32-bit floating-point bit depth");
6982
6983 /* Color Attribute Properties */
6984 RNA_def_enum(ot->srna,
6985 "domain",
6988 "Domain",
6989 "Type of element that attribute is stored on");
6990
6991 RNA_def_enum(ot->srna,
6992 "data_type",
6995 "Data Type",
6996 "Type of data stored in attribute");
6997}
6998
7000{
7001 /* no checks here, poll function does them for us */
7002 Main *bmain = CTX_data_main(C);
7004 Scene *scene = CTX_data_scene(C);
7005
7006 ED_uvedit_add_simple_uvs(bmain, scene, ob);
7007
7008 ED_paint_proj_mesh_data_check(*scene, *ob, nullptr, nullptr, nullptr, nullptr);
7009
7010 DEG_id_tag_update(static_cast<ID *>(ob->data), 0);
7013 return OPERATOR_FINISHED;
7014}
7015
7017{
7019
7020 if (!ob || ob->type != OB_MESH || ob->mode != OB_MODE_TEXTURE_PAINT) {
7021 return false;
7022 }
7023 return true;
7024}
7025
7027{
7028 /* identifiers */
7029 ot->name = "Add Simple UVs";
7030 ot->description = "Add cube map UVs on mesh";
7031 ot->idname = "PAINT_OT_add_simple_uvs";
7032
7033 /* API callbacks. */
7034 ot->exec = add_simple_uvs_exec;
7035 ot->poll = add_simple_uvs_poll;
7036
7037 /* flags */
7038 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
7039}
void BKE_id_attributes_default_color_set(struct ID *id, std::optional< blender::StringRef > name)
struct CustomDataLayer * BKE_attribute_new(AttributeOwner &owner, blender::StringRef name, eCustomDataType type, blender::bke::AttrDomain domain, struct ReportList *reports)
Definition attribute.cc:391
void BKE_id_attributes_active_color_set(struct ID *id, std::optional< blender::StringRef > name)
Definition attribute.cc:986
const MTex * BKE_brush_color_texture_get(const Brush *brush, eObjectMode object_mode)
Definition brush.cc:869
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1210
void BKE_brush_size_set(Scene *scene, Brush *brush, int size)
Definition brush.cc:1194
float BKE_brush_sample_tex_3d(const Scene *scene, const Brush *br, const MTex *mtex, const float point[3], float rgba[4], int thread, ImagePool *pool)
Definition brush.cc:877
const std::optional< BrushColorJitterSettings > BKE_brush_color_jitter_get_settings(const Scene *scene, const Paint *paint, const Brush *brush)
Definition brush.cc:1137
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
Definition brush.cc:1269
float BKE_brush_curve_strength_clamped(const Brush *br, float p, float len)
Definition brush.cc:1566
float BKE_brush_sample_masktex(const Scene *scene, Brush *br, const float point[2], int thread, ImagePool *pool)
Definition brush.cc:1000
Camera data-block and utility functions.
void BKE_camera_params_init(CameraParams *params)
void BKE_camera_params_from_object(CameraParams *params, const struct Object *cam_ob)
void BKE_camera_params_compute_viewplane(CameraParams *params, int winx, int winy, float aspx, float aspy)
void BKE_camera_params_compute_matrix(CameraParams *params)
bool BKE_colorband_evaluate(const ColorBand *coba, float in, float out[4])
Definition colorband.cc:395
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
bScreen * CTX_wm_screen(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_clone_layer(const CustomData *data, eCustomDataType type)
const void * CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_stencil_layer(const CustomData *data, eCustomDataType type)
#define ORIGINDEX_NONE
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
IDProperty * IDP_GetPropertyFromGroup(const IDProperty *prop, blender::StringRef name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:766
IDProperty * IDP_GetPropertyTypeFromGroup(const IDProperty *prop, blender::StringRef name, char type) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:779
bool IDP_AddToGroup(IDProperty *group, IDProperty *prop) ATTR_NONNULL()
Definition idprop.cc:725
IDProperty * IDP_EnsureProperties(ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition idprop.cc:892
IDProperty * IDP_GetProperties(ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition idprop.cc:887
#define IDP_Array(prop)
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_mark_dirty(Image *image, ImBuf *ibuf)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_pool_free(ImagePool *pool)
Image * BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name)
ImagePool * BKE_image_pool_new()
void BKE_imageuser_default(ImageUser *iuser)
void BKE_image_free_gputextures(Image *ima)
Definition image_gpu.cc:570
#define IMA_SIGNAL_USER_NEW_IMAGE
Definition BKE_image.hh:166
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser)
Image * BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], bool stereo3d, bool is_data, bool tiled)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
General operations, lookup, etc. for materials.
void BKE_texpaint_slot_refresh_cache(Scene *scene, Material *ma, const Object *ob)
Material * BKE_material_add(Main *bmain, const char *name)
Material * BKE_object_material_get(Object *ob, short act)
void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
@ BKE_MAT_ASSIGN_USERPREF
Mesh * BKE_mesh_from_object(Object *ob)
#define BKE_MESH_TESSTRI_VINDEX_ORDER(_tri, _v)
#define SH_NODE_TEX_IMAGE
#define SH_NODE_BSDF_PRINCIPLED
#define SH_NODE_BUMP
#define SH_NODE_NORMAL_MAP
#define SH_NODE_ATTRIBUTE
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
blender::float3 seed_hsv_jitter()
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:467
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:636
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
int BKE_scene_num_threads(const Scene *scene)
Definition scene.cc:2922
ARegion * BKE_area_find_region_active_win(const ScrArea *area)
Definition screen.cc:853
ScrArea * BKE_screen_find_big_area(const bScreen *screen, int spacetype, short min)
Definition screen.cc:938
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_CIRCULAR_BACKWARD_END(type, lb, lb_iter, lb_init)
#define LISTBASE_CIRCULAR_FORWARD_BEGIN(type, lb, lb_iter, lb_init)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#define LISTBASE_CIRCULAR_BACKWARD_BEGIN(type, lb, lb_iter, lb_init)
#define LISTBASE_CIRCULAR_FORWARD_END(type, lb, lb_iter, lb_init)
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
int pow_i(int base, int exp)
Definition math_base.cc:13
MINLINE float min_ff(float a, float b)
MINLINE int mod_i(int i, int n)
MINLINE float square_f(float a)
MINLINE float min_fff(float a, float b, float c)
MINLINE int square_s(short a)
MINLINE float safe_acosf(float a)
MINLINE int count_bits_i(unsigned int n)
MINLINE void straight_to_premul_v4_v4(float premul[4], const float straight[4])
MINLINE void rgba_float_args_set(float col[4], float r, float g, float b, float a)
MINLINE void straight_uchar_to_premul_float(float result[4], const unsigned char color[4])
MINLINE void premul_to_straight_v4(float color[4])
void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
MINLINE void srgb_to_linearrgb_uchar4(float linear[4], const unsigned char srgb[4])
MINLINE void float_to_byte_dither_v3(unsigned char b[3], const float f[3], float dither, int x, int y)
MINLINE void premul_to_straight_v4_v4(float straight[4], const float premul[4])
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
MINLINE void linearrgb_to_srgb_uchar3(unsigned char srgb[3], const float linear[3])
void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[3])
MINLINE void premul_float_to_straight_uchar(unsigned char *result, const float color[4])
MINLINE void blend_color_add_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_mix_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_mix_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void blend_color_interpolate_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4], float ft)
MINLINE void blend_color_interpolate_float(float dst[4], const float src1[4], const float src2[4], float t)
#define M_1_PI
#define M_PI_2
#define M_PI
int isect_seg_seg_v2(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
int isect_point_quad_v2(const float p[2], const float v1[2], const float v2[2], const float v3[2], const float v4[2])
MINLINE float area_tri_v2(const float v1[2], const float v2[2], const float v3[2])
void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
MINLINE float cross_tri_v2(const float v1[2], const float v2[2], const float v3[2])
bool isect_seg_seg_v2_simple(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
float line_point_factor_v2(const float p[2], const float l1[2], const float l2[2])
float resolve_quad_u_v2(const float st[2], const float st0[2], const float st1[2], const float st2[2], const float st3[2])
void barycentric_weights_v2_persp(const float v1[4], const float v2[4], const float v3[4], const float co[2], float w[3])
float dist_squared_to_line_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:278
int isect_point_tri_v2(const float pt[2], const float v1[2], const float v2[2], const float v3[2])
int isect_seg_seg_v2_point(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float r_vi[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
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_project_m4_v3(const float mat[4][4], float vec[3])
void mul_m4_v3(const float M[4][4], float r[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_m4_v4(const float mat[4][4], float r[4])
void normalize_m4(float R[4][4]) ATTR_NONNULL()
MINLINE void mul_v4_fl(float r[4], float f)
MINLINE void add_v4_v4(float r[4], const float a[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void mul_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
void interp_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float w[3])
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)
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v2_v2v2v2(float r[2], const float a[2], const float b[2], const float c[2], const float t[3])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void negate_v3(float r[3])
MINLINE void zero_v4(float r[4])
MINLINE void mul_v3_v3v3(float r[3], const float v1[3], const float v2[3])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
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 float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
#define FILE_MAX
void BLI_rcti_init_minmax(struct rcti *rect)
Definition rct.cc:474
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rcti_is_valid(const struct rcti *rect)
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
bool BLI_rcti_is_empty(const struct rcti *rect)
void BLI_rcti_do_minmax_rcti(struct rcti *rect, const struct rcti *other)
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
unsigned char uchar
unsigned int uint
unsigned short ushort
@ TASK_PRIORITY_HIGH
Definition BLI_task.h:53
TaskPool * BLI_task_pool_create_suspended(void *userdata, eTaskPriority priority)
Definition task_pool.cc:499
void BLI_task_pool_work_and_wait(TaskPool *pool)
Definition task_pool.cc:531
void BLI_task_pool_free(TaskPool *pool)
Definition task_pool.cc:517
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition task_pool.cc:522
pthread_spinlock_t SpinLock
void BLI_thread_unlock(int type)
Definition threads.cc:333
void BLI_thread_lock(int type)
Definition threads.cc:328
#define BLENDER_MAX_THREADS
Definition BLI_threads.h:16
void BLI_spin_init(SpinLock *spin)
Definition threads.cc:391
void BLI_spin_unlock(SpinLock *spin)
Definition threads.cc:430
void BLI_spin_lock(SpinLock *spin)
Definition threads.cc:405
@ LOCK_CUSTOM1
Definition BLI_threads.h:66
void BLI_spin_end(SpinLock *spin)
Definition threads.cc:445
#define CLAMP(a, b, c)
#define INIT_MINMAX2(min, max)
#define UNPACK4(a)
#define UNUSED_VARS(...)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
Compatibility-like things for windows.
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
#define DATA_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SHADING
Definition DNA_ID.h:1002
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ IDP_FLOAT
@ IDP_ARRAY
@ BRUSH_LOCK_ALPHA
@ BRUSH_ACCUMULATE
@ BRUSH_DIR_IN
@ BRUSH_USE_GRADIENT
@ IMAGE_PAINT_BRUSH_TYPE_MASK
@ IMAGE_PAINT_BRUSH_TYPE_FILL
@ IMAGE_PAINT_BRUSH_TYPE_DRAW
@ IMAGE_PAINT_BRUSH_TYPE_CLONE
@ IMAGE_PAINT_BRUSH_TYPE_SOFTEN
@ IMAGE_PAINT_BRUSH_TYPE_SMEAR
@ BRUSH_GRADIENT_LINEAR
@ BRUSH_GRADIENT_RADIAL
@ CD_PROP_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ IMA_SRC_TILED
@ IMA_GENTYPE_BLANK
@ ME_EDIT_PAINT_FACE_SEL
@ SOCK_OUT
@ SOCK_IN
@ SOCK_VECTOR
@ SOCK_FLOAT
@ SOCK_RGBA
eDrawType
@ OB_MODE_SCULPT
@ OB_MODE_TEXTURE_PAINT
Object is a sort of wrapper for general info.
@ OB_MESH
@ OB_NEG_SCALE
@ PAINT_SYMM_Y
@ PAINT_SYMM_X
@ PAINT_SYMM_Z
@ IMAGEPAINT_PROJECT_LAYER_STENCIL_INV
@ IMAGEPAINT_PROJECT_BACKFACE
@ IMAGEPAINT_PROJECT_XRAY
@ IMAGEPAINT_PROJECT_LAYER_STENCIL
@ IMAGEPAINT_PROJECT_LAYER_CLONE
@ IMAGEPAINT_PROJECT_FLAT
#define IMAGEPAINT_MODE_IMAGE
@ R_ALPHAPREMUL
#define IMAGEPAINT_MODE_MATERIAL
@ PAINT_USE_CAVITY_MASK
ePaintCanvasSource
@ PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE
@ PAINT_CANVAS_SOURCE_IMAGE
@ PAINT_CANVAS_SOURCE_MATERIAL
@ IMAGEPAINT_MISSING_TEX
@ IMAGEPAINT_MISSING_MATERIAL
@ IMAGEPAINT_MISSING_UVS
@ IMAGEPAINT_MISSING_STENCIL
@ IMAGEPAINT_DRAWING
@ SPACE_VIEW3D
@ MTEX_MAP_MODE_3D
@ USER_ORBIT_SELECTION
@ V3D_GIZMO_HIDE
@ V3D_HIDE_HELPLINES
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ V3D_OVERLAY_HIDE_OBJECT_ORIGINS
@ V3D_OVERLAY_HIDE_BONES
@ V3D_OVERLAY_HIDE_MOTION_PATHS
@ V3D_OVERLAY_HIDE_OBJECT_XTRAS
@ V3D_OVERLAY_HIDE_CURSOR
@ V3D_OVERLAY_HIDE_TEXT
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void ED_space_image_sync(Main *bmain, Image *image, bool ignore_render_viewer)
Definition image_edit.cc:70
void ED_node_shader_default(const bContext *C, ID *id)
Definition node_edit.cc:574
void * ED_image_paint_tile_push(PaintTileMap *paint_tile_map, Image *image, ImBuf *ibuf, ImBuf **tmpibuf, ImageUser *iuser, int x_tile, int y_tile, unsigned short **r_mask, bool **r_valid, bool use_thread_lock, bool find_prev)
void ED_image_paint_tile_lock_end()
Definition image_undo.cc:72
void ED_image_undo_push_begin(const char *name, PaintMode paint_mode)
#define ED_IMAGE_UNDO_TILE_NUMBER(size)
Definition ED_paint.hh:115
void ED_image_undo_push_end()
#define ED_IMAGE_UNDO_TILE_SIZE
Definition ED_paint.hh:114
PaintTileMap * ED_image_paint_tile_map_get()
void ED_image_paint_tile_lock_init()
Definition image_undo.cc:67
#define ED_IMAGE_UNDO_TILE_BITS
Definition ED_paint.hh:113
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:714
bool ED_operator_object_active_editable_mesh(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
bool ED_view3d_autodist(ARegion *region, View3D *v3d, const int mval[2], float mouse_worldloc[3], const float fallback_depth_pt[3])
void ED_view3d_clipping_local(RegionView3D *rv3d, const float mat[4][4])
bool ED_view3d_clip_range_get(const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d, bool use_ortho_factor, float *r_clip_start, float *r_clip_end)
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:192
void view3d_operator_needs_gpu(const bContext *C)
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], bool is_local)
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
ImBuf * ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, Scene *scene, eDrawType drawtype, View3D *v3d, ARegion *region, int sizex, int sizey, eImBufFlags imbuf_flag, int alpha_mode, const char *viewname, bool restore_rv3d_mats, GPUOffScreen *ofs, GPUViewport *viewport, bool use_camera_view_bounds, char err_out[256])
int GPU_max_texture_size()
bool GPU_is_init()
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
void IMB_byte_from_float(ImBuf *ibuf)
void IMB_blend_color_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4], IMB_BlendMode mode)
Definition rectop.cc:29
IMB_BlendMode
Definition IMB_imbuf.hh:185
@ IMB_BLEND_ERASE_ALPHA
Definition IMB_imbuf.hh:192
@ IMB_BLEND_ADD_ALPHA
Definition IMB_imbuf.hh:193
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_free_float_pixels(ImBuf *ibuf)
void IMB_free_byte_pixels(ImBuf *ibuf)
void IMB_blend_color_float(float dst[4], const float src1[4], const float src2[4], IMB_BlendMode mode)
Definition rectop.cc:117
void IMB_float_from_byte(ImBuf *ibuf)
@ IB_byte_data
Read Guarded memory(de)allocation.
struct blender::bke::bNodeTreeType * ntreeType_Shader
const EnumPropertyItem * RNA_image_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, bool *r_free)
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:406
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
@ PROP_PIXEL
Definition RNA_types.hh:236
@ PROP_COLOR_GAMMA
Definition RNA_types.hh:260
#define C
Definition RandGen.cpp:29
@ UI_ITEM_R_EXPAND
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
#define NC_SCENE
Definition WM_types.hh:375
#define NA_ADDED
Definition WM_types.hh:583
#define ND_TOOLSETTINGS
Definition WM_types.hh:446
#define NA_EDITED
Definition WM_types.hh:581
ReportList * reports
Definition WM_types.hh:1025
#define NC_IMAGE
Definition WM_types.hh:381
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE int32_t atomic_fetch_and_add_int32(int32_t *p, int32_t x)
#define U
return true
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
BPy_StructRNA * depsgraph
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
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
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
void append(const array< T > &from)
constexpr int64_t size() const
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr bool is_empty() const
Definition BLI_span.hh:260
#define sinf(x)
#define cosf(x)
#define tanf(x)
#define atan2f(x, y)
#define fmodf(x, y)
#define acosf(x)
#define fabsf(x)
#define sqrtf(x)
TaskPool * task_pool
uint pos
uint col
VecBase< float, 4 > float4
#define printf(...)
float distance(VecOp< float, D >, VecOp< float, D >) RET
#define MAX_ID_NAME
#define MEM_SIZE_OPTIMAL(size)
#define ID_IS_EDITABLE(_id)
#define MAX_NAME
#define ID_IS_OVERRIDE_LIBRARY(_id)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
const ccl_global KernelWorkTile * tile
const int tile_index
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float interp(const float a, const float b, const float t)
Definition math_base.h:502
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
#define unit_float_to_uchar_clamp_v3(v1, v2)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong state[N]
#define G(x, y, z)
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRef prop_name, int32_t value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_INT, set its name and value.
void node_position_propagate(bNode &node)
Definition node.cc:4314
void node_tree_free_tree(bNodeTree &ntree)
Definition node.cc:4718
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
void node_position_relative(bNode &from_node, const bNode &to_node, const bNodeSocket *from_sock, const bNodeSocket &to_sock)
Definition node.cc:4272
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3804
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
bNodeTree * node_tree_add_tree(Main *bmain, StringRef name, StringRef idname)
Definition node.cc:4362
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4996
Object * context_active_object(const bContext *C)
bool object_active_color_fill(Object &ob, const float fill_color[4], bool only_selected)
uchar4 interpolate_nearest_wrap_byte(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:42
uchar4 interpolate_bilinear_wrap_byte(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:95
uchar4 interpolate_cubic_bspline_byte(const ImBuf *in, float u, float v)
float4 interpolate_nearest_wrap_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:46
float4 interpolate_cubic_bspline_fl(const ImBuf *in, float u, float v)
float4 interpolate_bilinear_wrap_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:99
T clamp(const T &a, const T &min, const T &max)
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< int32_t, 3 > int3
void paint_delete_blur_kernel(BlurKernel *kernel)
void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, ImageUser *iuser, short texpaint)
void paint_brush_init_tex(Brush *brush)
bool paint_use_opacity_masking(const Scene *scene, const Paint *paint, const Brush *brush)
BlurKernel * paint_new_blur_kernel(Brush *br, bool proj)
void paint_brush_color_get(Scene *scene, const Paint *paint, Brush *br, std::optional< blender::float3 > &initial_hsv_jitter, bool color_correction, bool invert, float distance, float pressure, const ColorManagedDisplay *display, float r_color[3])
void paint_brush_exit_tex(Brush *brush)
void set_imapaintpartial(ImagePaintPartialRedraw *ippr)
#define PS_CORNER_TRI_AS_UV_3(uvlayer, face_i, tri)
static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *softenArena, LinkNode **softenPixels)
static bool project_paint_clone_face_skip(ProjPaintState *ps, ProjPaintLayerClone *lc, const TexPaintSlot *slot, const int tri_index)
static bool project_bucket_point_occluded(const ProjPaintState *ps, LinkNode *bucketFace, const int orig_face, const float pixelScreenCo[4])
static void screen_px_from_ortho(const float uv[2], const float v1co[3], const float v2co[3], const float v3co[3], const float uv1co[2], const float uv2co[2], const float uv3co[2], float pixelScreenCo[4], float w[3])
static int project_paint_face_paint_tile(Image *ima, const float *uv)
static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], float w[3])
static int project_bucket_offset(const ProjPaintState *ps, const float projCoSS[2])
static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], float *rgba_fp, uchar *rgba, const bool interp)
static void image_paint_partial_redraw_expand(ImagePaintPartialRedraw *cell, const ProjPixel *projPixel)
#define ISECT_TRUE_P1
#define PROJ_FACE_SEAM2
static void project_paint_begin(const bContext *C, ProjPaintState *ps, const bool is_multi_view, const char symmetry_flag)
#define PROJ_SRC_VIEW_FILL
#define ISECT_ALL4
static int project_bucket_offset_safe(const ProjPaintState *ps, const float projCoSS[2])
static bool project_paint_winclip(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS)
static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *ps)
static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter)
static ProjPixel * project_paint_uvpixel_init(const ProjPaintState *ps, MemArena *arena, const TileInfo *tinf, int x_px, int y_px, const float mask, const int tri_index, const float pixelScreenCo[4], const float world_spaceCo[3], const float w[3])
static void proj_paint_face_coSS_init(const ProjPaintState *ps, const int3 &corner_tri, ProjPaintFaceCoSS *coSS)
static void rect_to_uvspace_ortho(const rctf *bucket_bounds, const float *v1coSS, const float *v2coSS, const float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[4][2], const int flip)
static bool add_simple_uvs_poll(bContext *C)
static const char * proj_paint_color_attribute_create(wmOperator *op, Object &ob)
#define PROJ_VIEW_DATA_SIZE
#define PROJ_BUCKET_NULL
static int project_paint_pixel_sizeof(const short brush_type)
static bool IsectPT2Df_limit(const float pt[2], const float v1[2], const float v2[2], const float v3[2], const float limit)
#define ISECT_TRUE
#define PROJ_FACE_SEAM1
#define ISECT_TRUE_P2
static int get_texture_layer_type(wmOperator *op, const char *prop_name)
#define IMA_DEF_NAME
static void project_paint_bleed_add_face_user(const ProjPaintState *ps, MemArena *arena, const int3 &corner_tri, const int tri_index)
void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot)
static bool texture_paint_image_from_view_poll(bContext *C)
#define PROJ_BOUNDBOX_DIV
static void do_projectpaint_mask(ProjPaintState *ps, ProjPixel *projPixel, float mask)
static void screen_px_from_persp(const float uv[2], const float v1co[4], const float v2co[4], const float v3co[4], const float uv1co[2], const float uv2co[2], const float uv3co[2], float pixelScreenCo[4], float w[3])
static wmOperatorStatus add_simple_uvs_exec(bContext *C, wmOperator *)
static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
#define ISECT_4
#define PROJ_FACE_SEAM0
static void project_bucket_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const rctf *clip_rect, const rctf *bucket_bounds)
static bool line_rect_clip(const rctf *rect, const float l1[4], const float l2[4], const float uv1[2], const float uv2[2], float uv[2], bool is_ortho)
static void proj_paint_face_lookup_init(const ProjPaintState *ps, ProjPaintFaceLookup *face_lookup)
#define TILE_PENDING
static void paint_proj_stroke_ps(const bContext *, void *ps_handle_p, const float prev_pos[2], const float pos[2], const bool eraser, float pressure, float distance, float size, ProjPaintState *ps)
static bool cmp_uv(const float vec2a[2], const float vec2b[2])
static Image * project_paint_face_clone_image(const ProjPaintState *ps, int tri_index)
void * paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode)
#define PROJ_GEOM_TOLERANCE
static void partial_redraw_array_init(ImagePaintPartialRedraw *pr)
static bool project_paint_check_face_paintable(const ProjPaintState *ps, const ProjPaintFaceLookup *face_lookup, const int tri_i)
@ LAYER_NORMAL
@ LAYER_DISPLACEMENT
@ LAYER_ROUGHNESS
@ LAYER_METALLIC
@ LAYER_BUMP
@ LAYER_BASE_COLOR
@ LAYER_SPECULAR
static wmOperatorStatus texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask)
static void project_paint_build_proj_ima(ProjPaintState *ps, MemArena *arena, ListBase *used_images)
#define PS_CORNER_TRI_ASSIGN_UV_3(uv_tri, uvlayer, face_i, tri)
#define PROJ_SRC_IMAGE_CAM
static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone)
#define PROJ_FACE_NOSEAM0
static void project_bucket_bounds(const ProjPaintState *ps, const int bucket_x, const int bucket_y, rctf *r_bucket_bounds)
static int line_isect_x(const float p1[2], const float p2[2], const float x_level, float *y_isect)
#define PROJ_BUCKET_BRUSH_DIV
#define PS_CORNER_TRI_AS_VERT_INDEX_3(ps, tri)
#define PROJ_FACE_SEAM_INIT2
#define PROJ_BUCKET_RECT_MAX
BLI_INLINE uchar f_to_char(const float val)
static bool pixel_bounds_uv(const float uv_quad[4][2], const int ibuf_x, const int ibuf_y, rcti *r_bounds_px)
static int float_z_sort_flip(const void *p1, const void *p2)
static void do_projectpaint_thread(TaskPool *__restrict, void *ph_v)
#define PROJ_SRC_IMAGE_VIEW
static bool project_bucket_isect_circle(const float cent[2], const float radius_squared, const rctf *bucket_bounds)
static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *smearArena, LinkNode **smearPixels, const float co[2])
#define PROJ_FACE_WINDING_INIT
static Image * proj_paint_image_create(wmOperator *op, Main *bmain, bool is_data)
void PAINT_OT_add_simple_uvs(wmOperatorType *ot)
static bool project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2])
#define PROJ_BUCKET_RECT_MIN
static bool project_image_refresh_tagged(ProjPaintState *ps)
#define PROJ_VERT_CULL
#define PROJ_FACE_WINDING_CW
static bool check_seam(const ProjPaintState *ps, const int orig_face, const int orig_i1_fidx, const int orig_i2_fidx, int *other_face, int *orig_fidx)
static int float_z_sort(const void *p1, const void *p2)
static void scale_tri(float insetCos[3][3], const float *origCos[3], const float inset)
static void proj_paint_state_cavity_init(ProjPaintState *ps)
static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, ImagePaintPartialRedraw *pr_other, int tot)
void paint_proj_stroke(const bContext *C, void *ps_handle_p, const float prev_pos[2], const float pos[2], const bool eraser, float pressure, float distance, float size)
static int project_paint_occlude_ptv(const float pt[3], const float v1[4], const float v2[4], const float v3[4], float w[3], const bool is_ortho)
static void project_paint_end(ProjPaintState *ps)
static void get_default_texture_layer_name_for_object(Object *ob, int texture_type, char *dst, int dst_maxncpy)
void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final)
static void project_face_winding_init(const ProjPaintState *ps, const int tri_index)
static int line_isect_y(const float p1[2], const float p2[2], const float y_level, float *x_isect)
static void default_paint_slot_color_get(int layer_type, Material *ma, float color[4])
static void partial_redraw_single_init(ImagePaintPartialRedraw *pr)
static void insert_seam_vert_array(const ProjPaintState *ps, MemArena *arena, const int tri_index, const int fidx1, const int ibuf_x, const int ibuf_y)
static float project_paint_uvpixel_mask(const ProjPaintState *ps, const int tri_index, const float w[3])
static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *softenArena, LinkNode **softenPixels)
static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2])
static void project_paint_prepare_all_faces(ProjPaintState *ps, MemArena *arena, const ProjPaintFaceLookup *face_lookup, ProjPaintLayerClone *layer_clone, const float(*mloopuv_base)[2], const bool is_multi_view)
static const EnumPropertyItem layer_type_items[]
static wmOperatorStatus texture_paint_image_from_view_exec(bContext *C, wmOperator *op)
#define PROJ_PIXEL_TOLERANCE
static void project_face_pixel(const float *tri_uv[3], ImBuf *ibuf_other, const float w[3], uchar rgba_ub[4], float rgba_f[4])
static void do_projectpaint_mask_f(ProjPaintState *ps, ProjPixel *projPixel, float mask)
static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2])
static void proj_paint_state_seam_bleed_init(ProjPaintState *ps)
#define ISECT_3
static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float mask)
#define PROJ_VIEW_DATA_ID
static float len_squared_v2v2_alt(const float v1[2], const float v2_1, const float v2_2)
#define PROJ_PAINT_STATE_SHARED_CLEAR(ps)
#define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src)
static wmOperatorStatus texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op)
void PAINT_OT_project_image(wmOperatorType *ot)
static bool project_paint_op(void *state, const float lastpos[2], const float pos[2])
static float screen_px_line_point_factor_v2_persp(const ProjPaintState *ps, const float p[2], const float v1[3], const float v2[3])
static bool project_bucket_face_isect(ProjPaintState *ps, int bucket_x, int bucket_y, const int3 &tri)
static TexPaintSlot * project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index)
static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2])
static bool proj_paint_add_slot(bContext *C, wmOperator *op)
static void copy_original_alpha_channel(ProjPixel *pixel, bool is_floatbuf)
static float VecZDepthOrtho(const float pt[2], const float v1[3], const float v2[3], const float v3[3], float w[3])
#define PROJ_BOUNDBOX_SQUARED
void paint_proj_stroke_done(void *ps_handle_p)
#define ISECT_1
static bool IsectPoly2Df(const float pt[2], const float uv[][2], const int tot)
static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask, float dither, int u, int v)
static void project_face_seams_init(const ProjPaintState *ps, MemArena *arena, const int tri_index, const uint vert_index, bool init_all, const int ibuf_x, const int ibuf_y)
#define PROJ_FACE_SEAM_INIT0
static bool line_clip_rect2f(const rctf *cliprect, const rctf *rect, const float l1[2], const float l2[2], float l1_clip[2], float l2_clip[2])
#define PROJ_BUCKET_INIT
static wmOperatorStatus texture_paint_camera_project_exec(bContext *C, wmOperator *op)
#define ISECT_2
static float VecZDepthPersp(const float pt[2], const float v1[4], const float v2[4], const float v3[4], float w[3])
static void proj_paint_state_vert_flags_init(ProjPaintState *ps)
static TexPaintSlot * project_paint_face_clone_slot(const ProjPaintState *ps, int tri_index)
void ED_paint_data_warning(ReportList *reports, bool has_uvs, bool has_mat, bool has_tex, bool has_stencil)
#define PROJ_FACE_DEGENERATE
static bool pixel_bounds_array(float(*uv)[2], const int ibuf_x, const int ibuf_y, int tot, rcti *r_bounds_px)
static VertSeam * find_adjacent_seam(const ProjPaintState *ps, uint loop_index, uint vert_index, VertSeam **r_seam)
static int project_paint_occlude_ptv_clip(const float pt[3], const float v1[4], const float v2[4], const float v3[4], const float v1_3d[3], const float v2_3d[3], const float v3_3d[3], float w[3], const bool is_ortho, RegionView3D *rv3d)
static Material * get_or_create_current_material(bContext *C, Object *ob)
static float compute_seam_normal(VertSeam *seam, VertSeam *adj, float r_no[2])
static bool IsectPoly2Df_twoside(const float pt[2], const float uv[][2], const int tot)
static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_threads)
#define PROJ_SRC_VIEW
#define ISECT_ALL3
static Material * tex_get_material(const ProjPaintState *ps, int face_i)
static void screen_px_to_vector_persp(int winx, int winy, const float projmat_inv[4][4], const float view_pos[3], const float co_px[2], float r_dir[3])
static void project_bucket_clip_face(const bool is_ortho, const bool is_flip_object, const rctf *cliprect, const rctf *bucket_bounds, const float *v1coSS, const float *v2coSS, const float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[8][2], int *tot, bool cull)
static void project_paint_delayed_face_init(ProjPaintState *ps, const int3 &corner_tri, const int tri_index)
static void texture_paint_add_texture_paint_slot_ui(bContext *C, wmOperator *op)
static void proj_paint_state_viewport_init(ProjPaintState *ps, const char symmetry_flag)
static void project_paint_face_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int tri_index, const int image_index, const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf)
bool ED_paint_proj_mesh_data_check(Scene &scene, Object &ob, bool *r_has_uvs, bool *r_has_mat, bool *r_has_tex, bool *r_has_stencil)
static bool project_paint_flt_max_cull(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS)
#define PROJ_FACE_SCALE_SEAM
void PAINT_OT_image_from_view(wmOperatorType *ot)
static void uv_image_outset(const ProjPaintState *ps, float(*orig_uv)[2], float(*puv)[2], uint tri_index, const int ibuf_x, const int ibuf_y)
static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int mode)
static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float mask)
static Image * project_paint_face_paint_image(const ProjPaintState *ps, int tri_index)
#define PROJ_FACE_SEAM_INIT1
static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *smearArena, LinkNode **smearPixels_f, const float co[2])
static void rect_to_uvspace_persp(const rctf *bucket_bounds, const float *v1coSS, const float *v2coSS, const float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[4][2], const int flip)
BrushStrokeMode
@ BRUSH_STROKE_SMOOTH
@ BRUSH_STROKE_NORMAL
@ BRUSH_STROKE_INVERT
return ret
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
int RNA_enum_from_value(const EnumPropertyItem *item, const int value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const EnumPropertyItem rna_enum_color_attribute_domain_items[]
const EnumPropertyItem rna_enum_color_attribute_type_items[]
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_float_color(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_string_file_name(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
void RNA_def_property_float_array_default(PropertyRNA *prop, const float *array)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_image_generated_type_items[]
Definition rna_image.cc:24
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:26
#define min(a, b)
Definition sort.cc:36
#define FLT_MAX
Definition stdcycles.h:14
void * regiondata
float * wdata
struct ColorBand * gradient
struct MTex mtex
float sharp_threshold
char image_brush_type
char gradient_fill_mode
short blend
struct MTex mask_mtex
float weight
int len
Definition DNA_ID.h:165
char subtype
Definition DNA_ID.h:151
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
char filepath[IMB_FILEPATH_SIZE]
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
struct Image * stencil
struct Image * canvas
short source
void * link
struct LinkNode * next
void * first
float seam_puvs[2][2]
float seam_uvs[2][2]
float corner_dist_sq[2]
char brush_map_mode
struct Tex * tex
ListBase images
Definition BKE_main.hh:253
struct bNodeTree * nodetree
short paint_active_slot
struct TexPaintSlot * texpaintslot
int corners_num
char symmetry
CustomData corner_data
CustomData face_data
char * default_color_attribute
int faces_num
int verts_num
short transflag
struct CurveMapping * cavity_curve
PrepareImageEntry * prev
PrepareImageEntry * next
ImagePaintPartialRedraw * partRedrawRect
volatile void ** undoRect
const TexPaintSlot * slot_last_clone
const float(* mloopuv_clone_base)[2]
const TexPaintSlot * slot_clone
float projectMat[4][4]
Material ** mat_array
Depsgraph * depsgraph
const int * material_indices
LoopSeamData * loopSeamData
blender::Span< int > corner_verts_eval
blender::Span< blender::int2 > edges_eval
float paint_color_linear[3]
const float(** poly_to_loop_uv)[2]
BrushStrokeMode mode
LinkNode ** bucketFaces
const bool * select_poly_eval
LinkNode ** bucketRect
blender::Span< blender::float3 > vert_normals
float projectMatInv[4][4]
blender::Span< int3 > corner_tris_eval
RegionView3D * rv3d
const Paint * paint
blender::Span< int > corner_tri_faces_eval
const float(* mloopuv_stencil_eval)[2]
LinkNode ** vertFaces
CurveMapping * cavity_curve
ProjPaintImage * projImages
blender::OffsetIndices< int > faces_eval
float obmat_imat[4][4]
blender::Span< blender::float3 > vert_positions_eval
BlurKernel * blurkernel
const bool * hide_poly_eval
MemArena * arena_mt[BLENDER_MAX_THREADS]
const bool * sharp_faces_eval
float(* screenCoords)[4]
const float(** poly_to_loop_uv_clone)[2]
PixelStore newColor
float projCoSS[2]
PixelPointer pixel
PixelPointer origColor
float worldCoSS[3]
ushort * mask_accum
std::optional< blender::float3 > initial_hsv_jitter
ProjPaintState * ps_views[8]
ProjPaintState * ps
ProjPaintImage * projImages
float viewmat[4][4]
float viewinv[4][4]
float winmat[4][4]
struct ToolSettings * toolsettings
View3DCursor cursor
struct Object * camera
ListBase spacedata
struct Image * ima
SpinLock * lock
ImBuf ** tmpibuf
ProjPaintImage * pjima
struct ImagePaintSettings imapaint
struct UnifiedPaintSettings unified_paint_settings
VertSeam * next
VertSeam * prev
float texture_paint_mode_opacity
View3DOverlay overlay
View3DShading shading
struct bNodeLink * link
void * default_value
struct ID * id
void * storage
const c_style_mat & ptr() const
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
uiLayout & column(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
const char * name
Definition WM_types.hh:1030
struct ReportList * reports
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
ParamHandle ** handles
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4226
wmOperatorStatus WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)
wmOperatorStatus WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:139