smooth view transformations, set the smoothview value to about 250 and the view anima...
[blender.git] / source / blender / src / view.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * Trackball math (in calctrackballvec())  Copyright (C) Silicon Graphics, Inc.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL/BL DUAL LICENSE BLOCK *****
33  */
34
35 #include <math.h>
36 #include <string.h>
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #ifdef WIN32
43 #include <io.h>
44 #else
45 #include <unistd.h>
46 #endif   
47
48 #include "MEM_guardedalloc.h"
49
50 #include "BLI_blenlib.h"
51 #include "BLI_arithb.h"
52
53 #include "DNA_action_types.h"
54 #include "DNA_armature_types.h"
55 #include "DNA_camera_types.h"
56 #include "DNA_lamp_types.h"
57 #include "DNA_object_types.h"
58 #include "DNA_screen_types.h"
59 #include "DNA_scene_types.h"
60 #include "DNA_space_types.h"
61 #include "DNA_userdef_types.h"
62 #include "DNA_view3d_types.h"
63
64 #include "BKE_action.h"
65 #include "BKE_anim.h"
66 #include "BKE_global.h"
67 #include "BKE_main.h"
68 #include "BKE_object.h"
69 #include "BKE_utildefines.h"
70
71 #include "BIF_gl.h"
72 #include "BIF_space.h"
73 #include "BIF_mywindow.h"
74 #include "BIF_previewrender.h"
75 #include "BIF_retopo.h"
76 #include "BIF_screen.h"
77 #include "BIF_toolbox.h"
78
79 #include "BSE_view.h"
80 #include "BSE_edit.h"           /* For countall */
81 #include "BSE_drawview.h"       /* For inner_play_anim_loop */
82
83 #include "BDR_drawobject.h"     /* For draw_object */
84 #include "BDR_editface.h"       /* For minmax_tface */
85 #include "BDR_sculptmode.h"
86
87 #include "mydevice.h"
88 #include "blendef.h"
89
90 #include "PIL_time.h" /* smoothview */
91
92 #define TRACKBALLSIZE  (1.1)
93 #define BL_NEAR_CLIP 0.001
94
95
96 /* local prototypes ----------*/
97 void setcameratoview3d(void); /* windows.c & toets.c */
98
99 void persp_general(int a)
100 {
101         /* for all window types, not 3D */
102         
103         if(a== 0) {
104                 glPushMatrix();
105                 glMatrixMode(GL_PROJECTION);
106                 glPushMatrix();
107                 glMatrixMode(GL_MODELVIEW);
108
109                 myortho2(-0.375, ((float)(curarea->winx))-0.375, -0.375, ((float)(curarea->winy))-0.375);
110                 glLoadIdentity();
111         }
112         else if(a== 1) {
113                 glMatrixMode(GL_PROJECTION);
114                 glPopMatrix();
115                 glMatrixMode(GL_MODELVIEW);
116                 glPopMatrix();
117         }
118 }
119
120 void persp(int a)
121 {
122         /* only 3D windows */
123
124         if(curarea->spacetype!=SPACE_VIEW3D) persp_general(a);
125         else if(a == PERSP_STORE) {             // only store
126                 glMatrixMode(GL_PROJECTION);
127                 mygetmatrix(G.vd->winmat1);     
128                 glMatrixMode(GL_MODELVIEW);
129                 mygetmatrix(G.vd->viewmat1); 
130         }
131         else if(a== PERSP_WIN) {                // only set
132                 myortho2(-0.375, (float)(curarea->winx)-0.375, -0.375, (float)(curarea->winy)-0.375);
133                 glLoadIdentity();
134         }
135         else if(a== PERSP_VIEW) {
136                 glMatrixMode(GL_PROJECTION);
137                 myloadmatrix(G.vd->winmat1); // put back
138                 Mat4CpyMat4(curarea->winmat, G.vd->winmat1); // to be sure? 
139                 glMatrixMode(GL_MODELVIEW); 
140                 myloadmatrix(G.vd->viewmat); // put back
141                 
142         }
143 }
144
145
146 void initgrabz(float x, float y, float z)
147 {
148         if(G.vd==NULL) return;
149         G.vd->zfac= G.vd->persmat[0][3]*x+ G.vd->persmat[1][3]*y+ G.vd->persmat[2][3]*z+ G.vd->persmat[3][3];
150
151         /* if x,y,z is exactly the viewport offset, zfac is 0 and we don't want that 
152          * (accounting for near zero values)
153          * */
154         if (G.vd->zfac < 1.e-6f && G.vd->zfac > -1.e-6f) G.vd->zfac = 1.0f;
155 }
156
157 void window_to_3d(float *vec, short mx, short my)
158 {
159         /* always call initgrabz */
160         float dx, dy;
161         
162         dx= 2.0f*mx*G.vd->zfac/curarea->winx;
163         dy= 2.0f*my*G.vd->zfac/curarea->winy;
164         
165         vec[0]= (G.vd->persinv[0][0]*dx + G.vd->persinv[1][0]*dy);
166         vec[1]= (G.vd->persinv[0][1]*dx + G.vd->persinv[1][1]*dy);
167         vec[2]= (G.vd->persinv[0][2]*dx + G.vd->persinv[1][2]*dy);
168 }
169
170 void project_short(float *vec, short *adr)      /* clips */
171 {
172         float fx, fy, vec4[4];
173
174         adr[0]= IS_CLIPPED;
175         
176         if(G.vd->flag & V3D_CLIPPING) {
177                 if(view3d_test_clipping(G.vd, vec))
178                         return;
179         }
180
181         VECCOPY(vec4, vec);
182         vec4[3]= 1.0;
183         Mat4MulVec4fl(G.vd->persmat, vec4);
184         
185         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
186                 fx= (curarea->winx/2)*(1 + vec4[0]/vec4[3]);
187                 
188                 if( fx>0 && fx<curarea->winx) {
189                 
190                         fy= (curarea->winy/2)*(1 + vec4[1]/vec4[3]);
191                         
192                         if(fy>0.0 && fy< (float)curarea->winy) {
193                                 adr[0]= floor(fx); 
194                                 adr[1]= floor(fy);
195                         }
196                 }
197         }
198 }
199
200 void project_int(float *vec, int *adr)
201 {
202         float fx, fy, vec4[4];
203
204         adr[0]= 2140000000.0f;
205         VECCOPY(vec4, vec);
206         vec4[3]= 1.0;
207         
208         Mat4MulVec4fl(G.vd->persmat, vec4);
209
210         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
211                 fx= (curarea->winx/2)*(1 + vec4[0]/vec4[3]);
212                 
213                 if( fx>-2140000000.0f && fx<2140000000.0f) {
214                         fy= (curarea->winy/2)*(1 + vec4[1]/vec4[3]);
215                         
216                         if(fy>-2140000000.0f && fy<2140000000.0f) {
217                                 adr[0]= floor(fx); 
218                                 adr[1]= floor(fy);
219                         }
220                 }
221         }
222 }
223
224 void project_short_noclip(float *vec, short *adr)
225 {
226         float fx, fy, vec4[4];
227
228         adr[0]= IS_CLIPPED;
229         VECCOPY(vec4, vec);
230         vec4[3]= 1.0;
231         
232         Mat4MulVec4fl(G.vd->persmat, vec4);
233
234         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
235                 fx= (curarea->winx/2)*(1 + vec4[0]/vec4[3]);
236                 
237                 if( fx>-32700 && fx<32700) {
238                 
239                         fy= (curarea->winy/2)*(1 + vec4[1]/vec4[3]);
240                         
241                         if(fy>-32700.0 && fy<32700.0) {
242                                 adr[0]= floor(fx); 
243                                 adr[1]= floor(fy);
244                         }
245                 }
246         }
247 }
248
249 void project_float(float *vec, float *adr)
250 {
251         float vec4[4];
252         
253         adr[0]= IS_CLIPPED;
254         VECCOPY(vec4, vec);
255         vec4[3]= 1.0;
256         
257         Mat4MulVec4fl(G.vd->persmat, vec4);
258
259         if( vec4[3]>BL_NEAR_CLIP ) {
260                 adr[0]= (curarea->winx/2.0)+(curarea->winx/2.0)*vec4[0]/vec4[3];        
261                 adr[1]= (curarea->winy/2.0)+(curarea->winy/2.0)*vec4[1]/vec4[3];
262         }
263 }
264
265 void view3d_get_object_project_mat(ScrArea *area, Object *ob, float pmat[4][4], float vmat[4][4])
266 {
267         if (area->spacetype!=SPACE_VIEW3D || !area->spacedata.first) {
268                 Mat4One(pmat);
269                 Mat4One(vmat);
270         } else {
271                 View3D *vd = area->spacedata.first;
272
273                 Mat4MulMat4(vmat, ob->obmat, vd->viewmat);
274                 Mat4MulMat4(pmat, vmat, vd->winmat1);
275                 Mat4CpyMat4(vmat, ob->obmat);
276         }
277 }
278
279 /* projectmat brings it to window coords, wmat to rotated world space */
280 void view3d_project_short_clip(ScrArea *area, float *vec, short *adr, float projmat[4][4], float wmat[4][4])
281 {
282         View3D *v3d= area->spacedata.first;
283         float fx, fy, vec4[4];
284
285         adr[0]= IS_CLIPPED;
286         
287         /* clipplanes in eye space */
288         if(v3d->flag & V3D_CLIPPING) {
289                 VECCOPY(vec4, vec);
290                 Mat4MulVecfl(wmat, vec4);
291                 if(view3d_test_clipping(v3d, vec4))
292                         return;
293         }
294         
295         VECCOPY(vec4, vec);
296         vec4[3]= 1.0;
297         
298         Mat4MulVec4fl(projmat, vec4);
299         
300         /* clipplanes in window space */
301         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
302                 fx= (area->winx/2)*(1 + vec4[0]/vec4[3]);
303                 
304                 if( fx>0 && fx<area->winx) {
305                 
306                         fy= (area->winy/2)*(1 + vec4[1]/vec4[3]);
307                         
308                         if(fy>0.0 && fy< (float)area->winy) {
309                                 adr[0]= floor(fx); 
310                                 adr[1]= floor(fy);
311                         }
312                 }
313         }
314 }
315
316 void view3d_project_short_noclip(ScrArea *area, float *vec, short *adr, float mat[4][4])
317 {
318         float fx, fy, vec4[4];
319
320         adr[0]= IS_CLIPPED;
321         
322         VECCOPY(vec4, vec);
323         vec4[3]= 1.0;
324         
325         Mat4MulVec4fl(mat, vec4);
326
327         if( vec4[3]>BL_NEAR_CLIP ) {    /* 0.001 is the NEAR clipping cutoff for picking */
328                 fx= (area->winx/2)*(1 + vec4[0]/vec4[3]);
329                 
330                 if( fx>-32700 && fx<32700) {
331                 
332                         fy= (area->winy/2)*(1 + vec4[1]/vec4[3]);
333                         
334                         if(fy>-32700.0 && fy<32700.0) {
335                                 adr[0]= floor(fx); 
336                                 adr[1]= floor(fy);
337                         }
338                 }
339         }
340 }
341
342 void view3d_project_float(ScrArea *area, float *vec, float *adr, float mat[4][4])
343 {
344         float vec4[4];
345         
346         adr[0]= IS_CLIPPED;
347         VECCOPY(vec4, vec);
348         vec4[3]= 1.0;
349         
350         Mat4MulVec4fl(mat, vec4);
351
352         if( vec4[3]>FLT_EPSILON ) {
353                 adr[0] = (area->winx/2.0)+(area->winx/2.0)*vec4[0]/vec4[3];     
354                 adr[1] = (area->winy/2.0)+(area->winy/2.0)*vec4[1]/vec4[3];
355         } else {
356                 adr[0] = adr[1] = 0.0;
357         }
358 }
359
360 int boundbox_clip(float obmat[][4], BoundBox *bb)
361 {
362         /* return 1: draw */
363         
364         float mat[4][4];
365         float vec[4], min, max;
366         int a, flag= -1, fl;
367         
368         if(bb==NULL) return 1;
369         if(bb->flag & OB_BB_DISABLED) return 1;
370         
371         Mat4MulMat4(mat, obmat, G.vd->persmat);
372
373         for(a=0; a<8; a++) {
374                 VECCOPY(vec, bb->vec[a]);
375                 vec[3]= 1.0;
376                 Mat4MulVec4fl(mat, vec);
377                 max= vec[3];
378                 min= -vec[3];
379
380                 fl= 0;
381                 if(vec[0] < min) fl+= 1;
382                 if(vec[0] > max) fl+= 2;
383                 if(vec[1] < min) fl+= 4;
384                 if(vec[1] > max) fl+= 8;
385                 if(vec[2] < min) fl+= 16;
386                 if(vec[2] > max) fl+= 32;
387                 
388                 flag &= fl;
389                 if(flag==0) return 1;
390         }
391
392         return 0;
393
394 }
395
396 void fdrawline(float x1, float y1, float x2, float y2)
397 {
398         float v[2];
399
400         glBegin(GL_LINE_STRIP);
401         v[0] = x1; v[1] = y1;
402         glVertex2fv(v);
403         v[0] = x2; v[1] = y2;
404         glVertex2fv(v);
405         glEnd();
406 }
407
408 void fdrawbox(float x1, float y1, float x2, float y2)
409 {
410         float v[2];
411
412         glBegin(GL_LINE_STRIP);
413
414         v[0] = x1; v[1] = y1;
415         glVertex2fv(v);
416         v[0] = x1; v[1] = y2;
417         glVertex2fv(v);
418         v[0] = x2; v[1] = y2;
419         glVertex2fv(v);
420         v[0] = x2; v[1] = y1;
421         glVertex2fv(v);
422         v[0] = x1; v[1] = y1;
423         glVertex2fv(v);
424
425         glEnd();
426 }
427
428 void sdrawline(short x1, short y1, short x2, short y2)
429 {
430         short v[2];
431
432         glBegin(GL_LINE_STRIP);
433         v[0] = x1; v[1] = y1;
434         glVertex2sv(v);
435         v[0] = x2; v[1] = y2;
436         glVertex2sv(v);
437         glEnd();
438 }
439
440 void sdrawbox(short x1, short y1, short x2, short y2)
441 {
442         short v[2];
443
444         glBegin(GL_LINE_STRIP);
445
446         v[0] = x1; v[1] = y1;
447         glVertex2sv(v);
448         v[0] = x1; v[1] = y2;
449         glVertex2sv(v);
450         v[0] = x2; v[1] = y2;
451         glVertex2sv(v);
452         v[0] = x2; v[1] = y1;
453         glVertex2sv(v);
454         v[0] = x1; v[1] = y1;
455         glVertex2sv(v);
456
457         glEnd();
458 }
459
460 /* the central math in this function was copied from trackball.cpp, sample code from the 
461    Developers Toolbox series by SGI. */
462
463 /* trackball: better one than a full spherical solution */
464
465 void calctrackballvecfirst(rcti *area, short *mval, float *vec)
466 {
467         float x, y, radius, d, z, t;
468         
469         radius= TRACKBALLSIZE;
470         
471         /* normalize x and y */
472         x= (area->xmax + area->xmin)/2 -mval[0];
473         x/= (float)((area->xmax - area->xmin)/2);
474         y= (area->ymax + area->ymin)/2 -mval[1];
475         y/= (float)((area->ymax - area->ymin)/2);
476         
477         d = sqrt(x*x + y*y);
478         if (d < radius*M_SQRT1_2)       /* Inside sphere */
479                 z = sqrt(radius*radius - d*d);
480         else
481         {                       /* On hyperbola */
482                 t = radius / M_SQRT2;
483                 z = t*t / d;
484         }
485
486         vec[0]= x;
487         vec[1]= y;
488         vec[2]= -z;             /* yah yah! */
489
490         if( fabs(vec[2])>fabs(vec[1]) && fabs(vec[2])>fabs(vec[0]) ) {
491                 vec[0]= 0.0;
492                 vec[1]= 0.0;
493                 if(vec[2]>0.0) vec[2]= 1.0; else vec[2]= -1.0;
494         }
495         else if( fabs(vec[1])>fabs(vec[0]) && fabs(vec[1])>fabs(vec[2]) ) {
496                 vec[0]= 0.0;
497                 vec[2]= 0.0;
498                 if(vec[1]>0.0) vec[1]= 1.0; else vec[1]= -1.0;
499         }
500         else  {
501                 vec[1]= 0.0;
502                 vec[2]= 0.0;
503                 if(vec[0]>0.0) vec[0]= 1.0; else vec[0]= -1.0;
504         }
505 }
506
507 void calctrackballvec(rcti *area, short *mval, float *vec)
508 {
509         float x, y, radius, d, z, t;
510         
511         radius= TRACKBALLSIZE;
512         
513         /* x en y normalizeren */
514         x= (area->xmax + area->xmin)/2 -mval[0];
515         x/= (float)((area->xmax - area->xmin)/4);
516         y= (area->ymax + area->ymin)/2 -mval[1];
517         y/= (float)((area->ymax - area->ymin)/2);
518         
519         d = sqrt(x*x + y*y);
520         if (d < radius*M_SQRT1_2)       /* Inside sphere */
521                 z = sqrt(radius*radius - d*d);
522         else
523         {                       /* On hyperbola */
524                 t = radius / M_SQRT2;
525                 z = t*t / d;
526         }
527
528         vec[0]= x;
529         vec[1]= y;
530         vec[2]= -z;             /* yah yah! */
531
532 }
533
534 void viewmove(int mode)
535 {
536         Object *ob = OBACT;
537         float firstvec[3], newvec[3], dvec[3];
538         float reverse, oldquat[4], q1[4], si, phi, dist0;
539         float ofs[3], obofs[3]= {0.0f, 0.0f, 0.0f};
540         int firsttime=1;
541         short mvalball[2], mval[2], mvalo[2];
542         short use_sel = 0;
543         short preview3d_event= 1;
544         
545         /* 3D window may not be defined */
546         if( !G.vd ) {
547                 fprintf( stderr, "G.vd == NULL in viewmove()\n" );
548                 return;
549         }
550
551         /* sometimes this routine is called from headerbuttons */
552
553         areawinset(curarea->win);
554         
555         initgrabz(-G.vd->ofs[0], -G.vd->ofs[1], -G.vd->ofs[2]);
556         
557         QUATCOPY(oldquat, G.vd->viewquat);
558         
559         getmouseco_sc(mvalo);           /* work with screen coordinates because of trackball function */
560         mvalball[0]= mvalo[0];                  /* needed for turntable to work */
561         mvalball[1]= mvalo[1];
562         dist0= G.vd->dist;
563         
564         calctrackballvec(&curarea->winrct, mvalo, firstvec);
565
566         /* cumultime(0); */
567
568         if(!G.obedit && (G.f & G_SCULPTMODE) && ob && G.vd->pivot_last) {
569                 use_sel= 1;
570                 VecCopyf(ofs, G.vd->ofs);
571
572                 VecCopyf(obofs,&sculpt_session()->pivot.x);
573                 Mat4MulVecfl(ob->obmat, obofs);
574                 obofs[0]= -obofs[0];
575                 obofs[1]= -obofs[1];
576                 obofs[2]= -obofs[2];
577         }
578         else if (ob && (U.uiflag & USER_ORBIT_SELECTION)) {
579                 use_sel = 1;
580                 
581                 VECCOPY(ofs, G.vd->ofs);
582                 
583                 /* If there's no selection, obofs is unmodified, so <0,0,0> */
584                 calculateTransformCenter(V3D_CENTROID, obofs);
585                 VecMulf(obofs, -1.0f);
586         }
587         else
588                 ofs[0] = ofs[1] = ofs[2] = 0.0f;
589
590         reverse= 1.0f;
591         if (G.vd->persmat[2][1] < 0.0f)
592                 reverse= -1.0f;
593
594         while(TRUE) {
595                 getmouseco_sc(mval);
596                 
597                 // if playanim = alt+A, screenhandlers are for animated UI, python, etc
598                 if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || (G.f & G_PLAYANIM) || do_screenhandlers(G.curscreen)) {
599                         
600                         if(firsttime) {
601                                 
602                                 firsttime= 0;
603                                 /* are we translating, rotating or zooming? */
604                                 if(mode==0) {
605                                         if(G.vd->view!=0) scrarea_queue_headredraw(curarea);    /*for button */
606                                         G.vd->view= 0;
607                                 }
608                                 if(G.vd->persp==2 && mode!=1 && G.vd->camera) {
609                                         G.vd->persp= 1;
610                                         scrarea_do_windraw(curarea);
611                                         scrarea_queue_headredraw(curarea);
612                                 }
613                         }
614
615                         if(mode==0) {   /* view rotate */
616                                 if (U.uiflag & USER_AUTOPERSP) G.vd->persp= 1;
617
618                                 if (U.flag & USER_TRACKBALL) mvalball[0]= mval[0];
619                                 mvalball[1]= mval[1];
620                                 
621                                 calctrackballvec(&curarea->winrct, mvalball, newvec);
622                                 
623                                 VecSubf(dvec, newvec, firstvec);
624                                 
625                                 si= sqrt(dvec[0]*dvec[0]+ dvec[1]*dvec[1]+ dvec[2]*dvec[2]);
626                                 si/= (2.0*TRACKBALLSIZE);
627                         
628                                 if (U.flag & USER_TRACKBALL) {
629                                         Crossf(q1+1, firstvec, newvec);
630         
631                                         Normalize(q1+1);
632         
633                                         /* Allow for rotation beyond the interval
634                                          * [-pi, pi] */
635                                         while (si > 1.0)
636                                                 si -= 2.0;
637                 
638                                         /* This relation is used instead of
639                                          * phi = asin(si) so that the angle
640                                          * of rotation is linearly proportional
641                                          * to the distance that the mouse is
642                                          * dragged. */
643                                         phi = si * M_PI / 2.0;
644                 
645                                         si= sin(phi);
646                                         q1[0]= cos(phi);
647                                         q1[1]*= si;
648                                         q1[2]*= si;
649                                         q1[3]*= si;     
650                                         QuatMul(G.vd->viewquat, q1, oldquat);
651
652                                         if (use_sel) {
653                                                 /* compute the post multiplication quat, to rotate the offset correctly */
654                                                 QUATCOPY(q1, oldquat);
655                                                 QuatConj(q1);
656                                                 QuatMul(q1, q1, G.vd->viewquat);
657
658                                                 QuatConj(q1); /* conj == inv for unit quat */
659                                                 VECCOPY(G.vd->ofs, ofs);
660                                                 VecSubf(G.vd->ofs, G.vd->ofs, obofs);
661                                                 QuatMulVecf(q1, G.vd->ofs);
662                                                 VecAddf(G.vd->ofs, G.vd->ofs, obofs);
663                                         }
664                                 } else {
665                                         /* New turntable view code by John Aughey */
666
667                                         float m[3][3];
668                                         float m_inv[3][3];
669                                         float xvec[3] = {1,0,0};
670                                         /* Sensitivity will control how fast the viewport rotates.  0.0035 was
671                                            obtained experimentally by looking at viewport rotation sensitivities
672                                            on other modeling programs. */
673                                         /* Perhaps this should be a configurable user parameter. */
674                                         const float sensitivity = 0.0035;
675
676                                         /* Get the 3x3 matrix and its inverse from the quaternion */
677                                         QuatToMat3(G.vd->viewquat, m);
678                                         Mat3Inv(m_inv,m);
679
680                                         /* Determine the direction of the x vector (for rotating up and down) */
681                                         /* This can likely be compuated directly from the quaternion. */
682                                         Mat3MulVecfl(m_inv,xvec);
683
684                                         /* Perform the up/down rotation */
685                                         phi = sensitivity * -(mval[1] - mvalo[1]);
686                                         si = sin(phi);
687                                         q1[0] = cos(phi);
688                                         q1[1] = si * xvec[0];
689                                         q1[2] = si * xvec[1];
690                                         q1[3] = si * xvec[2];
691                                         QuatMul(G.vd->viewquat, G.vd->viewquat, q1);
692
693                                         if (use_sel) {
694                                                 QuatConj(q1); /* conj == inv for unit quat */
695                                                 VecSubf(G.vd->ofs, G.vd->ofs, obofs);
696                                                 QuatMulVecf(q1, G.vd->ofs);
697                                                 VecAddf(G.vd->ofs, G.vd->ofs, obofs);
698                                         }
699
700                                         /* Perform the orbital rotation */
701                                         phi = sensitivity * reverse * (mval[0] - mvalo[0]);
702                                         q1[0] = cos(phi);
703                                         q1[1] = q1[2] = 0.0;
704                                         q1[3] = sin(phi);
705                                         QuatMul(G.vd->viewquat, G.vd->viewquat, q1);
706
707                                         if (use_sel) {
708                                                 QuatConj(q1);
709                                                 VecSubf(G.vd->ofs, G.vd->ofs, obofs);
710                                                 QuatMulVecf(q1, G.vd->ofs);
711                                                 VecAddf(G.vd->ofs, G.vd->ofs, obofs);
712                                         }
713                                 }
714                         }
715                         else if(mode==1) {      /* translate */
716                                 if(G.vd->persp==2) {
717                                         float max= (float)MAX2(curarea->winx, curarea->winy);
718
719                                         G.vd->camdx += (mvalo[0]-mval[0])/(max);
720                                         G.vd->camdy += (mvalo[1]-mval[1])/(max);
721                                         CLAMP(G.vd->camdx, -1.0f, 1.0f);
722                                         CLAMP(G.vd->camdy, -1.0f, 1.0f);
723                                         preview3d_event= 0;
724                                 }
725                                 else {
726                                         window_to_3d(dvec, mval[0]-mvalo[0], mval[1]-mvalo[1]);
727                                         VecAddf(G.vd->ofs, G.vd->ofs, dvec);
728                                 }
729                         }
730                         else if(mode==2) {
731                                 if(U.viewzoom==USER_ZOOM_CONT) {
732                                         // oldstyle zoom
733                                         G.vd->dist*= 1.0+(float)(mvalo[0]-mval[0]+mvalo[1]-mval[1])/1000.0;
734                                 }
735                                 else if(U.viewzoom==USER_ZOOM_SCALE) {
736                                         int ctr[2], len1, len2;
737                                         // method which zooms based on how far you move the mouse
738                                         
739                                         ctr[0] = (curarea->winrct.xmax + curarea->winrct.xmin)/2;
740                                         ctr[1] = (curarea->winrct.ymax + curarea->winrct.ymin)/2;
741                                         
742                                         len1 = (int)sqrt((ctr[0] - mval[0])*(ctr[0] - mval[0]) + (ctr[1] - mval[1])*(ctr[1] - mval[1])) + 5;
743                                         len2 = (int)sqrt((ctr[0] - mvalo[0])*(ctr[0] - mvalo[0]) + (ctr[1] - mvalo[1])*(ctr[1] - mvalo[1])) + 5;
744                                         
745                                         G.vd->dist= dist0 * ((float)len2/len1);
746                                 }
747                                 else {  /* USER_ZOOM_DOLLY */
748                                         float len1 = (curarea->winrct.ymax - mval[1]) + 5;
749                                         float len2 = (curarea->winrct.ymax - mvalo[1]) + 5;
750                                         
751                                         G.vd->dist= dist0 * (2.0*((len2/len1)-1.0) + 1.0);
752                                 }
753                                 
754                                 /* these limits are in toets.c too */
755                                 if(G.vd->dist<0.001*G.vd->grid) G.vd->dist= 0.001*G.vd->grid;
756                                 if(G.vd->dist>10.0*G.vd->far) G.vd->dist=10.0*G.vd->far;
757                                 
758                                 mval[1]= mvalo[1]; /* preserve first value */
759                                 mval[0]= mvalo[0];
760                                 
761                                 if(G.vd->persp==0 || G.vd->persp==2) preview3d_event= 0;
762                         }
763                         
764                         mvalo[0]= mval[0];
765                         mvalo[1]= mval[1];
766
767                         if(G.f & G_PLAYANIM) inner_play_anim_loop(0, 0);
768                         if(G.f & G_SIMULATION) break;
769
770                         /* If in retopo paint mode, update lines */
771                         if(retopo_mesh_paint_check() && G.vd->retopo_view_data) {
772                                 G.vd->retopo_view_data->queue_matrix_update= 1;
773                                 retopo_paint_view_update(G.vd);
774                         }
775
776                         scrarea_do_windraw(curarea);
777                         screen_swapbuffers();
778                 }
779                 else {
780                         short val;
781                         unsigned short event;
782                         /* we need to empty the queue... when you do this very long it overflows */
783                         while(qtest()) event= extern_qread(&val);
784                         
785                         BIF_wait_for_statechange();
786                 }
787                 
788                 /* this in the end, otherwise get_mbut does not work on a PC... */
789                 if( !(get_mbut() & (L_MOUSE|M_MOUSE))) break;
790         }
791
792         if(G.vd->depths) G.vd->depths->damaged= 1;
793         retopo_queue_updates(G.vd);
794         allqueue(REDRAWVIEW3D, 0);
795
796         if(preview3d_event) 
797                 BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
798         else
799                 BIF_view3d_previewrender_signal(curarea, PR_PROJECTED);
800
801 }
802
803 int get_view3d_viewplane(int winxi, int winyi, rctf *viewplane, float *clipsta, float *clipend)
804 {
805         Camera *cam=NULL;
806         float lens, fac, x1, y1, x2, y2;
807         float winx= (float)winxi, winy= (float)winyi;
808         int orth= 0;
809         
810         lens= G.vd->lens;       
811         
812         *clipsta= G.vd->near;
813         *clipend= G.vd->far;
814
815         if(G.vd->persp==2) {
816                 *clipsta= G.vd->near;
817                 *clipend= G.vd->far;
818                 if(G.vd->camera) {
819                         if(G.vd->camera->type==OB_LAMP ) {
820                                 Lamp *la;
821                                 
822                                 la= G.vd->camera->data;
823                                 fac= cos( M_PI*la->spotsize/360.0);
824                                 
825                                 x1= saacos(fac);
826                                 lens= 16.0*fac/sin(x1);
827                 
828                                 *clipsta= la->clipsta;
829                                 *clipend= la->clipend;
830                         }
831                         else if(G.vd->camera->type==OB_CAMERA) {
832                                 cam= G.vd->camera->data;
833                                 lens= cam->lens;
834                                 *clipsta= cam->clipsta;
835                                 *clipend= cam->clipend;
836                         }
837                 }
838         }
839         
840         if(G.vd->persp==0) {
841                 if(winx>winy) x1= -G.vd->dist;
842                 else x1= -winx*G.vd->dist/winy;
843                 x2= -x1;
844
845                 if(winx>winy) y1= -winy*G.vd->dist/winx;
846                 else y1= -G.vd->dist;
847                 y2= -y1;
848                 
849                 *clipend *= 0.5;        // otherwise too extreme low zbuffer quality
850                 *clipsta= - *clipend;
851                 orth= 1;
852         }
853         else {
854                 /* fac for zoom, also used for camdx */
855                 if(G.vd->persp==2) {
856                         fac= (1.41421+( (float)G.vd->camzoom )/50.0);
857                         fac*= fac;
858                 }
859                 else fac= 2.0;
860                 
861                 /* viewplane size depends... */
862                 if(cam && cam->type==CAM_ORTHO) {
863                         /* ortho_scale == 1 means exact 1 to 1 mapping */
864                         float dfac= 2.0*cam->ortho_scale/fac;
865                         
866                         if(winx>winy) x1= -dfac;
867                         else x1= -winx*dfac/winy;
868                         x2= -x1;
869                         
870                         if(winx>winy) y1= -winy*dfac/winx;
871                         else y1= -dfac;
872                         y2= -y1;
873                         orth= 1;
874                 }
875                 else {
876                         float dfac;
877                         
878                         if(winx>winy) dfac= 64.0/(fac*winx*lens);
879                         else dfac= 64.0/(fac*winy*lens);
880                         
881                         x1= - *clipsta * winx*dfac;
882                         x2= -x1;
883                         y1= - *clipsta * winy*dfac;
884                         y2= -y1;
885                         orth= 0;
886                 }
887                 /* cam view offset */
888                 if(cam) {
889                         float dx= 0.5*fac*G.vd->camdx*(x2-x1);
890                         float dy= 0.5*fac*G.vd->camdy*(y2-y1);
891                         x1+= dx;
892                         x2+= dx;
893                         y1+= dy;
894                         y2+= dy;
895                 }
896         }
897         
898         viewplane->xmin= x1;
899         viewplane->ymin= y1;
900         viewplane->xmax= x2;
901         viewplane->ymax= y2;
902         
903         return orth;
904 }
905
906 /* important to not set windows active in here, can be renderwin for example */
907 void setwinmatrixview3d(int winx, int winy, rctf *rect)         /* rect: for picking */
908 {
909         rctf viewplane;
910         float clipsta, clipend, x1, y1, x2, y2;
911         int orth;
912         
913         orth= get_view3d_viewplane(winx, winy, &viewplane, &clipsta, &clipend);
914 //      printf("%d %d %f %f %f %f %f %f\n", winx, winy, viewplane.xmin, viewplane.ymin, viewplane.xmax, viewplane.ymax, clipsta, clipend);
915         x1= viewplane.xmin;
916         y1= viewplane.ymin;
917         x2= viewplane.xmax;
918         y2= viewplane.ymax;
919         
920         if(rect) {              /* picking */
921                 rect->xmin/= (float)curarea->winx;
922                 rect->xmin= x1+rect->xmin*(x2-x1);
923                 rect->ymin/= (float)curarea->winy;
924                 rect->ymin= y1+rect->ymin*(y2-y1);
925                 rect->xmax/= (float)curarea->winx;
926                 rect->xmax= x1+rect->xmax*(x2-x1);
927                 rect->ymax/= (float)curarea->winy;
928                 rect->ymax= y1+rect->ymax*(y2-y1);
929                 
930                 if(orth) myortho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend);
931                 else mywindow(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend);
932                 
933         }
934         else {
935                 if(orth) myortho(x1, x2, y1, y2, clipsta, clipend);
936                 else mywindow(x1, x2, y1, y2, clipsta, clipend);
937         }
938
939         /* not sure what this was for? (ton) */
940         glMatrixMode(GL_PROJECTION);
941         mygetmatrix(curarea->winmat);
942         glMatrixMode(GL_MODELVIEW);
943 }
944
945 void obmat_to_viewmat(Object *ob)
946 {
947         float bmat[4][4];
948         float tmat[3][3];
949
950         Mat4CpyMat4(bmat, ob->obmat);
951         Mat4Ortho(bmat);
952         Mat4Invert(G.vd->viewmat, bmat);
953         
954         /* view quat calculation, needed for add object */
955         Mat3CpyMat4(tmat, G.vd->viewmat);
956         Mat3ToQuat(tmat, G.vd->viewquat);
957 }
958
959 /* dont set windows active in in here, is used by renderwin too */
960 void setviewmatrixview3d()
961 {
962         Camera *cam;
963
964         if(G.vd->persp>=2) {        /* obs/camera */
965                 if(G.vd->camera) {
966                         
967                         where_is_object(G.vd->camera);  
968                         obmat_to_viewmat(G.vd->camera);
969                         
970                         if(G.vd->camera->type==OB_CAMERA) {
971                                 cam= G.vd->camera->data;
972                                 //if(cam->type==CAM_ORTHO) G.vd->viewmat[3][2]*= 100.0;
973                         }
974                 }
975                 else {
976                         QuatToMat4(G.vd->viewquat, G.vd->viewmat);
977                         G.vd->viewmat[3][2]-= G.vd->dist;
978                 }
979         }
980         else {
981                 
982                 QuatToMat4(G.vd->viewquat, G.vd->viewmat);
983                 if(G.vd->persp==1) G.vd->viewmat[3][2]-= G.vd->dist;
984                 if(G.vd->ob_centre) {
985                         Object *ob= G.vd->ob_centre;
986                         float vec[3];
987                         
988                         VECCOPY(vec, ob->obmat[3]);
989                         if(ob->type==OB_ARMATURE && G.vd->ob_centre_bone[0]) {
990                                 bPoseChannel *pchan= get_pose_channel(ob->pose, G.vd->ob_centre_bone);
991                                 if(pchan) {
992                                         VECCOPY(vec, pchan->pose_mat[3]);
993                                         Mat4MulVecfl(ob->obmat, vec);
994                                 }
995                         }
996                         i_translate(-vec[0], -vec[1], -vec[2], G.vd->viewmat);
997                 }
998                 else i_translate(G.vd->ofs[0], G.vd->ofs[1], G.vd->ofs[2], G.vd->viewmat);
999         }
1000 }
1001
1002 void setcameratoview3d(void)
1003 {
1004         Object *ob;
1005         float dvec[3];
1006
1007         ob= G.vd->camera;
1008         dvec[0]= G.vd->dist*G.vd->viewinv[2][0];
1009         dvec[1]= G.vd->dist*G.vd->viewinv[2][1];
1010         dvec[2]= G.vd->dist*G.vd->viewinv[2][2];                                        
1011         VECCOPY(ob->loc, dvec);
1012         VecSubf(ob->loc, ob->loc, G.vd->ofs);
1013         G.vd->viewquat[0]= -G.vd->viewquat[0];
1014         if (ob->transflag & OB_QUAT) {
1015                 QUATCOPY(ob->quat, G.vd->viewquat);
1016         } else {
1017                 QuatToEul(G.vd->viewquat, ob->rot);
1018         }
1019         G.vd->viewquat[0]= -G.vd->viewquat[0];
1020 }
1021
1022 /* IGLuint-> GLuint*/
1023 short  view3d_opengl_select(unsigned int *buffer, unsigned int bufsize, short x1, short y1, short x2, short y2)
1024 {
1025         rctf rect;
1026         short mval[2], code, hits;
1027
1028         G.f |= G_PICKSEL;
1029         
1030         if(x1==0 && x2==0 && y1==0 && y2==0) {
1031                 getmouseco_areawin(mval);
1032                 rect.xmin= mval[0]-12;  // seems to be default value for bones only now
1033                 rect.xmax= mval[0]+12;
1034                 rect.ymin= mval[1]-12;
1035                 rect.ymax= mval[1]+12;
1036         }
1037         else {
1038                 rect.xmin= x1;
1039                 rect.xmax= x2;
1040                 rect.ymin= y1;
1041                 rect.ymax= y2;
1042         }
1043         /* get rid of overlay button matrix */
1044         persp(PERSP_VIEW);
1045         setwinmatrixview3d(curarea->winx, curarea->winy, &rect);
1046         Mat4MulMat4(G.vd->persmat, G.vd->viewmat, curarea->winmat);
1047         
1048         if(G.vd->drawtype > OB_WIRE) {
1049                 G.vd->zbuf= TRUE;
1050                 glEnable(GL_DEPTH_TEST);
1051         }
1052
1053         if(G.vd->flag & V3D_CLIPPING)
1054                 view3d_set_clipping(G.vd);
1055         
1056         glSelectBuffer( bufsize, (GLuint *)buffer);
1057         glRenderMode(GL_SELECT);
1058         glInitNames();  /* these two calls whatfor? It doesnt work otherwise */
1059         glPushName(-1);
1060         code= 1;
1061         
1062         if(G.obedit && G.obedit->type==OB_MBALL) {
1063                 draw_object(BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1064         }
1065         else if ((G.obedit && G.obedit->type==OB_ARMATURE)) {
1066                 draw_object(BASACT, DRAW_PICKING|DRAW_CONSTCOLOR);
1067         }
1068         else {
1069                 Base *base;
1070                 
1071                 G.vd->xray= TRUE;       // otherwise it postpones drawing
1072                 for(base= G.scene->base.first; base; base= base->next) {
1073                         if(base->lay & G.vd->lay) {
1074                                 
1075                                 if (base->object->restrictflag & OB_RESTRICT_SELECT)
1076                                         base->selcol= 0;
1077                                 else {
1078                                         base->selcol= code;
1079                                         glLoadName(code);
1080                                         draw_object(base, DRAW_PICKING|DRAW_CONSTCOLOR);
1081                                         
1082                                         /* we draw group-duplicators for selection too */
1083                                         if((base->object->transflag & OB_DUPLI) && base->object->dup_group) {
1084                                                 ListBase *lb;
1085                                                 DupliObject *dob;
1086                                                 Base tbase;
1087                                                 
1088                                                 tbase.flag= OB_FROMDUPLI;
1089                                                 lb= object_duplilist(G.scene, base->object);
1090                                                 
1091                                                 for(dob= lb->first; dob; dob= dob->next) {
1092                                                         tbase.object= dob->ob;
1093                                                         Mat4CpyMat4(dob->ob->obmat, dob->mat);
1094                                                         
1095                                                         draw_object(&tbase, DRAW_PICKING|DRAW_CONSTCOLOR);
1096                                                         
1097                                                         Mat4CpyMat4(dob->ob->obmat, dob->omat);
1098                                                 }
1099                                                 free_object_duplilist(lb);
1100                                         }
1101                                         code++;
1102                                 }                               
1103                         }
1104                 }
1105                 G.vd->xray= FALSE;      // restore
1106         }
1107         
1108         glPopName();    /* see above (pushname) */
1109         hits= glRenderMode(GL_RENDER);
1110         if(hits<0) error("Too many objects in select buffer");
1111
1112         G.f &= ~G_PICKSEL;
1113         setwinmatrixview3d(curarea->winx, curarea->winy, NULL);
1114         Mat4MulMat4(G.vd->persmat, G.vd->viewmat, curarea->winmat);
1115         
1116         if(G.vd->drawtype > OB_WIRE) {
1117                 G.vd->zbuf= 0;
1118                 glDisable(GL_DEPTH_TEST);
1119         }
1120         persp(PERSP_WIN);
1121         
1122         if(G.vd->flag & V3D_CLIPPING)
1123                 view3d_clr_clipping();
1124
1125         return hits;
1126 }
1127
1128 float *give_cursor()
1129 {
1130         if(G.vd && G.vd->localview) return G.vd->cursor;
1131         else return G.scene->cursor;
1132 }
1133
1134 unsigned int free_localbit()
1135 {
1136         unsigned int lay;
1137         ScrArea *sa;
1138         bScreen *sc;
1139         
1140         lay= 0;
1141         
1142         /* sometimes we loose a localview: when an area is closed */
1143         /* check all areas: which localviews are in use? */
1144         sc= G.main->screen.first;
1145         while(sc) {
1146                 sa= sc->areabase.first;
1147                 while(sa) {
1148                         SpaceLink *sl= sa->spacedata.first;
1149                         while(sl) {
1150                                 if(sl->spacetype==SPACE_VIEW3D) {
1151                                         View3D *v3d= (View3D*) sl;
1152                                         lay |= v3d->lay;
1153                                 }
1154                                 sl= sl->next;
1155                         }
1156                         sa= sa->next;
1157                 }
1158                 sc= sc->id.next;
1159         }
1160         
1161         if( (lay & 0x01000000)==0) return 0x01000000;
1162         if( (lay & 0x02000000)==0) return 0x02000000;
1163         if( (lay & 0x04000000)==0) return 0x04000000;
1164         if( (lay & 0x08000000)==0) return 0x08000000;
1165         if( (lay & 0x10000000)==0) return 0x10000000;
1166         if( (lay & 0x20000000)==0) return 0x20000000;
1167         if( (lay & 0x40000000)==0) return 0x40000000;
1168         if( (lay & 0x80000000)==0) return 0x80000000;
1169         
1170         return 0;
1171 }
1172
1173
1174 void initlocalview()
1175 {
1176         Base *base;
1177         float size = 0.0, min[3], max[3], afm[3];
1178         unsigned int locallay;
1179         int ok=0;
1180
1181         if(G.vd->localvd) return;
1182
1183         min[0]= min[1]= min[2]= 1.0e10;
1184         max[0]= max[1]= max[2]= -1.0e10;
1185
1186         locallay= free_localbit();
1187
1188         if(locallay==0) {
1189                 error("Sorry,  no more than 8 localviews");
1190                 ok= 0;
1191         }
1192         else {
1193                 if(G.obedit) {
1194                         minmax_object(G.obedit, min, max);
1195                         
1196                         ok= 1;
1197                 
1198                         BASACT->lay |= locallay;
1199                         G.obedit->lay= BASACT->lay;
1200                 }
1201                 else {
1202                         base= FIRSTBASE;
1203                         while(base) {
1204                                 if TESTBASE(base)  {
1205                                         minmax_object(base->object, min, max);
1206                                         base->lay |= locallay;
1207                                         base->object->lay= base->lay;
1208                                         ok= 1;
1209                                 }
1210                                 base= base->next;
1211                         }
1212                 }
1213                 
1214                 afm[0]= (max[0]-min[0]);
1215                 afm[1]= (max[1]-min[1]);
1216                 afm[2]= (max[2]-min[2]);
1217                 size= 0.7*MAX3(afm[0], afm[1], afm[2]);
1218                 if(size<=0.01) size= 0.01;
1219         }
1220         
1221         if(ok) {
1222                 G.vd->localvd= MEM_mallocN(sizeof(View3D), "localview");
1223                 memcpy(G.vd->localvd, G.vd, sizeof(View3D));
1224
1225                 G.vd->ofs[0]= -(min[0]+max[0])/2.0;
1226                 G.vd->ofs[1]= -(min[1]+max[1])/2.0;
1227                 G.vd->ofs[2]= -(min[2]+max[2])/2.0;
1228
1229                 G.vd->dist= size;
1230
1231                 // correction for window aspect ratio
1232                 if(curarea->winy>2 && curarea->winx>2) {
1233                         size= (float)curarea->winx/(float)curarea->winy;
1234                         if(size<1.0) size= 1.0/size;
1235                         G.vd->dist*= size;
1236                 }
1237                 
1238                 if(G.vd->persp>1) {
1239                         G.vd->persp= 1;
1240                         
1241                 }
1242                 G.vd->near= 0.1;
1243                 G.vd->cursor[0]= -G.vd->ofs[0];
1244                 G.vd->cursor[1]= -G.vd->ofs[1];
1245                 G.vd->cursor[2]= -G.vd->ofs[2];
1246
1247                 G.vd->lay= locallay;
1248                 
1249                 countall();
1250                 scrarea_queue_winredraw(curarea);
1251         }
1252         else {
1253                 /* clear flags */ 
1254                 base= FIRSTBASE;
1255                 while(base) {
1256                         if( base->lay & locallay ) {
1257                                 base->lay-= locallay;
1258                                 if(base->lay==0) base->lay= G.vd->layact;
1259                                 if(base->object != G.obedit) base->flag |= SELECT;
1260                                 base->object->lay= base->lay;
1261                         }
1262                         base= base->next;
1263                 }
1264                 scrarea_queue_headredraw(curarea);
1265                 
1266                 G.vd->localview= 0;
1267         }
1268         BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1269 }
1270
1271 void centerview()       /* like a localview without local! */
1272 {
1273         Object *ob= OBACT;
1274         float size, min[3], max[3], afm[3];
1275         int ok=0;
1276         
1277         /* SMOOTHVIEW */
1278         float new_ofs[3];
1279         float new_dist;
1280         
1281         
1282         min[0]= min[1]= min[2]= 1.0e10;
1283         max[0]= max[1]= max[2]= -1.0e10;
1284
1285         if (G.f & G_WEIGHTPAINT) {
1286                 /* hardcoded exception, we look for the one selected armature */
1287                 /* this is weak code this way, we should make a generic active/selection callback interface once... */
1288                 Base *base;
1289                 for(base=FIRSTBASE; base; base= base->next) {
1290                         if(TESTBASELIB(base)) {
1291                                 if(base->object->type==OB_ARMATURE)
1292                                         if(base->object->flag & OB_POSEMODE)
1293                                                 break;
1294                         }
1295                 }
1296                 if(base)
1297                         ob= base->object;
1298         }
1299         
1300         
1301         if(G.obedit) {
1302                 minmax_verts(min, max); // ony selected
1303                 ok= 1;
1304         }
1305         else if(ob && (ob->flag & OB_POSEMODE)) {
1306                 if(ob->pose) {
1307                         bArmature *arm= ob->data;
1308                         bPoseChannel *pchan;
1309                         float vec[3];
1310                         
1311                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
1312                                 if(pchan->bone->flag & BONE_SELECTED) {
1313                                         if(pchan->bone->layer & arm->layer) {
1314                                                 ok= 1;
1315                                                 VECCOPY(vec, pchan->pose_head);
1316                                                 Mat4MulVecfl(ob->obmat, vec);
1317                                                 DO_MINMAX(vec, min, max);
1318                                                 VECCOPY(vec, pchan->pose_tail);
1319                                                 Mat4MulVecfl(ob->obmat, vec);
1320                                                 DO_MINMAX(vec, min, max);
1321                                         }
1322                                 }
1323                         }
1324                 }
1325         }
1326         else if (G.f & G_FACESELECT) {
1327                 minmax_tface(min, max);
1328                 ok= 1;
1329         }
1330         else {
1331                 Base *base= FIRSTBASE;
1332                 while(base) {
1333                         if TESTBASE(base)  {
1334                                 minmax_object(base->object, min, max);
1335                                 ok= 1;
1336                         }
1337                         base= base->next;
1338                 }
1339         }
1340         
1341         if(ok==0) return;
1342         
1343         afm[0]= (max[0]-min[0]);
1344         afm[1]= (max[1]-min[1]);
1345         afm[2]= (max[2]-min[2]);
1346         size= 0.7*MAX3(afm[0], afm[1], afm[2]);
1347         
1348         if(size<=0.01) size= 0.01;
1349         
1350         new_ofs[0]= -(min[0]+max[0])/2.0;
1351         new_ofs[1]= -(min[1]+max[1])/2.0;
1352         new_ofs[2]= -(min[2]+max[2])/2.0;
1353         
1354         new_dist = size;
1355
1356         // correction for window aspect ratio
1357         if(curarea->winy>2 && curarea->winx>2) {
1358                 size= (float)curarea->winx/(float)curarea->winy;
1359                 if(size<1.0) size= 1.0/size;
1360                 new_dist*= size;
1361         }
1362         
1363         if(G.vd->persp>1) {
1364                 G.vd->persp= 1;
1365         }
1366         
1367         G.vd->cursor[0]= -new_ofs[0];
1368         G.vd->cursor[1]= -new_ofs[1];
1369         G.vd->cursor[2]= -new_ofs[2];
1370         
1371         smooth_view(G.vd, new_ofs, NULL, &new_dist);
1372         
1373         scrarea_queue_winredraw(curarea);
1374         BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1375
1376 }
1377
1378
1379 void restore_localviewdata(View3D *vd)
1380 {
1381         if(vd->localvd==0) return;
1382         
1383         VECCOPY(vd->ofs, vd->localvd->ofs);
1384         vd->dist= vd->localvd->dist;
1385         vd->persp= vd->localvd->persp;
1386         vd->view= vd->localvd->view;
1387         vd->near= vd->localvd->near;
1388         vd->far= vd->localvd->far;
1389         vd->lay= vd->localvd->lay;
1390         vd->layact= vd->localvd->layact;
1391         vd->drawtype= vd->localvd->drawtype;
1392         vd->camera= vd->localvd->camera;
1393         QUATCOPY(vd->viewquat, vd->localvd->viewquat);
1394         
1395 }
1396
1397 void endlocalview(ScrArea *sa)
1398 {
1399         View3D *v3d;
1400         struct Base *base;
1401         unsigned int locallay;
1402         
1403         if(sa->spacetype!=SPACE_VIEW3D) return;
1404         v3d= sa->spacedata.first;
1405         
1406         if(v3d->localvd) {
1407                 
1408                 locallay= v3d->lay & 0xFF000000;
1409                 
1410                 restore_localviewdata(v3d);
1411                 
1412                 MEM_freeN(v3d->localvd);
1413                 v3d->localvd= 0;
1414                 v3d->localview= 0;
1415
1416                 /* for when in other window the layers have changed */
1417                 if(v3d->scenelock) v3d->lay= G.scene->lay;
1418                 
1419                 base= FIRSTBASE;
1420                 while(base) {
1421                         if( base->lay & locallay ) {
1422                                 base->lay-= locallay;
1423                                 if(base->lay==0) base->lay= v3d->layact;
1424                                 if(base->object != G.obedit) {
1425                                         base->flag |= SELECT;
1426                                         base->object->flag |= SELECT;
1427                                 }
1428                                 base->object->lay= base->lay;
1429                         }
1430                         base= base->next;
1431                 }
1432
1433                 countall();
1434                 allqueue(REDRAWVIEW3D, 0);      /* because of select */
1435                 allqueue(REDRAWOOPS, 0);        /* because of select */
1436                 BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1437         } 
1438 }
1439
1440 void view3d_home(int center)
1441 {
1442         Base *base;
1443         float size, min[3], max[3], afm[3];
1444         int ok= 1, onedone=0;
1445
1446         if(center) {
1447                 min[0]= min[1]= min[2]= 0.0;
1448                 max[0]= max[1]= max[2]= 0.0;
1449         }
1450         else {
1451                 min[0]= min[1]= min[2]= 1.0e10;
1452                 max[0]= max[1]= max[2]= -1.0e10;
1453         }
1454         
1455         for(base= FIRSTBASE; base; base= base->next) {
1456                 if(base->lay & G.vd->lay) {
1457                         onedone= 1;
1458                         minmax_object(base->object, min, max);
1459                 }
1460         }
1461         if(!onedone) return;
1462         
1463         afm[0]= (max[0]-min[0]);
1464         afm[1]= (max[1]-min[1]);
1465         afm[2]= (max[2]-min[2]);
1466         size= 0.7*MAX3(afm[0], afm[1], afm[2]);
1467         if(size==0.0) ok= 0;
1468                 
1469         if(ok) {
1470                 float new_dist;
1471                 float new_ofs[3];
1472                 
1473                 new_dist = size;
1474                 new_ofs[0]= -(min[0]+max[0])/2.0;
1475                 new_ofs[1]= -(min[1]+max[1])/2.0;
1476                 new_ofs[2]= -(min[2]+max[2])/2.0;
1477                 
1478                 // correction for window aspect ratio
1479                 if(curarea->winy>2 && curarea->winx>2) {
1480                         size= (float)curarea->winx/(float)curarea->winy;
1481                         if(size<1.0) size= 1.0/size;
1482                         new_dist*= size;
1483                 }
1484                 
1485                 if(G.vd->persp==2) G.vd->persp= 1;
1486                  
1487                 smooth_view(G.vd, new_ofs, NULL, &new_dist);
1488                 
1489                 scrarea_queue_winredraw(curarea);
1490         }
1491         BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
1492
1493 }
1494
1495
1496 void view3d_align_axis_to_vector(View3D *v3d, int axisidx, float vec[3])
1497 {
1498         float alignaxis[3];
1499         float norm[3], axis[3], angle;
1500
1501         alignaxis[0]= alignaxis[1]= alignaxis[2]= 0.0;
1502         alignaxis[axisidx]= 1.0;
1503
1504         norm[0]= vec[0], norm[1]= vec[1], norm[2]= vec[2];
1505         Normalize(norm);
1506
1507         angle= acos(Inpf(alignaxis, norm));
1508         Crossf(axis, alignaxis, norm);
1509         VecRotToQuat(axis, -angle, v3d->viewquat);
1510
1511         v3d->view= 0;
1512         if (v3d->persp>=2) v3d->persp= 0; /* switch out of camera mode */
1513 }
1514
1515
1516
1517 /* SMOOTHVIEW */
1518 void smooth_view(View3D *v3d, float *ofs, float *quat, float *dist)
1519 {
1520         /* View Animation enabled */
1521         if (U.smooth_viewtx) {
1522                 int i;
1523                 char changed = 0;
1524                 float step = 0.0, step_inv;
1525                 float orig_dist;
1526                 float orig_quat[4];
1527                 float orig_ofs[3];
1528                 
1529                 double time_allowed, time_current, time_start;
1530                 
1531                 /* if there is no difference, return */
1532                 changed = 0; /* zero means no difference */
1533                 if (dist) {
1534                         if ((*dist) != v3d->dist)
1535                                 changed = 1;
1536                 }
1537                 
1538                 if (!changed && ofs) {
1539                         if ((ofs[0]!=v3d->ofs[0]) ||
1540                                 (ofs[1]!=v3d->ofs[1]) ||
1541                                 (ofs[2]!=v3d->ofs[2]) )
1542                                 changed = 1;
1543                 }
1544                 
1545                 if (!changed && quat ) {
1546                         if ((quat[0]!=v3d->viewquat[0]) ||
1547                                 (quat[1]!=v3d->viewquat[1]) ||
1548                                 (quat[2]!=v3d->viewquat[2]) ||
1549                                 (quat[3]!=v3d->viewquat[3]) )
1550                                 changed = 1;
1551                 }
1552                 
1553                 /* The new view is different from teh old one
1554                  * so animate the view */
1555                 if (changed) {
1556                         
1557                         /* store original values */
1558                         VECCOPY(orig_ofs,       v3d->ofs);
1559                         QUATCOPY(orig_quat,     v3d->viewquat);
1560                         orig_dist =                     v3d->dist;
1561                         
1562                         time_allowed= (float)U.smooth_viewtx / 1000.0;
1563                         time_current = time_start = PIL_check_seconds_timer();
1564                         
1565                         /* if this is view rotation only
1566                          * we can decrease the time allowed by
1567                          * the angle between quats 
1568                          * this means small rotations wont lag */
1569                          if (quat && !ofs && !dist) {
1570                                 float vec1[3], vec2[3];
1571                                 VECCOPY(vec1, quat);
1572                                 VECCOPY(vec2, v3d->viewquat);
1573                                 Normalize(vec1);
1574                                 Normalize(vec2);
1575                                 /* scale the time allowed by the rotation */
1576                                 time_allowed *= NormalizedVecAngle2(vec1, vec2)/(M_PI/2); 
1577                          }
1578                         
1579                         while (time_start + time_allowed > time_current) {
1580                                 
1581                                 step =  (float)((time_current-time_start) / time_allowed);
1582                                 
1583                                 /* ease in/out */
1584                                 if (step < 0.5) step = pow(step*2, 2)/2;
1585                                 else                    step = 1-(pow(2*(1-step) ,2)/2);
1586                                 
1587                                 step_inv = 1-step;
1588                                 
1589                                 if (ofs)
1590                                         for (i=0; i<3; i++)
1591                                                 v3d->ofs[i] = ofs[i]*step + orig_ofs[i]*step_inv;
1592                                 
1593                                 if (quat)
1594                                         QuatInterpol(v3d->viewquat, orig_quat, quat, step);
1595                                         
1596                                 if (dist) {
1597                                         v3d->dist = ((*dist)*step) + (orig_dist*step_inv);
1598                                 }
1599                                 
1600                                 /*redraw the view*/
1601                                 scrarea_do_windraw(curarea);
1602                                 screen_swapbuffers();
1603                                 
1604                                 time_current= PIL_check_seconds_timer();
1605                         }
1606                 }
1607         }
1608         
1609         /* set these values even if animation is enabled because flaot
1610          * error will make then not quite accurate */
1611         if (ofs)
1612                 VECCOPY(v3d->ofs, ofs);
1613         if (quat)
1614                 QUATCOPY(v3d->viewquat, quat);
1615         if (dist)
1616                 v3d->dist = *dist;
1617         
1618 }
1619