Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/object/object_bake_api.c
| Show All 25 Lines | |||||
| #include "DNA_material_types.h" | #include "DNA_material_types.h" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "BLI_linklist.h" | |||||
| #include "BLI_string.h" | |||||
| #include "BLI_fileops.h" | #include "BLI_fileops.h" | ||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_path_util.h" | #include "BLI_path_util.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_image.h" | #include "BKE_image.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| Show All 25 Lines | |||||
| #include "ED_object.h" | #include "ED_object.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_uvedit.h" | #include "ED_uvedit.h" | ||||
| #include "GPU_draw.h" | #include "GPU_draw.h" | ||||
| #include "object_intern.h" | #include "object_intern.h" | ||||
| /* prototypes */ | typedef struct BakeTask { | ||||
| static void bake_set_props(wmOperator *op, Scene *scene); | struct BakeTask *next, *prev; | ||||
| typedef struct BakeAPIRender { | |||||
| Object *ob; | Object *ob; | ||||
| Main *main; | Main *main; | ||||
| Scene *scene; | Scene *scene; | ||||
| ViewLayer *view_layer; | ViewLayer *view_layer; | ||||
| ReportList *reports; | ReportList *reports; | ||||
| ListBase selected_objects; | |||||
| eScenePassType pass_type; | |||||
| int pass_filter; | |||||
| int margin; | int margin; | ||||
| int save_mode; | int save_mode; | ||||
| bool is_clear; | bool do_clear; | ||||
| bool is_split_materials; | |||||
| bool is_automatic_name; | bool is_automatic_name; | ||||
| bool is_selected_to_active; | |||||
| bool is_cage; | bool is_cage; | ||||
| float cage_extrusion; | float cage_extrusion; | ||||
| int normal_space; | int normal_space; | ||||
| eBakeNormalSwizzle normal_swizzle[3]; | eBakeNormalSwizzle normal_swizzle[3]; | ||||
| char uv_layer[MAX_CUSTOMDATA_LAYER_NAME]; | char uv_layer[MAX_CUSTOMDATA_LAYER_NAME]; | ||||
| char custom_cage[MAX_NAME]; | char custom_cage[MAX_NAME]; | ||||
| char filepath[FILE_MAX]; | char filepath[FILE_MAX]; | ||||
| struct ImageFormatData im_format; | |||||
| int width; | int width; | ||||
| int height; | int height; | ||||
| const char *identifier; | |||||
| int result; | |||||
| bool ready; | |||||
| /* callbacks */ | /* callbacks */ | ||||
| Render *render; | Render *render; | ||||
| float *progress; | float *progress; | ||||
| int task_num, num_tasks; | |||||
| short *do_update; | short *do_update; | ||||
| /* for redrawing */ | /* for redrawing */ | ||||
| ScrArea *sa; | ScrArea *sa; | ||||
| } BakeAPIRender; | |||||
| BakePass *bp; | |||||
| } BakeTask; | |||||
| /* callbacks */ | /* callbacks */ | ||||
| static void bake_progress_update(void *bjv, float progress) | static void bake_progress_update(void *bt_v, float progress) | ||||
| { | { | ||||
| BakeAPIRender *bj = bjv; | BakeTask *bt = bt_v; | ||||
| progress = (bt->task_num + progress) / bt->num_tasks; | |||||
| if (bj->progress && *bj->progress != progress) { | if (bt->progress && *bt->progress != progress) { | ||||
| *bj->progress = progress; | *bt->progress = progress; | ||||
| /* make jobs timer to send notifier */ | /* make jobs timer to send notifier */ | ||||
| *(bj->do_update) = true; | *(bt->do_update) = true; | ||||
| } | } | ||||
| } | } | ||||
| /* catch esc */ | /* catch esc */ | ||||
| static int bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) | static int bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) | ||||
| { | { | ||||
| /* no running blender, remove handler and 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)) { | if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_BAKE)) { | ||||
| return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; | return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; | ||||
| } | } | ||||
| /* running render */ | /* running render */ | ||||
| switch (event->type) { | switch (event->type) { | ||||
| case EVT_ESCKEY: { | case EVT_ESCKEY: { | ||||
| G.is_break = true; | G.is_break = true; | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| } | } | ||||
| return OPERATOR_PASS_THROUGH; | return OPERATOR_PASS_THROUGH; | ||||
| } | } | ||||
| /* for exec() when there is no render job | /* for exec() when there is no render job | ||||
| * note: this wont check for the escape key being pressed, but doing so isnt threadsafe */ | * note: this wont check for the escape key being pressed, but doing so isnt threadsafe */ | ||||
| static int bake_break(void *UNUSED(rjv)) | static int bake_break(void *UNUSED(arg)) | ||||
| { | { | ||||
| if (G.is_break) { | if (G.is_break) { | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static void bake_update_image(ScrArea *sa, Image *image) | static void bake_update_image(ScrArea *sa, Image *image) | ||||
| { | { | ||||
| if (sa && sa->spacetype == SPACE_IMAGE) { /* in case the user changed while baking */ | if (sa && sa->spacetype == SPACE_IMAGE) { /* in case the user changed while baking */ | ||||
| SpaceImage *sima = sa->spacedata.first; | SpaceImage *sima = sa->spacedata.first; | ||||
| if (sima) { | if (sima) { | ||||
| sima->image = image; | sima->image = image; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static bool write_internal_bake_pixels(Image *image, | static bool write_internal_bake_pixels(BakeTask *bt, | ||||
| BakePixel pixel_array[], | BakeImage *bima, | ||||
| float *buffer, | BakeResult *bres, | ||||
| const int width, | BakePixel pixel_array[]) | ||||
| const int height, | |||||
| const int margin, | |||||
| const bool is_clear, | |||||
| const bool is_noncolor) | |||||
| { | { | ||||
| ImBuf *ibuf; | ImBuf *ibuf; | ||||
| void *lock; | void *lock; | ||||
| bool is_float; | bool is_float; | ||||
| char *mask_buffer = NULL; | char *mask_buffer = NULL; | ||||
| const size_t num_pixels = (size_t)width * (size_t)height; | |||||
| ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); | ibuf = BKE_image_acquire_ibuf(bima->image, NULL, &lock); | ||||
| if (!ibuf) { | if (!ibuf) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (margin > 0 || !is_clear) { | if (bt->margin > 0 || !bt->do_clear) { | ||||
| mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); | mask_buffer = MEM_callocN(sizeof(char) * bres->num_pixels, "Bake Mask"); | ||||
| RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); | RE_bake_mask_fill(pixel_array, bres->num_pixels, mask_buffer); | ||||
| } | } | ||||
| is_float = (ibuf->rect_float != NULL); | is_float = (ibuf->rect_float != NULL); | ||||
| /* colormanagement conversions */ | /* colormanagement conversions */ | ||||
| if (!is_noncolor) { | if (bres->is_color) { | ||||
| const char *from_colorspace; | const char *from_colorspace; | ||||
| const char *to_colorspace; | const char *to_colorspace; | ||||
| from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); | from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); | ||||
| if (is_float) { | if (is_float) { | ||||
| to_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); | to_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); | ||||
| } | } | ||||
| else { | else { | ||||
| to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); | to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); | ||||
| } | } | ||||
| if (from_colorspace != to_colorspace) { | if (from_colorspace != to_colorspace) { | ||||
| IMB_colormanagement_transform( | IMB_colormanagement_transform( | ||||
| buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); | bres->pixels, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); | ||||
| } | } | ||||
| } | } | ||||
| /* populates the ImBuf */ | /* populates the ImBuf */ | ||||
| if (is_clear) { | if (bt->do_clear) { | ||||
| if (is_float) { | if (is_float) { | ||||
| IMB_buffer_float_from_float(ibuf->rect_float, | IMB_buffer_float_from_float(ibuf->rect_float, | ||||
| buffer, | bres->pixels, | ||||
| ibuf->channels, | ibuf->channels, | ||||
| IB_PROFILE_LINEAR_RGB, | IB_PROFILE_LINEAR_RGB, | ||||
| IB_PROFILE_LINEAR_RGB, | IB_PROFILE_LINEAR_RGB, | ||||
| false, | false, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->y, | ibuf->y, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->x); | ibuf->x); | ||||
| } | } | ||||
| else { | else { | ||||
| IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, | IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, | ||||
| buffer, | bres->pixels, | ||||
| ibuf->channels, | ibuf->channels, | ||||
| ibuf->dither, | ibuf->dither, | ||||
| IB_PROFILE_SRGB, | IB_PROFILE_SRGB, | ||||
| IB_PROFILE_SRGB, | IB_PROFILE_SRGB, | ||||
| false, | false, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->y, | ibuf->y, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->x); | ibuf->x); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| if (is_float) { | if (is_float) { | ||||
| IMB_buffer_float_from_float_mask(ibuf->rect_float, | IMB_buffer_float_from_float_mask(ibuf->rect_float, | ||||
| buffer, | bres->pixels, | ||||
| ibuf->channels, | ibuf->channels, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->y, | ibuf->y, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->x, | ibuf->x, | ||||
| mask_buffer); | mask_buffer); | ||||
| } | } | ||||
| else { | else { | ||||
| IMB_buffer_byte_from_float_mask((unsigned char *)ibuf->rect, | IMB_buffer_byte_from_float_mask((unsigned char *)ibuf->rect, | ||||
| buffer, | bres->pixels, | ||||
| ibuf->channels, | ibuf->channels, | ||||
| ibuf->dither, | ibuf->dither, | ||||
| false, | false, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->y, | ibuf->y, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->x, | ibuf->x, | ||||
| mask_buffer); | mask_buffer); | ||||
| } | } | ||||
| } | } | ||||
| /* margins */ | /* margins */ | ||||
| if (margin > 0) { | if (bt->margin > 0) { | ||||
| RE_bake_margin(ibuf, mask_buffer, margin); | RE_bake_margin(ibuf, mask_buffer, bt->margin, !bt->do_clear); | ||||
| } | } | ||||
| ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; | ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; | ||||
| BKE_image_mark_dirty(image, ibuf); | BKE_image_mark_dirty(bima->image, ibuf); | ||||
| if (ibuf->rect_float) { | if (ibuf->rect_float) { | ||||
| ibuf->userflags |= IB_RECT_INVALID; | ibuf->userflags |= IB_RECT_INVALID; | ||||
| } | } | ||||
| /* force mipmap recalc */ | /* force mipmap recalc */ | ||||
| if (ibuf->mipmap[0]) { | if (ibuf->mipmap[0]) { | ||||
| ibuf->userflags |= IB_MIPMAP_INVALID; | ibuf->userflags |= IB_MIPMAP_INVALID; | ||||
| imb_freemipmapImBuf(ibuf); | imb_freemipmapImBuf(ibuf); | ||||
| } | } | ||||
| BKE_image_release_ibuf(image, ibuf, NULL); | BKE_image_release_ibuf(bima->image, ibuf, NULL); | ||||
| if (mask_buffer) { | if (mask_buffer) { | ||||
| MEM_freeN(mask_buffer); | MEM_freeN(mask_buffer); | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* force OpenGL reload */ | /* force OpenGL reload */ | ||||
| static void refresh_images(BakeImages *bake_images) | static void refresh_images(BakeImage *bake_image) | ||||
| { | { | ||||
| int i; | Image *ima = bake_image->image; | ||||
| for (i = 0; i < bake_images->size; i++) { | |||||
| Image *ima = bake_images->data[i].image; | |||||
| LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { | LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { | ||||
| if (tile->ok == IMA_OK_LOADED) { | if (tile->ok == IMA_OK_LOADED) { | ||||
| GPU_free_image(ima); | GPU_free_image(ima); | ||||
| DEG_id_tag_update(&ima->id, 0); | DEG_id_tag_update(&ima->id, 0); | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| static bool write_external_bake_pixels(const char *filepath, | static bool write_external_bake_pixels(const char *filepath, | ||||
| BakePixel pixel_array[], | BakeTask *bt, | ||||
| float *buffer, | BakeResult *bres, | ||||
| const int width, | BakePixel pixel_array[]) | ||||
| const int height, | |||||
| const int margin, | |||||
| ImageFormatData *im_format, | |||||
| const bool is_noncolor) | |||||
| { | { | ||||
| ImBuf *ibuf = NULL; | ImBuf *ibuf = NULL; | ||||
| bool ok = false; | bool ok = false; | ||||
| bool is_float; | bool is_float; | ||||
| is_float = im_format->depth > 8; | is_float = bt->bp->im_format.depth > 8; | ||||
| /* create a new ImBuf */ | /* create a new ImBuf */ | ||||
| ibuf = IMB_allocImBuf(width, height, im_format->planes, (is_float ? IB_rectfloat : IB_rect)); | ibuf = IMB_allocImBuf( | ||||
| bt->width, bt->height, bt->bp->im_format.planes, (is_float ? IB_rectfloat : IB_rect)); | |||||
| if (!ibuf) { | if (!ibuf) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* populates the ImBuf */ | /* populates the ImBuf */ | ||||
| if (is_float) { | if (is_float) { | ||||
| IMB_buffer_float_from_float(ibuf->rect_float, | IMB_buffer_float_from_float(ibuf->rect_float, | ||||
| buffer, | bres->pixels, | ||||
| ibuf->channels, | ibuf->channels, | ||||
| IB_PROFILE_LINEAR_RGB, | IB_PROFILE_LINEAR_RGB, | ||||
| IB_PROFILE_LINEAR_RGB, | IB_PROFILE_LINEAR_RGB, | ||||
| false, | false, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->y, | ibuf->y, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->x); | ibuf->x); | ||||
| } | } | ||||
| else { | else { | ||||
| if (!is_noncolor) { | if (!bres->is_color) { | ||||
| const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get( | const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get( | ||||
| COLOR_ROLE_SCENE_LINEAR); | COLOR_ROLE_SCENE_LINEAR); | ||||
| const char *to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); | const char *to_colorspace = IMB_colormanagement_get_rect_colorspace(ibuf); | ||||
| IMB_colormanagement_transform( | IMB_colormanagement_transform( | ||||
| buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); | bres->pixels, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, false); | ||||
| } | } | ||||
| IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, | IMB_buffer_byte_from_float((unsigned char *)ibuf->rect, | ||||
| buffer, | bres->pixels, | ||||
| ibuf->channels, | ibuf->channels, | ||||
| ibuf->dither, | ibuf->dither, | ||||
| IB_PROFILE_SRGB, | IB_PROFILE_SRGB, | ||||
| IB_PROFILE_SRGB, | IB_PROFILE_SRGB, | ||||
| false, | false, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->y, | ibuf->y, | ||||
| ibuf->x, | ibuf->x, | ||||
| ibuf->x); | ibuf->x); | ||||
| } | } | ||||
| /* margins */ | /* margins */ | ||||
| if (margin > 0) { | if (bt->margin > 0) { | ||||
| char *mask_buffer = NULL; | char *mask_buffer = MEM_callocN(sizeof(char) * bres->num_pixels, "Bake Mask"); | ||||
| const size_t num_pixels = (size_t)width * (size_t)height; | RE_bake_mask_fill(pixel_array, bres->num_pixels, mask_buffer); | ||||
| RE_bake_margin(ibuf, mask_buffer, bt->margin, !bt->do_clear); | |||||
| 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) { | if (mask_buffer) { | ||||
| MEM_freeN(mask_buffer); | MEM_freeN(mask_buffer); | ||||
| } | } | ||||
| } | } | ||||
| if ((ok = BKE_imbuf_write(ibuf, filepath, im_format))) { | if ((ok = BKE_imbuf_write(ibuf, filepath, &bt->bp->im_format))) { | ||||
| #ifndef WIN32 | #ifndef WIN32 | ||||
| chmod(filepath, S_IRUSR | S_IWUSR); | chmod(filepath, S_IRUSR | S_IWUSR); | ||||
| #endif | #endif | ||||
| // printf("%s saving bake map: '%s'\n", __func__, filepath); | // printf("%s saving bake map: '%s'\n", __func__, filepath); | ||||
| } | } | ||||
| /* garbage collection */ | /* garbage collection */ | ||||
| IMB_freeImBuf(ibuf); | 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); | |||||
| } | |||||
| /* if all is good tag image and return true */ | /* if all is good tag image and return true */ | ||||
| static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *reports) | static bool bake_object_check(BakeTask *bt, Object *ob) | ||||
| { | { | ||||
| Image *image; | ReportList *reports = bt->reports; | ||||
| Base *base = BKE_view_layer_base_find(view_layer, ob); | Base *base = BKE_view_layer_base_find(bt->view_layer, ob); | ||||
| void *lock; | |||||
| int i; | |||||
| if (base == NULL) { | if (base == NULL) { | ||||
| BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not in view layer", ob->id.name + 2); | BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not in view layer", ob->id.name + 2); | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (!(base->flag & BASE_ENABLED_RENDER)) { | if (!(base->flag & BASE_ENABLED_RENDER)) { | ||||
| BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not enabled for rendering", ob->id.name + 2); | BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not enabled for rendering", ob->id.name + 2); | ||||
| Show All 9 Lines | else { | ||||
| if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) { | if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) { | ||||
| BKE_reportf( | BKE_reportf( | ||||
| reports, RPT_ERROR, "No active UV layer found in the object \"%s\"", ob->id.name + 2); | reports, RPT_ERROR, "No active UV layer found in the object \"%s\"", ob->id.name + 2); | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| for (i = 0; i < ob->totcol; i++) { | Material *mat_filter = bt->bp->material; | ||||
| 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 (mat_filter) { | ||||
| if (BKE_node_is_connected_to_output(ntree, node)) { | bool has_material = false; | ||||
| /* we don't return false since this may be a false positive | for (int i = 0; i < ob->totcol; i++) { | ||||
| * this can't be RPT_ERROR though, otherwise it prevents | if (mat_filter == BKE_object_material_get(ob, i + 1)) { | ||||
| * multiple highpoly objects to be baked at once */ | has_material = true; | ||||
| BKE_reportf(reports, | break; | ||||
| 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 { | if (!has_material) { | ||||
| BKE_reportf(reports, | BKE_reportf(bt->reports, | ||||
| RPT_ERROR, | RPT_ERROR, | ||||
| "Uninitialized image \"%s\" from object \"%s\"", | "Object \"%s\" doesn't contain the selected material \"%s\"", | ||||
| image->id.name + 2, | ob->id.name + 2, | ||||
| ob->id.name + 2); | mat_filter->id.name + 2); | ||||
| BKE_image_release_ibuf(image, ibuf, lock); | |||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| else { | |||||
| Material *mat = BKE_object_material_get(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; | return true; | ||||
| } | } | ||||
| static bool bake_pass_filter_check(eScenePassType pass_type, | /* before even getting in the bake function we check for some basic errors */ | ||||
| const int pass_filter, | static bool bake_objects_check(BakeTask *bt) | ||||
| ReportList *reports) | |||||
| { | { | ||||
| switch (pass_type) { | ReportList *reports = bt->reports; | ||||
| 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) { | if (!bt->ob) { | ||||
| BKE_report( | BKE_report(reports, RPT_ERROR, "No object selected"); | ||||
| 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; | return false; | ||||
| } | } | ||||
| break; | |||||
| case SCE_PASS_DIFFUSE_COLOR: | if (!bt->bp->image) { | ||||
| case SCE_PASS_GLOSSY_COLOR: | BKE_reportf(reports, RPT_ERROR, "Bake pass \"%s\" has no target image", bt->bp->name); | ||||
| 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; | 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) | |||||
| { | |||||
| 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)) { | if (!bake_object_check(bt, bt->ob)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| for (link = selected_objects->first; link; link = link->next) { | if (bt->bp->bake_from_collection) { | ||||
| Object *ob_iter = (Object *)link->ptr.data; | int tot_objects = 0; | ||||
| if (ob_iter == ob) { | FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN ( | ||||
| bt->bp->bake_from_collection, object, DAG_EVAL_RENDER) { | |||||
| if (object == bt->ob) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { | if (ELEM(object->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_ERROR, | RPT_ERROR, | ||||
| "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, " | "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, " | ||||
| "Surface or Metaball)", | "Surface or Metaball)", | ||||
| ob_iter->id.name + 2); | object->id.name + 2); | ||||
| return false; | return false; | ||||
| } | } | ||||
| tot_objects += 1; | tot_objects += 1; | ||||
| } | } | ||||
| FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; | |||||
| if (tot_objects == 0) { | if (tot_objects == 0) { | ||||
| BKE_report(reports, RPT_ERROR, "No valid selected objects"); | BKE_report(reports, RPT_ERROR, "No valid objects in collection"); | ||||
| return false; | 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; | 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); | |||||
| } | |||||
| } | |||||
| } | |||||
| 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; | |||||
| } | |||||
| /* | /* | ||||
| * returns the total number of pixels | * returns the total number of pixels | ||||
| */ | */ | ||||
| static size_t initialize_internal_images(BakeImages *bake_images, ReportList *reports) | static size_t initialize_internal_image(BakeImage *bake_image, ReportList *reports) | ||||
| { | { | ||||
| int i; | |||||
| size_t tot_size = 0; | |||||
| for (i = 0; i < bake_images->size; i++) { | |||||
| ImBuf *ibuf; | ImBuf *ibuf; | ||||
| void *lock; | void *lock; | ||||
| BakeImage *bk_image = &bake_images->data[i]; | ibuf = BKE_image_acquire_ibuf(bake_image->image, NULL, &lock); | ||||
| ibuf = BKE_image_acquire_ibuf(bk_image->image, NULL, &lock); | |||||
| if (ibuf) { | if (ibuf) { | ||||
| bk_image->width = ibuf->x; | bake_image->width = ibuf->x; | ||||
| bk_image->height = ibuf->y; | bake_image->height = ibuf->y; | ||||
| bk_image->offset = tot_size; | |||||
| tot_size += (size_t)ibuf->x * (size_t)ibuf->y; | BKE_image_release_ibuf(bake_image->image, ibuf, lock); | ||||
| return (size_t)ibuf->x * (size_t)ibuf->y; | |||||
| } | } | ||||
| else { | else { | ||||
| BKE_image_release_ibuf(bk_image->image, ibuf, lock); | BKE_image_release_ibuf(bake_image->image, ibuf, lock); | ||||
| BKE_reportf(reports, RPT_ERROR, "Uninitialized image %s", bk_image->image->id.name + 2); | BKE_reportf(reports, RPT_ERROR, "Uninitialized image %s", bake_image->image->id.name + 2); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| BKE_image_release_ibuf(bk_image->image, ibuf, lock); | |||||
| } | |||||
| return tot_size; | |||||
| } | } | ||||
| /* create new mesh with edit mode changes and modifiers applied */ | /* create new mesh with edit mode changes and modifiers applied */ | ||||
| static Mesh *bake_mesh_new_from_object(Object *object) | static Mesh *bake_mesh_new_from_object(Object *object) | ||||
| { | { | ||||
| Mesh *me = BKE_mesh_new_from_object(NULL, object, false); | Mesh *me = BKE_mesh_new_from_object(NULL, object, false); | ||||
| if (me->flag & ME_AUTOSMOOTH) { | if (me->flag & ME_AUTOSMOOTH) { | ||||
| BKE_mesh_split_faces(me, true); | BKE_mesh_split_faces(me, true); | ||||
| } | } | ||||
| return me; | return me; | ||||
| } | } | ||||
| static int bake(Render *re, | static int bake(BakeTask *bt) | ||||
| 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) | |||||
| { | { | ||||
| Scene *scene = bt->scene; | |||||
| Main *bmain = bt->main; | |||||
| ReportList *reports = bt->reports; | |||||
| Render *re = bt->render; | |||||
| ViewLayer *view_layer = bt->view_layer; | |||||
| char *uv_layer = bt->uv_layer; | |||||
| bool is_cage = bt->is_cage; | |||||
| char *custom_cage = bt->custom_cage; | |||||
| if (G.is_break) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| if (!bake_objects_check(bt)) { | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| RE_SetReports(re, reports); | |||||
| /* We build a depsgraph for the baking, | /* We build a depsgraph for the baking, | ||||
| * so we don't need to change the original data to adjust visibility and modifiers. */ | * so we don't need to change the original data to adjust visibility and modifiers. */ | ||||
| Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); | Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); | ||||
| DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); | DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); | ||||
| int op_result = OPERATOR_CANCELLED; | int op_result = OPERATOR_CANCELLED; | ||||
| bool ok = false; | bool ok = false; | ||||
| Object *ob_cage = NULL; | Object *ob_cage = NULL; | ||||
| Object *ob_cage_eval = NULL; | Object *ob_cage_eval = NULL; | ||||
| Object *ob_low_eval = NULL; | Object *ob_low_eval = NULL; | ||||
| BakeHighPolyData *highpoly = NULL; | BakeHighPolyData *highpoly = NULL; | ||||
| int tot_highpoly = 0; | int tot_highpoly = 0; | ||||
| Mesh *me_low = NULL; | Mesh *me_low = NULL; | ||||
| Mesh *me_cage = NULL; | Mesh *me_cage = NULL; | ||||
| MultiresModifierData *mmd_low = NULL; | MultiresModifierData *mmd_low = NULL; | ||||
| int mmd_flags_low = 0; | int mmd_flags_low = 0; | ||||
| float *result = NULL; | BakeResult result = {0}; | ||||
| BakePixel *pixel_array_low = NULL; | BakePixel *pixel_array_low = NULL; | ||||
| BakePixel *pixel_array_high = NULL; | BakePixel *pixel_array_high = NULL; | ||||
| const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL); | const bool is_save_internal = (bt->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}; | BakeImage bake_image = {0}; | ||||
| size_t num_pixels; | size_t num_pixels; | ||||
| int tot_materials; | |||||
| RE_bake_engine_set_engine_parameters(re, bmain, scene); | RE_bake_engine_set_engine_parameters(re, bmain, scene); | ||||
| if (!RE_bake_has_engine(re)) { | if (!RE_bake_has_engine(re)) { | ||||
| BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); | BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| tot_materials = ob_low->totcol; | |||||
| if (uv_layer && uv_layer[0] != '\0') { | if (uv_layer && uv_layer[0] != '\0') { | ||||
| Mesh *me = (Mesh *)ob_low->data; | Mesh *me = (Mesh *)bt->ob->data; | ||||
| if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) { | if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) { | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_ERROR, | RPT_ERROR, | ||||
| "No UV layer named \"%s\" found in the object \"%s\"", | "No UV layer named \"%s\" found in the object \"%s\"", | ||||
| uv_layer, | uv_layer, | ||||
| ob_low->id.name + 2); | bt->ob->id.name + 2); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| } | } | ||||
| if (tot_materials == 0) { | bake_image.image = bt->bp->image; | ||||
| if (is_save_internal) { | bake_image.mat_mask_length = bt->ob->totcol; | ||||
| BKE_report( | bake_image.mat_mask = MEM_mallocN(sizeof(bool) * bake_image.mat_mask_length, | ||||
| reports, RPT_ERROR, "No active image found, add a material or bake to an external file"); | "bake material mask"); | ||||
| goto cleanup; | Material *mat_filter = bt->bp->material; | ||||
| for (int i = 0; i < bake_image.mat_mask_length; i++) { | |||||
| bake_image.mat_mask[i] = (mat_filter == NULL) || | |||||
| (mat_filter == BKE_object_material_get(bt->ob, i + 1)); | |||||
| } | } | ||||
| 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) { | if (is_save_internal) { | ||||
| num_pixels = initialize_internal_images(&bake_images, reports); | num_pixels = initialize_internal_image(&bake_image, reports); | ||||
| if (num_pixels == 0) { | if (num_pixels == 0) { | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* when saving externally always use the size specified in the UI */ | /* when saving externally always use the size specified in the UI */ | ||||
| num_pixels = (size_t)width * (size_t)height * bake_images.size; | num_pixels = (size_t)bt->width * (size_t)bt->height; | ||||
| for (int i = 0; i < bake_images.size; i++) { | bake_image.width = bt->width; | ||||
| bake_images.data[i].width = width; | bake_image.height = bt->height; | ||||
| bake_images.data[i].height = height; | bake_image.image = NULL; | ||||
| bake_images.data[i].offset = (is_split_materials ? num_pixels : 0); | |||||
| bake_images.data[i].image = NULL; | |||||
| } | } | ||||
| if (!is_split_materials) { | if (bt->bp->bake_from_collection) { | ||||
| /* 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; | tot_highpoly = 0; | ||||
| for (link = selected_objects->first; link; link = link->next) { | FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN ( | ||||
| Object *ob_iter = link->ptr.data; | bt->bp->bake_from_collection, object, DAG_EVAL_RENDER) { | ||||
| if (object == bt->ob) { | |||||
| if (ob_iter == ob_low) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| tot_highpoly++; | tot_highpoly++; | ||||
| } | } | ||||
| FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; | |||||
| if (is_cage && custom_cage[0] != '\0') { | if (is_cage && custom_cage[0] != '\0') { | ||||
| ob_cage = BLI_findstring(&bmain->objects, custom_cage, offsetof(ID, name) + 2); | ob_cage = BLI_findstring(&bmain->objects, custom_cage, offsetof(ID, name) + 2); | ||||
| if (ob_cage == NULL || ob_cage->type != OB_MESH) { | if (ob_cage == NULL || ob_cage->type != OB_MESH) { | ||||
| BKE_report(reports, RPT_ERROR, "No valid cage object"); | BKE_report(reports, RPT_ERROR, "No valid cage object"); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| else { | else { | ||||
| ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); | ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage); | ||||
| ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; | ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; | ||||
| ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); | 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 */ | /* for multires bake, use linear UV subdivision to match low res UVs */ | ||||
| if (pass_type == SCE_PASS_NORMAL && normal_space == R_BAKE_SPACE_TANGENT && | if (bt->normal_space == R_BAKE_SPACE_TANGENT && !bt->bp->bake_from_collection) { | ||||
| !is_selected_to_active) { | mmd_low = (MultiresModifierData *)modifiers_findByType(bt->ob, eModifierType_Multires); | ||||
| mmd_low = (MultiresModifierData *)modifiers_findByType(ob_low, eModifierType_Multires); | |||||
| if (mmd_low) { | if (mmd_low) { | ||||
| mmd_flags_low = mmd_low->flags; | mmd_flags_low = mmd_low->flags; | ||||
| mmd_low->uv_smooth = SUBSURF_UV_SMOOTH_NONE; | mmd_low->uv_smooth = SUBSURF_UV_SMOOTH_NONE; | ||||
| } | } | ||||
| } | } | ||||
| result.num_pixels = num_pixels; | |||||
| /* Make sure depsgraph is up to date. */ | /* Make sure depsgraph is up to date. */ | ||||
| BKE_scene_graph_update_tagged(depsgraph, bmain); | BKE_scene_graph_update_tagged(depsgraph, bmain); | ||||
| ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low); | ob_low_eval = DEG_get_evaluated_object(depsgraph, bt->ob); | ||||
| /* get the mesh as it arrives in the renderer */ | /* get the mesh as it arrives in the renderer */ | ||||
| me_low = bake_mesh_new_from_object(ob_low_eval); | me_low = bake_mesh_new_from_object(ob_low_eval); | ||||
| /* populate the pixel array with the face data */ | /* populate the pixel array with the face data */ | ||||
| if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) { | if ((bt->bp->bake_from_collection && (ob_cage == NULL) && is_cage) == false) { | ||||
| RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images, uv_layer); | RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_image, uv_layer); | ||||
| } | } | ||||
| /* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh) */ | /* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh) */ | ||||
| if (is_selected_to_active) { | if (bt->bp->bake_from_collection) { | ||||
| CollectionPointerLink *link; | |||||
| int i = 0; | int i = 0; | ||||
| /* prepare cage mesh */ | /* prepare cage mesh */ | ||||
| if (ob_cage) { | if (ob_cage) { | ||||
| me_cage = bake_mesh_new_from_object(ob_cage_eval); | me_cage = bake_mesh_new_from_object(ob_cage_eval); | ||||
| if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) { | if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) { | ||||
| BKE_report(reports, | BKE_report(reports, | ||||
| RPT_ERROR, | RPT_ERROR, | ||||
| Show All 30 Lines | else if (is_cage) { | ||||
| * single modification to this object all the possible dependencies for evaluation are | * single modification to this object all the possible dependencies for evaluation are | ||||
| * already up to date. This means we can do a cheap single object update | * already up to date. This means we can do a cheap single object update | ||||
| * (as an opposite of full depsgraph update). */ | * (as an opposite of full depsgraph update). */ | ||||
| BKE_object_eval_reset(ob_low_eval); | BKE_object_eval_reset(ob_low_eval); | ||||
| BKE_object_handle_data_update(depsgraph, scene, ob_low_eval); | BKE_object_handle_data_update(depsgraph, scene, ob_low_eval); | ||||
| } | } | ||||
| me_cage = BKE_mesh_new_from_object(NULL, ob_low_eval, false); | me_cage = BKE_mesh_new_from_object(NULL, ob_low_eval, false); | ||||
| RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); | RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_image, uv_layer); | ||||
| } | } | ||||
| highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); | highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); | ||||
| /* populate highpoly array */ | /* populate highpoly array */ | ||||
| for (link = selected_objects->first; link; link = link->next) { | FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN ( | ||||
| Object *ob_iter = link->ptr.data; | bt->bp->bake_from_collection, object, DAG_EVAL_RENDER) { | ||||
| if (object == bt->ob) { | |||||
| if (ob_iter == ob_low) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| /* initialize highpoly_data */ | /* initialize highpoly_data */ | ||||
| highpoly[i].ob = ob_iter; | highpoly[i].ob = object; | ||||
| highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); | highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, object); | ||||
| highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; | highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; | ||||
| highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ||||
| highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false); | highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false); | ||||
| /* lowpoly to highpoly transformation matrix */ | /* lowpoly to highpoly transformation matrix */ | ||||
| copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); | copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); | ||||
| invert_m4_m4(highpoly[i].imat, highpoly[i].obmat); | invert_m4_m4(highpoly[i].imat, highpoly[i].obmat); | ||||
| highpoly[i].is_flip_object = is_negative_m4(highpoly[i].ob->obmat); | highpoly[i].is_flip_object = is_negative_m4(highpoly[i].ob->obmat); | ||||
| i++; | i++; | ||||
| } | } | ||||
| FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; | |||||
| BLI_assert(i == tot_highpoly); | BLI_assert(i == tot_highpoly); | ||||
| if (ob_cage != NULL) { | if (ob_cage != NULL) { | ||||
| ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; | ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER; | ||||
| ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ||||
| } | } | ||||
| ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; | ob_low_eval->restrictflag |= OB_RESTRICT_RENDER; | ||||
| ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); | ||||
| pixel_array_high = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); | |||||
| /* populate the pixel arrays with the corresponding face data for each high poly object */ | /* populate the pixel arrays with the corresponding face data for each high poly object */ | ||||
| if (!RE_bake_pixels_populate_from_objects(me_low, | if (!RE_bake_pixels_populate_from_objects(me_low, | ||||
| pixel_array_low, | pixel_array_low, | ||||
| pixel_array_high, | pixel_array_high, | ||||
| highpoly, | highpoly, | ||||
| tot_highpoly, | tot_highpoly, | ||||
| num_pixels, | num_pixels, | ||||
| ob_cage != NULL, | ob_cage != NULL, | ||||
| cage_extrusion, | bt->cage_extrusion, | ||||
| ob_low_eval->obmat, | ob_low_eval->obmat, | ||||
| (ob_cage ? ob_cage->obmat : ob_low_eval->obmat), | (ob_cage ? ob_cage->obmat : ob_low_eval->obmat), | ||||
| me_cage)) { | me_cage)) { | ||||
| BKE_report(reports, RPT_ERROR, "Error handling selected objects"); | BKE_report(reports, RPT_ERROR, "Error handling selected objects"); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| /* the baking itself */ | /* the baking itself */ | ||||
| for (i = 0; i < tot_highpoly; i++) { | for (i = 0; i < tot_highpoly; i++) { | ||||
| ok = RE_bake_engine(re, | ok = RE_bake_engine(re, depsgraph, bt->bp, highpoly[i].ob, i, pixel_array_high, &result); | ||||
| depsgraph, | |||||
| highpoly[i].ob, | |||||
| i, | |||||
| pixel_array_high, | |||||
| num_pixels, | |||||
| depth, | |||||
| pass_type, | |||||
| pass_filter, | |||||
| result); | |||||
| if (!ok) { | if (!ok) { | ||||
| BKE_reportf( | BKE_reportf( | ||||
| reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2); | reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* If low poly is not renderable it should have failed long ago. */ | /* If low poly is not renderable it should have failed long ago. */ | ||||
| BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0); | BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0); | ||||
| if (RE_bake_has_engine(re)) { | if (RE_bake_has_engine(re)) { | ||||
| ok = RE_bake_engine(re, | ok = RE_bake_engine(re, depsgraph, bt->bp, ob_low_eval, 0, pixel_array_low, &result); | ||||
| depsgraph, | |||||
| ob_low_eval, | |||||
| 0, | |||||
| pixel_array_low, | |||||
| num_pixels, | |||||
| depth, | |||||
| pass_type, | |||||
| pass_filter, | |||||
| result); | |||||
| } | } | ||||
| else { | else { | ||||
| BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); | BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); | ||||
| goto cleanup; | goto cleanup; | ||||
| } | } | ||||
| } | } | ||||
| /* normal space conversion | /* normal space conversion | ||||
| * the normals are expected to be in world space, +X +Y +Z */ | * the normals are expected to be in world space, +X +Y +Z */ | ||||
| if (ok && pass_type == SCE_PASS_NORMAL) { | if (ok && result.is_normal) { | ||||
| switch (normal_space) { | const eBakeNormalSwizzle *normal_swizzle = bt->normal_swizzle; | ||||
| switch (bt->normal_space) { | |||||
| case R_BAKE_SPACE_WORLD: { | case R_BAKE_SPACE_WORLD: { | ||||
| /* Cycles internal format */ | /* Cycles internal format */ | ||||
| if ((normal_swizzle[0] == R_BAKE_POSX) && (normal_swizzle[1] == R_BAKE_POSY) && | if ((normal_swizzle[0] == R_BAKE_POSX) && (normal_swizzle[1] == R_BAKE_POSY) && | ||||
| (normal_swizzle[2] == R_BAKE_POSZ)) { | (normal_swizzle[2] == R_BAKE_POSZ)) { | ||||
| break; | break; | ||||
| } | } | ||||
| else { | else { | ||||
| RE_bake_normal_world_to_world( | RE_bake_normal_world_to_world( | ||||
| pixel_array_low, num_pixels, depth, result, normal_swizzle); | pixel_array_low, num_pixels, result.depth, result.pixels, normal_swizzle); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case R_BAKE_SPACE_OBJECT: { | case R_BAKE_SPACE_OBJECT: { | ||||
| RE_bake_normal_world_to_object( | RE_bake_normal_world_to_object( | ||||
| pixel_array_low, num_pixels, depth, result, ob_low_eval, normal_swizzle); | pixel_array_low, num_pixels, result.depth, result.pixels, ob_low_eval, normal_swizzle); | ||||
| break; | break; | ||||
| } | } | ||||
| case R_BAKE_SPACE_TANGENT: { | case R_BAKE_SPACE_TANGENT: { | ||||
| if (is_selected_to_active) { | if (bt->bp->bake_from_collection) { | ||||
| RE_bake_normal_world_to_tangent(pixel_array_low, | RE_bake_normal_world_to_tangent(pixel_array_low, | ||||
| num_pixels, | num_pixels, | ||||
| depth, | result.depth, | ||||
| result, | result.pixels, | ||||
| me_low, | me_low, | ||||
| normal_swizzle, | normal_swizzle, | ||||
| ob_low_eval->obmat); | ob_low_eval->obmat); | ||||
| } | } | ||||
| else { | else { | ||||
| /* from multiresolution */ | /* from multiresolution */ | ||||
| Mesh *me_nores = NULL; | Mesh *me_nores = NULL; | ||||
| ModifierData *md = NULL; | ModifierData *md = NULL; | ||||
| int mode; | int mode; | ||||
| BKE_object_eval_reset(ob_low_eval); | BKE_object_eval_reset(ob_low_eval); | ||||
| md = modifiers_findByType(ob_low_eval, eModifierType_Multires); | md = modifiers_findByType(ob_low_eval, eModifierType_Multires); | ||||
| if (md) { | if (md) { | ||||
| mode = md->mode; | mode = md->mode; | ||||
| md->mode &= ~eModifierMode_Render; | md->mode &= ~eModifierMode_Render; | ||||
| } | } | ||||
| /* Evaluate modifiers again. */ | /* Evaluate modifiers again. */ | ||||
| me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false); | me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false); | ||||
| RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer); | RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_image, uv_layer); | ||||
| RE_bake_normal_world_to_tangent(pixel_array_low, | RE_bake_normal_world_to_tangent(pixel_array_low, | ||||
| num_pixels, | num_pixels, | ||||
| depth, | result.depth, | ||||
| result, | result.pixels, | ||||
| me_nores, | me_nores, | ||||
| normal_swizzle, | normal_swizzle, | ||||
| ob_low_eval->obmat); | ob_low_eval->obmat); | ||||
| BKE_id_free(NULL, &me_nores->id); | BKE_id_free(NULL, &me_nores->id); | ||||
| if (md) { | if (md) { | ||||
| md->mode = mode; | md->mode = mode; | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (!ok) { | if (bt->do_clear) { | ||||
| BKE_reportf(reports, RPT_ERROR, "Problem baking object \"%s\"", ob_low->id.name + 2); | RE_bake_ibuf_clear(bake_image.image, result.fill_color); | ||||
| } | |||||
| if (!ok || !result.pixels) { | |||||
| BKE_reportf(reports, RPT_ERROR, "Problem baking object \"%s\"", bt->ob->id.name + 2); | |||||
| op_result = OPERATOR_CANCELLED; | op_result = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| /* save the results */ | /* save the results */ | ||||
| for (int i = 0; i < bake_images.size; i++) { | |||||
| BakeImage *bk_image = &bake_images.data[i]; | |||||
| if (is_save_internal) { | if (is_save_internal) { | ||||
| ok = write_internal_bake_pixels(bk_image->image, | ok = write_internal_bake_pixels(bt, &bake_image, &result, pixel_array_low); | ||||
| 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 */ | /* might be read by UI to set active image for display */ | ||||
| bake_update_image(sa, bk_image->image); | bake_update_image(bt->sa, bake_image.image); | ||||
| if (!ok) { | if (!ok) { | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_ERROR, | RPT_ERROR, | ||||
| "Problem saving the bake map internally for object \"%s\"", | "Problem saving the bake map internally for object \"%s\"", | ||||
| ob_low->id.name + 2); | bt->ob->id.name + 2); | ||||
| op_result = OPERATOR_CANCELLED; | op_result = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| BKE_report(reports, | BKE_report(reports, | ||||
| RPT_INFO, | RPT_INFO, | ||||
| "Baking map saved to internal image, save it externally or pack it"); | "Baking map saved to internal image, save it externally or pack it"); | ||||
| op_result = OPERATOR_FINISHED; | op_result = OPERATOR_FINISHED; | ||||
| } | } | ||||
| } | } | ||||
| /* save externally */ | /* save externally */ | ||||
| else { | else { | ||||
| BakeData *bake = &scene->r.bake; | |||||
| char name[FILE_MAX]; | char name[FILE_MAX]; | ||||
| BKE_image_path_from_imtype(name, | BKE_image_path_from_imtype(name, | ||||
| filepath, | bt->filepath, | ||||
| BKE_main_blendfile_path(bmain), | BKE_main_blendfile_path(bmain), | ||||
| 0, | 0, | ||||
| bake->im_format.imtype, | bt->bp->im_format.imtype, | ||||
| true, | true, | ||||
| false, | false, | ||||
| NULL); | NULL); | ||||
| if (is_automatic_name) { | if (bt->is_automatic_name) { | ||||
| BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); | BLI_path_suffix(name, FILE_MAX, bt->ob->id.name + 2, "_"); | ||||
| BLI_path_suffix(name, FILE_MAX, identifier, "_"); | BLI_path_suffix(name, FILE_MAX, result.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 */ | /* save it externally */ | ||||
| ok = write_external_bake_pixels(name, | ok = write_external_bake_pixels(name, bt, &result, pixel_array_low); | ||||
| 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) { | if (!ok) { | ||||
| BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name); | BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name); | ||||
| op_result = OPERATOR_CANCELLED; | op_result = OPERATOR_CANCELLED; | ||||
| } | } | ||||
| else { | else { | ||||
| BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\"", name); | BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\"", name); | ||||
| op_result = OPERATOR_FINISHED; | op_result = OPERATOR_FINISHED; | ||||
| } | } | ||||
| if (!is_split_materials) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (is_save_internal) { | if (is_save_internal) { | ||||
| refresh_images(&bake_images); | refresh_images(&bake_image); | ||||
| } | } | ||||
| cleanup: | cleanup: | ||||
| if (highpoly) { | if (highpoly) { | ||||
| int i; | int i; | ||||
| for (i = 0; i < tot_highpoly; i++) { | for (i = 0; i < tot_highpoly; i++) { | ||||
| if (highpoly[i].me != NULL) { | if (highpoly[i].me != NULL) { | ||||
| Show All 10 Lines | cleanup: | ||||
| if (pixel_array_low) { | if (pixel_array_low) { | ||||
| MEM_freeN(pixel_array_low); | MEM_freeN(pixel_array_low); | ||||
| } | } | ||||
| if (pixel_array_high) { | if (pixel_array_high) { | ||||
| MEM_freeN(pixel_array_high); | MEM_freeN(pixel_array_high); | ||||
| } | } | ||||
| if (bake_images.data) { | if (result.pixels) { | ||||
| MEM_freeN(bake_images.data); | MEM_freeN(result.pixels); | ||||
| } | |||||
| if (bake_images.lookup) { | |||||
| MEM_freeN(bake_images.lookup); | |||||
| } | } | ||||
| if (result) { | if (bake_image.mat_mask) { | ||||
| MEM_freeN(result); | MEM_freeN(bake_image.mat_mask); | ||||
| } | } | ||||
| if (me_low != NULL) { | if (me_low != NULL) { | ||||
| BKE_id_free(NULL, &me_low->id); | BKE_id_free(NULL, &me_low->id); | ||||
| } | } | ||||
| if (me_cage != NULL) { | if (me_cage != NULL) { | ||||
| BKE_id_free(NULL, &me_cage->id); | BKE_id_free(NULL, &me_cage->id); | ||||
| } | } | ||||
| DEG_graph_free(depsgraph); | DEG_graph_free(depsgraph); | ||||
| RE_SetReports(re, NULL); | |||||
| return op_result; | return op_result; | ||||
| } | } | ||||
| static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr) | static void bake_init_api_data(wmOperator *op, bContext *C, Object *ob, BakePass *bp, BakeTask *bt) | ||||
| { | { | ||||
| bool is_save_internal; | |||||
| bScreen *sc = CTX_wm_screen(C); | bScreen *sc = CTX_wm_screen(C); | ||||
| bkr->ob = CTX_data_active_object(C); | bt->ob = ob; | ||||
| bkr->main = CTX_data_main(C); | bt->main = CTX_data_main(C); | ||||
| bkr->view_layer = CTX_data_view_layer(C); | bt->view_layer = CTX_data_view_layer(C); | ||||
| bkr->scene = CTX_data_scene(C); | bt->scene = CTX_data_scene(C); | ||||
| bkr->sa = sc ? BKE_screen_find_big_area(sc, SPACE_IMAGE, 10) : NULL; | bt->sa = sc ? BKE_screen_find_big_area(sc, SPACE_IMAGE, 10) : NULL; | ||||
| bkr->pass_type = RNA_enum_get(op->ptr, "type"); | #define RNA_ISDEF(name) (RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, name))) | ||||
| bkr->pass_filter = RNA_enum_get(op->ptr, "pass_filter"); | |||||
| bkr->margin = RNA_int_get(op->ptr, "margin"); | if (RNA_ISDEF("filepath")) | ||||
| RNA_string_get(op->ptr, "filepath", bt->filepath); | |||||
| bkr->save_mode = RNA_enum_get(op->ptr, "save_mode"); | else | ||||
| is_save_internal = (bkr->save_mode == R_BAKE_SAVE_INTERNAL); | BLI_strncpy(bt->filepath, bp->filepath, sizeof(bt->filepath)); | ||||
| bkr->is_clear = RNA_boolean_get(op->ptr, "use_clear"); | if (RNA_ISDEF("width")) | ||||
| bkr->is_split_materials = (!is_save_internal) && RNA_boolean_get(op->ptr, "use_split_materials"); | bt->width = RNA_int_get(op->ptr, "width"); | ||||
| bkr->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name"); | else | ||||
| bkr->is_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active"); | bt->width = bp->width; | ||||
| bkr->is_cage = RNA_boolean_get(op->ptr, "use_cage"); | |||||
| bkr->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion"); | if (RNA_ISDEF("height")) | ||||
| bt->height = RNA_int_get(op->ptr, "height"); | |||||
| else | |||||
| bt->height = bp->width; | |||||
| if (RNA_ISDEF("margin")) | |||||
| bt->margin = RNA_int_get(op->ptr, "margin"); | |||||
| else | |||||
| bt->margin = bp->margin; | |||||
| if (RNA_ISDEF("cage_extrusion")) | |||||
| bt->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion"); | |||||
| else | |||||
| bt->cage_extrusion = bp->cage_extrusion; | |||||
| if (RNA_ISDEF("cage_object")) | |||||
| RNA_string_get(op->ptr, "cage_object", bt->custom_cage); | |||||
| else if (bp->cage_object) | |||||
| BLI_strncpy(bt->custom_cage, bp->cage_object->id.name + 2, sizeof(bt->custom_cage)); | |||||
| if (RNA_ISDEF("normal_space")) | |||||
| bt->normal_space = RNA_enum_get(op->ptr, "normal_space"); | |||||
| else | |||||
| bt->normal_space = bp->normal_space; | |||||
| if (RNA_ISDEF("normal_r")) | |||||
| bt->normal_swizzle[0] = RNA_enum_get(op->ptr, "normal_r"); | |||||
| else | |||||
| bt->normal_swizzle[0] = bp->normal_swizzle[0]; | |||||
| if (RNA_ISDEF("normal_g")) | |||||
| bt->normal_swizzle[1] = RNA_enum_get(op->ptr, "normal_g"); | |||||
| else | |||||
| bt->normal_swizzle[1] = bp->normal_swizzle[1]; | |||||
| if (RNA_ISDEF("normal_b")) | |||||
| bt->normal_swizzle[2] = RNA_enum_get(op->ptr, "normal_b"); | |||||
| else | |||||
| bt->normal_swizzle[2] = bp->normal_swizzle[2]; | |||||
| if (RNA_ISDEF("save_mode")) | |||||
| bt->save_mode = RNA_enum_get(op->ptr, "save_mode"); | |||||
| else | |||||
| bt->save_mode = bp->save_mode; | |||||
| if (RNA_ISDEF("use_clear")) | |||||
| bt->do_clear = RNA_boolean_get(op->ptr, "use_clear"); | |||||
| else | |||||
| bt->do_clear = (bp->flag & R_BAKE_CLEAR) != 0; | |||||
| if (RNA_ISDEF("use_cage")) | |||||
| bt->is_cage = RNA_boolean_get(op->ptr, "use_cage"); | |||||
| else | |||||
| bt->is_cage = (bp->flag & R_BAKE_CAGE) != 0; | |||||
| if (RNA_ISDEF("use_automatic_name")) | |||||
| bt->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name"); | |||||
| else | |||||
| bt->is_automatic_name = (bp->flag & R_BAKE_AUTO_NAME) != 0; | |||||
| if (RNA_ISDEF("uv_layer")) | |||||
| RNA_string_get(op->ptr, "uv_layer", bt->uv_layer); | |||||
| else | |||||
| BLI_strncpy(bt->uv_layer, bp->uvlayer_name, sizeof(bt->uv_layer)); | |||||
| bkr->normal_space = RNA_enum_get(op->ptr, "normal_space"); | bt->bp = bp; | ||||
| 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"); | bt->reports = op->reports; | ||||
| bkr->height = RNA_int_get(op->ptr, "height"); | |||||
| bkr->identifier = ""; | |||||
| RNA_string_get(op->ptr, "uv_layer", bkr->uv_layer); | bt->render = RE_NewSceneRender(bt->scene); | ||||
| RE_test_break_cb(bt->render, NULL, bake_break); | |||||
| 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); | |||||
| } | |||||
| CTX_data_selected_objects(C, &bkr->selected_objects); | |||||
| bkr->reports = op->reports; | |||||
| bkr->result = OPERATOR_CANCELLED; | |||||
| bkr->render = RE_NewSceneRender(bkr->scene); | |||||
| /* XXX hack to force saving to always be internal. Whether (and how) to support | /* XXX hack to force saving to always be internal. Whether (and how) to support | ||||
| * external saving will be addressed later */ | * external saving will be addressed later */ | ||||
| bkr->save_mode = R_BAKE_SAVE_INTERNAL; | bt->save_mode = R_BAKE_SAVE_INTERNAL; | ||||
| } | } | ||||
| static int bake_exec(bContext *C, wmOperator *op) | static bool build_bake_task_list(bContext *C, wmOperator *op, ListBase *tasks) | ||||
| { | { | ||||
| Render *re; | ListBase selected_objects = {0}; | ||||
| int result = OPERATOR_CANCELLED; | CTX_data_selected_objects(C, &selected_objects); | ||||
| 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); | LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_objects) { | ||||
| re = bkr.render; | Object *ob = (Object *)link->ptr.data; | ||||
| /* setup new render */ | |||||
| RE_test_break_cb(re, NULL, bake_break); | |||||
| if (!bake_pass_filter_check(bkr.pass_type, bkr.pass_filter, bkr.reports)) { | for (BakePass *bp = ob->bake_passes.first; bp; bp = bp->next) { | ||||
| goto finally; | if (!(bp->flag & R_BAKE_ENABLED)) | ||||
| } | continue; | ||||
| 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); | BakeTask *bt = MEM_callocN(sizeof(BakeTask), "render bake pass"); | ||||
| bake_init_api_data(op, C, ob, bp, bt); | |||||
| finally: | BLI_addtail(tasks, bt); | ||||
| 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) { | BLI_freelistN(&selected_objects); | ||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| RE_SetReports(bkr->render, NULL); | if (BLI_listbase_is_empty(tasks)) { | ||||
| BKE_report(op->reports, RPT_ERROR, "No enabled Bake passes"); | |||||
| return false; | |||||
| } | } | ||||
| static void bake_freejob(void *bkv) | return true; | ||||
| { | |||||
| BakeAPIRender *bkr = (BakeAPIRender *)bkv; | |||||
| BLI_freelistN(&bkr->selected_objects); | |||||
| MEM_freeN(bkr); | |||||
| G.is_rendering = false; | |||||
| } | } | ||||
| static void bake_set_props(wmOperator *op, Scene *scene) | static int bake_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| PropertyRNA *prop; | int result = OPERATOR_CANCELLED; | ||||
| 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"); | G.is_break = false; | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | G.is_rendering = true; | ||||
| RNA_property_int_set(op->ptr, prop, bake->width); | |||||
| } | |||||
| prop = RNA_struct_find_property(op->ptr, "height"); | ListBase tasks = {NULL}; | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | if (!build_bake_task_list(C, op, &tasks)) { | ||||
| RNA_property_int_set(op->ptr, prop, bake->width); | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| prop = RNA_struct_find_property(op->ptr, "margin"); | LISTBASE_FOREACH (BakeTask *, bt, &tasks) { | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | result = bake(bt); | ||||
| RNA_property_int_set(op->ptr, prop, bake->margin); | |||||
| } | |||||
| prop = RNA_struct_find_property(op->ptr, "use_selected_to_active"); | if (result == OPERATOR_CANCELLED) { | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | break; | ||||
| 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"); | BLI_freelistN(&tasks); | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | G.is_rendering = false; | ||||
| 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"); | return result; | ||||
| 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"); | static void bake_startjob(void *tasks_v, short *UNUSED(stop), short *do_update, float *progress) | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | { | ||||
| RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[0]); | ListBase *tasks = (ListBase *)tasks_v; | ||||
| } | |||||
| prop = RNA_struct_find_property(op->ptr, "normal_g"); | int task_num = 0, num_tasks = BLI_listbase_count(tasks); | ||||
| 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"); | LISTBASE_FOREACH (BakeTask *, bt, tasks) { | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | /* setup new render */ | ||||
| RNA_property_enum_set(op->ptr, prop, bake->normal_swizzle[2]); | bt->do_update = do_update; | ||||
| } | bt->progress = progress; | ||||
| bt->task_num = task_num++; | |||||
| bt->num_tasks = num_tasks; | |||||
| RE_progress_cb(bt->render, bt, bake_progress_update); | |||||
| prop = RNA_struct_find_property(op->ptr, "save_mode"); | int result = bake(bt); | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | if (result == OPERATOR_CANCELLED) { | ||||
| RNA_property_enum_set(op->ptr, prop, bake->save_mode); | break; | ||||
| } | } | ||||
| 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"); | static void bake_freejob(void *tasks_v) | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | { | ||||
| RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_SPLIT_MAT) != 0); | ListBase *tasks = (ListBase *)tasks_v; | ||||
| } | |||||
| prop = RNA_struct_find_property(op->ptr, "use_automatic_name"); | BLI_freelistN(tasks); | ||||
| if (!RNA_property_is_set(op->ptr, prop)) { | MEM_freeN(tasks); | ||||
| RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_AUTO_NAME) != 0); | |||||
| } | |||||
| prop = RNA_struct_find_property(op->ptr, "pass_filter"); | G.is_rendering = false; | ||||
| 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)) | static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | ||||
| { | { | ||||
| wmJob *wm_job; | wmJob *wm_job; | ||||
| BakeAPIRender *bkr; | |||||
| Render *re; | |||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| bake_set_props(op, scene); | |||||
| /* only one render job at a time */ | /* only one render job at a time */ | ||||
| if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_OBJECT_BAKE)) { | if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_OBJECT_BAKE)) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| bkr = MEM_mallocN(sizeof(BakeAPIRender), "render bake"); | ListBase *tasks = MEM_callocN(sizeof(ListBase), "render bake tasks"); | ||||
| if (!build_bake_task_list(C, op, tasks)) { | |||||
| /* init bake render */ | return OPERATOR_CANCELLED; | ||||
| 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 job */ | /* setup job */ | ||||
| wm_job = WM_jobs_get(CTX_wm_manager(C), | wm_job = WM_jobs_get(CTX_wm_manager(C), | ||||
| CTX_wm_window(C), | CTX_wm_window(C), | ||||
| scene, | scene, | ||||
| "Texture Bake", | "Texture Bake", | ||||
| WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, | WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, | ||||
| WM_JOB_TYPE_OBJECT_BAKE); | WM_JOB_TYPE_OBJECT_BAKE); | ||||
| WM_jobs_customdata_set(wm_job, bkr, bake_freejob); | WM_jobs_customdata_set(wm_job, tasks, bake_freejob); | ||||
| WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO - only draw bake image, can we enforce this */ | 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); | WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL); | ||||
| G.is_break = false; | G.is_break = false; | ||||
| G.is_rendering = true; | 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 */ | /* add modal handler for ESC */ | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); | WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| void OBJECT_OT_bake(wmOperatorType *ot) | void OBJECT_OT_bake(wmOperatorType *ot) | ||||
| { | { | ||||
| PropertyRNA *prop; | |||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Bake"; | ot->name = "Bake"; | ||||
| ot->description = "Bake image textures of selected objects"; | ot->description = "Bake image textures of selected objects"; | ||||
| ot->idname = "OBJECT_OT_bake"; | ot->idname = "OBJECT_OT_bake"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->exec = bake_exec; | ot->exec = bake_exec; | ||||
| ot->modal = bake_modal; | ot->modal = bake_modal; | ||||
| ot->invoke = bake_invoke; | ot->invoke = bake_invoke; | ||||
| ot->poll = ED_operator_object_active_editable_mesh; | 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, | RNA_def_string_file_path(ot->srna, | ||||
| "filepath", | "filepath", | ||||
| NULL, | NULL, | ||||
| FILE_MAX, | FILE_MAX, | ||||
| "File Path", | "File Path", | ||||
| "Image filepath to use when saving externally"); | "Image filepath to use when saving externally"); | ||||
| RNA_def_int(ot->srna, | RNA_def_int(ot->srna, | ||||
| "width", | "width", | ||||
| ▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | RNA_def_enum(ot->srna, | ||||
| "Save Mode", | "Save Mode", | ||||
| "Choose how to save the baking map"); | "Choose how to save the baking map"); | ||||
| RNA_def_boolean(ot->srna, | RNA_def_boolean(ot->srna, | ||||
| "use_clear", | "use_clear", | ||||
| false, | false, | ||||
| "Clear", | "Clear", | ||||
| "Clear Images before baking (only for internal saving)"); | "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_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, | RNA_def_boolean(ot->srna, | ||||
| "use_automatic_name", | "use_automatic_name", | ||||
| false, | false, | ||||
| "Automatic Name", | "Automatic Name", | ||||
| "Automatically name the output file with the pass type"); | "Automatically name the output file with the pass type"); | ||||
| RNA_def_string(ot->srna, | RNA_def_string(ot->srna, | ||||
| "uv_layer", | "uv_layer", | ||||
| NULL, | NULL, | ||||
| MAX_CUSTOMDATA_LAYER_NAME, | MAX_CUSTOMDATA_LAYER_NAME, | ||||
| "UV Layer", | "UV Layer", | ||||
| "UV layer to override active"); | "UV layer to override active"); | ||||
| } | } | ||||