Blender V4.5
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
95 const CurvesSculpt *curves_sculpt_ = nullptr;
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(*ctx_.scene, *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 {
244 *ctx_.scene, math::transform_point(transforms_.curves_to_world, center_cu->center()));
245 }
246 }
247
248 if (add_outputs.uv_error) {
249 report_invalid_uv_map(stroke_extension.reports);
250 }
251
255 }
256
261 {
262 float3 ray_start_wo, ray_end_wo;
264 ctx_.depsgraph, ctx_.region, ctx_.v3d, brush_pos_re_, ray_start_wo, ray_end_wo, true);
265 const float3 ray_start_cu = math::transform_point(transforms_.world_to_curves, ray_start_wo);
266 const float3 ray_end_cu = math::transform_point(transforms_.world_to_curves, ray_end_wo);
267
268 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
270
271 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
272 const float4x4 transform = transforms_.curves_to_surface * brush_transform;
273 this->sample_in_center(r_sampled_uvs,
274 math::transform_point(transform, ray_start_cu),
275 math::transform_point(transform, ray_end_cu));
276 }
277 }
278
279 void sample_in_center(Vector<float2> &r_sampled_uvs,
280 const float3 &ray_start_su,
281 const float3 &ray_end_su)
282 {
283 const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
284
285 BVHTreeRayHit ray_hit;
286 ray_hit.dist = FLT_MAX;
287 ray_hit.index = -1;
289 ray_start_su,
290 ray_direction_su,
291 0.0f,
292 &ray_hit,
293 surface_bvh_eval_.raycast_callback,
295
296 if (ray_hit.index == -1) {
297 return;
298 }
299
300 const int tri_index = ray_hit.index;
301 const int3 &tri = surface_corner_tris_eval_[tri_index];
302 const float3 brush_pos_su = ray_hit.co;
305
307 bary_coords, tri, surface_uv_map_eval_);
308 r_sampled_uvs.append(uv);
309 }
310
315 {
316 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
318 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
319 this->sample_projected(rng, r_sampled_uvs, brush_transform);
320 }
321 }
322
324 Vector<float2> &r_sampled_uvs,
325 const float4x4 &brush_transform)
326 {
327 const int old_amount = r_sampled_uvs.size();
328 const int max_iterations = 100;
329 int current_iteration = 0;
330 while (r_sampled_uvs.size() < old_amount + add_amount_) {
331 if (current_iteration++ >= max_iterations) {
332 break;
333 }
334 Vector<float3> bary_coords;
335 Vector<int> tri_indices;
336 Vector<float3> positions_su;
337
338 const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size();
340 rng,
345 [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
346 float3 start_wo, end_wo;
348 ctx_.depsgraph, ctx_.region, ctx_.v3d, pos_re, start_wo, end_wo, true);
349 const float3 start_cu = math::transform_point(transforms_.world_to_curves, start_wo);
350 const float3 start_cu_tx = math::transform_point(brush_transform, start_cu);
351 const float3 end_cu = math::transform_point(transforms_.world_to_curves, end_wo);
352 const float3 end_cu_tx = math::transform_point(brush_transform, end_cu);
353 r_start_su = math::transform_point(transforms_.curves_to_surface, start_cu_tx);
354 r_end_su = math::transform_point(transforms_.curves_to_surface, end_cu_tx);
355 },
358 missing_amount,
359 bary_coords,
360 tri_indices,
361 positions_su);
362
363 for (const int i : IndexRange(new_points)) {
365 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
366 r_sampled_uvs.append(uv);
367 }
368 }
369 }
370
375 {
376 const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
377 *ctx_.region,
378 *ctx_.v3d,
383 if (!brush_3d.has_value()) {
384 return;
385 }
386
387 float3 view_ray_start_wo, view_ray_end_wo;
389 ctx_.region,
390 ctx_.v3d,
392 view_ray_start_wo,
393 view_ray_end_wo,
394 true);
395
396 const float3 view_ray_start_cu = math::transform_point(transforms_.world_to_curves,
397 view_ray_start_wo);
398 const float3 view_ray_end_cu = math::transform_point(transforms_.world_to_curves,
399 view_ray_end_wo);
400
401 const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
403 for (const float4x4 &brush_transform : symmetry_brush_transforms) {
404 const float4x4 transform = transforms_.curves_to_surface * brush_transform;
405
406 const float3 brush_pos_su = math::transform_point(transform, brush_3d->position_cu);
407 const float3 view_direction_su = math::normalize(
408 math::transform_point(transform, view_ray_end_cu) -
409 math::transform_point(transform, view_ray_start_cu));
410 const float brush_radius_su = transform_brush_radius(
411 transform, brush_3d->position_cu, brush_3d->radius_cu);
412
413 this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su);
414 }
415 }
416
418 Vector<float2> &r_sampled_uvs,
419 const float3 &brush_pos_su,
420 const float brush_radius_su,
421 const float3 &view_direction_su)
422 {
423 const float brush_radius_sq_su = pow2f(brush_radius_su);
424
425 /* Find surface triangles within brush radius. */
426 Vector<int> selected_tri_indices;
427 if (use_front_face_) {
429 *surface_bvh_eval_.tree,
430 brush_pos_su,
431 brush_radius_su,
432 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
433 const int3 &tri = surface_corner_tris_eval_[index];
434 const float3 &v0_su = surface_positions_eval_[surface_corner_verts_eval_[tri[0]]];
435 const float3 &v1_su = surface_positions_eval_[surface_corner_verts_eval_[tri[1]]];
436 const float3 &v2_su = surface_positions_eval_[surface_corner_verts_eval_[tri[2]]];
437 float3 normal_su;
438 normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
439 if (math::dot(normal_su, view_direction_su) >= 0.0f) {
440 return;
441 }
442 selected_tri_indices.append(index);
443 });
444 }
445 else {
448 brush_pos_su,
449 brush_radius_su,
450 [&](const int index, const float3 & /*co*/, const float /*dist_sq*/) {
451 selected_tri_indices.append(index);
452 });
453 }
454
455 /* Density used for sampling points. This does not have to be exact, because the loop below
456 * automatically runs until enough samples have been found. If too many samples are found, some
457 * will be discarded afterwards. */
458 const float brush_plane_area_su = M_PI * brush_radius_sq_su;
459 const float approximate_density_su = add_amount_ / brush_plane_area_su;
460
461 /* Usually one or two iterations should be enough. */
462 const int max_iterations = 5;
463 int current_iteration = 0;
464
465 const int old_amount = r_sampled_uvs.size();
466 while (r_sampled_uvs.size() < old_amount + add_amount_) {
467 if (current_iteration++ >= max_iterations) {
468 break;
469 }
470 Vector<float3> bary_coords;
471 Vector<int> tri_indices;
472 Vector<float3> positions_su;
474 rng,
476 selected_tri_indices,
477 brush_pos_su,
478 brush_radius_su,
479 approximate_density_su,
480 bary_coords,
481 tri_indices,
482 positions_su);
483 for (const int i : IndexRange(new_points)) {
485 bary_coords[i], surface_corner_tris_eval_[tri_indices[i]], surface_uv_map_eval_);
486 r_sampled_uvs.append(uv);
487 }
488 }
489
490 /* Remove samples when there are too many. */
491 while (r_sampled_uvs.size() > old_amount + add_amount_) {
492 const int index_to_remove = rng.get_int32(add_amount_) + old_amount;
493 r_sampled_uvs.remove_and_reorder(index_to_remove);
494 }
495 }
496
498 {
499 if (self_->curve_roots_kdtree_ == nullptr) {
500 self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num());
501 const Span<int> offsets = curves_orig_->offsets();
502 const Span<float3> positions = curves_orig_->positions();
503 for (const int curve_i : curves_orig_->curves_range()) {
504 BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, positions[offsets[curve_i]]);
505 }
506 BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
507 }
508 }
509};
510
511void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension)
512{
513 AddOperationExecutor executor{C};
514 executor.execute(*this, C, stroke_extension);
515}
516
517std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
518{
519 return std::make_unique<AddOperation>();
520}
521
522} // 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:641
#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:982
@ 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:639
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:390
#define ND_DATA
Definition WM_types.hh:506
#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)
float brush_radius_get(const Scene &scene, const Brush &brush, const StrokeExtension &stroke_extension)
void remember_stroke_position(Scene &scene, const float3 &brush_position_wo)
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)
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)
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)