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:
Diffstat (limited to 'source/blender/editors/render/render_preview.c')
-rw-r--r--source/blender/editors/render/render_preview.c160
1 files changed, 128 insertions, 32 deletions
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 7f7ee45a803..fe1e850dcba 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -54,7 +54,9 @@
#include "DNA_space_types.h"
#include "DNA_world_types.h"
+#include "BKE_animsys.h"
#include "BKE_appdir.h"
+#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
@@ -93,6 +95,7 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "ED_armature.h"
#include "ED_datafiles.h"
#include "ED_render.h"
#include "ED_screen.h"
@@ -151,6 +154,10 @@ typedef struct IconPreview {
void *owner;
ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */
ListBase sizes;
+
+ /* May be NULL, is used for rendering IDs that require some other object for it to be applied on
+ * before the ID can be represented as an image, for example when rendering an Action. */
+ struct Object *active_object;
} IconPreview;
/** \} */
@@ -227,7 +234,7 @@ static Scene *preview_get_scene(Main *pr_main)
return pr_main->scenes.first;
}
-static const char *preview_collection_name(const char pr_type)
+static const char *preview_collection_name(const ePreviewType pr_type)
{
switch (pr_type) {
case MA_FLAT:
@@ -258,10 +265,7 @@ static const char *preview_collection_name(const char pr_type)
}
}
-static void set_preview_visibility(Scene *scene,
- ViewLayer *view_layer,
- char pr_type,
- int pr_method)
+static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePreviewType pr_type)
{
/* Set appropriate layer as visible. */
LayerCollection *lc = view_layer->layer_collections.first;
@@ -275,7 +279,11 @@ static void set_preview_visibility(Scene *scene,
lc->collection->flag |= COLLECTION_RESTRICT_RENDER;
}
}
+}
+static void switch_preview_floor_visibility(ViewLayer *view_layer,
+ const ePreviewRenderMethod pr_method)
+{
/* Hide floor for icon renders. */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (STREQ(base->object->id.name + 2, "Floor")) {
@@ -287,7 +295,15 @@ static void set_preview_visibility(Scene *scene,
}
}
}
+}
+static void set_preview_visibility(Scene *scene,
+ ViewLayer *view_layer,
+ const ePreviewType pr_type,
+ const ePreviewRenderMethod pr_method)
+{
+ switch_preview_collection_visibilty(view_layer, pr_type);
+ switch_preview_floor_visibility(view_layer, pr_method);
BKE_layer_collection_sync(scene, view_layer);
}
@@ -341,6 +357,38 @@ static ID *duplicate_ids(ID *id, const bool allow_failure)
}
}
+static World *preview_get_world(Main *pr_main)
+{
+ World *result = NULL;
+ const char *world_name = "World";
+ result = BLI_findstring(&pr_main->worlds, world_name, offsetof(ID, name) + 2);
+
+ /* No world found return first world. */
+ if (result == NULL) {
+ result = pr_main->worlds.first;
+ }
+
+ BLI_assert_msg(result, "Preview file has no world.");
+ return result;
+}
+
+static void preview_sync_exposure(World *dst, const World *src)
+{
+ BLI_assert(dst);
+ BLI_assert(src);
+ dst->exp = src->exp;
+ dst->range = src->range;
+}
+
+static World *preview_prepare_world(Main *pr_main, const World *world)
+{
+ World *result = preview_get_world(pr_main);
+ if (world) {
+ preview_sync_exposure(result, world);
+ }
+ return result;
+}
+
/* call this with a pointer to initialize preview scene */
/* call this with NULL to restore assigned ID pointers in preview scene */
static Scene *preview_prepare_scene(
@@ -361,13 +409,7 @@ static Scene *preview_prepare_scene(
/* this flag tells render to not execute depsgraph or ipos etc */
sce->r.scemode |= R_BUTS_PREVIEW;
- /* set world always back, is used now */
- sce->world = pr_main->worlds.first;
- /* now: exposure copy */
- if (scene->world) {
- sce->world->exp = scene->world->exp;
- sce->world->range = scene->world->range;
- }
+ BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine));
sce->r.color_mgt_flag = scene->r.color_mgt_flag;
BKE_color_managed_display_settings_copy(&sce->display_settings, &scene->display_settings);
@@ -393,13 +435,13 @@ static Scene *preview_prepare_scene(
sce->r.cfra = scene->r.cfra;
+ /* Setup the world. */
+ sce->world = preview_prepare_world(pr_main, scene->world);
+
if (id_type == ID_TE) {
/* Texture is not actually rendered with engine, just set dummy value. */
BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine));
}
- else {
- BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine));
- }
if (id_type == ID_MA) {
Material *mat = NULL, *origmat = (Material *)id;
@@ -425,14 +467,12 @@ static Scene *preview_prepare_scene(
sce->world->horb = 0.05f;
}
- if (sp->pr_method == PR_ICON_RENDER && sp->pr_main == G_pr_main_grease_pencil) {
- /* For grease pencil, always use sphere for icon renders. */
- set_preview_visibility(sce, view_layer, MA_SPHERE_A, sp->pr_method);
- }
- else {
- /* Use specified preview shape for both preview panel and icon previews. */
- set_preview_visibility(sce, view_layer, mat->pr_type, sp->pr_method);
- }
+ /* For grease pencil, always use sphere for icon renders. */
+ const ePreviewType preview_type = (sp->pr_method == PR_ICON_RENDER &&
+ sp->pr_main == G_pr_main_grease_pencil) ?
+ MA_SPHERE_A :
+ mat->pr_type;
+ set_preview_visibility(sce, view_layer, preview_type, sp->pr_method);
if (sp->pr_method != PR_ICON_RENDER) {
if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
@@ -684,9 +724,11 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r
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). */
+ /* 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;
+ /* Current frame. */
+ int cfra;
int sizex;
int sizey;
};
@@ -720,6 +762,10 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
Depsgraph **r_depsgraph)
{
Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene");
+ /* Preview need to be in the current frame to get a thumbnail similar of what
+ * viewport displays. */
+ CFRA = preview_data->cfra;
+
ViewLayer *view_layer = scene->view_layers.first;
Depsgraph *depsgraph = DEG_graph_new(
preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT);
@@ -764,6 +810,7 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
.pr_main = preview_main,
/* Act on a copy. */
.object = (Object *)preview->id_copy,
+ .cfra = preview->scene->r.cfra,
.sizex = preview_sized->sizex,
.sizey = preview_sized->sizey,
};
@@ -813,9 +860,50 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
/** \name Action Preview
* \{ */
-/* Render a pose. It is assumed that the pose has already been applied and that the scene camera is
- * capturing the pose. In other words, this function just renders from the scene camera without
- * evaluating the Action stored in preview->id. */
+static struct PoseBackup *action_preview_render_prepare(IconPreview *preview)
+{
+ Object *object = preview->active_object;
+ if (object == NULL) {
+ WM_report(RPT_WARNING, "No active object, unable to apply the Action before rendering");
+ return NULL;
+ }
+ if (object->pose == NULL) {
+ WM_reportf(RPT_WARNING,
+ "Object %s has no pose, unable to apply the Action before rendering",
+ object->id.name + 2);
+ return NULL;
+ }
+
+ /* Create a backup of the current pose. */
+ struct bAction *action = (struct bAction *)preview->id;
+ struct PoseBackup *pose_backup = ED_pose_backup_create_all_bones(object, action);
+
+ /* Apply the Action as pose, so that it can be rendered. This assumes the Action represents a
+ * single pose, and that thus the evaluation time doesn't matter. */
+ AnimationEvalContext anim_eval_context = {preview->depsgraph, 0.0f};
+ BKE_pose_apply_action_all_bones(object, action, &anim_eval_context);
+
+ /* Force evaluation of the new pose, before the preview is rendered. */
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ DEG_evaluate_on_refresh(preview->depsgraph);
+
+ return pose_backup;
+}
+
+static void action_preview_render_cleanup(IconPreview *preview, struct PoseBackup *pose_backup)
+{
+ if (pose_backup == NULL) {
+ return;
+ }
+ ED_pose_backup_restore(pose_backup);
+ ED_pose_backup_free(pose_backup);
+
+ DEG_id_tag_update(&preview->active_object->id, ID_RECALC_GEOMETRY);
+}
+
+/* Render a pose from the scene camera. It is assumed that the scene camera is
+ * capturing the pose. The pose is applied temporarily to the current object
+ * before rendering. */
static void action_preview_render(IconPreview *preview, IconPreviewSize *preview_sized)
{
char err_out[256] = "";
@@ -827,6 +915,9 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview
BLI_assert(depsgraph != NULL);
BLI_assert(preview->scene == DEG_get_input_scene(depsgraph));
+ /* Apply the pose before getting the evaluated scene, so that the new pose is evaluated. */
+ struct PoseBackup *pose_backup = action_preview_render_prepare(preview);
+
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *camera_eval = scene_eval->camera;
if (camera_eval == NULL) {
@@ -850,6 +941,8 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview
NULL,
err_out);
+ action_preview_render_cleanup(preview, pose_backup);
+
if (err_out[0] != '\0') {
printf("Error rendering Action %s preview: %s\n", preview->id->name + 2, err_out);
}
@@ -1255,8 +1348,9 @@ static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect)
scaledy = (float)h;
}
- ex = (short)scaledx;
- ey = (short)scaledy;
+ /* Scaling down must never assign zero width/height, see: T89868. */
+ ex = MAX2(1, (short)scaledx);
+ ey = MAX2(1, (short)scaledy);
dx = (w - ex) / 2;
dy = (h - ey) / 2;
@@ -1497,8 +1591,8 @@ static void icon_preview_startjob_all_sizes(void *customdata,
/* check_engine_supports_preview() checks whether the engine supports "preview mode" (think:
* Material Preview). This check is only relevant when the render function called below is
- * going to use such a mode. Object and Action render functions use Solid mode, though, so they
- * can skip this test. */
+ * going to use such a mode. Object and Action render functions use Solid mode, though, so
+ * they can skip this test. */
/* TODO: Decouple the ID-type-specific render functions from this function, so that it's not
* necessary to know here what happens inside lower-level functions. */
const bool use_solid_render_mode = (ip->id != NULL) && ELEM(GS(ip->id->name), ID_OB, ID_AC);
@@ -1625,6 +1719,7 @@ void ED_preview_icon_render(
/* 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);
+ ip.active_object = CTX_data_active_object(C);
icon_preview_add_size(&ip, rect, sizex, sizey);
@@ -1666,6 +1761,7 @@ void ED_preview_icon_job(
ip->bmain = CTX_data_main(C);
ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ip->scene = DEG_get_input_scene(ip->depsgraph);
+ ip->active_object = CTX_data_active_object(C);
ip->owner = owner;
ip->id = id;
ip->id_copy = duplicate_ids(id, false);