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