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