1 #include "pch.h"
2 #include "../vdrift/par.h"
3 #include "common/Def_Str.h"
4 #include "common/RenderConst.h"
5 #include "CarModel.h"
6 #include "../vdrift/pathmanager.h"
7 #include "../vdrift/mathvector.h"
8 #include "../vdrift/track.h"
9 #include "../vdrift/game.h"
10 #include "common/data/SceneXml.h"
11 #include "common/CScene.h"
12 #include "CGame.h"
13 #include "SplitScreen.h"
14 #include "FollowCamera.h"
15 #include "CarReflection.h"
16 #include "../road/Road.h"
17 #include "../vdrift/par.h"
18 
19 #include <OgreRoot.h>
20 #include <OgreTerrain.h>
21 #include <OgreEntity.h>
22 #include <OgreManualObject.h>
23 #include <OgreMaterialManager.h>
24 #include <OgreParticleSystem.h>
25 #include <OgreParticleEmitter.h>
26 #include <OgreParticleAffector.h>
27 #include <OgreRibbonTrail.h>
28 #include <OgreBillboardSet.h>
29 #include <OgreSceneNode.h>
30 #include <OgreTechnique.h>
31 #include <OgreViewport.h>
32 #include <MyGUI_TextBox.h>
33 using namespace Ogre;
34 
35 
setVisible(bool vis)36 void CarModel::setVisible(bool vis)
37 {
38 	mbVisible = vis;
39 	hideTime = 0.f;
40 
41 	pMainNode->setVisible(vis);
42 	if (brakes)
43 		brakes->setVisible(bBraking && vis);
44 	for (int w=0; w < numWheels; ++w)
45 		ndWh[w]->setVisible(vis);
46 
47 	UpdParsTrails(vis);
48 }
49 
UpdNextCheck()50 void CarModel::UpdNextCheck()
51 {
52 	updTimes = true;
53 	if (eType != CarModel::CT_LOCAL)  return;
54 	if (!ndNextChk || !pApp || !pApp->scn->road)  return;
55 	if (pApp->scn->road->mChks.empty())  return;
56 
57 	Vector3 p;
58 	if (iNumChks == pApp->scn->road->mChks.size() && iCurChk != -1)
59 	{
60 		bool hasLaps = pSet->game.local_players > 1 || pSet->game.champ_num >= 0 || pSet->game.chall_num >= 0 || pApp->mClient;
61 		int lap = pGame->timer.GetCurrentLap(iIndex) + 1, laps = pSet->game.num_laps;
62 		String smtr = "checkpoint_lap";
63 		if (hasLaps)
64 		if (lap == laps - 1)   smtr = "checkpoint_lastlap";
65 		else if (lap == laps)  smtr = "checkpoint_finish";
66 		p = vStartPos;  // finish
67 		sChkMtr = smtr;
68 		bChkUpd = true;
69 	}
70 	else
71 	{
72 		p = pApp->scn->road->mChks[iNextChk].pos;
73 		sChkMtr = "checkpoint_normal";
74 		bChkUpd = true;
75 	}
76 
77 	p.y -= gPar.chkBeamSy;  // lower
78 	ndNextChk->setPosition(p);
79 	ndNextChk->setScale(gPar.chkBeamSx, gPar.chkBeamSy, gPar.chkBeamSx);
80 	ndNextChk->setVisible(pSet->check_beam && !pApp->bHideHudBeam);
81 }
ShowNextChk(bool visible)82 void CarModel::ShowNextChk(bool visible)
83 {
84 	if (ndNextChk)
85 		ndNextChk->setVisible(visible && !pApp->bHideHudBeam);
86 }
87 
88 
ResetChecks(bool bDist)89 void CarModel::ResetChecks(bool bDist)  // needs to be done after road load!
90 {
91 	updTimes = true;
92 	iCurChk = -1;  iNumChks = 0;  // reset lap, chk vars
93 	iLoopChk = -1;  iLoopLastCam = -1;
94 	trackPercent = 0.f;
95 	if (!pApp || !pApp->scn->road)  return;
96 
97 	const SplineRoad* road = pApp->scn->road;
98 	iNextChk = pSet->game.trackreverse ? road->iChkId1Rev : road->iChkId1;
99 	UpdNextCheck();
100 
101 	//  percent const  ------
102 	if (bDist && !road->mChks.empty())
103 	{
104 		const Vector3& firstC = road->mChks[road->iChkId1].pos, lastC = road->mChks[road->iChkId1Rev].pos;
105 
106 		Vector3 vFirst = vStartPos - firstC;  distFirst = vFirst.length();
107 		Vector3 vLast  = lastC - vStartPos;   distLast = vLast.length();
108 		distTotal = distFirst + distLast + road->chksRoadLen;
109 		//LogO("Chk first: "+toStr(distFirst)+" last: "+toStr(distLast)+" total: "+toStr(distTotal));
110 	}
111 }
112 
113 //  get track driven dist part in %
114 //--------------------------------------------------------------------------------------------------------
UpdTrackPercent()115 void CarModel::UpdTrackPercent()
116 {
117 	if (!pApp || !pApp->scn->road)  return;
118 	const SplineRoad* road = pApp->scn->road;
119 
120 	float perc = 0.f;
121 	if (road && !road->mChks.empty() && !isGhost())
122 	{
123 		const Vector3& car = pMainNode->getPosition(), next = road->mChks[iNextChk].pos,
124 			start = vStartPos, curr = road->mChks[std::max(0,iCurChk)].pos;
125 		bool bRev = pSet->game.trackreverse;
126 		Real firstD = bRev ? distLast : distFirst;
127 		Real nextR = road->mChks[iNextChk].r;  // chk .r radius to tweak when entering chk..
128 
129 		Real dist = 0.f;
130 		if (iNumChks > 0)  dist = firstD;  // already after 1st chk
131 		if (iNumChks > 1)  dist +=  // after 1st to 2nd chk or more
132 			road->mChks[iNumChks-2].dist[bRev ? 1 : 0];
133 
134 
135 		float dist01 = 0.f;  // smooth dist part
136 		//  start to 1st chk
137 		if (iNumChks == 0)
138 		{
139 			Vector3 curDist  = car - start;
140 			Vector3 chksDist = next - start;  // first
141 			dist01 = (curDist.length() /*- nextR*/) / (chksDist.length() - nextR);
142 
143 			float percD = std::min(1.f, std::max(0.f, dist01 ));  // clamp to 0..1
144 			dist += percD * firstD;
145 		}
146 		//  last chk to finish
147 		else if (iNumChks == road->mChks.size())
148 		{
149 			Vector3 curDist  = start - car;
150 			Vector3 chksDist = curr - start;  // last
151 			dist01 = 1.f - (curDist.length() /*- nextR*/) / (chksDist.length() - nextR);
152 
153 			float percD = std::min(1.f, std::max(0.f, dist01 ));  // clamp to 0..1
154 			dist += percD * (bRev ? distFirst : distLast);  //lastD;
155 		}
156 		else  // between 2 checks
157 		{
158 			Vector3 curDist  = car  - next;   // car dist to next checkpoint
159 			Vector3 chksDist = curr - next;  // divide by (cur to next) checks distance
160 			Real ckD = chksDist.length();
161 
162 			dist01 = 1.f - (curDist.length() - nextR) / (ckD - road->mChks[iCurChk].r);
163 
164 			float percD = std::min(1.f, std::max(0.f, dist01 ));  // clamp to 0..1
165 			dist += percD * (ckD + road->mChks[iCurChk].r*0.8f);  //road->mChks[iNumChks-1].dist;
166 		}
167 		perc = 100.f * dist / distTotal;
168 
169 		if (perc > trackPercent)
170 			trackPercent = perc;
171 	}
172 }
173 
174 
175 //-------------------------------------------------------------------------------------------------------
176 //  Update
177 //-------------------------------------------------------------------------------------------------------
Update(PosInfo & posInfo,PosInfo & posInfoCam,float time)178 void CarModel::Update(PosInfo& posInfo, PosInfo& posInfoCam, float time)
179 {
180 	pReflect->camPosition = pMainNode->getPosition();
181 	int w,i;
182 
183 	//  upd chk mtr
184 	if (bChkUpd && entNextChk)
185 	{
186 		MaterialPtr mtr = MaterialManager::getSingleton().getByName(sChkMtr);
187 		if (!mtr.isNull())
188 			entNextChk->setMaterial(mtr);
189 	}
190 
191 	//  stop/resume par sys
192 	float fa = pGame->pause ? 0.f : 1.f;
193 	for (w=0; w < numWheels; ++w)
194 	{
195 		for (i=0; i < PAR_ALL; ++i)
196 			if (par[i][w])  par[i][w]->setSpeedFactor(fa);
197 		if (w < PAR_BOOST && parBoost[w])  parBoost[w]->setSpeedFactor(fa);
198 		if (parHit)  parHit->setSpeedFactor(fa);
199 	}
200 	for (w=0; w < PAR_THRUST*2; ++w)
201 		if (parThrust[w])  parThrust[w]->setSpeedFactor(fa);
202 
203 
204 	if (!posInfo.bNew)  return;  // new only ?
205 	posInfo.bNew = false;
206 	/// dont get anything from pCar or car.dynamics here
207 	/// all must be read from posInfo (it is filled from vdrift car or from replay)
208 
209 	if (!pMainNode)  return;
210 
211 	//  set car pos and rot
212 	pMainNode->setPosition(posInfo.pos);
213 	if (vtype == V_Sphere)
214 		pMainNode->setOrientation(Quaternion(Quaternion(Degree(-posInfo.hov_roll),Vector3::UNIT_Y)));
215 	else
216 	if (vtype == V_Spaceship)  // roll  vis only
217 		pMainNode->setOrientation(posInfo.rot * Quaternion(Degree(posInfo.hov_roll),Vector3::UNIT_X));
218 	else
219 		pMainNode->setOrientation(posInfo.rot);
220 
221 	///()  grass sphere pos
222 	Vector3 vx(1,0,0);  // car x dir
223 	vx = posInfo.rot * vx * 1.1;  //par
224 	posSph[0] = posInfo.pos + vx;  posSph[0].y += 0.5f;
225 	posSph[1] = posInfo.pos - vx;  posSph[1].y += 0.5f;
226 	if (ndSph)  // sph test
227 	{	ndSph->setPosition(posSph[0]);
228 		ndSph->setScale(Vector3::UNIT_SCALE * 1.7 *2/0.6f);  //par
229 	}
230 
231 	//  set camera view
232 	if (fCam)
233 	{	fCam->Apply(posInfoCam);
234 
235 		///~~  camera in fluid fog, detect and compute
236 		iCamFluid = -1;  fCamFl = 0.f;  // none
237 		const size_t sf = sc->fluids.size();
238 		if (sf > 0  && pSet->game.local_players == 1)
239 		{
240 			const Vector3& p = posInfo.camPos;
241 			const float r = 0.2f;  //par, near cam?
242 
243 			//  check if any fluid box overlaps camera pos sphere
244 			bool srch = true;  size_t f = 0;
245 			while (srch && f < sf)
246 			{
247 				const FluidBox& fb = sc->fluids[f];
248 				const Vector3& fp = fb.pos;
249 				Vector3 fs = fb.size;  fs.x *= 0.5f;  fs.z *= 0.5f;
250 
251 				bool inFl =  //  p +r   -fs fp +fs  -r p
252 					p.y +r > fp.y - fs.y && p.y -r < fp.y &&
253 					p.x +r > fp.x - fs.x && p.x -r < fp.x + fs.x &&
254 					p.z +r > fp.z - fs.z && p.z -r < fp.z + fs.z;
255 
256 				if (inFl)  // 1st only
257 				{	iCamFluid = f;  fCamFl = std::min(1.f, std::max(0.f, fp.y - p.y)) * 3.f;
258 					srch = false;  }
259 				++f;
260 			}
261 	}	}
262 
263 	//  upd rotY for minimap
264 	if (vtype == V_Sphere)
265 		angCarY = posInfo.hov_roll * 180.f / PI_d + 180.f;
266 	else
267 	{	Quaternion q = posInfo.rot * Quaternion(Degree(90),Vector3(0,1,0));
268 		angCarY = q.getYaw().valueDegrees() + 90.f;
269 	}
270 
271 	//  brake state
272 	#ifndef CAR_PRV
273 	bool braking = posInfo.braking > 0;
274 	if (bBraking != braking)
275 	{
276 		bBraking = braking;
277 		UpdateBraking();
278 	}
279 	#endif
280 
281 	//  terrain lightmap enable/disable (depending on distance to terrain)
282 	#define MAX_TERRAIN_DIST 2.0  // meters
283 	bool changed = false;
284 	if (terrain)
285 	{
286 		Vector3 carPos = pMainNode->getPosition();
287 		float terrainHeight = terrain->getHeightAtWorldPosition(carPos);
288 		float diff = std::abs(carPos.y - terrainHeight);
289 		if (diff > MAX_TERRAIN_DIST)
290 		{
291 			if (bLightMapEnabled)
292 			{	changed = true;  bLightMapEnabled = false;	}
293 		}
294 		else if (!bLightMapEnabled)
295 		{	changed = true;  bLightMapEnabled = true;	}
296 	}
297 	//  if no terrain, disable
298 	else if (bLightMapEnabled)
299 	{	changed = true;  bLightMapEnabled = false;	}
300 
301 	if (changed)
302 		UpdateLightMap();
303 
304 
305 	//  update particle emitters
306 	if (pSet->particles && pCar)
307 	{
308 		//  boost
309 		for (i=0; i < PAR_BOOST; ++i)  if (parBoost[i])
310 		{
311 			/// <><> damage reduce
312 			float dmg = pCar->dynamics.fDamage >= 80.f ? 0.f : std::max(0.f, 1.4f - pCar->dynamics.fDamage*0.01f);
313 			float emitB = posInfo.fboost * 40.f * dmg;  // par
314 			ParticleEmitter* pe = parBoost[i]->getEmitter(0);
315 			pe->setEmissionRate(emitB);
316 		}
317 		//  spaceship thrusters
318 		for (i=0; i < PAR_THRUST*2; ++i)  if (parThrust[i])
319 		{
320 			float dmg = 1.f - 0.5f * pCar->dynamics.fDamage*0.01f;
321 			float emitT = posInfo.hov_throttle * 60.f * dmg;  // par
322 			ParticleEmitter* pe = parThrust[i]->getEmitter(0);
323 			pe->setEmissionRate(emitT);
324 		}
325 	}
326 
327 	//  world hit
328 	if (parHit)
329 	{	ParticleEmitter* pe = parHit->getEmitter(0);
330 		if (posInfo.fHitTime > 0.f && pSet->particles)
331 		{
332 			pe->setPosition(posInfo.vHitPos);
333 			pe->setDirection(posInfo.vHitNorm);
334 
335 			pe->setEmissionRate(pSet->particles_len * std::min(160.f, posInfo.fParIntens) * posInfo.fHitTime);
336 			pe->setParticleVelocity(posInfo.fParVel);
337 		}else
338 			pe->setEmissionRate(0.f);
339 	}
340 
341 	//  wheels  ------------------------------------------------------------------------
342 	const float trlH = sc->ter ? 0.90f : 0.76f;  // vdr needs up (ter bumps), no ter  ..get from wheel contact ?rpl
343 
344 	for (w=0; w < numWheels; ++w)
345 	{
346 		float wR = whRadius[w];
347 		#ifdef CAM_TILT_DBG  // cam debug test only
348 			if (fCam)
349 				ndWh[w]->setPosition(fCam->posHit[w]);
350 			ndWh[w]->setScale(0.5f*Vector3::UNIT_SCALE);
351 		#else
352 		ndWh[w]->setPosition(posInfo.whPos[w]);
353 		#endif
354 		ndWh[w]->setOrientation(posInfo.whRot[w]);
355 
356 		///  Update particles and trails
357 		if (isGhostTrk())
358 			continue;  // doesnt have any
359 
360 		int whMtr = posInfo.whTerMtr[w];
361 		int whRd = posInfo.whRoadMtr[w];
362 
363 		bool pipe = whRd >= 30 && whRd < 60;  //old: whRd == 2;
364 		//todo: road,pipe 4mtr [whRd] layer params..
365 		float whVel = posInfo.whVel[w] * 3.6f;  //kmh
366 		float slide = posInfo.whSlide[w], squeal = posInfo.whSqueal[w];
367 			//LogO(" slide:"+fToStr(slide,3,5)+" squeal:"+fToStr(squeal,3,5));
368 		float onGr = slide < 0.f ? 0.f : 1.f;
369 
370 		//  wheel temp
371 		whTemp[w] += std::min(12.f, std::max(0.f, squeal*8 - slide*2 + squeal*slide*2)*time);
372 		whTemp[w] = std::min(1.5f, whTemp[w]);  ///*
373 		whTemp[w] -= time*7.f;  if (whTemp[w] < 0.f)  whTemp[w] = 0.f;
374 			//LogO(toStr(w)+" wht "+fToStr(wht[w],3,5));
375 
376 		///  emit rates +
377 		Real sq = squeal* std::min(1.f, whTemp[w]), l = pSet->particles_len * onGr;
378 		Real emitS = sq * (whVel * 30) * l * 0.45f;  ///*
379 		Real emitM = slide < 1.4f ? 0.f :  (8.f * sq * std::min(5.f, slide) * l);
380 		Real emitD = (std::min(140.f, whVel) / 3.5f + slide * 1.f ) * l;
381 		Real sizeD = (0.8f + 0.6f * std::min(140.f, whVel) / 140.f) * (w < 2 ? 0.7f : 1.1f);
382 
383 		//  ter mtr factors
384 		int mtr = std::max(0, std::min(whMtr-1, (int)(sc->td.layers.size()-1)));
385 		int rd  = sc->td.road1mtr ? 0 : std::max(0, std::min(3, whRd));
386 
387 		TerLayer& lay = whMtr==0 ? sc->td.layerRoad[rd] : sc->td.layersAll[sc->td.layers[mtr]];
388 		emitD *= lay.dust;  emitM *= lay.mud;  sizeD *= lay.dustS;  emitS *= lay.smoke;
389 
390 		if (pipe)  emitD = 0;  // no dust in pipes
391 		if (posInfo.whH[w] > 0.1f)  emitD = 0;  // no dust in fluids
392 
393 		bool ghost = isGhost();  // opt dis for ghost
394 		bool ghPar = !(ghost && !pSet->rpl_ghostpar);
395 		if (!ghPar)
396 		{	emitD = 0.f;  emitM = 0.f;  emitS = 0.f;  }
397 
398 		///  emit particles
399 		Vector3 vpos = posInfo.whPos[w];
400 		if (pSet->particles)
401 		{
402 			ParticleSystem* ps = par[PAR_Smoke][w];
403 			if (ps)  //  smoke
404 			{	ParticleEmitter* pe = ps->getEmitter(0);
405 				pe->setPosition(vpos + posInfo.carY * wR*0.7f);  ///*
406 				ps->getAffector(0)->setParameter("alpha", toStr(-0.2f - 0.023f * whVel));  // fade out speed
407 				pe->setTimeToLive( std::max(0.12f, 2.f - whVel * 0.06f) );  // live time
408 				pe->setDirection(-posInfo.carY);	pe->setEmissionRate(emitS);
409 			}
410 			ps = par[PAR_Mud][w];
411 			if (ps)	 //  mud
412 			{	ParticleEmitter* pe = ps->getEmitter(0);
413 				//pe->setDimensions(sizeM,sizeM);
414 				pe->setPosition(vpos + posInfo.carY * wR*0.7f);
415 				pe->setDirection(-posInfo.carY);	pe->setEmissionRate(emitM);
416 			}
417 			ps = par[PAR_Dust][w];
418 			if (ps)	 //  dust
419 			{	ps->setDefaultDimensions(sizeD,sizeD);
420 				ParticleEmitter* pe = ps->getEmitter(0);
421 				pe->setPosition(vpos + posInfo.carY * wR*0.31f);
422 				pe->setDirection(-posInfo.carY);	pe->setEmissionRate(emitD);
423 			}
424 
425 			//  fluids .::.
426 			ps = par[PAR_Water][w];
427 			int idPar = posInfo.whP[w];
428 			if (ps)  //  Water ~
429 			{
430 				float vel = posInfo.speed;  // depth.. only on surface?
431 				bool e = idPar == 0 && ghPar &&  vel > 10.f && posInfo.whH[w] < 1.f;
432 				float emitW = e ?  std::min(80.f, 5.0f * vel)  : 0.f;
433 
434 				ParticleEmitter* pe = ps->getEmitter(0);
435 				pe->setPosition(vpos + posInfo.carY * wR*0.51f);
436 				pe->setMinParticleVelocity(0.07* vel);
437 				pe->setMaxParticleVelocity(0.20* vel);
438 				pe->setDirection(-posInfo.carY);	pe->setEmissionRate(emitW * pSet->particles_len);
439 			}
440 			ps = par[PAR_MudHard][w];
441 			if (ps)  //  Mud ^
442 			{
443 				float vel = Math::Abs(posInfo.whAngVel[w]);
444 				bool e = idPar == 2 && ghPar &&  vel > 30.f;
445 				float emitM = e ?  posInfo.whH[w] * std::min(80.f, 1.5f * vel)  : 0.f;
446 
447 				ParticleEmitter* pe = ps->getEmitter(0);
448 				pe->setPosition(vpos + posInfo.carY * wR*0.51f);
449 				pe->setDirection(-posInfo.carY);	pe->setEmissionRate(emitM * pSet->particles_len);
450 			}
451 			ps = par[PAR_MudSoft][w];
452 			if (ps)  //  Mud soft ^
453 			{
454 				float vel = Math::Abs(posInfo.whAngVel[w]);
455 				bool e = idPar == 1 && ghPar &&  vel > 30.f;
456 				float emitM = e ?  posInfo.whH[w] * std::min(160.f, 3.f * vel)  : 0.f;
457 
458 				ParticleEmitter* pe = ps->getEmitter(0);
459 				pe->setPosition(vpos + posInfo.carY * wR*0.51f);
460 				pe->setDirection(-posInfo.carY);	pe->setEmissionRate(emitM * pSet->particles_len);
461 			}
462 		}
463 
464 		//  update trails h+
465 		if (pSet->trails)
466 		{	if (ndWhE[w])
467 			{	Vector3 vp = vpos + posInfo.carY * wR*trlH;
468 				if (terrain && whMtr > 0)
469 					vp.y = terrain->getHeightAtWorldPosition(vp) + 0.02f;  // 0.05f
470 					//if (/*whOnRoad[w]*/whMtr > 0 && road)  // on road, add ofs
471 					//	vp.y += road->fHeight;	}/**/
472 				ndWhE[w]->setPosition(vp);
473 				ndWhE[w]->setOrientation(posInfo.rot);
474 			}
475 			//  const trail alpha
476 			float ac = pipe ? 0.f : /*own par..*/lay.smoke < 0.5f ? 0.14f : 0.f;
477 			float al = (ac + 0.6f * std::min(1.f, 0.7f * whTemp[w]) ) * onGr;  // par+
478 			if (whTrail[w])
479 			{	whTrail[w]->setInitialColour(0,
480 				lay.tcl.x, lay.tcl.y, lay.tcl.z, lay.tcl.w * al/**/);
481 				if (iFirst > 10)  //par
482 					whTrail[w]->setInitialWidth(0, whWidth[w]);
483 			}
484 		}
485 	}
486 
487 	//  blendmap
488 	UpdWhTerMtr();
489 
490 	//  update brake meshes orientation
491 	for (w=0; w < numWheels; ++w)
492 	{
493 		if (ndBrake[w])
494 		{
495 			ndBrake[w]->_setDerivedOrientation( pMainNode->getOrientation() );
496 
497 			// this transformation code is just so the brake mesh can have the same alignment as the wheel mesh
498 			ndBrake[w]->yaw(Degree(-90), Node::TS_LOCAL);
499 			if (w%2 == 1)
500 				ndBrake[w]->setScale(-1, 1, 1);
501 
502 			ndBrake[w]->pitch(Degree(180), Node::TS_LOCAL);
503 
504 			if (w < 2)  // turn only front wheels
505 				ndBrake[w]->yaw(-Degree(posInfo.whSteerAng[w]));
506 		}
507 	}
508 
509 	if (iFirst <= 10)  ++iFirst;  //par
510 
511 	UpdateKeys();
512 }
513 
514 //-------------------------------------------------------------------------------------------------------
First()515 void CarModel::First()
516 {
517 	if (fCam)  fCam->First();
518 	iFirst = 0;
519 
520 	for (int w=0; w < numWheels; ++w)  // hide trails
521 	if (whTrail[w])
522 		whTrail[w]->setInitialWidth(0, 0.f);
523 }
524 
UpdateKeys()525 void CarModel::UpdateKeys()
526 {
527 	if (!pCar)  return;
528 
529 	///  goto last checkp - reset cam
530 	if (pCar->bLastChk && !bLastChkOld)
531 		First();
532 
533 	bLastChkOld = pCar->bLastChk;
534 
535 	///  change Cameras  ---------------------------------
536 	//if (!pApp->isFocGui)
537 	int iC = pCar->iCamNext;  // iRplCarOfs..
538 	if (iC != 0 && iCamNextOld == 0)
539 	{
540 		//  with ctrl - change current camera car index  (mouse move camera for many players)
541 		if (pApp->ctrl && iIndex == 0)
542 			pApp->iCurCam = (pApp->iCurCam + iC + pSet->game.local_players) % pSet->game.local_players;
543 		else
544 		{
545 			int visMask = 255;
546 			pApp->roadUpdTm = 1.f;
547 
548 			if (fCam)
549 			{	fCam->Next(iC < 0, pApp->shift);
550 				pApp->carsCamNum[iIndex] = fCam->miCurrent +1;  // save for pSet
551 				visMask = fCam->ca->mHideGlass ? RV_MaskAll-RV_CarGlass : RV_MaskAll;
552 				for (std::list<Viewport*>::iterator it = pApp->mSplitMgr->mViewports.begin();
553 					it != pApp->mSplitMgr->mViewports.end(); ++it)
554 					(*it)->setVisibilityMask(visMask);
555 			}
556 		}
557 	}
558 	iCamNextOld = iC;
559 }
560 
561 
562 
563 //-------------------------------------------------------------------------------------------------------
564 //  utility
565 //-------------------------------------------------------------------------------------------------------
UpdateLightMap()566 void CarModel::UpdateLightMap()
567 {
568 	MaterialPtr mtr;
569 	for (int i=0; i < NumMaterials; ++i)
570 	{
571 		mtr = MaterialManager::getSingleton().getByName(sMtr[i]);
572 		if (!mtr.isNull())
573 		{	Material::TechniqueIterator techIt = mtr->getTechniqueIterator();
574 			while (techIt.hasMoreElements())
575 			{	Technique* tech = techIt.getNext();
576 				Technique::PassIterator passIt = tech->getPassIterator();
577 				while (passIt.hasMoreElements())
578 				{	Pass* pass = passIt.getNext();
579 					if (pass->hasFragmentProgram())
580 					{
581 						GpuProgramParametersSharedPtr params = pass->getFragmentProgramParameters();
582 						params->setIgnoreMissingParams(true);  // don't throw exception if material doesnt use lightmap
583 						params->setNamedConstant("enableTerrainLightMap", bLightMapEnabled ? 1.f : 0.f);
584 	}	}	}	}	}
585 }
586 
UpdateBraking()587 void CarModel::UpdateBraking()
588 {
589 	if (brakes)
590 		brakes->setVisible(bBraking && mbVisible);
591 
592 	std::string texName = sDirname + (bBraking ? "_body00_brake.png" : "_body00_add.png");
593 
594 	MaterialPtr mtr = MaterialManager::getSingleton().getByName(sMtr[Mtr_CarBody]);
595 	if (!mtr.isNull())
596 	{	Material::TechniqueIterator techIt = mtr->getTechniqueIterator();
597 		while (techIt.hasMoreElements())
598 		{	Technique* tech = techIt.getNext();
599 			Technique::PassIterator passIt = tech->getPassIterator();
600 			while (passIt.hasMoreElements())
601 			{	Pass* pass = passIt.getNext();
602 				Pass::TextureUnitStateIterator tusIt = pass->getTextureUnitStateIterator();
603 				while (tusIt.hasMoreElements())
604 				{
605 					TextureUnitState* tus = tusIt.getNext();
606 					if (tus->getName() == "diffuseMap")
607 					{	tus->setTextureName( texName );  return;  }
608 	}	}	}	}
609 }
610 
611 
UpdParsTrails(bool visible)612 void CarModel::UpdParsTrails(bool visible)
613 {
614 	bool vis = visible && pSet->particles;
615 	for (int w=0; w < numWheels; ++w)
616 	{
617 		uint8 grp = RQG_CarTrails;  //9=road  after glass
618 		if (w < PAR_BOOST && parBoost[w]) {  parBoost[w]->setVisible(vis);  parBoost[w]->setRenderQueueGroup(grp);  }
619 		if (whTrail[w]){  whTrail[w]->setVisible(visible && pSet->trails);  whTrail[w]->setRenderQueueGroup(grp);  }
620 		grp = RQG_CarParticles;
621 		for (int p=0; p < PAR_ALL; ++p)
622 			if (par[p][w]){  par[p][w]->setVisible(vis);  par[p][w]->setRenderQueueGroup(grp);  }
623 		if (parHit && w==0)	{  parHit->setVisible(vis);  parHit->setRenderQueueGroup(grp);  }
624 	}
625 	for (int w=0; w < PAR_THRUST*2; ++w)
626 		if (parThrust[w]) {  parThrust[w]->setVisible(vis);  parThrust[w]->setRenderQueueGroup(RQG_CarTrails);  }
627 }
628 
629 
630 ///  just to display info on wheel surfaces
631 //-------------------------------------------------------------------------------------------------------
UpdWhTerMtr()632 void CarModel::UpdWhTerMtr()
633 {
634 	if (!pCar || !ndWh[0])  return;
635 	//int t = blendMapSize;
636 	//Real tws = sc->td.fTerWorldSize;
637 
638 	txtDbgSurf = "";
639 	for (int i=0; i < pCar->numWheels; ++i)
640 	{
641 		//Vector3 w = ndWh[i]->getPosition();
642 		//int mx = (w.x + 0.5*tws)/tws*t, my = (-w.z + 0.5*tws)/tws*t;
643 		//mx = std::max(0,std::min(t-1, mx)), my = std::max(0,std::min(t-1, my));
644 
645 		//int mtr = pCar->dynamics.bWhOnRoad[i] ? 0 : blendMtr[my*t + mx];
646 		//whTerMtr[i] = mtr;
647 		//whRoadMtr[i] = pCar->dynamics.bWhOnRoad[i];
648 
649 		const CARDYNAMICS& cd = pCar->dynamics;  int iRd = cd.iWhOnRoad[i];
650 		float d = 0.5f * cd.wheel_contact[i].GetDepth() / cd.wheel[i].GetRadius();
651 
652 		const TRACKSURFACE* tsu = cd.GetWheelContact(WHEEL_POSITION(i)).GetSurfacePtr();
653 		//pCar->dynamics.bTerrain = true;
654 
655 		if (pSet->car_dbgsurf)  // dbg info surf  -------
656 		{
657 		//TerLayer& lay = /*mtr == 0 ? sc->td.layerRoad :*/ sc->td.layersAll[ sc->td.layers[ std::min((int)sc->td.layers.size()-1, mtr-1) ] ];
658 		txtDbgSurf += //"mx " + toStr(mx) + " my " + toStr(my) +
659 			( iRd == 0	? ( "T" + toStr(cd.whTerMtr[i]) )  // Terrain/Pipe/Road
660 						: ( (iRd==2 ? "P" : "R") + toStr(cd.whRoadMtr[i]) )  ) +
661 			(!tsu ? "  --" : (
662 				"  " + tsu->name + " " + csTRKsurf[tsu->type] + //" [" + lay.texFile + "] " +
663 				"\n      "+ tsu->tireName + "\n     "+
664 				" d= " + fToStr(d, 2,5) + "  dr " + fToStr(tsu->rollingDrag, 0,3) + " rr " + fToStr(tsu->rollingResist, 1,3) +
665 				"  fr " + fToStr(tsu->friction, 2,4) +
666 				"  ba " + fToStr(tsu->bumpAmplitude, 2,4) + " bw " + fToStr(tsu->bumpWaveLength, 2,4) +
667 				"  b0 " + fToStr(tsu->tire->longitudinal[0], 3,5)
668 				//,lay.dust, lay.mud, lay.dustS	//,lay.tclr.r, lay.tclr.g, lay.tclr.b, lay.tclr.a
669 			)) + "\n";
670 		}
671 	}
672 }
673 
674 
675 //  utils
676 //-------------------------------------------------------------------------------------------------------
677 
ChangeClr()678 void CarModel::ChangeClr()
679 {
680 	int i = iColor;
681 	float c_h = pSet->gui.car_hue[i], c_s = pSet->gui.car_sat[i],
682 	      c_v = pSet->gui.car_val[i], gloss = pSet->gui.car_gloss[i], refl = pSet->gui.car_refl[i];
683 	color.setHSB(1-c_h, c_s, c_v);  //set, mini pos clr
684 
685 	MaterialPtr mtr = MaterialManager::getSingleton().getByName(sMtr[Mtr_CarBody]);
686 	if (!mtr.isNull())
687 	{	Material::TechniqueIterator techIt = mtr->getTechniqueIterator();
688 		while (techIt.hasMoreElements())
689 		{	Technique* tech = techIt.getNext();
690 			Technique::PassIterator passIt = tech->getPassIterator();
691 			while (passIt.hasMoreElements())
692 			{	Pass* pass = passIt.getNext();
693 				if (pass->hasFragmentProgram())
694 				{
695 					GpuProgramParametersSharedPtr params = pass->getFragmentProgramParameters();
696 					params->setNamedConstant("carColour", color);
697 					params->setNamedConstant("glossiness", 1 - gloss);
698 					params->setNamedConstant("reflectiveness", refl);
699 	}	}	}	}
700 
701 	if (pNickTxt)
702 		pNickTxt->setTextColour(MyGUI::Colour(color.r,color.g,color.b));
703 
704 	// opp list text and mini pos colors - auto in hud update
705 }
706