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