diff options
author | Alex Fraser <alex@phatcore.com> | 2013-01-02 04:05:30 +0400 |
---|---|---|
committer | Alex Fraser <alex@phatcore.com> | 2013-01-02 04:05:30 +0400 |
commit | 5e0e62f0407e15c936bae92d8fec043adee6ab07 (patch) | |
tree | 96bfb5f997b68085ecc1f65cc1ce64b688fd500b /source/blender/render | |
parent | 7085387defb2e9ae90bdf3ed625f056d616fac6b (diff) |
Patch [#29035] Vertex colour baking
There is a new option in the Bake panel to enable baking to vertex colors. Unlike regular baking, this mode does not require a UV map or image to bake to, however the object must have a vertex color layer.
Thanks to:
- AutoCRC for funding
- Brech van Lommel and Dalai Felinto for their initial advice on how to implement it
- Campbell Barton for helping to make this feature work with modifiers and bmesh
Diffstat (limited to 'source/blender/render')
4 files changed, 295 insertions, 46 deletions
diff --git a/source/blender/render/intern/include/renderdatabase.h b/source/blender/render/intern/include/renderdatabase.h index 24989b13c48..1e81ca20d03 100644 --- a/source/blender/render/intern/include/renderdatabase.h +++ b/source/blender/render/intern/include/renderdatabase.h @@ -59,12 +59,16 @@ typedef struct VertTableNode { float *tangent; float *stress; float *winspeed; + /* Index of vertex in source mesh (before modifiers). */ + int *origindex; } VertTableNode; typedef struct VlakTableNode { struct VlakRen *vlak; struct MTFace *mtface; struct MCol *mcol; + /* Index of mpoly in source mesh (before tessellation). */ + int *origindex; int totmtface, totmcol; float *surfnor; float *tangent; @@ -114,9 +118,11 @@ float *RE_vertren_get_rad(struct ObjectRen *obr, struct VertRen *ver, int verify float *RE_vertren_get_strand(struct ObjectRen *obr, struct VertRen *ver, int verify); float *RE_vertren_get_tangent(struct ObjectRen *obr, struct VertRen *ver, int verify); float *RE_vertren_get_winspeed(struct ObjectInstanceRen *obi, struct VertRen *ver, int verify); +int *RE_vertren_get_origindex(struct ObjectRen *obr, VertRen *ver, int verify); struct MTFace *RE_vlakren_get_tface(struct ObjectRen *obr, VlakRen *ren, int n, char **name, int verify); struct MCol *RE_vlakren_get_mcol(struct ObjectRen *obr, VlakRen *ren, int n, char **name, int verify); +int *RE_vlakren_get_origindex(struct ObjectRen *obr, VlakRen *vlak, int verify); float *RE_vlakren_get_surfnor(struct ObjectRen *obr, VlakRen *ren, int verify); float *RE_vlakren_get_nmap_tangent(struct ObjectRen *obr, VlakRen *ren, int verify); RadFace **RE_vlakren_get_radface(struct ObjectRen *obr, VlakRen *ren, int verify); diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 76d67765424..6c5e558e6c7 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -3231,7 +3231,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) CustomDataMask mask; float xn, yn, zn, imat[3][3], mat[4][4]; //nor[3], float *orco=0; - int need_orco=0, need_stress=0, need_nmap_tangent=0, need_tangent=0; + int need_orco=0, need_stress=0, need_nmap_tangent=0, need_tangent=0, need_origindex=0; int a, a1, ok, vertofs; int end, do_autosmooth = FALSE, totvert = 0; int use_original_normals = FALSE; @@ -3281,6 +3281,10 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) need_nmap_tangent= 1; } + /* origindex currently only used when baking to vertex colors */ + if(re->flag & R_BAKING && re->r.bake_flag & R_BAKE_VCOL) + need_origindex= 1; + /* check autosmooth and displacement, we then have to skip only-verts optimize */ do_autosmooth |= (me->flag & ME_AUTOSMOOTH); if (do_autosmooth) @@ -3318,6 +3322,15 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) make_render_halos(re, obr, me, totvert, mvert, ma, orco); } else { + const int *index_vert_orig = NULL; + const int *index_mf_to_mpoly = NULL; + const int *index_mp_to_orig = NULL; + if (need_origindex) { + index_vert_orig = dm->getVertDataArray(dm, CD_ORIGINDEX); + /* double lookup for faces -> polys */ + index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + } for (a=0; a<totvert; a++, mvert++) { ver= RE_findOrAddVert(obr, obr->totvert++); @@ -3334,6 +3347,18 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) ver->orco= orco; orco+=3; } + + if (need_origindex) { + int *origindex; + origindex = RE_vertren_get_origindex(obr, ver, 1); + + /* Use orig index array if it's available (e.g. in the presence + * of modifiers). */ + if (index_vert_orig) + *origindex = index_vert_orig[a]; + else + *origindex = a; + } } if (!timeoffset) { @@ -3455,6 +3480,21 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset) } } } + + if (need_origindex) { + /* Find original index of mpoly for this tessface. Options: + - Modified mesh; two-step look up from tessface -> modified mpoly -> original mpoly + - OR Tesselated mesh; look up from tessface -> mpoly + - OR Failsafe; tessface == mpoly. Could probably assert(false) in this case? */ + int *origindex; + origindex = RE_vlakren_get_origindex(obr, vlr, 1); + if (index_mf_to_mpoly && index_mp_to_orig) + *origindex = DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a); + else if (index_mf_to_mpoly) + *origindex = index_mf_to_mpoly[a]; + else + *origindex = a; + } } } } diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 9b08d6c07e9..90ff96d7a64 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -20,6 +20,7 @@ * * Contributors: Hos, Robert Wenzlaff. * Contributors: 2004/2005/2006 Blender Foundation, full recode + * Contributors: Vertex color baking, Copyright 2011 AutoCRC * * ***** END GPL LICENSE BLOCK ***** */ @@ -51,9 +52,12 @@ #include "DNA_image_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_group_types.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" @@ -2003,6 +2007,12 @@ typedef struct BakeShade { float dir[3]; Object *actob; + + /* Output: vertex color or image data. If vcol is not NULL, rect and + * rect_float should be NULL. */ + MPoly *mpoly; + MLoop *mloop; + MLoopCol *vcol; unsigned int *rect; float *rect_float; @@ -2179,7 +2189,7 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua } } - if (bs->rect_float) { + if (bs->rect_float && !bs->vcol) { float *col= bs->rect_float + 4*(bs->rectx*y + x); copy_v3_v3(col, shr.combined); if (bs->type==RE_BAKE_ALL || bs->type==RE_BAKE_TEXTURE) { @@ -2190,7 +2200,8 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua } } else { - unsigned char *col= (unsigned char *)(bs->rect + bs->rectx*y + x); + /* Target is char (LDR). */ + unsigned char col[4]; if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE)) { float rgb[3]; @@ -2210,6 +2221,19 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua else { col[3]= 255; } + + if (bs->vcol) { + /* Vertex colour baking. Vcol has no useful alpha channel (it exists + * but is used only for vertex painting). */ + bs->vcol->r = col[0]; + bs->vcol->g = col[1]; + bs->vcol->b = col[2]; + } + else { + unsigned char *imcol= (unsigned char *)(bs->rect + bs->rectx*y + x); + copy_v4_v4_char((char *)imcol, (char *)col); + } + } if (bs->rect_mask) { @@ -2229,15 +2253,28 @@ static void bake_displacement(void *handle, ShadeInput *UNUSED(shi), float dist, disp = 0.5f + dist; /* alter the range from [-0.5,0.5] to [0,1]*/ } - if (bs->rect_float) { + if (bs->rect_float && !bs->vcol) { float *col= bs->rect_float + 4*(bs->rectx*y + x); col[0] = col[1] = col[2] = disp; col[3]= 1.0f; } else { - char *col= (char *)(bs->rect + bs->rectx*y + x); + /* Target is char (LDR). */ + unsigned char col[4]; col[0] = col[1] = col[2] = FTOCHAR(disp); - col[3]= 255; + col[3] = 255; + + if(bs->vcol) { + /* Vertex colour baking. Vcol has no useful alpha channel (it exists + * but is used only for vertex painting). */ + bs->vcol->r = col[0]; + bs->vcol->g = col[1]; + bs->vcol->b = col[2]; + } + else { + char *imcol= (char *)(bs->rect + bs->rectx*y + x); + copy_v4_v4_char((char *)imcol, (char *)col); + } } if (bs->rect_mask) { bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED; @@ -2473,13 +2510,55 @@ static int get_next_bake_face(BakeShade *bs) vlr= RE_findOrAddVlak(obr, v); if ((bs->actob && bs->actob == obr->ob) || (!bs->actob && (obr->ob->flag & SELECT))) { - tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0); + if(R.r.bake_flag & R_BAKE_VCOL) { + /* Gather face data for vertex colour bake */ + Mesh *me; + int *origindex, vcollayer; + CustomDataLayer *cdl; + + if(obr->ob->type != OB_MESH) + continue; + me = obr->ob->data; + + origindex = RE_vlakren_get_origindex(obr, vlr, 0); + if(origindex == NULL) + continue; + if (*origindex >= me->totpoly) { + /* Small hack for Array modifier, which gives false + original indices - z0r */ + continue; + } +#if 0 + /* Only shade selected faces. */ + if((me->mface[*origindex].flag & ME_FACE_SEL) == 0) + continue; +#endif + + vcollayer = CustomData_get_render_layer_index(&me->ldata, CD_MLOOPCOL); + if(vcollayer == -1) + continue; + + cdl = &me->ldata.layers[vcollayer]; + bs->mpoly = me->mpoly + *origindex; + bs->vcol = ((MLoopCol*)cdl->data) + bs->mpoly->loopstart; + bs->mloop = me->mloop + bs->mpoly->loopstart; - if (tface && tface->tpage) { - Image *ima= tface->tpage; - ImBuf *ibuf= BKE_image_acquire_ibuf(ima, NULL, NULL); + /* Tag mesh for reevaluation. */ + DAG_id_tag_update(&me->id, 0); + } + else { + Image *ima = NULL; + ImBuf *ibuf = NULL; const float vec_alpha[4]= {0.0f, 0.0f, 0.0f, 0.0f}; const float vec_solid[4]= {0.0f, 0.0f, 0.0f, 1.0f}; + + tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0); + + if (!tface || !tface->tpage) + continue; + + ima = tface->tpage; + ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); if (ibuf==NULL) continue; @@ -2515,20 +2594,17 @@ static int get_next_bake_face(BakeShade *bs) R.bakebuf= ima; } + /* Tag image for redraw. */ ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - - bs->obi= obi; - bs->vlr= vlr; - - bs->vdone++; /* only for error message if nothing was rendered */ - v++; - - BLI_unlock_thread(LOCK_CUSTOM1); - BKE_image_release_ibuf(ima, ibuf, NULL); - - return 1; } + + bs->obi = obi; + bs->vlr = vlr; + bs->vdone++; /* only for error message if nothing was rendered */ + v++; + BLI_unlock_thread(LOCK_CUSTOM1); + return 1; } } } @@ -2537,6 +2613,72 @@ static int get_next_bake_face(BakeShade *bs) return 0; } +static void bake_single_vertex(BakeShade *bs, VertRen *vert, float u, float v) { + int *origindex, i; + MLoopCol *basevcol; + MLoop *mloop; + + origindex = RE_vertren_get_origindex(bs->obi->obr, vert, 0); + if (!origindex || *origindex == ORIGINDEX_NONE) + return; + + /* Search for matching vertex index and apply shading. */ + for (i = 0; i < bs->mpoly->totloop; i++) { + mloop = bs->mloop + i; + if (mloop->v != *origindex) + continue; + basevcol = bs->vcol; + bs->vcol = basevcol + i; + do_bake_shade(bs, 0, 0, u, v); + bs->vcol = basevcol; + break; + } +} + +/* Bake all vertices of a face. Actually, this still works on a face-by-face + basis, and each vertex on each face is shaded. Vertex colors are a property + of loops, not vertices. */ +static void shade_verts(BakeShade *bs) +{ + VlakRen *vlr = bs->vlr; + + /* Disable baking to image; write to vcol instead. vcol pointer is set in + * bake_single_vertex. */ + bs->ima = NULL; + bs->rect = NULL; + bs->rect_float = NULL; + + bs->quad = 0; + + /* No anti-aliasing for vertices. */ + zero_v3(bs->dxco); + zero_v3(bs->dyco); + + /* Shade each vertex of the face. u and v are barycentric coordinates; since + we're only interested in vertices, these will be 0 or 1. */ + if ((vlr->flag & R_FACE_SPLIT) == 0) { + /* Processing triangle face, whole quad, or first half of split quad. */ + + bake_single_vertex(bs, bs->vlr->v1, 1.0f, 0.0f); + bake_single_vertex(bs, bs->vlr->v2, 0.0f, 1.0f); + bake_single_vertex(bs, bs->vlr->v3, 0.0f, 0.0f); + + if (vlr->v4) { + bs->quad = 1; + bake_single_vertex(bs, bs->vlr->v4, 0.0f, 0.0f); + } + } + else { + /* Processing second half of split quad. Only one vertex to go. */ + if (vlr->flag & R_DIVIDE_24) { + bake_single_vertex(bs, bs->vlr->v2, 0.0f, 1.0f); + } + else { + bake_single_vertex(bs, bs->vlr->v3, 0.0f, 0.0f); + } + } +} + /* already have tested for tface and ima and zspan */ static void shade_tface(BakeShade *bs) { @@ -2564,6 +2706,7 @@ static void shade_tface(BakeShade *bs) bs->rect= bs->ibuf->rect; bs->rect_colorspace= bs->ibuf->rect_colorspace; bs->rect_float= bs->ibuf->rect_float; + bs->vcol = NULL; bs->quad= 0; if (bs->use_mask) { @@ -2607,7 +2750,10 @@ static void *do_bake_thread(void *bs_v) BakeShade *bs= bs_v; while (get_next_bake_face(bs)) { - shade_tface(bs); + if (R.r.bake_flag & R_BAKE_VCOL) + shade_verts(bs); + else + shade_tface(bs); /* fast threadsafe break test */ if (R.test_break(R.tbh)) @@ -2671,14 +2817,16 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up use_mask = TRUE; /* baker uses this flag to detect if image was initialized */ - for (ima= G.main->image.first; ima; ima= ima->id.next) { - ImBuf *ibuf= BKE_image_acquire_ibuf(ima, NULL, NULL); - ima->id.flag |= LIB_DOIT; - ima->flag&= ~IMA_USED_FOR_RENDER; - if (ibuf) { - ibuf->userdata = NULL; /* use for masking if needed */ + if ((R.r.bake_flag & R_BAKE_VCOL) == 0) { + for (ima = G.main->image.first; ima; ima = ima->id.next) { + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ima->id.flag |= LIB_DOIT; + ima->flag &= ~IMA_USED_FOR_RENDER; + if (ibuf) { + ibuf->userdata = NULL; /* use for masking if needed */ + } + BKE_image_release_ibuf(ima, ibuf, NULL); } - BKE_image_release_ibuf(ima, ibuf, NULL); } BLI_init_threads(&threads, do_bake_thread, re->r.threads); @@ -2702,7 +2850,10 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up handles[a].type= type; handles[a].actob= actob; - handles[a].zspan= MEM_callocN(sizeof(ZSpan), "zspan for bake"); + if (R.r.bake_flag & R_BAKE_VCOL) + handles[a].zspan = NULL; + else + handles[a].zspan = MEM_callocN(sizeof(ZSpan), "zspan for bake"); handles[a].use_mask = use_mask; @@ -2729,27 +2880,29 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up } /* filter and refresh images */ - for (ima= G.main->image.first; ima; ima= ima->id.next) { - if ((ima->id.flag & LIB_DOIT)==0) { - ImBuf *ibuf= BKE_image_acquire_ibuf(ima, NULL, NULL); + if ((R.r.bake_flag & R_BAKE_VCOL) == 0) { + for (ima = G.main->image.first; ima; ima = ima->id.next) { + if ((ima->id.flag & LIB_DOIT)==0) { + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - if (ima->flag & IMA_USED_FOR_RENDER) - result= BAKE_RESULT_FEEDBACK_LOOP; + if (ima->flag & IMA_USED_FOR_RENDER) + result = BAKE_RESULT_FEEDBACK_LOOP; - if (!ibuf) - continue; + if (!ibuf) + continue; - RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, re->r.bake_filter); + RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, re->r.bake_filter); - ibuf->userflags |= IB_BITMAPDIRTY; - BKE_image_release_ibuf(ima, ibuf, NULL); + ibuf->userflags |= IB_BITMAPDIRTY; + BKE_image_release_ibuf(ima, ibuf, NULL); + } + } + + /* calculate return value */ + for (a = 0; a < re->r.threads; a++) { + zbuf_free_span(handles[a].zspan); + MEM_freeN(handles[a].zspan); } - } - - /* calculate return value */ - for (a=0; a<re->r.threads; a++) { - zbuf_free_span(handles[a].zspan); - MEM_freeN(handles[a].zspan); } MEM_freeN(handles); diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index a4e5b9eb7a2..7ca4f01ae47 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -105,6 +105,8 @@ #define RE_MTFACE_ELEMS 1 #define RE_MCOL_ELEMS 4 #define RE_UV_ELEMS 2 +#define RE_VLAK_ORIGINDEX_ELEMS 1 +#define RE_VERT_ORIGINDEX_ELEMS 1 #define RE_SURFNOR_ELEMS 3 #define RE_RADFACE_ELEMS 1 #define RE_SIMPLIFY_ELEMS 2 @@ -192,10 +194,26 @@ float *RE_vertren_get_winspeed(ObjectInstanceRen *obi, VertRen *ver, int verify) return winspeed + ver->index*RE_WINSPEED_ELEMS; } +int *RE_vertren_get_origindex(ObjectRen *obr, VertRen *ver, int verify) +{ + int *origindex; + int nr= ver->index>>8; + + origindex= obr->vertnodes[nr].origindex; + if (origindex==NULL) { + if (verify) + origindex= obr->vertnodes[nr].origindex= MEM_mallocN(256*RE_VERT_ORIGINDEX_ELEMS*sizeof(int), "origindex table"); + else + return NULL; + } + return origindex + (ver->index & 255)*RE_VERT_ORIGINDEX_ELEMS; +} + VertRen *RE_vertren_copy(ObjectRen *obr, VertRen *ver) { VertRen *v1= RE_findOrAddVert(obr, obr->totvert++); float *fp1, *fp2; + int *int1, *int2; int index= v1->index; *v1= *ver; @@ -221,6 +239,11 @@ VertRen *RE_vertren_copy(ObjectRen *obr, VertRen *ver) fp2= RE_vertren_get_tangent(obr, v1, 1); memcpy(fp2, fp1, RE_TANGENT_ELEMS*sizeof(float)); } + int1= RE_vertren_get_origindex(obr, ver, 0); + if (int1) { + int2= RE_vertren_get_origindex(obr, v1, 1); + memcpy(int2, int1, RE_VERT_ORIGINDEX_ELEMS*sizeof(int)); + } return v1; } @@ -332,6 +355,21 @@ MCol *RE_vlakren_get_mcol(ObjectRen *obr, VlakRen *vlr, int n, char **name, int return node->mcol + index*RE_MCOL_ELEMS; } +int *RE_vlakren_get_origindex(ObjectRen *obr, VlakRen *vlak, int verify) +{ + int *origindex; + int nr= vlak->index>>8; + + origindex= obr->vlaknodes[nr].origindex; + if(origindex==NULL) { + if(verify) + origindex= obr->vlaknodes[nr].origindex= MEM_callocN(256*RE_VLAK_ORIGINDEX_ELEMS*sizeof(int), "origindex table"); + else + return NULL; + } + return origindex + (vlak->index & 255)*RE_VLAK_ORIGINDEX_ELEMS; +} + float *RE_vlakren_get_surfnor(ObjectRen *obr, VlakRen *vlak, int verify) { float *surfnor; @@ -383,6 +421,7 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr) MTFace *mtface, *mtface1; MCol *mcol, *mcol1; float *surfnor, *surfnor1, *tangent, *tangent1; + int *origindex, *origindex1; RadFace **radface, **radface1; int i, index = vlr1->index; char *name; @@ -400,6 +439,13 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr) memcpy(mcol1, mcol, sizeof(MCol)*RE_MCOL_ELEMS); } + origindex= RE_vlakren_get_origindex(obr, vlr, 0); + if(origindex) { + origindex1= RE_vlakren_get_origindex(obr, vlr1, 1); + /* Just an int, but memcpy for consistency. */ + memcpy(origindex1, origindex, sizeof(int)*RE_VLAK_ORIGINDEX_ELEMS); + } + surfnor= RE_vlakren_get_surfnor(obr, vlr, 0); if (surfnor) { surfnor1= RE_vlakren_get_surfnor(obr, vlr1, 1); @@ -725,6 +771,8 @@ void free_renderdata_vertnodes(VertTableNode *vertnodes) MEM_freeN(vertnodes[a].stress); if (vertnodes[a].winspeed) MEM_freeN(vertnodes[a].winspeed); + if (vertnodes[a].origindex) + MEM_freeN(vertnodes[a].origindex); } MEM_freeN(vertnodes); @@ -743,6 +791,8 @@ void free_renderdata_vlaknodes(VlakTableNode *vlaknodes) MEM_freeN(vlaknodes[a].mtface); if (vlaknodes[a].mcol) MEM_freeN(vlaknodes[a].mcol); + if(vlaknodes[a].origindex) + MEM_freeN(vlaknodes[a].origindex); if (vlaknodes[a].surfnor) MEM_freeN(vlaknodes[a].surfnor); if (vlaknodes[a].tangent) |