Bake from multires mesh

=======================

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!
This commit is contained in:
Sergey Sharybin 2011-06-05 20:54:04 +00:00
parent a580b5ec80
commit a1c22262fe
12 changed files with 1472 additions and 157 deletions

@ -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__)

@ -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);

@ -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);

@ -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;

@ -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,

@ -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)

File diff suppressed because it is too large Load Diff

@ -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);

@ -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

@ -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

@ -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);

@ -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);
}