3
* Copyright (C) 2003 Fabien Chereau
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
#include "projector.h"
23
#include "fisheye_projector.h"
24
#include "cylinder_projector.h"
25
#include "stereographic_projector.h"
26
#include "spheric_mirror_projector.h"
29
const char *Projector::typeToString(PROJECTOR_TYPE type) {
31
case PERSPECTIVE_PROJECTOR: return "perspective";
32
case FISHEYE_PROJECTOR: return "fisheye";
33
case CYLINDER_PROJECTOR: return "cylinder";
34
case STEREOGRAPHIC_PROJECTOR: return "stereographic";
35
case SPHERIC_MIRROR_PROJECTOR: return "spheric_mirror";
37
cerr << "fatal: Projector::typeToString(" << type << ") failed" << endl;
39
// just shutup the compiler, this point will never be reached
43
Projector::PROJECTOR_TYPE Projector::stringToType(const string &s) {
44
if (s=="perspective") return PERSPECTIVE_PROJECTOR;
45
if (s=="fisheye") return FISHEYE_PROJECTOR;
46
if (s=="cylinder") return CYLINDER_PROJECTOR;
47
if (s=="stereographic") return STEREOGRAPHIC_PROJECTOR;
48
if (s=="spheric_mirror") return SPHERIC_MIRROR_PROJECTOR;
49
cerr << "fatal: Projector::stringToType(" << s << ") failed" << endl;
51
// just shutup the compiler, this point will never be reached
52
return PERSPECTIVE_PROJECTOR;
55
const char *Projector::maskTypeToString(PROJECTOR_MASK_TYPE type) {
56
if(type == DISK ) return "disk";
60
Projector::PROJECTOR_MASK_TYPE Projector::stringToMaskType(const string &s) {
61
if (s=="disk") return DISK;
66
Projector *Projector::create(PROJECTOR_TYPE type,
67
const Vec4i& viewport,
71
case PERSPECTIVE_PROJECTOR:
72
rval = new Projector(viewport,_fov);
74
case FISHEYE_PROJECTOR:
75
rval = new FisheyeProjector(viewport,_fov);
77
case CYLINDER_PROJECTOR:
78
rval = new CylinderProjector(viewport,_fov);
80
case STEREOGRAPHIC_PROJECTOR:
81
rval = new StereographicProjector(viewport,_fov);
83
case SPHERIC_MIRROR_PROJECTOR:
84
rval = new SphericMirrorProjector(viewport,_fov);
88
cerr << "fatal: Projector::create(" << type << ") failed" << endl;
91
// just shutup the compiler, this point will never be reached
95
Projector::Projector(const Vec4i& viewport, double _fov)
96
:maskType(NONE), fov(1.0), min_fov(0.0001), max_fov(100),
97
zNear(0.1), zFar(10000),
98
vec_viewport(viewport),
99
flag_auto_zoom(0), gravityLabels(0)
103
setViewport(viewport);
107
Projector::~Projector()
111
// Init the viewing matrix, setting the field of view, the clipping planes, and screen ratio
112
// The function is a reimplementation of gluPerspective
113
void Projector::init_project_matrix(void)
115
double f = 1./tan(fov*M_PI/360.);
116
double ratio = (double)getViewportHeight()/getViewportWidth();
117
mat_projection.set( flip_horz*f*ratio, 0., 0., 0.,
118
0., flip_vert*f, 0., 0.,
119
0., 0., (zFar + zNear)/(zNear - zFar), -1.,
120
0., 0., (2.*zFar*zNear)/(zNear - zFar), 0.);
121
glMatrixMode(GL_PROJECTION);
122
glLoadMatrixd(mat_projection);
123
glMatrixMode(GL_MODELVIEW);
126
void Projector::setViewport(int x, int y, int w, int h)
132
center.set(vec_viewport[0]+vec_viewport[2]/2,vec_viewport[1]+vec_viewport[3]/2,0);
133
view_scaling_factor = 1.0/fov*180./M_PI*MY_MIN(getViewportWidth(),getViewportHeight());
134
glViewport(x, y, w, h);
135
init_project_matrix();
138
void Projector::set_fov(double f)
141
if (f>max_fov) fov = max_fov;
142
if (f<min_fov) fov = min_fov;
143
view_scaling_factor = 1.0/fov*180./M_PI*MY_MIN(getViewportWidth(),getViewportHeight());
144
init_project_matrix();
147
void Projector::setMaxFov(double max) {
148
if (fov > max) set_fov(max);
152
// Fill with black around the circle
153
void Projector::draw_viewport_shape(void)
155
if (maskType != DISK) return;
158
glColor3f(0.f,0.f,0.f);
159
set_orthographic_projection();
160
glTranslatef(getViewportPosX()+getViewportWidth()/2,getViewportPosY()+getViewportHeight()/2,0.f);
161
GLUquadricObj * p = gluNewQuadric();
162
gluDisk(p, MY_MIN(getViewportWidth(),getViewportHeight())/2, getViewportWidth()+getViewportHeight(), 256, 1); // should always cover whole screen
164
reset_perspective_projection();
167
void Projector::set_clipping_planes(double znear, double zfar)
171
init_project_matrix();
174
void Projector::change_fov(double deltaFov)
176
// if we are zooming in or out
177
if (deltaFov) set_fov(fov+deltaFov);
181
// Set the standard modelview matrices used for projection
182
void Projector::set_modelview_matrices( const Mat4d& _mat_earth_equ_to_eye,
183
const Mat4d& _mat_helio_to_eye,
184
const Mat4d& _mat_local_to_eye,
185
const Mat4d& _mat_j2000_to_eye)
187
mat_earth_equ_to_eye = _mat_earth_equ_to_eye;
188
mat_j2000_to_eye = _mat_j2000_to_eye;
189
mat_helio_to_eye = _mat_helio_to_eye;
190
mat_local_to_eye = _mat_local_to_eye;
192
inv_mat_earth_equ_to_eye = (mat_projection*mat_earth_equ_to_eye).inverse();
193
inv_mat_j2000_to_eye = (mat_projection*mat_j2000_to_eye).inverse();
194
inv_mat_helio_to_eye = (mat_projection*mat_helio_to_eye).inverse();
195
inv_mat_local_to_eye = (mat_projection*mat_local_to_eye).inverse();
198
// Update auto_zoom if activated
199
void Projector::update_auto_zoom(int delta_time)
203
// Use a smooth function
206
if( zoom_move.start > zoom_move.aim )
208
// slow down as approach final view
209
c = 1 - (1-zoom_move.coef)*(1-zoom_move.coef)*(1-zoom_move.coef);
213
// speed up as leave zoom target
214
c = (zoom_move.coef)*(zoom_move.coef)*(zoom_move.coef);
217
set_fov(zoom_move.start + (zoom_move.aim - zoom_move.start) * c);
218
zoom_move.coef+=zoom_move.speed*delta_time;
219
if (zoom_move.coef>=1.)
222
set_fov(zoom_move.aim);
228
// Use a smooth function
230
double c = atanf(smooth * 2.*zoom_move.coef-smooth)/atanf(smooth)/2+0.5;
231
set_fov(zoom_move.start + (zoom_move.aim - zoom_move.start) * c);
232
zoom_move.coef+=zoom_move.speed*delta_time;
233
if (zoom_move.coef>=1.)
236
set_fov(zoom_move.aim);
241
// Zoom to the given field of view
242
void Projector::zoom_to(double aim_fov, float move_duration)
244
zoom_move.aim=aim_fov;
246
zoom_move.speed=1.f/(move_duration*1000);
248
flag_auto_zoom = true;
252
// Set the drawing mode in 2D. Use reset_perspective_projection() to reset
253
// previous projection mode
254
void Projector::set_orthographic_projection(void) const
256
glMatrixMode(GL_PROJECTION); // projection matrix mode
257
glPushMatrix(); // store previous matrix
259
gluOrtho2D( vec_viewport[0], vec_viewport[0] + vec_viewport[2],
260
vec_viewport[1], vec_viewport[1] + vec_viewport[3]); // set a 2D orthographic projection
261
glMatrixMode(GL_MODELVIEW); // modelview matrix mode
266
// Reset the previous projection mode after a call to set_orthographic_projection()
267
void Projector::reset_perspective_projection(void) const
269
glMatrixMode(GL_PROJECTION); // Restore previous matrix
271
glMatrixMode(GL_MODELVIEW);
277
// Reimplementation of gluSphere : glu is overrided for non standard projection
279
void Projector::sSphere(GLdouble radius, GLdouble one_minus_oblateness,
280
GLint slices, GLint stacks,
281
const Mat4d& mat, int orient_inside) const {
285
if (one_minus_oblateness == 1.0) { // gluSphere seems to have hardware acceleration
286
GLUquadricObj * p = gluNewQuadric();
287
gluQuadricTexture(p,GL_TRUE);
288
if (orient_inside) gluQuadricOrientation(p, GLU_INSIDE);
289
gluSphere(p, radius, slices, stacks);
292
//GLfloat rho, theta;
294
GLfloat s, t, ds, dt;
298
if (orient_inside) nsign = -1.0;
301
const GLfloat drho = M_PI / (GLfloat) stacks;
303
#if defined(__sun) || defined(__sun__)
304
// in Sun C/C++ on Solaris 8 VLAs are not allowed, so let's use new double[]
305
double* cos_sin_rho = new double[2 * (stacks + 1)];
307
double cos_sin_rho[2*(stacks+1)];
309
double *cos_sin_rho_p = cos_sin_rho;
310
for (i = 0; i <= stacks; i++) {
311
double rho = i * drho;
312
*cos_sin_rho_p++ = cos(rho);
313
*cos_sin_rho_p++ = sin(rho);
316
const GLfloat dtheta = 2.0 * M_PI / (GLfloat) slices;
317
double cos_sin_theta[2*(slices+1)];
318
double *cos_sin_theta_p = cos_sin_theta;
319
for (i = 0; i <= slices; i++) {
320
double theta = (i == slices) ? 0.0 : i * dtheta;
321
*cos_sin_theta_p++ = cos(theta);
322
*cos_sin_theta_p++ = sin(theta);
324
// texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
325
// t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
326
// cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
329
t = 1.0; // because loop now runs from 0
331
// draw intermediate stacks as quad strips
332
for (i = 0,cos_sin_rho_p = cos_sin_rho; i < stacks;
333
i++,cos_sin_rho_p+=2)
335
glBegin(GL_QUAD_STRIP);
337
for (j = 0,cos_sin_theta_p = cos_sin_theta; j <= slices;
338
j++,cos_sin_theta_p+=2)
340
x = -cos_sin_theta_p[1] * cos_sin_rho_p[1];
341
y = cos_sin_theta_p[0] * cos_sin_rho_p[1];
342
z = nsign * cos_sin_rho_p[0];
343
glNormal3f(x * one_minus_oblateness * nsign,
344
y * one_minus_oblateness * nsign,
349
one_minus_oblateness * z * radius, mat);
350
x = -cos_sin_theta_p[1] * cos_sin_rho_p[3];
351
y = cos_sin_theta_p[0] * cos_sin_rho_p[3];
352
z = nsign * cos_sin_rho_p[2];
353
glNormal3f(x * one_minus_oblateness * nsign,
354
y * one_minus_oblateness * nsign,
356
glTexCoord2f(s, t - dt);
360
one_minus_oblateness * z * radius, mat);
365
#if defined(__sun) || defined(__sun__)
366
delete[] cos_sin_rho;
374
// Draw a half sphere
375
void Projector::sHalfSphere(GLdouble radius, GLint slices, GLint stacks,
376
const Mat4d& mat, int orient_inside) const
381
GLfloat rho, drho, theta, dtheta;
383
GLfloat s, t, ds, dt;
384
GLint i, j, imin, imax;
387
if (orient_inside) nsign = -1.0;
390
drho = M_PI / (GLfloat) stacks;
391
dtheta = 2.0 * M_PI / (GLfloat) slices;
393
// texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
394
// t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
395
// cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
398
t = 1.0; // because loop now runs from 0
402
// draw intermediate stacks as quad strips
403
for (i = imin; i < imax/2; i++)
406
glBegin(GL_QUAD_STRIP);
408
for (j = 0; j <= slices; j++)
410
theta = (j == slices) ? 0.0 : j * dtheta;
411
x = -sin(theta) * sin(rho);
412
y = cos(theta) * sin(rho);
413
z = nsign * cos(rho);
414
glNormal3f(x * nsign, y * nsign, z * nsign);
416
sVertex3(x * radius, y * radius, z * radius, mat);
417
x = -sin(theta) * sin(rho + drho);
418
y = cos(theta) * sin(rho + drho);
419
z = nsign * cos(rho + drho);
420
glNormal3f(x * nsign, y * nsign, z * nsign);
421
glTexCoord2f(s, t - dt);
423
sVertex3(x * radius, y * radius, z * radius, mat);
431
// Draw a disk with a special texturing mode having texture center at disk center
432
void Projector::sDisk(GLdouble radius, GLint slices, GLint stacks,
433
const Mat4d& mat, int orient_inside) const
438
GLfloat r, dr, theta, dtheta;
443
if (orient_inside) nsign = -1.0;
446
dr = radius / (GLfloat) stacks;
447
dtheta = 2.0 * M_PI / (GLfloat) slices;
448
if (slices < 0) slices = -slices;
450
// draw intermediate stacks as quad strips
451
for (r = 0; r < radius; r+=dr)
453
glBegin(GL_TRIANGLE_STRIP);
454
for (j = 0; j <= slices; j++)
456
theta = (j == slices) ? 0.0 : j * dtheta;
459
glNormal3f(0, 0, nsign);
460
glTexCoord2f(0.5+x/2/radius, 0.5+y/2/radius);
461
sVertex3(x, y, 0, mat);
462
x = (r+dr)*cos(theta);
463
y = (r+dr)*sin(theta);
464
glNormal3f(0, 0, nsign);
465
glTexCoord2f(0.5+x/2/radius, 0.5+y/2/radius);
466
sVertex3(x, y, 0, mat);
473
void Projector::sRing(GLdouble r_min, GLdouble r_max,
474
GLint slices, GLint stacks,
475
const Mat4d& mat, int orient_inside) const {
483
const double nsign = (orient_inside)?-1.0:1.0;
485
const double dr = (r_max-r_min) / stacks;
486
const double dtheta = 2.0 * M_PI / slices;
487
if (slices < 0) slices = -slices;
489
#if defined(__sun) || defined(__sun__)
490
//in Sun C/C++ on Solaris 8 VLAs are not allowed, so let's use new double[]
491
double *cos_sin_theta = new double[2*(slices+1)];
493
double cos_sin_theta[2*(slices+1)];
495
double *cos_sin_theta_p = cos_sin_theta;
496
for (j = 0; j <= slices; j++) {
497
const double theta = (j == slices) ? 0.0 : j * dtheta;
498
*cos_sin_theta_p++ = cos(theta);
499
*cos_sin_theta_p++ = sin(theta);
503
// draw intermediate stacks as quad strips
504
for (double r = r_min; r < r_max; r+=dr) {
505
const double tex_r0 = (r-r_min)/(r_max-r_min);
506
const double tex_r1 = (r+dr-r_min)/(r_max-r_min);
507
glBegin(GL_QUAD_STRIP /*GL_TRIANGLE_STRIP*/);
508
for (j=0,cos_sin_theta_p=cos_sin_theta;
510
j++,cos_sin_theta_p+=2) {
511
theta = (j == slices) ? 0.0 : j * dtheta;
512
x = r*cos_sin_theta_p[0];
513
y = r*cos_sin_theta_p[1];
514
glNormal3d(0, 0, nsign);
515
glTexCoord2d(tex_r0, 0.5);
516
sVertex3(x, y, 0, mat);
517
x = (r+dr)*cos_sin_theta_p[0];
518
y = (r+dr)*cos_sin_theta_p[1];
519
glNormal3d(0, 0, nsign);
520
glTexCoord2d(tex_r1, 0.5);
521
sVertex3(x, y, 0, mat);
525
#if defined(__sun) || defined(__sun__)
526
delete[] cos_sin_theta;
533
inline void sSphereMapTexCoordFast(double rho_div_fov,
534
double costheta, double sintheta)
536
if (rho_div_fov>0.5) rho_div_fov=0.5;
537
glTexCoord2d(0.5 + rho_div_fov * costheta,
538
0.5 + rho_div_fov * sintheta);
541
void Projector::sSphere_map(GLdouble radius, GLint slices, GLint stacks,
542
const Mat4d& mat, double texture_fov,
543
int orient_inside) const
550
const double nsign = orient_inside?-1:1;
552
const double drho = M_PI / stacks;
554
#if defined(__sun) || defined(__sun__)
555
// in Sun C/C++ on Solaris 8 VLAs are not allowed, so let's use new double[]
556
double *cos_sin_rho = new double[2*(stacks+1)];
558
double cos_sin_rho[2*(stacks+1)];
560
double *cos_sin_rho_p = cos_sin_rho;
561
for (i = 0; i <= stacks; i++) {
562
const double rho = i * drho;
563
*cos_sin_rho_p++ = cos(rho);
564
*cos_sin_rho_p++ = sin(rho);
567
const double dtheta = 2.0 * M_PI / slices;
568
double cos_sin_theta[2*(slices+1)];
569
double *cos_sin_theta_p = cos_sin_theta;
570
for (i = 0; i <= slices; i++) {
571
const double theta = (i == slices) ? 0.0 : i * dtheta;
572
*cos_sin_theta_p++ = cos(theta);
573
*cos_sin_theta_p++ = sin(theta);
577
// texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis
578
// t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes)
579
// cannot use triangle fan on texturing (s coord. at top/bottom tip varies)
581
const int imax = stacks;
583
// draw intermediate stacks as quad strips
584
if (!orient_inside) // nsign==1
586
for (i = 0,cos_sin_rho_p=cos_sin_rho,rho=0.0;
587
i < imax; ++i,cos_sin_rho_p+=2,rho+=drho)
589
glBegin(GL_QUAD_STRIP);
590
for (j=0,cos_sin_theta_p=cos_sin_theta;
591
j<=slices;++j,cos_sin_theta_p+=2)
593
x = -cos_sin_theta_p[1] * cos_sin_rho_p[1];
594
y = cos_sin_theta_p[0] * cos_sin_rho_p[1];
595
z = cos_sin_rho_p[0];
596
glNormal3d(x * nsign, y * nsign, z * nsign);
597
sSphereMapTexCoordFast(rho/texture_fov,
600
sVertex3(x * radius, y * radius, z * radius, mat);
602
x = -cos_sin_theta_p[1] * cos_sin_rho_p[3];
603
y = cos_sin_theta_p[0] * cos_sin_rho_p[3];
604
z = cos_sin_rho_p[2];
605
glNormal3d(x * nsign, y * nsign, z * nsign);
606
sSphereMapTexCoordFast((rho + drho)/texture_fov,
609
sVertex3(x * radius, y * radius, z * radius, mat);
616
for (i = 0,cos_sin_rho_p=cos_sin_rho,rho=0.0;
617
i < imax; ++i,cos_sin_rho_p+=2,rho+=drho)
619
glBegin(GL_QUAD_STRIP);
620
for (j=0,cos_sin_theta_p=cos_sin_theta;
621
j<=slices;++j,cos_sin_theta_p+=2)
623
x = -cos_sin_theta_p[1] * cos_sin_rho_p[3];
624
y = cos_sin_theta_p[0] * cos_sin_rho_p[3];
625
z = cos_sin_rho_p[2];
626
glNormal3d(x * nsign, y * nsign, z * nsign);
627
sSphereMapTexCoordFast((rho + drho)/texture_fov,
629
-cos_sin_theta_p[1]);
630
sVertex3(x * radius, y * radius, z * radius, mat);
632
x = -cos_sin_theta_p[1] * cos_sin_rho_p[1];
633
y = cos_sin_theta_p[0] * cos_sin_rho_p[1];
634
z = cos_sin_rho_p[0];
635
glNormal3d(x * nsign, y * nsign, z * nsign);
636
sSphereMapTexCoordFast(rho/texture_fov,
638
-cos_sin_theta_p[1]);
639
sVertex3(x * radius, y * radius, z * radius, mat);
644
#if defined(__sun) || defined(__sun__)
645
delete[] cos_sin_rho;
652
// Reimplementation of gluCylinder : glu is overrided for non standard projection
653
void Projector::sCylinder(GLdouble radius, GLdouble height, GLint slices, GLint stacks, const Mat4d& mat, int orient_inside) const
657
GLUquadricObj * p = gluNewQuadric();
658
gluQuadricTexture(p,GL_TRUE);
661
glCullFace(GL_FRONT);
663
gluCylinder(p, radius, radius, height, slices, stacks);
673
void Projector::print_gravity180(s_font* font, float x, float y, const wstring& ws,
674
bool speed_optimize, float xshift, float yshift) const
676
static float dx, dy, d, theta, psi;
677
dx = x - (vec_viewport[0] + vec_viewport[2]/2);
678
dy = y - (vec_viewport[1] + vec_viewport[3]/2);
679
d = sqrt(dx*dx + dy*dy);
681
// If the text is too far away to be visible in the screen return
682
if (d>MY_MAX(vec_viewport[3], vec_viewport[2])*2) return;
685
theta = M_PI + atan2f(dx, dy - 1);
686
psi = atan2f((float)font->getStrLen(ws)/ws.length(),d + 1) * 180./M_PI;
689
set_orthographic_projection();
691
if(gravityLabels) glRotatef(theta*180./M_PI,0,0,-1);
692
glTranslatef(xshift, -yshift, 0);
696
glEnable(GL_TEXTURE_2D);
697
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Normal transparency mode
698
for (unsigned int i=0;i<ws.length();++i)
701
if (ws[i]>=16 && ws[i]<=18) {
702
// handle hilight colors (TUI)
704
// Note: this is hard coded - not generalized
706
if( ws[i]==17 ) glColor3f(0.5,1,0.5); // normal
707
if( ws[i]==18 ) glColor3f(1,1,1); // hilight
711
if( !speed_optimize ) {
712
font->print_char_outlined(ws[i]);
714
font->print_char(ws[i]);
717
// with typeface need to manually advance
718
// TODO, absolute rotation would be better than relative
719
// TODO: would look better with kerning information...
720
glTranslatef(font->getStrLen(ws.substr(i,1)) * 1.05, 0, 0);
722
if( !speed_optimize ) {
723
psi = atan2f((float)font->getStrLen(ws.substr(i,1))*1.05,d) * 180./M_PI;
727
// keep text horizontal if gravity labels off
728
if(gravityLabels) glRotatef(psi,0,0,-1);
732
reset_perspective_projection();