fcf7d2eba68cf8b38e689e6308ece33e27020154
[blender-staging.git] / source / blender / editors / sculpt_paint / paint_hide.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software  Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2010 by Nicholas Bishop
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s):
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  *
29  * Implements the PBVH node hiding operator
30  *
31  */
32
33 /** \file blender/editors/sculpt_paint/paint_hide.c
34  *  \ingroup edsculpt
35  */
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLI_bitmap.h"
40 #include "BLI_listbase.h"
41 #include "BLI_math_vector.h"
42 #include "BLI_pbvh.h"
43 #include "BLI_utildefines.h"
44
45 #include "DNA_mesh_types.h"
46 #include "DNA_meshdata_types.h"
47 #include "DNA_object_types.h"
48 #include "DNA_scene_types.h"
49
50 #include "BKE_context.h"
51 #include "BKE_DerivedMesh.h"
52 #include "BKE_mesh.h"
53 #include "BKE_multires.h"
54 #include "BKE_paint.h"
55 #include "BKE_subsurf.h"
56
57 #include "BIF_glutil.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "ED_screen.h"
63 #include "ED_view3d.h"
64
65 #include "RNA_access.h"
66 #include "RNA_define.h"
67
68 #include "paint_intern.h"
69 #include "sculpt_intern.h" /* for undo push */
70
71 #include <assert.h>
72
73 static int planes_contain_v3(float (*planes)[4], int totplane, const float p[3])
74 {
75         int i;
76
77         for (i = 0; i < totplane; i++) {
78                 if (dot_v3v3(planes[i], p) + planes[i][3] > 0)
79                         return 0;
80         }
81
82         return 1;
83 }
84
85 /* return true if the element should be hidden/shown */
86 static int is_effected(PartialVisArea area,
87                                            float planes[4][4],
88                                            const float co[3])
89 {
90         if (area == PARTIALVIS_ALL)
91                 return 1;
92         else {
93                 int inside = planes_contain_v3(planes, 4, co);
94                 return ((inside && area == PARTIALVIS_INSIDE) ||
95                                 (!inside && area == PARTIALVIS_OUTSIDE));
96         }
97 }
98
99 static void partialvis_update_mesh(Object *ob,
100                                                                    PBVH *pbvh,
101                                                                    PBVHNode *node,
102                                                                    PartialVisAction action,
103                                                                    PartialVisArea area,
104                                                                    float planes[4][4])
105 {
106         MVert *mvert;
107         int *vert_indices;
108         int any_changed = 0, any_visible = 0, totvert, i;
109                         
110         BLI_pbvh_node_num_verts(pbvh, node, NULL, &totvert);
111         BLI_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
112
113         sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
114
115         for (i = 0; i < totvert; i++) {
116                 MVert *v = &mvert[vert_indices[i]];
117
118                 /* hide vertex if in the hide volume */
119                 if (is_effected(area, planes, v->co)) {
120                         if (action == PARTIALVIS_HIDE)
121                                 v->flag |= ME_HIDE;
122                         else
123                                 v->flag &= ~ME_HIDE;
124                         any_changed = 1;
125                 }
126
127                 if (!(v->flag & ME_HIDE))
128                         any_visible = 1;
129         }
130
131         if (any_changed) {
132                 BLI_pbvh_node_mark_rebuild_draw(node);
133                 BLI_pbvh_node_fully_hidden_set(node, !any_visible);
134         }
135 }
136
137 /* Hide or show elements in multires grids with a special GridFlags
138    customdata layer. */
139 static void partialvis_update_grids(Object *ob,
140                                                                         PBVH *pbvh,
141                                                                         PBVHNode *node,
142                                                                         PartialVisAction action,
143                                                                         PartialVisArea area,
144                                                                         float planes[4][4])
145 {
146         DMGridData **grids;
147         BLI_bitmap *grid_hidden;
148         int any_visible = 0;
149         int *grid_indices, gridsize, totgrid, any_changed, i;
150
151         /* get PBVH data */
152         BLI_pbvh_node_get_grids(pbvh, node,
153                                                         &grid_indices, &totgrid, NULL, &gridsize,
154                                                         &grids, NULL);
155         grid_hidden = BLI_pbvh_grid_hidden(pbvh);
156         
157         sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
158         
159         any_changed = 0;
160         for (i = 0; i < totgrid; i++) {
161                 int any_hidden = 0;
162                 int g = grid_indices[i], x, y;
163                 BLI_bitmap gh = grid_hidden[g];
164
165                 if (!gh) {
166                         switch(action) {
167                         case PARTIALVIS_HIDE:
168                                 /* create grid flags data */
169                                 gh = grid_hidden[g] = BLI_BITMAP_NEW(gridsize * gridsize,
170                                                                                                          "partialvis_update_grids");
171                                 break;
172                         case PARTIALVIS_SHOW:
173                                 /* entire grid is visible, nothing to show */
174                                 continue;
175                         }
176                 }
177                 else if (action == PARTIALVIS_SHOW && area == PARTIALVIS_ALL) {
178                         /* special case if we're showing all, just free the
179                            grid */
180                         MEM_freeN(gh);
181                         grid_hidden[g] = NULL;
182                         any_changed = 1;
183                         any_visible = 1;
184                         continue;
185                 }
186
187                 for (y = 0; y < gridsize; y++) {
188                         for (x = 0; x < gridsize; x++) {
189                                 const float *co = grids[g][y * gridsize + x].co;
190
191                                 /* skip grid element if not in the effected area */
192                                 if (is_effected(area, planes, co)) {
193                                         /* set or clear the hide flag */
194                                         BLI_BITMAP_MODIFY(gh, y * gridsize + x,
195                                                                           action == PARTIALVIS_HIDE);
196
197                                         any_changed = 1;
198                                 }
199
200                                 /* keep track of whether any elements are still hidden */
201                                 if (BLI_BITMAP_GET(gh, y * gridsize + x))
202                                         any_hidden = 1;
203                                 else
204                                         any_visible = 1;
205                         }
206                 }
207
208                 /* if everything in the grid is now visible, free the grid
209                    flags */
210                 if (!any_hidden) {
211                         MEM_freeN(gh);
212                         grid_hidden[g] = NULL;
213                 }
214         }
215
216         /* mark updates if anything was hidden/shown */
217         if (any_changed) {
218                 BLI_pbvh_node_mark_rebuild_draw(node);
219                 BLI_pbvh_node_fully_hidden_set(node, !any_visible);
220                 multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED);
221         }
222 }
223
224 static void rect_from_props(rcti *rect, PointerRNA *ptr)
225 {
226         rect->xmin = RNA_int_get(ptr, "xmin");
227         rect->ymin = RNA_int_get(ptr, "ymin");
228         rect->xmax = RNA_int_get(ptr, "xmax");
229         rect->ymax = RNA_int_get(ptr, "ymax");
230 }
231
232 static void clip_planes_from_rect(bContext *C,
233                                                                   float clip_planes[4][4],
234                                                                   const rcti *rect)
235 {
236         ViewContext vc;
237         BoundBox bb;
238         bglMats mats= {{0}};
239         
240         view3d_operator_needs_opengl(C);
241         view3d_set_viewcontext(C, &vc);
242         view3d_get_transformation(vc.ar, vc.rv3d, vc.obact, &mats);
243         ED_view3d_calc_clipping(&bb, clip_planes, &mats, rect);
244         mul_m4_fl(clip_planes, -1.0f);
245 }
246
247 /* If mode is inside, get all PBVH nodes that lie at least partially
248    inside the clip_planes volume. If mode is outside, get all nodes
249    that lie at least partially outside the volume. If showing all, get
250    all nodes. */
251 static void get_pbvh_nodes(PBVH *pbvh,
252                                                    PBVHNode ***nodes,
253                                                    int *totnode,
254                                                    float clip_planes[4][4],
255                                                    PartialVisArea mode)
256 {
257         BLI_pbvh_SearchCallback cb;
258
259         /* select search callback */
260         switch(mode) {
261         case PARTIALVIS_INSIDE:
262                 cb = BLI_pbvh_node_planes_contain_AABB;
263                 break;
264         case PARTIALVIS_OUTSIDE:
265                 cb = BLI_pbvh_node_planes_exclude_AABB;
266                 break;
267         case PARTIALVIS_ALL:
268                 cb = NULL;
269         }
270         
271         BLI_pbvh_search_gather(pbvh, cb, clip_planes, nodes, totnode);
272 }
273
274 static int hide_show_exec(bContext *C, wmOperator *op)
275 {
276         ARegion *ar = CTX_wm_region(C);
277         Object *ob = CTX_data_active_object(C);
278         Mesh *me = ob->data;
279         PartialVisAction action;
280         PartialVisArea area;
281         PBVH *pbvh;
282         PBVHNode **nodes;
283         DerivedMesh *dm;
284         PBVHType pbvh_type;
285         float clip_planes[4][4];
286         rcti rect;
287         int totnode, i;
288
289         /* read operator properties */
290         action = RNA_enum_get(op->ptr, "action");
291         area = RNA_enum_get(op->ptr, "area");
292         rect_from_props(&rect, op->ptr);
293
294         clip_planes_from_rect(C, clip_planes, &rect);
295
296         dm = mesh_get_derived_final(CTX_data_scene(C), ob, CD_MASK_BAREMESH);
297         pbvh = dm->getPBVH(ob, dm);
298         ob->sculpt->pbvh = pbvh;
299
300         get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area);
301         pbvh_type = BLI_pbvh_type(pbvh);
302
303         /* start undo */
304         switch(action) {
305         case PARTIALVIS_HIDE:
306                 sculpt_undo_push_begin("Hide area");
307                 break;
308         case PARTIALVIS_SHOW:
309                 sculpt_undo_push_begin("Show area");
310                 break;
311         }
312
313         for (i = 0; i < totnode; i++) {
314                 switch(pbvh_type) {
315                 case PBVH_FACES:
316                         partialvis_update_mesh(ob, pbvh, nodes[i], action, area, clip_planes);
317                         break;
318                 case PBVH_GRIDS:
319                         partialvis_update_grids(ob, pbvh, nodes[i], action, area, clip_planes);
320                         break;
321                 }
322         }
323
324         if (nodes)
325                 MEM_freeN(nodes);
326         
327         /* end undo */
328         sculpt_undo_push_end();
329
330         /* ensure that edges and faces get hidden as well (not used by
331            sculpt but it looks wrong when entering editmode otherwise) */
332         if (pbvh_type == PBVH_FACES) {
333                 mesh_flush_hidden_from_verts(me->mvert, me->mloop,
334                                                                          me->medge, me->totedge,
335                                                                          me->mpoly, me->totpoly);
336         }
337
338         ED_region_tag_redraw(ar);
339         
340         return OPERATOR_FINISHED;
341 }
342
343 static int hide_show_invoke(bContext *C, wmOperator *op, wmEvent *event)
344 {
345         PartialVisArea area = RNA_enum_get(op->ptr, "area");
346
347         if (area != PARTIALVIS_ALL)
348                 return WM_border_select_invoke(C, op, event);
349         else
350                 return op->type->exec(C, op);
351 }
352
353 void PAINT_OT_hide_show(struct wmOperatorType *ot)
354 {
355         static EnumPropertyItem action_items[] = {
356                 {PARTIALVIS_HIDE, "HIDE", 0, "Hide", "Hide vertices"},
357                 {PARTIALVIS_SHOW, "SHOW", 0, "Show", "Show vertices"},
358                 {0}};
359
360         static EnumPropertyItem area_items[] = {
361                 {PARTIALVIS_OUTSIDE, "OUTSIDE", 0, "Outside",
362                  "Hide or show vertices outside the selection"},
363                 {PARTIALVIS_INSIDE, "INSIDE", 0, "Inside",
364                  "Hide or show vertices inside the selection"},
365                 {PARTIALVIS_ALL, "ALL", 0, "All",
366                  "Hide or show all vertices"},
367                 {0}};
368         
369         /* identifiers */
370         ot->name = "Hide/Show";
371         ot->idname = "PAINT_OT_hide_show";
372
373         /* api callbacks */
374         ot->invoke = hide_show_invoke;
375         ot->modal = WM_border_select_modal;
376         ot->exec = hide_show_exec;
377         /* sculpt-only for now */
378         ot->poll = sculpt_mode_poll;
379
380         ot->flag = OPTYPE_REGISTER;
381
382         /* rna */
383         RNA_def_enum(ot->srna, "action", action_items, PARTIALVIS_HIDE,
384                                  "Action", "Whether to hide or show vertices");
385         RNA_def_enum(ot->srna, "area", area_items, PARTIALVIS_INSIDE,
386                                  "Area", "Which vertices to hide or show");
387         
388         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
389         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
390         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
391         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
392 }