Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / sculpt_paint / paint_hide.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software  Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2010 by Nicholas Bishop
17  * All rights reserved.
18  * Implements the PBVH node hiding operator
19  */
20
21 /** \file
22  * \ingroup edsculpt
23  */
24
25 #include "MEM_guardedalloc.h"
26
27 #include "BLI_bitmap.h"
28 #include "BLI_math_vector.h"
29 #include "BLI_utildefines.h"
30
31 #include "DNA_mesh_types.h"
32 #include "DNA_meshdata_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_scene_types.h"
35
36 #include "BKE_pbvh.h"
37 #include "BKE_ccg.h"
38 #include "BKE_context.h"
39 #include "BKE_mesh.h"
40 #include "BKE_mesh_runtime.h"
41 #include "BKE_multires.h"
42 #include "BKE_paint.h"
43 #include "BKE_subsurf.h"
44
45 #include "DEG_depsgraph.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_screen.h"
51 #include "ED_view3d.h"
52
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55
56 #include "bmesh.h"
57
58 #include "paint_intern.h"
59 #include "sculpt_intern.h" /* for undo push */
60
61 #include <assert.h>
62
63 /* return true if the element should be hidden/shown */
64 static bool is_effected(PartialVisArea area,
65                         float planes[4][4],
66                         const float co[3],
67                         const float mask)
68 {
69   if (area == PARTIALVIS_ALL) {
70     return 1;
71   }
72   else if (area == PARTIALVIS_MASKED) {
73     return mask > 0.5f;
74   }
75   else {
76     bool inside = isect_point_planes_v3(planes, 4, co);
77     return ((inside && area == PARTIALVIS_INSIDE) || (!inside && area == PARTIALVIS_OUTSIDE));
78   }
79 }
80
81 static void partialvis_update_mesh(Object *ob,
82                                    PBVH *pbvh,
83                                    PBVHNode *node,
84                                    PartialVisAction action,
85                                    PartialVisArea area,
86                                    float planes[4][4])
87 {
88   Mesh *me = ob->data;
89   MVert *mvert;
90   const float *paint_mask;
91   const int *vert_indices;
92   int totvert, i;
93   bool any_changed = false, any_visible = false;
94
95   BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert);
96   BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
97   paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
98
99   sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
100
101   for (i = 0; i < totvert; i++) {
102     MVert *v = &mvert[vert_indices[i]];
103     float vmask = paint_mask ? paint_mask[vert_indices[i]] : 0;
104
105     /* hide vertex if in the hide volume */
106     if (is_effected(area, planes, v->co, vmask)) {
107       if (action == PARTIALVIS_HIDE) {
108         v->flag |= ME_HIDE;
109       }
110       else {
111         v->flag &= ~ME_HIDE;
112       }
113       any_changed = true;
114     }
115
116     if (!(v->flag & ME_HIDE)) {
117       any_visible = true;
118     }
119   }
120
121   if (any_changed) {
122     BKE_pbvh_node_mark_rebuild_draw(node);
123     BKE_pbvh_node_fully_hidden_set(node, !any_visible);
124   }
125 }
126
127 /* Hide or show elements in multires grids with a special GridFlags
128  * customdata layer. */
129 static void partialvis_update_grids(Object *ob,
130                                     PBVH *pbvh,
131                                     PBVHNode *node,
132                                     PartialVisAction action,
133                                     PartialVisArea area,
134                                     float planes[4][4])
135 {
136   CCGElem **grids;
137   CCGKey key;
138   BLI_bitmap **grid_hidden;
139   int *grid_indices, totgrid, i;
140   bool any_changed = false, any_visible = false;
141
142   /* get PBVH data */
143   BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids);
144   grid_hidden = BKE_pbvh_grid_hidden(pbvh);
145   BKE_pbvh_get_grid_key(pbvh, &key);
146
147   sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
148
149   for (i = 0; i < totgrid; i++) {
150     int any_hidden = 0;
151     int g = grid_indices[i], x, y;
152     BLI_bitmap *gh = grid_hidden[g];
153
154     if (!gh) {
155       switch (action) {
156         case PARTIALVIS_HIDE:
157           /* create grid flags data */
158           gh = grid_hidden[g] = BLI_BITMAP_NEW(key.grid_area, "partialvis_update_grids");
159           break;
160         case PARTIALVIS_SHOW:
161           /* entire grid is visible, nothing to show */
162           continue;
163       }
164     }
165     else if (action == PARTIALVIS_SHOW && area == PARTIALVIS_ALL) {
166       /* special case if we're showing all, just free the
167        * grid */
168       MEM_freeN(gh);
169       grid_hidden[g] = NULL;
170       any_changed = true;
171       any_visible = true;
172       continue;
173     }
174
175     for (y = 0; y < key.grid_size; y++) {
176       for (x = 0; x < key.grid_size; x++) {
177         CCGElem *elem = CCG_grid_elem(&key, grids[g], x, y);
178         const float *co = CCG_elem_co(&key, elem);
179         float mask = key.has_mask ? *CCG_elem_mask(&key, elem) : 0.0f;
180
181         /* skip grid element if not in the effected area */
182         if (is_effected(area, planes, co, mask)) {
183           /* set or clear the hide flag */
184           BLI_BITMAP_SET(gh, y * key.grid_size + x, action == PARTIALVIS_HIDE);
185
186           any_changed = true;
187         }
188
189         /* keep track of whether any elements are still hidden */
190         if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) {
191           any_hidden = true;
192         }
193         else {
194           any_visible = true;
195         }
196       }
197     }
198
199     /* if everything in the grid is now visible, free the grid
200      * flags */
201     if (!any_hidden) {
202       MEM_freeN(gh);
203       grid_hidden[g] = NULL;
204     }
205   }
206
207   /* mark updates if anything was hidden/shown */
208   if (any_changed) {
209     BKE_pbvh_node_mark_rebuild_draw(node);
210     BKE_pbvh_node_fully_hidden_set(node, !any_visible);
211     multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED);
212   }
213 }
214
215 static void partialvis_update_bmesh_verts(BMesh *bm,
216                                           GSet *verts,
217                                           PartialVisAction action,
218                                           PartialVisArea area,
219                                           float planes[4][4],
220                                           bool *any_changed,
221                                           bool *any_visible)
222 {
223   GSetIterator gs_iter;
224
225   GSET_ITER (gs_iter, verts) {
226     BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
227     float *vmask = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK);
228
229     /* hide vertex if in the hide volume */
230     if (is_effected(area, planes, v->co, *vmask)) {
231       if (action == PARTIALVIS_HIDE) {
232         BM_elem_flag_enable(v, BM_ELEM_HIDDEN);
233       }
234       else {
235         BM_elem_flag_disable(v, BM_ELEM_HIDDEN);
236       }
237       (*any_changed) = true;
238     }
239
240     if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
241       (*any_visible) = true;
242     }
243   }
244 }
245
246 static void partialvis_update_bmesh_faces(GSet *faces)
247 {
248   GSetIterator gs_iter;
249
250   GSET_ITER (gs_iter, faces) {
251     BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
252
253     if (paint_is_bmesh_face_hidden(f)) {
254       BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
255     }
256     else {
257       BM_elem_flag_disable(f, BM_ELEM_HIDDEN);
258     }
259   }
260 }
261
262 static void partialvis_update_bmesh(Object *ob,
263                                     PBVH *pbvh,
264                                     PBVHNode *node,
265                                     PartialVisAction action,
266                                     PartialVisArea area,
267                                     float planes[4][4])
268 {
269   BMesh *bm;
270   GSet *unique, *other, *faces;
271   bool any_changed = false, any_visible = false;
272
273   bm = BKE_pbvh_get_bmesh(pbvh);
274   unique = BKE_pbvh_bmesh_node_unique_verts(node);
275   other = BKE_pbvh_bmesh_node_other_verts(node);
276   faces = BKE_pbvh_bmesh_node_faces(node);
277
278   sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
279
280   partialvis_update_bmesh_verts(bm, unique, action, area, planes, &any_changed, &any_visible);
281
282   partialvis_update_bmesh_verts(bm, other, action, area, planes, &any_changed, &any_visible);
283
284   /* finally loop over node faces and tag the ones that are fully hidden */
285   partialvis_update_bmesh_faces(faces);
286
287   if (any_changed) {
288     BKE_pbvh_node_mark_rebuild_draw(node);
289     BKE_pbvh_node_fully_hidden_set(node, !any_visible);
290   }
291 }
292
293 static void rect_from_props(rcti *rect, PointerRNA *ptr)
294 {
295   rect->xmin = RNA_int_get(ptr, "xmin");
296   rect->ymin = RNA_int_get(ptr, "ymin");
297   rect->xmax = RNA_int_get(ptr, "xmax");
298   rect->ymax = RNA_int_get(ptr, "ymax");
299 }
300
301 static void clip_planes_from_rect(bContext *C, float clip_planes[4][4], const rcti *rect)
302 {
303   ViewContext vc;
304   BoundBox bb;
305
306   view3d_operator_needs_opengl(C);
307   ED_view3d_viewcontext_init(C, &vc);
308   ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, rect);
309   negate_m4(clip_planes);
310 }
311
312 /* If mode is inside, get all PBVH nodes that lie at least partially
313  * inside the clip_planes volume. If mode is outside, get all nodes
314  * that lie at least partially outside the volume. If showing all, get
315  * all nodes. */
316 static void get_pbvh_nodes(
317     PBVH *pbvh, PBVHNode ***nodes, int *totnode, float clip_planes[4][4], PartialVisArea mode)
318 {
319   BKE_pbvh_SearchCallback cb = NULL;
320
321   /* select search callback */
322   switch (mode) {
323     case PARTIALVIS_INSIDE:
324       cb = BKE_pbvh_node_planes_contain_AABB;
325       break;
326     case PARTIALVIS_OUTSIDE:
327       cb = BKE_pbvh_node_planes_exclude_AABB;
328       break;
329     case PARTIALVIS_ALL:
330     case PARTIALVIS_MASKED:
331       break;
332   }
333
334   BKE_pbvh_search_gather(pbvh, cb, clip_planes, nodes, totnode);
335 }
336
337 static int hide_show_exec(bContext *C, wmOperator *op)
338 {
339   ARegion *ar = CTX_wm_region(C);
340   Object *ob = CTX_data_active_object(C);
341   Depsgraph *depsgraph = CTX_data_depsgraph(C);
342   Mesh *me = ob->data;
343   PartialVisAction action;
344   PartialVisArea area;
345   PBVH *pbvh;
346   PBVHNode **nodes;
347   PBVHType pbvh_type;
348   float clip_planes[4][4];
349   rcti rect;
350   int totnode, i;
351
352   /* read operator properties */
353   action = RNA_enum_get(op->ptr, "action");
354   area = RNA_enum_get(op->ptr, "area");
355   rect_from_props(&rect, op->ptr);
356
357   clip_planes_from_rect(C, clip_planes, &rect);
358
359   pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
360   BLI_assert(ob->sculpt->pbvh == pbvh);
361
362   get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area);
363   pbvh_type = BKE_pbvh_type(pbvh);
364
365   /* start undo */
366   switch (action) {
367     case PARTIALVIS_HIDE:
368       sculpt_undo_push_begin("Hide area");
369       break;
370     case PARTIALVIS_SHOW:
371       sculpt_undo_push_begin("Show area");
372       break;
373   }
374
375   for (i = 0; i < totnode; i++) {
376     switch (pbvh_type) {
377       case PBVH_FACES:
378         partialvis_update_mesh(ob, pbvh, nodes[i], action, area, clip_planes);
379         break;
380       case PBVH_GRIDS:
381         partialvis_update_grids(ob, pbvh, nodes[i], action, area, clip_planes);
382         break;
383       case PBVH_BMESH:
384         partialvis_update_bmesh(ob, pbvh, nodes[i], action, area, clip_planes);
385         break;
386     }
387   }
388
389   if (nodes) {
390     MEM_freeN(nodes);
391   }
392
393   /* end undo */
394   sculpt_undo_push_end();
395
396   /* ensure that edges and faces get hidden as well (not used by
397    * sculpt but it looks wrong when entering editmode otherwise) */
398   if (pbvh_type == PBVH_FACES) {
399     BKE_mesh_flush_hidden_from_verts(me);
400   }
401
402   ED_region_tag_redraw(ar);
403
404   return OPERATOR_FINISHED;
405 }
406
407 static int hide_show_invoke(bContext *C, wmOperator *op, const wmEvent *event)
408 {
409   PartialVisArea area = RNA_enum_get(op->ptr, "area");
410
411   if (!ELEM(area, PARTIALVIS_ALL, PARTIALVIS_MASKED)) {
412     return WM_gesture_box_invoke(C, op, event);
413   }
414   else {
415     return op->type->exec(C, op);
416   }
417 }
418
419 void PAINT_OT_hide_show(struct wmOperatorType *ot)
420 {
421   static const EnumPropertyItem action_items[] = {
422       {PARTIALVIS_HIDE, "HIDE", 0, "Hide", "Hide vertices"},
423       {PARTIALVIS_SHOW, "SHOW", 0, "Show", "Show vertices"},
424       {0, NULL, 0, NULL, NULL},
425   };
426
427   static const EnumPropertyItem area_items[] = {
428       {PARTIALVIS_OUTSIDE, "OUTSIDE", 0, "Outside", "Hide or show vertices outside the selection"},
429       {PARTIALVIS_INSIDE, "INSIDE", 0, "Inside", "Hide or show vertices inside the selection"},
430       {PARTIALVIS_ALL, "ALL", 0, "All", "Hide or show all vertices"},
431       {PARTIALVIS_MASKED,
432        "MASKED",
433        0,
434        "Masked",
435        "Hide or show vertices that are masked (minimum mask value of 0.5)"},
436       {0, NULL, 0, NULL, NULL},
437   };
438
439   /* identifiers */
440   ot->name = "Hide/Show";
441   ot->idname = "PAINT_OT_hide_show";
442   ot->description = "Hide/show some vertices";
443
444   /* api callbacks */
445   ot->invoke = hide_show_invoke;
446   ot->modal = WM_gesture_box_modal;
447   ot->exec = hide_show_exec;
448   /* sculpt-only for now */
449   ot->poll = sculpt_mode_poll_view3d;
450
451   ot->flag = OPTYPE_REGISTER;
452
453   /* rna */
454   RNA_def_enum(ot->srna,
455                "action",
456                action_items,
457                PARTIALVIS_HIDE,
458                "Action",
459                "Whether to hide or show vertices");
460   RNA_def_enum(
461       ot->srna, "area", area_items, PARTIALVIS_INSIDE, "Area", "Which vertices to hide or show");
462
463   WM_operator_properties_border(ot);
464 }