diff options
51 files changed, 1644 insertions, 756 deletions
diff --git a/source/blender/blenkernel/BKE_crazyspace.hh b/source/blender/blenkernel/BKE_crazyspace.hh new file mode 100644 index 00000000000..adebf0b7884 --- /dev/null +++ b/source/blender/blenkernel/BKE_crazyspace.hh @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#pragma once + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_span.hh" + +struct Depsgraph; +struct Object; + +namespace blender::bke::crazyspace { + +/** + * Contains information about how points have been deformed during evaluation. + * This allows mapping edits on evaluated data back to original data in some cases. + */ +struct GeometryDeformation { + /** + * Positions of the deformed points. This may also point to the original position if no + * deformation data is available. + */ + Span<float3> positions; + /** + * Matrices that transform point translations on original data into corresponding translations in + * evaluated data. This may be empty if not available. + */ + Span<float3x3> deform_mats; + + float3 translation_from_deformed_to_original(const int position_i, + const float3 &translation) const + { + if (this->deform_mats.is_empty()) { + return translation; + } + const float3x3 &deform_mat = this->deform_mats[position_i]; + return deform_mat.inverted() * translation; + } +}; + +/** + * During evaluation of the object, deformation data may have been generated for this object. This + * function either retrieves the deformation data from the evaluated object, or falls back to + * returning the original data. + */ +GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, + const Object &ob_orig); + +} // namespace blender::bke::crazyspace diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 68c90a45031..568899721a9 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -11,6 +11,7 @@ #include <mutex> +#include "BLI_float3x3.hh" #include "BLI_float4x4.hh" #include "BLI_generic_virtual_array.hh" #include "BLI_index_mask.hh" @@ -414,6 +415,38 @@ class CurvesGeometry : public ::CurvesGeometry { } }; +/** + * Used to propagate deformation data through modifier evaluation so that sculpt tools can work on + * evaluated data. + */ +class CurvesEditHints { + public: + /** + * Original data that the edit hints below are meant to be used for. + */ + const Curves &curves_id_orig; + /** + * Evaluated positions for the points in #curves_orig. If this is empty, the positions from the + * evaluated #Curves should be used if possible. + */ + std::optional<Array<float3>> positions; + /** + * Matrices which transform point movement vectors from original data to corresponding movements + * of evaluated data. + */ + std::optional<Array<float3x3>> deform_mats; + + CurvesEditHints(const Curves &curves_id_orig) : curves_id_orig(curves_id_orig) + { + } + + /** + * The edit hints have to correspond to the original curves, i.e. the number of deformed points + * is the same as the number of original points. + */ + bool is_valid() const; +}; + namespace curves { /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index a28e9e6bdf6..97e69f3fe1f 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -23,9 +23,10 @@ typedef enum GeometryComponentType { GEO_COMPONENT_TYPE_INSTANCES = 2, GEO_COMPONENT_TYPE_VOLUME = 3, GEO_COMPONENT_TYPE_CURVE = 4, + GEO_COMPONENT_TYPE_EDIT = 5, } GeometryComponentType; -#define GEO_COMPONENT_TYPE_ENUM_SIZE 5 +#define GEO_COMPONENT_TYPE_ENUM_SIZE 6 void BKE_geometry_set_free(struct GeometrySet *geometry_set); diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 4108e2f7e2e..be2ec3e3dca 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -43,7 +43,8 @@ enum class GeometryOwnershipType { namespace blender::bke { class ComponentAttributeProviders; -} +class CurvesEditHints; +} // namespace blender::bke class GeometryComponent; @@ -168,6 +169,12 @@ struct GeometrySet { * Remove all geometry components with types that are not in the provided list. */ void keep_only(const blender::Span<GeometryComponentType> component_types); + /** + * Keeps the provided geometry types, but also instances and edit data. + * Instances must not be removed while using #modify_geometry_sets. + */ + void keep_only_during_modify(const blender::Span<GeometryComponentType> component_types); + void remove_geometry_during_modify(); void add(const GeometryComponent &component); @@ -287,6 +294,10 @@ struct GeometrySet { * Returns a read-only curves data-block or null. */ const Curves *get_curves_for_read() const; + /** + * Returns read-only curve edit hints or null. + */ + const blender::bke::CurvesEditHints *get_curve_edit_hints_for_read() const; /** * Returns a mutable mesh or null. No ownership is transferred. @@ -304,6 +315,10 @@ struct GeometrySet { * Returns a mutable curves data-block or null. No ownership is transferred. */ Curves *get_curves_for_write(); + /** + * Returns mutable curve edit hints or null. + */ + blender::bke::CurvesEditHints *get_curve_edit_hints_for_write(); /* Utility methods for replacement. */ /** @@ -825,3 +840,37 @@ class VolumeComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME; }; + +/** + * When the original data is in some edit mode, we want to propagate some additional information + * through object evaluation. This information can be used by edit modes to support working on + * evaluated data. + * + * This component is added at the beginning of modifier evaluation. + */ +class GeometryComponentEditData final : public GeometryComponent { + public: + /** + * Information about how original curves are manipulated during evaluation. This data is used so + * that curve sculpt tools can work on evaluated data. It is not stored in #CurveComponent + * because the data remains valid even when there is no actual curves geometry anymore, for + * example, when the curves have been converted to a mesh. + */ + std::unique_ptr<blender::bke::CurvesEditHints> curves_edit_hints_; + + GeometryComponentEditData(); + + GeometryComponent *copy() const final; + bool owns_direct_data() const final; + void ensure_owns_direct_data() final; + + /** + * The first node that does topology changing operations on curves should store the curve point + * positions it retrieved as input. Without this, information about the deformed positions is + * lost, which would make curves sculpt mode fall back to using original curve positions instead + * of deformed ones. + */ + static void remember_deformed_curve_positions_if_necessary(GeometrySet &geometry); + + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_EDIT; +}; diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 356709d8942..0b7d1a1835f 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -137,4 +137,15 @@ float3 compute_bary_coord_in_triangle(const Mesh &mesh, const MLoopTri &looptri, const float3 &position); +template<typename T> +inline T sample_corner_attrribute_with_bary_coords(const float3 &bary_weights, + const MLoopTri &looptri, + const Span<T> corner_attribute) +{ + return attribute_math::mix3(bary_weights, + corner_attribute[looptri.tri[0]], + corner_attribute[looptri.tri[1]], + corner_attribute[looptri.tri[2]]); +} + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index df4b70d4fe6..2ac42037198 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -139,6 +139,7 @@ set(SRC intern/freestyle.c intern/geometry_component_curve.cc intern/geometry_component_curves.cc + intern/geometry_component_edit_data.cc intern/geometry_component_instances.cc intern/geometry_component_mesh.cc intern/geometry_component_pointcloud.cc @@ -353,6 +354,7 @@ set(SRC BKE_constraint.h BKE_context.h BKE_crazyspace.h + BKE_crazyspace.hh BKE_cryptomatte.h BKE_cryptomatte.hh BKE_curve.h diff --git a/source/blender/blenkernel/intern/crazyspace.cc b/source/blender/blenkernel/intern/crazyspace.cc index c3db3095343..978606ef1fa 100644 --- a/source/blender/blenkernel/intern/crazyspace.cc +++ b/source/blender/blenkernel/intern/crazyspace.cc @@ -19,7 +19,10 @@ #include "BKE_DerivedMesh.h" #include "BKE_crazyspace.h" +#include "BKE_crazyspace.hh" +#include "BKE_curves.hh" #include "BKE_editmesh.h" +#include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" @@ -586,3 +589,64 @@ void BKE_crazyspace_api_eval_clear(Object *object) } /** \} */ + +namespace blender::bke::crazyspace { + +GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph, + const Object &ob_orig) +{ + BLI_assert(ob_orig.type == OB_CURVES); + const Curves &curves_id_orig = *static_cast<const Curves *>(ob_orig.data); + const CurvesGeometry &curves_orig = CurvesGeometry::wrap(curves_id_orig.geometry); + const int points_num = curves_orig.points_num(); + + GeometryDeformation deformation; + /* Use the undeformed positions by default. */ + deformation.positions = curves_orig.positions(); + + const Object *ob_eval = DEG_get_evaluated_object(&depsgraph, const_cast<Object *>(&ob_orig)); + if (ob_eval == nullptr) { + return deformation; + } + const GeometrySet *geometry_eval = ob_eval->runtime.geometry_set_eval; + if (geometry_eval == nullptr) { + return deformation; + } + + /* If available, use deformation information generated during evaluation. */ + const GeometryComponentEditData *edit_component_eval = + geometry_eval->get_component_for_read<GeometryComponentEditData>(); + bool uses_extra_positions = false; + if (edit_component_eval != nullptr) { + const CurvesEditHints *edit_hints = edit_component_eval->curves_edit_hints_.get(); + if (edit_hints != nullptr && &edit_hints->curves_id_orig == &curves_id_orig) { + if (edit_hints->positions.has_value()) { + BLI_assert(edit_hints->positions->size() == points_num); + deformation.positions = *edit_hints->positions; + uses_extra_positions = true; + } + if (edit_hints->deform_mats.has_value()) { + BLI_assert(edit_hints->deform_mats->size() == points_num); + deformation.deform_mats = *edit_hints->deform_mats; + } + } + } + + /* Use the positions of the evaluated curves directly, if the number of points matches. */ + if (!uses_extra_positions) { + const CurveComponent *curves_component_eval = + geometry_eval->get_component_for_read<CurveComponent>(); + if (curves_component_eval != nullptr) { + const Curves *curves_id_eval = curves_component_eval->get_for_read(); + if (curves_id_eval != nullptr) { + const CurvesGeometry &curves_eval = CurvesGeometry::wrap(curves_id_eval->geometry); + if (curves_eval.points_num() == points_num) { + deformation.positions = curves_eval.positions(); + } + } + } + } + return deformation; +} + +} // namespace blender::bke::crazyspace diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index 5684a2e5b07..6554f42d3dd 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -319,6 +319,14 @@ void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob Curves *curves = static_cast<Curves *>(object->data); GeometrySet geometry_set = GeometrySet::create_with_curves(curves, GeometryOwnershipType::ReadOnly); + if (object->mode == OB_MODE_SCULPT_CURVES) { + /* Try to propagate deformation data through modifier evaluation, so that sculpt mode can work + * on evaluated curves. */ + GeometryComponentEditData &edit_component = + geometry_set.get_component_for_write<GeometryComponentEditData>(); + edit_component.curves_edit_hints_ = std::make_unique<blender::bke::CurvesEditHints>( + *static_cast<const Curves *>(DEG_get_original_object(object)->data)); + } curves_evaluate_modifiers(depsgraph, scene, object, geometry_set); /* Assign evaluated object. */ @@ -409,4 +417,20 @@ CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const } } +bool CurvesEditHints::is_valid() const +{ + const int point_num = this->curves_id_orig.geometry.point_num; + if (this->positions.has_value()) { + if (this->positions->size() != point_num) { + return false; + } + } + if (this->deform_mats.has_value()) { + if (this->deform_mats->size() != point_num) { + return false; + } + } + return true; +} + } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_component_edit_data.cc b/source/blender/blenkernel/intern/geometry_component_edit_data.cc new file mode 100644 index 00000000000..2c00de3254f --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_component_edit_data.cc @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" + +using namespace blender; +using namespace blender::bke; + +GeometryComponentEditData::GeometryComponentEditData() : GeometryComponent(GEO_COMPONENT_TYPE_EDIT) +{ +} + +GeometryComponent *GeometryComponentEditData::copy() const +{ + GeometryComponentEditData *new_component = new GeometryComponentEditData(); + if (curves_edit_hints_) { + new_component->curves_edit_hints_ = std::make_unique<CurvesEditHints>(*curves_edit_hints_); + } + return new_component; +} + +bool GeometryComponentEditData::owns_direct_data() const +{ + return true; +} + +void GeometryComponentEditData::ensure_owns_direct_data() +{ + /* Nothing to do. */ +} + +void GeometryComponentEditData::remember_deformed_curve_positions_if_necessary( + GeometrySet &geometry) +{ + /* This component should be created at the start of object evaluation if it's necessary. */ + if (!geometry.has<GeometryComponentEditData>()) { + return; + } + GeometryComponentEditData &edit_component = + geometry.get_component_for_write<GeometryComponentEditData>(); + if (!edit_component.curves_edit_hints_) { + return; + } + if (edit_component.curves_edit_hints_->positions.has_value()) { + return; + } + const Curves *curves_id = geometry.get_curves_for_read(); + if (curves_id == nullptr) { + return; + } + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + const int points_num = curves.points_num(); + if (points_num != edit_component.curves_edit_hints_->curves_id_orig.geometry.point_num) { + return; + } + edit_component.curves_edit_hints_->positions.emplace(points_num); + edit_component.curves_edit_hints_->positions->as_mutable_span().copy_from(curves.positions()); +} diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index c6fe8eebc7f..1a0ce4f0893 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -53,6 +53,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ return new VolumeComponent(); case GEO_COMPONENT_TYPE_CURVE: return new CurveComponent(); + case GEO_COMPONENT_TYPE_EDIT: + return new GeometryComponentEditData(); } BLI_assert_unreachable(); return nullptr; @@ -175,6 +177,20 @@ void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component } } +void GeometrySet::keep_only_during_modify( + const blender::Span<GeometryComponentType> component_types) +{ + Vector<GeometryComponentType> extended_types = component_types; + extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_INSTANCES); + extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_EDIT); + this->keep_only(extended_types); +} + +void GeometrySet::remove_geometry_during_modify() +{ + this->keep_only_during_modify({}); +} + void GeometrySet::add(const GeometryComponent &component) { BLI_assert(!components_[component.type()]); @@ -290,6 +306,13 @@ const Curves *GeometrySet::get_curves_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } +const blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_read() const +{ + const GeometryComponentEditData *component = + this->get_component_for_read<GeometryComponentEditData>(); + return (component == nullptr) ? nullptr : component->curves_edit_hints_.get(); +} + bool GeometrySet::has_pointcloud() const { const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>(); @@ -453,6 +476,16 @@ Curves *GeometrySet::get_curves_for_write() return component == nullptr ? nullptr : component->get_for_write(); } +blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write() +{ + if (!this->has<GeometryComponentEditData>()) { + return nullptr; + } + GeometryComponentEditData &component = + this->get_component_for_write<GeometryComponentEditData>(); + return component.curves_edit_hints_.get(); +} + void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_types, const bool include_instances, const AttributeForeachCallback callback) const @@ -679,6 +712,8 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) case GEO_COMPONENT_TYPE_CURVE: is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT); break; + case GEO_COMPONENT_TYPE_EDIT: + break; } if (is_instance) { return true; diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index 8642d728909..17759b6a8ac 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -332,6 +332,29 @@ extern const float bvhtree_kdop_axes[13][3]; namespace blender { +using BVHTree_RayCastCallback_CPP = + FunctionRef<void(int index, const BVHTreeRay &ray, BVHTreeRayHit &hit)>; + +inline void BLI_bvhtree_ray_cast_all_cpp(BVHTree &tree, + const float3 co, + const float3 dir, + float radius, + float hit_dist, + BVHTree_RayCastCallback_CPP fn) +{ + BLI_bvhtree_ray_cast_all( + &tree, + co, + dir, + radius, + hit_dist, + [](void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) { + BVHTree_RayCastCallback_CPP fn = *static_cast<BVHTree_RayCastCallback_CPP *>(userdata); + fn(index, *ray, *hit); + }, + &fn); +} + using BVHTree_RangeQuery_CPP = FunctionRef<void(int index, const float3 &co, float dist_sq)>; inline void BLI_bvhtree_range_query_cpp(BVHTree &tree, diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc index 26145a386f5..2757701675b 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc @@ -23,6 +23,8 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_report.h" @@ -42,6 +44,8 @@ #include "WM_api.h" +#include "DEG_depsgraph_query.h" + /** * The code below uses a suffix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -80,13 +84,15 @@ struct AddOperationExecutor { AddOperation *self_ = nullptr; CurvesSculptCommonContext ctx_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; - Span<MLoopTri> surface_looptris_; + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; @@ -99,14 +105,6 @@ struct AddOperationExecutor { CurvesSurfaceTransforms transforms_; - BVHTreeFromMesh surface_bvh_; - - struct AddedPoints { - Vector<float3> positions_cu; - Vector<float3> bary_coords; - Vector<int> looptri_indices; - }; - AddOperationExecutor(const bContext &C) : ctx_(C) { } @@ -114,19 +112,26 @@ struct AddOperationExecutor { void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension) { self_ = &self; - object_ = CTX_data_active_object(&C); + curves_ob_orig_ = CTX_data_active_object(&C); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); - if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); return; } - transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - surface_ob_ = curves_id_->surface; - surface_ = static_cast<Mesh *>(surface_ob_->data); + Object &surface_ob_orig = *curves_id_orig_->surface; + Mesh &surface_orig = *static_cast<Mesh *>(surface_ob_orig.data); + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, &surface_ob_orig); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -143,56 +148,70 @@ struct AddOperationExecutor { return; } + /* Find UV map. */ + VArraySpan<float2> surface_uv_map; + if (curves_id_orig_->surface_uv_map != nullptr) { + surface_uv_map = bke::mesh_attributes(surface_orig) + .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_) + .lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + } + + if (surface_uv_map.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + const double time = PIL_check_seconds_timer() * 1000000.0; /* Use a pointer cast to avoid overflow warnings. */ RandomNumberGenerator rng{*(uint32_t *)(&time)}; - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; + surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), + BKE_mesh_runtime_looptri_len(surface_eval_)}; /* Sample points on the surface using one of multiple strategies. */ - AddedPoints added_points; + Vector<float2> sampled_uvs; if (add_amount_ == 1) { - this->sample_in_center_with_symmetry(added_points); + this->sample_in_center_with_symmetry(sampled_uvs); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected_with_symmetry(rng, added_points); + this->sample_projected_with_symmetry(rng, sampled_uvs); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical_with_symmetry(rng, added_points); + this->sample_spherical_with_symmetry(rng, sampled_uvs); } else { BLI_assert_unreachable(); } - if (added_points.bary_coords.is_empty()) { + if (sampled_uvs.is_empty()) { /* No new points have been added. */ return; } - /* Find UV map. */ - VArraySpan<float2> surface_uv_map; - if (curves_id_->surface_uv_map != nullptr) { - const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_); - surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map, - ATTR_DOMAIN_CORNER); - } + const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig), + BKE_mesh_runtime_looptri_len(&surface_orig)}; /* Find normals. */ - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); + if (!CustomData_has_layer(&surface_orig.ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(&surface_orig); } const Span<float3> corner_normals_su = { - reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), - surface_->totloop}; + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig.ldata, CD_NORMAL)), + surface_orig.totloop}; + + const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; geometry::AddCurvesOnMeshInputs add_inputs; - add_inputs.root_positions_cu = added_points.positions_cu; - add_inputs.bary_coords = added_points.bary_coords; - add_inputs.looptri_indices = added_points.looptri_indices; + add_inputs.uvs = sampled_uvs; add_inputs.interpolate_length = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; add_inputs.interpolate_shape = brush_settings_->flag & @@ -201,13 +220,10 @@ struct AddOperationExecutor { BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; add_inputs.fallback_curve_length = brush_settings_->curve_length; add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); - add_inputs.surface = surface_; - add_inputs.surface_bvh = &surface_bvh_; - add_inputs.surface_looptris = surface_looptris_; - add_inputs.surface_uv_map = surface_uv_map; + add_inputs.transforms = &transforms_; + add_inputs.reverse_uv_sampler = &reverse_uv_sampler; + add_inputs.surface = &surface_orig; add_inputs.corner_normals_su = corner_normals_su; - add_inputs.curves_to_surface_mat = transforms_.curves_to_surface; - add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal; if (add_inputs.interpolate_length || add_inputs.interpolate_shape || add_inputs.interpolate_point_count) { @@ -215,17 +231,17 @@ struct AddOperationExecutor { add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_; } - geometry::add_curves_on_mesh(*curves_, add_inputs); + geometry::add_curves_on_mesh(*curves_orig_, add_inputs); - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } /** * Sample a single point exactly at the mouse position. */ - void sample_in_center_with_symmetry(AddedPoints &r_added_points) + void sample_in_center_with_symmetry(Vector<float2> &r_sampled_uvs) { float3 ray_start_wo, ray_end_wo; ED_view3d_win_to_segment_clipped( @@ -234,15 +250,15 @@ struct AddOperationExecutor { const float3 ray_end_cu = transforms_.world_to_curves * ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float4x4 transform = transforms_.curves_to_surface * brush_transform; - this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu); + this->sample_in_center(r_sampled_uvs, transform * ray_start_cu, transform * ray_end_cu); } } - void sample_in_center(AddedPoints &r_added_points, + void sample_in_center(Vector<float2> &r_sampled_uvs, const float3 &ray_start_su, const float3 &ray_end_su) { @@ -251,58 +267,61 @@ struct AddOperationExecutor { BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, + BLI_bvhtree_ray_cast(surface_bvh_eval_.tree, ray_start_su, ray_direction_su, 0.0f, &ray_hit, - surface_bvh_.raycast_callback, - &surface_bvh_); + surface_bvh_eval_.raycast_callback, + &surface_bvh_eval_); if (ray_hit.index == -1) { return; } const int looptri_index = ray_hit.index; + const MLoopTri &looptri = surface_looptris_eval_[looptri_index]; const float3 brush_pos_su = ray_hit.co; const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle( - *surface_, surface_looptris_[looptri_index], brush_pos_su); - - const float3 brush_pos_cu = transforms_.surface_to_curves * brush_pos_su; + *surface_eval_, looptri, brush_pos_su); - r_added_points.positions_cu.append(brush_pos_cu); - r_added_points.bary_coords.append(bary_coords); - r_added_points.looptri_indices.append(looptri_index); + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords, looptri, surface_uv_map_eval_); + r_sampled_uvs.append(uv); } /** * Sample points by shooting rays within the brush radius in the 3D view. */ - void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->sample_projected(rng, r_added_points, brush_transform); + this->sample_projected(rng, r_sampled_uvs, brush_transform); } } void sample_projected(RandomNumberGenerator &rng, - AddedPoints &r_added_points, + Vector<float2> &r_sampled_uvs, const float4x4 &brush_transform) { - const int old_amount = r_added_points.bary_coords.size(); + const int old_amount = r_sampled_uvs.size(); const int max_iterations = 100; int current_iteration = 0; - while (r_added_points.bary_coords.size() < old_amount + add_amount_) { + while (r_sampled_uvs.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } - const int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size(); + Vector<float3> bary_coords; + Vector<int> looptri_indices; + Vector<float3> positions_su; + + const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size(); const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( rng, - *surface_, - surface_bvh_, + *surface_eval_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_, [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { @@ -317,11 +336,14 @@ struct AddOperationExecutor { use_front_face_, add_amount_, missing_amount, - r_added_points.bary_coords, - r_added_points.looptri_indices, - r_added_points.positions_cu); - for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { - pos = transforms_.surface_to_curves * pos; + bary_coords, + looptri_indices, + positions_su); + + for (const int i : IndexRange(new_points)) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_sampled_uvs.append(uv); } } } @@ -329,13 +351,13 @@ struct AddOperationExecutor { /** * Sample points in a 3D sphere around the surface position that the mouse hovers over. */ - void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points) + void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs) { const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, transforms_, - surface_bvh_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_); if (!brush_3d.has_value()) { @@ -355,7 +377,7 @@ struct AddOperationExecutor { const float3 view_ray_end_cu = transforms_.world_to_curves * view_ray_end_wo; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float4x4 transform = transforms_.curves_to_surface * brush_transform; @@ -365,13 +387,12 @@ struct AddOperationExecutor { const float brush_radius_su = transform_brush_radius( transform, brush_3d->position_cu, brush_3d->radius_cu); - this->sample_spherical( - rng, r_added_points, brush_pos_su, brush_radius_su, view_direction_su); + this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su); } } void sample_spherical(RandomNumberGenerator &rng, - AddedPoints &r_added_points, + Vector<float2> &r_sampled_uvs, const float3 &brush_pos_su, const float brush_radius_su, const float3 &view_direction_su) @@ -379,32 +400,32 @@ struct AddOperationExecutor { const float brush_radius_sq_su = pow2f(brush_radius_su); /* Find surface triangles within brush radius. */ - Vector<int> looptri_indices; + Vector<int> selected_looptri_indices; if (use_front_face_) { BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - const MLoopTri &looptri = surface_looptris_[index]; - const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co; - const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co; - const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; + const MLoopTri &looptri = surface_looptris_eval_[index]; + const float3 v0_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[0]].v].co; + const float3 v1_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[1]].v].co; + const float3 v2_su = surface_eval_->mvert[surface_eval_->mloop[looptri.tri[2]].v].co; float3 normal_su; normal_tri_v3(normal_su, v0_su, v1_su, v2_su); if (math::dot(normal_su, view_direction_su) >= 0.0f) { return; } - looptri_indices.append(index); + selected_looptri_indices.append(index); }); } else { BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - looptri_indices.append(index); + selected_looptri_indices.append(index); }); } @@ -418,42 +439,45 @@ struct AddOperationExecutor { const int max_iterations = 5; int current_iteration = 0; - const int old_amount = r_added_points.bary_coords.size(); - while (r_added_points.bary_coords.size() < old_amount + add_amount_) { + const int old_amount = r_sampled_uvs.size(); + while (r_sampled_uvs.size() < old_amount + add_amount_) { if (current_iteration++ >= max_iterations) { break; } + Vector<float3> bary_coords; + Vector<int> looptri_indices; + Vector<float3> positions_su; const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( rng, - *surface_, - looptri_indices, + *surface_eval_, + selected_looptri_indices, brush_pos_su, brush_radius_su, approximate_density_su, - r_added_points.bary_coords, - r_added_points.looptri_indices, - r_added_points.positions_cu); - for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) { - pos = transforms_.surface_to_curves * pos; + bary_coords, + looptri_indices, + positions_su); + for (const int i : IndexRange(new_points)) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_sampled_uvs.append(uv); } } /* Remove samples when there are too many. */ - while (r_added_points.bary_coords.size() > old_amount + add_amount_) { + while (r_sampled_uvs.size() > old_amount + add_amount_) { const int index_to_remove = rng.get_int32(add_amount_) + old_amount; - r_added_points.bary_coords.remove_and_reorder(index_to_remove); - r_added_points.looptri_indices.remove_and_reorder(index_to_remove); - r_added_points.positions_cu.remove_and_reorder(index_to_remove); + r_sampled_uvs.remove_and_reorder(index_to_remove); } } void ensure_curve_roots_kdtree() { if (self_->curve_roots_kdtree_ == nullptr) { - self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num()); - for (const int curve_i : curves_->curves_range()) { - const int root_point_i = curves_->offsets()[curve_i]; - const float3 &root_pos_cu = curves_->positions()[root_point_i]; + self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num()); + for (const int curve_i : curves_orig_->curves_range()) { + const int root_point_i = curves_orig_->offsets()[curve_i]; + const float3 &root_pos_cu = curves_orig_->positions()[root_point_i]; BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); } BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); @@ -467,17 +491,8 @@ void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension & executor.execute(*this, C, stroke_extension); } -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, - ReportList *reports) +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation() { - const Object &ob_active = *CTX_data_active_object(&C); - BLI_assert(ob_active.type == OB_CURVES); - const Curves &curves_id = *static_cast<Curves *>(ob_active.data); - if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) { - BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh"); - return {}; - } - return std::make_unique<AddOperation>(); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc index ff27c16dc36..9803c6fdcc6 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc @@ -8,6 +8,9 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_report.h" #include "ED_view3d.h" @@ -20,6 +23,10 @@ #include "BLI_length_parameterize.hh" #include "BLI_task.hh" +#include "DEG_depsgraph_query.h" + +#include "BLT_translation.h" + /** * The code below uses a prefix naming convention to indicate the coordinate space: * cu: Local space of the curves object that is being edited. @@ -48,7 +55,8 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu const float brush_radius_re, const ARegion ®ion, const RegionView3D &rv3d, - const Object &object) + const Object &object, + const Span<float3> positions) { /* This value might have to be adjusted based on user feedback. */ const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f); @@ -88,8 +96,6 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu } }; - const Span<float3> positions = curves.positions(); - BrushPositionCandidate best_candidate = threading::parallel_reduce( curves.curves_range(), 128, @@ -175,20 +181,21 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, { const Curves &curves_id = *static_cast<Curves *>(curves_object.data); const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - const Object *surface_object = curves_id.surface; + Object *surface_object = curves_id.surface; + Object *surface_object_eval = DEG_get_evaluated_object(&depsgraph, surface_object); float3 center_ray_start_wo, center_ray_end_wo; ED_view3d_win_to_segment_clipped( &depsgraph, ®ion, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true); /* Shorten ray when the surface object is hit. */ - if (surface_object != nullptr) { + if (surface_object_eval != nullptr) { const float4x4 surface_to_world_mat = surface_object->obmat; const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); - Mesh &surface = *static_cast<Mesh *>(surface_object->data); + Mesh *surface_eval = BKE_object_get_evaluated_mesh(surface_object_eval); BVHTreeFromMesh surface_bvh; - BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2); + BKE_bvhtree_from_mesh_get(&surface_bvh, surface_eval, BVHTREE_FROM_LOOPTRI, 2); BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo; @@ -222,6 +229,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo; const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo; + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_object); + const std::optional<float3> brush_position_optional_cu = find_curves_brush_position( curves, center_ray_start_cu, @@ -229,7 +239,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph, brush_radius_re, region, rv3d, - curves_object); + curves_object, + deformation.positions); if (!brush_position_optional_cu.has_value()) { /* Nothing found. */ return std::nullopt; @@ -380,4 +391,21 @@ CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C) this->rv3d = CTX_wm_region_view3d(&C); } +void report_missing_surface(ReportList *reports) +{ + BKE_report(reports, RPT_WARNING, TIP_("Missing surface mesh")); +} + +void report_missing_uv_map_on_original_surface(ReportList *reports) +{ + BKE_report( + reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on original surface")); +} + +void report_missing_uv_map_on_evaluated_surface(ReportList *reports) +{ + BKE_report( + reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on evaluated surface")); +} + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc index 449f1786167..52f2ddc6550 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc @@ -13,12 +13,15 @@ #include "PIL_time.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "BKE_attribute_math.hh" #include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" +#include "BKE_crazyspace.hh" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" @@ -88,9 +91,9 @@ struct CombOperationExecutor { eBrushFalloffShape falloff_shape_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; VArray<float> point_factors_; Vector<int64_t> selected_curve_indices_; @@ -112,7 +115,12 @@ struct CombOperationExecutor { BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; }); - object_ = CTX_data_active_object(&C); + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + if (curves_orig_->curves_num() == 0) { + return; + } curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -122,16 +130,10 @@ struct CombOperationExecutor { falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); - if (curves_->curves_num() == 0) { - return; - } - - transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - point_factors_ = get_point_selection(*curves_id_); - curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + point_factors_ = get_point_selection(*curves_id_orig_); + curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_); brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; @@ -160,9 +162,9 @@ struct CombOperationExecutor { this->restore_segment_lengths(changed_curves); - curves_->tag_positions_changed(); - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + curves_orig_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } @@ -172,7 +174,7 @@ struct CombOperationExecutor { void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { this->comb_projected(r_changed_curves, brush_transform); } @@ -183,10 +185,12 @@ struct CombOperationExecutor { { const float4x4 brush_transform_inv = brush_transform.inverted(); - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); @@ -195,16 +199,18 @@ struct CombOperationExecutor { Vector<int> &local_changed_curves = r_changed_curves.local(); for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 old_pos_cu = deformation.positions[point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; /* Find the position of the point in screen space. */ - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2( - old_pos_re, brush_pos_prev_re_, brush_pos_re_); + old_symm_pos_re, brush_pos_prev_re_, brush_pos_re_); if (distance_to_brush_sq_re > brush_radius_sq_re) { /* Ignore the point because it's too far away. */ continue; @@ -219,16 +225,20 @@ struct CombOperationExecutor { /* Offset the old point position in screen space and transform it back into 3D space. */ - const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; - float3 new_position_wo; + const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight; + float3 new_symm_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - transforms_.curves_to_world * old_pos_cu, - new_position_re, - new_position_wo); - const float3 new_position_cu = brush_transform * - (transforms_.world_to_curves * new_position_wo); - positions_cu[point_i] = new_position_cu; + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + const float3 new_pos_cu = brush_transform * + (transforms_.world_to_curves * new_symm_pos_wo); + + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu_orig[point_i] += translation_orig; curve_changed = true; } @@ -245,7 +255,7 @@ struct CombOperationExecutor { void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves) { float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); float3 brush_start_wo, brush_end_wo; ED_view3d_win_to_3d(ctx_.v3d, @@ -264,7 +274,7 @@ struct CombOperationExecutor { const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { this->comb_spherical(r_changed_curves, brush_transform * brush_start_cu, @@ -278,17 +288,20 @@ struct CombOperationExecutor { const float3 &brush_end_cu, const float brush_radius_cu) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { Vector<int> &local_changed_curves = r_changed_curves.local(); for (const int curve_i : curve_selection_.slice(range)) { bool curve_changed = false; - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 pos_old_cu = positions_cu[point_i]; + const float3 pos_old_cu = deformation.positions[point_i]; /* Compute distance to the brush. */ const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( @@ -306,8 +319,12 @@ struct CombOperationExecutor { /* Combine the falloff and brush strength. */ const float weight = brush_strength_ * radius_falloff * point_factors_[point_i]; + const float3 translation_eval_cu = weight * brush_diff_cu; + const float3 translation_orig_cu = deformation.translation_from_deformed_to_original( + point_i, translation_eval_cu); + /* Update the point position. */ - positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu; + positions_cu[point_i] += translation_orig_cu; curve_changed = true; } if (curve_changed) { @@ -326,7 +343,7 @@ struct CombOperationExecutor { *ctx_.region, *ctx_.v3d, *ctx_.rv3d, - *object_, + *curves_ob_orig_, brush_pos_re_, brush_radius_base_re_); if (brush_3d.has_value()) { @@ -340,11 +357,11 @@ struct CombOperationExecutor { */ void initialize_segment_lengths() { - const Span<float3> positions_cu = curves_->positions(); - self_->segment_lengths_cu_.reinitialize(curves_->points_num()); - threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) { + const Span<float3> positions_cu = curves_orig_->positions(); + self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num()); + threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) { for (const int curve_i : range) { - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int point_i : points.drop_back(1)) { const float3 &p1_cu = positions_cu[point_i]; const float3 &p2_cu = positions_cu[point_i + 1]; @@ -361,12 +378,12 @@ struct CombOperationExecutor { void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves) { const Span<float> expected_lengths_cu = self_->segment_lengths_cu_; - MutableSpan<float3> positions_cu = curves_->positions_for_write(); + MutableSpan<float3> positions_cu = curves_orig_->positions_for_write(); threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) { threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) { for (const int curve_i : changed_curves.as_span().slice(range)) { - const IndexRange points = curves_->points_for_curve(curve_i); + const IndexRange points = curves_orig_->points_for_curve(curve_i); for (const int segment_i : points.drop_back(1)) { const float3 &p1_cu = positions_cu[segment_i]; float3 &p2_cu = positions_cu[segment_i + 1]; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc index 777ebd16110..a44499ce133 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc @@ -51,6 +51,12 @@ using blender::bke::CurvesGeometry; class DeleteOperation : public CurvesSculptStrokeOperation { private: CurvesBrush3D brush_3d_; + /** + * Need to store those in case the brush is evaluated more than once before the curves are + * evaluated again. This can happen when the mouse is moved quickly and the brush spacing is + * small. + */ + Vector<float3> deformed_positions_; friend struct DeleteOperationExecutor; @@ -109,6 +115,9 @@ struct DeleteOperationExecutor { if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { this->initialize_spherical_brush_reference_point(); } + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + self_->deformed_positions_ = deformation.positions; } Array<bool> curves_to_delete(curves_->curves_num(), false); @@ -123,12 +132,22 @@ struct DeleteOperationExecutor { } Vector<int64_t> indices; - const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate( curves_->curves_range(), 4096, indices, [&](const int curve_i) { return curves_to_delete[curve_i]; }); - curves_->remove_curves(mask); + /* Remove deleted curves from the stored deformed positions. */ + const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert( + curves_->curves_range()); + Vector<float3> new_deformed_positions; + for (const IndexRange curves_range : ranges_to_keep) { + new_deformed_positions.extend( + self_->deformed_positions_.as_span().slice(curves_->points_for_curves(curves_range))); + } + self_->deformed_positions_ = std::move(new_deformed_positions); + + curves_->remove_curves(mask_to_delete); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); @@ -151,8 +170,6 @@ struct DeleteOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - Span<float3> positions_cu = curves_->positions(); - const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); @@ -160,7 +177,7 @@ struct DeleteOperationExecutor { for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); if (points.size() == 1) { - const float3 pos_cu = brush_transform_inv * positions_cu[points.first()]; + const float3 pos_cu = brush_transform_inv * self_->deformed_positions_[points.first()]; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); @@ -171,8 +188,8 @@ struct DeleteOperationExecutor { } for (const int segment_i : points.drop_back(1)) { - const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 pos1_cu = brush_transform_inv * self_->deformed_positions_[segment_i]; + const float3 pos2_cu = brush_transform_inv * self_->deformed_positions_[segment_i + 1]; float2 pos1_re, pos2_re; ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values); @@ -212,8 +229,6 @@ struct DeleteOperationExecutor { void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete) { - Span<float3> positions_cu = curves_->positions(); - const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -222,7 +237,7 @@ struct DeleteOperationExecutor { const IndexRange points = curves_->points_for_curve(curve_i); if (points.size() == 1) { - const float3 &pos_cu = positions_cu[points.first()]; + const float3 &pos_cu = self_->deformed_positions_[points.first()]; const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu); if (distance_sq_cu < brush_radius_sq_cu) { curves_to_delete[curve_i] = true; @@ -231,8 +246,8 @@ struct DeleteOperationExecutor { } for (const int segment_i : points.drop_back(1)) { - const float3 &pos1_cu = positions_cu[segment_i]; - const float3 &pos2_cu = positions_cu[segment_i + 1]; + const float3 &pos1_cu = self_->deformed_positions_[segment_i]; + const float3 &pos2_cu = self_->deformed_positions_[segment_i + 1]; const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu); if (distance_sq_cu > brush_radius_sq_cu) { diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc index e211a568705..b07f5c74055 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc @@ -2,6 +2,7 @@ #include <numeric> +#include "BKE_attribute_math.hh" #include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" @@ -9,11 +10,15 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_report.h" #include "ED_screen.h" #include "ED_view3d.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "BLI_index_mask_ops.hh" #include "BLI_kdtree.h" @@ -36,7 +41,11 @@ namespace blender::ed::sculpt_paint { class DensityAddOperation : public CurvesSculptStrokeOperation { private: /** Used when some data should be interpolated from existing curves. */ - KDTree_3d *curve_roots_kdtree_ = nullptr; + KDTree_3d *original_curve_roots_kdtree_ = nullptr; + /** Contains curve roots of all curves that existed before the brush started. */ + KDTree_3d *deformed_curve_roots_kdtree_ = nullptr; + /** Root positions of curves that have been added in the current brush stroke. */ + Vector<float3> new_deformed_root_positions_; int original_curve_num_ = 0; friend struct DensityAddOperationExecutor; @@ -44,8 +53,11 @@ class DensityAddOperation : public CurvesSculptStrokeOperation { public: ~DensityAddOperation() override { - if (curve_roots_kdtree_ != nullptr) { - BLI_kdtree_3d_free(curve_roots_kdtree_); + if (original_curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(original_curve_roots_kdtree_); + } + if (deformed_curve_roots_kdtree_ != nullptr) { + BLI_kdtree_3d_free(deformed_curve_roots_kdtree_); } } @@ -56,14 +68,18 @@ struct DensityAddOperationExecutor { DensityAddOperation *self_ = nullptr; CurvesSculptCommonContext ctx_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; + + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; - Span<MLoopTri> surface_looptris_; - Span<float3> corner_normals_su_; + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; @@ -75,8 +91,6 @@ struct DensityAddOperationExecutor { CurvesSurfaceTransforms transforms_; - BVHTreeFromMesh surface_bvh_; - DensityAddOperationExecutor(const bContext &C) : ctx_(C) { } @@ -86,32 +100,51 @@ struct DensityAddOperationExecutor { const StrokeExtension &stroke_extension) { self_ = &self; - object_ = CTX_data_active_object(&C); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); if (stroke_extension.is_first) { - self_->original_curve_num_ = curves_->curves_num(); + self_->original_curve_num_ = curves_orig_->curves_num(); } - if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); return; } - surface_ob_ = curves_id_->surface; - surface_ = static_cast<Mesh *>(surface_ob_->data); - - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; + surface_ob_orig_ = curves_id_orig_->surface; + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); - transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); + surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), + BKE_mesh_runtime_looptri_len(surface_eval_)}; + /* Find UV map. */ + VArraySpan<float2> surface_uv_map; + if (curves_id_orig_->surface_uv_map != nullptr) { + surface_uv_map = bke::mesh_attributes(*surface_orig_) + .lookup<float2>(curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER); + surface_uv_map_eval_ = bke::mesh_attributes(*surface_eval_) + .lookup<float2>(curves_id_orig_->surface_uv_map, + ATTR_DOMAIN_CORNER); + } + if (surface_uv_map.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; } - corner_normals_su_ = { - reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), - surface_->totloop}; + + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -123,23 +156,17 @@ struct DensityAddOperationExecutor { const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); - - Vector<float3> new_bary_coords; - Vector<int> new_looptri_indices; Vector<float3> new_positions_cu; + Vector<float2> new_uvs; const double time = PIL_check_seconds_timer() * 1000000.0; RandomNumberGenerator rng{*(uint32_t *)(&time)}; /* Find potential new curve root points. */ if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->sample_projected_with_symmetry( - rng, new_bary_coords, new_looptri_indices, new_positions_cu); + this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->sample_spherical_with_symmetry( - rng, new_bary_coords, new_looptri_indices, new_positions_cu); + this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu); } else { BLI_assert_unreachable(); @@ -148,9 +175,11 @@ struct DensityAddOperationExecutor { pos = transforms_.surface_to_curves * pos; } - this->ensure_curve_roots_kdtree(); + if (stroke_extension.is_first) { + this->prepare_curve_roots_kdtrees(); + } - const int already_added_curves = curves_->curves_num() - self_->original_curve_num_; + const int already_added_curves = self_->new_deformed_root_positions_.size(); KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves + new_positions_cu.size()); BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); }); @@ -161,15 +190,12 @@ struct DensityAddOperationExecutor { threading::parallel_invoke( /* Build kdtree from root points created by the current stroke. */ [&]() { - const Span<float3> positions_cu = curves_->positions(); - for (const int curve_i : curves_->curves_range().take_back(already_added_curves)) { - const float3 &root_pos_cu = positions_cu[curves_->offsets()[curve_i]]; - BLI_kdtree_3d_insert(new_roots_kdtree, curve_i, root_pos_cu); + for (const int i : IndexRange(already_added_curves)) { + BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]); } for (const int new_i : new_positions_cu.index_range()) { - const int index_in_kdtree = curves_->curves_num() + new_i; const float3 &root_pos_cu = new_positions_cu[new_i]; - BLI_kdtree_3d_insert(new_roots_kdtree, index_in_kdtree, root_pos_cu); + BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu); } BLI_kdtree_3d_balance(new_roots_kdtree); }, @@ -183,7 +209,7 @@ struct DensityAddOperationExecutor { KDTreeNearest_3d nearest; nearest.dist = FLT_MAX; BLI_kdtree_3d_find_nearest( - self_->curve_roots_kdtree_, new_root_pos_cu, &nearest); + self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest); if (nearest.dist < brush_settings_->minimum_distance) { new_curve_skipped[new_i] = true; } @@ -201,12 +227,11 @@ struct DensityAddOperationExecutor { new_roots_kdtree, root_pos_cu, brush_settings_->minimum_distance, - [&](const int other_i, const float *UNUSED(co), float UNUSED(dist_sq)) { - if (other_i < curves_->curves_num()) { + [&](const int other_new_i, const float *UNUSED(co), float UNUSED(dist_sq)) { + if (other_new_i == -1) { new_curve_skipped[new_i] = true; return false; } - const int other_new_i = other_i - curves_->curves_num(); if (new_i == other_new_i) { return true; } @@ -219,31 +244,25 @@ struct DensityAddOperationExecutor { for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) { if (new_curve_skipped[i]) { new_positions_cu.remove_and_reorder(i); - new_bary_coords.remove_and_reorder(i); - new_looptri_indices.remove_and_reorder(i); + new_uvs.remove_and_reorder(i); } } - - /* Find UV map. */ - VArraySpan<float2> surface_uv_map; - if (curves_id_->surface_uv_map != nullptr) { - bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_); - surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map, - ATTR_DOMAIN_CORNER); - } + self_->new_deformed_root_positions_.extend(new_positions_cu); /* Find normals. */ - if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) { - BKE_mesh_calc_normals_split(surface_); + if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_orig_); } const Span<float3> corner_normals_su = { - reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)), - surface_->totloop}; + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)), + surface_orig_->totloop}; + + const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(surface_orig_), + BKE_mesh_runtime_looptri_len(surface_orig_)}; + const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig}; geometry::AddCurvesOnMeshInputs add_inputs; - add_inputs.root_positions_cu = new_positions_cu; - add_inputs.bary_coords = new_bary_coords; - add_inputs.looptri_indices = new_looptri_indices; + add_inputs.uvs = new_uvs; add_inputs.interpolate_length = brush_settings_->flag & BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH; add_inputs.interpolate_shape = brush_settings_->flag & @@ -252,53 +271,67 @@ struct DensityAddOperationExecutor { BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT; add_inputs.fallback_curve_length = brush_settings_->curve_length; add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve); - add_inputs.surface = surface_; - add_inputs.surface_bvh = &surface_bvh_; - add_inputs.surface_looptris = surface_looptris_; - add_inputs.surface_uv_map = surface_uv_map; + add_inputs.transforms = &transforms_; + add_inputs.surface = surface_orig_; add_inputs.corner_normals_su = corner_normals_su; - add_inputs.curves_to_surface_mat = transforms_.curves_to_surface; - add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal; - add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_; + add_inputs.reverse_uv_sampler = &reverse_uv_sampler; + add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_; - geometry::add_curves_on_mesh(*curves_, add_inputs); + geometry::add_curves_on_mesh(*curves_orig_, add_inputs); - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } - void ensure_curve_roots_kdtree() + void prepare_curve_roots_kdtrees() { - if (self_->curve_roots_kdtree_ == nullptr) { - self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num()); - for (const int curve_i : curves_->curves_range()) { - const int root_point_i = curves_->offsets()[curve_i]; - const float3 &root_pos_cu = curves_->positions()[root_point_i]; - BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + const Span<int> curve_offsets = curves_orig_->offsets(); + const Span<float3> original_positions = curves_orig_->positions(); + const Span<float3> deformed_positions = deformation.positions; + BLI_assert(original_positions.size() == deformed_positions.size()); + + auto roots_kdtree_from_positions = [&](const Span<float3> positions) { + KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num()); + for (const int curve_i : curves_orig_->curves_range()) { + const int root_point_i = curve_offsets[curve_i]; + BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]); } - BLI_kdtree_3d_balance(self_->curve_roots_kdtree_); - } + BLI_kdtree_3d_balance(kdtree); + return kdtree; + }; + + threading::parallel_invoke( + [&]() { + self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions); + }, + [&]() { + self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions); + }); } void sample_projected_with_symmetry(RandomNumberGenerator &rng, - Vector<float3> &r_bary_coords, - Vector<int> &r_looptri_indices, + Vector<float2> &r_uvs, Vector<float3> &r_positions_su) { float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float4x4 brush_transform_inv = brush_transform.inverted(); const float4x4 transform = transforms_.curves_to_surface * brush_transform * transforms_.world_to_curves; + Vector<float3> positions_su; + Vector<float3> bary_coords; + Vector<int> looptri_indices; const int new_points = bke::mesh_surface_sample::sample_surface_points_projected( rng, - *surface_, - surface_bvh_, + *surface_eval_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_, [&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) { @@ -311,14 +344,13 @@ struct DensityAddOperationExecutor { true, brush_settings_->density_add_attempts, brush_settings_->density_add_attempts, - r_bary_coords, - r_looptri_indices, - r_positions_su); + bary_coords, + looptri_indices, + positions_su); /* Remove some sampled points randomly based on the brush falloff and strength. */ - const int old_points = r_bary_coords.size() - new_points; - for (int i = r_bary_coords.size() - 1; i >= old_points; i--) { - const float3 pos_su = r_positions_su[i]; + for (int i = new_points - 1; i >= 0; i--) { + const float3 pos_su = positions_su[i]; const float3 pos_cu = brush_transform_inv * transforms_.surface_to_curves * pos_su; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); @@ -327,24 +359,30 @@ struct DensityAddOperationExecutor { brush_, dist_to_brush_re, brush_radius_re_); const float weight = brush_strength_ * radius_falloff; if (rng.get_float() > weight) { - r_bary_coords.remove_and_reorder(i); - r_looptri_indices.remove_and_reorder(i); - r_positions_su.remove_and_reorder(i); + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + positions_su.remove_and_reorder(i); } } + + for (const int i : bary_coords.index_range()) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_uvs.append(uv); + } + r_positions_su.extend(positions_su); } } void sample_spherical_with_symmetry(RandomNumberGenerator &rng, - Vector<float3> &r_bary_coords, - Vector<int> &r_looptri_indices, + Vector<float2> &r_uvs, Vector<float3> &r_positions_su) { const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, *ctx_.region, *ctx_.v3d, transforms_, - surface_bvh_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re_); if (!brush_3d.has_value()) { @@ -352,7 +390,7 @@ struct DensityAddOperationExecutor { } const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); + eCurvesSymmetryType(curves_id_orig_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { const float3 brush_pos_cu = brush_transform * brush_3d->position_cu; const float3 brush_pos_su = transforms_.curves_to_surface * brush_pos_cu; @@ -360,45 +398,54 @@ struct DensityAddOperationExecutor { transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu); const float brush_radius_sq_su = pow2f(brush_radius_su); - Vector<int> looptri_indices; + Vector<int> selected_looptri_indices; BLI_bvhtree_range_query_cpp( - *surface_bvh_.tree, + *surface_bvh_eval_.tree, brush_pos_su, brush_radius_su, [&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) { - looptri_indices.append(index); + selected_looptri_indices.append(index); }); const float brush_plane_area_su = M_PI * brush_radius_sq_su; const float approximate_density_su = brush_settings_->density_add_attempts / brush_plane_area_su; + Vector<float3> positions_su; + Vector<float3> bary_coords; + Vector<int> looptri_indices; const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical( rng, - *surface_, - looptri_indices, + *surface_eval_, + selected_looptri_indices, brush_pos_su, brush_radius_su, approximate_density_su, - r_bary_coords, - r_looptri_indices, - r_positions_su); + bary_coords, + looptri_indices, + positions_su); /* Remove some sampled points randomly based on the brush falloff and strength. */ - const int old_points = r_bary_coords.size() - new_points; - for (int i = r_bary_coords.size() - 1; i >= old_points; i--) { - const float3 pos_su = r_positions_su[i]; + for (int i = new_points - 1; i >= 0; i--) { + const float3 pos_su = positions_su[i]; const float3 pos_cu = transforms_.surface_to_curves * pos_su; const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu); const float radius_falloff = BKE_brush_curve_strength( brush_, dist_to_brush_cu, brush_3d->radius_cu); const float weight = brush_strength_ * radius_falloff; if (rng.get_float() > weight) { - r_bary_coords.remove_and_reorder(i); - r_looptri_indices.remove_and_reorder(i); - r_positions_su.remove_and_reorder(i); + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + positions_su.remove_and_reorder(i); } } + + for (const int i : bary_coords.index_range()) { + const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords( + bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_); + r_uvs.append(uv); + } + r_positions_su.extend(positions_su); } } }; @@ -414,6 +461,13 @@ class DensitySubtractOperation : public CurvesSculptStrokeOperation { private: friend struct DensitySubtractOperationExecutor; + /** + * Deformed root positions of curves that still exist. This has to be stored in case the brush is + * executed more than once before the curves are evaluated again. This can happen when the mouse + * is moved quickly and the brush spacing is small. + */ + Vector<float3> deformed_root_positions_; + public: void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; @@ -433,8 +487,12 @@ struct DensitySubtractOperationExecutor { Vector<int64_t> selected_curve_indices_; IndexMask curve_selection_; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + BVHTreeFromMesh surface_bvh_eval_; const CurvesSculpt *curves_sculpt_ = nullptr; const Brush *brush_ = nullptr; @@ -446,7 +504,6 @@ struct DensitySubtractOperationExecutor { float minimum_distance_; CurvesSurfaceTransforms transforms_; - BVHTreeFromMesh surface_bvh_; KDTree_3d *root_points_kdtree_; @@ -468,11 +525,20 @@ struct DensitySubtractOperationExecutor { return; } - surface_ob_ = curves_id_->surface; - if (surface_ob_ == nullptr) { + surface_ob_orig_ = curves_id_->surface; + if (surface_ob_orig_ == nullptr) { + return; + } + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { return; } - surface_ = static_cast<Mesh *>(surface_ob_->data); + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -488,16 +554,20 @@ struct DensitySubtractOperationExecutor { transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>( brush_->falloff_shape); - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); - const Span<float3> positions_cu = curves_->positions(); + if (stroke_extension.is_first) { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + for (const int curve_i : curves_->curves_range()) { + const int first_point_i = curves_->offsets()[curve_i]; + self_->deformed_root_positions_.append(deformation.positions[first_point_i]); + } + } root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size()); BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); }); for (const int curve_i : curve_selection_) { - const int first_point_i = curves_->offsets()[curve_i]; - const float3 &pos_cu = positions_cu[first_point_i]; + const float3 &pos_cu = self_->deformed_root_positions_[curve_i]; BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu); } BLI_kdtree_3d_balance(root_points_kdtree_); @@ -515,12 +585,23 @@ struct DensitySubtractOperationExecutor { } Vector<int64_t> indices; - const IndexMask mask = index_mask_ops::find_indices_based_on_predicate( + const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate( curves_->curves_range(), 4096, indices, [&](const int curve_i) { return curves_to_delete[curve_i]; }); - curves_->remove_curves(mask); + /* Remove deleted curves fromt he stored deformed root positions. */ + const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert( + curves_->curves_range()); + BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size()); + Vector<float3> new_deformed_positions; + for (const IndexRange range : ranges_to_keep) { + new_deformed_positions.extend(self_->deformed_root_positions_.as_span().slice(range)); + } + self_->deformed_root_positions_ = std::move(new_deformed_positions); + + curves_->remove_curves(mask_to_delete); + BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size()); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); @@ -539,15 +620,12 @@ struct DensitySubtractOperationExecutor { void reduce_density_projected(const float4x4 &brush_transform, MutableSpan<bool> curves_to_delete) { - const Span<float3> positions_cu = curves_->positions(); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - const Span<int> offsets = curves_->offsets(); - /* Randomly select the curves that are allowed to be removed, based on the brush radius and * strength. */ Array<bool> allow_remove_curve(curves_->curves_num(), false); @@ -559,8 +637,7 @@ struct DensitySubtractOperationExecutor { allow_remove_curve[curve_i] = true; continue; } - const int first_point_i = offsets[curve_i]; - const float3 pos_cu = brush_transform * positions_cu[first_point_i]; + const float3 pos_cu = brush_transform * self_->deformed_root_positions_[curve_i]; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); @@ -586,8 +663,7 @@ struct DensitySubtractOperationExecutor { if (!allow_remove_curve[curve_i]) { continue; } - const int first_point_i = offsets[curve_i]; - const float3 orig_pos_cu = positions_cu[first_point_i]; + const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i]; const float3 pos_cu = brush_transform * orig_pos_cu; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); @@ -618,7 +694,7 @@ struct DensitySubtractOperationExecutor { *ctx_.region, *ctx_.v3d, transforms_, - surface_bvh_, + surface_bvh_eval_, brush_pos_re_, brush_radius_re); if (!brush_3d.has_value()) { @@ -638,8 +714,6 @@ struct DensitySubtractOperationExecutor { MutableSpan<bool> curves_to_delete) { const float brush_radius_sq_cu = pow2f(brush_radius_cu); - const Span<float3> positions_cu = curves_->positions(); - const Span<int> offsets = curves_->offsets(); /* Randomly select the curves that are allowed to be removed, based on the brush radius and * strength. */ @@ -652,8 +726,7 @@ struct DensitySubtractOperationExecutor { allow_remove_curve[curve_i] = true; continue; } - const int first_point_i = offsets[curve_i]; - const float3 pos_cu = positions_cu[first_point_i]; + const float3 pos_cu = self_->deformed_root_positions_[curve_i]; const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu); if (dist_to_brush_sq_cu > brush_radius_sq_cu) { @@ -677,8 +750,7 @@ struct DensitySubtractOperationExecutor { if (!allow_remove_curve[curve_i]) { continue; } - const int first_point_i = offsets[curve_i]; - const float3 &pos_cu = positions_cu[first_point_i]; + const float3 &pos_cu = self_->deformed_root_positions_[curve_i]; const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu); if (dist_to_brush_sq_cu > brush_radius_sq_cu) { continue; @@ -717,6 +789,10 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode, { const Scene &scene = *CTX_data_scene(&C); const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint); + const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C); + const ARegion ®ion = *CTX_wm_region(&C); + const View3D &v3d = *CTX_wm_view3d(&C); + const eBrushCurvesSculptDensityMode density_mode = static_cast<eBrushCurvesSculptDensityMode>( brush.curves_sculpt_settings->density_mode); const bool use_invert = brush_mode == BRUSH_STROKE_INVERT; @@ -728,26 +804,29 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode, return use_invert; } - const Object &curves_ob = *CTX_data_active_object(&C); - const Curves &curves_id = *static_cast<Curves *>(curves_ob.data); - const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); - if (curves_id.surface == nullptr) { - /* The brush won't do anything in this case anyway. */ + const Object &curves_ob_orig = *CTX_data_active_object(&C); + const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data); + Object *surface_ob_orig = curves_id_orig.surface; + if (surface_ob_orig == nullptr) { return true; } + Object *surface_ob_eval = DEG_get_evaluated_object(&depsgraph, surface_ob_orig); + if (surface_ob_eval == nullptr) { + return true; + } + const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id_orig.geometry); if (curves.curves_num() <= 1) { return true; } + const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval); + if (surface_mesh_eval == nullptr) { + return true; + } - const CurvesSurfaceTransforms transforms(curves_ob, curves_id.surface); - BVHTreeFromMesh surface_bvh; - BKE_bvhtree_from_mesh_get( - &surface_bvh, static_cast<const Mesh *>(curves_id.surface->data), BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); - - const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C); - const ARegion ®ion = *CTX_wm_region(&C); - const View3D &v3d = *CTX_wm_view3d(&C); + const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface); + BVHTreeFromMesh surface_bvh_eval; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_mesh_eval, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); }); const float2 brush_pos_re = stroke_start.mouse_position; /* Reduce radius so that only an inner circle is used to determine the existing density. */ @@ -755,7 +834,7 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode, /* Find the surface point under the brush. */ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush( - depsgraph, region, v3d, transforms, surface_bvh, brush_pos_re, brush_radius_re); + depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re); if (!brush_3d.has_value()) { return true; } @@ -764,8 +843,9 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode, const float brush_radius_cu = brush_3d->radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob_orig); const Span<int> offsets = curves.offsets(); - const Span<float3> positions_cu = curves.positions(); /* Compute distance from brush to curve roots. */ Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num()); @@ -774,7 +854,7 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode, int &valid_curve_count = valid_curve_count_by_thread.local(); for (const int curve_i : range) { const int root_point_i = offsets[curve_i]; - const float3 &root_pos_cu = positions_cu[root_point_i]; + const float3 &root_pos_cu = deformation.positions[root_point_i]; const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu); if (dist_sq_cu < brush_radius_sq_cu) { distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu), @@ -799,9 +879,9 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode, * center. */ float min_dist_sq_cu = FLT_MAX; for (const int i : IndexRange(check_curve_count)) { - const float3 &pos_i = positions_cu[offsets[distances_sq_to_brush[i].second]]; + const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]]; for (int j = i + 1; j < check_curve_count; j++) { - const float3 &pos_j = positions_cu[offsets[distances_sq_to_brush[j].second]]; + const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]]; const float dist_sq_cu = math::distance_squared(pos_i, pos_j); math::min_inplace(min_dist_sq_cu, dist_sq_cu); } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc index 8ef18ba7da7..1ee43d98e6f 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc @@ -342,7 +342,8 @@ struct CurvesEffectOperationExecutor { void gather_influences_projected( threading::EnumerableThreadSpecific<Influences> &influences_for_thread) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -368,8 +369,8 @@ struct CurvesEffectOperationExecutor { float max_move_distance_cu = 0.0f; for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) { for (const int segment_i : points.drop_back(1)) { - const float3 p1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 p1_cu = brush_transform_inv * deformation.positions[segment_i]; + const float3 p2_cu = brush_transform_inv * deformation.positions[segment_i + 1]; float2 p1_re, p2_re; ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values); @@ -430,7 +431,8 @@ struct CurvesEffectOperationExecutor { void gather_influences_spherical( threading::EnumerableThreadSpecific<Influences> &influences_for_thread) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); float3 brush_pos_start_wo, brush_pos_end_wo; ED_view3d_win_to_3d(ctx_.v3d, @@ -468,8 +470,8 @@ struct CurvesEffectOperationExecutor { const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu; for (const int segment_i : points.drop_back(1)) { - const float3 &p1_cu = positions_cu[segment_i]; - const float3 &p2_cu = positions_cu[segment_i + 1]; + const float3 &p1_cu = deformation.positions[segment_i]; + const float3 &p2_cu = deformation.positions[segment_i + 1]; float3 closest_on_segment_cu; float3 closest_on_brush_cu; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh index 61aa7d201b1..4bb00a7d621 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh @@ -12,6 +12,7 @@ #include "BLI_virtual_array.hh" #include "BKE_attribute.h" +#include "BKE_crazyspace.hh" #include "BKE_curves.hh" #include "ED_curves_sculpt.h" @@ -24,6 +25,7 @@ struct Object; struct Brush; struct Scene; struct BVHTreeFromMesh; +struct ReportList; namespace blender::ed::sculpt_paint { @@ -34,6 +36,7 @@ struct StrokeExtension { bool is_first; float2 mouse_position; float pressure; + ReportList *reports = nullptr; }; float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension); @@ -55,8 +58,7 @@ class CurvesSculptStrokeOperation { virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0; }; -std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C, - ReportList *reports); +std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation(); std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation(); @@ -126,4 +128,8 @@ float transform_brush_radius(const float4x4 &transform, const float3 &brush_position, const float old_radius); +void report_missing_surface(ReportList *reports); +void report_missing_uv_map_on_original_surface(ReportList *reports); +void report_missing_uv_map_on_evaluated_surface(ReportList *reports); + } // namespace blender::ed::sculpt_paint diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 47e0fe3a61a..9271309d922 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -9,6 +9,8 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "WM_api.h" @@ -24,6 +26,7 @@ #include "ED_view3d.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" @@ -122,7 +125,7 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation( case CURVES_SCULPT_TOOL_SNAKE_HOOK: return new_snake_hook_operation(); case CURVES_SCULPT_TOOL_ADD: - return new_add_operation(C, op.reports); + return new_add_operation(); case CURVES_SCULPT_TOOL_GROW_SHRINK: return new_grow_shrink_operation(mode, C); case CURVES_SCULPT_TOOL_SELECTION_PAINT: @@ -176,6 +179,7 @@ static void stroke_update_step(bContext *C, StrokeExtension stroke_extension; RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); stroke_extension.pressure = RNA_float_get(stroke_element, "pressure"); + stroke_extension.reports = op->reports; if (!op_data->operation) { stroke_extension.is_first = true; @@ -1129,14 +1133,21 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent * View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); - Object &curves_ob = *CTX_data_active_object(C); - Curves &curves_id = *static_cast<Curves *>(curves_ob.data); - Object &surface_ob = *curves_id.surface; - Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data); + Object &curves_ob_orig = *CTX_data_active_object(C); + Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data); + Object &surface_ob_orig = *curves_id_orig.surface; + Object *surface_ob_eval = DEG_get_evaluated_object(depsgraph, &surface_ob_orig); + if (surface_ob_eval == nullptr) { + return OPERATOR_CANCELLED; + } + Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval); + if (surface_me_eval == nullptr) { + return OPERATOR_CANCELLED; + } - BVHTreeFromMesh surface_bvh; - BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); + BVHTreeFromMesh surface_bvh_eval; + BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_me_eval, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); }); const int2 mouse_pos_int_re{event->mval}; const float2 mouse_pos_re{mouse_pos_int_re}; @@ -1145,23 +1156,22 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent * ED_view3d_win_to_segment_clipped( depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true); - const float4x4 surface_to_world_mat = surface_ob.obmat; - const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); + const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig}; - const float3 ray_start_su = world_to_surface_mat * ray_start_wo; - const float3 ray_end_su = world_to_surface_mat * ray_end_wo; + const float3 ray_start_su = transforms.world_to_surface * ray_start_wo; + const float3 ray_end_su = transforms.world_to_surface * ray_end_wo; const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); BVHTreeRayHit ray_hit; ray_hit.dist = FLT_MAX; ray_hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh.tree, + BLI_bvhtree_ray_cast(surface_bvh_eval.tree, ray_start_su, ray_direction_su, 0.0f, &ray_hit, - surface_bvh.raycast_callback, - &surface_bvh); + surface_bvh_eval.raycast_callback, + &surface_bvh_eval); if (ray_hit.index == -1) { WM_report(RPT_ERROR, "Cursor must be over the surface mesh"); return OPERATOR_CANCELLED; @@ -1169,16 +1179,13 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent * const float3 hit_pos_su = ray_hit.co; const float3 hit_normal_su = ray_hit.no; - const float4x4 curves_to_world_mat = curves_ob.obmat; - const float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); - const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat; - const float4x4 surface_to_curves_normal_mat = surface_to_curves_mat.inverted().transposed(); - const float3 hit_pos_cu = surface_to_curves_mat * hit_pos_su; - const float3 hit_normal_cu = math::normalize(surface_to_curves_normal_mat * hit_normal_su); + const float3 hit_pos_cu = transforms.surface_to_curves * hit_pos_su; + const float3 hit_normal_cu = math::normalize(transforms.surface_to_curves_normal * + hit_normal_su); MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__); - op_data->curves_to_world_mat = curves_to_world_mat; + op_data->curves_to_world_mat = transforms.curves_to_world; op_data->normal_cu = hit_normal_cu; op_data->pos_cu = hit_pos_cu; op_data->initial_mouse = event->xy; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc index 689b7d22e5e..3e43b1a6361 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc @@ -157,6 +157,9 @@ struct PinchOperationExecutor { { const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); MutableSpan<float3> positions_cu = curves_->positions_for_write(); @@ -167,11 +170,13 @@ struct PinchOperationExecutor { for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + const float3 old_pos_cu = deformation.positions[point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); - const float dist_to_brush_sq_re = math::distance_squared(old_pos_re, brush_pos_re_); + const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_re_); if (dist_to_brush_sq_re > brush_radius_sq_re) { continue; } @@ -182,14 +187,21 @@ struct PinchOperationExecutor { const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff * point_factors_[point_i]; - const float2 new_pos_re = math::interpolate(old_pos_re, brush_pos_re_, weight); - - const float3 old_pos_wo = transforms_.curves_to_world * old_pos_cu; - float3 new_pos_wo; - ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, old_pos_wo, new_pos_re, new_pos_wo); - - const float3 new_pos_cu = transforms_.world_to_curves * new_pos_wo; - positions_cu[point_i] = brush_transform * new_pos_cu; + const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight); + + float3 new_symm_pos_wo; + ED_view3d_win_to_3d(ctx_.v3d, + ctx_.region, + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + + const float3 new_pos_cu = brush_transform * transforms_.world_to_curves * + new_symm_pos_wo; + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu[point_i] += translation_orig; r_changed_curves[curve_i] = true; } } @@ -221,11 +233,14 @@ struct PinchOperationExecutor { MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 old_pos_cu = positions_cu[point_i]; + const float3 old_pos_cu = deformation.positions[point_i]; const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); if (dist_to_brush_sq_cu > brush_radius_sq_cu) { @@ -239,7 +254,10 @@ struct PinchOperationExecutor { point_factors_[point_i]; const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight); - positions_cu[point_i] = new_pos_cu; + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + point_i, translation_eval); + positions_cu[point_i] += translation_orig; r_changed_curves[curve_i] = true; } diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc index 83cfda6dc00..139e0d67e89 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc @@ -4,6 +4,7 @@ #include "BKE_brush.h" #include "BKE_bvhutils.h" #include "BKE_context.h" +#include "BKE_crazyspace.hh" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -20,6 +21,8 @@ #include "BLI_length_parameterize.hh" +#include "GEO_add_curves_on_mesh.hh" + #include "curves_sculpt_intern.hh" namespace blender::ed::sculpt_paint { @@ -38,23 +41,6 @@ class PuffOperation : public CurvesSculptStrokeOperation { void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override; }; -static float3 compute_surface_point_normal(const MLoopTri &looptri, - const float3 &bary_coord, - const Span<float3> corner_normals) -{ - const int l0 = looptri.tri[0]; - const int l1 = looptri.tri[1]; - const int l2 = looptri.tri[2]; - - const float3 &l0_normal = corner_normals[l0]; - const float3 &l1_normal = corner_normals[l1]; - const float3 &l2_normal = corner_normals[l2]; - - const float3 normal = math::normalize( - attribute_math::mix3(bary_coord, l0_normal, l1_normal, l2_normal)); - return normal; -} - /** * Utility class that actually executes the update when the stroke is updated. That's useful * because it avoids passing a very large number of parameters between functions. @@ -183,8 +169,6 @@ struct PuffOperationExecutor { void find_curve_weights_projected(const float4x4 &brush_transform, MutableSpan<float> r_curve_weights) { - Span<float3> positions_cu = curves_->positions(); - const float4x4 brush_transform_inv = brush_transform.inverted(); float4x4 projection; @@ -193,15 +177,18 @@ struct PuffOperationExecutor { const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { for (const int curve_selection_i : range) { const int curve_i = curve_selection_[curve_selection_i]; const IndexRange points = curves_->points_for_curve(curve_i); - const float3 first_pos_cu = brush_transform_inv * positions_cu[points[0]]; + const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]]; float2 prev_pos_re; ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values); for (const int point_i : points.drop_front(1)) { - const float3 pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 pos_cu = brush_transform_inv * deformation.positions[point_i]; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); BLI_SCOPED_DEFER([&]() { prev_pos_re = pos_re; }); @@ -248,16 +235,18 @@ struct PuffOperationExecutor { const float brush_radius_cu, MutableSpan<float> r_curve_weights) { - const Span<float3> positions_cu = curves_->positions(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { for (const int curve_selection_i : range) { const int curve_i = curve_selection_[curve_selection_i]; const IndexRange points = curves_->points_for_curve(curve_i); for (const int point_i : points.drop_front(1)) { - const float3 &prev_pos_cu = positions_cu[point_i - 1]; - const float3 &pos_cu = positions_cu[point_i]; + const float3 &prev_pos_cu = deformation.positions[point_i - 1]; + const float3 &pos_cu = deformation.positions[point_i]; const float dist_to_brush_sq_cu = dist_squared_to_line_segment_v3( brush_pos_cu, prev_pos_cu, pos_cu); if (dist_to_brush_sq_cu > brush_radius_sq_cu) { @@ -305,7 +294,7 @@ struct PuffOperationExecutor { const float3 &v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co; float3 bary_coords; interp_weights_tri_v3(bary_coords, v0_su, v1_su, v2_su, closest_pos_su); - const float3 normal_su = compute_surface_point_normal( + const float3 normal_su = geometry::compute_surface_point_normal( looptri, bary_coords, corner_normals_su_); const float3 normal_cu = math::normalize(transforms_.surface_to_curves_normal * normal_su); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc index 399d2c73ec3..cc5a5e7ae8a 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc @@ -161,14 +161,15 @@ struct SelectionPaintOperationExecutor { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); - Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { for (const int point_i : point_range) { - const float3 pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 pos_cu = brush_transform_inv * deformation.positions[point_i]; /* Find the position of the point in screen space. */ float2 pos_re; @@ -215,14 +216,15 @@ struct SelectionPaintOperationExecutor { void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) { - Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) { for (const int i : point_range) { - const float3 pos_old_cu = positions_cu[i]; + const float3 pos_old_cu = deformation.positions[i]; /* Compute distance to the brush. */ const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu); @@ -256,9 +258,11 @@ struct SelectionPaintOperationExecutor { void paint_curve_selection_projected(const float4x4 &brush_transform, MutableSpan<float> selection) { - const Span<float3> positions_cu = curves_->positions(); const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -274,8 +278,8 @@ struct SelectionPaintOperationExecutor { [&](const IndexRange segment_range, const float init) { float max_weight = init; for (const int segment_i : segment_range) { - const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i]; - const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1]; + const float3 pos1_cu = brush_transform_inv * deformation.positions[segment_i]; + const float3 pos2_cu = brush_transform_inv * deformation.positions[segment_i + 1]; float2 pos1_re; float2 pos2_re; @@ -323,7 +327,8 @@ struct SelectionPaintOperationExecutor { void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu) { - const Span<float3> positions_cu = curves_->positions(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); const float brush_radius_cu = self_->brush_3d_.radius_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -337,8 +342,8 @@ struct SelectionPaintOperationExecutor { [&](const IndexRange segment_range, const float init) { float max_weight = init; for (const int segment_i : segment_range) { - const float3 &pos1_cu = positions_cu[segment_i]; - const float3 &pos2_cu = positions_cu[segment_i + 1]; + const float3 &pos1_cu = deformation.positions[segment_i]; + const float3 &pos2_cu = deformation.positions[segment_i + 1]; const float distance_sq_cu = dist_squared_to_line_segment_v3( brush_cu, pos1_cu, pos2_cu); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc index aabe6fd93e4..443fbcb883c 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc @@ -4,6 +4,7 @@ #include "curves_sculpt_intern.hh" +#include "BLI_float3x3.hh" #include "BLI_float4x4.hh" #include "BLI_vector.hh" @@ -20,14 +21,16 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_mesh_sample.hh" +#include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" #include "DNA_curves_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -38,13 +41,27 @@ #include "WM_api.h" +#include "DEG_depsgraph_query.h" + +#include "GEO_add_curves_on_mesh.hh" +#include "GEO_reverse_uv_sampler.hh" + +#include "BLT_translation.h" + namespace blender::ed::sculpt_paint { +using geometry::ReverseUVSampler; + struct SlideCurveInfo { /** Index of the curve to slide. */ int curve_i; /** A weight based on the initial distance to the brush. */ float radius_falloff; + /** + * Normal of the surface where the curve was attached. This is used to rotate the curve if it is + * moved to a place with a different normal. + */ + float3 initial_normal_cu; }; struct SlideInfo { @@ -55,10 +72,13 @@ struct SlideInfo { class SlideOperation : public CurvesSculptStrokeOperation { private: - /** Last mouse position. */ - float2 brush_pos_last_re_; + float2 initial_brush_pos_re_; /** Information about which curves to slide. This is initialized when the brush starts. */ Vector<SlideInfo> slide_info_; + /** Positions of all curve points at the start of sliding. */ + Array<float3> initial_positions_cu_; + /** Deformed positions of all curve points at the start of sliding. */ + Array<float3> initial_deformed_positions_cu_; friend struct SlideOperationExecutor; @@ -80,27 +100,31 @@ struct SlideOperationExecutor { float brush_radius_factor_; float brush_strength_; - Object *object_ = nullptr; - Curves *curves_id_ = nullptr; - CurvesGeometry *curves_ = nullptr; + Object *curves_ob_orig_ = nullptr; + Curves *curves_id_orig_ = nullptr; + CurvesGeometry *curves_orig_ = nullptr; - Object *surface_ob_ = nullptr; - Mesh *surface_ = nullptr; - Span<MLoopTri> surface_looptris_; - VArraySpan<float2> surface_uv_map_; + Object *surface_ob_orig_ = nullptr; + Mesh *surface_orig_ = nullptr; + Span<MLoopTri> surface_looptris_orig_; + VArraySpan<float2> surface_uv_map_orig_; + Span<float3> corner_normals_orig_su_; + + Object *surface_ob_eval_ = nullptr; + Mesh *surface_eval_ = nullptr; + Span<MLoopTri> surface_looptris_eval_; + VArraySpan<float2> surface_uv_map_eval_; + BVHTreeFromMesh surface_bvh_eval_; VArray<float> curve_factors_; - VArray<float> point_factors_; Vector<int64_t> selected_curve_indices_; IndexMask curve_selection_; - float2 brush_pos_prev_re_; float2 brush_pos_re_; - float2 brush_pos_diff_re_; CurvesSurfaceTransforms transforms_; - BVHTreeFromMesh surface_bvh_; + std::atomic<bool> found_invalid_uv_mapping_{false}; SlideOperationExecutor(const bContext &C) : ctx_(C) { @@ -111,15 +135,21 @@ struct SlideOperationExecutor { UNUSED_VARS(C, stroke_extension); self_ = &self; - object_ = CTX_data_active_object(&C); - curves_id_ = static_cast<Curves *>(object_->data); - curves_ = &CurvesGeometry::wrap(curves_id_->geometry); - if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) { + curves_ob_orig_ = CTX_data_active_object(&C); + curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data); + curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry); + if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) { + report_missing_surface(stroke_extension.reports); + return; + } + if (curves_orig_->curves_num() == 0) { return; } - if (curves_->curves_num() == 0) { + if (curves_id_orig_->surface_uv_map == nullptr) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); return; } + const StringRefNull uv_map_name = curves_id_orig_->surface_uv_map; curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt; brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint); @@ -127,169 +157,312 @@ struct SlideOperationExecutor { brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension); brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension); - curve_factors_ = get_curves_selection(*curves_id_); - point_factors_ = get_point_selection(*curves_id_); - curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_); + curve_factors_ = get_curves_selection(*curves_id_orig_); + curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_); - brush_pos_prev_re_ = self_->brush_pos_last_re_; brush_pos_re_ = stroke_extension.mouse_position; - brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_; - BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = brush_pos_re_; }); - - transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface); - surface_ob_ = curves_id_->surface; - surface_ = static_cast<Mesh *>(surface_ob_->data); + transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface); - BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2); - BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); }); - - surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_), - BKE_mesh_runtime_looptri_len(surface_)}; + surface_ob_orig_ = curves_id_orig_->surface; + surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data); + surface_looptris_orig_ = {BKE_mesh_runtime_looptri_ensure(surface_orig_), + BKE_mesh_runtime_looptri_len(surface_orig_)}; + surface_uv_map_orig_ = + bke::mesh_attributes(*surface_orig_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); + if (surface_uv_map_orig_.is_empty()) { + report_missing_uv_map_on_original_surface(stroke_extension.reports); + return; + } + if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) { + BKE_mesh_calc_normals_split(surface_orig_); + } + corner_normals_orig_su_ = { + reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)), + surface_orig_->totloop}; - if (curves_id_->surface_uv_map != nullptr) { - const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_); - surface_uv_map_ = surface_attributes.lookup<float2>(curves_id_->surface_uv_map, - ATTR_DOMAIN_CORNER); + surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_); + if (surface_ob_eval_ == nullptr) { + return; + } + surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_); + if (surface_eval_ == nullptr) { + return; } + surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_), + BKE_mesh_runtime_looptri_len(surface_eval_)}; + surface_uv_map_eval_ = + bke::mesh_attributes(*surface_eval_).lookup<float2>(uv_map_name, ATTR_DOMAIN_CORNER); + if (surface_uv_map_eval_.is_empty()) { + report_missing_uv_map_on_evaluated_surface(stroke_extension.reports); + return; + } + BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); }); if (stroke_extension.is_first) { - const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms( - eCurvesSymmetryType(curves_id_->symmetry)); - for (const float4x4 &brush_transform : brush_transforms) { - this->detect_curves_to_slide(brush_transform); - } + self_->initial_brush_pos_re_ = brush_pos_re_; + /* Remember original and deformed positions of all points. Otherwise this information is lost + * when sliding starts, but it's still used. */ + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_); + self_->initial_positions_cu_ = curves_orig_->positions(); + self_->initial_deformed_positions_cu_ = deformation.positions; + + /* First find all curves to slide. When the mouse moves, only those curves will be moved. */ + this->find_curves_to_slide_with_symmetry(); return; } - this->slide_projected(); + this->slide_with_symmetry(); - curves_->tag_positions_changed(); - DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); + if (found_invalid_uv_mapping_) { + BKE_report( + stroke_extension.reports, RPT_WARNING, TIP_("UV map or surface attachment is invalid")); + } + + curves_orig_->tag_positions_changed(); + DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id); ED_region_tag_redraw(ctx_.region); } - void detect_curves_to_slide(const float4x4 &brush_transform) + void find_curves_to_slide_with_symmetry() { - const float4x4 brush_transform_inv = brush_transform.inverted(); - + const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms( + eCurvesSymmetryType(curves_id_orig_->symmetry)); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; - const float brush_radius_sq_re = pow2f(brush_radius_re); - - const Span<float3> positions_cu = curves_->positions(); - - float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph, + *ctx_.region, + *ctx_.v3d, + transforms_, + surface_bvh_eval_, + brush_pos_re_, + brush_radius_re); + if (!brush_3d.has_value()) { + return; + } + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_}; + for (const float4x4 &brush_transform : brush_transforms) { + self_->slide_info_.append_as(); + SlideInfo &slide_info = self_->slide_info_.last(); + slide_info.brush_transform = brush_transform; + this->find_curves_to_slide(brush_transform * brush_3d->position_cu, + brush_3d->radius_cu, + reverse_uv_sampler_orig, + slide_info.curves_to_slide); + } + } - self_->slide_info_.append({brush_transform}); - Vector<SlideCurveInfo> &curves_to_slide = self_->slide_info_.last().curves_to_slide; + void find_curves_to_slide(const float3 &brush_pos_cu, + const float brush_radius_cu, + const ReverseUVSampler &reverse_uv_sampler_orig, + Vector<SlideCurveInfo> &r_curves_to_slide) + { + const Span<float2> surface_uv_coords = curves_orig_->surface_uv_coords(); + const float brush_radius_sq_cu = pow2f(brush_radius_cu); - /* Find curves in brush radius that should be moved. */ + const Span<int> offsets = curves_orig_->offsets(); for (const int curve_i : curve_selection_) { - const int first_point_i = curves_->offsets()[curve_i]; - const float3 &first_pos_cu = brush_transform_inv * positions_cu[first_point_i]; - - float2 first_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, first_pos_re, projection.values); - - const float dist_to_brush_sq_re = math::distance_squared(first_pos_re, brush_pos_re_); - if (dist_to_brush_sq_re > brush_radius_sq_re) { + const int first_point_i = offsets[curve_i]; + const float3 old_pos_cu = self_->initial_deformed_positions_cu_[first_point_i]; + const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); + if (dist_to_brush_sq_cu > brush_radius_sq_cu) { + /* Root point is too far away from curve center. */ continue; } - const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re); + const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu); const float radius_falloff = BKE_brush_curve_strength( - brush_, dist_to_brush_re, brush_radius_re); - curves_to_slide.append({curve_i, radius_falloff}); + brush_, dist_to_brush_cu, brush_radius_cu); + + const float2 uv = surface_uv_coords[curve_i]; + ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + /* The curve does not have a valid surface attachment. */ + found_invalid_uv_mapping_.store(true); + continue; + } + /* Compute the normal at the initial surface position. */ + const float3 normal_cu = math::normalize( + transforms_.surface_to_curves_normal * + geometry::compute_surface_point_normal( + *result.looptri, result.bary_weights, corner_normals_orig_su_)); + + r_curves_to_slide.append({curve_i, radius_falloff, normal_cu}); } } - void slide_projected() + void slide_with_symmetry() { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); - - MutableSpan<float2> surface_uv_coords; - if (!surface_uv_map_.is_empty()) { - surface_uv_coords = curves_->surface_uv_coords_for_write(); + const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_}; + for (const SlideInfo &slide_info : self_->slide_info_) { + this->slide(slide_info.curves_to_slide, reverse_uv_sampler_orig, slide_info.brush_transform); } + } - float4x4 projection; - ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + void slide(const Span<SlideCurveInfo> slide_curves, + const ReverseUVSampler &reverse_uv_sampler_orig, + const float4x4 &brush_transform) + { + const float4x4 brush_transform_inv = brush_transform.inverted(); - for (const SlideInfo &slide_info : self_->slide_info_) { - const float4x4 &brush_transform = slide_info.brush_transform; - const float4x4 brush_transform_inv = brush_transform.inverted(); - const Span<SlideCurveInfo> curves_to_slide = slide_info.curves_to_slide; - - threading::parallel_for(curves_to_slide.index_range(), 256, [&](const IndexRange range) { - for (const SlideCurveInfo &curve_slide_info : curves_to_slide.slice(range)) { - const int curve_i = curve_slide_info.curve_i; - const IndexRange points = curves_->points_for_curve(curve_i); - const int first_point_i = points.first(); - const float3 old_first_pos_cu = brush_transform_inv * positions_cu[first_point_i]; - - float2 old_first_pos_re; - ED_view3d_project_float_v2_m4( - ctx_.region, old_first_pos_cu, old_first_pos_re, projection.values); - const float first_point_weight = brush_strength_ * curve_slide_info.radius_falloff; - - /* Slide root position in region space and then project it back onto the surface. */ - const float2 new_first_pos_re = old_first_pos_re + - first_point_weight * brush_pos_diff_re_; - - float3 ray_start_wo, ray_end_wo; - ED_view3d_win_to_segment_clipped(ctx_.depsgraph, - ctx_.region, - ctx_.v3d, - new_first_pos_re, - ray_start_wo, - ray_end_wo, - true); - const float3 ray_start_su = transforms_.world_to_surface * ray_start_wo; - const float3 ray_end_su = transforms_.world_to_surface * ray_end_wo; - - const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); - BVHTreeRayHit hit; - hit.dist = FLT_MAX; - hit.index = -1; - BLI_bvhtree_ray_cast(surface_bvh_.tree, - ray_start_su, - ray_direction_su, - 0.0f, - &hit, - surface_bvh_.raycast_callback, - &surface_bvh_); - if (hit.index == -1) { - continue; - } + const Span<MVert> verts_orig_su{surface_orig_->mvert, surface_orig_->totvert}; + const Span<MLoop> loops_orig{surface_orig_->mloop, surface_orig_->totloop}; - const int looptri_index = hit.index; - const float3 attached_pos_su = hit.co; + MutableSpan<float3> positions_orig_cu = curves_orig_->positions_for_write(); + MutableSpan<float2> surface_uv_coords = curves_orig_->surface_uv_coords_for_write(); - const float3 attached_pos_cu = transforms_.surface_to_curves * attached_pos_su; - const float3 pos_offset_cu = brush_transform * (attached_pos_cu - old_first_pos_cu); + float4x4 projection; + ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values); + + const float2 brush_pos_diff_re = brush_pos_re_ - self_->initial_brush_pos_re_; + + /* The brush transformation has to be applied in curves space. */ + const float4x4 world_to_surface_with_symmetry_mat = transforms_.curves_to_surface * + brush_transform * + transforms_.world_to_curves; + + threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) { + for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) { + const int curve_i = slide_curve_info.curve_i; + const IndexRange points = curves_orig_->points_for_curve(curve_i); + const int first_point_i = points[0]; + + const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i]; + const float3 old_first_symm_pos_eval_cu = brush_transform_inv * old_first_pos_eval_cu; + const float3 old_first_pos_eval_su = transforms_.curves_to_surface * old_first_pos_eval_cu; + + float2 old_first_symm_pos_eval_re; + ED_view3d_project_float_v2_m4(ctx_.region, + old_first_symm_pos_eval_cu, + old_first_symm_pos_eval_re, + projection.values); + + const float radius_falloff = slide_curve_info.radius_falloff; + const float curve_weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; + const float2 new_first_symm_pos_eval_re = old_first_symm_pos_eval_re + + curve_weight * brush_pos_diff_re; + + /* Compute the ray that will be used to find the new position on the surface. */ + float3 ray_start_wo, ray_end_wo; + ED_view3d_win_to_segment_clipped(ctx_.depsgraph, + ctx_.region, + ctx_.v3d, + new_first_symm_pos_eval_re, + ray_start_wo, + ray_end_wo, + true); + const float3 ray_start_su = world_to_surface_with_symmetry_mat * ray_start_wo; + const float3 ray_end_su = world_to_surface_with_symmetry_mat * ray_end_wo; + const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su); + + /* Find the ray hit that is closest to the initial curve root position. */ + int looptri_index_eval; + float3 hit_pos_eval_su; + if (!this->find_closest_ray_hit(ray_start_su, + ray_direction_su, + old_first_pos_eval_su, + looptri_index_eval, + hit_pos_eval_su)) { + continue; + } - /* Update positions. The first point doesn't have an additional weight here, because then - * it wouldn't be attached to the surface anymore. */ - positions_cu[first_point_i] += pos_offset_cu; - for (const int point_i : points.drop_front(1)) { - const float weight = point_factors_[point_i]; - positions_cu[point_i] += weight * pos_offset_cu; - } + /* Compute the uv of the new surface position on the evaluated mesh. */ + const MLoopTri &looptri_eval = surface_looptris_eval_[looptri_index_eval]; + const float3 bary_weights_eval = bke::mesh_surface_sample::compute_bary_coord_in_triangle( + *surface_eval_, looptri_eval, hit_pos_eval_su); + const float2 uv = attribute_math::mix3(bary_weights_eval, + surface_uv_map_eval_[looptri_eval.tri[0]], + surface_uv_map_eval_[looptri_eval.tri[1]], + surface_uv_map_eval_[looptri_eval.tri[2]]); + + /* Try to find the same uv on the original surface. */ + const ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + found_invalid_uv_mapping_.store(true); + continue; + } + const MLoopTri &looptri_orig = *result.looptri; + const float3 &bary_weights_orig = result.bary_weights; + + /* Gather old and new surface normal. */ + const float3 &initial_normal_cu = slide_curve_info.initial_normal_cu; + const float3 new_normal_cu = math::normalize( + transforms_.surface_to_curves_normal * + geometry::compute_surface_point_normal( + *result.looptri, result.bary_weights, corner_normals_orig_su_)); + + /* Gather old and new surface position. */ + const float3 old_first_pos_orig_cu = self_->initial_positions_cu_[first_point_i]; + const float3 new_first_pos_orig_cu = + transforms_.surface_to_curves * + attribute_math::mix3<float3>(bary_weights_orig, + verts_orig_su[loops_orig[looptri_orig.tri[0]].v].co, + verts_orig_su[loops_orig[looptri_orig.tri[1]].v].co, + verts_orig_su[loops_orig[looptri_orig.tri[2]].v].co); + + /* Actually transform curve points. */ + const float4x4 slide_transform = this->get_slide_transform( + old_first_pos_orig_cu, new_first_pos_orig_cu, initial_normal_cu, new_normal_cu); + for (const int point_i : points) { + positions_orig_cu[point_i] = slide_transform * self_->initial_positions_cu_[point_i]; + } + surface_uv_coords[curve_i] = uv; + } + }); + } - /* Update surface attachment information if necessary. */ - if (!surface_uv_map_.is_empty()) { - const MLoopTri &looptri = surface_looptris_[looptri_index]; - const float3 bary_coord = bke::mesh_surface_sample::compute_bary_coord_in_triangle( - *surface_, looptri, attached_pos_su); - const float2 &uv0 = surface_uv_map_[looptri.tri[0]]; - const float2 &uv1 = surface_uv_map_[looptri.tri[1]]; - const float2 &uv2 = surface_uv_map_[looptri.tri[2]]; - const float2 uv = attribute_math::mix3(bary_coord, uv0, uv1, uv2); - surface_uv_coords[curve_i] = uv; + bool find_closest_ray_hit(const float3 &ray_start_su, + const float3 &ray_direction_su, + const float3 &point_su, + int &r_looptri_index, + float3 &r_hit_pos) + { + float best_dist_sq_su = FLT_MAX; + int best_looptri_index_eval; + float3 best_hit_pos_su; + BLI_bvhtree_ray_cast_all_cpp( + *surface_bvh_eval_.tree, + ray_start_su, + ray_direction_su, + 0.0f, + FLT_MAX, + [&](const int looptri_index, const BVHTreeRay &ray, BVHTreeRayHit &hit) { + surface_bvh_eval_.raycast_callback(&surface_bvh_eval_, looptri_index, &ray, &hit); + if (hit.index < 0) { + return; } - } - }); + const float3 &hit_pos_su = hit.co; + const float dist_sq_su = math::distance_squared(hit_pos_su, point_su); + if (dist_sq_su < best_dist_sq_su) { + best_dist_sq_su = dist_sq_su; + best_hit_pos_su = hit_pos_su; + best_looptri_index_eval = hit.index; + } + }); + + if (best_dist_sq_su == FLT_MAX) { + return false; } + r_looptri_index = best_looptri_index_eval; + r_hit_pos = best_hit_pos_su; + return true; + } + + float4x4 get_slide_transform(const float3 &old_root_pos, + const float3 &new_root_pos, + const float3 &old_normal, + const float3 &new_normal) + { + float3x3 rotation_3x3; + rotation_between_vecs_to_mat3(rotation_3x3.values, old_normal, new_normal); + float4x4 rotation_4x4; + copy_m4_m3(rotation_4x4.values, rotation_3x3.values); + + float4x4 transform = float4x4::identity(); + sub_v3_v3(transform.values[3], old_root_pos); + mul_m4_m4_pre(transform.values, rotation_4x4.values); + add_v3_v3(transform.values[3], new_root_pos); + return transform; } }; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc index f874a9fc255..37a7f1c83ed 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc @@ -2,6 +2,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_crazyspace.hh" #include "ED_screen.h" #include "ED_view3d.h" @@ -95,60 +96,56 @@ struct SmoothOperationExecutor { } } + Array<float> point_smooth_factors(curves_->points_num(), 0.0f); + if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - this->smooth_projected_with_symmetry(); + this->find_projected_smooth_factors_with_symmetry(point_smooth_factors); } else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - this->smooth_spherical_with_symmetry(); + this->find_spherical_smooth_factors_with_symmetry(point_smooth_factors); } else { BLI_assert_unreachable(); } + this->smooth(point_smooth_factors); curves_->tag_positions_changed(); DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id); ED_region_tag_redraw(ctx_.region); } - void smooth_projected_with_symmetry() + void find_projected_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors) { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->smooth_projected(brush_transform); + this->find_projected_smooth_factors(brush_transform, r_point_smooth_factors); } } - void smooth_projected(const float4x4 &brush_transform) + void find_projected_smooth_factors(const float4x4 &brush_transform, + MutableSpan<float> r_point_smooth_factors) { const float4x4 brush_transform_inv = brush_transform.inverted(); - MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_; const float brush_radius_sq_re = pow2f(brush_radius_re); float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { - Vector<float2> old_curve_positions_re; for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); - - /* Find position of control points in screen space. */ - old_curve_positions_re.clear(); - old_curve_positions_re.reserve(points.size()); for (const int point_i : points) { - const float3 &pos_cu = brush_transform_inv * positions_cu[point_i]; + const float3 &pos_cu = brush_transform_inv * deformation.positions[point_i]; float2 pos_re; ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values); - old_curve_positions_re.append_unchecked(pos_re); - } - for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { - const int point_i = points[i]; - const float2 &old_pos_re = old_curve_positions_re[i]; - const float dist_to_brush_sq_re = math::distance_squared(old_pos_re, brush_pos_re_); + const float dist_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_); if (dist_to_brush_sq_re > brush_radius_sq_re) { continue; } @@ -161,27 +158,13 @@ struct SmoothOperationExecutor { const float weight_factor = 0.1f; const float weight = weight_factor * brush_strength_ * radius_falloff * point_factors_[point_i]; - - /* Move points towards the middle of their neighbors. */ - const float2 &old_pos_prev_re = old_curve_positions_re[i - 1]; - const float2 &old_pos_next_re = old_curve_positions_re[i + 1]; - const float2 goal_pos_re = math::midpoint(old_pos_prev_re, old_pos_next_re); - const float2 new_pos_re = math::interpolate(old_pos_re, goal_pos_re, weight); - const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i]; - float3 new_pos_wo; - ED_view3d_win_to_3d(ctx_.v3d, - ctx_.region, - transforms_.curves_to_world * old_pos_cu, - new_pos_re, - new_pos_wo); - const float3 new_pos_cu = brush_transform * (transforms_.world_to_curves * new_pos_wo); - positions_cu[point_i] = new_pos_cu; + math::max_inplace(r_point_smooth_factors[point_i], weight); } } }); } - void smooth_spherical_with_symmetry() + void find_spherical_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors) { float4x4 projection; ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values); @@ -198,27 +181,25 @@ struct SmoothOperationExecutor { const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms( eCurvesSymmetryType(curves_id_->symmetry)); for (const float4x4 &brush_transform : symmetry_brush_transforms) { - this->smooth_spherical(brush_transform * brush_pos_cu, brush_radius_cu); + this->find_spherical_smooth_factors( + brush_transform * brush_pos_cu, brush_radius_cu, r_point_smooth_factors); } } - void smooth_spherical(const float3 &brush_pos_cu, const float brush_radius_cu) + void find_spherical_smooth_factors(const float3 &brush_pos_cu, + const float brush_radius_cu, + MutableSpan<float> r_point_smooth_factors) { - MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float brush_radius_sq_cu = pow2f(brush_radius_cu); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { - Vector<float3> old_curve_positions_cu; for (const int curve_i : curve_selection_.slice(range)) { const IndexRange points = curves_->points_for_curve(curve_i); - /* Remember original positions so that we don't smooth based on already smoothed points - * below. */ - old_curve_positions_cu.clear(); - old_curve_positions_cu.extend(positions_cu.slice(points)); - for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { - const int point_i = points[i]; - const float3 &old_pos_cu = old_curve_positions_cu[i]; - const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu); + for (const int point_i : points) { + const float3 &pos_cu = deformation.positions[point_i]; + const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu); if (dist_to_brush_sq_cu > brush_radius_sq_cu) { continue; } @@ -231,13 +212,34 @@ struct SmoothOperationExecutor { const float weight_factor = 0.1f; const float weight = weight_factor * brush_strength_ * radius_falloff * point_factors_[point_i]; + math::max_inplace(r_point_smooth_factors[point_i], weight); + } + } + }); + } - /* Move points towards the middle of their neighbors. */ - const float3 &old_pos_prev_cu = old_curve_positions_cu[i - 1]; - const float3 &old_pos_next_cu = old_curve_positions_cu[i + 1]; - const float3 goal_pos_cu = math::midpoint(old_pos_prev_cu, old_pos_next_cu); - const float3 new_pos_cu = math::interpolate(old_pos_cu, goal_pos_cu, weight); - positions_cu[point_i] = new_pos_cu; + void smooth(const Span<float> point_smooth_factors) + { + MutableSpan<float3> positions = curves_->positions_for_write(); + threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) { + Vector<float3> old_positions; + for (const int curve_i : curve_selection_.slice(range)) { + const IndexRange points = curves_->points_for_curve(curve_i); + old_positions.clear(); + old_positions.extend(positions.slice(points)); + for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) { + const int point_i = points[i]; + const float smooth_factor = point_smooth_factors[point_i]; + if (smooth_factor == 0.0f) { + continue; + } + /* Move towards the middle of the neighboring points. */ + const float3 old_pos = old_positions[i]; + const float3 &prev_pos = old_positions[i - 1]; + const float3 &next_pos = old_positions[i + 1]; + const float3 goal_pos = math::midpoint(prev_pos, next_pos); + const float3 new_pos = math::interpolate(old_pos, goal_pos, smooth_factor); + positions[point_i] = new_pos; } } }); diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc index ec0e8ff45e5..54b81fa221d 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc @@ -176,6 +176,8 @@ struct SnakeHookOperatorExecutor { void projected_snake_hook(const float4x4 &brush_transform) { const float4x4 brush_transform_inv = brush_transform.inverted(); + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); MutableSpan<float3> positions_cu = curves_->positions_for_write(); @@ -189,12 +191,14 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i]; + const float3 old_pos_cu = deformation.positions[last_point_i]; + const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu; - float2 old_pos_re; - ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values); + float2 old_symm_pos_re; + ED_view3d_project_float_v2_m4( + ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values); - const float distance_to_brush_sq_re = math::distance_squared(old_pos_re, + const float distance_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_prev_re_); if (distance_to_brush_sq_re > brush_radius_sq_re) { continue; @@ -204,17 +208,21 @@ struct SnakeHookOperatorExecutor { brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re); const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; - const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight; - float3 new_position_wo; + const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight; + float3 new_symm_pos_wo; ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, - transforms_.curves_to_world * old_pos_cu, - new_position_re, - new_position_wo); - const float3 new_position_cu = brush_transform * - (transforms_.world_to_curves * new_position_wo); - - move_last_point_and_resample(positions_cu.slice(points), new_position_cu); + transforms_.curves_to_world * old_symm_pos_cu, + new_symm_pos_re, + new_symm_pos_wo); + const float3 new_pos_cu = brush_transform * + (transforms_.world_to_curves * new_symm_pos_wo); + const float3 translation_eval = new_pos_cu - old_pos_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + last_point_i, translation_eval); + + move_last_point_and_resample(positions_cu.slice(points), + positions_cu[last_point_i] + translation_orig); } }); } @@ -252,6 +260,9 @@ struct SnakeHookOperatorExecutor { const float3 &brush_end_cu, const float brush_radius_cu) { + const bke::crazyspace::GeometryDeformation deformation = + bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_); + MutableSpan<float3> positions_cu = curves_->positions_for_write(); const float3 brush_diff_cu = brush_end_cu - brush_start_cu; const float brush_radius_sq_cu = pow2f(brush_radius_cu); @@ -260,7 +271,7 @@ struct SnakeHookOperatorExecutor { for (const int curve_i : curves_range) { const IndexRange points = curves_->points_for_curve(curve_i); const int last_point_i = points.last(); - const float3 old_pos_cu = positions_cu[last_point_i]; + const float3 old_pos_cu = deformation.positions[last_point_i]; const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3( old_pos_cu, brush_start_cu, brush_end_cu); @@ -274,9 +285,12 @@ struct SnakeHookOperatorExecutor { brush_, distance_to_brush_cu, brush_radius_cu); const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i]; - const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu; + const float3 translation_eval = weight * brush_diff_cu; + const float3 translation_orig = deformation.translation_from_deformed_to_original( + last_point_i, translation_eval); - move_last_point_and_resample(positions_cu.slice(points), new_pos_cu); + move_last_point_and_resample(positions_cu.slice(points), + positions_cu[last_point_i] + translation_orig); } }); } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b879219e39c..9b8009cc7c0 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -937,6 +937,19 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo ss << TIP_("\u2022 Volume") << line_end; break; } + case GEO_COMPONENT_TYPE_EDIT: { + if (value_log.edit_data_info.has_value()) { + const geo_log::GeometryValueLog::EditDataInfo &edit_info = *value_log.edit_data_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Edit Curves: %s, %s"), + edit_info.has_deformed_positions ? TIP_("positions") : TIP_("no positions"), + edit_info.has_deform_matrices ? TIP_("matrices") : TIP_("no matrices")); + ss << line << line_end; + } + break; + } } } @@ -975,6 +988,9 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo ss << TIP_("Volume"); break; } + case GEO_COMPONENT_TYPE_EDIT: { + break; + } } ss << ((type == supported_types.last()) ? "" : ", "); } diff --git a/source/blender/geometry/GEO_add_curves_on_mesh.hh b/source/blender/geometry/GEO_add_curves_on_mesh.hh index cf60a8e8ace..68c8dc5b76b 100644 --- a/source/blender/geometry/GEO_add_curves_on_mesh.hh +++ b/source/blender/geometry/GEO_add_curves_on_mesh.hh @@ -13,13 +13,13 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "GEO_reverse_uv_sampler.hh" + namespace blender::geometry { struct AddCurvesOnMeshInputs { - /** Information about the root points where new curves should be generated. */ - Span<float3> root_positions_cu; - Span<float3> bary_coords; - Span<int> looptri_indices; + /** UV Coordinates at which the new curves should be added. */ + Span<float2> uvs; /** Determines shape of new curves. */ bool interpolate_length = false; @@ -30,14 +30,10 @@ struct AddCurvesOnMeshInputs { /** Information about the surface that the new curves are attached to. */ const Mesh *surface = nullptr; - BVHTreeFromMesh *surface_bvh = nullptr; - Span<MLoopTri> surface_looptris; - Span<float2> surface_uv_map; + const ReverseUVSampler *reverse_uv_sampler = nullptr; Span<float3> corner_normals_su; - /** Transformation matrices. */ - float4x4 curves_to_surface_mat; - float4x4 surface_to_curves_normal_mat; + bke::CurvesSurfaceTransforms *transforms = nullptr; /** * KD-Tree that contains the root points of existing curves. This is only necessary when @@ -51,4 +47,8 @@ struct AddCurvesOnMeshInputs { */ void add_curves_on_mesh(bke::CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs); +float3 compute_surface_point_normal(const MLoopTri &looptri, + const float3 &bary_coord, + const Span<float3> corner_normals); + } // namespace blender::geometry diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index e54e2bdd3b0..aa04cedb5c5 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -6,6 +6,7 @@ #include "BKE_mesh_sample.hh" #include "GEO_add_curves_on_mesh.hh" +#include "GEO_reverse_uv_sampler.hh" /** * The code below uses a suffix naming convention to indicate the coordinate space: @@ -27,9 +28,9 @@ struct NeighborCurve { static constexpr int max_neighbors = 5; using NeighborCurves = Vector<NeighborCurve, max_neighbors>; -static float3 compute_surface_point_normal(const MLoopTri &looptri, - const float3 &bary_coord, - const Span<float3> corner_normals) +float3 compute_surface_point_normal(const MLoopTri &looptri, + const float3 &bary_coord, + const Span<float3> corner_normals) { const int l0 = looptri.tri[0]; const int l1 = looptri.tri[1]; @@ -136,16 +137,15 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const int old_curves_num, const Span<float> new_lengths_cu, const Span<float3> new_normals_su, - const float4x4 &surface_to_curves_normal_mat, - const float4x4 &curves_to_surface_mat, - const BVHTreeFromMesh &surface_bvh, - const Span<MLoopTri> surface_looptris, - const Mesh &surface, + const bke::CurvesSurfaceTransforms &transforms, + const ReverseUVSampler &reverse_uv_sampler, const Span<float3> corner_normals_su) { MutableSpan<float3> positions_cu = curves.positions_for_write(); const int added_curves_num = root_positions_cu.size(); + const Span<float2> uv_coords = curves.surface_uv_coords(); + threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { for (const int added_curve_i : range) { const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i]; @@ -154,7 +154,7 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, const float length_cu = new_lengths_cu[added_curve_i]; const float3 &normal_su = new_normals_su[added_curve_i]; - const float3 normal_cu = math::normalize(surface_to_curves_normal_mat * normal_su); + const float3 normal_cu = math::normalize(transforms.surface_to_curves_normal * normal_su); const float3 &root_cu = root_positions_cu[added_curve_i]; @@ -169,26 +169,15 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves, for (const NeighborCurve &neighbor : neighbors) { const int neighbor_curve_i = neighbor.index; - const float3 &neighbor_first_pos_cu = positions_cu[curves.offsets()[neighbor_curve_i]]; - const float3 neighbor_first_pos_su = curves_to_surface_mat * neighbor_first_pos_cu; - - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest(surface_bvh.tree, - neighbor_first_pos_su, - &nearest, - surface_bvh.nearest_callback, - const_cast<BVHTreeFromMesh *>(&surface_bvh)); - const int neighbor_looptri_index = nearest.index; - const MLoopTri &neighbor_looptri = surface_looptris[neighbor_looptri_index]; - - const float3 neighbor_bary_coord = - bke::mesh_surface_sample::compute_bary_coord_in_triangle( - surface, neighbor_looptri, nearest.co); + const float2 neighbor_uv = uv_coords[neighbor_curve_i]; + const ReverseUVSampler::Result result = reverse_uv_sampler.sample(neighbor_uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + continue; + } const float3 neighbor_normal_su = compute_surface_point_normal( - surface_looptris[neighbor_looptri_index], neighbor_bary_coord, corner_normals_su); - const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat * + *result.looptri, result.bary_weights, corner_normals_su); + const float3 neighbor_normal_cu = math::normalize(transforms.surface_to_curves_normal * neighbor_normal_su); /* The rotation matrix used to transform relative coordinates of the neighbor curve @@ -245,13 +234,37 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp const bool use_interpolation = inputs.interpolate_length || inputs.interpolate_point_count || inputs.interpolate_shape; + Vector<float3> root_positions_cu; + Vector<float3> bary_coords; + Vector<const MLoopTri *> looptris; + Vector<float2> used_uvs; + + /* Find faces that the passed in uvs belong to. */ + for (const int i : inputs.uvs.index_range()) { + const float2 &uv = inputs.uvs[i]; + const ReverseUVSampler::Result result = inputs.reverse_uv_sampler->sample(uv); + if (result.type != ReverseUVSampler::ResultType::Ok) { + continue; + } + const MLoopTri &looptri = *result.looptri; + bary_coords.append(result.bary_weights); + looptris.append(&looptri); + const float3 root_position_su = attribute_math::mix3<float3>( + result.bary_weights, + inputs.surface->mvert[inputs.surface->mloop[looptri.tri[0]].v].co, + inputs.surface->mvert[inputs.surface->mloop[looptri.tri[1]].v].co, + inputs.surface->mvert[inputs.surface->mloop[looptri.tri[2]].v].co); + root_positions_cu.append(inputs.transforms->surface_to_curves * root_position_su); + used_uvs.append(uv); + } + Array<NeighborCurves> neighbors_per_curve; if (use_interpolation) { BLI_assert(inputs.old_roots_kdtree != nullptr); - neighbors_per_curve = find_curve_neighbors(inputs.root_positions_cu, *inputs.old_roots_kdtree); + neighbors_per_curve = find_curve_neighbors(root_positions_cu, *inputs.old_roots_kdtree); } - const int added_curves_num = inputs.root_positions_cu.size(); + const int added_curves_num = root_positions_cu.size(); const int old_points_num = curves.points_num(); const int old_curves_num = curves.curves_num(); const int new_curves_num = old_curves_num + added_curves_num; @@ -280,6 +293,10 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp curves.resize(new_points_num, new_curves_num); MutableSpan<float3> positions_cu = curves.positions_for_write(); + /* Initialize attachment information. */ + MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); + surface_uv_coords.take_back(added_curves_num).copy_from(used_uvs); + /* Determine length of new curves. */ Array<float> new_lengths_cu(added_curves_num); if (inputs.interpolate_length) { @@ -306,25 +323,11 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp Array<float3> new_normals_su(added_curves_num); threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) { for (const int i : range) { - const int looptri_index = inputs.looptri_indices[i]; - const float3 &bary_coord = inputs.bary_coords[i]; new_normals_su[i] = compute_surface_point_normal( - inputs.surface_looptris[looptri_index], bary_coord, inputs.corner_normals_su); + *looptris[i], bary_coords[i], inputs.corner_normals_su); } }); - /* Propagate attachment information. */ - if (!inputs.surface_uv_map.is_empty()) { - MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write(); - bke::mesh_surface_sample::sample_corner_attribute( - *inputs.surface, - inputs.looptri_indices, - inputs.bary_coords, - GVArray::ForSpan(inputs.surface_uv_map), - IndexRange(added_curves_num), - surface_uv_coords.take_back(added_curves_num)); - } - /* Update selection arrays when available. */ const VArray<float> points_selection = curves.selection_point_float(); if (points_selection.is_span()) { @@ -340,25 +343,22 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp /* Initialize position attribute. */ if (inputs.interpolate_shape) { interpolate_position_with_interpolation(curves, - inputs.root_positions_cu, + root_positions_cu, neighbors_per_curve, old_curves_num, new_lengths_cu, new_normals_su, - inputs.surface_to_curves_normal_mat, - inputs.curves_to_surface_mat, - *inputs.surface_bvh, - inputs.surface_looptris, - *inputs.surface, + *inputs.transforms, + *inputs.reverse_uv_sampler, inputs.corner_normals_su); } else { interpolate_position_without_interpolation(curves, old_curves_num, - inputs.root_positions_cu, + root_positions_cu, new_lengths_cu, new_normals_su, - inputs.surface_to_curves_normal_mat); + inputs.transforms->surface_to_curves_normal); } /* Set curve types. */ diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 0544f304283..4b3b184536b 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -204,7 +204,8 @@ struct GatherTasks { /* Volumes only have very simple support currently. Only the first found volume is put into the * output. */ - UserCounter<VolumeComponent> first_volume; + UserCounter<const VolumeComponent> first_volume; + UserCounter<const GeometryComponentEditData> first_edit_data; }; /** Current offsets while during the gather operation. */ @@ -582,7 +583,16 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, const VolumeComponent *volume_component = static_cast<const VolumeComponent *>(component); if (!gather_info.r_tasks.first_volume) { volume_component->user_add(); - gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component); + gather_info.r_tasks.first_volume = volume_component; + } + break; + } + case GEO_COMPONENT_TYPE_EDIT: { + const GeometryComponentEditData *edit_component = + static_cast<const GeometryComponentEditData *>(component); + if (!gather_info.r_tasks.first_edit_data) { + edit_component->user_add(); + gather_info.r_tasks.first_edit_data = edit_component; } break; } @@ -1405,6 +1415,9 @@ GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOp if (gather_info.r_tasks.first_volume) { new_geometry_set.add(*gather_info.r_tasks.first_volume); } + if (gather_info.r_tasks.first_edit_data) { + new_geometry_set.add(*gather_info.r_tasks.first_edit_data); + } return new_geometry_set; } diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 0d5ba6cf5db..c5bc42b059d 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -163,6 +163,7 @@ class GeoNodeExecParams { } void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const; + void check_output_geometry_set(const GeometrySet &geometry_set) const; /** * Get input as vector for multi input socket with the given identifier. @@ -231,6 +232,9 @@ class GeoNodeExecParams { #ifdef DEBUG this->check_output_access(identifier, type); #endif + if constexpr (std::is_same_v<StoredT, GeometrySet>) { + this->check_output_geometry_set(value); + } GMutablePointer gvalue = provider_->alloc_output_value(type); new (gvalue.get()) StoredT(std::forward<T>(value)); provider_->set_output(identifier, gvalue); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 05c97c3903d..46ba72d14d8 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -115,11 +115,16 @@ class GeometryValueLog : public ValueLog { struct InstancesInfo { int instances_num; }; + struct EditDataInfo { + bool has_deformed_positions; + bool has_deform_matrices; + }; std::optional<MeshInfo> mesh_info; std::optional<CurveInfo> curve_info; std::optional<PointCloudInfo> pointcloud_info; std::optional<InstancesInfo> instances_info; + std::optional<EditDataInfo> edit_data_info; GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index 00b10cc8a2f..75c198f7bdd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -50,7 +50,7 @@ static void node_geo_exec(GeoNodeExecParams params) } if (sub_min == float3(FLT_MAX)) { - sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + sub_geometry.remove_geometry_during_modify(); } else { const float3 scale = sub_max - sub_min; @@ -58,7 +58,7 @@ static void node_geo_exec(GeoNodeExecParams params) Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map"); transform_mesh(*mesh, center, float3(0), float3(1)); sub_geometry.replace_mesh(mesh); - sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + sub_geometry.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 7c26ffc2099..03892501c89 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -224,7 +224,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { Mesh *mesh = compute_hull(geometry_set); geometry_set.replace_mesh(mesh); - geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); }); params.set_output("Convex Hull", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 2815dd5b2e8..37fc6823b9a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -60,6 +60,8 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: { Field<int> count = params.extract_input<Field<int>>("Count"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 903a5e7c1d7..bc319ce905a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -28,9 +28,10 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const bool fill_caps) { const Curves &curves = *geometry_set.get_curves_for_read(); - const Curves *profile_curves = profile_set.get_curves_for_read(); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + if (profile_curves == nullptr) { Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry)); geometry_set.replace_mesh(mesh); @@ -55,7 +56,7 @@ static void node_geo_exec(GeoNodeExecParams params) has_curves = true; geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } - geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); }); params.set_output("Mesh", std::move(curve_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 6c4fb2e0855..fd75855bddb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -316,9 +316,11 @@ static void node_geo_exec(GeoNodeExecParams params) attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (!geometry_set.has_curves()) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( @@ -329,7 +331,7 @@ static void node_geo_exec(GeoNodeExecParams params) const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_num = offsets.last(); if (total_num == 0) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } @@ -355,7 +357,7 @@ static void node_geo_exec(GeoNodeExecParams params) point_attributes.tangents, point_attributes.normals, point_attributes.rotations); } - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); }); params.set_output("Points", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 51994cb8a41..0932de237a9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -579,6 +579,7 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { Field<float> start_field = params.extract_input<Field<float>>("Start"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index bd08abbd070..8b653296e12 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -41,7 +41,7 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curves")); } -static void deform_curves(CurvesGeometry &curves, +static void deform_curves(const CurvesGeometry &curves, const Mesh &surface_mesh_old, const Mesh &surface_mesh_new, const Span<float2> curve_attachment_uvs, @@ -51,6 +51,8 @@ static void deform_curves(CurvesGeometry &curves, const Span<float3> corner_normals_new, const Span<float3> rest_positions, const float4x4 &surface_to_curves, + MutableSpan<float3> r_positions, + MutableSpan<float3x3> r_rotations, std::atomic<int> &r_invalid_uv_count) { /* Find attachment points on old and new mesh. */ @@ -61,8 +63,6 @@ static void deform_curves(CurvesGeometry &curves, [&]() { reverse_uv_sampler_old.sample_many(curve_attachment_uvs, surface_samples_old); }, [&]() { reverse_uv_sampler_new.sample_many(curve_attachment_uvs, surface_samples_new); }); - MutableSpan<float3> positions = curves.positions_for_write(); - const float4x4 curves_to_surface = surface_to_curves.inverted(); threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { @@ -190,9 +190,15 @@ static void deform_curves(CurvesGeometry &curves, /* Actually transform all points. */ const IndexRange points = curves.points_for_curve(curve_i); for (const int point_i : points) { - const float3 old_point_pos = positions[point_i]; + const float3 old_point_pos = r_positions[point_i]; const float3 new_point_pos = curve_transform * old_point_pos; - positions[point_i] = new_point_pos; + r_positions[point_i] = new_point_pos; + } + + if (!r_rotations.is_empty()) { + for (const int point_i : points) { + r_rotations[point_i] = rotation * r_rotations[point_i]; + } } } }); @@ -320,17 +326,67 @@ static void node_geo_exec(GeoNodeExecParams params) const bke::CurvesSurfaceTransforms transforms{*self_ob_eval, surface_ob_eval}; - deform_curves(curves, - *surface_mesh_orig, - *surface_mesh_eval, - surface_uv_coords, - reverse_uv_sampler_orig, - reverse_uv_sampler_eval, - corner_normals_orig, - corner_normals_eval, - rest_positions, - transforms.surface_to_curves, - invalid_uv_count); + bke::CurvesEditHints *edit_hints = curves_geometry.get_curve_edit_hints_for_write(); + MutableSpan<float3> edit_hint_positions; + MutableSpan<float3x3> edit_hint_rotations; + if (edit_hints != nullptr) { + if (edit_hints->positions.has_value()) { + edit_hint_positions = *edit_hints->positions; + } + if (!edit_hints->deform_mats.has_value()) { + edit_hints->deform_mats.emplace(edit_hints->curves_id_orig.geometry.point_num, + float3x3::identity()); + edit_hints->deform_mats->fill(float3x3::identity()); + } + edit_hint_rotations = *edit_hints->deform_mats; + } + + if (edit_hint_positions.is_empty()) { + deform_curves(curves, + *surface_mesh_orig, + *surface_mesh_eval, + surface_uv_coords, + reverse_uv_sampler_orig, + reverse_uv_sampler_eval, + corner_normals_orig, + corner_normals_eval, + rest_positions, + transforms.surface_to_curves, + curves.positions_for_write(), + edit_hint_rotations, + invalid_uv_count); + } + else { + /* First deform the actual curves in the input geometry. */ + deform_curves(curves, + *surface_mesh_orig, + *surface_mesh_eval, + surface_uv_coords, + reverse_uv_sampler_orig, + reverse_uv_sampler_eval, + corner_normals_orig, + corner_normals_eval, + rest_positions, + transforms.surface_to_curves, + curves.positions_for_write(), + {}, + invalid_uv_count); + /* Then also deform edit curve information for use in sculpt mode. */ + const CurvesGeometry &curves_orig = CurvesGeometry::wrap(edit_hints->curves_id_orig.geometry); + deform_curves(curves_orig, + *surface_mesh_orig, + *surface_mesh_eval, + surface_uv_coords, + reverse_uv_sampler_orig, + reverse_uv_sampler_eval, + corner_normals_orig, + corner_normals_eval, + rest_positions, + transforms.surface_to_curves, + edit_hint_positions, + edit_hint_rotations, + invalid_uv_count); + } curves.tag_positions_changed(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 44793926bbd..cfb9cbf7e24 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -550,7 +550,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set, selection_field, method, seed, attribute_outputs, params); /* Keep instances because the original geometry set may contain instances that are processed as * well. */ - geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); }); params.set_output("Points", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 679a8ba4f8c..c6b0fb4c068 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -328,10 +328,11 @@ static void duplicate_curves(GeometrySet &geometry_set, const IndexAttributes &attribute_outputs) { if (!geometry_set.has_curves()) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } - geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE}); + GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); const Curves &curves_id = *src_component.get_for_read(); @@ -516,10 +517,10 @@ static void duplicate_faces(GeometrySet &geometry_set, const IndexAttributes &attribute_outputs) { if (!geometry_set.has_mesh()) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } - geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *src_component.get_for_read(); @@ -720,7 +721,7 @@ static void duplicate_edges(GeometrySet &geometry_set, const IndexAttributes &attribute_outputs) { if (!geometry_set.has_mesh()) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; }; const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); @@ -1032,7 +1033,7 @@ static void duplicate_points(GeometrySet &geometry_set, } } component_types.append(GEO_COMPONENT_TYPE_INSTANCES); - geometry_set.keep_only(component_types); + geometry_set.keep_only_during_modify(component_types); } /** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 3ce26a086e2..119d895fead 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -213,7 +213,7 @@ static void node_geo_exec(GeoNodeExecParams params) } } - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); }); /* Unused references may have been added above. Remove those now so that other nodes don't diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index f14329c96da..5e0789e557b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -94,7 +94,7 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<Field<float3>>("Position"), params.extract_input<Field<float>>("Radius"), params.extract_input<Field<bool>>("Selection")); - geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD}); + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_EDIT}); params.set_output("Points", std::move(geometry_set)); } else { diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 4a79ec159f4..40169def51e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -20,7 +20,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { const Mesh *mesh = geometry_set.get_mesh_for_read(); if (mesh == nullptr) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } @@ -31,13 +31,13 @@ static void node_geo_exec(GeoNodeExecParams params) evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); if (selection.size() == 0) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection); geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves))); - geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE}); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 8e621d3ed97..d3d1312be6d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -62,13 +62,13 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, { const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>(); if (mesh_component == nullptr) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } GeometryComponentFieldContext field_context{*mesh_component, domain}; const int domain_num = mesh_component->attribute_domain_size(domain); if (domain_num == 0) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } fn::FieldEvaluator evaluator{field_context, domain_num}; @@ -115,7 +115,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, } } - geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc index 5890e070b2f..92814a8bc5e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc @@ -154,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_mesh()) { Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params); geometry_set.replace_volume(volume); - geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME}); } }); params.set_output("Volume", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 74fff8efeee..ed7ef9b7c71 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -25,14 +25,14 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, const PointCloudComponent *point_component = geometry_set.get_component_for_read<PointCloudComponent>(); if (point_component == nullptr) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); if (domain_num == 0) { - geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.remove_geometry_during_modify(); return; } @@ -63,7 +63,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, } } - geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 28a01a5cbce..4a3048e5f4a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -224,7 +224,7 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, new_grid->transform().postScale(voxel_size); BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid)); - r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); + r_geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME}); r_geometry_set.replace_volume(volume); } #endif diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 8e65e73d1e2..945d5fbdcac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -123,6 +123,34 @@ static void translate_volume(Volume &volume, const float3 translation, const Dep transform_volume(volume, float4x4::from_location(translation), depsgraph); } +static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform) +{ + if (edit_hints.positions.has_value()) { + for (float3 &pos : *edit_hints.positions) { + pos = transform * pos; + } + } + float3x3 deform_mat; + copy_m3_m4(deform_mat.values, transform.values); + if (edit_hints.deform_mats.has_value()) { + for (float3x3 &mat : *edit_hints.deform_mats) { + mat = deform_mat * mat; + } + } + else { + edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat); + } +} + +static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation) +{ + if (edit_hints.positions.has_value()) { + for (float3 &pos : *edit_hints.positions) { + pos += translation; + } + } +} + static void translate_geometry_set(GeometrySet &geometry, const float3 translation, const Depsgraph &depsgraph) @@ -142,6 +170,9 @@ static void translate_geometry_set(GeometrySet &geometry, if (geometry.has_instances()) { translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation); } + if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { + translate_curve_edit_hints(*curve_edit_hints, translation); + } } void transform_geometry_set(GeometrySet &geometry, @@ -163,6 +194,9 @@ void transform_geometry_set(GeometrySet &geometry, if (geometry.has_instances()) { transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform); } + if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) { + transform_curve_edit_hints(*curve_edit_hints, transform); + } } void transform_mesh(Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index e1d1c67b8c8..91429560ac8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -193,7 +193,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { Mesh *mesh = create_mesh_from_volume(geometry_set, params); geometry_set.replace_mesh(mesh); - geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); }); #else params.error_message_add(NodeWarningType::Error, diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index cf7cbbdc4bf..55930dcb1ee 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -2,6 +2,7 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set_instances.hh" #include "DNA_modifier_types.h" @@ -264,6 +265,17 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful info.instances_num = instances_component.instances_num(); break; } + case GEO_COMPONENT_TYPE_EDIT: { + const GeometryComponentEditData &edit_component = *( + const GeometryComponentEditData *)component; + if (const bke::CurvesEditHints *curve_edit_hints = + edit_component.curves_edit_hints_.get()) { + EditDataInfo &info = this->edit_data_info.emplace(); + info.has_deform_matrices = curve_edit_hints->deform_mats.has_value(); + info.has_deformed_positions = curve_edit_hints->positions.has_value(); + } + break; + } case GEO_COMPONENT_TYPE_VOLUME: { break; } diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 56e9c9f0496..c6ebc22c43c 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -4,6 +4,7 @@ #include "DEG_depsgraph_query.h" +#include "BKE_curves.hh" #include "BKE_type_conversions.hh" #include "NOD_geometry_exec.hh" @@ -94,11 +95,27 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, message += TIP_("Curve"); break; } + case GEO_COMPONENT_TYPE_EDIT: { + continue; + } } this->error_message_add(NodeWarningType::Info, std::move(message)); } } +void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_set) const +{ + UNUSED_VARS_NDEBUG(geometry_set); +#ifdef DEBUG + if (const bke::CurvesEditHints *curve_edit_hints = + geometry_set.get_curve_edit_hints_for_read()) { + /* If this is not valid, it's likely that the number of stored deformed points does not match + * the number of points in the original data. */ + BLI_assert(curve_edit_hints->is_valid()); + } +#endif +} + const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { for (const InputSocketRef *socket : provider_->dnode->inputs()) { |