Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2020-12-14 14:48:16 +0300
committerJulian Eisel <julian@blender.org>2020-12-14 15:17:57 +0300
commit4b0396695c622d1ac8669600fa820e80b1f0979f (patch)
tree0d7938ad051d727be476b416e40869a90e87731b
parent732d0b458b6f9024b285747a643cacb128888b8c (diff)
UI/Assets: Support generating object preview images
Object previews are really helpful for visual data-block selection, like asset browsing. Having them be generative should also be quite handy and should work well enough in many, if not most cases. What this does is simple: * Place the object (actually a deep copy of it, for thread safety) in a virtual .blend into an empty scene/view-layer. * Add a camera, point it towards the front of the object, assuming that means pointing towards its +Y axis. * Use "Camera Fit Frame to Selected" logic to put the object into frame. * Create a threaded off-screen render. Of course, such an automatic preview will not work in all situations. E.g. it currently does a bad job capturing a single plane. We could add options for more advanced automatic previews, but probably custom previews is more important, which I committed already (812ea9184221). Part of the first Asset Browser milestone. Check the #asset_browser_milestone_1 project milestone on developer.blender.org. Reviewed as part of https://developer.blender.org/D9719. Reviewed by: Bastien Montagne, Brecht Van Lommel
-rw-r--r--source/blender/blenkernel/intern/object.c4
-rw-r--r--source/blender/editors/include/ED_view3d.h5
-rw-r--r--source/blender/editors/render/render_preview.c260
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c35
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c28
5 files changed, 255 insertions, 77 deletions
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index bdb907df1ac..f11e7bc429d 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -2208,7 +2208,9 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
BLI_listbase_clear(&psysn->childcachebufs);
if (flag & LIB_ID_CREATE_NO_MAIN) {
- BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0);
+ /* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview
+ * creation. */
+ // BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0);
psysn->flag |= PSYS_SHARED_CACHES;
BLI_assert(psysn->pointcache != NULL);
}
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 596533406c3..13687bf0450 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -141,6 +141,11 @@ void ED_view3d_to_object(const struct Depsgraph *depsgraph,
const float quat[4],
const float dist);
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ struct Depsgraph *depsgraph,
+ const struct Scene *scene,
+ struct Object *camera_ob);
+
void ED_view3d_lastview_store(struct RegionView3D *rv3d);
/* Depth buffer */
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index d67113083a3..4466d9f434d 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -68,6 +68,7 @@
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_node.h"
+#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
#include "BKE_world.h"
@@ -94,12 +95,16 @@
#include "ED_datafiles.h"
#include "ED_render.h"
#include "ED_screen.h"
+#include "ED_view3d.h"
+#include "ED_view3d_offscreen.h"
#ifndef NDEBUG
/* Used for database init assert(). */
# include "BLI_threads.h"
#endif
+static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect);
+
ImBuf *get_brush_icon(Brush *brush)
{
static const int flags = IB_rect | IB_multilayer | IB_metadata;
@@ -336,7 +341,7 @@ static World *preview_get_localized_world(ShaderPreview *sp, World *world)
return sp->worldcopy;
}
-static ID *duplicate_ids(ID *id)
+static ID *duplicate_ids(ID *id, const bool allow_failure)
{
if (id == NULL) {
/* Non-ID preview render. */
@@ -344,6 +349,7 @@ static ID *duplicate_ids(ID *id)
}
switch (GS(id->name)) {
+ case ID_OB:
case ID_MA:
case ID_TE:
case ID_LA:
@@ -357,7 +363,9 @@ static ID *duplicate_ids(ID *id)
case ID_SCR:
return NULL;
default:
- BLI_assert(!"ID type preview not supported.");
+ if (!allow_failure) {
+ BLI_assert(!"ID type preview not supported.");
+ }
return NULL;
}
}
@@ -698,6 +706,132 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r
}
}
+/* **************************** Object preview ****************** */
+
+struct ObjectPreviewData {
+ /* The main for the preview, not of the current file. */
+ Main *pr_main;
+ /* Copy of the object to create the preview for. The copy is for thread safety (and to insert it
+ * into an own main). */
+ Object *object;
+ int sizex;
+ int sizey;
+};
+
+static Object *object_preview_camera_create(
+ Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey)
+{
+ Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
+
+ float rotmat[3][3];
+ float dummyscale[3];
+ mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat);
+
+ /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */
+ float drotmat[3][3];
+ axis_angle_to_mat3_single(drotmat, 'X', M_PI_2);
+ mul_m3_m3_post(rotmat, drotmat);
+
+ camera->rotmode = ROT_MODE_QUAT;
+ mat3_to_quat(camera->quat, rotmat);
+
+ /* shader_preview_render() does this too. */
+ if (sizex > sizey) {
+ ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex;
+ }
+
+ return camera;
+}
+
+static Scene *object_preview_scene_create(const struct ObjectPreviewData *preview_data,
+ Depsgraph **r_depsgraph)
+{
+ Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene");
+ ViewLayer *view_layer = scene->view_layers.first;
+ Depsgraph *depsgraph = DEG_graph_new(
+ preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT);
+
+ BLI_assert(preview_data->object != NULL);
+ BLI_addtail(&preview_data->pr_main->objects, preview_data->object);
+
+ BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object);
+
+ Object *camera_object = object_preview_camera_create(preview_data->pr_main,
+ view_layer,
+ preview_data->object,
+ preview_data->sizex,
+ preview_data->sizey);
+
+ scene->camera = camera_object;
+ scene->r.xsch = preview_data->sizex;
+ scene->r.ysch = preview_data->sizey;
+ scene->r.size = 100;
+
+ Base *preview_base = BKE_view_layer_base_find(view_layer, preview_data->object);
+ /* For 'view selected' below. */
+ preview_base->flag |= BASE_SELECTED;
+
+ DEG_graph_build_from_view_layer(depsgraph);
+ DEG_evaluate_on_refresh(depsgraph);
+
+ ED_view3d_camera_to_view_selected(preview_data->pr_main, depsgraph, scene, camera_object);
+
+ BKE_scene_graph_update_tagged(depsgraph, preview_data->pr_main);
+
+ *r_depsgraph = depsgraph;
+ return scene;
+}
+
+static void object_preview_render(IconPreview *preview, IconPreviewSize *preview_sized)
+{
+ Main *preview_main = BKE_main_new();
+ const float pixelsize_old = U.pixelsize;
+ char err_out[256] = "unknown";
+
+ BLI_assert(preview->id_copy && (preview->id_copy != preview->id));
+
+ struct ObjectPreviewData preview_data = {
+ .pr_main = preview_main,
+ /* Act on a copy. */
+ .object = (Object *)preview->id_copy,
+ .sizex = preview_sized->sizex,
+ .sizey = preview_sized->sizey,
+ };
+ Depsgraph *depsgraph;
+ Scene *scene = object_preview_scene_create(&preview_data, &depsgraph);
+
+ /* Ownership is now ours. */
+ preview->id_copy = NULL;
+
+ U.pixelsize = 2.0f;
+
+ ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple(
+ depsgraph,
+ DEG_get_evaluated_scene(depsgraph),
+ NULL,
+ OB_SOLID,
+ DEG_get_evaluated_object(depsgraph, scene->camera),
+ preview_sized->sizex,
+ preview_sized->sizey,
+ IB_rect,
+ V3D_OFSDRAW_NONE,
+ R_ALPHAPREMUL,
+ NULL,
+ NULL,
+ err_out);
+ /* TODO color-management? */
+
+ U.pixelsize = pixelsize_old;
+
+ if (ibuf) {
+ icon_copy_rect(ibuf, preview_sized->sizex, preview_sized->sizey, preview_sized->rect);
+ IMB_freeImBuf(ibuf);
+ }
+
+ DEG_graph_free(depsgraph);
+ BKE_main_free(preview_main);
+}
+
/* **************************** new shader preview system ****************** */
/* inside thread, called by renderer, sets job update value */
@@ -1188,27 +1322,54 @@ static void common_preview_startjob(void *customdata,
}
}
-/* exported functions */
-
-static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey)
+/**
+ * Some ID types already have their own, more focused rendering (only objects right now). This is
+ * for the other ones, which all share #ShaderPreview and some functions.
+ */
+static void other_id_types_preview_render(IconPreview *ip,
+ IconPreviewSize *cur_size,
+ const bool is_deferred,
+ short *stop,
+ short *do_update,
+ float *progress)
{
- IconPreviewSize *cur_size = ip->sizes.first, *new_size;
+ ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
+ const bool is_render = !is_deferred;
+
+ /* These types don't use the ShaderPreview mess, they have their own types and functions. */
+ BLI_assert(!ELEM(GS(ip->id->name), ID_OB));
+
+ /* construct shader preview from image size and previewcustomdata */
+ sp->scene = ip->scene;
+ sp->owner = ip->owner;
+ sp->sizex = cur_size->sizex;
+ sp->sizey = cur_size->sizey;
+ sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
+ sp->pr_rect = cur_size->rect;
+ sp->id = ip->id;
+ sp->id_copy = ip->id_copy;
+ sp->bmain = ip->bmain;
+ sp->own_id_copy = false;
+ Material *ma = NULL;
- while (cur_size) {
- if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
- /* requested size is already in list, no need to add it again */
- return;
+ if (is_render) {
+ BLI_assert(ip->id);
+
+ /* grease pencil use its own preview file */
+ if (GS(ip->id->name) == ID_MA) {
+ ma = (Material *)ip->id;
}
- cur_size = cur_size->next;
+ if ((ma == NULL) || (ma->gp_style == NULL)) {
+ sp->pr_main = G_pr_main;
+ }
+ else {
+ sp->pr_main = G_pr_main_grease_pencil;
+ }
}
- new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
- new_size->sizex = sizex;
- new_size->sizey = sizey;
- new_size->rect = rect;
-
- BLI_addtail(&ip->sizes, new_size);
+ common_preview_startjob(sp, stop, do_update, progress);
+ shader_preview_free(sp);
}
static void icon_preview_startjob_all_sizes(void *customdata,
@@ -1235,41 +1396,36 @@ static void icon_preview_startjob_all_sizes(void *customdata,
continue;
}
- ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
- const bool is_render = !(prv->tag & PRV_TAG_DEFFERED);
-
- /* construct shader preview from image size and previewcustomdata */
- sp->scene = ip->scene;
- sp->owner = ip->owner;
- sp->sizex = cur_size->sizex;
- sp->sizey = cur_size->sizey;
- sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
- sp->pr_rect = cur_size->rect;
- sp->id = ip->id;
- sp->id_copy = ip->id_copy;
- sp->bmain = ip->bmain;
- sp->own_id_copy = false;
- Material *ma = NULL;
-
- if (is_render) {
- BLI_assert(ip->id);
-
- /* grease pencil use its own preview file */
- if (GS(ip->id->name) == ID_MA) {
- ma = (Material *)ip->id;
- }
+ if (ELEM(GS(ip->id->name), ID_OB)) {
+ /* Much simpler than the ShaderPreview mess used for other ID types. */
+ object_preview_render(ip, cur_size);
+ }
+ else {
+ other_id_types_preview_render(
+ ip, cur_size, (prv->tag & PRV_TAG_DEFFERED), stop, do_update, progress);
+ }
+ }
+}
- if ((ma == NULL) || (ma->gp_style == NULL)) {
- sp->pr_main = G_pr_main;
- }
- else {
- sp->pr_main = G_pr_main_grease_pencil;
- }
+static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey)
+{
+ IconPreviewSize *cur_size = ip->sizes.first, *new_size;
+
+ while (cur_size) {
+ if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
+ /* requested size is already in list, no need to add it again */
+ return;
}
- common_preview_startjob(sp, stop, do_update, progress);
- shader_preview_free(sp);
+ cur_size = cur_size->next;
}
+
+ new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
+ new_size->sizex = sizex;
+ new_size->sizey = sizey;
+ new_size->rect = rect;
+
+ BLI_addtail(&ip->sizes, new_size);
}
static void icon_preview_endjob(void *customdata)
@@ -1333,7 +1489,9 @@ void ED_preview_icon_render(Main *bmain, Scene *scene, ID *id, uint *rect, int s
ip.scene = scene;
ip.owner = BKE_previewimg_id_ensure(id);
ip.id = id;
- ip.id_copy = duplicate_ids(id);
+ /* Control isn't given back to the caller until the preview is done. So we don't need to copy
+ * the ID to avoid thread races. */
+ ip.id_copy = duplicate_ids(id, true);
icon_preview_add_size(&ip, rect, sizex, sizey);
@@ -1376,7 +1534,7 @@ void ED_preview_icon_job(
ip->scene = CTX_data_scene(C);
ip->owner = owner;
ip->id = id;
- ip->id_copy = duplicate_ids(id);
+ ip->id_copy = duplicate_ids(id, false);
icon_preview_add_size(ip, rect, sizex, sizey);
@@ -1445,7 +1603,7 @@ void ED_preview_shader_job(const bContext *C,
sp->sizey = sizey;
sp->pr_method = method;
sp->id = id;
- sp->id_copy = duplicate_ids(id);
+ sp->id_copy = duplicate_ids(id, false);
sp->own_id_copy = true;
sp->parent = parent;
sp->slot = slot;
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 1be9bd27c7a..2b7b8255068 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -1620,6 +1620,41 @@ void ED_view3d_to_object(const Depsgraph *depsgraph,
BKE_object_apply_mat4_ex(ob, mat, ob_eval->parent, ob_eval->parentinv, true);
}
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *camera_ob)
+{
+ Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
+ float co[3]; /* the new location to apply */
+ float scale; /* only for ortho cameras */
+
+ if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, co, &scale)) {
+ ObjectTfmProtectedChannels obtfm;
+ float obmat_new[4][4];
+
+ if ((camera_ob_eval->type == OB_CAMERA) &&
+ (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
+ ((Camera *)camera_ob->data)->ortho_scale = scale;
+ }
+
+ copy_m4_m4(obmat_new, camera_ob_eval->obmat);
+ copy_v3_v3(obmat_new[3], co);
+
+ /* only touch location */
+ BKE_object_tfm_protected_backup(camera_ob, &obtfm);
+ BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
+ BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
+
+ /* notifiers */
+ DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM);
+
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index a24f59019f0..9d947384bf0 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -535,40 +535,18 @@ void VIEW3D_OT_camera_to_view(wmOperatorType *ot)
* meant to take into account vertex/bone selection for eg. */
static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C); /* can be NULL */
Object *camera_ob = v3d ? v3d->camera : scene->camera;
- Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
-
- float r_co[3]; /* the new location to apply */
- float r_scale; /* only for ortho cameras */
- if (camera_ob_eval == NULL) {
+ if (camera_ob == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active camera");
return OPERATOR_CANCELLED;
}
- /* this function does all the important stuff */
- if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, r_co, &r_scale)) {
- ObjectTfmProtectedChannels obtfm;
- float obmat_new[4][4];
-
- if ((camera_ob_eval->type == OB_CAMERA) &&
- (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
- ((Camera *)camera_ob->data)->ortho_scale = r_scale;
- }
-
- copy_m4_m4(obmat_new, camera_ob_eval->obmat);
- copy_v3_v3(obmat_new[3], r_co);
-
- /* only touch location */
- BKE_object_tfm_protected_backup(camera_ob, &obtfm);
- BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
- BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
-
- /* notifiers */
- DEG_id_tag_update(&camera_ob->id, ID_RECALC_TRANSFORM);
+ if (ED_view3d_camera_to_view_selected(bmain, depsgraph, scene, camera_ob)) {
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, camera_ob);
return OPERATOR_FINISHED;
}