Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_automasking.cc')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.cc372
1 files changed, 372 insertions, 0 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc
new file mode 100644
index 00000000000..bb101717c9b
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2020 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_hash.h"
+#include "BLI_index_range.hh"
+#include "BLI_math.h"
+#include "BLI_task.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.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 "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "bmesh.h"
+
+#include <cmath>
+#include <cstdlib>
+
+using blender::IndexRange;
+
+AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
+{
+ if (ss->cache) {
+ return ss->cache->automasking;
+ }
+ if (ss->filter_cache) {
+ return ss->filter_cache->automasking;
+ }
+ return nullptr;
+}
+
+bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
+ const Brush *br,
+ const eAutomasking_flag mode)
+{
+ if (br) {
+ return br->automasking_flags & mode || sd->automasking_flags & mode;
+ }
+ return sd->automasking_flags & mode;
+}
+
+bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
+{
+ if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
+ return false;
+ }
+ if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
+ return true;
+ }
+ if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_FACE_SETS)) {
+ return true;
+ }
+ if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
+ return true;
+ }
+ if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
+ return true;
+ }
+ return false;
+}
+
+static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
+{
+ if (brush) {
+ return sculpt->automasking_flags | brush->automasking_flags;
+ }
+ return sculpt->automasking_flags;
+}
+
+static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
+{
+
+ const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
+ if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+ return true;
+ }
+ if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
+ return brush && brush->automasking_boundary_edges_propagation_steps != 1;
+ }
+ if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
+ return brush && brush->automasking_boundary_edges_propagation_steps != 1;
+ }
+ return false;
+}
+
+float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
+{
+ if (!automasking) {
+ return 1.0f;
+ }
+ /* If the cache is initialized with valid info, use the cache. This is used when the
+ * automasking information can't be computed in real time per vertex and needs to be
+ * initialized for the whole mesh when the stroke starts. */
+ if (automasking->factor) {
+ return automasking->factor[vert];
+ }
+
+ if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
+ if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) {
+ return 0.0f;
+ }
+ }
+
+ if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
+ if (SCULPT_vertex_is_boundary(ss, vert)) {
+ return 0.0f;
+ }
+ }
+
+ if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
+ if (!SCULPT_vertex_has_unique_face_set(ss, vert)) {
+ return 0.0f;
+ }
+ }
+
+ return 1.0f;
+}
+
+void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
+{
+ if (!automasking) {
+ return;
+ }
+
+ MEM_SAFE_FREE(automasking->factor);
+ MEM_SAFE_FREE(automasking);
+}
+
+static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
+{
+ /* 2D falloff is not constrained by radius. */
+ if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ return false;
+ }
+
+ if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE)) {
+ return true;
+ }
+ return false;
+}
+
+struct AutomaskFloodFillData {
+ float *automask_factor;
+ float radius;
+ bool use_radius;
+ float location[3];
+ char symm;
+};
+
+static bool automask_floodfill_cb(
+ SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+{
+ AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata;
+
+ data->automask_factor[to_v] = 1.0f;
+ data->automask_factor[from_v] = 1.0f;
+ return (!data->use_radius ||
+ SCULPT_is_vertex_inside_brush_radius_symm(
+ SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
+}
+
+static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
+ BLI_assert_msg(0, "Topology masking: pmap missing");
+ return nullptr;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ for (int i : IndexRange(totvert)) {
+ automask_factor[i] = 0.0f;
+ }
+
+ /* Flood fill automask to connected vertices. Limited to vertices inside
+ * the brush radius if the tool requires it. */
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
+ SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
+
+ AutomaskFloodFillData fdata = {nullptr};
+
+ fdata.automask_factor = automask_factor;
+ fdata.radius = radius;
+ fdata.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush);
+ fdata.symm = SCULPT_mesh_symmetry_xyz_get(ob);
+
+ copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
+ SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
+ SCULPT_floodfill_free(&flood);
+
+ return automask_factor;
+}
+
+static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
+ return nullptr;
+ }
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
+ BLI_assert_msg(0, "Face Sets automasking: pmap missing");
+ return nullptr;
+ }
+
+ int tot_vert = SCULPT_vertex_count_get(ss);
+ int active_face_set = SCULPT_active_face_set_get(ss);
+ for (int i : IndexRange(tot_vert)) {
+ if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ automask_factor[i] *= 0.0f;
+ }
+ }
+
+ return automask_factor;
+}
+
+#define EDGE_DISTANCE_INF -1
+
+float *SCULPT_boundary_automasking_init(Object *ob,
+ eBoundaryAutomaskMode mode,
+ int propagation_steps,
+ float *automask_factor)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->pmap) {
+ BLI_assert_msg(0, "Boundary Edges masking: pmap missing");
+ return nullptr;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor");
+
+ for (int i : IndexRange(totvert)) {
+ edge_distance[i] = EDGE_DISTANCE_INF;
+ switch (mode) {
+ case AUTOMASK_INIT_BOUNDARY_EDGES:
+ if (SCULPT_vertex_is_boundary(ss, i)) {
+ edge_distance[i] = 0;
+ }
+ break;
+ case AUTOMASK_INIT_BOUNDARY_FACE_SETS:
+ if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ edge_distance[i] = 0;
+ }
+ break;
+ }
+ }
+
+ for (int propagation_it : IndexRange(propagation_steps)) {
+ for (int i : IndexRange(totvert)) {
+ if (edge_distance[i] != EDGE_DISTANCE_INF) {
+ continue;
+ }
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ if (edge_distance[ni.index] == propagation_it) {
+ edge_distance[i] = propagation_it + 1;
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+ }
+ }
+
+ for (int i : IndexRange(totvert)) {
+ if (edge_distance[i] == EDGE_DISTANCE_INF) {
+ continue;
+ }
+ const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
+ const float edge_boundary_automask = pow2f(p);
+ automask_factor[i] *= (1.0f - edge_boundary_automask);
+ }
+
+ MEM_SAFE_FREE(edge_distance);
+ return automask_factor;
+}
+
+static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
+ SculptSession *ss,
+ Sculpt *sd,
+ Brush *brush)
+{
+ automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
+ automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
+}
+
+AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
+ return nullptr;
+ }
+
+ AutomaskingCache *automasking = (AutomaskingCache *)MEM_callocN(sizeof(AutomaskingCache),
+ "automasking cache");
+ SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush);
+ SCULPT_boundary_info_ensure(ob);
+
+ if (!SCULPT_automasking_needs_factors_cache(sd, brush)) {
+ return automasking;
+ }
+
+ automasking->factor = (float *)MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
+ for (int i : IndexRange(totvert)) {
+ automasking->factor[i] = 1.0f;
+ }
+
+ const int boundary_propagation_steps = brush ?
+ brush->automasking_boundary_edges_propagation_steps :
+ 1;
+
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_topology_automasking_init(sd, ob, automasking->factor);
+ }
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
+ SCULPT_vertex_random_access_ensure(ss);
+ sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
+ }
+
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_automasking_init(
+ ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
+ }
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_automasking_init(
+ ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
+ }
+
+ return automasking;
+}