2.50:
[blender.git] / source / blender / editors / space_view3d / view3d_view.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <math.h>
32 #include <float.h>
33
34 #include "DNA_action_types.h"
35 #include "DNA_armature_types.h"
36 #include "DNA_camera_types.h"
37 #include "DNA_lamp_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_space_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_userdef_types.h"
43 #include "DNA_view3d_types.h"
44 #include "DNA_world_types.h"
45
46 #include "MEM_guardedalloc.h"
47
48 #include "BLI_arithb.h"
49 #include "BLI_blenlib.h"
50 #include "BLI_editVert.h"
51 #include "BLI_rand.h"
52
53 #include "BKE_anim.h"
54 #include "BKE_action.h"
55 #include "BKE_context.h"
56 #include "BKE_object.h"
57 #include "BKE_global.h"
58 #include "BKE_main.h"
59 #include "BKE_scene.h"
60 #include "BKE_screen.h"
61 #include "BKE_utildefines.h"
62
63 #include "RE_pipeline.h"        // make_stars
64
65 #include "BIF_gl.h"
66
67 #include "WM_api.h"
68 #include "WM_types.h"
69
70 #include "ED_mesh.h"
71 #include "ED_screen.h"
72 #include "ED_view3d.h"
73 #include "ED_armature.h"
74
75 #include "UI_interface.h"
76 #include "UI_resources.h"
77 #include "UI_view2d.h"
78
79 #include "PIL_time.h" /* smoothview */
80
81 #include "view3d_intern.h"      // own include
82
83
84 /* use this call when executing an operator,
85    event system doesn't set for each event the
86    opengl drawing context */
87 void view3d_operator_needs_opengl(const bContext *C)
88 {
89         ARegion *ar= CTX_wm_region(C);
90
91         /* for debugging purpose, context should always be OK */
92         if(ar->regiontype!=RGN_TYPE_WINDOW)
93                 printf("view3d_operator_needs_opengl error, wrong region\n");
94         else {
95                 RegionView3D *rv3d= ar->regiondata;
96                 
97                 wmSubWindowSet(CTX_wm_window(C), ar->swinid);
98                 glMatrixMode(GL_PROJECTION);
99                 wmLoadMatrix(rv3d->winmat);
100                 glMatrixMode(GL_MODELVIEW);
101                 wmLoadMatrix(rv3d->viewmat);
102         }
103 }
104
105 float *give_cursor(Scene *scene, View3D *v3d)
106 {
107         if(v3d && v3d->localview) return v3d->cursor;
108         else return scene->cursor;
109 }
110
111
112 /* Gets the lens and clipping values from a camera of lamp type object */
113 static void object_lens_clip_settings(Object *ob, float *lens, float *clipsta, float *clipend)
114 {       
115         if (!ob) return;
116         
117         if(ob->type==OB_LAMP ) {
118                 Lamp *la = ob->data;
119                 if (lens) {
120                         float x1, fac;
121                         fac= cos( M_PI*la->spotsize/360.0);
122                         x1= saacos(fac);
123                         *lens= 16.0*fac/sin(x1);
124                 }
125                 if (clipsta)    *clipsta= la->clipsta;
126                 if (clipend)    *clipend= la->clipend;
127         }
128         else if(ob->type==OB_CAMERA) {
129                 Camera *cam= ob->data;
130                 if (lens)               *lens= cam->lens;
131                 if (clipsta)    *clipsta= cam->clipsta;
132                 if (clipend)    *clipend= cam->clipend;
133         }
134         else {
135                 if (lens)               *lens= 35.0f;
136         }
137 }
138
139
140 /* Gets the view trasnformation from a camera
141 * currently dosnt take camzoom into account
142
143 * The dist is not modified for this function, if NULL its assimed zero
144 * */
145 static void view_settings_from_ob(Object *ob, float *ofs, float *quat, float *dist, float *lens)
146 {       
147         float bmat[4][4];
148         float imat[4][4];
149         float tmat[3][3];
150         
151         if (!ob) return;
152         
153         /* Offset */
154         if (ofs) {
155                 VECCOPY(ofs, ob->obmat[3]);
156                 VecMulf(ofs, -1.0f); /*flip the vector*/
157         }
158         
159         /* Quat */
160         if (quat) {
161                 Mat4CpyMat4(bmat, ob->obmat);
162                 Mat4Ortho(bmat);
163                 Mat4Invert(imat, bmat);
164                 Mat3CpyMat4(tmat, imat);
165                 Mat3ToQuat(tmat, quat);
166         }
167         
168         if (dist) {
169                 float vec[3];
170                 Mat3CpyMat4(tmat, ob->obmat);
171                 
172                 vec[0]= vec[1] = 0.0;
173                 vec[2]= -(*dist);
174                 Mat3MulVecfl(tmat, vec);
175                 VecSubf(ofs, ofs, vec);
176         }
177         
178         /* Lens */
179         if (lens)
180                 object_lens_clip_settings(ob, lens, NULL, NULL);
181 }
182
183
184 /* ****************** smooth view operator ****************** */
185
186 struct SmoothViewStore {
187         float orig_dist, new_dist;
188         float orig_lens, new_lens;
189         float orig_quat[4], new_quat[4];
190         float orig_ofs[3], new_ofs[3];
191         
192         int to_camera, orig_view;
193         
194         double time_allowed;
195 };
196
197 /* will start timer if appropriate */
198 /* the arguments are the desired situation */
199 void smooth_view(bContext *C, Object *oldcamera, Object *camera, float *ofs, float *quat, float *dist, float *lens)
200 {
201         View3D *v3d = CTX_wm_view3d(C);
202         RegionView3D *rv3d= CTX_wm_region_view3d(C);
203         struct SmoothViewStore sms;
204         
205         /* initialize sms */
206         memset(&sms,0,sizeof(struct SmoothViewStore));
207         VECCOPY(sms.new_ofs, rv3d->ofs);
208         QUATCOPY(sms.new_quat, rv3d->viewquat);
209         sms.new_dist= rv3d->dist;
210         sms.new_lens= v3d->lens;
211         sms.to_camera= 0;
212         
213         /* store the options we want to end with */
214         if(ofs) VECCOPY(sms.new_ofs, ofs);
215         if(quat) QUATCOPY(sms.new_quat, quat);
216         if(dist) sms.new_dist= *dist;
217         if(lens) sms.new_lens= *lens;
218         
219         if (camera) {
220                 view_settings_from_ob(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens);
221                 sms.to_camera= 1; /* restore view3d values in end */
222         }
223         
224         if (C && U.smooth_viewtx) {
225                 int changed = 0; /* zero means no difference */
226                 
227                 if (sms.new_dist != rv3d->dist)
228                         changed = 1;
229                 if (sms.new_lens != v3d->lens)
230                         changed = 1;
231                 
232                 if ((sms.new_ofs[0]!=rv3d->ofs[0]) ||
233                         (sms.new_ofs[1]!=rv3d->ofs[1]) ||
234                         (sms.new_ofs[2]!=rv3d->ofs[2]) )
235                         changed = 1;
236                 
237                 if ((sms.new_quat[0]!=rv3d->viewquat[0]) ||
238                         (sms.new_quat[1]!=rv3d->viewquat[1]) ||
239                         (sms.new_quat[2]!=rv3d->viewquat[2]) ||
240                         (sms.new_quat[3]!=rv3d->viewquat[3]) )
241                         changed = 1;
242                 
243                 /* The new view is different from the old one
244                         * so animate the view */
245                 if (changed) {
246                         
247                         sms.time_allowed= (double)U.smooth_viewtx / 1000.0;
248                         
249                         /* if this is view rotation only
250                                 * we can decrease the time allowed by
251                                 * the angle between quats 
252                                 * this means small rotations wont lag */
253                         if (quat && !ofs && !dist) {
254                                 float vec1[3], vec2[3];
255                                 
256                                 VECCOPY(vec1, sms.new_quat);
257                                 VECCOPY(vec2, sms.orig_quat);
258                                 Normalize(vec1);
259                                 Normalize(vec2);
260                                 /* scale the time allowed by the rotation */
261                                 sms.time_allowed *= NormalizedVecAngle2(vec1, vec2)/(M_PI/2); 
262                         }
263                         
264                         /* original values */
265                         if (oldcamera) {
266                                 sms.orig_dist= rv3d->dist; // below function does weird stuff with it...
267                                 view_settings_from_ob(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens);
268                         }
269                         else {
270                                 VECCOPY(sms.orig_ofs, rv3d->ofs);
271                                 QUATCOPY(sms.orig_quat, rv3d->viewquat);
272                                 sms.orig_dist= rv3d->dist;
273                                 sms.orig_lens= v3d->lens;
274                         }
275                         /* grid draw as floor */
276                         sms.orig_view= rv3d->view;
277                         rv3d->view= 0;
278                         
279                         /* ensure it shows correct */
280                         if(sms.to_camera) rv3d->persp= V3D_PERSP;
281                         
282                         /* keep track of running timer! */
283                         if(rv3d->sms==NULL)
284                                 rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d");
285                         *rv3d->sms= sms;
286                         if(rv3d->smooth_timer)
287                                 WM_event_remove_window_timer(CTX_wm_window(C), rv3d->smooth_timer);
288                         /* TIMER1 is hardcoded in keymap */
289                         rv3d->smooth_timer= WM_event_add_window_timer(CTX_wm_window(C), TIMER1, 1.0/30.0);      /* max 30 frs/sec */
290                         
291                         return;
292                 }
293         }
294         
295         /* if we get here nothing happens */
296         if(sms.to_camera==0) {
297                 VECCOPY(rv3d->ofs, sms.new_ofs);
298                 QUATCOPY(rv3d->viewquat, sms.new_quat);
299                 rv3d->dist = sms.new_dist;
300                 v3d->lens = sms.new_lens;
301         }
302         ED_region_tag_redraw(CTX_wm_region(C));
303 }
304
305 /* only meant for timer usage */
306 static int view3d_smoothview_invoke(bContext *C, wmOperator *op, wmEvent *event)
307 {
308         View3D *v3d = CTX_wm_view3d(C);
309         RegionView3D *rv3d= CTX_wm_region_view3d(C);
310         struct SmoothViewStore *sms= rv3d->sms;
311         double step, step_inv;
312         
313         /* escape if not our timer */
314         if(rv3d->smooth_timer==NULL || rv3d->smooth_timer!=event->customdata)
315                 return OPERATOR_PASS_THROUGH;
316         
317         step =  (rv3d->smooth_timer->duration)/sms->time_allowed;
318         
319         /* end timer */
320         if(step >= 1.0f) {
321                 
322                 /* if we went to camera, store the original */
323                 if(sms->to_camera) {
324                         rv3d->persp= V3D_CAMOB;
325                         VECCOPY(rv3d->ofs, sms->orig_ofs);
326                         QUATCOPY(rv3d->viewquat, sms->orig_quat);
327                         rv3d->dist = sms->orig_dist;
328                         v3d->lens = sms->orig_lens;
329                 }
330                 else {
331                         VECCOPY(rv3d->ofs, sms->new_ofs);
332                         QUATCOPY(rv3d->viewquat, sms->new_quat);
333                         rv3d->dist = sms->new_dist;
334                         v3d->lens = sms->new_lens;
335                 }
336                 rv3d->view= sms->orig_view;
337                 
338                 MEM_freeN(rv3d->sms);
339                 rv3d->sms= NULL;
340                 
341                 WM_event_remove_window_timer(CTX_wm_window(C), rv3d->smooth_timer);
342                 rv3d->smooth_timer= NULL;
343         }
344         else {
345                 int i;
346                 
347                 /* ease in/out */
348                 if (step < 0.5) step = (float)pow(step*2, 2)/2;
349                 else                    step = (float)1-(pow(2*(1-step),2)/2);
350
351                 step_inv = 1.0-step;
352
353                 for (i=0; i<3; i++)
354                         rv3d->ofs[i] = sms->new_ofs[i]*step + sms->orig_ofs[i]*step_inv;
355
356                 QuatInterpol(rv3d->viewquat, sms->orig_quat, sms->new_quat, step);
357                 
358                 rv3d->dist = sms->new_dist*step + sms->orig_dist*step_inv;
359                 v3d->lens = sms->new_lens*step + sms->orig_lens*step_inv;
360         }
361         
362         ED_region_tag_redraw(CTX_wm_region(C));
363         
364         return OPERATOR_FINISHED;
365 }
366
367 void VIEW3D_OT_smoothview(wmOperatorType *ot)
368 {
369         
370         /* identifiers */
371         ot->name= "Smooth View";
372         ot->idname= "VIEW3D_OT_smoothview";
373         
374         /* api callbacks */
375         ot->invoke= view3d_smoothview_invoke;
376         
377         ot->poll= ED_operator_view3d_active;
378 }
379
380 static int view3d_setcameratoview_exec(bContext *C, wmOperator *op)
381 {
382         View3D *v3d = CTX_wm_view3d(C);
383         RegionView3D *rv3d= CTX_wm_region_view3d(C);
384         Object *ob;
385         float dvec[3];
386         
387         ob= v3d->camera;
388         dvec[0]= rv3d->dist*rv3d->viewinv[2][0];
389         dvec[1]= rv3d->dist*rv3d->viewinv[2][1];
390         dvec[2]= rv3d->dist*rv3d->viewinv[2][2];
391         
392         VECCOPY(ob->loc, dvec);
393         VecSubf(ob->loc, ob->loc, v3d->ofs);
394         rv3d->viewquat[0]= -rv3d->viewquat[0];
395
396         QuatToEul(rv3d->viewquat, ob->rot);
397         rv3d->viewquat[0]= -rv3d->viewquat[0];
398         
399         ob->recalc= OB_RECALC_OB;
400         
401         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, CTX_data_scene(C));
402         
403         return OPERATOR_FINISHED;
404
405 }
406
407 void VIEW3D_OT_setcameratoview(wmOperatorType *ot)
408 {
409         
410         /* identifiers */
411         ot->name= "Align Camera To View";
412         ot->idname= "VIEW3D_OT_camera_to_view";
413         
414         /* api callbacks */
415         ot->exec= view3d_setcameratoview_exec;  
416         ot->poll= ED_operator_view3d_active;
417         
418         /* flags */
419         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
420 }
421
422 /* ********************************** */
423
424 /* create intersection coordinates in view Z direction at mouse coordinates */
425 void viewline(ARegion *ar, View3D *v3d, short mval[2], float ray_start[3], float ray_end[3])
426 {
427         RegionView3D *rv3d= ar->regiondata;
428         float vec[4];
429         
430         if(rv3d->persp != V3D_ORTHO){
431                 vec[0]= 2.0f * mval[0] / ar->winx - 1;
432                 vec[1]= 2.0f * mval[1] / ar->winy - 1;
433                 vec[2]= -1.0f;
434                 vec[3]= 1.0f;
435                 
436                 Mat4MulVec4fl(rv3d->persinv, vec);
437                 VecMulf(vec, 1.0f / vec[3]);
438                 
439                 VECCOPY(ray_start, rv3d->viewinv[3]);
440                 VECSUB(vec, vec, ray_start);
441                 Normalize(vec);
442                 
443                 VECADDFAC(ray_start, rv3d->viewinv[3], vec, v3d->near);
444                 VECADDFAC(ray_end, rv3d->viewinv[3], vec, v3d->far);
445         }
446         else {
447                 vec[0] = 2.0f * mval[0] / ar->winx - 1;
448                 vec[1] = 2.0f * mval[1] / ar->winy - 1;
449                 vec[2] = 0.0f;
450                 vec[3] = 1.0f;
451                 
452                 Mat4MulVec4fl(rv3d->persinv, vec);
453                 
454                 VECADDFAC(ray_start, vec, rv3d->viewinv[2],  1000.0f);
455                 VECADDFAC(ray_end, vec, rv3d->viewinv[2], -1000.0f);
456         }
457 }
458
459 /* create intersection ray in view Z direction at mouse coordinates */
460 void viewray(ARegion *ar, View3D *v3d, short mval[2], float ray_start[3], float ray_normal[3])
461 {
462         float ray_end[3];
463         
464         viewline(ar, v3d, mval, ray_start, ray_end);
465         VecSubf(ray_normal, ray_end, ray_start);
466         Normalize(ray_normal);
467 }
468
469
470 void initgrabz(RegionView3D *rv3d, float x, float y, float z)
471 {
472         if(rv3d==NULL) return;
473         rv3d->zfac= rv3d->persmat[0][3]*x+ rv3d->persmat[1][3]*y+ rv3d->persmat[2][3]*z+ rv3d->persmat[3][3];
474         
475         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that 
476                 * (accounting for near zero values)
477                 * */
478         if (rv3d->zfac < 1.e-6f && rv3d->zfac > -1.e-6f) rv3d->zfac = 1.0f;
479         
480         /* Negative zfac means x, y, z was behind the camera (in perspective).
481                 * This gives flipped directions, so revert back to ok default case.
482         */
483         if (rv3d->zfac < 0.0f) rv3d->zfac = 1.0f;
484 }
485
486 /* always call initgrabz */
487 void window_to_3d(ARegion *ar, float *vec, short mx, short my)
488 {
489         RegionView3D *rv3d= ar->regiondata;
490         
491         float dx= ((float)(mx-(ar->winx/2)))*rv3d->zfac/(ar->winx/2);
492         float dy= ((float)(my-(ar->winy/2)))*rv3d->zfac/(ar->winy/2);
493         
494         float fz= rv3d->persmat[0][3]*vec[0]+ rv3d->persmat[1][3]*vec[1]+ rv3d->persmat[2][3]*vec[2]+ rv3d->persmat[3][3];
495         fz= fz/rv3d->zfac;
496         
497         vec[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy+ rv3d->persinv[2][0]*fz)-rv3d->ofs[0];
498         vec[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy+ rv3d->persinv[2][1]*fz)-rv3d->ofs[1];
499         vec[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy+ rv3d->persinv[2][2]*fz)-rv3d->ofs[2];
500         
501 }
502
503 /* always call initgrabz */
504 /* only to detect delta motion */
505 void window_to_3d_delta(ARegion *ar, float *vec, short mx, short my)
506 {
507         RegionView3D *rv3d= ar->regiondata;
508         float dx, dy;
509         
510         dx= 2.0f*mx*rv3d->zfac/ar->winx;
511         dy= 2.0f*my*rv3d->zfac/ar->winy;
512         
513         vec[0]= (rv3d->persinv[0][0]*dx + rv3d->persinv[1][0]*dy);
514         vec[1]= (rv3d->persinv[0][1]*dx + rv3d->persinv[1][1]*dy);
515         vec[2]= (rv3d->persinv[0][2]*dx + rv3d->persinv[1][2]*dy);
516 }
517
518 float read_cached_depth(ViewContext *vc, int x, int y)
519 {
520         ViewDepths *vd = vc->rv3d->depths;
521                 
522         x -= vc->ar->winrct.xmin;
523         y -= vc->ar->winrct.ymin;
524
525         if(vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
526                 return vd->depths[y * vd->w + x];
527         else
528                 return 1;
529 }
530
531 void request_depth_update(RegionView3D *rv3d)
532 {
533         if(rv3d->depths)
534                 rv3d->depths->damaged= 1;
535 }
536
537 void view3d_get_object_project_mat(RegionView3D *rv3d, Object *ob, float pmat[4][4])
538 {
539         float vmat[4][4];
540         
541         Mat4MulMat4(vmat, ob->obmat, rv3d->viewmat);
542         Mat4MulMat4(pmat, vmat, rv3d->winmat);
543 }
544
545
546 /* use above call to get projecting mat */
547 void view3d_project_float(ARegion *ar, float *vec, float *adr, float mat[4][4])
548 {
549         float vec4[4];
550         
551         adr[0]= IS_CLIPPED;
552         VECCOPY(vec4, vec);
553         vec4[3]= 1.0;
554         
555         Mat4MulVec4fl(mat, vec4);
556         
557         if( vec4[3]>FLT_EPSILON ) {
558                 adr[0] = (float)(ar->winx/2.0f)+(ar->winx/2.0f)*vec4[0]/vec4[3];        
559                 adr[1] = (float)(ar->winy/2.0f)+(ar->winy/2.0f)*vec4[1]/vec4[3];
560         } else {
561                 adr[0] = adr[1] = 0.0f;
562         }
563 }
564
565 int boundbox_clip(RegionView3D *rv3d, float obmat[][4], BoundBox *bb)
566 {
567         /* return 1: draw */
568         
569         float mat[4][4];
570         float vec[4], min, max;
571         int a, flag= -1, fl;
572         
573         if(bb==NULL) return 1;
574         if(bb->flag & OB_BB_DISABLED) return 1;
575         
576         Mat4MulMat4(mat, obmat, rv3d->persmat);
577         
578         for(a=0; a<8; a++) {
579                 VECCOPY(vec, bb->vec[a]);
580                 vec[3]= 1.0;
581                 Mat4MulVec4fl(mat, vec);
582                 max= vec[3];
583                 min= -vec[3];
584                 
585                 fl= 0;
586                 if(vec[0] < min) fl+= 1;
587                 if(vec[0] > max) fl+= 2;
588                 if(vec[1] < min) fl+= 4;
589                 if(vec[1] > max) fl+= 8;
590                 if(vec[2] < min) fl+= 16;
591                 if(vec[2] > max) fl+= 32;
592                 
593                 flag &= fl;
594                 if(flag==0) return 1;
595         }
596         
597         return 0;
598 }
599
600 void project_short(ARegion *ar, float *vec, short *adr) /* clips */
601 {
602         RegionView3D *rv3d= ar->regiondata;
603         float fx, fy, vec4[4];
604         
605         adr[0]= IS_CLIPPED;
606         
607         if(rv3d->rflag & RV3D_CLIPPING) {
608                 if(view3d_test_clipping(rv3d, vec))
609                         return;
610         }
611         
612         VECCOPY(vec4, vec);
613         vec4[3]= 1.0;
614         Mat4MulVec4fl(rv3d->persmat, vec4);
615         
616         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
617                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
618                 
619                 if( fx>0 && fx<ar->winx) {
620                         
621                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
622                         
623                         if(fy>0.0 && fy< (float)ar->winy) {
624                                 adr[0]= (short)floor(fx); 
625                                 adr[1]= (short)floor(fy);
626                         }
627                 }
628         }
629 }
630
631 void project_int(ARegion *ar, float *vec, int *adr)
632 {
633         RegionView3D *rv3d= ar->regiondata;
634         float fx, fy, vec4[4];
635         
636         adr[0]= (int)2140000000.0f;
637         VECCOPY(vec4, vec);
638         vec4[3]= 1.0;
639         
640         Mat4MulVec4fl(rv3d->persmat, vec4);
641         
642         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
643                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
644                 
645                 if( fx>-2140000000.0f && fx<2140000000.0f) {
646                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
647                         
648                         if(fy>-2140000000.0f && fy<2140000000.0f) {
649                                 adr[0]= (int)floor(fx); 
650                                 adr[1]= (int)floor(fy);
651                         }
652                 }
653         }
654 }
655
656 void project_int_noclip(ARegion *ar, float *vec, int *adr)
657 {
658         RegionView3D *rv3d= ar->regiondata;
659         float fx, fy, vec4[4];
660         
661         VECCOPY(vec4, vec);
662         vec4[3]= 1.0;
663         
664         Mat4MulVec4fl(rv3d->persmat, vec4);
665         
666         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
667                 fx = (ar->winx/2)*(1 + vec4[0]/vec4[3]);
668                 fy = (ar->winy/2)*(1 + vec4[1]/vec4[3]);
669                 
670                 adr[0] = (int)floor(fx); 
671                 adr[1] = (int)floor(fy);
672         }
673         else
674         {
675                 adr[0] = ar->winx / 2;
676                 adr[1] = ar->winy / 2;
677         }
678 }
679
680 void project_short_noclip(ARegion *ar, float *vec, short *adr)
681 {
682         RegionView3D *rv3d= ar->regiondata;
683         float fx, fy, vec4[4];
684         
685         adr[0]= IS_CLIPPED;
686         VECCOPY(vec4, vec);
687         vec4[3]= 1.0;
688         
689         Mat4MulVec4fl(rv3d->persmat, vec4);
690         
691         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
692                 fx= (ar->winx/2)*(1 + vec4[0]/vec4[3]);
693                 
694                 if( fx>-32700 && fx<32700) {
695                         
696                         fy= (ar->winy/2)*(1 + vec4[1]/vec4[3]);
697                         
698                         if(fy>-32700.0 && fy<32700.0) {
699                                 adr[0]= (short)floor(fx); 
700                                 adr[1]= (short)floor(fy);
701                         }
702                 }
703         }
704 }
705
706 void project_float(ARegion *ar, float *vec, float *adr)
707 {
708         RegionView3D *rv3d= ar->regiondata;
709         float vec4[4];
710         
711         adr[0]= IS_CLIPPED;
712         VECCOPY(vec4, vec);
713         vec4[3]= 1.0;
714         
715         Mat4MulVec4fl(rv3d->persmat, vec4);
716         
717         if( vec4[3]>BL_NEAR_CLIP ) {
718                 adr[0] = (float)(ar->winx/2.0)+(ar->winx/2.0)*vec4[0]/vec4[3];  
719                 adr[1] = (float)(ar->winy/2.0)+(ar->winy/2.0)*vec4[1]/vec4[3];
720         }
721 }
722
723 void project_float_noclip(ARegion *ar, float *vec, float *adr)
724 {
725         RegionView3D *rv3d= ar->regiondata;
726         float vec4[4];
727         
728         VECCOPY(vec4, vec);
729         vec4[3]= 1.0;
730         
731         Mat4MulVec4fl(rv3d->persmat, vec4);
732         
733         if( fabs(vec4[3]) > BL_NEAR_CLIP ) {
734                 adr[0] = (float)(ar->winx/2.0)+(ar->winx/2.0)*vec4[0]/vec4[3];  
735                 adr[1] = (float)(ar->winy/2.0)+(ar->winy/2.0)*vec4[1]/vec4[3];
736         }
737         else
738         {
739                 adr[0] = ar->winx / 2.0f;
740                 adr[1] = ar->winy / 2.0f;
741         }
742 }
743
744 int get_view3d_ortho(View3D *v3d, RegionView3D *rv3d)
745 {
746   Camera *cam;
747   
748   if(rv3d->persp==V3D_CAMOB) {
749       if(v3d->camera && v3d->camera->type==OB_CAMERA) {
750           cam= v3d->camera->data;
751
752           if(cam && cam->type==CAM_ORTHO)
753               return 1;
754           else
755               return 0;
756       }
757       else
758           return 0;
759   }
760   
761   if(rv3d->persp==V3D_ORTHO)
762       return 1;
763
764   return 0;
765 }
766
767 /* also exposed in previewrender.c */
768 int get_view3d_viewplane(View3D *v3d, RegionView3D *rv3d, int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend, float *pixsize)
769 {
770         Camera *cam=NULL;
771         float lens, fac, x1, y1, x2, y2;
772         float winx= (float)winxi, winy= (float)winyi;
773         int orth= 0;
774         
775         lens= v3d->lens;        
776         
777         *clipsta= v3d->near;
778         *clipend= v3d->far;
779         
780         if(rv3d->persp==V3D_CAMOB) {
781                 if(v3d->camera) {
782                         if(v3d->camera->type==OB_LAMP ) {
783                                 Lamp *la;
784                                 
785                                 la= v3d->camera->data;
786                                 fac= cos( M_PI*la->spotsize/360.0);
787                                 
788                                 x1= saacos(fac);
789                                 lens= 16.0*fac/sin(x1);
790                                 
791                                 *clipsta= la->clipsta;
792                                 *clipend= la->clipend;
793                         }
794                         else if(v3d->camera->type==OB_CAMERA) {
795                                 cam= v3d->camera->data;
796                                 lens= cam->lens;
797                                 *clipsta= cam->clipsta;
798                                 *clipend= cam->clipend;
799                         }
800                 }
801         }
802         
803         if(rv3d->persp==V3D_ORTHO) {
804                 if(winx>winy) x1= -rv3d->dist;
805                 else x1= -winx*rv3d->dist/winy;
806                 x2= -x1;
807                 
808                 if(winx>winy) y1= -winy*rv3d->dist/winx;
809                 else y1= -rv3d->dist;
810                 y2= -y1;
811                 
812                 *clipend *= 0.5;        // otherwise too extreme low zbuffer quality
813                 *clipsta= - *clipend;
814                 orth= 1;
815         }
816         else {
817                 /* fac for zoom, also used for camdx */
818                 if(rv3d->persp==V3D_CAMOB) {
819                         fac= (1.41421+( (float)rv3d->camzoom )/50.0);
820                         fac*= fac;
821                 }
822                 else fac= 2.0;
823                 
824                 /* viewplane size depends... */
825                 if(cam && cam->type==CAM_ORTHO) {
826                         /* ortho_scale == 1 means exact 1 to 1 mapping */
827                         float dfac= 2.0*cam->ortho_scale/fac;
828                         
829                         if(winx>winy) x1= -dfac;
830                         else x1= -winx*dfac/winy;
831                         x2= -x1;
832                         
833                         if(winx>winy) y1= -winy*dfac/winx;
834                         else y1= -dfac;
835                         y2= -y1;
836                         orth= 1;
837                 }
838                 else {
839                         float dfac;
840                         
841                         if(winx>winy) dfac= 64.0/(fac*winx*lens);
842                         else dfac= 64.0/(fac*winy*lens);
843                         
844                         x1= - *clipsta * winx*dfac;
845                         x2= -x1;
846                         y1= - *clipsta * winy*dfac;
847                         y2= -y1;
848                         orth= 0;
849                 }
850                 /* cam view offset */
851                 if(cam) {
852                         float dx= 0.5*fac*rv3d->camdx*(x2-x1);
853                         float dy= 0.5*fac*rv3d->camdy*(y2-y1);
854                         x1+= dx;
855                         x2+= dx;
856                         y1+= dy;
857                         y2+= dy;
858                 }
859         }
860         
861         if(pixsize) {
862                 float viewfac;
863                 
864                 if(orth) {
865                         viewfac= (winx >= winy)? winx: winy;
866                         *pixsize= 1.0f/viewfac;
867                 }
868                 else {
869                         viewfac= (((winx >= winy)? winx: winy)*lens)/32.0;
870                         *pixsize= *clipsta/viewfac;
871                 }
872         }
873         
874         viewplane->xmin= x1;
875         viewplane->ymin= y1;
876         viewplane->xmax= x2;
877         viewplane->ymax= y2;
878         
879         return orth;
880 }
881
882 void setwinmatrixview3d(ARegion *ar, View3D *v3d, rctf *rect)           /* rect: for picking */
883 {
884         RegionView3D *rv3d= ar->regiondata;
885         rctf viewplane;
886         float clipsta, clipend, x1, y1, x2, y2;
887         int orth;
888         
889         orth= get_view3d_viewplane(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL);
890         //      printf("%d %d %f %f %f %f %f %f\n", winx, winy, viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax, clipsta, clipend);
891         x1= viewplane.xmin;
892         y1= viewplane.ymin;
893         x2= viewplane.xmax;
894         y2= viewplane.ymax;
895         
896         if(rect) {              /* picking */
897                 rect->xmin/= (float)ar->winx;
898                 rect->xmin= x1+rect->xmin*(x2-x1);
899                 rect->ymin/= (float)ar->winy;
900                 rect->ymin= y1+rect->ymin*(y2-y1);
901                 rect->xmax/= (float)ar->winx;
902                 rect->xmax= x1+rect->xmax*(x2-x1);
903                 rect->ymax/= (float)ar->winy;
904                 rect->ymax= y1+rect->ymax*(y2-y1);
905                 
906                 if(orth) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
907                 else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
908                 
909         }
910         else {
911                 if(orth) wmOrtho(x1, x2, y1, y2, clipsta, clipend);
912                 else wmFrustum(x1, x2, y1, y2, clipsta, clipend);
913         }
914
915         /* not sure what this was for? (ton) */
916         glMatrixMode(GL_PROJECTION);
917         wmGetMatrix(rv3d->winmat);
918         glMatrixMode(GL_MODELVIEW);
919 }
920
921
922 static void obmat_to_viewmat(View3D *v3d, RegionView3D *rv3d, Object *ob, short smooth)
923 {
924         float bmat[4][4];
925         float tmat[3][3];
926         
927         rv3d->view= 0; /* dont show the grid */
928         
929         Mat4CpyMat4(bmat, ob->obmat);
930         Mat4Ortho(bmat);
931         Mat4Invert(rv3d->viewmat, bmat);
932         
933         /* view quat calculation, needed for add object */
934         Mat3CpyMat4(tmat, rv3d->viewmat);
935         if (smooth) {
936                 float new_quat[4];
937                 if (rv3d->persp==V3D_CAMOB && v3d->camera) {
938                         /* were from a camera view */
939                         
940                         float orig_ofs[3];
941                         float orig_dist= rv3d->dist;
942                         float orig_lens= v3d->lens;
943                         VECCOPY(orig_ofs, rv3d->ofs);
944                         
945                         /* Switch from camera view */
946                         Mat3ToQuat(tmat, new_quat);
947                         
948                         rv3d->persp=V3D_PERSP;
949                         rv3d->dist= 0.0;
950                         
951                         view_settings_from_ob(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
952                         smooth_view(NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
953                         
954                         rv3d->persp=V3D_CAMOB; /* just to be polite, not needed */
955                         
956                 } else {
957                         Mat3ToQuat(tmat, new_quat);
958                         smooth_view(NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
959                 }
960         } else {
961                 Mat3ToQuat(tmat, rv3d->viewquat);
962         }
963 }
964
965 #define QUATSET(a, b, c, d, e)  a[0]=b; a[1]=c; a[2]=d; a[3]=e; 
966
967 static void view3d_viewlock(RegionView3D *rv3d)
968 {
969         switch(rv3d->view) {
970         case V3D_VIEW_BOTTOM :
971                 QUATSET(rv3d->viewquat,0.0, -1.0, 0.0, 0.0);
972                 break;
973                 
974         case V3D_VIEW_BACK:
975                 QUATSET(rv3d->viewquat,0.0, 0.0, (float)-cos(M_PI/4.0), (float)-cos(M_PI/4.0));
976                 break;
977                 
978         case V3D_VIEW_LEFT:
979                 QUATSET(rv3d->viewquat,0.5, -0.5, 0.5, 0.5);
980                 break;
981                 
982         case V3D_VIEW_TOP:
983                 QUATSET(rv3d->viewquat,1.0, 0.0, 0.0, 0.0);
984                 break;
985                 
986         case V3D_VIEW_FRONT:
987                 QUATSET(rv3d->viewquat,(float)cos(M_PI/4.0), (float)-sin(M_PI/4.0), 0.0, 0.0);
988                 break;
989                 
990         case V3D_VIEW_RIGHT:
991                 QUATSET(rv3d->viewquat, 0.5, -0.5, -0.5, -0.5);
992                 break;
993         }
994 }
995
996 /* dont set windows active in in here, is used by renderwin too */
997 void setviewmatrixview3d(Scene *scene, View3D *v3d, RegionView3D *rv3d)
998 {
999         if(rv3d->persp==V3D_CAMOB) {        /* obs/camera */
1000                 if(v3d->camera) {
1001                         where_is_object(scene, v3d->camera);    
1002                         obmat_to_viewmat(v3d, rv3d, v3d->camera, 0);
1003                 }
1004                 else {
1005                         QuatToMat4(rv3d->viewquat, rv3d->viewmat);
1006                         rv3d->viewmat[3][2]-= rv3d->dist;
1007                 }
1008         }
1009         else {
1010                 /* should be moved to better initialize later on XXX */
1011                 if(rv3d->viewlock)
1012                         view3d_viewlock(rv3d);
1013                 
1014                 QuatToMat4(rv3d->viewquat, rv3d->viewmat);
1015                 if(rv3d->persp==V3D_PERSP) rv3d->viewmat[3][2]-= rv3d->dist;
1016                 if(v3d->ob_centre) {
1017                         Object *ob= v3d->ob_centre;
1018                         float vec[3];
1019                         
1020                         VECCOPY(vec, ob->obmat[3]);
1021                         if(ob->type==OB_ARMATURE && v3d->ob_centre_bone[0]) {
1022                                 bPoseChannel *pchan= get_pose_channel(ob->pose, v3d->ob_centre_bone);
1023                                 if(pchan) {
1024                                         VECCOPY(vec, pchan->pose_mat[3]);
1025                                         Mat4MulVecfl(ob->obmat, vec);
1026                                 }
1027                         }
1028                         i_translate(-vec[0], -vec[1], -vec[2], rv3d->viewmat);
1029                 }
1030                 else i_translate(rv3d->ofs[0], rv3d->ofs[1], rv3d->ofs[2], rv3d->viewmat);
1031         }
1032 }
1033
1034 /* IGLuint-> GLuint*/
1035 /* Warning: be sure to account for a negative return value
1036 *   This is an error, "Too many objects in select buffer"
1037 *   and no action should be taken (can crash blender) if this happens
1038 */
1039 short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input)
1040 {
1041         Scene *scene= vc->scene;
1042         View3D *v3d= vc->v3d;
1043         ARegion *ar= vc->ar;
1044         rctf rect;
1045         short code, hits;
1046         
1047         G.f |= G_PICKSEL;
1048         
1049         /* case not a border select */
1050         if(input->xmin==input->xmax) {
1051                 rect.xmin= input->xmin-12;      // seems to be default value for bones only now
1052                 rect.xmax= input->xmin+12;
1053                 rect.ymin= input->ymin-12;
1054                 rect.ymax= input->ymin+12;
1055         }
1056         else {
1057                 rect.xmin= input->xmin;
1058                 rect.xmax= input->xmax;
1059                 rect.ymin= input->ymin;
1060                 rect.ymax= input->ymax;
1061         }
1062         
1063         setwinmatrixview3d(ar, v3d, &rect);
1064         Mat4MulMat4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1065         
1066         if(v3d->drawtype > OB_WIRE) {
1067                 v3d->zbuf= TRUE;
1068                 glEnable(GL_DEPTH_TEST);
1069         }
1070         
1071         if(vc->rv3d->rflag & RV3D_CLIPPING)
1072                 view3d_set_clipping(vc->rv3d);
1073         
1074         glSelectBuffer( bufsize, (GLuint *)buffer);
1075         glRenderMode(GL_SELECT);
1076         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1077         glPushName(-1);
1078         code= 1;
1079         
1080         if(vc->obedit && vc->obedit->type==OB_MBALL) {
1081                 draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1082         }
1083         else if((vc->obedit && vc->obedit->type==OB_ARMATURE)) {
1084                 /* if not drawing sketch, draw bones */
1085                 if(!BDR_drawSketchNames(vc)) {
1086                         draw_object(scene, ar, v3d, BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1087                 }
1088         }
1089         else {
1090                 Base *base;
1091                 
1092                 v3d->xray= TRUE;        // otherwise it postpones drawing
1093                 for(base= scene->base.first; base; base= base->next) {
1094                         if(base->lay & v3d->lay) {
1095                                 
1096                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1097                                         base->selcol= 0;
1098                                 else {
1099                                         base->selcol= code;
1100                                         glLoadName(code);
1101                                         draw_object(scene, ar, v3d, base, DRAW_PICKING|DRAW_CONSTCOLOR);
1102                                         
1103                                         /* we draw group-duplicators for selection too */
1104                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1105                                                 ListBase *lb;
1106                                                 DupliObject *dob;
1107                                                 Base tbase;
1108                                                 
1109                                                 tbase.flag= OB_FROMDUPLI;
1110                                                 lb= object_duplilist(scene, base->object);
1111                                                 
1112                                                 for(dob= lb->first; dob; dob= dob->next) {
1113                                                         tbase.object= dob->ob;
1114                                                         Mat4CpyMat4(dob->ob->obmat, dob->mat);
1115                                                         
1116                                                         draw_object(scene, ar, v3d, &tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1117                                                         
1118                                                         Mat4CpyMat4(dob->ob->obmat, dob->omat);
1119                                                 }
1120                                                 free_object_duplilist(lb);
1121                                         }
1122                                         code++;
1123                                 }                               
1124                         }
1125                 }
1126                 v3d->xray= FALSE;       // restore
1127         }
1128         
1129         glPopName();    /* see above (pushname) */
1130         hits= glRenderMode(GL_RENDER);
1131         
1132         G.f &= ~G_PICKSEL;
1133         setwinmatrixview3d(ar, v3d, NULL);
1134         Mat4MulMat4(vc->rv3d->persmat, vc->rv3d->viewmat, vc->rv3d->winmat);
1135         
1136         if(v3d->drawtype > OB_WIRE) {
1137                 v3d->zbuf= 0;
1138                 glDisable(GL_DEPTH_TEST);
1139         }
1140 // XXX  persp(PERSP_WIN);
1141         
1142         if(vc->rv3d->rflag & RV3D_CLIPPING)
1143                 view3d_clr_clipping();
1144         
1145         if(hits<0) printf("Too many objects in select buffer\n");       // XXX make error message
1146         
1147         return hits;
1148 }
1149
1150 /* ********************** local view operator ******************** */
1151
1152 static unsigned int free_localbit(void)
1153 {
1154         unsigned int lay;
1155         ScrArea *sa;
1156         bScreen *sc;
1157         
1158         lay= 0;
1159         
1160         /* sometimes we loose a localview: when an area is closed */
1161         /* check all areas: which localviews are in use? */
1162         for(sc= G.main->screen.first; sc; sc= sc->id.next) {
1163                 for(sa= sc->areabase.first; sa; sa= sa->next) {
1164                         SpaceLink *sl= sa->spacedata.first;
1165                         for(; sl; sl= sl->next) {
1166                                 if(sl->spacetype==SPACE_VIEW3D) {
1167                                         View3D *v3d= (View3D*) sl;
1168                                         lay |= v3d->lay;
1169                                 }
1170                         }
1171                 }
1172         }
1173         
1174         if( (lay & 0x01000000)==0) return 0x01000000;
1175         if( (lay & 0x02000000)==0) return 0x02000000;
1176         if( (lay & 0x04000000)==0) return 0x04000000;
1177         if( (lay & 0x08000000)==0) return 0x08000000;
1178         if( (lay & 0x10000000)==0) return 0x10000000;
1179         if( (lay & 0x20000000)==0) return 0x20000000;
1180         if( (lay & 0x40000000)==0) return 0x40000000;
1181         if( (lay & 0x80000000)==0) return 0x80000000;
1182         
1183         return 0;
1184 }
1185
1186
1187 static void initlocalview(Scene *scene, ScrArea *sa)
1188 {
1189         View3D *v3d= sa->spacedata.first;
1190         Base *base;
1191         float size = 0.0, min[3], max[3], box[3];
1192         unsigned int locallay;
1193         int ok=0;
1194
1195         if(v3d->localvd) return;
1196
1197         INIT_MINMAX(min, max);
1198
1199         locallay= free_localbit();
1200
1201         if(locallay==0) {
1202                 printf("Sorry, no more than 8 localviews\n");   // XXX error 
1203                 ok= 0;
1204         }
1205         else {
1206                 if(scene->obedit) {
1207                         minmax_object(scene->obedit, min, max);
1208                         
1209                         ok= 1;
1210                 
1211                         BASACT->lay |= locallay;
1212                         scene->obedit->lay= BASACT->lay;
1213                 }
1214                 else {
1215                         for(base= FIRSTBASE; base; base= base->next) {
1216                                 if(TESTBASE(v3d, base))  {
1217                                         minmax_object(base->object, min, max);
1218                                         base->lay |= locallay;
1219                                         base->object->lay= base->lay;
1220                                         ok= 1;
1221                                 }
1222                         }
1223                 }
1224                 
1225                 box[0]= (max[0]-min[0]);
1226                 box[1]= (max[1]-min[1]);
1227                 box[2]= (max[2]-min[2]);
1228                 size= MAX3(box[0], box[1], box[2]);
1229                 if(size<=0.01) size= 0.01;
1230         }
1231         
1232         if(ok) {
1233                 ARegion *ar;
1234                 
1235                 v3d->localvd= MEM_mallocN(sizeof(View3D), "localview");
1236                 
1237                 memcpy(v3d->localvd, v3d, sizeof(View3D));
1238
1239                 for(ar= sa->regionbase.first; ar; ar= ar->next) {
1240                         if(ar->regiontype == RGN_TYPE_WINDOW) {
1241                                 RegionView3D *rv3d= ar->regiondata;
1242
1243                                 rv3d->localvd= MEM_mallocN(sizeof(RegionView3D), "localview region");
1244                                 memcpy(rv3d->localvd, rv3d, sizeof(RegionView3D));
1245                                 
1246                                 rv3d->ofs[0]= -(min[0]+max[0])/2.0;
1247                                 rv3d->ofs[1]= -(min[1]+max[1])/2.0;
1248                                 rv3d->ofs[2]= -(min[2]+max[2])/2.0;
1249
1250                                 rv3d->dist= size;
1251                                 /* perspective should be a bit farther away to look nice */
1252                                 if(rv3d->persp==V3D_ORTHO)
1253                                         rv3d->dist*= 0.7;
1254
1255                                 // correction for window aspect ratio
1256                                 if(ar->winy>2 && ar->winx>2) {
1257                                         float asp= (float)ar->winx/(float)ar->winy;
1258                                         if(asp<1.0) asp= 1.0/asp;
1259                                         rv3d->dist*= asp;
1260                                 }
1261                                 
1262                                 if (rv3d->persp==V3D_CAMOB) rv3d->persp= V3D_PERSP;
1263                                 
1264                                 v3d->cursor[0]= -rv3d->ofs[0];
1265                                 v3d->cursor[1]= -rv3d->ofs[1];
1266                                 v3d->cursor[2]= -rv3d->ofs[2];
1267                         }
1268                 }
1269                 if (v3d->near> 0.1) v3d->near= 0.1;
1270                 
1271                 v3d->lay= locallay;
1272         }
1273         else {
1274                 /* clear flags */ 
1275                 for(base= FIRSTBASE; base; base= base->next) {
1276                         if( base->lay & locallay ) {
1277                                 base->lay-= locallay;
1278                                 if(base->lay==0) base->lay= v3d->layact;
1279                                 if(base->object != scene->obedit) base->flag |= SELECT;
1280                                 base->object->lay= base->lay;
1281                         }
1282                 }               
1283                 v3d->localview= 0;
1284         }
1285
1286 }
1287
1288 static void restore_localviewdata(ScrArea *sa, int free)
1289 {
1290         ARegion *ar;
1291         View3D *v3d= sa->spacedata.first;
1292         
1293         if(v3d->localvd==NULL) return;
1294         
1295         v3d->near= v3d->localvd->near;
1296         v3d->far= v3d->localvd->far;
1297         v3d->lay= v3d->localvd->lay;
1298         v3d->layact= v3d->localvd->layact;
1299         v3d->drawtype= v3d->localvd->drawtype;
1300         v3d->camera= v3d->localvd->camera;
1301         
1302         if(free) {
1303                 MEM_freeN(v3d->localvd);
1304                 v3d->localvd= NULL;
1305                 v3d->localview= 0;
1306         }
1307         
1308         for(ar= sa->regionbase.first; ar; ar= ar->next) {
1309                 if(ar->regiontype == RGN_TYPE_WINDOW) {
1310                         RegionView3D *rv3d= ar->regiondata;
1311                         
1312                         if(rv3d->localvd) {
1313                                 rv3d->dist= rv3d->localvd->dist;
1314                                 VECCOPY(rv3d->ofs, rv3d->localvd->ofs);
1315                                 QUATCOPY(rv3d->viewquat, rv3d->localvd->viewquat);
1316                                 rv3d->view= rv3d->localvd->view;
1317                                 rv3d->persp= rv3d->localvd->persp;
1318                                 rv3d->camzoom= rv3d->localvd->camzoom;
1319
1320                                 if(free) {
1321                                         MEM_freeN(rv3d->localvd);
1322                                         rv3d->localvd= NULL;
1323                                 }
1324                         }
1325                 }
1326         }
1327 }
1328
1329 static void endlocalview(Scene *scene, ScrArea *sa)
1330 {
1331         View3D *v3d= sa->spacedata.first;
1332         struct Base *base;
1333         unsigned int locallay;
1334         
1335         if(v3d->localvd) {
1336                 
1337                 locallay= v3d->lay & 0xFF000000;
1338                 
1339                 restore_localviewdata(sa, 1); // 1 = free
1340
1341                 /* for when in other window the layers have changed */
1342                 if(v3d->scenelock) v3d->lay= scene->lay;
1343                 
1344                 for(base= FIRSTBASE; base; base= base->next) {
1345                         if( base->lay & locallay ) {
1346                                 base->lay-= locallay;
1347                                 if(base->lay==0) base->lay= v3d->layact;
1348                                 if(base->object != scene->obedit) {
1349                                         base->flag |= SELECT;
1350                                         base->object->flag |= SELECT;
1351                                 }
1352                                 base->object->lay= base->lay;
1353                         }
1354                 }
1355         } 
1356 }
1357
1358 static int localview_exec(bContext *C, wmOperator *unused)
1359 {
1360         View3D *v3d= CTX_wm_view3d(C);
1361         
1362         if(v3d->localvd)
1363                 endlocalview(CTX_data_scene(C), CTX_wm_area(C));
1364         else
1365                 initlocalview(CTX_data_scene(C), CTX_wm_area(C));
1366         
1367         ED_area_tag_redraw(CTX_wm_area(C));
1368         
1369         return OPERATOR_FINISHED;
1370 }
1371
1372 void VIEW3D_OT_localview(wmOperatorType *ot)
1373 {
1374         
1375         /* identifiers */
1376         ot->name= "Local View";
1377         ot->idname= "VIEW3D_OT_localview";
1378         
1379         /* api callbacks */
1380         ot->exec= localview_exec;
1381         
1382         ot->poll= ED_operator_view3d_active;
1383 }
1384
1385 /* ************************************** */
1386
1387 void view3d_align_axis_to_vector(View3D *v3d, RegionView3D *rv3d, int axisidx, float vec[3])
1388 {
1389         float alignaxis[3] = {0.0, 0.0, 0.0};
1390         float norm[3], axis[3], angle, new_quat[4];
1391         
1392         if(axisidx > 0) alignaxis[axisidx-1]= 1.0;
1393         else alignaxis[-axisidx-1]= -1.0;
1394         
1395         VECCOPY(norm, vec);
1396         Normalize(norm);
1397         
1398         angle= (float)acos(Inpf(alignaxis, norm));
1399         Crossf(axis, alignaxis, norm);
1400         VecRotToQuat(axis, -angle, new_quat);
1401         
1402         rv3d->view= 0;
1403         
1404         if (rv3d->persp==V3D_CAMOB && v3d->camera) {
1405                 /* switch out of camera view */
1406                 float orig_ofs[3];
1407                 float orig_dist= rv3d->dist;
1408                 float orig_lens= v3d->lens;
1409                 
1410                 VECCOPY(orig_ofs, rv3d->ofs);
1411                 rv3d->persp= V3D_PERSP;
1412                 rv3d->dist= 0.0;
1413                 view_settings_from_ob(v3d->camera, rv3d->ofs, NULL, NULL, &v3d->lens);
1414                 smooth_view(NULL, NULL, NULL, orig_ofs, new_quat, &orig_dist, &orig_lens); // XXX
1415         } else {
1416                 if (rv3d->persp==V3D_CAMOB) rv3d->persp= V3D_PERSP; /* switch out of camera mode */
1417                 smooth_view(NULL, NULL, NULL, NULL, new_quat, NULL, NULL); // XXX
1418         }
1419 }
1420