Move editmesh undo into its own file
[blender.git] / source / blender / editors / mesh / editmesh_undo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/mesh/editmesh_undo.c
22  *  \ingroup edmesh
23  */
24
25 #include "MEM_guardedalloc.h"
26
27 #include "DNA_mesh_types.h"
28 #include "DNA_object_types.h"
29 #include "DNA_key_types.h"
30
31 #include "BLI_listbase.h"
32
33 #include "BKE_DerivedMesh.h"
34 #include "BKE_context.h"
35 #include "BKE_key.h"
36 #include "BKE_mesh.h"
37 #include "BKE_editmesh.h"
38
39 #include "ED_mesh.h"
40 #include "ED_util.h"
41
42
43 /* for callbacks */
44
45 static void *getEditMesh(bContext *C)
46 {
47         Object *obedit = CTX_data_edit_object(C);
48         if (obedit && obedit->type == OB_MESH) {
49                 Mesh *me = obedit->data;
50                 return me->edit_btmesh;
51         }
52         return NULL;
53 }
54
55 typedef struct UndoMesh {
56         Mesh me;
57         int selectmode;
58
59         /** \note
60          * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]),
61          * but editing shape keys, going into object mode, removing or changing their order,
62          * then go back into editmode and undo will give issues - where the old index will be out of sync
63          * with the new object index.
64          *
65          * There are a few ways this could be made to work but for now its a known limitation with mixing
66          * object and editmode operations - Campbell */
67         int shapenr;
68 } UndoMesh;
69
70 /* undo simply makes copies of a bmesh */
71 static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
72 {
73         BMEditMesh *em = emv;
74         Mesh *obme = obdata;
75
76         UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh");
77
78         /* make sure shape keys work */
79         um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL;
80
81         /* BM_mesh_validate(em->bm); */ /* for troubleshooting */
82
83         BM_mesh_bm_to_me(
84                 em->bm, &um->me, (&(struct BMeshToMeshParams){
85                     .cd_mask_extra = CD_MASK_SHAPE_KEYINDEX,
86                 }));
87
88         um->selectmode = em->selectmode;
89         um->shapenr = em->bm->shapenr;
90
91         return um;
92 }
93
94 static void undoMesh_to_editbtMesh(void *umv, void *em_v, void *obdata)
95 {
96         BMEditMesh *em = em_v, *em_tmp;
97         Object *ob = em->ob;
98         UndoMesh *um = umv;
99         BMesh *bm;
100         Key *key = ((Mesh *) obdata)->key;
101
102         const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&um->me);
103
104         em->bm->shapenr = um->shapenr;
105
106         EDBM_mesh_free(em);
107
108         bm = BM_mesh_create(&allocsize);
109
110         BM_mesh_bm_from_me(
111                 bm, &um->me, (&(struct BMeshFromMeshParams){
112                     .calc_face_normal = true, .active_shapekey = um->shapenr,
113                 }));
114
115         em_tmp = BKE_editmesh_create(bm, true);
116         *em = *em_tmp;
117
118         em->selectmode = um->selectmode;
119         bm->selectmode = um->selectmode;
120         em->ob = ob;
121
122         /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens
123          *         if the active is a basis for any other. */
124         if (key && (key->type == KEY_RELATIVE)) {
125                 /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume
126                  * shapenr from restored bmesh and keyblock indices are in sync. */
127                 const int kb_act_idx = ob->shapenr - 1;
128
129                 /* If it is, let's patch the current mesh key block to its restored value.
130                  * Else, the offsets won't be computed and it won't matter. */
131                 if (BKE_keyblock_is_basis(key, kb_act_idx)) {
132                         KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx);
133
134                         if (kb_act->totelem != um->me.totvert) {
135                                 /* The current mesh has some extra/missing verts compared to the undo, adjust. */
136                                 MEM_SAFE_FREE(kb_act->data);
137                                 kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__);
138                                 kb_act->totelem = um->me.totvert;
139                         }
140
141                         BKE_keyblock_update_from_mesh(&um->me, kb_act);
142                 }
143         }
144
145         ob->shapenr = um->shapenr;
146
147         MEM_freeN(em_tmp);
148 }
149
150 static void free_undo(void *me_v)
151 {
152         Mesh *me = me_v;
153         if (me->key) {
154                 BKE_key_free(me->key);
155                 MEM_freeN(me->key);
156         }
157
158         BKE_mesh_free(me, false);
159         MEM_freeN(me);
160 }
161
162 /* and this is all the undo system needs to know */
163 void undo_push_mesh(bContext *C, const char *name)
164 {
165         /* em->ob gets out of date and crashes on mesh undo,
166          * this is an easy way to ensure its OK
167          * though we could investigate the matter further. */
168         Object *obedit = CTX_data_edit_object(C);
169         BMEditMesh *em = BKE_editmesh_from_object(obedit);
170         em->ob = obedit;
171
172         undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
173 }