Beispiel #1
0
entity_t createEntity(const char * name){
	entity_t newent;
	memset(&newent, 0, sizeof(entity_t));
	newent.type = 1;
	newent.name = malloc(strlen(name)+1); // todo maybe put this somewhere else...
	strcpy(newent.name, name);
/*
	int i;
	for(i = 0; i<3; i++){
		newent.pos[i]=0.0;
		newent.vel[i]=0.0;
		newent.angle[i]=0.0;
		newent.anglevel[i]=0.0;
	}

	newent.touch = 0;
	newent.think = 0;
	newent.spawn = 0;
	newent.nextthink = 0;
	newent.needsmatupdate = FALSE;
	newent.model = 0;
	newent.texturegroup = 0;
*/
	newent.scale = 1.0;
	Matrix4x4_CreateFromQuakeEntity(&newent.mat, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
//	entity_count++;
//	newent.spawncount = entity_count;
	return newent;
//todo
}
Beispiel #2
0
void R_Sky(void)
{
	Matrix4x4_CreateFromQuakeEntity(&skymatrix, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], 0, 0, 0, r_refdef.farclip * (0.5f / 16.0f));
	Matrix4x4_Invert_Simple(&skyinversematrix, &skymatrix);

	if (skyrendersphere)
	{
		// this does not modify depth buffer
		R_SkySphere();
	}
	else if (skyrenderbox)
	{
		// this does not modify depth buffer
		R_SkyBox();
	}
	/* this will be skyroom someday
	else
	{
		// this modifies the depth buffer so we have to clear it afterward
		//R_SkyRoom();
		// clear the depthbuffer that was used while rendering the skyroom
		//GL_Clear(GL_DEPTH_BUFFER_BIT);
	}
	*/
}
Beispiel #3
0
/**
 * Transforms a point by the inverse of the world-model matrix for the
 * specified entity.
 */
void R_TransformForEntity (const entity_t* e, const vec3_t in, vec3_t out)
{
	matrix4x4_t tmp, mat;

	Matrix4x4_CreateFromQuakeEntity(&tmp, e->origin[0], e->origin[1], e->origin[2], e->angles[0], e->angles[1],
			e->angles[2], e->getScaleX());

	Matrix4x4_Invert_Simple(&mat, &tmp);
	Matrix4x4_Transform(&mat, in, out);
}
Beispiel #4
0
trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, qboolean hitnetworkbrushmodels, qboolean hitnetworkplayers, int *hitnetworkentity, qboolean hitcsqcentities)
#endif
{
	vec3_t hullmins, hullmaxs;
	int i, bodysupercontents;
	int passedictprog;
	qboolean pointtrace;
	prvm_edict_t *traceowner, *touch;
	trace_t trace;
	// bounding box of entire move area
	vec3_t clipboxmins, clipboxmaxs;
	// size of the moving object
	vec3_t clipmins, clipmaxs;
	// size when clipping against monsters
	vec3_t clipmins2, clipmaxs2;
	// start and end origin of move
	vec3_t clipstart, clipend;
	// trace results
	trace_t cliptrace;
	// matrices to transform into/out of other entity's space
	matrix4x4_t matrix, imatrix;
	// model of other entity
	dp_model_t *model;
	// list of entities to test for collisions
	int numtouchedicts;
	static prvm_edict_t *touchedicts[MAX_EDICTS];
#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
	vec3_t end;
	vec_t len = 0;

	if (VectorCompare(mins, maxs))
	{
		vec3_t shiftstart, shiftend;
		VectorAdd(start, mins, shiftstart);
		VectorAdd(pEnd, mins, shiftend);
		if (VectorCompare(start, pEnd))
			trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
		else
			trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities, false);
		VectorSubtract(trace.endpos, mins, trace.endpos);
		return trace;
	}

	if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
	{
		// TRICK: make the trace 1 qu longer!
		VectorSubtract(pEnd, start, end);
		len = VectorNormalizeLength(end);
		VectorMA(pEnd, collision_endposnudge.value, end, end);
	}
	else
		VectorCopy(pEnd, end);
#else
	if (VectorCompare(mins, maxs))
	{
		vec3_t shiftstart, shiftend;
		VectorAdd(start, mins, shiftstart);
		VectorAdd(end, mins, shiftend);
		if (VectorCompare(start, end))
			trace = CL_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
		else
			trace = CL_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, hitnetworkbrushmodels, hitnetworkplayers, hitnetworkentity, hitcsqcentities);
		VectorSubtract(trace.endpos, mins, trace.endpos);
		return trace;
	}
#endif

	if (hitnetworkentity)
		*hitnetworkentity = 0;

	VectorCopy(start, clipstart);
	VectorCopy(end, clipend);
	VectorCopy(mins, clipmins);
	VectorCopy(maxs, clipmaxs);
	VectorCopy(mins, clipmins2);
	VectorCopy(maxs, clipmaxs2);
#if COLLISIONPARANOID >= 3
	Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
#endif

	// clip to world
	Collision_ClipToWorld(&cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
	cliptrace.bmodelstartsolid = cliptrace.startsolid;
	if (cliptrace.startsolid || cliptrace.fraction < 1)
		cliptrace.ent = prog ? prog->edicts : NULL;
	if (type == MOVE_WORLDONLY)
		goto finished;

	if (type == MOVE_MISSILE)
	{
		// LordHavoc: modified this, was = -15, now -= 15
		for (i = 0;i < 3;i++)
		{
			clipmins2[i] -= 15;
			clipmaxs2[i] += 15;
		}
	}

	// get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
	if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize)
		cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
	else
	{
		VectorCopy(clipmins, hullmins);
		VectorCopy(clipmaxs, hullmaxs);
	}

	// create the bounding box of the entire move
	for (i = 0;i < 3;i++)
	{
		clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
		clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
	}

	// debug override to test against everything
	if (sv_debugmove.integer)
	{
		clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
		clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
	}

	// if the passedict is world, make it NULL (to avoid two checks each time)
	// this checks prog because this function is often called without a CSQC
	// VM context
	if (prog == NULL || passedict == prog->edicts)
		passedict = NULL;
	// precalculate prog value for passedict for comparisons
	passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
	// figure out whether this is a point trace for comparisons
	pointtrace = VectorCompare(clipmins, clipmaxs);
	// precalculate passedict's owner edict pointer for comparisons
	traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.client->owner) : NULL;

	// collide against network entities
	if (hitnetworkbrushmodels)
	{
		for (i = 0;i < cl.num_brushmodel_entities;i++)
		{
			entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
			if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
				continue;
			Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask);
			if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
				*hitnetworkentity = cl.brushmodel_entities[i];
			Collision_CombineTraces(&cliptrace, &trace, NULL, true);
		}
	}

	// collide against player entities
	if (hitnetworkplayers)
	{
		vec3_t origin, entmins, entmaxs;
		matrix4x4_t entmatrix, entinversematrix;

		if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
		{
			// don't hit network players, if we are a nonsolid player
			if(cl.scores[cl.playerentity-1].frags == -666 || cl.scores[cl.playerentity-1].frags == -616)
				goto skipnetworkplayers;
		}

		for (i = 1;i <= cl.maxclients;i++)
		{
			entity_render_t *ent = &cl.entities[i].render;

			// don't hit ourselves
			if (i == cl.playerentity)
				continue;

			// don't hit players that don't exist
			if (!cl.scores[i-1].name[0])
				continue;

			if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
			{
				// don't hit spectators or nonsolid players
				if(cl.scores[i-1].frags == -666 || cl.scores[i-1].frags == -616)
					continue;
			}

			Matrix4x4_OriginFromMatrix(&ent->matrix, origin);
			VectorAdd(origin, cl.playerstandmins, entmins);
			VectorAdd(origin, cl.playerstandmaxs, entmaxs);
			if (!BoxesOverlap(clipboxmins, clipboxmaxs, entmins, entmaxs))
				continue;
			Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
			Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
			Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask);
			if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
				*hitnetworkentity = i;
			Collision_CombineTraces(&cliptrace, &trace, NULL, false);
		}

skipnetworkplayers:
		;
	}

	// clip to entities
	// because this uses World_EntitiestoBox, we know all entity boxes overlap
	// the clip region, so we can skip culling checks in the loop below
	// note: if prog is NULL then there won't be any linked entities
	numtouchedicts = 0;
	if (hitcsqcentities && prog != NULL)
	{
		numtouchedicts = World_EntitiesInBox(&cl.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
		if (numtouchedicts > MAX_EDICTS)
		{
			// this never happens
			Con_Printf("CL_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
			numtouchedicts = MAX_EDICTS;
		}
	}
	for (i = 0;i < numtouchedicts;i++)
	{
		touch = touchedicts[i];

		if (touch->fields.client->solid < SOLID_BBOX)
			continue;
		if (type == MOVE_NOMONSTERS && touch->fields.client->solid != SOLID_BSP)
			continue;

		if (passedict)
		{
			// don't clip against self
			if (passedict == touch)
				continue;
			// don't clip owned entities against owner
			if (traceowner == touch)
				continue;
			// don't clip owner against owned entities
			if (passedictprog == touch->fields.client->owner)
				continue;
			// don't clip points against points (they can't collide)
			if (pointtrace && VectorCompare(touch->fields.client->mins, touch->fields.client->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.client->flags & FL_MONSTER)))
				continue;
		}

		bodysupercontents = touch->fields.client->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;

		// might interact, so do an exact clip
		model = NULL;
		if ((int) touch->fields.client->solid == SOLID_BSP || type == MOVE_HITMODEL)
			model = CL_GetModelFromEdict(touch);
		if (model)
			Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2], touch->fields.client->angles[0], touch->fields.client->angles[1], touch->fields.client->angles[2], 1);
		else
			Matrix4x4_CreateTranslate(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2]);
		Matrix4x4_Invert_Simple(&imatrix, &matrix);
		if ((int)touch->fields.client->flags & FL_MONSTER)
			Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
		else
			Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);

		if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
			*hitnetworkentity = 0;
		Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.client->solid == SOLID_BSP);
	}

finished:
#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
	if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
		Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
#endif
	return cliptrace;
}
Beispiel #5
0
/*
 * State:
 *   cl.bob2_smooth
 *   cl.bobfall_speed
 *   cl.bobfall_swing
 *   cl.gunangles_adjustment_highpass
 *   cl.gunangles_adjustment_lowpass
 *   cl.gunangles_highpass
 *   cl.gunangles_prev
 *   cl.gunorg_adjustment_highpass
 *   cl.gunorg_adjustment_lowpass
 *   cl.gunorg_highpass
 *   cl.gunorg_prev
 *   cl.hitgroundtime
 *   cl.lastongroundtime
 *   cl.oldongrounbd
 *   cl.stairsmoothtime
 *   cl.stairsmoothz
 *   cl.calcrefdef_prevtime
 * Extra input:
 *   cl.movecmd[0].time
 *   cl.movevars_stepheight
 *   cl.movevars_timescale
 *   cl.oldtime
 *   cl.punchangle
 *   cl.punchvector
 *   cl.qw_intermission_angles
 *   cl.qw_intermission_origin
 *   cl.qw_weaponkick
 *   cls.protocol
 *   cl.time
 * Output:
 *   cl.csqc_viewanglesfromengine
 *   cl.csqc_viewmodelmatrixfromengine
 *   cl.csqc_vieworiginfromengine
 *   r_refdef.view.matrix
 *   viewmodelmatrix_nobob
 *   viewmodelmatrix_withbob
 */
void V_CalcRefdefUsing (const matrix4x4_t *entrendermatrix, const vec3_t clviewangles, qboolean teleported, qboolean clonground, qboolean clcmdjump, float clstatsviewheight, qboolean cldead, qboolean clintermission, const vec3_t clvelocity)
{
	float vieworg[3], viewangles[3], smoothtime;
	float gunorg[3], gunangles[3];
	matrix4x4_t tmpmatrix;
	
	static float viewheightavg;
	float viewheight;	
#if 0
// begin of chase camera bounding box size for proper collisions by Alexander Zubov
	vec3_t camboxmins = {-3, -3, -3};
	vec3_t camboxmaxs = {3, 3, 3};
// end of chase camera bounding box size for proper collisions by Alexander Zubov
#endif
	trace_t trace;

	// react to clonground state changes (for gun bob)
	if (clonground)
	{
		if (!cl.oldonground)
			cl.hitgroundtime = cl.movecmd[0].time;
		cl.lastongroundtime = cl.movecmd[0].time;
	}
	cl.oldonground = clonground;
	cl.calcrefdef_prevtime = max(cl.calcrefdef_prevtime, cl.oldtime);

	VectorClear(gunorg);
	viewmodelmatrix_nobob = identitymatrix;
	viewmodelmatrix_withbob = identitymatrix;
	r_refdef.view.matrix = identitymatrix;

	// player can look around, so take the origin from the entity,
	// and the angles from the input system
	Matrix4x4_OriginFromMatrix(entrendermatrix, vieworg);
	VectorCopy(clviewangles, viewangles);

	// calculate how much time has passed since the last V_CalcRefdef
	smoothtime = bound(0, cl.time - cl.stairsmoothtime, 0.1);
	cl.stairsmoothtime = cl.time;

	// fade damage flash
	if (v_dmg_time > 0)
		v_dmg_time -= bound(0, smoothtime, 0.1);

	if (clintermission)
	{
		// entity is a fixed camera, just copy the matrix
		if (cls.protocol == PROTOCOL_QUAKEWORLD)
			Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, cl.qw_intermission_origin[0], cl.qw_intermission_origin[1], cl.qw_intermission_origin[2], cl.qw_intermission_angles[0], cl.qw_intermission_angles[1], cl.qw_intermission_angles[2], 1);
		else
		{
			r_refdef.view.matrix = *entrendermatrix;
			Matrix4x4_AdjustOrigin(&r_refdef.view.matrix, 0, 0, clstatsviewheight);
		}
		Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
		Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);
		Matrix4x4_Copy(&viewmodelmatrix_withbob, &viewmodelmatrix_nobob);

		VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
		VectorCopy(viewangles, cl.csqc_viewanglesfromengine);

		Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
		Matrix4x4_CreateScale(&cl.csqc_viewmodelmatrixfromengine, cl_viewmodel_scale.value);
	}
	else
	{
		// smooth stair stepping, but only if clonground and enabled
		if (!clonground || cl_stairsmoothspeed.value <= 0 || teleported)
			cl.stairsmoothz = vieworg[2];
		else
		{
			if (cl.stairsmoothz < vieworg[2])
				vieworg[2] = cl.stairsmoothz = bound(vieworg[2] - cl.movevars_stepheight, cl.stairsmoothz + smoothtime * cl_stairsmoothspeed.value, vieworg[2]);
			else if (cl.stairsmoothz > vieworg[2])
				vieworg[2] = cl.stairsmoothz = bound(vieworg[2], cl.stairsmoothz - smoothtime * cl_stairsmoothspeed.value, vieworg[2] + cl.movevars_stepheight);
		}

		// apply qw weapon recoil effect (this did not work in QW)
		// TODO: add a cvar to disable this
		viewangles[PITCH] += cl.qw_weaponkick;

		// apply the viewofs (even if chasecam is used)
		// Samual: Lets add smoothing for this too so that things like crouching are done with a transition.
		viewheight = bound(0, (cl.time - cl.calcrefdef_prevtime) / max(0.0001, cl_smoothviewheight.value), 1);
		viewheightavg = viewheightavg * (1 - viewheight) + clstatsviewheight * viewheight;
		vieworg[2] += viewheightavg;

		if (chase_active.value)
		{
			// observing entity from third person. Added "campitch" by Alexander "motorsep" Zubov
			vec_t camback, camup, dist, campitch, forward[3], chase_dest[3];

			camback = chase_back.value;
			camup = chase_up.value;
			campitch = chase_pitchangle.value;

			AngleVectors(viewangles, forward, NULL, NULL);

			if (chase_overhead.integer)
			{
#if 1
				vec3_t offset;
				vec3_t bestvieworg;
#endif
				vec3_t up;
				viewangles[PITCH] = 0;
				AngleVectors(viewangles, forward, NULL, up);
				// trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
				chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup;
				chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup;
				chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup;
#if 0
#if 1
				//trace = CL_TraceLine(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
				trace = CL_TraceLine(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
#else
				//trace = CL_TraceBox(vieworg, eyeboxmins, eyeboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
				trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
#endif
				VectorCopy(trace.endpos, vieworg);
				vieworg[2] -= 8;
#else
				// trace from first person view location to our chosen third person view location
#if 1
				trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true);
#else
				trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
#endif
				VectorCopy(trace.endpos, bestvieworg);
				offset[2] = 0;
				for (offset[0] = -16;offset[0] <= 16;offset[0] += 8)
				{
					for (offset[1] = -16;offset[1] <= 16;offset[1] += 8)
					{
						AngleVectors(viewangles, NULL, NULL, up);
						chase_dest[0] = vieworg[0] - forward[0] * camback + up[0] * camup + offset[0];
						chase_dest[1] = vieworg[1] - forward[1] * camback + up[1] * camup + offset[1];
						chase_dest[2] = vieworg[2] - forward[2] * camback + up[2] * camup + offset[2];
#if 1
						trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true);
#else
						trace = CL_TraceBox(vieworg, camboxmins, camboxmaxs, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false);
#endif
						if (bestvieworg[2] > trace.endpos[2])
							bestvieworg[2] = trace.endpos[2];
					}
				}
				bestvieworg[2] -= 8;
				VectorCopy(bestvieworg, vieworg);
#endif
				viewangles[PITCH] = campitch;
			}
			else
			{
				if (gamemode == GAME_GOODVSBAD2 && chase_stevie.integer)
				{
					// look straight down from high above
					viewangles[PITCH] = 90;
					camback = 2048;
					VectorSet(forward, 0, 0, -1);
				}

				// trace a little further so it hits a surface more consistently (to avoid 'snapping' on the edge of the range)
				dist = -camback - 8;
				chase_dest[0] = vieworg[0] + forward[0] * dist;
				chase_dest[1] = vieworg[1] + forward[1] * dist;
				chase_dest[2] = vieworg[2] + forward[2] * dist + camup;
				trace = CL_TraceLine(vieworg, chase_dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_SKY, true, false, NULL, false, true);
				VectorMAMAM(1, trace.endpos, 8, forward, 4, trace.plane.normal, vieworg);
			}
		}
		else
		{
			// first person view from entity
			// angles
			if (cldead && v_deathtilt.integer)
				viewangles[ROLL] = v_deathtiltangle.value;

			if (cl_weaponrecoil.integer > 0)
				VectorAdd(viewangles, cl.punchangle, viewangles);
			viewangles[ROLL] += V_CalcRoll(clviewangles, clvelocity);
			if (v_dmg_time > 0)
			{
				viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
				viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
			}
			// origin
			if (cl_weaponrecoil.integer > 0)
            	VectorAdd(vieworg, cl.punchvector, vieworg);
			if (!cldead)
			{
				double xyspeed, bob, bobfall;
				float cycle;
				vec_t frametime;

				frametime = (cl.time - cl.calcrefdef_prevtime) * cl.movevars_timescale;

				// 1. if we teleported, clear the frametime... the lowpass will recover the previous value then
				if(teleported)
				{
					// try to fix the first highpass; result is NOT
					// perfect! TODO find a better fix
					VectorCopy(viewangles, cl.gunangles_prev);
					VectorCopy(vieworg, cl.gunorg_prev);
				}

				// 2. for the gun origin, only keep the high frequency (non-DC) parts, which is "somewhat like velocity"
				VectorAdd(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);
				highpass3_limited(vieworg, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_side_highpass1.value, cl_followmodel_side_limit.value, frametime*cl_followmodel_up_highpass1.value, cl_followmodel_up_limit.value, cl.gunorg_highpass, gunorg);
				VectorCopy(vieworg, cl.gunorg_prev);
				VectorSubtract(cl.gunorg_highpass, cl.gunorg_prev, cl.gunorg_highpass);

				// in the highpass, we _store_ the DIFFERENCE to the actual view angles...
				VectorAdd(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);
				cl.gunangles_highpass[PITCH] += 360 * floor((viewangles[PITCH] - cl.gunangles_highpass[PITCH]) / 360 + 0.5);
				cl.gunangles_highpass[YAW] += 360 * floor((viewangles[YAW] - cl.gunangles_highpass[YAW]) / 360 + 0.5);
				cl.gunangles_highpass[ROLL] += 360 * floor((viewangles[ROLL] - cl.gunangles_highpass[ROLL]) / 360 + 0.5);
				highpass3_limited(viewangles, frametime*cl_leanmodel_up_highpass1.value, cl_leanmodel_up_limit.value, frametime*cl_leanmodel_side_highpass1.value, cl_leanmodel_side_limit.value, 0, 0, cl.gunangles_highpass, gunangles);
				VectorCopy(viewangles, cl.gunangles_prev);
				VectorSubtract(cl.gunangles_highpass, cl.gunangles_prev, cl.gunangles_highpass);

				// 3. calculate the RAW adjustment vectors
				gunorg[0] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0);
				gunorg[1] *= (cl_followmodel.value ? -cl_followmodel_side_speed.value : 0);
				gunorg[2] *= (cl_followmodel.value ? -cl_followmodel_up_speed.value : 0);

				gunangles[PITCH] *= (cl_leanmodel.value ? -cl_leanmodel_up_speed.value : 0);
				gunangles[YAW] *= (cl_leanmodel.value ? -cl_leanmodel_side_speed.value : 0);
				gunangles[ROLL] = 0;

				// 4. perform highpass/lowpass on the adjustment vectors (turning velocity into acceleration!)
				//    trick: we must do the lowpass LAST, so the lowpass vector IS the final vector!
				highpass3(gunorg, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_side_highpass.value, frametime*cl_followmodel_up_highpass.value, cl.gunorg_adjustment_highpass, gunorg);
				lowpass3(gunorg, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_side_lowpass.value, frametime*cl_followmodel_up_lowpass.value, cl.gunorg_adjustment_lowpass, gunorg);
				// we assume here: PITCH = 0, YAW = 1, ROLL = 2
				highpass3(gunangles, frametime*cl_leanmodel_up_highpass.value, frametime*cl_leanmodel_side_highpass.value, 0, cl.gunangles_adjustment_highpass, gunangles);
				lowpass3(gunangles, frametime*cl_leanmodel_up_lowpass.value, frametime*cl_leanmodel_side_lowpass.value, 0, cl.gunangles_adjustment_lowpass, gunangles);

				// 5. use the adjusted vectors
				VectorAdd(vieworg, gunorg, gunorg);
				VectorAdd(viewangles, gunangles, gunangles);

				// bounded XY speed, used by several effects below
				xyspeed = bound (0, sqrt(clvelocity[0]*clvelocity[0] + clvelocity[1]*clvelocity[1]), 400);

				// vertical view bobbing code
				if (cl_bob.value && cl_bobcycle.value)
				{
					// LordHavoc: this code is *weird*, but not replacable (I think it
					// should be done in QC on the server, but oh well, quake is quake)
					// LordHavoc: figured out bobup: the time at which the sin is at 180
					// degrees (which allows lengthening or squishing the peak or valley)
					cycle = cl.time / cl_bobcycle.value;
					cycle -= (int) cycle;
					if (cycle < cl_bobup.value)
						cycle = sin(M_PI * cycle / cl_bobup.value);
					else
						cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
					// bob is proportional to velocity in the xy plane
					// (don't count Z, or jumping messes it up)
					bob = xyspeed * bound(0, cl_bob.value, 0.05);
					bob = bob*0.3 + bob*0.7*cycle;
					vieworg[2] += bob;
					// we also need to adjust gunorg, or this appears like pushing the gun!
					// In the old code, this was applied to vieworg BEFORE copying to gunorg,
					// but this is not viable with the new followmodel code as that would mean
					// that followmodel would work on the munged-by-bob vieworg and do feedback
					gunorg[2] += bob;
				}

				// horizontal view bobbing code
				if (cl_bob2.value && cl_bob2cycle.value)
				{
					vec3_t bob2vel;
					vec3_t forward, right, up;
					float side, front;

					cycle = cl.time / cl_bob2cycle.value;
					cycle -= (int) cycle;
					if (cycle < 0.5)
						cycle = cos(M_PI * cycle / 0.5); // cos looks better here with the other view bobbing using sin
					else
						cycle = cos(M_PI + M_PI * (cycle-0.5)/0.5);
					bob = bound(0, cl_bob2.value, 0.05) * cycle;

					// this value slowly decreases from 1 to 0 when we stop touching the ground.
					// The cycle is later multiplied with it so the view smooths back to normal
					if (clonground && !clcmdjump) // also block the effect while the jump button is pressed, to avoid twitches when bunny-hopping
						cl.bob2_smooth = 1;
					else
					{
						if(cl.bob2_smooth > 0)
							cl.bob2_smooth -= bound(0, cl_bob2smooth.value, 1);
						else
							cl.bob2_smooth = 0;
					}

					// calculate the front and side of the player between the X and Y axes
					AngleVectors(viewangles, forward, right, up);
					// now get the speed based on those angles. The bounds should match the same value as xyspeed's
					side = bound(-400, DotProduct (clvelocity, right) * cl.bob2_smooth, 400);
					front = bound(-400, DotProduct (clvelocity, forward) * cl.bob2_smooth, 400);
					VectorScale(forward, bob, forward);
					VectorScale(right, bob, right);
					// we use side with forward and front with right, so the bobbing goes
					// to the side when we walk forward and to the front when we strafe
					VectorMAMAM(side, forward, front, right, 0, up, bob2vel);
					vieworg[0] += bob2vel[0];
					vieworg[1] += bob2vel[1];
					// we also need to adjust gunorg, or this appears like pushing the gun!
					// In the old code, this was applied to vieworg BEFORE copying to gunorg,
					// but this is not viable with the new followmodel code as that would mean
					// that followmodel would work on the munged-by-bob vieworg and do feedback
					gunorg[0] += bob2vel[0];
					gunorg[1] += bob2vel[1];
				}

				// fall bobbing code
				// causes the view to swing down and back up when touching the ground
				if (cl_bobfall.value && cl_bobfallcycle.value)
				{
					if (!clonground)
					{
						cl.bobfall_speed = bound(-400, clvelocity[2], 0) * bound(0, cl_bobfall.value, 0.1);
						if (clvelocity[2] < -cl_bobfallminspeed.value)
							cl.bobfall_swing = 1;
						else
							cl.bobfall_swing = 0; // TODO really?
					}
					else
					{
						cl.bobfall_swing = max(0, cl.bobfall_swing - cl_bobfallcycle.value * frametime);

						bobfall = sin(M_PI * cl.bobfall_swing) * cl.bobfall_speed;
						vieworg[2] += bobfall;
						gunorg[2] += bobfall;
					}
				}

				// gun model bobbing code
				if (cl_bobmodel.value)
				{
					// calculate for swinging gun model
					// the gun bobs when running on the ground, but doesn't bob when you're in the air.
					// Sajt: I tried to smooth out the transitions between bob and no bob, which works
					// for the most part, but for some reason when you go through a message trigger or
					// pick up an item or anything like that it will momentarily jolt the gun.
					vec3_t forward, right, up;
					float bspeed;
					float s;
					float t;

					s = cl.time * cl_bobmodel_speed.value;
					if (clonground)
					{
						if (cl.time - cl.hitgroundtime < 0.2)
						{
							// just hit the ground, speed the bob back up over the next 0.2 seconds
							t = cl.time - cl.hitgroundtime;
							t = bound(0, t, 0.2);
							t *= 5;
						}
						else
							t = 1;
					}
					else
					{
						// recently left the ground, slow the bob down over the next 0.2 seconds
						t = cl.time - cl.lastongroundtime;
						t = 0.2 - bound(0, t, 0.2);
						t *= 5;
					}

					bspeed = xyspeed * 0.01f;
					AngleVectors (gunangles, forward, right, up);
					bob = bspeed * cl_bobmodel_side.value * cl_viewmodel_scale.value * sin (s) * t;
					VectorMA (gunorg, bob, right, gunorg);
					bob = bspeed * cl_bobmodel_up.value * cl_viewmodel_scale.value * cos (s * 2) * t;
					VectorMA (gunorg, bob, up, gunorg);
				}
			}
		}
		// calculate a view matrix for rendering the scene
		if (v_idlescale.value)
		{
			viewangles[0] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
			viewangles[1] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
			viewangles[2] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
		}
		Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2], 1);

		// calculate a viewmodel matrix for use in view-attached entities
		Matrix4x4_Copy(&viewmodelmatrix_nobob, &r_refdef.view.matrix);
		Matrix4x4_ConcatScale(&viewmodelmatrix_nobob, cl_viewmodel_scale.value);

		Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix_withbob, gunorg[0], gunorg[1], gunorg[2], gunangles[0], gunangles[1], gunangles[2], cl_viewmodel_scale.value);
		VectorCopy(vieworg, cl.csqc_vieworiginfromengine);
		VectorCopy(viewangles, cl.csqc_viewanglesfromengine);

		Matrix4x4_Invert_Simple(&tmpmatrix, &r_refdef.view.matrix);
		Matrix4x4_Concat(&cl.csqc_viewmodelmatrixfromengine, &tmpmatrix, &viewmodelmatrix_withbob);
	}

	cl.calcrefdef_prevtime = cl.time;
}