void update_view( player_data_t *plyr, scalar_t dt ) { point_t view_pt; vector_t view_dir, up_dir, vel_dir, view_vec; scalar_t ycoord; scalar_t course_angle; vector_t axis; matrixgl_t rot_mat; vector_t y_vec; vector_t mz_vec; vector_t vel_proj; quaternion_t rot_quat; scalar_t speed; vector_t vel_cpy; scalar_t time_constant_mult; vel_cpy = plyr->vel; speed = normalize_vector( &vel_cpy ); time_constant_mult = 1.0 / min( 1.0, max( 0.0, ( speed - NO_INTERPOLATION_SPEED ) / ( BASELINE_INTERPOLATION_SPEED - NO_INTERPOLATION_SPEED ))); up_dir = make_vector( 0, 1, 0 ); vel_dir = plyr->vel; normalize_vector( &vel_dir ); course_angle = get_course_angle(); switch( plyr->view.mode ) { case TUXEYE: { scalar_t f = 2; vector_t v = plyr->plane_nml; scalar_t n = 1.; view_pt = plyr->pos; view_pt.x += v.x / n * 0.3; view_pt.y += v.y / n * 0.3; view_pt.y += 0.1; view_pt.z += v.z / n * 0.3; if(plyr->control.flip_factor || plyr->control.barrel_roll_factor) { matrixgl_t mat1, mat; vector_t right; scalar_t n = sqrt(plyr->viewdir_for_tuxeye.x * plyr->viewdir_for_tuxeye.x + plyr->viewdir_for_tuxeye.y * plyr->viewdir_for_tuxeye.y + plyr->viewdir_for_tuxeye.z * plyr->viewdir_for_tuxeye.z); plyr->viewdir_for_tuxeye.x /= n; plyr->viewdir_for_tuxeye.y /= n; plyr->viewdir_for_tuxeye.z /= n; n = sqrt(plyr->updir_for_tuxeye.x * plyr->updir_for_tuxeye.x + plyr->updir_for_tuxeye.y * plyr->updir_for_tuxeye.y + plyr->updir_for_tuxeye.z * plyr->updir_for_tuxeye.z); plyr->updir_for_tuxeye.x /= n; plyr->updir_for_tuxeye.y /= n; plyr->updir_for_tuxeye.z /= n; right = cross_product(plyr->updir_for_tuxeye, plyr->viewdir_for_tuxeye); make_rotation_about_vector_matrix( mat1, right, jump_from_time(plyr->control.flip_factor) * 360 ); make_rotation_about_vector_matrix( mat, plyr->viewdir_for_tuxeye, jump_from_time(plyr->control.barrel_roll_factor) * 360 ); multiply_matrices(mat, mat1, mat); view_dir = transform_vector(mat, plyr->viewdir_for_tuxeye); up_dir = transform_vector(mat, plyr->updir_for_tuxeye); } else { view_dir = plyr->direction; view_dir.y += 0.1; view_dir.x = (plyr->view.dir.x * f + view_dir.x) / (f + 1); view_dir.y = (plyr->view.dir.y * f + view_dir.y) / (f + 1); view_dir.z = (plyr->view.dir.z * f + view_dir.z) / (f + 1); plyr->viewdir_for_tuxeye = view_dir; up_dir = plyr->plane_nml; up_dir.x = (plyr->view.up.x * f + up_dir.x) / (f + 1); up_dir.y = (plyr->view.up.y * f + up_dir.y) / (f + 1); up_dir.z = (plyr->view.up.z * f + up_dir.z) / (f + 1); plyr->updir_for_tuxeye = up_dir; } break; } case BEHIND: { /* Camera-on-a-string mode */ /* Construct vector from player to camera */ view_vec = make_vector( 0, sin( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ), cos( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ) ); view_vec = scale_vector( CAMERA_DISTANCE, view_vec ); y_vec = make_vector( 0.0, 1.0, 0.0 ); mz_vec = make_vector( 0.0, 0.0, -1.0 ); vel_proj = project_into_plane( y_vec, vel_dir ); normalize_vector( &vel_proj ); /* Rotate view_vec so that it places the camera behind player */ rot_quat = make_rotation_quaternion( mz_vec, vel_proj ); view_vec = rotate_vector( rot_quat, view_vec ); /* Construct view point */ view_pt = move_point( plyr->pos, view_vec ); /* Make sure view point is above terrain */ ycoord = find_y_coord( view_pt.x, view_pt.z ); if ( view_pt.y < ycoord + MIN_CAMERA_HEIGHT ) { view_pt.y = ycoord + MIN_CAMERA_HEIGHT; } /* Interpolate view point */ if ( plyr->view.initialized ) { /* Interpolate twice to get a second-order filter */ int i; for (i=0; i<2; i++) { view_pt = interpolate_view_pos( plyr->pos, plyr->pos, MAX_CAMERA_PITCH, plyr->view.pos, view_pt, CAMERA_DISTANCE, dt, BEHIND_ORBIT_TIME_CONSTANT * time_constant_mult ); } } /* Make sure interpolated view point is above terrain */ ycoord = find_y_coord( view_pt.x, view_pt.z ); if ( view_pt.y < ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT ) { view_pt.y = ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT; } /* Construct view direction */ view_vec = subtract_points( view_pt, plyr->pos ); axis = cross_product( y_vec, view_vec ); normalize_vector( &axis ); make_rotation_about_vector_matrix( rot_mat, axis, PLAYER_ANGLE_IN_CAMERA ); view_dir = scale_vector( -1.0, transform_vector( rot_mat, view_vec ) ); /* Interpolate orientation of camera */ if ( plyr->view.initialized ) { /* Interpolate twice to get a second-order filter */ int i; for (i=0; i<2; i++) { interpolate_view_frame( plyr->view.up, plyr->view.dir, &up_dir, &view_dir, dt, BEHIND_ORIENT_TIME_CONSTANT ); up_dir = make_vector( 0.0, 1.0, 0.0 ); } } break; } case FOLLOW: { /* Camera follows player (above and behind) */ up_dir = make_vector( 0, 1, 0 ); /* Construct vector from player to camera */ view_vec = make_vector( 0, sin( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ), cos( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ) ); view_vec = scale_vector( CAMERA_DISTANCE, view_vec ); y_vec = make_vector( 0.0, 1.0, 0.0 ); mz_vec = make_vector( 0.0, 0.0, -1.0 ); vel_proj = project_into_plane( y_vec, vel_dir ); normalize_vector( &vel_proj ); /* Rotate view_vec so that it places the camera behind player */ rot_quat = make_rotation_quaternion( mz_vec, vel_proj ); view_vec = rotate_vector( rot_quat, view_vec ); /* Construct view point */ view_pt = move_point( plyr->pos, view_vec ); /* Make sure view point is above terrain */ ycoord = find_y_coord( view_pt.x, view_pt.z ); if ( view_pt.y < ycoord + MIN_CAMERA_HEIGHT ) { view_pt.y = ycoord + MIN_CAMERA_HEIGHT; } /* Interpolate view point */ if ( plyr->view.initialized ) { /* Interpolate twice to get a second-order filter */ int i; for ( i=0; i<2; i++ ) { view_pt = interpolate_view_pos( plyr->view.plyr_pos, plyr->pos, MAX_CAMERA_PITCH, plyr->view.pos, view_pt, CAMERA_DISTANCE, dt, FOLLOW_ORBIT_TIME_CONSTANT * time_constant_mult ); } } /* Make sure interpolate view point is above terrain */ ycoord = find_y_coord( view_pt.x, view_pt.z ); if ( view_pt.y < ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT ) { view_pt.y = ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT; } /* Construct view direction */ view_vec = subtract_points( view_pt, plyr->pos ); axis = cross_product( y_vec, view_vec ); normalize_vector( &axis ); make_rotation_about_vector_matrix( rot_mat, axis, PLAYER_ANGLE_IN_CAMERA ); view_dir = scale_vector( -1.0, transform_vector( rot_mat, view_vec ) ); /* Interpolate orientation of camera */ if ( plyr->view.initialized ) { /* Interpolate twice to get a second-order filter */ int i; for ( i=0; i<2; i++ ) { interpolate_view_frame( plyr->view.up, plyr->view.dir, &up_dir, &view_dir, dt, FOLLOW_ORIENT_TIME_CONSTANT ); up_dir = make_vector( 0.0, 1.0, 0.0 ); } } break; } case ABOVE: { /* Camera always uphill of player */ up_dir = make_vector( 0, 1, 0 ); /* Construct vector from player to camera */ view_vec = make_vector( 0, sin( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE+ PLAYER_ANGLE_IN_CAMERA ) ), cos( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE+ PLAYER_ANGLE_IN_CAMERA ) ) ); view_vec = scale_vector( CAMERA_DISTANCE, view_vec ); /* Construct view point */ view_pt = move_point( plyr->pos, view_vec ); /* Make sure view point is above terrain */ ycoord = find_y_coord( view_pt.x, view_pt.z ); if ( view_pt.y < ycoord + MIN_CAMERA_HEIGHT ) { view_pt.y = ycoord + MIN_CAMERA_HEIGHT; } /* Construct view direction */ view_vec = subtract_points( view_pt, plyr->pos ); make_rotation_matrix( rot_mat, PLAYER_ANGLE_IN_CAMERA, 'x' ); view_dir = scale_vector( -1.0, transform_vector( rot_mat, view_vec ) ); break; } default: code_not_reached(); } /* Create view matrix */ plyr->view.pos = view_pt; plyr->view.dir = view_dir; plyr->view.up = up_dir; plyr->view.plyr_pos = plyr->pos; plyr->view.initialized = True; setup_view_matrix( plyr ); }
/*! Updates camera and sets the view matrix \pre plyr != NULL, plyr has been initialized with position & velocity info., plyr->view.mode has been set \arg \c plyr pointer to player data \arg \c dt time step size \return none \author jfpatry \date Created: 2000-08-26 \date Modified: 2000-08-26 */ void update_view(Player& plyr, const float dt) { ppogl::Vec3d view_pt; ppogl::Vec3d view_dir; ppogl::Vec3d vel_cpy = plyr.vel; const float speed = vel_cpy.normalize(); const float time_constant_mult = 1.0 / MIN( 1.0, MAX( 0.0, ( speed - NO_INTERPOLATION_SPEED ) / ( BASELINE_INTERPOLATION_SPEED - NO_INTERPOLATION_SPEED ))); ppogl::Vec3d up_dir = ppogl::Vec3d( 0, 1, 0 ); ppogl::Vec3d vel_dir = plyr.vel; vel_dir.normalize(); const float course_angle = Course::getAngle(); switch(plyr.view.mode){ case BEHIND: { /* Camera-on-a-string mode */ /* Construct vector from player to camera */ ppogl::Vec3d view_vec(0, sin( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ), cos( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ) ); view_vec = CAMERA_DISTANCE*view_vec; ppogl::Vec3d y_vec(0.0, 1.0, 0.0); ppogl::Vec3d mz_vec(0.0, 0.0, -1.0); ppogl::Vec3d vel_proj = projectIntoPlane( y_vec, vel_dir ); vel_proj.normalize(); /* Rotate view_vec so that it places the camera behind player */ pp::Quat rot_quat(mz_vec, vel_proj); view_vec = rot_quat.rotate(view_vec); /* Construct view point */ view_pt = plyr.pos - view_vec; /* Make sure view point is above terrain */ float ycoord = find_y_coord( view_pt ); if ( view_pt.y() < ycoord + MIN_CAMERA_HEIGHT ) { view_pt.y() = ycoord + MIN_CAMERA_HEIGHT; } /* Interpolate view point */ if ( plyr.view.initialized ) { /* Interpolate twice to get a second-order filter */ for (int i=0; i<2; i++) { view_pt = interpolate_view_pos( plyr.pos, plyr.pos, MAX_CAMERA_PITCH, plyr.view.pos, view_pt, CAMERA_DISTANCE, dt, BEHIND_ORBIT_TIME_CONSTANT * time_constant_mult ); } } /* Make sure interpolated view point is above terrain */ ycoord = find_y_coord( view_pt ); if ( view_pt.y() < ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT ) { view_pt.y() = ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT; } /* Construct view direction */ view_vec = view_pt - plyr.pos; ppogl::Vec3d axis = y_vec^view_vec; axis.normalize(); pp::Matrix rot_mat; rot_mat.makeRotationAboutVector( axis, PLAYER_ANGLE_IN_CAMERA ); view_dir = -1.0*rot_mat.transformVector( view_vec ); /* Interpolate orientation of camera */ if ( plyr.view.initialized ) { /* Interpolate twice to get a second-order filter */ for (int i=0; i<2; i++) { interpolate_view_frame( plyr.view.up, plyr.view.dir, up_dir, view_dir, dt, BEHIND_ORIENT_TIME_CONSTANT ); up_dir = ppogl::Vec3d( 0.0, 1.0, 0.0 ); } } break; } case FOLLOW: { /* Camera follows player (above and behind) */ up_dir = ppogl::Vec3d( 0, 1, 0 ); /* Construct vector from player to camera */ ppogl::Vec3d view_vec( 0, sin( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ), cos( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE + PLAYER_ANGLE_IN_CAMERA ) ) ); view_vec = CAMERA_DISTANCE*view_vec; ppogl::Vec3d y_vec(0.0, 1.0, 0.0); ppogl::Vec3d mz_vec(0.0, 0.0, -1.0); ppogl::Vec3d vel_proj = projectIntoPlane( y_vec, vel_dir ); vel_proj.normalize(); /* Rotate view_vec so that it places the camera behind player */ pp::Quat rot_quat(mz_vec, vel_proj); view_vec = rot_quat.rotate( view_vec ); /* Construct view point */ view_pt = plyr.pos + view_vec; /* Make sure view point is above terrain */ float ycoord = find_y_coord( view_pt ); if ( view_pt.y() < ycoord + MIN_CAMERA_HEIGHT ) { view_pt.y() = ycoord + MIN_CAMERA_HEIGHT; } /* Interpolate view point */ if ( plyr.view.initialized ) { /* Interpolate twice to get a second-order filter */ for (int i=0; i<2; i++ ) { view_pt = interpolate_view_pos( plyr.view.plyr_pos, plyr.pos, MAX_CAMERA_PITCH, plyr.view.pos, view_pt, CAMERA_DISTANCE, dt, FOLLOW_ORBIT_TIME_CONSTANT * time_constant_mult ); } } /* Make sure interpolate view point is above terrain */ ycoord = find_y_coord( view_pt ); if ( view_pt.y() < ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT ) { view_pt.y() = ycoord + ABSOLUTE_MIN_CAMERA_HEIGHT; } /* Construct view direction */ view_vec = view_pt - plyr.pos; ppogl::Vec3d axis = y_vec^view_vec; axis.normalize(); pp::Matrix rot_mat; rot_mat.makeRotationAboutVector( axis, PLAYER_ANGLE_IN_CAMERA ); view_dir = -1.0*rot_mat.transformVector( view_vec ); /* Interpolate orientation of camera */ if ( plyr.view.initialized ) { /* Interpolate twice to get a second-order filter */ for (int i=0; i<2; i++ ) { interpolate_view_frame( plyr.view.up, plyr.view.dir, up_dir, view_dir, dt, FOLLOW_ORIENT_TIME_CONSTANT ); up_dir = ppogl::Vec3d( 0.0, 1.0, 0.0 ); } } break; } case ABOVE: { /* Camera always uphill of player */ up_dir = ppogl::Vec3d( 0, 1, 0 ); /* Construct vector from player to camera */ ppogl::Vec3d view_vec( 0, sin( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE+ PLAYER_ANGLE_IN_CAMERA ) ), cos( ANGLES_TO_RADIANS( course_angle - CAMERA_ANGLE_ABOVE_SLOPE+ PLAYER_ANGLE_IN_CAMERA ) ) ); view_vec = CAMERA_DISTANCE*view_vec; /* Construct view point */ view_pt = plyr.pos + view_vec; /* Make sure view point is above terrain */ float ycoord = find_y_coord( view_pt ); if ( view_pt.y() < ycoord + MIN_CAMERA_HEIGHT ) { view_pt.y() = ycoord + MIN_CAMERA_HEIGHT; } /* Construct view direction */ view_vec = view_pt - plyr.pos; pp::Matrix rot_mat; rot_mat.makeRotation( PLAYER_ANGLE_IN_CAMERA, ppogl::AXIS_X ); view_dir = -1.0*rot_mat.transformVector( view_vec ); break; } default: PP_NOT_REACHED(); } /* Create view matrix */ plyr.view.pos = view_pt; plyr.view.dir = view_dir; plyr.view.up = up_dir; plyr.view.plyr_pos = plyr.pos; plyr.view.initialized = true; }