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