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