Blender V5.0
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
8
10
11#include "BKE_attribute.hh"
13#include "BKE_context.hh"
14#include "BKE_editmesh.hh"
15#include "BKE_layer.hh"
16#include "BKE_mesh.hh"
18
19#include "WM_api.hh"
20#include "WM_types.hh"
21
22#include "RNA_access.hh"
23#include "RNA_enum_types.hh"
24
25#include "ED_geometry.hh"
26#include "ED_mesh.hh"
27#include "ED_object.hh"
28#include "ED_screen.hh"
29#include "ED_transform.hh"
30#include "ED_view3d.hh"
31
32#include "DNA_object_types.h"
33
35#include "UI_resources.hh"
36
37#include "mesh_intern.hh"
38
39using blender::Vector;
40
41/* -------------------------------------------------------------------- */
44
45namespace blender::ed::mesh {
46
47static char domain_to_htype(const bke::AttrDomain domain)
48{
49 switch (domain) {
51 return BM_VERT;
53 return BM_EDGE;
55 return BM_FACE;
57 return BM_LOOP;
58 default:
60 return BM_VERT;
61 }
62}
63
65{
66 if (!ED_operator_editmesh(C)) {
67 return false;
68 }
69 const Mesh *mesh = ED_mesh_context(C);
71 return false;
72 }
73 return true;
74}
75
76namespace set_attribute {
77
79 const BMIterType iter_type,
80 const GPointer value,
81 const int offset)
82{
83 const CPPType &type = *value.type();
84 BMIter iter;
85 BMElem *elem;
86 BM_ITER_MESH (elem, &iter, &bm, iter_type) {
88 type.copy_assign(value.get(), POINTER_OFFSET(elem->head.data, offset));
89 }
90 }
91}
92
98 const GPointer value,
99 const int offset)
100{
101 /* In the separate select modes we may set the same loop values more than once.
102 * This is okay because we're always setting the same value. */
103 BMesh &bm = *em.bm;
104 const CPPType &type = *value.type();
105 if (em.selectmode & SCE_SELECT_FACE) {
106 BMIter face_iter;
107 BMFace *face;
108 BM_ITER_MESH (face, &face_iter, &bm, BM_FACES_OF_MESH) {
110 BMIter loop_iter;
111 BMLoop *loop;
112 BM_ITER_ELEM (loop, &loop_iter, face, BM_LOOPS_OF_FACE) {
113 type.copy_assign(value.get(), POINTER_OFFSET(loop->head.data, offset));
114 }
115 }
116 }
117 }
119 BMIter vert_iter;
120 BMVert *vert;
121 BM_ITER_MESH (vert, &vert_iter, &bm, BM_VERTS_OF_MESH) {
123 BMIter loop_iter;
124 BMLoop *loop;
125 BM_ITER_ELEM (loop, &loop_iter, vert, BM_LOOPS_OF_VERT) {
126 type.copy_assign(value.get(), POINTER_OFFSET(loop->head.data, offset));
127 }
128 }
129 }
130 }
131}
132
134{
135 const Scene *scene = CTX_data_scene(C);
136 ViewLayer *view_layer = CTX_data_view_layer(C);
137
139 scene, view_layer, CTX_wm_view3d(C));
140
141 Mesh *active_mesh = ED_mesh_context(C);
142 AttributeOwner active_owner = AttributeOwner::from_id(&active_mesh->id);
143 const StringRef name = *BKE_attributes_active_name_get(active_owner);
147 eCustomDataType(active_layer->type));
148 const CPPType &type = bke::attribute_type_to_cpp_type(active_type);
149
150 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
151 BLI_SCOPED_DEFER([&]() { type.destruct(buffer); });
153 *op->ptr, active_type, buffer);
154
156
157 bool changed = false;
158 for (Object *object : objects) {
159 Mesh *mesh = static_cast<Mesh *>(object->data);
161 BMesh *bm = em->bm;
165 if (!layer) {
166 continue;
167 }
168 /* Use implicit conversions to try to handle the case where the active attribute has a
169 * different type on multiple objects. */
170 const eCustomDataType dst_data_type = eCustomDataType(layer->type);
171 const CPPType &dst_type = *bke::custom_data_type_to_cpp_type(dst_data_type);
172 if (&type != &dst_type && !conversions.is_convertible(type, dst_type)) {
173 continue;
174 }
175 BUFFER_FOR_CPP_TYPE_VALUE(dst_type, dst_buffer);
176 BLI_SCOPED_DEFER([&]() { dst_type.destruct(dst_buffer); });
177 conversions.convert_to_uninitialized(type, dst_type, value.get(), dst_buffer);
178 const GPointer dst_value(dst_type, dst_buffer);
179 switch (BKE_attribute_domain(owner, layer)) {
182 *bm, BM_VERTS_OF_MESH, dst_value, layer->offset);
183 break;
186 *bm, BM_EDGES_OF_MESH, dst_value, layer->offset);
187 break;
190 *bm, BM_FACES_OF_MESH, dst_value, layer->offset);
191 break;
193 bmesh_loop_layer_selected_values_set(*em, dst_value, layer->offset);
194 break;
195 default:
197 break;
198 }
199
200 changed = true;
202 update.calc_looptris = false;
203 update.calc_normals = false;
204 update.is_destructive = false;
206 }
207
208 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
209}
210
212 wmOperator *op,
213 const wmEvent *event)
214{
216 BMesh *bm = mesh->runtime->edit_mesh->bm;
218
223 eCustomDataType(layer->type));
224 const bke::AttrDomain domain = BKE_attribute_domain(owner, layer);
225 const BMElem *active_elem = BM_mesh_active_elem_get(bm);
226 if (!active_elem) {
227 return WM_operator_props_popup(C, op, event);
228 }
229
230 /* Only support filling the active data when the active selection mode matches the active
231 * attribute domain. NOTE: This doesn't work well for corner domain attributes. */
232 if (active_elem->head.htype != domain_to_htype(domain)) {
233 return WM_operator_props_popup(C, op, event);
234 }
235
236 const CPPType &type = bke::attribute_type_to_cpp_type(data_type);
237 const GPointer active_value(type, POINTER_OFFSET(active_elem->head.data, layer->offset));
238
239 PropertyRNA *prop = geometry::rna_property_for_type(*op->ptr, data_type);
240 if (!RNA_property_is_set(op->ptr, prop)) {
242 }
243
244 return WM_operator_props_popup(C, op, event);
245}
246
248{
249 uiLayout *layout = &op->layout->column(true);
250 layout->use_property_split_set(true);
251 layout->use_property_decorate_set(false);
252
259 eCustomDataType(layer->type));
260 const StringRefNull prop_name = geometry::rna_property_name_for_type(active_type);
261 layout->prop(op->ptr, prop_name, UI_ITEM_NONE, name, ICON_NONE);
262}
263
264} // namespace set_attribute
265
266} // namespace blender::ed::mesh
267
269{
270 using namespace blender::ed::mesh;
271 using namespace blender::ed::mesh::set_attribute;
272 ot->name = "Set Attribute";
273 ot->description = "Set values of the active attribute for selected elements";
274 ot->idname = "MESH_OT_attribute_set";
275
280
282
284}
285
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:784
struct CustomDataLayer * BKE_attribute_search_for_write(AttributeOwner &owner, blender::StringRef name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:672
blender::bke::AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const struct CustomDataLayer *layer)
@ ATTR_DOMAIN_MASK_ALL
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:61
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:93
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define BLI_SCOPED_DEFER(function_to_defer)
#define POINTER_OFFSET(v, ofs)
#define CD_MASK_PROP_ALL
Object is a sort of wrapper for general info.
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
Mesh * ED_mesh_context(bContext *C)
bool ED_operator_editmesh(bContext *C)
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ BM_ELEM_SELECT
@ BM_LOOP
#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
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:44
static const CPPType & get()
void destruct(void *ptr) const
void copy_assign(const void *src, void *dst) const
const CPPType * type() const
const void * get() 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()
std::optional< AttrType > custom_data_type_to_attr_type(eCustomDataType data_type)
const CPPType * custom_data_type_to_cpp_type(eCustomDataType type)
const CPPType & attribute_type_to_cpp_type(AttrType type)
PropertyRNA * rna_property_for_type(PointerRNA &ptr, const bke::AttrType type)
GPointer rna_property_for_attribute_type_retrieve_value(PointerRNA &ptr, const bke::AttrType type, void *buffer)
StringRefNull rna_property_name_for_type(const bke::AttrType 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)
static void mesh_set_attribute_ui(bContext *C, wmOperator *op)
static wmOperatorStatus mesh_set_attribute_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus mesh_set_attribute_exec(bContext *C, wmOperator *op)
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)
const char * name
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
short selectmode
BMHeader head
void * data
BMHeader head
void use_property_decorate_set(bool is_sep)
uiLayout & column(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
struct uiLayout * layout
struct PointerRNA * ptr
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorStatus WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *)