diff options
author | Jacques Lucke <mail@jlucke.com> | 2019-11-05 00:09:06 +0300 |
---|---|---|
committer | Jacques Lucke <mail@jlucke.com> | 2019-11-05 00:09:06 +0300 |
commit | a63b896055c96c422e8d9d547022188729922500 (patch) | |
tree | 3377edef62e92e5ef1fbb89253be63674f002531 /source | |
parent | cacbe466b267671c400b4310674d7190f68a68b7 (diff) | |
parent | ef7fd50f8a9317f363eaeb29101cd7fce1111ff4 (diff) |
Merge branch 'master' into functions
Diffstat (limited to 'source')
94 files changed, 5381 insertions, 1991 deletions
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 0eb8df1b19d..94c2a94d420 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -136,6 +136,7 @@ struct IDProperty *IDP_New(const char type, void IDP_FreePropertyContent_ex(struct IDProperty *prop, const bool do_id_user); void IDP_FreePropertyContent(struct IDProperty *prop); +void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user); void IDP_FreeProperty(struct IDProperty *prop); void IDP_ClearProperty(IDProperty *prop); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index a5b223a73f2..c1bb60737ff 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -289,7 +289,7 @@ bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, struct Sequence *seq, float cfra); -void BKE_sequencer_proxy_rebuild_context(struct Main *bmain, +bool BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Depsgraph *depsgraph, struct Scene *scene, struct Sequence *seq, diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index fe740f4898e..73b7b1e2858 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -84,6 +84,7 @@ static void brush_defaults(Brush *brush) FROM_DEFAULT(normal_weight); FROM_DEFAULT(fill_threshold); FROM_DEFAULT(flag); + FROM_DEFAULT(sampling_flag); FROM_DEFAULT_PTR(rgb); FROM_DEFAULT_PTR(secondary_rgb); FROM_DEFAULT(spacing); diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 016da52252a..1125047be32 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -1085,6 +1085,12 @@ void IDP_FreePropertyContent(IDProperty *prop) IDP_FreePropertyContent_ex(prop, true); } +void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user) +{ + IDP_FreePropertyContent_ex(prop, do_id_user); + MEM_freeN(prop); +} + void IDP_FreeProperty(IDProperty *prop) { IDP_FreePropertyContent(prop); diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index f12999b033d..34c1c5ecab4 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -239,7 +239,7 @@ void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user) BKE_freestyle_config_free(&view_layer->freestyle_config, do_id_user); if (view_layer->id_properties) { - IDP_FreeProperty(view_layer->id_properties); + IDP_FreeProperty_ex(view_layer->id_properties, do_id_user); } MEM_SAFE_FREE(view_layer->object_bases_array); diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 26dd9aab511..b99d6e1684a 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -2093,7 +2093,7 @@ static int seq_proxy_context_count(Sequence *seq, Scene *scene) return num_views; } -void BKE_sequencer_proxy_rebuild_context(Main *bmain, +bool BKE_sequencer_proxy_rebuild_context(Main *bmain, Depsgraph *depsgraph, Scene *scene, Sequence *seq, @@ -2107,11 +2107,11 @@ void BKE_sequencer_proxy_rebuild_context(Main *bmain, int i; if (!seq->strip || !seq->strip->proxy) { - return; + return true; } if (!(seq->flag & SEQ_USE_PROXY)) { - return; + return true; } num_files = seq_proxy_context_count(seq, scene); @@ -2138,9 +2138,6 @@ void BKE_sequencer_proxy_rebuild_context(Main *bmain, context->view_id = i; /* only for images */ - link = BLI_genericNodeN(context); - BLI_addtail(queue, link); - if (nseq->type == SEQ_TYPE_MOVIE) { StripAnim *sanim; @@ -2155,8 +2152,16 @@ void BKE_sequencer_proxy_rebuild_context(Main *bmain, context->overwrite, file_list); } + if (!context->index_context) { + MEM_freeN(context); + return false; + } } + + link = BLI_genericNodeN(context); + BLI_addtail(queue, link); } + return true; } void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 568d6c9a84a..7ef5e518cc8 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -198,11 +198,45 @@ void BLI_task_parallel_range(const int start, TaskParallelRangeFunc func, const TaskParallelSettings *settings); -typedef void (*TaskParallelListbaseFunc)(void *userdata, struct Link *iter, int index); +/* This data is shared between all tasks, its access needs thread lock or similar protection. */ +typedef struct TaskParallelIteratorStateShared { + /* Maximum amount of items to acquire at once. */ + int chunk_size; + /* Next item to be acquired. */ + void *next_item; + /* Index of the next item to be acquired. */ + int next_index; + /* Indicates that end of iteration has been reached. */ + bool is_finished; + /* Helper lock to protect access to this data in iterator getter callback, + * can be ignored (if the callback implements its own protection system, using atomics e.g.). + * Will be NULL when iterator is actually processed in a single thread. */ + SpinLock *spin_lock; +} TaskParallelIteratorStateShared; + +typedef void (*TaskParallelIteratorIterFunc)(void *__restrict userdata, + const TaskParallelTLS *__restrict tls, + void **r_next_item, + int *r_next_index, + bool *r_do_abort); + +typedef void (*TaskParallelIteratorFunc)(void *__restrict userdata, + void *item, + int index, + const TaskParallelTLS *__restrict tls); + +void BLI_task_parallel_iterator(void *userdata, + TaskParallelIteratorIterFunc iter_func, + void *init_item, + const int init_index, + const int tot_items, + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings); + void BLI_task_parallel_listbase(struct ListBase *listbase, void *userdata, - TaskParallelListbaseFunc func, - const bool use_threading); + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings); typedef struct MempoolIterData MempoolIterData; typedef void (*TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter); diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index 6cdaec97d9a..bb69dc6452f 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -149,7 +149,7 @@ typedef struct TaskThreadLocalStorage { * without "interrupting" for task execution. * * We try to accumulate as much tasks as possible in a local queue without - * any locks first, and then we push all of them into a schedulers queue + * any locks first, and then we push all of them into a scheduler's queue * from within a single mutex lock. */ bool do_delayed_push; @@ -1052,14 +1052,20 @@ typedef struct ParallelRangeState { int chunk_size; } ParallelRangeState; -BLI_INLINE void task_parallel_range_calc_chunk_size(const TaskParallelSettings *settings, - const int num_tasks, - ParallelRangeState *state) +BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings, + const int tot_items, + int num_tasks, + int *r_chunk_size) { - const int tot_items = state->stop - state->start; int chunk_size = 0; - if (settings->min_iter_per_thread > 0) { + if (!settings->use_threading) { + /* Some users of this helper will still need a valid chunk size in case processing is not + * threaded. We can use a bigger one than in default threaded case then. */ + chunk_size = 1024; + num_tasks = 1; + } + else if (settings->min_iter_per_thread > 0) { /* Already set by user, no need to do anything here. */ chunk_size = settings->min_iter_per_thread; } @@ -1091,16 +1097,30 @@ BLI_INLINE void task_parallel_range_calc_chunk_size(const TaskParallelSettings * BLI_assert(chunk_size > 0); - switch (settings->scheduling_mode) { - case TASK_SCHEDULING_STATIC: - state->chunk_size = max_ii(chunk_size, tot_items / (num_tasks)); - break; - case TASK_SCHEDULING_DYNAMIC: - state->chunk_size = chunk_size; - break; + if (tot_items > 0) { + switch (settings->scheduling_mode) { + case TASK_SCHEDULING_STATIC: + *r_chunk_size = max_ii(chunk_size, tot_items / num_tasks); + break; + case TASK_SCHEDULING_DYNAMIC: + *r_chunk_size = chunk_size; + break; + } + } + else { + /* If total amount of items is unknown, we can only use dynamic scheduling. */ + *r_chunk_size = chunk_size; } } +BLI_INLINE void task_parallel_range_calc_chunk_size(const TaskParallelSettings *settings, + const int num_tasks, + ParallelRangeState *state) +{ + task_parallel_calc_chunk_size( + settings, state->stop - state->start, num_tasks, &state->chunk_size); +} + BLI_INLINE bool parallel_range_next_iter_get(ParallelRangeState *__restrict state, int *__restrict iter, int *__restrict count) @@ -1256,77 +1276,239 @@ void BLI_task_parallel_range(const int start, } } -#undef MALLOCA -#undef MALLOCA_FREE - -typedef struct ParallelListbaseState { +typedef struct TaskParallelIteratorState { void *userdata; - TaskParallelListbaseFunc func; + TaskParallelIteratorIterFunc iter_func; + TaskParallelIteratorFunc func; + + /* *** Data used to 'acquire' chunks of items from the iterator. *** */ + /* Common data also passed to the generator callback. */ + TaskParallelIteratorStateShared iter_shared; + /* Total number of items. If unknown, set it to a negative number. */ + int tot_items; +} TaskParallelIteratorState; + +BLI_INLINE void task_parallel_iterator_calc_chunk_size(const TaskParallelSettings *settings, + const int num_tasks, + TaskParallelIteratorState *state) +{ + task_parallel_calc_chunk_size( + settings, state->tot_items, num_tasks, &state->iter_shared.chunk_size); +} - int chunk_size; - int index; - Link *link; - SpinLock lock; -} ParallelListState; - -BLI_INLINE Link *parallel_listbase_next_iter_get(ParallelListState *__restrict state, - int *__restrict index, - int *__restrict count) +static void parallel_iterator_func_do(TaskParallelIteratorState *__restrict state, + void *userdata_chunk, + int threadid) { - int task_count = 0; - BLI_spin_lock(&state->lock); - Link *result = state->link; - if (LIKELY(result != NULL)) { - *index = state->index; - while (state->link != NULL && task_count < state->chunk_size) { - task_count++; - state->link = state->link->next; + TaskParallelTLS tls = { + .thread_id = threadid, + .userdata_chunk = userdata_chunk, + }; + + void **current_chunk_items; + int *current_chunk_indices; + int current_chunk_size; + + const size_t items_size = sizeof(*current_chunk_items) * (size_t)state->iter_shared.chunk_size; + const size_t indices_size = sizeof(*current_chunk_indices) * + (size_t)state->iter_shared.chunk_size; + + current_chunk_items = MALLOCA(items_size); + current_chunk_indices = MALLOCA(indices_size); + current_chunk_size = 0; + + for (bool do_abort = false; !do_abort;) { + if (state->iter_shared.spin_lock != NULL) { + BLI_spin_lock(state->iter_shared.spin_lock); + } + + /* Get current status. */ + int index = state->iter_shared.next_index; + void *item = state->iter_shared.next_item; + int i; + + /* 'Acquire' a chunk of items from the iterator function. */ + for (i = 0; i < state->iter_shared.chunk_size && !state->iter_shared.is_finished; i++) { + current_chunk_indices[i] = index; + current_chunk_items[i] = item; + state->iter_func(state->userdata, &tls, &item, &index, &state->iter_shared.is_finished); + } + + /* Update current status. */ + state->iter_shared.next_index = index; + state->iter_shared.next_item = item; + current_chunk_size = i; + + do_abort = state->iter_shared.is_finished; + + if (state->iter_shared.spin_lock != NULL) { + BLI_spin_unlock(state->iter_shared.spin_lock); + } + + for (i = 0; i < current_chunk_size; ++i) { + state->func(state->userdata, current_chunk_items[i], current_chunk_indices[i], &tls); } - state->index += task_count; } - BLI_spin_unlock(&state->lock); - *count = task_count; - return result; + + MALLOCA_FREE(current_chunk_items, items_size); + MALLOCA_FREE(current_chunk_indices, indices_size); } -static void parallel_listbase_func(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void parallel_iterator_func(TaskPool *__restrict pool, void *userdata_chunk, int threadid) { - ParallelListState *__restrict state = BLI_task_pool_userdata(pool); - Link *link; - int index, count; + TaskParallelIteratorState *__restrict state = BLI_task_pool_userdata(pool); - while ((link = parallel_listbase_next_iter_get(state, &index, &count)) != NULL) { - for (int i = 0; i < count; i++) { - state->func(state->userdata, link, index + i); - link = link->next; + parallel_iterator_func_do(state, userdata_chunk, threadid); +} + +static void task_parallel_iterator_no_threads(const TaskParallelSettings *settings, + TaskParallelIteratorState *state) +{ + /* Prepare user's TLS data. */ + void *userdata_chunk = settings->userdata_chunk; + const size_t userdata_chunk_size = settings->userdata_chunk_size; + void *userdata_chunk_local = NULL; + const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); + if (use_userdata_chunk) { + userdata_chunk_local = MALLOCA(userdata_chunk_size); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + + /* Also marking it as non-threaded for the iterator callback. */ + state->iter_shared.spin_lock = NULL; + + parallel_iterator_func_do(state, userdata_chunk, 0); + + if (use_userdata_chunk) { + if (settings->func_finalize != NULL) { + settings->func_finalize(state->userdata, userdata_chunk_local); } + MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size); } } -static void task_parallel_listbase_no_threads(struct ListBase *listbase, - void *userdata, - TaskParallelListbaseFunc func) +static void task_parallel_iterator_do(const TaskParallelSettings *settings, + TaskParallelIteratorState *state) { - int i = 0; - for (Link *link = listbase->first; link != NULL; link = link->next, i++) { - func(userdata, link, i); + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + task_parallel_iterator_calc_chunk_size(settings, num_threads, state); + + if (!settings->use_threading) { + task_parallel_iterator_no_threads(settings, state); + return; } + + const int chunk_size = state->iter_shared.chunk_size; + const int tot_items = state->tot_items; + const size_t num_tasks = tot_items >= 0 ? + (size_t)min_ii(num_threads, state->tot_items / chunk_size) : + (size_t)num_threads; + + BLI_assert(num_tasks > 0); + if (num_tasks == 1) { + task_parallel_iterator_no_threads(settings, state); + return; + } + + SpinLock spin_lock; + BLI_spin_init(&spin_lock); + state->iter_shared.spin_lock = &spin_lock; + + void *userdata_chunk = settings->userdata_chunk; + const size_t userdata_chunk_size = settings->userdata_chunk_size; + void *userdata_chunk_local = NULL; + void *userdata_chunk_array = NULL; + const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); + + TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state); + + if (use_userdata_chunk) { + userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); + } + + for (size_t i = 0; i < num_tasks; i++) { + if (use_userdata_chunk) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread(task_pool, + parallel_iterator_func, + userdata_chunk_local, + false, + TASK_PRIORITY_HIGH, + task_pool->thread_id); + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + if (use_userdata_chunk) { + if (settings->func_finalize != NULL) { + for (size_t i = 0; i < num_tasks; i++) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + settings->func_finalize(state->userdata, userdata_chunk_local); + } + } + MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks); + } + + BLI_spin_end(&spin_lock); + state->iter_shared.spin_lock = NULL; } -/* NOTE: The idea here is to compensate for rather measurable threading - * overhead caused by fetching tasks. With too many CPU threads we are starting - * to spend too much time in those overheads. */ -BLI_INLINE int task_parallel_listbasecalc_chunk_size(const int num_threads) +/** + * This function allows to parallelize for loops using a generic iterator. + * + * \param userdata: Common userdata passed to all instances of \a func. + * \param iter_func: Callback function used to generate chunks of items. + * \param init_item: The initial item, if necessary (may be NULL if unused). + * \param init_index: The initial index. + * \param tot_items: The total amount of items to iterate over + * (if unkown, set it to a negative number). + * \param func: Callback function. + * \param settings: See public API doc of TaskParallelSettings for description of all settings. + * + * \note Static scheduling is only available when \a tot_items is >= 0. + */ + +void BLI_task_parallel_iterator(void *userdata, + TaskParallelIteratorIterFunc iter_func, + void *init_item, + const int init_index, + const int tot_items, + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings) { - if (num_threads > 32) { - return 128; - } - else if (num_threads > 16) { - return 64; + TaskParallelIteratorState state = {0}; + + state.tot_items = tot_items; + state.iter_shared.next_index = init_index; + state.iter_shared.next_item = init_item; + state.iter_shared.is_finished = false; + state.userdata = userdata; + state.iter_func = iter_func; + state.func = func; + + task_parallel_iterator_do(settings, &state); +} + +static void task_parallel_listbase_get(void *__restrict UNUSED(userdata), + const TaskParallelTLS *__restrict UNUSED(tls), + void **r_next_item, + int *r_next_index, + bool *r_do_abort) +{ + /* Get current status. */ + Link *link = *r_next_item; + + if (link->next == NULL) { + *r_do_abort = true; } - return 32; + *r_next_item = link->next; + (*r_next_index)++; } /** @@ -1335,58 +1517,36 @@ BLI_INLINE int task_parallel_listbasecalc_chunk_size(const int num_threads) * \param listbase: The double linked list to loop over. * \param userdata: Common userdata passed to all instances of \a func. * \param func: Callback function. - * \param use_threading: If \a true, actually split-execute loop in threads, - * else just do a sequential forloop - * (allows caller to use any kind of test to switch on parallelization or not). + * \param settings: See public API doc of ParallelRangeSettings for description of all settings. * * \note There is no static scheduling here, * since it would need another full loop over items to count them. */ -void BLI_task_parallel_listbase(struct ListBase *listbase, +void BLI_task_parallel_listbase(ListBase *listbase, void *userdata, - TaskParallelListbaseFunc func, - const bool use_threading) + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings) { if (BLI_listbase_is_empty(listbase)) { return; } - if (!use_threading) { - task_parallel_listbase_no_threads(listbase, userdata, func); - return; - } - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); - /* TODO(sergey): Consider making chunk size configurable. */ - const int chunk_size = task_parallel_listbasecalc_chunk_size(num_threads); - const int num_tasks = min_ii(num_threads, BLI_listbase_count(listbase) / chunk_size); - if (num_tasks <= 1) { - task_parallel_listbase_no_threads(listbase, userdata, func); - return; - } - ParallelListState state; - TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state); + TaskParallelIteratorState state = {0}; - state.index = 0; - state.link = listbase->first; + state.tot_items = BLI_listbase_count(listbase); + state.iter_shared.next_index = 0; + state.iter_shared.next_item = listbase->first; + state.iter_shared.is_finished = false; state.userdata = userdata; + state.iter_func = task_parallel_listbase_get; state.func = func; - state.chunk_size = chunk_size; - BLI_spin_init(&state.lock); - BLI_assert(num_tasks > 0); - for (int i = 0; i < num_tasks; i++) { - /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread( - task_pool, parallel_listbase_func, NULL, false, TASK_PRIORITY_HIGH, task_pool->thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - BLI_spin_end(&state.lock); + task_parallel_iterator_do(settings, &state); } +#undef MALLOCA +#undef MALLOCA_FREE + typedef struct ParallelMempoolState { void *userdata; TaskParallelMempoolFunc func; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 9e0d3b7a419..50363e3f42a 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3932,5 +3932,10 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* Versioning code until next subversion bump goes here. */ + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + sa->flag &= ~AREA_FLAG_UNUSED_6; + } + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index c9fb8b6990b..9fce89558b6 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -435,6 +435,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) * Note that sculpt is an exception, * it's values are overwritten by #BKE_brush_sculpt_reset below. */ brush->alpha = 1.0; + + /* Enable antialiasing by default */ + brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING; } { diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 53a6b8b08cd..e8783f68bcb 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -160,6 +160,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) * Include next version bump. */ { + FROM_DEFAULT_V4_UCHAR(space_sequencer.anim_preview_range); } #undef FROM_DEFAULT_V4_UCHAR diff --git a/source/blender/compositor/operations/COM_OpenCLKernels.cl b/source/blender/compositor/operations/COM_OpenCLKernels.cl index 4dfca1777f1..ebe8a6d08ec 100644 --- a/source/blender/compositor/operations/COM_OpenCLKernels.cl +++ b/source/blender/compositor/operations/COM_OpenCLKernels.cl @@ -26,22 +26,22 @@ const sampler_t SAMPLER_NEAREST = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST; const sampler_t SAMPLER_NEAREST_CLAMP = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST; -__constant const int zero = {0,0}; +__constant const int2 zero = {0,0}; // KERNEL --- BOKEH BLUR --- __kernel void bokehBlurKernel(__read_only image2d_t boundingBox, __read_only image2d_t inputImage, __read_only image2d_t bokehImage, __write_only image2d_t output, - int offsetInput, int offsetOutput, int radius, int step, int dimension, int offset) + int2 offsetInput, int2 offsetOutput, int radius, int step, int2 dimension, int2 offset) { - int coords = {get_global_id(0), get_global_id(1)}; + int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; float tempBoundingBox; - float color = {0.0f,0.0f,0.0f,0.0f}; - float multiplyer = {0.0f,0.0f,0.0f,0.0f}; - float bokeh; + float4 color = {0.0f,0.0f,0.0f,0.0f}; + float4 multiplyer = {0.0f,0.0f,0.0f,0.0f}; + float4 bokeh; const float radius2 = radius*2.0f; - const int realCoordinate = coords + offsetOutput; - int imageCoordinates = realCoordinate - offsetInput; + const int2 realCoordinate = coords + offsetOutput; + int2 imageCoordinates = realCoordinate - offsetInput; tempBoundingBox = read_imagef(boundingBox, SAMPLER_NEAREST, coords).s0; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 1ea3fc13acd..2669bce52bc 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -122,31 +122,72 @@ namespace DEG { /* ***************** */ /* Relations Builder */ +namespace { + /* TODO(sergey): This is somewhat weak, but we don't want neither false-positive - * time dependencies nor special exceptions in the depsgraph evaluation. - */ -static bool python_driver_depends_on_time(ChannelDriver *driver) + * time dependencies nor special exceptions in the depsgraph evaluation. */ + +bool python_driver_exression_depends_on_time(const char *expression) { - if (driver->expression[0] == '\0') { + if (expression[0] == '\0') { /* Empty expression depends on nothing. */ return false; } - if (strchr(driver->expression, '(') != NULL) { + if (strchr(expression, '(') != NULL) { /* Function calls are considered dependent on a time. */ return true; } - if (strstr(driver->expression, "frame") != NULL) { + if (strstr(expression, "frame") != NULL) { /* Variable `frame` depends on time. */ - /* TODO(sergey): This is a bit weak, but not sure about better way of - * handling this. */ + /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */ + return true; + } + /* Possible indirect time relation s should be handled via variable targets. */ + return false; +} + +bool driver_target_depends_on_time(const DriverTarget *target) +{ + if (target->idtype == ID_SCE && STREQ(target->rna_path, "frame_current")) { return true; } - /* Possible indirect time relation s should be handled via variable - * targets. */ return false; } -static bool particle_system_depends_on_time(ParticleSystem *psys) +bool driver_variable_depends_on_time(const DriverVar *variable) +{ + for (int i = 0; i < variable->num_targets; ++i) { + if (driver_target_depends_on_time(&variable->targets[i])) { + return true; + } + } + return false; +} + +bool driver_variables_depends_on_time(const ListBase *variables) +{ + LISTBASE_FOREACH (const DriverVar *, variable, variables) { + if (driver_variable_depends_on_time(variable)) { + return true; + } + } + return false; +} + +bool driver_depends_on_time(ChannelDriver *driver) +{ + if (driver->type == DRIVER_TYPE_PYTHON) { + if (python_driver_exression_depends_on_time(driver->expression)) { + return true; + } + } + if (driver_variables_depends_on_time(&driver->variables)) { + return true; + } + return false; +} + +bool particle_system_depends_on_time(ParticleSystem *psys) { ParticleSettings *part = psys->part; /* Non-hair particles we always consider dependent on time. */ @@ -161,7 +202,7 @@ static bool particle_system_depends_on_time(ParticleSystem *psys) return false; } -static bool object_particles_depends_on_time(Object *object) +bool object_particles_depends_on_time(Object *object) { if (object->type != OB_MESH) { return false; @@ -174,7 +215,7 @@ static bool object_particles_depends_on_time(Object *object) return false; } -static bool check_id_has_anim_component(ID *id) +bool check_id_has_anim_component(ID *id) { AnimData *adt = BKE_animdata_from_id(id); if (adt == NULL) { @@ -183,11 +224,11 @@ static bool check_id_has_anim_component(ID *id) return (adt->action != NULL) || (!BLI_listbase_is_empty(&adt->nla_tracks)); } -static OperationCode bone_target_opcode(ID *target, - const char *subtarget, - ID *id, - const char *component_subdata, - RootPChanMap *root_map) +OperationCode bone_target_opcode(ID *target, + const char *subtarget, + ID *id, + const char *component_subdata, + RootPChanMap *root_map) { /* Same armature. */ if (target == id) { @@ -202,11 +243,13 @@ static OperationCode bone_target_opcode(ID *target, return OperationCode::BONE_DONE; } -static bool object_have_geometry_component(const Object *object) +bool object_have_geometry_component(const Object *object) { return ELEM(object->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL); } +} // namespace + /* **** General purpose functions **** */ DepsgraphRelationBuilder::DepsgraphRelationBuilder(Main *bmain, @@ -1382,7 +1425,7 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) /* It's quite tricky to detect if the driver actually depends on time or * not, so for now we'll be quite conservative here about optimization and * consider all python drivers to be depending on time. */ - if ((driver->type == DRIVER_TYPE_PYTHON) && python_driver_depends_on_time(driver)) { + if (driver_depends_on_time(driver)) { TimeSourceKey time_src_key; add_relation(time_src_key, driver_key, "TimeSrc -> Driver"); } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 701d73461fc..d0fe7c6637e 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -68,8 +68,6 @@ static struct { uint sss_count; - float alpha_hash_offset; - float alpha_hash_scale; float noise_offsets[3]; } e_data = {NULL}; /* Engine data */ @@ -311,6 +309,9 @@ static char *eevee_get_defines(int options) if ((options & VAR_MAT_LOOKDEV) != 0) { BLI_dynstr_append(ds, "#define LOOKDEV\n"); } + if ((options & VAR_MAT_HOLDOUT) != 0) { + BLI_dynstr_append(ds, "#define HOLDOUT\n"); + } str = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -609,14 +610,14 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, } if (!DRW_state_is_image_render() && ((stl->effects->enabled_effects & EFFECT_TAA) == 0)) { - e_data.alpha_hash_offset = 0.0f; - e_data.alpha_hash_scale = 1.0f; + sldata->common_data.alpha_hash_offset = 0.0f; + sldata->common_data.alpha_hash_scale = 1.0f; } else { double r; BLI_halton_1d(5, 0.0, stl->effects->taa_current_sample - 1, &r); - e_data.alpha_hash_offset = (float)r; - e_data.alpha_hash_scale = 0.01f; + sldata->common_data.alpha_hash_offset = (float)r; + sldata->common_data.alpha_hash_scale = 0.01f; } { @@ -865,6 +866,7 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(EEVEE_ViewLayerDa ParticleSystem *psys, ModifierData *md, bool is_hair, + bool holdout, bool use_ssr) { static int ssr_id; @@ -876,6 +878,7 @@ static struct DRWShadingGroup *EEVEE_default_shading_group_get(EEVEE_ViewLayerDa BLI_assert(!is_hair || (ob && psys && md)); SET_FLAG_FROM_TEST(options, is_hair, VAR_MAT_HAIR); + SET_FLAG_FROM_TEST(options, holdout, VAR_MAT_HOLDOUT); if (e_data.default_lit[options] == NULL) { create_default_shader(options); @@ -1116,7 +1119,8 @@ static void material_opaque(Material *ma, struct GPUMaterial **gpumat_depth, struct DRWShadingGroup **shgrp, struct DRWShadingGroup **shgrp_depth, - struct DRWShadingGroup **shgrp_depth_clip) + struct DRWShadingGroup **shgrp_depth_clip, + bool holdout) { EEVEE_EffectsInfo *effects = vedata->stl->effects; const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -1131,7 +1135,7 @@ static void material_opaque(Material *ma, float *rough_p = &ma->roughness; const bool do_cull = (ma->blend_flag & MA_BL_CULL_BACKFACE) != 0; - const bool use_gpumat = (ma->use_nodes && ma->nodetree); + const bool use_gpumat = (ma->use_nodes && ma->nodetree && !holdout); const bool use_ssrefract = ((ma->blend_flag & MA_BL_SS_REFRACTION) != 0) && ((effects->enabled_effects & EFFECT_REFRACT) != 0); const bool use_translucency = ((ma->blend_flag & MA_BL_TRANSLUCENCY) != 0); @@ -1218,14 +1222,6 @@ static void material_opaque(Material *ma, DRW_shgroup_uniform_float(*shgrp_depth, "alphaThreshold", &ma->alpha_threshold, 1); DRW_shgroup_uniform_float(*shgrp_depth_clip, "alphaThreshold", &ma->alpha_threshold, 1); } - else if (ma->blend_method == MA_BM_HASHED) { - DRW_shgroup_uniform_float(*shgrp_depth, "hashAlphaOffset", &e_data.alpha_hash_offset, 1); - DRW_shgroup_uniform_float( - *shgrp_depth_clip, "hashAlphaOffset", &e_data.alpha_hash_offset, 1); - DRW_shgroup_uniform_float_copy(*shgrp_depth, "hashAlphaScale", e_data.alpha_hash_scale); - DRW_shgroup_uniform_float_copy( - *shgrp_depth_clip, "hashAlphaScale", e_data.alpha_hash_scale); - } } } @@ -1301,7 +1297,8 @@ static void material_opaque(Material *ma, /* Fallback to default shader */ if (*shgrp == NULL) { bool use_ssr = ((effects->enabled_effects & EFFECT_SSR) != 0); - *shgrp = EEVEE_default_shading_group_get(sldata, vedata, NULL, NULL, NULL, false, use_ssr); + *shgrp = EEVEE_default_shading_group_get( + sldata, vedata, NULL, NULL, NULL, false, holdout, use_ssr); DRW_shgroup_uniform_vec3(*shgrp, "basecol", color_p, 1); DRW_shgroup_uniform_float(*shgrp, "metallic", metal_p, 1); DRW_shgroup_uniform_float(*shgrp, "specular", spec_p, 1); @@ -1453,6 +1450,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; GHash *material_hash = stl->g_data->material_hash; + const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0; bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && !DRW_state_is_image_render(); @@ -1479,6 +1477,20 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, shgrp_depth_array[i] = NULL; shgrp_depth_clip_array[i] = NULL; + if (holdout) { + material_opaque(ma_array[i], + material_hash, + sldata, + vedata, + &gpumat_array[i], + &gpumat_depth_array[i], + &shgrp_array[i], + &shgrp_depth_array[i], + &shgrp_depth_clip_array[i], + true); + continue; + } + switch (ma_array[i]->blend_method) { case MA_BM_SOLID: case MA_BM_CLIP: @@ -1491,7 +1503,8 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata, &gpumat_depth_array[i], &shgrp_array[i], &shgrp_depth_array[i], - &shgrp_depth_clip_array[i]); + &shgrp_depth_clip_array[i], + false); break; case MA_BM_BLEND: material_transparent(ma_array[i], @@ -1632,6 +1645,7 @@ void EEVEE_hair_cache_populate(EEVEE_Data *vedata, Scene *scene = draw_ctx->scene; bool use_ssr = ((stl->effects->enabled_effects & EFFECT_SSR) != 0); + const bool holdout = (ob->base_flag & BASE_HOLDOUT) != 0; if (ob->type == OB_MESH) { if (ob != draw_ctx->object_edit) { @@ -1664,7 +1678,8 @@ void EEVEE_hair_cache_populate(EEVEE_Data *vedata, ob, psys, md, psl->depth_pass_clip, e_data.default_hair_prepass_clip_sh); shgrp = NULL; - if (ma->use_nodes && ma->nodetree) { + + if (ma->use_nodes && ma->nodetree && !holdout) { static int ssr_id; ssr_id = (use_ssr) ? 1 : -1; static float half = 0.5f; @@ -1715,7 +1730,8 @@ void EEVEE_hair_cache_populate(EEVEE_Data *vedata, /* Fallback to default shader */ if (shgrp == NULL) { - shgrp = EEVEE_default_shading_group_get(sldata, vedata, ob, psys, md, true, use_ssr); + shgrp = EEVEE_default_shading_group_get( + sldata, vedata, ob, psys, md, true, holdout, use_ssr); DRW_shgroup_uniform_vec3(shgrp, "basecol", color_p, 1); DRW_shgroup_uniform_float(shgrp, "metallic", metal_p, 1); DRW_shgroup_uniform_float(shgrp, "specular", spec_p, 1); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index f4f40d40de6..7d02aacfac7 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -157,10 +157,11 @@ enum { VAR_MAT_BLEND = (1 << 3), VAR_MAT_VOLUME = (1 << 4), VAR_MAT_LOOKDEV = (1 << 5), + VAR_MAT_HOLDOUT = (1 << 6), /* Max number of variation */ /* IMPORTANT : Leave it last and set * it's value accordingly. */ - VAR_MAT_MAX = (1 << 6), + VAR_MAT_MAX = (1 << 7), /* These are options that are not counted in VAR_MAT_MAX * because they are not cumulative with the others above. */ VAR_MAT_CLIP = (1 << 9), @@ -665,9 +666,13 @@ typedef struct EEVEE_CommonUniformBuffer { float prb_lod_cube_max; /* float */ float prb_lod_planar_max; /* float */ /* Misc */ - int hiz_mip_offset; /* int */ - int ray_type; /* int */ - float ray_depth; /* float */ + int hiz_mip_offset; /* int */ + int ray_type; /* int */ + float ray_depth; /* float */ + float alpha_hash_offset; /* float */ + float alpha_hash_scale; /* float */ + float pad7; /* float */ + float pad8; /* float */ } EEVEE_CommonUniformBuffer; BLI_STATIC_ASSERT_ALIGN(EEVEE_CommonUniformBuffer, 16) diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index 46fc6e07c1c..1776f535237 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -365,7 +365,7 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView /* Precompute all shadow/view test before rendering and trashing the culling cache. */ BLI_bitmap *cube_visible = BLI_BITMAP_NEW_ALLOCA(MAX_SHADOW_CUBE); - bool any_visible = false; + bool any_visible = linfo->cascade_len > 0; for (int cube = 0; cube < linfo->cube_len; cube++) { if (DRW_culling_sphere_test(view, linfo->shadow_bounds + cube)) { BLI_BITMAP_ENABLE(cube_visible, cube); @@ -373,7 +373,7 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView } } - if (!any_visible && linfo->cascade_len == 0) { + if (any_visible) { sldata->common_data.ray_type = EEVEE_RAY_SHADOW; DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); } @@ -400,7 +400,7 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */ - if (!any_visible && linfo->cascade_len == 0) { + if (any_visible) { sldata->common_data.ray_type = saved_ray_type; DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); } diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index aaa351a1922..67fd441a0b1 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -41,6 +41,7 @@ #include "eevee_private.h" #include "GPU_draw.h" +#include "GPU_extensions.h" #include "GPU_texture.h" #include "GPU_material.h" @@ -81,7 +82,9 @@ extern char datatoc_volumetric_integration_frag_glsl[]; extern char datatoc_volumetric_lib_glsl[]; extern char datatoc_common_fullscreen_vert_glsl[]; -#define USE_VOLUME_OPTI (GLEW_ARB_shader_image_load_store && GLEW_ARB_shading_language_420pack) +#define USE_VOLUME_OPTI \ + (GLEW_ARB_shader_image_load_store && GLEW_ARB_shading_language_420pack && \ + !GPU_crappy_amd_driver()) static void eevee_create_shader_volumes(void) { diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index f10d98e68bd..b9a971570df 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -966,7 +966,6 @@ void main() # ifndef USE_ALPHA_BLEND float alpha_div = 1.0 / max(1e-8, alpha); outRadiance.rgb *= alpha_div; - outRadiance.a = 1.0; ssrData.rgb *= alpha_div; # ifdef USE_SSS sssAlbedo.rgb *= alpha_div; diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl index c7ae2417904..7f255b0859d 100644 --- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl @@ -43,6 +43,10 @@ layout(std140) uniform common_block int hizMipOffset; int rayType; float rayDepth; + float alphaHashOffset; + float alphaHashScale; + float pad7; + float pad8; }; /* rayType (keep in sync with ray_type) */ diff --git a/source/blender/draw/engines/eevee/shaders/default_frag.glsl b/source/blender/draw/engines/eevee/shaders/default_frag.glsl index ca06d458f6e..a031ed193b6 100644 --- a/source/blender/draw/engines/eevee/shaders/default_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/default_frag.glsl @@ -41,5 +41,10 @@ Closure nodetree_exec(void) gl_FragDepth = 0.0; #endif +#ifdef HOLDOUT + cl = CLOSURE_DEFAULT; + cl.holdout = 1.0; +#endif + return cl; } diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl index dea6bc020ec..b49dbfceba2 100644 --- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl @@ -12,14 +12,11 @@ float hash3d(vec3 a) return hash(vec2(hash(a.xy), a.z)); } -uniform float hashAlphaOffset; -uniform float hashAlphaScale = 1.0; /* Roughly in pixel */ - float hashed_alpha_threshold(vec3 co) { /* Find the discretized derivatives of our coordinates. */ float max_deriv = max(length(dFdx(co)), length(dFdy(co))); - float pix_scale = 1.0 / (hashAlphaScale * max_deriv); + float pix_scale = 1.0 / (alphaHashScale * max_deriv); /* Find two nearest log-discretized noise scales. */ float pix_scale_log = log2(pix_scale); @@ -52,7 +49,7 @@ float hashed_alpha_threshold(vec3 co) threshold = clamp(threshold, 1.0e-6, 1.0); /* Jitter the threshold for TAA accumulation. */ - return fract(threshold + hashAlphaOffset); + return fract(threshold + alphaHashOffset); } #endif diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c index af315597e68..efc73fd1056 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_utils.c @@ -1128,11 +1128,12 @@ static void gpencil_add_editpoints_vertexdata(GpencilBatchCache *cache, */ const bool show_points = (show_sculpt_points) || (is_weight_paint) || (GPENCIL_EDIT_MODE(gpd) && - (ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE)); + ((ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE) || + (gps->totpoints == 1))); if (cache->is_dirty) { if ((obact == ob) && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && - (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES)) { + (v3d->gp_flag & V3D_GP_SHOW_EDIT_LINES) && (gps->totpoints > 1)) { /* line of the original stroke */ gpencil_get_edlin_geom(&cache->b_edlin, gps, edit_alpha, hide_select); @@ -1259,9 +1260,11 @@ static void gpencil_draw_strokes(GpencilBatchCache *cache, gpencil_add_fill_vertexdata( cache, ob, gpl, gpf, gps, opacity, tintcolor, false, custonion); } - /* stroke */ - /* No fill strokes, must show stroke always */ - if (((gp_style->flag & GP_STYLE_STROKE_SHOW) || (gps->flag & GP_STROKE_NOFILL)) && + /* stroke + * No fill strokes, must show stroke always or if the total points is lower than 3, + * because the stroke cannot be filled and it would be invisible. */ + if (((gp_style->flag & GP_STYLE_STROKE_SHOW) || (gps->flag & GP_STROKE_NOFILL) || + (gps->totpoints < 3)) && ((gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gpl->blend_mode == eGplBlendMode_Regular))) { /* recalc strokes uv (geometry can be changed by modifiers) */ diff --git a/source/blender/draw/intern/draw_armature.c b/source/blender/draw/intern/draw_armature.c index 5cd6a4a1286..e16dfb5004b 100644 --- a/source/blender/draw/intern/draw_armature.c +++ b/source/blender/draw/intern/draw_armature.c @@ -1350,7 +1350,8 @@ static void draw_points(const EditBone *eBone, bone_hint_color_shade(col_hint_tail, (g_theme.const_color) ? col_solid_tail : col_wire_tail); /* Draw root point if we are not connected to our parent */ - if ((BONE_FLAG(eBone, pchan) & BONE_CONNECTED) == 0) { + if (!(eBone ? (eBone->parent && (eBone->flag & BONE_CONNECTED)) : + (pchan->bone->parent && (pchan->bone->flag & BONE_CONNECTED)))) { if (select_id != -1) { DRW_select_load_id(select_id | BONESEL_ROOT); } @@ -1518,29 +1519,32 @@ static void draw_bone_line(EditBone *eBone, const float *col_head = no_display; const float *col_tail = col_bone; - if (eBone) { - if (eBone->flag & BONE_TIPSEL) { - col_tail = g_theme.vertex_select_color; - } - if (boneflag & BONE_SELECTED) { - col_bone = g_theme.edge_select_color; - } - col_wire = g_theme.wire_color; + if (g_theme.const_color != NULL) { + col_wire = no_display; /* actually shrink the display. */ + col_bone = col_head = col_tail = g_theme.const_color; } - - /* Draw root point if we are not connected to our parent */ - if ((BONE_FLAG(eBone, pchan) & BONE_CONNECTED) == 0) { + else { if (eBone) { - col_head = (eBone->flag & BONE_ROOTSEL) ? g_theme.vertex_select_color : col_bone; - } - else if (pchan) { - col_head = col_bone; + if (eBone->flag & BONE_TIPSEL) { + col_tail = g_theme.vertex_select_color; + } + if (boneflag & BONE_SELECTED) { + col_bone = g_theme.edge_select_color; + } + col_wire = g_theme.wire_color; } - } - if (g_theme.const_color != NULL) { - col_wire = no_display; /* actually shrink the display. */ - col_bone = col_head = col_tail = g_theme.const_color; + /* Draw root point if we are not connected to our parent. */ + if (!(eBone ? (eBone->parent && (eBone->flag & BONE_CONNECTED)) : + (pchan->bone->parent && (pchan->bone->flag & BONE_CONNECTED)))) { + + if (eBone) { + col_head = (eBone->flag & BONE_ROOTSEL) ? g_theme.vertex_select_color : col_bone; + } + else { + col_head = col_bone; + } + } } if (select_id == -1) { diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index 70bccb4849c..fe17019a5b5 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -1235,22 +1235,42 @@ static void DRW_shgroup_camera_background_images(OBJECT_Shaders *sh_data, uv2img_space[1][1] = image_height; const float fit_scale = image_aspect / camera_aspect; - img2cam_space[0][0] = 1.0 / image_width; - img2cam_space[1][1] = 1.0 / fit_scale / image_height; + if (camera_aspect < image_aspect) { + img2cam_space[0][0] = 1.0 / (1.0 / fit_scale) / image_width; + img2cam_space[1][1] = 1.0 / image_height; + } + else { + img2cam_space[0][0] = 1.0 / image_width; + img2cam_space[1][1] = 1.0 / fit_scale / image_height; + } /* Update scaling based on image and camera framing */ float scale_x = bgpic->scale; float scale_y = bgpic->scale; + float scale_x_offset = image_width; + float scale_y_offset = image_height; + if (image_aspect > 1.0f) { + scale_x_offset /= image_aspect; + if (camera_aspect > 1.0f) { + scale_x_offset *= min_ff(image_aspect, camera_aspect); + scale_y_offset *= min_ff(image_aspect, camera_aspect); + } + } + else { + scale_y_offset *= image_aspect; + if (camera_aspect < 1.0f) { + scale_x_offset /= max_ff(image_aspect, camera_aspect); + scale_y_offset /= max_ff(image_aspect, camera_aspect); + } + } + if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_ASPECT) { if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_CROP) { - if (image_aspect > camera_aspect) { - scale_x *= fit_scale; - scale_y *= fit_scale; - } + /* pass */ } else { - if (image_aspect > camera_aspect) { + if (camera_aspect < image_aspect) { scale_x /= fit_scale; scale_y /= fit_scale; } @@ -1262,7 +1282,12 @@ static void DRW_shgroup_camera_background_images(OBJECT_Shaders *sh_data, } else { /* Stretch image to camera aspect */ - scale_y /= 1.0 / fit_scale; + if (camera_aspect < image_aspect) { + scale_x /= fit_scale; + } + else { + scale_y *= fit_scale; + } } // scale image to match the desired aspect ratio @@ -1270,8 +1295,8 @@ static void DRW_shgroup_camera_background_images(OBJECT_Shaders *sh_data, scale_m4[1][1] = scale_y; /* Translate */ - translate_m4[3][0] = image_width * bgpic->offset[0] * 2.0f; - translate_m4[3][1] = image_height * bgpic->offset[1] * 2.0f; + translate_m4[3][0] = bgpic->offset[0] * 2.0f * scale_x_offset; + translate_m4[3][1] = bgpic->offset[1] * 2.0f * scale_y_offset; mul_m4_series(bg_data->transform_mat, win_m4_translate, diff --git a/source/blender/draw/modes/sculpt_mode.c b/source/blender/draw/modes/sculpt_mode.c index 9749619cffe..f32eb3b73ba 100644 --- a/source/blender/draw/modes/sculpt_mode.c +++ b/source/blender/draw/modes/sculpt_mode.c @@ -157,7 +157,7 @@ static void SCULPT_cache_populate(void *vedata, Object *ob) if ((ob == draw_ctx->obact) && (BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) || - ob->sculpt->deform_modifiers_active || ob->sculpt->shapekey_active)) { + !ob->sculpt->deform_modifiers_active || ob->sculpt->shapekey_active)) { PBVH *pbvh = ob->sculpt->pbvh; if (pbvh && pbvh_has_mask(pbvh)) { DRW_shgroup_call_sculpt(stl->g_data->mask_overlay_grp, ob, false, true, false); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 1c7edb6c46a..680fe3a00e5 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -693,6 +693,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.gpencil.primitive_circle ops.gpencil.primitive_curve ops.gpencil.primitive_line + ops.gpencil.primitive_polyline ops.gpencil.radius ops.gpencil.sculpt_clone ops.gpencil.sculpt_grab @@ -728,6 +729,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.mesh.spin.duplicate ops.mesh.vertices_smooth ops.node.links_cut + ops.paint.eyedropper_add ops.paint.vertex_color_fill ops.paint.weight_fill ops.paint.weight_gradient diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index c4f18c60f4d..3ab11f8f3f7 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1538,6 +1538,7 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) /* GPencil layer to use. */ ot->prop = RNA_def_int(ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ********************* Add Blank Frame *************************** */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index f29e782c618..c34e670c872 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -837,6 +837,11 @@ static short gp_stroke_addpoint( gpd->runtime.sbuffer = ED_gpencil_sbuffer_ensure( gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false); + /* Check the buffer was created. */ + if (gpd->runtime.sbuffer == NULL) { + return GP_STROKEADD_INVALID; + } + /* get pointer to destination point */ pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used); @@ -3660,7 +3665,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) tGPsdata *p = op->customdata; ToolSettings *ts = CTX_data_tool_settings(C); GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide; - tGPspoint *points = (tGPspoint *)p->gpd->runtime.sbuffer; /* default exit state - pass through to support MMB view nav, etc. */ int estate = OPERATOR_PASS_THROUGH; @@ -3969,6 +3973,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) int size_after = p->gpd->runtime.sbuffer_used; /* Last point of the event is always real (not fake). */ + tGPspoint *points = (tGPspoint *)p->gpd->runtime.sbuffer; tGPspoint *pt = &points[size_after - 1]; pt->tflag &= ~GP_TPOINT_FAKE; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index b194d28a8b8..91af444c28a 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -2530,8 +2530,8 @@ void ED_gpencil_select_toggle_all(bContext *C, int action) /* Ensure the SBuffer (while drawing stroke) size is enough to save all points of the stroke */ tGPspoint *ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array, - short *buffer_size, - short *buffer_used, + int *buffer_size, + int *buffer_used, const bool clear) { tGPspoint *p = NULL; @@ -2548,6 +2548,11 @@ tGPspoint *ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array, *buffer_size += GP_STROKE_BUFFER_CHUNK; p = MEM_recallocN(buffer_array, sizeof(struct tGPspoint) * *buffer_size); } + + if (p == NULL) { + *buffer_size = *buffer_used = 0; + } + buffer_array = p; } diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 0ff1b8bb40b..dce0e3931be 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -292,8 +292,8 @@ void ED_gpencil_select_toggle_all(struct bContext *C, int action); /* Ensure stroke sbuffer size is enough */ struct tGPspoint *ED_gpencil_sbuffer_ensure(struct tGPspoint *buffer_array, - short *buffer_size, - short *buffer_used, + int *buffer_size, + int *buffer_used, const bool clear); /* Tag all scene grease pencil object to update. */ void ED_gpencil_tag_scene_gpencil(struct Scene *scene); diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h index df4349a9063..f4725fea039 100644 --- a/source/blender/editors/include/ED_mask.h +++ b/source/blender/editors/include/ED_mask.h @@ -77,41 +77,41 @@ void ED_mask_draw_frames( struct Mask *mask, struct ARegion *ar, const int cfra, const int sfra, const int efra); /* mask_shapekey.c */ -void ED_mask_layer_shape_auto_key(struct MaskLayer *masklay, const int frame); +void ED_mask_layer_shape_auto_key(struct MaskLayer *mask_layer, const int frame); bool ED_mask_layer_shape_auto_key_all(struct Mask *mask, const int frame); bool ED_mask_layer_shape_auto_key_select(struct Mask *mask, const int frame); /* ----------- Mask AnimEdit API ------------------ */ -bool ED_masklayer_frames_looper(struct MaskLayer *masklay, +bool ED_masklayer_frames_looper(struct MaskLayer *mask_layer, struct Scene *scene, - short (*masklay_shape_cb)(struct MaskLayerShape *, - struct Scene *)); -void ED_masklayer_make_cfra_list(struct MaskLayer *masklay, ListBase *elems, bool onlysel); + short (*mask_layer_shape_cb)(struct MaskLayerShape *, + struct Scene *)); +void ED_masklayer_make_cfra_list(struct MaskLayer *mask_layer, ListBase *elems, bool onlysel); -bool ED_masklayer_frame_select_check(struct MaskLayer *masklay); -void ED_masklayer_frame_select_set(struct MaskLayer *masklay, short mode); -void ED_masklayer_frames_select_box(struct MaskLayer *masklay, +bool ED_masklayer_frame_select_check(struct MaskLayer *mask_layer); +void ED_masklayer_frame_select_set(struct MaskLayer *mask_layer, short mode); +void ED_masklayer_frames_select_box(struct MaskLayer *mask_layer, float min, float max, short select_mode); void ED_masklayer_frames_select_region(struct KeyframeEditData *ked, - struct MaskLayer *masklay, + struct MaskLayer *mask_layer, short tool, short select_mode); -void ED_mask_select_frames(struct MaskLayer *masklay, short select_mode); -void ED_mask_select_frame(struct MaskLayer *masklay, int selx, short select_mode); +void ED_mask_select_frames(struct MaskLayer *mask_layer, short select_mode); +void ED_mask_select_frame(struct MaskLayer *mask_layer, int selx, short select_mode); -bool ED_masklayer_frames_delete(struct MaskLayer *masklay); -void ED_masklayer_frames_duplicate(struct MaskLayer *masklay); +bool ED_masklayer_frames_delete(struct MaskLayer *mask_layer); +void ED_masklayer_frames_duplicate(struct MaskLayer *mask_layer); -void ED_masklayer_snap_frames(struct MaskLayer *masklay, struct Scene *scene, short mode); +void ED_masklayer_snap_frames(struct MaskLayer *mask_layer, struct Scene *scene, short mode); #if 0 void free_gpcopybuf(void); void copy_gpdata(void); void paste_gpdata(void); -void mirror_masklayer_frames(struct MaskLayer *masklay, short mode); +void mirror_masklayer_frames(struct MaskLayer *mask_layer, short mode); #endif #endif /* __ED_MASK_H__ */ diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index c3e61f5f2b2..4ca7c8f96ad 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -404,13 +404,10 @@ void ED_screen_user_menu_register(void); /* Cache display helpers */ -void ED_region_cache_draw_background(const struct ARegion *ar); +void ED_region_cache_draw_background(struct ARegion *ar); void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y); -void ED_region_cache_draw_cached_segments(const struct ARegion *ar, - const int num_segments, - const int *points, - const int sfra, - const int efra); +void ED_region_cache_draw_cached_segments( + struct ARegion *ar, const int num_segments, const int *points, const int sfra, const int efra); /* area_utils.c */ void ED_region_generic_tools_region_message_subscribe(const struct bContext *C, diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 40e0005b487..0e20abe4221 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -68,6 +68,8 @@ struct SnapObjectParams { unsigned int use_object_edit_cage : 1; /* snap to the closest element, use when using more than one snap type */ unsigned int use_occlusion_test : 1; + /* exclude back facing geometry from snapping */ + unsigned int use_backface_culling : 1; }; typedef struct SnapObjectContext SnapObjectContext; diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 1c2bc54d821..c15f299bca8 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -76,7 +76,7 @@ DEF_ICON(X) DEF_ICON(DUPLICATE) DEF_ICON(TRASH) DEF_ICON(COLLECTION_NEW) -DEF_ICON_BLANK(77) +DEF_ICON(OPTIONS) DEF_ICON(NODE) DEF_ICON(NODE_SEL) @@ -104,8 +104,8 @@ DEF_ICON(RIGHTARROW) DEF_ICON(DOWNARROW_HLT) DEF_ICON(FCURVE_SNAPSHOT) DEF_ICON(OBJECT_HIDDEN) -DEF_ICON_BLANK(105) -DEF_ICON_BLANK(106) +DEF_ICON(TOPBAR) +DEF_ICON(STATUSBAR) DEF_ICON(PLUGIN) /* various ui */ @@ -283,7 +283,7 @@ DEF_ICON(MOUSE_MOVE) DEF_ICON(MOUSE_LMB_DRAG) DEF_ICON(MOUSE_MMB_DRAG) DEF_ICON(MOUSE_RMB_DRAG) -DEF_ICON_BLANK(284) +DEF_ICON(MEMORY) DEF_ICON(PRESET_NEW) DEF_ICON_BLANK(286) DEF_ICON(DECORATE) @@ -774,9 +774,9 @@ DEF_ICON_BLANK(277) DEF_ICON_BLANK(772) DEF_ICON_BLANK(773) DEF_ICON_BLANK(774) -DEF_ICON_BLANK(775) -DEF_ICON_BLANK(776) -DEF_ICON_BLANK(777) +DEF_ICON(HOME) +DEF_ICON(DOCUMENTS) +DEF_ICON(TEMP) /* FILE SELECT */ DEF_ICON(SORTALPHA) @@ -857,7 +857,7 @@ DEF_ICON(SCRIPTPLUGINS) DEF_ICON_BLANK(855) DEF_ICON_BLANK(856) DEF_ICON_BLANK(857) -DEF_ICON_BLANK(858) +DEF_ICON(DISC) DEF_ICON(DESKTOP) DEF_ICON(EXTERNAL_DRIVE) DEF_ICON(NETWORK_DRIVE) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index b3e039292e1..4cef9ace66e 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -184,6 +184,18 @@ static void color_ensure_contrast_v3(uchar cp[3], const uchar cp_other[3], int c } } +static void color_mul_hsl_v3(uchar ch[3], float h_factor, float s_factor, float l_factor) +{ + float rgb[3], hsl[3]; + rgb_uchar_to_float(rgb, ch); + rgb_to_hsl_v(rgb, hsl); + hsl[0] *= h_factor; + hsl[1] *= s_factor; + hsl[2] *= l_factor; + hsl_to_rgb_v(hsl, rgb); + rgb_float_to_uchar(ch, rgb); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2573,11 +2585,12 @@ static void ui_widget_color_disabled(uiWidgetType *wt) wt->wcol_theme = &wcol_theme_s; } -static void widget_active_color(uchar cp[3]) +static void widget_active_color(uiWidgetColors *wcol) { - cp[0] = cp[0] >= 240 ? 255 : cp[0] + 15; - cp[1] = cp[1] >= 240 ? 255 : cp[1] + 15; - cp[2] = cp[2] >= 240 ? 255 : cp[2] + 15; + bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner)); + color_mul_hsl_v3(wcol->inner, 1.0f, 1.15f, dark ? 1.2f : 1.1f); + color_mul_hsl_v3(wcol->outline, 1.0f, 1.15f, 1.15f); + color_mul_hsl_v3(wcol->text, 1.0f, 1.15f, dark ? 1.25f : 0.8f); } static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wcol_state, @@ -2641,10 +2654,10 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag) if (color_blend != NULL) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); } + } - if (state & UI_ACTIVE) { /* mouse over? */ - widget_active_color(wt->wcol.inner); - } + if (state & UI_ACTIVE) { + widget_active_color(&wt->wcol); } if (state & UI_BUT_REDALERT) { @@ -3403,7 +3416,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); if (state & UI_STATE_ACTIVE_LEFT) { - widget_active_color(wcol_zone.inner); + widget_active_color(&wcol_zone); } rect_zone = *rect; @@ -3423,7 +3436,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); if (state & UI_STATE_ACTIVE_RIGHT) { - widget_active_color(wcol_zone.inner); + widget_active_color(&wcol_zone); } rect_zone = *rect; @@ -3442,7 +3455,7 @@ static void widget_numbut_draw( wcol_zone = *wcol; copy_v3_v3_uchar(wcol_zone.item, wcol->text); if (!(state & (UI_STATE_ACTIVE_LEFT | UI_STATE_ACTIVE_RIGHT))) { - widget_active_color(wcol_zone.inner); + widget_active_color(&wcol_zone); } rect_zone = *rect; diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 4624611419e..5c730c74df7 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -55,7 +55,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, float tangent[2], const bool use_deform, const bool use_project, - MaskLayer **masklay_r, + MaskLayer **mask_layer_r, MaskSpline **spline_r, MaskSplinePoint **point_r, float *u_r, @@ -64,7 +64,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - MaskLayer *point_masklay; + MaskLayer *point_mask_layer; MaskSpline *point_spline; MaskSplinePoint *point = NULL; float dist_best_sq = FLT_MAX, co[2]; @@ -81,16 +81,16 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, co[0] = normal_co[0] * scalex; co[1] = normal_co[1] * scaley; - for (MaskLayer *masklay_orig = mask_orig->masklayers.first, - *masklay_eval = mask_eval->masklayers.first; - masklay_orig != NULL; - masklay_orig = masklay_orig->next, masklay_eval = masklay_eval->next) { - if (masklay_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { + if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (MaskSpline *spline_orig = masklay_orig->splines.first, - *spline_eval = masklay_eval->splines.first; + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; spline_orig != NULL; spline_orig = spline_orig->next, spline_eval = spline_eval->next) { int i; @@ -136,7 +136,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, sub_v2_v2v2(tangent, &diff_points[2 * j + 2], &diff_points[2 * j]); } - point_masklay = masklay_orig; + point_mask_layer = mask_layer_orig; point_spline = spline_orig; point = use_deform ? &spline_orig->points[(cur_point_eval - spline_eval->points_deform)] : @@ -156,8 +156,8 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } if (point && dist_best_sq < threshold) { - if (masklay_r) { - *masklay_r = point_masklay; + if (mask_layer_r) { + *mask_layer_r = point_mask_layer; } if (spline_r) { @@ -184,8 +184,8 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, return true; } - if (masklay_r) { - *masklay_r = NULL; + if (mask_layer_r) { + *mask_layer_r = NULL; } if (spline_r) { @@ -331,21 +331,22 @@ static void setup_vertex_point(Mask *mask, /* **** add extrude vertex **** */ -static void finSelectedSplinePoint(MaskLayer *masklay, +static void finSelectedSplinePoint(MaskLayer *mask_layer, MaskSpline **spline, MaskSplinePoint **point, bool check_active) { - MaskSpline *cur_spline = masklay->splines.first; + MaskSpline *cur_spline = mask_layer->splines.first; *spline = NULL; *point = NULL; if (check_active) { /* TODO, having an active point but no active spline is possible, why? */ - if (masklay->act_spline && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) { - *spline = masklay->act_spline; - *point = masklay->act_point; + if (mask_layer->act_spline && mask_layer->act_point && + MASKPOINT_ISSEL_ANY(mask_layer->act_point)) { + *spline = mask_layer->act_spline; + *point = mask_layer->act_point; return; } } @@ -397,7 +398,7 @@ static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index) static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2]) { - MaskLayer *masklay; + MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; const float threshold = 9; @@ -412,7 +413,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 tangent, true, true, - &masklay, + &mask_layer, &spline, &point, &u, @@ -432,14 +433,14 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 setup_vertex_point(mask, spline, new_point, co, u, ctime, NULL, true); /* TODO - we could pass the spline! */ - BKE_mask_layer_shape_changed_add(masklay, - BKE_mask_layer_shape_spline_to_index(masklay, spline) + + BKE_mask_layer_shape_changed_add(mask_layer, + BKE_mask_layer_shape_spline_to_index(mask_layer, spline) + point_index + 1, true, true); - masklay->act_spline = spline; - masklay->act_point = new_point; + mask_layer->act_spline = spline; + mask_layer->act_point = new_point; WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); @@ -451,7 +452,7 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 static bool add_vertex_extrude(const bContext *C, Mask *mask, - MaskLayer *masklay, + MaskLayer *mask_layer, const float co[2]) { Scene *scene = CTX_data_scene(C); @@ -468,11 +469,11 @@ static bool add_vertex_extrude(const bContext *C, bool do_cyclic_correct = false; bool do_prev; /* use prev point rather then next?? */ - if (!masklay) { + if (!mask_layer) { return false; } else { - finSelectedSplinePoint(masklay, &spline, &point, true); + finSelectedSplinePoint(mask_layer, &spline, &point, true); } ED_mask_select_toggle_all(mask, SEL_DESELECT); @@ -533,14 +534,17 @@ static bool add_vertex_extrude(const bContext *C, new_point = &spline->points[point_index + 1]; } - masklay->act_point = new_point; + mask_layer->act_point = new_point; setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false); - if (masklay->splines_shapes.first) { + if (mask_layer->splines_shapes.first) { point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); - BKE_mask_layer_shape_changed_add( - masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, true, true); + BKE_mask_layer_shape_changed_add(mask_layer, + BKE_mask_layer_shape_spline_to_index(mask_layer, spline) + + point_index, + true, + true); } WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); @@ -548,7 +552,7 @@ static bool add_vertex_extrude(const bContext *C, return true; } -static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *masklay, const float co[2]) +static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer, const float co[2]) { Scene *scene = CTX_data_scene(C); const float ctime = CFRA; @@ -556,27 +560,30 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *masklay, co MaskSpline *spline; MaskSplinePoint *new_point = NULL, *ref_point = NULL; - if (!masklay) { - /* if there's no masklay currently operationg on, create new one */ - masklay = BKE_mask_layer_new(mask, ""); + if (!mask_layer) { + /* if there's no mask layer currently operationg on, create new one */ + mask_layer = BKE_mask_layer_new(mask, ""); mask->masklay_act = mask->masklay_tot - 1; } ED_mask_select_toggle_all(mask, SEL_DESELECT); - spline = BKE_mask_spline_add(masklay); + spline = BKE_mask_spline_add(mask_layer); - masklay->act_spline = spline; + mask_layer->act_spline = spline; new_point = spline->points; - masklay->act_point = new_point; + mask_layer->act_point = new_point; setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false); { int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); - BKE_mask_layer_shape_changed_add( - masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, true, true); + BKE_mask_layer_shape_changed_add(mask_layer, + BKE_mask_layer_shape_spline_to_index(mask_layer, spline) + + point_index, + true, + true); } WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); @@ -584,69 +591,112 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *masklay, co return true; } -static int add_vertex_exec(bContext *C, wmOperator *op) +/* Convert coordinate from normalized space to pixel one. + * TODO(sergey): Make the function more generally available. */ +static void mask_point_make_pixel_space(bContext *C, + float point_normalized[2], + float point_pixel[2]) { - Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); - float co[2]; + float scalex, scaley; + ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley); - if (mask == NULL) { - /* if there's no active mask, create one */ - mask = ED_mask_new(C, NULL); + point_pixel[0] = point_normalized[0] * scalex; + point_pixel[1] = point_normalized[1] * scaley; +} + +static int add_vertex_handle_cyclic_at_point(bContext *C, + Mask *mask, + MaskSpline *spline, + MaskSplinePoint *active_point, + MaskSplinePoint *other_point, + float co[2]) +{ + const float tolerance_in_pixels_squared = 4 * 4; + + if (spline->flag & MASK_SPLINE_CYCLIC) { + /* No cycling toggle needed, we've got nothing meaningful to do in this operator. */ + return OPERATOR_CANCELLED; } - masklay = BKE_mask_layer_active(mask); + float co_pixel[2]; + mask_point_make_pixel_space(C, co, co_pixel); + + float point_pixel[2]; + mask_point_make_pixel_space(C, other_point->bezt.vec[1], point_pixel); - if (masklay && masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { - masklay = NULL; + const float dist_squared = len_squared_v2v2(co_pixel, point_pixel); + if (dist_squared > tolerance_in_pixels_squared) { + return OPERATOR_PASS_THROUGH; } - RNA_float_get_array(op->ptr, "location", co); + spline->flag |= MASK_SPLINE_CYCLIC; - /* TODO, having an active point but no active spline is possible, why? */ - if (masklay && masklay->act_spline && masklay->act_point && - MASKPOINT_ISSEL_ANY(masklay->act_point)) { + /* TODO, update keyframes in time. */ + BKE_mask_calc_handle_point_auto(spline, active_point, false); + BKE_mask_calc_handle_point_auto(spline, other_point, false); - /* cheap trick - double click for cyclic */ - MaskSpline *spline = masklay->act_spline; - MaskSplinePoint *point = masklay->act_point; + DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY); - const bool is_sta = (point == spline->points); - const bool is_end = (point == &spline->points[spline->tot_point - 1]); + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); - /* then check are we overlapping the mouse */ - if ((is_sta || is_end) && equals_v2v2(co, point->bezt.vec[1])) { - if (spline->flag & MASK_SPLINE_CYCLIC) { - /* nothing to do */ - return OPERATOR_CANCELLED; - } - else { - /* recalc the connecting point as well to make a nice even curve */ - MaskSplinePoint *point_other = is_end ? spline->points : - &spline->points[spline->tot_point - 1]; - spline->flag |= MASK_SPLINE_CYCLIC; + return OPERATOR_FINISHED; +} - /* TODO, update keyframes in time */ - BKE_mask_calc_handle_point_auto(spline, point, false); - BKE_mask_calc_handle_point_auto(spline, point_other, false); +static int add_vertex_handle_cyclic( + bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *active_point, float co[2]) +{ + MaskSplinePoint *first_point = &spline->points[0]; + MaskSplinePoint *last_point = &spline->points[spline->tot_point - 1]; + const bool is_first_point_active = (active_point == first_point); + const bool is_last_point_active = (active_point == last_point); + if (is_last_point_active) { + return add_vertex_handle_cyclic_at_point(C, mask, spline, active_point, first_point, co); + } + else if (is_first_point_active) { + return add_vertex_handle_cyclic_at_point(C, mask, spline, active_point, last_point, co); + } + return OPERATOR_PASS_THROUGH; +} - DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY); +static int add_vertex_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + if (mask == NULL) { + /* if there's no active mask, create one */ + mask = ED_mask_new(C, NULL); + } - WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); - return OPERATOR_FINISHED; - } + MaskLayer *mask_layer = BKE_mask_layer_active(mask); + + if (mask_layer && mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + mask_layer = NULL; + } + + float co[2]; + RNA_float_get_array(op->ptr, "location", co); + + /* TODO, having an active point but no active spline is possible, why? */ + if (mask_layer && mask_layer->act_spline && mask_layer->act_point && + MASKPOINT_ISSEL_ANY(mask_layer->act_point)) { + MaskSpline *spline = mask_layer->act_spline; + MaskSplinePoint *active_point = mask_layer->act_point; + const int cyclic_result = add_vertex_handle_cyclic(C, mask, spline, active_point, co); + if (cyclic_result != OPERATOR_PASS_THROUGH) { + return cyclic_result; } if (!add_vertex_subdivide(C, mask, co)) { - if (!add_vertex_extrude(C, mask, masklay, co)) { + if (!add_vertex_extrude(C, mask, mask_layer, co)) { return OPERATOR_CANCELLED; } } } else { if (!add_vertex_subdivide(C, mask, co)) { - if (!add_vertex_new(C, mask, masklay, co)) { + if (!add_vertex_new(C, mask, mask_layer, co)) { return OPERATOR_CANCELLED; } } @@ -704,7 +754,7 @@ void MASK_OT_add_vertex(wmOperatorType *ot) static int add_feather_vertex_exec(bContext *C, wmOperator *op) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; + MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; const float threshold = 9; @@ -717,8 +767,19 @@ static int add_feather_vertex_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - if (ED_mask_find_nearest_diff_point( - C, mask, co, threshold, true, NULL, true, true, &masklay, &spline, &point, &u, NULL)) { + if (ED_mask_find_nearest_diff_point(C, + mask, + co, + threshold, + true, + NULL, + true, + true, + &mask_layer, + &spline, + &point, + &u, + NULL)) { float w = BKE_mask_point_weight(spline, point, u); float weight_scalar = BKE_mask_point_weight_scalar(spline, point, u); diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 7247697dcb7..71aa860d703 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -38,6 +38,7 @@ #include "ED_clip.h" #include "ED_mask.h" /* own include */ +#include "ED_screen.h" #include "ED_space_api.h" #include "BIF_glutil.h" @@ -55,13 +56,13 @@ #include "mask_intern.h" /* own include */ -static void mask_spline_color_get(MaskLayer *masklay, +static void mask_spline_color_get(MaskLayer *mask_layer, MaskSpline *spline, const bool is_sel, unsigned char r_rgb[4]) { if (is_sel) { - if (masklay->act_spline == spline) { + if (mask_layer->act_spline == spline) { r_rgb[0] = r_rgb[1] = r_rgb[2] = 255; } else { @@ -77,7 +78,7 @@ static void mask_spline_color_get(MaskLayer *masklay, r_rgb[3] = 255; } -static void mask_spline_feather_color_get(MaskLayer *UNUSED(masklay), +static void mask_spline_feather_color_get(MaskLayer *UNUSED(mask_layer), MaskSpline *UNUSED(spline), const bool is_sel, unsigned char r_rgb[4]) @@ -186,13 +187,13 @@ static void draw_single_handle(const MaskLayer *mask_layer, /* return non-zero if spline is selected */ static void draw_spline_points(const bContext *C, - MaskLayer *masklay, + MaskLayer *mask_layer, MaskSpline *spline, const char draw_flag, const char draw_type) { const bool is_spline_sel = (spline->flag & SELECT) && - (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0; + (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0; const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0; unsigned char rgb_spline[4]; @@ -215,7 +216,7 @@ static void draw_spline_points(const bContext *C, /* TODO, add this to sequence editor */ float handle_size = 2.0f * UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE) * U.pixelsize; - mask_spline_color_get(masklay, spline, is_spline_sel, rgb_spline); + mask_spline_color_get(mask_layer, spline, is_spline_sel, rgb_spline); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); @@ -248,7 +249,7 @@ static void draw_spline_points(const bContext *C, } if (sel) { - if (point == masklay->act_point) { + if (point == mask_layer->act_point) { immUniformColor3f(1.0f, 1.0f, 1.0f); } else { @@ -299,7 +300,7 @@ static void draw_spline_points(const bContext *C, mask_point_undistort_pos(sc, handle, handle); } draw_single_handle( - masklay, point, MASK_WHICH_HANDLE_STICK, draw_type, handle_size, vert, handle); + mask_layer, point, MASK_WHICH_HANDLE_STICK, draw_type, handle_size, vert, handle); } else { float handle_left[2], handle_right[2]; @@ -310,9 +311,9 @@ static void draw_spline_points(const bContext *C, mask_point_undistort_pos(sc, handle_left, handle_left); } draw_single_handle( - masklay, point, MASK_WHICH_HANDLE_LEFT, draw_type, handle_size, vert, handle_left); + mask_layer, point, MASK_WHICH_HANDLE_LEFT, draw_type, handle_size, vert, handle_left); draw_single_handle( - masklay, point, MASK_WHICH_HANDLE_RIGHT, draw_type, handle_size, vert, handle_right); + mask_layer, point, MASK_WHICH_HANDLE_RIGHT, draw_type, handle_size, vert, handle_right); } /* bind program in loop so it does not interfere with draw_single_handle */ @@ -320,7 +321,7 @@ static void draw_spline_points(const bContext *C, /* draw CV point */ if (MASKPOINT_ISSEL_KNOT(point)) { - if (point == masklay->act_point) { + if (point == mask_layer->act_point) { immUniformColor3f(1.0f, 1.0f, 1.0f); } else { @@ -351,7 +352,7 @@ static void draw_spline_points(const bContext *C, immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); immUniform1f("outlineWidth", 1.5f); - if (masklay->act_spline == spline) { + if (mask_layer->act_spline == spline) { immUniformColor3f(1.0f, 1.0f, 1.0f); } else { @@ -518,7 +519,7 @@ static void mask_draw_curve_type(const bContext *C, } static void draw_spline_curve(const bContext *C, - MaskLayer *masklay, + MaskLayer *mask_layer, MaskSpline *spline, const char draw_flag, const char draw_type, @@ -532,7 +533,7 @@ static void draw_spline_curve(const bContext *C, unsigned char rgb_tmp[4]; const bool is_spline_sel = (spline->flag & SELECT) && - (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0; + (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0; const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0; const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0; @@ -556,7 +557,7 @@ static void draw_spline_curve(const bContext *C, spline, &tot_feather_point, resol, (is_fill != false)); /* draw feather */ - mask_spline_feather_color_get(masklay, spline, is_spline_sel, rgb_tmp); + mask_spline_feather_color_get(mask_layer, spline, is_spline_sel, rgb_tmp); mask_draw_curve_type( C, spline, feather_points, tot_feather_point, true, is_active, rgb_tmp, draw_type); @@ -580,7 +581,7 @@ static void draw_spline_curve(const bContext *C, MEM_freeN(feather_points); /* draw main curve */ - mask_spline_color_get(masklay, spline, is_spline_sel, rgb_tmp); + mask_spline_color_get(mask_layer, spline, is_spline_sel, rgb_tmp); mask_draw_curve_type( C, spline, diff_points, tot_diff_point, false, is_active, rgb_tmp, draw_type); MEM_freeN(diff_points); @@ -590,37 +591,37 @@ static void draw_spline_curve(const bContext *C, } } -static void draw_masklays(const bContext *C, - Mask *mask, - const char draw_flag, - const char draw_type, - const int width, - const int height) +static void draw_mask_layers(const bContext *C, + Mask *mask, + const char draw_flag, + const char draw_type, + const int width, + const int height) { GPU_blend(true); GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); GPU_program_point_size(true); - MaskLayer *masklay; + MaskLayer *mask_layer; int i; - for (masklay = mask->masklayers.first, i = 0; masklay; masklay = masklay->next, i++) { - MaskSpline *spline; + for (mask_layer = mask->masklayers.first, i = 0; mask_layer != NULL; + mask_layer = mask_layer->next, i++) { const bool is_active = (i == mask->masklay_act); - if (masklay->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { /* draw curve itself first... */ - draw_spline_curve(C, masklay, spline, draw_flag, draw_type, is_active, width, height); + draw_spline_curve(C, mask_layer, spline, draw_flag, draw_type, is_active, width, height); - if (!(masklay->restrictflag & MASK_RESTRICT_SELECT)) { + if (!(mask_layer->restrictflag & MASK_RESTRICT_SELECT)) { /* ...and then handles over the curve so they're nicely visible */ - draw_spline_points(C, masklay, spline, draw_flag, draw_type); + draw_spline_points(C, mask_layer, spline, draw_flag, draw_type); } /* show undeform for testing */ @@ -628,8 +629,8 @@ static void draw_masklays(const bContext *C, void *back = spline->points_deform; spline->points_deform = NULL; - draw_spline_curve(C, masklay, spline, draw_flag, draw_type, is_active, width, height); - draw_spline_points(C, masklay, spline, draw_flag, draw_type); + draw_spline_curve(C, mask_layer, spline, draw_flag, draw_type, is_active, width, height); + draw_spline_points(C, mask_layer, spline, draw_flag, draw_type); spline->points_deform = back; } } @@ -651,7 +652,7 @@ void ED_mask_draw(const bContext *C, const char draw_flag, const char draw_type) ED_mask_get_size(sa, &width, &height); - draw_masklays(C, mask, draw_flag, draw_type, width, height); + draw_mask_layers(C, mask, draw_flag, draw_type, width, height); } static float *mask_rasterize(Mask *mask, const int width, const int height) @@ -783,7 +784,7 @@ void ED_mask_draw_region( } /* draw! */ - draw_masklays(C, mask_eval, draw_flag, draw_type, width, height); + draw_mask_layers(C, mask_eval, draw_flag, draw_type, width, height); if (do_draw_cb) { ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW); @@ -796,32 +797,39 @@ void ED_mask_draw_frames(Mask *mask, ARegion *ar, const int cfra, const int sfra { const float framelen = ar->winx / (float)(efra - sfra + 1); - MaskLayer *masklay = BKE_mask_layer_active(mask); + MaskLayer *mask_layer = BKE_mask_layer_active(mask); + if (mask_layer == NULL) { + return; + } - if (masklay) { - unsigned int num_lines = BLI_listbase_count(&masklay->splines_shapes); + unsigned int num_lines = BLI_listbase_count(&mask_layer->splines_shapes); + if (num_lines == 0) { + return; + } - if (num_lines > 0) { - uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ + const rcti *rect_visible = ED_region_visible_rect(ar); + const int region_bottom = rect_visible->ymin; - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4ub(255, 175, 0, 255); + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBegin(GPU_PRIM_LINES, 2 * num_lines); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ub(255, 175, 0, 255); - for (MaskLayerShape *masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - int frame = masklay_shape->frame; + immBegin(GPU_PRIM_LINES, 2 * num_lines); - /* draw_keyframe(i, CFRA, sfra, framelen, 1); */ - int height = (frame == cfra) ? 22 : 10; - int x = (frame - sfra) * framelen; - immVertex2i(pos, x, 0); - immVertex2i(pos, x, height); - } - immEnd(); - immUnbindProgram(); - } + for (MaskLayerShape *mask_layer_shape = mask_layer->splines_shapes.first; + mask_layer_shape != NULL; + mask_layer_shape = mask_layer_shape->next) { + int frame = mask_layer_shape->frame; + + /* draw_keyframe(i, CFRA, sfra, framelen, 1); */ + int height = (frame == cfra) ? 22 : 10; + int x = (frame - sfra) * framelen; + immVertex2i(pos, x, region_bottom); + immVertex2i(pos, x, region_bottom + height * UI_DPI_FAC); } + immEnd(); + immUnbindProgram(); } diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c index 0c5591ed89b..56d8ef56160 100644 --- a/source/blender/editors/mask/mask_edit.c +++ b/source/blender/editors/mask/mask_edit.c @@ -371,7 +371,6 @@ void ED_mask_cursor_location_get(ScrArea *sa, float cursor[2]) bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2]) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *mask_layer; bool ok = false; if (mask == NULL) { @@ -379,15 +378,14 @@ bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2]) } INIT_MINMAX2(min, max); - for (mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { - MaskSpline *spline; + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; + mask_layer = mask_layer->next) { if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); - int i; - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; MaskSplinePoint *deform_point = &points_array[i]; BezTriple *bezt = &point->bezt; diff --git a/source/blender/editors/mask/mask_editaction.c b/source/blender/editors/mask/mask_editaction.c index 985fa58cace..33f4bab14ec 100644 --- a/source/blender/editors/mask/mask_editaction.c +++ b/source/blender/editors/mask/mask_editaction.c @@ -53,22 +53,22 @@ /* Generics - Loopers */ /* Loops over the mask-frames for a mask-layer, and applies the given callback */ -bool ED_masklayer_frames_looper(MaskLayer *masklay, +bool ED_masklayer_frames_looper(MaskLayer *mask_layer, Scene *scene, - short (*masklay_shape_cb)(MaskLayerShape *, Scene *)) + short (*mask_layer_shape_cb)(MaskLayerShape *, Scene *)) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; /* error checker */ - if (masklay == NULL) { + if (mask_layer == NULL) { return false; } /* do loop */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { /* execute callback */ - if (masklay_shape_cb(masklay_shape, scene)) { + if (mask_layer_shape_cb(mask_layer_shape, scene)) { return true; } } @@ -81,24 +81,24 @@ bool ED_masklayer_frames_looper(MaskLayer *masklay, /* Data Conversion Tools */ /* make a listing all the mask-frames in a layer as cfraelems */ -void ED_masklayer_make_cfra_list(MaskLayer *masklay, ListBase *elems, bool onlysel) +void ED_masklayer_make_cfra_list(MaskLayer *mask_layer, ListBase *elems, bool onlysel) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; CfraElem *ce; /* error checking */ - if (ELEM(NULL, masklay, elems)) { + if (ELEM(NULL, mask_layer, elems)) { return; } /* loop through mask-frames, adding */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - if ((onlysel == false) || (masklay_shape->flag & MASK_SHAPE_SELECT)) { + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { + if ((onlysel == false) || (mask_layer_shape->flag & MASK_SHAPE_SELECT)) { ce = MEM_callocN(sizeof(CfraElem), "CfraElem"); - ce->cfra = (float)masklay_shape->frame; - ce->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? 1 : 0; + ce->cfra = (float)mask_layer_shape->frame; + ce->sel = (mask_layer_shape->flag & MASK_SHAPE_SELECT) ? 1 : 0; BLI_addtail(elems, ce); } @@ -109,19 +109,19 @@ void ED_masklayer_make_cfra_list(MaskLayer *masklay, ListBase *elems, bool onlys /* Selection Tools */ /* check if one of the frames in this layer is selected */ -bool ED_masklayer_frame_select_check(MaskLayer *masklay) +bool ED_masklayer_frame_select_check(MaskLayer *mask_layer) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; /* error checking */ - if (masklay == NULL) { + if (mask_layer == NULL) { return 0; } /* stop at the first one found */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - if (masklay_shape->flag & MASK_SHAPE_SELECT) { + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { return 1; } } @@ -131,120 +131,120 @@ bool ED_masklayer_frame_select_check(MaskLayer *masklay) } /* helper function - select mask-frame based on SELECT_* mode */ -static void masklayshape_select(MaskLayerShape *masklay_shape, short select_mode) +static void mask_layer_shape_select(MaskLayerShape *mask_layer_shape, short select_mode) { - if (masklay_shape == NULL) { + if (mask_layer_shape == NULL) { return; } switch (select_mode) { case SELECT_ADD: - masklay_shape->flag |= MASK_SHAPE_SELECT; + mask_layer_shape->flag |= MASK_SHAPE_SELECT; break; case SELECT_SUBTRACT: - masklay_shape->flag &= ~MASK_SHAPE_SELECT; + mask_layer_shape->flag &= ~MASK_SHAPE_SELECT; break; case SELECT_INVERT: - masklay_shape->flag ^= MASK_SHAPE_SELECT; + mask_layer_shape->flag ^= MASK_SHAPE_SELECT; break; } } /* set all/none/invert select (like above, but with SELECT_* modes) */ -void ED_mask_select_frames(MaskLayer *masklay, short select_mode) +void ED_mask_select_frames(MaskLayer *mask_layer, short select_mode) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; /* error checking */ - if (masklay == NULL) { + if (mask_layer == NULL) { return; } /* handle according to mode */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - masklayshape_select(masklay_shape, select_mode); + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { + mask_layer_shape_select(mask_layer_shape, select_mode); } } /* set all/none/invert select */ -void ED_masklayer_frame_select_set(MaskLayer *masklay, short mode) +void ED_masklayer_frame_select_set(MaskLayer *mask_layer, short mode) { /* error checking */ - if (masklay == NULL) { + if (mask_layer == NULL) { return; } /* now call the standard function */ - ED_mask_select_frames(masklay, mode); + ED_mask_select_frames(mask_layer, mode); } /* select the frame in this layer that occurs on this frame (there should only be one at most) */ -void ED_mask_select_frame(MaskLayer *masklay, int selx, short select_mode) +void ED_mask_select_frame(MaskLayer *mask_layer, int selx, short select_mode) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; - if (masklay == NULL) { + if (mask_layer == NULL) { return; } - masklay_shape = BKE_mask_layer_shape_find_frame(masklay, selx); + mask_layer_shape = BKE_mask_layer_shape_find_frame(mask_layer, selx); - if (masklay_shape) { - masklayshape_select(masklay_shape, select_mode); + if (mask_layer_shape) { + mask_layer_shape_select(mask_layer_shape, select_mode); } } /* select the frames in this layer that occur within the bounds specified */ -void ED_masklayer_frames_select_box(MaskLayer *masklay, float min, float max, short select_mode) +void ED_masklayer_frames_select_box(MaskLayer *mask_layer, float min, float max, short select_mode) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; - if (masklay == NULL) { + if (mask_layer == NULL) { return; } /* only select those frames which are in bounds */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - if (IN_RANGE(masklay_shape->frame, min, max)) { - masklayshape_select(masklay_shape, select_mode); + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { + if (IN_RANGE(mask_layer_shape->frame, min, max)) { + mask_layer_shape_select(mask_layer_shape, select_mode); } } } /* select the frames in this layer that occur within the lasso/circle region specified */ void ED_masklayer_frames_select_region(KeyframeEditData *ked, - MaskLayer *masklay, + MaskLayer *mask_layer, short tool, short select_mode) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; - if (masklay == NULL) { + if (mask_layer == NULL) { return; } /* only select frames which are within the region */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { /* construct a dummy point coordinate to do this testing with */ float pt[2] = {0}; - pt[0] = masklay_shape->frame; + pt[0] = mask_layer_shape->frame; pt[1] = ked->channel_y; /* check the necessary regions */ if (tool == BEZT_OK_CHANNEL_LASSO) { /* Lasso */ if (keyframe_region_lasso_test(ked->data, pt)) { - masklayshape_select(masklay_shape, select_mode); + mask_layer_shape_select(mask_layer_shape, select_mode); } } else if (tool == BEZT_OK_CHANNEL_CIRCLE) { /* Circle */ if (keyframe_region_circle_test(ked->data, pt)) { - masklayshape_select(masklay_shape, select_mode); + mask_layer_shape_select(mask_layer_shape, select_mode); } } } @@ -254,23 +254,23 @@ void ED_masklayer_frames_select_region(KeyframeEditData *ked, /* Frame Editing Tools */ /* Delete selected frames */ -bool ED_masklayer_frames_delete(MaskLayer *masklay) +bool ED_masklayer_frames_delete(MaskLayer *mask_layer) { - MaskLayerShape *masklay_shape, *masklay_shape_next; + MaskLayerShape *mask_layer_shape, *mask_layer_shape_next; bool changed = false; /* error checking */ - if (masklay == NULL) { + if (mask_layer == NULL) { return false; } /* check for frames to delete */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape_next) { - masklay_shape_next = masklay_shape->next; + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape_next) { + mask_layer_shape_next = mask_layer_shape->next; - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - BKE_mask_layer_shape_unlink(masklay, masklay_shape); + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { + BKE_mask_layer_shape_unlink(mask_layer, mask_layer_shape); changed = true; } } @@ -279,29 +279,30 @@ bool ED_masklayer_frames_delete(MaskLayer *masklay) } /* Duplicate selected frames from given mask-layer */ -void ED_masklayer_frames_duplicate(MaskLayer *masklay) +void ED_masklayer_frames_duplicate(MaskLayer *mask_layer) { - MaskLayerShape *masklay_shape, *gpfn; + MaskLayerShape *mask_layer_shape, *gpfn; /* error checking */ - if (masklay == NULL) { + if (mask_layer == NULL) { return; } /* duplicate selected frames */ - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = gpfn) { - gpfn = masklay_shape->next; + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = gpfn) { + gpfn = mask_layer_shape->next; /* duplicate this frame */ - if (masklay_shape->flag & MASK_SHAPE_SELECT) { + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { MaskLayerShape *mask_shape_dupe; /* duplicate frame, and deselect self */ - mask_shape_dupe = BKE_mask_layer_shape_duplicate(masklay_shape); - masklay_shape->flag &= ~MASK_SHAPE_SELECT; + mask_shape_dupe = BKE_mask_layer_shape_duplicate(mask_layer_shape); + mask_layer_shape->flag &= ~MASK_SHAPE_SELECT; /* XXX - how to handle duplicate frames? */ - BLI_insertlinkafter(&masklay->splines_shapes, masklay_shape, mask_shape_dupe); + BLI_insertlinkafter(&mask_layer->splines_shapes, mask_layer_shape, mask_shape_dupe); } } } @@ -309,55 +310,55 @@ void ED_masklayer_frames_duplicate(MaskLayer *masklay) /* -------------------------------------- */ /* Snap Tools */ -static short snap_masklayer_nearest(MaskLayerShape *masklay_shape, Scene *UNUSED(scene)) +static short snap_mask_layer_nearest(MaskLayerShape *mask_layer_shape, Scene *UNUSED(scene)) { - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - masklay_shape->frame = (int)(floor(masklay_shape->frame + 0.5)); + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { + mask_layer_shape->frame = (int)(floor(mask_layer_shape->frame + 0.5)); } return 0; } -static short snap_masklayer_nearestsec(MaskLayerShape *masklay_shape, Scene *scene) +static short snap_mask_layer_nearestsec(MaskLayerShape *mask_layer_shape, Scene *scene) { float secf = (float)FPS; - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - masklay_shape->frame = (int)(floorf(masklay_shape->frame / secf + 0.5f) * secf); + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { + mask_layer_shape->frame = (int)(floorf(mask_layer_shape->frame / secf + 0.5f) * secf); } return 0; } -static short snap_masklayer_cframe(MaskLayerShape *masklay_shape, Scene *scene) +static short snap_mask_layer_cframe(MaskLayerShape *mask_layer_shape, Scene *scene) { - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - masklay_shape->frame = (int)CFRA; + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { + mask_layer_shape->frame = (int)CFRA; } return 0; } -static short snap_masklayer_nearmarker(MaskLayerShape *masklay_shape, Scene *scene) +static short snap_mask_layer_nearmarker(MaskLayerShape *mask_layer_shape, Scene *scene) { - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - masklay_shape->frame = (int)ED_markers_find_nearest_marker_time(&scene->markers, - (float)masklay_shape->frame); + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { + mask_layer_shape->frame = (int)ED_markers_find_nearest_marker_time( + &scene->markers, (float)mask_layer_shape->frame); } return 0; } /* snap selected frames to ... */ -void ED_masklayer_snap_frames(MaskLayer *masklay, Scene *scene, short mode) +void ED_masklayer_snap_frames(MaskLayer *mask_layer, Scene *scene, short mode) { switch (mode) { case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */ - ED_masklayer_frames_looper(masklay, scene, snap_masklayer_nearest); + ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_nearest); break; case SNAP_KEYS_CURFRAME: /* snap to current frame */ - ED_masklayer_frames_looper(masklay, scene, snap_masklayer_cframe); + ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_cframe); break; case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */ - ED_masklayer_frames_looper(masklay, scene, snap_masklayer_nearmarker); + ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_nearmarker); break; case SNAP_KEYS_NEARSEC: /* snap to nearest second */ - ED_masklayer_frames_looper(masklay, scene, snap_masklayer_nearestsec); + ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_nearestsec); break; default: /* just in case */ break; diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h index 67785f1ae46..7cf92a9051a 100644 --- a/source/blender/editors/mask/mask_intern.h +++ b/source/blender/editors/mask/mask_intern.h @@ -39,7 +39,7 @@ bool ED_mask_find_nearest_diff_point(const struct bContext *C, float tangent[2], const bool use_deform, const bool use_project, - struct MaskLayer **masklay_r, + struct MaskLayer **mask_layer_r, struct MaskSpline **spline_r, struct MaskSplinePoint **point_r, float *u_r, @@ -76,7 +76,7 @@ bool ED_mask_feather_find_nearest(const struct bContext *C, struct Mask *mask, const float normal_co[2], const float threshold, - struct MaskLayer **masklay_r, + struct MaskLayer **mask_layer_r, struct MaskSpline **spline_r, struct MaskSplinePoint **point_r, struct MaskSplinePointUW **uw_r, @@ -86,7 +86,7 @@ struct MaskSplinePoint *ED_mask_point_find_nearest(const struct bContext *C, struct Mask *mask, const float normal_co[2], const float threshold, - struct MaskLayer **masklay_r, + struct MaskLayer **mask_layer_r, struct MaskSpline **spline_r, eMaskWhichHandle *which_handle_r, float *score); @@ -114,11 +114,11 @@ void MASK_OT_select_more(struct wmOperatorType *ot); void MASK_OT_select_less(struct wmOperatorType *ot); bool ED_mask_spline_select_check(struct MaskSpline *spline); -bool ED_mask_layer_select_check(struct MaskLayer *masklay); +bool ED_mask_layer_select_check(struct MaskLayer *mask_layer); bool ED_mask_select_check(struct Mask *mask); void ED_mask_spline_select_set(struct MaskSpline *spline, const bool do_select); -void ED_mask_layer_select_set(struct MaskLayer *masklay, const bool do_select); +void ED_mask_layer_select_set(struct MaskLayer *mask_layer, const bool do_select); void ED_mask_select_toggle_all(struct Mask *mask, int action); void ED_mask_select_flush_all(struct Mask *mask); diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index c91f431ad20..3a982b255f0 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -69,7 +69,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask_orig, const float normal_co[2], const float threshold, - MaskLayer **masklay_r, + MaskLayer **mask_layer_r, MaskSpline **spline_r, eMaskWhichHandle *which_handle_r, float *score) @@ -77,7 +77,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - MaskLayer *point_masklay = NULL; + MaskLayer *point_mask_layer = NULL; MaskSpline *point_spline = NULL; MaskSplinePoint *point = NULL; float co[2]; @@ -95,17 +95,17 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, co[0] = normal_co[0] * scalex; co[1] = normal_co[1] * scaley; - for (MaskLayer *masklay_orig = mask_orig->masklayers.first, - *masklay_eval = mask_eval->masklayers.first; - masklay_orig != NULL; - masklay_orig = masklay_orig->next, masklay_eval = masklay_eval->next) { + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { - if (masklay_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (MaskSpline *spline_orig = masklay_orig->splines.first, - *spline_eval = masklay_eval->splines.first; + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; spline_orig != NULL; spline_orig = spline_orig->next, spline_eval = spline_eval->next) { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval); @@ -124,7 +124,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, if (cur_len_sq < len_sq) { point_spline = spline_orig; - point_masklay = masklay_orig; + point_mask_layer = mask_layer_orig; point = cur_point_orig; len_sq = cur_len_sq; which_handle = MASK_WHICH_HANDLE_NONE; @@ -174,7 +174,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, } if (cur_len_sq <= len_sq && cur_which_handle != MASK_WHICH_HANDLE_NONE) { - point_masklay = masklay_orig; + point_mask_layer = mask_layer_orig; point_spline = spline_orig; point = cur_point_orig; len_sq = cur_len_sq; @@ -185,8 +185,8 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, } if (len_sq < threshold_sq) { - if (masklay_r) { - *masklay_r = point_masklay; + if (mask_layer_r) { + *mask_layer_r = point_mask_layer; } if (spline_r) { @@ -204,8 +204,8 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, return point; } - if (masklay_r) { - *masklay_r = NULL; + if (mask_layer_r) { + *mask_layer_r = NULL; } if (spline_r) { @@ -223,7 +223,7 @@ bool ED_mask_feather_find_nearest(const bContext *C, Mask *mask_orig, const float normal_co[2], const float threshold, - MaskLayer **masklay_r, + MaskLayer **mask_layer_r, MaskSpline **spline_r, MaskSplinePoint **point_r, MaskSplinePointUW **uw_r, @@ -232,7 +232,7 @@ bool ED_mask_feather_find_nearest(const bContext *C, ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); - MaskLayer *point_masklay = NULL; + MaskLayer *point_mask_layer = NULL; MaskSpline *point_spline = NULL; MaskSplinePoint *point = NULL; MaskSplinePointUW *uw = NULL; @@ -250,13 +250,13 @@ bool ED_mask_feather_find_nearest(const bContext *C, co[0] = normal_co[0] * scalex; co[1] = normal_co[1] * scaley; - for (MaskLayer *masklay_orig = mask_orig->masklayers.first, - *masklay_eval = mask_eval->masklayers.first; - masklay_orig != NULL; - masklay_orig = masklay_orig->next, masklay_eval = masklay_eval->next) { + for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first, + *mask_layer_eval = mask_eval->masklayers.first; + mask_layer_orig != NULL; + mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) { - for (MaskSpline *spline_orig = masklay_orig->splines.first, - *spline_eval = masklay_eval->splines.first; + for (MaskSpline *spline_orig = mask_layer_orig->splines.first, + *spline_eval = mask_layer_eval->splines.first; spline_orig != NULL; spline_orig = spline_orig->next, spline_eval = spline_eval->next) { // MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); @@ -264,7 +264,7 @@ bool ED_mask_feather_find_nearest(const bContext *C, int i, tot_feather_point; float(*feather_points)[2], (*fp)[2]; - if (masklay_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } @@ -291,7 +291,7 @@ bool ED_mask_feather_find_nearest(const bContext *C, uw = &cur_point_orig->uw[j - 1]; } - point_masklay = masklay_orig; + point_mask_layer = mask_layer_orig; point_spline = spline_orig; point = cur_point_orig; len = cur_len_sq; @@ -306,8 +306,8 @@ bool ED_mask_feather_find_nearest(const bContext *C, } if (len < threshold_sq) { - if (masklay_r) { - *masklay_r = point_masklay; + if (mask_layer_r) { + *mask_layer_r = point_mask_layer; } if (spline_r) { @@ -329,8 +329,8 @@ bool ED_mask_feather_find_nearest(const bContext *C, return true; } - if (masklay_r) { - *masklay_r = NULL; + if (mask_layer_r) { + *mask_layer_r = NULL; } if (spline_r) { @@ -428,9 +428,9 @@ void MASK_OT_new(wmOperatorType *ot) RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Name of new mask"); } -/******************** create new masklay *********************/ +/******************** create new mask layer *********************/ -static int masklay_new_exec(bContext *C, wmOperator *op) +static int mask_layer_new_exec(bContext *C, wmOperator *op) { Mask *mask = CTX_data_edit_mask(C); char name[MAX_ID_NAME - 2]; @@ -454,7 +454,7 @@ void MASK_OT_layer_new(wmOperatorType *ot) ot->idname = "MASK_OT_layer_new"; /* api callbacks */ - ot->exec = masklay_new_exec; + ot->exec = mask_layer_new_exec; ot->poll = ED_maskedit_poll; /* flags */ @@ -466,13 +466,13 @@ void MASK_OT_layer_new(wmOperatorType *ot) /******************** remove mask layer *********************/ -static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op)) +static int mask_layer_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay = BKE_mask_layer_active(mask); + MaskLayer *mask_layer = BKE_mask_layer_active(mask); - if (masklay) { - BKE_mask_layer_remove(mask, masklay); + if (mask_layer) { + BKE_mask_layer_remove(mask, mask_layer); WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); DEG_id_tag_update(&mask->id, ID_RECALC_COPY_ON_WRITE); @@ -489,7 +489,7 @@ void MASK_OT_layer_remove(wmOperatorType *ot) ot->idname = "MASK_OT_layer_remove"; /* api callbacks */ - ot->exec = masklay_remove_exec; + ot->exec = mask_layer_remove_exec; ot->poll = ED_maskedit_poll; /* flags */ @@ -511,7 +511,7 @@ typedef struct SlidePointData { short event_invoke_type; int action; Mask *mask; - MaskLayer *masklay; + MaskLayer *mask_layer; MaskSpline *spline, *orig_spline; MaskSplinePoint *point; MaskSplinePointUW *uw; @@ -555,7 +555,6 @@ static bool spline_under_mouse_get(const bContext *C, const float threshold = 19.0f; ScrArea *sa = CTX_wm_area(C); SpaceClip *sc = CTX_wm_space_clip(C); - MaskLayer *mask_layer; int width, height; float pixel_co[2]; float closest_dist_squared = 0.0f; @@ -570,13 +569,13 @@ static bool spline_under_mouse_get(const bContext *C, if (sc != NULL) { undistort = (sc->clip != NULL) && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0; } - for (mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { - MaskSpline *spline; + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; + mask_layer = mask_layer->next) { if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { continue; } - for (spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) { MaskSplinePoint *points_array; float min[2], max[2], center[2]; float dist_squared; @@ -719,7 +718,7 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * Mask *mask = CTX_data_edit_mask(C); SlidePointData *customdata = NULL; - MaskLayer *masklay, *cv_masklay, *feather_masklay; + MaskLayer *mask_layer, *cv_mask_layer, *feather_mask_layer; MaskSpline *spline, *cv_spline, *feather_spline; MaskSplinePoint *point, *cv_point, *feather_point; MaskSplinePointUW *uw = NULL; @@ -733,13 +732,13 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * ED_mask_get_size(sa, &width, &height); cv_point = ED_mask_point_find_nearest( - C, mask, co, threshold, &cv_masklay, &cv_spline, &which_handle, &cv_score); + C, mask, co, threshold, &cv_mask_layer, &cv_spline, &which_handle, &cv_score); if (ED_mask_feather_find_nearest(C, mask, co, threshold, - &feather_masklay, + &feather_mask_layer, &feather_spline, &feather_point, &uw, @@ -747,7 +746,7 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * if (slide_feather || !cv_point || feather_score < cv_score) { action = SLIDE_ACTION_FEATHER; - masklay = feather_masklay; + mask_layer = feather_mask_layer; spline = feather_spline; point = feather_point; } @@ -761,13 +760,13 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * action = SLIDE_ACTION_POINT; } - masklay = cv_masklay; + mask_layer = cv_mask_layer; spline = cv_spline; point = cv_point; } if (action == SLIDE_ACTION_NONE) { - if (spline_under_mouse_get(C, mask, co, &masklay, &spline)) { + if (spline_under_mouse_get(C, mask, co, &mask_layer, &spline)) { action = SLIDE_ACTION_SPLINE; point = NULL; } @@ -777,7 +776,7 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data"); customdata->event_invoke_type = event->type; customdata->mask = mask; - customdata->masklay = masklay; + customdata->mask_layer = mask_layer; customdata->spline = spline; customdata->point = point; customdata->width = width; @@ -790,7 +789,7 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * if (customdata->action != SLIDE_ACTION_SPLINE) { customdata->old_h1 = point->bezt.h1; customdata->old_h2 = point->bezt.h2; - select_sliding_point(mask, masklay, spline, point, which_handle); + select_sliding_point(mask, mask_layer, spline, point, which_handle); check_sliding_handle_type(point, which_handle); } @@ -850,8 +849,8 @@ static int slide_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_modal_handler(C, op); - slidedata->masklay->act_spline = slidedata->spline; - slidedata->masklay->act_point = slidedata->point; + slidedata->mask_layer->act_spline = slidedata->spline; + slidedata->mask_layer->act_point = slidedata->point; WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); @@ -1140,7 +1139,7 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event) /* dont key sliding feather uw's */ if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == false) { if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(data->masklay, CFRA); + ED_mask_layer_shape_auto_key(data->mask_layer, CFRA); } } @@ -1586,16 +1585,13 @@ void MASK_OT_slide_spline_curvature(wmOperatorType *ot) static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; - - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { if (ED_mask_spline_select_check(spline)) { spline->flag ^= MASK_SPLINE_CYCLIC; } @@ -1666,18 +1662,17 @@ static void delete_feather_points(MaskSplinePoint *point) static int delete_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { MaskSpline *spline; int mask_layer_shape_ofs = 0; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - spline = masklay->splines.first; + spline = mask_layer->splines.first; while (spline) { const int tot_point_orig = spline->tot_point; @@ -1695,15 +1690,15 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op)) if (count == 0) { /* delete the whole spline */ - BLI_remlink(&masklay->splines, spline); + BLI_remlink(&mask_layer->splines, spline); BKE_mask_spline_free(spline); - if (spline == masklay->act_spline) { - masklay->act_spline = NULL; - masklay->act_point = NULL; + if (spline == mask_layer->act_spline) { + mask_layer->act_spline = NULL; + mask_layer->act_point = NULL; } - BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig); + BKE_mask_layer_shape_changed_remove(mask_layer, mask_layer_shape_ofs, tot_point_orig); } else { MaskSplinePoint *new_points; @@ -1715,8 +1710,8 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op)) MaskSplinePoint *point = &spline->points[i]; if (!MASKPOINT_ISSEL_ANY(point)) { - if (point == masklay->act_point) { - masklay->act_point = &new_points[j]; + if (point == mask_layer->act_point) { + mask_layer->act_point = &new_points[j]; } delete_feather_points(point); @@ -1725,14 +1720,14 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op)) j++; } else { - if (point == masklay->act_point) { - masklay->act_point = NULL; + if (point == mask_layer->act_point) { + mask_layer->act_point = NULL; } BKE_mask_point_free(point); spline->tot_point--; - BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1); + BKE_mask_layer_shape_changed_remove(mask_layer, mask_layer_shape_ofs + j, 1); } } @@ -1750,8 +1745,8 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op)) /* not essential but confuses users when there are keys with no data! * assume if they delete all data from the layer they also dont care about keys */ - if (BLI_listbase_is_empty(&masklay->splines)) { - BKE_mask_layer_free_shapes(masklay); + if (BLI_listbase_is_empty(&mask_layer->splines)) { + BKE_mask_layer_free_shapes(mask_layer); } } @@ -1787,22 +1782,20 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; /* do actual selection */ - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { bool changed_layer = false; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { if (ED_mask_spline_select_check(spline)) { - BKE_mask_spline_direction_switch(masklay, spline); + BKE_mask_spline_direction_switch(mask_layer, spline); changed = true; changed_layer = true; } @@ -1810,7 +1803,7 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) if (changed_layer) { if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(masklay, CFRA); + ED_mask_layer_shape_auto_key(mask_layer, CFRA); } } } @@ -1847,22 +1840,19 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op) { Scene *scene = CTX_data_scene(C); Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; - int i; bool changed = false; /* do actual selection */ - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { bool changed_layer = false; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(point)) { @@ -1875,7 +1865,7 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op) if (changed_layer) { if (IS_AUTOKEY_ON(scene)) { - ED_mask_layer_shape_auto_key(masklay, CFRA); + ED_mask_layer_shape_auto_key(mask_layer, CFRA); } } } @@ -1913,21 +1903,17 @@ void MASK_OT_normals_make_consistent(wmOperatorType *ot) static int set_handle_type_exec(bContext *C, wmOperator *op) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; int handle_type = RNA_enum_get(op->ptr, "type"); bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - int i; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(point)) { @@ -1999,15 +1985,14 @@ void MASK_OT_handle_type_set(wmOperatorType *ot) static int mask_hide_view_clear_exec(bContext *C, wmOperator *op) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; const bool select = RNA_boolean_get(op->ptr, "select"); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - if (masklay->restrictflag & OB_RESTRICT_VIEWPORT) { - ED_mask_layer_select_set(masklay, select); - masklay->restrictflag &= ~OB_RESTRICT_VIEWPORT; + if (mask_layer->restrictflag & OB_RESTRICT_VIEWPORT) { + ED_mask_layer_select_set(mask_layer, select); + mask_layer->restrictflag &= ~OB_RESTRICT_VIEWPORT; changed = true; } } @@ -2044,32 +2029,31 @@ void MASK_OT_hide_view_clear(wmOperatorType *ot) static int mask_hide_view_set_exec(bContext *C, wmOperator *op) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; const bool unselected = RNA_boolean_get(op->ptr, "unselected"); bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - if (masklay->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { continue; } if (!unselected) { - if (ED_mask_layer_select_check(masklay)) { - ED_mask_layer_select_set(masklay, false); + if (ED_mask_layer_select_check(mask_layer)) { + ED_mask_layer_select_set(mask_layer, false); - masklay->restrictflag |= OB_RESTRICT_VIEWPORT; + mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT; changed = true; - if (masklay == BKE_mask_layer_active(mask)) { + if (mask_layer == BKE_mask_layer_active(mask)) { BKE_mask_layer_active_set(mask, NULL); } } } else { - if (!ED_mask_layer_select_check(masklay)) { - masklay->restrictflag |= OB_RESTRICT_VIEWPORT; + if (!ED_mask_layer_select_check(mask_layer)) { + mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT; changed = true; - if (masklay == BKE_mask_layer_active(mask)) { + if (mask_layer == BKE_mask_layer_active(mask)) { BKE_mask_layer_active_set(mask, NULL); } } @@ -2108,19 +2092,15 @@ void MASK_OT_hide_view_set(wmOperatorType *ot) static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; - int i; - - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(point)) { @@ -2247,12 +2227,9 @@ void MASK_OT_layer_move(wmOperatorType *ot) static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *mask_layer; - - for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - MaskSpline *spline; - for (spline = mask_layer->splines.last; spline; spline = spline->prev) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + for (MaskSpline *spline = mask_layer->splines.last; spline; spline = spline->prev) { MaskSplinePoint *point = spline->points; int i = 0; while (i < spline->tot_point) { diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c index 75b2aee3570..130ebb61a90 100644 --- a/source/blender/editors/mask/mask_relationships.c +++ b/source/blender/editors/mask/mask_relationships.c @@ -43,18 +43,14 @@ static int mask_parent_clear_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - int i; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(point)) { @@ -89,7 +85,6 @@ void MASK_OT_parent_clear(wmOperatorType *ot) static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; /* parent info */ SpaceClip *sc = CTX_wm_space_clip(C); @@ -141,16 +136,13 @@ static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - int i; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(point)) { diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index 2b3bd1b72c9..656c055a7d9 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -68,15 +68,13 @@ bool ED_mask_spline_select_check(MaskSpline *spline) return false; } -bool ED_mask_layer_select_check(MaskLayer *masklay) +bool ED_mask_layer_select_check(MaskLayer *mask_layer) { - MaskSpline *spline; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { return false; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { if (ED_mask_spline_select_check(spline)) { return true; } @@ -87,10 +85,8 @@ bool ED_mask_layer_select_check(MaskLayer *masklay) bool ED_mask_select_check(Mask *mask) { - MaskLayer *masklay; - - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - if (ED_mask_layer_select_check(masklay)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (ED_mask_layer_select_check(mask_layer)) { return true; } } @@ -117,25 +113,21 @@ void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select) } } -void ED_mask_layer_select_set(MaskLayer *masklay, const bool do_select) +void ED_mask_layer_select_set(MaskLayer *mask_layer, const bool do_select) { - MaskSpline *spline; - - if (masklay->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { if (do_select == true) { return; } } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { ED_mask_spline_select_set(spline, do_select); } } void ED_mask_select_toggle_all(Mask *mask, int action) { - MaskLayer *masklay; - if (action == SEL_TOGGLE) { if (ED_mask_select_check(mask)) { action = SEL_DESELECT; @@ -145,9 +137,9 @@ void ED_mask_select_toggle_all(Mask *mask, int action) } } - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - if (masklay->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { continue; } @@ -155,11 +147,10 @@ void ED_mask_select_toggle_all(Mask *mask, int action) /* we don't have generic functions for this, its restricted to this operator * if one day we need to re-use such functionality, they can be split out */ - MaskSpline *spline; - if (masklay->restrictflag & MASK_RESTRICT_SELECT) { + if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { int i; for (i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; @@ -168,30 +159,24 @@ void ED_mask_select_toggle_all(Mask *mask, int action) } } else { - ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? true : false); + ED_mask_layer_select_set(mask_layer, (action == SEL_SELECT) ? true : false); } } } void ED_mask_select_flush_all(Mask *mask) { - MaskLayer *masklay; - - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - - for (spline = masklay->splines.first; spline; spline = spline->next) { - int i; - + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { spline->flag &= ~SELECT; - /* intentionally _dont_ do this in the masklay loop + /* intentionally _dont_ do this in the mask layer loop * so we clear flags on all splines */ - if (masklay->restrictflag & MASK_RESTRICT_VIEW) { + if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) { continue; } - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *cur_point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(cur_point)) { @@ -270,7 +255,7 @@ void MASK_OT_select_all(wmOperatorType *ot) static int select_exec(bContext *C, wmOperator *op) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; + MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; float co[2]; @@ -284,7 +269,7 @@ static int select_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "location", co); point = ED_mask_point_find_nearest( - C, mask, co, threshold, &masklay, &spline, &which_handle, NULL); + C, mask, co, threshold, &mask_layer, &spline, &which_handle, NULL); if (extend == false && deselect == false && toggle == false) { ED_mask_select_toggle_all(mask, SEL_DESELECT); @@ -293,8 +278,8 @@ static int select_exec(bContext *C, wmOperator *op) if (point) { if (which_handle != MASK_WHICH_HANDLE_NONE) { if (extend) { - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; BKE_mask_point_select_set_handle(point, which_handle, true); } @@ -302,8 +287,8 @@ static int select_exec(bContext *C, wmOperator *op) BKE_mask_point_select_set_handle(point, which_handle, false); } else { - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; if (!MASKPOINT_ISSEL_HANDLE(point, which_handle)) { BKE_mask_point_select_set_handle(point, which_handle, true); @@ -315,8 +300,8 @@ static int select_exec(bContext *C, wmOperator *op) } else { if (extend) { - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; BKE_mask_point_select_set(point, true); } @@ -324,8 +309,8 @@ static int select_exec(bContext *C, wmOperator *op) BKE_mask_point_select_set(point, false); } else { - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; if (!MASKPOINT_ISSEL_ANY(point)) { BKE_mask_point_select_set(point, true); @@ -336,8 +321,8 @@ static int select_exec(bContext *C, wmOperator *op) } } - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; ED_mask_select_flush_all(mask); @@ -350,11 +335,11 @@ static int select_exec(bContext *C, wmOperator *op) MaskSplinePointUW *uw; if (ED_mask_feather_find_nearest( - C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) { + C, mask, co, threshold, &mask_layer, &spline, &point, &uw, NULL)) { if (extend) { - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; if (uw) { uw->flag |= SELECT; @@ -366,8 +351,8 @@ static int select_exec(bContext *C, wmOperator *op) } } else { - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; if (uw) { if (!(uw->flag & SELECT)) { @@ -454,8 +439,6 @@ static int box_select_exec(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; - int i; rcti rect; rctf rectf; @@ -475,17 +458,15 @@ static int box_select_exec(bContext *C, wmOperator *op) ED_mask_point_pos(sa, ar, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax); /* do actual selection */ - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; MaskSplinePoint *point_deform = &points_array[i]; @@ -548,8 +529,6 @@ static bool do_lasso_select_mask(bContext *C, ARegion *ar = CTX_wm_region(C); Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; - int i; rcti rect; bool changed = false; @@ -564,17 +543,15 @@ static bool do_lasso_select_mask(bContext *C, BLI_lasso_boundbox(&rect, mcords, moves); /* do actual selection */ - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; MaskSplinePoint *point_deform = &points_array[i]; @@ -678,7 +655,6 @@ static int circle_select_exec(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; int i; float zoomx, zoomy, offset[2], ellipse[2]; @@ -709,14 +685,12 @@ static int circle_select_exec(bContext *C, wmOperator *op) } /* do actual selection */ - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); for (i = 0; i < spline->tot_point; i++) { @@ -778,7 +752,7 @@ static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE ARegion *ar = CTX_wm_region(C); Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; + MaskLayer *mask_layer; MaskSpline *spline; MaskSplinePoint *point = NULL; float co[2]; @@ -788,12 +762,12 @@ static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE ED_mask_mouse_pos(sa, ar, event->mval, co); - point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, NULL, NULL); + point = ED_mask_point_find_nearest(C, mask, co, threshold, &mask_layer, &spline, NULL, NULL); if (point) { ED_mask_spline_select_set(spline, do_select); - masklay->act_spline = spline; - masklay->act_point = point; + mask_layer->act_spline = spline; + mask_layer->act_point = point; changed = true; } @@ -836,19 +810,16 @@ void MASK_OT_select_linked_pick(wmOperatorType *ot) static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; /* do actual selection */ - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { if (ED_mask_spline_select_check(spline)) { ED_mask_spline_select_set(spline, true); changed = true; @@ -892,16 +863,13 @@ void MASK_OT_select_linked(wmOperatorType *ot) static int mask_select_more_less(bContext *C, bool more) { Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; - - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - for (spline = masklay->splines.first; spline; spline = spline->next) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { const bool cyclic = (spline->flag & MASK_SPLINE_CYCLIC) != 0; bool start_sel, end_sel, prev_sel, cur_sel; int i; diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index 1abe805192d..2300289fd3b 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -51,18 +51,17 @@ static int mask_shape_key_insert_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); const int frame = CFRA; Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskLayerShape *masklay_shape; + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + MaskLayerShape *mask_layer_shape; - if (!ED_mask_layer_select_check(masklay)) { + if (!ED_mask_layer_select_check(mask_layer)) { continue; } - masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, frame); - BKE_mask_layer_shape_from_mask(masklay, masklay_shape); + mask_layer_shape = BKE_mask_layer_shape_verify_frame(mask_layer, frame); + BKE_mask_layer_shape_from_mask(mask_layer, mask_layer_shape); changed = true; } @@ -97,20 +96,19 @@ static int mask_shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); const int frame = CFRA; Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskLayerShape *masklay_shape; + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + MaskLayerShape *mask_layer_shape; - if (!ED_mask_layer_select_check(masklay)) { + if (!ED_mask_layer_select_check(mask_layer)) { continue; } - masklay_shape = BKE_mask_layer_shape_find_frame(masklay, frame); + mask_layer_shape = BKE_mask_layer_shape_find_frame(mask_layer, frame); - if (masklay_shape) { - BKE_mask_layer_shape_unlink(masklay, masklay_shape); + if (mask_layer_shape) { + BKE_mask_layer_shape_unlink(mask_layer, mask_layer_shape); changed = true; } } @@ -146,39 +144,36 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op) Scene *scene = CTX_data_scene(C); const int frame = CFRA; Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } - if (masklay->splines_shapes.first) { - MaskLayerShape *masklay_shape_reset; - MaskLayerShape *masklay_shape; + if (mask_layer->splines_shapes.first) { + MaskLayerShape *mask_layer_shape_reset; + MaskLayerShape *mask_layer_shape; /* get the shapekey of the current state */ - masklay_shape_reset = BKE_mask_layer_shape_alloc(masklay, frame); + mask_layer_shape_reset = BKE_mask_layer_shape_alloc(mask_layer, frame); /* initialize from mask - as if inseting a keyframe */ - BKE_mask_layer_shape_from_mask(masklay, masklay_shape_reset); + BKE_mask_layer_shape_from_mask(mask_layer, mask_layer_shape_reset); - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape->next) { - if (masklay_shape_reset->tot_vert == masklay_shape->tot_vert) { + if (mask_layer_shape_reset->tot_vert == mask_layer_shape->tot_vert) { int i_abs = 0; - int i; - MaskSpline *spline; MaskLayerShapeElem *shape_ele_src; MaskLayerShapeElem *shape_ele_dst; - shape_ele_src = (MaskLayerShapeElem *)masklay_shape_reset->data; - shape_ele_dst = (MaskLayerShapeElem *)masklay_shape->data; + shape_ele_src = (MaskLayerShapeElem *)mask_layer_shape_reset->data; + shape_ele_dst = (MaskLayerShapeElem *)mask_layer_shape->data; - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (MASKPOINT_ISSEL_ANY(point)) { @@ -200,7 +195,7 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op) changed = true; } - BKE_mask_layer_shape_free(masklay_shape_reset); + BKE_mask_layer_shape_free(mask_layer_shape_reset); } } @@ -243,98 +238,94 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const int frame = CFRA; Mask *mask = CTX_data_edit_mask(C); - MaskLayer *masklay; bool changed = false; const bool do_feather = RNA_boolean_get(op->ptr, "feather"); const bool do_location = RNA_boolean_get(op->ptr, "location"); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - - if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { continue; } /* we need at least one point selected here to bother re-interpolating */ - if (!ED_mask_layer_select_check(masklay)) { + if (!ED_mask_layer_select_check(mask_layer)) { continue; } - if (masklay->splines_shapes.first) { - MaskLayerShape *masklay_shape, *masklay_shape_next; - MaskLayerShape *masklay_shape_lastsel = NULL; + if (mask_layer->splines_shapes.first) { + MaskLayerShape *mask_layer_shape, *mask_layer_shape_next; + MaskLayerShape *mask_layer_shape_lastsel = NULL; - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape_next) { - MaskLayerShape *masklay_shape_a = NULL; - MaskLayerShape *masklay_shape_b = NULL; + for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape; + mask_layer_shape = mask_layer_shape_next) { + MaskLayerShape *mask_layer_shape_a = NULL; + MaskLayerShape *mask_layer_shape_b = NULL; - masklay_shape_next = masklay_shape->next; + mask_layer_shape_next = mask_layer_shape->next; /* find contiguous selections */ - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - if (masklay_shape_lastsel == NULL) { - masklay_shape_lastsel = masklay_shape; + if (mask_layer_shape->flag & MASK_SHAPE_SELECT) { + if (mask_layer_shape_lastsel == NULL) { + mask_layer_shape_lastsel = mask_layer_shape; } - if ((masklay_shape->next == NULL) || - (((MaskLayerShape *)masklay_shape->next)->flag & MASK_SHAPE_SELECT) == 0) { - masklay_shape_a = masklay_shape_lastsel; - masklay_shape_b = masklay_shape; - masklay_shape_lastsel = NULL; + if ((mask_layer_shape->next == NULL) || + (((MaskLayerShape *)mask_layer_shape->next)->flag & MASK_SHAPE_SELECT) == 0) { + mask_layer_shape_a = mask_layer_shape_lastsel; + mask_layer_shape_b = mask_layer_shape; + mask_layer_shape_lastsel = NULL; /* this will be freed below, step over selection */ - masklay_shape_next = masklay_shape->next; + mask_layer_shape_next = mask_layer_shape->next; } } /* we have a from<>to? - re-interpolate! */ - if (masklay_shape_a && masklay_shape_b) { + if (mask_layer_shape_a && mask_layer_shape_b) { ListBase shapes_tmp = {NULL, NULL}; - MaskLayerShape *masklay_shape_tmp; - MaskLayerShape *masklay_shape_tmp_next; - MaskLayerShape *masklay_shape_tmp_last = masklay_shape_b->next; - MaskLayerShape *masklay_shape_tmp_rekey; + MaskLayerShape *mask_layer_shape_tmp; + MaskLayerShape *mask_layer_shape_tmp_next; + MaskLayerShape *mask_layer_shape_tmp_last = mask_layer_shape_b->next; + MaskLayerShape *mask_layer_shape_tmp_rekey; /* move keys */ - for (masklay_shape_tmp = masklay_shape_a; - masklay_shape_tmp && (masklay_shape_tmp != masklay_shape_tmp_last); - masklay_shape_tmp = masklay_shape_tmp_next) { - masklay_shape_tmp_next = masklay_shape_tmp->next; - BLI_remlink(&masklay->splines_shapes, masklay_shape_tmp); - BLI_addtail(&shapes_tmp, masklay_shape_tmp); + for (mask_layer_shape_tmp = mask_layer_shape_a; + mask_layer_shape_tmp && (mask_layer_shape_tmp != mask_layer_shape_tmp_last); + mask_layer_shape_tmp = mask_layer_shape_tmp_next) { + mask_layer_shape_tmp_next = mask_layer_shape_tmp->next; + BLI_remlink(&mask_layer->splines_shapes, mask_layer_shape_tmp); + BLI_addtail(&shapes_tmp, mask_layer_shape_tmp); } /* re-key, note: cant modify the keys here since it messes uop */ - for (masklay_shape_tmp = shapes_tmp.first; masklay_shape_tmp; - masklay_shape_tmp = masklay_shape_tmp->next) { - BKE_mask_layer_evaluate(masklay, masklay_shape_tmp->frame, true); - masklay_shape_tmp_rekey = BKE_mask_layer_shape_verify_frame(masklay, - masklay_shape_tmp->frame); - BKE_mask_layer_shape_from_mask(masklay, masklay_shape_tmp_rekey); - masklay_shape_tmp_rekey->flag = masklay_shape_tmp->flag & MASK_SHAPE_SELECT; + for (mask_layer_shape_tmp = shapes_tmp.first; mask_layer_shape_tmp; + mask_layer_shape_tmp = mask_layer_shape_tmp->next) { + BKE_mask_layer_evaluate(mask_layer, mask_layer_shape_tmp->frame, true); + mask_layer_shape_tmp_rekey = BKE_mask_layer_shape_verify_frame( + mask_layer, mask_layer_shape_tmp->frame); + BKE_mask_layer_shape_from_mask(mask_layer, mask_layer_shape_tmp_rekey); + mask_layer_shape_tmp_rekey->flag = mask_layer_shape_tmp->flag & MASK_SHAPE_SELECT; } /* restore unselected points and free copies */ - for (masklay_shape_tmp = shapes_tmp.first; masklay_shape_tmp; - masklay_shape_tmp = masklay_shape_tmp_next) { + for (mask_layer_shape_tmp = shapes_tmp.first; mask_layer_shape_tmp; + mask_layer_shape_tmp = mask_layer_shape_tmp_next) { /* restore */ int i_abs = 0; - int i; - MaskSpline *spline; MaskLayerShapeElem *shape_ele_src; MaskLayerShapeElem *shape_ele_dst; - masklay_shape_tmp_next = masklay_shape_tmp->next; + mask_layer_shape_tmp_next = mask_layer_shape_tmp->next; /* we know this exists, added above */ - masklay_shape_tmp_rekey = BKE_mask_layer_shape_find_frame(masklay, - masklay_shape_tmp->frame); + mask_layer_shape_tmp_rekey = BKE_mask_layer_shape_find_frame( + mask_layer, mask_layer_shape_tmp->frame); - shape_ele_src = (MaskLayerShapeElem *)masklay_shape_tmp->data; - shape_ele_dst = (MaskLayerShapeElem *)masklay_shape_tmp_rekey->data; + shape_ele_src = (MaskLayerShapeElem *)mask_layer_shape_tmp->data; + shape_ele_dst = (MaskLayerShapeElem *)mask_layer_shape_tmp_rekey->data; - for (spline = masklay->splines.first; spline; spline = spline->next) { - for (i = 0; i < spline->tot_point; i++) { + for (MaskSpline *spline = mask_layer->splines.first; spline; spline = spline->next) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; /* not especially efficient but makes this easier to follow */ @@ -356,7 +347,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) } } - BKE_mask_layer_shape_free(masklay_shape_tmp); + BKE_mask_layer_shape_free(mask_layer_shape_tmp); } changed = true; @@ -364,7 +355,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op) } /* re-evaluate */ - BKE_mask_layer_evaluate(masklay, frame, true); + BKE_mask_layer_evaluate(mask_layer, frame, true); } } @@ -401,21 +392,20 @@ void MASK_OT_shape_key_rekey(wmOperatorType *ot) /* *** Shape Key Utils *** */ -void ED_mask_layer_shape_auto_key(MaskLayer *masklay, const int frame) +void ED_mask_layer_shape_auto_key(MaskLayer *mask_layer, const int frame) { - MaskLayerShape *masklay_shape; + MaskLayerShape *mask_layer_shape; - masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, frame); - BKE_mask_layer_shape_from_mask(masklay, masklay_shape); + mask_layer_shape = BKE_mask_layer_shape_verify_frame(mask_layer, frame); + BKE_mask_layer_shape_from_mask(mask_layer, mask_layer_shape); } bool ED_mask_layer_shape_auto_key_all(Mask *mask, const int frame) { - MaskLayer *masklay; bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - ED_mask_layer_shape_auto_key(masklay, frame); + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + ED_mask_layer_shape_auto_key(mask_layer, frame); changed = true; } @@ -424,16 +414,15 @@ bool ED_mask_layer_shape_auto_key_all(Mask *mask, const int frame) bool ED_mask_layer_shape_auto_key_select(Mask *mask, const int frame) { - MaskLayer *masklay; bool changed = false; - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - if (!ED_mask_layer_select_check(masklay)) { + if (!ED_mask_layer_select_check(mask_layer)) { continue; } - ED_mask_layer_shape_auto_key(masklay, frame); + ED_mask_layer_shape_auto_key(mask_layer, frame); changed = true; } diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c index bf4403e49ee..7cad7e1e062 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin.c @@ -200,7 +200,10 @@ void MESH_OT_spin(wmOperatorType *ot) /* props */ RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000); - RNA_def_boolean(ot->srna, "dupli", 0, "Duplicate", "Make Duplicates"); + + prop = RNA_def_boolean(ot->srna, "dupli", 0, "Use Duplicates", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 8d98a3bf231..25d3118b3a9 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_editmesh.h" #include "BKE_layer.h" +#include "BKE_library.h" #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_paint.h" @@ -179,7 +180,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - BKE_mesh_free(new_mesh); + BKE_id_free(bmain, new_mesh); new_mesh = BKE_mesh_from_bmesh_nomain(bm, (&(struct BMeshToMeshParams){ .calc_object_remap = false, @@ -190,7 +191,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) MEM_freeN(em); if (new_mesh->totvert == 0) { - BKE_mesh_free(new_mesh); + BKE_id_free(bmain, new_mesh); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 21c850160dd..a91f0f9274e 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -216,7 +216,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, } if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; - if (BM_vert_is_edge_pair(v_act)) { + if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) { BM_edge_collapse(bm, v_act->e, v_act, true, true); changed = true; } diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index a54701f8725..cbedb25ea64 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -118,7 +118,7 @@ static ScrArea *find_area_image_empty(bContext *C) for (sa = sc->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_IMAGE) { sima = sa->spacedata.first; - if (!sima->image) { + if ((sima->mode == SI_MODE_VIEW) && !sima->image) { break; } } @@ -205,7 +205,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ if (sa->full) { - sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE); + sa->flag |= AREA_FLAG_STACKED_FULLSCREEN; } } else { @@ -222,6 +222,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) } } sima = sa->spacedata.first; + sima->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; /* get the correct image, and scale it */ sima->image = BKE_image_verify_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 9957fe0515c..ccee88eb0d6 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1947,7 +1947,7 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi if (sa->spacetype != type) { SpaceType *st; - SpaceLink *slold; + SpaceLink *slold = sa->spacedata.first; SpaceLink *sl; /* store sa->type->exit callback */ void *sa_exit = sa->type ? sa->type->exit : NULL; @@ -1963,7 +1963,7 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi */ int header_alignment = ED_area_header_alignment_or_fallback(sa, -1); const bool sync_header_alignment = ((header_alignment != -1) && - (sa->flag & AREA_FLAG_TEMP_TYPE) == 0); + ((slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0)); /* in some cases (opening temp space) we don't want to * call area exit callback, so we temporarily unset it */ @@ -1979,7 +1979,6 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi } st = BKE_spacetype_from_id(type); - slold = sa->spacedata.first; sa->spacetype = type; sa->type = st; @@ -2010,6 +2009,10 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi slold->regionbase = sa->regionbase; sa->regionbase = sl->regionbase; BLI_listbase_clear(&sl->regionbase); + /* SPACE_FLAG_TYPE_WAS_ACTIVE is only used to go back to a previously active space that is + * overlapped by temporary ones. It's now properly activated, so the flag should be cleared + * at this point. */ + sl->link_flag &= ~SPACE_FLAG_TYPE_WAS_ACTIVE; /* put in front of list */ BLI_remlink(&sa->spacedata, sl); @@ -2073,23 +2076,45 @@ void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exi ED_area_tag_redraw(sa); } -void ED_area_prevspace(bContext *C, ScrArea *sa) +static SpaceLink *area_get_prevspace(ScrArea *sa) { SpaceLink *sl = sa->spacedata.first; - if (sl && sl->next) { - ED_area_newspace(C, sa, sl->next->spacetype, false); + /* First toggle to the next temporary space in the list. */ + for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) { + if (sl_iter->link_flag & SPACE_FLAG_TYPE_TEMPORARY) { + return sl_iter; + } + } - /* keep old spacedata but move it to end, so calling - * ED_area_prevspace once more won't open it again */ - BLI_remlink(&sa->spacedata, sl); - BLI_addtail(&sa->spacedata, sl); + /* No temporary space, find the item marked as last active. */ + for (SpaceLink *sl_iter = sl->next; sl_iter; sl_iter = sl_iter->next) { + if (sl_iter->link_flag & SPACE_FLAG_TYPE_WAS_ACTIVE) { + return sl_iter; + } + } + + /* If neither is found, we can just return to the regular previous one. */ + return sl->next; +} + +void ED_area_prevspace(bContext *C, ScrArea *sa) +{ + SpaceLink *sl = sa->spacedata.first; + SpaceLink *prevspace = sl ? area_get_prevspace(sa) : NULL; + + if (prevspace) { + ED_area_newspace(C, sa, prevspace->spacetype, false); + /* We've exited the space, so it can't be considered temporary anymore. */ + sl->link_flag &= ~SPACE_FLAG_TYPE_TEMPORARY; } else { /* no change */ return; } - sa->flag &= ~(AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE); + /* If this is a stacked fullscreen, changing to previous area exits it (meaning we're still in a + * fullscreen, but not in a stacked one). */ + sa->flag &= ~AREA_FLAG_STACKED_FULLSCREEN; ED_area_tag_redraw(sa); @@ -3337,13 +3362,17 @@ const rcti *ED_region_visible_rect(ARegion *ar) /* Cache display helpers */ -void ED_region_cache_draw_background(const ARegion *ar) +void ED_region_cache_draw_background(ARegion *ar) { + /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ + const rcti *rect_visible = ED_region_visible_rect(ar); + const int region_bottom = rect_visible->ymin; + uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4ub(128, 128, 255, 64); - immRecti(pos, 0, 0, ar->winx, 8 * UI_DPI_FAC); + immRecti(pos, 0, region_bottom, ar->winx, region_bottom + 8 * UI_DPI_FAC); immUnbindProgram(); } @@ -3373,9 +3402,13 @@ void ED_region_cache_draw_curfra_label(const int framenr, const float x, const f } void ED_region_cache_draw_cached_segments( - const ARegion *ar, const int num_segments, const int *points, const int sfra, const int efra) + ARegion *ar, const int num_segments, const int *points, const int sfra, const int efra) { if (num_segments) { + /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ + const rcti *rect_visible = ED_region_visible_rect(ar); + const int region_bottom = rect_visible->ymin; + uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -3385,7 +3418,7 @@ void ED_region_cache_draw_cached_segments( float x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * ar->winx; float x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * ar->winx; - immRecti(pos, x1, 0, x2, 8 * UI_DPI_FAC); + immRecti(pos, x1, region_bottom, x2, region_bottom + 8 * UI_DPI_FAC); /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */ } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index bbdddfadc30..5b8fd33a4e9 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1115,6 +1115,7 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) { wmWindow *win = CTX_wm_window(C); ScrArea *newsa = NULL; + SpaceLink *newsl; if (!sa || sa->full == NULL) { newsa = ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED); @@ -1125,15 +1126,14 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) } BLI_assert(newsa); + newsl = newsa->spacedata.first; - if (sa && (sa->spacetype != type)) { - newsa->flag |= AREA_FLAG_TEMP_TYPE; - } - else { - newsa->flag &= ~AREA_FLAG_TEMP_TYPE; + /* Tag the active space before changing, so we can identify it when user wants to go back. */ + if ((newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) { + newsl->link_flag |= SPACE_FLAG_TYPE_WAS_ACTIVE; } - ED_area_newspace(C, newsa, type, (newsa->flag & AREA_FLAG_TEMP_TYPE)); + ED_area_newspace(C, newsa, type, newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY); return newsa; } @@ -1146,7 +1146,7 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *sa) BLI_assert(sa->full); if (sa->flag & AREA_FLAG_STACKED_FULLSCREEN) { - /* stacked fullscreen -> only go back to previous screen and don't toggle out of fullscreen */ + /* stacked fullscreen -> only go back to previous area and don't toggle out of fullscreen */ ED_area_prevspace(C, sa); } else { @@ -1156,13 +1156,13 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *sa) void ED_screen_restore_temp_type(bContext *C, ScrArea *sa) { + SpaceLink *sl = sa->spacedata.first; + /* In case nether functions below run. */ ED_area_tag_redraw(sa); - if (sa->flag & AREA_FLAG_TEMP_TYPE) { + if (sl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) { ED_area_prevspace(C, sa); - /* Flag should be cleared now. */ - BLI_assert((sa->flag & AREA_FLAG_TEMP_TYPE) == 0); } if (sa->full) { @@ -1182,7 +1182,7 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) * overlaid on top of an existing setup) then return to the previous space */ if (sl->next) { - if (sa->flag & AREA_FLAG_TEMP_TYPE) { + if (sl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) { ED_screen_full_prevspace(C, sa); } else { @@ -1392,14 +1392,15 @@ ScrArea *ED_screen_temp_space_open(bContext *C, if (ctx_sa->full) { sa = ctx_sa; ED_area_newspace(C, ctx_sa, space_type, true); - /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ - sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE); + sa->flag |= AREA_FLAG_STACKED_FULLSCREEN; + ((SpaceLink *)sa->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; } else if (ctx_sa->spacetype == space_type) { sa = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_sa, SCREENMAXIMIZED); } else { sa = ED_screen_full_newspace(C, ctx_sa, (int)space_type); + ((SpaceLink *)sa->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY; } break; } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 9c95a3cee4d..a1e67e78a10 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -387,7 +387,12 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, m = mask; int aa_samples = 1.0f / (radius * 0.20f); - aa_samples = clamp_i(aa_samples, 3, 16); + if (brush->sampling_flag & BRUSH_PAINT_ANTIALIASING) { + aa_samples = clamp_i(aa_samples, 3, 16); + } + else { + aa_samples = 1; + } /* Temporal until we have the brush properties */ const float hardness = 1.0f; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 77c95c6acb3..ac7cf310099 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2341,7 +2341,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, ss->cache->location); + paint_last_stroke_update(scene, ss->cache->true_location); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -3316,7 +3316,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, ss->cache->location); + paint_last_stroke_update(scene, ss->cache->true_location); ED_region_tag_redraw(vc->ar); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 5d95cc80280..fc990c01bfb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -77,6 +77,7 @@ static UndoSculpt *sculpt_undo_get_nodes(void); static void update_cb(PBVHNode *node, void *rebuild) { BKE_pbvh_node_mark_update(node); + BKE_pbvh_node_mark_update_mask(node); if (*((bool *)rebuild)) { BKE_pbvh_node_mark_rebuild_draw(node); } @@ -497,7 +498,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; SculptUndoNode *unode; - bool update = false, rebuild = false; + bool update = false, rebuild = false, update_mask = false; bool need_mask = false; for (unode = lb->first; unode; unode = unode->next) { @@ -579,6 +580,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_MASK: if (sculpt_undo_restore_mask(C, unode)) { update = true; + update_mask = true; } break; @@ -616,6 +618,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase }; BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); + if (update_mask) { + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + } if (BKE_sculpt_multires_active(scene, ob)) { if (rebuild) { diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 192449a219d..4a4b85cbf8f 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1118,7 +1118,7 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event ARegion *ar = CTX_wm_region(C); if (ar->regiontype == RGN_TYPE_WINDOW) { - if (event->mval[1] > 16) { + if (event->mval[1] > 16 * UI_DPI_FAC) { return OPERATOR_PASS_THROUGH; } } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index d29233618de..1ea7d81f9da 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -338,7 +338,7 @@ enum { enum { SPECIAL_IMG_DOCUMENT = 0, - SPECIAL_IMG_UNSUPORTED = 1, + SPECIAL_IMG_DRIVE_DISC = 1, SPECIAL_IMG_FOLDER = 2, SPECIAL_IMG_PARENT = 3, SPECIAL_IMG_DRIVE_FIXED = 4, diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 91e5ab61dd9..95ad56a35e6 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -303,8 +303,10 @@ static void graph_main_region_draw(const bContext *C, ARegion *ar) } /* preview range */ - UI_view2d_view_ortho(v2d); - ANIM_draw_previewrange(C, v2d, 0); + if (sipo->mode != SIPO_MODE_DRIVERS) { + UI_view2d_view_ortho(v2d); + ANIM_draw_previewrange(C, v2d, 0); + } /* callback */ UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 9a2b0d95c20..2d4ca6dc15a 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -901,6 +901,10 @@ void draw_image_cache(const bContext *C, ARegion *ar) mask = ED_space_image_get_mask(sima); } + /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ + const rcti *rect_visible = ED_region_visible_rect(ar); + const int region_bottom = rect_visible->ymin; + GPU_blend(true); GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); @@ -928,10 +932,10 @@ void draw_image_cache(const bContext *C, ARegion *ar) immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformThemeColor(TH_CFRAME); - immRecti(pos, x, 0, x + ceilf(framelen), 8 * UI_DPI_FAC); + immRecti(pos, x, region_bottom, x + ceilf(framelen), region_bottom + 8 * UI_DPI_FAC); immUnbindProgram(); - ED_region_cache_draw_curfra_label(cfra, x, 8.0f * UI_DPI_FAC); + ED_region_cache_draw_curfra_label(cfra, x, region_bottom + 8.0f * UI_DPI_FAC); if (mask != NULL) { ED_mask_draw_frames(mask, ar, cfra, sfra, efra); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index a8dfad85232..8d17b703449 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3886,7 +3886,12 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event if (ar->regiontype == RGN_TYPE_WINDOW) { SpaceImage *sima = CTX_wm_space_image(C); - if (event->mval[1] > 16 || !ED_space_image_show_cache(sima)) { + + /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ + const rcti *rect_visible = ED_region_visible_rect(ar); + const int region_bottom = rect_visible->ymin; + + if (event->mval[1] > (region_bottom + 16 * UI_DPI_FAC) || !ED_space_image_show_cache(sima)) { return OPERATOR_PASS_THROUGH; } } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index 577c4e24b11..b6b32293cee 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -115,7 +115,7 @@ typedef struct PaintTile { ushort *mask; bool valid; bool use_float; - int x, y; + int x_tile, y_tile; } PaintTile; static void ptile_free(PaintTile *ptile) @@ -154,7 +154,7 @@ void *ED_image_paint_tile_find(ListBase *paint_tiles, bool validate) { for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) { - if (ptile->x == x_tile && ptile->y == y_tile) { + if (ptile->x_tile == x_tile && ptile->y_tile == y_tile) { if (ptile->image == image && ptile->ibuf == ibuf) { if (r_mask) { /* allocate mask if requested. */ @@ -206,8 +206,8 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles, ptile->image = image; ptile->ibuf = ibuf; - ptile->x = x_tile; - ptile->y = y_tile; + ptile->x_tile = x_tile; + ptile->y_tile = y_tile; /* add mask explicitly here */ if (r_mask) { @@ -269,8 +269,14 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) SWAP(uint *, ptile->rect.uint, tmpibuf->rect); } - IMB_rectcpy( - ibuf, tmpibuf, ptile->x, ptile->y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); + IMB_rectcpy(ibuf, + tmpibuf, + ptile->x_tile * ED_IMAGE_UNDO_TILE_SIZE, + ptile->y_tile * ED_IMAGE_UNDO_TILE_SIZE, + 0, + 0, + ED_IMAGE_UNDO_TILE_SIZE, + ED_IMAGE_UNDO_TILE_SIZE); if (has_float) { SWAP(float *, ptile->rect.fp, tmpibuf->rect_float); @@ -764,7 +770,7 @@ static bool image_undosys_step_encode(struct bContext *C, utile->users = 1; utile->rect.pt = ptile->rect.pt; ptile->rect.pt = NULL; - const uint tile_index = index_from_xy(ptile->x, ptile->y, ubuf_pre->tiles_dims); + const uint tile_index = index_from_xy(ptile->x_tile, ptile->y_tile, ubuf_pre->tiles_dims); BLI_assert(ubuf_pre->tiles[tile_index] == NULL); ubuf_pre->tiles[tile_index] = utile; diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 14817e9ffa1..124e3c08165 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -211,6 +211,7 @@ static const EnumPropertyItem unpack_all_method_items[] = { "Write files to original location (overwrite existing files)", ""}, {PF_KEEP, "KEEP", 0, "Disable Auto-pack, keep all packed files", ""}, + {PF_REMOVE, "REMOVE", 0, "Remove Pack", ""}, /* {PF_ASK, "ASK", 0, "Ask for each file", ""}, */ {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index c5271e9df06..cb6275e3f44 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -756,14 +756,15 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) } case TSE_BONE: { - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + bArmature *arm = (bArmature *)tselem->id; Bone *bone = te->directdata; char newname[sizeof(bone->name)]; /* always make current object active */ - tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true); + tree_element_active(C, &tvc, soops, te, OL_SETSEL_NORMAL, true); /* restore bone name */ BLI_strncpy(newname, bone->name, sizeof(bone->name)); @@ -773,14 +774,15 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) break; } case TSE_POSE_CHANNEL: { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + Object *ob = (Object *)tselem->id; bPoseChannel *pchan = te->directdata; char newname[sizeof(pchan->name)]; /* always make current pose-bone active */ - tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true); + tree_element_active(C, &tvc, soops, te, OL_SETSEL_NORMAL, true); BLI_assert(ob->type == OB_ARMATURE); @@ -2819,8 +2821,7 @@ typedef struct MergedIconRow { static void outliner_draw_iconrow(bContext *C, uiBlock *block, const uiFontStyle *fstyle, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, ListBase *lb, int level, @@ -2831,7 +2832,6 @@ static void outliner_draw_iconrow(bContext *C, MergedIconRow *merged) { eOLDrawState active = OL_DRAWSEL_NONE; - const Object *obact = OBACT(view_layer); for (TreeElement *te = lb->first; te; te = te->next) { TreeStoreElem *tselem = TREESTORE(te); @@ -2841,14 +2841,13 @@ static void outliner_draw_iconrow(bContext *C, /* active blocks get white circle */ if (tselem->type == 0) { if (te->idcode == ID_OB) { - active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : - OL_DRAWSEL_NONE; + active = (tvc->obact == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE; } - else if (is_object_data_in_editmode(tselem->id, obact)) { + else if (is_object_data_in_editmode(tselem->id, tvc->obact)) { active = OL_DRAWSEL_ACTIVE; } else { - active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false); + active = tree_element_active(C, tvc, soops, te, OL_SETSEL_NONE, false); } } else if (tselem->type == TSE_GP_LAYER) { @@ -2856,8 +2855,7 @@ static void outliner_draw_iconrow(bContext *C, active = (gpl->flag & GP_LAYER_ACTIVE) ? OL_DRAWSEL_ACTIVE : OL_DRAWSEL_NONE; } else { - active = tree_element_type_active( - C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false); + active = tree_element_type_active(C, tvc, soops, te, tselem, OL_SETSEL_NONE, false); } if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) { @@ -2878,8 +2876,7 @@ static void outliner_draw_iconrow(bContext *C, outliner_draw_iconrow(C, block, fstyle, - scene, - view_layer, + tvc, soops, &te->subtree, level + 1, @@ -2937,8 +2934,7 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta static void outliner_draw_tree_element(bContext *C, uiBlock *block, const uiFontStyle *fstyle, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, ARegion *ar, SpaceOutliner *soops, TreeElement *te, @@ -2977,9 +2973,8 @@ static void outliner_draw_tree_element(bContext *C, /* colors for active/selected data */ if (tselem->type == 0) { - const Object *obact = OBACT(view_layer); if (te->idcode == ID_SCE) { - if (tselem->id == (ID *)scene) { + if (tselem->id == (ID *)tvc->scene) { /* active scene */ icon_bgcolor[3] = 0.2f; active = OL_DRAWSEL_ACTIVE; @@ -2988,15 +2983,15 @@ static void outliner_draw_tree_element(bContext *C, else if (te->idcode == ID_OB) { Object *ob = (Object *)tselem->id; Base *base = (te->directdata) ? (Base *)te->directdata : - BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_find(tvc->view_layer, ob); const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0); - if (ob == obact) { + if (ob == tvc->obact) { active = OL_DRAWSEL_ACTIVE; } if (is_selected) { - if (ob == obact) { + if (ob == tvc->obact) { /* active selected object */ UI_GetThemeColor3ubv(TH_ACTIVE_OBJECT, text_color); text_color[3] = 255; @@ -3008,14 +3003,14 @@ static void outliner_draw_tree_element(bContext *C, } } } - else if (is_object_data_in_editmode(tselem->id, obact)) { + else if (is_object_data_in_editmode(tselem->id, tvc->obact)) { /* objects being edited */ UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_bgcolor); icon_border[3] = 0.3f; active = OL_DRAWSEL_ACTIVE; } else { - if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) { + if (tree_element_active(C, tvc, soops, te, OL_SETSEL_NONE, false)) { /* active items like camera or material */ icon_bgcolor[3] = 0.2f; active = OL_DRAWSEL_ACTIVE; @@ -3030,8 +3025,7 @@ static void outliner_draw_tree_element(bContext *C, } } else { - active = tree_element_type_active( - C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false); + active = tree_element_type_active(C, tvc, soops, te, tselem, OL_SETSEL_NONE, false); /* active collection*/ icon_bgcolor[3] = 0.2f; } @@ -3040,7 +3034,7 @@ static void outliner_draw_tree_element(bContext *C, if ((tselem->type == TSE_LAYER_COLLECTION) && (soops->show_restrict_flags & SO_RESTRICT_ENABLE)) { tselem_draw_layer_collection_enable_icon( - scene, block, xmax, (float)startx + offsx + UI_UNIT_X, (float)*starty, te, 0.8f); + tvc->scene, block, xmax, (float)startx + offsx + UI_UNIT_X, (float)*starty, te, 0.8f); offsx += UI_UNIT_X; } @@ -3159,8 +3153,7 @@ static void outliner_draw_tree_element(bContext *C, outliner_draw_iconrow(C, block, fstyle, - scene, - view_layer, + tvc, soops, &te->subtree, 0, @@ -3190,8 +3183,7 @@ static void outliner_draw_tree_element(bContext *C, outliner_draw_tree_element(C, block, fstyle, - scene, - view_layer, + tvc, ar, soops, ten, @@ -3490,8 +3482,7 @@ static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int star static void outliner_draw_tree(bContext *C, uiBlock *block, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, ARegion *ar, SpaceOutliner *soops, const float restrict_column_width, @@ -3537,8 +3528,7 @@ static void outliner_draw_tree(bContext *C, outliner_draw_tree_element(C, block, fstyle, - scene, - view_layer, + tvc, ar, soops, te, @@ -3629,15 +3619,16 @@ static void outliner_update_viewable_area(ARegion *ar, void draw_outliner(const bContext *C) { Main *mainvar = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; SpaceOutliner *soops = CTX_wm_space_outliner(C); uiBlock *block; TreeElement *te_edit = NULL; - outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + + outliner_build_tree(mainvar, tvc.scene, tvc.view_layer, soops, ar); // always /* If global sync select is dirty, flag other outliners */ if (ED_outliner_select_sync_is_dirty(C)) { @@ -3659,8 +3650,7 @@ void draw_outliner(const bContext *C) const float restrict_column_width = outliner_restrict_columns_width(soops); outliner_back(ar); block = UI_block_begin(C, ar, __func__, UI_EMBOSS); - outliner_draw_tree( - (bContext *)C, block, scene, view_layer, ar, soops, restrict_column_width, &te_edit); + outliner_draw_tree((bContext *)C, block, &tvc, ar, soops, restrict_column_width, &te_edit); /* Compute outliner dimensions after it has been drawn. */ int tree_width, tree_height; @@ -3686,7 +3676,8 @@ void draw_outliner(const bContext *C) /* draw restriction columns */ RestrictPropertiesActive props_active; memset(&props_active, 1, sizeof(RestrictPropertiesActive)); - outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &soops->tree, props_active); + outliner_draw_restrictbuts( + block, tvc.scene, tvc.view_layer, ar, soops, &soops->tree, props_active); } UI_block_emboss_set(block, UI_EMBOSS); diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index c55140db46f..34bbf3a2a30 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -1600,7 +1600,11 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *soops, List for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); - if (tselem->type == 0) { + if (ELEM(tselem->type, + 0, + TSE_SCENE_OBJECTS_BASE, + TSE_VIEW_COLLECTION_BASE, + TSE_LAYER_COLLECTION)) { if (te->idcode == ID_SCE) { if (tselem->id != (ID *)scene) { tselem->flag |= TSE_CLOSED; diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 95e37dea249..23c883c0db3 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -200,6 +200,25 @@ typedef enum { #define TSELEM_OPEN(telm, sv) \ ((telm->flag & TSE_CLOSED) == 0 || (SEARCHING_OUTLINER(sv) && (telm->flag & TSE_CHILDSEARCH))) +/** + * Container to avoid passing around these variables to many functions. + * Also so we can have one place to assing these variables. + */ +typedef struct TreeViewContext { + /* Scene level. */ + struct Scene *scene; + struct ViewLayer *view_layer; + + /* Object level. */ + /** Avoid OBACT macro everywhere. */ + Object *obact; + Object *ob_edit; + /** + * The pose object may not be the active object (when in weight paint mode). + * Checking this in draw loops isn't efficient, so set only once. */ + Object *ob_pose; +} TreeViewContext; + /* outliner_tree.c ----------------------------------------------- */ void outliner_free_tree(ListBase *tree); @@ -237,16 +256,14 @@ int tree_element_id_type_to_index(TreeElement *te); /* outliner_select.c -------------------------------------------- */ eOLDrawState tree_element_type_active(struct bContext *C, - struct Scene *scene, - struct ViewLayer *view_layer, + const TreeViewContext *tvc, struct SpaceOutliner *soops, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive); eOLDrawState tree_element_active(struct bContext *C, - struct Scene *scene, - struct ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, const eOLSetState set, @@ -267,7 +284,8 @@ void outliner_object_mode_toggle(struct bContext *C, void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem); -bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x); +bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x); +bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x); /* outliner_edit.c ---------------------------------------------- */ typedef void (*outliner_operation_cb)(struct bContext *C, @@ -457,6 +475,8 @@ void OUTLINER_OT_unhide_all(struct wmOperatorType *ot); /* outliner_utils.c ---------------------------------------------- */ +void outliner_viewcontext_init(const struct bContext *C, TreeViewContext *tvc); + TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, const ListBase *tree, float view_co_y); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 19bbb115788..c96f2f9956f 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -595,6 +595,7 @@ static eOLDrawState tree_element_active_posegroup(bContext *C, static eOLDrawState tree_element_active_posechannel(bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, + Object *ob_pose, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, @@ -651,7 +652,7 @@ static eOLDrawState tree_element_active_posechannel(bContext *C, } } else { - if (ob == OBACT(view_layer) && ob->pose) { + if (ob == ob_pose && ob->pose) { if (pchan->bone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; } @@ -1007,8 +1008,7 @@ static eOLDrawState tree_element_active_layer_collection(bContext *C, /* generic call for ID data check or make/check active in UI */ eOLDrawState tree_element_active(bContext *C, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, const eOLSetState set, @@ -1020,17 +1020,18 @@ eOLDrawState tree_element_active(bContext *C, * See #do_outliner_item_activate. */ case ID_OB: if (handle_all_types) { - return tree_element_set_active_object(C, scene, view_layer, soops, te, set, false); + return tree_element_set_active_object( + C, tvc->scene, tvc->view_layer, soops, te, set, false); } break; case ID_MA: - return tree_element_active_material(C, scene, view_layer, soops, te, set); + return tree_element_active_material(C, tvc->scene, tvc->view_layer, soops, te, set); case ID_WO: - return tree_element_active_world(C, scene, view_layer, soops, te, set); + return tree_element_active_world(C, tvc->scene, tvc->view_layer, soops, te, set); case ID_TXT: - return tree_element_active_text(C, scene, view_layer, soops, te, set); + return tree_element_active_text(C, tvc->scene, tvc->view_layer, soops, te, set); case ID_CA: - return tree_element_active_camera(C, scene, view_layer, soops, te, set); + return tree_element_active_camera(C, tvc->scene, tvc->view_layer, soops, te, set); } return OL_DRAWSEL_NONE; } @@ -1039,8 +1040,7 @@ eOLDrawState tree_element_active(bContext *C, * Generic call for non-id data to make/check active in UI */ eOLDrawState tree_element_type_active(bContext *C, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, TreeStoreElem *tselem, @@ -1049,41 +1049,42 @@ eOLDrawState tree_element_type_active(bContext *C, { switch (tselem->type) { case TSE_DEFGROUP: - return tree_element_active_defgroup(C, view_layer, te, tselem, set); + return tree_element_active_defgroup(C, tvc->view_layer, te, tselem, set); case TSE_BONE: - return tree_element_active_bone(C, view_layer, te, tselem, set, recursive); + return tree_element_active_bone(C, tvc->view_layer, te, tselem, set, recursive); case TSE_EBONE: - return tree_element_active_ebone(C, view_layer, te, tselem, set, recursive); + return tree_element_active_ebone(C, tvc->view_layer, te, tselem, set, recursive); case TSE_MODIFIER: - return tree_element_active_modifier(C, scene, view_layer, te, tselem, set); + return tree_element_active_modifier(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_LINKED_OB: if (set != OL_SETSEL_NONE) { - tree_element_set_active_object(C, scene, view_layer, soops, te, set, false); + tree_element_set_active_object(C, tvc->scene, tvc->view_layer, soops, te, set, false); } - else if (tselem->id == (ID *)OBACT(view_layer)) { + else if (tselem->id == (ID *)tvc->obact) { return OL_DRAWSEL_NORMAL; } break; case TSE_LINKED_PSYS: - return tree_element_active_psys(C, scene, te, tselem, set); + return tree_element_active_psys(C, tvc->scene, te, tselem, set); case TSE_POSE_BASE: - return tree_element_active_pose(C, scene, view_layer, te, tselem, set); + return tree_element_active_pose(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_POSE_CHANNEL: - return tree_element_active_posechannel(C, scene, view_layer, te, tselem, set, recursive); + return tree_element_active_posechannel( + C, tvc->scene, tvc->view_layer, tvc->ob_pose, te, tselem, set, recursive); case TSE_CONSTRAINT: - return tree_element_active_constraint(C, scene, view_layer, te, tselem, set); + return tree_element_active_constraint(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_R_LAYER: - return active_viewlayer(C, scene, view_layer, te, set); + return active_viewlayer(C, tvc->scene, tvc->view_layer, te, set); case TSE_POSEGRP: - return tree_element_active_posegroup(C, scene, view_layer, te, tselem, set); + return tree_element_active_posegroup(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_SEQUENCE: - return tree_element_active_sequence(C, scene, te, tselem, set); + return tree_element_active_sequence(C, tvc->scene, te, tselem, set); case TSE_SEQUENCE_DUP: - return tree_element_active_sequence_dup(scene, te, tselem, set); + return tree_element_active_sequence_dup(tvc->scene, te, tselem, set); case TSE_KEYMAP_ITEM: - return tree_element_active_keymap_item(C, scene, view_layer, te, tselem, set); + return tree_element_active_keymap_item(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_GP_LAYER: - return tree_element_active_gplayer(C, scene, te, tselem, set); + return tree_element_active_gplayer(C, tvc->scene, te, tselem, set); break; case TSE_VIEW_COLLECTION_BASE: return tree_element_active_master_collection(C, te, set); @@ -1109,14 +1110,15 @@ void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem) * Needed to run from operators accessed from a menu. */ static void do_outliner_item_activate_tree_element(bContext *C, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, TreeStoreElem *tselem, const bool extend, - const bool recursive) + const bool recursive, + const bool is_over_name_icons) { + bool do_activate_data = soops->flag & SO_SYNC_SELECT || is_over_name_icons; /* Always makes active object, except for some specific types. */ if (ELEM(tselem->type, TSE_SEQUENCE, @@ -1133,11 +1135,11 @@ static void do_outliner_item_activate_tree_element(bContext *C, else if (tselem->type == TSE_POSE_BASE) { /* Support pose mode toggle, keeping the active object as is. */ } - else if (soops->flag & SO_SYNC_SELECT) { + else if (do_activate_data) { /* Only activate when synced selection is enabled */ tree_element_set_active_object(C, - scene, - view_layer, + tvc->scene, + tvc->view_layer, soops, te, (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : @@ -1149,9 +1151,11 @@ static void do_outliner_item_activate_tree_element(bContext *C, outliner_element_activate(soops, tselem); if (tselem->type == 0) { // the lib blocks - /* editmode? */ - if (te->idcode == ID_SCE) { - if (scene != (Scene *)tselem->id) { + if (do_activate_data == false) { + /* Only select in outliner. */ + } + else if (te->idcode == ID_SCE) { + if (tvc->scene != (Scene *)tselem->id) { WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id); } } @@ -1161,7 +1165,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, if (extend) { int sel = BA_SELECT; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) { - Base *base = BKE_view_layer_base_find(view_layer, object); + Base *base = BKE_view_layer_base_find(tvc->view_layer, object); if (base && (base->flag & BASE_SELECTED)) { sel = BA_DESELECT; break; @@ -1170,7 +1174,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, FOREACH_COLLECTION_OBJECT_RECURSIVE_END; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) { - Base *base = BKE_view_layer_base_find(view_layer, object); + Base *base = BKE_view_layer_base_find(tvc->view_layer, object); if (base) { ED_object_base_select(base, sel); } @@ -1178,10 +1182,10 @@ static void do_outliner_item_activate_tree_element(bContext *C, FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } else { - BKE_view_layer_base_deselect_all(view_layer); + BKE_view_layer_base_deselect_all(tvc->view_layer); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) { - Base *base = BKE_view_layer_base_find(view_layer, object); + Base *base = BKE_view_layer_base_find(tvc->view_layer, object); /* Object may not be in this scene */ if (base != NULL) { if ((base->flag & BASE_SELECTED) == 0) { @@ -1192,15 +1196,15 @@ static void do_outliner_item_activate_tree_element(bContext *C, FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + DEG_id_tag_update(&tvc->scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, tvc->scene); } else if (OB_DATA_SUPPORT_EDITMODE(te->idcode)) { Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); if ((ob != NULL) && (ob->data == tselem->id)) { - Base *base = BKE_view_layer_base_find(view_layer, ob); + Base *base = BKE_view_layer_base_find(tvc->view_layer, ob); if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { - do_outliner_activate_obdata(C, scene, view_layer, base, extend); + do_outliner_activate_obdata(C, tvc->scene, tvc->view_layer, base, extend); } } } @@ -1209,18 +1213,12 @@ static void do_outliner_item_activate_tree_element(bContext *C, WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); } else { // rest of types - tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); + tree_element_active(C, tvc, soops, te, OL_SETSEL_NORMAL, false); } } - else if (soops->flag & SO_SYNC_SELECT) { - tree_element_type_active(C, - scene, - view_layer, - soops, - te, - tselem, - extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, - recursive); + else if (do_activate_data) { + tree_element_type_active( + C, tvc, soops, te, tselem, extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, recursive); } } @@ -1327,12 +1325,12 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, void outliner_item_do_activate_from_tree_element( bContext *C, TreeElement *te, TreeStoreElem *tselem, bool extend, bool recursive) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); - do_outliner_item_activate_tree_element( - C, scene, view_layer, soops, te, tselem, extend, recursive); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + + do_outliner_item_activate_tree_element(C, &tvc, soops, te, tselem, extend, recursive, false); } /** @@ -1347,7 +1345,6 @@ static int outliner_item_do_activate_from_cursor(bContext *C, const bool deselect_all) { ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te; float view_mval[2]; @@ -1371,8 +1368,6 @@ static int outliner_item_do_activate_from_cursor(bContext *C, return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } else { - ViewLayer *view_layer = CTX_data_view_layer(C); - /* The row may also contain children, if one is hovered we want this instead of current te */ bool merged_elements = false; TreeElement *activate_te = outliner_find_item_at_x_in_row( @@ -1390,9 +1385,14 @@ static int outliner_item_do_activate_from_cursor(bContext *C, do_outliner_range_select(C, soops, activate_te, extend); } else { + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + + const bool is_over_name_icons = outliner_item_is_co_over_name_icons(activate_te, + view_mval[0]); outliner_item_select(soops, activate_te, extend, extend); do_outliner_item_activate_tree_element( - C, scene, view_layer, soops, activate_te, activate_tselem, extend, false); + C, &tvc, soops, activate_te, activate_tselem, extend, false, is_over_name_icons); } changed = true; @@ -1505,17 +1505,6 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -/* Find if x coordinate is over an icon or name */ -static bool outliner_item_is_co_over_name_icons(TreeElement *te, float view_co_x) -{ - /* Special case: count area left of Scene Collection as empty space */ - bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ? - (view_co_x > te->xs + UI_UNIT_X) : - (view_co_x > te->xs); - - return outside_left && (view_co_x < te->xend); -} - static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { SpaceOutliner *soops = CTX_wm_space_outliner(C); diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c index 29c820bce92..745a527cc15 100644 --- a/source/blender/editors/space_outliner/outliner_sync.c +++ b/source/blender/editors/space_outliner/outliner_sync.c @@ -37,6 +37,7 @@ #include "BKE_context.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_object.h" #include "BKE_sequencer.h" #include "DEG_depsgraph.h" @@ -129,14 +130,14 @@ static void outliner_sync_select_from_outliner_set_types(bContext *C, SpaceOutliner *soops, SyncSelectTypes *sync_types) { - Object *obact = CTX_data_active_object(C); - Object *obedit = CTX_data_edit_object(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); const bool sequence_view = soops->outlinevis == SO_SEQUENCE; sync_types->object = !sequence_view; - sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE); - sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE); + sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE); + sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE); sync_types->sequence = sequence_view; } @@ -149,16 +150,16 @@ static bool outliner_sync_select_to_outliner_set_types(const bContext *C, SpaceOutliner *soops, SyncSelectTypes *sync_types) { - Object *obact = CTX_data_active_object(C); - Object *obedit = CTX_data_edit_object(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); const bool sequence_view = soops->outlinevis == SO_SEQUENCE; sync_types->object = !sequence_view && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_OBJECT); - sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE) && + sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE); - sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE) && + sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE); sync_types->sequence = sequence_view && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE); diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 5dfdf6f129b..c3984ab16fa 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -21,6 +21,8 @@ * \ingroup spoutliner */ +#include <string.h> + #include "BLI_utildefines.h" #include "DNA_action_types.h" @@ -30,6 +32,7 @@ #include "BKE_context.h" #include "BKE_outliner_treehash.h" #include "BKE_layer.h" +#include "BKE_object.h" #include "ED_armature.h" #include "ED_outliner.h" @@ -39,6 +42,33 @@ #include "outliner_intern.h" +/* -------------------------------------------------------------------- */ +/** \name Tree View Context + * \{ */ + +void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc) +{ + memset(tvc, 0, sizeof(*tvc)); + + /* Scene level. */ + tvc->scene = CTX_data_scene(C); + tvc->view_layer = CTX_data_view_layer(C); + + /* Objects. */ + tvc->obact = OBACT(tvc->view_layer); + if (tvc->obact != NULL) { + tvc->ob_edit = OBEDIT_FROM_OBACT(tvc->obact); + + if ((tvc->obact->type == OB_ARMATURE) || + /* This could be made into it's own function. */ + ((tvc->obact->type == OB_MESH) && tvc->obact->mode & OB_MODE_WEIGHT_PAINT)) { + tvc->ob_pose = BKE_object_pose_armature_get(tvc->obact); + } + } +} + +/** \} */ + /** * Try to find an item under y-coordinate \a view_co_y (view-space). * \note Recursive @@ -361,8 +391,19 @@ bool outliner_is_element_visible(const TreeElement *te) return true; } +/* Find if x coordinate is over an icon or name */ +bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x) +{ + /* Special case: count area left of Scene Collection as empty space */ + bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ? + (view_co_x > te->xs + UI_UNIT_X) : + (view_co_x > te->xs); + + return outside_left && (view_co_x < te->xend); +} + /* Find if x coordinate is over element disclosure toggle */ -bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x) { return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 1a788237e6e..5b2ea9d4793 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1767,8 +1767,8 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *ar) static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) { const Editing *ed = BKE_sequencer_editing_get(scene, false); - const int frame_sta = PSFRA; - const int frame_end = PEFRA + 1; + const int frame_sta = scene->r.sfra; + const int frame_end = scene->r.efra + 1; GPU_blend(true); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 865dfb45278..ed384cfc1a8 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -177,7 +177,7 @@ static void proxy_endjob(void *pjv) WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, pj->scene); } -static void seq_proxy_build_job(const bContext *C) +static void seq_proxy_build_job(const bContext *C, ReportList *reports) { wmJob *wm_job; ProxyJob *pj; @@ -216,8 +216,11 @@ static void seq_proxy_build_job(const bContext *C) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); SEQP_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { - BKE_sequencer_proxy_rebuild_context( + bool success = BKE_sequencer_proxy_rebuild_context( pj->main, pj->depsgraph, pj->scene, seq, file_list, &pj->queue); + if (!success) { + BKE_reportf(reports, RPT_ERROR, "Could not build proxy for strip %s", seq->name); + } } } SEQ_END; @@ -3608,10 +3611,10 @@ void SEQUENCER_OT_view_ghost_border(wmOperatorType *ot) /* rebuild_proxy operator */ static int sequencer_rebuild_proxy_invoke(bContext *C, - wmOperator *UNUSED(op), + wmOperator *op, const wmEvent *UNUSED(event)) { - seq_proxy_build_job(C); + seq_proxy_build_job(C, op->reports); return OPERATOR_FINISHED; } @@ -4181,3 +4184,67 @@ void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot) FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); } + +static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = BKE_sequencer_editing_get(scene, false); + Sequence *seq; + + int sfra = MAXFRAME; + int efra = -MAXFRAME; + bool selected = false; + const bool preview = RNA_boolean_get(op->ptr, "preview"); + + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (seq->flag & SELECT) { + selected = true; + sfra = min_ii(sfra, seq->startdisp); + efra = max_ii(efra, seq->enddisp - 1); + } + } + + if (!selected) { + BKE_report(op->reports, RPT_WARNING, "Select one or more strips"); + return OPERATOR_CANCELLED; + } + else if (efra < 0) { + BKE_report(op->reports, RPT_ERROR, "Can't set a negative range"); + return OPERATOR_CANCELLED; + } + + if (preview) { + scene->r.flag |= SCER_PRV_RANGE; + scene->r.psfra = max_ii(0, sfra); + scene->r.pefra = efra; + } + else { + scene->r.flag &= ~SCER_PRV_RANGE; + scene->r.sfra = max_ii(0, sfra); + scene->r.efra = efra; + } + + WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); + + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Set Range to Strips"; + ot->idname = "SEQUENCER_OT_set_range_to_strips"; + ot->description = "Set the frame range to the selected strips start and end"; + + /* api callbacks */ + ot->exec = sequencer_set_range_to_strips_exec; + ot->poll = sequencer_edit_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_boolean(ot->srna, "preview", false, "Preview", "Set the preview range instead"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 851d3b5f3aa..0a51578da3b 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -149,6 +149,8 @@ void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot); void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot); +void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot); + /* preview specific operators */ void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot); @@ -160,7 +162,7 @@ void SEQUENCER_OT_select_less(struct wmOperatorType *ot); void SEQUENCER_OT_select_linked(struct wmOperatorType *ot); void SEQUENCER_OT_select_linked_pick(struct wmOperatorType *ot); void SEQUENCER_OT_select_handles(struct wmOperatorType *ot); -void SEQUENCER_OT_select_active_side(struct wmOperatorType *ot); +void SEQUENCER_OT_select_side(struct wmOperatorType *ot); void SEQUENCER_OT_select_box(struct wmOperatorType *ot); void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot); void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index b0bb775de83..af03035d10f 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -88,6 +88,8 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_change_effect_type); WM_operatortype_append(SEQUENCER_OT_change_path); + WM_operatortype_append(SEQUENCER_OT_set_range_to_strips); + /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); WM_operatortype_append(SEQUENCER_OT_select); @@ -96,7 +98,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_select_linked_pick); WM_operatortype_append(SEQUENCER_OT_select_linked); WM_operatortype_append(SEQUENCER_OT_select_handles); - WM_operatortype_append(SEQUENCER_OT_select_active_side); + WM_operatortype_append(SEQUENCER_OT_select_side); WM_operatortype_append(SEQUENCER_OT_select_box); WM_operatortype_append(SEQUENCER_OT_select_grouped); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 4c20fc1707a..a51b08f7525 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -27,6 +27,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_math.h" #include "DNA_scene_types.h" @@ -81,7 +82,7 @@ static void select_surrounding_handles(Scene *scene, Sequence *test) /* XXX BRIN } } -/* used for mouse selection and for SEQUENCER_OT_select_active_side() */ +/* Used for mouse selection in SEQUENCER_OT_select. */ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int frame) { Sequence *seq; @@ -110,7 +111,43 @@ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int } } -/* used for mouse selection and for SEQUENCER_OT_select_active_side() */ +/* Used for mouse selection in SEQUENCER_OT_select_side. */ +static void select_active_side_range(ListBase *seqbase, + const int sel_side, + const int frame_ranges[MAXSEQ], + const int frame_ignore) +{ + Sequence *seq; + + for (seq = seqbase->first; seq; seq = seq->next) { + if (seq->machine < MAXSEQ) { + const int frame = frame_ranges[seq->machine]; + if (frame == frame_ignore) { + continue; + } + switch (sel_side) { + case SEQ_SIDE_LEFT: + if (frame > (seq->startdisp)) { + seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; + } + break; + case SEQ_SIDE_RIGHT: + if (frame < (seq->startdisp)) { + seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; + } + break; + case SEQ_SIDE_BOTH: + seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; + break; + } + } + } +} + +/* used for mouse selection in SEQUENCER_OT_select */ static void select_linked_time(ListBase *seqbase, Sequence *seq_link) { Sequence *seq; @@ -913,20 +950,39 @@ void SEQUENCER_OT_select_handles(wmOperatorType *ot) } /* select side operator */ -static int sequencer_select_active_side_exec(bContext *C, wmOperator *op) +static int sequencer_select_side_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *seq_act = BKE_sequencer_active_get(scene); - if (ed == NULL || seq_act == NULL) { - return OPERATOR_CANCELLED; + const int sel_side = RNA_enum_get(op->ptr, "side"); + const int frame_init = sel_side == SEQ_SIDE_LEFT ? INT_MIN : INT_MAX; + int frame_ranges[MAXSEQ]; + bool selected = false; + + copy_vn_i(frame_ranges, ARRAY_SIZE(frame_ranges), frame_init); + + for (Sequence *seq = ed->seqbasep->first; seq; seq = seq->next) { + if (UNLIKELY(seq->machine >= MAXSEQ)) { + continue; + } + int *frame_limit_p = &frame_ranges[seq->machine]; + if (seq->flag & SELECT) { + selected = true; + if (sel_side == SEQ_SIDE_LEFT) { + *frame_limit_p = max_ii(*frame_limit_p, seq->startdisp); + } + else { + *frame_limit_p = min_ii(*frame_limit_p, seq->startdisp); + } + } } - seq_act->flag |= SELECT; + if (selected == false) { + return OPERATOR_CANCELLED; + } - select_active_side( - ed->seqbasep, RNA_enum_get(op->ptr, "side"), seq_act->machine, seq_act->startdisp); + select_active_side_range(ed->seqbasep, sel_side, frame_ranges, frame_init); ED_outliner_select_sync_from_sequence_tag(C); @@ -935,15 +991,15 @@ static int sequencer_select_active_side_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void SEQUENCER_OT_select_active_side(wmOperatorType *ot) +void SEQUENCER_OT_select_side(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Active Side"; - ot->idname = "SEQUENCER_OT_select_active_side"; - ot->description = "Select strips on the nominated side of the active strip"; + ot->name = "Select Side"; + ot->idname = "SEQUENCER_OT_select_side"; + ot->description = "Select strips on the nominated side of the selected strips"; /* api callbacks */ - ot->exec = sequencer_select_active_side_exec; + ot->exec = sequencer_select_side_exec; ot->poll = sequencer_edit_poll; /* flags */ @@ -955,7 +1011,7 @@ void SEQUENCER_OT_select_active_side(wmOperatorType *ot) prop_side_types, SEQ_SIDE_BOTH, "Side", - "The side of the handle that is selected"); + "The side to which the selection is applied"); } /* box_select operator */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index a5b7fac624d..e53957b054d 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -1183,6 +1183,10 @@ static int view3d_ruler_remove_invoke(bContext *C, wmOperator *op, const wmEvent else { ruler_item_remove(C, gzgroup, ruler_item); } + + /* Update the annotation layer. */ + view3d_ruler_to_gpencil(C, gzgroup); + ED_region_tag_redraw(ar); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index a66e76abc58..15208c1a7d2 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -379,6 +379,8 @@ void applyProject(TransInfo *t) .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = false, + .use_backface_culling = (t->scene->toolsettings->snap_flag & + SCE_SNAP_BACKFACE_CULLING) != 0, }, mval_fl, NULL, @@ -1364,6 +1366,8 @@ short snapObjectsTransform( .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, + .use_backface_culling = (t->scene->toolsettings->snap_flag & + SCE_SNAP_BACKFACE_CULLING) != 0, }, mval, t->tsnap.snapTarget, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index f35a2808f22..14e8b8f97e0 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -201,8 +201,12 @@ static SnapObjectData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext return *sod_p; } -typedef void (*IterSnapObjsCallback)( - SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data); +typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, + bool is_obedit, + bool use_backface_culling, + Object *ob, + float obmat[4][4], + void *data); /** * Walks through all objects in the scene to create the list of objects to snap. @@ -219,6 +223,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, const View3D *v3d = sctx->v3d_data.v3d; const eSnapSelect snap_select = params->snap_select; const bool use_object_edit_cage = params->use_object_edit_cage; + const bool use_backface_culling = params->use_backface_culling; Base *base_act = view_layer->basact; for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { @@ -250,12 +255,14 @@ static void iter_snap_objects(SnapObjectContext *sctx, DupliObject *dupli_ob; ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval); for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data); + sob_callback( + sctx, use_object_edit_cage, use_backface_culling, dupli_ob->ob, dupli_ob->mat, data); } free_object_duplilist(lb); } - sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data); + sob_callback( + sctx, use_object_edit_cage, use_backface_culling, obj_eval, obj_eval->obmat, data); } } @@ -350,6 +357,70 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH } } +static bool raycast_tri_backface_culling_test( + const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3]) +{ + cross_tri_v3(no, v0, v1, v2); + return dot_v3v3(no, dir) < 0.0f; +} + +/* Callback to raycast with backface culling (Mesh). */ +static void mesh_looptri_raycast_backface_culling_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata; + const MVert *vert = data->vert; + const MLoopTri *lt = &data->looptri[index]; + const float *vtri_co[3] = { + vert[data->loop[lt->tri[0]].v].co, + vert[data->loop[lt->tri[1]].v].co, + vert[data->loop[lt->tri[2]].v].co, + }; + float dist = bvhtree_ray_tri_intersection(ray, hit->dist, UNPACK3(vtri_co)); + + if (dist >= 0 && dist < hit->dist) { + float no[3]; + if (raycast_tri_backface_culling_test(ray->direction, UNPACK3(vtri_co), no)) { + hit->index = index; + hit->dist = dist; + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + normalize_v3_v3(hit->no, no); + } + } +} + +/* Callback to raycast with backface culling (EditMesh). */ +static void editmesh_looptri_raycast_backface_culling_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata; + BMEditMesh *em = data->em; + const BMLoop **ltri = (const BMLoop **)em->looptris[index]; + + const float *t0, *t1, *t2; + t0 = ltri[0]->v->co; + t1 = ltri[1]->v->co; + t2 = ltri[2]->v->co; + + { + float dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2); + + if (dist >= 0 && dist < hit->dist) { + float no[3]; + if (raycast_tri_backface_culling_test(ray->direction, t0, t1, t2, no)) { + hit->index = index; + hit->dist = dist; + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + normalize_v3_v3(hit->no, no); + } + } + } +} + static bool raycastMesh(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], @@ -358,6 +429,7 @@ static bool raycastMesh(SnapObjectContext *sctx, const float obmat[4][4], const unsigned int ob_index, bool use_hide, + bool use_backface_culling, /* read/write args */ float *ray_depth, /* return args */ @@ -494,7 +566,8 @@ static bool raycastMesh(SnapObjectContext *sctx, ray_normal_local, 0.0f, &hit, - treedata->raycast_callback, + use_backface_culling ? mesh_looptri_raycast_backface_culling_cb : + treedata->raycast_callback, treedata) != -1) { hit.dist += len_diff; hit.dist /= local_scale; @@ -530,6 +603,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, BMEditMesh *em, const float obmat[4][4], const unsigned int ob_index, + bool use_backface_culling, /* read/write args */ float *ray_depth, /* return args */ @@ -670,7 +744,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx, ray_normal_local, 0.0f, &hit, - treedata->raycast_callback, + use_backface_culling ? editmesh_looptri_raycast_backface_culling_cb : + treedata->raycast_callback, treedata) != -1) { hit.dist += len_diff; hit.dist /= local_scale; @@ -715,6 +790,7 @@ static bool raycastObj(SnapObjectContext *sctx, const unsigned int ob_index, bool use_obedit, bool use_occlusion_test, + bool use_backface_culling, /* read/write args */ float *ray_depth, /* return args */ @@ -753,6 +829,7 @@ static bool raycastObj(SnapObjectContext *sctx, em, obmat, ob_index, + use_backface_culling, ray_depth, r_loc, r_no, @@ -773,6 +850,7 @@ static bool raycastObj(SnapObjectContext *sctx, obmat, ob_index, use_hide, + use_backface_culling, ray_depth, r_loc, r_no, @@ -792,6 +870,7 @@ static bool raycastObj(SnapObjectContext *sctx, obmat, ob_index, false, + use_backface_culling, ray_depth, r_loc, r_no, @@ -832,8 +911,12 @@ struct RaycastObjUserData { bool ret; }; -static void raycast_obj_cb( - SnapObjectContext *sctx, bool use_obedit, Object *ob, float obmat[4][4], void *data) +static void raycast_obj_cb(SnapObjectContext *sctx, + bool use_obedit, + bool use_backface_culling, + Object *ob, + float obmat[4][4], + void *data) { struct RaycastObjUserData *dt = data; @@ -845,6 +928,7 @@ static void raycast_obj_cb( dt->ob_index++, use_obedit, dt->use_occlusion_test, + use_backface_culling, dt->ray_depth, dt->r_loc, dt->r_no, @@ -1088,8 +1172,6 @@ typedef void (*Nearest2DGetTriEdgesCallback)(const int index, int e_index[3], vo typedef void (*Nearest2DCopyVertNoCallback)(const int index, float r_no[3], void *data); typedef struct Nearest2dUserData { - bool is_persp; - void *userdata; Nearest2DGetVertCoCallback get_vert_co; Nearest2DGetEdgeVertsCallback get_edge_verts_index; @@ -1097,6 +1179,8 @@ typedef struct Nearest2dUserData { Nearest2DGetTriEdgesCallback get_tri_edges_index; Nearest2DCopyVertNoCallback copy_vert_no; + bool is_persp; + bool use_backface_culling; } Nearest2dUserData; static void cb_snap_vert(void *userdata, @@ -1181,6 +1265,20 @@ static void cb_snap_tri_edges(void *userdata, { struct Nearest2dUserData *data = userdata; + if (data->use_backface_culling) { + int vindex[3]; + data->get_tri_verts_index(index, vindex, data->userdata); + + const float *t0, *t1, *t2; + data->get_vert_co(vindex[0], &t0, data->userdata); + data->get_vert_co(vindex[1], &t1, data->userdata); + data->get_vert_co(vindex[2], &t2, data->userdata); + float dummy[3]; + if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) { + return; + } + } + int eindex[3]; data->get_tri_edges_index(index, eindex, data->userdata); for (int i = 3; i--;) { @@ -1204,6 +1302,18 @@ static void cb_snap_tri_verts(void *userdata, int vindex[3]; data->get_tri_verts_index(index, vindex, data->userdata); + + if (data->use_backface_culling) { + const float *t0, *t1, *t2; + data->get_vert_co(vindex[0], &t0, data->userdata); + data->get_vert_co(vindex[1], &t1, data->userdata); + data->get_vert_co(vindex[2], &t2, data->userdata); + float dummy[3]; + if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) { + return; + } + } + for (int i = 3; i--;) { if (vindex[i] == nearest->index) { continue; @@ -1222,6 +1332,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, SnapData *snapdata, Object *ob, const float obmat[4][4], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -1246,6 +1357,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, Nearest2dUserData nearest2d = { .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .use_backface_culling = use_backface_culling, }; BVHTreeNearest nearest = { @@ -1366,6 +1478,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, const float obmat[4][4], float original_dist_px, const float prev_co[3], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -1392,6 +1505,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, Nearest2dUserData nearest2d; { nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + nearest2d.use_backface_culling = use_backface_culling; if (sod->type == SNAP_MESH) { nearest2d.userdata = &((SnapObjectData_Mesh *)sod)->treedata; nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; @@ -1995,6 +2109,7 @@ static short snapMesh(SnapObjectContext *sctx, Object *ob, Mesh *me, const float obmat[4][4], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -2107,13 +2222,14 @@ static short snapMesh(SnapObjectContext *sctx, } Nearest2dUserData nearest2d = { - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, .userdata = treedata, .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get, .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get, .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get, .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get, .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy, + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .use_backface_culling = use_backface_culling, }; BVHTreeNearest nearest = { @@ -2233,6 +2349,7 @@ static short snapEditMesh(SnapObjectContext *sctx, Object *ob, BMEditMesh *em, const float obmat[4][4], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -2346,11 +2463,12 @@ static short snapEditMesh(SnapObjectContext *sctx, } Nearest2dUserData nearest2d = { - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, .userdata = em, .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get, .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get, .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy, + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .use_backface_culling = use_backface_culling, }; BVHTreeNearest nearest = { @@ -2436,6 +2554,7 @@ static short snapObject(SnapObjectContext *sctx, Object *ob, float obmat[4][4], bool use_obedit, + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -2453,7 +2572,8 @@ static short snapObject(SnapObjectContext *sctx, if (BKE_object_is_in_editmode(ob)) { BMEditMesh *em = BKE_editmesh_from_object(ob); if (use_obedit) { - retval = snapEditMesh(sctx, snapdata, ob, em, obmat, dist_px, r_loc, r_no, r_index); + retval = snapEditMesh( + sctx, snapdata, ob, em, obmat, use_backface_culling, dist_px, r_loc, r_no, r_index); break; } else if (em->mesh_eval_final) { @@ -2465,7 +2585,8 @@ static short snapObject(SnapObjectContext *sctx, return 0; } - retval = snapMesh(sctx, snapdata, ob, me, obmat, dist_px, r_loc, r_no, r_index); + retval = snapMesh( + sctx, snapdata, ob, me, obmat, use_backface_culling, dist_px, r_loc, r_no, r_index); break; } case OB_ARMATURE: @@ -2477,8 +2598,16 @@ static short snapObject(SnapObjectContext *sctx, case OB_SURF: case OB_FONT: { if (ob->runtime.mesh_eval) { - retval |= snapMesh( - sctx, snapdata, ob, ob->runtime.mesh_eval, obmat, dist_px, r_loc, r_no, r_index); + retval |= snapMesh(sctx, + snapdata, + ob, + ob->runtime.mesh_eval, + obmat, + use_backface_culling, + dist_px, + r_loc, + r_no, + r_index); } break; } @@ -2519,8 +2648,12 @@ struct SnapObjUserData { short ret; }; -static void sanp_obj_cb( - SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data) +static void sanp_obj_cb(SnapObjectContext *sctx, + bool is_obedit, + bool use_backface_culling, + Object *ob, + float obmat[4][4], + void *data) { struct SnapObjUserData *dt = data; @@ -2529,6 +2662,7 @@ static void sanp_obj_cb( ob, obmat, is_obedit, + use_backface_culling, /* read/write args */ dt->dist_px, /* return args */ @@ -2881,7 +3015,8 @@ static short transform_snap_context_project_view3d_mixed_impl( new_clipplane[3] += 0.01f; /* Try to snap only to the polygon. */ - elem_test = snap_mesh_polygon(sctx, &snapdata, ob, obmat, &dist_px_tmp, loc, no, &index); + elem_test = snap_mesh_polygon( + sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index); if (elem_test) { elem = elem_test; } @@ -2904,8 +3039,17 @@ static short transform_snap_context_project_view3d_mixed_impl( (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR))) { snapdata.snap_to_flag = snap_to_flag; - elem = snap_mesh_edge_verts_mixed( - sctx, &snapdata, ob, obmat, *dist_px, prev_co, &dist_px_tmp, loc, no, &index); + elem = snap_mesh_edge_verts_mixed(sctx, + &snapdata, + ob, + obmat, + *dist_px, + prev_co, + params->use_backface_culling, + &dist_px_tmp, + loc, + no, + &index); } if (elem & snap_to_flag) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 97a0cc85301..46095b7eedc 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -366,7 +366,7 @@ void IMB_anim_get_fname(struct anim *anim, char *file, int size) BLI_strncpy(file, fname, size); } -static void get_proxy_filename(struct anim *anim, +static bool get_proxy_filename(struct anim *anim, IMB_Proxy_Size preview_size, char *fname, bool temp) @@ -393,7 +393,12 @@ static void get_proxy_filename(struct anim *anim, get_index_dir(anim, index_dir, sizeof(index_dir)); + if (BLI_path_ncmp(anim->name, index_dir, FILE_MAXDIR) == 0) { + return false; + } + BLI_join_dirfile(fname, FILE_MAXFILE + FILE_MAXDIR, index_dir, proxy_name); + return true; } static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname) @@ -1154,8 +1159,9 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & proxy_sizes_to_build) { char filename[FILE_MAX]; - get_proxy_filename(anim, proxy_size, filename, false); - + if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + return NULL; + } void **filename_key_p; if (!BLI_gset_ensure_p_ex(file_list, filename, &filename_key_p)) { *filename_key_p = BLI_strdup(filename); @@ -1176,7 +1182,9 @@ IndexBuildContext *IMB_anim_index_rebuild_context(struct anim *anim, IMB_Proxy_Size proxy_size = proxy_sizes[i]; if (proxy_size & built_proxies) { char filename[FILE_MAX]; - get_proxy_filename(anim, proxy_size, filename, false); + if (get_proxy_filename(anim, proxy_size, filename, false) == false) { + return NULL; + } printf("Skipping proxy: %s\n", filename); } } diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 714c205cda2..b2d4124a348 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -34,6 +34,7 @@ { \ .blend = 0, \ .flag = (BRUSH_ALPHA_PRESSURE | BRUSH_SPACE | BRUSH_SPACE_ATTEN), \ + .sampling_flag = (BRUSH_PAINT_ANTIALIASING), \ \ .ob_mode = OB_MODE_ALL_PAINT, \ \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index fc8763f1519..63fbf576bba 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -245,8 +245,9 @@ typedef struct Brush { float weight; /** Brush diameter. */ int size; - /** General purpose flag. */ + /** General purpose flags. */ int flag; + int sampling_flag; /** Pressure influence for mask. */ int mask_pressure; /** Jitter the position of the brush. */ @@ -283,7 +284,7 @@ typedef struct Brush { /** Source for fill tool color gradient application. */ char gradient_fill_mode; - char _pad; + char _pad[5]; /** Projection shape (sphere, circle). */ char falloff_shape; float falloff_angle; @@ -435,6 +436,11 @@ typedef enum eBrushFlags { BRUSH_CURVE = (1u << 31), } eBrushFlags; +/* Brush.sampling_flag */ +typedef enum eBrushSamplingFlags { + BRUSH_PAINT_ANTIALIASING = (1 << 0), +} eBrushSamplingFlags; + typedef enum { BRUSH_MASK_PRESSURE_RAMP = (1 << 1), BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2), diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 1435d0a64b4..33dfe66a151 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -447,17 +447,16 @@ typedef struct bGPdata_Runtime { * - buffer must be initialized before use, but freed after * whole paint operation is over */ - /** Number of elements currently used in cache. */ - short sbuffer_used; /** Flags for stroke that cache represents. */ short sbuffer_sflag; + /** Number of elements currently used in cache. */ + int sbuffer_used; /** Number of total elements available in cache. */ - short sbuffer_size; - char _pad[4]; + int sbuffer_size; /** Number of control-points for stroke. */ int tot_cp_points; - char _pad1_[4]; + char _pad_[4]; /** Array of control-points for stroke. */ bGPDcontrolpoint *cp_points; } bGPdata_Runtime; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 1ebd19d6af1..81dc91950fb 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1119,7 +1119,13 @@ typedef struct SolidifyModifierData { float offset_fac_vg; /** Clamp offset based on surrounding geometry. */ float offset_clamp; - char _pad[4]; + char mode; + + /** Variables for #MOD_SOLIDIFY_MODE_NONMANIFOLD. */ + char nonmanifold_offset_mode; + char nonmanifold_boundary_mode; + + char _pad; float crease_inner; float crease_outer; float crease_rim; @@ -1128,6 +1134,7 @@ typedef struct SolidifyModifierData { short mat_ofs_rim; } SolidifyModifierData; +/** #SolidifyModifierData.flag */ enum { MOD_SOLIDIFY_RIM = (1 << 0), MOD_SOLIDIFY_EVEN = (1 << 1), @@ -1138,6 +1145,27 @@ enum { #endif MOD_SOLIDIFY_FLIP = (1 << 5), MOD_SOLIDIFY_NOSHELL = (1 << 6), + MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP = (1 << 7), +}; + +/** #SolidifyModifierData.mode */ +enum { + MOD_SOLIDIFY_MODE_EXTRUDE = 0, + MOD_SOLIDIFY_MODE_NONMANIFOLD = 1, +}; + +/** #SolidifyModifierData.nonmanifold_offset_mode */ +enum { + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_FIXED = 0, + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN = 1, + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS = 2, +}; + +/** #SolidifyModifierData.nonmanifold_boundary_mode */ +enum { + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE = 0, + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND = 1, + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT = 2, }; typedef struct ScrewModifierData { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3794508d5a2..2ca9e3b2ef7 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2035,6 +2035,7 @@ enum { #define SCE_SNAP_PROJECT (1 << 3) #define SCE_SNAP_NO_SELF (1 << 4) #define SCE_SNAP_ABS_GRID (1 << 5) +#define SCE_SNAP_BACKFACE_CULLING (1 << 6) /* ToolSettings.snap_target */ #define SCE_SNAP_TARGET_CLOSEST 0 diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index bf491e2eaea..ec42e9bd04f 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -461,9 +461,10 @@ enum { /** Update size of regions within the area. */ AREA_FLAG_REGION_SIZE_UPDATE = (1 << 3), AREA_FLAG_ACTIVE_TOOL_UPDATE = (1 << 4), + // AREA_FLAG_UNUSED_5 = (1 << 5), - /** Used to check if we should switch back to prevspace (of a different type). */ - AREA_FLAG_TEMP_TYPE = (1 << 6), + AREA_FLAG_UNUSED_6 = (1 << 6), /* cleared */ + /** * For temporary full-screens (file browser, image editor render) * that are opened above user set full-screens. diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 0f957a946d9..82f6da8bb46 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -79,6 +79,22 @@ typedef struct SpaceLink { char _pad0[6]; } SpaceLink; +/* SpaceLink.link_flag */ +enum { + /** + * The space is not a regular one opened through the editor menu (for example) but spawned by an + * operator to fulfill some task and then disappear again. Can typically be cancelled using Esc, + * but that is handled on the editor level. */ + SPACE_FLAG_TYPE_TEMPORARY = (1 << 0), + /** + * Used to mark a space as active but "overlapped" by temporary fullscreen spaces. Without this + * we wouldn't be able to restore the correct active space after closing temp fullscreens + * reliably if the same space type is opened twice in a fullscreen stack (see T19296). We don't + * actually open the same space twice, we have to pretend it is by managing area order carefully. + */ + SPACE_FLAG_TYPE_WAS_ACTIVE = (1 << 1), +}; + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 99c1bdfdbee..a392e4c080f 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2007,6 +2007,11 @@ static void rna_def_brush(BlenderRNA *brna) "Apply the maximum grab strength to the active vertex instead of the cursor location"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_paint_antialiasing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sampling_flag", BRUSH_PAINT_ANTIALIASING); + RNA_def_property_ui_text(prop, "Antialasing", "Smooths the edges of the strokes"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_pressure_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ALPHA_PRESSURE); RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index cc01554f73a..5890c3fe8a2 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3006,17 +3006,17 @@ static void rna_def_mesh(BlenderRNA *brna) "generating triangles. A value greater than 0 disables Fix Poles"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "remesh_smooth_normals", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_remesh_smooth_normals", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_SMOOTH_NORMALS); RNA_def_property_ui_text(prop, "Smooth Normals", "Smooth the normals of the remesher result"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "remesh_fix_poles", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_remesh_fix_poles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_FIX_POLES); RNA_def_property_ui_text(prop, "Fix Poles", "Produces less poles and a better topology flow"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "remesh_preserve_volume", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_remesh_preserve_volume", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VOLUME); RNA_def_property_ui_text( prop, @@ -3024,7 +3024,7 @@ static void rna_def_mesh(BlenderRNA *brna) "Projects the mesh to preserve the volume and details of the original mesh"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_PAINT_MASK); RNA_def_property_boolean_default(prop, false); RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh"); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index d8387cf4736..2fee003fd75 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4071,16 +4071,71 @@ static void rna_def_modifier_surface(BlenderRNA *brna) static void rna_def_modifier_solidify(BlenderRNA *brna) { + static const EnumPropertyItem mode_items[] = { + {MOD_SOLIDIFY_MODE_EXTRUDE, + "EXTRUDE", + 0, + "Simple", + "Output a solidified version of a mesh by simple extrusion"}, + {MOD_SOLIDIFY_MODE_NONMANIFOLD, + "NON_MANIFOLD", + 0, + "Complex", + "Output a manifold mesh even if the base mesh is non-manifold, " + "where edges have 3 or more connecting faces." + "This method is slower"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem nonmanifold_thickness_mode_items[] = { + {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_FIXED, + "FIXED", + 0, + "Fixed", + "Most basic thickness calculation"}, + {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN, + "EVEN", + 0, + "Even", + "Even thickness calculation which takes the angle between faces into account"}, + {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS, + "CONSTRAINTS", + 0, + "Constraints", + "Thickness calculation using constraints, most advanced"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem nonmanifold_boundary_mode_items[] = { + {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE, "NONE", 0, "None", "No shape correction"}, + {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND, + "ROUND", + 0, + "Round", + "Round open perimeter shape"}, + {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT, + "FLAT", + 0, + "Flat", + "Flat open perimeter shape"}, + {0, NULL, 0, NULL, NULL}, + }; + StructRNA *srna; PropertyRNA *prop; srna = RNA_def_struct(brna, "SolidifyModifier", "Modifier"); - RNA_def_struct_ui_text(srna, - "Solidify Modifier", - "Create a solid skin by extruding, compensating for sharp angles"); + RNA_def_struct_ui_text( + srna, "Solidify Modifier", "Create a solid skin, compensating for sharp angles"); RNA_def_struct_sdna(srna, "SolidifyModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_SOLIDIFY); + prop = RNA_def_property(srna, "solidify_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Selects the used algorithm"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "thickness", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "offset"); RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); @@ -4095,6 +4150,11 @@ static void rna_def_modifier_solidify(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Clamp", "Offset clamp based on geometry scale"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_thickness_angle_clamp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP); + RNA_def_property_ui_text(prop, "Angle Clamp", "Clamp thickness based on angles"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "thickness_vertex_group", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "offset_fac_vg"); RNA_def_property_range(prop, 0.0, 1.0); @@ -4188,6 +4248,18 @@ static void rna_def_modifier_solidify(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_SOLIDIFY_NOSHELL); RNA_def_property_ui_text(prop, "Only Rim", "Only add the rim to the original data"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + /* Settings for #MOD_SOLIDIFY_MODE_NONMANIFOLD */ + prop = RNA_def_property(srna, "nonmanifold_thickness_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "nonmanifold_offset_mode"); + RNA_def_property_enum_items(prop, nonmanifold_thickness_mode_items); + RNA_def_property_ui_text(prop, "Thickness Mode", "Selects the used thickness algorithm"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "nonmanifold_boundary_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, nonmanifold_boundary_mode_items); + RNA_def_property_ui_text(prop, "Boundary Shape", "Selects the boundary adjustment algorithm"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); } static void rna_def_modifier_screw(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_packedfile.c b/source/blender/makesrna/intern/rna_packedfile.c index dda1c637f39..8319612ea91 100644 --- a/source/blender/makesrna/intern/rna_packedfile.c +++ b/source/blender/makesrna/intern/rna_packedfile.c @@ -32,6 +32,7 @@ #include "rna_internal.h" const EnumPropertyItem rna_enum_unpack_method_items[] = { + {PF_REMOVE, "REMOVE", 0, "Remove Pack", ""}, {PF_USE_LOCAL, "USE_LOCAL", 0, "Use Local File", ""}, {PF_WRITE_LOCAL, "WRITE_LOCAL", 0, "Write Local File (overwrite existing)", ""}, {PF_USE_ORIGINAL, "USE_ORIGINAL", 0, "Use Original File", ""}, diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f1e20b32ddc..b9bc7b2bbf5 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3035,6 +3035,11 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Project individual elements on the surface of other objects"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_backface_culling", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_BACKFACE_CULLING); + RNA_def_property_ui_text(prop, "Backface Culling", "Exclude back facing geometry from snapping"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_self", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NO_SELF); RNA_def_property_ui_text(prop, "Project onto Self", "Snap onto itself (Edit Mode Only)"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 18985d41551..2c5f93e28ed 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2699,16 +2699,17 @@ static void rna_def_gaussian_blur(StructRNA *srna) static void rna_def_text(StructRNA *srna) { + /* Avoid text icons because they imply this aligns within a frame, see: T71082 */ static const EnumPropertyItem text_align_x_items[] = { - {SEQ_TEXT_ALIGN_X_LEFT, "LEFT", ICON_ALIGN_LEFT, "Left", ""}, - {SEQ_TEXT_ALIGN_X_CENTER, "CENTER", ICON_ALIGN_CENTER, "Center", ""}, - {SEQ_TEXT_ALIGN_X_RIGHT, "RIGHT", ICON_ALIGN_RIGHT, "Right", ""}, + {SEQ_TEXT_ALIGN_X_LEFT, "LEFT", 0, "Left", ""}, + {SEQ_TEXT_ALIGN_X_CENTER, "CENTER", 0, "Center", ""}, + {SEQ_TEXT_ALIGN_X_RIGHT, "RIGHT", 0, "Right", ""}, {0, NULL, 0, NULL, NULL}, }; static const EnumPropertyItem text_align_y_items[] = { - {SEQ_TEXT_ALIGN_Y_TOP, "TOP", ICON_ALIGN_TOP, "Top", ""}, - {SEQ_TEXT_ALIGN_Y_CENTER, "CENTER", ICON_ALIGN_MIDDLE, "Center", ""}, - {SEQ_TEXT_ALIGN_Y_BOTTOM, "BOTTOM", ICON_ALIGN_BOTTOM, "Bottom", ""}, + {SEQ_TEXT_ALIGN_Y_TOP, "TOP", 0, "Top", ""}, + {SEQ_TEXT_ALIGN_Y_CENTER, "CENTER", 0, "Center", ""}, + {SEQ_TEXT_ALIGN_Y_BOTTOM, "BOTTOM", 0, "Bottom", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2765,7 +2766,7 @@ static void rna_def_text(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "align"); RNA_def_property_enum_items(prop, text_align_x_items); RNA_def_property_ui_text( - prop, "Align X", "Align the text along the X axis, relative to the text midpoint"); + prop, "Align X", "Align the text along the X axis, relative to the text bounds"); RNA_def_property_update( prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); @@ -2773,7 +2774,7 @@ static void rna_def_text(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "align_y"); RNA_def_property_enum_items(prop, text_align_y_items); RNA_def_property_ui_text( - prop, "Align Y", "Align the image along the Y axis, relative to the text midpoint"); + prop, "Align Y", "Align the text along the Y axis, relative to the text bounds"); RNA_def_property_update( prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 3df678358d8..9ecfc7f675f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2979,6 +2979,12 @@ static void rna_def_userdef_theme_space_seq(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Metadata Text", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "preview_range", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "anim_preview_range"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Preview Range", "Color of preview range overlay"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } static void rna_def_userdef_theme_space_action(BlenderRNA *brna) diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index e508e38546e..d6e25c22866 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -91,6 +91,8 @@ set(SRC intern/MOD_smooth.c intern/MOD_softbody.c intern/MOD_solidify.c + intern/MOD_solidify_extrude.c + intern/MOD_solidify_nonmanifold.c intern/MOD_subsurf.c intern/MOD_surface.c intern/MOD_surfacedeform.c @@ -110,6 +112,7 @@ set(SRC MOD_modifiertypes.h intern/MOD_fluidsim_util.h intern/MOD_meshcache_util.h + intern/MOD_solidify_util.h intern/MOD_util.h intern/MOD_weightvg_util.h ) diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 292e659fe03..8ea0a602b65 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -23,142 +23,25 @@ #include "BLI_utildefines.h" -#include "BLI_bitmap.h" -#include "BLI_math.h" -#include "BLI_utildefines_stack.h" - #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "MEM_guardedalloc.h" -#include "BKE_mesh.h" #include "BKE_particle.h" -#include "BKE_deform.h" #include "MOD_modifiertypes.h" -#include "MOD_util.h" + +#include "MOD_solidify_util.h" #ifdef __GNUC__ # pragma GCC diagnostic error "-Wsign-conversion" #endif -/* skip shell thickness for non-manifold edges, see [#35710] */ -#define USE_NONMANIFOLD_WORKAROUND - -/* *** derived mesh high quality normal calculation function *** */ -/* could be exposed for other functions to use */ - -typedef struct EdgeFaceRef { - int p1; /* init as -1 */ - int p2; -} EdgeFaceRef; - -BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref) +static bool dependsOnNormals(ModifierData *md) { - return !((edge_ref->p1 == 0) && (edge_ref->p2 == 0)); -} - -/** - * \param dm: Mesh to calculate normals for. - * \param face_nors: Precalculated face normals. - * \param r_vert_nors: Return vert normals. - */ -static void mesh_calc_hq_normal(Mesh *mesh, float (*poly_nors)[3], float (*r_vert_nors)[3]) -{ - int i, numVerts, numEdges, numPolys; - MPoly *mpoly, *mp; - MLoop *mloop, *ml; - MEdge *medge, *ed; - MVert *mvert, *mv; - - numVerts = mesh->totvert; - numEdges = mesh->totedge; - numPolys = mesh->totpoly; - mpoly = mesh->mpoly; - medge = mesh->medge; - mvert = mesh->mvert; - mloop = mesh->mloop; - - /* we don't want to overwrite any referenced layers */ - - /* Doesn't work here! */ -#if 0 - mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, numVerts); - cddm->mvert = mv; -#endif - - mv = mvert; - mp = mpoly; - - { - EdgeFaceRef *edge_ref_array = MEM_calloc_arrayN( - (size_t)numEdges, sizeof(EdgeFaceRef), "Edge Connectivity"); - EdgeFaceRef *edge_ref; - float edge_normal[3]; - - /* Add an edge reference if it's not there, pointing back to the face index. */ - for (i = 0; i < numPolys; i++, mp++) { - int j; - - ml = mloop + mp->loopstart; - - for (j = 0; j < mp->totloop; j++, ml++) { - /* --- add edge ref to face --- */ - edge_ref = &edge_ref_array[ml->e]; - if (!edgeref_is_init(edge_ref)) { - edge_ref->p1 = i; - edge_ref->p2 = -1; - } - else if ((edge_ref->p1 != -1) && (edge_ref->p2 == -1)) { - edge_ref->p2 = i; - } - else { - /* 3+ faces using an edge, we can't handle this usefully */ - edge_ref->p1 = edge_ref->p2 = -1; -#ifdef USE_NONMANIFOLD_WORKAROUND - medge[ml->e].flag |= ME_EDGE_TMP_TAG; -#endif - } - /* --- done --- */ - } - } - - for (i = 0, ed = medge, edge_ref = edge_ref_array; i < numEdges; i++, ed++, edge_ref++) { - /* Get the edge vert indices, and edge value (the face indices that use it) */ - - if (edgeref_is_init(edge_ref) && (edge_ref->p1 != -1)) { - if (edge_ref->p2 != -1) { - /* We have 2 faces using this edge, calculate the edges normal - * using the angle between the 2 faces as a weighting */ -#if 0 - add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]); - normalize_v3_length( - edge_normal, - angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2])); -#else - mid_v3_v3v3_angle_weighted( - edge_normal, poly_nors[edge_ref->p1], poly_nors[edge_ref->p2]); -#endif - } - else { - /* only one face attached to that edge */ - /* an edge without another attached- the weight on this is undefined */ - copy_v3_v3(edge_normal, poly_nors[edge_ref->p1]); - } - add_v3_v3(r_vert_nors[ed->v1], edge_normal); - add_v3_v3(r_vert_nors[ed->v2], edge_normal); - } - } - MEM_freeN(edge_ref_array); - } - - /* normalize vertex normals and assign */ - for (i = 0; i < numVerts; i++, mv++) { - if (normalize_v3(r_vert_nors[i]) == 0.0f) { - normal_short_to_float_v3(r_vert_nors[i], mv->no); - } - } + const SolidifyModifierData *smd = (SolidifyModifierData *)md; + /* even when we calculate our own normals, + * the vertex normals are used as a fallback + * if manifold is enabled vertex normals are not used */ + return smd->mode == MOD_SOLIDIFY_MODE_EXTRUDE; } static void initData(ModifierData *md) @@ -167,6 +50,9 @@ static void initData(ModifierData *md) smd->offset = 0.01f; smd->offset_fac = -1.0f; smd->flag = MOD_SOLIDIFY_RIM; + smd->mode = MOD_SOLIDIFY_MODE_EXTRUDE; + smd->nonmanifold_offset_mode = MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS; + smd->nonmanifold_boundary_mode = MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE; } static void requiredDataMask(Object *UNUSED(ob), @@ -181,803 +67,16 @@ static void requiredDataMask(Object *UNUSED(ob), } } -/* specific function for solidify - define locally */ -BLI_INLINE void madd_v3v3short_fl(float r[3], const short a[3], const float f) -{ - r[0] += (float)a[0] * f; - r[1] += (float)a[1] * f; - r[2] += (float)a[2] * f; -} - static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { - Mesh *result; const SolidifyModifierData *smd = (SolidifyModifierData *)md; - - MVert *mv, *mvert, *orig_mvert; - MEdge *ed, *medge, *orig_medge; - MLoop *ml, *mloop, *orig_mloop; - MPoly *mp, *mpoly, *orig_mpoly; - const uint numVerts = (uint)mesh->totvert; - const uint numEdges = (uint)mesh->totedge; - const uint numPolys = (uint)mesh->totpoly; - const uint numLoops = (uint)mesh->totloop; - uint newLoops = 0, newPolys = 0, newEdges = 0, newVerts = 0, rimVerts = 0; - - /* only use material offsets if we have 2 or more materials */ - const short mat_nr_max = ctx->object->totcol > 1 ? ctx->object->totcol - 1 : 0; - const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0; - const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0; - - /* use for edges */ - /* over-alloc new_vert_arr, old_vert_arr */ - uint *new_vert_arr = NULL; - STACK_DECLARE(new_vert_arr); - - uint *new_edge_arr = NULL; - STACK_DECLARE(new_edge_arr); - - uint *old_vert_arr = MEM_calloc_arrayN( - numVerts, sizeof(*old_vert_arr), "old_vert_arr in solidify"); - - uint *edge_users = NULL; - char *edge_order = NULL; - - float(*vert_nors)[3] = NULL; - float(*poly_nors)[3] = NULL; - - const bool need_poly_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) || - (smd->flag & MOD_SOLIDIFY_EVEN); - - const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); - const float ofs_new = smd->offset + ofs_orig; - const float offset_fac_vg = smd->offset_fac_vg; - const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; - const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; - const bool do_clamp = (smd->offset_clamp != 0.0f); - const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == - 0; - - /* weights */ - MDeformVert *dvert; - const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; - int defgrp_index; - - /* array size is doubled in case of using a shell */ - const uint stride = do_shell ? 2 : 1; - - MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index); - - orig_mvert = mesh->mvert; - orig_medge = mesh->medge; - orig_mloop = mesh->mloop; - orig_mpoly = mesh->mpoly; - - if (need_poly_normals) { - /* calculate only face normals */ - poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); - BKE_mesh_calc_normals_poly(orig_mvert, - NULL, - (int)numVerts, - orig_mloop, - orig_mpoly, - (int)numLoops, - (int)numPolys, - poly_nors, - true); - } - - STACK_INIT(new_vert_arr, numVerts * 2); - STACK_INIT(new_edge_arr, numEdges * 2); - - if (smd->flag & MOD_SOLIDIFY_RIM) { - BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__); - uint eidx; - uint i; - -#define INVALID_UNUSED ((uint)-1) -#define INVALID_PAIR ((uint)-2) - - new_vert_arr = MEM_malloc_arrayN(numVerts, 2 * sizeof(*new_vert_arr), __func__); - new_edge_arr = MEM_malloc_arrayN(((numEdges * 2) + numVerts), sizeof(*new_edge_arr), __func__); - - edge_users = MEM_malloc_arrayN(numEdges, sizeof(*edge_users), "solid_mod edges"); - edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod eorder"); - - /* save doing 2 loops here... */ -#if 0 - copy_vn_i(edge_users, numEdges, INVALID_UNUSED); -#endif - - for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { - edge_users[eidx] = INVALID_UNUSED; - } - - for (i = 0, mp = orig_mpoly; i < numPolys; i++, mp++) { - MLoop *ml_prev; - int j; - - ml = orig_mloop + mp->loopstart; - ml_prev = ml + (mp->totloop - 1); - - for (j = 0; j < mp->totloop; j++, ml++) { - /* add edge user */ - eidx = ml_prev->e; - if (edge_users[eidx] == INVALID_UNUSED) { - ed = orig_medge + eidx; - BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); - edge_users[eidx] = (ml_prev->v > ml->v) == (ed->v1 < ed->v2) ? i : (i + numPolys); - edge_order[eidx] = j; - } - else { - edge_users[eidx] = INVALID_PAIR; - } - ml_prev = ml; - } - } - - for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { - if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) { - BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1); - BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2); - STACK_PUSH(new_edge_arr, eidx); - newPolys++; - newLoops += 4; - } - } - - for (i = 0; i < numVerts; i++) { - if (BLI_BITMAP_TEST(orig_mvert_tag, i)) { - old_vert_arr[i] = STACK_SIZE(new_vert_arr); - STACK_PUSH(new_vert_arr, i); - rimVerts++; - } - else { - old_vert_arr[i] = INVALID_UNUSED; - } - } - - MEM_freeN(orig_mvert_tag); - } - - if (do_shell == false) { - /* only add rim vertices */ - newVerts = rimVerts; - /* each extruded face needs an opposite edge */ - newEdges = newPolys; - } - else { - /* (stride == 2) in this case, so no need to add newVerts/newEdges */ - BLI_assert(newVerts == 0); - BLI_assert(newEdges == 0); - } - - if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { - vert_nors = MEM_calloc_arrayN(numVerts, 3 * sizeof(float), "mod_solid_vno_hq"); - mesh_calc_hq_normal(mesh, poly_nors, vert_nors); - } - - result = BKE_mesh_new_nomain_from_template(mesh, - (int)((numVerts * stride) + newVerts), - (int)((numEdges * stride) + newEdges + rimVerts), - 0, - (int)((numLoops * stride) + newLoops), - (int)((numPolys * stride) + newPolys)); - - mpoly = result->mpoly; - mloop = result->mloop; - medge = result->medge; - mvert = result->mvert; - - if (do_shell) { - CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)numVerts); - CustomData_copy_data(&mesh->vdata, &result->vdata, 0, (int)numVerts, (int)numVerts); - - CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, (int)numEdges); - CustomData_copy_data(&mesh->edata, &result->edata, 0, (int)numEdges, (int)numEdges); - - CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, (int)numLoops); - /* DO NOT copy here the 'copied' part of loop data, we want to reverse loops - * (so that winding of copied face get reversed, so that normals get reversed - * and point in expected direction...). - * If we also copy data here, then this data get overwritten - * (and allocated memory becomes memleak). */ - - CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)numPolys); - CustomData_copy_data(&mesh->pdata, &result->pdata, 0, (int)numPolys, (int)numPolys); + if (smd->mode == MOD_SOLIDIFY_MODE_EXTRUDE) { + return MOD_solidify_extrude_applyModifier(md, ctx, mesh); } - else { - int i, j; - CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)numVerts); - for (i = 0, j = (int)numVerts; i < numVerts; i++) { - if (old_vert_arr[i] != INVALID_UNUSED) { - CustomData_copy_data(&mesh->vdata, &result->vdata, i, j, 1); - j++; - } - } - - CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, (int)numEdges); - - for (i = 0, j = (int)numEdges; i < numEdges; i++) { - if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) { - MEdge *ed_src, *ed_dst; - CustomData_copy_data(&mesh->edata, &result->edata, i, j, 1); - - ed_src = &medge[i]; - ed_dst = &medge[j]; - ed_dst->v1 = old_vert_arr[ed_src->v1] + numVerts; - ed_dst->v2 = old_vert_arr[ed_src->v2] + numVerts; - j++; - } - } - - /* will be created later */ - CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, (int)numLoops); - CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)numPolys); + else if (smd->mode == MOD_SOLIDIFY_MODE_NONMANIFOLD) { + return MOD_solidify_nonmanifold_applyModifier(md, ctx, mesh); } - -#undef INVALID_UNUSED -#undef INVALID_PAIR - - /* initializes: (i_end, do_shell_align, mv) */ -#define INIT_VERT_ARRAY_OFFSETS(test) \ - if (((ofs_new >= ofs_orig) == do_flip) == test) { \ - i_end = numVerts; \ - do_shell_align = true; \ - mv = mvert; \ - } \ - else { \ - if (do_shell) { \ - i_end = numVerts; \ - do_shell_align = true; \ - } \ - else { \ - i_end = newVerts; \ - do_shell_align = false; \ - } \ - mv = &mvert[numVerts]; \ - } \ - (void)0 - - /* flip normals */ - - if (do_shell) { - uint i; - - mp = mpoly + numPolys; - for (i = 0; i < mesh->totpoly; i++, mp++) { - const int loop_end = mp->totloop - 1; - MLoop *ml2; - uint e; - int j; - - /* reverses the loop direction (MLoop.v as well as custom-data) - * MLoop.e also needs to be corrected too, done in a separate loop below. */ - ml2 = mloop + mp->loopstart + mesh->totloop; -#if 0 - for (j = 0; j < mp->totloop; j++) { - CustomData_copy_data(&mesh->ldata, - &result->ldata, - mp->loopstart + j, - mp->loopstart + (loop_end - j) + mesh->totloop, - 1); - } -#else - /* slightly more involved, keep the first vertex the same for the copy, - * ensures the diagonals in the new face match the original. */ - j = 0; - for (int j_prev = loop_end; j < mp->totloop; j_prev = j++) { - CustomData_copy_data(&mesh->ldata, - &result->ldata, - mp->loopstart + j, - mp->loopstart + (loop_end - j_prev) + mesh->totloop, - 1); - } -#endif - - if (mat_ofs) { - mp->mat_nr += mat_ofs; - CLAMP(mp->mat_nr, 0, mat_nr_max); - } - - e = ml2[0].e; - for (j = 0; j < loop_end; j++) { - ml2[j].e = ml2[j + 1].e; - } - ml2[loop_end].e = e; - - mp->loopstart += mesh->totloop; - - for (j = 0; j < mp->totloop; j++) { - ml2[j].e += numEdges; - ml2[j].v += numVerts; - } - } - - for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) { - ed->v1 += numVerts; - ed->v2 += numVerts; - } - } - - /* note, copied vertex layers don't have flipped normals yet. do this after applying offset */ - if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { - /* no even thickness, very simple */ - float scalar_short; - float scalar_short_vgroup; - - /* for clamping */ - float *vert_lens = NULL; - const float offset = fabsf(smd->offset) * smd->offset_clamp; - const float offset_sq = offset * offset; - - if (do_clamp) { - uint i; - - vert_lens = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_lens"); - copy_vn_fl(vert_lens, (int)numVerts, FLT_MAX); - for (i = 0; i < numEdges; i++) { - const float ed_len_sq = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); - vert_lens[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len_sq); - vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len_sq); - } - } - - if (ofs_new != 0.0f) { - uint i_orig, i_end; - bool do_shell_align; - - scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; - - INIT_VERT_ARRAY_OFFSETS(false); - - for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { - const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig]; - if (dvert) { - MDeformVert *dv = &dvert[i]; - if (defgrp_invert) { - scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); - } - else { - scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); - } - scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * - scalar_short; - } - if (do_clamp) { - /* always reset becaise we may have set before */ - if (dvert == NULL) { - scalar_short_vgroup = scalar_short; - } - if (vert_lens[i] < offset_sq) { - float scalar = sqrtf(vert_lens[i]) / offset; - scalar_short_vgroup *= scalar; - } - } - madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); - } - } - - if (ofs_orig != 0.0f) { - uint i_orig, i_end; - bool do_shell_align; - - scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; - - /* as above but swapped */ - INIT_VERT_ARRAY_OFFSETS(true); - - for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { - const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig]; - if (dvert) { - MDeformVert *dv = &dvert[i]; - if (defgrp_invert) { - scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); - } - else { - scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); - } - scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * - scalar_short; - } - if (do_clamp) { - /* always reset becaise we may have set before */ - if (dvert == NULL) { - scalar_short_vgroup = scalar_short; - } - if (vert_lens[i] < offset_sq) { - float scalar = sqrtf(vert_lens[i]) / offset; - scalar_short_vgroup *= scalar; - } - } - madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); - } - } - - if (do_clamp) { - MEM_freeN(vert_lens); - } - } - else { -#ifdef USE_NONMANIFOLD_WORKAROUND - const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0; -#endif - /* same as EM_solidify() in editmesh_lib.c */ - float *vert_angles = MEM_calloc_arrayN( - numVerts, 2 * sizeof(float), "mod_solid_pair"); /* 2 in 1 */ - float *vert_accum = vert_angles + numVerts; - uint vidx; - uint i; - - if (vert_nors == NULL) { - vert_nors = MEM_malloc_arrayN(numVerts, 3 * sizeof(float), "mod_solid_vno"); - for (i = 0, mv = mvert; i < numVerts; i++, mv++) { - normal_short_to_float_v3(vert_nors[i], mv->no); - } - } - - for (i = 0, mp = mpoly; i < numPolys; i++, mp++) { - /* #BKE_mesh_calc_poly_angles logic is inlined here */ - float nor_prev[3]; - float nor_next[3]; - - int i_curr = mp->totloop - 1; - int i_next = 0; - - ml = &mloop[mp->loopstart]; - - sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co); - normalize_v3(nor_prev); - - while (i_next < mp->totloop) { - float angle; - sub_v3_v3v3(nor_next, mvert[ml[i_curr].v].co, mvert[ml[i_next].v].co); - normalize_v3(nor_next); - angle = angle_normalized_v3v3(nor_prev, nor_next); - - /* --- not related to angle calc --- */ - if (angle < FLT_EPSILON) { - angle = FLT_EPSILON; - } - - vidx = ml[i_curr].v; - vert_accum[vidx] += angle; - -#ifdef USE_NONMANIFOLD_WORKAROUND - /* skip 3+ face user edges */ - if ((check_non_manifold == false) || - LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) && - ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) { - vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_nors[i]) * - angle; - } - else { - vert_angles[vidx] += angle; - } -#else - vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_nors[i]) * angle; -#endif - /* --- end non-angle-calc section --- */ - - /* step */ - copy_v3_v3(nor_prev, nor_next); - i_curr = i_next; - i_next++; - } - } - - /* vertex group support */ - if (dvert) { - MDeformVert *dv = dvert; - float scalar; - - if (defgrp_invert) { - for (i = 0; i < numVerts; i++, dv++) { - scalar = 1.0f - defvert_find_weight(dv, defgrp_index); - scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); - vert_angles[i] *= scalar; - } - } - else { - for (i = 0; i < numVerts; i++, dv++) { - scalar = defvert_find_weight(dv, defgrp_index); - scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); - vert_angles[i] *= scalar; - } - } - } - - if (do_clamp) { - float *vert_lens_sq = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_lens"); - const float offset = fabsf(smd->offset) * smd->offset_clamp; - const float offset_sq = offset * offset; - copy_vn_fl(vert_lens_sq, (int)numVerts, FLT_MAX); - for (i = 0; i < numEdges; i++) { - const float ed_len = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); - vert_lens_sq[medge[i].v1] = min_ff(vert_lens_sq[medge[i].v1], ed_len); - vert_lens_sq[medge[i].v2] = min_ff(vert_lens_sq[medge[i].v2], ed_len); - } - for (i = 0; i < numVerts; i++) { - if (vert_lens_sq[i] < offset_sq) { - float scalar = sqrtf(vert_lens_sq[i]) / offset; - vert_angles[i] *= scalar; - } - } - MEM_freeN(vert_lens_sq); - } - - if (ofs_new != 0.0f) { - uint i_orig, i_end; - bool do_shell_align; - - INIT_VERT_ARRAY_OFFSETS(false); - - for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { - const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; - if (vert_accum[i_other]) { /* zero if unselected */ - madd_v3_v3fl( - mv->co, vert_nors[i_other], ofs_new * (vert_angles[i_other] / vert_accum[i_other])); - } - } - } - - if (ofs_orig != 0.0f) { - uint i_orig, i_end; - bool do_shell_align; - - /* same as above but swapped, intentional use of 'ofs_new' */ - INIT_VERT_ARRAY_OFFSETS(true); - - for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { - const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; - if (vert_accum[i_other]) { /* zero if unselected */ - madd_v3_v3fl( - mv->co, vert_nors[i_other], ofs_orig * (vert_angles[i_other] / vert_accum[i_other])); - } - } - } - - MEM_freeN(vert_angles); - } - - if (vert_nors) { - MEM_freeN(vert_nors); - } - - /* must recalculate normals with vgroups since they can displace unevenly [#26888] */ - if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - } - else if (do_shell) { - uint i; - /* flip vertex normals for copied verts */ - mv = mvert + numVerts; - for (i = 0; i < numVerts; i++, mv++) { - negate_v3_short(mv->no); - } - } - - if (smd->flag & MOD_SOLIDIFY_RIM) { - uint i; - - /* bugger, need to re-calculate the normals for the new edge faces. - * This could be done in many ways, but probably the quickest way - * is to calculate the average normals for side faces only. - * Then blend them with the normals of the edge verts. - * - * at the moment its easiest to allocate an entire array for every vertex, - * even though we only need edge verts - campbell - */ - -#define SOLIDIFY_SIDE_NORMALS - -#ifdef SOLIDIFY_SIDE_NORMALS - /* Note that, due to the code setting cd_dirty_vert a few lines above, - * do_side_normals is always false. - Sybren */ - const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL); - /* annoying to allocate these since we only need the edge verts, */ - float(*edge_vert_nos)[3] = do_side_normals ? - MEM_calloc_arrayN(numVerts, 3 * sizeof(float), __func__) : - NULL; - float nor[3]; -#endif - const uchar crease_rim = smd->crease_rim * 255.0f; - const uchar crease_outer = smd->crease_outer * 255.0f; - const uchar crease_inner = smd->crease_inner * 255.0f; - - int *origindex_edge; - int *orig_ed; - uint j; - - if (crease_rim || crease_outer || crease_inner) { - result->cd_flag |= ME_CDFLAG_EDGE_CREASE; - } - - /* add faces & edges */ - origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX); - orig_ed = (origindex_edge) ? &origindex_edge[(numEdges * stride) + newEdges] : NULL; - ed = &medge[(numEdges * stride) + newEdges]; /* start after copied edges */ - for (i = 0; i < rimVerts; i++, ed++) { - ed->v1 = new_vert_arr[i]; - ed->v2 = (do_shell ? new_vert_arr[i] : i) + numVerts; - ed->flag |= ME_EDGEDRAW | ME_EDGERENDER; - - if (orig_ed) { - *orig_ed = ORIGINDEX_NONE; - orig_ed++; - } - - if (crease_rim) { - ed->crease = crease_rim; - } - } - - /* faces */ - mp = mpoly + (numPolys * stride); - ml = mloop + (numLoops * stride); - j = 0; - for (i = 0; i < newPolys; i++, mp++) { - uint eidx = new_edge_arr[i]; - uint pidx = edge_users[eidx]; - int k1, k2; - bool flip; - - if (pidx >= numPolys) { - pidx -= numPolys; - flip = true; - } - else { - flip = false; - } - - ed = medge + eidx; - - /* copy most of the face settings */ - CustomData_copy_data( - &mesh->pdata, &result->pdata, (int)pidx, (int)((numPolys * stride) + i), 1); - mp->loopstart = (int)(j + (numLoops * stride)); - mp->flag = mpoly[pidx].flag; - - /* notice we use 'mp->totloop' which is later overwritten, - * we could lookup the original face but there's no point since this is a copy - * and will have the same value, just take care when changing order of assignment */ - - /* prev loop */ - k1 = mpoly[pidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop); - - k2 = mpoly[pidx].loopstart + (edge_order[eidx]); - - mp->totloop = 4; - - CustomData_copy_data( - &mesh->ldata, &result->ldata, k2, (int)((numLoops * stride) + j + 0), 1); - CustomData_copy_data( - &mesh->ldata, &result->ldata, k1, (int)((numLoops * stride) + j + 1), 1); - CustomData_copy_data( - &mesh->ldata, &result->ldata, k1, (int)((numLoops * stride) + j + 2), 1); - CustomData_copy_data( - &mesh->ldata, &result->ldata, k2, (int)((numLoops * stride) + j + 3), 1); - - if (flip == false) { - ml[j].v = ed->v1; - ml[j++].e = eidx; - - ml[j].v = ed->v2; - ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; - - ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; - ml[j++].e = (do_shell ? eidx : i) + numEdges; - - ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; - ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; - } - else { - ml[j].v = ed->v2; - ml[j++].e = eidx; - - ml[j].v = ed->v1; - ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; - - ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; - ml[j++].e = (do_shell ? eidx : i) + numEdges; - - ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; - ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; - } - - if (origindex_edge) { - origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE; - origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE; - } - - /* use the next material index if option enabled */ - if (mat_ofs_rim) { - mp->mat_nr += mat_ofs_rim; - CLAMP(mp->mat_nr, 0, mat_nr_max); - } - if (crease_outer) { - /* crease += crease_outer; without wrapping */ - char *cr = &(ed->crease); - int tcr = *cr + crease_outer; - *cr = tcr > 255 ? 255 : tcr; - } - - if (crease_inner) { - /* crease += crease_inner; without wrapping */ - char *cr = &(medge[numEdges + (do_shell ? eidx : i)].crease); - int tcr = *cr + crease_inner; - *cr = tcr > 255 ? 255 : tcr; - } - -#ifdef SOLIDIFY_SIDE_NORMALS - if (do_side_normals) { - normal_quad_v3(nor, - mvert[ml[j - 4].v].co, - mvert[ml[j - 3].v].co, - mvert[ml[j - 2].v].co, - mvert[ml[j - 1].v].co); - - add_v3_v3(edge_vert_nos[ed->v1], nor); - add_v3_v3(edge_vert_nos[ed->v2], nor); - } -#endif - } - -#ifdef SOLIDIFY_SIDE_NORMALS - if (do_side_normals) { - const MEdge *ed_orig = medge; - ed = medge + (numEdges * stride); - for (i = 0; i < rimVerts; i++, ed++, ed_orig++) { - float nor_cpy[3]; - short *nor_short; - int k; - - /* note, only the first vertex (lower half of the index) is calculated */ - BLI_assert(ed->v1 < numVerts); - normalize_v3_v3(nor_cpy, edge_vert_nos[ed_orig->v1]); - - for (k = 0; k < 2; k++) { /* loop over both verts of the edge */ - nor_short = mvert[*(&ed->v1 + k)].no; - normal_short_to_float_v3(nor, nor_short); - add_v3_v3(nor, nor_cpy); - normalize_v3(nor); - normal_float_to_short_v3(nor_short, nor); - } - } - - MEM_freeN(edge_vert_nos); - } -#endif - - MEM_freeN(new_vert_arr); - MEM_freeN(new_edge_arr); - - MEM_freeN(edge_users); - MEM_freeN(edge_order); - } - - if (old_vert_arr) { - MEM_freeN(old_vert_arr); - } - - if (poly_nors) { - MEM_freeN(poly_nors); - } - - if (numPolys == 0 && numEdges != 0) { - modifier_setError(md, "Faces needed for useful output"); - } - - return result; -} - -#undef SOLIDIFY_SIDE_NORMALS - -static bool dependsOnNormals(ModifierData *UNUSED(md)) -{ - /* even when we calculate our own normals, - * the vertex normals are used as a fallback */ - return true; + return mesh; } ModifierTypeInfo modifierType_Solidify = { diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c new file mode 100644 index 00000000000..8c5a2551c0b --- /dev/null +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -0,0 +1,1105 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_utildefines.h" + +#include "BLI_bitmap.h" +#include "BLI_math.h" +#include "BLI_utildefines_stack.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_mesh.h" +#include "BKE_particle.h" +#include "BKE_deform.h" + +#include "MOD_modifiertypes.h" +#include "MOD_util.h" +#include "MOD_solidify_util.h" /* own include */ + +#ifdef __GNUC__ +# pragma GCC diagnostic error "-Wsign-conversion" +#endif + +/* -------------------------------------------------------------------- */ +/** \name Local Utilities + * \{ */ + +/* specific function for solidify - define locally */ +BLI_INLINE void madd_v3v3short_fl(float r[3], const short a[3], const float f) +{ + r[0] += (float)a[0] * f; + r[1] += (float)a[1] * f; + r[2] += (float)a[2] * f; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name High Quality Normal Calculation Function + * \{ */ + +/* skip shell thickness for non-manifold edges, see [#35710] */ +#define USE_NONMANIFOLD_WORKAROUND + +/* *** derived mesh high quality normal calculation function *** */ +/* could be exposed for other functions to use */ + +typedef struct EdgeFaceRef { + int p1; /* init as -1 */ + int p2; +} EdgeFaceRef; + +BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref) +{ + return !((edge_ref->p1 == 0) && (edge_ref->p2 == 0)); +} + +/** + * \param dm: Mesh to calculate normals for. + * \param poly_nors: Precalculated face normals. + * \param r_vert_nors: Return vert normals. + */ +static void mesh_calc_hq_normal(Mesh *mesh, float (*poly_nors)[3], float (*r_vert_nors)[3]) +{ + int i, numVerts, numEdges, numPolys; + MPoly *mpoly, *mp; + MLoop *mloop, *ml; + MEdge *medge, *ed; + MVert *mvert, *mv; + + numVerts = mesh->totvert; + numEdges = mesh->totedge; + numPolys = mesh->totpoly; + mpoly = mesh->mpoly; + medge = mesh->medge; + mvert = mesh->mvert; + mloop = mesh->mloop; + + /* we don't want to overwrite any referenced layers */ + + /* Doesn't work here! */ +#if 0 + mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, numVerts); + cddm->mvert = mv; +#endif + + mv = mvert; + mp = mpoly; + + { + EdgeFaceRef *edge_ref_array = MEM_calloc_arrayN( + (size_t)numEdges, sizeof(EdgeFaceRef), "Edge Connectivity"); + EdgeFaceRef *edge_ref; + float edge_normal[3]; + + /* Add an edge reference if it's not there, pointing back to the face index. */ + for (i = 0; i < numPolys; i++, mp++) { + int j; + + ml = mloop + mp->loopstart; + + for (j = 0; j < mp->totloop; j++, ml++) { + /* --- add edge ref to face --- */ + edge_ref = &edge_ref_array[ml->e]; + if (!edgeref_is_init(edge_ref)) { + edge_ref->p1 = i; + edge_ref->p2 = -1; + } + else if ((edge_ref->p1 != -1) && (edge_ref->p2 == -1)) { + edge_ref->p2 = i; + } + else { + /* 3+ faces using an edge, we can't handle this usefully */ + edge_ref->p1 = edge_ref->p2 = -1; +#ifdef USE_NONMANIFOLD_WORKAROUND + medge[ml->e].flag |= ME_EDGE_TMP_TAG; +#endif + } + /* --- done --- */ + } + } + + for (i = 0, ed = medge, edge_ref = edge_ref_array; i < numEdges; i++, ed++, edge_ref++) { + /* Get the edge vert indices, and edge value (the face indices that use it) */ + + if (edgeref_is_init(edge_ref) && (edge_ref->p1 != -1)) { + if (edge_ref->p2 != -1) { + /* We have 2 faces using this edge, calculate the edges normal + * using the angle between the 2 faces as a weighting */ +#if 0 + add_v3_v3v3(edge_normal, face_nors[edge_ref->f1], face_nors[edge_ref->f2]); + normalize_v3_length( + edge_normal, + angle_normalized_v3v3(face_nors[edge_ref->f1], face_nors[edge_ref->f2])); +#else + mid_v3_v3v3_angle_weighted( + edge_normal, poly_nors[edge_ref->p1], poly_nors[edge_ref->p2]); +#endif + } + else { + /* only one face attached to that edge */ + /* an edge without another attached- the weight on this is undefined */ + copy_v3_v3(edge_normal, poly_nors[edge_ref->p1]); + } + add_v3_v3(r_vert_nors[ed->v1], edge_normal); + add_v3_v3(r_vert_nors[ed->v2], edge_normal); + } + } + MEM_freeN(edge_ref_array); + } + + /* normalize vertex normals and assign */ + for (i = 0; i < numVerts; i++, mv++) { + if (normalize_v3(r_vert_nors[i]) == 0.0f) { + normal_short_to_float_v3(r_vert_nors[i], mv->no); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Solidify Function + * \{ */ + +Mesh *MOD_solidify_extrude_applyModifier(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh) +{ + Mesh *result; + const SolidifyModifierData *smd = (SolidifyModifierData *)md; + + MVert *mv, *mvert, *orig_mvert; + MEdge *ed, *medge, *orig_medge; + MLoop *ml, *mloop, *orig_mloop; + MPoly *mp, *mpoly, *orig_mpoly; + const uint numVerts = (uint)mesh->totvert; + const uint numEdges = (uint)mesh->totedge; + const uint numPolys = (uint)mesh->totpoly; + const uint numLoops = (uint)mesh->totloop; + uint newLoops = 0, newPolys = 0, newEdges = 0, newVerts = 0, rimVerts = 0; + + /* only use material offsets if we have 2 or more materials */ + const short mat_nr_max = ctx->object->totcol > 1 ? ctx->object->totcol - 1 : 0; + const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0; + const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0; + + /* use for edges */ + /* over-alloc new_vert_arr, old_vert_arr */ + uint *new_vert_arr = NULL; + STACK_DECLARE(new_vert_arr); + + uint *new_edge_arr = NULL; + STACK_DECLARE(new_edge_arr); + + uint *old_vert_arr = MEM_calloc_arrayN( + numVerts, sizeof(*old_vert_arr), "old_vert_arr in solidify"); + + uint *edge_users = NULL; + char *edge_order = NULL; + + float(*vert_nors)[3] = NULL; + float(*poly_nors)[3] = NULL; + + const bool need_poly_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) || + (smd->flag & MOD_SOLIDIFY_EVEN) || + (smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP); + + const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); + const float ofs_new = smd->offset + ofs_orig; + const float offset_fac_vg = smd->offset_fac_vg; + const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; + const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; + const bool do_clamp = (smd->offset_clamp != 0.0f); + const bool do_angle_clamp = (smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP) != 0; + const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == + 0; + + /* weights */ + MDeformVert *dvert; + const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; + int defgrp_index; + + /* array size is doubled in case of using a shell */ + const uint stride = do_shell ? 2 : 1; + + MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index); + + orig_mvert = mesh->mvert; + orig_medge = mesh->medge; + orig_mloop = mesh->mloop; + orig_mpoly = mesh->mpoly; + + if (need_poly_normals) { + /* calculate only face normals */ + poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); + BKE_mesh_calc_normals_poly(orig_mvert, + NULL, + (int)numVerts, + orig_mloop, + orig_mpoly, + (int)numLoops, + (int)numPolys, + poly_nors, + true); + } + + STACK_INIT(new_vert_arr, numVerts * 2); + STACK_INIT(new_edge_arr, numEdges * 2); + + if (smd->flag & MOD_SOLIDIFY_RIM) { + BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__); + uint eidx; + uint i; + +#define INVALID_UNUSED ((uint)-1) +#define INVALID_PAIR ((uint)-2) + + new_vert_arr = MEM_malloc_arrayN(numVerts, 2 * sizeof(*new_vert_arr), __func__); + new_edge_arr = MEM_malloc_arrayN(((numEdges * 2) + numVerts), sizeof(*new_edge_arr), __func__); + + edge_users = MEM_malloc_arrayN(numEdges, sizeof(*edge_users), "solid_mod edges"); + edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod eorder"); + + /* save doing 2 loops here... */ +#if 0 + copy_vn_i(edge_users, numEdges, INVALID_UNUSED); +#endif + + for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { + edge_users[eidx] = INVALID_UNUSED; + } + + for (i = 0, mp = orig_mpoly; i < numPolys; i++, mp++) { + MLoop *ml_prev; + int j; + + ml = orig_mloop + mp->loopstart; + ml_prev = ml + (mp->totloop - 1); + + for (j = 0; j < mp->totloop; j++, ml++) { + /* add edge user */ + eidx = ml_prev->e; + if (edge_users[eidx] == INVALID_UNUSED) { + ed = orig_medge + eidx; + BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); + edge_users[eidx] = (ml_prev->v > ml->v) == (ed->v1 < ed->v2) ? i : (i + numPolys); + edge_order[eidx] = j; + } + else { + edge_users[eidx] = INVALID_PAIR; + } + ml_prev = ml; + } + } + + for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { + if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) { + BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1); + BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2); + STACK_PUSH(new_edge_arr, eidx); + newPolys++; + newLoops += 4; + } + } + + for (i = 0; i < numVerts; i++) { + if (BLI_BITMAP_TEST(orig_mvert_tag, i)) { + old_vert_arr[i] = STACK_SIZE(new_vert_arr); + STACK_PUSH(new_vert_arr, i); + rimVerts++; + } + else { + old_vert_arr[i] = INVALID_UNUSED; + } + } + + MEM_freeN(orig_mvert_tag); + } + + if (do_shell == false) { + /* only add rim vertices */ + newVerts = rimVerts; + /* each extruded face needs an opposite edge */ + newEdges = newPolys; + } + else { + /* (stride == 2) in this case, so no need to add newVerts/newEdges */ + BLI_assert(newVerts == 0); + BLI_assert(newEdges == 0); + } + + if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { + vert_nors = MEM_calloc_arrayN(numVerts, 3 * sizeof(float), "mod_solid_vno_hq"); + mesh_calc_hq_normal(mesh, poly_nors, vert_nors); + } + + result = BKE_mesh_new_nomain_from_template(mesh, + (int)((numVerts * stride) + newVerts), + (int)((numEdges * stride) + newEdges + rimVerts), + 0, + (int)((numLoops * stride) + newLoops), + (int)((numPolys * stride) + newPolys)); + + mpoly = result->mpoly; + mloop = result->mloop; + medge = result->medge; + mvert = result->mvert; + + if (do_shell) { + CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)numVerts); + CustomData_copy_data(&mesh->vdata, &result->vdata, 0, (int)numVerts, (int)numVerts); + + CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, (int)numEdges); + CustomData_copy_data(&mesh->edata, &result->edata, 0, (int)numEdges, (int)numEdges); + + CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, (int)numLoops); + /* DO NOT copy here the 'copied' part of loop data, we want to reverse loops + * (so that winding of copied face get reversed, so that normals get reversed + * and point in expected direction...). + * If we also copy data here, then this data get overwritten + * (and allocated memory becomes memleak). */ + + CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)numPolys); + CustomData_copy_data(&mesh->pdata, &result->pdata, 0, (int)numPolys, (int)numPolys); + } + else { + int i, j; + CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)numVerts); + for (i = 0, j = (int)numVerts; i < numVerts; i++) { + if (old_vert_arr[i] != INVALID_UNUSED) { + CustomData_copy_data(&mesh->vdata, &result->vdata, i, j, 1); + j++; + } + } + + CustomData_copy_data(&mesh->edata, &result->edata, 0, 0, (int)numEdges); + + for (i = 0, j = (int)numEdges; i < numEdges; i++) { + if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) { + MEdge *ed_src, *ed_dst; + CustomData_copy_data(&mesh->edata, &result->edata, i, j, 1); + + ed_src = &medge[i]; + ed_dst = &medge[j]; + ed_dst->v1 = old_vert_arr[ed_src->v1] + numVerts; + ed_dst->v2 = old_vert_arr[ed_src->v2] + numVerts; + j++; + } + } + + /* will be created later */ + CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, (int)numLoops); + CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)numPolys); + } + + /* initializes: (i_end, do_shell_align, mv) */ +#define INIT_VERT_ARRAY_OFFSETS(test) \ + if (((ofs_new >= ofs_orig) == do_flip) == test) { \ + i_end = numVerts; \ + do_shell_align = true; \ + mv = mvert; \ + } \ + else { \ + if (do_shell) { \ + i_end = numVerts; \ + do_shell_align = true; \ + } \ + else { \ + i_end = newVerts; \ + do_shell_align = false; \ + } \ + mv = &mvert[numVerts]; \ + } \ + (void)0 + + /* flip normals */ + + if (do_shell) { + uint i; + + mp = mpoly + numPolys; + for (i = 0; i < mesh->totpoly; i++, mp++) { + const int loop_end = mp->totloop - 1; + MLoop *ml2; + uint e; + int j; + + /* reverses the loop direction (MLoop.v as well as custom-data) + * MLoop.e also needs to be corrected too, done in a separate loop below. */ + ml2 = mloop + mp->loopstart + mesh->totloop; +#if 0 + for (j = 0; j < mp->totloop; j++) { + CustomData_copy_data(&mesh->ldata, + &result->ldata, + mp->loopstart + j, + mp->loopstart + (loop_end - j) + mesh->totloop, + 1); + } +#else + /* slightly more involved, keep the first vertex the same for the copy, + * ensures the diagonals in the new face match the original. */ + j = 0; + for (int j_prev = loop_end; j < mp->totloop; j_prev = j++) { + CustomData_copy_data(&mesh->ldata, + &result->ldata, + mp->loopstart + j, + mp->loopstart + (loop_end - j_prev) + mesh->totloop, + 1); + } +#endif + + if (mat_ofs) { + mp->mat_nr += mat_ofs; + CLAMP(mp->mat_nr, 0, mat_nr_max); + } + + e = ml2[0].e; + for (j = 0; j < loop_end; j++) { + ml2[j].e = ml2[j + 1].e; + } + ml2[loop_end].e = e; + + mp->loopstart += mesh->totloop; + + for (j = 0; j < mp->totloop; j++) { + ml2[j].e += numEdges; + ml2[j].v += numVerts; + } + } + + for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) { + ed->v1 += numVerts; + ed->v2 += numVerts; + } + } + + /* note, copied vertex layers don't have flipped normals yet. do this after applying offset */ + if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { + /* no even thickness, very simple */ + float scalar_short; + float scalar_short_vgroup; + + /* for clamping */ + float *vert_lens = NULL; + float *vert_angs = NULL; + const float offset = fabsf(smd->offset) * smd->offset_clamp; + const float offset_sq = offset * offset; + + if (do_clamp) { + uint i; + + vert_lens = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_lens"); + copy_vn_fl(vert_lens, (int)numVerts, FLT_MAX); + for (i = 0; i < numEdges; i++) { + const float ed_len_sq = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); + vert_lens[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len_sq); + vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len_sq); + } + if (do_angle_clamp) { + uint eidx; + vert_angs = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_angs"); + copy_vn_fl(vert_angs, (int)numVerts, 0.5f * M_PI); + uint(*edge_user_pairs)[2] = MEM_malloc_arrayN( + numEdges, sizeof(*edge_user_pairs), "edge_user_pairs"); + for (eidx = 0; eidx < numEdges; eidx++) { + edge_user_pairs[eidx][0] = INVALID_UNUSED; + edge_user_pairs[eidx][1] = INVALID_UNUSED; + } + for (i = 0, mp = orig_mpoly; i < numPolys; i++, mp++) { + ml = orig_mloop + mp->loopstart; + MLoop *ml_prev = ml + (mp->totloop - 1); + + for (int j = 0; j < mp->totloop; j++, ml++) { + /* add edge user */ + eidx = ml_prev->e; + ed = orig_medge + eidx; + BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); + char flip = (char)((ml_prev->v > ml->v) == (ed->v1 < ed->v2)); + if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) { + edge_user_pairs[eidx][flip] = i; + } + else { + edge_user_pairs[eidx][0] = INVALID_PAIR; + edge_user_pairs[eidx][1] = INVALID_PAIR; + } + ml_prev = ml; + } + } + ed = orig_medge; + float e[3]; + for (i = 0; i < numEdges; i++, ed++) { + if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) && + !ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR)) { + const float *n0 = poly_nors[edge_user_pairs[i][0]]; + const float *n1 = poly_nors[edge_user_pairs[i][1]]; + sub_v3_v3v3(e, orig_mvert[ed->v1].co, orig_mvert[ed->v2].co); + normalize_v3(e); + const float angle = angle_signed_on_axis_v3v3_v3(n0, n1, e); + vert_angs[ed->v1] = max_ff(vert_angs[ed->v1], angle); + vert_angs[ed->v2] = max_ff(vert_angs[ed->v2], angle); + } + } + MEM_freeN(edge_user_pairs); + } + } + + if (ofs_new != 0.0f) { + uint i_orig, i_end; + bool do_shell_align; + + scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; + + INIT_VERT_ARRAY_OFFSETS(false); + + for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { + const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig]; + if (dvert) { + MDeformVert *dv = &dvert[i]; + if (defgrp_invert) { + scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); + } + else { + scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); + } + scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * + scalar_short; + } + if (do_clamp && offset > FLT_EPSILON) { + /* always reset because we may have set before */ + if (dvert == NULL) { + scalar_short_vgroup = scalar_short; + } + if (do_angle_clamp) { + float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f); + if (cos_ang > 0) { + float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang; + if (max_off < offset * 0.5f) { + scalar_short_vgroup *= max_off / offset * 2; + } + } + } + else { + if (vert_lens[i] < offset_sq) { + float scalar = sqrtf(vert_lens[i]) / offset; + scalar_short_vgroup *= scalar; + } + } + } + madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); + } + } + + if (ofs_orig != 0.0f) { + uint i_orig, i_end; + bool do_shell_align; + + scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; + + /* as above but swapped */ + INIT_VERT_ARRAY_OFFSETS(true); + + for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { + const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig]; + if (dvert) { + MDeformVert *dv = &dvert[i]; + if (defgrp_invert) { + scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); + } + else { + scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); + } + scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * + scalar_short; + } + if (do_clamp && offset > FLT_EPSILON) { + /* always reset because we may have set before */ + if (dvert == NULL) { + scalar_short_vgroup = scalar_short; + } + if (do_angle_clamp) { + float cos_ang = cosf(vert_angs[i_orig] * 0.5f); + if (cos_ang > 0) { + float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang; + if (max_off < offset * 0.5f) { + scalar_short_vgroup *= max_off / offset * 2; + } + } + } + else { + if (vert_lens[i] < offset_sq) { + float scalar = sqrtf(vert_lens[i]) / offset; + scalar_short_vgroup *= scalar; + } + } + } + madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); + } + } + + if (do_clamp) { + MEM_freeN(vert_lens); + if (do_angle_clamp) { + MEM_freeN(vert_angs); + } + } + } + else { +#ifdef USE_NONMANIFOLD_WORKAROUND + const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0; +#endif + /* same as EM_solidify() in editmesh_lib.c */ + float *vert_angles = MEM_calloc_arrayN( + numVerts, 2 * sizeof(float), "mod_solid_pair"); /* 2 in 1 */ + float *vert_accum = vert_angles + numVerts; + uint vidx; + uint i; + + if (vert_nors == NULL) { + vert_nors = MEM_malloc_arrayN(numVerts, 3 * sizeof(float), "mod_solid_vno"); + for (i = 0, mv = mvert; i < numVerts; i++, mv++) { + normal_short_to_float_v3(vert_nors[i], mv->no); + } + } + + for (i = 0, mp = mpoly; i < numPolys; i++, mp++) { + /* #BKE_mesh_calc_poly_angles logic is inlined here */ + float nor_prev[3]; + float nor_next[3]; + + int i_curr = mp->totloop - 1; + int i_next = 0; + + ml = &mloop[mp->loopstart]; + + sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co); + normalize_v3(nor_prev); + + while (i_next < mp->totloop) { + float angle; + sub_v3_v3v3(nor_next, mvert[ml[i_curr].v].co, mvert[ml[i_next].v].co); + normalize_v3(nor_next); + angle = angle_normalized_v3v3(nor_prev, nor_next); + + /* --- not related to angle calc --- */ + if (angle < FLT_EPSILON) { + angle = FLT_EPSILON; + } + + vidx = ml[i_curr].v; + vert_accum[vidx] += angle; + +#ifdef USE_NONMANIFOLD_WORKAROUND + /* skip 3+ face user edges */ + if ((check_non_manifold == false) || + LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) && + ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) { + vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_nors[i]) * + angle; + } + else { + vert_angles[vidx] += angle; + } +#else + vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_nors[i]) * angle; +#endif + /* --- end non-angle-calc section --- */ + + /* step */ + copy_v3_v3(nor_prev, nor_next); + i_curr = i_next; + i_next++; + } + } + + /* vertex group support */ + if (dvert) { + MDeformVert *dv = dvert; + float scalar; + + if (defgrp_invert) { + for (i = 0; i < numVerts; i++, dv++) { + scalar = 1.0f - defvert_find_weight(dv, defgrp_index); + scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); + vert_angles[i] *= scalar; + } + } + else { + for (i = 0; i < numVerts; i++, dv++) { + scalar = defvert_find_weight(dv, defgrp_index); + scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); + vert_angles[i] *= scalar; + } + } + } + + if (do_clamp) { + const float clamp_fac = 1 + (do_angle_clamp ? fabsf(smd->offset_fac) : 0); + const float offset = fabsf(smd->offset) * smd->offset_clamp * clamp_fac; + if (offset > FLT_EPSILON) { + float *vert_lens_sq = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_lens_sq"); + const float offset_sq = offset * offset; + copy_vn_fl(vert_lens_sq, (int)numVerts, FLT_MAX); + for (i = 0; i < numEdges; i++) { + const float ed_len = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); + vert_lens_sq[medge[i].v1] = min_ff(vert_lens_sq[medge[i].v1], ed_len); + vert_lens_sq[medge[i].v2] = min_ff(vert_lens_sq[medge[i].v2], ed_len); + } + if (do_angle_clamp) { + uint eidx; + float *vert_angs = MEM_malloc_arrayN(numVerts, sizeof(float), "vert_angs even"); + copy_vn_fl(vert_angs, (int)numVerts, 0.5f * M_PI); + uint(*edge_user_pairs)[2] = MEM_malloc_arrayN( + numEdges, sizeof(*edge_user_pairs), "edge_user_pairs"); + for (eidx = 0; eidx < numEdges; eidx++) { + edge_user_pairs[eidx][0] = INVALID_UNUSED; + edge_user_pairs[eidx][1] = INVALID_UNUSED; + } + for (i = 0, mp = orig_mpoly; i < numPolys; i++, mp++) { + ml = orig_mloop + mp->loopstart; + MLoop *ml_prev = ml + (mp->totloop - 1); + + for (int j = 0; j < mp->totloop; j++, ml++) { + /* add edge user */ + eidx = ml_prev->e; + ed = orig_medge + eidx; + BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); + char flip = (char)((ml_prev->v > ml->v) == (ed->v1 < ed->v2)); + if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) { + edge_user_pairs[eidx][flip] = i; + } + else { + edge_user_pairs[eidx][0] = INVALID_PAIR; + edge_user_pairs[eidx][1] = INVALID_PAIR; + } + ml_prev = ml; + } + } + ed = orig_medge; + for (i = 0; i < numEdges; i++, ed++) { + if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) && + !ELEM(edge_user_pairs[i][1], INVALID_UNUSED, INVALID_PAIR)) { + const float *n0 = poly_nors[edge_user_pairs[i][0]]; + const float *n1 = poly_nors[edge_user_pairs[i][1]]; + const float angle = M_PI - angle_normalized_v3v3(n0, n1); + vert_angs[ed->v1] = max_ff(vert_angs[ed->v1], angle); + vert_angs[ed->v2] = max_ff(vert_angs[ed->v2], angle); + } + } + MEM_freeN(edge_user_pairs); + + for (i = 0; i < numVerts; i++) { + float cos_ang = cosf(vert_angs[i] * 0.5f); + if (cos_ang > 0) { + float max_off = sqrtf(vert_lens_sq[i]) * 0.5f / cos_ang; + if (max_off < offset * 0.5f) { + vert_angles[i] *= max_off / offset * 2; + } + } + } + MEM_freeN(vert_angs); + } + else { + for (i = 0; i < numVerts; i++) { + if (vert_lens_sq[i] < offset_sq) { + float scalar = sqrtf(vert_lens_sq[i]) / offset; + vert_angles[i] *= scalar; + } + } + } + MEM_freeN(vert_lens_sq); + } + } + +#undef INVALID_UNUSED +#undef INVALID_PAIR + + if (ofs_new != 0.0f) { + uint i_orig, i_end; + bool do_shell_align; + + INIT_VERT_ARRAY_OFFSETS(false); + + for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { + const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; + if (vert_accum[i_other]) { /* zero if unselected */ + madd_v3_v3fl( + mv->co, vert_nors[i_other], ofs_new * (vert_angles[i_other] / vert_accum[i_other])); + } + } + } + + if (ofs_orig != 0.0f) { + uint i_orig, i_end; + bool do_shell_align; + + /* same as above but swapped, intentional use of 'ofs_new' */ + INIT_VERT_ARRAY_OFFSETS(true); + + for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { + const uint i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; + if (vert_accum[i_other]) { /* zero if unselected */ + madd_v3_v3fl( + mv->co, vert_nors[i_other], ofs_orig * (vert_angles[i_other] / vert_accum[i_other])); + } + } + } + + MEM_freeN(vert_angles); + } + + if (vert_nors) { + MEM_freeN(vert_nors); + } + + /* must recalculate normals with vgroups since they can displace unevenly [#26888] */ + if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) { + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + } + else if (do_shell) { + uint i; + /* flip vertex normals for copied verts */ + mv = mvert + numVerts; + for (i = 0; i < numVerts; i++, mv++) { + negate_v3_short(mv->no); + } + } + + if (smd->flag & MOD_SOLIDIFY_RIM) { + uint i; + + /* bugger, need to re-calculate the normals for the new edge faces. + * This could be done in many ways, but probably the quickest way + * is to calculate the average normals for side faces only. + * Then blend them with the normals of the edge verts. + * + * at the moment its easiest to allocate an entire array for every vertex, + * even though we only need edge verts - campbell + */ + +#define SOLIDIFY_SIDE_NORMALS + +#ifdef SOLIDIFY_SIDE_NORMALS + /* Note that, due to the code setting cd_dirty_vert a few lines above, + * do_side_normals is always false. - Sybren */ + const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL); + /* annoying to allocate these since we only need the edge verts, */ + float(*edge_vert_nos)[3] = do_side_normals ? + MEM_calloc_arrayN(numVerts, 3 * sizeof(float), __func__) : + NULL; + float nor[3]; +#endif + const uchar crease_rim = smd->crease_rim * 255.0f; + const uchar crease_outer = smd->crease_outer * 255.0f; + const uchar crease_inner = smd->crease_inner * 255.0f; + + int *origindex_edge; + int *orig_ed; + uint j; + + if (crease_rim || crease_outer || crease_inner) { + result->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + + /* add faces & edges */ + origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX); + orig_ed = (origindex_edge) ? &origindex_edge[(numEdges * stride) + newEdges] : NULL; + ed = &medge[(numEdges * stride) + newEdges]; /* start after copied edges */ + for (i = 0; i < rimVerts; i++, ed++) { + ed->v1 = new_vert_arr[i]; + ed->v2 = (do_shell ? new_vert_arr[i] : i) + numVerts; + ed->flag |= ME_EDGEDRAW | ME_EDGERENDER; + + if (orig_ed) { + *orig_ed = ORIGINDEX_NONE; + orig_ed++; + } + + if (crease_rim) { + ed->crease = crease_rim; + } + } + + /* faces */ + mp = mpoly + (numPolys * stride); + ml = mloop + (numLoops * stride); + j = 0; + for (i = 0; i < newPolys; i++, mp++) { + uint eidx = new_edge_arr[i]; + uint pidx = edge_users[eidx]; + int k1, k2; + bool flip; + + if (pidx >= numPolys) { + pidx -= numPolys; + flip = true; + } + else { + flip = false; + } + + ed = medge + eidx; + + /* copy most of the face settings */ + CustomData_copy_data( + &mesh->pdata, &result->pdata, (int)pidx, (int)((numPolys * stride) + i), 1); + mp->loopstart = (int)(j + (numLoops * stride)); + mp->flag = mpoly[pidx].flag; + + /* notice we use 'mp->totloop' which is later overwritten, + * we could lookup the original face but there's no point since this is a copy + * and will have the same value, just take care when changing order of assignment */ + + /* prev loop */ + k1 = mpoly[pidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop); + + k2 = mpoly[pidx].loopstart + (edge_order[eidx]); + + mp->totloop = 4; + + CustomData_copy_data( + &mesh->ldata, &result->ldata, k2, (int)((numLoops * stride) + j + 0), 1); + CustomData_copy_data( + &mesh->ldata, &result->ldata, k1, (int)((numLoops * stride) + j + 1), 1); + CustomData_copy_data( + &mesh->ldata, &result->ldata, k1, (int)((numLoops * stride) + j + 2), 1); + CustomData_copy_data( + &mesh->ldata, &result->ldata, k2, (int)((numLoops * stride) + j + 3), 1); + + if (flip == false) { + ml[j].v = ed->v1; + ml[j++].e = eidx; + + ml[j].v = ed->v2; + ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; + + ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; + ml[j++].e = (do_shell ? eidx : i) + numEdges; + + ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; + ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; + } + else { + ml[j].v = ed->v2; + ml[j++].e = eidx; + + ml[j].v = ed->v1; + ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; + + ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; + ml[j++].e = (do_shell ? eidx : i) + numEdges; + + ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; + ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; + } + + if (origindex_edge) { + origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE; + origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE; + } + + /* use the next material index if option enabled */ + if (mat_ofs_rim) { + mp->mat_nr += mat_ofs_rim; + CLAMP(mp->mat_nr, 0, mat_nr_max); + } + if (crease_outer) { + /* crease += crease_outer; without wrapping */ + char *cr = &(ed->crease); + int tcr = *cr + crease_outer; + *cr = tcr > 255 ? 255 : tcr; + } + + if (crease_inner) { + /* crease += crease_inner; without wrapping */ + char *cr = &(medge[numEdges + (do_shell ? eidx : i)].crease); + int tcr = *cr + crease_inner; + *cr = tcr > 255 ? 255 : tcr; + } + +#ifdef SOLIDIFY_SIDE_NORMALS + if (do_side_normals) { + normal_quad_v3(nor, + mvert[ml[j - 4].v].co, + mvert[ml[j - 3].v].co, + mvert[ml[j - 2].v].co, + mvert[ml[j - 1].v].co); + + add_v3_v3(edge_vert_nos[ed->v1], nor); + add_v3_v3(edge_vert_nos[ed->v2], nor); + } +#endif + } + +#ifdef SOLIDIFY_SIDE_NORMALS + if (do_side_normals) { + const MEdge *ed_orig = medge; + ed = medge + (numEdges * stride); + for (i = 0; i < rimVerts; i++, ed++, ed_orig++) { + float nor_cpy[3]; + short *nor_short; + int k; + + /* note, only the first vertex (lower half of the index) is calculated */ + BLI_assert(ed->v1 < numVerts); + normalize_v3_v3(nor_cpy, edge_vert_nos[ed_orig->v1]); + + for (k = 0; k < 2; k++) { /* loop over both verts of the edge */ + nor_short = mvert[*(&ed->v1 + k)].no; + normal_short_to_float_v3(nor, nor_short); + add_v3_v3(nor, nor_cpy); + normalize_v3(nor); + normal_float_to_short_v3(nor_short, nor); + } + } + + MEM_freeN(edge_vert_nos); + } +#endif + + MEM_freeN(new_vert_arr); + MEM_freeN(new_edge_arr); + + MEM_freeN(edge_users); + MEM_freeN(edge_order); + } + + if (old_vert_arr) { + MEM_freeN(old_vert_arr); + } + + if (poly_nors) { + MEM_freeN(poly_nors); + } + + if (numPolys == 0 && numVerts != 0) { + modifier_setError(md, "Faces needed for useful output"); + } + + return result; +} + +#undef SOLIDIFY_SIDE_NORMALS + +/** \} */ diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c new file mode 100644 index 00000000000..be7bbb86e4d --- /dev/null +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -0,0 +1,2268 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup modifiers + */ + +#include "BLI_utildefines.h" + +#include "BLI_math.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_mesh.h" +#include "BKE_particle.h" +#include "BKE_deform.h" + +#include "MOD_modifiertypes.h" +#include "MOD_util.h" +#include "MOD_solidify_util.h" /* Own include. */ + +#ifdef __GNUC__ +# pragma GCC diagnostic error "-Wsign-conversion" +#endif + +/* -------------------------------------------------------------------- */ +/** \name Local Utilities + * \{ */ + +/** + * Similar to #project_v3_v3v3_normalized that returns the dot-product. + */ +static float project_v3_v3(float r[3], const float a[3]) +{ + float d = dot_v3v3(r, a); + r[0] -= a[0] * d; + r[1] -= a[1] * d; + r[2] -= a[2] * d; + return d; +} + +static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3], + const float ref_n[3], + const float axis[3]) +{ + float d = dot_v3v3(n, ref_n); + CLAMP(d, -1, 1); + float angle = acosf(d); + float cross[3]; + cross_v3_v3v3(cross, n, ref_n); + if (dot_v3v3(cross, axis) >= 0) { + angle = 2 * M_PI - angle; + } + return angle; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Solidify Function + * \{ */ + +/* Data structures for manifold solidify. */ + +typedef struct NewFaceRef { + MPoly *face; + uint index; + bool reversed; + struct NewEdgeRef **link_edges; +} NewFaceRef; + +typedef struct OldEdgeFaceRef { + uint *faces; + uint faces_len; + bool *faces_reversed; + uint used; +} OldEdgeFaceRef; + +typedef struct OldVertEdgeRef { + uint *edges; + uint edges_len; +} OldVertEdgeRef; + +typedef struct NewEdgeRef { + uint old_edge; + NewFaceRef *faces[2]; + struct EdgeGroup *link_edge_groups[2]; + float angle; + uint new_edge; +} NewEdgeRef; + +typedef struct EdgeGroup { + bool valid; + NewEdgeRef **edges; + uint edges_len; + uint open_face_edge; + bool is_orig_closed; + bool is_even_split; + uint split; + bool is_singularity; + uint topo_group; + float co[3]; + float no[3]; + uint new_vert; +} EdgeGroup; + +typedef struct FaceKeyPair { + float angle; + NewFaceRef *face; +} FaceKeyPair; + +static int comp_float_int_pair(const void *a, const void *b) +{ + FaceKeyPair *x = (FaceKeyPair *)a; + FaceKeyPair *y = (FaceKeyPair *)b; + return (int)(x->angle > y->angle) - (int)(x->angle < y->angle); +} + +Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh) +{ + Mesh *result; + const SolidifyModifierData *smd = (SolidifyModifierData *)md; + + MVert *mv, *mvert, *orig_mvert; + MEdge *ed, *medge, *orig_medge; + MLoop *ml, *mloop, *orig_mloop; + MPoly *mp, *mpoly, *orig_mpoly; + const uint numVerts = (uint)mesh->totvert; + const uint numEdges = (uint)mesh->totedge; + const uint numPolys = (uint)mesh->totpoly; + const uint numLoops = (uint)mesh->totloop; + + if (numPolys == 0 && numVerts != 0) { + modifier_setError(md, "Faces needed for useful output"); + return mesh; + } + + /* Only use material offsets if we have 2 or more materials. */ + const short mat_nrs = ctx->object->totcol > 1 ? ctx->object->totcol : 1; + const short mat_nr_max = mat_nrs - 1; + const short mat_ofs = mat_nrs > 1 ? smd->mat_ofs : 0; + const short mat_ofs_rim = mat_nrs > 1 ? smd->mat_ofs_rim : 0; + + float(*poly_nors)[3] = NULL; + + const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset; + const float ofs_back = ofs_front - smd->offset * smd->offset_fac; + const float offset_fac_vg = smd->offset_fac_vg; + const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; + const float offset = fabsf(smd->offset) * smd->offset_clamp; + const bool do_angle_clamp = smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP; + const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; + const bool do_rim = smd->flag & MOD_SOLIDIFY_RIM; + const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == + 0; + const bool do_clamp = (smd->offset_clamp != 0.0f); + + MDeformVert *dvert; + const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; + int defgrp_index; + + MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index); + + orig_mvert = mesh->mvert; + orig_medge = mesh->medge; + orig_mloop = mesh->mloop; + orig_mpoly = mesh->mpoly; + + uint numNewVerts = 0; + uint numNewEdges = 0; + uint numNewLoops = 0; + uint numNewPolys = 0; + +#define MOD_SOLIDIFY_EMPTY_TAG ((uint)-1) + + /* Calculate only face normals. */ + poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__); + BKE_mesh_calc_normals_poly(orig_mvert, + NULL, + (int)numVerts, + orig_mloop, + orig_mpoly, + (int)numLoops, + (int)numPolys, + poly_nors, + true); + + NewFaceRef *face_sides_arr = MEM_malloc_arrayN( + numPolys * 2, sizeof(*face_sides_arr), "face_sides_arr in solidify"); + bool *null_faces = + (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) ? + MEM_calloc_arrayN(numPolys, sizeof(*null_faces), "null_faces in solidify") : + NULL; + uint largest_ngon = 3; + /* Calculate face to #NewFaceRef map. */ + { + mp = orig_mpoly; + for (uint i = 0; i < numPolys; i++, mp++) { + /* Make normals for faces without area (should really be avoided though). */ + if (len_squared_v3(poly_nors[i]) < 0.5f) { + MEdge *e = orig_medge + orig_mloop[mp->loopstart].e; + float edgedir[3]; + sub_v3_v3v3(edgedir, orig_mvert[e->v2].co, orig_mvert[e->v1].co); + if (fabsf(edgedir[2]) < fabsf(edgedir[1])) { + poly_nors[i][2] = 1.0f; + } + else { + poly_nors[i][1] = 1.0f; + } + if (null_faces) { + null_faces[i] = true; + } + } + + NewEdgeRef **link_edges = MEM_calloc_arrayN( + (uint)mp->totloop, sizeof(*link_edges), "NewFaceRef::link_edges in solidify"); + face_sides_arr[i * 2] = (NewFaceRef){ + .face = mp, .index = i, .reversed = false, .link_edges = link_edges}; + link_edges = MEM_calloc_arrayN( + (uint)mp->totloop, sizeof(*link_edges), "NewFaceRef::link_edges in solidify"); + face_sides_arr[i * 2 + 1] = (NewFaceRef){ + .face = mp, .index = i, .reversed = true, .link_edges = link_edges}; + if (mp->totloop > largest_ngon) { + largest_ngon = (uint)mp->totloop; + } + if (do_shell) { + numNewPolys += 2; + numNewLoops += (uint)mp->totloop * 2; + } + } + } + + uint *edge_adj_faces_len = MEM_calloc_arrayN( + numEdges, sizeof(*edge_adj_faces_len), "edge_adj_faces_len in solidify"); + /* Count for each edge how many faces it has adjacent. */ + { + mp = orig_mpoly; + for (uint i = 0; i < numPolys; i++, mp++) { + ml = orig_mloop + mp->loopstart; + for (uint j = 0; j < mp->totloop; j++, ml++) { + edge_adj_faces_len[ml->e]++; + } + } + } + + /* Original edge to #NewEdgeRef map. */ + NewEdgeRef ***orig_edge_data_arr = MEM_calloc_arrayN( + numEdges, sizeof(*orig_edge_data_arr), "orig_edge_data_arr in solidify"); + /* Original edge length cache. */ + float *orig_edge_lengths = MEM_calloc_arrayN( + numEdges, sizeof(*orig_edge_lengths), "orig_edge_lengths in solidify"); + /* Edge groups for every original vert. */ + EdgeGroup **orig_vert_groups_arr = MEM_calloc_arrayN( + numVerts, sizeof(*orig_vert_groups_arr), "orig_vert_groups_arr in solidify"); + /* Duplicate verts map. */ + uint *vm = MEM_malloc_arrayN(numVerts, sizeof(*vm), "orig_vert_map in solidify"); + for (uint i = 0; i < numVerts; i++) { + vm[i] = i; + } + + uint edge_index = 0; + uint loop_index = 0; + uint poly_index = 0; + + bool has_singularities = false; + + /* Vert edge adjacent map. */ + uint *vert_adj_edges_len = MEM_calloc_arrayN( + numVerts, sizeof(*vert_adj_edges_len), "vert_adj_edges_len in solidify"); + OldVertEdgeRef **vert_adj_edges = MEM_calloc_arrayN( + numVerts, sizeof(*vert_adj_edges), "vert_adj_edges in solidify"); + + /* Create edge to #NewEdgeRef map. */ + { + OldEdgeFaceRef **edge_adj_faces = MEM_calloc_arrayN( + numEdges, sizeof(*edge_adj_faces), "edge_adj_faces in solidify"); + + /* Create link_faces for edges. */ + { + mp = orig_mpoly; + for (uint i = 0; i < numPolys; i++, mp++) { + ml = orig_mloop + mp->loopstart; + for (uint j = 0; j < mp->totloop; j++, ml++) { + const uint edge = ml->e; + const bool reversed = orig_medge[edge].v2 != ml->v; + OldEdgeFaceRef *old_face_edge_ref = edge_adj_faces[edge]; + if (old_face_edge_ref == NULL) { + const uint len = edge_adj_faces_len[edge]; + BLI_assert(len > 0); + uint *adj_faces = MEM_malloc_arrayN( + len, sizeof(*adj_faces), "OldEdgeFaceRef::faces in solidify"); + bool *adj_faces_loops_reversed = MEM_malloc_arrayN( + len, sizeof(*adj_faces_loops_reversed), "OldEdgeFaceRef::reversed in solidify"); + adj_faces[0] = i; + for (uint k = 1; k < len; k++) { + adj_faces[k] = MOD_SOLIDIFY_EMPTY_TAG; + } + adj_faces_loops_reversed[0] = reversed; + OldEdgeFaceRef *ref = MEM_mallocN(sizeof(*ref), "OldEdgeFaceRef in solidify"); + *ref = (OldEdgeFaceRef){adj_faces, len, adj_faces_loops_reversed, 1}; + edge_adj_faces[edge] = ref; + } + else { + for (uint k = 1; k < old_face_edge_ref->faces_len; k++) { + if (old_face_edge_ref->faces[k] == MOD_SOLIDIFY_EMPTY_TAG) { + old_face_edge_ref->faces[k] = i; + old_face_edge_ref->faces_reversed[k] = reversed; + break; + } + } + } + } + } + } + + float edgedir[3] = {0, 0, 0}; + + /* Calculate edge lengths and len vert_adj edges. */ + { + bool *face_singularity = MEM_calloc_arrayN( + numPolys, sizeof(*face_singularity), "face_sides_arr in solidify"); + ed = orig_medge; + for (uint i = 0; i < numEdges; i++, ed++) { + if (edge_adj_faces_len[i] > 0) { + const uint v1 = vm[ed->v1]; + const uint v2 = vm[ed->v2]; + if (v1 != v2) { + sub_v3_v3v3(edgedir, orig_mvert[v2].co, orig_mvert[v1].co); + orig_edge_lengths[i] = len_squared_v3(edgedir); + } + if (v1 == v2 || orig_edge_lengths[i] <= FLT_EPSILON) { + if (v2 > v1) { + for (uint j = v2; j < numVerts; j++) { + if (vm[j] == v2) { + vm[j] = v1; + vert_adj_edges_len[v1] += vert_adj_edges_len[j]; + vert_adj_edges_len[j] = 0; + } + } + } + else if (v2 < v1) { + for (uint j = v1; j < numVerts; j++) { + if (vm[j] == v1) { + vm[j] = v2; + vert_adj_edges_len[v2] += vert_adj_edges_len[j]; + vert_adj_edges_len[j] = 0; + } + } + } + if (do_shell) { + numNewLoops -= edge_adj_faces_len[i] * 2; + } + if (v1 == v2) { + /* Remove polys. */ + for (uint j = 0; j < edge_adj_faces[i]->faces_len; j++) { + const uint face = edge_adj_faces[i]->faces[j]; + if (!face_singularity[face]) { + bool is_singularity = true; + for (uint k = 0; k < orig_mpoly[face].totloop; k++) { + if (vm[orig_mloop[((uint)orig_mpoly[face].loopstart) + k].v] != v1) { + is_singularity = false; + break; + } + } + if (is_singularity) { + face_singularity[face] = true; + if (do_shell) { + numNewPolys -= 2; + } + } + } + } + } + edge_adj_faces_len[i] = 0; + MEM_freeN(edge_adj_faces[i]->faces); + MEM_freeN(edge_adj_faces[i]->faces_reversed); + MEM_freeN(edge_adj_faces[i]); + edge_adj_faces[i] = NULL; + } + else if (edge_adj_faces_len[i] > 0) { + orig_edge_lengths[i] = sqrtf(orig_edge_lengths[i]); + vert_adj_edges_len[v1]++; + vert_adj_edges_len[v2]++; + } + } + } + MEM_freeN(face_singularity); + } + + /* Create vert_adj_edges for verts. */ + { + ed = orig_medge; + for (uint i = 0; i < numEdges; i++, ed++) { + if (edge_adj_faces_len[i] > 0) { + const uint vs[2] = {vm[ed->v1], vm[ed->v2]}; + uint invalid_edge_index = 0; + bool invalid_edge_reversed = false; + for (uint j = 0; j < 2; j++) { + const uint vert = vs[j]; + const uint len = vert_adj_edges_len[vert]; + if (len > 0) { + OldVertEdgeRef *old_edge_vert_ref = vert_adj_edges[vert]; + if (old_edge_vert_ref == NULL) { + uint *adj_edges = MEM_calloc_arrayN( + len, sizeof(*adj_edges), "OldVertEdgeRef::edges in solidify"); + adj_edges[0] = i; + for (uint k = 1; k < len; k++) { + adj_edges[k] = MOD_SOLIDIFY_EMPTY_TAG; + } + OldVertEdgeRef *ref = MEM_mallocN(sizeof(*ref), "OldVertEdgeRef in solidify"); + *ref = (OldVertEdgeRef){adj_edges, 1}; + vert_adj_edges[vert] = ref; + } + else { + const uint *f = old_edge_vert_ref->edges; + for (uint k = 0; k < len && k <= old_edge_vert_ref->edges_len; k++, f++) { + const uint edge = old_edge_vert_ref->edges[k]; + if (edge == MOD_SOLIDIFY_EMPTY_TAG || k == old_edge_vert_ref->edges_len) { + old_edge_vert_ref->edges[k] = i; + old_edge_vert_ref->edges_len++; + break; + } + else if (vm[orig_medge[edge].v1] == vs[1 - j]) { + invalid_edge_index = edge + 1; + invalid_edge_reversed = (j == 0); + break; + } + else if (vm[orig_medge[edge].v2] == vs[1 - j]) { + invalid_edge_index = edge + 1; + invalid_edge_reversed = (j == 1); + break; + } + } + if (invalid_edge_index) { + /* Should never actually be executed. */ + if (j == 1) { + vert_adj_edges[vs[0]]->edges_len--; + } + break; + } + } + } + } + if (invalid_edge_index) { + const uint tmp = invalid_edge_index - 1; + invalid_edge_index = i; + i = tmp; + OldEdgeFaceRef *i_adj_faces = edge_adj_faces[i]; + OldEdgeFaceRef *invalid_adj_faces = edge_adj_faces[invalid_edge_index]; + uint j = 0; + for (uint k = 0; k < i_adj_faces->faces_len; k++) { + for (uint l = 0; l < invalid_adj_faces->faces_len; l++) { + if (i_adj_faces->faces[k] == invalid_adj_faces->faces[l] && + i_adj_faces->faces[k] != MOD_SOLIDIFY_EMPTY_TAG) { + i_adj_faces->faces[k] = MOD_SOLIDIFY_EMPTY_TAG; + invalid_adj_faces->faces[l] = MOD_SOLIDIFY_EMPTY_TAG; + j++; + } + } + } + if (do_shell) { + numNewPolys -= 2 * j; + numNewLoops -= 4 * j; + } + const uint len = i_adj_faces->faces_len + invalid_adj_faces->faces_len - 2 * j; + uint *adj_faces = MEM_malloc_arrayN( + len, sizeof(*adj_faces), "OldEdgeFaceRef::faces in solidify"); + bool *adj_faces_loops_reversed = MEM_malloc_arrayN( + len, sizeof(*adj_faces_loops_reversed), "OldEdgeFaceRef::reversed in solidify"); + j = 0; + for (uint k = 0; k < i_adj_faces->faces_len; k++) { + if (i_adj_faces->faces[k] != MOD_SOLIDIFY_EMPTY_TAG) { + adj_faces[j] = i_adj_faces->faces[k]; + adj_faces_loops_reversed[j++] = i_adj_faces->faces_reversed[k]; + } + } + for (uint k = 0; k < invalid_adj_faces->faces_len; k++) { + if (invalid_adj_faces->faces[k] != MOD_SOLIDIFY_EMPTY_TAG) { + adj_faces[j] = invalid_adj_faces->faces[k]; + adj_faces_loops_reversed[j++] = (invalid_edge_reversed != + invalid_adj_faces->faces_reversed[k]); + } + } + BLI_assert(j == len); + edge_adj_faces_len[invalid_edge_index] = 0; + edge_adj_faces_len[i] = len; + MEM_freeN(i_adj_faces->faces); + MEM_freeN(i_adj_faces->faces_reversed); + i_adj_faces->faces_len = len; + i_adj_faces->faces = adj_faces; + i_adj_faces->faces_reversed = adj_faces_loops_reversed; + i_adj_faces->used += invalid_adj_faces->used; + MEM_freeN(invalid_adj_faces->faces); + MEM_freeN(invalid_adj_faces->faces_reversed); + MEM_freeN(invalid_adj_faces); + edge_adj_faces[invalid_edge_index] = i_adj_faces; + /* Reset counter to continue. */ + i = invalid_edge_index; + } + } + } + } + + /* Filter duplicate polys. */ + { + ed = orig_medge; + for (uint i = 0; i < numEdges; i++, ed++) { + if (edge_adj_faces_len[i] > 0) { + const OldEdgeFaceRef *adj_faces = edge_adj_faces[i]; + uint adj_len = adj_faces->faces_len; + if (adj_len > 1) { + /* For each face pair check if they have equal verts. */ + for (uint j = 0; j < adj_len; j++) { + const uint face = adj_faces->faces[j]; + const int j_loopstart = orig_mpoly[face].loopstart; + const int totloop = orig_mpoly[face].totloop; + const uint j_first_v = vm[orig_mloop[j_loopstart].v]; + for (uint k = j + 1; k < adj_len; k++) { + if (orig_mpoly[adj_faces->faces[k]].totloop != totloop) { + continue; + } + /* Find first face first loop vert in second face loops. */ + const int k_loopstart = orig_mpoly[adj_faces->faces[k]].loopstart; + int l; + ml = orig_mloop + k_loopstart; + for (l = 0; l < totloop && vm[ml->v] != j_first_v; l++, ml++) { + /* Pass. */ + } + if (l == totloop) { + continue; + } + /* Check if all following loops have equal verts. */ + const bool reversed = adj_faces->faces_reversed[j] != adj_faces->faces_reversed[k]; + const int count_dir = reversed ? -1 : 1; + bool has_diff = false; + ml = orig_mloop + j_loopstart; + for (int m = 0, n = l + totloop; m < totloop && !has_diff; + m++, n += count_dir, ml++) { + has_diff = has_diff || vm[ml->v] != vm[orig_mloop[k_loopstart + n % totloop].v]; + } + /* If the faces are equal, discard one (j). */ + if (!has_diff) { + ml = orig_mloop + j_loopstart; + uint del_loops = 0; + for (uint m = 0; m < totloop; m++, ml++) { + const uint e = ml->e; + uint face_index = j; + uint *e_adj_faces_faces = edge_adj_faces[e]->faces; + bool *e_adj_faces_reversed = edge_adj_faces[e]->faces_reversed; + const uint faces_len = edge_adj_faces[e]->faces_len; + if (e != i) { + /* Find index of e in #adj_faces. */ + for (face_index = 0; + face_index < faces_len && e_adj_faces_faces[face_index] != face; + face_index++) { + /* Pass. */ + } + /* If not found. */ + if (face_index == faces_len) { + continue; + } + } + else { + adj_len--; + } + memmove(e_adj_faces_faces + face_index, + e_adj_faces_faces + face_index + 1, + (faces_len - face_index - 1) * sizeof(*e_adj_faces_faces)); + memmove(e_adj_faces_reversed + face_index, + e_adj_faces_reversed + face_index + 1, + (faces_len - face_index - 1) * sizeof(*e_adj_faces_reversed)); + edge_adj_faces[e]->faces_len--; + if (edge_adj_faces_len[e] > 0) { + edge_adj_faces_len[e]--; + if (edge_adj_faces_len[e] == 0) { + edge_adj_faces[e]->used--; + edge_adj_faces[e] = NULL; + } + } + del_loops++; + } + if (do_shell) { + numNewPolys -= 2; + numNewLoops -= 2 * (uint)del_loops; + } + break; + } + } + } + } + } + } + } + + /* Create #NewEdgeRef array. */ + { + ed = orig_medge; + for (uint i = 0; i < numEdges; i++, ed++) { + const uint v1 = vm[ed->v1]; + const uint v2 = vm[ed->v2]; + if (edge_adj_faces_len[i] > 0) { + sub_v3_v3v3(edgedir, orig_mvert[v2].co, orig_mvert[v1].co); + mul_v3_fl(edgedir, 1.0f / orig_edge_lengths[i]); + + OldEdgeFaceRef *adj_faces = edge_adj_faces[i]; + const uint adj_len = adj_faces->faces_len; + const uint *adj_faces_faces = adj_faces->faces; + const bool *adj_faces_reversed = adj_faces->faces_reversed; + uint new_edges_len = 0; + FaceKeyPair *sorted_faces = MEM_malloc_arrayN( + adj_len, sizeof(*sorted_faces), "sorted_faces in solidify"); + if (adj_len > 1) { + new_edges_len = adj_len; + /* Get keys for sorting. */ + float ref_nor[3] = {0, 0, 0}; + float nor[3]; + for (uint j = 0; j < adj_len; j++) { + const bool reverse = adj_faces_reversed[j]; + const uint face_i = adj_faces_faces[j]; + if (reverse) { + negate_v3_v3(nor, poly_nors[face_i]); + } + else { + copy_v3_v3(nor, poly_nors[face_i]); + } + float d = 1; + if (orig_mpoly[face_i].totloop > 3) { + d = project_v3_v3(nor, edgedir); + if (LIKELY(d != 0)) { + d = normalize_v3(nor); + } + else { + d = 1; + } + } + if (UNLIKELY(d == 0.0f)) { + sorted_faces[j].angle = 0.0f; + } + else if (j == 0) { + copy_v3_v3(ref_nor, nor); + sorted_faces[j].angle = 0.0f; + } + else { + float angle = angle_signed_on_axis_normalized_v3v3_v3(nor, ref_nor, edgedir); + sorted_faces[j].angle = -angle; + } + sorted_faces[j].face = face_sides_arr + adj_faces_faces[j] * 2 + + (adj_faces_reversed[j] ? 1 : 0); + } + /* Sort faces by order around the edge (keep order in faces, + * reversed and face_angles the same). */ + qsort(sorted_faces, adj_len, sizeof(*sorted_faces), comp_float_int_pair); + } + else { + new_edges_len = 2; + sorted_faces[0].face = face_sides_arr + adj_faces_faces[0] * 2 + + (adj_faces_reversed[0] ? 1 : 0); + if (do_rim) { + /* Only add the loops parallel to the edge for now. */ + numNewLoops += 2; + numNewPolys++; + } + } + + /* Create a list of new edges and fill it. */ + NewEdgeRef **new_edges = MEM_malloc_arrayN( + new_edges_len + 1, sizeof(*new_edges), "new_edges in solidify"); + new_edges[new_edges_len] = NULL; + NewFaceRef *faces[2]; + for (uint j = 0; j < new_edges_len; j++) { + float angle; + if (adj_len > 1) { + const uint next_j = j + 1 == adj_len ? 0 : j + 1; + faces[0] = sorted_faces[j].face; + faces[1] = sorted_faces[next_j].face->reversed ? sorted_faces[next_j].face - 1 : + sorted_faces[next_j].face + 1; + angle = sorted_faces[next_j].angle - sorted_faces[j].angle; + if (angle < 0) { + angle += 2 * M_PI; + } + } + else { + faces[0] = sorted_faces[0].face->reversed ? sorted_faces[0].face - j : + sorted_faces[0].face + j; + faces[1] = NULL; + angle = 0; + } + NewEdgeRef *edge_data = MEM_mallocN(sizeof(*edge_data), "edge_data in solidify"); + uint edge_data_edge_index = MOD_SOLIDIFY_EMPTY_TAG; + if (do_shell || (adj_len == 1 && do_rim)) { + edge_data_edge_index = 0; + } + *edge_data = (NewEdgeRef){.old_edge = i, + .faces = {faces[0], faces[1]}, + .link_edge_groups = {NULL, NULL}, + .angle = angle, + .new_edge = edge_data_edge_index}; + new_edges[j] = edge_data; + for (uint k = 0; k < 2; k++) { + if (faces[k] != NULL) { + ml = orig_mloop + faces[k]->face->loopstart; + for (int l = 0; l < faces[k]->face->totloop; l++, ml++) { + if (edge_adj_faces[ml->e] == edge_adj_faces[i]) { + if (ml->e != i && orig_edge_data_arr[ml->e] == NULL) { + orig_edge_data_arr[ml->e] = new_edges; + } + faces[k]->link_edges[l] = edge_data; + break; + } + } + } + } + } + MEM_freeN(sorted_faces); + orig_edge_data_arr[i] = new_edges; + if (do_shell || (adj_len == 1 && do_rim)) { + numNewEdges += new_edges_len; + } + } + } + } + + for (uint i = 0; i < numEdges; i++) { + if (edge_adj_faces[i]) { + if (edge_adj_faces[i]->used > 1) { + edge_adj_faces[i]->used--; + } + else { + MEM_freeN(edge_adj_faces[i]->faces); + MEM_freeN(edge_adj_faces[i]->faces_reversed); + MEM_freeN(edge_adj_faces[i]); + } + } + } + MEM_freeN(edge_adj_faces); + } + + MEM_freeN(vert_adj_edges_len); + + /* Create sorted edge groups for every vert. */ + { + OldVertEdgeRef **adj_edges_ptr = vert_adj_edges; + for (uint i = 0; i < numVerts; i++, adj_edges_ptr++) { + if (*adj_edges_ptr != NULL && (*adj_edges_ptr)->edges_len >= 2) { + EdgeGroup *edge_groups; + + int eg_index = -1; + bool contains_long_groups = false; + uint topo_groups = 0; + + /* Initial sorted creation. */ + { + const uint *adj_edges = (*adj_edges_ptr)->edges; + const uint tot_adj_edges = (*adj_edges_ptr)->edges_len; + + uint unassigned_edges_len = 0; + for (uint j = 0; j < tot_adj_edges; j++) { + NewEdgeRef **new_edges = orig_edge_data_arr[adj_edges[j]]; + /* TODO check where the null pointer come from, + * because there should not be any... */ + if (new_edges) { + while (*new_edges) { + unassigned_edges_len++; + new_edges++; + } + } + } + NewEdgeRef **unassigned_edges = MEM_malloc_arrayN( + unassigned_edges_len, sizeof(*unassigned_edges), "unassigned_edges in solidify"); + for (uint j = 0, k = 0; j < tot_adj_edges; j++) { + NewEdgeRef **new_edges = orig_edge_data_arr[adj_edges[j]]; + if (new_edges) { + while (*new_edges) { + unassigned_edges[k++] = *new_edges; + new_edges++; + } + } + } + + edge_groups = MEM_calloc_arrayN( + (unassigned_edges_len / 2) + 1, sizeof(*edge_groups), "edge_groups in solidify"); + + uint assigned_edges_len = 0; + NewEdgeRef *found_edge = NULL; + uint found_edge_index = 0; + bool insert_at_start = false; + uint eg_capacity = 5; + NewFaceRef *eg_track_faces[2] = {NULL, NULL}; + NewFaceRef *last_open_edge_track = NULL; + NewEdgeRef *edge = NULL; + + while (assigned_edges_len < unassigned_edges_len) { + found_edge = NULL; + insert_at_start = false; + if (eg_index >= 0 && edge_groups[eg_index].edges_len == 0) { + uint j = 0; + edge = NULL; + while (!edge && j < unassigned_edges_len) { + edge = unassigned_edges[j++]; + if (edge && last_open_edge_track && + (edge->faces[0] != last_open_edge_track || edge->faces[1] != NULL)) { + edge = NULL; + } + } + if (!edge && last_open_edge_track) { + topo_groups++; + last_open_edge_track = NULL; + edge_groups[eg_index].topo_group++; + j = 0; + while (!edge && j < unassigned_edges_len) { + edge = unassigned_edges[j++]; + } + } + else if (!last_open_edge_track && eg_index > 0) { + topo_groups++; + edge_groups[eg_index].topo_group++; + } + BLI_assert(edge != NULL); + found_edge_index = j - 1; + found_edge = edge; + if (!last_open_edge_track && vm[orig_medge[edge->old_edge].v1] == i) { + eg_track_faces[0] = edge->faces[0]; + eg_track_faces[1] = edge->faces[1]; + if (edge->faces[1] == NULL) { + last_open_edge_track = edge->faces[0]->reversed ? edge->faces[0] - 1 : + edge->faces[0] + 1; + } + } + else { + eg_track_faces[0] = edge->faces[1]; + eg_track_faces[1] = edge->faces[0]; + } + } + else if (eg_index >= 0) { + NewEdgeRef **edge_ptr = unassigned_edges; + for (found_edge_index = 0; found_edge_index < unassigned_edges_len; + found_edge_index++, edge_ptr++) { + if (*edge_ptr) { + edge = *edge_ptr; + if (edge->faces[0] == eg_track_faces[1]) { + insert_at_start = false; + eg_track_faces[1] = edge->faces[1]; + found_edge = edge; + if (edge->faces[1] == NULL) { + edge_groups[eg_index].is_orig_closed = false; + last_open_edge_track = edge->faces[0]->reversed ? edge->faces[0] - 1 : + edge->faces[0] + 1; + } + break; + } + else if (edge->faces[0] == eg_track_faces[0]) { + insert_at_start = true; + eg_track_faces[0] = edge->faces[1]; + found_edge = edge; + if (edge->faces[1] == NULL) { + edge_groups[eg_index].is_orig_closed = false; + } + break; + } + else if (edge->faces[1] != NULL) { + if (edge->faces[1] == eg_track_faces[1]) { + insert_at_start = false; + eg_track_faces[1] = edge->faces[0]; + found_edge = edge; + break; + } + else if (edge->faces[1] == eg_track_faces[0]) { + insert_at_start = true; + eg_track_faces[0] = edge->faces[0]; + found_edge = edge; + break; + } + } + } + } + } + if (found_edge) { + unassigned_edges[found_edge_index] = NULL; + assigned_edges_len++; + const uint needed_capacity = edge_groups[eg_index].edges_len + 1; + if (needed_capacity > eg_capacity) { + eg_capacity = needed_capacity + 1; + NewEdgeRef **new_eg = MEM_calloc_arrayN( + eg_capacity, sizeof(*new_eg), "edge_group realloc in solidify"); + if (insert_at_start) { + memcpy(new_eg + 1, + edge_groups[eg_index].edges, + edge_groups[eg_index].edges_len * sizeof(*new_eg)); + } + else { + memcpy(new_eg, + edge_groups[eg_index].edges, + edge_groups[eg_index].edges_len * sizeof(*new_eg)); + } + MEM_freeN(edge_groups[eg_index].edges); + edge_groups[eg_index].edges = new_eg; + } + else if (insert_at_start) { + memmove(edge_groups[eg_index].edges + 1, + edge_groups[eg_index].edges, + edge_groups[eg_index].edges_len * sizeof(*edge_groups[eg_index].edges)); + } + edge_groups[eg_index].edges[insert_at_start ? 0 : edge_groups[eg_index].edges_len] = + found_edge; + edge_groups[eg_index].edges_len++; + if (edge_groups[eg_index].edges[edge_groups[eg_index].edges_len - 1]->faces[1] != + NULL) { + last_open_edge_track = NULL; + } + if (edge_groups[eg_index].edges_len > 3) { + contains_long_groups = true; + } + } + else { + eg_index++; + BLI_assert(eg_index < (unassigned_edges_len / 2)); + eg_capacity = 5; + NewEdgeRef **edges = MEM_calloc_arrayN( + eg_capacity, sizeof(*edges), "edge_group in solidify"); + edge_groups[eg_index] = (EdgeGroup){ + .valid = true, + .edges = edges, + .edges_len = 0, + .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, + .is_orig_closed = true, + .is_even_split = false, + .split = 0, + .is_singularity = false, + .topo_group = topo_groups, + .co = {0.0f, 0.0f, 0.0f}, + .no = {0.0f, 0.0f, 0.0f}, + .new_vert = MOD_SOLIDIFY_EMPTY_TAG, + }; + eg_track_faces[0] = NULL; + eg_track_faces[1] = NULL; + } + } + /* #eg_index is the number of groups from here on. */ + eg_index++; + /* #topo_groups is the number of topo groups from here on. */ + topo_groups++; + MEM_freeN(unassigned_edges); + } + + /* Split of long self intersection groups */ + { + uint splits = 0; + if (contains_long_groups) { + uint add_index = 0; + for (uint j = 0; j < eg_index; j++) { + const uint edges_len = edge_groups[j + add_index].edges_len; + if (edges_len > 3) { + bool has_doubles = false; + bool *doubles = MEM_calloc_arrayN( + edges_len, sizeof(*doubles), "doubles in solidify"); + EdgeGroup g = edge_groups[j + add_index]; + for (uint k = 0; k < edges_len; k++) { + for (uint l = k + 1; l < edges_len; l++) { + if (g.edges[k]->old_edge == g.edges[l]->old_edge) { + doubles[k] = true; + doubles[l] = true; + has_doubles = true; + } + } + } + if (has_doubles) { + const uint prior_splits = splits; + const uint prior_index = add_index; + int unique_start = -1; + int first_unique_end = -1; + int last_split = -1; + int first_split = -1; + bool first_even_split = false; + uint real_k = 0; + while (real_k < edges_len || + (g.is_orig_closed && + (real_k <= + (first_unique_end == -1 ? 0 : first_unique_end) + (int)edges_len || + first_split != last_split))) { + const uint k = real_k % edges_len; + if (!doubles[k]) { + if (first_unique_end != -1 && unique_start == -1) { + unique_start = (int)real_k; + } + } + else if (first_unique_end == -1) { + first_unique_end = (int)k; + } + else if (unique_start != -1) { + const uint split = (((uint)unique_start + real_k + 1) / 2) % edges_len; + const bool is_even_split = (((uint)unique_start + real_k) & 1); + if (last_split != -1) { + /* Override g on first split (no insert). */ + if (prior_splits != splits) { + memmove(edge_groups + j + add_index + 1, + edge_groups + j + add_index, + ((uint)eg_index - j) * sizeof(*edge_groups)); + add_index++; + } + if (last_split > split) { + const uint size = (split + edges_len) - (uint)last_split; + NewEdgeRef **edges = MEM_malloc_arrayN( + size, sizeof(*edges), "edge_group split in solidify"); + memcpy(edges, + g.edges + last_split, + (edges_len - (uint)last_split) * sizeof(*edges)); + memcpy(edges + (edges_len - (uint)last_split), + g.edges, + split * sizeof(*edges)); + edge_groups[j + add_index] = (EdgeGroup){ + .valid = true, + .edges = edges, + .edges_len = size, + .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, + .is_orig_closed = g.is_orig_closed, + .is_even_split = is_even_split, + .split = add_index - prior_index + 1 + (uint)!g.is_orig_closed, + .is_singularity = false, + .topo_group = g.topo_group, + .co = {0.0f, 0.0f, 0.0f}, + .no = {0.0f, 0.0f, 0.0f}, + .new_vert = MOD_SOLIDIFY_EMPTY_TAG, + }; + } + else { + const uint size = split - (uint)last_split; + NewEdgeRef **edges = MEM_malloc_arrayN( + size, sizeof(*edges), "edge_group split in solidify"); + memcpy(edges, g.edges + last_split, size * sizeof(*edges)); + edge_groups[j + add_index] = (EdgeGroup){ + .valid = true, + .edges = edges, + .edges_len = size, + .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, + .is_orig_closed = g.is_orig_closed, + .is_even_split = is_even_split, + .split = add_index - prior_index + 1 + (uint)!g.is_orig_closed, + .is_singularity = false, + .topo_group = g.topo_group, + .co = {0.0f, 0.0f, 0.0f}, + .no = {0.0f, 0.0f, 0.0f}, + .new_vert = MOD_SOLIDIFY_EMPTY_TAG, + }; + } + splits++; + } + last_split = (int)split; + if (first_split == -1) { + first_split = (int)split; + first_even_split = is_even_split; + } + unique_start = -1; + } + real_k++; + } + if (first_split != -1) { + if (!g.is_orig_closed) { + if (prior_splits != splits) { + memmove(edge_groups + (j + prior_index + 1), + edge_groups + (j + prior_index), + ((uint)eg_index + add_index - (j + prior_index)) * + sizeof(*edge_groups)); + memmove(edge_groups + (j + add_index + 2), + edge_groups + (j + add_index + 1), + ((uint)eg_index - j) * sizeof(*edge_groups)); + add_index++; + } + else { + memmove(edge_groups + (j + add_index + 2), + edge_groups + (j + add_index + 1), + ((uint)eg_index - j - 1) * sizeof(*edge_groups)); + } + NewEdgeRef **edges = MEM_malloc_arrayN( + (uint)first_split, sizeof(*edges), "edge_group split in solidify"); + memcpy(edges, g.edges, (uint)first_split * sizeof(*edges)); + edge_groups[j + prior_index] = (EdgeGroup){ + .valid = true, + .edges = edges, + .edges_len = (uint)first_split, + .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, + .is_orig_closed = g.is_orig_closed, + .is_even_split = first_even_split, + .split = 1, + .is_singularity = false, + .topo_group = g.topo_group, + .co = {0.0f, 0.0f, 0.0f}, + .no = {0.0f, 0.0f, 0.0f}, + .new_vert = MOD_SOLIDIFY_EMPTY_TAG, + }; + add_index++; + splits++; + edges = MEM_malloc_arrayN(edges_len - (uint)last_split, + sizeof(*edges), + "edge_group split in solidify"); + memcpy(edges, + g.edges + last_split, + (edges_len - (uint)last_split) * sizeof(*edges)); + edge_groups[j + add_index] = (EdgeGroup){ + .valid = true, + .edges = edges, + .edges_len = (edges_len - (uint)last_split), + .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, + .is_orig_closed = g.is_orig_closed, + .is_even_split = false, + .split = add_index - prior_index + 1, + .is_singularity = false, + .topo_group = g.topo_group, + .co = {0.0f, 0.0f, 0.0f}, + .no = {0.0f, 0.0f, 0.0f}, + .new_vert = MOD_SOLIDIFY_EMPTY_TAG, + }; + } + if (prior_splits != splits) { + MEM_freeN(g.edges); + } + } + if (first_unique_end != -1 && prior_splits == splits) { + has_singularities = true; + edge_groups[j + add_index].is_singularity = true; + } + } + MEM_freeN(doubles); + } + } + } + } + + orig_vert_groups_arr[i] = edge_groups; + /* Count new edges, loops, polys and add to link_edge_groups. */ + { + uint new_verts = 0; + bool contains_open_splits = false; + uint open_edges = 0; + uint contains_splits = 0; + uint last_added = 0; + uint first_added = 0; + bool first_set = false; + for (EdgeGroup *g = edge_groups; g->valid; g++) { + NewEdgeRef **e = g->edges; + for (uint j = 0; j < g->edges_len; j++, e++) { + const uint flip = (uint)(vm[orig_medge[(*e)->old_edge].v2] == i); + BLI_assert(flip || vm[orig_medge[(*e)->old_edge].v1] == i); + (*e)->link_edge_groups[flip] = g; + } + uint added = 0; + if (do_shell || (do_rim && !g->is_orig_closed)) { + BLI_assert(g->new_vert == MOD_SOLIDIFY_EMPTY_TAG); + g->new_vert = numNewVerts++; + if (do_rim || (do_shell && g->split)) { + new_verts++; + contains_splits += (g->split != 0); + contains_open_splits |= g->split && !g->is_orig_closed; + added = g->split; + } + } + open_edges += (uint)(added < last_added); + if (!first_set) { + first_set = true; + first_added = added; + } + last_added = added; + if (!(g + 1)->valid || g->topo_group != (g + 1)->topo_group) { + if (new_verts > 2) { + numNewPolys++; + numNewEdges += new_verts; + open_edges += (uint)(first_added < last_added); + open_edges -= (uint)(open_edges && !contains_open_splits); + if (do_shell && do_rim) { + numNewLoops += new_verts * 2; + } + else if (do_shell) { + numNewLoops += new_verts * 2 - open_edges; + } + else { // do_rim + numNewLoops += new_verts * 2 + open_edges - contains_splits; + } + } + else if (new_verts == 2) { + numNewEdges++; + numNewLoops += 2u - (uint)(!(do_rim && do_shell) && contains_open_splits); + } + new_verts = 0; + contains_open_splits = false; + contains_splits = 0; + open_edges = 0; + last_added = 0; + first_added = 0; + first_set = false; + } + } + } + } + } + } + + /* Free vert_adj_edges memory. */ + { + uint i = 0; + for (OldVertEdgeRef **p = vert_adj_edges; i < numVerts; i++, p++) { + if (*p) { + MEM_freeN((*p)->edges); + MEM_freeN(*p); + } + } + MEM_freeN(vert_adj_edges); + } + + /* TODO create_regions if fix_intersections. */ + + /* General use pointer for #EdgeGroup iteration. */ + EdgeGroup **gs_ptr; + + /* Calculate EdgeGroup vertex coordinates. */ + { + mv = orig_mvert; + gs_ptr = orig_vert_groups_arr; + for (uint i = 0; i < numVerts; i++, mv++, gs_ptr++) { + if (*gs_ptr) { + EdgeGroup *g = *gs_ptr; + for (uint j = 0; g->valid; j++, g++) { + if (!g->is_singularity) { + float *nor = g->no; + float move_nor[3] = {0, 0, 0}; + bool disable_boundary_fix = (smd->nonmanifold_boundary_mode == + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE || + (g->is_orig_closed || g->split)); + /* Constraints Method. */ + if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) { + NewEdgeRef *first_edge = NULL; + NewEdgeRef **edge_ptr = g->edges; + /* Contains normal and offset [nx, ny, nz, ofs]. */ + float(*normals_queue)[4] = MEM_malloc_arrayN( + g->edges_len + 1, sizeof(*normals_queue), "normals_queue in solidify"); + uint queue_index = 0; + + float face_nors[3][3]; + float nor_ofs[3]; + + const bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split; + for (uint k = 0; k < g->edges_len; k++, edge_ptr++) { + if (!(k & 1) || (!cycle && k == g->edges_len - 1)) { + NewEdgeRef *edge = *edge_ptr; + for (uint l = 0; l < 2; l++) { + NewFaceRef *face = edge->faces[l]; + if (face && (first_edge == NULL || + (first_edge->faces[0] != face && first_edge->faces[1] != face))) { + if (!null_faces[face->index]) { + mul_v3_v3fl(normals_queue[queue_index], + poly_nors[face->index], + face->reversed ? -1 : 1); + normals_queue[queue_index++][3] = face->reversed ? ofs_back : ofs_front; + } + else { + mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1); + nor_ofs[0] = face->reversed ? ofs_back : ofs_front; + } + } + } + if ((cycle && k == 0) || (!cycle && k + 3 >= g->edges_len)) { + first_edge = edge; + } + } + } + uint face_nors_len = 0; + const float stop_explosion = 1 - fabsf(smd->offset_fac) * 0.05f; + while (queue_index > 0) { + if (face_nors_len == 0) { + if (queue_index <= 2) { + for (uint k = 0; k < queue_index; k++) { + copy_v3_v3(face_nors[k], normals_queue[k]); + nor_ofs[k] = normals_queue[k][3]; + } + face_nors_len = queue_index; + queue_index = 0; + } + else { + /* Find most different two normals. */ + float min_p = 2; + uint min_n0 = 0; + uint min_n1 = 0; + for (uint k = 0; k < queue_index; k++) { + for (uint m = k + 1; m < queue_index; m++) { + float p = dot_v3v3(normals_queue[k], normals_queue[m]); + if (p <= min_p + FLT_EPSILON) { + min_p = p; + min_n0 = m; + min_n1 = k; + } + } + } + copy_v3_v3(face_nors[0], normals_queue[min_n0]); + copy_v3_v3(face_nors[1], normals_queue[min_n1]); + nor_ofs[0] = normals_queue[min_n0][3]; + nor_ofs[1] = normals_queue[min_n1][3]; + face_nors_len = 2; + queue_index--; + memmove(normals_queue + min_n0, + normals_queue + min_n0 + 1, + (queue_index - min_n0) * sizeof(*normals_queue)); + queue_index--; + memmove(normals_queue + min_n1, + normals_queue + min_n1 + 1, + (queue_index - min_n1) * sizeof(*normals_queue)); + min_p = 1; + min_n1 = 0; + float max_p = -1; + for (uint k = 0; k < queue_index; k++) { + max_p = -1; + for (uint m = 0; m < face_nors_len; m++) { + float p = dot_v3v3(face_nors[m], normals_queue[k]); + if (p > max_p + FLT_EPSILON) { + max_p = p; + } + } + if (max_p <= min_p + FLT_EPSILON) { + min_p = max_p; + min_n1 = k; + } + } + if (min_p < 0.8) { + copy_v3_v3(face_nors[2], normals_queue[min_n1]); + nor_ofs[2] = normals_queue[min_n1][3]; + face_nors_len++; + queue_index--; + memmove(normals_queue + min_n1, + normals_queue + min_n1 + 1, + (queue_index - min_n1) * sizeof(*normals_queue)); + } + } + } + else { + uint best = 0; + uint best_group = 0; + float best_p = -1.0f; + for (uint k = 0; k < queue_index; k++) { + for (uint m = 0; m < face_nors_len; m++) { + float p = dot_v3v3(face_nors[m], normals_queue[k]); + if (p > best_p + FLT_EPSILON) { + best_p = p; + best = m; + best_group = k; + } + } + } + add_v3_v3(face_nors[best], normals_queue[best_group]); + normalize_v3(face_nors[best]); + nor_ofs[best] = (nor_ofs[best] + normals_queue[best_group][3]) * 0.5f; + queue_index--; + memmove(normals_queue + best_group, + normals_queue + best_group + 1, + (queue_index - best_group) * sizeof(*normals_queue)); + } + } + MEM_freeN(normals_queue); + /* When up to 3 constraint normals are found. */ + float d, q; + switch (face_nors_len) { + case 0: + mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); + disable_boundary_fix = true; + break; + case 1: + mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); + disable_boundary_fix = true; + break; + case 2: + q = dot_v3v3(face_nors[0], face_nors[1]); + d = 1.0f - q * q; + if (LIKELY(d > FLT_EPSILON) && q < stop_explosion) { + d = 1.0f / d; + mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); + mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); + add_v3_v3v3(nor, face_nors[0], face_nors[1]); + } + else { + mul_v3_fl(face_nors[0], nor_ofs[0] * 0.5f); + mul_v3_fl(face_nors[1], nor_ofs[1] * 0.5f); + add_v3_v3v3(nor, face_nors[0], face_nors[1]); + } + if (!disable_boundary_fix) { + cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]); + } + break; + case 3: + q = dot_v3v3(face_nors[0], face_nors[1]); + d = 1.0f - q * q; + float *free_nor = move_nor; /* No need to allocate a new array. */ + cross_v3_v3v3(free_nor, face_nors[0], face_nors[1]); + if (LIKELY(d > FLT_EPSILON) && q < stop_explosion) { + d = 1.0f / d; + mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); + mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); + add_v3_v3v3(nor, face_nors[0], face_nors[1]); + } + else { + mul_v3_fl(face_nors[0], nor_ofs[0] * 0.5f); + mul_v3_fl(face_nors[1], nor_ofs[1] * 0.5f); + add_v3_v3v3(nor, face_nors[0], face_nors[1]); + } + mul_v3_fl(face_nors[2], nor_ofs[2]); + d = dot_v3v3(face_nors[2], free_nor); + if (LIKELY(fabsf(d) > FLT_EPSILON)) { + sub_v3_v3v3(face_nors[0], nor, face_nors[2]); /* Override face_nor[0]. */ + mul_v3_fl(free_nor, dot_v3v3(face_nors[2], face_nors[0]) / d); + sub_v3_v3(nor, free_nor); + } + disable_boundary_fix = true; + break; + default: + BLI_assert(0); + } + } + /* Simple/Even Method. */ + else { + float total_angle = 0; + float total_angle_back = 0; + NewEdgeRef *first_edge = NULL; + NewEdgeRef **edge_ptr = g->edges; + float face_nor[3]; + float nor_back[3] = {0, 0, 0}; + bool has_back = false; + bool has_front = false; + bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split; + for (uint k = 0; k < g->edges_len; k++, edge_ptr++) { + if (!(k & 1) || (!cycle && k == g->edges_len - 1)) { + NewEdgeRef *edge = *edge_ptr; + for (uint l = 0; l < 2; l++) { + NewFaceRef *face = edge->faces[l]; + if (face && (first_edge == NULL || + (first_edge->faces[0] != face && first_edge->faces[1] != face))) { + float angle = 1.0f; + float ofs = face->reversed ? -max_ff(1.0e-5f, ofs_back) : + max_ff(1.0e-5f, ofs_front); + if (smd->nonmanifold_offset_mode == + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { + MLoop *ml_next = orig_mloop + face->face->loopstart; + ml = ml_next + (face->face->totloop - 1); + MLoop *ml_prev = ml - 1; + for (int m = 0; m < face->face->totloop && vm[ml->v] != i; + m++, ml_next++) { + ml_prev = ml; + ml = ml_next; + } + angle = angle_v3v3v3( + orig_mvert[vm[ml_prev->v]].co, mv->co, orig_mvert[vm[ml_next->v]].co); + if (face->reversed) { + total_angle_back += angle * ofs * ofs; + } + else { + total_angle += angle * ofs * ofs; + } + } + else { + if (face->reversed) { + total_angle_back++; + } + else { + total_angle++; + } + } + mul_v3_v3fl(face_nor, poly_nors[face->index], angle * ofs); + if (face->reversed) { + add_v3_v3(nor_back, face_nor); + has_back = true; + } + else { + add_v3_v3(nor, face_nor); + has_front = true; + } + } + } + if ((cycle && k == 0) || (!cycle && k + 3 >= g->edges_len)) { + first_edge = edge; + } + } + } + + /* Set normal length with selected method. */ + if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { + float d = dot_v3v3(nor, nor_back); + if (has_front) { + float length = len_squared_v3(nor); + if (LIKELY(length > FLT_EPSILON)) { + mul_v3_fl(nor, total_angle / length); + } + } + if (has_back) { + float length = len_squared_v3(nor_back); + if (LIKELY(length > FLT_EPSILON)) { + mul_v3_fl(nor_back, total_angle_back / length); + } + if (!has_front) { + copy_v3_v3(nor, nor_back); + } + } + if (has_front && has_back) { + float nor_length = len_v3(nor); + float nor_back_length = len_v3(nor_back); + float q = dot_v3v3(nor, nor_back); + if (LIKELY(fabsf(q) > FLT_EPSILON)) { + q /= nor_length * nor_back_length; + } + d = 1.0f - q * q; + if (LIKELY(d > FLT_EPSILON)) { + d = 1.0f / d; + if (LIKELY(nor_length > FLT_EPSILON)) { + mul_v3_fl(nor, (1 - nor_back_length * q / nor_length) * d); + } + if (LIKELY(nor_back_length > FLT_EPSILON)) { + mul_v3_fl(nor_back, (1 - nor_length * q / nor_back_length) * d); + } + add_v3_v3(nor, nor_back); + } + else { + mul_v3_fl(nor, 0.5f); + mul_v3_fl(nor_back, 0.5f); + add_v3_v3(nor, nor_back); + } + } + } + else { + if (has_front && total_angle > FLT_EPSILON) { + mul_v3_fl(nor, 1.0f / total_angle); + } + if (has_back && total_angle_back > FLT_EPSILON) { + mul_v3_fl(nor_back, 1.0f / total_angle_back); + add_v3_v3(nor, nor_back); + } + } + /* Set move_nor for boundary fix. */ + if (!disable_boundary_fix && g->edges_len > 2) { + edge_ptr = g->edges + 1; + float tmp[3]; + uint k; + for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) { + MEdge *e = orig_medge + (*edge_ptr)->old_edge; + sub_v3_v3v3(tmp, orig_mvert[vm[e->v1] == i ? e->v2 : e->v1].co, mv->co); + add_v3_v3(move_nor, tmp); + } + if (k == 1) { + disable_boundary_fix = true; + } + else { + disable_boundary_fix = normalize_v3(move_nor) == 0.0f; + } + } + else { + disable_boundary_fix = true; + } + } + /* Fix boundary verts. */ + if (!disable_boundary_fix) { + /* Constraint normal, nor * constr_nor == 0 after this fix. */ + float constr_nor[3]; + MEdge *e0_edge = orig_medge + g->edges[0]->old_edge; + MEdge *e1_edge = orig_medge + g->edges[g->edges_len - 1]->old_edge; + float e0[3]; + float e1[3]; + sub_v3_v3v3( + e0, orig_mvert[vm[e0_edge->v1] == i ? e0_edge->v2 : e0_edge->v1].co, mv->co); + sub_v3_v3v3( + e1, orig_mvert[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1].co, mv->co); + if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) { + cross_v3_v3v3(constr_nor, e0, e1); + } + else { + float f0[3]; + float f1[3]; + if (g->edges[0]->faces[0]->reversed) { + negate_v3_v3(f0, poly_nors[g->edges[0]->faces[0]->index]); + } + else { + copy_v3_v3(f0, poly_nors[g->edges[0]->faces[0]->index]); + } + if (g->edges[g->edges_len - 1]->faces[0]->reversed) { + negate_v3_v3(f1, poly_nors[g->edges[g->edges_len - 1]->faces[0]->index]); + } + else { + copy_v3_v3(f1, poly_nors[g->edges[g->edges_len - 1]->faces[0]->index]); + } + float n0[3]; + float n1[3]; + cross_v3_v3v3(n0, e0, f0); + cross_v3_v3v3(n1, f1, e1); + normalize_v3(n0); + normalize_v3(n1); + add_v3_v3v3(constr_nor, n0, n1); + } + float d = dot_v3v3(constr_nor, move_nor); + if (LIKELY(fabsf(d) > FLT_EPSILON)) { + mul_v3_fl(move_nor, dot_v3v3(constr_nor, nor) / d); + sub_v3_v3(nor, move_nor); + } + } + float scalar_vgroup = 1; + /* Use vertex group. */ + if (dvert) { + MDeformVert *dv = &dvert[i]; + if (defgrp_invert) { + scalar_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); + } + else { + scalar_vgroup = defvert_find_weight(dv, defgrp_index); + } + scalar_vgroup = offset_fac_vg + (scalar_vgroup * offset_fac_vg_inv); + } + /* Do clamping. */ + if (do_clamp) { + if (do_angle_clamp) { + float min_length = 0; + float angle = 0.5f * M_PI; + uint k = 0; + for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) { + float length = orig_edge_lengths[(*p)->old_edge]; + float e_ang = (*p)->angle; + if (e_ang > angle) { + angle = e_ang; + } + if (length < min_length || k == 0) { + min_length = length; + } + } + float cos_ang = cosf(angle * 0.5f); + if (cos_ang > 0) { + float max_off = min_length * 0.5f / cos_ang; + if (max_off < offset * 0.5f) { + scalar_vgroup *= max_off / offset * 2; + } + } + } + else { + float min_length = 0; + uint k = 0; + for (NewEdgeRef **p = g->edges; k < g->edges_len; k++, p++) { + float length = orig_edge_lengths[(*p)->old_edge]; + if (length < min_length || k == 0) { + min_length = length; + } + } + if (min_length < offset) { + scalar_vgroup *= min_length / offset; + } + } + } + mul_v3_fl(nor, scalar_vgroup); + add_v3_v3v3(g->co, nor, mv->co); + } + else { + copy_v3_v3(g->co, mv->co); + } + } + } + } + } + + if (null_faces) { + MEM_freeN(null_faces); + } + + /* TODO create vertdata for intersection fixes (intersection fixing per topology region). */ + + /* Correction for adjacent one sided groups around a vert to + * prevent edge duplicates and null polys. */ + uint(*singularity_edges)[2] = NULL; + uint totsingularity = 0; + if (has_singularities) { + has_singularities = false; + uint i = 0; + uint singularity_edges_len = 1; + singularity_edges = MEM_malloc_arrayN( + singularity_edges_len, sizeof(*singularity_edges), "singularity_edges in solidify"); + for (NewEdgeRef ***new_edges = orig_edge_data_arr; i < numEdges; i++, new_edges++) { + if (*new_edges && (do_shell || edge_adj_faces_len[i] == 1) && (**new_edges)->old_edge == i) { + for (NewEdgeRef **l = *new_edges; *l; l++) { + if ((*l)->link_edge_groups[0]->is_singularity && + (*l)->link_edge_groups[1]->is_singularity) { + const uint v1 = (*l)->link_edge_groups[0]->new_vert; + const uint v2 = (*l)->link_edge_groups[1]->new_vert; + bool exists_already = false; + uint j = 0; + for (uint(*p)[2] = singularity_edges; j < totsingularity; p++, j++) { + if (((*p)[0] == v1 && (*p)[1] == v2) || ((*p)[0] == v2 && (*p)[1] == v1)) { + exists_already = true; + break; + } + } + if (!exists_already) { + has_singularities = true; + if (singularity_edges_len <= totsingularity) { + singularity_edges_len = totsingularity + 1; + singularity_edges = MEM_reallocN_id(singularity_edges, + singularity_edges_len * + sizeof(*singularity_edges), + "singularity_edges in solidify"); + } + singularity_edges[totsingularity][0] = v1; + singularity_edges[totsingularity][1] = v2; + totsingularity++; + if (edge_adj_faces_len[i] == 1 && do_rim) { + numNewLoops -= 2; + numNewPolys--; + } + } + else { + numNewEdges--; + } + } + } + } + } + } + + /* Create Mesh *result with proper capacity. */ + result = BKE_mesh_new_nomain_from_template( + mesh, (int)(numNewVerts), (int)(numNewEdges), 0, (int)(numNewLoops), (int)(numNewPolys)); + + mpoly = result->mpoly; + mloop = result->mloop; + medge = result->medge; + mvert = result->mvert; + + int *origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX); + int *origindex_poly = CustomData_get_layer(&result->pdata, CD_ORIGINDEX); + + /* Make_new_verts. */ + { + gs_ptr = orig_vert_groups_arr; + for (uint i = 0; i < numVerts; i++, gs_ptr++) { + EdgeGroup *gs = *gs_ptr; + if (gs) { + EdgeGroup *g = gs; + for (uint j = 0; g->valid; j++, g++) { + if (g->new_vert != MOD_SOLIDIFY_EMPTY_TAG) { + CustomData_copy_data(&mesh->vdata, &result->vdata, (int)i, (int)g->new_vert, 1); + copy_v3_v3(mvert[g->new_vert].co, g->co); + mvert[g->new_vert].flag = orig_mvert[i].flag; + } + } + } + } + } + + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + + /* Make edges. */ + { + uint i = 0; + edge_index += totsingularity; + for (NewEdgeRef ***new_edges = orig_edge_data_arr; i < numEdges; i++, new_edges++) { + if (*new_edges && (do_shell || edge_adj_faces_len[i] == 1) && (**new_edges)->old_edge == i) { + for (NewEdgeRef **l = *new_edges; *l; l++) { + if ((*l)->new_edge != MOD_SOLIDIFY_EMPTY_TAG) { + const uint v1 = (*l)->link_edge_groups[0]->new_vert; + const uint v2 = (*l)->link_edge_groups[1]->new_vert; + uint insert = edge_index; + if (has_singularities && ((*l)->link_edge_groups[0]->is_singularity && + (*l)->link_edge_groups[1]->is_singularity)) { + uint j = 0; + for (uint(*p)[2] = singularity_edges; j < totsingularity; p++, j++) { + if (((*p)[0] == v1 && (*p)[1] == v2) || ((*p)[0] == v2 && (*p)[1] == v1)) { + insert = j; + break; + } + } + BLI_assert(insert == j); + } + else { + edge_index++; + } + CustomData_copy_data(&mesh->edata, &result->edata, (int)i, (int)insert, 1); + BLI_assert(v1 != MOD_SOLIDIFY_EMPTY_TAG); + BLI_assert(v2 != MOD_SOLIDIFY_EMPTY_TAG); + medge[insert].v1 = v1; + medge[insert].v2 = v2; + medge[insert].flag = orig_medge[(*l)->old_edge].flag | ME_EDGEDRAW | ME_EDGERENDER; + medge[insert].crease = orig_medge[(*l)->old_edge].crease; + medge[insert].bweight = orig_medge[(*l)->old_edge].bweight; + (*l)->new_edge = insert; + } + } + } + } + } + if (singularity_edges) { + MEM_freeN(singularity_edges); + } + + /* DEBUG CODE FOR BUGFIXING (can not be removed because every bugfix needs this badly!). */ +#if 0 + { + gs_ptr = orig_vert_groups_arr; + for (uint i = 0; i < numVerts; i++, gs_ptr++) { + EdgeGroup *gs = *gs_ptr; + if (gs) { + for (EdgeGroup *g = gs; g->valid; g++) { + NewEdgeRef **e = g->edges; + for (uint j = 0; j < g->edges_len; j++, e++) { + printf("%u/%d, ", (*e)->old_edge, (int)(*e)->new_edge); + } + printf("(tg:%u)(s:%u,c:%d)\n", g->topo_group, g->split, g->is_orig_closed); + } + printf("\n"); + } + } + } +#endif + + /* Make boundary edges/faces. */ + { + gs_ptr = orig_vert_groups_arr; + for (uint i = 0; i < numVerts; i++, gs_ptr++) { + EdgeGroup *gs = *gs_ptr; + if (gs) { + EdgeGroup *g = gs; + EdgeGroup *g2 = gs; + EdgeGroup *last_g = NULL; + EdgeGroup *first_g = NULL; + /* Data calculation cache. */ + char max_crease; + char last_max_crease = 0; + char first_max_crease = 0; + char max_bweight; + char last_max_bweight = 0; + char first_max_bweight = 0; + short flag; + short last_flag = 0; + short first_flag = 0; + for (uint j = 0; g->valid; g++) { + if ((do_rim && !g->is_orig_closed) || (do_shell && g->split)) { + max_crease = 0; + max_bweight = 0; + flag = 0; + for (uint k = 1; k < g->edges_len - 1; k++) { + ed = orig_medge + g->edges[k]->old_edge; + if (ed->crease > max_crease) { + max_crease = ed->crease; + } + if (ed->bweight > max_bweight) { + max_bweight = ed->bweight; + } + flag |= ed->flag; + } + if (!first_g) { + first_g = g; + first_max_crease = max_crease; + first_max_bweight = max_bweight; + first_flag = flag; + } + else { + last_g->open_face_edge = edge_index; + CustomData_copy_data(&mesh->edata, + &result->edata, + (int)last_g->edges[0]->old_edge, + (int)edge_index, + 1); + if (origindex_edge) { + origindex_edge[edge_index] = ORIGINDEX_NONE; + } + medge[edge_index].v1 = last_g->new_vert; + medge[edge_index].v2 = g->new_vert; + medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER | + ((last_flag | flag) & (ME_SEAM | ME_SHARP)); + medge[edge_index].crease = MAX2(last_max_crease, max_crease); + medge[edge_index++].bweight = MAX2(last_max_bweight, max_bweight); + } + last_g = g; + last_max_crease = max_crease; + last_max_bweight = max_bweight; + last_flag = flag; + j++; + } + if (!(g + 1)->valid || g->topo_group != (g + 1)->topo_group) { + if (j == 2) { + last_g->open_face_edge = edge_index - 1; + } + if (j > 2) { + CustomData_copy_data(&mesh->edata, + &result->edata, + (int)last_g->edges[0]->old_edge, + (int)edge_index, + 1); + if (origindex_edge) { + origindex_edge[edge_index] = ORIGINDEX_NONE; + } + last_g->open_face_edge = edge_index; + medge[edge_index].v1 = last_g->new_vert; + medge[edge_index].v2 = first_g->new_vert; + medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER | + ((last_flag | first_flag) & (ME_SEAM | ME_SHARP)); + medge[edge_index].crease = MAX2(last_max_crease, first_max_crease); + medge[edge_index++].bweight = MAX2(last_max_bweight, first_max_bweight); + + /* Loop data. */ + int *loops = MEM_malloc_arrayN(j, sizeof(*loops), "loops in solidify"); + /* The #mat_nr is from consensus. */ + short most_mat_nr = 0; + uint most_mat_nr_face = 0; + uint most_mat_nr_count = 0; + for (short l = 0; l < mat_nrs; l++) { + uint count = 0; + uint face = 0; + uint k = 0; + for (EdgeGroup *g3 = g2; g3->valid && k < j; g3++) { + if ((do_rim && !g3->is_orig_closed) || (do_shell && g3->split)) { + /* Check both far ends in terms of faces of an edge group. */ + if (g3->edges[0]->faces[0]->face->mat_nr == l) { + face = g3->edges[0]->faces[0]->index; + count++; + } + NewEdgeRef *le = g3->edges[g3->edges_len - 1]; + if (le->faces[1] && le->faces[1]->face->mat_nr == l) { + face = le->faces[1]->index; + count++; + } + else if (!le->faces[1] && le->faces[0]->face->mat_nr == l) { + face = le->faces[0]->index; + count++; + } + k++; + } + } + if (count > most_mat_nr_count) { + most_mat_nr = l; + most_mat_nr_face = face; + most_mat_nr_count = count; + } + } + CustomData_copy_data( + &mesh->pdata, &result->pdata, (int)most_mat_nr_face, (int)poly_index, 1); + if (origindex_poly) { + origindex_poly[poly_index] = ORIGINDEX_NONE; + } + mpoly[poly_index].loopstart = (int)loop_index; + mpoly[poly_index].totloop = (int)j; + mpoly[poly_index].mat_nr = most_mat_nr + + (g->is_orig_closed || !do_rim ? 0 : mat_ofs_rim); + CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max); + mpoly[poly_index].flag = orig_mpoly[most_mat_nr_face].flag; + poly_index++; + + for (uint k = 0; g2->valid && k < j; g2++) { + if ((do_rim && !g2->is_orig_closed) || (do_shell && g2->split)) { + MPoly *face = g2->edges[0]->faces[0]->face; + ml = orig_mloop + face->loopstart; + for (int l = 0; l < face->totloop; l++, ml++) { + if (vm[ml->v] == i) { + loops[k] = face->loopstart + l; + break; + } + } + k++; + } + } + + if (!do_flip) { + for (uint k = 0; k < j; k++) { + CustomData_copy_data(&mesh->ldata, &result->ldata, loops[k], (int)loop_index, 1); + mloop[loop_index].v = medge[edge_index - j + k].v1; + mloop[loop_index++].e = edge_index - j + k; + } + } + else { + for (uint k = 1; k <= j; k++) { + CustomData_copy_data( + &mesh->ldata, &result->ldata, loops[j - k], (int)loop_index, 1); + mloop[loop_index].v = medge[edge_index - k].v2; + mloop[loop_index++].e = edge_index - k; + } + } + MEM_freeN(loops); + } + /* Reset everything for the next poly. */ + j = 0; + last_g = NULL; + first_g = NULL; + last_max_crease = 0; + first_max_crease = 0; + last_max_bweight = 0; + first_max_bweight = 0; + last_flag = 0; + first_flag = 0; + } + } + } + } + } + + /* Make boundary faces. */ + if (do_rim) { + for (uint i = 0; i < numEdges; i++) { + if (edge_adj_faces_len[i] == 1 && orig_edge_data_arr[i] && + (*orig_edge_data_arr[i])->old_edge == i) { + NewEdgeRef **new_edges = orig_edge_data_arr[i]; + + NewEdgeRef *edge1 = new_edges[0]; + NewEdgeRef *edge2 = new_edges[1]; + const bool v1_singularity = edge1->link_edge_groups[0]->is_singularity; + const bool v2_singularity = edge1->link_edge_groups[1]->is_singularity; + if (v1_singularity && v2_singularity) { + continue; + } + + MPoly *face = (*new_edges)->faces[0]->face; + CustomData_copy_data( + &mesh->pdata, &result->pdata, (int)(*new_edges)->faces[0]->index, (int)poly_index, 1); + mpoly[poly_index].loopstart = (int)loop_index; + mpoly[poly_index].totloop = 4 - (int)(v1_singularity || v2_singularity); + mpoly[poly_index].mat_nr = face->mat_nr + mat_ofs_rim; + CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max); + mpoly[poly_index].flag = face->flag; + poly_index++; + + int loop1 = -1; + int loop2 = -1; + ml = orig_mloop + face->loopstart; + const uint old_v1 = vm[orig_medge[edge1->old_edge].v1]; + const uint old_v2 = vm[orig_medge[edge1->old_edge].v2]; + for (uint j = 0; j < face->totloop; j++, ml++) { + if (vm[ml->v] == old_v1) { + loop1 = face->loopstart + (int)j; + } + else if (vm[ml->v] == old_v2) { + loop2 = face->loopstart + (int)j; + } + } + BLI_assert(loop1 != -1 && loop2 != -1); + MEdge *open_face_edge; + uint open_face_edge_index; + if (!do_flip) { + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); + mloop[loop_index].v = medge[edge1->new_edge].v1; + mloop[loop_index++].e = edge1->new_edge; + + if (!v2_singularity) { + open_face_edge_index = edge1->link_edge_groups[1]->open_face_edge; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); + mloop[loop_index].v = medge[edge1->new_edge].v2; + open_face_edge = medge + open_face_edge_index; + if (ELEM(medge[edge2->new_edge].v2, open_face_edge->v1, open_face_edge->v2)) { + mloop[loop_index++].e = open_face_edge_index; + } + else { + mloop[loop_index++].e = edge2->link_edge_groups[1]->open_face_edge; + } + } + + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); + mloop[loop_index].v = medge[edge2->new_edge].v2; + mloop[loop_index++].e = edge2->new_edge; + + if (!v1_singularity) { + open_face_edge_index = edge2->link_edge_groups[0]->open_face_edge; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); + mloop[loop_index].v = medge[edge2->new_edge].v1; + open_face_edge = medge + open_face_edge_index; + if (ELEM(medge[edge1->new_edge].v1, open_face_edge->v1, open_face_edge->v2)) { + mloop[loop_index++].e = open_face_edge_index; + } + else { + mloop[loop_index++].e = edge1->link_edge_groups[0]->open_face_edge; + } + } + } + else { + if (!v1_singularity) { + open_face_edge_index = edge1->link_edge_groups[0]->open_face_edge; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); + mloop[loop_index].v = medge[edge1->new_edge].v1; + open_face_edge = medge + open_face_edge_index; + if (ELEM(medge[edge2->new_edge].v1, open_face_edge->v1, open_face_edge->v2)) { + mloop[loop_index++].e = open_face_edge_index; + } + else { + mloop[loop_index++].e = edge2->link_edge_groups[0]->open_face_edge; + } + } + + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); + mloop[loop_index].v = medge[edge2->new_edge].v1; + mloop[loop_index++].e = edge2->new_edge; + + if (!v2_singularity) { + open_face_edge_index = edge2->link_edge_groups[1]->open_face_edge; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); + mloop[loop_index].v = medge[edge2->new_edge].v2; + open_face_edge = medge + open_face_edge_index; + if (ELEM(medge[edge1->new_edge].v2, open_face_edge->v1, open_face_edge->v2)) { + mloop[loop_index++].e = open_face_edge_index; + } + else { + mloop[loop_index++].e = edge1->link_edge_groups[1]->open_face_edge; + } + } + + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); + mloop[loop_index].v = medge[edge1->new_edge].v2; + mloop[loop_index++].e = edge1->new_edge; + } + } + } + } + + /* Make faces. */ + if (do_shell) { + NewFaceRef *fr = face_sides_arr; + uint *face_loops = MEM_malloc_arrayN( + largest_ngon * 2, sizeof(*face_loops), "face_loops in solidify"); + uint *face_verts = MEM_malloc_arrayN( + largest_ngon * 2, sizeof(*face_verts), "face_verts in solidify"); + uint *face_edges = MEM_malloc_arrayN( + largest_ngon * 2, sizeof(*face_edges), "face_edges in solidify"); + for (uint i = 0; i < numPolys * 2; i++, fr++) { + const uint loopstart = (uint)fr->face->loopstart; + uint totloop = (uint)fr->face->totloop; + uint valid_edges = 0; + uint k = 0; + while (totloop > 0 && (!fr->link_edges[totloop - 1] || + fr->link_edges[totloop - 1]->new_edge == MOD_SOLIDIFY_EMPTY_TAG)) { + totloop--; + } + if (totloop > 0) { + NewEdgeRef *prior_edge = fr->link_edges[totloop - 1]; + uint prior_flip = (uint)(vm[orig_medge[prior_edge->old_edge].v1] == + vm[orig_mloop[loopstart + (totloop - 1)].v]); + for (uint j = 0; j < totloop; j++) { + NewEdgeRef *new_edge = fr->link_edges[j]; + if (new_edge && new_edge->new_edge != MOD_SOLIDIFY_EMPTY_TAG) { + valid_edges++; + const uint flip = (uint)(vm[orig_medge[new_edge->old_edge].v2] == + vm[orig_mloop[loopstart + j].v]); + BLI_assert(flip || + vm[orig_medge[new_edge->old_edge].v1] == vm[orig_mloop[loopstart + j].v]); + /* The vert thats in the current loop. */ + const uint new_v1 = new_edge->link_edge_groups[flip]->new_vert; + /* The vert thats in the next loop. */ + const uint new_v2 = new_edge->link_edge_groups[1 - flip]->new_vert; + if (k == 0 || face_verts[k - 1] != new_v1) { + face_loops[k] = loopstart + j; + if (fr->reversed) { + face_edges[k] = prior_edge->link_edge_groups[prior_flip]->open_face_edge; + } + else { + face_edges[k] = new_edge->link_edge_groups[flip]->open_face_edge; + } + BLI_assert(k == 0 || medge[face_edges[k]].v2 == face_verts[k - 1] || + medge[face_edges[k]].v1 == face_verts[k - 1]); + BLI_assert(face_edges[k] == MOD_SOLIDIFY_EMPTY_TAG || + medge[face_edges[k]].v2 == new_v1 || medge[face_edges[k]].v1 == new_v1); + face_verts[k++] = new_v1; + } + prior_edge = new_edge; + prior_flip = 1 - flip; + if (j < totloop - 1 || face_verts[0] != new_v2) { + face_loops[k] = loopstart + (j + 1) % totloop; + face_edges[k] = new_edge->new_edge; + face_verts[k++] = new_v2; + } + else { + face_edges[0] = new_edge->new_edge; + } + } + } + if (k > 2 && valid_edges > 2) { + CustomData_copy_data(&mesh->pdata, &result->pdata, (int)(i / 2), (int)poly_index, 1); + mpoly[poly_index].loopstart = (int)loop_index; + mpoly[poly_index].totloop = (int)k; + mpoly[poly_index].mat_nr = fr->face->mat_nr + mat_ofs; + CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max); + mpoly[poly_index].flag = fr->face->flag; + if (fr->reversed != do_flip) { + for (int l = (int)k - 1; l >= 0; l--) { + CustomData_copy_data( + &mesh->ldata, &result->ldata, (int)face_loops[l], (int)loop_index, 1); + mloop[loop_index].v = face_verts[l]; + mloop[loop_index++].e = face_edges[l]; + } + } + else { + uint l = k - 1; + for (uint next_l = 0; next_l < k; next_l++) { + CustomData_copy_data( + &mesh->ldata, &result->ldata, (int)face_loops[l], (int)loop_index, 1); + mloop[loop_index].v = face_verts[l]; + mloop[loop_index++].e = face_edges[next_l]; + l = next_l; + } + } + poly_index++; + } + } + } + MEM_freeN(face_loops); + MEM_freeN(face_verts); + MEM_freeN(face_edges); + } + if (edge_index != numNewEdges) { + modifier_setError( + md, "Internal Error: edges array wrong size: %u instead of %u", numNewEdges, edge_index); + } + if (poly_index != numNewPolys) { + modifier_setError( + md, "Internal Error: polys array wrong size: %u instead of %u", numNewPolys, poly_index); + } + if (loop_index != numNewLoops) { + modifier_setError( + md, "Internal Error: loops array wrong size: %u instead of %u", numNewLoops, loop_index); + } + BLI_assert(edge_index == numNewEdges); + BLI_assert(poly_index == numNewPolys); + BLI_assert(loop_index == numNewLoops); + + /* Free remaining memory */ + { + MEM_freeN(vm); + MEM_freeN(edge_adj_faces_len); + uint i = 0; + for (EdgeGroup **p = orig_vert_groups_arr; i < numVerts; i++, p++) { + if (*p) { + for (EdgeGroup *eg = *p; eg->valid; eg++) { + MEM_freeN(eg->edges); + } + MEM_freeN(*p); + } + } + MEM_freeN(orig_vert_groups_arr); + i = numEdges; + for (NewEdgeRef ***p = orig_edge_data_arr + (numEdges - 1); i > 0; i--, p--) { + if (*p && (**p)->old_edge == i - 1) { + for (NewEdgeRef **l = *p; *l; l++) { + MEM_freeN(*l); + } + MEM_freeN(*p); + } + } + MEM_freeN(orig_edge_data_arr); + MEM_freeN(orig_edge_lengths); + i = 0; + for (NewFaceRef *p = face_sides_arr; i < numPolys * 2; i++, p++) { + MEM_freeN(p->link_edges); + } + MEM_freeN(face_sides_arr); + MEM_freeN(poly_nors); + } + +#undef MOD_SOLIDIFY_EMPTY_TAG + + return result; +} + +/** \} */ diff --git a/source/blender/modifiers/intern/MOD_solidify_util.h b/source/blender/modifiers/intern/MOD_solidify_util.h new file mode 100644 index 00000000000..dba360dc1ce --- /dev/null +++ b/source/blender/modifiers/intern/MOD_solidify_util.h @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup modifiers + */ + +#ifndef __MOD_MESHCACHE_UTIL_H__ +#define __MOD_MESHCACHE_UTIL_H__ + +/* MOD_solidify_extrude.c */ +Mesh *MOD_solidify_extrude_applyModifier(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh); + +/* MOD_solidify_nonmanifold.c */ +Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh); + +#endif /* __MOD_MESHCACHE_UTIL_H__ */ |