Blender V4.3
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_kdtree.h"
10#include "BLI_math_geom.h"
11#include "BLI_math_matrix.hh"
12#include "BLI_rand.hh"
13#include "BLI_vector.hh"
14
15#include "DEG_depsgraph.hh"
16
17#include "BKE_attribute_math.hh"
18#include "BKE_brush.hh"
19#include "BKE_bvhutils.hh"
20#include "BKE_context.hh"
21#include "BKE_curves.hh"
22#include "BKE_curves_utils.hh"
23#include "BKE_geometry_set.hh"
24#include "BKE_mesh.hh"
25#include "BKE_mesh_runtime.hh"
26#include "BKE_mesh_sample.hh"
27#include "BKE_modifier.hh"
28#include "BKE_object.hh"
29#include "BKE_paint.hh"
30#include "BKE_report.hh"
31
32#include "DNA_brush_enums.h"
33#include "DNA_brush_types.h"
34#include "DNA_curves_types.h"
35#include "DNA_object_types.h"
36#include "DNA_screen_types.h"
37#include "DNA_space_types.h"
38
39#include "ED_screen.hh"
40#include "ED_view3d.hh"
41
43
44#include "WM_api.hh"
45
47
57
58using bke::CurvesGeometry;
59
61 private:
63 KDTree_3d *curve_roots_kdtree_ = nullptr;
64
65 friend struct AddOperationExecutor;
66
67 public:
68 ~AddOperation() override
69 {
70 if (curve_roots_kdtree_ != nullptr) {
71 BLI_kdtree_3d_free(curve_roots_kdtree_);
72 }
73 }
74
75 void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
76};
77
83 AddOperation *self_ = nullptr;
85
89
91 Mesh *surface_eval_ = nullptr;
97
98 const CurvesSculpt *curves_sculpt_ = nullptr;
99 const Brush *brush_ = nullptr;
103
106
108
110
111 void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
112 {
113 self_ = &self;
115
116 curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
118
119 if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
120 report_missing_surface(stroke_extension.reports);
121 return;
122 }
123
125
126 Object &surface_ob_orig = *curves_id_orig_->surface;
127 const Mesh &surface_orig = *static_cast<const Mesh *>(surface_ob_orig.data);
128 if (surface_orig.faces_num == 0) {
129 report_empty_original_surface(stroke_extension.reports);
130 return;
131 }
132
134 if (surface_ob_eval_ == nullptr) {
135 return;
136 }
138 if (surface_eval_->faces_num == 0) {
139 report_empty_evaluated_surface(stroke_extension.reports);
140 return;
141 }
142 surface_positions_eval_ = surface_eval_->vert_positions();
147
151 brush_radius_re_ = brush_radius_get(*ctx_.scene, *brush_, stroke_extension);
152 brush_pos_re_ = stroke_extension.mouse_position;
153
156 add_amount_ = std::max(0, brush_settings_->add_amount);
157
158 if (add_amount_ == 0) {
159 return;
160 }
161
162 /* Find UV map. */
163 VArraySpan<float2> surface_uv_map;
164 if (curves_id_orig_->surface_uv_map != nullptr) {
165 surface_uv_map = *surface_orig.attributes().lookup<float2>(curves_id_orig_->surface_uv_map,
169 }
170
171 if (surface_uv_map.is_empty()) {
173 return;
174 }
175 if (surface_uv_map_eval_.is_empty()) {
177 return;
178 }
179
181
182 /* Sample points on the surface using one of multiple strategies. */
183 Vector<float2> sampled_uvs;
184 if (add_amount_ == 1) {
185 this->sample_in_center_with_symmetry(sampled_uvs);
186 }
187 else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
188 this->sample_projected_with_symmetry(rng, sampled_uvs);
189 }
190 else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
191 this->sample_spherical_with_symmetry(rng, sampled_uvs);
192 }
193 else {
195 }
196
197 if (sampled_uvs.is_empty()) {
198 /* No new points have been added. */
199 return;
200 }
201
202 const Span<int3> surface_corner_tris_orig = surface_orig.corner_tris();
203 const Span<float3> corner_normals_su = surface_orig.corner_normals();
204 const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris_orig};
205
207 add_inputs.uvs = sampled_uvs;
216 add_inputs.interpolate_resolution = curves_orig_->attributes().contains("resolution");
219 add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
220 add_inputs.transforms = &transforms_;
221 add_inputs.surface_corner_tris = surface_corner_tris_orig;
222 add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
223 add_inputs.surface = &surface_orig;
224 add_inputs.corner_normals_su = corner_normals_su;
225
226 if (add_inputs.interpolate_length || add_inputs.interpolate_radius ||
227 add_inputs.interpolate_shape || add_inputs.interpolate_point_count ||
228 add_inputs.interpolate_resolution)
229 {
231 add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
232 }
233
235 *curves_orig_, add_inputs);
237 if (bke::GSpanAttributeWriter selection = attributes.lookup_for_write_span(".selection")) {
238 curves::fill_selection_true(selection.span.slice(selection.domain == bke::AttrDomain::Point ?
239 add_outputs.new_points_range :
240 add_outputs.new_curves_range));
241 selection.finish();
242 }
243
244 if (add_outputs.uv_error) {
245 report_invalid_uv_map(stroke_extension.reports);
246 }
247
251 }
252
257 {
258 float3 ray_start_wo, ray_end_wo;
260 ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, ray_start_wo, ray_end_wo, true);
261 const float3 ray_start_cu = math::transform_point(transforms_.world_to_curves, ray_start_wo);
262 const float3 ray_end_cu = math::transform_point(transforms_.world_to_curves, ray_end_wo);
263
264 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
266
267 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
268 const float4x4 transform = transforms_.curves_to_surface * brush_transform;
269 this->sample_in_center(r_sampled_uvs,
270 math::transform_point(transform, ray_start_cu),
271 math::transform_point(transform, ray_end_cu));
272 }
273 }
274
275 void sample_in_center(Vector<float2> &r_sampled_uvs,
276 const float3 &ray_start_su,
277 const float3 &ray_end_su)
278 {
279 const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
280
281 BVHTreeRayHit ray_hit;
282 ray_hit.dist = FLT_MAX;
283 ray_hit.index = -1;
285 ray_start_su,
286 ray_direction_su,
287 0.0f,
288 &ray_hit,
291
292 if (ray_hit.index == -1) {
293 return;
294 }
295
296 const int tri_index = ray_hit.index;
297 const int3 &tri = surface_corner_tris_eval_[tri_index];
298 const float3 brush_pos_su = ray_hit.co;
301
303 bary_coords, tri, surface_uv_map_eval_);
304 r_sampled_uvs.append(uv);
305 }
306
311 {
312 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
314 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
315 this->sample_projected(rng, r_sampled_uvs, brush_transform);
316 }
317 }
318
320 Vector<float2> &r_sampled_uvs,
321 const float4x4 &brush_transform)
322 {
323 const int old_amount = r_sampled_uvs.size();
324 const int max_iterations = 100;
325 int current_iteration = 0;
326 while (r_sampled_uvs.size() < old_amount + add_amount_) {
327 if (current_iteration++ >= max_iterations) {
328 break;
329 }
330 Vector<float3> bary_coords;
331 Vector<int> tri_indices;
332 Vector<float3> positions_su;
333
334 const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size();
336 rng,
341 [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
342 float3 start_wo, end_wo;
344 ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
345 const float3 start_cu = math::transform_point(transforms_.world_to_curves, start_wo);
346 const float3 start_cu_tx = math::transform_point(brush_transform, start_cu);
348 const float3 end_cu_tx = math::transform_point(brush_transform, end_cu);
349 r_start_su = math::transform_point(transforms_.curves_to_surface, start_cu_tx);
351 },
354 missing_amount,
355 bary_coords,
356 tri_indices,
357 positions_su);
358
359 for (const int i : IndexRange(new_points)) {
361 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
362 r_sampled_uvs.append(uv);
363 }
364 }
365 }
366
371 {
372 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
373 *ctx_.region,
374 *ctx_.v3d,
379 if (!brush_3d.has_value()) {
380 return;
381 }
382
383 float3 view_ray_start_wo, view_ray_end_wo;
385 ctx_.region,
386 ctx_.v3d,
388 view_ray_start_wo,
389 view_ray_end_wo,
390 true);
391
393 view_ray_start_wo);
395 view_ray_end_wo);
396
397 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
399 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
400 const float4x4 transform = transforms_.curves_to_surface * brush_transform;
401
402 const float3 brush_pos_su = math::transform_point(transform, brush_3d->position_cu);
403 const float3 view_direction_su = math::normalize(
404 math::transform_point(transform, view_ray_end_cu) -
405 math::transform_point(transform, view_ray_start_cu));
406 const float brush_radius_su = transform_brush_radius(
407 transform, brush_3d->position_cu, brush_3d->radius_cu);
408
409 this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su);
410 }
411 }
412
414 Vector<float2> &r_sampled_uvs,
415 const float3 &brush_pos_su,
416 const float brush_radius_su,
417 const float3 &view_direction_su)
418 {
419 const float brush_radius_sq_su = pow2f(brush_radius_su);
420
421 /* Find surface triangles within brush radius. */
422 Vector<int> selected_tri_indices;
423 if (use_front_face_) {
424 BLI_bvhtree_range_query_cpp(
426 brush_pos_su,
427 brush_radius_su,
428 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
429 const int3 &tri = surface_corner_tris_eval_[index];
430 const float3 &v0_su = surface_positions_eval_[surface_corner_verts_eval_[tri[0]]];
431 const float3 &v1_su = surface_positions_eval_[surface_corner_verts_eval_[tri[1]]];
432 const float3 &v2_su = surface_positions_eval_[surface_corner_verts_eval_[tri[2]]];
433 float3 normal_su;
434 normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
435 if (math::dot(normal_su, view_direction_su) >= 0.0f) {
436 return;
437 }
438 selected_tri_indices.append(index);
439 });
440 }
441 else {
442 BLI_bvhtree_range_query_cpp(
444 brush_pos_su,
445 brush_radius_su,
446 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
447 selected_tri_indices.append(index);
448 });
449 }
450
451 /* Density used for sampling points. This does not have to be exact, because the loop below
452 * automatically runs until enough samples have been found. If too many samples are found, some
453 * will be discarded afterwards. */
454 const float brush_plane_area_su = M_PI * brush_radius_sq_su;
455 const float approximate_density_su = add_amount_ / brush_plane_area_su;
456
457 /* Usually one or two iterations should be enough. */
458 const int max_iterations = 5;
459 int current_iteration = 0;
460
461 const int old_amount = r_sampled_uvs.size();
462 while (r_sampled_uvs.size() < old_amount + add_amount_) {
463 if (current_iteration++ >= max_iterations) {
464 break;
465 }
466 Vector<float3> bary_coords;
467 Vector<int> tri_indices;
468 Vector<float3> positions_su;
470 rng,
472 selected_tri_indices,
473 brush_pos_su,
474 brush_radius_su,
475 approximate_density_su,
476 bary_coords,
477 tri_indices,
478 positions_su);
479 for (const int i : IndexRange(new_points)) {
481 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
482 r_sampled_uvs.append(uv);
483 }
484 }
485
486 /* Remove samples when there are too many. */
487 while (r_sampled_uvs.size() > old_amount + add_amount_) {
488 const int index_to_remove = rng.get_int32(add_amount_) + old_amount;
489 r_sampled_uvs.remove_and_reorder(index_to_remove);
490 }
491 }
492
494 {
495 if (self_->curve_roots_kdtree_ == nullptr) {
496 self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num());
497 const Span<int> offsets = curves_orig_->offsets();
498 const Span<float3> positions = curves_orig_->positions();
499 for (const int curve_i : curves_orig_->curves_range()) {
500 BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, positions[offsets[curve_i]]);
501 }
502 BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
503 }
504 }
505};
506
507void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
508{
509 AddOperationExecutor executor{C};
510 executor.execute(*this, C, stroke_extension);
511}
512
513std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
514{
515 return std::make_unique<AddOperation>();
516}
517
518} // namespace blender::ed::sculpt_paint
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
@ BVHTREE_FROM_CORNER_TRIS
Object * CTX_data_active_object(const bContext *C)
Low-level operations for curves.
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:654
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
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
#define BLI_SCOPED_DEFER(function_to_defer)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ 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
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
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 NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
PyObject * self
AttributeSet attributes
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
void append(const T &value)
bool is_empty() const
bool contains(const StringRef attribute_id) const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< float3 > positions() const
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override
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)
int sample_surface_points_projected(RandomNumberGenerator &rng, const Mesh &mesh, 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)
T sample_corner_attribute_with_bary_coords(const float3 &bary_weights, const int3 &corner_tri, const Span< T > corner_attribute)
void fill_selection_true(GMutableSpan selection)
std::unique_ptr< CurvesSculptStrokeOperation > new_add_operation()
std::optional< CurvesBrush3D > sample_curves_surface_3d_brush(const Depsgraph &depsgraph, const ARegion &region, const View3D &v3d, const CurvesSurfaceTransforms &transforms, const BVHTreeFromMesh &surface_bvh, const float2 &brush_pos_re, const float brush_radius_re)
void report_invalid_uv_map(ReportList *reports)
void report_empty_evaluated_surface(ReportList *reports)
float brush_radius_get(const Scene &scene, const Brush &brush, const StrokeExtension &stroke_extension)
void report_missing_uv_map_on_original_surface(ReportList *reports)
void report_missing_uv_map_on_evaluated_surface(ReportList *reports)
void report_missing_surface(ReportList *reports)
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)
#define FLT_MAX
Definition stdcycles.h:14
BVHTree_RayCastCallback raycast_callback
float co[3]
Definition BLI_kdopbvh.h:69
char falloff_shape
struct BrushCurvesSculptSettings * curves_sculpt_settings
CurvesGeometry geometry
struct Object * surface
char * surface_uv_map
int faces_num
struct ToolSettings * toolsettings
CurvesSculpt * curves_sculpt
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)
void WM_main_add_notifier(uint type, void *reference)