+ vg_camera_lerp_angles( localplayer.angles, velocity_angles,
+ inf_fpv,
+ localplayer.angles );
+
+ /*
+ * Third person camera
+ */
+
+ /* no idea what this technique is called, it acts like clamped position based
+ * on some derivative of where the final camera would end up ....
+ *
+ * it is done in the local basis then transformed back */
+
+ v3f future;
+ v3_muls( localplayer.rb.v, 0.4f*vg.time_frame_delta, future );
+
+ v3f camera_follow_dir =
+ { -sinf( localplayer.angles[0] ) * cosf( localplayer.angles[1] ),
+ sinf( localplayer.angles[1] ),
+ cosf( localplayer.angles[0] ) * cosf( localplayer.angles[1] ) };
+
+ v3f v0;
+ v3_sub( camera_follow_dir, future, v0 );
+
+ v3f follow_angles;
+ v3_copy( localplayer.angles, follow_angles );
+ follow_angles[0] = atan2f( -v0[0], v0[2] );
+ follow_angles[1] = 0.3f + velocity_angles[1] * 0.2f;
+
+ float ya = atan2f( -cc->cam_velocity_smooth[1], 30.0f );
+
+ follow_angles[1] = 0.3f + ya;
+ vg_camera_lerp_angles( localplayer.angles, follow_angles,
+ inf_tpv,
+ localplayer.angles );
+
+ v3f pco;
+ v4f pq;
+ rb_extrapolate( &localplayer.rb, pco, pq );
+ v3_muladds( pco, localplayer.holdout_pose.root_co,
+ localplayer.holdout_time, pco );
+ v3_lerp( cc->tpv_lpf, pco, 20.0f*vg.time_frame_delta, cc->tpv_lpf );
+
+ /* now move into world */
+ v3f tpv_pos, tpv_offset, tpv_origin;
+
+ /* TODO: whats up with CC and not CC but both sets of variables are doing
+ * the same ideas just saved in different places?
+ */
+ /* origin */
+ q_mulv( pq, cc->tpv_offset_smooth, tpv_origin );
+ v3_add( tpv_origin, cc->tpv_lpf, tpv_origin );
+
+ /* offset */
+ v3_muls( camera_follow_dir, localplayer.cam_dist_smooth, tpv_offset );
+ v3_muladds( tpv_offset, cc->cam_velocity_smooth, -0.025f, tpv_offset );
+
+ v3_add( tpv_origin, tpv_offset, tpv_pos );
+
+#if 0
+ if( localplayer.subsystem == k_player_subsystem_walk )
+ {
+ v3f fwd, right;
+ v3_angles_vector( localplayer.angles, fwd );
+ v3_cross( fwd, (v3f){0,1.001f,0}, right );
+ right[1] = 0.0f;
+ v3_normalize( right );
+ v3_muladds( tpv_pos, right, 0.5f, tpv_pos );
+ }
+#endif
+
+ /*
+ * Blend cameras
+ */
+ v3_lerp( tpv_pos, fpv_pos, cc->camera_type_blend, localplayer.cam.pos );
+ v3_copy( localplayer.angles, localplayer.cam.angles );
+
+ /* Camera shake */
+ f32 speed = v3_length(localplayer.rb.v),
+ strength = k_cam_shake_strength * speed;
+ localplayer.cam_trackshake +=
+ speed*k_cam_shake_trackspeed*vg.time_frame_delta;
+
+ v2f rnd = {vg_perlin_fract_1d( localplayer.cam_trackshake, 1.0f, 4, 20 ),
+ vg_perlin_fract_1d( localplayer.cam_trackshake, 1.0f, 4, 63 ) };
+ v2_muladds( localplayer.cam.angles, rnd, strength, localplayer.cam.angles );
+
+ v3f Fd, Fs, F;
+ v3_muls( localplayer.cam_land_punch_v, -k_cam_damp, Fd );
+ v3_muls( localplayer.cam_land_punch, -k_cam_spring, Fs );
+ v3_muladds( localplayer.cam_land_punch, localplayer.cam_land_punch_v,
+ vg.time_frame_delta, localplayer.cam_land_punch );
+ v3_add( Fd, Fs, F );
+ v3_muladds( localplayer.cam_land_punch_v, F, vg.time_frame_delta,
+ localplayer.cam_land_punch_v );
+ v3_add( localplayer.cam_land_punch, localplayer.cam.pos,
+ localplayer.cam.pos );