diff options
Diffstat (limited to 'source/blender/editors/object/object_bake_api.c')
-rw-r--r-- | source/blender/editors/object/object_bake_api.c | 2807 |
1 files changed, 1561 insertions, 1246 deletions
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index e3e3f3d10ef..fba5a4e281e 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -21,7 +21,6 @@ * \ingroup edobj */ - #include "MEM_guardedalloc.h" #include "DNA_object_types.h" @@ -77,517 +76,584 @@ static void bake_set_props(wmOperator *op, Scene *scene); typedef struct BakeAPIRender { - Object *ob; - Main *main; - Scene *scene; - ViewLayer *view_layer; - ReportList *reports; - ListBase selected_objects; - - eScenePassType pass_type; - int pass_filter; - int margin; - - int save_mode; - - bool is_clear; - bool is_split_materials; - bool is_automatic_name; - bool is_selected_to_active; - bool is_cage; - - float cage_extrusion; - int normal_space; - eBakeNormalSwizzle normal_swizzle[3]; - - char uv_layer[MAX_CUSTOMDATA_LAYER_NAME]; - char custom_cage[MAX_NAME]; - char filepath[FILE_MAX]; - - int width; - int height; - const char *identifier; - - int result; - bool ready; - - /* callbacks */ - Render *render; - float *progress; - short *do_update; - - /* for redrawing */ - ScrArea *sa; + Object *ob; + Main *main; + Scene *scene; + ViewLayer *view_layer; + ReportList *reports; + ListBase selected_objects; + + eScenePassType pass_type; + int pass_filter; + int margin; + + int save_mode; + + bool is_clear; + bool is_split_materials; + bool is_automatic_name; + bool is_selected_to_active; + bool is_cage; + + float cage_extrusion; + int normal_space; + eBakeNormalSwizzle normal_swizzle[3]; + + char uv_layer[MAX_CUSTOMDATA_LAYER_NAME]; + char custom_cage[MAX_NAME]; + char filepath[FILE_MAX]; + + int width; + int height; + const char *identifier; + + int result; + bool ready; + + /* callbacks */ + Render *render; + float *progress; + short *do_update; + + /* for redrawing */ + ScrArea *sa; } BakeAPIRender; /* callbacks */ static void bake_progress_update(void *bjv, float progress) { - BakeAPIRender *bj = bjv; + BakeAPIRender *bj = bjv; - if (bj->progress && *bj->progress != progress) { - *bj->progress = progress; + if (bj->progress && *bj->progress != progress) { + *bj->progress = progress; - /* make jobs timer to send notifier */ - *(bj->do_update) = true; - } + /* make jobs timer to send notifier */ + *(bj->do_update) = true; + } } /* catch esc */ static int bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { - /* no running blender, remove handler and pass through */ - if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_BAKE)) - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; - - /* running render */ - switch (event->type) { - case ESCKEY: - { - G.is_break = true; - return OPERATOR_RUNNING_MODAL; - } - } - return OPERATOR_PASS_THROUGH; + /* no running blender, remove handler and pass through */ + if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_BAKE)) + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + + /* running render */ + switch (event->type) { + case ESCKEY: { + G.is_break = true; + return OPERATOR_RUNNING_MODAL; + } + } + return OPERATOR_PASS_THROUGH; } /* for exec() when there is no render job * note: this wont check for the escape key being pressed, but doing so isnt threadsafe */ static int bake_break(void *UNUSED(rjv)) { - if (G.is_break) - return 1; - return 0; + if (G.is_break) + return 1; + return 0; } - static void bake_update_image(ScrArea *sa, Image *image) { - if (sa && sa->spacetype == SPACE_IMAGE) { /* in case the user changed while baking */ - SpaceImage *sima = sa->spacedata.first; - if (sima) - sima->image = image; - } + if (sa && sa->spacetype == SPACE_IMAGE) { /* in case the user changed while baking */ + SpaceImage *sima = sa->spacedata.first; + if (sima) + sima->image = image; + } } -static bool write_internal_bake_pixels( - Image *image, BakePixel pixel_array[], float *buffer, - const int width, const int height, const int margin, - const bool is_clear, const bool is_noncolor) +static bool write_internal_bake_pixels(Image *image, + BakePixel pixel_array[], + float *buffer, + const int width, + const int height, + const int margin, + const bool is_clear, + const bool is_noncolor) { - ImBuf *ibuf; - void *lock; - bool is_float; - char *mask_buffer = NULL; - const size_t num_pixels = (size_t)width * (size_t)height; - - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - - if (!ibuf) - return false; - - if (margin > 0 || !is_clear) { - mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); - RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); - } - - is_float = (ibuf->rect_float != NULL); - - /* colormanagement conversions */ - if (!is_noncolor) { - const char *from_colorspace; - const char *to_colorspace; - - from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); - - if (is_float) - to_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); - else - to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); - - if (from_colorspace != to_colorspace) - IMB_colormanagement_transform(buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); - } - - /* populates the ImBuf */ - if (is_clear) { - if (is_float) { - IMB_buffer_float_from_float( - ibuf->rect_float, buffer, ibuf->channels, - IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - else { - IMB_buffer_byte_from_float( - (unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither, - IB_PROFILE_SRGB, IB_PROFILE_SRGB, - false, ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - } - else { - if (is_float) { - IMB_buffer_float_from_float_mask( - ibuf->rect_float, buffer, ibuf->channels, - ibuf->x, ibuf->y, ibuf->x, ibuf->x, mask_buffer); - } - else { - IMB_buffer_byte_from_float_mask( - (unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither, - false, ibuf->x, ibuf->y, ibuf->x, ibuf->x, mask_buffer); - } - } - - /* margins */ - if (margin > 0) - RE_bake_margin(ibuf, mask_buffer, margin); - - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID | IB_BITMAPDIRTY; - - if (ibuf->rect_float) - ibuf->userflags |= IB_RECT_INVALID; - - /* force mipmap recalc */ - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; - imb_freemipmapImBuf(ibuf); - } - - BKE_image_release_ibuf(image, ibuf, NULL); - - if (mask_buffer) - MEM_freeN(mask_buffer); - - return true; + ImBuf *ibuf; + void *lock; + bool is_float; + char *mask_buffer = NULL; + const size_t num_pixels = (size_t)width * (size_t)height; + + ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + + if (!ibuf) + return false; + + if (margin > 0 || !is_clear) { + mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); + RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); + } + + is_float = (ibuf->rect_float != NULL); + + /* colormanagement conversions */ + if (!is_noncolor) { + const char *from_colorspace; + const char *to_colorspace; + + from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + + if (is_float) + to_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); + else + to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); + + if (from_colorspace != to_colorspace) + IMB_colormanagement_transform( + buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); + } + + /* populates the ImBuf */ + if (is_clear) { + if (is_float) { + IMB_buffer_float_from_float(ibuf->rect_float, + buffer, + ibuf->channels, + IB_PROFILE_LINEAR_RGB, + IB_PROFILE_LINEAR_RGB, + false, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x); + } + else { + IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, + buffer, + ibuf->channels, + ibuf->dither, + IB_PROFILE_SRGB, + IB_PROFILE_SRGB, + false, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x); + } + } + else { + if (is_float) { + IMB_buffer_float_from_float_mask(ibuf->rect_float, + buffer, + ibuf->channels, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x, + mask_buffer); + } + else { + IMB_buffer_byte_from_float_mask((unsigned char *)ibuf->rect, + buffer, + ibuf->channels, + ibuf->dither, + false, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x, + mask_buffer); + } + } + + /* margins */ + if (margin > 0) + RE_bake_margin(ibuf, mask_buffer, margin); + + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID | IB_BITMAPDIRTY; + + if (ibuf->rect_float) + ibuf->userflags |= IB_RECT_INVALID; + + /* force mipmap recalc */ + if (ibuf->mipmap[0]) { + ibuf->userflags |= IB_MIPMAP_INVALID; + imb_freemipmapImBuf(ibuf); + } + + BKE_image_release_ibuf(image, ibuf, NULL); + + if (mask_buffer) + MEM_freeN(mask_buffer); + + return true; } /* force OpenGL reload */ static void refresh_images(BakeImages *bake_images) { - int i; - for (i = 0; i < bake_images->size; i++) { - Image *ima = bake_images->data[i].image; - if (ima->ok == IMA_OK_LOADED) { - GPU_free_image(ima); - DEG_id_tag_update(&ima->id, 0); - } - } + int i; + for (i = 0; i < bake_images->size; i++) { + Image *ima = bake_images->data[i].image; + if (ima->ok == IMA_OK_LOADED) { + GPU_free_image(ima); + DEG_id_tag_update(&ima->id, 0); + } + } } -static bool write_external_bake_pixels( - const char *filepath, BakePixel pixel_array[], float *buffer, - const int width, const int height, const int margin, - ImageFormatData *im_format, const bool is_noncolor) +static bool write_external_bake_pixels(const char *filepath, + BakePixel pixel_array[], + float *buffer, + const int width, + const int height, + const int margin, + ImageFormatData *im_format, + const bool is_noncolor) { - ImBuf *ibuf = NULL; - bool ok = false; - bool is_float; - - is_float = im_format->depth > 8; - - /* create a new ImBuf */ - ibuf = IMB_allocImBuf(width, height, im_format->planes, (is_float ? IB_rectfloat : IB_rect)); - - if (!ibuf) - return false; - - /* populates the ImBuf */ - if (is_float) { - IMB_buffer_float_from_float( - ibuf->rect_float, buffer, ibuf->channels, - IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, - ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - else { - if (!is_noncolor) { - const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); - const char *to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); - IMB_colormanagement_transform(buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); - } - - IMB_buffer_byte_from_float( - (unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither, - IB_PROFILE_SRGB, IB_PROFILE_SRGB, - false, ibuf->x, ibuf->y, ibuf->x, ibuf->x); - } - - /* margins */ - if (margin > 0) { - char *mask_buffer = NULL; - const size_t num_pixels = (size_t)width * (size_t)height; - - mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); - RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); - RE_bake_margin(ibuf, mask_buffer, margin); - - if (mask_buffer) - MEM_freeN(mask_buffer); - } - - if ((ok = BKE_imbuf_write(ibuf, filepath, im_format))) { + ImBuf *ibuf = NULL; + bool ok = false; + bool is_float; + + is_float = im_format->depth > 8; + + /* create a new ImBuf */ + ibuf = IMB_allocImBuf(width, height, im_format->planes, (is_float ? IB_rectfloat : IB_rect)); + + if (!ibuf) + return false; + + /* populates the ImBuf */ + if (is_float) { + IMB_buffer_float_from_float(ibuf->rect_float, + buffer, + ibuf->channels, + IB_PROFILE_LINEAR_RGB, + IB_PROFILE_LINEAR_RGB, + false, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x); + } + else { + if (!is_noncolor) { + const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + const char *to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); + IMB_colormanagement_transform( + buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); + } + + IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, + buffer, + ibuf->channels, + ibuf->dither, + IB_PROFILE_SRGB, + IB_PROFILE_SRGB, + false, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x); + } + + /* margins */ + if (margin > 0) { + char *mask_buffer = NULL; + const size_t num_pixels = (size_t)width * (size_t)height; + + mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); + RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); + RE_bake_margin(ibuf, mask_buffer, margin); + + if (mask_buffer) + MEM_freeN(mask_buffer); + } + + if ((ok = BKE_imbuf_write(ibuf, filepath, im_format))) { #ifndef WIN32 - chmod(filepath, S_IRUSR | S_IWUSR); + chmod(filepath, S_IRUSR | S_IWUSR); #endif - //printf("%s saving bake map: '%s'\n", __func__, filepath); - } + //printf("%s saving bake map: '%s'\n", __func__, filepath); + } - /* garbage collection */ - IMB_freeImBuf(ibuf); + /* garbage collection */ + IMB_freeImBuf(ibuf); - return ok; + return ok; } static bool is_noncolor_pass(eScenePassType pass_type) { - return ELEM(pass_type, - SCE_PASS_Z, - SCE_PASS_NORMAL, - SCE_PASS_VECTOR, - SCE_PASS_INDEXOB, - SCE_PASS_UV, - SCE_PASS_RAYHITS, - SCE_PASS_INDEXMA); + return ELEM(pass_type, + SCE_PASS_Z, + SCE_PASS_NORMAL, + SCE_PASS_VECTOR, + SCE_PASS_INDEXOB, + SCE_PASS_UV, + SCE_PASS_RAYHITS, + SCE_PASS_INDEXMA); } /* if all is good tag image and return true */ static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *reports) { - Image *image; - Base *base = BKE_view_layer_base_find(view_layer, ob); - void *lock; - int i; - - if (base == NULL) { - BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not in view layer", ob->id.name + 2); - return false; - } - - if (!(base->flag & BASE_ENABLED_RENDER)) { - BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not enabled for rendering", ob->id.name + 2); - return false; - } - - - if (ob->type != OB_MESH) { - BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh", ob->id.name + 2); - return false; - } - else { - Mesh *me = (Mesh *)ob->data; - - if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) { - BKE_reportf(reports, RPT_ERROR, - "No active UV layer found in the object \"%s\"", ob->id.name + 2); - return false; - } - } - - for (i = 0; i < ob->totcol; i++) { - bNodeTree *ntree = NULL; - bNode *node = NULL; - ED_object_get_active_image(ob, i + 1, &image, NULL, &node, &ntree); - - if (image) { - ImBuf *ibuf; - - if (node) { - if (BKE_node_is_connected_to_output(ntree, node)) { - /* we don't return false since this may be a false positive - * this can't be RPT_ERROR though, otherwise it prevents - * multiple highpoly objects to be baked at once */ - BKE_reportf(reports, RPT_INFO, - "Circular dependency for image \"%s\" from object \"%s\"", - image->id.name + 2, ob->id.name + 2); - } - } - - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - - if (ibuf) { - BKE_image_release_ibuf(image, ibuf, lock); - } - else { - BKE_reportf(reports, RPT_ERROR, - "Uninitialized image \"%s\" from object \"%s\"", - image->id.name + 2, ob->id.name + 2); - - BKE_image_release_ibuf(image, ibuf, lock); - return false; - } - } - else { - Material *mat = give_current_material(ob, i); - if (mat != NULL) { - BKE_reportf(reports, RPT_INFO, - "No active image found in material \"%s\" (%d) for object \"%s\"", - mat->id.name + 2, i, ob->id.name + 2); - } - else { - BKE_reportf(reports, RPT_INFO, - "No active image found in material slot (%d) for object \"%s\"", - i, ob->id.name + 2); - } - continue; - } - - image->id.tag |= LIB_TAG_DOIT; - } - return true; + Image *image; + Base *base = BKE_view_layer_base_find(view_layer, ob); + void *lock; + int i; + + if (base == NULL) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not in view layer", ob->id.name + 2); + return false; + } + + if (!(base->flag & BASE_ENABLED_RENDER)) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not enabled for rendering", ob->id.name + 2); + return false; + } + + if (ob->type != OB_MESH) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh", ob->id.name + 2); + return false; + } + else { + Mesh *me = (Mesh *)ob->data; + + if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) { + BKE_reportf( + reports, RPT_ERROR, "No active UV layer found in the object \"%s\"", ob->id.name + 2); + return false; + } + } + + for (i = 0; i < ob->totcol; i++) { + bNodeTree *ntree = NULL; + bNode *node = NULL; + ED_object_get_active_image(ob, i + 1, &image, NULL, &node, &ntree); + + if (image) { + ImBuf *ibuf; + + if (node) { + if (BKE_node_is_connected_to_output(ntree, node)) { + /* we don't return false since this may be a false positive + * this can't be RPT_ERROR though, otherwise it prevents + * multiple highpoly objects to be baked at once */ + BKE_reportf(reports, + RPT_INFO, + "Circular dependency for image \"%s\" from object \"%s\"", + image->id.name + 2, + ob->id.name + 2); + } + } + + ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + + if (ibuf) { + BKE_image_release_ibuf(image, ibuf, lock); + } + else { + BKE_reportf(reports, + RPT_ERROR, + "Uninitialized image \"%s\" from object \"%s\"", + image->id.name + 2, + ob->id.name + 2); + + BKE_image_release_ibuf(image, ibuf, lock); + return false; + } + } + else { + Material *mat = give_current_material(ob, i); + if (mat != NULL) { + BKE_reportf(reports, + RPT_INFO, + "No active image found in material \"%s\" (%d) for object \"%s\"", + mat->id.name + 2, + i, + ob->id.name + 2); + } + else { + BKE_reportf(reports, + RPT_INFO, + "No active image found in material slot (%d) for object \"%s\"", + i, + ob->id.name + 2); + } + continue; + } + + image->id.tag |= LIB_TAG_DOIT; + } + return true; } -static bool bake_pass_filter_check(eScenePassType pass_type, const int pass_filter, ReportList *reports) +static bool bake_pass_filter_check(eScenePassType pass_type, + const int pass_filter, + ReportList *reports) { - switch (pass_type) { - case SCE_PASS_COMBINED: - if ((pass_filter & R_BAKE_PASS_FILTER_EMIT) != 0) { - return true; - } - - if (((pass_filter & R_BAKE_PASS_FILTER_DIRECT) != 0) || - ((pass_filter & R_BAKE_PASS_FILTER_INDIRECT) != 0)) - { - if (((pass_filter & R_BAKE_PASS_FILTER_DIFFUSE) != 0) || - ((pass_filter & R_BAKE_PASS_FILTER_GLOSSY) != 0) || - ((pass_filter & R_BAKE_PASS_FILTER_TRANSM) != 0) || - ((pass_filter & R_BAKE_PASS_FILTER_SUBSURFACE) != 0)) - { - return true; - } - - if ((pass_filter & R_BAKE_PASS_FILTER_AO) != 0) { - BKE_report(reports, RPT_ERROR, - "Combined bake pass Ambient Occlusion contribution requires an enabled light pass " - "(bake the Ambient Occlusion pass type instead)"); - } - else { - BKE_report(reports, RPT_ERROR, - "Combined bake pass requires Emit, or a light pass with " - "Direct or Indirect contributions enabled"); - } - - return false; - } - else { - BKE_report(reports, RPT_ERROR, - "Combined bake pass requires Emit, or a light pass with " - "Direct or Indirect contributions enabled"); - return false; - } - break; - case SCE_PASS_DIFFUSE_COLOR: - case SCE_PASS_GLOSSY_COLOR: - case SCE_PASS_TRANSM_COLOR: - case SCE_PASS_SUBSURFACE_COLOR: - if (((pass_filter & R_BAKE_PASS_FILTER_COLOR) != 0) || - ((pass_filter & R_BAKE_PASS_FILTER_DIRECT) != 0) || - ((pass_filter & R_BAKE_PASS_FILTER_INDIRECT) != 0)) - { - return true; - } - else { - BKE_report(reports, RPT_ERROR, - "Bake pass requires Direct, Indirect, or Color contributions to be enabled"); - return false; - } - break; - default: - return true; - break; - } + switch (pass_type) { + case SCE_PASS_COMBINED: + if ((pass_filter & R_BAKE_PASS_FILTER_EMIT) != 0) { + return true; + } + + if (((pass_filter & R_BAKE_PASS_FILTER_DIRECT) != 0) || + ((pass_filter & R_BAKE_PASS_FILTER_INDIRECT) != 0)) { + if (((pass_filter & R_BAKE_PASS_FILTER_DIFFUSE) != 0) || + ((pass_filter & R_BAKE_PASS_FILTER_GLOSSY) != 0) || + ((pass_filter & R_BAKE_PASS_FILTER_TRANSM) != 0) || + ((pass_filter & R_BAKE_PASS_FILTER_SUBSURFACE) != 0)) { + return true; + } + + if ((pass_filter & R_BAKE_PASS_FILTER_AO) != 0) { + BKE_report( + reports, + RPT_ERROR, + "Combined bake pass Ambient Occlusion contribution requires an enabled light pass " + "(bake the Ambient Occlusion pass type instead)"); + } + else { + BKE_report(reports, + RPT_ERROR, + "Combined bake pass requires Emit, or a light pass with " + "Direct or Indirect contributions enabled"); + } + + return false; + } + else { + BKE_report(reports, + RPT_ERROR, + "Combined bake pass requires Emit, or a light pass with " + "Direct or Indirect contributions enabled"); + return false; + } + break; + case SCE_PASS_DIFFUSE_COLOR: + case SCE_PASS_GLOSSY_COLOR: + case SCE_PASS_TRANSM_COLOR: + case SCE_PASS_SUBSURFACE_COLOR: + if (((pass_filter & R_BAKE_PASS_FILTER_COLOR) != 0) || + ((pass_filter & R_BAKE_PASS_FILTER_DIRECT) != 0) || + ((pass_filter & R_BAKE_PASS_FILTER_INDIRECT) != 0)) { + return true; + } + else { + BKE_report(reports, + RPT_ERROR, + "Bake pass requires Direct, Indirect, or Color contributions to be enabled"); + return false; + } + break; + default: + return true; + break; + } } /* before even getting in the bake function we check for some basic errors */ -static bool bake_objects_check(Main *bmain, ViewLayer *view_layer, Object *ob, ListBase *selected_objects, - ReportList *reports, const bool is_selected_to_active) +static bool bake_objects_check(Main *bmain, + ViewLayer *view_layer, + Object *ob, + ListBase *selected_objects, + ReportList *reports, + const bool is_selected_to_active) { - CollectionPointerLink *link; - - /* error handling and tag (in case multiple materials share the same image) */ - BKE_main_id_tag_idcode(bmain, ID_IM, LIB_TAG_DOIT, false); - - if (is_selected_to_active) { - int tot_objects = 0; - - if (!bake_object_check(view_layer, ob, reports)) - return false; - - for (link = selected_objects->first; link; link = link->next) { - Object *ob_iter = (Object *)link->ptr.data; - - if (ob_iter == ob) - continue; - - if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { - BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, Surface or Metaball)", ob_iter->id.name + 2); - return false; - } - tot_objects += 1; - } - - if (tot_objects == 0) { - BKE_report(reports, RPT_ERROR, "No valid selected objects"); - return false; - } - } - else { - if (BLI_listbase_is_empty(selected_objects)) { - BKE_report(reports, RPT_ERROR, "No valid selected objects"); - return false; - } - - for (link = selected_objects->first; link; link = link->next) { - if (!bake_object_check(view_layer, link->ptr.data, reports)) - return false; - } - } - return true; + CollectionPointerLink *link; + + /* error handling and tag (in case multiple materials share the same image) */ + BKE_main_id_tag_idcode(bmain, ID_IM, LIB_TAG_DOIT, false); + + if (is_selected_to_active) { + int tot_objects = 0; + + if (!bake_object_check(view_layer, ob, reports)) + return false; + + for (link = selected_objects->first; link; link = link->next) { + Object *ob_iter = (Object *)link->ptr.data; + + if (ob_iter == ob) + continue; + + if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { + BKE_reportf(reports, + RPT_ERROR, + "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, " + "Surface or Metaball)", + ob_iter->id.name + 2); + return false; + } + tot_objects += 1; + } + + if (tot_objects == 0) { + BKE_report(reports, RPT_ERROR, "No valid selected objects"); + return false; + } + } + else { + if (BLI_listbase_is_empty(selected_objects)) { + BKE_report(reports, RPT_ERROR, "No valid selected objects"); + return false; + } + + for (link = selected_objects->first; link; link = link->next) { + if (!bake_object_check(view_layer, link->ptr.data, reports)) + return false; + } + } + return true; } /* it needs to be called after bake_objects_check since the image tagging happens there */ static void bake_images_clear(Main *bmain, const bool is_tangent) { - Image *image; - for (image = bmain->images.first; image; image = image->id.next) { - if ((image->id.tag & LIB_TAG_DOIT) != 0) { - RE_bake_ibuf_clear(image, is_tangent); - } - } + Image *image; + for (image = bmain->images.first; image; image = image->id.next) { + if ((image->id.tag & LIB_TAG_DOIT) != 0) { + RE_bake_ibuf_clear(image, is_tangent); + } + } } static void build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images) { - const int tot_mat = ob->totcol; - int i, j; - int tot_images = 0; - - /* error handling and tag (in case multiple materials share the same image) */ - BKE_main_id_tag_idcode(bmain, ID_IM, LIB_TAG_DOIT, false); - - for (i = 0; i < tot_mat; i++) { - Image *image; - ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); - - /* Some materials have no image, we just ignore those cases. */ - if (image == NULL) { - bake_images->lookup[i] = -1; - } - else if (image->id.tag & LIB_TAG_DOIT) { - for (j = 0; j < i; j++) { - if (bake_images->data[j].image == image) { - bake_images->lookup[i] = j; - break; - } - } - } - else { - bake_images->lookup[i] = tot_images; - bake_images->data[tot_images].image = image; - image->id.tag |= LIB_TAG_DOIT; - tot_images++; - } - } - - bake_images->size = tot_images; + const int tot_mat = ob->totcol; + int i, j; + int tot_images = 0; + + /* error handling and tag (in case multiple materials share the same image) */ + BKE_main_id_tag_idcode(bmain, ID_IM, LIB_TAG_DOIT, false); + + for (i = 0; i < tot_mat; i++) { + Image *image; + ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); + + /* Some materials have no image, we just ignore those cases. */ + if (image == NULL) { + bake_images->lookup[i] = -1; + } + else if (image->id.tag & LIB_TAG_DOIT) { + for (j = 0; j < i; j++) { + if (bake_images->data[j].image == image) { + bake_images->lookup[i] = j; + break; + } + } + } + else { + bake_images->lookup[i] = tot_images; + bake_images->data[tot_images].image = image; + image->id.tag |= LIB_TAG_DOIT; + tot_images++; + } + } + + bake_images->size = tot_images; } /* @@ -595,881 +661,1130 @@ static void build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images) */ static size_t initialize_internal_images(BakeImages *bake_images, ReportList *reports) { - int i; - size_t tot_size = 0; - - for (i = 0; i < bake_images->size; i++) { - ImBuf *ibuf; - void *lock; - - BakeImage *bk_image = &bake_images->data[i]; - ibuf = BKE_image_acquire_ibuf(bk_image->image, NULL, &lock); - - if (ibuf) { - bk_image->width = ibuf->x; - bk_image->height = ibuf->y; - bk_image->offset = tot_size; - - tot_size += (size_t)ibuf->x * (size_t)ibuf->y; - } - else { - BKE_image_release_ibuf(bk_image->image, ibuf, lock); - BKE_reportf(reports, RPT_ERROR, "Uninitialized image %s", bk_image->image->id.name + 2); - return 0; - } - BKE_image_release_ibuf(bk_image->image, ibuf, lock); - } - return tot_size; + int i; + size_t tot_size = 0; + + for (i = 0; i < bake_images->size; i++) { + ImBuf *ibuf; + void *lock; + + BakeImage *bk_image = &bake_images->data[i]; + ibuf = BKE_image_acquire_ibuf(bk_image->image, NULL, &lock); + + if (ibuf) { + bk_image->width = ibuf->x; + bk_image->height = ibuf->y; + bk_image->offset = tot_size; + + tot_size += (size_t)ibuf->x * (size_t)ibuf->y; + } + else { + BKE_image_release_ibuf(bk_image->image, ibuf, lock); + BKE_reportf(reports, RPT_ERROR, "Uninitialized image %s", bk_image->image->id.name + 2); + return 0; + } + BKE_image_release_ibuf(bk_image->image, ibuf, lock); + } + return tot_size; } /* create new mesh with edit mode changes and modifiers applied */ static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph, Main *bmain, Scene *scene, Object *ob) { - bool apply_modifiers = (ob->type != OB_MESH); - Mesh *me = BKE_mesh_new_from_object(depsgraph, bmain, scene, ob, apply_modifiers, false); + bool apply_modifiers = (ob->type != OB_MESH); + Mesh *me = BKE_mesh_new_from_object(depsgraph, bmain, scene, ob, apply_modifiers, false); - if (me->flag & ME_AUTOSMOOTH) { - BKE_mesh_split_faces(me, true); - } + if (me->flag & ME_AUTOSMOOTH) { + BKE_mesh_split_faces(me, true); + } - return me; + return me; } -static int bake( - Render *re, Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob_low, ListBase *selected_objects, - ReportList *reports, - const eScenePassType pass_type, const int pass_filter, const int margin, - const eBakeSaveMode save_mode, const bool is_clear, const bool is_split_materials, - const bool is_automatic_name, const bool is_selected_to_active, const bool is_cage, - const float cage_extrusion, const int normal_space, const eBakeNormalSwizzle normal_swizzle[], - const char *custom_cage, const char *filepath, const int width, const int height, - const char *identifier, ScrArea *sa, const char *uv_layer) +static int bake(Render *re, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Object *ob_low, + ListBase *selected_objects, + ReportList *reports, + const eScenePassType pass_type, + const int pass_filter, + const int margin, + const eBakeSaveMode save_mode, + const bool is_clear, + const bool is_split_materials, + const bool is_automatic_name, + const bool is_selected_to_active, + const bool is_cage, + const float cage_extrusion, + const int normal_space, + const eBakeNormalSwizzle normal_swizzle[], + const char *custom_cage, + const char *filepath, + const int width, + const int height, + const char *identifier, + ScrArea *sa, + const char *uv_layer) { - /* We build a depsgraph for the baking, - * so we don't need to change the original data to adjust visibility and modifiers. */ - Depsgraph *depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER); - DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); - - int op_result = OPERATOR_CANCELLED; - bool ok = false; - - Object *ob_cage = NULL; - Object *ob_cage_eval = NULL; - Object *ob_low_eval = NULL; - - BakeHighPolyData *highpoly = NULL; - int tot_highpoly = 0; - - Mesh *me_low = NULL; - Mesh *me_cage = NULL; - - MultiresModifierData *mmd_low = NULL; - int mmd_flags_low = 0; - - float *result = NULL; - - BakePixel *pixel_array_low = NULL; - BakePixel *pixel_array_high = NULL; - - const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL); - const bool is_noncolor = is_noncolor_pass(pass_type); - const int depth = RE_pass_depth(pass_type); - - BakeImages bake_images = {NULL}; - - size_t num_pixels; - int tot_materials; - - RE_bake_engine_set_engine_parameters(re, bmain, scene); - - if (!RE_bake_has_engine(re)) { - BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); - goto cleanup; - } - - tot_materials = ob_low->totcol; - - if (uv_layer && uv_layer[0] != '\0') { - Mesh *me = (Mesh *)ob_low->data; - if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) { - BKE_reportf(reports, RPT_ERROR, - "No UV layer named \"%s\" found in the object \"%s\"", uv_layer, ob_low->id.name + 2); - goto cleanup; - } - } - - if (tot_materials == 0) { - if (is_save_internal) { - BKE_report(reports, RPT_ERROR, - "No active image found, add a material or bake to an external file"); - - goto cleanup; - } - else if (is_split_materials) { - BKE_report(reports, RPT_ERROR, - "No active image found, add a material or bake without the Split Materials option"); - - goto cleanup; - } - else { - /* baking externally without splitting materials */ - tot_materials = 1; - } - } - - /* we overallocate in case there is more materials than images */ - bake_images.data = MEM_mallocN(sizeof(BakeImage) * tot_materials, "bake images dimensions (width, height, offset)"); - bake_images.lookup = MEM_mallocN(sizeof(int) * tot_materials, "bake images lookup (from material to BakeImage)"); - - build_image_lookup(bmain, ob_low, &bake_images); - - if (is_save_internal) { - num_pixels = initialize_internal_images(&bake_images, reports); - - if (num_pixels == 0) { - goto cleanup; - } - } - else { - /* when saving externally always use the size specified in the UI */ - - num_pixels = (size_t)width * (size_t)height * bake_images.size; - - for (int i = 0; i < bake_images.size; i++) { - bake_images.data[i].width = width; - bake_images.data[i].height = height; - bake_images.data[i].offset = (is_split_materials ? num_pixels : 0); - bake_images.data[i].image = NULL; - } - - if (!is_split_materials) { - /* saving a single image */ - for (int i = 0; i < tot_materials; i++) { - bake_images.lookup[i] = 0; - } - } - } - - if (is_selected_to_active) { - CollectionPointerLink *link; - tot_highpoly = 0; - - for (link = selected_objects->first; link; link = link->next) { - Object *ob_iter = link->ptr.data; - - if (ob_iter == ob_low) - continue; - - tot_highpoly ++; - } - - if (is_cage && custom_cage[0] != '\0') { - ob_cage = BLI_findstring(&bmain->objects, custom_cage, offsetof(ID, name) + 2); - - if (ob_cage == NULL || ob_cage->type != OB_MESH) { - BKE_report(reports, RPT_ERROR, "No valid cage object"); - goto cleanup; - } - else { - ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); - ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; - ob_cage_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); - } - } - } - - pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); - pixel_array_high = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); - result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); - - /* for multires bake, use linear UV subdivision to match low res UVs */ - if (pass_type == SCE_PASS_NORMAL && normal_space == R_BAKE_SPACE_TANGENT && !is_selected_to_active) { - mmd_low = (MultiresModifierData *) modifiers_findByType(ob_low, eModifierType_Multires); - if (mmd_low) { - mmd_flags_low = mmd_low->flags; - mmd_low->uv_smooth = SUBSURF_UV_SMOOTH_NONE; - } - } - - /* Make sure depsgraph is up to date. */ - BKE_scene_graph_update_tagged(depsgraph, bmain); - ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low); - - /* get the mesh as it arrives in the renderer */ - me_low = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval); - - /* populate the pixel array with the face data */ - if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) - RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images, uv_layer); - /* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh) */ - - if (is_selected_to_active) { - CollectionPointerLink *link; - int i = 0; - - /* prepare cage mesh */ - if (ob_cage) { - me_cage = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_cage_eval); - if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) { - BKE_report(reports, RPT_ERROR, - "Invalid cage object, the cage mesh must have the same number " - "of faces as the active object"); - goto cleanup; - } - } - else if (is_cage) { - BKE_object_eval_reset(ob_low_eval); - - ModifierData *md = ob_low_eval->modifiers.first; - while (md) { - ModifierData *md_next = md->next; - - /* Edge Split cannot be applied in the cage, - * the cage is supposed to have interpolated normals - * between the faces unless the geometry is physically - * split. So we create a copy of the low poly mesh without - * the eventual edge split.*/ - - if (md->type == eModifierType_EdgeSplit) { - BLI_remlink(&ob_low_eval->modifiers, md); - modifier_free(md); - } - md = md_next; - } - - me_cage = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval); - RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); - } - - highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); - - /* populate highpoly array */ - for (link = selected_objects->first; link; link = link->next) { - Object *ob_iter = link->ptr.data; - - if (ob_iter == ob_low) - continue; - - /* initialize highpoly_data */ - highpoly[i].ob = ob_iter; - highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); - highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; - highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE | BASE_ENABLED_RENDER); - highpoly[i].me = bake_mesh_new_from_object(depsgraph, bmain, scene, highpoly[i].ob_eval); - - /* lowpoly to highpoly transformation matrix */ - copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); - invert_m4_m4(highpoly[i].imat, highpoly[i].obmat); - - highpoly[i].is_flip_object = is_negative_m4(highpoly[i].ob->obmat); - - i++; - } - - BLI_assert(i == tot_highpoly); - - - if (ob_cage != NULL) { - ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; - ob_cage_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); - } - ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; - ob_low_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); - - /* populate the pixel arrays with the corresponding face data for each high poly object */ - if (!RE_bake_pixels_populate_from_objects( - me_low, pixel_array_low, pixel_array_high, highpoly, tot_highpoly, num_pixels, ob_cage != NULL, - cage_extrusion, ob_low_eval->obmat, (ob_cage ? ob_cage->obmat : ob_low_eval->obmat), me_cage)) - { - BKE_report(reports, RPT_ERROR, "Error handling selected objects"); - goto cleanup; - } - - /* the baking itself */ - for (i = 0; i < tot_highpoly; i++) { - ok = RE_bake_engine(re, depsgraph, highpoly[i].ob, i, pixel_array_high, - num_pixels, depth, pass_type, pass_filter, result); - if (!ok) { - BKE_reportf(reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2); - goto cleanup; - } - } - } - else { - /* If low poly is not renderable it should have failed long ago. */ - BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0); - - if (RE_bake_has_engine(re)) { - ok = RE_bake_engine(re, depsgraph, ob_low_eval, 0, pixel_array_low, num_pixels, depth, pass_type, pass_filter, result); - } - else { - BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); - goto cleanup; - } - } - - /* normal space conversion - * the normals are expected to be in world space, +X +Y +Z */ - if (ok && pass_type == SCE_PASS_NORMAL) { - switch (normal_space) { - case R_BAKE_SPACE_WORLD: - { - /* Cycles internal format */ - if ((normal_swizzle[0] == R_BAKE_POSX) && - (normal_swizzle[1] == R_BAKE_POSY) && - (normal_swizzle[2] == R_BAKE_POSZ)) - { - break; - } - else { - RE_bake_normal_world_to_world(pixel_array_low, num_pixels, depth, result, normal_swizzle); - } - break; - } - case R_BAKE_SPACE_OBJECT: - { - RE_bake_normal_world_to_object(pixel_array_low, num_pixels, depth, result, ob_low_eval, normal_swizzle); - break; - } - case R_BAKE_SPACE_TANGENT: - { - if (is_selected_to_active) { - RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_low, normal_swizzle, ob_low_eval->obmat); - } - else { - /* from multiresolution */ - Mesh *me_nores = NULL; - ModifierData *md = NULL; - int mode; - - BKE_object_eval_reset(ob_low_eval); - md = modifiers_findByType(ob_low_eval, eModifierType_Multires); - - if (md) { - mode = md->mode; - md->mode &= ~eModifierMode_Render; - } - - /* Evaluate modifiers again. */ - me_nores = BKE_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval, true, false); - RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer); - - RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle, ob_low_eval->obmat); - BKE_id_free(bmain, me_nores); - - if (md) - md->mode = mode; - } - break; - } - default: - break; - } - } - - if (!ok) { - BKE_reportf(reports, RPT_ERROR, "Problem baking object \"%s\"", ob_low->id.name + 2); - op_result = OPERATOR_CANCELLED; - } - else { - /* save the results */ - for (int i = 0; i < bake_images.size; i++) { - BakeImage *bk_image = &bake_images.data[i]; - - if (is_save_internal) { - ok = write_internal_bake_pixels( - bk_image->image, - pixel_array_low + bk_image->offset, - result + bk_image->offset * depth, - bk_image->width, bk_image->height, - margin, is_clear, is_noncolor); - - /* might be read by UI to set active image for display */ - bake_update_image(sa, bk_image->image); - - if (!ok) { - BKE_reportf(reports, RPT_ERROR, - "Problem saving the bake map internally for object \"%s\"", ob_low->id.name + 2); - op_result = OPERATOR_CANCELLED; - } - else { - BKE_report(reports, RPT_INFO, - "Baking map saved to internal image, save it externally or pack it"); - op_result = OPERATOR_FINISHED; - } - } - /* save externally */ - else { - BakeData *bake = &scene->r.bake; - char name[FILE_MAX]; - - BKE_image_path_from_imtype(name, filepath, BKE_main_blendfile_path(bmain), - 0, bake->im_format.imtype, true, false, NULL); - - if (is_automatic_name) { - BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); - BLI_path_suffix(name, FILE_MAX, identifier, "_"); - } - - if (is_split_materials) { - if (bk_image->image) { - BLI_path_suffix(name, FILE_MAX, bk_image->image->id.name + 2, "_"); - } - else { - if (ob_low_eval->mat[i]) { - BLI_path_suffix(name, FILE_MAX, ob_low_eval->mat[i]->id.name + 2, "_"); - } - else if (me_low->mat[i]) { - BLI_path_suffix(name, FILE_MAX, me_low->mat[i]->id.name + 2, "_"); - } - else { - /* if everything else fails, use the material index */ - char tmp[5]; - sprintf(tmp, "%d", i % 1000); - BLI_path_suffix(name, FILE_MAX, tmp, "_"); - } - } - } - - /* save it externally */ - ok = write_external_bake_pixels( - name, - pixel_array_low + bk_image->offset, - result + bk_image->offset * depth, - bk_image->width, bk_image->height, - margin, &bake->im_format, is_noncolor); - - if (!ok) { - BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name); - op_result = OPERATOR_CANCELLED; - } - else { - BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\"", name); - op_result = OPERATOR_FINISHED; - } - - if (!is_split_materials) { - break; - } - } - } - } - - if (is_save_internal) - refresh_images(&bake_images); + /* We build a depsgraph for the baking, + * so we don't need to change the original data to adjust visibility and modifiers. */ + Depsgraph *depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER); + DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); + + int op_result = OPERATOR_CANCELLED; + bool ok = false; + + Object *ob_cage = NULL; + Object *ob_cage_eval = NULL; + Object *ob_low_eval = NULL; + + BakeHighPolyData *highpoly = NULL; + int tot_highpoly = 0; + + Mesh *me_low = NULL; + Mesh *me_cage = NULL; + + MultiresModifierData *mmd_low = NULL; + int mmd_flags_low = 0; + + float *result = NULL; + + BakePixel *pixel_array_low = NULL; + BakePixel *pixel_array_high = NULL; + + const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL); + const bool is_noncolor = is_noncolor_pass(pass_type); + const int depth = RE_pass_depth(pass_type); + + BakeImages bake_images = {NULL}; + + size_t num_pixels; + int tot_materials; + + RE_bake_engine_set_engine_parameters(re, bmain, scene); + + if (!RE_bake_has_engine(re)) { + BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); + goto cleanup; + } + + tot_materials = ob_low->totcol; + + if (uv_layer && uv_layer[0] != '\0') { + Mesh *me = (Mesh *)ob_low->data; + if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) { + BKE_reportf(reports, + RPT_ERROR, + "No UV layer named \"%s\" found in the object \"%s\"", + uv_layer, + ob_low->id.name + 2); + goto cleanup; + } + } + + if (tot_materials == 0) { + if (is_save_internal) { + BKE_report( + reports, RPT_ERROR, "No active image found, add a material or bake to an external file"); + + goto cleanup; + } + else if (is_split_materials) { + BKE_report( + reports, + RPT_ERROR, + "No active image found, add a material or bake without the Split Materials option"); + + goto cleanup; + } + else { + /* baking externally without splitting materials */ + tot_materials = 1; + } + } + + /* we overallocate in case there is more materials than images */ + bake_images.data = MEM_mallocN(sizeof(BakeImage) * tot_materials, + "bake images dimensions (width, height, offset)"); + bake_images.lookup = MEM_mallocN(sizeof(int) * tot_materials, + "bake images lookup (from material to BakeImage)"); + + build_image_lookup(bmain, ob_low, &bake_images); + + if (is_save_internal) { + num_pixels = initialize_internal_images(&bake_images, reports); + + if (num_pixels == 0) { + goto cleanup; + } + } + else { + /* when saving externally always use the size specified in the UI */ + + num_pixels = (size_t)width * (size_t)height * bake_images.size; + + for (int i = 0; i < bake_images.size; i++) { + bake_images.data[i].width = width; + bake_images.data[i].height = height; + bake_images.data[i].offset = (is_split_materials ? num_pixels : 0); + bake_images.data[i].image = NULL; + } + + if (!is_split_materials) { + /* saving a single image */ + for (int i = 0; i < tot_materials; i++) { + bake_images.lookup[i] = 0; + } + } + } + + if (is_selected_to_active) { + CollectionPointerLink *link; + tot_highpoly = 0; + + for (link = selected_objects->first; link; link = link->next) { + Object *ob_iter = link->ptr.data; + + if (ob_iter == ob_low) + continue; + + tot_highpoly++; + } + + if (is_cage && custom_cage[0] != '\0') { + ob_cage = BLI_findstring(&bmain->objects, custom_cage, offsetof(ID, name) + 2); + + if (ob_cage == NULL || ob_cage->type != OB_MESH) { + BKE_report(reports, RPT_ERROR, "No valid cage object"); + goto cleanup; + } + else { + ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); + ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_cage_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); + } + } + } + + pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); + pixel_array_high = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); + result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); + + /* for multires bake, use linear UV subdivision to match low res UVs */ + if (pass_type == SCE_PASS_NORMAL && normal_space == R_BAKE_SPACE_TANGENT && + !is_selected_to_active) { + mmd_low = (MultiresModifierData *)modifiers_findByType(ob_low, eModifierType_Multires); + if (mmd_low) { + mmd_flags_low = mmd_low->flags; + mmd_low->uv_smooth = SUBSURF_UV_SMOOTH_NONE; + } + } + + /* Make sure depsgraph is up to date. */ + BKE_scene_graph_update_tagged(depsgraph, bmain); + ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low); + + /* get the mesh as it arrives in the renderer */ + me_low = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval); + + /* populate the pixel array with the face data */ + if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) + RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images, uv_layer); + /* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh) */ + + if (is_selected_to_active) { + CollectionPointerLink *link; + int i = 0; + + /* prepare cage mesh */ + if (ob_cage) { + me_cage = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_cage_eval); + if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) { + BKE_report(reports, + RPT_ERROR, + "Invalid cage object, the cage mesh must have the same number " + "of faces as the active object"); + goto cleanup; + } + } + else if (is_cage) { + BKE_object_eval_reset(ob_low_eval); + + ModifierData *md = ob_low_eval->modifiers.first; + while (md) { + ModifierData *md_next = md->next; + + /* Edge Split cannot be applied in the cage, + * the cage is supposed to have interpolated normals + * between the faces unless the geometry is physically + * split. So we create a copy of the low poly mesh without + * the eventual edge split.*/ + + if (md->type == eModifierType_EdgeSplit) { + BLI_remlink(&ob_low_eval->modifiers, md); + modifier_free(md); + } + md = md_next; + } + + me_cage = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval); + RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); + } + + highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); + + /* populate highpoly array */ + for (link = selected_objects->first; link; link = link->next) { + Object *ob_iter = link->ptr.data; + + if (ob_iter == ob_low) + continue; + + /* initialize highpoly_data */ + highpoly[i].ob = ob_iter; + highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); + highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; + highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE | BASE_ENABLED_RENDER); + highpoly[i].me = bake_mesh_new_from_object(depsgraph, bmain, scene, highpoly[i].ob_eval); + + /* lowpoly to highpoly transformation matrix */ + copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); + invert_m4_m4(highpoly[i].imat, highpoly[i].obmat); + + highpoly[i].is_flip_object = is_negative_m4(highpoly[i].ob->obmat); + + i++; + } + + BLI_assert(i == tot_highpoly); + + if (ob_cage != NULL) { + ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_cage_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); + } + ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; + ob_low_eval->base_flag &= ~(BASE_VISIBLE | BASE_ENABLED_RENDER); + + /* populate the pixel arrays with the corresponding face data for each high poly object */ + if (!RE_bake_pixels_populate_from_objects(me_low, + pixel_array_low, + pixel_array_high, + highpoly, + tot_highpoly, + num_pixels, + ob_cage != NULL, + cage_extrusion, + ob_low_eval->obmat, + (ob_cage ? ob_cage->obmat : ob_low_eval->obmat), + me_cage)) { + BKE_report(reports, RPT_ERROR, "Error handling selected objects"); + goto cleanup; + } + + /* the baking itself */ + for (i = 0; i < tot_highpoly; i++) { + ok = RE_bake_engine(re, + depsgraph, + highpoly[i].ob, + i, + pixel_array_high, + num_pixels, + depth, + pass_type, + pass_filter, + result); + if (!ok) { + BKE_reportf( + reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2); + goto cleanup; + } + } + } + else { + /* If low poly is not renderable it should have failed long ago. */ + BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0); + + if (RE_bake_has_engine(re)) { + ok = RE_bake_engine(re, + depsgraph, + ob_low_eval, + 0, + pixel_array_low, + num_pixels, + depth, + pass_type, + pass_filter, + result); + } + else { + BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); + goto cleanup; + } + } + + /* normal space conversion + * the normals are expected to be in world space, +X +Y +Z */ + if (ok && pass_type == SCE_PASS_NORMAL) { + switch (normal_space) { + case R_BAKE_SPACE_WORLD: { + /* Cycles internal format */ + if ((normal_swizzle[0] == R_BAKE_POSX) && (normal_swizzle[1] == R_BAKE_POSY) && + (normal_swizzle[2] == R_BAKE_POSZ)) { + break; + } + else { + RE_bake_normal_world_to_world( + pixel_array_low, num_pixels, depth, result, normal_swizzle); + } + break; + } + case R_BAKE_SPACE_OBJECT: { + RE_bake_normal_world_to_object( + pixel_array_low, num_pixels, depth, result, ob_low_eval, normal_swizzle); + break; + } + case R_BAKE_SPACE_TANGENT: { + if (is_selected_to_active) { + RE_bake_normal_world_to_tangent(pixel_array_low, + num_pixels, + depth, + result, + me_low, + normal_swizzle, + ob_low_eval->obmat); + } + else { + /* from multiresolution */ + Mesh *me_nores = NULL; + ModifierData *md = NULL; + int mode; + + BKE_object_eval_reset(ob_low_eval); + md = modifiers_findByType(ob_low_eval, eModifierType_Multires); + + if (md) { + mode = md->mode; + md->mode &= ~eModifierMode_Render; + } + + /* Evaluate modifiers again. */ + me_nores = BKE_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval, true, false); + RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer); + + RE_bake_normal_world_to_tangent(pixel_array_low, + num_pixels, + depth, + result, + me_nores, + normal_swizzle, + ob_low_eval->obmat); + BKE_id_free(bmain, me_nores); + + if (md) + md->mode = mode; + } + break; + } + default: + break; + } + } + + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Problem baking object \"%s\"", ob_low->id.name + 2); + op_result = OPERATOR_CANCELLED; + } + else { + /* save the results */ + for (int i = 0; i < bake_images.size; i++) { + BakeImage *bk_image = &bake_images.data[i]; + + if (is_save_internal) { + ok = write_internal_bake_pixels(bk_image->image, + pixel_array_low + bk_image->offset, + result + bk_image->offset * depth, + bk_image->width, + bk_image->height, + margin, + is_clear, + is_noncolor); + + /* might be read by UI to set active image for display */ + bake_update_image(sa, bk_image->image); + + if (!ok) { + BKE_reportf(reports, + RPT_ERROR, + "Problem saving the bake map internally for object \"%s\"", + ob_low->id.name + 2); + op_result = OPERATOR_CANCELLED; + } + else { + BKE_report(reports, + RPT_INFO, + "Baking map saved to internal image, save it externally or pack it"); + op_result = OPERATOR_FINISHED; + } + } + /* save externally */ + else { + BakeData *bake = &scene->r.bake; + char name[FILE_MAX]; + + BKE_image_path_from_imtype(name, + filepath, + BKE_main_blendfile_path(bmain), + 0, + bake->im_format.imtype, + true, + false, + NULL); + + if (is_automatic_name) { + BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); + BLI_path_suffix(name, FILE_MAX, identifier, "_"); + } + + if (is_split_materials) { + if (bk_image->image) { + BLI_path_suffix(name, FILE_MAX, bk_image->image->id.name + 2, "_"); + } + else { + if (ob_low_eval->mat[i]) { + BLI_path_suffix(name, FILE_MAX, ob_low_eval->mat[i]->id.name + 2, "_"); + } + else if (me_low->mat[i]) { + BLI_path_suffix(name, FILE_MAX, me_low->mat[i]->id.name + 2, "_"); + } + else { + /* if everything else fails, use the material index */ + char tmp[5]; + sprintf(tmp, "%d", i % 1000); + BLI_path_suffix(name, FILE_MAX, tmp, "_"); + } + } + } + + /* save it externally */ + ok = write_external_bake_pixels(name, + pixel_array_low + bk_image->offset, + result + bk_image->offset * depth, + bk_image->width, + bk_image->height, + margin, + &bake->im_format, + is_noncolor); + + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name); + op_result = OPERATOR_CANCELLED; + } + else { + BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\"", name); + op_result = OPERATOR_FINISHED; + } + + if (!is_split_materials) { + break; + } + } + } + } + + if (is_save_internal) + refresh_images(&bake_images); cleanup: - if (highpoly) { - int i; - for (i = 0; i < tot_highpoly; i++) { - if (highpoly[i].me) - BKE_id_free(bmain, highpoly[i].me); - } - MEM_freeN(highpoly); - } + if (highpoly) { + int i; + for (i = 0; i < tot_highpoly; i++) { + if (highpoly[i].me) + BKE_id_free(bmain, highpoly[i].me); + } + MEM_freeN(highpoly); + } - if (mmd_low) - mmd_low->flags = mmd_flags_low; + if (mmd_low) + mmd_low->flags = mmd_flags_low; - if (pixel_array_low) - MEM_freeN(pixel_array_low); + if (pixel_array_low) + MEM_freeN(pixel_array_low); - if (pixel_array_high) - MEM_freeN(pixel_array_high); + if (pixel_array_high) + MEM_freeN(pixel_array_high); - if (bake_images.data) - MEM_freeN(bake_images.data); + if (bake_images.data) + MEM_freeN(bake_images.data); - if (bake_images.lookup) - MEM_freeN(bake_images.lookup); + if (bake_images.lookup) + MEM_freeN(bake_images.lookup); - if (result) - MEM_freeN(result); + if (result) + MEM_freeN(result); - if (me_low) - BKE_id_free(bmain, me_low); + if (me_low) + BKE_id_free(bmain, me_low); - if (me_cage) - BKE_id_free(bmain, me_cage); + if (me_cage) + BKE_id_free(bmain, me_cage); - DEG_graph_free(depsgraph); + DEG_graph_free(depsgraph); - return op_result; + return op_result; } static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr) { - bool is_save_internal; - bScreen *sc = CTX_wm_screen(C); + bool is_save_internal; + bScreen *sc = CTX_wm_screen(C); - bkr->ob = CTX_data_active_object(C); - bkr->main = CTX_data_main(C); - bkr->view_layer = CTX_data_view_layer(C); - bkr->scene = CTX_data_scene(C); - bkr->sa = sc ? BKE_screen_find_big_area(sc, SPACE_IMAGE, 10) : NULL; + bkr->ob = CTX_data_active_object(C); + bkr->main = CTX_data_main(C); + bkr->view_layer = CTX_data_view_layer(C); + bkr->scene = CTX_data_scene(C); + bkr->sa = sc ? BKE_screen_find_big_area(sc, SPACE_IMAGE, 10) : NULL; - bkr->pass_type = RNA_enum_get(op->ptr, "type"); - bkr->pass_filter = RNA_enum_get(op->ptr, "pass_filter"); - bkr->margin = RNA_int_get(op->ptr, "margin"); + bkr->pass_type = RNA_enum_get(op->ptr, "type"); + bkr->pass_filter = RNA_enum_get(op->ptr, "pass_filter"); + bkr->margin = RNA_int_get(op->ptr, "margin"); - bkr->save_mode = RNA_enum_get(op->ptr, "save_mode"); - is_save_internal = (bkr->save_mode == R_BAKE_SAVE_INTERNAL); + bkr->save_mode = RNA_enum_get(op->ptr, "save_mode"); + is_save_internal = (bkr->save_mode == R_BAKE_SAVE_INTERNAL); - bkr->is_clear = RNA_boolean_get(op->ptr, "use_clear"); - bkr->is_split_materials = (!is_save_internal) && RNA_boolean_get(op->ptr, "use_split_materials"); - bkr->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name"); - bkr->is_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active"); - bkr->is_cage = RNA_boolean_get(op->ptr, "use_cage"); - bkr->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion"); + bkr->is_clear = RNA_boolean_get(op->ptr, "use_clear"); + bkr->is_split_materials = (!is_save_internal) && RNA_boolean_get(op->ptr, "use_split_materials"); + bkr->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name"); + bkr->is_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active"); + bkr->is_cage = RNA_boolean_get(op->ptr, "use_cage"); + bkr->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion"); - bkr->normal_space = RNA_enum_get(op->ptr, "normal_space"); - bkr->normal_swizzle[0] = RNA_enum_get(op->ptr, "normal_r"); - bkr->normal_swizzle[1] = RNA_enum_get(op->ptr, "normal_g"); - bkr->normal_swizzle[2] = RNA_enum_get(op->ptr, "normal_b"); + bkr->normal_space = RNA_enum_get(op->ptr, "normal_space"); + bkr->normal_swizzle[0] = RNA_enum_get(op->ptr, "normal_r"); + bkr->normal_swizzle[1] = RNA_enum_get(op->ptr, "normal_g"); + bkr->normal_swizzle[2] = RNA_enum_get(op->ptr, "normal_b"); - bkr->width = RNA_int_get(op->ptr, "width"); - bkr->height = RNA_int_get(op->ptr, "height"); - bkr->identifier = ""; + bkr->width = RNA_int_get(op->ptr, "width"); + bkr->height = RNA_int_get(op->ptr, "height"); + bkr->identifier = ""; - RNA_string_get(op->ptr, "uv_layer", bkr->uv_layer); + RNA_string_get(op->ptr, "uv_layer", bkr->uv_layer); - RNA_string_get(op->ptr, "cage_object", bkr->custom_cage); + RNA_string_get(op->ptr, "cage_object", bkr->custom_cage); - if ((!is_save_internal) && bkr->is_automatic_name) { - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); - RNA_property_enum_identifier(C, op->ptr, prop, bkr->pass_type, &bkr->identifier); - } + if ((!is_save_internal) && bkr->is_automatic_name) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); + RNA_property_enum_identifier(C, op->ptr, prop, bkr->pass_type, &bkr->identifier); + } - CTX_data_selected_objects(C, &bkr->selected_objects); + CTX_data_selected_objects(C, &bkr->selected_objects); - bkr->reports = op->reports; + bkr->reports = op->reports; - bkr->result = OPERATOR_CANCELLED; + bkr->result = OPERATOR_CANCELLED; - bkr->render = RE_NewSceneRender(bkr->scene); + bkr->render = RE_NewSceneRender(bkr->scene); - /* XXX hack to force saving to always be internal. Whether (and how) to support - * external saving will be addressed later */ - bkr->save_mode = R_BAKE_SAVE_INTERNAL; + /* XXX hack to force saving to always be internal. Whether (and how) to support + * external saving will be addressed later */ + bkr->save_mode = R_BAKE_SAVE_INTERNAL; } static int bake_exec(bContext *C, wmOperator *op) { - Render *re; - int result = OPERATOR_CANCELLED; - BakeAPIRender bkr = {NULL}; - Scene *scene = CTX_data_scene(C); - - G.is_break = false; - G.is_rendering = true; - - bake_set_props(op, scene); - - bake_init_api_data(op, C, &bkr); - re = bkr.render; - - /* setup new render */ - RE_test_break_cb(re, NULL, bake_break); - - if (!bake_pass_filter_check(bkr.pass_type, bkr.pass_filter, bkr.reports)) { - goto finally; - } - - if (!bake_objects_check(bkr.main, bkr.view_layer, bkr.ob, &bkr.selected_objects, bkr.reports, bkr.is_selected_to_active)) { - goto finally; - } - - if (bkr.is_clear) { - const bool is_tangent = ((bkr.pass_type == SCE_PASS_NORMAL) && (bkr.normal_space == R_BAKE_SPACE_TANGENT)); - bake_images_clear(bkr.main, is_tangent); - } - - RE_SetReports(re, bkr.reports); - - if (bkr.is_selected_to_active) { - result = bake( - bkr.render, bkr.main, bkr.scene, bkr.view_layer, bkr.ob, &bkr.selected_objects, bkr.reports, - bkr.pass_type, bkr.pass_filter, bkr.margin, bkr.save_mode, - bkr.is_clear, bkr.is_split_materials, bkr.is_automatic_name, true, bkr.is_cage, - bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle, - bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa, - bkr.uv_layer); - } - else { - CollectionPointerLink *link; - const bool is_clear = bkr.is_clear && BLI_listbase_is_single(&bkr.selected_objects); - for (link = bkr.selected_objects.first; link; link = link->next) { - Object *ob_iter = link->ptr.data; - result = bake( - bkr.render, bkr.main, bkr.scene, bkr.view_layer, ob_iter, NULL, bkr.reports, - bkr.pass_type, bkr.pass_filter, bkr.margin, bkr.save_mode, - is_clear, bkr.is_split_materials, bkr.is_automatic_name, false, bkr.is_cage, - bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle, - bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa, - bkr.uv_layer); - } - } - - RE_SetReports(re, NULL); - + Render *re; + int result = OPERATOR_CANCELLED; + BakeAPIRender bkr = {NULL}; + Scene *scene = CTX_data_scene(C); + + G.is_break = false; + G.is_rendering = true; + + bake_set_props(op, scene); + + bake_init_api_data(op, C, &bkr); + re = bkr.render; + + /* setup new render */ + RE_test_break_cb(re, NULL, bake_break); + + if (!bake_pass_filter_check(bkr.pass_type, bkr.pass_filter, bkr.reports)) { + goto finally; + } + + if (!bake_objects_check(bkr.main, + bkr.view_layer, + bkr.ob, + &bkr.selected_objects, + bkr.reports, + bkr.is_selected_to_active)) { + goto finally; + } + + if (bkr.is_clear) { + const bool is_tangent = ((bkr.pass_type == SCE_PASS_NORMAL) && + (bkr.normal_space == R_BAKE_SPACE_TANGENT)); + bake_images_clear(bkr.main, is_tangent); + } + + RE_SetReports(re, bkr.reports); + + if (bkr.is_selected_to_active) { + result = bake(bkr.render, + bkr.main, + bkr.scene, + bkr.view_layer, + bkr.ob, + &bkr.selected_objects, + bkr.reports, + bkr.pass_type, + bkr.pass_filter, + bkr.margin, + bkr.save_mode, + bkr.is_clear, + bkr.is_split_materials, + bkr.is_automatic_name, + true, + bkr.is_cage, + bkr.cage_extrusion, + bkr.normal_space, + bkr.normal_swizzle, + bkr.custom_cage, + bkr.filepath, + bkr.width, + bkr.height, + bkr.identifier, + bkr.sa, + bkr.uv_layer); + } + else { + CollectionPointerLink *link; + const bool is_clear = bkr.is_clear && BLI_listbase_is_single(&bkr.selected_objects); + for (link = bkr.selected_objects.first; link; link = link->next) { + Object *ob_iter = link->ptr.data; + result = bake(bkr.render, + bkr.main, + bkr.scene, + bkr.view_layer, + ob_iter, + NULL, + bkr.reports, + bkr.pass_type, + bkr.pass_filter, + bkr.margin, + bkr.save_mode, + is_clear, + bkr.is_split_materials, + bkr.is_automatic_name, + false, + bkr.is_cage, + bkr.cage_extrusion, + bkr.normal_space, + bkr.normal_swizzle, + bkr.custom_cage, + bkr.filepath, + bkr.width, + bkr.height, + bkr.identifier, + bkr.sa, + bkr.uv_layer); + } + } + + RE_SetReports(re, NULL); finally: - G.is_rendering = false; - BLI_freelistN(&bkr.selected_objects); - return result; + G.is_rendering = false; + BLI_freelistN(&bkr.selected_objects); + return result; } static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, float *progress) { - BakeAPIRender *bkr = (BakeAPIRender *)bkv; - - /* setup new render */ - bkr->do_update = do_update; - bkr->progress = progress; - - RE_SetReports(bkr->render, bkr->reports); - - if (!bake_pass_filter_check(bkr->pass_type, bkr->pass_filter, bkr->reports)) { - bkr->result = OPERATOR_CANCELLED; - return; - } - - if (!bake_objects_check(bkr->main, bkr->view_layer, bkr->ob, &bkr->selected_objects, bkr->reports, bkr->is_selected_to_active)) { - bkr->result = OPERATOR_CANCELLED; - return; - } - - if (bkr->is_clear) { - const bool is_tangent = ((bkr->pass_type == SCE_PASS_NORMAL) && (bkr->normal_space == R_BAKE_SPACE_TANGENT)); - bake_images_clear(bkr->main, is_tangent); - } - - if (bkr->is_selected_to_active) { - bkr->result = bake( - bkr->render, bkr->main, bkr->scene, bkr->view_layer, bkr->ob, &bkr->selected_objects, bkr->reports, - bkr->pass_type, bkr->pass_filter, bkr->margin, bkr->save_mode, - bkr->is_clear, bkr->is_split_materials, bkr->is_automatic_name, true, bkr->is_cage, - bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle, - bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa, - bkr->uv_layer); - } - else { - CollectionPointerLink *link; - const bool is_clear = bkr->is_clear && BLI_listbase_is_single(&bkr->selected_objects); - for (link = bkr->selected_objects.first; link; link = link->next) { - Object *ob_iter = link->ptr.data; - bkr->result = bake( - bkr->render, bkr->main, bkr->scene, bkr->view_layer, ob_iter, NULL, bkr->reports, - bkr->pass_type, bkr->pass_filter, bkr->margin, bkr->save_mode, - is_clear, bkr->is_split_materials, bkr->is_automatic_name, false, bkr->is_cage, - bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle, - bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa, - bkr->uv_layer); - - if (bkr->result == OPERATOR_CANCELLED) - return; - } - } - - RE_SetReports(bkr->render, NULL); + BakeAPIRender *bkr = (BakeAPIRender *)bkv; + + /* setup new render */ + bkr->do_update = do_update; + bkr->progress = progress; + + RE_SetReports(bkr->render, bkr->reports); + + if (!bake_pass_filter_check(bkr->pass_type, bkr->pass_filter, bkr->reports)) { + bkr->result = OPERATOR_CANCELLED; + return; + } + + if (!bake_objects_check(bkr->main, + bkr->view_layer, + bkr->ob, + &bkr->selected_objects, + bkr->reports, + bkr->is_selected_to_active)) { + bkr->result = OPERATOR_CANCELLED; + return; + } + + if (bkr->is_clear) { + const bool is_tangent = ((bkr->pass_type == SCE_PASS_NORMAL) && + (bkr->normal_space == R_BAKE_SPACE_TANGENT)); + bake_images_clear(bkr->main, is_tangent); + } + + if (bkr->is_selected_to_active) { + bkr->result = bake(bkr->render, + bkr->main, + bkr->scene, + bkr->view_layer, + bkr->ob, + &bkr->selected_objects, + bkr->reports, + bkr->pass_type, + bkr->pass_filter, + bkr->margin, + bkr->save_mode, + bkr->is_clear, + bkr->is_split_materials, + bkr->is_automatic_name, + true, + bkr->is_cage, + bkr->cage_extrusion, + bkr->normal_space, + bkr->normal_swizzle, + bkr->custom_cage, + bkr->filepath, + bkr->width, + bkr->height, + bkr->identifier, + bkr->sa, + bkr->uv_layer); + } + else { + CollectionPointerLink *link; + const bool is_clear = bkr->is_clear && BLI_listbase_is_single(&bkr->selected_objects); + for (link = bkr->selected_objects.first; link; link = link->next) { + Object *ob_iter = link->ptr.data; + bkr->result = bake(bkr->render, + bkr->main, + bkr->scene, + bkr->view_layer, + ob_iter, + NULL, + bkr->reports, + bkr->pass_type, + bkr->pass_filter, + bkr->margin, + bkr->save_mode, + is_clear, + bkr->is_split_materials, + bkr->is_automatic_name, + false, + bkr->is_cage, + bkr->cage_extrusion, + bkr->normal_space, + bkr->normal_swizzle, + bkr->custom_cage, + bkr->filepath, + bkr->width, + bkr->height, + bkr->identifier, + bkr->sa, + bkr->uv_layer); + + if (bkr->result == OPERATOR_CANCELLED) + return; + } + } + + RE_SetReports(bkr->render, NULL); } static void bake_freejob(void *bkv) { - BakeAPIRender *bkr = (BakeAPIRender *)bkv; + BakeAPIRender *bkr = (BakeAPIRender *)bkv; - BLI_freelistN(&bkr->selected_objects); - MEM_freeN(bkr); + BLI_freelistN(&bkr->selected_objects); + MEM_freeN(bkr); - G.is_rendering = false; + G.is_rendering = false; } static void bake_set_props(wmOperator *op, Scene *scene) { - PropertyRNA *prop; - BakeData *bake = &scene->r.bake; - - prop = RNA_struct_find_property(op->ptr, "filepath"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_string_set(op->ptr, prop, bake->filepath); - } - - prop = RNA_struct_find_property(op->ptr, "width"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_int_set(op->ptr, prop, bake->width); - } - - prop = RNA_struct_find_property(op->ptr, "height"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_int_set(op->ptr, prop, bake->width); - } - - prop = RNA_struct_find_property(op->ptr, "margin"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_int_set(op->ptr, prop, bake->margin); - } - - prop = RNA_struct_find_property(op->ptr, "use_selected_to_active"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_TO_ACTIVE) != 0); - } - - prop = RNA_struct_find_property(op->ptr, "cage_extrusion"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_float_set(op->ptr, prop, bake->cage_extrusion); - } - - prop = RNA_struct_find_property(op->ptr, "cage_object"); - if (!RNA_property_is_set(op->ptr, prop)) { - if (bake->cage_object) { - RNA_property_string_set(op->ptr, prop, bake->cage_object->id.name + 2); - } - } - - prop = RNA_struct_find_property(op->ptr, "normal_space"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(op->ptr, prop, bake->normal_space); - } - - prop = RNA_struct_find_property(op->ptr, "normal_r"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[0]); - } - - prop = RNA_struct_find_property(op->ptr, "normal_g"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[1]); - } - - prop = RNA_struct_find_property(op->ptr, "normal_b"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[2]); - } - - prop = RNA_struct_find_property(op->ptr, "save_mode"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(op->ptr, prop, bake->save_mode); - } - - prop = RNA_struct_find_property(op->ptr, "use_clear"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CLEAR) != 0); - } - - prop = RNA_struct_find_property(op->ptr, "use_cage"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CAGE) != 0); - } - - prop = RNA_struct_find_property(op->ptr, "use_split_materials"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_SPLIT_MAT) != 0); - } - - prop = RNA_struct_find_property(op->ptr, "use_automatic_name"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_AUTO_NAME) != 0); - } - - prop = RNA_struct_find_property(op->ptr, "pass_filter"); - if (!RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(op->ptr, prop, bake->pass_filter); - } + PropertyRNA *prop; + BakeData *bake = &scene->r.bake; + + prop = RNA_struct_find_property(op->ptr, "filepath"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_string_set(op->ptr, prop, bake->filepath); + } + + prop = RNA_struct_find_property(op->ptr, "width"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_int_set(op->ptr, prop, bake->width); + } + + prop = RNA_struct_find_property(op->ptr, "height"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_int_set(op->ptr, prop, bake->width); + } + + prop = RNA_struct_find_property(op->ptr, "margin"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_int_set(op->ptr, prop, bake->margin); + } + + prop = RNA_struct_find_property(op->ptr, "use_selected_to_active"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_TO_ACTIVE) != 0); + } + + prop = RNA_struct_find_property(op->ptr, "cage_extrusion"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_set(op->ptr, prop, bake->cage_extrusion); + } + + prop = RNA_struct_find_property(op->ptr, "cage_object"); + if (!RNA_property_is_set(op->ptr, prop)) { + if (bake->cage_object) { + RNA_property_string_set(op->ptr, prop, bake->cage_object->id.name + 2); + } + } + + prop = RNA_struct_find_property(op->ptr, "normal_space"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, bake->normal_space); + } + + prop = RNA_struct_find_property(op->ptr, "normal_r"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[0]); + } + + prop = RNA_struct_find_property(op->ptr, "normal_g"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[1]); + } + + prop = RNA_struct_find_property(op->ptr, "normal_b"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[2]); + } + + prop = RNA_struct_find_property(op->ptr, "save_mode"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, bake->save_mode); + } + + prop = RNA_struct_find_property(op->ptr, "use_clear"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CLEAR) != 0); + } + + prop = RNA_struct_find_property(op->ptr, "use_cage"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CAGE) != 0); + } + + prop = RNA_struct_find_property(op->ptr, "use_split_materials"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_SPLIT_MAT) != 0); + } + + prop = RNA_struct_find_property(op->ptr, "use_automatic_name"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_AUTO_NAME) != 0); + } + + prop = RNA_struct_find_property(op->ptr, "pass_filter"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, bake->pass_filter); + } } static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - wmJob *wm_job; - BakeAPIRender *bkr; - Render *re; - Scene *scene = CTX_data_scene(C); + wmJob *wm_job; + BakeAPIRender *bkr; + Render *re; + Scene *scene = CTX_data_scene(C); - bake_set_props(op, scene); + bake_set_props(op, scene); - /* only one render job at a time */ - if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_OBJECT_BAKE)) - return OPERATOR_CANCELLED; + /* only one render job at a time */ + if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_OBJECT_BAKE)) + return OPERATOR_CANCELLED; - bkr = MEM_mallocN(sizeof(BakeAPIRender), "render bake"); + bkr = MEM_mallocN(sizeof(BakeAPIRender), "render bake"); - /* init bake render */ - bake_init_api_data(op, C, bkr); - re = bkr->render; + /* init bake render */ + bake_init_api_data(op, C, bkr); + re = bkr->render; - /* setup new render */ - RE_test_break_cb(re, NULL, bake_break); - RE_progress_cb(re, bkr, bake_progress_update); + /* setup new render */ + RE_test_break_cb(re, NULL, bake_break); + RE_progress_cb(re, bkr, bake_progress_update); - /* setup job */ - wm_job = 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_JOB_TYPE_OBJECT_BAKE); - WM_jobs_customdata_set(wm_job, bkr, bake_freejob); - WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */ - WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL); + /* setup job */ + wm_job = 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_JOB_TYPE_OBJECT_BAKE); + WM_jobs_customdata_set(wm_job, bkr, bake_freejob); + WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */ + WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL); - G.is_break = false; - G.is_rendering = true; + G.is_break = false; + G.is_rendering = true; - WM_jobs_start(CTX_wm_manager(C), wm_job); + WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); + 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); - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); - return OPERATOR_RUNNING_MODAL; + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); + return OPERATOR_RUNNING_MODAL; } void OBJECT_OT_bake(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Bake"; - ot->description = "Bake image textures of selected objects"; - ot->idname = "OBJECT_OT_bake"; - - /* api callbacks */ - ot->exec = bake_exec; - ot->modal = bake_modal; - ot->invoke = bake_invoke; - ot->poll = ED_operator_object_active_editable_mesh; - - RNA_def_enum(ot->srna, "type", rna_enum_bake_pass_type_items, SCE_PASS_COMBINED, "Type", - "Type of pass to bake, some of them may not be supported by the current render engine"); - prop = RNA_def_enum(ot->srna, "pass_filter", rna_enum_bake_pass_filter_type_items, R_BAKE_PASS_FILTER_NONE, "Pass Filter", - "Filter to combined, diffuse, glossy, transmission and subsurface passes"); - RNA_def_property_flag(prop, PROP_ENUM_FLAG); - RNA_def_string_file_path(ot->srna, "filepath", NULL, FILE_MAX, "File Path", - "Image filepath to use when saving externally"); - RNA_def_int(ot->srna, "width", 512, 1, INT_MAX, "Width", - "Horizontal dimension of the baking map (external only)", 64, 4096); - RNA_def_int(ot->srna, "height", 512, 1, INT_MAX, "Height", - "Vertical dimension of the baking map (external only)", 64, 4096); - RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin", - "Extends the baked result as a post process filter", 0, 64); - RNA_def_boolean(ot->srna, "use_selected_to_active", false, "Selected to Active", - "Bake shading on the surface of selected objects to the active object"); - RNA_def_float(ot->srna, "cage_extrusion", 0.0f, 0.0f, FLT_MAX, "Cage Extrusion", - "Distance to use for the inward ray cast when using selected to active", 0.0f, 1.0f); - RNA_def_string(ot->srna, "cage_object", NULL, MAX_NAME, "Cage Object", - "Object to use as cage, instead of calculating the cage from the active object with cage extrusion"); - RNA_def_enum(ot->srna, "normal_space", rna_enum_normal_space_items, R_BAKE_SPACE_TANGENT, "Normal Space", - "Choose normal space for baking"); - RNA_def_enum(ot->srna, "normal_r", rna_enum_normal_swizzle_items, R_BAKE_POSX, "R", "Axis to bake in red channel"); - RNA_def_enum(ot->srna, "normal_g", rna_enum_normal_swizzle_items, R_BAKE_POSY, "G", "Axis to bake in green channel"); - RNA_def_enum(ot->srna, "normal_b", rna_enum_normal_swizzle_items, R_BAKE_POSZ, "B", "Axis to bake in blue channel"); - RNA_def_enum(ot->srna, "save_mode", rna_enum_bake_save_mode_items, R_BAKE_SAVE_INTERNAL, "Save Mode", - "Choose how to save the baking map"); - RNA_def_boolean(ot->srna, "use_clear", false, "Clear", - "Clear Images before baking (only for internal saving)"); - RNA_def_boolean(ot->srna, "use_cage", false, "Cage", - "Cast rays to active object from a cage"); - RNA_def_boolean(ot->srna, "use_split_materials", false, "Split Materials", - "Split baked maps per material, using material name in output file (external only)"); - RNA_def_boolean(ot->srna, "use_automatic_name", false, "Automatic Name", - "Automatically name the output file with the pass type"); - RNA_def_string(ot->srna, "uv_layer", NULL, MAX_CUSTOMDATA_LAYER_NAME, "UV Layer", "UV layer to override active"); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Bake"; + ot->description = "Bake image textures of selected objects"; + ot->idname = "OBJECT_OT_bake"; + + /* api callbacks */ + ot->exec = bake_exec; + ot->modal = bake_modal; + ot->invoke = bake_invoke; + ot->poll = ED_operator_object_active_editable_mesh; + + RNA_def_enum( + ot->srna, + "type", + rna_enum_bake_pass_type_items, + SCE_PASS_COMBINED, + "Type", + "Type of pass to bake, some of them may not be supported by the current render engine"); + prop = RNA_def_enum(ot->srna, + "pass_filter", + rna_enum_bake_pass_filter_type_items, + R_BAKE_PASS_FILTER_NONE, + "Pass Filter", + "Filter to combined, diffuse, glossy, transmission and subsurface passes"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_string_file_path(ot->srna, + "filepath", + NULL, + FILE_MAX, + "File Path", + "Image filepath to use when saving externally"); + RNA_def_int(ot->srna, + "width", + 512, + 1, + INT_MAX, + "Width", + "Horizontal dimension of the baking map (external only)", + 64, + 4096); + RNA_def_int(ot->srna, + "height", + 512, + 1, + INT_MAX, + "Height", + "Vertical dimension of the baking map (external only)", + 64, + 4096); + RNA_def_int(ot->srna, + "margin", + 16, + 0, + INT_MAX, + "Margin", + "Extends the baked result as a post process filter", + 0, + 64); + RNA_def_boolean(ot->srna, + "use_selected_to_active", + false, + "Selected to Active", + "Bake shading on the surface of selected objects to the active object"); + RNA_def_float(ot->srna, + "cage_extrusion", + 0.0f, + 0.0f, + FLT_MAX, + "Cage Extrusion", + "Distance to use for the inward ray cast when using selected to active", + 0.0f, + 1.0f); + RNA_def_string(ot->srna, + "cage_object", + NULL, + MAX_NAME, + "Cage Object", + "Object to use as cage, instead of calculating the cage from the active object " + "with cage extrusion"); + RNA_def_enum(ot->srna, + "normal_space", + rna_enum_normal_space_items, + R_BAKE_SPACE_TANGENT, + "Normal Space", + "Choose normal space for baking"); + RNA_def_enum(ot->srna, + "normal_r", + rna_enum_normal_swizzle_items, + R_BAKE_POSX, + "R", + "Axis to bake in red channel"); + RNA_def_enum(ot->srna, + "normal_g", + rna_enum_normal_swizzle_items, + R_BAKE_POSY, + "G", + "Axis to bake in green channel"); + RNA_def_enum(ot->srna, + "normal_b", + rna_enum_normal_swizzle_items, + R_BAKE_POSZ, + "B", + "Axis to bake in blue channel"); + RNA_def_enum(ot->srna, + "save_mode", + rna_enum_bake_save_mode_items, + R_BAKE_SAVE_INTERNAL, + "Save Mode", + "Choose how to save the baking map"); + RNA_def_boolean(ot->srna, + "use_clear", + false, + "Clear", + "Clear Images before baking (only for internal saving)"); + RNA_def_boolean(ot->srna, "use_cage", false, "Cage", "Cast rays to active object from a cage"); + RNA_def_boolean( + ot->srna, + "use_split_materials", + false, + "Split Materials", + "Split baked maps per material, using material name in output file (external only)"); + RNA_def_boolean(ot->srna, + "use_automatic_name", + false, + "Automatic Name", + "Automatically name the output file with the pass type"); + RNA_def_string(ot->srna, + "uv_layer", + NULL, + MAX_CUSTOMDATA_LAYER_NAME, + "UV Layer", + "UV layer to override active"); } |