1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2004-2015 Steve Baker <sjbaker1@airmail.net>
4 // Copyright (C) 2006-2015 SuperTuxKart-Team, Steve Baker
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 3
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #include "graphics/camera_fps.hpp"
21
22 #include "config/stk_config.hpp"
23 #include "config/user_config.hpp"
24 #include "karts/abstract_kart.hpp"
25 #include "karts/skidding.hpp"
26
27 #include "vector3d.h"
28
29 using namespace irr;
30
31 // ============================================================================
CameraFPS(int camera_index,AbstractKart * kart)32 CameraFPS::CameraFPS(int camera_index, AbstractKart* kart)
33 : Camera(Camera::CM_TYPE_FPS, camera_index, kart)
34 {
35 m_attached = false;
36
37 // TODO: Put these values into a config file
38 // Global or per split screen zone?
39 // Either global or per user (for instance, some users may not like
40 // the extra camera rotation so they could set m_rotation_range to
41 // zero to disable it for themselves).
42 m_position_speed = 8.0f;
43 m_target_speed = 10.0f;
44 m_rotation_range = 0.4f;
45 m_rotation_range = 0.0f;
46 m_lin_velocity = core::vector3df(0, 0, 0);
47 m_target_velocity = core::vector3df(0, 0, 0);
48 m_target_direction = core::vector3df(0, 0, 1);
49 m_target_up_vector = core::vector3df(0, 1, 0);
50 m_direction_velocity = core::vector3df(0, 0, 0);
51
52 m_local_position = core::vector3df(0, 0, 0);
53 m_local_direction = core::vector3df(0, 0, 1);
54 m_local_up = core::vector3df(0, 1, 0);
55
56 m_angular_velocity = 0;
57 m_target_angular_velocity = 0;
58 m_max_velocity = 15;
59 reset();
60 } // Camera
61
62 // ----------------------------------------------------------------------------
63 /** Removes the camera scene node from the scene.
64 */
~CameraFPS()65 CameraFPS::~CameraFPS()
66 {
67 } // ~Camera
68
69 //-----------------------------------------------------------------------------
70 /** Applies mouse movement to the first person camera.
71 * \param x The horizontal difference of the mouse position.
72 * \param y The vertical difference of the mouse position.
73 */
applyMouseMovement(float x,float y)74 void CameraFPS::applyMouseMovement (float x, float y)
75 {
76 core::vector3df direction(m_target_direction);
77 core::vector3df up(m_camera->getUpVector());
78
79 // Set local values if the camera is attached to the kart
80 if (m_attached)
81 up = m_local_up;
82
83 direction.normalize();
84 up.normalize();
85
86 core::vector3df side(direction.crossProduct(up));
87 side.normalize();
88 core::quaternion quat;
89 quat.fromAngleAxis(y, side);
90
91 core::quaternion quat_x;
92 quat_x.fromAngleAxis(x, up);
93 quat *= quat_x;
94
95 direction = quat * direction;
96 // Try to prevent toppling over
97 // If the camera would topple over with the next movement, the vertical
98 // movement gets reset close to the up vector
99 if ((direction - up).getLengthSQ() + (m_target_direction - up).getLengthSQ()
100 <= (direction - m_target_direction).getLengthSQ())
101 direction = quat_x * ((m_target_direction - up).setLength(0.02f) + up);
102 // Prevent toppling under
103 else if ((direction + up).getLengthSQ() + (m_target_direction + up).getLengthSQ()
104 <= (direction - m_target_direction).getLengthSQ())
105 direction = quat_x * ((m_target_direction + up).setLength(0.02f) - up);
106 m_target_direction = direction;
107
108 // Don't do that because it looks ugly and is bad to handle ;)
109 /*side = direction.crossProduct(up);
110 // Compute new up vector
111 up = side.crossProduct(direction);
112 up.normalize();
113 cam->setUpVector(up);*/
114 } // applyMouseMovement
115
116 //-----------------------------------------------------------------------------
117 /** Called once per time frame to move the camera to the right position.
118 * \param dt Time step.
119 */
update(float dt)120 void CameraFPS::update(float dt)
121 {
122 Camera::update(dt);
123
124 // To view inside tunnels in top mode, increase near value
125 m_camera->setNearValue(1.0f);
126
127 core::vector3df direction(m_camera->getTarget() - m_camera->getPosition());
128 core::vector3df up(m_camera->getUpVector());
129 core::vector3df side(direction.crossProduct(up));
130 core::vector3df pos = m_camera->getPosition();
131
132 // Set local values if the camera is attached to the kart
133 if (m_attached)
134 {
135 direction = m_local_direction;
136 up = m_local_up;
137 pos = m_local_position;
138 }
139
140 // Update smooth movement
141 if (m_smooth)
142 {
143 // Angular velocity
144 if (m_angular_velocity < m_target_angular_velocity)
145 {
146 m_angular_velocity += UserConfigParams::m_fpscam_angular_velocity;
147 if (m_angular_velocity > m_target_angular_velocity)
148 m_angular_velocity = m_target_angular_velocity;
149 }
150 else if (m_angular_velocity > m_target_angular_velocity)
151 {
152 m_angular_velocity -= UserConfigParams::m_fpscam_angular_velocity;
153 if (m_angular_velocity < m_target_angular_velocity)
154 m_angular_velocity = m_target_angular_velocity;
155 }
156
157 // Linear velocity
158 core::vector3df diff(m_target_velocity - m_lin_velocity);
159 if (diff.X != 0 || diff.Y != 0 || diff.Z != 0)
160 {
161 if (diff.getLengthSQ() > 1) diff.setLength(1);
162 m_lin_velocity += diff;
163 }
164
165 // Camera direction
166 diff = m_target_direction - direction;
167 if (diff.X != 0 || diff.Y != 0 || diff.Z != 0)
168 {
169 diff.setLength(UserConfigParams::m_fpscam_direction_speed);
170 m_direction_velocity += diff;
171 if (m_direction_velocity.getLengthSQ() >
172 UserConfigParams::m_fpscam_smooth_direction_max_speed *
173 UserConfigParams::m_fpscam_smooth_direction_max_speed)
174 {
175 m_direction_velocity.setLength(
176 UserConfigParams::m_fpscam_smooth_direction_max_speed);
177 }
178 direction += m_direction_velocity;
179 m_target_direction = direction;
180 } // if diff is no 0
181
182 // Camera rotation
183 diff = m_target_up_vector - up;
184 if (diff.X != 0 || diff.Y != 0 || diff.Z != 0)
185 {
186 if (diff.getLengthSQ() >
187 UserConfigParams::m_fpscam_angular_velocity *
188 UserConfigParams::m_fpscam_angular_velocity)
189 {
190 diff.setLength(UserConfigParams::m_fpscam_angular_velocity);
191 }
192 up += diff;
193 }
194 }
195 else
196 {
197 direction = m_target_direction;
198 up = m_target_up_vector;
199 side = direction.crossProduct(up);
200 }
201
202 // Rotate camera
203 core::quaternion quat;
204 quat.fromAngleAxis(m_angular_velocity * dt, direction);
205 up = quat * up;
206 m_target_up_vector = quat * up;
207 direction.normalize();
208 up.normalize();
209 side.normalize();
210
211 // Top vector is the real up vector, not the one used by the camera
212 core::vector3df top(side.crossProduct(direction));
213
214 // Move camera
215 core::vector3df movement(direction * m_lin_velocity.Z +
216 top * m_lin_velocity.Y + side * m_lin_velocity.X);
217 pos = pos + movement * dt;
218
219 if (m_attached)
220 {
221 // Save current values
222 m_local_position = pos;
223 m_local_direction = direction;
224 m_local_up = up;
225
226 // Move the camera with the kart
227 btTransform t = m_kart->getSmoothedTrans();
228 if (stk_config->m_camera_follow_skid &&
229 m_kart->getSkidding()->getVisualSkidRotation() != 0)
230 {
231 // If the camera should follow the graphical skid, add the
232 // visual rotation to the relative vector:
233 btQuaternion q(m_kart->getSkidding()->getVisualSkidRotation(), 0, 0);
234 t.setBasis(t.getBasis() * btMatrix3x3(q));
235 }
236 pos = Vec3(t(Vec3(pos))).toIrrVector();
237
238 btQuaternion q = t.getRotation();
239 btMatrix3x3 mat(q);
240 direction = Vec3(mat * Vec3(direction)).toIrrVector();
241 up = Vec3(mat * Vec3(up)).toIrrVector();
242 }
243
244 // Set camera attributes
245 m_camera->setPosition(pos);
246 m_camera->setTarget(pos + direction);
247 m_camera->setUpVector(up);
248 } // update
249
250 // ----------------------------------------------------------------------------
251 /** Sets the angular velocity for this camera. */
setAngularVelocity(float vel)252 void CameraFPS::setAngularVelocity(float vel)
253 {
254 if (m_smooth)
255 m_target_angular_velocity = vel;
256 else
257 m_angular_velocity = vel;
258 } // setAngularVelocity
259
260 // ----------------------------------------------------------------------------
261 /** Returns the current target angular velocity. */
getAngularVelocity()262 float CameraFPS::getAngularVelocity()
263 {
264 if (m_smooth)
265 return m_target_angular_velocity;
266 else
267 return m_angular_velocity;
268 } // getAngularVelocity
269
270 // ----------------------------------------------------------------------------
271 /** Sets the linear velocity for this camera. */
setLinearVelocity(core::vector3df vel)272 void CameraFPS::setLinearVelocity(core::vector3df vel)
273 {
274 if (m_smooth)
275 m_target_velocity = vel;
276 else
277 m_lin_velocity = vel;
278 } // setLinearVelocity
279
280 // ----------------------------------------------------------------------------
281 /** Returns the current linear velocity. */
getLinearVelocity()282 const core::vector3df &CameraFPS::getLinearVelocity()
283 {
284 if (m_smooth)
285 return m_target_velocity;
286 else
287 return m_lin_velocity;
288 } // getLinearVelocity
289
290