Sculpt: Initialize hairs with custom radius #118339
|
@ -801,16 +801,22 @@ def brush_settings(layout, context, brush, popover=False):
|
|||
layout.prop(brush.curves_sculpt_settings, "add_amount")
|
||||
col = layout.column(heading="Interpolate", align=True)
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length")
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_radius", text="Radius")
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape")
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count")
|
||||
|
||||
col = layout.column()
|
||||
col.active = not brush.curves_sculpt_settings.interpolate_length
|
||||
col.prop(brush.curves_sculpt_settings, "curve_length")
|
||||
col.prop(brush.curves_sculpt_settings, "curve_length", text="Length")
|
||||
|
||||
col = layout.column()
|
||||
col.active = not brush.curves_sculpt_settings.interpolate_radius
|
||||
col.prop(brush.curves_sculpt_settings, "curve_radius", text="Radius")
|
||||
|
||||
col = layout.column()
|
||||
col.active = not brush.curves_sculpt_settings.interpolate_point_count
|
||||
col.prop(brush.curves_sculpt_settings, "points_per_curve")
|
||||
col.prop(brush.curves_sculpt_settings, "points_per_curve", text="Points")
|
||||
|
||||
elif brush.curves_sculpt_tool == 'GROW_SHRINK':
|
||||
layout.prop(brush.curves_sculpt_settings, "scale_uniform")
|
||||
layout.prop(brush.curves_sculpt_settings, "minimum_length")
|
||||
|
|
|
@ -8723,6 +8723,7 @@ class VIEW3D_PT_curves_sculpt_add_shape(Panel):
|
|||
|
||||
col = layout.column(heading="Interpolate", align=True)
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length")
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_radius", text="Radius")
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape")
|
||||
col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count")
|
||||
|
||||
|
@ -8730,6 +8731,10 @@ class VIEW3D_PT_curves_sculpt_add_shape(Panel):
|
|||
col.active = not brush.curves_sculpt_settings.interpolate_length
|
||||
col.prop(brush.curves_sculpt_settings, "curve_length", text="Length")
|
||||
|
||||
col = layout.column()
|
||||
col.active = not brush.curves_sculpt_settings.interpolate_radius
|
||||
col.prop(brush.curves_sculpt_settings, "curve_radius", text="Radius")
|
||||
|
||||
col = layout.column()
|
||||
col.active = not brush.curves_sculpt_settings.interpolate_point_count
|
||||
col.prop(brush.curves_sculpt_settings, "points_per_curve", text="Points")
|
||||
|
|
|
@ -1645,10 +1645,12 @@ void BKE_brush_init_curves_sculpt_settings(Brush *brush)
|
|||
brush->curves_sculpt_settings = MEM_cnew<BrushCurvesSculptSettings>(__func__);
|
||||
}
|
||||
BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings;
|
||||
settings->flag = BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS;
|
||||
settings->add_amount = 1;
|
||||
settings->points_per_curve = 8;
|
||||
settings->minimum_length = 0.01f;
|
||||
settings->curve_length = 0.3f;
|
||||
settings->curve_radius = 0.01f;
|
||||
settings->density_add_attempts = 100;
|
||||
settings->curve_parameter_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
|
|
@ -207,12 +207,15 @@ struct AddOperationExecutor {
|
|||
add_inputs.uvs = sampled_uvs;
|
||||
add_inputs.interpolate_length = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
|
||||
add_inputs.interpolate_radius = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS;
|
||||
add_inputs.interpolate_shape = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE;
|
||||
add_inputs.interpolate_point_count = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT;
|
||||
add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
|
||||
add_inputs.fallback_curve_length = brush_settings_->curve_length;
|
||||
add_inputs.fallback_curve_radius = brush_settings_->curve_radius;
|
||||
add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
|
||||
add_inputs.transforms = &transforms_;
|
||||
add_inputs.surface_corner_tris = surface_corner_tris_orig;
|
||||
|
@ -220,8 +223,9 @@ struct AddOperationExecutor {
|
|||
add_inputs.surface = &surface_orig;
|
||||
add_inputs.corner_normals_su = corner_normals_su;
|
||||
|
||||
if (add_inputs.interpolate_length || add_inputs.interpolate_shape ||
|
||||
add_inputs.interpolate_point_count || add_inputs.interpolate_resolution)
|
||||
if (add_inputs.interpolate_length || add_inputs.interpolate_radius ||
|
||||
add_inputs.interpolate_shape || add_inputs.interpolate_point_count ||
|
||||
add_inputs.interpolate_resolution)
|
||||
{
|
||||
this->ensure_curve_roots_kdtree();
|
||||
add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
|
||||
|
|
|
@ -261,12 +261,15 @@ struct DensityAddOperationExecutor {
|
|||
add_inputs.uvs = new_uvs;
|
||||
add_inputs.interpolate_length = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
|
||||
add_inputs.interpolate_radius = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS;
|
||||
add_inputs.interpolate_shape = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE;
|
||||
add_inputs.interpolate_point_count = brush_settings_->flag &
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT;
|
||||
add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
|
||||
add_inputs.fallback_curve_length = brush_settings_->curve_length;
|
||||
add_inputs.fallback_curve_radius = brush_settings_->curve_radius;
|
||||
add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
|
||||
add_inputs.transforms = &transforms_;
|
||||
add_inputs.surface = surface_orig_;
|
||||
|
|
|
@ -22,10 +22,12 @@ struct AddCurvesOnMeshInputs {
|
|||
|
||||
/** Determines shape of new curves. */
|
||||
bool interpolate_length = false;
|
||||
bool interpolate_radius = false;
|
||||
bool interpolate_shape = false;
|
||||
bool interpolate_point_count = false;
|
||||
bool interpolate_resolution = false;
|
||||
float fallback_curve_length = 0.0f;
|
||||
float fallback_curve_radius = 0.0f;
|
||||
int fallback_point_count = 0;
|
||||
|
||||
/** Information about the surface that the new curves are attached to. */
|
||||
|
|
|
@ -236,13 +236,92 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves,
|
|||
});
|
||||
}
|
||||
|
||||
static void calc_radius_without_interpolation(CurvesGeometry &curves,
|
||||
Zyq-XDz marked this conversation as resolved
Outdated
|
||||
const IndexRange new_points_range,
|
||||
const float radius)
|
||||
{
|
||||
bke::SpanAttributeWriter radius_attr =
|
||||
Zyq-XDz marked this conversation as resolved
Hans Goudey
commented
`points_by_curve` is unused
|
||||
curves.attributes_for_write().lookup_or_add_for_write_span<float>("radius",
|
||||
bke::AttrDomain::Point);
|
||||
radius_attr.span.slice(new_points_range).fill(radius);
|
||||
radius_attr.finish();
|
||||
}
|
||||
Zyq-XDz marked this conversation as resolved
Hans Goudey
commented
This can be replaced with This can be replaced with `radius_attr.span.slice(new_points_range).fill(radius);`
|
||||
|
||||
static void calc_radius_with_interpolation(CurvesGeometry &curves,
|
||||
const int old_curves_num,
|
||||
const float radius,
|
||||
const Span<float> new_lengths_cu,
|
||||
const Span<NeighborCurves> neighbors_per_curve)
|
||||
{
|
||||
const int added_curves_num = new_lengths_cu.size();
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
bke::SpanAttributeWriter radius_attr =
|
||||
curves.attributes_for_write().lookup_for_write_span<float>("radius");
|
||||
|
||||
if (!radius_attr) {
|
||||
return;
|
||||
}
|
||||
Zyq-XDz marked this conversation as resolved
Hans Goudey
commented
No need to call No need to call `finish()` in this case :)
|
||||
|
||||
MutableSpan<float3> positions_cu = curves.positions_for_write();
|
||||
MutableSpan<float> radii_cu = radius_attr.span;
|
||||
|
||||
threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const NeighborCurves &neighbors = neighbors_per_curve[i];
|
||||
Zyq-XDz marked this conversation as resolved
Outdated
Hans Goudey
commented
Spelling: Spelling: `radiuses` -> `radii`
|
||||
const float length_cu = new_lengths_cu[i];
|
||||
const int curve_i = old_curves_num + i;
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
|
||||
if (neighbors.is_empty()) {
|
||||
/* If there are no neighbors, just using uniform radius. */
|
||||
radii_cu.slice(points).fill(radius);
|
||||
continue;
|
||||
}
|
||||
|
||||
radii_cu.slice(points).fill(0.0f);
|
||||
|
||||
Zyq-XDz marked this conversation as resolved
Hans Goudey
commented
If there are no neighbors, where is If there are no neighbors, where is `root_radiuses_cu` coming from?
|
||||
for (const NeighborCurve &neighbor : neighbors) {
|
||||
const int neighbor_curve_i = neighbor.index;
|
||||
const IndexRange neighbor_points = points_by_curve[neighbor_curve_i];
|
||||
const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points);
|
||||
const Span<float> neighbor_radii_cu = radius_attr.span.slice(neighbor_points);
|
||||
|
||||
Array<float, 32> lengths(length_parameterize::segments_num(neighbor_points.size(), false));
|
||||
length_parameterize::accumulate_lengths<float3>(neighbor_positions_cu, false, lengths);
|
||||
Zyq-XDz marked this conversation as resolved
Outdated
Hans Goudey
commented
`0` -> `0.0f`
|
||||
|
||||
const float neighbor_length_cu = lengths.last();
|
||||
|
||||
Array<float, 32> sample_lengths(points.size());
|
||||
const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu);
|
||||
Zyq-XDz marked this conversation as resolved
Hans Goudey
commented
`neighbor_root_cu` is unused
|
||||
const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor;
|
||||
for (const int i : sample_lengths.index_range()) {
|
||||
sample_lengths[i] = i * resample_factor * neighbor_length_cu;
|
||||
}
|
||||
|
||||
Array<int, 32> indices(points.size());
|
||||
Array<float, 32> factors(points.size());
|
||||
length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors);
|
||||
|
||||
for (const int i : IndexRange(points.size())) {
|
||||
const float sample_cu = math::interpolate(
|
||||
neighbor_radii_cu[indices[i]], neighbor_radii_cu[indices[i] + 1], factors[i]);
|
||||
|
||||
radii_cu[points[i]] += neighbor.weight * sample_cu;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
radius_attr.finish();
|
||||
}
|
||||
|
||||
AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
|
||||
const AddCurvesOnMeshInputs &inputs)
|
||||
{
|
||||
AddCurvesOnMeshOutputs outputs;
|
||||
|
||||
const bool use_interpolation = inputs.interpolate_length || inputs.interpolate_point_count ||
|
||||
inputs.interpolate_shape || inputs.interpolate_resolution;
|
||||
inputs.interpolate_radius || inputs.interpolate_shape ||
|
||||
inputs.interpolate_resolution;
|
||||
|
||||
Vector<float3> root_positions_cu;
|
||||
Vector<float3> bary_coords;
|
||||
|
@ -314,7 +393,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
|
|||
|
||||
const int new_points_num = curves.offsets().last();
|
||||
curves.resize(new_points_num, new_curves_num);
|
||||
MutableSpan<float3> positions_cu = curves.positions_for_write();
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
|
||||
/* The new elements are added at the end of the arrays. */
|
||||
outputs.new_points_range = curves.points_range().drop_front(old_points_num);
|
||||
|
@ -325,9 +404,9 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
|
|||
surface_uv_coords.take_back(added_curves_num).copy_from(used_uvs);
|
||||
|
||||
/* Determine length of new curves. */
|
||||
Span<float3> positions_cu = curves.positions();
|
||||
Array<float> new_lengths_cu(added_curves_num);
|
||||
if (inputs.interpolate_length) {
|
||||
const OffsetIndices points_by_curve = curves.points_by_curve();
|
||||
interpolate_from_neighbors<float>(
|
||||
neighbors_per_curve,
|
||||
inputs.fallback_curve_length,
|
||||
|
@ -378,6 +457,16 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
|
|||
inputs.transforms->surface_to_curves_normal);
|
||||
}
|
||||
|
||||
/* Initialize radius attribute */
|
||||
if (inputs.interpolate_radius) {
|
||||
calc_radius_with_interpolation(
|
||||
curves, old_curves_num, inputs.fallback_curve_radius, new_lengths_cu, neighbors_per_curve);
|
||||
}
|
||||
else {
|
||||
calc_radius_without_interpolation(
|
||||
curves, outputs.new_points_range, inputs.fallback_curve_radius);
|
||||
}
|
||||
|
||||
curves.fill_curve_types(new_curves_range, CURVE_TYPE_CATMULL_ROM);
|
||||
|
||||
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
|
||||
|
@ -400,7 +489,7 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
|
|||
|
||||
/* Explicitly set all other attributes besides those processed above to default values. */
|
||||
bke::fill_attribute_range_default(
|
||||
attributes, bke::AttrDomain::Point, {"position"}, outputs.new_points_range);
|
||||
attributes, bke::AttrDomain::Point, {"position", "radius"}, outputs.new_points_range);
|
||||
bke::fill_attribute_range_default(attributes,
|
||||
bke::AttrDomain::Curve,
|
||||
{"curve_type", "surface_uv_coordinate", "resolution"},
|
||||
|
|
|
@ -656,6 +656,7 @@ typedef enum eBrushCurvesSculptFlag {
|
|||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH = (1 << 2),
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE = (1 << 3),
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT = (1 << 4),
|
||||
Zyq-XDz marked this conversation as resolved
Outdated
Hans Goudey
commented
Best not to change these values, since they're stored in files. You can just add the new one at the end Best not to change these values, since they're stored in files. You can just add the new one at the end
|
||||
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS = (1 << 5),
|
||||
} eBrushCurvesSculptFlag;
|
||||
|
||||
typedef enum eBrushCurvesSculptDensityMode {
|
||||
|
|
|
@ -155,11 +155,13 @@ typedef struct BrushCurvesSculptSettings {
|
|||
float curve_length;
|
||||
/** Minimum distance between curve root points used by the Density brush. */
|
||||
float minimum_distance;
|
||||
/** The initial radius of curve. **/
|
||||
float curve_radius;
|
||||
/** How often the Density brush tries to add a new curve. */
|
||||
int density_add_attempts;
|
||||
/** #eBrushCurvesSculptDensityMode. */
|
||||
uint8_t density_mode;
|
||||
char _pad[3];
|
||||
char _pad[7];
|
||||
struct CurveMapping *curve_parameter_falloff;
|
||||
} BrushCurvesSculptSettings;
|
||||
|
||||
|
|
|
@ -2173,6 +2173,13 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop, "Interpolate Length", "Use length of the curves in close proximity");
|
||||
|
||||
prop = RNA_def_property(srna, "interpolate_radius", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS);
|
||||
RNA_def_property_boolean_default(prop, true);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Interpolate Radius", "Use radius of the curves in close proximity");
|
||||
|
||||
prop = RNA_def_property(srna, "interpolate_point_count", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "flag", BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT);
|
||||
|
@ -2198,6 +2205,15 @@ static void rna_def_curves_sculpt_options(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop, "Minimum Distance", "Goal distance between curve roots for the Density brush");
|
||||
|
||||
prop = RNA_def_property(srna, "curve_radius", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_range(prop, 0.0, FLT_MAX);
|
||||
RNA_def_property_float_default(prop, 0.01f);
|
||||
RNA_def_property_ui_range(prop, 0.0, 1000.0f, 0.001, 2);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Curve Radius",
|
||||
"Radius of newly added curves when it is not interpolated from other curves");
|
||||
|
||||
prop = RNA_def_property(srna, "density_add_attempts", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0, INT32_MAX);
|
||||
RNA_def_property_ui_text(
|
||||
|
|
Loading…
Reference in New Issue
"do thing without doing thing" is a confusing name! How about "calc_radius_without_interpolation"?