Assets: temporarily apply pose when generating preview image

When generating a preview image for a pose, temporarily apply it to the
armature. Contrary to the usual pose application, this ignores the
selected bones and always applies the entire pose.
This commit is contained in:
Sybren A. Stüvel 2021-07-12 13:23:59 +02:00
parent f3610a23d1
commit 6e01b52100
1 changed files with 55 additions and 0 deletions

View File

@ -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;
/** \} */
@ -813,6 +820,47 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
/** \name Action Preview
* \{ */
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. 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. */
@ -827,6 +875,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 +901,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);
}
@ -1625,6 +1678,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 +1720,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);