Blender V4.3
sculpt_dyntopo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "sculpt_dyntopo.hh"
9
10#include <cmath>
11#include <cstdlib>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLT_translation.hh"
16
17#include "DNA_modifier_types.h"
18
19#include "BKE_context.hh"
20#include "BKE_global.hh"
21#include "BKE_mesh.hh"
22#include "BKE_modifier.hh"
23#include "BKE_object.hh"
24#include "BKE_paint.hh"
25#include "BKE_particle.h"
26#include "BKE_pbvh_api.hh"
27#include "BKE_pointcache.h"
28#include "BKE_scene.hh"
29
30#include "DEG_depsgraph.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "ED_undo.hh"
36
37#include "sculpt_intern.hh"
38#include "sculpt_undo.hh"
39
40#include "UI_interface.hh"
41#include "UI_resources.hh"
42
43#include "bmesh.hh"
44#include "bmesh_tools.hh"
45
47
49{
50 if (bm->totloop != bm->totface * 3) {
54 4,
55 false,
56 nullptr,
57 nullptr,
58 nullptr);
59 }
60}
61
62void enable_ex(Main &bmain, Depsgraph &depsgraph, Object &ob)
63{
64 SculptSession &ss = *ob.sculpt;
65 Mesh *mesh = static_cast<Mesh *>(ob.data);
66 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
67
69
70 /* Dynamic topology doesn't ensure selection state is valid, so remove #36280. */
72
73 /* Create triangles-only BMesh. */
74 BMeshCreateParams create_params{};
75 create_params.use_toolflags = false;
76 ss.bm = BM_mesh_create(&allocsize, &create_params);
77
78 BMeshFromMeshParams convert_params{};
79 convert_params.calc_face_normal = true;
80 convert_params.calc_vert_normal = true;
81 convert_params.use_shapekey = true;
82 convert_params.active_shapekey = ob.shapenr;
83 BM_mesh_bm_from_me(ss.bm, mesh, &convert_params);
84 triangulate(ss.bm);
85
86 BM_data_layer_ensure_named(ss.bm, &ss.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
87
88 /* Make sure the data for existing faces are initialized. */
89 if (mesh->faces_num != ss.bm->totface) {
91 }
92
93 /* Enable dynamic topology. */
94 mesh->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
95
96 /* Enable logging for undo/redo. */
97 ss.bm_log = BM_log_create(ss.bm);
98
99 /* Update dependency graph, so modifiers that depend on dyntopo being enabled
100 * are re-evaluated and the #bke::pbvh::Tree is re-created. */
103}
104
111static void disable(
112 Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob, undo::StepData *undo_step)
113{
114 SculptSession &ss = *ob.sculpt;
115 Mesh *mesh = static_cast<Mesh *>(ob.data);
116
117 if (BMesh *bm = ss.bm) {
118 BM_data_layer_free_named(bm, &bm->vdata, ".sculpt_dyntopo_node_id_vertex");
119 BM_data_layer_free_named(bm, &bm->pdata, ".sculpt_dyntopo_node_id_face");
120 }
121
123
124 if (undo_step) {
126 }
127 else {
129
130 /* Sync the visibility to vertices manually as `vert_to_face_map` is still not initialized. */
131 bool *hide_vert = (bool *)CustomData_get_layer_named_for_write(
132 &mesh->vert_data, CD_PROP_BOOL, ".hide_vert", mesh->verts_num);
133 if (hide_vert != nullptr) {
134 memset(hide_vert, 0, sizeof(bool) * mesh->verts_num);
135 }
136 }
137
138 /* Clear data. */
139 mesh->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY;
140
141 /* Typically valid but with global-undo they can be nullptr, see: #36234. */
142 if (ss.bm) {
143 BM_mesh_free(ss.bm);
144 ss.bm = nullptr;
145 }
146 if (ss.bm_log) {
148 ss.bm_log = nullptr;
149 }
150
153
154 /* Update dependency graph, so modifiers that depend on dyntopo being enabled
155 * are re-evaluated and the #bke::pbvh::Tree is re-created. */
158}
159
160void disable(bContext *C, undo::StepData *undo_step)
161{
162 Main *bmain = CTX_data_main(C);
164 Scene *scene = CTX_data_scene(C);
166 disable(*bmain, *depsgraph, *scene, *ob, undo_step);
167}
168
169void disable_with_undo(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
170{
171 SculptSession &ss = *ob.sculpt;
172 if (ss.bm != nullptr) {
173 /* May be false in background mode. */
174 const bool use_undo = G.background ? (ED_undo_stack_get() != nullptr) : true;
175 if (use_undo) {
176 undo::push_begin_ex(scene, ob, "Dynamic topology disable");
178 }
179 disable(bmain, depsgraph, scene, ob, nullptr);
180 if (use_undo) {
181 undo::push_end(ob);
182 }
183 }
184}
185
186static void enable_with_undo(Main &bmain, Depsgraph &depsgraph, const Scene &scene, Object &ob)
187{
188 SculptSession &ss = *ob.sculpt;
189 if (ss.bm == nullptr) {
190 /* May be false in background mode. */
191 const bool use_undo = G.background ? (ED_undo_stack_get() != nullptr) : true;
192 if (use_undo) {
193 undo::push_begin_ex(scene, ob, "Dynamic topology enable");
194 }
195 enable_ex(bmain, depsgraph, ob);
196 if (use_undo) {
198 undo::push_end(ob);
199 }
200 }
201}
202
204{
205 Main &bmain = *CTX_data_main(C);
207 Scene &scene = *CTX_data_scene(C);
209 SculptSession &ss = *ob.sculpt;
210
211 WM_cursor_wait(true);
212
213 if (ss.bm) {
214 disable_with_undo(bmain, depsgraph, scene, ob);
215 }
216 else {
217 enable_with_undo(bmain, depsgraph, scene, ob);
218 }
219
220 WM_cursor_wait(false);
222
223 return OPERATOR_FINISHED;
224}
225
227{
228 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR);
229 uiLayout *layout = UI_popup_menu_layout(pup);
230
231 if (flag & (VDATA | EDATA | LDATA)) {
232 const char *msg_error = RPT_("Attribute Data Detected");
233 const char *msg = RPT_("Dyntopo will not preserve colors, UVs, or other attributes");
234 uiItemL(layout, msg_error, ICON_INFO);
235 uiItemL(layout, msg, ICON_NONE);
236 uiItemS(layout);
237 }
238
239 if (flag & MODIFIER) {
240 const char *msg_error = RPT_("Generative Modifiers Detected!");
241 const char *msg = RPT_(
242 "Keeping the modifiers will increase polycount when returning to object mode");
243
244 uiItemL(layout, msg_error, ICON_INFO);
245 uiItemL(layout, msg, ICON_NONE);
246 uiItemS(layout);
247 }
248
250 layout, ot, IFACE_("OK"), ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, nullptr);
251
252 UI_popup_menu_end(C, pup);
253
254 return OPERATOR_INTERFACE;
255}
256
258{
259 if (layer.type == CD_PROP_FLOAT && STREQ(layer.name, ".sculpt_mask")) {
260 return true;
261 }
262 if (CD_TYPE_AS_MASK(layer.type) & CD_MASK_PROP_ALL) {
263 return BM_attribute_stored_in_bmesh_builtin(layer.name);
264 }
265 return ELEM(layer.type, CD_ORIGINDEX);
266}
267
269{
270 return std::all_of(layers.begin(), layers.end(), [&](const CustomDataLayer &layer) {
271 return dyntopo_supports_layer(layer);
272 });
273}
274
276{
277 Mesh *mesh = static_cast<Mesh *>(ob.data);
278 SculptSession &ss = *ob.sculpt;
279
281
282 BLI_assert(ss.bm == nullptr);
284
285 if (!dyntopo_supports_customdata_layers({mesh->vert_data.layers, mesh->vert_data.totlayer})) {
286 flag |= VDATA;
287 }
288 if (!dyntopo_supports_customdata_layers({mesh->edge_data.layers, mesh->edge_data.totlayer})) {
289 flag |= EDATA;
290 }
291 if (!dyntopo_supports_customdata_layers({mesh->face_data.layers, mesh->face_data.totlayer})) {
292 flag |= LDATA;
293 }
294 if (!dyntopo_supports_customdata_layers({mesh->corner_data.layers, mesh->corner_data.totlayer}))
295 {
296 flag |= LDATA;
297 }
298
299 {
300 VirtualModifierData virtual_modifier_data;
301 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&ob, &virtual_modifier_data);
302
303 /* Exception for shape keys because we can edit those. */
304 for (; md; md = md->next) {
307 continue;
308 }
309
311 flag |= MODIFIER;
312 break;
313 }
314 }
315 }
316
317 return flag;
318}
319
321 wmOperator *op,
322 const wmEvent * /*event*/)
323{
325 SculptSession &ss = *ob.sculpt;
326
327 if (!ss.bm) {
328 Scene &scene = *CTX_data_scene(C);
329 const WarnFlag flag = check_attribute_warning(scene, ob);
330
331 if (flag) {
332 /* The mesh has customdata that will be lost, let the user confirm this is OK. */
333 return dyntopo_warning_popup(C, op->type, flag);
334 }
335 }
336
338}
339
341{
342 /* Identifiers. */
343 ot->name = "Dynamic Topology Toggle";
344 ot->idname = "SCULPT_OT_dynamic_topology_toggle";
345 ot->description = "Dynamic topology alters the mesh topology while sculpting";
346
347 /* API callbacks. */
351
353}
354
355} // namespace blender::ed::sculpt_paint::dyntopo
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
#define CD_TYPE_AS_MASK(_type)
void BKE_mesh_mselect_clear(Mesh *mesh)
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
const ModifierTypeInfo * BKE_modifier_get_info(ModifierType type)
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
General operations, lookup, etc. for blender objects.
void BKE_sculptsession_bm_to_me(Object *ob, bool reorder)
Definition paint.cc:2088
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2099
void BKE_particlesystem_reset_all(struct Object *object)
A BVH for high poly meshes.
#define PTCACHE_RESET_OUTDATED
int BKE_ptcache_object_reset(struct Scene *scene, struct Object *ob, int mode)
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2568
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define CD_MASK_PROP_ALL
@ CD_PROP_FLOAT
@ ME_SCULPT_DYNAMIC_TOPOLOGY
@ eModifierMode_Realtime
@ MOD_TRIANGULATE_QUAD_BEAUTY
@ MOD_TRIANGULATE_NGON_EARCLIP
UndoStack * ED_undo_stack_get()
Definition ed_undo.cc:455
Read Guarded memory(de)allocation.
void uiItemL(uiLayout *layout, const char *name, int icon)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
void uiItemS(uiLayout *layout)
void uiItemFullO_ptr(uiLayout *layout, wmOperatorType *ot, const char *name, int icon, IDProperty *properties, wmOperatorCallContext context, eUI_Item_Flag flag, PointerRNA *r_opptr)
#define UI_ITEM_NONE
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_SCENE
Definition WM_types.hh:345
#define ND_TOOLSETTINGS
Definition WM_types.hh:416
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
bool BM_data_layer_free_named(BMesh *bm, CustomData *data, const char *name)
void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const char *name)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_log_free(BMLog *log)
Definition bmesh_log.cc:545
BMLog * BM_log_create(BMesh *bm)
Definition bmesh_log.cc:467
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
#define BMALLOC_TEMPLATE_FROM_ME(...)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
bool BM_attribute_stored_in_bmesh_builtin(const StringRef name)
void BM_mesh_normals_update(BMesh *bm)
void BM_mesh_triangulate(BMesh *bm, const int quad_method, const int ngon_method, const int min_vertices, const bool tag_only, BMOperator *op, BMOpSlot *slot_facemap_out, BMOpSlot *slot_facemap_double_out)
const Depsgraph * depsgraph
#define G(x, y, z)
void enable_ex(Main &bmain, Depsgraph &depsgraph, Object &ob)
static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *)
void disable_with_undo(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
static void disable(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob, undo::StepData *undo_step)
static bool dyntopo_supports_layer(const CustomDataLayer &layer)
static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum WarnFlag flag)
WarnFlag check_attribute_warning(Scene &scene, Object &ob)
void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot)
static bool dyntopo_supports_customdata_layers(const Span< CustomDataLayer > layers)
static void enable_with_undo(Main &bmain, Depsgraph &depsgraph, const Scene &scene, Object &ob)
static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, const wmEvent *)
void push_node(const Depsgraph &depsgraph, const Object &object, const bke::pbvh::Node *node, Type type)
void push_begin_ex(const Scene &, Object &ob, const char *name)
void restore_from_bmesh_enter_geometry(const StepData &step_data, Mesh &mesh)
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3560
CustomData vdata
int totloop
CustomData pdata
int totface
struct ModifierData * next
ModifierTypeType type
struct SculptSession * sculpt
BMLog * bm_log
Definition BKE_paint.hh:402
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
struct wmOperatorType * type
void WM_cursor_wait(bool val)
void WM_main_add_notifier(uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
uint8_t flag
Definition wm_window.cc:138