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