3D mapping for projective texture painting (only for draw brush). Useful
authorAntony Riakiotakis <kalast@gmail.com>
Sun, 20 Jan 2013 21:32:14 +0000 (21:32 +0000)
committerAntony Riakiotakis <kalast@gmail.com>
Sun, 20 Jan 2013 21:32:14 +0000 (21:32 +0000)
to draw with procedural textures on surface of object. 2D painting will
still paint as if tiled.

When we unify the paint systems, the texture sampling functions will
need to be changed. Sculpt uses a slightly different system that passes
both screen and 3d coordinates to the sampling function. This commit
however is not too disrupting for that however so it can go in now.

source/blender/blenkernel/BKE_brush.h
source/blender/blenkernel/intern/brush.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/makesrna/intern/rna_brush.c

index 248fe9c89683a2871ae5fa509c74913a2803bcc3..b19a9c4d4ebeeb121c34595ff48b738c53ac82fc 100644 (file)
@@ -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);
 
index e8275471b0917a3fe061476342f792b35f02b605..bb4f073a3d796228e18d8883937da85efe2feb91 100644 (file)
@@ -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];
index b9e5a082ff89704ff79b716213474d97cdc8c30e..40311658ed47d724aa0f713f4dd82b78c0ef11da 100644 (file)
@@ -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;
 
 
index 9965727ed792a484894b1db967d46af0d566bccf..c8cd38045bd515af349cd70acd05568637d4841a 100644 (file)
@@ -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];
 
index f17ee48a87ca09f2de572a4811bb9b0bd103fa4b..4fb26f2b007eeeb2e0e7b01e10db6042e782e646 100644 (file)
@@ -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}
        };