Quake3World.com Forums
     Programming Discussion
        BFP camera code implementation


Post new topicReply to topic
Login | Profile | | FAQ | Search | IRC




Print view Previous topic | Next topic 
Topic Starter Topic: BFP camera code implementation

Commander
Commander
Joined: 08 Jun 2022
Posts: 100
PostPosted: 02-05-2023 06:23 AM           Profile Send private message  E-mail  Edit post Reply with quote


I'm implementing BFP camera view, but it doesn't do the same like original Bid For Power (BFP). But AFAIK, the code uses modified Q3 camera parts, it doesn't do the same as ZEQ2-Lite (nothing to do). The camera does a pretty wild flip when flying upside down, have less zoom/more distance than BFP, ... That's badly implemented. Just asking, what should be correct to be the same as BFP camera view?

Video comparising camera implementation and original BFP camera:
https://streamable.com/4gee0d

Code of the camera implementation in CG_OffsetThirdPersonView:
Code:
/*
===============
CG_OffsetThirdPersonView

===============
*/
#define   FOCUS_DISTANCE   512
static void CG_OffsetThirdPersonView( void ) {
   vec3_t      forward, right, up;
   vec3_t      view;
   vec3_t      focusAngles;
   trace_t      trace;
   static vec3_t   mins = { -4, -4, -4 };
   static vec3_t   maxs = { 4, 4, 4 };
   vec3_t      focusPoint;
   float      focusDist;
   float      forwardScale, sideScale;

   // BFP - TODO: Improve camera, when player looks at the ground, the view should
   // be adjusted where the player position, in the screen, is further down like BFP does.
   // The camera isn't using a fixed view

   cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;

   VectorCopy( cg.refdefViewAngles, focusAngles );

   // if dead, look at killer
   if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
      focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
      cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
   }

   // if ( focusAngles[PITCH] > 45 ) {
   //    focusAngles[PITCH] = 45;      // don't go too far overhead
   // }
   AngleVectors( focusAngles, forward, NULL, NULL );

   VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );

   VectorCopy( cg.refdef.vieworg, view );

   // view[2] += 8;

   // BFP - Third person camera height
   view[2] += cg_thirdPersonHeight.value + 92;

   // BFP - Keep the looking on the ground at the same main focus position
   // if ( !( cg.predictedPlayerState.pm_flags & PMF_FLYING ) ) {
      cg.refdefViewAngles[PITCH] *= 1.55;
   // }

   AngleVectors( cg.refdefViewAngles, forward, right, up );
   
   forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
   sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
   VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
   VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );

   // trace a ray from the origin to the viewpoint to make sure the view isn't
   // in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything

   // if (!cg_cameraMode.integer) {
      CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );

      if ( trace.fraction != 1.0 ) {
         VectorCopy( trace.endpos, view );

         view[2] += (1.0 - trace.fraction) * 32;
         // try another trace to this position, because a tunnel may have the ceiling
         // close enogh that this is poking out

         CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
         VectorCopy( trace.endpos, view );
      }
   // }
   

   VectorCopy( view, cg.refdef.vieworg );

   // select pitch to look at focus point from vieword
   VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
   focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
   if ( focusDist < 1 ) {
      focusDist = 1;   // should never happen
   }
   // cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
   cg.refdefViewAngles[PITCH] = focusAngles[PITCH];
   cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
   //cg.refdefViewAngles[YAW] = focusAngles[YAW];
}




Top
                 

Commander
Commander
Joined: 08 Jun 2022
Posts: 100
PostPosted: 02-12-2023 08:34 AM           Profile Send private message  E-mail  Edit post Reply with quote


Finally, BFP third person camera implemented! Wooohoooohoho!!!
https://github.com/LegendaryGuard/BFP/c ... 220067e33d




Top
                 

Commander
Commander
Joined: 08 Jun 2022
Posts: 100
PostPosted: 02-22-2023 03:47 PM           Profile Send private message  E-mail  Edit post Reply with quote


BFP 3rd person camera matches 100% to original BFP!

Moreover, 1st person vis mode is implemented! Let's goooooo!!

Here the 3rd person camera code:

Code:
#define   FOCUS_DISTANCE   512
static void CG_OffsetThirdPersonView( void ) {
   vec3_t      forward, right, up;
   vec3_t      view;
   vec3_t      focusAngles;
   trace_t      trace;
   static vec3_t   mins = { -4, -4, -4 };
   static vec3_t   maxs = { 4, 4, 4 };
   float      forwardScale, sideScale;
   // BFP - Camera setup variables
   vec3_t      overrideOrg;
   float      camAngle, camHeight, camRange;
   // BFP - Fly tilt
   int         cmdNum;
   usercmd_t   cmd;
   // BFP - Last angled for fly tilt angle to move smoothly similar to BFP vanilla
   static float   lastAngled = 0.0f, lastRightAngled = 0.0f, lastUpAngled = 0.0f;
   float      rightAngled, upAngled;

   // BFP - Camera setup
   camAngle  =  cg_thirdPersonAngle.value;
   camHeight =  cg_thirdPersonHeight.value;
   camRange  =  cg_thirdPersonRange.value;
   if ( cg_fixedThirdPerson.integer >= 1 ) { // BFP - Fixed third person camera
      camAngle  =   0.0f;
      camHeight = -60.0f;
      camRange  = 110.0f;
   }
   VectorCopy( cg.refdef.vieworg, overrideOrg );

   // cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight; // BFP - BFP camera position doesn't use that to move to player's height

   VectorCopy( cg.refdefViewAngles, focusAngles );

   // if dead, look at killer
   if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
      focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
      cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
   }

   AngleVectors( focusAngles, forward, NULL, NULL );

   VectorCopy( cg.refdef.vieworg, view );

   AngleVectors( cg.refdefViewAngles, forward, right, up );

   // BFP - Camera height setup
   VectorMA( overrideOrg, -camHeight, up, overrideOrg );

   forwardScale = cos( camAngle / 180 * M_PI );
   sideScale = sin( camAngle / 180 * M_PI );

   // trace a ray from the origin to the viewpoint to make sure the view isn't
   // in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything

   // BFP - cg_cameraMode cvar to detect if it's disabled doesn't exist
   // That traces the camera pivot
   CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
   if ( trace.fraction != 1.0 ) {
      // BFP - Use the vector scale to trace something solid and add endpos
      VectorScale( trace.plane.normal, camRange, view );
      VectorAdd( trace.endpos, view, cg.refdef.vieworg );

      view[2] += (1.0 - trace.fraction) * 32;
      // try another trace to this position, because a tunnel may have the ceiling
      // close enogh that this is poking out

      CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
      VectorCopy( trace.endpos, view );
   }

   // BFP - Camera setup
   focusAngles[YAW] -= camAngle;

   // BFP - Fly tilt
   // Get the pressed keys to move left or right
   cmdNum = trap_GetCurrentCmdNumber();
   trap_GetUserCmd( cmdNum, &cmd );

   focusAngles[ROLL] = LERP( lastAngled, 0.0f, (float)(cg.frametime / 1000.00f) * 20.0f );
   rightAngled = LERP( lastRightAngled, 0.0f, (float)(cg.frametime / 1000.00f) * 20.0f );
   upAngled = LERP( lastUpAngled, 0.0f, (float)(cg.frametime / 1000.00f) * 20.0f );

   if ( cg_flytilt.integer >= 1
   && ( cg.predictedPlayerState.pm_flags & PMF_FLYING )
   && ( cg.predictedPlayerState.eFlags & EF_AURA ) && &cmd ) {
      if ( cmd.rightmove < 0 ) { // Left
         focusAngles[ROLL] = LERP( lastAngled, -20.0f, (float)(cg.frametime / 1000.00f) * 15.0f );
         rightAngled = LERP( lastRightAngled, focusAngles[ROLL] - 0.55f, (float)(cg.frametime / 1000.00f) * 15.0f );
         upAngled = LERP( lastUpAngled, -acos( focusAngles[ROLL] / 180 * M_PI ) - 1.7f, (float)(cg.frametime / 1000.00f) * 15.0f );
      } else if ( cmd.rightmove > 0 ) { // Right
         focusAngles[ROLL] = LERP( lastAngled, 20.0f, (float)(cg.frametime / 1000.00f) * 15.0f );
         rightAngled = LERP( lastRightAngled, focusAngles[ROLL] - 0.55f, (float)(cg.frametime / 1000.00f) * 15.0f );
         upAngled = LERP( lastUpAngled, -acos( focusAngles[ROLL] / 180 * M_PI ) - 1.7f, (float)(cg.frametime / 1000.00f) * 15.0f );
      }
   }
   // Last roll where it was "lerped"
   lastAngled = focusAngles[ROLL];
   lastRightAngled = rightAngled;
   lastUpAngled = upAngled;

   VectorCopy( focusAngles, cg.refdefViewAngles );
   // VectorCopy( focusAngles, cg.predictedPlayerState.viewangles ); // For player model, doesn't make sense though :P

   // BFP - NOTE: Applying angles to height and slide (up and right vectors), while rolling with fly tilt, is an odd case, BFP has something that handles up and right vectors  (· ·') *curiosity sweat*
   VectorMA( overrideOrg, -camRange * sideScale + rightAngled, right, cg.refdef.vieworg );
   VectorMA( cg.refdef.vieworg, -camRange * forwardScale, forward, cg.refdef.vieworg );
   VectorMA( cg.refdef.vieworg, upAngled, up, cg.refdef.vieworg );

   // BFP - Trace the camera position when being near to something solid
   CG_Trace( &trace, view, mins, maxs, cg.refdef.vieworg, cg.predictedPlayerState.clientNum, MASK_SOLID );
   if ( trace.fraction != 1.0f ) {
      VectorCopy( trace.endpos, cg.refdef.vieworg );

      cg.refdef.vieworg[2] += ( 1.0f - trace.fraction ) * 32;

      CG_Trace( &trace, view, mins, maxs, cg.refdef.vieworg, cg.predictedPlayerState.clientNum, MASK_SOLID );
      VectorCopy( trace.endpos, cg.refdef.vieworg );
   }
}




Top
                 

Commander
Commander
Joined: 08 Jun 2022
Posts: 100
PostPosted: 10-02-2023 02:59 AM           Profile Send private message  E-mail  Edit post Reply with quote


Here the part of 1st person vis mode (if you wanna use the code, you will need to use correctly the functions due the difference of the functionality. Look carefully to the implementation in the repository on cg_view.c and cg_players.c):
Code:
void CG_OffsetFirstPersonView( centity_t *cent, refEntity_t *parent, qhandle_t parentModel ) { // BFP - First person setup, originally, Q3 doesn't use these parameters
   float         *origin;
   float         *angles;
   float         bob;
   float         ratio;
   float         delta;
   float         speed;
   float         f;
   vec3_t         predictedVelocity;
   int            timeDelta;
   orientation_t   tagOrient; // BFP - First person vis mode orientation setup
   
   if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
      return;
   }

   origin = cg.refdef.vieworg;
   angles = cg.refdefViewAngles;

   // if dead, fix the angle and don't add any kick
   if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
      angles[ROLL] = 40;
      angles[PITCH] = -15;
      angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
      origin[2] += cg.predictedPlayerState.viewheight;
      return;
   }

   // add angles based on weapon kick
   VectorAdd (angles, cg.kick_angles, angles);

   // add angles based on damage kick
   if ( cg.damageTime ) {
      ratio = cg.time - cg.damageTime;
      if ( ratio < DAMAGE_DEFLECT_TIME ) {
         ratio /= DAMAGE_DEFLECT_TIME;
         angles[PITCH] += ratio * cg.v_dmg_pitch;
         angles[ROLL] += ratio * cg.v_dmg_roll;
      } else {
         ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
         if ( ratio > 0 ) {
            angles[PITCH] += ratio * cg.v_dmg_pitch;
            angles[ROLL] += ratio * cg.v_dmg_roll;
         }
      }
   }

   // add angles based on velocity
   VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );

   delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
   angles[PITCH] += delta * cg_runpitch.value;
   
   delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
   angles[ROLL] -= delta * cg_runroll.value;

   // add angles based on bob

   // make sure the bob is visible even at low speeds
   speed = cg.xyspeed > 200 ? cg.xyspeed : 200;

   delta = cg.bobfracsin * cg_bobpitch.value * speed;
   if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
      delta *= 3;      // crouching
   angles[PITCH] += delta;
   delta = cg.bobfracsin * cg_bobroll.value * speed;
   if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
      delta *= 3;      // crouching accentuates roll
   if (cg.bobcycle & 1)
      delta = -delta;
   angles[ROLL] += delta;

//===================================

   // add view height
   origin[2] += cg.predictedPlayerState.viewheight;

   // smooth out duck height changes
   timeDelta = cg.time - cg.duckTime;
   if ( timeDelta < DUCK_TIME) {
      cg.refdef.vieworg[2] -= cg.duckChange
         * (DUCK_TIME - timeDelta) / DUCK_TIME;
   }

   // add bob height
   bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
   if (bob > 6) {
      bob = 6;
   }

   origin[2] += bob;
   
   // BFP - First person vis mode
   // pivot the eye based on a neck length
#if 1
   if ( cg_drawOwnModel.integer >= 1 ) {
#define   NECK_LENGTH      8
      vec3_t         forward, up;

      VectorClear( cg.refdefViewAngles );
      
      if ( CG_GetTagOrientationFromPlayerEntityParentModel( cent, parent, parentModel, "tag_head", &tagOrient ) ) {
         VectorCopy( tagOrient.origin, cg.refdef.vieworg );
         cg.refdef.vieworg[2] -= NECK_LENGTH;
         AngleVectors( cg.refdefViewAngles, forward, NULL, up );
         VectorMA( cg.refdef.vieworg, -1, forward, cg.refdef.vieworg );
         VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
      }
   }
#endif

   // add fall height
   delta = cg.time - cg.landTime;
   if ( delta < LAND_DEFLECT_TIME ) {
      f = delta / LAND_DEFLECT_TIME;
      cg.refdef.vieworg[2] += cg.landChange * f;
   } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
      delta -= LAND_DEFLECT_TIME;
      f = 1.0 - ( delta / LAND_RETURN_TIME );
      cg.refdef.vieworg[2] += cg.landChange * f;
   }

   // add step offset
   CG_StepOffset();

   // add kick offset

   VectorAdd (origin, cg.kick_origin, origin);
}




Top
                 
Quake3World.com | Forum Index | Programming Discussion


Post new topic Reply to topic


cron
Quake3World.com
© ZeniMax. Zenimax, QUAKE III ARENA, Id Software and associated trademarks are trademarks of the ZeniMax group of companies. All rights reserved.
This is an unofficial fan website without any affiliation with or endorsement by ZeniMax.
All views and opinions expressed are those of the author.