1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include "UnitTracker.h"
5 #include "Game/Camera/FPSController.h"
6 #include "Game/CameraHandler.h"
7 #include "Game/Camera.h"
8 #include "Game/SelectedUnitsHandler.h"
9 #include "Map/Ground.h"
10 #include "Rendering/GlobalRendering.h"
11 #include "Sim/Units/Unit.h"
12 #include "Sim/Units/UnitHandler.h"
13 #include "System/Config/ConfigHandler.h"
14 #include "System/Log/ILog.h"
15
16
17 CUnitTracker unitTracker;
18
19
20 const char* CUnitTracker::modeNames[TrackModeCount] = {
21 "Single",
22 "Average",
23 "Extents"
24 };
25
26
CUnitTracker()27 CUnitTracker::CUnitTracker():
28 enabled(false),
29 doRoll(false),
30 firstUpdate(true),
31 trackMode(TrackSingle),
32 trackUnit(0),
33 timeOut(15),
34 lastFollowUnit(0),
35 lastUpdateTime(0.0f),
36 trackPos(500.0f, 100.0f, 500.0f),
37 trackDir(FwdVector),
38 oldCamDir(RgtVector),
39 oldCamPos(500.0f, 500.0f, 500.0f)
40 {
41 for (size_t a = 0; a < 32; ++a) {
42 oldUp[a] = UpVector;
43 }
44 }
45
46
~CUnitTracker()47 CUnitTracker::~CUnitTracker()
48 {
49 }
50
51
Disable()52 void CUnitTracker::Disable()
53 {
54 enabled = false;
55 }
56
57
GetMode() const58 int CUnitTracker::GetMode() const
59 {
60 return trackMode;
61 }
62
63
IncMode()64 void CUnitTracker::IncMode()
65 {
66 trackMode = (trackMode + 1) % TrackModeCount;
67 LOG("TrackMode: %s", modeNames[trackMode]);
68 }
69
70
SetMode(int mode)71 void CUnitTracker::SetMode(int mode)
72 {
73 if (mode < 0) {
74 trackMode = 0;
75 } else if (mode >= TrackModeCount) {
76 trackMode = TrackModeCount - 1;
77 } else {
78 trackMode = mode;
79 }
80 LOG("TrackMode: %s", modeNames[trackMode]);
81 }
82
83
84 /******************************************************************************/
85
Track()86 void CUnitTracker::Track()
87 {
88 CUnitSet& units = selectedUnitsHandler.selectedUnits;
89
90 CleanTrackGroup();
91
92 if (trackGroup.empty()) {
93 if (units.empty()) {
94 Disable();
95 } else {
96 MakeTrackGroup();
97 trackUnit = *trackGroup.begin();
98 enabled = true;
99 }
100 } else {
101 if (!units.empty()) {
102 MakeTrackGroup();
103 }
104 if (trackGroup.find(trackUnit) == trackGroup.end()) {
105 trackUnit = *trackGroup.begin();
106 enabled = true;
107 } else if (enabled) {
108 if (trackMode != TrackSingle) {
109 trackMode = TrackSingle;
110 LOG("TrackMode: %s", modeNames[TrackSingle]);
111 }
112 NextUnit();
113 } else {
114 enabled = true;
115 }
116 }
117 }
118
119
MakeTrackGroup()120 void CUnitTracker::MakeTrackGroup()
121 {
122 trackGroup.clear();
123 CUnitSet& units = selectedUnitsHandler.selectedUnits;
124 CUnitSet::const_iterator it;
125 for (it = units.begin(); it != units.end(); ++it) {
126 trackGroup.insert((*it)->id);
127 }
128 }
129
130
CleanTrackGroup()131 void CUnitTracker::CleanTrackGroup()
132 {
133 std::set<int>::iterator it = trackGroup.begin();
134
135 while (it != trackGroup.end()) {
136 if (unitHandler->GetUnitUnsafe(*it) != NULL) {
137 ++it;
138 continue;
139 }
140 std::set<int>::iterator it_next = it;
141 ++it_next;
142 if (trackUnit == *it) {
143 if (it_next == trackGroup.end()) {
144 trackUnit = *trackGroup.begin();
145 } else {
146 trackUnit = *it_next;
147 }
148 }
149 trackGroup.erase(it);
150 it = it_next;
151 }
152
153 if (trackGroup.empty()) {
154 Disable();
155 }
156 else if (trackGroup.find(trackUnit) == trackGroup.end()) {
157 trackUnit = *trackGroup.begin();
158 }
159 }
160
161
NextUnit()162 void CUnitTracker::NextUnit()
163 {
164 if (trackGroup.empty()) {
165 return;
166 }
167
168 std::set<int>::iterator it = trackGroup.find(trackUnit);
169 if (it == trackGroup.end()) {
170 trackUnit = *trackGroup.begin();
171 }
172 else {
173 ++it;
174 if (it == trackGroup.end()) {
175 trackUnit = *trackGroup.begin();
176 Disable();
177 } else {
178 trackUnit = *it;
179 }
180 }
181 }
182
183
GetTrackUnit()184 CUnit* CUnitTracker::GetTrackUnit()
185 {
186 CleanTrackGroup();
187
188 if (trackGroup.empty()) {
189 Disable();
190 return NULL;
191 }
192
193 return unitHandler->GetUnitUnsafe(trackUnit);
194 }
195
196
CalcAveragePos() const197 float3 CUnitTracker::CalcAveragePos() const
198 {
199 float3 p(ZeroVector);
200 std::set<int>::const_iterator it;
201 for (it = trackGroup.begin(); it != trackGroup.end(); ++it) {
202 p += unitHandler->GetUnitUnsafe(*it)->drawPos;
203 }
204 p /= (float)trackGroup.size();
205 return p;
206 }
207
208
CalcExtentsPos() const209 float3 CUnitTracker::CalcExtentsPos() const
210 {
211 float3 minPos(+1e9f, +1e9f, +1e9f);
212 float3 maxPos(-1e9f, -1e9f, -1e9f);
213 std::set<int>::const_iterator it;
214 for (it = trackGroup.begin(); it != trackGroup.end(); ++it) {
215 const float3& p = unitHandler->GetUnitUnsafe(*it)->drawPos;
216
217 if (p.x < minPos.x) { minPos.x = p.x; }
218 if (p.y < minPos.y) { minPos.y = p.y; }
219 if (p.z < minPos.z) { minPos.z = p.z; }
220 if (p.x > maxPos.x) { maxPos.x = p.x; }
221 if (p.y > maxPos.y) { maxPos.y = p.y; }
222 if (p.z > maxPos.z) { maxPos.z = p.z; }
223 }
224 return (minPos + maxPos) / 2.0f;
225 }
226
227
228 /******************************************************************************/
229
SetCam()230 void CUnitTracker::SetCam()
231 {
232 if (firstUpdate) {
233 firstUpdate = false;
234 doRoll = !configHandler->GetInt("ReflectiveWater");
235 }
236
237 CUnit* u = GetTrackUnit();
238 if (!u) {
239 Disable();
240 return;
241 }
242
243 if (lastFollowUnit != 0 && unitHandler->GetUnitUnsafe(lastFollowUnit) == 0) {
244 timeOut = 1;
245 lastFollowUnit = 0;
246 }
247
248 if (timeOut > 0) {
249 // Transition between 2 targets
250 timeOut++;
251 camera->forward = oldCamDir;
252 camera->SetPos(oldCamPos);
253 if (camHandler->GetCurrentControllerNum() == CCameraHandler::CAMERA_MODE_FIRSTPERSON) {
254 camHandler->GetCurrentController().SetDir(oldCamDir);
255 camHandler->GetCurrentController().SetPos(oldCamPos);
256 }
257 if (timeOut > 15) {
258 timeOut = 0;
259 }
260 camHandler->UpdateCam();
261 camera->Update();
262
263 } else if (camHandler->GetCurrentControllerNum() != CCameraHandler::CAMERA_MODE_FIRSTPERSON) {
264 // non-FPS camera modes (immediate positional tracking)
265 float3 pos;
266 switch (trackMode) {
267 case TrackAverage: {
268 pos = CalcAveragePos();
269 break;
270 }
271 case TrackExtents: {
272 pos = CalcExtentsPos();
273 break;
274 }
275 default: {
276 pos = u->drawMidPos;
277 break;
278 }
279 }
280 camHandler->GetCurrentController().SetTrackingInfo(pos, u->radius * 2.7182818f);
281 camHandler->UpdateCam();
282 camera->Update();
283
284 } else {
285 // FPS Camera
286 const float deltaTime = gs->frameNum + globalRendering->timeOffset - lastUpdateTime;
287 lastUpdateTime = gs->frameNum + globalRendering->timeOffset;
288
289 float3 modPlanePos(u->drawPos - (u->frontdir * u->radius * 3));
290 const float minHeight = CGround::GetHeightReal(modPlanePos.x, modPlanePos.z, false) + (u->radius * 2);
291 if (modPlanePos.y < minHeight) {
292 modPlanePos.y = minHeight;
293 }
294
295 trackPos += (modPlanePos - trackPos) * (1 - math::pow(0.95f, deltaTime));
296 trackDir += (u->frontdir - trackDir) * (1 - math::pow(0.90f, deltaTime));
297 trackDir.ANormalize();
298
299 camera->SetPos(trackPos);
300
301 camera->forward = u->pos + (u->speed * globalRendering->timeOffset) - camera->GetPos();
302 camera->forward.ANormalize();
303 camera->forward += trackDir;
304 camera->forward.ANormalize();
305
306 CFPSController& fpsCamera = static_cast<CFPSController&>(camHandler->GetCurrentController());
307 fpsCamera.SetDir(camera->forward);
308 fpsCamera.SetPos(trackPos);
309
310 if (doRoll) {
311 oldUp[gs->frameNum % 32] = u->updir;
312 float3 up(ZeroVector);
313 for (size_t a = 0; a < 32; ++a) {
314 up += oldUp[a];
315 }
316 camera->up = up;
317 } else {
318 camera->up = UpVector;
319 }
320
321 oldCamDir = camera->forward;
322 oldCamPos = camera->GetPos();
323 }
324 }
325