Curves: Implement join operator

Previously the object join operator wasn't implemented for the new
curves type. This commit implements the joining by passing the selected
curves geometry as instances to the realize instances function. That way
the complexity of the new code just relates to dealing with objects.

Pull Request: https://projects.blender.org/blender/blender/pulls/134691
This commit is contained in:
Hans Goudey
2025-02-19 15:24:29 +01:00
committed by Hans Goudey
parent 2f01da564d
commit 5a6d2e1ce2
4 changed files with 96 additions and 1 deletions

View File

@@ -24,6 +24,7 @@ set(SRC
intern/curves_ops.cc
intern/curves_selection.cc
intern/curves_undo.cc
intern/join.cc
intern/select_linked_pick.cc
intern/separate.cc
)

View File

@@ -0,0 +1,87 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_scene_types.h"
#include "BKE_context.hh"
#include "BKE_instances.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_curves.hh"
#include "ED_object.hh"
#include "GEO_realize_instances.hh"
namespace blender::ed::curves {
int join_objects(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *active_object = CTX_data_active_object(C);
BLI_assert(active_object);
BLI_assert(active_object->type == OB_CURVES);
Curves &active_curves = *static_cast<Curves *>(active_object->data);
const float4x4 &world_to_active = active_object->world_to_object();
Vector<Object *> objects{active_object};
bool active_object_selected = false;
CTX_DATA_BEGIN (C, Object *, object, selected_editable_objects) {
if (object == active_object) {
active_object_selected = true;
continue;
}
if (object->type != OB_CURVES) {
continue;
}
objects.append(object);
}
CTX_DATA_END;
if (!active_object_selected) {
BKE_report(op->reports, RPT_WARNING, "Active object is not a selected curves object");
return OPERATOR_CANCELLED;
}
bke::Instances instances;
instances.resize(objects.size());
MutableSpan<float4x4> transforms = instances.transforms_for_write();
MutableSpan<int> references = instances.reference_handles_for_write();
Map<const Curves *, int> reference_by_orig_curves;
for (const int i : objects.index_range()) {
transforms[i] = world_to_active * objects[i]->object_to_world();
const Curves *orig_curves = static_cast<const Curves *>(objects[i]->data);
references[i] = reference_by_orig_curves.lookup_or_add_cb(orig_curves, [&]() {
auto geometry = bke::GeometrySet::from_curves(BKE_curves_copy_for_eval(orig_curves));
return instances.add_new_reference(std::move(geometry));
});
}
bke::GeometrySet realized_geometry = geometry::realize_instances(
bke::GeometrySet::from_instances(&instances, bke::GeometryOwnershipType::ReadOnly),
geometry::RealizeInstancesOptions());
Curves *realized_curves = realized_geometry.get_curves_for_write();
active_curves.geometry.wrap() = std::move(realized_curves->geometry.wrap());
for (Object *object : objects.as_span().drop_front(1)) {
object::base_free_and_unlink(bmain, scene, object);
}
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&active_object->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
} // namespace blender::ed::curves

View File

@@ -26,6 +26,7 @@ struct ViewContext;
struct rcti;
struct TransVertStore;
struct wmKeyConfig;
struct wmOperator;
namespace blender::bke {
enum class AttrDomain : int8_t;
struct GSpanAttributeWriter;
@@ -461,6 +462,8 @@ void resize_curves(bke::CurvesGeometry &curves,
*/
void reorder_curves(bke::CurvesGeometry &curves, Span<int> old_by_new_indices_map);
int join_objects(bContext *C, wmOperator *op);
/** \} */
} // namespace blender::ed::curves

View File

@@ -4714,7 +4714,8 @@ static bool object_join_poll(bContext *C)
return false;
}
if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_ARMATURE, OB_GREASE_PENCIL)) {
if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_ARMATURE, OB_GREASE_PENCIL, OB_CURVES))
{
return true;
}
return false;
@@ -4751,6 +4752,9 @@ static int object_join_exec(bContext *C, wmOperator *op)
else if (ob->type == OB_ARMATURE) {
ret = ED_armature_join_objects_exec(C, op);
}
else if (ob->type == OB_CURVES) {
ret = curves::join_objects(C, op);
}
else if (ob->type == OB_GREASE_PENCIL) {
ret = ED_grease_pencil_join_objects_exec(C, op);
}