1 #include "pch.h"
2 #include "par.h"
3 #include "car.h"
4 #include "cardefs.h"
5 #include "configfile.h"
6 #include "collision_world.h"
7 #include "tracksurface.h"
8 #include "configfile.h"
9 #include "settings.h"
10 #include "../ogre/CGame.h"  // replay
11 #include "../ogre/CarModel.h"  // camera pos
12 #include "../ogre/FollowCamera.h"  // camera pos
13 #include "../ogre/common/Def_Str.h"
14 #include "../ogre/common/data/SceneXml.h"
15 #include "../ogre/common/CScene.h"
16 #include "../ogre/common/GraphView.h"
17 #include "../ogre/common/Axes.h"
18 #include "../network/protocol.hpp"
19 #include "tobullet.h"
20 #include "game.h"
21 #include "../ogre/SplitScreen.h"  // num plr
22 #include "../sound/SoundMgr.h"
23 #include "../sound/SoundBase.h"
24 #include "../sound/SoundBaseMgr.h"
25 #include <OgreCamera.h>
26 using namespace std;
27 using namespace Ogre;
28 
29 
30 //  Load
31 //--------------------------------------------------------------------------------------------------------------------------
LoadSounds(const std::string & carpath)32 bool CAR::LoadSounds(const std::string & carpath)
33 {
34 	Ogre::Timer ti;
35 	bool ss = pApp->pSet->game.local_players > 1;
36 	CARsounds& s = sounds;
37 
38 	SoundMgr* snd = pGame->snd;
39 	const string& eng = dynamics.engine.sound_name;
40 	s.engine = snd->createInstance(eng,0);  s.engine->set2D(ss);
41 	s.engine->setEngine(true);  s.engine->start();
42 
43 	int i;  float fw = numWheels;
44 	for (i = 0; i < numWheels; ++i)  // tires
45 	{
46 		s.asphalt[i] = snd->createInstance("asphalt", 0);	s.asphalt[i]->set2D(ss);
47 		s.grass[i]   = snd->createInstance("grass", 0);
48 		s.grass[i]->seek(float(i)/fw);  s.grass[i]->set2D(ss);
49 		s.gravel[i]  = snd->createInstance("gravel", 0);
50 		s.gravel[i]->seek(float(i)/fw);  s.gravel[i]->set2D(ss);
51 	}
52 	for (i = 0; i < numWheels; ++i)
53 	{
54 		s.bump[i] = snd->createInstance("bump"+toStr(i%4), 0);  s.bump[i]->set2D(ss);
55 		s.bump[i]->seek(float(i)/fw);
56 	}
57 
58 	for (i = 0; i < Ncrashsounds; ++i)  // crashes
59 	{	string cn = "crash/";  int n=i+1;  cn += toStr(n/10)+toStr(n%10);
60 		s.crash[i] = snd->createInstance(cn, 0);  s.crash[i]->set2D(ss);
61 	}
62 	s.scrap   = snd->createInstance("crash/scrap",  0);  s.scrap->set2D(ss);
63 	s.screech = snd->createInstance("crash/screech",0);  s.screech->set2D(ss);
64 
65 	s.wind  = snd->createInstance("wind",  0);  s.wind->set2D(ss);
66 	s.boost = snd->createInstance("boost", 0);  s.boost->set2D(ss);
67 
68 	for (i = 0; i < Nwatersounds; ++i)  // fluids
69 	{	s.water[i] = snd->createInstance("water"+toStr(i+1), 0);  s.water[i]->set2D(ss);  }
70 
71 	s.mud        = snd->createInstance("mud1", 0);        s.mud->set2D(ss);
72 	s.mud_cont   = snd->createInstance("mud_cont",   0);  s.mud_cont->set2D(ss);
73 	s.water_cont = snd->createInstance("water_cont", 0);  s.water_cont->set2D(ss);
74 
75 	LogO("::: Time car Sounds: "/*+carpath+" "*/+ fToStr(ti.getMilliseconds(),0,3) +" ms");
76 	return true;
77 }
78 
79 //  ctor
CARsounds()80 CAR::CARsounds::CARsounds()
81 	:fluidHitOld(0), whMudSpin(0.f)
82 {
83 	int i;
84 	crashtime.resize(Ncrashsounds);
85 	for (int i=0; i < Ncrashsounds; ++i)
86 		crashtime[i] = 0.f;
87 
88 	engine = 0;
89 	SetNumWheels(4);
90 	for (i = 0; i < 4; ++i)  // tires
91 	{	asphalt[i] = 0;  grass[i] = 0;  gravel[i] = 0;  bump[i] = 0;  }
92 
93 	crash.resize(Ncrashsounds);
94 	for (i = 0; i < Ncrashsounds; ++i)  // crashes
95 		crash[i] = 0;
96 
97 	scrap = 0;  screech = 0;
98 	wind = 0;  boost = 0;
99 
100 	water.resize(Nwatersounds);
101 	for (i = 0; i < Nwatersounds; ++i)  // fluids
102 		water[i] = 0;
103 
104 	mud = 0;  mud_cont = 0;  water_cont = 0;
105 }
106 
SetNumWheels(int n)107 void CAR::CARsounds::SetNumWheels(int n)
108 {
109 	asphalt.resize(n);  grass.resize(n);  gravel.resize(n);  bump.resize(n);
110 	bumptime.resize(n);  bumpvol.resize(n);
111 	for (int i=0; i < n; ++i)
112 	{	bumpvol[i]=0.f;  bumptime[i] = 5.f;  }
113 }
114 
Destroy()115 void CAR::CARsounds::Destroy()
116 {
117 	delete engine;
118 	int i;
119 	for (i = 0; i < gravel.size(); ++i)  // tires
120 	{
121 		delete asphalt[i];
122 		delete grass[i];
123 		delete gravel[i];
124 		delete bump[i];
125 	}
126 
127 	for (i = 0; i < Ncrashsounds; ++i)  // crashes
128 		delete crash[i];
129 
130 	delete scrap;  delete screech;
131 
132 	delete wind;  delete boost;
133 
134 	for (i = 0; i < Nwatersounds; ++i)  // fluids
135 		delete water[i];
136 
137 	delete mud;  delete mud_cont;  delete water_cont;
138 }
139 
140 
141 //--------------------------------------------------------------------------------------------------------------------------
UpdateSounds(float dt)142 void CAR::UpdateSounds(float dt)
143 {
144 	//  get data  //
145 	//  note: Damage is updated here
146 	bool bSound = !pGame->snd->isDisabled();
147 	CARsounds& s = sounds;
148 
149 	float rpm, throttle, speed, dynVel;  bool hitp = false;
150 	MATHVECTOR<float,3> pos, engPos, whPos[MAX_WHEELS], hitPos;  // car, engine, wheels pos
151 	QUATERNION<float> rot;
152 	TRACKSURFACE::TYPE surfType[MAX_WHEELS];
153 	float squeal[MAX_WHEELS],whVel[MAX_WHEELS], suspVel[MAX_WHEELS],suspDisp[MAX_WHEELS];
154 	float whH_all = 0.f;  bool mud = false;
155 	float fHitForce = 0.f, boostVal = 0.f, fCarScrap = 0.f, fCarScreech = 0.f;
156 
157 	bool dmg = pSet->game.damage_type > 0, reduced = pSet->game.damage_type==1;
158 	bool terminal = dynamics.fDamage >= 100.f;
159 	float fDmg = pApp->scn->sc->damageMul;
160 
161 	///  replay play  ------------------------------------------
162 	if (pApp->bRplPlay)
163 	{	dmg = false;
164 
165 		#ifdef DEBUG
166 		assert(id < pApp->frm.size());
167 		#endif
168 		const ReplayFrame2& fr = pApp->frm[id];
169 		pos = fr.pos;  rot = fr.rot;   rpm = fr.rpm;
170 		throttle = fr.throttle /255.f;  boostVal = fr.fboost /255.f;
171 		dynamics.fDamage = fr.damage /255.f*100.f;  //dmg read
172 
173 		MATHVECTOR<float,3> offset = dynamics.engine.GetPosition();
174 		rot.RotateVector(offset);
175 		engPos = offset + pos;
176 
177 		speed = fr.speed;  dynVel = fr.dynVel;
178 		s.whMudSpin = fr.get(b_fluid) ? fr.whMudSpin : 0.f;
179 
180 		if (fr.get(b_scrap))
181 		{
182 			const RScrap& sc = fr.scrap[0];
183 			fCarScrap = sc.fScrap;  fCarScreech = sc.fScreech;
184 		}
185 
186 		hitp = fr.get(b_hit);
187 		if (hitp)
188 		{
189 			const RHit& h = fr.hit[0];
190 			fHitForce = h.fHitForce;
191 			hitPos[0] = h.vHitPos.x;  hitPos[1] = -h.vHitPos.z;  hitPos[2] = h.vHitPos.y;
192 		}
193 
194 		int w, ww = fr.wheels.size();
195 		for (w=0; w < ww; ++w)
196 		{
197 			const RWheel& wh = fr.wheels[w];
198 			whPos[w] = wh.pos;
199 			surfType[w] = (TRACKSURFACE::TYPE)wh.surfType;
200 			squeal[w] = wh.squeal;  whVel[w] = wh.whVel;
201 			suspVel[w] = wh.suspVel;  suspDisp[w] = wh.suspDisp;
202 			//  fluids
203 			if (wh.whP >= 0)  // solid no snd
204 				whH_all += wh.whH;
205 			if (wh.whP >= 1)  mud = true;
206 		}
207 	}
208 	else  /// game  ------------------------------------------
209 	{
210 		pos = dynamics.GetPosition();  rot = dynamics.GetOrientation();
211 		rpm = GetEngineRPM();
212 		throttle = dynamics.GetThrottle();
213 		engPos = dynamics.GetEnginePosition();
214 		speed = GetSpeed();
215 		dynVel = dynamics.GetVelocity().Magnitude();
216 		fHitForce = dynamics.fHitForce;  hitp = true;
217 		hitPos[0] = dynamics.vHitPos.x;  hitPos[1] = -dynamics.vHitPos.z;  hitPos[2] = dynamics.vHitPos.y;
218 		boostVal = dynamics.boostVal;
219 
220 		for (int w=0; w < numWheels; ++w)
221 		{
222 			WHEEL_POSITION wp = WHEEL_POSITION(w);
223 			whPos[w] = dynamics.GetWheelPosition(wp);
224 
225 			const TRACKSURFACE* surface = dynamics.GetWheelContact(wp).GetSurfacePtr();
226 			surfType[w] = !surface ? TRACKSURFACE::NONE : surface->type;
227 			//  squeal
228 			squeal[w] = GetTireSquealAmount(wp);
229 			whVel[w] = dynamics.GetWheelVelocity(wp).Magnitude();
230 			//  susp
231 			suspVel[w] = dynamics.GetSuspension(wp).GetVelocity();
232 			suspDisp[w] = dynamics.GetSuspension(wp).GetDisplacementPercent();
233 			//  fluids
234 			if (dynamics.whP[w] >= 0)  // solid no snd
235 				whH_all += dynamics.whH[w];
236 			if (dynamics.whP[w] >= 1)  mud = true;
237 		}
238 
239 		//  wheels in mud, spinning intensity
240 		float mudSpin = 0.f;
241 		for (int w=0; w < numWheels; ++w)
242 		{
243 			float vel = std::abs(dynamics.wheel[w].GetAngularVelocity());
244 			if (vel <= 30.f)  continue;
245 			if (dynamics.whP[w] == 2)
246 				mudSpin += dynamics.whH[w] * std::min(80.f, 1.5f * vel) / 80.f;
247 			else if (dynamics.whP[w] == 1)
248 				mudSpin += dynamics.whH[w] * std::min(160.f, 3.f * vel) / 80.f;
249 		}
250 		s.whMudSpin = mudSpin * 0.5f;
251 
252 		//  car scrap, screech
253 		float gain = std::min(1.f, dynamics.fCarScrap);
254 		if (dynamics.fCarScrap > 0.f)
255 		{	dynamics.fCarScrap -= (-gain * 0.8f + 1.2f)* dt;
256 			if (dynamics.fCarScrap < 0.f)  dynamics.fCarScrap = 0.f;
257 		}
258 		fCarScrap = gain;
259 
260 		/// <><> Damage <><>
261 		if (dmg && !terminal)
262 			if (reduced)
263 				dynamics.fDamage += fDmg * fCarScrap * dt * dynamics.fHitDmgA * gPar.dmgFromScrap;
264 			else  // normal
265 				dynamics.fDamage += fDmg * fCarScrap * dt * dynamics.fHitDmgA * gPar.dmgFromScrap2;
266 
267 		gain = std::min(1.f, dynamics.fCarScreech);
268 		if (dynamics.fCarScreech > 0.f)
269 		{	dynamics.fCarScreech -= 3.f * dt;
270 			if (dynamics.fCarScreech < 0.f)  dynamics.fCarScreech = 0.f;
271 		}
272 		fCarScreech = gain;
273 	}
274 
275 	//  engine pos  //todo: vel..
276 	Vector3 ep, ev = Vector3::ZERO;
277 	ep = Axes::toOgre(engPos);
278 
279 
280 //))  update sounds
281 if (bSound)
282 {	/**/
283 
284 	///  engine  ====
285 	float gain = 1.f;
286 
287 	if (dynamics.vtype >= V_Spaceship)
288 	{
289 		s.engine->setPitch(1.f);
290 		gain = throttle;
291 	}else
292 	{	//  car
293 		gain = throttle * 0.5 + 0.5;
294 		s.engine->setPitch(rpm);
295 	}
296 	s.engine->setPosition(ep, ev);
297 	s.engine->setGain(gain * dynamics.engine_vol_mul * pSet->vol_engine);
298 
299 
300 	///  tires  oooo
301 	for (int i = 0; i < numWheels; ++i)
302 	{
303 		Vector3 wh;  wh = Axes::toOgre(whPos[i]);
304 
305 		float maxgain = 0.6f, pitchvar = 0.4f, pmul = 1.f;
306 
307 		Sound* snd = s.gravel[i];
308 		switch (surfType[i])
309 		{
310 		case TRACKSURFACE::ASPHALT:		snd = s.asphalt[i];  maxgain = 0.4f;  pitchvar = 0.40f;  pmul = 0.8f;  break;
311 		case TRACKSURFACE::GRASS:		snd = s.grass[i];    maxgain = 0.7f;  pitchvar = 0.25f;  break;
312 		case TRACKSURFACE::GRAVEL:		snd = s.gravel[i];   maxgain = 0.7f;  break;
313 		case TRACKSURFACE::CONCRETE:	snd = s.asphalt[i];  maxgain = 0.5f;  pitchvar = 0.25f;  pmul = 0.7f;  break;
314 		case TRACKSURFACE::SAND:		snd = s.grass[i];    maxgain = 0.5f;  pitchvar = 0.25f;  break;
315 		case TRACKSURFACE::NONE:
316 						default:		snd = s.asphalt[i];  maxgain = 0.0f;  break;
317 		}
318 		/// todo: more,sounds.. sand,snow,grass-new,mud..
319 		// todo: sum slip, spin, stop tire sounds
320 
321 		float pitch = std::min(1.f, std::max(0.f, (whVel[i]-5.0f)*0.1f ));
322 		pitch = (1.f - pitch) * pitchvar;
323 		pitch = pitch + (1.f - pitchvar);
324 		pitch = std::min(2.f, std::max(0.25f, pitch ));
325 
326 		snd->setPosition(wh, ev);
327 		snd->setGain(squeal[i]*maxgain * pSet->vol_tires);
328 		snd->setPitch(pitch * pmul);
329 		//  mute others
330 		if (snd != s.asphalt[i])  s.asphalt[i]->setGain(0.f);
331 		if (snd != s.grass[i])    s.grass[i]->setGain(0.f);
332 		if (snd != s.gravel[i])   s.gravel[i]->setGain(0.f);
333 
334 
335 		//  susp bump  ~~~
336 		if (dynamics.vtype == V_Car)
337 		{
338 			suspbump[i].Update(suspVel[i], suspDisp[i], dt);
339 			if (suspbump[i].JustSettled())
340 			{
341 				float bumpsize = suspbump[i].GetTotalBumpSize();
342 				float gain = bumpsize * speed * 0.2f;  //par
343 				gain = std::max(0.f, std::min(1.2f, gain));
344 
345 				if (gain > 0.2f && //!tirebump[i]->isAudible() &&
346 					(gain > s.bumpvol[i] || s.bumptime[i] > 0.22f))
347 				{
348 					s.bumpvol[i] = gain;
349 					s.bumptime[i] = 0.f;
350 					//tirebump[i]->start();
351 					//LogO("bump "+toStr(i)+" "+fToStr(gain));
352 				}
353 			}
354 			s.bump[i]->setPosition(wh, ev);  //par gain, time fade
355 			float gain = 0.5f + 0.7f*s.bumpvol[i] - s.bumptime[i]*(2.f+2.f*s.bumpvol[i]);
356 			gain = std::max(0.f, std::min(1.0f, gain));
357 
358 			s.bump[i]->setGain(gain * pSet->vol_susp);
359 			if (s.bumptime[i] < 5.f)
360 				s.bumptime[i] += dt;
361 		}
362 	}
363 
364 
365 	//  wind  ----
366 	gain = dynVel;
367 	if (dynamics.vtype == V_Spaceship)   gain *= 0.7f;
368 	//if (dynamics.sphere)  gain *= 0.9f;
369 	if (gain < 0.f)	gain = -gain;
370 	gain *= 0.02f;	gain *= gain;
371 	if (gain > 1.f)	gain = 1.f;
372 
373 	s.wind->setGain(gain * pSet->vol_env);
374 	s.wind->setPosition(ep, ev);
375 
376 	//  boost
377 	s.boost->setGain(boostVal * 0.55f * pSet->vol_engine);
378 	s.boost->setPosition(ep, ev);  //back?-
379 
380 
381 	//  fluids - hit  ~~~~
382 	bool fluidHit = whH_all > 1.f;
383 	//LogO(toStr(whH_all) + "  v "+ toStr(dynVel));
384 
385 	if (fluidHit && !s.fluidHitOld)
386 	//if (dynVel > 10.f && whH_all > 1.f && )
387 	{
388 		int i = std::min(Nwatersounds-1, (int)(dynVel / 15.f));
389 		float gain = std::min(3.0f, 0.3f + dynVel / 30.f);
390 		Sound* snd = /*mud ? s.mud : */s.water[i];
391 
392 		//LogO("fluid hit i"+toStr(i)+" g"+toStr(gain)+" "+(mud?"mud":"wtr"));
393 		if (!snd->isAudible())
394 		{
395 			snd->setGain(gain * pSet->vol_fl_splash * (mud ? 0.6f : 1.f));
396 			snd->setPosition(ep, ev);
397 			snd->start();
398 		}
399 
400 		if (s.mud)  {  Sound* snd = s.mud;
401 		if (!snd->isAudible())
402 		{
403 			snd->setGain(gain * pSet->vol_fl_splash);
404 			snd->setPosition(ep, ev);
405 			snd->start();
406 		}	}
407 	}
408 	s.fluidHitOld = fluidHit;
409 
410 	//  fluids - continuous
411 	float velM = mud && whH_all > 0.1f ?
412 		s.whMudSpin * 2.5f : 0.f;
413 	s.mud_cont->setGain(std::min(1.f, velM) * pSet->vol_fl_cont * 0.85f);
414 	s.mud_cont->setPitch(std::max(0.7f, std::min(/*3.f*/2.f, velM * 0.35f)));
415 	s.mud_cont->setPosition(ep, ev);
416 
417 	float velW = !mud && whH_all > 0.1f && whH_all < 3.9f ?
418 		dynVel / 30.f : 0.f;
419 	s.water_cont->setGain(std::min(1.f, velW * 1.5f) * pSet->vol_fl_cont);
420 	s.water_cont->setPitch(std::max(0.7f, std::min(1.3f, velW)));
421 	s.water_cont->setPosition(ep, ev);
422 }
423 //))  sounds
424 
425 
426 	//  crash  ----
427 	Vector3 hp;  hp = Axes::toOgre(hitPos);
428 	{
429 		crashdetection2.Update(-fHitForce, dt);
430 		crashdetection2.deceltrigger = 1.f;
431 		float crashdecel2 = crashdetection2.GetMaxDecel();
432 		dynamics.fHitForce3 = crashdecel2 / 30.f;
433 
434 		if (crashdecel2 > 0)
435 		{
436 			float gain = 0.9f;
437 
438 			int f = crashdecel2 / 30.f * Ncrashsounds;
439 			int i = std::max(1, std::min(Ncrashsounds-1, f));
440 			//LogO("crash: "+toStr(i));
441 
442 			if (s.crashtime[i] > /*ti*/0.4f)  //!crashsound.isAudible())
443 			{
444 				if (bSound)
445 				{
446 					s.crash[i]->setGain(gain * pSet->vol_car_crash);
447 					if (hitp)
448 					s.crash[i]->setPosition(hp, ev);
449 					s.crash[i]->start();
450 				}
451 				s.crashtime[i] = 0.f;
452 
453 				/// <><> Damage <><>
454 				if (dmg && !terminal)
455 					if (reduced)
456 						dynamics.fDamage += fDmg * crashdecel2 * dynamics.fHitDmgA * gPar.dmgFromHit;
457 					else  // normal
458 					{	float f = std::min(1.f, crashdecel2 / 30.f);
459 						f = powf(f, gPar.dmgPow2);
460 						dynamics.fDamage += fDmg * crashdecel2 * dynamics.fHitDmgA * gPar.dmgFromHit2 * f;
461 					}
462 			}
463 			//LogO("Car Snd: " + toStr(crashdecel));// + " force " + toStr(hit.force)
464 		}
465 	}
466 	//  time played
467 	for (int i=0; i < Ncrashsounds; ++i)
468 		if (s.crashtime[i] < 5.f)
469 			s.crashtime[i] += dt;
470 
471 
472 	//  crash scrap and screech
473 	if (bSound)
474 	{
475 		s.scrap->setGain(fCarScrap * pSet->vol_car_scrap);
476 		if (hitp)
477 		s.scrap->setPosition(hp, ev);
478 
479 		s.screech->setGain(fCarScreech * pSet->vol_car_scrap * 0.6f);
480 		if (hitp)
481 		s.screech->setPosition(hp, ev);
482 	}
483 
484 
485 	/// <><> Damage <><>
486 	if (dmg)
487 		if (dynamics.fDamage > 100.f)  dynamics.fDamage = 100.f;
488 }
489