Sculpt: Refactor distance falloff, clipping, and brush strength
Part of #118145. Rewrite the application of brush hardness, filtering for 3D view clipping and brush distance factor calculation to operate on arrays of data rather than a single element at a time. In the benchmark file from the task above, this improves performance by 5%, from 0.58s to 0.55s. I expect that's mainly because constant checks have been moved out of the hot loops, avoiding function call overhead, and because in some cases we avoid doing division for every element. Pull Request: https://projects.blender.org/blender/blender/pulls/123671
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
* General operations for brushes.
|
||||
*/
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "DNA_brush_enums.h"
|
||||
#include "DNA_color_types.h"
|
||||
#include "DNA_object_enums.h"
|
||||
@@ -87,6 +89,15 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask);
|
||||
* Library Operations
|
||||
*/
|
||||
void BKE_brush_curve_preset(Brush *b, enum eCurveMappingPreset preset);
|
||||
|
||||
/**
|
||||
* Combine the brush strength based on the distances and brush settings with the existing factors.
|
||||
*/
|
||||
void BKE_brush_calc_curve_factors(eBrushCurvePreset preset,
|
||||
const CurveMapping *cumap,
|
||||
blender::Span<float> distances,
|
||||
float brush_radius,
|
||||
blender::MutableSpan<float> factors);
|
||||
/**
|
||||
* Uses the brush curve control to find a strength value between 0 and 1.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_rand.h"
|
||||
|
||||
@@ -2618,6 +2619,129 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_brush_calc_curve_factors(const eBrushCurvePreset preset,
|
||||
const CurveMapping *cumap,
|
||||
const blender::Span<float> distances,
|
||||
const float brush_radius,
|
||||
const blender::MutableSpan<float> factors)
|
||||
{
|
||||
BLI_assert(factors.size() == distances.size());
|
||||
|
||||
const float radius_rcp = blender::math::rcp(brush_radius);
|
||||
switch (preset) {
|
||||
case BRUSH_CURVE_CUSTOM: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
factors[i] *= BKE_curvemapping_evaluateF(cumap, 0, distance * radius_rcp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_SHARP: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= factor * factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_SMOOTH: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= 3.0f * factor * factor - 2.0f * factor * factor * factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_SMOOTHER: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= pow3f(factor) * (factor * (factor * 6.0f - 15.0f) + 10.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_ROOT: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= sqrtf(factor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_LIN: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_CONSTANT: {
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_SPHERE: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= sqrtf(2 * factor - factor * factor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_POW4: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= factor * factor * factor * factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BRUSH_CURVE_INVSQUARE: {
|
||||
for (const int i : distances.index_range()) {
|
||||
const float distance = distances[i];
|
||||
if (distance >= brush_radius) {
|
||||
factors[i] = 0.0f;
|
||||
continue;
|
||||
}
|
||||
const float factor = 1.0f - distance * radius_rcp;
|
||||
factors[i] *= factor * (2.0f - factor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float BKE_brush_curve_strength(const eBrushCurvePreset preset,
|
||||
const CurveMapping *cumap,
|
||||
const float distance,
|
||||
|
||||
@@ -59,7 +59,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -68,6 +68,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_cube_distance_falloff(ss, brush, mat, positions_eval, verts, distances, factors);
|
||||
scale_factors(distances, cache.radius);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -90,7 +90,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -99,6 +99,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -54,7 +54,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -63,6 +63,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -75,7 +75,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -84,6 +84,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -56,7 +56,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -65,6 +65,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -55,7 +55,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -64,6 +64,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -62,7 +62,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -71,6 +71,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -76,7 +76,7 @@ static void calc_faces(const Brush &brush,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -85,6 +85,7 @@ static void calc_faces(const Brush &brush,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -56,7 +56,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -65,6 +65,7 @@ static void calc_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -121,7 +121,7 @@ BLI_NOINLINE static void apply_positions_faces(const Sculpt &sd,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide_and_mask(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -130,6 +130,7 @@ BLI_NOINLINE static void apply_positions_faces(const Sculpt &sd,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (cache.automasking) {
|
||||
|
||||
@@ -119,7 +119,7 @@ static void apply_masks_faces(const Brush &brush,
|
||||
tls.factors.reinitialize(verts.size());
|
||||
const MutableSpan<float> factors = tls.factors;
|
||||
fill_factor_from_hide(mesh, verts, factors);
|
||||
|
||||
filter_region_clip_factors(ss, positions_eval, verts, factors);
|
||||
if (brush.flag & BRUSH_FRONTFACE) {
|
||||
calc_front_face(cache.view_normal, vert_normals, verts, factors);
|
||||
}
|
||||
@@ -128,6 +128,7 @@ static void apply_masks_faces(const Brush &brush,
|
||||
const MutableSpan<float> distances = tls.distances;
|
||||
calc_distance_falloff(
|
||||
ss, positions_eval, verts, eBrushFalloffShape(brush.falloff_shape), distances, factors);
|
||||
apply_hardness_to_distances(cache, distances);
|
||||
calc_brush_strength_factors(cache, brush, distances, factors);
|
||||
|
||||
if (ss.cache->automasking) {
|
||||
|
||||
@@ -76,11 +76,21 @@ void calc_front_face(const float3 &view_normal,
|
||||
Span<int> vert_indices,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* When the 3D view's clipping planes are enabled, brushes shouldn't have any effect on vertices
|
||||
* outside of the planes, because they're not visible. This function disables the factors for those
|
||||
* vertices.
|
||||
*/
|
||||
void filter_region_clip_factors(const SculptSession &ss,
|
||||
Span<float3> vert_positions,
|
||||
Span<int> verts,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* Calculate distances based on the distance from the brush cursor and various other settings.
|
||||
* Also ignore vertices that are too far from the cursor.
|
||||
*/
|
||||
void calc_distance_falloff(SculptSession &ss,
|
||||
void calc_distance_falloff(const SculptSession &ss,
|
||||
Span<float3> vert_positions,
|
||||
Span<int> vert_indices,
|
||||
eBrushFalloffShape falloff_shape,
|
||||
@@ -99,6 +109,12 @@ void calc_cube_distance_falloff(SculptSession &ss,
|
||||
MutableSpan<float> r_distances,
|
||||
MutableSpan<float> factors);
|
||||
|
||||
/**
|
||||
* Scale the distances based on the brush radius and the cached "hardness" setting, which increases
|
||||
* the strength of the effect for vertices torwards the outside of the radius.
|
||||
*/
|
||||
void apply_hardness_to_distances(const StrokeCache &cache, MutableSpan<float> distances);
|
||||
|
||||
/**
|
||||
* Modify the factors based on distances to the brush cursor, using various brush settings.
|
||||
*/
|
||||
|
||||
@@ -6615,7 +6615,34 @@ void calc_front_face(const float3 &view_normal,
|
||||
}
|
||||
}
|
||||
|
||||
void calc_distance_falloff(SculptSession &ss,
|
||||
void filter_region_clip_factors(const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const Span<int> verts,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
const RegionView3D *rv3d = ss.cache ? ss.cache->vc->rv3d : ss.rv3d;
|
||||
const View3D *v3d = ss.cache ? ss.cache->vc->v3d : ss.v3d;
|
||||
if (!RV3D_CLIPPING_ENABLED(v3d, rv3d)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ePaintSymmetryFlags mirror_symmetry_pass = ss.cache ? ss.cache->mirror_symmetry_pass :
|
||||
ePaintSymmetryFlags(0);
|
||||
const int radial_symmetry_pass = ss.cache ? ss.cache->radial_symmetry_pass : 0;
|
||||
const float4x4 symm_rot_mat_inv = ss.cache ? ss.cache->symm_rot_mat_inv : float4x4::identity();
|
||||
for (const int i : verts.index_range()) {
|
||||
float3 symm_co;
|
||||
flip_v3_v3(symm_co, positions[verts[i]], mirror_symmetry_pass);
|
||||
if (radial_symmetry_pass) {
|
||||
symm_co = math::transform_point(symm_rot_mat_inv, symm_co);
|
||||
}
|
||||
if (ED_view3d_clipping_test(rv3d, symm_co, true)) {
|
||||
factors[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calc_distance_falloff(const SculptSession &ss,
|
||||
const Span<float3> positions,
|
||||
const Span<int> verts,
|
||||
const eBrushFalloffShape falloff_shape,
|
||||
@@ -6625,21 +6652,33 @@ void calc_distance_falloff(SculptSession &ss,
|
||||
BLI_assert(verts.size() == factors.size());
|
||||
BLI_assert(verts.size() == r_distances.size());
|
||||
|
||||
SculptBrushTest test;
|
||||
const SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
|
||||
ss, test, falloff_shape);
|
||||
const float3 &test_location = ss.cache ? ss.cache->location : ss.cursor_location;
|
||||
if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE && (ss.cache || ss.filter_cache)) {
|
||||
/* The tube falloff shape requires the cached view normal. */
|
||||
const float3 &view_normal = ss.cache ? ss.cache->view_normal : ss.filter_cache->view_normal;
|
||||
float4 test_plane;
|
||||
plane_from_point_normal_v3(test_plane, test_location, view_normal);
|
||||
for (const int i : verts.index_range()) {
|
||||
float3 projected;
|
||||
closest_to_plane_normalized_v3(projected, test_plane, positions[verts[i]]);
|
||||
r_distances[i] = math::distance_squared(projected, test_location);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : verts.index_range()) {
|
||||
r_distances[i] = math::distance_squared(test_location, positions[verts[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
for (const int i : verts.index_range()) {
|
||||
if (factors[i] == 0.0f) {
|
||||
r_distances[i] = FLT_MAX;
|
||||
continue;
|
||||
const float radius_sq = ss.cache ? ss.cache->radius_squared :
|
||||
ss.cursor_radius * ss.cursor_radius;
|
||||
for (const int i : r_distances.index_range()) {
|
||||
if (r_distances[i] < radius_sq) {
|
||||
r_distances[i] = std::sqrt(r_distances[i]);
|
||||
}
|
||||
if (!sculpt_brush_test_sq_fn(test, positions[verts[i]])) {
|
||||
else {
|
||||
factors[i] = 0.0f;
|
||||
r_distances[i] = FLT_MAX;
|
||||
continue;
|
||||
}
|
||||
r_distances[i] = math::sqrt(test.dist);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6673,26 +6712,38 @@ void calc_cube_distance_falloff(SculptSession &ss,
|
||||
}
|
||||
}
|
||||
|
||||
void apply_hardness_to_distances(const StrokeCache &cache, const MutableSpan<float> distances)
|
||||
{
|
||||
const float hardness = cache.paint_brush.hardness;
|
||||
if (hardness == 0.0f) {
|
||||
return;
|
||||
}
|
||||
if (hardness == 1.0f) {
|
||||
distances.fill(0.0f);
|
||||
return;
|
||||
}
|
||||
const float radius = cache.radius;
|
||||
const float threshold = hardness * radius;
|
||||
const float radius_inv = math::rcp(radius);
|
||||
const float hardness_inv_rcp = math::rcp(1.0f - hardness);
|
||||
for (const int i : distances.index_range()) {
|
||||
if (distances[i] < threshold) {
|
||||
distances[i] = 0.0f;
|
||||
}
|
||||
else {
|
||||
const float radius_factor = (distances[i] * radius_inv - hardness) * hardness_inv_rcp;
|
||||
distances[i] = radius_factor * radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calc_brush_strength_factors(const StrokeCache &cache,
|
||||
const Brush &brush,
|
||||
const Span<float> distances,
|
||||
const MutableSpan<float> factors)
|
||||
{
|
||||
BLI_assert(factors.size() == distances.size());
|
||||
|
||||
for (const int i : factors.index_range()) {
|
||||
if (factors[i] == 0.0f) {
|
||||
/* Skip already masked-out points, as they might be outside of the brush radius and be
|
||||
* unaffected anyway. Having such large values in the calculations below might lead to
|
||||
* non-finite values, leading to undesired results. */
|
||||
continue;
|
||||
}
|
||||
|
||||
const float hardness = sculpt_apply_hardness(cache, distances[i]);
|
||||
const float strength = BKE_brush_curve_strength(&brush, hardness, cache.radius);
|
||||
|
||||
factors[i] *= strength;
|
||||
}
|
||||
BKE_brush_calc_curve_factors(
|
||||
eBrushCurvePreset(brush.curve_preset), brush.curve, distances, cache.radius, factors);
|
||||
}
|
||||
|
||||
void calc_brush_texture_factors(SculptSession &ss,
|
||||
|
||||
Reference in New Issue
Block a user