2020-04-03 21:46:08 +02:00
|
|
|
/*
|
|
|
|
* 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
|
2020-05-09 17:14:35 +10:00
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
2020-04-03 21:46:08 +02:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup edsculpt
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
#include "BLI_math.h"
|
|
|
|
#include "BLI_task.h"
|
|
|
|
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
|
|
|
|
#include "DNA_brush_types.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
|
|
|
|
#include "BKE_brush.h"
|
|
|
|
#include "BKE_ccg.h"
|
|
|
|
#include "BKE_colortools.h"
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_mesh.h"
|
|
|
|
#include "BKE_multires.h"
|
|
|
|
#include "BKE_node.h"
|
|
|
|
#include "BKE_object.h"
|
|
|
|
#include "BKE_paint.h"
|
|
|
|
#include "BKE_pbvh.h"
|
|
|
|
#include "BKE_scene.h"
|
|
|
|
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_message.h"
|
|
|
|
#include "WM_toolsystem.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
#include "RNA_define.h"
|
|
|
|
|
|
|
|
#include "ED_object.h"
|
|
|
|
#include "ED_screen.h"
|
|
|
|
#include "ED_sculpt.h"
|
|
|
|
#include "ED_view3d.h"
|
|
|
|
#include "paint_intern.h"
|
|
|
|
#include "sculpt_intern.h"
|
|
|
|
|
|
|
|
#include "bmesh.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
|
|
|
|
|
|
|
|
MEM_freeN(op->customdata);
|
|
|
|
|
|
|
|
for (int n = 0; n < ss->filter_cache->totnode; n++) {
|
|
|
|
PBVHNode *node = ss->filter_cache->nodes[n];
|
|
|
|
if (create_face_set) {
|
|
|
|
for (int i = 0; i < ss->totfaces; i++) {
|
|
|
|
ss->face_sets[i] = ss->filter_cache->prev_face_set[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PBVHVertexIter vd;
|
2021-03-12 22:20:51 +01:00
|
|
|
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
|
2020-04-03 21:46:08 +02:00
|
|
|
*vd.mask = ss->filter_cache->prev_mask[vd.index];
|
|
|
|
}
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
BKE_pbvh_node_mark_redraw(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!create_face_set) {
|
|
|
|
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
|
|
|
|
}
|
|
|
|
SCULPT_filter_cache_free(ss);
|
|
|
|
SCULPT_undo_push_end();
|
|
|
|
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
|
|
|
|
ED_workspace_status_text(C, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sculpt_expand_task_cb(void *__restrict userdata,
|
|
|
|
const int i,
|
|
|
|
const TaskParallelTLS *__restrict UNUSED(tls))
|
|
|
|
{
|
|
|
|
SculptThreadedTaskData *data = userdata;
|
|
|
|
SculptSession *ss = data->ob->sculpt;
|
|
|
|
PBVHNode *node = data->nodes[i];
|
|
|
|
PBVHVertexIter vd;
|
|
|
|
int update_it = data->mask_expand_update_it;
|
|
|
|
|
2021-03-12 22:20:51 +01:00
|
|
|
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
|
2020-04-03 21:46:08 +02:00
|
|
|
int vi = vd.index;
|
|
|
|
float final_mask = *vd.mask;
|
|
|
|
if (data->mask_expand_use_normals) {
|
|
|
|
if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] <
|
|
|
|
ss->filter_cache->normal_factor[vd.index]) {
|
|
|
|
final_mask = 1.0f;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
final_mask = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (ss->filter_cache->mask_update_it[vi] <= update_it &&
|
|
|
|
ss->filter_cache->mask_update_it[vi] != 0) {
|
|
|
|
final_mask = 1.0f;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
final_mask = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->mask_expand_create_face_set) {
|
|
|
|
if (final_mask == 1.0f) {
|
|
|
|
SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set);
|
|
|
|
}
|
|
|
|
BKE_pbvh_node_mark_redraw(node);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
if (data->mask_expand_keep_prev_mask) {
|
|
|
|
final_mask = MAX2(ss->filter_cache->prev_mask[vd.index], final_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->mask_expand_invert_mask) {
|
|
|
|
final_mask = 1.0f - final_mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*vd.mask != final_mask) {
|
|
|
|
if (vd.mvert) {
|
|
|
|
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
|
|
|
|
}
|
|
|
|
*vd.mask = final_mask;
|
|
|
|
BKE_pbvh_node_mark_update_mask(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
{
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
|
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
float prevclick_f[2];
|
|
|
|
copy_v2_v2(prevclick_f, op->customdata);
|
2020-08-07 22:36:11 +10:00
|
|
|
const int prevclick[2] = {(int)prevclick_f[0], (int)prevclick_f[1]};
|
2020-04-03 21:46:08 +02:00
|
|
|
int len = (int)len_v2v2_int(prevclick, event->mval);
|
|
|
|
len = abs(len);
|
|
|
|
int mask_speed = RNA_int_get(op->ptr, "mask_speed");
|
|
|
|
int mask_expand_update_it = len / mask_speed;
|
|
|
|
mask_expand_update_it = mask_expand_update_it + 1;
|
|
|
|
|
|
|
|
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
|
|
|
|
|
|
|
|
if (RNA_boolean_get(op->ptr, "use_cursor")) {
|
|
|
|
SculptCursorGeometryInfo sgi;
|
|
|
|
float mouse[2];
|
|
|
|
mouse[0] = event->mval[0];
|
|
|
|
mouse[1] = event->mval[1];
|
2020-10-04 19:55:27 +02:00
|
|
|
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
|
|
|
|
/* The cursor is over the mesh, get the update iteration from the updated active vertex. */
|
|
|
|
mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* When the cursor is outside the mesh, affect the entire connected component. */
|
|
|
|
mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1;
|
|
|
|
}
|
2020-04-03 21:46:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
|
|
|
|
(event->type == RIGHTMOUSE && event->val == KM_PRESS)) {
|
|
|
|
/* Returning OPERATOR_CANCELLED will leak memory due to not finishing
|
|
|
|
* undo. Better solution could be to make paint_mesh_restore_co work
|
|
|
|
* for this case. */
|
|
|
|
sculpt_mask_expand_cancel(C, op);
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
|
|
|
|
(event->type == EVT_RETKEY && event->val == KM_PRESS) ||
|
|
|
|
(event->type == EVT_PADENTER && event->val == KM_PRESS)) {
|
|
|
|
|
|
|
|
/* Smooth iterations. */
|
Sculpt Vertex Colors: Initial implementation
Sculpt Vertex Colors is a painting system that runs inside sculpt mode, reusing all its tools and optimizations. This provides much better performance, easier to maintain code and more advanced features (new brush engine, filters, symmetry options, masks and face sets compatibility...). This is also the initial step for future features like vertex painting in Multires and brushes that can sculpt and paint at the same time.
This commit includes:
- SCULPT_UNDO_COLOR for undo support in sculpt mode
- SCULPT_UPDATE_COLOR and PBVH flags and rendering
- Sculpt Color API functions
- Sculpt capability for sculpt tools (only enabled in the Paint Brush for now)
- Rendering support in workbench (default to Sculpt Vertex Colors except in Vertex Paint)
- Conversion operator between MPropCol (Sculpt Vertex Colors) and MLoopCol (Vertex Paint)
- Remesher reprojection in the Voxel Remehser
- Paint Brush and Smear Brush with color smoothing in alt-smooth mode
- Parameters for the new brush engine (density, opacity, flow, wet paint mixing, tip scale) implemented in Sculpt Vertex Colors
- Color Filter
- Color picker (uses S shortcut, replaces smooth)
- Color selector in the top bar
Reviewed By: brecht
Maniphest Tasks: T72866
Differential Revision: https://developer.blender.org/D5975
2020-06-22 20:05:28 +02:00
|
|
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
|
2020-04-03 21:46:08 +02:00
|
|
|
const int smooth_iterations = RNA_int_get(op->ptr, "smooth_iterations");
|
2020-04-03 23:41:54 +02:00
|
|
|
SCULPT_mask_filter_smooth_apply(
|
|
|
|
sd, ob, ss->filter_cache->nodes, ss->filter_cache->totnode, smooth_iterations);
|
2020-04-03 21:46:08 +02:00
|
|
|
|
|
|
|
/* Pivot position. */
|
|
|
|
if (RNA_boolean_get(op->ptr, "update_pivot")) {
|
2020-09-18 19:58:48 +02:00
|
|
|
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
|
2020-04-03 21:46:08 +02:00
|
|
|
const float threshold = 0.2f;
|
|
|
|
float avg[3];
|
|
|
|
int total = 0;
|
|
|
|
zero_v3(avg);
|
|
|
|
|
|
|
|
for (int n = 0; n < ss->filter_cache->totnode; n++) {
|
|
|
|
PBVHVertexIter vd;
|
2021-03-12 22:20:51 +01:00
|
|
|
BKE_pbvh_vertex_iter_begin (ss->pbvh, ss->filter_cache->nodes[n], vd, PBVH_ITER_UNIQUE) {
|
2020-04-03 21:46:08 +02:00
|
|
|
const float mask = (vd.mask) ? *vd.mask : 0.0f;
|
|
|
|
if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) {
|
|
|
|
if (SCULPT_check_vertex_pivot_symmetry(
|
|
|
|
vd.co, ss->filter_cache->mask_expand_initial_co, symm)) {
|
|
|
|
add_v3_v3(avg, vd.co);
|
|
|
|
total++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BKE_pbvh_vertex_iter_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (total > 0) {
|
|
|
|
mul_v3_fl(avg, 1.0f / total);
|
|
|
|
copy_v3_v3(ss->pivot_pos, avg);
|
|
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_freeN(op->customdata);
|
|
|
|
|
|
|
|
for (int i = 0; i < ss->filter_cache->totnode; i++) {
|
|
|
|
BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCULPT_filter_cache_free(ss);
|
|
|
|
|
|
|
|
SCULPT_undo_push_end();
|
|
|
|
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
|
|
|
|
ED_workspace_status_text(C, NULL);
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When pressing Ctrl, expand directly to the max number of iterations. This allows to flood fill
|
|
|
|
* mask and face sets by connectivity directly. */
|
|
|
|
if (event->ctrl) {
|
|
|
|
mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ELEM(event->type, MOUSEMOVE, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask_expand_update_it == ss->filter_cache->mask_update_current_it) {
|
|
|
|
ED_region_tag_redraw(region);
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask_expand_update_it < ss->filter_cache->mask_update_last_it) {
|
|
|
|
|
|
|
|
if (create_face_set) {
|
|
|
|
for (int i = 0; i < ss->totfaces; i++) {
|
|
|
|
ss->face_sets[i] = ss->filter_cache->prev_face_set[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SculptThreadedTaskData data = {
|
|
|
|
.sd = sd,
|
|
|
|
.ob = ob,
|
|
|
|
.nodes = ss->filter_cache->nodes,
|
|
|
|
.mask_expand_update_it = mask_expand_update_it,
|
|
|
|
.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"),
|
|
|
|
.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"),
|
|
|
|
.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"),
|
|
|
|
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
|
|
|
|
};
|
2020-04-30 07:59:23 +02:00
|
|
|
TaskParallelSettings settings;
|
2020-06-29 18:16:15 +02:00
|
|
|
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
|
2020-04-30 07:59:23 +02:00
|
|
|
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
|
2020-04-03 21:46:08 +02:00
|
|
|
ss->filter_cache->mask_update_current_it = mask_expand_update_it;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
|
|
|
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct MaskExpandFloodFillData {
|
|
|
|
float original_normal[3];
|
|
|
|
float edge_sensitivity;
|
|
|
|
bool use_normals;
|
|
|
|
} MaskExpandFloodFillData;
|
|
|
|
|
|
|
|
static bool mask_expand_floodfill_cb(
|
|
|
|
SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
|
|
|
|
{
|
|
|
|
MaskExpandFloodFillData *data = userdata;
|
|
|
|
|
|
|
|
if (!is_duplicate) {
|
|
|
|
int to_it = ss->filter_cache->mask_update_it[from_v] + 1;
|
|
|
|
ss->filter_cache->mask_update_it[to_v] = to_it;
|
|
|
|
if (to_it > ss->filter_cache->mask_update_last_it) {
|
|
|
|
ss->filter_cache->mask_update_last_it = to_it;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->use_normals) {
|
|
|
|
float current_normal[3], prev_normal[3];
|
|
|
|
SCULPT_vertex_normal_get(ss, to_v, current_normal);
|
|
|
|
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
|
|
|
|
const float from_edge_factor = ss->filter_cache->edge_factor[from_v];
|
|
|
|
ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) *
|
|
|
|
from_edge_factor;
|
|
|
|
ss->filter_cache->normal_factor[to_v] = dot_v3v3(data->original_normal, current_normal) *
|
|
|
|
powf(from_edge_factor, data->edge_sensitivity);
|
|
|
|
CLAMP(ss->filter_cache->normal_factor[to_v], 0.0f, 1.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* PBVH_GRIDS duplicate handling. */
|
|
|
|
ss->filter_cache->mask_update_it[to_v] = ss->filter_cache->mask_update_it[from_v];
|
|
|
|
if (data->use_normals) {
|
|
|
|
ss->filter_cache->edge_factor[to_v] = ss->filter_cache->edge_factor[from_v];
|
|
|
|
ss->filter_cache->normal_factor[to_v] = ss->filter_cache->normal_factor[from_v];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
{
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
SculptSession *ss = ob->sculpt;
|
|
|
|
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
|
|
|
PBVH *pbvh = ob->sculpt->pbvh;
|
|
|
|
|
|
|
|
const bool use_normals = RNA_boolean_get(op->ptr, "use_normals");
|
|
|
|
const bool create_face_set = RNA_boolean_get(op->ptr, "create_face_set");
|
|
|
|
|
|
|
|
SculptCursorGeometryInfo sgi;
|
|
|
|
float mouse[2];
|
|
|
|
mouse[0] = event->mval[0];
|
|
|
|
mouse[1] = event->mval[1];
|
|
|
|
|
2020-08-11 02:12:08 +02:00
|
|
|
SCULPT_vertex_random_access_ensure(ss);
|
2020-04-03 21:46:08 +02:00
|
|
|
|
2020-08-08 13:29:21 +10:00
|
|
|
op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position");
|
2020-04-03 21:46:08 +02:00
|
|
|
copy_v2_v2(op->customdata, mouse);
|
|
|
|
|
|
|
|
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
|
|
|
|
|
Sculpt Vertex Colors: Initial implementation
Sculpt Vertex Colors is a painting system that runs inside sculpt mode, reusing all its tools and optimizations. This provides much better performance, easier to maintain code and more advanced features (new brush engine, filters, symmetry options, masks and face sets compatibility...). This is also the initial step for future features like vertex painting in Multires and brushes that can sculpt and paint at the same time.
This commit includes:
- SCULPT_UNDO_COLOR for undo support in sculpt mode
- SCULPT_UPDATE_COLOR and PBVH flags and rendering
- Sculpt Color API functions
- Sculpt capability for sculpt tools (only enabled in the Paint Brush for now)
- Rendering support in workbench (default to Sculpt Vertex Colors except in Vertex Paint)
- Conversion operator between MPropCol (Sculpt Vertex Colors) and MLoopCol (Vertex Paint)
- Remesher reprojection in the Voxel Remehser
- Paint Brush and Smear Brush with color smoothing in alt-smooth mode
- Parameters for the new brush engine (density, opacity, flow, wet paint mixing, tip scale) implemented in Sculpt Vertex Colors
- Color Filter
- Color picker (uses S shortcut, replaces smooth)
- Color selector in the top bar
Reviewed By: brecht
Maniphest Tasks: T72866
Differential Revision: https://developer.blender.org/D5975
2020-06-22 20:05:28 +02:00
|
|
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
|
2020-04-03 21:46:08 +02:00
|
|
|
|
|
|
|
int vertex_count = SCULPT_vertex_count_get(ss);
|
|
|
|
|
|
|
|
ss->filter_cache = MEM_callocN(sizeof(FilterCache), "filter cache");
|
|
|
|
|
|
|
|
BKE_pbvh_search_gather(pbvh, NULL, NULL, &ss->filter_cache->nodes, &ss->filter_cache->totnode);
|
|
|
|
|
2020-11-12 10:41:33 +01:00
|
|
|
SCULPT_undo_push_begin(ob, "Mask Expand");
|
2020-04-03 21:46:08 +02:00
|
|
|
|
|
|
|
if (create_face_set) {
|
|
|
|
SCULPT_undo_push_node(ob, ss->filter_cache->nodes[0], SCULPT_UNDO_FACE_SETS);
|
|
|
|
for (int i = 0; i < ss->filter_cache->totnode; i++) {
|
|
|
|
BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (int i = 0; i < ss->filter_cache->totnode; i++) {
|
|
|
|
SCULPT_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_MASK);
|
|
|
|
BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ss->filter_cache->mask_update_it = MEM_callocN(sizeof(int) * vertex_count,
|
|
|
|
"mask update iteration");
|
|
|
|
if (use_normals) {
|
|
|
|
ss->filter_cache->normal_factor = MEM_callocN(sizeof(float) * vertex_count,
|
|
|
|
"mask update normal factor");
|
|
|
|
ss->filter_cache->edge_factor = MEM_callocN(sizeof(float) * vertex_count,
|
|
|
|
"mask update normal factor");
|
|
|
|
for (int i = 0; i < vertex_count; i++) {
|
|
|
|
ss->filter_cache->edge_factor[i] = 1.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (create_face_set) {
|
|
|
|
ss->filter_cache->prev_face_set = MEM_callocN(sizeof(float) * ss->totfaces, "prev face mask");
|
|
|
|
for (int i = 0; i < ss->totfaces; i++) {
|
|
|
|
ss->filter_cache->prev_face_set[i] = ss->face_sets[i];
|
|
|
|
}
|
|
|
|
ss->filter_cache->new_face_set = SCULPT_face_set_next_available_get(ss);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask");
|
|
|
|
for (int i = 0; i < vertex_count; i++) {
|
|
|
|
ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ss->filter_cache->mask_update_last_it = 1;
|
|
|
|
ss->filter_cache->mask_update_current_it = 1;
|
|
|
|
ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0;
|
|
|
|
|
|
|
|
copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss));
|
|
|
|
|
|
|
|
SculptFloodFill flood;
|
|
|
|
SCULPT_floodfill_init(ss, &flood);
|
|
|
|
SCULPT_floodfill_add_active(sd, ob, ss, &flood, FLT_MAX);
|
|
|
|
|
|
|
|
MaskExpandFloodFillData fdata = {
|
|
|
|
.use_normals = use_normals,
|
|
|
|
.edge_sensitivity = RNA_int_get(op->ptr, "edge_sensitivity"),
|
|
|
|
};
|
|
|
|
SCULPT_active_vertex_normal_get(ss, fdata.original_normal);
|
|
|
|
SCULPT_floodfill_execute(ss, &flood, mask_expand_floodfill_cb, &fdata);
|
|
|
|
SCULPT_floodfill_free(&flood);
|
|
|
|
|
|
|
|
if (use_normals) {
|
|
|
|
for (int repeat = 0; repeat < 2; repeat++) {
|
|
|
|
for (int i = 0; i < vertex_count; i++) {
|
|
|
|
float avg = 0.0f;
|
|
|
|
SculptVertexNeighborIter ni;
|
|
|
|
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
|
|
|
|
avg += ss->filter_cache->normal_factor[ni.index];
|
|
|
|
}
|
|
|
|
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
|
|
|
|
ss->filter_cache->normal_factor[i] = avg / ni.size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(ss->filter_cache->edge_factor);
|
|
|
|
}
|
|
|
|
|
|
|
|
SculptThreadedTaskData data = {
|
|
|
|
.sd = sd,
|
|
|
|
.ob = ob,
|
|
|
|
.nodes = ss->filter_cache->nodes,
|
|
|
|
.mask_expand_update_it = 0,
|
|
|
|
.mask_expand_use_normals = RNA_boolean_get(op->ptr, "use_normals"),
|
|
|
|
.mask_expand_invert_mask = RNA_boolean_get(op->ptr, "invert"),
|
|
|
|
.mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"),
|
|
|
|
.mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"),
|
|
|
|
};
|
2020-04-30 07:59:23 +02:00
|
|
|
TaskParallelSettings settings;
|
2020-06-29 18:16:15 +02:00
|
|
|
BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
|
2020-04-30 07:59:23 +02:00
|
|
|
BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings);
|
2020-04-03 21:46:08 +02:00
|
|
|
|
|
|
|
const char *status_str = TIP_(
|
|
|
|
"Move the mouse to expand the mask from the active vertex. LMB: confirm mask, ESC/RMB: "
|
|
|
|
"cancel");
|
|
|
|
ED_workspace_status_text(C, status_str);
|
|
|
|
|
|
|
|
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
|
|
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SCULPT_OT_mask_expand(wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* Identifiers. */
|
|
|
|
ot->name = "Mask Expand";
|
|
|
|
ot->idname = "SCULPT_OT_mask_expand";
|
|
|
|
ot->description = "Expands a mask from the initial active vertex under the cursor";
|
|
|
|
|
|
|
|
/* API callbacks. */
|
|
|
|
ot->invoke = sculpt_mask_expand_invoke;
|
|
|
|
ot->modal = sculpt_mask_expand_modal;
|
|
|
|
ot->cancel = sculpt_mask_expand_cancel;
|
|
|
|
ot->poll = SCULPT_mode_poll;
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
ot->prop = RNA_def_boolean(ot->srna, "invert", true, "Invert", "Invert the new mask");
|
|
|
|
ot->prop = RNA_def_boolean(
|
|
|
|
ot->srna, "use_cursor", true, "Use Cursor", "Expand the mask to the cursor position");
|
|
|
|
ot->prop = RNA_def_boolean(ot->srna,
|
|
|
|
"update_pivot",
|
|
|
|
true,
|
|
|
|
"Update Pivot Position",
|
|
|
|
"Set the pivot position to the mask border after creating the mask");
|
2020-12-24 11:07:32 -06:00
|
|
|
ot->prop = RNA_def_int(ot->srna, "smooth_iterations", 2, 0, 10, "Smooth Iterations", "", 0, 10);
|
|
|
|
ot->prop = RNA_def_int(ot->srna, "mask_speed", 5, 1, 10, "Mask Speed", "", 1, 10);
|
2020-04-03 21:46:08 +02:00
|
|
|
|
|
|
|
ot->prop = RNA_def_boolean(ot->srna,
|
|
|
|
"use_normals",
|
|
|
|
true,
|
|
|
|
"Use Normals",
|
|
|
|
"Generate the mask using the normals and curvature of the model");
|
|
|
|
ot->prop = RNA_def_boolean(ot->srna,
|
|
|
|
"keep_previous_mask",
|
|
|
|
false,
|
|
|
|
"Keep Previous Mask",
|
|
|
|
"Generate the new mask on top of the current one");
|
|
|
|
ot->prop = RNA_def_int(ot->srna,
|
|
|
|
"edge_sensitivity",
|
|
|
|
300,
|
|
|
|
0,
|
|
|
|
2000,
|
|
|
|
"Edge Detection Sensitivity",
|
|
|
|
"Sensitivity for expanding the mask across sculpted sharp edges when "
|
|
|
|
"using normals to generate the mask",
|
|
|
|
0,
|
|
|
|
2000);
|
|
|
|
ot->prop = RNA_def_boolean(ot->srna,
|
|
|
|
"create_face_set",
|
|
|
|
false,
|
|
|
|
"Expand Face Mask",
|
|
|
|
"Expand a new Face Mask instead of the sculpt mask");
|
|
|
|
}
|