UV: During packing, add progress bar and ability to cancel
Uses job system. Co-authored-by: Campbell Barton <campbell@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/107859
This commit is contained in:
@@ -435,7 +435,9 @@ class IMAGE_MT_uvs(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
layout.operator("uv.pack_islands")
|
||||
layout.operator_context = 'EXEC_REGION_WIN'
|
||||
layout.operator("uv.average_islands_scale")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_convexhull_2d.h"
|
||||
#include "BLI_linklist.h"
|
||||
@@ -59,6 +61,7 @@
|
||||
#include "ED_image.h"
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_undo.h"
|
||||
#include "ED_uvedit.h"
|
||||
#include "ED_view3d.h"
|
||||
|
||||
@@ -169,6 +172,14 @@ void blender::geometry::UVPackIsland_Params::setUDIMOffsetFromSpaceImage(const S
|
||||
}
|
||||
/** \} */
|
||||
|
||||
bool blender::geometry::UVPackIsland_Params::isCancelled() const
|
||||
{
|
||||
if (stop) {
|
||||
return *stop;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Parametrizer Conversion
|
||||
* \{ */
|
||||
@@ -1141,6 +1152,7 @@ static bool island_has_pins(const Scene *scene,
|
||||
* This is needed to perform UV packing on objects that aren't in edit-mode.
|
||||
* \param udim_source_closest: UDIM source SpaceImage.
|
||||
* \param original_selection: Pack to original selection.
|
||||
* \param notify_wm: Notify the WM of any changes. (UI thread only.)
|
||||
* \param params: Parameters and options to pass to the packing engine.
|
||||
*/
|
||||
static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
@@ -1149,6 +1161,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
BMesh **bmesh_override,
|
||||
const SpaceImage *udim_source_closest,
|
||||
const bool original_selection,
|
||||
const bool notify_wm,
|
||||
blender::geometry::UVPackIsland_Params *params)
|
||||
{
|
||||
blender::Vector<FaceIsland *> island_vector;
|
||||
@@ -1271,6 +1284,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
BLI_memarena_free(arena);
|
||||
|
||||
const float scale = pack_islands(pack_island_vector, *params);
|
||||
const bool is_cancelled = params->isCancelled();
|
||||
|
||||
float base_offset[2] = {0.0f, 0.0f};
|
||||
copy_v2_v2(base_offset, params->udim_base_offset);
|
||||
@@ -1309,7 +1323,10 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
float matrix[2][2];
|
||||
float matrix_inverse[2][2];
|
||||
float pre_translate[2];
|
||||
for (int64_t i : pack_island_vector.index_range()) {
|
||||
for (const int64_t i : pack_island_vector.index_range()) {
|
||||
if (is_cancelled) {
|
||||
continue;
|
||||
}
|
||||
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
||||
FaceIsland *island = island_vector[pack_island->caller_index];
|
||||
const float island_scale = pack_island->can_scale_(*params) ? scale : 1.0f;
|
||||
@@ -1339,16 +1356,21 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
pre_translate[1] = selection_min_co[1] / rescale;
|
||||
island_uv_transform(island, matrix, pre_translate);
|
||||
}
|
||||
}
|
||||
|
||||
for (const int64_t i : pack_island_vector.index_range()) {
|
||||
blender::geometry::PackIsland *pack_island = pack_island_vector[i];
|
||||
/* Cleanup memory. */
|
||||
pack_island_vector[i] = nullptr;
|
||||
delete pack_island;
|
||||
}
|
||||
|
||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||
Object *obedit = objects[ob_index];
|
||||
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
|
||||
if (notify_wm && !is_cancelled) {
|
||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||
Object *obedit = objects[ob_index];
|
||||
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
|
||||
}
|
||||
}
|
||||
|
||||
for (FaceIsland *island : island_vector) {
|
||||
@@ -1361,6 +1383,9 @@ static void uvedit_pack_islands_multi(const Scene *scene,
|
||||
/** \name Pack UV Islands Operator
|
||||
* \{ */
|
||||
|
||||
/* TODO: support this, interaction with the job-system needs to be handled carefully. */
|
||||
// #define USE_INTERACTIVE_PACK
|
||||
|
||||
/* Packing targets. */
|
||||
enum {
|
||||
PACK_UDIM_SRC_CLOSEST = 0,
|
||||
@@ -1368,6 +1393,70 @@ enum {
|
||||
PACK_ORIGINAL_AABB,
|
||||
};
|
||||
|
||||
struct UVPackIslandsData {
|
||||
wmWindowManager *wm;
|
||||
|
||||
const Scene *scene;
|
||||
|
||||
Object **objects;
|
||||
uint objects_len;
|
||||
const SpaceImage *sima;
|
||||
int udim_source;
|
||||
|
||||
bContext *undo_context;
|
||||
const char *undo_str;
|
||||
bool use_job;
|
||||
|
||||
blender::geometry::UVPackIsland_Params pack_island_params;
|
||||
};
|
||||
|
||||
static void pack_islands_startjob(void *pidv, bool *stop, bool *do_update, float *progress)
|
||||
{
|
||||
*progress = 0.02f;
|
||||
|
||||
UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(pidv);
|
||||
|
||||
pid->pack_island_params.stop = stop;
|
||||
pid->pack_island_params.do_update = do_update;
|
||||
pid->pack_island_params.progress = progress;
|
||||
|
||||
uvedit_pack_islands_multi(pid->scene,
|
||||
pid->objects,
|
||||
pid->objects_len,
|
||||
nullptr,
|
||||
(pid->udim_source == PACK_UDIM_SRC_CLOSEST) ? pid->sima : nullptr,
|
||||
(pid->udim_source == PACK_ORIGINAL_AABB),
|
||||
!pid->use_job,
|
||||
&pid->pack_island_params);
|
||||
|
||||
*progress = 0.99f;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
static void pack_islands_endjob(void *pidv)
|
||||
{
|
||||
UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(pidv);
|
||||
for (uint ob_index = 0; ob_index < pid->objects_len; ob_index++) {
|
||||
Object *obedit = pid->objects[ob_index];
|
||||
DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
|
||||
}
|
||||
WM_main_add_notifier(NC_SPACE | ND_SPACE_IMAGE, NULL);
|
||||
|
||||
if (pid->undo_str) {
|
||||
ED_undo_push(pid->undo_context, pid->undo_str);
|
||||
}
|
||||
}
|
||||
|
||||
static void pack_islands_freejob(void *pidv)
|
||||
{
|
||||
WM_cursor_wait(false);
|
||||
UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(pidv);
|
||||
MEM_freeN(pid->objects);
|
||||
WM_set_locked_interface(pid->wm, false);
|
||||
MEM_freeN(pid);
|
||||
}
|
||||
|
||||
static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
@@ -1400,7 +1489,23 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
|
||||
}
|
||||
|
||||
blender::geometry::UVPackIsland_Params pack_island_params;
|
||||
UVPackIslandsData *pid = static_cast<UVPackIslandsData *>(
|
||||
MEM_callocN(sizeof(UVPackIslandsData), "pack_islands_data"));
|
||||
pid->use_job = op->flag & OP_IS_INVOKE;
|
||||
pid->scene = scene;
|
||||
pid->objects = objects;
|
||||
pid->objects_len = objects_len;
|
||||
pid->sima = sima;
|
||||
pid->udim_source = udim_source;
|
||||
pid->wm = CTX_wm_manager(C);
|
||||
|
||||
blender::geometry::UVPackIsland_Params &pack_island_params = pid->pack_island_params;
|
||||
{
|
||||
/* Call default constructor and copy the defaults. */
|
||||
blender::geometry::UVPackIsland_Params default_params;
|
||||
pack_island_params = default_params;
|
||||
}
|
||||
|
||||
pack_island_params.setFromUnwrapOptions(options);
|
||||
pack_island_params.rotate = RNA_boolean_get(op->ptr, "rotate");
|
||||
pack_island_params.scale_to_fit = RNA_boolean_get(op->ptr, "scale");
|
||||
@@ -1416,15 +1521,31 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
|
||||
pack_island_params.setUDIMOffsetFromSpaceImage(sima);
|
||||
}
|
||||
|
||||
uvedit_pack_islands_multi(scene,
|
||||
objects,
|
||||
objects_len,
|
||||
nullptr,
|
||||
(udim_source == PACK_UDIM_SRC_CLOSEST) ? sima : nullptr,
|
||||
(udim_source == PACK_ORIGINAL_AABB),
|
||||
&pack_island_params);
|
||||
if (pid->use_job) {
|
||||
/* Setup job. */
|
||||
if (pid->wm->op_undo_depth == 0) {
|
||||
/* The job must do it's own undo push. */
|
||||
pid->undo_context = C;
|
||||
pid->undo_str = op->type->name;
|
||||
}
|
||||
|
||||
MEM_freeN(objects);
|
||||
wmJob *wm_job = WM_jobs_get(
|
||||
pid->wm, CTX_wm_window(C), scene, "Packing UVs", WM_JOB_PROGRESS, WM_JOB_TYPE_UV_PACK);
|
||||
WM_jobs_customdata_set(wm_job, pid, pack_islands_freejob);
|
||||
WM_jobs_timer(wm_job, 0.1, 0, 0);
|
||||
WM_set_locked_interface(pid->wm, true);
|
||||
WM_jobs_callbacks(wm_job, pack_islands_startjob, nullptr, nullptr, pack_islands_endjob);
|
||||
|
||||
WM_cursor_wait(true);
|
||||
G.is_break = false;
|
||||
WM_jobs_start(CTX_wm_manager(C), wm_job);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
pack_islands_startjob(pid, nullptr, nullptr, nullptr);
|
||||
pack_islands_endjob(pid);
|
||||
|
||||
MEM_freeN(pid);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
@@ -1487,10 +1608,21 @@ void UV_OT_pack_islands(wmOperatorType *ot)
|
||||
ot->description =
|
||||
"Transform all islands so that they fill up the UV/UDIM space as much as possible";
|
||||
|
||||
#ifdef USE_INTERACTIVE_PACK
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
#else
|
||||
/* The operator will handle undo, so the job system can push() it after the job completes. */
|
||||
ot->flag = OPTYPE_REGISTER;
|
||||
#endif
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = pack_islands_exec;
|
||||
|
||||
#ifdef USE_INTERACTIVE_PACK
|
||||
ot->invoke = WM_operator_props_popup_call;
|
||||
#else
|
||||
ot->invoke = WM_operator_props_popup_confirm;
|
||||
#endif
|
||||
ot->poll = ED_operator_uvedit;
|
||||
|
||||
/* properties */
|
||||
@@ -2234,7 +2366,7 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
|
||||
pack_island_params.margin = scene->toolsettings->uvcalc_margin;
|
||||
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects, objects_len, nullptr, nullptr, false, &pack_island_params);
|
||||
scene, objects, objects_len, nullptr, nullptr, false, true, &pack_island_params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2377,7 +2509,7 @@ static int unwrap_exec(bContext *C, wmOperator *op)
|
||||
pack_island_params.margin = RNA_float_get(op->ptr, "margin");
|
||||
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects, objects_len, nullptr, nullptr, false, &pack_island_params);
|
||||
scene, objects, objects_len, nullptr, nullptr, false, true, &pack_island_params);
|
||||
|
||||
MEM_freeN(objects);
|
||||
|
||||
@@ -2759,7 +2891,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
|
||||
params.margin = RNA_float_get(op->ptr, "island_margin");
|
||||
|
||||
uvedit_pack_islands_multi(
|
||||
scene, objects_changed, object_changed_len, nullptr, nullptr, false, ¶ms);
|
||||
scene, objects_changed, object_changed_len, nullptr, nullptr, false, true, ¶ms);
|
||||
|
||||
/* #uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
|
||||
const bool per_face_aspect = false;
|
||||
@@ -3747,7 +3879,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
|
||||
params.margin_method = ED_UVPACK_MARGIN_SCALED;
|
||||
params.margin = 0.001f;
|
||||
|
||||
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, false, ¶ms);
|
||||
uvedit_pack_islands_multi(scene, &ob, 1, &bm, nullptr, false, true, ¶ms);
|
||||
|
||||
/* Write back from BMesh to Mesh. */
|
||||
BMeshToMeshParams bm_to_me_params{};
|
||||
|
||||
@@ -55,6 +55,7 @@ class UVPackIsland_Params {
|
||||
|
||||
void setFromUnwrapOptions(const UnwrapOptions &options);
|
||||
void setUDIMOffsetFromSpaceImage(const SpaceImage *sima);
|
||||
bool isCancelled() const;
|
||||
|
||||
/** Islands can be rotated to improve packing. */
|
||||
bool rotate;
|
||||
@@ -84,6 +85,12 @@ class UVPackIsland_Params {
|
||||
float target_aspect_y;
|
||||
/** Which shape to use when packing. */
|
||||
eUVPackIsland_ShapeMethod shape_method;
|
||||
|
||||
/** Abandon packing early when set by the job system. */
|
||||
bool *stop;
|
||||
bool *do_update;
|
||||
/** How much progress we have made. From wmJob. */
|
||||
float *progress;
|
||||
};
|
||||
|
||||
class uv_phi;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "GEO_uv_pack.hh"
|
||||
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_boxpack_2d.h"
|
||||
@@ -338,6 +340,9 @@ UVPackIsland_Params::UVPackIsland_Params()
|
||||
udim_base_offset[1] = 0.0f;
|
||||
target_aspect_y = 1.0f;
|
||||
shape_method = ED_UVPACK_SHAPE_AABB;
|
||||
stop = nullptr;
|
||||
do_update = nullptr;
|
||||
progress = nullptr;
|
||||
}
|
||||
|
||||
/* Compact representation for AABB packers. */
|
||||
@@ -1200,6 +1205,13 @@ static int64_t pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
|
||||
while (i < island_indices.size()) {
|
||||
|
||||
if (params.stop && G.is_break) {
|
||||
*params.stop = true;
|
||||
}
|
||||
if (params.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (traced_islands < i) {
|
||||
/* Trace an island that's been solved. (Greedy.) */
|
||||
const int64_t island_index = island_indices[traced_islands]->index;
|
||||
@@ -1308,17 +1320,26 @@ static int64_t pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
|
||||
else {
|
||||
scan_line = std::max(0, scan_line - 25); /* `-25` must by odd. */
|
||||
}
|
||||
|
||||
if (params.progress) {
|
||||
/* We don't (yet) have a good model for how long the pack operation is going
|
||||
* to take, so just update the progress a little bit. */
|
||||
const float previous_progress = *params.progress;
|
||||
*params.do_update = true;
|
||||
const float reduction = island_indices.size() / (island_indices.size() + 0.5f);
|
||||
*params.progress = 1.0f - (1.0f - previous_progress) * reduction;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_larger(*r_extent, extent, params)) {
|
||||
return 0;
|
||||
}
|
||||
*r_extent = extent;
|
||||
for (const int64_t i : phis.index_range()) {
|
||||
const int64_t island_index = island_indices[i]->index;
|
||||
for (int64_t j = 0; j < i; j++) {
|
||||
const int64_t island_index = island_indices[j]->index;
|
||||
r_phis[island_index] = phis[island_index];
|
||||
}
|
||||
return phis.size();
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1434,7 +1455,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
||||
alpaca_cutoff = alpaca_cutoff_fast;
|
||||
}
|
||||
}
|
||||
const int64_t max_box_pack = std::min(alpaca_cutoff, islands.size());
|
||||
int64_t max_box_pack = std::min(alpaca_cutoff, islands.size());
|
||||
|
||||
rctf extent = {0.0f, 1e30f, 0.0f, 1e30f};
|
||||
|
||||
@@ -1460,13 +1481,13 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
|
||||
switch (params.shape_method) {
|
||||
case ED_UVPACK_SHAPE_CONVEX:
|
||||
case ED_UVPACK_SHAPE_CONCAVE:
|
||||
pack_island_xatlas(aabbs.as_span().take_front(max_box_pack),
|
||||
islands,
|
||||
scale,
|
||||
margin,
|
||||
params,
|
||||
r_phis,
|
||||
&extent);
|
||||
max_box_pack = pack_island_xatlas(aabbs.as_span().take_front(max_box_pack),
|
||||
islands,
|
||||
scale,
|
||||
margin,
|
||||
params,
|
||||
r_phis,
|
||||
&extent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -1518,6 +1518,7 @@ typedef enum eWM_JobType {
|
||||
WM_JOB_TYPE_SEQ_DRAG_DROP_PREVIEW,
|
||||
WM_JOB_TYPE_CALCULATE_SIMULATION_NODES,
|
||||
WM_JOB_TYPE_BAKE_SIMULATION_NODES,
|
||||
WM_JOB_TYPE_UV_PACK,
|
||||
/* add as needed, bake, seq proxy build
|
||||
* if having hard coded values is a problem */
|
||||
} eWM_JobType;
|
||||
|
||||
Reference in New Issue
Block a user