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