Fix some inconsistencies in object visibility/selectability tests.
[blender.git] / source / blender / editors / object / object_facemap_ops.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  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/object/object_facemap_ops.c
28  *  \ingroup edobj
29  */
30
31 #include <string.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_utildefines.h"
36 #include "BLI_path_util.h"
37 #include "BLI_string.h"
38 #include "BLI_listbase.h"
39
40 #include "DNA_object_types.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_workspace_types.h"
43
44 #include "BKE_context.h"
45 #include "BKE_customdata.h"
46 #include "BKE_editmesh.h"
47 #include "BKE_object.h"
48 #include "BKE_object_facemap.h"
49 #include "BKE_object_deform.h"
50
51 #include "DEG_depsgraph.h"
52
53 #include "RNA_define.h"
54 #include "RNA_access.h"
55
56 #include "WM_types.h"
57 #include "WM_api.h"
58
59 #include "ED_mesh.h"
60 #include "ED_object.h"
61
62 #include "object_intern.h"
63
64 /* called while not in editmode */
65 void ED_object_facemap_face_add(Object *ob, bFaceMap *fmap, int facenum)
66 {
67         int fmap_nr;
68         if (GS(((ID *)ob->data)->name) != ID_ME)
69                 return;
70
71         /* get the face map number, exit if it can't be found */
72         fmap_nr = BLI_findindex(&ob->fmaps, fmap);
73
74         if (fmap_nr != -1) {
75                 int *facemap;
76                 Mesh *me = ob->data;
77
78                 /* if there's is no facemap layer then create one */
79                 if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL)
80                         facemap = CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_DEFAULT, NULL, me->totpoly);
81
82                 facemap[facenum] = fmap_nr;
83         }
84 }
85
86 /* called while not in editmode */
87 void ED_object_facemap_face_remove(Object *ob, bFaceMap *fmap, int facenum)
88 {
89         int fmap_nr;
90         if (GS(((ID *)ob->data)->name) != ID_ME)
91                 return;
92
93         /* get the face map number, exit if it can't be found */
94         fmap_nr = BLI_findindex(&ob->fmaps, fmap);
95
96         if (fmap_nr != -1) {
97                 int *facemap;
98                 Mesh *me = ob->data;
99
100                 if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL)
101                         return;
102
103                 facemap[facenum] = -1;
104         }
105 }
106
107 static void object_fmap_swap_edit_mode(Object *ob, int num1, int num2)
108 {
109         if (ob->type == OB_MESH) {
110                 Mesh *me = ob->data;
111
112                 if (me->edit_btmesh) {
113                         BMEditMesh *em = me->edit_btmesh;
114                         const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
115
116                         if (cd_fmap_offset != -1) {
117                                 BMFace *efa;
118                                 BMIter iter;
119                                 int *map;
120
121                                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
122                                         map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
123
124                                         if (map) {
125                                                 if (num1 != -1) {
126                                                         if (*map == num1)
127                                                                 *map = num2;
128                                                         else if (*map == num2)
129                                                                 *map = num1;
130                                                 }
131                                         }
132                                 }
133                         }
134                 }
135         }
136 }
137
138 static void object_fmap_swap_object_mode(Object *ob, int num1, int num2)
139 {
140         if (ob->type == OB_MESH) {
141                 Mesh *me = ob->data;
142
143                 if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
144                         int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP);
145                         int i;
146
147                         if (map) {
148                                 for (i = 0; i < me->totpoly; i++) {
149                                         if (num1 != -1) {
150                                                 if (map[i] == num1)
151                                                         map[i] = num2;
152                                                 else if (map[i] == num2)
153                                                         map[i] = num1;
154                                         }
155                                 }
156                         }
157                 }
158         }
159 }
160
161 static void object_facemap_swap(Object *ob, int num1, int num2)
162 {
163         if (BKE_object_is_in_editmode(ob))
164                 object_fmap_swap_edit_mode(ob, num1, num2);
165         else
166                 object_fmap_swap_object_mode(ob, num1, num2);
167 }
168
169 static bool face_map_supported_poll(bContext *C)
170 {
171         Object *ob = ED_object_context(C);
172         ID *data = (ob) ? ob->data : NULL;
173         return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib);
174 }
175
176 static bool face_map_supported_edit_mode_poll(bContext *C)
177 {
178         Object *ob = ED_object_context(C);
179         ID *data = (ob) ? ob->data : NULL;
180         if (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib) {
181                 if (ob->mode == OB_MODE_EDIT) {
182                         return true;
183                 }
184         }
185         return false;
186 }
187
188 static int face_map_add_exec(bContext *C, wmOperator *UNUSED(op))
189 {
190         Object *ob = ED_object_context(C);
191
192         BKE_object_facemap_add(ob);
193         DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
194         WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
195         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
196
197         return OPERATOR_FINISHED;
198 }
199
200 void OBJECT_OT_face_map_add(struct wmOperatorType *ot)
201 {
202         /* identifiers */
203         ot->name = "Add Face Map";
204         ot->idname = "OBJECT_OT_face_map_add";
205         ot->description = "Add a new face map to the active object";
206
207         /* api callbacks */
208         ot->poll = face_map_supported_poll;
209         ot->exec = face_map_add_exec;
210
211         /* flags */
212         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
213 }
214
215 static int face_map_remove_exec(bContext *C, wmOperator *UNUSED(op))
216 {
217         Object *ob = ED_object_context(C);
218         bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
219
220         if (fmap) {
221                 BKE_object_facemap_remove(ob, fmap);
222                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
223                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
224                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
225         }
226         return OPERATOR_FINISHED;
227 }
228
229 void OBJECT_OT_face_map_remove(struct wmOperatorType *ot)
230 {
231         /* identifiers */
232         ot->name = "Remove Face Map";
233         ot->idname = "OBJECT_OT_face_map_remove";
234         ot->description = "Remove a face map from the active object";
235
236         /* api callbacks */
237         ot->poll = face_map_supported_poll;
238         ot->exec = face_map_remove_exec;
239
240         /* flags */
241         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
242 }
243
244 static int face_map_assign_exec(bContext *C, wmOperator *UNUSED(op))
245 {
246         Object *ob = ED_object_context(C);
247         bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
248
249         if (fmap) {
250                 Mesh *me = ob->data;
251                 BMEditMesh *em = me->edit_btmesh;
252                 BMFace *efa;
253                 BMIter iter;
254                 int *map;
255                 int cd_fmap_offset;
256
257                 if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP))
258                         BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP);
259
260                 cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
261
262                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
263                         map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
264
265                         if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
266                                 *map = ob->actfmap - 1;
267                         }
268                 }
269
270                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
271                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
272                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
273         }
274         return OPERATOR_FINISHED;
275 }
276
277 void OBJECT_OT_face_map_assign(struct wmOperatorType *ot)
278 {
279         /* identifiers */
280         ot->name = "Assign Face Map";
281         ot->idname = "OBJECT_OT_face_map_assign";
282         ot->description = "Assign faces to a face map";
283
284         /* api callbacks */
285         ot->poll = face_map_supported_edit_mode_poll;
286         ot->exec = face_map_assign_exec;
287
288         /* flags */
289         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
290 }
291
292 static int face_map_remove_from_exec(bContext *C, wmOperator *UNUSED(op))
293 {
294         Object *ob = ED_object_context(C);
295         bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
296
297         if (fmap) {
298                 Mesh *me = ob->data;
299                 BMEditMesh *em = me->edit_btmesh;
300                 BMFace *efa;
301                 BMIter iter;
302                 int *map;
303                 int cd_fmap_offset;
304                 int mapindex = ob->actfmap - 1;
305
306                 if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP))
307                         return OPERATOR_CANCELLED;
308
309                 cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
310
311                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
312                         map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
313
314                         if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && *map == mapindex) {
315                                 *map = -1;
316                         }
317                 }
318
319                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
320                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
321                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
322         }
323         return OPERATOR_FINISHED;
324 }
325
326 void OBJECT_OT_face_map_remove_from(struct wmOperatorType *ot)
327 {
328         /* identifiers */
329         ot->name = "Remove From Face Map";
330         ot->idname = "OBJECT_OT_face_map_remove_from";
331         ot->description = "Remove faces from a face map";
332
333         /* api callbacks */
334         ot->poll = face_map_supported_edit_mode_poll;
335         ot->exec = face_map_remove_from_exec;
336
337         /* flags */
338         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
339 }
340
341 static void fmap_select(Object *ob, bool select)
342 {
343         Mesh *me = ob->data;
344         BMEditMesh *em = me->edit_btmesh;
345         BMFace *efa;
346         BMIter iter;
347         int *map;
348         int cd_fmap_offset;
349         int mapindex = ob->actfmap - 1;
350
351         if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP))
352                 BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP);
353
354         cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
355
356         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
357                 map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
358
359                 if (*map == mapindex) {
360                         BM_face_select_set(em->bm, efa, select);
361                 }
362         }
363 }
364
365 static int face_map_select_exec(bContext *C, wmOperator *UNUSED(op))
366 {
367         Object *ob = ED_object_context(C);
368         bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
369
370         if (fmap) {
371                 fmap_select(ob, true);
372
373                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
374                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
375                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
376         }
377         return OPERATOR_FINISHED;
378 }
379
380 void OBJECT_OT_face_map_select(struct wmOperatorType *ot)
381 {
382         /* identifiers */
383         ot->name = "Select Face Map Faces";
384         ot->idname = "OBJECT_OT_face_map_select";
385         ot->description = "Select faces belonging to a face map";
386
387         /* api callbacks */
388         ot->poll = face_map_supported_edit_mode_poll;
389         ot->exec = face_map_select_exec;
390
391         /* flags */
392         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
393 }
394
395 static int face_map_deselect_exec(bContext *C, wmOperator *UNUSED(op))
396 {
397         Object *ob = ED_object_context(C);
398         bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
399
400         if (fmap) {
401                 fmap_select(ob, false);
402
403                 DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
404                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
405                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
406         }
407         return OPERATOR_FINISHED;
408 }
409
410 void OBJECT_OT_face_map_deselect(struct wmOperatorType *ot)
411 {
412         /* identifiers */
413         ot->name = "Deselect Face Map Faces";
414         ot->idname = "OBJECT_OT_face_map_deselect";
415         ot->description = "Deselect faces belonging to a face map";
416
417         /* api callbacks */
418         ot->poll = face_map_supported_edit_mode_poll;
419         ot->exec = face_map_deselect_exec;
420
421         /* flags */
422         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
423 }
424
425
426 static int face_map_move_exec(bContext *C, wmOperator *op)
427 {
428         Object *ob = ED_object_context(C);
429         bFaceMap *fmap;
430         int dir = RNA_enum_get(op->ptr, "direction");
431         int pos1, pos2 = -1, count;
432
433         fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
434         if (!fmap) {
435                 return OPERATOR_CANCELLED;
436         }
437
438         count = BLI_listbase_count(&ob->fmaps);
439         pos1 = BLI_findindex(&ob->fmaps, fmap);
440
441         if (dir == 1) { /*up*/
442                 void *prev = fmap->prev;
443
444                 if (prev) {
445                         pos2 = pos1 - 1;
446                 }
447                 else {
448                         pos2 = count - 1;
449                 }
450
451                 BLI_remlink(&ob->fmaps, fmap);
452                 BLI_insertlinkbefore(&ob->fmaps, prev, fmap);
453         }
454         else { /*down*/
455                 void *next = fmap->next;
456
457                 if (next) {
458                         pos2 = pos1 + 1;
459                 }
460                 else {
461                         pos2 = 0;
462                 }
463
464                 BLI_remlink(&ob->fmaps, fmap);
465                 BLI_insertlinkafter(&ob->fmaps, next, fmap);
466         }
467
468         /* iterate through mesh and substitute the indices as necessary */
469         object_facemap_swap(ob, pos2, pos1);
470
471         ob->actfmap = pos2 + 1;
472
473         DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
474         WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob);
475
476         return OPERATOR_FINISHED;
477 }
478
479
480 void OBJECT_OT_face_map_move(wmOperatorType *ot)
481 {
482         static EnumPropertyItem fmap_slot_move[] = {
483                 {1, "UP", 0, "Up", ""},
484                 {-1, "DOWN", 0, "Down", ""},
485                 {0, NULL, 0, NULL, NULL}
486         };
487
488         /* identifiers */
489         ot->name = "Move Face Map";
490         ot->idname = "OBJECT_OT_face_map_move";
491         ot->description = "Move the active face map up/down in the list";
492
493         /* api callbacks */
494         ot->poll = face_map_supported_poll;
495         ot->exec = face_map_move_exec;
496
497         /* flags */
498         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
499
500         RNA_def_enum(ot->srna, "direction", fmap_slot_move, 0, "Direction", "Direction to move, UP or DOWN");
501 }