Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_automasking.cc
| /* SPDX-License-Identifier: GPL-2.0-or-later | /* SPDX-License-Identifier: GPL-2.0-or-later | ||||
| * Copyright 2020 Blender Foundation. All rights reserved. */ | * Copyright 2020 Blender Foundation. All rights reserved. */ | ||||
| /** \file | /** \file | ||||
| * \ingroup edsculpt | * \ingroup edsculpt | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_array.hh" | |||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_hash.h" | #include "BLI_hash.h" | ||||
| #include "BLI_index_range.hh" | #include "BLI_index_range.hh" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_math_vec_types.hh" | |||||
| #include "BLI_set.hh" | |||||
| #include "BLI_task.h" | #include "BLI_task.h" | ||||
| #include "BLI_vector.hh" | |||||
| #include "DNA_brush_types.h" | #include "DNA_brush_types.h" | ||||
| #include "DNA_mesh_types.h" | #include "DNA_mesh_types.h" | ||||
| #include "DNA_meshdata_types.h" | #include "DNA_meshdata_types.h" | ||||
| #include "BKE_brush.h" | #include "BKE_brush.h" | ||||
| #include "BKE_colortools.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "BKE_mesh_mapping.h" | #include "BKE_mesh_mapping.h" | ||||
| #include "BKE_object.h" | #include "BKE_object.h" | ||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_pbvh.h" | #include "BKE_pbvh.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| Show All 13 Lines | |||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "bmesh.h" | #include "bmesh.h" | ||||
| #include <cmath> | #include <cmath> | ||||
| #include <cstdlib> | #include <cstdlib> | ||||
| using blender::float3; | |||||
| using blender::IndexRange; | using blender::IndexRange; | ||||
| using blender::Set; | |||||
| using blender::Vector; | |||||
| AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) | AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) | ||||
| { | { | ||||
| if (ss->cache) { | if (ss->cache) { | ||||
| return ss->cache->automasking; | return ss->cache->automasking; | ||||
| } | } | ||||
| if (ss->filter_cache) { | if (ss->filter_cache) { | ||||
| return ss->filter_cache->automasking; | return ss->filter_cache->automasking; | ||||
| } | } | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, | bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, | ||||
| const Brush *br, | const Brush *br, | ||||
| const eAutomasking_flag mode) | const eAutomasking_flag mode) | ||||
| { | { | ||||
| int automasking = sd->automasking_flags; | |||||
| if (br) { | if (br) { | ||||
| return br->automasking_flags & mode || sd->automasking_flags & mode; | automasking |= br->automasking_flags; | ||||
| } | } | ||||
| return sd->automasking_flags & mode; | |||||
| return (eAutomasking_flag)automasking & mode; | |||||
| } | } | ||||
| bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) | bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) | ||||
| { | { | ||||
| if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) { | if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) { | if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_FACE_SETS)) { | if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_FACE_SETS)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { | if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { | if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BRUSH_NORMAL)) { | |||||
| return true; | |||||
| } | |||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_VIEW_NORMAL)) { | |||||
| return true; | |||||
| } | |||||
| if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CAVITY_ALL)) { | |||||
| return true; | |||||
| } | |||||
| return false; | return false; | ||||
| } | } | ||||
| static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush) | static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush) | ||||
| { | { | ||||
| if (brush) { | if (brush) { | ||||
| return sculpt->automasking_flags | brush->automasking_flags; | int flags = sculpt->automasking_flags | brush->automasking_flags; | ||||
| /* Check if we are using brush cavity settings. */ | |||||
| if (brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL) { | |||||
| flags &= ~(BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE | | |||||
| BRUSH_AUTOMASKING_CAVITY_NORMAL); | |||||
| flags |= brush->automasking_flags; | |||||
| } | |||||
| else if (sculpt->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL) { | |||||
| flags &= ~(BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE | | |||||
| BRUSH_AUTOMASKING_CAVITY_NORMAL); | |||||
| flags |= sculpt->automasking_flags; | |||||
| } | |||||
| return flags; | |||||
| } | } | ||||
| return sculpt->automasking_flags; | return sculpt->automasking_flags; | ||||
| } | } | ||||
| bool SCULPT_automasking_needs_normal(const SculptSession * /*ss*/, | |||||
| const Sculpt *sculpt, | |||||
| const Brush *brush) | |||||
| { | |||||
| int flags = sculpt_automasking_mode_effective_bits(sculpt, brush); | |||||
| return flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); | |||||
| } | |||||
| static float sculpt_automasking_normal_calc(SculptSession *ss, | |||||
| PBVHVertRef vertex, | |||||
| float3 &normal, | |||||
| float limit_lower, | |||||
| float limit_upper, | |||||
| AutomaskingNodeData *automask_data) | |||||
| { | |||||
| float3 normal_v; | |||||
| if (automask_data->have_orig_data) { | |||||
| normal_v = automask_data->orig_data.no; | |||||
| } | |||||
| else { | |||||
| SCULPT_vertex_normal_get(ss, vertex, normal_v); | |||||
| } | |||||
| float angle = saacos(dot_v3v3(normal, normal_v)); | |||||
| /* note that limit is pre-divided by M_PI */ | |||||
| if (angle > limit_lower && angle < limit_upper) { | |||||
| float t = 1.0f - (angle - limit_lower) / (limit_upper - limit_lower); | |||||
| /* smoothstep */ | |||||
| t = t * t * (3.0 - 2.0 * t); | |||||
| return t; | |||||
| } | |||||
| else if (angle > limit_upper) { | |||||
| return 0.0f; | |||||
| } | |||||
| return 1.0f; | |||||
| } | |||||
| static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush) | static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush) | ||||
| { | { | ||||
| const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush); | const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush); | ||||
| if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { | if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) { | |||||
| return brush && brush->automasking_boundary_edges_propagation_steps != 1; | if (automasking_flags & | ||||
| } | (BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS | | ||||
| if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) { | BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL)) { | ||||
| return brush && brush->automasking_boundary_edges_propagation_steps != 1; | return brush && brush->automasking_boundary_edges_propagation_steps != 1; | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static float automasking_brush_normal_factor(AutomaskingCache *automasking, | |||||
| SculptSession *ss, | |||||
| PBVHVertRef vertex, | |||||
| AutomaskingNodeData *automask_data) | |||||
| { | |||||
| float falloff = automasking->settings.start_normal_falloff * M_PI; | |||||
| float3 initial_normal; | |||||
| if (ss->cache) { | |||||
| initial_normal = ss->cache->initial_normal; | |||||
| } | |||||
| else { | |||||
| initial_normal = ss->filter_cache->initial_normal; | |||||
| } | |||||
| return sculpt_automasking_normal_calc(ss, | |||||
| vertex, | |||||
| initial_normal, | |||||
| automasking->settings.start_normal_limit - falloff * 0.5f, | |||||
| automasking->settings.start_normal_limit + falloff * 0.5f, | |||||
| automask_data); | |||||
| } | |||||
| static float automasking_view_normal_factor(AutomaskingCache *automasking, | |||||
| SculptSession *ss, | |||||
| PBVHVertRef vertex, | |||||
| AutomaskingNodeData *automask_data) | |||||
| { | |||||
| float falloff = automasking->settings.view_normal_falloff * M_PI; | |||||
| float3 view_normal; | |||||
| if (ss->cache) { | |||||
| view_normal = ss->cache->view_normal; | |||||
| } | |||||
| else { | |||||
| view_normal = ss->filter_cache->view_normal; | |||||
| } | |||||
| return sculpt_automasking_normal_calc(ss, | |||||
| vertex, | |||||
| view_normal, | |||||
| automasking->settings.view_normal_limit, | |||||
| automasking->settings.view_normal_limit + falloff, | |||||
| automask_data); | |||||
| } | |||||
| static float automasking_view_occlusion_factor(AutomaskingCache *automasking, | |||||
| SculptSession *ss, | |||||
| PBVHVertRef vertex, | |||||
| uchar stroke_id, | |||||
| AutomaskingNodeData * /*automask_data*/) | |||||
| { | |||||
| char f = *(char *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion); | |||||
| if (stroke_id != automasking->current_stroke_id) { | |||||
| f = *(char *)SCULPT_vertex_attr_get( | |||||
| vertex, | |||||
| ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1; | |||||
| } | |||||
| return f == 2; | |||||
| } | |||||
| /* Updates vertex stroke id. */ | |||||
| static float automasking_factor_end(SculptSession *ss, | |||||
| AutomaskingCache *automasking, | |||||
| PBVHVertRef vertex, | |||||
| float value) | |||||
| { | |||||
| if (ss->attrs.automasking_stroke_id) { | |||||
| *(uchar *)SCULPT_vertex_attr_get( | |||||
| vertex, ss->attrs.automasking_stroke_id) = automasking->current_stroke_id; | |||||
| } | |||||
| return value; | |||||
| } | |||||
| static float sculpt_cavity_calc_factor(AutomaskingCache *automasking, float factor) | |||||
| { | |||||
| float sign = signf(factor); | |||||
| factor = fabsf(factor) * automasking->settings.cavity_factor * 50.0f; | |||||
| factor = factor * sign * 0.5f + 0.5f; | |||||
| CLAMP(factor, 0.0f, 1.0f); | |||||
| return (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED) ? 1.0f - factor : | |||||
| factor; | |||||
| } | |||||
| struct CavityBlurVert { | |||||
| PBVHVertRef vertex; | |||||
| float dist; | |||||
| int depth; | |||||
| CavityBlurVert(PBVHVertRef vertex_, float dist_, int depth_) | |||||
| : vertex(vertex_), dist(dist_), depth(depth_) | |||||
| { | |||||
| } | |||||
| CavityBlurVert() = default; | |||||
| }; | |||||
| static void sculpt_calc_blurred_cavity(SculptSession *ss, | |||||
| AutomaskingCache *automasking, | |||||
| int steps, | |||||
| PBVHVertRef vertex) | |||||
| { | |||||
| float3 sno1(0.0f); | |||||
| float3 sno2(0.0f); | |||||
| float3 sco1(0.0f); | |||||
| float3 sco2(0.0f); | |||||
| float len1_sum = 0.0f; | |||||
| int sco1_len = 0, sco2_len = 0; | |||||
| /* Steps starts at 1, but API and user interface | |||||
| * are zero-based. | |||||
| */ | |||||
| steps++; | |||||
| Vector<CavityBlurVert, 64> queue; | |||||
| Set<int64_t, 64> visit; | |||||
| int start = 0, end = 0; | |||||
| queue.resize(64); | |||||
| CavityBlurVert initial(vertex, 0.0f, 0); | |||||
| visit.add_new(vertex.i); | |||||
| queue[0] = initial; | |||||
| end = 1; | |||||
| const float *co1 = SCULPT_vertex_co_get(ss, vertex); | |||||
| while (start != end) { | |||||
| CavityBlurVert &blurvert = queue[start]; | |||||
| PBVHVertRef v = blurvert.vertex; | |||||
| start = (start + 1) % queue.size(); | |||||
| float3 no; | |||||
| const float *co = SCULPT_vertex_co_get(ss, v); | |||||
| SCULPT_vertex_normal_get(ss, v, no); | |||||
| float centdist = len_v3v3(co, co1); | |||||
| sco1 += co; | |||||
| sno1 += no; | |||||
| len1_sum += centdist; | |||||
| sco1_len++; | |||||
| if (blurvert.depth < steps) { | |||||
| sco2 += co; | |||||
| sno2 += no; | |||||
| sco2_len++; | |||||
| } | |||||
| if (blurvert.depth >= steps) { | |||||
| continue; | |||||
| } | |||||
| SculptVertexNeighborIter ni; | |||||
| SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v, ni) { | |||||
| PBVHVertRef v2 = ni.vertex; | |||||
| if (visit.contains(v2.i)) { | |||||
| continue; | |||||
| } | |||||
| float dist = len_v3v3(SCULPT_vertex_co_get(ss, v2), SCULPT_vertex_co_get(ss, v)); | |||||
| visit.add_new(v2.i); | |||||
| CavityBlurVert blurvert2(v2, dist, blurvert.depth + 1); | |||||
| int nextend = (end + 1) % queue.size(); | |||||
| if (nextend == start) { | |||||
| int oldsize = queue.size(); | |||||
| queue.resize(queue.size() << 1); | |||||
| if (end < start) { | |||||
| int n = oldsize - start; | |||||
| for (int i = 0; i < n; i++) { | |||||
| queue[queue.size() - n + i] = queue[i + start]; | |||||
| } | |||||
| start = queue.size() - n; | |||||
| } | |||||
| } | |||||
| queue[end] = blurvert2; | |||||
| end = (end + 1) % queue.size(); | |||||
| } | |||||
| SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | |||||
| } | |||||
| BLI_assert(sco1_len != sco2_len); | |||||
| if (!sco1_len) { | |||||
| sco1 = SCULPT_vertex_co_get(ss, vertex); | |||||
| } | |||||
| else { | |||||
| sco1 /= float(sco1_len); | |||||
| len1_sum /= sco1_len; | |||||
| } | |||||
| if (!sco2_len) { | |||||
| sco2 = SCULPT_vertex_co_get(ss, vertex); | |||||
| } | |||||
| else { | |||||
| sco2 /= float(sco2_len); | |||||
| } | |||||
| normalize_v3(sno1); | |||||
| if (dot_v3v3(sno1, sno1) == 0.0f) { | |||||
| SCULPT_vertex_normal_get(ss, vertex, sno1); | |||||
| } | |||||
| normalize_v3(sno2); | |||||
| if (dot_v3v3(sno2, sno2) == 0.0f) { | |||||
| SCULPT_vertex_normal_get(ss, vertex, sno2); | |||||
| } | |||||
| float3 vec = sco1 - sco2; | |||||
| float factor_sum = dot_v3v3(vec, sno2) / len1_sum; | |||||
| factor_sum = sculpt_cavity_calc_factor(automasking, factor_sum); | |||||
| *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum; | |||||
| } | |||||
| int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking) | |||||
| { | |||||
| SculptSession *ss = ob->sculpt; | |||||
| int hash; | |||||
| int totvert = SCULPT_vertex_count_get(ss); | |||||
| hash = BLI_hash_int(automasking->settings.flags); | |||||
| hash = BLI_hash_int_2d(hash, totvert); | |||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { | |||||
| hash = BLI_hash_int_2d(hash, automasking->settings.cavity_blur_steps); | |||||
| hash = BLI_hash_int_2d(hash, *reinterpret_cast<uint *>(&automasking->settings.cavity_factor)); | |||||
| if (automasking->settings.cavity_curve) { | |||||
| CurveMap *cm = automasking->settings.cavity_curve->cm; | |||||
| for (int i = 0; i < cm->totpoint; i++) { | |||||
| hash = BLI_hash_int_2d(hash, *reinterpret_cast<uint *>(&cm->curve[i].x)); | |||||
| hash = BLI_hash_int_2d(hash, *reinterpret_cast<uint *>(&cm->curve[i].y)); | |||||
| hash = BLI_hash_int_2d(hash, uint(cm->curve[i].flag)); | |||||
| hash = BLI_hash_int_2d(hash, uint(cm->curve[i].shorty)); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { | |||||
| hash = BLI_hash_int_2d(hash, automasking->settings.initial_face_set); | |||||
| } | |||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL) { | |||||
| hash = BLI_hash_int_2d(hash, | |||||
| *reinterpret_cast<uint *>(&automasking->settings.view_normal_falloff)); | |||||
| hash = BLI_hash_int_2d(hash, | |||||
| *reinterpret_cast<uint *>(&automasking->settings.view_normal_limit)); | |||||
| } | |||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL) { | |||||
| hash = BLI_hash_int_2d(hash, | |||||
| *reinterpret_cast<uint *>(&automasking->settings.start_normal_falloff)); | |||||
| hash = BLI_hash_int_2d(hash, | |||||
| *reinterpret_cast<uint *>(&automasking->settings.start_normal_limit)); | |||||
| } | |||||
| return hash; | |||||
| } | |||||
| static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, | |||||
| SculptSession *ss, | |||||
| PBVHVertRef vertex) | |||||
| { | |||||
| uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id); | |||||
| if (stroke_id != automasking->current_stroke_id) { | |||||
| sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex); | |||||
| } | |||||
| float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity); | |||||
| bool inverted = automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED; | |||||
| if ((automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) && | |||||
| (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_USE_CURVE)) { | |||||
| factor = inverted ? 1.0f - factor : factor; | |||||
| factor = BKE_curvemapping_evaluateF(automasking->settings.cavity_curve, 0, factor); | |||||
| factor = inverted ? 1.0f - factor : factor; | |||||
| } | |||||
| return factor; | |||||
| } | |||||
| float SCULPT_automasking_factor_get(AutomaskingCache *automasking, | float SCULPT_automasking_factor_get(AutomaskingCache *automasking, | ||||
| SculptSession *ss, | SculptSession *ss, | ||||
| PBVHVertRef vert) | PBVHVertRef vert, | ||||
| AutomaskingNodeData *automask_data) | |||||
| { | { | ||||
| if (!automasking) { | if (!automasking) { | ||||
| return 1.0f; | return 1.0f; | ||||
| } | } | ||||
| /* If the cache is initialized with valid info, use the cache. This is used when the | /* 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 | * automasking information can't be computed in real time per vertex and needs to be | ||||
| * initialized for the whole mesh when the stroke starts. */ | * initialized for the whole mesh when the stroke starts. */ | ||||
| if (ss->attrs.automasking_factor) { | if (ss->attrs.automasking_factor) { | ||||
| return *(float *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor); | float factor = *(float *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor); | ||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { | |||||
| factor *= sculpt_automasking_cavity_factor(automasking, ss, vert); | |||||
| } | |||||
| return factor; | |||||
| } | |||||
| uchar stroke_id = ss->attrs.automasking_stroke_id ? | |||||
| *(uchar *)(SCULPT_vertex_attr_get(vert, ss->attrs.automasking_stroke_id)) : | |||||
| -1; | |||||
| bool do_occlusion = (automasking->settings.flags & | |||||
| (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL)) == | |||||
| (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL); | |||||
| if (do_occlusion && | |||||
| automasking_view_occlusion_factor(automasking, ss, vert, stroke_id, automask_data)) { | |||||
| return automasking_factor_end(ss, automasking, vert, 0.0f); | |||||
| } | } | ||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { | if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { | ||||
| if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) { | if (!SCULPT_vertex_has_face_set(ss, vert, automasking->settings.initial_face_set)) { | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) { | if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) { | ||||
| if (SCULPT_vertex_is_boundary(ss, vert)) { | if (SCULPT_vertex_is_boundary(ss, vert)) { | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) { | if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) { | ||||
| if (!SCULPT_vertex_has_unique_face_set(ss, vert)) { | if (!SCULPT_vertex_has_unique_face_set(ss, vert)) { | ||||
| return 0.0f; | return 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| return 1.0f; | float mask = 1.0f; | ||||
| if ((ss->cache || ss->filter_cache) && | |||||
| (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL)) { | |||||
| mask *= automasking_brush_normal_factor(automasking, ss, vert, automask_data); | |||||
| } | |||||
| if ((ss->cache || ss->filter_cache) && | |||||
| (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) { | |||||
| mask *= automasking_view_normal_factor(automasking, ss, vert, automask_data); | |||||
| } | |||||
| if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { | |||||
| mask *= sculpt_automasking_cavity_factor(automasking, ss, vert); | |||||
| } | |||||
| return automasking_factor_end(ss, automasking, vert, mask); | |||||
| } | } | ||||
| void SCULPT_automasking_cache_free(AutomaskingCache *automasking) | void SCULPT_automasking_cache_free(AutomaskingCache *automasking) | ||||
| { | { | ||||
| if (!automasking) { | if (!automasking) { | ||||
| return; | return; | ||||
| } | } | ||||
| Show All 15 Lines | |||||
| struct AutomaskFloodFillData { | struct AutomaskFloodFillData { | ||||
| float radius; | float radius; | ||||
| bool use_radius; | bool use_radius; | ||||
| float location[3]; | float location[3]; | ||||
| char symm; | char symm; | ||||
| }; | }; | ||||
| static bool automask_floodfill_cb(SculptSession *ss, | static bool automask_floodfill_cb( | ||||
| PBVHVertRef from_v, | SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool /*is_duplicate*/, void *userdata) | ||||
| PBVHVertRef to_v, | |||||
| bool UNUSED(is_duplicate), | |||||
| void *userdata) | |||||
| { | { | ||||
| AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; | AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; | ||||
| *(float *)SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f; | *(float *)SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f; | ||||
| *(float *)SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f; | *(float *)SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f; | ||||
| return (!data->use_radius || | return (!data->use_radius || | ||||
| SCULPT_is_vertex_inside_brush_radius_symm( | SCULPT_is_vertex_inside_brush_radius_symm( | ||||
| SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); | SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); | ||||
| } | } | ||||
| static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) | static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| Brush *brush = BKE_paint_brush(&sd->paint); | Brush *brush = BKE_paint_brush(&sd->paint); | ||||
| if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { | if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { | ||||
| BLI_assert_msg(0, "Topology masking: pmap missing"); | BLI_assert_unreachable(); | ||||
| return; | return; | ||||
| } | } | ||||
| const int totvert = SCULPT_vertex_count_get(ss); | const int totvert = SCULPT_vertex_count_get(ss); | ||||
| for (int i : IndexRange(totvert)) { | for (int i : IndexRange(totvert)) { | ||||
| PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | ||||
| (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; | (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; | ||||
| ▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | |||||
| static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, | static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, | ||||
| SculptSession *ss, | SculptSession *ss, | ||||
| Sculpt *sd, | Sculpt *sd, | ||||
| Brush *brush) | Brush *brush) | ||||
| { | { | ||||
| automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); | automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); | ||||
| automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); | automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); | ||||
| automasking->settings.view_normal_limit = sd->automasking_view_normal_limit; | |||||
| automasking->settings.view_normal_falloff = sd->automasking_view_normal_falloff; | |||||
| automasking->settings.start_normal_limit = sd->automasking_start_normal_limit; | |||||
| automasking->settings.start_normal_falloff = sd->automasking_start_normal_falloff; | |||||
| if (brush && (brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) { | |||||
| automasking->settings.cavity_curve = brush->automasking_cavity_curve; | |||||
| automasking->settings.cavity_factor = brush->automasking_cavity_factor; | |||||
| automasking->settings.cavity_blur_steps = brush->automasking_cavity_blur_steps; | |||||
| } | |||||
| else { | |||||
| automasking->settings.cavity_curve = sd->automasking_cavity_curve; | |||||
| automasking->settings.cavity_factor = sd->automasking_cavity_factor; | |||||
| automasking->settings.cavity_blur_steps = sd->automasking_cavity_blur_steps; | |||||
| } | |||||
| } | |||||
| static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automasking, | |||||
| Object *ob, | |||||
| eAutomasking_flag mode) | |||||
| { | |||||
| SculptSession *ss = ob->sculpt; | |||||
| const int totvert = SCULPT_vertex_count_get(ss); | |||||
| /* No need to build original data since this is only called at the beginning of strokes.*/ | |||||
| AutomaskingNodeData nodedata; | |||||
| nodedata.have_orig_data = false; | |||||
| for (int i = 0; i < totvert; i++) { | |||||
| PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | |||||
| float f = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor); | |||||
| if (int(mode) & BRUSH_AUTOMASKING_BRUSH_NORMAL) { | |||||
| f *= automasking_brush_normal_factor(automasking, ss, vertex, &nodedata); | |||||
| } | |||||
| if (int(mode) & BRUSH_AUTOMASKING_VIEW_NORMAL) { | |||||
| if (int(mode) & BRUSH_AUTOMASKING_VIEW_OCCLUSION) { | |||||
| f *= automasking_view_occlusion_factor(automasking, ss, vertex, -1, &nodedata); | |||||
| } | |||||
| f *= automasking_view_normal_factor(automasking, ss, vertex, &nodedata); | |||||
| } | |||||
| if (ss->attrs.automasking_stroke_id) { | |||||
| *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id) = ss->stroke_id; | |||||
| } | |||||
| *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f; | |||||
| } | |||||
| } | |||||
| bool SCULPT_tool_can_reuse_automask(int sculpt_tool) | |||||
| { | |||||
| return ELEM(sculpt_tool, | |||||
| SCULPT_TOOL_PAINT, | |||||
| SCULPT_TOOL_SMEAR, | |||||
| SCULPT_TOOL_MASK, | |||||
| SCULPT_TOOL_DRAW_FACE_SETS); | |||||
| } | } | ||||
| AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) | AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| const int totvert = SCULPT_vertex_count_get(ss); | const int totvert = SCULPT_vertex_count_get(ss); | ||||
| if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { | if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| AutomaskingCache *automasking = (AutomaskingCache *)MEM_callocN(sizeof(AutomaskingCache), | AutomaskingCache *automasking = (AutomaskingCache *)MEM_callocN(sizeof(AutomaskingCache), | ||||
| "automasking cache"); | "automasking cache"); | ||||
| SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush); | SCULPT_automasking_cache_settings_update(automasking, ss, sd, brush); | ||||
| SCULPT_boundary_info_ensure(ob); | SCULPT_boundary_info_ensure(ob); | ||||
| automasking->current_stroke_id = ss->stroke_id; | |||||
| bool use_stroke_id = false; | |||||
| int mode = sculpt_automasking_mode_effective_bits(sd, brush); | |||||
| if ((mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && (mode & BRUSH_AUTOMASKING_VIEW_NORMAL)) { | |||||
| use_stroke_id = true; | |||||
| if (!ss->attrs.automasking_occlusion) { | |||||
| SculptAttributeParams params = {0}; | |||||
| ss->attrs.automasking_occlusion = BKE_sculpt_attribute_ensure( | |||||
| ob, | |||||
| ATTR_DOMAIN_POINT, | |||||
| CD_PROP_INT8, | |||||
| SCULPT_ATTRIBUTE_NAME(automasking_occlusion), | |||||
| ¶ms); | |||||
| } | |||||
| } | |||||
| if (mode & BRUSH_AUTOMASKING_CAVITY_ALL) { | |||||
| use_stroke_id = true; | |||||
| if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CAVITY_USE_CURVE)) { | |||||
| BKE_curvemapping_init(brush->automasking_cavity_curve); | |||||
| BKE_curvemapping_init(sd->automasking_cavity_curve); | |||||
| } | |||||
| if (!ss->attrs.automasking_cavity) { | |||||
| SculptAttributeParams params = {0}; | |||||
| ss->attrs.automasking_cavity = BKE_sculpt_attribute_ensure( | |||||
| ob, | |||||
| ATTR_DOMAIN_POINT, | |||||
| CD_PROP_FLOAT, | |||||
| SCULPT_ATTRIBUTE_NAME(automasking_cavity), | |||||
| ¶ms); | |||||
| } | |||||
| } | |||||
| if (use_stroke_id) { | |||||
| SCULPT_stroke_id_ensure(ob); | |||||
| bool have_occlusion = (mode & BRUSH_AUTOMASKING_VIEW_OCCLUSION) && | |||||
| (mode & BRUSH_AUTOMASKING_VIEW_NORMAL); | |||||
| if (brush && SCULPT_tool_can_reuse_automask(brush->sculpt_tool) && !have_occlusion) { | |||||
| int hash = SCULPT_automasking_settings_hash(ob, automasking); | |||||
| if (hash == ss->last_automasking_settings_hash) { | |||||
| automasking->current_stroke_id = ss->last_automask_stroke_id; | |||||
| automasking->can_reuse_mask = true; | |||||
| } | |||||
| } | |||||
| if (!automasking->can_reuse_mask) { | |||||
| ss->last_automask_stroke_id = ss->stroke_id; | |||||
| } | |||||
| } | |||||
| if (!SCULPT_automasking_needs_factors_cache(sd, brush)) { | if (!SCULPT_automasking_needs_factors_cache(sd, brush)) { | ||||
| return automasking; | return automasking; | ||||
| } | } | ||||
| SculptAttributeParams params = {0}; | SculptAttributeParams params = {0}; | ||||
| params.stroke_only = true; | params.stroke_only = true; | ||||
| ss->attrs.automasking_factor = BKE_sculpt_attribute_ensure( | ss->attrs.automasking_factor = BKE_sculpt_attribute_ensure( | ||||
| ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(automasking_factor), ¶ms); | ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(automasking_factor), ¶ms); | ||||
| float initial_value; | |||||
| /* Topology, boundary and boundary face sets build up the mask | |||||
| * from zero which other modes can subtract from. If none of them are | |||||
| * enabled initialize to 1. | |||||
| */ | |||||
| if (!(mode & (BRUSH_AUTOMASKING_BOUNDARY_EDGES | BRUSH_AUTOMASKING_TOPOLOGY | | |||||
| BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS))) { | |||||
| initial_value = 1.0f; | |||||
| } | |||||
| else { | |||||
| initial_value = 0.0f; | |||||
| } | |||||
| for (int i : IndexRange(totvert)) { | for (int i : IndexRange(totvert)) { | ||||
| PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); | ||||
| (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; | (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = initial_value; | ||||
| } | } | ||||
| const int boundary_propagation_steps = brush ? | const int boundary_propagation_steps = brush ? | ||||
| brush->automasking_boundary_edges_propagation_steps : | brush->automasking_boundary_edges_propagation_steps : | ||||
| 1; | 1; | ||||
| /* Additive modes. */ | |||||
| if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) { | if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) { | ||||
| SCULPT_vertex_random_access_ensure(ss); | SCULPT_vertex_random_access_ensure(ss); | ||||
| SCULPT_topology_automasking_init(sd, ob); | SCULPT_topology_automasking_init(sd, ob); | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) { | if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) { | ||||
| SCULPT_vertex_random_access_ensure(ss); | SCULPT_vertex_random_access_ensure(ss); | ||||
| sculpt_face_sets_automasking_init(sd, ob); | sculpt_face_sets_automasking_init(sd, ob); | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { | if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { | ||||
| SCULPT_vertex_random_access_ensure(ss); | SCULPT_vertex_random_access_ensure(ss); | ||||
| SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps); | SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps); | ||||
| } | } | ||||
| if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { | if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { | ||||
| SCULPT_vertex_random_access_ensure(ss); | SCULPT_vertex_random_access_ensure(ss); | ||||
| SCULPT_boundary_automasking_init( | SCULPT_boundary_automasking_init( | ||||
| ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps); | ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps); | ||||
| } | } | ||||
| /* Subtractive modes. */ | |||||
| int normal_bits = sculpt_automasking_mode_effective_bits(sd, brush) & | |||||
| (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL | | |||||
| BRUSH_AUTOMASKING_VIEW_OCCLUSION); | |||||
| if (normal_bits) { | |||||
| sculpt_normal_occlusion_automasking_fill(automasking, ob, (eAutomasking_flag)normal_bits); | |||||
| } | |||||
| return automasking; | return automasking; | ||||
| } | } | ||||
| bool SCULPT_automasking_needs_original(const Sculpt *sd, const Brush *brush) | |||||
| { | |||||
| return sculpt_automasking_mode_effective_bits(sd, brush) & | |||||
| (BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_BRUSH_NORMAL | | |||||
| BRUSH_AUTOMASKING_VIEW_NORMAL); | |||||
| } | |||||