1 // views.cxx -- data structures and routines for managing and view
4 // Written by Curtis Olson, started August 1997.
6 // Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <Aircraft/aircraft.hxx>
30 #include <Cockpit/panel.hxx>
31 #include <Debug/logstream.hxx>
32 #include <Include/fg_constants.h>
33 #include <Math/mat3.h>
34 #include <Math/point3d.hxx>
35 #include <Math/polar3d.hxx>
36 #include <Math/vector.hxx>
37 #include <Scenery/scenery.hxx>
38 #include <Time/fg_time.hxx>
40 #include "options.hxx"
44 // Define following to extract various vectors directly
45 // from matrices we have allready computed
46 // rather then performing 'textbook algebra' to rederive them
47 // Norman Vine -- nhv@yahoo.com
48 // #define FG_VIEW_INLINE_OPTIMIZATIONS
50 // temporary (hopefully) hack
51 static int panel_hist = 0;
54 // specify code paths ... these are done as variable rather than
55 // #define's because down the road we may want to choose between them
56 // on the fly for different flight models ... this way magic carpet
57 // and external modes wouldn't need to recreate the LaRCsim matrices
60 static const bool use_larcsim_local_to_body = false;
63 // This is a record containing current view parameters
68 FGView::FGView( void ) {
73 // Initialize a view structure
74 void FGView::Init( void ) {
75 FG_LOG( FG_VIEW, FG_INFO, "Initializing View parameters" );
78 goal_view_offset = 0.0;
80 winWidth = current_options.get_xsize();
81 winHeight = current_options.get_ysize();
83 if ( ! current_options.get_panel_status() ) {
84 current_view.set_win_ratio( (GLfloat) winWidth / (GLfloat) winHeight );
86 current_view.set_win_ratio( (GLfloat) winWidth /
87 ((GLfloat) (winHeight)*0.4232) );
90 force_update_fov_math();
94 // Update the field of view coefficients
95 void FGView::UpdateFOV( const fgOPTIONS& o ) {
96 double fov, theta_x, theta_y;
100 // printf("win_ratio = %.2f\n", win_ratio);
101 // calculate sin() and cos() of fov / 2 in X direction;
102 theta_x = (fov * win_ratio * DEG_TO_RAD) / 2.0;
103 // printf("theta_x = %.2f\n", theta_x);
104 sin_fov_x = sin(theta_x);
105 cos_fov_x = cos(theta_x);
106 slope_x = -cos_fov_x / sin_fov_x;
107 // printf("slope_x = %.2f\n", slope_x);
109 // fov_x_clip and fov_y_clip convoluted algebraic simplification
110 // see code executed in tilemgr.cxx when USE_FAST_FOV_CLIP not
111 // defined Norman Vine -- nhv@yahoo.com
112 #if defined( USE_FAST_FOV_CLIP )
113 fov_x_clip = slope_x*cos_fov_x - sin_fov_x;
114 #endif // defined( USE_FAST_FOV_CLIP )
116 // calculate sin() and cos() of fov / 2 in Y direction;
117 theta_y = (fov * DEG_TO_RAD) / 2.0;
118 // printf("theta_y = %.2f\n", theta_y);
119 sin_fov_y = sin(theta_y);
120 cos_fov_y = cos(theta_y);
121 slope_y = cos_fov_y / sin_fov_y;
122 // printf("slope_y = %.2f\n", slope_y);
124 #if defined( USE_FAST_FOV_CLIP )
125 fov_y_clip = -(slope_y*cos_fov_y + sin_fov_y);
126 #endif // defined( USE_FAST_FOV_CLIP )
130 // Basically, this is a modified version of the Mesa gluLookAt()
131 // function that's been modified slightly so we can capture the
132 // result before sending it off to OpenGL land.
133 void FGView::LookAt( GLdouble eyex, GLdouble eyey, GLdouble eyez,
134 GLdouble centerx, GLdouble centery, GLdouble centerz,
135 GLdouble upx, GLdouble upy, GLdouble upz ) {
137 GLdouble x[3], y[3], z[3];
140 m = current_view.MODEL_VIEW;
142 /* Make rotation matrix */
145 z[0] = eyex - centerx;
146 z[1] = eyey - centery;
147 z[2] = eyez - centerz;
148 mag = sqrt( z[0]*z[0] + z[1]*z[1] + z[2]*z[2] );
149 if (mag) { /* mpichler, 19950515 */
160 /* X vector = Y cross Z */
161 x[0] = y[1]*z[2] - y[2]*z[1];
162 x[1] = -y[0]*z[2] + y[2]*z[0];
163 x[2] = y[0]*z[1] - y[1]*z[0];
165 /* Recompute Y = Z cross X */
166 y[0] = z[1]*x[2] - z[2]*x[1];
167 y[1] = -z[0]*x[2] + z[2]*x[0];
168 y[2] = z[0]*x[1] - z[1]*x[0];
170 /* mpichler, 19950515 */
171 /* cross product gives area of parallelogram, which is < 1.0 for
172 * non-perpendicular unit-length vectors; so normalize x, y here
175 mag = sqrt( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
182 mag = sqrt( y[0]*y[0] + y[1]*y[1] + y[2]*y[2] );
189 #define M(row,col) m[col*4+row]
190 M(0,0) = x[0]; M(0,1) = x[1]; M(0,2) = x[2]; M(0,3) = 0.0;
191 M(1,0) = y[0]; M(1,1) = y[1]; M(1,2) = y[2]; M(1,3) = 0.0;
192 M(2,0) = z[0]; M(2,1) = z[1]; M(2,2) = z[2]; M(2,3) = 0.0;
193 // the following is part of the original gluLookAt(), but we are
194 // commenting it out because we know we are going to be doing a
195 // translation below which will set these values anyways
196 // M(3,0) = 0.0; M(3,1) = 0.0; M(3,2) = 0.0; M(3,3) = 1.0;
199 // Translate Eye to Origin
200 // replaces: glTranslated( -eyex, -eyey, -eyez );
202 // this has been slightly modified from the original glTranslate()
203 // code because we know that coming into this m[12] = m[13] =
204 // m[14] = 0.0, and m[15] = 1.0;
205 m[12] = m[0] * -eyex + m[4] * -eyey + m[8] * -eyez /* + m[12] */;
206 m[13] = m[1] * -eyex + m[5] * -eyey + m[9] * -eyez /* + m[13] */;
207 m[14] = m[2] * -eyex + m[6] * -eyey + m[10] * -eyez /* + m[14] */;
208 m[15] = 1.0 /* m[3] * -eyex + m[7] * -eyey + m[11] * -eyez + m[15] */;
210 // xglMultMatrixd( m );
215 // Update the view volume, position, and orientation
216 void FGView::UpdateViewParams( void ) {
217 FGInterface *f = current_aircraft.fdm_state;
222 if ((current_options.get_panel_status() != panel_hist) && (current_options.get_panel_status()))
224 FGPanel::OurPanel->ReInit( 0, 0, 1024, 768);
227 if ( ! current_options.get_panel_status() ) {
228 xglViewport(0, 0 , (GLint)(winWidth), (GLint)(winHeight) );
230 xglViewport(0, (GLint)((winHeight)*0.5768), (GLint)(winWidth),
231 (GLint)((winHeight)*0.4232) );
234 // Tell GL we are about to modify the projection parameters
235 xglMatrixMode(GL_PROJECTION);
237 if ( f->get_Altitude() * FEET_TO_METER - scenery.cur_elev > 10.0 ) {
238 gluPerspective(current_options.get_fov(), win_ratio, 10.0, 100000.0);
240 gluPerspective(current_options.get_fov(), win_ratio, 0.5, 100000.0);
241 // printf("Near ground, minimizing near clip plane\n");
245 xglMatrixMode(GL_MODELVIEW);
248 // set up our view volume (default)
249 #if !defined(FG_VIEW_INLINE_OPTIMIZATIONS)
250 LookAt(view_pos.x(), view_pos.y(), view_pos.z(),
251 view_pos.x() + view_forward[0],
252 view_pos.y() + view_forward[1],
253 view_pos.z() + view_forward[2],
254 view_up[0], view_up[1], view_up[2]);
256 // look almost straight up (testing and eclipse watching)
257 /* LookAt(view_pos.x(), view_pos.y(), view_pos.z(),
258 view_pos.x() + view_up[0] + .001,
259 view_pos.y() + view_up[1] + .001,
260 view_pos.z() + view_up[2] + .001,
261 view_up[0], view_up[1], view_up[2]); */
263 // lock view horizontally towards sun (testing)
264 /* LookAt(view_pos.x(), view_pos.y(), view_pos.z(),
265 view_pos.x() + surface_to_sun[0],
266 view_pos.y() + surface_to_sun[1],
267 view_pos.z() + surface_to_sun[2],
268 view_up[0], view_up[1], view_up[2]); */
270 // lock view horizontally towards south (testing)
271 /* LookAt(view_pos.x(), view_pos.y(), view_pos.z(),
272 view_pos.x() + surface_south[0],
273 view_pos.y() + surface_south[1],
274 view_pos.z() + surface_south[2],
275 view_up[0], view_up[1], view_up[2]); */
277 #else // defined(FG_VIEW_INLINE_OPTIMIZATIONS)
278 //void FGView::LookAt( GLdouble eyex, GLdouble eyey, GLdouble eyez,
279 // GLdouble centerx, GLdouble centery, GLdouble centerz,
280 // GLdouble upx, GLdouble upy, GLdouble upz )
283 GLdouble x[3], y[3], z[3];
286 m = current_view.MODEL_VIEW;
288 /* Make rotation matrix */
291 z[0] = -view_forward[0]; //eyex - centerx;
292 z[1] = -view_forward[1]; //eyey - centery;
293 z[2] = -view_forward[2]; //eyez - centerz;
295 // In our case this is a unit vector NHV
297 // mag = sqrt( z[0]*z[0] + z[1]*z[1] + z[2]*z[2] );
298 // if (mag) { /* mpichler, 19950515 */
300 // printf("mag(%f) ", mag);
307 y[0] = view_up[0]; //upx;
308 y[1] = view_up[1]; //upy;
309 y[2] = view_up[2]; //upz;
311 /* X vector = Y cross Z */
312 x[0] = y[1]*z[2] - y[2]*z[1];
313 x[1] = -y[0]*z[2] + y[2]*z[0];
314 x[2] = y[0]*z[1] - y[1]*z[0];
316 // printf(" %f %f %f ", y[0], y[1], y[2]);
318 /* Recompute Y = Z cross X */
319 // y[0] = z[1]*x[2] - z[2]*x[1];
320 // y[1] = -z[0]*x[2] + z[2]*x[0];
321 // y[2] = z[0]*x[1] - z[1]*x[0];
323 // printf(" %f %f %f\n", y[0], y[1], y[2]);
325 // In our case these are unit vectors NHV
327 /* mpichler, 19950515 */
328 /* cross product gives area of parallelogram, which is < 1.0 for
329 * non-perpendicular unit-length vectors; so normalize x, y here
332 // mag = sqrt( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
335 // printf("mag2(%f) ", mag);
341 // mag = sqrt( y[0]*y[0] + y[1]*y[1] + y[2]*y[2] );
344 // printf("mag3(%f)\n", mag);
350 #define M(row,col) m[col*4+row]
351 M(0,0) = x[0]; M(0,1) = x[1]; M(0,2) = x[2]; M(0,3) = 0.0;
352 M(1,0) = y[0]; M(1,1) = y[1]; M(1,2) = y[2]; M(1,3) = 0.0;
353 M(2,0) = z[0]; M(2,1) = z[1]; M(2,2) = z[2]; M(2,3) = 0.0;
354 // the following is part of the original gluLookAt(), but we are
355 // commenting it out because we know we are going to be doing a
356 // translation below which will set these values anyways
357 // M(3,0) = 0.0; M(3,1) = 0.0; M(3,2) = 0.0; M(3,3) = 1.0;
360 // Translate Eye to Origin
361 // replaces: glTranslated( -eyex, -eyey, -eyez );
363 // this has been slightly modified from the original glTranslate()
364 // code because we know that coming into this m[12] = m[13] =
365 // m[14] = 0.0, and m[15] = 1.0;
366 m[12] = m[0] * -view_pos.x() + m[4] * -view_pos.y() + m[8] * -view_pos.z() /* + m[12] */;
367 m[13] = m[1] * -view_pos.x() + m[5] * -view_pos.y() + m[9] * -view_pos.z() /* + m[13] */;
368 m[14] = m[2] * -view_pos.x() + m[6] * -view_pos.y() + m[10] * -view_pos.z() /* + m[14] */;
369 m[15] = 1.0 /* m[3] * -view_pos.x() + m[7] * -view_pos.y() + m[11] * -view_pos.z() + m[15] */;
371 // xglMultMatrixd( m );
374 #endif // FG_VIEW_INLINE_OPTIMIZATIONS
377 panel_hist = current_options.get_panel_status();
381 void getRotMatrix(double* out, MAT3vec vec, double radians)
383 /* This function contributed by Erich Boleyn (erich@uruk.org) */
384 /* This function used from the Mesa OpenGL code (matrix.c) */
386 double vx, vy, vz, xy, yz, zx, xs, ys, zs, one_c; //, xx, yy, zz
392 // mag = getMagnitude();
398 #define M(row,col) out[row*4 + col]
401 * Arbitrary axis rotation matrix.
403 * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied
404 * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation
405 * (which is about the X-axis), and the two composite transforms
406 * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary
407 * from the arbitrary axis to the X-axis then back. They are
408 * all elementary rotations.
410 * Rz' is a rotation about the Z-axis, to bring the axis vector
411 * into the x-z plane. Then Ry' is applied, rotating about the
412 * Y-axis to bring the axis vector parallel with the X-axis. The
413 * rotation about the X-axis is then performed. Ry and Rz are
414 * simply the respective inverse transforms to bring the arbitrary
415 * axis back to it's original orientation. The first transforms
416 * Rz' and Ry' are considered inverses, since the data from the
417 * arbitrary axis gives you info on how to get to it, not how
418 * to get away from it, and an inverse must be applied.
420 * The basic calculation used is to recognize that the arbitrary
421 * axis vector (x, y, z), since it is of unit length, actually
422 * represents the sines and cosines of the angles to rotate the
423 * X-axis to the same orientation, with theta being the angle about
424 * Z and phi the angle about Y (in the order described above)
427 * cos ( theta ) = x / sqrt ( 1 - z^2 )
428 * sin ( theta ) = y / sqrt ( 1 - z^2 )
430 * cos ( phi ) = sqrt ( 1 - z^2 )
433 * Note that cos ( phi ) can further be inserted to the above
436 * cos ( theta ) = x / cos ( phi )
437 * sin ( theta ) = y / cos ( phi )
439 * ...etc. Because of those relations and the standard trigonometric
440 * relations, it is pssible to reduce the transforms down to what
441 * is used below. It may be that any primary axis chosen will give the
442 * same results (modulo a sign convention) using thie method.
444 * Particularly nice is to notice that all divisions that might
445 * have caused trouble when parallel to certain planes or
446 * axis go away with care paid to reducing the expressions.
447 * After checking, it does perform correctly under all cases, since
448 * in all the cases of division where the denominator would have
449 * been zero, the numerator would have been zero as well, giving
450 * the expected result.
464 M(0,0) = (one_c * vx * vx) + c;
466 yz = vy * vz * one_c;
470 M(1,1) = (one_c * vy * vy) + c;
472 zx = vz * vx * one_c;
476 M(2,2) = (one_c * vz *vz) + c;
478 xy = vx * vy * one_c;
482 // M(0,0) = (one_c * xx) + c;
483 // M(1,0) = (one_c * xy) - zs;
484 // M(2,0) = (one_c * zx) + ys;
486 // M(0,1) = (one_c * xy) + zs;
487 // M(1,1) = (one_c * yy) + c;
488 // M(2,1) = (one_c * yz) - xs;
490 // M(0,2) = (one_c * zx) - ys;
491 // M(1,2) = (one_c * yz) + xs;
492 // M(2,2) = (one_c * zz) + c;
498 // Update the view parameters
499 void FGView::UpdateViewMath( FGInterface *f ) {
501 MAT3vec vec, forward, v0, minus_z;
502 MAT3mat R, TMP, UP, LOCAL, VIEW;
506 // printf("Updating fov\n");
507 UpdateFOV( current_options );
511 scenery.center = scenery.next_center;
513 #if !defined(FG_VIEW_INLINE_OPTIMIZATIONS)
514 // printf("scenery center = %.2f %.2f %.2f\n", scenery.center.x,
515 // scenery.center.y, scenery.center.z);
517 // calculate the cartesion coords of the current lat/lon/0 elev
518 p = Point3D( f->get_Longitude(),
519 f->get_Lat_geocentric(),
520 f->get_Sea_level_radius() * FEET_TO_METER );
522 cur_zero_elev = fgPolarToCart3d(p) - scenery.center;
524 // calculate view position in current FG view coordinate system
525 // p.lon & p.lat are already defined earlier, p.radius was set to
526 // the sea level radius, so now we add in our altitude.
527 if ( f->get_Altitude() * FEET_TO_METER >
528 (scenery.cur_elev + 0.5 * METER_TO_FEET) ) {
529 p.setz( p.radius() + f->get_Altitude() * FEET_TO_METER );
531 p.setz( p.radius() + scenery.cur_elev + 0.5 * METER_TO_FEET );
534 abs_view_pos = fgPolarToCart3d(p);
536 #else // FG_VIEW_INLINE_OPTIMIZATIONS
538 double tmp_radius = f->get_Sea_level_radius() * FEET_TO_METER;
539 double tmp = f->get_cos_lat_geocentric() * tmp_radius;
541 cur_zero_elev.setx(f->get_cos_longitude()*tmp - scenery.center.x());
542 cur_zero_elev.sety(f->get_sin_longitude()*tmp - scenery.center.y());
543 cur_zero_elev.setz(f->get_sin_lat_geocentric()*tmp_radius - scenery.center.z());
545 // calculate view position in current FG view coordinate system
546 // p.lon & p.lat are already defined earlier, p.radius was set to
547 // the sea level radius, so now we add in our altitude.
548 if ( f->get_Altitude() * FEET_TO_METER >
549 (scenery.cur_elev + 0.5 * METER_TO_FEET) ) {
550 tmp_radius += f->get_Altitude() * FEET_TO_METER;
552 tmp_radius += scenery.cur_elev + 0.5 * METER_TO_FEET ;
554 tmp = f->get_cos_lat_geocentric() * tmp_radius;
555 abs_view_pos.setx(f->get_cos_longitude()*tmp);
556 abs_view_pos.sety(f->get_sin_longitude()*tmp);
557 abs_view_pos.setz(f->get_sin_lat_geocentric()*tmp_radius);
559 #endif // FG_VIEW_INLINE_OPTIMIZATIONS
561 view_pos = abs_view_pos - scenery.center;
563 FG_LOG( FG_VIEW, FG_DEBUG, "Polar view pos = " << p );
564 FG_LOG( FG_VIEW, FG_DEBUG, "Absolute view pos = " << abs_view_pos );
565 FG_LOG( FG_VIEW, FG_DEBUG, "Relative view pos = " << view_pos );
567 // Derive the LOCAL aircraft rotation matrix (roll, pitch, yaw)
568 // from FG_T_local_to_body[3][3]
570 if ( use_larcsim_local_to_body ) {
572 // Question: Why is the LaRCsim matrix arranged so differently
573 // than the one we need???
575 // Answer (I think): The LaRCsim matrix is generated in a
576 // different reference frame than we've set up for our world
578 LOCAL[0][0] = f->get_T_local_to_body_33();
579 LOCAL[0][1] = -f->get_T_local_to_body_32();
580 LOCAL[0][2] = -f->get_T_local_to_body_31();
582 LOCAL[1][0] = -f->get_T_local_to_body_23();
583 LOCAL[1][1] = f->get_T_local_to_body_22();
584 LOCAL[1][2] = f->get_T_local_to_body_21();
586 LOCAL[2][0] = -f->get_T_local_to_body_13();
587 LOCAL[2][1] = f->get_T_local_to_body_12();
588 LOCAL[2][2] = f->get_T_local_to_body_11();
590 LOCAL[3][0] = LOCAL[3][1] = LOCAL[3][2] = LOCAL[3][3] = 0.0;
593 // printf("LaRCsim LOCAL matrix\n");
594 // MAT3print(LOCAL, stdout);
598 // code to calculate LOCAL matrix calculated from Phi, Theta, and
599 // Psi (roll, pitch, yaw) in case we aren't running LaRCsim as our
602 MAT3_SET_VEC(vec, 0.0, 0.0, 1.0);
603 MAT3rotate(R, vec, f->get_Phi());
604 /* printf("Roll matrix\n"); */
605 /* MAT3print(R, stdout); */
607 MAT3_SET_VEC(vec, 0.0, 1.0, 0.0);
608 /* MAT3mult_vec(vec, vec, R); */
609 MAT3rotate(TMP, vec, f->get_Theta());
610 /* printf("Pitch matrix\n"); */
611 /* MAT3print(TMP, stdout); */
614 MAT3_SET_VEC(vec, 1.0, 0.0, 0.0);
615 /* MAT3mult_vec(vec, vec, R); */
616 /* MAT3rotate(TMP, vec, FG_Psi - FG_PI_2); */
617 MAT3rotate(TMP, vec, -f->get_Psi());
618 /* printf("Yaw matrix\n");
619 MAT3print(TMP, stdout); */
620 MAT3mult(LOCAL, R, TMP);
621 // printf("FG derived LOCAL matrix\n");
622 // MAT3print(LOCAL, stdout);
624 } // if ( use_larcsim_local_to_body )
626 #if !defined(FG_VIEW_INLINE_OPTIMIZATIONS)
628 // Derive the local UP transformation matrix based on *geodetic*
630 MAT3_SET_VEC(vec, 0.0, 0.0, 1.0);
631 MAT3rotate(R, vec, f->get_Longitude()); // R = rotate about Z axis
632 // printf("Longitude matrix\n");
633 // MAT3print(R, stdout);
635 MAT3_SET_VEC(vec, 0.0, 1.0, 0.0);
636 MAT3mult_vec(vec, vec, R);
637 MAT3rotate(TMP, vec, -f->get_Latitude()); // TMP = rotate about X axis
638 // printf("Latitude matrix\n");
639 // MAT3print(TMP, stdout);
641 MAT3mult(UP, R, TMP);
642 // printf("Local up matrix\n");
643 // MAT3print(UP, stdout);
645 MAT3_SET_VEC(local_up, 1.0, 0.0, 0.0);
646 MAT3mult_vec(local_up, local_up, UP);
648 // printf( "Local Up = (%.4f, %.4f, %.4f)\n",
649 // local_up[0], local_up[1], local_up[2]);
651 // Alternative method to Derive local up vector based on
652 // *geodetic* coordinates
653 // alt_up = fgPolarToCart(FG_Longitude, FG_Latitude, 1.0);
654 // printf( " Alt Up = (%.4f, %.4f, %.4f)\n",
655 // alt_up.x, alt_up.y, alt_up.z);
657 // Calculate the VIEW matrix
658 MAT3mult(VIEW, LOCAL, UP);
659 // printf("VIEW matrix\n");
660 // MAT3print(VIEW, stdout);
662 // generate the current up, forward, and fwrd-view vectors
663 MAT3_SET_VEC(vec, 1.0, 0.0, 0.0);
664 MAT3mult_vec(view_up, vec, VIEW);
666 MAT3_SET_VEC(vec, 0.0, 0.0, 1.0);
667 MAT3mult_vec(forward, vec, VIEW);
668 // printf( "Forward vector is (%.2f,%.2f,%.2f)\n", forward[0], forward[1],
671 MAT3rotate(TMP, view_up, view_offset);
672 MAT3mult_vec(view_forward, forward, TMP);
674 // make a vector to the current view position
675 MAT3_SET_VEC(v0, view_pos.x(), view_pos.y(), view_pos.z());
677 // Given a vector pointing straight down (-Z), map into onto the
678 // local plane representing "horizontal". This should give us the
679 // local direction for moving "south".
680 MAT3_SET_VEC(minus_z, 0.0, 0.0, -1.0);
681 map_vec_onto_cur_surface_plane(local_up, v0, minus_z, surface_south);
682 MAT3_NORMALIZE_VEC(surface_south, ntmp);
683 // printf( "Surface direction directly south %.2f %.2f %.2f\n",
684 // surface_south[0], surface_south[1], surface_south[2]);
686 // now calculate the surface east vector
687 MAT3rotate(TMP, view_up, FG_PI_2);
688 MAT3mult_vec(surface_east, surface_south, TMP);
689 // printf( "Surface direction directly east %.2f %.2f %.2f\n",
690 // surface_east[0], surface_east[1], surface_east[2]);
691 // printf( "Should be close to zero = %.2f\n",
692 // MAT3_DOT_PRODUCT(surface_south, surface_east));
694 #else // FG_VIEW_INLINE_OPTIMIZATIONS
696 // // Build spherical to cartesian transform matrix directly
697 double cos_lat = f->get_cos_latitude(); // cos(-f->get_Latitude());
698 double sin_lat = -f->get_sin_latitude(); // sin(-f->get_Latitude());
699 double cos_lon = f->get_cos_longitude(); //cos(f->get_Longitude());
700 double sin_lon = f->get_sin_longitude(); //sin(f->get_Longitude());
702 double *mat = (double *)UP;
704 mat[0] = cos_lat*cos_lon;
705 mat[1] = cos_lat*sin_lon;
712 mat[8] = sin_lat*cos_lon;
713 mat[9] = sin_lat*sin_lon;
715 mat[11] = mat[12] = mat[13] = mat[14] = 0.0;
718 MAT3mult(VIEW, LOCAL, UP);
720 // THESE COULD JUST BE POINTERS !!!
721 MAT3_SET_VEC(local_up, mat[0], mat[1], mat[2]);
722 MAT3_SET_VEC(view_up, VIEW[0][0], VIEW[0][1], VIEW[0][2]);
723 MAT3_SET_VEC(forward, VIEW[2][0], VIEW[2][1], VIEW[2][2]);
725 getRotMatrix((double *)TMP, view_up, view_offset);
726 MAT3mult_vec(view_forward, forward, TMP);
728 // make a vector to the current view position
729 MAT3_SET_VEC(v0, view_pos.x(), view_pos.y(), view_pos.z());
731 // Given a vector pointing straight down (-Z), map into onto the
732 // local plane representing "horizontal". This should give us the
733 // local direction for moving "south".
734 MAT3_SET_VEC(minus_z, 0.0, 0.0, -1.0);
735 map_vec_onto_cur_surface_plane(local_up, v0, minus_z, surface_south);
737 MAT3_NORMALIZE_VEC(surface_south, ntmp);
738 // printf( "Surface direction directly south %.6f %.6f %.6f\n",
739 // surface_south[0], surface_south[1], surface_south[2]);
741 // now calculate the surface east vector
742 getRotMatrix((double *)TMP, view_up, FG_PI_2);
743 MAT3mult_vec(surface_east, surface_south, TMP);
744 // printf( "Surface direction directly east %.6f %.6f %.6f\n",
745 // surface_east[0], surface_east[1], surface_east[2]);
746 // printf( "Should be close to zero = %.6f\n",
747 // MAT3_DOT_PRODUCT(surface_south, surface_east));
748 #endif // !defined(FG_VIEW_INLINE_OPTIMIZATIONS)
752 // Update the "World to Eye" transformation matrix
753 // This is most useful for view frustum culling
754 void FGView::UpdateWorldToEye( FGInterface *f ) {
755 MAT3mat R_Phi, R_Theta, R_Psi, R_Lat, R_Lon, T_view;
759 if ( use_larcsim_local_to_body ) {
761 // Question: hey this is even different then LOCAL[][] above??
762 // Answer: yet another coordinate system, this time the
763 // coordinate system in which we do our view frustum culling.
765 AIRCRAFT[0][0] = -f->get_T_local_to_body_22();
766 AIRCRAFT[0][1] = -f->get_T_local_to_body_23();
767 AIRCRAFT[0][2] = f->get_T_local_to_body_21();
768 AIRCRAFT[0][3] = 0.0;
769 AIRCRAFT[1][0] = f->get_T_local_to_body_32();
770 AIRCRAFT[1][1] = f->get_T_local_to_body_33();
771 AIRCRAFT[1][2] = -f->get_T_local_to_body_31();
772 AIRCRAFT[1][3] = 0.0;
773 AIRCRAFT[2][0] = f->get_T_local_to_body_12();
774 AIRCRAFT[2][1] = f->get_T_local_to_body_13();
775 AIRCRAFT[2][2] = -f->get_T_local_to_body_11();
776 AIRCRAFT[2][3] = 0.0;
777 AIRCRAFT[3][0] = AIRCRAFT[3][1] = AIRCRAFT[3][2] = AIRCRAFT[3][3] = 0.0;
778 AIRCRAFT[3][3] = 1.0;
783 MAT3_SET_HVEC(vec, 0.0, 0.0, -1.0, 1.0);
784 MAT3rotate(R_Phi, vec, f->get_Phi());
785 // printf("Roll matrix (Phi)\n");
786 // MAT3print(R_Phi, stdout);
789 MAT3_SET_HVEC(vec, 1.0, 0.0, 0.0, 1.0);
790 MAT3rotate(R_Theta, vec, f->get_Theta());
791 // printf("\nPitch matrix (Theta)\n");
792 // MAT3print(R_Theta, stdout);
795 MAT3_SET_HVEC(vec, 0.0, -1.0, 0.0, 1.0);
796 MAT3rotate(R_Psi, vec, f->get_Psi() + FG_PI /* - view_offset */ );
797 // MAT3rotate(R_Psi, vec, f->get_Psi() + FG_PI - view_offset );
798 // printf("\nYaw matrix (Psi)\n");
799 // MAT3print(R_Psi, stdout);
801 // aircraft roll/pitch/yaw
802 MAT3mult(TMP, R_Phi, R_Theta);
803 MAT3mult(AIRCRAFT, TMP, R_Psi);
805 } // if ( use_larcsim_local_to_body )
807 #if !defined(FG_VIEW_INLINE_OPTIMIZATIONS)
809 // printf("AIRCRAFT matrix\n");
810 // MAT3print(AIRCRAFT, stdout);
812 // View rotation matrix relative to current aircraft orientation
813 MAT3_SET_HVEC(vec, 0.0, -1.0, 0.0, 1.0);
814 MAT3mult_vec(vec, vec, AIRCRAFT);
815 // printf("aircraft up vector = %.2f %.2f %.2f\n",
816 // vec[0], vec[1], vec[2]);
817 MAT3rotate(TMP, vec, -view_offset );
818 MAT3mult(VIEW_OFFSET, AIRCRAFT, TMP);
819 // printf("VIEW_OFFSET matrix\n");
820 // MAT3print(VIEW_OFFSET, stdout);
822 // View position in scenery centered coordinates
823 MAT3_SET_HVEC(vec, view_pos.x(), view_pos.y(), view_pos.z(), 1.0);
824 MAT3translate(T_view, vec);
825 // printf("\nTranslation matrix\n");
826 // MAT3print(T_view, stdout);
829 MAT3_SET_HVEC(vec, 1.0, 0.0, 0.0, 1.0);
830 // R_Lat = rotate about X axis
831 MAT3rotate(R_Lat, vec, f->get_Latitude());
832 // printf("\nLatitude matrix\n");
833 // MAT3print(R_Lat, stdout);
836 MAT3_SET_HVEC(vec, 0.0, 0.0, 1.0, 1.0);
837 // R_Lon = rotate about Z axis
838 MAT3rotate(R_Lon, vec, f->get_Longitude() - FG_PI_2 );
839 // printf("\nLongitude matrix\n");
840 // MAT3print(R_Lon, stdout);
843 MAT3mult(WORLD, R_Lat, R_Lon);
844 // printf("\nworld\n");
845 // MAT3print(WORLD, stdout);
847 MAT3mult(EYE_TO_WORLD, VIEW_OFFSET, WORLD);
848 MAT3mult(EYE_TO_WORLD, EYE_TO_WORLD, T_view);
849 // printf("\nEye to world\n");
850 // MAT3print(EYE_TO_WORLD, stdout);
852 MAT3invert(WORLD_TO_EYE, EYE_TO_WORLD);
853 // printf("\nWorld to eye\n");
854 // MAT3print(WORLD_TO_EYE, stdout);
856 // printf( "\nview_pos = %.2f %.2f %.2f\n",
857 // view_pos.x, view_pos.y, view_pos.z );
859 // MAT3_SET_HVEC(eye, 0.0, 0.0, 0.0, 1.0);
860 // MAT3mult_vec(vec, eye, EYE_TO_WORLD);
861 // printf("\neye -> world = %.2f %.2f %.2f\n", vec[0], vec[1], vec[2]);
863 // MAT3_SET_HVEC(vec1, view_pos.x, view_pos.y, view_pos.z, 1.0);
864 // MAT3mult_vec(vec, vec1, WORLD_TO_EYE);
865 // printf( "\nabs_view_pos -> eye = %.2f %.2f %.2f\n",
866 // vec[0], vec[1], vec[2]);
867 #else // FG_VIEW_INLINE_OPTIMIZATIONS
869 MAT3_SET_HVEC(vec, -AIRCRAFT[1][0], -AIRCRAFT[1][1], -AIRCRAFT[1][2], -AIRCRAFT[1][3]);
870 getRotMatrix((double *)TMP, vec, -view_offset );
871 MAT3mult(VIEW_OFFSET, AIRCRAFT, TMP);
872 // MAT3print_formatted(VIEW_OFFSET, stdout, "VIEW_OFFSET matrix:\n",
873 // NULL, "%#8.6f ", "\n");
875 // Build spherical to cartesian transform matrix directly
876 double *mat = (double *)WORLD; //T_view; //WORLD;
877 double cos_lat = f->get_cos_latitude(); //cos(f->get_Latitude());
878 double sin_lat = f->get_sin_latitude(); //sin(f->get_Latitude());
879 // using trig identities this:
880 // mat[0] = cos(f->get_Longitude() - FG_PI_2);//cos_lon;
881 // mat[1] = sin(f->get_Longitude() - FG_PI_2);//sin_lon;
883 mat[0] = f->get_sin_longitude(); //cos_lon;
884 mat[1] = -f->get_cos_longitude(); //sin_lon;
885 mat[4] = -cos_lat*mat[1]; //mat[1]=sin_lon;
886 mat[5] = cos_lat*mat[0]; //mat[0]=cos_lon;
888 mat[8] = sin_lat*mat[1]; //mat[1]=sin_lon;
889 mat[9] = -sin_lat*mat[0]; //mat[0]=cos_lon;
892 // BUILD EYE_TO_WORLD = AIRCRAFT * WORLD
893 // and WORLD_TO_EYE = Inverse( EYE_TO_WORLD) concurrently
894 // by Transposing the 3x3 rotation sub-matrix
895 WORLD_TO_EYE[0][0] = EYE_TO_WORLD[0][0] =
896 VIEW_OFFSET[0][0]*mat[0] + VIEW_OFFSET[0][1]*mat[4] + VIEW_OFFSET[0][2]*mat[8];
898 WORLD_TO_EYE[1][0] = EYE_TO_WORLD[0][1] =
899 VIEW_OFFSET[0][0]*mat[1] + VIEW_OFFSET[0][1]*mat[5] + VIEW_OFFSET[0][2]*mat[9];
901 WORLD_TO_EYE[2][0] = EYE_TO_WORLD[0][2] =
902 VIEW_OFFSET[0][1]*mat[6] + VIEW_OFFSET[0][2]*mat[10];
904 WORLD_TO_EYE[0][1] = EYE_TO_WORLD[1][0] =
905 VIEW_OFFSET[1][0]*mat[0] + VIEW_OFFSET[1][1]*mat[4] + VIEW_OFFSET[1][2]*mat[8];
907 WORLD_TO_EYE[1][1] = EYE_TO_WORLD[1][1] =
908 VIEW_OFFSET[1][0]*mat[1] + VIEW_OFFSET[1][1]*mat[5] + VIEW_OFFSET[1][2]*mat[9];
910 WORLD_TO_EYE[2][1] = EYE_TO_WORLD[1][2] =
911 VIEW_OFFSET[1][1]*mat[6] + VIEW_OFFSET[1][2]*mat[10];
913 WORLD_TO_EYE[0][2] = EYE_TO_WORLD[2][0] =
914 VIEW_OFFSET[2][0]*mat[0] + VIEW_OFFSET[2][1]*mat[4] + VIEW_OFFSET[2][2]*mat[8];
916 WORLD_TO_EYE[1][2] = EYE_TO_WORLD[2][1] =
917 VIEW_OFFSET[2][0]*mat[1] + VIEW_OFFSET[2][1]*mat[5] + VIEW_OFFSET[2][2]*mat[9];
919 WORLD_TO_EYE[2][2] = EYE_TO_WORLD[2][2] =
920 VIEW_OFFSET[2][1]*mat[6] + VIEW_OFFSET[2][2]*mat[10];
922 // TRANSLATE TO VIEW POSITION
923 EYE_TO_WORLD[3][0] = view_pos.x();
924 EYE_TO_WORLD[3][1] = view_pos.y();
925 EYE_TO_WORLD[3][2] = view_pos.z();
928 WORLD_TO_EYE[0][3] = WORLD_TO_EYE[1][3] = WORLD_TO_EYE[2][3] =
929 EYE_TO_WORLD[0][3] = EYE_TO_WORLD[1][3] = EYE_TO_WORLD[2][3] = 0.0;
931 // FILL UNITY ENTRIES
932 WORLD_TO_EYE[3][3] = EYE_TO_WORLD[3][3] = 1.0;
934 /* MAKE THE INVERTED TRANSLATIONS */
935 mat = (double *)EYE_TO_WORLD;
936 WORLD_TO_EYE[3][0] = -mat[12]*mat[0]
940 WORLD_TO_EYE[3][1] = -mat[12]*mat[4]
944 WORLD_TO_EYE[3][2] = -mat[12]*mat[8]
948 // MAT3print_formatted(EYE_TO_WORLD, stdout, "EYE_TO_WORLD matrix:\n",
949 // NULL, "%#8.6f ", "\n");
951 // MAT3print_formatted(WORLD_TO_EYE, stdout, "WORLD_TO_EYE matrix:\n",
952 // NULL, "%#8.6f ", "\n");
954 #endif // defined(FG_VIEW_INLINE_OPTIMIZATIONS)
959 // Reject non viewable spheres from current View Frustrum by Curt
960 // Olson curt@me.umn.edu and Norman Vine nhv@yahoo.com with 'gentle
961 // guidance' from Steve Baker sbaker@link.com
963 FGView::SphereClip( const Point3D& cp, const double radius )
975 mat = (double *)(WORLD_TO_EYE);
977 eye[2] = x*mat[2] + y*mat[6] + z*mat[10] + mat[14];
979 // Check near and far clip plane
980 if( ( eye[2] > radius ) ||
981 ( eye[2] + radius + current_weather.visibility < 0) )
982 // ( eye[2] + radius + far_plane < 0) )
987 // check right and left clip plane (from eye perspective)
988 x1 = radius * fov_x_clip;
989 eye[0] = (x*mat[0] + y*mat[4] + z*mat[8] + mat[12]) * slope_x;
990 if( (eye[2] > -(eye[0]+x1)) || (eye[2] > (eye[0]-x1)) ) {
994 // check bottom and top clip plane (from eye perspective)
995 y1 = radius * fov_y_clip;
996 eye[1] = (x*mat[1] + y*mat[5] + z*mat[9] + mat[13]) * slope_y;
997 if( (eye[2] > -(eye[1]+y1)) || (eye[2] > (eye[1]-y1)) ) {
1007 FGView::~FGView( void ) {