1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #include "Frame.h"
5 
6 #include "GameSaveError.h"
7 #include "JsonUtils.h"
8 #include "Sfx.h"
9 #include "Space.h"
10 #include "collider/CollisionSpace.h"
11 #include "utils.h"
12 
13 std::vector<Frame> Frame::s_frames;
14 std::vector<CollisionSpace> Frame::s_collisionSpaces;
15 
Frame(const Dummy & d,FrameId parent,const char * label,unsigned int flags,double radius)16 Frame::Frame(const Dummy &d, FrameId parent, const char *label, unsigned int flags, double radius) :
17 	m_parent(parent),
18 	m_sbody(nullptr),
19 	m_astroBody(nullptr),
20 	m_pos(vector3d(0.0)),
21 	m_initialOrient(matrix3x3d::Identity()),
22 	m_orient(matrix3x3d::Identity()),
23 	m_vel(vector3d(0.0)),
24 	m_angSpeed(0.0),
25 	m_radius(radius),
26 	m_flags(flags)
27 {
28 	if (!d.madeWithFactory)
29 		Error("Frame ctor called directly!\n");
30 
31 	m_thisId = s_frames.size();
32 
33 	ClearMovement();
34 
35 	s_collisionSpaces.emplace_back();
36 	m_collisionSpace = s_collisionSpaces.size() - 1;
37 	// TODO: this hacks around TraceRay being called on an uninitialized collision space and crashing
38 	// Need to further evaluate the impact of this and whether it should be called in the CollisionSpace constructor instead
39 	// FIXME: this causes a crash when resizing the collisionSpace array because a collision space double-frees when move constructing
40 	// s_collisionSpaces.back().RebuildObjectTrees();
41 
42 	if (m_parent.valid())
43 		Frame::GetFrame(m_parent)->AddChild(m_thisId);
44 	if (label)
45 		m_label = label;
46 }
47 
Frame(const Dummy & d,FrameId parent)48 Frame::Frame(const Dummy &d, FrameId parent) :
49 	m_parent(parent),
50 	m_sbody(nullptr),
51 	m_astroBody(nullptr),
52 	m_pos(vector3d(0.0)),
53 	m_initialOrient(matrix3x3d::Identity()),
54 	m_orient(matrix3x3d::Identity()),
55 	m_vel(vector3d(0.0)),
56 	m_angSpeed(0.0),
57 	m_label("camera"),
58 	m_radius(0.0),
59 	m_flags(FLAG_ROTATING),
60 	m_collisionSpace(-1)
61 {
62 	if (!d.madeWithFactory)
63 		Error("Frame ctor called directly!\n");
64 
65 	m_thisId = s_frames.size();
66 
67 	ClearMovement();
68 	if (m_parent.valid())
69 		Frame::GetFrame(m_parent)->AddChild(m_thisId);
70 }
71 
Frame(Frame && other)72 Frame::Frame(Frame &&other) noexcept :
73 	m_sfx(std::move(other.m_sfx)),
74 	m_thisId(other.m_thisId),
75 	m_parent(other.m_parent),
76 	m_children(std::move(other.m_children)),
77 	m_sbody(other.m_sbody),
78 	m_astroBody(other.m_astroBody),
79 	m_pos(other.m_pos),
80 	m_oldPos(other.m_oldPos),
81 	m_interpPos(other.m_interpPos),
82 	m_initialOrient(other.m_initialOrient),
83 	m_orient(other.m_orient),
84 	m_vel(other.m_vel),
85 	m_angSpeed(other.m_angSpeed),
86 	m_oldAngDisplacement(other.m_oldAngDisplacement),
87 	m_label(std::move(other.m_label)),
88 	m_radius(other.m_radius),
89 	m_flags(other.m_flags),
90 	m_collisionSpace(other.m_collisionSpace),
91 	m_rootVel(other.m_rootVel),
92 	m_rootPos(other.m_rootPos),
93 	m_rootOrient(other.m_rootOrient),
94 	m_rootInterpPos(other.m_rootInterpPos),
95 	m_rootInterpOrient(other.m_rootInterpOrient),
96 	m_astroBodyIndex(other.m_astroBodyIndex),
97 	d(other.d)
98 {
99 	other.d.madeWithFactory = true;
100 }
101 
operator =(Frame && other)102 Frame &Frame::operator=(Frame &&other)
103 {
104 	m_sfx = std::move(other.m_sfx);
105 	m_thisId = other.m_thisId;
106 	m_parent = other.m_parent;
107 	m_children = std::move(other.m_children);
108 	m_sbody = other.m_sbody;
109 	m_astroBody = other.m_astroBody;
110 	m_pos = other.m_pos;
111 	m_oldPos = other.m_oldPos;
112 	m_interpPos = other.m_interpPos;
113 	m_initialOrient = other.m_initialOrient;
114 	m_orient = other.m_orient;
115 	m_vel = other.m_vel;
116 	m_angSpeed = other.m_angSpeed;
117 	m_oldAngDisplacement = other.m_oldAngDisplacement;
118 	m_label = std::move(other.m_label);
119 	m_radius = other.m_radius;
120 	m_flags = other.m_flags;
121 	m_collisionSpace = other.m_collisionSpace;
122 	m_rootVel = other.m_rootVel;
123 	m_rootPos = other.m_rootPos;
124 	m_rootOrient = other.m_rootOrient;
125 	m_rootInterpPos = other.m_rootInterpPos;
126 	m_rootInterpOrient = other.m_rootInterpOrient;
127 	m_astroBodyIndex = other.m_astroBodyIndex;
128 	d = other.d;
129 	return *this;
130 }
131 
ToJson(Json & frameObj,FrameId fId,Space * space)132 void Frame::ToJson(Json &frameObj, FrameId fId, Space *space)
133 {
134 	Frame *f = Frame::GetFrame(fId);
135 
136 	assert(f != nullptr);
137 
138 	frameObj["frameId"] = f->m_thisId;
139 	frameObj["flags"] = f->m_flags;
140 	frameObj["radius"] = f->m_radius;
141 	frameObj["label"] = f->m_label;
142 	frameObj["pos"] = f->m_pos;
143 	frameObj["ang_speed"] = f->m_angSpeed;
144 	frameObj["init_orient"] = f->m_initialOrient;
145 	frameObj["index_for_system_body"] = space->GetIndexForSystemBody(f->m_sbody);
146 	frameObj["index_for_astro_body"] = space->GetIndexForBody(f->m_astroBody);
147 
148 	Json childFrameArray = Json::array(); // Create JSON array to contain child frame data.
149 	for (FrameId kid : f->GetChildren()) {
150 		Json childFrameArrayEl = Json::object(); // Create JSON object to contain child frame.
151 		Frame::ToJson(childFrameArrayEl, kid, space);
152 		childFrameArray.push_back(childFrameArrayEl); // Append child frame object to array.
153 	}
154 	if (!childFrameArray.empty())
155 		frameObj["child_frames"] = childFrameArray; // Add child frame array to frame object.
156 
157 	// Add sfx array to supplied object.
158 	SfxManager::ToJson(frameObj, f->m_thisId);
159 }
160 
~Frame()161 Frame::~Frame()
162 {
163 	if (!d.madeWithFactory) {
164 		Error("Frame instance deletion outside 'DeleteFrame' [%zu]\n", m_thisId.id());
165 	}
166 }
167 
CreateFrame(FrameId parent,const char * label,unsigned int flags,double radius)168 FrameId Frame::CreateFrame(FrameId parent, const char *label, unsigned int flags, double radius)
169 {
170 	Dummy dummy;
171 	dummy.madeWithFactory = true;
172 
173 	s_frames.emplace_back(dummy, parent, label, flags, radius);
174 	return (s_frames.size() - 1);
175 }
176 
FromJson(const Json & frameObj,Space * space,FrameId parent,double at_time)177 FrameId Frame::FromJson(const Json &frameObj, Space *space, FrameId parent, double at_time)
178 {
179 	Dummy dummy;
180 	dummy.madeWithFactory = true;
181 
182 	// Set parent to nullptr here in order to avoid this frame
183 	// being a child twice (due to ctor calling AddChild)
184 	s_frames.emplace_back(dummy, FrameId(), nullptr);
185 
186 	Frame *f = &s_frames.back();
187 
188 	f->m_parent = parent;
189 	f->d.madeWithFactory = false;
190 
191 	try {
192 		f->m_thisId = frameObj["frameId"];
193 
194 		// Check if frames order in load and save are the same
195 		assert(s_frames.size() == 0 || (s_frames.size() - 1) == f->m_thisId.id());
196 
197 		f->m_flags = frameObj["flags"];
198 		f->m_radius = frameObj["radius"];
199 		f->m_label = frameObj["label"].get<std::string>();
200 
201 		f->m_pos = frameObj["pos"];
202 		f->m_angSpeed = frameObj["ang_speed"];
203 		f->SetInitialOrient(frameObj["init_orient"], at_time);
204 		f->m_sbody = space->GetSystemBodyByIndex(frameObj["index_for_system_body"]);
205 		f->m_astroBodyIndex = frameObj["index_for_astro_body"];
206 		f->m_vel = vector3d(0.0); // m_vel is set to zero.
207 
208 		if (frameObj.count("child_frames") && frameObj["child_frames"].is_array()) {
209 			Json childFrameArray = frameObj["child_frames"];
210 			f->m_children.reserve(childFrameArray.size());
211 			for (unsigned int i = 0; i < childFrameArray.size(); ++i) {
212 				// During 'FromJson' a reallocation may happens, invalidating 'f',
213 				// thus store his FrameId and renew it
214 				FrameId temp = f->m_thisId;
215 				FrameId kidId = FromJson(childFrameArray[i], space, f->m_thisId, at_time);
216 				f = &s_frames[temp];
217 				f->m_children.push_back(kidId);
218 			}
219 		} else {
220 			f->m_children.clear();
221 		}
222 	} catch (Json::type_error &) {
223 		Output("Loading error in '%s'\n", typeid(f).name());
224 		f->d.madeWithFactory = true;
225 		throw SavedGameCorruptException();
226 	}
227 
228 	SfxManager::FromJson(frameObj, f->m_thisId);
229 
230 	f->ClearMovement();
231 	return f->GetId();
232 }
233 
DeleteFrames()234 void Frame::DeleteFrames()
235 {
236 	// for each set "madeWithFactory"...
237 	std::for_each(begin(s_frames), end(s_frames), [](Frame &f) {
238 		f.d.madeWithFactory = true;
239 	});
240 	// then delete it
241 	s_frames.clear();
242 
243 	// remember to delete CollisionSpaces
244 	s_collisionSpaces.clear();
245 }
246 
GetFrame(FrameId fId)247 Frame *Frame::GetFrame(FrameId fId)
248 {
249 #ifndef NDEBUG
250 	if (fId && fId.id() >= s_frames.size())
251 		Error("In '%s': fId is valid but out of range (%zu)...\n", __func__, fId.id());
252 #endif
253 
254 	return fId.valid() ? &s_frames[fId] : nullptr;
255 }
256 
CreateCameraFrame(FrameId parent)257 FrameId Frame::CreateCameraFrame(FrameId parent)
258 {
259 	Dummy dummy;
260 	dummy.madeWithFactory = true;
261 
262 	s_frames.emplace_back(dummy, parent);
263 	return (s_frames.size() - 1);
264 }
265 
DeleteCameraFrame(FrameId camera)266 void Frame::DeleteCameraFrame(FrameId camera)
267 {
268 	if (!camera)
269 		return;
270 
271 	// Detach camera from parent, then delete:
272 	Frame *cameraFrame = Frame::GetFrame(camera);
273 	Frame *parent = Frame::GetFrame(cameraFrame->GetParent());
274 	if (parent)
275 		parent->RemoveChild(camera);
276 
277 // Call dtor "popping" element in vector
278 #ifndef NDEBUG
279 	if (s_frames.size() > 0 && camera.id() < s_frames.size() - 1) {
280 		Error("DeleteCameraFrame: seems camera frame is not the last frame!\n");
281 		abort();
282 	};
283 #endif // NDEBUG
284 	s_frames.back().d.madeWithFactory = true;
285 	s_frames.pop_back();
286 }
287 
PostUnserializeFixup(FrameId fId,Space * space)288 void Frame::PostUnserializeFixup(FrameId fId, Space *space)
289 {
290 	Frame *f = Frame::GetFrame(fId);
291 	f->UpdateRootRelativeVars();
292 	f->m_astroBody = space->GetBodyByIndex(f->m_astroBodyIndex);
293 	// build the object trees once after loading so they're initialized while paused.
294 	f->GetCollisionSpace()->RebuildObjectTrees();
295 	for (FrameId kid : f->GetChildren())
296 		PostUnserializeFixup(kid, space);
297 }
298 
CollideFrames(void (* callback)(CollisionContact *))299 void Frame::CollideFrames(void (*callback)(CollisionContact *))
300 {
301 	PROFILE_SCOPED()
302 
303 	std::for_each(begin(s_collisionSpaces), end(s_collisionSpaces), [&](CollisionSpace &cs) {
304 		cs.Collide(callback);
305 	});
306 }
307 
RemoveChild(FrameId fId)308 void Frame::RemoveChild(FrameId fId)
309 {
310 	PROFILE_SCOPED()
311 	if (!fId.valid()) return;
312 	Frame *f = Frame::GetFrame(fId);
313 	if (f == nullptr) return;
314 	const std::vector<FrameId>::iterator it = std::find(m_children.begin(), m_children.end(), fId);
315 	if (it != m_children.end())
316 		m_children.erase(it);
317 }
318 
AddGeom(Geom * g)319 void Frame::AddGeom(Geom *g) { s_collisionSpaces.at(m_collisionSpace).AddGeom(g); }
RemoveGeom(Geom * g)320 void Frame::RemoveGeom(Geom *g) { s_collisionSpaces.at(m_collisionSpace).RemoveGeom(g); }
AddStaticGeom(Geom * g)321 void Frame::AddStaticGeom(Geom *g) { s_collisionSpaces.at(m_collisionSpace).AddStaticGeom(g); }
RemoveStaticGeom(Geom * g)322 void Frame::RemoveStaticGeom(Geom *g) { s_collisionSpaces.at(m_collisionSpace).RemoveStaticGeom(g); }
SetPlanetGeom(double radius,Body * obj)323 void Frame::SetPlanetGeom(double radius, Body *obj)
324 {
325 	s_collisionSpaces.at(m_collisionSpace).SetSphere(vector3d(0, 0, 0), radius, static_cast<void *>(obj));
326 }
327 
GetCollisionSpace() const328 CollisionSpace *Frame::GetCollisionSpace() const
329 {
330 	if (m_collisionSpace >= 0)
331 		return &s_collisionSpaces[m_collisionSpace];
332 	else
333 		return nullptr;
334 }
335 
336 // doesn't consider stasis velocity
GetVelocityRelTo(FrameId relToId) const337 vector3d Frame::GetVelocityRelTo(FrameId relToId) const
338 {
339 	if (m_thisId == relToId) return vector3d(0, 0, 0); // early-out to avoid unnecessary computation
340 
341 	const Frame *relTo = Frame::GetFrame(relToId);
342 	vector3d diff = m_rootVel - relTo->m_rootVel;
343 	if (relTo->IsRotFrame())
344 		return diff * relTo->m_rootOrient;
345 	else
346 		return diff;
347 }
348 
GetPositionRelTo(FrameId relToId) const349 vector3d Frame::GetPositionRelTo(FrameId relToId) const
350 {
351 	// early-outs for simple cases, required for accuracy in large systems
352 	if (m_thisId == relToId) return vector3d(0, 0, 0);
353 
354 	const Frame *relTo = Frame::GetFrame(relToId);
355 
356 	if (GetParent() == relToId) return m_pos; // relative to parent
357 	if (relTo->GetParent() == m_thisId) {	  // relative to child
358 		if (!relTo->IsRotFrame())
359 			return -relTo->m_pos;
360 		else
361 			return -relTo->m_pos * relTo->m_orient;
362 	}
363 	if (relTo->GetParent() == GetParent()) { // common parent
364 		if (!relTo->IsRotFrame())
365 			return m_pos - relTo->m_pos;
366 		else
367 			return (m_pos - relTo->m_pos) * relTo->m_orient;
368 	}
369 
370 	// use precalculated absolute position and orient
371 	vector3d diff = m_rootPos - relTo->m_rootPos;
372 	if (relTo->IsRotFrame())
373 		return diff * relTo->m_rootOrient;
374 	else
375 		return diff;
376 }
377 
GetInterpPositionRelTo(FrameId relToId) const378 vector3d Frame::GetInterpPositionRelTo(FrameId relToId) const
379 {
380 	const Frame *relTo = Frame::GetFrame(relToId);
381 
382 	// early-outs for simple cases, required for accuracy in large systems
383 	if (m_thisId == relToId) return vector3d(0, 0, 0);
384 	if (GetParent() == relTo->GetId()) return m_interpPos; // relative to parent
385 	if (relTo->GetParent() == m_thisId) {				   // relative to child
386 		if (!relTo->IsRotFrame())
387 			return -relTo->m_interpPos;
388 		else
389 			return -relTo->m_interpPos * relTo->m_interpOrient;
390 	}
391 	if (relTo->GetParent() == GetParent()) { // common parent
392 		if (!relTo->IsRotFrame())
393 			return m_interpPos - relTo->m_interpPos;
394 		else
395 			return (m_interpPos - relTo->m_interpPos) * relTo->m_interpOrient;
396 	}
397 
398 	vector3d diff = m_rootInterpPos - relTo->m_rootInterpPos;
399 	if (relTo->IsRotFrame())
400 		return diff * relTo->m_rootInterpOrient;
401 	else
402 		return diff;
403 }
404 
GetOrientRelTo(FrameId relToId) const405 matrix3x3d Frame::GetOrientRelTo(FrameId relToId) const
406 {
407 	if (m_thisId == relToId) return matrix3x3d::Identity();
408 	return Frame::GetFrame(relToId)->m_rootOrient.Transpose() * m_rootOrient;
409 }
410 
GetInterpOrientRelTo(FrameId relToId) const411 matrix3x3d Frame::GetInterpOrientRelTo(FrameId relToId) const
412 {
413 	if (m_thisId == relToId) return matrix3x3d::Identity();
414 	return Frame::GetFrame(relToId)->m_rootInterpOrient.Transpose() * m_rootInterpOrient;
415 	/*	if (IsRotFrame()) {
416 		if (relTo->IsRotFrame()) return m_interpOrient * relTo->m_interpOrient.Transpose();
417 		else return m_interpOrient;
418 	}
419 	if (relTo->IsRotFrame()) return relTo->m_interpOrient.Transpose();
420 	else return matrix3x3d::Identity();
421 */
422 }
423 
GetTransformRelTo(FrameId relToId) const424 matrix4x4d Frame::GetTransformRelTo(FrameId relToId) const
425 {
426 	return matrix4x4d(GetOrientRelTo(relToId), GetPositionRelTo(relToId));
427 }
428 
GetInterpTransformRelTo(FrameId relToId) const429 matrix4x4d Frame::GetInterpTransformRelTo(FrameId relToId) const
430 {
431 	return matrix4x4d(GetInterpOrientRelTo(relToId), GetInterpPositionRelTo(relToId));
432 }
433 
UpdateInterpTransform(double alpha)434 void Frame::UpdateInterpTransform(double alpha)
435 {
436 	PROFILE_SCOPED()
437 	m_interpPos = alpha * m_pos + (1.0 - alpha) * m_oldPos;
438 
439 	double len = m_oldAngDisplacement * (1.0 - alpha);
440 	if (!is_zero_exact(len)) {					   // very small values are normal here
441 		matrix3x3d rot = matrix3x3d::RotateY(len); // RotateY is backwards
442 		m_interpOrient = m_orient * rot;
443 	} else
444 		m_interpOrient = m_orient;
445 
446 	Frame *parent = Frame::GetFrame(m_parent);
447 	if (!parent)
448 		ClearMovement();
449 	else {
450 		m_rootInterpPos = parent->m_rootInterpOrient * m_interpPos + parent->m_rootInterpPos;
451 		m_rootInterpOrient = parent->m_rootInterpOrient * m_interpOrient;
452 	}
453 
454 	for (FrameId kid : m_children) {
455 		Frame *kidFrame = Frame::GetFrame(kid);
456 		kidFrame->UpdateInterpTransform(alpha);
457 	}
458 }
459 
GetFrameTransform(const FrameId fFromId,const FrameId fToId,matrix4x4d & m)460 void Frame::GetFrameTransform(const FrameId fFromId, const FrameId fToId, matrix4x4d &m)
461 {
462 	matrix3x3d forient = Frame::GetFrame(fFromId)->GetOrientRelTo(fToId);
463 	vector3d fpos = Frame::GetFrame(fFromId)->GetPositionRelTo(fToId);
464 	m = forient;
465 	m.SetTranslate(fpos);
466 }
467 
ClearMovement()468 void Frame::ClearMovement()
469 {
470 	UpdateRootRelativeVars();
471 	m_rootInterpPos = m_rootPos;
472 	m_rootInterpOrient = m_rootOrient;
473 	m_oldPos = m_interpPos = m_pos;
474 	m_interpOrient = m_orient;
475 	m_oldAngDisplacement = 0.0;
476 }
477 
UpdateOrbitRails(double time,double timestep)478 void Frame::UpdateOrbitRails(double time, double timestep)
479 {
480 	PROFILE_SCOPED()
481 	std::for_each(begin(s_frames), end(s_frames), [&time, &timestep](Frame &frame) {
482 		frame.m_oldPos = frame.m_pos;
483 		frame.m_oldAngDisplacement = frame.m_angSpeed * timestep;
484 
485 		// update frame position and velocity
486 		if (frame.m_parent.valid() && frame.m_sbody && !frame.IsRotFrame()) {
487 			frame.m_pos = frame.m_sbody->GetOrbit().OrbitalPosAtTime(time);
488 			vector3d pos2 = frame.m_sbody->GetOrbit().OrbitalPosAtTime(time + timestep);
489 			frame.m_vel = (pos2 - frame.m_pos) / timestep;
490 		}
491 		// temporary test thing
492 		else
493 			frame.m_pos = frame.m_pos + frame.m_vel * timestep;
494 
495 		// update frame rotation
496 		double ang = fmod(frame.m_angSpeed * time, 2.0 * M_PI);
497 		if (!is_zero_exact(ang)) {						  // frequently used with e^-10 etc
498 			matrix3x3d rot = matrix3x3d::RotateY(-ang);	  // RotateY is backwards
499 			frame.m_orient = frame.m_initialOrient * rot; // angvel always +y
500 		}
501 		frame.UpdateRootRelativeVars(); // update root-relative pos/vel/orient
502 	});
503 	/*
504 	for (FrameId kid : m_children) {
505 		Frame *kidFrame = Frame::GetFrame(kid);
506 		kidFrame->UpdateOrbitRails(time, timestep);
507 	}
508 	*/
509 }
510 
SetInitialOrient(const matrix3x3d & m,double time)511 void Frame::SetInitialOrient(const matrix3x3d &m, double time)
512 {
513 	m_initialOrient = m;
514 	double ang = fmod(m_angSpeed * time, 2.0 * M_PI);
515 	if (!is_zero_exact(ang)) {						// frequently used with e^-10 etc
516 		matrix3x3d rot = matrix3x3d::RotateY(-ang); // RotateY is backwards
517 		m_orient = m_initialOrient * rot;			// angvel always +y
518 	} else {
519 		m_orient = m_initialOrient;
520 	}
521 }
522 
SetOrient(const matrix3x3d & m,double time)523 void Frame::SetOrient(const matrix3x3d &m, double time)
524 {
525 	m_orient = m;
526 	double ang = fmod(m_angSpeed * time, 2.0 * M_PI);
527 	if (!is_zero_exact(ang)) {					   // frequently used with e^-10 etc
528 		matrix3x3d rot = matrix3x3d::RotateY(ang); // RotateY is backwards
529 		m_initialOrient = m_orient * rot;		   // angvel always +y
530 	} else {
531 		m_initialOrient = m_orient;
532 	}
533 }
534 
UpdateRootRelativeVars()535 void Frame::UpdateRootRelativeVars()
536 {
537 	// update pos & vel relative to parent frame
538 	Frame *parent = Frame::GetFrame(m_parent);
539 	if (!parent) {
540 		m_rootPos = m_rootVel = vector3d(0, 0, 0);
541 		m_rootOrient = matrix3x3d::Identity();
542 	} else {
543 		m_rootPos = parent->m_rootOrient * m_pos + parent->m_rootPos;
544 		m_rootVel = parent->m_rootOrient * m_vel + parent->m_rootVel;
545 		m_rootOrient = parent->m_rootOrient * m_orient;
546 	}
547 }
548