Blender V5.0
curves_sculpt_add.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <algorithm>
6
8
9#include "BLI_bounds.hh"
10#include "BLI_kdtree.h"
11#include "BLI_math_geom.h"
12#include "BLI_math_matrix.hh"
13#include "BLI_rand.hh"
14#include "BLI_vector.hh"
15
16#include "DEG_depsgraph.hh"
17
18#include "BKE_brush.hh"
19#include "BKE_bvhutils.hh"
20#include "BKE_context.hh"
21#include "BKE_curves.hh"
22#include "BKE_geometry_set.hh"
23#include "BKE_mesh.hh"
24#include "BKE_mesh_runtime.hh"
25#include "BKE_mesh_sample.hh"
26#include "BKE_modifier.hh"
27#include "BKE_object.hh"
28#include "BKE_paint.hh"
29
30#include "DNA_brush_enums.h"
31#include "DNA_brush_types.h"
32#include "DNA_curves_types.h"
33#include "DNA_object_types.h"
34#include "DNA_screen_types.h"
35
36#include "ED_screen.hh"
37#include "ED_view3d.hh"
38
40
41#include "WM_api.hh"
42
44
52
54
55using bke::CurvesGeometry;
56
58 private:
60 KDTree_3d *curve_roots_kdtree_ = nullptr;
61
62 friend struct AddOperationExecutor;
63
64 public:
65 ~AddOperation() override
66 {
67 if (curve_roots_kdtree_ != nullptr) {
68 BLI_kdtree_3d_free(curve_roots_kdtree_);
69 }
70 }
71
72 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
73};
74
80 AddOperation *self_ = nullptr;
82
86
88 Mesh *surface_eval_ = nullptr;
94
96 const Brush *brush_ = nullptr;
100
103
105
107
108 void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
109 {
110 self_ = &self;
112
113 curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
114 curves_orig_ = &curves_id_orig_->geometry.wrap();
115
116 if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
117 report_missing_surface(stroke_extension.reports);
118 return;
119 }
120
122
123 Object &surface_ob_orig = *curves_id_orig_->surface;
124 const Mesh &surface_orig = *static_cast<const Mesh *>(surface_ob_orig.data);
125 if (surface_orig.faces_num == 0) {
126 report_empty_original_surface(stroke_extension.reports);
127 return;
128 }
129
130 surface_ob_eval_ = DEG_get_evaluated(ctx_.depsgraph, &surface_ob_orig);
131 if (surface_ob_eval_ == nullptr) {
132 return;
133 }
135 if (surface_eval_->faces_num == 0) {
136 report_empty_evaluated_surface(stroke_extension.reports);
137 return;
138 }
139 surface_positions_eval_ = surface_eval_->vert_positions();
142 surface_bvh_eval_ = surface_eval_->bvh_corner_tris();
143
144 curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
146 brush_settings_ = brush_->curves_sculpt_settings;
147 brush_radius_re_ = brush_radius_get(curves_sculpt_->paint, *brush_, stroke_extension);
148 brush_pos_re_ = stroke_extension.mouse_position;
149
151 const eBrushFalloffShape falloff_shape = eBrushFalloffShape(brush_->falloff_shape);
152 add_amount_ = std::max(0, brush_settings_->add_amount);
153
154 if (add_amount_ == 0) {
155 return;
156 }
157
158 /* Find UV map. */
159 VArraySpan<float2> surface_uv_map;
160 if (curves_id_orig_->surface_uv_map != nullptr) {
161 surface_uv_map = *surface_orig.attributes().lookup<float2>(curves_id_orig_->surface_uv_map,
163 surface_uv_map_eval_ = *surface_eval_->attributes().lookup<float2>(
165 }
166
167 if (surface_uv_map.is_empty()) {
169 return;
170 }
171 if (surface_uv_map_eval_.is_empty()) {
173 return;
174 }
175
177
178 /* Sample points on the surface using one of multiple strategies. */
179 Vector<float2> sampled_uvs;
180 if (add_amount_ == 1) {
181 this->sample_in_center_with_symmetry(sampled_uvs);
182 }
183 else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
184 this->sample_projected_with_symmetry(rng, sampled_uvs);
185 }
186 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
187 this->sample_spherical_with_symmetry(rng, sampled_uvs);
188 }
189 else {
191 }
192
193 if (sampled_uvs.is_empty()) {
194 /* No new points have been added. */
195 return;
196 }
197
198 const Span<int3> surface_corner_tris_orig = surface_orig.corner_tris();
199 const Span<float3> corner_normals_su = surface_orig.corner_normals();
200 const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris_orig};
201
203 add_inputs.uvs = sampled_uvs;
204 add_inputs.interpolate_length = brush_settings_->flag &
206 add_inputs.interpolate_radius = brush_settings_->flag &
208 add_inputs.interpolate_shape = brush_settings_->flag &
210 add_inputs.interpolate_point_count = brush_settings_->flag &
212 add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
213 add_inputs.fallback_curve_length = brush_settings_->curve_length;
214 add_inputs.fallback_curve_radius = brush_settings_->curve_radius;
215 add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
216 add_inputs.transforms = &transforms_;
217 add_inputs.surface_corner_tris = surface_corner_tris_orig;
218 add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
219 add_inputs.surface = &surface_orig;
220 add_inputs.corner_normals_su = corner_normals_su;
221
222 if (add_inputs.interpolate_length || add_inputs.interpolate_radius ||
223 add_inputs.interpolate_shape || add_inputs.interpolate_point_count ||
224 add_inputs.interpolate_resolution)
225 {
227 add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
228 }
229
231 *curves_orig_, add_inputs);
232 bke::MutableAttributeAccessor attributes = curves_orig_->attributes_for_write();
233 if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
234 curves::fill_selection_true(selection.span.slice(selection.domain == bke::AttrDomain::Point ?
235 add_outputs.new_points_range :
236 add_outputs.new_curves_range));
237 selection.finish();
238 }
239 if (U.uiflag & USER_ORBIT_SELECTION) {
240 if (const std::optional<Bounds<float3>> center_cu = bounds::min_max(
241 curves_orig_->positions().slice(add_outputs.new_points_range)))
242 {
245 math::transform_point(transforms_.curves_to_world, center_cu->center()));
246 }
247 }
248
249 if (add_outputs.uv_error) {
250 report_invalid_uv_map(stroke_extension.reports);
251 }
252
256 }
257
262 {
263 float3 ray_start_wo, ray_end_wo;
265 ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, ray_start_wo, ray_end_wo, true);
266 const float3 ray_start_cu = math::transform_point(transforms_.world_to_curves, ray_start_wo);
267 const float3 ray_end_cu = math::transform_point(transforms_.world_to_curves, ray_end_wo);
268
269 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
271
272 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
273 const float4x4 transform = transforms_.curves_to_surface * brush_transform;
274 this->sample_in_center(r_sampled_uvs,
275 math::transform_point(transform, ray_start_cu),
276 math::transform_point(transform, ray_end_cu));
277 }
278 }
279
280 void sample_in_center(Vector<float2> &r_sampled_uvs,
281 const float3 &ray_start_su,
282 const float3 &ray_end_su)
283 {
284 const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
285
286 BVHTreeRayHit ray_hit;
287 ray_hit.dist = FLT_MAX;
288 ray_hit.index = -1;
290 ray_start_su,
291 ray_direction_su,
292 0.0f,
293 &ray_hit,
294 surface_bvh_eval_.raycast_callback,
296
297 if (ray_hit.index == -1) {
298 return;
299 }
300
301 const int tri_index = ray_hit.index;
302 const int3 &tri = surface_corner_tris_eval_[tri_index];
303 const float3 brush_pos_su = ray_hit.co;
306
308 bary_coords, tri, surface_uv_map_eval_);
309 r_sampled_uvs.append(uv);
310 }
311
316 {
317 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
319 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
320 this->sample_projected(rng, r_sampled_uvs, brush_transform);
321 }
322 }
323
325 Vector<float2> &r_sampled_uvs,
326 const float4x4 &brush_transform)
327 {
328 const int old_amount = r_sampled_uvs.size();
329 const int max_iterations = 100;
330 int current_iteration = 0;
331 while (r_sampled_uvs.size() < old_amount + add_amount_) {
332 if (current_iteration++ >= max_iterations) {
333 break;
334 }
335 Vector<float3> bary_coords;
336 Vector<int> tri_indices;
337 Vector<float3> positions_su;
338
339 const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size();
341 rng,
346 [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
347 float3 start_wo, end_wo;
349 ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
350 const float3 start_cu = math::transform_point(transforms_.world_to_curves, start_wo);
351 const float3 start_cu_tx = math::transform_point(brush_transform, start_cu);
352 const float3 end_cu = math::transform_point(transforms_.world_to_curves, end_wo);
353 const float3 end_cu_tx = math::transform_point(brush_transform, end_cu);
354 r_start_su = math::transform_point(transforms_.curves_to_surface, start_cu_tx);
355 r_end_su = math::transform_point(transforms_.curves_to_surface, end_cu_tx);
356 },
359 missing_amount,
360 bary_coords,
361 tri_indices,
362 positions_su);
363
364 for (const int i : IndexRange(new_points)) {
366 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
367 r_sampled_uvs.append(uv);
368 }
369 }
370 }
371
376 {
377 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
378 *ctx_.region,
379 *ctx_.v3d,
384 if (!brush_3d.has_value()) {
385 return;
386 }
387
388 float3 view_ray_start_wo, view_ray_end_wo;
390 ctx_.region,
391 ctx_.v3d,
393 view_ray_start_wo,
394 view_ray_end_wo,
395 true);
396
397 const float3 view_ray_start_cu = math::transform_point(transforms_.world_to_curves,
398 view_ray_start_wo);
399 const float3 view_ray_end_cu = math::transform_point(transforms_.world_to_curves,
400 view_ray_end_wo);
401
402 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
404 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
405 const float4x4 transform = transforms_.curves_to_surface * brush_transform;
406
407 const float3 brush_pos_su = math::transform_point(transform, brush_3d->position_cu);
408 const float3 view_direction_su = math::normalize(
409 math::transform_point(transform, view_ray_end_cu) -
410 math::transform_point(transform, view_ray_start_cu));
411 const float brush_radius_su = transform_brush_radius(
412 transform, brush_3d->position_cu, brush_3d->radius_cu);
413
414 this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su);
415 }
416 }
417
419 Vector<float2> &r_sampled_uvs,
420 const float3 &brush_pos_su,
421 const float brush_radius_su,
422 const float3 &view_direction_su)
423 {
424 const float brush_radius_sq_su = pow2f(brush_radius_su);
425
426 /* Find surface triangles within brush radius. */
427 Vector<int> selected_tri_indices;
428 if (use_front_face_) {
430 *surface_bvh_eval_.tree,
431 brush_pos_su,
432 brush_radius_su,
433 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
434 const int3 &tri = surface_corner_tris_eval_[index];
435 const float3 &v0_su = surface_positions_eval_[surface_corner_verts_eval_[tri[0]]];
436 const float3 &v1_su = surface_positions_eval_[surface_corner_verts_eval_[tri[1]]];
437 const float3 &v2_su = surface_positions_eval_[surface_corner_verts_eval_[tri[2]]];
438 float3 normal_su;
439 normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
440 if (math::dot(normal_su, view_direction_su) >= 0.0f) {
441 return;
442 }
443 selected_tri_indices.append(index);
444 });
445 }
446 else {
449 brush_pos_su,
450 brush_radius_su,
451 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
452 selected_tri_indices.append(index);
453 });
454 }
455
456 /* Density used for sampling points. This does not have to be exact, because the loop below
457 * automatically runs until enough samples have been found. If too many samples are found, some
458 * will be discarded afterwards. */
459 const float brush_plane_area_su = M_PI * brush_radius_sq_su;
460 const float approximate_density_su = add_amount_ / brush_plane_area_su;
461
462 /* Usually one or two iterations should be enough. */
463 const int max_iterations = 5;
464 int current_iteration = 0;
465
466 const int old_amount = r_sampled_uvs.size();
467 while (r_sampled_uvs.size() < old_amount + add_amount_) {
468 if (current_iteration++ >= max_iterations) {
469 break;
470 }
471 Vector<float3> bary_coords;
472 Vector<int> tri_indices;
473 Vector<float3> positions_su;
475 rng,
477 selected_tri_indices,
478 brush_pos_su,
479 brush_radius_su,
480 approximate_density_su,
481 bary_coords,
482 tri_indices,
483 positions_su);
484 for (const int i : IndexRange(new_points)) {
486 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
487 r_sampled_uvs.append(uv);
488 }
489 }
490
491 /* Remove samples when there are too many. */
492 while (r_sampled_uvs.size() > old_amount + add_amount_) {
493 const int index_to_remove = rng.get_int32(add_amount_) + old_amount;
494 r_sampled_uvs.remove_and_reorder(index_to_remove);
495 }
496 }
497
499 {
500 if (self_->curve_roots_kdtree_ == nullptr) {
501 self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num());
502 const Span<int> offsets = curves_orig_->offsets();
503 const Span<float3> positions = curves_orig_->positions();
504 for (const int curve_i : curves_orig_->curves_range()) {
505 BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, positions[offsets[curve_i]]);
506 }
507 BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
508 }
509 }
510};
511
512void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
513{
514 AddOperationExecutor executor{C};
515 executor.execute(*this, C, stroke_extension);
516}
517
518std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
519{
520 return std::make_unique<AddOperation>();
521}
522
523} // namespace blender::ed::sculpt_paint
Object * CTX_data_active_object(const bContext *C)
Low-level operations for curves.
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
const Brush * BKE_paint_brush_for_read(const Paint *paint)
Definition paint.cc:650
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
MINLINE float pow2f(float x)
#define M_PI
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ BRUSH_FRONTFACE
eBrushFalloffShape
@ PAINT_FALLOFF_SHAPE_SPHERE
@ PAINT_FALLOFF_SHAPE_TUBE
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH
@ BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_SHAPE
eCurvesSymmetryType
Object is a sort of wrapper for general info.
@ OB_MESH
@ USER_ORBIT_SELECTION
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
bool ED_view3d_win_to_segment_clipped(const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], bool do_clip_planes)
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
#define U
PyObject * self
AttributeSet attributes
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
bool is_empty() const
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
int sample_surface_points_projected(RandomNumberGenerator &rng, const Mesh &mesh, bke::BVHTreeFromMesh &mesh_bvhtree, const float2 &sample_pos_re, float sample_radius_re, FunctionRef< void(const float2 &pos_re, float3 &r_start, float3 &r_end)> region_position_to_ray, bool front_face_only, int tries_num, int max_points, Vector< float3 > &r_bary_coords, Vector< int > &r_tri_indices, Vector< float3 > &r_positions)
int sample_surface_points_spherical(RandomNumberGenerator &rng, const Mesh &mesh, Span< int > tris_to_sample, const float3 &sample_pos, float sample_radius, float approximate_density, Vector< float3 > &r_bary_coords, Vector< int > &r_tri_indices, Vector< float3 > &r_positions)
float3 compute_bary_coord_in_triangle(Span< float3 > vert_positions, Span< int > corner_verts, const int3 &corner_tri, const float3 &position)
T sample_corner_attribute_with_bary_coords(const float3 &bary_weights, const int3 &corner_tri, const Span< T > corner_attribute)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
void fill_selection_true(GMutableSpan selection)
std::unique_ptr< CurvesSculptStrokeOperation > new_add_operation()
void report_invalid_uv_map(ReportList *reports)
void report_empty_evaluated_surface(ReportList *reports)
void report_missing_uv_map_on_original_surface(ReportList *reports)
void remember_stroke_position(CurvesSculpt &curves_sculpt, const float3 &brush_position_wo)
void report_missing_uv_map_on_evaluated_surface(ReportList *reports)
void report_missing_surface(ReportList *reports)
std::optional< CurvesBrush3D > sample_curves_surface_3d_brush(const Depsgraph &depsgraph, const ARegion &region, const View3D &v3d, const CurvesSurfaceTransforms &transforms, const bke::BVHTreeFromMesh &surface_bvh, const float2 &brush_pos_re, const float brush_radius_re)
float brush_radius_get(const Paint &paint, const Brush &brush, const StrokeExtension &stroke_extension)
void report_empty_original_surface(ReportList *reports)
Vector< float4x4 > get_symmetry_brush_transforms(const eCurvesSymmetryType symmetry)
float transform_brush_radius(const float4x4 &transform, const float3 &brush_position, const float old_radius)
AddCurvesOnMeshOutputs add_curves_on_mesh(bke::CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
void BLI_bvhtree_range_query_cpp(const BVHTree &tree, const float3 co, float radius, BVHTree_RangeQuery_CPP fn)
VecBase< float, 3 > float3
#define FLT_MAX
Definition stdcycles.h:14
int faces_num
void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector< float2 > &r_sampled_uvs)
void sample_in_center_with_symmetry(Vector< float2 > &r_sampled_uvs)
void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector< float2 > &r_sampled_uvs)
void sample_in_center(Vector< float2 > &r_sampled_uvs, const float3 &ray_start_su, const float3 &ray_end_su)
const BrushCurvesSculptSettings * brush_settings_
void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
void sample_spherical(RandomNumberGenerator &rng, Vector< float2 > &r_sampled_uvs, const float3 &brush_pos_su, const float brush_radius_su, const float3 &view_direction_su)
void sample_projected(RandomNumberGenerator &rng, Vector< float2 > &r_sampled_uvs, const float4x4 &brush_transform)
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)