BFP camera code implementation

Locked
User avatar
LegendGuard
Posts: 121
Joined: Thu Jun 09, 2022 7:35 am

BFP camera code implementation

Post by LegendGuard »

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: Select all

/*
===============
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];
}
User avatar
LegendGuard
Posts: 121
Joined: Thu Jun 09, 2022 7:35 am

Re: BFP camera code implementation

Post by LegendGuard »

Finally, BFP third person camera implemented! Wooohoooohoho!!!
https://github.com/LegendaryGuard/BFP/c ... 220067e33d
User avatar
LegendGuard
Posts: 121
Joined: Thu Jun 09, 2022 7:35 am

Re: BFP camera code implementation

Post by LegendGuard »

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: Select all

#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 );
	}
}
User avatar
LegendGuard
Posts: 121
Joined: Thu Jun 09, 2022 7:35 am

Re: BFP camera code implementation

Post by LegendGuard »

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: Select all

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);
}
Locked