Page 1 of 2

Engine Side Footsteps

Posted: Mon Jan 04, 2010 8:18 pm
by r00k
description: quick and dirty footstep sounds engine tutorial.
---------------------------------------------------------------------------------
purpose: adds footstep sounds to local gameplay.
---------------------------------------------------------------------------------
future expansion: different sounds for different surfaces and different speeds.
---------------------------------------------------------------------------------
First thing we need to do is precache the sound effects internally in the engine.

Open up cl_tent.c

near the top of the file look for the other sound effects precaches
and add these under sfx_t *cl_sfx_r_exp3,

Code: Select all

sfx_t		*cl_sfx_step1;
sfx_t		*cl_sfx_step2;
sfx_t		*cl_sfx_step3;
sfx_t		*cl_sfx_step4;
scroll down and find void CL_InitTEnts (void)
and add these precaches to be initialized.

Code: Select all

	cl_sfx_step1		= S_PrecacheSound ("misc/step1.wav");	
	cl_sfx_step2		= S_PrecacheSound ("misc/step2.wav");	
	cl_sfx_step3		= S_PrecacheSound ("misc/step3.wav");		
	cl_sfx_step4		= S_PrecacheSound ("misc/step4.wav");	
NOTE: you are going to have to find your own .wav files for the sounds and put them in quake/id1/sound/misc

okay now open up cl_main.c

near the top around all the other cvar_t definitions add,

Code: Select all

cvar_t	cl_footsteps		= {"cl_footsteps", "0", true};
now look for CL_Init and register the variable,

Code: Select all

     Cvar_RegisterVariable (&cl_footsteps);	
Scroll back to the top of the file in cl_main.c and add this code block prior to CL_RelinkEntities,

Code: Select all

void CL_Footsteps(entity_t	*ent, int frame)
{
	extern	sfx_t		*cl_sfx_step1;
	extern	sfx_t		*cl_sfx_step2;
	extern	sfx_t		*cl_sfx_step3;
	extern	sfx_t		*cl_sfx_step4;

	if (ent->steptime > cl.time)//Timer to sync with player animations
		return;

	if (ent != &cl_entities[cl.viewentity])//check if it's our player
		return; //only play our OWN footsteps! no multiplayer hax!
	
	if (TruePointContents(ent->origin) != CONTENTS_EMPTY)// if in water etc.. no sound
		return;

	if (frame == 2 || frame == 5 ||	frame == 7 || frame == 10)//check for run frames
	{
		vec3_t	dest, forward, right,up;
		trace_t	trace;
		float f;
		int	i,e;
		entity_t *p;

		AngleVectors (ent->angles, forward, right, up);
		
		VectorMA (ent->origin, -32, up, dest);//trace down 
			
		memset (&trace, 0, sizeof(trace_t));

		trace.fraction = 1;

		SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, ent->origin, dest, &trace);

		if (trace.fraction == 1) //if we didnt hit anything solid  dont make a sound
		{			
        		ent->steptime = cl.time + 0.1;//since the player model animates at 10 frames per sec, no need to come back here until time + 1/10...
			return;
		}
		
		f = (rand()%4)+1;

		e = (int)cl.viewentity;//emit the sound at our location
		
		if (f == 1)
			S_StartSound(e, 0, cl_sfx_step1, ent->origin, 0.40f, 1.0f);
		else if (f == 2)
			S_StartSound(e, 0, cl_sfx_step2, ent->origin, 0.40f, 1.0f);
		else if (f == 3)
			S_StartSound(e, 0, cl_sfx_step3, ent->origin, 0.40f, 1.0f);
		else
			S_StartSound(e, 0, cl_sfx_step4, ent->origin, 0.40f, 1.0f);
		
		ent->steptime = cl.time + 0.3;
	}
}
now scroll down into CL_RelinkEntites and add this bit of code before ent->forcelink = true;

Code: Select all

		if (cl_footsteps.value)
			CL_Footsteps(ent,ent->frame);
one last thing we need to do is add a timer to make the sound, so open up render.h and find the entity_s definition and add
to the end, like this

Code: Select all

	double	steptime;
} entity_t;

Posted: Mon Jan 04, 2010 8:40 pm
by Team Xlink
This is a very good tutorial and has much use to avoid tricky qc code. It allows you to play have sounds for footsteps in all mods. Thank you.

I am still intrigued by your mention of the future expansion's different footsteps for different surfaces, this would be great.

For example your running in a shallow puddle of water so you want to hear the small splash sounds, then you come up to a middle surface it then plays the ting sounds, so on and so fourth.

EDIT: Does anyone have any good sound files for this? My sound files aren't that good.

Posted: Mon Jan 04, 2010 9:56 pm
by Downsider
I'm pretty sure that when you trace against a face in the engine you can grab its texture name directly from the information returned, which could easily be your solution to getting surface-specific sounds.

Re: Engine Side Footsteps

Posted: Mon Jan 04, 2010 9:58 pm
by Baker
Is there a way to query what texture a player is walking on (Surface flags) like in Half-Life?

For instance, to play a different wav if the texture begins with "stone_" or "metal_".

Posted: Mon Jan 04, 2010 10:23 pm
by goldenboy
Some of the multiplayer types argue that footsteps are bad, or cheating.

Also, mods will want to use their own footstep sounds, plus monster footsteps, like RemakeQuake does.

It's not really hard to do in QC - I think this should be left to individual mods. See RMQ source.

I would get pissed if an engine "overwrote" my carefully selected and randomized footstep sounds.

Edit: It would be nice, however, if the engine provided surface detection which could then be used by the footstep QC code.

Posted: Mon Jan 04, 2010 10:50 pm
by r00k
Actually this doesnt provide a hack for multiplayer at all because it only plays your own footsteps. Also this shouldnt piss off modders cause it doesnt change monsters footsteps and the player footstep sounds can be copied to /sounds/misc. I think i would map out areas based on the origin, like how .locs are used. have an .fsm (footstep materail) file defined. That way at runtime im just comparing location to array not traceline collision detection.

It can be expanded to have custom monster sounds. The only real benefit of this is adding footsteps to mods that dont have them.

I think you are in your right that if your mod supports footsteps there should be a SVC_ trigger or ruleset or atleast you can stuffcmd to the client "cl_footstep 0\n", that way it will use your media/code instead.

Re: Engine Side Footsteps

Posted: Mon Jan 04, 2010 10:54 pm
by mh
Baker wrote:Is there a way to query what texture a player is walking on (Surface flags) like in Half-Life?

For instance, to play a different wav if the texture begins with "stone_" or "metal_".
R_LightPoint will give you it. :D

I'm in two minds though about doing this via QC in preference to the engine. A part of me wants to agree bigtime, but another part of me says that the engine has access to so much more info than QC does, and that doing it via the engine gives players the option to switch footsteps off by a cvar if they find them annoying. And players are the most important thing at the end of the day, aren't they?

I reckon the engine wins this one so far as I'm concerned. Perhaps a good meet-in-the-middle option might be to make this footstep code extensible via QC (it would need to move to server-side for that, but shouldn't need a new protocol as the standard server-side sound spawning should work fine).

Say let QC control the volume, the sounds used, etc. Provide some new builtins and we're done.

Posted: Tue Jan 05, 2010 1:41 am
by goldenboy
Yes, QC / the mod needs to control the sounds used and how they are used. There can be default sounds that the engine would supply, but as soon as a mod brings its own sound files along, control should shift to the mod.

RMQ uses much more than 4 footstep sound files, too. So that would be a possible clash. Requiring mods to provide exactly n footstep sounds is problematic. The engine should only offer an interface to the mod.

An engine that played the crappy hipnotic footsteps instead of the RMQ ones would make me want to shoot somebody.

Finally, disabling footstep sounds should be done by the mod, too, possibly via an impulse.

Posted: Fri Jan 08, 2010 4:18 pm
by Teiman
Telejano has footsteps builtin, I even have different sounds for different mosters.

Is a nice feature, and really adds to the game.

Posted: Fri Jan 08, 2010 4:20 pm
by mh
goldenboy wrote:Finally, disabling footstep sounds should be done by the mod, too, possibly via an impulse.
So long as it doesn't override what the player wants I'd agree.

Re: Engine Side Footsteps

Posted: Sun May 04, 2014 5:51 pm
by drm_wayne
mh wrote:
Baker wrote:Is there a way to query what texture a player is walking on (Surface flags) like in Half-Life?

For instance, to play a different wav if the texture begins with "stone_" or "metal_".
R_LightPoint will give you it. :D

Sorry for bumping this, but i adapted it to my engine and i wonder how exactly "R_LightPoint" will work gettign the texture name??
Can somebody explain it?

Re: Engine Side Footsteps

Posted: Sun May 04, 2014 10:34 pm
by mh
drm_wayne wrote:
mh wrote:
Baker wrote:Is there a way to query what texture a player is walking on (Surface flags) like in Half-Life?

For instance, to play a different wav if the texture begins with "stone_" or "metal_".
R_LightPoint will give you it. :D

Sorry for bumping this, but i adapted it to my engine and i wonder how exactly "R_LightPoint" will work gettign the texture name??
Can somebody explain it?
R_LightPoint will get you the lighting for an object from the msurface_t under it. It also gets you the lightspot (a position in 3D space where the trace intersects the surface) and the plane, which are used for Quake's crap planar shadows. It's a small enough thing to also save out surf->texinfo->texture.

The object may not be in direct contact with the surface but for the player you can then use cl.onground to determine if it is (you may also need cl.inwater, it's been a while). If you want footsteps on monsters you could put something together from the object's bounding box and the distance between the entity origin and the saved-out lightspot (monsters don't send SU_ONGROUND or SU_INWATER). Beware of MDL bounding boxes here: they need to be fixed in the engine to make this work too (if you do it server-side you'll get access to a lot more info, of course, but you'll need to implement a server-side version of R_LightPoint too). You'd also need to hack something up for if the object is standing on a brush model, and again for BSP models. It's up to you to decide at what point this crosses the line between "a technically interesting challenge" and "a gigantic pain in the ass".

Quake doesn't have surface flags so you need to hack something from the texture name if you want different sounds for metal, wood, grass, etc. That will break down if one of those wacky modders decides to use texture names like "dkfgjbakshdv" (and then pulls a fit of passive-agressive whining and "I can't understand why we can't do it if HL2 can" on you when you try to point this out). There's nothing you can do about that, so it's better to just accept that your chosen technology can't do what you want to do and use a sound that's tweaked to sound not-particularly-dreadful with everything. Or else choose a different technology that can.

Re: Engine Side Footsteps

Posted: Mon May 05, 2014 7:05 pm
by goldenboy
Hi mh :)

My evil hacky modder idea how to do surface dependant footsteps in a Quake mod has to do with simply placing triggers on top of every walkable surface.

A hack? Sure! Workable? Totally! Next problem!

Re: Engine Side Footsteps

Posted: Mon May 05, 2014 10:12 pm
by r00k
i have already implemented this 90%...

Code: Select all


char *nodename = "                 ";

int RecursiveNodePoint (mnode_t *node, vec3_t start, vec3_t end)
{
	float	front, back, frac;
	vec3_t	mid;
	void DrawGLPolyHighlight(glpoly_t *p);
	
loc0:
	if (node->contents < 0)
		return false;		// didn't hit anything
	
// calculate mid point
	if (node->plane->type < 3)
	{
		front = start[node->plane->type] - node->plane->dist;
		back = end[node->plane->type] - node->plane->dist;
	}
	else
	{
		front = DotProduct(start, node->plane->normal) - node->plane->dist;
		back = DotProduct(end, node->plane->normal) - node->plane->dist;
	}

	// optimized recursion
	if ((back < 0) == (front < 0))
	{
		node = node->children[front < 0];
		goto loc0;
	}
	
	frac = front / (front-back);
	mid[0] = start[0] + (end[0] - start[0]) * frac;
	mid[1] = start[1] + (end[1] - start[1]) * frac;
	mid[2] = start[2] + (end[2] - start[2]) * frac;
	
// go down front side
	if (RecursiveNodePoint(node->children[front < 0], start, mid))
	{		
		return true;	// hit something
	}
	else
	{
		int		i, ds, dt;
		msurface_t	*surf;

		surf = cl.worldmodel->surfaces + node->firstsurface;
		for (i = 0 ; i < node->numsurfaces ; i++, surf++)
		{
			ds = (int)((float)DotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
			dt = (int)((float)DotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);

			if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
				continue;
			
			ds -= surf->texturemins[0];
			dt -= surf->texturemins[1];
			
			if (ds > surf->extents[0] || dt > surf->extents[1])
				continue;
	
			if (developer.value)
				DrawGLPolyHighlight(surf->polys);

			nodename = (surf->texinfo->texture->name);
			return true;
		}

	// go down back side		
		return RecursiveNodePoint (node->children[front >= 0], mid, end);
	}
}

void GetNodePoint (vec3_t p, vec3_t	end)
{
	RecursiveNodePoint (cl.worldmodel->nodes, p, end);	
	SCR_CenterPrint (nodename);	
}
and then i just call GetNodePoint in the footstep code to get the name of the texture im standing on. I havent completed this but it would just parse the texturename and point it to a precached sound file.
But as MH says, texturenames do not have any strict syntax. Then there is the issues with brush models.

BTW MH, compiling the Jan 2012 RMQ SDK renders missing polygons on world surfaces :(

Re: Engine Side Footsteps

Posted: Tue May 06, 2014 12:07 am
by qbism
goldenboy wrote:My evil hacky modder idea how to do surface dependant footsteps in a Quake mod has to do with simply placing triggers on top of every walkable surface.
Wasn't there a mod that used a brute-force texture list file (read with frikfile) to determine footstep sounds? If mostly new textures, could encode 'surface flags' in the texture names to be decoded by qc. However, much could be done with audio triggers... put them off ledges to play "whoosh" or "oh shizzle!!" when player falls.