Second try to fix missing previews of mat/tex/etc. in .blend files.

This time, it's a dedicated operator user has to run before saving the file.

And it recursively check all IDs linked from each scene, therefore rendering
materials etc. previews using a scene they are used in.

Note the renderengine issue is not completely addressed this way
(existing code for icon previews seems to ignore completely other engines,
and IDs not linked anywhere (fake-user ones) will be rendered with current scene's engine
as fallback, also you can get a material linked to an hidden object in a scene, etc.).

Reviewers: sergey, campbellbarton

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D980
This commit is contained in:
Bastien Montagne 2015-01-12 14:44:54 +01:00
parent 51779d9407
commit 3027ff8b13
7 changed files with 117 additions and 33 deletions

View File

@ -132,6 +132,7 @@ class INFO_MT_file(Menu):
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.link", text="Link", icon='LINK_BLEND')
layout.operator("wm.append", text="Append", icon='APPEND_BLEND')
layout.menu("INFO_MT_file_previews")
layout.separator()
@ -195,6 +196,15 @@ class INFO_MT_file_external_data(Menu):
layout.operator("file.find_missing_files")
class INFO_MT_file_previews(Menu):
bl_label = "Data Previews"
def draw(self, context):
layout = self.layout
layout.operator("wm.previews_ensure")
class INFO_MT_game(Menu):
bl_label = "Game"

View File

@ -72,7 +72,7 @@ void ED_preview_init_dbase(void);
void ED_preview_free_dbase(void);
void ED_preview_shader_job(const struct bContext *C, void *owner, struct ID *id, struct ID *parent, struct MTex *slot, int sizex, int sizey, int method);
void ED_preview_icon_render(const struct bContext *C, void *owner, struct ID *id, unsigned int *rect, int sizex, int sizey);
void ED_preview_icon_render(struct Scene *scene, struct ID *id, unsigned int *rect, int sizex, int sizey);
void ED_preview_icon_job(const struct bContext *C, void *owner, struct ID *id, unsigned int *rect, int sizex, int sizey);
void ED_preview_kill_jobs(struct wmWindowManager *wm, struct Main *bmain);

View File

@ -64,7 +64,8 @@ void UI_icons_init(int first_dyn_id);
int UI_icon_get_width(int icon_id);
int UI_icon_get_height(int icon_id);
void UI_id_icon_render(const struct bContext *C, struct ID *id, const bool big, const bool use_job);
void UI_id_icon_render(
const struct bContext *C, struct Scene *scene, struct ID *id, const bool big, const bool use_job);
void UI_icon_draw(float x, float y, int icon_id);
void UI_icon_draw_preview(float x, float y, int icon_id);

View File

@ -931,7 +931,8 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size)
/* only called when icon has changed */
/* only call with valid pointer from UI_icon_draw */
static void icon_set_image(const bContext *C, ID *id, PreviewImage *prv_img, enum eIconSizes size, const bool use_job)
static void icon_set_image(
const bContext *C, Scene *scene, ID *id, PreviewImage *prv_img, enum eIconSizes size, const bool use_job)
{
if (!prv_img) {
if (G.debug & G_DEBUG)
@ -946,8 +947,11 @@ static void icon_set_image(const bContext *C, ID *id, PreviewImage *prv_img, enu
ED_preview_icon_job(C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
}
else {
if (!scene) {
scene = CTX_data_scene(C);
}
/* Immediate version */
ED_preview_icon_render(C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
ED_preview_icon_render(scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
}
}
@ -1155,25 +1159,26 @@ static void icon_draw_size(float x, float y, int icon_id, float aspect, float al
}
}
static void ui_id_preview_image_render_size(const bContext *C, ID *id, PreviewImage *pi, int size, const bool use_job)
static void ui_id_preview_image_render_size(
const bContext *C, Scene *scene, ID *id, PreviewImage *pi, int size, const bool use_job)
{
if ((pi->changed[size] || !pi->rect[size])) { /* changed only ever set by dynamic icons */
/* create the rect if necessary */
icon_set_image(C, id, pi, size, use_job);
icon_set_image(C, scene, id, pi, size, use_job);
pi->changed[size] = 0;
}
}
void UI_id_icon_render(const bContext *C, ID *id, const bool big, const bool use_job)
void UI_id_icon_render(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job)
{
PreviewImage *pi = BKE_previewimg_get(id);
if (pi) {
if (big)
ui_id_preview_image_render_size(C, id, pi, ICON_SIZE_PREVIEW, use_job); /* bigger preview size */
ui_id_preview_image_render_size(C, scene, id, pi, ICON_SIZE_PREVIEW, use_job); /* bigger preview size */
else
ui_id_preview_image_render_size(C, id, pi, ICON_SIZE_ICON, use_job); /* icon size */
ui_id_preview_image_render_size(C, scene, id, pi, ICON_SIZE_ICON, use_job); /* icon size */
}
}
@ -1189,7 +1194,7 @@ static void ui_id_brush_render(const bContext *C, ID *id)
/* check if rect needs to be created; changed
* only set by dynamic icons */
if ((pi->changed[i] || !pi->rect[i])) {
icon_set_image(C, id, pi, i, true);
icon_set_image(C, NULL, id, pi, i, true);
pi->changed[i] = 0;
}
}
@ -1265,7 +1270,7 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big)
case ID_LA: /* fall through */
iconid = BKE_icon_getid(id);
/* checks if not exists, or changed */
UI_id_icon_render(C, id, big, true);
UI_id_icon_render(C, NULL, id, big, true);
break;
default:
break;

View File

@ -1098,14 +1098,13 @@ static void icon_preview_free(void *customdata)
MEM_freeN(ip);
}
void ED_preview_icon_render(const bContext *C, void *UNUSED(owner), ID *id, unsigned int *rect, int sizex, int sizey)
void ED_preview_icon_render(Scene *scene, ID *id, unsigned int *rect, int sizex, int sizey)
{
IconPreview ip = {0};
short stop = false, update = false;
float progress = 0.0f;
/* customdata for preview thread */
ip.scene = CTX_data_scene(C);
ip.scene = scene;
ip.owner = id;
ip.id = id;

View File

@ -101,7 +101,6 @@
#include "GHOST_Path-api.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_view2d.h"
#include "GPU_draw.h"
@ -895,24 +894,6 @@ bool write_crash_blend(void)
}
}
static void UNUSED_FUNCTION(wm_ensure_previews)(bContext *C, Main *mainvar)
{
ListBase *lb[] = {&mainvar->mat, &mainvar->tex, &mainvar->image, &mainvar->world, &mainvar->lamp, NULL};
ID *id;
int i;
for (i = 0; lb[i]; i++) {
for (id = lb[i]->first; id; id = id->next) {
/* Only preview non-library datablocks, lib ones do not pertain to this .blend file!
* Same goes for ID with no user. */
if (!id->lib && (id->us != 0)) {
UI_id_icon_render(C, id, false, false);
UI_id_icon_render(C, id, true, false);
}
}
}
}
/**
* \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way.
*/

View File

@ -61,6 +61,7 @@
#include "BLI_blenlib.h"
#include "BLI_dial.h"
#include "BLI_dynstr.h" /*for WM_operator_pystring */
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@ -76,6 +77,7 @@
#include "BKE_idprop.h"
#include "BKE_image.h"
#include "BKE_library.h"
#include "BKE_library_query.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_material.h"
@ -105,6 +107,7 @@
#include "RNA_enum_types.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "WM_api.h"
@ -4705,6 +4708,90 @@ static void WM_OT_dependency_relations(wmOperatorType *ot)
ot->exec = dependency_relations_exec;
}
/* *************************** Mat/tex/etc. previews generation ************* */
typedef struct PreviewsIDEnsureStack {
Scene *scene;
BLI_LINKSTACK_DECLARE(id_stack, ID *);
} PreviewsIDEnsureStack;
static void previews_id_ensure(bContext *C, Scene *scene, ID *id)
{
BLI_assert(ELEM(GS(id->name), ID_MA, ID_TE, ID_IM, ID_WO, ID_LA));
/* Only preview non-library datablocks, lib ones do not pertain to this .blend file!
* Same goes for ID with no user. */
if (!id->lib && (id->us != 0)) {
UI_id_icon_render(C, scene, id, false, false);
UI_id_icon_render(C, scene, id, true, false);
}
}
static bool previews_id_ensure_callback(void *todo_v, ID **idptr, int UNUSED(cd_flag))
{
PreviewsIDEnsureStack *todo = todo_v;
ID *id = *idptr;
if (id && (id->flag & LIB_DOIT)) {
if (ELEM(GS(id->name), ID_MA, ID_TE, ID_IM, ID_WO, ID_LA)) {
previews_id_ensure(NULL, todo->scene, id);
}
id->flag &= ~LIB_DOIT; /* Tag the ID as done in any case. */
BLI_LINKSTACK_PUSH(todo->id_stack, id);
}
return true;
}
static int previews_ensure_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ListBase *lb[] = {&bmain->mat, &bmain->tex, &bmain->image, &bmain->world, &bmain->lamp, NULL};
PreviewsIDEnsureStack preview_id_stack;
Scene *scene;
ID *id;
int i;
/* We use LIB_DOIT to check whether we have already handled a given ID or not. */
BKE_main_id_flag_all(bmain, LIB_DOIT, true);
BLI_LINKSTACK_INIT(preview_id_stack.id_stack);
for (scene = bmain->scene.first; scene; scene = scene->id.next) {
preview_id_stack.scene = scene;
id = (ID *)scene;
do {
/* This will loop over all IDs linked by current one, render icons for them if needed,
* and add them to 'todo' preview_id_stack. */
BKE_library_foreach_ID_link(id, previews_id_ensure_callback, &preview_id_stack, IDWALK_READONLY);
} while((id = BLI_LINKSTACK_POP(preview_id_stack.id_stack)));
}
BLI_LINKSTACK_FREE(preview_id_stack.id_stack);
/* Check a last time for ID not used (fake users only, in theory), and
* do our best for those, using current scene... */
for (i = 0; lb[i]; i++) {
for (id = lb[i]->first; id; id = id->next) {
previews_id_ensure(C, NULL, id);
}
}
return OPERATOR_FINISHED;
}
static void WM_OT_previews_ensure(wmOperatorType *ot)
{
ot->name = "Refresh DataBlock Previews";
ot->idname = "WM_OT_previews_ensure";
ot->description = "Ensure datablock previews are available and up-to-date "
"(to be saved in .blend file, only for some types like materials, textures, etc.)";
ot->exec = previews_ensure_exec;
}
/* ******************************************************* */
static void operatortype_ghash_free_cb(wmOperatorType *ot)
@ -4768,6 +4855,7 @@ void wm_operatortype_init(void)
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
#endif
WM_operatortype_append(WM_OT_previews_ensure);
}
/* circleselect-like modal operators */