Second try to fix missing previews of mat/tex/etc. in .blend files.
authorBastien Montagne <montagne29@wanadoo.fr>
Mon, 12 Jan 2015 13:44:54 +0000 (14:44 +0100)
committerBastien Montagne <montagne29@wanadoo.fr>
Mon, 12 Jan 2015 14:13:46 +0000 (15:13 +0100)
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

release/scripts/startup/bl_ui/space_info.py
source/blender/editors/include/ED_render.h
source/blender/editors/include/UI_interface_icons.h
source/blender/editors/interface/interface_icons.c
source/blender/editors/render/render_preview.c
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_operators.c

index 2b075128fef22abd4a22c484a75ef4bc710b7a3a..b642b61fcdc23b6d5feda8b67cf0356fb8bd03c9 100644 (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"
 
index 32fa641e3aee425487a5be9305c57872bdb866e3..de3843c91eb3b3d558b6ad67da562525610b2af7 100644 (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);
 
index 2ba3d309aa79d5bcc8d8d44147f5b987b60e5c49..74927428363f2219e23da4e5c345572523c566ac 100644 (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);
index ac9abe8e7810abb8510d4f804222681c60fae0b5..679681cb37202585de844477625853ba8ce5de52 100644 (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;
index 8833d76fde2f659424836b32d70a1a61448de647..ea80a07fdd4ce2c3f326c07dd8209b6ade7650c2 100644 (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;
 
index fabb69b8eff6e55ccb6b5ab23413cbaf16a60a1b..c1c31f6795d5872f5b25921a3d61ff9ecffb1abe 100644 (file)
 #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.
  */
index 5b944a7b1ec828ad10ce84b17882f43c3afa38ee..02a029fc3f3fb6b051d316be8265a44bc6bc8e04 100644 (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"
 #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 */