Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_view3d / view3d_view.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/space_view3d/view3d_view.c
28  *  \ingroup spview3d
29  */
30
31 #include "DNA_camera_types.h"
32 #include "DNA_scene_types.h"
33 #include "DNA_object_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_math.h"
38 #include "BLI_rect.h"
39 #include "BLI_utildefines.h"
40
41 #include "BKE_action.h"
42 #include "BKE_camera.h"
43 #include "BKE_context.h"
44 #include "BKE_object.h"
45 #include "BKE_global.h"
46 #include "BKE_layer.h"
47 #include "BKE_main.h"
48 #include "BKE_report.h"
49 #include "BKE_scene.h"
50
51 #include "DEG_depsgraph.h"
52 #include "DEG_depsgraph_query.h"
53
54 #include "UI_resources.h"
55
56 #include "GPU_glew.h"
57 #include "GPU_select.h"
58 #include "GPU_matrix.h"
59 #include "GPU_state.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "ED_object.h"
65 #include "ED_screen.h"
66
67 #include "DRW_engine.h"
68
69 #include "view3d_intern.h"  /* own include */
70
71 /* -------------------------------------------------------------------- */
72 /** \name Smooth View Operator & Utilities
73  *
74  * Use for view transitions to have smooth (animated) transitions.
75  * \{ */
76
77 /* This operator is one of the 'timer refresh' ones like animation playback */
78
79 struct SmoothView3DState {
80         float dist;
81         float lens;
82         float quat[4];
83         float ofs[3];
84 };
85
86 struct SmoothView3DStore {
87         /* source*/
88         struct SmoothView3DState src;  /* source */
89         struct SmoothView3DState dst;  /* destination */
90         struct SmoothView3DState org;  /* original */
91
92         bool to_camera;
93
94         bool use_dyn_ofs;
95         float dyn_ofs[3];
96
97         /* When smooth-view is enabled, store the 'rv3d->view' here,
98          * assign back when the view motion is completed. */
99         char org_view;
100
101         double time_allowed;
102 };
103
104 static void view3d_smooth_view_state_backup(struct SmoothView3DState *sms_state,
105                                             const View3D *v3d, const RegionView3D *rv3d)
106 {
107         copy_v3_v3(sms_state->ofs,   rv3d->ofs);
108         copy_qt_qt(sms_state->quat,  rv3d->viewquat);
109         sms_state->dist            = rv3d->dist;
110         sms_state->lens            = v3d->lens;
111 }
112
113 static void view3d_smooth_view_state_restore(const struct SmoothView3DState *sms_state,
114                                              View3D *v3d, RegionView3D *rv3d)
115 {
116         copy_v3_v3(rv3d->ofs,      sms_state->ofs);
117         copy_qt_qt(rv3d->viewquat, sms_state->quat);
118         rv3d->dist               = sms_state->dist;
119         v3d->lens                = sms_state->lens;
120 }
121
122 /* will start timer if appropriate */
123 /* the arguments are the desired situation */
124 void ED_view3d_smooth_view_ex(
125         /* avoid passing in the context */
126         const Depsgraph *depsgraph, wmWindowManager *wm, wmWindow *win, ScrArea *sa,
127         View3D *v3d, ARegion *ar, const int smooth_viewtx,
128         const V3D_SmoothParams *sview)
129 {
130         RegionView3D *rv3d = ar->regiondata;
131         struct SmoothView3DStore sms = {{0}};
132         bool ok = false;
133
134         /* initialize sms */
135         view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d);
136         view3d_smooth_view_state_backup(&sms.src, v3d, rv3d);
137         /* if smoothview runs multiple times... */
138         if (rv3d->sms == NULL) {
139                 view3d_smooth_view_state_backup(&sms.org, v3d, rv3d);
140         }
141         else {
142                 sms.org = rv3d->sms->org;
143         }
144         sms.org_view = rv3d->view;
145
146         /* sms.to_camera = false; */  /* initizlized to zero anyway */
147
148         /* note on camera locking, this is a little confusing but works ok.
149          * we may be changing the view 'as if' there is no active camera, but in fact
150          * there is an active camera which is locked to the view.
151          *
152          * In the case where smooth view is moving _to_ a camera we don't want that
153          * camera to be moved or changed, so only when the camera is not being set should
154          * we allow camera option locking to initialize the view settings from the camera.
155          */
156         if (sview->camera == NULL && sview->camera_old == NULL) {
157                 ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
158         }
159
160         /* store the options we want to end with */
161         if (sview->ofs)
162                 copy_v3_v3(sms.dst.ofs, sview->ofs);
163         if (sview->quat)
164                 copy_qt_qt(sms.dst.quat, sview->quat);
165         if (sview->dist)
166                 sms.dst.dist = *sview->dist;
167         if (sview->lens)
168                 sms.dst.lens = *sview->lens;
169
170         if (sview->dyn_ofs) {
171                 BLI_assert(sview->ofs  == NULL);
172                 BLI_assert(sview->quat != NULL);
173
174                 copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs);
175                 sms.use_dyn_ofs = true;
176
177                 /* calculate the final destination offset */
178                 view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs);
179         }
180
181         if (sview->camera) {
182                 Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
183                 sms.dst.dist = ED_view3d_offset_distance(ob_camera_eval->obmat, sview->ofs, VIEW3D_DIST_FALLBACK);
184                 ED_view3d_from_object(ob_camera_eval, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens);
185                 sms.to_camera = true; /* restore view3d values in end */
186         }
187
188         /* skip smooth viewing for render engine draw */
189         if (smooth_viewtx && v3d->shading.type != OB_RENDER) {
190                 bool changed = false; /* zero means no difference */
191
192                 if (sview->camera_old != sview->camera)
193                         changed = true;
194                 else if (sms.dst.dist != rv3d->dist)
195                         changed = true;
196                 else if (sms.dst.lens != v3d->lens)
197                         changed = true;
198                 else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs))
199                         changed = true;
200                 else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat))
201                         changed = true;
202
203                 /* The new view is different from the old one
204                  * so animate the view */
205                 if (changed) {
206                         /* original values */
207                         if (sview->camera_old) {
208                                 Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old);
209                                 sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, rv3d->ofs, 0.0f);
210                                 /* this */
211                                 ED_view3d_from_object(ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens);
212                         }
213                         /* grid draw as floor */
214                         if ((rv3d->viewlock & RV3D_LOCKED) == 0) {
215                                 /* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
216                                 rv3d->view = RV3D_VIEW_USER;
217                         }
218
219                         sms.time_allowed = (double)smooth_viewtx / 1000.0;
220
221                         /* if this is view rotation only
222                          * we can decrease the time allowed by
223                          * the angle between quats
224                          * this means small rotations wont lag */
225                         if (sview->quat && !sview->ofs && !sview->dist) {
226                                 /* scale the time allowed by the rotation */
227                                 sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / M_PI; /* 180deg == 1.0 */
228                         }
229
230                         /* ensure it shows correct */
231                         if (sms.to_camera) {
232                                 /* use ortho if we move from an ortho view to an ortho camera */
233                                 Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera);
234                                 rv3d->persp = (((rv3d->is_persp == false) &&
235                                                 (ob_camera_eval->type == OB_CAMERA) &&
236                                                 (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ?
237                                                 RV3D_ORTHO : RV3D_PERSP);
238                         }
239
240                         rv3d->rflag |= RV3D_NAVIGATING;
241
242                         /* not essential but in some cases the caller will tag the area for redraw,
243                          * and in that case we can get a flicker of the 'org' user view but we want to see 'src' */
244                         view3d_smooth_view_state_restore(&sms.src, v3d, rv3d);
245
246                         /* keep track of running timer! */
247                         if (rv3d->sms == NULL) {
248                                 rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d");
249                         }
250                         *rv3d->sms = sms;
251                         if (rv3d->smooth_timer) {
252                                 WM_event_remove_timer(wm, win, rv3d->smooth_timer);
253                         }
254                         /* TIMER1 is hardcoded in keymap */
255                         rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); /* max 30 frs/sec */
256
257                         ok = true;
258                 }
259         }
260
261         /* if we get here nothing happens */
262         if (ok == false) {
263                 if (sms.to_camera == false) {
264                         copy_v3_v3(rv3d->ofs, sms.dst.ofs);
265                         copy_qt_qt(rv3d->viewquat, sms.dst.quat);
266                         rv3d->dist = sms.dst.dist;
267                         v3d->lens = sms.dst.lens;
268
269                         ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
270                 }
271
272                 if (rv3d->viewlock & RV3D_BOXVIEW) {
273                         view3d_boxview_copy(sa, ar);
274                 }
275
276                 ED_region_tag_redraw(ar);
277         }
278 }
279
280 void ED_view3d_smooth_view(
281         bContext *C,
282         View3D *v3d, ARegion *ar, const int smooth_viewtx,
283         const struct V3D_SmoothParams *sview)
284 {
285         const Depsgraph *depsgraph = CTX_data_depsgraph(C);
286         wmWindowManager *wm = CTX_wm_manager(C);
287         wmWindow *win = CTX_wm_window(C);
288         ScrArea *sa = CTX_wm_area(C);
289
290         ED_view3d_smooth_view_ex(
291                 depsgraph,
292                 wm, win, sa,
293                 v3d, ar, smooth_viewtx,
294                 sview);
295 }
296
297 /* only meant for timer usage */
298 static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *ar, bool sync_boxview)
299 {
300         const Depsgraph *depsgraph = CTX_data_depsgraph(C);
301         RegionView3D *rv3d = ar->regiondata;
302         struct SmoothView3DStore *sms = rv3d->sms;
303         float step, step_inv;
304
305         if (sms->time_allowed != 0.0)
306                 step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
307         else
308                 step = 1.0f;
309
310         /* end timer */
311         if (step >= 1.0f) {
312
313                 /* if we went to camera, store the original */
314                 if (sms->to_camera) {
315                         rv3d->persp = RV3D_CAMOB;
316                         view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
317                 }
318                 else {
319                         view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
320
321                         ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
322                         ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
323                 }
324
325                 if ((rv3d->viewlock & RV3D_LOCKED) == 0) {
326                         rv3d->view = sms->org_view;
327                 }
328
329                 MEM_freeN(rv3d->sms);
330                 rv3d->sms = NULL;
331
332                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer);
333                 rv3d->smooth_timer = NULL;
334                 rv3d->rflag &= ~RV3D_NAVIGATING;
335         }
336         else {
337                 /* ease in/out */
338                 step = (3.0f * step * step - 2.0f * step * step * step);
339
340                 step_inv = 1.0f - step;
341
342                 interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step);
343
344                 if (sms->use_dyn_ofs) {
345                         view3d_orbit_apply_dyn_ofs(rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
346                 }
347                 else {
348                         interp_v3_v3v3(rv3d->ofs, sms->src.ofs,  sms->dst.ofs,  step);
349                 }
350
351                 rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv;
352                 v3d->lens  = sms->dst.lens * step + sms->src.lens * step_inv;
353
354                 ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
355                 if (ED_screen_animation_playing(CTX_wm_manager(C))) {
356                         ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
357                 }
358
359                 /* Event handling won't know if a UI item has been moved under the pointer. */
360                 WM_event_add_mousemove(C);
361         }
362
363         if (sync_boxview && (rv3d->viewlock & RV3D_BOXVIEW)) {
364                 view3d_boxview_copy(CTX_wm_area(C), ar);
365         }
366
367         /* note: this doesn't work right because the v3d->lens is now used in ortho mode r51636,
368          * when switching camera in quad-view the other ortho views would zoom & reset.
369          *
370          * For now only redraw all regions when smoothview finishes.
371          */
372         if (step >= 1.0f) {
373                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
374         }
375         else {
376                 ED_region_tag_redraw(ar);
377         }
378 }
379
380 static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
381 {
382         View3D *v3d = CTX_wm_view3d(C);
383         ARegion *ar = CTX_wm_region(C);
384         RegionView3D *rv3d = ar->regiondata;
385
386         /* escape if not our timer */
387         if (rv3d->smooth_timer == NULL || rv3d->smooth_timer != event->customdata) {
388                 return OPERATOR_PASS_THROUGH;
389         }
390
391         view3d_smoothview_apply(C, v3d, ar, true);
392
393         return OPERATOR_FINISHED;
394 }
395
396 /**
397  * Apply the smoothview immediately, use when we need to start a new view operation.
398  * (so we don't end up half-applying a view operation when pressing keys quickly).
399  */
400 void ED_view3d_smooth_view_force_finish(
401         bContext *C,
402         View3D *v3d, ARegion *ar)
403 {
404         RegionView3D *rv3d = ar->regiondata;
405
406         if (rv3d && rv3d->sms) {
407                 rv3d->sms->time_allowed = 0.0;  /* force finishing */
408                 view3d_smoothview_apply(C, v3d, ar, false);
409
410                 /* force update of view matrix so tools that run immediately after
411                  * can use them without redrawing first */
412                 Depsgraph *depsgraph = CTX_data_depsgraph(C);
413                 Scene *scene = CTX_data_scene(C);
414                 ED_view3d_update_viewmat(depsgraph, scene, v3d, ar, NULL, NULL, NULL);
415         }
416 }
417
418 void VIEW3D_OT_smoothview(wmOperatorType *ot)
419 {
420         /* identifiers */
421         ot->name = "Smooth View";
422         ot->idname = "VIEW3D_OT_smoothview";
423
424         /* api callbacks */
425         ot->invoke = view3d_smoothview_invoke;
426
427         /* flags */
428         ot->flag = OPTYPE_INTERNAL;
429
430         ot->poll = ED_operator_view3d_active;
431 }
432
433 /** \} */
434
435 /* -------------------------------------------------------------------- */
436 /** \name Camera to View Operator
437  * \{ */
438
439 static int view3d_camera_to_view_exec(bContext *C, wmOperator *UNUSED(op))
440 {
441         const Depsgraph *depsgraph = CTX_data_depsgraph(C);
442         View3D *v3d;
443         ARegion *ar;
444         RegionView3D *rv3d;
445
446         ObjectTfmProtectedChannels obtfm;
447
448         ED_view3d_context_user_region(C, &v3d, &ar);
449         rv3d = ar->regiondata;
450
451         ED_view3d_lastview_store(rv3d);
452
453         BKE_object_tfm_protected_backup(v3d->camera, &obtfm);
454
455         ED_view3d_to_object(depsgraph, v3d->camera, rv3d->ofs, rv3d->viewquat, rv3d->dist);
456
457         BKE_object_tfm_protected_restore(v3d->camera, &obtfm, v3d->camera->protectflag);
458
459         DEG_id_tag_update(&v3d->camera->id, OB_RECALC_OB);
460         rv3d->persp = RV3D_CAMOB;
461
462         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, v3d->camera);
463
464         return OPERATOR_FINISHED;
465
466 }
467
468 static bool view3d_camera_to_view_poll(bContext *C)
469 {
470         View3D *v3d;
471         ARegion *ar;
472
473         if (ED_view3d_context_user_region(C, &v3d, &ar)) {
474                 RegionView3D *rv3d = ar->regiondata;
475                 if (v3d && v3d->camera && !ID_IS_LINKED(v3d->camera)) {
476                         if (rv3d && (rv3d->viewlock & RV3D_LOCKED) == 0) {
477                                 if (rv3d->persp != RV3D_CAMOB) {
478                                         return 1;
479                                 }
480                         }
481                 }
482         }
483
484         return 0;
485 }
486
487 void VIEW3D_OT_camera_to_view(wmOperatorType *ot)
488 {
489         /* identifiers */
490         ot->name = "Align Camera To View";
491         ot->description = "Set camera view to active view";
492         ot->idname = "VIEW3D_OT_camera_to_view";
493
494         /* api callbacks */
495         ot->exec = view3d_camera_to_view_exec;
496         ot->poll = view3d_camera_to_view_poll;
497
498         /* flags */
499         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
500 }
501
502 /** \} */
503
504 /* -------------------------------------------------------------------- */
505 /** \name Camera Fit Frame to Selected Operator
506  * \{ */
507
508 /* unlike VIEW3D_OT_view_selected this is for framing a render and not
509  * meant to take into account vertex/bone selection for eg. */
510 static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op)
511 {
512         Depsgraph *depsgraph = CTX_data_depsgraph(C);
513         Scene *scene = CTX_data_scene(C);
514         View3D *v3d = CTX_wm_view3d(C);  /* can be NULL */
515         Object *camera_ob = v3d ? v3d->camera : scene->camera;
516         Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
517
518         float r_co[3]; /* the new location to apply */
519         float r_scale; /* only for ortho cameras */
520
521         if (camera_ob_eval == NULL) {
522                 BKE_report(op->reports, RPT_ERROR, "No active camera");
523                 return OPERATOR_CANCELLED;
524         }
525
526         /* this function does all the important stuff */
527         if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, r_co, &r_scale)) {
528                 ObjectTfmProtectedChannels obtfm;
529                 float obmat_new[4][4];
530
531                 if ((camera_ob_eval->type == OB_CAMERA) && (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
532                         ((Camera *)camera_ob->data)->ortho_scale = r_scale;
533                 }
534
535                 copy_m4_m4(obmat_new, camera_ob_eval->obmat);
536                 copy_v3_v3(obmat_new[3], r_co);
537
538                 /* only touch location */
539                 BKE_object_tfm_protected_backup(camera_ob, &obtfm);
540                 BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
541                 BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
542
543                 /* notifiers */
544                 DEG_id_tag_update(&camera_ob->id, OB_RECALC_OB);
545                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, camera_ob);
546                 return OPERATOR_FINISHED;
547         }
548         else {
549                 return OPERATOR_CANCELLED;
550         }
551 }
552
553 void VIEW3D_OT_camera_to_view_selected(wmOperatorType *ot)
554 {
555         /* identifiers */
556         ot->name = "Camera Fit Frame to Selected";
557         ot->description = "Move the camera so selected objects are framed";
558         ot->idname = "VIEW3D_OT_camera_to_view_selected";
559
560         /* api callbacks */
561         ot->exec = view3d_camera_to_view_selected_exec;
562         ot->poll = ED_operator_scene_editable;
563
564         /* flags */
565         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
566 }
567
568 /** \} */
569
570 /* -------------------------------------------------------------------- */
571 /** \name Object as Camera Operator
572  * \{ */
573
574 static void sync_viewport_camera_smoothview(bContext *C, View3D *v3d, Object *ob, const int smooth_viewtx)
575 {
576         Main *bmain = CTX_data_main(C);
577         for (bScreen *screen = bmain->screen.first; screen != NULL; screen = screen->id.next) {
578                 for (ScrArea *area = screen->areabase.first; area != NULL; area = area->next) {
579                         for (SpaceLink *space_link = area->spacedata.first; space_link != NULL; space_link = space_link->next) {
580                                 if (space_link->spacetype == SPACE_VIEW3D) {
581                                         View3D *other_v3d = (View3D *)space_link;
582                                         if (other_v3d == v3d) {
583                                                 continue;
584                                         }
585                                         if (other_v3d->camera == ob) {
586                                                 continue;
587                                         }
588                                         if (v3d->scenelock) {
589                                                 ListBase *lb = (space_link == area->spacedata.first)
590                                                                    ? &area->regionbase
591                                                                    : &space_link->regionbase;
592                                                 for (ARegion *other_ar = lb->first; other_ar != NULL; other_ar = other_ar->next) {
593                                                         if (other_ar->regiontype == RGN_TYPE_WINDOW) {
594                                                                 if (other_ar->regiondata) {
595                                                                         RegionView3D *other_rv3d = other_ar->regiondata;
596                                                                         if (other_rv3d->persp == RV3D_CAMOB) {
597                                                                                 Object *other_camera_old = other_v3d->camera;
598                                                                                 other_v3d->camera = ob;
599                                                                                 ED_view3d_lastview_store(other_rv3d);
600                                                                                 ED_view3d_smooth_view(
601                                                                                         C, other_v3d, other_ar, smooth_viewtx,
602                                                                                         &(const V3D_SmoothParams) {
603                                                                                             .camera_old = other_camera_old,
604                                                                                             .camera = other_v3d->camera,
605                                                                                             .ofs = other_rv3d->ofs,
606                                                                                             .quat = other_rv3d->viewquat,
607                                                                                             .dist = &other_rv3d->dist,
608                                                                                             .lens = &other_v3d->lens});
609                                                                         }
610                                                                         else {
611                                                                                 other_v3d->camera = ob;
612                                                                         }
613                                                                 }
614                                                         }
615                                                 }
616                                         }
617                                 }
618                         }
619                 }
620         }
621 }
622
623 static int view3d_setobjectascamera_exec(bContext *C, wmOperator *op)
624 {
625         View3D *v3d;
626         ARegion *ar;
627         RegionView3D *rv3d;
628
629         Scene *scene = CTX_data_scene(C);
630         Object *ob = CTX_data_active_object(C);
631
632         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
633
634         /* no NULL check is needed, poll checks */
635         ED_view3d_context_user_region(C, &v3d, &ar);
636         rv3d = ar->regiondata;
637
638         if (ob) {
639                 Object *camera_old = (rv3d->persp == RV3D_CAMOB) ? V3D_CAMERA_SCENE(scene, v3d) : NULL;
640                 rv3d->persp = RV3D_CAMOB;
641                 v3d->camera = ob;
642                 if (v3d->scenelock)
643                         scene->camera = ob;
644
645                 /* unlikely but looks like a glitch when set to the same */
646                 if (camera_old != ob) {
647                         ED_view3d_lastview_store(rv3d);
648
649                         ED_view3d_smooth_view(
650                                 C, v3d, ar, smooth_viewtx,
651                                 &(const V3D_SmoothParams) {
652                                     .camera_old = camera_old, .camera = v3d->camera,
653                                     .ofs = rv3d->ofs, .quat = rv3d->viewquat,
654                                     .dist = &rv3d->dist, .lens = &v3d->lens});
655                 }
656
657                 if (v3d->scenelock) {
658                         sync_viewport_camera_smoothview(C, v3d, ob, smooth_viewtx);
659                         WM_event_add_notifier(C, NC_SCENE, scene);
660                 }
661                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, scene);
662         }
663
664         return OPERATOR_FINISHED;
665 }
666
667 bool ED_operator_rv3d_user_region_poll(bContext *C)
668 {
669         View3D *v3d_dummy;
670         ARegion *ar_dummy;
671
672         return ED_view3d_context_user_region(C, &v3d_dummy, &ar_dummy);
673 }
674
675 void VIEW3D_OT_object_as_camera(wmOperatorType *ot)
676 {
677         /* identifiers */
678         ot->name = "Set Active Object as Camera";
679         ot->description = "Set the active object as the active camera for this view or scene";
680         ot->idname = "VIEW3D_OT_object_as_camera";
681
682         /* api callbacks */
683         ot->exec = view3d_setobjectascamera_exec;
684         ot->poll = ED_operator_rv3d_user_region_poll;
685
686         /* flags */
687         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
688 }
689
690 /** \} */
691
692 /* -------------------------------------------------------------------- */
693 /** \name Window and View Matrix Calculation
694  * \{ */
695
696 /**
697  * \param rect optional for picking (can be NULL).
698  */
699 void view3d_winmatrix_set(Depsgraph *depsgraph, ARegion *ar, const View3D *v3d, const rcti *rect)
700 {
701         RegionView3D *rv3d = ar->regiondata;
702         rctf viewplane;
703         float clipsta, clipend;
704         bool is_ortho;
705
706         is_ortho = ED_view3d_viewplane_get(depsgraph, v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
707         rv3d->is_persp = !is_ortho;
708
709 #if 0
710         printf("%s: %d %d %f %f %f %f %f %f\n", __func__, winx, winy,
711                viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax,
712                clipsta, clipend);
713 #endif
714
715         if (rect) {  /* picking */
716                 rctf r;
717                 r.xmin = viewplane.xmin + (BLI_rctf_size_x(&viewplane) * (rect->xmin / (float)ar->winx));
718                 r.ymin = viewplane.ymin + (BLI_rctf_size_y(&viewplane) * (rect->ymin / (float)ar->winy));
719                 r.xmax = viewplane.xmin + (BLI_rctf_size_x(&viewplane) * (rect->xmax / (float)ar->winx));
720                 r.ymax = viewplane.ymin + (BLI_rctf_size_y(&viewplane) * (rect->ymax / (float)ar->winy));
721                 viewplane = r;
722         }
723
724         if (is_ortho) {
725                 GPU_matrix_ortho_set(viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend);
726         }
727         else {
728                 GPU_matrix_frustum_set(viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend);
729         }
730
731         /* update matrix in 3d view region */
732         GPU_matrix_projection_get(rv3d->winmat);
733 }
734
735 static void obmat_to_viewmat(RegionView3D *rv3d, Object *ob)
736 {
737         float bmat[4][4];
738
739         rv3d->view = RV3D_VIEW_USER; /* don't show the grid */
740
741         normalize_m4_m4(bmat, ob->obmat);
742         invert_m4_m4(rv3d->viewmat, bmat);
743
744         /* view quat calculation, needed for add object */
745         mat4_normalized_to_quat(rv3d->viewquat, rv3d->viewmat);
746 }
747
748 /**
749  * Sets #RegionView3D.viewmat
750  *
751  * \param depsgraph: Depsgraph.
752  * \param scene: Scene for camera and cursor location.
753  * \param v3d: View 3D space data.
754  * \param rv3d: 3D region which stores the final matrices.
755  * \param rect_scale: Optional 2D scale argument,
756  * Use when displaying a sub-region, eg: when #view3d_winmatrix_set takes a 'rect' argument.
757  *
758  * \note don't set windows active in here, is used by renderwin too.
759  */
760 void view3d_viewmatrix_set(
761         Depsgraph *depsgraph, Scene *scene,
762         const View3D *v3d, RegionView3D *rv3d, const float rect_scale[2])
763 {
764         if (rv3d->persp == RV3D_CAMOB) {      /* obs/camera */
765                 if (v3d->camera) {
766                         Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, v3d->camera);
767                         BKE_object_where_is_calc(depsgraph, scene, ob_camera_eval);
768                         obmat_to_viewmat(rv3d, ob_camera_eval);
769                 }
770                 else {
771                         quat_to_mat4(rv3d->viewmat, rv3d->viewquat);
772                         rv3d->viewmat[3][2] -= rv3d->dist;
773                 }
774         }
775         else {
776                 bool use_lock_ofs = false;
777
778
779                 /* should be moved to better initialize later on XXX */
780                 if (rv3d->viewlock & RV3D_LOCKED)
781                         ED_view3d_lock(rv3d);
782
783                 quat_to_mat4(rv3d->viewmat, rv3d->viewquat);
784                 if (rv3d->persp == RV3D_PERSP) rv3d->viewmat[3][2] -= rv3d->dist;
785                 if (v3d->ob_centre) {
786                         Object *ob_eval = DEG_get_evaluated_object(depsgraph, v3d->ob_centre);
787                         float vec[3];
788
789                         copy_v3_v3(vec, ob_eval->obmat[3]);
790                         if (ob_eval->type == OB_ARMATURE && v3d->ob_centre_bone[0]) {
791                                 bPoseChannel *pchan = BKE_pose_channel_find_name(ob_eval->pose, v3d->ob_centre_bone);
792                                 if (pchan) {
793                                         copy_v3_v3(vec, pchan->pose_mat[3]);
794                                         mul_m4_v3(ob_eval->obmat, vec);
795                                 }
796                         }
797                         translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]);
798                         use_lock_ofs = true;
799                 }
800                 else if (v3d->ob_centre_cursor) {
801                         float vec[3];
802                         copy_v3_v3(vec, scene->cursor.location);
803                         translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]);
804                         use_lock_ofs = true;
805                 }
806                 else {
807                         translate_m4(rv3d->viewmat, rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2]);
808                 }
809
810                 /* lock offset */
811                 if (use_lock_ofs) {
812                         float persmat[4][4], persinv[4][4];
813                         float vec[3];
814
815                         /* we could calculate the real persmat/persinv here
816                          * but it would be unreliable so better to later */
817                         mul_m4_m4m4(persmat, rv3d->winmat, rv3d->viewmat);
818                         invert_m4_m4(persinv, persmat);
819
820                         mul_v2_v2fl(vec, rv3d->ofs_lock, rv3d->is_persp ? rv3d->dist : 1.0f);
821                         vec[2] = 0.0f;
822
823                         if (rect_scale) {
824                                 /* Since 'RegionView3D.winmat' has been calculated and this function doesn't take the 'ARegion'
825                                  * we don't know about the region size.
826                                  * Use 'rect_scale' when drawing a sub-region to apply 2D offset,
827                                  * scaled by the difference between the sub-region and the region size.
828                                  */
829                                 vec[0] /= rect_scale[0];
830                                 vec[1] /= rect_scale[1];
831                         }
832
833                         mul_mat3_m4_v3(persinv, vec);
834                         translate_m4(rv3d->viewmat, vec[0], vec[1], vec[2]);
835                 }
836                 /* end lock offset */
837         }
838 }
839
840 /** \} */
841
842 /* -------------------------------------------------------------------- */
843 /** \name OpenGL Select Utilities
844  * \{ */
845
846 /**
847  * Optionally cache data for multiple calls to #view3d_opengl_select
848  *
849  * just avoid GPU_select headers outside this file
850  */
851 void view3d_opengl_select_cache_begin(void)
852 {
853         GPU_select_cache_begin();
854 }
855
856 void view3d_opengl_select_cache_end(void)
857 {
858         GPU_select_cache_end();
859 }
860
861 struct DrawSelectLoopUserData {
862         uint  pass;
863         uint  hits;
864         uint *buffer;
865         uint  buffer_len;
866         const rcti *rect;
867         char gpu_select_mode;
868 };
869
870 static bool drw_select_loop_pass(eDRWSelectStage stage, void *user_data)
871 {
872         bool continue_pass = false;
873         struct DrawSelectLoopUserData *data = user_data;
874         if (stage == DRW_SELECT_PASS_PRE) {
875                 GPU_select_begin(data->buffer, data->buffer_len, data->rect, data->gpu_select_mode, data->hits);
876                 /* always run POST after PRE. */
877                 continue_pass = true;
878         }
879         else if (stage == DRW_SELECT_PASS_POST) {
880                 int hits = GPU_select_end();
881                 if (data->pass == 0) {
882                         /* quirk of GPU_select_end, only take hits value from first call. */
883                         data->hits = hits;
884                 }
885                 if (data->gpu_select_mode == GPU_SELECT_NEAREST_FIRST_PASS) {
886                         data->gpu_select_mode = GPU_SELECT_NEAREST_SECOND_PASS;
887                         continue_pass = (hits > 0);
888                 }
889                 data->pass += 1;
890         }
891         else {
892                 BLI_assert(0);
893         }
894         return continue_pass;
895
896 }
897
898 eV3DSelectObjectFilter ED_view3d_select_filter_from_mode(const Scene *scene, const Object *obact)
899 {
900         if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
901                 if (obact && (obact->mode & OB_MODE_WEIGHT_PAINT) &&
902                     BKE_object_pose_armature_get((Object *)obact))
903                 {
904                         return VIEW3D_SELECT_FILTER_WPAINT_POSE_MODE_LOCK;
905                 }
906                 return VIEW3D_SELECT_FILTER_OBJECT_MODE_LOCK;
907         }
908         return VIEW3D_SELECT_FILTER_NOP;
909 }
910
911 /** Implement #VIEW3D_SELECT_FILTER_OBJECT_MODE_LOCK. */
912 static bool drw_select_filter_object_mode_lock(Object *ob, void *user_data)
913 {
914         const Object *obact = user_data;
915         return BKE_object_is_mode_compat(ob, obact->mode);
916 }
917
918 /** Implement #VIEW3D_SELECT_FILTER_WPAINT_POSE_MODE_LOCK for special case when
919  * we want to select pose bones (this doesn't switch modes). */
920 static bool drw_select_filter_object_mode_lock_for_weight_paint(Object *ob, void *user_data)
921 {
922         const Object *ob_pose = user_data;
923         return (DEG_get_original_object(ob) == ob_pose);
924 }
925
926 /**
927  * \warning be sure to account for a negative return value
928  * This is an error, "Too many objects in select buffer"
929  * and no action should be taken (can crash blender) if this happens
930  *
931  * \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection.
932  */
933 int view3d_opengl_select(
934         ViewContext *vc, unsigned int *buffer, unsigned int bufsize, const rcti *input,
935         eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter)
936 {
937         struct bThemeState theme_state;
938         Depsgraph *depsgraph = vc->depsgraph;
939         Scene *scene = vc->scene;
940         View3D *v3d = vc->v3d;
941         ARegion *ar = vc->ar;
942         rcti rect;
943         int hits = 0;
944         const bool use_obedit_skip = (OBEDIT_FROM_VIEW_LAYER(vc->view_layer) != NULL) && (vc->obedit == NULL);
945         const bool is_pick_select = (U.gpu_select_pick_deph != 0);
946         const bool do_passes = (
947                 (is_pick_select == false) &&
948                 (select_mode == VIEW3D_SELECT_PICK_NEAREST) &&
949                 GPU_select_query_check_active());
950         const bool use_nearest = (is_pick_select && select_mode == VIEW3D_SELECT_PICK_NEAREST);
951         bool draw_surface = true;
952
953         char gpu_select_mode;
954
955         /* case not a box select */
956         if (input->xmin == input->xmax) {
957                 /* seems to be default value for bones only now */
958                 BLI_rcti_init_pt_radius(&rect, (const int[2]){input->xmin, input->ymin}, 12);
959         }
960         else {
961                 rect = *input;
962         }
963
964         if (is_pick_select) {
965                 if (is_pick_select && select_mode == VIEW3D_SELECT_PICK_NEAREST) {
966                         gpu_select_mode = GPU_SELECT_PICK_NEAREST;
967                 }
968                 else if (is_pick_select && select_mode == VIEW3D_SELECT_PICK_ALL) {
969                         gpu_select_mode = GPU_SELECT_PICK_ALL;
970                 }
971                 else {
972                         gpu_select_mode = GPU_SELECT_ALL;
973                 }
974         }
975         else {
976                 if (do_passes) {
977                         gpu_select_mode = GPU_SELECT_NEAREST_FIRST_PASS;
978                 }
979                 else {
980                         gpu_select_mode = GPU_SELECT_ALL;
981                 }
982         }
983
984         struct {
985                 DRW_ObjectFilterFn fn;
986                 void *user_data;
987         } object_filter = {NULL, NULL};
988         switch (select_filter) {
989                 case VIEW3D_SELECT_FILTER_OBJECT_MODE_LOCK:
990                 {
991                         Object *obact = OBACT(vc->view_layer);
992                         if (obact && obact->mode != OB_MODE_OBJECT) {
993                                 object_filter.fn = drw_select_filter_object_mode_lock;
994                                 object_filter.user_data = obact;
995                         }
996                         break;
997                 }
998                 case VIEW3D_SELECT_FILTER_WPAINT_POSE_MODE_LOCK:
999                 {
1000                         Object *obact = OBACT(vc->view_layer);
1001                         BLI_assert(obact && (obact->mode & OB_MODE_WEIGHT_PAINT));
1002                         Object *ob_pose = BKE_object_pose_armature_get(obact);
1003
1004                         object_filter.fn = drw_select_filter_object_mode_lock_for_weight_paint;
1005                         object_filter.user_data = ob_pose;
1006                         break;
1007                 }
1008                 case VIEW3D_SELECT_FILTER_NOP:
1009                         break;
1010
1011         }
1012
1013         /* Tools may request depth outside of regular drawing code. */
1014         UI_Theme_Store(&theme_state);
1015         UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
1016
1017         /* Re-use cache (rect must be smaller then the cached)
1018          * other context is assumed to be unchanged */
1019         if (GPU_select_is_cached()) {
1020                 GPU_select_begin(buffer, bufsize, &rect, gpu_select_mode, 0);
1021                 GPU_select_cache_load_id();
1022                 hits = GPU_select_end();
1023                 goto finally;
1024         }
1025
1026         /* All of the queries need to be perform on the drawing context. */
1027         DRW_opengl_context_enable();
1028
1029         G.f |= G_PICKSEL;
1030
1031         /* Important we use the 'viewmat' and don't re-calculate since
1032          * the object & bone view locking takes 'rect' into account, see: T51629. */
1033         ED_view3d_draw_setup_view(vc->win, depsgraph, scene, ar, v3d, vc->rv3d->viewmat, NULL, &rect);
1034
1035         if (v3d->shading.type > OB_WIRE) {
1036                 GPU_depth_test(true);
1037         }
1038
1039         if (vc->rv3d->rflag & RV3D_CLIPPING) {
1040                 ED_view3d_clipping_set(vc->rv3d);
1041         }
1042
1043         /* If in xray mode, we select the wires in priority. */
1044         if ((v3d->shading.flag & V3D_XRAY_FLAG(v3d)) && use_nearest) {
1045                 /* We need to call "GPU_select_*" API's inside DRW_draw_select_loop
1046                  * because the OpenGL context created & destroyed inside this function. */
1047                 struct DrawSelectLoopUserData drw_select_loop_user_data = {
1048                         .pass = 0,
1049                         .hits = 0,
1050                         .buffer = buffer,
1051                         .buffer_len = bufsize,
1052                         .rect = &rect,
1053                         .gpu_select_mode = gpu_select_mode,
1054                 };
1055                 draw_surface = false;
1056                 DRW_draw_select_loop(
1057                         depsgraph, ar, v3d,
1058                         use_obedit_skip, draw_surface, use_nearest, &rect,
1059                         drw_select_loop_pass, &drw_select_loop_user_data,
1060                         object_filter.fn, object_filter.user_data);
1061                 hits = drw_select_loop_user_data.hits;
1062                 /* FIX: This cleanup the state before doing another selection pass.
1063                  * (see T56695) */
1064                 GPU_select_cache_end();
1065         }
1066
1067         if (hits == 0) {
1068                 /* We need to call "GPU_select_*" API's inside DRW_draw_select_loop
1069                  * because the OpenGL context created & destroyed inside this function. */
1070                 struct DrawSelectLoopUserData drw_select_loop_user_data = {
1071                         .pass = 0,
1072                         .hits = 0,
1073                         .buffer = buffer,
1074                         .buffer_len = bufsize,
1075                         .rect = &rect,
1076                         .gpu_select_mode = gpu_select_mode,
1077                 };
1078                 draw_surface = true;
1079                 DRW_draw_select_loop(
1080                         depsgraph, ar, v3d,
1081                         use_obedit_skip, draw_surface, use_nearest, &rect,
1082                         drw_select_loop_pass, &drw_select_loop_user_data,
1083                         object_filter.fn, object_filter.user_data);
1084                 hits = drw_select_loop_user_data.hits;
1085         }
1086
1087         G.f &= ~G_PICKSEL;
1088         ED_view3d_draw_setup_view(vc->win, depsgraph, scene, ar, v3d, vc->rv3d->viewmat, NULL, NULL);
1089
1090         if (v3d->shading.type > OB_WIRE) {
1091                 GPU_depth_test(false);
1092         }
1093
1094         if (vc->rv3d->rflag & RV3D_CLIPPING)
1095                 ED_view3d_clipping_disable();
1096
1097         DRW_opengl_context_disable();
1098
1099 finally:
1100
1101         if (hits < 0) printf("Too many objects in select buffer\n");  /* XXX make error message */
1102
1103         UI_Theme_Restore(&theme_state);
1104
1105         return hits;
1106 }
1107
1108 /** \} */
1109
1110 /* -------------------------------------------------------------------- */
1111 /** \name Local View Operators
1112  * \{ */
1113
1114 static unsigned int free_localbit(Main *bmain)
1115 {
1116         ScrArea *sa;
1117         bScreen *sc;
1118
1119         unsigned short local_view_bits = 0;
1120
1121         /* sometimes we loose a localview: when an area is closed */
1122         /* check all areas: which localviews are in use? */
1123         for (sc = bmain->screen.first; sc; sc = sc->id.next) {
1124                 for (sa = sc->areabase.first; sa; sa = sa->next) {
1125                         SpaceLink *sl = sa->spacedata.first;
1126                         for (; sl; sl = sl->next) {
1127                                 if (sl->spacetype == SPACE_VIEW3D) {
1128                                         View3D *v3d = (View3D *) sl;
1129                                         if (v3d->localvd) {
1130                                                 local_view_bits |= v3d->local_view_uuid;
1131                                         }
1132                                 }
1133                         }
1134                 }
1135         }
1136
1137         for (int i = 0; i < 16; i++) {
1138                 if ((local_view_bits & (1 << i)) == 0) {
1139                         return (1 << i);
1140                 }
1141         }
1142
1143         return 0;
1144 }
1145
1146 static bool view3d_localview_init(
1147         const Depsgraph *depsgraph,
1148         wmWindowManager *wm,
1149         wmWindow *win,
1150         Main *bmain,
1151         ViewLayer *view_layer,
1152         ScrArea *sa,
1153         const int smooth_viewtx,
1154         ReportList *reports)
1155 {
1156         View3D *v3d = sa->spacedata.first;
1157         Base *base;
1158         float min[3], max[3], box[3], mid[3];
1159         float size = 0.0f;
1160         unsigned int local_view_bit;
1161         bool ok = false;
1162
1163         if (v3d->localvd) {
1164                 return ok;
1165         }
1166
1167         INIT_MINMAX(min, max);
1168
1169         local_view_bit = free_localbit(bmain);
1170
1171         if (local_view_bit == 0) {
1172                 /* TODO(dfelinto): We can kick one of the other 3D views out of local view
1173                    specially if it is not being used.  */
1174                 BKE_report(reports, RPT_ERROR, "No more than 16 local views");
1175                 ok = false;
1176         }
1177         else {
1178                 Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
1179                 if (obedit) {
1180                         FOREACH_BASE_IN_EDIT_MODE_BEGIN(view_layer, v3d, base_iter) {
1181                                 BKE_object_minmax(base_iter->object, min, max, false);
1182                                 base_iter->local_view_bits |= local_view_bit;
1183                                 ok = true;
1184                         } FOREACH_BASE_IN_EDIT_MODE_END;
1185                 }
1186                 else {
1187                         for (base = FIRSTBASE(view_layer); base; base = base->next) {
1188                                 if (TESTBASE(v3d, base)) {
1189                                         BKE_object_minmax(base->object, min, max, false);
1190                                         base->local_view_bits |= local_view_bit;
1191                                         /* Technically we should leave for Depsgraph to handle this.
1192                                            But it is harmless to do it here, and it seems to be necessary. */
1193                                         base->object->base_local_view_bits = base->local_view_bits;
1194                                         ok = true;
1195                                 }
1196                         }
1197                 }
1198
1199                 sub_v3_v3v3(box, max, min);
1200                 size = max_fff(box[0], box[1], box[2]);
1201         }
1202
1203         if (ok == true) {
1204                 ARegion *ar;
1205
1206                 v3d->localvd = MEM_mallocN(sizeof(View3D), "localview");
1207
1208                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1209
1210                 mid_v3_v3v3(mid, min, max);
1211
1212                 for (ar = sa->regionbase.first; ar; ar = ar->next) {
1213                         if (ar->regiontype == RGN_TYPE_WINDOW) {
1214                                 RegionView3D *rv3d = ar->regiondata;
1215                                 bool ok_dist = true;
1216
1217                                 /* New view values. */
1218                                 Object *camera_old = NULL;
1219                                 float dist_new, ofs_new[3];
1220
1221                                 rv3d->localvd = MEM_mallocN(sizeof(RegionView3D), "localview region");
1222                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1223
1224                                 negate_v3_v3(ofs_new, mid);
1225
1226                                 if (rv3d->persp == RV3D_CAMOB) {
1227                                         rv3d->persp = RV3D_PERSP;
1228                                         camera_old = v3d->camera;
1229                                 }
1230
1231                                 if (rv3d->persp == RV3D_ORTHO) {
1232                                         if (size < 0.0001f) {
1233                                                 ok_dist = false;
1234                                         }
1235                                 }
1236
1237                                 if (ok_dist) {
1238                                         dist_new = ED_view3d_radius_to_dist(v3d, ar, depsgraph, rv3d->persp, true, (size / 2) * VIEW3D_MARGIN);
1239
1240                                         if (rv3d->persp == RV3D_PERSP) {
1241                                                 /* Don't zoom closer than the near clipping plane. */
1242                                                 dist_new = max_ff(dist_new, v3d->near * 1.5f);
1243                                         }
1244                                 }
1245
1246                                 ED_view3d_smooth_view_ex(
1247                                         depsgraph,
1248                                         wm, win, sa, v3d, ar, smooth_viewtx,
1249                                             &(const V3D_SmoothParams) {
1250                                                 .camera_old = camera_old,
1251                                                 .ofs = ofs_new, .quat = rv3d->viewquat,
1252                                                 .dist = ok_dist ? &dist_new : NULL, .lens = &v3d->lens});
1253                         }
1254                 }
1255
1256                 v3d->local_view_uuid = local_view_bit;
1257         }
1258
1259         DEG_on_visible_update(bmain, false);
1260         return ok;
1261 }
1262
1263 static void restore_localviewdata(
1264         const Depsgraph *depsgraph,
1265         wmWindowManager *wm,
1266         wmWindow *win,
1267         Main *bmain,
1268         ScrArea *sa,
1269         const int smooth_viewtx)
1270 {
1271         const bool free = true;
1272         ARegion *ar;
1273         View3D *v3d = sa->spacedata.first;
1274         Object *camera_old, *camera_new;
1275
1276         if (v3d->localvd == NULL) return;
1277
1278         camera_old = v3d->camera;
1279         camera_new = v3d->localvd->camera;
1280
1281         v3d->local_view_uuid = 0;
1282         v3d->camera = v3d->localvd->camera;
1283
1284         if (free) {
1285                 MEM_freeN(v3d->localvd);
1286                 v3d->localvd = NULL;
1287         }
1288
1289         for (ar = sa->regionbase.first; ar; ar = ar->next) {
1290                 if (ar->regiontype == RGN_TYPE_WINDOW) {
1291                         RegionView3D *rv3d = ar->regiondata;
1292
1293                         if (rv3d->localvd) {
1294                                 Object *camera_old_rv3d, *camera_new_rv3d;
1295
1296                                 camera_old_rv3d = (rv3d->persp          == RV3D_CAMOB) ? camera_old : NULL;
1297                                 camera_new_rv3d = (rv3d->localvd->persp == RV3D_CAMOB) ? camera_new : NULL;
1298
1299                                 rv3d->view = rv3d->localvd->view;
1300                                 rv3d->persp = rv3d->localvd->persp;
1301                                 rv3d->camzoom = rv3d->localvd->camzoom;
1302
1303                                 ED_view3d_smooth_view_ex(
1304                                         depsgraph,
1305                                         wm, win, sa,
1306                                         v3d, ar, smooth_viewtx,
1307                                         &(const V3D_SmoothParams) {
1308                                             .camera_old = camera_old_rv3d, .camera = camera_new_rv3d,
1309                                             .ofs = rv3d->localvd->ofs, .quat = rv3d->localvd->viewquat,
1310                                             .dist = &rv3d->localvd->dist});
1311
1312                                 if (free) {
1313                                         MEM_freeN(rv3d->localvd);
1314                                         rv3d->localvd = NULL;
1315                                 }
1316                         }
1317
1318                         ED_view3d_shade_update(bmain, v3d, sa);
1319                 }
1320         }
1321 }
1322
1323 static bool view3d_localview_exit(
1324         const Depsgraph *depsgraph,
1325         wmWindowManager *wm,
1326         wmWindow *win,
1327         Main *bmain,
1328         ViewLayer *view_layer,
1329         ScrArea *sa,
1330         const int smooth_viewtx)
1331 {
1332         View3D *v3d = sa->spacedata.first;
1333         struct Base *base;
1334         unsigned int local_view_bit;
1335
1336         if (v3d->localvd) {
1337
1338                 local_view_bit = v3d->local_view_uuid;
1339
1340                 restore_localviewdata(depsgraph, wm, win, bmain, sa, smooth_viewtx);
1341
1342                 Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
1343                 for (base = FIRSTBASE(view_layer); base; base = base->next) {
1344                         if (base->local_view_bits & local_view_bit) {
1345                                 base->local_view_bits &= ~local_view_bit;
1346                                 if (base->object != obedit) {
1347                                         ED_object_base_select(base, BA_SELECT);
1348                                 }
1349                         }
1350                 }
1351
1352                 DEG_on_visible_update(bmain, false);
1353
1354                 return true;
1355         }
1356         else {
1357                 return false;
1358         }
1359 }
1360
1361 static int localview_exec(bContext *C, wmOperator *op)
1362 {
1363         const Depsgraph *depsgraph = CTX_data_depsgraph(C);
1364         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1365         wmWindowManager *wm = CTX_wm_manager(C);
1366         wmWindow *win = CTX_wm_window(C);
1367         Main *bmain = CTX_data_main(C);
1368         Scene *scene = CTX_data_scene(C);
1369         ViewLayer *view_layer = CTX_data_view_layer(C);
1370         ScrArea *sa = CTX_wm_area(C);
1371         View3D *v3d = CTX_wm_view3d(C);
1372         bool changed;
1373
1374         if (v3d->localvd) {
1375                 changed = view3d_localview_exit(depsgraph, wm, win, bmain, view_layer, sa, smooth_viewtx);
1376         }
1377         else {
1378                 changed = view3d_localview_init(depsgraph, wm, win, bmain, view_layer, sa, smooth_viewtx, op->reports);
1379         }
1380
1381         if (changed) {
1382                 DEG_id_type_tag(bmain, ID_OB);
1383                 ED_area_tag_redraw(sa);
1384
1385                 /* Unselected objects become selected when exiting. */
1386                 if (v3d->localvd == NULL) {
1387                         DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE);
1388                         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1389                 }
1390                 else {
1391                         DEG_id_tag_update(&scene->id, DEG_TAG_BASE_FLAGS_UPDATE);
1392                 }
1393
1394                 return OPERATOR_FINISHED;
1395         }
1396         else {
1397                 return OPERATOR_CANCELLED;
1398         }
1399 }
1400
1401 void VIEW3D_OT_localview(wmOperatorType *ot)
1402 {
1403         /* identifiers */
1404         ot->name = "Local View";
1405         ot->description = "Toggle display of selected object(s) separately and centered in view";
1406         ot->idname = "VIEW3D_OT_localview";
1407
1408         /* api callbacks */
1409         ot->exec = localview_exec;
1410         ot->flag = OPTYPE_UNDO; /* localview changes object layer bitflags */
1411
1412         ot->poll = ED_operator_view3d_active;
1413 }
1414
1415 static int localview_remove_from_exec(bContext *C, wmOperator *op)
1416 {
1417         View3D *v3d = CTX_wm_view3d(C);
1418         Main *bmain = CTX_data_main(C);
1419         Scene *scene = CTX_data_scene(C);
1420         ViewLayer *view_layer = CTX_data_view_layer(C);
1421         bool changed = false;
1422
1423         for (Base *base = FIRSTBASE(view_layer); base; base = base->next) {
1424                 if (TESTBASE(v3d, base)) {
1425                         base->local_view_bits &= ~v3d->local_view_uuid;
1426                         ED_object_base_select(base, BA_DESELECT);
1427
1428                         if (base == BASACT(view_layer)) {
1429                                 view_layer->basact = NULL;
1430                         }
1431                         changed = true;
1432                 }
1433         }
1434
1435         if (changed) {
1436                 DEG_on_visible_update(bmain, false);
1437                 DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE);
1438                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1439                 WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
1440                 return OPERATOR_FINISHED;
1441         }
1442         else {
1443                 BKE_report(op->reports, RPT_ERROR, "No object selected");
1444                 return OPERATOR_CANCELLED;
1445         }
1446 }
1447
1448 static bool localview_remove_from_poll(bContext *C)
1449 {
1450         if (CTX_data_edit_object(C) != NULL) {
1451                 return false;
1452         }
1453
1454         View3D *v3d = CTX_wm_view3d(C);
1455         return v3d && v3d->localvd;
1456 }
1457
1458 void VIEW3D_OT_localview_remove_from(wmOperatorType *ot)
1459 {
1460         /* identifiers */
1461         ot->name = "Remove from Local View";
1462         ot->description = "Move selected objects out of local view";
1463         ot->idname = "VIEW3D_OT_localview_remove_from";
1464
1465         /* api callbacks */
1466         ot->exec = localview_remove_from_exec;
1467         ot->invoke = WM_operator_confirm;
1468         ot->poll = localview_remove_from_poll;
1469         ot->flag = OPTYPE_UNDO;
1470 }
1471
1472 /** \} */
1473
1474 /* -------------------------------------------------------------------- */
1475 /** \name View Layer Utilities
1476  * \{ */
1477
1478 int ED_view3d_view_layer_set(int lay, const bool *values, int *active)
1479 {
1480         int i, tot = 0;
1481
1482         /* ensure we always have some layer selected */
1483         for (i = 0; i < 20; i++)
1484                 if (values[i])
1485                         tot++;
1486
1487         if (tot == 0)
1488                 return lay;
1489
1490         for (i = 0; i < 20; i++) {
1491
1492                 if (active) {
1493                         /* if this value has just been switched on, make that layer active */
1494                         if (values[i] && (lay & (1 << i)) == 0) {
1495                                 *active = (1 << i);
1496                         }
1497                 }
1498
1499                 if (values[i]) lay |= (1 << i);
1500                 else lay &= ~(1 << i);
1501         }
1502
1503         /* ensure always an active layer */
1504         if (active && (lay & *active) == 0) {
1505                 for (i = 0; i < 20; i++) {
1506                         if (lay & (1 << i)) {
1507                                 *active = 1 << i;
1508                                 break;
1509                         }
1510                 }
1511         }
1512
1513         return lay;
1514 }
1515
1516 /** \} */