1 // Copyright © 2013-14 Meteoric Games Ltd
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #include "ShipCockpit.h"
5 
6 #include "Easing.h"
7 #include "Game.h"
8 #include "Pi.h"
9 #include "Player.h"
10 #include "WorldView.h"
11 #include "graphics/Renderer.h"
12 #include "ship/CameraController.h"
13 
ShipCockpit(const std::string & modelName)14 ShipCockpit::ShipCockpit(const std::string &modelName) :
15 	m_shipDir(0.0),
16 	m_shipYaw(0.0),
17 	m_dir(0.0),
18 	m_yaw(0.0),
19 	m_rotInterp(0.f),
20 	m_transInterp(0.f),
21 	m_gForce(0.f),
22 	m_offset(0.f),
23 	m_shipVel(0.f),
24 	m_translate(0.0),
25 	m_transform(matrix4x4d::Identity())
26 {
27 	assert(!modelName.empty());
28 	SetModel(modelName.c_str());
29 	assert(GetModel());
30 	SetColliding(false);
31 	m_icc = nullptr;
32 }
33 
~ShipCockpit()34 ShipCockpit::~ShipCockpit()
35 {
36 }
37 
Render(Graphics::Renderer * renderer,const Camera * camera,const vector3d & viewCoords,const matrix4x4d & viewTransform)38 void ShipCockpit::Render(Graphics::Renderer *renderer, const Camera *camera, const vector3d &viewCoords, const matrix4x4d &viewTransform)
39 {
40 	PROFILE_SCOPED()
41 	RenderModel(renderer, camera, viewCoords, viewTransform);
42 }
43 
resetInternalCameraController()44 inline void ShipCockpit::resetInternalCameraController()
45 {
46 	m_icc = static_cast<InternalCameraController *>(Pi::game->GetWorldView()->shipView->GetCameraController());
47 }
48 
Update(const Player * player,float timeStep)49 void ShipCockpit::Update(const Player *player, float timeStep)
50 {
51 	m_transform = matrix4x4d::Identity();
52 
53 	if (m_icc == nullptr) {
54 		// I don't know where to put this
55 		resetInternalCameraController();
56 	}
57 
58 	double rotX;
59 	double rotY;
60 	m_icc->getRots(rotX, rotY);
61 	m_transform.RotateX(rotX);
62 	m_transform.RotateY(rotY);
63 
64 	vector3d cur_dir = player->GetOrient().VectorZ().Normalized();
65 	if (cur_dir.Dot(m_shipDir) < 1.0f) {
66 		m_rotInterp = 0.0f;
67 		m_shipDir = cur_dir;
68 	}
69 
70 	//---------------------------------------- Acceleration
71 	float cur_vel = CalculateSignedForwardVelocity(-cur_dir, player->GetVelocity()); // Forward is -Z
72 	float gforce = Clamp(floorf(((fabs(cur_vel) - m_shipVel) / timeStep) / 9.8f), -COCKPIT_MAX_GFORCE, COCKPIT_MAX_GFORCE);
73 	if (fabs(cur_vel) > 500000.0f ||	   // Limit gforce measurement so we don't get astronomical fluctuations
74 		fabs(gforce - m_gForce) > 100.0) { // Smooth out gforce one frame spikes, sometimes happens when hitting max speed due to the thrust limiters
75 		gforce = 0.0f;
76 	}
77 	if (fabs(gforce - m_gForce) > 100.0) {
78 		gforce = 0.0f;
79 	}
80 	if (fabs(m_translate.z - m_offset) < 0.001f) {
81 		m_transInterp = 0.0f;
82 	}
83 	float offset = (gforce > 14.0f ? -1.0f : (gforce < -14.0f ? 1.0f : 0.0f)) * COCKPIT_ACCEL_OFFSET;
84 	m_transInterp += timeStep * COCKPIT_ACCEL_INTERP_MULTIPLIER;
85 	if (m_transInterp > 1.0f) {
86 		m_transInterp = 1.0f;
87 		m_translate.z = offset;
88 	}
89 	m_translate.z = Easing::Quad::EaseIn(double(m_transInterp), m_translate.z, offset - m_translate.z, 1.0);
90 	m_gForce = gforce;
91 	m_offset = offset;
92 	m_shipVel = cur_vel;
93 
94 	//------------------------------------------ Rotation
95 	// For yaw/pitch
96 	vector3d rot_axis = cur_dir.Cross(m_dir).Normalized();
97 	vector3d yaw_axis = player->GetOrient().VectorY().Normalized();
98 	vector3d pitch_axis = player->GetOrient().VectorX().Normalized();
99 	float dot = cur_dir.Dot(m_dir);
100 	float angle = acos(dot);
101 	// For roll
102 	if (yaw_axis.Dot(m_shipYaw) < 1.0f) {
103 		m_rotInterp = 0.0f;
104 		m_shipYaw = yaw_axis;
105 	}
106 	vector3d rot_yaw_axis = yaw_axis.Cross(m_yaw).Normalized();
107 	float dot_yaw = yaw_axis.Dot(m_yaw);
108 	float angle_yaw = acos(dot_yaw);
109 
110 	if (dot < 1.0f || dot_yaw < 1.0f) {
111 		// Lag/Recovery interpolation
112 		m_rotInterp += timeStep * COCKPIT_ROTATION_INTERP_MULTIPLIER;
113 		if (m_rotInterp > 1.0f) {
114 			m_rotInterp = 1.0f;
115 		}
116 
117 		// Yaw and Pitch
118 		if (dot < 1.0f) {
119 			if (angle > DEG2RAD(COCKPIT_LAG_MAX_ANGLE)) {
120 				angle = DEG2RAD(COCKPIT_LAG_MAX_ANGLE);
121 			}
122 			angle = Easing::Quad::EaseOut(m_rotInterp, angle, -angle, 1.0f);
123 			m_dir = cur_dir;
124 			if (angle >= 0.0f) {
125 				m_dir.ArbRotate(rot_axis, angle);
126 				// Apply pitch
127 				vector3d yz_proj = (m_dir - (m_dir.Dot(pitch_axis) * pitch_axis)).Normalized();
128 				float pitch_cos = yz_proj.Dot(cur_dir);
129 				float pitch_angle = 0.0f;
130 				if (pitch_cos < 1.0f) {
131 					pitch_angle = acos(pitch_cos);
132 					if (rot_axis.Dot(pitch_axis) < 0) {
133 						pitch_angle = -pitch_angle;
134 					}
135 					m_transform.RotateX(-pitch_angle);
136 				}
137 				// Apply yaw
138 				vector3d xz_proj = (m_dir - (m_dir.Dot(yaw_axis) * yaw_axis)).Normalized();
139 				float yaw_cos = xz_proj.Dot(cur_dir);
140 				float yaw_angle = 0.0f;
141 				if (yaw_cos < 1.0f) {
142 					yaw_angle = acos(yaw_cos);
143 					if (rot_axis.Dot(yaw_axis) < 0) {
144 						yaw_angle = -yaw_angle;
145 					}
146 					m_transform.RotateY(-yaw_angle);
147 				}
148 			} else {
149 				angle = 0.0f;
150 			}
151 		}
152 
153 		// Roll
154 		if (dot_yaw < 1.0f) {
155 			if (angle_yaw > DEG2RAD(COCKPIT_LAG_MAX_ANGLE)) {
156 				angle_yaw = DEG2RAD(COCKPIT_LAG_MAX_ANGLE);
157 			}
158 			if (dot_yaw < 1.0f) {
159 				angle_yaw = Easing::Quad::EaseOut(m_rotInterp, angle_yaw, -angle_yaw, 1.0f);
160 			}
161 			m_yaw = yaw_axis;
162 			if (angle_yaw >= 0.0f) {
163 				m_yaw.ArbRotate(rot_yaw_axis, angle_yaw);
164 				// Apply roll
165 				vector3d xy_proj = (m_yaw - (m_yaw.Dot(cur_dir) * cur_dir)).Normalized();
166 				float roll_cos = xy_proj.Dot(yaw_axis);
167 				float roll_angle = 0.0f;
168 				if (roll_cos < 1.0f) {
169 					roll_angle = acos(roll_cos);
170 					if (rot_yaw_axis.Dot(cur_dir) < 0) {
171 						roll_angle = -roll_angle;
172 					}
173 					m_transform.RotateZ(-roll_angle);
174 				}
175 			} else {
176 				angle_yaw = 0.0f;
177 			}
178 		}
179 	} else {
180 		m_rotInterp = 0.0f;
181 	}
182 }
183 
RenderCockpit(Graphics::Renderer * renderer,const Camera * camera,FrameId frameId)184 void ShipCockpit::RenderCockpit(Graphics::Renderer *renderer, const Camera *camera, FrameId frameId)
185 {
186 	PROFILE_SCOPED()
187 	renderer->ClearDepthBuffer();
188 	Body::SetFrame(frameId);
189 	Render(renderer, camera, m_translate, m_transform);
190 	Body::SetFrame(FrameId::Invalid);
191 }
192 
OnActivated(const Player * player)193 void ShipCockpit::OnActivated(const Player *player)
194 {
195 	assert(player);
196 	m_dir = player->GetOrient().VectorZ().Normalized();
197 	m_yaw = player->GetOrient().VectorY().Normalized();
198 	m_shipDir = m_dir;
199 	m_shipYaw = m_yaw;
200 	m_shipVel = CalculateSignedForwardVelocity(-m_shipDir, player->GetVelocity());
201 }
202 
CalculateSignedForwardVelocity(const vector3d & normalized_forward,const vector3d & velocity)203 float ShipCockpit::CalculateSignedForwardVelocity(const vector3d &normalized_forward, const vector3d &velocity)
204 {
205 	float velz_cos = velocity.Dot(normalized_forward);
206 	return (velz_cos * normalized_forward).Length() * (velz_cos < 0.0 ? -1.0 : 1.0);
207 }
208