Bake from multires mesh
authorSergey Sharybin <sergey.vfx@gmail.com>
Sun, 5 Jun 2011 20:54:04 +0000 (20:54 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Sun, 5 Jun 2011 20:54:04 +0000 (20:54 +0000)
=======================

Added option to baked named "Bake From Multires" which is avaliable for
normals baking and displacement baking.

If this option is enabled, then no additional hi-res meshes and render
structures would be created . This saves plenty of memory and meshes
with millions of faces could be successfully baked in few minutes.

Baking happens from highest level against viewport subdivision level,
so workflow is following:
  - Set viewport level to level at which texture would be applied
    during final rendering.
  - Choose Displacement/Normals baking.
  - Enable "Bake From Multires" option.
  - You're ready to bake.

Displacement baker had aditional option named "Low Resolution Mesh".
This option is used to set if you want texture for realtime (games)
usage.

Internally it does the following:
  - If it's disabled, displacement is calculated from subdivided
    viewport level, so texture looks "smooth" (it's how default
    baked works).
  - If it's enabled, dispalcement is calculated against unsubdivided
    viewport levels. This leads to "scales". This isn;t useful for
    offline renders much, but very useful for creating game textures.

Special thanks to Morten Mikkelsen (aka sparky) for all mathematics
and other work he've done fr this patch!

12 files changed:
release/scripts/startup/bl_ui/properties_render.py
source/blender/blenkernel/BKE_multires.h
source/blender/blenkernel/intern/customdata.c
source/blender/blenkernel/intern/multires.c
source/blender/blenlib/BLI_math_geom.h
source/blender/blenlib/intern/math_geom.c
source/blender/editors/object/object_bake.c
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/filter.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_scene.c
source/blender/render/intern/source/rendercore.c

index a3b10702fa71f78bc469a9aa18f70849476c3dae..c313942bea05acdafd98329ca3845856277e7039 100644 (file)
@@ -613,29 +613,41 @@ class RENDER_PT_bake(RenderButtonsPanel, bpy.types.Panel):
 
         layout.prop(rd, "bake_type")
 
-        if rd.bake_type == 'NORMALS':
-            layout.prop(rd, "bake_normal_space")
-        elif rd.bake_type in {'DISPLACEMENT', 'AO'}:
-            layout.prop(rd, "use_bake_normalize")
+        multires_bake = False
+        if rd.bake_type in ['NORMALS', 'DISPLACEMENT']:
+            layout.prop(rd, 'use_bake_multires')
+            multires_bake = rd.use_bake_multires
 
-        # col.prop(rd, "bake_aa_mode")
-        # col.prop(rd, "use_bake_antialiasing")
+        if not multires_bake:
+            if rd.bake_type == 'NORMALS':
+                layout.prop(rd, "bake_normal_space")
+            elif rd.bake_type in {'DISPLACEMENT', 'AO'}:
+                layout.prop(rd, "use_bake_normalize")
 
-        layout.separator()
+            # col.prop(rd, "bake_aa_mode")
+            # col.prop(rd, "use_bake_antialiasing")
 
-        split = layout.split()
+            layout.separator()
 
-        col = split.column()
-        col.prop(rd, "use_bake_clear")
-        col.prop(rd, "bake_margin")
-        col.prop(rd, "bake_quad_split", text="Split")
+            split = layout.split()
 
-        col = split.column()
-        col.prop(rd, "use_bake_selected_to_active")
-        sub = col.column()
-        sub.active = rd.use_bake_selected_to_active
-        sub.prop(rd, "bake_distance")
-        sub.prop(rd, "bake_bias")
+            col = split.column()
+            col.prop(rd, "use_bake_clear")
+            col.prop(rd, "bake_margin")
+            col.prop(rd, "bake_quad_split", text="Split")
+
+            col = split.column()
+            col.prop(rd, "use_bake_selected_to_active")
+            sub = col.column()
+            sub.active = rd.use_bake_selected_to_active
+            sub.prop(rd, "bake_distance")
+            sub.prop(rd, "bake_bias")
+        else:
+            if rd.bake_type == 'DISPLACEMENT':
+                layout.prop(rd, "use_bake_lores_mesh")
+
+            layout.prop(rd, "use_bake_clear")
+            layout.prop(rd, "bake_margin")
 
 if __name__ == "__main__":  # only for live edit.
     bpy.utils.register_module(__name__)
index ea34ff4aa0789050e4f1b15aad4752ea7fd182a6..ba23b2d79c084d7e54f230d14b8cc7fc7dbf6179 100644 (file)
@@ -91,6 +91,7 @@ void multires_topology_changed(struct Scene *scene, struct Object *ob);
 void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, float v);
 void mdisp_rot_crn_to_face(const int S, const int corners, const int face_side, const float x, const float y, float *u, float *v);
 int mdisp_rot_face_to_crn(const int corners, const int face_side, const float u, const float v, float *x, float *y);
+int mdisp_rot_face_to_quad_crn(const int corners, const int face_side, const float u, const float v, float *x, float *y);
 void mdisp_apply_weight(const int S, const int corners, int x, int y, const int face_side, float crn_weight[4][2], float *u_r, float *v_r);
 void mdisp_flip_disp(const int S, const int corners, const float axis_x[2], const float axis_y[2], float disp[3]);
 void mdisp_join_tris(struct MDisps *dst, struct MDisps *tri1, struct MDisps *tri2);
index f5f069767eb4666c0fca282dcbc5b47166e4371f..45faba8439c728e72e16643cadf511d771edbb9f 100644 (file)
@@ -540,7 +540,7 @@ static void layerInterp_mdisps(void **sources, float *UNUSED(weights),
                                float face_u, face_v, crn_u, crn_v;
 
                                mdisp_apply_weight(S, dst_corners, x, y, st, crn_weight, &face_u, &face_v);
-                               crn = mdisp_rot_face_to_crn(src_corners, st, face_u, face_v, &crn_u, &crn_v);
+                               crn = mdisp_rot_face_to_quad_crn(src_corners, st, face_u, face_v, &crn_u, &crn_v);
 
                                old_mdisps_bilinear((*out), &s->disps[crn*side*side], side, crn_u, crn_v);
                                mdisp_flip_disp(crn, dst_corners, axis_x, axis_y, *out);
index 13ab89200db1deac766e1b95fa3e94bfa3df1d64..5802bb2b697ef06977ce10340fa44b0dbb99068d 100644 (file)
@@ -1997,11 +1997,67 @@ void mdisp_rot_crn_to_face(const int S, const int corners, const int face_side,
        }
 }
 
+/* Find per-corner coordinate with given per-face UV coord */
 int mdisp_rot_face_to_crn(const int corners, const int face_side, const float u, const float v, float *x, float *y)
 {
        const float offset = face_side*0.5f - 0.5f;
        int S = 0;
 
+       if (corners == 4) {
+               if(u <= offset && v <= offset) S = 0;
+               else if(u > offset  && v <= offset) S = 1;
+               else if(u > offset  && v > offset) S = 2;
+               else if(u <= offset && v >= offset)  S = 3;
+
+               if(S == 0) {
+                       *y = offset - u;
+                       *x = offset - v;
+               } else if(S == 1) {
+                       *x = u - offset;
+                       *y = offset - v;
+               } else if(S == 2) {
+                       *y = u - offset;
+                       *x = v - offset;
+               } else if(S == 3) {
+                       *x= offset - u;
+                       *y = v - offset;
+               }
+       } else {
+               int grid_size = offset;
+               float w = (face_side - 1) - u - v;
+               float W1, W2;
+
+               if (u >= v && u >= w) {S = 0; W1= w; W2= v;}
+               else if (v >= u && v >= w) {S = 1; W1 = u; W2 = w;}
+               else {S = 2; W1 = v; W2 = u;}
+
+               W1 /= (face_side-1);
+               W2 /= (face_side-1);
+
+               *x = (1-(2*W1)/(1-W2)) * grid_size;
+               *y = (1-(2*W2)/(1-W1)) * grid_size;
+       }
+
+       return S;
+}
+
+/* Find per-corner coordinate with given per-face UV coord
+   Practically as the previous funciton but it assumes a bit different coordinate system for triangles
+   which is optimized for MDISP layer interpolation:
+
+   v
+   ^
+   |      /|
+   |    /  |
+   |  /    |
+   |/______|___> u
+
+ */
+int mdisp_rot_face_to_quad_crn(const int corners, const int face_side, const float u, const float v, float *x, float *y)
+{
+       const float offset = face_side*0.5f - 0.5f;
+       int S = 0;
+
        if (corners == 4) {
                if(u <= offset && v <= offset) S = 0;
                else if(u > offset  && v <= offset) S = 1;
@@ -2148,7 +2204,7 @@ void mdisp_join_tris(MDisps *dst, MDisps *tri1, MDisps *tri2)
                                        face_v = st - 1 - face_v;
                                } else src = tri1;
 
-                               crn = mdisp_rot_face_to_crn(3, st, face_u, face_v, &crn_u, &crn_v);
+                               crn = mdisp_rot_face_to_quad_crn(3, st, face_u, face_v, &crn_u, &crn_v);
 
                                old_mdisps_bilinear((*out), &src->disps[crn*side*side], side, crn_u, crn_v);
                                (*out)[0] = 0;
index 634634f02e5e366125fee10d5d370260eece1464..b34b9c4b70f27d99ba887e21ecb689a206da421e 100644 (file)
@@ -162,6 +162,9 @@ void barycentric_transform(float pt_tar[3], float const pt_src[3],
 void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2],
        const float co[2], float w[3]);
 
+void resolve_tri_uv(float uv[2], const float st[2], const float st0[2], const float st1[2], const float st2[2]);
+void resolve_quad_uv(float uv[2], const float st[2], const float st0[2], const float st1[2], const float st2[2], const float st3[2]);
+
 /***************************** View & Projection *****************************/
 
 void lookat_m4(float mat[4][4], float vx, float vy, 
index 5979a24c807af73649f1ae54283b0e876a829311..96ed788a49fa8ab363df59403ae774061ae20291 100644 (file)
@@ -1809,6 +1809,80 @@ void interp_cubic_v3(float x[3], float v[3], const float x1[3], const float v1[3
        v[2]= 3*a[2]*t2 + 2*b[2]*t + v1[2];
 }
 
+/* unfortunately internal calculations have to be done at double precision to achieve correct/stable results. */
+
+#define IS_ZERO(x) ((x>(-DBL_EPSILON) && x<DBL_EPSILON) ? 1 : 0)
+
+/* Barycentric reverse  */
+void resolve_tri_uv(float uv[2], const float st[2], const float st0[2], const float st1[2], const float st2[2])
+{
+       /* find UV such that
+          t= u*t0 + v*t1 + (1-u-v)*t2
+          u*(t0-t2) + v*(t1-t2)= t-t2 */
+       const double a= st0[0]-st2[0], b= st1[0]-st2[0];
+       const double c= st0[1]-st2[1], d= st1[1]-st2[1];
+       const double det= a*d - c*b;
+
+       if(IS_ZERO(det)==0)     {  /* det should never be zero since the determinant is the signed ST area of the triangle. */
+               const double x[]= {st[0]-st2[0], st[1]-st2[1]};
+
+               uv[0]= (float)((d*x[0] - b*x[1])/det);
+               uv[1]= (float)(((-c)*x[0] + a*x[1])/det);
+       } else zero_v2(uv);
+}
+
+/* bilinear reverse */
+void resolve_quad_uv(float uv[2], const float st[2], const float st0[2], const float st1[2], const float st2[2], const float st3[2])
+{
+       const double signed_area= (st0[0]*st1[1] - st0[1]*st1[0]) + (st1[0]*st2[1] - st1[1]*st2[0]) +
+                              (st2[0]*st3[1] - st2[1]*st3[0]) + (st3[0]*st0[1] - st3[1]*st0[0]);
+
+       /* X is 2D cross product (determinant)
+          A= (p0-p) X (p0-p3)*/
+       const double a= (st0[0]-st[0])*(st0[1]-st3[1]) - (st0[1]-st[1])*(st0[0]-st3[0]);
+
+       /* B= ( (p0-p) X (p1-p2) + (p1-p) X (p0-p3) ) / 2 */
+       const double b= 0.5 * ( ((st0[0]-st[0])*(st1[1]-st2[1]) - (st0[1]-st[1])*(st1[0]-st2[0])) +
+                                                        ((st1[0]-st[0])*(st0[1]-st3[1]) - (st1[1]-st[1])*(st0[0]-st3[0])) );
+
+       /* C = (p1-p) X (p1-p2) */
+       const double fC= (st1[0]-st[0])*(st1[1]-st2[1]) - (st1[1]-st[1])*(st1[0]-st2[0]);
+       const double denom= a - 2*b + fC;
+
+       // clear outputs
+       zero_v2(uv);
+
+       if(IS_ZERO(denom)!=0) {
+               const double fDen= a-fC;
+               if(IS_ZERO(fDen)==0)
+                       uv[0]= (float)(a / fDen);
+       } else {
+               const double desc_sq= b*b - a*fC;
+               const double desc= sqrt(desc_sq<0.0?0.0:desc_sq);
+               const double s= signed_area>0 ? (-1.0) : 1.0;
+
+               uv[0]= (float)(( (a-b) + s * desc ) / denom);
+       }
+
+       /* find UV such that
+         fST = (1-u)(1-v)*ST0 + u*(1-v)*ST1 + u*v*ST2 + (1-u)*v*ST3 */
+       {
+               const double denom_s= (1-uv[0])*(st0[0]-st3[0]) + uv[0]*(st1[0]-st2[0]);
+               const double denom_t= (1-uv[0])*(st0[1]-st3[1]) + uv[0]*(st1[1]-st2[1]);
+               int i= 0; double denom= denom_s;
+
+               if(fabs(denom_s)<fabs(denom_t)) {
+                       i= 1;
+                       denom=denom_t;
+               }
+
+               if(IS_ZERO(denom)==0)
+                       uv[1]= (float) (( (1-uv[0])*(st0[i]-st[i]) + uv[0]*(st1[i]-st[i]) ) / denom);
+       }
+}
+
+#undef IS_ZERO
+
 /***************************** View & Projection *****************************/
 
 void orthographic_m4(float matrix[][4], const float left, const float right, const float bottom, const float top, const float nearClip, const float farClip)
index b513bab3924f4edac875f9f2635201c014d477c0..af8159889353004b587db0c200956a66b6dd2bac 100644 (file)
@@ -22,7 +22,8 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Morten Mikkelsen,
+ *                 Sergey Sharybin
  *
  * ***** END GPL LICENSE BLOCK *****
  */
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
 #include "DNA_world_types.h"
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
 
 #include "BLI_blenlib.h"
 #include "BLI_threads.h"
 #include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_math_geom.h"
 
 #include "BKE_blender.h"
 #include "BKE_context.h"
 #include "BKE_main.h"
 #include "BKE_multires.h"
 #include "BKE_report.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_modifier.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_subsurf.h"
 
 #include "RE_pipeline.h"
 #include "RE_shader_ext.h"
 
 #include "object_intern.h"
 
+/* ****************** multires BAKING ********************** */
+
+/* holder of per-object data needed for bake job
+   needed to make job totally thread-safe */
+typedef struct MultiresBakerJobData {
+       struct MultiresBakerJobData *next, *prev;
+       DerivedMesh *lores_dm, *hires_dm;
+       int simple, lvl, tot_lvl;
+} MultiresBakerJobData;
+
+/* data passing to multires-baker job */
+typedef struct {
+       ListBase data;
+       int bake_clear, bake_filter;
+       short mode, use_lores_mesh;
+} MultiresBakeJob;
+
+/* data passing to multires baker */
+typedef struct {
+       DerivedMesh *lores_dm, *hires_dm;
+       int simple, lvl, tot_lvl, bake_filter;
+       short mode, use_lores_mesh;
+
+       int tot_obj, tot_image;
+       ListBase image;
+
+       int baked_objects, baked_faces;
+
+       short *stop;
+       short *do_update;
+       float *progress;
+} MultiresBakeRender;
+
+typedef void (*MPassKnownData)(DerivedMesh *lores_dm, DerivedMesh *hires_dm, const void *bake_data,
+                               const int face_index, const int lvl, const float st[2],
+                               float tangmat[3][3], const int x, const int y);
+
+typedef void* (*MInitBakeData)(MultiresBakeRender *bkr, Image* ima);
+typedef void (*MApplyBakeData)(void *bake_data);
+typedef void (*MFreeBakeData)(void *bake_data);
+
+typedef struct {
+       MVert *mvert;
+       MFace *mface;
+       MTFace *mtface;
+       float *pvtangent;
+       float *precomputed_normals;
+       int w, h;
+       int face_index;
+       int i0, i1, i2;
+       DerivedMesh *lores_dm, *hires_dm;
+       int lvl;
+       void *bake_data;
+       MPassKnownData pass_data;
+} MResolvePixelData;
+
+typedef void (*MFlushPixel)(const MResolvePixelData *data, const int x, const int y);
+
+typedef struct {
+       int w, h;
+       char *texels;
+       const MResolvePixelData *data;
+       MFlushPixel flush_pixel;
+} MBakeRast;
+
+typedef struct {
+       float *heights;
+       float height_min, height_max;
+       Image *ima;
+       DerivedMesh *ssdm;
+} MHeightBakeData;
+
+static void multiresbake_get_normal(const MResolvePixelData *data, float norm[], const int face_num, const int vert_index)
+{
+       unsigned int indices[]= {data->mface[face_num].v1, data->mface[face_num].v2,
+                             data->mface[face_num].v3, data->mface[face_num].v4};
+       const int smoothnormal= (data->mface[face_num].flag & ME_SMOOTH);
+
+       if(!smoothnormal)  { /* flat */
+               if(data->precomputed_normals) {
+                       copy_v3_v3(norm, &data->precomputed_normals[3*face_num]);
+               } else {
+                       float nor[3];
+                       float *p0, *p1, *p2;
+                       const int iGetNrVerts= data->mface[face_num].v4!=0 ? 4 : 3;
+
+                       p0= data->mvert[indices[0]].co;
+                       p1= data->mvert[indices[1]].co;
+                       p2= data->mvert[indices[2]].co;
+
+                       if(iGetNrVerts==4) {
+                               float *p3= data->mvert[indices[3]].co;
+                               normal_quad_v3(nor, p0, p1, p2, p3);
+                       } else {
+                               normal_tri_v3(nor, p0, p1, p2);
+                       }
+
+                       copy_v3_v3(norm, nor);
+               }
+       } else {
+               short *no= data->mvert[indices[vert_index]].no;
+
+               normal_short_to_float_v3(norm, no);
+               normalize_v3(norm);
+       }
+}
+
+static void init_bake_rast(MBakeRast *bake_rast, const ImBuf *ibuf, const MResolvePixelData *data, MFlushPixel flush_pixel)
+{
+       memset(bake_rast, 0, sizeof(MBakeRast));
+
+       bake_rast->texels = ibuf->userdata;
+       bake_rast->w= ibuf->x;
+       bake_rast->h= ibuf->y;
+       bake_rast->data= data;
+       bake_rast->flush_pixel= flush_pixel;
+}
+
+static void flush_pixel(const MResolvePixelData *data, const int x, const int y)
+{
+       float st[2]= {(x+0.5f)/data->w, (y+0.5f)/data->h};
+       float *st0, *st1, *st2;
+       float *tang0, *tang1, *tang2;
+       float no0[3], no1[3], no2[3];
+       float fUV[2], from_tang[3][3], to_tang[3][3];
+       float u, v, w, sign;
+       int r;
+
+       const int i0= data->i0;
+       const int i1= data->i1;
+       const int i2= data->i2;
+
+       st0= data->mtface[data->face_index].uv[i0];
+       st1= data->mtface[data->face_index].uv[i1];
+       st2= data->mtface[data->face_index].uv[i2];
+
+       tang0= data->pvtangent + data->face_index*16 + i0*4;
+       tang1= data->pvtangent + data->face_index*16 + i1*4;
+       tang2= data->pvtangent + data->face_index*16 + i2*4;
+
+       multiresbake_get_normal(data, no0, data->face_index, i0);       /* can optimize these 3 into one call */
+       multiresbake_get_normal(data, no1, data->face_index, i1);
+       multiresbake_get_normal(data, no2, data->face_index, i2);
+
+       resolve_tri_uv(fUV, st, st0, st1, st2);
+
+       u= fUV[0];
+       v= fUV[1];
+       w= 1-u-v;
+
+       /* the sign is the same at all face vertices for any non degenerate face.
+          Just in case we clamp the interpolated value though. */
+       sign= (tang0[3]*u + tang1[3]*v + tang2[3]*w)<0 ? (-1.0f) : 1.0f;
+
+       /* this sequence of math is designed specifically as is with great care
+          to be compatible with our shader. Please don't change without good reason. */
+       for(r= 0; r<3; r++) {
+               from_tang[0][r]= tang0[r]*u + tang1[r]*v + tang2[r]*w;
+               from_tang[2][r]= no0[r]*u + no1[r]*v + no2[r]*w;
+       }
+
+       cross_v3_v3v3(from_tang[1], from_tang[2], from_tang[0]);  /* B = sign * cross(N, T)  */
+       mul_v3_fl(from_tang[1], sign);
+       invert_m3_m3(to_tang, from_tang);
+       /* sequence end */
+
+       data->pass_data(data->lores_dm, data->hires_dm, data->bake_data,
+                       data->face_index, data->lvl, st, to_tang, x, y);
+}
+
+static void set_rast_triangle(const MBakeRast *bake_rast, const int x, const int y)
+{
+       const int w= bake_rast->w;
+       const int h= bake_rast->h;
+
+       if(x>=0 && x<w && y>=0 && y<h) {
+               if((bake_rast->texels[y*w+x])==0) {
+                       flush_pixel(bake_rast->data, x, y);
+                       bake_rast->texels[y*w+x]= FILTER_MASK_USED;
+               }
+       }
+}
+
+static void rasterize_half(const MBakeRast *bake_rast,
+                           const float s0_s, const float t0_s, const float s1_s, const float t1_s,
+                           const float s0_l, const float t0_l, const float s1_l, const float t1_l,
+                           const int y0_in, const int y1_in, const int is_mid_right)
+{
+       const int s_stable= fabsf(t1_s-t0_s)>FLT_EPSILON ? 1 : 0;
+       const int l_stable= fabsf(t1_l-t0_l)>FLT_EPSILON ? 1 : 0;
+       const int w= bake_rast->w;
+       const int h= bake_rast->h;
+       int y, y0, y1;
+
+       if(y1_in<=0 || y0_in>=h)
+               return;
+
+       y0= y0_in<0 ? 0 : y0_in;
+       y1= y1_in>=h ? h : y1_in;
+
+       for(y= y0; y<y1; y++) {
+               /*-b(x-x0) + a(y-y0) = 0 */
+               int iXl, iXr, x;
+               float x_l= s_stable!=0 ? (s0_s + (((s1_s-s0_s)*(y-t0_s))/(t1_s-t0_s))) : s0_s;
+               float x_r= l_stable!=0 ? (s0_l + (((s1_l-s0_l)*(y-t0_l))/(t1_l-t0_l))) : s0_l;
+
+               if(is_mid_right!=0)
+                       SWAP(float, x_l, x_r);
+
+               iXl= (int)ceilf(x_l);
+               iXr= (int)ceilf(x_r);
+
+               if(iXr>0 && iXl<w) {
+                       iXl= iXl<0?0:iXl;
+                       iXr= iXr>=w?w:iXr;
+
+                       for(x= iXl; x<iXr; x++)
+                               set_rast_triangle(bake_rast, x, y);
+               }
+       }
+}
+
+static void bake_rasterize(const MBakeRast *bake_rast, const float st0_in[2], const float st1_in[2], const float st2_in[2])
+{
+       const int w= bake_rast->w;
+       const int h= bake_rast->h;
+       float slo= st0_in[0]*w - 0.5f;
+       float tlo= st0_in[1]*h - 0.5f;
+       float smi= st1_in[0]*w - 0.5f;
+       float tmi= st1_in[1]*h - 0.5f;
+       float shi= st2_in[0]*w - 0.5f;
+       float thi= st2_in[1]*h - 0.5f;
+       int is_mid_right= 0, ylo, yhi, yhi_beg;
+
+       /* skip degenerates */
+       if((slo==smi && tlo==tmi) || (slo==shi && tlo==thi) || (smi==shi && tmi==thi))
+               return;
+
+       /* sort by T */
+       if(tlo>tmi && tlo>thi) {
+               SWAP(float, shi, slo);
+               SWAP(float, thi, tlo);
+       } else if(tmi>thi) {
+               SWAP(float, shi, smi);
+               SWAP(float, thi, tmi);
+       }
+
+       if(tlo>tmi) {
+               SWAP(float, slo, smi);
+               SWAP(float, tlo, tmi);
+       }
+
+       /* check if mid point is to the left or to the right of the lo-hi edge */
+       is_mid_right= (-(shi-slo)*(tmi-thi) + (thi-tlo)*(smi-shi))>0 ? 1 : 0;
+       ylo= (int) ceilf(tlo);
+       yhi_beg= (int) ceilf(tmi);
+       yhi= (int) ceilf(thi);
+
+       /*if(fTmi>ceilf(fTlo))*/
+       rasterize_half(bake_rast, slo, tlo, smi, tmi, slo, tlo, shi, thi, ylo, yhi_beg, is_mid_right);
+       rasterize_half(bake_rast, smi, tmi, shi, thi, slo, tlo, shi, thi, yhi_beg, yhi, is_mid_right);
+}
+
+static int multiresbake_test_break(MultiresBakeRender *bkr)
+{
+       if(!bkr->stop) {
+               /* this means baker is executed outside from job system */
+               return 0;
+       }
+
+       return G.afbreek;
+}
+
+static void do_multires_bake(MultiresBakeRender *bkr, Image* ima, MPassKnownData passKnownData,
+                             MInitBakeData initBakeData, MApplyBakeData applyBakeData, MFreeBakeData freeBakeData)
+{
+       DerivedMesh *dm= bkr->lores_dm;
+       ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+       const int lvl= bkr->lvl;
+       const int tot_face= dm->getNumFaces(dm);
+       MVert *mvert= dm->getVertArray(dm);
+       MFace *mface= dm->getFaceArray(dm);
+       MTFace *mtface= dm->getFaceDataArray(dm, CD_MTFACE);
+       float *pvtangent= NULL;
+
+       if(CustomData_get_layer_index(&dm->faceData, CD_TANGENT) == -1)
+               DM_add_tangent_layer(dm);
+
+       pvtangent= DM_get_face_data_layer(dm, CD_TANGENT);
+
+       if(tot_face > 0) {  /* sanity check */
+               int f= 0;
+               MBakeRast bake_rast;
+               MResolvePixelData data={NULL};
+
+               data.mface= mface;
+               data.mvert= mvert;
+               data.mtface= mtface;
+               data.pvtangent= pvtangent;
+               data.precomputed_normals= dm->getFaceDataArray(dm, CD_NORMAL);  /* don't strictly need this */
+               data.w= ibuf->x;
+               data.h= ibuf->y;
+               data.lores_dm= dm;
+               data.hires_dm= bkr->hires_dm;
+               data.lvl= lvl;
+               data.pass_data= passKnownData;
+
+               if(initBakeData)
+                       data.bake_data= initBakeData(bkr, ima);
+
+               init_bake_rast(&bake_rast, ibuf, &data, flush_pixel);
+
+               for(f= 0; f<tot_face; f++) {
+                       MTFace *mtfate= &mtface[f];
+                       int verts[3][2], nr_tris, t;
+
+                       if(multiresbake_test_break(bkr))
+                               break;
+
+                       if(mtfate->tpage!=ima)
+                               continue;
+
+                       data.face_index= f;
+
+                       /* might support other forms of diagonal splits later on such as
+                          split by shortest diagonal.*/
+                       verts[0][0]=0;
+                       verts[1][0]=1;
+                       verts[2][0]=2;
+
+                       verts[0][1]=0;
+                       verts[1][1]=2;
+                       verts[2][1]=3;
+
+                       nr_tris= mface[f].v4!=0 ? 2 : 1;
+                       for(t= 0; t<nr_tris; t++) {
+                               data.i0= verts[0][t];
+                               data.i1= verts[1][t];
+                               data.i2 =verts[2][t];
+
+                               bake_rasterize(&bake_rast, mtfate->uv[data.i0], mtfate->uv[data.i1], mtfate->uv[data.i2]);
+                       }
+
+                       bkr->baked_faces++;
+
+                       if(bkr->do_update)
+                               *bkr->do_update= 1;
+
+                       if(bkr->progress)
+                               *bkr->progress= ((float)bkr->baked_objects + (float)bkr->baked_faces / tot_face) / bkr->tot_obj;
+               }
+
+               if(applyBakeData)
+                       applyBakeData(data.bake_data);
+
+               if(freeBakeData)
+                       freeBakeData(data.bake_data);
+       }
+}
+
+static void interp_bilinear_quad_data(float data[4][3], float u, float v, float res[3])
+{
+       float vec[3];
+
+       copy_v3_v3(res, data[0]);
+       mul_v3_fl(res, (1-u)*(1-v));
+       copy_v3_v3(vec, data[1]);
+       mul_v3_fl(vec, u*(1-v)); add_v3_v3(res, vec);
+       copy_v3_v3(vec, data[2]);
+       mul_v3_fl(vec, u*v); add_v3_v3(res, vec);
+       copy_v3_v3(vec, data[3]);
+       mul_v3_fl(vec, (1-u)*v); add_v3_v3(res, vec);
+}
+
+static void interp_barycentric_tri_data(float data[3][3], float u, float v, float res[3])
+{
+       float vec[3];
+
+       copy_v3_v3(res, data[0]);
+       mul_v3_fl(res, u);
+       copy_v3_v3(vec, data[1]);
+       mul_v3_fl(vec, v); add_v3_v3(res, vec);
+       copy_v3_v3(vec, data[2]);
+       mul_v3_fl(vec, 1.0f-u-v); add_v3_v3(res, vec);
+}
+
+/* mode = 0: interpolate normals,
+   mode = 1: interpolate coord */
+static void interp_bilinear_grid(DMGridData *grid, int grid_size, float crn_x, float crn_y, int mode, float res[3])
+{
+       int x0, x1, y0, y1;
+       float u, v;
+       float data[4][3];
+
+       x0= (int) crn_x;
+       x1= x0>=(grid_size-1) ? (grid_size-1) : (x0+1);
+
+       y0= (int) crn_y;
+       y1= y0>=(grid_size-1) ? (grid_size-1) : (y0+1);
+
+       u= crn_x-x0;
+       v= crn_y-y0;
+
+       if(mode == 0) {
+               copy_v3_v3(data[0], grid[y0 * grid_size + x0].no);
+               copy_v3_v3(data[1], grid[y0 * grid_size + x1].no);
+               copy_v3_v3(data[2], grid[y1 * grid_size + x1].no);
+               copy_v3_v3(data[3], grid[y1 * grid_size + x0].no);
+       } else {
+               copy_v3_v3(data[0], grid[y0 * grid_size + x0].co);
+               copy_v3_v3(data[1], grid[y0 * grid_size + x1].co);
+               copy_v3_v3(data[2], grid[y1 * grid_size + x1].co);
+               copy_v3_v3(data[3], grid[y1 * grid_size + x0].co);
+       }
+
+       interp_bilinear_quad_data(data, u, v, res);
+}
+
+static void get_ccgdm_data(DerivedMesh *lodm, DerivedMesh *hidm,  const int lvl, const int face_index, const float u, const float v, float co[3], float n[3])
+{
+       MFace mface;
+       DMGridData **grid_data;
+       float crn_x, crn_y;
+       int grid_size, num_grids, S, face_side;
+       int *grid_offset, g_index;
+
+       lodm->getFace(lodm, face_index, &mface);
+
+       num_grids= hidm->getNumGrids(hidm);
+       grid_size= hidm->getGridSize(hidm);
+       grid_data= hidm->getGridData(hidm);
+       grid_offset= hidm->getGridOffset(hidm);
+
+       face_side= (grid_size<<1)-1;
+
+       if(lvl==0) {
+               g_index= grid_offset[face_index];
+               S= mdisp_rot_face_to_crn(mface.v4 ? 4 : 3, face_side, u*(face_side-1), v*(face_side-1), &crn_x, &crn_y);
+       } else {
+               const int *index= lodm->getFaceDataArray(lodm, CD_ORIGINDEX);
+               int side= (1 << (lvl-1)) + 1;
+               int grid_index= index[face_index];
+               int loc_offs= face_index % (1<<(2*lvl));
+               int cell_index= loc_offs % ((side-1)*(side-1));
+               int cell_side= grid_size / (side-1);
+               int row= cell_index / (side-1);
+               int col= cell_index % (side-1);
+
+               S= face_index / (1<<(2*(lvl-1))) - grid_offset[grid_index];
+               g_index= grid_offset[grid_index];
+
+               crn_y= (row * cell_side) + u * cell_side;
+               crn_x= (col * cell_side) + v * cell_side;
+       }
+
+       CLAMP(crn_x, 0.0f, grid_size);
+       CLAMP(crn_y, 0.0f, grid_size);
+
+       if(n != NULL)
+               interp_bilinear_grid(grid_data[g_index + S], grid_size, crn_x, crn_y, 0, n);
+
+       if(co != NULL)
+               interp_bilinear_grid(grid_data[g_index + S], grid_size, crn_x, crn_y, 1, co);
+}
+
+/* mode = 0: interpolate normals,
+   mode = 1: interpolate coord */
+static void interp_bilinear_mface(DerivedMesh *dm, MFace *mface, const float u, const float v, const int mode, float res[3])
+{
+       float data[4][3];
+
+       if(mode == 0) {
+               dm->getVertNo(dm, mface->v1, data[0]);
+               dm->getVertNo(dm, mface->v2, data[1]);
+               dm->getVertNo(dm, mface->v3, data[2]);
+               dm->getVertNo(dm, mface->v4, data[3]);
+       } else {
+               dm->getVertCo(dm, mface->v1, data[0]);
+               dm->getVertCo(dm, mface->v2, data[1]);
+               dm->getVertCo(dm, mface->v3, data[2]);
+               dm->getVertCo(dm, mface->v4, data[3]);
+       }
+
+       interp_bilinear_quad_data(data, u, v, res);
+}
+
+/* mode = 0: interpolate normals,
+   mode = 1: interpolate coord */
+static void interp_barycentric_mface(DerivedMesh *dm, MFace *mface, const float u, const float v, const int mode, float res[3])
+{
+       float data[3][3];
+
+       if(mode == 0) {
+               dm->getVertNo(dm, mface->v1, data[0]);
+               dm->getVertNo(dm, mface->v2, data[1]);
+               dm->getVertNo(dm, mface->v3, data[2]);
+       } else {
+               dm->getVertCo(dm, mface->v1, data[0]);
+               dm->getVertCo(dm, mface->v2, data[1]);
+               dm->getVertCo(dm, mface->v3, data[2]);
+       }
+
+       interp_barycentric_tri_data(data, u, v, res);
+}
+
+static void *init_heights_data(MultiresBakeRender *bkr, Image* ima)
+{
+       MHeightBakeData *height_data;
+       ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+
+       height_data= MEM_callocN(sizeof(MHeightBakeData), "MultiresBake heightData");
+
+       height_data->ima= ima;
+       height_data->heights= MEM_callocN(sizeof(float)*ibuf->x*ibuf->y, "MultiresBake heights");
+       height_data->height_max= -FLT_MAX;
+       height_data->height_min= FLT_MAX;
+
+       if(!bkr->use_lores_mesh) {
+               SubsurfModifierData smd= {{NULL}};
+               int ss_lvl= bkr->tot_lvl - bkr->lvl;
+
+               CLAMP(ss_lvl, 0, 6);
+
+               smd.levels= smd.renderLevels= ss_lvl;
+               smd.flags|= eSubsurfModifierFlag_SubsurfUv;
+
+               if(bkr->simple)
+                       smd.subdivType= ME_SIMPLE_SUBSURF;
+
+               height_data->ssdm= subsurf_make_derived_from_derived(bkr->lores_dm, &smd, 0, NULL, 0, 0, 0);
+       }
+
+       return (void*)height_data;
+}
+
+static void apply_heights_data(void *bake_data)
+{
+       MHeightBakeData *height_data= (MHeightBakeData*)bake_data;
+       ImBuf *ibuf= BKE_image_get_ibuf(height_data->ima, NULL);
+       int x, y, i;
+       float height, *heights= height_data->heights;
+       float min= height_data->height_min, max= height_data->height_max;
+
+       for(x= 0; x<ibuf->x; x++) {
+               for(y =0; y<ibuf->y; y++) {
+                       i= ibuf->x*y + x;
+
+                       if(((char*)ibuf->userdata)[i] != FILTER_MASK_USED)
+                               continue;
+
+                       if(ibuf->rect_float) {
+                               float *rrgbf= ibuf->rect_float + i*4;
+
+                               if(max-min > 1e-5) height= (heights[i]-min)/(max-min);
+                               else height= 0;
+
+                               rrgbf[0]=rrgbf[1]=rrgbf[2]= height;
+                       } else {
+                               char *rrgb= (char*)ibuf->rect + i*4;
+
+                               if(max-min > 1e-5) height= (heights[i]-min)/(max-min);
+                               else height= 0;
+
+                               rrgb[0]=rrgb[1]=rrgb[2]= FTOCHAR(height);
+                       }
+               }
+       }
+}
+
+static void free_heights_data(void *bake_data)
+{
+       MHeightBakeData *height_data= (MHeightBakeData*)bake_data;
+
+       if(height_data->ssdm)
+               height_data->ssdm->release(height_data->ssdm);
+
+       MEM_freeN(height_data->heights);
+       MEM_freeN(height_data);
+}
+
+/* MultiresBake callback for heights baking
+   general idea:
+     - find coord of point with specified UV in hi-res mesh (let's call it p1)
+     - find coord of point and normal with specified UV in lo-res mesh (or subdivided lo-res
+       mesh to make texture smoother) let's call this point p0 and n.
+     - height wound be dot(n, p1-p0) */
+static void apply_heights_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, const void *bake_data,
+                                   const int face_index, const int lvl, const float st[2],
+                                   float UNUSED(tangmat[3][3]), const int x, const int y)
+{
+       MTFace *mtface= CustomData_get_layer(&lores_dm->faceData, CD_MTFACE);
+       MFace mface;
+       Image *ima= mtface[face_index].tpage;
+       ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+       MHeightBakeData *height_data= (MHeightBakeData*)bake_data;
+       float uv[2], *st0, *st1, *st2, *st3;
+       int pixel= ibuf->x*y + x;
+       float vec[3], p0[3], p1[3], n[3], len;
+
+       lores_dm->getFace(lores_dm, face_index, &mface);
+
+       if(x==0 && y==0) {
+               zero_v3(p0);
+       }
+
+       st0= mtface[face_index].uv[0];
+       st1= mtface[face_index].uv[1];
+       st2= mtface[face_index].uv[2];
+
+       if(mface.v4) {
+               st3= mtface[face_index].uv[3];
+               resolve_quad_uv(uv, st, st0, st1, st2, st3);
+       } else
+               resolve_tri_uv(uv, st, st0, st1, st2);
+
+       CLAMP(uv[0], 0.0f, 1.0f);
+       CLAMP(uv[1], 0.0f, 1.0f);
+
+       get_ccgdm_data(lores_dm, hires_dm, lvl, face_index, uv[0], uv[1], p1, 0);
+
+       if(height_data->ssdm) {
+               //get_ccgdm_data_ss(lores_dm, height_data->ssdm, lvl, face_index, uv[0], uv[1], p0, n);
+               get_ccgdm_data(lores_dm, height_data->ssdm, 0, face_index, uv[0], uv[1], p0, n);
+       } else {
+               MFace mface;
+               lores_dm->getFace(lores_dm, face_index, &mface);
+
+               if(mface.v4) {
+                       interp_bilinear_mface(lores_dm, &mface, uv[0], uv[1], 1, p0);
+                       interp_bilinear_mface(lores_dm, &mface, uv[0], uv[1], 0, n);
+               } else {
+                       interp_barycentric_mface(lores_dm, &mface, uv[0], uv[1], 1, p0);
+                       interp_barycentric_mface(lores_dm, &mface, uv[0], uv[1], 0, n);
+               }
+       }
+
+       sub_v3_v3v3(vec, p1, p0);
+       //len= len_v3(vec);
+       len= dot_v3v3(n, vec);
+
+       height_data->heights[pixel]= len;
+       if(len<height_data->height_min) height_data->height_min= len;
+       if(len>height_data->height_max) height_data->height_max= len;
+
+       if(ibuf->rect_float) {
+               float *rrgbf= ibuf->rect_float + pixel*4;
+               rrgbf[3]= 1.0f;
+       } else {
+               char *rrgb= (char*)ibuf->rect + pixel*4;
+               rrgb[3]= 255;
+       }
+}
+
+/* MultiresBake callback for normals' baking
+   general idea:
+     - find coord and normal of point with specified UV in hi-res mesh
+     - multiply it by tangmat
+     - vector in color space would be norm(vec) /2 + (0.5, 0.5, 0.5) */
+static void apply_tangmat_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, const void *UNUSED(bake_data),
+                                   const int face_index, const int lvl, const float st[2],
+                                   float tangmat[3][3], const int x, const int y)
+{
+       MTFace *mtface= CustomData_get_layer(&lores_dm->faceData, CD_MTFACE);
+       MFace mface;
+       Image *ima= mtface[face_index].tpage;
+       ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+       float uv[2], *st0, *st1, *st2, *st3;
+       int pixel= ibuf->x*y + x;
+       float n[3], vec[3], tmp[3]= {0.5, 0.5, 0.5};
+
+       lores_dm->getFace(lores_dm, face_index, &mface);
+
+       st0= mtface[face_index].uv[0];
+       st1= mtface[face_index].uv[1];
+       st2= mtface[face_index].uv[2];
+
+       if(mface.v4) {
+               st3= mtface[face_index].uv[3];
+               resolve_quad_uv(uv, st, st0, st1, st2, st3);
+       } else
+               resolve_tri_uv(uv, st, st0, st1, st2);
+
+       CLAMP(uv[0], 0.0f, 1.0f);
+       CLAMP(uv[1], 0.0f, 1.0f);
+
+       get_ccgdm_data(lores_dm, hires_dm, lvl, face_index, uv[0], uv[1], NULL, n);
+
+       mul_v3_m3v3(vec, tangmat, n);
+       normalize_v3(vec);
+       mul_v3_fl(vec, 0.5);
+       add_v3_v3(vec, tmp);
+
+       if(ibuf->rect_float) {
+               float *rrgbf= ibuf->rect_float + pixel*4;
+               rrgbf[0]= vec[0];
+               rrgbf[1]= vec[1];
+               rrgbf[2]= vec[2];
+               rrgbf[3]= 1.0f;
+       } else {
+               char *rrgb= (char*)ibuf->rect + pixel*4;
+               rrgb[0]= FTOCHAR(vec[0]);
+               rrgb[1]= FTOCHAR(vec[1]);
+               rrgb[2]= FTOCHAR(vec[2]);
+               rrgb[3]= 255;
+       }
+}
+
+static void count_images(MultiresBakeRender *bkr)
+{
+       int a, totface;
+       DerivedMesh *dm= bkr->lores_dm;
+       MTFace *mtface= CustomData_get_layer(&dm->faceData, CD_MTFACE);
+
+       bkr->image.first= bkr->image.last= NULL;
+       bkr->tot_image= 0;
+
+       totface= dm->getNumFaces(dm);
+
+       for(a= 0; a<totface; a++)
+               mtface[a].tpage->id.flag&= ~LIB_DOIT;
+
+       for(a= 0; a<totface; a++) {
+               Image *ima= mtface[a].tpage;
+               if((ima->id.flag&LIB_DOIT)==0) {
+                       LinkData *data= BLI_genericNodeN(ima);
+                       BLI_addtail(&bkr->image, data);
+                       bkr->tot_image++;
+                       ima->id.flag|= LIB_DOIT;
+               }
+       }
+
+       for(a= 0; a<totface; a++)
+               mtface[a].tpage->id.flag&= ~LIB_DOIT;
+}
+
+static void bake_images(MultiresBakeRender *bkr)
+{
+       LinkData *link;
+
+       for(link= bkr->image.first; link; link= link->next) {
+               Image *ima= (Image*)link->data;
+               ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+
+               if(ibuf->x>0 && ibuf->y>0) {
+                       ibuf->userdata= MEM_callocN(ibuf->y*ibuf->x, "MultiresBake imbuf mask");
+
+                       switch(bkr->mode) {
+                               case RE_BAKE_NORMALS:
+                                       do_multires_bake(bkr, ima, apply_tangmat_callback, NULL, NULL, NULL);
+                                       break;
+                               case RE_BAKE_DISPLACEMENT:
+                                       do_multires_bake(bkr, ima, apply_heights_callback, init_heights_data,
+                                                        apply_heights_data, free_heights_data);
+                                       break;
+                       }
+               }
+
+               ima->id.flag|= LIB_DOIT;
+       }
+}
+
+static void finish_images(MultiresBakeRender *bkr)
+{
+       LinkData *link;
+
+       for(link= bkr->image.first; link; link= link->next) {
+               Image *ima= (Image*)link->data;
+               int i;
+               ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+
+               if(ibuf->x<=0 || ibuf->y<=0)
+                       continue;
+
+               /* Margin */
+               if(bkr->bake_filter) {
+                       char *temprect;
+
+                       /* extend the mask +2 pixels from the image,
+                        * this is so colors dont blend in from outside */
+
+                       for(i=0; i<bkr->bake_filter; i++)
+                               IMB_mask_filter_extend((char *)ibuf->userdata, ibuf->x, ibuf->y);
+
+                       temprect = MEM_dupallocN(ibuf->userdata);
+
+                       /* expand twice to clear this many pixels, so they blend back in */
+                       IMB_mask_filter_extend(temprect, ibuf->x, ibuf->y);
+                       IMB_mask_filter_extend(temprect, ibuf->x, ibuf->y);
+
+                       /* clear all pixels in the margin */
+                       IMB_mask_clear(ibuf, temprect, FILTER_MASK_MARGIN);
+                       MEM_freeN(temprect);
+
+                       for(i= 0; i<bkr->bake_filter; i++)
+                               IMB_filter_extend(ibuf, (char *)ibuf->userdata);
+               }
+
+               ibuf->userflags|= IB_BITMAPDIRTY;
+               if(ibuf->mipmap[0]) {
+                       ibuf->userflags|= IB_MIPMAP_INVALID;
+                       imb_freemipmapImBuf(ibuf);
+               }
+
+               if(ibuf->userdata) {
+                       MEM_freeN(ibuf->userdata);
+                       ibuf->userdata= NULL;
+               }
+       }
+}
+
+static void multiresbake_start(MultiresBakeRender *bkr)
+{
+       count_images(bkr);
+       bake_images(bkr);
+       finish_images(bkr);
+}
+
+static int multiresbake_check(bContext *C, wmOperator *op) {
+       Scene *scene= CTX_data_scene(C);
+       Object *ob;
+       Mesh *me;
+       MultiresModifierData *mmd;
+       int ok= 1, a;
+
+       CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
+               ob= base->object;
+
+               if(ob->type != OB_MESH) {
+                       BKE_report(op->reports, RPT_ERROR, "Basking of multires data only works with active object which is a mesh");
+
+                       ok= 0;
+                       break;
+               }
+
+               me= (Mesh*)ob->data;
+               mmd= get_multires_modifier(scene, ob, 0);
+
+               /* Multi-resolution should be and be last in the stack */
+               if(ok && mmd) {
+                       ModifierData *md;
+
+                       ok= mmd->totlvl>0;
+
+                       for(md = (ModifierData*)mmd->modifier.next; md && ok; md = md->next) {
+                               if (modifier_isEnabled(scene, md, eModifierMode_Realtime)) {
+                                       ok= 0;
+                               }
+                       }
+               } else ok= 0;
+
+               if(!ok) {
+                       BKE_report(op->reports, RPT_ERROR, "Multires data baking requires multi-resolution object");
+
+                       break;
+               }
+
+               if(!me->mtface) {
+                       BKE_report(op->reports, RPT_ERROR, "Mesh should be unwrapped before multires data baking");
+
+                       ok= 0;
+               } else {
+                       a= me->totface;
+                       while (ok && a--) {
+                               Image *ima= me->mtface[a].tpage;
+
+                               if(!ima) {
+                                       BKE_report(op->reports, RPT_ERROR, "You should have active texture to use multires baker");
+
+                                       ok= 0;
+                               } else {
+                                       ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+
+                                       if(!ibuf) {
+                                               BKE_report(op->reports, RPT_ERROR, "Baking should happend to image with image buffer");
+
+                                               ok= 0;
+                                       } else {
+                                               if(ibuf->rect==NULL && ibuf->rect_float==NULL)
+                                                       ok= 0;
+
+                                               if(ibuf->rect_float && !(ibuf->channels==0 || ibuf->channels==4))
+                                                       ok= 0;
+
+                                               if(!ok)
+                                                       BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type");
+                                       }
+                               }
+                       }
+               }
+
+               if(!ok)
+                       break;
+       }
+       CTX_DATA_END;
+
+       return ok;
+}
+
+static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *lvl)
+{
+       DerivedMesh *dm;
+       MultiresModifierData *mmd= get_multires_modifier(scene, ob, 0);
+       Mesh *me= (Mesh*)ob->data;
+
+       *lvl= mmd->lvl;
+
+       if(mmd->lvl==0) {
+               DerivedMesh *tmp_dm= CDDM_from_mesh(me, ob);
+               dm= CDDM_copy(tmp_dm);
+               tmp_dm->release(tmp_dm);
+       } else {
+               MultiresModifierData tmp_mmd= *mmd;
+               DerivedMesh *cddm= CDDM_from_mesh(me, ob);
+
+               tmp_mmd.lvl= mmd->lvl;
+               dm= multires_dm_create_from_derived(&tmp_mmd, 1, cddm, ob, 0, 0);
+               cddm->release(cddm);
+       }
+
+       return dm;
+}
+
+static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *lvl, int *simple)
+{
+       Mesh *me= (Mesh*)ob->data;
+       MultiresModifierData *mmd= get_multires_modifier(scene, ob, 0);
+       MultiresModifierData tmp_mmd= *mmd;
+       DerivedMesh *cddm= CDDM_from_mesh(me, ob);
+       DerivedMesh *dm;
+
+       *lvl= mmd->totlvl;
+       *simple= mmd->simple;
+
+       tmp_mmd.lvl= mmd->totlvl;
+       dm= multires_dm_create_from_derived(&tmp_mmd, 1, cddm, ob, 0, 0);
+       cddm->release(cddm);
+
+       return dm;
+}
+
+static void clear_images(MTFace *mtface, int totface)
+{
+       int a;
+       float vec[4]= {0.0f, 0.0f, 0.0f, 0.0f};
+
+       for(a= 0; a<totface; a++)
+               mtface[a].tpage->id.flag&= ~LIB_DOIT;
+
+       for(a= 0; a<totface; a++) {
+               Image *ima= mtface[a].tpage;
+
+               if((ima->id.flag&LIB_DOIT)==0) {
+                       ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+
+                       IMB_rectfill(ibuf, vec);
+                       ima->id.flag|= LIB_DOIT;
+               }
+       }
+
+       for(a= 0; a<totface; a++)
+               mtface[a].tpage->id.flag&= ~LIB_DOIT;
+}
+
+static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
+{
+       Object *ob;
+       Scene *scene= CTX_data_scene(C);
+
+       if(!multiresbake_check(C, op))
+               return OPERATOR_CANCELLED;
+
+       if(scene->r.bake_flag&R_BAKE_CLEAR) {  /* clear images */
+               CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
+                       Mesh *me;
+
+                       ob= base->object;
+                       me= (Mesh*)ob->data;
+
+                       clear_images(me->mtface, me->totface);
+               }
+               CTX_DATA_END;
+       }
+
+       CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
+               MultiresBakeRender bkr= {0};
+
+               ob= base->object;
+
+               /* copy data stored in job descriptor */
+               bkr.bake_filter= scene->r.bake_filter;
+               bkr.mode= scene->r.bake_mode;
+               bkr.use_lores_mesh= scene->r.bake_flag&R_BAKE_LORES_MESH;
+
+               /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
+               bkr.lores_dm= multiresbake_create_loresdm(scene, ob, &bkr.lvl);
+               bkr.hires_dm= multiresbake_create_hiresdm(scene, ob, &bkr.tot_lvl, &bkr.simple);
+
+               multiresbake_start(&bkr);
+
+               BLI_freelistN(&bkr.image);
+
+               bkr.lores_dm->release(bkr.lores_dm);
+               bkr.hires_dm->release(bkr.hires_dm);
+       }
+       CTX_DATA_END;
+
+       return OPERATOR_FINISHED;
+}
+
+/* Multiresbake adopted for job-system executing */
+static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
+{
+       Scene *scene= CTX_data_scene(C);
+       Object *ob;
+
+       /* backup scene settings, so their changing in UI would take no effect on baker */
+       bkj->bake_filter= scene->r.bake_filter;
+       bkj->mode= scene->r.bake_mode;
+       bkj->use_lores_mesh= scene->r.bake_flag&R_BAKE_LORES_MESH;
+       bkj->bake_clear= scene->r.bake_flag&R_BAKE_CLEAR;
+
+       CTX_DATA_BEGIN(C, Base*, base, selected_editable_bases) {
+               MultiresBakerJobData *data;
+               ob= base->object;
+
+               data= MEM_callocN(sizeof(MultiresBakerJobData), "multiresBaker derivedMesh_data");
+               data->lores_dm = multiresbake_create_loresdm(scene, ob, &data->lvl);
+               data->hires_dm = multiresbake_create_hiresdm(scene, ob, &data->tot_lvl, &data->simple);
+               BLI_addtail(&bkj->data, data);
+       }
+       CTX_DATA_END;
+}
+
+static void multiresbake_startjob(void *bkv, short *stop, short *do_update, float *progress)
+{
+       MultiresBakerJobData *data;
+       MultiresBakeJob *bkj= bkv;
+       int baked_objects= 0, tot_obj;
+
+       tot_obj= BLI_countlist(&bkj->data);
+
+       if(bkj->bake_clear) {  /* clear images */
+               for(data= bkj->data.first; data; data= data->next) {
+                       DerivedMesh *dm= data->lores_dm;
+                       MTFace *mtface= CustomData_get_layer(&dm->faceData, CD_MTFACE);
+
+                       clear_images(mtface, dm->getNumFaces(dm));
+               }
+       }
+
+       for(data= bkj->data.first; data; data= data->next) {
+               MultiresBakeRender bkr= {0};
+
+               /* copy data stored in job descriptor */
+               bkr.bake_filter= bkj->bake_filter;
+               bkr.mode= bkj->mode;
+               bkr.use_lores_mesh= bkj->use_lores_mesh;
+
+               /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
+               bkr.lores_dm= data->lores_dm;
+               bkr.hires_dm= data->hires_dm;
+               bkr.tot_lvl= data->tot_lvl;
+               bkr.lvl= data->lvl;
+               bkr.simple= data->simple;
+
+               /* needed for proper progress bar */
+               bkr.tot_obj= tot_obj;
+               bkr.baked_objects= baked_objects;
+
+               bkr.stop= stop;
+               bkr.do_update= do_update;
+               bkr.progress= progress;
+
+               multiresbake_start(&bkr);
+
+               BLI_freelistN(&bkr.image);
+
+               baked_objects++;
+       }
+}
+
+static void multiresbake_freejob(void *bkv)
+{
+       MultiresBakeJob *bkj= bkv;
+       MultiresBakerJobData *data, *next;
+
+       data= bkj->data.first;
+       while (data) {
+               next= data->next;
+               data->lores_dm->release(data->lores_dm);
+               data->hires_dm->release(data->hires_dm);
+               MEM_freeN(data);
+               data= next;
+       }
+
+       MEM_freeN(bkj);
+}
+
+static int multiresbake_image_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       MultiresBakeJob *bkr;
+       wmJob *steve;
+
+       if(!multiresbake_check(C, op))
+               return OPERATOR_CANCELLED;
+
+       bkr= MEM_callocN(sizeof(MultiresBakeJob), "MultiresBakeJob data");
+       init_multiresbake_job(C, bkr);
+
+       /* setup job */
+       steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Multires Bake", WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS);
+       WM_jobs_customdata(steve, bkr, multiresbake_freejob);
+       WM_jobs_timer(steve, 0.2, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */
+       WM_jobs_callbacks(steve, multiresbake_startjob, NULL, NULL, NULL);
+
+       G.afbreek= 0;
+
+       WM_jobs_start(CTX_wm_manager(C), steve);
+       WM_cursor_wait(0);
+
+       /* add modal handler for ESC */
+       WM_event_add_modal_handler(C, op);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
 /* ****************** render BAKING ********************** */
 
 /* threaded break test */
@@ -147,9 +1284,6 @@ static void init_bake_internal(BakeRender *bkr, bContext *C)
 {
        Scene *scene= CTX_data_scene(C);
 
-       /* flush multires changes (for sculpt) */
-       multires_force_render_update(CTX_data_active_object(C));
-
        /* get editmode results */
        ED_object_exit_editmode(C, 0);  /* 0 = does not exit editmode */
 
@@ -273,43 +1407,59 @@ static int objects_bake_render_modal(bContext *C, wmOperator *UNUSED(op), wmEven
        return OPERATOR_PASS_THROUGH;
 }
 
+static int is_multires_bake(Scene *scene)
+{
+       if ( ELEM(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT))
+               return scene->r.bake_flag & R_BAKE_MULTIRES;
+
+       return 0;
+}
+
 static int objects_bake_render_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(_event))
 {
        Scene *scene= CTX_data_scene(C);
+       int result= OPERATOR_CANCELLED;
 
-       /* only one render job at a time */
-       if(WM_jobs_test(CTX_wm_manager(C), scene))
-               return OPERATOR_CANCELLED;
-       
-       if(test_bake_internal(C, op->reports)==0) {
-               return OPERATOR_CANCELLED;
-       }
-       else {
-               BakeRender *bkr= MEM_callocN(sizeof(BakeRender), "render bake");
-               wmJob *steve;
+       if(is_multires_bake(scene)) {
+               result= multiresbake_image_exec(C, op);
+       } else {
+               /* only one render job at a time */
+               if(WM_jobs_test(CTX_wm_manager(C), scene))
+                       return OPERATOR_CANCELLED;
+
+               if(test_bake_internal(C, op->reports)==0) {
+                       return OPERATOR_CANCELLED;
+               }
+               else {
+                       BakeRender *bkr= MEM_callocN(sizeof(BakeRender), "render bake");
+                       wmJob *steve;
 
-               init_bake_internal(bkr, C);
-               bkr->reports= op->reports;
+                       init_bake_internal(bkr, C);
+                       bkr->reports= op->reports;
 
-               /* setup job */
-               steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Texture Bake", WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS);
-               WM_jobs_customdata(steve, bkr, bake_freejob);
-               WM_jobs_timer(steve, 0.2, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */
-               WM_jobs_callbacks(steve, bake_startjob, NULL, bake_update, NULL);
+                       /* setup job */
+                       steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Texture Bake", WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS);
+                       WM_jobs_customdata(steve, bkr, bake_freejob);
+                       WM_jobs_timer(steve, 0.2, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */
+                       WM_jobs_callbacks(steve, bake_startjob, NULL, bake_update, NULL);
 
-               G.afbreek= 0;
-               G.rendering = 1;
+                       G.afbreek= 0;
+                       G.rendering = 1;
 
-               WM_jobs_start(CTX_wm_manager(C), steve);
+                       WM_jobs_start(CTX_wm_manager(C), steve);
 
-               WM_cursor_wait(0);
-               WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
+                       WM_cursor_wait(0);
 
-               /* add modal handler for ESC */
-               WM_event_add_modal_handler(C, op);
+                       /* add modal handler for ESC */
+                       WM_event_add_modal_handler(C, op);
+               }
+
+               result= OPERATOR_RUNNING_MODAL;
        }
 
-       return OPERATOR_RUNNING_MODAL;
+       WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
+
+       return result;
 }
 
 
@@ -317,46 +1467,53 @@ static int bake_image_exec(bContext *C, wmOperator *op)
 {
        Main *bmain= CTX_data_main(C);
        Scene *scene= CTX_data_scene(C);
+       int result= OPERATOR_CANCELLED;
 
+       if(is_multires_bake(scene)) {
+               result= multiresbake_image_exec_locked(C, op);
+       } else  {
+               if(test_bake_internal(C, op->reports)==0) {
+                       return OPERATOR_CANCELLED;
+               }
+               else {
+                       ListBase threads;
+                       BakeRender bkr= {NULL};
 
-       if(test_bake_internal(C, op->reports)==0) {
-               return OPERATOR_CANCELLED;
-       }
-       else {
-               ListBase threads;
-               BakeRender bkr= {NULL};
+                       init_bake_internal(&bkr, C);
+                       bkr.reports= op->reports;
 
-               init_bake_internal(&bkr, C);
-               bkr.reports= op->reports;
+                       RE_test_break_cb(bkr.re, NULL, thread_break);
+                       G.afbreek= 0;   /* blender_test_break uses this global */
 
-               RE_test_break_cb(bkr.re, NULL, thread_break);
-               G.afbreek= 0;   /* blender_test_break uses this global */
+                       RE_Database_Baking(bkr.re, bmain, scene, scene->lay, scene->r.bake_mode, (scene->r.bake_flag & R_BAKE_TO_ACTIVE)? OBACT: NULL);
 
-               RE_Database_Baking(bkr.re, bmain, scene, scene->lay, scene->r.bake_mode, (scene->r.bake_flag & R_BAKE_TO_ACTIVE)? OBACT: NULL);
+                       /* baking itself is threaded, cannot use test_break in threads  */
+                       BLI_init_threads(&threads, do_bake_render, 1);
+                       bkr.ready= 0;
+                       BLI_insert_thread(&threads, &bkr);
 
-               /* baking itself is threaded, cannot use test_break in threads  */
-               BLI_init_threads(&threads, do_bake_render, 1);
-               bkr.ready= 0;
-               BLI_insert_thread(&threads, &bkr);
+                       while(bkr.ready==0) {
+                               PIL_sleep_ms(50);
+                               if(bkr.ready)
+                                       break;
 
-               while(bkr.ready==0) {
-                       PIL_sleep_ms(50);
-                       if(bkr.ready)
-                               break;
+                               /* used to redraw in 2.4x but this is just for exec in 2.5 */
+                               if (!G.background)
+                                       blender_test_break();
+                       }
+                       BLI_end_threads(&threads);
 
-                       /* used to redraw in 2.4x but this is just for exec in 2.5 */
-                       if (!G.background)
-                               blender_test_break();
-               }
-               BLI_end_threads(&threads);
+                       if(bkr.tot==0) BKE_report(op->reports, RPT_ERROR, "No valid images found to bake to");
 
-               if(bkr.tot==0) BKE_report(op->reports, RPT_ERROR, "No valid images found to bake to");
+                       finish_bake_internal(&bkr);
 
-               finish_bake_internal(&bkr);
+                       result= OPERATOR_FINISHED;
+               }
        }
 
        WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
-       return OPERATOR_FINISHED;
+
+       return result;
 }
 
 void OBJECT_OT_bake_image(wmOperatorType *ot)
index 21249c9e8f72804e0d9e7dfeae0d91d8b61e34dc..1eefc58d4de9769dd502a9d1ec825c435bb24757 100644 (file)
@@ -243,8 +243,15 @@ void IMB_free_anim(struct anim *anim);
  *
  * @attention Defined in filter.c
  */
+
+#define FILTER_MASK_NULL               0
+#define FILTER_MASK_MARGIN             1
+#define FILTER_MASK_USED               2
+
 void IMB_filter(struct ImBuf *ibuf);
 void IMB_filterN(struct ImBuf *out, struct ImBuf *in);
+void IMB_mask_filter_extend(char *mask, int width, int height);
+void IMB_mask_clear(struct ImBuf *ibuf, char *mask, int val);
 void IMB_filter_extend(struct ImBuf *ibuf, char *mask);
 void IMB_makemipmap(struct ImBuf *ibuf, int use_filter);
 void IMB_remakemipmap(struct ImBuf *ibuf, int use_filter);
index 16fb1fdf4aa982cd39702ae636c0111c57250150..d12360e5a7ead984f9436ec12f621018d9bdd6e6 100644 (file)
@@ -262,6 +262,70 @@ void IMB_filter(struct ImBuf *ibuf)
        imb_filterx(ibuf);
 }
 
+void IMB_mask_filter_extend(char *mask, int width, int height)
+{
+       char *row1, *row2, *row3;
+       int rowlen, x, y;
+       char *temprect;
+
+       rowlen= width;
+
+       /* make a copy, to prevent flooding */
+       temprect= MEM_dupallocN(mask);
+
+       for(y=1; y<=height; y++) {
+               /* setup rows */
+               row1= (char *)(temprect + (y-2)*rowlen);
+               row2= row1 + rowlen;
+               row3= row2 + rowlen;
+               if(y==1)
+                       row1= row2;
+               else if(y==height)
+                       row3= row2;
+
+               for(x=0; x<rowlen; x++) {
+                       if (mask[((y-1)*rowlen)+x]==0) {
+                               if (*row1 || *row2 || *row3 || *(row1+1) || *(row3+1) ) {
+                                       mask[((y-1)*rowlen)+x] = FILTER_MASK_MARGIN;
+                               } else if((x!=rowlen-1) && (*(row1+2) || *(row2+2) || *(row3+2)) ) {
+                                       mask[((y-1)*rowlen)+x] = FILTER_MASK_MARGIN;
+                               }
+                       }
+
+                       if(x!=0) {
+                               row1++; row2++; row3++;
+                       }
+               }
+       }
+
+       MEM_freeN(temprect);
+}
+
+void IMB_mask_clear(ImBuf *ibuf, char *mask, int val)
+{
+       int x,y;
+       if (ibuf->rect_float) {
+               for(x=0; x<ibuf->x; x++) {
+                       for(y=0; y<ibuf->y; y++) {
+                               if (mask[ibuf->x*y + x] == val) {
+                                       float *col= ibuf->rect_float + 4*(ibuf->x*y + x);
+                                       col[0] = col[1] = col[2] = col[3] = 0.0f;
+                               }
+                       }
+               }
+       } else {
+               /* char buffer */
+               for(x=0; x<ibuf->x; x++) {
+                       for(y=0; y<ibuf->y; y++) {
+                               if (mask[ibuf->x*y + x] == val) {
+                                       char *col= (char *)(ibuf->rect + ibuf->x*y + x);
+                                       col[0] = col[1] = col[2] = col[3] = 0;
+                               }
+                       }
+               }
+       }
+}
+
 #define EXTEND_PIXEL(color, w) if((color)[3]) {r+= w*(color)[0]; g+= w*(color)[1]; b+= w*(color)[2]; a+= w*(color)[3]; tot+=w;}
 
 /* if alpha is zero, it checks surrounding pixels and averages color. sets new alphas to 1.0
index 657bfb1c884885bde80df844f466d9ab6be830f8..70e90cfc71399a60f4620f64fb8f5af5977ab1f4 100644 (file)
@@ -1009,13 +1009,14 @@ typedef struct Scene {
 #define R_JPEG2K_CINE_PRESET   256
 #define R_JPEG2K_CINE_48FPS            512
 
-
 /* bake_mode: same as RE_BAKE_xxx defines */
 /* bake_flag: */
 #define R_BAKE_CLEAR           1
 #define R_BAKE_OSA                     2
 #define R_BAKE_TO_ACTIVE       4
 #define R_BAKE_NORMALIZE       8
+#define R_BAKE_MULTIRES                16
+#define R_BAKE_LORES_MESH      32
 
 /* bake_normal_space */
 #define R_BAKE_SPACE_CAMERA     0
index 72ec09a61fe73063cbeb165f1cb4efffbf7d4cb8..145a58ecf0f231ca14d699bc8c3a11afcc7ef70d 100644 (file)
@@ -2812,6 +2812,14 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
        RNA_def_property_range(prop, 0.0, 1000.0);
        RNA_def_property_ui_text(prop, "Bias", "Bias towards faces further away from the object (in blender units)");
        
+       prop= RNA_def_property(srna, "use_bake_multires", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "bake_flag", R_BAKE_MULTIRES);
+       RNA_def_property_ui_text(prop, "Bake from Multires", "Bake directly from multires object");
+
+       prop= RNA_def_property(srna, "use_bake_lores_mesh", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "bake_flag", R_BAKE_LORES_MESH);
+       RNA_def_property_ui_text(prop, "Low Resolution Mesh", "Calculate heights against unsubdivided low resolution mesh");
+
        /* stamp */
        
        prop= RNA_def_property(srna, "use_stamp_time", PROP_BOOLEAN, PROP_NONE);
index bc6c4795f5c02f72fd10b6c275bbb2d68d5602ca..0087be8cca94ce7a92b9b463bcf4729e42078178 100644 (file)
@@ -2018,74 +2018,6 @@ typedef struct BakeShade {
        short *do_update;
 } BakeShade;
 
-/* bake uses a char mask to know what has been baked */
-#define BAKE_MASK_NULL         0
-#define BAKE_MASK_MARGIN       1
-#define BAKE_MASK_BAKED                2
-static void bake_mask_filter_extend( char *mask, int width, int height )
-{
-       char *row1, *row2, *row3;
-       int rowlen, x, y;
-       char *temprect;
-       
-       rowlen= width;
-       
-       /* make a copy, to prevent flooding */
-       temprect= MEM_dupallocN(mask);
-       
-       for(y=1; y<=height; y++) {
-               /* setup rows */
-               row1= (char *)(temprect + (y-2)*rowlen);
-               row2= row1 + rowlen;
-               row3= row2 + rowlen;
-               if(y==1)
-                       row1= row2;
-               else if(y==height)
-                       row3= row2;
-               
-               for(x=0; x<rowlen; x++) {
-                       if (mask[((y-1)*rowlen)+x]==0) {
-                               if (*row1 || *row2 || *row3 || *(row1+1) || *(row3+1) ) {
-                                       mask[((y-1)*rowlen)+x] = BAKE_MASK_MARGIN;
-                               } else if((x!=rowlen-1) && (*(row1+2) || *(row2+2) || *(row3+2)) ) {
-                                       mask[((y-1)*rowlen)+x] = BAKE_MASK_MARGIN;
-                               }
-                       }
-                       
-                       if(x!=0) {
-                               row1++; row2++; row3++;
-                       }
-               }
-       }
-       MEM_freeN(temprect);
-}
-
-static void bake_mask_clear( ImBuf *ibuf, char *mask, char val )
-{
-       int x,y;
-       if (ibuf->rect_float) {
-               for(x=0; x<ibuf->x; x++) {
-                       for(y=0; y<ibuf->y; y++) {
-                               if (mask[ibuf->x*y + x] == val) {
-                                       float *col= ibuf->rect_float + 4*(ibuf->x*y + x);
-                                       col[0] = col[1] = col[2] = col[3] = 0.0f;
-                               }
-                       }
-               }
-               
-       } else {
-               /* char buffer */
-               for(x=0; x<ibuf->x; x++) {
-                       for(y=0; y<ibuf->y; y++) {
-                               if (mask[ibuf->x*y + x] == val) {
-                                       char *col= (char *)(ibuf->rect + ibuf->x*y + x);
-                                       col[0] = col[1] = col[2] = col[3] = 0;
-                               }
-                       }
-               }
-       }
-}
-
 static void bake_set_shade_input(ObjectInstanceRen *obi, VlakRen *vlr, ShadeInput *shi, int quad, int isect, int x, int y, float u, float v)
 {
        if(quad) 
@@ -2281,7 +2213,7 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int quad, int
        }
        
        if (bs->rect_mask) {
-               bs->rect_mask[bs->rectx*y + x] = BAKE_MASK_BAKED;
+               bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED;
        }
 }
 
@@ -2308,7 +2240,7 @@ static void bake_displacement(void *handle, ShadeInput *shi, float dist, int x,
                col[3]= 255;
        }
        if (bs->rect_mask) {
-               bs->rect_mask[bs->rectx*y + x] = BAKE_MASK_BAKED;
+               bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED;
        }
 }
 
@@ -2750,16 +2682,16 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up
                                        char *temprect;
                                        
                                        for(a=0; a<re->r.bake_filter; a++)
-                                               bake_mask_filter_extend((char *)ibuf->userdata, ibuf->x, ibuf->y);
+                                               IMB_mask_filter_extend((char *)ibuf->userdata, ibuf->x, ibuf->y);
                                        
                                        temprect = MEM_dupallocN(ibuf->userdata);
                                        
                                        /* expand twice to clear this many pixels, so they blend back in */
-                                       bake_mask_filter_extend(temprect, ibuf->x, ibuf->y);
-                                       bake_mask_filter_extend(temprect, ibuf->x, ibuf->y);
+                                       IMB_mask_filter_extend(temprect, ibuf->x, ibuf->y);
+                                       IMB_mask_filter_extend(temprect, ibuf->x, ibuf->y);
                                        
                                        /* clear all pixels in the margin*/
-                                       bake_mask_clear(ibuf, temprect, BAKE_MASK_MARGIN);
+                                       IMB_mask_clear(ibuf, temprect, FILTER_MASK_MARGIN);
                                        MEM_freeN(temprect);
                                }