diff options
-rw-r--r-- | source/blender/blenkernel/BKE_brush.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 55 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.c | 45 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image_2d.c | 9 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 1 |
5 files changed, 85 insertions, 28 deletions
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 248fe9c8968..b19a9c4d4eb 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -67,7 +67,8 @@ float BKE_brush_curve_strength_clamp(struct Brush *br, float p, const float len) float BKE_brush_curve_strength(struct Brush *br, float p, const float len); /* used for sculpt */ /* sampling */ -void BKE_brush_sample_tex(const struct Scene *scene, struct Brush *brush, const float xy[2], float rgba[4], const int thread); +void BKE_brush_sample_tex(const struct Scene *scene, struct Brush *brush, const float sampleco[3], float rgba[4], const int thread); +void BKE_brush_sample_tex_2D(const struct Scene *scene, struct Brush *brush, const float xy[2], float rgba[4], const int thread); void BKE_brush_imbuf_new(const struct Scene *scene, struct Brush *brush, short flt, short texfalloff, int size, struct ImBuf **imbuf, int use_color_correction); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index e8275471b09..bb4f073a3d7 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -471,8 +471,46 @@ int BKE_brush_clone_image_delete(Brush *brush) return 0; } -/* Brush Sampling */ -void BKE_brush_sample_tex(const Scene *scene, Brush *brush, const float xy[2], float rgba[4], const int thread) +/* Brush Sampling for 3d brushes. Currently used for texture painting only, but should be generalized */ +void BKE_brush_sample_tex(const Scene *scene, Brush *brush, const float sampleco[3], float rgba[4], const int thread) +{ + MTex *mtex = &brush->mtex; + + if (mtex && mtex->tex) { + float co[3], tin, tr, tg, tb, ta; + int hasrgb; + const int radius = BKE_brush_size_get(scene, brush); + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { + copy_v3_v3(co, sampleco); + } else { + co[0] = sampleco[0] / radius; + co[1] = sampleco[1] / radius; + co[2] = 0.0f; + } + + hasrgb = externtex(mtex, co, &tin, &tr, &tg, &tb, &ta, thread); + + if (hasrgb) { + rgba[0] = tr; + rgba[1] = tg; + rgba[2] = tb; + rgba[3] = ta; + } + else { + rgba[0] = tin; + rgba[1] = tin; + rgba[2] = tin; + rgba[3] = 1.0f; + } + } + else { + rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0f; + } +} + +/* Brush Sampling for 2D brushes. when we unify the brush systems this will be necessarily a separate function */ +void BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2], float rgba[4], const int thread) { MTex *mtex = &brush->mtex; @@ -505,7 +543,8 @@ void BKE_brush_sample_tex(const Scene *scene, Brush *brush, const float xy[2], f } } -/* TODO, use define for 'texfall' arg */ +/* TODO, use define for 'texfall' arg + * NOTE: only used for 2d brushes currently! */ void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction) { ImBuf *ibuf; @@ -544,10 +583,10 @@ void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texf dstf[3] = alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); } else if (texfall == 1) { - BKE_brush_sample_tex(scene, brush, xy, dstf, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, dstf, 0); } else { - BKE_brush_sample_tex(scene, brush, xy, rgba, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, rgba, 0); mul_v3_v3v3(dstf, rgba, brush_rgb); dstf[3] = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); } @@ -574,11 +613,11 @@ void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texf dst[3] = FTOCHAR(alpha_f); } else if (texfall == 1) { - BKE_brush_sample_tex(scene, brush, xy, rgba, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, rgba, 0); rgba_float_to_uchar(dst, rgba); } else if (texfall == 2) { - BKE_brush_sample_tex(scene, brush, xy, rgba, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, rgba, 0); mul_v3_v3(rgba, brush->rgb); alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); @@ -587,7 +626,7 @@ void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texf dst[3] = FTOCHAR(alpha_f); } else { - BKE_brush_sample_tex(scene, brush, xy, rgba, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, rgba, 0); alpha_f = rgba[3] * alpha * BKE_brush_curve_strength_clamp(brush, len_v2(xy), radius); dst[0] = crgb[0]; diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index b9e5a082ff8..40311658ed4 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -355,7 +355,7 @@ typedef union pixelStore { typedef struct ProjPixel { float projCoSS[2]; /* the floating point screen projection of this pixel */ - + float worldCoSS[3]; /* Only used when the airbrush is disabled. * Store the max mask value to avoid painting over an area with a lower opacity * with an advantage that we can avoid touching the pixel at all, if the @@ -1526,6 +1526,11 @@ static int project_paint_pixel_sizeof(const short tool) } } +static bool project_paint_supports_3d_mapping(Brush *brush) +{ + return (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) && (brush->imagepaint_tool == PAINT_TOOL_DRAW); +} + /* run this function when we know a bucket's, face's pixel can be initialized, * return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */ static ProjPixel *project_paint_uvpixel_init( @@ -1537,6 +1542,7 @@ static ProjPixel *project_paint_uvpixel_init( const int face_index, const int image_index, const float pixelScreenCo[4], + const float world_spaceCo[3], const int side, const float w[3]) { @@ -1565,6 +1571,9 @@ static ProjPixel *project_paint_uvpixel_init( } /* screenspace unclamped, we could keep its z and w values but don't need them at the moment */ + if(project_paint_supports_3d_mapping(ps->brush)) + copy_v3_v3(projPixel->worldCoSS, world_spaceCo); + copy_v2_v2(projPixel->projCoSS, pixelScreenCo); projPixel->x_px = x_px; @@ -2374,6 +2383,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i float *uv1co, *uv2co, *uv3co; /* for convenience only, these will be assigned to tf->uv[0],1,2 or tf->uv[0],2,3 */ float pixelScreenCo[4]; + bool do_3d_mapping = project_paint_supports_3d_mapping(ps->brush); rcti bounds_px; /* ispace bounds */ /* vars for getting uvspace bounds */ @@ -2449,7 +2459,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i v1coSS = ps->screenCoords[(*(&mf->v1 + i1))]; v2coSS = ps->screenCoords[(*(&mf->v1 + i2))]; v3coSS = ps->screenCoords[(*(&mf->v1 + i3))]; - + /* This funtion gives is a concave polyline in UV space from the clipped quad and tri*/ project_bucket_clip_face( is_ortho, bucket_bounds, @@ -2501,9 +2511,9 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i else screen_px_from_persp(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); /* a pity we need to get the worldspace pixel location here */ - if (do_clip) { + if (do_clip || do_3d_mapping) { interp_v3_v3v3v3(wco, ps->dm_mvert[(*(&mf->v1 + i1))].co, ps->dm_mvert[(*(&mf->v1 + i2))].co, ps->dm_mvert[(*(&mf->v1 + i3))].co, w); - if (ED_view3d_clipping_test(ps->rv3d, wco, TRUE)) { + if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, TRUE)) { continue; /* Watch out that no code below this needs to run */ } } @@ -2520,9 +2530,10 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (mask > 0.0f) { BLI_linklist_prepend_arena( bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, side, w), - arena - ); + project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, + image_index, pixelScreenCo, wco, side, w), + arena + ); } } @@ -2725,11 +2736,11 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i } /* a pity we need to get the worldspace pixel location here */ - if (do_clip) { + if (do_clip || do_3d_mapping) { if (side) interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v3].co, ps->dm_mvert[mf->v4].co, w); else interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v2].co, ps->dm_mvert[mf->v3].co, w); - if (ED_view3d_clipping_test(ps->rv3d, wco, TRUE)) { + if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, TRUE)) { continue; /* Watch out that no code below this needs to run */ } } @@ -2739,7 +2750,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (mask > 0.0f) { BLI_linklist_prepend_arena( bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, side, w), + project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, wco, side, w), arena ); } @@ -4105,7 +4116,7 @@ static void *do_projectpaint_thread(void *ph_v) /*if (dist < radius) {*/ /* correct but uses a sqrtf */ if (dist_nosqrt <= radius_squared) { - float samplecos[2]; + float samplecos[3]; dist = sqrtf(dist_nosqrt); falloff = BKE_brush_curve_strength_clamp(ps->brush, dist, radius); @@ -4114,9 +4125,11 @@ static void *do_projectpaint_thread(void *ph_v) if (ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { sub_v2_v2v2(samplecos, projPixel->projCoSS, pos); } - else { - copy_v2_v2(samplecos, projPixel->projCoSS); - } + /* taking 3d copy to account for 3D mapping too. It gets concatenated during sampling */ + else if (project_paint_supports_3d_mapping(ps->brush)) + copy_v3_v3(samplecos, projPixel->worldCoSS); + else + copy_v3_v3(samplecos, projPixel->projCoSS); } if (falloff > 0.0f) { @@ -5008,7 +5021,9 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps) ps->pixel_sizeof = project_paint_pixel_sizeof(ps->tool); BLI_assert(ps->pixel_sizeof >= sizeof(ProjPixel)); - ps->do_masking = (brush->flag & BRUSH_AIRBRUSH || brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) ? false : true; + /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ + ps->do_masking = (brush->flag & BRUSH_AIRBRUSH || brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW || + project_paint_supports_3d_mapping(brush)) ? false : true; ps->is_texbrush = (brush->mtex.tex) ? 1 : 0; diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 9965727ed79..c8cd38045bd 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -205,7 +205,7 @@ static void brush_painter_do_partial(BrushPainter *painter, ImBuf *oldtexibuf, xy[0] = x + xoff; xy[1] = y + yoff; - BKE_brush_sample_tex(scene, brush, xy, tf, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, tf, 0); } bf[0] = tf[0] * mf[0]; @@ -236,7 +236,7 @@ static void brush_painter_do_partial(BrushPainter *painter, ImBuf *oldtexibuf, xy[0] = x + xoff; xy[1] = y + yoff; - BKE_brush_sample_tex(scene, brush, xy, rgba, 0); + BKE_brush_sample_tex_2D(scene, brush, xy, rgba, 0); rgba_float_to_uchar(t, rgba); } @@ -314,6 +314,7 @@ static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2 short flt; const int diameter = 2 * BKE_brush_size_get(scene, brush); const float alpha = BKE_brush_alpha_get(scene, brush); + const bool do_tiled = ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_3D); if (diameter != cache->lastsize || alpha != cache->lastalpha || @@ -331,7 +332,7 @@ static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2 flt = cache->flt; size = (cache->size) ? cache->size : diameter; - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) { + if (do_tiled) { BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf, use_color_correction); brush_painter_fixed_tex_partial_update(painter, pos); } @@ -342,7 +343,7 @@ static void brush_painter_refresh_cache(BrushPainter *painter, const float pos[2 cache->lastalpha = alpha; cache->lastjitter = brush->jitter; } - else if ((brush->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) && mtex && mtex->tex) { + else if (do_tiled && mtex && mtex->tex) { int dx = (int)painter->lastpaintpos[0] - (int)pos[0]; int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f17ee48a87c..4fb26f2b007 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -432,6 +432,7 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna) static EnumPropertyItem prop_tex_paint_map_mode_items[] = { {MTEX_MAP_MODE_VIEW, "VIEW_PLANE", 0, "View Plane", ""}, {MTEX_MAP_MODE_TILED, "TILED", 0, "Tiled", ""}, + {MTEX_MAP_MODE_3D, "3D", 0, "3D", ""}, {0, NULL, 0, NULL, NULL} }; |