1 #include "pch.h"
2 #include "../vdrift/par.h"
3 #include "common/Def_Str.h"
4 #include "FollowCamera.h"
5 #include "../vdrift/settings.h"
6
7 #include "../tinyxml/tinyxml2.h"
8 #include "../vdrift/pathmanager.h"
9 #include "../vdrift/mathvector.h"
10 // ray cast
11 #include "../vdrift/collision_contact.h"
12 #include "../vdrift/collision_world.h"
13 #include "../btOgre/BtOgreDebug.h"
14 #include "btBulletCollisionCommon.h"
15 #include "CarPosInfo.h"
16
17 #include <OgreCamera.h>
18 #include <OgreSceneNode.h>
19 #include <OgreTerrainGroup.h>
20
21 #include <MyGUI.h>
22 using namespace Ogre;
23 using namespace tinyxml2;
24
25
GetAngle(float x,float y)26 static float GetAngle(float x, float y)
27 {
28 if (x == 0.f && y == 0.f)
29 return 0.f;
30
31 if (y == 0.f)
32 return (x < 0.f) ? PI_d : 0.f;
33 else
34 return (y < 0.f) ? atan2f(-y, x) : (-atan2f(y, x));
35 }
36
37 /// Update
38 //-----------------------------------------------------------------------------------------------------
39
update(Real time,const PosInfo & posIn,PosInfo * posOut,COLLISION_WORLD * world,bool bounce)40 void FollowCamera::update(Real time, const PosInfo& posIn, PosInfo* posOut, COLLISION_WORLD* world, bool bounce)
41 {
42 if (!ca || !posOut) return;
43
44 /// input from car posInfoIn
45 Vector3 posGoal = posIn.pos;
46 Quaternion orientGoal = posIn.rot;
47 /// output saved back to car posInfoOut
48 Quaternion camRotFinal;
49
50 const static Quaternion
51 qO = Quaternion(Degree(180),Vector3::UNIT_Z) * Quaternion(Degree(-90),Vector3::UNIT_Y),
52 qR = Quaternion(Degree(90),Vector3(0,1,0));
53
54 Quaternion orient = orientGoal * qO;
55 Vector3 ofs = orient * ca->mOffset, goalLook = posGoal + ofs;
56
57 first = iFirst < 2; ///par few first frames after reset
58 if (iFirst < 10) // after reset
59 { ++iFirst; mDistReduce = 0.f; mATilt = 0.f; }
60
61 /// Camera Tilt from terrain/road slope under car
62 //-------------------------------------------------------------------------------------------
63 const float // params . . .
64 Rdist = 1.f, // dist from car to ray (front or back)
65 HupCar = 1.5f, // car up dir dist - for pipes - so pos stays inside pipe
66 Habove = 1.5f, // up axis dist, above car - for very high terrain angles
67 HMaxDepth = 12.f; // how far down can the ray goes
68 const static Radian r0(0.f),
69 angMin = Degree(10.f), // below this angle, tilt has no effect - terrain bumps
70 maxDiff = Degree(1.4f); // max diff of tilt - no sudden jumps
71 const float smoothSpeed = 14.f; // how fast to apply tilt change
72
73 bool bUseTilt = ca->mType == CAM_ExtAng || ca->mType == CAM_Follow;
74 Radian tilt(0.f);
75 if (pSet->cam_tilt && bUseTilt)
76 {
77 // car pos
78 Vector3 carUp = posIn.pos - HupCar * posIn.carY;
79 MATHVECTOR<float,3> pos(carUp.x, -carUp.z, carUp.y + Habove); // to vdr/blt
80 const static MATHVECTOR<float,3> dir(0,0,-1); // cast rays down
81
82 // car rot, yaw angle
83 Quaternion q = posIn.rot * Quaternion(Degree(90),Vector3(0,1,0));
84 float angCarY = q.getYaw().valueRadians() + PI_d/2.f;
85 float ax = cosf(angCarY)*Rdist, ay = sinf(angCarY)*Rdist;
86 //LogO("pos: "+fToStr(pos[0],2,4)+" "+fToStr(pos[1],2,4)+" a: "+fToStr(angCarY,2,4)+" dir: "+fToStr(ax,2,4)+" "+fToStr(ay,2,4));
87
88 // cast 2 rays - 2 times, average 2 angles
89 COLLISION_CONTACT ct0,ct1,ct20,ct21;
90 MATHVECTOR<float,3> ofs(ax*0.5f,ay*0.5f,0),ofs2(ax,ay,0);
91 world->CastRay(pos+ofs, dir, HMaxDepth,chassis, ct0, 0,0, true, true);
92 world->CastRay(pos-ofs, dir, HMaxDepth,chassis, ct1, 0,0, true, true);
93 world->CastRay(pos+ofs2,dir, HMaxDepth,chassis, ct20, 0,0, true, true);
94 world->CastRay(pos-ofs2,dir, HMaxDepth,chassis, ct21, 0,0, true, true);
95
96 #ifdef CAM_TILT_DBG
97 MATHVECTOR<float,3> v;
98 v = pos+ofs; posHit[0] = Vector3(v[0],v[2]- ct0.GetDepth(), -v[1]);
99 v = pos-ofs; posHit[1] = Vector3(v[0],v[2]- ct1.GetDepth(), -v[1]);
100 v = pos+ofs2; posHit[2] = Vector3(v[0],v[2]- ct20.GetDepth(),-v[1]);
101 v = pos-ofs2; posHit[3] = Vector3(v[0],v[2]- ct21.GetDepth(),-v[1]);
102 #endif
103
104 if (ct0.GetColObj() && ct1.GetColObj() && ct20.GetColObj() && ct21.GetColObj() )
105 tilt = (GetAngle(Rdist, ct1.GetDepth() - ct0.GetDepth()) +
106 GetAngle(2.f*Rdist, ct21.GetDepth() - ct20.GetDepth())) * 0.5f;
107 //else LogO(String("no hit: ")+(ct0.col?"1":"0")+(ct1.col?" 1":" 0"));
108
109 //if (tilt < angMin && tilt > -angMin) tilt = 0.f;
110 if (tilt < r0 && tilt >-angMin) { Radian d = tilt-angMin; tilt = std::min(r0, tilt + d*d*5.f); }
111 if (tilt > r0 && tilt < angMin) { Radian d =-angMin-tilt; tilt = std::max(r0, tilt - d*d*5.f); }
112
113 //LogO("a "+fToStr(angCarY,3,5)+" d "+fToStr(ct0.GetDepth(),3,5)+" "+fToStr(ct1.GetDepth(),3,5)+" t "+fToStr(tilt.valueDegrees(),3,5));
114 }
115 // smooth tilt angle
116 mATilt += std::min(maxDiff, std::max(-maxDiff, tilt - mATilt)) * time * smoothSpeed;
117
118
119 //-------------------------------------------------------------------------------------------
120 if (ca->mType == CAM_Car) /* 3 Car - car pos & rot full */
121 {
122 camPosFinal = goalLook;
123 camRotFinal = orient;
124
125 posOut->camPos = camPosFinal; // save result in out posInfo
126 posOut->camRot = camRotFinal;
127 return;
128 }
129
130 if (ca->mType == CAM_Follow) ofs = ca->mOffset;
131
132 Vector3 pos,goalPos;
133 pos = camPosFinal - ofs;
134 goalPos = posGoal;
135
136 Vector3 xyz;
137 if (ca->mType != CAM_Arena)
138 {
139 Real x,y,z,xz; // pitch & yaw to direction vector
140 Real ap = bUseTilt ? (ca->mPitch.valueRadians() + mATilt.valueRadians()) : ca->mPitch.valueRadians(),
141 ay = ca->mYaw.valueRadians();
142 y = sin(ap), xz = cos(ap);
143 x = sin(ay) * xz, z = cos(ay) * xz;
144 xyz = Vector3(x,y,z); xyz *= ca->mDist;
145 }
146
147 bool manualOrient = false;
148 switch (ca->mType)
149 {
150 case CAM_Arena: /* 2 Arena - free pos & rot */
151 goalPos = ca->mOffset - ofs;
152 break;
153
154 case CAM_Free: /* 1 Free - free rot, pos from car */
155 goalPos += xyz;
156 break;
157
158 case CAM_Follow: /* 0 Follow - car rotY & pos from behind car, smooth */
159 { Quaternion orient = orientGoal * qR;
160 orient.FromAngleAxis(orient.getYaw(), Vector3::UNIT_Y);
161 goalPos += orient * xyz;
162 } break;
163
164 case CAM_ExtAng: /* 4 Extended Angle - car in center, angle smooth */
165 { Quaternion orient = orientGoal * qR;
166 Quaternion ory; ory.FromAngleAxis(orient.getYaw(), Vector3::UNIT_Y);
167
168 if (first) { qq = ory; }
169 else qq = orient.Slerp(ca->mSpeed * time, qq, ory, true);
170
171 // smooth dist from vel
172 #if 0
173 {
174 if (first) { mPosNodeOld = posGoal; }
175 Real vel = (posGoal - mPosNodeOld).length() / std::max(0.002f, std::min(0.1f, time));
176 mPosNodeOld = posGoal;
177 if (first) mVel = 0.f; else
178 mVel += (vel - mVel) * time * 8.f; //par- vel smooth speed
179 if (!first)
180 xyz *= 1.f + std::min(100.f, mVel) * 0.01f; //par- vel dist factor
181 }
182 #endif
183
184 Quaternion qy = Quaternion(ca->mYaw,Vector3(0,1,0));
185 goalPos += qq * (xyz + ca->mOffset);
186
187 camPosFinal = goalPos;
188 camRotFinal = qq * qy * Quaternion(Degree(-ca->mPitch - mATilt), Vector3(1,0,0));
189 manualOrient = true;
190 } break;
191 }
192
193 if (!manualOrient) // if !CAM_ExtAng
194 {
195 float dtmul = ca->mSpeed == 0 ? 1.0f : ca->mSpeed * time;
196
197 if (ca->mType == CAM_Arena)
198 {
199 Vector3 Pos(0,0,0), goalPos = ca->mOffset;
200 Pos = camPosFinal; //read last state (smooth)
201 Pos += (goalPos - Pos) * dtmul;
202
203 mAPitch += (ca->mPitch - mAPitch) * dtmul;
204 mAYaw += (ca->mYaw - mAYaw) * dtmul;
205
206 if (first) { Pos = goalPos; mAPitch = ca->mPitch; mAYaw = ca->mYaw; }
207 camPosFinal = Pos;
208 camRotFinal = Quaternion(Degree(mAYaw),Vector3(0,1,0)) * Quaternion(Degree(mAPitch),Vector3(1,0,0));
209 manualOrient = true;
210 }
211 else
212 {
213 if (first) pos = goalPos;
214 Vector3 addPos,addLook;
215 addPos = (goalPos - pos).normalisedCopy() * (goalPos - pos).length() * dtmul;
216 if (addPos.squaredLength() > (goalPos - pos).squaredLength()) addPos = goalPos - pos;
217 pos += addPos;
218 camPosFinal = pos + ofs;
219
220 goalLook = posGoal + ofs;
221 if (first) { mLook = goalLook; }
222
223 addLook = (goalLook - mLook) * dtmul;//Rot;
224 mLook += addLook;
225 }
226 }
227
228 //camLookFinal = mLook;
229 if (!manualOrient) // CAM_Free or CAM_Follow
230 {
231 Vector3 zdir = camPosFinal - mLook; zdir.normalise();
232 Vector3 xVec = Vector3::UNIT_Y.crossProduct(zdir); xVec.normalise();
233 Vector3 yVec = zdir.crossProduct(xVec); yVec.normalise();
234 Quaternion q; q.FromAxes(xVec, yVec, zdir);
235 camRotFinal = q;
236 }
237
238 // cast ray from car to camera, reduce dist if hit
239 //-------------------------------------------------------------------------------------------
240 Vector3 pp = camPosFinal;
241 if (bounce)
242 pp += posIn.camOfs * ca->mOfsMul
243 * gPar.camBncScale * pSet->cam_bnc_mul;
244
245 Vector3 p = posGoal; p.y += 1.f; //up
246 //Vector3 d = camRotFinal * Vector3::UNIT_Z; d.normalise();
247 Vector3 d = pp - p; d.normalise();
248
249 if (!first && ca->mType != CAM_Arena)
250 {
251 MATHVECTOR<float,3> pos1(p.x,-p.z,p.y), dir(d.x,-d.z,d.y); //dir = dir.Normalize();
252 COLLISION_CONTACT ct;
253 float maxLen = (p - pp).length(); //cam near
254 world->CastRay(pos1, dir, maxLen,chassis, ct, 0,0, true, true, true/*+*/);
255 //dbgLen = -maxLen;
256
257 if (ct.GetColObj())
258 {
259 float len = ct.GetDepth(); //dbgLen = len;
260 len -= 0.2f + ct.GetNormal()[2]; ///par normal up, flat terrain, move closer
261 if (len < maxLen)
262 {
263 Real dist = maxLen - len;
264 if (dist > mDistReduce)
265 mDistReduce = dist;
266 } }
267 }
268
269 // save result in out posInfo
270 posOut->camPos = mDistReduce > 0.0001f ? (pp - d * mDistReduce) : pp;
271 posOut->camRot = camRotFinal;
272
273 // smooth, back to normal dist
274 if (mDistReduce > 0.f)
275 mDistReduce -= time * 10.f;
276 }
277
278
Apply(const PosInfo & posIn)279 void FollowCamera::Apply(const PosInfo& posIn)
280 {
281 //boost::this_thread::sleep(boost::posix_time::milliseconds(rand()%20));
282 if (!mCamera) return;
283
284 mCamera->setPosition(posIn.camPos);
285 mCamera->setOrientation(posIn.camRot);
286 }
287
288
289 /// mouse Move
290 //-----------------------------------------------------------------------------------------------------
291
Move(bool mbLeft,bool mbRight,bool mbMiddle,bool shift,Real mx,Real my,Real mz)292 void FollowCamera::Move( bool mbLeft, bool mbRight, bool mbMiddle, bool shift, Real mx, Real my, Real mz )
293 {
294 if (!ca) return;
295 fMoveTime = 0;
296 bool arena = ca->mType == CAM_Arena;
297 Real myH = my * -0.01, mzH = mz/120.f;
298 mx *= 0.005; my *= 0.005;
299
300 if (shift && mbMiddle)
301 {
302 ca->mSpeed += my*5;
303 if (ca->mSpeed < 0.f) ca->mSpeed = 0.f;
304 return;
305 }
306
307 //----------------------------------------------
308 if (arena) // Arena - free camera
309 {
310 Real a = ca->mYaw.valueRadians(), sx = cosf(a), sy = sinf(a);
311 Vector3 vx(sx,0,-sy), vy(sy,0,sx);
312
313 if (mbMiddle)
314 {
315 const Real s = -20;
316 ca->mOffset += s*mx *vy + Vector3(0, s*my, 0);
317 }
318 if (mbRight)
319 {
320 const Real s = 20;
321 if (shift)
322 ca->mOffset += Vector3(0, s*myH, 0);
323 else
324 ca->mOffset += s*mx*vx + s*my*vy;
325 }
326 if (mbLeft)
327 {
328 const Real s = 0.5;
329 ca->mPitch -= Radian(s*my);
330 if (!shift)
331 ca->mYaw -= Radian(s*mx);
332 }
333 // wheel
334 ca->mPitch += Radian(mzH * 3.f*PI_d/180.f);
335 return;
336 }
337 //----------------------------------------------
338 if (ca->mType == CAM_ExtAng)
339 {
340 if (mbMiddle)
341 { ca->mOffset.x = 0; ca->mOffset.z = 0; ca->mYaw = 0.f; }
342 if (mbLeft)
343 {
344 ca->mPitch -= Radian(my);
345 if (shift)
346 ca->mYaw += Radian(mx);
347 else
348 ca->mDist *= 1.0 - mx * 0.4;
349 }
350 if (mbRight)
351 if (shift) ca->mOffset += Vector3(mx, 0, my);
352 else ca->mOffset += Vector3(0, -my, 0);
353
354 ca->mDist *= 1.0 - mzH * 0.1;
355 return;
356 }
357 if (ca->mType == CAM_Car)
358 {
359 if (mbMiddle)
360 ca->mOffset.x = 0;
361 if (mbLeft)
362 {
363 ca->mOffset += Vector3(0, -my, 0);
364 ca->mDist *= 1.0 - mx * 0.4;
365 }
366 if (mbRight)
367 ca->mOffset += Vector3(mx, 0, my);
368
369 ca->mOffset += Vector3(0, mzH * 0.004, 0);
370 return;
371 }
372 //----------------------------------------------
373 if (mbMiddle)
374 {
375 if (ca->mType == CAM_Follow)
376 ca->mYaw = Degree(0);
377 else
378 ca->mOffset = Vector3(0, 0.0, 0);
379 }
380 if (mbRight)
381 {
382 if (!shift)
383 ca->mOffset += Vector3(0, -my, 0);
384 else
385 { ca->mOffset += Vector3(0, myH, 0);
386 ca->mDist *= 1.0 - mx * 0.3;
387 }
388 }
389 if (mbLeft)
390 {
391 if (!shift)
392 { ca->mPitch -= Radian(my);
393 //ca->mDist *= 1.0 - mzH * 0.1;
394 } else {
395 ca->mYaw += Radian(mx);
396 ca->mPitch -= Radian(my);
397 }
398 if (ca->mDist < 1.5)
399 ca->mDist = 1.5;
400 }
401 ca->mDist *= 1.0 - mzH * 0.1;
402 }
403
404
405 /// upd Info
406 //-----------------------------------------------------------------------------------------------------
updInfo(Real time)407 bool FollowCamera::updInfo(Real time)
408 {
409 if (!ca) return false;
410
411 if (fMoveTime >= 1.0) // hide after 1sec
412 return false;
413 else
414 fMoveTime += time;
415
416 switch (ca->mType)
417 {
418 case CAM_Follow: sprintf(ss, sFmt_Follow.c_str()
419 ,ca->mType, CAM_Str[ca->mType], ca->mYaw.valueDegrees(), ca->mPitch.valueDegrees(), ca->mDist
420 ,ca->mOffset.y, ca->mSpeed); break;
421 case CAM_Free: sprintf(ss, sFmt_Free.c_str()
422 ,ca->mType, CAM_Str[ca->mType], ca->mYaw.valueDegrees(), ca->mPitch.valueDegrees(), ca->mDist
423 ,ca->mOffset.y, ca->mSpeed); break;
424 case CAM_ExtAng: sprintf(ss, sFmt_ExtAng.c_str()
425 ,ca->mType, CAM_Str[ca->mType], ca->mPitch.valueDegrees(), ca->mDist
426 ,ca->mOffset.y, ca->mOffset.x, ca->mOffset.z, ca->mSpeed); break;
427 case CAM_Arena: sprintf(ss, sFmt_Arena.c_str()
428 ,ca->mType, CAM_Str[ca->mType], ca->mYaw.valueDegrees(), ca->mPitch.valueDegrees(), ca->mDist
429 ,ca->mOffset.x, ca->mOffset.y, ca->mOffset.z, ca->mSpeed); break;
430 case CAM_Car: sprintf(ss, sFmt_Car.c_str()
431 ,ca->mType, CAM_Str[ca->mType], ca->mOffset.z, ca->mOffset.x, ca->mOffset.y); break;
432 }
433 return true;
434 }
435
436
437
438 /// Cameras
439 ///-----------------------------------------------------------------------------------------------------
440
updAngle()441 void FollowCamera::updAngle()
442 {
443 if (miCount <= 0) return;
444 miCurrent = std::max(0, std::min(miCount-1, miCurrent));
445
446 CameraAngle* c = mCameraAngles[miCurrent];
447 if (ca->mType != c->mType) First(); // changed type, reset
448 *ca = *c; // copy
449 mDistReduce = 0.f; //reset
450
451 sName = toStr(miCurrent+1) + "/" + toStr(miCount)
452 + (ca->mMain > 0 ? ". " : " ") + ca->mName;
453 updName = true;
454 }
455
saveCamera()456 void FollowCamera::saveCamera()
457 {
458 CameraAngle* c = mCameraAngles[miCurrent];
459 c->mName = ca->mName; c->mType = ca->mType; c->mSpeed = ca->mSpeed;
460 c->mYaw = ca->mYaw; c->mPitch = ca->mPitch;
461 c->mDist = ca->mDist; c->mOffset = ca->mOffset;
462 }
463
464
465 // change next,prev
466
incCur(int dir)467 void FollowCamera::incCur(int dir)
468 {
469 miCurrent += dir;
470 if (miCurrent >= miCount) miCurrent = 0; // -= miCount;
471 if (miCurrent < 0) miCurrent = miCount-1;
472 }
473
Next(bool bPrev,bool bMainOnly)474 void FollowCamera::Next(bool bPrev, bool bMainOnly)
475 {
476 int dir = bPrev ? -1 : 1;
477 if (!bMainOnly) // all
478 {
479 incCur(dir); updAngle(); return;
480 }else
481 { int cnt = 0, old = miCurrent;
482 while (cnt < miCount)
483 {
484 cnt++; incCur(dir);
485 CameraAngle* c = mCameraAngles[miCurrent];
486 if (c->mMain > 0)
487 { updAngle(); return; }
488 }
489 miCurrent = old;
490 }
491 }
492
setCamera(int ang)493 void FollowCamera::setCamera(int ang)
494 {
495 miCurrent = ang; updAngle();
496 }
497
498
499 // ctors
500
FollowCamera(Camera * cam,SETTINGS * pSet1)501 FollowCamera::FollowCamera(Camera* cam, SETTINGS* pSet1) :
502 first(true), iFirst(0), ca(0), updName(0),
503 mCamera(cam), mTerrain(0), chassis(0), pSet(pSet1),
504 mLook(Vector3::ZERO), mPosNodeOld(Vector3::ZERO), mVel(0),
505 mAPitch(0.f),mAYaw(0.f), mATilt(0.f), mDistReduce(0.f)
506 {
507 ca = new CameraAngle();
508 ss[0]=0;
509 }
510
First()511 void FollowCamera::First()
512 {
513 first = true; iFirst = 0;
514 }
515
~FollowCamera()516 FollowCamera::~FollowCamera()
517 {
518 delete ca; ca = 0;
519 Destroy();
520 }
521
Destroy()522 void FollowCamera::Destroy()
523 {
524 for (std::vector<CameraAngle*>::iterator it = mCameraAngles.begin(); it != mCameraAngles.end(); ++it)
525 delete *it;
526 mCameraAngles.clear();
527 }
528
CameraAngle()529 CameraAngle::CameraAngle()
530 :mType(CAM_Follow), mName("Follow Default"), mMain(0)
531 ,mDist(7), mSpeed(10), mSpeedRot(10), mOfsMul(1)
532 ,mYaw(0), mPitch(7), mOffset(0,1.2,0), mHideGlass(0)
533 { }
534
535
536
537 /// Load from xml
538 //-----------------------------------------------------------------------------------------------------
loadCameras()539 bool FollowCamera::loadCameras()
540 {
541 fMoveTime = 5.f; // hide hint on start
542
543 miCount = 0; miCurrent = 0;
544 Destroy();
545
546 XMLDocument doc;
547 XMLError e = doc.LoadFile((PATHMANAGER::Cars() + "/cameras.xml").c_str());
548 if (e == XML_SUCCESS)
549 {
550 XMLElement* root = doc.RootElement();
551 if (!root) { /*mErrorDialog->show(String("Error loading Cameras !!"), false ); return false;*/ }
552 XMLElement* cam = root->FirstChildElement("Camera");
553 if (!cam) { /*mErrorDialog->show(String("Error loading Camera !!"), false ); return false;*/ }
554
555 while (cam)
556 {
557 CameraAngle* c = new CameraAngle(); const char* a = 0;
558 c->mName = cam->Attribute("name");
559 c->mType = (CamTypes)s2i(cam->Attribute("type"));
560 c->mYaw = Degree(0); c->mPitch = Degree(0); c->mDist = 10; c->mSpeed = 10;
561
562 a = cam->Attribute("default"); if (a) if (s2i(a)==1) miCurrent = miCount;
563 a = cam->Attribute("on"); if (a) c->mMain = s2i(a)-1;
564 a = cam->Attribute("hideGlass"); if (a) c->mHideGlass = s2i(a);
565
566 a = cam->Attribute("yaw"); if (a) c->mYaw += Degree(s2r(a));
567 a = cam->Attribute("pitch"); if (a) c->mPitch = Degree(s2r(a));
568 a = cam->Attribute("dist"); if (a) c->mDist = s2r(a);
569 a = cam->Attribute("offset"); if (a) c->mOffset = s2v(a);
570 a = cam->Attribute("speed"); if (a) c->mSpeed = s2r(a);
571 a = cam->Attribute("spRot"); if (a) c->mSpeedRot = s2r(a); else c->mSpeedRot = c->mSpeed;
572 a = cam->Attribute("bounce"); if (a) c->mOfsMul = s2r(a);
573
574 if (c->mMain >= 0) {
575 mCameraAngles.push_back(c); miCount++; }
576 else
577 delete c;
578 cam = cam->NextSiblingElement("Camera");
579 }
580 }
581
582 miCount = mCameraAngles.size();
583 if (miCount == 0)
584 {
585 CameraAngle* c = new CameraAngle(); c->mName = "Follow Default";
586 c->mType = CAM_Follow; c->mYaw = Degree(0); c->mPitch = Degree(14);
587 c->mDist = 9; c->mOffset = Vector3(0,2,0); c->mSpeed = 15;
588 mCameraAngles.push_back(c);
589 miCount++;
590 }
591
592 updAngle(); updFmtTxt();
593 return true;
594 }
595
596
597 // update format texts, from translations
598 //-----------------------------------------------------------------------------------------------------
updFmtTxt()599 void FollowCamera::updFmtTxt()
600 {
601 String sTR = TR("#{CamInfoStrings}");
602 vector<String>::type vs = StringUtil::split(sTR,",");
603
604 if (vs.size() < 17)
605 { LogO("==== Error in camera info translate string. Need 17 strings, have "+toStr(vs.size())+", using default English. " + sTR);
606 sTR="Type,Yaw,Pitch,Dist,Height,Speed,Offset,LEFT,RIGHT,Middle,Wheel,shift,Rotate,reset,move,H,Pos";
607 vs = StringUtil::split(sTR,","); }
608
609 String sType =vs[0],
610 sYaw =vs[1], sPitch =vs[2], sDist =vs[3], sHeight=vs[4], sSpeed =vs[5], sOffset=vs[6],
611 sLEFT =vs[7], sRIGHT =vs[8], sMiddle=vs[9], sWheel =vs[10], sshift =vs[11],
612 sRotate=vs[12], sreset =vs[13], smove =vs[14], sH =vs[15], sPos =vs[16];
613
614 sFmt_Follow =
615 sType+": %d %s "+sYaw+":%5.1f "+sPitch+":%5.1f "+sDist+":%5.1f "+sHeight+": %3.1f "+sSpeed+": %2.0f\n"+
616 sLEFT+": "+sPitch+" "+sshift+": "+sRotate+" | "+sRIGHT+": "+sHeight+" "+sshift+": "+sDist+","+sH+"\n"+
617 sMiddle+": "+sreset+" "+sYaw+" "+sshift+": "+sSpeed+" | "+sWheel+": "+sDist; // | S: save"
618 sFmt_Free =
619 sType+": %d %s "+sYaw+":%5.1f "+sPitch+":%5.1f "+sDist+":%5.1f "+sHeight+": %3.1f "+sSpeed+": %2.0f\n"+
620 sLEFT+": "+sPitch+" "+sshift+": "+sRotate+" | "+sRIGHT+": "+sHeight+" "+sshift+": "+sDist+","+sH+"\n"+
621 sMiddle+": "+sreset+" "+sHeight+" "+sshift+": "+sSpeed+" | "+sWheel+": "+sDist;
622 sFmt_ExtAng =
623 sType+": %d %s "+sPitch+":%5.1f "+sDist+":%5.1f "+sHeight+": %3.1f "+sOffset+": %3.1f %3.1f "+sSpeed+": %3.1f\n"+
624 sLEFT+": "+sPitch+", "+sDist+" "+sshift+": "+sRotate+" | "+sRIGHT+": "+sHeight+" "+sshift+": "+sOffset+"\n"+
625 sMiddle+": "+sreset+" "+sOffset+" "+sshift+": "+sSpeed+" | "+sWheel+": "+sDist;
626 sFmt_Arena =
627 sType+": %d %s "+sYaw+":%5.1f "+sPitch+":%5.1f "+sDist+":%5.1f "+sPos+": %3.1f %3.1f %3.1f "+sSpeed+": %2.0f\n"+
628 sLEFT+": "+sRotate+" "+sshift+": "+sPitch+" | "+sRIGHT+": "+smove+" "+sshift+": "+sHeight+"\n"+
629 sMiddle+": "+smove+","+sH+" | "+sWheel+": "+sPitch;
630 sFmt_Car =
631 sType+": %d %s "+sOffset+": %4.2f %4.2f "+sHeight+": %4.2f\n"+
632 sLEFT+": "+sHeight+" | "+sRIGHT+": "+sOffset+" | "+sMiddle+": "+sreset+" "+sOffset+"X";
633 }
634