diff options
author | Jacques Lucke <jacques@blender.org> | 2022-07-08 15:45:48 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-07-08 15:47:10 +0300 |
commit | 05b38ecc7835b32a9f3aedf36ead4b3e41ec6ca4 (patch) | |
tree | 847549cae6eb8cf0344b14c6c0bc4d0cf5497a6e /source/blender/editors/curves | |
parent | aa78278ef6571b3784fe3d883ee00dc44bc7ed15 (diff) |
Curves: support deforming curves on surface
Curves that are attached to a surface can now follow the surface when
it is modified using shape keys or modifiers (but not when the original
surface is deformed in edit or sculpt mode).
The surface is allowed to be changed in any way that keeps uv maps
intact. So deformation is allowed, but also some topology changes like
subdivision.
The following features are added:
* A new `Deform Curves on Surface` node, which deforms curves with
attachment information based on the surface object and uv map set
in the properties panel.
* A new `Add Rest Position` checkbox in the shape keys panel. When checked,
a new `rest_position` vector attribute is added to the mesh before shape
keys and modifiers are applied. This is necessary to support proper
deformation of the curves, but can also be used for other purposes.
* The `Add > Curve > Empty Hair` operator now sets up a simple geometry
nodes setup that deforms the hair. It also makes sure that the rest
position attribute is added to the surface.
* A new `Object (Attach Curves to Surface)` operator in the `Set Parent To`
(ctrl+P) menu, which attaches existing curves to the surface and sets the
surface object as parent.
Limitations:
* Sculpting the procedurally deformed curves will be implemented separately.
* The `Deform Curves on Surface` node is not generic and can only be used
for one specific purpose currently. We plan to generalize this more in the
future by adding support by exposing more inputs and/or by turning it into
a node group.
Differential Revision: https://developer.blender.org/D14864
Diffstat (limited to 'source/blender/editors/curves')
-rw-r--r-- | source/blender/editors/curves/intern/curves_add.cc | 84 | ||||
-rw-r--r-- | source/blender/editors/curves/intern/curves_ops.cc | 86 |
2 files changed, 169 insertions, 1 deletions
diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 552ef1d96c8..79916253207 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -6,12 +6,96 @@ #include "BLI_rand.hh" +#include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "ED_curves.h" +#include "ED_node.h" +#include "ED_object.h" + +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" namespace blender::ed::curves { +static bool has_surface_deformation_node(const bNodeTree &ntree) +{ + LISTBASE_FOREACH (const bNode *, node, &ntree.nodes) { + if (node->type == GEO_NODE_DEFORM_CURVES_ON_SURFACE) { + return true; + } + if (node->type == NODE_GROUP) { + if (node->id != nullptr) { + if (has_surface_deformation_node(*reinterpret_cast<const bNodeTree *>(node->id))) { + return true; + } + } + } + } + return false; +} + +static bool has_surface_deformation_node(const Object &curves_ob) +{ + LISTBASE_FOREACH (const ModifierData *, md, &curves_ob.modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + if (nmd->node_group == nullptr) { + continue; + } + if (has_surface_deformation_node(*nmd->node_group)) { + return true; + } + } + return false; +} + +void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob) +{ + if (has_surface_deformation_node(curves_ob)) { + return; + } + + Main *bmain = CTX_data_main(&C); + Scene *scene = CTX_data_scene(&C); + + ModifierData *md = ED_object_modifier_add( + nullptr, bmain, scene, &curves_ob, "Surface Deform", eModifierType_Nodes); + NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(md); + nmd.node_group = ntreeAddTree(bmain, "Surface Deform", "GeometryNodeTree"); + + bNodeTree *ntree = nmd.node_group; + ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry"); + ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT); + bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT); + bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE); + + ED_node_tree_propagate_change(&C, bmain, nmd.node_group); + + nodeAddLink(ntree, + group_input, + static_cast<bNodeSocket *>(group_input->outputs.first), + deform_node, + nodeFindSocket(deform_node, SOCK_IN, "Curves")); + nodeAddLink(ntree, + deform_node, + nodeFindSocket(deform_node, SOCK_OUT, "Curves"), + group_output, + static_cast<bNodeSocket *>(group_output->inputs.first)); + + group_input->locx = -200; + group_output->locx = 200; + deform_node->locx = 0; + + ED_node_tree_propagate_change(&C, bmain, nmd.node_group); +} + bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int points_per_curve) { bke::CurvesGeometry curves(points_per_curve * curves_size, curves_size); diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 5dfadca5b63..49e21d00195 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -681,7 +681,8 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface"); } - WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); + /* Refresh the entire window to also clear eventual modifier and nodes editor warnings.*/ + WM_event_add_notifier(C, NC_WINDOW, nullptr); return OPERATOR_FINISHED; } @@ -944,6 +945,88 @@ static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } +namespace surface_set { + +static bool surface_set_poll(bContext *C) +{ + const Object *object = CTX_data_active_object(C); + if (object == nullptr) { + return false; + } + if (object->type != OB_MESH) { + return false; + } + return true; +} + +static int surface_set_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + Object &new_surface_ob = *CTX_data_active_object(C); + + Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data); + const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.ldata, + CD_MLOOPUV); + + CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) { + if (selected_ob->type != OB_CURVES) { + continue; + } + Object &curves_ob = *selected_ob; + Curves &curves_id = *static_cast<Curves *>(curves_ob.data); + + MEM_SAFE_FREE(curves_id.surface_uv_map); + if (new_uv_map_name != nullptr) { + curves_id.surface_uv_map = BLI_strdup(new_uv_map_name); + } + + bool missing_uvs; + bool invalid_uvs; + snap_curves_to_surface::snap_curves_to_surface_exec_object( + curves_ob, + new_surface_ob, + snap_curves_to_surface::AttachMode::Nearest, + &invalid_uvs, + &missing_uvs); + + /* Add deformation modifier if necessary. */ + blender::ed::curves::ensure_surface_deformation_node_exists(*C, curves_ob); + + curves_id.surface = &new_surface_ob; + ED_object_parent_set( + op->reports, C, scene, &curves_ob, &new_surface_ob, PAR_OBJECT, false, true, nullptr); + + DEG_id_tag_update(&curves_ob.id, ID_RECALC_TRANSFORM); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id); + + /* Required for deformation. */ + new_surface_ob.modifier_flag |= OB_MODIFIER_FLAG_ADD_REST_POSITION; + DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY); + } + CTX_DATA_END; + + DEG_relations_tag_update(bmain); + + return OPERATOR_FINISHED; +} + +} // namespace surface_set + +static void CURVES_OT_surface_set(wmOperatorType *ot) +{ + ot->name = "Set Curves Surface Object"; + ot->idname = __func__; + ot->description = + "Use the active object as surface for selected curves objects and set it as the parent"; + + ot->exec = surface_set::surface_set_exec; + ot->poll = surface_set::surface_set_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + } // namespace blender::ed::curves void ED_operatortypes_curves() @@ -955,4 +1038,5 @@ void ED_operatortypes_curves() WM_operatortype_append(CURVES_OT_set_selection_domain); WM_operatortype_append(SCULPT_CURVES_OT_select_all); WM_operatortype_append(CURVES_OT_disable_selection); + WM_operatortype_append(CURVES_OT_surface_set); } |