|
|
|
|
@@ -81,7 +81,11 @@
|
|
|
|
|
|
|
|
|
|
namespace blender::ed::sculpt_paint {
|
|
|
|
|
|
|
|
|
|
static int sculpt_set_persistent_base_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Set Persistent Base Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static int set_persistent_base_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
|
Object &ob = *CTX_data_active_object(C);
|
|
|
|
|
@@ -137,15 +141,19 @@ static void SCULPT_OT_set_persistent_base(wmOperatorType *ot)
|
|
|
|
|
ot->idname = "SCULPT_OT_set_persistent_base";
|
|
|
|
|
ot->description = "Reset the copy of the mesh that is being sculpted on";
|
|
|
|
|
|
|
|
|
|
ot->exec = sculpt_set_persistent_base_exec;
|
|
|
|
|
ot->exec = set_persistent_base_exec;
|
|
|
|
|
ot->poll = SCULPT_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************* SCULPT_OT_optimize *************************/
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
static int sculpt_optimize_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Optimize Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static int optimize_exec(bContext *C, wmOperator * /*op*/)
|
|
|
|
|
{
|
|
|
|
|
Object &ob = *CTX_data_active_object(C);
|
|
|
|
|
|
|
|
|
|
@@ -167,15 +175,19 @@ static void SCULPT_OT_optimize(wmOperatorType *ot)
|
|
|
|
|
ot->idname = "SCULPT_OT_optimize";
|
|
|
|
|
ot->description = "Recalculate the sculpt BVH to improve performance";
|
|
|
|
|
|
|
|
|
|
ot->exec = sculpt_optimize_exec;
|
|
|
|
|
ot->exec = optimize_exec;
|
|
|
|
|
ot->poll = SCULPT_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/********************* Dynamic topology symmetrize ********************/
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
static bool sculpt_no_multires_poll(bContext *C)
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Symmetrize Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool no_multires_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
if (!ob) {
|
|
|
|
|
@@ -191,7 +203,7 @@ static bool sculpt_no_multires_poll(bContext *C)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
|
|
|
|
|
static int symmetrize_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
|
|
|
|
|
@@ -276,25 +288,29 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot)
|
|
|
|
|
ot->idname = "SCULPT_OT_symmetrize";
|
|
|
|
|
ot->description = "Symmetrize the topology modifications";
|
|
|
|
|
|
|
|
|
|
ot->exec = sculpt_symmetrize_exec;
|
|
|
|
|
ot->poll = sculpt_no_multires_poll;
|
|
|
|
|
ot->exec = symmetrize_exec;
|
|
|
|
|
ot->poll = no_multires_poll;
|
|
|
|
|
|
|
|
|
|
PropertyRNA *prop = RNA_def_float(ot->srna,
|
|
|
|
|
"merge_tolerance",
|
|
|
|
|
0.0005f,
|
|
|
|
|
0.0f,
|
|
|
|
|
FLT_MAX,
|
|
|
|
|
std::numeric_limits<float>::max(),
|
|
|
|
|
"Merge Distance",
|
|
|
|
|
"Distance within which symmetrical vertices are merged",
|
|
|
|
|
0.0f,
|
|
|
|
|
1.0f);
|
|
|
|
|
|
|
|
|
|
RNA_def_property_ui_range(prop, 0.0, FLT_MAX, 0.001, 5);
|
|
|
|
|
RNA_def_property_ui_range(prop, 0.0, std::numeric_limits<float>::max(), 0.001, 5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** Toggle operator for turning sculpt mode on or off ****/
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
static void sculpt_init_session(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Sculpt Mode Toggle Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static void init_sculpt_mode_session(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
|
|
|
|
|
{
|
|
|
|
|
/* Create persistent sculpt mode data. */
|
|
|
|
|
BKE_sculpt_toolsettings_data_ensure(&bmain, &scene);
|
|
|
|
|
@@ -375,7 +391,7 @@ void object_sculpt_mode_enter(Main &bmain,
|
|
|
|
|
/* Enter sculpt mode. */
|
|
|
|
|
ob.mode |= mode_flag;
|
|
|
|
|
|
|
|
|
|
sculpt_init_session(bmain, depsgraph, scene, ob);
|
|
|
|
|
init_sculpt_mode_session(bmain, depsgraph, scene, ob);
|
|
|
|
|
|
|
|
|
|
if (!(fabsf(ob.scale[0] - ob.scale[1]) < 1e-4f && fabsf(ob.scale[1] - ob.scale[2]) < 1e-4f)) {
|
|
|
|
|
BKE_report(
|
|
|
|
|
@@ -403,7 +419,7 @@ void object_sculpt_mode_enter(Main &bmain,
|
|
|
|
|
message_unsupported = RPT_("multi-res modifier");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dyntopo::WarnFlag flag = dyntopo::check_attribute_warning(scene, ob);
|
|
|
|
|
const dyntopo::WarnFlag flag = dyntopo::check_attribute_warning(scene, ob);
|
|
|
|
|
if (flag == 0) {
|
|
|
|
|
/* pass */
|
|
|
|
|
}
|
|
|
|
|
@@ -420,14 +436,14 @@ void object_sculpt_mode_enter(Main &bmain,
|
|
|
|
|
message_unsupported = RPT_("constructive modifier");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BLI_assert(0);
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((message_unsupported == nullptr) || force_dyntopo) {
|
|
|
|
|
/* Needed because we may be entering this mode before the undo system loads. */
|
|
|
|
|
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain.wm.first);
|
|
|
|
|
bool has_undo = wm->undo_stack != nullptr;
|
|
|
|
|
const bool has_undo = wm->undo_stack != nullptr;
|
|
|
|
|
/* Undo push is needed to prevent memory leak. */
|
|
|
|
|
if (has_undo) {
|
|
|
|
|
undo::push_begin_ex(ob, "Dynamic topology enable");
|
|
|
|
|
@@ -581,73 +597,13 @@ static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot)
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void geometry_preview_lines_update(bContext *C, SculptSession &ss, float radius)
|
|
|
|
|
{
|
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
|
Object *ob = CTX_data_active_object(C);
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
ss.preview_verts = {};
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Sample Color Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
/* This function is called from the cursor drawing code, so the tree may not be build yet. */
|
|
|
|
|
const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*ob);
|
|
|
|
|
if (!pbvh) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ss.deform_modifiers_active) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pbvh->type() != bke::pbvh::Type::Mesh) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_sculpt_update_object_for_edit(depsgraph, ob, false);
|
|
|
|
|
|
|
|
|
|
const Mesh &mesh = *static_cast<const Mesh *>(ob->data);
|
|
|
|
|
/* Always grab active shape key if the sculpt happens on shapekey. */
|
|
|
|
|
const Span<float3> positions = ss.shapekey_active ?
|
|
|
|
|
bke::pbvh::vert_positions_eval(*depsgraph, *ob) :
|
|
|
|
|
mesh.vert_positions();
|
|
|
|
|
const OffsetIndices faces = mesh.faces();
|
|
|
|
|
const Span<int> corner_verts = mesh.corner_verts();
|
|
|
|
|
const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
|
|
|
|
|
const bke::AttributeAccessor attributes = mesh.attributes();
|
|
|
|
|
const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
|
|
|
|
|
|
|
|
|
|
const int active_vert = std::get<int>(ss.active_vert());
|
|
|
|
|
const float3 brush_co = positions[active_vert];
|
|
|
|
|
const float radius_sq = radius * radius;
|
|
|
|
|
|
|
|
|
|
Vector<int> preview_verts;
|
|
|
|
|
Vector<int> neighbors;
|
|
|
|
|
BitVector<> visited_verts(positions.size());
|
|
|
|
|
std::queue<int> queue;
|
|
|
|
|
queue.push(active_vert);
|
|
|
|
|
while (!queue.empty()) {
|
|
|
|
|
const int from_vert = queue.front();
|
|
|
|
|
queue.pop();
|
|
|
|
|
|
|
|
|
|
neighbors.clear();
|
|
|
|
|
for (const int neighbor : vert_neighbors_get_mesh(
|
|
|
|
|
faces, corner_verts, vert_to_face_map, hide_poly, from_vert, neighbors))
|
|
|
|
|
{
|
|
|
|
|
preview_verts.append(from_vert);
|
|
|
|
|
preview_verts.append(neighbor);
|
|
|
|
|
if (visited_verts[neighbor]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
visited_verts[neighbor].set();
|
|
|
|
|
if (math::distance_squared(brush_co, positions[neighbor]) < radius_sq) {
|
|
|
|
|
queue.push(neighbor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss.preview_verts = preview_verts.as_span();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
|
|
|
|
static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
|
|
|
|
{
|
|
|
|
|
Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
|
|
|
|
|
Scene &scene = *CTX_data_scene(C);
|
|
|
|
|
@@ -702,14 +658,20 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot)
|
|
|
|
|
ot->idname = "SCULPT_OT_sample_color";
|
|
|
|
|
ot->description = "Sample the vertex color of the active vertex";
|
|
|
|
|
|
|
|
|
|
ot->invoke = sculpt_sample_color_invoke;
|
|
|
|
|
ot->invoke = sample_color_invoke;
|
|
|
|
|
ot->poll = SCULPT_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
namespace mask {
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Mask By Color
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* #sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the
|
|
|
|
|
* mask based on the difference between two colors (the active color and the color of any other
|
|
|
|
|
@@ -721,14 +683,14 @@ namespace mask {
|
|
|
|
|
*/
|
|
|
|
|
#define MASK_BY_COLOR_SLOPE 0.25f
|
|
|
|
|
|
|
|
|
|
static float sculpt_mask_by_color_delta_get(const float *color_a,
|
|
|
|
|
const float *color_b,
|
|
|
|
|
const float threshold,
|
|
|
|
|
const bool invert)
|
|
|
|
|
static float color_delta_get(const float3 &color_a,
|
|
|
|
|
const float3 &color_b,
|
|
|
|
|
const float threshold,
|
|
|
|
|
const bool invert)
|
|
|
|
|
{
|
|
|
|
|
float len = len_v3v3(color_a, color_b);
|
|
|
|
|
float len = math::distance(color_a, color_b);
|
|
|
|
|
/* Normalize len to the (0, 1) range. */
|
|
|
|
|
len = len / M_SQRT3;
|
|
|
|
|
len = len / math::numbers::sqrt3_v<float>;
|
|
|
|
|
|
|
|
|
|
if (len < threshold - MASK_BY_COLOR_SLOPE) {
|
|
|
|
|
len = 1.0f;
|
|
|
|
|
@@ -746,26 +708,26 @@ static float sculpt_mask_by_color_delta_get(const float *color_a,
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float sculpt_mask_by_color_final_mask_get(const float current_mask,
|
|
|
|
|
const float new_mask,
|
|
|
|
|
const bool invert,
|
|
|
|
|
const bool preserve_mask)
|
|
|
|
|
static float final_mask_get(const float current_mask,
|
|
|
|
|
const float new_mask,
|
|
|
|
|
const bool invert,
|
|
|
|
|
const bool preserve_mask)
|
|
|
|
|
{
|
|
|
|
|
if (preserve_mask) {
|
|
|
|
|
if (invert) {
|
|
|
|
|
return min_ff(current_mask, new_mask);
|
|
|
|
|
return std::min(current_mask, new_mask);
|
|
|
|
|
}
|
|
|
|
|
return max_ff(current_mask, new_mask);
|
|
|
|
|
return std::max(current_mask, new_mask);
|
|
|
|
|
}
|
|
|
|
|
return new_mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sculpt_mask_by_color_contiguous_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const int vert,
|
|
|
|
|
const float threshold,
|
|
|
|
|
const bool invert,
|
|
|
|
|
const bool preserve_mask)
|
|
|
|
|
static void mask_by_color_contiguous_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const int vert,
|
|
|
|
|
const float threshold,
|
|
|
|
|
const bool invert,
|
|
|
|
|
const bool preserve_mask)
|
|
|
|
|
{
|
|
|
|
|
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
|
|
|
|
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
|
|
|
|
@@ -783,12 +745,12 @@ static void sculpt_mask_by_color_contiguous_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
flood.execute(object, vert_to_face_map, [&](int /*from_v*/, int to_v) {
|
|
|
|
|
const float4 current_color = float4(colors[to_v]);
|
|
|
|
|
|
|
|
|
|
float new_vertex_mask = sculpt_mask_by_color_delta_get(
|
|
|
|
|
current_color, active_color, threshold, invert);
|
|
|
|
|
float new_vertex_mask = color_delta_get(
|
|
|
|
|
current_color.xyz(), active_color.xyz(), threshold, invert);
|
|
|
|
|
new_mask[to_v] = new_vertex_mask;
|
|
|
|
|
|
|
|
|
|
float len = len_v3v3(current_color, active_color);
|
|
|
|
|
len = len / M_SQRT3;
|
|
|
|
|
float len = math::distance(current_color.xyz(), active_color.xyz());
|
|
|
|
|
len = len / math::numbers::sqrt3_v<float>;
|
|
|
|
|
return len <= threshold;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@@ -796,20 +758,19 @@ static void sculpt_mask_by_color_contiguous_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
|
|
|
|
|
|
|
|
|
|
update_mask_mesh(
|
|
|
|
|
depsgraph, object, node_mask, [&](MutableSpan<float> node_mask, const Span<int> verts) {
|
|
|
|
|
depsgraph, object, node_mask, [&](MutableSpan<float> node_masks, const Span<int> verts) {
|
|
|
|
|
for (const int i : verts.index_range()) {
|
|
|
|
|
node_mask[i] = sculpt_mask_by_color_final_mask_get(
|
|
|
|
|
node_mask[i], new_mask[verts[i]], invert, preserve_mask);
|
|
|
|
|
node_masks[i] = final_mask_get(node_masks[i], new_mask[verts[i]], invert, preserve_mask);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void sculpt_mask_by_color_full_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const int vert,
|
|
|
|
|
const float threshold,
|
|
|
|
|
const bool invert,
|
|
|
|
|
const bool preserve_mask)
|
|
|
|
|
static void mask_by_color_full_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const int vert,
|
|
|
|
|
const float threshold,
|
|
|
|
|
const bool invert,
|
|
|
|
|
const bool preserve_mask)
|
|
|
|
|
{
|
|
|
|
|
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
|
|
|
|
|
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
|
|
|
|
@@ -822,18 +783,18 @@ static void sculpt_mask_by_color_full_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
|
|
|
|
|
|
|
|
|
|
update_mask_mesh(
|
|
|
|
|
depsgraph, object, node_mask, [&](MutableSpan<float> node_mask, const Span<int> verts) {
|
|
|
|
|
depsgraph, object, node_mask, [&](MutableSpan<float> node_masks, const Span<int> verts) {
|
|
|
|
|
for (const int i : verts.index_range()) {
|
|
|
|
|
const float current_mask = node_mask[i];
|
|
|
|
|
const float new_mask = sculpt_mask_by_color_delta_get(
|
|
|
|
|
active_color, colors[verts[i]], threshold, invert);
|
|
|
|
|
node_mask[i] = sculpt_mask_by_color_final_mask_get(
|
|
|
|
|
current_mask, new_mask, invert, preserve_mask);
|
|
|
|
|
const float4 current_color = float4(colors[verts[i]]);
|
|
|
|
|
const float current_mask = node_masks[i];
|
|
|
|
|
const float new_mask = color_delta_get(
|
|
|
|
|
active_color.xyz(), current_color.xyz(), threshold, invert);
|
|
|
|
|
node_masks[i] = final_mask_get(current_mask, new_mask, invert, preserve_mask);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
static int mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
|
Object &ob = *CTX_data_active_object(C);
|
|
|
|
|
@@ -873,11 +834,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
|
|
|
|
|
|
|
|
|
|
const int active_vert = std::get<int>(ss.active_vert());
|
|
|
|
|
if (RNA_boolean_get(op->ptr, "contiguous")) {
|
|
|
|
|
sculpt_mask_by_color_contiguous_mesh(
|
|
|
|
|
*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
|
|
|
|
|
mask_by_color_contiguous_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sculpt_mask_by_color_full_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
|
|
|
|
|
mask_by_color_full_mesh(*depsgraph, ob, active_vert, threshold, invert, preserve_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
undo::push_end(ob);
|
|
|
|
|
@@ -894,7 +854,7 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
|
|
|
|
|
ot->idname = "SCULPT_OT_mask_by_color";
|
|
|
|
|
ot->description = "Creates a mask based on the active color attribute";
|
|
|
|
|
|
|
|
|
|
ot->invoke = sculpt_mask_by_color_invoke;
|
|
|
|
|
ot->invoke = mask_by_color_invoke;
|
|
|
|
|
ot->poll = SCULPT_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER;
|
|
|
|
|
@@ -921,19 +881,21 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
|
|
|
|
|
1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum CavityBakeMixMode {
|
|
|
|
|
AUTOMASK_BAKE_MIX,
|
|
|
|
|
AUTOMASK_BAKE_MULTIPLY,
|
|
|
|
|
AUTOMASK_BAKE_DIVIDE,
|
|
|
|
|
AUTOMASK_BAKE_ADD,
|
|
|
|
|
AUTOMASK_BAKE_SUBTRACT,
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Mask from Cavity
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
enum class ApplyMaskMode : int8_t {
|
|
|
|
|
Mix,
|
|
|
|
|
Multiply,
|
|
|
|
|
Divide,
|
|
|
|
|
Add,
|
|
|
|
|
Subtract,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum CavityBakeSettingsSource {
|
|
|
|
|
AUTOMASK_SETTINGS_OPERATOR,
|
|
|
|
|
AUTOMASK_SETTINGS_SCENE,
|
|
|
|
|
AUTOMASK_SETTINGS_BRUSH
|
|
|
|
|
};
|
|
|
|
|
enum class MaskSettingsSource : int8_t { Operator, Scene, Brush };
|
|
|
|
|
|
|
|
|
|
struct LocalData {
|
|
|
|
|
Vector<float> mask;
|
|
|
|
|
@@ -941,29 +903,29 @@ struct LocalData {
|
|
|
|
|
Vector<float> new_mask;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void calc_new_masks(const CavityBakeMixMode mode,
|
|
|
|
|
static void calc_new_masks(const ApplyMaskMode mode,
|
|
|
|
|
const Span<float> node_mask,
|
|
|
|
|
const MutableSpan<float> new_mask)
|
|
|
|
|
{
|
|
|
|
|
switch (mode) {
|
|
|
|
|
case AUTOMASK_BAKE_MIX:
|
|
|
|
|
case ApplyMaskMode::Mix:
|
|
|
|
|
break;
|
|
|
|
|
case AUTOMASK_BAKE_MULTIPLY:
|
|
|
|
|
case ApplyMaskMode::Multiply:
|
|
|
|
|
for (const int i : node_mask.index_range()) {
|
|
|
|
|
new_mask[i] = node_mask[i] * new_mask[i];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AUTOMASK_BAKE_DIVIDE:
|
|
|
|
|
case ApplyMaskMode::Divide:
|
|
|
|
|
for (const int i : node_mask.index_range()) {
|
|
|
|
|
new_mask[i] = new_mask[i] > 0.00001f ? node_mask[i] / new_mask[i] : 0.0f;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AUTOMASK_BAKE_ADD:
|
|
|
|
|
case ApplyMaskMode::Add:
|
|
|
|
|
for (const int i : node_mask.index_range()) {
|
|
|
|
|
new_mask[i] = node_mask[i] + new_mask[i];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case AUTOMASK_BAKE_SUBTRACT:
|
|
|
|
|
case ApplyMaskMode::Subtract:
|
|
|
|
|
for (const int i : node_mask.index_range()) {
|
|
|
|
|
new_mask[i] = node_mask[i] - new_mask[i];
|
|
|
|
|
}
|
|
|
|
|
@@ -972,14 +934,14 @@ static void calc_new_masks(const CavityBakeMixMode mode,
|
|
|
|
|
mask::clamp_mask(new_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void bake_mask_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
const Object &object,
|
|
|
|
|
const auto_mask::Cache &automasking,
|
|
|
|
|
const CavityBakeMixMode mode,
|
|
|
|
|
const float factor,
|
|
|
|
|
const bke::pbvh::MeshNode &node,
|
|
|
|
|
LocalData &tls,
|
|
|
|
|
const MutableSpan<float> mask)
|
|
|
|
|
static void apply_mask_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
const Object &object,
|
|
|
|
|
const auto_mask::Cache &automasking,
|
|
|
|
|
const ApplyMaskMode mode,
|
|
|
|
|
const float factor,
|
|
|
|
|
const bke::pbvh::MeshNode &node,
|
|
|
|
|
LocalData &tls,
|
|
|
|
|
const MutableSpan<float> mask)
|
|
|
|
|
{
|
|
|
|
|
const Mesh &mesh = *static_cast<const Mesh *>(object.data);
|
|
|
|
|
const Span<int> verts = node.verts();
|
|
|
|
|
@@ -1004,13 +966,13 @@ static void bake_mask_mesh(const Depsgraph &depsgraph,
|
|
|
|
|
scatter_data_mesh(node_mask.as_span(), verts, mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void bake_mask_grids(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const auto_mask::Cache &automasking,
|
|
|
|
|
const CavityBakeMixMode mode,
|
|
|
|
|
const float factor,
|
|
|
|
|
const bke::pbvh::GridsNode &node,
|
|
|
|
|
LocalData &tls)
|
|
|
|
|
static void apply_mask_grids(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const auto_mask::Cache &automasking,
|
|
|
|
|
const ApplyMaskMode mode,
|
|
|
|
|
const float factor,
|
|
|
|
|
const bke::pbvh::GridsNode &node,
|
|
|
|
|
LocalData &tls)
|
|
|
|
|
{
|
|
|
|
|
SculptSession &ss = *object.sculpt;
|
|
|
|
|
SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
|
|
|
|
|
@@ -1039,13 +1001,13 @@ static void bake_mask_grids(const Depsgraph &depsgraph,
|
|
|
|
|
scatter_mask_grids(node_mask.as_span(), subdiv_ccg, grids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void bake_mask_bmesh(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const auto_mask::Cache &automasking,
|
|
|
|
|
const CavityBakeMixMode mode,
|
|
|
|
|
const float factor,
|
|
|
|
|
bke::pbvh::BMeshNode &node,
|
|
|
|
|
LocalData &tls)
|
|
|
|
|
static void apply_mask_bmesh(const Depsgraph &depsgraph,
|
|
|
|
|
Object &object,
|
|
|
|
|
const auto_mask::Cache &automasking,
|
|
|
|
|
const ApplyMaskMode mode,
|
|
|
|
|
const float factor,
|
|
|
|
|
bke::pbvh::BMeshNode &node,
|
|
|
|
|
LocalData &tls)
|
|
|
|
|
{
|
|
|
|
|
const SculptSession &ss = *object.sculpt;
|
|
|
|
|
const Set<BMVert *, 0> &verts = BKE_pbvh_bmesh_node_unique_verts(&node);
|
|
|
|
|
@@ -1070,7 +1032,7 @@ static void bake_mask_bmesh(const Depsgraph &depsgraph,
|
|
|
|
|
scatter_mask_bmesh(node_mask.as_span(), *ss.bm, verts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
static int mask_from_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
|
|
|
|
Object &ob = *CTX_data_active_object(C);
|
|
|
|
|
@@ -1091,8 +1053,8 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
|
|
|
|
|
undo::push_begin(ob, op);
|
|
|
|
|
|
|
|
|
|
CavityBakeMixMode mode = CavityBakeMixMode(RNA_enum_get(op->ptr, "mix_mode"));
|
|
|
|
|
float factor = RNA_float_get(op->ptr, "mix_factor");
|
|
|
|
|
const ApplyMaskMode mode = ApplyMaskMode(RNA_enum_get(op->ptr, "mix_mode"));
|
|
|
|
|
const float factor = RNA_float_get(op->ptr, "mix_factor");
|
|
|
|
|
|
|
|
|
|
bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
|
|
|
|
|
|
|
|
|
|
@@ -1100,67 +1062,68 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
|
|
|
|
|
|
|
|
|
|
/* Set up automasking settings. */
|
|
|
|
|
Sculpt sd2 = sd;
|
|
|
|
|
Sculpt scene_copy = sd;
|
|
|
|
|
|
|
|
|
|
CavityBakeSettingsSource src = (CavityBakeSettingsSource)RNA_enum_get(op->ptr,
|
|
|
|
|
"settings_source");
|
|
|
|
|
MaskSettingsSource src = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
|
|
|
|
|
switch (src) {
|
|
|
|
|
case AUTOMASK_SETTINGS_OPERATOR:
|
|
|
|
|
case MaskSettingsSource::Operator:
|
|
|
|
|
if (RNA_boolean_get(op->ptr, "invert")) {
|
|
|
|
|
sd2.automasking_flags = BRUSH_AUTOMASKING_CAVITY_INVERTED;
|
|
|
|
|
scene_copy.automasking_flags = BRUSH_AUTOMASKING_CAVITY_INVERTED;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sd2.automasking_flags = BRUSH_AUTOMASKING_CAVITY_NORMAL;
|
|
|
|
|
scene_copy.automasking_flags = BRUSH_AUTOMASKING_CAVITY_NORMAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (RNA_boolean_get(op->ptr, "use_curve")) {
|
|
|
|
|
sd2.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
|
|
|
|
|
scene_copy.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sd2.automasking_cavity_blur_steps = RNA_int_get(op->ptr, "blur_steps");
|
|
|
|
|
sd2.automasking_cavity_factor = RNA_float_get(op->ptr, "factor");
|
|
|
|
|
scene_copy.automasking_cavity_blur_steps = RNA_int_get(op->ptr, "blur_steps");
|
|
|
|
|
scene_copy.automasking_cavity_factor = RNA_float_get(op->ptr, "factor");
|
|
|
|
|
|
|
|
|
|
sd2.automasking_cavity_curve = sd.automasking_cavity_curve_op;
|
|
|
|
|
scene_copy.automasking_cavity_curve = sd.automasking_cavity_curve_op;
|
|
|
|
|
break;
|
|
|
|
|
case AUTOMASK_SETTINGS_BRUSH:
|
|
|
|
|
case MaskSettingsSource::Brush:
|
|
|
|
|
if (brush) {
|
|
|
|
|
sd2.automasking_flags = brush->automasking_flags;
|
|
|
|
|
sd2.automasking_cavity_factor = brush->automasking_cavity_factor;
|
|
|
|
|
sd2.automasking_cavity_curve = brush->automasking_cavity_curve;
|
|
|
|
|
sd2.automasking_cavity_blur_steps = brush->automasking_cavity_blur_steps;
|
|
|
|
|
scene_copy.automasking_flags = brush->automasking_flags;
|
|
|
|
|
scene_copy.automasking_cavity_factor = brush->automasking_cavity_factor;
|
|
|
|
|
scene_copy.automasking_cavity_curve = brush->automasking_cavity_curve;
|
|
|
|
|
scene_copy.automasking_cavity_blur_steps = brush->automasking_cavity_blur_steps;
|
|
|
|
|
|
|
|
|
|
/* Ensure only cavity masking is enabled. */
|
|
|
|
|
sd2.automasking_flags &= BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
|
|
|
|
|
scene_copy.automasking_flags &= BRUSH_AUTOMASKING_CAVITY_ALL |
|
|
|
|
|
BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sd2.automasking_flags = 0;
|
|
|
|
|
scene_copy.automasking_flags = 0;
|
|
|
|
|
BKE_report(op->reports, RPT_WARNING, "No active brush");
|
|
|
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case AUTOMASK_SETTINGS_SCENE:
|
|
|
|
|
case MaskSettingsSource::Scene:
|
|
|
|
|
/* Ensure only cavity masking is enabled. */
|
|
|
|
|
sd2.automasking_flags &= BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
|
|
|
|
|
scene_copy.automasking_flags &= BRUSH_AUTOMASKING_CAVITY_ALL |
|
|
|
|
|
BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ensure cavity mask is actually enabled. */
|
|
|
|
|
if (!(sd2.automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
|
|
|
|
|
sd2.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_NORMAL;
|
|
|
|
|
if (!(scene_copy.automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
|
|
|
|
|
scene_copy.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_NORMAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create copy of brush with cleared automasking settings. */
|
|
|
|
|
Brush brush2 = dna::shallow_copy(*brush);
|
|
|
|
|
Brush brush_copy = dna::shallow_copy(*brush);
|
|
|
|
|
/* Set a brush type that doesn't change topology so automasking isn't "disabled". */
|
|
|
|
|
brush2.sculpt_brush_type = SCULPT_BRUSH_TYPE_SMOOTH;
|
|
|
|
|
brush2.automasking_flags = 0;
|
|
|
|
|
brush2.automasking_boundary_edges_propagation_steps = 1;
|
|
|
|
|
brush2.automasking_cavity_curve = sd2.automasking_cavity_curve;
|
|
|
|
|
brush_copy.sculpt_brush_type = SCULPT_BRUSH_TYPE_SMOOTH;
|
|
|
|
|
brush_copy.automasking_flags = 0;
|
|
|
|
|
brush_copy.automasking_boundary_edges_propagation_steps = 1;
|
|
|
|
|
brush_copy.automasking_cavity_curve = scene_copy.automasking_cavity_curve;
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<auto_mask::Cache> automasking = auto_mask::cache_init(
|
|
|
|
|
*depsgraph, sd2, &brush2, ob);
|
|
|
|
|
*depsgraph, scene_copy, &brush_copy, ob);
|
|
|
|
|
|
|
|
|
|
undo::push_nodes(*depsgraph, ob, node_mask, undo::Type::Mask);
|
|
|
|
|
|
|
|
|
|
@@ -1171,14 +1134,11 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
|
|
|
|
bke::SpanAttributeWriter mask = attributes.lookup_or_add_for_write_span<float>(
|
|
|
|
|
".sculpt_mask", bke::AttrDomain::Point);
|
|
|
|
|
if (!mask) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
MutableSpan<bke::pbvh::MeshNode> nodes = pbvh.nodes<bke::pbvh::MeshNode>();
|
|
|
|
|
threading::parallel_for(node_mask.index_range(), 1, [&](const IndexRange range) {
|
|
|
|
|
LocalData &tls = all_tls.local();
|
|
|
|
|
node_mask.slice(range).foreach_index([&](const int i) {
|
|
|
|
|
bake_mask_mesh(*depsgraph, ob, *automasking, mode, factor, nodes[i], tls, mask.span);
|
|
|
|
|
apply_mask_mesh(*depsgraph, ob, *automasking, mode, factor, nodes[i], tls, mask.span);
|
|
|
|
|
bke::pbvh::node_update_mask_mesh(mask.span, nodes[i]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@@ -1192,7 +1152,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
threading::parallel_for(node_mask.index_range(), 1, [&](const IndexRange range) {
|
|
|
|
|
LocalData &tls = all_tls.local();
|
|
|
|
|
node_mask.slice(range).foreach_index([&](const int i) {
|
|
|
|
|
bake_mask_grids(*depsgraph, ob, *automasking, mode, factor, nodes[i], tls);
|
|
|
|
|
apply_mask_grids(*depsgraph, ob, *automasking, mode, factor, nodes[i], tls);
|
|
|
|
|
bke::pbvh::node_update_mask_grids(key, masks, nodes[i]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@@ -1205,7 +1165,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
threading::parallel_for(node_mask.index_range(), 1, [&](const IndexRange range) {
|
|
|
|
|
LocalData &tls = all_tls.local();
|
|
|
|
|
node_mask.slice(range).foreach_index([&](const int i) {
|
|
|
|
|
bake_mask_bmesh(*depsgraph, ob, *automasking, mode, factor, nodes[i], tls);
|
|
|
|
|
apply_mask_bmesh(*depsgraph, ob, *automasking, mode, factor, nodes[i], tls);
|
|
|
|
|
bke::pbvh::node_update_mask_bmesh(mask_offset, nodes[i]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
@@ -1222,7 +1182,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cavity_bake_ui(bContext *C, wmOperator *op)
|
|
|
|
|
static void mask_from_cavity_ui(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
uiLayout *layout = op->layout;
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
|
|
|
|
@@ -1230,15 +1190,14 @@ static void cavity_bake_ui(bContext *C, wmOperator *op)
|
|
|
|
|
|
|
|
|
|
uiLayoutSetPropSep(layout, true);
|
|
|
|
|
uiLayoutSetPropDecorate(layout, false);
|
|
|
|
|
CavityBakeSettingsSource source = (CavityBakeSettingsSource)RNA_enum_get(op->ptr,
|
|
|
|
|
"settings_source");
|
|
|
|
|
MaskSettingsSource source = (MaskSettingsSource)RNA_enum_get(op->ptr, "settings_source");
|
|
|
|
|
|
|
|
|
|
if (!sd) {
|
|
|
|
|
source = AUTOMASK_SETTINGS_OPERATOR;
|
|
|
|
|
source = MaskSettingsSource::Operator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (source) {
|
|
|
|
|
case AUTOMASK_SETTINGS_OPERATOR: {
|
|
|
|
|
case MaskSettingsSource::Operator: {
|
|
|
|
|
uiItemR(layout, op->ptr, "mix_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
|
uiItemR(layout, op->ptr, "mix_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
|
uiItemR(layout, op->ptr, "settings_source", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
|
@@ -1254,8 +1213,8 @@ static void cavity_bake_ui(bContext *C, wmOperator *op)
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case AUTOMASK_SETTINGS_BRUSH:
|
|
|
|
|
case AUTOMASK_SETTINGS_SCENE:
|
|
|
|
|
case MaskSettingsSource::Brush:
|
|
|
|
|
case MaskSettingsSource::Scene:
|
|
|
|
|
uiItemR(layout, op->ptr, "mix_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
|
uiItemR(layout, op->ptr, "mix_factor", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
|
uiItemR(layout, op->ptr, "settings_source", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
|
@@ -1269,39 +1228,39 @@ static void SCULPT_OT_mask_from_cavity(wmOperatorType *ot)
|
|
|
|
|
ot->name = "Mask From Cavity";
|
|
|
|
|
ot->idname = "SCULPT_OT_mask_from_cavity";
|
|
|
|
|
ot->description = "Creates a mask based on the curvature of the surface";
|
|
|
|
|
ot->ui = cavity_bake_ui;
|
|
|
|
|
ot->ui = mask_from_cavity_ui;
|
|
|
|
|
|
|
|
|
|
static EnumPropertyItem mix_modes[] = {
|
|
|
|
|
{AUTOMASK_BAKE_MIX, "MIX", ICON_NONE, "Mix", ""},
|
|
|
|
|
{AUTOMASK_BAKE_MULTIPLY, "MULTIPLY", ICON_NONE, "Multiply", ""},
|
|
|
|
|
{AUTOMASK_BAKE_DIVIDE, "DIVIDE", ICON_NONE, "Divide", ""},
|
|
|
|
|
{AUTOMASK_BAKE_ADD, "ADD", ICON_NONE, "Add", ""},
|
|
|
|
|
{AUTOMASK_BAKE_SUBTRACT, "SUBTRACT", ICON_NONE, "Subtract", ""},
|
|
|
|
|
{int(ApplyMaskMode::Mix), "MIX", ICON_NONE, "Mix", ""},
|
|
|
|
|
{int(ApplyMaskMode::Multiply), "MULTIPLY", ICON_NONE, "Multiply", ""},
|
|
|
|
|
{int(ApplyMaskMode::Divide), "DIVIDE", ICON_NONE, "Divide", ""},
|
|
|
|
|
{int(ApplyMaskMode::Add), "ADD", ICON_NONE, "Add", ""},
|
|
|
|
|
{int(ApplyMaskMode::Subtract), "SUBTRACT", ICON_NONE, "Subtract", ""},
|
|
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ot->exec = sculpt_bake_cavity_exec;
|
|
|
|
|
ot->exec = mask_from_cavity_exec;
|
|
|
|
|
ot->poll = SCULPT_mode_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
RNA_def_enum(ot->srna, "mix_mode", mix_modes, AUTOMASK_BAKE_MIX, "Mode", "Mix mode");
|
|
|
|
|
RNA_def_enum(ot->srna, "mix_mode", mix_modes, int(ApplyMaskMode::Mix), "Mode", "Mix mode");
|
|
|
|
|
RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
static EnumPropertyItem settings_sources[] = {
|
|
|
|
|
{AUTOMASK_SETTINGS_OPERATOR,
|
|
|
|
|
{int(MaskSettingsSource::Operator),
|
|
|
|
|
"OPERATOR",
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
"Operator",
|
|
|
|
|
"Use settings from operator properties"},
|
|
|
|
|
{AUTOMASK_SETTINGS_BRUSH, "BRUSH", ICON_NONE, "Brush", "Use settings from brush"},
|
|
|
|
|
{AUTOMASK_SETTINGS_SCENE, "SCENE", ICON_NONE, "Scene", "Use settings from scene"},
|
|
|
|
|
{int(MaskSettingsSource::Brush), "BRUSH", ICON_NONE, "Brush", "Use settings from brush"},
|
|
|
|
|
{int(MaskSettingsSource::Scene), "SCENE", ICON_NONE, "Scene", "Use settings from scene"},
|
|
|
|
|
{0, nullptr, 0, nullptr, nullptr}};
|
|
|
|
|
|
|
|
|
|
RNA_def_enum(ot->srna,
|
|
|
|
|
"settings_source",
|
|
|
|
|
settings_sources,
|
|
|
|
|
AUTOMASK_SETTINGS_OPERATOR,
|
|
|
|
|
int(MaskSettingsSource::Operator),
|
|
|
|
|
"Settings",
|
|
|
|
|
"Use settings from here");
|
|
|
|
|
|
|
|
|
|
@@ -1328,6 +1287,8 @@ static void SCULPT_OT_mask_from_cavity(wmOperatorType *ot)
|
|
|
|
|
RNA_def_boolean(ot->srna, "invert", false, "Cavity (Inverted)", "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
} // namespace mask
|
|
|
|
|
|
|
|
|
|
void operatortypes_sculpt()
|
|
|
|
|
|