Collected and moved existing camera infrastructure from game.cpp to camera.cpp and...
[oweals/minetest.git] / src / camera.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (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 along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "camera.h"
21 #include "debug.h"
22 #include "main.h" // for g_settings
23 #include "map.h"
24 #include "player.h"
25 #include "utility.h"
26 #include <cmath>
27
28 Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control):
29         m_smgr(smgr),
30         m_playernode(NULL),
31         m_cameranode(NULL),
32         m_draw_control(draw_control),
33         m_viewing_range_min(5.0),
34         m_viewing_range_max(5.0),
35
36         m_camera_position(0,0,0),
37         m_camera_direction(0,0,0),
38
39         m_aspect(1.0),
40         m_fov_x(1.0),
41         m_fov_y(1.0),
42
43         m_wanted_frametime(0.0),
44         m_added_frametime(0),
45         m_added_frames(0),
46         m_range_old(0),
47         m_frametime_old(0),
48         m_frametime_counter(0),
49         m_time_per_range(30. / 50), // a sane default of 30ms per 50 nodes of range
50
51         m_view_bobbing_anim(0),
52         m_view_bobbing_anim_left(0)
53 {
54         dstream<<__FUNCTION_NAME<<std::endl;
55
56         m_playernode = smgr->addEmptySceneNode(smgr->getRootSceneNode());
57         m_cameranode = smgr->addCameraSceneNode(m_playernode);
58
59         updateSettings();
60 }
61
62 Camera::~Camera()
63 {
64 }
65
66 void Camera::step(f32 dtime)
67 {
68 }
69
70 void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize)
71 {
72         if (m_playernode == NULL || m_cameranode == NULL)
73                 return;
74
75         // FOV and and aspect ratio
76         m_aspect = (f32)screensize.X / (f32) screensize.Y;
77         m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y));
78         m_cameranode->setAspectRatio(m_aspect);
79         m_cameranode->setFOV(m_fov_y);
80
81         // Just so big a value that everything rendered is visible
82         // Some more allowance that m_viewing_range_max * BS because of active objects etc.
83         m_cameranode->setFarValue(m_viewing_range_max * BS * 10);
84
85         m_camera_position = player->getEyePosition();  // TODO bobbing
86         m_cameranode->setPosition(m_camera_position);
87
88         m_camera_direction = v3f(0,0,1);
89         m_camera_direction.rotateYZBy(player->getPitch());
90         m_camera_direction.rotateXZBy(player->getYaw());
91         // *100.0 helps in large map coordinates
92         m_cameranode->setTarget(m_camera_position + m_camera_direction * 100.0);
93
94         // Render distance feedback loop
95         updateViewingRange(frametime);
96
97         // Check if view bobbing is active
98         v3f speed = player->getSpeed();
99         f32 epsilon = BS / 1000.0;
100         if (speed.X * speed.X + speed.Z * speed.Z > epsilon*epsilon &&
101                         speed.Y < epsilon &&
102                         g_settings.getBool("view_bobbing") == true &&
103                         g_settings.getBool("free_move") == false)
104         {
105                 // The player seems to be walking on solid ground.
106                 // Enable view bobbing.
107                 //dstream << "View bobbing active" << std::endl;
108         }
109         else
110         {
111                 //dstream << "View bobbing inactive" << std::endl;
112         }
113 }
114
115 void Camera::updateViewingRange(f32 frametime_in)
116 {
117         if (m_draw_control.range_all)
118                 return;
119
120         m_added_frametime += frametime_in;
121         m_added_frames += 1;
122
123         // Actually this counter kind of sucks because frametime is busytime
124         m_frametime_counter -= frametime_in;
125         if (m_frametime_counter > 0)
126                 return;
127         m_frametime_counter = 0.2;
128
129         dstream<<__FUNCTION_NAME
130                         <<": Collected "<<m_added_frames<<" frames, total of "
131                         <<m_added_frametime<<"s."<<std::endl;
132
133         dstream<<"m_draw_control.blocks_drawn="
134                         <<m_draw_control.blocks_drawn
135                         <<", m_draw_control.blocks_would_have_drawn="
136                         <<m_draw_control.blocks_would_have_drawn
137                         <<std::endl;
138
139         m_draw_control.wanted_min_range = m_viewing_range_min;
140         m_draw_control.wanted_max_blocks = (1.5*m_draw_control.blocks_would_have_drawn)+1;
141         if (m_draw_control.wanted_max_blocks < 10)
142                 m_draw_control.wanted_max_blocks = 10;
143
144         f32 block_draw_ratio = 1.0;
145         if (m_draw_control.blocks_would_have_drawn != 0)
146         {
147                 block_draw_ratio = (f32)m_draw_control.blocks_drawn
148                         / (f32)m_draw_control.blocks_would_have_drawn;
149         }
150
151         // Calculate the average frametime in the case that all wanted
152         // blocks had been drawn
153         f32 frametime = m_added_frametime / m_added_frames / block_draw_ratio;
154
155         m_added_frametime = 0.0;
156         m_added_frames = 0;
157
158         f32 wanted_frametime_change = m_wanted_frametime - frametime;
159         dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
160
161         // If needed frametime change is small, just return
162         if (fabs(wanted_frametime_change) < m_wanted_frametime*0.4)
163         {
164                 dstream<<"ignoring small wanted_frametime_change"<<std::endl;
165                 return;
166         }
167
168         f32 range = m_draw_control.wanted_range;
169         f32 new_range = range;
170
171         f32 d_range = range - m_range_old;
172         f32 d_frametime = frametime - m_frametime_old;
173         if (d_range != 0)
174         {
175                 m_time_per_range = d_frametime / d_range;
176         }
177
178         // The minimum allowed calculated frametime-range derivative:
179         // Practically this sets the maximum speed of changing the range.
180         // The lower this value, the higher the maximum changing speed.
181         // A low value here results in wobbly range (0.001)
182         // A high value here results in slow changing range (0.0025)
183         // SUGG: This could be dynamically adjusted so that when
184         //       the camera is turning, this is lower
185         //f32 min_time_per_range = 0.0015;
186         f32 min_time_per_range = 0.0010;
187         //f32 min_time_per_range = 0.05 / range;
188         if(m_time_per_range < min_time_per_range)
189         {
190                 m_time_per_range = min_time_per_range;
191                 dstream<<"m_time_per_range="<<m_time_per_range<<" (min)"<<std::endl;
192         }
193         else
194         {
195                 dstream<<"m_time_per_range="<<m_time_per_range<<std::endl;
196         }
197
198         f32 wanted_range_change = wanted_frametime_change / m_time_per_range;
199         // Dampen the change a bit to kill oscillations
200         //wanted_range_change *= 0.9;
201         //wanted_range_change *= 0.75;
202         wanted_range_change *= 0.5;
203         dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
204
205         // If needed range change is very small, just return
206         if(fabs(wanted_range_change) < 0.001)
207         {
208                 dstream<<"ignoring small wanted_range_change"<<std::endl;
209                 return;
210         }
211
212         new_range += wanted_range_change;
213         
214         f32 new_range_unclamped = new_range;
215         new_range = MYMAX(new_range, m_viewing_range_min);
216         new_range = MYMIN(new_range, m_viewing_range_max);
217         dstream<<"new_range="<<new_range_unclamped
218                         <<", clamped to "<<new_range<<std::endl;
219
220         m_draw_control.wanted_range = new_range;
221
222         m_range_old = new_range;
223         m_frametime_old = frametime;
224 }
225
226 void Camera::updateSettings()
227 {
228         m_viewing_range_min = g_settings.getS16("viewing_range_nodes_min");
229         m_viewing_range_min = MYMAX(5.0, m_viewing_range_min);
230
231         m_viewing_range_max = g_settings.getS16("viewing_range_nodes_max");
232         m_viewing_range_max = MYMAX(m_viewing_range_min, m_viewing_range_max);
233
234         f32 fov_degrees = g_settings.getFloat("fov");
235         fov_degrees = MYMAX(fov_degrees, 10.0);
236         fov_degrees = MYMIN(fov_degrees, 170.0);
237         m_fov_y = fov_degrees * M_PI / 180.0;
238
239         f32 wanted_fps = g_settings.getFloat("wanted_fps");
240         wanted_fps = MYMAX(wanted_fps, 1.0);
241         m_wanted_frametime = 1.0 / wanted_fps;
242 }
243