1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2018 SuperTuxKart-Team
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License
7 //  as published by the Free Software Foundation; either version 3
8 //  of the License, or (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 #include "network/smooth_network_body.hpp"
20 #include "config/stk_config.hpp"
21 
22 #include <algorithm>
23 
24 // ----------------------------------------------------------------------------
SmoothNetworkBody(bool enable)25 SmoothNetworkBody::SmoothNetworkBody(bool enable)
26 {
27     reset();
28     m_enabled = enable;
29     m_smooth_rotation = true;
30     m_adjust_vertical_offset = true;
31     m_min_adjust_length = stk_config->m_snb_min_adjust_length;
32     m_max_adjust_length = stk_config->m_snb_max_adjust_length;
33     m_min_adjust_speed = stk_config->m_snb_min_adjust_speed;
34     m_max_adjust_time = stk_config->m_snb_max_adjust_time;
35     m_adjust_length_threshold = stk_config->m_snb_adjust_length_threshold;
36 }   // SmoothNetworkBody
37 
38 // ----------------------------------------------------------------------------
prepareSmoothing(const btTransform & current_transform,const Vec3 & current_velocity)39 void SmoothNetworkBody::prepareSmoothing(const btTransform& current_transform,
40                                          const Vec3& current_velocity)
41 {
42 #ifndef SERVER_ONLY
43     // Continuous smooth enabled
44     //if (m_smoothing != SS_NONE)
45     //    return;
46 
47     m_prev_position_data = std::make_pair(current_transform,
48         current_velocity);
49 #endif
50 }   // prepareSmoothing
51 
52 // ----------------------------------------------------------------------------
53 /** Adds a new error between graphical and physical position/rotation. Called
54  * in case of a rewind to allow to for smoothing the visuals in case of
55  * incorrect client prediction.
56  */
checkSmoothing(const btTransform & current_transform,const Vec3 & current_velocity)57 void SmoothNetworkBody::checkSmoothing(const btTransform& current_transform,
58                                        const Vec3& current_velocity)
59 {
60 #ifndef SERVER_ONLY
61     // Continuous smooth enabled
62     //if (m_smoothing != SS_NONE)
63     //    return;
64 
65     float adjust_length = (current_transform.getOrigin() -
66         m_prev_position_data.first.getOrigin()).length();
67     if (adjust_length < m_min_adjust_length ||
68         adjust_length > m_max_adjust_length)
69         return;
70 
71     float speed = m_prev_position_data.second.length();
72     speed = std::max(speed, current_velocity.length());
73     if (speed < m_min_adjust_speed)
74         return;
75 
76     float adjust_time = (adjust_length * m_adjust_length_threshold) / speed;
77     if (adjust_time > m_max_adjust_time)
78         return;
79 
80     m_start_smoothing_postion.first = m_smoothing == SS_NONE ?
81         m_prev_position_data.first.getOrigin() :
82         m_smoothed_transform.getOrigin();
83     m_start_smoothing_postion.second = m_smoothing == SS_NONE ?
84         m_prev_position_data.first.getRotation() :
85         m_smoothed_transform.getRotation();
86     m_start_smoothing_postion.second.normalize();
87 
88     m_smoothing = SS_TO_ADJUST;
89     m_adjust_time_dt = 0.0f;
90     m_adjust_time = adjust_time;
91 
92     m_adjust_control_point = m_start_smoothing_postion.first +
93         m_prev_position_data.second * m_adjust_time;
94     Vec3 p2 = current_transform.getOrigin() + current_velocity * m_adjust_time;
95 
96     m_adjust_position.first.setInterpolate3(m_adjust_control_point, p2, 0.5f);
97     m_adjust_position.second = current_transform.getRotation();
98     m_adjust_position.second.normalize();
99 #endif
100 }   // checkSmoothing
101 
102 // ------------------------------------------------------------------------
updateSmoothedGraphics(const btTransform & current_transform,const Vec3 & current_velocity,float dt)103 void SmoothNetworkBody::updateSmoothedGraphics(
104     const btTransform& current_transform, const Vec3& current_velocity,
105     float dt)
106 {
107 #ifndef SERVER_ONLY
108     Vec3 cur_xyz = current_transform.getOrigin();
109     btQuaternion cur_rot = current_transform.getRotation();
110 
111     if (!m_enabled)
112     {
113         m_smoothed_transform.setOrigin(cur_xyz);
114         m_smoothed_transform.setRotation(cur_rot);
115         return;
116     }
117 
118     float ratio = 0.0f;
119     if (m_smoothing != SS_NONE)
120     {
121         float adjust_time_dt = m_adjust_time_dt + dt;
122         ratio = adjust_time_dt / m_adjust_time;
123         if (ratio > 1.0f)
124         {
125             ratio -= 1.0f;
126             m_adjust_time_dt = adjust_time_dt - m_adjust_time;
127             if (m_smoothing == SS_TO_ADJUST)
128             {
129                 m_smoothing = SS_TO_REAL;
130                 m_adjust_control_point = m_adjust_position.first +
131                     current_velocity * m_adjust_time;
132             }
133             else
134                 m_smoothing = SS_NONE;
135         }
136         else
137             m_adjust_time_dt = adjust_time_dt;
138     }
139 
140     assert(m_adjust_time_dt >= 0.0f);
141     assert(ratio >= 0.0f);
142     if (m_smoothing == SS_TO_ADJUST)
143     {
144         cur_xyz.setInterpolate3(m_start_smoothing_postion.first,
145             m_adjust_position.first, ratio);
146         Vec3 to_control;
147         to_control.setInterpolate3(m_start_smoothing_postion.first,
148             m_adjust_control_point, ratio);
149         cur_xyz.setInterpolate3(cur_xyz, to_control, 1.0f - ratio);
150         if (m_smooth_rotation)
151         {
152             cur_rot = m_start_smoothing_postion.second;
153             if (dot(cur_rot, m_adjust_position.second) < 0.0f)
154                 cur_rot = -cur_rot;
155             cur_rot = cur_rot.slerp(m_adjust_position.second, ratio);
156         }
157     }
158     else if (m_smoothing == SS_TO_REAL)
159     {
160         Vec3 to_control;
161         to_control.setInterpolate3(m_adjust_position.first,
162             m_adjust_control_point, ratio);
163         float ratio_sqrt = sqrtf(ratio);
164         cur_xyz.setInterpolate3(m_adjust_position.first, cur_xyz, ratio_sqrt);
165         cur_xyz.setInterpolate3(to_control, cur_xyz, ratio);
166         if (m_smooth_rotation)
167         {
168             cur_rot.normalize();
169             if (dot(cur_rot, m_adjust_position.second) < 0.0f)
170                 cur_rot = -cur_rot;
171             cur_rot = cur_rot.slerp(m_adjust_position.second, 1.0f - ratio);
172         }
173     }
174 
175     m_smoothed_transform.setOrigin(cur_xyz);
176     m_smoothed_transform.setRotation(cur_rot);
177 
178     if (m_adjust_vertical_offset && m_smoothing != SS_NONE)
179     {
180         Vec3 lc = current_transform.inverse()(cur_xyz);
181         // Adjust vertical position for up/down-sloping
182         cur_xyz = m_smoothed_transform(Vec3(0.0f, -lc.y(), 0.0f));
183         m_smoothed_transform.setOrigin(cur_xyz);
184     }
185 #endif
186 }   // updateSmoothedGraphics
187