Blender V4.3
editmesh_attribute.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_color.hh"
12
13#include "BKE_attribute.hh"
14#include "BKE_context.hh"
15#include "BKE_editmesh.hh"
16#include "BKE_layer.hh"
17#include "BKE_mesh.hh"
18#include "BKE_report.hh"
20
21#include "WM_api.hh"
22#include "WM_types.hh"
23
24#include "RNA_access.hh"
25#include "RNA_define.hh"
26#include "RNA_enum_types.hh"
27
28#include "ED_geometry.hh"
29#include "ED_mesh.hh"
30#include "ED_object.hh"
31#include "ED_screen.hh"
32#include "ED_transform.hh"
33#include "ED_view3d.hh"
34
35#include "BLT_translation.hh"
36
37#include "DNA_object_types.h"
38
39#include "UI_interface.hh"
40#include "UI_resources.hh"
41
42#include "bmesh_tools.hh"
43
44#include "DEG_depsgraph.hh"
46
47#include "mesh_intern.hh"
48
49using blender::Vector;
50
51/* -------------------------------------------------------------------- */
56
57static char domain_to_htype(const bke::AttrDomain domain)
58{
59 switch (domain) {
61 return BM_VERT;
63 return BM_EDGE;
65 return BM_FACE;
67 return BM_LOOP;
68 default:
70 return BM_VERT;
71 }
72}
73
75{
76 if (!ED_operator_editmesh(C)) {
77 return false;
78 }
79 const Mesh *mesh = ED_mesh_context(C);
80 if (!geometry::attribute_set_poll(*C, mesh->id)) {
81 return false;
82 }
83 return true;
84}
85
86namespace set_attribute {
87
89 const BMIterType iter_type,
90 const GPointer value,
91 const int offset)
92{
93 const CPPType &type = *value.type();
94 BMIter iter;
95 BMElem *elem;
96 BM_ITER_MESH (elem, &iter, &bm, iter_type) {
98 type.copy_assign(value.get(), POINTER_OFFSET(elem->head.data, offset));
99 }
100 }
101}
102
108 const GPointer value,
109 const int offset)
110{
111 /* In the separate select modes we may set the same loop values more than once.
112 * This is okay because we're always setting the same value. */
113 BMesh &bm = *em.bm;
114 const CPPType &type = *value.type();
115 if (em.selectmode & SCE_SELECT_FACE) {
116 BMIter face_iter;
117 BMFace *face;
118 BM_ITER_MESH (face, &face_iter, &bm, BM_FACES_OF_MESH) {
120 BMIter loop_iter;
121 BMLoop *loop;
122 BM_ITER_ELEM (loop, &loop_iter, face, BM_LOOPS_OF_FACE) {
123 type.copy_assign(value.get(), POINTER_OFFSET(loop->head.data, offset));
124 }
125 }
126 }
127 }
129 BMIter vert_iter;
130 BMVert *vert;
131 BM_ITER_MESH (vert, &vert_iter, &bm, BM_VERTS_OF_MESH) {
133 BMIter loop_iter;
134 BMLoop *loop;
135 BM_ITER_ELEM (loop, &loop_iter, vert, BM_LOOPS_OF_VERT) {
136 type.copy_assign(value.get(), POINTER_OFFSET(loop->head.data, offset));
137 }
138 }
139 }
140 }
141}
142
144{
145 const Scene *scene = CTX_data_scene(C);
146 ViewLayer *view_layer = CTX_data_view_layer(C);
147
149 scene, view_layer, CTX_wm_view3d(C));
150
151 Mesh *mesh = ED_mesh_context(C);
152 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
153 CustomDataLayer *active_attribute = BKE_attributes_active_get(owner);
154 const eCustomDataType active_type = eCustomDataType(active_attribute->type);
155 const CPPType &type = *bke::custom_data_type_to_cpp_type(active_type);
156
157 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
158 BLI_SCOPED_DEFER([&]() { type.destruct(buffer); });
160 *op->ptr, active_type, buffer);
161
163
164 bool changed = false;
165 for (Object *object : objects) {
166 Mesh *mesh = static_cast<Mesh *>(object->data);
168 BMesh *bm = em->bm;
169
171 if (!layer) {
172 continue;
173 }
174 /* Use implicit conversions to try to handle the case where the active attribute has a
175 * different type on multiple objects. */
176 const eCustomDataType dst_data_type = eCustomDataType(active_attribute->type);
177 const CPPType &dst_type = *bke::custom_data_type_to_cpp_type(dst_data_type);
178 if (&type != &dst_type && !conversions.is_convertible(type, dst_type)) {
179 continue;
180 }
181 BUFFER_FOR_CPP_TYPE_VALUE(dst_type, dst_buffer);
182 BLI_SCOPED_DEFER([&]() { dst_type.destruct(dst_buffer); });
183 conversions.convert_to_uninitialized(type, dst_type, value.get(), dst_buffer);
184 const GPointer dst_value(dst_type, dst_buffer);
185 switch (BKE_attribute_domain(owner, layer)) {
188 *bm, BM_VERTS_OF_MESH, dst_value, layer->offset);
189 break;
192 *bm, BM_EDGES_OF_MESH, dst_value, layer->offset);
193 break;
196 *bm, BM_FACES_OF_MESH, dst_value, layer->offset);
197 break;
199 bmesh_loop_layer_selected_values_set(*em, dst_value, layer->offset);
200 break;
201 default:
203 break;
204 }
205
206 changed = true;
208 update.calc_looptris = false;
209 update.calc_normals = false;
210 update.is_destructive = false;
211 EDBM_update(mesh, &update);
212 }
213
214 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
215}
216
217static int mesh_set_attribute_invoke(bContext *C, wmOperator *op, const wmEvent *event)
218{
219 Mesh *mesh = ED_mesh_context(C);
220 BMesh *bm = mesh->runtime->edit_mesh->bm;
221 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
222
223 const CustomDataLayer *layer = BKE_attributes_active_get(owner);
224 const eCustomDataType data_type = eCustomDataType(layer->type);
225 const bke::AttrDomain domain = BKE_attribute_domain(owner, layer);
226 const BMElem *active_elem = BM_mesh_active_elem_get(bm);
227 if (!active_elem) {
228 return WM_operator_props_popup(C, op, event);
229 }
230
231 /* Only support filling the active data when the active selection mode matches the active
232 * attribute domain. NOTE: This doesn't work well for corner domain attributes. */
233 if (active_elem->head.htype != domain_to_htype(domain)) {
234 return WM_operator_props_popup(C, op, event);
235 }
236
237 const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
238 const GPointer active_value(type, POINTER_OFFSET(active_elem->head.data, layer->offset));
239
240 PropertyRNA *prop = geometry::rna_property_for_type(*op->ptr, data_type);
241 if (!RNA_property_is_set(op->ptr, prop)) {
243 }
244
245 return WM_operator_props_popup(C, op, event);
246}
247
249{
250 uiLayout *layout = uiLayoutColumn(op->layout, true);
251 uiLayoutSetPropSep(layout, true);
252 uiLayoutSetPropDecorate(layout, false);
253
254 Mesh *mesh = ED_mesh_context(C);
255 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
256 CustomDataLayer *active_attribute = BKE_attributes_active_get(owner);
257 const eCustomDataType active_type = eCustomDataType(active_attribute->type);
258 const StringRefNull prop_name = geometry::rna_property_name_for_type(active_type);
259 const char *name = active_attribute->name;
260 uiItemR(layout, op->ptr, prop_name.c_str(), UI_ITEM_NONE, name, ICON_NONE);
261}
262
263} // namespace set_attribute
264
265} // namespace blender::ed::mesh
266
268{
269 using namespace blender::ed::mesh;
270 using namespace blender::ed::mesh::set_attribute;
271 ot->name = "Set Attribute";
272 ot->description = "Set values of the active attribute for selected elements";
273 ot->idname = "MESH_OT_attribute_set";
274
275 ot->exec = mesh_set_attribute_exec;
276 ot->invoke = mesh_set_attribute_invoke;
277 ot->poll = mesh_active_attribute_poll;
278 ot->ui = mesh_set_attribute_ui;
279
281
283}
284
blender::bke::AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const struct CustomDataLayer *layer)
struct CustomDataLayer * BKE_attributes_active_get(AttributeOwner &owner)
Definition attribute.cc:781
Scene * CTX_data_scene(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define BLI_SCOPED_DEFER(function_to_defer)
#define POINTER_OFFSET(v, ofs)
Object is a sort of wrapper for general info.
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
Mesh * ED_mesh_context(bContext *C)
bool ED_operator_editmesh(bContext *C)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ BM_LOOP
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
BMIterType
BMesh Iterators.
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMElem * BM_mesh_active_elem_get(BMesh *bm)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
static const CPPType & get()
void destruct(void *ptr) const
constexpr const char * c_str() const
void convert_to_uninitialized(const CPPType &from_type, const CPPType &to_type, const void *from_value, void *to_value) const
bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
void MESH_OT_attribute_set(wmOperatorType *ot)
const DataTypeConversions & get_implicit_type_conversions()
const CPPType * custom_data_type_to_cpp_type(eCustomDataType type)
GPointer rna_property_for_attribute_type_retrieve_value(PointerRNA &ptr, const eCustomDataType type, void *buffer)
StringRefNull rna_property_name_for_type(const eCustomDataType type)
void register_rna_properties_for_attribute_types(StructRNA &srna)
void rna_property_for_attribute_type_set_value(PointerRNA &ptr, PropertyRNA &prop, const GPointer value)
bool attribute_set_poll(bContext &C, const ID &object_data)
PropertyRNA * rna_property_for_type(PointerRNA &ptr, const eCustomDataType type)
static int mesh_set_attribute_exec(bContext *C, wmOperator *op)
static void mesh_set_attribute_ui(bContext *C, wmOperator *op)
static int mesh_set_attribute_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void bmesh_loop_layer_selected_values_set(BMEditMesh &em, const GPointer value, const int offset)
static void bmesh_vert_edge_face_layer_selected_values_set(BMesh &bm, const BMIterType iter_type, const GPointer value, const int offset)
static char domain_to_htype(const bke::AttrDomain domain)
static bool mesh_active_attribute_poll(bContext *C)
static void update(bNodeTree *ntree)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
short selectmode
BMHeader head
void * data
BMHeader head
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
StructRNA * srna
Definition WM_types.hh:1080
struct uiLayout * layout
struct PointerRNA * ptr
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *)