diff options
Diffstat (limited to 'source/blender/editors/curves/intern/curves_ops.cc')
-rw-r--r-- | source/blender/editors/curves/intern/curves_ops.cc | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index c6dd4508b5a..3a4dcd4f5f6 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -4,13 +4,17 @@ * \ingroup edcurves */ +#include <atomic> + #include "BLI_utildefines.h" #include "ED_curves.h" #include "ED_object.h" +#include "ED_screen.h" #include "WM_api.h" +#include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" #include "BKE_layer.h" @@ -18,6 +22,7 @@ #include "BKE_mesh_runtime.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BKE_report.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -28,6 +33,9 @@ #include "DEG_depsgraph.h" +#include "RNA_access.h" +#include "RNA_define.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. @@ -283,10 +291,204 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; } +namespace snap_curves_to_surface { + +enum class AttachMode { + Nearest, + Deform, +}; + +static bool snap_curves_to_surface_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (ob == nullptr || ob->type != OB_CURVES) { + return false; + } + if (!ED_operator_object_active_editable_ex(C, ob)) { + return false; + } + Curves &curves = *static_cast<Curves *>(ob->data); + if (curves.surface == nullptr) { + return false; + } + return true; +} + +static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) +{ + const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode")); + + std::atomic<bool> found_invalid_looptri_index = false; + + CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) { + if (curves_ob->type != OB_CURVES) { + continue; + } + Curves &curves_id = *static_cast<Curves *>(curves_ob->data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + if (curves_id.surface == nullptr) { + continue; + } + Object &surface_ob = *curves_id.surface; + if (surface_ob.type != OB_MESH) { + continue; + } + Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data); + + MutableSpan<float3> positions_cu = curves.positions_for_write(); + MutableSpan<int> surface_triangle_indices = curves.surface_triangle_indices_for_write(); + MutableSpan<float2> surface_triangle_coords = curves.surface_triangle_coords_for_write(); + + const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh), + BKE_mesh_runtime_looptri_len(&surface_mesh)}; + + const float4x4 curves_to_world_mat = curves_ob->obmat; + const float4x4 world_to_curves_mat = curves_to_world_mat.inverted(); + const float4x4 surface_to_world_mat = surface_ob.obmat; + const float4x4 world_to_surface_mat = surface_to_world_mat.inverted(); + const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat; + const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat; + + switch (attach_mode) { + case AttachMode::Nearest: { + BVHTreeFromMesh surface_bvh; + BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_LOOPTRI, 2); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); }); + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange points = curves.points_for_curve(curve_i); + const int first_point_i = points.first(); + const float3 old_first_point_pos_cu = positions_cu[first_point_i]; + const float3 old_first_point_pos_su = curves_to_surface_mat * old_first_point_pos_cu; + + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest(surface_bvh.tree, + old_first_point_pos_su, + &nearest, + surface_bvh.nearest_callback, + &surface_bvh); + const int looptri_index = nearest.index; + if (looptri_index == -1) { + continue; + } + + const float3 new_first_point_pos_su = nearest.co; + const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su; + const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu; + + for (float3 &pos_cu : positions_cu.slice(points)) { + pos_cu += pos_diff_cu; + } + + surface_triangle_indices[curve_i] = looptri_index; + + const MLoopTri &looptri = surface_looptris[looptri_index]; + const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co; + const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co; + const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co; + float3 bary_coords; + interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su); + surface_triangle_coords[curve_i] = bke::curves::encode_surface_bary_coord(bary_coords); + } + }); + break; + } + case AttachMode::Deform: { + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange points = curves.points_for_curve(curve_i); + const int first_point_i = points.first(); + const float3 old_first_point_pos_cu = positions_cu[first_point_i]; + + const int looptri_index = surface_triangle_indices[curve_i]; + if (!surface_looptris.index_range().contains(looptri_index)) { + found_invalid_looptri_index = true; + continue; + } + + const MLoopTri &looptri = surface_looptris[looptri_index]; + + const float3 bary_coords = bke::curves::decode_surface_bary_coord( + surface_triangle_coords[curve_i]); + + const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co; + const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co; + const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co; + + float3 new_first_point_pos_su; + interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords); + const float3 new_first_point_pos_cu = surface_to_curves_mat * new_first_point_pos_su; + + const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu; + for (float3 &pos_cu : positions_cu.slice(points)) { + pos_cu += pos_diff_cu; + } + } + }); + break; + } + } + + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + } + CTX_DATA_END; + + if (found_invalid_looptri_index) { + BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface"); + } + + WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); + + return OPERATOR_FINISHED; +} + +} // namespace snap_curves_to_surface + +static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot) +{ + using namespace snap_curves_to_surface; + + ot->name = "Snap Curves to Surface"; + ot->idname = "CURVES_OT_snap_curves_to_surface"; + ot->description = "Move curves so that the first point is exactly on the surface mesh"; + + ot->poll = snap_curves_to_surface_poll; + ot->exec = snap_curves_to_surface_exec; + + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + static const EnumPropertyItem attach_mode_items[] = { + {static_cast<int>(AttachMode::Nearest), + "NEAREST", + 0, + "Nearest", + "Find the closest point on the surface for the root point of every curve and move the root " + "there"}, + {static_cast<int>(AttachMode::Deform), + "DEFORM", + 0, + "Deform", + "Re-attach curves to a deformed surface using the existing attachment information. This " + "only works when the topology of the surface mesh has not changed"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_enum(ot->srna, + "attach_mode", + attach_mode_items, + static_cast<int>(AttachMode::Nearest), + "Attach Mode", + "How to find the point on the surface to attach to"); +} + } // namespace blender::ed::curves void ED_operatortypes_curves() { using namespace blender::ed::curves; WM_operatortype_append(CURVES_OT_convert_to_particle_system); + WM_operatortype_append(CURVES_OT_snap_curves_to_surface); } |