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