Blender V4.3
object_multires_modifier.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "object_intern.hh"
6
7#include "DNA_mesh_types.h"
9#include "DNA_object_types.h"
10#include "DNA_space_types.h"
12
13#include "BKE_context.hh"
14#include "BKE_customdata.hh"
15#include "BKE_main.hh"
16#include "BKE_multires.hh"
17#include "BKE_paint.hh"
18#include "BKE_report.hh"
19
20#include "BLI_path_utils.hh"
21#include "BLI_string.h"
22
23#include "DEG_depsgraph.hh"
24
25#include "ED_object.hh"
26#include "ED_sculpt.hh"
27
28#include "RNA_access.hh"
29#include "RNA_define.hh"
30#include "RNA_prototypes.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35namespace blender::ed::object {
36
37/* ------------------------------------------------------------------- */
41static bool multires_poll(bContext *C)
42{
43 return edit_modifier_poll_generic(C, &RNA_MultiresModifier, (1 << OB_MESH), true, false);
44}
45
65
67 wmOperator *op,
68 const wmEvent * /*event*/)
69{
72 }
73 return OPERATOR_CANCELLED;
74}
75
77{
78 ot->name = "Delete Higher Levels";
79 ot->description = "Deletes the higher resolution mesh, potential loss of detail";
80 ot->idname = "OBJECT_OT_multires_higher_levels_delete";
81
85
86 /* flags */
89}
90
93/* ------------------------------------------------------------------- */
99 "CATMULL_CLARK",
100 0,
101 "Catmull-Clark",
102 "Create a new level using Catmull-Clark subdivisions"},
104 "SIMPLE",
105 0,
106 "Simple",
107 "Create a new level using simple subdivisions"},
109 "LINEAR",
110 0,
111 "Linear",
112 "Create a new level using linear interpolation of the sculpted displacement"},
113 {0, nullptr, 0, nullptr, nullptr},
114};
115
117{
118 Object *object = context_active_object(C);
120 op, object, eModifierType_Multires);
121
122 if (!mmd) {
123 return OPERATOR_CANCELLED;
124 }
125
127 op->ptr, "mode");
128 multiresModifier_subdivide(object, mmd, subdivide_mode);
129
131
134
135 if (object->mode & OB_MODE_SCULPT) {
136 /* ensure that grid paint mask layer is created */
139 }
140
141 return OPERATOR_FINISHED;
142}
143
144static int multires_subdivide_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
145{
147 return multires_subdivide_exec(C, op);
148 }
149 return OPERATOR_CANCELLED;
150}
151
153{
154 ot->name = "Multires Subdivide";
155 ot->description = "Add a new level of subdivision";
156 ot->idname = "OBJECT_OT_multires_subdivide";
157
161
162 /* flags */
166 "mode",
169 "Subdivision Mode",
170 "How the mesh is going to be subdivided to create a new level");
171}
172
175/* ------------------------------------------------------------------- */
180{
182 Object *ob = context_active_object(C), *secondob = nullptr;
184 op, ob, eModifierType_Multires);
185
186 if (!mmd) {
187 return OPERATOR_CANCELLED;
188 }
189
190 if (mmd->lvl == 0) {
191 BKE_report(op->reports, RPT_ERROR, "Reshape can work only with higher levels of subdivisions");
192 return OPERATOR_CANCELLED;
193 }
194
195 CTX_DATA_BEGIN (C, Object *, selob, selected_editable_objects) {
196 if (selob->type == OB_MESH && selob != ob) {
197 secondob = selob;
198 break;
199 }
200 }
202
203 if (!secondob) {
204 BKE_report(op->reports, RPT_ERROR, "Second selected mesh object required to copy shape from");
205 return OPERATOR_CANCELLED;
206 }
207
208 if (!multiresModifier_reshapeFromObject(depsgraph, mmd, ob, secondob)) {
209 BKE_report(op->reports, RPT_ERROR, "Objects do not have the same number of vertices");
210 return OPERATOR_CANCELLED;
211 }
212
215
216 return OPERATOR_FINISHED;
217}
218
219static int multires_reshape_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
220{
222 return multires_reshape_exec(C, op);
223 }
224 return OPERATOR_CANCELLED;
225}
226
228{
229 ot->name = "Multires Reshape";
230 ot->description = "Copy vertex coordinates from other object";
231 ot->idname = "OBJECT_OT_multires_reshape";
232
236
237 /* flags */
240}
241
244/* ------------------------------------------------------------------- */
249{
250 Main *bmain = CTX_data_main(C);
252 Mesh *mesh = (ob) ? static_cast<Mesh *>(ob->data) : static_cast<Mesh *>(op->customdata);
253 char filepath[FILE_MAX];
254 const bool relative = RNA_boolean_get(op->ptr, "relative_path");
255
256 if (!mesh) {
257 return OPERATOR_CANCELLED;
258 }
259
260 if (CustomData_external_test(&mesh->corner_data, CD_MDISPS)) {
261 return OPERATOR_CANCELLED;
262 }
263
264 RNA_string_get(op->ptr, "filepath", filepath);
265
266 if (relative) {
267 BLI_path_rel(filepath, BKE_main_blendfile_path(bmain));
268 }
269
270 CustomData_external_add(&mesh->corner_data, &mesh->id, CD_MDISPS, mesh->corners_num, filepath);
272 &mesh->corner_data, &mesh->id, CD_MASK_MESH.lmask, mesh->corners_num, 0);
273
274 return OPERATOR_FINISHED;
275}
276
277static int multires_external_save_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
278{
280 Mesh *mesh = static_cast<Mesh *>(ob->data);
281 char filepath[FILE_MAX];
282
284 return OPERATOR_CANCELLED;
285 }
286
288 op, ob, eModifierType_Multires);
289
290 if (!mmd) {
291 return OPERATOR_CANCELLED;
292 }
293
294 if (CustomData_external_test(&mesh->corner_data, CD_MDISPS)) {
295 return OPERATOR_CANCELLED;
296 }
297
298 if (RNA_struct_property_is_set(op->ptr, "filepath")) {
299 return multires_external_save_exec(C, op);
300 }
301
302 op->customdata = mesh;
303
304 SNPRINTF(filepath, "//%s.btx", mesh->id.name + 2);
305 RNA_string_set(op->ptr, "filepath", filepath);
306
308
310}
311
313{
314 ot->name = "Multires Save External";
315 ot->description = "Save displacements to an external file";
316 ot->idname = "OBJECT_OT_multires_external_save";
317
318 /* XXX modifier no longer in context after file browser: `ot->poll = multires_poll;`. */
322
323 /* flags */
325
329 FILE_SAVE,
334}
335
338/* ------------------------------------------------------------------- */
343{
345 Mesh *mesh = static_cast<Mesh *>(ob->data);
346
347 if (!CustomData_external_test(&mesh->corner_data, CD_MDISPS)) {
348 return OPERATOR_CANCELLED;
349 }
350
351 /* XXX don't remove. */
352 CustomData_external_remove(&mesh->corner_data, &mesh->id, CD_MDISPS, mesh->corners_num);
353
354 return OPERATOR_FINISHED;
355}
356
358{
359 ot->name = "Multires Pack External";
360 ot->description = "Pack displacements from an external file";
361 ot->idname = "OBJECT_OT_multires_external_pack";
362
365
366 /* flags */
368}
369
372/* ------------------------------------------------------------------- */
398
399static int multires_base_apply_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
400{
402 return multires_base_apply_exec(C, op);
403 }
404 return OPERATOR_CANCELLED;
405}
406
408{
409 ot->name = "Multires Apply Base";
410 ot->description = "Modify the base mesh to conform to the displaced mesh";
411 ot->idname = "OBJECT_OT_multires_base_apply";
412
416
417 /* flags */
420}
421
424/* ------------------------------------------------------------------- */
429{
431 Object *object = context_active_object(C);
433 op, object, eModifierType_Multires);
434
435 if (!mmd) {
436 return OPERATOR_CANCELLED;
437 }
438
439 int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true);
440 if (new_levels == 0) {
441 BKE_report(op->reports, RPT_ERROR, "No valid subdivisions found to rebuild a lower level");
442 return OPERATOR_CANCELLED;
443 }
444
447
448 return OPERATOR_FINISHED;
449}
450
451static int multires_unsubdivide_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
452{
454 return multires_unsubdivide_exec(C, op);
455 }
456 return OPERATOR_CANCELLED;
457}
458
460{
461 ot->name = "Unsubdivide";
462 ot->description = "Rebuild a lower subdivision level of the current base mesh";
463 ot->idname = "OBJECT_OT_multires_unsubdivide";
464
468
469 /* flags */
472}
473
476/* ------------------------------------------------------------------- */
481{
483 Object *object = context_active_object(C);
485 op, object, eModifierType_Multires);
486
487 if (!mmd) {
488 return OPERATOR_CANCELLED;
489 }
490
491 int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, INT_MAX, false);
492 if (new_levels == 0) {
493 BKE_report(op->reports, RPT_ERROR, "No valid subdivisions found to rebuild lower levels");
494 return OPERATOR_CANCELLED;
495 }
496
497 BKE_reportf(op->reports, RPT_INFO, "%d new levels rebuilt", new_levels);
498
501
502 return OPERATOR_FINISHED;
503}
504
505static int multires_rebuild_subdiv_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
506{
508 return multires_rebuild_subdiv_exec(C, op);
509 }
510 return OPERATOR_CANCELLED;
511}
512
514{
515 ot->name = "Rebuild Lower Subdivisions";
516 ot->description =
517 "Rebuilds all possible subdivisions levels to generate a lower resolution base mesh";
518 ot->idname = "OBJECT_OT_multires_rebuild_subdiv";
519
523
524 /* flags */
527}
528
531} // namespace blender::ed::object
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
#define CTX_DATA_END
CustomData interface, see also DNA_customdata_types.h.
void CustomData_external_remove(CustomData *data, ID *id, eCustomDataType type, int totelem)
void CustomData_external_add(CustomData *data, ID *id, eCustomDataType type, int totelem, const char *filepath)
void CustomData_external_write(CustomData *data, ID *id, eCustomDataMask mask, int totelem, int free)
bool CustomData_external_test(CustomData *data, eCustomDataType type)
const CustomData_MeshMasks CD_MASK_MESH
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:832
bool multiresModifier_reshapeFromObject(Depsgraph *depsgraph, MultiresModifierData *mmd, Object *dst, Object *src)
void multiresModifier_del_levels(MultiresModifierData *mmd, Scene *scene, Object *object, int direction)
Definition multires.cc:704
void multiresModifier_subdivide(Object *object, MultiresModifierData *mmd, eMultiresSubdivideModeType mode)
eMultiresSubdivideModeType
@ MULTIRES_SUBDIVIDE_LINEAR
@ MULTIRES_SUBDIVIDE_CATMULL_CLARK
@ MULTIRES_SUBDIVIDE_SIMPLE
void multiresModifier_base_apply(Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd)
int multiresModifier_rebuild_subdiv(Depsgraph *depsgraph, Object *object, MultiresModifierData *mmd, int rebuild_limit, bool switch_view_to_lower_level)
void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd)
Definition paint.cc:2610
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define FILE_MAX
bool void BLI_path_rel(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ eModifierType_Multires
@ OB_MODE_SCULPT
Object is a sort of wrapper for general info.
@ OB_MESH
@ FILE_SORT_DEFAULT
@ FILE_SPECIAL
@ FILE_TYPE_BTX
@ FILE_TYPE_FOLDER
@ FILE_DEFAULTDISPLAY
@ OPERATOR_RUNNING_MODAL
@ WM_FILESEL_RELPATH
Definition WM_api.hh:933
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:936
@ FILE_SAVE
Definition WM_api.hh:946
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_MODIFIER
Definition WM_types.hh:429
#define NC_OBJECT
Definition WM_types.hh:346
const Depsgraph * depsgraph
static int multires_higher_levels_delete_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int multires_base_apply_exec(bContext *C, wmOperator *op)
static int multires_subdivide_exec(bContext *C, wmOperator *op)
void OBJECT_OT_multires_base_apply(wmOperatorType *ot)
static int multires_external_pack_exec(bContext *C, wmOperator *)
static int multires_external_save_exec(bContext *C, wmOperator *op)
static int multires_rebuild_subdiv_exec(bContext *C, wmOperator *op)
bool edit_modifier_invoke_properties(bContext *C, wmOperator *op)
void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot)
void OBJECT_OT_multires_reshape(wmOperatorType *ot)
bool iter_other(Main *bmain, Object *orig_ob, bool include_orig, bool(*callback)(Object *ob, void *callback_data), void *callback_data)
static int multires_reshape_exec(bContext *C, wmOperator *op)
void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot)
bool multires_update_totlevels(Object *ob, void *totlevel_v)
void OBJECT_OT_multires_subdivide(wmOperatorType *ot)
static int multires_subdivide_invoke(bContext *C, wmOperator *op, const wmEvent *)
bool edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag, bool is_editmode_allowed, bool is_liboverride_allowed)
static int multires_external_save_invoke(bContext *C, wmOperator *op, const wmEvent *)
ModifierData * edit_modifier_property_get(wmOperator *op, Object *ob, int type)
static int multires_unsubdivide_invoke(bContext *C, wmOperator *op, const wmEvent *)
Object * context_active_object(const bContext *C)
void OBJECT_OT_multires_external_save(wmOperatorType *ot)
static int multires_unsubdivide_exec(bContext *C, wmOperator *op)
static int multires_higher_levels_delete_exec(bContext *C, wmOperator *op)
void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot)
static int multires_reshape_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int multires_base_apply_invoke(bContext *C, wmOperator *op, const wmEvent *)
static EnumPropertyItem prop_multires_subdivide_mode_type[]
void OBJECT_OT_multires_external_pack(wmOperatorType *ot)
static int multires_rebuild_subdiv_invoke(bContext *C, wmOperator *op, const wmEvent *)
static bool multires_poll(bContext *C)
void edit_modifier_properties(wmOperatorType *ot)
void push_multires_mesh_begin(bContext *C, const char *str)
void push_multires_mesh_end(bContext *C, const char *str)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
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
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
void WM_event_add_fileselect(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)