Back to index

extremetuxracer  0.5beta
view.cpp
Go to the documentation of this file.
00001 /* 
00002  * PPRacer 
00003  * Copyright (C) 2004-2005 Volker Stroebel <volker@planetpenguin.de>
00004  *
00005  * Copyright (C) 1999-2001 Jasmin F. Patry
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020  */
00021 
00022 #include "view.h"
00023 #include "phys_sim.h"
00024 #include "model_hndl.h"
00025 #include "hier.h"
00026 #include "stuff.h"
00027 
00028 #include "ppgltk/alg/defs.h"
00029 
00030 /* This defines the camera height "target" for all cameras; 
00031    cameras can go below this because of interpolation (m) */
00032 #define MIN_CAMERA_HEIGHT  1.5
00033 
00034 /* Absolute lower bound on camera height above terrain (m) */
00035 #define ABSOLUTE_MIN_CAMERA_HEIGHT  0.3
00036 
00037 /* Distance of camera from player (m) */
00038 #define CAMERA_DISTANCE 4.0
00039 
00040 /* Angle that camera should look above the slope of the terrain (deg) */
00041 #define CAMERA_ANGLE_ABOVE_SLOPE 10
00042 
00043 /* Angle at which to position player in camera below the horizontal (deg) */
00044 #define PLAYER_ANGLE_IN_CAMERA 20
00045 
00046 /* Maximum downward pitch of camera (deg)  */
00047 #define MAX_CAMERA_PITCH 40
00048 
00049 /* Time constant for interpolation of camera position in "behind" mode (s) */
00050 #define BEHIND_ORBIT_TIME_CONSTANT 0.06
00051 
00052 /* Time constant for interpolation of camera orientation in 
00053    "behind" mode (s) */
00054 #define BEHIND_ORIENT_TIME_CONSTANT 0.06
00055 
00056 /* Time constant for interpolation of camera position in "follow" mode (s) */
00057 #define FOLLOW_ORBIT_TIME_CONSTANT 0.06
00058 
00059 /* Time constant for interpolation of orientation position in 
00060    "follow" mode (s) */
00061 #define FOLLOW_ORIENT_TIME_CONSTANT 0.06
00062 
00063 /* This places an upper limit on the interpolation, which prevent excessive
00064    "jumping" of the camera during the occaisonal slow frame */
00065 #define MAX_INTERPOLATION_VALUE 0.3
00066 
00067 /* Speed above which interpolation is "normal" (m/s) */
00068 #define BASELINE_INTERPOLATION_SPEED 4.5
00069 
00070 /* Speed below which no camera position interpolation occurs (m/s) */
00071 #define NO_INTERPOLATION_SPEED 2.0
00072 
00073 static pp::Vec3d   tux_eye_pts[2];
00074 static pp::Vec3d   tux_view_pt;
00075 
00076 void set_view_mode( Player& plyr, view_mode_t mode )
00077 {
00078     plyr.view.mode = mode;
00079     print_debug( DEBUG_VIEW, "View mode: %d", plyr.view.mode );
00080 } 
00081 
00082 view_mode_t get_view_mode( Player& plyr )
00083 {
00084     return plyr.view.mode;
00085 } 
00086 
00087 void traverse_dag_for_view_point( scene_node_t *node, pp::Matrix trans )
00088 {
00089     pp::Matrix new_trans;
00090     scene_node_t *child;
00091 
00092     check_assertion( node != NULL, "node is NULL" );
00093 
00094     new_trans=trans*node->trans;
00095 
00096     if ( node->eye == true ) {
00097        set_tux_eye( node->which_eye,
00098                    new_trans.transformPoint( pp::Vec3d( 0., 0., 0. ) ) );
00099     }
00100 
00101     child = node->child;
00102     while (child != NULL) {
00103         traverse_dag_for_view_point( child, new_trans );
00104         child = child->next;
00105     } 
00106 }
00107 
00108 pp::Vec3d get_tux_view_pt( Player& plyr ) 
00109 { 
00110     pp::Matrix trans;
00111     char *tux_root_node_name;
00112     scene_node_t *tux_root_node;
00113 
00114     trans.makeIdentity();
00115 
00116     tux_root_node_name = ModelHndl->get_tux_root_node();
00117 
00118     if ( get_scene_node( tux_root_node_name, &tux_root_node ) != TCL_OK ) {
00119        check_assertion(0, "couldn't load tux's root node" );
00120     } 
00121 
00122     traverse_dag_for_view_point( tux_root_node, trans );
00123 
00124     tux_view_pt = tux_view_pt+ (0.2*plyr.plane_nml);
00125 
00126     return tux_view_pt; 
00127 }
00128 
00129 void set_tux_eye( tux_eye_t which_eye, pp::Vec3d pt ) 
00130 {
00131     tux_eye_pts[ which_eye ] = pt;
00132 
00133     tux_view_pt.x = ( tux_eye_pts[0].x + tux_eye_pts[1].x ) / 2.0; 
00134     tux_view_pt.y = ( tux_eye_pts[0].y + tux_eye_pts[1].y ) / 2.0; 
00135     tux_view_pt.z = ( tux_eye_pts[0].z + tux_eye_pts[1].z ) / 2.0; 
00136 }
00137 
00138 
00155 pp::Vec3d interpolate_view_pos( pp::Vec3d plyr_pos1, pp::Vec3d plyr_pos2,
00156                            double max_vec_angle,
00157                            pp::Vec3d pos1, pp::Vec3d pos2,
00158                            double dist, double dt,
00159                            double time_constant )
00160 {
00161     static pp::Vec3d y_vec(0.0, 1.0, 0.0);
00162 
00163     pp::Quat q1, q2;
00164     pp::Vec3d vec1, vec2;
00165     double alpha;
00166     double theta;
00167     pp::Matrix rot_mat;
00168     pp::Vec3d axis;
00169 
00170     vec1 = pos1 - plyr_pos1;
00171     vec2 = pos2 - plyr_pos2;
00172 
00173     vec1.normalize();
00174     vec2.normalize();
00175 
00176     q1 = pp::Quat( y_vec, vec1 );
00177     q2 = pp::Quat( y_vec, vec2 );
00178 
00179     alpha = MIN( MAX_INTERPOLATION_VALUE,
00180                1.0 - exp ( -dt / time_constant ) );
00181 
00182     q2 = pp::Quat::interpolate( q1, q2, alpha );
00183 
00184     vec2 = q2.rotate( y_vec );
00185 
00186     /* Constrain angle with x-z plane */
00187     theta = RADIANS_TO_ANGLES( M_PI/2 - acos(vec2*y_vec) );
00188 
00189     if ( theta > max_vec_angle )
00190     {
00191        axis = y_vec^vec2;
00192        axis.normalize();
00193 
00194        rot_mat.makeRotationAboutVector( axis, 
00195                                       theta-max_vec_angle );
00196        vec2 = rot_mat.transformVector( vec2 );
00197     }
00198 
00199     return plyr_pos2+dist*vec2;
00200 
00201 }
00202 
00203 
00222 void interpolate_view_frame( pp::Vec3d up1, pp::Vec3d dir1,
00223                           pp::Vec3d *p_up2, pp::Vec3d *p_dir2,
00224                           double dt, double time_constant )
00225 {
00226     pp::Quat q1, q2;
00227     double alpha;
00228     pp::Vec3d x1, y1, z1;
00229     pp::Vec3d x2, y2, z2;
00230     pp::Matrix cob_mat1, inv_cob_mat1;
00231     pp::Matrix inv_cob_mat2;
00232 
00233     /* Now interpolate between camera orientations */
00234     z1 = -1.0*dir1;
00235     z1.normalize();
00236 
00237     y1 = projectIntoPlane( z1, up1 );
00238     y1.normalize();
00239 
00240     x1 = y1^z1;
00241 
00242     pp::Matrix::makeChangeOfBasisMatrix( cob_mat1, inv_cob_mat1,
00243                              x1, y1, z1 );
00244 
00245     q1 = pp::Quat( cob_mat1 );
00246 
00247     z2 = -1.0*(*p_dir2);
00248     z2.normalize();
00249 
00250     y2 = projectIntoPlane( z2, *p_up2 );
00251     y2.normalize();
00252 
00253     x2 = y2^z2;
00254        {
00255               pp::Matrix cob_mat2;
00256        pp::Matrix::makeChangeOfBasisMatrix( cob_mat2, inv_cob_mat2,
00257                              x2, y2, z2 );
00258 
00259        q2 = pp::Quat( cob_mat2 );
00260        }
00261     alpha = MIN( MAX_INTERPOLATION_VALUE, 
00262                1.0 - exp( -dt / time_constant ) );
00263 
00264     q2 = pp::Quat::interpolate( q1, q2, alpha );
00265 
00266     pp::Matrix cob_mat2( q2 );
00267 
00268     p_up2->x = cob_mat2.data[1][0];
00269     p_up2->y = cob_mat2.data[1][1];
00270     p_up2->z = cob_mat2.data[1][2];
00271 
00272     p_dir2->x = -cob_mat2.data[2][0];
00273     p_dir2->y = -cob_mat2.data[2][1];
00274     p_dir2->z = -cob_mat2.data[2][2];
00275 }
00276 
00277 void setup_view_matrix( Player& plyr ) 
00278 {
00279     pp::Vec3d view_x, view_y, view_z;
00280     pp::Matrix view_mat;
00281     pp::Vec3d viewpt_in_view_frame;
00282 
00283     view_z = -1*plyr.view.dir;
00284     view_x = plyr.view.up^view_z;
00285     view_y = view_z^view_x;
00286     view_z.normalize();
00287     view_x.normalize();
00288     view_y.normalize();
00289 
00290     plyr.view.inv_view_mat.makeIdentity();
00291 
00292     plyr.view.inv_view_mat.data[0][0] = view_x.x;
00293     plyr.view.inv_view_mat.data[0][1] = view_x.y;
00294     plyr.view.inv_view_mat.data[0][2] = view_x.z;
00295 
00296     plyr.view.inv_view_mat.data[1][0] = view_y.x;
00297     plyr.view.inv_view_mat.data[1][1] = view_y.y;
00298     plyr.view.inv_view_mat.data[1][2] = view_y.z;
00299 
00300     plyr.view.inv_view_mat.data[2][0] = view_z.x;
00301     plyr.view.inv_view_mat.data[2][1] = view_z.y;
00302     plyr.view.inv_view_mat.data[2][2] = view_z.z;
00303 
00304     plyr.view.inv_view_mat.data[3][0] = plyr.view.pos.x;
00305     plyr.view.inv_view_mat.data[3][1] = plyr.view.pos.y;
00306     plyr.view.inv_view_mat.data[3][2] = plyr.view.pos.z;
00307     plyr.view.inv_view_mat.data[3][3] = 1;
00308     
00309     view_mat.transpose(plyr.view.inv_view_mat);
00310 
00311     view_mat.data[0][3] = 0;
00312     view_mat.data[1][3] = 0;
00313     view_mat.data[2][3] = 0;
00314     
00315     viewpt_in_view_frame = view_mat.transformPoint( plyr.view.pos );
00316     
00317     view_mat.data[3][0] = -viewpt_in_view_frame.x;
00318     view_mat.data[3][1] = -viewpt_in_view_frame.y;
00319     view_mat.data[3][2] = -viewpt_in_view_frame.z;
00320     
00321     glLoadIdentity();
00322     glMultMatrixd( (double *) view_mat.data );
00323 }
00324 
00337 void update_view( Player& plyr, double dt )
00338 {
00339     pp::Vec3d view_pt;
00340     pp::Vec3d view_dir, up_dir, vel_dir, view_vec;
00341     double ycoord;
00342     double course_angle;
00343     pp::Vec3d axis;
00344     pp::Matrix rot_mat;
00345     pp::Vec3d y_vec;
00346     pp::Vec3d mz_vec;
00347     pp::Vec3d vel_proj;
00348     pp::Quat rot_quat;
00349     double speed;
00350     pp::Vec3d vel_cpy;
00351     double time_constant_mult;
00352 
00353     vel_cpy = plyr.vel;
00354     speed = vel_cpy.normalize();
00355 
00356     time_constant_mult = 1.0 /
00357        MIN( 1.0, 
00358             MAX( 0.0, 
00359                 ( speed - NO_INTERPOLATION_SPEED ) /
00360                 ( BASELINE_INTERPOLATION_SPEED - NO_INTERPOLATION_SPEED )));
00361 
00362     up_dir = pp::Vec3d( 0, 1, 0 );
00363 
00364     vel_dir = plyr.vel;
00365     vel_dir.normalize();
00366 
00367     course_angle = get_course_angle();
00368 
00369     switch( plyr.view.mode ) {
00370 
00371     case BEHIND:
00372     {
00373        /* Camera-on-a-string mode */
00374 
00375        /* Construct vector from player to camera */
00376        view_vec = pp::Vec3d( 0, 
00377                             sin( ANGLES_TO_RADIANS( 
00378                                 course_angle -
00379                                 CAMERA_ANGLE_ABOVE_SLOPE + 
00380                                 PLAYER_ANGLE_IN_CAMERA ) ),
00381                             cos( ANGLES_TO_RADIANS( 
00382                                 course_angle -
00383                                 CAMERA_ANGLE_ABOVE_SLOPE + 
00384                                 PLAYER_ANGLE_IN_CAMERA ) ) );
00385 
00386        view_vec = CAMERA_DISTANCE*view_vec;
00387 
00388        y_vec = pp::Vec3d( 0.0, 1.0, 0.0 );
00389        mz_vec = pp::Vec3d( 0.0, 0.0, -1.0 );
00390        vel_proj = projectIntoPlane( y_vec, vel_dir );
00391 
00392        vel_proj.normalize();
00393 
00394        /* Rotate view_vec so that it places the camera behind player */
00395        rot_quat = pp::Quat( mz_vec, vel_proj );
00396 
00397        view_vec = rot_quat.rotate(view_vec);
00398 
00399 
00400        /* Construct view point */
00401        view_pt = plyr.pos - view_vec;
00402 
00403        /* Make sure view point is above terrain */
00404         ycoord = find_y_coord( view_pt.x, view_pt.z );
00405 
00406         if ( view_pt.y < ycoord + MIN_CAMERA_HEIGHT ) {
00407             view_pt.y = ycoord + MIN_CAMERA_HEIGHT;
00408         } 
00409 
00410        /* Interpolate view point */
00411        if ( plyr.view.initialized ) {
00412            /* Interpolate twice to get a second-order filter */
00413            int i;
00414            for (i=0; i<2; i++) {
00415               view_pt = 
00416                   interpolate_view_pos( plyr.pos, plyr.pos, 
00417                                      MAX_CAMERA_PITCH, plyr.view.pos, 
00418                                      view_pt, CAMERA_DISTANCE, dt,
00419                                      BEHIND_ORBIT_TIME_CONSTANT * 
00420                                      time_constant_mult );
00421            }
00422        }
00423 
00424        /* Make sure interpolated view point is above terrain */
00425         ycoord = find_y_coord( view_pt.x, view_pt.z );
00426 
00427         if ( view_pt.y < ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT ) {
00428             view_pt.y = ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT;
00429         } 
00430 
00431        /* Construct view direction */
00432        view_vec = view_pt - plyr.pos;
00433        
00434        axis = y_vec^view_vec;
00435        axis.normalize();
00436        
00437        rot_mat.makeRotationAboutVector( axis,
00438                                       PLAYER_ANGLE_IN_CAMERA );
00439        view_dir = -1.0*rot_mat.transformVector( view_vec );
00440 
00441        /* Interpolate orientation of camera */
00442        if ( plyr.view.initialized ) {
00443            /* Interpolate twice to get a second-order filter */
00444            int i;
00445            for (i=0; i<2; i++) {
00446               interpolate_view_frame( plyr.view.up, plyr.view.dir,
00447                                    &up_dir, &view_dir, dt,
00448                                    BEHIND_ORIENT_TIME_CONSTANT );
00449               up_dir = pp::Vec3d( 0.0, 1.0, 0.0 );
00450            }
00451        }
00452 
00453         break;
00454     }
00455 
00456     case FOLLOW: 
00457     {
00458        /* Camera follows player (above and behind) */
00459 
00460        up_dir = pp::Vec3d( 0, 1, 0 );
00461 
00462        /* Construct vector from player to camera */
00463        view_vec = pp::Vec3d( 0, 
00464                             sin( ANGLES_TO_RADIANS( 
00465                                 course_angle -
00466                                 CAMERA_ANGLE_ABOVE_SLOPE +
00467                                 PLAYER_ANGLE_IN_CAMERA ) ),
00468                             cos( ANGLES_TO_RADIANS( 
00469                                 course_angle -
00470                                 CAMERA_ANGLE_ABOVE_SLOPE + 
00471                                 PLAYER_ANGLE_IN_CAMERA ) ) );
00472        view_vec = CAMERA_DISTANCE*view_vec;
00473 
00474        y_vec = pp::Vec3d( 0.0, 1.0, 0.0 );
00475        mz_vec = pp::Vec3d( 0.0, 0.0, -1.0 );
00476        vel_proj = projectIntoPlane( y_vec, vel_dir );
00477 
00478        vel_proj.normalize();
00479 
00480        /* Rotate view_vec so that it places the camera behind player */
00481        rot_quat = pp::Quat( mz_vec, vel_proj );
00482 
00483        view_vec = rot_quat.rotate( view_vec );
00484 
00485 
00486        /* Construct view point */
00487        view_pt = plyr.pos + view_vec;
00488 
00489 
00490        /* Make sure view point is above terrain */
00491         ycoord = find_y_coord( view_pt.x, view_pt.z );
00492 
00493         if ( view_pt.y < ycoord + MIN_CAMERA_HEIGHT ) {
00494             view_pt.y = ycoord + MIN_CAMERA_HEIGHT;
00495        }
00496 
00497        /* Interpolate view point */
00498        if ( plyr.view.initialized ) {
00499            /* Interpolate twice to get a second-order filter */
00500            int i;
00501            for ( i=0; i<2; i++ ) {
00502               view_pt = 
00503                   interpolate_view_pos( plyr.view.plyr_pos, plyr.pos, 
00504                                      MAX_CAMERA_PITCH, plyr.view.pos, 
00505                                      view_pt, CAMERA_DISTANCE, dt,
00506                                      FOLLOW_ORBIT_TIME_CONSTANT *
00507                                      time_constant_mult );
00508            }
00509        }
00510 
00511        /* Make sure interpolate view point is above terrain */
00512         ycoord = find_y_coord( view_pt.x, view_pt.z );
00513 
00514         if ( view_pt.y < ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT ) {
00515             view_pt.y = ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT;
00516         } 
00517 
00518        /* Construct view direction */
00519        view_vec = view_pt - plyr.pos;
00520        
00521        axis = y_vec^view_vec;
00522        axis.normalize();
00523        
00524        rot_mat.makeRotationAboutVector( axis, PLAYER_ANGLE_IN_CAMERA );
00525        view_dir = -1.0*rot_mat.transformVector( view_vec );
00526 
00527        /* Interpolate orientation of camera */
00528        if ( plyr.view.initialized ) {
00529            /* Interpolate twice to get a second-order filter */
00530            int i;
00531            for ( i=0; i<2; i++ ) {
00532               interpolate_view_frame( plyr.view.up, plyr.view.dir,
00533                                    &up_dir, &view_dir, dt,
00534                                    FOLLOW_ORIENT_TIME_CONSTANT );
00535               up_dir = pp::Vec3d( 0.0, 1.0, 0.0 );
00536            }
00537        }
00538 
00539         break;
00540     }
00541 
00542     case ABOVE:
00543     {
00544        /* Camera always uphill of player */
00545 
00546        up_dir = pp::Vec3d( 0, 1, 0 );
00547 
00548 
00549        /* Construct vector from player to camera */
00550        view_vec = pp::Vec3d( 0, 
00551                             sin( ANGLES_TO_RADIANS( 
00552                                 course_angle - 
00553                                 CAMERA_ANGLE_ABOVE_SLOPE+
00554                                 PLAYER_ANGLE_IN_CAMERA ) ),
00555                             cos( ANGLES_TO_RADIANS( 
00556                                 course_angle - 
00557                                 CAMERA_ANGLE_ABOVE_SLOPE+ 
00558                                 PLAYER_ANGLE_IN_CAMERA ) ) );
00559        view_vec = CAMERA_DISTANCE*view_vec;
00560 
00561        
00562        /* Construct view point */
00563        view_pt = plyr.pos + view_vec;
00564 
00565 
00566        /* Make sure view point is above terrain */
00567         ycoord = find_y_coord( view_pt.x, view_pt.z );
00568 
00569         if ( view_pt.y < ycoord + MIN_CAMERA_HEIGHT ) {
00570             view_pt.y = ycoord + MIN_CAMERA_HEIGHT;
00571        }
00572 
00573        /* Construct view direction */
00574        view_vec = view_pt - plyr.pos;
00575 
00576        rot_mat.makeRotation( PLAYER_ANGLE_IN_CAMERA, 'x' );
00577        view_dir = -1.0*rot_mat.transformVector( view_vec );
00578 
00579         break;
00580     }
00581 
00582     default:
00583        code_not_reached();
00584     } 
00585 
00586     /* Create view matrix */
00587     plyr.view.pos = view_pt;
00588     plyr.view.dir = view_dir;
00589     plyr.view.up = up_dir;
00590     plyr.view.plyr_pos = plyr.pos;
00591     plyr.view.initialized = true;
00592 
00593     setup_view_matrix( plyr );
00594 }