Blender V4.5
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"
12#include "BKE_context.hh"
13#include "BKE_editmesh.hh"
14#include "BKE_layer.hh"
15#include "BKE_mesh.hh"
17
18#include "WM_api.hh"
19#include "WM_types.hh"
20
21#include "RNA_access.hh"
22#include "RNA_enum_types.hh"
23
24#include "ED_geometry.hh"
25#include "ED_mesh.hh"
26#include "ED_object.hh"
27#include "ED_screen.hh"
28#include "ED_transform.hh"
29#include "ED_view3d.hh"
30
31#include "DNA_object_types.h"
32
33#include "UI_interface.hh"
34#include "UI_resources.hh"
35
36#include "mesh_intern.hh"
37
38using blender::Vector;
39
40/* -------------------------------------------------------------------- */
43
45
46static char domain_to_htype(const bke::AttrDomain domain)
47{
48 switch (domain) {
50 return BM_VERT;
52 return BM_EDGE;
54 return BM_FACE;
56 return BM_LOOP;
57 default:
59 return BM_VERT;
60 }
61}
62
64{
65 if (!ED_operator_editmesh(C)) {
66 return false;
67 }
68 const Mesh *mesh = ED_mesh_context(C);
70 return false;
71 }
72 return true;
73}
74
75namespace set_attribute {
76
78 const BMIterType iter_type,
79 const GPointer value,
80 const int offset)
81{
82 const CPPType &type = *value.type();
83 BMIter iter;
84 BMElem *elem;
85 BM_ITER_MESH (elem, &iter, &bm, iter_type) {
87 type.copy_assign(value.get(), POINTER_OFFSET(elem->head.data, offset));
88 }
89 }
90}
91
97 const GPointer value,
98 const int offset)
99{
100 /* In the separate select modes we may set the same loop values more than once.
101 * This is okay because we're always setting the same value. */
102 BMesh &bm = *em.bm;
103 const CPPType &type = *value.type();
104 if (em.selectmode & SCE_SELECT_FACE) {
105 BMIter face_iter;
106 BMFace *face;
107 BM_ITER_MESH (face, &face_iter, &bm, BM_FACES_OF_MESH) {
109 BMIter loop_iter;
110 BMLoop *loop;
111 BM_ITER_ELEM (loop, &loop_iter, face, BM_LOOPS_OF_FACE) {
112 type.copy_assign(value.get(), POINTER_OFFSET(loop->head.data, offset));
113 }
114 }
115 }
116 }
118 BMIter vert_iter;
119 BMVert *vert;
120 BM_ITER_MESH (vert, &vert_iter, &bm, BM_VERTS_OF_MESH) {
122 BMIter loop_iter;
123 BMLoop *loop;
124 BM_ITER_ELEM (loop, &loop_iter, vert, BM_LOOPS_OF_VERT) {
125 type.copy_assign(value.get(), POINTER_OFFSET(loop->head.data, offset));
126 }
127 }
128 }
129 }
130}
131
133{
134 const Scene *scene = CTX_data_scene(C);
135 ViewLayer *view_layer = CTX_data_view_layer(C);
136
138 scene, view_layer, CTX_wm_view3d(C));
139
140 Mesh *active_mesh = ED_mesh_context(C);
141 AttributeOwner active_owner = AttributeOwner::from_id(&active_mesh->id);
142 const StringRef name = *BKE_attributes_active_name_get(active_owner);
144 active_owner, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
145 const eCustomDataType active_type = eCustomDataType(active_layer->type);
146 const CPPType &type = *bke::custom_data_type_to_cpp_type(active_type);
147
148 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
149 BLI_SCOPED_DEFER([&]() { type.destruct(buffer); });
151 *op->ptr, active_type, buffer);
152
154
155 bool changed = false;
156 for (Object *object : objects) {
157 Mesh *mesh = static_cast<Mesh *>(object->data);
159 BMesh *bm = em->bm;
163 if (!layer) {
164 continue;
165 }
166 /* Use implicit conversions to try to handle the case where the active attribute has a
167 * different type on multiple objects. */
168 const eCustomDataType dst_data_type = eCustomDataType(layer->type);
169 const CPPType &dst_type = *bke::custom_data_type_to_cpp_type(dst_data_type);
170 if (&type != &dst_type && !conversions.is_convertible(type, dst_type)) {
171 continue;
172 }
173 BUFFER_FOR_CPP_TYPE_VALUE(dst_type, dst_buffer);
174 BLI_SCOPED_DEFER([&]() { dst_type.destruct(dst_buffer); });
175 conversions.convert_to_uninitialized(type, dst_type, value.get(), dst_buffer);
176 const GPointer dst_value(dst_type, dst_buffer);
177 switch (BKE_attribute_domain(owner, layer)) {
180 *bm, BM_VERTS_OF_MESH, dst_value, layer->offset);
181 break;
184 *bm, BM_EDGES_OF_MESH, dst_value, layer->offset);
185 break;
188 *bm, BM_FACES_OF_MESH, dst_value, layer->offset);
189 break;
191 bmesh_loop_layer_selected_values_set(*em, dst_value, layer->offset);
192 break;
193 default:
195 break;
196 }
197
198 changed = true;
200 update.calc_looptris = false;
201 update.calc_normals = false;
202 update.is_destructive = false;
204 }
205
206 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
207}
208
210 wmOperator *op,
211 const wmEvent *event)
212{
214 BMesh *bm = mesh->runtime->edit_mesh->bm;
216
217 const StringRef name = *BKE_attributes_active_name_get(owner);
220 const eCustomDataType data_type = eCustomDataType(layer->type);
221 const bke::AttrDomain domain = BKE_attribute_domain(owner, layer);
222 const BMElem *active_elem = BM_mesh_active_elem_get(bm);
223 if (!active_elem) {
224 return WM_operator_props_popup(C, op, event);
225 }
226
227 /* Only support filling the active data when the active selection mode matches the active
228 * attribute domain. NOTE: This doesn't work well for corner domain attributes. */
229 if (active_elem->head.htype != domain_to_htype(domain)) {
230 return WM_operator_props_popup(C, op, event);
231 }
232
233 const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
234 const GPointer active_value(type, POINTER_OFFSET(active_elem->head.data, layer->offset));
235
236 PropertyRNA *prop = geometry::rna_property_for_type(*op->ptr, data_type);
237 if (!RNA_property_is_set(op->ptr, prop)) {
239 }
240
241 return WM_operator_props_popup(C, op, event);
242}
243
245{
246 uiLayout *layout = &op->layout->column(true);
247 uiLayoutSetPropSep(layout, true);
248 uiLayoutSetPropDecorate(layout, false);
249
252 const StringRef name = *BKE_attributes_active_name_get(owner);
255 const eCustomDataType active_type = eCustomDataType(layer->type);
256 const StringRefNull prop_name = geometry::rna_property_name_for_type(active_type);
257 layout->prop(op->ptr, prop_name, UI_ITEM_NONE, name, ICON_NONE);
258}
259
260} // namespace set_attribute
261
262} // namespace blender::ed::mesh
263
265{
266 using namespace blender::ed::mesh;
267 using namespace blender::ed::mesh::set_attribute;
268 ot->name = "Set Attribute";
269 ot->description = "Set values of the active attribute for selected elements";
270 ot->idname = "MESH_OT_attribute_set";
271
276
278
280}
281
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:802
struct CustomDataLayer * BKE_attribute_search_for_write(AttributeOwner &owner, blender::StringRef name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:690
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)
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
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ 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
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
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)
#define CD_MASK_PROP_ALL
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 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)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
short selectmode
BMHeader head
void * data
BMHeader head
uiLayout & column(bool align)
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:4225
wmOperatorStatus WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *)